From c51e5f7f71e42b350097011ce0ad8d6aa5b463f9 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Fri, 24 Nov 2023 04:43:26 +0800 Subject: [PATCH 001/143] Implement rudimentary `PreTyper` and a prototype of new UCS desugarer --- .../src/main/scala/mlscript/JSBackend.scala | 15 ++ shared/src/main/scala/mlscript/Typer.scala | 7 +- .../main/scala/mlscript/codegen/Helpers.scala | 4 +- shared/src/main/scala/mlscript/helpers.scala | 24 ++ .../scala/mlscript/pretyper/PreTyper.scala | 186 ++++++++++++++++ .../main/scala/mlscript/pretyper/Scope.scala | 47 ++++ .../main/scala/mlscript/pretyper/Symbol.scala | 77 +++++++ .../scala/mlscript/pretyper/Traceable.scala | 39 ++++ .../mlscript/pretyper/TypeContents.scala | 5 + .../scala/mlscript/pretyper/package.scala | 14 ++ .../main/scala/mlscript/ucs/DesugarUCS.scala | 97 +++++++++ .../main/scala/mlscript/ucs/Desugarer.scala | 9 +- .../main/scala/mlscript/ucs/PartialTerm.scala | 10 + shared/src/main/scala/mlscript/ucs/core.scala | 80 +++++++ .../src/main/scala/mlscript/ucs/package.scala | 31 +++ .../mlscript/ucs/stages/Desugaring.scala | 126 +++++++++++ .../mlscript/ucs/stages/Normalization.scala | 205 ++++++++++++++++++ .../mlscript/ucs/stages/PostProcessing.scala | 60 +++++ .../mlscript/ucs/stages/Transformation.scala | 145 +++++++++++++ .../scala/mlscript/ucs/stages/package.scala | 7 + .../src/main/scala/mlscript/ucs/syntax.scala | 152 +++++++++++++ shared/src/test/diff/mlscript/Basics.mls | 2 +- .../src/test/diff/mlscript/ByNameByValue.mls | 4 +- shared/src/test/diff/mlscript/MultiArgs.mls | 8 +- shared/src/test/diff/mlscript/Ops.mls | 10 +- shared/src/test/diff/mlscript/Repro.mls | 14 ++ shared/src/test/diff/nu/LamPatterns.mls | 2 +- shared/src/test/diff/nu/OverrideShorthand.mls | 4 +- shared/src/test/diff/nu/RightAssocOps.mls | 6 +- .../src/test/diff/pretyper/Declarations.mls | 89 ++++++++ .../src/test/diff/pretyper/ucs/DualOption.mls | 135 ++++++++++++ .../test/diff/pretyper/ucs/NamePattern.mls | 9 + .../test/diff/pretyper/ucs/RecordPattern.mls | 8 + .../test/diff/pretyper/ucs/TransfromUCS.mls | 45 ++++ .../test/diff/pretyper/ucs/TuplePattern.mls | 10 + shared/src/test/diff/pretyper/ucs/Unapply.mls | 13 ++ .../test/diff/pretyper/ucs/Unconditional.mls | 26 +++ .../diff/pretyper/ucs/examples/Option.mls | 42 ++++ shared/src/test/diff/ucs/LeadingAnd.mls | 4 +- shared/src/test/diff/ucs/SplitOps.mls | 4 +- .../src/test/scala/mlscript/DiffTests.scala | 29 ++- 41 files changed, 1771 insertions(+), 33 deletions(-) create mode 100644 shared/src/main/scala/mlscript/pretyper/PreTyper.scala create mode 100644 shared/src/main/scala/mlscript/pretyper/Scope.scala create mode 100644 shared/src/main/scala/mlscript/pretyper/Symbol.scala create mode 100644 shared/src/main/scala/mlscript/pretyper/Traceable.scala create mode 100644 shared/src/main/scala/mlscript/pretyper/TypeContents.scala create mode 100644 shared/src/main/scala/mlscript/pretyper/package.scala create mode 100644 shared/src/main/scala/mlscript/ucs/DesugarUCS.scala create mode 100644 shared/src/main/scala/mlscript/ucs/core.scala create mode 100644 shared/src/main/scala/mlscript/ucs/package.scala create mode 100644 shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala create mode 100644 shared/src/main/scala/mlscript/ucs/stages/Normalization.scala create mode 100644 shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala create mode 100644 shared/src/main/scala/mlscript/ucs/stages/Transformation.scala create mode 100644 shared/src/main/scala/mlscript/ucs/stages/package.scala create mode 100644 shared/src/main/scala/mlscript/ucs/syntax.scala create mode 100644 shared/src/test/diff/pretyper/Declarations.mls create mode 100644 shared/src/test/diff/pretyper/ucs/DualOption.mls create mode 100644 shared/src/test/diff/pretyper/ucs/NamePattern.mls create mode 100644 shared/src/test/diff/pretyper/ucs/RecordPattern.mls create mode 100644 shared/src/test/diff/pretyper/ucs/TransfromUCS.mls create mode 100644 shared/src/test/diff/pretyper/ucs/TuplePattern.mls create mode 100644 shared/src/test/diff/pretyper/ucs/Unapply.mls create mode 100644 shared/src/test/diff/pretyper/ucs/Unconditional.mls create mode 100644 shared/src/test/diff/pretyper/ucs/examples/Option.mls diff --git a/shared/src/main/scala/mlscript/JSBackend.scala b/shared/src/main/scala/mlscript/JSBackend.scala index 9ea71bf8..d95f3122 100644 --- a/shared/src/main/scala/mlscript/JSBackend.scala +++ b/shared/src/main/scala/mlscript/JSBackend.scala @@ -187,6 +187,8 @@ class JSBackend(allowUnresolvedSymbols: Boolean) { JSRecord(fields map { case (key, Fld(_, value)) => key.name -> translateTerm(value) }) + case Sel(receiver, JSBackend.TupleIndex(n)) => + JSField(translateTerm(receiver), n.toString) case Sel(receiver, fieldName) => JSField(translateTerm(receiver), fieldName.name) // Turn let into an IIFE. @@ -1576,4 +1578,17 @@ object JSBackend { def isSafeInteger(value: BigInt): Boolean = MinimalSafeInteger <= value && value <= MaximalSafeInteger + + // Temporary measurement until we adopt the new tuple index. + object TupleIndex { + def unapply(fieldName: Var): Opt[Int] = { + val name = fieldName.name + if (name.startsWith("_") && name.forall(_.isDigit)) + name.drop(1).toIntOption match { + case S(n) if n > 0 => S(n - 1) + case _ => N + } + else N + } + } } diff --git a/shared/src/main/scala/mlscript/Typer.scala b/shared/src/main/scala/mlscript/Typer.scala index 3b11dfca..c2a75e96 100644 --- a/shared/src/main/scala/mlscript/Typer.scala +++ b/shared/src/main/scala/mlscript/Typer.scala @@ -1207,8 +1207,11 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne } con(s_ty, req, cs_ty) case elf: If => - try typeTerm(desugarIf(elf)) catch { - case e: ucs.DesugaringException => err(e.messages) + elf.desugaredTerm match { + case S(desugared) => typeTerm(desugared) + case N => try typeTerm(desugarIf(elf)) catch { + case e: ucs.DesugaringException => err(e.messages) + } } case AdtMatchWith(cond, arms) => println(s"typed condition term ${cond}") diff --git a/shared/src/main/scala/mlscript/codegen/Helpers.scala b/shared/src/main/scala/mlscript/codegen/Helpers.scala index 2ffcbdcf..298ec576 100644 --- a/shared/src/main/scala/mlscript/codegen/Helpers.scala +++ b/shared/src/main/scala/mlscript/codegen/Helpers.scala @@ -13,8 +13,8 @@ object Helpers { case App(lhs, rhs) => s"App(${inspect(lhs)}, ${inspect(rhs)})" case Tup(fields) => val entries = fields map { - case (S(name), Fld(_, value)) => s"$name: ${inspect(value)}" - case (N, Fld(_, value)) => s"_: ${inspect(value)}" + case (S(name), Fld(_, value)) => s"(S(${inspect(name)}), ${inspect(value)})" + case (N, Fld(_, value)) => s"(N, ${inspect(value)})" } s"Tup(${entries mkString ", "})" case Rcd(fields) => diff --git a/shared/src/main/scala/mlscript/helpers.scala b/shared/src/main/scala/mlscript/helpers.scala index a6e2784f..3285c8d8 100644 --- a/shared/src/main/scala/mlscript/helpers.scala +++ b/shared/src/main/scala/mlscript/helpers.scala @@ -480,6 +480,14 @@ trait TypingUnitImpl extends Located { self: TypingUnit => }.mkString("{", "; ", "}") override def toString: String = s"‹${entities.mkString("; ")}›" lazy val children: List[Located] = entities + def describe: Str = entities.iterator.map { + case term: Term => term.describe + case NuFunDef(S(rec), nme, _, _, _) => + s"let ${if (rec) "rec " else ""}$nme" + case NuFunDef(N, nme, _, _, _) => s"fun $nme" + case typ: NuTypeDef => typ.describe + case other => "?" + }.mkString("{", "; ", "}") } trait ConstructorImpl { self: Constructor => @@ -493,6 +501,7 @@ trait TypeNameImpl extends Ordered[TypeName] { self: TypeName => def targs: Ls[Type] = Nil def compare(that: TypeName): Int = this.name compare that.name lazy val toVar: Var = Var(name).withLocOf(this) + var symbol: Opt[pretyper.TypeSymbol] = N } trait FldImpl extends Located { self: Fld => @@ -727,6 +736,21 @@ trait VarImpl { self: Var => (name.head.isLetter && name.head.isLower || name.head === '_' || name.head === '$') && name =/= "true" && name =/= "false" def toVar: Var = this var uid: Opt[Int] = N + + // PreTyper additions + import pretyper.{Symbol} + + private var _symbol: Opt[Symbol] = N + def symbolOption: Opt[Symbol] = _symbol + def symbol: Symbol = _symbol.getOrElse(???) + def symbol_=(symbol: Symbol): Unit = + _symbol match { + case N => _symbol = S(symbol) + case S(_) => ??? + } + // TODO: Remove this methods if they are useless. + // def withSymbol: Var = { symbol = S(new ValueSymbol(this, false)); this } + // def withSymbol(s: TermSymbol): Var = { symbol = S(s); this } } trait TupImpl { self: Tup => diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala new file mode 100644 index 00000000..f76c0628 --- /dev/null +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -0,0 +1,186 @@ +package mlscript.pretyper + +import mlscript.ucs.DesugarUCS +import mlscript._, utils._, shorthands._ +import mlscript.codegen.Helpers.inspect + +class PreTyper(override val debugLevel: Opt[Int], useNewDefs: Bool) extends Traceable with DesugarUCS { + private def extractParameters(fields: Term): Ls[ValueSymbol] = fields match { + case Tup(arguments) => + if (useNewDefs) { + arguments.map { + case (S(nme: Var), Fld(_, _)) => new ValueSymbol(nme, false) + case (_, Fld(_, nme: Var)) => new ValueSymbol(nme, false) + case (_, Fld(_, x)) => println(x.toString); ??? + } + } else { + arguments.map { + case (_, Fld(_, nme: Var)) => new ValueSymbol(nme, false) + case (_, Fld(_, x)) => println(x.toString); ??? + } + } + case PlainTup(arguments @ _*) => + arguments.map { + case nme: Var => new ValueSymbol(nme, false) + case other => println("Unknown parameters: " + inspect(other)); ??? // TODO: bad + }.toList + case other => println("Unknown parameters: " + inspect(other)); ??? // TODO: bad + } + + // `visitIf` is meaningless because it represents patterns with terms. + + protected def resolveVar(v: Var)(implicit scope: Scope): Unit = + trace(s"resolveVar(name = \"$v\")") { + scope.get(v.name) match { + case Some(sym: ValueSymbol) => + println(s"Resolve variable $v to a value.", 2) + v.symbol = sym + case Some(sym: SubValueSymbol) => + println(s"Resolve variable $v to a value.", 2) + v.symbol = sym + case Some(sym: FunctionSymbol) => + println(s"Resolve variable $v to a function.", 2) + v.symbol = sym + case Some(sym: TypeSymbol) => + if (sym.defn.kind == Cls) { + println(s"Resolve variable $v to a class.", 2) + v.symbol = sym + } else { + throw new Exception(s"Name $v refers to a type") + } + case None => throw new Exception(s"Variable $v not found in scope") + } + }() + + protected def visitVar(v: Var)(implicit scope: Scope): Unit = + trace(s"visitVar(name = \"$v\")") { + v.symbolOption match { + case N => resolveVar(v) + case S(symbol) => scope.get(v.name) match { + case S(other) if other === symbol => () + case S(other) => throw new Exception(s"Variable $v refers to a different symbol") + case N => throw new Exception(s"Variable $v not found in scope. It is possibly a free variable.") + } + } + }() + + protected def visitTerm(term: Term)(implicit scope: Scope): Unit = + trace(s"visitTerm <== ${shortName(term)}") { + term match { + case Assign(lhs, rhs) => visitTerm(lhs); visitTerm(rhs) + case Bra(_, trm) => visitTerm(trm) + case Lam(lhs, rhs) => + visitTerm(rhs)(scope ++ extractParameters(lhs)) + case Sel(receiver, fieldName) => visitTerm(receiver) + case Let(isRec, nme, rhs, body) => + visitTerm(rhs) + visitTerm(body)(scope + new ValueSymbol(nme, false)) + case New(head, body) => + case Tup(fields) => fields.foreach { case (_, Fld(_, t)) => visitTerm(t) } + case Asc(trm, ty) => visitTerm(trm) + case ef @ If(_, _) => visitIf(ef)(scope) + case TyApp(lhs, targs) => // TODO: When? + case Eqn(lhs, rhs) => ??? // TODO: How? + case Blk(stmts) => stmts.foreach { + case t: Term => visitTerm(t) + case _ => ??? // TODO: When? + } + case Subs(arr, idx) => visitTerm(arr); visitTerm(idx) + case Bind(lhs, rhs) => visitTerm(lhs); visitTerm(rhs) + case Splc(fields) => fields.foreach { + case L(t) => visitTerm(t) + case R(Fld(_, t)) => visitTerm(t) + } + case Forall(params, body) => ??? // TODO: When? + case Rcd(fields) => fields.foreach { case (_, Fld(_, t)) => visitTerm(t) } + case CaseOf(trm, cases) => + case With(trm, fields) => visitTerm(trm); visitTerm(fields) + case Where(body, where) => ??? // TODO: When? + case App(lhs, rhs) => visitTerm(lhs); visitTerm(rhs) + case Test(trm, ty) => visitTerm(trm) + case _: Lit | _: Super => () + case v: Var => visitVar(v) + case AdtMatchWith(cond, arms) => ??? // TODO: How? + case Inst(body) => visitTerm(body) + } + }(_ => s"visitTerm ==> ${shortName(term)}") + + private def visitNuTypeDef(symbol: TypeSymbol, defn: NuTypeDef)(implicit scope: Scope): Unit = + trace(s"visitNuTypeDef <== ${defn.kind} ${defn.nme.name}") { + visitTypingUnit(defn.body, defn.nme.name, scope) + () + }(_ => s"visitNuTypeDef <== ${defn.kind} ${defn.nme.name}") + + private def visitFunction(symbol: FunctionSymbol, defn: NuFunDef)(implicit scope: Scope): Unit = + trace(s"visitFunction <== ${defn.nme.name}") { + defn.rhs match { + case Left(term) => + val subScope = if (defn.isLetRec == S(false)) scope else scope + symbol + visitTerm(term)(subScope) + case Right(value) => () + } + }(_ => s"visitFunction ==> ${defn.nme.name}") + + private def visitLetBinding(symbol: ValueSymbol, rec: Bool, rhs: Term)(implicit scope: Scope): Unit = + trace(s"visitLetBinding(rec = $rec, ${symbol.name})") { + + }() + + private def visitTypingUnit(typingUnit: TypingUnit, name: Str, parentScope: Scope): (Scope, TypeContents) = + trace(s"visitTypingUnit <== $name: ${typingUnit.describe}") { + import mlscript.{Cls, Trt, Mxn, Als, Mod} + // Pass 1: Build a scope with hoisted symbols. + val hoistedScope = typingUnit.entities.foldLeft(parentScope.derive) { + case (acc, _: Term) => acc // Skip + case (acc, defn: NuTypeDef) => + val `var` = Var(defn.nme.name).withLoc(defn.nme.toLoc) + // Create a type symbol but do not visit its inner members + acc ++ (new TypeSymbol(defn.nme, defn) :: + (defn.kind match { + case Mod => new ValueSymbol(`var`, true) :: Nil + case Als | Cls | Mxn | Trt => Nil + })) + case (acc, defn: NuFunDef) if defn.isLetRec.isEmpty => + acc + new FunctionSymbol(defn.nme, defn) + case (acc, _: NuFunDef) => acc + case (acc, _: Constructor | _: DataDefn | _: DatatypeDefn | _: Def | _: LetS | _: TypeDef) => ??? // TODO: When? + } + println(hoistedScope.symbols.map(_.name).mkString("1. scope = {", ", ", "}")) + // Pass 2: Visit non-hoisted and build a complete scope. + val completeScope = typingUnit.entities.foldLeft[Scope](hoistedScope) { + case (acc, term: Term) => visitTerm(term)(acc); acc + case (acc, defn: NuTypeDef) => acc + case (acc, defn @ NuFunDef(Some(rec), nme, _, _, L(rhs))) => + val symbol = new ValueSymbol(defn.nme, true) + val scopeWithVar = acc + symbol + visitLetBinding(symbol, rec, rhs)(if (rec) { scopeWithVar } else { acc }) + scopeWithVar + case (acc, _: NuFunDef) => acc + case (acc, _: Constructor | _: DataDefn | _: DatatypeDefn | _: Def | _: LetS | _: TypeDef) => ??? // TODO: When? + } + println(hoistedScope.symbols.map(_.name).mkString("2. scope = {", ", ", "}")) + import pretyper.TypeSymbol + // Pass 3: Visit hoisted symbols. + completeScope.symbols.foreach { + case symbol: TypeSymbol => + val innerScope = symbol.defn.kind match { + case Cls => + completeScope.derive ++ (symbol.defn.params match { + case N => Nil + case S(fields) => extractParameters(fields) + }) + case Als | Mod | Mxn | Trt => completeScope + } + visitNuTypeDef(symbol, symbol.defn)(innerScope) + case symbol: FunctionSymbol => visitFunction(symbol, symbol.defn)(completeScope) + case _: ValueSymbol => () + case _: SubValueSymbol => () + } + (completeScope, new TypeContents) + }({ case (scope, contents) => s"visitTypingUnit ==> ${scope.showLocalSymbols}" }) + + def process(typingUnit: TypingUnit, scope: Scope, name: Str): (Scope, TypeContents) = + trace(s"process <== $name: ${typingUnit.describe}") { + visitTypingUnit(typingUnit, name, scope) + }({ case (scope, contents) => s"process ==> ${scope.showLocalSymbols}" }) +} diff --git a/shared/src/main/scala/mlscript/pretyper/Scope.scala b/shared/src/main/scala/mlscript/pretyper/Scope.scala new file mode 100644 index 00000000..e47690a9 --- /dev/null +++ b/shared/src/main/scala/mlscript/pretyper/Scope.scala @@ -0,0 +1,47 @@ +package mlscript.pretyper + +import collection.immutable.Map +import mlscript.utils._, shorthands._ +import mlscript.Var + +class Scope(val enclosing: Opt[Scope], val entries: Map[String, Symbol]) { + @inline + def get(name: String): Opt[Symbol] = entries.get(name) match { + case Some(sym) => S(sym) + case None => enclosing.fold(N: Opt[Symbol])(_.get(name)) + } + + @inline + def +(sym: Symbol): Scope = new Scope(S(this), entries + (sym.name -> sym)) + + @inline + def ++(syms: IterableOnce[Symbol]): Scope = + new Scope(S(this), entries ++ syms.iterator.map(sym => sym.name -> sym)) + + def withEntries(syms: IterableOnce[Var -> Symbol]): Scope = + new Scope(S(this), entries ++ syms.iterator.map { + case (nme, sym) => nme.name -> sym + }) + + @inline + def symbols: Iterable[Symbol] = entries.values + + def derive: Scope = new Scope(S(this), Map.empty) + + def showLocalSymbols: Str = entries.iterator.map(_._1).mkString(", ") +} + +object Scope { + def from(symbols: IterableOnce[Symbol]): Scope = + new Scope(N, Map.from(symbols.iterator.map(sym => sym.name -> sym))) + + val global: Scope = Scope.from( + """true,false,document,window,typeof,toString,not,succ,log,discard,negate, + |round,add,sub,mul,div,sqrt,lt,le,gt,ge,slt,sle,sgt,sge,length,concat,eq, + |ne,error,id,if,emptyArray,+,-,*,%,/,<,>,<=,>=,==,===,<>,&&,||""" + .stripMargin + .split(",") + .iterator + .map(name => new ValueSymbol(Var(name), false)) + ) +} diff --git a/shared/src/main/scala/mlscript/pretyper/Symbol.scala b/shared/src/main/scala/mlscript/pretyper/Symbol.scala new file mode 100644 index 00000000..7cd3d335 --- /dev/null +++ b/shared/src/main/scala/mlscript/pretyper/Symbol.scala @@ -0,0 +1,77 @@ +package mlscript.pretyper + +import collection.mutable.{Buffer, Map => MutMap} +import mlscript.{NuFunDef, NuTypeDef, TypeName, Var} +import mlscript.utils._, shorthands._ + +sealed abstract class Symbol(val name: String) + +final class TypeSymbol(val nme: TypeName, val defn: NuTypeDef) extends Symbol(nme.name) { + var containedScope: Opt[Scope] = N + var containedContents: Opt[TypeContents] = N + + def scope: Scope = containedScope.getOrElse(throw new Exception("Scope not set")) + def contents: TypeContents = containedContents.getOrElse(throw new Exception("TypeContents not set")) + + def scope_=(s: Scope): Unit = containedScope = S(s) + def contents_=(c: TypeContents): Unit = containedContents = S(c) +} + +sealed abstract class TermSymbol(name: String) extends Symbol(name) + +final class FunctionSymbol(val nme: Var, val defn: NuFunDef) extends TermSymbol(nme.name) { + var containedScope: Opt[Scope] = N + + def scope: Scope = containedScope.getOrElse(throw new Exception("Scope not set")) + + def scope_=(s: Scope): Unit = containedScope = S(s) +} + +sealed abstract class ScrutineeSymbol(name: Str) extends TermSymbol(name) { + /** + * This map contains the sub-scrutinee symbols when this scrutinee is matched + * against class patterns. + */ + val classParameterScrutineeMap: MutMap[Var -> Int, SubValueSymbol] = MutMap.empty + val tupleElementScrutineeMap: MutMap[Int, SubValueSymbol] = MutMap.empty + val recordValueScrutineeMap: MutMap[Var, SubValueSymbol] = MutMap.empty + + def addSubScrutinee(className: Var, index: Int, parameter: Var): SubValueSymbol = + classParameterScrutineeMap.getOrElseUpdate(className -> index, { + new SubValueSymbol(this, S(className) -> S(index), parameter.name) + }) + + def addSubScrutinee(fieldName: Var): SubValueSymbol = + recordValueScrutineeMap.getOrElseUpdate(fieldName, { + val synthesizedName = s"${name}$$record${fieldName}" + new SubValueSymbol(this, S(fieldName) -> N, synthesizedName) + }) + + def addSubScrutinee(index: Int): SubValueSymbol = + tupleElementScrutineeMap.getOrElseUpdate(index, { + val synthesizedName = s"${name}$$tuple${index.toString}" + new SubValueSymbol(this, N -> S(index), synthesizedName) + }) + + /** + * This buffer contains alias variables which created by "let" bindings or + * alias patterns. + */ + val aliases: Buffer[Var] = Buffer.empty +} + +final class ValueSymbol(val nme: Var, val hoisted: Bool) extends ScrutineeSymbol(nme.name) + +final class SubValueSymbol( + val parentSymbol: ScrutineeSymbol, + /** + * TODO: This becomes useless now. + * Class patterns: (S(className), S(index)) + * Record patterns: (S(fieldName), N) + * Tuple patterns: (N, S(index)) + */ + val accessor: (Opt[Var], Opt[Int]), + override val name: Str +) extends ScrutineeSymbol(name) { + lazy val toVar: Var = Var(name) +} diff --git a/shared/src/main/scala/mlscript/pretyper/Traceable.scala b/shared/src/main/scala/mlscript/pretyper/Traceable.scala new file mode 100644 index 00000000..f707416c --- /dev/null +++ b/shared/src/main/scala/mlscript/pretyper/Traceable.scala @@ -0,0 +1,39 @@ +package mlscript.pretyper + +import mlscript.utils._, shorthands._ + +trait Traceable { + // Override this to change the base indentation level. + def baseIndent: Int = 0 + + protected val debugLevel: Opt[Int] = N // The number of verbose. + protected var indent = baseIndent + + def trace[T](pre: => String)(thunk: => T)(post: T => String = Traceable.noPostTrace): T = { + println(pre) + indent += 1 + val res = try thunk finally indent -= 1 + if (post isnt Traceable.noPostTrace) println(post(res)) + res + } + + @inline def traceNot[T](pre: => String)(thunk: => T)(post: T => String = Traceable.noPostTrace): T = + thunk + + def emitDbg(str: String): Unit = scala.Predef.println(str) + + @inline + protected def println(msg: => Any): Unit = println(msg, 0) + + @inline + protected def println(msg: => Any, level: Int): Unit = + if (debugLevel exists (_ >= level)) printLineByLine(msg) + + @inline + private def printLineByLine(msg: => Any): Unit = + msg.toString.linesIterator.foreach { line => emitDbg("| " * indent + line) } +} + +object Traceable { + val noPostTrace: Any => String = _ => "" +} diff --git a/shared/src/main/scala/mlscript/pretyper/TypeContents.scala b/shared/src/main/scala/mlscript/pretyper/TypeContents.scala new file mode 100644 index 00000000..fc9e0a9c --- /dev/null +++ b/shared/src/main/scala/mlscript/pretyper/TypeContents.scala @@ -0,0 +1,5 @@ +package mlscript.pretyper + +class TypeContents { + // Stub +} diff --git a/shared/src/main/scala/mlscript/pretyper/package.scala b/shared/src/main/scala/mlscript/pretyper/package.scala new file mode 100644 index 00000000..06d8773c --- /dev/null +++ b/shared/src/main/scala/mlscript/pretyper/package.scala @@ -0,0 +1,14 @@ +package mlscript + +import mlscript.utils._, shorthands._ + +package object pretyper { + def shortName(term: Term): Str = term match { + case Var(name) => s"Var(\"$name\")" + case literal: Lit => literal.toString + case _ => + val name = term.getClass.getSimpleName + val arity = term.children.length // Imprecise + if (arity === 0) { name } else s"${name}(${(", _" * arity).drop(2)})" + } +} diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala new file mode 100644 index 00000000..685d2365 --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -0,0 +1,97 @@ +package mlscript.ucs + +import collection.mutable.{Map => MutMap} +import mlscript.ucs.stages._ +import mlscript.pretyper.{PreTyper, Scope, ScrutineeSymbol, Symbol, ValueSymbol} +import mlscript._, utils._, shorthands._ +import mlscript.codegen.Helpers.inspect +import mlscript.Message, Message.MessageContext + +import mlscript.ucs.core.Pattern.Name + +// TODO: Rename to `Desugarer` once the old desugarer is removed. +trait DesugarUCS extends Transformation with Desugaring with Normalization with PostProcessing { self: PreTyper => + protected def visitIf(`if`: If)(implicit scope: Scope): Unit = + trace("visitIf") { + // Stage 0: Transformation + val transformed = transform(`if`, true) + println("Transformed UCS term:") + println(transformed.toString, 2) + println(ucs.syntax.printTermSplit(transformed)) + // Stage 1: Desugaring + // This stage will generate new names based on the position of the scrutinee. + // Therefore, we need to call `visitSplit` to associate these newly generated + // names with symbols. + val desugared = desugar(transformed) + println(desugared.toString, 2) + println("Desugared UCS term:") + println(ucs.core.printSplit(desugared)) + visitSplit(desugared) + // Stage 2: Normalization + val normalized = normalize(desugared) + println(normalized.toString, 2) + println("Normalized UCS term:") + printNormalizedTerm(normalized) + // Stage 3: Post-processing + val postProcessed = postProcess(normalized) + println("Post-processed UCS term:") + printNormalizedTerm(postProcessed) + // Epilogue + `if`.desugaredTerm = S(normalized) + }(_ => "visitIf ==> ()") + + private def visitSplit(split: core.Split)(implicit scope: Scope): Unit = + trace(s"visitSplit <== [${scope.showLocalSymbols}]") { + import core._ + split match { + case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => + println(s"found branch: $scrutinee is $pattern") + visitTerm(scrutinee) + val patternSymbols = visitPattern(scrutinee, pattern) + visitSplit(continuation)(scope.withEntries(patternSymbols)) + visitSplit(tail) + case Split.Let(_, name, _, tail) => + println(s"found let binding: \"$name\"") + visitSplit(tail)(scope + new ValueSymbol(name, false)) + case Split.Else(default) => visitTerm(default) + case Split.Nil => println("the end") + } + }() + + private def visitPattern(scrutinee: Var, pattern: core.Pattern): List[Var -> Symbol] = + trace(s"visitPattern <== $pattern") { + lazy val scrutineeSymbol = scrutinee.symbol match { + case symbol: ScrutineeSymbol => symbol + case other: Symbol => + throw new DesugaringException(msg"Scrutinee is not a scrutinee symbol" -> scrutinee.toLoc :: Nil) + } + pattern match { + case core.Pattern.Literal(literal) => Nil + case core.Pattern.Name(nme) => + nme.symbol = scrutineeSymbol + nme -> scrutineeSymbol :: Nil + case core.Pattern.Class(_, N) => Nil + case core.Pattern.Class(nme, S(parameters)) => + parameters.iterator.zipWithIndex.flatMap { + case (N, _) => N + case (S(parameter), index) => + val symbol = scrutineeSymbol.addSubScrutinee(nme, index, parameter) + parameter.symbol = symbol; S(parameter -> symbol) + }.toList + case core.Pattern.Tuple(elements) => elements.flatMap { + case N => Nil + case S(pattern) => elements.iterator.zipWithIndex.flatMap { + case (N, _) => N + case (S(element), index) => + val symbol = scrutineeSymbol.addSubScrutinee(index) + element.symbol = symbol; S(element -> symbol) + }.toList + } + case core.Pattern.Record(entries) => + entries.iterator.zipWithIndex.map { case ((fieldName, bindingName), _) => + val symbol = scrutineeSymbol.addSubScrutinee(fieldName) + bindingName.symbol = symbol; bindingName -> symbol + }.toList + } + }(_.iterator.map(_._1.name).mkString("visitPattern ==> [", ", ", "]")) +} diff --git a/shared/src/main/scala/mlscript/ucs/Desugarer.scala b/shared/src/main/scala/mlscript/ucs/Desugarer.scala index 6358f189..34075afe 100644 --- a/shared/src/main/scala/mlscript/ucs/Desugarer.scala +++ b/shared/src/main/scala/mlscript/ucs/Desugarer.scala @@ -808,7 +808,8 @@ class Desugarer extends TypeDefs { self: Typer => } } - App(Lam(Tup( + // Well, reading the following piece of code is somewhat overwhelming for me. + App(Lam(Tup( /* begin params */ N -> Fld(FldFlags.empty, Tup( fields.distinctBy(_._1).map { case (_ -> Var(alias)) => @@ -816,9 +817,9 @@ class Desugarer extends TypeDefs { self: Typer => else N -> Fld(FldFlags.empty, Var(alias)) }.toList )) :: Nil - ), extraAlias.toList.foldRight(consequent)((lt, rs) => Let(false, Var(lt._2), Var(lt._1), rs))), - Tup(N -> Fld(FldFlags.empty, App(Sel(className, Var(unapplyMtd.name)), - Tup(N -> Fld(FldFlags.empty, scrutinee.reference) :: Nil)) + ) /* end params */, extraAlias.toList.foldRight(consequent)((lt, rs) => Let(false, Var(lt._2), Var(lt._1), rs))), + Tup(N -> Fld(FldFlags.empty, App(Sel(className, Var(unapplyMtd.name) /* ClassName.unapply */), + Tup(N -> Fld(FldFlags.empty, scrutinee.reference) :: Nil)) /* ClassName.unapply(scrutinee) */ ) :: Nil) ) case _ => mkLetFromFields(scrutinee, fields.filter(_._2.name =/= "_").toList, consequent) diff --git a/shared/src/main/scala/mlscript/ucs/PartialTerm.scala b/shared/src/main/scala/mlscript/ucs/PartialTerm.scala index 7ce2d6a0..a150714f 100644 --- a/shared/src/main/scala/mlscript/ucs/PartialTerm.scala +++ b/shared/src/main/scala/mlscript/ucs/PartialTerm.scala @@ -4,6 +4,9 @@ import mlscript._ import mlscript.utils.shorthands._ import helpers._ +import mlscript.ucs.PartialTerm.Empty +import mlscript.ucs.PartialTerm.Total +import mlscript.ucs.PartialTerm.Half class PartialTermError(term: PartialTerm, message: Str) extends Error(message) @@ -19,6 +22,13 @@ sealed abstract class PartialTerm { def addOp(op: Var): PartialTerm.Half def addTermOp(term: Term, op: Var, newDefs: Bool): PartialTerm.Half = this.addTerm(term, newDefs).addOp(op) + def addOpTerm(op: Var, term: Term, newDefs: Bool): PartialTerm.Total = + this.addOp(op).addTerm(term, newDefs) + def get: Term = this match { + case Empty => throw new PartialTermError(this, "expect a term but nothing was given") + case Total(term, fragments) => term + case Half(lhs, op, fragments) => throw new PartialTermError(this, "expect an operator but nothing was given") + } } object PartialTerm { diff --git a/shared/src/main/scala/mlscript/ucs/core.scala b/shared/src/main/scala/mlscript/ucs/core.scala new file mode 100644 index 00000000..5b0dc640 --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/core.scala @@ -0,0 +1,80 @@ +package mlscript.ucs + +import mlscript.{Lit, Located, Term, Var} +import mlscript.utils._, shorthands._ + +package object core { + sealed abstract class Pattern extends Located { + override def toString(): String = this match { + case Pattern.Literal(literal) => literal.toString + case Pattern.Name(Var(name)) => name + case Pattern.Class(Var(name), N) => name + case Pattern.Class(Var(name), S(parameters)) => + parameters.iterator.map(_.fold("_")(_.name)).mkString(s"$name(", ", ", ")") + case Pattern.Tuple(fields) => fields.iterator.map(_.fold("_")(_.name)).mkString("(", ", ", ")") + case Pattern.Record(Nil) => "{}" + case Pattern.Record(entries) => entries.iterator.map { case (nme, als) => s"$nme: $als" }.mkString("{ ", ", ", " }") + } + } + object Pattern { + final case class Literal(literal: Lit) extends Pattern { + override def children: Ls[Located] = literal :: Nil + } + final case class Name(nme: Var) extends Pattern { + override def children: Ls[Located] = nme :: Nil + } + final case class Class(nme: Var, parameters: Opt[List[Opt[Var]]]) extends Pattern { + override def children: Ls[Located] = nme :: parameters.fold(Ls.empty[Located])(_.flatten) + } + final case class Tuple(elements: List[Opt[Var]]) extends Pattern { + override def children: Ls[Located] = elements.flatten + } + final case class Record(entries: List[(Var -> Var)]) extends Pattern { + override def children: Ls[Located] = entries.iterator.flatMap { case (nme, als) => nme :: als :: Nil }.toList + } + } + + final case class Branch(scrutinee: Var, pattern: Pattern, continuation: Split) + + sealed abstract class Split { + def ::(head: Branch): Split = Split.Cons(head, this) + + def ++(that: Split): Split = this match { + case me: Split.Cons => me.copy(tail = me.tail ++ that) + case me: Split.Let => me.copy(tail = me.tail ++ that) + case _: Split.Else => this + case Split.Nil => that + } + } + + object Split { + final case class Cons(head: Branch, tail: Split) extends Split + final case class Let(rec: Bool, name: Var, term: Term, tail: Split) extends Split + final case class Else(default: Term) extends Split + final case object Nil extends Split + + def just(term: Term): Split = Else(term) + } + + @inline def printSplit(s: Split): Str = showSplit("if", s) + + def showSplit(prefix: Str, s: Split): Str = { + // TODO: tailrec + def split(s: Split, isFirst: Bool, isTopLevel: Bool): Lines = s match { + case Split.Cons(head, tail) => (branch(head) match { + case (n, line) :: tail => (n, (if (isTopLevel) "" else "") + s"$line") :: tail + case Nil => Nil + }) ::: split(tail, false, isTopLevel) + case Split.Let(_, nme, rhs, tail) => (0, s"let $nme = $rhs") :: split(tail, false, isTopLevel) + case Split.Else(term) => (if (isFirst) (0, s"then $term") else (0, s"else $term")) :: Nil + case Split.Nil => Nil + } + def branch(b: Branch): Lines = { + val Branch(scrutinee, pattern, continuation) = b + s"$scrutinee is $pattern" #: split(continuation, true, false) + } + val lines = split(s, true, true) + (if (prefix.isEmpty) lines else prefix #: lines) + .iterator.map { case (n, line) => " " * n + line }.mkString("\n") + } +} \ No newline at end of file diff --git a/shared/src/main/scala/mlscript/ucs/package.scala b/shared/src/main/scala/mlscript/ucs/package.scala new file mode 100644 index 00000000..21516814 --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/package.scala @@ -0,0 +1,31 @@ +package mlscript + +import scala.annotation.tailrec + +package object ucs { + type Lines = List[(Int, String)] + + implicit class LinesOps(private val lines: Lines) extends AnyVal { + def indent: Lines = { + @tailrec + def rec(acc: Lines, lines: Lines): Lines = lines match { + case (n, line) :: tail => rec((n + 1, line) :: acc, tail) + case Nil => acc.reverse + } + rec(Nil, lines) + } + def ##:(prefix: String): Lines = (0, prefix) :: lines.indent + def #:(prefix: String): Lines = { + lines match { + case (0, line) :: lines if lines.forall(_._1 > 0) => (0, s"$prefix $line") :: lines + case lines => (0, prefix) :: lines.indent + } + } + def @:(prefix: String): Lines = { + lines match { + case (_, line) :: Nil => (0, prefix + " " + line) :: Nil + case lines => (0, prefix) :: lines.indent + } + } + } +} diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala new file mode 100644 index 00000000..dffa403c --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -0,0 +1,126 @@ +package mlscript.ucs.stages + +import mlscript.{Term, Var} +import mlscript.ucs.{syntax => s, core => c, PartialTerm} +import mlscript.utils._, shorthands._ +import mlscript.pretyper.{ScrutineeSymbol, SubValueSymbol, ValueSymbol} +import mlscript.ucs.DesugaringException +import mlscript.Message, Message.MessageContext + +trait Desugaring { self: mlscript.pretyper.Traceable => + @inline def desugar(term: s.TermSplit): c.Split = desugarTermSplit(term)(PartialTerm.Empty) + + private var nextScrutineeIndex: Int = 0 + + private def freshName(): Str = { + val thisIndex = nextScrutineeIndex + nextScrutineeIndex += 1 + s"scrut$thisIndex" // FIXME: use `freeVars` to avoid name collision. + } + + private def freshScrutinee(): Var = Var(freshName()) + + private def freshScrutinee(parentScrutinee: Var, parentClassName: Var, index: Int): Var = + Var(s"${parentScrutinee}$$${parentClassName}_${index.toString}") + + private val truePattern = c.Pattern.Class(Var("true"), N) + + private def flattenClassParameters(parentScrutinee: Var, parentClassName: Var, parameters: Opt[Ls[Opt[s.Pattern]]]): Opt[Ls[Opt[Var]]] -> Ls[Opt[Var -> s.ClassPattern]] = + parameters match { + case S(parameters) => + val (a, b) = parameters.zipWithIndex.unzip { + case (N, index) => N -> N + case (S(s.NamePattern(name)), index) => (S(name), N) + case (S(parameterPattern: s.ClassPattern), index) => + val scrutinee = freshScrutinee(parentScrutinee, parentClassName, index) + (S(scrutinee), Some((scrutinee, parameterPattern))) + case _ => ??? // Other patterns are not implemented yet. + } + (S(a), b) + case N => (N, Nil) + } + + private def flattenNestedSplitLet(pattern: s.ClassPattern, term: Var, tail: c.Split): c.Split = { + val (parameterBindings, childrenBindings) = flattenClassParameters(term, pattern.nme, pattern.parameters) + c.Branch(term, c.Pattern.Class(pattern.nme, parameterBindings), childrenBindings.foldRight(tail){ + case (N, tail) => tail + case (S((nme, parameterPattern)), tail) => + flattenNestedSplitLet(parameterPattern, nme, tail) + }) :: c.Split.Nil + } + + private def desugarTermSplit(split: s.TermSplit)(implicit termPart: PartialTerm): c.Split = + split match { + case s.Split.Cons(head, tail) => desugarTermBranch(head) ++ desugarTermSplit(tail) + case s.Split.Let(rec, nme, rhs, tail) => c.Split.Let(rec, nme, rhs, desugarTermSplit(tail)) + case s.Split.Else(default) => c.Split.Else(default); + case s.Split.Nil => c.Split.Nil + } + + private def desugarTermBranch(branch: s.TermBranch)(implicit termPart: PartialTerm): c.Split = + // Note: `Branch` is `(Term, Pattern, Either[Split, Term])`. + branch match { + case s.TermBranch.Boolean(condition, continuation) => + val `var` = freshScrutinee() + c.Split.Let( + rec = false, + name = `var`, + term = condition, + tail = c.Branch(`var`, truePattern, desugarTermSplit(continuation)) :: c.Split.Nil + ) + case s.TermBranch.Match(scrutinee, split) => + desugarPatternSplit(split)(termPart.addTerm(scrutinee, true).get) + case s.TermBranch.Left(left, continuation) => + desugarOperatorSplit(continuation)(termPart.addTerm(left, true)) + } + + private def desugarOperatorSplit(split: s.OperatorSplit)(implicit termPart: PartialTerm): c.Split = + split match { + case s.Split.Cons(head, tail) => desugarOperatorBranch(head) ++ desugarOperatorSplit(tail) + case s.Split.Let(rec, nme, rhs, tail) => c.Split.Let(rec, nme, rhs, desugarOperatorSplit(tail)) + case s.Split.Else(default) => c.Split.Else(default) + case s.Split.Nil => c.Split.Nil + } + + private def desugarOperatorBranch(branch: s.OperatorBranch)(implicit termPart: PartialTerm): c.Split = + branch match { + case s.OperatorBranch.Binary(op, split) => desugarTermSplit(split)(termPart.addOp(op)) + case s.OperatorBranch.Match(_, split) => desugarPatternSplit(split)(termPart.get) + } + + private def flattenNestedPattern(pattern: s.ClassPattern, scrutinee: Var, next: c.Split): c.Branch = { + val (parameterBindings, subPatterns) = flattenClassParameters(scrutinee, pattern.nme, pattern.parameters) + c.Branch(scrutinee, c.Pattern.Class(pattern.nme, parameterBindings), subPatterns.foldRight(next) { + case (None, next) => next + case (Some((nme, pattern)), next) => + flattenNestedPattern(pattern, nme, next) :: c.Split.Nil + }) + } + + private def desugarPatternBranch(scrutinee: Var, branch: s.PatternBranch): c.Branch = { + lazy val continuation = desugarTermSplit(branch.continuation)(PartialTerm.Empty) + branch.pattern match { + case s.AliasPattern(nme, pattern) => ??? + case s.LiteralPattern(literal) => ??? + case s.NamePattern(nme) => c.Branch(scrutinee, c.Pattern.Name(nme), continuation) + case pattern @ s.ClassPattern(nme, fields) => flattenNestedPattern(pattern, scrutinee, continuation) + case s.TuplePattern(fields) => ??? + case s.RecordPattern(entries) => ??? + } + } + + private def desugarPatternSplit(split: s.PatternSplit)(implicit scrutinee: Term): c.Split = { + def rec(scrutinee: Var, split: s.PatternSplit): c.Split = split match { + case s.Split.Cons(head, tail) => desugarPatternBranch(scrutinee, head) :: rec(scrutinee, tail) + case s.Split.Let(isRec, nme, rhs, tail) => c.Split.Let(isRec, nme, rhs, rec(scrutinee, tail)) + case s.Split.Else(default) => c.Split.Else(default) + case s.Split.Nil => c.Split.Nil + } + scrutinee match { + case nme: Var => rec(nme, split) + case other => + val alias = freshScrutinee() + c.Split.Let(false, alias, scrutinee, rec(alias, split)) + } + } +} diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala new file mode 100644 index 00000000..93662236 --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -0,0 +1,205 @@ + +package mlscript.ucs.stages + +import mlscript.ucs.{Lines, LinesOps} +import mlscript.ucs.core._ +import mlscript.ucs.helpers._ +import mlscript.pretyper.Symbol +import mlscript.{App, CaseOf, Fld, FldFlags, Let, Loc, Sel, Term, Tup, Var, StrLit} +import mlscript.{CaseBranches, Case, Wildcard, NoCases} +import mlscript.Message, Message.MessageContext +import mlscript.utils._, shorthands._ + +trait Normalization { self: mlscript.pretyper.Traceable => + import Normalization._ + + /** + * Normalize core abstract syntax to MLscript syntax. + * + * @param split the split to normalize + * @return the normalized term + */ + @inline def normalize(split: Split): Term = normalizeToTerm(split) + + private def normalizeToTerm(split: Split): Term = trace("normalizeToTerm") { + split match { + case Split.Cons(Branch(scrutinee, Pattern.Name(nme), continuation), tail) => + println(s"alias $scrutinee => $nme") + Let(false, nme, scrutinee, normalizeToTerm(continuation ++ tail)) + case Split.Cons(Branch(scrutinee, pattern @ Pattern.Literal(literal), continuation), tail) => + val trueBranch = normalizeToTerm(specialize(continuation ++ tail, Yes)(scrutinee.symbol, pattern)) + val falseBranch = normalizeToCaseBranches(specialize(tail, No)(scrutinee.symbol, pattern)) + CaseOf(scrutinee, Case(literal, trueBranch, falseBranch)) + // No class parameters. Easy + case Split.Cons(Branch(scrutinee, pattern @ Pattern.Class(nme, N), continuation), tail) => + println(s"match $scrutinee with $nme") + val trueBranch = normalizeToTerm(specialize(continuation ++ tail, Yes)(scrutinee.symbol, pattern)) + val falseBranch = normalizeToCaseBranches(specialize(tail, No)(scrutinee.symbol, pattern)) + CaseOf(scrutinee, Case(nme, trueBranch, falseBranch)) + case Split.Cons(Branch(scrutinee, pattern @ Pattern.Class(nme, S(parameters)), continuation), tail) => + println(s"match $scrutinee with $pattern") + val trueBranch = trace("compute true branch"){ + normalizeToTerm(specialize(continuation ++ tail, Yes)(scrutinee.symbol, pattern)) + }() + val falseBranch = trace("compute false branch"){ + normalizeToCaseBranches(specialize(tail, No)(scrutinee.symbol, pattern)) + }() + val unappliedVar = Var(s"args_${scrutinee.name}$$${nme.name}") + val trueBranchWithBindings = Let( + isRec = false, + name = unappliedVar, + rhs = { + val arguments = N -> Fld(FldFlags.empty, scrutinee) :: Nil + App(Sel(nme, Var("unapply")), Tup(arguments)) + }, + body = parameters.zipWithIndex.foldRight(trueBranch) { + case ((N, i), next) => next + case ((S(parameter), i), next) => Let(false, parameter, Sel(unappliedVar, Var(i.toString)), next) + } + ) + CaseOf(scrutinee, Case(nme, trueBranchWithBindings, falseBranch)) + case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => + throw new NormalizationException((msg"Unsupported pattern: ${pattern.toString}" -> pattern.toLoc) :: Nil) + case Split.Let(rec, nme, rhs, tail) => Let(rec, nme, rhs, normalizeToTerm(tail)) + case Split.Else(default) => default + case Split.Nil => StrLit("test") // FIXME + } + }() + + private def normalizeToCaseBranches(split: Split): CaseBranches = trace("normalizeToCaseBranches") { + split match { + // case Split.Cons(head, Split.Nil) => Case(head.pattern, normalizeToTerm(head.continuation), NoCases) + case other @ (Split.Cons(_, _) | Split.Let(_, _, _, _)) => Wildcard(normalizeToTerm(other)) + case Split.Else(default) => Wildcard(default) + case Split.Nil => NoCases + } + }() + + // Specialize `split` with the assumption that `scrutinee` matches `pattern`. + private def specialize + (split: Split, matchOrNot: MatchOrNot) + (implicit scrutinee: Symbol, pattern: Pattern): Split = + trace(s"S${matchOrNot} <== ${scrutinee.name} is ${pattern}") { + (matchOrNot, split) match { + // Name patterns are translated to let bindings. + case (Yes | No, Split.Cons(Branch(otherScrutineeVar, Pattern.Name(alias), continuation), tail)) => + Split.Let(false, alias, otherScrutineeVar, specialize(continuation, matchOrNot)) + // Class pattern. Positive. + case (Yes, split @ Split.Cons(head @ Branch(otherScrutineeVar, Pattern.Class(otherClassName, otherParameters), continuation), tail)) => + val otherScrutinee = otherScrutineeVar.symbol + lazy val specializedTail = { + println(s"specialized next") + specialize(tail, Yes) + } + if (scrutinee === otherScrutinee) { + println(s"scrutinee: ${scrutinee.name} === ${otherScrutinee.name}") + pattern match { + case Pattern.Class(className, parameters) => + if (className === otherClassName) { + println(s"class name: $className === $otherClassName") + // TODO: Subsitute parameters to otherParameters + specialize(continuation ++ tail, Yes) + } else { + println(s"class name: $className =/= $otherClassName") + specializedTail + } + case _ => throw new NormalizationException((msg"Incompatible: ${pattern.toString}" -> pattern.toLoc) :: Nil) + } + } else { + println(s"scrutinee: ${scrutinee.name} =/= ${otherScrutinee.name}") + if (scrutinee.name === otherScrutinee.name) { + println(s"WRONG!!!") + } + split.copy( + head = head.copy(continuation = specialize(continuation, Yes)), + tail = specializedTail + ) + } + // Class pattern. Negative + case (No, split @ Split.Cons(head @ Branch(otherScrutineeVar, Pattern.Class(otherClassName, otherParameters), continuation), tail)) => + val otherScrutinee = otherScrutineeVar.symbol + if (scrutinee === otherScrutinee) { + println(s"scrutinee: ${scrutinee.name} === ${otherScrutinee.name}") + pattern match { + case Pattern.Class(className, parameters) => + if (className === otherClassName) { + println(s"class name: $className === $otherClassName") + specialize(tail, No) // TODO: Subsitute parameters to otherParameters + } else { + println(s"class name: $className =/= $otherClassName") + split.copy( + // head = head.copy(continuation = specialize(continuation, No)), + tail = specialize(tail, No) + ) + } + case _ => throw new NormalizationException((msg"Incompatible: ${pattern.toString}" -> pattern.toLoc) :: Nil) + } + } else { + println(s"scrutinee: ${scrutinee.name} =/= ${otherScrutinee.name}") + split.copy( + head = head.copy(continuation = specialize(continuation, matchOrNot)), + tail = specialize(tail, matchOrNot) + ) + } + // Other patterns. Not implemented. + case (Yes | No, Split.Cons(Branch(otherScrutineeVar, pattern, continuation), tail)) => + throw new NormalizationException((msg"Unsupported pattern: ${pattern.toString}" -> pattern.toLoc) :: Nil) + case (Yes | No, let @ Split.Let(_, _, _, tail)) => + println("let binding, go next") + let.copy(tail = specialize(tail, matchOrNot)) + // Ending cases remain the same. + case (Yes | No, end @ (Split.Else(_) | Split.Nil)) => println("the end"); end + } // <-- end match + }(showSplit(s"S${matchOrNot} ==>", _)) + + /** + * Print a normalized term with indentation. + */ + @inline protected def printNormalizedTerm(term: Term): Unit = + println(showNormalizedTerm(term)) +} + +object Normalization { + private sealed abstract class MatchOrNot { + override def toString(): String = this match { + case Yes => "+" + case No => "-" + } + } + private final case object Yes extends MatchOrNot + private final case object No extends MatchOrNot + + class NormalizationException(val messages: Ls[Message -> Opt[Loc]]) extends Throwable { + def this(message: Message, location: Opt[Loc]) = this(message -> location :: Nil) + } + + /** + * Convert a normalized term to a string with indentation. + */ + def showNormalizedTerm(term: Term): String = { + def showTerm(term: Term): Lines = term match { + case let: Let => showLet(let) + case caseOf: CaseOf => showCaseOf(caseOf) + case other => (0, other.toString) :: Nil + } + def showCaseOf(caseOf: CaseOf): Lines = { + val CaseOf(trm, cases) = caseOf + s"$trm match" ##: showCaseBranches(cases) + } + def showCaseBranches(caseBranches: CaseBranches): Lines = + caseBranches match { + case Case(pat, rhs, tail) => + (s"case $pat =>" @: showTerm(rhs)) ++ showCaseBranches(tail) + case Wildcard(term) => s"case _ =>" @: showTerm(term) + case NoCases => Nil + } + def showVar(`var`: Var): Str = + // If the variable is associated with a symbol, mark it with an asterisk. + `var`.name + (`var`.symbolOption.fold("")(_ => "*")) + def showLet(let: Let): Lines = { + val Let(rec, nme, rhs, body) = let + (0, s"let ${showVar(nme)} = $rhs") :: showTerm(body) + } + showTerm(term).map { case (n, line) => " " * n + line }.mkString("\n") + } +} \ No newline at end of file diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala new file mode 100644 index 00000000..ff8c08cc --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -0,0 +1,60 @@ +package mlscript.ucs.stages + +import mlscript.{Case, CaseOf, Let, NoCases, Term, Var, Wildcard} +import mlscript.pretyper.{Symbol} +import mlscript.utils._, shorthands._ +import mlscript.CaseBranches + +trait PostProcessing { self: mlscript.pretyper.Traceable => + def postProcess(term: Term): Term = trace(s"postProcess <== ${term.describe}") { + // Normalized terms are constructed using `Let` and `CaseOf`. + term match { + case top @ CaseOf(scrutinee: Var, fst @ Case(pattern, body, NoCases)) => + println(s"found a UNARY case: $scrutinee is $pattern") + top.copy(cases = fst.copy(body = postProcess(body))) + case top @ CaseOf(scrutinee: Var, fst @ Case(pattern, trueBranch, snd @ Wildcard(falseBranch))) => + println(s"found a BINARY case: $scrutinee is $pattern") + println("post-processing the true branch") + val processedTrueBranch = postProcess(trueBranch) + println("post-processing the false branch") + val processedFalseBranch = postProcess(falseBranch) + // Check if the false branch is another `CaseOf` with the same scrutinee. + processedFalseBranch match { + case CaseOf(otherScrutinee: Var, actualFalseBranch) => + if (scrutinee.symbol === otherScrutinee.symbol) { + println(s"identical: $scrutinee === $otherScrutinee") + if (scrutinee.name =/= otherScrutinee.name) { + // TODO: solve name collision by creating a lifted `Let` + ??? + } + println(s"actual false branch: $actualFalseBranch") + top.copy(cases = fst.copy(body = processedTrueBranch, rest = actualFalseBranch)) + } else { + println(s"different: $scrutinee =/= $otherScrutinee") + top.copy(cases = fst.copy(body = processedTrueBranch, rest = snd.copy(body = processedFalseBranch))) + } + case other => top + } + // We recursively process the body of `Let` bindings. + case let @ Let(_, _, _, body) => let.copy(body = postProcess(body)) + // Otherwise, this is not a part of a normalized term. + case other => println(s"CANNOT post-process"); other + } + }() + + private def hasScrutinee(term: Term, symbol: Symbol): Bool = { + term match { + case CaseOf(scrutinee: Var, Case(pattern, trueBranch, Wildcard(falseBranch))) => + if (scrutinee.symbolOption.exists(_ === symbol)) true + else hasScrutinee(trueBranch, symbol) || hasScrutinee(falseBranch, symbol) + case Let(_, _, _, body) => hasScrutinee(body, symbol) + case _ => false + } + } + + private def separateCaseBranches(term: Term)(implicit scrutinee: Symbol): (CaseBranches, Term) = ??? +} + +object PostProcessing { + +} diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala new file mode 100644 index 00000000..8d34067b --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -0,0 +1,145 @@ +package mlscript.ucs.stages + +import mlscript.ucs.helpers +import mlscript.{If, IfBody, IfBlock, IfElse, IfLet, IfOpApp, IfOpsApp, IfThen} +import mlscript.{Term, Var, App, Tup, Lit, Fld, Loc} +import mlscript.ucs.syntax._ +import mlscript.Message, Message._ +import mlscript.utils._, shorthands._ +import mlscript.NuFunDef +import mlscript.PlainTup +import scala.collection.immutable + +/** + * Transform the parsed AST into an AST similar to the one in the paper. + * The parsed AST represents pattern with terms and does not distingiush + * `is` and `and` operators. + * The AST in the paper is more flexible. For example, it allows interleaved + * `let` bindings in operator splits. + */ +trait Transformation { self: mlscript.pretyper.Traceable => + import Transformation._ + + def transform(`if`: If, useNewDefs: Bool = true): TermSplit = + transformIfBody(`if`.body)(useNewDefs) ++ `if`.els.fold(Split.empty)(Split.default) + + import helpers.splitAnd + + private def transformIfBody(body: IfBody)(implicit useNewDefs: Bool): TermSplit = { + body match { + case IfThen(expr, rhs) => + splitAnd(expr).foldRight(Split.then(rhs)) { + case (OperatorIs(scrutinee, pattern), acc) => + TermBranch.Match(scrutinee, PatternBranch(rec(pattern), acc) |> Split.single) |> Split.single + case (test, acc) => TermBranch.Boolean(test, acc) |> Split.single + } + case IfLet(isRec, name, rhs, body) => rare + case IfElse(expr) => Split.then(expr) + case IfOpApp(lhs, Var("is"), rhs) => + splitAnd(lhs) match { + case init :+ last => + init.foldRight[TermSplit](TermBranch.Match(last, transformPatternMatching(rhs)) |> Split.single) { + case (OperatorIs(scrutinee, pattern), acc) => + TermBranch.Match(scrutinee, PatternBranch(rec(pattern), acc) |> Split.single) |> Split.single + case (test, acc) => TermBranch.Boolean(test, acc) |> Split.single + } + case _ => rare + } + case IfOpApp(lhs, op, rhs) => + splitAnd(lhs) match { + case init :+ last => + val first = TermBranch.Left(last, OperatorBranch.Binary(op, transformIfBody(rhs)) |> Split.single) |> Split.single + init.foldRight[TermSplit](first) { + case (OperatorIs(scrutinee, pattern), acc) => + TermBranch.Match(scrutinee, PatternBranch(rec(pattern), acc) |> Split.single) |> Split.single + case (test, acc) => TermBranch.Boolean(test, acc) |> Split.single + } + case _ => rare + } + case IfBlock(lines) => + lines.foldLeft(Split.empty[TermBranch]) { + case (acc, L(body)) => acc ++ transformIfBody(body) + case (acc, R(NuFunDef(S(rec), nme, _, _, L(rhs)))) => + acc ++ Split.Let(rec, nme, rhs, Split.Nil) + case (acc, R(statement)) => + throw new TransformException(msg"Unexpected statement in an if block", statement.toLoc) + } + case IfOpsApp(lhs, opsRhss) => + TermBranch.Left(lhs, Split.from(opsRhss.map(transformOperatorBranch))) |> Split.single + } + } + + private def transformOperatorBranch(opsRhs: Var -> IfBody)(implicit useNewDefs: Bool): OperatorBranch = + opsRhs match { + case (op @ Var("is"), rhs) => OperatorBranch.Match(op, transformPatternMatching(rhs)) + case (op, rhs) => OperatorBranch.Binary(op, transformIfBody(rhs)) + } + + private def transformPatternMatching(body: IfBody)(implicit useNewDefs: Bool): PatternSplit = { + body match { + case IfThen(expr, rhs) => + separatePattern(expr) match { + case (pattern, S(extraTest)) => + PatternBranch(pattern, transformIfBody(IfThen(extraTest, rhs))) |> Split.single + case (pattern, N) => + PatternBranch(pattern, Split.default(rhs)) |> Split.single + } + case IfOpApp(lhs, Var("and"), rhs) => + separatePattern(lhs) match { + case (pattern, S(extraTest)) => + PatternBranch(rec(lhs), ???) |> Split.single + case (pattern, N) => + PatternBranch(rec(lhs), transformIfBody(rhs)) |> Split.single + } + case IfOpApp(lhs, op, rhs) => ??? + case IfOpsApp(lhs, opsRhss) => ??? + case IfLet(rec, nme, rhs, body) => rare + case IfBlock(lines) => lines.foldLeft(Split.empty[PatternBranch]) { + case (acc, L(body)) => acc ++ transformPatternMatching(body) + case (acc, R(NuFunDef(S(rec), nme, _, _, L(rhs)))) => + acc ++ Split.Let(rec, nme, rhs, Split.Nil) + case (acc, R(statement)) => + throw new TransformException(msg"Unexpected statement in an if block", statement.toLoc) + } + case IfElse(expr) => Split.default(expr) + } + } + + private def rec(term: Term)(implicit useNewDefs: Bool): Pattern = term match { + case nme @ Var(name) if name.headOption.exists(_.isUpper) => ClassPattern(nme, N) + case nme: Var => NamePattern(nme) + case literal: Lit => LiteralPattern(literal) + case App(classNme @ Var(_), Tup(parameters)) => + ClassPattern(classNme, S(parameters.map { + case (_, Fld(_, Var("_"))) => N // Consider "_" as wildcard. + case (_, Fld(_, t)) => S(rec(t)) + })) + case Tup(fields) => TuplePattern(fields.map { + case _ -> Fld(_, Var("_")) => N // Consider "_" as wildcard. + case _ -> Fld(_, t ) => S(rec(t)) + }) + // TODO: Support more patterns. + case _ => throw new TransformException(msg"Unknown pattern", term.toLoc) + } + + private def separatePattern(term: Term)(implicit useNewDefs: Bool): (Pattern, Opt[Term]) = { + val (rawPattern, extraTest) = helpers.separatePattern(term, useNewDefs) + (rec(rawPattern), extraTest) + } + + private def rare: Nothing = throw new TransformException(msg"Wow, a rare case.", N) +} + +object Transformation { + private object OperatorIs { + def unapply(term: Term)(implicit useNewDefs: Bool): Opt[(Term, Term)] = term match { + case App(App(Var("is"), Tup(_ -> Fld(_, scrutinee) :: Nil)), Tup(_ -> Fld(_, pattern) :: Nil)) if !useNewDefs => S(scrutinee -> pattern) + case App(Var("is"), PlainTup(scrutinee, pattern)) => S(scrutinee -> pattern) + case _ => N + } + } + + class TransformException(val messages: Ls[Message -> Opt[Loc]]) extends Throwable { + def this(message: Message, location: Opt[Loc]) = this(message -> location :: Nil) + } +} \ No newline at end of file diff --git a/shared/src/main/scala/mlscript/ucs/stages/package.scala b/shared/src/main/scala/mlscript/ucs/stages/package.scala new file mode 100644 index 00000000..c0d7c81f --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/stages/package.scala @@ -0,0 +1,7 @@ +package mlscript.ucs + +import mlscript.{Term, Var} +import mlscript.pretyper.Symbol +import mlscript.utils._, shorthands._ + +package object stages diff --git a/shared/src/main/scala/mlscript/ucs/syntax.scala b/shared/src/main/scala/mlscript/ucs/syntax.scala new file mode 100644 index 00000000..1137baed --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/syntax.scala @@ -0,0 +1,152 @@ +package mlscript.ucs + +import mlscript.{Lit, Located, Term, Var} +import mlscript.utils._, shorthands._ +import scala.annotation.tailrec +import scala.collection.immutable + +package object syntax { + sealed abstract class Pattern extends Located { + override def toString(): String = this match { + case AliasPattern(nme, pattern) => s"$nme @ $pattern" + case LiteralPattern(literal) => literal.toString + case NamePattern(nme) => nme.toString + case ClassPattern(Var(name), N) => name + case ClassPattern(Var(name), S(parameters)) => + parameters.iterator.map(_.fold("_")(_.toString)).mkString(s"$name(", ", ", ")") + case TuplePattern(fields) => fields.iterator.map(_.fold("_")(_.toString)).mkString("(", ", ", ")") + case RecordPattern(Nil) => "{}" + case RecordPattern(entries) => entries.iterator.map { case (nme, als) => s"$nme: $als" }.mkString("{ ", ", ", " }") + } + } + final case class AliasPattern(nme: Var, pattern: Pattern) extends Pattern { + override def children: List[Located] = nme :: pattern :: Nil + } + final case class LiteralPattern(literal: Lit) extends Pattern { + override def children: List[Located] = literal :: Nil + } + final case class NamePattern(nme: Var) extends Pattern { + override def children: List[Located] = nme :: Nil + } + final case class ClassPattern(val nme: Var, val parameters: Opt[List[Opt[Pattern]]]) extends Pattern { + override def children: List[Located] = nme :: parameters.fold(List.empty[Located])(_.flatten) + } + final case class TuplePattern(fields: List[Opt[Pattern]]) extends Pattern { + override def children: List[Located] = fields.flatten + } + final case class RecordPattern(entries: List[(Var -> Pattern)]) extends Pattern { + override def children: List[Located] = entries.iterator.flatMap { case (nme, als) => nme :: als :: Nil }.toList + } + + sealed abstract class Split[+SomeBranch <: Branch] extends Located { + def ++[OtherBranch >: SomeBranch <: Branch](that: Split[OtherBranch]): Split[OtherBranch] = this match { + case Split.Cons(head, tail) => Split.Cons(head, tail ++ that) + case Split.Let(rec, nme, rhs, tail) => Split.Let(rec, nme, rhs, tail ++ that) + case Split.Else(_) => this + case Split.Nil => that + } + def ::[OtherBranch >: SomeBranch <: Branch](head: OtherBranch): Split[OtherBranch] = Split.Cons(head, this) + } + object Split { + import immutable.{Nil => LNil} + final case class Cons[SomeBranch <: Branch](head: SomeBranch, tail: Split[SomeBranch]) extends Split[SomeBranch] { + override def children: List[Located] = head :: tail :: LNil + } + final case class Let[SomeBranch <: Branch](rec: Bool, nme: Var, rhs: Term, tail: Split[SomeBranch]) extends Split[SomeBranch] { + override def children: List[Located] = nme :: rhs :: tail :: LNil + } + final case class Else(term: Term) extends Split[Nothing] { + override def children: List[Located] = term :: LNil + } + final case object Nil extends Split[Nothing] { + override def children: List[Located] = LNil + } + + def empty[SomeBranch <: Branch]: Split[SomeBranch] = Nil + def single[SomeBranch <: Branch](branch: SomeBranch): Split[SomeBranch] = Cons(branch, Nil) + def `then`(term: Term): Split[TermBranch] = Else(term) + def default[SomeBranch <: Branch](term: Term): Split[SomeBranch] = Else(term) + def from[SomeBranch <: Branch](branches: Iterable[SomeBranch]): Split[SomeBranch] = + branches.foldRight(Nil: Split[SomeBranch])(Cons.apply) + } + + sealed abstract class Branch extends Located + + sealed abstract class TermBranch extends Branch + object TermBranch { + final case class Boolean(test: Term, continuation: TermSplit) extends TermBranch { + override def children: List[Located] = test :: continuation :: Nil + } + final case class Match(scrutinee: Term, continuation: PatternSplit) extends TermBranch { + override def children: List[Located] = scrutinee :: continuation :: Nil + } + final case class Left(left: Term, continuation: OperatorSplit) extends TermBranch { + override def children: List[Located] = left :: continuation :: Nil + } + } + type TermSplit = Split[TermBranch] + + sealed abstract class OperatorBranch extends Branch { + val operator: Var + } + object OperatorBranch { + final case class Match(override val operator: Var, continuation: PatternSplit) extends OperatorBranch { + override def children: List[Located] = operator :: continuation :: Nil + } + final case class Binary(override val operator: Var, continuation: TermSplit) extends OperatorBranch { + override def children: List[Located] = operator :: continuation :: Nil + } + } + type OperatorSplit = Split[OperatorBranch] + + final case class PatternBranch(val pattern: Pattern, val continuation: TermSplit) extends Branch { + override def children: List[Located] = pattern :: continuation :: Nil + } + type PatternSplit = Split[PatternBranch] + + def printTermSplit(split: TermSplit): Str = { + // TODO: tailrec + def termSplit(split: TermSplit, isFirst: Bool, isTopLevel: Bool): Lines = split match { + case Split.Cons(head, tail) => (termBranch(head) match { + case (n, line) :: tail => (n, (if (isTopLevel) "" else "and ") + s"$line") :: tail + case Nil => Nil + }) ::: termSplit(tail, false, isTopLevel) + case Split.Let(_, nme, rhs, tail) => (0, s"let $nme = $rhs") :: termSplit(tail, false, isTopLevel) + case Split.Else(term) => (if (isFirst) (0, s"then $term") else (0, s"else $term")) :: Nil + case Split.Nil => Nil + } + def termBranch(branch: TermBranch): Lines = branch match { + case TermBranch.Boolean(test, continuation) => + s"$test" #: termSplit(continuation, true, false) + case TermBranch.Match(scrutinee, continuation) => + s"$scrutinee is" #: patternSplit(continuation) + case TermBranch.Left(left, continuation) => + s"$left" #: operatorSplit(continuation) + } + def patternSplit(split: PatternSplit): Lines = split match { + case Split.Cons(head, tail) => patternBranch(head) ::: patternSplit(tail) + case Split.Let(rec, nme, rhs, tail) => (0, s"let $nme = $rhs") :: patternSplit(tail) + case Split.Else(term) => (0, s"else $term") :: Nil + case Split.Nil => Nil + } + def operatorSplit(split: OperatorSplit): Lines = split match { + case Split.Cons(head, tail) => operatorBranch(head) ::: operatorSplit(tail) + case Split.Let(rec, nme, rhs, tail) => (0, s"let $nme = $rhs") :: operatorSplit(tail) + case Split.Else(term) => (0, s"else $term") :: Nil + case Split.Nil => Nil + } + def operatorBranch(branch: OperatorBranch): Lines = + s"${branch.operator}" #: (branch match { + case OperatorBranch.Match(_, continuation) => patternSplit(continuation) + case OperatorBranch.Binary(_, continuation) => termSplit(continuation, true, false) + }) + def patternBranch(branch: PatternBranch): Lines = { + val PatternBranch(pattern, continuation) = branch + termSplit(continuation, true, false) match { + case (0, line) :: lines => (0, s"$pattern $line") :: lines + case lines => (0, pattern.toString) :: lines + } + } + ("if" #: termSplit(split, true, true)).iterator.map { case (n, line) => " " * n + line }.mkString("\n") + } +} diff --git a/shared/src/test/diff/mlscript/Basics.mls b/shared/src/test/diff/mlscript/Basics.mls index 2af98bf0..f6ec00c7 100644 --- a/shared/src/test/diff/mlscript/Basics.mls +++ b/shared/src/test/diff/mlscript/Basics.mls @@ -96,7 +96,7 @@ def f (x y z) = add x y //│ ╙── ^^^^^ //│ f: error -> int //│ Code generation encountered an error: -//│ term App(App(Var(x), Tup(_: Var(y))), Tup(_: Var(z))) is not a valid pattern +//│ term App(App(Var(x), Tup((N, Var(y)))), Tup((N, Var(z)))) is not a valid pattern f 1 //│ res: int diff --git a/shared/src/test/diff/mlscript/ByNameByValue.mls b/shared/src/test/diff/mlscript/ByNameByValue.mls index 3e876407..be234e2c 100644 --- a/shared/src/test/diff/mlscript/ByNameByValue.mls +++ b/shared/src/test/diff/mlscript/ByNameByValue.mls @@ -16,7 +16,7 @@ def incr x = x.a <- x.a + 1 def gensym = let n = { mut a = 0 } in fun () -> (incr n, n) //│ Parsed: def gensym: let n = {mut a: 0} in () => [incr(n,), n,]; //│ Desugared: def gensym: let n = {mut a: 0} in () => [incr(n,), n,] -//│ AST: Def(false, gensym, Let(false, n, Rcd(Var(a) = IntLit(0)), Lam(Tup(), Tup(_: App(Var(incr), Tup(_: Var(n))), _: Var(n)))), true) +//│ AST: Def(false, gensym, Let(false, n, Rcd(Var(a) = IntLit(0)), Lam(Tup(), Tup((N, App(Var(incr), Tup((N, Var(n))))), (N, Var(n))))), true) //│ // Query 1 //│ globalThis.gensym = function gensym() { //│ return (((n) => () => [ @@ -35,7 +35,7 @@ def gensym = let n = { mut a = 0 } in fun () -> (incr n, n) gensym1 = let n = { mut a = 0 } in fun () -> (incr n, n) //│ Parsed: let gensym1 = let n = {mut a: 0} in () => [incr(n,), n,]; //│ Desugared: def gensym1: let n = {mut a: 0} in () => [incr(n,), n,] -//│ AST: Def(false, gensym1, Let(false, n, Rcd(Var(a) = IntLit(0)), Lam(Tup(), Tup(_: App(Var(incr), Tup(_: Var(n))), _: Var(n)))), false) +//│ AST: Def(false, gensym1, Let(false, n, Rcd(Var(a) = IntLit(0)), Lam(Tup(), Tup((N, App(Var(incr), Tup((N, Var(n))))), (N, Var(n))))), false) //│ // Query 1 //│ globalThis.gensym1 = ((n) => () => [ //│ incr(n), diff --git a/shared/src/test/diff/mlscript/MultiArgs.mls b/shared/src/test/diff/mlscript/MultiArgs.mls index 62e7a809..0c5f8e7d 100644 --- a/shared/src/test/diff/mlscript/MultiArgs.mls +++ b/shared/src/test/diff/mlscript/MultiArgs.mls @@ -75,9 +75,9 @@ f = fun (x, y) -> add x y f(1, 2) //│ Parsed: let f = (x, y,) => add(x,)(y,); f(1, 2,); //│ Desugared: def f: (x, y,) => add(x,)(y,) -//│ AST: Def(false, f, Lam(Tup(_: Var(x), _: Var(y)), App(App(Var(add), Tup(_: Var(x))), Tup(_: Var(y)))), false) +//│ AST: Def(false, f, Lam(Tup((N, Var(x)), (N, Var(y))), App(App(Var(add), Tup((N, Var(x)))), Tup((N, Var(y))))), false) //│ Desugared: f(1, 2,) -//│ AST: App(Var(f), Tup(_: IntLit(1), _: IntLit(2))) +//│ AST: App(Var(f), Tup((N, IntLit(1)), (N, IntLit(2)))) //│ f: (int, int,) -> int //│ = [Function: f] //│ res: int @@ -119,9 +119,9 @@ f = fun ((x, y)) -> add x y f((1, 2)) //│ Parsed: let f = ('(' [x, y,] ')',) => add(x,)(y,); f('(' [1, 2,] ')',); //│ Desugared: def f: ('(' [x, y,] ')',) => add(x,)(y,) -//│ AST: Def(false, f, Lam(Tup(_: Bra(rcd = false, Tup(_: Var(x), _: Var(y)))), App(App(Var(add), Tup(_: Var(x))), Tup(_: Var(y)))), false) +//│ AST: Def(false, f, Lam(Tup((N, Bra(rcd = false, Tup((N, Var(x)), (N, Var(y)))))), App(App(Var(add), Tup((N, Var(x)))), Tup((N, Var(y))))), false) //│ Desugared: f('(' [1, 2,] ')',) -//│ AST: App(Var(f), Tup(_: Bra(rcd = false, Tup(_: IntLit(1), _: IntLit(2))))) +//│ AST: App(Var(f), Tup((N, Bra(rcd = false, Tup((N, IntLit(1)), (N, IntLit(2))))))) //│ f: ((int, int,),) -> int //│ = [Function: f1] //│ res: int diff --git a/shared/src/test/diff/mlscript/Ops.mls b/shared/src/test/diff/mlscript/Ops.mls index 837b2c09..b278598e 100644 --- a/shared/src/test/diff/mlscript/Ops.mls +++ b/shared/src/test/diff/mlscript/Ops.mls @@ -3,7 +3,7 @@ 2 + 2 //│ Parsed: +(2,)(2,); //│ Desugared: +(2,)(2,) -//│ AST: App(App(Var(+), Tup(_: IntLit(2))), Tup(_: IntLit(2))) +//│ AST: App(App(Var(+), Tup((N, IntLit(2)))), Tup((N, IntLit(2)))) //│ res: int //│ = 4 @@ -11,7 +11,7 @@ 1 + 2 * 2 + 3 //│ Parsed: +(+(1,)(*(2,)(2,),),)(3,); //│ Desugared: +(+(1,)(*(2,)(2,),),)(3,) -//│ AST: App(App(Var(+), Tup(_: App(App(Var(+), Tup(_: IntLit(1))), Tup(_: App(App(Var(*), Tup(_: IntLit(2))), Tup(_: IntLit(2))))))), Tup(_: IntLit(3))) +//│ AST: App(App(Var(+), Tup((N, App(App(Var(+), Tup((N, IntLit(1)))), Tup((N, App(App(Var(*), Tup((N, IntLit(2)))), Tup((N, IntLit(2)))))))))), Tup((N, IntLit(3)))) //│ res: int //│ = 8 @@ -20,7 +20,7 @@ 1 + 2 / 2 + 3 //│ Parsed: +(+(1,)(/(2,)(2,),),)(3,); //│ Desugared: +(+(1,)(/(2,)(2,),),)(3,) -//│ AST: App(App(Var(+), Tup(_: App(App(Var(+), Tup(_: IntLit(1))), Tup(_: App(App(Var(/), Tup(_: IntLit(2))), Tup(_: IntLit(2))))))), Tup(_: IntLit(3))) +//│ AST: App(App(Var(+), Tup((N, App(App(Var(+), Tup((N, IntLit(1)))), Tup((N, App(App(Var(/), Tup((N, IntLit(2)))), Tup((N, IntLit(2)))))))))), Tup((N, IntLit(3)))) //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.20: 1 + 2 / 2 + 3 //│ ║ ^^^^^^^^^ @@ -36,7 +36,7 @@ 1 |> 2 || 3 //│ Parsed: ||(|>(1,)(2,),)(3,); //│ Desugared: ||(|>(1,)(2,),)(3,) -//│ AST: App(App(Var(||), Tup(_: App(App(Var(|>), Tup(_: IntLit(1))), Tup(_: IntLit(2))))), Tup(_: IntLit(3))) +//│ AST: App(App(Var(||), Tup((N, App(App(Var(|>), Tup((N, IntLit(1)))), Tup((N, IntLit(2))))))), Tup((N, IntLit(3)))) //│ ╔══[ERROR] identifier not found: |> //│ ║ l.36: 1 |> 2 || 3 //│ ╙── ^^ @@ -54,7 +54,7 @@ true || false && true || false //│ Parsed: ||(||(true,)(&&(false,)(true,),),)(false,); //│ Desugared: ||(||(true,)(&&(false,)(true,),),)(false,) -//│ AST: App(App(Var(||), Tup(_: App(App(Var(||), Tup(_: Var(true))), Tup(_: App(App(Var(&&), Tup(_: Var(false))), Tup(_: Var(true))))))), Tup(_: Var(false))) +//│ AST: App(App(Var(||), Tup((N, App(App(Var(||), Tup((N, Var(true)))), Tup((N, App(App(Var(&&), Tup((N, Var(false)))), Tup((N, Var(true)))))))))), Tup((N, Var(false)))) //│ res: bool //│ = true diff --git a/shared/src/test/diff/mlscript/Repro.mls b/shared/src/test/diff/mlscript/Repro.mls index 539c2378..97fb1eac 100644 --- a/shared/src/test/diff/mlscript/Repro.mls +++ b/shared/src/test/diff/mlscript/Repro.mls @@ -1,3 +1,17 @@ :NewDefs +:PreTyper +class Point(x: Int, y: Int) +//│ class Point(x: Int, y: Int) +fun length(p) = if p is Point(x, y) then x + y +//│ fun length: Point -> Int + +let xy = Point.unapply(Point(0, 1)) +xy.1 +//│ let xy: [Int, Int] +//│ Int +//│ xy +//│ = [ 0, 1 ] +//│ res +//│ = 1 diff --git a/shared/src/test/diff/nu/LamPatterns.mls b/shared/src/test/diff/nu/LamPatterns.mls index 58aa85eb..c61d5f19 100644 --- a/shared/src/test/diff/nu/LamPatterns.mls +++ b/shared/src/test/diff/nu/LamPatterns.mls @@ -14,7 +14,7 @@ Some(x) => x //│ ╙── ^ //│ error -> error //│ Code generation encountered an error: -//│ term App(Var(Some), Tup(_: Var(x))) is not a valid pattern +//│ term App(Var(Some), Tup((N, Var(x)))) is not a valid pattern :js // FIXME type diff --git a/shared/src/test/diff/nu/OverrideShorthand.mls b/shared/src/test/diff/nu/OverrideShorthand.mls index 25db2319..b6273930 100644 --- a/shared/src/test/diff/nu/OverrideShorthand.mls +++ b/shared/src/test/diff/nu/OverrideShorthand.mls @@ -10,7 +10,7 @@ class Pair(lhs: Int, rhs: Int) :e fun f(override Pair(x, y)) = x + y //│ |#fun| |f|(|#override| |Pair|(|x|,| |y|)|)| |#=| |x| |+| |y| -//│ AST: TypingUnit(NuFunDef(None, f, None, [], Lam(Tup(_: Var(_$0)), If(IfOpApp(Var(_$0), Var(is), IfThen(App(Var(Pair), Tup(_: Var(x), _: Var(y))), App(Var(+), Tup(_: Var(x), _: Var(y))), Some(App(Sel(Super(), f), Tup(_: Var(_$0)))))))) +//│ AST: TypingUnit(NuFunDef(None, f, None, [], Lam(Tup((N, Var(_$0))), If(IfOpApp(Var(_$0), Var(is), IfThen(App(Var(Pair), Tup((N, Var(x)), (N, Var(y)))), App(Var(+), Tup((N, Var(x)), (N, Var(y)))), Some(App(Sel(Super(), f), Tup((N, Var(_$0))))))))) //│ Parsed: fun f = (_$0,) => if _$0 is (Pair(x, y,)) then +(x, y,) else (super).f(_$0,); //│ ╔══[ERROR] identifier not found: super //│ ║ l.11: fun f(override Pair(x, y)) = x + y @@ -46,7 +46,7 @@ fun f(override Pair(x, y), z) = x + y //│ ╙── ^ //│ fun f: (error, anything) -> Int //│ Code generation encountered an error: -//│ term App(Var(Pair), Tup(_: Var(x), _: Var(y))) is not a valid pattern +//│ term App(Var(Pair), Tup((N, Var(x)), (N, Var(y)))) is not a valid pattern // TODO diff --git a/shared/src/test/diff/nu/RightAssocOps.mls b/shared/src/test/diff/nu/RightAssocOps.mls index 15a34aaa..46187337 100644 --- a/shared/src/test/diff/nu/RightAssocOps.mls +++ b/shared/src/test/diff/nu/RightAssocOps.mls @@ -30,7 +30,7 @@ fun (++) conc(xs, ys) = [xs, ys] :p 1 +: "a" ++ "b" :+ 2 //│ |1| |+:| |"a"| |++| |"b"| |:+| |2| -//│ AST: TypingUnit(App(Var(+:), Tup(_: IntLit(1), _: App(Var(:+), Tup(_: App(Var(++), Tup(_: StrLit(a), _: StrLit(b))), _: IntLit(2)))))) +//│ AST: TypingUnit(App(Var(+:), Tup((N, IntLit(1)), (N, App(Var(:+), Tup((N, App(Var(++), Tup((N, StrLit(a)), (N, StrLit(b))))), (N, IntLit(2)))))))) //│ Parsed: +:(1, :+(++("a", "b",), 2,),); //│ [[Int], [["a", "b"], [Int]]] //│ res @@ -39,7 +39,7 @@ fun (++) conc(xs, ys) = [xs, ys] :p 1 +: "a" :+ 2 ++ "b" //│ |1| |+:| |"a"| |:+| |2| |++| |"b"| -//│ AST: TypingUnit(App(Var(+:), Tup(_: IntLit(1), _: App(Var(++), Tup(_: App(Var(:+), Tup(_: StrLit(a), _: IntLit(2))), _: StrLit(b)))))) +//│ AST: TypingUnit(App(Var(+:), Tup((N, IntLit(1)), (N, App(Var(++), Tup((N, App(Var(:+), Tup((N, StrLit(a)), (N, IntLit(2))))), (N, StrLit(b)))))))) //│ Parsed: +:(1, ++(:+("a", 2,), "b",),); //│ [[Int], [["a", [Int]], "b"]] //│ res @@ -49,7 +49,7 @@ fun (++) conc(xs, ys) = [xs, ys] :e 1 +: "a" ++ 2 +: "b" //│ |1| |+:| |"a"| |++| |2| |+:| |"b"| -//│ AST: TypingUnit(App(Var(+:), Tup(_: IntLit(1), _: App(Var(+:), Tup(_: App(Var(++), Tup(_: StrLit(a), _: IntLit(2))), _: StrLit(b)))))) +//│ AST: TypingUnit(App(Var(+:), Tup((N, IntLit(1)), (N, App(Var(+:), Tup((N, App(Var(++), Tup((N, StrLit(a)), (N, IntLit(2))))), (N, StrLit(b)))))))) //│ Parsed: +:(1, +:(++("a", 2,), "b",),); //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.50: 1 +: "a" ++ 2 +: "b" diff --git a/shared/src/test/diff/pretyper/Declarations.mls b/shared/src/test/diff/pretyper/Declarations.mls new file mode 100644 index 00000000..1401303e --- /dev/null +++ b/shared/src/test/diff/pretyper/Declarations.mls @@ -0,0 +1,89 @@ +:NewDefs +:PreTyper +:NoJS + +:dpt +fun test(x, y) = x + y +//│ process <== : {fun test} +//│ | visitTypingUnit <== : {fun test} +//│ | | 1. scope = {test} +//│ | | 2. scope = {test} +//│ | | visitFunction <== test +//│ | | | visitTerm <== Lam(_, _) +//│ | | | | visitTerm <== App(_, _) +//│ | | | | | visitTerm <== Var("+") +//│ | | | | | | visitVar(name = "+") +//│ | | | | | | | resolveVar(name = "+") +//│ | | | | | visitTerm ==> Var("+") +//│ | | | | | visitTerm <== Tup(_, _) +//│ | | | | | | visitTerm <== Var("x") +//│ | | | | | | | visitVar(name = "x") +//│ | | | | | | | | resolveVar(name = "x") +//│ | | | | | | visitTerm ==> Var("x") +//│ | | | | | | visitTerm <== Var("y") +//│ | | | | | | | visitVar(name = "y") +//│ | | | | | | | | resolveVar(name = "y") +//│ | | | | | | visitTerm ==> Var("y") +//│ | | | | | visitTerm ==> Tup(_, _) +//│ | | | | visitTerm ==> App(_, _) +//│ | | | visitTerm ==> Lam(_, _) +//│ | | visitFunction ==> test +//│ | visitTypingUnit ==> test +//│ process ==> test +//│ fun test: (Int, Int) -> Int + +:dpt +// Functions are hoisted. +let y = id(42) +fun id(x) = x +//│ process <== : {let y; fun id} +//│ | visitTypingUnit <== : {let y; fun id} +//│ | | 1. scope = {id} +//│ | | visitLetBinding(rec = false, y) +//│ | | 2. scope = {id} +//│ | | visitFunction <== id +//│ | | | visitTerm <== Lam(_, _) +//│ | | | | visitTerm <== Var("x") +//│ | | | | | visitVar(name = "x") +//│ | | | | | | resolveVar(name = "x") +//│ | | | | visitTerm ==> Var("x") +//│ | | | visitTerm ==> Lam(_, _) +//│ | | visitFunction ==> id +//│ | visitTypingUnit ==> id, y +//│ process ==> id, y +//│ let y: 42 | 'a +//│ fun id: forall 'b. ('a & 'b) -> (42 | 'b) + +:dpt +// Function bodies can access variables declare after them. +fun q(x) = x + p +let p = 0 +//│ process <== : {fun q; let p} +//│ | visitTypingUnit <== : {fun q; let p} +//│ | | 1. scope = {q} +//│ | | visitLetBinding(rec = false, p) +//│ | | 2. scope = {q} +//│ | | visitFunction <== q +//│ | | | visitTerm <== Lam(_, _) +//│ | | | | visitTerm <== App(_, _) +//│ | | | | | visitTerm <== Var("+") +//│ | | | | | | visitVar(name = "+") +//│ | | | | | | | resolveVar(name = "+") +//│ | | | | | visitTerm ==> Var("+") +//│ | | | | | visitTerm <== Tup(_, _) +//│ | | | | | | visitTerm <== Var("x") +//│ | | | | | | | visitVar(name = "x") +//│ | | | | | | | | resolveVar(name = "x") +//│ | | | | | | visitTerm ==> Var("x") +//│ | | | | | | visitTerm <== Var("p") +//│ | | | | | | | visitVar(name = "p") +//│ | | | | | | | | resolveVar(name = "p") +//│ | | | | | | visitTerm ==> Var("p") +//│ | | | | | visitTerm ==> Tup(_, _) +//│ | | | | visitTerm ==> App(_, _) +//│ | | | visitTerm ==> Lam(_, _) +//│ | | visitFunction ==> q +//│ | visitTypingUnit ==> q, p +//│ process ==> q, p +//│ fun q: Int -> Int +//│ let p: 0 diff --git a/shared/src/test/diff/pretyper/ucs/DualOption.mls b/shared/src/test/diff/pretyper/ucs/DualOption.mls new file mode 100644 index 00000000..2ebf1e75 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/DualOption.mls @@ -0,0 +1,135 @@ +:NewDefs +:PreTyper + +class Some[T](value: T) +module None +type Option[T] = Some[T] | None +class Pair[A, B](x: A, y: B) +//│ class Some[T](value: T) +//│ module None +//│ type Option[T] = None | Some[T] +//│ class Pair[A, B](x: A, y: B) + +// All `add_n` functions should be inferred to have the same type. + +fun add_1(x, y) = + if + x is Some(xv) and y is Some(yv) then xv + yv + x is Some(xv) and y is None then xv + x is None and y is Some(yv) then yv + x is None and y is None then 0 +//│ fun add_1: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) + +add_1(None, None) +add_1(Some(5), None) +add_1(None, Some(9)) +add_1(Some(5), Some(9)) +//│ Int +//│ res +//│ = 0 +//│ res +//│ = 5 +//│ res +//│ = 9 +//│ res +//│ = 14 + +fun add_2(x, y) = + if x is + Some(xv) and y is + Some(yv) then xv + yv + None then xv + None and y is + Some(yv) then yv + None then 0 +//│ fun add_2: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) + +add_2(None, None) +add_2(Some(5), None) +add_2(None, Some(9)) +add_2(Some(5), Some(9)) +//│ Int +//│ res +//│ = 0 +//│ res +//│ = 5 +//│ res +//│ = 9 +//│ res +//│ = 14 + +fun add_3(x, y) = + if Pair(x, y) is + Pair(Some(xv), Some(yv)) then xv + yv + Pair(Some(xv), None) then xv + Pair(None, Some(yv)) then yv + Pair(None, None) then 0 +//│ fun add_3: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) + +add_3(None, None) +add_3(Some(5), None) +add_3(None, Some(9)) +add_3(Some(5), Some(9)) +//│ Int +//│ res +//│ = 0 +//│ res +//│ = 5 +//│ res +//│ = 9 +//│ res +//│ = 14 + +fun add_4(x, y) = + if + x + is + Some(xv) and + y + is + Some(yv) then xv + yv + is + None then xv + None and + y + is + Some(yv) then yv + is + None then 0 +//│ fun add_4: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) + +add_4(None, None) +add_4(Some(5), None) +add_4(None, Some(9)) +add_4(Some(5), Some(9)) +//│ Int +//│ res +//│ = 0 +//│ res +//│ = 5 +//│ res +//│ = 9 +//│ res +//│ = 14 + +fun add_5(x, y) = + if + x is Some(xv) and y is Some(yv) then xv + yv + y is None and x is Some(xv) then xv + x is None and y is Some(yv) then yv + y is None and x is None then 0 +//│ fun add_5: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) + +add_5(None, None) +add_5(Some(5), None) +add_5(None, Some(9)) +add_5(Some(5), Some(9)) +//│ Int +//│ res +//│ = 0 +//│ res +//│ = 5 +//│ res +//│ = 9 +//│ res +//│ = 14 diff --git a/shared/src/test/diff/pretyper/ucs/NamePattern.mls b/shared/src/test/diff/pretyper/ucs/NamePattern.mls new file mode 100644 index 00000000..bbe1fa40 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/NamePattern.mls @@ -0,0 +1,9 @@ +:PreTyper + +fun id(x) = x +//│ fun id: forall 'a. 'a -> 'a + +if id(0) is t then t +//│ 0 +//│ res +//│ = 0 diff --git a/shared/src/test/diff/pretyper/ucs/RecordPattern.mls b/shared/src/test/diff/pretyper/ucs/RecordPattern.mls new file mode 100644 index 00000000..ed86bd8a --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/RecordPattern.mls @@ -0,0 +1,8 @@ +:PreTyper + +// FIXME +fun take_1(p) = + if p is + { x, y } then x + y + else 0 +//│ /!!!\ Uncaught error: mlscript.ucs.stages.Transformation$TransformException diff --git a/shared/src/test/diff/pretyper/ucs/TransfromUCS.mls b/shared/src/test/diff/pretyper/ucs/TransfromUCS.mls new file mode 100644 index 00000000..2facf100 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/TransfromUCS.mls @@ -0,0 +1,45 @@ +:PreTyper + +class Some[T](value: T) +module None +type Option[T] = Some[T] | None +//│ class Some[T](value: T) +//│ module None +//│ type Option[T] = None | Some[T] + +class Cons[T](head: T, tail: List[T]) +module Nil +type List[T] = Cons[T] | Nil +//│ class Cons[T](head: T, tail: List[T]) +//│ module Nil +//│ type List[T] = Cons[T] | Nil + +abstract class Either[out A, out B] +class Left[out A, out B](value: A) extends Either[A, B] +class Right[out A, out B](value: B) extends Either[A, B] +//│ abstract class Either[A, B] +//│ class Left[A, B](value: A) extends Either +//│ class Right[A, B](value: B) extends Either + +class Pair[A, B](x: A, y: B) { + fun mapFirst[C](f: A -> C): Pair[C, B] = Pair(f(x), y) + fun mapSecond[C](f: B -> C): Pair[A, C] = Pair(x, f(y)) +} +//│ class Pair[A, B](x: A, y: B) { +//│ fun mapFirst: forall 'C. (f: A -> 'C) -> Pair['C, B] +//│ fun mapSecond: forall 'C0. (f: B -> 'C0) -> Pair[A, 'C0] +//│ } + +fun zipWith(f, xs, ys) = + if xs is + Cons(x, xs) and ys is Cons(y, ys) and zipWith(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) + Nil and ys is Nil then Some(Nil) + else None +//│ fun zipWith: forall 'T 'T0 'T1. (('T, 'T0) -> 'T1, Cons['T] | Object & ~#Cons, Cons['T0] | Object & ~#Cons) -> (None | Some[Cons['T1] | Nil]) + + +fun getOrElse[T](x: Option[T], default: T): T = + if x is + Some(value) then value + None then default +//│ fun getOrElse: forall 'T. (x: Option['T], default: 'T) -> 'T diff --git a/shared/src/test/diff/pretyper/ucs/TuplePattern.mls b/shared/src/test/diff/pretyper/ucs/TuplePattern.mls new file mode 100644 index 00000000..99cad895 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/TuplePattern.mls @@ -0,0 +1,10 @@ +:PreTyper + +// FIXME +fun flex(x) = + if x is + [a, b, c] then + a + b + c + else + 0 +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing diff --git a/shared/src/test/diff/pretyper/ucs/Unapply.mls b/shared/src/test/diff/pretyper/ucs/Unapply.mls new file mode 100644 index 00000000..3bd099ff --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/Unapply.mls @@ -0,0 +1,13 @@ +:NewDefs +:PreTyper + +class Point(x: Int, y: Int, z: Int) +//│ class Point(x: Int, y: Int, z: Int) + +fun f_0(p) = if p is Point(x, _, z) then x + z +//│ fun f_0: Point -> Int + +f_0(Point(1, 2, 3)) +//│ Int +//│ res +//│ = 4 diff --git a/shared/src/test/diff/pretyper/ucs/Unconditional.mls b/shared/src/test/diff/pretyper/ucs/Unconditional.mls new file mode 100644 index 00000000..804033b2 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/Unconditional.mls @@ -0,0 +1,26 @@ +:PreTyper + +class Point(x: Int, y: Int) +class Rectangle(x: Int, y: Int, width: Int, height: Int) +//│ class Point(x: Int, y: Int) +//│ class Rectangle(x: Int, y: Int, width: Int, height: Int) + +fun sum(p) = if p is Point(x, y) then x + y +//│ fun sum: Point -> Int + +sum(Point(1, 2)) +//│ Int +//│ res +//│ = 3 + +fun abs(x) = if x < 0 then -x else x +//│ fun abs: Int -> Int + +fun dist(p, q) = if p is Point(x1, y1) and q is Point(x2, y2) then + abs(x1 - x2) + abs(y1 - y2) +//│ fun dist: (Point, Point) -> Int + +dist(Point(1, 2), Point(3, 4)) +//│ Int +//│ res +//│ = 4 diff --git a/shared/src/test/diff/pretyper/ucs/examples/Option.mls b/shared/src/test/diff/pretyper/ucs/examples/Option.mls new file mode 100644 index 00000000..22207938 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/examples/Option.mls @@ -0,0 +1,42 @@ +:PreTyper + +class Some[T](value: T) +module None +type Option[T] = Some[T] | None +//│ class Some[T](value: T) +//│ module None +//│ type Option[T] = None | Some[T] + +fun getOrElse[T](self: Option[T], default: T): T = + if self is + Some(value) then value + None then default +//│ fun getOrElse: forall 'T. (self: Option['T], default: 'T) -> 'T + +getOrElse(None, 0) +getOrElse(None, "hello") +getOrElse(None, true) +//│ true +//│ res +//│ = 0 +//│ res +//│ = 'hello' +//│ res +//│ = true + +getOrElse(Some(true), false) +//│ Bool +//│ res +//│ = true + +fun map[T, U](self: Option[T], f: (T) -> U): Option[U] = + if self is + Some(value) then Some(f(value)) + None then None +//│ fun map: forall 'T 'U. (self: Option['T], f: 'T -> 'U) -> Option['U] + +fun flatMap[T, U](self: Option[T], f: (T) -> Option[U]): Option[U] = + if self is + Some(value) then f(value) + None then None +//│ fun flatMap: forall 'T 'U. (self: Option['T], f: 'T -> Option['U]) -> Option['U] diff --git a/shared/src/test/diff/ucs/LeadingAnd.mls b/shared/src/test/diff/ucs/LeadingAnd.mls index faa751e4..ae365f48 100644 --- a/shared/src/test/diff/ucs/LeadingAnd.mls +++ b/shared/src/test/diff/ucs/LeadingAnd.mls @@ -23,7 +23,7 @@ fun f(a, b) = if a is Some(av) and b is Some(bv) then av + bv //│ |#fun| |f|(|a|,| |b|)| |#=| |#if| |a| |is| |Some|(|av|)|→|and| |b| |is| |Some|(|bv|)|↵|#then| |av| |+| |bv|←| -//│ AST: TypingUnit(NuFunDef(None, f, None, [], Lam(Tup(_: Var(a), _: Var(b)), If(IfOpApp(Var(a), Var(is), I; f; O; p; s; A; p; p; (; A; p; p; (; V; a; r; (; S; o; m; e; ); ,; ; T; u; p; (; _; :; ; V; a; r; (; a; v; ); ); ); ,; ; <; i; t; e; r; a; t; o; r; >, None)))) +//│ AST: TypingUnit(NuFunDef(None, f, None, [], Lam(Tup((N, Var(a)), (N, Var(b))), If(IfOpApp(Var(a), Var(is), I; f; O; p; s; A; p; p; (; A; p; p; (; V; a; r; (; S; o; m; e; ); ,; ; T; u; p; (; (; N; ,; ; V; a; r; (; a; v; ); ); ); ); ,; ; <; i; t; e; r; a; t; o; r; >, None)))) //│ Parsed: fun f = (a, b,) => if a is Some(av,) ‹· and (is(b, Some(bv,),)) then +(av, bv,)›; //│ fun f: (Some[Int], Some[Int]) -> Int @@ -34,7 +34,7 @@ fun f(a, b) = if a is and b is Some(bv) then av + bv //│ |#fun| |f|(|a|,| |b|)| |#=| |#if| |a| |is|→|Some|(|av|)|→|and| |b| |is| |Some|(|bv|)|↵|#then| |av| |+| |bv|←|←| -//│ AST: TypingUnit(NuFunDef(None, f, None, [], Lam(Tup(_: Var(a), _: Var(b)), If(IfOpApp(Var(a), Var(is), IfBlock(I; f; O; p; s; A; p; p; (; A; p; p; (; V; a; r; (; S; o; m; e; ); ,; ; T; u; p; (; _; :; ; V; a; r; (; a; v; ); ); ); ,; ; <; i; t; e; r; a; t; o; r; >), None)))) +//│ AST: TypingUnit(NuFunDef(None, f, None, [], Lam(Tup((N, Var(a)), (N, Var(b))), If(IfOpApp(Var(a), Var(is), IfBlock(I; f; O; p; s; A; p; p; (; A; p; p; (; V; a; r; (; S; o; m; e; ); ,; ; T; u; p; (; (; N; ,; ; V; a; r; (; a; v; ); ); ); ); ,; ; <; i; t; e; r; a; t; o; r; >), None)))) //│ Parsed: fun f = (a, b,) => if a is ‹Some(av,) ‹· and (is(b, Some(bv,),)) then +(av, bv,)››; //│ ╔══[ERROR] Illegal pattern `and` //│ ║ l.34: and b is Some(bv) diff --git a/shared/src/test/diff/ucs/SplitOps.mls b/shared/src/test/diff/ucs/SplitOps.mls index 03ee227f..7171667b 100644 --- a/shared/src/test/diff/ucs/SplitOps.mls +++ b/shared/src/test/diff/ucs/SplitOps.mls @@ -93,10 +93,10 @@ fun f(a, b, c) = if a == 0 and b is B() and c is C() then 0 //│ |#fun| |f|(|a|,| |b|,| |c|)| |#=|→|#if| |a|→|==| |0| |and| |b| |is| |B|(||)| |and| |c| |is| |C|(||)| |#then| |0|←|←| -//│ AST: TypingUnit(NuFunDef(None, f, None, [], Lam(Tup(_: Var(a), _: Var(b), _: Var(c)), Blk(...)))) +//│ AST: TypingUnit(NuFunDef(None, f, None, [], Lam(Tup((N, Var(a)), (N, Var(b)), (N, Var(c))), Blk(...)))) //│ Parsed: fun f = (a, b, c,) => {if a ‹· == (and(and(0,)(is(b,)(B(),),),)(is(c,)(C(),),)) then 0›}; //│ Desugared: rec def f: (a, b, c,) => {if a ‹· == (and(and(0,)(is(b,)(B(),),),)(is(c,)(C(),),)) then 0›} -//│ AST: Def(true, f, Lam(Tup(_: Var(a), _: Var(b), _: Var(c)), Blk(...)), true) +//│ AST: Def(true, f, Lam(Tup((N, Var(a)), (N, Var(b)), (N, Var(c))), Blk(...)), true) //│ ╔══[ERROR] The case when this is false is not handled: ==(a,)(0,) //│ ║ l.93: if a //│ ║ ^ diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index 9d74c269..4efc4d6a 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -12,7 +12,7 @@ import mlscript.codegen.typescript.TsTypegenCodeBuilder import org.scalatest.{funsuite, ParallelTestExecution} import org.scalatest.time._ import org.scalatest.concurrent.{TimeLimitedTests, Signaler} - +import pretyper.PreTyper abstract class ModeType { def expectTypeErrors: Bool @@ -138,6 +138,7 @@ class DiffTests var declared: Map[Str, typer.ST] = Map.empty val failures = mutable.Buffer.empty[Int] val unmergedChanges = mutable.Buffer.empty[Int] + var preTyperScope = mlscript.pretyper.Scope.global case class Mode( expectTypeErrors: Bool = false, @@ -150,6 +151,7 @@ class DiffTests explainErrors: Bool = false, dbg: Bool = false, dbgParsing: Bool = false, + dbgPreTyper: Opt[Int] = N, dbgSimplif: Bool = false, dbgUCS: Bool = false, fullExceptionStack: Bool = false, @@ -188,6 +190,7 @@ class DiffTests var noRecursiveTypes = false var constrainedTypes = false var irregularTypes = false + var usePreTyper = false // * This option makes some test cases pass which assume generalization should happen in arbitrary arguments // * but it's way too aggressive to be ON by default, as it leads to more extrusion, cycle errors, etc. @@ -210,6 +213,7 @@ class DiffTests case "p" => mode.copy(showParse = true) case "d" => mode.copy(dbg = true) case "dp" => mode.copy(dbgParsing = true) + case PreTyperOption(nv) => mode.copy(dbgPreTyper = S(nv)) case "ds" => mode.copy(dbgSimplif = true) case "ducs" => mode.copy(dbg = true, dbgUCS = true) case "s" => mode.copy(fullExceptionStack = true) @@ -254,6 +258,9 @@ class DiffTests // println("'"+line.drop(str.length + 2)+"'") typer.startingFuel = line.drop(str.length + 2).toInt; mode case "ResetFuel" => typer.startingFuel = typer.defaultStartingFuel; mode + // I believe `PreTyper` will become a part of new definition typing. + // So, this will be removed after `PreTyper` is done. + case "PreTyper" => newParser = true; newDefs = true; usePreTyper = true; mode case "ne" => mode.copy(noExecution = true) case "ng" => mode.copy(noGeneration = true) case "js" => mode.copy(showGeneratedJS = true) @@ -512,9 +519,16 @@ class DiffTests } val (typeDefs, stmts, newDefsResults) = if (newDefs) { - val vars: Map[Str, typer.SimpleType] = Map.empty - val tpd = typer.typeTypingUnit(TypingUnit(p.tops), N)(ctx, raise, vars) + val rootTypingUnit = TypingUnit(p.tops) + if (usePreTyper) { + val preTyper = new PreTyper(mode.dbgPreTyper, newDefs) { + override def emitDbg(str: String): Unit = output(str) + } + // This should be passed to code generation somehow. + preTyperScope = preTyper.process(rootTypingUnit, preTyperScope, "")._1 + } + val tpd = typer.typeTypingUnit(rootTypingUnit, N)(ctx, raise, vars) def showTTU(ttu: typer.TypedTypingUnit, ind: Int): Unit = { val indStr = " " * ind @@ -1144,4 +1158,13 @@ object DiffTests { // name.startsWith("new/") // file.segments.toList.init.lastOption.contains("parser") } + + object PreTyperOption { + def unapply(str: String): Option[Int] = str match { + case "dpt" => Some(0) + case "dpt:v" => Some(1) + case "dpt:vv" => Some(2) + case _ => None + } + } } From fbdbe0be283309430b50926dace7eafc69b25737 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Fri, 24 Nov 2023 14:58:39 +0800 Subject: [PATCH 002/143] Start working on exhaustiveness checking --- shared/src/main/scala/mlscript/ucs/DesugarUCS.scala | 6 +++++- .../scala/mlscript/ucs/stages/ExhaustivenessChecking.scala | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 shared/src/main/scala/mlscript/ucs/stages/ExhaustivenessChecking.scala diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index 685d2365..c19d058c 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -10,7 +10,11 @@ import mlscript.Message, Message.MessageContext import mlscript.ucs.core.Pattern.Name // TODO: Rename to `Desugarer` once the old desugarer is removed. -trait DesugarUCS extends Transformation with Desugaring with Normalization with PostProcessing { self: PreTyper => +trait DesugarUCS extends Transformation + with Desugaring + with Normalization + with PostProcessing + with ExhaustivenessChecking { self: PreTyper => protected def visitIf(`if`: If)(implicit scope: Scope): Unit = trace("visitIf") { // Stage 0: Transformation diff --git a/shared/src/main/scala/mlscript/ucs/stages/ExhaustivenessChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/ExhaustivenessChecking.scala new file mode 100644 index 00000000..33b17d88 --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/stages/ExhaustivenessChecking.scala @@ -0,0 +1,5 @@ +package mlscript.ucs.stages + +trait ExhaustivenessChecking { self: mlscript.pretyper.Traceable => + +} From 84f98f67d2a76faaaff31e544bae94d0acbf9f40 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sat, 25 Nov 2023 03:36:14 +0800 Subject: [PATCH 003/143] An ineffective attempt on the new post-processing --- shared/src/main/scala/mlscript/helpers.scala | 9 +- .../scala/mlscript/pretyper/PreTyper.scala | 2 +- .../main/scala/mlscript/pretyper/Symbol.scala | 6 +- shared/src/main/scala/mlscript/syntax.scala | 2 +- .../main/scala/mlscript/ucs/DesugarUCS.scala | 5 +- .../mlscript/ucs/stages/Normalization.scala | 9 - .../mlscript/ucs/stages/PostProcessing.scala | 176 ++++++- .../scala/mlscript/ucs/stages/package.scala | 11 +- .../src/test/diff/pretyper/ucs/DualOption.mls | 446 ++++++++++++++++++ 9 files changed, 627 insertions(+), 39 deletions(-) diff --git a/shared/src/main/scala/mlscript/helpers.scala b/shared/src/main/scala/mlscript/helpers.scala index 3285c8d8..ce8a9a04 100644 --- a/shared/src/main/scala/mlscript/helpers.scala +++ b/shared/src/main/scala/mlscript/helpers.scala @@ -1040,6 +1040,13 @@ trait BlkImpl { self: Blk => } +trait CaseOfImpl extends Located { self: CaseOf => + def isEmpty: Bool = { + val CaseOf(scrut, cases) = this + cases === NoCases + } +} + trait CaseBranchesImpl extends Located { self: CaseBranches => def children: List[Located] = this match { @@ -1057,7 +1064,7 @@ trait CaseBranchesImpl extends Located { self: CaseBranches => "_ => " + body.print(false) case NoCases => "" } - + } trait AdtMatchPatImpl extends Located { self: AdtMatchPat => diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index f76c0628..d4444507 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -115,7 +115,7 @@ class PreTyper(override val debugLevel: Opt[Int], useNewDefs: Bool) extends Trac trace(s"visitFunction <== ${defn.nme.name}") { defn.rhs match { case Left(term) => - val subScope = if (defn.isLetRec == S(false)) scope else scope + symbol + val subScope = if (defn.isLetRec === S(false)) scope else scope + symbol visitTerm(term)(subScope) case Right(value) => () } diff --git a/shared/src/main/scala/mlscript/pretyper/Symbol.scala b/shared/src/main/scala/mlscript/pretyper/Symbol.scala index 7cd3d335..475c33fc 100644 --- a/shared/src/main/scala/mlscript/pretyper/Symbol.scala +++ b/shared/src/main/scala/mlscript/pretyper/Symbol.scala @@ -1,6 +1,6 @@ package mlscript.pretyper -import collection.mutable.{Buffer, Map => MutMap} +import collection.mutable.{Buffer, Map => MutMap, Set => MutSet} import mlscript.{NuFunDef, NuTypeDef, TypeName, Var} import mlscript.utils._, shorthands._ @@ -28,6 +28,7 @@ final class FunctionSymbol(val nme: Var, val defn: NuFunDef) extends TermSymbol( } sealed abstract class ScrutineeSymbol(name: Str) extends TermSymbol(name) { + val matchedClasses: MutSet[Var] = MutSet.empty /** * This map contains the sub-scrutinee symbols when this scrutinee is matched * against class patterns. @@ -36,10 +37,11 @@ sealed abstract class ScrutineeSymbol(name: Str) extends TermSymbol(name) { val tupleElementScrutineeMap: MutMap[Int, SubValueSymbol] = MutMap.empty val recordValueScrutineeMap: MutMap[Var, SubValueSymbol] = MutMap.empty - def addSubScrutinee(className: Var, index: Int, parameter: Var): SubValueSymbol = + def addSubScrutinee(className: Var, index: Int, parameter: Var): SubValueSymbol = { classParameterScrutineeMap.getOrElseUpdate(className -> index, { new SubValueSymbol(this, S(className) -> S(index), parameter.name) }) + } def addSubScrutinee(fieldName: Var): SubValueSymbol = recordValueScrutineeMap.getOrElseUpdate(fieldName, { diff --git a/shared/src/main/scala/mlscript/syntax.scala b/shared/src/main/scala/mlscript/syntax.scala index f33023a3..368faca8 100644 --- a/shared/src/main/scala/mlscript/syntax.scala +++ b/shared/src/main/scala/mlscript/syntax.scala @@ -77,7 +77,7 @@ final case class Asc(trm: Term, ty: Type) extends Ter final case class Bind(lhs: Term, rhs: Term) extends Term final case class Test(trm: Term, ty: Term) extends Term final case class With(trm: Term, fields: Rcd) extends Term -final case class CaseOf(trm: Term, cases: CaseBranches) extends Term +final case class CaseOf(trm: Term, cases: CaseBranches) extends Term with CaseOfImpl final case class Subs(arr: Term, idx: Term) extends Term final case class Assign(lhs: Term, rhs: Term) extends Term final case class Splc(fields: Ls[Either[Term, Fld]]) extends Term diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index c19d058c..051541e6 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -74,8 +74,11 @@ trait DesugarUCS extends Transformation case core.Pattern.Name(nme) => nme.symbol = scrutineeSymbol nme -> scrutineeSymbol :: Nil - case core.Pattern.Class(_, N) => Nil + case core.Pattern.Class(nme, N) => + scrutineeSymbol.matchedClasses += nme + Nil case core.Pattern.Class(nme, S(parameters)) => + scrutineeSymbol.matchedClasses += nme parameters.iterator.zipWithIndex.flatMap { case (N, _) => N case (S(parameter), index) => diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 93662236..623f8596 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -160,15 +160,6 @@ trait Normalization { self: mlscript.pretyper.Traceable => } object Normalization { - private sealed abstract class MatchOrNot { - override def toString(): String = this match { - case Yes => "+" - case No => "-" - } - } - private final case object Yes extends MatchOrNot - private final case object No extends MatchOrNot - class NormalizationException(val messages: Ls[Message -> Opt[Loc]]) extends Throwable { def this(message: Message, location: Opt[Loc]) = this(message -> location :: Nil) } diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index ff8c08cc..f9ae645e 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -1,23 +1,163 @@ package mlscript.ucs.stages -import mlscript.{Case, CaseOf, Let, NoCases, Term, Var, Wildcard} -import mlscript.pretyper.{Symbol} +import mlscript.{Case, CaseOf, Let, Loc, NoCases, Term, Var, Wildcard} +import mlscript.pretyper.{ScrutineeSymbol, Symbol} import mlscript.utils._, shorthands._ import mlscript.CaseBranches +import mlscript.Message, Message.MessageContext +import mlscript.pretyper.shortName trait PostProcessing { self: mlscript.pretyper.Traceable => - def postProcess(term: Term): Term = trace(s"postProcess <== ${term.describe}") { + import PostProcessing._ + + def postProcess(term: Term): Term = trace(s"postProcess <== ${shortName(term)}") { + // Normalized terms are constructed using `Let` and `CaseOf`. + term match { + case top @ CaseOf(scrutinee: Var, fst @ Case(className: Var, body, NoCases)) => + println(s"found a UNARY case: $scrutinee is $className") + println("post-processing the body") + top.copy(cases = fst.copy(body = postProcess(body))) + case top @ CaseOf(scrutinee: Var, fst @ Case(className: Var, trueBranch, Wildcard(falseBranch))) => + println(s"found a BINARY case: $scrutinee is $className") + val scrutineeSymbol = scrutinee.symbol match { + case symbol: ScrutineeSymbol => symbol + case _ => throw new PostProcessingException( + msg"variable ${scrutinee.name} is not a scrutinee" -> N :: Nil + ) + } + println(s"`${scrutinee}`'s matched classes: ${scrutineeSymbol.matchedClasses.iterator.map(_.name).mkString("[", ", ", "]")}") + // Post-process the first branch body. + println("post-processing the first branch") + val processedTrueBranch = postProcess(trueBranch) + // Post-process the false branch. + val (default, cases) = scrutineeSymbol.matchedClasses.iterator.filter(_ =/= className) + // For each case class name, distangle case branch body terms from the false branch. + .foldLeft[Opt[Term] -> Ls[Var -> Term]](S(falseBranch) -> Nil) { + case ((S(remainingTerm), cases), className) => + val (leftoverTerm, caseBranchBodyTerms) = disentangle(remainingTerm, scrutineeSymbol, className) + avoidEmptyCaseOf(leftoverTerm) -> (caseBranchBodyTerms match { + case Nil => + println(s"no case branches about $className were found") + cases + case terms @ (head :: tail) => + println(s"found ${terms.length} case branches about $className") + val body = trace(s"merging terms <== ${terms.iterator.map(shortName).mkString("[", ", ", "]")}") { + tail.foldLeft(head)(mergeTerms) + }(t => s"merging terms ==> ${shortName(t)}") + className -> postProcess(body) :: cases + }) + // TODO: Consider either make the first tuple element as non-optional. + // TODO: Then, write a new helper function which checks if the term is an empty `CaseOf`. + case ((N, cases), _) => (N, cases) + } + println(s"found ${cases.length} cases") + // Assemble a `CaseBranches`. + val actualFalseBranch = cases.foldRight[CaseBranches]( + default.fold[CaseBranches](NoCases)(Wildcard(_)) + ) { case (className -> body, rest) => Case(className, body, rest) } + // Assemble the final `CaseOf`. + top.copy(cases = fst.copy(body = processedTrueBranch, rest = actualFalseBranch)) + // We recursively process the body of as`Let` bindings. + case let @ Let(_, _, _, body) => let.copy(body = postProcess(body)) + // Otherwise, this is not a part of a normalized term. + case other => println(s"CANNOT post-process"); other + } + }(_ => "postProcess ==> ") + + private def avoidEmptyCaseOf(term: Term): Opt[Term] = term match { + case CaseOf(_, NoCases) => println(s"$term is empty"); N + case _ => S(term) + } + + private def mergeTerms(t1: Term, t2: Term): Term = + trace(s"mergeTerms <== ${t1.describe} ${t2.describe}") { + t1 match { + case t1 @ Let(_, _, _, body) => t1.copy(body = mergeTerms(body, t2)) + case t1 @ CaseOf(scrutinee: Var, cases) => + t1.copy(cases = mergeTermIntoCaseBranches(t2, cases)) + case _ => println("CANNOT merge. Discard t2."); t1 + } + }() + + private def mergeTermIntoCaseBranches(term: Term, cases: CaseBranches): CaseBranches = + trace(s"mergeTermIntoCaseBranches <== ${term.describe} ${cases}") { + cases match { + case NoCases => Wildcard(term).withLocOf(term) + case Wildcard(body) => Wildcard(mergeTerms(body, term)) + case cases @ Case(_, _, rest) => cases.copy(rest = mergeTermIntoCaseBranches(term, rest)) + } + }() + + /** + * Disentangle case branches that match `scrutinee` against `className` from `term`. + * The `term` should be obtained from _normalization_. Because there may exists multiple + * `CaseOf`s which contains such case branches, we return a list of disentangled terms. + * + * @param term the term to disentangle from + * @param scrutinee the symbol of the scrutinee variable + * @param className the class name + * @return the remaining term and the disentangled case branch body terms + */ + def disentangle(term: Term, scrutinee: ScrutineeSymbol, className: Var): (Term, Ls[Term]) = + trace[(Term, Ls[Term])](s"disentangle <== ${scrutinee.name}: $className") { + term match { + case top @ CaseOf(scrutineeVar: Var, cases) => + if (scrutineeVar.symbol match { + case s: ScrutineeSymbol => s === scrutinee; case _ => false + }) { + println(s"found a `CaseOf` that matches on ${scrutinee.name}") + def rec(cases: CaseBranches): (CaseBranches, Ls[Term]) = cases match { + case NoCases => println("found the end, stop"); NoCases -> Nil + case wildcard @ Wildcard(body) => + println("found a wildcard, stop") + val (y, ns) = disentangle(body, scrutinee, className) + wildcard.copy(body = y) -> ns + case kase @ Case(`className`, body, rest) => + println(s"found a case branch matching against $className") + val (y, ns) = rec(rest) + y -> (ns :+ body) + case kase @ Case(otherClassName, body, rest) => + val (y, ns) = rec(rest) + kase.copy(rest = y) -> ns + } + val (y, ns) = rec(cases) + top.copy(cases = y) -> ns + } else { + println(s"found a `CaseOf` that does NOT match on ${scrutinee.name}") + def rec(cases: CaseBranches): (CaseBranches, Ls[Term]) = cases match { + case NoCases => println("found the end, stop"); NoCases -> Nil + case wildcard @ Wildcard(body) => + println("found a wildcard, stop") + val (y, ns) = disentangle(body, scrutinee, className) + wildcard.copy(body = y) -> ns + case kase @ Case(_, body, rest) => + println(s"found a case branch") + val (y1, ns1) = disentangle(body, scrutinee, className) + val (y2, ns) = rec(rest) + kase.copy(body = y1, rest = y2) -> (ns1 ++ ns) + } + val (y, ns) = rec(cases) + top.copy(cases = y) -> ns + } + case let @ Let(_, _, _, body) => + val (y, ns) = disentangle(body, scrutinee, className) + let.copy(body = y) -> ns + case other => println(s"CANNOT disentangle"); other -> Nil + } + }({ case (y, ns) => s"disentangle ==> `${y}` ${ns.length}" }) + + def cascadeConsecutiveCaseOf(term: Term): Term = trace(s"cascade consecutive CaseOf <== ${term.describe}") { // Normalized terms are constructed using `Let` and `CaseOf`. term match { case top @ CaseOf(scrutinee: Var, fst @ Case(pattern, body, NoCases)) => println(s"found a UNARY case: $scrutinee is $pattern") - top.copy(cases = fst.copy(body = postProcess(body))) + top.copy(cases = fst.copy(body = cascadeConsecutiveCaseOf(body))) case top @ CaseOf(scrutinee: Var, fst @ Case(pattern, trueBranch, snd @ Wildcard(falseBranch))) => println(s"found a BINARY case: $scrutinee is $pattern") - println("post-processing the true branch") - val processedTrueBranch = postProcess(trueBranch) - println("post-processing the false branch") - val processedFalseBranch = postProcess(falseBranch) + println("cascading the true branch") + val processedTrueBranch = cascadeConsecutiveCaseOf(trueBranch) + println("cascading the false branch") + val processedFalseBranch = cascadeConsecutiveCaseOf(falseBranch) // Check if the false branch is another `CaseOf` with the same scrutinee. processedFalseBranch match { case CaseOf(otherScrutinee: Var, actualFalseBranch) => @@ -36,25 +176,15 @@ trait PostProcessing { self: mlscript.pretyper.Traceable => case other => top } // We recursively process the body of `Let` bindings. - case let @ Let(_, _, _, body) => let.copy(body = postProcess(body)) + case let @ Let(_, _, _, body) => let.copy(body = cascadeConsecutiveCaseOf(body)) // Otherwise, this is not a part of a normalized term. - case other => println(s"CANNOT post-process"); other + case other => println(s"CANNOT cascade"); other } }() - - private def hasScrutinee(term: Term, symbol: Symbol): Bool = { - term match { - case CaseOf(scrutinee: Var, Case(pattern, trueBranch, Wildcard(falseBranch))) => - if (scrutinee.symbolOption.exists(_ === symbol)) true - else hasScrutinee(trueBranch, symbol) || hasScrutinee(falseBranch, symbol) - case Let(_, _, _, body) => hasScrutinee(body, symbol) - case _ => false - } - } - - private def separateCaseBranches(term: Term)(implicit scrutinee: Symbol): (CaseBranches, Term) = ??? } object PostProcessing { - + class PostProcessingException(val messages: Ls[Message -> Opt[Loc]]) extends Throwable { + def this(message: Message, location: Opt[Loc]) = this(message -> location :: Nil) + } } diff --git a/shared/src/main/scala/mlscript/ucs/stages/package.scala b/shared/src/main/scala/mlscript/ucs/stages/package.scala index c0d7c81f..d1c5c2fb 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/package.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/package.scala @@ -4,4 +4,13 @@ import mlscript.{Term, Var} import mlscript.pretyper.Symbol import mlscript.utils._, shorthands._ -package object stages +package object stages { + private[stages] sealed abstract class MatchOrNot { + override def toString(): String = this match { + case Yes => "+" + case No => "-" + } + } + private[stages] final case object Yes extends MatchOrNot + private[stages] final case object No extends MatchOrNot +} diff --git a/shared/src/test/diff/pretyper/ucs/DualOption.mls b/shared/src/test/diff/pretyper/ucs/DualOption.mls index 2ebf1e75..9b245535 100644 --- a/shared/src/test/diff/pretyper/ucs/DualOption.mls +++ b/shared/src/test/diff/pretyper/ucs/DualOption.mls @@ -58,6 +58,7 @@ add_2(Some(5), Some(9)) //│ res //│ = 14 + fun add_3(x, y) = if Pair(x, y) is Pair(Some(xv), Some(yv)) then xv + yv @@ -80,6 +81,7 @@ add_3(Some(5), Some(9)) //│ res //│ = 14 + fun add_4(x, y) = if x @@ -98,6 +100,7 @@ fun add_4(x, y) = None then 0 //│ fun add_4: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) + add_4(None, None) add_4(Some(5), None) add_4(None, Some(9)) @@ -112,12 +115,455 @@ add_4(Some(5), Some(9)) //│ res //│ = 14 +:dpt fun add_5(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv y is None and x is Some(xv) then xv x is None and y is Some(yv) then yv y is None and x is None then 0 +//│ process <== : {fun add_5} +//│ | visitTypingUnit <== : {fun add_5} +//│ | | 1. scope = {add_5} +//│ | | 2. scope = {add_5} +//│ | | visitFunction <== add_5 +//│ | | | visitTerm <== Lam(_, _) +//│ | | | | visitTerm <== Blk(_) +//│ | | | | | visitTerm <== If(_) +//│ | | | | | | visitIf +//│ | | | | | | | Transformed UCS term: +//│ | | | | | | | if +//│ | | | | | | | x is Some(xv) and y is Some(yv) then +(xv, yv,) +//│ | | | | | | | y is None and x is Some(xv) then xv +//│ | | | | | | | x is None and y is Some(yv) then yv +//│ | | | | | | | y is None and x is None then 0 +//│ | | | | | | | Desugared UCS term: +//│ | | | | | | | if +//│ | | | | | | | x is Some(xv) y is Some(yv) then +(xv, yv,) +//│ | | | | | | | y is None x is Some(xv) then xv +//│ | | | | | | | x is None y is Some(yv) then yv +//│ | | | | | | | y is None x is None then 0 +//│ | | | | | | | visitSplit <== [add_5, x, y] +//│ | | | | | | | | found branch: x is Some(xv) +//│ | | | | | | | | visitTerm <== Var("x") +//│ | | | | | | | | | visitVar(name = "x") +//│ | | | | | | | | | | resolveVar(name = "x") +//│ | | | | | | | | visitTerm ==> Var("x") +//│ | | | | | | | | visitPattern <== Some(xv) +//│ | | | | | | | | visitPattern ==> [xv] +//│ | | | | | | | | visitSplit <== [add_5, x, y, xv] +//│ | | | | | | | | | found branch: y is Some(yv) +//│ | | | | | | | | | visitTerm <== Var("y") +//│ | | | | | | | | | | visitVar(name = "y") +//│ | | | | | | | | | | | resolveVar(name = "y") +//│ | | | | | | | | | visitTerm ==> Var("y") +//│ | | | | | | | | | visitPattern <== Some(yv) +//│ | | | | | | | | | visitPattern ==> [yv] +//│ | | | | | | | | | visitSplit <== [x, y, add_5, xv, yv] +//│ | | | | | | | | | | visitTerm <== App(_, _) +//│ | | | | | | | | | | | visitTerm <== Var("+") +//│ | | | | | | | | | | | | visitVar(name = "+") +//│ | | | | | | | | | | | | | resolveVar(name = "+") +//│ | | | | | | | | | | | visitTerm ==> Var("+") +//│ | | | | | | | | | | | visitTerm <== Tup(_, _) +//│ | | | | | | | | | | | | visitTerm <== Var("xv") +//│ | | | | | | | | | | | | | visitVar(name = "xv") +//│ | | | | | | | | | | | | | | resolveVar(name = "xv") +//│ | | | | | | | | | | | | visitTerm ==> Var("xv") +//│ | | | | | | | | | | | | visitTerm <== Var("yv") +//│ | | | | | | | | | | | | | visitVar(name = "yv") +//│ | | | | | | | | | | | | | | resolveVar(name = "yv") +//│ | | | | | | | | | | | | visitTerm ==> Var("yv") +//│ | | | | | | | | | | | visitTerm ==> Tup(_, _) +//│ | | | | | | | | | | visitTerm ==> App(_, _) +//│ | | | | | | | | | visitSplit <== [add_5, x, y, xv] +//│ | | | | | | | | | | the end +//│ | | | | | | | | visitSplit <== [add_5, x, y] +//│ | | | | | | | | | found branch: y is None +//│ | | | | | | | | | visitTerm <== Var("y") +//│ | | | | | | | | | | visitVar(name = "y") +//│ | | | | | | | | | | | resolveVar(name = "y") +//│ | | | | | | | | | visitTerm ==> Var("y") +//│ | | | | | | | | | visitPattern <== None +//│ | | | | | | | | | visitPattern ==> [] +//│ | | | | | | | | | visitSplit <== [add_5, x, y] +//│ | | | | | | | | | | found branch: x is Some(xv) +//│ | | | | | | | | | | visitTerm <== Var("x") +//│ | | | | | | | | | | | visitVar(name = "x") +//│ | | | | | | | | | | | | resolveVar(name = "x") +//│ | | | | | | | | | | visitTerm ==> Var("x") +//│ | | | | | | | | | | visitPattern <== Some(xv) +//│ | | | | | | | | | | visitPattern ==> [xv] +//│ | | | | | | | | | | visitSplit <== [add_5, x, y, xv] +//│ | | | | | | | | | | | visitTerm <== Var("xv") +//│ | | | | | | | | | | | | visitVar(name = "xv") +//│ | | | | | | | | | | | | | resolveVar(name = "xv") +//│ | | | | | | | | | | | visitTerm ==> Var("xv") +//│ | | | | | | | | | | visitSplit <== [add_5, x, y] +//│ | | | | | | | | | | | the end +//│ | | | | | | | | | visitSplit <== [add_5, x, y] +//│ | | | | | | | | | | found branch: x is None +//│ | | | | | | | | | | visitTerm <== Var("x") +//│ | | | | | | | | | | | visitVar(name = "x") +//│ | | | | | | | | | | | | resolveVar(name = "x") +//│ | | | | | | | | | | visitTerm ==> Var("x") +//│ | | | | | | | | | | visitPattern <== None +//│ | | | | | | | | | | visitPattern ==> [] +//│ | | | | | | | | | | visitSplit <== [add_5, x, y] +//│ | | | | | | | | | | | found branch: y is Some(yv) +//│ | | | | | | | | | | | visitTerm <== Var("y") +//│ | | | | | | | | | | | | visitVar(name = "y") +//│ | | | | | | | | | | | | | resolveVar(name = "y") +//│ | | | | | | | | | | | visitTerm ==> Var("y") +//│ | | | | | | | | | | | visitPattern <== Some(yv) +//│ | | | | | | | | | | | visitPattern ==> [yv] +//│ | | | | | | | | | | | visitSplit <== [add_5, x, y, yv] +//│ | | | | | | | | | | | | visitTerm <== Var("yv") +//│ | | | | | | | | | | | | | visitVar(name = "yv") +//│ | | | | | | | | | | | | | | resolveVar(name = "yv") +//│ | | | | | | | | | | | | visitTerm ==> Var("yv") +//│ | | | | | | | | | | | visitSplit <== [add_5, x, y] +//│ | | | | | | | | | | | | the end +//│ | | | | | | | | | | visitSplit <== [add_5, x, y] +//│ | | | | | | | | | | | found branch: y is None +//│ | | | | | | | | | | | visitTerm <== Var("y") +//│ | | | | | | | | | | | | visitVar(name = "y") +//│ | | | | | | | | | | | | | resolveVar(name = "y") +//│ | | | | | | | | | | | visitTerm ==> Var("y") +//│ | | | | | | | | | | | visitPattern <== None +//│ | | | | | | | | | | | visitPattern ==> [] +//│ | | | | | | | | | | | visitSplit <== [add_5, x, y] +//│ | | | | | | | | | | | | found branch: x is None +//│ | | | | | | | | | | | | visitTerm <== Var("x") +//│ | | | | | | | | | | | | | visitVar(name = "x") +//│ | | | | | | | | | | | | | | resolveVar(name = "x") +//│ | | | | | | | | | | | | visitTerm ==> Var("x") +//│ | | | | | | | | | | | | visitPattern <== None +//│ | | | | | | | | | | | | visitPattern ==> [] +//│ | | | | | | | | | | | | visitSplit <== [add_5, x, y] +//│ | | | | | | | | | | | | | visitTerm <== 0 +//│ | | | | | | | | | | | | | visitTerm ==> 0 +//│ | | | | | | | | | | | | visitSplit <== [add_5, x, y] +//│ | | | | | | | | | | | | | the end +//│ | | | | | | | | | | | visitSplit <== [add_5, x, y] +//│ | | | | | | | | | | | | the end +//│ | | | | | | | normalizeToTerm +//│ | | | | | | | | match x with Some(xv) +//│ | | | | | | | | compute true branch +//│ | | | | | | | | | S+ <== x is Some(xv) +//│ | | | | | | | | | | scrutinee: x =/= y +//│ | | | | | | | | | | S+ <== x is Some(xv) +//│ | | | | | | | | | | | the end +//│ | | | | | | | | | | S+ ==> then +(xv, yv,) +//│ | | | | | | | | | | specialized next +//│ | | | | | | | | | | S+ <== x is Some(xv) +//│ | | | | | | | | | | | scrutinee: x =/= y +//│ | | | | | | | | | | | S+ <== x is Some(xv) +//│ | | | | | | | | | | | | scrutinee: x === x +//│ | | | | | | | | | | | | class name: Some === Some +//│ | | | | | | | | | | | | S+ <== x is Some(xv) +//│ | | | | | | | | | | | | | the end +//│ | | | | | | | | | | | | S+ ==> then xv +//│ | | | | | | | | | | | S+ ==> then xv +//│ | | | | | | | | | | | specialized next +//│ | | | | | | | | | | | S+ <== x is Some(xv) +//│ | | | | | | | | | | | | scrutinee: x === x +//│ | | | | | | | | | | | | class name: Some =/= None +//│ | | | | | | | | | | | | specialized next +//│ | | | | | | | | | | | | S+ <== x is Some(xv) +//│ | | | | | | | | | | | | | scrutinee: x =/= y +//│ | | | | | | | | | | | | | S+ <== x is Some(xv) +//│ | | | | | | | | | | | | | | scrutinee: x === x +//│ | | | | | | | | | | | | | | class name: Some =/= None +//│ | | | | | | | | | | | | | | specialized next +//│ | | | | | | | | | | | | | | S+ <== x is Some(xv) +//│ | | | | | | | | | | | | | | | the end +//│ | | | | | | | | | | | | | | S+ ==> +//│ | | | | | | | | | | | | | S+ ==> +//│ | | | | | | | | | | | | | specialized next +//│ | | | | | | | | | | | | | S+ <== x is Some(xv) +//│ | | | | | | | | | | | | | | the end +//│ | | | | | | | | | | | | | S+ ==> +//│ | | | | | | | | | | | | S+ ==> y is None +//│ | | | | | | | | | | | S+ ==> y is None +//│ | | | | | | | | | | S+ ==> +//│ | | | | | | | | | | y is None then xv +//│ | | | | | | | | | | y is None +//│ | | | | | | | | | S+ ==> +//│ | | | | | | | | | y is Some(yv) then +(xv, yv,) +//│ | | | | | | | | | y is None then xv +//│ | | | | | | | | | y is None +//│ | | | | | | | | | normalizeToTerm +//│ | | | | | | | | | | match y with Some(yv) +//│ | | | | | | | | | | compute true branch +//│ | | | | | | | | | | | S+ <== y is Some(yv) +//│ | | | | | | | | | | | | the end +//│ | | | | | | | | | | | S+ ==> then +(xv, yv,) +//│ | | | | | | | | | | | normalizeToTerm +//│ | | | | | | | | | | compute false branch +//│ | | | | | | | | | | | S- <== y is Some(yv) +//│ | | | | | | | | | | | | scrutinee: y === y +//│ | | | | | | | | | | | | class name: Some =/= None +//│ | | | | | | | | | | | | S- <== y is Some(yv) +//│ | | | | | | | | | | | | | scrutinee: y === y +//│ | | | | | | | | | | | | | class name: Some =/= None +//│ | | | | | | | | | | | | | S- <== y is Some(yv) +//│ | | | | | | | | | | | | | | the end +//│ | | | | | | | | | | | | | S- ==> +//│ | | | | | | | | | | | | S- ==> y is None +//│ | | | | | | | | | | | S- ==> +//│ | | | | | | | | | | | y is None then xv +//│ | | | | | | | | | | | y is None +//│ | | | | | | | | | | | normalizeToCaseBranches +//│ | | | | | | | | | | | | normalizeToTerm +//│ | | | | | | | | | | | | | match y with None +//│ | | | | | | | | | | | | | S+ <== y is None +//│ | | | | | | | | | | | | | | the end +//│ | | | | | | | | | | | | | S+ ==> then xv +//│ | | | | | | | | | | | | | normalizeToTerm +//│ | | | | | | | | | | | | | S- <== y is None +//│ | | | | | | | | | | | | | | scrutinee: y === y +//│ | | | | | | | | | | | | | | class name: None === None +//│ | | | | | | | | | | | | | | S- <== y is None +//│ | | | | | | | | | | | | | | | the end +//│ | | | | | | | | | | | | | | S- ==> +//│ | | | | | | | | | | | | | S- ==> +//│ | | | | | | | | | | | | | normalizeToCaseBranches +//│ | | | | | | | | compute false branch +//│ | | | | | | | | | S- <== x is Some(xv) +//│ | | | | | | | | | | scrutinee: x =/= y +//│ | | | | | | | | | | S- <== x is Some(xv) +//│ | | | | | | | | | | | scrutinee: x === x +//│ | | | | | | | | | | | class name: Some === Some +//│ | | | | | | | | | | | S- <== x is Some(xv) +//│ | | | | | | | | | | | | the end +//│ | | | | | | | | | | | S- ==> +//│ | | | | | | | | | | S- ==> +//│ | | | | | | | | | | S- <== x is Some(xv) +//│ | | | | | | | | | | | scrutinee: x === x +//│ | | | | | | | | | | | class name: Some =/= None +//│ | | | | | | | | | | | S- <== x is Some(xv) +//│ | | | | | | | | | | | | scrutinee: x =/= y +//│ | | | | | | | | | | | | S- <== x is Some(xv) +//│ | | | | | | | | | | | | | scrutinee: x === x +//│ | | | | | | | | | | | | | class name: Some =/= None +//│ | | | | | | | | | | | | | S- <== x is Some(xv) +//│ | | | | | | | | | | | | | | the end +//│ | | | | | | | | | | | | | S- ==> +//│ | | | | | | | | | | | | S- ==> x is None then 0 +//│ | | | | | | | | | | | | S- <== x is Some(xv) +//│ | | | | | | | | | | | | | the end +//│ | | | | | | | | | | | | S- ==> +//│ | | | | | | | | | | | S- ==> y is None x is None then 0 +//│ | | | | | | | | | | S- ==> +//│ | | | | | | | | | | x is None y is Some(yv) then yv +//│ | | | | | | | | | | y is None x is None then 0 +//│ | | | | | | | | | S- ==> +//│ | | | | | | | | | y is None +//│ | | | | | | | | | x is None y is Some(yv) then yv +//│ | | | | | | | | | y is None x is None then 0 +//│ | | | | | | | | | normalizeToCaseBranches +//│ | | | | | | | | | | normalizeToTerm +//│ | | | | | | | | | | | match y with None +//│ | | | | | | | | | | | S+ <== y is None +//│ | | | | | | | | | | | | scrutinee: y =/= x +//│ | | | | | | | | | | | | S+ <== y is None +//│ | | | | | | | | | | | | | scrutinee: y === y +//│ | | | | | | | | | | | | | class name: None =/= Some +//│ | | | | | | | | | | | | | specialized next +//│ | | | | | | | | | | | | | S+ <== y is None +//│ | | | | | | | | | | | | | | the end +//│ | | | | | | | | | | | | | S+ ==> +//│ | | | | | | | | | | | | S+ ==> +//│ | | | | | | | | | | | | specialized next +//│ | | | | | | | | | | | | S+ <== y is None +//│ | | | | | | | | | | | | | scrutinee: y === y +//│ | | | | | | | | | | | | | class name: None === None +//│ | | | | | | | | | | | | | S+ <== y is None +//│ | | | | | | | | | | | | | | scrutinee: y =/= x +//│ | | | | | | | | | | | | | | S+ <== y is None +//│ | | | | | | | | | | | | | | | the end +//│ | | | | | | | | | | | | | | S+ ==> then 0 +//│ | | | | | | | | | | | | | | specialized next +//│ | | | | | | | | | | | | | | S+ <== y is None +//│ | | | | | | | | | | | | | | | the end +//│ | | | | | | | | | | | | | | S+ ==> +//│ | | | | | | | | | | | | | S+ ==> x is None then 0 +//│ | | | | | | | | | | | | S+ ==> x is None then 0 +//│ | | | | | | | | | | | S+ ==> +//│ | | | | | | | | | | | x is None +//│ | | | | | | | | | | | x is None then 0 +//│ | | | | | | | | | | | normalizeToTerm +//│ | | | | | | | | | | | | match x with None +//│ | | | | | | | | | | | | S+ <== x is None +//│ | | | | | | | | | | | | | scrutinee: x === x +//│ | | | | | | | | | | | | | class name: None === None +//│ | | | | | | | | | | | | | S+ <== x is None +//│ | | | | | | | | | | | | | | the end +//│ | | | | | | | | | | | | | S+ ==> then 0 +//│ | | | | | | | | | | | | S+ ==> then 0 +//│ | | | | | | | | | | | | normalizeToTerm +//│ | | | | | | | | | | | | S- <== x is None +//│ | | | | | | | | | | | | | scrutinee: x === x +//│ | | | | | | | | | | | | | class name: None === None +//│ | | | | | | | | | | | | | S- <== x is None +//│ | | | | | | | | | | | | | | the end +//│ | | | | | | | | | | | | | S- ==> +//│ | | | | | | | | | | | | S- ==> +//│ | | | | | | | | | | | | normalizeToCaseBranches +//│ | | | | | | | | | | | S- <== y is None +//│ | | | | | | | | | | | | scrutinee: y =/= x +//│ | | | | | | | | | | | | S- <== y is None +//│ | | | | | | | | | | | | | scrutinee: y === y +//│ | | | | | | | | | | | | | class name: None =/= Some +//│ | | | | | | | | | | | | | S- <== y is None +//│ | | | | | | | | | | | | | | the end +//│ | | | | | | | | | | | | | S- ==> +//│ | | | | | | | | | | | | S- ==> y is Some(yv) then yv +//│ | | | | | | | | | | | | S- <== y is None +//│ | | | | | | | | | | | | | scrutinee: y === y +//│ | | | | | | | | | | | | | class name: None === None +//│ | | | | | | | | | | | | | S- <== y is None +//│ | | | | | | | | | | | | | | the end +//│ | | | | | | | | | | | | | S- ==> +//│ | | | | | | | | | | | | S- ==> +//│ | | | | | | | | | | | S- ==> x is None y is Some(yv) then yv +//│ | | | | | | | | | | | normalizeToCaseBranches +//│ | | | | | | | | | | | | normalizeToTerm +//│ | | | | | | | | | | | | | match x with None +//│ | | | | | | | | | | | | | S+ <== x is None +//│ | | | | | | | | | | | | | | scrutinee: x =/= y +//│ | | | | | | | | | | | | | | S+ <== x is None +//│ | | | | | | | | | | | | | | | the end +//│ | | | | | | | | | | | | | | S+ ==> then yv +//│ | | | | | | | | | | | | | | specialized next +//│ | | | | | | | | | | | | | | S+ <== x is None +//│ | | | | | | | | | | | | | | | the end +//│ | | | | | | | | | | | | | | S+ ==> +//│ | | | | | | | | | | | | | S+ ==> y is Some(yv) then yv +//│ | | | | | | | | | | | | | normalizeToTerm +//│ | | | | | | | | | | | | | | match y with Some(yv) +//│ | | | | | | | | | | | | | | compute true branch +//│ | | | | | | | | | | | | | | | S+ <== y is Some(yv) +//│ | | | | | | | | | | | | | | | | the end +//│ | | | | | | | | | | | | | | | S+ ==> then yv +//│ | | | | | | | | | | | | | | | normalizeToTerm +//│ | | | | | | | | | | | | | | compute false branch +//│ | | | | | | | | | | | | | | | S- <== y is Some(yv) +//│ | | | | | | | | | | | | | | | | the end +//│ | | | | | | | | | | | | | | | S- ==> +//│ | | | | | | | | | | | | | | | normalizeToCaseBranches +//│ | | | | | | | | | | | | | S- <== x is None +//│ | | | | | | | | | | | | | | the end +//│ | | | | | | | | | | | | | S- ==> +//│ | | | | | | | | | | | | | normalizeToCaseBranches +//│ | | | | | | | Normalized UCS term: +//│ | | | | | | | x match +//│ | | | | | | | case Some => +//│ | | | | | | | let args_x$Some = (Some).unapply(x,) +//│ | | | | | | | let xv* = (args_x$Some).0 +//│ | | | | | | | y match +//│ | | | | | | | case Some => +//│ | | | | | | | let args_y$Some = (Some).unapply(y,) +//│ | | | | | | | let yv* = (args_y$Some).0 +//│ | | | | | | | +(xv, yv,) +//│ | | | | | | | case _ => +//│ | | | | | | | y match +//│ | | | | | | | case None => xv +//│ | | | | | | | case _ => +//│ | | | | | | | y match +//│ | | | | | | | case None => +//│ | | | | | | | x match +//│ | | | | | | | case None => 0 +//│ | | | | | | | case _ => +//│ | | | | | | | x match +//│ | | | | | | | case None => +//│ | | | | | | | y match +//│ | | | | | | | case Some => +//│ | | | | | | | let args_y$Some = (Some).unapply(y,) +//│ | | | | | | | let yv* = (args_y$Some).0 +//│ | | | | | | | yv +//│ | | | | | | | postProcess <== CaseOf(_, _) +//│ | | | | | | | | found a BINARY case: x is Some +//│ | | | | | | | | `x`'s matched classes: [None, Some] +//│ | | | | | | | | post-processing the first branch +//│ | | | | | | | | postProcess <== Let(_, _) +//│ | | | | | | | | | postProcess <== Let(_, _) +//│ | | | | | | | | | | postProcess <== CaseOf(_, _) +//│ | | | | | | | | | | | found a BINARY case: y is Some +//│ | | | | | | | | | | | `y`'s matched classes: [None, Some] +//│ | | | | | | | | | | | post-processing the first branch +//│ | | | | | | | | | | | postProcess <== Let(_, _) +//│ | | | | | | | | | | | | postProcess <== Let(_, _) +//│ | | | | | | | | | | | | | postProcess <== App(_, _) +//│ | | | | | | | | | | | | | | CANNOT post-process +//│ | | | | | | | | | | | | | postProcess ==> +//│ | | | | | | | | | | | | postProcess ==> +//│ | | | | | | | | | | | postProcess ==> +//│ | | | | | | | | | | | disentangle <== y: None +//│ | | | | | | | | | | | | found a `CaseOf` that matches on y +//│ | | | | | | | | | | | | found a case branch matching against None +//│ | | | | | | | | | | | | found the end, stop +//│ | | | | | | | | | | | disentangle ==> `case y of { }` 1 +//│ | | | | | | | | | | | case y of { } is empty +//│ | | | | | | | | | | | found 1 case branches about None +//│ | | | | | | | | | | | merging terms <== [Var("xv")] +//│ | | | | | | | | | | | merging terms ==> Var("xv") +//│ | | | | | | | | | | | postProcess <== Var("xv") +//│ | | | | | | | | | | | | CANNOT post-process +//│ | | | | | | | | | | | postProcess ==> +//│ | | | | | | | | | | | found 1 cases +//│ | | | | | | | | | | postProcess ==> +//│ | | | | | | | | | postProcess ==> +//│ | | | | | | | | postProcess ==> +//│ | | | | | | | | disentangle <== x: None +//│ | | | | | | | | | found a `CaseOf` that does NOT match on x +//│ | | | | | | | | | found a case branch +//│ | | | | | | | | | disentangle <== x: None +//│ | | | | | | | | | | found a `CaseOf` that matches on x +//│ | | | | | | | | | | found a case branch matching against None +//│ | | | | | | | | | | found the end, stop +//│ | | | | | | | | | disentangle ==> `case x of { }` 1 +//│ | | | | | | | | | found a wildcard, stop +//│ | | | | | | | | | disentangle <== x: None +//│ | | | | | | | | | | found a `CaseOf` that matches on x +//│ | | | | | | | | | | found a case branch matching against None +//│ | | | | | | | | | | found the end, stop +//│ | | | | | | | | | disentangle ==> `case x of { }` 1 +//│ | | | | | | | | disentangle ==> `case y of { None => case x of { }; _ => case x of { } }` 2 +//│ | | | | | | | | found 2 case branches about None +//│ | | | | | | | | merging terms <== [0, CaseOf(_, _)] +//│ | | | | | | | | | mergeTerms <== integer literal `case` expression +//│ | | | | | | | | | | CANNOT merge. Discard t2. +//│ | | | | | | | | merging terms ==> 0 +//│ | | | | | | | | postProcess <== 0 +//│ | | | | | | | | | CANNOT post-process +//│ | | | | | | | | postProcess ==> +//│ | | | | | | | | found 1 cases +//│ | | | | | | | postProcess ==> +//│ | | | | | | | Post-processed UCS term: +//│ | | | | | | | x match +//│ | | | | | | | case Some => +//│ | | | | | | | let args_x$Some = (Some).unapply(x,) +//│ | | | | | | | let xv* = (args_x$Some).0 +//│ | | | | | | | y match +//│ | | | | | | | case Some => +//│ | | | | | | | let args_y$Some = (Some).unapply(y,) +//│ | | | | | | | let yv* = (args_y$Some).0 +//│ | | | | | | | +(xv, yv,) +//│ | | | | | | | case None => xv +//│ | | | | | | | case None => 0 +//│ | | | | | | | case _ => +//│ | | | | | | | y match +//│ | | | | | | | case None => x match +//│ | | | | | | | case _ => x match +//│ | | | | | | visitIf ==> () +//│ | | | | | visitTerm ==> If(_) +//│ | | | | visitTerm ==> Blk(_) +//│ | | | visitTerm ==> Lam(_, _) +//│ | | visitFunction ==> add_5 +//│ | visitTypingUnit ==> add_5 +//│ process ==> add_5 //│ fun add_5: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) add_5(None, None) From a08eba62a1cac83407b740b566c72bd55274b8d2 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sat, 25 Nov 2023 04:25:55 +0800 Subject: [PATCH 004/143] Fix the disentanglement of case branches in post-processing --- .../mlscript/ucs/stages/PostProcessing.scala | 86 ++-- .../src/test/diff/pretyper/ucs/DualOption.mls | 444 +----------------- 2 files changed, 50 insertions(+), 480 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index f9ae645e..51a0bce0 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -6,6 +6,7 @@ import mlscript.utils._, shorthands._ import mlscript.CaseBranches import mlscript.Message, Message.MessageContext import mlscript.pretyper.shortName +import scala.annotation.tailrec trait PostProcessing { self: mlscript.pretyper.Traceable => import PostProcessing._ @@ -34,17 +35,14 @@ trait PostProcessing { self: mlscript.pretyper.Traceable => // For each case class name, distangle case branch body terms from the false branch. .foldLeft[Opt[Term] -> Ls[Var -> Term]](S(falseBranch) -> Nil) { case ((S(remainingTerm), cases), className) => - val (leftoverTerm, caseBranchBodyTerms) = disentangle(remainingTerm, scrutineeSymbol, className) - avoidEmptyCaseOf(leftoverTerm) -> (caseBranchBodyTerms match { - case Nil => - println(s"no case branches about $className were found") + val (leftoverTerm, extracted) = disentangle(remainingTerm, scrutineeSymbol, className) + avoidEmptyCaseOf(leftoverTerm) -> (extracted match { + case N => + println(s"no extracted term about $className") cases - case terms @ (head :: tail) => - println(s"found ${terms.length} case branches about $className") - val body = trace(s"merging terms <== ${terms.iterator.map(shortName).mkString("[", ", ", "]")}") { - tail.foldLeft(head)(mergeTerms) - }(t => s"merging terms ==> ${shortName(t)}") - className -> postProcess(body) :: cases + case terms @ S(extractedTerm) => + println(s"extracted a term about $className") + className -> postProcess(extractedTerm) :: cases }) // TODO: Consider either make the first tuple element as non-optional. // TODO: Then, write a new helper function which checks if the term is an empty `CaseOf`. @@ -66,6 +64,15 @@ trait PostProcessing { self: mlscript.pretyper.Traceable => private def avoidEmptyCaseOf(term: Term): Opt[Term] = term match { case CaseOf(_, NoCases) => println(s"$term is empty"); N + case CaseOf(_, cases: Case) => + @tailrec + def containsNoWildcard(cases: CaseBranches): Bool = cases match { + case NoCases => true + case Wildcard(_) => false + case Case(_, body, rest) => + avoidEmptyCaseOf(body) === N && containsNoWildcard(rest) + } + if (containsNoWildcard(cases)) S(term) else N case _ => S(term) } @@ -91,60 +98,65 @@ trait PostProcessing { self: mlscript.pretyper.Traceable => /** * Disentangle case branches that match `scrutinee` against `className` from `term`. * The `term` should be obtained from _normalization_. Because there may exists multiple - * `CaseOf`s which contains such case branches, we return a list of disentangled terms. + * `CaseOf`s which contains such case branches, we merge them on the fly. * * @param term the term to disentangle from * @param scrutinee the symbol of the scrutinee variable * @param className the class name - * @return the remaining term and the disentangled case branch body terms + * @return the remaining term and the disentangled term */ - def disentangle(term: Term, scrutinee: ScrutineeSymbol, className: Var): (Term, Ls[Term]) = - trace[(Term, Ls[Term])](s"disentangle <== ${scrutinee.name}: $className") { + def disentangle(term: Term, scrutinee: ScrutineeSymbol, className: Var): (Term, Opt[Term]) = + trace[(Term, Opt[Term])](s"disentangle <== ${scrutinee.name}: $className") { term match { case top @ CaseOf(scrutineeVar: Var, cases) => if (scrutineeVar.symbol match { case s: ScrutineeSymbol => s === scrutinee; case _ => false }) { println(s"found a `CaseOf` that matches on ${scrutinee.name}") - def rec(cases: CaseBranches): (CaseBranches, Ls[Term]) = cases match { - case NoCases => println("found the end, stop"); NoCases -> Nil + def rec(cases: CaseBranches): (CaseBranches, Opt[Term]) = cases match { + case NoCases => println("found the end, stop"); NoCases -> N case wildcard @ Wildcard(body) => println("found a wildcard, stop") - val (y, ns) = disentangle(body, scrutinee, className) - wildcard.copy(body = y) -> ns + val (y, n) = disentangle(body, scrutinee, className) + wildcard.copy(body = y) -> n case kase @ Case(`className`, body, rest) => println(s"found a case branch matching against $className") - val (y, ns) = rec(rest) - y -> (ns :+ body) + val (y, n) = rec(rest) + y -> S(n.fold(body)(mergeTerms(_, body))) case kase @ Case(otherClassName, body, rest) => - val (y, ns) = rec(rest) - kase.copy(rest = y) -> ns + val (y, n) = rec(rest) + kase.copy(rest = y) -> n } - val (y, ns) = rec(cases) - top.copy(cases = y) -> ns + val (y, n) = rec(cases) + top.copy(cases = y) -> n } else { println(s"found a `CaseOf` that does NOT match on ${scrutinee.name}") - def rec(cases: CaseBranches): (CaseBranches, Ls[Term]) = cases match { - case NoCases => println("found the end, stop"); NoCases -> Nil + def rec(cases: CaseBranches): (CaseBranches, CaseBranches) = cases match { + case NoCases => + println("found the end, stop") + NoCases -> NoCases case wildcard @ Wildcard(body) => println("found a wildcard, stop") - val (y, ns) = disentangle(body, scrutinee, className) - wildcard.copy(body = y) -> ns + val (y, n) = disentangle(body, scrutinee, className) + wildcard.copy(body = y) -> n.fold(NoCases: CaseBranches)(Wildcard(_)) case kase @ Case(_, body, rest) => println(s"found a case branch") - val (y1, ns1) = disentangle(body, scrutinee, className) - val (y2, ns) = rec(rest) - kase.copy(body = y1, rest = y2) -> (ns1 ++ ns) + val (y1, n1) = disentangle(body, scrutinee, className) + val (y2, n2) = rec(rest) + kase.copy(body = y1, rest = y2) -> (n1 match { + case S(term) => kase.copy(body = term, rest = n2) + case N => n2 + }) } - val (y, ns) = rec(cases) - top.copy(cases = y) -> ns + val (y, n) = rec(cases) + top.copy(cases = y) -> (if (n === NoCases) N else S(top.copy(cases = n))) } case let @ Let(_, _, _, body) => - val (y, ns) = disentangle(body, scrutinee, className) - let.copy(body = y) -> ns - case other => println(s"CANNOT disentangle"); other -> Nil + val (y, n) = disentangle(body, scrutinee, className) + let.copy(body = y) -> n.map(t => let.copy(body = t)) + case other => println(s"CANNOT disentangle"); other -> N } - }({ case (y, ns) => s"disentangle ==> `${y}` ${ns.length}" }) + }({ case (y, n) => s"disentangle ==> `${shortName(y)}` and `${n.fold("_")(shortName)}`" }) def cascadeConsecutiveCaseOf(term: Term): Term = trace(s"cascade consecutive CaseOf <== ${term.describe}") { // Normalized terms are constructed using `Let` and `CaseOf`. diff --git a/shared/src/test/diff/pretyper/ucs/DualOption.mls b/shared/src/test/diff/pretyper/ucs/DualOption.mls index 9b245535..4460835a 100644 --- a/shared/src/test/diff/pretyper/ucs/DualOption.mls +++ b/shared/src/test/diff/pretyper/ucs/DualOption.mls @@ -115,455 +115,13 @@ add_4(Some(5), Some(9)) //│ res //│ = 14 -:dpt + fun add_5(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv y is None and x is Some(xv) then xv x is None and y is Some(yv) then yv y is None and x is None then 0 -//│ process <== : {fun add_5} -//│ | visitTypingUnit <== : {fun add_5} -//│ | | 1. scope = {add_5} -//│ | | 2. scope = {add_5} -//│ | | visitFunction <== add_5 -//│ | | | visitTerm <== Lam(_, _) -//│ | | | | visitTerm <== Blk(_) -//│ | | | | | visitTerm <== If(_) -//│ | | | | | | visitIf -//│ | | | | | | | Transformed UCS term: -//│ | | | | | | | if -//│ | | | | | | | x is Some(xv) and y is Some(yv) then +(xv, yv,) -//│ | | | | | | | y is None and x is Some(xv) then xv -//│ | | | | | | | x is None and y is Some(yv) then yv -//│ | | | | | | | y is None and x is None then 0 -//│ | | | | | | | Desugared UCS term: -//│ | | | | | | | if -//│ | | | | | | | x is Some(xv) y is Some(yv) then +(xv, yv,) -//│ | | | | | | | y is None x is Some(xv) then xv -//│ | | | | | | | x is None y is Some(yv) then yv -//│ | | | | | | | y is None x is None then 0 -//│ | | | | | | | visitSplit <== [add_5, x, y] -//│ | | | | | | | | found branch: x is Some(xv) -//│ | | | | | | | | visitTerm <== Var("x") -//│ | | | | | | | | | visitVar(name = "x") -//│ | | | | | | | | | | resolveVar(name = "x") -//│ | | | | | | | | visitTerm ==> Var("x") -//│ | | | | | | | | visitPattern <== Some(xv) -//│ | | | | | | | | visitPattern ==> [xv] -//│ | | | | | | | | visitSplit <== [add_5, x, y, xv] -//│ | | | | | | | | | found branch: y is Some(yv) -//│ | | | | | | | | | visitTerm <== Var("y") -//│ | | | | | | | | | | visitVar(name = "y") -//│ | | | | | | | | | | | resolveVar(name = "y") -//│ | | | | | | | | | visitTerm ==> Var("y") -//│ | | | | | | | | | visitPattern <== Some(yv) -//│ | | | | | | | | | visitPattern ==> [yv] -//│ | | | | | | | | | visitSplit <== [x, y, add_5, xv, yv] -//│ | | | | | | | | | | visitTerm <== App(_, _) -//│ | | | | | | | | | | | visitTerm <== Var("+") -//│ | | | | | | | | | | | | visitVar(name = "+") -//│ | | | | | | | | | | | | | resolveVar(name = "+") -//│ | | | | | | | | | | | visitTerm ==> Var("+") -//│ | | | | | | | | | | | visitTerm <== Tup(_, _) -//│ | | | | | | | | | | | | visitTerm <== Var("xv") -//│ | | | | | | | | | | | | | visitVar(name = "xv") -//│ | | | | | | | | | | | | | | resolveVar(name = "xv") -//│ | | | | | | | | | | | | visitTerm ==> Var("xv") -//│ | | | | | | | | | | | | visitTerm <== Var("yv") -//│ | | | | | | | | | | | | | visitVar(name = "yv") -//│ | | | | | | | | | | | | | | resolveVar(name = "yv") -//│ | | | | | | | | | | | | visitTerm ==> Var("yv") -//│ | | | | | | | | | | | visitTerm ==> Tup(_, _) -//│ | | | | | | | | | | visitTerm ==> App(_, _) -//│ | | | | | | | | | visitSplit <== [add_5, x, y, xv] -//│ | | | | | | | | | | the end -//│ | | | | | | | | visitSplit <== [add_5, x, y] -//│ | | | | | | | | | found branch: y is None -//│ | | | | | | | | | visitTerm <== Var("y") -//│ | | | | | | | | | | visitVar(name = "y") -//│ | | | | | | | | | | | resolveVar(name = "y") -//│ | | | | | | | | | visitTerm ==> Var("y") -//│ | | | | | | | | | visitPattern <== None -//│ | | | | | | | | | visitPattern ==> [] -//│ | | | | | | | | | visitSplit <== [add_5, x, y] -//│ | | | | | | | | | | found branch: x is Some(xv) -//│ | | | | | | | | | | visitTerm <== Var("x") -//│ | | | | | | | | | | | visitVar(name = "x") -//│ | | | | | | | | | | | | resolveVar(name = "x") -//│ | | | | | | | | | | visitTerm ==> Var("x") -//│ | | | | | | | | | | visitPattern <== Some(xv) -//│ | | | | | | | | | | visitPattern ==> [xv] -//│ | | | | | | | | | | visitSplit <== [add_5, x, y, xv] -//│ | | | | | | | | | | | visitTerm <== Var("xv") -//│ | | | | | | | | | | | | visitVar(name = "xv") -//│ | | | | | | | | | | | | | resolveVar(name = "xv") -//│ | | | | | | | | | | | visitTerm ==> Var("xv") -//│ | | | | | | | | | | visitSplit <== [add_5, x, y] -//│ | | | | | | | | | | | the end -//│ | | | | | | | | | visitSplit <== [add_5, x, y] -//│ | | | | | | | | | | found branch: x is None -//│ | | | | | | | | | | visitTerm <== Var("x") -//│ | | | | | | | | | | | visitVar(name = "x") -//│ | | | | | | | | | | | | resolveVar(name = "x") -//│ | | | | | | | | | | visitTerm ==> Var("x") -//│ | | | | | | | | | | visitPattern <== None -//│ | | | | | | | | | | visitPattern ==> [] -//│ | | | | | | | | | | visitSplit <== [add_5, x, y] -//│ | | | | | | | | | | | found branch: y is Some(yv) -//│ | | | | | | | | | | | visitTerm <== Var("y") -//│ | | | | | | | | | | | | visitVar(name = "y") -//│ | | | | | | | | | | | | | resolveVar(name = "y") -//│ | | | | | | | | | | | visitTerm ==> Var("y") -//│ | | | | | | | | | | | visitPattern <== Some(yv) -//│ | | | | | | | | | | | visitPattern ==> [yv] -//│ | | | | | | | | | | | visitSplit <== [add_5, x, y, yv] -//│ | | | | | | | | | | | | visitTerm <== Var("yv") -//│ | | | | | | | | | | | | | visitVar(name = "yv") -//│ | | | | | | | | | | | | | | resolveVar(name = "yv") -//│ | | | | | | | | | | | | visitTerm ==> Var("yv") -//│ | | | | | | | | | | | visitSplit <== [add_5, x, y] -//│ | | | | | | | | | | | | the end -//│ | | | | | | | | | | visitSplit <== [add_5, x, y] -//│ | | | | | | | | | | | found branch: y is None -//│ | | | | | | | | | | | visitTerm <== Var("y") -//│ | | | | | | | | | | | | visitVar(name = "y") -//│ | | | | | | | | | | | | | resolveVar(name = "y") -//│ | | | | | | | | | | | visitTerm ==> Var("y") -//│ | | | | | | | | | | | visitPattern <== None -//│ | | | | | | | | | | | visitPattern ==> [] -//│ | | | | | | | | | | | visitSplit <== [add_5, x, y] -//│ | | | | | | | | | | | | found branch: x is None -//│ | | | | | | | | | | | | visitTerm <== Var("x") -//│ | | | | | | | | | | | | | visitVar(name = "x") -//│ | | | | | | | | | | | | | | resolveVar(name = "x") -//│ | | | | | | | | | | | | visitTerm ==> Var("x") -//│ | | | | | | | | | | | | visitPattern <== None -//│ | | | | | | | | | | | | visitPattern ==> [] -//│ | | | | | | | | | | | | visitSplit <== [add_5, x, y] -//│ | | | | | | | | | | | | | visitTerm <== 0 -//│ | | | | | | | | | | | | | visitTerm ==> 0 -//│ | | | | | | | | | | | | visitSplit <== [add_5, x, y] -//│ | | | | | | | | | | | | | the end -//│ | | | | | | | | | | | visitSplit <== [add_5, x, y] -//│ | | | | | | | | | | | | the end -//│ | | | | | | | normalizeToTerm -//│ | | | | | | | | match x with Some(xv) -//│ | | | | | | | | compute true branch -//│ | | | | | | | | | S+ <== x is Some(xv) -//│ | | | | | | | | | | scrutinee: x =/= y -//│ | | | | | | | | | | S+ <== x is Some(xv) -//│ | | | | | | | | | | | the end -//│ | | | | | | | | | | S+ ==> then +(xv, yv,) -//│ | | | | | | | | | | specialized next -//│ | | | | | | | | | | S+ <== x is Some(xv) -//│ | | | | | | | | | | | scrutinee: x =/= y -//│ | | | | | | | | | | | S+ <== x is Some(xv) -//│ | | | | | | | | | | | | scrutinee: x === x -//│ | | | | | | | | | | | | class name: Some === Some -//│ | | | | | | | | | | | | S+ <== x is Some(xv) -//│ | | | | | | | | | | | | | the end -//│ | | | | | | | | | | | | S+ ==> then xv -//│ | | | | | | | | | | | S+ ==> then xv -//│ | | | | | | | | | | | specialized next -//│ | | | | | | | | | | | S+ <== x is Some(xv) -//│ | | | | | | | | | | | | scrutinee: x === x -//│ | | | | | | | | | | | | class name: Some =/= None -//│ | | | | | | | | | | | | specialized next -//│ | | | | | | | | | | | | S+ <== x is Some(xv) -//│ | | | | | | | | | | | | | scrutinee: x =/= y -//│ | | | | | | | | | | | | | S+ <== x is Some(xv) -//│ | | | | | | | | | | | | | | scrutinee: x === x -//│ | | | | | | | | | | | | | | class name: Some =/= None -//│ | | | | | | | | | | | | | | specialized next -//│ | | | | | | | | | | | | | | S+ <== x is Some(xv) -//│ | | | | | | | | | | | | | | | the end -//│ | | | | | | | | | | | | | | S+ ==> -//│ | | | | | | | | | | | | | S+ ==> -//│ | | | | | | | | | | | | | specialized next -//│ | | | | | | | | | | | | | S+ <== x is Some(xv) -//│ | | | | | | | | | | | | | | the end -//│ | | | | | | | | | | | | | S+ ==> -//│ | | | | | | | | | | | | S+ ==> y is None -//│ | | | | | | | | | | | S+ ==> y is None -//│ | | | | | | | | | | S+ ==> -//│ | | | | | | | | | | y is None then xv -//│ | | | | | | | | | | y is None -//│ | | | | | | | | | S+ ==> -//│ | | | | | | | | | y is Some(yv) then +(xv, yv,) -//│ | | | | | | | | | y is None then xv -//│ | | | | | | | | | y is None -//│ | | | | | | | | | normalizeToTerm -//│ | | | | | | | | | | match y with Some(yv) -//│ | | | | | | | | | | compute true branch -//│ | | | | | | | | | | | S+ <== y is Some(yv) -//│ | | | | | | | | | | | | the end -//│ | | | | | | | | | | | S+ ==> then +(xv, yv,) -//│ | | | | | | | | | | | normalizeToTerm -//│ | | | | | | | | | | compute false branch -//│ | | | | | | | | | | | S- <== y is Some(yv) -//│ | | | | | | | | | | | | scrutinee: y === y -//│ | | | | | | | | | | | | class name: Some =/= None -//│ | | | | | | | | | | | | S- <== y is Some(yv) -//│ | | | | | | | | | | | | | scrutinee: y === y -//│ | | | | | | | | | | | | | class name: Some =/= None -//│ | | | | | | | | | | | | | S- <== y is Some(yv) -//│ | | | | | | | | | | | | | | the end -//│ | | | | | | | | | | | | | S- ==> -//│ | | | | | | | | | | | | S- ==> y is None -//│ | | | | | | | | | | | S- ==> -//│ | | | | | | | | | | | y is None then xv -//│ | | | | | | | | | | | y is None -//│ | | | | | | | | | | | normalizeToCaseBranches -//│ | | | | | | | | | | | | normalizeToTerm -//│ | | | | | | | | | | | | | match y with None -//│ | | | | | | | | | | | | | S+ <== y is None -//│ | | | | | | | | | | | | | | the end -//│ | | | | | | | | | | | | | S+ ==> then xv -//│ | | | | | | | | | | | | | normalizeToTerm -//│ | | | | | | | | | | | | | S- <== y is None -//│ | | | | | | | | | | | | | | scrutinee: y === y -//│ | | | | | | | | | | | | | | class name: None === None -//│ | | | | | | | | | | | | | | S- <== y is None -//│ | | | | | | | | | | | | | | | the end -//│ | | | | | | | | | | | | | | S- ==> -//│ | | | | | | | | | | | | | S- ==> -//│ | | | | | | | | | | | | | normalizeToCaseBranches -//│ | | | | | | | | compute false branch -//│ | | | | | | | | | S- <== x is Some(xv) -//│ | | | | | | | | | | scrutinee: x =/= y -//│ | | | | | | | | | | S- <== x is Some(xv) -//│ | | | | | | | | | | | scrutinee: x === x -//│ | | | | | | | | | | | class name: Some === Some -//│ | | | | | | | | | | | S- <== x is Some(xv) -//│ | | | | | | | | | | | | the end -//│ | | | | | | | | | | | S- ==> -//│ | | | | | | | | | | S- ==> -//│ | | | | | | | | | | S- <== x is Some(xv) -//│ | | | | | | | | | | | scrutinee: x === x -//│ | | | | | | | | | | | class name: Some =/= None -//│ | | | | | | | | | | | S- <== x is Some(xv) -//│ | | | | | | | | | | | | scrutinee: x =/= y -//│ | | | | | | | | | | | | S- <== x is Some(xv) -//│ | | | | | | | | | | | | | scrutinee: x === x -//│ | | | | | | | | | | | | | class name: Some =/= None -//│ | | | | | | | | | | | | | S- <== x is Some(xv) -//│ | | | | | | | | | | | | | | the end -//│ | | | | | | | | | | | | | S- ==> -//│ | | | | | | | | | | | | S- ==> x is None then 0 -//│ | | | | | | | | | | | | S- <== x is Some(xv) -//│ | | | | | | | | | | | | | the end -//│ | | | | | | | | | | | | S- ==> -//│ | | | | | | | | | | | S- ==> y is None x is None then 0 -//│ | | | | | | | | | | S- ==> -//│ | | | | | | | | | | x is None y is Some(yv) then yv -//│ | | | | | | | | | | y is None x is None then 0 -//│ | | | | | | | | | S- ==> -//│ | | | | | | | | | y is None -//│ | | | | | | | | | x is None y is Some(yv) then yv -//│ | | | | | | | | | y is None x is None then 0 -//│ | | | | | | | | | normalizeToCaseBranches -//│ | | | | | | | | | | normalizeToTerm -//│ | | | | | | | | | | | match y with None -//│ | | | | | | | | | | | S+ <== y is None -//│ | | | | | | | | | | | | scrutinee: y =/= x -//│ | | | | | | | | | | | | S+ <== y is None -//│ | | | | | | | | | | | | | scrutinee: y === y -//│ | | | | | | | | | | | | | class name: None =/= Some -//│ | | | | | | | | | | | | | specialized next -//│ | | | | | | | | | | | | | S+ <== y is None -//│ | | | | | | | | | | | | | | the end -//│ | | | | | | | | | | | | | S+ ==> -//│ | | | | | | | | | | | | S+ ==> -//│ | | | | | | | | | | | | specialized next -//│ | | | | | | | | | | | | S+ <== y is None -//│ | | | | | | | | | | | | | scrutinee: y === y -//│ | | | | | | | | | | | | | class name: None === None -//│ | | | | | | | | | | | | | S+ <== y is None -//│ | | | | | | | | | | | | | | scrutinee: y =/= x -//│ | | | | | | | | | | | | | | S+ <== y is None -//│ | | | | | | | | | | | | | | | the end -//│ | | | | | | | | | | | | | | S+ ==> then 0 -//│ | | | | | | | | | | | | | | specialized next -//│ | | | | | | | | | | | | | | S+ <== y is None -//│ | | | | | | | | | | | | | | | the end -//│ | | | | | | | | | | | | | | S+ ==> -//│ | | | | | | | | | | | | | S+ ==> x is None then 0 -//│ | | | | | | | | | | | | S+ ==> x is None then 0 -//│ | | | | | | | | | | | S+ ==> -//│ | | | | | | | | | | | x is None -//│ | | | | | | | | | | | x is None then 0 -//│ | | | | | | | | | | | normalizeToTerm -//│ | | | | | | | | | | | | match x with None -//│ | | | | | | | | | | | | S+ <== x is None -//│ | | | | | | | | | | | | | scrutinee: x === x -//│ | | | | | | | | | | | | | class name: None === None -//│ | | | | | | | | | | | | | S+ <== x is None -//│ | | | | | | | | | | | | | | the end -//│ | | | | | | | | | | | | | S+ ==> then 0 -//│ | | | | | | | | | | | | S+ ==> then 0 -//│ | | | | | | | | | | | | normalizeToTerm -//│ | | | | | | | | | | | | S- <== x is None -//│ | | | | | | | | | | | | | scrutinee: x === x -//│ | | | | | | | | | | | | | class name: None === None -//│ | | | | | | | | | | | | | S- <== x is None -//│ | | | | | | | | | | | | | | the end -//│ | | | | | | | | | | | | | S- ==> -//│ | | | | | | | | | | | | S- ==> -//│ | | | | | | | | | | | | normalizeToCaseBranches -//│ | | | | | | | | | | | S- <== y is None -//│ | | | | | | | | | | | | scrutinee: y =/= x -//│ | | | | | | | | | | | | S- <== y is None -//│ | | | | | | | | | | | | | scrutinee: y === y -//│ | | | | | | | | | | | | | class name: None =/= Some -//│ | | | | | | | | | | | | | S- <== y is None -//│ | | | | | | | | | | | | | | the end -//│ | | | | | | | | | | | | | S- ==> -//│ | | | | | | | | | | | | S- ==> y is Some(yv) then yv -//│ | | | | | | | | | | | | S- <== y is None -//│ | | | | | | | | | | | | | scrutinee: y === y -//│ | | | | | | | | | | | | | class name: None === None -//│ | | | | | | | | | | | | | S- <== y is None -//│ | | | | | | | | | | | | | | the end -//│ | | | | | | | | | | | | | S- ==> -//│ | | | | | | | | | | | | S- ==> -//│ | | | | | | | | | | | S- ==> x is None y is Some(yv) then yv -//│ | | | | | | | | | | | normalizeToCaseBranches -//│ | | | | | | | | | | | | normalizeToTerm -//│ | | | | | | | | | | | | | match x with None -//│ | | | | | | | | | | | | | S+ <== x is None -//│ | | | | | | | | | | | | | | scrutinee: x =/= y -//│ | | | | | | | | | | | | | | S+ <== x is None -//│ | | | | | | | | | | | | | | | the end -//│ | | | | | | | | | | | | | | S+ ==> then yv -//│ | | | | | | | | | | | | | | specialized next -//│ | | | | | | | | | | | | | | S+ <== x is None -//│ | | | | | | | | | | | | | | | the end -//│ | | | | | | | | | | | | | | S+ ==> -//│ | | | | | | | | | | | | | S+ ==> y is Some(yv) then yv -//│ | | | | | | | | | | | | | normalizeToTerm -//│ | | | | | | | | | | | | | | match y with Some(yv) -//│ | | | | | | | | | | | | | | compute true branch -//│ | | | | | | | | | | | | | | | S+ <== y is Some(yv) -//│ | | | | | | | | | | | | | | | | the end -//│ | | | | | | | | | | | | | | | S+ ==> then yv -//│ | | | | | | | | | | | | | | | normalizeToTerm -//│ | | | | | | | | | | | | | | compute false branch -//│ | | | | | | | | | | | | | | | S- <== y is Some(yv) -//│ | | | | | | | | | | | | | | | | the end -//│ | | | | | | | | | | | | | | | S- ==> -//│ | | | | | | | | | | | | | | | normalizeToCaseBranches -//│ | | | | | | | | | | | | | S- <== x is None -//│ | | | | | | | | | | | | | | the end -//│ | | | | | | | | | | | | | S- ==> -//│ | | | | | | | | | | | | | normalizeToCaseBranches -//│ | | | | | | | Normalized UCS term: -//│ | | | | | | | x match -//│ | | | | | | | case Some => -//│ | | | | | | | let args_x$Some = (Some).unapply(x,) -//│ | | | | | | | let xv* = (args_x$Some).0 -//│ | | | | | | | y match -//│ | | | | | | | case Some => -//│ | | | | | | | let args_y$Some = (Some).unapply(y,) -//│ | | | | | | | let yv* = (args_y$Some).0 -//│ | | | | | | | +(xv, yv,) -//│ | | | | | | | case _ => -//│ | | | | | | | y match -//│ | | | | | | | case None => xv -//│ | | | | | | | case _ => -//│ | | | | | | | y match -//│ | | | | | | | case None => -//│ | | | | | | | x match -//│ | | | | | | | case None => 0 -//│ | | | | | | | case _ => -//│ | | | | | | | x match -//│ | | | | | | | case None => -//│ | | | | | | | y match -//│ | | | | | | | case Some => -//│ | | | | | | | let args_y$Some = (Some).unapply(y,) -//│ | | | | | | | let yv* = (args_y$Some).0 -//│ | | | | | | | yv -//│ | | | | | | | postProcess <== CaseOf(_, _) -//│ | | | | | | | | found a BINARY case: x is Some -//│ | | | | | | | | `x`'s matched classes: [None, Some] -//│ | | | | | | | | post-processing the first branch -//│ | | | | | | | | postProcess <== Let(_, _) -//│ | | | | | | | | | postProcess <== Let(_, _) -//│ | | | | | | | | | | postProcess <== CaseOf(_, _) -//│ | | | | | | | | | | | found a BINARY case: y is Some -//│ | | | | | | | | | | | `y`'s matched classes: [None, Some] -//│ | | | | | | | | | | | post-processing the first branch -//│ | | | | | | | | | | | postProcess <== Let(_, _) -//│ | | | | | | | | | | | | postProcess <== Let(_, _) -//│ | | | | | | | | | | | | | postProcess <== App(_, _) -//│ | | | | | | | | | | | | | | CANNOT post-process -//│ | | | | | | | | | | | | | postProcess ==> -//│ | | | | | | | | | | | | postProcess ==> -//│ | | | | | | | | | | | postProcess ==> -//│ | | | | | | | | | | | disentangle <== y: None -//│ | | | | | | | | | | | | found a `CaseOf` that matches on y -//│ | | | | | | | | | | | | found a case branch matching against None -//│ | | | | | | | | | | | | found the end, stop -//│ | | | | | | | | | | | disentangle ==> `case y of { }` 1 -//│ | | | | | | | | | | | case y of { } is empty -//│ | | | | | | | | | | | found 1 case branches about None -//│ | | | | | | | | | | | merging terms <== [Var("xv")] -//│ | | | | | | | | | | | merging terms ==> Var("xv") -//│ | | | | | | | | | | | postProcess <== Var("xv") -//│ | | | | | | | | | | | | CANNOT post-process -//│ | | | | | | | | | | | postProcess ==> -//│ | | | | | | | | | | | found 1 cases -//│ | | | | | | | | | | postProcess ==> -//│ | | | | | | | | | postProcess ==> -//│ | | | | | | | | postProcess ==> -//│ | | | | | | | | disentangle <== x: None -//│ | | | | | | | | | found a `CaseOf` that does NOT match on x -//│ | | | | | | | | | found a case branch -//│ | | | | | | | | | disentangle <== x: None -//│ | | | | | | | | | | found a `CaseOf` that matches on x -//│ | | | | | | | | | | found a case branch matching against None -//│ | | | | | | | | | | found the end, stop -//│ | | | | | | | | | disentangle ==> `case x of { }` 1 -//│ | | | | | | | | | found a wildcard, stop -//│ | | | | | | | | | disentangle <== x: None -//│ | | | | | | | | | | found a `CaseOf` that matches on x -//│ | | | | | | | | | | found a case branch matching against None -//│ | | | | | | | | | | found the end, stop -//│ | | | | | | | | | disentangle ==> `case x of { }` 1 -//│ | | | | | | | | disentangle ==> `case y of { None => case x of { }; _ => case x of { } }` 2 -//│ | | | | | | | | found 2 case branches about None -//│ | | | | | | | | merging terms <== [0, CaseOf(_, _)] -//│ | | | | | | | | | mergeTerms <== integer literal `case` expression -//│ | | | | | | | | | | CANNOT merge. Discard t2. -//│ | | | | | | | | merging terms ==> 0 -//│ | | | | | | | | postProcess <== 0 -//│ | | | | | | | | | CANNOT post-process -//│ | | | | | | | | postProcess ==> -//│ | | | | | | | | found 1 cases -//│ | | | | | | | postProcess ==> -//│ | | | | | | | Post-processed UCS term: -//│ | | | | | | | x match -//│ | | | | | | | case Some => -//│ | | | | | | | let args_x$Some = (Some).unapply(x,) -//│ | | | | | | | let xv* = (args_x$Some).0 -//│ | | | | | | | y match -//│ | | | | | | | case Some => -//│ | | | | | | | let args_y$Some = (Some).unapply(y,) -//│ | | | | | | | let yv* = (args_y$Some).0 -//│ | | | | | | | +(xv, yv,) -//│ | | | | | | | case None => xv -//│ | | | | | | | case None => 0 -//│ | | | | | | | case _ => -//│ | | | | | | | y match -//│ | | | | | | | case None => x match -//│ | | | | | | | case _ => x match -//│ | | | | | | visitIf ==> () -//│ | | | | | visitTerm ==> If(_) -//│ | | | | visitTerm ==> Blk(_) -//│ | | | visitTerm ==> Lam(_, _) -//│ | | visitFunction ==> add_5 -//│ | visitTypingUnit ==> add_5 -//│ process ==> add_5 //│ fun add_5: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) add_5(None, None) From 63677a17543706250b924c32710cfc28a5cc0e07 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Mon, 27 Nov 2023 02:06:05 +0800 Subject: [PATCH 005/143] Add a basic coverage checker --- .../src/main/scala/mlscript/Diagnostic.scala | 1 + shared/src/main/scala/mlscript/helpers.scala | 20 +++ .../scala/mlscript/pretyper/PreTyper.scala | 2 + .../scala/mlscript/pretyper/Traceable.scala | 1 + .../main/scala/mlscript/ucs/DesugarUCS.scala | 10 +- .../ucs/stages/CoverageChecking.scala | 125 ++++++++++++++++++ .../ucs/stages/ExhaustivenessChecking.scala | 5 - .../mlscript/ucs/stages/PostProcessing.scala | 3 +- .../scala/mlscript/ucs/stages/package.scala | 12 +- .../pretyper/ucs/coverage/MissingCases.mls | 45 +++++++ .../diff/pretyper/ucs/coverage/Tautology.mls | 27 ++++ .../src/test/scala/mlscript/DiffTests.scala | 1 + 12 files changed, 241 insertions(+), 11 deletions(-) create mode 100644 shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala delete mode 100644 shared/src/main/scala/mlscript/ucs/stages/ExhaustivenessChecking.scala create mode 100644 shared/src/test/diff/pretyper/ucs/coverage/MissingCases.mls create mode 100644 shared/src/test/diff/pretyper/ucs/coverage/Tautology.mls diff --git a/shared/src/main/scala/mlscript/Diagnostic.scala b/shared/src/main/scala/mlscript/Diagnostic.scala index b552dc7f..00d59a3b 100644 --- a/shared/src/main/scala/mlscript/Diagnostic.scala +++ b/shared/src/main/scala/mlscript/Diagnostic.scala @@ -19,6 +19,7 @@ object Diagnostic { sealed abstract class Source case object Lexing extends Source case object Parsing extends Source + case object PreTyping extends Source case object Typing extends Source case object Compilation extends Source case object Runtime extends Source diff --git a/shared/src/main/scala/mlscript/helpers.scala b/shared/src/main/scala/mlscript/helpers.scala index ce8a9a04..6f653b34 100644 --- a/shared/src/main/scala/mlscript/helpers.scala +++ b/shared/src/main/scala/mlscript/helpers.scala @@ -6,6 +6,7 @@ import scala.collection.mutable.{Map => MutMap, SortedMap => SortedMutMap, Set = import math.Ordered.orderingToOrdered import mlscript.utils._, shorthands._ +import scala.annotation.tailrec // Auxiliary definitions for types @@ -1065,6 +1066,25 @@ trait CaseBranchesImpl extends Located { self: CaseBranches => case NoCases => "" } + def foldLeft[A, B](z: A)(f: (A, SimpleTerm -> Term) => A)(e: (A, Opt[Term]) => B): B = { + @tailrec + def rec(acc: A, current: CaseBranches): B = current match { + case Case(pat, body, rest) => rec(f(acc, pat -> body), rest) + case Wildcard(body) => e(acc, S(body)) + case NoCases => e(acc, N) + } + rec(z, this) + } + + def foreach(f: Opt[SimpleTerm] -> Term => Unit): Unit = { + @tailrec + def rec(current: CaseBranches): Unit = current match { + case Case(pat, body, rest) => f(S(pat) -> body); rec(rest) + case Wildcard(body) => f(N -> body) + case NoCases => () + } + rec(this) + } } trait AdtMatchPatImpl extends Located { self: AdtMatchPat => diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index d4444507..07ccacb2 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -5,6 +5,8 @@ import mlscript._, utils._, shorthands._ import mlscript.codegen.Helpers.inspect class PreTyper(override val debugLevel: Opt[Int], useNewDefs: Bool) extends Traceable with DesugarUCS { + protected def raise(diagnostics: Ls[Diagnostic]): Unit = () + private def extractParameters(fields: Term): Ls[ValueSymbol] = fields match { case Tup(arguments) => if (useNewDefs) { diff --git a/shared/src/main/scala/mlscript/pretyper/Traceable.scala b/shared/src/main/scala/mlscript/pretyper/Traceable.scala index f707416c..781fa803 100644 --- a/shared/src/main/scala/mlscript/pretyper/Traceable.scala +++ b/shared/src/main/scala/mlscript/pretyper/Traceable.scala @@ -1,5 +1,6 @@ package mlscript.pretyper +import mlscript.Diagnostic import mlscript.utils._, shorthands._ trait Traceable { diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index 051541e6..636306f8 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -7,14 +7,14 @@ import mlscript._, utils._, shorthands._ import mlscript.codegen.Helpers.inspect import mlscript.Message, Message.MessageContext -import mlscript.ucs.core.Pattern.Name - // TODO: Rename to `Desugarer` once the old desugarer is removed. trait DesugarUCS extends Transformation with Desugaring with Normalization with PostProcessing - with ExhaustivenessChecking { self: PreTyper => + with CoverageChecking { self: PreTyper => + + protected def visitIf(`if`: If)(implicit scope: Scope): Unit = trace("visitIf") { // Stage 0: Transformation @@ -40,6 +40,10 @@ trait DesugarUCS extends Transformation val postProcessed = postProcess(normalized) println("Post-processed UCS term:") printNormalizedTerm(postProcessed) + // Stage 4: Coverage checking + val diagnostics = checkCoverage(postProcessed) + println(s"Coverage checking result: ${diagnostics.size} errors") + raise(diagnostics) // Epilogue `if`.desugaredTerm = S(normalized) }(_ => "visitIf ==> ()") diff --git a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala new file mode 100644 index 00000000..bc024782 --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala @@ -0,0 +1,125 @@ +package mlscript.ucs.stages + +import mlscript.{Case, CaseBranches, CaseOf, Let, Loc, NoCases, Term, Var, Wildcard} +import mlscript.pretyper.{ScrutineeSymbol, Symbol} +import mlscript.utils._, shorthands._ +import mlscript.Message, Message.MessageContext +import mlscript.pretyper.shortName +import scala.annotation.tailrec +import mlscript.{SimpleTerm, Diagnostic, ErrorReport, WarningReport} + +trait CoverageChecking { self: mlscript.pretyper.Traceable => + import CoverageChecking._ + + def checkCoverage(term: Term): Ls[Diagnostic] = { + val registry = collectRegistry(term) + println("collected match register: " + showRegistry(registry)) + checkCoverage(term, Map.empty, registry, Map.empty) + } + + private def collectRegistry(term: Term): MatchRegistry = { + @tailrec + def rec(acc: MatchRegistry, rest: Ls[Term]): MatchRegistry = rest match { + case Nil => acc + case head :: tail => head match { + case Let(_, _, _, body) => rec(acc, body :: tail) + case CaseOf(Scrutinee(_, scrutinee), cases) => + rec( + acc.updatedWith(scrutinee)(vs => S(cases.foldLeft(vs.getOrElse(Set.empty))({ + case (acc, (className: Var) -> _) => acc + className + case (acc, _) => acc + })((x, _) => x))), + tail ++ cases.foldLeft(Nil: Ls[Term])({ case (acc, _ -> body) => body :: acc })((x, _) => x) + ) + case _ => rec(acc, tail) + } + } + rec(Map.empty, term :: Nil) + } + + private def checkCoverage( + term: Term, + pending: MatchRegistry, + working: MatchRegistry, + assumptions: Map[ScrutineeSymbol, Var] + ): Ls[Diagnostic] = + trace(s"checkCoverage <== ${shortName(term)}, ${pending.size} pending, ${working.size} working") { + println(s"assumptions: " + (if (assumptions.isEmpty) "empty" else + assumptions.iterator.map { case (k, v) => s"${k.name} is $v" }.mkString(", ") + )) + term match { + case Let(_, _, _, body) => checkCoverage(body, pending, working, assumptions) + case CaseOf(Scrutinee(scrutineeVar, scrutinee), cases) => + println(s"scrutinee: ${scrutinee.name}") + // If the scrutinee is still pending (i.e., not matched yet), then we + // remove it from the pending list. If the scrutinee is matched, and + // there are still classes to be matched, then we find the remaining + // classes from the working list. If neither of the above is true, + // there are two possible cases: + // 1. The scrutinee has been never visited, which is an error. + // 2. It has been matched to be an instance of some class. Therefore, + // we need to check if this is a contradiction. + val (unseenClasses, newPending) = pending.get(scrutinee) match { + case S(matchedClasses) => matchedClasses -> (pending - scrutinee) + case N => working.get(scrutinee) match { + case S(unseenClasses) => unseenClasses -> pending + case N => throw new Exception(s"Scrutinee ${scrutinee.name} is not matched.") + } + } + // We keep removing classes from the unseen class list and adding + // diagnostics (warnings and errors). + cases.foldLeft((unseenClasses, Nil: Ls[Diagnostic]))({ + case ((red, acc), (className: Var) -> body) => + if (red contains className) { + ( + // The class is matched. Remove it from the class list. + red - className, + // We remove the scrutinee from the working list and add + // `scrutinee` is `className` to the assumptions. + acc ++ checkCoverage(body, newPending, working - scrutinee, assumptions + (scrutinee -> className)) + ) + } else { + red -> (acc :+ (assumptions.get(scrutinee) match { + case S(`className`) => WarningReport("tautology", Nil, Diagnostic.PreTyping) + case S(otherClassName) => ErrorReport("contradiction", Nil, Diagnostic.PreTyping) + case N => ErrorReport("unvisited scrutinee", Nil, Diagnostic.PreTyping) + })) + } + case (acc, _ -> _) => + println("CANNOT check literal patterns") + acc + }) { + case ((missingCases, diagnostics), N) => + println("MISSING cases: " + missingCases.iterator.map(className => s"${scrutinee.name} is $className").mkString("[", ", ", "]")) + diagnostics ++ (missingCases.iterator.map { className => + ErrorReport({ + val s1 = assumptions.iterator.map { + case (scrutinee, className) => s"${scrutinee.name} is $className" + }.mkString(", ") + val s2 = if (s1.isEmpty) "" else s" $s1, and" + msg"missing a case where$s2 ${scrutinee.name} is ${className.name}" -> N :: + msg"missing the condition" -> className.toLoc :: + assumptions.iterator.zipWithIndex.map({ case (scrutinee, className) -> index => + msg"${if (index > 0) "and" else "when"} ${scrutinee.name} is ${className.name}" -> className.toLoc + }).toList + }, true, Diagnostic.PreTyping) + }) + case ((unseenClasses, diagnostics), S(default)) => + println("wildcard case") + checkCoverage(default, newPending, working.updated(scrutinee, unseenClasses), assumptions) + } + case other => println("STOP"); Nil + } + }(ls => s"checkCoverage ==> ${ls.length}") +} + +object CoverageChecking { + type MatchRegistry = Map[ScrutineeSymbol, Set[Var]] + + /** A helper function that prints entries from the given registry line by line. */ + def showRegistry(registry: MatchRegistry): Str = + if (registry.isEmpty) "empty" else + registry.iterator.map { case (scrutinee, matchedClasses) => + matchedClasses.iterator.mkString(s">>> ${scrutinee.name} => [", ", ", "]") + }.mkString("\n", "\n", "") +} diff --git a/shared/src/main/scala/mlscript/ucs/stages/ExhaustivenessChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/ExhaustivenessChecking.scala deleted file mode 100644 index 33b17d88..00000000 --- a/shared/src/main/scala/mlscript/ucs/stages/ExhaustivenessChecking.scala +++ /dev/null @@ -1,5 +0,0 @@ -package mlscript.ucs.stages - -trait ExhaustivenessChecking { self: mlscript.pretyper.Traceable => - -} diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index 51a0bce0..416a329d 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -1,9 +1,8 @@ package mlscript.ucs.stages -import mlscript.{Case, CaseOf, Let, Loc, NoCases, Term, Var, Wildcard} +import mlscript.{Case, CaseBranches, CaseOf, Let, Loc, NoCases, Term, Var, Wildcard} import mlscript.pretyper.{ScrutineeSymbol, Symbol} import mlscript.utils._, shorthands._ -import mlscript.CaseBranches import mlscript.Message, Message.MessageContext import mlscript.pretyper.shortName import scala.annotation.tailrec diff --git a/shared/src/main/scala/mlscript/ucs/stages/package.scala b/shared/src/main/scala/mlscript/ucs/stages/package.scala index d1c5c2fb..7e23850f 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/package.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/package.scala @@ -1,10 +1,20 @@ package mlscript.ucs import mlscript.{Term, Var} -import mlscript.pretyper.Symbol +import mlscript.pretyper.ScrutineeSymbol import mlscript.utils._, shorthands._ package object stages { + object Scrutinee { + def unapply(term: Term): Opt[(Var, ScrutineeSymbol)] = term match { + case v @ Var(_) => v.symbol match { + case symbol: ScrutineeSymbol => S(v, symbol) + case _ => N + } + case _ => N + } + } + private[stages] sealed abstract class MatchOrNot { override def toString(): String = this match { case Yes => "+" diff --git a/shared/src/test/diff/pretyper/ucs/coverage/MissingCases.mls b/shared/src/test/diff/pretyper/ucs/coverage/MissingCases.mls new file mode 100644 index 00000000..b0050498 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/coverage/MissingCases.mls @@ -0,0 +1,45 @@ +:PreTyper + +class Some[T](value: T) +module None +type Option[T] = Some[T] | None +class Pair[A, B](x: A, y: B) +//│ class Some[T](value: T) +//│ module None +//│ type Option[T] = None | Some[T] +//│ class Pair[A, B](x: A, y: B) + +fun failed_add_1(x, y) = + if + x is Some(xv) and y is Some(yv) then xv + yv + x is Some(xv) and y is None then xv + x is None and y is Some(yv) then yv +//│ ╔══[ERROR] missing a case where x is None, and y is None +//│ ╟── missing the condition +//│ ║ l.15: x is Some(xv) and y is None then xv +//│ ║ ^^^^ +//│ ╟── when x is None +//│ ║ l.16: x is None and y is Some(yv) then yv +//│ ╙── ^^^^ +//│ fun failed_add_1: forall 'a. (None | Some[Int], Some[Int & 'a]) -> (Int | 'a) + +fun failed_add_2(x, y) = + if + x is Some(xv) and y is None then xv + x is None and y is Some(yv) then yv +//│ ╔══[ERROR] missing a case where x is Some, and y is Some +//│ ╟── missing the condition +//│ ║ l.29: x is None and y is Some(yv) then yv +//│ ║ ^^^^ +//│ ╟── when x is Some +//│ ║ l.28: x is Some(xv) and y is None then xv +//│ ╙── ^^^^ +//│ ╔══[ERROR] missing a case where x is None, and y is None +//│ ╟── missing the condition +//│ ║ l.28: x is Some(xv) and y is None then xv +//│ ║ ^^^^ +//│ ╟── when x is None +//│ ║ l.29: x is None and y is Some(yv) then yv +//│ ╙── ^^^^ +//│ fun failed_add_2: forall 'a. (None | Some['a], nothing) -> 'a + diff --git a/shared/src/test/diff/pretyper/ucs/coverage/Tautology.mls b/shared/src/test/diff/pretyper/ucs/coverage/Tautology.mls new file mode 100644 index 00000000..b1ef516c --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/coverage/Tautology.mls @@ -0,0 +1,27 @@ +:PreTyper + +class Some[T](value: T) +module None +type Option[T] = Some[T] | None +class Pair[A, B](x: A, y: B) +//│ class Some[T](value: T) +//│ module None +//│ type Option[T] = None | Some[T] +//│ class Pair[A, B](x: A, y: B) + +// FIXME +fun useless_negate_1(x) = + if + x is Some(y) and x is Some(z) then x + z +//│ ╔══[ERROR] identifier not found: z +//│ ║ l.15: x is Some(y) and x is Some(z) then x + z +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.15: x is Some(y) and x is Some(z) then x + z +//│ ║ ^^^^^ +//│ ╟── reference of type `Some[?T]` is not an instance of type `Int` +//│ ║ l.15: x is Some(y) and x is Some(z) then x + z +//│ ╙── ^ +//│ fun useless_negate_1: Some[anything] -> (Int | error) +//│ Code generation encountered an error: +//│ unresolved symbol z diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index 4efc4d6a..8e1191bd 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -523,6 +523,7 @@ class DiffTests val rootTypingUnit = TypingUnit(p.tops) if (usePreTyper) { val preTyper = new PreTyper(mode.dbgPreTyper, newDefs) { + override protected def raise(diagnostics: Ls[Diagnostic]): Unit = report(diagnostics) override def emitDbg(str: String): Unit = output(str) } // This should be passed to code generation somehow. From 86168500069b3379719e8ad431247e9542a28b32 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Thu, 30 Nov 2023 02:55:12 +0800 Subject: [PATCH 006/143] Improve detecting tautology and unreachable cases --- .../main/scala/mlscript/ucs/DesugarUCS.scala | 5 + .../main/scala/mlscript/ucs/Desugarer.scala | 16 +- .../main/scala/mlscript/ucs/MutCaseOf.scala | 2 +- shared/src/main/scala/mlscript/ucs/core.scala | 44 +++- .../ucs/stages/CoverageChecking.scala | 7 +- .../mlscript/ucs/stages/Normalization.scala | 37 +++- .../main/scala/mlscript/utils/package.scala | 5 +- shared/src/test/diff/codegen/NewMatching.mls | 2 +- .../diff/pretyper/ucs/coverage/Tautology.mls | 21 +- .../pretyper/ucs/coverage/Unreachable.mls | 193 ++++++++++++++++++ .../diff/pretyper/ucs/patterns/Literals.mls | 29 +++ 11 files changed, 330 insertions(+), 31 deletions(-) create mode 100644 shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls create mode 100644 shared/src/test/diff/pretyper/ucs/patterns/Literals.mls diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index 636306f8..c921cd04 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -18,6 +18,7 @@ trait DesugarUCS extends Transformation protected def visitIf(`if`: If)(implicit scope: Scope): Unit = trace("visitIf") { // Stage 0: Transformation + println("STEP 0") val transformed = transform(`if`, true) println("Transformed UCS term:") println(transformed.toString, 2) @@ -26,21 +27,25 @@ trait DesugarUCS extends Transformation // This stage will generate new names based on the position of the scrutinee. // Therefore, we need to call `visitSplit` to associate these newly generated // names with symbols. + println("STEP 1") val desugared = desugar(transformed) println(desugared.toString, 2) println("Desugared UCS term:") println(ucs.core.printSplit(desugared)) visitSplit(desugared) // Stage 2: Normalization + println("STEP 2") val normalized = normalize(desugared) println(normalized.toString, 2) println("Normalized UCS term:") printNormalizedTerm(normalized) // Stage 3: Post-processing + println("STEP 3") val postProcessed = postProcess(normalized) println("Post-processed UCS term:") printNormalizedTerm(postProcessed) // Stage 4: Coverage checking + println("STEP 4") val diagnostics = checkCoverage(postProcessed) println(s"Coverage checking result: ${diagnostics.size} errors") raise(diagnostics) diff --git a/shared/src/main/scala/mlscript/ucs/Desugarer.scala b/shared/src/main/scala/mlscript/ucs/Desugarer.scala index 34075afe..dec70ac7 100644 --- a/shared/src/main/scala/mlscript/ucs/Desugarer.scala +++ b/shared/src/main/scala/mlscript/ucs/Desugarer.scala @@ -269,10 +269,10 @@ class Desugarer extends TypeDefs { self: Typer => throw new DesugaringException({ val expected = positionals.length val actual = args.length - msg"${kind.str} $className expects ${expected.toString} ${ - "parameter".pluralize(expected) - } but found ${args.length.toString} ${ - "parameter".pluralize(expected) + msg"${kind.str} $className expects ${ + "parameter".pluralize(expected, true) + } but found ${ + "parameter".pluralize(args.length, true) }" }, app.toLoc) } @@ -305,8 +305,8 @@ class Desugarer extends TypeDefs { self: Typer => val num = td.positionals.length throw new DesugaringException({ val expected = td.positionals.length - msg"${td.kind.str} `$op` expects ${expected.toString} ${ - "parameter".pluralize(expected) + msg"${td.kind.str} `$op` expects ${ + "parameter".pluralize(expected, true) } but found two parameters" }, app.toLoc) } @@ -749,8 +749,8 @@ class Desugarer extends TypeDefs { self: Typer => throw new DesugaringException({ val numMissingCases = missingCases.size (msg"The match is not exhaustive." -> scrutinee.matchRootLoc) :: - (msg"The scrutinee at this position misses ${numMissingCases.toString} ${ - "case".pluralize(numMissingCases) + (msg"The scrutinee at this position misses ${ + "case".pluralize(numMissingCases, true) }." -> scrutinee.term.toLoc) :: missingCases.iterator.zipWithIndex.flatMap { case ((pattern, locations), index) => val patternName = pattern match { diff --git a/shared/src/main/scala/mlscript/ucs/MutCaseOf.scala b/shared/src/main/scala/mlscript/ucs/MutCaseOf.scala index 199d5619..a8bf41fa 100644 --- a/shared/src/main/scala/mlscript/ucs/MutCaseOf.scala +++ b/shared/src/main/scala/mlscript/ucs/MutCaseOf.scala @@ -285,7 +285,7 @@ object MutCaseOf { ) extends MutCaseOf { def describe: Str = { val n = branches.length - s"Match($scrutinee, $n ${"branch".pluralize(n, true)}, ${ + s"Match($scrutinee, ${"branch".pluralize(n, true, true)}, ${ wildcard.fold("no wildcard")(n => s"wildcard = ${n.kind}") })" } diff --git a/shared/src/main/scala/mlscript/ucs/core.scala b/shared/src/main/scala/mlscript/ucs/core.scala index 5b0dc640..d7886438 100644 --- a/shared/src/main/scala/mlscript/ucs/core.scala +++ b/shared/src/main/scala/mlscript/ucs/core.scala @@ -1,6 +1,7 @@ package mlscript.ucs -import mlscript.{Lit, Located, Term, Var} +import collection.mutable.Buffer +import mlscript.{Diagnostic, Lit, Loc, Located, Message, Term, Var} import mlscript.utils._, shorthands._ package object core { @@ -32,19 +33,60 @@ package object core { final case class Record(entries: List[(Var -> Var)]) extends Pattern { override def children: Ls[Located] = entries.iterator.flatMap { case (nme, als) => nme :: als :: Nil }.toList } + + def getParametersLoc(parameters: List[Opt[Var]]): Opt[Loc] = + parameters.foldLeft(None: Opt[Loc]) { + case (acc, N) => acc + case (N, S(nme)) => nme.toLoc + case (S(loc), S(nme)) => S(nme.toLoc.fold(loc)(loc ++ _)) + } + def getParametersLoc(parameters: Opt[List[Opt[Var]]]): Opt[Loc] = + parameters.fold(None: Opt[Loc])(getParametersLoc) + + def showParameters(parameters: Opt[List[Opt[Var]]]): Str = + parameters.fold("empty")(_.map(_.fold("_")(_.name)).mkString("[", ", ", "]")) } final case class Branch(scrutinee: Var, pattern: Pattern, continuation: Split) sealed abstract class Split { + @inline def ::(head: Branch): Split = Split.Cons(head, this) + /** + * Concatenates two splits. Beware that `that` may be discarded if `this` + * has an else branch. Make sure to make diagnostics for discarded `that`. + */ def ++(that: Split): Split = this match { case me: Split.Cons => me.copy(tail = me.tail ++ that) case me: Split.Let => me.copy(tail = me.tail ++ that) case _: Split.Else => this case Split.Nil => that } + + /** + * Returns true if the split has an else branch. + */ + lazy val hasElse: Bool = this match { + case Split.Cons(_, tail) => tail.hasElse + case Split.Let(_, _, _, tail) => tail.hasElse + case Split.Else(_) => true + case Split.Nil => false + } + + private val diagnostics: Buffer[Message -> Opt[Loc]] = Buffer.empty + + def withDiagnostic(diagnostic: Message -> Opt[Loc]): this.type = { + diagnostics += diagnostic + this + } + + def collectDiagnostics(): Ls[Message -> Opt[Loc]] = + diagnostics.toList ++ (this match { + case Split.Cons(_, tail) => tail.collectDiagnostics() + case Split.Let(_, _, _, tail) => tail.collectDiagnostics() + case Split.Else(_) | Split.Nil => Nil + }) } object Split { diff --git a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala index bc024782..0b3d0353 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala @@ -13,7 +13,7 @@ trait CoverageChecking { self: mlscript.pretyper.Traceable => def checkCoverage(term: Term): Ls[Diagnostic] = { val registry = collectRegistry(term) - println("collected match register: " + showRegistry(registry)) + println("collected match registry: " + showRegistry(registry)) checkCoverage(term, Map.empty, registry, Map.empty) } @@ -29,7 +29,10 @@ trait CoverageChecking { self: mlscript.pretyper.Traceable => case (acc, (className: Var) -> _) => acc + className case (acc, _) => acc })((x, _) => x))), - tail ++ cases.foldLeft(Nil: Ls[Term])({ case (acc, _ -> body) => body :: acc })((x, _) => x) + tail ++ cases.foldLeft + (Nil: Ls[Term]) + ({ case (acc, _ -> body) => body :: acc }) + ((acc, els) => els.fold(acc)(_ :: acc)) ) case _ => rec(acc, tail) } diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 623f8596..1d5fa1c5 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -97,8 +97,41 @@ trait Normalization { self: mlscript.pretyper.Traceable => case Pattern.Class(className, parameters) => if (className === otherClassName) { println(s"class name: $className === $otherClassName") - // TODO: Subsitute parameters to otherParameters - specialize(continuation ++ tail, Yes) + (parameters, otherParameters) match { + case (S(parameters), S(otherParameters)) => + if (parameters.length === otherParameters.length) { + println(s"same number of parameters: ${parameters.length}") + // Check if the class parameters are the same. + // Generate a function that generates bindings. + // TODO: Hygienic problems. + val addLetBindings = parameters.iterator.zip(otherParameters).zipWithIndex.foldLeft[Split => Split](identity) { + case (acc, N -> S(otherParameter) -> index) => ??? // TODO: How can we get the unapplied variable? + case (acc, S(parameter) -> S(otherParameter) -> index) if parameter.name =/= otherParameter.name => + println(s"different parameter names at $index: ${parameter.name} =/= ${otherParameter.name}") + tail => Split.Let(false, otherParameter, parameter, tail) + case (acc, _) => acc + } + // addLetBindings(specialize(continuation ++ tail, Yes)) + val specialized = addLetBindings(specialize(continuation, Yes)) + if (specialized.hasElse) { + println("tail is discarded") + specialized.withDiagnostic( + msg"Discarded split because of else branch" -> None // TODO: Synthesize locations + ) + } else { + specialized ++ specialize(tail, Yes) + } + } else { + throw new NormalizationException({ + msg"Mismatched numbers of parameters of ${className.name}:" -> otherClassName.toLoc :: + msg"There are ${"parameters".pluralize(parameters.length, true)}." -> Pattern.getParametersLoc(parameters) :: + msg"But there are ${"parameters".pluralize(otherParameters.length, true)}." -> Pattern.getParametersLoc(otherParameters) :: + Nil + }) + } + // TODO: Other cases + case (_, _) => specialize(continuation ++ tail, Yes) + } // END match } else { println(s"class name: $className =/= $otherClassName") specializedTail diff --git a/shared/src/main/scala/mlscript/utils/package.scala b/shared/src/main/scala/mlscript/utils/package.scala index 212f8cac..adb607a6 100644 --- a/shared/src/main/scala/mlscript/utils/package.scala +++ b/shared/src/main/scala/mlscript/utils/package.scala @@ -45,8 +45,9 @@ package object utils { def decapitalize: String = if (self.length === 0 || !self.charAt(0).isUpper) self else self.updated(0, self.charAt(0).toLower) - def pluralize(quantity: Int, es: Boolean = false): String = - if (quantity > 1) self + (if (es) "es" else "s") else self + def pluralize(quantity: Int, inclusive: Boolean = false, es: Boolean = false): String = + (if (inclusive) quantity.toString + " " else "") + + (if (quantity > 1 || quantity === 0) self + (if (es) "es" else "s") else self) @SuppressWarnings(Array("org.wartremover.warts.Equals")) def ===(other: String): Bool = self.equals(other) } diff --git a/shared/src/test/diff/codegen/NewMatching.mls b/shared/src/test/diff/codegen/NewMatching.mls index 50d2dbc1..8d270d15 100644 --- a/shared/src/test/diff/codegen/NewMatching.mls +++ b/shared/src/test/diff/codegen/NewMatching.mls @@ -218,7 +218,7 @@ fun ft(x) = if x is FooBar(x) then x _ then 0 -//│ ╔══[ERROR] class FooBar expects 0 parameter but found 1 parameter +//│ ╔══[ERROR] class FooBar expects 0 parameters but found 1 parameter //│ ║ l.219: FooBar(x) then x //│ ╙── ^^^^^^^^^ //│ fun ft: anything -> error diff --git a/shared/src/test/diff/pretyper/ucs/coverage/Tautology.mls b/shared/src/test/diff/pretyper/ucs/coverage/Tautology.mls index b1ef516c..a2b73e9f 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/Tautology.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/Tautology.mls @@ -9,19 +9,12 @@ class Pair[A, B](x: A, y: B) //│ type Option[T] = None | Some[T] //│ class Pair[A, B](x: A, y: B) -// FIXME fun useless_negate_1(x) = if - x is Some(y) and x is Some(z) then x + z -//│ ╔══[ERROR] identifier not found: z -//│ ║ l.15: x is Some(y) and x is Some(z) then x + z -//│ ╙── ^ -//│ ╔══[ERROR] Type mismatch in operator application: -//│ ║ l.15: x is Some(y) and x is Some(z) then x + z -//│ ║ ^^^^^ -//│ ╟── reference of type `Some[?T]` is not an instance of type `Int` -//│ ║ l.15: x is Some(y) and x is Some(z) then x + z -//│ ╙── ^ -//│ fun useless_negate_1: Some[anything] -> (Int | error) -//│ Code generation encountered an error: -//│ unresolved symbol z + x is Some(y) and x is Some(z) then y + z +//│ fun useless_negate_1: Some[Int] -> Int + +useless_negate_1(Some(1)) +//│ Int +//│ res +//│ = 2 diff --git a/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls b/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls new file mode 100644 index 00000000..1062a28a --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls @@ -0,0 +1,193 @@ +:PreTyper + +class Some[T](value: T) +module None +type Option[T] = Some[T] | None +class Pair[A, B](x: A, y: B) +//│ class Some[T](value: T) +//│ module None +//│ type Option[T] = None | Some[T] +//│ class Pair[A, B](x: A, y: B) + +fun f(x) = if x is Some(xv) and xv === 1 then true else false +//│ fun f: (Object & ~#Some | Some[Eql[1]]) -> Bool + +f(Some(1)) +f(Some(2)) +f(None) +//│ Bool +//│ res +//│ = true +//│ res +//│ = false +//│ res +//│ = false + +fun reachable_1(x) = + if x is + _ and f(x) then "cos" + Some(xv) then "sin" + None then "tan" +//│ fun reachable_1: (None | Some[Eql[1]] | Some[anything] & ~#Some) -> ("cos" | "sin" | "tan") + +reachable_1(Some(1)) +reachable_1(Some(2)) +reachable_1(None) +//│ "cos" | "sin" | "tan" +//│ res +//│ = 'cos' +//│ res +//│ = 'sin' +//│ res +//│ = 'tan' + +:dpt +fun unreachable_1(x) = + if x is + _ and + f(x) then "tmux" + else "screen" + Some(xv) then "sin" + None then "tan" +//│ process <== : {fun unreachable_1} +//│ | visitTypingUnit <== : {fun unreachable_1} +//│ | | 1. scope = {unreachable_1} +//│ | | 2. scope = {unreachable_1} +//│ | | visitFunction <== unreachable_1 +//│ | | | visitTerm <== Lam(_, _) +//│ | | | | visitTerm <== Blk(_) +//│ | | | | | visitTerm <== If(_) +//│ | | | | | | visitIf +//│ | | | | | | | STEP 0 +//│ | | | | | | | Transformed UCS term: +//│ | | | | | | | if x is +//│ | | | | | | | _ and f(x,) then "tmux" +//│ | | | | | | | else "screen" +//│ | | | | | | | Some(xv) then "sin" +//│ | | | | | | | None then "tan" +//│ | | | | | | | STEP 1 +//│ | | | | | | | Desugared UCS term: +//│ | | | | | | | if +//│ | | | | | | | x is _ +//│ | | | | | | | let scrut0 = f(x,) +//│ | | | | | | | scrut0 is true then "tmux" +//│ | | | | | | | else "screen" +//│ | | | | | | | x is Some(xv) then "sin" +//│ | | | | | | | x is None then "tan" +//│ | | | | | | | visitSplit <== [unreachable_1, x] +//│ | | | | | | | | found branch: x is _ +//│ | | | | | | | | visitTerm <== Var("x") +//│ | | | | | | | | | visitVar(name = "x") +//│ | | | | | | | | | | resolveVar(name = "x") +//│ | | | | | | | | visitTerm ==> Var("x") +//│ | | | | | | | | visitPattern <== _ +//│ | | | | | | | | visitPattern ==> [_] +//│ | | | | | | | | visitSplit <== [unreachable_1, x, _] +//│ | | | | | | | | | found let binding: "scrut0" +//│ | | | | | | | | | visitSplit <== [unreachable_1, x, _, scrut0] +//│ | | | | | | | | | | found branch: scrut0 is true +//│ | | | | | | | | | | visitTerm <== Var("scrut0") +//│ | | | | | | | | | | | visitVar(name = "scrut0") +//│ | | | | | | | | | | | | resolveVar(name = "scrut0") +//│ | | | | | | | | | | visitTerm ==> Var("scrut0") +//│ | | | | | | | | | | visitPattern <== true +//│ | | | | | | | | | | visitPattern ==> [] +//│ | | | | | | | | | | visitSplit <== [unreachable_1, x, _, scrut0] +//│ | | | | | | | | | | | visitTerm <== "tmux" +//│ | | | | | | | | | | | visitTerm ==> "tmux" +//│ | | | | | | | | | | visitSplit <== [unreachable_1, x, _, scrut0] +//│ | | | | | | | | | | | visitTerm <== "screen" +//│ | | | | | | | | | | | visitTerm ==> "screen" +//│ | | | | | | | | visitSplit <== [unreachable_1, x] +//│ | | | | | | | | | found branch: x is Some(xv) +//│ | | | | | | | | | visitTerm <== Var("x") +//│ | | | | | | | | | | visitVar(name = "x") +//│ | | | | | | | | | visitTerm ==> Var("x") +//│ | | | | | | | | | visitPattern <== Some(xv) +//│ | | | | | | | | | visitPattern ==> [xv] +//│ | | | | | | | | | visitSplit <== [unreachable_1, x, xv] +//│ | | | | | | | | | | visitTerm <== "sin" +//│ | | | | | | | | | | visitTerm ==> "sin" +//│ | | | | | | | | | visitSplit <== [unreachable_1, x] +//│ | | | | | | | | | | found branch: x is None +//│ | | | | | | | | | | visitTerm <== Var("x") +//│ | | | | | | | | | | | visitVar(name = "x") +//│ | | | | | | | | | | visitTerm ==> Var("x") +//│ | | | | | | | | | | visitPattern <== None +//│ | | | | | | | | | | visitPattern ==> [] +//│ | | | | | | | | | | visitSplit <== [unreachable_1, x] +//│ | | | | | | | | | | | visitTerm <== "tan" +//│ | | | | | | | | | | | visitTerm ==> "tan" +//│ | | | | | | | | | | visitSplit <== [unreachable_1, x] +//│ | | | | | | | | | | | the end +//│ | | | | | | | STEP 2 +//│ | | | | | | | normalizeToTerm +//│ | | | | | | | | alias x => _ +//│ | | | | | | | | normalizeToTerm +//│ | | | | | | | | | normalizeToTerm +//│ | | | | | | | | | | match scrut0 with true +//│ | | | | | | | | | | S+ <== scrut0 is true +//│ | | | | | | | | | | | the end +//│ | | | | | | | | | | S+ ==> then "tmux" +//│ | | | | | | | | | | normalizeToTerm +//│ | | | | | | | | | | S- <== scrut0 is true +//│ | | | | | | | | | | | the end +//│ | | | | | | | | | | S- ==> then "screen" +//│ | | | | | | | | | | normalizeToCaseBranches +//│ | | | | | | | Normalized UCS term: +//│ | | | | | | | let _* = x +//│ | | | | | | | let scrut0* = f(x,) +//│ | | | | | | | scrut0 match +//│ | | | | | | | case true => "tmux" +//│ | | | | | | | case _ => "screen" +//│ | | | | | | | STEP 3 +//│ | | | | | | | postProcess <== Let(_, _) +//│ | | | | | | | | postProcess <== Let(_, _) +//│ | | | | | | | | | postProcess <== CaseOf(_, _) +//│ | | | | | | | | | | found a BINARY case: scrut0 is true +//│ | | | | | | | | | | `scrut0`'s matched classes: [true] +//│ | | | | | | | | | | post-processing the first branch +//│ | | | | | | | | | | postProcess <== "tmux" +//│ | | | | | | | | | | | CANNOT post-process +//│ | | | | | | | | | | postProcess ==> +//│ | | | | | | | | | | found 0 cases +//│ | | | | | | | | | postProcess ==> +//│ | | | | | | | | postProcess ==> +//│ | | | | | | | postProcess ==> +//│ | | | | | | | Post-processed UCS term: +//│ | | | | | | | let _* = x +//│ | | | | | | | let scrut0* = f(x,) +//│ | | | | | | | scrut0 match +//│ | | | | | | | case true => "tmux" +//│ | | | | | | | case _ => "screen" +//│ | | | | | | | STEP 4 +//│ | | | | | | | collected match registry: +//│ | | | | | | | >>> scrut0 => [true] +//│ | | | | | | | checkCoverage <== Let(_, _), 0 pending, 1 working +//│ | | | | | | | | assumptions: empty +//│ | | | | | | | | checkCoverage <== Let(_, _), 0 pending, 1 working +//│ | | | | | | | | | assumptions: empty +//│ | | | | | | | | | checkCoverage <== CaseOf(_, _), 0 pending, 1 working +//│ | | | | | | | | | | assumptions: empty +//│ | | | | | | | | | | scrutinee: scrut0 +//│ | | | | | | | | | | checkCoverage <== "tmux", 0 pending, 0 working +//│ | | | | | | | | | | | assumptions: scrut0 is true +//│ | | | | | | | | | | | STOP +//│ | | | | | | | | | | checkCoverage ==> 0 +//│ | | | | | | | | | | wildcard case +//│ | | | | | | | | | | checkCoverage <== "screen", 0 pending, 1 working +//│ | | | | | | | | | | | assumptions: empty +//│ | | | | | | | | | | | STOP +//│ | | | | | | | | | | checkCoverage ==> 0 +//│ | | | | | | | | | checkCoverage ==> 0 +//│ | | | | | | | | checkCoverage ==> 0 +//│ | | | | | | | checkCoverage ==> 0 +//│ | | | | | | | Coverage checking result: 0 errors +//│ | | | | | | visitIf ==> () +//│ | | | | | visitTerm ==> If(_) +//│ | | | | visitTerm ==> Blk(_) +//│ | | | visitTerm ==> Lam(_, _) +//│ | | visitFunction ==> unreachable_1 +//│ | visitTypingUnit ==> unreachable_1 +//│ process ==> unreachable_1 +//│ fun unreachable_1: (Object & ~#Some | Some[Eql[1]]) -> ("screen" | "tmux") diff --git a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls new file mode 100644 index 00000000..815daa3c --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls @@ -0,0 +1,29 @@ +:PreTyper + +class Some[T](value: T) +module None +type Option[T] = Some[T] | None +class Pair[A, B](x: A, y: B) +//│ class Some[T](value: T) +//│ module None +//│ type Option[T] = None | Some[T] +//│ class Pair[A, B](x: A, y: B) + +:dpt +// FIXME +fun f(x) = if x is Some(1) then true else false +//│ process <== : {fun f} +//│ | visitTypingUnit <== : {fun f} +//│ | | 1. scope = {f} +//│ | | 2. scope = {f} +//│ | | visitFunction <== f +//│ | | | visitTerm <== Lam(_, _) +//│ | | | | visitTerm <== If(_, _) +//│ | | | | | visitIf +//│ | | | | | | STEP 0 +//│ | | | | | | Transformed UCS term: +//│ | | | | | | if +//│ | | | | | | x is Some(1) then true +//│ | | | | | | else false +//│ | | | | | | STEP 1 +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing From b4e5de3be38a2173c1cee6b358ad6b0e4070c012 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Thu, 30 Nov 2023 10:49:25 +0800 Subject: [PATCH 007/143] Support literal patterns --- .../mlscript/ucs/stages/Desugaring.scala | 27 ++++++++++--------- .../diff/pretyper/ucs/patterns/Literals.mls | 18 +------------ 2 files changed, 16 insertions(+), 29 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index dffa403c..d2c58f5d 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -1,7 +1,8 @@ package mlscript.ucs.stages -import mlscript.{Term, Var} +import mlscript.{App, Fld, Term, Var} import mlscript.ucs.{syntax => s, core => c, PartialTerm} +import mlscript.ucs.helpers.mkBinOp import mlscript.utils._, shorthands._ import mlscript.pretyper.{ScrutineeSymbol, SubValueSymbol, ValueSymbol} import mlscript.ucs.DesugaringException @@ -25,7 +26,7 @@ trait Desugaring { self: mlscript.pretyper.Traceable => private val truePattern = c.Pattern.Class(Var("true"), N) - private def flattenClassParameters(parentScrutinee: Var, parentClassName: Var, parameters: Opt[Ls[Opt[s.Pattern]]]): Opt[Ls[Opt[Var]]] -> Ls[Opt[Var -> s.ClassPattern]] = + private def flattenClassParameters(parentScrutinee: Var, parentClassName: Var, parameters: Opt[Ls[Opt[s.Pattern]]]): Opt[Ls[Opt[Var]]] -> Ls[Opt[Var -> s.Pattern]] = parameters match { case S(parameters) => val (a, b) = parameters.zipWithIndex.unzip { @@ -34,21 +35,15 @@ trait Desugaring { self: mlscript.pretyper.Traceable => case (S(parameterPattern: s.ClassPattern), index) => val scrutinee = freshScrutinee(parentScrutinee, parentClassName, index) (S(scrutinee), Some((scrutinee, parameterPattern))) + case (S(parameterPattern: s.LiteralPattern), index) => + val scrutinee = freshScrutinee(parentScrutinee, parentClassName, index) + (S(scrutinee), Some((scrutinee, parameterPattern))) case _ => ??? // Other patterns are not implemented yet. } (S(a), b) case N => (N, Nil) } - private def flattenNestedSplitLet(pattern: s.ClassPattern, term: Var, tail: c.Split): c.Split = { - val (parameterBindings, childrenBindings) = flattenClassParameters(term, pattern.nme, pattern.parameters) - c.Branch(term, c.Pattern.Class(pattern.nme, parameterBindings), childrenBindings.foldRight(tail){ - case (N, tail) => tail - case (S((nme, parameterPattern)), tail) => - flattenNestedSplitLet(parameterPattern, nme, tail) - }) :: c.Split.Nil - } - private def desugarTermSplit(split: s.TermSplit)(implicit termPart: PartialTerm): c.Split = split match { case s.Split.Cons(head, tail) => desugarTermBranch(head) ++ desugarTermSplit(tail) @@ -92,8 +87,16 @@ trait Desugaring { self: mlscript.pretyper.Traceable => val (parameterBindings, subPatterns) = flattenClassParameters(scrutinee, pattern.nme, pattern.parameters) c.Branch(scrutinee, c.Pattern.Class(pattern.nme, parameterBindings), subPatterns.foldRight(next) { case (None, next) => next - case (Some((nme, pattern)), next) => + case (Some((nme, pattern: s.ClassPattern)), next) => flattenNestedPattern(pattern, nme, next) :: c.Split.Nil + case (Some((nme, pattern: s.LiteralPattern)), next) => + val scrutinee = freshScrutinee() + c.Split.Let( + rec = false, + scrutinee, + mkBinOp(nme, Var("=="), pattern.literal, true), + c.Branch(scrutinee, truePattern, next) :: c.Split.Nil + ) }) } diff --git a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls index 815daa3c..4a2cd857 100644 --- a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls +++ b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls @@ -9,21 +9,5 @@ class Pair[A, B](x: A, y: B) //│ type Option[T] = None | Some[T] //│ class Pair[A, B](x: A, y: B) -:dpt -// FIXME fun f(x) = if x is Some(1) then true else false -//│ process <== : {fun f} -//│ | visitTypingUnit <== : {fun f} -//│ | | 1. scope = {f} -//│ | | 2. scope = {f} -//│ | | visitFunction <== f -//│ | | | visitTerm <== Lam(_, _) -//│ | | | | visitTerm <== If(_, _) -//│ | | | | | visitIf -//│ | | | | | | STEP 0 -//│ | | | | | | Transformed UCS term: -//│ | | | | | | if -//│ | | | | | | x is Some(1) then true -//│ | | | | | | else false -//│ | | | | | | STEP 1 -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ fun f: (Object & ~#Some | Some[Num]) -> Bool From 2e98bc25fee5c6d925887020ec5b587fe2604076 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Thu, 30 Nov 2023 23:45:29 +0800 Subject: [PATCH 008/143] Address minor issues during mentioned in the meeting - Rename functions from `visitX` to `traverseX`. - Add Boolean condition test cases. - Use dollar symbol in generated variables. - Add `TupleCase` which currently is commented. --- .../scala/mlscript/pretyper/PreTyper.scala | 88 ++++++------ shared/src/main/scala/mlscript/syntax.scala | 1 + .../main/scala/mlscript/ucs/DesugarUCS.scala | 32 ++--- .../mlscript/ucs/stages/Desugaring.scala | 2 +- .../src/test/diff/pretyper/Declarations.mls | 98 ++++++------- .../pretyper/ucs/coverage/Unreachable.mls | 134 +++++++++--------- .../diff/pretyper/ucs/patterns/Literals.mls | 17 +++ 7 files changed, 195 insertions(+), 177 deletions(-) diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index 07ccacb2..a3f27e6c 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -29,7 +29,7 @@ class PreTyper(override val debugLevel: Opt[Int], useNewDefs: Bool) extends Trac case other => println("Unknown parameters: " + inspect(other)); ??? // TODO: bad } - // `visitIf` is meaningless because it represents patterns with terms. + // `traverseIf` is meaningless because it represents patterns with terms. protected def resolveVar(v: Var)(implicit scope: Scope): Unit = trace(s"resolveVar(name = \"$v\")") { @@ -54,8 +54,8 @@ class PreTyper(override val debugLevel: Opt[Int], useNewDefs: Bool) extends Trac } }() - protected def visitVar(v: Var)(implicit scope: Scope): Unit = - trace(s"visitVar(name = \"$v\")") { + protected def traverseVar(v: Var)(implicit scope: Scope): Unit = + trace(s"traverseVar(name = \"$v\")") { v.symbolOption match { case N => resolveVar(v) case S(symbol) => scope.get(v.name) match { @@ -66,70 +66,70 @@ class PreTyper(override val debugLevel: Opt[Int], useNewDefs: Bool) extends Trac } }() - protected def visitTerm(term: Term)(implicit scope: Scope): Unit = - trace(s"visitTerm <== ${shortName(term)}") { + protected def traverseTerm(term: Term)(implicit scope: Scope): Unit = + trace(s"traverseTerm <== ${shortName(term)}") { term match { - case Assign(lhs, rhs) => visitTerm(lhs); visitTerm(rhs) - case Bra(_, trm) => visitTerm(trm) + case Assign(lhs, rhs) => traverseTerm(lhs); traverseTerm(rhs) + case Bra(_, trm) => traverseTerm(trm) case Lam(lhs, rhs) => - visitTerm(rhs)(scope ++ extractParameters(lhs)) - case Sel(receiver, fieldName) => visitTerm(receiver) + traverseTerm(rhs)(scope ++ extractParameters(lhs)) + case Sel(receiver, fieldName) => traverseTerm(receiver) case Let(isRec, nme, rhs, body) => - visitTerm(rhs) - visitTerm(body)(scope + new ValueSymbol(nme, false)) + traverseTerm(rhs) + traverseTerm(body)(scope + new ValueSymbol(nme, false)) case New(head, body) => - case Tup(fields) => fields.foreach { case (_, Fld(_, t)) => visitTerm(t) } - case Asc(trm, ty) => visitTerm(trm) - case ef @ If(_, _) => visitIf(ef)(scope) + case Tup(fields) => fields.foreach { case (_, Fld(_, t)) => traverseTerm(t) } + case Asc(trm, ty) => traverseTerm(trm) + case ef @ If(_, _) => traverseIf(ef)(scope) case TyApp(lhs, targs) => // TODO: When? case Eqn(lhs, rhs) => ??? // TODO: How? case Blk(stmts) => stmts.foreach { - case t: Term => visitTerm(t) + case t: Term => traverseTerm(t) case _ => ??? // TODO: When? } - case Subs(arr, idx) => visitTerm(arr); visitTerm(idx) - case Bind(lhs, rhs) => visitTerm(lhs); visitTerm(rhs) + case Subs(arr, idx) => traverseTerm(arr); traverseTerm(idx) + case Bind(lhs, rhs) => traverseTerm(lhs); traverseTerm(rhs) case Splc(fields) => fields.foreach { - case L(t) => visitTerm(t) - case R(Fld(_, t)) => visitTerm(t) + case L(t) => traverseTerm(t) + case R(Fld(_, t)) => traverseTerm(t) } case Forall(params, body) => ??? // TODO: When? - case Rcd(fields) => fields.foreach { case (_, Fld(_, t)) => visitTerm(t) } + case Rcd(fields) => fields.foreach { case (_, Fld(_, t)) => traverseTerm(t) } case CaseOf(trm, cases) => - case With(trm, fields) => visitTerm(trm); visitTerm(fields) + case With(trm, fields) => traverseTerm(trm); traverseTerm(fields) case Where(body, where) => ??? // TODO: When? - case App(lhs, rhs) => visitTerm(lhs); visitTerm(rhs) - case Test(trm, ty) => visitTerm(trm) + case App(lhs, rhs) => traverseTerm(lhs); traverseTerm(rhs) + case Test(trm, ty) => traverseTerm(trm) case _: Lit | _: Super => () - case v: Var => visitVar(v) + case v: Var => traverseVar(v) case AdtMatchWith(cond, arms) => ??? // TODO: How? - case Inst(body) => visitTerm(body) + case Inst(body) => traverseTerm(body) } - }(_ => s"visitTerm ==> ${shortName(term)}") + }(_ => s"traverseTerm ==> ${shortName(term)}") - private def visitNuTypeDef(symbol: TypeSymbol, defn: NuTypeDef)(implicit scope: Scope): Unit = - trace(s"visitNuTypeDef <== ${defn.kind} ${defn.nme.name}") { - visitTypingUnit(defn.body, defn.nme.name, scope) + private def traverseNuTypeDef(symbol: TypeSymbol, defn: NuTypeDef)(implicit scope: Scope): Unit = + trace(s"traverseNuTypeDef <== ${defn.kind} ${defn.nme.name}") { + traverseTypingUnit(defn.body, defn.nme.name, scope) () - }(_ => s"visitNuTypeDef <== ${defn.kind} ${defn.nme.name}") + }(_ => s"traverseNuTypeDef <== ${defn.kind} ${defn.nme.name}") - private def visitFunction(symbol: FunctionSymbol, defn: NuFunDef)(implicit scope: Scope): Unit = - trace(s"visitFunction <== ${defn.nme.name}") { + private def traverseFunction(symbol: FunctionSymbol, defn: NuFunDef)(implicit scope: Scope): Unit = + trace(s"traverseFunction <== ${defn.nme.name}") { defn.rhs match { case Left(term) => val subScope = if (defn.isLetRec === S(false)) scope else scope + symbol - visitTerm(term)(subScope) + traverseTerm(term)(subScope) case Right(value) => () } - }(_ => s"visitFunction ==> ${defn.nme.name}") + }(_ => s"traverseFunction ==> ${defn.nme.name}") - private def visitLetBinding(symbol: ValueSymbol, rec: Bool, rhs: Term)(implicit scope: Scope): Unit = - trace(s"visitLetBinding(rec = $rec, ${symbol.name})") { + private def traverseLetBinding(symbol: ValueSymbol, rec: Bool, rhs: Term)(implicit scope: Scope): Unit = + trace(s"traverseLetBinding(rec = $rec, ${symbol.name})") { }() - private def visitTypingUnit(typingUnit: TypingUnit, name: Str, parentScope: Scope): (Scope, TypeContents) = - trace(s"visitTypingUnit <== $name: ${typingUnit.describe}") { + private def traverseTypingUnit(typingUnit: TypingUnit, name: Str, parentScope: Scope): (Scope, TypeContents) = + trace(s"traverseTypingUnit <== $name: ${typingUnit.describe}") { import mlscript.{Cls, Trt, Mxn, Als, Mod} // Pass 1: Build a scope with hoisted symbols. val hoistedScope = typingUnit.entities.foldLeft(parentScope.derive) { @@ -150,12 +150,12 @@ class PreTyper(override val debugLevel: Opt[Int], useNewDefs: Bool) extends Trac println(hoistedScope.symbols.map(_.name).mkString("1. scope = {", ", ", "}")) // Pass 2: Visit non-hoisted and build a complete scope. val completeScope = typingUnit.entities.foldLeft[Scope](hoistedScope) { - case (acc, term: Term) => visitTerm(term)(acc); acc + case (acc, term: Term) => traverseTerm(term)(acc); acc case (acc, defn: NuTypeDef) => acc case (acc, defn @ NuFunDef(Some(rec), nme, _, _, L(rhs))) => val symbol = new ValueSymbol(defn.nme, true) val scopeWithVar = acc + symbol - visitLetBinding(symbol, rec, rhs)(if (rec) { scopeWithVar } else { acc }) + traverseLetBinding(symbol, rec, rhs)(if (rec) { scopeWithVar } else { acc }) scopeWithVar case (acc, _: NuFunDef) => acc case (acc, _: Constructor | _: DataDefn | _: DatatypeDefn | _: Def | _: LetS | _: TypeDef) => ??? // TODO: When? @@ -173,16 +173,16 @@ class PreTyper(override val debugLevel: Opt[Int], useNewDefs: Bool) extends Trac }) case Als | Mod | Mxn | Trt => completeScope } - visitNuTypeDef(symbol, symbol.defn)(innerScope) - case symbol: FunctionSymbol => visitFunction(symbol, symbol.defn)(completeScope) + traverseNuTypeDef(symbol, symbol.defn)(innerScope) + case symbol: FunctionSymbol => traverseFunction(symbol, symbol.defn)(completeScope) case _: ValueSymbol => () case _: SubValueSymbol => () } (completeScope, new TypeContents) - }({ case (scope, contents) => s"visitTypingUnit ==> ${scope.showLocalSymbols}" }) + }({ case (scope, contents) => s"traverseTypingUnit ==> ${scope.showLocalSymbols}" }) def process(typingUnit: TypingUnit, scope: Scope, name: Str): (Scope, TypeContents) = trace(s"process <== $name: ${typingUnit.describe}") { - visitTypingUnit(typingUnit, name, scope) + traverseTypingUnit(typingUnit, name, scope) }({ case (scope, contents) => s"process ==> ${scope.showLocalSymbols}" }) } diff --git a/shared/src/main/scala/mlscript/syntax.scala b/shared/src/main/scala/mlscript/syntax.scala index 368faca8..6ac60216 100644 --- a/shared/src/main/scala/mlscript/syntax.scala +++ b/shared/src/main/scala/mlscript/syntax.scala @@ -110,6 +110,7 @@ object FldFlags { val empty: FldFlags = FldFlags(false, false, false) } sealed abstract class CaseBranches extends CaseBranchesImpl final case class Case(pat: SimpleTerm, body: Term, rest: CaseBranches) extends CaseBranches final case class Wildcard(body: Term) extends CaseBranches +// final case class TupleCase(numElems: Int, canHaveMore: Bool, body: Term, rest: CaseBranches) extends CaseBranches final case object NoCases extends CaseBranches final case class IntLit(value: BigInt) extends Lit diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index c921cd04..5bee6056 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -15,8 +15,8 @@ trait DesugarUCS extends Transformation with CoverageChecking { self: PreTyper => - protected def visitIf(`if`: If)(implicit scope: Scope): Unit = - trace("visitIf") { + protected def traverseIf(`if`: If)(implicit scope: Scope): Unit = + trace("traverseIf") { // Stage 0: Transformation println("STEP 0") val transformed = transform(`if`, true) @@ -25,14 +25,14 @@ trait DesugarUCS extends Transformation println(ucs.syntax.printTermSplit(transformed)) // Stage 1: Desugaring // This stage will generate new names based on the position of the scrutinee. - // Therefore, we need to call `visitSplit` to associate these newly generated + // Therefore, we need to call `traverseSplit` to associate these newly generated // names with symbols. println("STEP 1") val desugared = desugar(transformed) println(desugared.toString, 2) println("Desugared UCS term:") println(ucs.core.printSplit(desugared)) - visitSplit(desugared) + traverseSplit(desugared) // Stage 2: Normalization println("STEP 2") val normalized = normalize(desugared) @@ -51,28 +51,28 @@ trait DesugarUCS extends Transformation raise(diagnostics) // Epilogue `if`.desugaredTerm = S(normalized) - }(_ => "visitIf ==> ()") + }(_ => "traverseIf ==> ()") - private def visitSplit(split: core.Split)(implicit scope: Scope): Unit = - trace(s"visitSplit <== [${scope.showLocalSymbols}]") { + private def traverseSplit(split: core.Split)(implicit scope: Scope): Unit = + trace(s"traverseSplit <== [${scope.showLocalSymbols}]") { import core._ split match { case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => println(s"found branch: $scrutinee is $pattern") - visitTerm(scrutinee) - val patternSymbols = visitPattern(scrutinee, pattern) - visitSplit(continuation)(scope.withEntries(patternSymbols)) - visitSplit(tail) + traverseTerm(scrutinee) + val patternSymbols = traversePattern(scrutinee, pattern) + traverseSplit(continuation)(scope.withEntries(patternSymbols)) + traverseSplit(tail) case Split.Let(_, name, _, tail) => println(s"found let binding: \"$name\"") - visitSplit(tail)(scope + new ValueSymbol(name, false)) - case Split.Else(default) => visitTerm(default) + traverseSplit(tail)(scope + new ValueSymbol(name, false)) + case Split.Else(default) => traverseTerm(default) case Split.Nil => println("the end") } }() - private def visitPattern(scrutinee: Var, pattern: core.Pattern): List[Var -> Symbol] = - trace(s"visitPattern <== $pattern") { + private def traversePattern(scrutinee: Var, pattern: core.Pattern): List[Var -> Symbol] = + trace(s"traversePattern <== $pattern") { lazy val scrutineeSymbol = scrutinee.symbol match { case symbol: ScrutineeSymbol => symbol case other: Symbol => @@ -109,5 +109,5 @@ trait DesugarUCS extends Transformation bindingName.symbol = symbol; bindingName -> symbol }.toList } - }(_.iterator.map(_._1.name).mkString("visitPattern ==> [", ", ", "]")) + }(_.iterator.map(_._1.name).mkString("traversePattern ==> [", ", ", "]")) } diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index d2c58f5d..6c8301fc 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -16,7 +16,7 @@ trait Desugaring { self: mlscript.pretyper.Traceable => private def freshName(): Str = { val thisIndex = nextScrutineeIndex nextScrutineeIndex += 1 - s"scrut$thisIndex" // FIXME: use `freeVars` to avoid name collision. + s"scrut$$$thisIndex" // FIXME: use `freeVars` to avoid name collision. } private def freshScrutinee(): Var = Var(freshName()) diff --git a/shared/src/test/diff/pretyper/Declarations.mls b/shared/src/test/diff/pretyper/Declarations.mls index 1401303e..3ba12d95 100644 --- a/shared/src/test/diff/pretyper/Declarations.mls +++ b/shared/src/test/diff/pretyper/Declarations.mls @@ -5,30 +5,30 @@ :dpt fun test(x, y) = x + y //│ process <== : {fun test} -//│ | visitTypingUnit <== : {fun test} +//│ | traverseTypingUnit <== : {fun test} //│ | | 1. scope = {test} //│ | | 2. scope = {test} -//│ | | visitFunction <== test -//│ | | | visitTerm <== Lam(_, _) -//│ | | | | visitTerm <== App(_, _) -//│ | | | | | visitTerm <== Var("+") -//│ | | | | | | visitVar(name = "+") +//│ | | traverseFunction <== test +//│ | | | traverseTerm <== Lam(_, _) +//│ | | | | traverseTerm <== App(_, _) +//│ | | | | | traverseTerm <== Var("+") +//│ | | | | | | traverseVar(name = "+") //│ | | | | | | | resolveVar(name = "+") -//│ | | | | | visitTerm ==> Var("+") -//│ | | | | | visitTerm <== Tup(_, _) -//│ | | | | | | visitTerm <== Var("x") -//│ | | | | | | | visitVar(name = "x") +//│ | | | | | traverseTerm ==> Var("+") +//│ | | | | | traverseTerm <== Tup(_, _) +//│ | | | | | | traverseTerm <== Var("x") +//│ | | | | | | | traverseVar(name = "x") //│ | | | | | | | | resolveVar(name = "x") -//│ | | | | | | visitTerm ==> Var("x") -//│ | | | | | | visitTerm <== Var("y") -//│ | | | | | | | visitVar(name = "y") +//│ | | | | | | traverseTerm ==> Var("x") +//│ | | | | | | traverseTerm <== Var("y") +//│ | | | | | | | traverseVar(name = "y") //│ | | | | | | | | resolveVar(name = "y") -//│ | | | | | | visitTerm ==> Var("y") -//│ | | | | | visitTerm ==> Tup(_, _) -//│ | | | | visitTerm ==> App(_, _) -//│ | | | visitTerm ==> Lam(_, _) -//│ | | visitFunction ==> test -//│ | visitTypingUnit ==> test +//│ | | | | | | traverseTerm ==> Var("y") +//│ | | | | | traverseTerm ==> Tup(_, _) +//│ | | | | traverseTerm ==> App(_, _) +//│ | | | traverseTerm ==> Lam(_, _) +//│ | | traverseFunction ==> test +//│ | traverseTypingUnit ==> test //│ process ==> test //│ fun test: (Int, Int) -> Int @@ -37,19 +37,19 @@ fun test(x, y) = x + y let y = id(42) fun id(x) = x //│ process <== : {let y; fun id} -//│ | visitTypingUnit <== : {let y; fun id} +//│ | traverseTypingUnit <== : {let y; fun id} //│ | | 1. scope = {id} -//│ | | visitLetBinding(rec = false, y) +//│ | | traverseLetBinding(rec = false, y) //│ | | 2. scope = {id} -//│ | | visitFunction <== id -//│ | | | visitTerm <== Lam(_, _) -//│ | | | | visitTerm <== Var("x") -//│ | | | | | visitVar(name = "x") +//│ | | traverseFunction <== id +//│ | | | traverseTerm <== Lam(_, _) +//│ | | | | traverseTerm <== Var("x") +//│ | | | | | traverseVar(name = "x") //│ | | | | | | resolveVar(name = "x") -//│ | | | | visitTerm ==> Var("x") -//│ | | | visitTerm ==> Lam(_, _) -//│ | | visitFunction ==> id -//│ | visitTypingUnit ==> id, y +//│ | | | | traverseTerm ==> Var("x") +//│ | | | traverseTerm ==> Lam(_, _) +//│ | | traverseFunction ==> id +//│ | traverseTypingUnit ==> id, y //│ process ==> id, y //│ let y: 42 | 'a //│ fun id: forall 'b. ('a & 'b) -> (42 | 'b) @@ -59,31 +59,31 @@ fun id(x) = x fun q(x) = x + p let p = 0 //│ process <== : {fun q; let p} -//│ | visitTypingUnit <== : {fun q; let p} +//│ | traverseTypingUnit <== : {fun q; let p} //│ | | 1. scope = {q} -//│ | | visitLetBinding(rec = false, p) +//│ | | traverseLetBinding(rec = false, p) //│ | | 2. scope = {q} -//│ | | visitFunction <== q -//│ | | | visitTerm <== Lam(_, _) -//│ | | | | visitTerm <== App(_, _) -//│ | | | | | visitTerm <== Var("+") -//│ | | | | | | visitVar(name = "+") +//│ | | traverseFunction <== q +//│ | | | traverseTerm <== Lam(_, _) +//│ | | | | traverseTerm <== App(_, _) +//│ | | | | | traverseTerm <== Var("+") +//│ | | | | | | traverseVar(name = "+") //│ | | | | | | | resolveVar(name = "+") -//│ | | | | | visitTerm ==> Var("+") -//│ | | | | | visitTerm <== Tup(_, _) -//│ | | | | | | visitTerm <== Var("x") -//│ | | | | | | | visitVar(name = "x") +//│ | | | | | traverseTerm ==> Var("+") +//│ | | | | | traverseTerm <== Tup(_, _) +//│ | | | | | | traverseTerm <== Var("x") +//│ | | | | | | | traverseVar(name = "x") //│ | | | | | | | | resolveVar(name = "x") -//│ | | | | | | visitTerm ==> Var("x") -//│ | | | | | | visitTerm <== Var("p") -//│ | | | | | | | visitVar(name = "p") +//│ | | | | | | traverseTerm ==> Var("x") +//│ | | | | | | traverseTerm <== Var("p") +//│ | | | | | | | traverseVar(name = "p") //│ | | | | | | | | resolveVar(name = "p") -//│ | | | | | | visitTerm ==> Var("p") -//│ | | | | | visitTerm ==> Tup(_, _) -//│ | | | | visitTerm ==> App(_, _) -//│ | | | visitTerm ==> Lam(_, _) -//│ | | visitFunction ==> q -//│ | visitTypingUnit ==> q, p +//│ | | | | | | traverseTerm ==> Var("p") +//│ | | | | | traverseTerm ==> Tup(_, _) +//│ | | | | traverseTerm ==> App(_, _) +//│ | | | traverseTerm ==> Lam(_, _) +//│ | | traverseFunction ==> q +//│ | traverseTypingUnit ==> q, p //│ process ==> q, p //│ fun q: Int -> Int //│ let p: 0 diff --git a/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls b/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls index 1062a28a..d8723813 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls @@ -50,14 +50,14 @@ fun unreachable_1(x) = Some(xv) then "sin" None then "tan" //│ process <== : {fun unreachable_1} -//│ | visitTypingUnit <== : {fun unreachable_1} +//│ | traverseTypingUnit <== : {fun unreachable_1} //│ | | 1. scope = {unreachable_1} //│ | | 2. scope = {unreachable_1} -//│ | | visitFunction <== unreachable_1 -//│ | | | visitTerm <== Lam(_, _) -//│ | | | | visitTerm <== Blk(_) -//│ | | | | | visitTerm <== If(_) -//│ | | | | | | visitIf +//│ | | traverseFunction <== unreachable_1 +//│ | | | traverseTerm <== Lam(_, _) +//│ | | | | traverseTerm <== Blk(_) +//│ | | | | | traverseTerm <== If(_) +//│ | | | | | | traverseIf //│ | | | | | | | STEP 0 //│ | | | | | | | Transformed UCS term: //│ | | | | | | | if x is @@ -69,83 +69,83 @@ fun unreachable_1(x) = //│ | | | | | | | Desugared UCS term: //│ | | | | | | | if //│ | | | | | | | x is _ -//│ | | | | | | | let scrut0 = f(x,) -//│ | | | | | | | scrut0 is true then "tmux" +//│ | | | | | | | let scrut$0 = f(x,) +//│ | | | | | | | scrut$0 is true then "tmux" //│ | | | | | | | else "screen" //│ | | | | | | | x is Some(xv) then "sin" //│ | | | | | | | x is None then "tan" -//│ | | | | | | | visitSplit <== [unreachable_1, x] +//│ | | | | | | | traverseSplit <== [unreachable_1, x] //│ | | | | | | | | found branch: x is _ -//│ | | | | | | | | visitTerm <== Var("x") -//│ | | | | | | | | | visitVar(name = "x") +//│ | | | | | | | | traverseTerm <== Var("x") +//│ | | | | | | | | | traverseVar(name = "x") //│ | | | | | | | | | | resolveVar(name = "x") -//│ | | | | | | | | visitTerm ==> Var("x") -//│ | | | | | | | | visitPattern <== _ -//│ | | | | | | | | visitPattern ==> [_] -//│ | | | | | | | | visitSplit <== [unreachable_1, x, _] -//│ | | | | | | | | | found let binding: "scrut0" -//│ | | | | | | | | | visitSplit <== [unreachable_1, x, _, scrut0] -//│ | | | | | | | | | | found branch: scrut0 is true -//│ | | | | | | | | | | visitTerm <== Var("scrut0") -//│ | | | | | | | | | | | visitVar(name = "scrut0") -//│ | | | | | | | | | | | | resolveVar(name = "scrut0") -//│ | | | | | | | | | | visitTerm ==> Var("scrut0") -//│ | | | | | | | | | | visitPattern <== true -//│ | | | | | | | | | | visitPattern ==> [] -//│ | | | | | | | | | | visitSplit <== [unreachable_1, x, _, scrut0] -//│ | | | | | | | | | | | visitTerm <== "tmux" -//│ | | | | | | | | | | | visitTerm ==> "tmux" -//│ | | | | | | | | | | visitSplit <== [unreachable_1, x, _, scrut0] -//│ | | | | | | | | | | | visitTerm <== "screen" -//│ | | | | | | | | | | | visitTerm ==> "screen" -//│ | | | | | | | | visitSplit <== [unreachable_1, x] +//│ | | | | | | | | traverseTerm ==> Var("x") +//│ | | | | | | | | traversePattern <== _ +//│ | | | | | | | | traversePattern ==> [_] +//│ | | | | | | | | traverseSplit <== [unreachable_1, x, _] +//│ | | | | | | | | | found let binding: "scrut$0" +//│ | | | | | | | | | traverseSplit <== [unreachable_1, x, _, scrut$0] +//│ | | | | | | | | | | found branch: scrut$0 is true +//│ | | | | | | | | | | traverseTerm <== Var("scrut$0") +//│ | | | | | | | | | | | traverseVar(name = "scrut$0") +//│ | | | | | | | | | | | | resolveVar(name = "scrut$0") +//│ | | | | | | | | | | traverseTerm ==> Var("scrut$0") +//│ | | | | | | | | | | traversePattern <== true +//│ | | | | | | | | | | traversePattern ==> [] +//│ | | | | | | | | | | traverseSplit <== [unreachable_1, x, _, scrut$0] +//│ | | | | | | | | | | | traverseTerm <== "tmux" +//│ | | | | | | | | | | | traverseTerm ==> "tmux" +//│ | | | | | | | | | | traverseSplit <== [unreachable_1, x, _, scrut$0] +//│ | | | | | | | | | | | traverseTerm <== "screen" +//│ | | | | | | | | | | | traverseTerm ==> "screen" +//│ | | | | | | | | traverseSplit <== [unreachable_1, x] //│ | | | | | | | | | found branch: x is Some(xv) -//│ | | | | | | | | | visitTerm <== Var("x") -//│ | | | | | | | | | | visitVar(name = "x") -//│ | | | | | | | | | visitTerm ==> Var("x") -//│ | | | | | | | | | visitPattern <== Some(xv) -//│ | | | | | | | | | visitPattern ==> [xv] -//│ | | | | | | | | | visitSplit <== [unreachable_1, x, xv] -//│ | | | | | | | | | | visitTerm <== "sin" -//│ | | | | | | | | | | visitTerm ==> "sin" -//│ | | | | | | | | | visitSplit <== [unreachable_1, x] +//│ | | | | | | | | | traverseTerm <== Var("x") +//│ | | | | | | | | | | traverseVar(name = "x") +//│ | | | | | | | | | traverseTerm ==> Var("x") +//│ | | | | | | | | | traversePattern <== Some(xv) +//│ | | | | | | | | | traversePattern ==> [xv] +//│ | | | | | | | | | traverseSplit <== [unreachable_1, x, xv] +//│ | | | | | | | | | | traverseTerm <== "sin" +//│ | | | | | | | | | | traverseTerm ==> "sin" +//│ | | | | | | | | | traverseSplit <== [unreachable_1, x] //│ | | | | | | | | | | found branch: x is None -//│ | | | | | | | | | | visitTerm <== Var("x") -//│ | | | | | | | | | | | visitVar(name = "x") -//│ | | | | | | | | | | visitTerm ==> Var("x") -//│ | | | | | | | | | | visitPattern <== None -//│ | | | | | | | | | | visitPattern ==> [] -//│ | | | | | | | | | | visitSplit <== [unreachable_1, x] -//│ | | | | | | | | | | | visitTerm <== "tan" -//│ | | | | | | | | | | | visitTerm ==> "tan" -//│ | | | | | | | | | | visitSplit <== [unreachable_1, x] +//│ | | | | | | | | | | traverseTerm <== Var("x") +//│ | | | | | | | | | | | traverseVar(name = "x") +//│ | | | | | | | | | | traverseTerm ==> Var("x") +//│ | | | | | | | | | | traversePattern <== None +//│ | | | | | | | | | | traversePattern ==> [] +//│ | | | | | | | | | | traverseSplit <== [unreachable_1, x] +//│ | | | | | | | | | | | traverseTerm <== "tan" +//│ | | | | | | | | | | | traverseTerm ==> "tan" +//│ | | | | | | | | | | traverseSplit <== [unreachable_1, x] //│ | | | | | | | | | | | the end //│ | | | | | | | STEP 2 //│ | | | | | | | normalizeToTerm //│ | | | | | | | | alias x => _ //│ | | | | | | | | normalizeToTerm //│ | | | | | | | | | normalizeToTerm -//│ | | | | | | | | | | match scrut0 with true -//│ | | | | | | | | | | S+ <== scrut0 is true +//│ | | | | | | | | | | match scrut$0 with true +//│ | | | | | | | | | | S+ <== scrut$0 is true //│ | | | | | | | | | | | the end //│ | | | | | | | | | | S+ ==> then "tmux" //│ | | | | | | | | | | normalizeToTerm -//│ | | | | | | | | | | S- <== scrut0 is true +//│ | | | | | | | | | | S- <== scrut$0 is true //│ | | | | | | | | | | | the end //│ | | | | | | | | | | S- ==> then "screen" //│ | | | | | | | | | | normalizeToCaseBranches //│ | | | | | | | Normalized UCS term: //│ | | | | | | | let _* = x -//│ | | | | | | | let scrut0* = f(x,) -//│ | | | | | | | scrut0 match +//│ | | | | | | | let scrut$0* = f(x,) +//│ | | | | | | | scrut$0 match //│ | | | | | | | case true => "tmux" //│ | | | | | | | case _ => "screen" //│ | | | | | | | STEP 3 //│ | | | | | | | postProcess <== Let(_, _) //│ | | | | | | | | postProcess <== Let(_, _) //│ | | | | | | | | | postProcess <== CaseOf(_, _) -//│ | | | | | | | | | | found a BINARY case: scrut0 is true -//│ | | | | | | | | | | `scrut0`'s matched classes: [true] +//│ | | | | | | | | | | found a BINARY case: scrut$0 is true +//│ | | | | | | | | | | `scrut$0`'s matched classes: [true] //│ | | | | | | | | | | post-processing the first branch //│ | | | | | | | | | | postProcess <== "tmux" //│ | | | | | | | | | | | CANNOT post-process @@ -156,22 +156,22 @@ fun unreachable_1(x) = //│ | | | | | | | postProcess ==> //│ | | | | | | | Post-processed UCS term: //│ | | | | | | | let _* = x -//│ | | | | | | | let scrut0* = f(x,) -//│ | | | | | | | scrut0 match +//│ | | | | | | | let scrut$0* = f(x,) +//│ | | | | | | | scrut$0 match //│ | | | | | | | case true => "tmux" //│ | | | | | | | case _ => "screen" //│ | | | | | | | STEP 4 //│ | | | | | | | collected match registry: -//│ | | | | | | | >>> scrut0 => [true] +//│ | | | | | | | >>> scrut$0 => [true] //│ | | | | | | | checkCoverage <== Let(_, _), 0 pending, 1 working //│ | | | | | | | | assumptions: empty //│ | | | | | | | | checkCoverage <== Let(_, _), 0 pending, 1 working //│ | | | | | | | | | assumptions: empty //│ | | | | | | | | | checkCoverage <== CaseOf(_, _), 0 pending, 1 working //│ | | | | | | | | | | assumptions: empty -//│ | | | | | | | | | | scrutinee: scrut0 +//│ | | | | | | | | | | scrutinee: scrut$0 //│ | | | | | | | | | | checkCoverage <== "tmux", 0 pending, 0 working -//│ | | | | | | | | | | | assumptions: scrut0 is true +//│ | | | | | | | | | | | assumptions: scrut$0 is true //│ | | | | | | | | | | | STOP //│ | | | | | | | | | | checkCoverage ==> 0 //│ | | | | | | | | | | wildcard case @@ -183,11 +183,11 @@ fun unreachable_1(x) = //│ | | | | | | | | checkCoverage ==> 0 //│ | | | | | | | checkCoverage ==> 0 //│ | | | | | | | Coverage checking result: 0 errors -//│ | | | | | | visitIf ==> () -//│ | | | | | visitTerm ==> If(_) -//│ | | | | visitTerm ==> Blk(_) -//│ | | | visitTerm ==> Lam(_, _) -//│ | | visitFunction ==> unreachable_1 -//│ | visitTypingUnit ==> unreachable_1 +//│ | | | | | | traverseIf ==> () +//│ | | | | | traverseTerm ==> If(_) +//│ | | | | traverseTerm ==> Blk(_) +//│ | | | traverseTerm ==> Lam(_, _) +//│ | | traverseFunction ==> unreachable_1 +//│ | traverseTypingUnit ==> unreachable_1 //│ process ==> unreachable_1 //│ fun unreachable_1: (Object & ~#Some | Some[Eql[1]]) -> ("screen" | "tmux") diff --git a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls index 4a2cd857..e36980c2 100644 --- a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls +++ b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls @@ -11,3 +11,20 @@ class Pair[A, B](x: A, y: B) fun f(x) = if x is Some(1) then true else false //│ fun f: (Object & ~#Some | Some[Num]) -> Bool + +fun g(x) = if x then 1 else 2 +//│ fun g: Object -> (1 | 2) + +fun g(x) = if x is true then 1 else 2 +//│ fun g: anything -> 1 +//│ Syntax error: +//│ Invalid destructuring assignment target + +fun g(x) = if x && true is true then 1 else 2 +//│ fun g: Bool -> 1 +//│ Syntax error: +//│ Invalid destructuring assignment target + +fun h(x) = if (x : Bool) then 1 else 2 +//│ fun h: Bool -> (1 | 2) + From 3d355f253a375665dd48ad01b074a403b5ea5c1c Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Fri, 1 Dec 2023 18:54:07 +0800 Subject: [PATCH 009/143] Support pattern like `x is true` and constrain tests to be Booleans --- .../mlscript/ucs/stages/Desugaring.scala | 35 +++++++++++-------- .../mlscript/ucs/stages/Transformation.scala | 19 +++++----- .../src/main/scala/mlscript/ucs/syntax.scala | 4 +++ .../pretyper/ucs/coverage/Unreachable.mls | 6 ++-- .../diff/pretyper/ucs/patterns/Literals.mls | 18 ++++++---- 5 files changed, 48 insertions(+), 34 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 6c8301fc..107535e6 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -1,6 +1,6 @@ package mlscript.ucs.stages -import mlscript.{App, Fld, Term, Var} +import mlscript.{App, Asc, Fld, Term, Var, TypeName} import mlscript.ucs.{syntax => s, core => c, PartialTerm} import mlscript.ucs.helpers.mkBinOp import mlscript.utils._, shorthands._ @@ -60,7 +60,7 @@ trait Desugaring { self: mlscript.pretyper.Traceable => c.Split.Let( rec = false, name = `var`, - term = condition, + term = Asc(condition, TypeName("Bool")), tail = c.Branch(`var`, truePattern, desugarTermSplit(continuation)) :: c.Split.Nil ) case s.TermBranch.Match(scrutinee, split) => @@ -100,21 +100,26 @@ trait Desugaring { self: mlscript.pretyper.Traceable => }) } - private def desugarPatternBranch(scrutinee: Var, branch: s.PatternBranch): c.Branch = { - lazy val continuation = desugarTermSplit(branch.continuation)(PartialTerm.Empty) - branch.pattern match { - case s.AliasPattern(nme, pattern) => ??? - case s.LiteralPattern(literal) => ??? - case s.NamePattern(nme) => c.Branch(scrutinee, c.Pattern.Name(nme), continuation) - case pattern @ s.ClassPattern(nme, fields) => flattenNestedPattern(pattern, scrutinee, continuation) - case s.TuplePattern(fields) => ??? - case s.RecordPattern(entries) => ??? - } - } - private def desugarPatternSplit(split: s.PatternSplit)(implicit scrutinee: Term): c.Split = { def rec(scrutinee: Var, split: s.PatternSplit): c.Split = split match { - case s.Split.Cons(head, tail) => desugarPatternBranch(scrutinee, head) :: rec(scrutinee, tail) + case s.Split.Cons(head, tail) => + lazy val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty) + head.pattern match { + case s.AliasPattern(nme, pattern) => ??? + case s.LiteralPattern(literal) => ??? + case s.ConcretePattern(nme) => + val condition = freshScrutinee() + c.Split.Let( + rec = false, + name = condition, + term = mkBinOp(scrutinee, Var("==="), nme, true), + tail = c.Branch(condition, truePattern, continuation) :: rec(scrutinee, tail) + ) + case s.NamePattern(nme) => c.Branch(scrutinee, c.Pattern.Name(nme), continuation) :: rec(scrutinee, tail) + case pattern @ s.ClassPattern(nme, fields) => flattenNestedPattern(pattern, scrutinee, continuation) :: rec(scrutinee, tail) + case s.TuplePattern(fields) => ??? + case s.RecordPattern(entries) => ??? + } case s.Split.Let(isRec, nme, rhs, tail) => c.Split.Let(isRec, nme, rhs, rec(scrutinee, tail)) case s.Split.Else(default) => c.Split.Else(default) case s.Split.Nil => c.Split.Nil diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index 8d34067b..85952793 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -30,7 +30,7 @@ trait Transformation { self: mlscript.pretyper.Traceable => case IfThen(expr, rhs) => splitAnd(expr).foldRight(Split.then(rhs)) { case (OperatorIs(scrutinee, pattern), acc) => - TermBranch.Match(scrutinee, PatternBranch(rec(pattern), acc) |> Split.single) |> Split.single + TermBranch.Match(scrutinee, PatternBranch(transformPattern(pattern), acc) |> Split.single) |> Split.single case (test, acc) => TermBranch.Boolean(test, acc) |> Split.single } case IfLet(isRec, name, rhs, body) => rare @@ -40,7 +40,7 @@ trait Transformation { self: mlscript.pretyper.Traceable => case init :+ last => init.foldRight[TermSplit](TermBranch.Match(last, transformPatternMatching(rhs)) |> Split.single) { case (OperatorIs(scrutinee, pattern), acc) => - TermBranch.Match(scrutinee, PatternBranch(rec(pattern), acc) |> Split.single) |> Split.single + TermBranch.Match(scrutinee, PatternBranch(transformPattern(pattern), acc) |> Split.single) |> Split.single case (test, acc) => TermBranch.Boolean(test, acc) |> Split.single } case _ => rare @@ -51,7 +51,7 @@ trait Transformation { self: mlscript.pretyper.Traceable => val first = TermBranch.Left(last, OperatorBranch.Binary(op, transformIfBody(rhs)) |> Split.single) |> Split.single init.foldRight[TermSplit](first) { case (OperatorIs(scrutinee, pattern), acc) => - TermBranch.Match(scrutinee, PatternBranch(rec(pattern), acc) |> Split.single) |> Split.single + TermBranch.Match(scrutinee, PatternBranch(transformPattern(pattern), acc) |> Split.single) |> Split.single case (test, acc) => TermBranch.Boolean(test, acc) |> Split.single } case _ => rare @@ -87,9 +87,9 @@ trait Transformation { self: mlscript.pretyper.Traceable => case IfOpApp(lhs, Var("and"), rhs) => separatePattern(lhs) match { case (pattern, S(extraTest)) => - PatternBranch(rec(lhs), ???) |> Split.single + PatternBranch(transformPattern(lhs), ???) |> Split.single case (pattern, N) => - PatternBranch(rec(lhs), transformIfBody(rhs)) |> Split.single + PatternBranch(transformPattern(lhs), transformIfBody(rhs)) |> Split.single } case IfOpApp(lhs, op, rhs) => ??? case IfOpsApp(lhs, opsRhss) => ??? @@ -105,18 +105,19 @@ trait Transformation { self: mlscript.pretyper.Traceable => } } - private def rec(term: Term)(implicit useNewDefs: Bool): Pattern = term match { + private def transformPattern(term: Term)(implicit useNewDefs: Bool): Pattern = term match { + case nme @ Var("true" | "false") => ConcretePattern(nme) case nme @ Var(name) if name.headOption.exists(_.isUpper) => ClassPattern(nme, N) case nme: Var => NamePattern(nme) case literal: Lit => LiteralPattern(literal) case App(classNme @ Var(_), Tup(parameters)) => ClassPattern(classNme, S(parameters.map { case (_, Fld(_, Var("_"))) => N // Consider "_" as wildcard. - case (_, Fld(_, t)) => S(rec(t)) + case (_, Fld(_, t)) => S(transformPattern(t)) })) case Tup(fields) => TuplePattern(fields.map { case _ -> Fld(_, Var("_")) => N // Consider "_" as wildcard. - case _ -> Fld(_, t ) => S(rec(t)) + case _ -> Fld(_, t ) => S(transformPattern(t)) }) // TODO: Support more patterns. case _ => throw new TransformException(msg"Unknown pattern", term.toLoc) @@ -124,7 +125,7 @@ trait Transformation { self: mlscript.pretyper.Traceable => private def separatePattern(term: Term)(implicit useNewDefs: Bool): (Pattern, Opt[Term]) = { val (rawPattern, extraTest) = helpers.separatePattern(term, useNewDefs) - (rec(rawPattern), extraTest) + (transformPattern(rawPattern), extraTest) } private def rare: Nothing = throw new TransformException(msg"Wow, a rare case.", N) diff --git a/shared/src/main/scala/mlscript/ucs/syntax.scala b/shared/src/main/scala/mlscript/ucs/syntax.scala index 1137baed..61d107bf 100644 --- a/shared/src/main/scala/mlscript/ucs/syntax.scala +++ b/shared/src/main/scala/mlscript/ucs/syntax.scala @@ -10,6 +10,7 @@ package object syntax { override def toString(): String = this match { case AliasPattern(nme, pattern) => s"$nme @ $pattern" case LiteralPattern(literal) => literal.toString + case ConcretePattern(nme) => s"`${nme.name}`" case NamePattern(nme) => nme.toString case ClassPattern(Var(name), N) => name case ClassPattern(Var(name), S(parameters)) => @@ -25,6 +26,9 @@ package object syntax { final case class LiteralPattern(literal: Lit) extends Pattern { override def children: List[Located] = literal :: Nil } + final case class ConcretePattern(nme: Var) extends Pattern { + override def children: List[Located] = nme :: Nil + } final case class NamePattern(nme: Var) extends Pattern { override def children: List[Located] = nme :: Nil } diff --git a/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls b/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls index d8723813..ad944a17 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls @@ -69,7 +69,7 @@ fun unreachable_1(x) = //│ | | | | | | | Desugared UCS term: //│ | | | | | | | if //│ | | | | | | | x is _ -//│ | | | | | | | let scrut$0 = f(x,) +//│ | | | | | | | let scrut$0 = f(x,) : Bool //│ | | | | | | | scrut$0 is true then "tmux" //│ | | | | | | | else "screen" //│ | | | | | | | x is Some(xv) then "sin" @@ -136,7 +136,7 @@ fun unreachable_1(x) = //│ | | | | | | | | | | normalizeToCaseBranches //│ | | | | | | | Normalized UCS term: //│ | | | | | | | let _* = x -//│ | | | | | | | let scrut$0* = f(x,) +//│ | | | | | | | let scrut$0* = f(x,) : Bool //│ | | | | | | | scrut$0 match //│ | | | | | | | case true => "tmux" //│ | | | | | | | case _ => "screen" @@ -156,7 +156,7 @@ fun unreachable_1(x) = //│ | | | | | | | postProcess ==> //│ | | | | | | | Post-processed UCS term: //│ | | | | | | | let _* = x -//│ | | | | | | | let scrut$0* = f(x,) +//│ | | | | | | | let scrut$0* = f(x,) : Bool //│ | | | | | | | scrut$0 match //│ | | | | | | | case true => "tmux" //│ | | | | | | | case _ => "screen" diff --git a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls index e36980c2..990af73c 100644 --- a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls +++ b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls @@ -13,17 +13,21 @@ fun f(x) = if x is Some(1) then true else false //│ fun f: (Object & ~#Some | Some[Num]) -> Bool fun g(x) = if x then 1 else 2 -//│ fun g: Object -> (1 | 2) +//│ fun g: Bool -> (1 | 2) + +:e +fun test_must_be_boolean(x) = if 0 then 1 else 2 +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.19: fun test_must_be_boolean(x) = if 0 then 1 else 2 +//│ ║ ^ +//│ ╙── integer literal of type `0` is not an instance of type `Bool` +//│ fun test_must_be_boolean: anything -> (1 | 2) fun g(x) = if x is true then 1 else 2 -//│ fun g: anything -> 1 -//│ Syntax error: -//│ Invalid destructuring assignment target +//│ fun g: Eql[true] -> (1 | 2) fun g(x) = if x && true is true then 1 else 2 -//│ fun g: Bool -> 1 -//│ Syntax error: -//│ Invalid destructuring assignment target +//│ fun g: Bool -> (1 | 2) fun h(x) = if (x : Bool) then 1 else 2 //│ fun h: Bool -> (1 | 2) From 8710d4d56329e5fc2e8caaa176c6fd4f16d40a9a Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Fri, 1 Dec 2023 18:58:31 +0800 Subject: [PATCH 010/143] Amend changed files due to the merge --- shared/src/test/diff/nu/AbstractClasses.mls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/src/test/diff/nu/AbstractClasses.mls b/shared/src/test/diff/nu/AbstractClasses.mls index ab09dd59..dd80a049 100644 --- a/shared/src/test/diff/nu/AbstractClasses.mls +++ b/shared/src/test/diff/nu/AbstractClasses.mls @@ -51,7 +51,7 @@ new Foo(1) { fun f = id } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ error //│ Code generation encountered an error: -//│ cannot generate code for term Rft(App(NuNew(Var(Foo)), Tup(_: IntLit(1))), ...) +//│ cannot generate code for term Rft(App(NuNew(Var(Foo)), Tup((N, IntLit(1)))), ...) abstract class Bar extends Foo(1) From 69e8460cd87882e900dfd41d5f2b0ca537684c36 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 20 Dec 2023 01:18:55 +0800 Subject: [PATCH 011/143] Extract the inspect function as a general utility This commit was cherry-picked from a future commit which changed many files. --- .../scala/mlscript/compiler/ClassLifter.scala | 2 +- .../scala/mlscript/compiler/Helpers.scala | 2 +- .../src/main/scala/mlscript/JSBackend.scala | 11 +- .../main/scala/mlscript/codegen/Helpers.scala | 100 ---------- .../scala/mlscript/pretyper/PreTyper.scala | 5 +- .../main/scala/mlscript/ucs/DesugarUCS.scala | 1 - .../main/scala/mlscript/utils/inspect.scala | 182 ++++++++++++++++++ shared/src/test/diff/basics/Blocks.fun | 10 +- shared/src/test/diff/basics/Data.fun | 4 +- shared/src/test/diff/basics/Datatypes.fun | 14 +- shared/src/test/diff/basics/Either.fun | 4 +- shared/src/test/diff/basics/Flow.fun | 4 +- shared/src/test/diff/basics/Operators.fun | 16 +- shared/src/test/diff/basics/Slashes.fun | 2 +- shared/src/test/diff/mlscript/Basics.mls | 2 +- .../src/test/diff/mlscript/ByNameByValue.mls | 4 +- shared/src/test/diff/mlscript/MultiArgs.mls | 8 +- shared/src/test/diff/mlscript/Mut.mls | 2 +- shared/src/test/diff/mlscript/Ops.mls | 10 +- shared/src/test/diff/mlscript/Weird.mls | 2 +- shared/src/test/diff/nu/AbstractClasses.mls | 2 +- shared/src/test/diff/nu/BadBlocks.mls | 2 +- shared/src/test/diff/nu/LamPatterns.mls | 2 +- shared/src/test/diff/nu/LetRec.mls | 2 +- shared/src/test/diff/nu/NewNew.mls | 10 +- shared/src/test/diff/nu/OpLam.mls | 2 +- shared/src/test/diff/nu/OverrideShorthand.mls | 4 +- shared/src/test/diff/nu/RightAssocOps.mls | 6 +- shared/src/test/diff/nu/TODO_Classes.mls | 2 +- shared/src/test/diff/ucs/LeadingAnd.mls | 4 +- shared/src/test/diff/ucs/SplitOps.mls | 4 +- .../src/test/scala/mlscript/DiffTests.scala | 4 +- 32 files changed, 254 insertions(+), 175 deletions(-) delete mode 100644 shared/src/main/scala/mlscript/codegen/Helpers.scala create mode 100644 shared/src/main/scala/mlscript/utils/inspect.scala diff --git a/compiler/shared/main/scala/mlscript/compiler/ClassLifter.scala b/compiler/shared/main/scala/mlscript/compiler/ClassLifter.scala index 5124a31c..947db768 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ClassLifter.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ClassLifter.scala @@ -7,7 +7,7 @@ import scala.collection.mutable.StringBuilder as StringBuilder import scala.collection.mutable.Map as MutMap import scala.collection.mutable.Set as MutSet import scala.collection.mutable.ArrayBuffer as ArrayBuffer -import mlscript.codegen.Helpers.inspect as showStructure +import inspect.deep as showStructure import mlscript.codegen.CodeGenError import mlscript.compiler.mono.MonomorphError diff --git a/compiler/shared/main/scala/mlscript/compiler/Helpers.scala b/compiler/shared/main/scala/mlscript/compiler/Helpers.scala index 5eb1a526..ed842b70 100644 --- a/compiler/shared/main/scala/mlscript/compiler/Helpers.scala +++ b/compiler/shared/main/scala/mlscript/compiler/Helpers.scala @@ -1,7 +1,7 @@ package mlscript package compiler -import mlscript.codegen.Helpers.inspect as showStructure +import mlscript.utils.inspect.deep as showStructure import mlscript.compiler.mono.{Monomorph, MonomorphError} import scala.collection.mutable.ArrayBuffer diff --git a/shared/src/main/scala/mlscript/JSBackend.scala b/shared/src/main/scala/mlscript/JSBackend.scala index 6a3b9e3c..621fb7bd 100644 --- a/shared/src/main/scala/mlscript/JSBackend.scala +++ b/shared/src/main/scala/mlscript/JSBackend.scala @@ -1,7 +1,6 @@ package mlscript import mlscript.utils._, shorthands._, algorithms._ -import mlscript.codegen.Helpers._ import mlscript.codegen._ import scala.collection.mutable.{ListBuffer, HashMap, HashSet} import mlscript.{JSField, JSLit} @@ -69,7 +68,7 @@ abstract class JSBackend(allowUnresolvedSymbols: Bool) { case Inst(bod) => translatePattern(bod) case _: Lam | _: App | _: Sel | _: Let | _: Blk | _: Bind | _: Test | _: With | _: CaseOf | _: Subs | _: Assign | If(_, _) | New(_, _) | NuNew(_) | _: Splc | _: Forall | _: Where | _: Super | _: Eqn | _: AdtMatchWith | _: Rft => - throw CodeGenError(s"term ${inspect(t)} is not a valid pattern") + throw CodeGenError(s"term ${inspect.deep(t)} is not a valid pattern") } private def translateParams(t: Term)(implicit scope: Scope): Ls[JSPattern] = t match { @@ -172,7 +171,7 @@ abstract class JSBackend(allowUnresolvedSymbols: Bool) { } callee(args map { case (_, Fld(_, arg)) => translateTerm(arg) }: _*) case App(trm, splice) => ??? // TODO represents `trm(...splice)` - case _ => throw CodeGenError(s"ill-formed application ${inspect(term)}") + case _ => throw CodeGenError(s"ill-formed application ${inspect.deep(term)}") } /** @@ -299,7 +298,7 @@ abstract class JSBackend(allowUnresolvedSymbols: Bool) { case _: Subs | _: Sel | _: Var => JSCommaExpr(JSAssignExpr(translateTerm(lhs), translateTerm(value)) :: JSArray(Nil) :: Nil) case _ => - throw CodeGenError(s"illegal assignemnt left-hand side: ${inspect(lhs)}") + throw CodeGenError(s"illegal assignemnt left-hand side: ${inspect.deep(lhs)}") } case Inst(bod) => translateTerm(bod) case iff: If => @@ -312,7 +311,7 @@ abstract class JSBackend(allowUnresolvedSymbols: Bool) { case n: JSNew => n case t => JSNew(t) } - case _ => throw CodeGenError(s"Unsupported `new` class term: ${inspect(cls)}") + case _ => throw CodeGenError(s"Unsupported `new` class term: ${inspect.deep(cls)}") } // * Would not be quite correct: // JSNew(translateTerm(cls)) @@ -330,7 +329,7 @@ abstract class JSBackend(allowUnresolvedSymbols: Bool) { case Eqn(Var(name), _) => throw CodeGenError(s"assignment of $name is not supported outside a constructor") case _: Bind | _: Test | If(_, _) | _: Splc | _: Where | _: AdtMatchWith | _: Rft => - throw CodeGenError(s"cannot generate code for term ${inspect(term)}") + throw CodeGenError(s"cannot generate code for term ${inspect.deep(term)}") } private def translateCaseBranch(scrut: JSExpr, branch: CaseBranches)(implicit diff --git a/shared/src/main/scala/mlscript/codegen/Helpers.scala b/shared/src/main/scala/mlscript/codegen/Helpers.scala deleted file mode 100644 index 5c20613c..00000000 --- a/shared/src/main/scala/mlscript/codegen/Helpers.scala +++ /dev/null @@ -1,100 +0,0 @@ -package mlscript.codegen - -import mlscript._, mlscript.utils.shorthands._ -import scala.collection.immutable.{Map, Set} - -object Helpers { - /** - * Show how a term is actually structured. - */ - def inspect(t: Terms): Str = t match { - case Var(name) => s"Var($name)" - case Lam(lhs, rhs) => s"Lam(${inspect(lhs)}, ${inspect(rhs)})" - case App(lhs, rhs) => s"App(${inspect(lhs)}, ${inspect(rhs)})" - case Tup(fields) => - val entries = fields map { - case (S(name), Fld(_, value)) => s"(S(${inspect(name)}), ${inspect(value)})" - case (N, Fld(_, value)) => s"(N, ${inspect(value)})" - } - s"Tup(${entries mkString ", "})" - case Rcd(fields) => - val entries = fields.iterator - .map { case k -> Fld(_, v) => s"${inspect(k)} = ${inspect(v)}" } - .mkString(", ") - s"Rcd($entries)" - case Sel(receiver, fieldName) => s"Sel(${inspect(receiver)}, $fieldName)" - case Let(isRec, name, rhs, body) => s"Let($isRec, $name, ${inspect(rhs)}, ${inspect(body)})" - case Blk(stmts) => s"Blk(...)" - case Bra(rcd, trm) => s"Bra(rcd = $rcd, ${inspect(trm)})" - case Asc(trm, ty) => s"Asc(${inspect(trm)}, $ty)" - case Bind(lhs, rhs) => s"Bind(${inspect(lhs)}, ${inspect(rhs)})" - case Test(trm, ty) => s"Test(${inspect(trm)}, ${inspect(ty)})" - case With(trm, fields) => - s"With(${inspect(trm)}, ${inspect(fields)})" - case CaseOf(trm, cases) => - def inspectCaseBranches(br: CaseBranches): Str = br match { - case Case(clsNme, body, rest) => - s"Case($clsNme, ${inspect(body)}, ${inspectCaseBranches(rest)})" - case Wildcard(body) => s"Wildcard(${inspect(body)})" - case NoCases => "NoCases" - } - s"CaseOf(${inspect(trm)}, ${inspectCaseBranches(cases)}" - case IntLit(value) => s"IntLit($value)" - case DecLit(value) => s"DecLit($value)" - case StrLit(value) => s"StrLit($value)" - case UnitLit(value) => s"UnitLit($value)" - case Subs(arr, idx) => s"Subs(${inspect(arr)}, ${inspect(idx)})" - case Assign(f, v) => s"Assign(${inspect(f)}, ${inspect(v)})" - case Splc(fs) => - val elems = fs.map{case L(l) => s"...${inspect(l)}" case R(Fld(_, r)) => inspect(r)}.mkString(", ") - s"Splc($elems)" - case If(bod, els) => s"If(${inspect(bod)}, ${els.map(inspect)})" - case New(base, body) => s"New(${base}, ${inspect(body)})" - case NuNew(base) => s"NuNew(${inspect(base)})" - case TyApp(base, targs) => s"TyApp(${inspect(base)}, ${targs})" - case Def(rec, nme, rhs, isByname) => - s"Def($rec, $nme, ${rhs.fold(inspect, "" + _)}, $isByname)" - case Where(bod, sts) => s"Where(${inspect(bod)}, ...)" - case Forall(ps, bod) => s"Forall($ps, ${inspect(bod)})" - case Inst(bod) => s"Inst(${inspect(bod)})" - case Eqn(lhs, rhs) => s"Eqn(${inspect(lhs)}, ${inspect(rhs)})" - case Super() => "Super()" - case AdtMatchWith(cond, arms) => - s"match ${inspect(cond)} with ${arms.map(patmat => s"${inspect(patmat.pat)} -> ${inspect(patmat.rhs)}").mkString(" | ")}" - case Rft(bse, tu) => s"Rft(${inspect(bse)}, ...)" - } - - def inspect(body: IfBody): Str = body match { - case IfElse(expr) => s"IfElse(${inspect(expr)}" - case IfThen(expr, rhs) => s"IfThen(${inspect(expr)}, ${inspect(rhs)}" - case IfBlock(lines) => s"IfBlock(${ - lines.iterator.map { - case L(body) => inspect(body) - case R(NuFunDef(S(isRec), nme, _, _, L(rhs))) => - s"Let($isRec, ${nme.name}, ${inspect(rhs)})" - case R(_) => ??? - }.mkString(";") - })" - case IfOpsApp(lhs, opsRhss) => s"IfOpsApp(${inspect(lhs)}, ${ - opsRhss.iterator.map { case (op, body) => - s"$op -> ${inspect(body)}" - } - }".mkString("; ") - case IfLet(isRec, name, rhs, body) => ??? - case IfOpApp(lhs, op, rhs) => - s"IfOpApp(${inspect(lhs)}, ${inspect(op)}, ${inspect(rhs)}" - } - def inspect(t: TypingUnit): Str = t.entities.iterator - .map { - case term: Term => inspect(term) - case NuFunDef(lt, nme, symNme, targs, L(term)) => - s"NuFunDef(${lt}, ${nme.name}, $symNme, ${targs.mkString("[", ", ", "]")}, ${inspect(term)})" - case NuFunDef(lt, nme, symNme, targs, R(ty)) => - s"NuFunDef(${lt}, ${nme.name}, $symNme, ${targs.mkString("[", ", ", "]")}, $ty)" - case NuTypeDef(kind, nme, tparams, params, ctor, sig, parents, sup, ths, body) => - s"NuTypeDef(${kind.str}, ${nme.name}, ${tparams.mkString("(", ", ", ")")}, ${ - inspect(params.getOrElse(Tup(Nil)))}, ${parents.map(inspect).mkString("(", ", ", ")")}, $sup, $ths, ${inspect(body)})" - case others => others.toString() - } - .mkString("TypingUnit(", ", ", ")") -} diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index a3f27e6c..e4e4cfee 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -2,7 +2,6 @@ package mlscript.pretyper import mlscript.ucs.DesugarUCS import mlscript._, utils._, shorthands._ -import mlscript.codegen.Helpers.inspect class PreTyper(override val debugLevel: Opt[Int], useNewDefs: Bool) extends Traceable with DesugarUCS { protected def raise(diagnostics: Ls[Diagnostic]): Unit = () @@ -24,9 +23,9 @@ class PreTyper(override val debugLevel: Opt[Int], useNewDefs: Bool) extends Trac case PlainTup(arguments @ _*) => arguments.map { case nme: Var => new ValueSymbol(nme, false) - case other => println("Unknown parameters: " + inspect(other)); ??? // TODO: bad + case other => println("Unknown parameters: " + inspect.deep(other)); ??? // TODO: bad }.toList - case other => println("Unknown parameters: " + inspect(other)); ??? // TODO: bad + case other => println("Unknown parameters: " + inspect.deep(other)); ??? // TODO: bad } // `traverseIf` is meaningless because it represents patterns with terms. diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index 5bee6056..0c09de16 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -4,7 +4,6 @@ import collection.mutable.{Map => MutMap} import mlscript.ucs.stages._ import mlscript.pretyper.{PreTyper, Scope, ScrutineeSymbol, Symbol, ValueSymbol} import mlscript._, utils._, shorthands._ -import mlscript.codegen.Helpers.inspect import mlscript.Message, Message.MessageContext // TODO: Rename to `Desugarer` once the old desugarer is removed. diff --git a/shared/src/main/scala/mlscript/utils/inspect.scala b/shared/src/main/scala/mlscript/utils/inspect.scala new file mode 100644 index 00000000..3e4fa7d9 --- /dev/null +++ b/shared/src/main/scala/mlscript/utils/inspect.scala @@ -0,0 +1,182 @@ +package mlscript.utils + +import mlscript._, shorthands._ + +object inspect { + object shallow { + def apply(term: Statement): Str = term match { + case Var(name) => s"Var(\"$name\")" + case literal: Lit => s"Lit(${literal.toString})" + case fd: NuFunDef => fd.isLetRec.fold("fun")(if (_) "let rec" else "let") + " " + fd.nme.name + case td: NuTypeDef => s"${td.kind.str} ${td.nme.name}" + case _ => + val name = term.getClass.getSimpleName + val arity = term.children.length // Imprecise + if (arity === 0) { name } else s"${name}(${(", _" * arity).drop(2)})" + } + + def apply(d: TypingUnit): Str = d.entities.iterator + .map(apply) + .mkString("{", ", ", "}") + } + + object deep { + def apply(t: Opt[Located]): Str = t match { + case N => "N" + case S(l) => s"S(${apply(l)})" + } + + def apply(t: Ls[Located]): Str = t match { + case Nil => "Nil" + case head :: Nil => s"${apply(head)} :: Nil" + case first :: second :: Nil => s"${apply(first)} :: ${apply(second)} :: Nil" + case _ => t.iterator.map(apply).mkString("Ls(", ", ", ")") + } + + def apply[A <: Located, B <: Located](t: Either[A, B]): Str = t match { + case L(value) => s"L(${apply(value)})" + case R(value) => s"R(${apply(value)})" + } + + def apply(t: Located): Str = t match { + case st: Statement => statement(st) + case fl: Field => field(fl) + case ty: TypeLike => typeLike(ty) + case ib: IfBody => ifBody(ib) + case tu: TypingUnit => typingUnit(tu) + case _ => "??" + } + + private def statement(statement: Statement): Str = statement match { + case Def(rec, nme, rhs, isByname) => s"Def($rec, ${apply(nme)}, ${apply(rhs)}, $isByname)" + case TypeDef(kind, nme, tparams, body, mthDecls, mthDefs, positionals, adtInfo) => s"TypeDef(...)" + case Var(name) => s"Var(\"$name\")" + case Lam(lhs, rhs) => s"Lam(${apply(lhs)}, ${apply(rhs)})" + case App(lhs, rhs) => s"App(${apply(lhs)}, ${apply(rhs)})" + case Tup(fields) => + fields.iterator.map { + case (S(name), Fld(_, value)) => s"(S(${apply(name)}), ${apply(value)})" + case (N, Fld(_, value)) => s"(N, ${apply(value)})" + }.mkString("Tup(", ", ", ")") + case Rcd(fields) => + fields.iterator.map { case k -> Fld(_, v) => s"${apply(k)} = ${apply(v)}" }.mkString("Rcd(", ", ", ")") + case Sel(receiver, fieldName) => s"Sel(${apply(receiver)}, $fieldName)" + case Let(isRec, name, rhs, body) => s"Let($isRec, $name, ${apply(rhs)}, ${apply(body)})" + case Blk(stmts) => s"Blk(${stmts.iterator.map(apply).mkString(", ")})" + case Bra(rcd, trm) => s"Bra(rcd = $rcd, ${apply(trm)})" + case Asc(trm, ty) => s"Asc(${apply(trm)}, $ty)" + case Bind(lhs, rhs) => s"Bind(${apply(lhs)}, ${apply(rhs)})" + case Test(trm, ty) => s"Test(${apply(trm)}, ${apply(ty)})" + case With(trm, fields) => + s"With(${apply(trm)}, ${apply(fields)})" + case CaseOf(trm, cases) => + def inspectCaseBranches(br: CaseBranches): Str = br match { + case Case(clsNme, body, rest) => + s"Case($clsNme, ${apply(body)}, ${inspectCaseBranches(rest)})" + case Wildcard(body) => s"Wildcard(${apply(body)})" + case NoCases => "NoCases" + } + s"CaseOf(${apply(trm)}, ${inspectCaseBranches(cases)}" + case IntLit(value) => s"IntLit($value)" + case DecLit(value) => s"DecLit($value)" + case StrLit(value) => s"StrLit($value)" + case UnitLit(value) => s"UnitLit($value)" + case Subs(arr, idx) => s"Subs(${apply(arr)}, ${apply(idx)})" + case Assign(f, v) => s"Assign(${apply(f)}, ${apply(v)})" + case Splc(fs) => + fs.iterator.map{ case L(l) => s"...${apply(l)}" case R(Fld(_, r)) => apply(r)}.mkString("Splc(", ", ", ")") + case If(bod, els) => s"If(${apply(bod)}, ${els.map(apply)})" + case New(base, body) => s"New(${base}, ${apply(body)})" + case NuNew(base) => s"NuNew(${apply(base)})" + case TyApp(base, targs) => s"TyApp(${apply(base)}, ${targs})" + case Where(bod, sts) => s"Where(${apply(bod)}, ...)" + case Forall(ps, bod) => s"Forall($ps, ${apply(bod)})" + case Inst(bod) => s"Inst(${apply(bod)})" + case Eqn(lhs, rhs) => s"Eqn(${apply(lhs)}, ${apply(rhs)})" + case Super() => "Super()" + case AdtMatchWith(cond, arms) => + s"match ${apply(cond)} with ${arms.map(patmat => s"${apply(patmat.pat)} -> ${apply(patmat.rhs)}").mkString(" | ")}" + case Rft(bse, tu) => s"Rft(${apply(bse)}, ${apply(tu)})" + case LetS(isRec, pat, rhs) => s"LetS($isRec, ${apply(pat)}, ${apply(rhs)})" + case DataDefn(body) => s"DataDefn(${apply(body)})" + case DatatypeDefn(head, body) => s"DatatypeDefn(${apply(head)}, ${apply(body)})" + case NuTypeDef(kind, nme, tparams, params, ctor, sig, parents, superAnnot, thisAnnot, body) => + s"NuTypeDef($kind, ${apply(nme)}, Ls(" + + tparams.iterator.map { + case (S(vi), tn) => s"S($vi) -> ${apply(tn)}" + case (N, tn) => s"N -> ${apply(tn)}" + }.mkString(", ") + "), " + + apply(params) + ", " + + apply(ctor) + ", " + + apply(sig) + ", " + + parents.iterator.map(apply).mkString("Ls(", ", ", ")") + ", " + + apply(superAnnot) + ", " + + apply(thisAnnot) + ", " + + apply(body) + ")" + case NuFunDef(isLetRec, nme, symbolicNme, tparams, rhs) => + s"NuFunDef($isLetRec, ${nme.name}, ${apply(symbolicNme)}, ${apply(tparams)}, ${apply(rhs)})" + case Constructor(params, body) => s"Constructor" + } + + private def field(field: Field): Str = field match { + case Field(nme, value) => s"Fld(${apply(nme)}, ${apply(value)})" + } + + private def typeLike(ty: TypeLike): Str = ty match { + case Union(lhs, rhs) => s"Union(${apply(lhs)}, ${apply(rhs)})" + case Inter(lhs, rhs) => s"Inter(${apply(lhs)}, ${apply(rhs)})" + case Function(lhs, rhs) => s"Function(${apply(lhs)}, ${apply(rhs)})" + case Record(fields) => s"Record(${fields.iterator.map { case (nme, ty) => s"$nme: ${apply(ty)}" }.mkString(", ")})" + case Tuple(fields) => s"Tuple(${fields.iterator.map { + case N -> field => s"N -> ${apply(field)}" + case S(nme) -> field => s"S($nme) -> ${apply(field)}" + }.mkString(", ")})" + case Recursive(uv, body) => s"Recursive(${apply(uv)}, ${apply(body)})" + case AppliedType(base, targs) => s"AppliedType(${apply(base)}, ${apply(targs)})" + case Selection(base, name) => s"Selection(${apply(base)}, $name)" + case Neg(base) => s"Neg(${apply(base)})" + case Rem(base, names) => s"Rem(${apply(base)}, ${names.iterator.map(apply).mkString(", ")})" + case Bounds(lb, ub) => s"Bounds(${apply(lb)}, ${apply(ub)})" + case WithExtension(base, rcd) => s"WithExtension(${apply(base)}, ${apply(rcd)})" + case Splice(fields) => s"Splice(${fields.iterator.map { + case L(l) => s"L(${apply(l)})" + case R(f) => s"R(${apply(f)})" + }.mkString(", ")})" + case Constrained(base, tvBounds, where) => s"Constrained(${apply(base)}, Ls(${tvBounds.iterator.map { + case (tv, bounds) => s"${apply(tv)} -> ${apply(bounds)}" + }}), ${apply(where)})" + case Top => "Top" + case Bot => "Bot" + case Literal(lit) => s"Literal(${lit.toString})" + case TypeName(name) => s"TypeName(\"$name\")" + case TypeTag(name) => s"TypeTag($name)" + case TypeVar(identifier, nameHint) => s"TypeVar(${identifier match { + case L(i) => s"L($i)" + case R(n) => s"R($n)" + }}, ${nameHint.fold("N")(n => s"S($n)")})" + case PolyType(targs, body) => s"PolyType(Ls(${targs.iterator.map(_.fold(apply, apply)).mkString(", ")}), ${apply(body)})" + case Signature(members, result) => s"Signature(${members.iterator.map(apply(_: Statement)).mkString("Ls(", ", ", ")")}, ${apply(result)})" + case t: NuDecl => apply(t: Statement) + } + + private def ifBody(body: IfBody): Str = body match { + case IfElse(expr) => s"IfElse(${apply(expr)}" + case IfThen(expr, rhs) => s"IfThen(${apply(expr)}, ${apply(rhs)}" + case IfBlock(lines) => s"IfBlock(${ + lines.iterator.map(_.fold(apply, apply)).mkString("Ls(", ", ", ")") + })" + case IfOpsApp(lhs, opsRhss) => s"IfOpsApp(${apply(lhs)}, ${ + opsRhss.iterator.map { case (op, body) => + s"$op -> ${apply(body)}" + } + }".mkString("; ") + case IfLet(isRec, name, rhs, body) => ??? + case IfOpApp(lhs, op, rhs) => + s"IfOpApp(${apply(lhs)}, ${apply(op)}, ${apply(rhs)}" + } + + private def typingUnit(t: TypingUnit): Str = t.entities.iterator + .map(apply) + .mkString("TypingUnit(", ", ", ")") + } +} \ No newline at end of file diff --git a/shared/src/test/diff/basics/Blocks.fun b/shared/src/test/diff/basics/Blocks.fun index 65d88924..3d0a46ee 100644 --- a/shared/src/test/diff/basics/Blocks.fun +++ b/shared/src/test/diff/basics/Blocks.fun @@ -44,7 +44,7 @@ discard / foo 1 //│ Parsed: discard(...(foo(...{1}))); //│ Desugared: discard(...(foo(...{1}))) -//│ AST: App(Var(discard), App(Var(foo), Blk(...))) +//│ AST: App(Var("discard"), App(Var("foo"), Blk(IntLit(1)))) :e discard foo @@ -81,7 +81,7 @@ id id id //│ Parsed: id(...id)(...{id}); //│ Desugared: id(...id)(...{id}) -//│ AST: App(App(Var(id), Var(id)), Blk(...)) +//│ AST: App(App(Var("id"), Var("id")), Blk(Var("id"))) //│ res: 'a -> 'a :p @@ -91,7 +91,7 @@ id id id id id id //│ Parsed: id(...id)(...id)(...{id(...id)(...id)(...{id(...id)(...id)(...{id(...id)(...id)})})}); //│ Desugared: id(...id)(...id)(...{id(...id)(...id)(...{id(...id)(...id)(...{id(...id)(...id)})})}) -//│ AST: App(App(App(Var(id), Var(id)), Var(id)), Blk(...)) +//│ AST: App(App(App(Var("id"), Var("id")), Var("id")), Blk(App(App(App(Var("id"), Var("id")), Var("id")), Blk(App(App(App(Var("id"), Var("id")), Var("id")), Blk(App(App(Var("id"), Var("id")), Var("id")))))))) //│ res: 'a -> 'a :p @@ -100,7 +100,7 @@ id id / id id //│ Parsed: id(...id)(...{id(...id)(...{id(...id)})}); //│ Desugared: id(...id)(...{id(...id)(...{id(...id)})}) -//│ AST: App(App(Var(id), Var(id)), Blk(...)) +//│ AST: App(App(Var("id"), Var("id")), Blk(App(App(Var("id"), Var("id")), Blk(App(Var("id"), Var("id")))))) //│ res: 'a -> 'a :p @@ -109,7 +109,7 @@ id id id id //│ Parsed: id(...id)(...{id(...id)})(...{id(...id)}); //│ Desugared: id(...id)(...{id(...id)})(...{id(...id)}) -//│ AST: App(App(App(Var(id), Var(id)), Blk(...)), Blk(...)) +//│ AST: App(App(App(Var("id"), Var("id")), Blk(App(Var("id"), Var("id")))), Blk(App(Var("id"), Var("id")))) //│ res: 'a -> 'a let foo = diff --git a/shared/src/test/diff/basics/Data.fun b/shared/src/test/diff/basics/Data.fun index f155732e..d30a7ba9 100644 --- a/shared/src/test/diff/basics/Data.fun +++ b/shared/src/test/diff/basics/Data.fun @@ -4,7 +4,7 @@ data Test a b //│ Parsed: data Test(...a)(...b); //│ Desugared: class Test[a, b]: {a: a, b: b} //│ Desugared: def Test: forall a b. (...a) -> (...b) -> Test[a, b] -//│ AST: Def(false, Test, PolyType(List(Left(TypeName(a)), Left(TypeName(b))),Function(TypeName(a),Function(TypeName(b),AppliedType(TypeName(Test),List(TypeName(a), TypeName(b)))))), true) +//│ AST: Def(false, Var("Test"), R(PolyType(Ls(TypeName("a"), TypeName("b")), Function(TypeName("a"), Function(TypeName("b"), AppliedType(TypeName("Test"), TypeName("a") :: TypeName("b") :: Nil))))), true) //│ Defined class Test[+a, +b] //│ Test: 'a -> 'b -> Test['a, 'b] @@ -13,7 +13,7 @@ data Person(name: string, age: int) //│ Parsed: data Person(...'(' {[name: string, age: int,]} ')'); //│ Desugared: class Person: {age: int, name: string} //│ Desugared: def Person: (name: string, age: int) -> Person[] -//│ AST: Def(false, Person, PolyType(List(),Function(Tuple(List((Some(name),Field(None,TypeName(string))), (Some(age),Field(None,TypeName(int))))),AppliedType(TypeName(Person),List()))), true) +//│ AST: Def(false, Var("Person"), R(PolyType(Ls(), Function(Tuple(S(name) -> Fld(N, TypeName("string")), S(age) -> Fld(N, TypeName("int"))), AppliedType(TypeName("Person"), Nil)))), true) //│ Defined class Person //│ Person: (name: string, age: int,) -> Person diff --git a/shared/src/test/diff/basics/Datatypes.fun b/shared/src/test/diff/basics/Datatypes.fun index 2b2b0e73..89f0a689 100644 --- a/shared/src/test/diff/basics/Datatypes.fun +++ b/shared/src/test/diff/basics/Datatypes.fun @@ -6,9 +6,9 @@ data type Boolean of Tru, Fals //│ Desugared: class Tru: {} //│ Desugared: class Fals: {} //│ Desugared: def Tru: Tru[] -//│ AST: Def(false, Tru, PolyType(List(),AppliedType(TypeName(Tru),List())), true) +//│ AST: Def(false, Var("Tru"), R(PolyType(Ls(), AppliedType(TypeName("Tru"), Nil))), true) //│ Desugared: def Fals: Fals[] -//│ AST: Def(false, Fals, PolyType(List(),AppliedType(TypeName(Fals),List())), true) +//│ AST: Def(false, Var("Fals"), R(PolyType(Ls(), AppliedType(TypeName("Fals"), Nil))), true) //│ Defined type alias Boolean //│ Defined class Tru //│ Defined class Fals @@ -29,7 +29,7 @@ data type Bool2 of True2 & False2 //│ Desugared: type alias Bool2 = &[True2, False2] //│ Desugared: class &[True2, False2]: {False2 <: False2, True2 <: True2} //│ Desugared: def &: forall True2 False2. (...True2) -> (...False2) -> &[True2, False2] -//│ AST: Def(false, &, PolyType(List(Left(TypeName(True2)), Left(TypeName(False2))),Function(TypeName(True2),Function(TypeName(False2),AppliedType(TypeName(&),List(TypeName(True2), TypeName(False2)))))), true) +//│ AST: Def(false, Var("&"), R(PolyType(Ls(TypeName("True2"), TypeName("False2")), Function(TypeName("True2"), Function(TypeName("False2"), AppliedType(TypeName("&"), TypeName("True2") :: TypeName("False2") :: Nil))))), true) //│ ╔══[ERROR] type identifier not found: True2 //│ ║ l.27: data type Bool2 of True2 & False2 //│ ╙── ^^^^^ @@ -122,9 +122,9 @@ data type List a of //│ Desugared: class Nil[a]: {} //│ Desugared: class Cons[a]: {head: a, tail: anything} //│ Desugared: def Nil: forall a. Nil[a] -//│ AST: Def(false, Nil, PolyType(List(Left(TypeName(a))),AppliedType(TypeName(Nil),List(TypeName(a)))), true) +//│ AST: Def(false, Var("Nil"), R(PolyType(Ls(TypeName("a")), AppliedType(TypeName("Nil"), TypeName("a") :: Nil))), true) //│ Desugared: def Cons: forall a. (head: a) -> (tail: anything) -> Cons[a] -//│ AST: Def(false, Cons, PolyType(List(Left(TypeName(a))),Function(Tuple(List((Some(head),Field(None,TypeName(a))))),Function(Tuple(List((Some(tail),Field(None,Top)))),AppliedType(TypeName(Cons),List(TypeName(a)))))), true) +//│ AST: Def(false, Var("Cons"), R(PolyType(Ls(TypeName("a")), Function(Tuple(S(head) -> Fld(N, TypeName("a"))), Function(Tuple(S(tail) -> Fld(N, Top)), AppliedType(TypeName("Cons"), TypeName("a") :: Nil))))), true) //│ Defined type alias List[+a] //│ Defined class Nil[±a] //│ Defined class Cons[+a] @@ -144,7 +144,7 @@ data type Ls of LsA a //│ Desugared: type alias Ls = LsA[a] //│ Desugared: class LsA[a]: {a: a} //│ Desugared: def LsA: forall a. (...a) -> LsA[a] -//│ AST: Def(false, LsA, PolyType(List(Left(TypeName(a))),Function(TypeName(a),AppliedType(TypeName(LsA),List(TypeName(a))))), true) +//│ AST: Def(false, Var("LsA"), R(PolyType(Ls(TypeName("a")), Function(TypeName("a"), AppliedType(TypeName("LsA"), TypeName("a") :: Nil)))), true) //│ ╔══[ERROR] type identifier not found: a //│ ║ l.142: data type Ls of LsA a //│ ╙── ^ @@ -159,7 +159,7 @@ data type Ls2 of LsA2 `a //│ Desugared: type alias Ls2 = LsA2[] //│ Desugared: class LsA2: {`a: 'a} //│ Desugared: def LsA2: (...'a) -> LsA2[] -//│ AST: Def(false, LsA2, PolyType(List(),Function(a,AppliedType(TypeName(LsA2),List()))), true) +//│ AST: Def(false, Var("LsA2"), R(PolyType(Ls(), Function(TypeVar(R(a), N), AppliedType(TypeName("LsA2"), Nil)))), true) //│ ╔══[ERROR] cannot inherit from a polymorphic type //│ ║ l.157: data type Ls2 of LsA2 `a //│ ╙── ^^^^^^^ diff --git a/shared/src/test/diff/basics/Either.fun b/shared/src/test/diff/basics/Either.fun index 7ea04bae..02a36556 100644 --- a/shared/src/test/diff/basics/Either.fun +++ b/shared/src/test/diff/basics/Either.fun @@ -9,9 +9,9 @@ data type Either l r of //│ Desugared: class Left[l, r]: {l: l} //│ Desugared: class Right[l, r]: {r: r} //│ Desugared: def Left: forall l r. (...l) -> Left[l, r] -//│ AST: Def(false, Left, PolyType(List(Left(TypeName(l)), Left(TypeName(r))),Function(TypeName(l),AppliedType(TypeName(Left),List(TypeName(l), TypeName(r))))), true) +//│ AST: Def(false, Var("Left"), R(PolyType(Ls(TypeName("l"), TypeName("r")), Function(TypeName("l"), AppliedType(TypeName("Left"), TypeName("l") :: TypeName("r") :: Nil)))), true) //│ Desugared: def Right: forall l r. (...r) -> Right[l, r] -//│ AST: Def(false, Right, PolyType(List(Left(TypeName(l)), Left(TypeName(r))),Function(TypeName(r),AppliedType(TypeName(Right),List(TypeName(l), TypeName(r))))), true) +//│ AST: Def(false, Var("Right"), R(PolyType(Ls(TypeName("l"), TypeName("r")), Function(TypeName("r"), AppliedType(TypeName("Right"), TypeName("l") :: TypeName("r") :: Nil)))), true) //│ Defined type alias Either[+l, +r] //│ Defined class Left[+l, ±r] //│ Defined class Right[±l, +r] diff --git a/shared/src/test/diff/basics/Flow.fun b/shared/src/test/diff/basics/Flow.fun index 5005f8a4..16b7c5a1 100644 --- a/shared/src/test/diff/basics/Flow.fun +++ b/shared/src/test/diff/basics/Flow.fun @@ -6,9 +6,9 @@ data R x //│ Desugared: class L[x]: {x: x} //│ Desugared: class R[x]: {x: x} //│ Desugared: def L: forall x. (...x) -> L[x] -//│ AST: Def(false, L, PolyType(List(Left(TypeName(x))),Function(TypeName(x),AppliedType(TypeName(L),List(TypeName(x))))), true) +//│ AST: Def(false, Var("L"), R(PolyType(Ls(TypeName("x")), Function(TypeName("x"), AppliedType(TypeName("L"), TypeName("x") :: Nil)))), true) //│ Desugared: def R: forall x. (...x) -> R[x] -//│ AST: Def(false, R, PolyType(List(Left(TypeName(x))),Function(TypeName(x),AppliedType(TypeName(R),List(TypeName(x))))), true) +//│ AST: Def(false, Var("R"), R(PolyType(Ls(TypeName("x")), Function(TypeName("x"), AppliedType(TypeName("R"), TypeName("x") :: Nil)))), true) //│ Defined class L[+x] //│ Defined class R[+x] //│ L: 'a -> L['a] diff --git a/shared/src/test/diff/basics/Operators.fun b/shared/src/test/diff/basics/Operators.fun index bbf5eff5..845354e7 100644 --- a/shared/src/test/diff/basics/Operators.fun +++ b/shared/src/test/diff/basics/Operators.fun @@ -16,7 +16,7 @@ a + b //│ Parsed: +(...a)(...{b}); //│ Desugared: +(...a)(...{b}) -//│ AST: App(App(Var(+), Var(a)), Blk(...)) +//│ AST: App(App(Var("+"), Var("a")), Blk(Var("b"))) //│ res: int :pe @@ -31,7 +31,7 @@ succ a + c //│ Parsed: +(...(succ(...a)))(...{+(...b)(...{c})}); //│ Desugared: +(...(succ(...a)))(...{+(...b)(...{c})}) -//│ AST: App(App(Var(+), App(Var(succ), Var(a))), Blk(...)) +//│ AST: App(App(Var("+"), App(Var("succ"), Var("a"))), Blk(App(App(Var("+"), Var("b")), Blk(Var("c"))))) //│ res: int :p @@ -40,7 +40,7 @@ succ / a + c //│ Parsed: succ(...(+(...a)(...{+(...b)(...{c})}))); //│ Desugared: succ(...(+(...a)(...{+(...b)(...{c})}))) -//│ AST: App(Var(succ), App(App(Var(+), Var(a)), Blk(...))) +//│ AST: App(Var("succ"), App(App(Var("+"), Var("a")), Blk(App(App(Var("+"), Var("b")), Blk(Var("c")))))) //│ res: int :p @@ -55,11 +55,11 @@ a + d //│ Parsed: +(...a)(...b); +(...(+(...a)(...b)))(...c); +(...(+(...(+(...a)(...b)))(...c)))(...d); //│ Desugared: +(...a)(...b) -//│ AST: App(App(Var(+), Var(a)), Var(b)) +//│ AST: App(App(Var("+"), Var("a")), Var("b")) //│ Desugared: +(...(+(...a)(...b)))(...c) -//│ AST: App(App(Var(+), App(App(Var(+), Var(a)), Var(b))), Var(c)) +//│ AST: App(App(Var("+"), App(App(Var("+"), Var("a")), Var("b"))), Var("c")) //│ Desugared: +(...(+(...(+(...a)(...b)))(...c)))(...d) -//│ AST: App(App(Var(+), App(App(Var(+), App(App(Var(+), Var(a)), Var(b))), Var(c))), Var(d)) +//│ AST: App(App(Var("+"), App(App(Var("+"), App(App(Var("+"), Var("a")), Var("b"))), Var("c"))), Var("d")) //│ res: int //│ res: int //│ res: int @@ -74,7 +74,7 @@ a + d //│ Parsed: +(...(+(...(+(...a)(...b)))(...(+(...(+(...c)(...1)))(...(+(...2)(...3)))))))(...d); //│ Desugared: +(...(+(...(+(...a)(...b)))(...(+(...(+(...c)(...1)))(...(+(...2)(...3)))))))(...d) -//│ AST: App(App(Var(+), App(App(Var(+), App(App(Var(+), Var(a)), Var(b))), App(App(Var(+), App(App(Var(+), Var(c)), IntLit(1))), App(App(Var(+), IntLit(2)), IntLit(3))))), Var(d)) +//│ AST: App(App(Var("+"), App(App(Var("+"), App(App(Var("+"), Var("a")), Var("b"))), App(App(Var("+"), App(App(Var("+"), Var("c")), IntLit(1))), App(App(Var("+"), IntLit(2)), IntLit(3))))), Var("d")) //│ res: int :pe @@ -112,7 +112,7 @@ succ + c //│ Parsed: +(...(succ(...{+(...a)(...b)})))(...c); //│ Desugared: +(...(succ(...{+(...a)(...b)})))(...c) -//│ AST: App(App(Var(+), App(Var(succ), Blk(...))), Var(c)) +//│ AST: App(App(Var("+"), App(Var("succ"), Blk(App(App(Var("+"), Var("a")), Var("b"))))), Var("c")) //│ res: int // Maybe allow this as it lets us nicely align the operands? diff --git a/shared/src/test/diff/basics/Slashes.fun b/shared/src/test/diff/basics/Slashes.fun index ef7aef70..09db214d 100644 --- a/shared/src/test/diff/basics/Slashes.fun +++ b/shared/src/test/diff/basics/Slashes.fun @@ -23,7 +23,7 @@ x => succ / succ / x + 1 foo / x => succ / succ / x //│ Parsed: foo(...((...x) => succ(...(succ(...x))))); //│ Desugared: foo(...((...x) => succ(...(succ(...x))))) -//│ AST: App(Var(foo), Lam(Var(x), App(Var(succ), App(Var(succ), Var(x))))) +//│ AST: App(Var("foo"), Lam(Var("x"), App(Var("succ"), App(Var("succ"), Var("x"))))) //│ res: int :e diff --git a/shared/src/test/diff/mlscript/Basics.mls b/shared/src/test/diff/mlscript/Basics.mls index f6ec00c7..2fb3e1b4 100644 --- a/shared/src/test/diff/mlscript/Basics.mls +++ b/shared/src/test/diff/mlscript/Basics.mls @@ -96,7 +96,7 @@ def f (x y z) = add x y //│ ╙── ^^^^^ //│ f: error -> int //│ Code generation encountered an error: -//│ term App(App(Var(x), Tup((N, Var(y)))), Tup((N, Var(z)))) is not a valid pattern +//│ term App(App(Var("x"), Tup((N, Var("y")))), Tup((N, Var("z")))) is not a valid pattern f 1 //│ res: int diff --git a/shared/src/test/diff/mlscript/ByNameByValue.mls b/shared/src/test/diff/mlscript/ByNameByValue.mls index be234e2c..5d481618 100644 --- a/shared/src/test/diff/mlscript/ByNameByValue.mls +++ b/shared/src/test/diff/mlscript/ByNameByValue.mls @@ -16,7 +16,7 @@ def incr x = x.a <- x.a + 1 def gensym = let n = { mut a = 0 } in fun () -> (incr n, n) //│ Parsed: def gensym: let n = {mut a: 0} in () => [incr(n,), n,]; //│ Desugared: def gensym: let n = {mut a: 0} in () => [incr(n,), n,] -//│ AST: Def(false, gensym, Let(false, n, Rcd(Var(a) = IntLit(0)), Lam(Tup(), Tup((N, App(Var(incr), Tup((N, Var(n))))), (N, Var(n))))), true) +//│ AST: Def(false, Var("gensym"), L(Let(false, n, Rcd(Var("a") = IntLit(0)), Lam(Tup(), Tup((N, App(Var("incr"), Tup((N, Var("n"))))), (N, Var("n")))))), true) //│ // Query 1 //│ globalThis.gensym = function gensym() { //│ return (((n) => () => [ @@ -35,7 +35,7 @@ def gensym = let n = { mut a = 0 } in fun () -> (incr n, n) gensym1 = let n = { mut a = 0 } in fun () -> (incr n, n) //│ Parsed: let gensym1 = let n = {mut a: 0} in () => [incr(n,), n,]; //│ Desugared: def gensym1: let n = {mut a: 0} in () => [incr(n,), n,] -//│ AST: Def(false, gensym1, Let(false, n, Rcd(Var(a) = IntLit(0)), Lam(Tup(), Tup((N, App(Var(incr), Tup((N, Var(n))))), (N, Var(n))))), false) +//│ AST: Def(false, Var("gensym1"), L(Let(false, n, Rcd(Var("a") = IntLit(0)), Lam(Tup(), Tup((N, App(Var("incr"), Tup((N, Var("n"))))), (N, Var("n")))))), false) //│ // Query 1 //│ globalThis.gensym1 = ((n) => () => [ //│ incr(n), diff --git a/shared/src/test/diff/mlscript/MultiArgs.mls b/shared/src/test/diff/mlscript/MultiArgs.mls index 0c5f8e7d..738d3567 100644 --- a/shared/src/test/diff/mlscript/MultiArgs.mls +++ b/shared/src/test/diff/mlscript/MultiArgs.mls @@ -75,9 +75,9 @@ f = fun (x, y) -> add x y f(1, 2) //│ Parsed: let f = (x, y,) => add(x,)(y,); f(1, 2,); //│ Desugared: def f: (x, y,) => add(x,)(y,) -//│ AST: Def(false, f, Lam(Tup((N, Var(x)), (N, Var(y))), App(App(Var(add), Tup((N, Var(x)))), Tup((N, Var(y))))), false) +//│ AST: Def(false, Var("f"), L(Lam(Tup((N, Var("x")), (N, Var("y"))), App(App(Var("add"), Tup((N, Var("x")))), Tup((N, Var("y")))))), false) //│ Desugared: f(1, 2,) -//│ AST: App(Var(f), Tup((N, IntLit(1)), (N, IntLit(2)))) +//│ AST: App(Var("f"), Tup((N, IntLit(1)), (N, IntLit(2)))) //│ f: (int, int,) -> int //│ = [Function: f] //│ res: int @@ -119,9 +119,9 @@ f = fun ((x, y)) -> add x y f((1, 2)) //│ Parsed: let f = ('(' [x, y,] ')',) => add(x,)(y,); f('(' [1, 2,] ')',); //│ Desugared: def f: ('(' [x, y,] ')',) => add(x,)(y,) -//│ AST: Def(false, f, Lam(Tup((N, Bra(rcd = false, Tup((N, Var(x)), (N, Var(y)))))), App(App(Var(add), Tup((N, Var(x)))), Tup((N, Var(y))))), false) +//│ AST: Def(false, Var("f"), L(Lam(Tup((N, Bra(rcd = false, Tup((N, Var("x")), (N, Var("y")))))), App(App(Var("add"), Tup((N, Var("x")))), Tup((N, Var("y")))))), false) //│ Desugared: f('(' [1, 2,] ')',) -//│ AST: App(Var(f), Tup((N, Bra(rcd = false, Tup((N, IntLit(1)), (N, IntLit(2))))))) +//│ AST: App(Var("f"), Tup((N, Bra(rcd = false, Tup((N, IntLit(1)), (N, IntLit(2))))))) //│ f: ((int, int,),) -> int //│ = [Function: f1] //│ res: int diff --git a/shared/src/test/diff/mlscript/Mut.mls b/shared/src/test/diff/mlscript/Mut.mls index 5abffdda..20bb9fc2 100644 --- a/shared/src/test/diff/mlscript/Mut.mls +++ b/shared/src/test/diff/mlscript/Mut.mls @@ -613,7 +613,7 @@ b1.x <- 1 + 2 <- 4 //│ ║ l.550: b1.x <- 1 + 2 <- 4 //│ ╙── ^ //│ Code generation encountered an error: -//│ illegal assignemnt left-hand side: Bra(rcd = false, Assign(Sel(Var(mt1), 0), Sel(Var(b1), t))) +//│ illegal assignemnt left-hand side: Bra(rcd = false, Assign(Sel(Var("mt1"), 0), Sel(Var("b1"), t))) def f : {mut 0 : int} -> int -> unit def g : (mut int, bool) -> int -> unit diff --git a/shared/src/test/diff/mlscript/Ops.mls b/shared/src/test/diff/mlscript/Ops.mls index b278598e..f0ea6554 100644 --- a/shared/src/test/diff/mlscript/Ops.mls +++ b/shared/src/test/diff/mlscript/Ops.mls @@ -3,7 +3,7 @@ 2 + 2 //│ Parsed: +(2,)(2,); //│ Desugared: +(2,)(2,) -//│ AST: App(App(Var(+), Tup((N, IntLit(2)))), Tup((N, IntLit(2)))) +//│ AST: App(App(Var("+"), Tup((N, IntLit(2)))), Tup((N, IntLit(2)))) //│ res: int //│ = 4 @@ -11,7 +11,7 @@ 1 + 2 * 2 + 3 //│ Parsed: +(+(1,)(*(2,)(2,),),)(3,); //│ Desugared: +(+(1,)(*(2,)(2,),),)(3,) -//│ AST: App(App(Var(+), Tup((N, App(App(Var(+), Tup((N, IntLit(1)))), Tup((N, App(App(Var(*), Tup((N, IntLit(2)))), Tup((N, IntLit(2)))))))))), Tup((N, IntLit(3)))) +//│ AST: App(App(Var("+"), Tup((N, App(App(Var("+"), Tup((N, IntLit(1)))), Tup((N, App(App(Var("*"), Tup((N, IntLit(2)))), Tup((N, IntLit(2)))))))))), Tup((N, IntLit(3)))) //│ res: int //│ = 8 @@ -20,7 +20,7 @@ 1 + 2 / 2 + 3 //│ Parsed: +(+(1,)(/(2,)(2,),),)(3,); //│ Desugared: +(+(1,)(/(2,)(2,),),)(3,) -//│ AST: App(App(Var(+), Tup((N, App(App(Var(+), Tup((N, IntLit(1)))), Tup((N, App(App(Var(/), Tup((N, IntLit(2)))), Tup((N, IntLit(2)))))))))), Tup((N, IntLit(3)))) +//│ AST: App(App(Var("+"), Tup((N, App(App(Var("+"), Tup((N, IntLit(1)))), Tup((N, App(App(Var("/"), Tup((N, IntLit(2)))), Tup((N, IntLit(2)))))))))), Tup((N, IntLit(3)))) //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.20: 1 + 2 / 2 + 3 //│ ║ ^^^^^^^^^ @@ -36,7 +36,7 @@ 1 |> 2 || 3 //│ Parsed: ||(|>(1,)(2,),)(3,); //│ Desugared: ||(|>(1,)(2,),)(3,) -//│ AST: App(App(Var(||), Tup((N, App(App(Var(|>), Tup((N, IntLit(1)))), Tup((N, IntLit(2))))))), Tup((N, IntLit(3)))) +//│ AST: App(App(Var("||"), Tup((N, App(App(Var("|>"), Tup((N, IntLit(1)))), Tup((N, IntLit(2))))))), Tup((N, IntLit(3)))) //│ ╔══[ERROR] identifier not found: |> //│ ║ l.36: 1 |> 2 || 3 //│ ╙── ^^ @@ -54,7 +54,7 @@ true || false && true || false //│ Parsed: ||(||(true,)(&&(false,)(true,),),)(false,); //│ Desugared: ||(||(true,)(&&(false,)(true,),),)(false,) -//│ AST: App(App(Var(||), Tup((N, App(App(Var(||), Tup((N, Var(true)))), Tup((N, App(App(Var(&&), Tup((N, Var(false)))), Tup((N, Var(true)))))))))), Tup((N, Var(false)))) +//│ AST: App(App(Var("||"), Tup((N, App(App(Var("||"), Tup((N, Var("true")))), Tup((N, App(App(Var("&&"), Tup((N, Var("false")))), Tup((N, Var("true")))))))))), Tup((N, Var("false")))) //│ res: bool //│ = true diff --git a/shared/src/test/diff/mlscript/Weird.mls b/shared/src/test/diff/mlscript/Weird.mls index 9aa518f7..ffc7a116 100644 --- a/shared/src/test/diff/mlscript/Weird.mls +++ b/shared/src/test/diff/mlscript/Weird.mls @@ -41,7 +41,7 @@ class C def n: C{} //│ Parsed: rec def n: C; {}; //│ Desugared: rec def n: C -//│ AST: Def(true, n, PolyType(List(),TypeName(C)), true) +//│ AST: Def(true, Var("n"), R(PolyType(Ls(), TypeName("C"))), true) //│ Desugared: {} //│ AST: Rcd() //│ n: C diff --git a/shared/src/test/diff/nu/AbstractClasses.mls b/shared/src/test/diff/nu/AbstractClasses.mls index dd80a049..c00679e1 100644 --- a/shared/src/test/diff/nu/AbstractClasses.mls +++ b/shared/src/test/diff/nu/AbstractClasses.mls @@ -51,7 +51,7 @@ new Foo(1) { fun f = id } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ error //│ Code generation encountered an error: -//│ cannot generate code for term Rft(App(NuNew(Var(Foo)), Tup((N, IntLit(1)))), ...) +//│ cannot generate code for term Rft(App(NuNew(Var("Foo")), Tup((N, IntLit(1)))), TypingUnit(NuFunDef(None, f, N, Nil, L(Var("id"))))) abstract class Bar extends Foo(1) diff --git a/shared/src/test/diff/nu/BadBlocks.mls b/shared/src/test/diff/nu/BadBlocks.mls index eedcd08d..03fb69cc 100644 --- a/shared/src/test/diff/nu/BadBlocks.mls +++ b/shared/src/test/diff/nu/BadBlocks.mls @@ -176,5 +176,5 @@ fun test = (new { //│ ╙── ^ //│ fun test: error //│ Code generation encountered an error: -//│ Unsupported `new` class term: Bra(rcd = true, Rcd(Var(res) = Var(res))) +//│ Unsupported `new` class term: Bra(rcd = true, Rcd(Var("res") = Var("res"))) diff --git a/shared/src/test/diff/nu/LamPatterns.mls b/shared/src/test/diff/nu/LamPatterns.mls index 38092d8d..98bb1511 100644 --- a/shared/src/test/diff/nu/LamPatterns.mls +++ b/shared/src/test/diff/nu/LamPatterns.mls @@ -14,7 +14,7 @@ Some(x) => x //│ ╙── ^ //│ error -> error //│ Code generation encountered an error: -//│ term App(Var(Some), Tup((N, Var(x)))) is not a valid pattern +//│ term App(Var("Some"), Tup((N, Var("x")))) is not a valid pattern :js // FIXME type diff --git a/shared/src/test/diff/nu/LetRec.mls b/shared/src/test/diff/nu/LetRec.mls index 74f8a3f5..51ad7c37 100644 --- a/shared/src/test/diff/nu/LetRec.mls +++ b/shared/src/test/diff/nu/LetRec.mls @@ -129,7 +129,7 @@ let rec lol = () => lol :p let rec f = 1 //│ |#let| |#rec| |f| |#=| |1| -//│ AST: TypingUnit(NuFunDef(Some(true), f, None, [], IntLit(1))) +//│ AST: TypingUnit(NuFunDef(Some(true), f, N, Nil, L(IntLit(1)))) //│ Parsed: let rec f = 1; //│ let rec f: 1 //│ f diff --git a/shared/src/test/diff/nu/NewNew.mls b/shared/src/test/diff/nu/NewNew.mls index 8ab334a6..86753b6e 100644 --- a/shared/src/test/diff/nu/NewNew.mls +++ b/shared/src/test/diff/nu/NewNew.mls @@ -167,7 +167,7 @@ new PoInt['_] //│ ╙── ^ //│ PoInt[nothing] | error //│ Code generation encountered an error: -//│ Unsupported `new` class term: TyApp(Var(PoInt), List('_)) +//│ Unsupported `new` class term: TyApp(Var("PoInt"), List('_)) :e new PoInt[Str](0, 0) @@ -176,7 +176,7 @@ new PoInt[Str](0, 0) //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ PoInt[0] //│ Code generation encountered an error: -//│ Unsupported `new` class term: TyApp(Var(PoInt), List(TypeName(Str))) +//│ Unsupported `new` class term: TyApp(Var("PoInt"), List(TypeName(Str))) type T = PoInt[Str] //│ type T = PoInt[Str] @@ -211,7 +211,7 @@ let origin = new PoInt[Int](0, 0) //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ let origin: PoInt[0] //│ Code generation encountered an error: -//│ Unsupported `new` class term: TyApp(Var(PoInt), List(TypeName(Int))) +//│ Unsupported `new` class term: TyApp(Var("PoInt"), List(TypeName(Int))) :e // TODO support @@ -247,7 +247,7 @@ new //│ ╙── ^ //│ error //│ Code generation encountered an error: -//│ Unsupported `new` class term: Blk(...) +//│ Unsupported `new` class term: Blk(Asc(Var("x"), Literal(0))) @@ -302,6 +302,6 @@ module A { class B } new A.B //│ B //│ Code generation encountered an error: -//│ Unsupported `new` class term: Sel(Var(A), B) +//│ Unsupported `new` class term: Sel(Var("A"), B) diff --git a/shared/src/test/diff/nu/OpLam.mls b/shared/src/test/diff/nu/OpLam.mls index 15b57dbc..ec57b8f8 100644 --- a/shared/src/test/diff/nu/OpLam.mls +++ b/shared/src/test/diff/nu/OpLam.mls @@ -107,6 +107,6 @@ x => x.y => y //│ ╙── ^ //│ anything -> error -> error //│ Code generation encountered an error: -//│ term Sel(Var(x), y) is not a valid pattern +//│ term Sel(Var("x"), y) is not a valid pattern diff --git a/shared/src/test/diff/nu/OverrideShorthand.mls b/shared/src/test/diff/nu/OverrideShorthand.mls index b6273930..8383f5a7 100644 --- a/shared/src/test/diff/nu/OverrideShorthand.mls +++ b/shared/src/test/diff/nu/OverrideShorthand.mls @@ -10,7 +10,7 @@ class Pair(lhs: Int, rhs: Int) :e fun f(override Pair(x, y)) = x + y //│ |#fun| |f|(|#override| |Pair|(|x|,| |y|)|)| |#=| |x| |+| |y| -//│ AST: TypingUnit(NuFunDef(None, f, None, [], Lam(Tup((N, Var(_$0))), If(IfOpApp(Var(_$0), Var(is), IfThen(App(Var(Pair), Tup((N, Var(x)), (N, Var(y)))), App(Var(+), Tup((N, Var(x)), (N, Var(y)))), Some(App(Sel(Super(), f), Tup((N, Var(_$0))))))))) +//│ AST: TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Var("_$0"))), If(IfOpApp(Var("_$0"), Var("is"), IfThen(App(Var("Pair"), Tup((N, Var("x")), (N, Var("y")))), App(Var("+"), Tup((N, Var("x")), (N, Var("y")))), Some(App(Sel(Super(), f), Tup((N, Var("_$0")))))))))) //│ Parsed: fun f = (_$0,) => if _$0 is (Pair(x, y,)) then +(x, y,) else (super).f(_$0,); //│ ╔══[ERROR] identifier not found: super //│ ║ l.11: fun f(override Pair(x, y)) = x + y @@ -46,7 +46,7 @@ fun f(override Pair(x, y), z) = x + y //│ ╙── ^ //│ fun f: (error, anything) -> Int //│ Code generation encountered an error: -//│ term App(Var(Pair), Tup((N, Var(x)), (N, Var(y)))) is not a valid pattern +//│ term App(Var("Pair"), Tup((N, Var("x")), (N, Var("y")))) is not a valid pattern // TODO diff --git a/shared/src/test/diff/nu/RightAssocOps.mls b/shared/src/test/diff/nu/RightAssocOps.mls index 46187337..df596915 100644 --- a/shared/src/test/diff/nu/RightAssocOps.mls +++ b/shared/src/test/diff/nu/RightAssocOps.mls @@ -30,7 +30,7 @@ fun (++) conc(xs, ys) = [xs, ys] :p 1 +: "a" ++ "b" :+ 2 //│ |1| |+:| |"a"| |++| |"b"| |:+| |2| -//│ AST: TypingUnit(App(Var(+:), Tup((N, IntLit(1)), (N, App(Var(:+), Tup((N, App(Var(++), Tup((N, StrLit(a)), (N, StrLit(b))))), (N, IntLit(2)))))))) +//│ AST: TypingUnit(App(Var("+:"), Tup((N, IntLit(1)), (N, App(Var(":+"), Tup((N, App(Var("++"), Tup((N, StrLit(a)), (N, StrLit(b))))), (N, IntLit(2)))))))) //│ Parsed: +:(1, :+(++("a", "b",), 2,),); //│ [[Int], [["a", "b"], [Int]]] //│ res @@ -39,7 +39,7 @@ fun (++) conc(xs, ys) = [xs, ys] :p 1 +: "a" :+ 2 ++ "b" //│ |1| |+:| |"a"| |:+| |2| |++| |"b"| -//│ AST: TypingUnit(App(Var(+:), Tup((N, IntLit(1)), (N, App(Var(++), Tup((N, App(Var(:+), Tup((N, StrLit(a)), (N, IntLit(2))))), (N, StrLit(b)))))))) +//│ AST: TypingUnit(App(Var("+:"), Tup((N, IntLit(1)), (N, App(Var("++"), Tup((N, App(Var(":+"), Tup((N, StrLit(a)), (N, IntLit(2))))), (N, StrLit(b)))))))) //│ Parsed: +:(1, ++(:+("a", 2,), "b",),); //│ [[Int], [["a", [Int]], "b"]] //│ res @@ -49,7 +49,7 @@ fun (++) conc(xs, ys) = [xs, ys] :e 1 +: "a" ++ 2 +: "b" //│ |1| |+:| |"a"| |++| |2| |+:| |"b"| -//│ AST: TypingUnit(App(Var(+:), Tup((N, IntLit(1)), (N, App(Var(+:), Tup((N, App(Var(++), Tup((N, StrLit(a)), (N, IntLit(2))))), (N, StrLit(b)))))))) +//│ AST: TypingUnit(App(Var("+:"), Tup((N, IntLit(1)), (N, App(Var("+:"), Tup((N, App(Var("++"), Tup((N, StrLit(a)), (N, IntLit(2))))), (N, StrLit(b)))))))) //│ Parsed: +:(1, +:(++("a", 2,), "b",),); //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.50: 1 +: "a" ++ 2 +: "b" diff --git a/shared/src/test/diff/nu/TODO_Classes.mls b/shared/src/test/diff/nu/TODO_Classes.mls index bc37f00a..c3cef2e7 100644 --- a/shared/src/test/diff/nu/TODO_Classes.mls +++ b/shared/src/test/diff/nu/TODO_Classes.mls @@ -80,7 +80,7 @@ new C { val x = 1 } //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ error //│ Code generation encountered an error: -//│ cannot generate code for term Rft(NuNew(Var(C)), ...) +//│ cannot generate code for term Rft(NuNew(Var("C")), TypingUnit(NuFunDef(Some(false), x, N, Nil, L(IntLit(1))))) diff --git a/shared/src/test/diff/ucs/LeadingAnd.mls b/shared/src/test/diff/ucs/LeadingAnd.mls index ae365f48..f1e65d9f 100644 --- a/shared/src/test/diff/ucs/LeadingAnd.mls +++ b/shared/src/test/diff/ucs/LeadingAnd.mls @@ -23,7 +23,7 @@ fun f(a, b) = if a is Some(av) and b is Some(bv) then av + bv //│ |#fun| |f|(|a|,| |b|)| |#=| |#if| |a| |is| |Some|(|av|)|→|and| |b| |is| |Some|(|bv|)|↵|#then| |av| |+| |bv|←| -//│ AST: TypingUnit(NuFunDef(None, f, None, [], Lam(Tup((N, Var(a)), (N, Var(b))), If(IfOpApp(Var(a), Var(is), I; f; O; p; s; A; p; p; (; A; p; p; (; V; a; r; (; S; o; m; e; ); ,; ; T; u; p; (; (; N; ,; ; V; a; r; (; a; v; ); ); ); ); ,; ; <; i; t; e; r; a; t; o; r; >, None)))) +//│ AST: TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Var("a")), (N, Var("b"))), If(IfOpApp(Var("a"), Var("is"), I; f; O; p; s; A; p; p; (; A; p; p; (; V; a; r; (; "; S; o; m; e; "; ); ,; ; T; u; p; (; (; N; ,; ; V; a; r; (; "; a; v; "; ); ); ); ); ,; ; <; i; t; e; r; a; t; o; r; >, None))))) //│ Parsed: fun f = (a, b,) => if a is Some(av,) ‹· and (is(b, Some(bv,),)) then +(av, bv,)›; //│ fun f: (Some[Int], Some[Int]) -> Int @@ -34,7 +34,7 @@ fun f(a, b) = if a is and b is Some(bv) then av + bv //│ |#fun| |f|(|a|,| |b|)| |#=| |#if| |a| |is|→|Some|(|av|)|→|and| |b| |is| |Some|(|bv|)|↵|#then| |av| |+| |bv|←|←| -//│ AST: TypingUnit(NuFunDef(None, f, None, [], Lam(Tup((N, Var(a)), (N, Var(b))), If(IfOpApp(Var(a), Var(is), IfBlock(I; f; O; p; s; A; p; p; (; A; p; p; (; V; a; r; (; S; o; m; e; ); ,; ; T; u; p; (; (; N; ,; ; V; a; r; (; a; v; ); ); ); ); ,; ; <; i; t; e; r; a; t; o; r; >), None)))) +//│ AST: TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Var("a")), (N, Var("b"))), If(IfOpApp(Var("a"), Var("is"), IfBlock(Ls(I; f; O; p; s; A; p; p; (; A; p; p; (; V; a; r; (; "; S; o; m; e; "; ); ,; ; T; u; p; (; (; N; ,; ; V; a; r; (; "; a; v; "; ); ); ); ); ,; ; <; i; t; e; r; a; t; o; r; >)), None))))) //│ Parsed: fun f = (a, b,) => if a is ‹Some(av,) ‹· and (is(b, Some(bv,),)) then +(av, bv,)››; //│ ╔══[ERROR] Illegal pattern `and` //│ ║ l.34: and b is Some(bv) diff --git a/shared/src/test/diff/ucs/SplitOps.mls b/shared/src/test/diff/ucs/SplitOps.mls index 7171667b..70e9e053 100644 --- a/shared/src/test/diff/ucs/SplitOps.mls +++ b/shared/src/test/diff/ucs/SplitOps.mls @@ -93,10 +93,10 @@ fun f(a, b, c) = if a == 0 and b is B() and c is C() then 0 //│ |#fun| |f|(|a|,| |b|,| |c|)| |#=|→|#if| |a|→|==| |0| |and| |b| |is| |B|(||)| |and| |c| |is| |C|(||)| |#then| |0|←|←| -//│ AST: TypingUnit(NuFunDef(None, f, None, [], Lam(Tup((N, Var(a)), (N, Var(b)), (N, Var(c))), Blk(...)))) +//│ AST: TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Var("a")), (N, Var("b")), (N, Var("c"))), Blk(If(I; f; O; p; s; A; p; p; (; V; a; r; (; "; a; "; ); ,; ; <; i; t; e; r; a; t; o; r; >, None)))))) //│ Parsed: fun f = (a, b, c,) => {if a ‹· == (and(and(0,)(is(b,)(B(),),),)(is(c,)(C(),),)) then 0›}; //│ Desugared: rec def f: (a, b, c,) => {if a ‹· == (and(and(0,)(is(b,)(B(),),),)(is(c,)(C(),),)) then 0›} -//│ AST: Def(true, f, Lam(Tup((N, Var(a)), (N, Var(b)), (N, Var(c))), Blk(...)), true) +//│ AST: Def(true, Var("f"), L(Lam(Tup((N, Var("a")), (N, Var("b")), (N, Var("c"))), Blk(If(I; f; O; p; s; A; p; p; (; V; a; r; (; "; a; "; ); ,; ; <; i; t; e; r; a; t; o; r; >, None)))), true) //│ ╔══[ERROR] The case when this is false is not handled: ==(a,)(0,) //│ ║ l.93: if a //│ ║ ^ diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index 73e235e5..d8a2cf98 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -437,7 +437,7 @@ class DiffTests output("Parsed: " + res.showDbg) if (mode.showParse) - output("AST: " + mlscript.codegen.Helpers.inspect(res)) + output("AST: " + inspect.deep(res)) postProcess(mode, basePath, testName, res).foreach(output) @@ -637,7 +637,7 @@ class DiffTests typeDefs.foreach(td => output("Desugared: " + td)) stmts.foreach { s => output("Desugared: " + s) - output("AST: " + mlscript.codegen.Helpers.inspect(s)) + output("AST: " + inspect.deep(s)) } } From 146cebae75f4b0dc87d76b77bf0c27403dc19eda Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 20 Dec 2023 01:25:31 +0800 Subject: [PATCH 012/143] Fix warnings on inexhaustiveness pattern matching --- shared/src/main/scala/mlscript/pretyper/PreTyper.scala | 5 +++++ shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala | 1 + 2 files changed, 6 insertions(+) diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index e4e4cfee..0b0459ec 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -103,6 +103,11 @@ class PreTyper(override val debugLevel: Opt[Int], useNewDefs: Bool) extends Trac case v: Var => traverseVar(v) case AdtMatchWith(cond, arms) => ??? // TODO: How? case Inst(body) => traverseTerm(body) + case NuNew(cls) => traverseTerm(cls) + case Rft(base, decls) => // For object refinement + traverseTerm(base) + traverseTypingUnit(decls, "Rft", scope) + () } }(_ => s"traverseTerm ==> ${shortName(term)}") diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 107535e6..b8c1d988 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -97,6 +97,7 @@ trait Desugaring { self: mlscript.pretyper.Traceable => mkBinOp(nme, Var("=="), pattern.literal, true), c.Branch(scrutinee, truePattern, next) :: c.Split.Nil ) + case (Some((nme, pattern)), next) => ??? // Other patterns are not implemented yet. }) } From cd4770c2b54a6d1b03f4b1a6e3794a119c7b29eb Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Thu, 21 Dec 2023 01:49:07 +0800 Subject: [PATCH 013/143] Improve coverage checking and fix a fatal bug in post-processing --- shared/src/main/scala/mlscript/helpers.scala | 7 +- .../scala/mlscript/pretyper/DataTypes.scala | 10 + .../scala/mlscript/pretyper/PreTyper.scala | 185 +++++++++--- .../main/scala/mlscript/pretyper/Scope.scala | 114 ++++++-- .../main/scala/mlscript/pretyper/Symbol.scala | 180 ++++++++---- .../scala/mlscript/pretyper/package.scala | 11 +- .../main/scala/mlscript/ucs/DesugarUCS.scala | 50 +++- .../ucs/stages/CoverageChecking.scala | 276 +++++++++++++----- .../mlscript/ucs/stages/Desugaring.scala | 2 +- .../mlscript/ucs/stages/Normalization.scala | 72 +++-- .../mlscript/ucs/stages/PostProcessing.scala | 171 +++++++---- .../mlscript/ucs/stages/Transformation.scala | 6 + .../scala/mlscript/ucs/stages/package.scala | 18 +- .../main/scala/mlscript/utils/inspect.scala | 2 +- shared/src/test/diff/mlscript/Repro.mls | 16 - .../src/test/diff/pretyper/Declarations.mls | 74 +---- shared/src/test/diff/pretyper/Repro.mls | 1 + .../src/test/diff/pretyper/ucs/DualOption.mls | 12 +- .../src/test/diff/pretyper/ucs/Overlaps.mls | 76 +++++ .../pretyper/ucs/coverage/MissingCases.mls | 146 +++++++-- .../pretyper/ucs/coverage/SealedClasses.mls | 58 ++++ .../pretyper/ucs/coverage/Unreachable.mls | 142 --------- .../pretyper/ucs/examples/EitherOrBoth.mls | 100 +++++++ .../diff/pretyper/ucs/examples/Option.mls | 65 ++--- 24 files changed, 1178 insertions(+), 616 deletions(-) create mode 100644 shared/src/main/scala/mlscript/pretyper/DataTypes.scala create mode 100644 shared/src/test/diff/pretyper/Repro.mls create mode 100644 shared/src/test/diff/pretyper/ucs/Overlaps.mls create mode 100644 shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls create mode 100644 shared/src/test/diff/pretyper/ucs/examples/EitherOrBoth.mls diff --git a/shared/src/main/scala/mlscript/helpers.scala b/shared/src/main/scala/mlscript/helpers.scala index e9a60f89..9bd5d430 100644 --- a/shared/src/main/scala/mlscript/helpers.scala +++ b/shared/src/main/scala/mlscript/helpers.scala @@ -502,7 +502,7 @@ trait TypeNameImpl extends Ordered[TypeName] { self: TypeName => def targs: Ls[Type] = Nil def compare(that: TypeName): Int = this.name compare that.name lazy val toVar: Var = Var(name).withLocOf(this) - var symbol: Opt[pretyper.TypeSymbol] = N + var symbol: Opt[pretyper.symbol.TypeSymbol] = N } trait FldImpl extends Located { self: Fld => @@ -743,7 +743,7 @@ trait VarImpl { self: Var => var uid: Opt[Int] = N // PreTyper additions - import pretyper.{Symbol} + import pretyper.symbol.Symbol private var _symbol: Opt[Symbol] = N def symbolOption: Opt[Symbol] = _symbol @@ -753,9 +753,8 @@ trait VarImpl { self: Var => case N => _symbol = S(symbol) case S(_) => ??? } - // TODO: Remove this methods if they are useless. // def withSymbol: Var = { symbol = S(new ValueSymbol(this, false)); this } - // def withSymbol(s: TermSymbol): Var = { symbol = S(s); this } + def withSymbol(symbol: Symbol): Var = { this.symbol = symbol; this } } trait TupImpl { self: Tup => diff --git a/shared/src/main/scala/mlscript/pretyper/DataTypes.scala b/shared/src/main/scala/mlscript/pretyper/DataTypes.scala new file mode 100644 index 00000000..aaebd901 --- /dev/null +++ b/shared/src/main/scala/mlscript/pretyper/DataTypes.scala @@ -0,0 +1,10 @@ +package mlscript.pretyper + +import mlscript.{NuFunDef, NuTypeDef, Cls, Trt, Mxn, Als, Mod, Var, TypeName} +import collection.mutable.{Buffer, Map => MutMap, Set => MutSet} +import mlscript.utils._, shorthands._ +import scala.annotation.tailrec + +trait DataTypes { self: PreTyper => + +} \ No newline at end of file diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index 0b0459ec..eb3da508 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -1,9 +1,16 @@ package mlscript.pretyper +import collection.mutable.{Set => MutSet} import mlscript.ucs.DesugarUCS +import symbol._ import mlscript._, utils._, shorthands._ +import scala.annotation.tailrec +import mlscript.Message, Message.MessageContext class PreTyper(override val debugLevel: Opt[Int], useNewDefs: Bool) extends Traceable with DesugarUCS { + import PreTyper._ + + protected def raise(diagnostics: Diagnostic): Unit = () protected def raise(diagnostics: Ls[Diagnostic]): Unit = () private def extractParameters(fields: Term): Ls[ValueSymbol] = fields match { @@ -32,24 +39,28 @@ class PreTyper(override val debugLevel: Opt[Int], useNewDefs: Bool) extends Trac protected def resolveVar(v: Var)(implicit scope: Scope): Unit = trace(s"resolveVar(name = \"$v\")") { - scope.get(v.name) match { - case Some(sym: ValueSymbol) => + scope.getTermSymbol(v.name) match { + case S(sym: ValueSymbol) => println(s"Resolve variable $v to a value.", 2) v.symbol = sym - case Some(sym: SubValueSymbol) => + case S(sym: SubValueSymbol) => println(s"Resolve variable $v to a value.", 2) v.symbol = sym - case Some(sym: FunctionSymbol) => + case S(sym: FunctionSymbol) => println(s"Resolve variable $v to a function.", 2) v.symbol = sym - case Some(sym: TypeSymbol) => - if (sym.defn.kind == Cls) { - println(s"Resolve variable $v to a class.", 2) - v.symbol = sym - } else { - throw new Exception(s"Name $v refers to a type") + case N => + scope.getTypeSymbol(v.name) match { + case S(sym: ClassSymbol) => + if (sym.defn.kind == Cls) { + println(s"Resolve variable $v to a class.", 2) + v.symbol = sym + } else { + throw new Exception(s"Name $v refers to a type") + } + case S(_) => throw new Exception(s"Name $v refers to a type") + case N => throw new Exception(s"Variable $v not found in scope") } - case None => throw new Exception(s"Variable $v not found in scope") } }() @@ -57,16 +68,16 @@ class PreTyper(override val debugLevel: Opt[Int], useNewDefs: Bool) extends Trac trace(s"traverseVar(name = \"$v\")") { v.symbolOption match { case N => resolveVar(v) - case S(symbol) => scope.get(v.name) match { - case S(other) if other === symbol => () - case S(other) => throw new Exception(s"Variable $v refers to a different symbol") - case N => throw new Exception(s"Variable $v not found in scope. It is possibly a free variable.") + case S(symbol) => scope.getSymbols(v.name) match { + case Nil => throw new Exception(s"Variable $v not found in scope. It is possibly a free variable.") + case symbols if symbols.contains(symbol) => () + case _ => throw new Exception(s"Variable $v refers to a different symbol") } } }() protected def traverseTerm(term: Term)(implicit scope: Scope): Unit = - trace(s"traverseTerm <== ${shortName(term)}") { + trace(s"traverseTerm <== ${inspect.shallow(term)}") { term match { case Assign(lhs, rhs) => traverseTerm(lhs); traverseTerm(rhs) case Bra(_, trm) => traverseTerm(trm) @@ -108,22 +119,28 @@ class PreTyper(override val debugLevel: Opt[Int], useNewDefs: Bool) extends Trac traverseTerm(base) traverseTypingUnit(decls, "Rft", scope) () + case NuNew(cls) => traverseTerm(cls) + case Rft(base, decls) => + traverseTerm(base) + traverseTypingUnit(decls, "Rft", scope) + () } - }(_ => s"traverseTerm ==> ${shortName(term)}") + }(_ => s"traverseTerm ==> ${inspect.shallow(term)}") - private def traverseNuTypeDef(symbol: TypeSymbol, defn: NuTypeDef)(implicit scope: Scope): Unit = - trace(s"traverseNuTypeDef <== ${defn.kind} ${defn.nme.name}") { + private def traverseTypeDefinition(symbol: TypeSymbol, defn: NuTypeDef)(implicit scope: Scope): Unit = + trace(s"traverseTypeDefinition <== ${defn.describe}") { traverseTypingUnit(defn.body, defn.nme.name, scope) () - }(_ => s"traverseNuTypeDef <== ${defn.kind} ${defn.nme.name}") + }(_ => s"traverseTypeDefinition <== ${defn.describe}") private def traverseFunction(symbol: FunctionSymbol, defn: NuFunDef)(implicit scope: Scope): Unit = trace(s"traverseFunction <== ${defn.nme.name}") { + require(defn.isLetRec.isEmpty) // Make sure it's defined via `fun`. defn.rhs match { case Left(term) => val subScope = if (defn.isLetRec === S(false)) scope else scope + symbol traverseTerm(term)(subScope) - case Right(value) => () + case Right(ty) => println(s"found a pure declaration: $ty") } }(_ => s"traverseFunction ==> ${defn.nme.name}") @@ -141,17 +158,68 @@ class PreTyper(override val debugLevel: Opt[Int], useNewDefs: Bool) extends Trac case (acc, defn: NuTypeDef) => val `var` = Var(defn.nme.name).withLoc(defn.nme.toLoc) // Create a type symbol but do not visit its inner members - acc ++ (new TypeSymbol(defn.nme, defn) :: + val symbol = defn.kind match { + case Cls => new ClassSymbol(defn) + case Als => new TypeAliasSymbol(defn) + case Mxn => new MixinSymbol(defn) + case Trt => new TraitSymbol(defn) + case Mod => new ModuleSymbol(defn) + } + println("parent types: " + defn.parents.iterator.map(inspect.deep(_)).mkString("; ")) + acc ++ (symbol :: (defn.kind match { case Mod => new ValueSymbol(`var`, true) :: Nil case Als | Cls | Mxn | Trt => Nil })) - case (acc, defn: NuFunDef) if defn.isLetRec.isEmpty => - acc + new FunctionSymbol(defn.nme, defn) + case (acc, defn: NuFunDef) if defn.isLetRec.isEmpty => acc + new FunctionSymbol(defn) case (acc, _: NuFunDef) => acc case (acc, _: Constructor | _: DataDefn | _: DatatypeDefn | _: Def | _: LetS | _: TypeDef) => ??? // TODO: When? } - println(hoistedScope.symbols.map(_.name).mkString("1. scope = {", ", ", "}")) + // Resolve base types. + val subtypingRelations = typingUnit.entities.foldLeft(Map.empty[Var, Ls[Var]]) { + case (acc, defn: NuTypeDef) => + val thisType = Var(defn.nme.name).withLoc(defn.nme.toLoc) + val superTypes = extractSuperTypes(defn.parents) + acc + (thisType -> superTypes) + case (acc, _: Term | _: NuFunDef) => acc + case (acc, _: Constructor | _: DataDefn | _: DatatypeDefn | _: Def | _: LetS | _: TypeDef) => ??? // TODO: When? + } + val superTypes = transitiveClosure(subtypingRelations) + printGraph(subtypingRelations, println, "Subtyping relations", "->") + superTypes.foreachEntry { case (thisType, baseTypes) => + hoistedScope.getTypeSymbol(thisType.name) match { + case S(symbol) => symbol.baseTypes = + baseTypes.map(baseType => hoistedScope.getTypeSymbol(baseType.name).getOrElse(???)) + case N => ??? + } // FIXME: Generate proper diagnostics. + } + // Resolve sealed types. + println("Resolve sealed signature types") + hoistedScope.types.foreachEntry { + case _ -> (_: MixinSymbol | _: TypeAliasSymbol) => () + case (name, symbol) => symbol.defn.sig.foreach { unions => + def rec(acc: Ls[TypeName], ty: Type): Ls[TypeName] = ty match { + case tn: TypeName => tn :: acc + case AppliedType(tn: TypeName, _) => tn :: acc + case Union(lhs, tn: TypeName) => rec(tn :: acc, lhs) + case Union(lhs, AppliedType(tn: TypeName, _)) => rec(tn :: acc, lhs) + case other => println(s"Unknown type: $other"); ??? + } + val derivedTypes = try rec(Nil, unions) catch { case _: NotImplementedError => Nil } + symbol.sealedDerivedTypes = derivedTypes.flatMap { derivedType => + val maybeSymbol = hoistedScope.getTypeSymbol(derivedType.name) + if (maybeSymbol.isEmpty) raise(ErrorReport( + msg"Undefined type $derivedType" -> derivedType.toLoc :: Nil, + true, + Diagnostic.PreTyping + )) + maybeSymbol + } + println(s">>> $name: ${symbol.sealedDerivedTypes.iterator.map(_.name).mkString(", ")}") + } + } + println(hoistedScope.symbols.map(_.name).mkString("(hoisted) scope = {", ", ", "}")) + // // Pass 2: Visit non-hoisted and build a complete scope. val completeScope = typingUnit.entities.foldLeft[Scope](hoistedScope) { case (acc, term: Term) => traverseTerm(term)(acc); acc @@ -164,29 +232,74 @@ class PreTyper(override val debugLevel: Opt[Int], useNewDefs: Bool) extends Trac case (acc, _: NuFunDef) => acc case (acc, _: Constructor | _: DataDefn | _: DatatypeDefn | _: Def | _: LetS | _: TypeDef) => ??? // TODO: When? } - println(hoistedScope.symbols.map(_.name).mkString("2. scope = {", ", ", "}")) - import pretyper.TypeSymbol + println(hoistedScope.symbols.map(_.name).mkString("(complete) scope = {", ", ", "}")) // Pass 3: Visit hoisted symbols. + val visitedSymbol = MutSet.empty[FunctionSymbol] completeScope.symbols.foreach { case symbol: TypeSymbol => val innerScope = symbol.defn.kind match { case Cls => - completeScope.derive ++ (symbol.defn.params match { - case N => Nil - case S(fields) => extractParameters(fields) - }) + completeScope.derive( + new ValueSymbol(Var("this"), false) :: + (symbol.defn.params match { + case N => Nil + case S(fields) => extractParameters(fields) + }) + ) case Als | Mod | Mxn | Trt => completeScope } - traverseNuTypeDef(symbol, symbol.defn)(innerScope) - case symbol: FunctionSymbol => traverseFunction(symbol, symbol.defn)(completeScope) - case _: ValueSymbol => () - case _: SubValueSymbol => () + traverseTypeDefinition(symbol, symbol.defn)(innerScope) + case symbol: FunctionSymbol if !visitedSymbol(symbol) => + visitedSymbol += symbol + traverseFunction(symbol, symbol.defn)(completeScope) + case _: FunctionSymbol | _: ValueSymbol | _: SubValueSymbol => () } (completeScope, new TypeContents) - }({ case (scope, contents) => s"traverseTypingUnit ==> ${scope.showLocalSymbols}" }) + }({ case (scope, contents) => s"traverseTypingUnit ==> Scope {${scope.showLocalSymbols}}" }) def process(typingUnit: TypingUnit, scope: Scope, name: Str): (Scope, TypeContents) = trace(s"process <== $name: ${typingUnit.describe}") { traverseTypingUnit(typingUnit, name, scope) }({ case (scope, contents) => s"process ==> ${scope.showLocalSymbols}" }) } + +object PreTyper { + + def extractSuperTypes(parents: Ls[Term]): Ls[Var] = { + @tailrec + def rec(results: Ls[Var], waitlist: Ls[Term]): Ls[Var] = + waitlist match { + case Nil => results.reverse + case (nme: Var) :: tail => rec(nme :: results, tail) + case (TyApp(ty, _)) :: tail => rec(results, ty :: tail) + case other :: _ => ??? + } + rec(Nil, parents) + } + + def transitiveClosure(graph: Map[Var, List[Var]]): Map[Var, List[Var]] = { + def dfs(vertex: Var, visited: Set[Var]): Set[Var] = { + if (visited.contains(vertex)) visited + else graph.getOrElse(vertex, List()) + .foldLeft(visited + vertex)((acc, v) => dfs(v, acc)) + } + + graph.keys.map { vertex => + val closure = dfs(vertex, Set()) + vertex -> (closure - vertex).toList + }.toMap + } + + def printGraph(graph: Map[Var, List[Var]], print: (=> Any) => Unit, title: String, arrow: String): Unit = { + print(s"• $title") + if (graph.isEmpty) + print(" + ") + else + graph.foreachEntry { (source, targets) => + print(s" + $source $arrow " + { + if (targets.isEmpty) s"{}" + else targets.mkString("{ ", ", ", " }") + }) + } + } +} \ No newline at end of file diff --git a/shared/src/main/scala/mlscript/pretyper/Scope.scala b/shared/src/main/scala/mlscript/pretyper/Scope.scala index e47690a9..23e7113b 100644 --- a/shared/src/main/scala/mlscript/pretyper/Scope.scala +++ b/shared/src/main/scala/mlscript/pretyper/Scope.scala @@ -2,46 +2,102 @@ package mlscript.pretyper import collection.immutable.Map import mlscript.utils._, shorthands._ -import mlscript.Var +import mlscript.{Mod, NuTypeDef, TypeName, TypingUnit, Var} +import scala.annotation.tailrec +import symbol._ -class Scope(val enclosing: Opt[Scope], val entries: Map[String, Symbol]) { - @inline - def get(name: String): Opt[Symbol] = entries.get(name) match { - case Some(sym) => S(sym) - case None => enclosing.fold(N: Opt[Symbol])(_.get(name)) - } +final class Scope(val enclosing: Opt[Scope], val types: Map[Str, TypeSymbol], val terms: Map[Str, TermSymbol]) { + import Scope._ + + + @tailrec + final def getTypeSymbol(name: Str): Opt[TypeSymbol] = + types.get(name) match { + case entry @ S(_) => entry + case N => enclosing match { + case N => N + case S(scope) => scope.getTypeSymbol(name) + } + } + + @tailrec + final def getTermSymbol(name: Str): Opt[TermSymbol] = + terms.get(name) match { + case entry @ S(_) => entry + case N => enclosing match { + case N => N + case S(scope) => scope.getTermSymbol(name) + } + } + + final def getSymbols(name: Str): Ls[Symbol] = Ls.concat(getTypeSymbol(name), getTermSymbol(name)) @inline - def +(sym: Symbol): Scope = new Scope(S(this), entries + (sym.name -> sym)) + def +(sym: Symbol): Scope = sym match { + case symbol: TypeSymbol => new Scope(enclosing, types + (symbol.name -> symbol), terms) + case symbol @ FunctionSymbol(Var(name), S(Var(operator)), _) => + new Scope(enclosing, types, terms + (name -> symbol) + (operator -> symbol)) + case symbol: TermSymbol => new Scope(enclosing, types, terms + (symbol.name -> symbol)) + } @inline - def ++(syms: IterableOnce[Symbol]): Scope = - new Scope(S(this), entries ++ syms.iterator.map(sym => sym.name -> sym)) + def ++(symbols: IterableOnce[Symbol]): Scope = { + val (newTypes, newTerms) = partitionSymbols((types, terms), symbols) + new Scope(enclosing, newTypes, newTerms) + } - def withEntries(syms: IterableOnce[Var -> Symbol]): Scope = - new Scope(S(this), entries ++ syms.iterator.map { - case (nme, sym) => nme.name -> sym - }) + def withEntries(syms: IterableOnce[Var -> Symbol]): Scope = { + val (newTypes, newTerms) = syms.iterator.foldLeft((types, terms)) { + case ((types, terms), (nme, symbol: TypeSymbol)) => (types + (nme.name -> symbol), terms) + case ((types, terms), (nme, symbol: TermSymbol)) => (types, terms + (nme.name -> symbol)) + } + new Scope(enclosing, newTypes, newTerms) + } @inline - def symbols: Iterable[Symbol] = entries.values + def symbols: Iterator[Symbol] = Iterator.concat[Symbol](types.values, terms.values) + + def derive: Scope = new Scope(S(this), Map.empty, Map.empty) - def derive: Scope = new Scope(S(this), Map.empty) + def derive(symbols: IterableOnce[Symbol]): Scope = { + val (newTypes, newTerms) = partitionSymbols((types, terms), symbols) + new Scope(S(this), newTypes, newTerms) + } - def showLocalSymbols: Str = entries.iterator.map(_._1).mkString(", ") + def showLocalSymbols: Str = symbols.iterator.map(_.name).mkString(", ") } object Scope { - def from(symbols: IterableOnce[Symbol]): Scope = - new Scope(N, Map.from(symbols.iterator.map(sym => sym.name -> sym))) - - val global: Scope = Scope.from( - """true,false,document,window,typeof,toString,not,succ,log,discard,negate, - |round,add,sub,mul,div,sqrt,lt,le,gt,ge,slt,sle,sgt,sge,length,concat,eq, - |ne,error,id,if,emptyArray,+,-,*,%,/,<,>,<=,>=,==,===,<>,&&,||""" - .stripMargin - .split(",") - .iterator - .map(name => new ValueSymbol(Var(name), false)) - ) + def partitionSymbols( + z: (Map[Str, TypeSymbol], Map[Str, TermSymbol]), + symbols: IterableOnce[Symbol] + ): (Map[Str, TypeSymbol], Map[Str, TermSymbol]) = + symbols.iterator.foldLeft((z._1, z._2)) { + case ((types, terms), symbol: TypeSymbol) => (types + (symbol.name -> symbol), terms) + case ((types, terms), symbol @ FunctionSymbol(Var(name), S(Var(operator)), _)) => + (types, terms + (name -> symbol) + (operator -> symbol)) + case ((types, terms), symbol: TermSymbol) => (types, terms + (symbol.name -> symbol)) + } + + def from(symbols: IterableOnce[Symbol]): Scope = { + val (newTypes, newTerms) = partitionSymbols((Map.empty, Map.empty), symbols) + new Scope(N, newTypes, newTerms) + } + + val global: Scope = { + val trueDefn = NuTypeDef(Mod, TypeName("true"), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, N) + val falseDefn = NuTypeDef(Mod, TypeName("false"), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, N) + val trueSymbol = new ModuleSymbol(trueDefn) + val falseSymbol = new ModuleSymbol(falseDefn) + Scope.from( + """true,false,document,window,typeof,toString,not,succ,log,discard,negate, + |round,add,sub,mul,div,sqrt,lt,le,gt,ge,slt,sle,sgt,sge,length,concat,eq, + |ne,error,id,if,emptyArray,+,-,*,%,/,<,>,<=,>=,==,===,<>,&&,||""" + .stripMargin + .split(",") + .iterator + .map(name => new ValueSymbol(Var(name), false)) + .concat(trueSymbol :: falseSymbol :: Nil) + ) + } } diff --git a/shared/src/main/scala/mlscript/pretyper/Symbol.scala b/shared/src/main/scala/mlscript/pretyper/Symbol.scala index 475c33fc..64d046e2 100644 --- a/shared/src/main/scala/mlscript/pretyper/Symbol.scala +++ b/shared/src/main/scala/mlscript/pretyper/Symbol.scala @@ -1,79 +1,133 @@ package mlscript.pretyper import collection.mutable.{Buffer, Map => MutMap, Set => MutSet} -import mlscript.{NuFunDef, NuTypeDef, TypeName, Var} +import mlscript.{Loc, NuFunDef, NuTypeDef, TypeName, Var} +import mlscript.{Cls, Trt, Mxn, Als, Mod} import mlscript.utils._, shorthands._ -sealed abstract class Symbol(val name: String) +package object symbol { + sealed abstract class Symbol(val name: Str) { + def typeSymbolOption: Opt[TypeSymbol] = this match { + case symbol: TypeSymbol => S(symbol) + case _ => N + } + def termSymbolOption: Opt[TermSymbol] = this match { + case symbol: TermSymbol => S(symbol) + case _ => N + } + } + + sealed abstract class TypeSymbol(val defn: NuTypeDef) extends Symbol(defn.name) { + def scope: Scope = ??? + def contents: Map[Str, Symbol] = ??? + + def complete(scope: Scope, contents: Map[Str, Symbol]): Unit = ??? + + var baseTypes: Ls[TypeSymbol] = Nil + var sealedDerivedTypes: Ls[TypeSymbol] = Nil + + @inline def hasSuperType(superType: TypeSymbol): Bool = baseTypes.exists(_ === superType) + } + + final class ClassSymbol(/* enclosingScope: Scope, */ defn: NuTypeDef) extends TypeSymbol(defn) { + require(defn.kind === Cls) + // lazy val (scope, contents) = (enclosingScope.derive, Map.empty[Str, Symbol]) + } + + final class TraitSymbol(/* enclosingScope: Scope, */ defn: NuTypeDef) extends TypeSymbol(defn) { + require(defn.kind === Trt) + // lazy val (scope, contents) = (enclosingScope.derive, Map.empty[Str, Symbol]) + } + + final class MixinSymbol(/* enclosingScope: Scope, */ defn: NuTypeDef) extends TypeSymbol(defn) { + require(defn.kind === Mxn) + // lazy val (scope, contents) = (enclosingScope.derive, Map.empty[Str, Symbol]) + } + + final class TypeAliasSymbol(/* enclosingScope: Scope, */ defn: NuTypeDef) extends TypeSymbol(defn) { + require(defn.kind === Als) + // lazy val (scope, contents) = (enclosingScope.derive, Map.empty[Str, Symbol]) + } -final class TypeSymbol(val nme: TypeName, val defn: NuTypeDef) extends Symbol(nme.name) { - var containedScope: Opt[Scope] = N - var containedContents: Opt[TypeContents] = N + final class ModuleSymbol(/* enclosingScope: Scope, */ defn: NuTypeDef) extends TypeSymbol(defn) { + require(defn.kind === Mod) + // lazy val (scope, contents) = (enclosingScope.derive, Map.empty[Str, Symbol]) + } - def scope: Scope = containedScope.getOrElse(throw new Exception("Scope not set")) - def contents: TypeContents = containedContents.getOrElse(throw new Exception("TypeContents not set")) + sealed abstract class TermSymbol(name: String) extends Symbol(name) - def scope_=(s: Scope): Unit = containedScope = S(s) - def contents_=(c: TypeContents): Unit = containedContents = S(c) -} + final class FunctionSymbol(val defn: NuFunDef) extends TermSymbol(defn.nme.name) { + require(defn.isLetRec.isEmpty) + val nme: Var = defn.nme + val operator: Opt[Var] = defn.symbolicNme + } -sealed abstract class TermSymbol(name: String) extends Symbol(name) + object FunctionSymbol { + def unapply(symbol: TermSymbol): Opt[(Var, Opt[Var], NuFunDef)] = + symbol match { + case fs: FunctionSymbol => S(fs.nme, fs.operator, fs.defn) + case _ => N + } + } -final class FunctionSymbol(val nme: Var, val defn: NuFunDef) extends TermSymbol(nme.name) { - var containedScope: Opt[Scope] = N + sealed abstract class ScrutineeSymbol(name: Str) extends TermSymbol(name) { + def toLoc: Opt[Loc] - def scope: Scope = containedScope.getOrElse(throw new Exception("Scope not set")) + val matchedClasses: MutMap[TypeSymbol, Buffer[Loc]] = MutMap.empty - def scope_=(s: Scope): Unit = containedScope = S(s) -} + def addMatchedClass(symbol: TypeSymbol, loc: Opt[Loc]): Unit = { + matchedClasses.getOrElseUpdate(symbol, Buffer.empty) ++= loc + } -sealed abstract class ScrutineeSymbol(name: Str) extends TermSymbol(name) { - val matchedClasses: MutSet[Var] = MutSet.empty - /** - * This map contains the sub-scrutinee symbols when this scrutinee is matched - * against class patterns. - */ - val classParameterScrutineeMap: MutMap[Var -> Int, SubValueSymbol] = MutMap.empty - val tupleElementScrutineeMap: MutMap[Int, SubValueSymbol] = MutMap.empty - val recordValueScrutineeMap: MutMap[Var, SubValueSymbol] = MutMap.empty + /** + * This map contains the sub-scrutinee symbols when this scrutinee is matched + * against class patterns. + */ + val classParameterScrutineeMap: MutMap[Var -> Int, SubValueSymbol] = MutMap.empty + val tupleElementScrutineeMap: MutMap[Int, SubValueSymbol] = MutMap.empty + val recordValueScrutineeMap: MutMap[Var, SubValueSymbol] = MutMap.empty - def addSubScrutinee(className: Var, index: Int, parameter: Var): SubValueSymbol = { - classParameterScrutineeMap.getOrElseUpdate(className -> index, { - new SubValueSymbol(this, S(className) -> S(index), parameter.name) - }) + def addSubScrutinee(className: Var, index: Int, parameter: Var, loc: Opt[Loc]): SubValueSymbol = { + classParameterScrutineeMap.getOrElseUpdate(className -> index, { + new SubValueSymbol(this, S(className) -> S(index), parameter.name, loc) + }) + } + + def addSubScrutinee(fieldName: Var, loc: Opt[Loc]): SubValueSymbol = + recordValueScrutineeMap.getOrElseUpdate(fieldName, { + val synthesizedName = s"${name}$$record${fieldName}" + new SubValueSymbol(this, S(fieldName) -> N, synthesizedName, loc) + }) + + def addSubScrutinee(index: Int, loc: Opt[Loc]): SubValueSymbol = + tupleElementScrutineeMap.getOrElseUpdate(index, { + val synthesizedName = s"${name}$$tuple${index.toString}" + new SubValueSymbol(this, N -> S(index), synthesizedName, loc) + }) + + /** + * This buffer contains alias variables which created by "let" bindings or + * alias patterns. + */ + val aliases: Buffer[Var] = Buffer.empty } - def addSubScrutinee(fieldName: Var): SubValueSymbol = - recordValueScrutineeMap.getOrElseUpdate(fieldName, { - val synthesizedName = s"${name}$$record${fieldName}" - new SubValueSymbol(this, S(fieldName) -> N, synthesizedName) - }) - - def addSubScrutinee(index: Int): SubValueSymbol = - tupleElementScrutineeMap.getOrElseUpdate(index, { - val synthesizedName = s"${name}$$tuple${index.toString}" - new SubValueSymbol(this, N -> S(index), synthesizedName) - }) - - /** - * This buffer contains alias variables which created by "let" bindings or - * alias patterns. - */ - val aliases: Buffer[Var] = Buffer.empty -} - -final class ValueSymbol(val nme: Var, val hoisted: Bool) extends ScrutineeSymbol(nme.name) - -final class SubValueSymbol( - val parentSymbol: ScrutineeSymbol, - /** - * TODO: This becomes useless now. - * Class patterns: (S(className), S(index)) - * Record patterns: (S(fieldName), N) - * Tuple patterns: (N, S(index)) - */ - val accessor: (Opt[Var], Opt[Int]), - override val name: Str -) extends ScrutineeSymbol(name) { - lazy val toVar: Var = Var(name) -} + final class ValueSymbol(val nme: Var, val hoisted: Bool) extends ScrutineeSymbol(nme.name) { + override def toLoc: Opt[Loc] = nme.toLoc + } + + final class SubValueSymbol( + val parentSymbol: ScrutineeSymbol, + /** + * TODO: This becomes useless now. + * Class patterns: (S(className), S(index)) + * Record patterns: (S(fieldName), N) + * Tuple patterns: (N, S(index)) + */ + val accessor: (Opt[Var], Opt[Int]), + override val name: Str, + override val toLoc: Opt[Loc] + ) extends ScrutineeSymbol(name) { + lazy val toVar: Var = Var(name) + } +} \ No newline at end of file diff --git a/shared/src/main/scala/mlscript/pretyper/package.scala b/shared/src/main/scala/mlscript/pretyper/package.scala index 06d8773c..addb94ce 100644 --- a/shared/src/main/scala/mlscript/pretyper/package.scala +++ b/shared/src/main/scala/mlscript/pretyper/package.scala @@ -2,13 +2,4 @@ package mlscript import mlscript.utils._, shorthands._ -package object pretyper { - def shortName(term: Term): Str = term match { - case Var(name) => s"Var(\"$name\")" - case literal: Lit => literal.toString - case _ => - val name = term.getClass.getSimpleName - val arity = term.children.length // Imprecise - if (arity === 0) { name } else s"${name}(${(", _" * arity).drop(2)})" - } -} +package object pretyper diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index 0c09de16..1b192a48 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -2,7 +2,8 @@ package mlscript.ucs import collection.mutable.{Map => MutMap} import mlscript.ucs.stages._ -import mlscript.pretyper.{PreTyper, Scope, ScrutineeSymbol, Symbol, ValueSymbol} +import mlscript.pretyper.{PreTyper, Scope} +import mlscript.pretyper.symbol._ import mlscript._, utils._, shorthands._ import mlscript.Message, Message.MessageContext @@ -12,7 +13,6 @@ trait DesugarUCS extends Transformation with Normalization with PostProcessing with CoverageChecking { self: PreTyper => - protected def traverseIf(`if`: If)(implicit scope: Scope): Unit = trace("traverseIf") { @@ -70,7 +70,7 @@ trait DesugarUCS extends Transformation } }() - private def traversePattern(scrutinee: Var, pattern: core.Pattern): List[Var -> Symbol] = + private def traversePattern(scrutinee: Var, pattern: core.Pattern)(implicit scope: Scope): List[Var -> Symbol] = trace(s"traversePattern <== $pattern") { lazy val scrutineeSymbol = scrutinee.symbol match { case symbol: ScrutineeSymbol => symbol @@ -82,29 +82,47 @@ trait DesugarUCS extends Transformation case core.Pattern.Name(nme) => nme.symbol = scrutineeSymbol nme -> scrutineeSymbol :: Nil - case core.Pattern.Class(nme, N) => - scrutineeSymbol.matchedClasses += nme - Nil - case core.Pattern.Class(nme, S(parameters)) => - scrutineeSymbol.matchedClasses += nme - parameters.iterator.zipWithIndex.flatMap { - case (N, _) => N - case (S(parameter), index) => - val symbol = scrutineeSymbol.addSubScrutinee(nme, index, parameter) - parameter.symbol = symbol; S(parameter -> symbol) - }.toList + case core.Pattern.Class(nme, maybeParameters) => + println(s"name has location: ${nme.toLoc.isDefined}") + // Resolve `nme`. It can either be a class, a trait, or a module. + val symbol = scope.getTypeSymbol(nme.name) match { + case S(symbol: TraitSymbol) => println(s"${nme.name} is a trait"); symbol + case S(symbol: ClassSymbol) => println(s"${nme.name} is a class"); symbol + case S(symbol: ModuleSymbol) => println(s"${nme.name} is a module"); symbol + case S(symbol: MixinSymbol) => + throw new DesugaringException(msg"Mixins are not allowed in pattern" -> nme.toLoc :: Nil) + case S(symbol: TypeAliasSymbol) => + throw new DesugaringException(msg"Type alias is not allowed in pattern" -> nme.toLoc :: Nil) + // case S(symbol: TermSymbol) => + // throw new DesugaringException(msg"Only classes, modules, and traits can be matched against." -> nme.toLoc :: Nil) + case N => + throw new DesugaringException(msg"Undefined symbol found in patterns." -> nme.toLoc :: Nil) + } + nme.symbol = symbol + // Add the class to the list of matched classes. + scrutineeSymbol.addMatchedClass(symbol, nme.toLoc) + maybeParameters match { + case N => Nil + case S(parameters) => + parameters.iterator.zipWithIndex.flatMap { + case (N, _) => N + case (S(parameter), index) => + val symbol = scrutineeSymbol.addSubScrutinee(nme, index, parameter, parameter.toLoc) + parameter.symbol = symbol; S(parameter -> symbol) + }.toList + } case core.Pattern.Tuple(elements) => elements.flatMap { case N => Nil case S(pattern) => elements.iterator.zipWithIndex.flatMap { case (N, _) => N case (S(element), index) => - val symbol = scrutineeSymbol.addSubScrutinee(index) + val symbol = scrutineeSymbol.addSubScrutinee(index, element.toLoc) element.symbol = symbol; S(element -> symbol) }.toList } case core.Pattern.Record(entries) => entries.iterator.zipWithIndex.map { case ((fieldName, bindingName), _) => - val symbol = scrutineeSymbol.addSubScrutinee(fieldName) + val symbol = scrutineeSymbol.addSubScrutinee(fieldName, bindingName.toLoc) bindingName.symbol = symbol; bindingName -> symbol }.toList } diff --git a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala index 0b3d0353..fc157c5c 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala @@ -1,11 +1,11 @@ package mlscript.ucs.stages -import mlscript.{Case, CaseBranches, CaseOf, Let, Loc, NoCases, Term, Var, Wildcard} -import mlscript.pretyper.{ScrutineeSymbol, Symbol} +import annotation.tailrec +import collection.mutable.ListBuffer +import mlscript.{Case, CaseBranches, CaseOf, Let, Lit, Loc, NoCases, Term, Var, Wildcard} +import mlscript.pretyper.symbol._ import mlscript.utils._, shorthands._ import mlscript.Message, Message.MessageContext -import mlscript.pretyper.shortName -import scala.annotation.tailrec import mlscript.{SimpleTerm, Diagnostic, ErrorReport, WarningReport} trait CoverageChecking { self: mlscript.pretyper.Traceable => @@ -19,24 +19,37 @@ trait CoverageChecking { self: mlscript.pretyper.Traceable => private def collectRegistry(term: Term): MatchRegistry = { @tailrec - def rec(acc: MatchRegistry, rest: Ls[Term]): MatchRegistry = rest match { - case Nil => acc - case head :: tail => head match { - case Let(_, _, _, body) => rec(acc, body :: tail) - case CaseOf(Scrutinee(_, scrutinee), cases) => - rec( - acc.updatedWith(scrutinee)(vs => S(cases.foldLeft(vs.getOrElse(Set.empty))({ - case (acc, (className: Var) -> _) => acc + className - case (acc, _) => acc - })((x, _) => x))), - tail ++ cases.foldLeft - (Nil: Ls[Term]) - ({ case (acc, _ -> body) => body :: acc }) - ((acc, els) => els.fold(acc)(_ :: acc)) - ) - case _ => rec(acc, tail) + def rec(acc: MatchRegistry, rest: Ls[Term]): MatchRegistry = + rest match { + case Nil => println("end"); acc + case head :: tail => + println(s"collect ${inspect.shallow(head)}") + head match { + case Let(_, _, _, body) => + println(s"continue on ${inspect.shallow(body)}") + rec(acc, body :: tail) + case CaseOf(Scrutinee(_, scrutinee), cases) => + println(s"scrutinee: ${scrutinee.name}") + rec( + acc.updatedWith(scrutinee)(vs => S(cases.foldLeft(vs.getOrElse(CaseSet.empty))({ + case (acc, (className: Var) -> _) => + println(s"found pattern $className (has location = ${className.toLoc.nonEmpty})") + val classLikeSymbol = className.symbolOption.flatMap(_.typeSymbolOption).getOrElse { + throw new Exception(s"$className is not associated with any type symbol.") + } + acc.add(classLikeSymbol, className.toLoc) + case (acc, (literal: Lit) -> _) => + println(s"TODO: skipped literal $literal") + acc + })((x, _) => x))), + tail ++ cases.foldLeft + (Nil: Ls[Term]) + ({ case (acc, _ -> body) => body :: acc }) + ((acc, els) => els.fold(acc)(_ :: acc)) + ) + case _ => rec(acc, tail) + } } - } rec(Map.empty, term :: Nil) } @@ -44,14 +57,14 @@ trait CoverageChecking { self: mlscript.pretyper.Traceable => term: Term, pending: MatchRegistry, working: MatchRegistry, - assumptions: Map[ScrutineeSymbol, Var] + seen: SeenRegistry ): Ls[Diagnostic] = - trace(s"checkCoverage <== ${shortName(term)}, ${pending.size} pending, ${working.size} working") { - println(s"assumptions: " + (if (assumptions.isEmpty) "empty" else - assumptions.iterator.map { case (k, v) => s"${k.name} is $v" }.mkString(", ") + trace(s"checkCoverage <== ${inspect.shallow(term)}, ${pending.size} pending, ${working.size} working, ${seen.size} seen") { + println(s"seen: " + (if (seen.isEmpty) "empty" else + seen.iterator.map { case (k, (s, _, _)) => s"${k.name} is ${s.name}" }.mkString(", ") )) term match { - case Let(_, _, _, body) => checkCoverage(body, pending, working, assumptions) + case Let(_, _, _, body) => checkCoverage(body, pending, working, seen) case CaseOf(Scrutinee(scrutineeVar, scrutinee), cases) => println(s"scrutinee: ${scrutinee.name}") // If the scrutinee is still pending (i.e., not matched yet), then we @@ -62,67 +75,190 @@ trait CoverageChecking { self: mlscript.pretyper.Traceable => // 1. The scrutinee has been never visited, which is an error. // 2. It has been matched to be an instance of some class. Therefore, // we need to check if this is a contradiction. - val (unseenClasses, newPending) = pending.get(scrutinee) match { + val (unseenPatterns, newPending) = pending.get(scrutinee) match { case S(matchedClasses) => matchedClasses -> (pending - scrutinee) case N => working.get(scrutinee) match { - case S(unseenClasses) => unseenClasses -> pending - case N => throw new Exception(s"Scrutinee ${scrutinee.name} is not matched.") + case S(unseenPatterns) => unseenPatterns -> pending + case N => + // Neither in the working list nor in the pending list. + seen.get(scrutinee) match { + // The scrutine may have been matched. + case S((_, _, remainingPatterns)) => remainingPatterns -> pending + case N => + // The scrutinee has never been visited. This should not happen. + println("working list: " + showRegistry(working)) + throw new Exception(s"Scrutinee ${scrutinee.name} is not in the working list.") + } } } - // We keep removing classes from the unseen class list and adding - // diagnostics (warnings and errors). - cases.foldLeft((unseenClasses, Nil: Ls[Diagnostic]))({ - case ((red, acc), (className: Var) -> body) => - if (red contains className) { - ( - // The class is matched. Remove it from the class list. - red - className, - // We remove the scrutinee from the working list and add - // `scrutinee` is `className` to the assumptions. - acc ++ checkCoverage(body, newPending, working - scrutinee, assumptions + (scrutinee -> className)) - ) - } else { - red -> (acc :+ (assumptions.get(scrutinee) match { - case S(`className`) => WarningReport("tautology", Nil, Diagnostic.PreTyping) - case S(otherClassName) => ErrorReport("contradiction", Nil, Diagnostic.PreTyping) - case N => ErrorReport("unvisited scrutinee", Nil, Diagnostic.PreTyping) - })) + // We go through cases and keep removing the current pattern from the + // unseen pattern set. + // Meanwhile, we keep adding diagnostics if we meet warnings and errors. + cases.foldLeft((unseenPatterns, Nil: Ls[Diagnostic]))({ + case ((unseenPatterns, diagnostics), (className: Var) -> body) => + val classSymbol = className.symbolOption.flatMap(_.typeSymbolOption).getOrElse { + throw new Exception(s"$className is not associated with a type symbol") + } + println(s"class symbol: ${classSymbol.name}") + unseenPatterns.split(classSymbol) match { + case S((locations, refiningPatterns, remainingPatterns)) => + println(s"remove ${className} and it's unrelated patterns from working") + println("unseen patterns: " + unseenPatterns.patterns.mkString("[", ", ", "]")) + println("remaining patterns: " + remainingPatterns.patterns.mkString("[", ", ", "]")) + // Remove the scrutinee from the working list. + val newWorking = if (remainingPatterns.isEmpty) + working - scrutinee + else + working.updated(scrutinee, remainingPatterns) + // Add "`scrutinee` is `className`" to the seen registry. + val newSeen = seen + (scrutinee -> (classSymbol, locations, refiningPatterns)) + ( + remainingPatterns, + diagnostics ++ checkCoverage(body, newPending, newWorking, newSeen) + ) + case N => + unseenPatterns -> (diagnostics :+ (seen.get(scrutinee) match { + case S((`classSymbol`, _, _)) => WarningReport("tautology", Nil, Diagnostic.PreTyping) + case S(_) => ErrorReport("contradiction", Nil, Diagnostic.PreTyping) + case N => ErrorReport("unvisited scrutinee", Nil, Diagnostic.PreTyping) + })) } - case (acc, _ -> _) => + case (diagnostics, (_: Lit) -> _) => println("CANNOT check literal patterns") - acc + diagnostics }) { case ((missingCases, diagnostics), N) => - println("MISSING cases: " + missingCases.iterator.map(className => s"${scrutinee.name} is $className").mkString("[", ", ", "]")) - diagnostics ++ (missingCases.iterator.map { className => - ErrorReport({ - val s1 = assumptions.iterator.map { - case (scrutinee, className) => s"${scrutinee.name} is $className" - }.mkString(", ") - val s2 = if (s1.isEmpty) "" else s" $s1, and" - msg"missing a case where$s2 ${scrutinee.name} is ${className.name}" -> N :: - msg"missing the condition" -> className.toLoc :: - assumptions.iterator.zipWithIndex.map({ case (scrutinee, className) -> index => - msg"${if (index > 0) "and" else "when"} ${scrutinee.name} is ${className.name}" -> className.toLoc - }).toList - }, true, Diagnostic.PreTyping) - }) - case ((unseenClasses, diagnostics), S(default)) => - println("wildcard case") - checkCoverage(default, newPending, working.updated(scrutinee, unseenClasses), assumptions) + println("remaining cases should are not covered") + println("MISSING cases: " + missingCases.patterns.mkString("[", ", ", "]")) + diagnostics ++ explainMissingCases(scrutinee, seen, missingCases) + case ((remainingCases, diagnostics), S(default)) => + println("remaining cases should be covered by the wildcard") + checkCoverage(default, newPending, working.updated(scrutinee, remainingCases), seen) } case other => println("STOP"); Nil } - }(ls => s"checkCoverage ==> ${ls.length}") + }(ls => s"checkCoverage ==> ${ls.length} diagnostics") } object CoverageChecking { - type MatchRegistry = Map[ScrutineeSymbol, Set[Var]] + type MatchRegistry = Map[ScrutineeSymbol, CaseSet] + + type SeenRegistry = Map[ScrutineeSymbol, (TypeSymbol, Ls[Loc], CaseSet)] + + sealed abstract class Pattern { + override def toString(): String = this match { + case Pattern.ClassLike(symbol) => s"${symbol.defn.kind.str} `${symbol.name}`" + case Pattern.Tuple(_) => "tuple" + case Pattern.Literal(literal) => s"literal ${inspect.deep(literal)}" + } + } + object Pattern { + final case class ClassLike(symbol: TypeSymbol) extends Pattern + final case class Tuple(TODO: Nothing) extends Pattern // To be implemented in the near future. + final case class Literal(literal: Lit) extends Pattern + } + + /** + * A `CaseSet` represents all patterns that a particular scrutinee is + * being matched with within a UCS expression. Each Pattern is associated + * with the locations where these patterns appear. + * + * @param patterns a set of patterns that the scrutinee is matched with. + * @param hasWildcard if the scrutinee is matched with a wildcard pattern. + */ + final case class CaseSet(val cases: Map[Pattern, Ls[Loc]], val hasWildcard: Bool) { + /** TODO: This seems useless. */ + @inline def withWildcard: CaseSet = if (hasWildcard) this else copy(hasWildcard = true) + + /** + * Split the pattern set into two pattern sets. + * + * For example, let A be the base type of B, C, and D. Plus, class `Z` is + * unrelated to any of them. Suppose the initial pattern set is + * `{ A, B, C, Z }`. Splitting the set results in two sets, one set + * contains classes that are compatible with `A`, and the other set + * contains classes that are unrelated to `A`. + * + * For example, if we split the set with `A`, then we get `{ B, C }` and + * set `{ Z }`. Set `{ B, C }` represents that the scrutinee can be further + * refined to class `B` or `class C`. Set `{ Z }` represents that if the + * scrutinee is not `A`, then it can be `Z`. + * + * If `A` is sealed to `B`, `C`, and `D`, then we get `{ B, C, D }` and + * `{ Z }`. Because if the scrutinee is assumed to be `A`, then it can also + * be `D` other than `B`, `C`. + * + * @param classLikeSymbol the type symbol represents the class like type + * @return If the pattern set doesn't include the given type symbol, this + * returns `None`. Otherwise, the function returns a triplet of the + * locations where the pattern appears, the related patterns, and + * unrelated patterns. + */ + def split(classLikeSymbol: TypeSymbol): Opt[(Ls[Loc], CaseSet, CaseSet)] = { + val classLikePattern = Pattern.ClassLike(classLikeSymbol) + cases.get(classLikePattern).map { locations => + val withoutSymbol = cases - classLikePattern + val relatedPatterns = withoutSymbol.filter { + case (Pattern.ClassLike(otherSymbol), _) => otherSymbol.baseTypes.contains(classLikeSymbol) + case (_: Pattern.Tuple | _: Pattern.Literal) -> _ => false + } ++ classLikeSymbol.sealedDerivedTypes.iterator.map { symbol => + Pattern.ClassLike(symbol) -> symbol.defn.nme.toLoc.toList + } + val unrelatedPatterns = withoutSymbol.filter { + case (Pattern.ClassLike(otherSymbol), _) => !otherSymbol.baseTypes.contains(classLikeSymbol) + case (_: Pattern.Tuple | _: Pattern.Literal) -> _ => true + } + (locations, copy(relatedPatterns), copy(unrelatedPatterns)) + } + } + + /** Add a type sysmbol as a class like pattern to the set. */ + def add(classLikeSymbol: TypeSymbol, location: Opt[Loc]): CaseSet = { + val classLikePattern = Pattern.ClassLike(classLikeSymbol) + copy(cases = cases.updatedWith(classLikePattern) { + case N => S(location.toList) + case S(locations) => S(location.toList ++ locations) + }) + } + + /** Get an iterator of only patterns. */ + @inline def patterns: Iterator[Pattern] = cases.iterator.map(_._1) + + @inline def isEmpty: Bool = cases.isEmpty + + @inline def size: Int = cases.size + } + + object CaseSet { + lazy val empty: CaseSet = CaseSet(Map.empty, false) + } + + /** Create an `ErrorReport` that explains missing cases. */ + private def explainMissingCases(scrutinee: ScrutineeSymbol, seen: SeenRegistry, missingCases: CaseSet): Opt[ErrorReport] = + if (missingCases.isEmpty) { + N + } else { + S(ErrorReport({ + val lines = (msg"Scrutinee `${scrutinee.name}` has ${"missing case".pluralize(missingCases.size, true)}" -> scrutinee.toLoc) :: + (missingCases.cases.iterator.flatMap { case (pattern, locations) => + (msg"It can be ${pattern.toString}" -> locations.headOption) :: Nil + }.toList) + if (seen.isEmpty) { + lines + } else { + seen.iterator.zipWithIndex.map { case ((scrutinee, (classSymbol, locations, cases)), i) => + val prologue = if (i === 0) "When " else "" + val epilogue = if (seen.size === 1) "" else if (i === seen.size - 1) ", and" else "," + msg"${prologue}scrutinee `${scrutinee.name}` is `${classSymbol.name}`$epilogue" -> locations.headOption + }.toList ::: lines + } + }, true, Diagnostic.PreTyping)) + } /** A helper function that prints entries from the given registry line by line. */ - def showRegistry(registry: MatchRegistry): Str = + private def showRegistry(registry: MatchRegistry): Str = if (registry.isEmpty) "empty" else registry.iterator.map { case (scrutinee, matchedClasses) => - matchedClasses.iterator.mkString(s">>> ${scrutinee.name} => [", ", ", "]") + matchedClasses.patterns.mkString(s">>> ${scrutinee.name} => [", ", ", "]") }.mkString("\n", "\n", "") } diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index b8c1d988..7a97e9ed 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -4,7 +4,7 @@ import mlscript.{App, Asc, Fld, Term, Var, TypeName} import mlscript.ucs.{syntax => s, core => c, PartialTerm} import mlscript.ucs.helpers.mkBinOp import mlscript.utils._, shorthands._ -import mlscript.pretyper.{ScrutineeSymbol, SubValueSymbol, ValueSymbol} +import mlscript.pretyper.symbol._ import mlscript.ucs.DesugaringException import mlscript.Message, Message.MessageContext diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 1d5fa1c5..193933e6 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -4,7 +4,8 @@ package mlscript.ucs.stages import mlscript.ucs.{Lines, LinesOps} import mlscript.ucs.core._ import mlscript.ucs.helpers._ -import mlscript.pretyper.Symbol +import mlscript.pretyper.Scope +import mlscript.pretyper.symbol._ import mlscript.{App, CaseOf, Fld, FldFlags, Let, Loc, Sel, Term, Tup, Var, StrLit} import mlscript.{CaseBranches, Case, Wildcard, NoCases} import mlscript.Message, Message.MessageContext @@ -19,30 +20,30 @@ trait Normalization { self: mlscript.pretyper.Traceable => * @param split the split to normalize * @return the normalized term */ - @inline def normalize(split: Split): Term = normalizeToTerm(split) + @inline def normalize(split: Split)(implicit scope: Scope): Term = normalizeToTerm(split) - private def normalizeToTerm(split: Split): Term = trace("normalizeToTerm") { + private def normalizeToTerm(split: Split)(implicit scope: Scope): Term = trace("normalizeToTerm") { split match { case Split.Cons(Branch(scrutinee, Pattern.Name(nme), continuation), tail) => println(s"alias $scrutinee => $nme") Let(false, nme, scrutinee, normalizeToTerm(continuation ++ tail)) case Split.Cons(Branch(scrutinee, pattern @ Pattern.Literal(literal), continuation), tail) => - val trueBranch = normalizeToTerm(specialize(continuation ++ tail, Yes)(scrutinee.symbol, pattern)) - val falseBranch = normalizeToCaseBranches(specialize(tail, No)(scrutinee.symbol, pattern)) + val trueBranch = normalizeToTerm(specialize(continuation ++ tail, Yes)(scrutinee.symbol, pattern, scope)) + val falseBranch = normalizeToCaseBranches(specialize(tail, No)(scrutinee.symbol, pattern, scope)) CaseOf(scrutinee, Case(literal, trueBranch, falseBranch)) // No class parameters. Easy case Split.Cons(Branch(scrutinee, pattern @ Pattern.Class(nme, N), continuation), tail) => - println(s"match $scrutinee with $nme") - val trueBranch = normalizeToTerm(specialize(continuation ++ tail, Yes)(scrutinee.symbol, pattern)) - val falseBranch = normalizeToCaseBranches(specialize(tail, No)(scrutinee.symbol, pattern)) + println(s"match $scrutinee with $nme (has location: ${nme.toLoc.isDefined})") + val trueBranch = normalizeToTerm(specialize(continuation ++ tail, Yes)(scrutinee.symbol, pattern, scope)) + val falseBranch = normalizeToCaseBranches(specialize(tail, No)(scrutinee.symbol, pattern, scope)) CaseOf(scrutinee, Case(nme, trueBranch, falseBranch)) case Split.Cons(Branch(scrutinee, pattern @ Pattern.Class(nme, S(parameters)), continuation), tail) => println(s"match $scrutinee with $pattern") val trueBranch = trace("compute true branch"){ - normalizeToTerm(specialize(continuation ++ tail, Yes)(scrutinee.symbol, pattern)) + normalizeToTerm(specialize(continuation ++ tail, Yes)(scrutinee.symbol, pattern, scope)) }() val falseBranch = trace("compute false branch"){ - normalizeToCaseBranches(specialize(tail, No)(scrutinee.symbol, pattern)) + normalizeToCaseBranches(specialize(tail, No)(scrutinee.symbol, pattern, scope)) }() val unappliedVar = Var(s"args_${scrutinee.name}$$${nme.name}") val trueBranchWithBindings = Let( @@ -62,11 +63,11 @@ trait Normalization { self: mlscript.pretyper.Traceable => throw new NormalizationException((msg"Unsupported pattern: ${pattern.toString}" -> pattern.toLoc) :: Nil) case Split.Let(rec, nme, rhs, tail) => Let(rec, nme, rhs, normalizeToTerm(tail)) case Split.Else(default) => default - case Split.Nil => StrLit("test") // FIXME + case Split.Nil => println("unexpected empty split"); ??? } }() - private def normalizeToCaseBranches(split: Split): CaseBranches = trace("normalizeToCaseBranches") { + private def normalizeToCaseBranches(split: Split)(implicit scope: Scope): CaseBranches = trace("normalizeToCaseBranches") { split match { // case Split.Cons(head, Split.Nil) => Case(head.pattern, normalizeToTerm(head.continuation), NoCases) case other @ (Split.Cons(_, _) | Split.Let(_, _, _, _)) => Wildcard(normalizeToTerm(other)) @@ -78,7 +79,7 @@ trait Normalization { self: mlscript.pretyper.Traceable => // Specialize `split` with the assumption that `scrutinee` matches `pattern`. private def specialize (split: Split, matchOrNot: MatchOrNot) - (implicit scrutinee: Symbol, pattern: Pattern): Split = + (implicit scrutinee: Symbol, pattern: Pattern, scope: Scope): Split = trace(s"S${matchOrNot} <== ${scrutinee.name} is ${pattern}") { (matchOrNot, split) match { // Name patterns are translated to let bindings. @@ -133,8 +134,17 @@ trait Normalization { self: mlscript.pretyper.Traceable => case (_, _) => specialize(continuation ++ tail, Yes) } // END match } else { - println(s"class name: $className =/= $otherClassName") - specializedTail + (otherClassName.symbolOption, className.symbolOption) match { + case (S(otherClassSymbol: TypeSymbol), S(classSymbol: TypeSymbol)) if ( + otherClassSymbol.baseTypes contains classSymbol + ) => + println(s"class name: $otherClassName <: $className") + split + case (there, here) => + println(s"class name: $className =/= $otherClassName") + println(s"class symbols: ${there.fold("_")(_.name)} ${here.fold("_")(_.name)}") + specializedTail + } } case _ => throw new NormalizationException((msg"Incompatible: ${pattern.toString}" -> pattern.toLoc) :: Nil) } @@ -159,11 +169,20 @@ trait Normalization { self: mlscript.pretyper.Traceable => println(s"class name: $className === $otherClassName") specialize(tail, No) // TODO: Subsitute parameters to otherParameters } else { - println(s"class name: $className =/= $otherClassName") - split.copy( - // head = head.copy(continuation = specialize(continuation, No)), - tail = specialize(tail, No) - ) + (otherClassName.symbolOption, className.symbolOption) match { + case (S(otherClassSymbol: TypeSymbol), S(classSymbol: TypeSymbol)) if ( + otherClassSymbol.baseTypes contains classSymbol + ) => + println(s"class name: $otherClassName <: $className") + Split.Nil + case (there, here) => + println(s"class name: $className =/= $otherClassName") + println(s"class symbols: ${there.fold("_")(_.name)} ${here.fold("_")(_.name)}") + split.copy( + // head = head.copy(continuation = specialize(continuation, No)), + tail = specialize(tail, No) + ) + } } case _ => throw new NormalizationException((msg"Incompatible: ${pattern.toString}" -> pattern.toLoc) :: Nil) } @@ -198,7 +217,11 @@ object Normalization { } /** - * Convert a normalized term to a string with indentation. + * Convert a normalized term to a string with indentation. It makes use of + * some special markers. + * + * - `*` if the variable is associated with a symbol, + * - `†` if the class name has a location. */ def showNormalizedTerm(term: Term): String = { def showTerm(term: Term): Lines = term match { @@ -213,12 +236,15 @@ object Normalization { def showCaseBranches(caseBranches: CaseBranches): Lines = caseBranches match { case Case(pat, rhs, tail) => - (s"case $pat =>" @: showTerm(rhs)) ++ showCaseBranches(tail) + // If the class name has a location, mark it with a dagger †. + // This is to track the location information. + val marker = if (pat.toLoc.isDefined) "†" else "" + (s"case $pat$marker =>" @: showTerm(rhs)) ++ showCaseBranches(tail) case Wildcard(term) => s"case _ =>" @: showTerm(term) case NoCases => Nil } def showVar(`var`: Var): Str = - // If the variable is associated with a symbol, mark it with an asterisk. + // If the variable is associated with a symbol, mark it with an asterisk *. `var`.name + (`var`.symbolOption.fold("")(_ => "*")) def showLet(let: Let): Lines = { val Let(rec, nme, rhs, body) = let diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index 416a329d..ae4cfaa6 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -1,16 +1,33 @@ package mlscript.ucs.stages import mlscript.{Case, CaseBranches, CaseOf, Let, Loc, NoCases, Term, Var, Wildcard} -import mlscript.pretyper.{ScrutineeSymbol, Symbol} +import mlscript.pretyper.symbol._ import mlscript.utils._, shorthands._ import mlscript.Message, Message.MessageContext -import mlscript.pretyper.shortName import scala.annotation.tailrec trait PostProcessing { self: mlscript.pretyper.Traceable => import PostProcessing._ - def postProcess(term: Term): Term = trace(s"postProcess <== ${shortName(term)}") { + /** + * If the given `Var` represents a class name, get its `ClassSymbol`. + * + * @param className the class name variable + */ + def getClassSymbolFromVar(className: Var): TypeSymbol = + trace(s"getClassSymbolFromVar <== ${inspect.shallow(className)}") { + className.symbolOption match { + case S(symbol: ClassSymbol) => symbol + case S(symbol: TraitSymbol) => symbol + case S(symbol: ModuleSymbol) => symbol + case S(symbol: Symbol) => throw new PostProcessingException( + msg"variable ${className.name} is not associated with a class symbol" -> N :: Nil) + case N => throw new PostProcessingException( + msg"variable ${className.name} is not associated with any symbols" -> N :: Nil) + } + }(symbol => s"getClassSymbolFromVar ==> ${symbol.name}") + + def postProcess(term: Term): Term = trace(s"postProcess <== ${inspect.shallow(term)}") { // Normalized terms are constructed using `Let` and `CaseOf`. term match { case top @ CaseOf(scrutinee: Var, fst @ Case(className: Var, body, NoCases)) => @@ -25,33 +42,40 @@ trait PostProcessing { self: mlscript.pretyper.Traceable => msg"variable ${scrutinee.name} is not a scrutinee" -> N :: Nil ) } - println(s"`${scrutinee}`'s matched classes: ${scrutineeSymbol.matchedClasses.iterator.map(_.name).mkString("[", ", ", "]")}") - // Post-process the first branch body. + val classSymbol = getClassSymbolFromVar(className) + println(s"`${scrutinee}`'s matched classes: ${scrutineeSymbol.matchedClasses.keysIterator.map(_.name).mkString("[", ", ", "]")}") + // Post-process the true branch. println("post-processing the first branch") val processedTrueBranch = postProcess(trueBranch) // Post-process the false branch. - val (default, cases) = scrutineeSymbol.matchedClasses.iterator.filter(_ =/= className) + println("post-processing the false branch") + println(s"searching for cases: " + scrutineeSymbol.matchedClasses.keysIterator.filter(_ =/= classSymbol).map(_.name).mkString("[", ", ", "]")) + val (default, cases) = scrutineeSymbol.matchedClasses.iterator.filter(_._1 =/= classSymbol) // For each case class name, distangle case branch body terms from the false branch. - .foldLeft[Opt[Term] -> Ls[Var -> Term]](S(falseBranch) -> Nil) { - case ((S(remainingTerm), cases), className) => - val (leftoverTerm, extracted) = disentangle(remainingTerm, scrutineeSymbol, className) - avoidEmptyCaseOf(leftoverTerm) -> (extracted match { + .foldLeft[Opt[Term] -> Ls[(TypeSymbol, Opt[Loc], Term)]](S(falseBranch) -> Nil) { + case ((S(remainingTerm), cases), (classSymbol -> locations)) => + println(s"searching for case: ${classSymbol.name}") + val (leftoverTerm, extracted) = disentangle(remainingTerm, scrutineeSymbol, classSymbol) + trimEmptyTerm(leftoverTerm) -> (extracted match { case N => - println(s"no extracted term about $className") + println(s"no extracted term about ${classSymbol.name}") cases case terms @ S(extractedTerm) => - println(s"extracted a term about $className") - className -> postProcess(extractedTerm) :: cases + println(s"extracted a term about ${classSymbol.name}") + (classSymbol, locations.headOption, postProcess(extractedTerm)) :: cases }) - // TODO: Consider either make the first tuple element as non-optional. - // TODO: Then, write a new helper function which checks if the term is an empty `CaseOf`. case ((N, cases), _) => (N, cases) } - println(s"found ${cases.length} cases") + println(s"found ${cases.length} case branches") + val postProcessedDefault = default.map(postProcess) // Assemble a `CaseBranches`. val actualFalseBranch = cases.foldRight[CaseBranches]( - default.fold[CaseBranches](NoCases)(Wildcard(_)) - ) { case (className -> body, rest) => Case(className, body, rest) } + postProcessedDefault.fold[CaseBranches](NoCases)(Wildcard(_)) + ) { case ((classSymbol, loc, body), rest) => + // TODO: Why not just keep the class name? + val className = Var(classSymbol.name).withLoc(loc).withSymbol(classSymbol) + Case(className, body, rest) + } // Assemble the final `CaseOf`. top.copy(cases = fst.copy(body = processedTrueBranch, rest = actualFalseBranch)) // We recursively process the body of as`Let` bindings. @@ -61,29 +85,43 @@ trait PostProcessing { self: mlscript.pretyper.Traceable => } }(_ => "postProcess ==> ") - private def avoidEmptyCaseOf(term: Term): Opt[Term] = term match { - case CaseOf(_, NoCases) => println(s"$term is empty"); N - case CaseOf(_, cases: Case) => - @tailrec - def containsNoWildcard(cases: CaseBranches): Bool = cases match { - case NoCases => true - case Wildcard(_) => false - case Case(_, body, rest) => - avoidEmptyCaseOf(body) === N && containsNoWildcard(rest) - } - if (containsNoWildcard(cases)) S(term) else N + private def trimEmptyTerm(term: Term): Opt[Term] = term match { + case k @ CaseOf(_, cases) => trimEmptyCaseBranches(cases).map(c => k.copy(cases = c)) + case let @ Let(_, _, _, body) => trimEmptyTerm(body).map(t => let.copy(body = t)) case _ => S(term) } + private def trimEmptyCaseBranches(cases: CaseBranches): Opt[CaseBranches] = cases match { + case NoCases => N + case w @ Wildcard(body) => trimEmptyTerm(body).map(t => w.copy(body = t)) + case k @ Case(_, body, rest) => + (trimEmptyTerm(body), trimEmptyCaseBranches(rest)) match { + case (N, N) => N + case (S(body), N) => S(k.copy(body = body, rest = NoCases)) + case (N, S(rest)) => S(rest) + case (S(body), S(rest)) => S(k.copy(body = body, rest = rest)) + } + } + + private def mergeTerms(t1: Opt[Term], t2: Opt[Term]): Opt[Term] = + (t1, t2) match { + case (N, N) => N + case (S(t1), N) => S(t1) + case (N, S(t2)) => S(t2) + case (S(t1), S(t2)) => S(mergeTerms(t1, t2)) + } + private def mergeTerms(t1: Term, t2: Term): Term = - trace(s"mergeTerms <== ${t1.describe} ${t2.describe}") { + trace(s"mergeTerms <== ${inspect.shallow(t1)} ${inspect.shallow(t2)}") { t1 match { case t1 @ Let(_, _, _, body) => t1.copy(body = mergeTerms(body, t2)) case t1 @ CaseOf(scrutinee: Var, cases) => t1.copy(cases = mergeTermIntoCaseBranches(t2, cases)) - case _ => println("CANNOT merge. Discard t2."); t1 + case _ => + println(s"CANNOT merge. Discard ${inspect.shallow(t2)}.") + t1 } - }() + }(merged => s"mergedTerms ==> ${inspect.shallow(merged)}") private def mergeTermIntoCaseBranches(term: Term, cases: CaseBranches): CaseBranches = trace(s"mergeTermIntoCaseBranches <== ${term.describe} ${cases}") { @@ -104,58 +142,69 @@ trait PostProcessing { self: mlscript.pretyper.Traceable => * @param className the class name * @return the remaining term and the disentangled term */ - def disentangle(term: Term, scrutinee: ScrutineeSymbol, className: Var): (Term, Opt[Term]) = - trace[(Term, Opt[Term])](s"disentangle <== ${scrutinee.name}: $className") { + def disentangle(term: Term, scrutinee: ScrutineeSymbol, classSymbol: TypeSymbol): (Term, Opt[Term]) = + trace[(Term, Opt[Term])](s"disentangle <== ${scrutinee.name}: ${classSymbol.name}") { term match { case top @ CaseOf(scrutineeVar: Var, cases) => if (scrutineeVar.symbol match { case s: ScrutineeSymbol => s === scrutinee; case _ => false }) { - println(s"found a `CaseOf` that matches on ${scrutinee.name}") + println(s"found a `CaseOf` that matches on `${scrutinee.name}`") def rec(cases: CaseBranches): (CaseBranches, Opt[Term]) = cases match { - case NoCases => println("found the end, stop"); NoCases -> N + case NoCases => + println("no cases, STOP") + NoCases -> N case wildcard @ Wildcard(body) => - println("found a wildcard, stop") - val (y, n) = disentangle(body, scrutinee, className) - wildcard.copy(body = y) -> n - case kase @ Case(`className`, body, rest) => + println("found a wildcard, go deeper") + val (n, y) = disentangle(body, scrutinee, classSymbol) + wildcard.copy(body = n) -> y + case kase @ Case(className: Var, body, rest) => println(s"found a case branch matching against $className") - val (y, n) = rec(rest) - y -> S(n.fold(body)(mergeTerms(_, body))) + val otherClassSymbol = getClassSymbolFromVar(className) + if (otherClassSymbol === classSymbol) { + rest -> S(body) + } else { + val (n1, y1) = disentangle(body, scrutinee, classSymbol) + val (n2, y2) = rec(rest) + (kase.copy(body = n1, rest = n2), mergeTerms(y1, y2)) + } case kase @ Case(otherClassName, body, rest) => - val (y, n) = rec(rest) - kase.copy(rest = y) -> n + println(s"found another case branch matching against $otherClassName") + val (n, y) = rec(rest) + kase.copy(rest = n) -> y } - val (y, n) = rec(cases) - top.copy(cases = y) -> n + val (n, y) = rec(cases) + (top.copy(cases = n), y) } else { println(s"found a `CaseOf` that does NOT match on ${scrutinee.name}") def rec(cases: CaseBranches): (CaseBranches, CaseBranches) = cases match { case NoCases => - println("found the end, stop") + println("no cases, STOP") NoCases -> NoCases case wildcard @ Wildcard(body) => println("found a wildcard, stop") - val (y, n) = disentangle(body, scrutinee, className) - wildcard.copy(body = y) -> n.fold(NoCases: CaseBranches)(Wildcard(_)) + val (n, y) = disentangle(body, scrutinee, classSymbol) + (wildcard.copy(body = n), y.fold(NoCases: CaseBranches)(Wildcard(_))) case kase @ Case(_, body, rest) => println(s"found a case branch") - val (y1, n1) = disentangle(body, scrutinee, className) - val (y2, n2) = rec(rest) - kase.copy(body = y1, rest = y2) -> (n1 match { - case S(term) => kase.copy(body = term, rest = n2) - case N => n2 - }) + val (n1, y1) = disentangle(body, scrutinee, classSymbol) + val (n2, y2) = rec(rest) + (kase.copy(body = n1, rest = n2), (y1 match { + case S(term) => kase.copy(body = term, rest = y2) + case N => y2 + })) } - val (y, n) = rec(cases) - top.copy(cases = y) -> (if (n === NoCases) N else S(top.copy(cases = n))) + val (n, y) = rec(cases) + (top.copy(cases = y), (if (n === NoCases) N else S(top.copy(cases = n)))) } case let @ Let(_, _, _, body) => - val (y, n) = disentangle(body, scrutinee, className) - let.copy(body = y) -> n.map(t => let.copy(body = t)) - case other => println(s"CANNOT disentangle"); other -> N + val (n, y) = disentangle(body, scrutinee, classSymbol) + (let.copy(body = n), y.map(t => let.copy(body = t))) + case other => + println(s"cannot disentangle ${inspect.shallow(other)}. STOP") + other -> N } - }({ case (y, n) => s"disentangle ==> `${shortName(y)}` and `${n.fold("_")(shortName)}`" }) + }({ case (n, y) => s"disentangle ==> `${inspect.deep(n)}` and `${y.fold("")(inspect.deep(_))}`" }) def cascadeConsecutiveCaseOf(term: Term): Term = trace(s"cascade consecutive CaseOf <== ${term.describe}") { // Normalized terms are constructed using `Let` and `CaseOf`. diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index 85952793..cfa6b4d1 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -45,6 +45,12 @@ trait Transformation { self: mlscript.pretyper.Traceable => } case _ => rare } + case IfOpApp(lhs, Var("and"), rhs) => + splitAnd(lhs).foldRight(transformIfBody(rhs)) { + case (OperatorIs(scrutinee, pattern), acc) => + TermBranch.Match(scrutinee, PatternBranch(transformPattern(pattern), acc) |> Split.single) |> Split.single + case (test, acc) => TermBranch.Boolean(test, acc) |> Split.single + } case IfOpApp(lhs, op, rhs) => splitAnd(lhs) match { case init :+ last => diff --git a/shared/src/main/scala/mlscript/ucs/stages/package.scala b/shared/src/main/scala/mlscript/ucs/stages/package.scala index 7e23850f..2260c70d 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/package.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/package.scala @@ -1,7 +1,7 @@ package mlscript.ucs -import mlscript.{Term, Var} -import mlscript.pretyper.ScrutineeSymbol +import mlscript.{Lit, Term, Var} +import mlscript.pretyper.symbol._ import mlscript.utils._, shorthands._ package object stages { @@ -23,4 +23,18 @@ package object stages { } private[stages] final case object Yes extends MatchOrNot private[stages] final case object No extends MatchOrNot + + sealed abstract class CasePattern { + override def toString(): String = this match { + case CasePattern.Class(symbol) => symbol.name + case CasePattern.Boolean(value) => value.toString + case CasePattern.Literal(value) => value.toString + } + } + + object CasePattern { + final case class Class(symbol: TypeSymbol) extends CasePattern + final case class Boolean(value: Boolean) extends CasePattern + final case class Literal(value: Lit) extends CasePattern + } } diff --git a/shared/src/main/scala/mlscript/utils/inspect.scala b/shared/src/main/scala/mlscript/utils/inspect.scala index 3e4fa7d9..5a88c1f3 100644 --- a/shared/src/main/scala/mlscript/utils/inspect.scala +++ b/shared/src/main/scala/mlscript/utils/inspect.scala @@ -76,7 +76,7 @@ object inspect { case Wildcard(body) => s"Wildcard(${apply(body)})" case NoCases => "NoCases" } - s"CaseOf(${apply(trm)}, ${inspectCaseBranches(cases)}" + s"CaseOf(${apply(trm)}, ${inspectCaseBranches(cases)})" case IntLit(value) => s"IntLit($value)" case DecLit(value) => s"DecLit($value)" case StrLit(value) => s"StrLit($value)" diff --git a/shared/src/test/diff/mlscript/Repro.mls b/shared/src/test/diff/mlscript/Repro.mls index 97fb1eac..bf3a52e7 100644 --- a/shared/src/test/diff/mlscript/Repro.mls +++ b/shared/src/test/diff/mlscript/Repro.mls @@ -1,17 +1 @@ -:NewDefs :PreTyper - -class Point(x: Int, y: Int) -//│ class Point(x: Int, y: Int) - -fun length(p) = if p is Point(x, y) then x + y -//│ fun length: Point -> Int - -let xy = Point.unapply(Point(0, 1)) -xy.1 -//│ let xy: [Int, Int] -//│ Int -//│ xy -//│ = [ 0, 1 ] -//│ res -//│ = 1 diff --git a/shared/src/test/diff/pretyper/Declarations.mls b/shared/src/test/diff/pretyper/Declarations.mls index 3ba12d95..450ef6e3 100644 --- a/shared/src/test/diff/pretyper/Declarations.mls +++ b/shared/src/test/diff/pretyper/Declarations.mls @@ -2,88 +2,20 @@ :PreTyper :NoJS -:dpt + fun test(x, y) = x + y -//│ process <== : {fun test} -//│ | traverseTypingUnit <== : {fun test} -//│ | | 1. scope = {test} -//│ | | 2. scope = {test} -//│ | | traverseFunction <== test -//│ | | | traverseTerm <== Lam(_, _) -//│ | | | | traverseTerm <== App(_, _) -//│ | | | | | traverseTerm <== Var("+") -//│ | | | | | | traverseVar(name = "+") -//│ | | | | | | | resolveVar(name = "+") -//│ | | | | | traverseTerm ==> Var("+") -//│ | | | | | traverseTerm <== Tup(_, _) -//│ | | | | | | traverseTerm <== Var("x") -//│ | | | | | | | traverseVar(name = "x") -//│ | | | | | | | | resolveVar(name = "x") -//│ | | | | | | traverseTerm ==> Var("x") -//│ | | | | | | traverseTerm <== Var("y") -//│ | | | | | | | traverseVar(name = "y") -//│ | | | | | | | | resolveVar(name = "y") -//│ | | | | | | traverseTerm ==> Var("y") -//│ | | | | | traverseTerm ==> Tup(_, _) -//│ | | | | traverseTerm ==> App(_, _) -//│ | | | traverseTerm ==> Lam(_, _) -//│ | | traverseFunction ==> test -//│ | traverseTypingUnit ==> test -//│ process ==> test //│ fun test: (Int, Int) -> Int -:dpt + // Functions are hoisted. let y = id(42) fun id(x) = x -//│ process <== : {let y; fun id} -//│ | traverseTypingUnit <== : {let y; fun id} -//│ | | 1. scope = {id} -//│ | | traverseLetBinding(rec = false, y) -//│ | | 2. scope = {id} -//│ | | traverseFunction <== id -//│ | | | traverseTerm <== Lam(_, _) -//│ | | | | traverseTerm <== Var("x") -//│ | | | | | traverseVar(name = "x") -//│ | | | | | | resolveVar(name = "x") -//│ | | | | traverseTerm ==> Var("x") -//│ | | | traverseTerm ==> Lam(_, _) -//│ | | traverseFunction ==> id -//│ | traverseTypingUnit ==> id, y -//│ process ==> id, y //│ let y: 42 | 'a //│ fun id: forall 'b. ('a & 'b) -> (42 | 'b) -:dpt + // Function bodies can access variables declare after them. fun q(x) = x + p let p = 0 -//│ process <== : {fun q; let p} -//│ | traverseTypingUnit <== : {fun q; let p} -//│ | | 1. scope = {q} -//│ | | traverseLetBinding(rec = false, p) -//│ | | 2. scope = {q} -//│ | | traverseFunction <== q -//│ | | | traverseTerm <== Lam(_, _) -//│ | | | | traverseTerm <== App(_, _) -//│ | | | | | traverseTerm <== Var("+") -//│ | | | | | | traverseVar(name = "+") -//│ | | | | | | | resolveVar(name = "+") -//│ | | | | | traverseTerm ==> Var("+") -//│ | | | | | traverseTerm <== Tup(_, _) -//│ | | | | | | traverseTerm <== Var("x") -//│ | | | | | | | traverseVar(name = "x") -//│ | | | | | | | | resolveVar(name = "x") -//│ | | | | | | traverseTerm ==> Var("x") -//│ | | | | | | traverseTerm <== Var("p") -//│ | | | | | | | traverseVar(name = "p") -//│ | | | | | | | | resolveVar(name = "p") -//│ | | | | | | traverseTerm ==> Var("p") -//│ | | | | | traverseTerm ==> Tup(_, _) -//│ | | | | traverseTerm ==> App(_, _) -//│ | | | traverseTerm ==> Lam(_, _) -//│ | | traverseFunction ==> q -//│ | traverseTypingUnit ==> q, p -//│ process ==> q, p //│ fun q: Int -> Int //│ let p: 0 diff --git a/shared/src/test/diff/pretyper/Repro.mls b/shared/src/test/diff/pretyper/Repro.mls new file mode 100644 index 00000000..bf3a52e7 --- /dev/null +++ b/shared/src/test/diff/pretyper/Repro.mls @@ -0,0 +1 @@ +:PreTyper diff --git a/shared/src/test/diff/pretyper/ucs/DualOption.mls b/shared/src/test/diff/pretyper/ucs/DualOption.mls index 4460835a..e6e45799 100644 --- a/shared/src/test/diff/pretyper/ucs/DualOption.mls +++ b/shared/src/test/diff/pretyper/ucs/DualOption.mls @@ -1,13 +1,13 @@ :NewDefs :PreTyper -class Some[T](value: T) -module None -type Option[T] = Some[T] | None +abstract class Option[T] +class Some[T](value: T) extends Option[T] +module None extends Option[nothing] class Pair[A, B](x: A, y: B) -//│ class Some[T](value: T) -//│ module None -//│ type Option[T] = None | Some[T] +//│ abstract class Option[T] +//│ class Some[T](value: T) extends Option +//│ module None extends Option //│ class Pair[A, B](x: A, y: B) // All `add_n` functions should be inferred to have the same type. diff --git a/shared/src/test/diff/pretyper/ucs/Overlaps.mls b/shared/src/test/diff/pretyper/ucs/Overlaps.mls new file mode 100644 index 00000000..eb65c35e --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/Overlaps.mls @@ -0,0 +1,76 @@ +:PreTyper + +class Point(x: Int, y: Int) +abstract class Shape: (Circle | Rectangle | LineSegment) +class Circle(center: Point, radius: Int) extends Shape +class Rectangle(position: Point, width: Int, height: Int) extends Shape +class LineSegment(start: Point, end: Point) extends Shape +//│ class Point(x: Int, y: Int) +//│ abstract class Shape: Circle | LineSegment | Rectangle +//│ class Circle(center: Point, radius: Int) extends Shape +//│ class Rectangle(position: Point, width: Int, height: Int) extends Shape +//│ class LineSegment(start: Point, end: Point) extends Shape + +fun hidden(p) = if p is Circle then true else false +//│ fun hidden: Object -> Bool + +fun this_is_sealed(x: Shape) = + if x is + Circle(_, _) then "Circle" + Rectangle(_, _, _) then "Rectangle" + LineSegment(_, _) then "LineSegment" +//│ fun this_is_sealed: (x: Shape) -> ("Circle" | "LineSegment" | "Rectangle") + +// TODO +fun missing_a_case(x: Shape) = + if x is + Circle(_, _) then "Circle" + Rectangle(_, _, _) then "Rectangle" +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.26: if x is +//│ ║ ^^^^ +//│ ║ l.27: Circle(_, _) then "Circle" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.28: Rectangle(_, _, _) then "Rectangle" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type `LineSegment` is not an instance of type `Rectangle` +//│ ║ l.25: fun missing_a_case(x: Shape) = +//│ ║ ^^^^^ +//│ ╟── but it flows into reference with expected type `Rectangle` +//│ ║ l.26: if x is +//│ ║ ^ +//│ ╟── Note: constraint arises from class pattern: +//│ ║ l.28: Rectangle(_, _, _) then "Rectangle" +//│ ╙── ^^^^^^^^^ +//│ fun missing_a_case: (x: Shape) -> ("Circle" | "Rectangle") + +fun missing_a_case(x: Shape) = + if x is + Circle(_, _) then "Circle" + Rectangle(_, _, _) then "Rectangle" + else x +//│ fun missing_a_case: (x: Shape) -> ("Circle" | "Rectangle" | LineSegment) + +// TODO +fun countLineSegments(x) = + if x is + Shape and hidden(x) then "bro" + Rectangle(_, _, _) then "bro" + LineSegment(_, _) then "bro" + Circle(_, _) then "bro" +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.56: if x is +//│ ║ ^^^^ +//│ ║ l.57: Shape and hidden(x) then "bro" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.58: Rectangle(_, _, _) then "bro" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.59: LineSegment(_, _) then "bro" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.60: Circle(_, _) then "bro" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── expression of type `Shape & ~#LineSegment & ~#Rectangle` is not an instance of type `Circle` +//│ ╟── Note: constraint arises from class pattern: +//│ ║ l.60: Circle(_, _) then "bro" +//│ ╙── ^^^^^^ +//│ fun countLineSegments: Shape -> "bro" diff --git a/shared/src/test/diff/pretyper/ucs/coverage/MissingCases.mls b/shared/src/test/diff/pretyper/ucs/coverage/MissingCases.mls index b0050498..099f9dba 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/MissingCases.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/MissingCases.mls @@ -9,37 +9,133 @@ class Pair[A, B](x: A, y: B) //│ type Option[T] = None | Some[T] //│ class Pair[A, B](x: A, y: B) -fun failed_add_1(x, y) = +fun good_add_1(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv x is Some(xv) and y is None then xv x is None and y is Some(yv) then yv -//│ ╔══[ERROR] missing a case where x is None, and y is None -//│ ╟── missing the condition -//│ ║ l.15: x is Some(xv) and y is None then xv -//│ ║ ^^^^ -//│ ╟── when x is None -//│ ║ l.16: x is None and y is Some(yv) then yv -//│ ╙── ^^^^ -//│ fun failed_add_1: forall 'a. (None | Some[Int], Some[Int & 'a]) -> (Int | 'a) + x is None and y is None then 0 +//│ fun good_add_1: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) -fun failed_add_2(x, y) = +:e +fun bad_add_missing_SS(x, y) = if x is Some(xv) and y is None then xv x is None and y is Some(yv) then yv -//│ ╔══[ERROR] missing a case where x is Some, and y is Some -//│ ╟── missing the condition -//│ ║ l.29: x is None and y is Some(yv) then yv -//│ ║ ^^^^ -//│ ╟── when x is Some -//│ ║ l.28: x is Some(xv) and y is None then xv -//│ ╙── ^^^^ -//│ ╔══[ERROR] missing a case where x is None, and y is None -//│ ╟── missing the condition -//│ ║ l.28: x is Some(xv) and y is None then xv -//│ ║ ^^^^ -//│ ╟── when x is None -//│ ║ l.29: x is None and y is Some(yv) then yv -//│ ╙── ^^^^ -//│ fun failed_add_2: forall 'a. (None | Some['a], nothing) -> 'a + x is None and y is None then 0 +//│ ╔══[ERROR] When scrutinee `x` is `Some` +//│ ║ l.23: x is Some(xv) and y is None then xv +//│ ║ ^^^^ +//│ ╟── Scrutinee `y` has 1 missing case +//│ ║ l.21: fun bad_add_missing_SS(x, y) = +//│ ║ ^ +//│ ╟── It can be class `Some` +//│ ║ l.24: x is None and y is Some(yv) then yv +//│ ╙── ^^^^ +//│ fun bad_add_missing_SS: forall 'a. (None | Some['a], None) -> (0 | 'a) +:e +fun bad_add_missing_SN(x, y) = + if + x is Some(xv) and y is Some(yv) then xv + yv + x is None and y is Some(yv) then yv + x is None and y is None then 0 +//│ ╔══[ERROR] When scrutinee `x` is `Some` +//│ ║ l.40: x is Some(xv) and y is Some(yv) then xv + yv +//│ ║ ^^^^ +//│ ╟── Scrutinee `y` has 1 missing case +//│ ║ l.38: fun bad_add_missing_SN(x, y) = +//│ ║ ^ +//│ ╟── It can be module `None` +//│ ║ l.42: x is None and y is None then 0 +//│ ╙── ^^^^ +//│ fun bad_add_missing_SN: forall 'a. (None | Some[Int], Some[Int & 'a]) -> (Int | 'a) + +:e +fun bad_add_missing_NS(x, y) = + if + x is Some(xv) and y is Some(yv) then xv + yv + x is Some(xv) and y is None then xv + x is None and y is None then 0 +//│ ╔══[ERROR] When scrutinee `x` is `None` +//│ ║ l.59: x is None and y is None then 0 +//│ ║ ^^^^ +//│ ╟── Scrutinee `y` has 1 missing case +//│ ║ l.55: fun bad_add_missing_NS(x, y) = +//│ ║ ^ +//│ ╟── It can be class `Some` +//│ ║ l.57: x is Some(xv) and y is Some(yv) then xv + yv +//│ ╙── ^^^^ +//│ fun bad_add_missing_NS: (None | Some[Int], None) -> Int + +:e +fun bad_add_missing_NN(x, y) = + if + x is Some(xv) and y is Some(yv) then xv + yv + x is Some(xv) and y is None then xv + x is None and y is Some(yv) then yv +//│ ╔══[ERROR] When scrutinee `x` is `None` +//│ ║ l.76: x is None and y is Some(yv) then yv +//│ ║ ^^^^ +//│ ╟── Scrutinee `y` has 1 missing case +//│ ║ l.72: fun bad_add_missing_NN(x, y) = +//│ ║ ^ +//│ ╟── It can be module `None` +//│ ║ l.75: x is Some(xv) and y is None then xv +//│ ╙── ^^^^ +//│ fun bad_add_missing_NN: forall 'a. (None | Some[Int], Some[Int & 'a]) -> (Int | 'a) + +:e +fun bad_add_missing_SS_NN(x, y) = + if + x is Some(xv) and y is None then xv + x is None and y is Some(yv) then yv +//│ ╔══[ERROR] When scrutinee `x` is `Some` +//│ ║ l.91: x is Some(xv) and y is None then xv +//│ ║ ^^^^ +//│ ╟── Scrutinee `y` has 1 missing case +//│ ║ l.89: fun bad_add_missing_SS_NN(x, y) = +//│ ║ ^ +//│ ╟── It can be class `Some` +//│ ║ l.92: x is None and y is Some(yv) then yv +//│ ╙── ^^^^ +//│ ╔══[ERROR] When scrutinee `x` is `None` +//│ ║ l.92: x is None and y is Some(yv) then yv +//│ ║ ^^^^ +//│ ╟── Scrutinee `y` has 1 missing case +//│ ║ l.89: fun bad_add_missing_SS_NN(x, y) = +//│ ║ ^ +//│ ╟── It can be module `None` +//│ ║ l.91: x is Some(xv) and y is None then xv +//│ ╙── ^^^^ +//│ fun bad_add_missing_SS_NN: forall 'a. (None | Some['a], nothing) -> 'a + +:e +fun bad_add_missing_SN_NS(x, y) = + if + x is Some(xv) and y is Some(yv) then xv + yv + x is None and y is None then 0 +//│ ╔══[ERROR] When scrutinee `x` is `Some` +//│ ║ l.116: x is Some(xv) and y is Some(yv) then xv + yv +//│ ║ ^^^^ +//│ ╟── Scrutinee `y` has 1 missing case +//│ ║ l.114: fun bad_add_missing_SN_NS(x, y) = +//│ ║ ^ +//│ ╟── It can be module `None` +//│ ║ l.117: x is None and y is None then 0 +//│ ╙── ^^^^ +//│ ╔══[ERROR] When scrutinee `x` is `None` +//│ ║ l.117: x is None and y is None then 0 +//│ ║ ^^^^ +//│ ╟── Scrutinee `y` has 1 missing case +//│ ║ l.114: fun bad_add_missing_SN_NS(x, y) = +//│ ║ ^ +//│ ╟── It can be class `Some` +//│ ║ l.116: x is Some(xv) and y is Some(yv) then xv + yv +//│ ╙── ^^^^ +//│ fun bad_add_missing_SN_NS: (None | Some[Int], nothing) -> Int + +fun actually_fine_add(x, y) = + if + x is Some(xv) and y is None then xv +//│ fun actually_fine_add: forall 'a. (Some['a], None) -> 'a diff --git a/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls b/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls new file mode 100644 index 00000000..996388f0 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls @@ -0,0 +1,58 @@ +:PreTyper + +abstract class Term: Abs | App | Var +class Var(name: Str) extends Term +class Abs(param: Str, body: Term) extends Term +class App(func: Term, arg: Term) extends Term +//│ abstract class Term: Abs | App | Var +//│ class Var(name: Str) extends Term +//│ class Abs(param: Str, body: Term) extends Term +//│ class App(func: Term, arg: Term) extends Term + +// FIXME +fun is_value(term) = + if term is Term and term is + Abs(_, _) then true + Var(_) then false + App(_, _) then false +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.14: if term is Term and term is +//│ ║ ^^^^^^^ +//│ ║ l.15: Abs(_, _) then true +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.16: Var(_) then false +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.17: App(_, _) then false +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── expression of type `Term & ~#Abs & ~#Var` is not an instance of type `App` +//│ ╟── Note: constraint arises from class pattern: +//│ ║ l.17: App(_, _) then false +//│ ╙── ^^^ +//│ fun is_value: Term -> Bool + +:e +fun is_value(term) = + if term is Term and term is + Abs(_, _) then true + Var(_) then false +//│ ╔══[ERROR] When scrutinee `term` is `Term` +//│ ║ l.35: if term is Term and term is +//│ ║ ^^^^ +//│ ╟── Scrutinee `term` has 1 missing case +//│ ║ l.34: fun is_value(term) = +//│ ║ ^^^^ +//│ ╟── It can be class `App` +//│ ║ l.6: class App(func: Term, arg: Term) extends Term +//│ ╙── ^^^ +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.35: if term is Term and term is +//│ ║ ^^^^^^^ +//│ ║ l.36: Abs(_, _) then true +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.37: Var(_) then false +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── expression of type `Term & ~#Abs` is not an instance of type `Var` +//│ ╟── Note: constraint arises from class pattern: +//│ ║ l.37: Var(_) then false +//│ ╙── ^^^ +//│ fun is_value: Term -> Bool diff --git a/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls b/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls index ad944a17..a4a1e897 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls @@ -41,7 +41,6 @@ reachable_1(None) //│ res //│ = 'tan' -:dpt fun unreachable_1(x) = if x is _ and @@ -49,145 +48,4 @@ fun unreachable_1(x) = else "screen" Some(xv) then "sin" None then "tan" -//│ process <== : {fun unreachable_1} -//│ | traverseTypingUnit <== : {fun unreachable_1} -//│ | | 1. scope = {unreachable_1} -//│ | | 2. scope = {unreachable_1} -//│ | | traverseFunction <== unreachable_1 -//│ | | | traverseTerm <== Lam(_, _) -//│ | | | | traverseTerm <== Blk(_) -//│ | | | | | traverseTerm <== If(_) -//│ | | | | | | traverseIf -//│ | | | | | | | STEP 0 -//│ | | | | | | | Transformed UCS term: -//│ | | | | | | | if x is -//│ | | | | | | | _ and f(x,) then "tmux" -//│ | | | | | | | else "screen" -//│ | | | | | | | Some(xv) then "sin" -//│ | | | | | | | None then "tan" -//│ | | | | | | | STEP 1 -//│ | | | | | | | Desugared UCS term: -//│ | | | | | | | if -//│ | | | | | | | x is _ -//│ | | | | | | | let scrut$0 = f(x,) : Bool -//│ | | | | | | | scrut$0 is true then "tmux" -//│ | | | | | | | else "screen" -//│ | | | | | | | x is Some(xv) then "sin" -//│ | | | | | | | x is None then "tan" -//│ | | | | | | | traverseSplit <== [unreachable_1, x] -//│ | | | | | | | | found branch: x is _ -//│ | | | | | | | | traverseTerm <== Var("x") -//│ | | | | | | | | | traverseVar(name = "x") -//│ | | | | | | | | | | resolveVar(name = "x") -//│ | | | | | | | | traverseTerm ==> Var("x") -//│ | | | | | | | | traversePattern <== _ -//│ | | | | | | | | traversePattern ==> [_] -//│ | | | | | | | | traverseSplit <== [unreachable_1, x, _] -//│ | | | | | | | | | found let binding: "scrut$0" -//│ | | | | | | | | | traverseSplit <== [unreachable_1, x, _, scrut$0] -//│ | | | | | | | | | | found branch: scrut$0 is true -//│ | | | | | | | | | | traverseTerm <== Var("scrut$0") -//│ | | | | | | | | | | | traverseVar(name = "scrut$0") -//│ | | | | | | | | | | | | resolveVar(name = "scrut$0") -//│ | | | | | | | | | | traverseTerm ==> Var("scrut$0") -//│ | | | | | | | | | | traversePattern <== true -//│ | | | | | | | | | | traversePattern ==> [] -//│ | | | | | | | | | | traverseSplit <== [unreachable_1, x, _, scrut$0] -//│ | | | | | | | | | | | traverseTerm <== "tmux" -//│ | | | | | | | | | | | traverseTerm ==> "tmux" -//│ | | | | | | | | | | traverseSplit <== [unreachable_1, x, _, scrut$0] -//│ | | | | | | | | | | | traverseTerm <== "screen" -//│ | | | | | | | | | | | traverseTerm ==> "screen" -//│ | | | | | | | | traverseSplit <== [unreachable_1, x] -//│ | | | | | | | | | found branch: x is Some(xv) -//│ | | | | | | | | | traverseTerm <== Var("x") -//│ | | | | | | | | | | traverseVar(name = "x") -//│ | | | | | | | | | traverseTerm ==> Var("x") -//│ | | | | | | | | | traversePattern <== Some(xv) -//│ | | | | | | | | | traversePattern ==> [xv] -//│ | | | | | | | | | traverseSplit <== [unreachable_1, x, xv] -//│ | | | | | | | | | | traverseTerm <== "sin" -//│ | | | | | | | | | | traverseTerm ==> "sin" -//│ | | | | | | | | | traverseSplit <== [unreachable_1, x] -//│ | | | | | | | | | | found branch: x is None -//│ | | | | | | | | | | traverseTerm <== Var("x") -//│ | | | | | | | | | | | traverseVar(name = "x") -//│ | | | | | | | | | | traverseTerm ==> Var("x") -//│ | | | | | | | | | | traversePattern <== None -//│ | | | | | | | | | | traversePattern ==> [] -//│ | | | | | | | | | | traverseSplit <== [unreachable_1, x] -//│ | | | | | | | | | | | traverseTerm <== "tan" -//│ | | | | | | | | | | | traverseTerm ==> "tan" -//│ | | | | | | | | | | traverseSplit <== [unreachable_1, x] -//│ | | | | | | | | | | | the end -//│ | | | | | | | STEP 2 -//│ | | | | | | | normalizeToTerm -//│ | | | | | | | | alias x => _ -//│ | | | | | | | | normalizeToTerm -//│ | | | | | | | | | normalizeToTerm -//│ | | | | | | | | | | match scrut$0 with true -//│ | | | | | | | | | | S+ <== scrut$0 is true -//│ | | | | | | | | | | | the end -//│ | | | | | | | | | | S+ ==> then "tmux" -//│ | | | | | | | | | | normalizeToTerm -//│ | | | | | | | | | | S- <== scrut$0 is true -//│ | | | | | | | | | | | the end -//│ | | | | | | | | | | S- ==> then "screen" -//│ | | | | | | | | | | normalizeToCaseBranches -//│ | | | | | | | Normalized UCS term: -//│ | | | | | | | let _* = x -//│ | | | | | | | let scrut$0* = f(x,) : Bool -//│ | | | | | | | scrut$0 match -//│ | | | | | | | case true => "tmux" -//│ | | | | | | | case _ => "screen" -//│ | | | | | | | STEP 3 -//│ | | | | | | | postProcess <== Let(_, _) -//│ | | | | | | | | postProcess <== Let(_, _) -//│ | | | | | | | | | postProcess <== CaseOf(_, _) -//│ | | | | | | | | | | found a BINARY case: scrut$0 is true -//│ | | | | | | | | | | `scrut$0`'s matched classes: [true] -//│ | | | | | | | | | | post-processing the first branch -//│ | | | | | | | | | | postProcess <== "tmux" -//│ | | | | | | | | | | | CANNOT post-process -//│ | | | | | | | | | | postProcess ==> -//│ | | | | | | | | | | found 0 cases -//│ | | | | | | | | | postProcess ==> -//│ | | | | | | | | postProcess ==> -//│ | | | | | | | postProcess ==> -//│ | | | | | | | Post-processed UCS term: -//│ | | | | | | | let _* = x -//│ | | | | | | | let scrut$0* = f(x,) : Bool -//│ | | | | | | | scrut$0 match -//│ | | | | | | | case true => "tmux" -//│ | | | | | | | case _ => "screen" -//│ | | | | | | | STEP 4 -//│ | | | | | | | collected match registry: -//│ | | | | | | | >>> scrut$0 => [true] -//│ | | | | | | | checkCoverage <== Let(_, _), 0 pending, 1 working -//│ | | | | | | | | assumptions: empty -//│ | | | | | | | | checkCoverage <== Let(_, _), 0 pending, 1 working -//│ | | | | | | | | | assumptions: empty -//│ | | | | | | | | | checkCoverage <== CaseOf(_, _), 0 pending, 1 working -//│ | | | | | | | | | | assumptions: empty -//│ | | | | | | | | | | scrutinee: scrut$0 -//│ | | | | | | | | | | checkCoverage <== "tmux", 0 pending, 0 working -//│ | | | | | | | | | | | assumptions: scrut$0 is true -//│ | | | | | | | | | | | STOP -//│ | | | | | | | | | | checkCoverage ==> 0 -//│ | | | | | | | | | | wildcard case -//│ | | | | | | | | | | checkCoverage <== "screen", 0 pending, 1 working -//│ | | | | | | | | | | | assumptions: empty -//│ | | | | | | | | | | | STOP -//│ | | | | | | | | | | checkCoverage ==> 0 -//│ | | | | | | | | | checkCoverage ==> 0 -//│ | | | | | | | | checkCoverage ==> 0 -//│ | | | | | | | checkCoverage ==> 0 -//│ | | | | | | | Coverage checking result: 0 errors -//│ | | | | | | traverseIf ==> () -//│ | | | | | traverseTerm ==> If(_) -//│ | | | | traverseTerm ==> Blk(_) -//│ | | | traverseTerm ==> Lam(_, _) -//│ | | traverseFunction ==> unreachable_1 -//│ | traverseTypingUnit ==> unreachable_1 -//│ process ==> unreachable_1 //│ fun unreachable_1: (Object & ~#Some | Some[Eql[1]]) -> ("screen" | "tmux") diff --git a/shared/src/test/diff/pretyper/ucs/examples/EitherOrBoth.mls b/shared/src/test/diff/pretyper/ucs/examples/EitherOrBoth.mls new file mode 100644 index 00000000..fbeeeda7 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/examples/EitherOrBoth.mls @@ -0,0 +1,100 @@ +:PreTyper + +abstract class Option[out T]: (Some[T] | None) +class Some[out T](value: T) extends Option[T] +module None extends Option[nothing] +//│ abstract class Option[T]: None | Some[T] +//│ class Some[T](value: T) extends Option +//│ module None extends Option + +abstract class EitherOrBoth[out A, out B]: (Left[A, B] | Right[A, B] | Both[A, B]) +class Left[out A, out B](value: A) extends EitherOrBoth[A, B] +class Right[out A, out B](value: B) extends EitherOrBoth[A, B] +class Both[out A, out B](left: A, right: B) extends EitherOrBoth[A, B] +//│ abstract class EitherOrBoth[A, B]: Both[A, B] | Left[A, B] | Right[A, B] +//│ class Left[A, B](value: A) extends EitherOrBoth +//│ class Right[A, B](value: B) extends EitherOrBoth +//│ class Both[A, B](left: A, right: B) extends EitherOrBoth + +type Either[A, B] = Left[A, B] | Right[A, B] +//│ type Either[A, B] = Left[A, B] | Right[A, B] + +fun getLeft[A, B](eob: EitherOrBoth[A, B]): Option[A] = + if eob is + Left(left) then Some(left) + Right(_) then None + Both(left, _) then Some(left) +//│ fun getLeft: forall 'A. (eob: EitherOrBoth['A, anything]) -> Option['A] + +fun getRight[A, B](eob: EitherOrBoth[A, B]): Option[B] = + if eob is + Left(_) then None + Right(right) then Some(right) + Both(_, right) then Some(right) +//│ fun getRight: forall 'B. (eob: EitherOrBoth[anything, 'B]) -> Option['B] + +fun getBoth[A, B](eob: EitherOrBoth[A, B]): Option[[A, B]] = + if eob is + Left(_) then None + Right(_) then None + Both(left, right) then Some([left, right]) +//│ fun getBoth: forall 'A 'B. (eob: EitherOrBoth['A, 'B]) -> Option[['A, 'B]] + +fun mapLeft[A, B, C](eob: EitherOrBoth[A, B], f: A -> C): EitherOrBoth[C, B] = + if eob is + Left(left) then Left(f(left)) + Right(right) then Right(right) + Both(left, right) then Both(f(left), right) +//│ fun mapLeft: forall 'A 'B 'C. (eob: EitherOrBoth['A, 'B], f: 'A -> 'C) -> EitherOrBoth['C, 'B] + +fun mapRight[A, B, C](eob: EitherOrBoth[A, B], f: B -> C): EitherOrBoth[A, C] = + if eob is + Left(left) then Left(left) + Right(right) then Right(f(right)) + Both(left, right) then Both(left, f(right)) +//│ fun mapRight: forall 'A 'B 'C. (eob: EitherOrBoth['A, 'B], f: 'B -> 'C) -> EitherOrBoth['A, 'C] + +fun map[A, B, C, D](eob: EitherOrBoth[A, B], f: A -> C, g: B -> D): EitherOrBoth[C, D] = + if eob is + Left(left) then Left(f(left)) + Right(right) then Right(g(right)) + Both(left, right) then Both(f(left), g(right)) +//│ fun map: forall 'A 'B 'C 'D. (eob: EitherOrBoth['A, 'B], f: 'A -> 'C, g: 'B -> 'D) -> EitherOrBoth['C, 'D] + +fun fold[A, B, C](eob: EitherOrBoth[A, B], f: A -> C, g: B -> C, h: [A, B] -> C): C = + if eob is + Left(left) then f(left) + Right(right) then g(right) + Both(left, right) then h(left, right) +//│ fun fold: forall 'A 'B 'C. (eob: EitherOrBoth['A, 'B], f: 'A -> 'C, g: 'B -> 'C, h: ('A, 'B) -> 'C) -> 'C + +fun isLeft[A, B](eob: EitherOrBoth[A, B]): Bool = + if eob is + Left(_) then true + Right(_) then false + Both(_, _) then false +//│ fun isLeft: (eob: EitherOrBoth[anything, anything]) -> Bool + +fun isRight[A, B](eob: EitherOrBoth[A, B]): Bool = + if eob is + Left(_) then false + Right(_) then true + Both(_, _) then false +//│ fun isRight: (eob: EitherOrBoth[anything, anything]) -> Bool + +fun isBoth[A, B](eob: EitherOrBoth[A, B]): Bool = + if eob is + Left(_) then false + Right(_) then false + Both(_, _) then true +//│ fun isBoth: (eob: EitherOrBoth[anything, anything]) -> Bool + +fun (++) strcat(a: Str, b: Str): Str = concat(a)(b) +//│ fun (++) strcat: (a: Str, b: Str) -> Str + +fun eobToString[A, B](eob: EitherOrBoth[A, B]): Str = + if eob is + Left(left) then "Left(" ++ toString(left) ++ ")" + Right(right) then "Right(" ++ toString(right) ++ ")" + Both(left, right) then "Both(" ++ toString(left) ++ ", " ++ toString(right) ++ ")" +//│ fun eobToString: (eob: EitherOrBoth[anything, anything]) -> Str diff --git a/shared/src/test/diff/pretyper/ucs/examples/Option.mls b/shared/src/test/diff/pretyper/ucs/examples/Option.mls index 22207938..70756855 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/Option.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/Option.mls @@ -1,42 +1,27 @@ :PreTyper -class Some[T](value: T) -module None -type Option[T] = Some[T] | None -//│ class Some[T](value: T) -//│ module None -//│ type Option[T] = None | Some[T] - -fun getOrElse[T](self: Option[T], default: T): T = - if self is - Some(value) then value - None then default -//│ fun getOrElse: forall 'T. (self: Option['T], default: 'T) -> 'T - -getOrElse(None, 0) -getOrElse(None, "hello") -getOrElse(None, true) -//│ true -//│ res -//│ = 0 -//│ res -//│ = 'hello' -//│ res -//│ = true - -getOrElse(Some(true), false) -//│ Bool -//│ res -//│ = true - -fun map[T, U](self: Option[T], f: (T) -> U): Option[U] = - if self is - Some(value) then Some(f(value)) - None then None -//│ fun map: forall 'T 'U. (self: Option['T], f: 'T -> 'U) -> Option['U] - -fun flatMap[T, U](self: Option[T], f: (T) -> Option[U]): Option[U] = - if self is - Some(value) then f(value) - None then None -//│ fun flatMap: forall 'T 'U. (self: Option['T], f: 'T -> Option['U]) -> Option['U] +// FIXME: Finish this example after the relevant issue is fixed. +abstract class Option[out T]: (Some[T] | None) { + virtual fun filter: (p: T -> Bool) -> Option[T] +} +class Some[out T](val value: T) extends Option[T] { + fun filter(p) = if p of value then Some(value) else None +} +module None extends Option[nothing] { + fun filter(_) = None +} +//│ ╔══[ERROR] Type `#Some & {Some#T <: ?T}` does not contain member `Option#T` +//│ ║ l.4: abstract class Option[out T]: (Some[T] | None) { +//│ ╙── ^ +//│ ╔══[ERROR] Type `#None` does not contain member `Option#T` +//│ ║ l.4: abstract class Option[out T]: (Some[T] | None) { +//│ ╙── ^ +//│ abstract class Option[T]: None | Some[T] { +//│ fun filter: (p: T -> Bool) -> Option[T] +//│ } +//│ class Some[T](value: T) extends Option { +//│ fun filter: (T -> Bool) -> (None | Some[T]) +//│ } +//│ module None extends Option { +//│ fun filter: anything -> None +//│ } From 8183b6fa0aa97fe1db4d163b99b3fa42ac9842d9 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Thu, 21 Dec 2023 01:50:50 +0800 Subject: [PATCH 014/143] Update a piece of code that uses the inspect function --- compiler/shared/test/scala/mlscript/compiler/Test.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/shared/test/scala/mlscript/compiler/Test.scala b/compiler/shared/test/scala/mlscript/compiler/Test.scala index e0df0d8f..2e2349bf 100644 --- a/compiler/shared/test/scala/mlscript/compiler/Test.scala +++ b/compiler/shared/test/scala/mlscript/compiler/Test.scala @@ -3,7 +3,7 @@ package mlscript.compiler import mlscript.utils.shorthands.* import scala.util.control.NonFatal import scala.collection.mutable.StringBuilder -import mlscript.codegen.Helpers.inspect as showStructure +import mlscript.utils.inspect.deep as showStructure import mlscript.{DiffTests, ModeType, TypingUnit} import mlscript.compiler.debug.TreeDebug import mlscript.compiler.mono.Monomorph From 3e95aa1d1d2bd765ac0442c5684725614d6dade4 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Thu, 21 Dec 2023 01:53:23 +0800 Subject: [PATCH 015/143] Fix two inexhaustive pattern matching expressions --- .../src/main/scala/mlscript/ucs/stages/CoverageChecking.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala index fc157c5c..4108b807 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala @@ -200,13 +200,13 @@ object CoverageChecking { val withoutSymbol = cases - classLikePattern val relatedPatterns = withoutSymbol.filter { case (Pattern.ClassLike(otherSymbol), _) => otherSymbol.baseTypes.contains(classLikeSymbol) - case (_: Pattern.Tuple | _: Pattern.Literal) -> _ => false + case ((_: Pattern.Tuple | _: Pattern.Literal), _) => false } ++ classLikeSymbol.sealedDerivedTypes.iterator.map { symbol => Pattern.ClassLike(symbol) -> symbol.defn.nme.toLoc.toList } val unrelatedPatterns = withoutSymbol.filter { case (Pattern.ClassLike(otherSymbol), _) => !otherSymbol.baseTypes.contains(classLikeSymbol) - case (_: Pattern.Tuple | _: Pattern.Literal) -> _ => true + case ((_: Pattern.Tuple | _: Pattern.Literal), _) => true } (locations, copy(relatedPatterns), copy(unrelatedPatterns)) } From d32d9d809f6bc1177cfdd336f3ec0ceece241807 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Fri, 22 Dec 2023 01:46:22 +0800 Subject: [PATCH 016/143] Add two more interesting examples and fix unifying branch parameters --- .../scala/mlscript/pretyper/PreTyper.scala | 4 + .../main/scala/mlscript/pretyper/Symbol.scala | 6 + .../main/scala/mlscript/ucs/DesugarUCS.scala | 5 +- .../mlscript/ucs/stages/Desugaring.scala | 2 +- .../mlscript/ucs/stages/Normalization.scala | 45 +- .../mlscript/ucs/stages/PostProcessing.scala | 5 + .../mlscript/ucs/stages/Transformation.scala | 2 + .../ucs/examples/BinarySearchTree.mls | 613 ++++++++++++++++++ .../pretyper/ucs/examples/LeftistTree.mls | 185 ++++++ 9 files changed, 859 insertions(+), 8 deletions(-) create mode 100644 shared/src/test/diff/pretyper/ucs/examples/BinarySearchTree.mls create mode 100644 shared/src/test/diff/pretyper/ucs/examples/LeftistTree.mls diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index eb3da508..d6827ea2 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -15,6 +15,7 @@ class PreTyper(override val debugLevel: Opt[Int], useNewDefs: Bool) extends Trac private def extractParameters(fields: Term): Ls[ValueSymbol] = fields match { case Tup(arguments) => + println(s"arguments: ${inspect.deep(fields)}") if (useNewDefs) { arguments.map { case (S(nme: Var), Fld(_, _)) => new ValueSymbol(nme, false) @@ -229,6 +230,9 @@ class PreTyper(override val debugLevel: Opt[Int], useNewDefs: Bool) extends Trac val scopeWithVar = acc + symbol traverseLetBinding(symbol, rec, rhs)(if (rec) { scopeWithVar } else { acc }) scopeWithVar + case (acc, defn @ NuFunDef(Some(rec), nme, _, _, R(ty))) => + val symbol = new ValueSymbol(defn.nme, true) + acc + symbol case (acc, _: NuFunDef) => acc case (acc, _: Constructor | _: DataDefn | _: DatatypeDefn | _: Def | _: LetS | _: TypeDef) => ??? // TODO: When? } diff --git a/shared/src/main/scala/mlscript/pretyper/Symbol.scala b/shared/src/main/scala/mlscript/pretyper/Symbol.scala index 64d046e2..44d86efb 100644 --- a/shared/src/main/scala/mlscript/pretyper/Symbol.scala +++ b/shared/src/main/scala/mlscript/pretyper/Symbol.scala @@ -74,11 +74,17 @@ package object symbol { def toLoc: Opt[Loc] val matchedClasses: MutMap[TypeSymbol, Buffer[Loc]] = MutMap.empty + val unappliedVarMap: MutMap[TypeSymbol, Var] = MutMap.empty + // Hmm, maybe we can merge these two maps into one. def addMatchedClass(symbol: TypeSymbol, loc: Opt[Loc]): Unit = { matchedClasses.getOrElseUpdate(symbol, Buffer.empty) ++= loc } + def addUnappliedVar(symbol: TypeSymbol, nme: Var): Unit = { + unappliedVarMap += symbol -> nme + } + /** * This map contains the sub-scrutinee symbols when this scrutinee is matched * against class patterns. diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index 1b192a48..351e5b3f 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -82,8 +82,11 @@ trait DesugarUCS extends Transformation case core.Pattern.Name(nme) => nme.symbol = scrutineeSymbol nme -> scrutineeSymbol :: Nil + // case core.Pattern.Class(nme @ Var("true"), N) => + // println(s"found true pattern") + // nme -> scrutineeSymbol :: Nil case core.Pattern.Class(nme, maybeParameters) => - println(s"name has location: ${nme.toLoc.isDefined}") + println(s"`$nme` has location: ${nme.toLoc.isDefined}") // Resolve `nme`. It can either be a class, a trait, or a module. val symbol = scope.getTypeSymbol(nme.name) match { case S(symbol: TraitSymbol) => println(s"${nme.name} is a trait"); symbol diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 7a97e9ed..16fd8b01 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -24,7 +24,7 @@ trait Desugaring { self: mlscript.pretyper.Traceable => private def freshScrutinee(parentScrutinee: Var, parentClassName: Var, index: Int): Var = Var(s"${parentScrutinee}$$${parentClassName}_${index.toString}") - private val truePattern = c.Pattern.Class(Var("true"), N) + private def truePattern = c.Pattern.Class(Var("true"), N) private def flattenClassParameters(parentScrutinee: Var, parentClassName: Var, parameters: Opt[Ls[Opt[s.Pattern]]]): Opt[Ls[Opt[Var]]] -> Ls[Opt[Var -> s.Pattern]] = parameters match { diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 193933e6..36dcbe23 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -39,13 +39,25 @@ trait Normalization { self: mlscript.pretyper.Traceable => CaseOf(scrutinee, Case(nme, trueBranch, falseBranch)) case Split.Cons(Branch(scrutinee, pattern @ Pattern.Class(nme, S(parameters)), continuation), tail) => println(s"match $scrutinee with $pattern") + val unappliedVar = Var(s"args_${scrutinee.name}$$${nme.name}") + println(s"make unapplied var: $unappliedVar") + // Update the scrutinee symbol. The variable will be used in merging + // branches of the same pattern later. + scrutinee.symbol match { + case symbol: ScrutineeSymbol => + nme.symbolOption.flatMap(_.typeSymbolOption) match { + case N => throw new NormalizationException(msg"class name is not resolved" -> nme.toLoc :: Nil) + case S(typeSymbol) => + println(s"add unapplied var for ${typeSymbol.name}") + symbol.addUnappliedVar(typeSymbol, unappliedVar) + } + case _ => + // FIXME: I guess this should not happen. + throw new NormalizationException(msg"Scrutinee is not a scrutinee symbol" -> scrutinee.toLoc :: Nil) + } val trueBranch = trace("compute true branch"){ normalizeToTerm(specialize(continuation ++ tail, Yes)(scrutinee.symbol, pattern, scope)) }() - val falseBranch = trace("compute false branch"){ - normalizeToCaseBranches(specialize(tail, No)(scrutinee.symbol, pattern, scope)) - }() - val unappliedVar = Var(s"args_${scrutinee.name}$$${nme.name}") val trueBranchWithBindings = Let( isRec = false, name = unappliedVar, @@ -58,6 +70,9 @@ trait Normalization { self: mlscript.pretyper.Traceable => case ((S(parameter), i), next) => Let(false, parameter, Sel(unappliedVar, Var(i.toString)), next) } ) + val falseBranch = trace("compute false branch"){ + normalizeToCaseBranches(specialize(tail, No)(scrutinee.symbol, pattern, scope)) + }() CaseOf(scrutinee, Case(nme, trueBranchWithBindings, falseBranch)) case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => throw new NormalizationException((msg"Unsupported pattern: ${pattern.toString}" -> pattern.toLoc) :: Nil) @@ -87,6 +102,10 @@ trait Normalization { self: mlscript.pretyper.Traceable => Split.Let(false, alias, otherScrutineeVar, specialize(continuation, matchOrNot)) // Class pattern. Positive. case (Yes, split @ Split.Cons(head @ Branch(otherScrutineeVar, Pattern.Class(otherClassName, otherParameters), continuation), tail)) => + val otherClassSymbol = otherClassName.symbolOption.flatMap(_.typeSymbolOption) match { + case N => throw new NormalizationException(msg"class name is not resolved" -> otherClassName.toLoc :: Nil) + case S(typeSymbol) => typeSymbol + } val otherScrutinee = otherScrutineeVar.symbol lazy val specializedTail = { println(s"specialized next") @@ -96,7 +115,7 @@ trait Normalization { self: mlscript.pretyper.Traceable => println(s"scrutinee: ${scrutinee.name} === ${otherScrutinee.name}") pattern match { case Pattern.Class(className, parameters) => - if (className === otherClassName) { + if (className === otherClassName) { // FIXME: Use class symbol. println(s"class name: $className === $otherClassName") (parameters, otherParameters) match { case (S(parameters), S(otherParameters)) => @@ -106,7 +125,21 @@ trait Normalization { self: mlscript.pretyper.Traceable => // Generate a function that generates bindings. // TODO: Hygienic problems. val addLetBindings = parameters.iterator.zip(otherParameters).zipWithIndex.foldLeft[Split => Split](identity) { - case (acc, N -> S(otherParameter) -> index) => ??? // TODO: How can we get the unapplied variable? + case (acc, N -> S(otherParameter) -> index) => + scrutinee match { + case symbol: ScrutineeSymbol => + symbol.unappliedVarMap.get(otherClassSymbol) match { + case N => + println(symbol.unappliedVarMap) + die + case S(unappliedVar) => + println(s"we can create binding for ${otherParameter.name} at $index") + tail => Split.Let(false, otherParameter, Sel(unappliedVar, Var(index.toString)), tail) + } + case _ => + println(s"we can't create binding for ${otherParameter.name} at $index") + die + } case (acc, S(parameter) -> S(otherParameter) -> index) if parameter.name =/= otherParameter.name => println(s"different parameter names at $index: ${parameter.name} =/= ${otherParameter.name}") tail => Split.Let(false, otherParameter, parameter, tail) diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index ae4cfaa6..0299c610 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -34,6 +34,11 @@ trait PostProcessing { self: mlscript.pretyper.Traceable => println(s"found a UNARY case: $scrutinee is $className") println("post-processing the body") top.copy(cases = fst.copy(body = postProcess(body))) + case top @ CaseOf(scrutinee, fst @ Case(Var("true"), trueBranch, Wildcard(falseBranch))) => + println(s"found a if-then-else case: $scrutinee is true") + val processedTrueBranch = postProcess(trueBranch) + val processedFalseBranch = postProcess(falseBranch) + top.copy(cases = fst.copy(body = processedTrueBranch, rest = Wildcard(processedFalseBranch))) case top @ CaseOf(scrutinee: Var, fst @ Case(className: Var, trueBranch, Wildcard(falseBranch))) => println(s"found a BINARY case: $scrutinee is $className") val scrutineeSymbol = scrutinee.symbol match { diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index cfa6b4d1..b3acf72b 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -31,6 +31,7 @@ trait Transformation { self: mlscript.pretyper.Traceable => splitAnd(expr).foldRight(Split.then(rhs)) { case (OperatorIs(scrutinee, pattern), acc) => TermBranch.Match(scrutinee, PatternBranch(transformPattern(pattern), acc) |> Split.single) |> Split.single + case (Var("_"), acc) => acc case (test, acc) => TermBranch.Boolean(test, acc) |> Split.single } case IfLet(isRec, name, rhs, body) => rare @@ -49,6 +50,7 @@ trait Transformation { self: mlscript.pretyper.Traceable => splitAnd(lhs).foldRight(transformIfBody(rhs)) { case (OperatorIs(scrutinee, pattern), acc) => TermBranch.Match(scrutinee, PatternBranch(transformPattern(pattern), acc) |> Split.single) |> Split.single + case (Var("_"), acc) => acc case (test, acc) => TermBranch.Boolean(test, acc) |> Split.single } case IfOpApp(lhs, op, rhs) => diff --git a/shared/src/test/diff/pretyper/ucs/examples/BinarySearchTree.mls b/shared/src/test/diff/pretyper/ucs/examples/BinarySearchTree.mls new file mode 100644 index 00000000..7c58261c --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/examples/BinarySearchTree.mls @@ -0,0 +1,613 @@ +:PreTyper + +fun (|>) pipe(x, f) = f(x) +fun (~~>) toBe(x, y) = if x === y then () else error +fun (?) max(x, y) = if x > y then x else y +fun abs(x) = if x < 0 then -x else x +//│ fun (|>) pipe: forall 'a 'b. ('a, 'a -> 'b) -> 'b +//│ fun (~~>) toBe: forall 'c. (Eql['c], 'c) -> () +//│ fun ( 'd +//│ fun (>?) max: forall 'e. (Num & 'e, Num & 'e) -> 'e +//│ fun abs: Int -> Int + + +abstract class Option[out T]: (Some[T] | None) +class Some[out T](val value: T) extends Option[T] +module None extends Option[nothing] +//│ abstract class Option[T]: None | Some[T] +//│ class Some[T](value: T) extends Option +//│ module None extends Option + +fun (??) getOrElse(o, v) = if o is + Some(v') then v' + None then v +//│ fun (??) getOrElse: forall 'a. (None | Some['a], 'a) -> 'a + +fun (++) strcat(s1, s2) = concat(s1)(s2) +//│ fun (++) strcat: (Str, Str) -> Str + +let anyToString = toString +//│ let anyToString: anything -> Str +//│ anyToString +//│ = [Function: toString] + +abstract class List[out T]: (Cons[T] | Nil) +class Cons[out T](val head: T, val tail: List[T]) extends List[T] +module Nil extends List[nothing] +//│ abstract class List[T]: Cons[T] | Nil +//│ class Cons[T](head: T, tail: List[T]) extends List +//│ module Nil extends List + +fun (::) cons(head, tail) = Cons(head, tail) +//│ fun (::) cons: forall 'T. ('T, List['T]) -> Cons['T] + +1 :: 2 :: 3 :: 4 :: Nil +//│ Cons[1 | 2 | 3 | 4] +//│ res +//│ = Cons {} + +abstract class Tree[out A]: (Empty | Node[A]) +class Node[A](value: A, left: Tree[A], right: Tree[A]) extends Tree[A] +module Empty extends Tree[nothing] +//│ abstract class Tree[A]: Empty | Node[A] +//│ class Node[A](value: A, left: Tree[A], right: Tree[A]) extends Tree +//│ module Empty extends Tree + +fun single(v) = Node(v, Empty, Empty) +//│ fun single: forall 'A. 'A -> Node['A] + +fun show(t: Tree[anything]): Str = if t is + Node(v, l, r) then + "(" ++ show(l) ++ " " ++ toString(v) ++ " " ++ show(r) ++ ")" + Empty then "•" +//│ fun show: (t: Tree[anything]) -> Str + +show(Empty) +show(Node(0, Empty, Empty)) +show(Node(1, Node(0, Empty, Empty), Empty)) +show(Node(1, Node(0, Empty, Empty), Node(2, Empty, Empty))) +//│ Str +//│ res +//│ = '•' +//│ res +//│ = '(• 0 •)' +//│ res +//│ = '((• 0 •) 1 •)' +//│ res +//│ = '((• 0 •) 1 (• 2 •))' + +fun insert(t, v) = if t is + Node(v', l, r) and + v < v' then Node(v', insert(l, v), r) + v > v' then Node(v', l, insert(r, v)) + _ then t + Empty then Node(v, Empty, Empty) +//│ fun insert: forall 'A 'A0. (Empty | Node[Num & 'A], Num & 'A & 'A0) -> Node['A | 'A0] + +// FIXME +fun insert'(t, v) = if t is + Node(v', l, r) and v + < v' then Node(v', insert(l, v), r) + > v' then Node(v', l, insert(r, v)) + else t + Empty then Node(v, Empty, Empty) +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing + +insert(Empty, 0) |> show +insert(Node(0, Empty, Empty), 0) |> show +insert(Node(1, Empty, Empty), 0) |> show +insert(Node(1, Node(0, Empty, Empty), Empty), 0) |> show +insert(Node(1, Node(0, Empty, Empty), Empty), 2) |> show +//│ Str +//│ res +//│ = '(• 0 •)' +//│ res +//│ = '(• 0 •)' +//│ res +//│ = '((• 0 •) 1 •)' +//│ res +//│ = '((• 0 •) 1 •)' +//│ res +//│ = '((• 0 •) 1 (• 2 •))' + +// FIXME +fun fromList(l) = + fun fromList'(t, xs) = + if xs is + Cons(x, xs') then fromList'(insert(t, x), xs') + Nil then Empty + fromList'(Empty, l) +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing + +fun fromList(t, xs) = + if xs is + Cons(x, xs') then fromList(insert(t, x), xs') + Nil then t +//│ fun fromList: forall 'a 'A 'A0. ('a & (Empty | Node['A]), Cons[Num & 'A & 'A0] | Nil) -> (Node['A | 'A0] | 'a) +//│ where +//│ 'A <: Num + +fromList(Empty, 1 :: 2 :: 3 :: 4 :: Nil) |> show +fromList(Empty, 2 :: 1 :: 4 :: 3 :: Nil) |> show +fromList(Empty, 4 :: 3 :: 2 :: 1 :: Nil) |> show +let example1 = fromList(Empty, 1 :: 3 :: 2 :: 4 :: Nil) +example1 |> show +//│ let example1: Empty | Node[1 | 2 | 3 | 4] +//│ Str +//│ res +//│ = '(• 1 (• 2 (• 3 (• 4 •))))' +//│ res +//│ = '((• 1 •) 2 ((• 3 •) 4 •))' +//│ res +//│ = '((((• 1 •) 2 •) 3 •) 4 •)' +//│ example1 +//│ = Node {} +//│ res +//│ = '(• 1 ((• 2 •) 3 (• 4 •)))' + +fun contains(t, v) = if t is + Node(v', l, r) and + v < v' then contains(l, v) + v > v' then contains(r, v) + _ then true + Empty then false +//│ fun contains: (Empty | Node[Num], Num) -> Bool + +// Writing tests like this is very interesting. +contains(Empty, 0) ~~> false +contains(Node(0, Empty, Empty), 0) ~~> true +contains(Node(1, Empty, Empty), 0) ~~> false +//│ () +//│ res +//│ = undefined +//│ res +//│ = undefined +//│ res +//│ = undefined + +fun minValue(t) = if t is + Empty then None + Node(v, Empty, _) then Some(v) + Node(_, l, _) then minValue(l) +//│ fun minValue: forall 'T. (Empty | Node['T]) -> (None | Some['T]) + +minValue(Empty) ?? "not found" +minValue(Node(0, Empty, Empty)) ?? "not found" +minValue(example1) ?? "not found" +//│ "not found" | 1 | 2 | 3 | 4 +//│ res +//│ = 'not found' +//│ res +//│ = 0 +//│ res +//│ = 1 + +fun maxValue(t) = if t is + Empty then None + Node(v, _, Empty) then Some(v) + Node(_, _, r) then maxValue(r) +//│ fun maxValue: forall 'T. (Empty | Node['T]) -> (None | Some['T]) + +maxValue(Empty) ?? "not found" +maxValue(Node(0, Empty, Empty)) ?? "not found" +maxValue(example1) ?? "not found" +//│ "not found" | 1 | 2 | 3 | 4 +//│ res +//│ = 'not found' +//│ res +//│ = 0 +//│ res +//│ = 4 + +fun lowerBound(t, v) = if t is + Node(v', l, r) and + v < v' then lowerBound(l, v) + v > v' then Some(lowerBound(r, v) ?? v') + _ then Some(v') + Empty then None +//│ fun lowerBound: forall 'a 'T. (Empty | Node[Num & 'a & 'T], Num) -> (None | Some['T | 'a]) + +lowerBound(Empty, 0) ?? "not found" +lowerBound(Node(0, Empty, Empty), 0) ?? "not found" +lowerBound(Node(1, Empty, Empty), 0) ?? "not found" +lowerBound(Node(-1, Empty, Empty), 0) ?? "not found" +//│ "not found" | -1 +//│ res +//│ = 'not found' +//│ res +//│ = 0 +//│ res +//│ = 'not found' +//│ res +//│ = -1 + +lowerBound(example1, 0) ?? "not found" +lowerBound(example1, 1) ?? "not found" +lowerBound(example1, 2) ?? "not found" +lowerBound(example1, 3) ?? "not found" +lowerBound(example1, 4) ?? "not found" +lowerBound(example1, 5) ?? "not found" +//│ "not found" | 1 | 2 | 3 | 4 +//│ res +//│ = 'not found' +//│ res +//│ = 1 +//│ res +//│ = 2 +//│ res +//│ = 3 +//│ res +//│ = 4 +//│ res +//│ = 4 + +let example2 = fromList(Empty, 1 :: 5 :: 42 :: 10 :: 23 :: 59 :: 81 :: Nil) +lowerBound(example2, 0) ?? "not found" +lowerBound(example2, 25) ?? "not found" +lowerBound(example2, 99) ?? "not found" +lowerBound(example2, 7) ?? "not found" +lowerBound(example2, 32) ?? "not found" +lowerBound(example2, 41) ?? "not found" +//│ let example2: Empty | Node[10 | 1 | 23 | 42 | 59 | 5 | 81] +//│ "not found" | 10 | 1 | 23 | 42 | 59 | 5 | 81 +//│ example2 +//│ = Node {} +//│ res +//│ = 'not found' +//│ res +//│ = 23 +//│ res +//│ = 81 +//│ res +//│ = 5 +//│ res +//│ = 23 +//│ res +//│ = 23 + +fun upperBound(t, v) = if t is + Node(v', l, r) and + v < v' then Some(upperBound(l, v) ?? v') + v > v' then upperBound(r, v) + _ then Some(v') + Empty then None +//│ fun upperBound: forall 'a 'T. (Empty | Node[Num & 'a & 'T], Num) -> (None | Some['T | 'a]) + +upperBound(example2, 0) ?? "not found" +upperBound(example2, 25) ?? "not found" +upperBound(example2, 99) ?? "not found" +upperBound(example2, 7) ?? "not found" +upperBound(example2, 32) ?? "not found" +upperBound(example2, 41) ?? "not found" +//│ "not found" | 10 | 1 | 23 | 42 | 59 | 5 | 81 +//│ res +//│ = 1 +//│ res +//│ = 42 +//│ res +//│ = 'not found' +//│ res +//│ = 10 +//│ res +//│ = 42 +//│ res +//│ = 42 + +fun remove(t, v) = + if t is + Node(v', l, r) and + v < v' then Node(v', remove(l, v), r) + v > v' then Node(v', l, remove(r, v)) + minValue(r) is + None then l + Some(v'') then Node(v'', l, remove(r, v'')) + Empty then Empty +//│ fun remove: forall 'A. (Empty | Node['A], Num) -> (Empty | Node['A] | Tree['A]) +//│ where +//│ 'A <: Num + +remove(Empty, 0) |> show +remove(Node(0, Empty, Empty), 0) |> show +remove(Node(1, Empty, Empty), 0) |> show +remove(Node(1, Node(0, Empty, Empty), Empty), 0) |> show +remove(Node(1, Empty, Node(2, Empty, Empty)), 2) |> show +remove(Node(1, Node(0, Empty, Empty), Node(2, Empty, Empty)), 1) |> show +//│ Str +//│ res +//│ = '•' +//│ res +//│ = '•' +//│ res +//│ = '(• 1 •)' +//│ res +//│ = '(• 1 •)' +//│ res +//│ = '(• 1 •)' +//│ res +//│ = '((• 0 •) 2 •)' + +example1 |> show +remove(example1, 0) |> show +remove(example1, 1) |> show +remove(example1, 2) |> show +remove(example1, 3) |> show +remove(example1, 4) |> show +//│ Str +//│ res +//│ = '(• 1 ((• 2 •) 3 (• 4 •)))' +//│ res +//│ = '(• 1 ((• 2 •) 3 (• 4 •)))' +//│ res +//│ = '(• 2 (• 3 (• 4 •)))' +//│ res +//│ = '(• 1 (• 3 (• 4 •)))' +//│ res +//│ = '(• 1 ((• 2 •) 4 •))' +//│ res +//│ = '(• 1 ((• 2 •) 3 •))' + +class Pair[A, B](val first: A, val second: B) { + fun mapFirst(f) = Pair(f(first), second) + fun mapSecond(f) = Pair(first, f(second)) +} +//│ class Pair[A, B](first: A, second: B) { +//│ fun mapFirst: forall 'A. (A -> 'A) -> Pair['A, B] +//│ fun mapSecond: forall 'B. (B -> 'B) -> Pair[A, 'B] +//│ } + +fun extractMin(t) = + if t is + Node(v, Empty, r) then Pair(Some(v), r) + Node(v, l, r) and + extractMin(l) is Pair(m, l') then + Pair(m, Node(v, l', r)) + Empty then Pair(None, Empty) +//│ fun extractMin: forall 'A. (Empty | Node['A]) -> Pair[None | Some['A], Empty | Node['A] | Tree['A]] + +extractMin(example1).first ?? "not found" +extractMin(example1).second |> show +//│ Str +//│ res +//│ = 1 +//│ res +//│ = '((• 2 •) 3 (• 4 •))' + +fun merge(l, r) = + if extractMin(r) is + Pair(None, _) then l + Pair(Some(m), r') then Node(m, l, r') +//│ fun merge: forall 'A 'a. (Tree['A] & 'a, Empty | Node['A]) -> (Node['A] | 'a) + +merge(Empty, Empty) |> show +merge(Empty, Node(0, Empty, Empty)) |> show +merge(Node(0, Empty, Empty), Empty) |> show +merge(Node(0, Empty, Empty), Node(1, Empty, Empty)) |> show +merge(Node(0, Empty, Empty), Node(2, Node(1, Empty, Empty), Empty)) |> show +//│ Str +//│ res +//│ = '•' +//│ res +//│ = '(• 0 •)' +//│ res +//│ = '(• 0 •)' +//│ res +//│ = '((• 0 •) 1 •)' +//│ res +//│ = '((• 0 •) 1 (• 2 •))' + +fun removeGte(t, v) = + if t is + Node(v', l, r) and + v < v' then removeGte(l, v) + v > v' then Node(v', l, removeGte(r, v)) + _ then l // lucky case + Empty then Empty +//│ fun removeGte: forall 'A. (Empty | Node['A], Num) -> (Empty | Node['A] | Tree['A]) +//│ where +//│ 'A <: Num + +removeGte(Empty, 0) |> show +removeGte(example1, 0) |> show +removeGte(example1, 1) |> show +removeGte(example1, 2) |> show +removeGte(example1, 3) |> show +removeGte(example1, 4) |> show +removeGte(example1, 5) |> show +//│ Str +//│ res +//│ = '•' +//│ res +//│ = '•' +//│ res +//│ = '•' +//│ res +//│ = '(• 1 •)' +//│ res +//│ = '(• 1 (• 2 •))' +//│ res +//│ = '(• 1 ((• 2 •) 3 •))' +//│ res +//│ = '(• 1 ((• 2 •) 3 (• 4 •)))' + +example2 |> show +removeGte(example2, 10) |> show +removeGte(example2, 22) |> show +removeGte(example2, 23) |> show +removeGte(example2, 24) |> show +removeGte(example2, 70) |> show +removeGte(example2, 99) |> show +//│ Str +//│ res +//│ = '(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 59 (• 81 •)))))' +//│ res +//│ = '(• 1 (• 5 •))' +//│ res +//│ = '(• 1 (• 5 (• 10 •)))' +//│ res +//│ = '(• 1 (• 5 (• 10 •)))' +//│ res +//│ = '(• 1 (• 5 (• 10 (• 23 •))))' +//│ res +//│ = '(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 59 •))))' +//│ res +//│ = '(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 59 (• 81 •)))))' + +fun removeLt(t, v) = + if t is + Node(v', l, r) and + v' < v then removeLt(r, v) + else Node(v', removeLt(l, v), r) + Empty then Empty +//│ fun removeLt: forall 'A. (Empty | Node[Num & 'A], Num) -> (Empty | Node['A]) + +example2 |> show +removeLt(example2, 10) |> show +removeLt(example2, 22) |> show +removeLt(example2, 23) |> show +removeLt(example2, 24) |> show +removeLt(example2, 70) |> show +removeLt(example2, 99) |> show +//│ Str +//│ res +//│ = '(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 59 (• 81 •)))))' +//│ res +//│ = '((• 10 (• 23 •)) 42 (• 59 (• 81 •)))' +//│ res +//│ = '((• 23 •) 42 (• 59 (• 81 •)))' +//│ res +//│ = '((• 23 •) 42 (• 59 (• 81 •)))' +//│ res +//│ = '(• 42 (• 59 (• 81 •)))' +//│ res +//│ = '(• 81 •)' +//│ res +//│ = '•' + +// Remove elements from `begin` until `end`. +fun removeRange(t, begin, end) = + if t is + Node(v, l, r) and + begin > v then Node(v, l, removeRange(r, begin, end)) + end <= v then Node(v, removeRange(l, begin, end), r) + _ then merge(removeGte(l, begin), removeLt(r, end)) + Empty then Empty +//│ fun removeRange: forall 'A. (Empty | Node[Num & 'A], Num, Num) -> (Empty | Node['A] | Tree['A]) +//│ where +//│ 'A <: Num + +example2 |> show +removeRange(example2, 1, 82) |> show +removeRange(example2, 1, 50) |> show +removeRange(example2, 50, 81) |> show +removeRange(example2, 20, 60) |> show +removeRange(example2, 20, 24) |> show +removeRange(example2, 59, 60) |> show +//│ Str +//│ res +//│ = '(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 59 (• 81 •)))))' +//│ res +//│ = '•' +//│ res +//│ = '(• 59 (• 81 •))' +//│ res +//│ = '(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 81 •))))' +//│ res +//│ = '(• 1 (• 5 ((• 10 •) 81 •)))' +//│ res +//│ = '(• 1 (• 5 ((• 10 •) 42 (• 59 (• 81 •)))))' +//│ res +//│ = '(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 81 •))))' + +fun size(t) = + if t is + Node(_, l, r) then 1 + size(l) + size(r) + Empty then 0 +//│ fun size: (Empty | Node[anything]) -> Int + +size(Empty) ~~> 0 +size(Node(0, Empty, Empty)) ~~> 1 +size(example1) ~~> 4 +size(example2) ~~> 7 +//│ () +//│ res +//│ = undefined +//│ res +//│ = undefined +//│ res +//│ = undefined +//│ res +//│ = undefined + +fun inverse(t) = + if t is + Node(v, l, r) then Node(v, inverse(r), inverse(l)) + Empty then Empty +//│ fun inverse: forall 'A. (Empty | Node['A]) -> (Empty | Node['A]) + +inverse(Empty) |> show +inverse(Node(0, Empty, Empty)) |> show +inverse(example1) |> show +inverse(example2) |> show +//│ Str +//│ res +//│ = '•' +//│ res +//│ = '(• 0 •)' +//│ res +//│ = '(((• 4 •) 3 (• 2 •)) 1 •)' +//│ res +//│ = '(((((• 81 •) 59 •) 42 ((• 23 •) 10 •)) 5 •) 1 •)' + +fun height(t) = + if t is + Node(_, l, r) then 1 + max(height(l), height(r)) + Empty then 0 +//│ fun height: (Empty | Node[anything]) -> Int + +height(Empty) ~~> 0 +height(Node(0, Empty, Empty)) ~~> 1 +height(example1) ~~> 3 +height(example2) ~~> 5 +//│ () +//│ res +//│ = undefined +//│ res +//│ = undefined +//│ res +//│ = undefined +//│ res +//│ = undefined + +fun isBalanced(t) = + if t is + Empty then true + Node(_, l, r) and height(l) is hl and height(r) is hr then + // The precedence of `<=` seems to be broken. + (abs(hl - hr) <= 1) && isBalanced(l) && isBalanced(r) +//│ fun isBalanced: (Empty | Node[anything]) -> Bool + +isBalanced(Empty) ~~> true +isBalanced(Node(0, Empty, Empty)) ~~> true +isBalanced(example1) ~~> false +isBalanced(example2) ~~> false +//│ () +//│ res +//│ = undefined +//│ res +//│ = undefined +//│ res +//│ = undefined +//│ res +//│ = undefined + +isBalanced(Node(1, single(-1), single(3))) ~~> true +isBalanced(Node(1, single(-1), Node(3, single(2), Empty))) ~~> true +isBalanced(Node(1, single(-1), Node(3, Empty, single(4)))) ~~> true +//│ () +//│ res +//│ = undefined +//│ res +//│ = undefined +//│ res +//│ = undefined diff --git a/shared/src/test/diff/pretyper/ucs/examples/LeftistTree.mls b/shared/src/test/diff/pretyper/ucs/examples/LeftistTree.mls new file mode 100644 index 00000000..76b7c420 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/examples/LeftistTree.mls @@ -0,0 +1,185 @@ +:PreTyper + +fun (|>) pipe(x, f) = f(x) +fun (~~>) toBe(x, y) = if x === y then () else error +fun (?) max(x, y) = if x > y then x else y +fun abs(x) = if x < 0 then -x else x +//│ fun (|>) pipe: forall 'a 'b. ('a, 'a -> 'b) -> 'b +//│ fun (~~>) toBe: forall 'c. (Eql['c], 'c) -> () +//│ fun ( 'd +//│ fun (>?) max: forall 'e. (Num & 'e, Num & 'e) -> 'e +//│ fun abs: Int -> Int + + +abstract class Option[out T]: (Some[T] | None) +class Some[out T](val value: T) extends Option[T] +module None extends Option[nothing] +//│ abstract class Option[T]: None | Some[T] +//│ class Some[T](value: T) extends Option +//│ module None extends Option + +fun (??) getOrElse(o, v) = if o is + Some(v') then v' + None then v +//│ fun (??) getOrElse: forall 'a. (None | Some['a], 'a) -> 'a + +fun (++) strcat(s1, s2) = concat(s1)(s2) +//│ fun (++) strcat: (Str, Str) -> Str + +let anyToString = toString +//│ let anyToString: anything -> Str +//│ anyToString +//│ = [Function: toString] + +abstract class List[out T]: (Cons[T] | Nil) +class Cons[out T](val head: T, val tail: List[T]) extends List[T] +module Nil extends List[nothing] +//│ abstract class List[T]: Cons[T] | Nil +//│ class Cons[T](head: T, tail: List[T]) extends List +//│ module Nil extends List + +fun (::) cons(head, tail) = Cons(head, tail) +//│ fun (::) cons: forall 'T. ('T, List['T]) -> Cons['T] + +abstract class Tree[out A]: (Empty | Node[A]) +class Node[A](value: A, left: Tree[A], right: Tree[A], rank: Int) extends Tree[A] +module Empty extends Tree[nothing] +//│ abstract class Tree[A]: Empty | Node[A] +//│ class Node[A](value: A, left: Tree[A], right: Tree[A], rank: Int) extends Tree +//│ module Empty extends Tree + +fun show(t: Tree[anything]): Str = if t is + Node(v, l, r, _) then + "(" ++ show(l) ++ " " ++ toString(v) ++ " " ++ show(r) ++ ")" + Empty then "•" +//│ fun show: (t: Tree[anything]) -> Str + +fun singleton(x) = Node(x, Empty, Empty, 1) +fun rank(t) = if t is + Empty then 0 + Node(_, _, _, r) then r +//│ fun singleton: forall 'A. 'A -> Node['A] +//│ fun rank: (Empty | Node[anything]) -> Int + +// This can be improved. This can be better. +fun merge(t1: Tree[Num], t2: Tree[Num]): Tree[Num] = + if + t1 is Node(v1, l1, r1, _) and t2 is Node(v2, _, _, _) and + v1 > v2 then merge(t2, t1) + _ and merge(r1, t2) is merged and + rank(l1) is rank_left and rank(r1) is rank_right and + rank_left >= rank_right then Node(v1, l1, merged, rank_right + 1) + else Node(v1, merged, l1, rank_left + 1) + t1 is Empty and t2 is Node then t2 + t1 is Node and t2 is Empty then t1 + t1 is Empty and t2 is Empty then Empty +//│ fun merge: (t1: Tree[Num], t2: Tree[Num]) -> Tree[Num] + +fun insert(t, v) = merge(t, singleton(v)) +//│ fun insert: (Tree[Num], Num) -> Tree[Num] + +fun getMin(t) = + if t is + Empty then None + Node(x, _, _, _) then Some(x) +//│ fun getMin: forall 'T. (Empty | Node['T]) -> (None | Some['T]) + +fun deleteMin(t) = + if t is + Empty then Empty + Node(_, l, r, _) then merge(l, r) +//│ fun deleteMin: (Empty | Node[Num]) -> (Empty | Tree[Num]) + +fun fromList(t, xs) = + if xs is + Cons(x, xs') then fromList(insert(t, x), xs') + Nil then t +//│ fun fromList: (Tree[Num], Cons[Num] | Nil) -> Tree[Num] + +let tree1 = fromList(Empty, 3 :: 4 :: 1 :: 2 :: Nil) +tree1 |> show +//│ let tree1: Empty | Tree[Num] +//│ Str +//│ tree1 +//│ = Node {} +//│ res +//│ = '((• 2 (• 3 (• 4 •))) 1 •)' + +// Remove the smallest element. It should be 1. +getMin(tree1) ?? "nothing" +let tree1' = deleteMin(tree1) +tree1' |> show +//│ let tree1': Empty | Tree[Num] +//│ Str +//│ res +//│ = 1 +//│ tree1' +//│ = Node {} +//│ res +//│ = '(• 2 (• 3 (• 4 •)))' + +// Remove one more element. It should be 2. +getMin(tree1') ?? "nothing" +let tree1'' = deleteMin(tree1') +tree1'' |> show +//│ let tree1'': Empty | Tree[Num] +//│ Str +//│ res +//│ = 2 +//│ tree1'' +//│ = Node {} +//│ res +//│ = '(• 3 (• 4 •))' + +// Remove one more element. It should be 3. +getMin(tree1'') ?? "nothing" +let tree1''' = deleteMin(tree1'') +tree1''' |> show +//│ let tree1''': Empty | Tree[Num] +//│ Str +//│ res +//│ = 3 +//│ tree1''' +//│ = Node {} +//│ res +//│ = '(• 4 •)' + +// Remove the last element. It should be 4. +getMin(tree1''') ?? "nothing" +let tree1'''' = deleteMin(tree1''') +tree1'''' |> show +//│ let tree1'''': Empty | Tree[Num] +//│ Str +//│ res +//│ = 4 +//│ tree1'''' +//│ = Empty { class: [class Empty extends Tree] } +//│ res +//│ = '•' + +// =========================================================================== + +fun drain(t) = + if getMin(t) is + None then Nil + Some(x) then x :: drain(deleteMin(t)) +//│ fun drain: forall 'T. (Empty | Node[Num & 'T]) -> (Cons[Num | 'T] | Nil) + +fun sorted(xs) = fromList(Empty, xs) |> drain +//│ fun sorted: (Cons[Num] | Nil) -> (Cons[Num] | Nil) + +fun showList(xs) = + if xs is + Cons(x, Nil) then toString(x) + Cons(x, xs') then toString(x) ++ ", " ++ showList(xs') + Nil then "" +//│ fun showList: (Cons[anything] | Nil) -> Str + +sorted(3 :: 4 :: 1 :: 2 :: Nil) |> showList +sorted(42 :: 58 :: 19 :: 37 :: 44 :: 99 :: 68 :: 60 :: 77 :: 61 :: Nil) |> showList +//│ Str +//│ res +//│ = '1, 2, 3, 4' +//│ res +//│ = '19, 37, 42, 44, 58, 60, 61, 68, 77, 99' From 8f8431060023d6f99a89d0223aa937db8c431717 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sat, 23 Dec 2023 00:51:51 +0800 Subject: [PATCH 017/143] Add AVL tree for testing and fix many issues - Very minor but important: should use post-processed term as the desugared term. - Add incomplete AVL tree test cases which reveals many problems. - Cache operator split partial terms for efficiency. - `Traceable` now supports filtering debugging by topics. - Skip speicalization Boolean condition scrutinees. --- .../scala/mlscript/pretyper/PreTyper.scala | 10 +- .../scala/mlscript/pretyper/Traceable.scala | 36 +- .../main/scala/mlscript/ucs/DesugarUCS.scala | 67 ++-- .../main/scala/mlscript/ucs/PartialTerm.scala | 7 +- .../mlscript/ucs/stages/Desugaring.scala | 89 +++-- .../mlscript/ucs/stages/Normalization.scala | 14 +- .../mlscript/ucs/stages/PostProcessing.scala | 8 +- .../mlscript/ucs/stages/Transformation.scala | 31 +- .../src/main/scala/mlscript/ucs/syntax.scala | 10 +- .../main/scala/mlscript/utils/inspect.scala | 9 + .../src/test/diff/pretyper/ucs/Overlaps.mls | 44 +-- .../test/diff/pretyper/ucs/TransfromUCS.mls | 2 +- .../pretyper/ucs/coverage/SealedClasses.mls | 38 ++- .../diff/pretyper/ucs/examples/AVLTree.mls | 309 ++++++++++++++++++ .../src/test/scala/mlscript/DiffTests.scala | 25 +- 15 files changed, 551 insertions(+), 148 deletions(-) create mode 100644 shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index d6827ea2..c48e582e 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -7,7 +7,7 @@ import mlscript._, utils._, shorthands._ import scala.annotation.tailrec import mlscript.Message, Message.MessageContext -class PreTyper(override val debugLevel: Opt[Int], useNewDefs: Bool) extends Traceable with DesugarUCS { +class PreTyper(override val debugTopics: Opt[Set[Str]], useNewDefs: Bool) extends Traceable with DesugarUCS { import PreTyper._ protected def raise(diagnostics: Diagnostic): Unit = () @@ -42,19 +42,19 @@ class PreTyper(override val debugLevel: Opt[Int], useNewDefs: Bool) extends Trac trace(s"resolveVar(name = \"$v\")") { scope.getTermSymbol(v.name) match { case S(sym: ValueSymbol) => - println(s"Resolve variable $v to a value.", 2) + println(s"Resolve variable $v to a value.") v.symbol = sym case S(sym: SubValueSymbol) => - println(s"Resolve variable $v to a value.", 2) + println(s"Resolve variable $v to a value.") v.symbol = sym case S(sym: FunctionSymbol) => - println(s"Resolve variable $v to a function.", 2) + println(s"Resolve variable $v to a function.") v.symbol = sym case N => scope.getTypeSymbol(v.name) match { case S(sym: ClassSymbol) => if (sym.defn.kind == Cls) { - println(s"Resolve variable $v to a class.", 2) + println(s"Resolve variable $v to a class.") v.symbol = sym } else { throw new Exception(s"Name $v refers to a type") diff --git a/shared/src/main/scala/mlscript/pretyper/Traceable.scala b/shared/src/main/scala/mlscript/pretyper/Traceable.scala index 781fa803..206982fe 100644 --- a/shared/src/main/scala/mlscript/pretyper/Traceable.scala +++ b/shared/src/main/scala/mlscript/pretyper/Traceable.scala @@ -4,11 +4,15 @@ import mlscript.Diagnostic import mlscript.utils._, shorthands._ trait Traceable { - // Override this to change the base indentation level. - def baseIndent: Int = 0 + /** The set of topics to debug. Empty set indicates all topics. */ + protected val debugTopics: Opt[Set[Str]] = N + protected var indent = 0 + private var topic: Opt[Str] = N - protected val debugLevel: Opt[Int] = N // The number of verbose. - protected var indent = baseIndent + def emitString(str: String): Unit = scala.Predef.println(str) + + @inline private def printLineByLine(x: => Any): Unit = + x.toString.linesIterator.foreach { line => emitString("| " * indent + line) } def trace[T](pre: => String)(thunk: => T)(post: T => String = Traceable.noPostTrace): T = { println(pre) @@ -18,21 +22,21 @@ trait Traceable { res } + def traceWithTopic[T](topic: Str)(thunk: => T): T = { + this.topic = S(topic) + val res = thunk + this.topic = N + res + } + @inline def traceNot[T](pre: => String)(thunk: => T)(post: T => String = Traceable.noPostTrace): T = thunk - def emitDbg(str: String): Unit = scala.Predef.println(str) - - @inline - protected def println(msg: => Any): Unit = println(msg, 0) - - @inline - protected def println(msg: => Any, level: Int): Unit = - if (debugLevel exists (_ >= level)) printLineByLine(msg) - - @inline - private def printLineByLine(msg: => Any): Unit = - msg.toString.linesIterator.foreach { line => emitDbg("| " * indent + line) } + @inline protected def println(x: => Any): Unit = + topic match { + case N => if (debugTopics.isDefined) printLineByLine(x) + case S(topic) => if (debugTopics.fold(false)(ts => ts.isEmpty || ts.contains(topic))) printLineByLine(x) + } } object Traceable { diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index 351e5b3f..a9743b5c 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -17,39 +17,50 @@ trait DesugarUCS extends Transformation protected def traverseIf(`if`: If)(implicit scope: Scope): Unit = trace("traverseIf") { // Stage 0: Transformation - println("STEP 0") - val transformed = transform(`if`, true) - println("Transformed UCS term:") - println(transformed.toString, 2) - println(ucs.syntax.printTermSplit(transformed)) + val transformed = traceWithTopic("transform") { + println("STEP 0") + val transformed = transform(`if`, true) + println("Transformed UCS term:") + println(ucs.syntax.printTermSplit(transformed)) + transformed + } // Stage 1: Desugaring - // This stage will generate new names based on the position of the scrutinee. - // Therefore, we need to call `traverseSplit` to associate these newly generated - // names with symbols. - println("STEP 1") - val desugared = desugar(transformed) - println(desugared.toString, 2) - println("Desugared UCS term:") - println(ucs.core.printSplit(desugared)) - traverseSplit(desugared) + val desugared = traceWithTopic("desugar") { + println("STEP 1") + val desugared = desugar(transformed) + println("Desugared UCS term:") + println(ucs.core.printSplit(desugared)) + desugared + } + traceWithTopic("traverse") { + println("STEP 1.5") + traverseSplit(desugared) + } // Stage 2: Normalization - println("STEP 2") - val normalized = normalize(desugared) - println(normalized.toString, 2) - println("Normalized UCS term:") - printNormalizedTerm(normalized) + val normalized = traceWithTopic("normalize") { + println("STEP 2") + val normalized = normalize(desugared) + println("Normalized UCS term:") + printNormalizedTerm(normalized) + normalized + } // Stage 3: Post-processing - println("STEP 3") - val postProcessed = postProcess(normalized) - println("Post-processed UCS term:") - printNormalizedTerm(postProcessed) + val postProcessed = traceWithTopic("postprocess") { + println("STEP 3") + val postProcessed = postProcess(normalized) + println("Post-processed UCS term:") + printNormalizedTerm(postProcessed) + postProcessed + } // Stage 4: Coverage checking - println("STEP 4") - val diagnostics = checkCoverage(postProcessed) - println(s"Coverage checking result: ${diagnostics.size} errors") - raise(diagnostics) + traceWithTopic("coverage") { + val checked = println("STEP 4") + val diagnostics = checkCoverage(postProcessed) + println(s"Coverage checking result: ${diagnostics.size} errors") + raise(diagnostics) + } // Epilogue - `if`.desugaredTerm = S(normalized) + `if`.desugaredTerm = S(postProcessed) }(_ => "traverseIf ==> ()") private def traverseSplit(split: core.Split)(implicit scope: Scope): Unit = diff --git a/shared/src/main/scala/mlscript/ucs/PartialTerm.scala b/shared/src/main/scala/mlscript/ucs/PartialTerm.scala index a150714f..40a9767a 100644 --- a/shared/src/main/scala/mlscript/ucs/PartialTerm.scala +++ b/shared/src/main/scala/mlscript/ucs/PartialTerm.scala @@ -27,7 +27,12 @@ sealed abstract class PartialTerm { def get: Term = this match { case Empty => throw new PartialTermError(this, "expect a term but nothing was given") case Total(term, fragments) => term - case Half(lhs, op, fragments) => throw new PartialTermError(this, "expect an operator but nothing was given") + case Half(lhs, op, fragments) => throw new PartialTermError(this, "incomplete term") + } + override def toString(): String = this match { + case Empty => "" + case Total(term, fragments) => s" $term" + case Half(lhs, op, fragments) => s" $lhs $op" } } diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 16fd8b01..9a1a1972 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -11,15 +11,11 @@ import mlscript.Message, Message.MessageContext trait Desugaring { self: mlscript.pretyper.Traceable => @inline def desugar(term: s.TermSplit): c.Split = desugarTermSplit(term)(PartialTerm.Empty) - private var nextScrutineeIndex: Int = 0 + import Desugaring._ - private def freshName(): Str = { - val thisIndex = nextScrutineeIndex - nextScrutineeIndex += 1 - s"scrut$$$thisIndex" // FIXME: use `freeVars` to avoid name collision. - } - - private def freshScrutinee(): Var = Var(freshName()) + private val freshCache = new VariableGenerator(cachePrefix) + private val freshScrutinee = new VariableGenerator(scrutineePrefix) + private val freshTest = new VariableGenerator(testPrefix) private def freshScrutinee(parentScrutinee: Var, parentClassName: Var, index: Int): Var = Var(s"${parentScrutinee}$$${parentClassName}_${index.toString}") @@ -52,36 +48,49 @@ trait Desugaring { self: mlscript.pretyper.Traceable => case s.Split.Nil => c.Split.Nil } + // This function does not need to can `withCachedTermPart` because all branches assume that + // `termPart` is either empty or waiting for an RHS. private def desugarTermBranch(branch: s.TermBranch)(implicit termPart: PartialTerm): c.Split = - // Note: `Branch` is `(Term, Pattern, Either[Split, Term])`. - branch match { - case s.TermBranch.Boolean(condition, continuation) => - val `var` = freshScrutinee() - c.Split.Let( - rec = false, - name = `var`, - term = Asc(condition, TypeName("Bool")), - tail = c.Branch(`var`, truePattern, desugarTermSplit(continuation)) :: c.Split.Nil - ) - case s.TermBranch.Match(scrutinee, split) => - desugarPatternSplit(split)(termPart.addTerm(scrutinee, true).get) - case s.TermBranch.Left(left, continuation) => - desugarOperatorSplit(continuation)(termPart.addTerm(left, true)) + trace(s"desugarTermBranch <== $termPart") { + branch match { + case s.TermBranch.Boolean(testPart, continuation) => + val test = freshTest() + c.Split.Let( + rec = false, + name = test, + term = Asc(termPart.addTerm(testPart, true).get, TypeName("Bool")), + tail = c.Branch(test, truePattern, desugarTermSplit(continuation)(PartialTerm.Empty)) :: c.Split.Nil + ) + case s.TermBranch.Match(scrutinee, split) => + desugarPatternSplit(split)(termPart.addTerm(scrutinee, true).get) + case s.TermBranch.Left(left, continuation) => + desugarOperatorSplit(continuation)(termPart.addTerm(left, true)) + } + }() + + private def withCachedTermPart[B <: s.Branch](desugar: (PartialTerm) => c.Split)(implicit termPart: PartialTerm): c.Split = + termPart.get match { + case v: Var => desugar(termPart) // No need to cache variables. + case rhs => + val cache = freshCache() + c.Split.Let(false, cache, rhs, desugar(PartialTerm.Total(cache, Nil))) } private def desugarOperatorSplit(split: s.OperatorSplit)(implicit termPart: PartialTerm): c.Split = - split match { - case s.Split.Cons(head, tail) => desugarOperatorBranch(head) ++ desugarOperatorSplit(tail) - case s.Split.Let(rec, nme, rhs, tail) => c.Split.Let(rec, nme, rhs, desugarOperatorSplit(tail)) + withCachedTermPart { termPart => split match { + case s.Split.Cons(head, tail) => desugarOperatorBranch(head)(termPart) ++ desugarOperatorSplit(tail)(termPart) + case s.Split.Let(rec, nme, rhs, tail) => c.Split.Let(rec, nme, rhs, desugarOperatorSplit(tail)(termPart)) case s.Split.Else(default) => c.Split.Else(default) case s.Split.Nil => c.Split.Nil - } + }} private def desugarOperatorBranch(branch: s.OperatorBranch)(implicit termPart: PartialTerm): c.Split = - branch match { - case s.OperatorBranch.Binary(op, split) => desugarTermSplit(split)(termPart.addOp(op)) - case s.OperatorBranch.Match(_, split) => desugarPatternSplit(split)(termPart.get) - } + trace(s"desugarOperatorBranch <== $termPart") { + branch match { + case s.OperatorBranch.Binary(op, split) => desugarTermSplit(split)(termPart.addOp(op)) + case s.OperatorBranch.Match(_, split) => desugarPatternSplit(split)(termPart.get) + } + }() private def flattenNestedPattern(pattern: s.ClassPattern, scrutinee: Var, next: c.Split): c.Branch = { val (parameterBindings, subPatterns) = flattenClassParameters(scrutinee, pattern.nme, pattern.parameters) @@ -133,3 +142,23 @@ trait Desugaring { self: mlscript.pretyper.Traceable => } } } + +object Desugaring { + class VariableGenerator(prefix: Str) { + private var nextIndex = 0 + + def apply(): Var = { + val thisIndex = nextIndex + nextIndex += 1 + Var(s"$prefix$thisIndex") + } + } + + val cachePrefix = "cache$" + val scrutineePrefix = "scrut$" + val testPrefix = "test$" + + def isCacheVar(nme: Var): Bool = nme.name.startsWith(cachePrefix) + def isScrutineeVar(nme: Var): Bool = nme.name.startsWith(scrutineePrefix) + def isTestVar(nme: Var): Bool = nme.name.startsWith(testPrefix) +} \ No newline at end of file diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 36dcbe23..046d2e2c 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -22,11 +22,16 @@ trait Normalization { self: mlscript.pretyper.Traceable => */ @inline def normalize(split: Split)(implicit scope: Scope): Term = normalizeToTerm(split) - private def normalizeToTerm(split: Split)(implicit scope: Scope): Term = trace("normalizeToTerm") { + private def normalizeToTerm(split: Split)(implicit scope: Scope): Term = trace("normalizeToTerm <==") { split match { case Split.Cons(Branch(scrutinee, Pattern.Name(nme), continuation), tail) => println(s"alias $scrutinee => $nme") Let(false, nme, scrutinee, normalizeToTerm(continuation ++ tail)) + // Skip Boolean conditions as scrutinees, because they only appear once. + case Split.Cons(Branch(test, pattern @ Pattern.Class(nme @ Var("true"), N), continuation), tail) if Desugaring.isTestVar(test) => + val trueBranch = normalizeToTerm(continuation ++ tail) + val falseBranch = normalizeToCaseBranches(tail) + CaseOf(test, Case(nme, trueBranch, falseBranch)) case Split.Cons(Branch(scrutinee, pattern @ Pattern.Literal(literal), continuation), tail) => val trueBranch = normalizeToTerm(specialize(continuation ++ tail, Yes)(scrutinee.symbol, pattern, scope)) val falseBranch = normalizeToCaseBranches(specialize(tail, No)(scrutinee.symbol, pattern, scope)) @@ -76,11 +81,12 @@ trait Normalization { self: mlscript.pretyper.Traceable => CaseOf(scrutinee, Case(nme, trueBranchWithBindings, falseBranch)) case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => throw new NormalizationException((msg"Unsupported pattern: ${pattern.toString}" -> pattern.toLoc) :: Nil) + case Split.Let(rec, Var("_"), rhs, tail) => normalizeToTerm(tail) case Split.Let(rec, nme, rhs, tail) => Let(rec, nme, rhs, normalizeToTerm(tail)) case Split.Else(default) => default case Split.Nil => println("unexpected empty split"); ??? } - }() + }(_ => "normalizeToTerm ==> ") private def normalizeToCaseBranches(split: Split)(implicit scope: Scope): CaseBranches = trace("normalizeToCaseBranches") { split match { @@ -254,7 +260,7 @@ object Normalization { * some special markers. * * - `*` if the variable is associated with a symbol, - * - `†` if the class name has a location. + * - `†` if the variable has a location. */ def showNormalizedTerm(term: Term): String = { def showTerm(term: Term): Lines = term match { @@ -278,7 +284,7 @@ object Normalization { } def showVar(`var`: Var): Str = // If the variable is associated with a symbol, mark it with an asterisk *. - `var`.name + (`var`.symbolOption.fold("")(_ => "*")) + `var`.name + (`var`.symbolOption.fold("")(_ => "*")) + (`var`.toLoc.fold("")(_ => "†")) def showLet(let: Let): Lines = { val Let(rec, nme, rhs, body) = let (0, s"let ${showVar(nme)} = $rhs") :: showTerm(body) diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index 0299c610..8ecb313f 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -34,8 +34,8 @@ trait PostProcessing { self: mlscript.pretyper.Traceable => println(s"found a UNARY case: $scrutinee is $className") println("post-processing the body") top.copy(cases = fst.copy(body = postProcess(body))) - case top @ CaseOf(scrutinee, fst @ Case(Var("true"), trueBranch, Wildcard(falseBranch))) => - println(s"found a if-then-else case: $scrutinee is true") + case top @ CaseOf(test: Var, fst @ Case(Var("true"), trueBranch, Wildcard(falseBranch))) if Desugaring.isTestVar(test) => + println(s"found a if-then-else case: $test is true") val processedTrueBranch = postProcess(trueBranch) val processedFalseBranch = postProcess(falseBranch) top.copy(cases = fst.copy(body = processedTrueBranch, rest = Wildcard(processedFalseBranch))) @@ -187,7 +187,7 @@ trait PostProcessing { self: mlscript.pretyper.Traceable => println("no cases, STOP") NoCases -> NoCases case wildcard @ Wildcard(body) => - println("found a wildcard, stop") + println("found a wildcard, go deeper") val (n, y) = disentangle(body, scrutinee, classSymbol) (wildcard.copy(body = n), y.fold(NoCases: CaseBranches)(Wildcard(_))) case kase @ Case(_, body, rest) => @@ -200,7 +200,7 @@ trait PostProcessing { self: mlscript.pretyper.Traceable => })) } val (n, y) = rec(cases) - (top.copy(cases = y), (if (n === NoCases) N else S(top.copy(cases = n)))) + (top.copy(cases = n), (if (y === NoCases) N else S(top.copy(cases = y)))) } case let @ Let(_, _, _, body) => val (n, y) = disentangle(body, scrutinee, classSymbol) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index b3acf72b..531492c5 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -23,9 +23,7 @@ trait Transformation { self: mlscript.pretyper.Traceable => def transform(`if`: If, useNewDefs: Bool = true): TermSplit = transformIfBody(`if`.body)(useNewDefs) ++ `if`.els.fold(Split.empty)(Split.default) - import helpers.splitAnd - - private def transformIfBody(body: IfBody)(implicit useNewDefs: Bool): TermSplit = { + private def transformIfBody(body: IfBody)(implicit useNewDefs: Bool): TermSplit = trace(s"transformIfBody <== ${inspect.shallow(body)}") { body match { case IfThen(expr, rhs) => splitAnd(expr).foldRight(Split.then(rhs)) { @@ -73,9 +71,18 @@ trait Transformation { self: mlscript.pretyper.Traceable => throw new TransformException(msg"Unexpected statement in an if block", statement.toLoc) } case IfOpsApp(lhs, opsRhss) => - TermBranch.Left(lhs, Split.from(opsRhss.map(transformOperatorBranch))) |> Split.single + splitAnd(lhs) match { + case init :+ last => + val first = TermBranch.Left(last, Split.from(opsRhss.map(transformOperatorBranch))) |> Split.single + init.foldRight[TermSplit](first) { + case (OperatorIs(scrutinee, pattern), acc) => + TermBranch.Match(scrutinee, PatternBranch(transformPattern(pattern), acc) |> Split.single) |> Split.single + case (test, acc) => TermBranch.Boolean(test, acc) |> Split.single + } + case _ => rare + } } - } + }(_ => "transformIfBody ==> ") private def transformOperatorBranch(opsRhs: Var -> IfBody)(implicit useNewDefs: Bool): OperatorBranch = opsRhs match { @@ -137,6 +144,20 @@ trait Transformation { self: mlscript.pretyper.Traceable => } private def rare: Nothing = throw new TransformException(msg"Wow, a rare case.", N) + + private def splitAnd(t: Term): Ls[Term] = trace(s"splitAnd <== ${inspect.deep(t)}") { + t match { + case App( + App(Var("and"), + Tup((_ -> Fld(_, lhs)) :: Nil)), + Tup((_ -> Fld(_, rhs)) :: Nil) + ) => // * Old-style operators + splitAnd(lhs) :+ rhs + case App(Var("and"), PlainTup(lhs, rhs)) => + splitAnd(lhs) :+ rhs + case _ => t :: Nil + } + }(r => "splitAnd ==> " + r.iterator.map(_.toString).mkString(" ∧ ")) } object Transformation { diff --git a/shared/src/main/scala/mlscript/ucs/syntax.scala b/shared/src/main/scala/mlscript/ucs/syntax.scala index 61d107bf..85b47765 100644 --- a/shared/src/main/scala/mlscript/ucs/syntax.scala +++ b/shared/src/main/scala/mlscript/ucs/syntax.scala @@ -110,12 +110,12 @@ package object syntax { def printTermSplit(split: TermSplit): Str = { // TODO: tailrec - def termSplit(split: TermSplit, isFirst: Bool, isTopLevel: Bool): Lines = split match { + def termSplit(split: TermSplit, isFirst: Bool, isAfterAnd: Bool): Lines = split match { case Split.Cons(head, tail) => (termBranch(head) match { - case (n, line) :: tail => (n, (if (isTopLevel) "" else "and ") + s"$line") :: tail + case (n, line) :: tail => (n, (if (isAfterAnd) "" else "and ") + s"$line") :: tail case Nil => Nil - }) ::: termSplit(tail, false, isTopLevel) - case Split.Let(_, nme, rhs, tail) => (0, s"let $nme = $rhs") :: termSplit(tail, false, isTopLevel) + }) ::: termSplit(tail, false, isAfterAnd) + case Split.Let(_, nme, rhs, tail) => (0, s"let $nme = $rhs") :: termSplit(tail, false, isAfterAnd) case Split.Else(term) => (if (isFirst) (0, s"then $term") else (0, s"else $term")) :: Nil case Split.Nil => Nil } @@ -142,7 +142,7 @@ package object syntax { def operatorBranch(branch: OperatorBranch): Lines = s"${branch.operator}" #: (branch match { case OperatorBranch.Match(_, continuation) => patternSplit(continuation) - case OperatorBranch.Binary(_, continuation) => termSplit(continuation, true, false) + case OperatorBranch.Binary(_, continuation) => termSplit(continuation, true, true) }) def patternBranch(branch: PatternBranch): Lines = { val PatternBranch(pattern, continuation) = branch diff --git a/shared/src/main/scala/mlscript/utils/inspect.scala b/shared/src/main/scala/mlscript/utils/inspect.scala index 5a88c1f3..63a57302 100644 --- a/shared/src/main/scala/mlscript/utils/inspect.scala +++ b/shared/src/main/scala/mlscript/utils/inspect.scala @@ -15,6 +15,15 @@ object inspect { if (arity === 0) { name } else s"${name}(${(", _" * arity).drop(2)})" } + def apply(body: IfBody): Str = body match { + case IfOpApp(lhs, op, rhs) => s"IfOpApp(${apply(lhs)}, ${apply(op)}, _)" + case IfLet(isRec, name, rhs, body) => s"IfLet($isRec, $name, _, _)" + case IfThen(expr, rhs) => s"IfThen(${apply(expr)}, _)" + case IfOpsApp(lhs, opsRhss) => s"IfOpsApp(${apply(lhs)}, ${opsRhss.map { case (op, body) => s"$op -> _" }.mkString("; ")})" + case IfBlock(lines) => s"IfBlock(_)" + case IfElse(expr) => s"IfElse(${apply(expr)})" + } + def apply(d: TypingUnit): Str = d.entities.iterator .map(apply) .mkString("{", ", ", "}") diff --git a/shared/src/test/diff/pretyper/ucs/Overlaps.mls b/shared/src/test/diff/pretyper/ucs/Overlaps.mls index eb65c35e..fb78fa19 100644 --- a/shared/src/test/diff/pretyper/ucs/Overlaps.mls +++ b/shared/src/test/diff/pretyper/ucs/Overlaps.mls @@ -21,27 +21,25 @@ fun this_is_sealed(x: Shape) = LineSegment(_, _) then "LineSegment" //│ fun this_is_sealed: (x: Shape) -> ("Circle" | "LineSegment" | "Rectangle") -// TODO +// The error message here makes some sense right now. +:e fun missing_a_case(x: Shape) = if x is Circle(_, _) then "Circle" Rectangle(_, _, _) then "Rectangle" //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.26: if x is +//│ ║ l.27: if x is //│ ║ ^^^^ -//│ ║ l.27: Circle(_, _) then "Circle" +//│ ║ l.28: Circle(_, _) then "Circle" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.28: Rectangle(_, _, _) then "Rectangle" +//│ ║ l.29: Rectangle(_, _, _) then "Rectangle" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── type `LineSegment` is not an instance of type `Rectangle` -//│ ║ l.25: fun missing_a_case(x: Shape) = +//│ ╟── type `Circle | LineSegment | Rectangle` does not match type `Circle | Rectangle` +//│ ║ l.26: fun missing_a_case(x: Shape) = //│ ║ ^^^^^ -//│ ╟── but it flows into reference with expected type `Rectangle` -//│ ║ l.26: if x is -//│ ║ ^ -//│ ╟── Note: constraint arises from class pattern: -//│ ║ l.28: Rectangle(_, _, _) then "Rectangle" -//│ ╙── ^^^^^^^^^ +//│ ╟── but it flows into reference with expected type `Circle | Rectangle` +//│ ║ l.27: if x is +//│ ╙── ^ //│ fun missing_a_case: (x: Shape) -> ("Circle" | "Rectangle") fun missing_a_case(x: Shape) = @@ -51,7 +49,7 @@ fun missing_a_case(x: Shape) = else x //│ fun missing_a_case: (x: Shape) -> ("Circle" | "Rectangle" | LineSegment) -// TODO +// TODO: Why doesn't `Shape` match `Circle | Rectangle | LineSegment`? fun countLineSegments(x) = if x is Shape and hidden(x) then "bro" @@ -59,18 +57,20 @@ fun countLineSegments(x) = LineSegment(_, _) then "bro" Circle(_, _) then "bro" //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.56: if x is +//│ ║ l.54: if x is //│ ║ ^^^^ -//│ ║ l.57: Shape and hidden(x) then "bro" +//│ ║ l.55: Shape and hidden(x) then "bro" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.58: Rectangle(_, _, _) then "bro" +//│ ║ l.56: Rectangle(_, _, _) then "bro" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.59: LineSegment(_, _) then "bro" +//│ ║ l.57: LineSegment(_, _) then "bro" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.60: Circle(_, _) then "bro" +//│ ║ l.58: Circle(_, _) then "bro" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── expression of type `Shape & ~#LineSegment & ~#Rectangle` is not an instance of type `Circle` -//│ ╟── Note: constraint arises from class pattern: -//│ ║ l.60: Circle(_, _) then "bro" -//│ ╙── ^^^^^^ +//│ ╟── class pattern of type `Shape` does not match type `Circle | LineSegment | Rectangle` +//│ ║ l.55: Shape and hidden(x) then "bro" +//│ ║ ^^^^^ +//│ ╟── but it flows into reference with expected type `Circle | LineSegment | Rectangle` +//│ ║ l.54: if x is +//│ ╙── ^ //│ fun countLineSegments: Shape -> "bro" diff --git a/shared/src/test/diff/pretyper/ucs/TransfromUCS.mls b/shared/src/test/diff/pretyper/ucs/TransfromUCS.mls index 2facf100..31eac641 100644 --- a/shared/src/test/diff/pretyper/ucs/TransfromUCS.mls +++ b/shared/src/test/diff/pretyper/ucs/TransfromUCS.mls @@ -35,7 +35,7 @@ fun zipWith(f, xs, ys) = Cons(x, xs) and ys is Cons(y, ys) and zipWith(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) Nil and ys is Nil then Some(Nil) else None -//│ fun zipWith: forall 'T 'T0 'T1. (('T, 'T0) -> 'T1, Cons['T] | Object & ~#Cons, Cons['T0] | Object & ~#Cons) -> (None | Some[Cons['T1] | Nil]) +//│ fun zipWith: forall 'T 'T0 'T1. (('T, 'T0) -> 'T1, Cons['T] | Nil | Object & ~#Cons & ~#Nil, Cons['T0] | Object & ~#Cons) -> (None | Some[Cons['T1] | Nil]) fun getOrElse[T](x: Option[T], default: T): T = diff --git a/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls b/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls index 996388f0..c87fec46 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls @@ -9,7 +9,7 @@ class App(func: Term, arg: Term) extends Term //│ class Abs(param: Str, body: Term) extends Term //│ class App(func: Term, arg: Term) extends Term -// FIXME +// TODO: Why doesn't `Term` match `Abs | App | Var`? fun is_value(term) = if term is Term and term is Abs(_, _) then true @@ -24,35 +24,39 @@ fun is_value(term) = //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.17: App(_, _) then false //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── expression of type `Term & ~#Abs & ~#Var` is not an instance of type `App` -//│ ╟── Note: constraint arises from class pattern: -//│ ║ l.17: App(_, _) then false -//│ ╙── ^^^ +//│ ╟── class pattern of type `Term` does not match type `Abs | App | Var` +//│ ║ l.14: if term is Term and term is +//│ ║ ^^^^ +//│ ╟── but it flows into reference with expected type `Abs | App | Var` +//│ ║ l.14: if term is Term and term is +//│ ╙── ^^^^ //│ fun is_value: Term -> Bool :e -fun is_value(term) = +fun is_value'(term) = if term is Term and term is Abs(_, _) then true Var(_) then false //│ ╔══[ERROR] When scrutinee `term` is `Term` -//│ ║ l.35: if term is Term and term is +//│ ║ l.37: if term is Term and term is //│ ║ ^^^^ //│ ╟── Scrutinee `term` has 1 missing case -//│ ║ l.34: fun is_value(term) = -//│ ║ ^^^^ +//│ ║ l.36: fun is_value'(term) = +//│ ║ ^^^^ //│ ╟── It can be class `App` //│ ║ l.6: class App(func: Term, arg: Term) extends Term //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.35: if term is Term and term is +//│ ║ l.37: if term is Term and term is //│ ║ ^^^^^^^ -//│ ║ l.36: Abs(_, _) then true +//│ ║ l.38: Abs(_, _) then true //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.37: Var(_) then false +//│ ║ l.39: Var(_) then false //│ ║ ^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── expression of type `Term & ~#Abs` is not an instance of type `Var` -//│ ╟── Note: constraint arises from class pattern: -//│ ║ l.37: Var(_) then false -//│ ╙── ^^^ -//│ fun is_value: Term -> Bool +//│ ╟── class pattern of type `Term` does not match type `Abs | Var` +//│ ║ l.37: if term is Term and term is +//│ ║ ^^^^ +//│ ╟── but it flows into reference with expected type `Abs | Var` +//│ ║ l.37: if term is Term and term is +//│ ╙── ^^^^ +//│ fun is_value': Term -> Bool diff --git a/shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls b/shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls new file mode 100644 index 00000000..b04eb208 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls @@ -0,0 +1,309 @@ +:PreTyper + +fun (|>) pipe(x, f) = f(x) +fun (~~>) toBe(x, y) = if x === y then () else error +fun (?) max(x, y) = if x > y then x else y +fun abs(x) = if x < 0 then -x else x +//│ fun (|>) pipe: forall 'a 'b. ('a, 'a -> 'b) -> 'b +//│ fun (~~>) toBe: forall 'c. (Eql['c], 'c) -> () +//│ fun ( 'd +//│ fun (>?) max: forall 'e. (Num & 'e, Num & 'e) -> 'e +//│ fun abs: Int -> Int + +abstract class Option[out T]: (Some[T] | None) +class Some[out T](val value: T) extends Option[T] +module None extends Option[nothing] +//│ abstract class Option[T]: None | Some[T] +//│ class Some[T](value: T) extends Option +//│ module None extends Option + +fun (??) getOrElse(o, v) = if o is + Some(v') then v' + None then v +//│ fun (??) getOrElse: forall 'a. (None | Some['a], 'a) -> 'a + +fun (++) strcat(s1, s2) = concat(s1)(s2) +//│ fun (++) strcat: (Str, Str) -> Str + +let anyToString = toString +//│ let anyToString: anything -> Str +//│ anyToString +//│ = [Function: toString] + +abstract class List[out T]: (Cons[T] | Nil) +class Cons[out T](val head: T, val tail: List[T]) extends List[T] +module Nil extends List[nothing] +//│ abstract class List[T]: Cons[T] | Nil +//│ class Cons[T](head: T, tail: List[T]) extends List +//│ module Nil extends List + +fun (::) cons(head, tail) = Cons(head, tail) +//│ fun (::) cons: forall 'T. ('T, List['T]) -> Cons['T] + +abstract class Tree[out A]: (Empty | Node[A]) +class Node[A](value: A, left: Tree[A], right: Tree[A], height: Int) extends Tree[A] +module Empty extends Tree[nothing] +//│ abstract class Tree[A]: Empty | Node[A] +//│ class Node[A](value: A, left: Tree[A], right: Tree[A], height: Int) extends Tree +//│ module Empty extends Tree + +fun showTree(t: Tree[anything]): Str = if t is + Node(v, l, r, _) then + "(" ++ showTree(l) ++ " " ++ toString(v) ++ " " ++ showTree(r) ++ ")" + Empty then "•" +//│ fun showTree: (t: Tree[anything]) -> Str + +fun showTreeWithHeight(t: Tree[anything]): Str = if t is + Node(v, l, r, h) then + "(" ++ showTreeWithHeight(l) ++ " " ++ toString(v) ++ " [" ++ toString(h) ++ "]" ++ " " ++ showTreeWithHeight(r) ++ ")" + Empty then "•" +//│ fun showTreeWithHeight: (t: Tree[anything]) -> Str + +fun height(t) = + if t is + Node(_, _, _, h) then h + Empty then 0 +//│ fun height: (Empty | Node[anything]) -> Int + +let nil = Empty +fun node(v, l, r) = Node(v, l, r, max(height(l), height(r)) + 1) +fun single(v) = Node(v, Empty, Empty, 1) +//│ let nil: Empty +//│ fun node: forall 'A. ('A, Empty & Tree['A] | Node[anything] & Tree['A], Empty & Tree['A] | Node[anything] & Tree['A]) -> Node['A] +//│ fun single: forall 'A0. 'A0 -> Node['A0] +//│ nil +//│ = Empty { class: [class Empty extends Tree] } + +fun isBalanced(t) = + if t is + Node(t, l, r, _) then + (abs(height(l) - height(r)) <= 1) && isBalanced(l) && isBalanced(r) + Empty then true +//│ fun isBalanced: (Empty | Node[anything]) -> Bool + +isBalanced(nil) ~~> true +//│ () +//│ res +//│ = undefined + +// _ _ _ _ +// ___(_)_ __ __ _| | ___ _ __ ___ | |_ __ _| |_ ___ +// / __| | '_ \ / _` | |/ _ \ | '__/ _ \| __/ _` | __/ _ \ +// \__ \ | | | | (_| | | __/ | | | (_) | || (_| | || __/ +// |___/_|_| |_|\__, |_|\___| |_| \___/ \__\__,_|\__\___| +// |___/ + +fun rotateLeft(t: Tree['A]): Tree['A] = + if t is + Node(v, l, Node(v', l', r', _), _) and + max(height(l), height(l')) + 1 is h and + max(h, height(r')) + 1 is h' then + Node(v', Node(v, l, l', h), r', h') + _ then t +//│ fun rotateLeft: forall 'A. (t: Tree['A]) -> Tree['A] + +rotateLeft(nil) |> showTree +rotateLeft(single(0)) |> showTree +//│ Str +//│ res +//│ = '•' +//│ res +//│ = '(• 0 •)' + +let unbalanced1 = node(0, nil, node(1, nil, single(2))) +isBalanced(unbalanced1) ~~> false +unbalanced1 |> showTree +rotateLeft(unbalanced1) |> showTree +//│ let unbalanced1: Node[0 | 1 | 2] +//│ Str +//│ unbalanced1 +//│ = Node {} +//│ res +//│ = undefined +//│ res +//│ = '(• 0 (• 1 (• 2 •)))' +//│ res +//│ = '((• 0 •) 1 (• 2 •))' + +fun rotateRight(t: Tree['A]): Tree['A] = + if t is + Node(v, Node(v', l', r', _), r, _) then + Node(v', l', Node(v, r', r, 0), 0) + _ then t +//│ fun rotateRight: forall 'A. (t: Tree['A]) -> Tree['A] + +rotateRight(nil) |> showTree +rotateRight(single(0)) |> showTree +//│ Str +//│ res +//│ = '•' +//│ res +//│ = '(• 0 •)' + +let unbalanced2 = node(2, node(1, single(0), nil), nil) +isBalanced(unbalanced2) ~~> false +unbalanced2 |> showTree +rotateRight(unbalanced2) |> showTree +//│ let unbalanced2: Node[0 | 1 | 2] +//│ Str +//│ unbalanced2 +//│ = Node {} +//│ res +//│ = undefined +//│ res +//│ = '(((• 0 •) 1 •) 2 •)' +//│ res +//│ = '((• 0 •) 1 (• 2 •))' + +// _ _ _ _ _ +// __| | ___ _ _| |__ | | ___ _ __ ___ | |_ __ _| |_ ___ +// / _` |/ _ \| | | | '_ \| |/ _ \ | '__/ _ \| __/ _` | __/ _ \ +// | (_| | (_) | |_| | |_) | | __/ | | | (_) | || (_| | || __/ +// \__,_|\___/ \__,_|_.__/|_|\___| |_| \___/ \__\__,_|\__\___| +// + +fun rotateRightLeft(t: Tree['A]): Tree['A] = + if t is + Node(v, t1, Node(v', Node(v'', t2, t3, _), t4, _), _) then + Node(v'', Node(v, t1, t2, 0), Node(v', t3, t4, 0), 0) + else t +//│ fun rotateRightLeft: forall 'A. (t: Tree['A]) -> Tree['A] + +// Should remain the same. +rotateRightLeft(nil) |> showTree +rotateRightLeft(single(0)) |> showTree +rotateRightLeft(unbalanced1) |> showTree +rotateRightLeft(unbalanced2) |> showTree +//│ Str +//│ res +//│ = '•' +//│ res +//│ = '(• 0 •)' +//│ res +//│ = '(• 0 (• 1 (• 2 •)))' +//│ res +//│ = '(((• 0 •) 1 •) 2 •)' + +let unbalanced3 = node(0, nil, node(3, node(1, nil, single(2)), nil)) +isBalanced(unbalanced3) ~~> false +unbalanced3 |> showTree +rotateRightLeft(unbalanced3) |> showTree +//│ let unbalanced3: Node[0 | 1 | 2 | 3] +//│ Str +//│ unbalanced3 +//│ = Node {} +//│ res +//│ = undefined +//│ res +//│ = '(• 0 ((• 1 (• 2 •)) 3 •))' +//│ res +//│ = '((• 0 •) 1 ((• 2 •) 3 •))' + +fun rotateLeftRight(t: Tree['A]): Tree['A] = + if t is + Node(v, Node(v', t1, Node(v'', t2, t3, _), _), t4, _) then + Node(v'', Node(v', t1, t2, 0), Node(v, t3, t4, 0), 0) + else t +//│ fun rotateLeftRight: forall 'A. (t: Tree['A]) -> Tree['A] + +// Should remain the same. +rotateLeftRight(nil) |> showTree +rotateLeftRight(single(0)) |> showTree +rotateLeftRight(unbalanced1) |> showTree +rotateLeftRight(unbalanced2) |> showTree +//│ Str +//│ res +//│ = '•' +//│ res +//│ = '(• 0 •)' +//│ res +//│ = '(• 0 (• 1 (• 2 •)))' +//│ res +//│ = '(((• 0 •) 1 •) 2 •)' + +let unbalanced4 = node(3, node(0, nil, node(2, single(1), nil)), nil) +isBalanced(unbalanced4) ~~> false +unbalanced4 |> showTree +rotateRightLeft(unbalanced4) |> showTree +//│ let unbalanced4: Node[0 | 1 | 2 | 3] +//│ Str +//│ unbalanced4 +//│ = Node {} +//│ res +//│ = undefined +//│ res +//│ = '((• 0 ((• 1 •) 2 •)) 3 •)' +//│ res +//│ = '((• 0 ((• 1 •) 2 •)) 3 •)' + +fun bf(t) = + if t is + Node(_, l, r, _) then height(l) - height(r) + Empty then 0 +//│ fun bf: (Empty | Node[anything]) -> Int + +// _ _ +// (_)_ __ ___ ___ _ __| |_ +// | | '_ \/ __|/ _ \ '__| __| +// | | | | \__ \ __/ | | |_ +// |_|_| |_|___/\___|_| \__| +// + +// This function does not work for now as it exposed a lot of problems we have +// in desugaring and normalization. For example: +// +// - [x] We need to mark the Boolean scrutinees and skip the specialization of +// these scrutinees in the normalization process, otherwise, it would result +// in many futile computations. +// - [x] We should cache the expressions that are broken by conditional splits, +// otherwise, they will be evaluated for more than one time. +// - [ ] The branches of an operator split should be "chained" rather +// than placed in parallel, otherwise, the later branches will appear in the +// else branch of all its previous branches. **WORK IN PROGRESS** +// +// :dpt:postprocess +fun balance(t: Tree['A]): Tree['A] = + if t is + Node(x, l, r, _) and height(r) - height(l) + > 1 and r is Node(y, l', r', _) and height(r') - height(l') + > 0 then rotateLeft(t) + < 0 and l' is Node then rotateRightLeft(t) + < 1 and l is Node(y, l', r', _) and height(r') - height(l') + > 0 and r' is Node then rotateLeftRight(t) + < 0 then rotateRight(t) + _ then t +//│ fun balance: forall 'A. (t: Tree['A]) -> Tree['A] + +fun insert(t: Tree[Num], v: Num) = if t is + Node(v', l, r, h) and + v < v' then Node(v', insert(l, v), r, h) |> balance + v > v' then Node(v', l, insert(r, v), h) |> balance + _ then t + Empty then Node(v, Empty, Empty, 1) +//│ fun insert: (t: Tree[Num], v: Num) -> (Node[Num] | Tree[Num]) + + +insert(nil, 0) |> showTree +insert(single(0), 1) |> showTree +insert(single(0), -1) |> showTree +//│ Str +//│ res +//│ = '(• 0 •)' +//│ res +//│ = '(• 0 (• 1 •))' +//│ res +//│ = '((• -1 •) 0 •)' + +insert(node(0, nil, single(1)), 2) |> showTreeWithHeight +insert(node(0, nil, single(1)), 2) |> showTree +//│ Str +//│ res +//│ = '(• 0 [2] (• 1 [1] (• 2 [1] •)))' +//│ res +//│ = '(• 0 (• 1 (• 2 •)))' + +insert(unbalanced1, 3) |> showTree +//│ Str +//│ res +//│ = '((• 0 •) 1 (• 2 (• 3 •)))' diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index d8a2cf98..3dac9144 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -151,7 +151,7 @@ class DiffTests explainErrors: Bool = false, dbg: Bool = false, dbgParsing: Bool = false, - dbgPreTyper: Opt[Int] = N, + dbgPreTyper: Opt[Set[Str]] = N, dbgSimplif: Bool = false, dbgUCS: Bool = false, fullExceptionStack: Bool = false, @@ -215,7 +215,7 @@ class DiffTests case "p" => mode.copy(showParse = true) case "d" => mode.copy(dbg = true) case "dp" => mode.copy(dbgParsing = true) - case PreTyperOption(nv) => mode.copy(dbgPreTyper = S(nv)) + case PreTyperFlags(ts) => mode.copy(dbgPreTyper = S(ts)) case "ds" => mode.copy(dbgSimplif = true) case "ducs" => mode.copy(dbg = true, dbgUCS = true) case "s" => mode.copy(fullExceptionStack = true) @@ -526,7 +526,7 @@ class DiffTests if (usePreTyper) { val preTyper = new PreTyper(mode.dbgPreTyper, newDefs) { override protected def raise(diagnostics: Ls[Diagnostic]): Unit = report(diagnostics) - override def emitDbg(str: String): Unit = output(str) + override def emitString(str: String): Unit = output(str) } // This should be passed to code generation somehow. preTyperScope = preTyper.process(rootTypingUnit, preTyperScope, "")._1 @@ -1162,12 +1162,17 @@ object DiffTests { // file.segments.toList.init.lastOption.contains("parser") } - object PreTyperOption { - def unapply(str: String): Option[Int] = str match { - case "dpt" => Some(0) - case "dpt:v" => Some(1) - case "dpt:vv" => Some(2) - case _ => None - } + object PreTyperFlags { + private val pattern = "^dpt(?::\\s*([A-Za-z]+)(,\\s*[A-Za-z]+)*)?$".r + def unapply(flags: Str): Opt[Set[Str]] = + flags match { + case pattern(head, tail) => + (Option(head), Option(tail)) match { + case (N, _) => S(Set.empty) + case (S(head), N) => S(Set.single(head)) + case (S(head), S(tail)) => S(Set.from(head :: tail.split(",").drop(1).toList)) + } + case _ => N + } } } From ddb9f2ca552663c316a78a18eb00a1ab24f99772 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sat, 23 Dec 2023 00:58:34 +0800 Subject: [PATCH 018/143] Remove unreachable code from previous merging stashes --- shared/src/main/scala/mlscript/pretyper/PreTyper.scala | 5 ----- 1 file changed, 5 deletions(-) diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index c48e582e..a65cf8d7 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -120,11 +120,6 @@ class PreTyper(override val debugTopics: Opt[Set[Str]], useNewDefs: Bool) extend traverseTerm(base) traverseTypingUnit(decls, "Rft", scope) () - case NuNew(cls) => traverseTerm(cls) - case Rft(base, decls) => - traverseTerm(base) - traverseTypingUnit(decls, "Rft", scope) - () } }(_ => s"traverseTerm ==> ${inspect.shallow(term)}") From daeea5986255e516145fb4003bde6fe67ce205e0 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sat, 23 Dec 2023 03:01:00 +0800 Subject: [PATCH 019/143] Add two test cases: list fold functions and list permutations --- .../scala/mlscript/pretyper/PreTyper.scala | 65 +++--- .../main/scala/mlscript/utils/inspect.scala | 10 +- shared/src/test/diff/mlscript/Basics.mls | 2 +- .../src/test/diff/mlscript/ByNameByValue.mls | 4 +- shared/src/test/diff/mlscript/MultiArgs.mls | 8 +- shared/src/test/diff/mlscript/Ops.mls | 10 +- shared/src/test/diff/nu/AbstractClasses.mls | 2 +- shared/src/test/diff/nu/LamPatterns.mls | 2 +- shared/src/test/diff/nu/OverrideShorthand.mls | 4 +- shared/src/test/diff/nu/RightAssocOps.mls | 6 +- .../ucs/examples/BinarySearchTree.mls | 11 +- .../diff/pretyper/ucs/examples/ListFold.mls | 208 ++++++++++++++++++ .../pretyper/ucs/examples/Permutations.mls | 127 +++++++++++ shared/src/test/diff/ucs/LeadingAnd.mls | 4 +- shared/src/test/diff/ucs/SplitOps.mls | 4 +- .../src/test/scala/mlscript/DiffTests.scala | 2 +- 16 files changed, 405 insertions(+), 64 deletions(-) create mode 100644 shared/src/test/diff/pretyper/ucs/examples/ListFold.mls create mode 100644 shared/src/test/diff/pretyper/ucs/examples/Permutations.mls diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index a65cf8d7..a0311fbe 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -7,34 +7,30 @@ import mlscript._, utils._, shorthands._ import scala.annotation.tailrec import mlscript.Message, Message.MessageContext -class PreTyper(override val debugTopics: Opt[Set[Str]], useNewDefs: Bool) extends Traceable with DesugarUCS { +class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with DesugarUCS { import PreTyper._ protected def raise(diagnostics: Diagnostic): Unit = () protected def raise(diagnostics: Ls[Diagnostic]): Unit = () - private def extractParameters(fields: Term): Ls[ValueSymbol] = fields match { - case Tup(arguments) => - println(s"arguments: ${inspect.deep(fields)}") - if (useNewDefs) { - arguments.map { - case (S(nme: Var), Fld(_, _)) => new ValueSymbol(nme, false) - case (_, Fld(_, nme: Var)) => new ValueSymbol(nme, false) - case (_, Fld(_, x)) => println(x.toString); ??? - } - } else { - arguments.map { - case (_, Fld(_, nme: Var)) => new ValueSymbol(nme, false) - case (_, Fld(_, x)) => println(x.toString); ??? - } - } - case PlainTup(arguments @ _*) => - arguments.map { - case nme: Var => new ValueSymbol(nme, false) + private def extractParameters(fields: Term): Ls[ValueSymbol] = + trace(s"extractParameters <== ${inspect.deep(fields)}") { + fields match { + case Tup(arguments) => + arguments.map { + case (S(nme: Var), Fld(_, _)) => new ValueSymbol(nme, false) + case (_, Fld(_, nme: Var)) => new ValueSymbol(nme, false) + case (_, Fld(_, Bra(false, nme: Var))) => new ValueSymbol(nme, false) + case (_, _) => ??? + } + case PlainTup(arguments @ _*) => + arguments.map { + case nme: Var => new ValueSymbol(nme, false) + case other => println("Unknown parameters: " + inspect.deep(other)); ??? // TODO: bad + }.toList case other => println("Unknown parameters: " + inspect.deep(other)); ??? // TODO: bad - }.toList - case other => println("Unknown parameters: " + inspect.deep(other)); ??? // TODO: bad - } + } + }(rs => s"extractParameters ==> ${rs.iterator.map(_.name).mkString(", ")}") // `traverseIf` is meaningless because it represents patterns with terms. @@ -94,10 +90,9 @@ class PreTyper(override val debugTopics: Opt[Set[Str]], useNewDefs: Bool) extend case ef @ If(_, _) => traverseIf(ef)(scope) case TyApp(lhs, targs) => // TODO: When? case Eqn(lhs, rhs) => ??? // TODO: How? - case Blk(stmts) => stmts.foreach { - case t: Term => traverseTerm(t) - case _ => ??? // TODO: When? - } + case Blk(stmts) => + traverseStatements(stmts, "block", scope) + () case Subs(arr, idx) => traverseTerm(arr); traverseTerm(idx) case Bind(lhs, rhs) => traverseTerm(lhs); traverseTerm(rhs) case Splc(fields) => fields.foreach { @@ -118,14 +113,14 @@ class PreTyper(override val debugTopics: Opt[Set[Str]], useNewDefs: Bool) extend case NuNew(cls) => traverseTerm(cls) case Rft(base, decls) => // For object refinement traverseTerm(base) - traverseTypingUnit(decls, "Rft", scope) + traverseStatements(decls.entities, "Rft", scope) () } }(_ => s"traverseTerm ==> ${inspect.shallow(term)}") private def traverseTypeDefinition(symbol: TypeSymbol, defn: NuTypeDef)(implicit scope: Scope): Unit = trace(s"traverseTypeDefinition <== ${defn.describe}") { - traverseTypingUnit(defn.body, defn.nme.name, scope) + traverseStatements(defn.body.entities, defn.nme.name, scope) () }(_ => s"traverseTypeDefinition <== ${defn.describe}") @@ -145,11 +140,11 @@ class PreTyper(override val debugTopics: Opt[Set[Str]], useNewDefs: Bool) extend }() - private def traverseTypingUnit(typingUnit: TypingUnit, name: Str, parentScope: Scope): (Scope, TypeContents) = - trace(s"traverseTypingUnit <== $name: ${typingUnit.describe}") { + private def traverseStatements(statements: Ls[Statement], name: Str, parentScope: Scope): (Scope, TypeContents) = + trace(s"traverseStatements <== $name: ${"statement".pluralize(statements.size, true)}") { import mlscript.{Cls, Trt, Mxn, Als, Mod} // Pass 1: Build a scope with hoisted symbols. - val hoistedScope = typingUnit.entities.foldLeft(parentScope.derive) { + val hoistedScope = statements.foldLeft(parentScope.derive) { case (acc, _: Term) => acc // Skip case (acc, defn: NuTypeDef) => val `var` = Var(defn.nme.name).withLoc(defn.nme.toLoc) @@ -172,7 +167,7 @@ class PreTyper(override val debugTopics: Opt[Set[Str]], useNewDefs: Bool) extend case (acc, _: Constructor | _: DataDefn | _: DatatypeDefn | _: Def | _: LetS | _: TypeDef) => ??? // TODO: When? } // Resolve base types. - val subtypingRelations = typingUnit.entities.foldLeft(Map.empty[Var, Ls[Var]]) { + val subtypingRelations = statements.foldLeft(Map.empty[Var, Ls[Var]]) { case (acc, defn: NuTypeDef) => val thisType = Var(defn.nme.name).withLoc(defn.nme.toLoc) val superTypes = extractSuperTypes(defn.parents) @@ -217,7 +212,7 @@ class PreTyper(override val debugTopics: Opt[Set[Str]], useNewDefs: Bool) extend println(hoistedScope.symbols.map(_.name).mkString("(hoisted) scope = {", ", ", "}")) // // Pass 2: Visit non-hoisted and build a complete scope. - val completeScope = typingUnit.entities.foldLeft[Scope](hoistedScope) { + val completeScope = statements.foldLeft[Scope](hoistedScope) { case (acc, term: Term) => traverseTerm(term)(acc); acc case (acc, defn: NuTypeDef) => acc case (acc, defn @ NuFunDef(Some(rec), nme, _, _, L(rhs))) => @@ -254,11 +249,11 @@ class PreTyper(override val debugTopics: Opt[Set[Str]], useNewDefs: Bool) extend case _: FunctionSymbol | _: ValueSymbol | _: SubValueSymbol => () } (completeScope, new TypeContents) - }({ case (scope, contents) => s"traverseTypingUnit ==> Scope {${scope.showLocalSymbols}}" }) + }({ case (scope, contents) => s"traverseStatements ==> Scope {${scope.showLocalSymbols}}" }) def process(typingUnit: TypingUnit, scope: Scope, name: Str): (Scope, TypeContents) = trace(s"process <== $name: ${typingUnit.describe}") { - traverseTypingUnit(typingUnit, name, scope) + traverseStatements(typingUnit.entities, name, scope) }({ case (scope, contents) => s"process ==> ${scope.showLocalSymbols}" }) } diff --git a/shared/src/main/scala/mlscript/utils/inspect.scala b/shared/src/main/scala/mlscript/utils/inspect.scala index 63a57302..5b095229 100644 --- a/shared/src/main/scala/mlscript/utils/inspect.scala +++ b/shared/src/main/scala/mlscript/utils/inspect.scala @@ -62,11 +62,13 @@ object inspect { case Var(name) => s"Var(\"$name\")" case Lam(lhs, rhs) => s"Lam(${apply(lhs)}, ${apply(rhs)})" case App(lhs, rhs) => s"App(${apply(lhs)}, ${apply(rhs)})" + case Tup(Nil) => "Tup(Nil)" case Tup(fields) => - fields.iterator.map { - case (S(name), Fld(_, value)) => s"(S(${apply(name)}), ${apply(value)})" - case (N, Fld(_, value)) => s"(N, ${apply(value)})" - }.mkString("Tup(", ", ", ")") + fields.iterator.map { case (maybeName, Fld(flags, value)) => + val first = maybeName.fold("N") { name => s"S($name)" } + val second = s"Fld(_, ${apply(value)})" + s"($first, $second)" + }.mkString("Tup(", " :: ", " :: Nil)") case Rcd(fields) => fields.iterator.map { case k -> Fld(_, v) => s"${apply(k)} = ${apply(v)}" }.mkString("Rcd(", ", ", ")") case Sel(receiver, fieldName) => s"Sel(${apply(receiver)}, $fieldName)" diff --git a/shared/src/test/diff/mlscript/Basics.mls b/shared/src/test/diff/mlscript/Basics.mls index 2fb3e1b4..26a81c59 100644 --- a/shared/src/test/diff/mlscript/Basics.mls +++ b/shared/src/test/diff/mlscript/Basics.mls @@ -96,7 +96,7 @@ def f (x y z) = add x y //│ ╙── ^^^^^ //│ f: error -> int //│ Code generation encountered an error: -//│ term App(App(Var("x"), Tup((N, Var("y")))), Tup((N, Var("z")))) is not a valid pattern +//│ term App(App(Var("x"), Tup((N, Fld(_, Var("y"))) :: Nil)), Tup((N, Fld(_, Var("z"))) :: Nil)) is not a valid pattern f 1 //│ res: int diff --git a/shared/src/test/diff/mlscript/ByNameByValue.mls b/shared/src/test/diff/mlscript/ByNameByValue.mls index 5d481618..c13304d1 100644 --- a/shared/src/test/diff/mlscript/ByNameByValue.mls +++ b/shared/src/test/diff/mlscript/ByNameByValue.mls @@ -16,7 +16,7 @@ def incr x = x.a <- x.a + 1 def gensym = let n = { mut a = 0 } in fun () -> (incr n, n) //│ Parsed: def gensym: let n = {mut a: 0} in () => [incr(n,), n,]; //│ Desugared: def gensym: let n = {mut a: 0} in () => [incr(n,), n,] -//│ AST: Def(false, Var("gensym"), L(Let(false, n, Rcd(Var("a") = IntLit(0)), Lam(Tup(), Tup((N, App(Var("incr"), Tup((N, Var("n"))))), (N, Var("n")))))), true) +//│ AST: Def(false, Var("gensym"), L(Let(false, n, Rcd(Var("a") = IntLit(0)), Lam(Tup(Nil), Tup((N, Fld(_, App(Var("incr"), Tup((N, Fld(_, Var("n"))) :: Nil)))) :: (N, Fld(_, Var("n"))) :: Nil)))), true) //│ // Query 1 //│ globalThis.gensym = function gensym() { //│ return (((n) => () => [ @@ -35,7 +35,7 @@ def gensym = let n = { mut a = 0 } in fun () -> (incr n, n) gensym1 = let n = { mut a = 0 } in fun () -> (incr n, n) //│ Parsed: let gensym1 = let n = {mut a: 0} in () => [incr(n,), n,]; //│ Desugared: def gensym1: let n = {mut a: 0} in () => [incr(n,), n,] -//│ AST: Def(false, Var("gensym1"), L(Let(false, n, Rcd(Var("a") = IntLit(0)), Lam(Tup(), Tup((N, App(Var("incr"), Tup((N, Var("n"))))), (N, Var("n")))))), false) +//│ AST: Def(false, Var("gensym1"), L(Let(false, n, Rcd(Var("a") = IntLit(0)), Lam(Tup(Nil), Tup((N, Fld(_, App(Var("incr"), Tup((N, Fld(_, Var("n"))) :: Nil)))) :: (N, Fld(_, Var("n"))) :: Nil)))), false) //│ // Query 1 //│ globalThis.gensym1 = ((n) => () => [ //│ incr(n), diff --git a/shared/src/test/diff/mlscript/MultiArgs.mls b/shared/src/test/diff/mlscript/MultiArgs.mls index 738d3567..a22d9095 100644 --- a/shared/src/test/diff/mlscript/MultiArgs.mls +++ b/shared/src/test/diff/mlscript/MultiArgs.mls @@ -75,9 +75,9 @@ f = fun (x, y) -> add x y f(1, 2) //│ Parsed: let f = (x, y,) => add(x,)(y,); f(1, 2,); //│ Desugared: def f: (x, y,) => add(x,)(y,) -//│ AST: Def(false, Var("f"), L(Lam(Tup((N, Var("x")), (N, Var("y"))), App(App(Var("add"), Tup((N, Var("x")))), Tup((N, Var("y")))))), false) +//│ AST: Def(false, Var("f"), L(Lam(Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("y"))) :: Nil), App(App(Var("add"), Tup((N, Fld(_, Var("x"))) :: Nil)), Tup((N, Fld(_, Var("y"))) :: Nil)))), false) //│ Desugared: f(1, 2,) -//│ AST: App(Var("f"), Tup((N, IntLit(1)), (N, IntLit(2)))) +//│ AST: App(Var("f"), Tup((N, Fld(_, IntLit(1))) :: (N, Fld(_, IntLit(2))) :: Nil)) //│ f: (int, int,) -> int //│ = [Function: f] //│ res: int @@ -119,9 +119,9 @@ f = fun ((x, y)) -> add x y f((1, 2)) //│ Parsed: let f = ('(' [x, y,] ')',) => add(x,)(y,); f('(' [1, 2,] ')',); //│ Desugared: def f: ('(' [x, y,] ')',) => add(x,)(y,) -//│ AST: Def(false, Var("f"), L(Lam(Tup((N, Bra(rcd = false, Tup((N, Var("x")), (N, Var("y")))))), App(App(Var("add"), Tup((N, Var("x")))), Tup((N, Var("y")))))), false) +//│ AST: Def(false, Var("f"), L(Lam(Tup((N, Fld(_, Bra(rcd = false, Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("y"))) :: Nil)))) :: Nil), App(App(Var("add"), Tup((N, Fld(_, Var("x"))) :: Nil)), Tup((N, Fld(_, Var("y"))) :: Nil)))), false) //│ Desugared: f('(' [1, 2,] ')',) -//│ AST: App(Var("f"), Tup((N, Bra(rcd = false, Tup((N, IntLit(1)), (N, IntLit(2))))))) +//│ AST: App(Var("f"), Tup((N, Fld(_, Bra(rcd = false, Tup((N, Fld(_, IntLit(1))) :: (N, Fld(_, IntLit(2))) :: Nil)))) :: Nil)) //│ f: ((int, int,),) -> int //│ = [Function: f1] //│ res: int diff --git a/shared/src/test/diff/mlscript/Ops.mls b/shared/src/test/diff/mlscript/Ops.mls index f0ea6554..8a7682fc 100644 --- a/shared/src/test/diff/mlscript/Ops.mls +++ b/shared/src/test/diff/mlscript/Ops.mls @@ -3,7 +3,7 @@ 2 + 2 //│ Parsed: +(2,)(2,); //│ Desugared: +(2,)(2,) -//│ AST: App(App(Var("+"), Tup((N, IntLit(2)))), Tup((N, IntLit(2)))) +//│ AST: App(App(Var("+"), Tup((N, Fld(_, IntLit(2))) :: Nil)), Tup((N, Fld(_, IntLit(2))) :: Nil)) //│ res: int //│ = 4 @@ -11,7 +11,7 @@ 1 + 2 * 2 + 3 //│ Parsed: +(+(1,)(*(2,)(2,),),)(3,); //│ Desugared: +(+(1,)(*(2,)(2,),),)(3,) -//│ AST: App(App(Var("+"), Tup((N, App(App(Var("+"), Tup((N, IntLit(1)))), Tup((N, App(App(Var("*"), Tup((N, IntLit(2)))), Tup((N, IntLit(2)))))))))), Tup((N, IntLit(3)))) +//│ AST: App(App(Var("+"), Tup((N, Fld(_, App(App(Var("+"), Tup((N, Fld(_, IntLit(1))) :: Nil)), Tup((N, Fld(_, App(App(Var("*"), Tup((N, Fld(_, IntLit(2))) :: Nil)), Tup((N, Fld(_, IntLit(2))) :: Nil)))) :: Nil)))) :: Nil)), Tup((N, Fld(_, IntLit(3))) :: Nil)) //│ res: int //│ = 8 @@ -20,7 +20,7 @@ 1 + 2 / 2 + 3 //│ Parsed: +(+(1,)(/(2,)(2,),),)(3,); //│ Desugared: +(+(1,)(/(2,)(2,),),)(3,) -//│ AST: App(App(Var("+"), Tup((N, App(App(Var("+"), Tup((N, IntLit(1)))), Tup((N, App(App(Var("/"), Tup((N, IntLit(2)))), Tup((N, IntLit(2)))))))))), Tup((N, IntLit(3)))) +//│ AST: App(App(Var("+"), Tup((N, Fld(_, App(App(Var("+"), Tup((N, Fld(_, IntLit(1))) :: Nil)), Tup((N, Fld(_, App(App(Var("/"), Tup((N, Fld(_, IntLit(2))) :: Nil)), Tup((N, Fld(_, IntLit(2))) :: Nil)))) :: Nil)))) :: Nil)), Tup((N, Fld(_, IntLit(3))) :: Nil)) //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.20: 1 + 2 / 2 + 3 //│ ║ ^^^^^^^^^ @@ -36,7 +36,7 @@ 1 |> 2 || 3 //│ Parsed: ||(|>(1,)(2,),)(3,); //│ Desugared: ||(|>(1,)(2,),)(3,) -//│ AST: App(App(Var("||"), Tup((N, App(App(Var("|>"), Tup((N, IntLit(1)))), Tup((N, IntLit(2))))))), Tup((N, IntLit(3)))) +//│ AST: App(App(Var("||"), Tup((N, Fld(_, App(App(Var("|>"), Tup((N, Fld(_, IntLit(1))) :: Nil)), Tup((N, Fld(_, IntLit(2))) :: Nil)))) :: Nil)), Tup((N, Fld(_, IntLit(3))) :: Nil)) //│ ╔══[ERROR] identifier not found: |> //│ ║ l.36: 1 |> 2 || 3 //│ ╙── ^^ @@ -54,7 +54,7 @@ true || false && true || false //│ Parsed: ||(||(true,)(&&(false,)(true,),),)(false,); //│ Desugared: ||(||(true,)(&&(false,)(true,),),)(false,) -//│ AST: App(App(Var("||"), Tup((N, App(App(Var("||"), Tup((N, Var("true")))), Tup((N, App(App(Var("&&"), Tup((N, Var("false")))), Tup((N, Var("true")))))))))), Tup((N, Var("false")))) +//│ AST: App(App(Var("||"), Tup((N, Fld(_, App(App(Var("||"), Tup((N, Fld(_, Var("true"))) :: Nil)), Tup((N, Fld(_, App(App(Var("&&"), Tup((N, Fld(_, Var("false"))) :: Nil)), Tup((N, Fld(_, Var("true"))) :: Nil)))) :: Nil)))) :: Nil)), Tup((N, Fld(_, Var("false"))) :: Nil)) //│ res: bool //│ = true diff --git a/shared/src/test/diff/nu/AbstractClasses.mls b/shared/src/test/diff/nu/AbstractClasses.mls index c00679e1..3aa16ec7 100644 --- a/shared/src/test/diff/nu/AbstractClasses.mls +++ b/shared/src/test/diff/nu/AbstractClasses.mls @@ -51,7 +51,7 @@ new Foo(1) { fun f = id } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ error //│ Code generation encountered an error: -//│ cannot generate code for term Rft(App(NuNew(Var("Foo")), Tup((N, IntLit(1)))), TypingUnit(NuFunDef(None, f, N, Nil, L(Var("id"))))) +//│ cannot generate code for term Rft(App(NuNew(Var("Foo")), Tup((N, Fld(_, IntLit(1))) :: Nil)), TypingUnit(NuFunDef(None, f, N, Nil, L(Var("id"))))) abstract class Bar extends Foo(1) diff --git a/shared/src/test/diff/nu/LamPatterns.mls b/shared/src/test/diff/nu/LamPatterns.mls index 98bb1511..a396d13e 100644 --- a/shared/src/test/diff/nu/LamPatterns.mls +++ b/shared/src/test/diff/nu/LamPatterns.mls @@ -14,7 +14,7 @@ Some(x) => x //│ ╙── ^ //│ error -> error //│ Code generation encountered an error: -//│ term App(Var("Some"), Tup((N, Var("x")))) is not a valid pattern +//│ term App(Var("Some"), Tup((N, Fld(_, Var("x"))) :: Nil)) is not a valid pattern :js // FIXME type diff --git a/shared/src/test/diff/nu/OverrideShorthand.mls b/shared/src/test/diff/nu/OverrideShorthand.mls index 8383f5a7..79da224f 100644 --- a/shared/src/test/diff/nu/OverrideShorthand.mls +++ b/shared/src/test/diff/nu/OverrideShorthand.mls @@ -10,7 +10,7 @@ class Pair(lhs: Int, rhs: Int) :e fun f(override Pair(x, y)) = x + y //│ |#fun| |f|(|#override| |Pair|(|x|,| |y|)|)| |#=| |x| |+| |y| -//│ AST: TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Var("_$0"))), If(IfOpApp(Var("_$0"), Var("is"), IfThen(App(Var("Pair"), Tup((N, Var("x")), (N, Var("y")))), App(Var("+"), Tup((N, Var("x")), (N, Var("y")))), Some(App(Sel(Super(), f), Tup((N, Var("_$0")))))))))) +//│ AST: TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Fld(_, Var("_$0"))) :: Nil), If(IfOpApp(Var("_$0"), Var("is"), IfThen(App(Var("Pair"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("y"))) :: Nil)), App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("y"))) :: Nil)), Some(App(Sel(Super(), f), Tup((N, Fld(_, Var("_$0"))) :: Nil)))))))) //│ Parsed: fun f = (_$0,) => if _$0 is (Pair(x, y,)) then +(x, y,) else (super).f(_$0,); //│ ╔══[ERROR] identifier not found: super //│ ║ l.11: fun f(override Pair(x, y)) = x + y @@ -46,7 +46,7 @@ fun f(override Pair(x, y), z) = x + y //│ ╙── ^ //│ fun f: (error, anything) -> Int //│ Code generation encountered an error: -//│ term App(Var("Pair"), Tup((N, Var("x")), (N, Var("y")))) is not a valid pattern +//│ term App(Var("Pair"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("y"))) :: Nil)) is not a valid pattern // TODO diff --git a/shared/src/test/diff/nu/RightAssocOps.mls b/shared/src/test/diff/nu/RightAssocOps.mls index df596915..a18e6893 100644 --- a/shared/src/test/diff/nu/RightAssocOps.mls +++ b/shared/src/test/diff/nu/RightAssocOps.mls @@ -30,7 +30,7 @@ fun (++) conc(xs, ys) = [xs, ys] :p 1 +: "a" ++ "b" :+ 2 //│ |1| |+:| |"a"| |++| |"b"| |:+| |2| -//│ AST: TypingUnit(App(Var("+:"), Tup((N, IntLit(1)), (N, App(Var(":+"), Tup((N, App(Var("++"), Tup((N, StrLit(a)), (N, StrLit(b))))), (N, IntLit(2)))))))) +//│ AST: TypingUnit(App(Var("+:"), Tup((N, Fld(_, IntLit(1))) :: (N, Fld(_, App(Var(":+"), Tup((N, Fld(_, App(Var("++"), Tup((N, Fld(_, StrLit(a))) :: (N, Fld(_, StrLit(b))) :: Nil)))) :: (N, Fld(_, IntLit(2))) :: Nil)))) :: Nil))) //│ Parsed: +:(1, :+(++("a", "b",), 2,),); //│ [[Int], [["a", "b"], [Int]]] //│ res @@ -39,7 +39,7 @@ fun (++) conc(xs, ys) = [xs, ys] :p 1 +: "a" :+ 2 ++ "b" //│ |1| |+:| |"a"| |:+| |2| |++| |"b"| -//│ AST: TypingUnit(App(Var("+:"), Tup((N, IntLit(1)), (N, App(Var("++"), Tup((N, App(Var(":+"), Tup((N, StrLit(a)), (N, IntLit(2))))), (N, StrLit(b)))))))) +//│ AST: TypingUnit(App(Var("+:"), Tup((N, Fld(_, IntLit(1))) :: (N, Fld(_, App(Var("++"), Tup((N, Fld(_, App(Var(":+"), Tup((N, Fld(_, StrLit(a))) :: (N, Fld(_, IntLit(2))) :: Nil)))) :: (N, Fld(_, StrLit(b))) :: Nil)))) :: Nil))) //│ Parsed: +:(1, ++(:+("a", 2,), "b",),); //│ [[Int], [["a", [Int]], "b"]] //│ res @@ -49,7 +49,7 @@ fun (++) conc(xs, ys) = [xs, ys] :e 1 +: "a" ++ 2 +: "b" //│ |1| |+:| |"a"| |++| |2| |+:| |"b"| -//│ AST: TypingUnit(App(Var("+:"), Tup((N, IntLit(1)), (N, App(Var("+:"), Tup((N, App(Var("++"), Tup((N, StrLit(a)), (N, IntLit(2))))), (N, StrLit(b)))))))) +//│ AST: TypingUnit(App(Var("+:"), Tup((N, Fld(_, IntLit(1))) :: (N, Fld(_, App(Var("+:"), Tup((N, Fld(_, App(Var("++"), Tup((N, Fld(_, StrLit(a))) :: (N, Fld(_, IntLit(2))) :: Nil)))) :: (N, Fld(_, StrLit(b))) :: Nil)))) :: Nil))) //│ Parsed: +:(1, +:(++("a", 2,), "b",),); //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.50: 1 +: "a" ++ 2 +: "b" diff --git a/shared/src/test/diff/pretyper/ucs/examples/BinarySearchTree.mls b/shared/src/test/diff/pretyper/ucs/examples/BinarySearchTree.mls index 7c58261c..e7da1db2 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/BinarySearchTree.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/BinarySearchTree.mls @@ -118,7 +118,16 @@ fun fromList(l) = Cons(x, xs') then fromList'(insert(t, x), xs') Nil then Empty fromList'(Empty, l) -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ ╔══[ERROR] Cannot use `val` or `fun` in local block; use `let` instead. +//│ ║ l.116: fun fromList'(t, xs) = +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.117: if xs is +//│ ║ ^^^^^^^^^^^^ +//│ ║ l.118: Cons(x, xs') then fromList'(insert(t, x), xs') +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.119: Nil then Empty +//│ ╙── ^^^^^^^^^^^^^^^^^^^^ +//│ fun fromList: (Cons[Num] | Nil) -> Empty fun fromList(t, xs) = if xs is diff --git a/shared/src/test/diff/pretyper/ucs/examples/ListFold.mls b/shared/src/test/diff/pretyper/ucs/examples/ListFold.mls new file mode 100644 index 00000000..bde6e1b4 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/examples/ListFold.mls @@ -0,0 +1,208 @@ +:PreTyper + +fun (|>) pipe(x, f) = f(x) +fun (++) strcat(s1, s2) = concat(s1)(s2) +//│ fun (|>) pipe: forall 'a 'b. ('a, 'a -> 'b) -> 'b +//│ fun (++) strcat: (Str, Str) -> Str + +abstract class List[out T]: (Cons[T] | Nil) +class Cons[out T](val head: T, val tail: List[T]) extends List[T] +module Nil extends List[nothing] +//│ abstract class List[T]: Cons[T] | Nil +//│ class Cons[T](head: T, tail: List[T]) extends List +//│ module Nil extends List + +fun (::) cons(head, tail) = Cons(head, tail) +//│ fun (::) cons: forall 'T. ('T, List['T]) -> Cons['T] + +let oneTwoThree = 1 :: 2 :: 3 :: Nil +//│ let oneTwoThree: Cons[1 | 2 | 3] +//│ oneTwoThree +//│ = Cons {} + +// Note that JavaScript doesn't have tail call optimization. Therefore, this +// implementation is still inefficient in practice. +fun join(sep) = + let aux(acc, xs) = + if xs is + Nil then acc + Cons(x, xs') then aux(acc ++ sep ++ toString(x), xs') + (xs) => + if xs is + Cons(x, xs') then aux(toString(x), xs') + Nil then "" +//│ fun join: Str -> (Cons[anything] | Nil) -> Str + +join(", ")(1 :: 2 :: 3 :: Nil) +(1 :: 2 :: 3 :: Nil) |> join(", ") +//│ Str +//│ res +//│ = '1, 2, 3' +//│ res +//│ = '1, 2, 3' + +fun showList(xs) = "[" ++ join(", ")(xs) ++ "]" +//│ fun showList: (Cons[anything] | Nil) -> Str + +fun (:::) appendAll(xs, ys) = + if xs is + Nil then ys + Cons(x, xs') then x :: (xs' ::: ys) +//│ fun (:::) appendAll: forall 'T 'a. (Cons['T] | Nil, List['T] & 'a) -> (Cons['T] | 'a) + +((1 :: 2 :: 3 :: Nil) ::: (4 :: 5 :: 6 :: Nil)) |> showList +//│ Str +//│ res +//│ = '[1, 2, 3, 4, 5, 6]' + +fun reverse(xs) = + let aux(acc, xs) = + if xs is + Nil then acc + Cons(x, xs') then aux(x :: acc, xs') + aux(Nil, xs) +//│ fun reverse: (Cons[anything] | Nil) -> (Cons[nothing] | Nil) + +(1 :: 2 :: 3 :: Nil) |> showList +reverse(1 :: 2 :: 3 :: Nil) |> showList +//│ Str +//│ res +//│ = '[1, 2, 3]' +//│ res +//│ = '[3, 2, 1]' + +// __ _ _ _ __ _ +// / _| ___ | | __| | | ___ / _| |_ +// | |_ / _ \| |/ _` | | / _ \ |_| __| +// | _| (_) | | (_| | |__| __/ _| |_ +// |_| \___/|_|\__,_|_____\___|_| \__| +// + +fun foldLeft(f)(z) = + let aux(acc, xs) = + if xs is + Nil then acc + Cons(x, xs') then aux(f(acc, x), xs') + (xs) => aux(z, xs) +//│ fun foldLeft: forall 'a 'b 'c. (('a, 'b) -> ('a & 'c)) -> ('a & 'c) -> (Cons['b] | Nil) -> 'c + +let sum = foldLeft((acc, x) => acc + x)(0) +sum(Nil) +sum(1 :: 2 :: 3 :: Nil) +//│ let sum: (Cons[Int] | Nil) -> Int +//│ Int +//│ sum +//│ = [Function (anonymous)] +//│ res +//│ = 0 +//│ res +//│ = 6 + +let product = foldLeft((acc, x) => acc * x)(1) +product(Nil) +product(1 :: 2 :: 3 :: Nil) +//│ let product: (Cons[Int] | Nil) -> Int +//│ Int +//│ product +//│ = [Function (anonymous)] +//│ res +//│ = 1 +//│ res +//│ = 6 + +let length = foldLeft((acc, _) => acc + 1)(0) +length(Nil) +length(1 :: 2 :: 3 :: Nil) +//│ let length: (Cons[anything] | Nil) -> Int +//│ Int +//│ length +//│ = [Function (anonymous)] +//│ res +//│ = 0 +//│ res +//│ = 3 + +let reverse' = foldLeft((acc, x) => x :: acc)(Nil) +reverse'(Nil) +reverse'(1 :: 2 :: 3 :: Nil) |> showList +//│ let reverse': (Cons['T] | Nil) -> (Cons[1 | 2 | 3 | 'T] | Nil) +//│ Str +//│ reverse' +//│ = [Function (anonymous)] +//│ res +//│ = Nil { class: [class Nil extends List] } +//│ res +//│ = '[3, 2, 1]' + + +// __ _ _ ____ _ _ _ +// / _| ___ | | __| | _ \(_) __ _| |__ | |_ +// | |_ / _ \| |/ _` | |_) | |/ _` | '_ \| __| +// | _| (_) | | (_| | _ <| | (_| | | | | |_ +// |_| \___/|_|\__,_|_| \_\_|\__, |_| |_|\__| +// |___/ + +fun foldRight(f)(z) = + let aux(acc, xs) = + if xs is + Nil then acc + Cons(x, xs') then f(x, aux(acc, xs')) + (xs) => aux(z, xs) +//│ fun foldRight: forall 'a 'b. (('a, 'b) -> 'b) -> 'b -> (Cons['a] | Nil) -> 'b + +let double = foldRight((x, acc) => x :: x :: acc)(Nil) +double(Nil) |> showList +double(1 :: 2 :: 3 :: Nil) |> showList +//│ let double: (Cons[anything] | Nil) -> (Cons[1 | 2 | 3] | Nil) +//│ Str +//│ double +//│ = [Function (anonymous)] +//│ res +//│ = '[]' +//│ res +//│ = '[1, 1, 2, 2, 3, 3]' + +let flatten = foldRight((xs, acc) => xs ::: acc)(Nil) +flatten(Nil) |> showList +flatten(oneTwoThree :: oneTwoThree :: oneTwoThree :: Nil) |> showList +//│ let flatten: (Cons[Cons['T] | Nil] | Nil) -> (Cons[1 | 2 | 3 | 'T] | Nil) +//│ Str +//│ flatten +//│ = [Function (anonymous)] +//│ res +//│ = '[]' +//│ res +//│ = '[1, 2, 3, 1, 2, 3, 1, 2, 3]' + +fun id(x) = x +//│ fun id: forall 'a. 'a -> 'a + +fun foldLeft'(f: ('A, 'B) -> 'A)(z: 'A) = + let g(x, y)(z) = y(f(z, x)) + (xs) => foldRight(g)(id)(xs)(z) +//│ fun foldLeft': forall 'A 'B. (f: ('A, 'B) -> 'A) -> (z: 'A) -> (Cons['B] | Nil) -> 'A + +let minus = foldLeft'((acc, x) => acc - x)(0) +minus(Nil) +minus(1 :: 2 :: 3 :: Nil) +//│ let minus: (Cons[Int] | Nil) -> Int +//│ Int +//│ minus +//│ = [Function (anonymous)] +//│ res +//│ = 0 +//│ res +//│ = -6 + +let reverse'' = foldLeft'((acc, x) => x :: acc)(Nil) +reverse(Nil) +reverse(1 :: 2 :: 3 :: Nil) |> showList +//│ let reverse'': (Cons[anything] | Nil) -> (Cons[nothing] | Nil) +//│ Str +//│ reverse'' +//│ = [Function (anonymous)] +//│ res +//│ = Nil { class: [class Nil extends List] } +//│ res +//│ = '[3, 2, 1]' + diff --git a/shared/src/test/diff/pretyper/ucs/examples/Permutations.mls b/shared/src/test/diff/pretyper/ucs/examples/Permutations.mls new file mode 100644 index 00000000..a42d3237 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/examples/Permutations.mls @@ -0,0 +1,127 @@ +:PreTyper + +fun (|>) pipe(x, f) = f(x) +fun (++) strcat(s1, s2) = concat(s1)(s2) +//│ fun (|>) pipe: forall 'a 'b. ('a, 'a -> 'b) -> 'b +//│ fun (++) strcat: (Str, Str) -> Str + +abstract class List[out T]: (Cons[T] | Nil) +class Cons[out T](val head: T, val tail: List[T]) extends List[T] +module Nil extends List[nothing] +fun (::) cons(head, tail) = Cons(head, tail) +fun (:::) appendAll(xs: List['A], ys: List['A]): List['A] = + if xs is + Nil then ys + Cons(x, xs') then x :: (xs' ::: ys) +fun (:+) append(xs, x): List['A] = xs ::: (x :: Nil) +fun reverse(xs) = + let aux(acc, xs) = + if xs is + Nil then acc + Cons(x, xs') then aux(x :: acc, xs') + aux(Nil, xs) +fun join(sep) = + let aux(acc, xs) = + if xs is + Nil then acc + Cons(x, xs') then aux(acc ++ sep ++ toString(x), xs') + (xs) => + if xs is + Cons(x, xs') then aux(toString(x), xs') + Nil then "" +fun showList(xs) = "[" ++ join(", ")(xs) ++ "]" +fun foldLeft(f)(z) = + let aux(acc, xs) = + if xs is + Nil then acc + Cons(x, xs') then aux(f(acc, x), xs') + (xs) => aux(z, xs) +fun map(f, xs) = + if xs is + Nil then Nil + Cons(x, xs') then f(x) :: map(f, xs') +fun showListList(xs) = "[" ++ join(", ")(map(showList, xs)) ++ "]" +//│ abstract class List[T]: Cons[T] | Nil +//│ class Cons[T](head: T, tail: List[T]) extends List +//│ module Nil extends List +//│ fun (::) cons: forall 'T. ('T, List['T]) -> Cons['T] +//│ fun (:::) appendAll: forall 'T0. (xs: List['T0], ys: List['T0]) -> List['T0] +//│ fun (:+) append: forall 'T1. (List['T1], 'T1) -> List['T1] +//│ fun reverse: (Cons[anything] | Nil) -> (Cons[nothing] | Nil) +//│ fun join: Str -> (Cons[anything] | Nil) -> Str +//│ fun showList: (Cons[anything] | Nil) -> Str +//│ fun foldLeft: forall 'a 'b 'c. (('a, 'b) -> ('a & 'c)) -> ('a & 'c) -> (Cons['b] | Nil) -> 'c +//│ fun map: forall 'd 'T2. ('d -> 'T2, Cons['d] | Nil) -> (Cons['T2] | Nil) +//│ fun showListList: (Cons[Cons[anything] | Nil] | Nil) -> Str + +fun insertAllPositions(z) = + let aux(prev, acc, xs) = + if xs is + Nil then ((prev :+ z) :: acc) |> reverse + Cons(head, tail) then + let nu = ((prev :+ z) ::: xs) + aux(prev :+ head, nu :: acc, tail) + xs => aux(Nil, Nil, xs) +//│ fun insertAllPositions: forall 'a. 'a -> (forall 'A. (Cons['A] | Nil) -> (Cons[List['A | 'a]] & {Cons#T <: List['A | 'a]} | Nil)) + +insertAllPositions(0)(1 :: 2 :: 3 :: Nil) |> showListList +//│ Str +//│ res +//│ = '[[0, 1, 2, 3], [1, 0, 2, 3], [1, 2, 0, 3], [1, 2, 3, 0]]' + +fun permutations(xs) = + if xs is + Nil then Nil + Cons(x, Nil) then (x :: Nil) :: Nil + Cons(x, xs') then foldLeft((acc, ys) => acc ::: insertAllPositions(x)(ys))(Nil)(permutations(xs')) +//│ fun permutations: forall 'T 'A. (Cons['T] | Nil) -> (Cons[Cons['T]] | List[List['T & 'A]] | Nil) +//│ where +//│ 'T <: 'A +//│ 'A := 'T + +permutations(Nil) |> showListList +permutations(1 :: Nil) |> showListList +permutations(1 :: 2 :: Nil) |> showListList +permutations(1 :: 2 :: 3 :: Nil) |> showListList +//│ Str +//│ res +//│ = '[]' +//│ res +//│ = '[[1]]' +//│ res +//│ = '[[1, 2], [2, 1]]' +//│ res +//│ = '[[1, 2, 3], [2, 1, 3], [2, 3, 1], [1, 3, 2], [3, 1, 2], [3, 2, 1]]' + +fun filterNot(f, xs) = + if xs is + Nil then Nil + Cons(x, xs') then + if f(x) then filterNot(f, xs') + else x :: filterNot(f, xs') +//│ fun filterNot: forall 'T. ('T -> Bool, Cons['T] | Nil) -> (Cons['T] | Nil) + +fun permutations'(xs) = + if xs is + Nil then Nil + Cons(x, Nil) then (x :: Nil) :: Nil + else + let f(acc, x) = acc ::: map((ys) => x :: ys, permutations'(filterNot((y) => x === y, xs))) + foldLeft(f)(Nil)(xs) +//│ fun permutations': forall 'T. (Cons['T] | Nil) -> (Cons[Cons['T]] | List[Cons['T]] | Nil) +//│ where +//│ 'T <: Eql['T] + +permutations'(Nil) |> showListList +permutations'(1 :: Nil) |> showListList +permutations'(1 :: 2 :: Nil) |> showListList +permutations'(1 :: 2 :: 3 :: Nil) |> showListList +//│ Str +//│ res +//│ = '[]' +//│ res +//│ = '[[1]]' +//│ res +//│ = '[[1, 2], [2, 1]]' +//│ res +//│ = '[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]' diff --git a/shared/src/test/diff/ucs/LeadingAnd.mls b/shared/src/test/diff/ucs/LeadingAnd.mls index f1e65d9f..dea9201a 100644 --- a/shared/src/test/diff/ucs/LeadingAnd.mls +++ b/shared/src/test/diff/ucs/LeadingAnd.mls @@ -23,7 +23,7 @@ fun f(a, b) = if a is Some(av) and b is Some(bv) then av + bv //│ |#fun| |f|(|a|,| |b|)| |#=| |#if| |a| |is| |Some|(|av|)|→|and| |b| |is| |Some|(|bv|)|↵|#then| |av| |+| |bv|←| -//│ AST: TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Var("a")), (N, Var("b"))), If(IfOpApp(Var("a"), Var("is"), I; f; O; p; s; A; p; p; (; A; p; p; (; V; a; r; (; "; S; o; m; e; "; ); ,; ; T; u; p; (; (; N; ,; ; V; a; r; (; "; a; v; "; ); ); ); ); ,; ; <; i; t; e; r; a; t; o; r; >, None))))) +//│ AST: TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Fld(_, Var("a"))) :: (N, Fld(_, Var("b"))) :: Nil), If(IfOpApp(Var("a"), Var("is"), I; f; O; p; s; A; p; p; (; A; p; p; (; V; a; r; (; "; S; o; m; e; "; ); ,; ; T; u; p; (; (; N; ,; ; F; l; d; (; _; ,; ; V; a; r; (; "; a; v; "; ); ); ); ; :; :; ; N; i; l; ); ); ,; ; <; i; t; e; r; a; t; o; r; >, None))))) //│ Parsed: fun f = (a, b,) => if a is Some(av,) ‹· and (is(b, Some(bv,),)) then +(av, bv,)›; //│ fun f: (Some[Int], Some[Int]) -> Int @@ -34,7 +34,7 @@ fun f(a, b) = if a is and b is Some(bv) then av + bv //│ |#fun| |f|(|a|,| |b|)| |#=| |#if| |a| |is|→|Some|(|av|)|→|and| |b| |is| |Some|(|bv|)|↵|#then| |av| |+| |bv|←|←| -//│ AST: TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Var("a")), (N, Var("b"))), If(IfOpApp(Var("a"), Var("is"), IfBlock(Ls(I; f; O; p; s; A; p; p; (; A; p; p; (; V; a; r; (; "; S; o; m; e; "; ); ,; ; T; u; p; (; (; N; ,; ; V; a; r; (; "; a; v; "; ); ); ); ); ,; ; <; i; t; e; r; a; t; o; r; >)), None))))) +//│ AST: TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Fld(_, Var("a"))) :: (N, Fld(_, Var("b"))) :: Nil), If(IfOpApp(Var("a"), Var("is"), IfBlock(Ls(I; f; O; p; s; A; p; p; (; A; p; p; (; V; a; r; (; "; S; o; m; e; "; ); ,; ; T; u; p; (; (; N; ,; ; F; l; d; (; _; ,; ; V; a; r; (; "; a; v; "; ); ); ); ; :; :; ; N; i; l; ); ); ,; ; <; i; t; e; r; a; t; o; r; >)), None))))) //│ Parsed: fun f = (a, b,) => if a is ‹Some(av,) ‹· and (is(b, Some(bv,),)) then +(av, bv,)››; //│ ╔══[ERROR] Illegal pattern `and` //│ ║ l.34: and b is Some(bv) diff --git a/shared/src/test/diff/ucs/SplitOps.mls b/shared/src/test/diff/ucs/SplitOps.mls index 70e9e053..b0160174 100644 --- a/shared/src/test/diff/ucs/SplitOps.mls +++ b/shared/src/test/diff/ucs/SplitOps.mls @@ -93,10 +93,10 @@ fun f(a, b, c) = if a == 0 and b is B() and c is C() then 0 //│ |#fun| |f|(|a|,| |b|,| |c|)| |#=|→|#if| |a|→|==| |0| |and| |b| |is| |B|(||)| |and| |c| |is| |C|(||)| |#then| |0|←|←| -//│ AST: TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Var("a")), (N, Var("b")), (N, Var("c"))), Blk(If(I; f; O; p; s; A; p; p; (; V; a; r; (; "; a; "; ); ,; ; <; i; t; e; r; a; t; o; r; >, None)))))) +//│ AST: TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Fld(_, Var("a"))) :: (N, Fld(_, Var("b"))) :: (N, Fld(_, Var("c"))) :: Nil), Blk(If(I; f; O; p; s; A; p; p; (; V; a; r; (; "; a; "; ); ,; ; <; i; t; e; r; a; t; o; r; >, None)))))) //│ Parsed: fun f = (a, b, c,) => {if a ‹· == (and(and(0,)(is(b,)(B(),),),)(is(c,)(C(),),)) then 0›}; //│ Desugared: rec def f: (a, b, c,) => {if a ‹· == (and(and(0,)(is(b,)(B(),),),)(is(c,)(C(),),)) then 0›} -//│ AST: Def(true, Var("f"), L(Lam(Tup((N, Var("a")), (N, Var("b")), (N, Var("c"))), Blk(If(I; f; O; p; s; A; p; p; (; V; a; r; (; "; a; "; ); ,; ; <; i; t; e; r; a; t; o; r; >, None)))), true) +//│ AST: Def(true, Var("f"), L(Lam(Tup((N, Fld(_, Var("a"))) :: (N, Fld(_, Var("b"))) :: (N, Fld(_, Var("c"))) :: Nil), Blk(If(I; f; O; p; s; A; p; p; (; V; a; r; (; "; a; "; ); ,; ; <; i; t; e; r; a; t; o; r; >, None)))), true) //│ ╔══[ERROR] The case when this is false is not handled: ==(a,)(0,) //│ ║ l.93: if a //│ ║ ^ diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index 3dac9144..99898c74 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -524,7 +524,7 @@ class DiffTests val vars: Map[Str, typer.SimpleType] = Map.empty val rootTypingUnit = TypingUnit(p.tops) if (usePreTyper) { - val preTyper = new PreTyper(mode.dbgPreTyper, newDefs) { + val preTyper = new PreTyper(mode.dbgPreTyper) { override protected def raise(diagnostics: Ls[Diagnostic]): Unit = report(diagnostics) override def emitString(str: String): Unit = output(str) } From 3ab610e4d6a3eeb244813e2362fcf638a00c1b0e Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sat, 23 Dec 2023 12:57:07 +0800 Subject: [PATCH 020/143] Update test file changes in other sub-projects to make CI happy --- compiler/shared/test/diff/LambLift.mls | 8 ++-- compiler/shared/test/diff/LiftType.mls | 10 ++--- compiler/shared/test/diff/Lifter.mls | 30 ++++++------- compiler/shared/test/diff/LifterBlks.mls | 36 ++++++++-------- compiler/shared/test/diff/mono.mls | 54 ++++++++++++------------ 5 files changed, 69 insertions(+), 69 deletions(-) diff --git a/compiler/shared/test/diff/LambLift.mls b/compiler/shared/test/diff/LambLift.mls index c42974bf..0a85395c 100644 --- a/compiler/shared/test/diff/LambLift.mls +++ b/compiler/shared/test/diff/LambLift.mls @@ -10,7 +10,7 @@ fun foo() = local(1) foo() //│ Parsed: -//│ TypingUnit(NuFunDef(None, foo, None, [], Lam(Tup(), Blk(...))), App(Var(foo), Tup())) +//│ TypingUnit(NuFunDef(None, foo, N, Nil, L(Lam(Tup(Nil), Blk(NuFunDef(Some(false), local, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Blk(NuTypeDef(Cls, TypeName("Foo"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, bar, N, Nil, L(App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, App(Var("foo"), Tup(Nil)))) :: Nil)))))), Sel(Bra(rcd = false, App(NuNew(Var("Foo")), Tup(Nil))), bar))))), App(Var("local"), Tup((N, Fld(_, IntLit(1))) :: Nil)))))), App(Var("foo"), Tup(Nil))) //│ Lifted: //│ TypingUnit { //│ class Foo$1([x,]) {fun bar = () => +((this).x, foo$1(),)} @@ -28,7 +28,7 @@ fun foo(f) = f(1) foo(x => x+1) //│ Parsed: -//│ TypingUnit(NuFunDef(None, foo, None, [], Lam(Tup(_: Var(f)), Blk(...))), App(Var(foo), Tup(_: Lam(Tup(_: Var(x)), App(Var(+), Tup(_: Var(x), _: IntLit(1))))))) +//│ TypingUnit(NuFunDef(None, foo, N, Nil, L(Lam(Tup((N, Fld(_, Var("f"))) :: Nil), Blk(App(Var("f"), Tup((N, Fld(_, IntLit(1))) :: Nil)))))), App(Var("foo"), Tup((N, Fld(_, Lam(Tup((N, Fld(_, Var("x"))) :: Nil), App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, IntLit(1))) :: Nil))))) :: Nil))) //│ Lifted: //│ TypingUnit { //│ class Lambda1$2$1([]) {fun apply = (x,) => +(x, 1,)} @@ -46,7 +46,7 @@ fun foo(x) = bar(y => y+x) foo(1) //│ Parsed: -//│ TypingUnit(NuFunDef(None, foo, None, [], Lam(Tup(_: Var(x)), Blk(...))), App(Var(foo), Tup(_: IntLit(1)))) +//│ TypingUnit(NuFunDef(None, foo, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Blk(NuFunDef(Some(false), bar, N, Nil, L(Lam(Tup((N, Fld(_, Var("f"))) :: Nil), Blk(App(Var("f"), Tup((N, Fld(_, Var("x"))) :: Nil)))))), App(Var("bar"), Tup((N, Fld(_, Lam(Tup((N, Fld(_, Var("y"))) :: Nil), App(Var("+"), Tup((N, Fld(_, Var("y"))) :: (N, Fld(_, Var("x"))) :: Nil))))) :: Nil)))))), App(Var("foo"), Tup((N, Fld(_, IntLit(1))) :: Nil))) //│ Lifted: //│ TypingUnit { //│ class Lambda1$3$1([x,]) {fun apply = (y,) => +(y, (this).x,)} @@ -68,7 +68,7 @@ fun app(a) = foo(z => a.bar(z)) app(new A(1)) //│ Parsed: -//│ TypingUnit(NuFunDef(None, foo, None, [], Lam(Tup(_: Var(f)), Blk(...))), NuTypeDef(class, A, (), Tup(y: Var(Int)), (), None, None, TypingUnit(NuFunDef(None, bar, None, [], Lam(Tup(_: Var(z)), App(Var(+), Tup(_: Var(y), _: Var(z))))))), NuFunDef(None, app, None, [], Lam(Tup(_: Var(a)), Blk(...))), App(Var(app), Tup(_: App(NuNew(Var(A)), Tup(_: IntLit(1)))))) +//│ TypingUnit(NuFunDef(None, foo, N, Nil, L(Lam(Tup((N, Fld(_, Var("f"))) :: Nil), Blk(App(Var("f"), Tup((N, Fld(_, IntLit(1))) :: Nil)))))), NuTypeDef(Cls, TypeName("A"), Ls(), S(Tup((S(y), Fld(_, Var("Int"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, bar, N, Nil, L(Lam(Tup((N, Fld(_, Var("z"))) :: Nil), App(Var("+"), Tup((N, Fld(_, Var("y"))) :: (N, Fld(_, Var("z"))) :: Nil))))))), NuFunDef(None, app, N, Nil, L(Lam(Tup((N, Fld(_, Var("a"))) :: Nil), Blk(App(Var("foo"), Tup((N, Fld(_, Lam(Tup((N, Fld(_, Var("z"))) :: Nil), App(Sel(Var("a"), bar), Tup((N, Fld(_, Var("z"))) :: Nil))))) :: Nil)))))), App(Var("app"), Tup((N, Fld(_, App(NuNew(Var("A")), Tup((N, Fld(_, IntLit(1))) :: Nil)))) :: Nil))) //│ Lifted: //│ TypingUnit { //│ class A$1([y: Int,]) {fun bar = (z,) => +((this).y, z,)} diff --git a/compiler/shared/test/diff/LiftType.mls b/compiler/shared/test/diff/LiftType.mls index 773e24b1..dfcd4ee6 100644 --- a/compiler/shared/test/diff/LiftType.mls +++ b/compiler/shared/test/diff/LiftType.mls @@ -8,7 +8,7 @@ class CTX{ //│ |#class| |CTX|{|→|#class| |A| |{||}|↵|#fun| |foo|(|f|#:| |A| |#=>| |A|)|#:| |(|A| |#=>| |A|)| |#=>| |A| |#=| |f|(|#new| |A|)|←|↵|}| //│ Parsed: {class CTX {class A {}; fun foo = (f: (A,) => A,) => f(new A,) : (A -> A) -> A}} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, CTX, (), Tup(), (), None, None, TypingUnit(NuTypeDef(class, A, (), Tup(), (), None, None, TypingUnit()), NuFunDef(None, foo, None, [], Lam(Tup(f: Lam(Tup(_: Var(A)), Var(A))), Asc(App(Var(f), Tup(_: NuNew(Var(A)))), Function(Tuple(List((None,Field(None,Function(Tuple(List((None,Field(None,TypeName(A))))),TypeName(A)))))),TypeName(A)))))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("CTX"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuTypeDef(Cls, TypeName("A"), Ls(), N, N, N, Ls(), N, N, TypingUnit()), NuFunDef(None, foo, N, Nil, L(Lam(Tup((S(f), Fld(_, Lam(Tup((N, Fld(_, Var("A"))) :: Nil), Var("A")))) :: Nil), Asc(App(Var("f"), Tup((N, Fld(_, NuNew(Var("A")))) :: Nil)), Function(Tuple(List((None,Field(None,Function(Tuple(List((None,Field(None,TypeName(A))))),TypeName(A)))))),TypeName(A))))))))) //│ Lifted: //│ TypingUnit { //│ class CTX$1_A$2([par$CTX$1,]) {} @@ -26,7 +26,7 @@ class CTX(x, y){ //│ |#class| |CTX|(|x|,| |y|)|{|→|#class| |A|{| |#fun| |foo| |#=| |x|}|↵|#class| |B|#:| |A| |{| |#fun| |foo| |#=| |y|}|↵|#fun| |foo|(|any|#:| |[|A|,| |B|]|)|#:| |[|B|,| |A|]| |#=| |[|any|._2|,| |any|._1|]|←|↵|}| //│ Parsed: {class CTX(x, y,) {class A {fun foo = x}; class B: A {fun foo = y}; fun foo = (any: [A, B,],) => [(any)._2, (any)._1,] : [B, A]}} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, CTX, (), Tup(_: Var(x), _: Var(y)), (), None, None, TypingUnit(NuTypeDef(class, A, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, foo, None, [], Var(x)))), NuTypeDef(class, B, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, foo, None, [], Var(y)))), NuFunDef(None, foo, None, [], Lam(Tup(any: Tup(_: Var(A), _: Var(B))), Asc(Tup(_: Sel(Var(any), _2), _: Sel(Var(any), _1)), Tuple(List((None,Field(None,TypeName(B))), (None,Field(None,TypeName(A))))))))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("CTX"), Ls(), S(Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("y"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuTypeDef(Cls, TypeName("A"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, foo, N, Nil, L(Var("x"))))), NuTypeDef(Cls, TypeName("B"), Ls(), N, N, S(TypeName("A")), Ls(), N, N, TypingUnit(NuFunDef(None, foo, N, Nil, L(Var("y"))))), NuFunDef(None, foo, N, Nil, L(Lam(Tup((S(any), Fld(_, Tup((N, Fld(_, Var("A"))) :: (N, Fld(_, Var("B"))) :: Nil))) :: Nil), Asc(Tup((N, Fld(_, Sel(Var("any"), _2))) :: (N, Fld(_, Sel(Var("any"), _1))) :: Nil), Tuple(List((None,Field(None,TypeName(B))), (None,Field(None,TypeName(A)))))))))))) //│ Lifted: //│ TypingUnit { //│ class CTX$1_A$2([par$CTX$1,]) {fun foo = () => ((this).par$CTX$1).x} @@ -45,7 +45,7 @@ class CTX(x, y){ //│ |#class| |CTX|(|x|,| |y|)|{|→|#class| |A|{| |#fun| |foo| |#=| |x|}|↵|#class| |B|#:| |A| |{| |#fun| |foo| |#=| |y|}|↵|#fun| |foo|(|any|#:| |{|p1|#:| |A|,| |p2|#:| |B|}|)|#:| |[|B|,| |A|]| |#=| |[|any|.p2|,| |any|.p1|]|←|↵|}| //│ Parsed: {class CTX(x, y,) {class A {fun foo = x}; class B: A {fun foo = y}; fun foo = (any: '{' {p1: A, p2: B} '}',) => [(any).p2, (any).p1,] : [B, A]}} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, CTX, (), Tup(_: Var(x), _: Var(y)), (), None, None, TypingUnit(NuTypeDef(class, A, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, foo, None, [], Var(x)))), NuTypeDef(class, B, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, foo, None, [], Var(y)))), NuFunDef(None, foo, None, [], Lam(Tup(any: Bra(rcd = true, Rcd(Var(p1) = Var(A), Var(p2) = Var(B)))), Asc(Tup(_: Sel(Var(any), p2), _: Sel(Var(any), p1)), Tuple(List((None,Field(None,TypeName(B))), (None,Field(None,TypeName(A))))))))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("CTX"), Ls(), S(Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("y"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuTypeDef(Cls, TypeName("A"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, foo, N, Nil, L(Var("x"))))), NuTypeDef(Cls, TypeName("B"), Ls(), N, N, S(TypeName("A")), Ls(), N, N, TypingUnit(NuFunDef(None, foo, N, Nil, L(Var("y"))))), NuFunDef(None, foo, N, Nil, L(Lam(Tup((S(any), Fld(_, Bra(rcd = true, Rcd(Var("p1") = Var("A"), Var("p2") = Var("B"))))) :: Nil), Asc(Tup((N, Fld(_, Sel(Var("any"), p2))) :: (N, Fld(_, Sel(Var("any"), p1))) :: Nil), Tuple(List((None,Field(None,TypeName(B))), (None,Field(None,TypeName(A)))))))))))) //│ Lifted: //│ TypingUnit { //│ class CTX$1_A$2([par$CTX$1,]) {fun foo = () => ((this).par$CTX$1).x} @@ -64,7 +64,7 @@ class CTX(x, y){ //│ |#class| |CTX|(|x|,| |y|)|{|→|#class| |A|{| |#fun| |foo| |#=| |x|}|↵|#class| |B|‹|T|›| |{| |#fun| |foo| |#=| |y|}|↵|#fun| |foo|(|any|#:| |[|A|,| |B|‹|A|›|]|)|#:| |[|[|B|‹|A|›|,| |A|]|,| |A|]| |#=| |[|any|,| |any|._1|]|←|↵|}| //│ Parsed: {class CTX(x, y,) {class A {fun foo = x}; class B‹T› {fun foo = y}; fun foo = (any: [A, B‹A›,],) => [any, (any)._1,] : [[B[A], A], A]}} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, CTX, (), Tup(_: Var(x), _: Var(y)), (), None, None, TypingUnit(NuTypeDef(class, A, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, foo, None, [], Var(x)))), NuTypeDef(class, B, ((None,TypeName(T))), Tup(), (), None, None, TypingUnit(NuFunDef(None, foo, None, [], Var(y)))), NuFunDef(None, foo, None, [], Lam(Tup(any: Tup(_: Var(A), _: TyApp(Var(B), List(TypeName(A))))), Asc(Tup(_: Var(any), _: Sel(Var(any), _1)), Tuple(List((None,Field(None,Tuple(List((None,Field(None,AppliedType(TypeName(B),List(TypeName(A))))), (None,Field(None,TypeName(A))))))), (None,Field(None,TypeName(A))))))))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("CTX"), Ls(), S(Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("y"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuTypeDef(Cls, TypeName("A"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, foo, N, Nil, L(Var("x"))))), NuTypeDef(Cls, TypeName("B"), Ls(N -> TypeName("T")), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, foo, N, Nil, L(Var("y"))))), NuFunDef(None, foo, N, Nil, L(Lam(Tup((S(any), Fld(_, Tup((N, Fld(_, Var("A"))) :: (N, Fld(_, TyApp(Var("B"), List(TypeName(A))))) :: Nil))) :: Nil), Asc(Tup((N, Fld(_, Var("any"))) :: (N, Fld(_, Sel(Var("any"), _1))) :: Nil), Tuple(List((None,Field(None,Tuple(List((None,Field(None,AppliedType(TypeName(B),List(TypeName(A))))), (None,Field(None,TypeName(A))))))), (None,Field(None,TypeName(A)))))))))))) //│ Lifted: //│ TypingUnit { //│ class CTX$1_A$2([par$CTX$1,]) {fun foo = () => ((this).par$CTX$1).x} @@ -86,7 +86,7 @@ class CTX{ //│ |#class| |CTX|{|→|#fun| |ctx|(|x|,|y|)| |#=| |→|#class| |A|{| |#fun| |foo| |#=| |x| |}|↵|#fun| |bar|‹|T|›|(|any|#:| |T|)|#:| |A| |#=| |→|#let| |x| |#=| |#new| |T|↵|#new| |A|←|↵|(|#new| |CTX|)|.bar|‹|CTX|›|←|←|↵|}| //│ Parsed: {class CTX {fun ctx = (x, y,) => {class A {fun foo = x}; fun bar = (any: T,) => {let x = new T; new A} : A; ('(' new CTX ')').bar‹CTX›}}} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, CTX, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, ctx, None, [], Lam(Tup(_: Var(x), _: Var(y)), Blk(...)))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("CTX"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, ctx, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("y"))) :: Nil), Blk(NuTypeDef(Cls, TypeName("A"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, foo, N, Nil, L(Var("x"))))), NuFunDef(None, bar, N, TypeName("T") :: Nil, L(Lam(Tup((S(any), Fld(_, Var("T"))) :: Nil), Asc(Blk(NuFunDef(Some(false), x, N, Nil, L(NuNew(Var("T")))), NuNew(Var("A"))), TypeName(A))))), TyApp(Sel(Bra(rcd = false, NuNew(Var("CTX"))), bar), List(TypeName(CTX)))))))))) //│ Lifted: //│ Lifting failed: mlscript.codegen.CodeGenError: Cannot find type T. Class values are not supported in lifter. //│ diff --git a/compiler/shared/test/diff/Lifter.mls b/compiler/shared/test/diff/Lifter.mls index fffb3d72..21b4750d 100644 --- a/compiler/shared/test/diff/Lifter.mls +++ b/compiler/shared/test/diff/Lifter.mls @@ -28,7 +28,7 @@ class A(x) { //│ |#class| |A|(|x|)| |{|→|#class| |B|(|y|)| |{|→|#fun| |getX| |#=| |x|↵|#fun| |getB1| |#=| |B1|(|y|)|↵|#class| |C|(|z|)| |{|→|#fun| |inc|(||)| |#=| |x| |+| |1|↵|#fun| |getY| |#=| |y|↵|#fun| |getA| |#=| |A|(|z|)|↵|#fun| |getB|(|w|)| |#=| |B|(|w|)|↵|#fun| |getC| |#=| |#new| |C|(|inc|(||)|)|↵|#fun| |getSelf| |#=| |this|←|↵|}|←|↵|}|↵|#class| |B1|(|y|)| |{|→|#fun| |getX| |#=| |x|↵|#fun| |getY| |#=| |y|↵|#fun| |getB| |#=| |#new| |B|(|y|)|↵|#fun| |getB1| |#=| |#new| |B1|(|y|)|←|↵|}|↵|#fun| |getB| |#=| |#new| |B|(|x|)|↵|#fun| |getB2|(|y|)| |#=| |B1|(|y|)|↵|#fun| |getB3|(|z|)| |#=| |getB2|(|z|)|↵|#fun| |getA| |#=| |A|(|x|)|←|↵|}| //│ Parsed: {class A(x,) {class B(y,) {fun getX = x; fun getB1 = B1(y,); class C(z,) {fun inc = () => +(x, 1,); fun getY = y; fun getA = A(z,); fun getB = (w,) => B(w,); fun getC = (new C)(inc(),); fun getSelf = this}}; class B1(y,) {fun getX = x; fun getY = y; fun getB = (new B)(y,); fun getB1 = (new B1)(y,)}; fun getB = (new B)(x,); fun getB2 = (y,) => B1(y,); fun getB3 = (z,) => getB2(z,); fun getA = A(x,)}} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, A, (), Tup(_: Var(x)), (), None, None, TypingUnit(NuTypeDef(class, B, (), Tup(_: Var(y)), (), None, None, TypingUnit(NuFunDef(None, getX, None, [], Var(x)), NuFunDef(None, getB1, None, [], App(Var(B1), Tup(_: Var(y)))), NuTypeDef(class, C, (), Tup(_: Var(z)), (), None, None, TypingUnit(NuFunDef(None, inc, None, [], Lam(Tup(), App(Var(+), Tup(_: Var(x), _: IntLit(1))))), NuFunDef(None, getY, None, [], Var(y)), NuFunDef(None, getA, None, [], App(Var(A), Tup(_: Var(z)))), NuFunDef(None, getB, None, [], Lam(Tup(_: Var(w)), App(Var(B), Tup(_: Var(w))))), NuFunDef(None, getC, None, [], App(NuNew(Var(C)), Tup(_: App(Var(inc), Tup())))), NuFunDef(None, getSelf, None, [], Var(this)))))), NuTypeDef(class, B1, (), Tup(_: Var(y)), (), None, None, TypingUnit(NuFunDef(None, getX, None, [], Var(x)), NuFunDef(None, getY, None, [], Var(y)), NuFunDef(None, getB, None, [], App(NuNew(Var(B)), Tup(_: Var(y)))), NuFunDef(None, getB1, None, [], App(NuNew(Var(B1)), Tup(_: Var(y)))))), NuFunDef(None, getB, None, [], App(NuNew(Var(B)), Tup(_: Var(x)))), NuFunDef(None, getB2, None, [], Lam(Tup(_: Var(y)), App(Var(B1), Tup(_: Var(y))))), NuFunDef(None, getB3, None, [], Lam(Tup(_: Var(z)), App(Var(getB2), Tup(_: Var(z))))), NuFunDef(None, getA, None, [], App(Var(A), Tup(_: Var(x))))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("A"), Ls(), S(Tup((N, Fld(_, Var("x"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuTypeDef(Cls, TypeName("B"), Ls(), S(Tup((N, Fld(_, Var("y"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, getX, N, Nil, L(Var("x"))), NuFunDef(None, getB1, N, Nil, L(App(Var("B1"), Tup((N, Fld(_, Var("y"))) :: Nil)))), NuTypeDef(Cls, TypeName("C"), Ls(), S(Tup((N, Fld(_, Var("z"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, inc, N, Nil, L(Lam(Tup(Nil), App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, IntLit(1))) :: Nil))))), NuFunDef(None, getY, N, Nil, L(Var("y"))), NuFunDef(None, getA, N, Nil, L(App(Var("A"), Tup((N, Fld(_, Var("z"))) :: Nil)))), NuFunDef(None, getB, N, Nil, L(Lam(Tup((N, Fld(_, Var("w"))) :: Nil), App(Var("B"), Tup((N, Fld(_, Var("w"))) :: Nil))))), NuFunDef(None, getC, N, Nil, L(App(NuNew(Var("C")), Tup((N, Fld(_, App(Var("inc"), Tup(Nil)))) :: Nil)))), NuFunDef(None, getSelf, N, Nil, L(Var("this"))))))), NuTypeDef(Cls, TypeName("B1"), Ls(), S(Tup((N, Fld(_, Var("y"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, getX, N, Nil, L(Var("x"))), NuFunDef(None, getY, N, Nil, L(Var("y"))), NuFunDef(None, getB, N, Nil, L(App(NuNew(Var("B")), Tup((N, Fld(_, Var("y"))) :: Nil)))), NuFunDef(None, getB1, N, Nil, L(App(NuNew(Var("B1")), Tup((N, Fld(_, Var("y"))) :: Nil)))))), NuFunDef(None, getB, N, Nil, L(App(NuNew(Var("B")), Tup((N, Fld(_, Var("x"))) :: Nil)))), NuFunDef(None, getB2, N, Nil, L(Lam(Tup((N, Fld(_, Var("y"))) :: Nil), App(Var("B1"), Tup((N, Fld(_, Var("y"))) :: Nil))))), NuFunDef(None, getB3, N, Nil, L(Lam(Tup((N, Fld(_, Var("z"))) :: Nil), App(Var("getB2"), Tup((N, Fld(_, Var("z"))) :: Nil))))), NuFunDef(None, getA, N, Nil, L(App(Var("A"), Tup((N, Fld(_, Var("x"))) :: Nil))))))) //│ Lifted: //│ TypingUnit { //│ class A$1_B$2_C$4([par$A$1_B$2, z, x,]) { @@ -68,7 +68,7 @@ class A(x) { //│ |#class| |A|(|x|)| |{|→|#class| |B|(|y|)| |{|→|#class| |C|(|z|)| |{|→|#fun| |sum| |#=| |x| |+| |y| |+| |z|←|↵|}|←|↵|}|←|↵|}| //│ Parsed: {class A(x,) {class B(y,) {class C(z,) {fun sum = +(+(x, y,), z,)}}}} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, A, (), Tup(_: Var(x)), (), None, None, TypingUnit(NuTypeDef(class, B, (), Tup(_: Var(y)), (), None, None, TypingUnit(NuTypeDef(class, C, (), Tup(_: Var(z)), (), None, None, TypingUnit(NuFunDef(None, sum, None, [], App(Var(+), Tup(_: App(Var(+), Tup(_: Var(x), _: Var(y))), _: Var(z))))))))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("A"), Ls(), S(Tup((N, Fld(_, Var("x"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuTypeDef(Cls, TypeName("B"), Ls(), S(Tup((N, Fld(_, Var("y"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuTypeDef(Cls, TypeName("C"), Ls(), S(Tup((N, Fld(_, Var("z"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, sum, N, Nil, L(App(Var("+"), Tup((N, Fld(_, App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("y"))) :: Nil)))) :: (N, Fld(_, Var("z"))) :: Nil))))))))))) //│ Lifted: //│ TypingUnit { //│ class A$1_B$2_C$3([par$A$1_B$2, z, x,]) { @@ -108,7 +108,7 @@ new C{ //│ |#class| |A|(|x|)| |{|→|#class| |B|{|→|#fun| |foo| |#=| |1|↵|#fun| |bar| |#=| |11|←|↵|}|↵|#fun| |getB| |#=| |#new| |B|{|→|#fun| |foo| |#=| |2|↵|#fun| |bar| |#=| |12|←|↵|}|↵|#fun| |bar| |#=| |13|←|↵|}|↵|#class| |C|#:| |A|{|→|#fun| |getB| |#=| |#new| |B|{|→|#fun| |foo| |#=| |3|↵|#fun| |bar| |#=| |14|←|↵|}|↵|#fun| |bar| |#=| |15|←|↵|}|↵|#new| |C|{|→|#fun| |getB| |#=| |#new| |B|{|→|#fun| |foo| |#=| |4|↵|#fun| |bar| |#=| |16|←|↵|}|↵|#fun| |bar| |#=| |17|←|↵|}| //│ Parsed: {class A(x,) {class B {fun foo = 1; fun bar = 11}; fun getB = new B { ‹fun foo = 2; fun bar = 12› }; fun bar = 13}; class C: A {fun getB = new B { ‹fun foo = 3; fun bar = 14› }; fun bar = 15}; new C { ‹fun getB = new B { ‹fun foo = 4; fun bar = 16› }; fun bar = 17› }} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, A, (), Tup(_: Var(x)), (), None, None, TypingUnit(NuTypeDef(class, B, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, foo, None, [], IntLit(1)), NuFunDef(None, bar, None, [], IntLit(11)))), NuFunDef(None, getB, None, [], Rft(NuNew(Var(B)), ...)), NuFunDef(None, bar, None, [], IntLit(13)))), NuTypeDef(class, C, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, getB, None, [], Rft(NuNew(Var(B)), ...)), NuFunDef(None, bar, None, [], IntLit(15)))), Rft(NuNew(Var(C)), ...)) +//│ TypingUnit(NuTypeDef(Cls, TypeName("A"), Ls(), S(Tup((N, Fld(_, Var("x"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuTypeDef(Cls, TypeName("B"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, foo, N, Nil, L(IntLit(1))), NuFunDef(None, bar, N, Nil, L(IntLit(11))))), NuFunDef(None, getB, N, Nil, L(Rft(NuNew(Var("B")), TypingUnit(NuFunDef(None, foo, N, Nil, L(IntLit(2))), NuFunDef(None, bar, N, Nil, L(IntLit(12))))))), NuFunDef(None, bar, N, Nil, L(IntLit(13))))), NuTypeDef(Cls, TypeName("C"), Ls(), N, N, S(TypeName("A")), Ls(), N, N, TypingUnit(NuFunDef(None, getB, N, Nil, L(Rft(NuNew(Var("B")), TypingUnit(NuFunDef(None, foo, N, Nil, L(IntLit(3))), NuFunDef(None, bar, N, Nil, L(IntLit(14))))))), NuFunDef(None, bar, N, Nil, L(IntLit(15))))), Rft(NuNew(Var("C")), TypingUnit(NuFunDef(None, getB, N, Nil, L(Rft(NuNew(Var("B")), TypingUnit(NuFunDef(None, foo, N, Nil, L(IntLit(4))), NuFunDef(None, bar, N, Nil, L(IntLit(16))))))), NuFunDef(None, bar, N, Nil, L(IntLit(17)))))) //│ Lifted: //│ Lifting failed: mlscript.codegen.CodeGenError: Cannot find type B. Class values are not supported in lifter. //│ @@ -125,7 +125,7 @@ class Parent(x) { //│ |#class| |Parent|‹|T|,| |U|,| |V|›|(|x|)| |{| |→|#fun| |foo|(|x|#:| |Int|)|#:| |T| |#=| |x|+|1|↵|#class| |Inner|‹|W|›|(|y|#:| |Int|)|{|→|#fun| |bar|(|z|#:| |U|)| |#=| |foo|(|y|)|↵|#fun| |boo|(|z|#:| |W|)| |#=| |z|←|↵|}|←|↵|}| //│ Parsed: {class Parent‹T, U, V›(x,) {fun foo = (x: Int,) => +(x, 1,) : T; class Inner‹W›(y: Int,) {fun bar = (z: U,) => foo(y,); fun boo = (z: W,) => z}}} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, Parent, ((None,TypeName(T)), (None,TypeName(U)), (None,TypeName(V))), Tup(_: Var(x)), (), None, None, TypingUnit(NuFunDef(None, foo, None, [], Lam(Tup(x: Var(Int)), Asc(App(Var(+), Tup(_: Var(x), _: IntLit(1))), TypeName(T)))), NuTypeDef(class, Inner, ((None,TypeName(W))), Tup(y: Var(Int)), (), None, None, TypingUnit(NuFunDef(None, bar, None, [], Lam(Tup(z: Var(U)), App(Var(foo), Tup(_: Var(y))))), NuFunDef(None, boo, None, [], Lam(Tup(z: Var(W)), Var(z)))))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("Parent"), Ls(N -> TypeName("T"), N -> TypeName("U"), N -> TypeName("V")), S(Tup((N, Fld(_, Var("x"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, foo, N, Nil, L(Lam(Tup((S(x), Fld(_, Var("Int"))) :: Nil), Asc(App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, IntLit(1))) :: Nil)), TypeName(T))))), NuTypeDef(Cls, TypeName("Inner"), Ls(N -> TypeName("W")), S(Tup((S(y), Fld(_, Var("Int"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, bar, N, Nil, L(Lam(Tup((S(z), Fld(_, Var("U"))) :: Nil), App(Var("foo"), Tup((N, Fld(_, Var("y"))) :: Nil))))), NuFunDef(None, boo, N, Nil, L(Lam(Tup((S(z), Fld(_, Var("W"))) :: Nil), Var("z"))))))))) //│ Lifted: //│ TypingUnit { //│ class Parent$1_Inner$2[W,U]([par$Parent$1, y: Int,]) { @@ -148,7 +148,7 @@ class A(x: Int): ({a1: Int} & B & D(x)) { //│ |#class| |B|‹|T|›| |{||}|↵|#class| |C| |{||}|↵|#class| |D|(|y|#:| |Int|)| |{||}|↵|#class| |A|‹|T|,| |U|›|(|x|#:| |Int|)|#:| |(|{|a1|#:| |Int|}| |&| |B|‹|T|›| |&| |D|(|x|)|)| |{|→|#fun| |getA|(||)| |#=| |#new| |C|{|→|#fun| |foo|(|x|#:| |T|)| |#=| |x|←|↵|}|←|↵|}| //│ Parsed: {class B‹T› {}; class C {}; class D(y: Int,) {}; class A‹T, U›(x: Int,): {a1: Int} & B[T] & D[x] {fun getA = () => new C { ‹fun foo = (x: T,) => x› }}} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, B, ((None,TypeName(T))), Tup(), (), None, None, TypingUnit()), NuTypeDef(class, C, (), Tup(), (), None, None, TypingUnit()), NuTypeDef(class, D, (), Tup(y: Var(Int)), (), None, None, TypingUnit()), NuTypeDef(class, A, ((None,TypeName(T)), (None,TypeName(U))), Tup(x: Var(Int)), (), None, None, TypingUnit(NuFunDef(None, getA, None, [], Lam(Tup(), Rft(NuNew(Var(C)), ...)))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("B"), Ls(N -> TypeName("T")), N, N, N, Ls(), N, N, TypingUnit()), NuTypeDef(Cls, TypeName("C"), Ls(), N, N, N, Ls(), N, N, TypingUnit()), NuTypeDef(Cls, TypeName("D"), Ls(), S(Tup((S(y), Fld(_, Var("Int"))) :: Nil)), N, N, Ls(), N, N, TypingUnit()), NuTypeDef(Cls, TypeName("A"), Ls(N -> TypeName("T"), N -> TypeName("U")), S(Tup((S(x), Fld(_, Var("Int"))) :: Nil)), N, S(Inter(Inter(Record(a1: Fld(N, TypeName("Int"))), AppliedType(TypeName("B"), TypeName("T") :: Nil)), AppliedType(TypeName("D"), TypeName("x") :: Nil))), Ls(), N, N, TypingUnit(NuFunDef(None, getA, N, Nil, L(Lam(Tup(Nil), Rft(NuNew(Var("C")), TypingUnit(NuFunDef(None, foo, N, Nil, L(Lam(Tup((S(x), Fld(_, Var("T"))) :: Nil), Var("x")))))))))))) //│ Lifted: //│ TypingUnit { //│ class B$1[T]([]) {} @@ -170,7 +170,7 @@ class A(x: Int) extends {a1: Int}, B, D(x){ //│ |#class| |B|‹|T|›| |{||}|↵|#class| |C| |{||}|↵|#class| |D|(|y|#:| |Int|)| |{||}|↵|#class| |A|‹|T|,| |U|›|(|x|#:| |Int|)| |#extends| |{|a1|#:| |Int|}|,| |B|‹|T|›|,| |D|(|x|)|{|→|#fun| |getA|(||)| |#=| |#new| |C|{|→|#fun| |foo|(|x|)| |#=| |x|←|↵|}|←|↵|}| //│ Parsed: {class B‹T› {}; class C {}; class D(y: Int,) {}; class A‹T, U›(x: Int,): '{' {a1: Int} '}', B‹T›, D(x,) {fun getA = () => new C { ‹fun foo = (x,) => x› }}} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, B, ((None,TypeName(T))), Tup(), (), None, None, TypingUnit()), NuTypeDef(class, C, (), Tup(), (), None, None, TypingUnit()), NuTypeDef(class, D, (), Tup(y: Var(Int)), (), None, None, TypingUnit()), NuTypeDef(class, A, ((None,TypeName(T)), (None,TypeName(U))), Tup(x: Var(Int)), (Bra(rcd = true, Rcd(Var(a1) = Var(Int))), TyApp(Var(B), List(TypeName(T))), App(Var(D), Tup(_: Var(x)))), None, None, TypingUnit(NuFunDef(None, getA, None, [], Lam(Tup(), Rft(NuNew(Var(C)), ...)))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("B"), Ls(N -> TypeName("T")), N, N, N, Ls(), N, N, TypingUnit()), NuTypeDef(Cls, TypeName("C"), Ls(), N, N, N, Ls(), N, N, TypingUnit()), NuTypeDef(Cls, TypeName("D"), Ls(), S(Tup((S(y), Fld(_, Var("Int"))) :: Nil)), N, N, Ls(), N, N, TypingUnit()), NuTypeDef(Cls, TypeName("A"), Ls(N -> TypeName("T"), N -> TypeName("U")), S(Tup((S(x), Fld(_, Var("Int"))) :: Nil)), N, N, Ls(Bra(rcd = true, Rcd(Var("a1") = Var("Int"))), TyApp(Var("B"), List(TypeName(T))), App(Var("D"), Tup((N, Fld(_, Var("x"))) :: Nil))), N, N, TypingUnit(NuFunDef(None, getA, N, Nil, L(Lam(Tup(Nil), Rft(NuNew(Var("C")), TypingUnit(NuFunDef(None, foo, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Var("x")))))))))))) //│ Lifted: //│ TypingUnit { //│ class B$1[T]([]) {} @@ -190,7 +190,7 @@ class Child(x): ({ age: T } & { name: String}) { //│ |#class| |Child|‹|T|,| |U|›|(|x|)|#:| |(|{| |age|#:| |T| |}| |&| |{| |name|#:| |String|}|)| |{|→|#class| |Inner|{|→|#fun| |foo| |#=| |age|←|↵|}|↵|#fun| |bar| |#=| |age|↵|#fun| |boo| |#=| |#new| |Inner|←|↵|}| //│ Parsed: {class Child‹T, U›(x,): {age: T} & {name: String} {class Inner {fun foo = age}; fun bar = age; fun boo = new Inner}} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, Child, ((None,TypeName(T)), (None,TypeName(U))), Tup(_: Var(x)), (), None, None, TypingUnit(NuTypeDef(class, Inner, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, foo, None, [], Var(age)))), NuFunDef(None, bar, None, [], Var(age)), NuFunDef(None, boo, None, [], NuNew(Var(Inner)))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("Child"), Ls(N -> TypeName("T"), N -> TypeName("U")), S(Tup((N, Fld(_, Var("x"))) :: Nil)), N, S(Inter(Record(age: Fld(N, TypeName("T"))), Record(name: Fld(N, TypeName("String"))))), Ls(), N, N, TypingUnit(NuTypeDef(Cls, TypeName("Inner"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, foo, N, Nil, L(Var("age"))))), NuFunDef(None, bar, N, Nil, L(Var("age"))), NuFunDef(None, boo, N, Nil, L(NuNew(Var("Inner"))))))) //│ Lifted: //│ TypingUnit { //│ class Child$1_Inner$2([par$Child$1, age,]) {fun foo = () => (this).age} @@ -213,7 +213,7 @@ new A(0) { //│ |#class| |A|(|x|#:| |Int|)| |{|→|#fun| |getA|#:| |Int| |#=| |0|↵|#fun| |getA1| |#=| |1|←|↵|}|↵|#new| |A|(|0|)| |{|→|#fun| |getA| |#=| |3|↵|#fun| |getA2| |#=| |2|←|↵|}| //│ Parsed: {class A(x: Int,) {fun getA = 0 : Int; fun getA1 = 1}; (new A)(0,) { ‹fun getA = 3; fun getA2 = 2› }} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, A, (), Tup(x: Var(Int)), (), None, None, TypingUnit(NuFunDef(None, getA, None, [], Asc(IntLit(0), TypeName(Int))), NuFunDef(None, getA1, None, [], IntLit(1)))), Rft(App(NuNew(Var(A)), Tup(_: IntLit(0))), ...)) +//│ TypingUnit(NuTypeDef(Cls, TypeName("A"), Ls(), S(Tup((S(x), Fld(_, Var("Int"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, getA, N, Nil, L(Asc(IntLit(0), TypeName(Int)))), NuFunDef(None, getA1, N, Nil, L(IntLit(1))))), Rft(App(NuNew(Var("A")), Tup((N, Fld(_, IntLit(0))) :: Nil)), TypingUnit(NuFunDef(None, getA, N, Nil, L(IntLit(3))), NuFunDef(None, getA2, N, Nil, L(IntLit(2)))))) //│ Lifted: //│ TypingUnit { //│ class A$1([x: Int,]) {fun getA = () => 0 : Int; fun getA1 = () => 1} @@ -234,7 +234,7 @@ new A(1) { //│ |#class| |A|(|x|)| |{|→|#class| |B|(|y|)| |{| |}|←|↵|}|↵|#new| |A|(|1|)| |{|→|#fun| |getB| |#=| |#new| |B|(|2|)|{|→|#fun| |getB| |#=| |#new| |B|(|3|)|←|↵|}|←|↵|}| //│ Parsed: {class A(x,) {class B(y,) {}}; (new A)(1,) { ‹fun getB = (new B)(2,) { ‹fun getB = (new B)(3,)› }› }} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, A, (), Tup(_: Var(x)), (), None, None, TypingUnit(NuTypeDef(class, B, (), Tup(_: Var(y)), (), None, None, TypingUnit()))), Rft(App(NuNew(Var(A)), Tup(_: IntLit(1))), ...)) +//│ TypingUnit(NuTypeDef(Cls, TypeName("A"), Ls(), S(Tup((N, Fld(_, Var("x"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuTypeDef(Cls, TypeName("B"), Ls(), S(Tup((N, Fld(_, Var("y"))) :: Nil)), N, N, Ls(), N, N, TypingUnit()))), Rft(App(NuNew(Var("A")), Tup((N, Fld(_, IntLit(1))) :: Nil)), TypingUnit(NuFunDef(None, getB, N, Nil, L(Rft(App(NuNew(Var("B")), Tup((N, Fld(_, IntLit(2))) :: Nil)), TypingUnit(NuFunDef(None, getB, N, Nil, L(App(NuNew(Var("B")), Tup((N, Fld(_, IntLit(3))) :: Nil))))))))))) //│ Lifted: //│ TypingUnit { //│ class A$1_B$2([par$A$1, y,]) {} @@ -267,7 +267,7 @@ new B{ //│ |#class| |A| |{|→|#fun| |getA| |#=| |0|↵|#fun| |funcA| |#=| |10|←|↵|}|↵|#class| |B|#:| |A|{|→|#fun| |getA| |#=| |1|↵|#fun| |funcB| |#=| |11|←|↵|}|↵|#new| |A|↵|#new| |B|↵|#fun| |f|(|x|)| |#=| |#if| |x| |is| |A| |#then| |0| |#else| |1|↵|f|(|#new| |A|{|→|#fun| |getA| |#=| |2|←|↵|}|)|↵|#new| |B|{|→|#fun| |getA| |#=| |funcB|←|↵|}| //│ Parsed: {class A {fun getA = 0; fun funcA = 10}; class B: A {fun getA = 1; fun funcB = 11}; new A; new B; fun f = (x,) => if (is(x, A,)) then 0 else 1; f(new A { ‹fun getA = 2› },); new B { ‹fun getA = funcB› }} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, A, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, getA, None, [], IntLit(0)), NuFunDef(None, funcA, None, [], IntLit(10)))), NuTypeDef(class, B, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, getA, None, [], IntLit(1)), NuFunDef(None, funcB, None, [], IntLit(11)))), NuNew(Var(A)), NuNew(Var(B)), NuFunDef(None, f, None, [], Lam(Tup(_: Var(x)), If(IfThen(App(Var(is), Tup(_: Var(x), _: Var(A))), IntLit(0), Some(IntLit(1))))), App(Var(f), Tup(_: Rft(NuNew(Var(A)), ...))), Rft(NuNew(Var(B)), ...)) +//│ TypingUnit(NuTypeDef(Cls, TypeName("A"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, getA, N, Nil, L(IntLit(0))), NuFunDef(None, funcA, N, Nil, L(IntLit(10))))), NuTypeDef(Cls, TypeName("B"), Ls(), N, N, S(TypeName("A")), Ls(), N, N, TypingUnit(NuFunDef(None, getA, N, Nil, L(IntLit(1))), NuFunDef(None, funcB, N, Nil, L(IntLit(11))))), NuNew(Var("A")), NuNew(Var("B")), NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), If(IfThen(App(Var("is"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("A"))) :: Nil)), IntLit(0), Some(IntLit(1)))))), App(Var("f"), Tup((N, Fld(_, Rft(NuNew(Var("A")), TypingUnit(NuFunDef(None, getA, N, Nil, L(IntLit(2))))))) :: Nil)), Rft(NuNew(Var("B")), TypingUnit(NuFunDef(None, getA, N, Nil, L(Var("funcB")))))) //│ Lifted: //│ TypingUnit { //│ class A$1([]) {fun getA = () => 0; fun funcA = () => 10} @@ -306,7 +306,7 @@ class A{ //│ |#class| |A|{|→|#class| |B|{|→|#fun| |funB| |#=| |1|↵|#fun| |foo| |#=| |100|←|↵|}|↵|#class| |C|#:| |B|{|→|#fun| |funC| |#=| |2|↵|#fun| |foo| |#=| |1000|←|↵|}|↵|#class| |D|{|→|#fun| |funD| |#=| |3|↵|#fun| |foo| |#=| |10000| |↵|#class| |E|#:| |C|{|→|#fun| |funE| |#=| |4|↵|#fun| |foo| |#=| |100000|←|↵|}|↵|#class| |F|#:| |E|{|→|#fun| |funF| |#=| |5|↵|#fun| |foo| |#=| |1000000|←|↵|}|←|↵|}|←|↵|}| //│ Parsed: {class A {class B {fun funB = 1; fun foo = 100}; class C: B {fun funC = 2; fun foo = 1000}; class D {fun funD = 3; fun foo = 10000; class E: C {fun funE = 4; fun foo = 100000}; class F: E {fun funF = 5; fun foo = 1000000}}}} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, A, (), Tup(), (), None, None, TypingUnit(NuTypeDef(class, B, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, funB, None, [], IntLit(1)), NuFunDef(None, foo, None, [], IntLit(100)))), NuTypeDef(class, C, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, funC, None, [], IntLit(2)), NuFunDef(None, foo, None, [], IntLit(1000)))), NuTypeDef(class, D, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, funD, None, [], IntLit(3)), NuFunDef(None, foo, None, [], IntLit(10000)), NuTypeDef(class, E, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, funE, None, [], IntLit(4)), NuFunDef(None, foo, None, [], IntLit(100000)))), NuTypeDef(class, F, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, funF, None, [], IntLit(5)), NuFunDef(None, foo, None, [], IntLit(1000000))))))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("A"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuTypeDef(Cls, TypeName("B"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, funB, N, Nil, L(IntLit(1))), NuFunDef(None, foo, N, Nil, L(IntLit(100))))), NuTypeDef(Cls, TypeName("C"), Ls(), N, N, S(TypeName("B")), Ls(), N, N, TypingUnit(NuFunDef(None, funC, N, Nil, L(IntLit(2))), NuFunDef(None, foo, N, Nil, L(IntLit(1000))))), NuTypeDef(Cls, TypeName("D"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, funD, N, Nil, L(IntLit(3))), NuFunDef(None, foo, N, Nil, L(IntLit(10000))), NuTypeDef(Cls, TypeName("E"), Ls(), N, N, S(TypeName("C")), Ls(), N, N, TypingUnit(NuFunDef(None, funE, N, Nil, L(IntLit(4))), NuFunDef(None, foo, N, Nil, L(IntLit(100000))))), NuTypeDef(Cls, TypeName("F"), Ls(), N, N, S(TypeName("E")), Ls(), N, N, TypingUnit(NuFunDef(None, funF, N, Nil, L(IntLit(5))), NuFunDef(None, foo, N, Nil, L(IntLit(1000000)))))))))) //│ Lifted: //│ TypingUnit { //│ class A$1_B$2([par$A$1,]) {fun funB = () => 1; fun foo = () => 100} @@ -349,7 +349,7 @@ class A{ //│ |#class| |A|{|→|#class| |B|{|→|#fun| |funB| |#=| |1|↵|#fun| |foo| |#=| |100|←|↵|}|↵|#class| |C|#:| |B|{|→|#fun| |funC| |#=| |2|↵|#fun| |foo| |#=| |1000|↵|#fun| |getB| |#=| |#new| |B|←|↵|}|↵|#class| |D|{|→|#fun| |funD| |#=| |3|↵|#fun| |foo| |#=| |10000| |↵|#class| |E|#:| |C|{|→|#fun| |funE| |#=| |4|↵|#fun| |foo| |#=| |100000|↵|#fun| |getD| |#=| |#new| |D|←|↵|}|↵|#class| |F|#:| |E|{|→|#fun| |funF| |#=| |5|↵|#fun| |foo| |#=| |1000000|↵|#fun| |getE| |#=| |#new| |E|{|→|#fun| |foo| |#=| |0|←|↵|}|←|↵|}|←|↵|}|←|↵|}| //│ Parsed: {class A {class B {fun funB = 1; fun foo = 100}; class C: B {fun funC = 2; fun foo = 1000; fun getB = new B}; class D {fun funD = 3; fun foo = 10000; class E: C {fun funE = 4; fun foo = 100000; fun getD = new D}; class F: E {fun funF = 5; fun foo = 1000000; fun getE = new E { ‹fun foo = 0› }}}}} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, A, (), Tup(), (), None, None, TypingUnit(NuTypeDef(class, B, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, funB, None, [], IntLit(1)), NuFunDef(None, foo, None, [], IntLit(100)))), NuTypeDef(class, C, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, funC, None, [], IntLit(2)), NuFunDef(None, foo, None, [], IntLit(1000)), NuFunDef(None, getB, None, [], NuNew(Var(B))))), NuTypeDef(class, D, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, funD, None, [], IntLit(3)), NuFunDef(None, foo, None, [], IntLit(10000)), NuTypeDef(class, E, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, funE, None, [], IntLit(4)), NuFunDef(None, foo, None, [], IntLit(100000)), NuFunDef(None, getD, None, [], NuNew(Var(D))))), NuTypeDef(class, F, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, funF, None, [], IntLit(5)), NuFunDef(None, foo, None, [], IntLit(1000000)), NuFunDef(None, getE, None, [], Rft(NuNew(Var(E)), ...))))))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("A"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuTypeDef(Cls, TypeName("B"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, funB, N, Nil, L(IntLit(1))), NuFunDef(None, foo, N, Nil, L(IntLit(100))))), NuTypeDef(Cls, TypeName("C"), Ls(), N, N, S(TypeName("B")), Ls(), N, N, TypingUnit(NuFunDef(None, funC, N, Nil, L(IntLit(2))), NuFunDef(None, foo, N, Nil, L(IntLit(1000))), NuFunDef(None, getB, N, Nil, L(NuNew(Var("B")))))), NuTypeDef(Cls, TypeName("D"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, funD, N, Nil, L(IntLit(3))), NuFunDef(None, foo, N, Nil, L(IntLit(10000))), NuTypeDef(Cls, TypeName("E"), Ls(), N, N, S(TypeName("C")), Ls(), N, N, TypingUnit(NuFunDef(None, funE, N, Nil, L(IntLit(4))), NuFunDef(None, foo, N, Nil, L(IntLit(100000))), NuFunDef(None, getD, N, Nil, L(NuNew(Var("D")))))), NuTypeDef(Cls, TypeName("F"), Ls(), N, N, S(TypeName("E")), Ls(), N, N, TypingUnit(NuFunDef(None, funF, N, Nil, L(IntLit(5))), NuFunDef(None, foo, N, Nil, L(IntLit(1000000))), NuFunDef(None, getE, N, Nil, L(Rft(NuNew(Var("E")), TypingUnit(NuFunDef(None, foo, N, Nil, L(IntLit(0)))))))))))))) //│ Lifted: //│ TypingUnit { //│ class A$1_B$2([par$A$1,]) {fun funB = () => 1; fun foo = () => 100} @@ -384,7 +384,7 @@ new A //│ |#class| |A|{|→|#class| |B|{|→|#fun| |foo| |#=| |1|←|↵|}|↵|#fun| |bar| |#=| |#new| |B|←|↵|}|↵|#new| |A| //│ Parsed: {class A {class B {fun foo = 1}; fun bar = new B}; new A} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, A, (), Tup(), (), None, None, TypingUnit(NuTypeDef(class, B, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, foo, None, [], IntLit(1)))), NuFunDef(None, bar, None, [], NuNew(Var(B))))), NuNew(Var(A))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("A"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuTypeDef(Cls, TypeName("B"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, foo, N, Nil, L(IntLit(1))))), NuFunDef(None, bar, N, Nil, L(NuNew(Var("B")))))), NuNew(Var("A"))) //│ Lifted: //│ TypingUnit { //│ class A$1_B$2([par$A$1,]) {fun foo = () => 1} @@ -409,7 +409,7 @@ let x = new A{ //│ |#class| |A|(|x|)| |{|→|#fun| |foo| |#=| |0|↵|#fun| |bar| |#=| |x|←|↵|}|↵|#let| |x| |#=| |#new| |A|{|→|#fun| |foo| |#=| |1|↵|#fun| |newFun| |#=| |2|↵|#fun| |bar| |#=| |#new| |A|(|foo|)|{|→|#fun| |foo| |#=| |bar| |+| |1|↵|#fun| |bar2| |#=| |newFun| |+| |1|←|↵|}|←|↵|}| //│ Parsed: {class A(x,) {fun foo = 0; fun bar = x}; let x = new A { ‹fun foo = 1; fun newFun = 2; fun bar = (new A)(foo,) { ‹fun foo = +(bar, 1,); fun bar2 = +(newFun, 1,)› }› }} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, A, (), Tup(_: Var(x)), (), None, None, TypingUnit(NuFunDef(None, foo, None, [], IntLit(0)), NuFunDef(None, bar, None, [], Var(x)))), NuFunDef(Some(false), x, None, [], Rft(NuNew(Var(A)), ...))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("A"), Ls(), S(Tup((N, Fld(_, Var("x"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, foo, N, Nil, L(IntLit(0))), NuFunDef(None, bar, N, Nil, L(Var("x"))))), NuFunDef(Some(false), x, N, Nil, L(Rft(NuNew(Var("A")), TypingUnit(NuFunDef(None, foo, N, Nil, L(IntLit(1))), NuFunDef(None, newFun, N, Nil, L(IntLit(2))), NuFunDef(None, bar, N, Nil, L(Rft(App(NuNew(Var("A")), Tup((N, Fld(_, Var("foo"))) :: Nil)), TypingUnit(NuFunDef(None, foo, N, Nil, L(App(Var("+"), Tup((N, Fld(_, Var("bar"))) :: (N, Fld(_, IntLit(1))) :: Nil)))), NuFunDef(None, bar2, N, Nil, L(App(Var("+"), Tup((N, Fld(_, Var("newFun"))) :: (N, Fld(_, IntLit(1))) :: Nil))))))))))))) //│ Lifted: //│ TypingUnit { //│ class A$1([x,]) {fun foo = () => 0; fun bar = () => (this).x} @@ -437,7 +437,7 @@ new A{ //│ |#class| |A| |{||}|↵|#new| |A|{|→|#fun| |foo| |#=| |1|↵|#fun| |bar| |#=| |#new| |A|{|→|#fun| |foo1| |#=| |foo|↵|#fun| |bar1| |#=| |#new| |A|{|→|#fun| |foo2| |#=| |foo|↵|#fun| |bar2| |#=| |#new| |A|{|→|#fun| |foo3| |#=| |foo|↵|#fun| |bar3| |#=| |#new| |A|{|→|#fun| |foo4| |#=| |foo|↵|#fun| |bar4| |#=| |0|←|↵|}|←|↵|}|←|↵|}|←|↵|}|←|↵|}| //│ Parsed: {class A {}; new A { ‹fun foo = 1; fun bar = new A { ‹fun foo1 = foo; fun bar1 = new A { ‹fun foo2 = foo; fun bar2 = new A { ‹fun foo3 = foo; fun bar3 = new A { ‹fun foo4 = foo; fun bar4 = 0› }› }› }› }› }} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, A, (), Tup(), (), None, None, TypingUnit()), Rft(NuNew(Var(A)), ...)) +//│ TypingUnit(NuTypeDef(Cls, TypeName("A"), Ls(), N, N, N, Ls(), N, N, TypingUnit()), Rft(NuNew(Var("A")), TypingUnit(NuFunDef(None, foo, N, Nil, L(IntLit(1))), NuFunDef(None, bar, N, Nil, L(Rft(NuNew(Var("A")), TypingUnit(NuFunDef(None, foo1, N, Nil, L(Var("foo"))), NuFunDef(None, bar1, N, Nil, L(Rft(NuNew(Var("A")), TypingUnit(NuFunDef(None, foo2, N, Nil, L(Var("foo"))), NuFunDef(None, bar2, N, Nil, L(Rft(NuNew(Var("A")), TypingUnit(NuFunDef(None, foo3, N, Nil, L(Var("foo"))), NuFunDef(None, bar3, N, Nil, L(Rft(NuNew(Var("A")), TypingUnit(NuFunDef(None, foo4, N, Nil, L(Var("foo"))), NuFunDef(None, bar4, N, Nil, L(IntLit(0)))))))))))))))))))))) //│ Lifted: //│ TypingUnit {class A$1([]) {}; Code(List(new A$1([]) {}))} //│ diff --git a/compiler/shared/test/diff/LifterBlks.mls b/compiler/shared/test/diff/LifterBlks.mls index 414d4109..4738cf4c 100644 --- a/compiler/shared/test/diff/LifterBlks.mls +++ b/compiler/shared/test/diff/LifterBlks.mls @@ -7,7 +7,7 @@ fun foo = //│ |#fun| |foo| |#=|→|print|(|"ok"|)|↵|print|(|"ko"|)|←| //│ Parsed: {fun foo = {print("ok",); print("ko",)}} //│ Parsed: -//│ TypingUnit(NuFunDef(None, foo, None, [], Blk(...))) +//│ TypingUnit(NuFunDef(None, foo, N, Nil, L(Blk(App(Var("print"), Tup((N, Fld(_, StrLit(ok))) :: Nil)), App(Var("print"), Tup((N, Fld(_, StrLit(ko))) :: Nil)))))) //│ Lifted: //│ TypingUnit {fun foo$1 = () => {print("ok",); print("ko",)}} //│ @@ -19,7 +19,7 @@ class A{ //│ |#class| |A|{|→|#class| |B| |{||}|↵|#fun| |foo|(|x|#:| |B|)| |#=| |(|x| |#:| |B|)|←|↵|}| //│ Parsed: {class A {class B {}; fun foo = (x: B,) => '(' x : B ')'}} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, A, (), Tup(), (), None, None, TypingUnit(NuTypeDef(class, B, (), Tup(), (), None, None, TypingUnit()), NuFunDef(None, foo, None, [], Lam(Tup(x: Var(B)), Bra(rcd = false, Asc(Var(x), TypeName(B)))))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("A"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuTypeDef(Cls, TypeName("B"), Ls(), N, N, N, Ls(), N, N, TypingUnit()), NuFunDef(None, foo, N, Nil, L(Lam(Tup((S(x), Fld(_, Var("B"))) :: Nil), Bra(rcd = false, Asc(Var("x"), TypeName(B))))))))) //│ Lifted: //│ TypingUnit { //│ class A$1_B$2([par$A$1,]) {} @@ -41,7 +41,7 @@ fun foo = //│ |#fun| |foo| |#=|→|#let| |local|(|x|)| |#=|→|#class| |Foo| |{|→|#fun| |bar| |#=| |x| |+| |1|←|↵|}|↵|Foo|(||)|.bar|←|↵|print| |#of| |local|(|0|)| |+| |local|(|1|)|↵|print| |#of| |(|local| |#of| |0|)| |+| |local| |#of| |1|↵|#fun| |tmp| |#=| |1|↵|print| |#of| |local| |#of| |0| |+| |local| |#of| |1|↵|#fun| |tmp| |#=| |2|←| //│ Parsed: {fun foo = {let local = (x,) => {class Foo {fun bar = +(x, 1,)}; (Foo()).bar}; print(+(local(0,), local(1,),),); print(+('(' local(0,) ')', local(1,),),); fun tmp = 1; print(local(+(0, local(1,),),),); fun tmp = 2}} //│ Parsed: -//│ TypingUnit(NuFunDef(None, foo, None, [], Blk(...))) +//│ TypingUnit(NuFunDef(None, foo, N, Nil, L(Blk(NuFunDef(Some(false), local, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Blk(NuTypeDef(Cls, TypeName("Foo"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, bar, N, Nil, L(App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, IntLit(1))) :: Nil)))))), Sel(App(Var("Foo"), Tup(Nil)), bar))))), App(Var("print"), Tup((N, Fld(_, App(Var("+"), Tup((N, Fld(_, App(Var("local"), Tup((N, Fld(_, IntLit(0))) :: Nil)))) :: (N, Fld(_, App(Var("local"), Tup((N, Fld(_, IntLit(1))) :: Nil)))) :: Nil)))) :: Nil)), App(Var("print"), Tup((N, Fld(_, App(Var("+"), Tup((N, Fld(_, Bra(rcd = false, App(Var("local"), Tup((N, Fld(_, IntLit(0))) :: Nil))))) :: (N, Fld(_, App(Var("local"), Tup((N, Fld(_, IntLit(1))) :: Nil)))) :: Nil)))) :: Nil)), NuFunDef(None, tmp, N, Nil, L(IntLit(1))), App(Var("print"), Tup((N, Fld(_, App(Var("local"), Tup((N, Fld(_, App(Var("+"), Tup((N, Fld(_, IntLit(0))) :: (N, Fld(_, App(Var("local"), Tup((N, Fld(_, IntLit(1))) :: Nil)))) :: Nil)))) :: Nil)))) :: Nil)), NuFunDef(None, tmp, N, Nil, L(IntLit(2))))))) //│ Lifted: //│ TypingUnit { //│ class Foo$1([x,]) {fun bar = () => +((this).x, 1,)} @@ -57,7 +57,7 @@ f(0) //│ |#class| |A|(|y|)|{||}|↵|#let| |f| |#=| |x| |#=>| |#new| |A|(|0|)|{|#fun| |bar| |#=| |x|+|y|}|↵|f|(|0|)| //│ Parsed: {class A(y,) {}; let f = (x,) => (new A)(0,) { ‹fun bar = +(x, y,)› }; f(0,)} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, A, (), Tup(_: Var(y)), (), None, None, TypingUnit()), NuFunDef(Some(false), f, None, [], Lam(Tup(_: Var(x)), Rft(App(NuNew(Var(A)), Tup(_: IntLit(0))), ...))), App(Var(f), Tup(_: IntLit(0)))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("A"), Ls(), S(Tup((N, Fld(_, Var("y"))) :: Nil)), N, N, Ls(), N, N, TypingUnit()), NuFunDef(Some(false), f, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Rft(App(NuNew(Var("A")), Tup((N, Fld(_, IntLit(0))) :: Nil)), TypingUnit(NuFunDef(None, bar, N, Nil, L(App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("y"))) :: Nil))))))))), App(Var("f"), Tup((N, Fld(_, IntLit(0))) :: Nil))) //│ Lifted: //│ TypingUnit { //│ class A$1([y,]) {} @@ -80,7 +80,7 @@ class A(x){ //│ |#class| |A|(|x|)|{|→|#fun| |w| |#=| |x|↵|#fun| |foo|(|y|)| |#=| |→|#class| |B|(|z|)|{|→|#fun| |bar| |#=| |x|+|y|+|z|←|↵|}|↵|#new| |B|(|0|)|{|→|#fun| |bar| |#=| |w|+|y|+|z|←|↵|}|←|←|↵|}| //│ Parsed: {class A(x,) {fun w = x; fun foo = (y,) => {class B(z,) {fun bar = +(+(x, y,), z,)}; (new B)(0,) { ‹fun bar = +(+(w, y,), z,)› }}}} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, A, (), Tup(_: Var(x)), (), None, None, TypingUnit(NuFunDef(None, w, None, [], Var(x)), NuFunDef(None, foo, None, [], Lam(Tup(_: Var(y)), Blk(...)))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("A"), Ls(), S(Tup((N, Fld(_, Var("x"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, w, N, Nil, L(Var("x"))), NuFunDef(None, foo, N, Nil, L(Lam(Tup((N, Fld(_, Var("y"))) :: Nil), Blk(NuTypeDef(Cls, TypeName("B"), Ls(), S(Tup((N, Fld(_, Var("z"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, bar, N, Nil, L(App(Var("+"), Tup((N, Fld(_, App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("y"))) :: Nil)))) :: (N, Fld(_, Var("z"))) :: Nil)))))), Rft(App(NuNew(Var("B")), Tup((N, Fld(_, IntLit(0))) :: Nil)), TypingUnit(NuFunDef(None, bar, N, Nil, L(App(Var("+"), Tup((N, Fld(_, App(Var("+"), Tup((N, Fld(_, Var("w"))) :: (N, Fld(_, Var("y"))) :: Nil)))) :: (N, Fld(_, Var("z"))) :: Nil))))))))))))) //│ Lifted: //│ TypingUnit { //│ class A$1_B$2([par$A$1, z, y,]) { @@ -111,7 +111,7 @@ fun f(x,y,z) = //│ |#fun| |f|(|x|,|y|,|z|)| |#=| |→|#class| |A|{|→|#fun| |foo| |#=| |#new| |B|↵|#fun| |bar1| |#=| |x|←|↵|}|↵|#class| |B|{|→|#fun| |foo| |#=| |#new| |A|↵|#fun| |bar2| |#=| |y|←|↵|}|↵|#class| |C| |#extends| |A|,| |B| |{|→|#fun| |bar| |#=| |bar1| |+| |bar2|←|↵|}|←| //│ Parsed: {fun f = (x, y, z,) => {class A {fun foo = new B; fun bar1 = x}; class B {fun foo = new A; fun bar2 = y}; class C: A, B {fun bar = +(bar1, bar2,)}}} //│ Parsed: -//│ TypingUnit(NuFunDef(None, f, None, [], Lam(Tup(_: Var(x), _: Var(y), _: Var(z)), Blk(...)))) +//│ TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("y"))) :: (N, Fld(_, Var("z"))) :: Nil), Blk(NuTypeDef(Cls, TypeName("A"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, foo, N, Nil, L(NuNew(Var("B")))), NuFunDef(None, bar1, N, Nil, L(Var("x"))))), NuTypeDef(Cls, TypeName("B"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, foo, N, Nil, L(NuNew(Var("A")))), NuFunDef(None, bar2, N, Nil, L(Var("y"))))), NuTypeDef(Cls, TypeName("C"), Ls(), N, N, N, Ls(Var("A"), Var("B")), N, N, TypingUnit(NuFunDef(None, bar, N, Nil, L(App(Var("+"), Tup((N, Fld(_, Var("bar1"))) :: (N, Fld(_, Var("bar2"))) :: Nil))))))))))) //│ Lifted: //│ TypingUnit { //│ class A$1([x, y,]) { @@ -142,7 +142,7 @@ fun f(x,y,z) = //│ |#fun| |f|(|x|,|y|,|z|)| |#=| |→|#class| |C|{|→|#class| |A|{|→|#fun| |foo| |#=| |#new| |B|↵|#fun| |bar1| |#=| |x|←|↵|}|↵|#class| |B|{|→|#fun| |foo| |#=| |#new| |A|↵|#fun| |bar2| |#=| |y|←|↵|}|↵|#fun| |boo| |#=| |(|#new| |A|)|.bar1| |+| |B|(||)|.bar2| |+| |z|←|↵|}|←| //│ Parsed: {fun f = (x, y, z,) => {class C {class A {fun foo = new B; fun bar1 = x}; class B {fun foo = new A; fun bar2 = y}; fun boo = +(+(('(' new A ')').bar1, (B()).bar2,), z,)}}} //│ Parsed: -//│ TypingUnit(NuFunDef(None, f, None, [], Lam(Tup(_: Var(x), _: Var(y), _: Var(z)), Blk(...)))) +//│ TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("y"))) :: (N, Fld(_, Var("z"))) :: Nil), Blk(NuTypeDef(Cls, TypeName("C"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuTypeDef(Cls, TypeName("A"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, foo, N, Nil, L(NuNew(Var("B")))), NuFunDef(None, bar1, N, Nil, L(Var("x"))))), NuTypeDef(Cls, TypeName("B"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, foo, N, Nil, L(NuNew(Var("A")))), NuFunDef(None, bar2, N, Nil, L(Var("y"))))), NuFunDef(None, boo, N, Nil, L(App(Var("+"), Tup((N, Fld(_, App(Var("+"), Tup((N, Fld(_, Sel(Bra(rcd = false, NuNew(Var("A"))), bar1))) :: (N, Fld(_, Sel(App(Var("B"), Tup(Nil)), bar2))) :: Nil)))) :: (N, Fld(_, Var("z"))) :: Nil))))))))))) //│ Lifted: //│ TypingUnit { //│ class C$1_A$2([par$C$1,]) { @@ -168,7 +168,7 @@ fun f(y) = //│ |#fun| |f|(|y|)| |#=|→|#let| |g|(|x|)| |#=| |x| |+| |y| |+| |1|↵|#class| |Foo|(|x|)| |{|→|#fun| |h| |#=| |g|(|x|)|←|↵|}|←| //│ Parsed: {fun f = (y,) => {let g = (x,) => +(+(x, y,), 1,); class Foo(x,) {fun h = g(x,)}}} //│ Parsed: -//│ TypingUnit(NuFunDef(None, f, None, [], Lam(Tup(_: Var(y)), Blk(...)))) +//│ TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Fld(_, Var("y"))) :: Nil), Blk(NuFunDef(Some(false), g, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), App(Var("+"), Tup((N, Fld(_, App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("y"))) :: Nil)))) :: (N, Fld(_, IntLit(1))) :: Nil))))), NuTypeDef(Cls, TypeName("Foo"), Ls(), S(Tup((N, Fld(_, Var("x"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, h, N, Nil, L(App(Var("g"), Tup((N, Fld(_, Var("x"))) :: Nil))))))))))) //│ Lifted: //│ TypingUnit { //│ class Foo$1([x, y,]) {fun h = () => g$2((this).x, y,)} @@ -180,7 +180,7 @@ fun f(y) = //│ | |Foo|(|1|)|.h| //│ Parsed: {(Foo(1,)).h} //│ Parsed: -//│ TypingUnit(Sel(App(Var(Foo), Tup(_: IntLit(1))), h)) +//│ TypingUnit(Sel(App(Var("Foo"), Tup((N, Fld(_, IntLit(1))) :: Nil)), h)) //│ Lifted: //│ TypingUnit {Code(List((Foo(1,)).h))} //│ @@ -188,7 +188,7 @@ fun f(y) = //│ | |Foo|(|x|)|.h| //│ Parsed: {(Foo(x,)).h} //│ Parsed: -//│ TypingUnit(Sel(App(Var(Foo), Tup(_: Var(x))), h)) +//│ TypingUnit(Sel(App(Var("Foo"), Tup((N, Fld(_, Var("x"))) :: Nil)), h)) //│ Lifted: //│ TypingUnit {Code(List((Foo(x,)).h))} //│ @@ -204,7 +204,7 @@ fun f(x) = //│ |#fun| |f|(|x|)| |#=|→|#let| |g|(|x|)| |#=| |→|#let| |h|(|x|)| |#=| |x| |+| |2|↵|Foo|(|h|(|x|)|,| |x|)|.bar|←|↵|#class| |Foo|(|x|,| |y|)| |{|→|#fun| |bar| |#=| |g|(|x|)|+|y|←|↵|}|↵|Foo|(|x|,| |x|)|.bar|←| //│ Parsed: {fun f = (x,) => {let g = (x,) => {let h = (x,) => +(x, 2,); (Foo(h(x,), x,)).bar}; class Foo(x, y,) {fun bar = +(g(x,), y,)}; (Foo(x, x,)).bar}} //│ Parsed: -//│ TypingUnit(NuFunDef(None, f, None, [], Lam(Tup(_: Var(x)), Blk(...)))) +//│ TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Blk(NuFunDef(Some(false), g, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Blk(NuFunDef(Some(false), h, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, IntLit(2))) :: Nil))))), Sel(App(Var("Foo"), Tup((N, Fld(_, App(Var("h"), Tup((N, Fld(_, Var("x"))) :: Nil)))) :: (N, Fld(_, Var("x"))) :: Nil)), bar))))), NuTypeDef(Cls, TypeName("Foo"), Ls(), S(Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("y"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, bar, N, Nil, L(App(Var("+"), Tup((N, Fld(_, App(Var("g"), Tup((N, Fld(_, Var("x"))) :: Nil)))) :: (N, Fld(_, Var("y"))) :: Nil)))))), Sel(App(Var("Foo"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("x"))) :: Nil)), bar)))))) //│ Lifted: //│ TypingUnit { //│ class Foo$1([x, y,]) {fun bar = () => +(g$2((this).x,), (this).y,)} @@ -218,7 +218,7 @@ class Foo(x, y) extends Bar(y, x), Baz(x + y) //│ |#class| |Foo|(|x|,| |y|)| |#extends| |Bar|(|y|,| |x|)|,| |Baz|(|x| |+| |y|)| //│ Parsed: {class Foo(x, y,): Bar(y, x,), Baz(+(x, y,),) {}} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, Foo, (), Tup(_: Var(x), _: Var(y)), (App(Var(Bar), Tup(_: Var(y), _: Var(x))), App(Var(Baz), Tup(_: App(Var(+), Tup(_: Var(x), _: Var(y)))))), None, None, TypingUnit())) +//│ TypingUnit(NuTypeDef(Cls, TypeName("Foo"), Ls(), S(Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("y"))) :: Nil)), N, N, Ls(App(Var("Bar"), Tup((N, Fld(_, Var("y"))) :: (N, Fld(_, Var("x"))) :: Nil)), App(Var("Baz"), Tup((N, Fld(_, App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("y"))) :: Nil)))) :: Nil))), N, N, TypingUnit())) //│ Lifted: //│ TypingUnit { //│ class Foo$1([x, y,]): Bar((this).y, (this).x,), Baz(+((this).x, (this).y,),) {} @@ -233,7 +233,7 @@ fun foo(x: T): string = //│ |#fun| |foo|‹|T|,| |U|›|(|x|#:| |T|)|#:| |string| |#=| |→|#class| |A|(|y|)| |#extends| |B|‹|T|›|,| |C|(|y|#:| |U|)| |{|→|#fun| |bar| |#=| |this|←|↵|}|↵|"rua"|←| //│ Parsed: {fun foo = (x: T,) => {class A(y,): B‹T›, C(y: U,) {fun bar = this}; "rua"} : string} //│ Parsed: -//│ TypingUnit(NuFunDef(None, foo, None, [TypeName(T), TypeName(U)], Lam(Tup(x: Var(T)), Asc(Blk(...), TypeName(string))))) +//│ TypingUnit(NuFunDef(None, foo, N, TypeName("T") :: TypeName("U") :: Nil, L(Lam(Tup((S(x), Fld(_, Var("T"))) :: Nil), Asc(Blk(NuTypeDef(Cls, TypeName("A"), Ls(), S(Tup((N, Fld(_, Var("y"))) :: Nil)), N, N, Ls(TyApp(Var("B"), List(TypeName(T))), App(Var("C"), Tup((S(y), Fld(_, Var("U"))) :: Nil))), N, N, TypingUnit(NuFunDef(None, bar, N, Nil, L(Var("this"))))), StrLit(rua)), TypeName(string)))))) //│ Lifted: //│ TypingUnit { //│ class A$1[T,U]([y,]): B‹T›, C(y: U,) {fun bar = () => this} @@ -250,7 +250,7 @@ class A{ //│ |#class| |A|‹|T|›|{|→|#class| |B|{|→|#fun| |f| |#=| |x| |#=>| |y| |#=>| |x|↵|#fun| |g|#:| |T| |#=>| |B| |#=>| |T|←|↵|}|←|↵|}| //│ Parsed: {class A‹T› {class B {fun f = (x,) => (y,) => x; fun g: T -> B -> T}}} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, A, ((None,TypeName(T))), Tup(), (), None, None, TypingUnit(NuTypeDef(class, B, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, f, None, [], Lam(Tup(_: Var(x)), Lam(Tup(_: Var(y)), Var(x)))), NuFunDef(None, g, None, [], PolyType(List(),Function(Tuple(List((None,Field(None,TypeName(T))))),Function(Tuple(List((None,Field(None,TypeName(B))))),TypeName(T)))))))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("A"), Ls(N -> TypeName("T")), N, N, N, Ls(), N, N, TypingUnit(NuTypeDef(Cls, TypeName("B"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Lam(Tup((N, Fld(_, Var("y"))) :: Nil), Var("x"))))), NuFunDef(None, g, N, Nil, R(PolyType(Ls(), Function(Tuple(N -> Fld(N, TypeName("T"))), Function(Tuple(N -> Fld(N, TypeName("B"))), TypeName("T"))))))))))) //│ Lifted: //│ TypingUnit { //│ class A$1_B$2_Lambda1$1$3([par$A$1_B$2, x,]) {fun apply = (y,) => (this).x} @@ -270,7 +270,7 @@ class Foo{ //│ |#class| |Foo|‹|T|›|{|→|#class| |RectangleBox|#:| |Box|‹|T|›| |&| |{| |breadth|#:| |T| |}|↵|#class| |StackedRectangleBoxes|‹|N|›| |#:| |RectangleBox|‹|T|›| |&| |{| |size|#:| |N| |}|↵|#class| |Bar|#:| |{|any|#:| |RectangleBox| |#=>| |StackedRectangleBoxes|}|←|↵|}| //│ Parsed: {class Foo‹T› {class RectangleBox: Box[T] & {breadth: T} {}; class StackedRectangleBoxes‹N›: RectangleBox[T] & {size: N} {}; class Bar: {any: RectangleBox -> StackedRectangleBoxes} {}}} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, Foo, ((None,TypeName(T))), Tup(), (), None, None, TypingUnit(NuTypeDef(class, RectangleBox, (), Tup(), (), None, None, TypingUnit()), NuTypeDef(class, StackedRectangleBoxes, ((None,TypeName(N))), Tup(), (), None, None, TypingUnit()), NuTypeDef(class, Bar, (), Tup(), (), None, None, TypingUnit())))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("Foo"), Ls(N -> TypeName("T")), N, N, N, Ls(), N, N, TypingUnit(NuTypeDef(Cls, TypeName("RectangleBox"), Ls(), N, N, S(Inter(AppliedType(TypeName("Box"), TypeName("T") :: Nil), Record(breadth: Fld(N, TypeName("T"))))), Ls(), N, N, TypingUnit()), NuTypeDef(Cls, TypeName("StackedRectangleBoxes"), Ls(N -> TypeName("N")), N, N, S(Inter(AppliedType(TypeName("RectangleBox"), TypeName("T") :: Nil), Record(size: Fld(N, TypeName("N"))))), Ls(), N, N, TypingUnit()), NuTypeDef(Cls, TypeName("Bar"), Ls(), N, N, S(Record(any: Fld(N, Function(Tuple(N -> Fld(N, TypeName("RectangleBox"))), TypeName("StackedRectangleBoxes"))))), Ls(), N, N, TypingUnit())))) //│ Lifted: //│ TypingUnit { //│ class Foo$1_RectangleBox$2([par$Foo$1,]) {} @@ -293,7 +293,7 @@ fun ctx(a,b) = //│ |#class| |Func|‹|T|,| |U|›| |{|→|#fun| |apply|#:| |T| |#=>| |U|←|↵|}|↵|#class| |Lambda|‹|T|,| |U|›| |#:| |Func|‹|T|,| |U|›| |{||}|↵|#fun| |ctx|(|a|,|b|)| |#=|→|#fun| |foo|(|f|#:| |Func|,| |x|)| |#=| |→|f|.apply|(|x|)|←|↵|foo|(|#new| |Lambda|{|→|#fun| |apply|(|x|)| |#=| |a|+|x|←|↵|}|,| |b|)|←| //│ Parsed: {class Func‹T, U› {fun apply: T -> U}; class Lambda‹T, U›: Func[T, U] {}; fun ctx = (a, b,) => {fun foo = (f: Func, x,) => {(f).apply(x,)}; foo(new Lambda { ‹fun apply = (x,) => +(a, x,)› }, b,)}} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, Func, ((None,TypeName(T)), (None,TypeName(U))), Tup(), (), None, None, TypingUnit(NuFunDef(None, apply, None, [], PolyType(List(),Function(Tuple(List((None,Field(None,TypeName(T))))),TypeName(U)))))), NuTypeDef(class, Lambda, ((None,TypeName(T)), (None,TypeName(U))), Tup(), (), None, None, TypingUnit()), NuFunDef(None, ctx, None, [], Lam(Tup(_: Var(a), _: Var(b)), Blk(...)))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("Func"), Ls(N -> TypeName("T"), N -> TypeName("U")), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, apply, N, Nil, R(PolyType(Ls(), Function(Tuple(N -> Fld(N, TypeName("T"))), TypeName("U"))))))), NuTypeDef(Cls, TypeName("Lambda"), Ls(N -> TypeName("T"), N -> TypeName("U")), N, N, S(AppliedType(TypeName("Func"), TypeName("T") :: TypeName("U") :: Nil)), Ls(), N, N, TypingUnit()), NuFunDef(None, ctx, N, Nil, L(Lam(Tup((N, Fld(_, Var("a"))) :: (N, Fld(_, Var("b"))) :: Nil), Blk(NuFunDef(None, foo, N, Nil, L(Lam(Tup((S(f), Fld(_, Var("Func"))) :: (N, Fld(_, Var("x"))) :: Nil), Blk(App(Sel(Var("f"), apply), Tup((N, Fld(_, Var("x"))) :: Nil)))))), App(Var("foo"), Tup((N, Fld(_, Rft(NuNew(Var("Lambda")), TypingUnit(NuFunDef(None, apply, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), App(Var("+"), Tup((N, Fld(_, Var("a"))) :: (N, Fld(_, Var("x"))) :: Nil))))))))) :: (N, Fld(_, Var("b"))) :: Nil))))))) //│ Lifted: //│ TypingUnit { //│ class Func$1[T,U]([]) {fun apply = T -> U} @@ -309,7 +309,7 @@ f(MyClass) //│ |#fun| |f|(|T|)| |#=| |→|#new| |T|(||)|←|↵|f|(|MyClass|)| //│ Parsed: {fun f = (T,) => {(new T)()}; f(MyClass,)} //│ Parsed: -//│ TypingUnit(NuFunDef(None, f, None, [], Lam(Tup(_: Var(T)), Blk(...))), App(Var(f), Tup(_: Var(MyClass)))) +//│ TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Fld(_, Var("T"))) :: Nil), Blk(App(NuNew(Var("T")), Tup(Nil)))))), App(Var("f"), Tup((N, Fld(_, Var("MyClass"))) :: Nil))) //│ Lifted: //│ Lifting failed: mlscript.codegen.CodeGenError: Cannot find type T. Class values are not supported in lifter. //│ @@ -322,7 +322,7 @@ class A { //│ |#class| |A| |{|→|#fun| |foo| |#=| |→|#fun| |bar| |#=| |foo|(||)|↵|bar|(||)|←|←|↵|}| //│ Parsed: {class A {fun foo = {fun bar = foo(); bar()}}} //│ Parsed: -//│ TypingUnit(NuTypeDef(class, A, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, foo, None, [], Blk(...))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("A"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, foo, N, Nil, L(Blk(NuFunDef(None, bar, N, Nil, L(App(Var("foo"), Tup(Nil)))), App(Var("bar"), Tup(Nil)))))))) //│ Lifted: //│ TypingUnit { //│ class A$1([]) {fun foo = () => {bar$1(this,)}} diff --git a/compiler/shared/test/diff/mono.mls b/compiler/shared/test/diff/mono.mls index 780851b8..8514285f 100644 --- a/compiler/shared/test/diff/mono.mls +++ b/compiler/shared/test/diff/mono.mls @@ -4,7 +4,7 @@ :mono fun f(x: Int) = if x then 42 else 1337 //│ Parsed: -//│ TypingUnit(NuFunDef(None, f, None, [], Lam(Tup(x: Var(Int)), If(IfThen(Var(x), IntLit(42), Some(IntLit(1337)))))) +//│ TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((S(x), Fld(_, Var("Int"))) :: Nil), If(IfThen(Var("x"), IntLit(42), Some(IntLit(1337))))))) //│ Lifted: //│ TypingUnit { //│ fun f$1 = (x: Int,) => if (x) then 42 else 1337 @@ -19,7 +19,7 @@ fun f(x: Int) = if x then 42 else 1337 :mono fun foo() = 42 //│ Parsed: -//│ TypingUnit(NuFunDef(None, foo, None, [], Lam(Tup(), IntLit(42)))) +//│ TypingUnit(NuFunDef(None, foo, N, Nil, L(Lam(Tup(Nil), IntLit(42))))) //│ Lifted: //│ TypingUnit {fun foo$1 = () => 42} //│ Mono: @@ -34,7 +34,7 @@ fun foo(x, #b) = if b then x else 1337 let a = foo(42, true) let b = foo(23, false) //│ Parsed: -//│ TypingUnit(NuFunDef(None, foo, None, [], Lam(Tup(_: Var(x), _: Var(b)), If(IfThen(Var(b), Var(x), Some(IntLit(1337))))), NuFunDef(Some(false), a, None, [], App(Var(foo), Tup(_: IntLit(42), _: Var(true)))), NuFunDef(Some(false), b, None, [], App(Var(foo), Tup(_: IntLit(23), _: Var(false))))) +//│ TypingUnit(NuFunDef(None, foo, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("b"))) :: Nil), If(IfThen(Var("b"), Var("x"), Some(IntLit(1337)))))), NuFunDef(Some(false), a, N, Nil, L(App(Var("foo"), Tup((N, Fld(_, IntLit(42))) :: (N, Fld(_, Var("true"))) :: Nil)))), NuFunDef(Some(false), b, N, Nil, L(App(Var("foo"), Tup((N, Fld(_, IntLit(23))) :: (N, Fld(_, Var("false"))) :: Nil))))) //│ Lifted: //│ TypingUnit { //│ fun foo$3 = (x, #b,) => if (b) then x else 1337 @@ -61,7 +61,7 @@ let b = foo(23, false) :mono let x = 42 + 1337 //│ Parsed: -//│ TypingUnit(NuFunDef(Some(false), x, None, [], App(Var(+), Tup(_: IntLit(42), _: IntLit(1337))))) +//│ TypingUnit(NuFunDef(Some(false), x, N, Nil, L(App(Var("+"), Tup((N, Fld(_, IntLit(42))) :: (N, Fld(_, IntLit(1337))) :: Nil))))) //│ Lifted: //│ TypingUnit {let x$1 = () => +(42, 1337,)} //│ Mono: @@ -99,7 +99,7 @@ let x = 42 + 1337 if true then 1 else 0 if 1+1 > 1 then 1 - 1 else 1*1 //│ Parsed: -//│ TypingUnit(If(IfThen(Var(true), IntLit(1), Some(IntLit(0))), If(IfThen(App(Var(>), Tup(_: App(Var(+), Tup(_: IntLit(1), _: IntLit(1))), _: IntLit(1))), App(Var(-), Tup(_: IntLit(1), _: IntLit(1))), Some(App(Var(*), Tup(_: IntLit(1), _: IntLit(1)))))) +//│ TypingUnit(If(IfThen(Var("true"), IntLit(1), Some(IntLit(0))), If(IfThen(App(Var(">"), Tup((N, Fld(_, App(Var("+"), Tup((N, Fld(_, IntLit(1))) :: (N, Fld(_, IntLit(1))) :: Nil)))) :: (N, Fld(_, IntLit(1))) :: Nil)), App(Var("-"), Tup((N, Fld(_, IntLit(1))) :: (N, Fld(_, IntLit(1))) :: Nil)), Some(App(Var("*"), Tup((N, Fld(_, IntLit(1))) :: (N, Fld(_, IntLit(1))) :: Nil))))) //│ Lifted: //│ TypingUnit { //│ Code(List(if (true) then 1 else 0)) @@ -123,7 +123,7 @@ if 1+1 > 1 then 1 - 1 else 1*1 :mono if(b) then 1 else 2 //│ Parsed: -//│ TypingUnit(If(IfThen(Bra(rcd = false, Var(b)), IntLit(1), Some(IntLit(2)))) +//│ TypingUnit(If(IfThen(Bra(rcd = false, Var("b")), IntLit(1), Some(IntLit(2)))) //│ Lifted: //│ TypingUnit {Code(List(if ('(' b ')') then 1 else 2))} //│ Mono: @@ -139,7 +139,7 @@ if(b) then 1 else 2 :mono ((f, g) => f(g))(f => f, true) //│ Parsed: -//│ TypingUnit(App(Bra(rcd = false, Lam(Tup(_: Var(f), _: Var(g)), App(Var(f), Tup(_: Var(g))))), Tup(_: Lam(Tup(_: Var(f)), Var(f)), _: Var(true)))) +//│ TypingUnit(App(Bra(rcd = false, Lam(Tup((N, Fld(_, Var("f"))) :: (N, Fld(_, Var("g"))) :: Nil), App(Var("f"), Tup((N, Fld(_, Var("g"))) :: Nil)))), Tup((N, Fld(_, Lam(Tup((N, Fld(_, Var("f"))) :: Nil), Var("f")))) :: (N, Fld(_, Var("true"))) :: Nil))) //│ Lifted: //│ TypingUnit { //│ class Lambda2$1$1([]) {fun apply = (f, g,) => f(g,)} @@ -168,7 +168,7 @@ if(b) then 1 else 2 :mono (b => if b then true else false) (true) //│ Parsed: -//│ TypingUnit(App(Bra(rcd = false, Lam(Tup(_: Var(b)), If(IfThen(Var(b), Var(true), Some(Var(false))))), Tup(_: Var(true)))) +//│ TypingUnit(App(Bra(rcd = false, Lam(Tup((N, Fld(_, Var("b"))) :: Nil), If(IfThen(Var("b"), Var("true"), Some(Var("false"))))), Tup((N, Fld(_, Var("true"))) :: Nil))) //│ Lifted: //│ TypingUnit { //│ class Lambda1$1$1([]) {fun apply = (b,) => if (b) then true else false} @@ -193,7 +193,7 @@ fun f(x) = if(x > 0) then x+1 else x - 1 f(2)+3 //│ Parsed: -//│ TypingUnit(NuFunDef(None, f, None, [], Lam(Tup(_: Var(x)), Blk(...))), App(Var(+), Tup(_: App(Var(f), Tup(_: IntLit(2))), _: IntLit(3)))) +//│ TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Blk(If(IfThen(Bra(rcd = false, App(Var(">"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, IntLit(0))) :: Nil))), App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, IntLit(1))) :: Nil)), Some(App(Var("-"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, IntLit(1))) :: Nil)))))))), App(Var("+"), Tup((N, Fld(_, App(Var("f"), Tup((N, Fld(_, IntLit(2))) :: Nil)))) :: (N, Fld(_, IntLit(3))) :: Nil))) //│ Lifted: //│ TypingUnit { //│ fun f$1 = (x,) => {if ('(' >(x, 0,) ')') then +(x, 1,) else -(x, 1,)} @@ -217,7 +217,7 @@ fun fac(n) = if (n > 1) then fac(n - 1) * n else 1 fac(2) //│ Parsed: -//│ TypingUnit(NuFunDef(None, fac, None, [], Lam(Tup(_: Var(n)), Blk(...))), App(Var(fac), Tup(_: IntLit(2)))) +//│ TypingUnit(NuFunDef(None, fac, N, Nil, L(Lam(Tup((N, Fld(_, Var("n"))) :: Nil), Blk(If(IfThen(Bra(rcd = false, App(Var(">"), Tup((N, Fld(_, Var("n"))) :: (N, Fld(_, IntLit(1))) :: Nil))), App(Var("*"), Tup((N, Fld(_, App(Var("fac"), Tup((N, Fld(_, App(Var("-"), Tup((N, Fld(_, Var("n"))) :: (N, Fld(_, IntLit(1))) :: Nil)))) :: Nil)))) :: (N, Fld(_, Var("n"))) :: Nil)), Some(IntLit(1))))))), App(Var("fac"), Tup((N, Fld(_, IntLit(2))) :: Nil))) //│ Lifted: //│ TypingUnit { //│ fun fac$1 = (n,) => {if ('(' >(n, 1,) ')') then *(fac$1(-(n, 1,),), n,) else 1} @@ -246,7 +246,7 @@ fun count(lst) = else 0 count(new List(new List(new Nil(undefined, false), true), true)) //│ Parsed: -//│ TypingUnit(NuTypeDef(class, List, (), Tup(l: App(Var(|), Tup(_: App(Var(|), Tup(_: Var(List), _: Var(Nil))), _: UnitLit(true))), hasTail: Var(Bool)), (), None, None, TypingUnit()), NuTypeDef(class, Nil, (), Tup(l: App(Var(|), Tup(_: App(Var(|), Tup(_: Var(List), _: Var(Nil))), _: UnitLit(true))), hasTail: Var(Bool)), (), None, None, TypingUnit()), NuFunDef(None, count, None, [], Lam(Tup(_: Var(lst)), Blk(...))), App(Var(count), Tup(_: App(NuNew(Var(List)), Tup(_: App(NuNew(Var(List)), Tup(_: App(NuNew(Var(Nil)), Tup(_: UnitLit(true), _: Var(false))), _: Var(true))), _: Var(true)))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("List"), Ls(), S(Tup((S(l), Fld(_, App(Var("|"), Tup((N, Fld(_, App(Var("|"), Tup((N, Fld(_, Var("List"))) :: (N, Fld(_, Var("Nil"))) :: Nil)))) :: (N, Fld(_, UnitLit(true))) :: Nil)))) :: (S(hasTail), Fld(_, Var("Bool"))) :: Nil)), N, N, Ls(), N, N, TypingUnit()), NuTypeDef(Cls, TypeName("Nil"), Ls(), S(Tup((S(l), Fld(_, App(Var("|"), Tup((N, Fld(_, App(Var("|"), Tup((N, Fld(_, Var("List"))) :: (N, Fld(_, Var("Nil"))) :: Nil)))) :: (N, Fld(_, UnitLit(true))) :: Nil)))) :: (S(hasTail), Fld(_, Var("Bool"))) :: Nil)), N, N, Ls(), N, N, TypingUnit()), NuFunDef(None, count, N, Nil, L(Lam(Tup((N, Fld(_, Var("lst"))) :: Nil), Blk(If(IfThen(Sel(Var("lst"), hasTail), Blk(NuFunDef(Some(false), l, N, Nil, L(Sel(Var("lst"), l))), If(IfThen(App(Var("is"), Tup((N, Fld(_, Var("l"))) :: (N, Fld(_, UnitLit(true))) :: Nil)), IntLit(1), Some(App(Var("+"), Tup((N, Fld(_, App(Var("count"), Tup((N, Fld(_, Var("l"))) :: Nil)))) :: (N, Fld(_, IntLit(1))) :: Nil))))), Some(IntLit(0))))))), App(Var("count"), Tup((N, Fld(_, App(NuNew(Var("List")), Tup((N, Fld(_, App(NuNew(Var("List")), Tup((N, Fld(_, App(NuNew(Var("Nil")), Tup((N, Fld(_, UnitLit(true))) :: (N, Fld(_, Var("false"))) :: Nil)))) :: (N, Fld(_, Var("true"))) :: Nil)))) :: (N, Fld(_, Var("true"))) :: Nil)))) :: Nil))) //│ Lifted: //│ TypingUnit { //│ class List$1([val l: |(|(List, Nil,), undefined,), val hasTail: Bool,]) {} @@ -303,7 +303,7 @@ class Nil() { fun add2(x) = x+2 (new List(1, new List(2, new Nil()))).map(x => x+1).map(x => add2(x)) //│ Parsed: -//│ TypingUnit(NuTypeDef(class, List, (), Tup(e: Var(Int), tail: App(Var(|), Tup(_: Var(List), _: Var(Nil)))), (), None, None, TypingUnit(NuFunDef(None, map, None, [], PolyType(List(),Function(Tuple(List((None,Field(None,Function(Tuple(List((None,Field(None,TypeName(Int))))),TypeName(Int)))))),TypeName(List)))), NuFunDef(None, map, None, [], Lam(Tup(_: Var(f)), App(NuNew(Var(List)), Tup(_: App(Var(f), Tup(_: Var(e))), _: App(Sel(Var(tail), map), Tup(_: Var(f))))))), NuFunDef(None, count, None, [], PolyType(List(),Function(Tuple(List()),TypeName(Int)))), NuFunDef(None, count, None, [], Lam(Tup(), App(Var(+), Tup(_: IntLit(1), _: App(Sel(Var(tail), count), Tup()))))))), NuTypeDef(class, Nil, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, map, None, [], Lam(Tup(_: Var(f)), Var(this))), NuFunDef(None, count, None, [], Lam(Tup(), IntLit(0))))), NuFunDef(None, add2, None, [], Lam(Tup(_: Var(x)), App(Var(+), Tup(_: Var(x), _: IntLit(2))))), App(Sel(App(Sel(Bra(rcd = false, App(NuNew(Var(List)), Tup(_: IntLit(1), _: App(NuNew(Var(List)), Tup(_: IntLit(2), _: App(NuNew(Var(Nil)), Tup())))))), map), Tup(_: Lam(Tup(_: Var(x)), App(Var(+), Tup(_: Var(x), _: IntLit(1)))))), map), Tup(_: Lam(Tup(_: Var(x)), App(Var(add2), Tup(_: Var(x))))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("List"), Ls(), S(Tup((S(e), Fld(_, Var("Int"))) :: (S(tail), Fld(_, App(Var("|"), Tup((N, Fld(_, Var("List"))) :: (N, Fld(_, Var("Nil"))) :: Nil)))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, map, N, Nil, R(PolyType(Ls(), Function(Tuple(N -> Fld(N, Function(Tuple(N -> Fld(N, TypeName("Int"))), TypeName("Int")))), TypeName("List"))))), NuFunDef(None, map, N, Nil, L(Lam(Tup((N, Fld(_, Var("f"))) :: Nil), App(NuNew(Var("List")), Tup((N, Fld(_, App(Var("f"), Tup((N, Fld(_, Var("e"))) :: Nil)))) :: (N, Fld(_, App(Sel(Var("tail"), map), Tup((N, Fld(_, Var("f"))) :: Nil)))) :: Nil))))), NuFunDef(None, count, N, Nil, R(PolyType(Ls(), Function(Tuple(), TypeName("Int"))))), NuFunDef(None, count, N, Nil, L(Lam(Tup(Nil), App(Var("+"), Tup((N, Fld(_, IntLit(1))) :: (N, Fld(_, App(Sel(Var("tail"), count), Tup(Nil)))) :: Nil))))))), NuTypeDef(Cls, TypeName("Nil"), Ls(), S(Tup(Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, map, N, Nil, L(Lam(Tup((N, Fld(_, Var("f"))) :: Nil), Var("this")))), NuFunDef(None, count, N, Nil, L(Lam(Tup(Nil), IntLit(0)))))), NuFunDef(None, add2, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, IntLit(2))) :: Nil))))), App(Sel(App(Sel(Bra(rcd = false, App(NuNew(Var("List")), Tup((N, Fld(_, IntLit(1))) :: (N, Fld(_, App(NuNew(Var("List")), Tup((N, Fld(_, IntLit(2))) :: (N, Fld(_, App(NuNew(Var("Nil")), Tup(Nil)))) :: Nil)))) :: Nil))), map), Tup((N, Fld(_, Lam(Tup((N, Fld(_, Var("x"))) :: Nil), App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, IntLit(1))) :: Nil))))) :: Nil)), map), Tup((N, Fld(_, Lam(Tup((N, Fld(_, Var("x"))) :: Nil), App(Var("add2"), Tup((N, Fld(_, Var("x"))) :: Nil))))) :: Nil))) //│ Lifted: //│ TypingUnit { //│ class List$1([e: Int, tail: |(List, Nil,),]) { @@ -370,7 +370,7 @@ fun generate(x) = foo(new List(1, new List(2, new Nil()))) foo(generate(1)) //│ Parsed: -//│ TypingUnit(NuTypeDef(class, List, (), Tup(e: Var(Int), tail: App(Var(|), Tup(_: Var(List), _: Var(Nil)))), (), None, None, TypingUnit(NuFunDef(None, count, None, [], PolyType(List(),Function(Tuple(List()),TypeName(Int)))), NuFunDef(None, count, None, [], Lam(Tup(), App(Var(+), Tup(_: IntLit(1), _: App(Sel(Var(tail), count), Tup()))))))), NuTypeDef(class, Nil, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, count, None, [], Lam(Tup(), IntLit(0))))), NuFunDef(None, foo, None, [], Lam(Tup(_: Var(x)), App(Sel(Var(x), count), Tup()))), NuFunDef(None, generate, None, [], Lam(Tup(_: Var(x)), Blk(...))), App(Var(foo), Tup(_: App(NuNew(Var(List)), Tup(_: IntLit(1), _: App(NuNew(Var(List)), Tup(_: IntLit(2), _: App(NuNew(Var(Nil)), Tup()))))))), App(Var(foo), Tup(_: App(Var(generate), Tup(_: IntLit(1)))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("List"), Ls(), S(Tup((S(e), Fld(_, Var("Int"))) :: (S(tail), Fld(_, App(Var("|"), Tup((N, Fld(_, Var("List"))) :: (N, Fld(_, Var("Nil"))) :: Nil)))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, count, N, Nil, R(PolyType(Ls(), Function(Tuple(), TypeName("Int"))))), NuFunDef(None, count, N, Nil, L(Lam(Tup(Nil), App(Var("+"), Tup((N, Fld(_, IntLit(1))) :: (N, Fld(_, App(Sel(Var("tail"), count), Tup(Nil)))) :: Nil))))))), NuTypeDef(Cls, TypeName("Nil"), Ls(), S(Tup(Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, count, N, Nil, L(Lam(Tup(Nil), IntLit(0)))))), NuFunDef(None, foo, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), App(Sel(Var("x"), count), Tup(Nil))))), NuFunDef(None, generate, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Blk(If(IfThen(App(Var(">"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, IntLit(0))) :: Nil)), App(NuNew(Var("List")), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, App(Var("generate"), Tup((N, Fld(_, App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, IntLit(1))) :: Nil)))) :: Nil)))) :: Nil)), Some(App(NuNew(Var("Nil")), Tup(Nil)))))))), App(Var("foo"), Tup((N, Fld(_, App(NuNew(Var("List")), Tup((N, Fld(_, IntLit(1))) :: (N, Fld(_, App(NuNew(Var("List")), Tup((N, Fld(_, IntLit(2))) :: (N, Fld(_, App(NuNew(Var("Nil")), Tup(Nil)))) :: Nil)))) :: Nil)))) :: Nil)), App(Var("foo"), Tup((N, Fld(_, App(Var("generate"), Tup((N, Fld(_, IntLit(1))) :: Nil)))) :: Nil))) //│ Lifted: //│ TypingUnit { //│ class List$1([e: Int, tail: |(List, Nil,),]) { @@ -424,7 +424,7 @@ fun foo(x) = (f => f(x))(z => z+1) foo(2) //│ Parsed: -//│ TypingUnit(NuFunDef(None, foo, None, [], Lam(Tup(_: Var(x)), Blk(...))), App(Var(foo), Tup(_: IntLit(2)))) +//│ TypingUnit(NuFunDef(None, foo, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Blk(App(Bra(rcd = false, Lam(Tup((N, Fld(_, Var("f"))) :: Nil), App(Var("f"), Tup((N, Fld(_, Var("x"))) :: Nil)))), Tup((N, Fld(_, Lam(Tup((N, Fld(_, Var("z"))) :: Nil), App(Var("+"), Tup((N, Fld(_, Var("z"))) :: (N, Fld(_, IntLit(1))) :: Nil))))) :: Nil)))))), App(Var("foo"), Tup((N, Fld(_, IntLit(2))) :: Nil))) //│ Lifted: //│ TypingUnit { //│ class Lambda1$2$1([x,]) {fun apply = (f,) => f((this).x,)} @@ -458,7 +458,7 @@ fun f(x) = (y => f(x+y))(x+1) f(1) //│ Parsed: -//│ TypingUnit(NuFunDef(None, f, None, [], Lam(Tup(_: Var(x)), Blk(...))), App(Var(f), Tup(_: IntLit(1)))) +//│ TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Blk(App(Bra(rcd = false, Lam(Tup((N, Fld(_, Var("y"))) :: Nil), App(Var("f"), Tup((N, Fld(_, App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("y"))) :: Nil)))) :: Nil)))), Tup((N, Fld(_, App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, IntLit(1))) :: Nil)))) :: Nil)))))), App(Var("f"), Tup((N, Fld(_, IntLit(1))) :: Nil))) //│ Lifted: //│ TypingUnit { //│ class Lambda1$2$1([x,]) {fun apply = (y,) => f$1(+((this).x, y,),)} @@ -489,7 +489,7 @@ fun f(x) = f(x) f(0) f(1) //│ Parsed: -//│ TypingUnit(NuFunDef(None, f, None, [], Lam(Tup(_: Var(x)), App(Var(f), Tup(_: Var(x))))), App(Var(f), Tup(_: IntLit(0))), App(Var(f), Tup(_: IntLit(1)))) +//│ TypingUnit(NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), App(Var("f"), Tup((N, Fld(_, Var("x"))) :: Nil))))), App(Var("f"), Tup((N, Fld(_, IntLit(0))) :: Nil)), App(Var("f"), Tup((N, Fld(_, IntLit(1))) :: Nil))) //│ Lifted: //│ TypingUnit { //│ fun f$1 = (x,) => f$1(x,) @@ -537,7 +537,7 @@ fun foo(x) = foo(new Lambda()) foo(new Lambda2(2)) //│ Parsed: -//│ TypingUnit(NuTypeDef(class, Cons, (), Tup(e: Var('A), tail: App(Var(|), Tup(_: Var(Cons), _: Var(Nil)))), (), None, None, TypingUnit(NuFunDef(None, count, None, [], PolyType(List(),Function(Tuple(List()),TypeName(Int)))), NuFunDef(None, count, None, [], Lam(Tup(), App(Var(+), Tup(_: IntLit(1), _: App(Sel(Var(tail), count), Tup()))))))), NuTypeDef(class, Nil, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, count, None, [], Lam(Tup(), IntLit(0))))), NuTypeDef(class, Lambda, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, apply, None, [], Lam(Tup(_: Var(l)), Blk(...))))), NuTypeDef(class, Lambda2, (), Tup(a: Var(Int)), (), None, None, TypingUnit(NuFunDef(None, apply, None, [], Lam(Tup(_: Var(l)), Blk(...))))), NuFunDef(None, foo, None, [], Lam(Tup(_: Var(x)), Blk(...))), App(Var(foo), Tup(_: App(NuNew(Var(Lambda)), Tup()))), App(Var(foo), Tup(_: App(NuNew(Var(Lambda2)), Tup(_: IntLit(2)))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("Cons"), Ls(), S(Tup((S(e), Fld(_, Var("'A"))) :: (S(tail), Fld(_, App(Var("|"), Tup((N, Fld(_, Var("Cons"))) :: (N, Fld(_, Var("Nil"))) :: Nil)))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, count, N, Nil, R(PolyType(Ls(), Function(Tuple(), TypeName("Int"))))), NuFunDef(None, count, N, Nil, L(Lam(Tup(Nil), App(Var("+"), Tup((N, Fld(_, IntLit(1))) :: (N, Fld(_, App(Sel(Var("tail"), count), Tup(Nil)))) :: Nil))))))), NuTypeDef(Cls, TypeName("Nil"), Ls(), S(Tup(Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, count, N, Nil, L(Lam(Tup(Nil), IntLit(0)))))), NuTypeDef(Cls, TypeName("Lambda"), Ls(), S(Tup(Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, apply, N, Nil, L(Lam(Tup((N, Fld(_, Var("l"))) :: Nil), Blk(App(Sel(Var("l"), count), Tup(Nil)))))))), NuTypeDef(Cls, TypeName("Lambda2"), Ls(), S(Tup((S(a), Fld(_, Var("Int"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, apply, N, Nil, L(Lam(Tup((N, Fld(_, Var("l"))) :: Nil), Blk(App(Sel(Bra(rcd = false, App(NuNew(Var("Cons")), Tup((N, Fld(_, Var("a"))) :: (N, Fld(_, Var("l"))) :: Nil))), count), Tup(Nil)))))))), NuFunDef(None, foo, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Blk(App(Var("+"), Tup((N, Fld(_, App(Sel(Var("x"), apply), Tup((N, Fld(_, App(NuNew(Var("Cons")), Tup((N, Fld(_, IntLit(1))) :: (N, Fld(_, App(NuNew(Var("Nil")), Tup(Nil)))) :: Nil)))) :: Nil)))) :: (N, Fld(_, App(Sel(Var("x"), apply), Tup((N, Fld(_, App(NuNew(Var("Nil")), Tup(Nil)))) :: Nil)))) :: Nil)))))), App(Var("foo"), Tup((N, Fld(_, App(NuNew(Var("Lambda")), Tup(Nil)))) :: Nil)), App(Var("foo"), Tup((N, Fld(_, App(NuNew(Var("Lambda2")), Tup((N, Fld(_, IntLit(2))) :: Nil)))) :: Nil))) //│ Lifted: //│ TypingUnit { //│ class Cons$1([e: 'A, tail: |(Cons, Nil,),]) { @@ -612,7 +612,7 @@ fun foo(x) = foo(l => l.count()) foo(l => (new Cons(2, l)).count()) //│ Parsed: -//│ TypingUnit(NuTypeDef(class, Cons, (), Tup(e: Var(Int), tail: App(Var(|), Tup(_: Var(Cons), _: Var(Nil)))), (), None, None, TypingUnit(NuFunDef(None, count, None, [], PolyType(List(),Function(Tuple(List()),TypeName(Int)))), NuFunDef(None, count, None, [], Lam(Tup(), App(Var(+), Tup(_: IntLit(1), _: App(Sel(Var(tail), count), Tup()))))))), NuTypeDef(class, Nil, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, count, None, [], Lam(Tup(), IntLit(0))))), NuFunDef(None, foo, None, [], Lam(Tup(_: Var(x)), Blk(...))), App(Var(foo), Tup(_: Lam(Tup(_: Var(l)), App(Sel(Var(l), count), Tup())))), App(Var(foo), Tup(_: Lam(Tup(_: Var(l)), App(Sel(Bra(rcd = false, App(NuNew(Var(Cons)), Tup(_: IntLit(2), _: Var(l)))), count), Tup()))))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("Cons"), Ls(), S(Tup((S(e), Fld(_, Var("Int"))) :: (S(tail), Fld(_, App(Var("|"), Tup((N, Fld(_, Var("Cons"))) :: (N, Fld(_, Var("Nil"))) :: Nil)))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, count, N, Nil, R(PolyType(Ls(), Function(Tuple(), TypeName("Int"))))), NuFunDef(None, count, N, Nil, L(Lam(Tup(Nil), App(Var("+"), Tup((N, Fld(_, IntLit(1))) :: (N, Fld(_, App(Sel(Var("tail"), count), Tup(Nil)))) :: Nil))))))), NuTypeDef(Cls, TypeName("Nil"), Ls(), S(Tup(Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, count, N, Nil, L(Lam(Tup(Nil), IntLit(0)))))), NuFunDef(None, foo, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Blk(App(Var("+"), Tup((N, Fld(_, App(Var("x"), Tup((N, Fld(_, App(NuNew(Var("Cons")), Tup((N, Fld(_, IntLit(1))) :: (N, Fld(_, App(NuNew(Var("Nil")), Tup(Nil)))) :: Nil)))) :: Nil)))) :: (N, Fld(_, App(Var("x"), Tup((N, Fld(_, App(NuNew(Var("Nil")), Tup(Nil)))) :: Nil)))) :: Nil)))))), App(Var("foo"), Tup((N, Fld(_, Lam(Tup((N, Fld(_, Var("l"))) :: Nil), App(Sel(Var("l"), count), Tup(Nil))))) :: Nil)), App(Var("foo"), Tup((N, Fld(_, Lam(Tup((N, Fld(_, Var("l"))) :: Nil), App(Sel(Bra(rcd = false, App(NuNew(Var("Cons")), Tup((N, Fld(_, IntLit(2))) :: (N, Fld(_, Var("l"))) :: Nil))), count), Tup(Nil))))) :: Nil))) //│ Lifted: //│ TypingUnit { //│ class Cons$1([e: Int, tail: |(Cons, Nil,),]) { @@ -707,7 +707,7 @@ class C(e1: Exp, e2: Exp) extends Exp { } (new C(new Ch(1), new A(new Ch(2), new Ch(3)))).derive(0).isEmpty() //│ Parsed: -//│ TypingUnit(NuTypeDef(class, Exp, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, derive, None, [], PolyType(List(),Function(Tuple(List((Some(x),Field(None,TypeName(Int))))),TypeName(Exp)))), NuFunDef(None, derive, None, [], Lam(Tup(x: Var(Int)), App(Var(Exp), Tup()))), NuFunDef(None, isEmpty, None, [], PolyType(List(),Function(Tuple(List()),TypeName(Bool)))), NuFunDef(None, isEmpty, None, [], Lam(Tup(), Var(false))))), NuTypeDef(class, E, (), Tup(), (Var(Exp)), None, None, TypingUnit(NuFunDef(None, derive, None, [], Lam(Tup(_: Var(x)), Blk(...))), NuFunDef(None, isEmpty, None, [], Lam(Tup(), Blk(...))))), NuTypeDef(class, Ep, (), Tup(), (Var(Exp)), None, None, TypingUnit(NuFunDef(None, derive, None, [], Lam(Tup(_: Var(x)), Blk(...))), NuFunDef(None, isEmpty, None, [], Lam(Tup(), Blk(...))))), NuTypeDef(class, Ch, (), Tup(i: Var(Int)), (Var(Exp)), None, None, TypingUnit(NuFunDef(None, derive, None, [], Lam(Tup(_: Var(x)), Blk(...))), NuFunDef(None, isEmpty, None, [], Lam(Tup(), Blk(...))))), NuTypeDef(class, A, (), Tup(e1: Var(Exp), e2: Var(Exp)), (Var(Exp)), None, None, TypingUnit(NuFunDef(None, derive, None, [], Lam(Tup(_: Var(x)), Blk(...))), NuFunDef(None, isEmpty, None, [], Lam(Tup(), Blk(...))))), NuTypeDef(class, C, (), Tup(e1: Var(Exp), e2: Var(Exp)), (Var(Exp)), None, None, TypingUnit(NuFunDef(None, derive, None, [], Lam(Tup(_: Var(x)), Blk(...))), NuFunDef(None, isEmpty, None, [], Lam(Tup(), Blk(...))))), App(Sel(App(Sel(Bra(rcd = false, App(NuNew(Var(C)), Tup(_: App(NuNew(Var(Ch)), Tup(_: IntLit(1))), _: App(NuNew(Var(A)), Tup(_: App(NuNew(Var(Ch)), Tup(_: IntLit(2))), _: App(NuNew(Var(Ch)), Tup(_: IntLit(3)))))))), derive), Tup(_: IntLit(0))), isEmpty), Tup())) +//│ TypingUnit(NuTypeDef(Cls, TypeName("Exp"), Ls(), S(Tup(Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, derive, N, Nil, R(PolyType(Ls(), Function(Tuple(S(x) -> Fld(N, TypeName("Int"))), TypeName("Exp"))))), NuFunDef(None, derive, N, Nil, L(Lam(Tup((S(x), Fld(_, Var("Int"))) :: Nil), App(Var("Exp"), Tup(Nil))))), NuFunDef(None, isEmpty, N, Nil, R(PolyType(Ls(), Function(Tuple(), TypeName("Bool"))))), NuFunDef(None, isEmpty, N, Nil, L(Lam(Tup(Nil), Var("false")))))), NuTypeDef(Cls, TypeName("E"), Ls(), S(Tup(Nil)), N, N, Ls(Var("Exp")), N, N, TypingUnit(NuFunDef(None, derive, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Blk(NuNew(Var("E")))))), NuFunDef(None, isEmpty, N, Nil, L(Lam(Tup(Nil), Blk(Var("false"))))))), NuTypeDef(Cls, TypeName("Ep"), Ls(), S(Tup(Nil)), N, N, Ls(Var("Exp")), N, N, TypingUnit(NuFunDef(None, derive, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Blk(NuNew(Var("E")))))), NuFunDef(None, isEmpty, N, Nil, L(Lam(Tup(Nil), Blk(Var("true"))))))), NuTypeDef(Cls, TypeName("Ch"), Ls(), S(Tup((S(i), Fld(_, Var("Int"))) :: Nil)), N, N, Ls(Var("Exp")), N, N, TypingUnit(NuFunDef(None, derive, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Blk(If(IfThen(App(Var("=="), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("i"))) :: Nil)), NuNew(Var("Ep")), Some(NuNew(Var("E")))))))), NuFunDef(None, isEmpty, N, Nil, L(Lam(Tup(Nil), Blk(Var("false"))))))), NuTypeDef(Cls, TypeName("A"), Ls(), S(Tup((S(e1), Fld(_, Var("Exp"))) :: (S(e2), Fld(_, Var("Exp"))) :: Nil)), N, N, Ls(Var("Exp")), N, N, TypingUnit(NuFunDef(None, derive, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Blk(App(NuNew(Var("A")), Tup((N, Fld(_, App(Sel(Var("e1"), derive), Tup((N, Fld(_, Var("x"))) :: Nil)))) :: (N, Fld(_, App(Sel(Var("e2"), derive), Tup((N, Fld(_, Var("x"))) :: Nil)))) :: Nil)))))), NuFunDef(None, isEmpty, N, Nil, L(Lam(Tup(Nil), Blk(App(Var("||"), Tup((N, Fld(_, App(Sel(Var("e1"), isEmpty), Tup(Nil)))) :: (N, Fld(_, App(Sel(Var("e2"), isEmpty), Tup(Nil)))) :: Nil)))))))), NuTypeDef(Cls, TypeName("C"), Ls(), S(Tup((S(e1), Fld(_, Var("Exp"))) :: (S(e2), Fld(_, Var("Exp"))) :: Nil)), N, N, Ls(Var("Exp")), N, N, TypingUnit(NuFunDef(None, derive, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Blk(If(IfThen(App(Sel(Var("e1"), isEmpty), Tup(Nil)), App(NuNew(Var("A")), Tup((N, Fld(_, App(NuNew(Var("C")), Tup((N, Fld(_, App(Sel(Var("e1"), derive), Tup((N, Fld(_, Var("x"))) :: Nil)))) :: (N, Fld(_, Var("e2"))) :: Nil)))) :: (N, Fld(_, App(Sel(Var("e2"), derive), Tup((N, Fld(_, Var("x"))) :: Nil)))) :: Nil)), Some(App(NuNew(Var("C")), Tup((N, Fld(_, App(Sel(Var("e1"), derive), Tup((N, Fld(_, Var("x"))) :: Nil)))) :: (N, Fld(_, Var("e2"))) :: Nil)))))))), NuFunDef(None, isEmpty, N, Nil, L(Lam(Tup(Nil), Blk(App(Var("&&"), Tup((N, Fld(_, App(Sel(Var("e1"), isEmpty), Tup(Nil)))) :: (N, Fld(_, App(Sel(Var("e2"), isEmpty), Tup(Nil)))) :: Nil)))))))), App(Sel(App(Sel(Bra(rcd = false, App(NuNew(Var("C")), Tup((N, Fld(_, App(NuNew(Var("Ch")), Tup((N, Fld(_, IntLit(1))) :: Nil)))) :: (N, Fld(_, App(NuNew(Var("A")), Tup((N, Fld(_, App(NuNew(Var("Ch")), Tup((N, Fld(_, IntLit(2))) :: Nil)))) :: (N, Fld(_, App(NuNew(Var("Ch")), Tup((N, Fld(_, IntLit(3))) :: Nil)))) :: Nil)))) :: Nil))), derive), Tup((N, Fld(_, IntLit(0))) :: Nil)), isEmpty), Tup(Nil))) //│ Lifted: //│ TypingUnit { //│ class Exp$1([]) { @@ -809,7 +809,7 @@ fun gen() = if anyUnknown then new List(gen(), true) else new Nil(false) gen() //│ Parsed: -//│ TypingUnit(NuFunDef(Some(false), anyUnknown, None, [], Var(false)), NuTypeDef(class, List, (), Tup(l: App(Var(|), Tup(_: Var(List), _: Var(Nil))), hasTail: Var(Bool)), (), None, None, TypingUnit()), NuTypeDef(class, Nil, (), Tup(hasTail: Var(Bool)), (), None, None, TypingUnit()), NuFunDef(None, gen, None, [], Lam(Tup(), Blk(...))), App(Var(gen), Tup())) +//│ TypingUnit(NuFunDef(Some(false), anyUnknown, N, Nil, L(Var("false"))), NuTypeDef(Cls, TypeName("List"), Ls(), S(Tup((S(l), Fld(_, App(Var("|"), Tup((N, Fld(_, Var("List"))) :: (N, Fld(_, Var("Nil"))) :: Nil)))) :: (S(hasTail), Fld(_, Var("Bool"))) :: Nil)), N, N, Ls(), N, N, TypingUnit()), NuTypeDef(Cls, TypeName("Nil"), Ls(), S(Tup((S(hasTail), Fld(_, Var("Bool"))) :: Nil)), N, N, Ls(), N, N, TypingUnit()), NuFunDef(None, gen, N, Nil, L(Lam(Tup(Nil), Blk(If(IfThen(Var("anyUnknown"), App(NuNew(Var("List")), Tup((N, Fld(_, App(Var("gen"), Tup(Nil)))) :: (N, Fld(_, Var("true"))) :: Nil)), Some(App(NuNew(Var("Nil")), Tup((N, Fld(_, Var("false"))) :: Nil)))))))), App(Var("gen"), Tup(Nil))) //│ Lifted: //│ TypingUnit { //│ class List$1([l: |(List, Nil,), hasTail: Bool,]) {} @@ -851,7 +851,7 @@ class Foo(x: Int){ } (new Foo(1)).boo(2) //│ Parsed: -//│ TypingUnit(NuTypeDef(class, Foo, (), Tup(x: Var(Int)), (), None, None, TypingUnit(NuFunDef(None, bar, None, [], Lam(Tup(_: Var(y)), App(Var(+), Tup(_: Var(x), _: Var(y))))), NuFunDef(None, boo, None, [], Lam(Tup(_: Var(z)), App(Var(+), Tup(_: App(Var(bar), Tup(_: Var(z))), _: Var(x))))))), App(Sel(Bra(rcd = false, App(NuNew(Var(Foo)), Tup(_: IntLit(1)))), boo), Tup(_: IntLit(2)))) +//│ TypingUnit(NuTypeDef(Cls, TypeName("Foo"), Ls(), S(Tup((S(x), Fld(_, Var("Int"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, bar, N, Nil, L(Lam(Tup((N, Fld(_, Var("y"))) :: Nil), App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("y"))) :: Nil))))), NuFunDef(None, boo, N, Nil, L(Lam(Tup((N, Fld(_, Var("z"))) :: Nil), App(Var("+"), Tup((N, Fld(_, App(Var("bar"), Tup((N, Fld(_, Var("z"))) :: Nil)))) :: (N, Fld(_, Var("x"))) :: Nil))))))), App(Sel(Bra(rcd = false, App(NuNew(Var("Foo")), Tup((N, Fld(_, IntLit(1))) :: Nil))), boo), Tup((N, Fld(_, IntLit(2))) :: Nil))) //│ Lifted: //│ TypingUnit { //│ class Foo$1([x: Int,]) { @@ -888,7 +888,7 @@ class OneInt(a: Int){ } (new OneInt(10)).fac() //│ Parsed: -//│ TypingUnit(NuTypeDef(class, OneInt, (), Tup(a: Var(Int)), (), None, None, TypingUnit(NuFunDef(None, fac, None, [], PolyType(List(),Function(Tuple(List()),TypeName(Int)))), NuFunDef(None, fac, None, [], Lam(Tup(), Blk(...))))), App(Sel(Bra(rcd = false, App(NuNew(Var(OneInt)), Tup(_: IntLit(10)))), fac), Tup())) +//│ TypingUnit(NuTypeDef(Cls, TypeName("OneInt"), Ls(), S(Tup((S(a), Fld(_, Var("Int"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, fac, N, Nil, R(PolyType(Ls(), Function(Tuple(), TypeName("Int"))))), NuFunDef(None, fac, N, Nil, L(Lam(Tup(Nil), Blk(If(IfThen(Bra(rcd = false, App(Var(">"), Tup((N, Fld(_, Var("a"))) :: (N, Fld(_, IntLit(0))) :: Nil))), App(Sel(Bra(rcd = false, App(NuNew(Var("OneInt")), Tup((N, Fld(_, App(Var("-"), Tup((N, Fld(_, Var("a"))) :: (N, Fld(_, IntLit(1))) :: Nil)))) :: Nil))), fac), Tup(Nil)), Some(IntLit(1))))))))), App(Sel(Bra(rcd = false, App(NuNew(Var("OneInt")), Tup((N, Fld(_, IntLit(10))) :: Nil))), fac), Tup(Nil))) //│ Lifted: //│ TypingUnit { //│ class OneInt$1([a: Int,]) { @@ -938,7 +938,7 @@ fun g(x) = else f(x - 2) g(1) //│ Parsed: -//│ TypingUnit(NuFunDef(Some(false), any, None, [], IntLit(-20)), NuFunDef(None, f, None, [], Lam(Tup(_: Var(x)), Blk(...))), NuFunDef(None, g, None, [], Lam(Tup(_: Var(x)), Blk(...))), App(Var(g), Tup(_: IntLit(1)))) +//│ TypingUnit(NuFunDef(Some(false), any, N, Nil, L(IntLit(-20))), NuFunDef(None, f, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Blk(If(IfThen(App(Var(">"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("any"))) :: Nil)), IntLit(0), Some(App(Var("g"), Tup((N, Fld(_, App(Var("-"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, IntLit(1))) :: Nil)))) :: Nil)))))))), NuFunDef(None, g, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Blk(If(IfThen(App(Var(">"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("any"))) :: Nil)), App(Var("g"), Tup((N, Fld(_, App(Var("-"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, IntLit(1))) :: Nil)))) :: Nil)), Some(App(Var("f"), Tup((N, Fld(_, App(Var("-"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, IntLit(2))) :: Nil)))) :: Nil)))))))), App(Var("g"), Tup((N, Fld(_, IntLit(1))) :: Nil))) //│ Lifted: //│ TypingUnit { //│ let any$3 = () => -20 @@ -977,7 +977,7 @@ class OneBool(b: Bool){ } (if b then new OneInt(1) else new OneBool(true)).get() //│ Parsed: -//│ TypingUnit(NuTypeDef(class, OneInt, (), Tup(a: Var(Int)), (), None, None, TypingUnit(NuFunDef(None, get, None, [], Lam(Tup(), Var(a))))), NuTypeDef(class, OneBool, (), Tup(b: Var(Bool)), (), None, None, TypingUnit(NuFunDef(None, get, None, [], Lam(Tup(), Var(b))))), App(Sel(Bra(rcd = false, If(IfThen(Var(b), App(NuNew(Var(OneInt)), Tup(_: IntLit(1))), Some(App(NuNew(Var(OneBool)), Tup(_: Var(true)))))), get), Tup())) +//│ TypingUnit(NuTypeDef(Cls, TypeName("OneInt"), Ls(), S(Tup((S(a), Fld(_, Var("Int"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, get, N, Nil, L(Lam(Tup(Nil), Var("a")))))), NuTypeDef(Cls, TypeName("OneBool"), Ls(), S(Tup((S(b), Fld(_, Var("Bool"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, get, N, Nil, L(Lam(Tup(Nil), Var("b")))))), App(Sel(Bra(rcd = false, If(IfThen(Var("b"), App(NuNew(Var("OneInt")), Tup((N, Fld(_, IntLit(1))) :: Nil)), Some(App(NuNew(Var("OneBool")), Tup((N, Fld(_, Var("true"))) :: Nil))))), get), Tup(Nil))) //│ Lifted: //│ TypingUnit { //│ class OneInt$1([a: Int,]) {fun get = () => (this).a} @@ -1023,7 +1023,7 @@ baz(bar) (new Car()).da(Bar(1337)) bar.car //│ Parsed: -//│ TypingUnit(NuTypeDef(class, Bar, (), Tup(x: Var(Int)), (), None, None, TypingUnit(NuFunDef(None, foo, None, [], Lam(Tup(_: Var(x)), Var(x))), NuFunDef(None, FooMinus, None, [], Lam(Tup(y: Var(Int)), App(Var(+), Tup(_: Var(x), _: Var(y))))), NuFunDef(None, car, None, [], App(Var(foo), Tup(_: IntLit(2)))))), NuTypeDef(class, Car, (), Tup(), (), None, None, TypingUnit(NuFunDef(None, da, None, [], Lam(Tup(b: Var(Bar)), App(Sel(Var(b), foo), Tup(_: IntLit(2))))))), NuFunDef(None, baz, None, [], Lam(Tup(b: Var(Bar)), App(Sel(Var(b), foo), Tup(_: IntLit(2))))), NuFunDef(Some(false), bar, None, [], App(Var(Bar), Tup(_: IntLit(42)))), App(Var(baz), Tup(_: Var(bar))), App(Sel(Bra(rcd = false, App(NuNew(Var(Car)), Tup())), da), Tup(_: App(Var(Bar), Tup(_: IntLit(1337))))), Sel(Var(bar), car)) +//│ TypingUnit(NuTypeDef(Cls, TypeName("Bar"), Ls(), S(Tup((S(x), Fld(_, Var("Int"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, foo, N, Nil, L(Lam(Tup((N, Fld(_, Var("x"))) :: Nil), Var("x")))), NuFunDef(None, FooMinus, N, Nil, L(Lam(Tup((S(y), Fld(_, Var("Int"))) :: Nil), App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, Var("y"))) :: Nil))))), NuFunDef(None, car, N, Nil, L(App(Var("foo"), Tup((N, Fld(_, IntLit(2))) :: Nil)))))), NuTypeDef(Cls, TypeName("Car"), Ls(), N, N, N, Ls(), N, N, TypingUnit(NuFunDef(None, da, N, Nil, L(Lam(Tup((S(b), Fld(_, Var("Bar"))) :: Nil), App(Sel(Var("b"), foo), Tup((N, Fld(_, IntLit(2))) :: Nil))))))), NuFunDef(None, baz, N, Nil, L(Lam(Tup((S(b), Fld(_, Var("Bar"))) :: Nil), App(Sel(Var("b"), foo), Tup((N, Fld(_, IntLit(2))) :: Nil))))), NuFunDef(Some(false), bar, N, Nil, L(App(Var("Bar"), Tup((N, Fld(_, IntLit(42))) :: Nil)))), App(Var("baz"), Tup((N, Fld(_, Var("bar"))) :: Nil)), App(Sel(Bra(rcd = false, App(NuNew(Var("Car")), Tup(Nil))), da), Tup((N, Fld(_, App(Var("Bar"), Tup((N, Fld(_, IntLit(1337))) :: Nil)))) :: Nil)), Sel(Var("bar"), car)) //│ Lifted: //│ TypingUnit { //│ class Bar$1([x: Int,]) { @@ -1094,7 +1094,7 @@ class Sub2(c: Int) extends Sub(c+c){ (new Sub(10)).foo() (new Sub2(c)).foo() //│ Parsed: -//│ TypingUnit(NuFunDef(Some(false), c, None, [], IntLit(5)), NuTypeDef(class, Sup, (), Tup(a: Var(Int)), (), None, None, TypingUnit(NuFunDef(None, foo, None, [], Lam(Tup(), Var(a))))), NuTypeDef(class, Sub, (), Tup(b: Var(Int)), (App(Var(Sup), Tup(_: App(Var(+), Tup(_: Var(b), _: Var(b)))))), None, None, TypingUnit()), NuTypeDef(class, Sub2, (), Tup(c: Var(Int)), (App(Var(Sub), Tup(_: App(Var(+), Tup(_: Var(c), _: Var(c)))))), None, None, TypingUnit(NuFunDef(None, foo, None, [], Lam(Tup(), App(Var(+), Tup(_: Var(a), _: Var(c))))))), App(Sel(Bra(rcd = false, App(NuNew(Var(Sub)), Tup(_: IntLit(10)))), foo), Tup()), App(Sel(Bra(rcd = false, App(NuNew(Var(Sub2)), Tup(_: Var(c)))), foo), Tup())) +//│ TypingUnit(NuFunDef(Some(false), c, N, Nil, L(IntLit(5))), NuTypeDef(Cls, TypeName("Sup"), Ls(), S(Tup((S(a), Fld(_, Var("Int"))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, foo, N, Nil, L(Lam(Tup(Nil), Var("a")))))), NuTypeDef(Cls, TypeName("Sub"), Ls(), S(Tup((S(b), Fld(_, Var("Int"))) :: Nil)), N, N, Ls(App(Var("Sup"), Tup((N, Fld(_, App(Var("+"), Tup((N, Fld(_, Var("b"))) :: (N, Fld(_, Var("b"))) :: Nil)))) :: Nil))), N, N, TypingUnit()), NuTypeDef(Cls, TypeName("Sub2"), Ls(), S(Tup((S(c), Fld(_, Var("Int"))) :: Nil)), N, N, Ls(App(Var("Sub"), Tup((N, Fld(_, App(Var("+"), Tup((N, Fld(_, Var("c"))) :: (N, Fld(_, Var("c"))) :: Nil)))) :: Nil))), N, N, TypingUnit(NuFunDef(None, foo, N, Nil, L(Lam(Tup(Nil), App(Var("+"), Tup((N, Fld(_, Var("a"))) :: (N, Fld(_, Var("c"))) :: Nil))))))), App(Sel(Bra(rcd = false, App(NuNew(Var("Sub")), Tup((N, Fld(_, IntLit(10))) :: Nil))), foo), Tup(Nil)), App(Sel(Bra(rcd = false, App(NuNew(Var("Sub2")), Tup((N, Fld(_, Var("c"))) :: Nil))), foo), Tup(Nil))) //│ Lifted: //│ TypingUnit { //│ class Sup$1([a: Int,]) {fun foo = () => (this).a} @@ -1152,7 +1152,7 @@ class F2() extends Foo(x => x+2){} (new F1()).foo() (new F2()).foo() //│ Parsed: -//│ TypingUnit(NuTypeDef(class, Foo, (), Tup(f: App(Var(->), Tup(_: Var(Int), _: Var(Int)))), (), None, None, TypingUnit(NuFunDef(None, foo, None, [], Lam(Tup(), App(Var(f), Tup(_: IntLit(1))))))), NuTypeDef(class, F1, (), Tup(), (App(Var(Foo), Tup(_: Lam(Tup(_: Var(x)), App(Var(+), Tup(_: Var(x), _: IntLit(1))))))), None, None, TypingUnit()), NuTypeDef(class, F2, (), Tup(), (App(Var(Foo), Tup(_: Lam(Tup(_: Var(x)), App(Var(+), Tup(_: Var(x), _: IntLit(2))))))), None, None, TypingUnit()), App(Sel(Bra(rcd = false, App(NuNew(Var(F1)), Tup())), foo), Tup()), App(Sel(Bra(rcd = false, App(NuNew(Var(F2)), Tup())), foo), Tup())) +//│ TypingUnit(NuTypeDef(Cls, TypeName("Foo"), Ls(), S(Tup((S(f), Fld(_, App(Var("->"), Tup((N, Fld(_, Var("Int"))) :: (N, Fld(_, Var("Int"))) :: Nil)))) :: Nil)), N, N, Ls(), N, N, TypingUnit(NuFunDef(None, foo, N, Nil, L(Lam(Tup(Nil), App(Var("f"), Tup((N, Fld(_, IntLit(1))) :: Nil))))))), NuTypeDef(Cls, TypeName("F1"), Ls(), S(Tup(Nil)), N, N, Ls(App(Var("Foo"), Tup((N, Fld(_, Lam(Tup((N, Fld(_, Var("x"))) :: Nil), App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, IntLit(1))) :: Nil))))) :: Nil))), N, N, TypingUnit()), NuTypeDef(Cls, TypeName("F2"), Ls(), S(Tup(Nil)), N, N, Ls(App(Var("Foo"), Tup((N, Fld(_, Lam(Tup((N, Fld(_, Var("x"))) :: Nil), App(Var("+"), Tup((N, Fld(_, Var("x"))) :: (N, Fld(_, IntLit(2))) :: Nil))))) :: Nil))), N, N, TypingUnit()), App(Sel(Bra(rcd = false, App(NuNew(Var("F1")), Tup(Nil))), foo), Tup(Nil)), App(Sel(Bra(rcd = false, App(NuNew(Var("F2")), Tup(Nil))), foo), Tup(Nil))) //│ Lifted: //│ TypingUnit { //│ class Foo$1([f: ->(Int, Int,),]) {fun foo = () => (this).f(1,)} From abc352f9f38aabcc8e91a698e18a18ec0ec5b6ad Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sat, 23 Dec 2023 13:23:50 +0800 Subject: [PATCH 021/143] Fix missing let bindings when merging branches during specialization and clean up some code... --- .../scala/mlscript/pretyper/PreTyper.scala | 3 +- .../main/scala/mlscript/pretyper/Symbol.scala | 6 - .../mlscript/ucs/stages/Normalization.scala | 124 ++++++++---------- .../scala/mlscript/ucs/stages/package.scala | 13 +- .../pretyper/ucs/SpecilizationCollision.mls | 50 +++++++ 5 files changed, 115 insertions(+), 81 deletions(-) create mode 100644 shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index a0311fbe..55b1e772 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -266,7 +266,8 @@ object PreTyper { case Nil => results.reverse case (nme: Var) :: tail => rec(nme :: results, tail) case (TyApp(ty, _)) :: tail => rec(results, ty :: tail) - case other :: _ => ??? + case (App(nme @ Var(_), Tup(_))) :: tail => rec(nme :: results, tail) + case other :: _ => println(s"Unknown parent type: ${inspect.deep(other)}"); ??? } rec(Nil, parents) } diff --git a/shared/src/main/scala/mlscript/pretyper/Symbol.scala b/shared/src/main/scala/mlscript/pretyper/Symbol.scala index 44d86efb..1630f721 100644 --- a/shared/src/main/scala/mlscript/pretyper/Symbol.scala +++ b/shared/src/main/scala/mlscript/pretyper/Symbol.scala @@ -110,12 +110,6 @@ package object symbol { val synthesizedName = s"${name}$$tuple${index.toString}" new SubValueSymbol(this, N -> S(index), synthesizedName, loc) }) - - /** - * This buffer contains alias variables which created by "let" bindings or - * alias patterns. - */ - val aliases: Buffer[Var] = Buffer.empty } final class ValueSymbol(val nme: Var, val hoisted: Bool) extends ScrutineeSymbol(nme.name) { diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 046d2e2c..def15d0b 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -33,14 +33,14 @@ trait Normalization { self: mlscript.pretyper.Traceable => val falseBranch = normalizeToCaseBranches(tail) CaseOf(test, Case(nme, trueBranch, falseBranch)) case Split.Cons(Branch(scrutinee, pattern @ Pattern.Literal(literal), continuation), tail) => - val trueBranch = normalizeToTerm(specialize(continuation ++ tail, Yes)(scrutinee.symbol, pattern, scope)) - val falseBranch = normalizeToCaseBranches(specialize(tail, No)(scrutinee.symbol, pattern, scope)) + val trueBranch = normalizeToTerm(specialize(continuation ++ tail, true)(scrutinee.symbol, pattern, scope)) + val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutinee.symbol, pattern, scope)) CaseOf(scrutinee, Case(literal, trueBranch, falseBranch)) - // No class parameters. Easy + // false class parameters. Easy case Split.Cons(Branch(scrutinee, pattern @ Pattern.Class(nme, N), continuation), tail) => println(s"match $scrutinee with $nme (has location: ${nme.toLoc.isDefined})") - val trueBranch = normalizeToTerm(specialize(continuation ++ tail, Yes)(scrutinee.symbol, pattern, scope)) - val falseBranch = normalizeToCaseBranches(specialize(tail, No)(scrutinee.symbol, pattern, scope)) + val trueBranch = normalizeToTerm(specialize(continuation ++ tail, true)(scrutinee.symbol, pattern, scope)) + val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutinee.symbol, pattern, scope)) CaseOf(scrutinee, Case(nme, trueBranch, falseBranch)) case Split.Cons(Branch(scrutinee, pattern @ Pattern.Class(nme, S(parameters)), continuation), tail) => println(s"match $scrutinee with $pattern") @@ -61,7 +61,7 @@ trait Normalization { self: mlscript.pretyper.Traceable => throw new NormalizationException(msg"Scrutinee is not a scrutinee symbol" -> scrutinee.toLoc :: Nil) } val trueBranch = trace("compute true branch"){ - normalizeToTerm(specialize(continuation ++ tail, Yes)(scrutinee.symbol, pattern, scope)) + normalizeToTerm(specialize(continuation ++ tail, true)(scrutinee.symbol, pattern, scope)) }() val trueBranchWithBindings = Let( isRec = false, @@ -76,7 +76,7 @@ trait Normalization { self: mlscript.pretyper.Traceable => } ) val falseBranch = trace("compute false branch"){ - normalizeToCaseBranches(specialize(tail, No)(scrutinee.symbol, pattern, scope)) + normalizeToCaseBranches(specialize(tail, false)(scrutinee.symbol, pattern, scope)) }() CaseOf(scrutinee, Case(nme, trueBranchWithBindings, falseBranch)) case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => @@ -99,40 +99,34 @@ trait Normalization { self: mlscript.pretyper.Traceable => // Specialize `split` with the assumption that `scrutinee` matches `pattern`. private def specialize - (split: Split, matchOrNot: MatchOrNot) + (split: Split, matchOrNot: Bool) (implicit scrutinee: Symbol, pattern: Pattern, scope: Scope): Split = - trace(s"S${matchOrNot} <== ${scrutinee.name} is ${pattern}") { + trace(s"S${if (matchOrNot) "+" else "-"} <== ${scrutinee.name} is ${pattern}") { (matchOrNot, split) match { // Name patterns are translated to let bindings. - case (Yes | No, Split.Cons(Branch(otherScrutineeVar, Pattern.Name(alias), continuation), tail)) => + case (true | false, Split.Cons(Branch(otherScrutineeVar, Pattern.Name(alias), continuation), tail)) => Split.Let(false, alias, otherScrutineeVar, specialize(continuation, matchOrNot)) // Class pattern. Positive. - case (Yes, split @ Split.Cons(head @ Branch(otherScrutineeVar, Pattern.Class(otherClassName, otherParameters), continuation), tail)) => - val otherClassSymbol = otherClassName.symbolOption.flatMap(_.typeSymbolOption) match { - case N => throw new NormalizationException(msg"class name is not resolved" -> otherClassName.toLoc :: Nil) - case S(typeSymbol) => typeSymbol - } - val otherScrutinee = otherScrutineeVar.symbol + case (true, split @ Split.Cons(head @ Branch(ScrutineeOnly(otherScrutinee), Pattern.Class(otherClassName, otherParameters), continuation), tail)) => + val otherClassSymbol = getClassLikeSymbol(otherClassName) lazy val specializedTail = { println(s"specialized next") - specialize(tail, Yes) + specialize(tail, true) } if (scrutinee === otherScrutinee) { println(s"scrutinee: ${scrutinee.name} === ${otherScrutinee.name}") pattern match { case Pattern.Class(className, parameters) => - if (className === otherClassName) { // FIXME: Use class symbol. - println(s"class name: $className === $otherClassName") + val classSymbol = getClassLikeSymbol(className) + if (classSymbol === otherClassSymbol) { + println(s"Case 1: class name: $className === $otherClassName") (parameters, otherParameters) match { case (S(parameters), S(otherParameters)) => if (parameters.length === otherParameters.length) { println(s"same number of parameters: ${parameters.length}") - // Check if the class parameters are the same. - // Generate a function that generates bindings. - // TODO: Hygienic problems. val addLetBindings = parameters.iterator.zip(otherParameters).zipWithIndex.foldLeft[Split => Split](identity) { case (acc, N -> S(otherParameter) -> index) => - scrutinee match { + scrutinee match { // Well, it's a mistake to create a dedicated symbol for scrutinees. case symbol: ScrutineeSymbol => symbol.unappliedVarMap.get(otherClassSymbol) match { case N => @@ -140,7 +134,7 @@ trait Normalization { self: mlscript.pretyper.Traceable => die case S(unappliedVar) => println(s"we can create binding for ${otherParameter.name} at $index") - tail => Split.Let(false, otherParameter, Sel(unappliedVar, Var(index.toString)), tail) + tail => Split.Let(false, otherParameter, Sel(unappliedVar, Var(index.toString)), acc(tail)) } case _ => println(s"we can't create binding for ${otherParameter.name} at $index") @@ -148,18 +142,20 @@ trait Normalization { self: mlscript.pretyper.Traceable => } case (acc, S(parameter) -> S(otherParameter) -> index) if parameter.name =/= otherParameter.name => println(s"different parameter names at $index: ${parameter.name} =/= ${otherParameter.name}") - tail => Split.Let(false, otherParameter, parameter, tail) - case (acc, _) => acc + tail => Split.Let(false, otherParameter, parameter, acc(tail)) + case (acc, _) => + println(s"other cases") + acc } - // addLetBindings(specialize(continuation ++ tail, Yes)) - val specialized = addLetBindings(specialize(continuation, Yes)) + // addLetBindings(specialize(continuation ++ tail, true)) + val specialized = addLetBindings(specialize(continuation, true)) if (specialized.hasElse) { println("tail is discarded") specialized.withDiagnostic( msg"Discarded split because of else branch" -> None // TODO: Synthesize locations ) } else { - specialized ++ specialize(tail, Yes) + specialized ++ specialize(tail, true) } } else { throw new NormalizationException({ @@ -170,58 +166,44 @@ trait Normalization { self: mlscript.pretyper.Traceable => }) } // TODO: Other cases - case (_, _) => specialize(continuation ++ tail, Yes) + case (_, _) => specialize(continuation ++ tail, true) } // END match + } else if (otherClassSymbol.baseTypes contains classSymbol) { + println(s"Case 2: $otherClassName <: $className") + split } else { - (otherClassName.symbolOption, className.symbolOption) match { - case (S(otherClassSymbol: TypeSymbol), S(classSymbol: TypeSymbol)) if ( - otherClassSymbol.baseTypes contains classSymbol - ) => - println(s"class name: $otherClassName <: $className") - split - case (there, here) => - println(s"class name: $className =/= $otherClassName") - println(s"class symbols: ${there.fold("_")(_.name)} ${here.fold("_")(_.name)}") - specializedTail - } + println(s"Case 3: $className and $otherClassName are unrelated") + specializedTail } case _ => throw new NormalizationException((msg"Incompatible: ${pattern.toString}" -> pattern.toLoc) :: Nil) } } else { println(s"scrutinee: ${scrutinee.name} =/= ${otherScrutinee.name}") - if (scrutinee.name === otherScrutinee.name) { - println(s"WRONG!!!") - } split.copy( - head = head.copy(continuation = specialize(continuation, Yes)), + head = head.copy(continuation = specialize(continuation, true)), tail = specializedTail ) } // Class pattern. Negative - case (No, split @ Split.Cons(head @ Branch(otherScrutineeVar, Pattern.Class(otherClassName, otherParameters), continuation), tail)) => - val otherScrutinee = otherScrutineeVar.symbol + case (false, split @ Split.Cons(head @ Branch(ScrutineeOnly(otherScrutinee), Pattern.Class(otherClassName, otherParameters), continuation), tail)) => + val otherClassSymbol = getClassLikeSymbol(otherClassName) if (scrutinee === otherScrutinee) { println(s"scrutinee: ${scrutinee.name} === ${otherScrutinee.name}") pattern match { case Pattern.Class(className, parameters) => + val classSymbol = getClassLikeSymbol(className) if (className === otherClassName) { - println(s"class name: $className === $otherClassName") - specialize(tail, No) // TODO: Subsitute parameters to otherParameters + println(s"Case 1: class name: $otherClassName === $className") + println(s"parameters:") + println(s" LHS: ${otherParameters.fold("N")(_.iterator.map(_.fold("N")(_.name)).mkString(", "))}") + println(s" RHS: ${parameters.fold("N")(_.iterator.map(_.fold("N")(_.name)).mkString(", "))}") + specialize(tail, false) // TODO: Subsitute parameters to otherParameters + } else if (otherClassSymbol.baseTypes contains classSymbol) { + println(s"Case 2: class name: $otherClassName <: $className") + Split.Nil } else { - (otherClassName.symbolOption, className.symbolOption) match { - case (S(otherClassSymbol: TypeSymbol), S(classSymbol: TypeSymbol)) if ( - otherClassSymbol.baseTypes contains classSymbol - ) => - println(s"class name: $otherClassName <: $className") - Split.Nil - case (there, here) => - println(s"class name: $className =/= $otherClassName") - println(s"class symbols: ${there.fold("_")(_.name)} ${here.fold("_")(_.name)}") - split.copy( - // head = head.copy(continuation = specialize(continuation, No)), - tail = specialize(tail, No) - ) - } + println(s"Case 3: class name: $otherClassName and $className are unrelated") + split.copy(tail = specialize(tail, false)) } case _ => throw new NormalizationException((msg"Incompatible: ${pattern.toString}" -> pattern.toLoc) :: Nil) } @@ -233,15 +215,15 @@ trait Normalization { self: mlscript.pretyper.Traceable => ) } // Other patterns. Not implemented. - case (Yes | No, Split.Cons(Branch(otherScrutineeVar, pattern, continuation), tail)) => + case (true | false, Split.Cons(Branch(otherScrutineeVar, pattern, continuation), tail)) => throw new NormalizationException((msg"Unsupported pattern: ${pattern.toString}" -> pattern.toLoc) :: Nil) - case (Yes | No, let @ Split.Let(_, _, _, tail)) => + case (true | false, let @ Split.Let(_, _, _, tail)) => println("let binding, go next") let.copy(tail = specialize(tail, matchOrNot)) // Ending cases remain the same. - case (Yes | No, end @ (Split.Else(_) | Split.Nil)) => println("the end"); end - } // <-- end match - }(showSplit(s"S${matchOrNot} ==>", _)) + case (true | false, end @ (Split.Else(_) | Split.Nil)) => println("the end"); end + } + }(showSplit(s"S${if (matchOrNot) "+" else "-"} ==>", _)) /** * Print a normalized term with indentation. @@ -251,6 +233,12 @@ trait Normalization { self: mlscript.pretyper.Traceable => } object Normalization { + private def getClassLikeSymbol(className: Var): TypeSymbol = + className.symbolOption.flatMap(_.typeSymbolOption) match { + case N => throw new NormalizationException(msg"class name is not resolved" -> className.toLoc :: Nil) + case S(typeSymbol) => typeSymbol + } + class NormalizationException(val messages: Ls[Message -> Opt[Loc]]) extends Throwable { def this(message: Message, location: Opt[Loc]) = this(message -> location :: Nil) } diff --git a/shared/src/main/scala/mlscript/ucs/stages/package.scala b/shared/src/main/scala/mlscript/ucs/stages/package.scala index 2260c70d..825b3cbb 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/package.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/package.scala @@ -15,14 +15,15 @@ package object stages { } } - private[stages] sealed abstract class MatchOrNot { - override def toString(): String = this match { - case Yes => "+" - case No => "-" + object ScrutineeOnly { + def unapply(term: Term): Opt[ScrutineeSymbol] = term match { + case v: Var => v.symbol match { + case symbol: ScrutineeSymbol => S(symbol) + case _ => N + } + case _ => N } } - private[stages] final case object Yes extends MatchOrNot - private[stages] final case object No extends MatchOrNot sealed abstract class CasePattern { override def toString(): String = this match { diff --git a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls new file mode 100644 index 00000000..759e3ae2 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls @@ -0,0 +1,50 @@ +:PreTyper + +// This test file is to track possible name collision during specialization. + +class Pair[out A, out B](val first: A, val second: B) +//│ class Pair[A, B](first: A, second: B) + +fun p1(x) = x < 0 +fun p2(x) = x > 0 +fun p3(x) = x == 0 +//│ fun p1: Num -> Bool +//│ fun p2: Num -> Bool +//│ fun p3: Num -> Bool + +fun example1(p) = + if p is + Pair(x, y) and p1(x) and p1(y) then "both negative" + Pair(a, b) and p2(a) and p2(b) then "both positive" + else "nah" +//│ fun example1: (Object & ~#Pair | Pair[Num, Num]) -> ("both negative" | "both positive" | "nah") + +// FIXME: The error should be handled gracefully. +fun example2(p) = + if p is + Pair(x, y) and p1(x) and p1(y) then "both negative" + Pair(a, b) and p2(a) and p2(b) then x + y + else "nah" +//│ /!!!\ Uncaught error: java.lang.Exception: Variable x not found in scope + +// Next, let's check the name collision between a class and its super class. + +class Base(x: Int) +class Derived(y: Int) extends Base(y + 1) +//│ class Base(x: Int) +//│ class Derived(y: Int) extends Base + +fun example3(t) = + if t is + Base(x) and p1(x) then x + Derived(y) then y + else 42 +//│ fun example3: Base -> Int + +// FIXME +fun example4(t, x) = + if t is + Base(x) and p1(x) then x + Derived(y) then y + x // Oh no, x is captured by Base! Because this branch is absorbed by the previous one. + else 42 +//│ fun example4: (Base, anything) -> Int From 2c0a27f57c3a95d4e71f34030840986f83438287 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sun, 24 Dec 2023 04:51:41 +0800 Subject: [PATCH 022/143] Add a failed case where variable was accidentally captured --- .../pretyper/ucs/SpecilizationCollision.mls | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls index 759e3ae2..97039e52 100644 --- a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls +++ b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls @@ -2,7 +2,9 @@ // This test file is to track possible name collision during specialization. +fun (~~>) check(x, y) = if x === y then "passed" else error class Pair[out A, out B](val first: A, val second: B) +//│ fun (~~>) check: forall 'a. (Eql['a], 'a) -> "passed" //│ class Pair[A, B](first: A, second: B) fun p1(x) = x < 0 @@ -41,10 +43,27 @@ fun example3(t) = else 42 //│ fun example3: Base -> Int -// FIXME fun example4(t, x) = if t is Base(x) and p1(x) then x - Derived(y) then y + x // Oh no, x is captured by Base! Because this branch is absorbed by the previous one. + Derived(y) then y + x + // ^ + // Oh no, x is captured by x from Base! + // Because the branch is absorbed by the previous one. else 42 //│ fun example4: (Base, anything) -> Int + +example4(Base(-1), 0) ~~> -1 +example4(Base(1), 2) ~~> 42 +//│ "passed" +//│ res +//│ = 'passed' +//│ res +//│ = 'passed' + +// FIXME: Caused by variable shadowing during speicalization. +example4(Derived(1), 4) ~~> 5 +//│ "passed" +//│ res +//│ Runtime error: +//│ Error: an error was thrown From b47dea45cd4e872e1205d7561976835484bb1c03 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Mon, 25 Dec 2023 23:13:14 +0800 Subject: [PATCH 023/143] Generate let bindings for class parameters during desugaring We used to generate let bindings which destruct class parameters during normalization. This works fine but it exposes some inflexibility. For example, in the previous commit, I added a case where free variables from a absorbed case branch are shadowed. If we moving let bindings generation to desugaring stage, the problem can be solved easily. This change also paves the road for duplicated branch lifting. --- shared/src/main/scala/mlscript/helpers.scala | 12 + .../scala/mlscript/pretyper/PreTyper.scala | 5 +- .../main/scala/mlscript/pretyper/Symbol.scala | 72 ++--- .../main/scala/mlscript/ucs/DesugarUCS.scala | 56 +--- shared/src/main/scala/mlscript/ucs/core.scala | 29 +- .../src/main/scala/mlscript/ucs/package.scala | 5 + .../mlscript/ucs/stages/Desugaring.scala | 298 ++++++++++++++---- .../mlscript/ucs/stages/Normalization.scala | 56 +++- .../pretyper/ucs/SpecilizationCollision.mls | 6 +- 9 files changed, 359 insertions(+), 180 deletions(-) diff --git a/shared/src/main/scala/mlscript/helpers.scala b/shared/src/main/scala/mlscript/helpers.scala index 9bd5d430..524cae27 100644 --- a/shared/src/main/scala/mlscript/helpers.scala +++ b/shared/src/main/scala/mlscript/helpers.scala @@ -751,6 +751,7 @@ trait VarImpl { self: Var => def symbol_=(symbol: Symbol): Unit = _symbol match { case N => _symbol = S(symbol) + case S(`symbol`) => () case S(_) => ??? } // def withSymbol: Var = { symbol = S(new ValueSymbol(this, false)); this } @@ -787,6 +788,17 @@ trait Located { lazy val freeVars: Set[Var] = this match { case v: Var => Set.single(v) + case Let(true, nme, rhs, body) => body.freeVars ++ rhs.freeVars - nme + case Let(false, nme, rhs, body) => body.freeVars - nme ++ rhs.freeVars + case Lam(tup: Tup, body) => body.freeVars -- tup.freeVars + case Tup(fields) => fields.iterator.flatMap(_._2.value.freeVars.iterator).toSet + case Blk(stmts) => stmts.iterator.foldRight(Set.empty[Var]) { + case (NuFunDef(isLetRec, nme, _, _, L(rhs)), fvs) => fvs - nme ++ (isLetRec match { + case N | S(true) => rhs.freeVars - nme + case S(false) => rhs.freeVars + }) + case (statement, fvs) => fvs ++ statement.freeVars + } case _ => children.iterator.flatMap(_.freeVars.iterator).toSet } diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index 55b1e772..361dea79 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -40,9 +40,6 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D case S(sym: ValueSymbol) => println(s"Resolve variable $v to a value.") v.symbol = sym - case S(sym: SubValueSymbol) => - println(s"Resolve variable $v to a value.") - v.symbol = sym case S(sym: FunctionSymbol) => println(s"Resolve variable $v to a function.") v.symbol = sym @@ -246,7 +243,7 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D case symbol: FunctionSymbol if !visitedSymbol(symbol) => visitedSymbol += symbol traverseFunction(symbol, symbol.defn)(completeScope) - case _: FunctionSymbol | _: ValueSymbol | _: SubValueSymbol => () + case _: FunctionSymbol | _: ValueSymbol => () } (completeScope, new TypeContents) }({ case (scope, contents) => s"traverseStatements ==> Scope {${scope.showLocalSymbols}}" }) diff --git a/shared/src/main/scala/mlscript/pretyper/Symbol.scala b/shared/src/main/scala/mlscript/pretyper/Symbol.scala index 1630f721..543d4fec 100644 --- a/shared/src/main/scala/mlscript/pretyper/Symbol.scala +++ b/shared/src/main/scala/mlscript/pretyper/Symbol.scala @@ -70,64 +70,54 @@ package object symbol { } } + // FIXME: + // We should remove `ScrutineeSymbol` ASAP. There are too many reasons. The + // most important one I can remember right now is that multiple variables + // may share the same scrutinee symbol. But for now symbol must have a unique + // name. We should use a dedicated symbol for tracking scrutinees. sealed abstract class ScrutineeSymbol(name: Str) extends TermSymbol(name) { def toLoc: Opt[Loc] val matchedClasses: MutMap[TypeSymbol, Buffer[Loc]] = MutMap.empty val unappliedVarMap: MutMap[TypeSymbol, Var] = MutMap.empty - // Hmm, maybe we can merge these two maps into one. + val subScrutineeMap: MutMap[TypeSymbol, MutMap[Int, MutMap[Var, ValueSymbol]]] = MutMap.empty + // Hmm, maybe we can merge these three maps into one. + // Urgh, let's do this in the next refactor. + // I really should move these imperative and stateful functions to a + // separate class! + + def getSubScrutineeSymbolOrElse( + classLikeSymbol: TypeSymbol, + index: Int, + name: Var, // <-- Remove this parameter after we remove `ScrutineeSymbol`. + default: => ValueSymbol + ): ValueSymbol = + subScrutineeMap.getOrElseUpdate(classLikeSymbol, MutMap.empty) + .getOrElseUpdate(index, MutMap.empty) + .getOrElseUpdate(name, default) def addMatchedClass(symbol: TypeSymbol, loc: Opt[Loc]): Unit = { matchedClasses.getOrElseUpdate(symbol, Buffer.empty) ++= loc } + def getUnappliedVarOrElse(classLikeSymbol: TypeSymbol, default: => Var): Var = + unappliedVarMap.getOrElseUpdate(classLikeSymbol, default) + def addUnappliedVar(symbol: TypeSymbol, nme: Var): Unit = { unappliedVarMap += symbol -> nme } - /** - * This map contains the sub-scrutinee symbols when this scrutinee is matched - * against class patterns. - */ - val classParameterScrutineeMap: MutMap[Var -> Int, SubValueSymbol] = MutMap.empty - val tupleElementScrutineeMap: MutMap[Int, SubValueSymbol] = MutMap.empty - val recordValueScrutineeMap: MutMap[Var, SubValueSymbol] = MutMap.empty - - def addSubScrutinee(className: Var, index: Int, parameter: Var, loc: Opt[Loc]): SubValueSymbol = { - classParameterScrutineeMap.getOrElseUpdate(className -> index, { - new SubValueSymbol(this, S(className) -> S(index), parameter.name, loc) - }) - } - - def addSubScrutinee(fieldName: Var, loc: Opt[Loc]): SubValueSymbol = - recordValueScrutineeMap.getOrElseUpdate(fieldName, { - val synthesizedName = s"${name}$$record${fieldName}" - new SubValueSymbol(this, S(fieldName) -> N, synthesizedName, loc) - }) - - def addSubScrutinee(index: Int, loc: Opt[Loc]): SubValueSymbol = - tupleElementScrutineeMap.getOrElseUpdate(index, { - val synthesizedName = s"${name}$$tuple${index.toString}" - new SubValueSymbol(this, N -> S(index), synthesizedName, loc) - }) + // Store the symbol of the parent scrutinee. I doubt if this is necessary. + private var maybeParentSymbol: Opt[ScrutineeSymbol] = N + def parentSymbol: Opt[ScrutineeSymbol] = maybeParentSymbol + def withParentSymbol(parentSymbol: ScrutineeSymbol): this.type = + maybeParentSymbol match { + case N => maybeParentSymbol = S(parentSymbol); this + case S(_) => throw new IllegalStateException("Parent symbol is already set.") + } } final class ValueSymbol(val nme: Var, val hoisted: Bool) extends ScrutineeSymbol(nme.name) { override def toLoc: Opt[Loc] = nme.toLoc } - - final class SubValueSymbol( - val parentSymbol: ScrutineeSymbol, - /** - * TODO: This becomes useless now. - * Class patterns: (S(className), S(index)) - * Record patterns: (S(fieldName), N) - * Tuple patterns: (N, S(index)) - */ - val accessor: (Opt[Var], Opt[Int]), - override val name: Str, - override val toLoc: Opt[Loc] - ) extends ScrutineeSymbol(name) { - lazy val toVar: Var = Var(name) - } } \ No newline at end of file diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index a9743b5c..0dcbd456 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -75,7 +75,7 @@ trait DesugarUCS extends Transformation traverseSplit(tail) case Split.Let(_, name, _, tail) => println(s"found let binding: \"$name\"") - traverseSplit(tail)(scope + new ValueSymbol(name, false)) + traverseSplit(tail)(scope + name.symbol) case Split.Else(default) => traverseTerm(default) case Split.Nil => println("the end") } @@ -90,55 +90,11 @@ trait DesugarUCS extends Transformation } pattern match { case core.Pattern.Literal(literal) => Nil - case core.Pattern.Name(nme) => - nme.symbol = scrutineeSymbol - nme -> scrutineeSymbol :: Nil - // case core.Pattern.Class(nme @ Var("true"), N) => - // println(s"found true pattern") - // nme -> scrutineeSymbol :: Nil - case core.Pattern.Class(nme, maybeParameters) => - println(s"`$nme` has location: ${nme.toLoc.isDefined}") - // Resolve `nme`. It can either be a class, a trait, or a module. - val symbol = scope.getTypeSymbol(nme.name) match { - case S(symbol: TraitSymbol) => println(s"${nme.name} is a trait"); symbol - case S(symbol: ClassSymbol) => println(s"${nme.name} is a class"); symbol - case S(symbol: ModuleSymbol) => println(s"${nme.name} is a module"); symbol - case S(symbol: MixinSymbol) => - throw new DesugaringException(msg"Mixins are not allowed in pattern" -> nme.toLoc :: Nil) - case S(symbol: TypeAliasSymbol) => - throw new DesugaringException(msg"Type alias is not allowed in pattern" -> nme.toLoc :: Nil) - // case S(symbol: TermSymbol) => - // throw new DesugaringException(msg"Only classes, modules, and traits can be matched against." -> nme.toLoc :: Nil) - case N => - throw new DesugaringException(msg"Undefined symbol found in patterns." -> nme.toLoc :: Nil) - } - nme.symbol = symbol - // Add the class to the list of matched classes. - scrutineeSymbol.addMatchedClass(symbol, nme.toLoc) - maybeParameters match { - case N => Nil - case S(parameters) => - parameters.iterator.zipWithIndex.flatMap { - case (N, _) => N - case (S(parameter), index) => - val symbol = scrutineeSymbol.addSubScrutinee(nme, index, parameter, parameter.toLoc) - parameter.symbol = symbol; S(parameter -> symbol) - }.toList - } - case core.Pattern.Tuple(elements) => elements.flatMap { - case N => Nil - case S(pattern) => elements.iterator.zipWithIndex.flatMap { - case (N, _) => N - case (S(element), index) => - val symbol = scrutineeSymbol.addSubScrutinee(index, element.toLoc) - element.symbol = symbol; S(element -> symbol) - }.toList - } - case core.Pattern.Record(entries) => - entries.iterator.zipWithIndex.map { case ((fieldName, bindingName), _) => - val symbol = scrutineeSymbol.addSubScrutinee(fieldName, bindingName.toLoc) - bindingName.symbol = symbol; bindingName -> symbol - }.toList + case core.Pattern.Name(nme) => nme -> nme.symbol :: Nil + // For now, there should only be parameter-less class patterns. + case core.Pattern.Class(nme, maybeParameters) => Nil + case core.Pattern.Tuple(_) => ??? + case core.Pattern.Record(_) => ??? } }(_.iterator.map(_._1.name).mkString("traversePattern ==> [", ", ", "]")) } diff --git a/shared/src/main/scala/mlscript/ucs/core.scala b/shared/src/main/scala/mlscript/ucs/core.scala index d7886438..94af18ac 100644 --- a/shared/src/main/scala/mlscript/ucs/core.scala +++ b/shared/src/main/scala/mlscript/ucs/core.scala @@ -3,6 +3,10 @@ package mlscript.ucs import collection.mutable.Buffer import mlscript.{Diagnostic, Lit, Loc, Located, Message, Term, Var} import mlscript.utils._, shorthands._ +import mlscript.ucs.core.Split.Cons +import mlscript.ucs.core.Split.Let +import mlscript.ucs.core.Split.Else +import mlscript.ucs.stages.Desugaring package object core { sealed abstract class Pattern extends Located { @@ -74,6 +78,27 @@ package object core { case Split.Nil => false } + lazy val freeVars: Set[Var] = this match { + case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => + // FIXME: It is safe to ignore `pattern` for now. + continuation.freeVars ++ tail.freeVars + case Split.Let(true, nme, rhs, tail) => tail.freeVars ++ rhs.freeVars - nme + case Split.Let(false, nme, rhs, tail) => tail.freeVars - nme ++ rhs.freeVars + case Split.Else(term) => term.freeVars + case Split.Nil => Set.empty + } + + /** + * Remove duplicated bindings. + */ + def withoutBindings(vars: Set[Var]): Split = this match { + case self @ Cons(head @ Branch(_, _, continuation), tail) => + self.copy(head.copy(continuation = continuation.withoutBindings(vars)), tail.withoutBindings(vars)) + case self @ Let(_, name, _, tail) if vars contains name => tail.withoutBindings(vars) + case self @ Let(_, _, _, tail) => self.copy(tail = tail.withoutBindings(vars)) + case Else(_) | Split.Nil => this + } + private val diagnostics: Buffer[Message -> Opt[Loc]] = Buffer.empty def withDiagnostic(diagnostic: Message -> Opt[Loc]): this.type = { @@ -107,13 +132,13 @@ package object core { case (n, line) :: tail => (n, (if (isTopLevel) "" else "") + s"$line") :: tail case Nil => Nil }) ::: split(tail, false, isTopLevel) - case Split.Let(_, nme, rhs, tail) => (0, s"let $nme = $rhs") :: split(tail, false, isTopLevel) + case Split.Let(_, nme, rhs, tail) => (0, s"let ${showVar(nme)} = $rhs") :: split(tail, false, isTopLevel) case Split.Else(term) => (if (isFirst) (0, s"then $term") else (0, s"else $term")) :: Nil case Split.Nil => Nil } def branch(b: Branch): Lines = { val Branch(scrutinee, pattern, continuation) = b - s"$scrutinee is $pattern" #: split(continuation, true, false) + s"${showVar(scrutinee)} is $pattern" #: split(continuation, true, false) } val lines = split(s, true, true) (if (prefix.isEmpty) lines else prefix #: lines) diff --git a/shared/src/main/scala/mlscript/ucs/package.scala b/shared/src/main/scala/mlscript/ucs/package.scala index 21516814..bf4df92e 100644 --- a/shared/src/main/scala/mlscript/ucs/package.scala +++ b/shared/src/main/scala/mlscript/ucs/package.scala @@ -28,4 +28,9 @@ package object ucs { } } } + + /** If the variable is associated with a symbol, mark it with an asterisk. + * If the variable has a location, mark it with a dagger. */ + private[ucs] def showVar(`var`: Var): String = + `var`.name + (`var`.symbolOption.fold("")(_ => "*")) + (`var`.toLoc.fold("")(_ => "†")) } diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 9a1a1972..75526abc 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -1,144 +1,312 @@ package mlscript.ucs.stages -import mlscript.{App, Asc, Fld, Term, Var, TypeName} +import mlscript.{App, Asc, Fld, FldFlags, Lit, Sel, Term, Tup, TypeName, Var} import mlscript.ucs.{syntax => s, core => c, PartialTerm} import mlscript.ucs.helpers.mkBinOp import mlscript.utils._, shorthands._ import mlscript.pretyper.symbol._ +import mlscript.pretyper.{PreTyper, Scope} import mlscript.ucs.DesugaringException import mlscript.Message, Message.MessageContext -trait Desugaring { self: mlscript.pretyper.Traceable => - @inline def desugar(term: s.TermSplit): c.Split = desugarTermSplit(term)(PartialTerm.Empty) +/** + * The desugaring stage of UCS. In this stage, we transform the source abstract + * syntax into core abstract syntax, which is more concise. We will make + * following changes during this stage: + * + * 1. Flatten nested patterns and generated bindings for nameless parameters. + * 2. Desugar variable patterns to plain let bindings. + * 3. Desugar literal patterns to equivalent boolean expressions. + * 4. Reassemble partial terms that are broken by "conditional splits". + * 5. Associate each scrutinee with a unique "scrutinee symbol". + * TODO: `ScrutineeSymbol` will be removed in the future. + * + * Desugared UCS terms (core abstract syntax) are in the form of `Split`, which + * is a list of branches. Each branch consists of a scrutinee, a pattern, and a + * continuation. + */ +trait Desugaring { self: PreTyper => + @inline def desugar(term: s.TermSplit)(implicit scope: Scope): c.Split = + desugarTermSplit(term)(PartialTerm.Empty, scope) import Desugaring._ + // Call these objects to generate fresh variables for different purposes. + // I plan to mix the unique identifiers of UCS expressions into the prefixes. + // So that the generated variables will definitely not conflict with others. private val freshCache = new VariableGenerator(cachePrefix) private val freshScrutinee = new VariableGenerator(scrutineePrefix) private val freshTest = new VariableGenerator(testPrefix) - private def freshScrutinee(parentScrutinee: Var, parentClassName: Var, index: Int): Var = + /** + * Coin a fresh name for a destructed parameter. The name consists of three + * parts: the name of the parent scrutinee, the name of matched class, and + * the index of the parameter. For example, if variable `x` is matched as + * `Cons(hd, tl)`, then the name of `hd` will be `x$Cons_0` and the name of + * `tl` will be `x$Cons_1`. + */ + private def freshScrutinee(parentScrutinee: Var, parentClassName: Str, index: Int): Var = Var(s"${parentScrutinee}$$${parentClassName}_${index.toString}") - private def truePattern = c.Pattern.Class(Var("true"), N) + /** + * Coin a fresh name for the result of `unapply` method. The name begins with + * `args_`, followed by the name of the scrutinee, and finally ends with the + * name of the matched class. For example, if variable `x` is matched as + * `Cons(hd, tl)`, then the name of `Cons.unapply(x)` will be `args_x$Cons`. + * Parameters `hd` and `tl` are obtained by selecting `.1` and `.2` from + * `args_x$Cons`. + */ + private def makeUnappliedVar(scrutinee: Var, className: Var): Var = + Var(s"args_${scrutinee.name}$$${className.name}") - private def flattenClassParameters(parentScrutinee: Var, parentClassName: Var, parameters: Opt[Ls[Opt[s.Pattern]]]): Opt[Ls[Opt[Var]]] -> Ls[Opt[Var -> s.Pattern]] = - parameters match { - case S(parameters) => - val (a, b) = parameters.zipWithIndex.unzip { - case (N, index) => N -> N - case (S(s.NamePattern(name)), index) => (S(name), N) - case (S(parameterPattern: s.ClassPattern), index) => - val scrutinee = freshScrutinee(parentScrutinee, parentClassName, index) - (S(scrutinee), Some((scrutinee, parameterPattern))) - case (S(parameterPattern: s.LiteralPattern), index) => - val scrutinee = freshScrutinee(parentScrutinee, parentClassName, index) - (S(scrutinee), Some((scrutinee, parameterPattern))) - case _ => ??? // Other patterns are not implemented yet. - } - (S(a), b) - case N => (N, Nil) + // I plan to integrate scrutinee symbols into a field of `ValueSymbol`. + // Because each `ValueSymbol` can be matched in multiple UCS expressions. + private implicit class VarOps(nme: Var) { + def withFreshSymbol: Var = nme.withSymbol(freshSymbol(nme)) + + def getScrutineeSymbol: ScrutineeSymbol = nme.symbolOption match { + case S(symbol: ScrutineeSymbol) => symbol + case S(otherSymbol) => throw new DesugaringException( + msg"Expected scrutinee symbol, found ${nme.symbol.name}" -> nme.toLoc :: Nil + ) + case N => throw new DesugaringException( + msg"Scrutinee symbol not found" -> nme.toLoc :: Nil + ) + } + + def withResolvedTypeSymbol(implicit scope: Scope): Var = { + nme.symbol = nme.resolveTypeSymbol + nme } - private def desugarTermSplit(split: s.TermSplit)(implicit termPart: PartialTerm): c.Split = + def resolveTypeSymbol(implicit scope: Scope): TypeSymbol = scope.getTypeSymbol(nme.name) match { + case S(symbol: TraitSymbol) => + println(s"resolveTypeSymbol ${nme} ==> trait") + nme.symbol = symbol + symbol + case S(symbol: ClassSymbol) => + println(s"resolveTypeSymbol ${nme} ==> class") + nme.symbol = symbol + symbol + case S(symbol: ModuleSymbol) => + println(s"resolveTypeSymbol ${nme} ==> module") + nme.symbol = symbol + symbol + case S(symbol: MixinSymbol) => + throw new DesugaringException(msg"Mixins are not allowed in pattern" -> nme.toLoc :: Nil) + case S(symbol: TypeAliasSymbol) => + throw new DesugaringException(msg"Type alias is not allowed in pattern" -> nme.toLoc :: Nil) + case N => + throw new DesugaringException(msg"Undefined symbol found in patterns." -> nme.toLoc :: Nil) + } + } + + /** + * A shorthand for making a true pattern, which is useful in desugaring + * Boolean conditions. + */ + private def truePattern(implicit scope: Scope) = c.Pattern.Class(Var("true").withResolvedTypeSymbol, N) + + private def freshSymbol(nme: Var): ValueSymbol = new ValueSymbol(nme, false) + + private def desugarTermSplit(split: s.TermSplit)(implicit termPart: PartialTerm, scope: Scope): c.Split = split match { case s.Split.Cons(head, tail) => desugarTermBranch(head) ++ desugarTermSplit(tail) - case s.Split.Let(rec, nme, rhs, tail) => c.Split.Let(rec, nme, rhs, desugarTermSplit(tail)) + case s.Split.Let(rec, nme, rhs, tail) => + c.Split.Let(rec, nme, rhs, desugarTermSplit(tail)(termPart, scope + nme.withFreshSymbol.symbol)) // <-- Weird use. case s.Split.Else(default) => c.Split.Else(default); case s.Split.Nil => c.Split.Nil } // This function does not need to can `withCachedTermPart` because all branches assume that // `termPart` is either empty or waiting for an RHS. - private def desugarTermBranch(branch: s.TermBranch)(implicit termPart: PartialTerm): c.Split = + private def desugarTermBranch(branch: s.TermBranch)(implicit termPart: PartialTerm, scope: Scope): c.Split = trace(s"desugarTermBranch <== $termPart") { branch match { case s.TermBranch.Boolean(testPart, continuation) => - val test = freshTest() + val test = freshTest().withFreshSymbol c.Split.Let( rec = false, name = test, term = Asc(termPart.addTerm(testPart, true).get, TypeName("Bool")), - tail = c.Branch(test, truePattern, desugarTermSplit(continuation)(PartialTerm.Empty)) :: c.Split.Nil + tail = c.Branch(test, truePattern, desugarTermSplit(continuation)(PartialTerm.Empty, scope + test.symbol)) :: c.Split.Nil ) case s.TermBranch.Match(scrutinee, split) => - desugarPatternSplit(split)(termPart.addTerm(scrutinee, true).get) + desugarPatternSplit(split)(termPart.addTerm(scrutinee, true).get, scope) case s.TermBranch.Left(left, continuation) => - desugarOperatorSplit(continuation)(termPart.addTerm(left, true)) + desugarOperatorSplit(continuation)(termPart.addTerm(left, true), scope) } }() - private def withCachedTermPart[B <: s.Branch](desugar: (PartialTerm) => c.Split)(implicit termPart: PartialTerm): c.Split = + private def withCachedTermPart[B <: s.Branch](desugar: (PartialTerm, Scope) => c.Split)(implicit termPart: PartialTerm, scope: Scope): c.Split = termPart.get match { - case v: Var => desugar(termPart) // No need to cache variables. + case v: Var => desugar(termPart, scope) // No need to cache variables. case rhs => - val cache = freshCache() - c.Split.Let(false, cache, rhs, desugar(PartialTerm.Total(cache, Nil))) + val cache = freshCache().withFreshSymbol + c.Split.Let(false, cache, rhs, desugar(PartialTerm.Total(cache, Nil), scope + cache.symbol)) } - private def desugarOperatorSplit(split: s.OperatorSplit)(implicit termPart: PartialTerm): c.Split = - withCachedTermPart { termPart => split match { - case s.Split.Cons(head, tail) => desugarOperatorBranch(head)(termPart) ++ desugarOperatorSplit(tail)(termPart) - case s.Split.Let(rec, nme, rhs, tail) => c.Split.Let(rec, nme, rhs, desugarOperatorSplit(tail)(termPart)) + private def desugarOperatorSplit(split: s.OperatorSplit)(implicit termPart: PartialTerm, scope: Scope): c.Split = + withCachedTermPart { (termPart, scope) => split match { + case s.Split.Cons(head, tail) => desugarOperatorBranch(head)(termPart, scope) ++ desugarOperatorSplit(tail)(termPart, scope) + case s.Split.Let(rec, nme, rhs, tail) => + c.Split.Let(rec, nme, rhs, desugarOperatorSplit(tail)(termPart, scope + nme.withFreshSymbol.symbol)) // <-- Weird use. case s.Split.Else(default) => c.Split.Else(default) case s.Split.Nil => c.Split.Nil }} - private def desugarOperatorBranch(branch: s.OperatorBranch)(implicit termPart: PartialTerm): c.Split = + private def desugarOperatorBranch(branch: s.OperatorBranch)(implicit termPart: PartialTerm, scope: Scope): c.Split = trace(s"desugarOperatorBranch <== $termPart") { branch match { - case s.OperatorBranch.Binary(op, split) => desugarTermSplit(split)(termPart.addOp(op)) - case s.OperatorBranch.Match(_, split) => desugarPatternSplit(split)(termPart.get) + case s.OperatorBranch.Binary(op, split) => desugarTermSplit(split)(termPart.addOp(op), scope) + case s.OperatorBranch.Match(_, split) => desugarPatternSplit(split)(termPart.get, scope) } }() - private def flattenNestedPattern(pattern: s.ClassPattern, scrutinee: Var, next: c.Split): c.Branch = { - val (parameterBindings, subPatterns) = flattenClassParameters(scrutinee, pattern.nme, pattern.parameters) - c.Branch(scrutinee, c.Pattern.Class(pattern.nme, parameterBindings), subPatterns.foldRight(next) { - case (None, next) => next - case (Some((nme, pattern: s.ClassPattern)), next) => - flattenNestedPattern(pattern, nme, next) :: c.Split.Nil - case (Some((nme, pattern: s.LiteralPattern)), next) => - val scrutinee = freshScrutinee() - c.Split.Let( - rec = false, - scrutinee, - mkBinOp(nme, Var("=="), pattern.literal, true), - c.Branch(scrutinee, truePattern, next) :: c.Split.Nil + /** Make a term like `ClassName.unapply(scrutinee)`. */ + private def makeUnapplyCall(scrutinee: Var, className: Var) = + App(Sel(className, Var("unapply")), Tup(N -> Fld(FldFlags.empty, scrutinee) :: Nil)) + + private def makeLiteralTest(test: Var, scrutinee: Var, literal: Lit)(implicit scope: Scope): c.Split => c.Split = + next => c.Split.Let( + rec = false, + name = scrutinee, + term = mkBinOp(scrutinee, Var("=="), literal, true), + tail = c.Branch(scrutinee, truePattern, next) :: c.Split.Nil + ) + + private def flattenClassParameters(parentScrutinee: Var, parentClassLikeSymbol: TypeSymbol, parameters: Ls[Opt[s.Pattern]]): Ls[Opt[Var -> Opt[s.Pattern]]] = + parameters.zipWithIndex.map { + case (N, _) => N + case (S(s.NamePattern(name)), index) => + val symbol = parentScrutinee.getScrutineeSymbol.getSubScrutineeSymbolOrElse( + parentClassLikeSymbol, index, name, new ValueSymbol(name, false) ) - case (Some((nme, pattern)), next) => ??? // Other patterns are not implemented yet. - }) + S(name.withSymbol(symbol) -> N) + case (S(parameterPattern @ (s.ClassPattern(_, _) | s.LiteralPattern(_))), index) => + val scrutinee = freshScrutinee(parentScrutinee, parentClassLikeSymbol.name, index) + val symbol = parentScrutinee.getScrutineeSymbol.getSubScrutineeSymbolOrElse( + parentClassLikeSymbol, index, scrutinee, new ValueSymbol(scrutinee, false) + ) + S(scrutinee.withSymbol(symbol) -> S(parameterPattern)) + case _ => ??? // Other patterns are not implemented yet. + } + + /** + * Recursively decompose and flatten a possibly nested class pattern. Any + * user-declared and generated variables will be added to the given scope and + * a augmented scope will be returned. Meanwhile, it produces a function that + * wrap a split with all bindings and matches. + * + * This function involves some higher-order function's compose operation, so + * it is somewhat convoluted to read. However, this is a necessary + * complication, as we need information from the scope when generating + * variable names. + * + * @param pattern the class pattern + * @param scrutinee the scrutinee of the pattern. The caller should make sure + * that the scrutinee is associated with a symbol in the + * given scope. + * @param initialScope the scope before flattening the class pattern + * @return a tuple of the augmented scope and a function that wrap a split + */ + private def flattenClassPattern(pattern: s.ClassPattern, scrutinee: Var, initialScope: Scope): (Scope, c.Split => c.Branch) = { + val scrutineeSymbol = scrutinee.getScrutineeSymbol + val patternClassSymbol = pattern.nme.resolveTypeSymbol(initialScope) + // Most importantly, we need to add the class to the list of matched classes. + scrutineeSymbol.addMatchedClass(patternClassSymbol, pattern.nme.toLoc) + val (scopeWithAll, bindAll) = pattern.parameters match { + case S(parameters) => + // Before processing sub-patterns, we need to generate a variable that + // holds the result of `unapply` method. Such variable might have been + // generated by a previous branches. We MUST reuse so that we can merge + // duplicated bindings during normalization. + val unapp = scrutineeSymbol.getUnappliedVarOrElse(patternClassSymbol, { + val vari = makeUnappliedVar(scrutinee, pattern.nme) + vari.withSymbol(new ValueSymbol(vari, false)) + }) + val nestedPatterns = flattenClassParameters(scrutinee, patternClassSymbol, parameters) + // First, handle bindings of parameters of the current class pattern. + val bindClassParameters = nestedPatterns.iterator.zipWithIndex.foldRight[c.Split => c.Split](identity) { + case ((N, _), bindNextParameter) => bindNextParameter + case ((S(parameter -> _), index), bindNextParameter) => + bindNextParameter.andThen { c.Split.Let(false, parameter, Sel(unapp, Var(index.toString)), _) } + }.andThen { c.Split.Let(false, unapp, makeUnapplyCall(scrutinee, pattern.nme), _): c.Split } + val scopeWithClassParameters = initialScope ++ (unapp.symbol :: nestedPatterns.flatMap(_.map(_._1.symbol))) + // Second, collect bindings from sub-patterns and accumulate a function + // that add let bindings to a split (we call it "binder"). + nestedPatterns.foldLeft((scopeWithClassParameters, bindClassParameters)) { + // If this parameter is not matched with a sub-pattern, then we do + // nothing and pass on scope and binder. + case (acc, S(_ -> N)) => acc + // If this sub-pattern is a class pattern, we need to recursively flatten + // the class pattern. We will get a scope with all bindings and a function + // that adds all bindings to a split. The scope can be passed on to the + // next sub-pattern. The binder needs to be composed with the previous + // binder. + case ((scope, bindPrevious), S(nme -> S(pattern: s.ClassPattern))) => + val (scopeWithNestedAll, bindNestedAll) = flattenClassPattern(pattern, nme, scope) + (scopeWithNestedAll, split => bindPrevious(bindNestedAll(split) :: c.Split.Nil)) + case ((scope, bindPrevious), S(nme -> S(pattern: s.LiteralPattern))) => + val test = freshTest().withFreshSymbol + (scope + test.symbol, makeLiteralTest(test, nme, pattern.literal)(scope).andThen(bindPrevious)) + // Well, other patterns are not supported yet. + case (acc, S((nme, pattern))) => ??? + // If this parameter is empty (e.g. produced by wildcard), then we do + // nothing and pass on scope and binder. + case (acc, N) => acc + } + // If there is no parameter, then we are done. + case N => (initialScope, identity(_: c.Split)) + } + // Last, return the scope with all bindings and a function that adds all matches and bindings to a split. + (scopeWithAll, split => c.Branch(scrutinee, c.Pattern.Class(pattern.nme, N), bindAll(split))) } - private def desugarPatternSplit(split: s.PatternSplit)(implicit scrutinee: Term): c.Split = { - def rec(scrutinee: Var, split: s.PatternSplit): c.Split = split match { + private def desugarPatternSplit(split: s.PatternSplit)(implicit scrutinee: Term, scope: Scope): c.Split = { + def rec(scrutinee: Var, split: s.PatternSplit)(implicit scope: Scope): c.Split = split match { case s.Split.Cons(head, tail) => - lazy val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty) head.pattern match { case s.AliasPattern(nme, pattern) => ??? case s.LiteralPattern(literal) => ??? case s.ConcretePattern(nme) => - val condition = freshScrutinee() + val test = freshTest().withFreshSymbol c.Split.Let( rec = false, - name = condition, + name = test, term = mkBinOp(scrutinee, Var("==="), nme, true), - tail = c.Branch(condition, truePattern, continuation) :: rec(scrutinee, tail) + tail = c.Branch( + scrutinee = test, + pattern = truePattern, + continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope + test.symbol) + ) :: rec(scrutinee, tail) ) - case s.NamePattern(nme) => c.Branch(scrutinee, c.Pattern.Name(nme), continuation) :: rec(scrutinee, tail) - case pattern @ s.ClassPattern(nme, fields) => flattenNestedPattern(pattern, scrutinee, continuation) :: rec(scrutinee, tail) + case s.NamePattern(Var("_")) => + desugarTermSplit(head.continuation)(PartialTerm.Empty, scope) ++ rec(scrutinee, tail) + case s.NamePattern(nme) => + // Share the scrutinee's symbol with its aliases. + nme.symbol = scrutinee.symbol + val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope + nme.symbol) + c.Branch(scrutinee, c.Pattern.Name(nme), continuation) :: rec(scrutinee, tail) + case pattern @ s.ClassPattern(nme, fields) => + println(s"find term symbol of $scrutinee in ${scope.showLocalSymbols}") + scrutinee.symbol = scope.getTermSymbol(scrutinee.name).getOrElse(???) + val (scopeWithAll, bindAll) = flattenClassPattern(pattern, scrutinee, scope) + val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scopeWithAll) + bindAll(continuation) :: rec(scrutinee, tail) case s.TuplePattern(fields) => ??? case s.RecordPattern(entries) => ??? } - case s.Split.Let(isRec, nme, rhs, tail) => c.Split.Let(isRec, nme, rhs, rec(scrutinee, tail)) + case s.Split.Let(isRec, nme, rhs, tail) => + c.Split.Let(isRec, nme, rhs, rec(scrutinee, tail)(scope + nme.withFreshSymbol.symbol)) // <-- Weird use. case s.Split.Else(default) => c.Split.Else(default) case s.Split.Nil => c.Split.Nil } scrutinee match { case nme: Var => rec(nme, split) case other => - val alias = freshScrutinee() - c.Split.Let(false, alias, scrutinee, rec(alias, split)) + val alias = freshScrutinee().withFreshSymbol + c.Split.Let(false, alias, scrutinee, rec(alias, split)(scope + alias.symbol)) } } } diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index def15d0b..7f995371 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -1,7 +1,7 @@ package mlscript.ucs.stages -import mlscript.ucs.{Lines, LinesOps} +import mlscript.ucs.{showVar, Lines, LinesOps} import mlscript.ucs.core._ import mlscript.ucs.helpers._ import mlscript.pretyper.Scope @@ -14,6 +14,28 @@ import mlscript.utils._, shorthands._ trait Normalization { self: mlscript.pretyper.Traceable => import Normalization._ + private val freshShadowed = new Desugaring.VariableGenerator("shadowed$") + + def concat(lhs: Split, rhs: Split): Split = traceNot(s"concat <== ${printSplit(lhs)} ${printSplit(rhs)}") { + def rec(these: Split, those: Split)(implicit vars: Set[Var]): Split = { + these match { + case these @ Split.Cons(_, tail) => these.copy(tail = rec(tail, those)) + case these @ Split.Let(_, nme, _, tail) => + if (those.freeVars contains nme) { + val fresh = freshShadowed() + val thoseWithShadowed = Split.Let(false, nme, fresh, those) + val concatenated = these.copy(tail = rec(tail, thoseWithShadowed)) + Split.Let(false, fresh, nme, concatenated) + } else { + these.copy(tail = rec(tail, those)(vars + nme)) + } + case _: Split.Else => these + case Split.Nil => those.withoutBindings(vars) + } + } + rec(lhs, rhs)(Set.empty) + }(sp => s"concat => ${printSplit(sp)}") + /** * Normalize core abstract syntax to MLscript syntax. * @@ -26,20 +48,20 @@ trait Normalization { self: mlscript.pretyper.Traceable => split match { case Split.Cons(Branch(scrutinee, Pattern.Name(nme), continuation), tail) => println(s"alias $scrutinee => $nme") - Let(false, nme, scrutinee, normalizeToTerm(continuation ++ tail)) + Let(false, nme, scrutinee, normalizeToTerm(concat(continuation, tail))) // Skip Boolean conditions as scrutinees, because they only appear once. case Split.Cons(Branch(test, pattern @ Pattern.Class(nme @ Var("true"), N), continuation), tail) if Desugaring.isTestVar(test) => - val trueBranch = normalizeToTerm(continuation ++ tail) + val trueBranch = normalizeToTerm(concat(continuation, tail)) val falseBranch = normalizeToCaseBranches(tail) CaseOf(test, Case(nme, trueBranch, falseBranch)) case Split.Cons(Branch(scrutinee, pattern @ Pattern.Literal(literal), continuation), tail) => - val trueBranch = normalizeToTerm(specialize(continuation ++ tail, true)(scrutinee.symbol, pattern, scope)) + val trueBranch = normalizeToTerm(specialize(concat(continuation, tail), true)(scrutinee.symbol, pattern, scope)) val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutinee.symbol, pattern, scope)) CaseOf(scrutinee, Case(literal, trueBranch, falseBranch)) // false class parameters. Easy case Split.Cons(Branch(scrutinee, pattern @ Pattern.Class(nme, N), continuation), tail) => println(s"match $scrutinee with $nme (has location: ${nme.toLoc.isDefined})") - val trueBranch = normalizeToTerm(specialize(continuation ++ tail, true)(scrutinee.symbol, pattern, scope)) + val trueBranch = normalizeToTerm(specialize(concat(continuation, tail), true)(scrutinee.symbol, pattern, scope)) val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutinee.symbol, pattern, scope)) CaseOf(scrutinee, Case(nme, trueBranch, falseBranch)) case Split.Cons(Branch(scrutinee, pattern @ Pattern.Class(nme, S(parameters)), continuation), tail) => @@ -61,7 +83,7 @@ trait Normalization { self: mlscript.pretyper.Traceable => throw new NormalizationException(msg"Scrutinee is not a scrutinee symbol" -> scrutinee.toLoc :: Nil) } val trueBranch = trace("compute true branch"){ - normalizeToTerm(specialize(continuation ++ tail, true)(scrutinee.symbol, pattern, scope)) + normalizeToTerm(specialize(concat(continuation, tail), true)(scrutinee.symbol, pattern, scope)) }() val trueBranchWithBindings = Let( isRec = false, @@ -166,7 +188,16 @@ trait Normalization { self: mlscript.pretyper.Traceable => }) } // TODO: Other cases - case (_, _) => specialize(continuation ++ tail, true) + case (_, _) => + val specialized = specialize(continuation, true) + if (specialized.hasElse) { + println("tail is discarded") + specialized.withDiagnostic( + msg"Discarded split because of else branch" -> None // TODO: Synthesize locations + ) + } else { + specialized ++ specialize(tail, true) + } } // END match } else if (otherClassSymbol.baseTypes contains classSymbol) { println(s"Case 2: $otherClassName <: $className") @@ -215,13 +246,13 @@ trait Normalization { self: mlscript.pretyper.Traceable => ) } // Other patterns. Not implemented. - case (true | false, Split.Cons(Branch(otherScrutineeVar, pattern, continuation), tail)) => + case (_, Split.Cons(Branch(otherScrutineeVar, pattern, continuation), tail)) => throw new NormalizationException((msg"Unsupported pattern: ${pattern.toString}" -> pattern.toLoc) :: Nil) - case (true | false, let @ Split.Let(_, _, _, tail)) => - println("let binding, go next") + case (_, let @ Split.Let(_, nme, _, tail)) => + println(s"let binding $nme, go next") let.copy(tail = specialize(tail, matchOrNot)) // Ending cases remain the same. - case (true | false, end @ (Split.Else(_) | Split.Nil)) => println("the end"); end + case (_, end @ (Split.Else(_) | Split.Nil)) => println("the end"); end } }(showSplit(s"S${if (matchOrNot) "+" else "-"} ==>", _)) @@ -270,9 +301,6 @@ object Normalization { case Wildcard(term) => s"case _ =>" @: showTerm(term) case NoCases => Nil } - def showVar(`var`: Var): Str = - // If the variable is associated with a symbol, mark it with an asterisk *. - `var`.name + (`var`.symbolOption.fold("")(_ => "*")) + (`var`.toLoc.fold("")(_ => "†")) def showLet(let: Let): Lines = { val Let(rec, nme, rhs, body) = let (0, s"let ${showVar(nme)} = $rhs") :: showTerm(body) diff --git a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls index 97039e52..1a526497 100644 --- a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls +++ b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls @@ -51,7 +51,7 @@ fun example4(t, x) = // Oh no, x is captured by x from Base! // Because the branch is absorbed by the previous one. else 42 -//│ fun example4: (Base, anything) -> Int +//│ fun example4: (Base, Int) -> Int example4(Base(-1), 0) ~~> -1 example4(Base(1), 2) ~~> 42 @@ -61,9 +61,7 @@ example4(Base(1), 2) ~~> 42 //│ res //│ = 'passed' -// FIXME: Caused by variable shadowing during speicalization. example4(Derived(1), 4) ~~> 5 //│ "passed" //│ res -//│ Runtime error: -//│ Error: an error was thrown +//│ = 'passed' From 6c9cc89c59dc91fa4d1acf2b3c8ceec7f6a1158a Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Mon, 25 Dec 2023 23:36:06 +0800 Subject: [PATCH 024/143] Remove useless code due to previous commit Normalization stage is greatly simplified from now! --- .../main/scala/mlscript/ucs/DesugarUCS.scala | 2 +- shared/src/main/scala/mlscript/ucs/core.scala | 8 +- .../mlscript/ucs/stages/Desugaring.scala | 4 +- .../mlscript/ucs/stages/Normalization.scala | 120 +++--------------- 4 files changed, 22 insertions(+), 112 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index 0dcbd456..f33baa12 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -92,7 +92,7 @@ trait DesugarUCS extends Transformation case core.Pattern.Literal(literal) => Nil case core.Pattern.Name(nme) => nme -> nme.symbol :: Nil // For now, there should only be parameter-less class patterns. - case core.Pattern.Class(nme, maybeParameters) => Nil + case core.Pattern.Class(nme) => Nil case core.Pattern.Tuple(_) => ??? case core.Pattern.Record(_) => ??? } diff --git a/shared/src/main/scala/mlscript/ucs/core.scala b/shared/src/main/scala/mlscript/ucs/core.scala index 94af18ac..65a194a3 100644 --- a/shared/src/main/scala/mlscript/ucs/core.scala +++ b/shared/src/main/scala/mlscript/ucs/core.scala @@ -13,9 +13,7 @@ package object core { override def toString(): String = this match { case Pattern.Literal(literal) => literal.toString case Pattern.Name(Var(name)) => name - case Pattern.Class(Var(name), N) => name - case Pattern.Class(Var(name), S(parameters)) => - parameters.iterator.map(_.fold("_")(_.name)).mkString(s"$name(", ", ", ")") + case Pattern.Class(Var(name)) => name case Pattern.Tuple(fields) => fields.iterator.map(_.fold("_")(_.name)).mkString("(", ", ", ")") case Pattern.Record(Nil) => "{}" case Pattern.Record(entries) => entries.iterator.map { case (nme, als) => s"$nme: $als" }.mkString("{ ", ", ", " }") @@ -28,8 +26,8 @@ package object core { final case class Name(nme: Var) extends Pattern { override def children: Ls[Located] = nme :: Nil } - final case class Class(nme: Var, parameters: Opt[List[Opt[Var]]]) extends Pattern { - override def children: Ls[Located] = nme :: parameters.fold(Ls.empty[Located])(_.flatten) + final case class Class(nme: Var) extends Pattern { + override def children: Ls[Located] = nme :: Nil } final case class Tuple(elements: List[Opt[Var]]) extends Pattern { override def children: Ls[Located] = elements.flatten diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 75526abc..5bfecc01 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -105,7 +105,7 @@ trait Desugaring { self: PreTyper => * A shorthand for making a true pattern, which is useful in desugaring * Boolean conditions. */ - private def truePattern(implicit scope: Scope) = c.Pattern.Class(Var("true").withResolvedTypeSymbol, N) + private def truePattern(implicit scope: Scope) = c.Pattern.Class(Var("true").withResolvedTypeSymbol) private def freshSymbol(nme: Var): ValueSymbol = new ValueSymbol(nme, false) @@ -260,7 +260,7 @@ trait Desugaring { self: PreTyper => case N => (initialScope, identity(_: c.Split)) } // Last, return the scope with all bindings and a function that adds all matches and bindings to a split. - (scopeWithAll, split => c.Branch(scrutinee, c.Pattern.Class(pattern.nme, N), bindAll(split))) + (scopeWithAll, split => c.Branch(scrutinee, c.Pattern.Class(pattern.nme), bindAll(split))) } private def desugarPatternSplit(split: s.PatternSplit)(implicit scrutinee: Term, scope: Scope): c.Split = { diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 7f995371..0ccf28cb 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -50,7 +50,7 @@ trait Normalization { self: mlscript.pretyper.Traceable => println(s"alias $scrutinee => $nme") Let(false, nme, scrutinee, normalizeToTerm(concat(continuation, tail))) // Skip Boolean conditions as scrutinees, because they only appear once. - case Split.Cons(Branch(test, pattern @ Pattern.Class(nme @ Var("true"), N), continuation), tail) if Desugaring.isTestVar(test) => + case Split.Cons(Branch(test, pattern @ Pattern.Class(nme @ Var("true")), continuation), tail) if Desugaring.isTestVar(test) => val trueBranch = normalizeToTerm(concat(continuation, tail)) val falseBranch = normalizeToCaseBranches(tail) CaseOf(test, Case(nme, trueBranch, falseBranch)) @@ -59,48 +59,11 @@ trait Normalization { self: mlscript.pretyper.Traceable => val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutinee.symbol, pattern, scope)) CaseOf(scrutinee, Case(literal, trueBranch, falseBranch)) // false class parameters. Easy - case Split.Cons(Branch(scrutinee, pattern @ Pattern.Class(nme, N), continuation), tail) => + case Split.Cons(Branch(scrutinee, pattern @ Pattern.Class(nme), continuation), tail) => println(s"match $scrutinee with $nme (has location: ${nme.toLoc.isDefined})") val trueBranch = normalizeToTerm(specialize(concat(continuation, tail), true)(scrutinee.symbol, pattern, scope)) val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutinee.symbol, pattern, scope)) CaseOf(scrutinee, Case(nme, trueBranch, falseBranch)) - case Split.Cons(Branch(scrutinee, pattern @ Pattern.Class(nme, S(parameters)), continuation), tail) => - println(s"match $scrutinee with $pattern") - val unappliedVar = Var(s"args_${scrutinee.name}$$${nme.name}") - println(s"make unapplied var: $unappliedVar") - // Update the scrutinee symbol. The variable will be used in merging - // branches of the same pattern later. - scrutinee.symbol match { - case symbol: ScrutineeSymbol => - nme.symbolOption.flatMap(_.typeSymbolOption) match { - case N => throw new NormalizationException(msg"class name is not resolved" -> nme.toLoc :: Nil) - case S(typeSymbol) => - println(s"add unapplied var for ${typeSymbol.name}") - symbol.addUnappliedVar(typeSymbol, unappliedVar) - } - case _ => - // FIXME: I guess this should not happen. - throw new NormalizationException(msg"Scrutinee is not a scrutinee symbol" -> scrutinee.toLoc :: Nil) - } - val trueBranch = trace("compute true branch"){ - normalizeToTerm(specialize(concat(continuation, tail), true)(scrutinee.symbol, pattern, scope)) - }() - val trueBranchWithBindings = Let( - isRec = false, - name = unappliedVar, - rhs = { - val arguments = N -> Fld(FldFlags.empty, scrutinee) :: Nil - App(Sel(nme, Var("unapply")), Tup(arguments)) - }, - body = parameters.zipWithIndex.foldRight(trueBranch) { - case ((N, i), next) => next - case ((S(parameter), i), next) => Let(false, parameter, Sel(unappliedVar, Var(i.toString)), next) - } - ) - val falseBranch = trace("compute false branch"){ - normalizeToCaseBranches(specialize(tail, false)(scrutinee.symbol, pattern, scope)) - }() - CaseOf(scrutinee, Case(nme, trueBranchWithBindings, falseBranch)) case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => throw new NormalizationException((msg"Unsupported pattern: ${pattern.toString}" -> pattern.toLoc) :: Nil) case Split.Let(rec, Var("_"), rhs, tail) => normalizeToTerm(tail) @@ -123,13 +86,13 @@ trait Normalization { self: mlscript.pretyper.Traceable => private def specialize (split: Split, matchOrNot: Bool) (implicit scrutinee: Symbol, pattern: Pattern, scope: Scope): Split = - trace(s"S${if (matchOrNot) "+" else "-"} <== ${scrutinee.name} is ${pattern}") { + trace[Split](s"S${if (matchOrNot) "+" else "-"} <== ${scrutinee.name} is ${pattern}") { (matchOrNot, split) match { // Name patterns are translated to let bindings. case (true | false, Split.Cons(Branch(otherScrutineeVar, Pattern.Name(alias), continuation), tail)) => Split.Let(false, alias, otherScrutineeVar, specialize(continuation, matchOrNot)) // Class pattern. Positive. - case (true, split @ Split.Cons(head @ Branch(ScrutineeOnly(otherScrutinee), Pattern.Class(otherClassName, otherParameters), continuation), tail)) => + case (true, split @ Split.Cons(head @ Branch(ScrutineeOnly(otherScrutinee), Pattern.Class(otherClassName), continuation), tail)) => val otherClassSymbol = getClassLikeSymbol(otherClassName) lazy val specializedTail = { println(s"specialized next") @@ -138,67 +101,19 @@ trait Normalization { self: mlscript.pretyper.Traceable => if (scrutinee === otherScrutinee) { println(s"scrutinee: ${scrutinee.name} === ${otherScrutinee.name}") pattern match { - case Pattern.Class(className, parameters) => + case Pattern.Class(className) => val classSymbol = getClassLikeSymbol(className) if (classSymbol === otherClassSymbol) { println(s"Case 1: class name: $className === $otherClassName") - (parameters, otherParameters) match { - case (S(parameters), S(otherParameters)) => - if (parameters.length === otherParameters.length) { - println(s"same number of parameters: ${parameters.length}") - val addLetBindings = parameters.iterator.zip(otherParameters).zipWithIndex.foldLeft[Split => Split](identity) { - case (acc, N -> S(otherParameter) -> index) => - scrutinee match { // Well, it's a mistake to create a dedicated symbol for scrutinees. - case symbol: ScrutineeSymbol => - symbol.unappliedVarMap.get(otherClassSymbol) match { - case N => - println(symbol.unappliedVarMap) - die - case S(unappliedVar) => - println(s"we can create binding for ${otherParameter.name} at $index") - tail => Split.Let(false, otherParameter, Sel(unappliedVar, Var(index.toString)), acc(tail)) - } - case _ => - println(s"we can't create binding for ${otherParameter.name} at $index") - die - } - case (acc, S(parameter) -> S(otherParameter) -> index) if parameter.name =/= otherParameter.name => - println(s"different parameter names at $index: ${parameter.name} =/= ${otherParameter.name}") - tail => Split.Let(false, otherParameter, parameter, acc(tail)) - case (acc, _) => - println(s"other cases") - acc - } - // addLetBindings(specialize(continuation ++ tail, true)) - val specialized = addLetBindings(specialize(continuation, true)) - if (specialized.hasElse) { - println("tail is discarded") - specialized.withDiagnostic( - msg"Discarded split because of else branch" -> None // TODO: Synthesize locations - ) - } else { - specialized ++ specialize(tail, true) - } - } else { - throw new NormalizationException({ - msg"Mismatched numbers of parameters of ${className.name}:" -> otherClassName.toLoc :: - msg"There are ${"parameters".pluralize(parameters.length, true)}." -> Pattern.getParametersLoc(parameters) :: - msg"But there are ${"parameters".pluralize(otherParameters.length, true)}." -> Pattern.getParametersLoc(otherParameters) :: - Nil - }) - } - // TODO: Other cases - case (_, _) => - val specialized = specialize(continuation, true) - if (specialized.hasElse) { - println("tail is discarded") - specialized.withDiagnostic( - msg"Discarded split because of else branch" -> None // TODO: Synthesize locations - ) - } else { - specialized ++ specialize(tail, true) - } - } // END match + val specialized = specialize(continuation, true) + if (specialized.hasElse) { + println("tail is discarded") + specialized.withDiagnostic( + msg"Discarded split because of else branch" -> None // TODO: Synthesize locations + ) + } else { + specialized ++ specialize(tail, true) + } } else if (otherClassSymbol.baseTypes contains classSymbol) { println(s"Case 2: $otherClassName <: $className") split @@ -216,18 +131,15 @@ trait Normalization { self: mlscript.pretyper.Traceable => ) } // Class pattern. Negative - case (false, split @ Split.Cons(head @ Branch(ScrutineeOnly(otherScrutinee), Pattern.Class(otherClassName, otherParameters), continuation), tail)) => + case (false, split @ Split.Cons(head @ Branch(ScrutineeOnly(otherScrutinee), Pattern.Class(otherClassName), continuation), tail)) => val otherClassSymbol = getClassLikeSymbol(otherClassName) if (scrutinee === otherScrutinee) { println(s"scrutinee: ${scrutinee.name} === ${otherScrutinee.name}") pattern match { - case Pattern.Class(className, parameters) => + case Pattern.Class(className) => val classSymbol = getClassLikeSymbol(className) if (className === otherClassName) { println(s"Case 1: class name: $otherClassName === $className") - println(s"parameters:") - println(s" LHS: ${otherParameters.fold("N")(_.iterator.map(_.fold("N")(_.name)).mkString(", "))}") - println(s" RHS: ${parameters.fold("N")(_.iterator.map(_.fold("N")(_.name)).mkString(", "))}") specialize(tail, false) // TODO: Subsitute parameters to otherParameters } else if (otherClassSymbol.baseTypes contains classSymbol) { println(s"Case 2: class name: $otherClassName <: $className") From dbf627e867254608dce732e892ba45cf7a054d9e Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Tue, 26 Dec 2023 16:09:23 +0800 Subject: [PATCH 025/143] Add calculator example and fix a bug in transformation also support traversing let bindings --- shared/src/main/scala/mlscript/helpers.scala | 1 + .../scala/mlscript/pretyper/PreTyper.scala | 2 +- .../main/scala/mlscript/ucs/DesugarUCS.scala | 4 +- .../mlscript/ucs/stages/Desugaring.scala | 8 +- .../mlscript/ucs/stages/Transformation.scala | 69 +-- .../diff/pretyper/ucs/examples/Calculator.mls | 440 ++++++++++++++++++ .../pretyper/ucs/examples/Permutations.mls | 2 +- 7 files changed, 490 insertions(+), 36 deletions(-) create mode 100644 shared/src/test/diff/pretyper/ucs/examples/Calculator.mls diff --git a/shared/src/main/scala/mlscript/helpers.scala b/shared/src/main/scala/mlscript/helpers.scala index 524cae27..bbf2a826 100644 --- a/shared/src/main/scala/mlscript/helpers.scala +++ b/shared/src/main/scala/mlscript/helpers.scala @@ -788,6 +788,7 @@ trait Located { lazy val freeVars: Set[Var] = this match { case v: Var => Set.single(v) + case Sel(receiver, _) => receiver.freeVars case Let(true, nme, rhs, body) => body.freeVars ++ rhs.freeVars - nme case Let(false, nme, rhs, body) => body.freeVars - nme ++ rhs.freeVars case Lam(tup: Tup, body) => body.freeVars -- tup.freeVars diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index 361dea79..140cfd2b 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -134,7 +134,7 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D private def traverseLetBinding(symbol: ValueSymbol, rec: Bool, rhs: Term)(implicit scope: Scope): Unit = trace(s"traverseLetBinding(rec = $rec, ${symbol.name})") { - + traverseTerm(rhs)(scope + symbol) }() private def traverseStatements(statements: Ls[Statement], name: Str, parentScope: Scope): (Scope, TypeContents) = diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index f33baa12..3b440676 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -73,8 +73,10 @@ trait DesugarUCS extends Transformation val patternSymbols = traversePattern(scrutinee, pattern) traverseSplit(continuation)(scope.withEntries(patternSymbols)) traverseSplit(tail) - case Split.Let(_, name, _, tail) => + case Split.Let(_, name, rhs, tail) => println(s"found let binding: \"$name\"") + println(s"traverse rhs: $rhs") + traverseTerm(rhs) traverseSplit(tail)(scope + name.symbol) case Split.Else(default) => traverseTerm(default) case Split.Nil => println("the end") diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 5bfecc01..ab0bc7b2 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -14,7 +14,8 @@ import mlscript.Message, Message.MessageContext * syntax into core abstract syntax, which is more concise. We will make * following changes during this stage: * - * 1. Flatten nested patterns and generated bindings for nameless parameters. + * 1. Flatten nested patterns and generated bindings for nameless scrutinees + * and parameters of class-like patterns. * 2. Desugar variable patterns to plain let bindings. * 3. Desugar literal patterns to equivalent boolean expressions. * 4. Reassemble partial terms that are broken by "conditional splits". @@ -285,9 +286,10 @@ trait Desugaring { self: PreTyper => desugarTermSplit(head.continuation)(PartialTerm.Empty, scope) ++ rec(scrutinee, tail) case s.NamePattern(nme) => // Share the scrutinee's symbol with its aliases. - nme.symbol = scrutinee.symbol + // nme.symbol = scrutinee.symbol // <-- This currently causes a bug, reuse this line after we remove `ScrutineeSymbol`. + nme.symbol = new ValueSymbol(nme, false) val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope + nme.symbol) - c.Branch(scrutinee, c.Pattern.Name(nme), continuation) :: rec(scrutinee, tail) + c.Branch(scrutinee, c.Pattern.Name(nme), continuation) :: rec(scrutinee, tail)(scope + nme.symbol) case pattern @ s.ClassPattern(nme, fields) => println(s"find term symbol of $scrutinee in ${scope.showLocalSymbols}") scrutinee.symbol = scope.getTermSymbol(scrutinee.name).getOrElse(???) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index 531492c5..9b500556 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -36,8 +36,8 @@ trait Transformation { self: mlscript.pretyper.Traceable => case IfElse(expr) => Split.then(expr) case IfOpApp(lhs, Var("is"), rhs) => splitAnd(lhs) match { - case init :+ last => - init.foldRight[TermSplit](TermBranch.Match(last, transformPatternMatching(rhs)) |> Split.single) { + case tests :+ scrutinee => + tests.foldRight[TermSplit](TermBranch.Match(scrutinee, transformPatternMatching(rhs)) |> Split.single) { case (OperatorIs(scrutinee, pattern), acc) => TermBranch.Match(scrutinee, PatternBranch(transformPattern(pattern), acc) |> Split.single) |> Split.single case (test, acc) => TermBranch.Boolean(test, acc) |> Split.single @@ -90,35 +90,40 @@ trait Transformation { self: mlscript.pretyper.Traceable => case (op, rhs) => OperatorBranch.Binary(op, transformIfBody(rhs)) } - private def transformPatternMatching(body: IfBody)(implicit useNewDefs: Bool): PatternSplit = { - body match { - case IfThen(expr, rhs) => - separatePattern(expr) match { - case (pattern, S(extraTest)) => - PatternBranch(pattern, transformIfBody(IfThen(extraTest, rhs))) |> Split.single - case (pattern, N) => - PatternBranch(pattern, Split.default(rhs)) |> Split.single - } - case IfOpApp(lhs, Var("and"), rhs) => - separatePattern(lhs) match { - case (pattern, S(extraTest)) => - PatternBranch(transformPattern(lhs), ???) |> Split.single - case (pattern, N) => - PatternBranch(transformPattern(lhs), transformIfBody(rhs)) |> Split.single + /** + * Transform an `IfBody` into a `PatternSplit`. + */ + private def transformPatternMatching(body: IfBody)(implicit useNewDefs: Bool): PatternSplit = + trace(s"transformPatternMatching <== ${inspect.shallow(body)}") { + body match { + case IfThen(expr, rhs) => + separatePattern(expr) match { + case (pattern, S(extraTest)) => + PatternBranch(pattern, transformIfBody(IfThen(extraTest, rhs))) |> Split.single + case (pattern, N) => + PatternBranch(pattern, Split.default(rhs)) |> Split.single + } + case IfOpApp(lhs, Var("and"), rhs) => + println(s"lhs: ${inspect.deep(lhs)}") + separatePattern(lhs) match { + case (pattern, S(extraTest)) => + PatternBranch(pattern, TermBranch.Boolean(extraTest, transformIfBody(rhs)) |> Split.single) |> Split.single + case (pattern, N) => + PatternBranch(pattern, transformIfBody(rhs)) |> Split.single + } + case IfOpApp(lhs, op, rhs) => ??? // <-- Syntactic split of patterns are not supported. + case IfOpsApp(lhs, opsRhss) => ??? // <-- Syntactic split of patterns are not supported. + case IfLet(rec, nme, rhs, body) => rare + case IfBlock(lines) => lines.foldLeft(Split.empty[PatternBranch]) { + case (acc, L(body)) => acc ++ transformPatternMatching(body) + case (acc, R(NuFunDef(S(rec), nme, _, _, L(rhs)))) => + acc ++ Split.Let(rec, nme, rhs, Split.Nil) + case (acc, R(statement)) => + throw new TransformException(msg"Unexpected statement in an if block", statement.toLoc) } - case IfOpApp(lhs, op, rhs) => ??? - case IfOpsApp(lhs, opsRhss) => ??? - case IfLet(rec, nme, rhs, body) => rare - case IfBlock(lines) => lines.foldLeft(Split.empty[PatternBranch]) { - case (acc, L(body)) => acc ++ transformPatternMatching(body) - case (acc, R(NuFunDef(S(rec), nme, _, _, L(rhs)))) => - acc ++ Split.Let(rec, nme, rhs, Split.Nil) - case (acc, R(statement)) => - throw new TransformException(msg"Unexpected statement in an if block", statement.toLoc) + case IfElse(expr) => Split.default(expr) } - case IfElse(expr) => Split.default(expr) - } - } + }(_ => "transformPatternMatching ==>") private def transformPattern(term: Term)(implicit useNewDefs: Bool): Pattern = term match { case nme @ Var("true" | "false") => ConcretePattern(nme) @@ -135,11 +140,15 @@ trait Transformation { self: mlscript.pretyper.Traceable => case _ -> Fld(_, t ) => S(transformPattern(t)) }) // TODO: Support more patterns. - case _ => throw new TransformException(msg"Unknown pattern", term.toLoc) + case _ => + println(s"unknown pattern: $term") + throw new TransformException(msg"Unknown pattern", term.toLoc) } private def separatePattern(term: Term)(implicit useNewDefs: Bool): (Pattern, Opt[Term]) = { val (rawPattern, extraTest) = helpers.separatePattern(term, useNewDefs) + println("rawPattern: " + inspect.deep(rawPattern)) + println("extraTest: " + inspect.deep(extraTest)) (transformPattern(rawPattern), extraTest) } diff --git a/shared/src/test/diff/pretyper/ucs/examples/Calculator.mls b/shared/src/test/diff/pretyper/ucs/examples/Calculator.mls new file mode 100644 index 00000000..ff2005ed --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/examples/Calculator.mls @@ -0,0 +1,440 @@ +:PreTyper + +// This test file explores implementing a calculator using UCS. + +fun (++) concatOp(a, b) = concat(a)(b) +fun (|>) pipe(a, f) = f(a) +fun (!==) notEqual(x, y) = not(x === y) +let anyToString = toString +//│ fun (++) concatOp: (Str, Str) -> Str +//│ fun (|>) pipe: forall 'a 'b. ('a, 'a -> 'b) -> 'b +//│ fun (!==) notEqual: forall 'c. (Eql['c], 'c) -> Bool +//│ let anyToString: anything -> Str +//│ anyToString +//│ = [Function: toString] + +fun par(s, p) = if p then "(" ++ s ++ ")" else s +//│ fun par: (Str, Bool) -> Str + +type StringOps = { + length: Int, + charAt: Int => Str, + charCodeAt: Int => Int, + slice: Int => Str +} +declare fun String: nothing +let toStringOps: anything => StringOps = String +//│ type StringOps = {charAt: Int -> Str, charCodeAt: Int -> Int, length: Int, slice: Int -> Str} +//│ let toStringOps: anything -> StringOps +//│ fun String: nothing +//│ toStringOps +//│ = [Function: String] + +type Option[A] = Some[A] | None +class Some[A](value: A) +module None +//│ type Option[A] = None | Some[A] +//│ class Some[A](value: A) +//│ module None + +fun showOption(x) = if x is + Some(value) then "Some(" ++ toString(value) ++ ")" + None then "None" +fun mapOption(f, x) = if x is + Some(value) then Some(f(value)) + None then None +fun (??) getOrElse(x, default) = if x is + Some(value) then value + None then default +fun flatten(x) = if x is + Some(value) then value + other then other +//│ fun showOption: (None | Some[anything]) -> Str +//│ fun mapOption: forall 'a 'A. ('a -> 'A, None | Some['a]) -> (None | Some['A]) +//│ fun (??) getOrElse: forall 'b. (None | Some['b], 'b) -> 'b +//│ fun flatten: forall 'c. (Object & 'c & ~#Some | Some['c]) -> 'c + +type List[A] = Cons[A] | Nil +class Cons[A](head: A, tail: List[A]) +module Nil +fun (::) cons(head, tail) = Cons(head, tail) +fun reverse(xs) = + let aux(acc, xs) = + if xs is + Nil then acc + Cons(x, xs') then aux(x :: acc, xs') + aux(Nil, xs) +fun join(sep) = + let aux(acc, xs) = + if xs is + Nil then acc + Cons(x, xs') then aux(acc ++ sep ++ toString(x), xs') + (xs) => + if xs is + Cons(x, xs') then aux(toString(x), xs') + Nil then "" +fun showList(xs) = "[" ++ join(", ")(xs) ++ "]" +//│ type List[A] = Cons[A] | Nil +//│ class Cons[A](head: A, tail: List[A]) +//│ module Nil +//│ fun (::) cons: forall 'A. ('A, List['A]) -> Cons['A] +//│ fun reverse: forall 'A0 'A1. (Cons['A0] | Nil) -> (Cons['A1] | Nil) +//│ fun join: Str -> (forall 'A2. (Cons['A2] | Nil) -> Str) +//│ fun showList: forall 'A3. (Cons['A3] | Nil) -> Str +//│ where +//│ 'A0 <: 'A1 + +// _ +// | | _____ _____ _ __ +// | | / _ \ \/ / _ \ '__| +// | |__| __/> < __/ | +// |_____\___/_/\_\___|_| +// + +fun isDigit(n) = (48 <= n) && (n <= 57) +fun isBlank(n) = (n === 32) || (n === 9) || (n === 10) || (n === 13) +//│ fun isDigit: Num -> Bool +//│ fun isBlank: Eql[10 | 13 | 32 | 9] -> Bool + +fun scanInt(text: StringOps, at: Int): Option[[Int, Int]] = + let aux(acc, i: Int): Option[[Int, Int]] = if + i < 0 then None + i >= text.length then mapOption(n => [n, i], acc) + let c = text.charCodeAt(i) + isDigit(c) then aux(Some((acc ?? 0) * 10 + c - 48), i + 1) + else mapOption(n => [n, i], acc) + aux(None, at) +//│ fun scanInt: (text: StringOps, at: Int) -> Option[[Int, Int]] + +scanInt("a123" |> toStringOps, 0) |> showOption +scanInt("a123" |> toStringOps, 1) |> showOption +scanInt("a123" |> toStringOps, 2) |> showOption +scanInt("a123" |> toStringOps, 3) |> showOption +scanInt("a123" |> toStringOps, 4) |> showOption +//│ Str +//│ res +//│ = 'None' +//│ res +//│ = 'Some(123,4)' +//│ res +//│ = 'Some(23,4)' +//│ res +//│ = 'Some(3,4)' +//│ res +//│ = 'None' + +fun skipBlank(text: StringOps, at: Int): Int = + let aux(i: Int): Int = if + i >= text.length then i + isBlank(text.charCodeAt(i)) then aux(i + 1) + else i + aux(at) +//│ fun skipBlank: (text: StringOps, at: Int) -> Int + +skipBlank("abc" |> toStringOps, 0) +skipBlank("abc" |> toStringOps, 1) +skipBlank("abc" |> toStringOps, 2) +skipBlank(" \t\n\r123" |> toStringOps, 0) +//│ Int +//│ res +//│ = 0 +//│ res +//│ = 1 +//│ res +//│ = 2 +//│ res +//│ = 5 + +class Span(val start: Int, val end: Int) +//│ class Span(start: Int, end: Int) + +abstract class Token(val span: Span) +class IntegerLiteral(value: Int, span2: Span) extends Token(span2) { // It seems that constructor parameters is not sanitized + fun toString(): Str = "IntegerLiteral(" ++ anyToString(value) ++ ")" +} +class LeftParen(at: Int) extends Token(Span(at, at + 1)) { + fun toString(): Str = "LeftParen" +} +class RightParen(at: Int) extends Token(Span(at, at + 1)) { + fun toString(): Str = "RightParen" +} +class BinaryOperator(value: Str, val bp: Int, at: Int) extends Token(Span(at, at + 1)) { + fun toString(): Str = "BinaryOperator(" ++ value ++ ", " ++ anyToString(bp) ++ ")" +} +class EndOfInput(at: Int) extends Token(Span(at, at)) { + fun toString(): Str = "EndOfInput" +} +class UnknownInput(rest: Str, at: Int, length: Int) extends Token(Span(at, at + length)) { + fun toString(): Str = "UnknownInput(" ++ rest ++ ")" +} +//│ abstract class Token(span: Span) +//│ class IntegerLiteral(value: Int, span2: Span) extends Token { +//│ fun toString: () -> Str +//│ } +//│ class LeftParen(at: Int) extends Token { +//│ fun toString: () -> Str +//│ } +//│ class RightParen(at: Int) extends Token { +//│ fun toString: () -> Str +//│ } +//│ class BinaryOperator(value: Str, bp: Int, at: Int) extends Token { +//│ fun toString: () -> Str +//│ } +//│ class EndOfInput(at: Int) extends Token { +//│ fun toString: () -> Str +//│ } +//│ class UnknownInput(rest: Str, at: Int, length: Int) extends Token { +//│ fun toString: () -> Str +//│ } + +fun scanToken(text: StringOps, at: Int): [Token, Int] = + if + let at' = skipBlank(text, at) + at' >= text.length then [EndOfInput(at'), at'] + let head = text.charCodeAt(at') + head === + 37 then [BinaryOperator("%", 20, at'), at' + 1] + 40 then [LeftParen(at'), at' + 1] + 41 then [RightParen(at'), at' + 1] + 42 then [BinaryOperator("*", 20, at'), at' + 1] + 43 then [BinaryOperator("+", 10, at'), at' + 1] + 45 then [BinaryOperator("-", 10, at'), at' + 1] + 47 then [BinaryOperator("/", 20, at'), at' + 1] + (48 <= head) && (head <= 57) and + scanInt(text, at') is Some(res) then + let at'' = res.1 + [IntegerLiteral(res.0, Span(at', at'')), at''] + else [UnknownInput(text.slice(at'), at', text.length - at'), at'] +//│ fun scanToken: (text: StringOps, at: Int) -> [Token, Int] + +scanToken("bruh" |> toStringOps, 0) +scanToken("1" |> toStringOps, 0) +scanToken("+" |> toStringOps, 0) +scanToken(" 42" |> toStringOps, 0) +//│ [Token, Int] +//│ res +//│ = [ UnknownInput {}, 0 ] +//│ res +//│ = [ IntegerLiteral {}, 1 ] +//│ res +//│ = [ BinaryOperator {}, 1 ] +//│ res +//│ = [ IntegerLiteral {}, 4 ] + +fun tokenize(str: Str): List[Token] = + let text = str |> toStringOps + let aux(acc, at) = if + let res = scanToken(text, at) // TODO: Tuple pattern + let token = res.0 + let at' = res.1 + token is + UnknownInput then (token :: acc) |> reverse + EndOfInput then acc |> reverse + else aux(token :: acc, at') + aux(Nil, 0) +//│ fun tokenize: (str: Str) -> List[Token] + +tokenize("0") |> showList +tokenize("1 + 2 * 3") |> showList +tokenize("bruh") |> showList +//│ Str +//│ res +//│ = '[IntegerLiteral(0)]' +//│ res +//│ = '[IntegerLiteral(1), BinaryOperator(+, 10), IntegerLiteral(2), BinaryOperator(*, 20), IntegerLiteral(3)]' +//│ res +//│ = '[UnknownInput(bruh)]' + +// ____ _ +// | _ \ __ _ _ __ ___(_)_ __ __ _ +// | |_) / _` | '__/ __| | '_ \ / _` | +// | __/ (_| | | \__ \ | | | | (_| | +// |_| \__,_|_| |___/_|_| |_|\__, | +// |___/ + +type Expression = IntegerLiteral | BinaryExpression +class BinaryExpression(op: BinaryOperator, left: Expression, right: Expression) +//│ type Expression = BinaryExpression | IntegerLiteral +//│ class BinaryExpression(op: BinaryOperator, left: Expression, right: Expression) + +fun bindingPower(t: Expression): Int = + if t is + IntegerLiteral then 30 + BinaryExpression(op, _, _) then op.bp +//│ fun bindingPower: (t: Expression) -> Int + +fun showExpression(t: Expression): Str = + if t is + IntegerLiteral(n) then anyToString(n) + BinaryExpression(BinaryOperator(op, bp), left, right) then + let lbp = bindingPower of left + let rbp = bindingPower of right + par(showExpression(left), lbp < bp) ++ " " ++ op ++ " " ++ par(showExpression(right), rbp < bp) +//│ fun showExpression: (t: Expression) -> Str + +let s = Span(0, 0) +IntegerLiteral(42, s) |> showExpression +let t1 = BinaryExpression(BinaryOperator("+", 10, 0), IntegerLiteral(1, s), IntegerLiteral(2, s)) +t1 |> showExpression +let t2 = BinaryExpression(BinaryOperator("*", 20, 0), t1, IntegerLiteral(3, s)) +t2 |> showExpression +let t3 = BinaryExpression(BinaryOperator("*", 20, 0), t2, IntegerLiteral(4, s)) +t3 |> showExpression +//│ let s: Span +//│ let t1: BinaryExpression +//│ let t2: BinaryExpression +//│ let t3: BinaryExpression +//│ Str +//│ s +//│ = Span {} +//│ res +//│ = '42' +//│ t1 +//│ = BinaryExpression {} +//│ res +//│ = '1 + 2' +//│ t2 +//│ = BinaryExpression {} +//│ res +//│ = '(1 + 2) * 3' +//│ t3 +//│ = BinaryExpression {} +//│ res +//│ = '(1 + 2) * 3 * 4' + +type ParseResult[A] = Some[A] | Failed +class Failed(message: Str) +fun showParseResult(r: ParseResult['A]) = if r is + Some(value) then "Some(" ++ toString(value) ++ ")" + Failed(message) then "Failed(" ++ message ++ ")" +fun (?>) mapParseResult(x, f) = if x is + Some(value) then Some(f(value)) + failed then failed +//│ type ParseResult[A] = Failed | Some[A] +//│ class Failed(message: Str) +//│ fun showParseResult: forall 'A. (r: ParseResult['A]) -> Str +//│ fun (?>) mapParseResult: forall 'a 'b 'A0. (Object & 'a & ~#Some | Some['b], 'b -> 'A0) -> (Some['A0] | 'a) + +fun showParsedExpression(r: ParseResult[Expression]) = if r is + Some(value) then "Some(" ++ showExpression(value) ++ ")" + Failed(message) then "Failed(" ++ message ++ ")" +//│ fun showParsedExpression: (r: ParseResult[Expression]) -> Str + +fun lastPosition(t: Expression): Int = + if t is + IntegerLiteral(_, span) then span.end + BinaryExpression(_, _, right) then lastPosition(right) +//│ fun lastPosition: (t: Expression) -> Int + +fun parseAtom(ts: List[Token]): ParseResult[[Expression, List[Token]]] = + if ts is + Cons(IntegerLiteral(n, span), ts') then Some([IntegerLiteral(n, span), ts']) + Cons(LeftParen, ts') and parseExpression(0, ts') is + Some(res) and res.1 is + Cons(RightParen, ts'') then Some([res.0, ts'']) + else Failed("Expected a right parenthesis at " ++ toString(lastPosition of res.0)) + failed then failed + Cons(token, _) then Failed("Unexpected token " ++ toString(token) ++ " at " ++ toString(token.span.start)) + Nil then Failed("Unexpected end of input") +fun parseExpression(bp: Int, ts: List[Token]): ParseResult[[Expression, List[Token]]] = + if parseAtom(ts) is + Some(res) then + let aux(left, ts) = if ts is + Cons(BinaryOperator(op, bp', opAt), ts') and bp < bp' and + parseExpression(bp', ts') is + Some(res) then + let right = res.0 + let ts'' = res.1 + aux(BinaryExpression(BinaryOperator(op, bp', opAt), left, right), ts'') + failed then failed + else Some([left, ts]) + let leftmost = res.0 + let ts' = res.1 + aux(leftmost, ts') + failed then failed +fun parse(source: Str): ParseResult[Expression] = + if parseExpression(0, tokenize(source)) is + Some(res) and res.1 is + Nil then Some(res.0) + else Failed("Unexpected token: " ++ showList(res.1) ++ " at " ++ toString(lastPosition of res.0)) + failed then failed +//│ fun parseAtom: (ts: List[Token]) -> ParseResult[[Expression, List[Token]]] +//│ fun parseExpression: (bp: Int, ts: List[Token]) -> ParseResult[[Expression, List[Token]]] +//│ fun parse: (source: Str) -> ParseResult[Expression] + +parse("1 + 2 * 3") |> showParsedExpression +parse("(1 + 2) * 3") |> showParsedExpression +parse("2 * (1 + 3 + 5 + 7 + 9 + 11) - 2 - 4 - 6") |> showParsedExpression +parse("2 * (1 + 3) * (5 + 7) * (9 - 11)") |> showParsedExpression +parse("(((((((((42)))))))))") |> showParsedExpression +//│ Str +//│ res +//│ = 'Some(1 + 2 * 3)' +//│ res +//│ = 'Some((1 + 2) * 3)' +//│ res +//│ = 'Some(2 * (1 + 3 + 5 + 7 + 9 + 11) - 2 - 4 - 6)' +//│ res +//│ = 'Some(2 * (1 + 3) * (5 + 7) * (9 - 11))' +//│ res +//│ = 'Some(42)' + +parse("1 + ") |> showParsedExpression +parse("1 bruh") |> showParsedExpression +parse("1 * (2 + 3") |> showParsedExpression +parse("1 - bruh") |> showParsedExpression +//│ Str +//│ res +//│ = 'Failed(Unexpected end of input)' +//│ res +//│ = 'Failed(Unexpected token: [UnknownInput(bruh)] at 1)' +//│ res +//│ = 'Failed(Expected a right parenthesis at 10)' +//│ res +//│ = 'Failed(Unexpected token UnknownInput(bruh) at 4)' + +// _____ _ _ _ +// | ____|_ ____ _| |_ _ __ _| |_(_) ___ _ __ +// | _| \ \ / / _` | | | | |/ _` | __| |/ _ \| '_ \ +// | |___ \ V / (_| | | |_| | (_| | |_| | (_) | | | | +// |_____| \_/ \__,_|_|\__,_|\__,_|\__|_|\___/|_| |_| +// + +fun evaluate(t: Expression): Option[Int] = + if t is + IntegerLiteral(n) then Some(n) + BinaryExpression(BinaryOperator(op, _, _), left, right) and + evaluate(left) is Some(leftResult) and + evaluate(right) is Some(rightResult) and op === + "+" then Some(leftResult + rightResult) + "-" then Some(leftResult - rightResult) + "*" then Some(leftResult * rightResult) + // "/" then Some(leftResult / rightResult) + "%" then Some(leftResult % rightResult) + else None +//│ fun evaluate: (t: Expression) -> Option[Int] + +fun evaluation(source: Str): Str = + if parse(source) is + Some(expression) and evaluate(expression) is + Some(result) then toString(result) + None then "Evaluation failed" + Failed(message) then "Parsing failed: " ++ message +//│ fun evaluation: (source: Str) -> Str + +evaluation("1 + 2 * 3") +evaluation("(((((42)))))") +evaluation("1 * (3 + 4) - 5") +evaluation("1 + ") +evaluation("1 bruh") +//│ Str +//│ res +//│ = '7' +//│ res +//│ = '42' +//│ res +//│ = '2' +//│ res +//│ = 'Parsing failed: Unexpected end of input' +//│ res +//│ = 'Parsing failed: Unexpected token: [UnknownInput(bruh)] at 1' diff --git a/shared/src/test/diff/pretyper/ucs/examples/Permutations.mls b/shared/src/test/diff/pretyper/ucs/examples/Permutations.mls index a42d3237..0497d873 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/Permutations.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/Permutations.mls @@ -62,7 +62,7 @@ fun insertAllPositions(z) = let nu = ((prev :+ z) ::: xs) aux(prev :+ head, nu :: acc, tail) xs => aux(Nil, Nil, xs) -//│ fun insertAllPositions: forall 'a. 'a -> (forall 'A. (Cons['A] | Nil) -> (Cons[List['A | 'a]] & {Cons#T <: List['A | 'a]} | Nil)) +//│ fun insertAllPositions: forall 'a. 'a -> (forall 'A. (Cons['A] | Nil) -> (Cons[List['a | 'A]] & {Cons#T <: List['a | 'A]} | Nil)) insertAllPositions(0)(1 :: 2 :: 3 :: Nil) |> showListList //│ Str From 9cce6241f62b72753f2fe86c1befe217171317d9 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Tue, 26 Dec 2023 21:50:35 +0800 Subject: [PATCH 026/143] Support simple tuple patterns which is good for making examples --- .../scala/mlscript/pretyper/PreTyper.scala | 11 +- .../main/scala/mlscript/pretyper/Symbol.scala | 12 ++ .../mlscript/ucs/stages/Desugaring.scala | 123 +++++++++++++----- .../mlscript/ucs/stages/Normalization.scala | 88 ++++++++----- .../src/test/diff/pretyper/ucs/DualOption.mls | 23 ++++ .../test/diff/pretyper/ucs/TuplePattern.mls | 3 +- .../diff/pretyper/ucs/examples/Calculator.mls | 30 ++--- .../pretyper/ucs/patterns/SimpleTuple.mls | 94 +++++++++++++ 8 files changed, 292 insertions(+), 92 deletions(-) create mode 100644 shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index 140cfd2b..2cfcaaaa 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -17,11 +17,12 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D trace(s"extractParameters <== ${inspect.deep(fields)}") { fields match { case Tup(arguments) => - arguments.map { - case (S(nme: Var), Fld(_, _)) => new ValueSymbol(nme, false) - case (_, Fld(_, nme: Var)) => new ValueSymbol(nme, false) - case (_, Fld(_, Bra(false, nme: Var))) => new ValueSymbol(nme, false) - case (_, _) => ??? + arguments.flatMap { + case (S(nme: Var), Fld(_, _)) => new ValueSymbol(nme, false) :: Nil + case (_, Fld(_, nme: Var)) => new ValueSymbol(nme, false) :: Nil + case (_, Fld(_, Bra(false, nme: Var))) => new ValueSymbol(nme, false) :: Nil + case (_, Fld(_, tuple @ Tup(_))) => extractParameters(tuple) + case (_, Fld(_, _)) => ??? } case PlainTup(arguments @ _*) => arguments.map { diff --git a/shared/src/main/scala/mlscript/pretyper/Symbol.scala b/shared/src/main/scala/mlscript/pretyper/Symbol.scala index 543d4fec..5763876f 100644 --- a/shared/src/main/scala/mlscript/pretyper/Symbol.scala +++ b/shared/src/main/scala/mlscript/pretyper/Symbol.scala @@ -85,6 +85,10 @@ package object symbol { // Urgh, let's do this in the next refactor. // I really should move these imperative and stateful functions to a // separate class! + val tupleSubScrutineeMap: MutMap[Int, MutMap[Var, ValueSymbol]] = MutMap.empty + // Note that the innermost map is a map from variable names to symbols. + // Sometimes a class parameter may have many names. We maintain the + // uniqueness of the symbol for now. def getSubScrutineeSymbolOrElse( classLikeSymbol: TypeSymbol, @@ -95,6 +99,14 @@ package object symbol { subScrutineeMap.getOrElseUpdate(classLikeSymbol, MutMap.empty) .getOrElseUpdate(index, MutMap.empty) .getOrElseUpdate(name, default) + + def getTupleSubScrutineeSymbolOrElse( + index: Int, + name: Var, // <-- Remove this parameter after we remove `ScrutineeSymbol`. + default: => ValueSymbol + ): ValueSymbol = + tupleSubScrutineeMap.getOrElseUpdate(index, MutMap.empty) + .getOrElseUpdate(name, default) def addMatchedClass(symbol: TypeSymbol, loc: Opt[Loc]): Unit = { matchedClasses.getOrElseUpdate(symbol, Buffer.empty) ++= loc diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index ab0bc7b2..a68ca224 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -58,7 +58,7 @@ trait Desugaring { self: PreTyper => * `args_x$Cons`. */ private def makeUnappliedVar(scrutinee: Var, className: Var): Var = - Var(s"args_${scrutinee.name}$$${className.name}") + Var(s"$unappliedPrefix${scrutinee.name}$$${className.name}") // I plan to integrate scrutinee symbols into a field of `ValueSymbol`. // Because each `ValueSymbol` can be matched in multiple UCS expressions. @@ -177,21 +177,21 @@ trait Desugaring { self: PreTyper => ) private def flattenClassParameters(parentScrutinee: Var, parentClassLikeSymbol: TypeSymbol, parameters: Ls[Opt[s.Pattern]]): Ls[Opt[Var -> Opt[s.Pattern]]] = - parameters.zipWithIndex.map { + parameters.iterator.zipWithIndex.map { case (N, _) => N case (S(s.NamePattern(name)), index) => val symbol = parentScrutinee.getScrutineeSymbol.getSubScrutineeSymbolOrElse( parentClassLikeSymbol, index, name, new ValueSymbol(name, false) ) S(name.withSymbol(symbol) -> N) - case (S(parameterPattern @ (s.ClassPattern(_, _) | s.LiteralPattern(_))), index) => + case (S(parameterPattern @ (s.ClassPattern(_, _) | s.LiteralPattern(_) | s.TuplePattern(_))), index) => val scrutinee = freshScrutinee(parentScrutinee, parentClassLikeSymbol.name, index) val symbol = parentScrutinee.getScrutineeSymbol.getSubScrutineeSymbolOrElse( parentClassLikeSymbol, index, scrutinee, new ValueSymbol(scrutinee, false) ) S(scrutinee.withSymbol(symbol) -> S(parameterPattern)) case _ => ??? // Other patterns are not implemented yet. - } + }.toList /** * Recursively decompose and flatten a possibly nested class pattern. Any @@ -211,7 +211,7 @@ trait Desugaring { self: PreTyper => * @param initialScope the scope before flattening the class pattern * @return a tuple of the augmented scope and a function that wrap a split */ - private def flattenClassPattern(pattern: s.ClassPattern, scrutinee: Var, initialScope: Scope): (Scope, c.Split => c.Branch) = { + private def desugarClassPattern(pattern: s.ClassPattern, scrutinee: Var, initialScope: Scope): (Scope, c.Split => c.Branch) = { val scrutineeSymbol = scrutinee.getScrutineeSymbol val patternClassSymbol = pattern.nme.resolveTypeSymbol(initialScope) // Most importantly, we need to add the class to the list of matched classes. @@ -234,29 +234,7 @@ trait Desugaring { self: PreTyper => bindNextParameter.andThen { c.Split.Let(false, parameter, Sel(unapp, Var(index.toString)), _) } }.andThen { c.Split.Let(false, unapp, makeUnapplyCall(scrutinee, pattern.nme), _): c.Split } val scopeWithClassParameters = initialScope ++ (unapp.symbol :: nestedPatterns.flatMap(_.map(_._1.symbol))) - // Second, collect bindings from sub-patterns and accumulate a function - // that add let bindings to a split (we call it "binder"). - nestedPatterns.foldLeft((scopeWithClassParameters, bindClassParameters)) { - // If this parameter is not matched with a sub-pattern, then we do - // nothing and pass on scope and binder. - case (acc, S(_ -> N)) => acc - // If this sub-pattern is a class pattern, we need to recursively flatten - // the class pattern. We will get a scope with all bindings and a function - // that adds all bindings to a split. The scope can be passed on to the - // next sub-pattern. The binder needs to be composed with the previous - // binder. - case ((scope, bindPrevious), S(nme -> S(pattern: s.ClassPattern))) => - val (scopeWithNestedAll, bindNestedAll) = flattenClassPattern(pattern, nme, scope) - (scopeWithNestedAll, split => bindPrevious(bindNestedAll(split) :: c.Split.Nil)) - case ((scope, bindPrevious), S(nme -> S(pattern: s.LiteralPattern))) => - val test = freshTest().withFreshSymbol - (scope + test.symbol, makeLiteralTest(test, nme, pattern.literal)(scope).andThen(bindPrevious)) - // Well, other patterns are not supported yet. - case (acc, S((nme, pattern))) => ??? - // If this parameter is empty (e.g. produced by wildcard), then we do - // nothing and pass on scope and binder. - case (acc, N) => acc - } + desugarNestedPatterns(nestedPatterns, scopeWithClassParameters, bindClassParameters) // If there is no parameter, then we are done. case N => (initialScope, identity(_: c.Split)) } @@ -264,6 +242,78 @@ trait Desugaring { self: PreTyper => (scopeWithAll, split => c.Branch(scrutinee, c.Pattern.Class(pattern.nme), bindAll(split))) } + /** + * This function collects bindings from nested patterns and accumulate a + * function that add let bindings to a split (we call such function a + * "binder"). This function is supposed to be called from pattern desugaring + * functions. + * + * @param nestedPatterns nested patterns are a list of sub-scrutinees and + * corresponding sub-patterns + * @param scopeWithScrutinees a scope with all sub-scrutinees + * @param bindScrutinees a function that adds all bindings to a split + */ + private def desugarNestedPatterns( + nestedPatterns: Ls[Opt[Var -> Opt[s.Pattern]]], + scopeWithScrutinees: Scope, + bindScrutinees: c.Split => c.Split + ): (Scope, c.Split => c.Split) = { + nestedPatterns.foldLeft((scopeWithScrutinees, bindScrutinees)) { + // If this parameter is not matched with a sub-pattern, then we do + // nothing and pass on scope and binder. + case (acc, S(_ -> N)) => acc + // If this sub-pattern is a class pattern, we need to recursively flatten + // the class pattern. We will get a scope with all bindings and a function + // that adds all bindings to a split. The scope can be passed on to the + // next sub-pattern. The binder needs to be composed with the previous + // binder. + case ((scope, bindPrevious), S(nme -> S(pattern: s.ClassPattern))) => + val (scopeWithNestedAll, bindNestedAll) = desugarClassPattern(pattern, nme, scope) + (scopeWithNestedAll, split => bindPrevious(bindNestedAll(split) :: c.Split.Nil)) + case ((scope, bindPrevious), S(nme -> S(pattern: s.LiteralPattern))) => + val test = freshTest().withFreshSymbol + (scope + test.symbol, makeLiteralTest(test, nme, pattern.literal)(scope).andThen(bindPrevious)) + case ((scope, bindPrevious), S(nme -> S(s.TuplePattern(fields)))) => + val (scopeWithNestedAll, bindNestedAll) = desugarTuplePattern(fields, nme, scope) + (scopeWithNestedAll, bindNestedAll.andThen(bindPrevious)) + // Well, other patterns are not supported yet. + case (acc, S((nme, pattern))) => ??? + // If this parameter is empty (e.g. produced by wildcard), then we do + // nothing and pass on scope and binder. + case (acc, N) => acc + } + } + + private def flattenTupleFields(parentScrutinee: Var, fields: Ls[Opt[s.Pattern]]): Ls[Opt[Var -> Opt[s.Pattern]]] = + fields.iterator.zipWithIndex.map { + case (N, _) => N + case (S(s.NamePattern(name)), index) => + val symbol = parentScrutinee.getScrutineeSymbol.getTupleSubScrutineeSymbolOrElse( + index, name, new ValueSymbol(name, false) + ) + S(name.withSymbol(symbol) -> N) + case (S(parameterPattern @ (s.ClassPattern(_, _) | s.LiteralPattern(_) | s.TuplePattern(_))), index) => + val scrutinee = freshScrutinee(parentScrutinee, "Tuple$2", index) + val symbol = parentScrutinee.getScrutineeSymbol.getTupleSubScrutineeSymbolOrElse( + index, scrutinee, new ValueSymbol(scrutinee, false) + ) + S(scrutinee.withSymbol(symbol) -> S(parameterPattern)) + case _ => ??? + }.toList + + private def desugarTuplePattern(fields: Ls[Opt[s.Pattern]], scrutinee: Var, initialScope: Scope): (Scope, c.Split => c.Split) = { + val scrutineeSymbol = scrutinee.getScrutineeSymbol + val nestedPatterns = flattenTupleFields(scrutinee, fields) + val bindTupleFields = nestedPatterns.iterator.zipWithIndex.foldRight[c.Split => c.Split](identity) { + case ((N, _), bindNextField) => bindNextField + case ((S(parameter -> _), index), bindNextField) => + val indexVar = Var(index.toString).withLoc(parameter.toLoc) + bindNextField.andThen { c.Split.Let(false, parameter, Sel(scrutinee, indexVar), _) } + } + val scopeWithTupleFields = initialScope ++ nestedPatterns.flatMap(_.map(_._1.symbol)) + desugarNestedPatterns(nestedPatterns, scopeWithTupleFields, bindTupleFields) + } + private def desugarPatternSplit(split: s.PatternSplit)(implicit scrutinee: Term, scope: Scope): c.Split = { def rec(scrutinee: Var, split: s.PatternSplit)(implicit scope: Scope): c.Split = split match { case s.Split.Cons(head, tail) => @@ -293,10 +343,19 @@ trait Desugaring { self: PreTyper => case pattern @ s.ClassPattern(nme, fields) => println(s"find term symbol of $scrutinee in ${scope.showLocalSymbols}") scrutinee.symbol = scope.getTermSymbol(scrutinee.name).getOrElse(???) - val (scopeWithAll, bindAll) = flattenClassPattern(pattern, scrutinee, scope) + val (scopeWithAll, bindAll) = desugarClassPattern(pattern, scrutinee, scope) val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scopeWithAll) bindAll(continuation) :: rec(scrutinee, tail) - case s.TuplePattern(fields) => ??? + case s.TuplePattern(fields) => + scrutinee.symbol = scope.getTermSymbol(scrutinee.name).getOrElse(???) + val (scopeWithAll, bindAll) = desugarTuplePattern(fields, scrutinee, scope) + val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scopeWithAll) + val withBindings = bindAll(continuation) + if (withBindings.hasElse) { + withBindings + } else { + withBindings ++ rec(scrutinee, tail) + } case s.RecordPattern(entries) => ??? } case s.Split.Let(isRec, nme, rhs, tail) => @@ -327,8 +386,12 @@ object Desugaring { val cachePrefix = "cache$" val scrutineePrefix = "scrut$" val testPrefix = "test$" + val unappliedPrefix = "args_" def isCacheVar(nme: Var): Bool = nme.name.startsWith(cachePrefix) def isScrutineeVar(nme: Var): Bool = nme.name.startsWith(scrutineePrefix) def isTestVar(nme: Var): Bool = nme.name.startsWith(testPrefix) + def isUnappliedVar(nme: Var): Bool = nme.name.startsWith(unappliedPrefix) + def isGeneratedVar(nme: Var): Bool = + isCacheVar(nme) || isScrutineeVar(nme) || isTestVar(nme) || isUnappliedVar(nme) } \ No newline at end of file diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 0ccf28cb..ba6c2e37 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -16,25 +16,28 @@ trait Normalization { self: mlscript.pretyper.Traceable => private val freshShadowed = new Desugaring.VariableGenerator("shadowed$") - def concat(lhs: Split, rhs: Split): Split = traceNot(s"concat <== ${printSplit(lhs)} ${printSplit(rhs)}") { - def rec(these: Split, those: Split)(implicit vars: Set[Var]): Split = { - these match { - case these @ Split.Cons(_, tail) => these.copy(tail = rec(tail, those)) - case these @ Split.Let(_, nme, _, tail) => - if (those.freeVars contains nme) { - val fresh = freshShadowed() - val thoseWithShadowed = Split.Let(false, nme, fresh, those) - val concatenated = these.copy(tail = rec(tail, thoseWithShadowed)) - Split.Let(false, fresh, nme, concatenated) - } else { - these.copy(tail = rec(tail, those)(vars + nme)) - } - case _: Split.Else => these - case Split.Nil => those.withoutBindings(vars) - } - } - rec(lhs, rhs)(Set.empty) - }(sp => s"concat => ${printSplit(sp)}") + def concatImpl(these: Split, those: Split)(implicit generatedVars: Set[Var]): Split = + if (these.hasElse) these else (these match { + case these @ Split.Cons(_, tail) => these.copy(tail = concatImpl(tail, those)) + case these @ Split.Let(_, nme, _, tail) => + if (those.freeVars contains nme) { + val fresh = freshShadowed() + val thoseWithShadowed = Split.Let(false, nme, fresh, those) + val concatenated = these.copy(tail = concatImpl(tail, thoseWithShadowed)) + Split.Let(false, fresh, nme, concatenated) + } else { + these.copy(tail = concatImpl(tail, those)(generatedVars + nme)) + } + case _: Split.Else => these + case Split.Nil => those.withoutBindings(generatedVars) + }) + + def concat(these: Split, those: Split)(implicit vars: Set[Var]): Split = + trace(s"concat <== ${vars.mkString("{", ", ", "}")}") { + println(s"these: ${printSplit(these)}") + println(s"those: ${printSplit(those)}") + concatImpl(these, those) + }(sp => s"concat => ${printSplit(sp)}") /** * Normalize core abstract syntax to MLscript syntax. @@ -42,9 +45,9 @@ trait Normalization { self: mlscript.pretyper.Traceable => * @param split the split to normalize * @return the normalized term */ - @inline def normalize(split: Split)(implicit scope: Scope): Term = normalizeToTerm(split) + @inline protected def normalize(split: Split): Term = normalizeToTerm(split)(Set.empty) - private def normalizeToTerm(split: Split)(implicit scope: Scope): Term = trace("normalizeToTerm <==") { + private def normalizeToTerm(split: Split)(implicit generatedVars: Set[Var]): Term = trace("normalizeToTerm <==") { split match { case Split.Cons(Branch(scrutinee, Pattern.Name(nme), continuation), tail) => println(s"alias $scrutinee => $nme") @@ -55,37 +58,52 @@ trait Normalization { self: mlscript.pretyper.Traceable => val falseBranch = normalizeToCaseBranches(tail) CaseOf(test, Case(nme, trueBranch, falseBranch)) case Split.Cons(Branch(scrutinee, pattern @ Pattern.Literal(literal), continuation), tail) => - val trueBranch = normalizeToTerm(specialize(concat(continuation, tail), true)(scrutinee.symbol, pattern, scope)) - val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutinee.symbol, pattern, scope)) + val trueBranch = normalizeToTerm(specialize(concat(continuation, tail), true)(scrutinee.symbol, pattern)) + val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutinee.symbol, pattern)) CaseOf(scrutinee, Case(literal, trueBranch, falseBranch)) // false class parameters. Easy case Split.Cons(Branch(scrutinee, pattern @ Pattern.Class(nme), continuation), tail) => println(s"match $scrutinee with $nme (has location: ${nme.toLoc.isDefined})") - val trueBranch = normalizeToTerm(specialize(concat(continuation, tail), true)(scrutinee.symbol, pattern, scope)) - val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutinee.symbol, pattern, scope)) + val trueBranch = normalizeToTerm(specialize(concat(continuation, tail), true)(scrutinee.symbol, pattern)) + val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutinee.symbol, pattern)) CaseOf(scrutinee, Case(nme, trueBranch, falseBranch)) case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => throw new NormalizationException((msg"Unsupported pattern: ${pattern.toString}" -> pattern.toLoc) :: Nil) case Split.Let(rec, Var("_"), rhs, tail) => normalizeToTerm(tail) - case Split.Let(rec, nme, rhs, tail) => Let(rec, nme, rhs, normalizeToTerm(tail)) + case Split.Let(_, nme, _, tail) if Desugaring.isScrutineeVar(nme) && generatedVars.contains(nme) => + normalizeToTerm(tail) + case Split.Let(rec, nme, rhs, tail) => + val newDeclaredBindings = if (Desugaring.isGeneratedVar(nme)) generatedVars + nme else generatedVars + Let(rec, nme, rhs, normalizeToTerm(tail)(newDeclaredBindings)) case Split.Else(default) => default case Split.Nil => println("unexpected empty split"); ??? } }(_ => "normalizeToTerm ==> ") - private def normalizeToCaseBranches(split: Split)(implicit scope: Scope): CaseBranches = trace("normalizeToCaseBranches") { - split match { - // case Split.Cons(head, Split.Nil) => Case(head.pattern, normalizeToTerm(head.continuation), NoCases) - case other @ (Split.Cons(_, _) | Split.Let(_, _, _, _)) => Wildcard(normalizeToTerm(other)) - case Split.Else(default) => Wildcard(default) - case Split.Nil => NoCases - } - }() + private def normalizeToCaseBranches(split: Split)(implicit generatedVars: Set[Var]): CaseBranches = + trace("normalizeToCaseBranches") { + split match { + // case Split.Cons(head, Split.Nil) => Case(head.pattern, normalizeToTerm(head.continuation), NoCases) + case other: Split.Cons => Wildcard(normalizeToTerm(other)) + case Split.Let(rec, Var("_"), rhs, tail) => normalizeToCaseBranches(tail) + case Split.Let(_, nme, _, tail) if Desugaring.isScrutineeVar(nme) && generatedVars.contains(nme) => + normalizeToCaseBranches(tail) + case Split.Let(rec, nme, rhs, tail) => + val newDeclaredBindings = if (Desugaring.isGeneratedVar(nme)) generatedVars + nme else generatedVars + normalizeToCaseBranches(tail)(newDeclaredBindings) match { + case NoCases => Wildcard(rhs) + case Wildcard(term) => Wildcard(Let(rec, nme, rhs, term)) + case _: Case => die + } + case Split.Else(default) => Wildcard(default) + case Split.Nil => NoCases + } + }() // Specialize `split` with the assumption that `scrutinee` matches `pattern`. private def specialize (split: Split, matchOrNot: Bool) - (implicit scrutinee: Symbol, pattern: Pattern, scope: Scope): Split = + (implicit scrutinee: Symbol, pattern: Pattern): Split = trace[Split](s"S${if (matchOrNot) "+" else "-"} <== ${scrutinee.name} is ${pattern}") { (matchOrNot, split) match { // Name patterns are translated to let bindings. diff --git a/shared/src/test/diff/pretyper/ucs/DualOption.mls b/shared/src/test/diff/pretyper/ucs/DualOption.mls index e6e45799..d7a09632 100644 --- a/shared/src/test/diff/pretyper/ucs/DualOption.mls +++ b/shared/src/test/diff/pretyper/ucs/DualOption.mls @@ -137,3 +137,26 @@ add_5(Some(5), Some(9)) //│ = 9 //│ res //│ = 14 + + +fun add_6(x, y) = + if [x, y] is + [Some(xv), Some(yv)] then xv + yv + [Some(xv), None] then xv + [None, Some(yv)] then yv + [None, None] then 0 +//│ fun add_6: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) + +add_6(None, None) +add_6(Some(5), None) +add_6(None, Some(9)) +add_6(Some(5), Some(9)) +//│ Int +//│ res +//│ = 0 +//│ res +//│ = 5 +//│ res +//│ = 9 +//│ res +//│ = 14 diff --git a/shared/src/test/diff/pretyper/ucs/TuplePattern.mls b/shared/src/test/diff/pretyper/ucs/TuplePattern.mls index 99cad895..880c7908 100644 --- a/shared/src/test/diff/pretyper/ucs/TuplePattern.mls +++ b/shared/src/test/diff/pretyper/ucs/TuplePattern.mls @@ -1,10 +1,9 @@ :PreTyper -// FIXME fun flex(x) = if x is [a, b, c] then a + b + c else 0 -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ fun flex: {0: Int, 1: Int, 2: Int} -> Int diff --git a/shared/src/test/diff/pretyper/ucs/examples/Calculator.mls b/shared/src/test/diff/pretyper/ucs/examples/Calculator.mls index ff2005ed..3b8df255 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/Calculator.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/Calculator.mls @@ -201,9 +201,8 @@ fun scanToken(text: StringOps, at: Int): [Token, Int] = 45 then [BinaryOperator("-", 10, at'), at' + 1] 47 then [BinaryOperator("/", 20, at'), at' + 1] (48 <= head) && (head <= 57) and - scanInt(text, at') is Some(res) then - let at'' = res.1 - [IntegerLiteral(res.0, Span(at', at'')), at''] + scanInt(text, at') is Some([n, at'']) then + [IntegerLiteral(n, Span(at', at'')), at''] else [UnknownInput(text.slice(at'), at', text.length - at'), at'] //│ fun scanToken: (text: StringOps, at: Int) -> [Token, Int] @@ -223,11 +222,8 @@ scanToken(" 42" |> toStringOps, 0) fun tokenize(str: Str): List[Token] = let text = str |> toStringOps - let aux(acc, at) = if - let res = scanToken(text, at) // TODO: Tuple pattern - let token = res.0 - let at' = res.1 - token is + let aux(acc, at) = + if scanToken(text, at) is [token, at'] and token is UnknownInput then (token :: acc) |> reverse EndOfInput then acc |> reverse else aux(token :: acc, at') @@ -330,33 +326,27 @@ fun parseAtom(ts: List[Token]): ParseResult[[Expression, List[Token]]] = if ts is Cons(IntegerLiteral(n, span), ts') then Some([IntegerLiteral(n, span), ts']) Cons(LeftParen, ts') and parseExpression(0, ts') is - Some(res) and res.1 is - Cons(RightParen, ts'') then Some([res.0, ts'']) - else Failed("Expected a right parenthesis at " ++ toString(lastPosition of res.0)) + Some([body, Cons(RightParen, ts'')]) then Some([body, ts'']) + Some([body, _]) then Failed("Expected a right parenthesis at " ++ toString(lastPosition of body)) failed then failed Cons(token, _) then Failed("Unexpected token " ++ toString(token) ++ " at " ++ toString(token.span.start)) Nil then Failed("Unexpected end of input") fun parseExpression(bp: Int, ts: List[Token]): ParseResult[[Expression, List[Token]]] = if parseAtom(ts) is - Some(res) then + Some([leftmost, ts']) then let aux(left, ts) = if ts is Cons(BinaryOperator(op, bp', opAt), ts') and bp < bp' and parseExpression(bp', ts') is - Some(res) then - let right = res.0 - let ts'' = res.1 + Some([right, ts'']) then aux(BinaryExpression(BinaryOperator(op, bp', opAt), left, right), ts'') failed then failed else Some([left, ts]) - let leftmost = res.0 - let ts' = res.1 aux(leftmost, ts') failed then failed fun parse(source: Str): ParseResult[Expression] = if parseExpression(0, tokenize(source)) is - Some(res) and res.1 is - Nil then Some(res.0) - else Failed("Unexpected token: " ++ showList(res.1) ++ " at " ++ toString(lastPosition of res.0)) + Some([expr, Nil]) then Some(expr) + Some([expr, rest]) then Failed("Unexpected token: " ++ showList(rest) ++ " at " ++ toString(lastPosition of expr)) failed then failed //│ fun parseAtom: (ts: List[Token]) -> ParseResult[[Expression, List[Token]]] //│ fun parseExpression: (bp: Int, ts: List[Token]) -> ParseResult[[Expression, List[Token]]] diff --git a/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls b/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls new file mode 100644 index 00000000..3b0e1996 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls @@ -0,0 +1,94 @@ +:PreTyper + +// We test the support for simple tuple patterns in this file. +// Splice tuple patterns will be implement in the future. + +fun sum(x, y) = x + y +sum(1, 2) +//│ fun sum: (Int, Int) -> Int +//│ Int +//│ res +//│ = 3 + +fun sum'([x, y]) = x + y +sum'([1, 2]) +//│ fun sum': ([Int, Int]) -> Int +//│ Int +//│ res +//│ = 3 + +fun sum''(pair) = + if pair is [x, y] then x + y +sum'([1, 2]) +//│ fun sum'': {0: Int, 1: Int} -> Int +//│ Int +//│ res +//│ = 3 + +// We need native support for tuple patterns in MLscript syntax. +// Otherwise the following cases work. + +fun test(thing) = + if thing is [] then 0 +test("") +test(12) +//│ fun test: anything -> 0 +//│ 0 +//│ res +//│ = 0 +//│ res +//│ = 0 + +// Since pattern destruction is desugared to let bindings, matching with other +// classes after the tuple pattern will not work. +class Point(x: Int, y: Int) +fun discarded_cases(thing) = + if thing is + [x, y] then x + y + Point(x, y) then x + y +//│ class Point(x: Int, y: Int) +//│ fun discarded_cases: {0: Int, 1: Int} -> Int + +:e +discarded_cases(Point(0, 0)) +//│ ╔══[ERROR] Type `Point` does not contain member `1` +//│ ║ l.47: [x, y] then x + y +//│ ╙── ^ +//│ Int | error +//│ res +//│ = NaN + +// A workaround is to move the tuple pattern to the last case. +fun working_cases(thing) = + if thing is + Point(x, y) then x + y + [x, y] then x + y +//│ fun working_cases: (Object & {0: Int, 1: Int} & ~#Point | Point) -> Int + +working_cases(Point(0, 0)) +//│ Int +//│ res +//│ = 0 + +// However, the `Object` type forbids tuples to be used. +:e +working_cases([0, 0]) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.75: working_cases([0, 0]) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── tuple literal of type `[0, 0]` is not an instance of type `Object` +//│ ║ l.75: working_cases([0, 0]) +//│ ║ ^^^^^^ +//│ ╟── Note: constraint arises from `case` expression: +//│ ║ l.63: if thing is +//│ ║ ^^^^^^^^ +//│ ║ l.64: Point(x, y) then x + y +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.65: [x, y] then x + y +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── from reference: +//│ ║ l.63: if thing is +//│ ╙── ^^^^^ +//│ Int | error +//│ res +//│ = 0 From 9874ac9d8dda6c0cfa22d744b344644aa32be96c Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 27 Dec 2023 11:28:24 +0800 Subject: [PATCH 027/143] Add UCS context and facilities to replace `ScrutineeSymbol` This is a refactor and not test changes were found. --- .../src/main/scala/mlscript/ucs/Context.scala | 37 ++++++ .../main/scala/mlscript/ucs/DesugarUCS.scala | 6 +- .../scala/mlscript/ucs/ScrutineeData.scala | 64 +++++++++++ .../src/main/scala/mlscript/ucs/package.scala | 10 ++ .../mlscript/ucs/stages/Desugaring.scala | 106 +++++++----------- .../mlscript/ucs/stages/Normalization.scala | 34 +++--- .../mlscript/ucs/stages/PostProcessing.scala | 5 +- .../mlscript/ucs/stages/Transformation.scala | 20 ++-- 8 files changed, 185 insertions(+), 97 deletions(-) create mode 100644 shared/src/main/scala/mlscript/ucs/Context.scala create mode 100644 shared/src/main/scala/mlscript/ucs/ScrutineeData.scala diff --git a/shared/src/main/scala/mlscript/ucs/Context.scala b/shared/src/main/scala/mlscript/ucs/Context.scala new file mode 100644 index 00000000..1065a238 --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/Context.scala @@ -0,0 +1,37 @@ +package mlscript.ucs + +import collection.mutable.{Buffer, Map => MutMap, SortedMap => MutSortedMap, Set => MutSet} +import mlscript.{If, Loc, NuFunDef, NuTypeDef, TypeName, Var} +import mlscript.{Cls, Trt, Mxn, Als, Mod} +import mlscript.pretyper.symbol.TypeSymbol +import mlscript.pretyper.Scope +import mlscript.utils._, shorthands._ + +class Context(originalTerm: If) { + val prefix: Str = Context.freshPrefix().name + + private val cachePrefix = prefix + "$cache$" + private val scrutineePrefix = prefix + "$scrut$" + private val testPrefix = prefix + "$test$" + private val shadowPrefix = prefix + "$shadow$" + val unappliedPrefix: Str = prefix + "$args_" + + def isCacheVar(nme: Var): Bool = nme.name.startsWith(cachePrefix) + def isScrutineeVar(nme: Var): Bool = nme.name.startsWith(scrutineePrefix) + def isTestVar(nme: Var): Bool = nme.name.startsWith(testPrefix) + def isUnappliedVar(nme: Var): Bool = nme.name.startsWith(unappliedPrefix) + def isGeneratedVar(nme: Var): Bool = + isCacheVar(nme) || isScrutineeVar(nme) || isTestVar(nme) || isUnappliedVar(nme) + + // Call these objects to generate fresh variables for different purposes. + // I plan to mix the unique identifiers of UCS expressions into the prefixes. + // So that the generated variables will definitely not conflict with others. + val freshCache: VariableGenerator = new VariableGenerator(cachePrefix) + val freshScrutinee: VariableGenerator = new VariableGenerator(scrutineePrefix) + val freshTest: VariableGenerator = new VariableGenerator(testPrefix) + val freshShadowed: VariableGenerator = new VariableGenerator("shadowed$") +} + +object Context { + private val freshPrefix = new VariableGenerator("ucs") +} diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index 3b440676..944bca1c 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -14,12 +14,13 @@ trait DesugarUCS extends Transformation with PostProcessing with CoverageChecking { self: PreTyper => - protected def traverseIf(`if`: If)(implicit scope: Scope): Unit = + protected def traverseIf(`if`: If)(implicit scope: Scope): Unit = { + implicit val context: Context = new Context(`if`) trace("traverseIf") { // Stage 0: Transformation val transformed = traceWithTopic("transform") { println("STEP 0") - val transformed = transform(`if`, true) + val transformed = transform(`if`) println("Transformed UCS term:") println(ucs.syntax.printTermSplit(transformed)) transformed @@ -62,6 +63,7 @@ trait DesugarUCS extends Transformation // Epilogue `if`.desugaredTerm = S(postProcessed) }(_ => "traverseIf ==> ()") + } private def traverseSplit(split: core.Split)(implicit scope: Scope): Unit = trace(s"traverseSplit <== [${scope.showLocalSymbols}]") { diff --git a/shared/src/main/scala/mlscript/ucs/ScrutineeData.scala b/shared/src/main/scala/mlscript/ucs/ScrutineeData.scala new file mode 100644 index 00000000..2b259b99 --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/ScrutineeData.scala @@ -0,0 +1,64 @@ +package mlscript.ucs + +import collection.mutable.{Buffer, Map => MutMap, SortedMap => MutSortedMap, Set => MutSet} +import mlscript.{Loc, NuFunDef, NuTypeDef, TypeName, Var} +import mlscript.{Cls, Trt, Mxn, Als, Mod} +import mlscript.pretyper.symbol.TypeSymbol +import mlscript.utils._, shorthands._ + +class ClassPatternInfo(scrutinee: ScrutineeData) { + private val locations: Buffer[Loc] = Buffer.empty + private var unappliedVarOpt: Opt[Var] = N + private val parameters: MutSortedMap[Int, ScrutineeData] = MutSortedMap.empty + + def getParameter(index: Int): ScrutineeData = + parameters.getOrElse(index, new ScrutineeData(S(scrutinee))) + + def addLocation(location: Opt[Loc]): Unit = locations ++= location + + def getUnappliedVar(default: => Var): Var = + unappliedVarOpt.getOrElse { + val unappliedVar = default + unappliedVarOpt = S(unappliedVar) + unappliedVar + } +} + +class TuplePatternInfo(scrutinee: ScrutineeData) { + private val locations: Buffer[Loc] = Buffer.empty + private val fields: MutSortedMap[Int, ScrutineeData] = MutSortedMap.empty + + def getField(index: Int): ScrutineeData = + fields.getOrElse(index, new ScrutineeData(S(scrutinee))) +} + +class ScrutineeData(parent: Opt[ScrutineeData]) { + private var generatedVarOpt: Opt[Var] = N + private val classLikePatterns: MutMap[TypeSymbol, ClassPatternInfo] = MutMap.empty + // Currently we only support simple tuple patterns, so there is only _one_ + // slot for tuple patterns. After we support complex tuple patterns, we need + // to extend this fields to a map from tuple arity to `TuplePatternInfo`. + // private val tuplePatterns: MutMap[Int, TuplePatternInfo] = MutMap.empty + // If we support tuple pattern splice, we need a more expressive key in the + // map's type. + private var tuplePatternOpt: Opt[TuplePatternInfo] = N + + def getClassUnappliedVar(classLikeSymbol: TypeSymbol, default: => Var): Var = + classLikePatterns.getOrElse(classLikeSymbol, new ClassPatternInfo(this)).getUnappliedVar(default) + + def getClassLikeParameter(classLikeSymbol: TypeSymbol, index: Int, location: Opt[Loc]): ScrutineeData = { + val classPattern = classLikePatterns.getOrElse(classLikeSymbol, new ClassPatternInfo(this)) + classPattern.addLocation(location) + classPattern.getParameter(index) + } + + def getClassLikeParameter(classLikeSymbol: TypeSymbol, index: Int): ScrutineeData = + classLikePatterns.getOrElse(classLikeSymbol, new ClassPatternInfo(this)).getParameter(index) + + def getTupleField(index: Int): ScrutineeData = + tuplePatternOpt.getOrElse { + val tuplePattern = new TuplePatternInfo(this) + tuplePatternOpt = S(tuplePattern) + tuplePattern + }.getField(index) +} \ No newline at end of file diff --git a/shared/src/main/scala/mlscript/ucs/package.scala b/shared/src/main/scala/mlscript/ucs/package.scala index bf4df92e..e284d520 100644 --- a/shared/src/main/scala/mlscript/ucs/package.scala +++ b/shared/src/main/scala/mlscript/ucs/package.scala @@ -3,6 +3,16 @@ package mlscript import scala.annotation.tailrec package object ucs { + class VariableGenerator(prefix: String) { + private var nextIndex = 0 + + def apply(): Var = { + val thisIndex = nextIndex + nextIndex += 1 + Var(s"$prefix$thisIndex") + } + } + type Lines = List[(Int, String)] implicit class LinesOps(private val lines: Lines) extends AnyVal { diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index a68ca224..f2d69e38 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -1,7 +1,7 @@ package mlscript.ucs.stages import mlscript.{App, Asc, Fld, FldFlags, Lit, Sel, Term, Tup, TypeName, Var} -import mlscript.ucs.{syntax => s, core => c, PartialTerm} +import mlscript.ucs.{syntax => s, core => c, Context, PartialTerm} import mlscript.ucs.helpers.mkBinOp import mlscript.utils._, shorthands._ import mlscript.pretyper.symbol._ @@ -27,17 +27,17 @@ import mlscript.Message, Message.MessageContext * continuation. */ trait Desugaring { self: PreTyper => - @inline def desugar(term: s.TermSplit)(implicit scope: Scope): c.Split = - desugarTermSplit(term)(PartialTerm.Empty, scope) - - import Desugaring._ - - // Call these objects to generate fresh variables for different purposes. - // I plan to mix the unique identifiers of UCS expressions into the prefixes. - // So that the generated variables will definitely not conflict with others. - private val freshCache = new VariableGenerator(cachePrefix) - private val freshScrutinee = new VariableGenerator(scrutineePrefix) - private val freshTest = new VariableGenerator(testPrefix) + /** + * The entry point of the desugaring stage. + * + * @param term the root of source abstrax syntax term, obtained from the + * transformation stage + * @param context the scope is for resolving type symbols. The scope should + * contains a TypeSymbol for `true` literal. + * @return the root of desugared core abstract syntax term + */ + @inline def desugar(term: s.TermSplit)(implicit scope: Scope, context: Context): c.Split = + desugarTermSplit(term)(PartialTerm.Empty, scope, context) /** * Coin a fresh name for a destructed parameter. The name consists of three @@ -57,8 +57,8 @@ trait Desugaring { self: PreTyper => * Parameters `hd` and `tl` are obtained by selecting `.1` and `.2` from * `args_x$Cons`. */ - private def makeUnappliedVar(scrutinee: Var, className: Var): Var = - Var(s"$unappliedPrefix${scrutinee.name}$$${className.name}") + private def makeUnappliedVar(scrutinee: Var, className: Var)(implicit context: Context): Var = + Var(s"${context.unappliedPrefix}${scrutinee.name}$$${className.name}") // I plan to integrate scrutinee symbols into a field of `ValueSymbol`. // Because each `ValueSymbol` can be matched in multiple UCS expressions. @@ -110,57 +110,57 @@ trait Desugaring { self: PreTyper => private def freshSymbol(nme: Var): ValueSymbol = new ValueSymbol(nme, false) - private def desugarTermSplit(split: s.TermSplit)(implicit termPart: PartialTerm, scope: Scope): c.Split = + private def desugarTermSplit(split: s.TermSplit)(implicit termPart: PartialTerm, scope: Scope, context: Context): c.Split = split match { case s.Split.Cons(head, tail) => desugarTermBranch(head) ++ desugarTermSplit(tail) case s.Split.Let(rec, nme, rhs, tail) => - c.Split.Let(rec, nme, rhs, desugarTermSplit(tail)(termPart, scope + nme.withFreshSymbol.symbol)) // <-- Weird use. + c.Split.Let(rec, nme, rhs, desugarTermSplit(tail)(termPart, scope + nme.withFreshSymbol.symbol, context)) // <-- Weird use. case s.Split.Else(default) => c.Split.Else(default); case s.Split.Nil => c.Split.Nil } // This function does not need to can `withCachedTermPart` because all branches assume that // `termPart` is either empty or waiting for an RHS. - private def desugarTermBranch(branch: s.TermBranch)(implicit termPart: PartialTerm, scope: Scope): c.Split = + private def desugarTermBranch(branch: s.TermBranch)(implicit termPart: PartialTerm, scope: Scope, context: Context): c.Split = trace(s"desugarTermBranch <== $termPart") { branch match { case s.TermBranch.Boolean(testPart, continuation) => - val test = freshTest().withFreshSymbol + val test = context.freshTest().withFreshSymbol c.Split.Let( rec = false, name = test, term = Asc(termPart.addTerm(testPart, true).get, TypeName("Bool")), - tail = c.Branch(test, truePattern, desugarTermSplit(continuation)(PartialTerm.Empty, scope + test.symbol)) :: c.Split.Nil + tail = c.Branch(test, truePattern, desugarTermSplit(continuation)(PartialTerm.Empty, scope + test.symbol, context)) :: c.Split.Nil ) case s.TermBranch.Match(scrutinee, split) => - desugarPatternSplit(split)(termPart.addTerm(scrutinee, true).get, scope) + desugarPatternSplit(termPart.addTerm(scrutinee, true).get, split) case s.TermBranch.Left(left, continuation) => - desugarOperatorSplit(continuation)(termPart.addTerm(left, true), scope) + desugarOperatorSplit(continuation)(termPart.addTerm(left, true), scope, context) } }() - private def withCachedTermPart[B <: s.Branch](desugar: (PartialTerm, Scope) => c.Split)(implicit termPart: PartialTerm, scope: Scope): c.Split = + private def withCachedTermPart[B <: s.Branch](desugar: (PartialTerm, Scope) => c.Split)(implicit termPart: PartialTerm, scope: Scope, context: Context): c.Split = termPart.get match { case v: Var => desugar(termPart, scope) // No need to cache variables. case rhs => - val cache = freshCache().withFreshSymbol + val cache = context.freshCache().withFreshSymbol c.Split.Let(false, cache, rhs, desugar(PartialTerm.Total(cache, Nil), scope + cache.symbol)) } - private def desugarOperatorSplit(split: s.OperatorSplit)(implicit termPart: PartialTerm, scope: Scope): c.Split = + private def desugarOperatorSplit(split: s.OperatorSplit)(implicit termPart: PartialTerm, scope: Scope, context: Context): c.Split = withCachedTermPart { (termPart, scope) => split match { - case s.Split.Cons(head, tail) => desugarOperatorBranch(head)(termPart, scope) ++ desugarOperatorSplit(tail)(termPart, scope) + case s.Split.Cons(head, tail) => desugarOperatorBranch(head)(termPart, scope, context) ++ desugarOperatorSplit(tail)(termPart, scope, context) case s.Split.Let(rec, nme, rhs, tail) => - c.Split.Let(rec, nme, rhs, desugarOperatorSplit(tail)(termPart, scope + nme.withFreshSymbol.symbol)) // <-- Weird use. + c.Split.Let(rec, nme, rhs, desugarOperatorSplit(tail)(termPart, scope + nme.withFreshSymbol.symbol, context)) // <-- Weird use. case s.Split.Else(default) => c.Split.Else(default) case s.Split.Nil => c.Split.Nil }} - private def desugarOperatorBranch(branch: s.OperatorBranch)(implicit termPart: PartialTerm, scope: Scope): c.Split = + private def desugarOperatorBranch(branch: s.OperatorBranch)(implicit termPart: PartialTerm, scope: Scope, context: Context): c.Split = trace(s"desugarOperatorBranch <== $termPart") { branch match { - case s.OperatorBranch.Binary(op, split) => desugarTermSplit(split)(termPart.addOp(op), scope) - case s.OperatorBranch.Match(_, split) => desugarPatternSplit(split)(termPart.get, scope) + case s.OperatorBranch.Binary(op, split) => desugarTermSplit(split)(termPart.addOp(op), scope, context) + case s.OperatorBranch.Match(_, split) => desugarPatternSplit(termPart.get, split)(scope, context) } }() @@ -211,7 +211,7 @@ trait Desugaring { self: PreTyper => * @param initialScope the scope before flattening the class pattern * @return a tuple of the augmented scope and a function that wrap a split */ - private def desugarClassPattern(pattern: s.ClassPattern, scrutinee: Var, initialScope: Scope): (Scope, c.Split => c.Branch) = { + private def desugarClassPattern(pattern: s.ClassPattern, scrutinee: Var, initialScope: Scope)(implicit context: Context): (Scope, c.Split => c.Branch) = { val scrutineeSymbol = scrutinee.getScrutineeSymbol val patternClassSymbol = pattern.nme.resolveTypeSymbol(initialScope) // Most importantly, we need to add the class to the list of matched classes. @@ -257,7 +257,7 @@ trait Desugaring { self: PreTyper => nestedPatterns: Ls[Opt[Var -> Opt[s.Pattern]]], scopeWithScrutinees: Scope, bindScrutinees: c.Split => c.Split - ): (Scope, c.Split => c.Split) = { + )(implicit context: Context): (Scope, c.Split => c.Split) = { nestedPatterns.foldLeft((scopeWithScrutinees, bindScrutinees)) { // If this parameter is not matched with a sub-pattern, then we do // nothing and pass on scope and binder. @@ -271,7 +271,7 @@ trait Desugaring { self: PreTyper => val (scopeWithNestedAll, bindNestedAll) = desugarClassPattern(pattern, nme, scope) (scopeWithNestedAll, split => bindPrevious(bindNestedAll(split) :: c.Split.Nil)) case ((scope, bindPrevious), S(nme -> S(pattern: s.LiteralPattern))) => - val test = freshTest().withFreshSymbol + val test = context.freshTest().withFreshSymbol (scope + test.symbol, makeLiteralTest(test, nme, pattern.literal)(scope).andThen(bindPrevious)) case ((scope, bindPrevious), S(nme -> S(s.TuplePattern(fields)))) => val (scopeWithNestedAll, bindNestedAll) = desugarTuplePattern(fields, nme, scope) @@ -301,7 +301,7 @@ trait Desugaring { self: PreTyper => case _ => ??? }.toList - private def desugarTuplePattern(fields: Ls[Opt[s.Pattern]], scrutinee: Var, initialScope: Scope): (Scope, c.Split => c.Split) = { + private def desugarTuplePattern(fields: Ls[Opt[s.Pattern]], scrutinee: Var, initialScope: Scope)(implicit context: Context): (Scope, c.Split => c.Split) = { val scrutineeSymbol = scrutinee.getScrutineeSymbol val nestedPatterns = flattenTupleFields(scrutinee, fields) val bindTupleFields = nestedPatterns.iterator.zipWithIndex.foldRight[c.Split => c.Split](identity) { @@ -314,14 +314,14 @@ trait Desugaring { self: PreTyper => desugarNestedPatterns(nestedPatterns, scopeWithTupleFields, bindTupleFields) } - private def desugarPatternSplit(split: s.PatternSplit)(implicit scrutinee: Term, scope: Scope): c.Split = { + private def desugarPatternSplit(scrutinee: Term, split: s.PatternSplit)(implicit scope: Scope, context: Context): c.Split = { def rec(scrutinee: Var, split: s.PatternSplit)(implicit scope: Scope): c.Split = split match { case s.Split.Cons(head, tail) => head.pattern match { case s.AliasPattern(nme, pattern) => ??? case s.LiteralPattern(literal) => ??? case s.ConcretePattern(nme) => - val test = freshTest().withFreshSymbol + val test = context.freshTest().withFreshSymbol c.Split.Let( rec = false, name = test, @@ -329,27 +329,27 @@ trait Desugaring { self: PreTyper => tail = c.Branch( scrutinee = test, pattern = truePattern, - continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope + test.symbol) + continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope + test.symbol, context) ) :: rec(scrutinee, tail) ) case s.NamePattern(Var("_")) => - desugarTermSplit(head.continuation)(PartialTerm.Empty, scope) ++ rec(scrutinee, tail) + desugarTermSplit(head.continuation)(PartialTerm.Empty, scope, context) ++ rec(scrutinee, tail) case s.NamePattern(nme) => // Share the scrutinee's symbol with its aliases. // nme.symbol = scrutinee.symbol // <-- This currently causes a bug, reuse this line after we remove `ScrutineeSymbol`. nme.symbol = new ValueSymbol(nme, false) - val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope + nme.symbol) + val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope + nme.symbol, context) c.Branch(scrutinee, c.Pattern.Name(nme), continuation) :: rec(scrutinee, tail)(scope + nme.symbol) case pattern @ s.ClassPattern(nme, fields) => println(s"find term symbol of $scrutinee in ${scope.showLocalSymbols}") scrutinee.symbol = scope.getTermSymbol(scrutinee.name).getOrElse(???) val (scopeWithAll, bindAll) = desugarClassPattern(pattern, scrutinee, scope) - val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scopeWithAll) + val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scopeWithAll, context) bindAll(continuation) :: rec(scrutinee, tail) case s.TuplePattern(fields) => scrutinee.symbol = scope.getTermSymbol(scrutinee.name).getOrElse(???) val (scopeWithAll, bindAll) = desugarTuplePattern(fields, scrutinee, scope) - val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scopeWithAll) + val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scopeWithAll, context) val withBindings = bindAll(continuation) if (withBindings.hasElse) { withBindings @@ -366,32 +366,8 @@ trait Desugaring { self: PreTyper => scrutinee match { case nme: Var => rec(nme, split) case other => - val alias = freshScrutinee().withFreshSymbol + val alias = context.freshScrutinee().withFreshSymbol c.Split.Let(false, alias, scrutinee, rec(alias, split)(scope + alias.symbol)) } } } - -object Desugaring { - class VariableGenerator(prefix: Str) { - private var nextIndex = 0 - - def apply(): Var = { - val thisIndex = nextIndex - nextIndex += 1 - Var(s"$prefix$thisIndex") - } - } - - val cachePrefix = "cache$" - val scrutineePrefix = "scrut$" - val testPrefix = "test$" - val unappliedPrefix = "args_" - - def isCacheVar(nme: Var): Bool = nme.name.startsWith(cachePrefix) - def isScrutineeVar(nme: Var): Bool = nme.name.startsWith(scrutineePrefix) - def isTestVar(nme: Var): Bool = nme.name.startsWith(testPrefix) - def isUnappliedVar(nme: Var): Bool = nme.name.startsWith(unappliedPrefix) - def isGeneratedVar(nme: Var): Bool = - isCacheVar(nme) || isScrutineeVar(nme) || isTestVar(nme) || isUnappliedVar(nme) -} \ No newline at end of file diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index ba6c2e37..34e06d51 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -1,7 +1,7 @@ package mlscript.ucs.stages -import mlscript.ucs.{showVar, Lines, LinesOps} +import mlscript.ucs.{showVar, Context, Lines, LinesOps, VariableGenerator} import mlscript.ucs.core._ import mlscript.ucs.helpers._ import mlscript.pretyper.Scope @@ -14,26 +14,24 @@ import mlscript.utils._, shorthands._ trait Normalization { self: mlscript.pretyper.Traceable => import Normalization._ - private val freshShadowed = new Desugaring.VariableGenerator("shadowed$") - - def concatImpl(these: Split, those: Split)(implicit generatedVars: Set[Var]): Split = + private def concatImpl(these: Split, those: Split)(implicit context: Context, generatedVars: Set[Var]): Split = if (these.hasElse) these else (these match { case these @ Split.Cons(_, tail) => these.copy(tail = concatImpl(tail, those)) case these @ Split.Let(_, nme, _, tail) => if (those.freeVars contains nme) { - val fresh = freshShadowed() + val fresh = context.freshShadowed() val thoseWithShadowed = Split.Let(false, nme, fresh, those) val concatenated = these.copy(tail = concatImpl(tail, thoseWithShadowed)) Split.Let(false, fresh, nme, concatenated) } else { - these.copy(tail = concatImpl(tail, those)(generatedVars + nme)) + these.copy(tail = concatImpl(tail, those)(context, generatedVars + nme)) } case _: Split.Else => these case Split.Nil => those.withoutBindings(generatedVars) }) - def concat(these: Split, those: Split)(implicit vars: Set[Var]): Split = - trace(s"concat <== ${vars.mkString("{", ", ", "}")}") { + private def concat(these: Split, those: Split)(implicit context: Context, generatedVars: Set[Var]): Split = + trace(s"concat <== ${generatedVars.mkString("{", ", ", "}")}") { println(s"these: ${printSplit(these)}") println(s"those: ${printSplit(those)}") concatImpl(these, those) @@ -45,15 +43,15 @@ trait Normalization { self: mlscript.pretyper.Traceable => * @param split the split to normalize * @return the normalized term */ - @inline protected def normalize(split: Split): Term = normalizeToTerm(split)(Set.empty) + @inline protected def normalize(split: Split)(implicit context: Context): Term = normalizeToTerm(split)(context, Set.empty) - private def normalizeToTerm(split: Split)(implicit generatedVars: Set[Var]): Term = trace("normalizeToTerm <==") { + private def normalizeToTerm(split: Split)(implicit context: Context, generatedVars: Set[Var]): Term = trace("normalizeToTerm <==") { split match { case Split.Cons(Branch(scrutinee, Pattern.Name(nme), continuation), tail) => println(s"alias $scrutinee => $nme") Let(false, nme, scrutinee, normalizeToTerm(concat(continuation, tail))) // Skip Boolean conditions as scrutinees, because they only appear once. - case Split.Cons(Branch(test, pattern @ Pattern.Class(nme @ Var("true")), continuation), tail) if Desugaring.isTestVar(test) => + case Split.Cons(Branch(test, pattern @ Pattern.Class(nme @ Var("true")), continuation), tail) if context.isTestVar(test) => val trueBranch = normalizeToTerm(concat(continuation, tail)) val falseBranch = normalizeToCaseBranches(tail) CaseOf(test, Case(nme, trueBranch, falseBranch)) @@ -70,27 +68,27 @@ trait Normalization { self: mlscript.pretyper.Traceable => case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => throw new NormalizationException((msg"Unsupported pattern: ${pattern.toString}" -> pattern.toLoc) :: Nil) case Split.Let(rec, Var("_"), rhs, tail) => normalizeToTerm(tail) - case Split.Let(_, nme, _, tail) if Desugaring.isScrutineeVar(nme) && generatedVars.contains(nme) => + case Split.Let(_, nme, _, tail) if context.isScrutineeVar(nme) && generatedVars.contains(nme) => normalizeToTerm(tail) case Split.Let(rec, nme, rhs, tail) => - val newDeclaredBindings = if (Desugaring.isGeneratedVar(nme)) generatedVars + nme else generatedVars - Let(rec, nme, rhs, normalizeToTerm(tail)(newDeclaredBindings)) + val newDeclaredBindings = if (context.isGeneratedVar(nme)) generatedVars + nme else generatedVars + Let(rec, nme, rhs, normalizeToTerm(tail)(context, newDeclaredBindings)) case Split.Else(default) => default case Split.Nil => println("unexpected empty split"); ??? } }(_ => "normalizeToTerm ==> ") - private def normalizeToCaseBranches(split: Split)(implicit generatedVars: Set[Var]): CaseBranches = + private def normalizeToCaseBranches(split: Split)(implicit context: Context, generatedVars: Set[Var]): CaseBranches = trace("normalizeToCaseBranches") { split match { // case Split.Cons(head, Split.Nil) => Case(head.pattern, normalizeToTerm(head.continuation), NoCases) case other: Split.Cons => Wildcard(normalizeToTerm(other)) case Split.Let(rec, Var("_"), rhs, tail) => normalizeToCaseBranches(tail) - case Split.Let(_, nme, _, tail) if Desugaring.isScrutineeVar(nme) && generatedVars.contains(nme) => + case Split.Let(_, nme, _, tail) if context.isScrutineeVar(nme) && generatedVars.contains(nme) => normalizeToCaseBranches(tail) case Split.Let(rec, nme, rhs, tail) => - val newDeclaredBindings = if (Desugaring.isGeneratedVar(nme)) generatedVars + nme else generatedVars - normalizeToCaseBranches(tail)(newDeclaredBindings) match { + val newDeclaredBindings = if (context.isGeneratedVar(nme)) generatedVars + nme else generatedVars + normalizeToCaseBranches(tail)(context, newDeclaredBindings) match { case NoCases => Wildcard(rhs) case Wildcard(term) => Wildcard(Let(rec, nme, rhs, term)) case _: Case => die diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index 8ecb313f..60028ef7 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -1,6 +1,7 @@ package mlscript.ucs.stages import mlscript.{Case, CaseBranches, CaseOf, Let, Loc, NoCases, Term, Var, Wildcard} +import mlscript.ucs.Context import mlscript.pretyper.symbol._ import mlscript.utils._, shorthands._ import mlscript.Message, Message.MessageContext @@ -27,14 +28,14 @@ trait PostProcessing { self: mlscript.pretyper.Traceable => } }(symbol => s"getClassSymbolFromVar ==> ${symbol.name}") - def postProcess(term: Term): Term = trace(s"postProcess <== ${inspect.shallow(term)}") { + def postProcess(term: Term)(implicit context: Context): Term = trace(s"postProcess <== ${inspect.shallow(term)}") { // Normalized terms are constructed using `Let` and `CaseOf`. term match { case top @ CaseOf(scrutinee: Var, fst @ Case(className: Var, body, NoCases)) => println(s"found a UNARY case: $scrutinee is $className") println("post-processing the body") top.copy(cases = fst.copy(body = postProcess(body))) - case top @ CaseOf(test: Var, fst @ Case(Var("true"), trueBranch, Wildcard(falseBranch))) if Desugaring.isTestVar(test) => + case top @ CaseOf(test: Var, fst @ Case(Var("true"), trueBranch, Wildcard(falseBranch))) if context.isTestVar(test) => println(s"found a if-then-else case: $test is true") val processedTrueBranch = postProcess(trueBranch) val processedFalseBranch = postProcess(falseBranch) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index 9b500556..64f5ede3 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -20,10 +20,10 @@ import scala.collection.immutable trait Transformation { self: mlscript.pretyper.Traceable => import Transformation._ - def transform(`if`: If, useNewDefs: Bool = true): TermSplit = - transformIfBody(`if`.body)(useNewDefs) ++ `if`.els.fold(Split.empty)(Split.default) + def transform(`if`: If): TermSplit = + transformIfBody(`if`.body) ++ `if`.els.fold(Split.empty)(Split.default) - private def transformIfBody(body: IfBody)(implicit useNewDefs: Bool): TermSplit = trace(s"transformIfBody <== ${inspect.shallow(body)}") { + private def transformIfBody(body: IfBody): TermSplit = trace(s"transformIfBody <== ${inspect.shallow(body)}") { body match { case IfThen(expr, rhs) => splitAnd(expr).foldRight(Split.then(rhs)) { @@ -84,7 +84,7 @@ trait Transformation { self: mlscript.pretyper.Traceable => } }(_ => "transformIfBody ==> ") - private def transformOperatorBranch(opsRhs: Var -> IfBody)(implicit useNewDefs: Bool): OperatorBranch = + private def transformOperatorBranch(opsRhs: Var -> IfBody): OperatorBranch = opsRhs match { case (op @ Var("is"), rhs) => OperatorBranch.Match(op, transformPatternMatching(rhs)) case (op, rhs) => OperatorBranch.Binary(op, transformIfBody(rhs)) @@ -93,7 +93,7 @@ trait Transformation { self: mlscript.pretyper.Traceable => /** * Transform an `IfBody` into a `PatternSplit`. */ - private def transformPatternMatching(body: IfBody)(implicit useNewDefs: Bool): PatternSplit = + private def transformPatternMatching(body: IfBody): PatternSplit = trace(s"transformPatternMatching <== ${inspect.shallow(body)}") { body match { case IfThen(expr, rhs) => @@ -125,7 +125,7 @@ trait Transformation { self: mlscript.pretyper.Traceable => } }(_ => "transformPatternMatching ==>") - private def transformPattern(term: Term)(implicit useNewDefs: Bool): Pattern = term match { + private def transformPattern(term: Term): Pattern = term match { case nme @ Var("true" | "false") => ConcretePattern(nme) case nme @ Var(name) if name.headOption.exists(_.isUpper) => ClassPattern(nme, N) case nme: Var => NamePattern(nme) @@ -145,8 +145,8 @@ trait Transformation { self: mlscript.pretyper.Traceable => throw new TransformException(msg"Unknown pattern", term.toLoc) } - private def separatePattern(term: Term)(implicit useNewDefs: Bool): (Pattern, Opt[Term]) = { - val (rawPattern, extraTest) = helpers.separatePattern(term, useNewDefs) + private def separatePattern(term: Term): (Pattern, Opt[Term]) = { + val (rawPattern, extraTest) = helpers.separatePattern(term, true) println("rawPattern: " + inspect.deep(rawPattern)) println("extraTest: " + inspect.deep(extraTest)) (transformPattern(rawPattern), extraTest) @@ -171,8 +171,8 @@ trait Transformation { self: mlscript.pretyper.Traceable => object Transformation { private object OperatorIs { - def unapply(term: Term)(implicit useNewDefs: Bool): Opt[(Term, Term)] = term match { - case App(App(Var("is"), Tup(_ -> Fld(_, scrutinee) :: Nil)), Tup(_ -> Fld(_, pattern) :: Nil)) if !useNewDefs => S(scrutinee -> pattern) + def unapply(term: Term): Opt[(Term, Term)] = term match { + // case App(App(Var("is"), Tup(_ -> Fld(_, scrutinee) :: Nil)), Tup(_ -> Fld(_, pattern) :: Nil)) if !useNewDefs => S(scrutinee -> pattern) case App(Var("is"), PlainTup(scrutinee, pattern)) => S(scrutinee -> pattern) case _ => N } From 5e9df42aa5c9012401b407901304125673fef862 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 27 Dec 2023 18:14:19 +0800 Subject: [PATCH 028/143] Track scrutiness with contexts and decouple scrutinees from symbols Finally! I made this. In this commit, we track and store the information of scrutinee through the context of per-UCS-term; at the same time, we decouple the scrutinee and term symbol. This has the following advantages. 1. The symbol corresponding to each variable can store information from different UCS terms. 2. The error location of the scrutinee is more precise. 3. This enables coverage checking to take advantage of previously collected patterns, eliminating the need to traverse the term again. In addition, there are a few minor changes. 1. All functions used for pretty printing have been extracted into a separate package, namely `mlscript.ucs.display`. 2. Common operations about `Var` have been extracted into an implicit class in `DesugarUCS` base class. --- .../main/scala/mlscript/pretyper/Symbol.scala | 81 ++---- .../src/main/scala/mlscript/ucs/Context.scala | 40 ++- .../main/scala/mlscript/ucs/DesugarUCS.scala | 93 ++++++- .../scala/mlscript/ucs/ScrutineeData.scala | 139 ++++++++-- .../scala/mlscript/ucs/context/CaseSet.scala | 97 +++++++ .../scala/mlscript/ucs/context/package.scala | 13 + shared/src/main/scala/mlscript/ucs/core.scala | 22 -- .../src/main/scala/mlscript/ucs/display.scala | 133 +++++++++ .../src/main/scala/mlscript/ucs/package.scala | 7 +- .../ucs/stages/CoverageChecking.scala | 175 ++---------- .../mlscript/ucs/stages/Desugaring.scala | 262 ++++++++---------- .../mlscript/ucs/stages/Normalization.scala | 94 +++---- .../mlscript/ucs/stages/PostProcessing.scala | 90 +++--- .../scala/mlscript/ucs/stages/package.scala | 20 -- .../src/main/scala/mlscript/ucs/syntax.scala | 46 --- .../pretyper/ucs/coverage/MissingCases.mls | 32 +-- .../pretyper/ucs/coverage/SealedClasses.mls | 4 +- 17 files changed, 729 insertions(+), 619 deletions(-) create mode 100644 shared/src/main/scala/mlscript/ucs/context/CaseSet.scala create mode 100644 shared/src/main/scala/mlscript/ucs/context/package.scala create mode 100644 shared/src/main/scala/mlscript/ucs/display.scala diff --git a/shared/src/main/scala/mlscript/pretyper/Symbol.scala b/shared/src/main/scala/mlscript/pretyper/Symbol.scala index 5763876f..c25fd1da 100644 --- a/shared/src/main/scala/mlscript/pretyper/Symbol.scala +++ b/shared/src/main/scala/mlscript/pretyper/Symbol.scala @@ -4,6 +4,7 @@ import collection.mutable.{Buffer, Map => MutMap, Set => MutSet} import mlscript.{Loc, NuFunDef, NuTypeDef, TypeName, Var} import mlscript.{Cls, Trt, Mxn, Als, Mod} import mlscript.utils._, shorthands._ +import mlscript.ucs.{Context, ScrutineeData} package object symbol { sealed abstract class Symbol(val name: Str) { @@ -54,7 +55,22 @@ package object symbol { // lazy val (scope, contents) = (enclosingScope.derive, Map.empty[Str, Symbol]) } - sealed abstract class TermSymbol(name: String) extends Symbol(name) + sealed abstract class TermSymbol(name: String) extends Symbol(name) { + private val scrutinees: MutMap[Context, ScrutineeData] = MutMap.empty + + def getOrCreateScrutinee(implicit context: Context): ScrutineeData = + scrutinees.getOrElseUpdate(context, context.freshScrutinee) + + def getScrutinee(implicit context: Context): Opt[ScrutineeData] = + scrutinees.get(context) + + def isScrutinee(implicit context: Context): Bool = scrutinees.contains(context) + + def addScrutinee(scrutinee: ScrutineeData)(implicit context: Context): Unit = { + require(!isScrutinee) + scrutinees += context -> scrutinee + } + } final class FunctionSymbol(val defn: NuFunDef) extends TermSymbol(defn.nme.name) { require(defn.isLetRec.isEmpty) @@ -70,66 +86,5 @@ package object symbol { } } - // FIXME: - // We should remove `ScrutineeSymbol` ASAP. There are too many reasons. The - // most important one I can remember right now is that multiple variables - // may share the same scrutinee symbol. But for now symbol must have a unique - // name. We should use a dedicated symbol for tracking scrutinees. - sealed abstract class ScrutineeSymbol(name: Str) extends TermSymbol(name) { - def toLoc: Opt[Loc] - - val matchedClasses: MutMap[TypeSymbol, Buffer[Loc]] = MutMap.empty - val unappliedVarMap: MutMap[TypeSymbol, Var] = MutMap.empty - val subScrutineeMap: MutMap[TypeSymbol, MutMap[Int, MutMap[Var, ValueSymbol]]] = MutMap.empty - // Hmm, maybe we can merge these three maps into one. - // Urgh, let's do this in the next refactor. - // I really should move these imperative and stateful functions to a - // separate class! - val tupleSubScrutineeMap: MutMap[Int, MutMap[Var, ValueSymbol]] = MutMap.empty - // Note that the innermost map is a map from variable names to symbols. - // Sometimes a class parameter may have many names. We maintain the - // uniqueness of the symbol for now. - - def getSubScrutineeSymbolOrElse( - classLikeSymbol: TypeSymbol, - index: Int, - name: Var, // <-- Remove this parameter after we remove `ScrutineeSymbol`. - default: => ValueSymbol - ): ValueSymbol = - subScrutineeMap.getOrElseUpdate(classLikeSymbol, MutMap.empty) - .getOrElseUpdate(index, MutMap.empty) - .getOrElseUpdate(name, default) - - def getTupleSubScrutineeSymbolOrElse( - index: Int, - name: Var, // <-- Remove this parameter after we remove `ScrutineeSymbol`. - default: => ValueSymbol - ): ValueSymbol = - tupleSubScrutineeMap.getOrElseUpdate(index, MutMap.empty) - .getOrElseUpdate(name, default) - - def addMatchedClass(symbol: TypeSymbol, loc: Opt[Loc]): Unit = { - matchedClasses.getOrElseUpdate(symbol, Buffer.empty) ++= loc - } - - def getUnappliedVarOrElse(classLikeSymbol: TypeSymbol, default: => Var): Var = - unappliedVarMap.getOrElseUpdate(classLikeSymbol, default) - - def addUnappliedVar(symbol: TypeSymbol, nme: Var): Unit = { - unappliedVarMap += symbol -> nme - } - - // Store the symbol of the parent scrutinee. I doubt if this is necessary. - private var maybeParentSymbol: Opt[ScrutineeSymbol] = N - def parentSymbol: Opt[ScrutineeSymbol] = maybeParentSymbol - def withParentSymbol(parentSymbol: ScrutineeSymbol): this.type = - maybeParentSymbol match { - case N => maybeParentSymbol = S(parentSymbol); this - case S(_) => throw new IllegalStateException("Parent symbol is already set.") - } - } - - final class ValueSymbol(val nme: Var, val hoisted: Bool) extends ScrutineeSymbol(nme.name) { - override def toLoc: Opt[Loc] = nme.toLoc - } + final class ValueSymbol(val nme: Var, val hoisted: Bool) extends TermSymbol(nme.name) } \ No newline at end of file diff --git a/shared/src/main/scala/mlscript/ucs/Context.scala b/shared/src/main/scala/mlscript/ucs/Context.scala index 1065a238..ad94bc1b 100644 --- a/shared/src/main/scala/mlscript/ucs/Context.scala +++ b/shared/src/main/scala/mlscript/ucs/Context.scala @@ -1,15 +1,15 @@ package mlscript.ucs -import collection.mutable.{Buffer, Map => MutMap, SortedMap => MutSortedMap, Set => MutSet} +import collection.mutable.{Buffer, Map => MutMap, SortedMap => MutSortedMap} import mlscript.{If, Loc, NuFunDef, NuTypeDef, TypeName, Var} import mlscript.{Cls, Trt, Mxn, Als, Mod} import mlscript.pretyper.symbol.TypeSymbol import mlscript.pretyper.Scope import mlscript.utils._, shorthands._ +import mlscript.ucs.context.MatchRegistry class Context(originalTerm: If) { - val prefix: Str = Context.freshPrefix().name - + private val prefix = Context.freshPrefix() private val cachePrefix = prefix + "$cache$" private val scrutineePrefix = prefix + "$scrut$" private val testPrefix = prefix + "$test$" @@ -27,11 +27,39 @@ class Context(originalTerm: If) { // I plan to mix the unique identifiers of UCS expressions into the prefixes. // So that the generated variables will definitely not conflict with others. val freshCache: VariableGenerator = new VariableGenerator(cachePrefix) - val freshScrutinee: VariableGenerator = new VariableGenerator(scrutineePrefix) + val freshScrutineeVar: VariableGenerator = new VariableGenerator(scrutineePrefix) val freshTest: VariableGenerator = new VariableGenerator(testPrefix) val freshShadowed: VariableGenerator = new VariableGenerator("shadowed$") + + /** The buffer contains all `ScrutineeData` created within this context. */ + private val scrutineeBuffer: Buffer[ScrutineeData] = Buffer.empty + + // TODO: Mark this two files as package private. + def freshScrutinee: ScrutineeData = { + val scrutinee = new ScrutineeData(this, N) + scrutineeBuffer += scrutinee + scrutinee + } + + // TODO: Mark this two files as package private. + def freshScrutinee(parent: ScrutineeData): ScrutineeData = { + val scrutinee = new ScrutineeData(this, S(parent)) + scrutineeBuffer += scrutinee + scrutinee + } + + /** + * Create a `MatchRegistry` from the current context. + */ + def toMatchRegistry: MatchRegistry = + scrutineeBuffer.iterator.flatMap { scrutinee => + val caseSet = scrutinee.toCaseSet + scrutinee.aliasesIterator.map(alias => (alias -> scrutinee) -> caseSet) + }.toMap } object Context { - private val freshPrefix = new VariableGenerator("ucs") -} + // TODO: Generate fresh prefix in a determinstic way. I tried to use a counter, + // but the produced value is not stable across different runs. + def freshPrefix(): Str = "ucs" +} \ No newline at end of file diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index 944bca1c..116ee0fc 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -2,10 +2,12 @@ package mlscript.ucs import collection.mutable.{Map => MutMap} import mlscript.ucs.stages._ +import mlscript.ucs.display.showNormalizedTerm import mlscript.pretyper.{PreTyper, Scope} import mlscript.pretyper.symbol._ import mlscript._, utils._, shorthands._ import mlscript.Message, Message.MessageContext +import mlscript.ucs.display.showSplit // TODO: Rename to `Desugarer` once the old desugarer is removed. trait DesugarUCS extends Transformation @@ -14,6 +16,84 @@ trait DesugarUCS extends Transformation with PostProcessing with CoverageChecking { self: PreTyper => + protected def freshSymbol(nme: Var): ValueSymbol = new ValueSymbol(nme, false) + + /** Common operations of `Var` which can be shared within all stages. */ + protected implicit class VarOps(nme: Var) { + /** Associate the given `Var` with a fresh `ValueSymbol`. */ + def withFreshSymbol: Var = nme.withSymbol(freshSymbol(nme)) + + /** + * If the given `Var` represents a class name, get its associated `ClassSymbol`. + * + * @param className the class name variable + */ + def getClassLikeSymbol: TypeSymbol = + trace(s"getClassLikeSymbol <== ${inspect.shallow(nme)}") { + nme.symbolOption match { + case S(symbol: ClassSymbol) => symbol + case S(symbol: TraitSymbol) => symbol + case S(symbol: ModuleSymbol) => symbol + case S(symbol: Symbol) => throw new DesugaringException( + msg"variable ${nme.name} is not associated with a class symbol" -> N :: Nil) + case N => throw new DesugaringException( + msg"variable ${nme.name} is not associated with any symbols" -> N :: Nil) + } + }(symbol => s"getClassLikeSymbol ==> ${symbol.name}") + + /** + * A short hand for `nme.symbol.getScrutinee` but add a diagnostic message + * to a local diagnostic archive (TODO) if there's any error. + */ + def getOrCreateScrutinee(implicit context: Context): ScrutineeData = nme.symbolOption match { + case S(symbol: TermSymbol) => symbol.getOrCreateScrutinee + case S(otherSymbol) => throw new DesugaringException( + msg"Expected scrutinee symbol, found ${nme.symbol.name}" -> nme.toLoc :: Nil + ) + case N => throw new DesugaringException( + msg"Scrutinee symbol not found" -> nme.toLoc :: Nil + ) + } + + def withScrutinee(scrutinee: ScrutineeData)(implicit context: Context): Var = nme.symbolOption match { + case S(symbol: TermSymbol) => + symbol.addScrutinee(scrutinee) + nme + case S(otherSymbol) => throw new DesugaringException( + msg"Expected scrutinee symbol, found ${nme.symbol.name}" -> nme.toLoc :: Nil + ) + case N => throw new DesugaringException( + msg"Scrutinee symbol not found" -> nme.toLoc :: Nil + ) + } + + def withResolvedTypeSymbol(implicit scope: Scope): Var = { + nme.symbol = nme.resolveTypeSymbol + nme + } + + def resolveTypeSymbol(implicit scope: Scope): TypeSymbol = scope.getTypeSymbol(nme.name) match { + case S(symbol: TraitSymbol) => + println(s"resolveTypeSymbol ${nme} ==> trait") + nme.symbol = symbol + symbol + case S(symbol: ClassSymbol) => + println(s"resolveTypeSymbol ${nme} ==> class") + nme.symbol = symbol + symbol + case S(symbol: ModuleSymbol) => + println(s"resolveTypeSymbol ${nme} ==> module") + nme.symbol = symbol + symbol + case S(symbol: MixinSymbol) => + throw new DesugaringException(msg"Mixins are not allowed in pattern" -> nme.toLoc :: Nil) + case S(symbol: TypeAliasSymbol) => + throw new DesugaringException(msg"Type alias is not allowed in pattern" -> nme.toLoc :: Nil) + case N => + throw new DesugaringException(msg"Undefined symbol found in patterns." -> nme.toLoc :: Nil) + } + } + protected def traverseIf(`if`: If)(implicit scope: Scope): Unit = { implicit val context: Context = new Context(`if`) trace("traverseIf") { @@ -22,7 +102,7 @@ trait DesugarUCS extends Transformation println("STEP 0") val transformed = transform(`if`) println("Transformed UCS term:") - println(ucs.syntax.printTermSplit(transformed)) + println(showSplit(transformed)) transformed } // Stage 1: Desugaring @@ -30,7 +110,7 @@ trait DesugarUCS extends Transformation println("STEP 1") val desugared = desugar(transformed) println("Desugared UCS term:") - println(ucs.core.printSplit(desugared)) + println(showSplit(desugared)) desugared } traceWithTopic("traverse") { @@ -42,7 +122,7 @@ trait DesugarUCS extends Transformation println("STEP 2") val normalized = normalize(desugared) println("Normalized UCS term:") - printNormalizedTerm(normalized) + println(showNormalizedTerm(normalized)) normalized } // Stage 3: Post-processing @@ -50,7 +130,7 @@ trait DesugarUCS extends Transformation println("STEP 3") val postProcessed = postProcess(normalized) println("Post-processed UCS term:") - printNormalizedTerm(postProcessed) + println(showNormalizedTerm(postProcessed)) postProcessed } // Stage 4: Coverage checking @@ -87,11 +167,6 @@ trait DesugarUCS extends Transformation private def traversePattern(scrutinee: Var, pattern: core.Pattern)(implicit scope: Scope): List[Var -> Symbol] = trace(s"traversePattern <== $pattern") { - lazy val scrutineeSymbol = scrutinee.symbol match { - case symbol: ScrutineeSymbol => symbol - case other: Symbol => - throw new DesugaringException(msg"Scrutinee is not a scrutinee symbol" -> scrutinee.toLoc :: Nil) - } pattern match { case core.Pattern.Literal(literal) => Nil case core.Pattern.Name(nme) => nme -> nme.symbol :: Nil diff --git a/shared/src/main/scala/mlscript/ucs/ScrutineeData.scala b/shared/src/main/scala/mlscript/ucs/ScrutineeData.scala index 2b259b99..3925e446 100644 --- a/shared/src/main/scala/mlscript/ucs/ScrutineeData.scala +++ b/shared/src/main/scala/mlscript/ucs/ScrutineeData.scala @@ -1,20 +1,32 @@ package mlscript.ucs -import collection.mutable.{Buffer, Map => MutMap, SortedMap => MutSortedMap, Set => MutSet} -import mlscript.{Loc, NuFunDef, NuTypeDef, TypeName, Var} +import collection.mutable.{Buffer, Map => MutMap, SortedMap => MutSortedMap, SortedSet => MutSortedSet} +import mlscript.{Loc, Located, NuFunDef, NuTypeDef, TypeName, Var} import mlscript.{Cls, Trt, Mxn, Als, Mod} import mlscript.pretyper.symbol.TypeSymbol import mlscript.utils._, shorthands._ +import mlscript.ucs.context.CaseSet class ClassPatternInfo(scrutinee: ScrutineeData) { - private val locations: Buffer[Loc] = Buffer.empty + private val locationsBuffer: Buffer[Loc] = Buffer.empty private var unappliedVarOpt: Opt[Var] = N private val parameters: MutSortedMap[Int, ScrutineeData] = MutSortedMap.empty - def getParameter(index: Int): ScrutineeData = - parameters.getOrElse(index, new ScrutineeData(S(scrutinee))) + /** + * Get or create a sub-scrutinee for the given parameter index. + * + * @param index the index of the parameter. + * @return a `ScrutineeData` for the parameter whose parent scrutinee is the + * current scrutinee + */ + def getParameter(index: Int): ScrutineeData = { + require(index >= 0) + parameters.getOrElseUpdate(index, scrutinee.freshSubScrutinee) + } + + def addLocation(located: Located): Unit = located.getLoc.foreach(locationsBuffer += _) - def addLocation(location: Opt[Loc]): Unit = locations ++= location + def addLocation(location: Opt[Loc]): Unit = locationsBuffer ++= location def getUnappliedVar(default: => Var): Var = unappliedVarOpt.getOrElse { @@ -22,17 +34,28 @@ class ClassPatternInfo(scrutinee: ScrutineeData) { unappliedVarOpt = S(unappliedVar) unappliedVar } + + def arity: Opt[Int] = parameters.keysIterator.maxOption.map(_ + 1) + + def firstOccurrence: Option[Loc] = locationsBuffer.headOption + + def locations: Ls[Loc] = locationsBuffer.toList } class TuplePatternInfo(scrutinee: ScrutineeData) { - private val locations: Buffer[Loc] = Buffer.empty + private val locationsBuffer: Buffer[Loc] = Buffer.empty private val fields: MutSortedMap[Int, ScrutineeData] = MutSortedMap.empty def getField(index: Int): ScrutineeData = - fields.getOrElse(index, new ScrutineeData(S(scrutinee))) + fields.getOrElseUpdate(index, scrutinee.freshSubScrutinee) + + def arity: Opt[Int] = fields.keysIterator.maxOption.map(_ + 1) + + def locations: Ls[Loc] = locationsBuffer.toList } -class ScrutineeData(parent: Opt[ScrutineeData]) { +class ScrutineeData(val context: Context, parent: Opt[ScrutineeData]) { + private val locations: Buffer[Loc] = Buffer.empty private var generatedVarOpt: Opt[Var] = N private val classLikePatterns: MutMap[TypeSymbol, ClassPatternInfo] = MutMap.empty // Currently we only support simple tuple patterns, so there is only _one_ @@ -42,23 +65,99 @@ class ScrutineeData(parent: Opt[ScrutineeData]) { // If we support tuple pattern splice, we need a more expressive key in the // map's type. private var tuplePatternOpt: Opt[TuplePatternInfo] = N + private var alisesSet: MutSortedSet[Var] = MutSortedSet.empty - def getClassUnappliedVar(classLikeSymbol: TypeSymbol, default: => Var): Var = - classLikePatterns.getOrElse(classLikeSymbol, new ClassPatternInfo(this)).getUnappliedVar(default) + def +=(alias: Var): Unit = alisesSet += alias - def getClassLikeParameter(classLikeSymbol: TypeSymbol, index: Int, location: Opt[Loc]): ScrutineeData = { - val classPattern = classLikePatterns.getOrElse(classLikeSymbol, new ClassPatternInfo(this)) - classPattern.addLocation(location) - classPattern.getParameter(index) - } + def withAlias(alias: Var): ScrutineeData = { this += alias; this } + + def aliasesIterator: Iterator[Var] = alisesSet.iterator - def getClassLikeParameter(classLikeSymbol: TypeSymbol, index: Int): ScrutineeData = - classLikePatterns.getOrElse(classLikeSymbol, new ClassPatternInfo(this)).getParameter(index) + /** + * If there is already a `ClassPatternInfo` for the given symbol, return it. + * Otherwise, create a new `ClassPatternInfo` and return it. + */ + def getOrCreateClassPattern(classLikeSymbol: TypeSymbol): ClassPatternInfo = + classLikePatterns.getOrElseUpdate(classLikeSymbol, new ClassPatternInfo(this)) - def getTupleField(index: Int): ScrutineeData = + /** + * Get the class pattern but DO NOT create a new one if there isn't. This + * function is mainly used in post-processing because we don't want to + * accidentally create new patterns. + */ + def getClassPattern(classLikeSymbol: TypeSymbol): Opt[ClassPatternInfo] = + classLikePatterns.get(classLikeSymbol) + + /** + * If there is already a `TuplePatternInfo`, return it. Otherwise, create a + * new `TuplePatternInfo` and return it. + * + * **NOTE**: There's only one slot for tuple patterns because we cannot + * differentiate tuple types in underlying MLscript case terms. In the future, + * we will support complex tuple patterns, so we need to extend this method to + * a signature like this. + * + * ```scala + * def getOrCreateTuplePattern(dimension: TupleDimension): TuplePatternInfo + * case class TupleDimension(knownArity: Int, hasSplice: Bool) + * ``` + */ + def getOrCreateTuplePattern: TuplePatternInfo = tuplePatternOpt.getOrElse { val tuplePattern = new TuplePatternInfo(this) tuplePatternOpt = S(tuplePattern) tuplePattern - }.getField(index) + } + + def classLikePatternsIterator: Iterator[TypeSymbol -> ClassPatternInfo] = classLikePatterns.iterator + + /** Get the name representation of patterns. Only for debugging. */ + def patterns: Iterator[Str] = { + val classLikePatternsStr = classLikePatterns.iterator.map { case (symbol, pattern) => + s"${symbol.name}(${pattern.arity.fold("?")(_.toString)})" + } + val tuplePatternStr = tuplePatternOpt.iterator.map { tuplePattern => + s"tuple(${tuplePattern.arity.fold("?")(_.toString)})" + } + classLikePatternsStr ++ tuplePatternStr + } + + def freshSubScrutinee: ScrutineeData = context.freshScrutinee(this) + + def toCaseSet: CaseSet = { + import mlscript.ucs.context.Pattern + val cases = classLikePatterns.iterator.map { case (symbol, pattern) => + Pattern.ClassLike(symbol) -> pattern.locations + }.toMap[Pattern, Ls[Loc]] + val tuplePattern = tuplePatternOpt.map { tuplePattern => + Pattern.Tuple() -> tuplePattern.locations + }.toMap[Pattern, Ls[Loc]] + CaseSet(cases ++ tuplePattern, false) + } +} + +object ScrutineeData { + // We might need to move these method to a private `VarOps` because they may + // emit diagnostics. + + import mlscript.Term + import mlscript.pretyper.symbol.TermSymbol + + def unapply(term: Term)(implicit context: Context): Opt[ScrutineeData] = term match { + case v: Var => v.symbol match { + case symbol: TermSymbol => symbol.getScrutinee + case _ => N + } + case _ => N + } + + object WithVar { + def unapply(term: Term)(implicit context: Context): Opt[(ScrutineeData, Var)] = term match { + case v @ Var(_) => v.symbol match { + case symbol: TermSymbol => symbol.getScrutinee.map(_ -> v) + case _ => N + } + case _ => N + } + } } \ No newline at end of file diff --git a/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala b/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala new file mode 100644 index 00000000..a3fc6fdf --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala @@ -0,0 +1,97 @@ +package mlscript.ucs.context + +import mlscript.{Lit, Loc} +import mlscript.pretyper.symbol.TypeSymbol +import mlscript.utils._, shorthands._ + +sealed abstract class Pattern { + override def toString(): String = this match { + case Pattern.ClassLike(symbol) => s"${symbol.defn.kind.str} `${symbol.name}`" + case Pattern.Tuple() => "tuple" + case Pattern.Literal(literal) => s"literal ${inspect.deep(literal)}" + } +} + +object Pattern { + final case class ClassLike(symbol: TypeSymbol) extends Pattern + // Currently, there is only simple tuple pattern, so we cannot differentiate + // between tuple patterns of different arity. That's why the parameter list + // is empty for now. + final case class Tuple() extends Pattern + final case class Literal(literal: Lit) extends Pattern +} + +/** + * A `CaseSet` represents all patterns that a particular scrutinee is + * being matched with within a UCS expression. Each Pattern is associated + * with the locations where these patterns appear. + * + * @param patterns a set of patterns that the scrutinee is matched with. + * @param hasWildcard if the scrutinee is matched with a wildcard pattern. + */ +final case class CaseSet(val cases: Map[Pattern, Ls[Loc]], val hasWildcard: Bool) { + /** TODO: This seems useless. */ + @inline def withWildcard: CaseSet = if (hasWildcard) this else copy(hasWildcard = true) + + /** + * Split the pattern set into two pattern sets. + * + * For example, let A be the base type of B, C, and D. Plus, class `Z` is + * unrelated to any of them. Suppose the initial pattern set is + * `{ A, B, C, Z }`. Splitting the set results in two sets, one set + * contains classes that are compatible with `A`, and the other set + * contains classes that are unrelated to `A`. + * + * For example, if we split the set with `A`, then we get `{ B, C }` and + * set `{ Z }`. Set `{ B, C }` represents that the scrutinee can be further + * refined to class `B` or `class C`. Set `{ Z }` represents that if the + * scrutinee is not `A`, then it can be `Z`. + * + * If `A` is sealed to `B`, `C`, and `D`, then we get `{ B, C, D }` and + * `{ Z }`. Because if the scrutinee is assumed to be `A`, then it can also + * be `D` other than `B`, `C`. + * + * @param classLikeSymbol the type symbol represents the class like type + * @return If the pattern set doesn't include the given type symbol, this + * returns `None`. Otherwise, the function returns a triplet of the + * locations where the pattern appears, the related patterns, and + * unrelated patterns. + */ + def split(classLikeSymbol: TypeSymbol): Opt[(Ls[Loc], CaseSet, CaseSet)] = { + val classLikePattern = Pattern.ClassLike(classLikeSymbol) + cases.get(classLikePattern).map { locations => + val withoutSymbol = cases - classLikePattern + val relatedPatterns = withoutSymbol.filter { + case (Pattern.ClassLike(otherSymbol), _) => otherSymbol.baseTypes.contains(classLikeSymbol) + case ((_: Pattern.Tuple | _: Pattern.Literal), _) => false + } ++ classLikeSymbol.sealedDerivedTypes.iterator.map { symbol => + Pattern.ClassLike(symbol) -> symbol.defn.nme.toLoc.toList + } + val unrelatedPatterns = withoutSymbol.filter { + case (Pattern.ClassLike(otherSymbol), _) => !otherSymbol.baseTypes.contains(classLikeSymbol) + case ((_: Pattern.Tuple | _: Pattern.Literal), _) => true + } + (locations, copy(relatedPatterns), copy(unrelatedPatterns)) + } + } + + /** Add a type sysmbol as a class like pattern to the set. */ + def add(classLikeSymbol: TypeSymbol, location: Opt[Loc]): CaseSet = { + val classLikePattern = Pattern.ClassLike(classLikeSymbol) + copy(cases = cases.updatedWith(classLikePattern) { + case N => S(location.toList) + case S(locations) => S(location.toList ++ locations) + }) + } + + /** Get an iterator of only patterns. */ + @inline def patterns: Iterator[Pattern] = cases.iterator.map(_._1) + + @inline def isEmpty: Bool = cases.isEmpty + + @inline def size: Int = cases.size +} + +object CaseSet { + lazy val empty: CaseSet = CaseSet(Map.empty, false) +} diff --git a/shared/src/main/scala/mlscript/ucs/context/package.scala b/shared/src/main/scala/mlscript/ucs/context/package.scala new file mode 100644 index 00000000..03a1e46b --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/context/package.scala @@ -0,0 +1,13 @@ +package mlscript.ucs + +import mlscript.{Loc, Var} +import mlscript.pretyper.symbol.TypeSymbol +import mlscript.utils._, shorthands._ + +package object context { + type NamedScrutineeData = (Var -> ScrutineeData) + + type MatchRegistry = Map[NamedScrutineeData, CaseSet] + + type SeenRegistry = Map[NamedScrutineeData, (TypeSymbol, Ls[Loc], CaseSet)] +} diff --git a/shared/src/main/scala/mlscript/ucs/core.scala b/shared/src/main/scala/mlscript/ucs/core.scala index 65a194a3..14b1577f 100644 --- a/shared/src/main/scala/mlscript/ucs/core.scala +++ b/shared/src/main/scala/mlscript/ucs/core.scala @@ -120,26 +120,4 @@ package object core { def just(term: Term): Split = Else(term) } - - @inline def printSplit(s: Split): Str = showSplit("if", s) - - def showSplit(prefix: Str, s: Split): Str = { - // TODO: tailrec - def split(s: Split, isFirst: Bool, isTopLevel: Bool): Lines = s match { - case Split.Cons(head, tail) => (branch(head) match { - case (n, line) :: tail => (n, (if (isTopLevel) "" else "") + s"$line") :: tail - case Nil => Nil - }) ::: split(tail, false, isTopLevel) - case Split.Let(_, nme, rhs, tail) => (0, s"let ${showVar(nme)} = $rhs") :: split(tail, false, isTopLevel) - case Split.Else(term) => (if (isFirst) (0, s"then $term") else (0, s"else $term")) :: Nil - case Split.Nil => Nil - } - def branch(b: Branch): Lines = { - val Branch(scrutinee, pattern, continuation) = b - s"${showVar(scrutinee)} is $pattern" #: split(continuation, true, false) - } - val lines = split(s, true, true) - (if (prefix.isEmpty) lines else prefix #: lines) - .iterator.map { case (n, line) => " " * n + line }.mkString("\n") - } } \ No newline at end of file diff --git a/shared/src/main/scala/mlscript/ucs/display.scala b/shared/src/main/scala/mlscript/ucs/display.scala new file mode 100644 index 00000000..63fb23a5 --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/display.scala @@ -0,0 +1,133 @@ +package mlscript.ucs + +import mlscript.ucs.{Context, Lines, LinesOps, ScrutineeData} +import mlscript.ucs.{core, syntax} +import mlscript.pretyper.symbol.{TermSymbol, TypeSymbol} +import mlscript.{App, CaseOf, Fld, FldFlags, Let, Loc, Sel, SimpleTerm, Term, Tup, Var} +import mlscript.{CaseBranches, Case, Wildcard, NoCases} +import mlscript.utils._, shorthands._ + +/** All the pretty-printing stuff go here. */ +package object display { + + /** If the variable has a location, mark it with an asterisk. + * If the variable is associated with a term symbol, mark it with an asterisk. + * If the variable is associated with a term symbol and has a scrutinee, mark it with a double dagger. */ + private def showVar(`var`: Var)(implicit context: Context): String = { + val postfix = `var`.symbolOption match { + case S(symbol: TermSymbol) if symbol.isScrutinee => "‡" + case S(_: TermSymbol) => "†" + case S(_: TypeSymbol) => "◊" + case N => "" + } + `var`.name + (`var`.symbolOption.fold("")(_ => "*")) + postfix + } + + def showSplit(split: syntax.TermSplit)(implicit context: Context): Str = { + // TODO: tailrec + def termSplit(split: syntax.TermSplit, isFirst: Bool, isAfterAnd: Bool): Lines = split match { + case syntax.Split.Cons(head, tail) => (termBranch(head) match { + case (n, line) :: tail => (n, (if (isAfterAnd) "" else "and ") + s"$line") :: tail + case Nil => Nil + }) ::: termSplit(tail, false, isAfterAnd) + case syntax.Split.Let(_, nme, rhs, tail) => (0, s"let $nme = $rhs") :: termSplit(tail, false, isAfterAnd) + case syntax.Split.Else(term) => (if (isFirst) (0, s"then $term") else (0, s"else $term")) :: Nil + case syntax.Split.Nil => Nil + } + def termBranch(branch: syntax.TermBranch): Lines = branch match { + case syntax.TermBranch.Boolean(test, continuation) => + s"$test" #: termSplit(continuation, true, false) + case syntax.TermBranch.Match(scrutinee, continuation) => + s"$scrutinee is" #: patternSplit(continuation) + case syntax.TermBranch.Left(left, continuation) => + s"$left" #: operatorSplit(continuation) + } + def patternSplit(split: syntax.PatternSplit): Lines = split match { + case syntax.Split.Cons(head, tail) => patternBranch(head) ::: patternSplit(tail) + case syntax.Split.Let(rec, nme, rhs, tail) => (0, s"let $nme = $rhs") :: patternSplit(tail) + case syntax.Split.Else(term) => (0, s"else $term") :: Nil + case syntax.Split.Nil => Nil + } + def operatorSplit(split: syntax.OperatorSplit): Lines = split match { + case syntax.Split.Cons(head, tail) => operatorBranch(head) ::: operatorSplit(tail) + case syntax.Split.Let(rec, nme, rhs, tail) => (0, s"let $nme = $rhs") :: operatorSplit(tail) + case syntax.Split.Else(term) => (0, s"else $term") :: Nil + case syntax.Split.Nil => Nil + } + def operatorBranch(branch: syntax.OperatorBranch): Lines = + s"${branch.operator}" #: (branch match { + case syntax.OperatorBranch.Match(_, continuation) => patternSplit(continuation) + case syntax.OperatorBranch.Binary(_, continuation) => termSplit(continuation, true, true) + }) + def patternBranch(branch: syntax.PatternBranch): Lines = { + val syntax.PatternBranch(pattern, continuation) = branch + termSplit(continuation, true, false) match { + case (0, line) :: lines => (0, s"$pattern $line") :: lines + case lines => (0, pattern.toString) :: lines + } + } + ("if" #: termSplit(split, true, true)).iterator.map { case (n, line) => " " * n + line }.mkString("\n") + } + + @inline def showSplit(s: core.Split)(implicit context: Context): Str = showSplit("if", s) + + def showSplit(prefix: Str, s: core.Split)(implicit context: Context): Str = { + // TODO: tailrec + def split(s: core.Split, isFirst: Bool, isTopLevel: Bool): Lines = s match { + case core.Split.Cons(head, tail) => (branch(head) match { + case (n, line) :: tail => (n, (if (isTopLevel) "" else "") + s"$line") :: tail + case Nil => Nil + }) ::: split(tail, false, isTopLevel) + case core.Split.Let(_, nme, rhs, tail) => (0, s"let ${showVar(nme)} = $rhs") :: split(tail, false, isTopLevel) + case core.Split.Else(term) => (if (isFirst) (0, s"then $term") else (0, s"else $term")) :: Nil + case core.Split.Nil => Nil + } + def branch(b: core.Branch): Lines = { + val core.Branch(scrutinee, pattern, continuation) = b + s"${showVar(scrutinee)} is $pattern" #: split(continuation, true, false) + } + val lines = split(s, true, true) + (if (prefix.isEmpty) lines else prefix #: lines) + .iterator.map { case (n, line) => " " * n + line }.mkString("\n") + } + + /** + * Convert a normalized term to a string with indentation. It makes use of + * some special markers. + * + * - `*` if the variable has a location. + * - `†` if the variable is associated with a term symbol. + * - `‡` if the variable is associated with a term symbol and has a scrutinee. + */ + def showNormalizedTerm(term: Term)(implicit context: Context): String = { + def showTerm(term: Term): Lines = term match { + case let: Let => showLet(let) + case caseOf: CaseOf => showCaseOf(caseOf) + case other => (0, other.toString) :: Nil + } + def showScrutinee(term: Term): Str = term match { + case vari: Var => showVar(vari) + case _ => term.toString + } + def showPattern(pat: SimpleTerm): Str = pat match { + case vari: Var => showVar(vari) + case _ => pat.toString + } + def showCaseOf(caseOf: CaseOf): Lines = { + val CaseOf(trm, cases) = caseOf + s"case ${showScrutinee(trm)} of" ##: showCaseBranches(cases) + } + def showCaseBranches(caseBranches: CaseBranches): Lines = + caseBranches match { + case Case(pat, rhs, tail) => + (s"${showPattern(pat)} ->" @: showTerm(rhs)) ++ showCaseBranches(tail) + case Wildcard(term) => s"_ ->" @: showTerm(term) + case NoCases => Nil + } + def showLet(let: Let): Lines = { + val Let(rec, nme, rhs, body) = let + (0, s"let ${showVar(nme)} = $rhs") :: showTerm(body) + } + showTerm(term).map { case (n, line) => " " * n + line }.mkString("\n") + } +} diff --git a/shared/src/main/scala/mlscript/ucs/package.scala b/shared/src/main/scala/mlscript/ucs/package.scala index e284d520..7cd468c4 100644 --- a/shared/src/main/scala/mlscript/ucs/package.scala +++ b/shared/src/main/scala/mlscript/ucs/package.scala @@ -11,6 +11,8 @@ package object ucs { nextIndex += 1 Var(s"$prefix$thisIndex") } + + def reset(): Unit = nextIndex = 0 } type Lines = List[(Int, String)] @@ -38,9 +40,4 @@ package object ucs { } } } - - /** If the variable is associated with a symbol, mark it with an asterisk. - * If the variable has a location, mark it with a dagger. */ - private[ucs] def showVar(`var`: Var): String = - `var`.name + (`var`.symbolOption.fold("")(_ => "*")) + (`var`.toLoc.fold("")(_ => "†")) } diff --git a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala index 4108b807..851535cb 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala @@ -3,70 +3,36 @@ package mlscript.ucs.stages import annotation.tailrec import collection.mutable.ListBuffer import mlscript.{Case, CaseBranches, CaseOf, Let, Lit, Loc, NoCases, Term, Var, Wildcard} +import mlscript.ucs.{Context, ScrutineeData} import mlscript.pretyper.symbol._ import mlscript.utils._, shorthands._ import mlscript.Message, Message.MessageContext import mlscript.{SimpleTerm, Diagnostic, ErrorReport, WarningReport} +import mlscript.ucs.context.{CaseSet, NamedScrutineeData, MatchRegistry, SeenRegistry} trait CoverageChecking { self: mlscript.pretyper.Traceable => import CoverageChecking._ - def checkCoverage(term: Term): Ls[Diagnostic] = { - val registry = collectRegistry(term) + def checkCoverage(term: Term)(implicit context: Context): Ls[Diagnostic] = { + val registry = context.toMatchRegistry println("collected match registry: " + showRegistry(registry)) checkCoverage(term, Map.empty, registry, Map.empty) } - private def collectRegistry(term: Term): MatchRegistry = { - @tailrec - def rec(acc: MatchRegistry, rest: Ls[Term]): MatchRegistry = - rest match { - case Nil => println("end"); acc - case head :: tail => - println(s"collect ${inspect.shallow(head)}") - head match { - case Let(_, _, _, body) => - println(s"continue on ${inspect.shallow(body)}") - rec(acc, body :: tail) - case CaseOf(Scrutinee(_, scrutinee), cases) => - println(s"scrutinee: ${scrutinee.name}") - rec( - acc.updatedWith(scrutinee)(vs => S(cases.foldLeft(vs.getOrElse(CaseSet.empty))({ - case (acc, (className: Var) -> _) => - println(s"found pattern $className (has location = ${className.toLoc.nonEmpty})") - val classLikeSymbol = className.symbolOption.flatMap(_.typeSymbolOption).getOrElse { - throw new Exception(s"$className is not associated with any type symbol.") - } - acc.add(classLikeSymbol, className.toLoc) - case (acc, (literal: Lit) -> _) => - println(s"TODO: skipped literal $literal") - acc - })((x, _) => x))), - tail ++ cases.foldLeft - (Nil: Ls[Term]) - ({ case (acc, _ -> body) => body :: acc }) - ((acc, els) => els.fold(acc)(_ :: acc)) - ) - case _ => rec(acc, tail) - } - } - rec(Map.empty, term :: Nil) - } - private def checkCoverage( term: Term, pending: MatchRegistry, working: MatchRegistry, seen: SeenRegistry - ): Ls[Diagnostic] = + )(implicit context: Context): Ls[Diagnostic] = trace(s"checkCoverage <== ${inspect.shallow(term)}, ${pending.size} pending, ${working.size} working, ${seen.size} seen") { println(s"seen: " + (if (seen.isEmpty) "empty" else - seen.iterator.map { case (k, (s, _, _)) => s"${k.name} is ${s.name}" }.mkString(", ") + seen.iterator.map { case ((k, _), (s, _, _)) => s"${k.name} is ${s.name}" }.mkString(", ") )) term match { case Let(_, _, _, body) => checkCoverage(body, pending, working, seen) - case CaseOf(Scrutinee(scrutineeVar, scrutinee), cases) => - println(s"scrutinee: ${scrutinee.name}") + case CaseOf(ScrutineeData.WithVar(scrutinee, scrutineeVar), cases) => + println(s"scrutinee: ${scrutineeVar.name}") // If the scrutinee is still pending (i.e., not matched yet), then we // remove it from the pending list. If the scrutinee is matched, and // there are still classes to be matched, then we find the remaining @@ -75,19 +41,20 @@ trait CoverageChecking { self: mlscript.pretyper.Traceable => // 1. The scrutinee has been never visited, which is an error. // 2. It has been matched to be an instance of some class. Therefore, // we need to check if this is a contradiction. - val (unseenPatterns, newPending) = pending.get(scrutinee) match { - case S(matchedClasses) => matchedClasses -> (pending - scrutinee) - case N => working.get(scrutinee) match { + val namedScrutinee = scrutineeVar -> scrutinee + val (unseenPatterns, newPending) = pending.get(namedScrutinee) match { + case S(matchedClasses) => matchedClasses -> (pending - namedScrutinee) + case N => working.get(namedScrutinee) match { case S(unseenPatterns) => unseenPatterns -> pending case N => // Neither in the working list nor in the pending list. - seen.get(scrutinee) match { + seen.get(namedScrutinee) match { // The scrutine may have been matched. case S((_, _, remainingPatterns)) => remainingPatterns -> pending case N => // The scrutinee has never been visited. This should not happen. println("working list: " + showRegistry(working)) - throw new Exception(s"Scrutinee ${scrutinee.name} is not in the working list.") + throw new Exception(s"Scrutinee ${scrutineeVar.name} is not in the working list.") } } } @@ -107,17 +74,17 @@ trait CoverageChecking { self: mlscript.pretyper.Traceable => println("remaining patterns: " + remainingPatterns.patterns.mkString("[", ", ", "]")) // Remove the scrutinee from the working list. val newWorking = if (remainingPatterns.isEmpty) - working - scrutinee + working - namedScrutinee else - working.updated(scrutinee, remainingPatterns) + working.updated(namedScrutinee, remainingPatterns) // Add "`scrutinee` is `className`" to the seen registry. - val newSeen = seen + (scrutinee -> (classSymbol, locations, refiningPatterns)) + val newSeen = seen + (namedScrutinee -> (classSymbol, locations, refiningPatterns)) ( remainingPatterns, diagnostics ++ checkCoverage(body, newPending, newWorking, newSeen) ) case N => - unseenPatterns -> (diagnostics :+ (seen.get(scrutinee) match { + unseenPatterns -> (diagnostics :+ (seen.get(namedScrutinee) match { case S((`classSymbol`, _, _)) => WarningReport("tautology", Nil, Diagnostic.PreTyping) case S(_) => ErrorReport("contradiction", Nil, Diagnostic.PreTyping) case N => ErrorReport("unvisited scrutinee", Nil, Diagnostic.PreTyping) @@ -130,10 +97,10 @@ trait CoverageChecking { self: mlscript.pretyper.Traceable => case ((missingCases, diagnostics), N) => println("remaining cases should are not covered") println("MISSING cases: " + missingCases.patterns.mkString("[", ", ", "]")) - diagnostics ++ explainMissingCases(scrutinee, seen, missingCases) + diagnostics ++ explainMissingCases(namedScrutinee, seen, missingCases) case ((remainingCases, diagnostics), S(default)) => println("remaining cases should be covered by the wildcard") - checkCoverage(default, newPending, working.updated(scrutinee, remainingCases), seen) + checkCoverage(default, newPending, working.updated(namedScrutinee, remainingCases), seen) } case other => println("STOP"); Nil } @@ -141,105 +108,13 @@ trait CoverageChecking { self: mlscript.pretyper.Traceable => } object CoverageChecking { - type MatchRegistry = Map[ScrutineeSymbol, CaseSet] - - type SeenRegistry = Map[ScrutineeSymbol, (TypeSymbol, Ls[Loc], CaseSet)] - - sealed abstract class Pattern { - override def toString(): String = this match { - case Pattern.ClassLike(symbol) => s"${symbol.defn.kind.str} `${symbol.name}`" - case Pattern.Tuple(_) => "tuple" - case Pattern.Literal(literal) => s"literal ${inspect.deep(literal)}" - } - } - object Pattern { - final case class ClassLike(symbol: TypeSymbol) extends Pattern - final case class Tuple(TODO: Nothing) extends Pattern // To be implemented in the near future. - final case class Literal(literal: Lit) extends Pattern - } - - /** - * A `CaseSet` represents all patterns that a particular scrutinee is - * being matched with within a UCS expression. Each Pattern is associated - * with the locations where these patterns appear. - * - * @param patterns a set of patterns that the scrutinee is matched with. - * @param hasWildcard if the scrutinee is matched with a wildcard pattern. - */ - final case class CaseSet(val cases: Map[Pattern, Ls[Loc]], val hasWildcard: Bool) { - /** TODO: This seems useless. */ - @inline def withWildcard: CaseSet = if (hasWildcard) this else copy(hasWildcard = true) - - /** - * Split the pattern set into two pattern sets. - * - * For example, let A be the base type of B, C, and D. Plus, class `Z` is - * unrelated to any of them. Suppose the initial pattern set is - * `{ A, B, C, Z }`. Splitting the set results in two sets, one set - * contains classes that are compatible with `A`, and the other set - * contains classes that are unrelated to `A`. - * - * For example, if we split the set with `A`, then we get `{ B, C }` and - * set `{ Z }`. Set `{ B, C }` represents that the scrutinee can be further - * refined to class `B` or `class C`. Set `{ Z }` represents that if the - * scrutinee is not `A`, then it can be `Z`. - * - * If `A` is sealed to `B`, `C`, and `D`, then we get `{ B, C, D }` and - * `{ Z }`. Because if the scrutinee is assumed to be `A`, then it can also - * be `D` other than `B`, `C`. - * - * @param classLikeSymbol the type symbol represents the class like type - * @return If the pattern set doesn't include the given type symbol, this - * returns `None`. Otherwise, the function returns a triplet of the - * locations where the pattern appears, the related patterns, and - * unrelated patterns. - */ - def split(classLikeSymbol: TypeSymbol): Opt[(Ls[Loc], CaseSet, CaseSet)] = { - val classLikePattern = Pattern.ClassLike(classLikeSymbol) - cases.get(classLikePattern).map { locations => - val withoutSymbol = cases - classLikePattern - val relatedPatterns = withoutSymbol.filter { - case (Pattern.ClassLike(otherSymbol), _) => otherSymbol.baseTypes.contains(classLikeSymbol) - case ((_: Pattern.Tuple | _: Pattern.Literal), _) => false - } ++ classLikeSymbol.sealedDerivedTypes.iterator.map { symbol => - Pattern.ClassLike(symbol) -> symbol.defn.nme.toLoc.toList - } - val unrelatedPatterns = withoutSymbol.filter { - case (Pattern.ClassLike(otherSymbol), _) => !otherSymbol.baseTypes.contains(classLikeSymbol) - case ((_: Pattern.Tuple | _: Pattern.Literal), _) => true - } - (locations, copy(relatedPatterns), copy(unrelatedPatterns)) - } - } - - /** Add a type sysmbol as a class like pattern to the set. */ - def add(classLikeSymbol: TypeSymbol, location: Opt[Loc]): CaseSet = { - val classLikePattern = Pattern.ClassLike(classLikeSymbol) - copy(cases = cases.updatedWith(classLikePattern) { - case N => S(location.toList) - case S(locations) => S(location.toList ++ locations) - }) - } - - /** Get an iterator of only patterns. */ - @inline def patterns: Iterator[Pattern] = cases.iterator.map(_._1) - - @inline def isEmpty: Bool = cases.isEmpty - - @inline def size: Int = cases.size - } - - object CaseSet { - lazy val empty: CaseSet = CaseSet(Map.empty, false) - } - /** Create an `ErrorReport` that explains missing cases. */ - private def explainMissingCases(scrutinee: ScrutineeSymbol, seen: SeenRegistry, missingCases: CaseSet): Opt[ErrorReport] = + private def explainMissingCases(scrutinee: NamedScrutineeData, seen: SeenRegistry, missingCases: CaseSet): Opt[ErrorReport] = if (missingCases.isEmpty) { N } else { S(ErrorReport({ - val lines = (msg"Scrutinee `${scrutinee.name}` has ${"missing case".pluralize(missingCases.size, true)}" -> scrutinee.toLoc) :: + val lines = (msg"Scrutinee `${scrutinee._1.name}` has ${"missing case".pluralize(missingCases.size, true)}" -> scrutinee._1.toLoc) :: (missingCases.cases.iterator.flatMap { case (pattern, locations) => (msg"It can be ${pattern.toString}" -> locations.headOption) :: Nil }.toList) @@ -249,7 +124,7 @@ object CoverageChecking { seen.iterator.zipWithIndex.map { case ((scrutinee, (classSymbol, locations, cases)), i) => val prologue = if (i === 0) "When " else "" val epilogue = if (seen.size === 1) "" else if (i === seen.size - 1) ", and" else "," - msg"${prologue}scrutinee `${scrutinee.name}` is `${classSymbol.name}`$epilogue" -> locations.headOption + msg"${prologue}scrutinee `${scrutinee._1.name}` is `${classSymbol.name}`$epilogue" -> locations.headOption }.toList ::: lines } }, true, Diagnostic.PreTyping)) @@ -258,7 +133,7 @@ object CoverageChecking { /** A helper function that prints entries from the given registry line by line. */ private def showRegistry(registry: MatchRegistry): Str = if (registry.isEmpty) "empty" else - registry.iterator.map { case (scrutinee, matchedClasses) => - matchedClasses.patterns.mkString(s">>> ${scrutinee.name} => [", ", ", "]") + registry.iterator.map { case (scrutineeVar -> scrutinee, matchedClasses) => + matchedClasses.patterns.mkString(s">>> ${scrutineeVar.name} => [", ", ", "]") }.mkString("\n", "\n", "") } diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index f2d69e38..752ec1ad 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -8,6 +8,7 @@ import mlscript.pretyper.symbol._ import mlscript.pretyper.{PreTyper, Scope} import mlscript.ucs.DesugaringException import mlscript.Message, Message.MessageContext +import mlscript.ucs.ScrutineeData /** * The desugaring stage of UCS. In this stage, we transform the source abstract @@ -27,6 +28,8 @@ import mlscript.Message, Message.MessageContext * continuation. */ trait Desugaring { self: PreTyper => + import Desugaring._ + /** * The entry point of the desugaring stage. * @@ -46,7 +49,7 @@ trait Desugaring { self: PreTyper => * `Cons(hd, tl)`, then the name of `hd` will be `x$Cons_0` and the name of * `tl` will be `x$Cons_1`. */ - private def freshScrutinee(parentScrutinee: Var, parentClassName: Str, index: Int): Var = + private def freshSubScrutinee(parentScrutinee: Var, parentClassName: Str, index: Int): Var = Var(s"${parentScrutinee}$$${parentClassName}_${index.toString}") /** @@ -60,56 +63,12 @@ trait Desugaring { self: PreTyper => private def makeUnappliedVar(scrutinee: Var, className: Var)(implicit context: Context): Var = Var(s"${context.unappliedPrefix}${scrutinee.name}$$${className.name}") - // I plan to integrate scrutinee symbols into a field of `ValueSymbol`. - // Because each `ValueSymbol` can be matched in multiple UCS expressions. - private implicit class VarOps(nme: Var) { - def withFreshSymbol: Var = nme.withSymbol(freshSymbol(nme)) - - def getScrutineeSymbol: ScrutineeSymbol = nme.symbolOption match { - case S(symbol: ScrutineeSymbol) => symbol - case S(otherSymbol) => throw new DesugaringException( - msg"Expected scrutinee symbol, found ${nme.symbol.name}" -> nme.toLoc :: Nil - ) - case N => throw new DesugaringException( - msg"Scrutinee symbol not found" -> nme.toLoc :: Nil - ) - } - - def withResolvedTypeSymbol(implicit scope: Scope): Var = { - nme.symbol = nme.resolveTypeSymbol - nme - } - - def resolveTypeSymbol(implicit scope: Scope): TypeSymbol = scope.getTypeSymbol(nme.name) match { - case S(symbol: TraitSymbol) => - println(s"resolveTypeSymbol ${nme} ==> trait") - nme.symbol = symbol - symbol - case S(symbol: ClassSymbol) => - println(s"resolveTypeSymbol ${nme} ==> class") - nme.symbol = symbol - symbol - case S(symbol: ModuleSymbol) => - println(s"resolveTypeSymbol ${nme} ==> module") - nme.symbol = symbol - symbol - case S(symbol: MixinSymbol) => - throw new DesugaringException(msg"Mixins are not allowed in pattern" -> nme.toLoc :: Nil) - case S(symbol: TypeAliasSymbol) => - throw new DesugaringException(msg"Type alias is not allowed in pattern" -> nme.toLoc :: Nil) - case N => - throw new DesugaringException(msg"Undefined symbol found in patterns." -> nme.toLoc :: Nil) - } - } - /** * A shorthand for making a true pattern, which is useful in desugaring * Boolean conditions. */ private def truePattern(implicit scope: Scope) = c.Pattern.Class(Var("true").withResolvedTypeSymbol) - private def freshSymbol(nme: Var): ValueSymbol = new ValueSymbol(nme, false) - private def desugarTermSplit(split: s.TermSplit)(implicit termPart: PartialTerm, scope: Scope, context: Context): c.Split = split match { case s.Split.Cons(head, tail) => desugarTermBranch(head) ++ desugarTermSplit(tail) @@ -164,34 +123,35 @@ trait Desugaring { self: PreTyper => } }() - /** Make a term like `ClassName.unapply(scrutinee)`. */ - private def makeUnapplyCall(scrutinee: Var, className: Var) = - App(Sel(className, Var("unapply")), Tup(N -> Fld(FldFlags.empty, scrutinee) :: Nil)) - private def makeLiteralTest(test: Var, scrutinee: Var, literal: Lit)(implicit scope: Scope): c.Split => c.Split = next => c.Split.Let( rec = false, - name = scrutinee, + name = test, term = mkBinOp(scrutinee, Var("=="), literal, true), - tail = c.Branch(scrutinee, truePattern, next) :: c.Split.Nil + tail = c.Branch(test, truePattern, next) :: c.Split.Nil ) - private def flattenClassParameters(parentScrutinee: Var, parentClassLikeSymbol: TypeSymbol, parameters: Ls[Opt[s.Pattern]]): Ls[Opt[Var -> Opt[s.Pattern]]] = - parameters.iterator.zipWithIndex.map { - case (N, _) => N - case (S(s.NamePattern(name)), index) => - val symbol = parentScrutinee.getScrutineeSymbol.getSubScrutineeSymbolOrElse( - parentClassLikeSymbol, index, name, new ValueSymbol(name, false) - ) - S(name.withSymbol(symbol) -> N) - case (S(parameterPattern @ (s.ClassPattern(_, _) | s.LiteralPattern(_) | s.TuplePattern(_))), index) => - val scrutinee = freshScrutinee(parentScrutinee, parentClassLikeSymbol.name, index) - val symbol = parentScrutinee.getScrutineeSymbol.getSubScrutineeSymbolOrElse( - parentClassLikeSymbol, index, scrutinee, new ValueSymbol(scrutinee, false) - ) - S(scrutinee.withSymbol(symbol) -> S(parameterPattern)) - case _ => ??? // Other patterns are not implemented yet. - }.toList + private def flattenClassParameters( + parentScrutineeVar: Var, + parentClassLikeSymbol: TypeSymbol, + parameters: Ls[Opt[s.Pattern]] + )(implicit context: Context): Ls[Opt[Var -> Opt[s.Pattern]]] = + trace(s"flattenClassParameters <== ${parentScrutineeVar.name} is ${parentClassLikeSymbol.name}") { + // Make it `lazy` so that it will not be created if all fields are wildcards. + lazy val classPattern = parentScrutineeVar.getOrCreateScrutinee.getOrCreateClassPattern(parentClassLikeSymbol) + parameters.iterator.zipWithIndex.map { + case (N, _) => N + case (S(s.NamePattern(name)), index) => + val subScrutinee = classPattern.getParameter(index).withAlias(name) + S(name.withFreshSymbol.withScrutinee(subScrutinee) -> N) + case (S(parameterPattern @ (s.ClassPattern(_, _) | s.LiteralPattern(_) | s.TuplePattern(_))), index) => + val subScrutineeVar = freshSubScrutinee(parentScrutineeVar, parentClassLikeSymbol.name, index) + val symbol = new ValueSymbol(subScrutineeVar, false) + symbol.addScrutinee(classPattern.getParameter(index).withAlias(subScrutineeVar)) + S(subScrutineeVar.withSymbol(symbol) -> S(parameterPattern)) + case _ => ??? // Other patterns are not implemented yet. + }.toList + }(r => s"flattenClassParameters ==> ${r.mkString(", ")}") /** * Recursively decompose and flatten a possibly nested class pattern. Any @@ -211,35 +171,40 @@ trait Desugaring { self: PreTyper => * @param initialScope the scope before flattening the class pattern * @return a tuple of the augmented scope and a function that wrap a split */ - private def desugarClassPattern(pattern: s.ClassPattern, scrutinee: Var, initialScope: Scope)(implicit context: Context): (Scope, c.Split => c.Branch) = { - val scrutineeSymbol = scrutinee.getScrutineeSymbol + private def desugarClassPattern(pattern: s.ClassPattern, scrutineeVar: Var, initialScope: Scope)(implicit context: Context): (Scope, c.Split => c.Branch) = { + val scrutinee = scrutineeVar.getOrCreateScrutinee.withAlias(scrutineeVar) val patternClassSymbol = pattern.nme.resolveTypeSymbol(initialScope) - // Most importantly, we need to add the class to the list of matched classes. - scrutineeSymbol.addMatchedClass(patternClassSymbol, pattern.nme.toLoc) + val classPattern = scrutinee.getOrCreateClassPattern(patternClassSymbol) + println(s"desugarClassPattern: ${scrutineeVar.name} is ${pattern.nme.name}") + classPattern.addLocation(pattern.nme) val (scopeWithAll, bindAll) = pattern.parameters match { case S(parameters) => // Before processing sub-patterns, we need to generate a variable that // holds the result of `unapply` method. Such variable might have been // generated by a previous branches. We MUST reuse so that we can merge // duplicated bindings during normalization. - val unapp = scrutineeSymbol.getUnappliedVarOrElse(patternClassSymbol, { - val vari = makeUnappliedVar(scrutinee, pattern.nme) + lazy val unapp = classPattern.getUnappliedVar { + val vari = makeUnappliedVar(scrutineeVar, pattern.nme) vari.withSymbol(new ValueSymbol(vari, false)) - }) - val nestedPatterns = flattenClassParameters(scrutinee, patternClassSymbol, parameters) + } + val nestedPatterns = flattenClassParameters(scrutineeVar, patternClassSymbol, parameters) // First, handle bindings of parameters of the current class pattern. - val bindClassParameters = nestedPatterns.iterator.zipWithIndex.foldRight[c.Split => c.Split](identity) { + val bindParameters = nestedPatterns.iterator.zipWithIndex.foldRight[c.Split => c.Split](identity) { case ((N, _), bindNextParameter) => bindNextParameter case ((S(parameter -> _), index), bindNextParameter) => bindNextParameter.andThen { c.Split.Let(false, parameter, Sel(unapp, Var(index.toString)), _) } - }.andThen { c.Split.Let(false, unapp, makeUnapplyCall(scrutinee, pattern.nme), _): c.Split } + } + val bindAll = if (bindParameters === identity) bindParameters else bindParameters.andThen { + c.Split.Let(false, unapp, makeUnapplyCall(scrutineeVar, pattern.nme), _): c.Split + } val scopeWithClassParameters = initialScope ++ (unapp.symbol :: nestedPatterns.flatMap(_.map(_._1.symbol))) - desugarNestedPatterns(nestedPatterns, scopeWithClassParameters, bindClassParameters) + desugarNestedPatterns(nestedPatterns, scopeWithClassParameters, bindAll) // If there is no parameter, then we are done. case N => (initialScope, identity(_: c.Split)) } + println(s"${scrutineeVar.name}: ${scrutinee.patterns.mkString(", ")}") // Last, return the scope with all bindings and a function that adds all matches and bindings to a split. - (scopeWithAll, split => c.Branch(scrutinee, c.Pattern.Class(pattern.nme), bindAll(split))) + (scopeWithAll, split => c.Branch(scrutineeVar, c.Pattern.Class(pattern.nme), bindAll(split))) } /** @@ -257,65 +222,68 @@ trait Desugaring { self: PreTyper => nestedPatterns: Ls[Opt[Var -> Opt[s.Pattern]]], scopeWithScrutinees: Scope, bindScrutinees: c.Split => c.Split - )(implicit context: Context): (Scope, c.Split => c.Split) = { - nestedPatterns.foldLeft((scopeWithScrutinees, bindScrutinees)) { - // If this parameter is not matched with a sub-pattern, then we do - // nothing and pass on scope and binder. - case (acc, S(_ -> N)) => acc - // If this sub-pattern is a class pattern, we need to recursively flatten - // the class pattern. We will get a scope with all bindings and a function - // that adds all bindings to a split. The scope can be passed on to the - // next sub-pattern. The binder needs to be composed with the previous - // binder. - case ((scope, bindPrevious), S(nme -> S(pattern: s.ClassPattern))) => - val (scopeWithNestedAll, bindNestedAll) = desugarClassPattern(pattern, nme, scope) - (scopeWithNestedAll, split => bindPrevious(bindNestedAll(split) :: c.Split.Nil)) - case ((scope, bindPrevious), S(nme -> S(pattern: s.LiteralPattern))) => - val test = context.freshTest().withFreshSymbol - (scope + test.symbol, makeLiteralTest(test, nme, pattern.literal)(scope).andThen(bindPrevious)) - case ((scope, bindPrevious), S(nme -> S(s.TuplePattern(fields)))) => - val (scopeWithNestedAll, bindNestedAll) = desugarTuplePattern(fields, nme, scope) - (scopeWithNestedAll, bindNestedAll.andThen(bindPrevious)) - // Well, other patterns are not supported yet. - case (acc, S((nme, pattern))) => ??? - // If this parameter is empty (e.g. produced by wildcard), then we do - // nothing and pass on scope and binder. - case (acc, N) => acc - } - } + )(implicit context: Context): (Scope, c.Split => c.Split) = + trace("desugarNestedPatterns") { + nestedPatterns.foldLeft((scopeWithScrutinees, bindScrutinees)) { + // If this parameter is not matched with a sub-pattern, then we do + // nothing and pass on scope and binder. + case (acc, S(_ -> N)) => acc + // If this sub-pattern is a class pattern, we need to recursively flatten + // the class pattern. We will get a scope with all bindings and a function + // that adds all bindings to a split. The scope can be passed on to the + // next sub-pattern. The binder needs to be composed with the previous + // binder. + case ((scope, bindPrevious), S(nme -> S(pattern: s.ClassPattern))) => + println(s"${nme.name} is ${pattern.nme.name}") + val (scopeWithNestedAll, bindNestedAll) = desugarClassPattern(pattern, nme, scope) + (scopeWithNestedAll, split => bindPrevious(bindNestedAll(split) :: c.Split.Nil)) + case ((scope, bindPrevious), S(nme -> S(pattern: s.LiteralPattern))) => + val test = context.freshTest().withFreshSymbol + println(s"fresh test var: ${test.name}") + (scope + test.symbol, makeLiteralTest(test, nme, pattern.literal)(scope).andThen(bindPrevious)) + case ((scope, bindPrevious), S(nme -> S(s.TuplePattern(fields)))) => + val (scopeWithNestedAll, bindNestedAll) = desugarTuplePattern(fields, nme, scope) + (scopeWithNestedAll, bindNestedAll.andThen(bindPrevious)) + // Well, other patterns are not supported yet. + case (acc, S((nme, pattern))) => ??? + // If this parameter is empty (e.g. produced by wildcard), then we do + // nothing and pass on scope and binder. + case (acc, N) => acc + } + }() - private def flattenTupleFields(parentScrutinee: Var, fields: Ls[Opt[s.Pattern]]): Ls[Opt[Var -> Opt[s.Pattern]]] = + private def flattenTupleFields(parentScrutineeVar: Var, fields: Ls[Opt[s.Pattern]])(implicit context: Context): Ls[Opt[Var -> Opt[s.Pattern]]] = { + // Make it `lazy` so that it will not be created if all fields are wildcards. + lazy val tuplePattern = parentScrutineeVar.getOrCreateScrutinee.getOrCreateTuplePattern fields.iterator.zipWithIndex.map { case (N, _) => N case (S(s.NamePattern(name)), index) => - val symbol = parentScrutinee.getScrutineeSymbol.getTupleSubScrutineeSymbolOrElse( - index, name, new ValueSymbol(name, false) - ) - S(name.withSymbol(symbol) -> N) + S(name.withFreshSymbol.withScrutinee(tuplePattern.getField(index)) -> N) case (S(parameterPattern @ (s.ClassPattern(_, _) | s.LiteralPattern(_) | s.TuplePattern(_))), index) => - val scrutinee = freshScrutinee(parentScrutinee, "Tuple$2", index) - val symbol = parentScrutinee.getScrutineeSymbol.getTupleSubScrutineeSymbolOrElse( - index, scrutinee, new ValueSymbol(scrutinee, false) - ) - S(scrutinee.withSymbol(symbol) -> S(parameterPattern)) + val arity = fields.length + val subScrutineeVar = freshSubScrutinee(parentScrutineeVar, s"Tuple$$$arity", index) + val symbol = new ValueSymbol(subScrutineeVar, false) + symbol.addScrutinee(tuplePattern.getField(index).withAlias(subScrutineeVar)) + S(subScrutineeVar.withSymbol(symbol) -> S(parameterPattern)) case _ => ??? }.toList + } - private def desugarTuplePattern(fields: Ls[Opt[s.Pattern]], scrutinee: Var, initialScope: Scope)(implicit context: Context): (Scope, c.Split => c.Split) = { - val scrutineeSymbol = scrutinee.getScrutineeSymbol - val nestedPatterns = flattenTupleFields(scrutinee, fields) - val bindTupleFields = nestedPatterns.iterator.zipWithIndex.foldRight[c.Split => c.Split](identity) { + private def desugarTuplePattern(fields: Ls[Opt[s.Pattern]], scrutineeVar: Var, initialScope: Scope)(implicit context: Context): (Scope, c.Split => c.Split) = { + val scrutinee = scrutineeVar.getOrCreateScrutinee.withAlias(scrutineeVar) + val nestedPatterns = flattenTupleFields(scrutineeVar, fields) + val bindFields = nestedPatterns.iterator.zipWithIndex.foldRight[c.Split => c.Split](identity) { case ((N, _), bindNextField) => bindNextField case ((S(parameter -> _), index), bindNextField) => val indexVar = Var(index.toString).withLoc(parameter.toLoc) - bindNextField.andThen { c.Split.Let(false, parameter, Sel(scrutinee, indexVar), _) } + bindNextField.andThen { c.Split.Let(false, parameter, Sel(scrutineeVar, indexVar), _) } } - val scopeWithTupleFields = initialScope ++ nestedPatterns.flatMap(_.map(_._1.symbol)) - desugarNestedPatterns(nestedPatterns, scopeWithTupleFields, bindTupleFields) + val scopeWithFields = initialScope ++ nestedPatterns.flatMap(_.map(_._1.symbol)) + desugarNestedPatterns(nestedPatterns, scopeWithFields, bindFields) } - private def desugarPatternSplit(scrutinee: Term, split: s.PatternSplit)(implicit scope: Scope, context: Context): c.Split = { - def rec(scrutinee: Var, split: s.PatternSplit)(implicit scope: Scope): c.Split = split match { + private def desugarPatternSplit(scrutineeTerm: Term, split: s.PatternSplit)(implicit scope: Scope, context: Context): c.Split = { + def rec(scrutineeVar: Var, split: s.PatternSplit)(implicit scope: Scope): c.Split = split match { case s.Split.Cons(head, tail) => head.pattern match { case s.AliasPattern(nme, pattern) => ??? @@ -325,49 +293,59 @@ trait Desugaring { self: PreTyper => c.Split.Let( rec = false, name = test, - term = mkBinOp(scrutinee, Var("==="), nme, true), + term = mkBinOp(scrutineeVar, Var("==="), nme, true), tail = c.Branch( scrutinee = test, pattern = truePattern, continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope + test.symbol, context) - ) :: rec(scrutinee, tail) + ) :: rec(scrutineeVar, tail) ) case s.NamePattern(Var("_")) => - desugarTermSplit(head.continuation)(PartialTerm.Empty, scope, context) ++ rec(scrutinee, tail) + desugarTermSplit(head.continuation)(PartialTerm.Empty, scope, context) ++ rec(scrutineeVar, tail) case s.NamePattern(nme) => - // Share the scrutinee's symbol with its aliases. - // nme.symbol = scrutinee.symbol // <-- This currently causes a bug, reuse this line after we remove `ScrutineeSymbol`. - nme.symbol = new ValueSymbol(nme, false) + // Create a symbol for the binding. + val symbol = new ValueSymbol(nme, false) + // Share the scrutineeVar's symbol with its aliases. + symbol.addScrutinee(scrutineeVar.getOrCreateScrutinee.withAlias(nme)) + // Associate the symbol with the binding. + nme.symbol = symbol + // Whoosh! Done. val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope + nme.symbol, context) - c.Branch(scrutinee, c.Pattern.Name(nme), continuation) :: rec(scrutinee, tail)(scope + nme.symbol) + c.Branch(scrutineeVar, c.Pattern.Name(nme), continuation) :: rec(scrutineeVar, tail)(scope + nme.symbol) case pattern @ s.ClassPattern(nme, fields) => - println(s"find term symbol of $scrutinee in ${scope.showLocalSymbols}") - scrutinee.symbol = scope.getTermSymbol(scrutinee.name).getOrElse(???) - val (scopeWithAll, bindAll) = desugarClassPattern(pattern, scrutinee, scope) + println(s"find term symbol of $scrutineeVar in ${scope.showLocalSymbols}") + scrutineeVar.symbol = scope.getTermSymbol(scrutineeVar.name).getOrElse(???) + val (scopeWithAll, bindAll) = desugarClassPattern(pattern, scrutineeVar, scope) val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scopeWithAll, context) - bindAll(continuation) :: rec(scrutinee, tail) + bindAll(continuation) :: rec(scrutineeVar, tail) case s.TuplePattern(fields) => - scrutinee.symbol = scope.getTermSymbol(scrutinee.name).getOrElse(???) - val (scopeWithAll, bindAll) = desugarTuplePattern(fields, scrutinee, scope) + scrutineeVar.symbol = scope.getTermSymbol(scrutineeVar.name).getOrElse(???) + val (scopeWithAll, bindAll) = desugarTuplePattern(fields, scrutineeVar, scope) val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scopeWithAll, context) val withBindings = bindAll(continuation) if (withBindings.hasElse) { withBindings } else { - withBindings ++ rec(scrutinee, tail) + withBindings ++ rec(scrutineeVar, tail) } case s.RecordPattern(entries) => ??? } case s.Split.Let(isRec, nme, rhs, tail) => - c.Split.Let(isRec, nme, rhs, rec(scrutinee, tail)(scope + nme.withFreshSymbol.symbol)) // <-- Weird use. + c.Split.Let(isRec, nme, rhs, rec(scrutineeVar, tail)(scope + nme.withFreshSymbol.symbol)) // <-- Weird use. case s.Split.Else(default) => c.Split.Else(default) case s.Split.Nil => c.Split.Nil } - scrutinee match { + scrutineeTerm match { case nme: Var => rec(nme, split) case other => - val alias = context.freshScrutinee().withFreshSymbol - c.Split.Let(false, alias, scrutinee, rec(alias, split)(scope + alias.symbol)) + val alias = context.freshScrutineeVar().withFreshSymbol + c.Split.Let(false, alias, other, rec(alias, split)(scope + alias.symbol)) } } } + +object Desugaring { + /** Make a term like `ClassName.unapply(scrutinee)`. */ + private def makeUnapplyCall(scrutinee: Var, className: Var) = + App(Sel(className, Var("unapply").withLocOf(className)), Tup(N -> Fld(FldFlags.empty, scrutinee) :: Nil)) +} diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 34e06d51..0fb20118 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -1,15 +1,17 @@ package mlscript.ucs.stages -import mlscript.ucs.{showVar, Context, Lines, LinesOps, VariableGenerator} +import mlscript.ucs.{Context, Lines, LinesOps, ScrutineeData, VariableGenerator} import mlscript.ucs.core._ import mlscript.ucs.helpers._ +import mlscript.ucs.display.showNormalizedTerm import mlscript.pretyper.Scope import mlscript.pretyper.symbol._ import mlscript.{App, CaseOf, Fld, FldFlags, Let, Loc, Sel, Term, Tup, Var, StrLit} import mlscript.{CaseBranches, Case, Wildcard, NoCases} import mlscript.Message, Message.MessageContext import mlscript.utils._, shorthands._ +import mlscript.ucs.display.showSplit trait Normalization { self: mlscript.pretyper.Traceable => import Normalization._ @@ -32,10 +34,10 @@ trait Normalization { self: mlscript.pretyper.Traceable => private def concat(these: Split, those: Split)(implicit context: Context, generatedVars: Set[Var]): Split = trace(s"concat <== ${generatedVars.mkString("{", ", ", "}")}") { - println(s"these: ${printSplit(these)}") - println(s"those: ${printSplit(those)}") + println(s"these: ${showSplit(these)}") + println(s"those: ${showSplit(those)}") concatImpl(these, those) - }(sp => s"concat => ${printSplit(sp)}") + }(sp => s"concat => ${showSplit(sp)}") /** * Normalize core abstract syntax to MLscript syntax. @@ -55,16 +57,16 @@ trait Normalization { self: mlscript.pretyper.Traceable => val trueBranch = normalizeToTerm(concat(continuation, tail)) val falseBranch = normalizeToCaseBranches(tail) CaseOf(test, Case(nme, trueBranch, falseBranch)) - case Split.Cons(Branch(scrutinee, pattern @ Pattern.Literal(literal), continuation), tail) => - val trueBranch = normalizeToTerm(specialize(concat(continuation, tail), true)(scrutinee.symbol, pattern)) - val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutinee.symbol, pattern)) - CaseOf(scrutinee, Case(literal, trueBranch, falseBranch)) + case Split.Cons(Branch(ScrutineeData.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Literal(literal), continuation), tail) => + val trueBranch = normalizeToTerm(specialize(concat(continuation, tail), true)(scrutineeVar, scrutinee, pattern, context)) + val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context)) + CaseOf(scrutineeVar, Case(literal, trueBranch, falseBranch)) // false class parameters. Easy - case Split.Cons(Branch(scrutinee, pattern @ Pattern.Class(nme), continuation), tail) => - println(s"match $scrutinee with $nme (has location: ${nme.toLoc.isDefined})") - val trueBranch = normalizeToTerm(specialize(concat(continuation, tail), true)(scrutinee.symbol, pattern)) - val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutinee.symbol, pattern)) - CaseOf(scrutinee, Case(nme, trueBranch, falseBranch)) + case Split.Cons(Branch(ScrutineeData.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Class(nme), continuation), tail) => + println(s"match ${scrutineeVar.name} with $nme (has location: ${nme.toLoc.isDefined})") + val trueBranch = normalizeToTerm(specialize(concat(continuation, tail), true)(scrutineeVar, scrutinee, pattern, context)) + val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context)) + CaseOf(scrutineeVar, Case(nme, trueBranch, falseBranch)) case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => throw new NormalizationException((msg"Unsupported pattern: ${pattern.toString}" -> pattern.toLoc) :: Nil) case Split.Let(rec, Var("_"), rhs, tail) => normalizeToTerm(tail) @@ -101,21 +103,26 @@ trait Normalization { self: mlscript.pretyper.Traceable => // Specialize `split` with the assumption that `scrutinee` matches `pattern`. private def specialize (split: Split, matchOrNot: Bool) - (implicit scrutinee: Symbol, pattern: Pattern): Split = - trace[Split](s"S${if (matchOrNot) "+" else "-"} <== ${scrutinee.name} is ${pattern}") { + (implicit scrutineeVar: Var, scrutinee: ScrutineeData, pattern: Pattern, context: Context): Split = + trace[Split](s"S${if (matchOrNot) "+" else "-"} <== ${scrutineeVar.name} is ${pattern}") { (matchOrNot, split) match { // Name patterns are translated to let bindings. - case (true | false, Split.Cons(Branch(otherScrutineeVar, Pattern.Name(alias), continuation), tail)) => + case (_, Split.Cons(Branch(otherScrutineeVar, Pattern.Name(alias), continuation), tail)) => Split.Let(false, alias, otherScrutineeVar, specialize(continuation, matchOrNot)) + case (_, split @ Split.Cons(head @ Branch(test, Pattern.Class(Var("true")), continuation), tail)) if context.isTestVar(test) => + println(s"found a Boolean test: $test is true") + val trueBranch = specialize(continuation, matchOrNot) + val falseBranch = specialize(tail, matchOrNot) + split.copy(head = head.copy(continuation = trueBranch), tail = falseBranch) // Class pattern. Positive. - case (true, split @ Split.Cons(head @ Branch(ScrutineeOnly(otherScrutinee), Pattern.Class(otherClassName), continuation), tail)) => + case (true, split @ Split.Cons(head @ Branch(ScrutineeData.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Class(otherClassName), continuation), tail)) => val otherClassSymbol = getClassLikeSymbol(otherClassName) lazy val specializedTail = { println(s"specialized next") specialize(tail, true) } if (scrutinee === otherScrutinee) { - println(s"scrutinee: ${scrutinee.name} === ${otherScrutinee.name}") + println(s"scrutinee: ${scrutineeVar.name} === ${otherScrutineeVar.name}") pattern match { case Pattern.Class(className) => val classSymbol = getClassLikeSymbol(className) @@ -140,23 +147,23 @@ trait Normalization { self: mlscript.pretyper.Traceable => case _ => throw new NormalizationException((msg"Incompatible: ${pattern.toString}" -> pattern.toLoc) :: Nil) } } else { - println(s"scrutinee: ${scrutinee.name} =/= ${otherScrutinee.name}") + println(s"scrutinee: ${scrutineeVar.name} =/= ${otherScrutineeVar.name}") split.copy( head = head.copy(continuation = specialize(continuation, true)), tail = specializedTail ) } // Class pattern. Negative - case (false, split @ Split.Cons(head @ Branch(ScrutineeOnly(otherScrutinee), Pattern.Class(otherClassName), continuation), tail)) => + case (false, split @ Split.Cons(head @ Branch(ScrutineeData.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Class(otherClassName), continuation), tail)) => val otherClassSymbol = getClassLikeSymbol(otherClassName) if (scrutinee === otherScrutinee) { - println(s"scrutinee: ${scrutinee.name} === ${otherScrutinee.name}") + println(s"scrutinee: ${scrutineeVar.name} === ${otherScrutineeVar.name}") pattern match { case Pattern.Class(className) => val classSymbol = getClassLikeSymbol(className) if (className === otherClassName) { println(s"Case 1: class name: $otherClassName === $className") - specialize(tail, false) // TODO: Subsitute parameters to otherParameters + specialize(tail, false) } else if (otherClassSymbol.baseTypes contains classSymbol) { println(s"Case 2: class name: $otherClassName <: $className") Split.Nil @@ -167,7 +174,7 @@ trait Normalization { self: mlscript.pretyper.Traceable => case _ => throw new NormalizationException((msg"Incompatible: ${pattern.toString}" -> pattern.toLoc) :: Nil) } } else { - println(s"scrutinee: ${scrutinee.name} =/= ${otherScrutinee.name}") + println(s"scrutinee: ${scrutineeVar.name} =/= ${otherScrutineeVar.name}") split.copy( head = head.copy(continuation = specialize(continuation, matchOrNot)), tail = specialize(tail, matchOrNot) @@ -175,6 +182,7 @@ trait Normalization { self: mlscript.pretyper.Traceable => } // Other patterns. Not implemented. case (_, Split.Cons(Branch(otherScrutineeVar, pattern, continuation), tail)) => + println(s"unsupported pattern: $pattern") throw new NormalizationException((msg"Unsupported pattern: ${pattern.toString}" -> pattern.toLoc) :: Nil) case (_, let @ Split.Let(_, nme, _, tail)) => println(s"let binding $nme, go next") @@ -183,12 +191,6 @@ trait Normalization { self: mlscript.pretyper.Traceable => case (_, end @ (Split.Else(_) | Split.Nil)) => println("the end"); end } }(showSplit(s"S${if (matchOrNot) "+" else "-"} ==>", _)) - - /** - * Print a normalized term with indentation. - */ - @inline protected def printNormalizedTerm(term: Term): Unit = - println(showNormalizedTerm(term)) } object Normalization { @@ -201,38 +203,4 @@ object Normalization { class NormalizationException(val messages: Ls[Message -> Opt[Loc]]) extends Throwable { def this(message: Message, location: Opt[Loc]) = this(message -> location :: Nil) } - - /** - * Convert a normalized term to a string with indentation. It makes use of - * some special markers. - * - * - `*` if the variable is associated with a symbol, - * - `†` if the variable has a location. - */ - def showNormalizedTerm(term: Term): String = { - def showTerm(term: Term): Lines = term match { - case let: Let => showLet(let) - case caseOf: CaseOf => showCaseOf(caseOf) - case other => (0, other.toString) :: Nil - } - def showCaseOf(caseOf: CaseOf): Lines = { - val CaseOf(trm, cases) = caseOf - s"$trm match" ##: showCaseBranches(cases) - } - def showCaseBranches(caseBranches: CaseBranches): Lines = - caseBranches match { - case Case(pat, rhs, tail) => - // If the class name has a location, mark it with a dagger †. - // This is to track the location information. - val marker = if (pat.toLoc.isDefined) "†" else "" - (s"case $pat$marker =>" @: showTerm(rhs)) ++ showCaseBranches(tail) - case Wildcard(term) => s"case _ =>" @: showTerm(term) - case NoCases => Nil - } - def showLet(let: Let): Lines = { - val Let(rec, nme, rhs, body) = let - (0, s"let ${showVar(nme)} = $rhs") :: showTerm(body) - } - showTerm(term).map { case (n, line) => " " * n + line }.mkString("\n") - } } \ No newline at end of file diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index 60028ef7..f0db405c 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -1,38 +1,20 @@ package mlscript.ucs.stages import mlscript.{Case, CaseBranches, CaseOf, Let, Loc, NoCases, Term, Var, Wildcard} -import mlscript.ucs.Context +import mlscript.ucs.{Context, DesugarUCS, ScrutineeData} import mlscript.pretyper.symbol._ import mlscript.utils._, shorthands._ import mlscript.Message, Message.MessageContext import scala.annotation.tailrec -trait PostProcessing { self: mlscript.pretyper.Traceable => +trait PostProcessing { self: DesugarUCS with mlscript.pretyper.Traceable => import PostProcessing._ - /** - * If the given `Var` represents a class name, get its `ClassSymbol`. - * - * @param className the class name variable - */ - def getClassSymbolFromVar(className: Var): TypeSymbol = - trace(s"getClassSymbolFromVar <== ${inspect.shallow(className)}") { - className.symbolOption match { - case S(symbol: ClassSymbol) => symbol - case S(symbol: TraitSymbol) => symbol - case S(symbol: ModuleSymbol) => symbol - case S(symbol: Symbol) => throw new PostProcessingException( - msg"variable ${className.name} is not associated with a class symbol" -> N :: Nil) - case N => throw new PostProcessingException( - msg"variable ${className.name} is not associated with any symbols" -> N :: Nil) - } - }(symbol => s"getClassSymbolFromVar ==> ${symbol.name}") - def postProcess(term: Term)(implicit context: Context): Term = trace(s"postProcess <== ${inspect.shallow(term)}") { // Normalized terms are constructed using `Let` and `CaseOf`. term match { - case top @ CaseOf(scrutinee: Var, fst @ Case(className: Var, body, NoCases)) => - println(s"found a UNARY case: $scrutinee is $className") + case top @ CaseOf(scrutineeVar: Var, fst @ Case(className: Var, body, NoCases)) => + println(s"found a UNARY case: $scrutineeVar is $className") println("post-processing the body") top.copy(cases = fst.copy(body = postProcess(body))) case top @ CaseOf(test: Var, fst @ Case(Var("true"), trueBranch, Wildcard(falseBranch))) if context.isTestVar(test) => @@ -40,35 +22,34 @@ trait PostProcessing { self: mlscript.pretyper.Traceable => val processedTrueBranch = postProcess(trueBranch) val processedFalseBranch = postProcess(falseBranch) top.copy(cases = fst.copy(body = processedTrueBranch, rest = Wildcard(processedFalseBranch))) - case top @ CaseOf(scrutinee: Var, fst @ Case(className: Var, trueBranch, Wildcard(falseBranch))) => - println(s"found a BINARY case: $scrutinee is $className") - val scrutineeSymbol = scrutinee.symbol match { - case symbol: ScrutineeSymbol => symbol - case _ => throw new PostProcessingException( - msg"variable ${scrutinee.name} is not a scrutinee" -> N :: Nil + case top @ CaseOf(ScrutineeData.WithVar(scrutinee, scrutineeVar), fst @ Case(className: Var, trueBranch, Wildcard(falseBranch))) => + println(s"found a BINARY case: $scrutineeVar is $className") + val classSymbol = className.getClassLikeSymbol + println(s"matched classes: ${scrutinee.patterns.mkString(", ")}") + val classPattern = scrutinee.getClassPattern(classSymbol).getOrElse { + throw new PostProcessingException( + msg"cannot find class pattern for ${className.name}" -> className.toLoc :: Nil ) } - val classSymbol = getClassSymbolFromVar(className) - println(s"`${scrutinee}`'s matched classes: ${scrutineeSymbol.matchedClasses.keysIterator.map(_.name).mkString("[", ", ", "]")}") + println(s"patterns of `${scrutineeVar}`: ${scrutinee.patterns.mkString("{", ", ", "}")}") // Post-process the true branch. println("post-processing the first branch") val processedTrueBranch = postProcess(trueBranch) // Post-process the false branch. println("post-processing the false branch") - println(s"searching for cases: " + scrutineeSymbol.matchedClasses.keysIterator.filter(_ =/= classSymbol).map(_.name).mkString("[", ", ", "]")) - val (default, cases) = scrutineeSymbol.matchedClasses.iterator.filter(_._1 =/= classSymbol) + val (default, cases) = scrutinee.classLikePatternsIterator.filter(_._1 =/= classSymbol) // For each case class name, distangle case branch body terms from the false branch. .foldLeft[Opt[Term] -> Ls[(TypeSymbol, Opt[Loc], Term)]](S(falseBranch) -> Nil) { - case ((S(remainingTerm), cases), (classSymbol -> locations)) => + case ((S(remainingTerm), cases), (classSymbol -> classPattern)) => println(s"searching for case: ${classSymbol.name}") - val (leftoverTerm, extracted) = disentangle(remainingTerm, scrutineeSymbol, classSymbol) + val (leftoverTerm, extracted) = disentangle(remainingTerm, scrutineeVar, scrutinee, classSymbol) trimEmptyTerm(leftoverTerm) -> (extracted match { case N => println(s"no extracted term about ${classSymbol.name}") cases case terms @ S(extractedTerm) => println(s"extracted a term about ${classSymbol.name}") - (classSymbol, locations.headOption, postProcess(extractedTerm)) :: cases + (classSymbol, classPattern.firstOccurrence, postProcess(extractedTerm)) :: cases }) case ((N, cases), _) => (N, cases) } @@ -148,29 +129,27 @@ trait PostProcessing { self: mlscript.pretyper.Traceable => * @param className the class name * @return the remaining term and the disentangled term */ - def disentangle(term: Term, scrutinee: ScrutineeSymbol, classSymbol: TypeSymbol): (Term, Opt[Term]) = - trace[(Term, Opt[Term])](s"disentangle <== ${scrutinee.name}: ${classSymbol.name}") { + def disentangle(term: Term, scrutineeVar: Var, scrutinee: ScrutineeData, classSymbol: TypeSymbol)(implicit context: Context): (Term, Opt[Term]) = + trace[(Term, Opt[Term])](s"disentangle <== ${scrutineeVar.name}: ${classSymbol.name}") { term match { - case top @ CaseOf(scrutineeVar: Var, cases) => - if (scrutineeVar.symbol match { - case s: ScrutineeSymbol => s === scrutinee; case _ => false - }) { - println(s"found a `CaseOf` that matches on `${scrutinee.name}`") + case top @ CaseOf(ScrutineeData.WithVar(otherScrutinee, otherScrutineeVar), cases) => + if (scrutinee === otherScrutinee) { + println(s"found a `CaseOf` that matches on `${scrutineeVar.name}`") def rec(cases: CaseBranches): (CaseBranches, Opt[Term]) = cases match { case NoCases => println("no cases, STOP") NoCases -> N case wildcard @ Wildcard(body) => println("found a wildcard, go deeper") - val (n, y) = disentangle(body, scrutinee, classSymbol) + val (n, y) = disentangle(body, scrutineeVar, scrutinee, classSymbol) wildcard.copy(body = n) -> y case kase @ Case(className: Var, body, rest) => println(s"found a case branch matching against $className") - val otherClassSymbol = getClassSymbolFromVar(className) + val otherClassSymbol = className.getClassLikeSymbol if (otherClassSymbol === classSymbol) { rest -> S(body) } else { - val (n1, y1) = disentangle(body, scrutinee, classSymbol) + val (n1, y1) = disentangle(body, scrutineeVar, scrutinee, classSymbol) val (n2, y2) = rec(rest) (kase.copy(body = n1, rest = n2), mergeTerms(y1, y2)) } @@ -182,18 +161,18 @@ trait PostProcessing { self: mlscript.pretyper.Traceable => val (n, y) = rec(cases) (top.copy(cases = n), y) } else { - println(s"found a `CaseOf` that does NOT match on ${scrutinee.name}") + println(s"found a `CaseOf` that does NOT match on ${scrutineeVar.name}") def rec(cases: CaseBranches): (CaseBranches, CaseBranches) = cases match { case NoCases => println("no cases, STOP") NoCases -> NoCases case wildcard @ Wildcard(body) => println("found a wildcard, go deeper") - val (n, y) = disentangle(body, scrutinee, classSymbol) + val (n, y) = disentangle(body, scrutineeVar, scrutinee, classSymbol) (wildcard.copy(body = n), y.fold(NoCases: CaseBranches)(Wildcard(_))) case kase @ Case(_, body, rest) => println(s"found a case branch") - val (n1, y1) = disentangle(body, scrutinee, classSymbol) + val (n1, y1) = disentangle(body, scrutineeVar, scrutinee, classSymbol) val (n2, y2) = rec(rest) (kase.copy(body = n1, rest = n2), (y1 match { case S(term) => kase.copy(body = term, rest = y2) @@ -204,7 +183,7 @@ trait PostProcessing { self: mlscript.pretyper.Traceable => (top.copy(cases = n), (if (y === NoCases) N else S(top.copy(cases = y)))) } case let @ Let(_, _, _, body) => - val (n, y) = disentangle(body, scrutinee, classSymbol) + val (n, y) = disentangle(body, scrutineeVar, scrutinee, classSymbol) (let.copy(body = n), y.map(t => let.copy(body = t))) case other => println(s"cannot disentangle ${inspect.shallow(other)}. STOP") @@ -212,18 +191,19 @@ trait PostProcessing { self: mlscript.pretyper.Traceable => } }({ case (n, y) => s"disentangle ==> `${inspect.deep(n)}` and `${y.fold("")(inspect.deep(_))}`" }) - def cascadeConsecutiveCaseOf(term: Term): Term = trace(s"cascade consecutive CaseOf <== ${term.describe}") { + // FIXME: What? Is this seemingly useful function useless? + def cascadeCaseTerm(term: Term): Term = trace(s"cascadeCaseTerm <== ${term.describe}") { // Normalized terms are constructed using `Let` and `CaseOf`. term match { case top @ CaseOf(scrutinee: Var, fst @ Case(pattern, body, NoCases)) => println(s"found a UNARY case: $scrutinee is $pattern") - top.copy(cases = fst.copy(body = cascadeConsecutiveCaseOf(body))) + top.copy(cases = fst.copy(body = cascadeCaseTerm(body))) case top @ CaseOf(scrutinee: Var, fst @ Case(pattern, trueBranch, snd @ Wildcard(falseBranch))) => println(s"found a BINARY case: $scrutinee is $pattern") println("cascading the true branch") - val processedTrueBranch = cascadeConsecutiveCaseOf(trueBranch) + val processedTrueBranch = cascadeCaseTerm(trueBranch) println("cascading the false branch") - val processedFalseBranch = cascadeConsecutiveCaseOf(falseBranch) + val processedFalseBranch = cascadeCaseTerm(falseBranch) // Check if the false branch is another `CaseOf` with the same scrutinee. processedFalseBranch match { case CaseOf(otherScrutinee: Var, actualFalseBranch) => @@ -242,11 +222,11 @@ trait PostProcessing { self: mlscript.pretyper.Traceable => case other => top } // We recursively process the body of `Let` bindings. - case let @ Let(_, _, _, body) => let.copy(body = cascadeConsecutiveCaseOf(body)) + case let @ Let(_, _, _, body) => let.copy(body = cascadeCaseTerm(body)) // Otherwise, this is not a part of a normalized term. case other => println(s"CANNOT cascade"); other } - }() + }(_ => "cascadeCaseTerm ==> ") } object PostProcessing { diff --git a/shared/src/main/scala/mlscript/ucs/stages/package.scala b/shared/src/main/scala/mlscript/ucs/stages/package.scala index 825b3cbb..0fa9fde4 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/package.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/package.scala @@ -5,26 +5,6 @@ import mlscript.pretyper.symbol._ import mlscript.utils._, shorthands._ package object stages { - object Scrutinee { - def unapply(term: Term): Opt[(Var, ScrutineeSymbol)] = term match { - case v @ Var(_) => v.symbol match { - case symbol: ScrutineeSymbol => S(v, symbol) - case _ => N - } - case _ => N - } - } - - object ScrutineeOnly { - def unapply(term: Term): Opt[ScrutineeSymbol] = term match { - case v: Var => v.symbol match { - case symbol: ScrutineeSymbol => S(symbol) - case _ => N - } - case _ => N - } - } - sealed abstract class CasePattern { override def toString(): String = this match { case CasePattern.Class(symbol) => symbol.name diff --git a/shared/src/main/scala/mlscript/ucs/syntax.scala b/shared/src/main/scala/mlscript/ucs/syntax.scala index 85b47765..433cd835 100644 --- a/shared/src/main/scala/mlscript/ucs/syntax.scala +++ b/shared/src/main/scala/mlscript/ucs/syntax.scala @@ -107,50 +107,4 @@ package object syntax { override def children: List[Located] = pattern :: continuation :: Nil } type PatternSplit = Split[PatternBranch] - - def printTermSplit(split: TermSplit): Str = { - // TODO: tailrec - def termSplit(split: TermSplit, isFirst: Bool, isAfterAnd: Bool): Lines = split match { - case Split.Cons(head, tail) => (termBranch(head) match { - case (n, line) :: tail => (n, (if (isAfterAnd) "" else "and ") + s"$line") :: tail - case Nil => Nil - }) ::: termSplit(tail, false, isAfterAnd) - case Split.Let(_, nme, rhs, tail) => (0, s"let $nme = $rhs") :: termSplit(tail, false, isAfterAnd) - case Split.Else(term) => (if (isFirst) (0, s"then $term") else (0, s"else $term")) :: Nil - case Split.Nil => Nil - } - def termBranch(branch: TermBranch): Lines = branch match { - case TermBranch.Boolean(test, continuation) => - s"$test" #: termSplit(continuation, true, false) - case TermBranch.Match(scrutinee, continuation) => - s"$scrutinee is" #: patternSplit(continuation) - case TermBranch.Left(left, continuation) => - s"$left" #: operatorSplit(continuation) - } - def patternSplit(split: PatternSplit): Lines = split match { - case Split.Cons(head, tail) => patternBranch(head) ::: patternSplit(tail) - case Split.Let(rec, nme, rhs, tail) => (0, s"let $nme = $rhs") :: patternSplit(tail) - case Split.Else(term) => (0, s"else $term") :: Nil - case Split.Nil => Nil - } - def operatorSplit(split: OperatorSplit): Lines = split match { - case Split.Cons(head, tail) => operatorBranch(head) ::: operatorSplit(tail) - case Split.Let(rec, nme, rhs, tail) => (0, s"let $nme = $rhs") :: operatorSplit(tail) - case Split.Else(term) => (0, s"else $term") :: Nil - case Split.Nil => Nil - } - def operatorBranch(branch: OperatorBranch): Lines = - s"${branch.operator}" #: (branch match { - case OperatorBranch.Match(_, continuation) => patternSplit(continuation) - case OperatorBranch.Binary(_, continuation) => termSplit(continuation, true, true) - }) - def patternBranch(branch: PatternBranch): Lines = { - val PatternBranch(pattern, continuation) = branch - termSplit(continuation, true, false) match { - case (0, line) :: lines => (0, s"$pattern $line") :: lines - case lines => (0, pattern.toString) :: lines - } - } - ("if" #: termSplit(split, true, true)).iterator.map { case (n, line) => " " * n + line }.mkString("\n") - } } diff --git a/shared/src/test/diff/pretyper/ucs/coverage/MissingCases.mls b/shared/src/test/diff/pretyper/ucs/coverage/MissingCases.mls index 099f9dba..01811788 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/MissingCases.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/MissingCases.mls @@ -27,8 +27,8 @@ fun bad_add_missing_SS(x, y) = //│ ║ l.23: x is Some(xv) and y is None then xv //│ ║ ^^^^ //│ ╟── Scrutinee `y` has 1 missing case -//│ ║ l.21: fun bad_add_missing_SS(x, y) = -//│ ║ ^ +//│ ║ l.23: x is Some(xv) and y is None then xv +//│ ║ ^ //│ ╟── It can be class `Some` //│ ║ l.24: x is None and y is Some(yv) then yv //│ ╙── ^^^^ @@ -44,8 +44,8 @@ fun bad_add_missing_SN(x, y) = //│ ║ l.40: x is Some(xv) and y is Some(yv) then xv + yv //│ ║ ^^^^ //│ ╟── Scrutinee `y` has 1 missing case -//│ ║ l.38: fun bad_add_missing_SN(x, y) = -//│ ║ ^ +//│ ║ l.40: x is Some(xv) and y is Some(yv) then xv + yv +//│ ║ ^ //│ ╟── It can be module `None` //│ ║ l.42: x is None and y is None then 0 //│ ╙── ^^^^ @@ -61,8 +61,8 @@ fun bad_add_missing_NS(x, y) = //│ ║ l.59: x is None and y is None then 0 //│ ║ ^^^^ //│ ╟── Scrutinee `y` has 1 missing case -//│ ║ l.55: fun bad_add_missing_NS(x, y) = -//│ ║ ^ +//│ ║ l.59: x is None and y is None then 0 +//│ ║ ^ //│ ╟── It can be class `Some` //│ ║ l.57: x is Some(xv) and y is Some(yv) then xv + yv //│ ╙── ^^^^ @@ -78,8 +78,8 @@ fun bad_add_missing_NN(x, y) = //│ ║ l.76: x is None and y is Some(yv) then yv //│ ║ ^^^^ //│ ╟── Scrutinee `y` has 1 missing case -//│ ║ l.72: fun bad_add_missing_NN(x, y) = -//│ ║ ^ +//│ ║ l.76: x is None and y is Some(yv) then yv +//│ ║ ^ //│ ╟── It can be module `None` //│ ║ l.75: x is Some(xv) and y is None then xv //│ ╙── ^^^^ @@ -94,8 +94,8 @@ fun bad_add_missing_SS_NN(x, y) = //│ ║ l.91: x is Some(xv) and y is None then xv //│ ║ ^^^^ //│ ╟── Scrutinee `y` has 1 missing case -//│ ║ l.89: fun bad_add_missing_SS_NN(x, y) = -//│ ║ ^ +//│ ║ l.91: x is Some(xv) and y is None then xv +//│ ║ ^ //│ ╟── It can be class `Some` //│ ║ l.92: x is None and y is Some(yv) then yv //│ ╙── ^^^^ @@ -103,8 +103,8 @@ fun bad_add_missing_SS_NN(x, y) = //│ ║ l.92: x is None and y is Some(yv) then yv //│ ║ ^^^^ //│ ╟── Scrutinee `y` has 1 missing case -//│ ║ l.89: fun bad_add_missing_SS_NN(x, y) = -//│ ║ ^ +//│ ║ l.92: x is None and y is Some(yv) then yv +//│ ║ ^ //│ ╟── It can be module `None` //│ ║ l.91: x is Some(xv) and y is None then xv //│ ╙── ^^^^ @@ -119,8 +119,8 @@ fun bad_add_missing_SN_NS(x, y) = //│ ║ l.116: x is Some(xv) and y is Some(yv) then xv + yv //│ ║ ^^^^ //│ ╟── Scrutinee `y` has 1 missing case -//│ ║ l.114: fun bad_add_missing_SN_NS(x, y) = -//│ ║ ^ +//│ ║ l.116: x is Some(xv) and y is Some(yv) then xv + yv +//│ ║ ^ //│ ╟── It can be module `None` //│ ║ l.117: x is None and y is None then 0 //│ ╙── ^^^^ @@ -128,8 +128,8 @@ fun bad_add_missing_SN_NS(x, y) = //│ ║ l.117: x is None and y is None then 0 //│ ║ ^^^^ //│ ╟── Scrutinee `y` has 1 missing case -//│ ║ l.114: fun bad_add_missing_SN_NS(x, y) = -//│ ║ ^ +//│ ║ l.117: x is None and y is None then 0 +//│ ║ ^ //│ ╟── It can be class `Some` //│ ║ l.116: x is Some(xv) and y is Some(yv) then xv + yv //│ ╙── ^^^^ diff --git a/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls b/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls index c87fec46..6194694a 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls @@ -41,8 +41,8 @@ fun is_value'(term) = //│ ║ l.37: if term is Term and term is //│ ║ ^^^^ //│ ╟── Scrutinee `term` has 1 missing case -//│ ║ l.36: fun is_value'(term) = -//│ ║ ^^^^ +//│ ║ l.37: if term is Term and term is +//│ ║ ^^^^ //│ ╟── It can be class `App` //│ ║ l.6: class App(func: Term, arg: Term) extends Term //│ ╙── ^^^ From dead5269dae2db3058dd198943dd6842759cde55 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 27 Dec 2023 20:09:47 +0800 Subject: [PATCH 029/143] Group source files related to context and scrutinees --- shared/src/main/scala/mlscript/pretyper/Symbol.scala | 2 +- shared/src/main/scala/mlscript/ucs/DesugarUCS.scala | 4 ++-- .../main/scala/mlscript/ucs/{ => context}/Context.scala | 9 ++++----- .../scala/mlscript/ucs/{ => context}/ScrutineeData.scala | 2 +- shared/src/main/scala/mlscript/ucs/display.scala | 3 ++- .../scala/mlscript/ucs/stages/CoverageChecking.scala | 9 +++------ .../src/main/scala/mlscript/ucs/stages/Desugaring.scala | 4 ++-- .../main/scala/mlscript/ucs/stages/Normalization.scala | 6 +++--- .../main/scala/mlscript/ucs/stages/PostProcessing.scala | 3 ++- 9 files changed, 20 insertions(+), 22 deletions(-) rename shared/src/main/scala/mlscript/ucs/{ => context}/Context.scala (93%) rename shared/src/main/scala/mlscript/ucs/{ => context}/ScrutineeData.scala (99%) diff --git a/shared/src/main/scala/mlscript/pretyper/Symbol.scala b/shared/src/main/scala/mlscript/pretyper/Symbol.scala index c25fd1da..fc395a87 100644 --- a/shared/src/main/scala/mlscript/pretyper/Symbol.scala +++ b/shared/src/main/scala/mlscript/pretyper/Symbol.scala @@ -4,7 +4,7 @@ import collection.mutable.{Buffer, Map => MutMap, Set => MutSet} import mlscript.{Loc, NuFunDef, NuTypeDef, TypeName, Var} import mlscript.{Cls, Trt, Mxn, Als, Mod} import mlscript.utils._, shorthands._ -import mlscript.ucs.{Context, ScrutineeData} +import mlscript.ucs.context.{Context, ScrutineeData} package object symbol { sealed abstract class Symbol(val name: Str) { diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index 116ee0fc..4db6a025 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -2,12 +2,12 @@ package mlscript.ucs import collection.mutable.{Map => MutMap} import mlscript.ucs.stages._ -import mlscript.ucs.display.showNormalizedTerm +import mlscript.ucs.context.{Context, ScrutineeData} +import mlscript.ucs.display.{showNormalizedTerm, showSplit} import mlscript.pretyper.{PreTyper, Scope} import mlscript.pretyper.symbol._ import mlscript._, utils._, shorthands._ import mlscript.Message, Message.MessageContext -import mlscript.ucs.display.showSplit // TODO: Rename to `Desugarer` once the old desugarer is removed. trait DesugarUCS extends Transformation diff --git a/shared/src/main/scala/mlscript/ucs/Context.scala b/shared/src/main/scala/mlscript/ucs/context/Context.scala similarity index 93% rename from shared/src/main/scala/mlscript/ucs/Context.scala rename to shared/src/main/scala/mlscript/ucs/context/Context.scala index ad94bc1b..0c457bb1 100644 --- a/shared/src/main/scala/mlscript/ucs/Context.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Context.scala @@ -1,12 +1,11 @@ -package mlscript.ucs +package mlscript.ucs.context import collection.mutable.{Buffer, Map => MutMap, SortedMap => MutSortedMap} -import mlscript.{If, Loc, NuFunDef, NuTypeDef, TypeName, Var} -import mlscript.{Cls, Trt, Mxn, Als, Mod} +import mlscript.{If, Loc, Var} import mlscript.pretyper.symbol.TypeSymbol import mlscript.pretyper.Scope +import mlscript.ucs.VariableGenerator import mlscript.utils._, shorthands._ -import mlscript.ucs.context.MatchRegistry class Context(originalTerm: If) { private val prefix = Context.freshPrefix() @@ -62,4 +61,4 @@ object Context { // TODO: Generate fresh prefix in a determinstic way. I tried to use a counter, // but the produced value is not stable across different runs. def freshPrefix(): Str = "ucs" -} \ No newline at end of file +} diff --git a/shared/src/main/scala/mlscript/ucs/ScrutineeData.scala b/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala similarity index 99% rename from shared/src/main/scala/mlscript/ucs/ScrutineeData.scala rename to shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala index 3925e446..cd6394c8 100644 --- a/shared/src/main/scala/mlscript/ucs/ScrutineeData.scala +++ b/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala @@ -1,4 +1,4 @@ -package mlscript.ucs +package mlscript.ucs.context import collection.mutable.{Buffer, Map => MutMap, SortedMap => MutSortedMap, SortedSet => MutSortedSet} import mlscript.{Loc, Located, NuFunDef, NuTypeDef, TypeName, Var} diff --git a/shared/src/main/scala/mlscript/ucs/display.scala b/shared/src/main/scala/mlscript/ucs/display.scala index 63fb23a5..ce907286 100644 --- a/shared/src/main/scala/mlscript/ucs/display.scala +++ b/shared/src/main/scala/mlscript/ucs/display.scala @@ -1,7 +1,8 @@ package mlscript.ucs -import mlscript.ucs.{Context, Lines, LinesOps, ScrutineeData} +import mlscript.ucs.{Lines, LinesOps} import mlscript.ucs.{core, syntax} +import mlscript.ucs.context.{Context} import mlscript.pretyper.symbol.{TermSymbol, TypeSymbol} import mlscript.{App, CaseOf, Fld, FldFlags, Let, Loc, Sel, SimpleTerm, Term, Tup, Var} import mlscript.{CaseBranches, Case, Wildcard, NoCases} diff --git a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala index 851535cb..baa1fea2 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala @@ -1,14 +1,11 @@ package mlscript.ucs.stages -import annotation.tailrec -import collection.mutable.ListBuffer import mlscript.{Case, CaseBranches, CaseOf, Let, Lit, Loc, NoCases, Term, Var, Wildcard} -import mlscript.ucs.{Context, ScrutineeData} +import mlscript.{Diagnostic, ErrorReport, WarningReport} +import mlscript.Message, Message.MessageContext +import mlscript.ucs.context.{Context, CaseSet, NamedScrutineeData, MatchRegistry, ScrutineeData, SeenRegistry} import mlscript.pretyper.symbol._ import mlscript.utils._, shorthands._ -import mlscript.Message, Message.MessageContext -import mlscript.{SimpleTerm, Diagnostic, ErrorReport, WarningReport} -import mlscript.ucs.context.{CaseSet, NamedScrutineeData, MatchRegistry, SeenRegistry} trait CoverageChecking { self: mlscript.pretyper.Traceable => import CoverageChecking._ diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 752ec1ad..fd38f234 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -1,14 +1,14 @@ package mlscript.ucs.stages import mlscript.{App, Asc, Fld, FldFlags, Lit, Sel, Term, Tup, TypeName, Var} -import mlscript.ucs.{syntax => s, core => c, Context, PartialTerm} +import mlscript.ucs.{syntax => s, core => c, PartialTerm} +import mlscript.ucs.context.{Context, ScrutineeData} import mlscript.ucs.helpers.mkBinOp import mlscript.utils._, shorthands._ import mlscript.pretyper.symbol._ import mlscript.pretyper.{PreTyper, Scope} import mlscript.ucs.DesugaringException import mlscript.Message, Message.MessageContext -import mlscript.ucs.ScrutineeData /** * The desugaring stage of UCS. In this stage, we transform the source abstract diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 0fb20118..5f42fd2b 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -1,17 +1,17 @@ package mlscript.ucs.stages -import mlscript.ucs.{Context, Lines, LinesOps, ScrutineeData, VariableGenerator} +import mlscript.ucs.{Lines, LinesOps, VariableGenerator} +import mlscript.ucs.context.{Context, ScrutineeData} import mlscript.ucs.core._ +import mlscript.ucs.display.{showNormalizedTerm, showSplit} import mlscript.ucs.helpers._ -import mlscript.ucs.display.showNormalizedTerm import mlscript.pretyper.Scope import mlscript.pretyper.symbol._ import mlscript.{App, CaseOf, Fld, FldFlags, Let, Loc, Sel, Term, Tup, Var, StrLit} import mlscript.{CaseBranches, Case, Wildcard, NoCases} import mlscript.Message, Message.MessageContext import mlscript.utils._, shorthands._ -import mlscript.ucs.display.showSplit trait Normalization { self: mlscript.pretyper.Traceable => import Normalization._ diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index f0db405c..0e4a2e27 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -1,7 +1,8 @@ package mlscript.ucs.stages import mlscript.{Case, CaseBranches, CaseOf, Let, Loc, NoCases, Term, Var, Wildcard} -import mlscript.ucs.{Context, DesugarUCS, ScrutineeData} +import mlscript.ucs.DesugarUCS +import mlscript.ucs.context.{Context, ScrutineeData} import mlscript.pretyper.symbol._ import mlscript.utils._, shorthands._ import mlscript.Message, Message.MessageContext From 9e41fc358818a289ad027ee80c46e727e9c7d79a Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 27 Dec 2023 20:12:23 +0800 Subject: [PATCH 030/143] Forget to use `shadowPrefix` in `Context` --- shared/src/main/scala/mlscript/ucs/context/Context.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/src/main/scala/mlscript/ucs/context/Context.scala b/shared/src/main/scala/mlscript/ucs/context/Context.scala index 0c457bb1..bb5ae179 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Context.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Context.scala @@ -28,7 +28,7 @@ class Context(originalTerm: If) { val freshCache: VariableGenerator = new VariableGenerator(cachePrefix) val freshScrutineeVar: VariableGenerator = new VariableGenerator(scrutineePrefix) val freshTest: VariableGenerator = new VariableGenerator(testPrefix) - val freshShadowed: VariableGenerator = new VariableGenerator("shadowed$") + val freshShadowed: VariableGenerator = new VariableGenerator(shadowPrefix) /** The buffer contains all `ScrutineeData` created within this context. */ private val scrutineeBuffer: Buffer[ScrutineeData] = Buffer.empty From 0026f16e8aa0d1c2716215403e0a231d54e6d3c4 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 27 Dec 2023 20:18:52 +0800 Subject: [PATCH 031/143] Remove some `TODO`s in UCS source files --- .../scala/mlscript/ucs/context/CaseSet.scala | 8 +--- .../scala/mlscript/ucs/context/Context.scala | 4 +- .../mlscript/ucs/context/ScrutineeData.scala | 2 +- .../mlscript/ucs/stages/Desugaring.scala | 3 +- .../mlscript/ucs/stages/PostProcessing.scala | 37 ------------------- .../mlscript/ucs/stages/Transformation.scala | 1 - 6 files changed, 5 insertions(+), 50 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala b/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala index a3fc6fdf..6a897c5b 100644 --- a/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala +++ b/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala @@ -27,12 +27,8 @@ object Pattern { * with the locations where these patterns appear. * * @param patterns a set of patterns that the scrutinee is matched with. - * @param hasWildcard if the scrutinee is matched with a wildcard pattern. */ -final case class CaseSet(val cases: Map[Pattern, Ls[Loc]], val hasWildcard: Bool) { - /** TODO: This seems useless. */ - @inline def withWildcard: CaseSet = if (hasWildcard) this else copy(hasWildcard = true) - +final case class CaseSet(val cases: Map[Pattern, Ls[Loc]]) { /** * Split the pattern set into two pattern sets. * @@ -93,5 +89,5 @@ final case class CaseSet(val cases: Map[Pattern, Ls[Loc]], val hasWildcard: Bool } object CaseSet { - lazy val empty: CaseSet = CaseSet(Map.empty, false) + def empty: CaseSet = CaseSet(Map.empty) } diff --git a/shared/src/main/scala/mlscript/ucs/context/Context.scala b/shared/src/main/scala/mlscript/ucs/context/Context.scala index bb5ae179..bc143961 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Context.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Context.scala @@ -33,15 +33,13 @@ class Context(originalTerm: If) { /** The buffer contains all `ScrutineeData` created within this context. */ private val scrutineeBuffer: Buffer[ScrutineeData] = Buffer.empty - // TODO: Mark this two files as package private. def freshScrutinee: ScrutineeData = { val scrutinee = new ScrutineeData(this, N) scrutineeBuffer += scrutinee scrutinee } - // TODO: Mark this two files as package private. - def freshScrutinee(parent: ScrutineeData): ScrutineeData = { + private[context] def freshScrutinee(parent: ScrutineeData): ScrutineeData = { val scrutinee = new ScrutineeData(this, S(parent)) scrutineeBuffer += scrutinee scrutinee diff --git a/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala b/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala index cd6394c8..37f839d8 100644 --- a/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala +++ b/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala @@ -132,7 +132,7 @@ class ScrutineeData(val context: Context, parent: Opt[ScrutineeData]) { val tuplePattern = tuplePatternOpt.map { tuplePattern => Pattern.Tuple() -> tuplePattern.locations }.toMap[Pattern, Ls[Loc]] - CaseSet(cases ++ tuplePattern, false) + CaseSet(cases ++ tuplePattern) } } diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index fd38f234..64215a0e 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -20,8 +20,7 @@ import mlscript.Message, Message.MessageContext * 2. Desugar variable patterns to plain let bindings. * 3. Desugar literal patterns to equivalent boolean expressions. * 4. Reassemble partial terms that are broken by "conditional splits". - * 5. Associate each scrutinee with a unique "scrutinee symbol". - * TODO: `ScrutineeSymbol` will be removed in the future. + * 5. Associate each scrutinee with a unique `ScrutineeData`. * * Desugared UCS terms (core abstract syntax) are in the form of `Split`, which * is a list of branches. Each branch consists of a scrutinee, a pattern, and a diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index 0e4a2e27..e288ead4 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -191,43 +191,6 @@ trait PostProcessing { self: DesugarUCS with mlscript.pretyper.Traceable => other -> N } }({ case (n, y) => s"disentangle ==> `${inspect.deep(n)}` and `${y.fold("")(inspect.deep(_))}`" }) - - // FIXME: What? Is this seemingly useful function useless? - def cascadeCaseTerm(term: Term): Term = trace(s"cascadeCaseTerm <== ${term.describe}") { - // Normalized terms are constructed using `Let` and `CaseOf`. - term match { - case top @ CaseOf(scrutinee: Var, fst @ Case(pattern, body, NoCases)) => - println(s"found a UNARY case: $scrutinee is $pattern") - top.copy(cases = fst.copy(body = cascadeCaseTerm(body))) - case top @ CaseOf(scrutinee: Var, fst @ Case(pattern, trueBranch, snd @ Wildcard(falseBranch))) => - println(s"found a BINARY case: $scrutinee is $pattern") - println("cascading the true branch") - val processedTrueBranch = cascadeCaseTerm(trueBranch) - println("cascading the false branch") - val processedFalseBranch = cascadeCaseTerm(falseBranch) - // Check if the false branch is another `CaseOf` with the same scrutinee. - processedFalseBranch match { - case CaseOf(otherScrutinee: Var, actualFalseBranch) => - if (scrutinee.symbol === otherScrutinee.symbol) { - println(s"identical: $scrutinee === $otherScrutinee") - if (scrutinee.name =/= otherScrutinee.name) { - // TODO: solve name collision by creating a lifted `Let` - ??? - } - println(s"actual false branch: $actualFalseBranch") - top.copy(cases = fst.copy(body = processedTrueBranch, rest = actualFalseBranch)) - } else { - println(s"different: $scrutinee =/= $otherScrutinee") - top.copy(cases = fst.copy(body = processedTrueBranch, rest = snd.copy(body = processedFalseBranch))) - } - case other => top - } - // We recursively process the body of `Let` bindings. - case let @ Let(_, _, _, body) => let.copy(body = cascadeCaseTerm(body)) - // Otherwise, this is not a part of a normalized term. - case other => println(s"CANNOT cascade"); other - } - }(_ => "cascadeCaseTerm ==> ") } object PostProcessing { diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index 64f5ede3..83851a77 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -139,7 +139,6 @@ trait Transformation { self: mlscript.pretyper.Traceable => case _ -> Fld(_, Var("_")) => N // Consider "_" as wildcard. case _ -> Fld(_, t ) => S(transformPattern(t)) }) - // TODO: Support more patterns. case _ => println(s"unknown pattern: $term") throw new TransformException(msg"Unknown pattern", term.toLoc) From 3b04b1a3dba4a331e4f249131021c24f6a4a073b Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 27 Dec 2023 21:22:03 +0800 Subject: [PATCH 032/143] Remake untyped lambda calculus example --- .../test/diff/pretyper/ucs/examples/ULC.mls | 533 ++++++++++++++++++ 1 file changed, 533 insertions(+) create mode 100644 shared/src/test/diff/pretyper/ucs/examples/ULC.mls diff --git a/shared/src/test/diff/pretyper/ucs/examples/ULC.mls b/shared/src/test/diff/pretyper/ucs/examples/ULC.mls new file mode 100644 index 00000000..cc1c507f --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/examples/ULC.mls @@ -0,0 +1,533 @@ +:PreTyper + +fun (++) concatOp(a, b) = concat(a)(b) +fun (|>) pipe(a, f) = f(a) +fun (!==) notEqual(x, y) = not(x === y) +//│ fun (++) concatOp: (Str, Str) -> Str +//│ fun (|>) pipe: forall 'a 'b. ('a, 'a -> 'b) -> 'b +//│ fun (!==) notEqual: forall 'c. (Eql['c], 'c) -> Bool + +fun par(a) = "(" ++ a ++ ")" +//│ fun par: Str -> Str + +declare fun String: nothing +//│ fun String: nothing + +let makeString: anything => { length: Int, charCodeAt: Int => Int } = String +let StringInstance: { fromCharCode: Int => Str } = String +//│ let makeString: anything -> {charCodeAt: Int -> Int, length: Int} +//│ let StringInstance: {fromCharCode: Int -> Str} +//│ makeString +//│ = [Function: String] +//│ StringInstance +//│ = [Function: String] + +let anythingToString = toString +fun fromCharCode(n: Int) = StringInstance.fromCharCode(n) +fun stringCharCodeAt(s: Str, i) = makeString(s).charCodeAt(i) +fun stringLength(s: Str) = makeString(s).length +//│ let anythingToString: anything -> Str +//│ fun fromCharCode: (n: Int) -> Str +//│ fun stringCharCodeAt: (s: Str, Int) -> Int +//│ fun stringLength: (s: Str) -> Int +//│ anythingToString +//│ = [Function: toString] + +type Option[A] = Some[A] | None +class Some[A](value: A) { + fun toString() = "Some(" ++ anythingToString(value) ++ ")" +} +module None { + fun toString() = "None" +} +fun showOption(opt) = + if opt is + Some(x) then "Some(" ++ toString(x) ++ ")" + None then "None" +//│ type Option[A] = None | Some[A] +//│ class Some[A](value: A) { +//│ fun toString: () -> Str +//│ } +//│ module None { +//│ fun toString: () -> "None" +//│ } +//│ fun showOption: (None | Some[anything]) -> Str + +type List[A] = Cons[A] | Nil +class Cons[A](head: A, tail: List[A]) +module Nil +//│ type List[A] = Cons[A] | Nil +//│ class Cons[A](head: A, tail: List[A]) +//│ module Nil + +fun (::) cons(head, tail) = Cons(head, tail) +//│ fun (::) cons: forall 'A. ('A, List['A]) -> Cons['A] + +fun join(sep) = + let aux(acc, xs) = + if xs is + Nil then acc + Cons(x, xs') then aux(acc ++ sep ++ toString(x), xs') + (xs) => + if xs is + Cons(x, xs') then aux(toString(x), xs') + Nil then "" +//│ fun join: Str -> (forall 'A. (Cons['A] | Nil) -> Str) + +fun showList(xs) = "[" ++ join(", ")(xs) ++ "]" +//│ fun showList: forall 'A. (Cons['A] | Nil) -> Str + +fun findFirst(list, p) = + if list is + Nil then None + Cons(x, xs) and + p(x) then Some(x) + else findFirst(xs, p) +//│ fun findFirst: forall 'A. (Cons['A] | Nil, 'A -> Bool) -> (None | Some['A]) + +fun (:::) listConcat(xs, ys) = + if xs is + Nil then ys + Cons(x, xs') then Cons(x, listConcat(xs', ys)) +//│ fun (:::) listConcat: forall 'A 'A0 'a. (Cons['A] | Nil, List['A0] & 'a) -> (Cons['A0] | 'a) +//│ where +//│ 'A <: 'A0 + +fun contains(xs, x) = + if xs is + Nil then false + Cons(x', xs') and + x === x' then true + else contains(xs', x) +//│ fun contains: forall 'A. (Cons['A] | Nil, Eql['A]) -> Bool + +contains("x" :: "y" :: "z" :: Nil, "y") +contains("x" :: "y" :: "z" :: Nil, "w") +//│ Bool +//│ res +//│ = true +//│ res +//│ = false + +fun exclude(xs, x) = + if xs is + Nil then Nil + Cons(x', xs') and + x === x' then exclude(xs', x) + else Cons(x', exclude(xs', x)) +//│ fun exclude: forall 'A 'A0. (Cons['A] | Nil, Eql['A]) -> (Cons['A0] | Nil) +//│ where +//│ 'A <: 'A0 + +exclude("x" :: "y" :: "z" :: Nil, "y") |> showList +exclude("x" :: "y" :: "z" :: Nil, "w") |> showList +//│ Str +//│ res +//│ = '[x, z]' +//│ res +//│ = '[x, y, z]' + +fun reverse(xs: List['B]): List['B] = + let aux(acc: List['B], ys: List['B]): List['B] = + if ys is + Nil then acc + Cons(y, ys') then aux(Cons(y, acc), ys') + aux(Nil, xs) +//│ fun reverse: forall 'B 'A. (xs: List['B]) -> List['A] +//│ where +//│ 'B <: 'A + +reverse(1 :: 2 :: 3 :: Nil) |> showList +reverse(3 :: 2 :: 1 :: Nil) |> showList +//│ Str +//│ res +//│ = '[3, 2, 1]' +//│ res +//│ = '[1, 2, 3]' + +// _____ +// |_ _|___ _ __ _ __ ___ +// | | / _ \| '__|| '_ ` _ \ +// | || __/| | | | | | | | +// |_| \___||_| |_| |_| |_| +// + +type Term = Var | Abs | App +class Var(name: Str) +class Abs(lhs: Var, rhs: Term) +class App(lhs: Term, rhs: Term) +//│ type Term = Abs | App | Var +//│ class Var(name: Str) +//│ class Abs(lhs: Var, rhs: Term) +//│ class App(lhs: Term, rhs: Term) + +fun showTerm(t) = + if t is + Var(name) then toString(name) + Abs(lhs, rhs) then "λ" ++ showTerm(lhs) ++ ". " ++ showTerm(rhs) + App(Abs(lhs0, lhs1), rhs) then + "((" ++ "λ" ++ showTerm(lhs0) ++ ". " ++ showTerm(lhs1) ++ ") " ++ showTerm(rhs) ++ ")" + App(lhs, rhs) then par(showTerm(lhs) ++ " " ++ showTerm(rhs)) +//│ fun showTerm: (Abs | App | Var) -> Str + +showTerm(Var("x")) +showTerm(Abs(Var("x"), Var("y"))) +showTerm(App(Var("x"), Var("y"))) +showTerm(App(Abs(Var("x"), Var("y")), Var("z"))) +//│ Str +//│ res +//│ = 'x' +//│ res +//│ = 'λx. y' +//│ res +//│ = '(x y)' +//│ res +//│ = '((λx. y) z)' + +fun (=:=) equalTerm(t1: Term, t2: Term) = + if t1 is + Var(x1) and t2 is Var(x2) then x1 === x2 + Abs(x1, t1') and t2 is Abs(x2, t2') then (x1 =:= x2) && (t1' =:= t2') + App(t1', t1'') and t2 is App(t2', t2'') then (t1' =:= t2') && (t1'' =:= t2'') + else false +//│ fun (=:=) equalTerm: (t1: Term, t2: Term) -> Bool + +Var("x") =:= Var("x") +Var("x") =:= Var("y") +Abs(Var("x"), Var("x")) =:= Abs(Var("x"), Var("x")) +Abs(Var("x"), Var("x")) =:= Abs(Var("x"), Var("y")) +Abs(Var("x"), Var("y")) =:= Abs(Var("x"), Var("x")) +//│ Bool +//│ res +//│ = true +//│ res +//│ = false +//│ res +//│ = true +//│ res +//│ = false +//│ res +//│ = false + +fun isValue(t) = + if t is + Abs then true + Var then false + App then false +//│ fun isValue: (Abs | App | Var) -> Bool + +isValue(Var("x")) +isValue(Abs(Var("x"), Var("y"))) +isValue(App(Var("x"), Var("y"))) +//│ Bool +//│ res +//│ = false +//│ res +//│ = true +//│ res +//│ = false + +fun hasFree(t, x) = + if t is + Var(x') then x === x' + Abs(Var(x'), body) and x === x' then false + Abs(Var(_), body) then hasFree(body, x) + App(lhs, rhs) then hasFree(lhs, x) || hasFree(rhs, x) + _ then false +//│ fun hasFree: (Abs | App | Object & ~#Abs & ~#App & ~#Var | Var, Eql[Str]) -> Bool + +fun showHasFree(t, n) = + showTerm(t) ++ (if hasFree(t, n) then " has " else " DOES NOT have ") ++ "free variable " ++ n +//│ fun showHasFree: (Abs | App | Var, Eql[Str] & Str) -> Str + +showHasFree(Var("x"), "x") +showHasFree(Var("x"), "y") +showHasFree(Abs(Var("x"), Var("x")), "x") +showHasFree(Abs(Var("x"), Var("x")), "y") +showHasFree(Abs(Var("x"), Var("y")), "x") +showHasFree(Abs(Var("x"), Var("y")), "y") +showHasFree(App(Var("x"), Var("y")), "x") +showHasFree(App(Var("x"), Var("y")), "y") +showHasFree(App(Abs(Var("x"), Var("x")), Var("x")), "x") +showHasFree(App(Abs(Var("x"), Var("x")), Var("x")), "y") +showHasFree(App(Abs(Var("x"), Var("x")), Var("y")), "y") +showHasFree(App(Abs(Var("x"), Var("x")), Var("x")), "y") +//│ Str +//│ res +//│ = 'x has free variable x' +//│ res +//│ = 'x DOES NOT have free variable y' +//│ res +//│ = 'λx. x DOES NOT have free variable x' +//│ res +//│ = 'λx. x DOES NOT have free variable y' +//│ res +//│ = 'λx. y DOES NOT have free variable x' +//│ res +//│ = 'λx. y has free variable y' +//│ res +//│ = '(x y) has free variable x' +//│ res +//│ = '(x y) has free variable y' +//│ res +//│ = '((λx. x) x) has free variable x' +//│ res +//│ = '((λx. x) x) DOES NOT have free variable y' +//│ res +//│ = '((λx. x) y) has free variable y' +//│ res +//│ = '((λx. x) x) DOES NOT have free variable y' + +fun freeVars(t) = + if t is + Var(x) then x :: Nil + Abs(Var(x), body) then exclude(freeVars(body), x) + App(lhs, rhs) then freeVars(lhs) ::: freeVars(rhs) +//│ fun freeVars: forall 'A. (Abs | App | Var) -> (Cons['A] | Nil) +//│ where +//│ 'A :> Str + +(freeVars of Var("x")) |> showList +(freeVars of Abs(Var("x"), Var("x"))) |> showList +(freeVars of Abs(Var("x"), Var("y"))) |> showList +(freeVars of App(Var("x"), Var("y"))) |> showList +(freeVars of App(Abs(Var("x"), Var("x")), Var("x"))) |> showList +//│ Str +//│ res +//│ = '[x]' +//│ res +//│ = '[]' +//│ res +//│ = '[y]' +//│ res +//│ = '[x, y]' +//│ res +//│ = '[x]' + +let alphabet: List[Str] = "a" :: "b" :: "c" :: "d" :: "e" :: "f" :: "g" :: "h" :: "i" :: "j" :: "k" :: "l" :: "m" :: "n" :: "o" :: "p" :: "q" :: "r" :: "s" :: "t" :: "u" :: "v" :: "w" :: "x" :: "y" :: "z" :: Nil +//│ let alphabet: List[Str] +//│ alphabet +//│ = Cons {} + +fun search(f: 'A -> Option['B], xs: List['A]): Option['B] = + if xs is + Nil then None + Cons(x, xs') and + f(x) is + Some(x') then Some(x') + None then search(f, xs') +//│ fun search: forall 'A 'B 'A0. (f: 'A -> Option['B], xs: List['A]) -> Option['A0] +//│ where +//│ 'B <: 'A0 + +// The removal of type annotations will cause running out of fuel. +fun combinations(n: Int, acc: List['A], alphabet: List['A], xs: List[Str]): Option[Str] = + if + n <= 0 and + let x = reverse(acc) |> join("") + contains(xs, x) then None + else Some(x) + else + search((x) => combinations(n - 1, x :: acc, alphabet, xs), alphabet) +//│ fun combinations: forall 'A 'A0 'A1. (n: Int, acc: List['A], alphabet: List['A1], xs: List[Str]) -> Option[Str] +//│ where +//│ 'A1 <: 'A & 'A0 +//│ 'A :> 'A0 +//│ 'A0 := 'A + +combinations(1, Nil, 1 :: 2 :: 3 :: Nil, Nil) |> showOption +combinations(2, Nil, 1 :: 2 :: 3 :: Nil, Nil) |> showOption +combinations(3, Nil, 1 :: 2 :: 3 :: Nil, Nil) |> showOption +combinations(1, Nil, 1 :: 2 :: 3 :: Nil, "1" :: "3" :: Nil) |> showOption +combinations(2, Nil, 1 :: 2 :: 3 :: Nil, "11" :: "12" :: "13" :: Nil) |> showOption +combinations(3, Nil, 1 :: 2 :: 3 :: Nil, "111" :: "112" :: "113" :: "121" :: Nil) |> showOption +//│ Str +//│ res +//│ = 'Some(1)' +//│ res +//│ = 'Some(11)' +//│ res +//│ = 'Some(111)' +//│ res +//│ = 'Some(2)' +//│ res +//│ = 'Some(21)' +//│ res +//│ = 'Some(122)' + +fun freshVar(t: Term): Str = + let fvs = freeVars(t) + let aux(n: Int): Str = + if combinations(n, Nil, alphabet, fvs) is + Some(x) then x + None then aux(n + 1) + aux(1) +//│ fun freshVar: (t: Term) -> Str + +freshVar(Var("x")) +freshVar(App(Var("a"), Var("b"))) +freshVar(App(Abs(Var("a"), Var("a")), Var("b"))) +//│ Str +//│ res +//│ = 'a' +//│ res +//│ = 'c' +//│ res +//│ = 'a' + +fun subst(t: Term, x: Str, v: Term): Term = + if t is + Var(y) and x === y then v + Abs(Var(y), t') and x !== y and + hasFree(v, y) then + let y' = freshVar(t') + let t'' = subst(t', y, Var(y')) + Abs(Var(y'), subst(t'', x, v)) + else + Abs(Var(y), subst(t', x, v)) + App(lhs, rhs) then App(subst(lhs, x, v), subst(rhs, x, v)) + else t +//│ fun subst: (t: Term, x: Str, v: Term) -> Term + +fun showSubst(t, n, v) = + showTerm(t) ++ " [" ++ n ++ " / " ++ showTerm(v) ++ "]" ++ " = " ++ showTerm(subst(t, n, v)) +//│ fun showSubst: (Abs & Term | App & Term | Var & Term, Str, Abs & Term | App & Term | Var & Term) -> Str + +showSubst(Var("x"), "x", Var("y")) +showSubst(Abs(Var("x"), Var("x")), "x", Var("z")) +showSubst(App(Var("x"), Var("y")), "x", Abs(Var("x"), Var("x"))) +showSubst(App(Abs(Var("x"), Var("x")), Var("x")), "x", Abs(Var("y"), Var("y"))) +showSubst(Abs(Var("x"), App(Var("x"), Var("y"))), "y", Var("x")) +showSubst(Abs(Var("z"), Abs(Var("x"), App(Var("z"), App(Var("x"), Var("y"))))), "y", Var("x")) +showSubst(Abs(Var("z"), Abs(Var("x"), App(Var("z"), App(Var("x"), Var("y"))))), "y", App(Var("x"), Var("z"))) +//│ Str +//│ res +//│ = 'x [x / y] = y' +//│ res +//│ = 'λx. x [x / z] = λx. x' +//│ res +//│ = '(x y) [x / λx. x] = ((λx. x) y)' +//│ res +//│ = '((λx. x) x) [x / λy. y] = ((λx. x) λy. y)' +//│ res +//│ = 'λx. (x y) [y / x] = λa. (a x)' +//│ res +//│ = 'λz. λx. (z (x y)) [y / x] = λz. λa. (z (a x))' +//│ res +//│ = 'λz. λx. (z (x y)) [y / (x z)] = λa. λb. (a (b (x z)))' + +// ____ _ _ ____ _ +// / ___| _ __ ___ __ _ | || | / ___| | |_ ___ _ __ +// \___ \ | '_ ` _ \ / _` || || | \___ \ | __|/ _ \| '_ \ +// ___) || | | | | || (_| || || | ___) || |_| __/| |_) | +// |____/ |_| |_| |_| \__,_||_||_| |____/ \__|\___|| .__/ +// |_| + +type Result = Normal | Stuck | Stepped +class Normal(term: Term) { + fun toString() = "Normal form: " ++ showTerm(term) +} +class Stuck(term: Term, part: Term) { + fun toString() = "Stuck: " ++ showTerm(part) ++ " in " ++ showTerm(term) +} +class Stepped(from: Term, to: Term) { + fun toString() = showTerm(from) ++ " => " ++ showTerm(to) +} +//│ type Result = Normal | Stepped | Stuck +//│ class Normal(term: Term) { +//│ fun toString: () -> Str +//│ } +//│ class Stuck(term: Term, part: Term) { +//│ fun toString: () -> Str +//│ } +//│ class Stepped(from: Term, to: Term) { +//│ fun toString: () -> Str +//│ } + +fun stepByValue(t) = + if t is + Var then Stuck(t, t) + Abs then Normal(t) + App(lhs, rhs) and stepByValue(lhs) is + Stepped(_, lhs) then Stepped(t, App(lhs, rhs)) + Stuck(_, part) then Stuck(t, part) + Normal and stepByValue(rhs) is + Stepped(_, rhs) then Stepped(t, App(lhs, rhs)) + Stuck(_, part) then Stuck(t, part) + Normal and lhs is + Abs(Var(name), body) then Stepped(t, subst(body, name, rhs)) + _ then Stuck(t, lhs) +//│ fun stepByValue: (Abs | App | Var) -> (Normal | Stepped | Stuck) + +toString of stepByValue of Var("x") +toString of stepByValue of Abs(Var("x"), Var("y")) +toString of stepByValue of App(Var("x"), Var("y")) +toString of stepByValue of App(Abs(Var("x"), Var("x")), Var("x")) +toString of stepByValue of App(Abs(Var("x"), Var("x")), Abs(Var("y"), Var("y"))) +//│ Str +//│ res +//│ = 'Stuck: x in x' +//│ res +//│ = 'Normal form: λx. y' +//│ res +//│ = 'Stuck: x in (x y)' +//│ res +//│ = 'Stuck: x in ((λx. x) x)' +//│ res +//│ = '((λx. x) λy. y) => λy. y' + +// _____ _ _ _ +// | ____|__ __ __ _ | | _ _ __ _ | |_ (_) ___ _ __ +// | _| \ \ / // _` || || | | | / _` || __|| | / _ \ | '_ \ +// | |___ \ V /| (_| || || |_| || (_| || |_ | || (_) || | | | +// |_____| \_/ \__,_||_| \__,_| \__,_| \__||_| \___/ |_| |_| +// + +fun eval(step) = + let aux(t) = + if step(t) is result and result is + Stepped(_, t') then aux(t') + else result + aux +//│ fun eval: forall 'a 'b. ((Term | 'a) -> (Object & 'b & ~#Stepped | Stepped)) -> 'a -> 'b + +let evalByValue = eval(stepByValue) +//│ let evalByValue: (Abs | App | Var) -> (Normal | Stuck) +//│ evalByValue +//│ = [Function: aux] + +// Let's program with Church encoding! +let zero = Abs(Var("f"), Abs(Var("x"), Var("x"))) +let one = Abs(Var("f"), Abs(Var("x"), App(Var("f"), Var("x")))) +toString of stepByValue of zero +toString of stepByValue of one +let succ = Abs(Var("n"), Abs(Var("f"), Abs(Var("x"), App(Var("f"), App(App(Var("n"), Var("f")), Var("x")))))) +toString of stepByValue of succ +toString of stepByValue of App(succ, zero) +//│ let zero: Abs +//│ let one: Abs +//│ let succ: Abs +//│ Str +//│ zero +//│ = Abs {} +//│ one +//│ = Abs {} +//│ res +//│ = 'Normal form: λf. λx. x' +//│ res +//│ = 'Normal form: λf. λx. (f x)' +//│ succ +//│ = Abs {} +//│ res +//│ = 'Normal form: λn. λf. λx. (f ((n f) x))' +//│ res +//│ = '((λn. λf. λx. (f ((n f) x))) λf. λx. x) => λf. λx. (f (((λf. λx. x) f) x))' + +toString of evalByValue of App(succ, App(succ, zero)) +toString of evalByValue of App(succ, App(succ, App(succ, App(succ, zero)))) +//│ Str +//│ res +//│ = 'Normal form: λf. λx. (f (((λf. λx. (f (((λf. λx. x) f) x))) f) x))' +//│ res +//│ = 'Normal form: λf. λx. (f (((λf. λx. (f (((λf. λx. (f (((λf. λx. (f (((λf. λx. x) f) x))) f) x))) f) x))) f) x))' + From 4cc5957b04a03e0d9d8023a3cfc23fd7e6d3e2c9 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Fri, 29 Dec 2023 00:34:23 +0800 Subject: [PATCH 033/143] Fix code generation for local functions declared by `let rec` --- shared/src/main/scala/mlscript/JSBackend.scala | 3 ++- shared/src/test/diff/nu/LetRec.mls | 9 --------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/shared/src/main/scala/mlscript/JSBackend.scala b/shared/src/main/scala/mlscript/JSBackend.scala index 621fb7bd..c193e469 100644 --- a/shared/src/main/scala/mlscript/JSBackend.scala +++ b/shared/src/main/scala/mlscript/JSBackend.scala @@ -239,7 +239,8 @@ abstract class JSBackend(allowUnresolvedSymbols: Bool) { case (t: Term, index) => JSExprStmt(translateTerm(t)(blkScope)) case (NuFunDef(isLetRec, Var(nme), symNme, _, L(rhs)), _) => val symb = symNme.map(_.name) - val pat = blkScope.declareValue(nme, isLetRec, isLetRec.isEmpty, symb) + val isLocalFunction = isLetRec.isEmpty || (rhs match { case _: Lam => true; case _ => false }) + val pat = blkScope.declareValue(nme, isLetRec, isLocalFunction, symb) JSLetDecl(Ls(pat.runtimeName -> S(translateTerm(rhs)(blkScope)))) case (nt: NuTypeDef, _) => translateLocalNewType(nt)(blkScope) // TODO: find out if we need to support this. diff --git a/shared/src/test/diff/nu/LetRec.mls b/shared/src/test/diff/nu/LetRec.mls index 51ad7c37..ab93a341 100644 --- a/shared/src/test/diff/nu/LetRec.mls +++ b/shared/src/test/diff/nu/LetRec.mls @@ -95,29 +95,20 @@ fun test = //│ Code generation encountered an error: //│ unguarded recursive use of by-value binding f -:ge // TODO this one should actually be accepted by codegen! fun test = let rec f() = f() //│ fun test: () -//│ Code generation encountered an error: -//│ unguarded recursive use of by-value binding f -:ge // TODO this one should actually be accepted by codegen! fun test = let rec lol = () => lol //│ fun test: () -//│ Code generation encountered an error: -//│ unguarded recursive use of by-value binding lol -:ge // TODO this one should actually be accepted by codegen! fun test = let rec lol() = lol lol //│ fun test: forall 'lol. 'lol //│ where //│ 'lol :> () -> 'lol -//│ Code generation encountered an error: -//│ unguarded recursive use of by-value binding lol let rec lol = () => lol //│ let rec lol: forall 'lol. 'lol From cfc51a3d87d124ddb58d589ac87c43299dc89da1 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Fri, 29 Dec 2023 04:42:55 +0800 Subject: [PATCH 034/143] Add Lisp interpreter example and improve `PreTyper`'s structure --- .../src/main/scala/mlscript/JSBackend.scala | 1 + .../scala/mlscript/pretyper/PreTyper.scala | 218 +++----- .../main/scala/mlscript/pretyper/Scope.scala | 19 +- .../main/scala/mlscript/pretyper/Symbol.scala | 78 +-- .../main/scala/mlscript/ucs/DesugarUCS.scala | 2 +- .../mlscript/ucs/context/Matchable.scala | 39 ++ .../mlscript/ucs/stages/Desugaring.scala | 25 +- .../scala/mlscript/ucs/stages/package.scala | 16 +- shared/src/test/diff/mlscript/Repro.mls | 7 +- .../src/test/diff/pretyper/ucs/DualOption.mls | 2 + .../diff/pretyper/ucs/examples/Calculator.mls | 12 +- .../pretyper/ucs/examples/LispInterpreter.mls | 508 ++++++++++++++++++ .../diff/pretyper/ucs/examples/ListFold.mls | 10 +- .../diff/pretyper/ucs/examples/Option.mls | 2 +- .../pretyper/ucs/examples/Permutations.mls | 20 +- .../test/diff/pretyper/ucs/examples/ULC.mls | 18 +- .../diff/pretyper/ucs/patterns/Literals.mls | 2 +- 17 files changed, 751 insertions(+), 228 deletions(-) create mode 100644 shared/src/main/scala/mlscript/ucs/context/Matchable.scala create mode 100644 shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls diff --git a/shared/src/main/scala/mlscript/JSBackend.scala b/shared/src/main/scala/mlscript/JSBackend.scala index c193e469..75e2aa84 100644 --- a/shared/src/main/scala/mlscript/JSBackend.scala +++ b/shared/src/main/scala/mlscript/JSBackend.scala @@ -107,6 +107,7 @@ abstract class JSBackend(allowUnresolvedSymbols: Bool) { else throw new UnimplementedError(sym) case S(sym: ValueSymbol) => + // Temporary disable this line of code because it invalidates many working test cases. if (sym.isByvalueRec.getOrElse(false) && !sym.isLam) throw CodeGenError(s"unguarded recursive use of by-value binding $name") sym.visited = true val ident = JSIdent(sym.runtimeName) diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index 2cfcaaaa..414c150f 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -4,6 +4,7 @@ import collection.mutable.{Set => MutSet} import mlscript.ucs.DesugarUCS import symbol._ import mlscript._, utils._, shorthands._ +import mlscript.{Cls, Trt, Mxn, Als, Mod} import scala.annotation.tailrec import mlscript.Message, Message.MessageContext @@ -13,20 +14,23 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D protected def raise(diagnostics: Diagnostic): Unit = () protected def raise(diagnostics: Ls[Diagnostic]): Unit = () - private def extractParameters(fields: Term): Ls[ValueSymbol] = + private def extractParameters(fields: Term): Ls[LocalTermSymbol] = trace(s"extractParameters <== ${inspect.deep(fields)}") { fields match { case Tup(arguments) => arguments.flatMap { - case (S(nme: Var), Fld(_, _)) => new ValueSymbol(nme, false) :: Nil - case (_, Fld(_, nme: Var)) => new ValueSymbol(nme, false) :: Nil - case (_, Fld(_, Bra(false, nme: Var))) => new ValueSymbol(nme, false) :: Nil + case (S(nme: Var), Fld(_, _)) => new LocalTermSymbol(nme) :: Nil + case (_, Fld(_, nme: Var)) => new LocalTermSymbol(nme) :: Nil + case (_, Fld(_, Bra(false, nme: Var))) => new LocalTermSymbol(nme) :: Nil case (_, Fld(_, tuple @ Tup(_))) => extractParameters(tuple) - case (_, Fld(_, _)) => ??? + case (_, Fld(_, Asc(term, _))) => extractParameters(term) + case (_, Fld(_, parameter)) => + println(s"unknown parameter: ${inspect.deep(parameter)}") + ??? } case PlainTup(arguments @ _*) => arguments.map { - case nme: Var => new ValueSymbol(nme, false) + case nme: Var => new LocalTermSymbol(nme) case other => println("Unknown parameters: " + inspect.deep(other)); ??? // TODO: bad }.toList case other => println("Unknown parameters: " + inspect.deep(other)); ??? // TODO: bad @@ -38,21 +42,20 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D protected def resolveVar(v: Var)(implicit scope: Scope): Unit = trace(s"resolveVar(name = \"$v\")") { scope.getTermSymbol(v.name) match { - case S(sym: ValueSymbol) => - println(s"Resolve variable $v to a value.") + case S(sym: LocalTermSymbol) => + println(s"Resolve variable $v to a local term.") v.symbol = sym - case S(sym: FunctionSymbol) => - println(s"Resolve variable $v to a function.") + case S(sym: DefinedTermSymbol) => + println(s"Resolve variable $v to a defined term.") + v.symbol = sym + case S(sym: ModuleSymbol) => + println(s"Resolve variable $v to a module.") v.symbol = sym case N => scope.getTypeSymbol(v.name) match { case S(sym: ClassSymbol) => - if (sym.defn.kind == Cls) { - println(s"Resolve variable $v to a class.") - v.symbol = sym - } else { - throw new Exception(s"Name $v refers to a type") - } + println(s"Resolve variable $v to a class.") + v.symbol = sym case S(_) => throw new Exception(s"Name $v refers to a type") case N => throw new Exception(s"Variable $v not found in scope") } @@ -81,8 +84,9 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D case Sel(receiver, fieldName) => traverseTerm(receiver) case Let(isRec, nme, rhs, body) => traverseTerm(rhs) - traverseTerm(body)(scope + new ValueSymbol(nme, false)) + traverseTerm(body)(scope + new LocalTermSymbol(nme)) case New(head, body) => + // `new C(...)` or `new C(){...}` or `new{...}` case Tup(fields) => fields.foreach { case (_, Fld(_, t)) => traverseTerm(t) } case Asc(trm, ty) => traverseTerm(trm) case ef @ If(_, _) => traverseIf(ef)(scope) @@ -122,81 +126,32 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D () }(_ => s"traverseTypeDefinition <== ${defn.describe}") - private def traverseFunction(symbol: FunctionSymbol, defn: NuFunDef)(implicit scope: Scope): Unit = - trace(s"traverseFunction <== ${defn.nme.name}") { - require(defn.isLetRec.isEmpty) // Make sure it's defined via `fun`. - defn.rhs match { - case Left(term) => - val subScope = if (defn.isLetRec === S(false)) scope else scope + symbol - traverseTerm(term)(subScope) - case Right(ty) => println(s"found a pure declaration: $ty") - } - }(_ => s"traverseFunction ==> ${defn.nme.name}") - - private def traverseLetBinding(symbol: ValueSymbol, rec: Bool, rhs: Term)(implicit scope: Scope): Unit = - trace(s"traverseLetBinding(rec = $rec, ${symbol.name})") { - traverseTerm(rhs)(scope + symbol) - }() - private def traverseStatements(statements: Ls[Statement], name: Str, parentScope: Scope): (Scope, TypeContents) = trace(s"traverseStatements <== $name: ${"statement".pluralize(statements.size, true)}") { - import mlscript.{Cls, Trt, Mxn, Als, Mod} - // Pass 1: Build a scope with hoisted symbols. - val hoistedScope = statements.foldLeft(parentScope.derive) { - case (acc, _: Term) => acc // Skip - case (acc, defn: NuTypeDef) => - val `var` = Var(defn.nme.name).withLoc(defn.nme.toLoc) - // Create a type symbol but do not visit its inner members - val symbol = defn.kind match { - case Cls => new ClassSymbol(defn) - case Als => new TypeAliasSymbol(defn) - case Mxn => new MixinSymbol(defn) - case Trt => new TraitSymbol(defn) - case Mod => new ModuleSymbol(defn) - } - println("parent types: " + defn.parents.iterator.map(inspect.deep(_)).mkString("; ")) - acc ++ (symbol :: - (defn.kind match { - case Mod => new ValueSymbol(`var`, true) :: Nil - case Als | Cls | Mxn | Trt => Nil - })) - case (acc, defn: NuFunDef) if defn.isLetRec.isEmpty => acc + new FunctionSymbol(defn) - case (acc, _: NuFunDef) => acc - case (acc, _: Constructor | _: DataDefn | _: DatatypeDefn | _: Def | _: LetS | _: TypeDef) => ??? // TODO: When? + // Pass 1: Build a scope with type symbols only. + val filterNuTypeDef = { (_: Statement) match { case t: NuTypeDef => S(t); case _ => N } } + val typeSymbols = statements.iterator.flatMap(filterNuTypeDef).map(TypeSymbol(_)).toList + val scopeWithTypes = parentScope.derive ++ typeSymbols + println(typeSymbols.iterator.map(_.name).mkString("type symbols: {", ", ", "}")) + // val scopeWithTypes = statements.iterator.flatMap(filterNuTypeDef).foldLeft(parentScope.derive)(_ + TypeSymbol(_)) + // Pass 1.1: Resolve subtyping relations. Build a graph and compute base types of each type. + val edges = typeSymbols.foldLeft(Map.empty[TypeSymbol, Ls[TypeSymbol]]) { case (acc, self) => + acc + (self -> extractSuperTypes(self.defn.parents).map { nme => + scopeWithTypes.getTypeSymbol(nme.name).getOrElse(???) }) } - // Resolve base types. - val subtypingRelations = statements.foldLeft(Map.empty[Var, Ls[Var]]) { - case (acc, defn: NuTypeDef) => - val thisType = Var(defn.nme.name).withLoc(defn.nme.toLoc) - val superTypes = extractSuperTypes(defn.parents) - acc + (thisType -> superTypes) - case (acc, _: Term | _: NuFunDef) => acc - case (acc, _: Constructor | _: DataDefn | _: DatatypeDefn | _: Def | _: LetS | _: TypeDef) => ??? // TODO: When? + printGraph(edges, println, "inheritance relations", "->") + transitiveClosure(edges).foreachEntry { (self, bases) => + self.baseTypes = bases + println(s"base types of `${self.name}`: ${bases.iterator.map(_.name).mkString(", ")}") } - val superTypes = transitiveClosure(subtypingRelations) - printGraph(subtypingRelations, println, "Subtyping relations", "->") - superTypes.foreachEntry { case (thisType, baseTypes) => - hoistedScope.getTypeSymbol(thisType.name) match { - case S(symbol) => symbol.baseTypes = - baseTypes.map(baseType => hoistedScope.getTypeSymbol(baseType.name).getOrElse(???)) - case N => ??? - } // FIXME: Generate proper diagnostics. - } - // Resolve sealed types. + // Pass 1.2: Resolve signature types for collecting sealed derived types. println("Resolve sealed signature types") - hoistedScope.types.foreachEntry { - case _ -> (_: MixinSymbol | _: TypeAliasSymbol) => () - case (name, symbol) => symbol.defn.sig.foreach { unions => - def rec(acc: Ls[TypeName], ty: Type): Ls[TypeName] = ty match { - case tn: TypeName => tn :: acc - case AppliedType(tn: TypeName, _) => tn :: acc - case Union(lhs, tn: TypeName) => rec(tn :: acc, lhs) - case Union(lhs, AppliedType(tn: TypeName, _)) => rec(tn :: acc, lhs) - case other => println(s"Unknown type: $other"); ??? - } - val derivedTypes = try rec(Nil, unions) catch { case _: NotImplementedError => Nil } + typeSymbols.foreach { + case _: MixinSymbol | _: TypeAliasSymbol | _: ModuleSymbol => () + case symbol => symbol.defn.sig.foreach { unions => + val derivedTypes = try extractSignatureTypes(unions) catch { case _: NotImplementedError => Nil } symbol.sealedDerivedTypes = derivedTypes.flatMap { derivedType => - val maybeSymbol = hoistedScope.getTypeSymbol(derivedType.name) + val maybeSymbol = scopeWithTypes.getTypeSymbol(derivedType.name) if (maybeSymbol.isEmpty) raise(ErrorReport( msg"Undefined type $derivedType" -> derivedType.toLoc :: Nil, true, @@ -207,44 +162,38 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D println(s">>> $name: ${symbol.sealedDerivedTypes.iterator.map(_.name).mkString(", ")}") } } - println(hoistedScope.symbols.map(_.name).mkString("(hoisted) scope = {", ", ", "}")) - // - // Pass 2: Visit non-hoisted and build a complete scope. - val completeScope = statements.foldLeft[Scope](hoistedScope) { - case (acc, term: Term) => traverseTerm(term)(acc); acc - case (acc, defn: NuTypeDef) => acc - case (acc, defn @ NuFunDef(Some(rec), nme, _, _, L(rhs))) => - val symbol = new ValueSymbol(defn.nme, true) - val scopeWithVar = acc + symbol - traverseLetBinding(symbol, rec, rhs)(if (rec) { scopeWithVar } else { acc }) - scopeWithVar - case (acc, defn @ NuFunDef(Some(rec), nme, _, _, R(ty))) => - val symbol = new ValueSymbol(defn.nme, true) - acc + symbol - case (acc, _: NuFunDef) => acc - case (acc, _: Constructor | _: DataDefn | _: DatatypeDefn | _: Def | _: LetS | _: TypeDef) => ??? // TODO: When? + // Pass 2: Build a complete scope and collect definitional terms and terms to be traversed. + val (completeScope, thingsToTraverse) = statements.foldLeft[(Scope, Ls[(Term \/ DefinedTermSymbol, Scope)])](scopeWithTypes, Nil) { + case ((scope, acc), term: Term) => (scope, (L(term), scope) :: acc) + case ((scope, acc), defn: NuFunDef) => + val termSymbol = new DefinedTermSymbol(defn) + val scopeWithSymbol = scope + termSymbol + (scopeWithSymbol, (R(termSymbol), if (termSymbol.isRecursive) scopeWithSymbol else scope) :: acc) + case (acc, _: NuTypeDef) => acc // Ignore type definitions. + case (acc, other @ (_: Constructor | _: DataDefn | _: DatatypeDefn | _: Def | _: LetS | _: TypeDef)) => + println(s"unknown statement: ${other.getClass.getSimpleName}") + acc } - println(hoistedScope.symbols.map(_.name).mkString("(complete) scope = {", ", ", "}")) - // Pass 3: Visit hoisted symbols. - val visitedSymbol = MutSet.empty[FunctionSymbol] - completeScope.symbols.foreach { - case symbol: TypeSymbol => - val innerScope = symbol.defn.kind match { - case Cls => - completeScope.derive( - new ValueSymbol(Var("this"), false) :: - (symbol.defn.params match { - case N => Nil - case S(fields) => extractParameters(fields) - }) - ) - case Als | Mod | Mxn | Trt => completeScope - } - traverseTypeDefinition(symbol, symbol.defn)(innerScope) - case symbol: FunctionSymbol if !visitedSymbol(symbol) => - visitedSymbol += symbol - traverseFunction(symbol, symbol.defn)(completeScope) - case _: FunctionSymbol | _: ValueSymbol => () + println(thingsToTraverse.iterator.map { + case (L(term), _) => inspect.shallow(term) + case (R(symbol), _) => symbol.name + }.mkString("to be traversed: {", ", ", "}")) + // Pass 3: Traverse terms collected from the last pass. + println("Pass 3") + thingsToTraverse.foreach { + case (L(term), scope) => + println("traverseTerm: " + inspect.shallow(term)) + println("scope: " + scope.showLocalSymbols) + traverseTerm(term)(scope) + case (R(symbol), scope) => symbol.body match { + case L(term) => + if (symbol.isFunction) { + traverseTerm(term)(completeScope) + } else { + traverseTerm(term)(scope) + } + case R(_) => () + } } (completeScope, new TypeContents) }({ case (scope, contents) => s"traverseStatements ==> Scope {${scope.showLocalSymbols}}" }) @@ -270,28 +219,39 @@ object PreTyper { rec(Nil, parents) } - def transitiveClosure(graph: Map[Var, List[Var]]): Map[Var, List[Var]] = { - def dfs(vertex: Var, visited: Set[Var]): Set[Var] = { + def extractSignatureTypes(ty: Type): Ls[TypeName] = { + @tailrec + def rec(acc: Ls[TypeName], ty: Type): Ls[TypeName] = ty match { + case tn: TypeName => tn :: acc + case AppliedType(tn: TypeName, _) => tn :: acc + case Union(lhs, tn: TypeName) => rec(tn :: acc, lhs) + case Union(lhs, AppliedType(tn: TypeName, _)) => rec(tn :: acc, lhs) + case other => println(s"Unknown type in signature: $other"); ??? + } + rec(Nil, ty).reverse + } + + def transitiveClosure[A](graph: Map[A, List[A]]): Map[A, List[A]] = { + def dfs(vertex: A, visited: Set[A]): Set[A] = { if (visited.contains(vertex)) visited else graph.getOrElse(vertex, List()) .foldLeft(visited + vertex)((acc, v) => dfs(v, acc)) } - graph.keys.map { vertex => val closure = dfs(vertex, Set()) vertex -> (closure - vertex).toList }.toMap } - def printGraph(graph: Map[Var, List[Var]], print: (=> Any) => Unit, title: String, arrow: String): Unit = { + def printGraph(graph: Map[TypeSymbol, List[TypeSymbol]], print: (=> Any) => Unit, title: String, arrow: String): Unit = { print(s"• $title") if (graph.isEmpty) print(" + ") else graph.foreachEntry { (source, targets) => - print(s" + $source $arrow " + { + print(s" + ${source.name} $arrow " + { if (targets.isEmpty) s"{}" - else targets.mkString("{ ", ", ", " }") + else targets.iterator.map(_.name).mkString("{ ", ", ", " }") }) } } diff --git a/shared/src/main/scala/mlscript/pretyper/Scope.scala b/shared/src/main/scala/mlscript/pretyper/Scope.scala index 23e7113b..d6eae37c 100644 --- a/shared/src/main/scala/mlscript/pretyper/Scope.scala +++ b/shared/src/main/scala/mlscript/pretyper/Scope.scala @@ -34,9 +34,14 @@ final class Scope(val enclosing: Opt[Scope], val types: Map[Str, TypeSymbol], va @inline def +(sym: Symbol): Scope = sym match { + case symbol: ModuleSymbol => new Scope(enclosing, types + (symbol.name -> symbol), terms + (symbol.name -> symbol)) case symbol: TypeSymbol => new Scope(enclosing, types + (symbol.name -> symbol), terms) - case symbol @ FunctionSymbol(Var(name), S(Var(operator)), _) => - new Scope(enclosing, types, terms + (name -> symbol) + (operator -> symbol)) + case symbol: DefinedTermSymbol => + val newTerms = terms + (symbol.name -> symbol) + new Scope(enclosing, types, symbol.operatorAlias match { + case N => newTerms + case S(alias) => newTerms + (alias.name -> symbol) + }) case symbol: TermSymbol => new Scope(enclosing, types, terms + (symbol.name -> symbol)) } @@ -48,6 +53,7 @@ final class Scope(val enclosing: Opt[Scope], val types: Map[Str, TypeSymbol], va def withEntries(syms: IterableOnce[Var -> Symbol]): Scope = { val (newTypes, newTerms) = syms.iterator.foldLeft((types, terms)) { + case ((types, terms), (nme, symbol: ModuleSymbol)) => (types + (nme.name -> symbol), terms + (nme.name -> symbol)) case ((types, terms), (nme, symbol: TypeSymbol)) => (types + (nme.name -> symbol), terms) case ((types, terms), (nme, symbol: TermSymbol)) => (types, terms + (nme.name -> symbol)) } @@ -73,9 +79,10 @@ object Scope { symbols: IterableOnce[Symbol] ): (Map[Str, TypeSymbol], Map[Str, TermSymbol]) = symbols.iterator.foldLeft((z._1, z._2)) { + case ((types, terms), symbol: ModuleSymbol) => (types + (symbol.name -> symbol), terms + (symbol.name -> symbol)) case ((types, terms), symbol: TypeSymbol) => (types + (symbol.name -> symbol), terms) - case ((types, terms), symbol @ FunctionSymbol(Var(name), S(Var(operator)), _)) => - (types, terms + (name -> symbol) + (operator -> symbol)) + case ((types, terms), symbol: DefinedTermSymbol) => + (types, terms + (symbol.name -> symbol) ++ symbol.operatorAlias.map(_.name -> symbol)) case ((types, terms), symbol: TermSymbol) => (types, terms + (symbol.name -> symbol)) } @@ -92,11 +99,11 @@ object Scope { Scope.from( """true,false,document,window,typeof,toString,not,succ,log,discard,negate, |round,add,sub,mul,div,sqrt,lt,le,gt,ge,slt,sle,sgt,sge,length,concat,eq, - |ne,error,id,if,emptyArray,+,-,*,%,/,<,>,<=,>=,==,===,<>,&&,||""" + |ne,error,id,if,emptyArray,+,-,*,%,/,<,>,<=,>=,==,===,<>,&&,||,and""" .stripMargin .split(",") .iterator - .map(name => new ValueSymbol(Var(name), false)) + .map(name => new LocalTermSymbol(Var(name))) .concat(trueSymbol :: falseSymbol :: Nil) ) } diff --git a/shared/src/main/scala/mlscript/pretyper/Symbol.scala b/shared/src/main/scala/mlscript/pretyper/Symbol.scala index fc395a87..89db935a 100644 --- a/shared/src/main/scala/mlscript/pretyper/Symbol.scala +++ b/shared/src/main/scala/mlscript/pretyper/Symbol.scala @@ -1,13 +1,15 @@ package mlscript.pretyper import collection.mutable.{Buffer, Map => MutMap, Set => MutSet} -import mlscript.{Loc, NuFunDef, NuTypeDef, TypeName, Var} +import mlscript.{Loc, NuFunDef, NuTypeDef, Term, Type, TypeName, Var} import mlscript.{Cls, Trt, Mxn, Als, Mod} import mlscript.utils._, shorthands._ -import mlscript.ucs.context.{Context, ScrutineeData} +import mlscript.ucs.context.Matchable package object symbol { - sealed abstract class Symbol(val name: Str) { + sealed trait Symbol { + def name: Str + def typeSymbolOption: Opt[TypeSymbol] = this match { case symbol: TypeSymbol => S(symbol) case _ => N @@ -18,7 +20,11 @@ package object symbol { } } - sealed abstract class TypeSymbol(val defn: NuTypeDef) extends Symbol(defn.name) { + sealed trait TypeSymbol extends Symbol { + val defn: NuTypeDef + + override def name: Str = defn.name + def scope: Scope = ??? def contents: Map[Str, Symbol] = ??? @@ -30,61 +36,55 @@ package object symbol { @inline def hasSuperType(superType: TypeSymbol): Bool = baseTypes.exists(_ === superType) } - final class ClassSymbol(/* enclosingScope: Scope, */ defn: NuTypeDef) extends TypeSymbol(defn) { + object TypeSymbol { + def apply(defn: NuTypeDef): TypeSymbol = + defn.kind match { + case Cls => new ClassSymbol(defn) + case Als => new TypeAliasSymbol(defn) + case Mxn => new MixinSymbol(defn) + case Trt => new TraitSymbol(defn) + case Mod => new ModuleSymbol(defn) + } + def unapply(symbol: TypeSymbol): Opt[NuTypeDef] = S(symbol.defn) + } + + final class ClassSymbol(override val defn: NuTypeDef) extends TypeSymbol { require(defn.kind === Cls) - // lazy val (scope, contents) = (enclosingScope.derive, Map.empty[Str, Symbol]) } - final class TraitSymbol(/* enclosingScope: Scope, */ defn: NuTypeDef) extends TypeSymbol(defn) { + final class TraitSymbol(override val defn: NuTypeDef) extends TypeSymbol { require(defn.kind === Trt) - // lazy val (scope, contents) = (enclosingScope.derive, Map.empty[Str, Symbol]) } - final class MixinSymbol(/* enclosingScope: Scope, */ defn: NuTypeDef) extends TypeSymbol(defn) { + final class MixinSymbol(override val defn: NuTypeDef) extends TypeSymbol { require(defn.kind === Mxn) - // lazy val (scope, contents) = (enclosingScope.derive, Map.empty[Str, Symbol]) } - final class TypeAliasSymbol(/* enclosingScope: Scope, */ defn: NuTypeDef) extends TypeSymbol(defn) { + final class TypeAliasSymbol(override val defn: NuTypeDef) extends TypeSymbol { require(defn.kind === Als) - // lazy val (scope, contents) = (enclosingScope.derive, Map.empty[Str, Symbol]) } - final class ModuleSymbol(/* enclosingScope: Scope, */ defn: NuTypeDef) extends TypeSymbol(defn) { + final class ModuleSymbol(override val defn: NuTypeDef) extends TypeSymbol with TermSymbol { require(defn.kind === Mod) - // lazy val (scope, contents) = (enclosingScope.derive, Map.empty[Str, Symbol]) } - sealed abstract class TermSymbol(name: String) extends Symbol(name) { - private val scrutinees: MutMap[Context, ScrutineeData] = MutMap.empty - - def getOrCreateScrutinee(implicit context: Context): ScrutineeData = - scrutinees.getOrElseUpdate(context, context.freshScrutinee) + sealed trait TermSymbol extends Symbol with Matchable - def getScrutinee(implicit context: Context): Opt[ScrutineeData] = - scrutinees.get(context) + class DefinedTermSymbol(defn: NuFunDef) extends TermSymbol { + override def name: Str = defn.name - def isScrutinee(implicit context: Context): Bool = scrutinees.contains(context) + def body: Term \/ Type = defn.rhs - def addScrutinee(scrutinee: ScrutineeData)(implicit context: Context): Unit = { - require(!isScrutinee) - scrutinees += context -> scrutinee - } - } + def isFunction: Bool = defn.isLetRec.isEmpty - final class FunctionSymbol(val defn: NuFunDef) extends TermSymbol(defn.nme.name) { - require(defn.isLetRec.isEmpty) - val nme: Var = defn.nme - val operator: Opt[Var] = defn.symbolicNme - } + def isRecursive: Bool = defn.isLetRec.getOrElse(true) - object FunctionSymbol { - def unapply(symbol: TermSymbol): Opt[(Var, Opt[Var], NuFunDef)] = - symbol match { - case fs: FunctionSymbol => S(fs.nme, fs.operator, fs.defn) - case _ => N - } + def isDeclaration: Bool = defn.rhs.isRight + + def operatorAlias: Opt[Var] = defn.symbolicNme } - final class ValueSymbol(val nme: Var, val hoisted: Bool) extends TermSymbol(nme.name) + class LocalTermSymbol(val nme: Var) extends TermSymbol { + override def name: Str = nme.name + } } \ No newline at end of file diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index 4db6a025..7143660c 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -16,7 +16,7 @@ trait DesugarUCS extends Transformation with PostProcessing with CoverageChecking { self: PreTyper => - protected def freshSymbol(nme: Var): ValueSymbol = new ValueSymbol(nme, false) + protected def freshSymbol(nme: Var): LocalTermSymbol = new LocalTermSymbol(nme) /** Common operations of `Var` which can be shared within all stages. */ protected implicit class VarOps(nme: Var) { diff --git a/shared/src/main/scala/mlscript/ucs/context/Matchable.scala b/shared/src/main/scala/mlscript/ucs/context/Matchable.scala new file mode 100644 index 00000000..9d9d96b8 --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/context/Matchable.scala @@ -0,0 +1,39 @@ +package mlscript.ucs.context + +import collection.mutable.{Map => MutMap} +import mlscript.utils._, shorthands._ + +/** + * A trait for "things" (e.g., variables, functions, symbols, etc) that can be + * matched in a UCS term. This trait holds everything that is needed to track + * scrutinee information. + */ +trait Matchable { + /** + * A map from the context to the scrutinee associated with the symbol in the + * context. The reason why we need this map is that the same symbol may be + * matched in different UCS terms. Each context corresponds to a UCS term. + */ + private val scrutinees: MutMap[Context, ScrutineeData] = MutMap.empty + + /** + * Get the scrutinee associated with the symbol in the given context. If + * there's no scrutinee associated with the symbol, create a new one in the + * given context. **Note**: This function should only be called from the + * UCS desugarer. + */ + private[ucs] def getOrCreateScrutinee(implicit context: Context): ScrutineeData = + scrutinees.getOrElseUpdate(context, context.freshScrutinee) + + /** Get the scrutinee associated with the symbol in the given context. */ + def getScrutinee(implicit context: Context): Opt[ScrutineeData] = scrutinees.get(context) + + /** Check if the symbol is associated with a scrutinee in the given context. */ + def isScrutinee(implicit context: Context): Bool = scrutinees.contains(context) + + /** Associate the symbol with a scrutinee in the given context. */ + private[ucs] def addScrutinee(scrutinee: ScrutineeData)(implicit context: Context): Unit = { + require(!isScrutinee) // It should be impossible to add a scrutinee twice. + scrutinees += context -> scrutinee + } +} diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 64215a0e..52058db3 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -34,7 +34,7 @@ trait Desugaring { self: PreTyper => * * @param term the root of source abstrax syntax term, obtained from the * transformation stage - * @param context the scope is for resolving type symbols. The scope should + * @param scope the scope is for resolving type symbols. The scope should * contains a TypeSymbol for `true` literal. * @return the root of desugared core abstract syntax term */ @@ -126,7 +126,7 @@ trait Desugaring { self: PreTyper => next => c.Split.Let( rec = false, name = test, - term = mkBinOp(scrutinee, Var("=="), literal, true), + term = mkBinOp(scrutinee, Var("==="), literal, true), tail = c.Branch(test, truePattern, next) :: c.Split.Nil ) @@ -145,7 +145,7 @@ trait Desugaring { self: PreTyper => S(name.withFreshSymbol.withScrutinee(subScrutinee) -> N) case (S(parameterPattern @ (s.ClassPattern(_, _) | s.LiteralPattern(_) | s.TuplePattern(_))), index) => val subScrutineeVar = freshSubScrutinee(parentScrutineeVar, parentClassLikeSymbol.name, index) - val symbol = new ValueSymbol(subScrutineeVar, false) + val symbol = new LocalTermSymbol(subScrutineeVar) symbol.addScrutinee(classPattern.getParameter(index).withAlias(subScrutineeVar)) S(subScrutineeVar.withSymbol(symbol) -> S(parameterPattern)) case _ => ??? // Other patterns are not implemented yet. @@ -184,7 +184,7 @@ trait Desugaring { self: PreTyper => // duplicated bindings during normalization. lazy val unapp = classPattern.getUnappliedVar { val vari = makeUnappliedVar(scrutineeVar, pattern.nme) - vari.withSymbol(new ValueSymbol(vari, false)) + vari.withSymbol(new LocalTermSymbol(vari)) } val nestedPatterns = flattenClassParameters(scrutineeVar, patternClassSymbol, parameters) // First, handle bindings of parameters of the current class pattern. @@ -261,7 +261,7 @@ trait Desugaring { self: PreTyper => case (S(parameterPattern @ (s.ClassPattern(_, _) | s.LiteralPattern(_) | s.TuplePattern(_))), index) => val arity = fields.length val subScrutineeVar = freshSubScrutinee(parentScrutineeVar, s"Tuple$$$arity", index) - val symbol = new ValueSymbol(subScrutineeVar, false) + val symbol = new LocalTermSymbol(subScrutineeVar) symbol.addScrutinee(tuplePattern.getField(index).withAlias(subScrutineeVar)) S(subScrutineeVar.withSymbol(symbol) -> S(parameterPattern)) case _ => ??? @@ -286,7 +286,18 @@ trait Desugaring { self: PreTyper => case s.Split.Cons(head, tail) => head.pattern match { case s.AliasPattern(nme, pattern) => ??? - case s.LiteralPattern(literal) => ??? + case s.LiteralPattern(literal) => + val test = context.freshTest().withFreshSymbol + c.Split.Let( + rec = false, + name = test, + term = mkBinOp(scrutineeVar, Var("==="), literal, true), + tail = c.Branch( + scrutinee = test, + pattern = truePattern, + continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope + test.symbol, context) + ) :: rec(scrutineeVar, tail) + ) case s.ConcretePattern(nme) => val test = context.freshTest().withFreshSymbol c.Split.Let( @@ -303,7 +314,7 @@ trait Desugaring { self: PreTyper => desugarTermSplit(head.continuation)(PartialTerm.Empty, scope, context) ++ rec(scrutineeVar, tail) case s.NamePattern(nme) => // Create a symbol for the binding. - val symbol = new ValueSymbol(nme, false) + val symbol = new LocalTermSymbol(nme) // Share the scrutineeVar's symbol with its aliases. symbol.addScrutinee(scrutineeVar.getOrCreateScrutinee.withAlias(nme)) // Associate the symbol with the binding. diff --git a/shared/src/main/scala/mlscript/ucs/stages/package.scala b/shared/src/main/scala/mlscript/ucs/stages/package.scala index 0fa9fde4..0e091845 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/package.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/package.scala @@ -4,18 +4,4 @@ import mlscript.{Lit, Term, Var} import mlscript.pretyper.symbol._ import mlscript.utils._, shorthands._ -package object stages { - sealed abstract class CasePattern { - override def toString(): String = this match { - case CasePattern.Class(symbol) => symbol.name - case CasePattern.Boolean(value) => value.toString - case CasePattern.Literal(value) => value.toString - } - } - - object CasePattern { - final case class Class(symbol: TypeSymbol) extends CasePattern - final case class Boolean(value: Boolean) extends CasePattern - final case class Literal(value: Lit) extends CasePattern - } -} +package object stages diff --git a/shared/src/test/diff/mlscript/Repro.mls b/shared/src/test/diff/mlscript/Repro.mls index bf3a52e7..169d1c0b 100644 --- a/shared/src/test/diff/mlscript/Repro.mls +++ b/shared/src/test/diff/mlscript/Repro.mls @@ -1 +1,6 @@ -:PreTyper +:NewDefs + +fun test() = + let rec aux(n) = if n == 0 then 0 else aux(n - 1) + n + aux(100) +//│ fun test: () -> Int diff --git a/shared/src/test/diff/pretyper/ucs/DualOption.mls b/shared/src/test/diff/pretyper/ucs/DualOption.mls index d7a09632..a8bc582f 100644 --- a/shared/src/test/diff/pretyper/ucs/DualOption.mls +++ b/shared/src/test/diff/pretyper/ucs/DualOption.mls @@ -124,6 +124,7 @@ fun add_5(x, y) = y is None and x is None then 0 //│ fun add_5: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) + add_5(None, None) add_5(Some(5), None) add_5(None, Some(9)) @@ -147,6 +148,7 @@ fun add_6(x, y) = [None, None] then 0 //│ fun add_6: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) + add_6(None, None) add_6(Some(5), None) add_6(None, Some(9)) diff --git a/shared/src/test/diff/pretyper/ucs/examples/Calculator.mls b/shared/src/test/diff/pretyper/ucs/examples/Calculator.mls index 3b8df255..050ed0a9 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/Calculator.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/Calculator.mls @@ -59,13 +59,13 @@ class Cons[A](head: A, tail: List[A]) module Nil fun (::) cons(head, tail) = Cons(head, tail) fun reverse(xs) = - let aux(acc, xs) = + let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(x :: acc, xs') aux(Nil, xs) fun join(sep) = - let aux(acc, xs) = + let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(acc ++ sep ++ toString(x), xs') @@ -97,7 +97,7 @@ fun isBlank(n) = (n === 32) || (n === 9) || (n === 10) || (n === 13) //│ fun isBlank: Eql[10 | 13 | 32 | 9] -> Bool fun scanInt(text: StringOps, at: Int): Option[[Int, Int]] = - let aux(acc, i: Int): Option[[Int, Int]] = if + let rec aux(acc, i: Int): Option[[Int, Int]] = if i < 0 then None i >= text.length then mapOption(n => [n, i], acc) let c = text.charCodeAt(i) @@ -124,7 +124,7 @@ scanInt("a123" |> toStringOps, 4) |> showOption //│ = 'None' fun skipBlank(text: StringOps, at: Int): Int = - let aux(i: Int): Int = if + let rec aux(i: Int): Int = if i >= text.length then i isBlank(text.charCodeAt(i)) then aux(i + 1) else i @@ -222,7 +222,7 @@ scanToken(" 42" |> toStringOps, 0) fun tokenize(str: Str): List[Token] = let text = str |> toStringOps - let aux(acc, at) = + let rec aux(acc, at) = if scanToken(text, at) is [token, at'] and token is UnknownInput then (token :: acc) |> reverse EndOfInput then acc |> reverse @@ -334,7 +334,7 @@ fun parseAtom(ts: List[Token]): ParseResult[[Expression, List[Token]]] = fun parseExpression(bp: Int, ts: List[Token]): ParseResult[[Expression, List[Token]]] = if parseAtom(ts) is Some([leftmost, ts']) then - let aux(left, ts) = if ts is + let rec aux(left, ts) = if ts is Cons(BinaryOperator(op, bp', opAt), ts') and bp < bp' and parseExpression(bp', ts') is Some([right, ts'']) then diff --git a/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls b/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls new file mode 100644 index 00000000..8e0d2e29 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls @@ -0,0 +1,508 @@ +:PreTyper + +// Summon the underlying JavaScript `Object.is` function so that we can compare +// any objects. For example, functions do not conforms to `Eql` so we cannot +// compare them with `===` directly. But, we can use `Object.is`. +declare fun Object: nothing +let (=:=) objectEqual: (anything, anything) -> Bool = Object.is +fun (=/=) objectNotEqual(x, y) = not(x =:= y) +//│ let (=:=) objectEqual: (anything, anything) -> Bool +//│ fun (=/=) objectNotEqual: (anything, anything) -> Bool +//│ fun Object: nothing +//│ objectEqual +//│ = [Function: is] + +fun (!==) notEqual(x, y) = not(x === y) +fun (++) concatOp(a, b) = concat(a)(b) +declare fun parseInt: (Str, Int) -> Int +//│ fun (!==) notEqual: forall 'a. (Eql['a], 'a) -> Bool +//│ fun (++) concatOp: (Str, Str) -> Str +//│ fun parseInt: (Str, Int) -> Int + +// `List` and its utilities: +abstract class List[out T]: Cons[T] | Nil +class Cons[T](head: T, tail: List[T]) extends List[T] +module Nil extends List +fun (::) cons(head: 'T, tail: List['T]): List['T] = Cons(head, tail) +fun reverse(l: List['A]): List['A] = + let rec r(l', l) = if l is Cons(x, xs) then r(x :: l', xs) else l' + r(Nil, l) +fun join(sep: Str, xs: List['B]) = if xs is + Cons(x, Nil) then toString(x) + Cons(x, xs) then toString(x) ++ sep ++ join(sep, xs) + Nil then "" +fun showList(xs: List['C]) = "[" ++ join(", ", xs) ++ "]" +fun map(f: 'D -> 'E, xs: List['D]): List['E] = if xs is + Cons(x, xs) then f(x) :: map(f, xs) + Nil then Nil +fun equalList(xs: List['A], ys: List['A], equal: ('A, 'A) -> Bool): Bool = if xs is + Cons(x, xs') and ys is Cons(y, ys') then equal(x, y) and equalList(xs', ys', equal) + Nil and ys is Nil then true + else false +// `Option` and its utilities: +abstract class Option[A]: Some[A] | None +class Some[out A](value: A) extends Option[A] +module None extends Option +//│ abstract class List[T]: Cons[T] | Nil +//│ class Cons[T](head: T, tail: List[T]) extends List +//│ module Nil extends List +//│ fun (::) cons: forall 'T. (head: 'T, tail: List['T]) -> List['T] +//│ fun reverse: forall 'A. (l: List['A]) -> List['A] +//│ fun join: (sep: Str, xs: List[anything]) -> Str +//│ fun showList: (xs: List[anything]) -> Str +//│ fun map: forall 'D 'T0. (f: 'D -> 'T0, xs: List['D]) -> List['T0] +//│ fun equalList: forall 'A0. (xs: List['A0], ys: List['A0], equal: ('A0, 'A0) -> Bool) -> Bool +//│ abstract class Option[A]: None | Some[A] +//│ class Some[A](value: A) extends Option +//│ module None extends Option + +// _ ____ _____ +// / \ / ___|_ _| +// / _ \ \___ \ | | +// / ___ \ ___) || | +// /_/ \_\____/ |_| +// + +abstract class Expr: Lambda | BuiltIn | Instance | Thunk | StrLit | IntLit | ExprList +class Lambda(f: List[Data] -> Data) extends Expr +class BuiltIn(e: List[Data] -> Data) extends Expr +class Instance(n: Str, m: List[[Str, Expr]]) extends Expr +class Thunk(e: () -> Data) extends Expr +class StrLit(s: Str) extends Expr +class IntLit(n: Int) extends Expr +class ExprList(l: List[Expr]) extends Expr +fun (=@=) equalExpr(x: Expr, y: Expr): Bool = if x is + Lambda(f) and y is Lambda(g) then f =:= g + BuiltIn(f) and y is BuiltIn(g) then f =:= g + Instance(n, m) and y is Instance(n', m') then + let equalPair = ([k, v], [k', v']) => k =:= k' and equalExpr(v, v') + n === n' and equalList(m, m', equalPair) + Thunk(e) and y is Thunk(e') then e =:= e' + StrLit(s) and y is StrLit(s') then s === s' + IntLit(n) and y is IntLit(n') then n === n' + ExprList(l) and y is ExprList(l') then equalList(l, l', equalExpr) + else false +fun showExpr(e: Expr): Str = if e is + Lambda(f) then "" + BuiltIn(f) then "" + Instance(n, m) then "" + Thunk(e) then "" + // StrLit(s) then "str:" ++ s + // IntLit(n) then "int:" ++ toString(n) + StrLit(s) then s + IntLit(n) then toString(n) + ExprList(l) then showList(map(showExpr, l)) +abstract class Data: Literal | DataList | Symbol +class Literal(e: Expr) extends Data +class DataList(l: List[Data]) extends Data +class Symbol(s: Str) extends Data +fun (=#=) equalData(x: Data, y: Data): Bool = if x is + Literal(e) and y is Literal(e') then e =@= e' + DataList(l) and y is DataList(l') then equalList(l, l', equalData) + Symbol(s) and y is Symbol(s') then s === s' + else false +fun showData(d: Data): Str = if d is + Literal(e) then showExpr(e) + DataList(l) then "(" ++ join(", ", map(showData, l)) ++ ")" + // Symbol(s) then "sym:" ++ s + Symbol(s) then s +//│ abstract class Expr: BuiltIn | ExprList | Instance | IntLit | Lambda | StrLit | Thunk +//│ class Lambda(f: List[Data] -> Data) extends Expr +//│ class BuiltIn(e: List[Data] -> Data) extends Expr +//│ class Instance(n: Str, m: List[[Str, Expr]]) extends Expr +//│ class Thunk(e: () -> Data) extends Expr +//│ class StrLit(s: Str) extends Expr +//│ class IntLit(n: Int) extends Expr +//│ class ExprList(l: List[Expr]) extends Expr +//│ fun (=@=) equalExpr: (x: Expr, y: Expr) -> Bool +//│ fun showExpr: (e: Expr) -> Str +//│ abstract class Data: DataList | Literal | Symbol +//│ class Literal(e: Expr) extends Data +//│ class DataList(l: List[Data]) extends Data +//│ class Symbol(s: Str) extends Data +//│ fun (=#=) equalData: (x: Data, y: Data) -> Bool +//│ fun showData: (d: Data) -> Str + +// _____ _ _ +// |_ _|___ | | __ ___ _ __ (_) ____ ___ _ __ +// | | / _ \ | |/ // _ \| '_ \ | ||_ // _ \| '__| +// | || (_) || <| __/| | | || | / /| __/| | +// |_| \___/ |_|\_\\___||_| |_||_|/___|\___||_| +// + +type StringOps = { + length: Int, + charAt: Int => Str, + charCodeAt: Int => Int, + slice: Int => Str +} +declare fun String: nothing +let toStringOps: anything => StringOps = String +//│ type StringOps = {charAt: Int -> Str, charCodeAt: Int -> Int, length: Int, slice: Int -> Str} +//│ let toStringOps: anything -> StringOps +//│ fun String: nothing +//│ toStringOps +//│ = [Function: String] + +fun skipBlank(s: StringOps, i: Int): Int = + if + i < 0 then skipBlank(s, 0) + i >= s.length then s.length + s.charCodeAt(i) == 32 then skipBlank(s, i + 1) + else i +//│ fun skipBlank: (s: StringOps, i: Int) -> Int + +fun scanWhile(s: StringOps, i: Int, p: Str -> Bool): Option[[Str, Int]] = + let rec aux(acc, i) = + if i < s.length and s.charAt(i) is ch and p of ch then + aux(acc ++ ch, i + 1) + else + [acc, i] + if aux("", i) is + ["", _] then None + [acc, i] then Some([acc, i]) +//│ fun scanWhile: (s: StringOps, i: Int, p: Str -> Bool) -> Option[[Str, Int]] + +fun isDelimiter(ch) = (ch !== " ") && (ch !== "(") && (ch !== ")") +//│ fun isDelimiter: Eql[" " | "(" | ")"] -> Bool + +fun nextToken(s: StringOps, i: Int): Option[[Str, Int]] = + let i' = skipBlank(s, i) + if s.charAt(i') is + "(" then Some(["(", i' + 1]) + ")" then Some([")", i' + 1]) + else scanWhile(s, i', isDelimiter) +//│ fun nextToken: (s: StringOps, i: Int) -> Option[[Str, Int]] + +fun tokenize(s: Str): List[Str] = + let s' = toStringOps(s) + let rec aux(acc, i) = + if nextToken(s', i) is + None then reverse(acc) + Some([token, i']) then aux(token :: acc, i') + aux(Nil, 0) +//│ fun tokenize: (s: Str) -> List[Str] + +showList of tokenize("") +showList of tokenize("12") +showList of tokenize("x") +showList of tokenize("(quote (cons 1 nil))") +showList of tokenize("(+ 1 2)") +//│ Str +//│ res +//│ = '[]' +//│ res +//│ = '[12]' +//│ res +//│ = '[x]' +//│ res +//│ = '[(, quote, (, cons, 1, nil, ), )]' +//│ res +//│ = '[(, +, 1, 2, )]' + +// ____ +// | _ \ __ _ _ __ ___ ___ _ __ +// | |_) |/ _` || '__|/ __| / _ \| '__| +// | __/| (_| || | \__ \| __/| | +// |_| \__,_||_| |___/ \___||_| +// + +fun isDigit(n) = 48 <= n and n <= 57 +fun isDigits(s: Str): Bool = + let s' = toStringOps(s) + let rec aux(i) = + if i < s'.length and isDigit of s'.charCodeAt(i) then + aux(i + 1) + else + i === s'.length + aux(0) +isDigits("123") +isDigits("123jump") +isDigits("bruh") +//│ fun isDigit: Num -> Bool +//│ fun isDigits: (s: Str) -> Bool +//│ Bool +//│ res +//│ = true +//│ res +//│ = false +//│ res +//│ = false + +abstract class ParseResult[out A]: Success[A] | Failure +class Success[out A](value: A) extends ParseResult[A] +class Failure(error: Str) extends ParseResult +//│ abstract class ParseResult[A]: Failure | Success[A] +//│ class Success[A](value: A) extends ParseResult +//│ class Failure(error: Str) extends ParseResult + +// Notes +// ===== +// Sometimes, the precedence of comma is less than `of`. +// For example, in `[Success of DataList of reverse of acc, tail]`. +fun parseExpr(tokens: List[Str]): [ParseResult[Data], List[Str]] = + if tokens is + Cons("(", tail) then parseList(tail) + Cons(")", _) then [Failure("Unmatched closing parenthesis."), tokens] + Cons(token, tail) and + isDigits(token) then [Success(Literal(IntLit(parseInt(token, 10)))), tail] + else [Success(Symbol(token)), tail] + Nil then [Failure("Unexpected end of input, expect either `)` or more tokens."), Nil] +fun parseList(tokens: List[Str]): [ParseResult[DataList], List[Str]] = + let rec collect(acc, ts) = if ts is + Cons(")", tail) then [(Success of DataList of reverse of acc), tail] + Cons and parseExpr(ts) is + [Success(data), rest] then collect(data :: acc, rest) + [Failure(error), rest] then [Failure(error), rest] + Nil then [Failure("Unexpected end of input, expect either `)` or more tokens."), Nil] + collect(Nil, tokens) +//│ fun parseExpr: (tokens: List[Str]) -> [ParseResult[Data], List[Str]] +//│ fun parseList: (tokens: List[Str]) -> [ParseResult[DataList], List[Str]] + +fun showParse(source: Str): Str = + if parseExpr(tokenize(source)) is + [Success(data), _] then "Ok: " ++ showData(data) + [Failure(error), _] then "Error: " ++ error +//│ fun showParse: (source: Str) -> Str + +showParse("(cons 1 nil)") +showParse("(cons 1 (cons 2 nil))") +showParse("(cons 1 (cons 2 (cons 3 nil)))") +showParse("(+ 1 2)") +showParse("(car (cons 1 nil))") +//│ Str +//│ res +//│ = 'Ok: (cons, 1, nil)' +//│ res +//│ = 'Ok: (cons, 1, (cons, 2, nil))' +//│ res +//│ = 'Ok: (cons, 1, (cons, 2, (cons, 3, nil)))' +//│ res +//│ = 'Ok: (+, 1, 2)' +//│ res +//│ = 'Ok: (car, (cons, 1, nil))' + +// _____ +// | ____| _ __ __ __ +// | _| | '_ \\ \ / / +// | |___ | | | |\ V / +// |_____||_| |_| \_/ +// + +// As of the time I wrote this code, MLscript did not yet support term +// refinement, so I used lambda expression to implement `Env` first. +type Env[T] = Str -> T +let emptyEnv: Str -> nothing = _ => error +// The lookup function becomes useless because we can just call the `Env`. +fun lookup(env, name: Str): 'A = env(name) +//│ type Env[T] = Str -> T +//│ let emptyEnv: Str -> nothing +//│ fun lookup: forall 'A. (Str -> 'A, name: Str) -> 'A +//│ emptyEnv +//│ = [Function: emptyEnv1] + +// It is tricky to write an annotation that simplifies the inferred type. +fun (+:) extend(env, [name, expr]: [Str, 'A]) = + (name': Str) => if name' === name then expr else env(name') +//│ fun (+:) extend: forall 'A. (Str -> 'A, [Str, 'A]) -> (name': Str) -> 'A + +fun extendRec(env, name: Str, expr: (Str -> 'A) -> 'A) = + let rec env'(name': Str): 'A = if name' === name then expr(env') else env(name') + env' +//│ fun extendRec: forall 'A. (Str -> 'A, name: Str, expr: (Str -> 'A) -> 'A) -> (name': Str) -> 'A + +fun extendParameters(env: Str -> 'A, ps: List[Str], vs: List['A]) = + if [ps, vs] is + [Nil, Nil] then env + [Cons(p, ps'), Cons(v, vs')] then extendParameters(extend(env, [p, v]), ps', vs') + else error +//│ fun extendParameters: forall 'A. (env: Str -> 'A, ps: List[Str], vs: List['A]) -> Str -> 'A + +fun (++:) extendEntries(env, ps: List[[Str, 'A]]) = + if ps is + Nil then env + Cons(p, ps') then extendEntries(extend(env, p), ps') + else error +//│ fun (++:) extendEntries: forall 'A 'a. (Str -> 'A & 'a, ps: List[[Str, 'A]]) -> ((name': Str) -> 'A | 'a) + +let intEnv1 = extend(emptyEnv, ["one", 1]) +let intEnv2 = extend(intEnv1, ["two", 2]) +let intEnv3 = extend(intEnv2, ["three", 3]) +//│ let intEnv1: (name': Str) -> 1 +//│ let intEnv2: (name': Str) -> (1 | 2) +//│ let intEnv3: (name': Str) -> (1 | 2 | 3) +//│ intEnv1 +//│ = [Function (anonymous)] +//│ intEnv2 +//│ = [Function (anonymous)] +//│ intEnv3 +//│ = [Function (anonymous)] + +// Make a tuple to save some lines. +[intEnv1("one"), intEnv2("one"), intEnv3("one"), intEnv2("two"), intEnv3("two"), intEnv3("three")] +//│ [1, 1 | 2, 1 | 2 | 3, 1 | 2, 1 | 2 | 3, 1 | 2 | 3] +//│ res +//│ = [ 1, 1, 1, 2, 2, 3 ] + +:re +intEnv1("two") +intEnv2("three") +intEnv3("bruh") +//│ 1 | 2 | 3 +//│ res +//│ Runtime error: +//│ Error: an error was thrown +//│ res +//│ Runtime error: +//│ Error: an error was thrown +//│ res +//│ Runtime error: +//│ Error: an error was thrown + +let intEnv6 = intEnv3 ++: (["four", 4] :: ["five", 5] :: ["six", 6] :: Nil) +//│ let intEnv6: (name': Str) -> (1 | 2 | 3 | 4 | 5 | 6) +//│ intEnv6 +//│ = [Function (anonymous)] + +[intEnv6("one"), intEnv6("two"), intEnv6("three"), intEnv6("four"), intEnv6("five"), intEnv6("six")] +//│ [1 | 2 | 3 | 4 | 5 | 6, 1 | 2 | 3 | 4 | 5 | 6, 1 | 2 | 3 | 4 | 5 | 6, 1 | 2 | 3 | 4 | 5 | 6, 1 | 2 | 3 | 4 | 5 | 6, 1 | 2 | 3 | 4 | 5 | 6] +//│ res +//│ = [ 1, 2, 3, 4, 5, 6 ] + +:re +intEnv6("seven") +//│ 1 | 2 | 3 | 4 | 5 | 6 +//│ res +//│ Runtime error: +//│ Error: an error was thrown + +fun builtinEq(args: List[Data]): Data = if args is + Cons(x, Cons(y, Nil)) and + x =#= y then Literal(IntLit(1)) + else Literal(IntLit(0)) + else error +fun builtinAdd(args: List[Data]): Data = if args is + Cons(Literal(IntLit(x)), Cons(Literal(IntLit(y)), Nil)) then Literal(IntLit(x + y)) + else error +fun builtinSub(args: List[Data]): Data = if args is + Cons(Literal(IntLit(x)), Cons(Literal(IntLit(y)), Nil)) then Literal(IntLit(x - y)) + else error +fun builtinMul(args: List[Data]): Data = if args is + Cons(Literal(IntLit(x)), Cons(Literal(IntLit(y)), Nil)) then Literal(IntLit(x * y)) + else error +let builtinNil = DataList(Nil) +fun builtinCons(args: List[Data]): Data = if args is + Cons(x, Cons(DataList(xs), Nil)) then DataList(x :: xs) + else error +fun builtinCar(args: List[Data]): Data = if args is + Cons(DataList(Cons(x, _)), Nil) then x + else error +fun builtinCdr(args: List[Data]): Data = if args is + Cons(DataList(Cons(_, xs)), Nil) then DataList(xs) + else error +fun builtinNull(args: List[Data]): Data = if args is + Cons(DataList(Nil), Nil) then Literal(IntLit(1)) + else Literal(IntLit(0)) +//│ fun builtinEq: (args: List[Data]) -> Data +//│ fun builtinAdd: (args: List[Data]) -> Data +//│ fun builtinSub: (args: List[Data]) -> Data +//│ fun builtinMul: (args: List[Data]) -> Data +//│ let builtinNil: DataList +//│ fun builtinCons: (args: List[Data]) -> Data +//│ fun builtinCar: (args: List[Data]) -> Data +//│ fun builtinCdr: (args: List[Data]) -> Data +//│ fun builtinNull: (args: List[Data]) -> Data +//│ builtinNil +//│ = DataList {} + +let globalEnv = emptyEnv ++: ( + ["eq", Literal(Lambda(builtinEq))] :: ["+", Literal(Lambda(builtinAdd))] :: + ["-", Literal(Lambda(builtinSub))] :: ["*", Literal(Lambda(builtinMul))] :: + ["nil", builtinNil] :: ["cons", Literal(Lambda(builtinCons))] :: + ["car", Literal(Lambda(builtinCar))] :: ["cdr", Literal(Lambda(builtinCdr))] :: + ["null", Literal(Lambda(builtinNull))] :: Nil) +//│ let globalEnv: (name': Str) -> (DataList | Literal) +//│ globalEnv +//│ = [Function (anonymous)] + +fun toName(x: Data): Str = if x is Symbol(s) then s else error +//│ fun toName: (x: Data) -> Str + +fun asList(x: Data): List[Data] = if x is DataList(ys) then ys else error +//│ fun asList: (x: Data) -> List[Data] + +fun mkLambda(ps: List[Str], body: Data, env: Str -> Data) = + Literal(Lambda(args => eval(body, extendParameters(env, ps, args)))) +fun eval(x: Data, env: Str -> Data): Data = if x is + Literal(StrLit(x)) then Literal(StrLit(x)) + Literal(IntLit(x)) then Literal(IntLit(x)) + Symbol(name) then env(name) + DataList(Cons(Symbol("val"), tail)) and tail is + Cons(param, Cons(expr, Cons(rest, Nil))) then + eval(rest, env +: [toName(param), eval(expr, env)]) + else error + DataList(Cons(Symbol("def"), Cons(param, Cons(body, Cons(rest, Nil))))) then + let env' = extendRec of env, toName(param), (env => eval(body, env)) + eval(rest, env') + DataList(Cons(Symbol("if"), Cons(cond, Cons(thenPart, Cons(elsePart, Nil))))) and + eval(cond, env) is Literal(IntLit(0)) then eval(elsePart, env) + else eval(thenPart, env) + DataList(Cons(Symbol("quote"), Cons(y, Nil))) then y + DataList(Cons(Symbol("lambda"), Cons(params, Cons(body, Nil)))) then + mkLambda(map(toName, asList(params)), body, env) + DataList(Cons(operator, operands)) and eval(operator, env) is + Literal(Lambda(f)) then + f of map((x) => eval(x, env), operands) + else error // application of a non-function + else error // unrecognized program +//│ fun mkLambda: (ps: List[Str], body: Data, env: Str -> Data) -> Literal +//│ fun eval: (x: Data, env: Str -> Data) -> Data + +fun showEval(source: Str): Str = + if parseExpr(tokenize(source)) is + [Success(data), _] then "Ok: " ++ showData of eval(data, globalEnv) + [Failure(error), _] then "Error: " ++ error +//│ fun showEval: (source: Str) -> Str + +showEval("1") +showEval("(+ 1 2)") +showEval("(val x 7 (val y 6 (* x y)))") +showEval("(quote x)") +showEval("(cons 1 nil)") +showEval("(car (cons 1 nil))") +showEval("(null 0)") +showEval("(null nil)") +showEval("(def id (lambda (x) x) (id 42))") +//│ Str +//│ res +//│ = 'Ok: 1' +//│ res +//│ = 'Ok: 3' +//│ res +//│ = 'Ok: 42' +//│ res +//│ = 'Ok: x' +//│ res +//│ = 'Ok: (1)' +//│ res +//│ = 'Ok: 1' +//│ res +//│ = 'Ok: 0' +//│ res +//│ = 'Ok: 1' +//│ res +//│ = 'Ok: 42' + +showEval("(def fact (lambda (n) (if (eq n 0) 1 (* n (fact (- n 1))))) (fact 5))") +showEval("(def fib (lambda (n) (if (eq n 0) 0 (if (eq n 1) 1 (+ (fib (- n 1)) (fib (- n 2)))))) (fib 10))") +showEval("(def sum (lambda (n) (if (eq n 0) 0 (+ n (sum (- n 1))))) (sum 50))") +showEval("(def ack (lambda (m n) (if (eq m 0) (+ n 1) (if (eq n 0) (ack (- m 1) 1) (ack (- m 1) (ack m (- n 1)))))) (ack 3 2))") +//│ Str +//│ res +//│ = 'Ok: 120' +//│ res +//│ = 'Ok: 55' +//│ res +//│ = 'Ok: 1275' +//│ res +//│ = 'Ok: 29' diff --git a/shared/src/test/diff/pretyper/ucs/examples/ListFold.mls b/shared/src/test/diff/pretyper/ucs/examples/ListFold.mls index bde6e1b4..fccc64cc 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/ListFold.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/ListFold.mls @@ -23,7 +23,7 @@ let oneTwoThree = 1 :: 2 :: 3 :: Nil // Note that JavaScript doesn't have tail call optimization. Therefore, this // implementation is still inefficient in practice. fun join(sep) = - let aux(acc, xs) = + let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(acc ++ sep ++ toString(x), xs') @@ -56,7 +56,7 @@ fun (:::) appendAll(xs, ys) = //│ = '[1, 2, 3, 4, 5, 6]' fun reverse(xs) = - let aux(acc, xs) = + let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(x :: acc, xs') @@ -79,12 +79,12 @@ reverse(1 :: 2 :: 3 :: Nil) |> showList // fun foldLeft(f)(z) = - let aux(acc, xs) = + let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(f(acc, x), xs') (xs) => aux(z, xs) -//│ fun foldLeft: forall 'a 'b 'c. (('a, 'b) -> ('a & 'c)) -> ('a & 'c) -> (Cons['b] | Nil) -> 'c +//│ fun foldLeft: forall 'a 'b. (('a, 'b) -> 'a) -> 'a -> (Cons['b] | Nil) -> 'a let sum = foldLeft((acc, x) => acc + x)(0) sum(Nil) @@ -143,7 +143,7 @@ reverse'(1 :: 2 :: 3 :: Nil) |> showList // |___/ fun foldRight(f)(z) = - let aux(acc, xs) = + let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then f(x, aux(acc, xs')) diff --git a/shared/src/test/diff/pretyper/ucs/examples/Option.mls b/shared/src/test/diff/pretyper/ucs/examples/Option.mls index 70756855..c7f9c942 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/Option.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/Option.mls @@ -20,7 +20,7 @@ module None extends Option[nothing] { //│ fun filter: (p: T -> Bool) -> Option[T] //│ } //│ class Some[T](value: T) extends Option { -//│ fun filter: (T -> Bool) -> (None | Some[T]) +//│ fun filter: (T -> Object) -> (None | Some[T]) //│ } //│ module None extends Option { //│ fun filter: anything -> None diff --git a/shared/src/test/diff/pretyper/ucs/examples/Permutations.mls b/shared/src/test/diff/pretyper/ucs/examples/Permutations.mls index 0497d873..1cad991c 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/Permutations.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/Permutations.mls @@ -15,13 +15,13 @@ fun (:::) appendAll(xs: List['A], ys: List['A]): List['A] = Cons(x, xs') then x :: (xs' ::: ys) fun (:+) append(xs, x): List['A] = xs ::: (x :: Nil) fun reverse(xs) = - let aux(acc, xs) = + let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(x :: acc, xs') aux(Nil, xs) fun join(sep) = - let aux(acc, xs) = + let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(acc ++ sep ++ toString(x), xs') @@ -31,7 +31,7 @@ fun join(sep) = Nil then "" fun showList(xs) = "[" ++ join(", ")(xs) ++ "]" fun foldLeft(f)(z) = - let aux(acc, xs) = + let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(f(acc, x), xs') @@ -50,19 +50,23 @@ fun showListList(xs) = "[" ++ join(", ")(map(showList, xs)) ++ "]" //│ fun reverse: (Cons[anything] | Nil) -> (Cons[nothing] | Nil) //│ fun join: Str -> (Cons[anything] | Nil) -> Str //│ fun showList: (Cons[anything] | Nil) -> Str -//│ fun foldLeft: forall 'a 'b 'c. (('a, 'b) -> ('a & 'c)) -> ('a & 'c) -> (Cons['b] | Nil) -> 'c -//│ fun map: forall 'd 'T2. ('d -> 'T2, Cons['d] | Nil) -> (Cons['T2] | Nil) +//│ fun foldLeft: forall 'a 'b. (('a, 'b) -> 'a) -> 'a -> (Cons['b] | Nil) -> 'a +//│ fun map: forall 'c 'T2. ('c -> 'T2, Cons['c] | Nil) -> (Cons['T2] | Nil) //│ fun showListList: (Cons[Cons[anything] | Nil] | Nil) -> Str fun insertAllPositions(z) = - let aux(prev, acc, xs) = + let rec aux(prev, acc, xs) = if xs is Nil then ((prev :+ z) :: acc) |> reverse Cons(head, tail) then let nu = ((prev :+ z) ::: xs) aux(prev :+ head, nu :: acc, tail) xs => aux(Nil, Nil, xs) -//│ fun insertAllPositions: forall 'a. 'a -> (forall 'A. (Cons['A] | Nil) -> (Cons[List['a | 'A]] & {Cons#T <: List['a | 'A]} | Nil)) +//│ fun insertAllPositions: forall 'a. 'a -> (forall 'A 'A0. (Cons['A & 'A0] | Nil) -> (Cons[List['A0 | 'a]] & {Cons#T <: List['A0 | 'a]} | Nil)) +//│ where +//│ 'A <: 'A0 +//│ 'A0 :> 'a +//│ <: 'A insertAllPositions(0)(1 :: 2 :: 3 :: Nil) |> showListList //│ Str @@ -74,7 +78,7 @@ fun permutations(xs) = Nil then Nil Cons(x, Nil) then (x :: Nil) :: Nil Cons(x, xs') then foldLeft((acc, ys) => acc ::: insertAllPositions(x)(ys))(Nil)(permutations(xs')) -//│ fun permutations: forall 'T 'A. (Cons['T] | Nil) -> (Cons[Cons['T]] | List[List['T & 'A]] | Nil) +//│ fun permutations: forall 'T 'A. (Cons['T] | Nil) -> (Cons[Cons['T]] | List[List['A]] | Nil) //│ where //│ 'T <: 'A //│ 'A := 'T diff --git a/shared/src/test/diff/pretyper/ucs/examples/ULC.mls b/shared/src/test/diff/pretyper/ucs/examples/ULC.mls index cc1c507f..357d5811 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/ULC.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/ULC.mls @@ -64,7 +64,7 @@ fun (::) cons(head, tail) = Cons(head, tail) //│ fun (::) cons: forall 'A. ('A, List['A]) -> Cons['A] fun join(sep) = - let aux(acc, xs) = + let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(acc ++ sep ++ toString(x), xs') @@ -128,7 +128,7 @@ exclude("x" :: "y" :: "z" :: Nil, "w") |> showList //│ = '[x, y, z]' fun reverse(xs: List['B]): List['B] = - let aux(acc: List['B], ys: List['B]): List['B] = + let rec aux(acc: List['B], ys: List['B]): List['B] = if ys is Nil then acc Cons(y, ys') then aux(Cons(y, acc), ys') @@ -321,7 +321,7 @@ fun search(f: 'A -> Option['B], xs: List['A]): Option['B] = //│ 'B <: 'A0 // The removal of type annotations will cause running out of fuel. -fun combinations(n: Int, acc: List['A], alphabet: List['A], xs: List[Str]): Option[Str] = +fun combinations(n: Int, acc: List['T], alphabet: List['T], xs: List[Str]): Option[Str] = if n <= 0 and let x = reverse(acc) |> join("") @@ -329,11 +329,11 @@ fun combinations(n: Int, acc: List['A], alphabet: List['A], xs: List[Str]): Opti else Some(x) else search((x) => combinations(n - 1, x :: acc, alphabet, xs), alphabet) -//│ fun combinations: forall 'A 'A0 'A1. (n: Int, acc: List['A], alphabet: List['A1], xs: List[Str]) -> Option[Str] +//│ fun combinations: forall 'T 'A 'T0. (n: Int, acc: List['T], alphabet: List['T0], xs: List[Str]) -> Option[Str] //│ where -//│ 'A1 <: 'A & 'A0 -//│ 'A :> 'A0 -//│ 'A0 := 'A +//│ 'T0 <: 'T & 'A +//│ 'T :> 'A +//│ 'A := 'T combinations(1, Nil, 1 :: 2 :: 3 :: Nil, Nil) |> showOption combinations(2, Nil, 1 :: 2 :: 3 :: Nil, Nil) |> showOption @@ -357,7 +357,7 @@ combinations(3, Nil, 1 :: 2 :: 3 :: Nil, "111" :: "112" :: "113" :: "121" :: Nil fun freshVar(t: Term): Str = let fvs = freeVars(t) - let aux(n: Int): Str = + let rec aux(n: Int): Str = if combinations(n, Nil, alphabet, fvs) is Some(x) then x None then aux(n + 1) @@ -484,7 +484,7 @@ toString of stepByValue of App(Abs(Var("x"), Var("x")), Abs(Var("y"), Var("y"))) // fun eval(step) = - let aux(t) = + let rec aux(t) = if step(t) is result and result is Stepped(_, t') then aux(t') else result diff --git a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls index 990af73c..4bec2657 100644 --- a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls +++ b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls @@ -10,7 +10,7 @@ class Pair[A, B](x: A, y: B) //│ class Pair[A, B](x: A, y: B) fun f(x) = if x is Some(1) then true else false -//│ fun f: (Object & ~#Some | Some[Num]) -> Bool +//│ fun f: (Object & ~#Some | Some[Eql[1]]) -> Bool fun g(x) = if x then 1 else 2 //│ fun g: Bool -> (1 | 2) From f52701cd5ade1ff96e44bf7b0db17d38e5f4aab9 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Fri, 29 Dec 2023 16:14:53 +0800 Subject: [PATCH 035/143] Improve string type definitions in the LispInterpreter example --- shared/src/test/diff/mlscript/Repro.mls | 7 +- .../pretyper/ucs/examples/LispInterpreter.mls | 83 ++++++++++++------- 2 files changed, 56 insertions(+), 34 deletions(-) diff --git a/shared/src/test/diff/mlscript/Repro.mls b/shared/src/test/diff/mlscript/Repro.mls index 169d1c0b..bf3a52e7 100644 --- a/shared/src/test/diff/mlscript/Repro.mls +++ b/shared/src/test/diff/mlscript/Repro.mls @@ -1,6 +1 @@ -:NewDefs - -fun test() = - let rec aux(n) = if n == 0 then 0 else aux(n - 1) + n - aux(100) -//│ fun test: () -> Int +:PreTyper diff --git a/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls b/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls index 8e0d2e29..2dc1c106 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls @@ -12,11 +12,52 @@ fun (=/=) objectNotEqual(x, y) = not(x =:= y) //│ objectEqual //│ = [Function: is] +type NStr = Str & { + length: Int, + at: Int -> NStr, + charAt: Int -> NStr, + charCodeAt: Int -> Int, + slice: (Int, Int) -> NStr, + startsWith: Str -> Bool, + endsWith: Str -> Bool, + split: Str -> Array[NStr], + trim: () -> NStr, + trimStart: () -> NStr, + trimEnd: () -> NStr, + padStart: (Int, Str) -> NStr, + padEnd: (Int, Str) -> NStr, + repeat: Int -> NStr, + indexOf: Str -> Int, + lastIndexOf: Str -> Int, + includes: Str -> Bool, +} +declare fun String: anything -> NStr +fun (++) strcat(a, b) = String of concat(a)(b) +//│ type NStr = Str & { +//│ at: Int -> NStr, +//│ charAt: Int -> NStr, +//│ charCodeAt: Int -> Int, +//│ endsWith: Str -> Bool, +//│ includes: Str -> Bool, +//│ indexOf: Str -> Int, +//│ lastIndexOf: Str -> Int, +//│ length: Int, +//│ padEnd: (Int, Str) -> NStr, +//│ padStart: (Int, Str) -> NStr, +//│ repeat: Int -> NStr, +//│ slice: (Int, Int) -> NStr, +//│ split: Str -> Array[NStr], +//│ startsWith: Str -> Bool, +//│ trim: () -> NStr, +//│ trimEnd: () -> NStr, +//│ trimStart: () -> NStr +//│ } +//│ fun (++) strcat: (Str, Str) -> NStr +//│ fun String: anything -> NStr + fun (!==) notEqual(x, y) = not(x === y) -fun (++) concatOp(a, b) = concat(a)(b) declare fun parseInt: (Str, Int) -> Int //│ fun (!==) notEqual: forall 'a. (Eql['a], 'a) -> Bool -//│ fun (++) concatOp: (Str, Str) -> Str //│ fun parseInt: (Str, Int) -> Int // `List` and its utilities: @@ -48,8 +89,8 @@ module None extends Option //│ module Nil extends List //│ fun (::) cons: forall 'T. (head: 'T, tail: List['T]) -> List['T] //│ fun reverse: forall 'A. (l: List['A]) -> List['A] -//│ fun join: (sep: Str, xs: List[anything]) -> Str -//│ fun showList: (xs: List[anything]) -> Str +//│ fun join: (sep: Str, xs: List[anything]) -> (Str | NStr) +//│ fun showList: (xs: List[anything]) -> NStr //│ fun map: forall 'D 'T0. (f: 'D -> 'T0, xs: List['D]) -> List['T0] //│ fun equalList: forall 'A0. (xs: List['A0], ys: List['A0], equal: ('A0, 'A0) -> Bool) -> Bool //│ abstract class Option[A]: None | Some[A] @@ -130,29 +171,15 @@ fun showData(d: Data): Str = if d is // |_| \___/ |_|\_\\___||_| |_||_|/___|\___||_| // -type StringOps = { - length: Int, - charAt: Int => Str, - charCodeAt: Int => Int, - slice: Int => Str -} -declare fun String: nothing -let toStringOps: anything => StringOps = String -//│ type StringOps = {charAt: Int -> Str, charCodeAt: Int -> Int, length: Int, slice: Int -> Str} -//│ let toStringOps: anything -> StringOps -//│ fun String: nothing -//│ toStringOps -//│ = [Function: String] - -fun skipBlank(s: StringOps, i: Int): Int = +fun skipBlank(s: NStr, i: Int): Int = if i < 0 then skipBlank(s, 0) i >= s.length then s.length s.charCodeAt(i) == 32 then skipBlank(s, i + 1) else i -//│ fun skipBlank: (s: StringOps, i: Int) -> Int +//│ fun skipBlank: (s: NStr, i: Int) -> Int -fun scanWhile(s: StringOps, i: Int, p: Str -> Bool): Option[[Str, Int]] = +fun scanWhile(s: NStr, i: Int, p: Str -> Bool): Option[[Str, Int]] = let rec aux(acc, i) = if i < s.length and s.charAt(i) is ch and p of ch then aux(acc ++ ch, i + 1) @@ -161,21 +188,21 @@ fun scanWhile(s: StringOps, i: Int, p: Str -> Bool): Option[[Str, Int]] = if aux("", i) is ["", _] then None [acc, i] then Some([acc, i]) -//│ fun scanWhile: (s: StringOps, i: Int, p: Str -> Bool) -> Option[[Str, Int]] +//│ fun scanWhile: (s: NStr, i: Int, p: Str -> Bool) -> Option[[Str, Int]] -fun isDelimiter(ch) = (ch !== " ") && (ch !== "(") && (ch !== ")") +fun isDelimiter(ch) = ch !== " " and ch !== "(" and ch !== ")" //│ fun isDelimiter: Eql[" " | "(" | ")"] -> Bool -fun nextToken(s: StringOps, i: Int): Option[[Str, Int]] = +fun nextToken(s: NStr, i: Int): Option[[Str, Int]] = let i' = skipBlank(s, i) if s.charAt(i') is "(" then Some(["(", i' + 1]) ")" then Some([")", i' + 1]) else scanWhile(s, i', isDelimiter) -//│ fun nextToken: (s: StringOps, i: Int) -> Option[[Str, Int]] +//│ fun nextToken: (s: NStr, i: Int) -> Option[[Str, Int]] fun tokenize(s: Str): List[Str] = - let s' = toStringOps(s) + let s' = String(s) let rec aux(acc, i) = if nextToken(s', i) is None then reverse(acc) @@ -188,7 +215,7 @@ showList of tokenize("12") showList of tokenize("x") showList of tokenize("(quote (cons 1 nil))") showList of tokenize("(+ 1 2)") -//│ Str +//│ NStr //│ res //│ = '[]' //│ res @@ -209,7 +236,7 @@ showList of tokenize("(+ 1 2)") fun isDigit(n) = 48 <= n and n <= 57 fun isDigits(s: Str): Bool = - let s' = toStringOps(s) + let s' = String(s) let rec aux(i) = if i < s'.length and isDigit of s'.charCodeAt(i) then aux(i + 1) From 014923ca9476e29f996d561a18757e5fd40f34da Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Fri, 29 Dec 2023 21:17:03 +0800 Subject: [PATCH 036/143] Fix a typo in a comment Co-authored-by: Lionel Parreaux --- shared/src/main/scala/mlscript/JSBackend.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/src/main/scala/mlscript/JSBackend.scala b/shared/src/main/scala/mlscript/JSBackend.scala index 75e2aa84..3ca22699 100644 --- a/shared/src/main/scala/mlscript/JSBackend.scala +++ b/shared/src/main/scala/mlscript/JSBackend.scala @@ -1594,7 +1594,7 @@ object JSBackend { def isSafeInteger(value: BigInt): Boolean = MinimalSafeInteger <= value && value <= MaximalSafeInteger - // Temporary measurement until we adopt the new tuple index. + // Temporary measure until we adopt the new tuple index. object TupleIndex { def unapply(fieldName: Var): Opt[Int] = { val name = fieldName.name From 4623fb8018f65c1132eaa759868218c7f5fcc08c Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sat, 30 Dec 2023 02:33:23 +0800 Subject: [PATCH 037/143] Fix symbol duplication in code generation for new definition typing Please `git cherrypick` this to a separate PR. --- .../src/main/scala/mlscript/JSBackend.scala | 46 ++++---- .../main/scala/mlscript/codegen/Scope.scala | 30 +++-- .../main/scala/mlscript/codegen/Symbol.scala | 20 +++- .../diff/codegen/AuxiliaryConstructors.mls | 8 +- .../src/test/diff/codegen/ConstructorStmt.mls | 10 +- shared/src/test/diff/codegen/Nested.mls | 36 +++--- shared/src/test/diff/codegen/New.mls | 4 +- shared/src/test/diff/codegen/NewDefsBug.mls | 72 ++++++++++++ shared/src/test/diff/codegen/NewMatching.mls | 2 +- shared/src/test/diff/codegen/NuReplHost.mls | 4 +- shared/src/test/diff/codegen/SymbolicOps.mls | 10 +- shared/src/test/diff/mlscript/Sequence.mls | 2 +- shared/src/test/diff/nu/ArrayProg.mls | 2 +- shared/src/test/diff/nu/CtorSubtraction.mls | 4 +- shared/src/test/diff/nu/FlatIndentFuns.mls | 4 +- shared/src/test/diff/nu/FlatMonads_repro.mls | 2 +- shared/src/test/diff/nu/HeungTung.mls | 2 +- shared/src/test/diff/nu/LamPatterns.mls | 4 +- shared/src/test/diff/nu/LocalLets.mls | 2 +- shared/src/test/diff/nu/MIscPoly.mls | 2 +- shared/src/test/diff/nu/Misc.mls | 14 +-- shared/src/test/diff/nu/NamedArgs.mls | 10 +- shared/src/test/diff/nu/NestedRecords.mls | 2 +- shared/src/test/diff/nu/NewNew.mls | 2 +- shared/src/test/diff/nu/Res.mls | 2 +- shared/src/test/diff/nu/Vals.mls | 2 +- .../pretyper/ucs/examples/LispInterpreter.mls | 2 +- .../diff/pretyper/ucs/examples/Option.mls | 106 ++++++++++++++++-- 28 files changed, 298 insertions(+), 108 deletions(-) create mode 100644 shared/src/test/diff/codegen/NewDefsBug.mls diff --git a/shared/src/main/scala/mlscript/JSBackend.scala b/shared/src/main/scala/mlscript/JSBackend.scala index 3ca22699..34b26f46 100644 --- a/shared/src/main/scala/mlscript/JSBackend.scala +++ b/shared/src/main/scala/mlscript/JSBackend.scala @@ -10,6 +10,15 @@ import scala.util.chaining._ abstract class JSBackend(allowUnresolvedSymbols: Bool) { def oldDefs: Bool + + protected implicit class TermOps(term: Term) { + def isLam: Bool = term match { + case _: Lam => true + case Bra(false, inner) => inner.isLam + case Asc(inner, _) => inner.isLam + case _ => false + } + } /** * The root scope of the program. @@ -240,7 +249,7 @@ abstract class JSBackend(allowUnresolvedSymbols: Bool) { case (t: Term, index) => JSExprStmt(translateTerm(t)(blkScope)) case (NuFunDef(isLetRec, Var(nme), symNme, _, L(rhs)), _) => val symb = symNme.map(_.name) - val isLocalFunction = isLetRec.isEmpty || (rhs match { case _: Lam => true; case _ => false }) + val isLocalFunction = isLetRec.isEmpty || rhs.isLam val pat = blkScope.declareValue(nme, isLetRec, isLocalFunction, symb) JSLetDecl(Ls(pat.runtimeName -> S(translateTerm(rhs)(blkScope)))) case (nt: NuTypeDef, _) => translateLocalNewType(nt)(blkScope) @@ -1170,18 +1179,17 @@ class JSWebBackend extends JSBackend(allowUnresolvedSymbols = true) { // ``` .concat(otherStmts.flatMap { case Def(recursive, Var(name), L(body), isByname) => - val isLam = body.isInstanceOf[Lam] val (originalExpr, sym) = if (recursive) { val isByvalueRecIn = if (isByname) None else Some(true) - val sym = topLevelScope.declareValue(name, isByvalueRecIn, isLam, N) + val sym = topLevelScope.declareValue(name, isByvalueRecIn, body.isLam, N) val translated = translateTerm(body)(topLevelScope) topLevelScope.unregisterSymbol(sym) val isByvalueRecOut = if (isByname) None else Some(false) - (translated, topLevelScope.declareValue(name, isByvalueRecOut, isLam, N)) + (translated, topLevelScope.declareValue(name, isByvalueRecOut, body.isLam, N)) } else { val translatedBody = translateTerm(body)(topLevelScope) val isByvalueRec = if (isByname) None else Some(false) - (translatedBody, topLevelScope.declareValue(name, isByvalueRec, isLam, N)) + (translatedBody, topLevelScope.declareValue(name, isByvalueRec, body.isLam, N)) } val translatedBody = if (sym.isByvalueRec.isEmpty && !sym.isLam) JSArrowFn(Nil, L(originalExpr)) else originalExpr topLevelScope.tempVars `with` JSConstDecl(sym.runtimeName, translatedBody) :: @@ -1231,22 +1239,21 @@ class JSWebBackend extends JSBackend(allowUnresolvedSymbols = true) { case NuFunDef(isLetRec, nme @ Var(name), symNme, tys, rhs @ L(body)) => val recursive = isLetRec.getOrElse(true) val isByname = isLetRec.isEmpty - val bodyIsLam = body match { case _: Lam => true case _ => false } val symb = symNme.map(_.name) val (originalExpr, sym) = (if (recursive) { val isByvalueRecIn = if (isByname) None else Some(true) // TODO Improve: (Lionel) what?! - val sym = topLevelScope.declareValue(name, isByvalueRecIn, bodyIsLam, N) + val sym = topLevelScope.declareValue(name, isByvalueRecIn, body.isLam, N) val translated = translateTerm(body)(topLevelScope) topLevelScope.unregisterSymbol(sym) val isByvalueRecOut = if (isByname) None else Some(false) - (translated, topLevelScope.declareValue(name, isByvalueRecOut, bodyIsLam, symb)) + (translated, topLevelScope.declareValue(name, isByvalueRecOut, body.isLam, symb)) } else { val translated = translateTerm(body)(topLevelScope) val isByvalueRec = if (isByname) None else Some(false) - (translated, topLevelScope.declareValue(name, isByvalueRec, bodyIsLam, symb)) + (translated, topLevelScope.declareValue(name, isByvalueRec, body.isLam, symb)) }) val translatedBody = if (sym.isByvalueRec.isEmpty && !sym.isLam) JSArrowFn(Nil, L(originalExpr)) else originalExpr resultNames += sym.runtimeName @@ -1324,15 +1331,14 @@ abstract class JSTestBackend extends JSBackend(allowUnresolvedSymbols = false) { // Generate statements. val queries = otherStmts.map { case Def(recursive, Var(name), L(body), isByname) => - val bodyIsLam = body match { case _: Lam => true case _ => false } (if (recursive) { val isByvalueRecIn = if (isByname) None else Some(true) - val sym = scope.declareValue(name, isByvalueRecIn, bodyIsLam, N) + val sym = scope.declareValue(name, isByvalueRecIn, body.isLam, N) try { val translated = translateTerm(body) scope.unregisterSymbol(sym) val isByvalueRecOut = if (isByname) None else Some(false) - R((translated, scope.declareValue(name, isByvalueRecOut, bodyIsLam, N))) + R((translated, scope.declareValue(name, isByvalueRecOut, body.isLam, N))) } catch { case e: UnimplementedError => scope.stubize(sym, e.symbol) @@ -1340,7 +1346,7 @@ abstract class JSTestBackend extends JSBackend(allowUnresolvedSymbols = false) { case NonFatal(e) => scope.unregisterSymbol(sym) val isByvalueRecOut = if (isByname) None else Some(false) - scope.declareValue(name, isByvalueRecOut, bodyIsLam, N) + scope.declareValue(name, isByvalueRecOut, body.isLam, N) throw e } } else { @@ -1350,7 +1356,7 @@ abstract class JSTestBackend extends JSBackend(allowUnresolvedSymbols = false) { L(e.getMessage()) }) map { val isByvalueRec = if (isByname) None else Some(false) - expr => (expr, scope.declareValue(name, isByvalueRec, bodyIsLam, N)) + expr => (expr, scope.declareValue(name, isByvalueRec, body.isLam, N)) } }) match { case R((originalExpr, sym)) => @@ -1408,9 +1414,8 @@ abstract class JSTestBackend extends JSBackend(allowUnresolvedSymbols = false) { case fd @ NuFunDef(isLetRec, Var(nme), symNme, _, L(body)) => val isByname = isLetRec.isEmpty val isByvalueRecIn = if (isByname) None else Some(true) - val bodyIsLam = body match { case _: Lam => true case _ => false } val symb = symNme.map(_.name) - scope.declareValue(nme, isByvalueRecIn, bodyIsLam, symb) + scope.declareValue(nme, isByvalueRecIn, body.isLam, symb, true) case _ => () } @@ -1438,26 +1443,25 @@ abstract class JSTestBackend extends JSBackend(allowUnresolvedSymbols = false) { case NuFunDef(isLetRec, nme @ Var(name), symNme, tys, rhs @ L(body)) => val recursive = isLetRec.getOrElse(true) val isByname = isLetRec.isEmpty - val bodyIsLam = body match { case _: Lam => true case _ => false } val symb = symNme.map(_.name) (if (recursive) { val isByvalueRecIn = if (isByname) None else Some(true) val sym = scope.resolveValue(name) match { case Some(s: ValueSymbol) => s - case _ => scope.declareValue(name, isByvalueRecIn, bodyIsLam, symb) + case _ => scope.declareValue(name, isByvalueRecIn, body.isLam, symb) } val isByvalueRecOut = if (isByname) None else Some(false) try { val translated = translateTerm(body) // TODO Improve: (Lionel) Why are the bodies translated in the SAME scope?! scope.unregisterSymbol(sym) // TODO Improve: (Lionel) ??? - R((translated, scope.declareValue(name, isByvalueRecOut, bodyIsLam, symb))) + R((translated, scope.declareValue(name, isByvalueRecOut, body.isLam, symb))) } catch { case e: UnimplementedError => scope.stubize(sym, e.symbol) L(e.getMessage()) case NonFatal(e) => scope.unregisterSymbol(sym) // TODO Improve: (Lionel) You should only try/catch around the part that may actually fail, and if `unregisterSymbol` should always be called, that should be done in `finally`... but the very logic of calling `unregisterSymbol` is very fishy, to say the least - scope.declareValue(name, isByvalueRecOut, bodyIsLam, symb) + scope.declareValue(name, isByvalueRecOut, body.isLam, symb) throw e } } else { @@ -1467,7 +1471,7 @@ abstract class JSTestBackend extends JSBackend(allowUnresolvedSymbols = false) { L(e.getMessage()) }) map { val isByvalueRec = if (isByname) None else Some(false) - expr => (expr, scope.declareValue(name, isByvalueRec, bodyIsLam, symb)) + expr => (expr, scope.declareValue(name, isByvalueRec, body.isLam, symb)) } }) match { case R((originalExpr, sym)) => diff --git a/shared/src/main/scala/mlscript/codegen/Scope.scala b/shared/src/main/scala/mlscript/codegen/Scope.scala index 80afdc31..68c442ca 100644 --- a/shared/src/main/scala/mlscript/codegen/Scope.scala +++ b/shared/src/main/scala/mlscript/codegen/Scope.scala @@ -313,18 +313,26 @@ class Scope(val name: Str, enclosing: Opt[Scope]) { symbol } - def declareValue(lexicalName: Str, isByvalueRec: Option[Boolean], isLam: Boolean, symbolicName: Opt[Str]): ValueSymbol = { + def declareValue( + lexicalName: Str, + isByvalueRec: Option[Boolean], + isLam: Boolean, + symbolicName: Opt[Str], + /** Workaround for the first pass traversal with new definition typing. */ + forNewDefsDryRun: Bool = false + ): ValueSymbol = { val runtimeName = lexicalValueSymbols.get(lexicalName) match { // If we are implementing a stub symbol and the stub symbol did not shadow any other // symbols, it is safe to reuse its `runtimeName`. - case S(sym: StubValueSymbol) if !sym.shadowing => sym.runtimeName - case S(sym: BuiltinSymbol) if !sym.accessed => sym.runtimeName - case _ => allocateRuntimeName(lexicalName) + case S(sym: StubValueSymbol) if !sym.shadowing => sym.runtimeName + case S(sym: ValueSymbol) if sym.forNewDefsDryRun => sym.runtimeName + case S(sym: BuiltinSymbol) if !sym.accessed => sym.runtimeName + case _ => allocateRuntimeName(lexicalName) } - val symbol = ValueSymbol(lexicalName, runtimeName, isByvalueRec, isLam) + val symbol = ValueSymbol(lexicalName, runtimeName, isByvalueRec, isLam, forNewDefsDryRun) register(symbol) symbolicName.foreach { symbolicName => - register(ValueSymbol(symbolicName, runtimeName, isByvalueRec, isLam)) + register(ValueSymbol(symbolicName, runtimeName, isByvalueRec, isLam, forNewDefsDryRun)) } symbol } @@ -350,10 +358,16 @@ class Scope(val name: Str, enclosing: Opt[Scope]) { allowEscape: Bool ): StubValueSymbol = { val symbol = lexicalValueSymbols.get(lexicalName) match { + // If the existing symbol is a value symbol, but the value symbol is + // declared in the dry-run of new definition typing, we can reuse the + // runtime name. + case S(valueSymbol: ValueSymbol) if valueSymbol.forNewDefsDryRun => + StubValueSymbol(lexicalName, valueSymbol.runtimeName, false, previous) // If a stub with the same name has been defined, use the name. - case S(value) => StubValueSymbol(lexicalName, value.runtimeName, true, previous) + case S(symbol) => StubValueSymbol(lexicalName, symbol.runtimeName, true, previous) // Otherwise, we will allocate a new name. - case N => StubValueSymbol(lexicalName, allocateRuntimeName(lexicalName), false, previous) + case N => + StubValueSymbol(lexicalName, allocateRuntimeName(lexicalName), false, previous) } register(symbol) symbolicName.foreach { symbolicName => diff --git a/shared/src/main/scala/mlscript/codegen/Symbol.scala b/shared/src/main/scala/mlscript/codegen/Symbol.scala index 2d844d2f..85474c8f 100644 --- a/shared/src/main/scala/mlscript/codegen/Symbol.scala +++ b/shared/src/main/scala/mlscript/codegen/Symbol.scala @@ -45,13 +45,27 @@ sealed trait NuTypeSymbol { sym: TypeSymbol => def isNested: Bool = qualifier.isDefined // is nested in another class/mixin/module } -sealed class ValueSymbol(val lexicalName: Str, val runtimeName: Str, val isByvalueRec: Option[Boolean], val isLam: Boolean) extends RuntimeSymbol { +sealed class ValueSymbol( + val lexicalName: Str, + val runtimeName: Str, + val isByvalueRec: Option[Boolean], + val isLam: Boolean, + /** Workaround for the first pass traversal with new definition typing. */ + val forNewDefsDryRun: Boolean +) extends RuntimeSymbol { override def toString: Str = s"value $lexicalName" } object ValueSymbol { - def apply(lexicalName: Str, runtimeName: Str, isByvalueRec: Option[Boolean], isLam: Boolean): ValueSymbol = - new ValueSymbol(lexicalName, runtimeName, isByvalueRec, isLam) + def apply( + lexicalName: Str, + runtimeName: Str, + isByvalueRec: Option[Boolean], + isLam: Boolean, + /** Workaround for the first pass traversal with new definition typing. */ + forNewDefsDryRun: Boolean = false + ): ValueSymbol = + new ValueSymbol(lexicalName, runtimeName, isByvalueRec, isLam, forNewDefsDryRun) } sealed case class TypeAliasSymbol( diff --git a/shared/src/test/diff/codegen/AuxiliaryConstructors.mls b/shared/src/test/diff/codegen/AuxiliaryConstructors.mls index 5c729795..3fd393ed 100644 --- a/shared/src/test/diff/codegen/AuxiliaryConstructors.mls +++ b/shared/src/test/diff/codegen/AuxiliaryConstructors.mls @@ -464,11 +464,11 @@ n.ll //│ class TypingUnit19 {} //│ const typing_unit19 = new TypingUnit19; //│ // Query 1 -//│ globalThis.m1 = g(1); +//│ globalThis.m = g(1); //│ // Query 2 -//│ globalThis.n1 = m1(2); +//│ globalThis.n = m(2); //│ // Query 3 -//│ res = n1.ll; +//│ res = n.ll; //│ // End of generated code //│ m //│ = [Function (anonymous)] @@ -487,7 +487,7 @@ let mm = new M() //│ class TypingUnit21 {} //│ const typing_unit21 = new TypingUnit21; //│ // Query 1 -//│ globalThis.mm1 = new M.class(); +//│ globalThis.mm = new M.class(); //│ // End of generated code //│ mm //│ = M {} diff --git a/shared/src/test/diff/codegen/ConstructorStmt.mls b/shared/src/test/diff/codegen/ConstructorStmt.mls index 4f769b8f..a6130134 100644 --- a/shared/src/test/diff/codegen/ConstructorStmt.mls +++ b/shared/src/test/diff/codegen/ConstructorStmt.mls @@ -105,7 +105,7 @@ let aa = A(42) //│ class TypingUnit5 {} //│ const typing_unit5 = new TypingUnit5; //│ // Query 1 -//│ globalThis.aa1 = A(42); +//│ globalThis.aa = A(42); //│ // End of generated code //│ aa //│ = A {} @@ -119,7 +119,7 @@ aa //│ class TypingUnit6 {} //│ const typing_unit6 = new TypingUnit6; //│ // Query 1 -//│ res = aa1; +//│ res = aa; //│ // End of generated code //│ res //│ = A {} @@ -131,7 +131,7 @@ let ab = A(0) //│ class TypingUnit7 {} //│ const typing_unit7 = new TypingUnit7; //│ // Query 1 -//│ globalThis.ab1 = A(0); +//│ globalThis.ab = A(0); //│ // End of generated code //│ ab //│ = A {} @@ -408,9 +408,9 @@ www.add(42) //│ class TypingUnit15 {} //│ const typing_unit15 = new TypingUnit15; //│ // Query 1 -//│ globalThis.www1 = W(); +//│ globalThis.www = W(); //│ // Query 2 -//│ res = www1.add(42); +//│ res = www.add(42); //│ // End of generated code //│ www //│ = W {} diff --git a/shared/src/test/diff/codegen/Nested.mls b/shared/src/test/diff/codegen/Nested.mls index 227d0c7a..10843c74 100644 --- a/shared/src/test/diff/codegen/Nested.mls +++ b/shared/src/test/diff/codegen/Nested.mls @@ -78,9 +78,9 @@ bb.b //│ class TypingUnit1 {} //│ const typing_unit1 = new TypingUnit1; //│ // Query 1 -//│ globalThis.bb1 = A.B(A.a); +//│ globalThis.bb = A.B(A.a); //│ // Query 2 -//│ res = bb1.b; +//│ res = bb.b; //│ // End of generated code //│ bb //│ = B {} @@ -241,9 +241,9 @@ ee.x //│ class TypingUnit5 {} //│ const typing_unit5 = new TypingUnit5; //│ // Query 1 -//│ globalThis.ee1 = D.createE(42); +//│ globalThis.ee = D.createE(42); //│ // Query 2 -//│ res = ee1.x; +//│ res = ee.x; //│ // End of generated code //│ ee //│ = E {} @@ -361,13 +361,13 @@ gg.sum //│ class TypingUnit7 {} //│ const typing_unit7 = new TypingUnit7; //│ // Query 1 -//│ globalThis.es1 = E(1); +//│ globalThis.es = E(1); //│ // Query 2 -//│ globalThis.fff1 = es1.F(2); +//│ globalThis.fff = es.F(2); //│ // Query 3 -//│ globalThis.gg1 = fff1.G(3); +//│ globalThis.gg = fff.G(3); //│ // Query 4 -//│ res = gg1.sum; +//│ res = gg.sum; //│ // End of generated code //│ es //│ = E {} @@ -559,11 +559,11 @@ i.x //│ class TypingUnit10 {} //│ const typing_unit10 = new TypingUnit10; //│ // Query 1 -//│ globalThis.jj1 = G.H.J(42); +//│ globalThis.jj = G.H.J(42); //│ // Query 2 -//│ globalThis.i1 = jj1.ii(2); +//│ globalThis.i = jj.ii(2); //│ // Query 3 -//│ res = i1.x; +//│ res = i.x; //│ // End of generated code //│ jj //│ = J {} @@ -666,9 +666,9 @@ j.i.x //│ class TypingUnit12 {} //│ const typing_unit12 = new TypingUnit12; //│ // Query 1 -//│ globalThis.j1 = H.J(42); +//│ globalThis.j = H.J(42); //│ // Query 2 -//│ res = j1.i.x; +//│ res = j.i.x; //│ // End of generated code //│ j //│ = J {} @@ -757,11 +757,11 @@ ij.incY //│ const typing_unit13 = new TypingUnit13; //│ globalThis.I = typing_unit13.I; //│ // Query 1 -//│ globalThis.i3 = I(1); +//│ globalThis.i1 = I(1); //│ // Query 2 -//│ globalThis.ij1 = i3.J(0); +//│ globalThis.ij = i1.J(0); //│ // Query 3 -//│ res = ij1.incY; +//│ res = ij.incY; //│ // End of generated code //│ i //│ = I {} @@ -890,9 +890,9 @@ let n = J.N(2) //│ class TypingUnit15 {} //│ const typing_unit15 = new TypingUnit15; //│ // Query 1 -//│ globalThis.m1 = J.M(); +//│ globalThis.m = J.M(); //│ // Query 2 -//│ globalThis.n1 = J.N(2); +//│ globalThis.n = J.N(2); //│ // End of generated code //│ m //│ = M {} diff --git a/shared/src/test/diff/codegen/New.mls b/shared/src/test/diff/codegen/New.mls index 8636a83c..9909f20d 100644 --- a/shared/src/test/diff/codegen/New.mls +++ b/shared/src/test/diff/codegen/New.mls @@ -46,7 +46,7 @@ let c = C //│ class TypingUnit3 {} //│ const typing_unit3 = new TypingUnit3; //│ // Query 1 -//│ globalThis.c1 = C; +//│ globalThis.c = C; //│ // End of generated code //│ c //│ = [class C] @@ -93,7 +93,7 @@ let c = C //│ class TypingUnit8 {} //│ const typing_unit8 = new TypingUnit8; //│ // Query 1 -//│ globalThis.c3 = C; +//│ globalThis.c1 = C; //│ // End of generated code //│ c //│ = [Function (anonymous)] { diff --git a/shared/src/test/diff/codegen/NewDefsBug.mls b/shared/src/test/diff/codegen/NewDefsBug.mls new file mode 100644 index 00000000..2f004173 --- /dev/null +++ b/shared/src/test/diff/codegen/NewDefsBug.mls @@ -0,0 +1,72 @@ +:NewDefs + +:js +fun foo: Int -> Int +fun foo = x => x + 1 +class Bar { + fun calc(x) = foo(x) +} +//│ fun foo: Int -> Int +//│ class Bar { +//│ constructor() +//│ fun calc: Int -> Int +//│ } +//│ fun foo: Int -> Int +//│ // Prelude +//│ let res; +//│ class TypingUnit { +//│ #Bar; +//│ constructor() { +//│ } +//│ get Bar() { +//│ const qualifier = this; +//│ if (this.#Bar === undefined) { +//│ class Bar { +//│ constructor() { +//│ } +//│ calc(x) { +//│ return foo(x); +//│ } +//│ }; +//│ this.#Bar = Bar; +//│ } +//│ return this.#Bar; +//│ } +//│ } +//│ const typing_unit = new TypingUnit; +//│ globalThis.Bar = typing_unit.Bar; +//│ // Query 1 is empty +//│ // Query 2 +//│ globalThis.foo = function foo(x) { +//│ return x + 1; +//│ }; +//│ // End of generated code + +// Note: This test case looks trivial but it was like: +// +// ``` +// :re +// (new Bar()).calc(0) +// //│ Int +// //│ res +// //│ Runtime error: +// //│ ReferenceError: foo is not defined +// ``` +// +// My fix is a little bit hacky. The root of the problem is: when generating +// code within a class, we need all top-level bindings to be accessible. This +// part of implementation of new-definition-typing chose to declare all term +// `NuFunDef` as `ValueSymbol` in advance, but this can lead to the fact that +// the same symbol is declared multiple times, thus wasting some runtime names. +// Consequently, the code that references these wasted runtime names are invalid. +// +// Actually, I have a better solution, but it requires adjusting the order of +// translation, and I don't have much time to spend on this at the moment. So, +// my current fix is rather hacky. But I will complete this part when `PreTyper` +// is finished, when replacing the old Scope with the new Scope. +// +// Luyu Cheng on 2023/12/30 +(new Bar()).calc(0) +//│ Int +//│ res +//│ = 1 diff --git a/shared/src/test/diff/codegen/NewMatching.mls b/shared/src/test/diff/codegen/NewMatching.mls index 608ca2e5..8d270d15 100644 --- a/shared/src/test/diff/codegen/NewMatching.mls +++ b/shared/src/test/diff/codegen/NewMatching.mls @@ -136,7 +136,7 @@ fun foo(s) = //│ // Query 1 //│ globalThis.foo = function foo(s) { //│ return ((() => { -//│ return s instanceof Some.class ? (([t]) => ((b) => b + t.x)(s21.value))(Some.unapply(s)) : 0; +//│ return s instanceof Some.class ? (([t]) => ((b) => b + t.x)(s2.value))(Some.unapply(s)) : 0; //│ })()); //│ }; //│ // End of generated code diff --git a/shared/src/test/diff/codegen/NuReplHost.mls b/shared/src/test/diff/codegen/NuReplHost.mls index c5aaf456..d4805d1c 100644 --- a/shared/src/test/diff/codegen/NuReplHost.mls +++ b/shared/src/test/diff/codegen/NuReplHost.mls @@ -33,7 +33,7 @@ let r = foo(1) //│ └─┬ Query 2/2 //│ ├── Prelude: //│ ├── Code: -//│ ├── globalThis.r1 = foo(1); +//│ ├── globalThis.r = foo(1); //│ └── Reply: [runtime error] Error: an error was thrown //│ r //│ Runtime error: @@ -44,7 +44,7 @@ r //│ nothing //│ res //│ Runtime error: -//│ ReferenceError: r1 is not defined +//│ ReferenceError: r is not defined diff --git a/shared/src/test/diff/codegen/SymbolicOps.mls b/shared/src/test/diff/codegen/SymbolicOps.mls index 37e42ce6..9dd61cb7 100644 --- a/shared/src/test/diff/codegen/SymbolicOps.mls +++ b/shared/src/test/diff/codegen/SymbolicOps.mls @@ -14,7 +14,7 @@ let r = succ >> succ //│ class TypingUnit1 {} //│ const typing_unit1 = new TypingUnit1; //│ // Query 1 -//│ globalThis.r1 = compose(succ, succ); +//│ globalThis.r = compose(succ, succ); //│ // End of generated code //│ r //│ = [Function (anonymous)] @@ -65,7 +65,7 @@ let f = (>>) //│ class TypingUnit7 {} //│ const typing_unit7 = new TypingUnit7; //│ // Query 1 -//│ globalThis.f1 = compose; +//│ globalThis.f = compose; //│ // End of generated code //│ f //│ = [Function: compose] @@ -315,7 +315,7 @@ fun (:-D) dd(a, b) = a + b val (->) f(x, y) = [x, y] //│ val (->) f: forall 'a 'b. ('a, 'b) -> ['a, 'b] //│ f -//│ = [Function: f3] +//│ = [Function: f1] 12 -> 34 //│ [12, 34] @@ -326,7 +326,7 @@ val (->) f(x, y) = [x, y] let (->) _ = f //│ let (->) _: forall 'a 'b. ('a, 'b) -> ['a, 'b] //│ _ -//│ = [Function: f3] +//│ = [Function: f1] :js 12 -> 34 @@ -335,7 +335,7 @@ let (->) _ = f //│ class TypingUnit42 {} //│ const typing_unit42 = new TypingUnit42; //│ // Query 1 -//│ res = _1(12, 34); +//│ res = _(12, 34); //│ // End of generated code //│ res //│ = [ 12, 34 ] diff --git a/shared/src/test/diff/mlscript/Sequence.mls b/shared/src/test/diff/mlscript/Sequence.mls index 6c52dff4..919c7251 100644 --- a/shared/src/test/diff/mlscript/Sequence.mls +++ b/shared/src/test/diff/mlscript/Sequence.mls @@ -4,7 +4,7 @@ let test(x) = log(x); x + 1 //│ let test: Int -> Int //│ test -//│ = [Function: test1] +//│ = [Function: test] test(log("here we go"); 123) //│ Int diff --git a/shared/src/test/diff/nu/ArrayProg.mls b/shared/src/test/diff/nu/ArrayProg.mls index b33cb35f..857ee4e3 100644 --- a/shared/src/test/diff/nu/ArrayProg.mls +++ b/shared/src/test/diff/nu/ArrayProg.mls @@ -40,7 +40,7 @@ fun zip(xs, ys) = mapi of xs, (x, i) => zip //│ forall 'a 'b. (Array['a], Array[Object & 'b & ~()]) -> Array[['a, 'b]] //│ res -//│ = [Function: zip1] +//│ = [Function: zip] diff --git a/shared/src/test/diff/nu/CtorSubtraction.mls b/shared/src/test/diff/nu/CtorSubtraction.mls index 38d37eaa..9a58ce68 100644 --- a/shared/src/test/diff/nu/CtorSubtraction.mls +++ b/shared/src/test/diff/nu/CtorSubtraction.mls @@ -16,12 +16,12 @@ fun x = case x : Int -> Int //│ Int -> Int //│ res -//│ = [Function: x1] +//│ = [Function: x] x : Cls -> nothing //│ Cls -> nothing //│ res -//│ = [Function: x1] +//│ = [Function: x] fun x: (Int | Str | Cls) \ Cls diff --git a/shared/src/test/diff/nu/FlatIndentFuns.mls b/shared/src/test/diff/nu/FlatIndentFuns.mls index 77ace9e7..3a58afdd 100644 --- a/shared/src/test/diff/nu/FlatIndentFuns.mls +++ b/shared/src/test/diff/nu/FlatIndentFuns.mls @@ -20,7 +20,7 @@ y => x + y //│ let r: Int -> Int -> Int //│ r -//│ = [Function: r1] +//│ = [Function: r] r(1)(2) //│ Int @@ -59,6 +59,6 @@ x + y //│ ╙── //│ let r: Int -> Int -> Int //│ r -//│ = [Function: r3] +//│ = [Function: r1] diff --git a/shared/src/test/diff/nu/FlatMonads_repro.mls b/shared/src/test/diff/nu/FlatMonads_repro.mls index 1fe5bef9..938516f4 100644 --- a/shared/src/test/diff/nu/FlatMonads_repro.mls +++ b/shared/src/test/diff/nu/FlatMonads_repro.mls @@ -48,7 +48,7 @@ let ri(f) = Bind(Pure(42), f) //│ where //│ 'CC :> 42 //│ ri -//│ = [Function: ri1] +//│ = [Function: ri] ri(Pure) //│ Bind['CC, 'AA] diff --git a/shared/src/test/diff/nu/HeungTung.mls b/shared/src/test/diff/nu/HeungTung.mls index 264a5417..51f4efad 100644 --- a/shared/src/test/diff/nu/HeungTung.mls +++ b/shared/src/test/diff/nu/HeungTung.mls @@ -202,7 +202,7 @@ type Res = M(T) let f = x => [x, x] //│ let f: forall 'a. 'a -> ['a, 'a] //│ f -//│ = [Function: f3] +//│ = [Function: f1] [f(1), f(true)] //│ [[1, 1], [true, true]] diff --git a/shared/src/test/diff/nu/LamPatterns.mls b/shared/src/test/diff/nu/LamPatterns.mls index a396d13e..f9e5dace 100644 --- a/shared/src/test/diff/nu/LamPatterns.mls +++ b/shared/src/test/diff/nu/LamPatterns.mls @@ -24,12 +24,12 @@ let f = Some => 0 //│ class TypingUnit2 {} //│ const typing_unit2 = new TypingUnit2; //│ // Query 1 -//│ globalThis.f1 = function f1(Some) { +//│ globalThis.f = function f(Some) { //│ return 0; //│ }; //│ // End of generated code //│ f -//│ = [Function: f1] +//│ = [Function: f] // :e // TODO f(Some) diff --git a/shared/src/test/diff/nu/LocalLets.mls b/shared/src/test/diff/nu/LocalLets.mls index 15717f5d..211987ae 100644 --- a/shared/src/test/diff/nu/LocalLets.mls +++ b/shared/src/test/diff/nu/LocalLets.mls @@ -32,6 +32,6 @@ let E(x) = new E(1) //│ ╙── ^ //│ let E: anything -> error //│ E -//│ = [Function: E2] +//│ = [Function: E1] diff --git a/shared/src/test/diff/nu/MIscPoly.mls b/shared/src/test/diff/nu/MIscPoly.mls index 34235988..3d9c94a5 100644 --- a/shared/src/test/diff/nu/MIscPoly.mls +++ b/shared/src/test/diff/nu/MIscPoly.mls @@ -69,7 +69,7 @@ r() //│ nothing //│ res //│ Runtime error: -//│ TypeError: r1 is not a function +//│ TypeError: r is not a function diff --git a/shared/src/test/diff/nu/Misc.mls b/shared/src/test/diff/nu/Misc.mls index a0aa87e2..821a12d2 100644 --- a/shared/src/test/diff/nu/Misc.mls +++ b/shared/src/test/diff/nu/Misc.mls @@ -65,7 +65,7 @@ f of [1, 2] let f = (x, y) => x + y //│ let f: (Int, Int) -> Int //│ f -//│ = [Function: f5] +//│ = [Function: f4] f(1, 2) //│ Int @@ -96,7 +96,7 @@ let f = ((x, y)) => x + y //│ ╙── ^^^^^^ //│ let f: ([Int, Int]) -> Int //│ f -//│ = [Function: f7] +//│ = [Function: f5] :e f(1, 2) @@ -132,7 +132,7 @@ f[1, 2] //│ ╙── ^^^^^^^ //│ ([Int, Int]) -> Int //│ res -//│ = [Function: f7] +//│ = [Function: f5] :pe @@ -142,14 +142,14 @@ let f = (((x, y))) => x + y //│ ╙── ^^^^^^ //│ let f: ([Int, Int]) -> Int //│ f -//│ = [Function: f9] +//│ = [Function: f6] // * TODO maybe parse as type lambda? let f = [x, y] => x + y //│ let f: ([Int, Int]) -> Int //│ f -//│ = [Function: f11] +//│ = [Function: f7] :e f(1, 2) @@ -173,7 +173,7 @@ f([1, 2]) let f = ([x, y]) => x + y //│ let f: ([Int, Int]) -> Int //│ f -//│ = [Function: f13] +//│ = [Function: f8] f([1, 2]) //│ Int @@ -197,7 +197,7 @@ f(1, 2) let f = [[[x, y]]] => x + y //│ let f: ([[[Int, Int]]]) -> Int //│ f -//│ = [Function: f15] +//│ = [Function: f9] :e f([[1, 2]]) diff --git a/shared/src/test/diff/nu/NamedArgs.mls b/shared/src/test/diff/nu/NamedArgs.mls index c846ee54..8c4d4606 100644 --- a/shared/src/test/diff/nu/NamedArgs.mls +++ b/shared/src/test/diff/nu/NamedArgs.mls @@ -116,9 +116,9 @@ test(0, y: 200) //│ class TypingUnit13 {} //│ const typing_unit13 = new TypingUnit13; //│ // Query 1 -//│ globalThis.tmp1 = 2; +//│ globalThis.tmp = 2; //│ // Query 2 -//│ res = test1(0, tmp1); +//│ res = test1(0, tmp); //│ // Query 3 //│ res = test1(0, 200); //│ // End of generated code @@ -171,11 +171,11 @@ fff(y: 2, z: y_1 + 1, x: z_1 - 2) //│ class TypingUnit17 {} //│ const typing_unit17 = new TypingUnit17; //│ // Query 1 -//│ globalThis["y_11"] = 2; +//│ globalThis["y_1"] = 2; //│ // Query 2 -//│ globalThis["z_11"] = 3; +//│ globalThis["z_1"] = 3; //│ // Query 3 -//│ res = ((z_2) => ((x_1) => fff(x_1, 2, z_2))(z_11 - 2))(y_11 + 1); +//│ res = ((z_2) => ((x_1) => fff(x_1, 2, z_2))(z_1 - 2))(y_1 + 1); //│ // End of generated code //│ y_1 //│ = 2 diff --git a/shared/src/test/diff/nu/NestedRecords.mls b/shared/src/test/diff/nu/NestedRecords.mls index ca216f47..ebe7bbcb 100644 --- a/shared/src/test/diff/nu/NestedRecords.mls +++ b/shared/src/test/diff/nu/NestedRecords.mls @@ -4,7 +4,7 @@ let f(x) = [x, x] //│ let f: forall 'a. 'a -> ['a, 'a] //│ f -//│ = [Function: f1] +//│ = [Function: f] let a = { u: f(f(f(1))), v: f(f(f(1))) } //│ let a: {u: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]], v: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]]} diff --git a/shared/src/test/diff/nu/NewNew.mls b/shared/src/test/diff/nu/NewNew.mls index 86753b6e..e54f469a 100644 --- a/shared/src/test/diff/nu/NewNew.mls +++ b/shared/src/test/diff/nu/NewNew.mls @@ -103,7 +103,7 @@ f(1) //│ error //│ res //│ Runtime error: -//│ TypeError: f9 is not a function +//│ TypeError: f4 is not a function :e new Foo("2") diff --git a/shared/src/test/diff/nu/Res.mls b/shared/src/test/diff/nu/Res.mls index e8936ec3..0f7f660b 100644 --- a/shared/src/test/diff/nu/Res.mls +++ b/shared/src/test/diff/nu/Res.mls @@ -19,7 +19,7 @@ res(1) let res = x => x + 2 //│ let res: Int -> Int //│ res -//│ = [Function: res2] +//│ = [Function: res1] res(1) //│ Int diff --git a/shared/src/test/diff/nu/Vals.mls b/shared/src/test/diff/nu/Vals.mls index e90080f1..413b7a84 100644 --- a/shared/src/test/diff/nu/Vals.mls +++ b/shared/src/test/diff/nu/Vals.mls @@ -32,7 +32,7 @@ val a = a val f(x) = x //│ val f: forall 'a. 'a -> 'a //│ f -//│ = [Function: f1] +//│ = [Function: f] f(123) //│ 123 diff --git a/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls b/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls index 2dc1c106..21825930 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls @@ -326,7 +326,7 @@ fun lookup(env, name: Str): 'A = env(name) //│ let emptyEnv: Str -> nothing //│ fun lookup: forall 'A. (Str -> 'A, name: Str) -> 'A //│ emptyEnv -//│ = [Function: emptyEnv1] +//│ = [Function: emptyEnv] // It is tricky to write an annotation that simplifies the inferred type. fun (+:) extend(env, [name, expr]: [Str, 'A]) = diff --git a/shared/src/test/diff/pretyper/ucs/examples/Option.mls b/shared/src/test/diff/pretyper/ucs/examples/Option.mls index c7f9c942..687b4b37 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/Option.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/Option.mls @@ -1,27 +1,113 @@ :PreTyper // FIXME: Finish this example after the relevant issue is fixed. +abstract class MyOption[out T]: (MySome[T] | MyNone) { + virtual fun filter: (p: T -> Bool) -> MyOption[T] +} +class MySome[out T](val value: T) extends MyOption[T] { + fun filter(p) = if p of value then MySome(value) else MyNone +} +module MyNone extends MyOption[nothing] { + fun filter(_) = MyNone +} +//│ ╔══[ERROR] Type `#MySome & {MySome#T <: ?T}` does not contain member `MyOption#T` +//│ ║ l.4: abstract class MyOption[out T]: (MySome[T] | MyNone) { +//│ ╙── ^ +//│ ╔══[ERROR] Type `#MyNone` does not contain member `MyOption#T` +//│ ║ l.4: abstract class MyOption[out T]: (MySome[T] | MyNone) { +//│ ╙── ^ +//│ abstract class MyOption[T]: MyNone | MySome[T] { +//│ fun filter: (p: T -> Bool) -> MyOption[T] +//│ } +//│ class MySome[T](value: T) extends MyOption { +//│ fun filter: (T -> Object) -> (MyNone | MySome[T]) +//│ } +//│ module MyNone extends MyOption { +//│ fun filter: anything -> MyNone +//│ } + +// The following code aims to find a workaround that allows the functions +// operating ADT to be defined as member functions of classes, and also ensures +// the correct generation of JavaScript code. + +// Create an alias constructor for `Some` +// ====================================== +// // This works: +fun some: forall 'a: 'a -> Option['a] +fun some = x => Some(x) +// // This doesn't work with `map`: +// let some: forall 'a: 'a -> Option['a] = x => Some(x) +// +// Create an alias constructor for `None` +// ====================================== +// // This works: +// fun none: forall 'a: () -> Option['a] +// fun none = () => None +// // This also works: +// fun none: Option[nothing] +// fun none = None +// This also works: +let none: () -> Option[nothing] = () => None +// // This also works but failed in code generation: +// let none: Option[nothing] = None +// +// The class definitions +// ===================== abstract class Option[out T]: (Some[T] | None) { + virtual fun isEmpty: Bool + virtual fun isDefined: Bool + virtual fun map: forall 'b: (T -> 'b) -> Option['b] + virtual fun flatMap: forall 'b: (T -> Option['b]) -> Option['b] virtual fun filter: (p: T -> Bool) -> Option[T] + virtual fun get: T } class Some[out T](val value: T) extends Option[T] { - fun filter(p) = if p of value then Some(value) else None + fun isEmpty = false + fun isDefined = true + fun map(f) = some(f(value)) + fun flatMap(f) = f(value) + fun filter(p) = if p of value then some(value) else none() + fun get = value } module None extends Option[nothing] { - fun filter(_) = None + fun isEmpty = true + fun isDefined = false + fun map(_) = none() + fun flatMap(_) = none() + fun filter(_) = none() + fun get = error } -//│ ╔══[ERROR] Type `#Some & {Some#T <: ?T}` does not contain member `Option#T` -//│ ║ l.4: abstract class Option[out T]: (Some[T] | None) { -//│ ╙── ^ -//│ ╔══[ERROR] Type `#None` does not contain member `Option#T` -//│ ║ l.4: abstract class Option[out T]: (Some[T] | None) { -//│ ╙── ^ +//│ fun some: forall 'T. 'T -> Some['T] +//│ let none: () -> Option[nothing] //│ abstract class Option[T]: None | Some[T] { //│ fun filter: (p: T -> Bool) -> Option[T] +//│ fun flatMap: forall 'b. (T -> Option['b]) -> Option['b] +//│ fun get: T +//│ fun isDefined: Bool +//│ fun isEmpty: Bool +//│ fun map: forall 'b0. (T -> 'b0) -> Option['b0] //│ } //│ class Some[T](value: T) extends Option { -//│ fun filter: (T -> Object) -> (None | Some[T]) +//│ fun filter: (T -> Object) -> Option[T] +//│ fun flatMap: forall 'c. (T -> 'c) -> 'c +//│ fun get: T +//│ fun isDefined: true +//│ fun isEmpty: false +//│ fun map: forall 'a. (T -> 'a) -> Option['a] //│ } //│ module None extends Option { -//│ fun filter: anything -> None +//│ fun filter: anything -> Option[nothing] +//│ fun flatMap: anything -> Option[nothing] +//│ fun get: nothing +//│ fun isDefined: false +//│ fun isEmpty: true +//│ fun map: anything -> Option[nothing] //│ } +//│ fun some: forall 'a0. 'a0 -> Option['a0] +//│ none +//│ = [Function: none] + +some(0).map(x => x + 1).get +//│ Int +//│ res +//│ = 1 From 4529fe9db4d4bf683b103c0a6379cbb164acaade Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sat, 30 Dec 2023 05:10:01 +0800 Subject: [PATCH 038/143] Support hex, octal, and binary integers --- shared/src/main/scala/mlscript/NewLexer.scala | 47 ++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/shared/src/main/scala/mlscript/NewLexer.scala b/shared/src/main/scala/mlscript/NewLexer.scala index f5ac3262..7db5f049 100644 --- a/shared/src/main/scala/mlscript/NewLexer.scala +++ b/shared/src/main/scala/mlscript/NewLexer.scala @@ -23,6 +23,12 @@ class NewLexer(origin: Origin, raise: Diagnostic => Unit, dbg: Bool) { c.isLetter || c === '_' || c === '\'' def isIdentChar(c: Char): Bool = isIdentFirstChar(c) || isDigit(c) || c === '\'' + def isHexDigit(c: Char): Bool = + isDigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') + def isOctDigit(c: Char): Bool = + c >= '0' && c <= '7' + def isBinDigit(c: Char): Bool = + c === '0' || c === '1' def isDigit(c: Char): Bool = c >= '0' && c <= '9' @@ -59,15 +65,52 @@ class NewLexer(origin: Origin, raise: Diagnostic => Unit, dbg: Bool) { if (i < length && pred(bytes(i))) takeWhile(i + 1, bytes(i) :: cur)(pred) else (cur.reverseIterator.mkString, i) + final def int(i: Int): (BigInt, Int) = { + def radix(i: Int, radix: Int, desc: Str, pred: Char => Bool): (BigInt, Int) = { + val (str, j) = takeWhile(i)(pred) + if (str.isEmpty) { + raise(ErrorReport(msg"Expect at least one $desc digit" -> S(loc(i, i + 2)) :: Nil, + newDefs = true, source = Lexing)) + (BigInt(0), j) + } + else (BigInt(str, radix), j) + } + if (i < length) { + if (bytes(i) === '0') { + if (i + 1 < length) { + bytes(i + 1) match { + case 'x' => radix(i + 2, 16, "hexadecimal", isHexDigit) + case 'o' => radix(i + 2, 8, "octal", isOctDigit) + case 'b' => radix(i + 2, 2, "binary", isBinDigit) + case _ => + val (str, j) = takeWhile(i + 1, '0' :: Nil)(isDigit) + (BigInt(str), j) + } + } else { + (BigInt(0), i + 1) + } + } else { + radix(i, 10, "decimal", isDigit) + } + } else { + raise(ErrorReport(msg"Expect a integer literal" -> S(loc(i, i + 1)) :: Nil, + newDefs = true, source = Lexing)) + (BigInt(0), i) + } + } + @tailrec final def str(i: Int, escapeMode: Bool, cur: Ls[Char] = Nil): (Str, Int) = if (escapeMode) if (i < length) bytes(i) match { + case '\\' => str(i + 1, false, '\\' :: cur) case '"' => str(i + 1, false, '"' :: cur) case 'n' => str(i + 1, false, '\n' :: cur) case 't' => str(i + 1, false, '\t' :: cur) case 'r' => str(i + 1, false, '\r' :: cur) + case 'b' => str(i + 1, false, '\b' :: cur) + case 'f' => str(i + 1, false, '\f' :: cur) case ch => raise(WarningReport(msg"Found invalid escape character" -> S(loc(i, i + 1)) :: Nil, newDefs = true, source = Lexing)) @@ -190,9 +233,9 @@ class NewLexer(origin: Origin, raise: Diagnostic => Unit, dbg: Bool) { // else go(j, if (isSymKeyword.contains(n)) KEYWORD(n) else IDENT(n, true)) else lex(j, ind, next(j, if (isSymKeyword.contains(n)) KEYWORD(n) else IDENT(n, true))) case _ if isDigit(c) => - val (str, j) = takeWhile(i)(isDigit) + val (value, j) = int(i) // go(j, LITVAL(IntLit(BigInt(str)))) - lex(j, ind, next(j, LITVAL(IntLit(BigInt(str))))) + lex(j, ind, next(j, LITVAL(IntLit(value)))) case _ => pe(msg"unexpected character '${escapeChar(c)}'") // go(i + 1, ERROR) From 72e454e872655252d2dd24b62b50c214e3181961 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sat, 30 Dec 2023 05:10:54 +0800 Subject: [PATCH 039/143] Add `+`, `-`, and `*` functions for numbers; also add `**` operator --- shared/src/main/scala/mlscript/Typer.scala | 4 ++++ shared/src/main/scala/mlscript/codegen/Polyfill.scala | 3 +++ shared/src/main/scala/mlscript/codegen/Scope.scala | 3 +++ shared/src/main/scala/mlscript/pretyper/Scope.scala | 4 +++- shared/src/test/diff/codegen/Terms.mls | 7 +++++-- 5 files changed, 18 insertions(+), 3 deletions(-) diff --git a/shared/src/main/scala/mlscript/Typer.scala b/shared/src/main/scala/mlscript/Typer.scala index 452c059c..6e8f03d4 100644 --- a/shared/src/main/scala/mlscript/Typer.scala +++ b/shared/src/main/scala/mlscript/Typer.scala @@ -318,6 +318,9 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne "sub" -> intBinOpTy, "mul" -> intBinOpTy, "div" -> intBinOpTy, + "numAdd" -> numberBinOpTy, + "numSub" -> numberBinOpTy, + "numMul" -> numberBinOpTy, "sqrt" -> fun(singleTup(IntType), IntType)(noProv), "lt" -> numberBinPred, "le" -> numberBinPred, @@ -343,6 +346,7 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne "*" -> intBinOpTy, "%" -> intBinOpTy, "/" -> numberBinOpTy, + "**" -> numberBinOpTy, "<" -> numberBinPred, ">" -> numberBinPred, "<=" -> numberBinPred, diff --git a/shared/src/main/scala/mlscript/codegen/Polyfill.scala b/shared/src/main/scala/mlscript/codegen/Polyfill.scala index 9a01564d..e7d62f3a 100644 --- a/shared/src/main/scala/mlscript/codegen/Polyfill.scala +++ b/shared/src/main/scala/mlscript/codegen/Polyfill.scala @@ -161,6 +161,9 @@ object Polyfill { buffer += BuiltinFunc("add", makeBinaryFunc("+")) buffer += BuiltinFunc("sub", makeBinaryFunc("-")) buffer += BuiltinFunc("mul", makeBinaryFunc("*")) + buffer += BuiltinFunc("numAdd", makeBinaryFunc("+")) + buffer += BuiltinFunc("numSub", makeBinaryFunc("-")) + buffer += BuiltinFunc("numMul", makeBinaryFunc("*")) buffer += BuiltinFunc("div", makeBinaryFunc("/")) buffer += BuiltinFunc("gt", makeBinaryFunc(">")) buffer += BuiltinFunc("not", makeUnaryFunc("!")) diff --git a/shared/src/main/scala/mlscript/codegen/Scope.scala b/shared/src/main/scala/mlscript/codegen/Scope.scala index 68c442ca..c31732d3 100644 --- a/shared/src/main/scala/mlscript/codegen/Scope.scala +++ b/shared/src/main/scala/mlscript/codegen/Scope.scala @@ -44,6 +44,9 @@ class Scope(val name: Str, enclosing: Opt[Scope]) { "add", "sub", "mul", + "numAdd", + "numSub", + "numMul", "div", "gt", "not", diff --git a/shared/src/main/scala/mlscript/pretyper/Scope.scala b/shared/src/main/scala/mlscript/pretyper/Scope.scala index d6eae37c..ffc041a1 100644 --- a/shared/src/main/scala/mlscript/pretyper/Scope.scala +++ b/shared/src/main/scala/mlscript/pretyper/Scope.scala @@ -99,10 +99,12 @@ object Scope { Scope.from( """true,false,document,window,typeof,toString,not,succ,log,discard,negate, |round,add,sub,mul,div,sqrt,lt,le,gt,ge,slt,sle,sgt,sge,length,concat,eq, - |ne,error,id,if,emptyArray,+,-,*,%,/,<,>,<=,>=,==,===,<>,&&,||,and""" + |ne,error,id,if,emptyArray,+,-,*,%,/,**,<,>,<=,>=,==,===,<>,&&,||,and, + |numAdd,numSub,numMul""" .stripMargin .split(",") .iterator + .map(_.trim) .map(name => new LocalTermSymbol(Var(name))) .concat(trueSymbol :: falseSymbol :: Nil) ) diff --git a/shared/src/test/diff/codegen/Terms.mls b/shared/src/test/diff/codegen/Terms.mls index 1df094b5..e445c30f 100644 --- a/shared/src/test/diff/codegen/Terms.mls +++ b/shared/src/test/diff/codegen/Terms.mls @@ -66,9 +66,12 @@ z ** 4 * z / (x + 5) //│ // Query 1 //│ res = z() ** 4 * z() / (x() + 5); //│ // End of generated code -//│ ╔══[ERROR] identifier not found: ** +//│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.65: z ** 4 * z / (x + 5) -//│ ╙── ^^ +//│ ║ ^^^^^^^^ +//│ ╟── operator application of type `number` is not an instance of type `int` +//│ ║ l.65: z ** 4 * z / (x + 5) +//│ ╙── ^^^^^^ //│ res: number //│ = 6.4 From 10fb3e156ada18b948d4234750755fde1c82aa64 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sat, 30 Dec 2023 05:11:55 +0800 Subject: [PATCH 040/143] Add JSON parser example --- .../test/diff/pretyper/ucs/examples/JSON.mls | 552 ++++++++++++++++++ 1 file changed, 552 insertions(+) create mode 100644 shared/src/test/diff/pretyper/ucs/examples/JSON.mls diff --git a/shared/src/test/diff/pretyper/ucs/examples/JSON.mls b/shared/src/test/diff/pretyper/ucs/examples/JSON.mls new file mode 100644 index 00000000..32900a89 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/examples/JSON.mls @@ -0,0 +1,552 @@ +:PreTyper + + +type NStr = Str & { + length: Int, + at: Int -> NStr, + charAt: Int -> NStr, + charCodeAt: Int -> Int, + slice: (Int, Int) -> NStr, + startsWith: (Str, Int) -> Bool, + endsWith: Str -> Bool, + split: Str -> Array[NStr], + trim: () -> NStr, + trimStart: () -> NStr, + trimEnd: () -> NStr, + padStart: (Int, Str) -> NStr, + padEnd: (Int, Str) -> NStr, + repeat: Int -> NStr, + indexOf: Str -> Int, + lastIndexOf: Str -> Int, + includes: Str -> Bool, + localeCompare: Str -> Int +} +declare fun String: (anything -> NStr) & { fromCodePoint: Int -> NStr } +fun (++) strcat(a, b) = String of concat(a)(b) +fun (<*) strlt(a: NStr, b: NStr) = a.localeCompare(b) < 0 +fun (*>) strgt(a: NStr, b: NStr) = a.localeCompare(b) > 0 +//│ type NStr = Str & { +//│ at: Int -> NStr, +//│ charAt: Int -> NStr, +//│ charCodeAt: Int -> Int, +//│ endsWith: Str -> Bool, +//│ includes: Str -> Bool, +//│ indexOf: Str -> Int, +//│ lastIndexOf: Str -> Int, +//│ length: Int, +//│ localeCompare: Str -> Int, +//│ padEnd: (Int, Str) -> NStr, +//│ padStart: (Int, Str) -> NStr, +//│ repeat: Int -> NStr, +//│ slice: (Int, Int) -> NStr, +//│ split: Str -> Array[NStr], +//│ startsWith: (Str, Int) -> Bool, +//│ trim: () -> NStr, +//│ trimEnd: () -> NStr, +//│ trimStart: () -> NStr +//│ } +//│ fun (++) strcat: (Str, Str) -> NStr +//│ fun (<*) strlt: (a: NStr, b: NStr) -> Bool +//│ fun (*>) strgt: (a: NStr, b: NStr) -> Bool +//│ fun String: anything -> NStr & {fromCodePoint: Int -> NStr} + +declare fun Math: { log10: Num -> Num, floor: Num -> Num, ceil: Num -> Num } +//│ fun Math: {ceil: Num -> Num, floor: Num -> Num, log10: Num -> Num} + +// Steal operators from OCaml: +let (+.) numAdd' = numAdd +let (-.) numSub' = numSub +let (*.) numMul' = numMul +//│ let (+.) numAdd': (Num, Num) -> Num +//│ let (-.) numSub': (Num, Num) -> Num +//│ let (*.) numMul': (Num, Num) -> Num +//│ numAdd' +//│ = [Function: numAdd] +//│ numSub' +//│ = [Function: numSub] +//│ numMul' +//│ = [Function: numMul] + +fun (!==) notEqual(x, y) = not(x === y) +declare fun parseInt: (Str, Int) -> Int +//│ fun (!==) notEqual: forall 'a. (Eql['a], 'a) -> Bool +//│ fun parseInt: (Str, Int) -> Int + +// `List` and its utilities: +abstract class List[out T]: Cons[T] | Nil +class Cons[T](head: T, tail: List[T]) extends List[T] +module Nil extends List +fun (::) cons(head: 'T, tail: List['T]): List['T] = Cons(head, tail) +fun reverse(l: List['A]): List['A] = + let rec r(l', l) = if l is Cons(x, xs) then r(x :: l', xs) else l' + r(Nil, l) +fun join(sep: Str, xs: List['B]) = if xs is + Cons(x, Nil) then toString(x) + Cons(x, xs) then toString(x) ++ sep ++ join(sep, xs) + Nil then "" +fun showList(xs: List['C]) = "[" ++ join(", ", xs) ++ "]" +fun map(f: 'D -> 'E, xs: List['D]): List['E] = if xs is + Cons(x, xs) then f(x) :: map(f, xs) + Nil then Nil +fun equalList(xs: List['A], ys: List['A], equal: ('A, 'A) -> Bool): Bool = if xs is + Cons(x, xs') and ys is Cons(y, ys') then equal(x, y) and equalList(xs', ys', equal) + Nil and ys is Nil then true + else false +//│ abstract class List[T]: Cons[T] | Nil +//│ class Cons[T](head: T, tail: List[T]) extends List +//│ module Nil extends List +//│ fun (::) cons: forall 'T. (head: 'T, tail: List['T]) -> List['T] +//│ fun reverse: forall 'A. (l: List['A]) -> List['A] +//│ fun join: (sep: Str, xs: List[anything]) -> (Str | NStr) +//│ fun showList: (xs: List[anything]) -> NStr +//│ fun map: forall 'D 'T0. (f: 'D -> 'T0, xs: List['D]) -> List['T0] +//│ fun equalList: forall 'A0. (xs: List['A0], ys: List['A0], equal: ('A0, 'A0) -> Bool) -> Bool + +// `Option` and its utilities: +abstract class Option[out A]: Some[A] | None +class Some[out A](value: A) extends Option[A] +module None extends Option +//│ abstract class Option[A]: None | Some[A] +//│ class Some[A](value: A) extends Option +//│ module None extends Option + +fun (->) makePair(a, b) = [a, b] +//│ fun (->) makePair: forall 'a 'b. ('a, 'b) -> ['a, 'b] + +abstract class ListMap[K, out V]: (ConsMap[K, V] | NilMap) +class ConsMap[K, out V](head: [K, V], tail: ListMap[K, V]) extends ListMap[K, V] +module NilMap extends ListMap +fun containsKey(map: ListMap['K, 'V], key: 'K): Bool = if map is + ConsMap([k, _], _) and k === key then true + ConsMap(_, tail) then containsKey(tail, key) + NilMap then false +fun (:+) insert(map, entry) = if map is + ConsMap(entry', map) and + entry'.0 === entry.0 then ConsMap(entry, map) + else ConsMap(entry', insert(map, entry)) + NilMap then ConsMap(entry, NilMap) +fun showMap(map) = + let showEntry([k, v]) = toString(k) ++ " -> " ++ toString(v) + let rec aux(map) = if map is + ConsMap(last, NilMap) then showEntry(last) + ConsMap(head, tail) then showEntry(head) ++ ", " ++ aux(tail) + NilMap then "" + if map is NilMap then String("{}") else "{ " ++ aux(map) ++ " }" +//│ abstract class ListMap[K, V]: ConsMap[K, V] | NilMap +//│ class ConsMap[K, V](head: [K, V], tail: ListMap[K, V]) extends ListMap +//│ module NilMap extends ListMap +//│ fun containsKey: forall 'K 'a. (map: ListMap['K, anything], key: 'a) -> Bool +//│ fun (:+) insert: forall 'K0 'b 'V. (ConsMap['K0, 'V] | NilMap, ['K0 & 'b, 'V]) -> ConsMap['K0, 'V] +//│ fun showMap: forall 'K1. (ConsMap['K1, anything] | NilMap) -> NStr +//│ where +//│ 'K0 <: Eql['b] +//│ 'K <: Eql['a] + +showMap of NilMap +showMap of NilMap :+ ["b", 2] +showMap of NilMap :+ [1, "a"] :+ [2, "b"] +showMap of NilMap :+ [1, "a"] :+ [2, "b"] :+ [1, "c"] +//│ NStr +//│ res +//│ = '{}' +//│ res +//│ = '{ b -> 2 }' +//│ res +//│ = '{ 1 -> a, 2 -> b }' +//│ res +//│ = '{ 1 -> c, 2 -> b }' + +abstract class JsonValue: JsonNumber | JsonString | JsonArray | JsonObject | JsonBoolean | JsonNull +class JsonNumber(value: Num) extends JsonValue +class JsonString(value: Str) extends JsonValue +class JsonArray(value: List[JsonValue]) extends JsonValue +class JsonObject(value: ListMap[Str, JsonValue]) extends JsonValue +class JsonBoolean(value: Bool) extends JsonValue +module JsonNull extends JsonValue +//│ abstract class JsonValue: JsonArray | JsonBoolean | JsonNull | JsonNumber | JsonObject | JsonString +//│ class JsonNumber(value: Num) extends JsonValue +//│ class JsonString(value: Str) extends JsonValue +//│ class JsonArray(value: List[JsonValue]) extends JsonValue +//│ class JsonObject(value: ListMap[Str, JsonValue]) extends JsonValue +//│ class JsonBoolean(value: Bool) extends JsonValue +//│ module JsonNull extends JsonValue + +class ParserState(val text: NStr, val at: Int) { + fun drained: Bool = at === text.length + fun peek: Option[NStr] = if drained then None else Some(text.charAt(at)) + fun peekCode: Option[Int] = if drained then None else Some(text.charCodeAt(at)) + fun next: ParserState = if drained then this else ParserState(text, at + 1) + fun nextDigit: Option[[Num, ParserState]] = if peekCode is + Some(ch) and 48 <= ch and ch <= 57 then Some([ch - 48, next]) + else None + fun match(prefix: Str): Option[ParserState] = + let prefix' = String(prefix) + if + prefix'.length > text.length - at then None + text.startsWith(prefix', at) then Some(ParserState(text, at + prefix'.length)) + else None + fun rest: NStr = text.slice(at, text.length) +} +fun showParserState(state) = "ParserState(_, " ++ toString(state.at) ++ ")" +fun success: forall 't: ('t, ParserState) -> ParseResult['t] +fun success = (value, state) => Success(value, state) +fun failure: forall 't: Str -> ParseResult[nothing] +fun failure = error => Failure(error) +abstract class ParseResult[out T]: (Success[T] | Failure) { + virtual fun flatMap(f: (T, ParserState) -> ParseResult['U]): ParseResult['U] + virtual fun map(f: T -> 'U): ParseResult['U] +} +class Success[out T](value: T, state: ParserState) extends ParseResult[T] { + fun flatMap(f) = f(value, state) + fun map(f) = success(f(value), state) +} +class Failure(error: Str) extends ParseResult[nothing] { + fun flatMap(_) = failure(error) + fun map(_) = failure(error) +} +fun showParseResult(result) = if result is + Success(value, state) then "Success after " ++ toString(state.at) ++ ": " ++ toString(value) + Failure(error) then "Failure: " ++ toString(error) +//│ class ParserState(text: NStr, at: Int) { +//│ fun drained: Bool +//│ fun match: (prefix: Str) -> Option[ParserState] +//│ fun next: ParserState +//│ fun nextDigit: Option[[Num, ParserState]] +//│ fun peek: Option[NStr] +//│ fun peekCode: Option[Int] +//│ fun rest: NStr +//│ } +//│ fun showParserState: {at: anything} -> NStr +//│ fun success: forall 'T. ('T, ParserState) -> Success['T] +//│ fun failure: Str -> Failure +//│ abstract class ParseResult[T]: Failure | Success[T] { +//│ fun flatMap: forall 'U. (f: (T, ParserState) -> ParseResult['U]) -> ParseResult['U] +//│ fun map: forall 'U0. (f: T -> 'U0) -> ParseResult['U0] +//│ } +//│ class Success[T](value: T, state: ParserState) extends ParseResult { +//│ fun flatMap: forall 'a. ((T, ParserState) -> 'a) -> 'a +//│ fun map: forall 't. (T -> 't) -> ParseResult['t] +//│ } +//│ class Failure(error: Str) extends ParseResult { +//│ fun flatMap: anything -> ParseResult[nothing] +//│ fun map: anything -> ParseResult[nothing] +//│ } +//│ fun showParseResult: (Failure | Success[anything]) -> NStr +//│ fun success: forall 't0. ('t0, ParserState) -> ParseResult['t0] +//│ fun failure: Str -> ParseResult[nothing] + +fun isWhiteSpace(ch: NStr): Bool = + (ch === " ") || (ch === "\n") || (ch === "\r") || (ch === "\t") +fun skipWhiteSpace(state: ParserState): ParserState = if state.peek is + Some(ch) and isWhiteSpace(ch) then skipWhiteSpace(state.next) + else state +//│ fun isWhiteSpace: (ch: NStr) -> Bool +//│ fun skipWhiteSpace: (state: ParserState) -> ParserState + +(skipWhiteSpace of ParserState(String(" \n\r\t"), 0)).at +//│ Int +//│ res +//│ = 4 + +fun isDigit(ch) = sge(ch, "0") && sle(ch, "9") +//│ fun isDigit: Str -> Bool + +fun parseNumber(state: ParserState): ParseResult[Num] = + let toFraction(n) = n / (10 ** Math.ceil of Math.log10 of n) + let parseNegative(state): ParseResult[Bool] = if state.peek is + Some("-") then Success(true, state.next) + else Success(false, state) + // Parse one or more decimal digits + // -------------------------------- + let parseDigits(state): ParseResult[Num] = + // Parse remaining digits + let rec aux(acc, state) = if state.nextDigit is + Some([digit, state']) then aux((acc *. 10) +. digit, state') + None then [acc, state] + // Parse the first digit + if state.nextDigit is + Some([digit, state']) and aux(digit, state') is + [num, state''] then Success(num, state'') + None then Failure("expected one or more decimal digits") + // Parse the integral part of the number + // ------------------------------------- + let parseIntegral(state): ParseResult[Num] = if state.nextDigit is + Some([0, state']) then Success(0, state') + else parseDigits(state) + // Parse the fractional part of the number + // --------------------------------------- + let parseFraction(state): ParseResult[Num] = if state.peek is + Some(".") then parseDigits(state.next).map of toFraction + else Success(0, state) + let parseExponent(state): ParseResult[Num] = + let parseSign(state): ParseResult[Bool] = if state.peek is + Some("-") then Success(true, state.next) + Some("+") then Success(false, state.next) + else Success(false, state) + if state.peek is Some(e) and (e === "e") || (e === "E") then + parseSign(state.next).flatMap of (sign, state) => + parseDigits(state).map of exponent => + if sign then 10 ** (0 -. exponent) else 10 ** exponent + else + Success(1, state) + parseNegative(state).flatMap of (negative, state) => + parseIntegral(state).flatMap of (integral, state) => + parseFraction(state).flatMap of (fraction, state) => + parseExponent(state).flatMap of (exponent, state) => + let value = (integral +. fraction) *. exponent + Success(if negative then (0 -. value) else value, state) +//│ fun parseNumber: (state: ParserState) -> ParseResult[Num] + +showParseResult of parseNumber of ParserState of String("0"), 0 +showParseResult of parseNumber of ParserState of String("0234"), 0 +showParseResult of parseNumber of ParserState of String("123"), 0 +showParseResult of parseNumber of ParserState of String("12.34"), 0 +showParseResult of parseNumber of ParserState of String("1e10"), 0 +showParseResult of parseNumber of ParserState of String("1E5"), 0 +showParseResult of parseNumber of ParserState of String("1E-1"), 0 +showParseResult of parseNumber of ParserState of String("1E+1"), 0 +//│ NStr +//│ res +//│ = 'Success after 1: 0' +//│ res +//│ = 'Success after 1: 0' +//│ res +//│ = 'Success after 3: 123' +//│ res +//│ = 'Success after 5: 12.34' +//│ res +//│ = 'Success after 4: 10000000000' +//│ res +//│ = 'Success after 3: 100000' +//│ res +//│ = 'Success after 4: 0.1' +//│ res +//│ = 'Success after 4: 10' + +fun parseString(state: ParserState): ParseResult[Str] = + let rec parseCodePoint(n, acc, state) = if + n === 0 then Success(acc, state) + state.peekCode is Some(code) and + 48 <= code and code <= 57 then parseCodePoint(n - 1, acc * 16 + code - 48, state.next) + 65 <= code and code <= 70 then parseCodePoint(n - 1, acc * 16 + code - 55, state.next) + 97 <= code and code <= 102 then parseCodePoint(n - 1, acc * 16 + code - 87, state.next) + else Failure("expect " ++ toString(n) ++ " hex digit(s) instead of '" ++ String.fromCodePoint(code) ++ "'") + else Failure("expect " ++ toString(n) ++ " hex digit(s) instead of end of input") + let rec parseContent(acc, state) = if state.peek is + Some("\"") then Success(acc, state.next) + Some("\\") and + let state' = state.next + state'.peek is + Some("\"") then parseContent(acc ++ "\"", state'.next) + Some("\\") then parseContent(acc ++ "\\", state'.next) + Some("/") then parseContent(acc ++ "/", state'.next) + Some("b") then parseContent(acc ++ "\b", state'.next) + Some("f") then parseContent(acc ++ "\f", state'.next) + Some("n") then parseContent(acc ++ "\n", state'.next) + Some("r") then parseContent(acc ++ "\r", state'.next) + Some("t") then parseContent(acc ++ "\t", state'.next) + Some("u") then + parseCodePoint(4, 0, state'.next).flatMap of (codePoint, state) => + if codePoint < 0xD800 || 0xDFFF < codePoint then + parseContent(acc ++ String.fromCodePoint(codePoint), state) + else Failure("invalid code point") + else Failure("invalid escape sequence") + Some(ch) then parseContent(acc ++ ch, state.next) + None then Failure("expected '\"' instead of end of input") + if state.peek is + Some("\"") then parseContent("", state.next) + Some(ch) then Failure("expected '\"' instead of '" ++ ch ++ "'") + else Failure("expected '\"' instead of end of input") +//│ fun parseString: (state: ParserState) -> ParseResult[Str] + +showParseResult of parseString of ParserState of String("\"\""), 0 +showParseResult of parseString of ParserState of String("\"abc\""), 0 +showParseResult of parseString of ParserState of String("\"\\\"\""), 0 +showParseResult of parseString of ParserState of String("\"\\\\\""), 0 +showParseResult of parseString of ParserState of String("\"\\/\""), 0 +showParseResult of parseString of ParserState of String("\"\\b\""), 0 +showParseResult of parseString of ParserState of String("\""), 0 +showParseResult of parseString of ParserState of String("\"\\u\""), 0 +showParseResult of parseString of ParserState of String("\"\\u0\""), 0 +showParseResult of parseString of ParserState of String("\"\\u004c\""), 0 +//│ NStr +//│ res +//│ = 'Success after 2: ' +//│ res +//│ = 'Success after 5: abc' +//│ res +//│ = 'Success after 4: "' +//│ res +//│ = 'Success after 4: \\' +//│ res +//│ = 'Success after 4: /' +//│ res +//│ = 'Success after 4: \b' +//│ res +//│ = `Failure: expected '"' instead of end of input` +//│ res +//│ = `Failure: expect 4 hex digit(s) instead of '"'` +//│ res +//│ = `Failure: expect 3 hex digit(s) instead of '"'` +//│ res +//│ = 'Success after 8: L' + +fun parseTrue(state: ParserState): ParseResult[Bool] = + if state.match("true") is + Some(state) then Success(true, state) + None then Failure("expected 'true'") +fun parseFalse(state: ParserState): ParseResult[Bool] = + if state.match("false") is + Some(state) then Success(false, state) + None then Failure("expected 'false'") +fun parseNull(state: ParserState): ParseResult[()] = + if state.match("null") is + Some(state) then Success((), state) + None then Failure("expected 'null'") +//│ fun parseTrue: (state: ParserState) -> ParseResult[Bool] +//│ fun parseFalse: (state: ParserState) -> ParseResult[Bool] +//│ fun parseNull: (state: ParserState) -> ParseResult[()] + +fun parseObjectEntry(state: ParserState): ParseResult[[Str, JsonValue]] = + let state' = skipWhiteSpace(state) + parseString(state').flatMap of (key, state) => + let state' = skipWhiteSpace(state) + if state'.peek is + Some(":") then + parseValue(state'.next).flatMap of (value, state') => + Success([key, value], state') + Some(ch) then Failure("expected ':' instead of '" ++ ch ++ "'") + None then Failure("expected ':' instead of end of input") + else Failure("expected ':' instead of end of input") +fun parseObject(state: ParserState): ParseResult[ListMap[Str, JsonValue]] = + let rec parseObjectTail(acc, state) = + let state' = skipWhiteSpace(state) + if state'.peek is + Some(",") then + parseObjectEntry(state'.next).flatMap of (entry, state') => + if containsKey(acc, entry.0) then + Failure("duplicate key '" ++ toString(entry.0) ++ "'") + else + parseObjectTail(ConsMap(entry, acc), state') + Some("}") then Success(acc, state'.next) + Some(ch) then Failure("expected ',' or ']' instead of " ++ ch) + None then Failure("expected ',' or ']' instead of end of input") + let state' = skipWhiteSpace(state) + if state'.peek is + Some("}") then Success(NilMap, state'.next) + None then Failure("expected ',' or ']' instead of end of input") + else + parseObjectEntry(state').flatMap of (head, state) => + parseObjectTail(ConsMap(head, NilMap), state) +fun parseArray(state: ParserState): ParseResult[List[JsonValue]] = + let rec parseArrayTail(acc, state) = + let state' = skipWhiteSpace(state) + if state'.peek is + Some(",") then + parseValue(state'.next).flatMap of (value, state') => + parseArrayTail(value :: acc, state') + Some("]") then Success(reverse(acc), state'.next) + Some(ch) then Failure("expected ',' or ']' instead of " ++ ch) + None then Failure("expected ',' or ']' instead of end of input") + let state' = skipWhiteSpace(state) + if state'.peek is + Some("]") then Success(Nil, state'.next) + None then Failure("expected ',' or ']' instead of end of input") + else + parseValue(state').flatMap of (head, state) => + parseArrayTail(head :: Nil, state) +fun parseValue(state: ParserState): ParseResult[JsonValue] = + let state' = skipWhiteSpace(state) + if state'.peek is + Some(ch) and + ch === "\"" then parseString(state').map of JsonString + (ch === "-") || isDigit(ch) then parseNumber(state').map of JsonNumber + ch === "[" then parseArray(state'.next).map of JsonArray + ch === "{" then parseObject(state'.next).map of JsonObject + ch === "t" then parseTrue(state').map of JsonBoolean + ch === "f" then parseFalse(state').map of JsonBoolean + ch === "n" then parseNull(state').map of _ => JsonNull + else Failure("cannot recognize " ++ ch ++ " as the beginning of a JSON value") + None then Failure("expected a JSON value instead of end of input") +//│ fun parseObjectEntry: (state: ParserState) -> ParseResult[[Str, JsonValue]] +//│ fun parseObject: (state: ParserState) -> ParseResult[ListMap[Str, JsonValue]] +//│ fun parseArray: (state: ParserState) -> ParseResult[List[JsonValue]] +//│ fun parseValue: (state: ParserState) -> ParseResult[JsonValue] + +fun parse(source: Str): ParseResult[JsonValue] = + (parseValue of ParserState of String(source), 0).flatMap of (value, finalState) => + let shouldBeEnd = skipWhiteSpace of finalState + if shouldBeEnd.drained then + Success(value, shouldBeEnd) + else + Failure("expected end of input instead of: " ++ shouldBeEnd.rest) +//│ fun parse: (source: Str) -> ParseResult[JsonValue] + +fun stringify(value: JsonValue): Str = + let stringifyObject(map) = + let showEntry([k, v]) = "\"" ++ toString(k) ++ "\": " ++ stringify(v) + let rec aux(map) = if map is + ConsMap(last, NilMap) then showEntry(last) + ConsMap(head, tail) then showEntry(head) ++ ", " ++ aux(tail) + NilMap then "" + if map is NilMap then String("{}") else "{ " ++ aux(map) ++ " }" + if value is + JsonNumber(n) then toString(n) + JsonString(s) then "\"" ++ s ++ "\"" + JsonArray(xs) then "[" ++ join(", ", map(stringify, xs)) ++ "]" + JsonObject(m) then stringifyObject(m) + JsonBoolean(b) then if b then "true" else "false" + JsonNull then "null" +//│ fun stringify: (value: JsonValue) -> Str + +fun showResult(result) = if result is + Success(value, state) then "Success after " ++ toString(state.at) ++ ": " ++ stringify(value) + Failure(error) then "Failure: " ++ toString(error) +//│ fun showResult: (Failure | Success[JsonValue]) -> NStr + +// Simple tests. +showResult of parse of "null" +showResult of parse of "true" +showResult of parse of "false" +showResult of parse of "123" +showResult of parse of "\"abc\"" +showResult of parse of "[1, 2, 3]" +showResult of parse of "{\"a\": 1, \"b\": 2}" +showResult of parse of "nul" +showResult of parse of "[1, 3, 5" +showResult of parse of "[1, 3, 5]" +//│ NStr +//│ res +//│ = 'Success after 4: null' +//│ res +//│ = 'Success after 4: true' +//│ res +//│ = 'Success after 5: false' +//│ res +//│ = 'Success after 3: 123' +//│ res +//│ = 'Success after 5: "abc"' +//│ res +//│ = 'Success after 9: [1, 2, 3]' +//│ res +//│ = 'Success after 16: { "b": 2, "a": 1 }' +//│ res +//│ = "Failure: expected 'null'" +//│ res +//│ = "Failure: expected ',' or ']' instead of end of input" +//│ res +//│ = 'Success after 9: [1, 3, 5]' + +// Complicated tests. +showResult of parse of "{ \"origin\": { \"x\": 0, \"y\": 0 } }" +showResult of parse of "[ { \"origin\": { \"x\": 0, \"y\": 0 } , \"size\": { \"width\": 100, \"height\": 100 } } ]" +showResult of parse of "{\"id\":\"658f34f88882211aa8679240\",\"children\":[{\"name\":\"Jo Rosales\",\"age\":8},{\"name\":\"Shawn Burke\",\"age\":7},{\"name\":\"Gomez Guthrie\",\"age\":10},{\"name\":\"Tandy Christensen\",\"age\":9},{\"name\":\"Jody Langley\",\"age\":3}],\"currentJob\":{\"title\":\"Developer\",\"salary\":\"mask;\"},\"jobs\":[{\"title\":\"medic\",\"salary\":\"R$ 6.400,90\"},{\"title\":\"teacher\",\"salary\":\"R$ 7.960,31\"}],\"maxRunDistance\":14.7,\"cpf\":\"713.763.356-03\",\"cnpj\":\"33.385.435/0001-50\",\"pretendSalary\":\"R$ 9.247,29\",\"age\":63,\"gender\":\"male\",\"firstName\":\"Parker\",\"lastName\":\"Case\",\"phone\":\"+55 (83) 95023-7077\",\"address\":\"14 Orient Avenue - Harmon, Northern Mariana Islands, Myanmar.\",\"hairColor\":\"yellow\"}" +//│ NStr +//│ res +//│ = 'Success after 32: { "origin": { "y": 0, "x": 0 } }' +//│ res +//│ = 'Success after 82: [{ "size": { "height": 100, "width": 100 }, "origin": { "y": 0, "x": 0 } }]' +//│ res +//│ = 'Success after 647: { "hairColor": "yellow", "address": "14 Orient Avenue - Harmon, Northern Mariana Islands, Myanmar.", "phone": "+55 (83) 95023-7077", "lastName": "Case", "firstName": "Parker", "gender": "male", "age": 63, "pretendSalary": "R$ 9.247,29", "cnpj": "33.385.435/0001-50", "cpf": "713.763.356-03", "maxRunDistance": 14.7, "jobs": [{ "salary": "R$ 6.400,90", "title": "medic" }, { "salary": "R$ 7.960,31", "title": "teacher" }], "currentJob": { "salary": "mask;", "title": "Developer" }, "children": [{ "age": 8, "name": "Jo Rosales" }, { "age": 7, "name": "Shawn Burke" }, { "age": 10, "name": "Gomez Guthrie" }, { "age": 9, "name": "Tandy Christensen" }, { "age": 3, "name": "Jody Langley" }], "id": "658f34f88882211aa8679240" }' + +// Nice. From f81bf0b50e9695aa70c3d8c280ccee6037dcb73f Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Mon, 1 Jan 2024 21:48:23 +0800 Subject: [PATCH 041/143] Address minor issues mentioned in the PR --- .../scala/mlscript/pretyper/PreTyper.scala | 1 - .../mlscript/ucs/stages/Transformation.scala | 18 +++++++++--------- .../pretyper/ucs/SpecilizationCollision.mls | 5 +++-- .../test/diff/pretyper/ucs/examples/ULC.mls | 8 ++++---- .../diff/pretyper/ucs/patterns/Literals.mls | 13 ++++++++++++- 5 files changed, 28 insertions(+), 17 deletions(-) diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index 414c150f..22fb88f1 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -4,7 +4,6 @@ import collection.mutable.{Set => MutSet} import mlscript.ucs.DesugarUCS import symbol._ import mlscript._, utils._, shorthands._ -import mlscript.{Cls, Trt, Mxn, Als, Mod} import scala.annotation.tailrec import mlscript.Message, Message.MessageContext diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index 83851a77..3939084c 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -125,20 +125,20 @@ trait Transformation { self: mlscript.pretyper.Traceable => } }(_ => "transformPatternMatching ==>") + private def transformTupleTerm(tuple: Tup): Ls[Opt[Pattern]] = + tuple.fields.map { + case _ -> Fld(_, Var("_")) => N // Consider "_" as wildcard. + case _ -> Fld(_, t) => S(transformPattern(t)) + } + private def transformPattern(term: Term): Pattern = term match { case nme @ Var("true" | "false") => ConcretePattern(nme) case nme @ Var(name) if name.headOption.exists(_.isUpper) => ClassPattern(nme, N) case nme: Var => NamePattern(nme) case literal: Lit => LiteralPattern(literal) - case App(classNme @ Var(_), Tup(parameters)) => - ClassPattern(classNme, S(parameters.map { - case (_, Fld(_, Var("_"))) => N // Consider "_" as wildcard. - case (_, Fld(_, t)) => S(transformPattern(t)) - })) - case Tup(fields) => TuplePattern(fields.map { - case _ -> Fld(_, Var("_")) => N // Consider "_" as wildcard. - case _ -> Fld(_, t ) => S(transformPattern(t)) - }) + case App(classNme @ Var(_), parameters: Tup) => + ClassPattern(classNme, S(transformTupleTerm(parameters))) + case tuple: Tup => TuplePattern(transformTupleTerm(tuple)) case _ => println(s"unknown pattern: $term") throw new TransformException(msg"Unknown pattern", term.toLoc) diff --git a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls index 1a526497..f10c944f 100644 --- a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls +++ b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls @@ -48,8 +48,9 @@ fun example4(t, x) = Base(x) and p1(x) then x Derived(y) then y + x // ^ - // Oh no, x is captured by x from Base! - // Because the branch is absorbed by the previous one. + // Note that this branch will be absorbed by the previous one. As the + // previous branch shadows the variable `x`, a correct implementation + // should restore the original value of `x` in this branch. else 42 //│ fun example4: (Base, Int) -> Int diff --git a/shared/src/test/diff/pretyper/ucs/examples/ULC.mls b/shared/src/test/diff/pretyper/ucs/examples/ULC.mls index 357d5811..9add8993 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/ULC.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/ULC.mls @@ -329,11 +329,11 @@ fun combinations(n: Int, acc: List['T], alphabet: List['T], xs: List[Str]): Opti else Some(x) else search((x) => combinations(n - 1, x :: acc, alphabet, xs), alphabet) -//│ fun combinations: forall 'T 'A 'T0. (n: Int, acc: List['T], alphabet: List['T0], xs: List[Str]) -> Option[Str] +//│ fun combinations: forall 'T 'T0 'A. (n: Int, acc: List['T0], alphabet: List['T], xs: List[Str]) -> Option[Str] //│ where -//│ 'T0 <: 'T & 'A -//│ 'T :> 'A -//│ 'A := 'T +//│ 'T <: 'T0 & 'A +//│ 'T0 :> 'A +//│ 'A := 'T0 combinations(1, Nil, 1 :: 2 :: 3 :: Nil, Nil) |> showOption combinations(2, Nil, 1 :: 2 :: 3 :: Nil, Nil) |> showOption diff --git a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls index 4bec2657..fe9713c6 100644 --- a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls +++ b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls @@ -12,13 +12,24 @@ class Pair[A, B](x: A, y: B) fun f(x) = if x is Some(1) then true else false //│ fun f: (Object & ~#Some | Some[Eql[1]]) -> Bool +:e +// TODO: Proper diagnostic information reporting. +fun f(x) = if x is Some(1) then true +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.17: fun f(x) = if x is Some(1) then true +//│ ║ ^^^^ +//│ ╟── application of type `Bool` is not an instance of type `true` +//│ ║ l.17: fun f(x) = if x is Some(1) then true +//│ ╙── ^ +//│ fun f: Some[Eql[1]] -> true + fun g(x) = if x then 1 else 2 //│ fun g: Bool -> (1 | 2) :e fun test_must_be_boolean(x) = if 0 then 1 else 2 //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.19: fun test_must_be_boolean(x) = if 0 then 1 else 2 +//│ ║ l.30: fun test_must_be_boolean(x) = if 0 then 1 else 2 //│ ║ ^ //│ ╙── integer literal of type `0` is not an instance of type `Bool` //│ fun test_must_be_boolean: anything -> (1 | 2) From 9469158165b2fb1163b7a41ab264a5f3cc9b4204 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Tue, 2 Jan 2024 17:28:01 +0800 Subject: [PATCH 042/143] Add `Diagnosable` and report errors during transformation stage --- .../scala/mlscript/pretyper/Diagnosable.scala | 26 ++++ .../scala/mlscript/pretyper/PreTyper.scala | 15 +- .../main/scala/mlscript/ucs/DesugarUCS.scala | 2 +- .../mlscript/ucs/stages/Desugaring.scala | 20 +-- .../mlscript/ucs/stages/Transformation.scala | 136 +++++++++--------- .../src/main/scala/mlscript/ucs/syntax.scala | 26 +++- .../test/diff/pretyper/ucs/RecordPattern.mls | 2 +- .../src/test/scala/mlscript/DiffTests.scala | 2 +- 8 files changed, 138 insertions(+), 91 deletions(-) create mode 100644 shared/src/main/scala/mlscript/pretyper/Diagnosable.scala diff --git a/shared/src/main/scala/mlscript/pretyper/Diagnosable.scala b/shared/src/main/scala/mlscript/pretyper/Diagnosable.scala new file mode 100644 index 00000000..332da9f9 --- /dev/null +++ b/shared/src/main/scala/mlscript/pretyper/Diagnosable.scala @@ -0,0 +1,26 @@ +package mlscript.pretyper + +import scala.collection.mutable.Buffer +import mlscript.{Diagnostic, ErrorReport, Loc, Message, WarningReport}, Diagnostic.Source, Message.MessageContext +import mlscript.utils._, shorthands._ + +/** + * A trait containing a mutable buffer of diagnostics. + */ +trait Diagnosable { + private val diagnosticBuffer = Buffer.empty[Diagnostic] + + protected def raise(diagnostics: Diagnostic): Unit = + diagnosticBuffer += diagnostics + + protected def raiseMany(diagnostics: IterableOnce[Diagnostic]): Unit = + diagnosticBuffer ++= diagnostics + + protected def raiseError(source: Source, messages: (Message -> Opt[Loc])*): Unit = + raise(ErrorReport(messages.toList, newDefs = true, source)) + + protected def raiseWarning(source: Source, messages: (Message -> Opt[Loc])*): Unit = + raise(WarningReport(messages.toList, newDefs = true, source)) + + def getDiagnostics: Ls[Diagnostic] = diagnosticBuffer.toList +} diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index 22fb88f1..0fd49001 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -7,12 +7,9 @@ import mlscript._, utils._, shorthands._ import scala.annotation.tailrec import mlscript.Message, Message.MessageContext -class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with DesugarUCS { +class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with Diagnosable with DesugarUCS { import PreTyper._ - protected def raise(diagnostics: Diagnostic): Unit = () - protected def raise(diagnostics: Ls[Diagnostic]): Unit = () - private def extractParameters(fields: Term): Ls[LocalTermSymbol] = trace(s"extractParameters <== ${inspect.deep(fields)}") { fields match { @@ -218,6 +215,16 @@ object PreTyper { rec(Nil, parents) } + /** + * Extract types in class signatures. For example, for this piece of code + * ```mls + * abstract class Option[A]: Some[A] | None + * ``` + * this function returns, `Some` and `None`. + * + * @param ty a type obtained from `NuTypeDef.sig` + * @return a list of type names, without any parameters + */ def extractSignatureTypes(ty: Type): Ls[TypeName] = { @tailrec def rec(acc: Ls[TypeName], ty: Type): Ls[TypeName] = ty match { diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index 7143660c..aed60275 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -138,7 +138,7 @@ trait DesugarUCS extends Transformation val checked = println("STEP 4") val diagnostics = checkCoverage(postProcessed) println(s"Coverage checking result: ${diagnostics.size} errors") - raise(diagnostics) + raiseMany(diagnostics) } // Epilogue `if`.desugaredTerm = S(postProcessed) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 52058db3..0a812d87 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -133,17 +133,17 @@ trait Desugaring { self: PreTyper => private def flattenClassParameters( parentScrutineeVar: Var, parentClassLikeSymbol: TypeSymbol, - parameters: Ls[Opt[s.Pattern]] + parameters: Ls[s.Pattern] )(implicit context: Context): Ls[Opt[Var -> Opt[s.Pattern]]] = trace(s"flattenClassParameters <== ${parentScrutineeVar.name} is ${parentClassLikeSymbol.name}") { // Make it `lazy` so that it will not be created if all fields are wildcards. lazy val classPattern = parentScrutineeVar.getOrCreateScrutinee.getOrCreateClassPattern(parentClassLikeSymbol) parameters.iterator.zipWithIndex.map { - case (N, _) => N - case (S(s.NamePattern(name)), index) => + case (_: s.EmptyPattern, _) => N + case (s.NamePattern(name), index) => val subScrutinee = classPattern.getParameter(index).withAlias(name) S(name.withFreshSymbol.withScrutinee(subScrutinee) -> N) - case (S(parameterPattern @ (s.ClassPattern(_, _) | s.LiteralPattern(_) | s.TuplePattern(_))), index) => + case (parameterPattern @ (s.ClassPattern(_, _) | s.LiteralPattern(_) | s.TuplePattern(_)), index) => val subScrutineeVar = freshSubScrutinee(parentScrutineeVar, parentClassLikeSymbol.name, index) val symbol = new LocalTermSymbol(subScrutineeVar) symbol.addScrutinee(classPattern.getParameter(index).withAlias(subScrutineeVar)) @@ -251,14 +251,14 @@ trait Desugaring { self: PreTyper => } }() - private def flattenTupleFields(parentScrutineeVar: Var, fields: Ls[Opt[s.Pattern]])(implicit context: Context): Ls[Opt[Var -> Opt[s.Pattern]]] = { + private def flattenTupleFields(parentScrutineeVar: Var, fields: Ls[s.Pattern])(implicit context: Context): Ls[Opt[Var -> Opt[s.Pattern]]] = { // Make it `lazy` so that it will not be created if all fields are wildcards. lazy val tuplePattern = parentScrutineeVar.getOrCreateScrutinee.getOrCreateTuplePattern fields.iterator.zipWithIndex.map { - case (N, _) => N - case (S(s.NamePattern(name)), index) => + case (_: s.EmptyPattern, _) => N + case (s.NamePattern(name), index) => S(name.withFreshSymbol.withScrutinee(tuplePattern.getField(index)) -> N) - case (S(parameterPattern @ (s.ClassPattern(_, _) | s.LiteralPattern(_) | s.TuplePattern(_))), index) => + case (parameterPattern @ (s.ClassPattern(_, _) | s.LiteralPattern(_) | s.TuplePattern(_)), index) => val arity = fields.length val subScrutineeVar = freshSubScrutinee(parentScrutineeVar, s"Tuple$$$arity", index) val symbol = new LocalTermSymbol(subScrutineeVar) @@ -268,7 +268,7 @@ trait Desugaring { self: PreTyper => }.toList } - private def desugarTuplePattern(fields: Ls[Opt[s.Pattern]], scrutineeVar: Var, initialScope: Scope)(implicit context: Context): (Scope, c.Split => c.Split) = { + private def desugarTuplePattern(fields: Ls[s.Pattern], scrutineeVar: Var, initialScope: Scope)(implicit context: Context): (Scope, c.Split => c.Split) = { val scrutinee = scrutineeVar.getOrCreateScrutinee.withAlias(scrutineeVar) val nestedPatterns = flattenTupleFields(scrutineeVar, fields) val bindFields = nestedPatterns.iterator.zipWithIndex.foldRight[c.Split => c.Split](identity) { @@ -310,7 +310,7 @@ trait Desugaring { self: PreTyper => continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope + test.symbol, context) ) :: rec(scrutineeVar, tail) ) - case s.NamePattern(Var("_")) => + case s.EmptyPattern(_) | s.NamePattern(Var("_")) => desugarTermSplit(head.continuation)(PartialTerm.Empty, scope, context) ++ rec(scrutineeVar, tail) case s.NamePattern(nme) => // Create a symbol for the binding. diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index 3939084c..14f613c3 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -3,6 +3,8 @@ package mlscript.ucs.stages import mlscript.ucs.helpers import mlscript.{If, IfBody, IfBlock, IfElse, IfLet, IfOpApp, IfOpsApp, IfThen} import mlscript.{Term, Var, App, Tup, Lit, Fld, Loc} +import mlscript.Diagnostic.PreTyping +import mlscript.pretyper.{Diagnosable, Traceable} import mlscript.ucs.syntax._ import mlscript.Message, Message._ import mlscript.utils._, shorthands._ @@ -17,70 +19,63 @@ import scala.collection.immutable * The AST in the paper is more flexible. For example, it allows interleaved * `let` bindings in operator splits. */ -trait Transformation { self: mlscript.pretyper.Traceable => +trait Transformation { self: Traceable with Diagnosable => import Transformation._ + /** The entry point of transformation. */ def transform(`if`: If): TermSplit = transformIfBody(`if`.body) ++ `if`.els.fold(Split.empty)(Split.default) + /** + * Transform a conjunction of terms into a nested split. The conjunction is + * of the following form. + * ``` + * conjunction ::= term "is" term conjunction-tail + * | "_" conjunction-tail + * | term conjunction-tail + * conjunction-tail ::= "and" conjunction + * | ε + * ``` + * @param init a list of term representing the conjunction + * @param last the innermost split we should take if all terms of the + * conjunction work + * @return + */ + private def transformConjunction[B <: Branch](init: Ls[Term], last: TermSplit, skipWildcard: Bool): TermSplit = + init.foldRight(last) { + case (scrutinee is pattern, following) => + val branch = PatternBranch(transformPattern(pattern), following).toSplit + TermBranch.Match(scrutinee, branch).toSplit + // Maybe we don't need `skipWildcard` flag and we should take care of + // wildcards at _this_ level in all cases. + case (Var("_"), following) if skipWildcard => following + case (test, following) => TermBranch.Boolean(test, following).toSplit + } + private def transformIfBody(body: IfBody): TermSplit = trace(s"transformIfBody <== ${inspect.shallow(body)}") { body match { - case IfThen(expr, rhs) => - splitAnd(expr).foldRight(Split.then(rhs)) { - case (OperatorIs(scrutinee, pattern), acc) => - TermBranch.Match(scrutinee, PatternBranch(transformPattern(pattern), acc) |> Split.single) |> Split.single - case (Var("_"), acc) => acc - case (test, acc) => TermBranch.Boolean(test, acc) |> Split.single - } + case IfThen(expr, rhs) => transformConjunction(splitAnd(expr), Split.then(rhs), true) case IfLet(isRec, name, rhs, body) => rare case IfElse(expr) => Split.then(expr) case IfOpApp(lhs, Var("is"), rhs) => - splitAnd(lhs) match { - case tests :+ scrutinee => - tests.foldRight[TermSplit](TermBranch.Match(scrutinee, transformPatternMatching(rhs)) |> Split.single) { - case (OperatorIs(scrutinee, pattern), acc) => - TermBranch.Match(scrutinee, PatternBranch(transformPattern(pattern), acc) |> Split.single) |> Split.single - case (test, acc) => TermBranch.Boolean(test, acc) |> Split.single - } - case _ => rare - } - case IfOpApp(lhs, Var("and"), rhs) => - splitAnd(lhs).foldRight(transformIfBody(rhs)) { - case (OperatorIs(scrutinee, pattern), acc) => - TermBranch.Match(scrutinee, PatternBranch(transformPattern(pattern), acc) |> Split.single) |> Split.single - case (Var("_"), acc) => acc - case (test, acc) => TermBranch.Boolean(test, acc) |> Split.single - } + val (tests, scrutinee) = extractLast(splitAnd(lhs)) + transformConjunction(tests, TermBranch.Match(scrutinee, transformPatternMatching(rhs)).toSplit, false) + case IfOpApp(lhs, Var("and"), rhs) => transformConjunction(splitAnd(lhs), transformIfBody(rhs), true) case IfOpApp(lhs, op, rhs) => - splitAnd(lhs) match { - case init :+ last => - val first = TermBranch.Left(last, OperatorBranch.Binary(op, transformIfBody(rhs)) |> Split.single) |> Split.single - init.foldRight[TermSplit](first) { - case (OperatorIs(scrutinee, pattern), acc) => - TermBranch.Match(scrutinee, PatternBranch(transformPattern(pattern), acc) |> Split.single) |> Split.single - case (test, acc) => TermBranch.Boolean(test, acc) |> Split.single - } - case _ => rare - } + val (init, last) = extractLast(splitAnd(lhs)) + transformConjunction(init, TermBranch.Left(last, OperatorBranch.Binary(op, transformIfBody(rhs)).toSplit).toSplit, false) case IfBlock(lines) => lines.foldLeft(Split.empty[TermBranch]) { case (acc, L(body)) => acc ++ transformIfBody(body) case (acc, R(NuFunDef(S(rec), nme, _, _, L(rhs)))) => acc ++ Split.Let(rec, nme, rhs, Split.Nil) case (acc, R(statement)) => - throw new TransformException(msg"Unexpected statement in an if block", statement.toLoc) + raiseError(PreTyping, msg"Unexpected statement in an if block" -> statement.toLoc) + acc } case IfOpsApp(lhs, opsRhss) => - splitAnd(lhs) match { - case init :+ last => - val first = TermBranch.Left(last, Split.from(opsRhss.map(transformOperatorBranch))) |> Split.single - init.foldRight[TermSplit](first) { - case (OperatorIs(scrutinee, pattern), acc) => - TermBranch.Match(scrutinee, PatternBranch(transformPattern(pattern), acc) |> Split.single) |> Split.single - case (test, acc) => TermBranch.Boolean(test, acc) |> Split.single - } - case _ => rare - } + val (init, last) = extractLast(splitAnd(lhs)) + transformConjunction(init, TermBranch.Left(last, Split.from(opsRhss.map(transformOperatorBranch))).toSplit, false) } }(_ => "transformIfBody ==> ") @@ -99,17 +94,17 @@ trait Transformation { self: mlscript.pretyper.Traceable => case IfThen(expr, rhs) => separatePattern(expr) match { case (pattern, S(extraTest)) => - PatternBranch(pattern, transformIfBody(IfThen(extraTest, rhs))) |> Split.single + PatternBranch(pattern, transformIfBody(IfThen(extraTest, rhs))).toSplit case (pattern, N) => - PatternBranch(pattern, Split.default(rhs)) |> Split.single + PatternBranch(pattern, Split.default(rhs)).toSplit } case IfOpApp(lhs, Var("and"), rhs) => println(s"lhs: ${inspect.deep(lhs)}") separatePattern(lhs) match { case (pattern, S(extraTest)) => - PatternBranch(pattern, TermBranch.Boolean(extraTest, transformIfBody(rhs)) |> Split.single) |> Split.single + PatternBranch(pattern, TermBranch.Boolean(extraTest, transformIfBody(rhs)).toSplit).toSplit case (pattern, N) => - PatternBranch(pattern, transformIfBody(rhs)) |> Split.single + PatternBranch(pattern, transformIfBody(rhs)).toSplit } case IfOpApp(lhs, op, rhs) => ??? // <-- Syntactic split of patterns are not supported. case IfOpsApp(lhs, opsRhss) => ??? // <-- Syntactic split of patterns are not supported. @@ -119,19 +114,25 @@ trait Transformation { self: mlscript.pretyper.Traceable => case (acc, R(NuFunDef(S(rec), nme, _, _, L(rhs)))) => acc ++ Split.Let(rec, nme, rhs, Split.Nil) case (acc, R(statement)) => - throw new TransformException(msg"Unexpected statement in an if block", statement.toLoc) + raiseError(PreTyping, msg"Unexpected statement in an if block" -> statement.toLoc) + acc } case IfElse(expr) => Split.default(expr) } }(_ => "transformPatternMatching ==>") - private def transformTupleTerm(tuple: Tup): Ls[Opt[Pattern]] = - tuple.fields.map { - case _ -> Fld(_, Var("_")) => N // Consider "_" as wildcard. - case _ -> Fld(_, t) => S(transformPattern(t)) - } + private def transformTupleTerm(tuple: Tup): Ls[Pattern] = + tuple.fields.map(_._2.value |> transformPattern) + /** + * If we know the `term` represents a pattern, we can transform it to a + * pattern with this function. + * + * @param term the term representing a pattern + * @return + */ private def transformPattern(term: Term): Pattern = term match { + case wildcard @ Var("_") => EmptyPattern(wildcard) // The case for wildcard. case nme @ Var("true" | "false") => ConcretePattern(nme) case nme @ Var(name) if name.headOption.exists(_.isUpper) => ClassPattern(nme, N) case nme: Var => NamePattern(nme) @@ -139,9 +140,10 @@ trait Transformation { self: mlscript.pretyper.Traceable => case App(classNme @ Var(_), parameters: Tup) => ClassPattern(classNme, S(transformTupleTerm(parameters))) case tuple: Tup => TuplePattern(transformTupleTerm(tuple)) - case _ => - println(s"unknown pattern: $term") - throw new TransformException(msg"Unknown pattern", term.toLoc) + case other => + println(s"other $other") + raiseError(PreTyping, msg"Unknown pattern ${other.toString}" -> other.toLoc) + EmptyPattern(other) } private def separatePattern(term: Term): (Pattern, Opt[Term]) = { @@ -151,8 +153,6 @@ trait Transformation { self: mlscript.pretyper.Traceable => (transformPattern(rawPattern), extraTest) } - private def rare: Nothing = throw new TransformException(msg"Wow, a rare case.", N) - private def splitAnd(t: Term): Ls[Term] = trace(s"splitAnd <== ${inspect.deep(t)}") { t match { case App( @@ -169,15 +169,17 @@ trait Transformation { self: mlscript.pretyper.Traceable => } object Transformation { - private object OperatorIs { + private def rare: Nothing = lastWords("found a very rare case during desugaring UCS terms") + + private def extractLast[T](xs: List[T]): (List[T], T) = xs match { + case init :+ last => init -> last + case _ => rare + } + + private object is { def unapply(term: Term): Opt[(Term, Term)] = term match { - // case App(App(Var("is"), Tup(_ -> Fld(_, scrutinee) :: Nil)), Tup(_ -> Fld(_, pattern) :: Nil)) if !useNewDefs => S(scrutinee -> pattern) case App(Var("is"), PlainTup(scrutinee, pattern)) => S(scrutinee -> pattern) case _ => N } } - - class TransformException(val messages: Ls[Message -> Opt[Loc]]) extends Throwable { - def this(message: Message, location: Opt[Loc]) = this(message -> location :: Nil) - } -} \ No newline at end of file +} diff --git a/shared/src/main/scala/mlscript/ucs/syntax.scala b/shared/src/main/scala/mlscript/ucs/syntax.scala index 433cd835..cbf18797 100644 --- a/shared/src/main/scala/mlscript/ucs/syntax.scala +++ b/shared/src/main/scala/mlscript/ucs/syntax.scala @@ -12,10 +12,11 @@ package object syntax { case LiteralPattern(literal) => literal.toString case ConcretePattern(nme) => s"`${nme.name}`" case NamePattern(nme) => nme.toString + case EmptyPattern(_) => "•" case ClassPattern(Var(name), N) => name case ClassPattern(Var(name), S(parameters)) => - parameters.iterator.map(_.fold("_")(_.toString)).mkString(s"$name(", ", ", ")") - case TuplePattern(fields) => fields.iterator.map(_.fold("_")(_.toString)).mkString("(", ", ", ")") + parameters.mkString(s"$name(", ", ", ")") + case TuplePattern(fields) => fields.mkString("(", ", ", ")") case RecordPattern(Nil) => "{}" case RecordPattern(entries) => entries.iterator.map { case (nme, als) => s"$nme: $als" }.mkString("{ ", ", ", " }") } @@ -32,11 +33,18 @@ package object syntax { final case class NamePattern(nme: Var) extends Pattern { override def children: List[Located] = nme :: Nil } - final case class ClassPattern(val nme: Var, val parameters: Opt[List[Opt[Pattern]]]) extends Pattern { - override def children: List[Located] = nme :: parameters.fold(List.empty[Located])(_.flatten) + /** + * Represents wildcard patterns or missing patterns which match everything. + * Should be transformed from `Var("_")` or unrecognized terms. + */ + final case class EmptyPattern(source: Term) extends Pattern { + override def children: List[Located] = source :: Nil } - final case class TuplePattern(fields: List[Opt[Pattern]]) extends Pattern { - override def children: List[Located] = fields.flatten + final case class ClassPattern(val nme: Var, val parameters: Opt[List[Pattern]]) extends Pattern { + override def children: List[Located] = nme :: parameters.getOrElse(Nil) + } + final case class TuplePattern(fields: List[Pattern]) extends Pattern { + override def children: List[Located] = fields } final case class RecordPattern(entries: List[(Var -> Pattern)]) extends Pattern { override def children: List[Located] = entries.iterator.flatMap { case (nme, als) => nme :: als :: Nil }.toList @@ -76,7 +84,9 @@ package object syntax { sealed abstract class Branch extends Located - sealed abstract class TermBranch extends Branch + sealed abstract class TermBranch extends Branch { + final def toSplit: TermSplit = Split.single(this) + } object TermBranch { final case class Boolean(test: Term, continuation: TermSplit) extends TermBranch { override def children: List[Located] = test :: continuation :: Nil @@ -92,6 +102,7 @@ package object syntax { sealed abstract class OperatorBranch extends Branch { val operator: Var + final def toSplit: OperatorSplit = Split.single(this) } object OperatorBranch { final case class Match(override val operator: Var, continuation: PatternSplit) extends OperatorBranch { @@ -105,6 +116,7 @@ package object syntax { final case class PatternBranch(val pattern: Pattern, val continuation: TermSplit) extends Branch { override def children: List[Located] = pattern :: continuation :: Nil + final def toSplit: PatternSplit = Split.single(this) } type PatternSplit = Split[PatternBranch] } diff --git a/shared/src/test/diff/pretyper/ucs/RecordPattern.mls b/shared/src/test/diff/pretyper/ucs/RecordPattern.mls index ed86bd8a..c686cff3 100644 --- a/shared/src/test/diff/pretyper/ucs/RecordPattern.mls +++ b/shared/src/test/diff/pretyper/ucs/RecordPattern.mls @@ -5,4 +5,4 @@ fun take_1(p) = if p is { x, y } then x + y else 0 -//│ /!!!\ Uncaught error: mlscript.ucs.stages.Transformation$TransformException +//│ /!!!\ Uncaught error: java.lang.Exception: Variable x not found in scope diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index 99898c74..a3f247cd 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -525,11 +525,11 @@ class DiffTests val rootTypingUnit = TypingUnit(p.tops) if (usePreTyper) { val preTyper = new PreTyper(mode.dbgPreTyper) { - override protected def raise(diagnostics: Ls[Diagnostic]): Unit = report(diagnostics) override def emitString(str: String): Unit = output(str) } // This should be passed to code generation somehow. preTyperScope = preTyper.process(rootTypingUnit, preTyperScope, "")._1 + report(preTyper.getDiagnostics) } val tpd = typer.typeTypingUnit(rootTypingUnit, N)(ctx, raise, vars) From 13ddcf037c36ceeec2b9d21c30daa73df1dcac19 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Tue, 2 Jan 2024 17:59:44 +0800 Subject: [PATCH 043/143] Report errors during desugaring stage --- .../scala/mlscript/pretyper/PreTyper.scala | 17 +++++----- .../mlscript/ucs/stages/Desugaring.scala | 22 +++++++++---- .../test/diff/pretyper/ucs/RecordPattern.mls | 21 ++++++++++-- .../pretyper/ucs/SpecilizationCollision.mls | 18 ++++++++-- .../test/diff/pretyper/ucs/TuplePattern.mls | 9 ----- .../{Overlaps.mls => coverage/Refinement.mls} | 26 +++++++-------- .../pretyper/ucs/patterns/SimpleTuple.mls | 33 +++++++++++++++++++ .../ucs/{ => stages}/TransfromUCS.mls | 0 8 files changed, 106 insertions(+), 40 deletions(-) delete mode 100644 shared/src/test/diff/pretyper/ucs/TuplePattern.mls rename shared/src/test/diff/pretyper/ucs/{Overlaps.mls => coverage/Refinement.mls} (81%) rename shared/src/test/diff/pretyper/ucs/{ => stages}/TransfromUCS.mls (100%) diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index 0fd49001..9b60a5ac 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -1,11 +1,9 @@ package mlscript.pretyper import collection.mutable.{Set => MutSet} -import mlscript.ucs.DesugarUCS import symbol._ -import mlscript._, utils._, shorthands._ +import mlscript._, utils._, shorthands._, Diagnostic.PreTyping, Message.MessageContext, ucs.DesugarUCS import scala.annotation.tailrec -import mlscript.Message, Message.MessageContext class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with Diagnosable with DesugarUCS { import PreTyper._ @@ -52,8 +50,10 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D case S(sym: ClassSymbol) => println(s"Resolve variable $v to a class.") v.symbol = sym - case S(_) => throw new Exception(s"Name $v refers to a type") - case N => throw new Exception(s"Variable $v not found in scope") + case S(_) => + raiseError(PreTyping, msg"Name ${v.name} refers to a type" -> v.toLoc) + case N => + raiseError(PreTyping, msg"Variable ${v.name} not found in scope" -> v.toLoc) } } }() @@ -63,9 +63,10 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D v.symbolOption match { case N => resolveVar(v) case S(symbol) => scope.getSymbols(v.name) match { - case Nil => throw new Exception(s"Variable $v not found in scope. It is possibly a free variable.") + case Nil => raiseError(PreTyping, msg"Variable ${v.name} not found in scope. It is possibly a free variable." -> v.toLoc) case symbols if symbols.contains(symbol) => () - case _ => throw new Exception(s"Variable $v refers to a different symbol") + case _ => + raiseError(PreTyping, msg"Variable ${v.name} refers to different symbols." -> v.toLoc) } } }() @@ -223,7 +224,7 @@ object PreTyper { * this function returns, `Some` and `None`. * * @param ty a type obtained from `NuTypeDef.sig` - * @return a list of type names, without any parameters + * @return a list of type names, without any p */ def extractSignatureTypes(ty: Type): Ls[TypeName] = { @tailrec diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 0a812d87..c9a38c73 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -7,7 +7,7 @@ import mlscript.ucs.helpers.mkBinOp import mlscript.utils._, shorthands._ import mlscript.pretyper.symbol._ import mlscript.pretyper.{PreTyper, Scope} -import mlscript.ucs.DesugaringException +import mlscript.Diagnostic.PreTyping import mlscript.Message, Message.MessageContext /** @@ -148,7 +148,9 @@ trait Desugaring { self: PreTyper => val symbol = new LocalTermSymbol(subScrutineeVar) symbol.addScrutinee(classPattern.getParameter(index).withAlias(subScrutineeVar)) S(subScrutineeVar.withSymbol(symbol) -> S(parameterPattern)) - case _ => ??? // Other patterns are not implemented yet. + case (pattern, _) => + raiseError(PreTyping, msg"unsupported pattern" -> pattern.toLoc) + N }.toList }(r => s"flattenClassParameters ==> ${r.mkString(", ")}") @@ -244,7 +246,9 @@ trait Desugaring { self: PreTyper => val (scopeWithNestedAll, bindNestedAll) = desugarTuplePattern(fields, nme, scope) (scopeWithNestedAll, bindNestedAll.andThen(bindPrevious)) // Well, other patterns are not supported yet. - case (acc, S((nme, pattern))) => ??? + case (acc, S(nme -> S(pattern))) => + raiseError(PreTyping, msg"unsupported pattern is" -> pattern.toLoc) + acc // If this parameter is empty (e.g. produced by wildcard), then we do // nothing and pass on scope and binder. case (acc, N) => acc @@ -264,7 +268,9 @@ trait Desugaring { self: PreTyper => val symbol = new LocalTermSymbol(subScrutineeVar) symbol.addScrutinee(tuplePattern.getField(index).withAlias(subScrutineeVar)) S(subScrutineeVar.withSymbol(symbol) -> S(parameterPattern)) - case _ => ??? + case (pattern, _) => + raiseError(PreTyping, msg"unsupported pattern" -> pattern.toLoc) + N }.toList } @@ -285,7 +291,9 @@ trait Desugaring { self: PreTyper => def rec(scrutineeVar: Var, split: s.PatternSplit)(implicit scope: Scope): c.Split = split match { case s.Split.Cons(head, tail) => head.pattern match { - case s.AliasPattern(nme, pattern) => ??? + case pattern @ s.AliasPattern(_, _) => + raiseError(PreTyping, msg"alias pattern is not supported for now" -> pattern.toLoc) + rec(scrutineeVar, tail) case s.LiteralPattern(literal) => val test = context.freshTest().withFreshSymbol c.Split.Let( @@ -338,7 +346,9 @@ trait Desugaring { self: PreTyper => } else { withBindings ++ rec(scrutineeVar, tail) } - case s.RecordPattern(entries) => ??? + case pattern @ s.RecordPattern(_) => + raiseError(PreTyping, msg"record pattern is not supported for now" -> pattern.toLoc) + rec(scrutineeVar, tail) } case s.Split.Let(isRec, nme, rhs, tail) => c.Split.Let(isRec, nme, rhs, rec(scrutineeVar, tail)(scope + nme.withFreshSymbol.symbol)) // <-- Weird use. diff --git a/shared/src/test/diff/pretyper/ucs/RecordPattern.mls b/shared/src/test/diff/pretyper/ucs/RecordPattern.mls index c686cff3..61500fd8 100644 --- a/shared/src/test/diff/pretyper/ucs/RecordPattern.mls +++ b/shared/src/test/diff/pretyper/ucs/RecordPattern.mls @@ -1,8 +1,25 @@ :PreTyper -// FIXME +:e fun take_1(p) = if p is { x, y } then x + y else 0 -//│ /!!!\ Uncaught error: java.lang.Exception: Variable x not found in scope +//│ ╔══[ERROR] Unknown pattern '{' {x: x, y: y} '}' +//│ ║ l.6: { x, y } then x + y +//│ ╙── ^^^^^^^^ +//│ ╔══[ERROR] Variable x not found in scope +//│ ║ l.6: { x, y } then x + y +//│ ╙── ^ +//│ ╔══[ERROR] Variable y not found in scope +//│ ║ l.6: { x, y } then x + y +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.6: { x, y } then x + y +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: y +//│ ║ l.6: { x, y } then x + y +//│ ╙── ^ +//│ fun take_1: anything -> Int +//│ Code generation encountered an error: +//│ unresolved symbol x diff --git a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls index f10c944f..19a29b08 100644 --- a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls +++ b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls @@ -21,13 +21,27 @@ fun example1(p) = else "nah" //│ fun example1: (Object & ~#Pair | Pair[Num, Num]) -> ("both negative" | "both positive" | "nah") -// FIXME: The error should be handled gracefully. +:e fun example2(p) = if p is Pair(x, y) and p1(x) and p1(y) then "both negative" Pair(a, b) and p2(a) and p2(b) then x + y else "nah" -//│ /!!!\ Uncaught error: java.lang.Exception: Variable x not found in scope +//│ ╔══[ERROR] Variable x not found in scope +//│ ║ l.28: Pair(a, b) and p2(a) and p2(b) then x + y +//│ ╙── ^ +//│ ╔══[ERROR] Variable y not found in scope +//│ ║ l.28: Pair(a, b) and p2(a) and p2(b) then x + y +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.27: Pair(x, y) and p1(x) and p1(y) then "both negative" +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: y +//│ ║ l.27: Pair(x, y) and p1(x) and p1(y) then "both negative" +//│ ╙── ^ +//│ fun example2: (Object & ~#Pair | Pair[Num, Num]) -> ("both negative" | "nah" | Int) +//│ Code generation encountered an error: +//│ unresolved symbol y // Next, let's check the name collision between a class and its super class. diff --git a/shared/src/test/diff/pretyper/ucs/TuplePattern.mls b/shared/src/test/diff/pretyper/ucs/TuplePattern.mls deleted file mode 100644 index 880c7908..00000000 --- a/shared/src/test/diff/pretyper/ucs/TuplePattern.mls +++ /dev/null @@ -1,9 +0,0 @@ -:PreTyper - -fun flex(x) = - if x is - [a, b, c] then - a + b + c - else - 0 -//│ fun flex: {0: Int, 1: Int, 2: Int} -> Int diff --git a/shared/src/test/diff/pretyper/ucs/Overlaps.mls b/shared/src/test/diff/pretyper/ucs/coverage/Refinement.mls similarity index 81% rename from shared/src/test/diff/pretyper/ucs/Overlaps.mls rename to shared/src/test/diff/pretyper/ucs/coverage/Refinement.mls index fb78fa19..a8913a37 100644 --- a/shared/src/test/diff/pretyper/ucs/Overlaps.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/Refinement.mls @@ -52,25 +52,25 @@ fun missing_a_case(x: Shape) = // TODO: Why doesn't `Shape` match `Circle | Rectangle | LineSegment`? fun countLineSegments(x) = if x is - Shape and hidden(x) then "bro" - Rectangle(_, _, _) then "bro" - LineSegment(_, _) then "bro" - Circle(_, _) then "bro" + Shape and hidden(x) then "1" + Rectangle(_, _, _) then "2" + LineSegment(_, _) then "3" + Circle(_, _) then "4" //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.54: if x is //│ ║ ^^^^ -//│ ║ l.55: Shape and hidden(x) then "bro" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.56: Rectangle(_, _, _) then "bro" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.57: LineSegment(_, _) then "bro" +//│ ║ l.55: Shape and hidden(x) then "1" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.58: Circle(_, _) then "bro" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.56: Rectangle(_, _, _) then "2" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.57: LineSegment(_, _) then "3" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.58: Circle(_, _) then "4" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── class pattern of type `Shape` does not match type `Circle | LineSegment | Rectangle` -//│ ║ l.55: Shape and hidden(x) then "bro" +//│ ║ l.55: Shape and hidden(x) then "1" //│ ║ ^^^^^ //│ ╟── but it flows into reference with expected type `Circle | LineSegment | Rectangle` //│ ║ l.54: if x is //│ ╙── ^ -//│ fun countLineSegments: Shape -> "bro" +//│ fun countLineSegments: Shape -> ("1" | "2" | "3" | "4") diff --git a/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls b/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls index 3b0e1996..7939013b 100644 --- a/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls +++ b/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls @@ -92,3 +92,36 @@ working_cases([0, 0]) //│ Int | error //│ res //│ = 0 + +fun not_working(x) = + if x is + [a, b, c] then + a + b + c + else + 0 +//│ fun not_working: {0: Int, 1: Int, 2: Int} -> Int + +not_working([1, 2, 3]) +//│ Int +//│ res +//│ = 6 + +:e +not_working([1, 2]) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.110: not_working([1, 2]) +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ╟── tuple literal of type `{0: 1, 1: 2}` does not have field '2' +//│ ║ l.110: not_working([1, 2]) +//│ ║ ^^^^^^ +//│ ╟── Note: constraint arises from field selection: +//│ ║ l.97: if x is +//│ ║ ^^^^ +//│ ║ l.98: [a, b, c] then +//│ ║ ^^^^^^^^^^^^ +//│ ╟── from reference: +//│ ║ l.97: if x is +//│ ╙── ^ +//│ Int | error +//│ res +//│ = NaN diff --git a/shared/src/test/diff/pretyper/ucs/TransfromUCS.mls b/shared/src/test/diff/pretyper/ucs/stages/TransfromUCS.mls similarity index 100% rename from shared/src/test/diff/pretyper/ucs/TransfromUCS.mls rename to shared/src/test/diff/pretyper/ucs/stages/TransfromUCS.mls From d6bf3a38642274f7384f78d5a53ac1a69491a6d2 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Tue, 2 Jan 2024 18:05:38 +0800 Subject: [PATCH 044/143] Migrate all TAPL examples --- .../ucs/examples/STLC.mls} | 5 +- shared/src/test/diff/tapl/NuUntyped.mls | 448 ------------ shared/src/test/diff/tapl/SimplyTyped.mls | 503 -------------- shared/src/test/diff/tapl/Untyped.mls | 644 ------------------ 4 files changed, 2 insertions(+), 1598 deletions(-) rename shared/src/test/diff/{tapl/NuSimplyTyped.mls => pretyper/ucs/examples/STLC.mls} (97%) delete mode 100644 shared/src/test/diff/tapl/NuUntyped.mls delete mode 100644 shared/src/test/diff/tapl/SimplyTyped.mls delete mode 100644 shared/src/test/diff/tapl/Untyped.mls diff --git a/shared/src/test/diff/tapl/NuSimplyTyped.mls b/shared/src/test/diff/pretyper/ucs/examples/STLC.mls similarity index 97% rename from shared/src/test/diff/tapl/NuSimplyTyped.mls rename to shared/src/test/diff/pretyper/ucs/examples/STLC.mls index bc71deb0..c0b8868c 100644 --- a/shared/src/test/diff/tapl/NuSimplyTyped.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/STLC.mls @@ -1,5 +1,4 @@ -:NewParser -:NewDefs +:PreTyper fun (++) concatOp(a, b) = concat(a)(b) //│ fun (++) concatOp: (Str, Str) -> Str @@ -97,7 +96,7 @@ fun typeEqual(t1, t2) = t1 is FunctionType(lhs1, rhs1) and t2 is FunctionType(lhs2, rhs2) then typeEqual(lhs1, lhs2) and typeEqual(rhs1, rhs2) _ then false -//│ fun typeEqual: (Object, Object) -> Bool +//│ fun typeEqual: (FunctionType | Object & ~#FunctionType & ~#PrimitiveType | PrimitiveType, Object) -> Bool fun showTerm(t) = if t is diff --git a/shared/src/test/diff/tapl/NuUntyped.mls b/shared/src/test/diff/tapl/NuUntyped.mls deleted file mode 100644 index 29ff0ec9..00000000 --- a/shared/src/test/diff/tapl/NuUntyped.mls +++ /dev/null @@ -1,448 +0,0 @@ -:NewDefs - -fun (++) concatOp(a, b) = concat(a)(b) -//│ fun (++) concatOp: (Str, Str) -> Str - -fun par(a) = "(" ++ a ++ ")" -//│ fun par: Str -> Str - -declare fun String: nothing -//│ fun String: nothing - -let makeString: anything => { length: Int, charCodeAt: Int => Int } = String -let StringInstance: { fromCharCode: Int => Str } = String -//│ let makeString: anything -> {charCodeAt: Int -> Int, length: Int} -//│ let StringInstance: {fromCharCode: Int -> Str} -//│ makeString -//│ = [Function: String] -//│ StringInstance -//│ = [Function: String] - -let anythingToString = toString -fun fromCharCode(n: Int) = StringInstance.fromCharCode(n) -fun stringCharCodeAt(s: Str, i) = makeString(s).charCodeAt(i) -fun stringLength(s: Str) = makeString(s).length -//│ let anythingToString: anything -> Str -//│ fun fromCharCode: (n: Int) -> Str -//│ fun stringCharCodeAt: (s: Str, Int) -> Int -//│ fun stringLength: (s: Str) -> Int -//│ anythingToString -//│ = [Function: toString] - -type Option[A] = Some[A] | None -class Some[A](value: A) { - fun toString() = "Some(" ++ anythingToString(value) ++ ")" -} -module None { - fun toString() = "None" -} -//│ type Option[A] = None | Some[A] -//│ class Some[A](value: A) { -//│ fun toString: () -> Str -//│ } -//│ module None { -//│ fun toString: () -> "None" -//│ } - -type List[A] = Cons[A] | Nil -class Cons[A](head: A, tail: List[A]) -module Nil -//│ type List[A] = Cons[A] | Nil -//│ class Cons[A](head: A, tail: List[A]) -//│ module Nil - -// * We could define a shorthand for these, but let's leave them as useful tests -fun list1(x) = Cons(x, Nil) -fun list2(x, y) = Cons(x, list1(y)) -fun list3(x, y, z) = Cons(x, list2(y, z)) -fun list4(x, y, z, w) = Cons(x, list3(y, z, w)) -fun list5(x, y, z, w, v) = Cons(x, list4(y, z, w, v)) -fun list6(x, y, z, w, v, u) = Cons(x, list5(y, z, w, v, u)) -fun list7(x, y, z, w, v, u, t) = Cons(x, list6(y, z, w, v, u, t)) -fun list8(x, y, z, w, v, u, t, s) = Cons(x, list7(y, z, w, v, u, t, s)) -//│ fun list1: forall 'A. 'A -> Cons['A] -//│ fun list2: forall 'A0. ('A0, 'A0) -> Cons['A0] -//│ fun list3: forall 'A1. ('A1, 'A1, 'A1) -> Cons['A1] -//│ fun list4: forall 'A2. ('A2, 'A2, 'A2, 'A2) -> Cons['A2] -//│ fun list5: forall 'A3. ('A3, 'A3, 'A3, 'A3, 'A3) -> Cons['A3] -//│ fun list6: forall 'A4. ('A4, 'A4, 'A4, 'A4, 'A4, 'A4) -> Cons['A4] -//│ fun list7: forall 'A5. ('A5, 'A5, 'A5, 'A5, 'A5, 'A5, 'A5) -> Cons['A5] -//│ fun list8: forall 'A6. ('A6, 'A6, 'A6, 'A6, 'A6, 'A6, 'A6, 'A6) -> Cons['A6] - -fun findFirst(list, p) = - if list is - Nil then None - Cons(x, xs) and - p(x) then Some(x) - else findFirst(xs, p) -//│ fun findFirst: forall 'A. (Cons['A] | Nil, 'A -> Object) -> (None | Some['A]) - -fun listConcat(xs, ys) = - if xs is - Nil then ys - Cons(x, xs') then Cons(x, listConcat(xs', ys)) -//│ fun listConcat: forall 'A 'A0 'a. (Cons['A] | Nil, List['A0] & 'a) -> (Cons['A0] | 'a) -//│ where -//│ 'A <: 'A0 - -fun listContains(xs, x) = - if xs is - Nil then false - Cons(x', xs') and - eq(x)(x') then true - _ then listContains(xs', x) -//│ fun listContains: forall 'A. (Cons['A] | Nil, anything) -> Bool - -// Remove all occurrences of x from xs. -fun listWithout(xs, x) = - if xs is - Nil then Nil - Cons(x', xs') and - eq(x)(x') then listWithout(xs', x) - _ then Cons(x', listWithout(xs', x)) -//│ fun listWithout: forall 'A 'A0. (Cons['A] | Nil, anything) -> (Cons['A0] | Nil) -//│ where -//│ 'A <: 'A0 - - -// * FIXME? -fun listJoin(xs, sep) = - if xs is - Nil then "" - Cons(x, Nil) then toString(x) - Cons(x, xs') then toString(x) ++ sep ++ listJoin(xs', sep) -//│ fun listJoin: forall 'A. (Cons['A] | Nil, Str) -> Str - -fun listJoin(xs, sep) = - if xs is - Nil then "" - Cons(x, xs') and xs' is - Nil then toString(x) - _ then toString(x) ++ sep ++ listJoin(xs', sep) -//│ fun listJoin: forall 'A. (Cons['A] | Nil, Str) -> Str - -listJoin(list3("x", "y", "z"), ", ") -//│ Str -//│ res -//│ = 'x, y, z' - -type Term = Var | Abs | App -class Var(name: Str) -class Abs(lhs: Var, rhs: Term) -class App(lhs: Term, rhs: Term) -//│ type Term = Abs | App | Var -//│ class Var(name: Str) -//│ class Abs(lhs: Var, rhs: Term) -//│ class App(lhs: Term, rhs: Term) - -fun showTerm(t) = - if t is - Var(name) then toString(name) - Abs(lhs, rhs) then "&" ++ showTerm(lhs) ++ ". " ++ showTerm(rhs) - App(Abs(lhs0, lhs1), rhs) then - "((" ++ "&" ++ showTerm(lhs0) ++ ". " ++ showTerm(lhs1) ++ ") " ++ showTerm(rhs) ++ ")" - App(lhs, rhs) then par(showTerm(lhs) ++ " " ++ showTerm(rhs)) -//│ fun showTerm: (Abs | App | Var) -> Str - -showTerm(Var("x")) -showTerm(Abs(Var("x"), Var("y"))) -showTerm(App(Var("x"), Var("y"))) -showTerm(App(Abs(Var("x"), Var("y")), Var("z"))) -//│ Str -//│ res -//│ = 'x' -//│ res -//│ = '&x. y' -//│ res -//│ = '(x y)' -//│ res -//│ = '((&x. y) z)' - -fun isValue(t) = - if t is - Var then true - Abs then true - App then false -//│ fun isValue: (Abs | App | Var) -> Bool - -isValue(Var("x")) -isValue(Abs(Var("x"), Var("y"))) -isValue(App(Var("x"), Var("y"))) -//│ Bool -//│ res -//│ = true -//│ res -//│ = true -//│ res -//│ = false - -fun hasFree(t, n) = - if t is - // let __ = debug(concat3(showTerm(t), ", ", n)) - Var(na) then eq(n)(na) - Abs(Var(name), body) and eq(name)(n) then false - Abs(Var(name), body) then hasFree(body, n) - App(lhs, rhs) then hasFree(lhs, n) || hasFree(rhs, n) - _ then false -//│ fun hasFree: (Object, anything) -> Bool - -fun showHasFree(t, n) = - showTerm(t) ++ (if hasFree(t, n) then " has " else " DOES NOT have ") ++ "free variable " ++ n -//│ fun showHasFree: (Abs | App | Var, Str) -> Str - -showHasFree(Var("x"), "x") -showHasFree(Var("x"), "y") -showHasFree(Abs(Var("x"), Var("x")), "x") -showHasFree(Abs(Var("x"), Var("x")), "y") -showHasFree(Abs(Var("x"), Var("y")), "x") -showHasFree(Abs(Var("x"), Var("y")), "y") -showHasFree(App(Var("x"), Var("y")), "x") -showHasFree(App(Var("x"), Var("y")), "y") -showHasFree(App(Abs(Var("x"), Var("x")), Var("x")), "x") -showHasFree(App(Abs(Var("x"), Var("x")), Var("x")), "y") -showHasFree(App(Abs(Var("x"), Var("x")), Var("y")), "y") -showHasFree(App(Abs(Var("x"), Var("x")), Var("x")), "y") -//│ Str -//│ res -//│ = 'x has free variable x' -//│ res -//│ = 'x DOES NOT have free variable y' -//│ res -//│ = '&x. x DOES NOT have free variable x' -//│ res -//│ = '&x. x DOES NOT have free variable y' -//│ res -//│ = '&x. y DOES NOT have free variable x' -//│ res -//│ = '&x. y has free variable y' -//│ res -//│ = '(x y) has free variable x' -//│ res -//│ = '(x y) has free variable y' -//│ res -//│ = '((&x. x) x) has free variable x' -//│ res -//│ = '((&x. x) x) DOES NOT have free variable y' -//│ res -//│ = '((&x. x) y) has free variable y' -//│ res -//│ = '((&x. x) x) DOES NOT have free variable y' - -fun fv(t) = - if t is - Var(name) then list1(name) - Abs(Var(name), body) then listWithout(fv(body), name) - App(lhs, rhs) then listConcat(fv(lhs), fv(rhs)) -//│ fun fv: forall 'A. (Abs | App | Var) -> (Cons['A] | Nil) -//│ where -//│ 'A :> Str - -fun showFv(t) = - showTerm(t) ++ if fv(t) is - Nil then " DOES NOT have free variables" - _ then " has free variables: " ++ listJoin(fv(t), ", ") -//│ fun showFv: (Abs | App | Var) -> Str - -showFv(Var("x")) -showFv(Abs(Var("x"), Var("x"))) -showFv(Abs(Var("x"), Var("y"))) -showFv(App(Var("x"), Var("y"))) -showFv(App(Abs(Var("x"), Var("x")), Var("x"))) -//│ Str -//│ res -//│ = 'x has free variables: x' -//│ res -//│ = '&x. x DOES NOT have free variables' -//│ res -//│ = '&x. y has free variables: y' -//│ res -//│ = '(x y) has free variables: x, y' -//│ res -//│ = '((&x. x) x) has free variables: x' - -fun tryNextAlphabet(initialCode, currentCode, freeNames) = - if - currentCode - > 122 then tryNextAlphabet(initialCode, 97, freeNames) - == initialCode then None - let name = fromCharCode(currentCode) - listContains(freeNames, name) then tryNextAlphabet(initialCode, currentCode + 1, freeNames) - _ then Some(name) -//│ fun tryNextAlphabet: forall 'A. (Num, Int, Cons['A] | Nil) -> (None | Some[Str]) - -tryNextAlphabet(97, 97, list1("a")).toString() -tryNextAlphabet(97, 98, list1("a")).toString() -tryNextAlphabet(97, 98, list2("a", "b")).toString() -tryNextAlphabet(121, 122, list1("y")).toString() -tryNextAlphabet(121, 122, list2("y", "z")).toString() -//│ Str -//│ res -//│ = 'None' -//│ res -//│ = 'Some(b)' -//│ res -//│ = 'Some(c)' -//│ res -//│ = 'Some(z)' -//│ res -//│ = 'Some(a)' - -fun tryAppendDigits(name, index, freeNames) = - if - let currentName = name ++ toString(index) - listContains(freeNames, currentName) then - tryAppendDigits(name, index + 1, freeNames) - _ then currentName -//│ fun tryAppendDigits: forall 'A. (Str, Int, Cons['A] | Nil) -> Str - -// Note: some weird behavior here... Just try the commented code. -fun findFreshName(name, freeNames) = - if - stringLength(name) == 1 and - let charCode = stringCharCodeAt(name, 0) - tryNextAlphabet(charCode, charCode + 1, freeNames) is - Some(newName) then newName - _ then tryAppendDigits(name, 0, freeNames) -//│ fun findFreshName: forall 'A 'A0 'A1. (Str, Cons[in 'A | 'A0 | 'A1 out 'A & 'A0 & 'A1] | Nil) -> Str - -// Find a fresh name to replace `name` that does not conflict with any bound -// variables in the `body`. -fun freshName(name, body) = findFreshName(name, fv(body)) -//│ fun freshName: (Str, Abs | App | Var) -> Str - -fun subst(t, n, v) = - if t is - Var(name) and eq(name)(n) then v - Abs(Var(name), body) and ne(name)(n) and - hasFree(v, name) and freshName(name, body) is newName then - subst(Abs(Var(newName), subst(body, name, Var(newName))), n, v) - _ then Abs(Var(name), subst(body, n, v)) - App(lhs, rhs) then App(subst(lhs, n, v), subst(rhs, n, v)) - _ then t -//│ fun subst: forall 'a. (Abs | App | Term & Object & 'a & ~#Abs & ~#App & ~#Var | Var, anything, Term & Object & 'a) -> ('a | Abs | App | Var) - -fun showSubst(t, n, v) = - showTerm(t) ++ " [" ++ n ++ " / " ++ showTerm(v) ++ "]" ++ " => " ++ showTerm(subst(t, n, v)) -//│ fun showSubst: (Abs | App | Var, Str, Abs & Term | App & Term | Var & Term) -> Str - -showSubst(Var("x"), "x", Var("y")) -showSubst(Abs(Var("x"), Var("x")), "x", Var("z")) -showSubst(App(Var("x"), Var("y")), "x", Abs(Var("x"), Var("x"))) -showSubst(App(Abs(Var("x"), Var("x")), Var("x")), "x", Abs(Var("y"), Var("y"))) -showSubst(Abs(Var("x"), App(Var("x"), Var("y"))), "y", Var("x")) -showSubst(Abs(Var("z"), Abs(Var("x"), App(Var("z"), App(Var("x"), Var("y"))))), "y", Var("x")) -//│ Str -//│ res -//│ = 'x [x / y] => y' -//│ res -//│ = '&x. x [x / z] => &x. x' -//│ res -//│ = '(x y) [x / &x. x] => ((&x. x) y)' -//│ res -//│ = '((&x. x) x) [x / &y. y] => ((&x. x) &y. y)' -//│ res -//│ = '&x. (x y) [y / x] => &z. (z x)' -//│ res -//│ = '&z. &x. (z (x y)) [y / x] => &z. &a. (z (a x))' - -type Result = Normal | Stuck | Stepped -class Normal(term: Term) { - fun toString() = "Normal form: " ++ showTerm(term) -} -class Stuck(term: Term, part: Term) { - fun toString() = "Stuck: " ++ showTerm(part) ++ " in " ++ showTerm(term) -} -class Stepped(from: Term, to: Term) { - fun toString() = showTerm(from) ++ " => " ++ showTerm(to) -} -//│ type Result = Normal | Stepped | Stuck -//│ class Normal(term: Term) { -//│ fun toString: () -> Str -//│ } -//│ class Stuck(term: Term, part: Term) { -//│ fun toString: () -> Str -//│ } -//│ class Stepped(from: Term, to: Term) { -//│ fun toString: () -> Str -//│ } - -fun stepByValue(t) = - if t is - Var then Stuck(t, t) - Abs then Normal(t) - App(lhs, rhs) and stepByValue(lhs) is - Stepped(_, lhs) then Stepped(t, App(lhs, rhs)) - Stuck(_, part) then Stuck(t, part) - Normal and stepByValue(rhs) is - Stepped(_, rhs) then Stepped(t, App(lhs, rhs)) - Stuck(_, part) then Stuck(t, part) - Normal and lhs is - Abs(Var(name), body) then Stepped(t, subst(body, name, rhs)) - _ then Stuck(t, lhs) -//│ fun stepByValue: (Abs | App | Var) -> (Normal | Stepped | Stuck) - -toString of stepByValue of Var("x") -toString of stepByValue of Abs(Var("x"), Var("y")) -toString of stepByValue of App(Var("x"), Var("y")) -toString of stepByValue of App(Abs(Var("x"), Var("x")), Var("x")) -toString of stepByValue of App(Abs(Var("x"), Var("x")), Abs(Var("y"), Var("y"))) -//│ Str -//│ res -//│ = 'Stuck: x in x' -//│ res -//│ = 'Normal form: &x. y' -//│ res -//│ = 'Stuck: x in (x y)' -//│ res -//│ = 'Stuck: x in ((&x. x) x)' -//│ res -//│ = '((&x. x) &y. y) => &y. y' - -fun evalByValue(t) = - if stepByValue(t) is result and result is - Stepped(_, term) then evalByValue(term) - else result -//│ fun evalByValue: (Abs | App | Var) -> (Normal | Stuck) - -// Let's program with Church encoding! -let zero = Abs(Var("f"), Abs(Var("x"), Var("x"))) -let one = Abs(Var("f"), Abs(Var("x"), App(Var("f"), Var("x")))) -toString of stepByValue of zero -toString of stepByValue of one -let succ = Abs(Var("n"), Abs(Var("f"), Abs(Var("x"), App(Var("f"), App(App(Var("n"), Var("f")), Var("x")))))) -toString of stepByValue of succ -toString of stepByValue of App(succ, zero) -//│ let zero: Abs -//│ let one: Abs -//│ let succ: Abs -//│ Str -//│ zero -//│ = Abs {} -//│ one -//│ = Abs {} -//│ res -//│ = 'Normal form: &f. &x. x' -//│ res -//│ = 'Normal form: &f. &x. (f x)' -//│ succ -//│ = Abs {} -//│ res -//│ = 'Normal form: &n. &f. &x. (f ((n f) x))' -//│ res -//│ = '((&n. &f. &x. (f ((n f) x))) &f. &x. x) => &f. &x. (f (((&f. &x. x) f) x))' - -toString of evalByValue of App(succ, App(succ, zero)) -toString of evalByValue of App(succ, App(succ, App(succ, App(succ, zero)))) -//│ Str -//│ res -//│ = 'Normal form: &f. &x. (f (((&f. &x. (f (((&f. &x. x) f) x))) f) x))' -//│ res -//│ = 'Normal form: &f. &x. (f (((&f. &x. (f (((&f. &x. (f (((&f. &x. (f (((&f. &x. x) f) x))) f) x))) f) x))) f) x))' - -fun equalTerm(a, b) = - if a is - Var(na) and b is Var(nb) then eq(na)(nb) - Abs(la, ra) and b is Abs(lb, rb) then equalTerm(la, lb) && equalTerm(ra, rb) - App(la, ra) and b is App(lb, rb) then equalTerm(la, lb) && equalTerm(ra, rb) - _ then false -//│ fun equalTerm: (Object, Object) -> Bool diff --git a/shared/src/test/diff/tapl/SimplyTyped.mls b/shared/src/test/diff/tapl/SimplyTyped.mls deleted file mode 100644 index 400be3d6..00000000 --- a/shared/src/test/diff/tapl/SimplyTyped.mls +++ /dev/null @@ -1,503 +0,0 @@ -:NewParser - -fun concat2(a, b) = concat(a)(b) -fun concat3(a, b, c) = concat2(a, concat2(b, c)) -fun concat4(a, b, c, d) = concat2(a, concat3(b, c, d)) -fun concat5(a, b, c, d, e) = concat2(a, concat4(b, c, d, e)) -fun concat6(a, b, c, d, e, f) = concat2(a, concat5(b, c, d, e, f)) -fun concat7(a, b, c, d, e, f, g) = concat2(a, concat6(b, c, d, e, f, g)) -fun concat8(a, b, c, d, e, f, g, h) = concat2(a, concat7(b, c, d, e, f, g, h)) -fun par(a) = concat3("(", a, ")") -//│ concat2: (string, string,) -> string -//│ = [Function: concat2] -//│ concat3: (string, string, string,) -> string -//│ = [Function: concat3] -//│ concat4: (string, string, string, string,) -> string -//│ = [Function: concat4] -//│ concat5: (string, string, string, string, string,) -> string -//│ = [Function: concat5] -//│ concat6: (string, string, string, string, string, string,) -> string -//│ = [Function: concat6] -//│ concat7: (string, string, string, string, string, string, string,) -> string -//│ = [Function: concat7] -//│ concat8: (string, string, string, string, string, string, string, string,) -> string -//│ = [Function: concat8] -//│ par: string -> string -//│ = [Function: par] - -class Option -class Some(value): Option -class None(): Option -//│ Defined class Option -//│ Defined class Some -//│ Defined class None -//│ Option: () -> Option -//│ = [Function: Option1] -//│ Some: 'value -> (Some & {value: 'value}) -//│ = [Function: Some1] -//│ None: () -> None -//│ = [Function: None1] - -class Result -class Ok(value): Result -class Err(message): Result -//│ Defined class Result -//│ Defined class Ok -//│ Defined class Err -//│ Result: () -> Result -//│ = [Function: Result1] -//│ Ok: 'value -> (Ok & {value: 'value}) -//│ = [Function: Ok1] -//│ Err: 'message -> (Err & {message: 'message}) -//│ = [Function: Err1] - -class Type -class FunctionType(lhs, rhs): Type -class PrimitiveType(name): Type -//│ Defined class Type -//│ Defined class FunctionType -//│ Defined class PrimitiveType -//│ Type: () -> Type -//│ = [Function: Type1] -//│ FunctionType: ('lhs, 'rhs,) -> (FunctionType & {lhs: 'lhs, rhs: 'rhs}) -//│ = [Function: FunctionType1] -//│ PrimitiveType: 'name -> (PrimitiveType & {name: 'name}) -//│ = [Function: PrimitiveType1] - -// Helpers. -fun _f(lhs, rhs) = FunctionType(lhs, rhs) -fun _t(name) = PrimitiveType(name) -//│ _f: ('lhs, 'rhs,) -> (FunctionType & {lhs: 'lhs, rhs: 'rhs}) -//│ = [Function: _f] -//│ _t: 'name -> (PrimitiveType & {name: 'name}) -//│ = [Function: _t] - -class Term -class Lit(tag, ty): Term -class Var(name): Term -class Abs(lhs, lty, rhs): Term -class App(lhs, rhs): Term -// class App(lhs: Term, rhs: Term): Term -//│ Defined class Term -//│ Defined class Lit -//│ Defined class Var -//│ Defined class Abs -//│ Defined class App -//│ Term: () -> Term -//│ = [Function: Term1] -//│ Lit: ('tag, 'ty,) -> (Lit & {tag: 'tag, ty: 'ty}) -//│ = [Function: Lit1] -//│ Var: 'name -> (Var & {name: 'name}) -//│ = [Function: Var1] -//│ Abs: ('lhs, 'lty, 'rhs,) -> (Abs & {lhs: 'lhs, lty: 'lty, rhs: 'rhs}) -//│ = [Function: Abs1] -//│ App: ('lhs, 'rhs,) -> (App & {lhs: 'lhs, rhs: 'rhs}) -//│ = [Function: App1] - -class Assumption(name, ty) -//│ Defined class Assumption -//│ Assumption: ('name, 'ty,) -> (Assumption & {name: 'name, ty: 'ty}) -//│ = [Function: Assumption1] - -class Tree -class Node(key, value, left, right): Tree -class Empty(): Tree -//│ Defined class Tree -//│ Defined class Node -//│ Defined class Empty -//│ Tree: () -> Tree -//│ = [Function: Tree1] -//│ Node: ('key, 'value, 'left, 'right,) -> (Node & {key: 'key, left: 'left, right: 'right, value: 'value}) -//│ = [Function: Node1] -//│ Empty: () -> Empty -//│ = [Function: Empty1] - -fun empty = Empty() -fun insert(t, k, v) = - if t is - Node(k', _, l, r) and - slt(k)(k') then Node(k', v, insert(l, k, v), r) - sgt(k)(k') then Node(k', v, l, insert(r, k, v)) - _ then Node(k, v, l, r) - Empty then Node(k, v, empty, empty) -fun find(t, k) = - if t is - Node(k', v, l, r) and - slt(k)(k') then find(l, k) - sgt(k)(k') then find(r, k) - _ then Some(v) - Empty then None() -//│ empty: Empty -//│ = [Function: empty] -//│ insert: (Empty | Node & 'a, string & 'key, 'value,) -> (Node & {key: 'key, left: Empty | 'left, right: Empty | 'right, value: 'value} | 'b) -//│ where -//│ 'b :> Node & { -//│ key: 'key0, -//│ left: Node & {key: 'key, left: Empty | 'left, right: Empty | 'right, value: 'value} | 'b, -//│ right: 'right, -//│ value: 'value -//│ } | Node & { -//│ key: 'key0, -//│ left: 'left, -//│ right: Node & {key: 'key, left: Empty | 'left, right: Empty | 'right, value: 'value} | 'b, -//│ value: 'value -//│ } -//│ 'a <: {key: string & 'key0, left: 'left, right: 'right} -//│ 'right <: Empty | Node & 'a -//│ 'left <: Empty | Node & 'a -//│ = [Function: insert] -//│ find: (Empty | Node & 'a, string,) -> (Some & {value: 'value} | None) -//│ where -//│ 'a <: {key: string, left: Empty | Node & 'a, right: Empty | Node & 'a, value: 'value} -//│ = [Function: find] - -fun showType(ty) = - if ty is - FunctionType(PrimitiveType(name), rhs) then concat3(name, " -> ", showType(rhs)) - FunctionType(lhs, rhs) then concat4("(", showType(lhs), ") -> ", showType(rhs)) - PrimitiveType(name) then name -//│ showType: (FunctionType & 'a | PrimitiveType & {name: string}) -> string -//│ where -//│ 'a <: { -//│ lhs: FunctionType & 'a | PrimitiveType & {name: string}, -//│ rhs: FunctionType & 'a | PrimitiveType & {name: string} -//│ } -//│ = [Function: showType] - -showType(_t("int")) -showType(_f(_t("int"), _t("bool"))) -showType(_f(_f(_t("int"), _t("bool")), _t("bool"))) -showType(_f(_t("bool"), _f(_t("int"), _t("bool")))) -//│ res: string -//│ = 'int' -//│ res: string -//│ = 'int -> bool' -//│ res: string -//│ = '(int -> bool) -> bool' -//│ res: string -//│ = 'bool -> int -> bool' - -fun typeEqual(t1, t2) = - if - t1 is PrimitiveType(name1) and t2 is PrimitiveType(name2) then eq(name1)(name2) - t1 is FunctionType(lhs1, rhs1) and t2 is FunctionType(lhs2, rhs2) then - typeEqual(lhs1, lhs2) and typeEqual(rhs1, rhs2) - _ then false -//│ typeEqual: (FunctionType & 'a | PrimitiveType | ~FunctionType & ~PrimitiveType, FunctionType & 'b | PrimitiveType | ~FunctionType & ~PrimitiveType,) -> bool -//│ where -//│ 'b <: { -//│ lhs: FunctionType & 'b | PrimitiveType | ~FunctionType & ~PrimitiveType, -//│ rhs: FunctionType & 'b | PrimitiveType | ~FunctionType & ~PrimitiveType -//│ } -//│ 'a <: { -//│ lhs: FunctionType & 'a | PrimitiveType | ~FunctionType & ~PrimitiveType, -//│ rhs: FunctionType & 'a | PrimitiveType | ~FunctionType & ~PrimitiveType -//│ } -//│ = [Function: typeEqual] - -fun showTerm(t) = - if t is - Lit(tag, _) then toString(tag) - Var(name) then toString(name) - Abs(lhs, ty, rhs) then concat6("&", showTerm(lhs), ": ", showType(ty), " => ", showTerm(rhs)) - App(Abs(lhs0, ty, lhs1), rhs) then - concat5("((", showTerm(Abs(lhs0, ty, rhs)), ") ", showTerm(rhs), ")") - App(lhs, rhs) then par(concat3(showTerm(lhs), " ", showTerm(rhs))) -//│ showTerm: (Abs & 'a | App & 'b | Lit | Var) -> string -//│ where -//│ 'a <: { -//│ lhs: Abs & 'a | App & 'b | Lit | Var, -//│ lty: FunctionType & 'c | PrimitiveType & {name: string}, -//│ rhs: Abs & 'a | App & 'b | Lit | Var -//│ } -//│ 'b <: { -//│ lhs: App & 'b | Lit | Var | 'a & (Abs & { -//│ lhs: Abs & 'a | App & 'b | Lit | Var, -//│ lty: FunctionType & 'c | PrimitiveType & {name: string} -//│ } | Abs & ~#Abs), -//│ rhs: Abs & 'a | App & 'b | Lit | Var -//│ } -//│ 'c <: { -//│ lhs: FunctionType & 'c | PrimitiveType & {name: string}, -//│ rhs: FunctionType & 'c | PrimitiveType & {name: string} -//│ } -//│ = [Function: showTerm] - -showTerm(Var("x")) -showTerm(Abs(Var("x"), _t("int"), Var("y"))) -showTerm(App(Var("x"), Var("y"))) -showTerm(App(Abs(Var("x"), _t("int"), Var("y")), Var("z"))) -//│ res: string -//│ = 'x' -//│ res: string -//│ = '&x: int => y' -//│ res: string -//│ = '(x y)' -//│ res: string -//│ = '((&x: int => z) z)' - -// FIXME -fun typeTerm(t, ctx) = - if t is - Lit(_, ty) then Ok(ty) - Var(name) and find(ctx, name) is - Some(ty) then Ok(ty) - None then Err(concat3("unbound variable `", name, "`")) - Abs(Var(name), ty, body) and typeTerm(body, insert(ctx, name, ty)) is - Ok(resTy) then Ok(FunctionType(ty, resTy)) - Err(message) then Err(message) - App(lhs, rhs) and typeTerm(lhs, ctx) is - Ok(FunctionType(pTy, resTy)) and typeTerm(rhs, ctx) is - Ok(aTy) and - typeEqual(pTy, aTy) then Ok(resTy) - _ then Err(concat5("expect the argument to be of type `", showType(pTy), "` but found `", showType(aTy), "`")) - Err(message) then Err(message) - Ok(PrimitiveType(name)) then Err(concat3("cannot apply primitive type `", name, "`")) - Err(message) then Err(message) -//│ ╔══[ERROR] Cyclic-looking constraint while typing binding of lambda expression; a type annotation may be required -//│ ║ l.240: fun typeTerm(t, ctx) = -//│ ║ ^^^^^^^^^^ -//│ ║ l.241: if t is -//│ ║ ^^^^^^^^^ -//│ ║ l.242: Lit(_, ty) then Ok(ty) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.243: Var(name) and find(ctx, name) is -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.244: Some(ty) then Ok(ty) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.245: None then Err(concat3("unbound variable `", name, "`")) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.246: Abs(Var(name), ty, body) and typeTerm(body, insert(ctx, name, ty)) is -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.247: Ok(resTy) then Ok(FunctionType(ty, resTy)) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.248: Err(message) then Err(message) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.249: App(lhs, rhs) and typeTerm(lhs, ctx) is -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.250: Ok(FunctionType(pTy, resTy)) and typeTerm(rhs, ctx) is -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.251: Ok(aTy) and -//│ ║ ^^^^^^^^^^^^^^^^^^^ -//│ ║ l.252: typeEqual(pTy, aTy) then Ok(resTy) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.253: _ then Err(concat5("expect the argument to be of type `", showType(pTy), "` but found `", showType(aTy), "`")) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.254: Err(message) then Err(message) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.255: Ok(PrimitiveType(name)) then Err(concat3("cannot apply primitive type `", name, "`")) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.256: Err(message) then Err(message) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╙── Note: use flag `:ex` to see internal error info. -//│ ╔══[ERROR] Cyclic-looking constraint while typing binding of lambda expression; a type annotation may be required -//│ ║ l.240: fun typeTerm(t, ctx) = -//│ ║ ^^^^^^^^^^ -//│ ║ l.241: if t is -//│ ║ ^^^^^^^^^ -//│ ║ l.242: Lit(_, ty) then Ok(ty) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.243: Var(name) and find(ctx, name) is -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.244: Some(ty) then Ok(ty) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.245: None then Err(concat3("unbound variable `", name, "`")) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.246: Abs(Var(name), ty, body) and typeTerm(body, insert(ctx, name, ty)) is -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.247: Ok(resTy) then Ok(FunctionType(ty, resTy)) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.248: Err(message) then Err(message) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.249: App(lhs, rhs) and typeTerm(lhs, ctx) is -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.250: Ok(FunctionType(pTy, resTy)) and typeTerm(rhs, ctx) is -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.251: Ok(aTy) and -//│ ║ ^^^^^^^^^^^^^^^^^^^ -//│ ║ l.252: typeEqual(pTy, aTy) then Ok(resTy) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.253: _ then Err(concat5("expect the argument to be of type `", showType(pTy), "` but found `", showType(aTy), "`")) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.254: Err(message) then Err(message) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.255: Ok(PrimitiveType(name)) then Err(concat3("cannot apply primitive type `", name, "`")) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.256: Err(message) then Err(message) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╙── Note: use flag `:ex` to see internal error info. -//│ typeTerm: (Abs & 'a | App & 'b | Lit & {ty: 'ty & (FunctionType & 'c & 'd & 'e | PrimitiveType & {name: string})} | Var & {name: string}, Empty | Node & 'f & 'g,) -> (Err & { -//│ message: forall 'message 'message0 'message1. string | 'message | 'message0 | 'message1 -//│ } | Ok & {value: forall 'h. 'lty | 'rhs | 'ty | 'h}) -//│ where -//│ 'message :> forall 'message0 'message1. string | 'message0 | 'message1 -//│ 'message0 :> forall 'message 'message1. 'message | 'message1 -//│ 'message1 :> forall 'message 'message0. 'message | 'message0 -//│ 'g <: {key: string, left: Empty | Node & 'f & 'g, right: Empty | Node & 'f & 'g} -//│ 'f <: { -//│ key: string, -//│ left: Empty | Node & 'f, -//│ right: Empty | Node & 'f, -//│ value: 'ty & (FunctionType & 'c & 'd & 'e | PrimitiveType & {name: string}) -//│ } -//│ 'a <: { -//│ lhs: Var & {name: string}, -//│ lty: 'lty & (FunctionType & 'c & 'd & 'e & 'i & 'j | PrimitiveType & {name: string}), -//│ rhs: Abs & 'a | App & 'b | Lit & {ty: 'ty & (FunctionType & 'c & 'd & 'e | PrimitiveType & {name: string})} | Var & {name: string} -//│ } -//│ 'b <: { -//│ lhs: Abs & 'a | App & 'b | Lit & {ty: 'ty & (FunctionType & 'c & 'd & 'e | PrimitiveType & {name: string})} | Var & {name: string}, -//│ rhs: Abs & 'a | App & 'b | Lit & {ty: 'ty & (FunctionType & 'c & 'd & 'e | PrimitiveType & {name: string})} | Var & {name: string} -//│ } -//│ 'e <: { -//│ lhs: PrimitiveType & {name: string} | 'j & (FunctionType & 'i | FunctionType & ~#FunctionType), -//│ rhs: 'rhs -//│ } -//│ 'rhs :> forall 'value. 'value -//│ <: FunctionType & 'c & 'd & 'e | PrimitiveType & {name: string} -//│ 'value :> forall 'h. 'lty | 'rhs | 'ty | 'h -//│ 'h :> FunctionType & {lhs: 'lty, rhs: forall 'value. 'value} -//│ 'i <: { -//│ lhs: FunctionType & 'i | PrimitiveType | ~FunctionType & ~PrimitiveType, -//│ rhs: FunctionType & 'i | PrimitiveType | ~FunctionType & ~PrimitiveType -//│ } -//│ 'j <: { -//│ lhs: FunctionType & 'j | PrimitiveType & {name: string}, -//│ rhs: FunctionType & 'j | PrimitiveType & {name: string} -//│ } -//│ 'd <: { -//│ lhs: FunctionType & 'd | PrimitiveType | ~FunctionType & ~PrimitiveType, -//│ rhs: FunctionType & 'd | PrimitiveType | ~FunctionType & ~PrimitiveType -//│ } -//│ 'c <: { -//│ lhs: FunctionType & 'c | PrimitiveType & {name: string}, -//│ rhs: FunctionType & 'c | PrimitiveType & {name: string} -//│ } -//│ = [Function: typeTerm] - -fun showTypeTerm(t, ctx) = - if typeTerm(t, ctx) is - Ok(ty) then concat3(showTerm(t), " : ", showType(ty)) - Err(message) then concat2("Type error: ", message) -//│ showTypeTerm: (Abs & 'a & 'b | App & 'c & 'd | Lit & {ty: FunctionType & 'e & 'f & 'g & 'h | PrimitiveType & {name: string}} | Var & {name: string}, Empty | Node & 'i & 'j,) -> string -//│ where -//│ 'j <: { -//│ key: string, -//│ left: Empty | Node & 'j, -//│ right: Empty | Node & 'j, -//│ value: FunctionType & 'h & 'k & 'l & 'm | PrimitiveType & {name: string} -//│ } -//│ 'm <: { -//│ lhs: PrimitiveType & {name: string} | 'n & (FunctionType & 'o | FunctionType & ~#FunctionType), -//│ rhs: FunctionType & 'h & 'k & 'l & 'm | PrimitiveType & {name: string} -//│ } -//│ 'o <: { -//│ lhs: FunctionType & 'o | PrimitiveType | ~FunctionType & ~PrimitiveType, -//│ rhs: FunctionType & 'o | PrimitiveType | ~FunctionType & ~PrimitiveType -//│ } -//│ 'n <: { -//│ lhs: FunctionType & 'n | PrimitiveType & {name: string}, -//│ rhs: FunctionType & 'n | PrimitiveType & {name: string} -//│ } -//│ 'l <: { -//│ lhs: FunctionType & 'l | PrimitiveType | ~FunctionType & ~PrimitiveType, -//│ rhs: FunctionType & 'l | PrimitiveType | ~FunctionType & ~PrimitiveType -//│ } -//│ 'k <: { -//│ lhs: FunctionType & 'k | PrimitiveType & {name: string}, -//│ rhs: FunctionType & 'k | PrimitiveType & {name: string} -//│ } -//│ 'i <: {key: string, left: Empty | Node & 'i & 'p, right: Empty | Node & 'i & 'p} -//│ 'p <: { -//│ key: string, -//│ left: Empty | Node & 'p, -//│ right: Empty | Node & 'p, -//│ value: FunctionType & 'h & 'q & 'r & 's | PrimitiveType & {name: string} -//│ } -//│ 's <: { -//│ lhs: PrimitiveType & {name: string} | 't & (FunctionType & 'u | FunctionType & ~#FunctionType), -//│ rhs: FunctionType & 'h & 'q & 'r & 's | PrimitiveType & {name: string} -//│ } -//│ 'u <: { -//│ lhs: FunctionType & 'u | PrimitiveType | ~FunctionType & ~PrimitiveType, -//│ rhs: FunctionType & 'u | PrimitiveType | ~FunctionType & ~PrimitiveType -//│ } -//│ 't <: { -//│ lhs: FunctionType & 't | PrimitiveType & {name: string}, -//│ rhs: FunctionType & 't | PrimitiveType & {name: string} -//│ } -//│ 'r <: { -//│ lhs: FunctionType & 'r | PrimitiveType | ~FunctionType & ~PrimitiveType, -//│ rhs: FunctionType & 'r | PrimitiveType | ~FunctionType & ~PrimitiveType -//│ } -//│ 'q <: { -//│ lhs: FunctionType & 'q | PrimitiveType & {name: string}, -//│ rhs: FunctionType & 'q | PrimitiveType & {name: string} -//│ } -//│ 'b <: { -//│ lhs: Abs & 'b | App & 'd | Lit | Var, -//│ lty: FunctionType & 'v | PrimitiveType & {name: string}, -//│ rhs: Abs & 'b | App & 'd | Lit | Var -//│ } -//│ 'd <: { -//│ lhs: App & 'd | Lit | Var | 'b & (Abs & { -//│ lhs: Abs & 'b | App & 'd | Lit | Var, -//│ lty: FunctionType & 'v | PrimitiveType & {name: string} -//│ } | Abs & ~#Abs), -//│ rhs: Abs & 'b | App & 'd | Lit | Var -//│ } -//│ 'v <: { -//│ lhs: FunctionType & 'v | PrimitiveType & {name: string}, -//│ rhs: FunctionType & 'v | PrimitiveType & {name: string} -//│ } -//│ 'a <: { -//│ lhs: Var & {name: string}, -//│ lty: FunctionType & 'e & 'f & 'g & 'w & 'x & 'h | PrimitiveType & {name: string}, -//│ rhs: Abs & 'a | App & 'c | Lit & {ty: FunctionType & 'e & 'f & 'g & 'h | PrimitiveType & {name: string}} | Var & {name: string} -//│ } -//│ 'c <: { -//│ lhs: Abs & 'a | App & 'c | Lit & {ty: FunctionType & 'e & 'f & 'g & 'h | PrimitiveType & {name: string}} | Var & {name: string}, -//│ rhs: Abs & 'a | App & 'c | Lit & {ty: FunctionType & 'e & 'f & 'g & 'h | PrimitiveType & {name: string}} | Var & {name: string} -//│ } -//│ 'g <: { -//│ lhs: PrimitiveType & {name: string} | 'x & (FunctionType & 'w | FunctionType & ~#FunctionType), -//│ rhs: FunctionType & 'e & 'f & 'g & 'h | PrimitiveType & {name: string} -//│ } -//│ 'h <: { -//│ lhs: FunctionType & 'h | PrimitiveType & {name: string}, -//│ rhs: FunctionType & 'h | PrimitiveType & {name: string} -//│ } -//│ 'w <: { -//│ lhs: FunctionType & 'w | PrimitiveType | ~FunctionType & ~PrimitiveType, -//│ rhs: FunctionType & 'w | PrimitiveType | ~FunctionType & ~PrimitiveType -//│ } -//│ 'x <: { -//│ lhs: FunctionType & 'x | PrimitiveType & {name: string}, -//│ rhs: FunctionType & 'x | PrimitiveType & {name: string} -//│ } -//│ 'f <: { -//│ lhs: FunctionType & 'f | PrimitiveType | ~FunctionType & ~PrimitiveType, -//│ rhs: FunctionType & 'f | PrimitiveType | ~FunctionType & ~PrimitiveType -//│ } -//│ 'e <: { -//│ lhs: FunctionType & 'e | PrimitiveType & {name: string}, -//│ rhs: FunctionType & 'e | PrimitiveType & {name: string} -//│ } -//│ = [Function: showTypeTerm] - -// FIXME -showTypeTerm(Var("x"), empty) -showTypeTerm(Abs(Var("x"), _t("int"), Var("x")), empty) -showTypeTerm(App(Var("f"), Lit("0", _t("int"))), insert(empty, "f", _f(_t("int"), _t("int")))) -showTypeTerm(App(Var("f"), Lit("0.2", _t("float"))), insert(empty, "f", _f(_t("int"), _t("int")))) -showTypeTerm(App(Var("f"), Lit("0", _t("int"))), insert(empty, "f", _t("string"))) -//│ res: string -//│ = 'Type error: unbound variable `x`' -//│ res: string -//│ = '&x: int => x : int -> int' -//│ res: string -//│ = '(f 0) : int' -//│ res: string -//│ = 'Type error: expect the argument to be of type `int` but found `float`' -//│ res: string -//│ = 'Type error: cannot apply primitive type `string`' diff --git a/shared/src/test/diff/tapl/Untyped.mls b/shared/src/test/diff/tapl/Untyped.mls deleted file mode 100644 index 65d6ead6..00000000 --- a/shared/src/test/diff/tapl/Untyped.mls +++ /dev/null @@ -1,644 +0,0 @@ -:NewParser - -fun concat2(a, b) = concat(a)(b) -fun concat3(a, b, c) = concat2(a, concat2(b, c)) -fun concat4(a, b, c, d) = concat2(a, concat3(b, c, d)) -fun concat5(a, b, c, d, e) = concat2(a, concat4(b, c, d, e)) -fun concat6(a, b, c, d, e, f) = concat2(a, concat5(b, c, d, e, f)) -fun concat7(a, b, c, d, e, f, g) = concat2(a, concat6(b, c, d, e, f, g)) -fun concat8(a, b, c, d, e, f, g, h) = concat2(a, concat7(b, c, d, e, f, g, h)) -fun par(a) = concat3("(", a, ")") -//│ concat2: (string, string,) -> string -//│ = [Function: concat2] -//│ concat3: (string, string, string,) -> string -//│ = [Function: concat3] -//│ concat4: (string, string, string, string,) -> string -//│ = [Function: concat4] -//│ concat5: (string, string, string, string, string,) -> string -//│ = [Function: concat5] -//│ concat6: (string, string, string, string, string, string,) -> string -//│ = [Function: concat6] -//│ concat7: (string, string, string, string, string, string, string,) -> string -//│ = [Function: concat7] -//│ concat8: (string, string, string, string, string, string, string, string,) -> string -//│ = [Function: concat8] -//│ par: string -> string -//│ = [Function: par] - -:escape -let String: nothing -let makeString: anything => { length: int, charCodeAt: int => int } = String -let StringInstance: { fromCharCode: int => string } = String -//│ String: nothing -//│ = -//│ makeString: anything -> {charCodeAt: int -> int, length: int} -//│ = [Function: String] -//│ StringInstance: {fromCharCode: int -> string} -//│ = [Function: String] - -fun fromCharCode(n) = StringInstance.fromCharCode(n) -fun stringCharCodeAt(s, i) = makeString(s).charCodeAt(i) -fun stringLength(s) = makeString(s).length -//│ fromCharCode: int -> string -//│ = [Function: fromCharCode] -//│ stringCharCodeAt: (anything, int,) -> int -//│ = [Function: stringCharCodeAt] -//│ stringLength: anything -> int -//│ = [Function: stringLength] - -class Option -class Some(value): Option -class None(): Option -//│ Defined class Option -//│ Defined class Some -//│ Defined class None -//│ Option: () -> Option -//│ = [Function: Option1] -//│ Some: 'value -> (Some & {value: 'value}) -//│ = [Function: Some1] -//│ None: () -> None -//│ = [Function: None1] - -class List -class Cons(head, tail): List -class Nil(): List -//│ Defined class List -//│ Defined class Cons -//│ Defined class Nil -//│ List: () -> List -//│ = [Function: List1] -//│ Cons: ('head, 'tail,) -> (Cons & {head: 'head, tail: 'tail}) -//│ = [Function: Cons1] -//│ Nil: () -> Nil -//│ = [Function: Nil1] - -fun list1(x) = Cons(x, Nil()) -fun list2(x, y) = Cons(x, list1(y)) -fun list3(x, y, z) = Cons(x, list2(y, z)) -fun list4(x, y, z, w) = Cons(x, list3(y, z, w)) -fun list5(x, y, z, w, v) = Cons(x, list4(y, z, w, v)) -fun list6(x, y, z, w, v, u) = Cons(x, list5(y, z, w, v, u)) -fun list7(x, y, z, w, v, u, t) = Cons(x, list6(y, z, w, v, u, t)) -fun list8(x, y, z, w, v, u, t, s) = Cons(x, list7(y, z, w, v, u, t, s)) -//│ list1: 'head -> (Cons & {head: 'head, tail: Nil}) -//│ = [Function: list1] -//│ list2: ('head, 'head0,) -> (Cons & {head: 'head, tail: Cons & {head: 'head0, tail: Nil}}) -//│ = [Function: list2] -//│ list3: ('head, 'head0, 'head1,) -> (Cons & {head: 'head, tail: Cons & {head: 'head0, tail: Cons & {head: 'head1, tail: Nil}}}) -//│ = [Function: list3] -//│ list4: ('head, 'head0, 'head1, 'head2,) -> (Cons & { -//│ head: 'head, -//│ tail: Cons & {head: 'head0, tail: Cons & {head: 'head1, tail: Cons & {head: 'head2, tail: Nil}}} -//│ }) -//│ = [Function: list4] -//│ list5: ('head, 'head0, 'head1, 'head2, 'head3,) -> (Cons & { -//│ head: 'head, -//│ tail: Cons & { -//│ head: 'head0, -//│ tail: Cons & {head: 'head1, tail: Cons & {head: 'head2, tail: Cons & {head: 'head3, tail: Nil}}} -//│ } -//│ }) -//│ = [Function: list5] -//│ list6: ('head, 'head0, 'head1, 'head2, 'head3, 'head4,) -> (Cons & { -//│ head: 'head, -//│ tail: Cons & { -//│ head: 'head0, -//│ tail: Cons & { -//│ head: 'head1, -//│ tail: Cons & {head: 'head2, tail: Cons & {head: 'head3, tail: Cons & {head: 'head4, tail: Nil}}} -//│ } -//│ } -//│ }) -//│ = [Function: list6] -//│ list7: ('head, 'head0, 'head1, 'head2, 'head3, 'head4, 'head5,) -> (Cons & { -//│ head: 'head, -//│ tail: Cons & { -//│ head: 'head0, -//│ tail: Cons & { -//│ head: 'head1, -//│ tail: Cons & { -//│ head: 'head2, -//│ tail: Cons & {head: 'head3, tail: Cons & {head: 'head4, tail: Cons & {head: 'head5, tail: Nil}}} -//│ } -//│ } -//│ } -//│ }) -//│ = [Function: list7] -//│ list8: ('head, 'head0, 'head1, 'head2, 'head3, 'head4, 'head5, 'head6,) -> (Cons & { -//│ head: 'head, -//│ tail: Cons & { -//│ head: 'head0, -//│ tail: Cons & { -//│ head: 'head1, -//│ tail: Cons & { -//│ head: 'head2, -//│ tail: Cons & { -//│ head: 'head3, -//│ tail: Cons & {head: 'head4, tail: Cons & {head: 'head5, tail: Cons & {head: 'head6, tail: Nil}}} -//│ } -//│ } -//│ } -//│ } -//│ }) -//│ = [Function: list8] - -fun listConcat(xs, ys) = - if xs is - Nil() then ys - Cons(x, xs') then Cons(x, listConcat(xs', ys)) -//│ listConcat: (Cons & 'a | Nil, 'b,) -> 'b -//│ where -//│ 'b :> Cons & {head: 'head, tail: 'b} -//│ 'a <: {head: 'head, tail: Cons & 'a | Nil} -//│ = [Function: listConcat] - -fun listContains(xs, x) = - if xs is - Nil() then false - Cons(x', xs') and - eq(x)(x') then true - _ then listContains(xs', x) -//│ listContains: (Cons & 'a | Nil, anything,) -> Bool -//│ where -//│ 'a <: {head: anything, tail: Cons & 'a | Nil} -//│ = [Function: listContains] - -// Remove all occurrences of x from xs. -fun listWithout(xs, x) = - if xs is - Nil() then Nil() - Cons(x', xs') and - eq(x)(x') then listWithout(xs', x) - _ then Cons(x', listWithout(xs', x)) -//│ listWithout: (Cons & 'a | Nil, anything,) -> 'b -//│ where -//│ 'b :> Nil | Cons & {head: 'head, tail: 'b} -//│ 'a <: {head: 'head, tail: Cons & 'a | Nil} -//│ = [Function: listWithout] - -fun listJoin(xs, sep) = - if xs is - Nil() then "" - Cons(x, Nil()) then toString(x) - Cons(x, xs') then concat3(toString(x), sep, listJoin(xs', sep)) -//│ listJoin: (Cons & 'a | Nil, string,) -> string -//│ where -//│ 'a <: {head: anything, tail: Cons & 'a | Nil} -//│ = [Function: listJoin] - -listJoin(list3("x", "y", "z"), ", ") -//│ res: string -//│ = 'x, y, z' - -class Term -class Var(name): Term -class Abs(lhs, rhs): Term -class App(lhs, rhs): Term -//│ Defined class Term -//│ Defined class Var -//│ Defined class Abs -//│ Defined class App -//│ Term: () -> Term -//│ = [Function: Term1] -//│ Var: 'name -> (Var & {name: 'name}) -//│ = [Function: Var1] -//│ Abs: ('lhs, 'rhs,) -> (Abs & {lhs: 'lhs, rhs: 'rhs}) -//│ = [Function: Abs1] -//│ App: ('lhs, 'rhs,) -> (App & {lhs: 'lhs, rhs: 'rhs}) -//│ = [Function: App1] - -fun showTerm(t) = - if t is - Var(name) then toString(name) - Abs(lhs, rhs) then concat4("&", showTerm(lhs), ". ", showTerm(rhs)) - App(Abs(lhs0, lhs1), rhs) then - concat8("((", "&", showTerm(lhs0), ". ", showTerm(lhs1), ") ", showTerm(rhs), ")") - App(lhs, rhs) then par(concat3(showTerm(lhs), " ", showTerm(rhs))) -//│ showTerm: (Abs & 'a | App & 'b | Var) -> string -//│ where -//│ 'a <: {lhs: Abs & 'a | App & 'b | Var, rhs: Abs & 'a | App & 'b | Var} -//│ 'b <: { -//│ lhs: App & 'b | Var | 'a & (Abs & 'a | Abs & ~#Abs), -//│ rhs: Abs & 'a | App & 'b | Var -//│ } -//│ = [Function: showTerm] - -showTerm(Var("x")) -showTerm(Abs(Var("x"), Var("y"))) -showTerm(App(Var("x"), Var("y"))) -showTerm(App(Abs(Var("x"), Var("y")), Var("z"))) -//│ res: string -//│ = 'x' -//│ res: string -//│ = '&x. y' -//│ res: string -//│ = '(x y)' -//│ res: string -//│ = '((&x. y) z)' - -fun isValue(t) = - if t is - Var then true - Abs then true - App then false -//│ isValue: (Abs | App | Var) -> Bool -//│ = [Function: isValue] - -isValue(Var("x")) -isValue(Abs(Var("x"), Var("y"))) -isValue(App(Var("x"), Var("y"))) -//│ res: Bool -//│ = true -//│ res: Bool -//│ = true -//│ res: Bool -//│ = false - -fun hasFree(t, n) = - if t is - // let __ = debug(concat3(showTerm(t), ", ", n)) - Var(na) then eq(n)(na) - Abs(Var(name), body) and eq(name)(n) then false - Abs(Var(name), body) then hasFree(body, n) - App(lhs, rhs) then hasFree(lhs, n) || hasFree(rhs, n) - _ then false -//│ hasFree: (Abs & 'a | App & 'b | Var | ~Abs & ~App & ~Var, anything,) -> bool -//│ where -//│ 'a <: {lhs: anything, rhs: Abs & 'a | App & 'b | Var | ~Abs & ~App & ~Var} -//│ 'b <: { -//│ lhs: Abs & 'a | App & 'b | Var | ~Abs & ~App & ~Var, -//│ rhs: Abs & 'a | App & 'b | Var | ~Abs & ~App & ~Var -//│ } -//│ = [Function: hasFree] - -fun showHasFree(t, n) = - concat4(showTerm(t), if hasFree(t, n) then " has " else " DOES NOT have ", "free variable ", n) -//│ showHasFree: (Abs & 'a & 'b | App & 'c & 'd | Var, string,) -> string -//│ where -//│ 'b <: {lhs: anything, rhs: Abs & 'b | App & 'd | Var | ~Abs & ~App & ~Var} -//│ 'd <: { -//│ lhs: Abs & 'b | App & 'd | Var | ~Abs & ~App & ~Var, -//│ rhs: Abs & 'b | App & 'd | Var | ~Abs & ~App & ~Var -//│ } -//│ 'a <: {lhs: Abs & 'a | App & 'c | Var, rhs: Abs & 'a | App & 'c | Var} -//│ 'c <: { -//│ lhs: App & 'c | Var | 'a & (Abs & 'a | Abs & ~#Abs), -//│ rhs: Abs & 'a | App & 'c | Var -//│ } -//│ = [Function: showHasFree] - -showHasFree(Var("x"), "x") -showHasFree(Var("x"), "y") -showHasFree(Abs(Var("x"), Var("x")), "x") -showHasFree(Abs(Var("x"), Var("x")), "y") -showHasFree(Abs(Var("x"), Var("y")), "x") -showHasFree(Abs(Var("x"), Var("y")), "y") -showHasFree(App(Var("x"), Var("y")), "x") -showHasFree(App(Var("x"), Var("y")), "y") -showHasFree(App(Abs(Var("x"), Var("x")), Var("x")), "x") -showHasFree(App(Abs(Var("x"), Var("x")), Var("x")), "y") -showHasFree(App(Abs(Var("x"), Var("x")), Var("y")), "y") -showHasFree(App(Abs(Var("x"), Var("x")), Var("x")), "y") -//│ res: string -//│ = 'x has free variable x' -//│ res: string -//│ = 'x DOES NOT have free variable y' -//│ res: string -//│ = '&x. x DOES NOT have free variable x' -//│ res: string -//│ = '&x. x DOES NOT have free variable y' -//│ res: string -//│ = '&x. y DOES NOT have free variable x' -//│ res: string -//│ = '&x. y has free variable y' -//│ res: string -//│ = '(x y) has free variable x' -//│ res: string -//│ = '(x y) has free variable y' -//│ res: string -//│ = '((&x. x) x) has free variable x' -//│ res: string -//│ = '((&x. x) x) DOES NOT have free variable y' -//│ res: string -//│ = '((&x. x) y) has free variable y' -//│ res: string -//│ = '((&x. x) x) DOES NOT have free variable y' - -fun fv(t) = - if t is - Var(name) then list1(name) - Abs(Var(name), body) then listWithout(fv(body), name) - App(lhs, rhs) then listConcat(fv(lhs), fv(rhs)) -//│ fv: (Abs & 'a | App & 'b | Var & {name: 'name}) -> (Cons & {head: 'name, tail: Nil} | 'c | 'd) -//│ where -//│ 'd :> forall 'e. 'f | 'e -//│ 'e :> Cons & {head: forall 'head. 'head, tail: 'f | 'e} -//│ 'f :> forall 'g. Cons & {head: 'name, tail: Nil} | 'g | 'd -//│ 'g :> Nil | Cons & {head: forall 'head0. 'head0, tail: 'g} -//│ 'c :> Nil | Cons & {head: forall 'head0. 'head0, tail: 'c} -//│ 'head0 :> forall 'head. 'head | 'name -//│ 'head :> forall 'head0. 'head0 | 'name -//│ 'a <: {lhs: Var, rhs: Abs & 'a | App & 'b | Var & {name: 'name}} -//│ 'b <: { -//│ lhs: Abs & 'a | App & 'b | Var & {name: 'name}, -//│ rhs: Abs & 'a | App & 'b | Var & {name: 'name} -//│ } -//│ = [Function: fv] - -fun showFv(t) = - concat2(showTerm(t), if fv(t) is - Nil then " DOES NOT have free variables" - _ then concat2(" has free variables: ", listJoin(fv(t), ", ")) - ) -//│ showFv: (Abs & 'a & 'b & 'c | App & 'd & 'e & 'f | Var) -> string -//│ where -//│ 'c <: {lhs: Var, rhs: Abs & 'c | App & 'f | Var} -//│ 'f <: {lhs: Abs & 'c | App & 'f | Var, rhs: Abs & 'c | App & 'f | Var} -//│ 'b <: {lhs: Var, rhs: Abs & 'b | App & 'e | Var} -//│ 'e <: {lhs: Abs & 'b | App & 'e | Var, rhs: Abs & 'b | App & 'e | Var} -//│ 'a <: {lhs: Abs & 'a | App & 'd | Var, rhs: Abs & 'a | App & 'd | Var} -//│ 'd <: { -//│ lhs: App & 'd | Var | 'a & (Abs & 'a | Abs & ~#Abs), -//│ rhs: Abs & 'a | App & 'd | Var -//│ } -//│ = [Function: showFv] - -showFv(Var("x")) -showFv(Abs(Var("x"), Var("x"))) -showFv(Abs(Var("x"), Var("y"))) -showFv(App(Var("x"), Var("y"))) -showFv(App(Abs(Var("x"), Var("x")), Var("x"))) -//│ res: string -//│ = 'x has free variables: x' -//│ res: string -//│ = '&x. x DOES NOT have free variables' -//│ res: string -//│ = '&x. y has free variables: y' -//│ res: string -//│ = '(x y) has free variables: x, y' -//│ res: string -//│ = '((&x. x) x) has free variables: x' - -fun tryNextAlphabet(initialCode, currentCode, freeNames) = - if - currentCode - > 122 then tryNextAlphabet(initialCode, 97, freeNames) - == initialCode then None() - let name = fromCharCode(currentCode) - listContains(freeNames, name) then tryNextAlphabet(initialCode, currentCode + 1, freeNames) - _ then Some(name) -//│ tryNextAlphabet: (number, int, Cons & 'a | Nil,) -> (Some & {value: string} | None) -//│ where -//│ 'a <: {head: anything, tail: Cons & 'a | Nil} -//│ = [Function: tryNextAlphabet] - -tryNextAlphabet(97, 97, list1("a")) -tryNextAlphabet(97, 98, list1("a")) -tryNextAlphabet(97, 98, list2("a", "b")) -tryNextAlphabet(121, 122, list1("y")) -tryNextAlphabet(121, 122, list2("y", "z")) -//│ res: Some & {value: string} | None -//│ = None {} -//│ res: Some & {value: string} | None -//│ = Some { value: 'b' } -//│ res: Some & {value: string} | None -//│ = Some { value: 'c' } -//│ res: Some & {value: string} | None -//│ = Some { value: 'z' } -//│ res: Some & {value: string} | None -//│ = Some { value: 'a' } - -fun tryAppendDigits(name, index, freeNames) = - if - let currentName = concat2(name, toString(index)) - listContains(freeNames, currentName) then - tryAppendDigits(name, index + 1, freeNames) - _ then currentName -//│ tryAppendDigits: (string, int, Cons & 'a | Nil,) -> string -//│ where -//│ 'a <: {head: anything, tail: Cons & 'a | Nil} -//│ = [Function: tryAppendDigits] - -// Note: some weird behavior here... Just try the commented code. -fun findFreshName(name, freeNames) = - if - stringLength(name) == 1 and - let charCode = stringCharCodeAt(name, 0) - tryNextAlphabet(charCode, charCode + 1, freeNames) is - Some(newName) then newName - _ then tryAppendDigits(name, 0, freeNames) -//│ findFreshName: (string, Cons & 'a & 'b & 'c | Nil,) -> string -//│ where -//│ 'c <: {head: anything, tail: Cons & 'c | Nil} -//│ 'b <: {head: anything, tail: Cons & 'b | Nil} -//│ 'a <: {head: anything, tail: Cons & 'a | Nil} -//│ = [Function: findFreshName] - -// Find a fresh name to replace `name` that does not conflict with any bound -// variables in the `body`. -fun freshName(name, body) = findFreshName(name, fv(body)) -//│ freshName: (string, Abs & 'a | App & 'b | Var,) -> string -//│ where -//│ 'a <: {lhs: Var, rhs: Abs & 'a | App & 'b | Var} -//│ 'b <: {lhs: Abs & 'a | App & 'b | Var, rhs: Abs & 'a | App & 'b | Var} -//│ = [Function: freshName] - -fun subst(t, n, v) = - if t is - Var(name) and eq(name)(n) then v - Abs(Var(name), body) and ne(name)(n) and - hasFree(v, name) and freshName(name, body) is newName then - subst(Abs(Var(newName), subst(body, name, Var(newName))), n, v) - _ then Abs(Var(name), subst(body, n, v)) - App(lhs, rhs) then App(subst(lhs, n, v), subst(rhs, n, v)) - _ then t -//│ subst: (Abs & 'a | App & 'b | Var & 'c | 'd & ~#Abs & ~#App & ~#Var, anything, 'e & (Var & 'c | 'b & 'f & (App & ~#App | App & 'g) | 'a & 'h & (Abs & ~#Abs | Abs & 'i) | 'd & (Abs & 'h & ~#Abs | App & 'f & ~#App | Var & ~#Var)),) -> (Var & {name: string} | 'e | 'c | 'a | 'd) -//│ where -//│ 'g <: { -//│ lhs: Abs & 'i | App & 'g | Var | ~Abs & ~App & ~Var, -//│ rhs: Abs & 'i | App & 'g | Var | ~Abs & ~App & ~Var -//│ } -//│ 'i <: {lhs: anything, rhs: Abs & 'i | App & 'g | Var | ~Abs & ~App & ~Var} -//│ 'a :> Abs & {lhs: Var & {name: string}, rhs: Var & {name: string} | 'rhs | 'e | 'c | 'a | 'd} -//│ <: Abs & {lhs: Var & {name: string} | ~Var, rhs: Abs & 'a & 'h | App & 'b & 'f | Var & 'c} & 'h | App & {lhs: Var & {name: string} | ~Var, rhs: Abs & 'a & 'h | App & 'b & 'f | Var & 'c} & 'b & 'f | Var & {lhs: Var & {name: string} | ~Var, rhs: Abs & 'a & 'h | App & 'b & 'f | Var & 'c} & 'c | 'd & (App & {lhs: Var & {name: string} | ~Var, rhs: Abs & 'a & 'h | App & 'b & 'f | Var & 'c} & 'f & ~#App | Var & {lhs: Var & {name: string} | ~Var, rhs: Abs & 'a & 'h | App & 'b & 'f | Var & 'c} & ~#Var) -//│ 'rhs :> Var & {name: string} | 'e | 'c | 'a | 'd -//│ 'e :> Var & {name: string} | 'c | 'a | 'd | App & { -//│ lhs: Var & {name: string} | 'e | 'c | 'a | 'd, -//│ rhs: Var & {name: string} | 'e | 'c | 'a | 'd -//│ } | Abs & {lhs: Var & {name: string}, rhs: 'rhs} -//│ 'c :> Var & {name: string} -//│ <: Abs & {name: anything} & 'a & 'h | App & {name: anything} & 'b & 'f | Var | 'd & (Abs & {name: anything} & 'h & ~#Abs | App & {name: anything} & 'f & ~#App) -//│ 'b <: { -//│ lhs: Abs & 'a | App & 'b | Var & 'c | 'd & ~#Abs & ~#App & ~#Var, -//│ rhs: Abs & 'a | App & 'b | Var & 'c | 'd & ~#Abs & ~#App & ~#Var -//│ } -//│ 'd <: Var & ~#Var | Var & 'c | 'f & (App & ~#App | App & 'b) | 'h & (Abs & ~#Abs | Abs & 'a) -//│ 'h <: {lhs: Var, rhs: Abs & 'h | App & 'f | Var} -//│ 'f <: {lhs: Abs & 'h | App & 'f | Var, rhs: Abs & 'h | App & 'f | Var} -//│ = [Function: subst] - -fun showSubst(t, n, v) = - concat8(showTerm(t), " [", n, " / ", showTerm(v), "]", " => ", showTerm(subst(t, n, v))) -//│ showSubst: (Abs & 'a & 'b | App & 'c & 'd | Var & 'e, string, Abs & 'b & 'f & 'g & 'h & 'i | App & 'j & 'd & 'k & 'l & 'm | Var & 'e,) -> string -//│ where -//│ 'i <: {lhs: anything, rhs: Abs & 'i | App & 'm | Var | ~Abs & ~App & ~Var} -//│ 'm <: { -//│ lhs: Abs & 'i | App & 'm | Var | ~Abs & ~App & ~Var, -//│ rhs: Abs & 'i | App & 'm | Var | ~Abs & ~App & ~Var -//│ } -//│ 'h <: {lhs: Abs & 'h | App & 'l | Var, rhs: Abs & 'h | App & 'l | Var} -//│ 'l <: { -//│ lhs: App & 'l | Var | 'h & (Abs & 'h | Abs & ~#Abs), -//│ rhs: Abs & 'h | App & 'l | Var -//│ } -//│ 'b <: Abs & {lhs: Var & {name: string} | ~Var, rhs: Abs & 'b & 'f | App & 'j & 'd | Var & 'e} & 'f & 'g | Var & {lhs: Var & {name: string} | ~Var, rhs: Abs & 'b & 'f | App & 'j & 'd | Var & 'e} & 'e | Var & {lhs: Var & {name: string} | ~Var, rhs: Abs & 'b & 'f | App & 'j & 'd | Var & 'e} & 'n & ~#Var | 'j & 'k & (App & {lhs: Var & {name: string} | ~Var, rhs: Abs & 'b & 'f | App & 'j & 'd | Var & 'e} & 'd | App & {lhs: Var & {name: string} | ~Var, rhs: Abs & 'b & 'f | App & 'j & 'd | Var & 'e} & 'n & ~#App) -//│ 'd <: { -//│ lhs: Abs & 'b | App & 'd | Var & 'e | 'n & ~#Abs & ~#App & ~#Var, -//│ rhs: Abs & 'b | App & 'd | Var & 'e | 'n & ~#Abs & ~#App & ~#Var -//│ } -//│ 'e <: Var | 'j & 'k & (App & {name: anything} & 'd | App & {name: anything} & 'n & ~#App) | 'f & 'g & (Abs & {name: anything} & 'n & ~#Abs | Abs & {name: anything} & 'b) -//│ 'n <: Var & ~#Var | Var & 'e | 'j & 'k & (App & ~#App | App & 'd) | 'f & 'g & (Abs & ~#Abs | Abs & 'b) -//│ 'k <: { -//│ lhs: App & 'k | Var | 'g & (Abs & ~#Abs | Abs & 'g), -//│ rhs: Abs & 'g | App & 'k | Var -//│ } -//│ 'g <: {lhs: Abs & 'g | App & 'k | Var, rhs: Abs & 'g | App & 'k | Var} -//│ 'f <: {lhs: Var, rhs: Abs & 'f | App & 'j | Var} -//│ 'j <: {lhs: Abs & 'f | App & 'j | Var, rhs: Abs & 'f | App & 'j | Var} -//│ 'a <: {lhs: Abs & 'a | App & 'c | Var, rhs: Abs & 'a | App & 'c | Var} -//│ 'c <: { -//│ lhs: App & 'c | Var | 'a & (Abs & 'a | Abs & ~#Abs), -//│ rhs: Abs & 'a | App & 'c | Var -//│ } -//│ = [Function: showSubst] - -showSubst(Var("x"), "x", Var("y")) -showSubst(Abs(Var("x"), Var("x")), "x", Var("z")) -showSubst(App(Var("x"), Var("y")), "x", Abs(Var("x"), Var("x"))) -showSubst(App(Abs(Var("x"), Var("x")), Var("x")), "x", Abs(Var("y"), Var("y"))) -showSubst(Abs(Var("x"), App(Var("x"), Var("y"))), "y", Var("x")) -//│ res: string -//│ = 'x [x / y] => y' -//│ res: string -//│ = '&x. x [x / z] => &x. x' -//│ res: string -//│ = '(x y) [x / &x. x] => ((&x. x) y)' -//│ res: string -//│ = '((&x. x) x) [x / &y. y] => ((&x. x) &y. y)' -//│ res: string -//│ = '&x. (x y) [y / x] => &z. (z x)' - -fun stepByValue(t) = - if t is - Var then None() - Abs then None() - App(lhs, rhs) and stepByValue(lhs) is - Some(lhs) then Some(App(lhs, rhs)) - None and stepByValue(rhs) is - Some(rhs) then Some(App(lhs, rhs)) - None and lhs is - Abs(Var(name), body) then Some(subst(body, name, rhs)) - _ then None() -//│ stepByValue: (Abs | App & 'a | Var) -> (None | Some & { -//│ value: Var & {name: string} | 'rhs | App & { -//│ lhs: Var & {name: string} | 'rhs | App & {lhs: 'lhs, rhs: Var & {name: string} | 'rhs | 'b | 'c | 'd | 'e} | 'b | 'c | 'd | 'e, -//│ rhs: 'rhs -//│ } | App & {lhs: 'lhs, rhs: Var & {name: string} | 'rhs | 'b | 'c | 'd | 'e} | 'b | 'c | 'd | 'e -//│ }) -//│ where -//│ 'a <: {lhs: 'lhs, rhs: 'rhs} -//│ 'lhs <: Abs & {rhs: Abs & 'c | App & 'f | Var & 'b | 'e & ~#Abs & ~#App & ~#Var} | Abs & ~#Abs | App & 'a | Var -//│ 'c :> Abs & { -//│ lhs: Var & {name: string}, -//│ rhs: Var & {name: string} | 'rhs0 | 'rhs | 'b | 'c | 'd | 'e -//│ } -//│ <: Abs & {lhs: Var & {name: string} | ~Var, rhs: Abs & 'c & 'g | App & 'h & 'f | Var & 'b} & 'g | Var & {lhs: Var & {name: string} | ~Var, rhs: Abs & 'c & 'g | App & 'h & 'f | Var & 'b} & 'b | Var & {lhs: Var & {name: string} | ~Var, rhs: Abs & 'c & 'g | App & 'h & 'f | Var & 'b} & 'e & ~#Var | 'h & (App & {lhs: Var & {name: string} | ~Var, rhs: Abs & 'c & 'g | App & 'h & 'f | Var & 'b} & 'f | App & {lhs: Var & {name: string} | ~Var, rhs: Abs & 'c & 'g | App & 'h & 'f | Var & 'b} & 'e & ~#App) -//│ 'rhs0 :> Var & {name: string} | 'rhs | 'b | 'c | 'd | 'e -//│ 'd :> Var & {name: string} | 'rhs | 'b | 'c | 'e | Abs & {lhs: Var & {name: string}, rhs: 'rhs0} | App & { -//│ lhs: Var & {name: string} | 'rhs | 'b | 'c | 'd | 'e, -//│ rhs: Var & {name: string} | 'rhs | 'b | 'c | 'd | 'e -//│ } -//│ 'rhs <: Abs & 'c & 'g & 'i | App & 'a & 'h & 'f & 'j | Var & 'b -//│ 'f <: { -//│ lhs: Abs & 'c | App & 'f | Var & 'b | 'e & ~#Abs & ~#App & ~#Var, -//│ rhs: Abs & 'c | App & 'f | Var & 'b | 'e & ~#Abs & ~#App & ~#Var -//│ } -//│ 'b :> Var & {name: string} -//│ <: Var | 'h & (App & {name: anything} & 'f | App & {name: anything} & 'e & ~#App) | 'g & (Abs & {name: anything} & 'e & ~#Abs | Abs & {name: anything} & 'c) -//│ 'e <: Var & ~#Var | Var & 'b | 'h & (App & ~#App | App & 'f) | 'g & (Abs & ~#Abs | Abs & 'c) -//│ 'i <: {lhs: anything, rhs: Abs & 'i | App & 'j | Var | ~Abs & ~App & ~Var} -//│ 'j <: { -//│ lhs: Abs & 'i | App & 'j | Var | ~Abs & ~App & ~Var, -//│ rhs: Abs & 'i | App & 'j | Var | ~Abs & ~App & ~Var -//│ } -//│ 'g <: {lhs: Var, rhs: Abs & 'g | App & 'h | Var} -//│ 'h <: {lhs: Abs & 'g | App & 'h | Var, rhs: Abs & 'g | App & 'h | Var} -//│ = [Function: stepByValue] - -fun showStepByValue(t) = - concat3(showTerm(t), " => ", if stepByValue(t) is - Some(t) then showTerm(t) - None then "stuck" - ) -//│ showStepByValue: (Abs & 'a | App & 'b & 'c | Var) -> string -//│ where -//│ 'c <: { -//│ lhs: App & 'c & 'd | Var | 'e & (Abs & {rhs: Abs & 'f | App & 'g | Var & 'h | 'i & ~#Abs & ~#App & ~#Var} | Abs & ~#Abs), -//│ rhs: Abs & 'f & 'j & 'e & 'k | App & 'c & 'l & 'g & 'd & 'm | Var & 'h -//│ } -//│ 'k <: {lhs: anything, rhs: Abs & 'k | App & 'm | Var | ~Abs & ~App & ~Var} -//│ 'm <: { -//│ lhs: Abs & 'k | App & 'm | Var | ~Abs & ~App & ~Var, -//│ rhs: Abs & 'k | App & 'm | Var | ~Abs & ~App & ~Var -//│ } -//│ 'f <: Abs & {lhs: Var & {name: string} | ~Var, rhs: Abs & 'f & 'j | App & 'l & 'g | Var & 'h} & 'j & 'e | Var & {lhs: Var & {name: string} | ~Var, rhs: Abs & 'f & 'j | App & 'l & 'g | Var & 'h} & 'h | Var & {lhs: Var & {name: string} | ~Var, rhs: Abs & 'f & 'j | App & 'l & 'g | Var & 'h} & 'i & ~#Var | 'l & 'd & (App & {lhs: Var & {name: string} | ~Var, rhs: Abs & 'f & 'j | App & 'l & 'g | Var & 'h} & 'g | App & {lhs: Var & {name: string} | ~Var, rhs: Abs & 'f & 'j | App & 'l & 'g | Var & 'h} & 'i & ~#App) -//│ 'g <: { -//│ lhs: Abs & 'f | App & 'g | Var & 'h | 'i & ~#Abs & ~#App & ~#Var, -//│ rhs: Abs & 'f | App & 'g | Var & 'h | 'i & ~#Abs & ~#App & ~#Var -//│ } -//│ 'h <: Var | 'l & 'd & (App & {name: anything} & 'g | App & {name: anything} & 'i & ~#App) | 'j & 'e & (Abs & {name: anything} & 'i & ~#Abs | Abs & {name: anything} & 'f) -//│ 'i <: Var & ~#Var | Var & 'h | 'l & 'd & (App & ~#App | App & 'g) | 'j & 'e & (Abs & ~#Abs | Abs & 'f) -//│ 'j <: {lhs: Var, rhs: Abs & 'j | App & 'l | Var} -//│ 'l <: {lhs: Abs & 'j | App & 'l | Var, rhs: Abs & 'j | App & 'l | Var} -//│ 'd <: { -//│ lhs: App & 'd | Var | 'e & (Abs & ~#Abs | Abs & 'e), -//│ rhs: Abs & 'e | App & 'd | Var -//│ } -//│ 'e <: {lhs: Abs & 'e | App & 'd | Var, rhs: Abs & 'e | App & 'd | Var} -//│ 'a <: {lhs: Abs & 'a | App & 'b | Var, rhs: Abs & 'a | App & 'b | Var} -//│ 'b <: { -//│ lhs: App & 'b | Var | 'a & (Abs & 'a | Abs & ~#Abs), -//│ rhs: Abs & 'a | App & 'b | Var -//│ } -//│ = [Function: showStepByValue] - -showStepByValue(Var("x")) -showStepByValue(Abs(Var("x"), Var("y"))) -showStepByValue(App(Var("x"), Var("y"))) -showStepByValue(App(Abs(Var("x"), Var("x")), Var("y"))) -//│ res: string -//│ = 'x => stuck' -//│ res: string -//│ = '&x. y => stuck' -//│ res: string -//│ = '(x y) => stuck' -//│ res: string -//│ = '((&x. x) y) => y' - -fun equalTerm(a, b) = - if a is - Var(na) and b is Var(nb) then eq(na)(nb) - Abs(la, ra) and b is Abs(lb, rb) then equalTerm(la, lb) && equalTerm(ra, rb) - App(la, ra) and b is App(lb, rb) then equalTerm(la, lb) && equalTerm(ra, rb) - _ then false -//│ equalTerm: (Abs & 'a | App & 'a | Var | ~Abs & ~App & ~Var, Abs & 'b | App & 'b | Var | ~Abs & ~App & ~Var,) -> bool -//│ where -//│ 'b <: { -//│ lhs: Abs & 'b | App & 'b | Var | ~Abs & ~App & ~Var, -//│ rhs: Abs & 'b | App & 'b | Var | ~Abs & ~App & ~Var -//│ } -//│ 'a <: { -//│ lhs: Abs & 'a | App & 'a | Var | ~Abs & ~App & ~Var, -//│ rhs: Abs & 'a | App & 'a | Var | ~Abs & ~App & ~Var -//│ } -//│ = [Function: equalTerm] From 4416b2484bf2a58fff611ee549ca8760a76b5334 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 3 Jan 2024 01:32:55 +0800 Subject: [PATCH 045/143] Transform a rare case found in `zipWith.mls` --- .../mlscript/ucs/stages/Transformation.scala | 78 +++++++++++++++++-- 1 file changed, 73 insertions(+), 5 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index 14f613c3..396c02b7 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -29,7 +29,7 @@ trait Transformation { self: Traceable with Diagnosable => /** * Transform a conjunction of terms into a nested split. The conjunction is * of the following form. - * ``` + * ```bnf * conjunction ::= term "is" term conjunction-tail * | "_" conjunction-tail * | term conjunction-tail @@ -74,10 +74,35 @@ trait Transformation { self: Traceable with Diagnosable => acc } case IfOpsApp(lhs, opsRhss) => - val (init, last) = extractLast(splitAnd(lhs)) - transformConjunction(init, TermBranch.Left(last, Split.from(opsRhss.map(transformOperatorBranch))).toSplit, false) + splitAnd(lhs) match { + case init :+ (scrutinee is pattern) => + // If `last` is in the form of `scrutinee is pattern`, the `op` in + // `opsRhss` must be `and`. Otherwise, it's a syntax error. + val innermost = transformBrokenIfOpsApp(opsRhss) + val inner = TermBranch.Match(scrutinee, PatternBranch(transformPattern(pattern), innermost).toSplit).toSplit + transformConjunction(init, inner, false) + case init :+ last => + transformConjunction(init, TermBranch.Left(last, Split.from(opsRhss.map(transformOperatorBranch))).toSplit, false) + case _ => rare + } } }(_ => "transformIfBody ==> ") + + /** + * Transform the case where `lhs` of `IfOpsApp` concludes pattern matching + * and we need to handle its `opsRhss`. This is special, because the first + * field of elements of `opsRhss` must be `and`. + */ + private def transformBrokenIfOpsApp(opsRhss: Ls[Var -> IfBody]): TermSplit = { + opsRhss.iterator.flatMap { + case Var("and") -> rhs => S(transformIfBody(rhs)) + case op -> rhs => + raiseError(PreTyping, + msg"cannot transform due to an illegal split operator ${op.name}" -> op.toLoc, + msg"the following branch will be discarded" -> rhs.toLoc) + N + }.foldLeft(Split.Nil: TermSplit)(_ ++ _) + } private def transformOperatorBranch(opsRhs: Var -> IfBody): OperatorBranch = opsRhs match { @@ -107,7 +132,48 @@ trait Transformation { self: Traceable with Diagnosable => PatternBranch(pattern, transformIfBody(rhs)).toSplit } case IfOpApp(lhs, op, rhs) => ??? // <-- Syntactic split of patterns are not supported. - case IfOpsApp(lhs, opsRhss) => ??? // <-- Syntactic split of patterns are not supported. + case IfOpsApp(lhs, opsRhss) => + // BEGIN TEMPORARY PATCH + // Generally, syntactic split of patterns are not supported. Examples + // like the following code is impossible. + // ``` + // fun pairwise(xs) = + // if xs is Cons of + // x, Nil then [x, x] + // x, Cons of x', tail then [x, x'] :: pairwise of tail + // else Nil + // ``` + // We could support this in future but it's disallowed for now. But I + // found some mis-parsed examples. For example, as in `zipWith.mls:76`. + // ``` + // fun zipWith_wrong(f, xs, ys) = + // if xs is Cons(x, xs) + // and ys is Cons(y, ys) + // and zipWith_wrong(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) + // else None + // ``` + // This is parsed as `{ and ( Cons x xs ) [ is ys ( Cons y ys ) ] }`. + // I think it's not very well-formed. But I still implement it for not + // breaking existing tests. + splitAnd(lhs) match { + case Nil => rare // It's impossible to be empty. + case pattern :: tail => + // Here, `pattern` will be `( Cons x xs )` and `tail` will be + // `[ is ys (Cons y ys) ]`. We can make a new `IfOpsApp`. + println(s"lol, pattern is $pattern") + println(s"lol, tail is $tail") + tail match { + case init :+ last => + println(s"lol, init is $init") + println(s"lol, last is $last") + val remake = IfOpsApp(last, opsRhss) + val following = transformConjunction(init, transformIfBody(remake), true) + PatternBranch(transformPattern(pattern), following).toSplit + case _ => // This can only be `Nil`. + PatternBranch(transformPattern(pattern), transformBrokenIfOpsApp(opsRhss)).toSplit + } + } + // END TEMPORARY PATCH case IfLet(rec, nme, rhs, body) => rare case IfBlock(lines) => lines.foldLeft(Split.empty[PatternBranch]) { case (acc, L(body)) => acc ++ transformPatternMatching(body) @@ -153,7 +219,9 @@ trait Transformation { self: Traceable with Diagnosable => (transformPattern(rawPattern), extraTest) } - private def splitAnd(t: Term): Ls[Term] = trace(s"splitAnd <== ${inspect.deep(t)}") { + // TODO: Maybe we can change the return type to `::[Term]` so that it will not + // empty. + private def splitAnd(t: Term): List[Term] = trace(s"splitAnd <== ${inspect.deep(t)}") { t match { case App( App(Var("and"), From 8e8251dc1e4a309803ef65b43596fe3a9d8a8807 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 3 Jan 2024 01:34:29 +0800 Subject: [PATCH 046/143] Literal patterns should be tested by case branches directly --- .../main/scala/mlscript/pretyper/Scope.scala | 2 +- .../scala/mlscript/ucs/context/CaseSet.scala | 10 ++-- .../mlscript/ucs/context/ScrutineeData.scala | 46 ++++++++++++------- .../ucs/stages/CoverageChecking.scala | 8 ++-- .../mlscript/ucs/stages/Desugaring.scala | 40 ++++++++-------- 5 files changed, 58 insertions(+), 48 deletions(-) diff --git a/shared/src/main/scala/mlscript/pretyper/Scope.scala b/shared/src/main/scala/mlscript/pretyper/Scope.scala index ffc041a1..e8c859ff 100644 --- a/shared/src/main/scala/mlscript/pretyper/Scope.scala +++ b/shared/src/main/scala/mlscript/pretyper/Scope.scala @@ -100,7 +100,7 @@ object Scope { """true,false,document,window,typeof,toString,not,succ,log,discard,negate, |round,add,sub,mul,div,sqrt,lt,le,gt,ge,slt,sle,sgt,sge,length,concat,eq, |ne,error,id,if,emptyArray,+,-,*,%,/,**,<,>,<=,>=,==,===,<>,&&,||,and, - |numAdd,numSub,numMul""" + |numAdd,numSub,numMul,NaN""" .stripMargin .split(",") .iterator diff --git a/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala b/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala index 6a897c5b..d517df52 100644 --- a/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala +++ b/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala @@ -71,13 +71,9 @@ final case class CaseSet(val cases: Map[Pattern, Ls[Loc]]) { } } - /** Add a type sysmbol as a class like pattern to the set. */ - def add(classLikeSymbol: TypeSymbol, location: Opt[Loc]): CaseSet = { - val classLikePattern = Pattern.ClassLike(classLikeSymbol) - copy(cases = cases.updatedWith(classLikePattern) { - case N => S(location.toList) - case S(locations) => S(location.toList ++ locations) - }) + def remove(literal: Lit): CaseSet = { + val literalPattern = Pattern.Literal(literal) + copy(cases - literalPattern) } /** Get an iterator of only patterns. */ diff --git a/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala b/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala index 37f839d8..907b9436 100644 --- a/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala +++ b/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala @@ -1,14 +1,26 @@ package mlscript.ucs.context import collection.mutable.{Buffer, Map => MutMap, SortedMap => MutSortedMap, SortedSet => MutSortedSet} -import mlscript.{Loc, Located, NuFunDef, NuTypeDef, TypeName, Var} -import mlscript.{Cls, Trt, Mxn, Als, Mod} +import mlscript.{Lit, Loc, Located, NuFunDef, NuTypeDef, TypeName, Var} import mlscript.pretyper.symbol.TypeSymbol import mlscript.utils._, shorthands._ import mlscript.ucs.context.CaseSet -class ClassPatternInfo(scrutinee: ScrutineeData) { +abstract class PatternInfo { private val locationsBuffer: Buffer[Loc] = Buffer.empty + + def addLocation(located: Located): Unit = located.getLoc.foreach(locationsBuffer += _) + + def addLocation(location: Opt[Loc]): Unit = locationsBuffer ++= location + + def firstOccurrence: Option[Loc] = locationsBuffer.headOption + + def locations: Ls[Loc] = locationsBuffer.toList + + def arity: Opt[Int] +} + +class ClassPatternInfo(scrutinee: ScrutineeData) extends PatternInfo { private var unappliedVarOpt: Opt[Var] = N private val parameters: MutSortedMap[Int, ScrutineeData] = MutSortedMap.empty @@ -24,10 +36,6 @@ class ClassPatternInfo(scrutinee: ScrutineeData) { parameters.getOrElseUpdate(index, scrutinee.freshSubScrutinee) } - def addLocation(located: Located): Unit = located.getLoc.foreach(locationsBuffer += _) - - def addLocation(location: Opt[Loc]): Unit = locationsBuffer ++= location - def getUnappliedVar(default: => Var): Var = unappliedVarOpt.getOrElse { val unappliedVar = default @@ -35,23 +43,20 @@ class ClassPatternInfo(scrutinee: ScrutineeData) { unappliedVar } - def arity: Opt[Int] = parameters.keysIterator.maxOption.map(_ + 1) - - def firstOccurrence: Option[Loc] = locationsBuffer.headOption - - def locations: Ls[Loc] = locationsBuffer.toList + override def arity: Opt[Int] = parameters.keysIterator.maxOption.map(_ + 1) } -class TuplePatternInfo(scrutinee: ScrutineeData) { - private val locationsBuffer: Buffer[Loc] = Buffer.empty +class TuplePatternInfo(scrutinee: ScrutineeData) extends PatternInfo { private val fields: MutSortedMap[Int, ScrutineeData] = MutSortedMap.empty def getField(index: Int): ScrutineeData = fields.getOrElseUpdate(index, scrutinee.freshSubScrutinee) - def arity: Opt[Int] = fields.keysIterator.maxOption.map(_ + 1) + override def arity: Opt[Int] = fields.keysIterator.maxOption.map(_ + 1) +} - def locations: Ls[Loc] = locationsBuffer.toList +class LiteralPatternInfo extends PatternInfo { + override def arity: Opt[Int] = N } class ScrutineeData(val context: Context, parent: Opt[ScrutineeData]) { @@ -67,6 +72,8 @@ class ScrutineeData(val context: Context, parent: Opt[ScrutineeData]) { private var tuplePatternOpt: Opt[TuplePatternInfo] = N private var alisesSet: MutSortedSet[Var] = MutSortedSet.empty + private val literalPatterns: MutMap[Lit, LiteralPatternInfo] = MutMap.empty + def +=(alias: Var): Unit = alisesSet += alias def withAlias(alias: Var): ScrutineeData = { this += alias; this } @@ -109,6 +116,10 @@ class ScrutineeData(val context: Context, parent: Opt[ScrutineeData]) { tuplePattern } + /** Get the tuple pattern and create a new one if there isn't. */ + def getOrCreateLiteralPattern(literal: Lit): LiteralPatternInfo = + literalPatterns.getOrElseUpdate(literal, new LiteralPatternInfo) + def classLikePatternsIterator: Iterator[TypeSymbol -> ClassPatternInfo] = classLikePatterns.iterator /** Get the name representation of patterns. Only for debugging. */ @@ -132,6 +143,9 @@ class ScrutineeData(val context: Context, parent: Opt[ScrutineeData]) { val tuplePattern = tuplePatternOpt.map { tuplePattern => Pattern.Tuple() -> tuplePattern.locations }.toMap[Pattern, Ls[Loc]] + val literalPatterns = this.literalPatterns.iterator.map { case (literal, pattern) => + Pattern.Literal(literal) -> pattern.locations + }.toMap[Pattern, Ls[Loc]] CaseSet(cases ++ tuplePattern) } } diff --git a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala index baa1fea2..b583c1e0 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala @@ -87,9 +87,11 @@ trait CoverageChecking { self: mlscript.pretyper.Traceable => case N => ErrorReport("unvisited scrutinee", Nil, Diagnostic.PreTyping) })) } - case (diagnostics, (_: Lit) -> _) => - println("CANNOT check literal patterns") - diagnostics + case ((unseenPatterns, diagnostics), (literal: Lit) -> body) => + ( + unseenPatterns.remove(literal), + diagnostics ++ checkCoverage(body, newPending, working - namedScrutinee, seen) + ) }) { case ((missingCases, diagnostics), N) => println("remaining cases should are not covered") diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index c9a38c73..8987c987 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -122,13 +122,8 @@ trait Desugaring { self: PreTyper => } }() - private def makeLiteralTest(test: Var, scrutinee: Var, literal: Lit)(implicit scope: Scope): c.Split => c.Split = - next => c.Split.Let( - rec = false, - name = test, - term = mkBinOp(scrutinee, Var("==="), literal, true), - tail = c.Branch(test, truePattern, next) :: c.Split.Nil - ) + private def makeLiteralTest(scrutinee: Var, literal: Lit)(implicit scope: Scope): c.Split => c.Split = + next => c.Branch(scrutinee, c.Pattern.Literal(literal), next) :: c.Split.Nil private def flattenClassParameters( parentScrutineeVar: Var, @@ -189,12 +184,15 @@ trait Desugaring { self: PreTyper => vari.withSymbol(new LocalTermSymbol(vari)) } val nestedPatterns = flattenClassParameters(scrutineeVar, patternClassSymbol, parameters) + println(s"nestedPatterns = $nestedPatterns") // First, handle bindings of parameters of the current class pattern. + val identity = (split: c.Split) => split val bindParameters = nestedPatterns.iterator.zipWithIndex.foldRight[c.Split => c.Split](identity) { case ((N, _), bindNextParameter) => bindNextParameter case ((S(parameter -> _), index), bindNextParameter) => bindNextParameter.andThen { c.Split.Let(false, parameter, Sel(unapp, Var(index.toString)), _) } } + println(s"bindParameters === identity: ${bindParameters === identity}") val bindAll = if (bindParameters === identity) bindParameters else bindParameters.andThen { c.Split.Let(false, unapp, makeUnapplyCall(scrutineeVar, pattern.nme), _): c.Split } @@ -239,9 +237,11 @@ trait Desugaring { self: PreTyper => val (scopeWithNestedAll, bindNestedAll) = desugarClassPattern(pattern, nme, scope) (scopeWithNestedAll, split => bindPrevious(bindNestedAll(split) :: c.Split.Nil)) case ((scope, bindPrevious), S(nme -> S(pattern: s.LiteralPattern))) => - val test = context.freshTest().withFreshSymbol - println(s"fresh test var: ${test.name}") - (scope + test.symbol, makeLiteralTest(test, nme, pattern.literal)(scope).andThen(bindPrevious)) + nme.getOrCreateScrutinee + .withAlias(nme) + .getOrCreateLiteralPattern(pattern.literal) + .addLocation(pattern.literal) + (scope, makeLiteralTest(nme, pattern.literal)(scope).andThen(bindPrevious)) case ((scope, bindPrevious), S(nme -> S(s.TuplePattern(fields)))) => val (scopeWithNestedAll, bindNestedAll) = desugarTuplePattern(fields, nme, scope) (scopeWithNestedAll, bindNestedAll.andThen(bindPrevious)) @@ -295,17 +295,15 @@ trait Desugaring { self: PreTyper => raiseError(PreTyping, msg"alias pattern is not supported for now" -> pattern.toLoc) rec(scrutineeVar, tail) case s.LiteralPattern(literal) => - val test = context.freshTest().withFreshSymbol - c.Split.Let( - rec = false, - name = test, - term = mkBinOp(scrutineeVar, Var("==="), literal, true), - tail = c.Branch( - scrutinee = test, - pattern = truePattern, - continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope + test.symbol, context) - ) :: rec(scrutineeVar, tail) - ) + scrutineeVar.getOrCreateScrutinee + .withAlias(scrutineeVar) + .getOrCreateLiteralPattern(literal) + .addLocation(literal) + c.Branch( + scrutinee = scrutineeVar, + pattern = c.Pattern.Literal(literal), + continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope, context) + ) :: rec(scrutineeVar, tail) case s.ConcretePattern(nme) => val test = context.freshTest().withFreshSymbol c.Split.Let( From b6c868563ff29ef443ed5eb259bf89605a7f3f1e Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 3 Jan 2024 01:35:42 +0800 Subject: [PATCH 047/143] Support `Forall` term and more shapes of function parameters --- shared/src/main/scala/mlscript/pretyper/PreTyper.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index 9b60a5ac..dc349fbe 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -11,13 +11,17 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D private def extractParameters(fields: Term): Ls[LocalTermSymbol] = trace(s"extractParameters <== ${inspect.deep(fields)}") { fields match { + case nme: Var => new LocalTermSymbol(nme) :: Nil case Tup(arguments) => arguments.flatMap { case (S(nme: Var), Fld(_, _)) => new LocalTermSymbol(nme) :: Nil case (_, Fld(_, nme: Var)) => new LocalTermSymbol(nme) :: Nil - case (_, Fld(_, Bra(false, nme: Var))) => new LocalTermSymbol(nme) :: Nil + case (_, Fld(_, Bra(false, term))) => extractParameters(term) case (_, Fld(_, tuple @ Tup(_))) => extractParameters(tuple) case (_, Fld(_, Asc(term, _))) => extractParameters(term) + case (_, Fld(_, _: Lit)) => Nil + case (_, Fld(_, App(Var(name), parameters))) if name.headOption.forall(_.isUpper) => + extractParameters(parameters) case (_, Fld(_, parameter)) => println(s"unknown parameter: ${inspect.deep(parameter)}") ??? @@ -98,7 +102,7 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D case L(t) => traverseTerm(t) case R(Fld(_, t)) => traverseTerm(t) } - case Forall(params, body) => ??? // TODO: When? + case Forall(params, body) => traverseTerm(body) case Rcd(fields) => fields.foreach { case (_, Fld(_, t)) => traverseTerm(t) } case CaseOf(trm, cases) => case With(trm, fields) => traverseTerm(trm); traverseTerm(fields) From 39c8e46dd558f72228ea3a949ffc05618bb34581 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 3 Jan 2024 04:25:45 +0800 Subject: [PATCH 048/143] Fix desugaring and normalization for literal patterns --- .../src/main/scala/mlscript/JSBackend.scala | 3 +- .../main/scala/mlscript/ucs/DesugarUCS.scala | 10 +++ .../mlscript/ucs/context/ScrutineeData.scala | 14 +++ .../mlscript/ucs/stages/Desugaring.scala | 21 +++-- .../mlscript/ucs/stages/Normalization.scala | 85 ++++++++++++++++--- .../mlscript/ucs/stages/PostProcessing.scala | 2 +- .../diff/pretyper/ucs/patterns/Literals.mls | 79 ++++++++++++++--- 7 files changed, 183 insertions(+), 31 deletions(-) diff --git a/shared/src/main/scala/mlscript/JSBackend.scala b/shared/src/main/scala/mlscript/JSBackend.scala index 34b26f46..2271da07 100644 --- a/shared/src/main/scala/mlscript/JSBackend.scala +++ b/shared/src/main/scala/mlscript/JSBackend.scala @@ -380,8 +380,7 @@ abstract class JSBackend(allowUnresolvedSymbols: Bool) { case _ => throw new CodeGenError(s"unknown match case: $name") } } - case lit: Lit => - JSBinary("===", scrut, JSLit(lit.idStr)) + case lit: Lit => JSBinary("===", scrut, translateTerm(lit)) }, _, _ diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index aed60275..91a39491 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -67,6 +67,16 @@ trait DesugarUCS extends Transformation ) } + def withResolvedTermSymbol(implicit scope: Scope): Var = { + nme.symbol = nme.resolveTermSymbol + nme + } + + def resolveTermSymbol(implicit scope: Scope): TermSymbol = + scope.getTermSymbol(nme.name).getOrElse { + throw new DesugaringException(msg"Undefined symbol found in patterns." -> nme.toLoc :: Nil) + } + def withResolvedTypeSymbol(implicit scope: Scope): Var = { nme.symbol = nme.resolveTypeSymbol nme diff --git a/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala b/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala index 907b9436..251e674f 100644 --- a/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala +++ b/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala @@ -59,6 +59,16 @@ class LiteralPatternInfo extends PatternInfo { override def arity: Opt[Int] = N } +/** + * This can be actually merged with `LiteralPatternInfo`. However, there's no + * `Lit` sub-classes for Boolean types, so the representation is a little bit + * awkward, also, it makes sense to consider Boolean patterns separately + * because we can check the Boolean exhaustiveness with them. + */ +class BooleanPatternInfo extends PatternInfo { + override def arity: Opt[Int] = N +} + class ScrutineeData(val context: Context, parent: Opt[ScrutineeData]) { private val locations: Buffer[Loc] = Buffer.empty private var generatedVarOpt: Opt[Var] = N @@ -73,6 +83,7 @@ class ScrutineeData(val context: Context, parent: Opt[ScrutineeData]) { private var alisesSet: MutSortedSet[Var] = MutSortedSet.empty private val literalPatterns: MutMap[Lit, LiteralPatternInfo] = MutMap.empty + private val booleanPatterns: MutMap[Bool, BooleanPatternInfo] = MutMap.empty def +=(alias: Var): Unit = alisesSet += alias @@ -120,6 +131,9 @@ class ScrutineeData(val context: Context, parent: Opt[ScrutineeData]) { def getOrCreateLiteralPattern(literal: Lit): LiteralPatternInfo = literalPatterns.getOrElseUpdate(literal, new LiteralPatternInfo) + def getOrCreateBooleanPattern(value: Bool): BooleanPatternInfo = + booleanPatterns.getOrElseUpdate(value, new BooleanPatternInfo) + def classLikePatternsIterator: Iterator[TypeSymbol -> ClassPatternInfo] = classLikePatterns.iterator /** Get the name representation of patterns. Only for debugging. */ diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 8987c987..db8b6679 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -48,7 +48,7 @@ trait Desugaring { self: PreTyper => * `Cons(hd, tl)`, then the name of `hd` will be `x$Cons_0` and the name of * `tl` will be `x$Cons_1`. */ - private def freshSubScrutinee(parentScrutinee: Var, parentClassName: Str, index: Int): Var = + private def freshSubScrutineeVar(parentScrutinee: Var, parentClassName: Str, index: Int): Var = Var(s"${parentScrutinee}$$${parentClassName}_${index.toString}") /** @@ -67,6 +67,7 @@ trait Desugaring { self: PreTyper => * Boolean conditions. */ private def truePattern(implicit scope: Scope) = c.Pattern.Class(Var("true").withResolvedTypeSymbol) + private def falsePattern(implicit scope: Scope) = c.Pattern.Class(Var("false").withResolvedTypeSymbol) private def desugarTermSplit(split: s.TermSplit)(implicit termPart: PartialTerm, scope: Scope, context: Context): c.Split = split match { @@ -139,7 +140,7 @@ trait Desugaring { self: PreTyper => val subScrutinee = classPattern.getParameter(index).withAlias(name) S(name.withFreshSymbol.withScrutinee(subScrutinee) -> N) case (parameterPattern @ (s.ClassPattern(_, _) | s.LiteralPattern(_) | s.TuplePattern(_)), index) => - val subScrutineeVar = freshSubScrutinee(parentScrutineeVar, parentClassLikeSymbol.name, index) + val subScrutineeVar = freshSubScrutineeVar(parentScrutineeVar, parentClassLikeSymbol.name, index) val symbol = new LocalTermSymbol(subScrutineeVar) symbol.addScrutinee(classPattern.getParameter(index).withAlias(subScrutineeVar)) S(subScrutineeVar.withSymbol(symbol) -> S(parameterPattern)) @@ -264,7 +265,7 @@ trait Desugaring { self: PreTyper => S(name.withFreshSymbol.withScrutinee(tuplePattern.getField(index)) -> N) case (parameterPattern @ (s.ClassPattern(_, _) | s.LiteralPattern(_) | s.TuplePattern(_)), index) => val arity = fields.length - val subScrutineeVar = freshSubScrutinee(parentScrutineeVar, s"Tuple$$$arity", index) + val subScrutineeVar = freshSubScrutineeVar(parentScrutineeVar, s"Tuple$$$arity", index) val symbol = new LocalTermSymbol(subScrutineeVar) symbol.addScrutinee(tuplePattern.getField(index).withAlias(subScrutineeVar)) S(subScrutineeVar.withSymbol(symbol) -> S(parameterPattern)) @@ -304,7 +305,17 @@ trait Desugaring { self: PreTyper => pattern = c.Pattern.Literal(literal), continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope, context) ) :: rec(scrutineeVar, tail) - case s.ConcretePattern(nme) => + case s.ConcretePattern(nme @ (Var("true") | Var("false"))) => + scrutineeVar.getOrCreateScrutinee + .withAlias(scrutineeVar) + .getOrCreateBooleanPattern(nme.name === "true") + .addLocation(nme) + c.Branch( + scrutinee = scrutineeVar, + pattern = c.Pattern.Class(nme.withResolvedTypeSymbol), + continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope, context) + ) :: rec(scrutineeVar, tail) + case s.ConcretePattern(nme) => val test = context.freshTest().withFreshSymbol c.Split.Let( rec = false, @@ -354,7 +365,7 @@ trait Desugaring { self: PreTyper => case s.Split.Nil => c.Split.Nil } scrutineeTerm match { - case nme: Var => rec(nme, split) + case nme: Var => rec(nme.withResolvedTermSymbol, split) case other => val alias = context.freshScrutineeVar().withFreshSymbol c.Split.Let(false, alias, other, rec(alias, split)(scope + alias.symbol)) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 5f42fd2b..2aecc7ac 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -12,13 +12,22 @@ import mlscript.{App, CaseOf, Fld, FldFlags, Let, Loc, Sel, Term, Tup, Var, StrL import mlscript.{CaseBranches, Case, Wildcard, NoCases} import mlscript.Message, Message.MessageContext import mlscript.utils._, shorthands._ +import mlscript.pretyper.{Traceable, Diagnosable} -trait Normalization { self: mlscript.pretyper.Traceable => +trait Normalization { self: Traceable with Diagnosable => import Normalization._ private def concatImpl(these: Split, those: Split)(implicit context: Context, generatedVars: Set[Var]): Split = if (these.hasElse) these else (these match { - case these @ Split.Cons(_, tail) => these.copy(tail = concatImpl(tail, those)) + case these @ Split.Cons(head, tail) => + println(s"found a cons: $head") + if (head.continuation.hasElse) { + these.copy(tail = concatImpl(tail, those)) + } else { + println("found a branch without default, duplicating...") + val newHead = head.copy(continuation = concat(head.continuation, those)) + these.copy(head = newHead, tail = concatImpl(tail, those)) + } case these @ Split.Let(_, nme, _, tail) => if (those.freeVars contains nme) { val fresh = context.freshShadowed() @@ -50,38 +59,50 @@ trait Normalization { self: mlscript.pretyper.Traceable => private def normalizeToTerm(split: Split)(implicit context: Context, generatedVars: Set[Var]): Term = trace("normalizeToTerm <==") { split match { case Split.Cons(Branch(scrutinee, Pattern.Name(nme), continuation), tail) => - println(s"alias $scrutinee => $nme") + println(s"normalizing name pattern ${scrutinee.name} is ${nme.name}") Let(false, nme, scrutinee, normalizeToTerm(concat(continuation, tail))) // Skip Boolean conditions as scrutinees, because they only appear once. case Split.Cons(Branch(test, pattern @ Pattern.Class(nme @ Var("true")), continuation), tail) if context.isTestVar(test) => + println(s"normalizing true pattern: ${test.name} is true") val trueBranch = normalizeToTerm(concat(continuation, tail)) val falseBranch = normalizeToCaseBranches(tail) CaseOf(test, Case(nme, trueBranch, falseBranch)) case Split.Cons(Branch(ScrutineeData.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Literal(literal), continuation), tail) => - val trueBranch = normalizeToTerm(specialize(concat(continuation, tail), true)(scrutineeVar, scrutinee, pattern, context)) + println(s"normalizing literal pattern: ${scrutineeVar.name} is $literal") + val concatenatedTrueBranch = concat(continuation, tail) + println(s"true branch: ${showSplit(concatenatedTrueBranch)}") + val trueBranch = normalizeToTerm(specialize(concatenatedTrueBranch, true)(scrutineeVar, scrutinee, pattern, context)) + println(s"false branch: ${showSplit(tail)}") val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context)) CaseOf(scrutineeVar, Case(literal, trueBranch, falseBranch)) - // false class parameters. Easy case Split.Cons(Branch(ScrutineeData.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Class(nme), continuation), tail) => - println(s"match ${scrutineeVar.name} with $nme (has location: ${nme.toLoc.isDefined})") + println(s"normalizing class pattern: ${scrutineeVar.name} is ${nme.name}") + // println(s"match ${scrutineeVar.name} with $nme (has location: ${nme.toLoc.isDefined})") val trueBranch = normalizeToTerm(specialize(concat(continuation, tail), true)(scrutineeVar, scrutinee, pattern, context)) val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context)) CaseOf(scrutineeVar, Case(nme, trueBranch, falseBranch)) case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => + println(s"unknown pattern ${pattern.getClass().getSimpleName()}") throw new NormalizationException((msg"Unsupported pattern: ${pattern.toString}" -> pattern.toLoc) :: Nil) case Split.Let(rec, Var("_"), rhs, tail) => normalizeToTerm(tail) case Split.Let(_, nme, _, tail) if context.isScrutineeVar(nme) && generatedVars.contains(nme) => + println(s"normalizing let binding of generated variable: ${nme.name}") normalizeToTerm(tail) case Split.Let(rec, nme, rhs, tail) => + println(s"normalizing let binding ${nme.name}") val newDeclaredBindings = if (context.isGeneratedVar(nme)) generatedVars + nme else generatedVars Let(rec, nme, rhs, normalizeToTerm(tail)(context, newDeclaredBindings)) - case Split.Else(default) => default - case Split.Nil => println("unexpected empty split"); ??? + case Split.Else(default) => + println(s"normalizing default: $default") + default + case Split.Nil => + println(s"normalizing nil") + ??? } - }(_ => "normalizeToTerm ==> ") + }(split => "normalizeToTerm ==> " + showNormalizedTerm(split)) private def normalizeToCaseBranches(split: Split)(implicit context: Context, generatedVars: Set[Var]): CaseBranches = - trace("normalizeToCaseBranches") { + trace(s"normalizeToCaseBranches <== $split") { split match { // case Split.Cons(head, Split.Nil) => Case(head.pattern, normalizeToTerm(head.continuation), NoCases) case other: Split.Cons => Wildcard(normalizeToTerm(other)) @@ -98,7 +119,7 @@ trait Normalization { self: mlscript.pretyper.Traceable => case Split.Else(default) => Wildcard(default) case Split.Nil => NoCases } - }() + }(r => "normalizeToCaseBranches ==> ") // Specialize `split` with the assumption that `scrutinee` matches `pattern`. private def specialize @@ -180,6 +201,48 @@ trait Normalization { self: mlscript.pretyper.Traceable => tail = specialize(tail, matchOrNot) ) } + // Literal pattern. Positive. + case (true, split @ Split.Cons(head @ Branch(ScrutineeData.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Literal(otherLiteral), continuation), tail)) => + if (scrutinee === otherScrutinee) { + println(s"scrutinee: ${scrutineeVar.name} is ${otherScrutineeVar.name}") + pattern match { + case Pattern.Literal(literal) if literal === otherLiteral => + val specialized = specialize(continuation, true) + if (specialized.hasElse) { + println("tail is discarded") + specialized.withDiagnostic( + msg"Discarded split because of else branch" -> None // TODO: Synthesize locations + ) + } else { + specialized ++ specialize(tail, true) + } + case _ => specialize(tail, true) + } + } else { + println(s"scrutinee: ${scrutineeVar.name} is NOT ${otherScrutineeVar.name}") + split.copy( + head = head.copy(continuation = specialize(continuation, true)), + tail = specialize(tail, true) + ) + } + // Literal pattern. Negative. + case (false, split @ Split.Cons(head @ Branch(ScrutineeData.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Literal(otherLiteral), continuation), tail)) => + if (scrutinee === otherScrutinee) { + println(s"scrutinee: ${scrutineeVar.name} is ${otherScrutineeVar.name}") + pattern match { + case Pattern.Literal(literal) if literal === otherLiteral => + specialize(tail, false) + case _ => + // No need to check `continuation` because literals don't overlap. + split.copy(tail = specialize(tail, false)) + } + } else { + println(s"scrutinee: ${scrutineeVar.name} is NOT ${otherScrutineeVar.name}") + split.copy( + head = head.copy(continuation = specialize(continuation, false)), + tail = specialize(tail, false) + ) + } // Other patterns. Not implemented. case (_, Split.Cons(Branch(otherScrutineeVar, pattern, continuation), tail)) => println(s"unsupported pattern: $pattern") diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index e288ead4..4958ec79 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -18,7 +18,7 @@ trait PostProcessing { self: DesugarUCS with mlscript.pretyper.Traceable => println(s"found a UNARY case: $scrutineeVar is $className") println("post-processing the body") top.copy(cases = fst.copy(body = postProcess(body))) - case top @ CaseOf(test: Var, fst @ Case(Var("true"), trueBranch, Wildcard(falseBranch))) if context.isTestVar(test) => + case top @ CaseOf(test: Var, fst @ Case(Var("true"), trueBranch, Wildcard(falseBranch))) => println(s"found a if-then-else case: $test is true") val processedTrueBranch = postProcess(trueBranch) val processedFalseBranch = postProcess(falseBranch) diff --git a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls index fe9713c6..2dd0048f 100644 --- a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls +++ b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls @@ -10,18 +10,25 @@ class Pair[A, B](x: A, y: B) //│ class Pair[A, B](x: A, y: B) fun f(x) = if x is Some(1) then true else false -//│ fun f: (Object & ~#Some | Some[Eql[1]]) -> Bool +//│ fun f: (Object & ~#Some | Some[Object]) -> Bool + +[f(Some(1)), f(None), f(Some(2)), f(Some(-1))] +//│ [Bool, Bool, Bool, Bool] +//│ res +//│ = [ true, false, false, false ] -:e -// TODO: Proper diagnostic information reporting. fun f(x) = if x is Some(1) then true -//│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.17: fun f(x) = if x is Some(1) then true -//│ ║ ^^^^ -//│ ╟── application of type `Bool` is not an instance of type `true` -//│ ║ l.17: fun f(x) = if x is Some(1) then true -//│ ╙── ^ -//│ fun f: Some[Eql[1]] -> true +//│ fun f: Some[1] -> true + +fun f(x) = if x is + Some(1) then true + Some(2) then false +//│ fun f: Some[1 | 2] -> Bool + +[f(Some(1)), f(Some(2))] +//│ [Bool, Bool] +//│ res +//│ = [ true, false ] fun g(x) = if x then 1 else 2 //│ fun g: Bool -> (1 | 2) @@ -29,13 +36,18 @@ fun g(x) = if x then 1 else 2 :e fun test_must_be_boolean(x) = if 0 then 1 else 2 //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.30: fun test_must_be_boolean(x) = if 0 then 1 else 2 +//│ ║ l.37: fun test_must_be_boolean(x) = if 0 then 1 else 2 //│ ║ ^ //│ ╙── integer literal of type `0` is not an instance of type `Bool` //│ fun test_must_be_boolean: anything -> (1 | 2) fun g(x) = if x is true then 1 else 2 -//│ fun g: Eql[true] -> (1 | 2) +//│ fun g: Object -> (1 | 2) + +[g(true), g(false), g(None)] +//│ [1 | 2, 1 | 2, 1 | 2] +//│ res +//│ = [ 1, 2, 2 ] fun g(x) = if x && true is true then 1 else 2 //│ fun g: Bool -> (1 | 2) @@ -43,3 +55,46 @@ fun g(x) = if x && true is true then 1 else 2 fun h(x) = if (x : Bool) then 1 else 2 //│ fun h: Bool -> (1 | 2) +// Currently post-processing cannot hanlde this case. The desugared term is not +// perfect. Also, is the inferred type wrong? +fun mix(x) = if x is + true then "true" + Some(value) then "Some" + 0 then "zero" +//│ fun mix: (0 | Some[anything]) -> ("Some" | "true" | "zero") + +[mix(true), mix(Some(1)), mix(0)] +//│ ["Some" | "true" | "zero", "Some" | "true" | "zero", "Some" | "true" | "zero"] +//│ res +//│ = [ 'true', 'Some', 'zero' ] + +:e +[mix(false), mix(None)] +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.72: [mix(false), mix(None)] +//│ ║ ^^^^^^^^^^ +//│ ╟── reference of type `false` does not match type `0` +//│ ║ l.72: [mix(false), mix(None)] +//│ ║ ^^^^^ +//│ ╟── Note: constraint arises from literal pattern: +//│ ║ l.63: 0 then "zero" +//│ ║ ^ +//│ ╟── from reference: +//│ ║ l.60: fun mix(x) = if x is +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.72: [mix(false), mix(None)] +//│ ║ ^^^^^^^^^ +//│ ╟── reference of type `None` does not match type `0` +//│ ║ l.72: [mix(false), mix(None)] +//│ ║ ^^^^ +//│ ╟── Note: constraint arises from literal pattern: +//│ ║ l.63: 0 then "zero" +//│ ║ ^ +//│ ╟── from reference: +//│ ║ l.60: fun mix(x) = if x is +//│ ╙── ^ +//│ ["Some" | "true" | "zero" | error, "Some" | "true" | "zero" | error] +//│ res +//│ Runtime error: +//│ Error: non-exhaustive case expression From a55e9597df73fc7d4f3e855be2b76e7c909dbdee Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 3 Jan 2024 15:04:45 +0800 Subject: [PATCH 049/143] Remove vertically aligned lines --- .../main/scala/mlscript/ucs/stages/Desugaring.scala | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 480ef239..e58d9b56 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -296,20 +296,14 @@ trait Desugaring { self: PreTyper => raiseError(PreTyping, msg"alias pattern is not supported for now" -> pattern.toLoc) rec(scrutineeVar, tail) case s.LiteralPattern(literal) => - scrutineeVar.getOrCreateScrutinee - .withAlias(scrutineeVar) - .getOrCreateLiteralPattern(literal) - .addLocation(literal) + scrutineeVar.getOrCreateScrutinee.withAlias(scrutineeVar).getOrCreateLiteralPattern(literal).addLocation(literal) c.Branch( scrutinee = scrutineeVar, pattern = c.Pattern.Literal(literal), continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope, context) ) :: rec(scrutineeVar, tail) case s.ConcretePattern(nme @ (Var("true") | Var("false"))) => - scrutineeVar.getOrCreateScrutinee - .withAlias(scrutineeVar) - .getOrCreateBooleanPattern(nme.name === "true") - .addLocation(nme) + scrutineeVar.getOrCreateScrutinee.withAlias(scrutineeVar).getOrCreateBooleanPattern(nme.name === "true").addLocation(nme) c.Branch( scrutinee = scrutineeVar, pattern = c.Pattern.Class(nme.withResolvedTypeSymbol), From 70f825aab0324fafb8e71af14584cb7455c4d28d Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 3 Jan 2024 15:09:13 +0800 Subject: [PATCH 050/143] Replace `rare` with `die` --- .../scala/mlscript/ucs/stages/Transformation.scala | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index 396c02b7..a02dcb36 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -55,7 +55,7 @@ trait Transformation { self: Traceable with Diagnosable => private def transformIfBody(body: IfBody): TermSplit = trace(s"transformIfBody <== ${inspect.shallow(body)}") { body match { case IfThen(expr, rhs) => transformConjunction(splitAnd(expr), Split.then(rhs), true) - case IfLet(isRec, name, rhs, body) => rare + case IfLet(isRec, name, rhs, body) => die case IfElse(expr) => Split.then(expr) case IfOpApp(lhs, Var("is"), rhs) => val (tests, scrutinee) = extractLast(splitAnd(lhs)) @@ -83,7 +83,7 @@ trait Transformation { self: Traceable with Diagnosable => transformConjunction(init, inner, false) case init :+ last => transformConjunction(init, TermBranch.Left(last, Split.from(opsRhss.map(transformOperatorBranch))).toSplit, false) - case _ => rare + case _ => die } } }(_ => "transformIfBody ==> ") @@ -156,7 +156,7 @@ trait Transformation { self: Traceable with Diagnosable => // I think it's not very well-formed. But I still implement it for not // breaking existing tests. splitAnd(lhs) match { - case Nil => rare // It's impossible to be empty. + case Nil => die // It's impossible to be empty. case pattern :: tail => // Here, `pattern` will be `( Cons x xs )` and `tail` will be // `[ is ys (Cons y ys) ]`. We can make a new `IfOpsApp`. @@ -174,7 +174,7 @@ trait Transformation { self: Traceable with Diagnosable => } } // END TEMPORARY PATCH - case IfLet(rec, nme, rhs, body) => rare + case IfLet(rec, nme, rhs, body) => die case IfBlock(lines) => lines.foldLeft(Split.empty[PatternBranch]) { case (acc, L(body)) => acc ++ transformPatternMatching(body) case (acc, R(NuFunDef(S(rec), nme, _, _, L(rhs)))) => @@ -237,11 +237,9 @@ trait Transformation { self: Traceable with Diagnosable => } object Transformation { - private def rare: Nothing = lastWords("found a very rare case during desugaring UCS terms") - private def extractLast[T](xs: List[T]): (List[T], T) = xs match { case init :+ last => init -> last - case _ => rare + case _ => die } private object is { From 3ff17d14ed10974bef5ab1c1a53d4a50e55d129f Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 3 Jan 2024 15:21:56 +0800 Subject: [PATCH 051/143] Remove `inspect` from this branch --- .../scala/mlscript/pretyper/PreTyper.scala | 18 +- .../main/scala/mlscript/ucs/DesugarUCS.scala | 3 +- .../scala/mlscript/ucs/context/CaseSet.scala | 2 +- .../ucs/stages/CoverageChecking.scala | 2 +- .../mlscript/ucs/stages/PostProcessing.scala | 12 +- .../mlscript/ucs/stages/Transformation.scala | 12 +- .../main/scala/mlscript/utils/inspect.scala | 193 ------------------ .../test/diff/pretyper/ucs/examples/ULC.mls | 8 +- 8 files changed, 29 insertions(+), 221 deletions(-) delete mode 100644 shared/src/main/scala/mlscript/utils/inspect.scala diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index dc349fbe..8b4ba5ec 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -9,7 +9,7 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D import PreTyper._ private def extractParameters(fields: Term): Ls[LocalTermSymbol] = - trace(s"extractParameters <== ${inspect.deep(fields)}") { + trace(s"extractParameters <== $fields") { fields match { case nme: Var => new LocalTermSymbol(nme) :: Nil case Tup(arguments) => @@ -23,15 +23,15 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D case (_, Fld(_, App(Var(name), parameters))) if name.headOption.forall(_.isUpper) => extractParameters(parameters) case (_, Fld(_, parameter)) => - println(s"unknown parameter: ${inspect.deep(parameter)}") + println(s"unknown parameter: $parameter") ??? } case PlainTup(arguments @ _*) => arguments.map { case nme: Var => new LocalTermSymbol(nme) - case other => println("Unknown parameters: " + inspect.deep(other)); ??? // TODO: bad + case other => println("Unknown parameters: " + other.toString); ??? // TODO: bad }.toList - case other => println("Unknown parameters: " + inspect.deep(other)); ??? // TODO: bad + case other => println("Unknown parameters: " + other.toString); ??? // TODO: bad } }(rs => s"extractParameters ==> ${rs.iterator.map(_.name).mkString(", ")}") @@ -76,7 +76,7 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D }() protected def traverseTerm(term: Term)(implicit scope: Scope): Unit = - trace(s"traverseTerm <== ${inspect.shallow(term)}") { + trace(s"traverseTerm <== ${term.showDbg}") { term match { case Assign(lhs, rhs) => traverseTerm(lhs); traverseTerm(rhs) case Bra(_, trm) => traverseTerm(trm) @@ -119,7 +119,7 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D traverseStatements(decls.entities, "Rft", scope) () } - }(_ => s"traverseTerm ==> ${inspect.shallow(term)}") + }(_ => s"traverseTerm ==> ${term.showDbg}") private def traverseTypeDefinition(symbol: TypeSymbol, defn: NuTypeDef)(implicit scope: Scope): Unit = trace(s"traverseTypeDefinition <== ${defn.describe}") { @@ -176,14 +176,14 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D acc } println(thingsToTraverse.iterator.map { - case (L(term), _) => inspect.shallow(term) + case (L(term), _) => term.showDbg case (R(symbol), _) => symbol.name }.mkString("to be traversed: {", ", ", "}")) // Pass 3: Traverse terms collected from the last pass. println("Pass 3") thingsToTraverse.foreach { case (L(term), scope) => - println("traverseTerm: " + inspect.shallow(term)) + println("traverseTerm: " + term.showDbg) println("scope: " + scope.showLocalSymbols) traverseTerm(term)(scope) case (R(symbol), scope) => symbol.body match { @@ -215,7 +215,7 @@ object PreTyper { case (nme: Var) :: tail => rec(nme :: results, tail) case (TyApp(ty, _)) :: tail => rec(results, ty :: tail) case (App(nme @ Var(_), Tup(_))) :: tail => rec(nme :: results, tail) - case other :: _ => println(s"Unknown parent type: ${inspect.deep(other)}"); ??? + case other :: _ => println(s"Unknown parent type: $other"); ??? } rec(Nil, parents) } diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index 91a39491..8c36f23a 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -29,7 +29,7 @@ trait DesugarUCS extends Transformation * @param className the class name variable */ def getClassLikeSymbol: TypeSymbol = - trace(s"getClassLikeSymbol <== ${inspect.shallow(nme)}") { + trace(s"getClassLikeSymbol <== ${nme.showDbg}") { nme.symbolOption match { case S(symbol: ClassSymbol) => symbol case S(symbol: TraitSymbol) => symbol @@ -152,6 +152,7 @@ trait DesugarUCS extends Transformation } // Epilogue `if`.desugaredTerm = S(postProcessed) + println(s"Desugared term: ${postProcessed.showDbg}") }(_ => "traverseIf ==> ()") } diff --git a/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala b/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala index d517df52..313eb615 100644 --- a/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala +++ b/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala @@ -8,7 +8,7 @@ sealed abstract class Pattern { override def toString(): String = this match { case Pattern.ClassLike(symbol) => s"${symbol.defn.kind.str} `${symbol.name}`" case Pattern.Tuple() => "tuple" - case Pattern.Literal(literal) => s"literal ${inspect.deep(literal)}" + case Pattern.Literal(literal) => s"literal $literal" } } diff --git a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala index b583c1e0..1ac91959 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala @@ -22,7 +22,7 @@ trait CoverageChecking { self: mlscript.pretyper.Traceable => working: MatchRegistry, seen: SeenRegistry )(implicit context: Context): Ls[Diagnostic] = - trace(s"checkCoverage <== ${inspect.shallow(term)}, ${pending.size} pending, ${working.size} working, ${seen.size} seen") { + trace(s"checkCoverage <== ${term.showDbg}, ${pending.size} pending, ${working.size} working, ${seen.size} seen") { println(s"seen: " + (if (seen.isEmpty) "empty" else seen.iterator.map { case ((k, _), (s, _, _)) => s"${k.name} is ${s.name}" }.mkString(", ") )) diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index 4958ec79..53401c0e 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -11,7 +11,7 @@ import scala.annotation.tailrec trait PostProcessing { self: DesugarUCS with mlscript.pretyper.Traceable => import PostProcessing._ - def postProcess(term: Term)(implicit context: Context): Term = trace(s"postProcess <== ${inspect.shallow(term)}") { + def postProcess(term: Term)(implicit context: Context): Term = trace(s"postProcess <== ${term.showDbg}") { // Normalized terms are constructed using `Let` and `CaseOf`. term match { case top @ CaseOf(scrutineeVar: Var, fst @ Case(className: Var, body, NoCases)) => @@ -100,16 +100,16 @@ trait PostProcessing { self: DesugarUCS with mlscript.pretyper.Traceable => } private def mergeTerms(t1: Term, t2: Term): Term = - trace(s"mergeTerms <== ${inspect.shallow(t1)} ${inspect.shallow(t2)}") { + trace(s"mergeTerms <== ${t1.showDbg} ${t2.showDbg}") { t1 match { case t1 @ Let(_, _, _, body) => t1.copy(body = mergeTerms(body, t2)) case t1 @ CaseOf(scrutinee: Var, cases) => t1.copy(cases = mergeTermIntoCaseBranches(t2, cases)) case _ => - println(s"CANNOT merge. Discard ${inspect.shallow(t2)}.") + println(s"CANNOT merge. Discard ${t2.showDbg}.") t1 } - }(merged => s"mergedTerms ==> ${inspect.shallow(merged)}") + }(merged => s"mergedTerms ==> ${merged.showDbg}") private def mergeTermIntoCaseBranches(term: Term, cases: CaseBranches): CaseBranches = trace(s"mergeTermIntoCaseBranches <== ${term.describe} ${cases}") { @@ -187,10 +187,10 @@ trait PostProcessing { self: DesugarUCS with mlscript.pretyper.Traceable => val (n, y) = disentangle(body, scrutineeVar, scrutinee, classSymbol) (let.copy(body = n), y.map(t => let.copy(body = t))) case other => - println(s"cannot disentangle ${inspect.shallow(other)}. STOP") + println(s"cannot disentangle ${other.showDbg}. STOP") other -> N } - }({ case (n, y) => s"disentangle ==> `${inspect.deep(n)}` and `${y.fold("")(inspect.deep(_))}`" }) + }({ case (n, y) => s"disentangle ==> `$n` and `${y.fold("")(_.toString)}`" }) } object PostProcessing { diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index a02dcb36..c9f989d9 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -52,7 +52,7 @@ trait Transformation { self: Traceable with Diagnosable => case (test, following) => TermBranch.Boolean(test, following).toSplit } - private def transformIfBody(body: IfBody): TermSplit = trace(s"transformIfBody <== ${inspect.shallow(body)}") { + private def transformIfBody(body: IfBody): TermSplit = trace(s"transformIfBody <== ${body.showDbg}") { body match { case IfThen(expr, rhs) => transformConjunction(splitAnd(expr), Split.then(rhs), true) case IfLet(isRec, name, rhs, body) => die @@ -114,7 +114,7 @@ trait Transformation { self: Traceable with Diagnosable => * Transform an `IfBody` into a `PatternSplit`. */ private def transformPatternMatching(body: IfBody): PatternSplit = - trace(s"transformPatternMatching <== ${inspect.shallow(body)}") { + trace(s"transformPatternMatching <== ${body.showDbg}") { body match { case IfThen(expr, rhs) => separatePattern(expr) match { @@ -124,7 +124,7 @@ trait Transformation { self: Traceable with Diagnosable => PatternBranch(pattern, Split.default(rhs)).toSplit } case IfOpApp(lhs, Var("and"), rhs) => - println(s"lhs: ${inspect.deep(lhs)}") + println(s"lhs: $lhs") separatePattern(lhs) match { case (pattern, S(extraTest)) => PatternBranch(pattern, TermBranch.Boolean(extraTest, transformIfBody(rhs)).toSplit).toSplit @@ -214,14 +214,14 @@ trait Transformation { self: Traceable with Diagnosable => private def separatePattern(term: Term): (Pattern, Opt[Term]) = { val (rawPattern, extraTest) = helpers.separatePattern(term, true) - println("rawPattern: " + inspect.deep(rawPattern)) - println("extraTest: " + inspect.deep(extraTest)) + println("rawPattern: " + rawPattern.toString) + println("extraTest: " + extraTest.toString) (transformPattern(rawPattern), extraTest) } // TODO: Maybe we can change the return type to `::[Term]` so that it will not // empty. - private def splitAnd(t: Term): List[Term] = trace(s"splitAnd <== ${inspect.deep(t)}") { + private def splitAnd(t: Term): List[Term] = trace(s"splitAnd <== $t") { t match { case App( App(Var("and"), diff --git a/shared/src/main/scala/mlscript/utils/inspect.scala b/shared/src/main/scala/mlscript/utils/inspect.scala deleted file mode 100644 index 5b095229..00000000 --- a/shared/src/main/scala/mlscript/utils/inspect.scala +++ /dev/null @@ -1,193 +0,0 @@ -package mlscript.utils - -import mlscript._, shorthands._ - -object inspect { - object shallow { - def apply(term: Statement): Str = term match { - case Var(name) => s"Var(\"$name\")" - case literal: Lit => s"Lit(${literal.toString})" - case fd: NuFunDef => fd.isLetRec.fold("fun")(if (_) "let rec" else "let") + " " + fd.nme.name - case td: NuTypeDef => s"${td.kind.str} ${td.nme.name}" - case _ => - val name = term.getClass.getSimpleName - val arity = term.children.length // Imprecise - if (arity === 0) { name } else s"${name}(${(", _" * arity).drop(2)})" - } - - def apply(body: IfBody): Str = body match { - case IfOpApp(lhs, op, rhs) => s"IfOpApp(${apply(lhs)}, ${apply(op)}, _)" - case IfLet(isRec, name, rhs, body) => s"IfLet($isRec, $name, _, _)" - case IfThen(expr, rhs) => s"IfThen(${apply(expr)}, _)" - case IfOpsApp(lhs, opsRhss) => s"IfOpsApp(${apply(lhs)}, ${opsRhss.map { case (op, body) => s"$op -> _" }.mkString("; ")})" - case IfBlock(lines) => s"IfBlock(_)" - case IfElse(expr) => s"IfElse(${apply(expr)})" - } - - def apply(d: TypingUnit): Str = d.entities.iterator - .map(apply) - .mkString("{", ", ", "}") - } - - object deep { - def apply(t: Opt[Located]): Str = t match { - case N => "N" - case S(l) => s"S(${apply(l)})" - } - - def apply(t: Ls[Located]): Str = t match { - case Nil => "Nil" - case head :: Nil => s"${apply(head)} :: Nil" - case first :: second :: Nil => s"${apply(first)} :: ${apply(second)} :: Nil" - case _ => t.iterator.map(apply).mkString("Ls(", ", ", ")") - } - - def apply[A <: Located, B <: Located](t: Either[A, B]): Str = t match { - case L(value) => s"L(${apply(value)})" - case R(value) => s"R(${apply(value)})" - } - - def apply(t: Located): Str = t match { - case st: Statement => statement(st) - case fl: Field => field(fl) - case ty: TypeLike => typeLike(ty) - case ib: IfBody => ifBody(ib) - case tu: TypingUnit => typingUnit(tu) - case _ => "??" - } - - private def statement(statement: Statement): Str = statement match { - case Def(rec, nme, rhs, isByname) => s"Def($rec, ${apply(nme)}, ${apply(rhs)}, $isByname)" - case TypeDef(kind, nme, tparams, body, mthDecls, mthDefs, positionals, adtInfo) => s"TypeDef(...)" - case Var(name) => s"Var(\"$name\")" - case Lam(lhs, rhs) => s"Lam(${apply(lhs)}, ${apply(rhs)})" - case App(lhs, rhs) => s"App(${apply(lhs)}, ${apply(rhs)})" - case Tup(Nil) => "Tup(Nil)" - case Tup(fields) => - fields.iterator.map { case (maybeName, Fld(flags, value)) => - val first = maybeName.fold("N") { name => s"S($name)" } - val second = s"Fld(_, ${apply(value)})" - s"($first, $second)" - }.mkString("Tup(", " :: ", " :: Nil)") - case Rcd(fields) => - fields.iterator.map { case k -> Fld(_, v) => s"${apply(k)} = ${apply(v)}" }.mkString("Rcd(", ", ", ")") - case Sel(receiver, fieldName) => s"Sel(${apply(receiver)}, $fieldName)" - case Let(isRec, name, rhs, body) => s"Let($isRec, $name, ${apply(rhs)}, ${apply(body)})" - case Blk(stmts) => s"Blk(${stmts.iterator.map(apply).mkString(", ")})" - case Bra(rcd, trm) => s"Bra(rcd = $rcd, ${apply(trm)})" - case Asc(trm, ty) => s"Asc(${apply(trm)}, $ty)" - case Bind(lhs, rhs) => s"Bind(${apply(lhs)}, ${apply(rhs)})" - case Test(trm, ty) => s"Test(${apply(trm)}, ${apply(ty)})" - case With(trm, fields) => - s"With(${apply(trm)}, ${apply(fields)})" - case CaseOf(trm, cases) => - def inspectCaseBranches(br: CaseBranches): Str = br match { - case Case(clsNme, body, rest) => - s"Case($clsNme, ${apply(body)}, ${inspectCaseBranches(rest)})" - case Wildcard(body) => s"Wildcard(${apply(body)})" - case NoCases => "NoCases" - } - s"CaseOf(${apply(trm)}, ${inspectCaseBranches(cases)})" - case IntLit(value) => s"IntLit($value)" - case DecLit(value) => s"DecLit($value)" - case StrLit(value) => s"StrLit($value)" - case UnitLit(value) => s"UnitLit($value)" - case Subs(arr, idx) => s"Subs(${apply(arr)}, ${apply(idx)})" - case Assign(f, v) => s"Assign(${apply(f)}, ${apply(v)})" - case Splc(fs) => - fs.iterator.map{ case L(l) => s"...${apply(l)}" case R(Fld(_, r)) => apply(r)}.mkString("Splc(", ", ", ")") - case If(bod, els) => s"If(${apply(bod)}, ${els.map(apply)})" - case New(base, body) => s"New(${base}, ${apply(body)})" - case NuNew(base) => s"NuNew(${apply(base)})" - case TyApp(base, targs) => s"TyApp(${apply(base)}, ${targs})" - case Where(bod, sts) => s"Where(${apply(bod)}, ...)" - case Forall(ps, bod) => s"Forall($ps, ${apply(bod)})" - case Inst(bod) => s"Inst(${apply(bod)})" - case Eqn(lhs, rhs) => s"Eqn(${apply(lhs)}, ${apply(rhs)})" - case Super() => "Super()" - case AdtMatchWith(cond, arms) => - s"match ${apply(cond)} with ${arms.map(patmat => s"${apply(patmat.pat)} -> ${apply(patmat.rhs)}").mkString(" | ")}" - case Rft(bse, tu) => s"Rft(${apply(bse)}, ${apply(tu)})" - case LetS(isRec, pat, rhs) => s"LetS($isRec, ${apply(pat)}, ${apply(rhs)})" - case DataDefn(body) => s"DataDefn(${apply(body)})" - case DatatypeDefn(head, body) => s"DatatypeDefn(${apply(head)}, ${apply(body)})" - case NuTypeDef(kind, nme, tparams, params, ctor, sig, parents, superAnnot, thisAnnot, body) => - s"NuTypeDef($kind, ${apply(nme)}, Ls(" + - tparams.iterator.map { - case (S(vi), tn) => s"S($vi) -> ${apply(tn)}" - case (N, tn) => s"N -> ${apply(tn)}" - }.mkString(", ") + "), " + - apply(params) + ", " + - apply(ctor) + ", " + - apply(sig) + ", " + - parents.iterator.map(apply).mkString("Ls(", ", ", ")") + ", " + - apply(superAnnot) + ", " + - apply(thisAnnot) + ", " + - apply(body) + ")" - case NuFunDef(isLetRec, nme, symbolicNme, tparams, rhs) => - s"NuFunDef($isLetRec, ${nme.name}, ${apply(symbolicNme)}, ${apply(tparams)}, ${apply(rhs)})" - case Constructor(params, body) => s"Constructor" - } - - private def field(field: Field): Str = field match { - case Field(nme, value) => s"Fld(${apply(nme)}, ${apply(value)})" - } - - private def typeLike(ty: TypeLike): Str = ty match { - case Union(lhs, rhs) => s"Union(${apply(lhs)}, ${apply(rhs)})" - case Inter(lhs, rhs) => s"Inter(${apply(lhs)}, ${apply(rhs)})" - case Function(lhs, rhs) => s"Function(${apply(lhs)}, ${apply(rhs)})" - case Record(fields) => s"Record(${fields.iterator.map { case (nme, ty) => s"$nme: ${apply(ty)}" }.mkString(", ")})" - case Tuple(fields) => s"Tuple(${fields.iterator.map { - case N -> field => s"N -> ${apply(field)}" - case S(nme) -> field => s"S($nme) -> ${apply(field)}" - }.mkString(", ")})" - case Recursive(uv, body) => s"Recursive(${apply(uv)}, ${apply(body)})" - case AppliedType(base, targs) => s"AppliedType(${apply(base)}, ${apply(targs)})" - case Selection(base, name) => s"Selection(${apply(base)}, $name)" - case Neg(base) => s"Neg(${apply(base)})" - case Rem(base, names) => s"Rem(${apply(base)}, ${names.iterator.map(apply).mkString(", ")})" - case Bounds(lb, ub) => s"Bounds(${apply(lb)}, ${apply(ub)})" - case WithExtension(base, rcd) => s"WithExtension(${apply(base)}, ${apply(rcd)})" - case Splice(fields) => s"Splice(${fields.iterator.map { - case L(l) => s"L(${apply(l)})" - case R(f) => s"R(${apply(f)})" - }.mkString(", ")})" - case Constrained(base, tvBounds, where) => s"Constrained(${apply(base)}, Ls(${tvBounds.iterator.map { - case (tv, bounds) => s"${apply(tv)} -> ${apply(bounds)}" - }}), ${apply(where)})" - case Top => "Top" - case Bot => "Bot" - case Literal(lit) => s"Literal(${lit.toString})" - case TypeName(name) => s"TypeName(\"$name\")" - case TypeTag(name) => s"TypeTag($name)" - case TypeVar(identifier, nameHint) => s"TypeVar(${identifier match { - case L(i) => s"L($i)" - case R(n) => s"R($n)" - }}, ${nameHint.fold("N")(n => s"S($n)")})" - case PolyType(targs, body) => s"PolyType(Ls(${targs.iterator.map(_.fold(apply, apply)).mkString(", ")}), ${apply(body)})" - case Signature(members, result) => s"Signature(${members.iterator.map(apply(_: Statement)).mkString("Ls(", ", ", ")")}, ${apply(result)})" - case t: NuDecl => apply(t: Statement) - } - - private def ifBody(body: IfBody): Str = body match { - case IfElse(expr) => s"IfElse(${apply(expr)}" - case IfThen(expr, rhs) => s"IfThen(${apply(expr)}, ${apply(rhs)}" - case IfBlock(lines) => s"IfBlock(${ - lines.iterator.map(_.fold(apply, apply)).mkString("Ls(", ", ", ")") - })" - case IfOpsApp(lhs, opsRhss) => s"IfOpsApp(${apply(lhs)}, ${ - opsRhss.iterator.map { case (op, body) => - s"$op -> ${apply(body)}" - } - }".mkString("; ") - case IfLet(isRec, name, rhs, body) => ??? - case IfOpApp(lhs, op, rhs) => - s"IfOpApp(${apply(lhs)}, ${apply(op)}, ${apply(rhs)}" - } - - private def typingUnit(t: TypingUnit): Str = t.entities.iterator - .map(apply) - .mkString("TypingUnit(", ", ", ")") - } -} \ No newline at end of file diff --git a/shared/src/test/diff/pretyper/ucs/examples/ULC.mls b/shared/src/test/diff/pretyper/ucs/examples/ULC.mls index 9add8993..357d5811 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/ULC.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/ULC.mls @@ -329,11 +329,11 @@ fun combinations(n: Int, acc: List['T], alphabet: List['T], xs: List[Str]): Opti else Some(x) else search((x) => combinations(n - 1, x :: acc, alphabet, xs), alphabet) -//│ fun combinations: forall 'T 'T0 'A. (n: Int, acc: List['T0], alphabet: List['T], xs: List[Str]) -> Option[Str] +//│ fun combinations: forall 'T 'A 'T0. (n: Int, acc: List['T], alphabet: List['T0], xs: List[Str]) -> Option[Str] //│ where -//│ 'T <: 'T0 & 'A -//│ 'T0 :> 'A -//│ 'A := 'T0 +//│ 'T0 <: 'T & 'A +//│ 'T :> 'A +//│ 'A := 'T combinations(1, Nil, 1 :: 2 :: 3 :: Nil, Nil) |> showOption combinations(2, Nil, 1 :: 2 :: 3 :: Nil, Nil) |> showOption From ec5cca7e05f789c3570256a838c951df32db5131 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 3 Jan 2024 15:56:48 +0800 Subject: [PATCH 052/143] Address the test case that keeps changing --- .../main/scala/mlscript/ucs/DesugarUCS.scala | 6 ++- .../test/diff/pretyper/ucs/examples/ULC.mls | 52 ++++++++++++++----- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index 8c36f23a..871420ec 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -108,7 +108,7 @@ trait DesugarUCS extends Transformation implicit val context: Context = new Context(`if`) trace("traverseIf") { // Stage 0: Transformation - val transformed = traceWithTopic("transform") { + val transformed = traceWithTopic("ucs.transform") { println("STEP 0") val transformed = transform(`if`) println("Transformed UCS term:") @@ -150,9 +150,11 @@ trait DesugarUCS extends Transformation println(s"Coverage checking result: ${diagnostics.size} errors") raiseMany(diagnostics) } + traceWithTopic("desugared") { + println(s"Desugared term: ${postProcessed.showDbg}") + } // Epilogue `if`.desugaredTerm = S(postProcessed) - println(s"Desugared term: ${postProcessed.showDbg}") }(_ => "traverseIf ==> ()") } diff --git a/shared/src/test/diff/pretyper/ucs/examples/ULC.mls b/shared/src/test/diff/pretyper/ucs/examples/ULC.mls index 357d5811..4cccc0ec 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/ULC.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/ULC.mls @@ -320,20 +320,46 @@ fun search(f: 'A -> Option['B], xs: List['A]): Option['B] = //│ where //│ 'B <: 'A0 -// The removal of type annotations will cause running out of fuel. +// ============================================================================= +// TO BE INVESTIGATED: The following version does not have a stable type. +// The desugared term look like this: +// let ucs$test$0 = <=(n, 0,) : Bool in +// case ucs$test$0 of { +// true => +// let x = |>(reverse(acc,), join("",),) in +// let ucs$test$1 = contains(xs, x,) : Bool in +// case ucs$test$1 of { +// true => None; +// _ => Some(x,) +// }; +// _ => { +// search(('(' x ')',) => combinations(-(n, 1,), ::(x, acc,), alphabet, xs,), alphabet,) +// } +// } +// fun combinations(n: Int, acc: List['T], alphabet: List['T], xs: List[Str]): Option[Str] = +// if +// n <= 0 and +// let x = reverse(acc) |> join("") +// contains(xs, x) then None +// else Some(x) +// else +// search((x) => combinations(n - 1, x :: acc, alphabet, xs), alphabet) +// //│ fun combinations: forall 'T 'A 'T0. (n: Int, acc: List['T], alphabet: List['T0], xs: List[Str]) -> Option[Str] +// //│ where +// //│ 'T0 <: 'T & 'A +// //│ 'T :> 'A +// //│ 'A := 'T +// ============================================================================= + +fun combinations: forall 'a: (Int, List['a], List['a], List[Str]) -> Option[Str] fun combinations(n: Int, acc: List['T], alphabet: List['T], xs: List[Str]): Option[Str] = - if - n <= 0 and - let x = reverse(acc) |> join("") - contains(xs, x) then None - else Some(x) - else - search((x) => combinations(n - 1, x :: acc, alphabet, xs), alphabet) -//│ fun combinations: forall 'T 'A 'T0. (n: Int, acc: List['T], alphabet: List['T0], xs: List[Str]) -> Option[Str] -//│ where -//│ 'T0 <: 'T & 'A -//│ 'T :> 'A -//│ 'A := 'T + if n <= 0 then + let x = reverse(acc) |> join("") + if contains(xs, x) then None else Some(x) + else + search((x) => combinations(n - 1, x :: acc, alphabet, xs), alphabet) +//│ fun combinations: forall 'T. (n: Int, acc: List['T], alphabet: List['T], xs: List[Str]) -> Option[Str] +//│ fun combinations: forall 'a. (Int, List['a], List['a], List[Str]) -> Option[Str] combinations(1, Nil, 1 :: 2 :: 3 :: Nil, Nil) |> showOption combinations(2, Nil, 1 :: 2 :: 3 :: Nil, Nil) |> showOption From d742ec15801e1dc89b85e0feaf62ff615073c3f8 Mon Sep 17 00:00:00 2001 From: Lionel Parreaux Date: Thu, 11 Jan 2024 11:08:18 +0800 Subject: [PATCH 053/143] Fix handling of boolean patterns --- shared/src/main/scala/mlscript/Typer.scala | 15 ++++++++------- shared/src/test/diff/nu/FilterMap.mls | 2 +- shared/src/test/diff/nu/LitMatch.mls | 4 ++-- .../test/diff/pretyper/ucs/patterns/Literals.mls | 2 +- shared/src/test/diff/ucs/ElseIf.mls | 2 +- shared/src/test/diff/ucs/SplitAroundOp.mls | 2 +- shared/src/test/diff/ucs/WeirdIf.mls | 2 +- 7 files changed, 15 insertions(+), 14 deletions(-) diff --git a/shared/src/main/scala/mlscript/Typer.scala b/shared/src/main/scala/mlscript/Typer.scala index b686b87c..9e7fc455 100644 --- a/shared/src/main/scala/mlscript/Typer.scala +++ b/shared/src/main/scala/mlscript/Typer.scala @@ -1458,7 +1458,13 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne case v @ Var(nme) => val tpr = tp(pat.toLoc, "type pattern") ctx.tyDefs.get(nme) match { - case None => + case Some(td) if !newDefs => + td.kind match { + case Als | Mod | Mxn => val t = err(msg"can only match on classes and traits", pat.toLoc)(raise); t -> t + case Cls => val t = clsNameToNomTag(td)(tp(pat.toLoc, "class pattern"), ctx); t -> t + case Trt => val t = trtNameToNomTag(td)(tp(pat.toLoc, "trait pattern"), ctx); t -> t + } + case _ => val bail = () => { val e = ClassTag(ErrTypeId, Set.empty)(tpr) return ((e -> e) :: Nil) -> e @@ -1473,6 +1479,7 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne lti match { case dti: DelayedTypeInfo => val tag = clsNameToNomTag(dti.decl match { case decl: NuTypeDef => decl; case _ => die })(prov, ctx) + println(s"CASE $tag") val ty = RecordType.mk(dti.tparams.map { case (tn, tv, vi) => @@ -1500,12 +1507,6 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne err("type identifier not found: " + nme, pat.toLoc)(raise) bail() } - case Some(td) => - td.kind match { - case Als | Mod | Mxn => val t = err(msg"can only match on classes and traits", pat.toLoc)(raise); t -> t - case Cls => val t = clsNameToNomTag(td)(tp(pat.toLoc, "class pattern"), ctx); t -> t - case Trt => val t = trtNameToNomTag(td)(tp(pat.toLoc, "trait pattern"), ctx); t -> t - } } } val newCtx = ctx.nest diff --git a/shared/src/test/diff/nu/FilterMap.mls b/shared/src/test/diff/nu/FilterMap.mls index 2ac040cb..08b3dc9b 100644 --- a/shared/src/test/diff/nu/FilterMap.mls +++ b/shared/src/test/diff/nu/FilterMap.mls @@ -30,7 +30,7 @@ fun filtermap(f, xs) = if xs is [true, z] then Cons(y, filtermap(f, ys)) //│ ╔══[ERROR] type identifier not found: Tuple#2 //│ ╙── -//│ fun filtermap: ((Cons[nothing] | Nil) -> nothing, Cons[anything] | Nil) -> (Cons[nothing] | Nil | error) +//│ fun filtermap: ((Cons[nothing] | Nil) -> Bool, Cons[anything] | Nil) -> (Cons[nothing] | Nil | error) //│ Code generation encountered an error: //│ unknown match case: Tuple#2 diff --git a/shared/src/test/diff/nu/LitMatch.mls b/shared/src/test/diff/nu/LitMatch.mls index 211cec96..a2e74b8f 100644 --- a/shared/src/test/diff/nu/LitMatch.mls +++ b/shared/src/test/diff/nu/LitMatch.mls @@ -28,11 +28,11 @@ if false is false then 0 fun foo(x) = if x is false then 0 -//│ fun foo: nothing -> 0 +//│ fun foo: false -> 0 fun foo(x) = if x is false then 0 true then 1 -//│ fun foo: nothing -> (0 | 1) +//│ fun foo: Bool -> (0 | 1) diff --git a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls index 2dd0048f..2feac471 100644 --- a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls +++ b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls @@ -61,7 +61,7 @@ fun mix(x) = if x is true then "true" Some(value) then "Some" 0 then "zero" -//│ fun mix: (0 | Some[anything]) -> ("Some" | "true" | "zero") +//│ fun mix: (0 | Some[anything] | true) -> ("Some" | "true" | "zero") [mix(true), mix(Some(1)), mix(0)] //│ ["Some" | "true" | "zero", "Some" | "true" | "zero", "Some" | "true" | "zero"] diff --git a/shared/src/test/diff/ucs/ElseIf.mls b/shared/src/test/diff/ucs/ElseIf.mls index 5b8446a1..bcb30cd3 100644 --- a/shared/src/test/diff/ucs/ElseIf.mls +++ b/shared/src/test/diff/ucs/ElseIf.mls @@ -82,7 +82,7 @@ fun g(x, y) = if x is _ and y is true then true false then false -//│ fun g: (Object, nothing) -> Bool +//│ fun g: (Object, Bool) -> Bool // Chained UCS terms fun f(x, y) = if x is diff --git a/shared/src/test/diff/ucs/SplitAroundOp.mls b/shared/src/test/diff/ucs/SplitAroundOp.mls index 198590fa..1cbf9e1d 100644 --- a/shared/src/test/diff/ucs/SplitAroundOp.mls +++ b/shared/src/test/diff/ucs/SplitAroundOp.mls @@ -12,7 +12,7 @@ fun f(x, b) = "1" then "s1" "2" then "s2" else ":p" -//│ fun f: (Eql["0" | "1" | "2" | 0 | 1 | 2], Object) -> (":p" | "n0" | "n1" | "n2" | "s0" | "s1" | "s2") +//│ fun f: (Eql["0" | "1" | "2" | 0 | 1 | 2], Object & ~true | true) -> (":p" | "n0" | "n1" | "n2" | "s0" | "s1" | "s2") fun f(x, y, a, b) = if x === 0 diff --git a/shared/src/test/diff/ucs/WeirdIf.mls b/shared/src/test/diff/ucs/WeirdIf.mls index 5f4d4443..db0705fd 100644 --- a/shared/src/test/diff/ucs/WeirdIf.mls +++ b/shared/src/test/diff/ucs/WeirdIf.mls @@ -102,7 +102,7 @@ fun boolToStr(x) = if x is true then "yah" false then "nah" -//│ fun boolToStr: nothing -> ("nah" | "yah") +//│ fun boolToStr: Bool -> ("nah" | "yah") boolToStr of true boolToStr of false From dfefcbd3379c3c86a470bb7c4bbc7710fe995084 Mon Sep 17 00:00:00 2001 From: Lionel Parreaux Date: Thu, 11 Jan 2024 12:25:14 +0800 Subject: [PATCH 054/143] Add pattern refining --- shared/src/main/scala/mlscript/MLParser.scala | 2 +- shared/src/main/scala/mlscript/Typer.scala | 11 +++-- shared/src/main/scala/mlscript/helpers.scala | 4 +- shared/src/main/scala/mlscript/syntax.scala | 2 +- .../main/scala/mlscript/ucs/DesugarUCS.scala | 2 +- .../main/scala/mlscript/ucs/Desugarer.scala | 6 +-- shared/src/main/scala/mlscript/ucs/core.scala | 4 +- .../mlscript/ucs/stages/Desugaring.scala | 30 ++++++------ .../mlscript/ucs/stages/Normalization.scala | 22 +++++---- .../mlscript/ucs/stages/PostProcessing.scala | 25 +++++----- .../mlscript/ucs/stages/Transformation.scala | 9 +++- .../src/main/scala/mlscript/ucs/syntax.scala | 9 ++-- shared/src/test/diff/nu/RefinedPattern.mls | 46 +++++++++++++++++++ 13 files changed, 116 insertions(+), 56 deletions(-) create mode 100644 shared/src/test/diff/nu/RefinedPattern.mls diff --git a/shared/src/main/scala/mlscript/MLParser.scala b/shared/src/main/scala/mlscript/MLParser.scala index 1ccd0686..d05e7a6c 100644 --- a/shared/src/main/scala/mlscript/MLParser.scala +++ b/shared/src/main/scala/mlscript/MLParser.scala @@ -140,7 +140,7 @@ class MLParser(origin: Origin, indent: Int = 0, recordLocations: Bool = true) { def matchArms[p: P](sep: Str): P[CaseBranches] = P( ( ("_" ~ "->" ~ term).map(Wildcard) | ((lit | variable) ~ "->" ~ term ~ matchArms2(sep)) - .map { case (t, b, rest) => Case(t, b, rest) } + .map { case (t, b, rest) => Case(t, b, rest)(refined = false) } ).?.map { case None => NoCases case Some(b) => b diff --git a/shared/src/main/scala/mlscript/Typer.scala b/shared/src/main/scala/mlscript/Typer.scala index 9e7fc455..0c3ab5d2 100644 --- a/shared/src/main/scala/mlscript/Typer.scala +++ b/shared/src/main/scala/mlscript/Typer.scala @@ -723,7 +723,8 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne } // TODO also prevent rebinding of "not" - val reservedVarNames: Set[Str] = Set("|", "&", "~", ",", "neg", "and", "or", "is") + val reservedVarNames: Set[Str] = + Set("|", "&", "~", ",", "neg", "and", "or", "is", "refined") object ValidVar { def unapply(v: Var)(implicit raise: Raise): S[Str] = S { @@ -1449,7 +1450,7 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne case _ => (fv -> TopType :: Nil) -> typeTerm(b) } - case Case(pat, bod, rest) => + case cse @ Case(pat, bod, rest) => val (tagTy, patTy) : (ST, ST) = pat match { case lit: Lit => val t = ClassTag(lit, @@ -1479,8 +1480,7 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne lti match { case dti: DelayedTypeInfo => val tag = clsNameToNomTag(dti.decl match { case decl: NuTypeDef => decl; case _ => die })(prov, ctx) - println(s"CASE $tag") - val ty = + val ty = // TODO update as below for refined RecordType.mk(dti.tparams.map { case (tn, tv, vi) => val nv = freshVar(tv.prov, S(tv), tv.nameHint) @@ -1491,7 +1491,8 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne tag -> ty case CompletedTypeInfo(cls: TypedNuCls) => val tag = clsNameToNomTag(cls.td)(prov, ctx) - val ty = + println(s"CASE $tag ${cse.refined}") + val ty = if (cse.refined) freshVar(tp(v.toLoc, "refined scrutinee"), N) else RecordType.mk(cls.tparams.map { case (tn, tv, vi) => val nv = freshVar(tv.prov, S(tv), tv.nameHint) diff --git a/shared/src/main/scala/mlscript/helpers.scala b/shared/src/main/scala/mlscript/helpers.scala index ee53a370..da85c084 100644 --- a/shared/src/main/scala/mlscript/helpers.scala +++ b/shared/src/main/scala/mlscript/helpers.scala @@ -1085,9 +1085,9 @@ trait CaseBranchesImpl extends Located { self: CaseBranches => } def print(isFirst: Bool): Str = this match { - case Case(pat, body, rest) => + case c @ Case(pat, body, rest) => (if (isFirst) { "" } else { "; " }) + - pat.print(false) + " => " + body.print(false) + rest.print(false) + (if (c.refined) "refined " else "") + pat.print(false) + " => " + body.print(false) + rest.print(false) case Wildcard(body) => (if (isFirst) { "" } else { "; " }) + "_ => " + body.print(false) diff --git a/shared/src/main/scala/mlscript/syntax.scala b/shared/src/main/scala/mlscript/syntax.scala index 9a802bb5..ed2a8942 100644 --- a/shared/src/main/scala/mlscript/syntax.scala +++ b/shared/src/main/scala/mlscript/syntax.scala @@ -110,7 +110,7 @@ final case class Fld(flags: FldFlags, value: Term) extends FldImpl object FldFlags { val empty: FldFlags = FldFlags(false, false, false) } sealed abstract class CaseBranches extends CaseBranchesImpl -final case class Case(pat: SimpleTerm, body: Term, rest: CaseBranches) extends CaseBranches +final case class Case(pat: SimpleTerm, body: Term, rest: CaseBranches)(val refined: Bool) extends CaseBranches final case class Wildcard(body: Term) extends CaseBranches // final case class TupleCase(numElems: Int, canHaveMore: Bool, body: Term, rest: CaseBranches) extends CaseBranches final case object NoCases extends CaseBranches diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index 871420ec..80822750 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -184,7 +184,7 @@ trait DesugarUCS extends Transformation case core.Pattern.Literal(literal) => Nil case core.Pattern.Name(nme) => nme -> nme.symbol :: Nil // For now, there should only be parameter-less class patterns. - case core.Pattern.Class(nme) => Nil + case core.Pattern.Class(nme, _) => Nil case core.Pattern.Tuple(_) => ??? case core.Pattern.Record(_) => ??? } diff --git a/shared/src/main/scala/mlscript/ucs/Desugarer.scala b/shared/src/main/scala/mlscript/ucs/Desugarer.scala index 5d7eff4c..7f2d3004 100644 --- a/shared/src/main/scala/mlscript/ucs/Desugarer.scala +++ b/shared/src/main/scala/mlscript/ucs/Desugarer.scala @@ -824,10 +824,10 @@ class Desugarer extends TypeDefs { self: Typer => ) case _ => mkLetFromFields(scrutinee, fields.filter(_._2.name =/= "_").toList, consequent) } - Case(className, body, rec2(next)) + Case(className, body, rec2(next))(refined = false) case MutCase.Literal(literal, cases) :: next => printlnUCS(s"• Literal pattern: $literal") - Case(literal, rec(cases), rec2(next)) + Case(literal, rec(cases), rec2(next))(refined = false) case Nil => wildcard match { case None => @@ -906,7 +906,7 @@ class Desugarer extends TypeDefs { self: Typer => val falseBody = mkBindings(whenFalse.getBindings.toList, rec(whenFalse)(defs ++ whenFalse.getBindings.iterator.map(_.name)), defs) val trueBody = mkBindings(whenTrue.getBindings.toList, rec(whenTrue)(defs ++ whenTrue.getBindings.iterator.map(_.name)), defs) val falseBranch = Wildcard(falseBody) - val trueBranch = Case(Var("true"), trueBody, falseBranch) + val trueBranch = Case(Var("true"), trueBody, falseBranch)(refined = false) CaseOf(condition, trueBranch) } }() diff --git a/shared/src/main/scala/mlscript/ucs/core.scala b/shared/src/main/scala/mlscript/ucs/core.scala index 14b1577f..6b9dc433 100644 --- a/shared/src/main/scala/mlscript/ucs/core.scala +++ b/shared/src/main/scala/mlscript/ucs/core.scala @@ -13,7 +13,7 @@ package object core { override def toString(): String = this match { case Pattern.Literal(literal) => literal.toString case Pattern.Name(Var(name)) => name - case Pattern.Class(Var(name)) => name + case Pattern.Class(Var(name), rfd) => (if (rfd) "refined " else "") + name case Pattern.Tuple(fields) => fields.iterator.map(_.fold("_")(_.name)).mkString("(", ", ", ")") case Pattern.Record(Nil) => "{}" case Pattern.Record(entries) => entries.iterator.map { case (nme, als) => s"$nme: $als" }.mkString("{ ", ", ", " }") @@ -26,7 +26,7 @@ package object core { final case class Name(nme: Var) extends Pattern { override def children: Ls[Located] = nme :: Nil } - final case class Class(nme: Var) extends Pattern { + final case class Class(nme: Var, refined: Bool) extends Pattern { override def children: Ls[Located] = nme :: Nil } final case class Tuple(elements: List[Opt[Var]]) extends Pattern { diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index e58d9b56..bd92198c 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -66,8 +66,10 @@ trait Desugaring { self: PreTyper => * A shorthand for making a true pattern, which is useful in desugaring * Boolean conditions. */ - private def truePattern(implicit scope: Scope) = c.Pattern.Class(Var("true").withResolvedTypeSymbol) - private def falsePattern(implicit scope: Scope) = c.Pattern.Class(Var("false").withResolvedTypeSymbol) + private def truePattern(implicit scope: Scope) = + c.Pattern.Class(Var("true").withResolvedTypeSymbol, refined = false) + private def falsePattern(implicit scope: Scope) = + c.Pattern.Class(Var("false").withResolvedTypeSymbol, refined = false) private def desugarTermSplit(split: s.TermSplit)(implicit termPart: PartialTerm, scope: Scope, context: Context): c.Split = split match { @@ -139,7 +141,7 @@ trait Desugaring { self: PreTyper => case (s.NamePattern(name), index) => val subScrutinee = classPattern.getParameter(index).withAlias(name) S(name.withFreshSymbol.withScrutinee(subScrutinee) -> N) - case (parameterPattern @ (s.ClassPattern(_, _) | s.LiteralPattern(_) | s.TuplePattern(_)), index) => + case (parameterPattern @ (s.ClassPattern(_, _, _) | s.LiteralPattern(_) | s.TuplePattern(_)), index) => val subScrutineeVar = freshSubScrutineeVar(parentScrutineeVar, parentClassLikeSymbol.name, index) val symbol = new LocalTermSymbol(subScrutineeVar) symbol.addScrutinee(classPattern.getParameter(index).withAlias(subScrutineeVar)) @@ -168,7 +170,7 @@ trait Desugaring { self: PreTyper => * @param initialScope the scope before flattening the class pattern * @return a tuple of the augmented scope and a function that wrap a split */ - private def desugarClassPattern(pattern: s.ClassPattern, scrutineeVar: Var, initialScope: Scope)(implicit context: Context): (Scope, c.Split => c.Branch) = { + private def desugarClassPattern(pattern: s.ClassPattern, scrutineeVar: Var, initialScope: Scope, refined: Bool)(implicit context: Context): (Scope, c.Split => c.Branch) = { val scrutinee = scrutineeVar.getOrCreateScrutinee.withAlias(scrutineeVar) val patternClassSymbol = pattern.nme.resolveTypeSymbol(initialScope) val classPattern = scrutinee.getOrCreateClassPattern(patternClassSymbol) @@ -204,7 +206,7 @@ trait Desugaring { self: PreTyper => } println(s"${scrutineeVar.name}: ${scrutinee.patterns.mkString(", ")}") // Last, return the scope with all bindings and a function that adds all matches and bindings to a split. - (scopeWithAll, split => c.Branch(scrutineeVar, c.Pattern.Class(pattern.nme), bindAll(split))) + (scopeWithAll, split => c.Branch(scrutineeVar, c.Pattern.Class(pattern.nme, refined), bindAll(split))) } /** @@ -235,7 +237,7 @@ trait Desugaring { self: PreTyper => // binder. case ((scope, bindPrevious), S(nme -> S(pattern: s.ClassPattern))) => println(s"${nme.name} is ${pattern.nme.name}") - val (scopeWithNestedAll, bindNestedAll) = desugarClassPattern(pattern, nme, scope) + val (scopeWithNestedAll, bindNestedAll) = desugarClassPattern(pattern, nme, scope, pattern.refined) (scopeWithNestedAll, split => bindPrevious(bindNestedAll(split) :: c.Split.Nil)) case ((scope, bindPrevious), S(nme -> S(pattern: s.LiteralPattern))) => nme.getOrCreateScrutinee @@ -263,7 +265,7 @@ trait Desugaring { self: PreTyper => case (_: s.EmptyPattern, _) => N case (s.NamePattern(name), index) => S(name.withFreshSymbol.withScrutinee(tuplePattern.getField(index)) -> N) - case (parameterPattern @ (s.ClassPattern(_, _) | s.LiteralPattern(_) | s.TuplePattern(_)), index) => + case (parameterPattern @ (s.ClassPattern(_, _, _) | s.LiteralPattern(_) | s.TuplePattern(_)), index) => val arity = fields.length val subScrutineeVar = freshSubScrutineeVar(parentScrutineeVar, s"Tuple$$$arity", index) val symbol = new LocalTermSymbol(subScrutineeVar) @@ -290,7 +292,7 @@ trait Desugaring { self: PreTyper => private def desugarPatternSplit(scrutineeTerm: Term, split: s.PatternSplit)(implicit scope: Scope, context: Context): c.Split = { def rec(scrutineeVar: Var, split: s.PatternSplit)(implicit scope: Scope): c.Split = split match { - case s.Split.Cons(head, tail) => + case s.Split.Cons(head, tail) => head.pattern match { case pattern @ s.AliasPattern(_, _) => raiseError(PreTyping, msg"alias pattern is not supported for now" -> pattern.toLoc) @@ -302,11 +304,11 @@ trait Desugaring { self: PreTyper => pattern = c.Pattern.Literal(literal), continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope, context) ) :: rec(scrutineeVar, tail) - case s.ConcretePattern(nme @ (Var("true") | Var("false"))) => + case s.ConcretePattern(nme @ (Var("true") | Var("false"))) => scrutineeVar.getOrCreateScrutinee.withAlias(scrutineeVar).getOrCreateBooleanPattern(nme.name === "true").addLocation(nme) c.Branch( scrutinee = scrutineeVar, - pattern = c.Pattern.Class(nme.withResolvedTypeSymbol), + pattern = c.Pattern.Class(nme.withResolvedTypeSymbol, refined = false), continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope, context) ) :: rec(scrutineeVar, tail) case s.ConcretePattern(nme) => @@ -333,14 +335,14 @@ trait Desugaring { self: PreTyper => // Whoosh! Done. val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope + nme.symbol, context) c.Branch(scrutineeVar, c.Pattern.Name(nme), continuation) :: rec(scrutineeVar, tail)(scope + nme.symbol) - case pattern @ s.ClassPattern(nme, fields) => + case pattern @ s.ClassPattern(nme, fields, rfd) => println(s"find term symbol of $scrutineeVar in ${scope.showLocalSymbols}") - scrutineeVar.symbol = scope.getTermSymbol(scrutineeVar.name).getOrElse(???) - val (scopeWithAll, bindAll) = desugarClassPattern(pattern, scrutineeVar, scope) + scrutineeVar.symbol = scope.getTermSymbol(scrutineeVar.name).getOrElse(???/*FIXME*/) + val (scopeWithAll, bindAll) = desugarClassPattern(pattern, scrutineeVar, scope, rfd) val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scopeWithAll, context) bindAll(continuation) :: rec(scrutineeVar, tail) case s.TuplePattern(fields) => - scrutineeVar.symbol = scope.getTermSymbol(scrutineeVar.name).getOrElse(???) + scrutineeVar.symbol = scope.getTermSymbol(scrutineeVar.name).getOrElse(???/*FIXME*/) val (scopeWithAll, bindAll) = desugarTuplePattern(fields, scrutineeVar, scope) val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scopeWithAll, context) val withBindings = bindAll(continuation) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 20870a2e..0d907166 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -62,11 +62,11 @@ trait Normalization { self: Traceable with Diagnosable => println(s"normalizing name pattern ${scrutinee.name} is ${nme.name}") Let(false, nme, scrutinee, normalizeToTerm(concat(continuation, tail))) // Skip Boolean conditions as scrutinees, because they only appear once. - case Split.Cons(Branch(test, pattern @ Pattern.Class(nme @ Var("true")), continuation), tail) => + case Split.Cons(Branch(test, pattern @ Pattern.Class(nme @ Var("true"), _), continuation), tail) => println(s"normalizing true pattern: ${test.name} is true") val trueBranch = normalizeToTerm(concat(continuation, tail)) val falseBranch = normalizeToCaseBranches(tail) - CaseOf(test, Case(nme, trueBranch, falseBranch)) + CaseOf(test, Case(nme, trueBranch, falseBranch)(refined = false)) case Split.Cons(Branch(ScrutineeData.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Literal(literal), continuation), tail) => println(s"normalizing literal pattern: ${scrutineeVar.name} is $literal") val concatenatedTrueBranch = concat(continuation, tail) @@ -74,13 +74,13 @@ trait Normalization { self: Traceable with Diagnosable => val trueBranch = normalizeToTerm(specialize(concatenatedTrueBranch, true)(scrutineeVar, scrutinee, pattern, context)) println(s"false branch: ${showSplit(tail)}") val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context)) - CaseOf(scrutineeVar, Case(literal, trueBranch, falseBranch)) - case Split.Cons(Branch(ScrutineeData.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Class(nme), continuation), tail) => + CaseOf(scrutineeVar, Case(literal, trueBranch, falseBranch)(refined = false)) + case Split.Cons(Branch(ScrutineeData.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Class(nme, rfd), continuation), tail) => println(s"normalizing class pattern: ${scrutineeVar.name} is ${nme.name}") // println(s"match ${scrutineeVar.name} with $nme (has location: ${nme.toLoc.isDefined})") val trueBranch = normalizeToTerm(specialize(concat(continuation, tail), true)(scrutineeVar, scrutinee, pattern, context)) val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context)) - CaseOf(scrutineeVar, Case(nme, trueBranch, falseBranch)) + CaseOf(scrutineeVar, Case(nme, trueBranch, falseBranch)(refined = rfd)) case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => println(s"unknown pattern $pattern") throw new NormalizationException((msg"Unsupported pattern: ${pattern.toString}" -> pattern.toLoc) :: Nil) @@ -130,13 +130,13 @@ trait Normalization { self: Traceable with Diagnosable => // Name patterns are translated to let bindings. case (_, Split.Cons(Branch(otherScrutineeVar, Pattern.Name(alias), continuation), tail)) => Split.Let(false, alias, otherScrutineeVar, specialize(continuation, matchOrNot)) - case (_, split @ Split.Cons(head @ Branch(test, Pattern.Class(Var("true")), continuation), tail)) if context.isTestVar(test) => + case (_, split @ Split.Cons(head @ Branch(test, Pattern.Class(Var("true"), _), continuation), tail)) if context.isTestVar(test) => println(s"found a Boolean test: $test is true") val trueBranch = specialize(continuation, matchOrNot) val falseBranch = specialize(tail, matchOrNot) split.copy(head = head.copy(continuation = trueBranch), tail = falseBranch) // Class pattern. Positive. - case (true, split @ Split.Cons(head @ Branch(ScrutineeData.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Class(otherClassName), continuation), tail)) => + case (true, split @ Split.Cons(head @ Branch(ScrutineeData.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Class(otherClassName, rfd), continuation), tail)) => val otherClassSymbol = getClassLikeSymbol(otherClassName) lazy val specializedTail = { println(s"specialized next") @@ -145,7 +145,8 @@ trait Normalization { self: Traceable with Diagnosable => if (scrutinee === otherScrutinee) { println(s"scrutinee: ${scrutineeVar.name} === ${otherScrutineeVar.name}") pattern match { - case Pattern.Class(className) => + case Pattern.Class(className, rfd2) => + assert(rfd === rfd2) // TODO: raise warning instead of crash val classSymbol = getClassLikeSymbol(className) if (classSymbol === otherClassSymbol) { println(s"Case 1: class name: $className === $otherClassName") @@ -175,12 +176,13 @@ trait Normalization { self: Traceable with Diagnosable => ) } // Class pattern. Negative - case (false, split @ Split.Cons(head @ Branch(ScrutineeData.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Class(otherClassName), continuation), tail)) => + case (false, split @ Split.Cons(head @ Branch(ScrutineeData.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Class(otherClassName, rfd), continuation), tail)) => val otherClassSymbol = getClassLikeSymbol(otherClassName) if (scrutinee === otherScrutinee) { println(s"scrutinee: ${scrutineeVar.name} === ${otherScrutineeVar.name}") pattern match { - case Pattern.Class(className) => + case Pattern.Class(className, rfd2) => + assert(rfd === rfd2) // TODO: raise warning instead of crash val classSymbol = getClassLikeSymbol(className) if (className === otherClassName) { println(s"Case 1: class name: $otherClassName === $className") diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index 53401c0e..d56b34ee 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -17,12 +17,13 @@ trait PostProcessing { self: DesugarUCS with mlscript.pretyper.Traceable => case top @ CaseOf(scrutineeVar: Var, fst @ Case(className: Var, body, NoCases)) => println(s"found a UNARY case: $scrutineeVar is $className") println("post-processing the body") - top.copy(cases = fst.copy(body = postProcess(body))) + top.copy(cases = fst.copy(body = postProcess(body))(refined = fst.refined)) case top @ CaseOf(test: Var, fst @ Case(Var("true"), trueBranch, Wildcard(falseBranch))) => println(s"found a if-then-else case: $test is true") val processedTrueBranch = postProcess(trueBranch) val processedFalseBranch = postProcess(falseBranch) - top.copy(cases = fst.copy(body = processedTrueBranch, rest = Wildcard(processedFalseBranch))) + top.copy(cases = fst.copy(body = processedTrueBranch, rest = Wildcard(processedFalseBranch) + )(refined = fst.refined)) case top @ CaseOf(ScrutineeData.WithVar(scrutinee, scrutineeVar), fst @ Case(className: Var, trueBranch, Wildcard(falseBranch))) => println(s"found a BINARY case: $scrutineeVar is $className") val classSymbol = className.getClassLikeSymbol @@ -62,10 +63,11 @@ trait PostProcessing { self: DesugarUCS with mlscript.pretyper.Traceable => ) { case ((classSymbol, loc, body), rest) => // TODO: Why not just keep the class name? val className = Var(classSymbol.name).withLoc(loc).withSymbol(classSymbol) - Case(className, body, rest) + Case(className, body, rest)(refined = false/*FIXME?*/) } // Assemble the final `CaseOf`. - top.copy(cases = fst.copy(body = processedTrueBranch, rest = actualFalseBranch)) + top.copy(cases = fst.copy(body = processedTrueBranch, rest = actualFalseBranch) + (refined = fst.refined)) // We recursively process the body of as`Let` bindings. case let @ Let(_, _, _, body) => let.copy(body = postProcess(body)) // Otherwise, this is not a part of a normalized term. @@ -85,9 +87,9 @@ trait PostProcessing { self: DesugarUCS with mlscript.pretyper.Traceable => case k @ Case(_, body, rest) => (trimEmptyTerm(body), trimEmptyCaseBranches(rest)) match { case (N, N) => N - case (S(body), N) => S(k.copy(body = body, rest = NoCases)) + case (S(body), N) => S(k.copy(body = body, rest = NoCases)(refined = k.refined)) case (N, S(rest)) => S(rest) - case (S(body), S(rest)) => S(k.copy(body = body, rest = rest)) + case (S(body), S(rest)) => S(k.copy(body = body, rest = rest)(refined = k.refined)) } } @@ -116,7 +118,8 @@ trait PostProcessing { self: DesugarUCS with mlscript.pretyper.Traceable => cases match { case NoCases => Wildcard(term).withLocOf(term) case Wildcard(body) => Wildcard(mergeTerms(body, term)) - case cases @ Case(_, _, rest) => cases.copy(rest = mergeTermIntoCaseBranches(term, rest)) + case cases @ Case(_, _, rest) => + cases.copy(rest = mergeTermIntoCaseBranches(term, rest))(refined = cases.refined) } }() @@ -152,12 +155,12 @@ trait PostProcessing { self: DesugarUCS with mlscript.pretyper.Traceable => } else { val (n1, y1) = disentangle(body, scrutineeVar, scrutinee, classSymbol) val (n2, y2) = rec(rest) - (kase.copy(body = n1, rest = n2), mergeTerms(y1, y2)) + (kase.copy(body = n1, rest = n2)(refined = kase.refined), mergeTerms(y1, y2)) } case kase @ Case(otherClassName, body, rest) => println(s"found another case branch matching against $otherClassName") val (n, y) = rec(rest) - kase.copy(rest = n) -> y + kase.copy(rest = n)(refined = kase.refined) -> y } val (n, y) = rec(cases) (top.copy(cases = n), y) @@ -175,8 +178,8 @@ trait PostProcessing { self: DesugarUCS with mlscript.pretyper.Traceable => println(s"found a case branch") val (n1, y1) = disentangle(body, scrutineeVar, scrutinee, classSymbol) val (n2, y2) = rec(rest) - (kase.copy(body = n1, rest = n2), (y1 match { - case S(term) => kase.copy(body = term, rest = y2) + (kase.copy(body = n1, rest = n2)(refined = kase.refined), (y1 match { + case S(term) => kase.copy(body = term, rest = y2)(refined = kase.refined) case N => y2 })) } diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index c9f989d9..54752168 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -200,11 +200,16 @@ trait Transformation { self: Traceable with Diagnosable => private def transformPattern(term: Term): Pattern = term match { case wildcard @ Var("_") => EmptyPattern(wildcard) // The case for wildcard. case nme @ Var("true" | "false") => ConcretePattern(nme) - case nme @ Var(name) if name.headOption.exists(_.isUpper) => ClassPattern(nme, N) + case nme @ Var(name) if name.headOption.exists(_.isUpper) => ClassPattern(nme, N, refined = false) case nme: Var => NamePattern(nme) case literal: Lit => LiteralPattern(literal) + case App(Var("refined"), PlainTup(p)) => + transformPattern(p) match { + case cp: ClassPattern => cp.copy(refined = true).withLocOf(cp) + case _ => ??? // TODO error + } case App(classNme @ Var(_), parameters: Tup) => - ClassPattern(classNme, S(transformTupleTerm(parameters))) + ClassPattern(classNme, S(transformTupleTerm(parameters)), refined = false) case tuple: Tup => TuplePattern(transformTupleTerm(tuple)) case other => println(s"other $other") diff --git a/shared/src/main/scala/mlscript/ucs/syntax.scala b/shared/src/main/scala/mlscript/ucs/syntax.scala index cbf18797..e7510743 100644 --- a/shared/src/main/scala/mlscript/ucs/syntax.scala +++ b/shared/src/main/scala/mlscript/ucs/syntax.scala @@ -13,9 +13,10 @@ package object syntax { case ConcretePattern(nme) => s"`${nme.name}`" case NamePattern(nme) => nme.toString case EmptyPattern(_) => "•" - case ClassPattern(Var(name), N) => name - case ClassPattern(Var(name), S(parameters)) => - parameters.mkString(s"$name(", ", ", ")") + case ClassPattern(Var(name), ps, rfd) => (if (rfd) "refined " else "") + (ps match { + case N => name + case S(parameters) => parameters.mkString(s"$name(", ", ", ")") + }) case TuplePattern(fields) => fields.mkString("(", ", ", ")") case RecordPattern(Nil) => "{}" case RecordPattern(entries) => entries.iterator.map { case (nme, als) => s"$nme: $als" }.mkString("{ ", ", ", " }") @@ -40,7 +41,7 @@ package object syntax { final case class EmptyPattern(source: Term) extends Pattern { override def children: List[Located] = source :: Nil } - final case class ClassPattern(val nme: Var, val parameters: Opt[List[Pattern]]) extends Pattern { + final case class ClassPattern(nme: Var, parameters: Opt[List[Pattern]], refined: Bool) extends Pattern { override def children: List[Located] = nme :: parameters.getOrElse(Nil) } final case class TuplePattern(fields: List[Pattern]) extends Pattern { diff --git a/shared/src/test/diff/nu/RefinedPattern.mls b/shared/src/test/diff/nu/RefinedPattern.mls new file mode 100644 index 00000000..a6eec809 --- /dev/null +++ b/shared/src/test/diff/nu/RefinedPattern.mls @@ -0,0 +1,46 @@ +:PreTyper + + +class C +//│ class C { +//│ constructor() +//│ } + + +:e +fun test(x) = if x is + C then x.a +//│ ╔══[ERROR] Type `C` does not contain member `a` +//│ ║ l.12: C then x.a +//│ ╙── ^^ +//│ fun test: C -> error + +fun test(x) = if x is + refined(C) then x.a +//│ fun test: forall 'a. (C & {a: 'a}) -> 'a + +class D(val a: Int) extends C +//│ class D(a: Int) extends C + +test(D(123)) +//│ Int +//│ res +//│ = 123 + + +:e +refined +//│ ╔══[ERROR] Variable refined not found in scope +//│ ║ l.32: refined +//│ ╙── ^^^^^^^ +//│ ╔══[ERROR] Illegal use of reserved operator: refined +//│ ║ l.32: refined +//│ ╙── ^^^^^^^ +//│ ╔══[ERROR] identifier not found: refined +//│ ║ l.32: refined +//│ ╙── ^^^^^^^ +//│ error +//│ Code generation encountered an error: +//│ unresolved symbol refined + + From 3c0209acfe868e2e86192614507d7990e17166cf Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Fri, 12 Jan 2024 21:55:04 +0800 Subject: [PATCH 055/143] Post-processing can handle all patterns and minor fixes Major updates - Post-processing handles not only class patterns right now. - Normalization should NOT concatenate branches deeply or it may generate infinitely large splits. Minor changes - Remove useless classes, methods, and fields. - Extract classes into files. For example, `PatternInfo`. - Add test files which shows the intermediate results of post- processing and normalization. - Improve debugging utilities. - Add `SimpleList` test example. - Improve debugging output --- shared/src/main/scala/mlscript/helpers.scala | 6 +- .../main/scala/mlscript/pretyper/Scope.scala | 6 +- .../main/scala/mlscript/pretyper/Symbol.scala | 2 + .../scala/mlscript/pretyper/Traceable.scala | 14 +- .../main/scala/mlscript/ucs/DesugarUCS.scala | 145 +++++++------- .../mlscript/ucs/context/Matchable.scala | 8 +- .../mlscript/ucs/context/PatternInfo.scala | 127 ++++++++++++ .../mlscript/ucs/context/ScrutineeData.scala | 157 +++++++-------- shared/src/main/scala/mlscript/ucs/core.scala | 55 ++---- .../src/main/scala/mlscript/ucs/display.scala | 30 +-- .../mlscript/ucs/stages/Desugaring.scala | 12 +- .../mlscript/ucs/stages/Normalization.scala | 163 +++++++-------- .../mlscript/ucs/stages/PostProcessing.scala | 185 ++++++++++-------- .../src/main/scala/mlscript/ucs/syntax.scala | 4 +- .../diff/pretyper/ucs/coverage/Refinement.mls | 10 +- .../pretyper/ucs/examples/LispInterpreter.mls | 45 ++--- .../test/diff/pretyper/ucs/examples/STLC.mls | 2 +- .../diff/pretyper/ucs/examples/SimpleLisp.mls | 141 +++++++++++++ .../diff/pretyper/ucs/patterns/Literals.mls | 60 ++++-- .../pretyper/ucs/stages/Normalization.mls | 81 ++++++++ .../pretyper/ucs/stages/PostProcessing.mls | 71 +++++++ .../diff/pretyper/ucs/stages/TransfromUCS.mls | 2 +- .../src/test/scala/mlscript/DiffTests.scala | 4 +- 23 files changed, 893 insertions(+), 437 deletions(-) create mode 100644 shared/src/main/scala/mlscript/ucs/context/PatternInfo.scala create mode 100644 shared/src/test/diff/pretyper/ucs/examples/SimpleLisp.mls create mode 100644 shared/src/test/diff/pretyper/ucs/stages/Normalization.mls create mode 100644 shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls diff --git a/shared/src/main/scala/mlscript/helpers.scala b/shared/src/main/scala/mlscript/helpers.scala index ee53a370..68fda965 100644 --- a/shared/src/main/scala/mlscript/helpers.scala +++ b/shared/src/main/scala/mlscript/helpers.scala @@ -585,7 +585,11 @@ trait TermImpl extends StatementImpl { self: Term => case Blk(stmts) => stmts.iterator.map(_.showDbg).mkString("{", "; ", "}") case IntLit(value) => value.toString case DecLit(value) => value.toString - case StrLit(value) => '"'.toString + value + '"' + case StrLit(value) => value.iterator.map { + case '\\' => "\\\\"; case '"' => "\\\""; case '\'' => "\\'" + case '\n' => "\\n"; case '\r' => "\\r"; case '\t' => "\\t" + case '\f' => "\\f"; case '\b' => "\\b"; case c => c.toString() + }.mkString("\"", "", "\"") case UnitLit(value) => if (value) "undefined" else "null" case v @ Var(name) => name + v.uid.fold("")("::"+_.toString) case Asc(trm, ty) => s"${trm.showDbg} : ${ty.showDbg2}" |> bra diff --git a/shared/src/main/scala/mlscript/pretyper/Scope.scala b/shared/src/main/scala/mlscript/pretyper/Scope.scala index e8c859ff..56c94aa6 100644 --- a/shared/src/main/scala/mlscript/pretyper/Scope.scala +++ b/shared/src/main/scala/mlscript/pretyper/Scope.scala @@ -2,7 +2,7 @@ package mlscript.pretyper import collection.immutable.Map import mlscript.utils._, shorthands._ -import mlscript.{Mod, NuTypeDef, TypeName, TypingUnit, Var} +import mlscript.{Als, Mod, NuTypeDef, TypeName, TypingUnit, Var} import scala.annotation.tailrec import symbol._ @@ -94,8 +94,10 @@ object Scope { val global: Scope = { val trueDefn = NuTypeDef(Mod, TypeName("true"), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, N) val falseDefn = NuTypeDef(Mod, TypeName("false"), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, N) + val nothingDefn = NuTypeDef(Als, TypeName("nothing"), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, N) val trueSymbol = new ModuleSymbol(trueDefn) val falseSymbol = new ModuleSymbol(falseDefn) + val nothingSymbol = new TypeAliasSymbol(nothingDefn) Scope.from( """true,false,document,window,typeof,toString,not,succ,log,discard,negate, |round,add,sub,mul,div,sqrt,lt,le,gt,ge,slt,sle,sgt,sge,length,concat,eq, @@ -106,7 +108,7 @@ object Scope { .iterator .map(_.trim) .map(name => new LocalTermSymbol(Var(name))) - .concat(trueSymbol :: falseSymbol :: Nil) + .concat(trueSymbol :: falseSymbol :: nothingSymbol :: Nil) ) } } diff --git a/shared/src/main/scala/mlscript/pretyper/Symbol.scala b/shared/src/main/scala/mlscript/pretyper/Symbol.scala index 89db935a..48ae45c7 100644 --- a/shared/src/main/scala/mlscript/pretyper/Symbol.scala +++ b/shared/src/main/scala/mlscript/pretyper/Symbol.scala @@ -34,6 +34,8 @@ package object symbol { var sealedDerivedTypes: Ls[TypeSymbol] = Nil @inline def hasSuperType(superType: TypeSymbol): Bool = baseTypes.exists(_ === superType) + + def showDbg: Str = s"${defn.kind.str} $name" } object TypeSymbol { diff --git a/shared/src/main/scala/mlscript/pretyper/Traceable.scala b/shared/src/main/scala/mlscript/pretyper/Traceable.scala index 206982fe..c2ffd59c 100644 --- a/shared/src/main/scala/mlscript/pretyper/Traceable.scala +++ b/shared/src/main/scala/mlscript/pretyper/Traceable.scala @@ -7,7 +7,7 @@ trait Traceable { /** The set of topics to debug. Empty set indicates all topics. */ protected val debugTopics: Opt[Set[Str]] = N protected var indent = 0 - private var topic: Opt[Str] = N + private var currentTopic: Opt[Str] = N def emitString(str: String): Unit = scala.Predef.println(str) @@ -22,10 +22,10 @@ trait Traceable { res } - def traceWithTopic[T](topic: Str)(thunk: => T): T = { - this.topic = S(topic) + def traceWithTopic[T](currentTopic: Str)(thunk: => T): T = { + this.currentTopic = S(currentTopic) val res = thunk - this.topic = N + this.currentTopic = N res } @@ -33,9 +33,9 @@ trait Traceable { thunk @inline protected def println(x: => Any): Unit = - topic match { - case N => if (debugTopics.isDefined) printLineByLine(x) - case S(topic) => if (debugTopics.fold(false)(ts => ts.isEmpty || ts.contains(topic))) printLineByLine(x) + debugTopics match { + case S(someTopics) if someTopics.isEmpty || currentTopic.fold(false)(someTopics) => printLineByLine(x) + case N | S(_) => () } } diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index 871420ec..2617c402 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -6,8 +6,8 @@ import mlscript.ucs.context.{Context, ScrutineeData} import mlscript.ucs.display.{showNormalizedTerm, showSplit} import mlscript.pretyper.{PreTyper, Scope} import mlscript.pretyper.symbol._ -import mlscript._, utils._, shorthands._ -import mlscript.Message, Message.MessageContext +import mlscript.{If, Loc, Message, Var}, Message.MessageContext, mlscript.Diagnostic.PreTyping +import mlscript.utils._, shorthands._ // TODO: Rename to `Desugarer` once the old desugarer is removed. trait DesugarUCS extends Transformation @@ -16,6 +16,15 @@ trait DesugarUCS extends Transformation with PostProcessing with CoverageChecking { self: PreTyper => + /** A shorthand function to raise errors without specifying the source. */ + protected def raiseError(messages: (Message -> Opt[Loc])*): Unit = + raiseError(PreTyping, messages: _*) + + /** A shorthand function to raise warnings without specifying the source. */ + protected def raiseWarning(messages: (Message -> Opt[Loc])*): Unit = + raiseWarning(PreTyping, messages: _*) + + /** Create a fresh local symbol for the given `Var`. */ protected def freshSymbol(nme: Var): LocalTermSymbol = new LocalTermSymbol(nme) /** Common operations of `Var` which can be shared within all stages. */ @@ -23,23 +32,30 @@ trait DesugarUCS extends Transformation /** Associate the given `Var` with a fresh `ValueSymbol`. */ def withFreshSymbol: Var = nme.withSymbol(freshSymbol(nme)) + private def requireClassLikeSymbol(symbol: TypeSymbol): TypeSymbol = symbol match { + case symbol @ (_: TraitSymbol | _: ClassSymbol | _: ModuleSymbol) => symbol + case symbol: MixinSymbol => + throw new DesugaringException(msg"Mixins are not allowed in pattern" -> nme.toLoc :: Nil) + case symbol: TypeAliasSymbol => + throw new DesugaringException(msg"Type alias is not allowed in pattern" -> nme.toLoc :: Nil) + } + /** * If the given `Var` represents a class name, get its associated `ClassSymbol`. * * @param className the class name variable */ - def getClassLikeSymbol: TypeSymbol = - trace(s"getClassLikeSymbol <== ${nme.showDbg}") { - nme.symbolOption match { - case S(symbol: ClassSymbol) => symbol - case S(symbol: TraitSymbol) => symbol - case S(symbol: ModuleSymbol) => symbol - case S(symbol: Symbol) => throw new DesugaringException( - msg"variable ${nme.name} is not associated with a class symbol" -> N :: Nil) - case N => throw new DesugaringException( - msg"variable ${nme.name} is not associated with any symbols" -> N :: Nil) - } - }(symbol => s"getClassLikeSymbol ==> ${symbol.name}") + def getClassLikeSymbol: TypeSymbol = { + val symbol = nme.symbolOption match { + case S(symbol: TypeSymbol) => requireClassLikeSymbol(symbol) + case S(symbol: TermSymbol) => throw new DesugaringException( + msg"variable ${nme.name} is not associated with a class symbol" -> nme.toLoc :: Nil) + case N => throw new DesugaringException( + msg"variable ${nme.name} is not associated with any symbols" -> nme.toLoc :: Nil) + } + println(s"getClassLikeSymbol: ${nme.name} ==> ${symbol.showDbg}") + symbol + } /** * A short hand for `nme.symbol.getScrutinee` but add a diagnostic message @@ -50,11 +66,10 @@ trait DesugarUCS extends Transformation case S(otherSymbol) => throw new DesugaringException( msg"Expected scrutinee symbol, found ${nme.symbol.name}" -> nme.toLoc :: Nil ) - case N => throw new DesugaringException( - msg"Scrutinee symbol not found" -> nme.toLoc :: Nil - ) + case N => throw new DesugaringException(msg"Scrutinee symbol not found" -> nme.toLoc :: Nil) } + /** Associate the `Var` with a scrutinee and returns the same `Var`. */ def withScrutinee(scrutinee: ScrutineeData)(implicit context: Context): Var = nme.symbolOption match { case S(symbol: TermSymbol) => symbol.addScrutinee(scrutinee) @@ -67,46 +82,35 @@ trait DesugarUCS extends Transformation ) } - def withResolvedTermSymbol(implicit scope: Scope): Var = { - nme.symbol = nme.resolveTermSymbol - nme - } - - def resolveTermSymbol(implicit scope: Scope): TermSymbol = - scope.getTermSymbol(nme.name).getOrElse { + /** Associate the `Var` with a term symbol and returns the term symbol. */ + def resolveTermSymbol(implicit scope: Scope): TermSymbol = { + val symbol = scope.getTermSymbol(nme.name).getOrElse { throw new DesugaringException(msg"Undefined symbol found in patterns." -> nme.toLoc :: Nil) } - - def withResolvedTypeSymbol(implicit scope: Scope): Var = { - nme.symbol = nme.resolveTypeSymbol - nme + nme.symbol = symbol + symbol } - def resolveTypeSymbol(implicit scope: Scope): TypeSymbol = scope.getTypeSymbol(nme.name) match { - case S(symbol: TraitSymbol) => - println(s"resolveTypeSymbol ${nme} ==> trait") - nme.symbol = symbol - symbol - case S(symbol: ClassSymbol) => - println(s"resolveTypeSymbol ${nme} ==> class") - nme.symbol = symbol - symbol - case S(symbol: ModuleSymbol) => - println(s"resolveTypeSymbol ${nme} ==> module") - nme.symbol = symbol - symbol - case S(symbol: MixinSymbol) => - throw new DesugaringException(msg"Mixins are not allowed in pattern" -> nme.toLoc :: Nil) - case S(symbol: TypeAliasSymbol) => - throw new DesugaringException(msg"Type alias is not allowed in pattern" -> nme.toLoc :: Nil) - case N => - throw new DesugaringException(msg"Undefined symbol found in patterns." -> nme.toLoc :: Nil) + /** Associate the `Var` with a term symbol and returns the same `Var`. */ + def withResolvedTermSymbol(implicit scope: Scope): Var = { nme.resolveTermSymbol; nme } + + /** Associate the `Var` with a class like symbol and returns the class like symbol. */ + def resolveClassLikeSymbol(implicit scope: Scope): TypeSymbol = { + val symbol = scope.getTypeSymbol(nme.name) match { + case S(symbol) => requireClassLikeSymbol(symbol) + case N => throw new DesugaringException(msg"Undefined symbol found in patterns." -> nme.toLoc :: Nil) + } + nme.symbol = symbol + symbol } + + /** Associate the `Var` with a class like symbol and returns the same `Var`. */ + def withResolvedClassLikeSymbol(implicit scope: Scope): Var = { nme.resolveClassLikeSymbol; nme } } protected def traverseIf(`if`: If)(implicit scope: Scope): Unit = { implicit val context: Context = new Context(`if`) - trace("traverseIf") { + try trace("traverseIf") { // Stage 0: Transformation val transformed = traceWithTopic("ucs.transform") { println("STEP 0") @@ -118,10 +122,11 @@ trait DesugarUCS extends Transformation // Stage 1: Desugaring val desugared = traceWithTopic("desugar") { println("STEP 1") - val desugared = desugar(transformed) + desugar(transformed) + } + traceWithTopic("desugar.result") { println("Desugared UCS term:") println(showSplit(desugared)) - desugared } traceWithTopic("traverse") { println("STEP 1.5") @@ -130,18 +135,20 @@ trait DesugarUCS extends Transformation // Stage 2: Normalization val normalized = traceWithTopic("normalize") { println("STEP 2") - val normalized = normalize(desugared) + normalize(desugared) + } + traceWithTopic("normalize.result") { println("Normalized UCS term:") println(showNormalizedTerm(normalized)) - normalized } // Stage 3: Post-processing val postProcessed = traceWithTopic("postprocess") { println("STEP 3") - val postProcessed = postProcess(normalized) + postProcess(normalized) + } + traceWithTopic("postprocess.result") { println("Post-processed UCS term:") println(showNormalizedTerm(postProcessed)) - postProcessed } // Stage 4: Coverage checking traceWithTopic("coverage") { @@ -155,7 +162,9 @@ trait DesugarUCS extends Transformation } // Epilogue `if`.desugaredTerm = S(postProcessed) - }(_ => "traverseIf ==> ()") + }(_ => "traverseIf ==> ()") catch { + case e: DesugaringException => raiseError(e.messages: _*) + } } private def traverseSplit(split: core.Split)(implicit scope: Scope): Unit = @@ -163,30 +172,16 @@ trait DesugarUCS extends Transformation import core._ split match { case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => - println(s"found branch: $scrutinee is $pattern") traverseTerm(scrutinee) - val patternSymbols = traversePattern(scrutinee, pattern) + val patternSymbols = pattern.declaredVars.map(nme => nme -> nme.symbol) traverseSplit(continuation)(scope.withEntries(patternSymbols)) traverseSplit(tail) - case Split.Let(_, name, rhs, tail) => - println(s"found let binding: \"$name\"") - println(s"traverse rhs: $rhs") - traverseTerm(rhs) - traverseSplit(tail)(scope + name.symbol) + case Split.Let(isRec, name, rhs, tail) => + val scopeWithName = scope + name.symbol + traverseTerm(rhs)(if (isRec) scopeWithName else scope) + traverseSplit(tail)(scopeWithName) case Split.Else(default) => traverseTerm(default) - case Split.Nil => println("the end") + case Split.Nil => () } }() - - private def traversePattern(scrutinee: Var, pattern: core.Pattern)(implicit scope: Scope): List[Var -> Symbol] = - trace(s"traversePattern <== $pattern") { - pattern match { - case core.Pattern.Literal(literal) => Nil - case core.Pattern.Name(nme) => nme -> nme.symbol :: Nil - // For now, there should only be parameter-less class patterns. - case core.Pattern.Class(nme) => Nil - case core.Pattern.Tuple(_) => ??? - case core.Pattern.Record(_) => ??? - } - }(_.iterator.map(_._1.name).mkString("traversePattern ==> [", ", ", "]")) } diff --git a/shared/src/main/scala/mlscript/ucs/context/Matchable.scala b/shared/src/main/scala/mlscript/ucs/context/Matchable.scala index 9d9d96b8..ab85e6de 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Matchable.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Matchable.scala @@ -35,5 +35,11 @@ trait Matchable { private[ucs] def addScrutinee(scrutinee: ScrutineeData)(implicit context: Context): Unit = { require(!isScrutinee) // It should be impossible to add a scrutinee twice. scrutinees += context -> scrutinee - } + } + + /** Associate the symbol with a scrutinee in the given context and returns the current object. */ + private[ucs] def withScrutinee(scrutinee: ScrutineeData)(implicit context: Context): this.type = { + addScrutinee(scrutinee) + this + } } diff --git a/shared/src/main/scala/mlscript/ucs/context/PatternInfo.scala b/shared/src/main/scala/mlscript/ucs/context/PatternInfo.scala new file mode 100644 index 00000000..2d8d4a1a --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/context/PatternInfo.scala @@ -0,0 +1,127 @@ +package mlscript.ucs.context + +import collection.mutable.{Buffer, SortedMap => MutSortedMap} +import mlscript.{Lit, Loc, Located, SimpleTerm, TypeName, Var} +import mlscript.pretyper.symbol.TypeSymbol +import mlscript.utils._, shorthands._ + +abstract class PatternInfo { + private val locationsBuffer: Buffer[Loc] = Buffer.empty + + def addLocation(located: Located): Unit = located.getLoc.foreach(locationsBuffer += _) + + def addLocation(location: Opt[Loc]): Unit = locationsBuffer ++= location + + /** Get the location of this pattern's first occurrence. */ + def firstOccurrence: Option[Loc] = locationsBuffer.headOption + + def locations: Ls[Loc] = locationsBuffer.toList + + def arity: Opt[Int] + + def showDbg: Str + + /** + * Checks if the pattern is same as expressed by the given `SimpleTerm`. Note + * that we should pass `pat` of `Case` to this function. + */ + def matches(pat: SimpleTerm): Bool + + /** Create a `SimpleTerm` which can be used as `pat` of `Case`. */ + def toCasePattern: SimpleTerm +} + +object PatternInfo { + class ClassLike(val classLikeSymbol: TypeSymbol, scrutinee: ScrutineeData) extends PatternInfo { + private var unappliedVarOpt: Opt[Var] = N + private val parameters: MutSortedMap[Int, ScrutineeData] = MutSortedMap.empty + + /** + * Get or create a sub-scrutinee for the given parameter index. + * + * @param index the index of the parameter. + * @return a `ScrutineeData` for the parameter whose parent scrutinee is the + * current scrutinee + */ + def getParameter(index: Int): ScrutineeData = { + require(index >= 0) + parameters.getOrElseUpdate(index, scrutinee.freshSubScrutinee) + } + + def getUnappliedVar(default: => Var): Var = + unappliedVarOpt.getOrElse { + val unappliedVar = default + unappliedVarOpt = S(unappliedVar) + unappliedVar + } + + override def arity: Opt[Int] = parameters.keysIterator.maxOption.map(_ + 1) + + override def showDbg: Str = s"${classLikeSymbol.name}" + + override def matches(pat: SimpleTerm): Bool = + pat match { + case pat: Var => pat.symbolOption.exists(_ === classLikeSymbol) + case _: Lit => false + } + + override def toCasePattern: SimpleTerm = + Var(classLikeSymbol.name).withLoc(firstOccurrence).withSymbol(classLikeSymbol) + } + + class Tuple(scrutinee: ScrutineeData) extends PatternInfo { + private val fields: MutSortedMap[Int, ScrutineeData] = MutSortedMap.empty + + def getField(index: Int): ScrutineeData = + fields.getOrElseUpdate(index, scrutinee.freshSubScrutinee) + + override def arity: Opt[Int] = fields.keysIterator.maxOption.map(_ + 1) + + override def showDbg: Str = s"tuple#${arity.getOrElse("?")}" + + override def matches(pat: SimpleTerm): Bool = false + + /** + * Note that currently we only support simple tuple patterns. They should + * disappear after desugaring stage, therefore, it will be an error if you + * find an instance of this class. + */ + override def toCasePattern: SimpleTerm = ??? + } + + class Literal(val literal: Lit) extends PatternInfo { + override def arity: Opt[Int] = N + + override def showDbg: Str = literal.idStr + + override def matches(pat: SimpleTerm): Bool = + pat match { + case _: Var => false + case pat => pat === literal + } + + override def toCasePattern: SimpleTerm = literal.withLoc(firstOccurrence) + } + + /** + * This can be actually merged with `LiteralPatternInfo`. However, there's no + * `Lit` sub-classes for Boolean types, so the representation is a little bit + * awkward, also, it makes sense to consider Boolean patterns separately + * because we can check the Boolean exhaustiveness with them. + */ + class Boolean(val value: Var) extends PatternInfo { + require(value.name === "true" || value.name === "false") + + override def arity: Opt[Int] = N + + override def showDbg: Str = value.toString + + override def matches(pat: SimpleTerm): Bool = + pat match { + case Var(name) => value.name === name + case _ => false + } + + override def toCasePattern: SimpleTerm = value.withLoc(firstOccurrence) + } +} diff --git a/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala b/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala index 251e674f..7f4b08b0 100644 --- a/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala +++ b/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala @@ -1,89 +1,36 @@ package mlscript.ucs.context -import collection.mutable.{Buffer, Map => MutMap, SortedMap => MutSortedMap, SortedSet => MutSortedSet} -import mlscript.{Lit, Loc, Located, NuFunDef, NuTypeDef, TypeName, Var} +import collection.mutable.{Buffer, SortedMap => MutSortedMap, SortedSet => MutSortedSet} +import mlscript.{Lit, Loc, Var} import mlscript.pretyper.symbol.TypeSymbol import mlscript.utils._, shorthands._ import mlscript.ucs.context.CaseSet - -abstract class PatternInfo { - private val locationsBuffer: Buffer[Loc] = Buffer.empty - - def addLocation(located: Located): Unit = located.getLoc.foreach(locationsBuffer += _) - - def addLocation(location: Opt[Loc]): Unit = locationsBuffer ++= location - - def firstOccurrence: Option[Loc] = locationsBuffer.headOption - - def locations: Ls[Loc] = locationsBuffer.toList - - def arity: Opt[Int] -} - -class ClassPatternInfo(scrutinee: ScrutineeData) extends PatternInfo { - private var unappliedVarOpt: Opt[Var] = N - private val parameters: MutSortedMap[Int, ScrutineeData] = MutSortedMap.empty - - /** - * Get or create a sub-scrutinee for the given parameter index. - * - * @param index the index of the parameter. - * @return a `ScrutineeData` for the parameter whose parent scrutinee is the - * current scrutinee - */ - def getParameter(index: Int): ScrutineeData = { - require(index >= 0) - parameters.getOrElseUpdate(index, scrutinee.freshSubScrutinee) - } - - def getUnappliedVar(default: => Var): Var = - unappliedVarOpt.getOrElse { - val unappliedVar = default - unappliedVarOpt = S(unappliedVar) - unappliedVar - } - - override def arity: Opt[Int] = parameters.keysIterator.maxOption.map(_ + 1) -} - -class TuplePatternInfo(scrutinee: ScrutineeData) extends PatternInfo { - private val fields: MutSortedMap[Int, ScrutineeData] = MutSortedMap.empty - - def getField(index: Int): ScrutineeData = - fields.getOrElseUpdate(index, scrutinee.freshSubScrutinee) - - override def arity: Opt[Int] = fields.keysIterator.maxOption.map(_ + 1) -} - -class LiteralPatternInfo extends PatternInfo { - override def arity: Opt[Int] = N -} - -/** - * This can be actually merged with `LiteralPatternInfo`. However, there's no - * `Lit` sub-classes for Boolean types, so the representation is a little bit - * awkward, also, it makes sense to consider Boolean patterns separately - * because we can check the Boolean exhaustiveness with them. - */ -class BooleanPatternInfo extends PatternInfo { - override def arity: Opt[Int] = N -} +import mlscript.DecLit +import mlscript.IntLit +import mlscript.StrLit +import mlscript.UnitLit class ScrutineeData(val context: Context, parent: Opt[ScrutineeData]) { + import ScrutineeData._ + private val locations: Buffer[Loc] = Buffer.empty private var generatedVarOpt: Opt[Var] = N - private val classLikePatterns: MutMap[TypeSymbol, ClassPatternInfo] = MutMap.empty + private val classLikePatterns: MutSortedMap[TypeSymbol, PatternInfo.ClassLike] = MutSortedMap.empty(classLikeSymbolOrdering) // Currently we only support simple tuple patterns, so there is only _one_ // slot for tuple patterns. After we support complex tuple patterns, we need - // to extend this fields to a map from tuple arity to `TuplePatternInfo`. - // private val tuplePatterns: MutMap[Int, TuplePatternInfo] = MutMap.empty + // to extend this fields to a map from tuple arity to `PatternInfo.Tuple`. + // private val tuplePatterns: MutMap[Int, PatternInfo.Tuple] = MutMap.empty // If we support tuple pattern splice, we need a more expressive key in the // map's type. - private var tuplePatternOpt: Opt[TuplePatternInfo] = N + private var tuplePatternOpt: Opt[PatternInfo.Tuple] = N private var alisesSet: MutSortedSet[Var] = MutSortedSet.empty - private val literalPatterns: MutMap[Lit, LiteralPatternInfo] = MutMap.empty - private val booleanPatterns: MutMap[Bool, BooleanPatternInfo] = MutMap.empty + private val literalPatterns: MutSortedMap[Lit, PatternInfo.Literal] = MutSortedMap.empty(literalOrdering) + /** + * The key should be either `Var("true")` or `Var("false")`. We want to keep + * the type symbol of the variable so that it still work in following stages. + */ + private val booleanPatterns: MutSortedMap[Var, PatternInfo.Boolean] = MutSortedMap.empty(varNameOrdering) def +=(alias: Var): Unit = alisesSet += alias @@ -92,23 +39,23 @@ class ScrutineeData(val context: Context, parent: Opt[ScrutineeData]) { def aliasesIterator: Iterator[Var] = alisesSet.iterator /** - * If there is already a `ClassPatternInfo` for the given symbol, return it. - * Otherwise, create a new `ClassPatternInfo` and return it. + * If there is already a `PatternInfo.ClassLike` for the given symbol, return it. + * Otherwise, create a new `PatternInfo.ClassLike` and return it. */ - def getOrCreateClassPattern(classLikeSymbol: TypeSymbol): ClassPatternInfo = - classLikePatterns.getOrElseUpdate(classLikeSymbol, new ClassPatternInfo(this)) + def getOrCreateClassPattern(classLikeSymbol: TypeSymbol): PatternInfo.ClassLike = + classLikePatterns.getOrElseUpdate(classLikeSymbol, new PatternInfo.ClassLike(classLikeSymbol, this)) /** * Get the class pattern but DO NOT create a new one if there isn't. This * function is mainly used in post-processing because we don't want to * accidentally create new patterns. */ - def getClassPattern(classLikeSymbol: TypeSymbol): Opt[ClassPatternInfo] = + def getClassPattern(classLikeSymbol: TypeSymbol): Opt[PatternInfo.ClassLike] = classLikePatterns.get(classLikeSymbol) /** - * If there is already a `TuplePatternInfo`, return it. Otherwise, create a - * new `TuplePatternInfo` and return it. + * If there is already a `PatternInfo.Tuple`, return it. Otherwise, create a + * new `PatternInfo.Tuple` and return it. * * **NOTE**: There's only one slot for tuple patterns because we cannot * differentiate tuple types in underlying MLscript case terms. In the future, @@ -116,35 +63,42 @@ class ScrutineeData(val context: Context, parent: Opt[ScrutineeData]) { * a signature like this. * * ```scala - * def getOrCreateTuplePattern(dimension: TupleDimension): TuplePatternInfo + * def getOrCreateTuplePattern(dimension: TupleDimension): PatternInfo.Tuple * case class TupleDimension(knownArity: Int, hasSplice: Bool) * ``` */ - def getOrCreateTuplePattern: TuplePatternInfo = + def getOrCreateTuplePattern: PatternInfo.Tuple = tuplePatternOpt.getOrElse { - val tuplePattern = new TuplePatternInfo(this) + val tuplePattern = new PatternInfo.Tuple(this) tuplePatternOpt = S(tuplePattern) tuplePattern } /** Get the tuple pattern and create a new one if there isn't. */ - def getOrCreateLiteralPattern(literal: Lit): LiteralPatternInfo = - literalPatterns.getOrElseUpdate(literal, new LiteralPatternInfo) + def getOrCreateLiteralPattern(literal: Lit): PatternInfo.Literal = + literalPatterns.getOrElseUpdate(literal, new PatternInfo.Literal(literal)) + + /** + * The key should be either `Var("true")` or `Var("false")`. We want to keep + * the type symbol of the variable so that it still work in following stages. + */ + def getOrCreateBooleanPattern(value: Var): PatternInfo.Boolean = + booleanPatterns.getOrElseUpdate(value, new PatternInfo.Boolean(value)) - def getOrCreateBooleanPattern(value: Bool): BooleanPatternInfo = - booleanPatterns.getOrElseUpdate(value, new BooleanPatternInfo) + def classLikePatternsIterator: Iterator[PatternInfo.ClassLike] = classLikePatterns.valuesIterator - def classLikePatternsIterator: Iterator[TypeSymbol -> ClassPatternInfo] = classLikePatterns.iterator + def patternsIterator: Iterator[PatternInfo] = + classLikePatterns.valuesIterator ++ literalPatterns.valuesIterator ++ booleanPatterns.valuesIterator - /** Get the name representation of patterns. Only for debugging. */ - def patterns: Iterator[Str] = { + /** Get a list of string representation of patterns. Only for debugging. */ + private[ucs] def showPatternsDbg: Str = { val classLikePatternsStr = classLikePatterns.iterator.map { case (symbol, pattern) => s"${symbol.name}(${pattern.arity.fold("?")(_.toString)})" } val tuplePatternStr = tuplePatternOpt.iterator.map { tuplePattern => s"tuple(${tuplePattern.arity.fold("?")(_.toString)})" } - classLikePatternsStr ++ tuplePatternStr + (classLikePatternsStr ++ tuplePatternStr).mkString(", ") } def freshSubScrutinee: ScrutineeData = context.freshScrutinee(this) @@ -188,4 +142,29 @@ object ScrutineeData { case _ => N } } + + private def literalInternalOrder(literal: Lit): Int = literal match { + case UnitLit(true) => 0 + case UnitLit(false) => 1 + case _: DecLit => 2 + case _: IntLit => 3 + case _: StrLit => 4 + } + + private implicit val classLikeSymbolOrdering: Ordering[TypeSymbol] = new Ordering[TypeSymbol] { + override def compare(x: TypeSymbol, y: TypeSymbol): Int = x.defn.name.compareTo(y.defn.name) + } + + private implicit val literalOrdering: Ordering[Lit] = new Ordering[Lit] { + override def compare(x: Lit, y: Lit): Int = (x, y) match { + case (DecLit(x), DecLit(y)) => x.compare(y) + case (IntLit(x), IntLit(y)) => x.compare(y) + case (StrLit(x), StrLit(y)) => x.compare(y) + case _ => literalInternalOrder(x).compare(literalInternalOrder(y)) + } + } + + private implicit val varNameOrdering: Ordering[Var] = new Ordering[Var] { + override def compare(x: Var, y: Var): Int = x.name.compareTo(y.name) + } } \ No newline at end of file diff --git a/shared/src/main/scala/mlscript/ucs/core.scala b/shared/src/main/scala/mlscript/ucs/core.scala index 14b1577f..2d9c725b 100644 --- a/shared/src/main/scala/mlscript/ucs/core.scala +++ b/shared/src/main/scala/mlscript/ucs/core.scala @@ -1,22 +1,18 @@ package mlscript.ucs -import collection.mutable.Buffer import mlscript.{Diagnostic, Lit, Loc, Located, Message, Term, Var} import mlscript.utils._, shorthands._ -import mlscript.ucs.core.Split.Cons -import mlscript.ucs.core.Split.Let -import mlscript.ucs.core.Split.Else -import mlscript.ucs.stages.Desugaring package object core { sealed abstract class Pattern extends Located { + def declaredVars: Iterator[Var] = this match { + case _: Pattern.Literal | _: Pattern.Class => Iterator.empty + case Pattern.Name(nme) => Iterator.single(nme) + } override def toString(): String = this match { - case Pattern.Literal(literal) => literal.toString + case Pattern.Literal(literal) => literal.idStr case Pattern.Name(Var(name)) => name case Pattern.Class(Var(name)) => name - case Pattern.Tuple(fields) => fields.iterator.map(_.fold("_")(_.name)).mkString("(", ", ", ")") - case Pattern.Record(Nil) => "{}" - case Pattern.Record(entries) => entries.iterator.map { case (nme, als) => s"$nme: $als" }.mkString("{ ", ", ", " }") } } object Pattern { @@ -29,12 +25,6 @@ package object core { final case class Class(nme: Var) extends Pattern { override def children: Ls[Located] = nme :: Nil } - final case class Tuple(elements: List[Opt[Var]]) extends Pattern { - override def children: Ls[Located] = elements.flatten - } - final case class Record(entries: List[(Var -> Var)]) extends Pattern { - override def children: Ls[Located] = entries.iterator.flatMap { case (nme, als) => nme :: als :: Nil }.toList - } def getParametersLoc(parameters: List[Opt[Var]]): Opt[Loc] = parameters.foldLeft(None: Opt[Loc]) { @@ -49,9 +39,11 @@ package object core { parameters.fold("empty")(_.map(_.fold("_")(_.name)).mkString("[", ", ", "]")) } - final case class Branch(scrutinee: Var, pattern: Pattern, continuation: Split) + final case class Branch(scrutinee: Var, pattern: Pattern, continuation: Split) extends Located { + override def children: List[Located] = scrutinee :: pattern :: continuation :: Nil + } - sealed abstract class Split { + sealed abstract class Split extends Located { @inline def ::(head: Branch): Split = Split.Cons(head, this) @@ -76,7 +68,7 @@ package object core { case Split.Nil => false } - lazy val freeVars: Set[Var] = this match { + override lazy val freeVars: Set[Var] = this match { case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => // FIXME: It is safe to ignore `pattern` for now. continuation.freeVars ++ tail.freeVars @@ -90,26 +82,19 @@ package object core { * Remove duplicated bindings. */ def withoutBindings(vars: Set[Var]): Split = this match { - case self @ Cons(head @ Branch(_, _, continuation), tail) => + case self @ Split.Cons(head @ Branch(_, _, continuation), tail) => self.copy(head.copy(continuation = continuation.withoutBindings(vars)), tail.withoutBindings(vars)) - case self @ Let(_, name, _, tail) if vars contains name => tail.withoutBindings(vars) - case self @ Let(_, _, _, tail) => self.copy(tail = tail.withoutBindings(vars)) - case Else(_) | Split.Nil => this + case self @ Split.Let(_, name, _, tail) if vars contains name => tail.withoutBindings(vars) + case self @ Split.Let(_, _, _, tail) => self.copy(tail = tail.withoutBindings(vars)) + case Split.Else(_) | Split.Nil => this } - private val diagnostics: Buffer[Message -> Opt[Loc]] = Buffer.empty - - def withDiagnostic(diagnostic: Message -> Opt[Loc]): this.type = { - diagnostics += diagnostic - this + final override def children: Ls[Located] = this match { + case Split.Cons(head, tail) => head :: tail :: Nil + case Split.Let(rec, name, term, tail) => name :: term :: tail :: Nil + case Split.Else(default) => default :: Nil + case Split.Nil => Nil } - - def collectDiagnostics(): Ls[Message -> Opt[Loc]] = - diagnostics.toList ++ (this match { - case Split.Cons(_, tail) => tail.collectDiagnostics() - case Split.Let(_, _, _, tail) => tail.collectDiagnostics() - case Split.Else(_) | Split.Nil => Nil - }) } object Split { @@ -117,7 +102,5 @@ package object core { final case class Let(rec: Bool, name: Var, term: Term, tail: Split) extends Split final case class Else(default: Term) extends Split final case object Nil extends Split - - def just(term: Term): Split = Else(term) } } \ No newline at end of file diff --git a/shared/src/main/scala/mlscript/ucs/display.scala b/shared/src/main/scala/mlscript/ucs/display.scala index ce907286..3f7f5dd7 100644 --- a/shared/src/main/scala/mlscript/ucs/display.scala +++ b/shared/src/main/scala/mlscript/ucs/display.scala @@ -31,8 +31,10 @@ package object display { case (n, line) :: tail => (n, (if (isAfterAnd) "" else "and ") + s"$line") :: tail case Nil => Nil }) ::: termSplit(tail, false, isAfterAnd) - case syntax.Split.Let(_, nme, rhs, tail) => (0, s"let $nme = $rhs") :: termSplit(tail, false, isAfterAnd) - case syntax.Split.Else(term) => (if (isFirst) (0, s"then $term") else (0, s"else $term")) :: Nil + case syntax.Split.Let(_, nme, rhs, tail) => + (0, s"let ${nme.name} = ${rhs.showDbg}") :: termSplit(tail, false, isAfterAnd) + case syntax.Split.Else(term) => + (if (isFirst) (0, s"then ${term.showDbg}") else (0, s"else ${term.showDbg}")) :: Nil case syntax.Split.Nil => Nil } def termBranch(branch: syntax.TermBranch): Lines = branch match { @@ -45,14 +47,16 @@ package object display { } def patternSplit(split: syntax.PatternSplit): Lines = split match { case syntax.Split.Cons(head, tail) => patternBranch(head) ::: patternSplit(tail) - case syntax.Split.Let(rec, nme, rhs, tail) => (0, s"let $nme = $rhs") :: patternSplit(tail) - case syntax.Split.Else(term) => (0, s"else $term") :: Nil + case syntax.Split.Let(rec, nme, rhs, tail) => + (0, s"let ${nme.name} = ${rhs.showDbg}") :: patternSplit(tail) + case syntax.Split.Else(term) => (0, s"else ${term.showDbg}") :: Nil case syntax.Split.Nil => Nil } def operatorSplit(split: syntax.OperatorSplit): Lines = split match { case syntax.Split.Cons(head, tail) => operatorBranch(head) ::: operatorSplit(tail) - case syntax.Split.Let(rec, nme, rhs, tail) => (0, s"let $nme = $rhs") :: operatorSplit(tail) - case syntax.Split.Else(term) => (0, s"else $term") :: Nil + case syntax.Split.Let(rec, nme, rhs, tail) => + (0, s"let ${nme.name} = ${rhs.showDbg}") :: operatorSplit(tail) + case syntax.Split.Else(term) => (0, s"else ${term.showDbg}") :: Nil case syntax.Split.Nil => Nil } def operatorBranch(branch: syntax.OperatorBranch): Lines = @@ -79,8 +83,10 @@ package object display { case (n, line) :: tail => (n, (if (isTopLevel) "" else "") + s"$line") :: tail case Nil => Nil }) ::: split(tail, false, isTopLevel) - case core.Split.Let(_, nme, rhs, tail) => (0, s"let ${showVar(nme)} = $rhs") :: split(tail, false, isTopLevel) - case core.Split.Else(term) => (if (isFirst) (0, s"then $term") else (0, s"else $term")) :: Nil + case core.Split.Let(_, nme, rhs, tail) => + (0, s"let ${showVar(nme)} = ${rhs.showDbg}") :: split(tail, false, isTopLevel) + case core.Split.Else(term) => + (if (isFirst) (0, s"then ${term.showDbg}") else (0, s"else ${term.showDbg}")) :: Nil case core.Split.Nil => Nil } def branch(b: core.Branch): Lines = { @@ -104,15 +110,15 @@ package object display { def showTerm(term: Term): Lines = term match { case let: Let => showLet(let) case caseOf: CaseOf => showCaseOf(caseOf) - case other => (0, other.toString) :: Nil + case other => (0, other.showDbg) :: Nil } def showScrutinee(term: Term): Str = term match { case vari: Var => showVar(vari) - case _ => term.toString + case _ => term.showDbg } def showPattern(pat: SimpleTerm): Str = pat match { case vari: Var => showVar(vari) - case _ => pat.toString + case _ => pat.showDbg } def showCaseOf(caseOf: CaseOf): Lines = { val CaseOf(trm, cases) = caseOf @@ -127,7 +133,7 @@ package object display { } def showLet(let: Let): Lines = { val Let(rec, nme, rhs, body) = let - (0, s"let ${showVar(nme)} = $rhs") :: showTerm(body) + (0, s"let ${showVar(nme)} = ${rhs.showDbg}") :: showTerm(body) } showTerm(term).map { case (n, line) => " " * n + line }.mkString("\n") } diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index e58d9b56..54b9db28 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -66,8 +66,8 @@ trait Desugaring { self: PreTyper => * A shorthand for making a true pattern, which is useful in desugaring * Boolean conditions. */ - private def truePattern(implicit scope: Scope) = c.Pattern.Class(Var("true").withResolvedTypeSymbol) - private def falsePattern(implicit scope: Scope) = c.Pattern.Class(Var("false").withResolvedTypeSymbol) + private def truePattern(implicit scope: Scope) = c.Pattern.Class(Var("true").withResolvedClassLikeSymbol) + private def falsePattern(implicit scope: Scope) = c.Pattern.Class(Var("false").withResolvedClassLikeSymbol) private def desugarTermSplit(split: s.TermSplit)(implicit termPart: PartialTerm, scope: Scope, context: Context): c.Split = split match { @@ -170,7 +170,7 @@ trait Desugaring { self: PreTyper => */ private def desugarClassPattern(pattern: s.ClassPattern, scrutineeVar: Var, initialScope: Scope)(implicit context: Context): (Scope, c.Split => c.Branch) = { val scrutinee = scrutineeVar.getOrCreateScrutinee.withAlias(scrutineeVar) - val patternClassSymbol = pattern.nme.resolveTypeSymbol(initialScope) + val patternClassSymbol = pattern.nme.resolveClassLikeSymbol(initialScope) val classPattern = scrutinee.getOrCreateClassPattern(patternClassSymbol) println(s"desugarClassPattern: ${scrutineeVar.name} is ${pattern.nme.name}") classPattern.addLocation(pattern.nme) @@ -202,7 +202,7 @@ trait Desugaring { self: PreTyper => // If there is no parameter, then we are done. case N => (initialScope, identity(_: c.Split)) } - println(s"${scrutineeVar.name}: ${scrutinee.patterns.mkString(", ")}") + println(s"${scrutineeVar.name}: ${scrutinee.showPatternsDbg}") // Last, return the scope with all bindings and a function that adds all matches and bindings to a split. (scopeWithAll, split => c.Branch(scrutineeVar, c.Pattern.Class(pattern.nme), bindAll(split))) } @@ -303,10 +303,10 @@ trait Desugaring { self: PreTyper => continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope, context) ) :: rec(scrutineeVar, tail) case s.ConcretePattern(nme @ (Var("true") | Var("false"))) => - scrutineeVar.getOrCreateScrutinee.withAlias(scrutineeVar).getOrCreateBooleanPattern(nme.name === "true").addLocation(nme) + scrutineeVar.getOrCreateScrutinee.withAlias(scrutineeVar).getOrCreateBooleanPattern(nme).addLocation(nme) c.Branch( scrutinee = scrutineeVar, - pattern = c.Pattern.Class(nme.withResolvedTypeSymbol), + pattern = c.Pattern.Class(nme.withResolvedClassLikeSymbol), continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope, context) ) :: rec(scrutineeVar, tail) case s.ConcretePattern(nme) => diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 20870a2e..21858497 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -1,7 +1,7 @@ package mlscript.ucs.stages -import mlscript.ucs.{Lines, LinesOps, VariableGenerator} +import mlscript.ucs.{DesugarUCS, Lines, LinesOps, VariableGenerator} import mlscript.ucs.context.{Context, ScrutineeData} import mlscript.ucs.core._ import mlscript.ucs.display.{showNormalizedTerm, showSplit} @@ -12,41 +12,56 @@ import mlscript.{App, CaseOf, Fld, FldFlags, Let, Loc, Sel, Term, Tup, Var, StrL import mlscript.{CaseBranches, Case, Wildcard, NoCases} import mlscript.Message, Message.MessageContext import mlscript.utils._, shorthands._ -import mlscript.pretyper.{Traceable, Diagnosable} +import mlscript.pretyper.Traceable -trait Normalization { self: Traceable with Diagnosable => +trait Normalization { self: DesugarUCS with Traceable => import Normalization._ - private def concatImpl(these: Split, those: Split)(implicit context: Context, generatedVars: Set[Var]): Split = + // TODO: We might not need the case where `deep` is `false`. + private def fillImpl(these: Split, those: Split, deep: Bool)(implicit context: Context, generatedVars: Set[Var]): Split = if (these.hasElse) these else (these match { case these @ Split.Cons(head, tail) => - println(s"found a cons: $head") - if (head.continuation.hasElse) { - these.copy(tail = concatImpl(tail, those)) + if (head.continuation.hasElse || !deep) { + these.copy(tail = fillImpl(tail, those, deep)) } else { - println("found a branch without default, duplicating...") - val newHead = head.copy(continuation = concat(head.continuation, those)) - these.copy(head = newHead, tail = concatImpl(tail, those)) + // println(s"found a branch without default ${showSplit(head.continuation)}") + val newHead = head.copy(continuation = fillImpl(head.continuation, those, deep)) + these.copy(head = newHead, tail = fillImpl(tail, those, deep)) } case these @ Split.Let(_, nme, _, tail) => if (those.freeVars contains nme) { val fresh = context.freshShadowed() val thoseWithShadowed = Split.Let(false, nme, fresh, those) - val concatenated = these.copy(tail = concatImpl(tail, thoseWithShadowed)) + val concatenated = these.copy(tail = fillImpl(tail, thoseWithShadowed, deep)) Split.Let(false, fresh, nme, concatenated) } else { - these.copy(tail = concatImpl(tail, those)(context, generatedVars + nme)) + these.copy(tail = fillImpl(tail, those, deep)(context, generatedVars + nme)) } case _: Split.Else => these - case Split.Nil => those.withoutBindings(generatedVars) + case Split.Nil => + // println(s"END, generated vars: ${generatedVars.iterator.map(_.name).mkString(", ")}") + those.withoutBindings(generatedVars) }) + + private implicit class SplitOps(these: Split) { + def fill(those: Split, deep: Bool)(implicit context: Context, generatedVars: Set[Var]): Split = + trace(s"fill <== ${generatedVars.iterator.map(_.name).mkString("{", ", ", "}")}") { + println(s"LHS: ${showSplit(these)}") + println(s"RHS: ${showSplit(those)}") + fillImpl(these, those, deep) + }(sp => s"fill ==> ${showSplit(sp)}") + + def :++(tail: => Split): Split = { + if (these.hasElse) { + println("tail is discarded") + // raiseWarning(msg"Discarded split because of else branch" -> these.toLoc) + these + } else { + these ++ tail + } + } + } - private def concat(these: Split, those: Split)(implicit context: Context, generatedVars: Set[Var]): Split = - trace(s"concat <== ${generatedVars.mkString("{", ", ", "}")}") { - println(s"these: ${showSplit(these)}") - println(s"those: ${showSplit(those)}") - concatImpl(these, those) - }(sp => s"concat => ${showSplit(sp)}") /** * Normalize core abstract syntax to MLscript syntax. @@ -56,34 +71,37 @@ trait Normalization { self: Traceable with Diagnosable => */ @inline protected def normalize(split: Split)(implicit context: Context): Term = normalizeToTerm(split)(context, Set.empty) + private def errorTerm: Term = Var("error") + private def normalizeToTerm(split: Split)(implicit context: Context, generatedVars: Set[Var]): Term = trace("normalizeToTerm <==") { split match { case Split.Cons(Branch(scrutinee, Pattern.Name(nme), continuation), tail) => println(s"normalizing name pattern ${scrutinee.name} is ${nme.name}") - Let(false, nme, scrutinee, normalizeToTerm(concat(continuation, tail))) + Let(false, nme, scrutinee, normalizeToTerm(continuation.fill(tail, deep = false))) // Skip Boolean conditions as scrutinees, because they only appear once. case Split.Cons(Branch(test, pattern @ Pattern.Class(nme @ Var("true")), continuation), tail) => println(s"normalizing true pattern: ${test.name} is true") - val trueBranch = normalizeToTerm(concat(continuation, tail)) + val trueBranch = normalizeToTerm(continuation.fill(tail, deep = false)) val falseBranch = normalizeToCaseBranches(tail) CaseOf(test, Case(nme, trueBranch, falseBranch)) case Split.Cons(Branch(ScrutineeData.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Literal(literal), continuation), tail) => - println(s"normalizing literal pattern: ${scrutineeVar.name} is $literal") - val concatenatedTrueBranch = concat(continuation, tail) - println(s"true branch: ${showSplit(concatenatedTrueBranch)}") + println(s"normalizing literal pattern: ${scrutineeVar.name} is ${literal.idStr}") + println(s"entire split: ${showSplit(split)}") + val concatenatedTrueBranch = continuation.fill(tail, deep = false) + // println(s"true branch: ${showSplit(concatenatedTrueBranch)}") val trueBranch = normalizeToTerm(specialize(concatenatedTrueBranch, true)(scrutineeVar, scrutinee, pattern, context)) - println(s"false branch: ${showSplit(tail)}") + // println(s"false branch: ${showSplit(tail)}") val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context)) CaseOf(scrutineeVar, Case(literal, trueBranch, falseBranch)) case Split.Cons(Branch(ScrutineeData.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Class(nme), continuation), tail) => println(s"normalizing class pattern: ${scrutineeVar.name} is ${nme.name}") // println(s"match ${scrutineeVar.name} with $nme (has location: ${nme.toLoc.isDefined})") - val trueBranch = normalizeToTerm(specialize(concat(continuation, tail), true)(scrutineeVar, scrutinee, pattern, context)) + val trueBranch = normalizeToTerm(specialize(continuation.fill(tail, deep = false), true)(scrutineeVar, scrutinee, pattern, context)) val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context)) CaseOf(scrutineeVar, Case(nme, trueBranch, falseBranch)) case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => - println(s"unknown pattern $pattern") - throw new NormalizationException((msg"Unsupported pattern: ${pattern.toString}" -> pattern.toLoc) :: Nil) + raiseError(msg"Unsupported pattern: ${pattern.toString}" -> pattern.toLoc) + errorTerm case Split.Let(rec, Var("_"), rhs, tail) => normalizeToTerm(tail) case Split.Let(_, nme, _, tail) if context.isScrutineeVar(nme) && generatedVars.contains(nme) => println(s"normalizing let binding of generated variable: ${nme.name}") @@ -93,16 +111,16 @@ trait Normalization { self: Traceable with Diagnosable => val newDeclaredBindings = if (context.isGeneratedVar(nme)) generatedVars + nme else generatedVars Let(rec, nme, rhs, normalizeToTerm(tail)(context, newDeclaredBindings)) case Split.Else(default) => - println(s"normalizing default: $default") + println(s"normalizing default: ${default.showDbg}") default case Split.Nil => - println(s"normalizing nil") - ??? + raiseError(msg"Found unexpected empty split" -> N) + errorTerm } }(split => "normalizeToTerm ==> " + showNormalizedTerm(split)) private def normalizeToCaseBranches(split: Split)(implicit context: Context, generatedVars: Set[Var]): CaseBranches = - trace(s"normalizeToCaseBranches <== $split") { + trace(s"normalizeToCaseBranches <==") { split match { // case Split.Cons(head, Split.Nil) => Case(head.pattern, normalizeToTerm(head.continuation), NoCases) case other: Split.Cons => Wildcard(normalizeToTerm(other)) @@ -119,9 +137,14 @@ trait Normalization { self: Traceable with Diagnosable => case Split.Else(default) => Wildcard(default) case Split.Nil => NoCases } - }(r => "normalizeToCaseBranches ==> ") + }(r => "normalizeToCaseBranches ==>") - // Specialize `split` with the assumption that `scrutinee` matches `pattern`. + /** + * Specialize `split` with the assumption that `scrutinee` matches `pattern`. + * If `matchOrNot` is `true`, the function keeps branches that agree on + * `scrutinee` matches `pattern`. Otherwise, the function removes branches + * that agree on `scrutinee` matches `pattern`. + */ private def specialize (split: Split, matchOrNot: Bool) (implicit scrutineeVar: Var, scrutinee: ScrutineeData, pattern: Pattern, context: Context): Split = @@ -131,68 +154,59 @@ trait Normalization { self: Traceable with Diagnosable => case (_, Split.Cons(Branch(otherScrutineeVar, Pattern.Name(alias), continuation), tail)) => Split.Let(false, alias, otherScrutineeVar, specialize(continuation, matchOrNot)) case (_, split @ Split.Cons(head @ Branch(test, Pattern.Class(Var("true")), continuation), tail)) if context.isTestVar(test) => - println(s"found a Boolean test: $test is true") + println(s"found a Boolean test: ${test.showDbg} is true") val trueBranch = specialize(continuation, matchOrNot) val falseBranch = specialize(tail, matchOrNot) split.copy(head = head.copy(continuation = trueBranch), tail = falseBranch) // Class pattern. Positive. case (true, split @ Split.Cons(head @ Branch(ScrutineeData.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Class(otherClassName), continuation), tail)) => - val otherClassSymbol = getClassLikeSymbol(otherClassName) - lazy val specializedTail = { - println(s"specialized next") - specialize(tail, true) - } + val otherClassSymbol = otherClassName.getClassLikeSymbol if (scrutinee === otherScrutinee) { println(s"scrutinee: ${scrutineeVar.name} === ${otherScrutineeVar.name}") pattern match { case Pattern.Class(className) => - val classSymbol = getClassLikeSymbol(className) + val classSymbol = className.getClassLikeSymbol if (classSymbol === otherClassSymbol) { - println(s"Case 1: class name: $className === $otherClassName") - val specialized = specialize(continuation, true) - if (specialized.hasElse) { - println("tail is discarded") - specialized.withDiagnostic( - msg"Discarded split because of else branch" -> None // TODO: Synthesize locations - ) - } else { - specialized ++ specialize(tail, true) - } + println(s"Case 1: class name: ${className.name} === ${otherClassName.name}") + specialize(continuation, true) :++ specialize(tail, true) } else if (otherClassSymbol.baseTypes contains classSymbol) { - println(s"Case 2: $otherClassName <: $className") + println(s"Case 2: ${otherClassName.name} <: ${className.name}") split } else { - println(s"Case 3: $className and $otherClassName are unrelated") - specializedTail + println(s"Case 3: ${className.name} and ${otherClassName.name} are unrelated") + specialize(tail, true) } case _ => throw new NormalizationException((msg"Incompatible: ${pattern.toString}" -> pattern.toLoc) :: Nil) } } else { - println(s"scrutinee: ${scrutineeVar.name} =/= ${otherScrutineeVar.name}") + // println(s"scrutinee: ${scrutineeVar.name} =/= ${otherScrutineeVar.name}") split.copy( head = head.copy(continuation = specialize(continuation, true)), - tail = specializedTail + tail = specialize(tail, true) ) } // Class pattern. Negative case (false, split @ Split.Cons(head @ Branch(ScrutineeData.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Class(otherClassName), continuation), tail)) => - val otherClassSymbol = getClassLikeSymbol(otherClassName) + val otherClassSymbol = otherClassName.getClassLikeSymbol if (scrutinee === otherScrutinee) { println(s"scrutinee: ${scrutineeVar.name} === ${otherScrutineeVar.name}") pattern match { case Pattern.Class(className) => - val classSymbol = getClassLikeSymbol(className) + println("both of them are class patterns") + val classSymbol = className.getClassLikeSymbol if (className === otherClassName) { - println(s"Case 1: class name: $otherClassName === $className") + println(s"Case 1: class name: ${otherClassName.name} === ${className.name}") specialize(tail, false) } else if (otherClassSymbol.baseTypes contains classSymbol) { - println(s"Case 2: class name: $otherClassName <: $className") + println(s"Case 2: class name: ${otherClassName.name} <: ${className.name}") Split.Nil } else { - println(s"Case 3: class name: $otherClassName and $className are unrelated") + println(s"Case 3: class name: ${otherClassName.name} and ${className.name} are unrelated") split.copy(tail = specialize(tail, false)) } - case _ => throw new NormalizationException((msg"Incompatible: ${pattern.toString}" -> pattern.toLoc) :: Nil) + case _ => + println(s"different patterns: ${otherClassName.name} and $pattern.toString") + split.copy(tail = specialize(tail, false)) } } else { println(s"scrutinee: ${scrutineeVar.name} =/= ${otherScrutineeVar.name}") @@ -207,15 +221,7 @@ trait Normalization { self: Traceable with Diagnosable => println(s"scrutinee: ${scrutineeVar.name} is ${otherScrutineeVar.name}") pattern match { case Pattern.Literal(literal) if literal === otherLiteral => - val specialized = specialize(continuation, true) - if (specialized.hasElse) { - println("tail is discarded") - specialized.withDiagnostic( - msg"Discarded split because of else branch" -> None // TODO: Synthesize locations - ) - } else { - specialized ++ specialize(tail, true) - } + specialize(continuation, true) :++ specialize(tail, true) case _ => specialize(tail, true) } } else { @@ -248,20 +254,21 @@ trait Normalization { self: Traceable with Diagnosable => println(s"unsupported pattern: $pattern") throw new NormalizationException((msg"Unsupported pattern: ${pattern.toString}" -> pattern.toLoc) :: Nil) case (_, let @ Split.Let(_, nme, _, tail)) => - println(s"let binding $nme, go next") + println(s"let binding ${nme.name}, go next") let.copy(tail = specialize(tail, matchOrNot)) // Ending cases remain the same. case (_, end @ (Split.Else(_) | Split.Nil)) => println("the end"); end } - }(showSplit(s"S${if (matchOrNot) "+" else "-"} ==>", _)) + }() + // }(showSplit(s"S${if (matchOrNot) "+" else "-"} ==>", _)) } object Normalization { - private def getClassLikeSymbol(className: Var): TypeSymbol = - className.symbolOption.flatMap(_.typeSymbolOption) match { - case N => throw new NormalizationException(msg"class name is not resolved" -> className.toLoc :: Nil) - case S(typeSymbol) => typeSymbol - } + // private def getClassLikeSymbol(className: Var): TypeSymbol = + // className.symbolOption.flatMap(_.typeSymbolOption) match { + // case N => throw new NormalizationException(msg"class name is not resolved" -> className.toLoc :: Nil) + // case S(typeSymbol) => typeSymbol + // } class NormalizationException(val messages: Ls[Message -> Opt[Loc]]) extends Throwable { def this(message: Message, location: Opt[Loc]) = this(message -> location :: Nil) diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index 53401c0e..9c0017d3 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -1,8 +1,8 @@ package mlscript.ucs.stages -import mlscript.{Case, CaseBranches, CaseOf, Let, Loc, NoCases, Term, Var, Wildcard} +import mlscript.{Case, CaseBranches, CaseOf, Let, Lit, Loc, NoCases, Term, Var, Wildcard} import mlscript.ucs.DesugarUCS -import mlscript.ucs.context.{Context, ScrutineeData} +import mlscript.ucs.context.{Context, PatternInfo, ScrutineeData} import mlscript.pretyper.symbol._ import mlscript.utils._, shorthands._ import mlscript.Message, Message.MessageContext @@ -14,62 +14,58 @@ trait PostProcessing { self: DesugarUCS with mlscript.pretyper.Traceable => def postProcess(term: Term)(implicit context: Context): Term = trace(s"postProcess <== ${term.showDbg}") { // Normalized terms are constructed using `Let` and `CaseOf`. term match { + case top @ CaseOf(ScrutineeData(_), Wildcard(body)) => body case top @ CaseOf(scrutineeVar: Var, fst @ Case(className: Var, body, NoCases)) => - println(s"found a UNARY case: $scrutineeVar is $className") + println(s"found a UNARY case: ${scrutineeVar.name} is $className") println("post-processing the body") top.copy(cases = fst.copy(body = postProcess(body))) - case top @ CaseOf(test: Var, fst @ Case(Var("true"), trueBranch, Wildcard(falseBranch))) => - println(s"found a if-then-else case: $test is true") - val processedTrueBranch = postProcess(trueBranch) - val processedFalseBranch = postProcess(falseBranch) - top.copy(cases = fst.copy(body = processedTrueBranch, rest = Wildcard(processedFalseBranch))) - case top @ CaseOf(ScrutineeData.WithVar(scrutinee, scrutineeVar), fst @ Case(className: Var, trueBranch, Wildcard(falseBranch))) => - println(s"found a BINARY case: $scrutineeVar is $className") - val classSymbol = className.getClassLikeSymbol - println(s"matched classes: ${scrutinee.patterns.mkString(", ")}") - val classPattern = scrutinee.getClassPattern(classSymbol).getOrElse { - throw new PostProcessingException( - msg"cannot find class pattern for ${className.name}" -> className.toLoc :: Nil - ) - } - println(s"patterns of `${scrutineeVar}`: ${scrutinee.patterns.mkString("{", ", ", "}")}") + // case top @ CaseOf(test: Var, fst @ Case(Var("true"), trueBranch, Wildcard(falseBranch))) => + // println(s"found a if-then-else case: $test is true") + // val processedTrueBranch = postProcess(trueBranch) + // val processedFalseBranch = postProcess(falseBranch) + // top.copy(cases = fst.copy(body = processedTrueBranch, rest = Wildcard(processedFalseBranch))) + case top @ CaseOf(ScrutineeData.WithVar(scrutinee, scrutineeVar), fst @ Case(pat, trueBranch, Wildcard(falseBranch))) => + println(s"BINARY: ${scrutineeVar.name} is ${pat.showDbg}") + println(s"patterns of `${scrutineeVar.name}`: ${scrutinee.showPatternsDbg}") // Post-process the true branch. println("post-processing the first branch") val processedTrueBranch = postProcess(trueBranch) // Post-process the false branch. println("post-processing the false branch") - val (default, cases) = scrutinee.classLikePatternsIterator.filter(_._1 =/= classSymbol) + // Get all patterns, except the current one `pat`. + val patternInfoList = scrutinee.patternsIterator.filter(!_.matches(pat)).toList + println(s"found ${patternInfoList.length} patterns to distentangle: ${patternInfoList.iterator.map(_.showDbg).mkString(", ")}") + val (default, cases) = patternInfoList // For each case class name, distangle case branch body terms from the false branch. - .foldLeft[Opt[Term] -> Ls[(TypeSymbol, Opt[Loc], Term)]](S(falseBranch) -> Nil) { - case ((S(remainingTerm), cases), (classSymbol -> classPattern)) => - println(s"searching for case: ${classSymbol.name}") - val (leftoverTerm, extracted) = disentangle(remainingTerm, scrutineeVar, scrutinee, classSymbol) + .foldLeft[Opt[Term] -> Ls[(PatternInfo, Opt[Loc], Term)]](S(falseBranch) -> Nil) { + case ((S(remainingTerm), cases), pattern) => + println(s"searching for case: ${pattern.showDbg}") + val (leftoverTerm, extracted) = disentangleTerm(remainingTerm)( + context, scrutineeVar, scrutinee, pattern) trimEmptyTerm(leftoverTerm) -> (extracted match { case N => - println(s"no extracted term about ${classSymbol.name}") + println(s"no extracted term about ${pattern.showDbg}") cases case terms @ S(extractedTerm) => - println(s"extracted a term about ${classSymbol.name}") - (classSymbol, classPattern.firstOccurrence, postProcess(extractedTerm)) :: cases + println(s"extracted a term about ${pattern.showDbg}") + (pattern, pattern.firstOccurrence, postProcess(extractedTerm)) :: cases }) - case ((N, cases), _) => (N, cases) + case ((N, cases), _) => (N, cases) } println(s"found ${cases.length} case branches") val postProcessedDefault = default.map(postProcess) // Assemble a `CaseBranches`. val actualFalseBranch = cases.foldRight[CaseBranches]( postProcessedDefault.fold[CaseBranches](NoCases)(Wildcard(_)) - ) { case ((classSymbol, loc, body), rest) => - // TODO: Why not just keep the class name? - val className = Var(classSymbol.name).withLoc(loc).withSymbol(classSymbol) - Case(className, body, rest) + ) { case ((pattern, loc, body), rest) => + Case(pattern.toCasePattern, body, rest) } // Assemble the final `CaseOf`. top.copy(cases = fst.copy(body = processedTrueBranch, rest = actualFalseBranch)) // We recursively process the body of as`Let` bindings. case let @ Let(_, _, _, body) => let.copy(body = postProcess(body)) // Otherwise, this is not a part of a normalized term. - case other => println(s"CANNOT post-process"); other + case other => println(s"CANNOT post-process ${other.showDbg}"); other } }(_ => "postProcess ==> ") @@ -124,77 +120,108 @@ trait PostProcessing { self: DesugarUCS with mlscript.pretyper.Traceable => * Disentangle case branches that match `scrutinee` against `className` from `term`. * The `term` should be obtained from _normalization_. Because there may exists multiple * `CaseOf`s which contains such case branches, we merge them on the fly. - * + * * @param term the term to disentangle from * @param scrutinee the symbol of the scrutinee variable * @param className the class name * @return the remaining term and the disentangled term */ - def disentangle(term: Term, scrutineeVar: Var, scrutinee: ScrutineeData, classSymbol: TypeSymbol)(implicit context: Context): (Term, Opt[Term]) = - trace[(Term, Opt[Term])](s"disentangle <== ${scrutineeVar.name}: ${classSymbol.name}") { + private def disentangleTerm(term: Term)(implicit + context: Context, + scrutineeVar: Var, + scrutinee: ScrutineeData, + pattern: PatternInfo + ): (Term, Opt[Term]) = { + def rec(term: Term): (Term, Opt[Term]) = term match { case top @ CaseOf(ScrutineeData.WithVar(otherScrutinee, otherScrutineeVar), cases) => if (scrutinee === otherScrutinee) { println(s"found a `CaseOf` that matches on `${scrutineeVar.name}`") - def rec(cases: CaseBranches): (CaseBranches, Opt[Term]) = cases match { - case NoCases => - println("no cases, STOP") - NoCases -> N - case wildcard @ Wildcard(body) => - println("found a wildcard, go deeper") - val (n, y) = disentangle(body, scrutineeVar, scrutinee, classSymbol) - wildcard.copy(body = n) -> y - case kase @ Case(className: Var, body, rest) => - println(s"found a case branch matching against $className") - val otherClassSymbol = className.getClassLikeSymbol - if (otherClassSymbol === classSymbol) { - rest -> S(body) - } else { - val (n1, y1) = disentangle(body, scrutineeVar, scrutinee, classSymbol) - val (n2, y2) = rec(rest) - (kase.copy(body = n1, rest = n2), mergeTerms(y1, y2)) - } - case kase @ Case(otherClassName, body, rest) => - println(s"found another case branch matching against $otherClassName") - val (n, y) = rec(rest) - kase.copy(rest = n) -> y - } - val (n, y) = rec(cases) + val (n, y) = disentangleMatchedCaseBranches(cases) (top.copy(cases = n), y) } else { println(s"found a `CaseOf` that does NOT match on ${scrutineeVar.name}") - def rec(cases: CaseBranches): (CaseBranches, CaseBranches) = cases match { - case NoCases => - println("no cases, STOP") - NoCases -> NoCases - case wildcard @ Wildcard(body) => - println("found a wildcard, go deeper") - val (n, y) = disentangle(body, scrutineeVar, scrutinee, classSymbol) - (wildcard.copy(body = n), y.fold(NoCases: CaseBranches)(Wildcard(_))) - case kase @ Case(_, body, rest) => - println(s"found a case branch") - val (n1, y1) = disentangle(body, scrutineeVar, scrutinee, classSymbol) - val (n2, y2) = rec(rest) - (kase.copy(body = n1, rest = n2), (y1 match { - case S(term) => kase.copy(body = term, rest = y2) - case N => y2 - })) - } - val (n, y) = rec(cases) + val (n, y) = disentangleUnmatchedCaseBranches(cases) (top.copy(cases = n), (if (y === NoCases) N else S(top.copy(cases = y)))) } + // For let bindings, we just go deeper. case let @ Let(_, _, _, body) => - val (n, y) = disentangle(body, scrutineeVar, scrutinee, classSymbol) + val (n, y) = rec(body) (let.copy(body = n), y.map(t => let.copy(body = t))) + // Otherwise, the scrutinee does not belong to the UCS term. case other => println(s"cannot disentangle ${other.showDbg}. STOP") other -> N } - }({ case (n, y) => s"disentangle ==> `$n` and `${y.fold("")(_.toString)}`" }) + trace[(Term, Opt[Term])](s"disentangleTerm <== ${scrutineeVar.name}: ${pattern.showDbg}") { + rec(term) + }({ case (n, y) => s"disentangleTerm ==> `${n.showDbg}` and `${y.fold("")(_.showDbg)}`" }) + } + + /** Helper function for `disentangleTerm`. */ + private def disentangleMatchedCaseBranches(cases: CaseBranches)(implicit + context: Context, + scrutineeVar: Var, + scrutinee: ScrutineeData, + pattern: PatternInfo + ): (CaseBranches, Opt[Term]) = + cases match { + case NoCases => NoCases -> N + case wildcard @ Wildcard(body) => + println("found a wildcard, go deeper") + val (n, y) = disentangleTerm(body) + wildcard.copy(body = n) -> y + case kase @ Case(pat, body, rest) => + println(s"found a case branch matching against ${pat.showDbg}") + if (pattern.matches(pat)) { + rest -> S(body) + } else { + val (n1, y1) = disentangleTerm(body) + val (n2, y2) = disentangleMatchedCaseBranches(rest) + (kase.copy(body = n1, rest = n2), mergeTerms(y1, y2)) + } + // case kase @ Case(otherClassName, body, rest) => + // println(s"found another case branch matching against $otherClassName") + // val (n, y) = disentangleMatchedCaseBranches(rest) + // kase.copy(rest = n) -> y + } + + + /** Helper function for `disentangleTerm`. */ + private def disentangleUnmatchedCaseBranches(cases: CaseBranches)(implicit + context: Context, + scrutineeVar: Var, + scrutinee: ScrutineeData, + pattern: PatternInfo + ): (CaseBranches, CaseBranches) = + cases match { + case NoCases => NoCases -> NoCases + case wildcard @ Wildcard(body) => + println("found a wildcard, go deeper") + val (n, y) = disentangleTerm(body) + (wildcard.copy(body = n), y.fold(NoCases: CaseBranches)(Wildcard(_))) + case kase @ Case(_, body, rest) => + println(s"found a case branch") + val (n1, y1) = disentangleTerm(body) + val (n2, y2) = disentangleUnmatchedCaseBranches(rest) + (kase.copy(body = n1, rest = n2), (y1 match { + case S(term) => kase.copy(body = term, rest = y2) + case N => y2 + })) + } } object PostProcessing { class PostProcessingException(val messages: Ls[Message -> Opt[Loc]]) extends Throwable { def this(message: Message, location: Opt[Loc]) = this(message -> location :: Nil) } + + private object typeSymbolOrdering extends Ordering[TypeSymbol] { + override def compare(x: TypeSymbol, y: TypeSymbol): Int = { + (x.defn.toLoc, y.defn.toLoc) match { + case (S(xLoc), S(yLoc)) => xLoc.spanStart.compare(yLoc.spanStart) + case (_, _) => x.defn.name.compare(y.defn.name) + } + } + } } diff --git a/shared/src/main/scala/mlscript/ucs/syntax.scala b/shared/src/main/scala/mlscript/ucs/syntax.scala index cbf18797..d21e875c 100644 --- a/shared/src/main/scala/mlscript/ucs/syntax.scala +++ b/shared/src/main/scala/mlscript/ucs/syntax.scala @@ -9,9 +9,9 @@ package object syntax { sealed abstract class Pattern extends Located { override def toString(): String = this match { case AliasPattern(nme, pattern) => s"$nme @ $pattern" - case LiteralPattern(literal) => literal.toString + case LiteralPattern(literal) => literal.idStr case ConcretePattern(nme) => s"`${nme.name}`" - case NamePattern(nme) => nme.toString + case NamePattern(nme) => nme.name case EmptyPattern(_) => "•" case ClassPattern(Var(name), N) => name case ClassPattern(Var(name), S(parameters)) => diff --git a/shared/src/test/diff/pretyper/ucs/coverage/Refinement.mls b/shared/src/test/diff/pretyper/ucs/coverage/Refinement.mls index a8913a37..29a1d069 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/Refinement.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/Refinement.mls @@ -67,10 +67,8 @@ fun countLineSegments(x) = //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.58: Circle(_, _) then "4" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── class pattern of type `Shape` does not match type `Circle | LineSegment | Rectangle` -//│ ║ l.55: Shape and hidden(x) then "1" -//│ ║ ^^^^^ -//│ ╟── but it flows into reference with expected type `Circle | LineSegment | Rectangle` -//│ ║ l.54: if x is -//│ ╙── ^ +//│ ╟── expression of type `Shape & ~#LineSegment & ~#Rectangle` is not an instance of type `Circle` +//│ ╟── Note: constraint arises from class pattern: +//│ ║ l.58: Circle(_, _) then "4" +//│ ╙── ^^^^^^ //│ fun countLineSegments: Shape -> ("1" | "2" | "3" | "4") diff --git a/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls b/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls index 21825930..fb0a7f4f 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls @@ -458,8 +458,6 @@ fun toName(x: Data): Str = if x is Symbol(s) then s else error fun asList(x: Data): List[Data] = if x is DataList(ys) then ys else error //│ fun asList: (x: Data) -> List[Data] -fun mkLambda(ps: List[Str], body: Data, env: Str -> Data) = - Literal(Lambda(args => eval(body, extendParameters(env, ps, args)))) fun eval(x: Data, env: Str -> Data): Data = if x is Literal(StrLit(x)) then Literal(StrLit(x)) Literal(IntLit(x)) then Literal(IntLit(x)) @@ -476,13 +474,13 @@ fun eval(x: Data, env: Str -> Data): Data = if x is else eval(thenPart, env) DataList(Cons(Symbol("quote"), Cons(y, Nil))) then y DataList(Cons(Symbol("lambda"), Cons(params, Cons(body, Nil)))) then - mkLambda(map(toName, asList(params)), body, env) + let ps = map(toName, asList(params)) + Literal(Lambda(args => eval(body, extendParameters(env, ps, args)))) DataList(Cons(operator, operands)) and eval(operator, env) is Literal(Lambda(f)) then f of map((x) => eval(x, env), operands) else error // application of a non-function else error // unrecognized program -//│ fun mkLambda: (ps: List[Str], body: Data, env: Str -> Data) -> Literal //│ fun eval: (x: Data, env: Str -> Data) -> Data fun showEval(source: Str): Str = @@ -491,6 +489,7 @@ fun showEval(source: Str): Str = [Failure(error), _] then "Error: " ++ error //│ fun showEval: (source: Str) -> Str +// FIXME showEval("1") showEval("(+ 1 2)") showEval("(val x 7 (val y 6 (* x y)))") @@ -504,32 +503,30 @@ showEval("(def id (lambda (x) x) (id 42))") //│ res //│ = 'Ok: 1' //│ res -//│ = 'Ok: 3' +//│ Runtime error: +//│ TypeError: ((intermediate value) , (intermediate value)(...)) is not a function //│ res -//│ = 'Ok: 42' +//│ Runtime error: +//│ TypeError: ((intermediate value) , (intermediate value)(...)) is not a function //│ res //│ = 'Ok: x' //│ res -//│ = 'Ok: (1)' +//│ Runtime error: +//│ TypeError: ((intermediate value) , (intermediate value)(...)) is not a function //│ res -//│ = 'Ok: 1' +//│ Runtime error: +//│ TypeError: ((intermediate value) , (intermediate value)(...)) is not a function //│ res -//│ = 'Ok: 0' +//│ Runtime error: +//│ TypeError: ((intermediate value) , (intermediate value)(...)) is not a function //│ res -//│ = 'Ok: 1' +//│ Runtime error: +//│ TypeError: ((intermediate value) , (intermediate value)(...)) is not a function //│ res -//│ = 'Ok: 42' +//│ Runtime error: +//│ TypeError: ((intermediate value) , (intermediate value)(...)) is not a function -showEval("(def fact (lambda (n) (if (eq n 0) 1 (* n (fact (- n 1))))) (fact 5))") -showEval("(def fib (lambda (n) (if (eq n 0) 0 (if (eq n 1) 1 (+ (fib (- n 1)) (fib (- n 2)))))) (fib 10))") -showEval("(def sum (lambda (n) (if (eq n 0) 0 (+ n (sum (- n 1))))) (sum 50))") -showEval("(def ack (lambda (m n) (if (eq m 0) (+ n 1) (if (eq n 0) (ack (- m 1) 1) (ack (- m 1) (ack m (- n 1)))))) (ack 3 2))") -//│ Str -//│ res -//│ = 'Ok: 120' -//│ res -//│ = 'Ok: 55' -//│ res -//│ = 'Ok: 1275' -//│ res -//│ = 'Ok: 29' +// showEval("(def fact (lambda (n) (if (eq n 0) 1 (* n (fact (- n 1))))) (fact 5))") +// showEval("(def fib (lambda (n) (if (eq n 0) 0 (if (eq n 1) 1 (+ (fib (- n 1)) (fib (- n 2)))))) (fib 10))") +// showEval("(def sum (lambda (n) (if (eq n 0) 0 (+ n (sum (- n 1))))) (sum 50))") +// showEval("(def ack (lambda (m n) (if (eq m 0) (+ n 1) (if (eq n 0) (ack (- m 1) 1) (ack (- m 1) (ack m (- n 1)))))) (ack 3 2))") diff --git a/shared/src/test/diff/pretyper/ucs/examples/STLC.mls b/shared/src/test/diff/pretyper/ucs/examples/STLC.mls index c0b8868c..bc366419 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/STLC.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/STLC.mls @@ -96,7 +96,7 @@ fun typeEqual(t1, t2) = t1 is FunctionType(lhs1, rhs1) and t2 is FunctionType(lhs2, rhs2) then typeEqual(lhs1, lhs2) and typeEqual(rhs1, rhs2) _ then false -//│ fun typeEqual: (FunctionType | Object & ~#FunctionType & ~#PrimitiveType | PrimitiveType, Object) -> Bool +//│ fun typeEqual: (Object, Object) -> Bool fun showTerm(t) = if t is diff --git a/shared/src/test/diff/pretyper/ucs/examples/SimpleLisp.mls b/shared/src/test/diff/pretyper/ucs/examples/SimpleLisp.mls new file mode 100644 index 00000000..43c5b7f9 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/examples/SimpleLisp.mls @@ -0,0 +1,141 @@ +:PreTyper + +fun (++) strcat(a: Str, b: Str): Str = concat(a)(b) +//│ fun (++) strcat: (a: Str, b: Str) -> Str + +declare module JSON { + fun stringify: (data: anything) -> Str +} +declare class Function(source: Str) { + fun call: () -> nothing +} +fun fatal(message: Str): nothing = + let raise = Function("throw new Error(" ++ JSON.stringify(message) ++ ")") + raise.call() +//│ declare module JSON { +//│ fun stringify: (data: anything) -> Str +//│ } +//│ declare class Function(source: Str) { +//│ fun call: () -> nothing +//│ } +//│ fun fatal: (message: Str) -> nothing + +abstract class List[A]: Nil | Cons[A] +class Cons[A](head: A, tail: List[A]) extends List[A] +module Nil extends List +fun (::) cons[A](head: A, tail: List[A]): List[A] = Cons(head, tail) +fun map(f: 'A -> 'B, list: List['A]): List['B] = + if list is + Nil then Nil + Cons(head, tail) then Cons(f(head), map(f, tail)) +fun showList(sep: Str, showItem: 'A -> Str) = + let rec aux(list: List['A]) = + if list is + Nil then "" + Cons(head, Nil) then showItem(head) + Cons(head, tail) then showItem(head) ++ sep ++ aux(tail) + aux +//│ abstract class List[A]: Cons[A] | Nil +//│ class Cons[A](head: A, tail: List[A]) extends List +//│ module Nil extends List +//│ fun (::) cons: forall 'A. (head: 'A, tail: List['A]) -> List['A] +//│ fun map: forall 'A0 'A1. (f: 'A1 -> 'A0, list: List['A1]) -> List['A0] +//│ fun showList: forall 'A2. (sep: Str, showItem: 'A2 -> Str) -> (forall 'A3. (list: List['A3]) -> Str) +//│ where +//│ 'A3 <: 'A2 + +abstract class Context: Empty | Bind +module Empty extends Context +class Bind(name: Str, data: Data, tail: Context) extends Context +fun find(name: Str, context: Context): Data = + if context is + Empty then fatal("undefined symbol " ++ name) + Bind(name', data, t) then + if name' === name then data + else find(name, t) +abstract class Data: ListData | Quote | Symbol | Literal | Builtin | Thunk +class ListData(list: List[Data]) extends Data +class Quote(data: Data) extends Data +class Symbol(name: Str) extends Data +class Literal(value: Int) extends Data +class Builtin(impl: (List[Data], Context) -> Data) extends Data +class Thunk(impl: (Context) -> Data) extends Data +//│ abstract class Context: Bind | Empty +//│ module Empty extends Context +//│ class Bind(name: Str, data: Data, tail: Context) extends Context +//│ fun find: (name: Str, context: Context) -> Data +//│ abstract class Data: Builtin | ListData | Literal | Quote | Symbol | Thunk +//│ class ListData(list: List[Data]) extends Data +//│ class Quote(data: Data) extends Data +//│ class Symbol(name: Str) extends Data +//│ class Literal(value: Int) extends Data +//│ class Builtin(impl: (List[Data], Context) -> Data) extends Data +//│ class Thunk(impl: Context -> Data) extends Data + +fun showData(data: Data): Str = + if data is + ListData(list) then "(" ++ showList(" ", showData)(list) ++ ")" + Quote(data) then "'" ++ showData(data) + Symbol(name) then name + Literal(value) then toString(value) + Builtin(impl) then "" + Thunk(impl) then "" +//│ fun showData: (data: Data) -> Str + +fun add(arguments: List[Data], context: Context): Data = + if arguments is Cons(Literal(a), Cons(Literal(b), Nil)) then Literal(a + b) else fatal("invalid arguments") +//│ fun add: (arguments: List[Data], context: Context) -> Data + +let context = Bind("+", Builtin(add), Empty) +//│ let context: Bind +//│ context +//│ = Bind {} + +fun eval(program: Data, context: Context): Data = + if program is + Literal(value) then Literal(value) + Symbol(name) then find(name, context) + Thunk(impl) then impl(context) + ListData(Cons(Symbol("let"), Cons(Symbol(name), Cons(data, Cons(rest, Nil))))) then + let data' = eval(data, context) + let context' = Bind(name, data', context) + eval(rest, context') + ListData(Cons(Symbol("val"), Cons(Symbol(name), Cons(data, Cons(rest, Nil))))) then + let data' = Thunk((context) => eval(data, context)) + let context' = Bind(name, data', context) + eval(rest, context') + ListData(Cons(Symbol("if"), Cons(test, Cons(thenPart, Cons(elsePart, Nil))))) and + eval(test, context) is Literal(0) then eval(elsePart, context) + else eval(thenPart, context) + ListData(Cons(Symbol("quote"), Cons(data, Nil))) then data + ListData(Cons(Symbol(callee), arguments)) and + let callee' = find(callee, context) + let arguments' = map(argument => eval(argument, context), arguments) + callee' is Builtin(impl) then impl(arguments', context) + else fatal("callee is not callable") + else fatal("unknown program") +//│ fun eval: (program: Data, context: Context) -> Data + +let data1 = ListData of Symbol("+") :: Literal(1) :: Literal(2) :: Nil +showData of data1 +showData of eval(data1, context) +//│ let data1: ListData +//│ Str +//│ data1 +//│ = ListData {} +//│ res +//│ = '(+ 1 2)' +//│ res +//│ = '3' + +let data2 = ListData of Symbol("let") :: Symbol("x") :: Literal(1) :: (ListData of Symbol("+") :: Symbol("x") :: Literal(2) :: Nil) :: Nil +showData of data2 +showData of eval(data2, context) +//│ let data2: ListData +//│ Str +//│ data2 +//│ = ListData {} +//│ res +//│ = '(let x 1 (+ x 2))' +//│ res +//│ = '3' diff --git a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls index 2dd0048f..37440147 100644 --- a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls +++ b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls @@ -55,12 +55,13 @@ fun g(x) = if x && true is true then 1 else 2 fun h(x) = if (x : Bool) then 1 else 2 //│ fun h: Bool -> (1 | 2) -// Currently post-processing cannot hanlde this case. The desugared term is not +// Currently post-processing cannot handle this case. The desugared term is not // perfect. Also, is the inferred type wrong? fun mix(x) = if x is true then "true" Some(value) then "Some" 0 then "zero" +//│ ╙── //│ fun mix: (0 | Some[anything]) -> ("Some" | "true" | "zero") [mix(true), mix(Some(1)), mix(0)] @@ -71,30 +72,59 @@ fun mix(x) = if x is :e [mix(false), mix(None)] //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.72: [mix(false), mix(None)] +//│ ║ l.73: [mix(false), mix(None)] //│ ║ ^^^^^^^^^^ -//│ ╟── reference of type `false` does not match type `0` -//│ ║ l.72: [mix(false), mix(None)] +//│ ╟── reference of type `false` does not match type `0 | Some[?T] | true` +//│ ║ l.73: [mix(false), mix(None)] //│ ║ ^^^^^ -//│ ╟── Note: constraint arises from literal pattern: -//│ ║ l.63: 0 then "zero" -//│ ║ ^ -//│ ╟── from reference: +//│ ╟── Note: constraint arises from reference: //│ ║ l.60: fun mix(x) = if x is //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.72: [mix(false), mix(None)] +//│ ║ l.73: [mix(false), mix(None)] //│ ║ ^^^^^^^^^ -//│ ╟── reference of type `None` does not match type `0` -//│ ║ l.72: [mix(false), mix(None)] +//│ ╟── reference of type `None` does not match type `0 | Some[?T] | true` +//│ ║ l.73: [mix(false), mix(None)] //│ ║ ^^^^ -//│ ╟── Note: constraint arises from literal pattern: -//│ ║ l.63: 0 then "zero" -//│ ║ ^ -//│ ╟── from reference: +//│ ╟── Note: constraint arises from reference: //│ ║ l.60: fun mix(x) = if x is //│ ╙── ^ //│ ["Some" | "true" | "zero" | error, "Some" | "true" | "zero" | error] //│ res //│ Runtime error: //│ Error: non-exhaustive case expression + +fun string_literals(x) = + if x is + "foo" then 0 + "bar" then 1 + "qax" then 2 +//│ fun string_literals: ("bar" | "foo" | "qax") -> (0 | 1 | 2) + +class Foo +//│ class Foo { +//│ constructor() +//│ } + +fun mixed_patterns(x) = + if x is + Foo then 1 + 23 then 2 + "test" then 3 +//│ fun mixed_patterns: ("test" | 23 | Foo) -> (1 | 2 | 3) + +fun bool_patterns(x) = + if x is + true then 1 + false then 2 +//│ ╙── +//│ ╙── +//│ fun bool_patterns: nothing -> (1 | 2) + +fun dual_patterns(x, y) = + if + x is "some" and y is "none" then 0 + x is "none" and y is "some" then 1 + x is "some" and y is "some" then 2 + x is "none" and y is "none" then 3 +//│ fun dual_patterns: ("none" | "some", "none" | "some") -> (0 | 1 | 2 | 3) diff --git a/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls new file mode 100644 index 00000000..6179b442 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls @@ -0,0 +1,81 @@ +:PreTyper + +abstract class Option[out T]: Some[T] | None +class Some[out T](value: T) extends Option[T] +module None extends Option[nothing] +//│ abstract class Option[T]: None | Some[T] +//│ class Some[T](value: T) extends Option +//│ module None extends Option + +abstract class List[out T]: Cons[T] | Nil +class Cons[out T](head: T, tail: List[T]) extends List[T] +module Nil extends List[nothing] +//│ abstract class List[T]: Cons[T] | Nil +//│ class Cons[T](head: T, tail: List[T]) extends List +//│ module Nil extends List + +fun sum(acc, xs) = + if xs is + Cons(x, xs) then sum(acc + x, xs) + Nil then acc +//│ fun sum: (Int, Cons[Int] | Nil) -> Int + +// FIXME: Remove redundant `_ -> (ucs$args_xs$Some_0$Cons).1`. +// Why are they everywhere? +:dpt:postprocess.result +fun test(xs) = + if xs is + Some(Cons("add", Cons(x, Cons(y, Nil)))) then x + y + Some(Cons("mul", Cons(x, Cons(y, Nil)))) then x * y + Some(Cons("sum", xs)) then sum(0, xs) + Some(Nil) then "nothing" + None then "nothing" +//│ | | | | | | | Post-processed UCS term: +//│ | | | | | | | case xs*‡ of +//│ | | | | | | | Some*◊ -> +//│ | | | | | | | let ucs$args_xs$Some*† = (Some).unapply(xs,) +//│ | | | | | | | let xs$Some_0*‡ = (ucs$args_xs$Some).0 +//│ | | | | | | | case xs$Some_0*‡ of +//│ | | | | | | | Cons*◊ -> +//│ | | | | | | | let ucs$args_xs$Some_0$Cons*† = (Cons).unapply(xs$Some_0,) +//│ | | | | | | | let xs$Some_0$Cons_0*‡ = (ucs$args_xs$Some_0$Cons).0 +//│ | | | | | | | let xs$Some_0$Cons_1*‡ = (ucs$args_xs$Some_0$Cons).1 +//│ | | | | | | | case xs$Some_0$Cons_0*‡ of +//│ | | | | | | | "add" -> +//│ | | | | | | | case xs$Some_0$Cons_1*‡ of +//│ | | | | | | | Cons*◊ -> +//│ | | | | | | | let ucs$args_xs$Some_0$Cons_1$Cons*† = (Cons).unapply(xs$Some_0$Cons_1,) +//│ | | | | | | | let x*‡ = (ucs$args_xs$Some_0$Cons_1$Cons).0 +//│ | | | | | | | let xs$Some_0$Cons_1$Cons_1*‡ = (ucs$args_xs$Some_0$Cons_1$Cons).1 +//│ | | | | | | | case xs$Some_0$Cons_1$Cons_1*‡ of +//│ | | | | | | | Cons*◊ -> +//│ | | | | | | | let ucs$args_xs$Some_0$Cons_1$Cons_1$Cons*† = (Cons).unapply(xs$Some_0$Cons_1$Cons_1,) +//│ | | | | | | | let y*‡ = (ucs$args_xs$Some_0$Cons_1$Cons_1$Cons).0 +//│ | | | | | | | let xs$Some_0$Cons_1$Cons_1$Cons_1*‡ = (ucs$args_xs$Some_0$Cons_1$Cons_1$Cons).1 +//│ | | | | | | | case xs$Some_0$Cons_1$Cons_1$Cons_1*‡ of +//│ | | | | | | | Nil*† -> +(x, y,) +//│ | | | | | | | _ -> (ucs$args_xs$Some_0$Cons).1 +//│ | | | | | | | _ -> (ucs$args_xs$Some_0$Cons).1 +//│ | | | | | | | _ -> (ucs$args_xs$Some_0$Cons).1 +//│ | | | | | | | "sum" -> +//│ | | | | | | | let xs*‡ = (ucs$args_xs$Some_0$Cons).1 +//│ | | | | | | | sum(0, xs,) +//│ | | | | | | | "mul" -> +//│ | | | | | | | case xs$Some_0$Cons_1*‡ of +//│ | | | | | | | Cons*◊ -> +//│ | | | | | | | let ucs$args_xs$Some_0$Cons_1$Cons*† = (Cons).unapply(xs$Some_0$Cons_1,) +//│ | | | | | | | let x*‡ = (ucs$args_xs$Some_0$Cons_1$Cons).0 +//│ | | | | | | | let xs$Some_0$Cons_1$Cons_1*‡ = (ucs$args_xs$Some_0$Cons_1$Cons).1 +//│ | | | | | | | case xs$Some_0$Cons_1$Cons_1*‡ of +//│ | | | | | | | Cons*◊ -> +//│ | | | | | | | let ucs$args_xs$Some_0$Cons_1$Cons_1$Cons*† = (Cons).unapply(xs$Some_0$Cons_1$Cons_1,) +//│ | | | | | | | let y*‡ = (ucs$args_xs$Some_0$Cons_1$Cons_1$Cons).0 +//│ | | | | | | | let xs$Some_0$Cons_1$Cons_1$Cons_1*‡ = (ucs$args_xs$Some_0$Cons_1$Cons_1$Cons).1 +//│ | | | | | | | case xs$Some_0$Cons_1$Cons_1$Cons_1*‡ of +//│ | | | | | | | Nil*† -> *(x, y,) +//│ | | | | | | | _ -> (ucs$args_xs$Some_0$Cons).1 +//│ | | | | | | | _ -> (ucs$args_xs$Some_0$Cons).1 +//│ | | | | | | | _ -> (ucs$args_xs$Some_0$Cons).1 +//│ | | | | | | | Nil*† -> "nothing" +//│ | | | | | | | None*† -> "nothing" +//│ fun test: (None | Some[Cons[nothing] | Nil]) -> ("nothing" | Int | List[nothing]) diff --git a/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls b/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls new file mode 100644 index 00000000..23bfbddf --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls @@ -0,0 +1,71 @@ +:PreTyper + +:dpt:postprocess.result,desugared +fun mixed_literals(v) = + if v is + true then "true" + false then "false" + 1 then "1" + 2 then "2" +//│ | | | | | | | Post-processed UCS term: +//│ | | | | | | | case v*‡ of +//│ | | | | | | | true*† -> "true" +//│ | | | | | | | false*† -> "false" +//│ | | | | | | | 2 -> "2" +//│ | | | | | | | 1 -> "1" +//│ | | | | | | | Desugared term: case v of { true => "true"; false => "false"; 2 => "2"; 1 => "1" } +//│ ╙── +//│ ╙── +//│ fun mixed_literals: (1 | 2) -> ("1" | "2" | "false" | "true") + +:dpt:postprocess.result +fun separated_by_and(v) = + if v is + true then "true" + _ and v is + false then "false" +//│ | | | | | | | Post-processed UCS term: +//│ | | | | | | | case v*‡ of +//│ | | | | | | | true*† -> "true" +//│ | | | | | | | false*† -> "false" +//│ ╙── +//│ ╙── +//│ fun separated_by_and: nothing -> ("false" | "true") + +:dpt:postprocess.result +fun dual_patterns(x, y) = + if + x is "some" and y is "none" then 0 + x is "none" and y is "some" then 1 + x is "some" and y is "some" then 2 + x is "none" and y is "none" then 3 +//│ | | | | | | | Post-processed UCS term: +//│ | | | | | | | case x*‡ of +//│ | | | | | | | "some" -> +//│ | | | | | | | case y*‡ of +//│ | | | | | | | "none" -> 0 +//│ | | | | | | | "some" -> 2 +//│ | | | | | | | "none" -> +//│ | | | | | | | case y*‡ of +//│ | | | | | | | "some" -> 1 +//│ | | | | | | | "none" -> 3 +//│ fun dual_patterns: ("none" | "some", "none" | "some") -> (0 | 1 | 2 | 3) + +:dpt:postprocess.result +fun unordered_dual_patterns(x, y) = + if + x is "some" and y is "none" then 0 + y is "some" and x is "none" then 1 + y is "some" and x is "some" then 2 + x is "none" and y is "none" then 3 +//│ | | | | | | | Post-processed UCS term: +//│ | | | | | | | case x*‡ of +//│ | | | | | | | "some" -> +//│ | | | | | | | case y*‡ of +//│ | | | | | | | "none" -> 0 +//│ | | | | | | | "some" -> 2 +//│ | | | | | | | "none" -> +//│ | | | | | | | case y*‡ of +//│ | | | | | | | "some" -> 1 +//│ | | | | | | | "none" -> 3 +//│ fun unordered_dual_patterns: ("none" | "some", "none" | "some") -> (0 | 1 | 2 | 3) diff --git a/shared/src/test/diff/pretyper/ucs/stages/TransfromUCS.mls b/shared/src/test/diff/pretyper/ucs/stages/TransfromUCS.mls index 31eac641..2facf100 100644 --- a/shared/src/test/diff/pretyper/ucs/stages/TransfromUCS.mls +++ b/shared/src/test/diff/pretyper/ucs/stages/TransfromUCS.mls @@ -35,7 +35,7 @@ fun zipWith(f, xs, ys) = Cons(x, xs) and ys is Cons(y, ys) and zipWith(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) Nil and ys is Nil then Some(Nil) else None -//│ fun zipWith: forall 'T 'T0 'T1. (('T, 'T0) -> 'T1, Cons['T] | Nil | Object & ~#Cons & ~#Nil, Cons['T0] | Object & ~#Cons) -> (None | Some[Cons['T1] | Nil]) +//│ fun zipWith: forall 'T 'T0 'T1. (('T, 'T0) -> 'T1, Cons['T] | Object & ~#Cons, Cons['T0] | Object & ~#Cons) -> (None | Some[Cons['T1] | Nil]) fun getOrElse[T](x: Option[T], default: T): T = diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index 5effd948..4d9929ce 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -215,7 +215,7 @@ class DiffTests case "p" => mode.copy(showParse = true) case "d" => mode.copy(dbg = true) case "dp" => mode.copy(dbgParsing = true) - case PreTyperFlags(ts) => mode.copy(dbgPreTyper = S(ts)) + case PreTyperFlags(ts) => mode.copy(dbgPreTyper = mode.dbgPreTyper.fold(S(ts))(ts0 => S(ts0 ++ ts))) case "ds" => mode.copy(dbgSimplif = true) case "ducs" => mode.copy(dbg = true, dbgUCS = true) case "s" => mode.copy(fullExceptionStack = true) @@ -1163,7 +1163,7 @@ object DiffTests { } object PreTyperFlags { - private val pattern = "^dpt(?::\\s*([A-Za-z]+)(,\\s*[A-Za-z]+)*)?$".r + private val pattern = "^dpt(?::\\s*([A-Za-z\\.-]+)(,\\s*[A-Za-z\\.-]+)*)?$".r def unapply(flags: Str): Opt[Set[Str]] = flags match { case pattern(head, tail) => From 95c344ddccbd4bc64717c83b459e1d59fbc73d54 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Fri, 12 Jan 2024 23:40:41 +0800 Subject: [PATCH 056/143] Add changes caused by previous commit and fix examples --- shared/src/test/diff/mlscript/Repro.mls | 40 +++++++++- shared/src/test/diff/nu/HeungTung.mls | 2 +- .../diff/pretyper/ucs/examples/AVLTree.mls | 2 +- .../ucs/examples/BinarySearchTree.mls | 74 +++++++------------ .../pretyper/ucs/examples/EitherOrBoth.mls | 2 +- .../test/diff/pretyper/ucs/examples/JSON.mls | 14 ++-- .../pretyper/ucs/examples/LeftistTree.mls | 4 +- .../pretyper/ucs/examples/LispInterpreter.mls | 32 +++----- .../test/diff/pretyper/ucs/examples/List.mls | 30 ++++++++ .../diff/pretyper/ucs/examples/Option.mls | 7 -- .../pretyper/ucs/examples/Permutations.mls | 4 +- .../diff/pretyper/ucs/examples/SimpleList.mls | 18 +++++ .../diff/pretyper/ucs/examples/SimpleTree.mls | 31 ++++++++ .../test/diff/pretyper/ucs/examples/ULC.mls | 4 +- .../diff/pretyper/ucs/stages/TransfromUCS.mls | 2 +- 15 files changed, 176 insertions(+), 90 deletions(-) create mode 100644 shared/src/test/diff/pretyper/ucs/examples/List.mls create mode 100644 shared/src/test/diff/pretyper/ucs/examples/SimpleList.mls create mode 100644 shared/src/test/diff/pretyper/ucs/examples/SimpleTree.mls diff --git a/shared/src/test/diff/mlscript/Repro.mls b/shared/src/test/diff/mlscript/Repro.mls index bf3a52e7..272dfe5e 100644 --- a/shared/src/test/diff/mlscript/Repro.mls +++ b/shared/src/test/diff/mlscript/Repro.mls @@ -1 +1,39 @@ -:PreTyper +type Tree = Node | Empty +class Node: { value: int; left: Tree; right: Tree } +class Empty +//│ Defined type alias Tree +//│ Defined class Node +//│ Defined class Empty + +rec def insert(t, v) = case t of + { Node -> + let v' = t.value in + let l = t.left in + let r = t.right in + let ucs__test__0 = v < v' : bool in + case ucs__test__0 of + { true -> Node { value = v'; left = insert(l, v,); right = r } + | _ -> + let ucs__test__1 = v > v' : bool in + case ucs__test__1 of + { true -> Node { value = v'; left = l; right = insert(r, v,) } + | _ -> t + } + } + | Empty -> Node { value = v; left = Empty {}; right = Empty {} } + } +//│ insert: (Empty | Node & 'a, int & 'value,) -> ((Node with {left: Empty, right: Empty, value: 'value}) | 'a | 'b) +//│ where +//│ 'b :> (Node with { +//│ left: (Node with {left: Empty, right: Empty, value: 'value}) | 'a | 'b, +//│ right: 'right, +//│ value: 'value0 +//│ }) | (Node with { +//│ left: 'left, +//│ right: (Node with {left: Empty, right: Empty, value: 'value}) | 'a | 'b, +//│ value: 'value0 +//│ }) +//│ 'a <: Tree & {left: 'left, right: 'right, value: int & 'value0} +//│ 'right <: Empty | Node & 'a +//│ 'left <: Empty | Node & 'a +//│ = [Function: insert] diff --git a/shared/src/test/diff/nu/HeungTung.mls b/shared/src/test/diff/nu/HeungTung.mls index 4406f74b..3b0c9c24 100644 --- a/shared/src/test/diff/nu/HeungTung.mls +++ b/shared/src/test/diff/nu/HeungTung.mls @@ -229,7 +229,7 @@ f: (Int -> Int) & (Bool -> Bool) //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.228: f: (Int -> Int) & (Bool -> Bool) //│ ║ ^ -//│ ╟── type `Bool` is not an instance of `Int` +//│ ╟── type `Bool` is not an instance of type `Int` //│ ║ l.228: f: (Int -> Int) & (Bool -> Bool) //│ ║ ^^^^ //│ ╟── Note: constraint arises from type reference: diff --git a/shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls b/shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls index b04eb208..dd5dcf0e 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls @@ -42,7 +42,7 @@ fun (::) cons(head, tail) = Cons(head, tail) //│ fun (::) cons: forall 'T. ('T, List['T]) -> Cons['T] abstract class Tree[out A]: (Empty | Node[A]) -class Node[A](value: A, left: Tree[A], right: Tree[A], height: Int) extends Tree[A] +class Node[out A](value: A, left: Tree[A], right: Tree[A], height: Int) extends Tree[A] module Empty extends Tree[nothing] //│ abstract class Tree[A]: Empty | Node[A] //│ class Node[A](value: A, left: Tree[A], right: Tree[A], height: Int) extends Tree diff --git a/shared/src/test/diff/pretyper/ucs/examples/BinarySearchTree.mls b/shared/src/test/diff/pretyper/ucs/examples/BinarySearchTree.mls index e7da1db2..a8f0c8d2 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/BinarySearchTree.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/BinarySearchTree.mls @@ -12,8 +12,8 @@ fun abs(x) = if x < 0 then -x else x //│ fun abs: Int -> Int -abstract class Option[out T]: (Some[T] | None) -class Some[out T](val value: T) extends Option[T] +abstract class Option[T]: (Some[T] | None) +class Some[T](val value: T) extends Option[T] module None extends Option[nothing] //│ abstract class Option[T]: None | Some[T] //│ class Some[T](value: T) extends Option @@ -48,8 +48,8 @@ fun (::) cons(head, tail) = Cons(head, tail) //│ = Cons {} abstract class Tree[out A]: (Empty | Node[A]) -class Node[A](value: A, left: Tree[A], right: Tree[A]) extends Tree[A] -module Empty extends Tree[nothing] +class Node[out A](value: A, left: Tree[A], right: Tree[A]) extends Tree[A] +module Empty extends Tree //│ abstract class Tree[A]: Empty | Node[A] //│ class Node[A](value: A, left: Tree[A], right: Tree[A]) extends Tree //│ module Empty extends Tree @@ -57,7 +57,7 @@ module Empty extends Tree[nothing] fun single(v) = Node(v, Empty, Empty) //│ fun single: forall 'A. 'A -> Node['A] -fun show(t: Tree[anything]): Str = if t is +fun show(t: Tree['A]): Str = if t is Node(v, l, r) then "(" ++ show(l) ++ " " ++ toString(v) ++ " " ++ show(r) ++ ")" Empty then "•" @@ -83,7 +83,7 @@ fun insert(t, v) = if t is v > v' then Node(v', l, insert(r, v)) _ then t Empty then Node(v, Empty, Empty) -//│ fun insert: forall 'A 'A0. (Empty | Node[Num & 'A], Num & 'A & 'A0) -> Node['A | 'A0] +//│ fun insert: forall 'A. (Empty | Node[Num & 'A], Num & 'A) -> (Node[nothing] | Node['A]) // FIXME fun insert'(t, v) = if t is @@ -111,36 +111,19 @@ insert(Node(1, Node(0, Empty, Empty), Empty), 2) |> show //│ res //│ = '((• 0 •) 1 (• 2 •))' -// FIXME + fun fromList(l) = - fun fromList'(t, xs) = + let rec fromList'(t, xs) = if xs is Cons(x, xs') then fromList'(insert(t, x), xs') - Nil then Empty + Nil then t fromList'(Empty, l) -//│ ╔══[ERROR] Cannot use `val` or `fun` in local block; use `let` instead. -//│ ║ l.116: fun fromList'(t, xs) = -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.117: if xs is -//│ ║ ^^^^^^^^^^^^ -//│ ║ l.118: Cons(x, xs') then fromList'(insert(t, x), xs') -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.119: Nil then Empty -//│ ╙── ^^^^^^^^^^^^^^^^^^^^ -//│ fun fromList: (Cons[Num] | Nil) -> Empty - -fun fromList(t, xs) = - if xs is - Cons(x, xs') then fromList(insert(t, x), xs') - Nil then t -//│ fun fromList: forall 'a 'A 'A0. ('a & (Empty | Node['A]), Cons[Num & 'A & 'A0] | Nil) -> (Node['A | 'A0] | 'a) -//│ where -//│ 'A <: Num +//│ fun fromList: forall 'A. (Cons[Num & 'A] | Nil) -> (Empty | Node['A]) -fromList(Empty, 1 :: 2 :: 3 :: 4 :: Nil) |> show -fromList(Empty, 2 :: 1 :: 4 :: 3 :: Nil) |> show -fromList(Empty, 4 :: 3 :: 2 :: 1 :: Nil) |> show -let example1 = fromList(Empty, 1 :: 3 :: 2 :: 4 :: Nil) +fromList(1 :: 2 :: 3 :: 4 :: Nil) |> show +fromList(2 :: 1 :: 4 :: 3 :: Nil) |> show +fromList(4 :: 3 :: 2 :: 1 :: Nil) |> show +let example1 = fromList(1 :: 3 :: 2 :: 4 :: Nil) example1 |> show //│ let example1: Empty | Node[1 | 2 | 3 | 4] //│ Str @@ -215,7 +198,7 @@ fun lowerBound(t, v) = if t is v > v' then Some(lowerBound(r, v) ?? v') _ then Some(v') Empty then None -//│ fun lowerBound: forall 'a 'T. (Empty | Node[Num & 'a & 'T], Num) -> (None | Some['T | 'a]) +//│ fun lowerBound: forall 'a. (Empty | Node[Num & 'a], Num) -> (None | Some['a]) lowerBound(Empty, 0) ?? "not found" lowerBound(Node(0, Empty, Empty), 0) ?? "not found" @@ -251,7 +234,7 @@ lowerBound(example1, 5) ?? "not found" //│ res //│ = 4 -let example2 = fromList(Empty, 1 :: 5 :: 42 :: 10 :: 23 :: 59 :: 81 :: Nil) +let example2 = fromList(1 :: 5 :: 42 :: 10 :: 23 :: 59 :: 81 :: Nil) lowerBound(example2, 0) ?? "not found" lowerBound(example2, 25) ?? "not found" lowerBound(example2, 99) ?? "not found" @@ -281,7 +264,7 @@ fun upperBound(t, v) = if t is v > v' then upperBound(r, v) _ then Some(v') Empty then None -//│ fun upperBound: forall 'a 'T. (Empty | Node[Num & 'a & 'T], Num) -> (None | Some['T | 'a]) +//│ fun upperBound: forall 'a. (Empty | Node[Num & 'a], Num) -> (None | Some['a]) upperBound(example2, 0) ?? "not found" upperBound(example2, 25) ?? "not found" @@ -312,9 +295,7 @@ fun remove(t, v) = None then l Some(v'') then Node(v'', l, remove(r, v'')) Empty then Empty -//│ fun remove: forall 'A. (Empty | Node['A], Num) -> (Empty | Node['A] | Tree['A]) -//│ where -//│ 'A <: Num +//│ fun remove: forall 'A. (Empty | Node[Num & 'A], Num) -> (Empty | Node['A] | Tree['A]) remove(Empty, 0) |> show remove(Node(0, Empty, Empty), 0) |> show @@ -361,9 +342,12 @@ class Pair[A, B](val first: A, val second: B) { fun mapSecond(f) = Pair(first, f(second)) } //│ class Pair[A, B](first: A, second: B) { -//│ fun mapFirst: forall 'A. (A -> 'A) -> Pair['A, B] -//│ fun mapSecond: forall 'B. (B -> 'B) -> Pair[A, 'B] +//│ fun mapFirst: forall 'A 'B. (A -> 'A) -> Pair['A, 'B] +//│ fun mapSecond: forall 'B0 'A0. (B -> 'B0) -> Pair['A0, 'B0] //│ } +//│ where +//│ 'A0 :> A +//│ 'B :> B fun extractMin(t) = if t is @@ -372,7 +356,9 @@ fun extractMin(t) = extractMin(l) is Pair(m, l') then Pair(m, Node(v, l', r)) Empty then Pair(None, Empty) -//│ fun extractMin: forall 'A. (Empty | Node['A]) -> Pair[None | Some['A], Empty | Node['A] | Tree['A]] +//│ fun extractMin: forall 'A 'A0 'B 'T 'A1. (Empty | Node['A0 & 'T & 'A1]) -> Pair[in 'A out 'A | None, in Tree['A1] & 'B out Empty | 'B | Node['A1] | Tree['A0]] +//│ where +//│ 'A :> None | Some['T] extractMin(example1).first ?? "not found" extractMin(example1).second |> show @@ -412,9 +398,7 @@ fun removeGte(t, v) = v > v' then Node(v', l, removeGte(r, v)) _ then l // lucky case Empty then Empty -//│ fun removeGte: forall 'A. (Empty | Node['A], Num) -> (Empty | Node['A] | Tree['A]) -//│ where -//│ 'A <: Num +//│ fun removeGte: forall 'A. (Empty | Node[Num & 'A], Num) -> (Empty | Node['A] | Tree['A]) removeGte(Empty, 0) |> show removeGte(example1, 0) |> show @@ -502,8 +486,6 @@ fun removeRange(t, begin, end) = _ then merge(removeGte(l, begin), removeLt(r, end)) Empty then Empty //│ fun removeRange: forall 'A. (Empty | Node[Num & 'A], Num, Num) -> (Empty | Node['A] | Tree['A]) -//│ where -//│ 'A <: Num example2 |> show removeRange(example2, 1, 82) |> show @@ -552,7 +534,7 @@ fun inverse(t) = if t is Node(v, l, r) then Node(v, inverse(r), inverse(l)) Empty then Empty -//│ fun inverse: forall 'A. (Empty | Node['A]) -> (Empty | Node['A]) +//│ fun inverse: (Empty | Node[anything]) -> (Empty | Node[nothing]) inverse(Empty) |> show inverse(Node(0, Empty, Empty)) |> show diff --git a/shared/src/test/diff/pretyper/ucs/examples/EitherOrBoth.mls b/shared/src/test/diff/pretyper/ucs/examples/EitherOrBoth.mls index fbeeeda7..76a8a831 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/EitherOrBoth.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/EitherOrBoth.mls @@ -59,7 +59,7 @@ fun map[A, B, C, D](eob: EitherOrBoth[A, B], f: A -> C, g: B -> D): EitherOrBoth Left(left) then Left(f(left)) Right(right) then Right(g(right)) Both(left, right) then Both(f(left), g(right)) -//│ fun map: forall 'A 'B 'C 'D. (eob: EitherOrBoth['A, 'B], f: 'A -> 'C, g: 'B -> 'D) -> EitherOrBoth['C, 'D] +//│ fun map: forall 'D 'A 'B 'C. (eob: EitherOrBoth['A, 'B], f: 'A -> 'C, g: 'B -> 'D) -> EitherOrBoth['C, 'D] fun fold[A, B, C](eob: EitherOrBoth[A, B], f: A -> C, g: B -> C, h: [A, B] -> C): C = if eob is diff --git a/shared/src/test/diff/pretyper/ucs/examples/JSON.mls b/shared/src/test/diff/pretyper/ucs/examples/JSON.mls index 32900a89..8113539d 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/JSON.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/JSON.mls @@ -74,7 +74,7 @@ declare fun parseInt: (Str, Int) -> Int // `List` and its utilities: abstract class List[out T]: Cons[T] | Nil -class Cons[T](head: T, tail: List[T]) extends List[T] +class Cons[out T](head: T, tail: List[T]) extends List[T] module Nil extends List fun (::) cons(head: 'T, tail: List['T]): List['T] = Cons(head, tail) fun reverse(l: List['A]): List['A] = @@ -96,11 +96,11 @@ fun equalList(xs: List['A], ys: List['A], equal: ('A, 'A) -> Bool): Bool = if xs //│ class Cons[T](head: T, tail: List[T]) extends List //│ module Nil extends List //│ fun (::) cons: forall 'T. (head: 'T, tail: List['T]) -> List['T] -//│ fun reverse: forall 'A. (l: List['A]) -> List['A] +//│ fun reverse: forall 'T0. (l: List['T0]) -> List['T0] //│ fun join: (sep: Str, xs: List[anything]) -> (Str | NStr) //│ fun showList: (xs: List[anything]) -> NStr -//│ fun map: forall 'D 'T0. (f: 'D -> 'T0, xs: List['D]) -> List['T0] -//│ fun equalList: forall 'A0. (xs: List['A0], ys: List['A0], equal: ('A0, 'A0) -> Bool) -> Bool +//│ fun map: forall 'D 'T1. (f: 'D -> 'T1, xs: List['D]) -> List['T1] +//│ fun equalList: forall 'A. (xs: List['A], ys: List['A], equal: ('A, 'A) -> Bool) -> Bool // `Option` and its utilities: abstract class Option[out A]: Some[A] | None @@ -136,7 +136,7 @@ fun showMap(map) = //│ class ConsMap[K, V](head: [K, V], tail: ListMap[K, V]) extends ListMap //│ module NilMap extends ListMap //│ fun containsKey: forall 'K 'a. (map: ListMap['K, anything], key: 'a) -> Bool -//│ fun (:+) insert: forall 'K0 'b 'V. (ConsMap['K0, 'V] | NilMap, ['K0 & 'b, 'V]) -> ConsMap['K0, 'V] +//│ fun (:+) insert: forall 'b 'V 'K0. (ConsMap['K0, 'V] | NilMap, ['K0 & 'b, 'V]) -> ConsMap['K0, 'V] //│ fun showMap: forall 'K1. (ConsMap['K1, anything] | NilMap) -> NStr //│ where //│ 'K0 <: Eql['b] @@ -294,7 +294,7 @@ fun parseNumber(state: ParserState): ParseResult[Num] = parseFraction(state).flatMap of (fraction, state) => parseExponent(state).flatMap of (exponent, state) => let value = (integral +. fraction) *. exponent - Success(if negative then (0 -. value) else value, state) + Success of (if negative then (0 -. value) else value), state //│ fun parseNumber: (state: ParserState) -> ParseResult[Num] showParseResult of parseNumber of ParserState of String("0"), 0 @@ -419,7 +419,7 @@ fun parseObjectEntry(state: ParserState): ParseResult[[Str, JsonValue]] = None then Failure("expected ':' instead of end of input") else Failure("expected ':' instead of end of input") fun parseObject(state: ParserState): ParseResult[ListMap[Str, JsonValue]] = - let rec parseObjectTail(acc, state) = + let rec parseObjectTail(acc: ListMap[Str, JsonValue], state: ParserState) = let state' = skipWhiteSpace(state) if state'.peek is Some(",") then diff --git a/shared/src/test/diff/pretyper/ucs/examples/LeftistTree.mls b/shared/src/test/diff/pretyper/ucs/examples/LeftistTree.mls index 76b7c420..3cccb825 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/LeftistTree.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/LeftistTree.mls @@ -43,7 +43,7 @@ fun (::) cons(head, tail) = Cons(head, tail) //│ fun (::) cons: forall 'T. ('T, List['T]) -> Cons['T] abstract class Tree[out A]: (Empty | Node[A]) -class Node[A](value: A, left: Tree[A], right: Tree[A], rank: Int) extends Tree[A] +class Node[out A](value: A, left: Tree[A], right: Tree[A], rank: Int) extends Tree[A] module Empty extends Tree[nothing] //│ abstract class Tree[A]: Empty | Node[A] //│ class Node[A](value: A, left: Tree[A], right: Tree[A], rank: Int) extends Tree @@ -164,7 +164,7 @@ fun drain(t) = if getMin(t) is None then Nil Some(x) then x :: drain(deleteMin(t)) -//│ fun drain: forall 'T. (Empty | Node[Num & 'T]) -> (Cons[Num | 'T] | Nil) +//│ fun drain: (Empty | Node[Num]) -> (Cons[Num] | Nil) fun sorted(xs) = fromList(Empty, xs) |> drain //│ fun sorted: (Cons[Num] | Nil) -> (Cons[Num] | Nil) diff --git a/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls b/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls index fb0a7f4f..90cc4f58 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls @@ -62,7 +62,7 @@ declare fun parseInt: (Str, Int) -> Int // `List` and its utilities: abstract class List[out T]: Cons[T] | Nil -class Cons[T](head: T, tail: List[T]) extends List[T] +class Cons[out T](head: T, tail: List[T]) extends List[T] module Nil extends List fun (::) cons(head: 'T, tail: List['T]): List['T] = Cons(head, tail) fun reverse(l: List['A]): List['A] = @@ -81,18 +81,18 @@ fun equalList(xs: List['A], ys: List['A], equal: ('A, 'A) -> Bool): Bool = if xs Nil and ys is Nil then true else false // `Option` and its utilities: -abstract class Option[A]: Some[A] | None +abstract class Option[out A]: Some[A] | None class Some[out A](value: A) extends Option[A] module None extends Option //│ abstract class List[T]: Cons[T] | Nil //│ class Cons[T](head: T, tail: List[T]) extends List //│ module Nil extends List //│ fun (::) cons: forall 'T. (head: 'T, tail: List['T]) -> List['T] -//│ fun reverse: forall 'A. (l: List['A]) -> List['A] +//│ fun reverse: forall 'T0. (l: List['T0]) -> List['T0] //│ fun join: (sep: Str, xs: List[anything]) -> (Str | NStr) //│ fun showList: (xs: List[anything]) -> NStr -//│ fun map: forall 'D 'T0. (f: 'D -> 'T0, xs: List['D]) -> List['T0] -//│ fun equalList: forall 'A0. (xs: List['A0], ys: List['A0], equal: ('A0, 'A0) -> Bool) -> Bool +//│ fun map: forall 'D 'T1. (f: 'D -> 'T1, xs: List['D]) -> List['T1] +//│ fun equalList: forall 'A. (xs: List['A], ys: List['A], equal: ('A, 'A) -> Bool) -> Bool //│ abstract class Option[A]: None | Some[A] //│ class Some[A](value: A) extends Option //│ module None extends Option @@ -489,7 +489,6 @@ fun showEval(source: Str): Str = [Failure(error), _] then "Error: " ++ error //│ fun showEval: (source: Str) -> Str -// FIXME showEval("1") showEval("(+ 1 2)") showEval("(val x 7 (val y 6 (* x y)))") @@ -503,28 +502,21 @@ showEval("(def id (lambda (x) x) (id 42))") //│ res //│ = 'Ok: 1' //│ res -//│ Runtime error: -//│ TypeError: ((intermediate value) , (intermediate value)(...)) is not a function +//│ = 'Ok: 3' //│ res -//│ Runtime error: -//│ TypeError: ((intermediate value) , (intermediate value)(...)) is not a function +//│ = 'Ok: 42' //│ res //│ = 'Ok: x' //│ res -//│ Runtime error: -//│ TypeError: ((intermediate value) , (intermediate value)(...)) is not a function +//│ = 'Ok: (1)' //│ res -//│ Runtime error: -//│ TypeError: ((intermediate value) , (intermediate value)(...)) is not a function +//│ = 'Ok: 1' //│ res -//│ Runtime error: -//│ TypeError: ((intermediate value) , (intermediate value)(...)) is not a function +//│ = 'Ok: 0' //│ res -//│ Runtime error: -//│ TypeError: ((intermediate value) , (intermediate value)(...)) is not a function +//│ = 'Ok: 1' //│ res -//│ Runtime error: -//│ TypeError: ((intermediate value) , (intermediate value)(...)) is not a function +//│ = 'Ok: 42' // showEval("(def fact (lambda (n) (if (eq n 0) 1 (* n (fact (- n 1))))) (fact 5))") // showEval("(def fib (lambda (n) (if (eq n 0) 0 (if (eq n 1) 1 (+ (fib (- n 1)) (fib (- n 2)))))) (fib 10))") diff --git a/shared/src/test/diff/pretyper/ucs/examples/List.mls b/shared/src/test/diff/pretyper/ucs/examples/List.mls new file mode 100644 index 00000000..d922fd4a --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/examples/List.mls @@ -0,0 +1,30 @@ +:PreTyper + +// TODO +abstract class List[out A]: (Cons[A] | Nil) { + fun isEmpty: Bool + fun map[B]: (A -> B) -> List[B] +} +class Cons[out A](head: A, tail: List[A]) extends List[A] { + fun isEmpty: Bool = false + fun map(f) = Cons(f(head), tail.map(f)) +} +module Nil extends List { + fun isEmpty: Bool = true + fun map(f) = Nil +} +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.10: fun map(f) = Cons(f(head), tail.map(f)) +//│ ╙── ^^^^ +//│ abstract class List[A]: Cons[A] | Nil { +//│ fun isEmpty: Bool +//│ fun map: forall 'B. (A -> 'B) -> List['B] +//│ } +//│ class Cons[A](head: A, tail: List[A]) extends List { +//│ fun isEmpty: Bool +//│ fun map: forall 'A. (A -> 'A) -> Cons['A] +//│ } +//│ module Nil extends List { +//│ fun isEmpty: Bool +//│ fun map: anything -> Nil +//│ } diff --git a/shared/src/test/diff/pretyper/ucs/examples/Option.mls b/shared/src/test/diff/pretyper/ucs/examples/Option.mls index 687b4b37..32a2706f 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/Option.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/Option.mls @@ -1,6 +1,5 @@ :PreTyper -// FIXME: Finish this example after the relevant issue is fixed. abstract class MyOption[out T]: (MySome[T] | MyNone) { virtual fun filter: (p: T -> Bool) -> MyOption[T] } @@ -10,12 +9,6 @@ class MySome[out T](val value: T) extends MyOption[T] { module MyNone extends MyOption[nothing] { fun filter(_) = MyNone } -//│ ╔══[ERROR] Type `#MySome & {MySome#T <: ?T}` does not contain member `MyOption#T` -//│ ║ l.4: abstract class MyOption[out T]: (MySome[T] | MyNone) { -//│ ╙── ^ -//│ ╔══[ERROR] Type `#MyNone` does not contain member `MyOption#T` -//│ ║ l.4: abstract class MyOption[out T]: (MySome[T] | MyNone) { -//│ ╙── ^ //│ abstract class MyOption[T]: MyNone | MySome[T] { //│ fun filter: (p: T -> Bool) -> MyOption[T] //│ } diff --git a/shared/src/test/diff/pretyper/ucs/examples/Permutations.mls b/shared/src/test/diff/pretyper/ucs/examples/Permutations.mls index 959a1140..04277c54 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/Permutations.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/Permutations.mls @@ -62,7 +62,7 @@ fun insertAllPositions(z) = let nu = ((prev :+ z) ::: xs) aux(prev :+ head, nu :: acc, tail) xs => aux(Nil, Nil, xs) -//│ fun insertAllPositions: forall 'a. 'a -> (forall 'A 'A0. (Cons['A & 'A0] | Nil) -> (Cons[List['A0 | 'a]] & {Cons#T <: List['A0 | 'a]} | Nil)) +//│ fun insertAllPositions: forall 'a. 'a -> (forall 'A 'A0. (Cons['A & 'A0] | Nil) -> (Cons[List['A0 | 'a]] | Nil)) //│ where //│ 'A <: 'A0 //│ 'A0 :> 'a @@ -79,7 +79,7 @@ fun permutations(xs) = Nil then Nil Cons(x, Nil) then (x :: Nil) :: Nil Cons(x, xs') then foldLeft((acc, ys) => acc ::: insertAllPositions(x)(ys))(Nil)(permutations(xs')) -//│ fun permutations: forall 'T 'A. (Cons['T] | Nil) -> (Cons[Cons['T]] | List[List['A]] | Nil) +//│ fun permutations: forall 'A 'T. (Cons['T] | Nil) -> (Cons[Cons['T]] | List[List['A]] | Nil) //│ where //│ 'T <: 'A //│ 'A := 'T diff --git a/shared/src/test/diff/pretyper/ucs/examples/SimpleList.mls b/shared/src/test/diff/pretyper/ucs/examples/SimpleList.mls new file mode 100644 index 00000000..0da096a9 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/examples/SimpleList.mls @@ -0,0 +1,18 @@ +:PreTyper + +abstract class List[T]: (Cons[T] | Nil) +class Cons[T](val head: T, val tail: List[T]) extends List[T] +module Nil extends List +//│ abstract class List[T]: Cons[T] | Nil +//│ class Cons[T](head: T, tail: List[T]) extends List +//│ module Nil extends List + +fun (::) cons(head, tail) = Cons(head, tail) +//│ fun (::) cons: forall 'T. ('T, List['T]) -> Cons['T] + +1 :: 2 :: 3 :: 4 :: 5 :: 6 :: 7 :: 8 :: Nil +//│ Cons['T] +//│ where +//│ 'T :> 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 +//│ res +//│ = Cons {} diff --git a/shared/src/test/diff/pretyper/ucs/examples/SimpleTree.mls b/shared/src/test/diff/pretyper/ucs/examples/SimpleTree.mls new file mode 100644 index 00000000..db617d7c --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/examples/SimpleTree.mls @@ -0,0 +1,31 @@ +:PreTyper + +// abstract class Tree[out A]: (Empty | Node[A]) +// class Node[out A](value: A, left: Tree[A], right: Tree[A]) extends Tree[A] +// module Empty extends Tree +// //│ abstract class Tree[A]: Empty | Node[A] +// //│ class Node[A](value: A, left: Tree[A], right: Tree[A]) extends Tree +// //│ module Empty extends Tree + +// fun insert(t, v) = if t is +// Node(v', l, r) and +// v < v' then Node(v', insert(l, v), r) +// v > v' then Node(v', l, insert(r, v)) +// _ then t +// Empty then Node(v, Empty, Empty) +// //│ fun insert: forall 'A. (Empty | Node[Num & 'A], Num & 'A) -> (Node[nothing] | Node['A]) + +abstract class List[out T]: (Cons[T] | Nil) +class Cons[out T](val head: T, val tail: List[T]) extends List[T] +module Nil extends List +//│ abstract class List[T]: Cons[T] | Nil +//│ class Cons[T](head: T, tail: List[T]) extends List +//│ module Nil extends List + +fun (::) cons(head, tail) = Cons(head, tail) +//│ fun (::) cons: forall 'T. ('T, List['T]) -> Cons['T] + +1 :: 2 :: 3 :: 4 :: Nil +//│ Cons[1 | 2 | 3 | 4] +//│ res +//│ = Cons {} diff --git a/shared/src/test/diff/pretyper/ucs/examples/ULC.mls b/shared/src/test/diff/pretyper/ucs/examples/ULC.mls index 4cccc0ec..d94e810a 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/ULC.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/ULC.mls @@ -83,7 +83,9 @@ fun findFirst(list, p) = Cons(x, xs) and p(x) then Some(x) else findFirst(xs, p) -//│ fun findFirst: forall 'A. (Cons['A] | Nil, 'A -> Bool) -> (None | Some['A]) +//│ fun findFirst: forall 'A 'A0. (Cons['A] | Nil, 'A -> Bool) -> (None | Some['A0]) +//│ where +//│ 'A <: 'A0 fun (:::) listConcat(xs, ys) = if xs is diff --git a/shared/src/test/diff/pretyper/ucs/stages/TransfromUCS.mls b/shared/src/test/diff/pretyper/ucs/stages/TransfromUCS.mls index 2facf100..a3e04419 100644 --- a/shared/src/test/diff/pretyper/ucs/stages/TransfromUCS.mls +++ b/shared/src/test/diff/pretyper/ucs/stages/TransfromUCS.mls @@ -35,7 +35,7 @@ fun zipWith(f, xs, ys) = Cons(x, xs) and ys is Cons(y, ys) and zipWith(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) Nil and ys is Nil then Some(Nil) else None -//│ fun zipWith: forall 'T 'T0 'T1. (('T, 'T0) -> 'T1, Cons['T] | Object & ~#Cons, Cons['T0] | Object & ~#Cons) -> (None | Some[Cons['T1] | Nil]) +//│ fun zipWith: forall 'T 'T0 'T1 'T2. (('T0, 'T1) -> 'T2, Cons['T0] | Object & ~#Cons, Cons['T1] | Object & ~#Cons) -> (None | Some[in List['T2] & 'T out Nil | 'T | Cons['T2]]) fun getOrElse[T](x: Option[T], default: T): T = From a048607dc70f5c037a2808ac13d0d93ed37b2654 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sat, 13 Jan 2024 02:11:40 +0800 Subject: [PATCH 057/143] Uncomment lines which I forgot --- .../pretyper/ucs/examples/LispInterpreter.mls | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls b/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls index 90cc4f58..c309a504 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls @@ -518,7 +518,16 @@ showEval("(def id (lambda (x) x) (id 42))") //│ res //│ = 'Ok: 42' -// showEval("(def fact (lambda (n) (if (eq n 0) 1 (* n (fact (- n 1))))) (fact 5))") -// showEval("(def fib (lambda (n) (if (eq n 0) 0 (if (eq n 1) 1 (+ (fib (- n 1)) (fib (- n 2)))))) (fib 10))") -// showEval("(def sum (lambda (n) (if (eq n 0) 0 (+ n (sum (- n 1))))) (sum 50))") -// showEval("(def ack (lambda (m n) (if (eq m 0) (+ n 1) (if (eq n 0) (ack (- m 1) 1) (ack (- m 1) (ack m (- n 1)))))) (ack 3 2))") +showEval("(def fact (lambda (n) (if (eq n 0) 1 (* n (fact (- n 1))))) (fact 5))") +showEval("(def fib (lambda (n) (if (eq n 0) 0 (if (eq n 1) 1 (+ (fib (- n 1)) (fib (- n 2)))))) (fib 10))") +showEval("(def sum (lambda (n) (if (eq n 0) 0 (+ n (sum (- n 1))))) (sum 50))") +showEval("(def ack (lambda (m n) (if (eq m 0) (+ n 1) (if (eq n 0) (ack (- m 1) 1) (ack (- m 1) (ack m (- n 1)))))) (ack 3 2))") +//│ Str +//│ res +//│ = 'Ok: 120' +//│ res +//│ = 'Ok: 55' +//│ res +//│ = 'Ok: 1275' +//│ res +//│ = 'Ok: 29' From 7580b2af86f45e6a67cde6626a94777020570530 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sat, 13 Jan 2024 17:02:44 +0800 Subject: [PATCH 058/143] Mark refinement cases during normalization --- .../mlscript/ucs/context/PatternInfo.scala | 6 +- shared/src/main/scala/mlscript/ucs/core.scala | 10 ++- .../src/main/scala/mlscript/ucs/display.scala | 75 +++++++++---------- .../mlscript/ucs/stages/Desugaring.scala | 6 +- .../mlscript/ucs/stages/Normalization.scala | 59 +++++++++------ .../mlscript/ucs/stages/PostProcessing.scala | 17 ++--- .../pretyper/ucs/SpecilizationCollision.mls | 4 +- .../diff/pretyper/ucs/coverage/Refinement.mls | 18 +---- .../pretyper/ucs/coverage/SealedClasses.mls | 71 +++++++++--------- 9 files changed, 135 insertions(+), 131 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/context/PatternInfo.scala b/shared/src/main/scala/mlscript/ucs/context/PatternInfo.scala index 2d8d4a1a..9d6f15e6 100644 --- a/shared/src/main/scala/mlscript/ucs/context/PatternInfo.scala +++ b/shared/src/main/scala/mlscript/ucs/context/PatternInfo.scala @@ -61,7 +61,11 @@ object PatternInfo { override def matches(pat: SimpleTerm): Bool = pat match { - case pat: Var => pat.symbolOption.exists(_ === classLikeSymbol) + case pat: Var => pat.symbolOption match { + case S(patternSymbol: TypeSymbol) => + patternSymbol === classLikeSymbol || patternSymbol.hasSuperType(classLikeSymbol) + case S(_) | N => false + } case _: Lit => false } diff --git a/shared/src/main/scala/mlscript/ucs/core.scala b/shared/src/main/scala/mlscript/ucs/core.scala index e05f6e4f..a41f4d63 100644 --- a/shared/src/main/scala/mlscript/ucs/core.scala +++ b/shared/src/main/scala/mlscript/ucs/core.scala @@ -22,8 +22,16 @@ package object core { final case class Name(nme: Var) extends Pattern { override def children: Ls[Located] = nme :: Nil } - final case class Class(nme: Var, refined: Bool) extends Pattern { + /** + * TODO: Consider make `refined` immutable. + * @param nme the name of the class-like symbol + * @param originallyRefined whether the class is marked as refined from + * in source code + */ + final case class Class(nme: Var, val originallyRefined: Bool) extends Pattern { override def children: Ls[Located] = nme :: Nil + + var refined: Bool = originallyRefined } def getParametersLoc(parameters: List[Opt[Var]]): Opt[Loc] = diff --git a/shared/src/main/scala/mlscript/ucs/display.scala b/shared/src/main/scala/mlscript/ucs/display.scala index 3f7f5dd7..03bd31ca 100644 --- a/shared/src/main/scala/mlscript/ucs/display.scala +++ b/shared/src/main/scala/mlscript/ucs/display.scala @@ -1,7 +1,6 @@ package mlscript.ucs -import mlscript.ucs.{Lines, LinesOps} -import mlscript.ucs.{core, syntax} +import mlscript.ucs.{core => c, syntax => s} import mlscript.ucs.context.{Context} import mlscript.pretyper.symbol.{TermSymbol, TypeSymbol} import mlscript.{App, CaseOf, Fld, FldFlags, Let, Loc, Sel, SimpleTerm, Term, Tup, Var} @@ -24,48 +23,48 @@ package object display { `var`.name + (`var`.symbolOption.fold("")(_ => "*")) + postfix } - def showSplit(split: syntax.TermSplit)(implicit context: Context): Str = { + def showSplit(split: s.TermSplit)(implicit context: Context): Str = { // TODO: tailrec - def termSplit(split: syntax.TermSplit, isFirst: Bool, isAfterAnd: Bool): Lines = split match { - case syntax.Split.Cons(head, tail) => (termBranch(head) match { + def termSplit(split: s.TermSplit, isFirst: Bool, isAfterAnd: Bool): Lines = split match { + case s.Split.Cons(head, tail) => (termBranch(head) match { case (n, line) :: tail => (n, (if (isAfterAnd) "" else "and ") + s"$line") :: tail case Nil => Nil }) ::: termSplit(tail, false, isAfterAnd) - case syntax.Split.Let(_, nme, rhs, tail) => + case s.Split.Let(_, nme, rhs, tail) => (0, s"let ${nme.name} = ${rhs.showDbg}") :: termSplit(tail, false, isAfterAnd) - case syntax.Split.Else(term) => + case s.Split.Else(term) => (if (isFirst) (0, s"then ${term.showDbg}") else (0, s"else ${term.showDbg}")) :: Nil - case syntax.Split.Nil => Nil + case s.Split.Nil => Nil } - def termBranch(branch: syntax.TermBranch): Lines = branch match { - case syntax.TermBranch.Boolean(test, continuation) => + def termBranch(branch: s.TermBranch): Lines = branch match { + case s.TermBranch.Boolean(test, continuation) => s"$test" #: termSplit(continuation, true, false) - case syntax.TermBranch.Match(scrutinee, continuation) => + case s.TermBranch.Match(scrutinee, continuation) => s"$scrutinee is" #: patternSplit(continuation) - case syntax.TermBranch.Left(left, continuation) => + case s.TermBranch.Left(left, continuation) => s"$left" #: operatorSplit(continuation) } - def patternSplit(split: syntax.PatternSplit): Lines = split match { - case syntax.Split.Cons(head, tail) => patternBranch(head) ::: patternSplit(tail) - case syntax.Split.Let(rec, nme, rhs, tail) => + def patternSplit(split: s.PatternSplit): Lines = split match { + case s.Split.Cons(head, tail) => patternBranch(head) ::: patternSplit(tail) + case s.Split.Let(rec, nme, rhs, tail) => (0, s"let ${nme.name} = ${rhs.showDbg}") :: patternSplit(tail) - case syntax.Split.Else(term) => (0, s"else ${term.showDbg}") :: Nil - case syntax.Split.Nil => Nil + case s.Split.Else(term) => (0, s"else ${term.showDbg}") :: Nil + case s.Split.Nil => Nil } - def operatorSplit(split: syntax.OperatorSplit): Lines = split match { - case syntax.Split.Cons(head, tail) => operatorBranch(head) ::: operatorSplit(tail) - case syntax.Split.Let(rec, nme, rhs, tail) => + def operatorSplit(split: s.OperatorSplit): Lines = split match { + case s.Split.Cons(head, tail) => operatorBranch(head) ::: operatorSplit(tail) + case s.Split.Let(rec, nme, rhs, tail) => (0, s"let ${nme.name} = ${rhs.showDbg}") :: operatorSplit(tail) - case syntax.Split.Else(term) => (0, s"else ${term.showDbg}") :: Nil - case syntax.Split.Nil => Nil + case s.Split.Else(term) => (0, s"else ${term.showDbg}") :: Nil + case s.Split.Nil => Nil } - def operatorBranch(branch: syntax.OperatorBranch): Lines = + def operatorBranch(branch: s.OperatorBranch): Lines = s"${branch.operator}" #: (branch match { - case syntax.OperatorBranch.Match(_, continuation) => patternSplit(continuation) - case syntax.OperatorBranch.Binary(_, continuation) => termSplit(continuation, true, true) + case s.OperatorBranch.Match(_, continuation) => patternSplit(continuation) + case s.OperatorBranch.Binary(_, continuation) => termSplit(continuation, true, true) }) - def patternBranch(branch: syntax.PatternBranch): Lines = { - val syntax.PatternBranch(pattern, continuation) = branch + def patternBranch(branch: s.PatternBranch): Lines = { + val s.PatternBranch(pattern, continuation) = branch termSplit(continuation, true, false) match { case (0, line) :: lines => (0, s"$pattern $line") :: lines case lines => (0, pattern.toString) :: lines @@ -74,23 +73,23 @@ package object display { ("if" #: termSplit(split, true, true)).iterator.map { case (n, line) => " " * n + line }.mkString("\n") } - @inline def showSplit(s: core.Split)(implicit context: Context): Str = showSplit("if", s) + @inline def showSplit(s: c.Split)(implicit context: Context): Str = showSplit("if", s) - def showSplit(prefix: Str, s: core.Split)(implicit context: Context): Str = { + def showSplit(prefix: Str, s: c.Split)(implicit context: Context): Str = { // TODO: tailrec - def split(s: core.Split, isFirst: Bool, isTopLevel: Bool): Lines = s match { - case core.Split.Cons(head, tail) => (branch(head) match { + def split(s: c.Split, isFirst: Bool, isTopLevel: Bool): Lines = s match { + case c.Split.Cons(head, tail) => (branch(head) match { case (n, line) :: tail => (n, (if (isTopLevel) "" else "") + s"$line") :: tail case Nil => Nil }) ::: split(tail, false, isTopLevel) - case core.Split.Let(_, nme, rhs, tail) => + case c.Split.Let(_, nme, rhs, tail) => (0, s"let ${showVar(nme)} = ${rhs.showDbg}") :: split(tail, false, isTopLevel) - case core.Split.Else(term) => + case c.Split.Else(term) => (if (isFirst) (0, s"then ${term.showDbg}") else (0, s"else ${term.showDbg}")) :: Nil - case core.Split.Nil => Nil + case c.Split.Nil => Nil } - def branch(b: core.Branch): Lines = { - val core.Branch(scrutinee, pattern, continuation) = b + def branch(b: c.Branch): Lines = { + val c.Branch(scrutinee, pattern, continuation) = b s"${showVar(scrutinee)} is $pattern" #: split(continuation, true, false) } val lines = split(s, true, true) @@ -126,8 +125,8 @@ package object display { } def showCaseBranches(caseBranches: CaseBranches): Lines = caseBranches match { - case Case(pat, rhs, tail) => - (s"${showPattern(pat)} ->" @: showTerm(rhs)) ++ showCaseBranches(tail) + case k @ Case(pat, rhs, tail) => + (s"${if (k.refined) "refined " else ""}${showPattern(pat)} ->" @: showTerm(rhs)) ++ showCaseBranches(tail) case Wildcard(term) => s"_ ->" @: showTerm(term) case NoCases => Nil } diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index cecf9e02..2adae6fa 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -67,9 +67,9 @@ trait Desugaring { self: PreTyper => * Boolean conditions. */ private def truePattern(implicit scope: Scope) = - c.Pattern.Class(Var("true").withResolvedClassLikeSymbol, refined = false) + c.Pattern.Class(Var("true").withResolvedClassLikeSymbol, false) private def falsePattern(implicit scope: Scope) = - c.Pattern.Class(Var("false").withResolvedClassLikeSymbol, refined = false) + c.Pattern.Class(Var("false").withResolvedClassLikeSymbol, false) private def desugarTermSplit(split: s.TermSplit)(implicit termPart: PartialTerm, scope: Scope, context: Context): c.Split = split match { @@ -308,7 +308,7 @@ trait Desugaring { self: PreTyper => scrutineeVar.getOrCreateScrutinee.withAlias(scrutineeVar).getOrCreateBooleanPattern(nme).addLocation(nme) c.Branch( scrutinee = scrutineeVar, - pattern = c.Pattern.Class(nme.withResolvedClassLikeSymbol, refined = false), + pattern = c.Pattern.Class(nme.withResolvedClassLikeSymbol, false), continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope, context) ) :: rec(scrutineeVar, tail) case s.ConcretePattern(nme) => diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 269fb04e..a814b404 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -98,7 +98,7 @@ trait Normalization { self: DesugarUCS with Traceable => // println(s"match ${scrutineeVar.name} with $nme (has location: ${nme.toLoc.isDefined})") val trueBranch = normalizeToTerm(specialize(continuation.fill(tail, deep = false), true)(scrutineeVar, scrutinee, pattern, context)) val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context)) - CaseOf(scrutineeVar, Case(nme, trueBranch, falseBranch)(refined = rfd)) + CaseOf(scrutineeVar, Case(nme, trueBranch, falseBranch)(refined = pattern.refined)) case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => raiseError(msg"Unsupported pattern: ${pattern.toString}" -> pattern.toLoc) errorTerm @@ -159,25 +159,41 @@ trait Normalization { self: DesugarUCS with Traceable => val falseBranch = specialize(tail, matchOrNot) split.copy(head = head.copy(continuation = trueBranch), tail = falseBranch) // Class pattern. Positive. - case (true, split @ Split.Cons(head @ Branch(ScrutineeData.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Class(otherClassName, rfd), continuation), tail)) => + case (true, split @ Split.Cons(head @ Branch(ScrutineeData.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Class(otherClassName, otherRefined), continuation), tail)) => val otherClassSymbol = otherClassName.getClassLikeSymbol if (scrutinee === otherScrutinee) { println(s"scrutinee: ${scrutineeVar.name} === ${otherScrutineeVar.name}") pattern match { - case Pattern.Class(className, rfd2) => - assert(rfd === rfd2) // TODO: raise warning instead of crash + case classPattern @ Pattern.Class(className, refined) => val classSymbol = className.getClassLikeSymbol if (classSymbol === otherClassSymbol) { println(s"Case 1: class name: ${className.name} === ${otherClassName.name}") + if (otherRefined =/= refined) { + def be(value: Bool): Str = if (value) "is" else "is not" + raiseWarning( + msg"inconsistent refined case branches" -> pattern.toLoc, + msg"class pattern ${className.name} ${be(refined)} refined" -> className.toLoc, + msg"but class pattern ${otherClassName.name} ${be(otherRefined)} refined" -> otherClassName.toLoc + ) + } specialize(continuation, true) :++ specialize(tail, true) - } else if (otherClassSymbol.baseTypes contains classSymbol) { + } else if (otherClassSymbol hasSuperType classSymbol) { println(s"Case 2: ${otherClassName.name} <: ${className.name}") + println(s"${otherClassSymbol.name} is refining ${className.name}") + // We should mark `pattern` as refined. + classPattern.refined = true split } else { println(s"Case 3: ${className.name} and ${otherClassName.name} are unrelated") specialize(tail, true) } - case _ => throw new NormalizationException((msg"Incompatible: ${pattern.toString}" -> pattern.toLoc) :: Nil) + case _ => + // TODO: Make a case for this. Check if this is a valid case. + raiseError( + msg"pattern ${pattern.toString}" -> pattern.toLoc, + msg"is incompatible with class pattern ${otherClassName.name}" -> otherClassName.toLoc, + ) + split } } else { // println(s"scrutinee: ${scrutineeVar.name} =/= ${otherScrutineeVar.name}") @@ -187,14 +203,21 @@ trait Normalization { self: DesugarUCS with Traceable => ) } // Class pattern. Negative - case (false, split @ Split.Cons(head @ Branch(ScrutineeData.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Class(otherClassName, rfd), continuation), tail)) => + case (false, split @ Split.Cons(head @ Branch(ScrutineeData.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Class(otherClassName, otherRefined), continuation), tail)) => val otherClassSymbol = otherClassName.getClassLikeSymbol if (scrutinee === otherScrutinee) { println(s"scrutinee: ${scrutineeVar.name} === ${otherScrutineeVar.name}") pattern match { - case Pattern.Class(className, rfd2) => + case Pattern.Class(className, refined) => println("both of them are class patterns") - assert(rfd === rfd2) // TODO: raise warning instead of crash + if (otherRefined =/= refined) { + def be(value: Bool): Str = if (value) "is" else "is not" + raiseWarning( + msg"inconsistent refined case branches" -> pattern.toLoc, + msg"class pattern ${className.name} ${be(refined)} refined" -> className.toLoc, + msg"but class pattern ${otherClassName.name} ${be(otherRefined)} refined" -> otherClassName.toLoc + ) + } val classSymbol = className.getClassLikeSymbol if (className === otherClassName) { println(s"Case 1: class name: ${otherClassName.name} === ${className.name}") @@ -252,9 +275,9 @@ trait Normalization { self: DesugarUCS with Traceable => ) } // Other patterns. Not implemented. - case (_, Split.Cons(Branch(otherScrutineeVar, pattern, continuation), tail)) => - println(s"unsupported pattern: $pattern") - throw new NormalizationException((msg"Unsupported pattern: ${pattern.toString}" -> pattern.toLoc) :: Nil) + case (_, split @ Split.Cons(Branch(otherScrutineeVar, pattern, continuation), tail)) => + raiseError(msg"found unsupported pattern: ${pattern.toString}" -> pattern.toLoc) + split case (_, let @ Split.Let(_, nme, _, tail)) => println(s"let binding ${nme.name}, go next") let.copy(tail = specialize(tail, matchOrNot)) @@ -265,14 +288,4 @@ trait Normalization { self: DesugarUCS with Traceable => // }(showSplit(s"S${if (matchOrNot) "+" else "-"} ==>", _)) } -object Normalization { - // private def getClassLikeSymbol(className: Var): TypeSymbol = - // className.symbolOption.flatMap(_.typeSymbolOption) match { - // case N => throw new NormalizationException(msg"class name is not resolved" -> className.toLoc :: Nil) - // case S(typeSymbol) => typeSymbol - // } - - class NormalizationException(val messages: Ls[Message -> Opt[Loc]]) extends Throwable { - def this(message: Message, location: Opt[Loc]) = this(message -> location :: Nil) - } -} \ No newline at end of file +object Normalization diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index 5f042656..adf791cf 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -15,24 +15,17 @@ trait PostProcessing { self: DesugarUCS with mlscript.pretyper.Traceable => // Normalized terms are constructed using `Let` and `CaseOf`. term match { case top @ CaseOf(ScrutineeData(_), Wildcard(body)) => body - case top @ CaseOf(scrutineeVar: Var, fst @ Case(className: Var, body, NoCases)) => - println(s"found a UNARY case: ${scrutineeVar.name} is $className") - println("post-processing the body") + case top @ CaseOf(ScrutineeData.WithVar(scrutinee, scrutineeVar), fst @ Case(className: Var, body, NoCases)) => + println(s"UNARY: ${scrutineeVar.name} is ${className.name}") top.copy(cases = fst.copy(body = postProcess(body))(refined = fst.refined)) - // case top @ CaseOf(test: Var, fst @ Case(Var("true"), trueBranch, Wildcard(falseBranch))) => - // println(s"found a if-then-else case: $test is true") - // val processedTrueBranch = postProcess(trueBranch) - // val processedFalseBranch = postProcess(falseBranch) - // top.copy(cases = fst.copy(body = processedTrueBranch, rest = Wildcard(processedFalseBranch) - // )(refined = fst.refined)) case top @ CaseOf(ScrutineeData.WithVar(scrutinee, scrutineeVar), fst @ Case(pat, trueBranch, Wildcard(falseBranch))) => println(s"BINARY: ${scrutineeVar.name} is ${pat.showDbg}") println(s"patterns of `${scrutineeVar.name}`: ${scrutinee.showPatternsDbg}") // Post-process the true branch. - println("post-processing the first branch") + println("TRUE") val processedTrueBranch = postProcess(trueBranch) // Post-process the false branch. - println("post-processing the false branch") + println("FALSE") // Get all patterns, except the current one `pat`. val patternInfoList = scrutinee.patternsIterator.filter(!_.matches(pat)).toList println(s"found ${patternInfoList.length} patterns to distentangle: ${patternInfoList.iterator.map(_.showDbg).mkString(", ")}") @@ -69,7 +62,7 @@ trait PostProcessing { self: DesugarUCS with mlscript.pretyper.Traceable => // Otherwise, this is not a part of a normalized term. case other => println(s"CANNOT post-process ${other.showDbg}"); other } - }(_ => "postProcess ==> ") + }(res => s"postProcess ==> ${res.showDbg}") private def trimEmptyTerm(term: Term): Opt[Term] = term match { case k @ CaseOf(_, cases) => trimEmptyCaseBranches(cases).map(c => k.copy(cases = c)) diff --git a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls index 19a29b08..563791bf 100644 --- a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls +++ b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls @@ -55,7 +55,7 @@ fun example3(t) = Base(x) and p1(x) then x Derived(y) then y else 42 -//│ fun example3: Base -> Int +//│ fun example3: forall 'a. (Base & {#x: Num & 'a}) -> (Int | 'a) fun example4(t, x) = if t is @@ -66,7 +66,7 @@ fun example4(t, x) = // previous branch shadows the variable `x`, a correct implementation // should restore the original value of `x` in this branch. else 42 -//│ fun example4: (Base, Int) -> Int +//│ fun example4: forall 'a. (Base & {#x: Num & 'a}, Int) -> (Int | 'a) example4(Base(-1), 0) ~~> -1 example4(Base(1), 2) ~~> 42 diff --git a/shared/src/test/diff/pretyper/ucs/coverage/Refinement.mls b/shared/src/test/diff/pretyper/ucs/coverage/Refinement.mls index 29a1d069..7469887e 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/Refinement.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/Refinement.mls @@ -49,26 +49,10 @@ fun missing_a_case(x: Shape) = else x //│ fun missing_a_case: (x: Shape) -> ("Circle" | "Rectangle" | LineSegment) -// TODO: Why doesn't `Shape` match `Circle | Rectangle | LineSegment`? fun countLineSegments(x) = if x is Shape and hidden(x) then "1" Rectangle(_, _, _) then "2" LineSegment(_, _) then "3" Circle(_, _) then "4" -//│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.54: if x is -//│ ║ ^^^^ -//│ ║ l.55: Shape and hidden(x) then "1" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.56: Rectangle(_, _, _) then "2" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.57: LineSegment(_, _) then "3" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.58: Circle(_, _) then "4" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── expression of type `Shape & ~#LineSegment & ~#Rectangle` is not an instance of type `Circle` -//│ ╟── Note: constraint arises from class pattern: -//│ ║ l.58: Circle(_, _) then "4" -//│ ╙── ^^^^^^ -//│ fun countLineSegments: Shape -> ("1" | "2" | "3" | "4") +//│ fun countLineSegments: (Circle | LineSegment | Rectangle) -> ("1" | "2" | "3" | "4") diff --git a/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls b/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls index 6194694a..2d74e704 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls @@ -9,28 +9,44 @@ class App(func: Term, arg: Term) extends Term //│ class Abs(param: Str, body: Term) extends Term //│ class App(func: Term, arg: Term) extends Term -// TODO: Why doesn't `Term` match `Abs | App | Var`? -fun is_value(term) = +:dpt:desugar.result +fun is_value_explicit_refinement(term) = + if term is refined(Term) and term is + Abs(_, _) then true + Var(_) then false + App(_, _) then false +//│ | | | | | | | Desugared UCS term: +//│ | | | | | | | if term*‡ is refined Term +//│ | | | | | | | term*‡ is Abs then true +//│ | | | | | | | term*‡ is Var then false +//│ | | | | | | | term*‡ is App then false +//│ fun is_value_explicit_refinement: (Abs | App | Var) -> Bool + +:dpt:normalize.result,postprocess.result +fun is_value_automatic_refinement(term) = if term is Term and term is Abs(_, _) then true Var(_) then false App(_, _) then false -//│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.14: if term is Term and term is -//│ ║ ^^^^^^^ -//│ ║ l.15: Abs(_, _) then true -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.16: Var(_) then false -//│ ║ ^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.17: App(_, _) then false -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── class pattern of type `Term` does not match type `Abs | App | Var` -//│ ║ l.14: if term is Term and term is -//│ ║ ^^^^ -//│ ╟── but it flows into reference with expected type `Abs | App | Var` -//│ ║ l.14: if term is Term and term is -//│ ╙── ^^^^ -//│ fun is_value: Term -> Bool +//│ | | | | | | | Normalized UCS term: +//│ | | | | | | | case term*‡ of +//│ | | | | | | | refined Term*◊ -> +//│ | | | | | | | case term*‡ of +//│ | | | | | | | Abs*◊ -> true +//│ | | | | | | | _ -> +//│ | | | | | | | case term*‡ of +//│ | | | | | | | Var*◊ -> false +//│ | | | | | | | _ -> +//│ | | | | | | | case term*‡ of +//│ | | | | | | | App*◊ -> false +//│ | | | | | | | Post-processed UCS term: +//│ | | | | | | | case term*‡ of +//│ | | | | | | | refined Term*◊ -> +//│ | | | | | | | case term*‡ of +//│ | | | | | | | Abs*◊ -> true +//│ | | | | | | | Var*◊ -> false +//│ | | | | | | | App*◊ -> false +//│ fun is_value_automatic_refinement: (Abs | App | Var) -> Bool :e fun is_value'(term) = @@ -38,25 +54,12 @@ fun is_value'(term) = Abs(_, _) then true Var(_) then false //│ ╔══[ERROR] When scrutinee `term` is `Term` -//│ ║ l.37: if term is Term and term is +//│ ║ l.53: if term is Term and term is //│ ║ ^^^^ //│ ╟── Scrutinee `term` has 1 missing case -//│ ║ l.37: if term is Term and term is +//│ ║ l.53: if term is Term and term is //│ ║ ^^^^ //│ ╟── It can be class `App` //│ ║ l.6: class App(func: Term, arg: Term) extends Term //│ ╙── ^^^ -//│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.37: if term is Term and term is -//│ ║ ^^^^^^^ -//│ ║ l.38: Abs(_, _) then true -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.39: Var(_) then false -//│ ║ ^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── class pattern of type `Term` does not match type `Abs | Var` -//│ ║ l.37: if term is Term and term is -//│ ║ ^^^^ -//│ ╟── but it flows into reference with expected type `Abs | Var` -//│ ║ l.37: if term is Term and term is -//│ ╙── ^^^^ -//│ fun is_value': Term -> Bool +//│ fun is_value': (Abs | Var) -> Bool From 78850d7047a1462c1829d74a4ba0ad446587b43e Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sat, 13 Jan 2024 17:58:16 +0800 Subject: [PATCH 059/143] Move old desugarer, migrate most test cases, and fix minor bugs --- shared/src/main/scala/mlscript/Typer.scala | 34 +- .../scala/mlscript/pretyper/PreTyper.scala | 181 +++++--- .../main/scala/mlscript/pretyper/Scope.scala | 3 +- .../main/scala/mlscript/pretyper/Symbol.scala | 16 +- .../main/scala/mlscript/ucs/DesugarUCS.scala | 64 ++- .../scala/mlscript/ucs/context/CaseSet.scala | 8 +- .../scala/mlscript/ucs/context/Context.scala | 12 + .../mlscript/ucs/context/ScrutineeData.scala | 2 +- .../src/main/scala/mlscript/ucs/display.scala | 11 +- .../src/main/scala/mlscript/ucs/helpers.scala | 20 - .../scala/mlscript/ucs/{ => old}/Clause.scala | 2 +- .../mlscript/ucs/{ => old}/Conjunction.scala | 2 +- .../mlscript/ucs/{ => old}/Desugarer.scala | 8 +- .../mlscript/ucs/{ => old}/LetBinding.scala | 2 +- .../mlscript/ucs/{ => old}/MutCaseOf.scala | 8 +- .../mlscript/ucs/{ => old}/Scrutinee.scala | 2 +- .../main/scala/mlscript/ucs/old/helpers.scala | 28 ++ .../mlscript/ucs/stages/Desugaring.scala | 20 +- .../mlscript/ucs/stages/Normalization.scala | 2 +- .../mlscript/ucs/stages/Transformation.scala | 18 +- .../diff/codegen/AuxiliaryConstructors.mls | 47 +- shared/src/test/diff/codegen/Mixin.mls | 17 +- shared/src/test/diff/codegen/MixinCapture.mls | 4 +- shared/src/test/diff/codegen/Nested.mls | 36 +- shared/src/test/diff/codegen/NewMatching.mls | 56 +-- shared/src/test/diff/codegen/ValLet.mls | 2 +- .../test/diff/ecoop23/ComparePointPoly.mls | 2 +- .../test/diff/ecoop23/ExpressionProblem.mls | 2 +- shared/src/test/diff/ecoop23/Intro.mls | 2 +- .../test/diff/ecoop23/PolymorphicVariants.mls | 6 +- .../diff/ecoop23/SimpleRegionDSL_annot.mls | 24 +- .../test/diff/ecoop23/SimpleRegionDSL_raw.mls | 34 +- shared/src/test/diff/fcp/QML_exist_nu.mls | 2 +- shared/src/test/diff/gadt/Exp1.mls | 27 +- shared/src/test/diff/gadt/Exp2.mls | 4 +- shared/src/test/diff/gadt/ThisMatching.mls | 82 ++-- shared/src/test/diff/nu/Andong.mls | 2 +- shared/src/test/diff/nu/ArrayProg.mls | 54 ++- shared/src/test/diff/nu/BadUCS.mls | 91 ++-- .../test/diff/nu/BasicClassInheritance.mls | 2 +- shared/src/test/diff/nu/BasicClasses.mls | 2 +- shared/src/test/diff/nu/CaseExpr.mls | 73 +-- shared/src/test/diff/nu/ClassSignatures.mls | 2 +- shared/src/test/diff/nu/ClassesInMixins.mls | 28 +- shared/src/test/diff/nu/CommaOperator.mls | 51 ++- shared/src/test/diff/nu/CtorSubtraction.mls | 2 +- shared/src/test/diff/nu/Eval.mls | 14 +- shared/src/test/diff/nu/EvalNegNeg.mls | 2 +- .../test/diff/nu/ExpressionProblem_repro.mls | 2 +- .../test/diff/nu/ExpressionProblem_small.mls | 2 +- shared/src/test/diff/nu/FilterMap.mls | 11 +- shared/src/test/diff/nu/FlatIfThenElse.mls | 59 ++- shared/src/test/diff/nu/FlatMonads.mls | 32 +- shared/src/test/diff/nu/FunnyIndet.mls | 10 +- shared/src/test/diff/nu/GADTMono.mls | 2 +- shared/src/test/diff/nu/GenericClasses.mls | 2 +- shared/src/test/diff/nu/GenericModules.mls | 2 +- shared/src/test/diff/nu/HeungTung.mls | 28 +- shared/src/test/diff/nu/Huawei1.mls | 2 +- shared/src/test/diff/nu/InterfaceMono.mls | 125 +++--- shared/src/test/diff/nu/Interfaces.mls | 165 ++++--- shared/src/test/diff/nu/LetRec.mls | 5 +- shared/src/test/diff/nu/ListConsNil.mls | 4 +- shared/src/test/diff/nu/LitMatch.mls | 6 +- shared/src/test/diff/nu/MissingTypeArg.mls | 2 +- shared/src/test/diff/nu/NamedArgs.mls | 46 +- shared/src/test/diff/nu/New.mls | 12 + shared/src/test/diff/nu/NewNew.mls | 31 +- shared/src/test/diff/nu/Object.mls | 20 +- shared/src/test/diff/nu/OpLam.mls | 8 +- shared/src/test/diff/nu/OptionFilter.mls | 4 +- shared/src/test/diff/nu/OverrideShorthand.mls | 2 +- shared/src/test/diff/nu/ParamPassing.mls | 40 +- .../test/diff/nu/PolymorphicVariants_Alt.mls | 8 +- .../test/diff/nu/PostHocMixinSignature.mls | 6 +- .../test/diff/nu/PrivateMemberOverriding.mls | 2 +- shared/src/test/diff/nu/RefinedPattern.mls | 2 +- shared/src/test/diff/nu/SelfRec.mls | 2 +- shared/src/test/diff/nu/Subscripts.mls | 2 +- shared/src/test/diff/nu/TODO_Classes.mls | 2 +- shared/src/test/diff/nu/Unapply.mls | 2 +- shared/src/test/diff/nu/UndefMatching.mls | 2 +- shared/src/test/diff/nu/WeirdUnions.mls | 2 +- shared/src/test/diff/nu/i180.mls | 2 +- shared/src/test/diff/nu/repro0.mls | 5 +- shared/src/test/diff/nu/repro1.mls | 2 +- shared/src/test/diff/nu/repro_EvalNegNeg.mls | 8 +- .../diff/nu/repro_PolymorphicVariants.mls | 2 +- .../test/diff/pretyper/ucs/RecordPattern.mls | 6 +- .../pretyper/ucs/SpecilizationCollision.mls | 4 +- .../diff/pretyper/ucs/examples/Option.mls | 4 +- shared/src/test/diff/ucs/AppSplits.mls | 52 ++- .../src/test/diff/ucs/CrossBranchCapture.mls | 44 +- shared/src/test/diff/ucs/DirectLines.mls | 59 ++- shared/src/test/diff/ucs/ElseIf.mls | 78 +++- shared/src/test/diff/ucs/ErrorMessage.mls | 14 +- shared/src/test/diff/ucs/Exhaustiveness.mls | 72 ++- shared/src/test/diff/ucs/Humiliation.mls | 285 +++++++++++- shared/src/test/diff/ucs/Hygiene.mls | 25 +- shared/src/test/diff/ucs/HygienicBindings.mls | 130 +++--- shared/src/test/diff/ucs/InterleavedLet.mls | 217 ++++++++- shared/src/test/diff/ucs/JSON.mls | 319 ------------- shared/src/test/diff/ucs/LeadingAnd.mls | 28 +- shared/src/test/diff/ucs/LitUCS.mls | 2 +- shared/src/test/diff/ucs/MultiwayIf.mls | 2 +- shared/src/test/diff/ucs/NestedBranches.mls | 159 ++++--- shared/src/test/diff/ucs/NestedOpSplits.mls | 14 +- shared/src/test/diff/ucs/NestedPattern.mls | 88 ++-- .../src/test/diff/ucs/NuPlainConditionals.mls | 53 ++- shared/src/test/diff/ucs/Or.mls | 15 +- .../src/test/diff/ucs/OverlappedBranches.mls | 21 + shared/src/test/diff/ucs/ParseFailures.mls | 12 +- .../src/test/diff/ucs/PlainConditionals.mls | 118 +++-- shared/src/test/diff/ucs/SimpleUCS.mls | 420 +++++++++--------- shared/src/test/diff/ucs/SplitAfterOp.mls | 287 +++++++----- shared/src/test/diff/ucs/SplitAnd.mls | 49 +- shared/src/test/diff/ucs/SplitAroundOp.mls | 41 +- shared/src/test/diff/ucs/SplitBeforeOp.mls | 91 +++- shared/src/test/diff/ucs/SplitOps.mls | 140 +++--- shared/src/test/diff/ucs/SplitScrutinee.mls | 10 +- shared/src/test/diff/ucs/ThenIndent.mls | 15 +- shared/src/test/diff/ucs/Tree.mls | 2 +- shared/src/test/diff/ucs/TrivialIf.mls | 66 ++- shared/src/test/diff/ucs/WeirdIf.mls | 80 ++-- shared/src/test/diff/ucs/WeirdSplit.mls | 42 +- shared/src/test/diff/ucs/Wildcard.mls | 71 ++- shared/src/test/diff/ucs/zipWith.mls | 6 +- todo.md | 133 ++++++ 128 files changed, 3088 insertions(+), 1970 deletions(-) rename shared/src/main/scala/mlscript/ucs/{ => old}/Clause.scala (98%) rename shared/src/main/scala/mlscript/ucs/{ => old}/Conjunction.scala (99%) rename shared/src/main/scala/mlscript/ucs/{ => old}/Desugarer.scala (99%) rename shared/src/main/scala/mlscript/ucs/{ => old}/LetBinding.scala (98%) rename shared/src/main/scala/mlscript/ucs/{ => old}/MutCaseOf.scala (99%) rename shared/src/main/scala/mlscript/ucs/{ => old}/Scrutinee.scala (97%) create mode 100644 shared/src/main/scala/mlscript/ucs/old/helpers.scala delete mode 100644 shared/src/test/diff/ucs/JSON.mls create mode 100644 todo.md diff --git a/shared/src/main/scala/mlscript/Typer.scala b/shared/src/main/scala/mlscript/Typer.scala index 7c95b177..da37ad1c 100644 --- a/shared/src/main/scala/mlscript/Typer.scala +++ b/shared/src/main/scala/mlscript/Typer.scala @@ -15,7 +15,7 @@ import mlscript.Message._ * In order to turn the resulting CompactType into a mlscript.Type, we use `expandCompactType`. */ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val newDefs: Bool) - extends ucs.Desugarer with TypeSimplifier { + extends mlscript.ucs.old.Desugarer with TypeSimplifier { def funkyTuples: Bool = false def doFactorize: Bool = false @@ -1058,21 +1058,29 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne val argProv = tp(args.toLoc, "argument list") con(new_ty, FunctionType(typeTerm(args).withProv(argProv), res)(noProv), res) case App(App(Var("is"), _), _) => // * Old-style operators - val desug = If(IfThen(term, Var("true")), S(Var("false"))) - term.desugaredTerm = S(desug) - typeTerm(desug) + typeTerm(term.desugaredTerm.getOrElse { + val desug = If(IfThen(term, Var("true")), S(Var("false"))) + term.desugaredTerm = S(desug) + desug + }) case App(Var("is"), _) => - val desug = If(IfThen(term, Var("true")), S(Var("false"))) - term.desugaredTerm = S(desug) - typeTerm(desug) + typeTerm(term.desugaredTerm.getOrElse { + val desug = If(IfThen(term, Var("true")), S(Var("false"))) + term.desugaredTerm = S(desug) + desug + }) case App(App(Var("and"), PlainTup(lhs)), PlainTup(rhs)) => // * Old-style operators - val desug = If(IfThen(lhs, rhs), S(Var("false"))) - term.desugaredTerm = S(desug) - typeTerm(desug) + typeTerm(term.desugaredTerm.getOrElse { + val desug = If(IfThen(lhs, rhs), S(Var("false"))) + term.desugaredTerm = S(desug) + desug + }) case App(Var("and"), PlainTup(lhs, rhs)) => - val desug = If(IfThen(lhs, rhs), S(Var("false"))) - term.desugaredTerm = S(desug) - typeTerm(desug) + typeTerm(term.desugaredTerm.getOrElse { + val desug = If(IfThen(lhs, rhs), S(Var("false"))) + term.desugaredTerm = S(desug) + desug + }) case App(f: Term, a @ Tup(fields)) if (fields.exists(x => x._1.isDefined)) => def getLowerBoundFunctionType(t: SimpleType): List[FunctionType] = t.unwrapProvs match { case PolymorphicType(_, AliasOf(fun_ty @ FunctionType(_, _))) => diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index 8b4ba5ec..903b216a 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -1,6 +1,7 @@ package mlscript.pretyper import collection.mutable.{Set => MutSet} +import collection.immutable.SortedMap import symbol._ import mlscript._, utils._, shorthands._, Diagnostic.PreTyping, Message.MessageContext, ucs.DesugarUCS import scala.annotation.tailrec @@ -8,69 +9,86 @@ import scala.annotation.tailrec class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with Diagnosable with DesugarUCS { import PreTyper._ - private def extractParameters(fields: Term): Ls[LocalTermSymbol] = - trace(s"extractParameters <== $fields") { - fields match { - case nme: Var => new LocalTermSymbol(nme) :: Nil - case Tup(arguments) => - arguments.flatMap { - case (S(nme: Var), Fld(_, _)) => new LocalTermSymbol(nme) :: Nil - case (_, Fld(_, nme: Var)) => new LocalTermSymbol(nme) :: Nil - case (_, Fld(_, Bra(false, term))) => extractParameters(term) - case (_, Fld(_, tuple @ Tup(_))) => extractParameters(tuple) - case (_, Fld(_, Asc(term, _))) => extractParameters(term) - case (_, Fld(_, _: Lit)) => Nil - case (_, Fld(_, App(Var(name), parameters))) if name.headOption.forall(_.isUpper) => - extractParameters(parameters) - case (_, Fld(_, parameter)) => - println(s"unknown parameter: $parameter") - ??? - } - case PlainTup(arguments @ _*) => - arguments.map { - case nme: Var => new LocalTermSymbol(nme) - case other => println("Unknown parameters: " + other.toString); ??? // TODO: bad - }.toList - case other => println("Unknown parameters: " + other.toString); ??? // TODO: bad - } - }(rs => s"extractParameters ==> ${rs.iterator.map(_.name).mkString(", ")}") + /** A shorthand function to raise errors without specifying the source. */ + protected override def raiseError(messages: (Message -> Opt[Loc])*): Unit = + raiseError(PreTyping, messages: _*) - // `traverseIf` is meaningless because it represents patterns with terms. + /** A shorthand function to raise warnings without specifying the source. */ + protected override def raiseWarning(messages: (Message -> Opt[Loc])*): Unit = + raiseWarning(PreTyping, messages: _*) + + private def extractParameters(fields: Term): Ls[LocalTermSymbol] = { + def rec(term: Term): Iterator[LocalTermSymbol] = term match { + case nme: Var => Iterator.single(new LocalTermSymbol(nme)) + case Bra(false, term) => rec(term) + case Bra(true, Rcd(fields)) => + fields.iterator.flatMap { case (_, Fld(_, pat)) => rec(pat) } + case Asc(term, _) => rec(term) + case literal: Lit => + raiseWarning(msg"literal patterns are ignored" -> literal.toLoc) + Iterator.empty + case App(Var(name), parameters) if name.isCapitalized => rec(parameters) + case Tup(arguments) => + arguments.iterator.flatMap { + case (S(nme: Var), Fld(_, _)) => Iterator.single(new LocalTermSymbol(nme)) + case (N, Fld(_, term)) => rec(term) + } + case PlainTup(arguments @ _*) => arguments.iterator.flatMap(extractParameters) + case other => + raiseError(msg"unsupported pattern shape" -> other.toLoc) + Iterator.empty + } + val rs = rec(fields).toList + println(s"extractParameters ${fields.showDbg} ==> ${rs.iterator.map(_.name).mkString(", ")}") + rs + } protected def resolveVar(v: Var)(implicit scope: Scope): Unit = - trace(s"resolveVar(name = \"$v\")") { - scope.getTermSymbol(v.name) match { - case S(sym: LocalTermSymbol) => - println(s"Resolve variable $v to a local term.") - v.symbol = sym - case S(sym: DefinedTermSymbol) => - println(s"Resolve variable $v to a defined term.") - v.symbol = sym - case S(sym: ModuleSymbol) => - println(s"Resolve variable $v to a module.") - v.symbol = sym - case N => - scope.getTypeSymbol(v.name) match { - case S(sym: ClassSymbol) => - println(s"Resolve variable $v to a class.") - v.symbol = sym - case S(_) => - raiseError(PreTyping, msg"Name ${v.name} refers to a type" -> v.toLoc) - case N => - raiseError(PreTyping, msg"Variable ${v.name} not found in scope" -> v.toLoc) - } - } - }() + scope.getTermSymbol(v.name) match { + case S(sym: LocalTermSymbol) => + println(s"resolveVar `${v.name}` ==> local term") + v.symbol = sym + case S(sym: DefinedTermSymbol) => + println(s"resolveVar `${v.name}` ==> defined term") + v.symbol = sym + case S(sym: ModuleSymbol) => + println(s"resolveVar `${v.name}` ==> module") + v.symbol = sym + case N => + scope.getTypeSymbol(v.name) match { + case S(sym: ClassSymbol) => + println(s"resolveVar `${v.name}` ==> class") + v.symbol = sym + case S(_) => + raiseError(PreTyping, msg"identifier `${v.name}` is resolved to a type" -> v.toLoc) + case N => + raiseError(PreTyping, msg"identifier `${v.name}` not found" -> v.toLoc) + } + } protected def traverseVar(v: Var)(implicit scope: Scope): Unit = - trace(s"traverseVar(name = \"$v\")") { + trace(s"traverseVar(name = \"${v.name}\")") { v.symbolOption match { case N => resolveVar(v) case S(symbol) => scope.getSymbols(v.name) match { - case Nil => raiseError(PreTyping, msg"Variable ${v.name} not found in scope. It is possibly a free variable." -> v.toLoc) + case Nil => raiseError(PreTyping, msg"identifier `${v.name}` not found" -> v.toLoc) case symbols if symbols.contains(symbol) => () - case _ => - raiseError(PreTyping, msg"Variable ${v.name} refers to different symbols." -> v.toLoc) + case symbols => + def toNameLoc(symbol: Symbol): (Str, Opt[Loc]) = symbol match { + case ds: DummyClassSymbol => s"`${ds.name}`" -> N + case ts: TypeSymbol => s"`${ts.name}`" -> ts.defn.toLoc + case ls: LocalTermSymbol => s"local `${ls.name}`" -> ls.nme.toLoc + case dt: DefinedTermSymbol => s"`${dt.name}`" -> dt.defn.toLoc + } + val (name, loc) = toNameLoc(symbol) + raiseError(PreTyping, + (msg"identifier ${v.name} refers to different symbols." -> v.toLoc :: + msg"it is resolved to $name" -> loc :: + symbols.map { s => + val (name, loc) = toNameLoc(s) + msg"it was previously resolved to $name" -> loc + }): _* + ) } } }() @@ -107,6 +125,30 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D case CaseOf(trm, cases) => case With(trm, fields) => traverseTerm(trm); traverseTerm(fields) case Where(body, where) => ??? // TODO: When? + // begin UCS shorthands + // * Old-style operators + case App(App(Var("is"), _), _) => + println(s"found UCS shorthand: ${term.showDbg}") + val desugared = If(IfThen(term, Var("true")), S(Var("false"))) + traverseIf(desugared) + term.desugaredTerm = desugared.desugaredTerm + case App(Var("is"), _) => + println(s"found UCS shorthand: ${term.showDbg}") + val desugared = If(IfThen(term, Var("true")), S(Var("false"))) + traverseIf(desugared) + term.desugaredTerm = desugared.desugaredTerm + // * Old-style operators + case App(App(Var("and"), PlainTup(lhs)), PlainTup(rhs)) => + println(s"found UCS shorthand: ${term.showDbg}") + val desugared = If(IfThen(lhs, rhs), S(Var("false"))) + traverseIf(desugared) + term.desugaredTerm = desugared.desugaredTerm + case App(Var("and"), PlainTup(lhs, rhs)) => + println(s"found UCS shorthand: ${term.showDbg}") + val desugared = If(IfThen(lhs, rhs), S(Var("false"))) + traverseIf(desugared) + term.desugaredTerm = desugared.desugaredTerm + // end UCS shorthands case App(lhs, rhs) => traverseTerm(lhs); traverseTerm(rhs) case Test(trm, ty) => traverseTerm(trm) case _: Lit | _: Super => () @@ -136,9 +178,22 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D println(typeSymbols.iterator.map(_.name).mkString("type symbols: {", ", ", "}")) // val scopeWithTypes = statements.iterator.flatMap(filterNuTypeDef).foldLeft(parentScope.derive)(_ + TypeSymbol(_)) // Pass 1.1: Resolve subtyping relations. Build a graph and compute base types of each type. - val edges = typeSymbols.foldLeft(Map.empty[TypeSymbol, Ls[TypeSymbol]]) { case (acc, self) => - acc + (self -> extractSuperTypes(self.defn.parents).map { nme => - scopeWithTypes.getTypeSymbol(nme.name).getOrElse(???) }) + // Keep a stable ordering of type symbols when printing the graph. + implicit val ord: Ordering[TypeSymbol] = new Ordering[TypeSymbol] { + override def compare(x: TypeSymbol, y: TypeSymbol): Int = + x.name.compareTo(y.name) + } + // Collect inheritance relations, represented by a map from a type symbol + // to its base types. If a type symbol is not found, we will ignore it + // and report the error (but not fatal). + val edges = typeSymbols.foldLeft(SortedMap.empty[TypeSymbol, Ls[TypeSymbol]]) { case (acc, self) => + acc + (self -> extractSuperTypes(self.defn.parents).flatMap { nme => + val maybeSymbol = scopeWithTypes.getTypeSymbol(nme.name) + if (maybeSymbol.isEmpty) { + raiseError(PreTyping, msg"could not find definition `${nme.name}`" -> nme.toLoc) + } + maybeSymbol + }) } printGraph(edges, println, "inheritance relations", "->") transitiveClosure(edges).foreachEntry { (self, bases) => @@ -175,6 +230,12 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D println(s"unknown statement: ${other.getClass.getSimpleName}") acc } + // Pass 2.5: Traverse type definitions + statements.iterator.flatMap { case defn: NuTypeDef => S(defn); case _ => N }.foreach { defn => + val parameters = defn.params.map(extractParameters).getOrElse(Nil) + val thisSymbol = new LocalTermSymbol(Var("this")) + traverseTypeDefinition(TypeSymbol(defn), defn)(completeScope ++ (thisSymbol :: parameters)) + } println(thingsToTraverse.iterator.map { case (L(term), _) => term.showDbg case (R(symbol), _) => symbol.name @@ -214,7 +275,7 @@ object PreTyper { case Nil => results.reverse case (nme: Var) :: tail => rec(nme :: results, tail) case (TyApp(ty, _)) :: tail => rec(results, ty :: tail) - case (App(nme @ Var(_), Tup(_))) :: tail => rec(nme :: results, tail) + case (App(term, Tup(_))) :: tail => rec(results, term :: tail) case other :: _ => println(s"Unknown parent type: $other"); ??? } rec(Nil, parents) @@ -242,7 +303,7 @@ object PreTyper { rec(Nil, ty).reverse } - def transitiveClosure[A](graph: Map[A, List[A]]): Map[A, List[A]] = { + def transitiveClosure[A](graph: Map[A, List[A]])(implicit ord: Ordering[A]): SortedMap[A, List[A]] = { def dfs(vertex: A, visited: Set[A]): Set[A] = { if (visited.contains(vertex)) visited else graph.getOrElse(vertex, List()) @@ -251,7 +312,7 @@ object PreTyper { graph.keys.map { vertex => val closure = dfs(vertex, Set()) vertex -> (closure - vertex).toList - }.toMap + }.toSortedMap } def printGraph(graph: Map[TypeSymbol, List[TypeSymbol]], print: (=> Any) => Unit, title: String, arrow: String): Unit = { diff --git a/shared/src/main/scala/mlscript/pretyper/Scope.scala b/shared/src/main/scala/mlscript/pretyper/Scope.scala index 56c94aa6..89b30bd6 100644 --- a/shared/src/main/scala/mlscript/pretyper/Scope.scala +++ b/shared/src/main/scala/mlscript/pretyper/Scope.scala @@ -98,6 +98,7 @@ object Scope { val trueSymbol = new ModuleSymbol(trueDefn) val falseSymbol = new ModuleSymbol(falseDefn) val nothingSymbol = new TypeAliasSymbol(nothingDefn) + val commaSymbol = new LocalTermSymbol(Var(",")) Scope.from( """true,false,document,window,typeof,toString,not,succ,log,discard,negate, |round,add,sub,mul,div,sqrt,lt,le,gt,ge,slt,sle,sgt,sge,length,concat,eq, @@ -108,7 +109,7 @@ object Scope { .iterator .map(_.trim) .map(name => new LocalTermSymbol(Var(name))) - .concat(trueSymbol :: falseSymbol :: nothingSymbol :: Nil) + .concat(trueSymbol :: falseSymbol :: nothingSymbol :: commaSymbol :: Nil) ) } } diff --git a/shared/src/main/scala/mlscript/pretyper/Symbol.scala b/shared/src/main/scala/mlscript/pretyper/Symbol.scala index 48ae45c7..41bad997 100644 --- a/shared/src/main/scala/mlscript/pretyper/Symbol.scala +++ b/shared/src/main/scala/mlscript/pretyper/Symbol.scala @@ -21,7 +21,7 @@ package object symbol { } sealed trait TypeSymbol extends Symbol { - val defn: NuTypeDef + def defn: NuTypeDef override def name: Str = defn.name @@ -50,6 +50,18 @@ package object symbol { def unapply(symbol: TypeSymbol): Opt[NuTypeDef] = S(symbol.defn) } + /** + * When a type symbol is not defined and we must need a symbol in some + * scenarios, we use a dummy symbol to represent it. + * + * @param nme the name of the expect type symbol. + */ + final class DummyClassSymbol(val nme: Var) extends TypeSymbol { + override def defn: NuTypeDef = die + + override def name: Str = nme.name + } + final class ClassSymbol(override val defn: NuTypeDef) extends TypeSymbol { require(defn.kind === Cls) } @@ -72,7 +84,7 @@ package object symbol { sealed trait TermSymbol extends Symbol with Matchable - class DefinedTermSymbol(defn: NuFunDef) extends TermSymbol { + class DefinedTermSymbol(val defn: NuFunDef) extends TermSymbol { override def name: Str = defn.name def body: Term \/ Type = defn.rhs diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index 2617c402..40567bbd 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -32,12 +32,19 @@ trait DesugarUCS extends Transformation /** Associate the given `Var` with a fresh `ValueSymbol`. */ def withFreshSymbol: Var = nme.withSymbol(freshSymbol(nme)) - private def requireClassLikeSymbol(symbol: TypeSymbol): TypeSymbol = symbol match { - case symbol @ (_: TraitSymbol | _: ClassSymbol | _: ModuleSymbol) => symbol + /** + * Expect the given `symbol` to be a class-like symbol. If it is not, we + * get or create a dummy class symbol for it. The desugaring can continue + * and `Typer` will throw an error for this miuse. + */ + private def requireClassLikeSymbol(symbol: TypeSymbol)(implicit context: Context): TypeSymbol = symbol match { + case symbol @ (_: TraitSymbol | _: ClassSymbol | _: ModuleSymbol | _: DummyClassSymbol) => symbol case symbol: MixinSymbol => - throw new DesugaringException(msg"Mixins are not allowed in pattern" -> nme.toLoc :: Nil) + raiseError(msg"Mixins are not allowed in pattern" -> nme.toLoc) + context.getOrCreateDummyClassSymbol(nme) case symbol: TypeAliasSymbol => - throw new DesugaringException(msg"Type alias is not allowed in pattern" -> nme.toLoc :: Nil) + raiseError(msg"Type alias is not allowed in pattern" -> nme.toLoc) + context.getOrCreateDummyClassSymbol(nme) } /** @@ -45,13 +52,15 @@ trait DesugarUCS extends Transformation * * @param className the class name variable */ - def getClassLikeSymbol: TypeSymbol = { + def getClassLikeSymbol(implicit context: Context): TypeSymbol = { val symbol = nme.symbolOption match { case S(symbol: TypeSymbol) => requireClassLikeSymbol(symbol) - case S(symbol: TermSymbol) => throw new DesugaringException( - msg"variable ${nme.name} is not associated with a class symbol" -> nme.toLoc :: Nil) - case N => throw new DesugaringException( - msg"variable ${nme.name} is not associated with any symbols" -> nme.toLoc :: Nil) + case S(symbol: TermSymbol) => + raiseError(msg"variable ${nme.name} is not associated with a class symbol" -> nme.toLoc) + context.getOrCreateDummyClassSymbol(nme) + case N => + raiseError(msg"variable ${nme.name} is not associated with any symbols" -> nme.toLoc) + context.getOrCreateDummyClassSymbol(nme) } println(s"getClassLikeSymbol: ${nme.name} ==> ${symbol.showDbg}") symbol @@ -82,10 +91,25 @@ trait DesugarUCS extends Transformation ) } + /** + * If the `Var` is associated with a term symbol, returns it. Otherwise, + * resolve the term symbol and associate the `Var` with the term symbol. + */ + def getOrResolveTermSymbol(implicit scope: Scope): TermSymbol = { + nme.symbolOption match { + case N => resolveTermSymbol + case S(symbol: TermSymbol) => symbol + case S(otherSymbol) => + raiseError(msg"identifier `${nme.name}` should be a term" -> nme.toLoc) + freshSymbol(nme) // TODO: Maybe we should maintain a "lost" symbol map. + } + } + /** Associate the `Var` with a term symbol and returns the term symbol. */ def resolveTermSymbol(implicit scope: Scope): TermSymbol = { val symbol = scope.getTermSymbol(nme.name).getOrElse { - throw new DesugaringException(msg"Undefined symbol found in patterns." -> nme.toLoc :: Nil) + raiseError(msg"identifier `${nme.name}` not found" -> nme.toLoc) + freshSymbol(nme) // TODO: Maybe we should maintain a "lost" symbol map. } nme.symbol = symbol symbol @@ -95,17 +119,23 @@ trait DesugarUCS extends Transformation def withResolvedTermSymbol(implicit scope: Scope): Var = { nme.resolveTermSymbol; nme } /** Associate the `Var` with a class like symbol and returns the class like symbol. */ - def resolveClassLikeSymbol(implicit scope: Scope): TypeSymbol = { + def resolveClassLikeSymbol(implicit scope: Scope, context: Context): TypeSymbol = { val symbol = scope.getTypeSymbol(nme.name) match { case S(symbol) => requireClassLikeSymbol(symbol) - case N => throw new DesugaringException(msg"Undefined symbol found in patterns." -> nme.toLoc :: Nil) + case N => + raiseError(msg"type identifier `${nme.name}` not found" -> nme.toLoc) + context.getOrCreateDummyClassSymbol(nme) } nme.symbol = symbol symbol } - /** Associate the `Var` with a class like symbol and returns the same `Var`. */ - def withResolvedClassLikeSymbol(implicit scope: Scope): Var = { nme.resolveClassLikeSymbol; nme } + /** + * Associate the `Var` with a class like symbol and returns the same `Var`. + * We might be able to remove this function. Currently, it is only used for + * resolving `Var("true")` and `Var("false")` in `Desugaring`. */ + def withResolvedClassLikeSymbol(implicit scope: Scope, context: Context): Var = + { nme.resolveClassLikeSymbol; nme } } protected def traverseIf(`if`: If)(implicit scope: Scope): Unit = { @@ -177,9 +207,9 @@ trait DesugarUCS extends Transformation traverseSplit(continuation)(scope.withEntries(patternSymbols)) traverseSplit(tail) case Split.Let(isRec, name, rhs, tail) => - val scopeWithName = scope + name.symbol - traverseTerm(rhs)(if (isRec) scopeWithName else scope) - traverseSplit(tail)(scopeWithName) + val recScope = scope + name.symbol + traverseTerm(rhs)(if (isRec) recScope else scope) + traverseSplit(tail)(recScope) case Split.Else(default) => traverseTerm(default) case Split.Nil => () } diff --git a/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala b/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala index 313eb615..b31f0928 100644 --- a/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala +++ b/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala @@ -3,10 +3,16 @@ package mlscript.ucs.context import mlscript.{Lit, Loc} import mlscript.pretyper.symbol.TypeSymbol import mlscript.utils._, shorthands._ +import mlscript.pretyper.symbol.DummyClassSymbol sealed abstract class Pattern { override def toString(): String = this match { - case Pattern.ClassLike(symbol) => s"${symbol.defn.kind.str} `${symbol.name}`" + case Pattern.ClassLike(symbol) => + val kind = symbol match { + case _: DummyClassSymbol => "dummy class" + case _ => symbol.defn.kind.str + } + s"$kind `${symbol.name}`" case Pattern.Tuple() => "tuple" case Pattern.Literal(literal) => s"literal $literal" } diff --git a/shared/src/main/scala/mlscript/ucs/context/Context.scala b/shared/src/main/scala/mlscript/ucs/context/Context.scala index bc143961..3e594157 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Context.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Context.scala @@ -6,8 +6,10 @@ import mlscript.pretyper.symbol.TypeSymbol import mlscript.pretyper.Scope import mlscript.ucs.VariableGenerator import mlscript.utils._, shorthands._ +import mlscript.pretyper.symbol.DummyClassSymbol class Context(originalTerm: If) { + /** The prefix of all prefixes. */ private val prefix = Context.freshPrefix() private val cachePrefix = prefix + "$cache$" private val scrutineePrefix = prefix + "$scrut$" @@ -30,6 +32,16 @@ class Context(originalTerm: If) { val freshTest: VariableGenerator = new VariableGenerator(testPrefix) val freshShadowed: VariableGenerator = new VariableGenerator(shadowPrefix) + // Symbol Management + // ================= + + private val dummyClassSymbols: MutMap[Var, DummyClassSymbol] = MutMap.empty + def getOrCreateDummyClassSymbol(nme: Var): DummyClassSymbol = + dummyClassSymbols.getOrElseUpdate(nme, new DummyClassSymbol(nme)) + + // Scrutinee Management + // ==================== + /** The buffer contains all `ScrutineeData` created within this context. */ private val scrutineeBuffer: Buffer[ScrutineeData] = Buffer.empty diff --git a/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala b/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala index 7f4b08b0..4ca6aede 100644 --- a/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala +++ b/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala @@ -152,7 +152,7 @@ object ScrutineeData { } private implicit val classLikeSymbolOrdering: Ordering[TypeSymbol] = new Ordering[TypeSymbol] { - override def compare(x: TypeSymbol, y: TypeSymbol): Int = x.defn.name.compareTo(y.defn.name) + override def compare(x: TypeSymbol, y: TypeSymbol): Int = x.name.compareTo(y.name) } private implicit val literalOrdering: Ordering[Lit] = new Ordering[Lit] { diff --git a/shared/src/main/scala/mlscript/ucs/display.scala b/shared/src/main/scala/mlscript/ucs/display.scala index 03bd31ca..7ffffe28 100644 --- a/shared/src/main/scala/mlscript/ucs/display.scala +++ b/shared/src/main/scala/mlscript/ucs/display.scala @@ -106,10 +106,13 @@ package object display { * - `‡` if the variable is associated with a term symbol and has a scrutinee. */ def showNormalizedTerm(term: Term)(implicit context: Context): String = { - def showTerm(term: Term): Lines = term match { - case let: Let => showLet(let) - case caseOf: CaseOf => showCaseOf(caseOf) - case other => (0, other.showDbg) :: Nil + def showTerm(term: Term): Lines = term.desugaredTerm match { + case N => term match { + case let: Let => showLet(let) + case caseOf: CaseOf => showCaseOf(caseOf) + case other => (0, other.showDbg) :: Nil + } + case S(desugaredTerm) => "[desugared]" @: showTerm(desugaredTerm) } def showScrutinee(term: Term): Str = term match { case vari: Var => showVar(vari) diff --git a/shared/src/main/scala/mlscript/ucs/helpers.scala b/shared/src/main/scala/mlscript/ucs/helpers.scala index 0babee09..74491123 100644 --- a/shared/src/main/scala/mlscript/ucs/helpers.scala +++ b/shared/src/main/scala/mlscript/ucs/helpers.scala @@ -85,24 +85,4 @@ object helpers { } case _ => (term, N) } - - /** - * Generate a chain of `Let` from a list of bindings. - * - * @param bindings a list of bindings, - * @param body the final body - */ - def mkBindings(bindings: Ls[LetBinding], body: Term, defs: Set[Var]): Term = { - def rec(bindings: Ls[LetBinding], defs: Set[Var]): Term = - bindings match { - case Nil => body - case LetBinding(_, isRec, nameVar, value) :: tail => - if (defs.contains(nameVar)) { - rec(tail, defs) - } else { - Let(isRec, nameVar, value, rec(tail, defs + nameVar)) - } - } - rec(bindings, defs) - } } diff --git a/shared/src/main/scala/mlscript/ucs/Clause.scala b/shared/src/main/scala/mlscript/ucs/old/Clause.scala similarity index 98% rename from shared/src/main/scala/mlscript/ucs/Clause.scala rename to shared/src/main/scala/mlscript/ucs/old/Clause.scala index b91354d9..c9f6243a 100644 --- a/shared/src/main/scala/mlscript/ucs/Clause.scala +++ b/shared/src/main/scala/mlscript/ucs/old/Clause.scala @@ -1,4 +1,4 @@ -package mlscript.ucs +package mlscript.ucs.old import mlscript._ import mlscript.utils._ diff --git a/shared/src/main/scala/mlscript/ucs/Conjunction.scala b/shared/src/main/scala/mlscript/ucs/old/Conjunction.scala similarity index 99% rename from shared/src/main/scala/mlscript/ucs/Conjunction.scala rename to shared/src/main/scala/mlscript/ucs/old/Conjunction.scala index f0928dcc..1cfbf1f6 100644 --- a/shared/src/main/scala/mlscript/ucs/Conjunction.scala +++ b/shared/src/main/scala/mlscript/ucs/old/Conjunction.scala @@ -1,4 +1,4 @@ -package mlscript.ucs +package mlscript.ucs.old import mlscript._, utils._, shorthands._ import Clause._, helpers._ diff --git a/shared/src/main/scala/mlscript/ucs/Desugarer.scala b/shared/src/main/scala/mlscript/ucs/old/Desugarer.scala similarity index 99% rename from shared/src/main/scala/mlscript/ucs/Desugarer.scala rename to shared/src/main/scala/mlscript/ucs/old/Desugarer.scala index 7f2d3004..92b18dd3 100644 --- a/shared/src/main/scala/mlscript/ucs/Desugarer.scala +++ b/shared/src/main/scala/mlscript/ucs/old/Desugarer.scala @@ -1,12 +1,13 @@ -package mlscript.ucs +package mlscript.ucs.old import scala.collection.mutable.{Map => MutMap, HashMap} import scala.collection.mutable.Buffer import mlscript._, utils._, shorthands._ -import helpers._ +import mlscript.ucs.{DesugaringException, PartialTerm} +import mlscript.ucs.helpers._, helpers._ import Message.MessageContext -import mlscript.ucs.MutCaseOf.MutCase.Constructor +import MutCaseOf.MutCase.Constructor import scala.collection.mutable.ListBuffer /** @@ -359,6 +360,7 @@ class Desugarer extends TypeDefs { self: Typer => * @return the desugared term */ def desugarIf(elf: If)(implicit ctx: Ctx, raise: Raise): Term = traceUCS("[desugarIf]") { + raise(WarningReport(msg"old desugarer used" -> elf.toLoc :: Nil, false)) val superClassMap = getClassHierarchy() Desugarer.printGraph(superClassMap, printlnUCS, "Super-class map", "<:") val subClassMap = Desugarer.reverseGraph(superClassMap) diff --git a/shared/src/main/scala/mlscript/ucs/LetBinding.scala b/shared/src/main/scala/mlscript/ucs/old/LetBinding.scala similarity index 98% rename from shared/src/main/scala/mlscript/ucs/LetBinding.scala rename to shared/src/main/scala/mlscript/ucs/old/LetBinding.scala index 6e08b3ce..fb217c41 100644 --- a/shared/src/main/scala/mlscript/ucs/LetBinding.scala +++ b/shared/src/main/scala/mlscript/ucs/old/LetBinding.scala @@ -1,4 +1,4 @@ -package mlscript.ucs +package mlscript.ucs.old import mlscript._ import mlscript.utils._ diff --git a/shared/src/main/scala/mlscript/ucs/MutCaseOf.scala b/shared/src/main/scala/mlscript/ucs/old/MutCaseOf.scala similarity index 99% rename from shared/src/main/scala/mlscript/ucs/MutCaseOf.scala rename to shared/src/main/scala/mlscript/ucs/old/MutCaseOf.scala index a217907d..eac05684 100644 --- a/shared/src/main/scala/mlscript/ucs/MutCaseOf.scala +++ b/shared/src/main/scala/mlscript/ucs/old/MutCaseOf.scala @@ -1,4 +1,4 @@ -package mlscript.ucs +package mlscript.ucs.old import mlscript._ import mlscript.utils._ @@ -6,11 +6,11 @@ import mlscript.utils.shorthands._ import scala.collection.immutable.Set import scala.collection.mutable.{Map => MutMap, Set => MutSet, Buffer} -import helpers._ -import mlscript.ucs.MutCaseOf.Consequent +import mlscript.ucs.helpers._ +import MutCaseOf.Consequent import scala.collection.immutable import Desugarer.{ExhaustivenessMap, SuperClassMap} -import mlscript.ucs.Clause.MatchAny +import Clause.MatchAny sealed abstract class MutCaseOf extends WithBindings { def kind: Str = { diff --git a/shared/src/main/scala/mlscript/ucs/Scrutinee.scala b/shared/src/main/scala/mlscript/ucs/old/Scrutinee.scala similarity index 97% rename from shared/src/main/scala/mlscript/ucs/Scrutinee.scala rename to shared/src/main/scala/mlscript/ucs/old/Scrutinee.scala index 899c5629..9c9a4623 100644 --- a/shared/src/main/scala/mlscript/ucs/Scrutinee.scala +++ b/shared/src/main/scala/mlscript/ucs/old/Scrutinee.scala @@ -1,4 +1,4 @@ -package mlscript.ucs +package mlscript.ucs.old import mlscript.{Loc, SimpleTerm, Term, Var} import mlscript.utils.lastWords diff --git a/shared/src/main/scala/mlscript/ucs/old/helpers.scala b/shared/src/main/scala/mlscript/ucs/old/helpers.scala new file mode 100644 index 00000000..bbfc7989 --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/old/helpers.scala @@ -0,0 +1,28 @@ +package mlscript.ucs.old + +import scala.collection.mutable.{Set => MutSet} + +import mlscript._ +import mlscript.utils.shorthands._ + +object helpers { + /** + * Generate a chain of `Let` from a list of bindings. + * + * @param bindings a list of bindings, + * @param body the final body + */ + def mkBindings(bindings: Ls[LetBinding], body: Term, defs: Set[Var]): Term = { + def rec(bindings: Ls[LetBinding], defs: Set[Var]): Term = + bindings match { + case Nil => body + case LetBinding(_, isRec, nameVar, value) :: tail => + if (defs.contains(nameVar)) { + rec(tail, defs) + } else { + Let(isRec, nameVar, value, rec(tail, defs + nameVar)) + } + } + rec(bindings, defs) + } +} diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 2adae6fa..d7e81961 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -66,9 +66,9 @@ trait Desugaring { self: PreTyper => * A shorthand for making a true pattern, which is useful in desugaring * Boolean conditions. */ - private def truePattern(implicit scope: Scope) = + private def truePattern(implicit scope: Scope, context: Context) = c.Pattern.Class(Var("true").withResolvedClassLikeSymbol, false) - private def falsePattern(implicit scope: Scope) = + private def falsePattern(implicit scope: Scope, context: Context) = c.Pattern.Class(Var("false").withResolvedClassLikeSymbol, false) private def desugarTermSplit(split: s.TermSplit)(implicit termPart: PartialTerm, scope: Scope, context: Context): c.Split = @@ -170,9 +170,13 @@ trait Desugaring { self: PreTyper => * @param initialScope the scope before flattening the class pattern * @return a tuple of the augmented scope and a function that wrap a split */ - private def desugarClassPattern(pattern: s.ClassPattern, scrutineeVar: Var, initialScope: Scope, refined: Bool)(implicit context: Context): (Scope, c.Split => c.Branch) = { + private def desugarClassPattern( + pattern: s.ClassPattern, + scrutineeVar: Var, + initialScope: Scope, + refined: Bool)(implicit context: Context): (Scope, c.Split => c.Branch) = { val scrutinee = scrutineeVar.getOrCreateScrutinee.withAlias(scrutineeVar) - val patternClassSymbol = pattern.nme.resolveClassLikeSymbol(initialScope) + val patternClassSymbol = pattern.nme.resolveClassLikeSymbol(initialScope, context) val classPattern = scrutinee.getOrCreateClassPattern(patternClassSymbol) println(s"desugarClassPattern: ${scrutineeVar.name} is ${pattern.nme.name}") classPattern.addLocation(pattern.nme) @@ -292,6 +296,9 @@ trait Desugaring { self: PreTyper => private def desugarPatternSplit(scrutineeTerm: Term, split: s.PatternSplit)(implicit scope: Scope, context: Context): c.Split = { def rec(scrutineeVar: Var, split: s.PatternSplit)(implicit scope: Scope): c.Split = split match { + // TODO: We should resolve `scrutineeVar`'s symbol and make sure it is a term symbol in the + // beginning. So that we can call methods on the symbol directly. Now there are too many + // helper functions on `VarOps`. case s.Split.Cons(head, tail) => head.pattern match { case pattern @ s.AliasPattern(_, _) => @@ -336,13 +343,12 @@ trait Desugaring { self: PreTyper => val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope + nme.symbol, context) c.Branch(scrutineeVar, c.Pattern.Name(nme), continuation) :: rec(scrutineeVar, tail)(scope + nme.symbol) case pattern @ s.ClassPattern(nme, fields, rfd) => - println(s"find term symbol of $scrutineeVar in ${scope.showLocalSymbols}") - scrutineeVar.symbol = scope.getTermSymbol(scrutineeVar.name).getOrElse(???/*FIXME*/) + val scrutineeSymbol = scrutineeVar.getOrResolveTermSymbol // TODO: Useless. val (scopeWithAll, bindAll) = desugarClassPattern(pattern, scrutineeVar, scope, rfd) val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scopeWithAll, context) bindAll(continuation) :: rec(scrutineeVar, tail) case s.TuplePattern(fields) => - scrutineeVar.symbol = scope.getTermSymbol(scrutineeVar.name).getOrElse(???/*FIXME*/) + val scrutineeSymbol = scrutineeVar.getOrResolveTermSymbol // TODO: Useless. val (scopeWithAll, bindAll) = desugarTuplePattern(fields, scrutineeVar, scope) val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scopeWithAll, context) val withBindings = bindAll(continuation) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index a814b404..ffdf5fc3 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -12,7 +12,7 @@ import mlscript.{App, CaseOf, Fld, FldFlags, Let, Loc, Sel, Term, Tup, Var, StrL import mlscript.{CaseBranches, Case, Wildcard, NoCases} import mlscript.Message, Message.MessageContext import mlscript.utils._, shorthands._ -import mlscript.pretyper.Traceable +import mlscript.pretyper.{Diagnosable, Traceable} trait Normalization { self: DesugarUCS with Traceable => import Normalization._ diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index 54752168..5956e69a 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -1,10 +1,10 @@ package mlscript.ucs.stages -import mlscript.ucs.helpers +import mlscript.ucs.{DesugarUCS, helpers} import mlscript.{If, IfBody, IfBlock, IfElse, IfLet, IfOpApp, IfOpsApp, IfThen} import mlscript.{Term, Var, App, Tup, Lit, Fld, Loc} import mlscript.Diagnostic.PreTyping -import mlscript.pretyper.{Diagnosable, Traceable} +import mlscript.pretyper.Traceable import mlscript.ucs.syntax._ import mlscript.Message, Message._ import mlscript.utils._, shorthands._ @@ -19,7 +19,7 @@ import scala.collection.immutable * The AST in the paper is more flexible. For example, it allows interleaved * `let` bindings in operator splits. */ -trait Transformation { self: Traceable with Diagnosable => +trait Transformation { self: DesugarUCS with Traceable => import Transformation._ /** The entry point of transformation. */ @@ -70,7 +70,7 @@ trait Transformation { self: Traceable with Diagnosable => case (acc, R(NuFunDef(S(rec), nme, _, _, L(rhs)))) => acc ++ Split.Let(rec, nme, rhs, Split.Nil) case (acc, R(statement)) => - raiseError(PreTyping, msg"Unexpected statement in an if block" -> statement.toLoc) + raiseError(msg"Unexpected statement in an if block" -> statement.toLoc) acc } case IfOpsApp(lhs, opsRhss) => @@ -97,7 +97,7 @@ trait Transformation { self: Traceable with Diagnosable => opsRhss.iterator.flatMap { case Var("and") -> rhs => S(transformIfBody(rhs)) case op -> rhs => - raiseError(PreTyping, + raiseError( msg"cannot transform due to an illegal split operator ${op.name}" -> op.toLoc, msg"the following branch will be discarded" -> rhs.toLoc) N @@ -131,7 +131,9 @@ trait Transformation { self: Traceable with Diagnosable => case (pattern, N) => PatternBranch(pattern, transformIfBody(rhs)).toSplit } - case IfOpApp(lhs, op, rhs) => ??? // <-- Syntactic split of patterns are not supported. + case IfOpApp(lhs, op, rhs) => + raiseError(msg"Syntactic split of patterns are not supported" -> op.toLoc) + Split.Nil case IfOpsApp(lhs, opsRhss) => // BEGIN TEMPORARY PATCH // Generally, syntactic split of patterns are not supported. Examples @@ -180,7 +182,7 @@ trait Transformation { self: Traceable with Diagnosable => case (acc, R(NuFunDef(S(rec), nme, _, _, L(rhs)))) => acc ++ Split.Let(rec, nme, rhs, Split.Nil) case (acc, R(statement)) => - raiseError(PreTyping, msg"Unexpected statement in an if block" -> statement.toLoc) + raiseError(msg"Unexpected statement in an if block" -> statement.toLoc) acc } case IfElse(expr) => Split.default(expr) @@ -213,7 +215,7 @@ trait Transformation { self: Traceable with Diagnosable => case tuple: Tup => TuplePattern(transformTupleTerm(tuple)) case other => println(s"other $other") - raiseError(PreTyping, msg"Unknown pattern ${other.toString}" -> other.toLoc) + raiseError(msg"Unknown pattern ${other.showDbg}" -> other.toLoc) EmptyPattern(other) } diff --git a/shared/src/test/diff/codegen/AuxiliaryConstructors.mls b/shared/src/test/diff/codegen/AuxiliaryConstructors.mls index 36b8a2c8..ac763dbf 100644 --- a/shared/src/test/diff/codegen/AuxiliaryConstructors.mls +++ b/shared/src/test/diff/codegen/AuxiliaryConstructors.mls @@ -1,5 +1,4 @@ -:NewParser -:NewDefs +:PreTyper :js class A(x: Int) {} @@ -65,7 +64,7 @@ new B :e B() //│ ╔══[ERROR] Construction of unparameterized class B should use the `new` keyword -//│ ║ l.66: B() +//│ ║ l.65: B() //│ ╙── ^ //│ B //│ res @@ -78,7 +77,7 @@ abstract class C :e new C //│ ╔══[ERROR] Class C is abstract and cannot be instantiated -//│ ║ l.79: new C +//│ ║ l.78: new C //│ ╙── ^ //│ C //│ res @@ -87,10 +86,10 @@ new C :e C() //│ ╔══[ERROR] Class C is abstract and cannot be instantiated -//│ ║ l.88: C() +//│ ║ l.87: C() //│ ╙── ^ //│ ╔══[ERROR] Class C cannot be instantiated as it exposes no constructor -//│ ║ l.88: C() +//│ ║ l.87: C() //│ ╙── ^ //│ error //│ res @@ -132,10 +131,10 @@ class C { :e let c = new C() //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.133: let c = new C() +//│ ║ l.132: let c = new C() //│ ║ ^^^^^^^ //│ ╟── argument list of type `[]` does not match type `[x: Int]` -//│ ║ l.133: let c = new C() +//│ ║ l.132: let c = new C() //│ ╙── ^^ //│ let c: C | error //│ c @@ -220,7 +219,7 @@ class E { constructor(y: Int) } //│ ╔══[PARSE ERROR] A class may have at most one explicit constructor -//│ ║ l.218: class E { +//│ ║ l.217: class E { //│ ╙── ^^^^^ //│ class E { //│ constructor(x: Int) @@ -229,7 +228,7 @@ class E { :e constructor(x: Int) //│ ╔══[ERROR] Illegal position for this constructor statement. -//│ ║ l.230: constructor(x: Int) +//│ ║ l.229: constructor(x: Int) //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ //│ Code generation encountered an error: @@ -318,7 +317,7 @@ fun f(c) = //│ globalThis.f = function f(c) { //│ return ((() => { //│ let a; -//│ return a = c, a instanceof F.class ? (([x]) => x)(F.unapply(c)) : a instanceof G ? 2 : 0; +//│ return a = c, a instanceof F.class ? ((ucs$args_c$F) => ((x) => x)(ucs$args_c$F[0]))(F.unapply(c)) : a instanceof G ? 2 : 0; //│ })()); //│ }; //│ // End of generated code @@ -501,7 +500,7 @@ fun h(z: Int) = } N //│ ╔══[ERROR] Construction of unparameterized class N should use the `new` keyword -//│ ║ l.502: N +//│ ║ l.501: N //│ ╙── ^ //│ fun h: (z: Int) -> (x: Int) -> N @@ -513,7 +512,7 @@ let hh = h(1) :e new hh(1) //│ ╔══[ERROR] type identifier not found: hh -//│ ║ l.514: new hh(1) +//│ ║ l.513: new hh(1) //│ ╙── ^^ //│ error //│ res @@ -529,10 +528,10 @@ mixin P { constructor(x: Int) } //│ ╔══[ERROR] Explicit module constructors are not supported -//│ ║ l.526: constructor(x: Int) +//│ ║ l.525: constructor(x: Int) //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Explicit mixin constructors are not supported -//│ ║ l.529: constructor(x: Int) +//│ ║ l.528: constructor(x: Int) //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ module O { //│ constructor(x: Int) @@ -548,28 +547,28 @@ class QQ(qq: Str) { } } //│ ╔══[ERROR] identifier not found: lol -//│ ║ l.546: lol +//│ ║ l.545: lol //│ ╙── ^^^ //│ ╔══[WARNING] Pure expression does nothing in statement position. -//│ ║ l.546: lol +//│ ║ l.545: lol //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in auxiliary class constructor: -//│ ║ l.545: constructor(foo: Int) { +//│ ║ l.544: constructor(foo: Int) { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.546: lol +//│ ║ l.545: lol //│ ║ ^^^^^^^ -//│ ║ l.547: qq = foo +//│ ║ l.546: qq = foo //│ ║ ^^^^^^^^^^^^ -//│ ║ l.548: } +//│ ║ l.547: } //│ ║ ^^^ //│ ╟── type `Int` is not an instance of type `Str` -//│ ║ l.545: constructor(foo: Int) { +//│ ║ l.544: constructor(foo: Int) { //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `Str` -//│ ║ l.547: qq = foo +//│ ║ l.546: qq = foo //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.544: class QQ(qq: Str) { +//│ ║ l.543: class QQ(qq: Str) { //│ ╙── ^^^ //│ class QQ(qq: Str) { //│ constructor(foo: Int) diff --git a/shared/src/test/diff/codegen/Mixin.mls b/shared/src/test/diff/codegen/Mixin.mls index 8e60a28e..4462228f 100644 --- a/shared/src/test/diff/codegen/Mixin.mls +++ b/shared/src/test/diff/codegen/Mixin.mls @@ -1,5 +1,4 @@ -:NewParser -:NewDefs +:PreTyper :js class Add(lhs: E, rhs: E) @@ -87,10 +86,7 @@ mixin EvalBase { //│ const qualifier1 = this; //│ return ((() => { //│ let a; -//│ return (a = e, a instanceof Lit.class ? (([n]) => n)(Lit.unapply(e)) : a instanceof Add.class ? (([ -//│ l, -//│ r -//│ ]) => qualifier1.eval(l) + qualifier1.eval(r))(Add.unapply(e)) : (() => { +//│ return (a = e, a instanceof Lit.class ? ((ucs$args_e$Lit) => ((n) => n)(ucs$args_e$Lit[0]))(Lit.unapply(e)) : a instanceof Add.class ? ((ucs$args_e$Add) => ((l) => ((r) => qualifier1.eval(l) + qualifier1.eval(r))(ucs$args_e$Add[1]))(ucs$args_e$Add[0]))(Add.unapply(e)) : (() => { //│ throw new Error("non-exhaustive case expression"); //│ })()); //│ })()); @@ -158,7 +154,7 @@ mixin EvalNeg { //│ eval(e) { //│ const qualifier1 = this; //│ return ((() => { -//│ return e instanceof Neg.class ? (([d]) => 0 - qualifier1.eval(d))(Neg.unapply(e)) : super.eval(e); +//│ return e instanceof Neg.class ? ((ucs$args_e$Neg) => ((d) => 0 - qualifier1.eval(d))(ucs$args_e$Neg[0]))(Neg.unapply(e)) : super.eval(e); //│ })()); //│ } //│ }); @@ -192,7 +188,7 @@ mixin EvalNegNeg { //│ eval(e) { //│ const qualifier1 = this; //│ return ((() => { -//│ return e instanceof Neg.class ? (([tmp0]) => tmp0 instanceof Neg.class ? (([d]) => qualifier1.eval(d))(Neg.unapply(tmp0)) : super.eval(e))(Neg.unapply(e)) : super.eval(e); +//│ return e instanceof Neg.class ? ((ucs$args_e$Neg) => ((e$Neg_0) => e$Neg_0 instanceof Neg.class ? ((ucs$args_e$Neg_0$Neg) => ((d) => qualifier1.eval(d))(ucs$args_e$Neg_0$Neg[0]))(Neg.unapply(e$Neg_0)) : super.eval(e))(ucs$args_e$Neg[0]))(Neg.unapply(e)) : super.eval(e); //│ })()); //│ } //│ }); @@ -371,8 +367,11 @@ Barr.y mixin Base { fun x = y } +//│ ╔══[ERROR] identifier `y` not found +//│ ║ l.368: fun x = y +//│ ╙── ^ //│ ╔══[ERROR] identifier not found: y -//│ ║ l.372: fun x = y +//│ ║ l.368: fun x = y //│ ╙── ^ //│ mixin Base() { //│ fun x: error diff --git a/shared/src/test/diff/codegen/MixinCapture.mls b/shared/src/test/diff/codegen/MixinCapture.mls index cf721dcf..8bad474e 100644 --- a/shared/src/test/diff/codegen/MixinCapture.mls +++ b/shared/src/test/diff/codegen/MixinCapture.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper :js class Lit(n: Int) @@ -26,7 +26,7 @@ mixin EvalAddLit { //│ eval(e) { //│ return ((() => { //│ let a; -//│ return (a = e, a instanceof qualifier.Lit.class ? (([n]) => n)(qualifier.Lit.unapply(e)) : (() => { +//│ return (a = e, a instanceof qualifier.Lit.class ? ((ucs$args_e$Lit) => ((n) => n)(ucs$args_e$Lit[0]))(qualifier.Lit.unapply(e)) : (() => { //│ throw new Error("non-exhaustive case expression"); //│ })()); //│ })()); diff --git a/shared/src/test/diff/codegen/Nested.mls b/shared/src/test/diff/codegen/Nested.mls index 10843c74..59fe95ae 100644 --- a/shared/src/test/diff/codegen/Nested.mls +++ b/shared/src/test/diff/codegen/Nested.mls @@ -1,5 +1,4 @@ -:NewParser -:NewDefs +:PreTyper :js module A { @@ -70,7 +69,7 @@ module A { let bb = A.B(A.a) bb.b //│ ╔══[ERROR] Access to class member not yet supported -//│ ║ l.70: let bb = A.B(A.a) +//│ ║ l.69: let bb = A.B(A.a) //│ ╙── ^^ //│ let bb: error //│ error @@ -103,10 +102,10 @@ c.outer1 let d = b.D(1) d.outer //│ ╔══[ERROR] Access to class member not yet supported -//│ ║ l.101: let c = b.C(1) +//│ ║ l.100: let c = b.C(1) //│ ╙── ^^ //│ ╔══[ERROR] Access to class member not yet supported -//│ ║ l.103: let d = b.D(1) +//│ ║ l.102: let d = b.D(1) //│ ╙── ^^ //│ class B(x: Int) { //│ class C(y: Int) { @@ -351,7 +350,7 @@ let fff = es.F(2) let gg = fff.G(3) gg.sum //│ ╔══[ERROR] Access to class member not yet supported -//│ ║ l.350: let fff = es.F(2) +//│ ║ l.349: let fff = es.F(2) //│ ╙── ^^ //│ let es: E //│ let fff: error @@ -550,7 +549,7 @@ let jj = G.H.J(42) let i = jj.ii(2) i.x //│ ╔══[ERROR] Access to module member not yet supported -//│ ║ l.549: let jj = G.H.J(42) +//│ ║ l.548: let jj = G.H.J(42) //│ ╙── ^^ //│ let jj: error //│ let i: error @@ -658,7 +657,7 @@ module H { let j = H.J(42) j.i.x //│ ╔══[ERROR] Access to class member not yet supported -//│ ║ l.658: let j = H.J(42) +//│ ║ l.657: let j = H.J(42) //│ ╙── ^^ //│ let j: error //│ error @@ -688,7 +687,7 @@ let i = I(1) let ij = i.J(0) ij.incY //│ ╔══[ERROR] Access to class member not yet supported -//│ ║ l.688: let ij = i.J(0) +//│ ║ l.687: let ij = i.J(0) //│ ╙── ^^ //│ class I(x: Int) { //│ class J(x: Int) { @@ -879,10 +878,10 @@ module J { let m = J.M() let n = J.N(2) //│ ╔══[ERROR] Access to class member not yet supported -//│ ║ l.879: let m = J.M() +//│ ║ l.878: let m = J.M() //│ ╙── ^^ //│ ╔══[ERROR] Access to class member not yet supported -//│ ║ l.880: let n = J.N(2) +//│ ║ l.879: let n = J.N(2) //│ ╙── ^^ //│ let m: error //│ let n: error @@ -923,7 +922,7 @@ module K { let m = K.L.M() m.f //│ ╔══[ERROR] Access to module member not yet supported -//│ ║ l.923: let m = K.L.M() +//│ ║ l.922: let m = K.L.M() //│ ╙── ^^ //│ let m: error //│ error @@ -953,7 +952,7 @@ module L { let op = L.N.O.P(0) op.x //│ ╔══[ERROR] Access to module member not yet supported -//│ ║ l.953: let op = L.N.O.P(0) +//│ ║ l.952: let op = L.N.O.P(0) //│ ╙── ^^ //│ let op: error //│ error @@ -980,10 +979,10 @@ module M { } M.N.op(M.P()) //│ ╔══[ERROR] Access to module member not yet supported -//│ ║ l.981: M.N.op(M.P()) +//│ ║ l.980: M.N.op(M.P()) //│ ╙── ^^ //│ ╔══[ERROR] Access to class member not yet supported -//│ ║ l.981: M.N.op(M.P()) +//│ ║ l.980: M.N.op(M.P()) //│ ╙── ^^ //│ module M { //│ module N { @@ -1166,7 +1165,7 @@ module N { :e N.O.P() //│ ╔══[ERROR] Access to module member not yet supported -//│ ║ l.1167: N.O.P() +//│ ║ l.1166: N.O.P() //│ ╙── ^^ //│ error //│ res @@ -1182,7 +1181,7 @@ class I(x: Int) { } I(1).J(3).a //│ ╔══[ERROR] Access to class member not yet supported -//│ ║ l.1183: I(1).J(3).a +//│ ║ l.1182: I(1).J(3).a //│ ╙── ^^ //│ class I(x: Int) { //│ class J(z: Int) { @@ -1263,6 +1262,9 @@ fun main = else g(x - 1) let g(x: Int): Int = f(x) f +//│ ╔══[ERROR] identifier `g` not found +//│ ║ l.1262: else g(x - 1) +//│ ╙── ^ //│ fun main: (x: Int) -> Int //│ // Prelude //│ class TypingUnit24 {} diff --git a/shared/src/test/diff/codegen/NewMatching.mls b/shared/src/test/diff/codegen/NewMatching.mls index 12fc2aaa..40b5a0bb 100644 --- a/shared/src/test/diff/codegen/NewMatching.mls +++ b/shared/src/test/diff/codegen/NewMatching.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper class V0() class V1(val x: Int) @@ -31,7 +31,7 @@ fun sum(v) = Half(_, x) then x None(_) then 0 _ then -1 -//│ fun sum: Object -> Int +//│ fun sum: (Half | None | Object & ~#Half & ~#None & ~#Pos & ~#V0 & ~#V1 & ~#V2 & ~#V22 | Pos | V0 | V1 | V22 | V2) -> Int //│ // Prelude //│ class TypingUnit2 {} //│ const typing_unit2 = new TypingUnit2; @@ -39,22 +39,7 @@ fun sum(v) = //│ globalThis.sum = function sum(v) { //│ return ((() => { //│ let a; -//│ return (a = v, a instanceof V0.class ? 0 : a instanceof V1.class ? (([a]) => a)(V1.unapply(v)) : a instanceof V2.class ? (([ -//│ a, -//│ b -//│ ]) => a + b)(V2.unapply(v)) : a instanceof Pos.class ? (([x]) => x > 0 === true ? x : -1)(Pos.unapply(v)) : a instanceof V22.class ? (([ -//│ tmp0, -//│ tmp1 -//│ ]) => tmp0 instanceof V2.class ? (([ -//│ x1, -//│ y1 -//│ ]) => tmp1 instanceof V2.class ? (([ -//│ x2, -//│ y2 -//│ ]) => x1 + y1 + x2 + y2)(V2.unapply(tmp1)) : -1)(V2.unapply(tmp0)) : -1)(V22.unapply(v)) : a instanceof Half.class ? (([ -//│ tmp2, -//│ x -//│ ]) => x)(Half.unapply(v)) : a instanceof None.class ? (([tmp3]) => 0)(None.unapply(v)) : -1); +//│ return a = v, a instanceof V0.class ? 0 : a instanceof V22.class ? ((ucs$args_v$V22) => ((v$V22_0) => ((v$V22_1) => v$V22_0 instanceof V2.class ? ((ucs$args_v$V22_0$V2) => ((x1) => ((y1) => v$V22_1 instanceof V2.class ? ((ucs$args_v$V22_1$V2) => ((x2) => ((y2) => x1 + y1 + x2 + y2)(ucs$args_v$V22_1$V2[1]))(ucs$args_v$V22_1$V2[0]))(V2.unapply(v$V22_1)) : -1)(ucs$args_v$V22_0$V2[1]))(ucs$args_v$V22_0$V2[0]))(V2.unapply(v$V22_0)) : -1)(ucs$args_v$V22[1]))(ucs$args_v$V22[0]))(V22.unapply(v)) : a instanceof V2.class ? ((ucs$args_v$V2) => ((a) => ((b) => a + b)(ucs$args_v$V2[1]))(ucs$args_v$V2[0]))(V2.unapply(v)) : a instanceof V1.class ? ((ucs$args_v$V1) => ((a) => a)(ucs$args_v$V1[0]))(V1.unapply(v)) : a instanceof Pos.class ? ((ucs$args_v$Pos) => ((x) => ((ucs$test$0) => ucs$test$0 === true ? x : -1)(x > 0))(ucs$args_v$Pos[0]))(Pos.unapply(v)) : a instanceof None.class ? 0 : a instanceof Half.class ? ((ucs$args_v$Half) => ((x) => x)(ucs$args_v$Half[1]))(Half.unapply(v)) : (v, v, v, v, v, -1); //│ })()); //│ }; //│ // End of generated code @@ -104,7 +89,7 @@ fun get1(s) = //│ globalThis.get1 = function get1(s) { //│ return ((() => { //│ let a; -//│ return (a = s, a instanceof Some.class ? (([tmp4]) => ((y) => tmp4 instanceof V1.class ? (([x]) => x)(V1.unapply(tmp4)) : y)(tmp4))(Some.unapply(s)) : (() => { +//│ return (a = s, a instanceof Some.class ? ((ucs$args_s$Some) => ((s$Some_0) => s$Some_0 instanceof V1.class ? ((ucs$args_s$Some_0$V1) => ((x) => x)(ucs$args_s$Some_0$V1[0]))(V1.unapply(s$Some_0)) : ((y) => y)(ucs$args_s$Some[0]))(ucs$args_s$Some[0]))(Some.unapply(s)) : (() => { //│ throw new Error("non-exhaustive case expression"); //│ })()); //│ })()); @@ -136,7 +121,7 @@ fun foo(s) = //│ // Query 1 //│ globalThis.foo = function foo(s) { //│ return ((() => { -//│ return s instanceof Some.class ? (([t]) => ((b) => b + t.x)(s2.value))(Some.unapply(s)) : 0; +//│ return s instanceof Some.class ? ((ucs$args_s$Some) => ((t) => ((b) => b + t.x)(s2.value))(ucs$args_s$Some[0]))(Some.unapply(s)) : 0; //│ })()); //│ }; //│ // End of generated code @@ -213,17 +198,18 @@ t(new FooBar()) //│ = 1 :e -:ge fun ft(x) = if x is FooBar(x) then x _ then 0 -//│ ╔══[ERROR] class FooBar expects 0 parameters but found 1 parameter -//│ ║ l.219: FooBar(x) then x -//│ ╙── ^^^^^^^^^ -//│ fun ft: anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╔══[ERROR] Construction of unparameterized class FooBar should use the `new` keyword +//│ ║ l.203: FooBar(x) then x +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] Type mismatch in field selection: +//│ ║ l.203: FooBar(x) then x +//│ ║ ^^^^^^ +//│ ╙── reference of type `() -> FooBar` does not have field 'unapply' +//│ fun ft: Object -> (0 | error) :js module MM @@ -264,13 +250,17 @@ fun c(x) = if x is VVC(x, y, z) then x + y + z _ then 0 -//│ ╔══[ERROR] class VVC expects 2 parameters but found 3 parameters -//│ ║ l.265: VVC(x, y, z) then x + y + z -//│ ╙── ^^^^^^^^^^^^ +//│ ╔══[ERROR] Type mismatch in field selection: +//│ ╟── tuple literal of type `{0: ?#v, 1: ?#vc}` does not have field '2' +//│ ║ l.248: class VVC(v: Int, vc: Int) +//│ ║ ^^^^^^^^^^ +//│ ╟── but it flows into operator application with expected type `{2: ?a}` +//│ ║ l.250: if x is +//│ ║ ^^^^ +//│ ║ l.251: VVC(x, y, z) then x + y + z +//│ ╙── ^^^^^^^ //│ class VVC(v: Int, vc: Int) -//│ fun c: anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ fun c: Object -> Int diff --git a/shared/src/test/diff/codegen/ValLet.mls b/shared/src/test/diff/codegen/ValLet.mls index 6cdfebc4..e1b04b7e 100644 --- a/shared/src/test/diff/codegen/ValLet.mls +++ b/shared/src/test/diff/codegen/ValLet.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper :js class A(x0: Int) { diff --git a/shared/src/test/diff/ecoop23/ComparePointPoly.mls b/shared/src/test/diff/ecoop23/ComparePointPoly.mls index c90dfde5..e3cd651e 100644 --- a/shared/src/test/diff/ecoop23/ComparePointPoly.mls +++ b/shared/src/test/diff/ecoop23/ComparePointPoly.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper class Some[out A](val value: A) diff --git a/shared/src/test/diff/ecoop23/ExpressionProblem.mls b/shared/src/test/diff/ecoop23/ExpressionProblem.mls index 7745355a..8b6b16d7 100644 --- a/shared/src/test/diff/ecoop23/ExpressionProblem.mls +++ b/shared/src/test/diff/ecoop23/ExpressionProblem.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper // * Motivating paper example, demonstrating the expression problem solution diff --git a/shared/src/test/diff/ecoop23/Intro.mls b/shared/src/test/diff/ecoop23/Intro.mls index dda08211..880c19b9 100644 --- a/shared/src/test/diff/ecoop23/Intro.mls +++ b/shared/src/test/diff/ecoop23/Intro.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper // * Examples from paper intro diff --git a/shared/src/test/diff/ecoop23/PolymorphicVariants.mls b/shared/src/test/diff/ecoop23/PolymorphicVariants.mls index a92b52ca..66da4d52 100644 --- a/shared/src/test/diff/ecoop23/PolymorphicVariants.mls +++ b/shared/src/test/diff/ecoop23/PolymorphicVariants.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper // * Adapted example from Code reuse through polymorphic variants (FOSE 2000) @@ -127,7 +127,7 @@ fun map_expr(f, v) = Numb then v Add(l, r) then Add(f(l), f(r)) Mul(l, r) then Mul(f(l), f(r)) -//│ fun map_expr: forall 'a 'A 'b 'A0. ('b -> 'A0 & 'a -> 'A, Add['b] | Mul['a] | Numb | Var) -> (Add['A0] | Mul['A] | Numb | Var) +//│ fun map_expr: forall 'a 'A 'b 'A0. ('a -> 'A & 'b -> 'A0, Add['b] | Mul['a] | Numb | Var) -> (Add['A0] | Mul['A] | Numb | Var) mixin EvalExpr { fun eval(sub, v) = @@ -206,7 +206,7 @@ module Test3 extends EvalVar, EvalExpr, EvalLambda Test3.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) //│ 'a //│ where -//│ 'a :> App['a] | Abs['a] | Abs[Var] | Numb | Var +//│ 'a :> Abs[Var] | Numb | Var | App['a] | Abs['a] //│ res //│ = Abs {} diff --git a/shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls b/shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls index d2b54c67..df937b92 100644 --- a/shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls +++ b/shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper // * Adapted example from Compositional Embeddings of Domain-Specific Languages (OOPSLA 2022) @@ -173,24 +173,24 @@ mixin Text { Translate then concat("a translated region of size ", toString(this.size(e))) } //│ mixin Text() { -//│ this: {size: (Intersect[nothing] | Translate['Region] | Union[nothing] | 'a) -> Int} -//│ fun text: (Circle | Intersect[anything] | Outside['a] | Translate['Region] | Union[anything]) -> Str +//│ this: {size: (Intersect['Region] | Translate[nothing] | Union[nothing] | 'a) -> Int} +//│ fun text: (Circle | Intersect['Region] | Outside['a] | Translate[anything] | Union[anything]) -> Str //│ } :e module SizeText extends Text //│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` -//│ ║ l.173: Translate then concat("a translated region of size ", toString(this.size(e))) -//│ ╙── ^^^^^ -//│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` //│ ║ l.172: Intersect then concat("the intersection of two regions of size ", toString(this.size(e))) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` -//│ ║ l.171: Union then concat("the union of two regions of size ", toString(this.size(e))) -//│ ╙── ^^^^^ -//│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` //│ ║ l.170: Outside(a) then concat("outside a region of size ", toString(this.size(a))) //│ ╙── ^^^^^ +//│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` +//│ ║ l.173: Translate then concat("a translated region of size ", toString(this.size(e))) +//│ ╙── ^^^^^ +//│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` +//│ ║ l.171: Union then concat("the union of two regions of size ", toString(this.size(e))) +//│ ╙── ^^^^^ //│ module SizeText { //│ fun text: (Circle | Intersect[anything] | Outside[anything] | Translate[anything] | Union[anything]) -> Str //│ } @@ -320,7 +320,7 @@ mixin Eliminate { //│ this: { //│ eliminate: 'a -> 'b & 'c -> 'Region & 'd -> 'Region0 & 'e -> 'Region1 & 'f -> 'Region2 & 'g -> 'Region3 //│ } -//│ fun eliminate: (Intersect['e] | Object & 'b & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union | Outside['c & (Object & ~#Outside | Outside['a])] | Scale['g] | Translate['f] | Union['d]) -> (Intersect['Region1] | Outside['Region] | Scale['Region3] | Translate['Region2] | Union['Region0] | 'b) +//│ fun eliminate: (Intersect['g] | Object & 'b & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union | Outside['c & (Object & ~#Outside | Outside['a])] | Scale['f] | Translate['e] | Union['d]) -> (Intersect['Region3] | Outside['Region] | Scale['Region2] | Translate['Region1] | Union['Region0] | 'b) //│ } module TestElim extends Eliminate { @@ -346,9 +346,9 @@ 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 'a. Object -> 'a +//│ fun mk: forall 'a. (1 | 2 | 3 | 4 | Object & ~1 & ~2 & ~3 & ~4) -> 'a //│ where -//│ 'a :> Outside['a] | Scale['a] | Union['a] | Intersect['a] | Translate['a] +//│ 'a :> Outside['a] | Translate['a] | Intersect['a] | Union['a] | Scale['a] :re TestElim.eliminate(mk(100)) diff --git a/shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls b/shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls index 22b0dec1..21c6d8ee 100644 --- a/shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls +++ b/shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper // * Adapted example from Compositional Embeddings of Domain-Specific Languages (OOPSLA 2022) @@ -161,24 +161,24 @@ mixin Text { Translate then concat("a translated region of size ", toString(this.size(e))) } //│ mixin Text() { -//│ this: {size: (Intersect[nothing] | Translate['Region] | Union[nothing] | 'a) -> Int} -//│ fun text: (Circle | Intersect[anything] | Outside['a] | Translate['Region] | Union[anything]) -> Str +//│ this: {size: (Intersect['Region] | Translate[nothing] | Union[nothing] | 'a) -> Int} +//│ fun text: (Circle | Intersect['Region] | Outside['a] | Translate[anything] | Union[anything]) -> Str //│ } :e module SizeText extends Text //│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` -//│ ║ l.161: Translate then concat("a translated region of size ", toString(this.size(e))) -//│ ╙── ^^^^^ -//│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` //│ ║ l.160: Intersect then concat("the intersection of two regions of size ", toString(this.size(e))) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` -//│ ║ l.159: Union then concat("the union of two regions of size ", toString(this.size(e))) -//│ ╙── ^^^^^ -//│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` //│ ║ l.158: Outside(a) then concat("outside a region of size ", toString(this.size(a))) //│ ╙── ^^^^^ +//│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` +//│ ║ l.161: Translate then concat("a translated region of size ", toString(this.size(e))) +//│ ╙── ^^^^^ +//│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` +//│ ║ l.159: Union then concat("the union of two regions of size ", toString(this.size(e))) +//│ ╙── ^^^^^ //│ module SizeText { //│ fun text: (Circle | Intersect[anything] | Outside[anything] | Translate[anything] | Union[anything]) -> Str //│ } @@ -294,7 +294,7 @@ mixin Eliminate { //│ this: { //│ eliminate: 'a -> 'b & 'c -> 'Region & 'd -> 'Region0 & 'e -> 'Region1 & 'f -> 'Region2 & 'g -> 'Region3 //│ } -//│ fun eliminate: (Intersect['e] | Object & 'b & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union | Outside['c & (Object & ~#Outside | Outside['a])] | Scale['g] | Translate['f] | Union['d]) -> (Intersect['Region1] | Outside['Region] | Scale['Region3] | Translate['Region2] | Union['Region0] | 'b) +//│ fun eliminate: (Intersect['g] | Object & 'b & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union | Outside['c & (Object & ~#Outside | Outside['a])] | Scale['f] | Translate['e] | Union['d]) -> (Intersect['Region3] | Outside['Region] | Scale['Region2] | Translate['Region1] | Union['Region0] | 'b) //│ } module TestElim extends Eliminate @@ -303,19 +303,19 @@ module TestElim extends Eliminate //│ } //│ where //│ 'a <: Intersect['a] | Object & 'b & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union | Outside['a & (Object & ~#Outside | Outside['a])] | Scale['a] | Translate['a] | Union['a] -//│ 'b :> Outside['b] | Union['b] | Intersect['b] | Translate['b] | Scale['b] +//│ 'b :> Outside['b] | Union['b] | Translate['b] | Scale['b] | Intersect['b] TestElim.eliminate(Outside(Outside(Univ()))) //│ 'a //│ where -//│ 'a :> Univ | Union[Univ | 'a] | Intersect['a] | Translate[Univ | 'a] | Scale[Univ | 'a] | Outside[Univ | 'a] +//│ 'a :> Univ | Outside[Univ | 'a] | Union[Univ | 'a] | Translate[Univ | 'a] | Scale[Univ | 'a] | Intersect['a] //│ res //│ = Univ {} TestElim.eliminate(circles) //│ 'a //│ where -//│ 'a :> Circle | Translate[Circle | 'a] | Scale[Circle | 'a] | Outside[Circle | 'a] | Union[Circle | 'a] | Intersect['a] +//│ 'a :> Circle | Outside[Circle | 'a] | Union[Circle | 'a] | Translate[Circle | 'a] | Scale[Circle | 'a] | Intersect['a] //│ res //│ = Union {} @@ -325,15 +325,15 @@ 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 'a. Object -> 'a +//│ fun mk: forall 'a. (1 | 2 | 3 | 4 | Object & ~1 & ~2 & ~3 & ~4) -> 'a //│ where -//│ 'a :> Outside['a] | Intersect['a] | Translate['a] | Scale['a] | Union['a] +//│ 'a :> Outside['a] | Union['a] | Scale['a] | Translate['a] | Intersect['a] :re TestElim.eliminate(mk(100)) //│ 'a //│ where -//│ 'a :> Outside['a] | Union['a] | Intersect['a] | Translate['a] | Scale['a] +//│ 'a :> Outside['a] | Union['a] | Translate['a] | Scale['a] | Intersect['a] //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded @@ -355,7 +355,7 @@ module Lang extends SizeBase, SizeExt, Contains, Text, IsUniv, IsEmpty, Eliminat //│ 'e <: Intersect['e] | Object & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union & ~#Univ | Outside['e] | Scale['e] | Translate['e] | Union['e] | Univ //│ 'd <: Intersect['d] | Object & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union & ~#Univ | Outside['d] | Scale['d] | Translate['d] | Union['d] | Univ //│ 'b <: Intersect['b] | Object & 'c & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union | Outside['b & (Object & ~#Outside | Outside['b])] | Scale['b] | Translate['b] | Union['b] -//│ 'c :> Translate['c] | Scale['c] | Outside['c] | Union['c] | Intersect['c] +//│ 'c :> Outside['c] | Union['c] | Translate['c] | Scale['c] | Intersect['c] //│ 'a <: Circle | Intersect['a] | Outside['a] | Translate['a] | Union['a] Lang.size(circles) diff --git a/shared/src/test/diff/fcp/QML_exist_nu.mls b/shared/src/test/diff/fcp/QML_exist_nu.mls index 39fa4e19..2c0986bf 100644 --- a/shared/src/test/diff/fcp/QML_exist_nu.mls +++ b/shared/src/test/diff/fcp/QML_exist_nu.mls @@ -1,6 +1,6 @@ // * TODO also a GADT version of this where we use `Arrays[A]: ArraysImpl[A, ?]` -:NewDefs +:PreTyper :DontDistributeForalls // * Also works without this diff --git a/shared/src/test/diff/gadt/Exp1.mls b/shared/src/test/diff/gadt/Exp1.mls index 489ddea7..d98bdcab 100644 --- a/shared/src/test/diff/gadt/Exp1.mls +++ b/shared/src/test/diff/gadt/Exp1.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper abstract class Exp[A]: Pair | Lit @@ -33,12 +33,24 @@ fun f(e) = if e is :e // TODO support fun f(e) = if e is Pair['a, 'b](l, r) then [l, r] -//│ ╔══[ERROR] illegal pattern +//│ ╔══[ERROR] Unknown pattern Pair‹'a, 'b›(l, r,) //│ ║ l.35: Pair['a, 'b](l, r) then [l, r] //│ ╙── ^^^^^^^^^^^^^^^^^^ -//│ fun f: anything -> error +//│ ╔══[ERROR] identifier `l` not found +//│ ║ l.35: Pair['a, 'b](l, r) then [l, r] +//│ ╙── ^ +//│ ╔══[ERROR] identifier `r` not found +//│ ║ l.35: Pair['a, 'b](l, r) then [l, r] +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: l +//│ ║ l.35: Pair['a, 'b](l, r) then [l, r] +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: r +//│ ║ l.35: Pair['a, 'b](l, r) then [l, r] +//│ ╙── ^ +//│ fun f: anything -> [error, error] //│ Code generation encountered an error: -//│ if expression was not desugared +//│ unresolved symbol l :e // TODO support @@ -46,11 +58,14 @@ fun f(e) = if e is Pair(l: a, r) then let f(x: a) = x f(l) +//│ ╔══[ERROR] identifier `l` not found +//│ ║ l.60: f(l) +//│ ╙── ^ //│ ╔══[ERROR] type identifier not found: a -//│ ║ l.47: let f(x: a) = x +//│ ║ l.59: let f(x: a) = x //│ ╙── ^ //│ ╔══[ERROR] identifier not found: l -//│ ║ l.48: f(l) +//│ ║ l.60: f(l) //│ ╙── ^ //│ fun f: Pair[anything, anything] -> error //│ Code generation encountered an error: diff --git a/shared/src/test/diff/gadt/Exp2.mls b/shared/src/test/diff/gadt/Exp2.mls index 45449541..c8f3abc4 100644 --- a/shared/src/test/diff/gadt/Exp2.mls +++ b/shared/src/test/diff/gadt/Exp2.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper @@ -67,7 +67,7 @@ fun f(e) = if e is //│ ╟── type variable `L` leaks out of its scope //│ ║ l.43: class Pair[L, R](val lhs: Exp[L], val rhs: Exp[R]) extends Exp[[L, R]] //│ ╙── ^ -//│ forall 'X 'L 'R. (e: Exp['X]) -> (Int | error | [Exp['L], Exp['R]]) +//│ forall 'L 'R 'X. (e: Exp['X]) -> (Int | error | [Exp['L], Exp['R]]) //│ where //│ 'R :> ??R //│ <: ??R0 diff --git a/shared/src/test/diff/gadt/ThisMatching.mls b/shared/src/test/diff/gadt/ThisMatching.mls index cf63b325..0d58169a 100644 --- a/shared/src/test/diff/gadt/ThisMatching.mls +++ b/shared/src/test/diff/gadt/ThisMatching.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper :e @@ -28,6 +28,9 @@ Dummy.introspect abstract class Funny: Int { fun test = this + 1 } +//│ ╔══[ERROR] Undefined type Int +//│ ║ l.30: abstract class Funny: Int { fun test = this + 1 } +//│ ╙── ^^^ //│ abstract class Funny: Int { //│ fun test: Int //│ } @@ -35,10 +38,10 @@ abstract class Funny: Int { fun test = this + 1 } :e class Unfunny { fun test = this + 1 } //│ ╔══[ERROR] Type mismatch in operator application: -//│ ║ l.36: class Unfunny { fun test = this + 1 } +//│ ║ l.39: class Unfunny { fun test = this + 1 } //│ ║ ^^^^^^^^ //│ ╟── reference of type `#Unfunny` is not an instance of type `Int` -//│ ║ l.36: class Unfunny { fun test = this + 1 } +//│ ║ l.39: class Unfunny { fun test = this + 1 } //│ ╙── ^^^^ //│ class Unfunny { //│ constructor() @@ -87,15 +90,15 @@ abstract class Exp: (() | Wrap) { } class Wrap(n: Exp) extends Exp //│ ╔══[ERROR] Unhandled cyclic definition -//│ ║ l.83: abstract class Exp: (() | Wrap) { +//│ ║ l.86: abstract class Exp: (() | Wrap) { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.84: fun test = if this is +//│ ║ l.87: fun test = if this is //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.85: Wrap(a) then 0 +//│ ║ l.88: Wrap(a) then 0 //│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.86: () then 1 +//│ ║ l.89: () then 1 //│ ║ ^^^^^^^^^^^^^ -//│ ║ l.87: } +//│ ║ l.90: } //│ ╙── ^ //│ abstract class Exp: Wrap | () { //│ fun test: 0 | 1 @@ -113,17 +116,17 @@ abstract class Exp: (Pair | Lit) { class Lit(n: Int) extends Exp class Pair(lhs: Exp, rhs: Exp) extends Exp //│ ╔══[ERROR] Unhandled cyclic definition -//│ ║ l.107: abstract class Exp: (Pair | Lit) { +//│ ║ l.110: abstract class Exp: (Pair | Lit) { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.108: fun test: Int +//│ ║ l.111: fun test: Int //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.109: fun test = if this is +//│ ║ l.112: fun test = if this is //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.110: Lit then 0 +//│ ║ l.113: Lit then 0 //│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.111: Pair(l, r) then 1 +//│ ║ l.114: Pair(l, r) then 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.112: } +//│ ║ l.115: } //│ ╙── ^ //│ abstract class Exp: Lit | Pair { //│ fun test: Int @@ -136,7 +139,7 @@ class Pair(lhs: Exp, rhs: Exp) extends Exp :e // TODO Pair(Lit(1), Lit(2)).test //│ ╔══[ERROR] Type `Pair` does not contain member `test` -//│ ║ l.137: Pair(Lit(1), Lit(2)).test +//│ ║ l.140: Pair(Lit(1), Lit(2)).test //│ ╙── ^^^^^ //│ error //│ res @@ -152,24 +155,27 @@ abstract class Exp: (Pair | Lit) { class Lit(n: Int) extends Exp class Pair(lhs: Exp, rhs: Exp) extends Exp //│ ╔══[ERROR] Unhandled cyclic definition -//│ ║ l.147: abstract class Exp: (Pair | Lit) { +//│ ║ l.150: abstract class Exp: (Pair | Lit) { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.148: fun test = if this is +//│ ║ l.151: fun test = if this is //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.149: Lit then 0 +//│ ║ l.152: Lit then 0 //│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.150: Pair(l, r) then l.test + r.test +//│ ║ l.153: Pair(l, r) then l.test + r.test //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.151: } +//│ ║ l.154: } //│ ╙── ^ //│ ╔══[ERROR] Indirectly-recursive member should have type annotation -//│ ║ l.150: Pair(l, r) then l.test + r.test +//│ ║ l.153: Pair(l, r) then l.test + r.test //│ ╙── ^^^^^ +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.153: Pair(l, r) then l.test + r.test +//│ ╙── ^^^^^ //│ abstract class Exp: Lit | Pair { -//│ fun test: Int | error +//│ fun test: Int //│ } //│ class Lit(n: Int) extends Exp { -//│ fun test: Int | error +//│ fun test: Int //│ } //│ class Pair(lhs: Exp, rhs: Exp) extends Exp @@ -184,17 +190,17 @@ abstract class Exp: (Pair | Lit) { class Lit(n: Int) extends Exp class Pair(lhs: Exp, rhs: Exp) extends Exp //│ ╔══[ERROR] Unhandled cyclic definition -//│ ║ l.178: abstract class Exp: (Pair | Lit) { +//│ ║ l.184: abstract class Exp: (Pair | Lit) { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.179: fun test : Int +//│ ║ l.185: fun test : Int //│ ║ ^^^^^^^^^^^^^^^^ -//│ ║ l.180: fun test = if this is +//│ ║ l.186: fun test = if this is //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.181: Lit then 0 +//│ ║ l.187: Lit then 0 //│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.182: Pair(l, r) then l.test + r.test +//│ ║ l.188: Pair(l, r) then l.test + r.test //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.183: } +//│ ║ l.189: } //│ ╙── ^ //│ abstract class Exp: Lit | Pair { //│ fun test: Int @@ -214,25 +220,25 @@ abstract class Exp[A]: (Pair | Lit) { class Lit(n: Int) extends Exp[Int] class Pair[L, R](lhs: L, rhs: R) extends Exp[[L, R]] //│ ╔══[ERROR] Unhandled cyclic definition -//│ ║ l.209: abstract class Exp[A]: (Pair | Lit) { +//│ ║ l.215: abstract class Exp[A]: (Pair | Lit) { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.210: fun test = if this is +//│ ║ l.216: fun test = if this is //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.211: Lit then 0 +//│ ║ l.217: Lit then 0 //│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.212: Pair then 1 +//│ ║ l.218: Pair then 1 //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.213: } +//│ ║ l.219: } //│ ╙── ^ //│ ╔══[ERROR] Type error in `case` expression -//│ ║ l.210: fun test = if this is +//│ ║ l.216: fun test = if this is //│ ║ ^^^^^^^ -//│ ║ l.211: Lit then 0 +//│ ║ l.217: Lit then 0 //│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.212: Pair then 1 +//│ ║ l.218: Pair then 1 //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── type variable `L` leaks out of its scope -//│ ║ l.215: class Pair[L, R](lhs: L, rhs: R) extends Exp[[L, R]] +//│ ║ l.221: class Pair[L, R](lhs: L, rhs: R) extends Exp[[L, R]] //│ ╙── ^ //│ abstract class Exp[A]: Lit | Pair[anything, anything] { //│ fun test: 0 | 1 diff --git a/shared/src/test/diff/nu/Andong.mls b/shared/src/test/diff/nu/Andong.mls index e2f1e77e..fa997a58 100644 --- a/shared/src/test/diff/nu/Andong.mls +++ b/shared/src/test/diff/nu/Andong.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper class Union(a: Region, b: Region) diff --git a/shared/src/test/diff/nu/ArrayProg.mls b/shared/src/test/diff/nu/ArrayProg.mls index 857ee4e3..57e8f50f 100644 --- a/shared/src/test/diff/nu/ArrayProg.mls +++ b/shared/src/test/diff/nu/ArrayProg.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper @@ -112,10 +112,10 @@ fun add2(e) = if e is Pair(Numbr(n), Numbr(m)) then Numbr(m + m) Pair(Numbr(n), Vectr(n)) then n -//│ fun add2: Pair[Numbr, Numbr | Vectr] -> (Numbr | Array[Numbr | Vectr]) +//│ fun add2: Pair[Numbr, Numbr | Vectr] -> (Int | Numbr) add2(Pair(Numbr(0), Numbr(1))) -//│ Numbr | Array[Numbr | Vectr] +//│ Int | Numbr //│ res //│ = Numbr {} @@ -189,34 +189,44 @@ fun add(e) = Pair(Numbr(n), Numbr(m)) then 0 Pair(Vectr(xs), Vectr(ys)) then 1 Pair(Vectr(xs), Numbr(n)) then 2 -//│ ╔══[ERROR] The match is not exhaustive. -//│ ║ l.+2: if e is -//│ ║ ^^^^ -//│ ╟── The scrutinee at this position misses 1 case. +//│ ╔══[ERROR] When scrutinee `e` is `Pair`, //│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 -//│ ║ ^^^^^^^^ -//│ ╟── [Missing Case 1/1] `Vectr` -//│ ╟── It first appears here. +//│ ║ ^^^^ +//│ ╟── scrutinee `e$Pair_0` is `Numbr`, and +//│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 +//│ ║ ^^^^^ +//│ ╟── Scrutinee `e$Pair_1` has 1 missing case +//│ ╟── It can be class `Vectr` //│ ║ l.+4: Pair(Vectr(xs), Vectr(ys)) then 1 -//│ ╙── ^^^^^^^^^ -//│ fun add: anything -> error +//│ ╙── ^^^^^ +//│ fun add: Pair[Numbr | Vectr, Numbr] -> (0 | 1 | 2) :e fun add(e) = if e is Pair(Numbr(n), Numbr(m)) then 0 Pair(Vectr(xs), Vectr(ys)) then 1 -//│ ╔══[ERROR] The match is not exhaustive. -//│ ║ l.+2: if e is -//│ ║ ^^^^ -//│ ╟── The scrutinee at this position misses 1 case. +//│ ╔══[ERROR] When scrutinee `e` is `Pair`, +//│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 +//│ ║ ^^^^ +//│ ╟── scrutinee `e$Pair_0` is `Numbr`, and //│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 -//│ ║ ^^^^^^^^ -//│ ╟── [Missing Case 1/1] `Vectr` -//│ ╟── It first appears here. +//│ ║ ^^^^^ +//│ ╟── Scrutinee `e$Pair_1` has 1 missing case +//│ ╟── It can be class `Vectr` //│ ║ l.+4: Pair(Vectr(xs), Vectr(ys)) then 1 -//│ ╙── ^^^^^^^^^ -//│ fun add: anything -> error +//│ ╙── ^^^^^ +//│ ╔══[ERROR] When scrutinee `e` is `Pair`, +//│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 +//│ ║ ^^^^ +//│ ╟── scrutinee `e$Pair_0` is `Vectr`, and +//│ ║ l.+4: Pair(Vectr(xs), Vectr(ys)) then 1 +//│ ║ ^^^^^ +//│ ╟── Scrutinee `e$Pair_1` has 1 missing case +//│ ╟── It can be class `Numbr` +//│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 +//│ ╙── ^^^^^ +//│ fun add: Pair[Numbr | Vectr, nothing] -> (0 | 1) :e add2(Pair(Vectr(0), Numbr(1))) @@ -244,6 +254,6 @@ add2(Pair(Vectr(0), Numbr(1))) //│ ╟── Note: type parameter A is defined at: //│ ║ l.52: class Pair[A, B](a: A, b: B) //│ ╙── ^ -//│ Numbr | error | Array[Numbr | Vectr] +//│ Int | Numbr | error diff --git a/shared/src/test/diff/nu/BadUCS.mls b/shared/src/test/diff/nu/BadUCS.mls index b0eda308..c4fd3013 100644 --- a/shared/src/test/diff/nu/BadUCS.mls +++ b/shared/src/test/diff/nu/BadUCS.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper class Foo @@ -23,57 +23,77 @@ fun foo(x) = if x is Bar then 0 //│ fun foo: Bar -> 0 :e +:ge fun foo(x) = if x is Foo0 then 0 -//│ ╔══[ERROR] Cannot find constructor `Foo0` in scope -//│ ║ l.26: fun foo(x) = if x is Foo0 then 0 +//│ ╔══[ERROR] type identifier `Foo0` not found +//│ ║ l.27: fun foo(x) = if x is Foo0 then 0 //│ ╙── ^^^^ -//│ fun foo: anything -> error +//│ ╔══[ERROR] type identifier not found: Foo0 +//│ ║ l.27: fun foo(x) = if x is Foo0 then 0 +//│ ╙── ^^^^ +//│ fun foo: nothing -> error //│ Code generation encountered an error: -//│ if expression was not desugared +//│ unknown match case: Foo0 type F = Foo //│ type F = Foo :e +:ge fun foo(x) = if x is F then 0 -//│ ╔══[ERROR] Cannot find constructor `F` in scope -//│ ║ l.39: fun foo(x) = if x is F then 0 +//│ ╔══[ERROR] Type alias is not allowed in pattern +//│ ║ l.44: fun foo(x) = if x is F then 0 +//│ ╙── ^ +//│ ╔══[ERROR] can only match on classes and traits +//│ ║ l.44: fun foo(x) = if x is F then 0 //│ ╙── ^ -//│ fun foo: anything -> error +//│ fun foo: nothing -> error //│ Code generation encountered an error: -//│ if expression was not desugared +//│ cannot match type alias F :e +:ge fun foo(x) = if x is F() then 0 -//│ ╔══[ERROR] Illegal pattern `F` -//│ ║ l.48: fun foo(x) = if x is F() then 0 +//│ ╔══[ERROR] Type alias is not allowed in pattern +//│ ║ l.57: fun foo(x) = if x is F() then 0 +//│ ╙── ^ +//│ ╔══[ERROR] can only match on classes and traits +//│ ║ l.57: fun foo(x) = if x is F() then 0 //│ ╙── ^ -//│ fun foo: anything -> error +//│ fun foo: nothing -> error //│ Code generation encountered an error: -//│ if expression was not desugared +//│ cannot match type alias F mixin M //│ mixin M() :e +:ge fun foo(x) = if x is M then 0 -//│ ╔══[ERROR] Cannot find constructor `M` in scope -//│ ║ l.61: fun foo(x) = if x is M then 0 +//│ ╔══[ERROR] Mixins are not allowed in pattern +//│ ║ l.74: fun foo(x) = if x is M then 0 //│ ╙── ^ -//│ fun foo: anything -> error +//│ ╔══[ERROR] can only match on classes and traits +//│ ║ l.74: fun foo(x) = if x is M then 0 +//│ ╙── ^ +//│ fun foo: nothing -> error //│ Code generation encountered an error: -//│ if expression was not desugared +//│ unknown match case: M :e +:ge fun foo(x) = if x is M() then 0 -//│ ╔══[ERROR] Illegal pattern `M` -//│ ║ l.70: fun foo(x) = if x is M() then 0 +//│ ╔══[ERROR] Mixins are not allowed in pattern +//│ ║ l.87: fun foo(x) = if x is M() then 0 +//│ ╙── ^ +//│ ╔══[ERROR] can only match on classes and traits +//│ ║ l.87: fun foo(x) = if x is M() then 0 //│ ╙── ^ -//│ fun foo: anything -> error +//│ fun foo: nothing -> error //│ Code generation encountered an error: -//│ if expression was not desugared +//│ unknown match case: M fun foo0(x, y) = if x is y then 0 @@ -84,22 +104,29 @@ fun foo = 0 //│ fun foo: 0 :e +:ge fun foo0(x) = if x is foo() then 0 -//│ ╔══[ERROR] Illegal pattern `foo` -//│ ║ l.87: fun foo0(x) = if x is foo() then 0 -//│ ╙── ^^^ -//│ fun foo0: anything -> error +//│ ╔══[ERROR] type identifier `foo` not found +//│ ║ l.108: fun foo0(x) = if x is foo() then 0 +//│ ╙── ^^^ +//│ ╔══[ERROR] can only match on classes and traits +//│ ║ l.108: fun foo0(x) = if x is foo() then 0 +//│ ╙── ^^^ +//│ fun foo0: nothing -> error //│ Code generation encountered an error: -//│ if expression was not desugared +//│ unknown match case: foo :e +:ge +// FIXME: Typer.scala:1497 fun foo(x) = if x is foo() then 0 -//│ ╔══[ERROR] Illegal pattern `foo` -//│ ║ l.96: fun foo(x) = if x is foo() then 0 -//│ ╙── ^^^ -//│ fun foo: anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╔══[ERROR] type identifier `foo` not found +//│ ║ l.122: fun foo(x) = if x is foo() then 0 +//│ ╙── ^^^ +//│ ╔══[ERROR] can only match on classes and traits +//│ ║ l.122: fun foo(x) = if x is foo() then 0 +//│ ╙── ^^^ +//│ /!!!\ Uncaught error: java.lang.Exception: Internal Error: Program reached and unexpected state. module Nil class Cons[out A](head: A, tail: Cons[A] | Nil) diff --git a/shared/src/test/diff/nu/BasicClassInheritance.mls b/shared/src/test/diff/nu/BasicClassInheritance.mls index 29907d33..b365a2b0 100644 --- a/shared/src/test/diff/nu/BasicClassInheritance.mls +++ b/shared/src/test/diff/nu/BasicClassInheritance.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper class A diff --git a/shared/src/test/diff/nu/BasicClasses.mls b/shared/src/test/diff/nu/BasicClasses.mls index a24dc50f..0877423d 100644 --- a/shared/src/test/diff/nu/BasicClasses.mls +++ b/shared/src/test/diff/nu/BasicClasses.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper class A(val n: Int) diff --git a/shared/src/test/diff/nu/CaseExpr.mls b/shared/src/test/diff/nu/CaseExpr.mls index c6ebf859..0cb1b78b 100644 --- a/shared/src/test/diff/nu/CaseExpr.mls +++ b/shared/src/test/diff/nu/CaseExpr.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper case 0 then true @@ -63,21 +63,27 @@ map(succ) of None fun map(f) = case Some(x) then Some(f(x)) None as n then n -//│ ╔══[ERROR] Illegal pattern `as` +//│ ╔══[ERROR] type identifier `as` not found //│ ║ l.65: None as n then n //│ ╙── ^^ -//│ fun map: anything -> anything -> error +//│ ╔══[ERROR] identifier `as` not found +//│ ║ l.65: None as n then n +//│ ╙── ^^ +//│ ╔══[ERROR] type identifier not found: as +//│ ║ l.65: None as n then n +//│ ╙── ^^ +//│ fun map: forall 'a 'A. ('a -> 'A) -> Some['a] -> (Some['A] | error) //│ Code generation encountered an error: -//│ if expression was not desugared +//│ unresolved symbol as :pe case 1 //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'case'; found integer literal instead -//│ ║ l.75: case 1 +//│ ║ l.81: case 1 //│ ║ ^ //│ ╟── Note: 'case' expression starts here: -//│ ║ l.75: case 1 +//│ ║ l.81: case 1 //│ ╙── ^^^^ //│ anything -> 1 //│ res @@ -86,13 +92,13 @@ case 1 :pe case (1 then true) //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here -//│ ║ l.87: case (1 then true) +//│ ║ l.93: case (1 then true) //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'case'; found integer literal instead -//│ ║ l.87: case (1 then true) +//│ ║ l.93: case (1 then true) //│ ║ ^^^^^^^^^^^^^ //│ ╟── Note: 'case' expression starts here: -//│ ║ l.87: case (1 then true) +//│ ║ l.93: case (1 then true) //│ ╙── ^^^^ //│ anything -> 1 //│ res @@ -106,16 +112,16 @@ case else 0 :pe case then 1 else 0 //│ ╔══[PARSE ERROR] Unexpected 'then' keyword in expression position -//│ ║ l.107: case then 1 else 0 +//│ ║ l.113: case then 1 else 0 //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'case'; found integer literal instead -//│ ║ l.107: case then 1 else 0 +//│ ║ l.113: case then 1 else 0 //│ ║ ^ //│ ╟── Note: 'case' expression starts here: -//│ ║ l.107: case then 1 else 0 +//│ ║ l.113: case then 1 else 0 //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected end of input; found 'else' keyword instead -//│ ║ l.107: case then 1 else 0 +//│ ║ l.113: case then 1 else 0 //│ ╙── ^^^^ //│ anything -> 1 //│ res @@ -129,16 +135,19 @@ case then 1 else 0 :e case x, y then x + y //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead -//│ ║ l.130: case x, y then x + y +//│ ║ l.136: case x, y then x + y //│ ╙── ^^^^^^^^^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'case'; found operator application instead -//│ ║ l.130: case x, y then x + y +//│ ║ l.136: case x, y then x + y //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── Note: 'case' expression starts here: -//│ ║ l.130: case x, y then x + y +//│ ║ l.136: case x, y then x + y //│ ╙── ^^^^ +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.136: case x, y then x + y +//│ ╙── ^ //│ ╔══[ERROR] identifier not found: x -//│ ║ l.130: case x, y then x + y +//│ ║ l.136: case x, y then x + y //│ ╙── ^ //│ anything -> () //│ Code generation encountered an error: @@ -146,20 +155,28 @@ case x, y then x + y :e case (x, y) then x + y -//│ ╔══[ERROR] Illegal pattern `,` -//│ ║ l.148: case (x, y) then x + y +//│ ╔══[ERROR] type identifier `,` not found +//│ ║ l.157: case (x, y) then x + y +//│ ╙── ^^^^ +//│ ╔══[ERROR] identifier , refers to different symbols. +//│ ║ l.157: case (x, y) then x + y +//│ ║ ^^^^ +//│ ╟── it is resolved to `,` +//│ ╙── it was previously resolved to local `,` +//│ ╔══[ERROR] type identifier not found: , +//│ ║ l.157: case (x, y) then x + y //│ ╙── ^^^^ -//│ anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared - -:e // * FIXME[UCS] -case [x, y] then x + y -//│ ╔══[ERROR] type identifier not found: Tuple#2 -//│ ╙── //│ nothing -> error //│ Code generation encountered an error: -//│ unknown match case: Tuple#2 +//│ unresolved symbol , +let foo = case [x, y] then x + y +//│ let foo: {0: Int, 1: Int} -> Int +//│ foo +//│ = [Function: foo1] +foo([1, 2]) +//│ Int +//│ res +//│ = 3 diff --git a/shared/src/test/diff/nu/ClassSignatures.mls b/shared/src/test/diff/nu/ClassSignatures.mls index 20c59c94..f4fe3759 100644 --- a/shared/src/test/diff/nu/ClassSignatures.mls +++ b/shared/src/test/diff/nu/ClassSignatures.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper abstract class C0: C1 | C2 diff --git a/shared/src/test/diff/nu/ClassesInMixins.mls b/shared/src/test/diff/nu/ClassesInMixins.mls index eb762841..c3258204 100644 --- a/shared/src/test/diff/nu/ClassesInMixins.mls +++ b/shared/src/test/diff/nu/ClassesInMixins.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper @@ -41,17 +41,18 @@ M.Foo :e // TODO support fun foo(x) = if x is M.Foo then 1 -//│ ╔══[ERROR] illegal pattern +//│ ╔══[ERROR] Unknown pattern (M).Foo //│ ║ l.43: fun foo(x) = if x is M.Foo then 1 //│ ╙── ^^^^^ -//│ fun foo: anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ fun foo: anything -> 1 :e mixin Test2 { let f = Foo(1) } +//│ ╔══[ERROR] identifier `Foo` not found +//│ ║ l.50: mixin Test2 { let f = Foo(1) } +//│ ╙── ^^^ //│ ╔══[ERROR] identifier not found: Foo -//│ ║ l.52: mixin Test2 { let f = Foo(1) } +//│ ║ l.50: mixin Test2 { let f = Foo(1) } //│ ╙── ^^^ //│ mixin Test2() { //│ let f: error @@ -61,14 +62,17 @@ mixin Test2 { let f = Foo(1) } :e mixin Test3 { fun f(x) = if x is Foo then 1 } -//│ ╔══[ERROR] Cannot find constructor `Foo` in scope -//│ ║ l.63: mixin Test3 { fun f(x) = if x is Foo then 1 } +//│ ╔══[ERROR] type identifier `Foo` not found +//│ ║ l.64: mixin Test3 { fun f(x) = if x is Foo then 1 } +//│ ╙── ^^^ +//│ ╔══[ERROR] type identifier not found: Foo +//│ ║ l.64: mixin Test3 { fun f(x) = if x is Foo then 1 } //│ ╙── ^^^ //│ mixin Test3() { -//│ fun f: anything -> error +//│ fun f: nothing -> error //│ } //│ Code generation encountered an error: -//│ if expression was not desugared +//│ unknown match case: Foo @@ -83,10 +87,10 @@ mixin Test { Add(l, r) then this.size(l) + this.size(r) } //│ ╔══[ERROR] Type error in application -//│ ║ l.80: fun cached = size(this) +//│ ║ l.84: fun cached = size(this) //│ ║ ^^^^^^^^^^ //│ ╟── type variable `A` leaks out of its scope -//│ ║ l.78: class Add(lhs: A, rhs: A) { +//│ ║ l.82: class Add(lhs: A, rhs: A) { //│ ╙── ^ //│ mixin Test() { //│ this: {size: (??A | 'a) -> Int} diff --git a/shared/src/test/diff/nu/CommaOperator.mls b/shared/src/test/diff/nu/CommaOperator.mls index 6d7e46db..4c13a824 100644 --- a/shared/src/test/diff/nu/CommaOperator.mls +++ b/shared/src/test/diff/nu/CommaOperator.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper 1; 2 @@ -208,6 +208,9 @@ foo(1), 2 :ge // FIXME let rec x() = x() in x +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.210: let rec x() = x() in x +//│ ╙── ^ //│ nothing //│ Code generation encountered an error: //│ recursive non-function definition x is not supported @@ -216,7 +219,7 @@ let rec x() = x() in x :pe let x[T] = 1 in x //│ ╔══[PARSE ERROR] Expected function parameter list; found square bracket section instead -//│ ║ l.217: let x[T] = 1 in x +//│ ║ l.220: let x[T] = 1 in x //│ ╙── ^^^ //│ 1 //│ res @@ -281,16 +284,16 @@ foo( :e foo(log(1);2) //│ ╔══[PARSE ERROR] Unexpected semicolon here -//│ ║ l.282: foo(log(1);2) +//│ ║ l.285: foo(log(1);2) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.282: foo(log(1);2) +//│ ║ l.285: foo(log(1);2) //│ ║ ^^^^^^^^^^^^^ //│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` -//│ ║ l.282: foo(log(1);2) +//│ ║ l.285: foo(log(1);2) //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: -//│ ║ l.245: fun foo(x, y) = [x, y] +//│ ║ l.248: fun foo(x, y) = [x, y] //│ ╙── ^^^^^^ //│ error | [nothing, nothing] //│ res @@ -301,13 +304,13 @@ foo(log(1);2) :e foo((log(1),2)) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.302: foo((log(1),2)) +//│ ║ l.305: foo((log(1),2)) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` -//│ ║ l.302: foo((log(1),2)) +//│ ║ l.305: foo((log(1),2)) //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: -//│ ║ l.245: fun foo(x, y) = [x, y] +//│ ║ l.248: fun foo(x, y) = [x, y] //│ ╙── ^^^^^^ //│ error | [nothing, nothing] //│ res @@ -326,13 +329,13 @@ foo((let x = log(0), 1; log(x), x + 1), 2) :e foo(let x = log(0), 1; log(x), x + 1) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.327: foo(let x = log(0), 1; log(x), x + 1) +//│ ║ l.330: foo(let x = log(0), 1; log(x), x + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` -//│ ║ l.327: foo(let x = log(0), 1; log(x), x + 1) +//│ ║ l.330: foo(let x = log(0), 1; log(x), x + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: -//│ ║ l.245: fun foo(x, y) = [x, y] +//│ ║ l.248: fun foo(x, y) = [x, y] //│ ╙── ^^^^^^ //│ error | [nothing, nothing] //│ res @@ -344,13 +347,13 @@ foo(let x = log(0), 1; log(x), x + 1) :e foo(let x = log(0), 1 in log(x), x + 1) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.345: foo(let x = log(0), 1 in log(x), x + 1) +//│ ║ l.348: foo(let x = log(0), 1 in log(x), x + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` -//│ ║ l.345: foo(let x = log(0), 1 in log(x), x + 1) +//│ ║ l.348: foo(let x = log(0), 1 in log(x), x + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: -//│ ║ l.245: fun foo(x, y) = [x, y] +//│ ║ l.248: fun foo(x, y) = [x, y] //│ ╙── ^^^^^^ //│ error | [nothing, nothing] //│ res @@ -362,13 +365,13 @@ foo(let x = log(0), 1 in log(x), x + 1) :e foo(let x = log(0), 1; log(x), 1 + 1) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.363: foo(let x = log(0), 1; log(x), 1 + 1) +//│ ║ l.366: foo(let x = log(0), 1; log(x), 1 + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` -//│ ║ l.363: foo(let x = log(0), 1; log(x), 1 + 1) +//│ ║ l.366: foo(let x = log(0), 1; log(x), 1 + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: -//│ ║ l.245: fun foo(x, y) = [x, y] +//│ ║ l.248: fun foo(x, y) = [x, y] //│ ╙── ^^^^^^ //│ error | [nothing, nothing] //│ res @@ -380,13 +383,13 @@ foo(let x = log(0), 1; log(x), 1 + 1) :e foo(if true then 1 else 2, 3) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.381: foo(if true then 1 else 2, 3) +//│ ║ l.384: foo(if true then 1 else 2, 3) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── argument of type `[1 | ?a]` does not match type `[?b, ?c]` -//│ ║ l.381: foo(if true then 1 else 2, 3) +//│ ║ l.384: foo(if true then 1 else 2, 3) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: -//│ ║ l.245: fun foo(x, y) = [x, y] +//│ ║ l.248: fun foo(x, y) = [x, y] //│ ╙── ^^^^^^ //│ error | [nothing, nothing] //│ res @@ -400,13 +403,13 @@ foo((if true then 1 else 2), 3) :e foo(if true then log("ok"), 1 else log("nok"), 2, 3) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.401: foo(if true then log("ok"), 1 else log("nok"), 2, 3) +//│ ║ l.404: foo(if true then log("ok"), 1 else log("nok"), 2, 3) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── argument of type `[?a | ?b]` does not match type `[?c, ?d]` -//│ ║ l.401: foo(if true then log("ok"), 1 else log("nok"), 2, 3) +//│ ║ l.404: foo(if true then log("ok"), 1 else log("nok"), 2, 3) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: -//│ ║ l.245: fun foo(x, y) = [x, y] +//│ ║ l.248: fun foo(x, y) = [x, y] //│ ╙── ^^^^^^ //│ error | [nothing, nothing] //│ res diff --git a/shared/src/test/diff/nu/CtorSubtraction.mls b/shared/src/test/diff/nu/CtorSubtraction.mls index 9a58ce68..06555d1e 100644 --- a/shared/src/test/diff/nu/CtorSubtraction.mls +++ b/shared/src/test/diff/nu/CtorSubtraction.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper class Cls diff --git a/shared/src/test/diff/nu/Eval.mls b/shared/src/test/diff/nu/Eval.mls index 0f5dd3d2..1fdb1d62 100644 --- a/shared/src/test/diff/nu/Eval.mls +++ b/shared/src/test/diff/nu/Eval.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper @@ -138,7 +138,7 @@ module Lists { // TODO use name List when module overloading is supported: //│ module Lists { //│ fun assoc: forall 'a 'A. 'a -> (Cons[{key: Eql['a], value: 'A}] | Nil) -> (None | Some['A]) //│ fun map: forall 'b 'A0. ('b -> 'A0) -> (Cons['b] | Nil) -> (Cons['A0] | Nil) -//│ fun zip: forall 'c 'd. (Cons['d] | Nil, Cons['c] | Nil) -> (Cons[['d, 'c]] | Nil) +//│ fun zip: forall 'c 'd. (Cons['c] | Nil, Cons['d] | Nil) -> (Cons[['c, 'd]] | Nil) //│ } let xs = 1 :: 2 :: 3 :: Nil @@ -260,12 +260,12 @@ fun eval(t, env) = if t is else err(String(pree) ++ " does not have field " ++ nme) Rcd(fs) then Rcd of fs |> Lists.map of {key, value} => {key, value: eval(value, env)} -//│ fun eval: forall 'a 'b 'c. ('c, Cons[{key: Eql[Str], value: 'a}] & {List#A <: {key: Eql[Str], value: 'a}} & List[{key: Eql[Str], value: 'a}] | Nil & {List#A <: {key: Eql[Str], value: 'a}} & List[{key: Eql[Str], value: 'a}]) -> 'b +//│ fun eval: forall 'a 'b 'c. ('a, Cons[{key: Eql[Str], value: 'b}] & {List#A <: {key: Eql[Str], value: 'b}} & List[{key: Eql[Str], value: 'b}] | Nil & {List#A <: {key: Eql[Str], value: 'b}} & List[{key: Eql[Str], value: 'b}]) -> ('b | 'c) //│ where -//│ 'a :> 'b -//│ <: Object & ~#Rcd | Rcd['a] -//│ 'b :> 'a | Rcd[Lam | Lit[??A] | 'b] | Lam | Lit[??A] -//│ 'c <: App | Lam | Lit[anything] | Rcd['c] | Sel | Var +//│ 'b :> 'c +//│ <: Object & ~#Rcd | Rcd['b] +//│ 'c :> Rcd[Lam | Lit[??A] | 'b | 'c] | Lam | Lit[??A] | 'b +//│ 'a <: App | Lam | Lit[anything] | Rcd['a] | Sel | Var eval : (Term, List[{key: Str, value: Value}]) -> Value //│ (Term, List[{key: Str, value: Value}]) -> Value diff --git a/shared/src/test/diff/nu/EvalNegNeg.mls b/shared/src/test/diff/nu/EvalNegNeg.mls index 4e3fbbab..2b8f7d52 100644 --- a/shared/src/test/diff/nu/EvalNegNeg.mls +++ b/shared/src/test/diff/nu/EvalNegNeg.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper class Add(lhs: E, rhs: E) diff --git a/shared/src/test/diff/nu/ExpressionProblem_repro.mls b/shared/src/test/diff/nu/ExpressionProblem_repro.mls index 70a8a32e..d6b883fe 100644 --- a/shared/src/test/diff/nu/ExpressionProblem_repro.mls +++ b/shared/src/test/diff/nu/ExpressionProblem_repro.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper :NoJS diff --git a/shared/src/test/diff/nu/ExpressionProblem_small.mls b/shared/src/test/diff/nu/ExpressionProblem_small.mls index 128c1eee..db03532e 100644 --- a/shared/src/test/diff/nu/ExpressionProblem_small.mls +++ b/shared/src/test/diff/nu/ExpressionProblem_small.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper :NoJS diff --git a/shared/src/test/diff/nu/FilterMap.mls b/shared/src/test/diff/nu/FilterMap.mls index 2f2526e1..b926fc85 100644 --- a/shared/src/test/diff/nu/FilterMap.mls +++ b/shared/src/test/diff/nu/FilterMap.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper // * From https://arxiv.org/abs/2302.12783 @@ -28,11 +28,10 @@ fun filtermap(f, xs) = if xs is false then filtermap(f, ys) true then Cons(y, filtermap(f, ys)) [true, z] then Cons(y, filtermap(f, ys)) -//│ ╔══[ERROR] type identifier not found: Tuple#2 -//│ ╙── -//│ fun filtermap: ((Cons[nothing] | Nil) -> Bool, Cons[anything] | Nil) -> (Cons[nothing] | Nil | error) -//│ Code generation encountered an error: -//│ unknown match case: Tuple#2 +//│ ╔══[ERROR] unsupported pattern +//│ ║ l.30: [true, z] then Cons(y, filtermap(f, ys)) +//│ ╙── ^^^^ +//│ fun filtermap: ((Cons[nothing] | Nil) -> (Object & {1: anything} & ~false & ~true | false | true), Cons[anything] | Nil) -> (Cons[nothing] | Nil) module Tru diff --git a/shared/src/test/diff/nu/FlatIfThenElse.mls b/shared/src/test/diff/nu/FlatIfThenElse.mls index 03a92984..37e3a3da 100644 --- a/shared/src/test/diff/nu/FlatIfThenElse.mls +++ b/shared/src/test/diff/nu/FlatIfThenElse.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper type Option[out A] = Some[A] | None @@ -97,6 +97,7 @@ fun test(x: Option[Int]) = // Q: Support? :pe :e +:w fun test(x: Option[Int]) = if x is None then [0, 0] @@ -105,22 +106,64 @@ fun test(x: Option[Int]) = log(value) [value - 1, value + 1] //│ ╔══[PARSE ERROR] Unexpected 'then' keyword in expression position -//│ ║ l.104: then +//│ ║ l.105: then //│ ╙── ^^^^ -//│ ╔══[ERROR] Illegal interleaved statement App(Var(Some),Tup(List((None,Fld(_,Var(value)))))) -//│ ║ l.103: Some(value) +//│ ╔══[ERROR] identifier `value` not found +//│ ║ l.107: [value - 1, value + 1] +//│ ╙── ^^^^^ +//│ ╔══[ERROR] identifier `value` not found +//│ ║ l.107: [value - 1, value + 1] +//│ ╙── ^^^^^ +//│ ╔══[ERROR] identifier `value` not found +//│ ║ l.106: log(value) +//│ ╙── ^^^^^ +//│ ╔══[ERROR] Unexpected statement in an if block +//│ ║ l.104: Some(value) //│ ╙── ^^^^^^^^^^^ +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.102: if x is +//│ ║ ^^^^ +//│ ║ l.103: None then [0, 0] +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type `Some[Int]` is not an instance of type `None` +//│ ║ l.4: type Option[out A] = Some[A] | None +//│ ║ ^^^^^^^ +//│ ╟── but it flows into reference with expected type `None` +//│ ║ l.102: if x is +//│ ║ ^ +//│ ╟── Note: constraint arises from class pattern: +//│ ║ l.103: None then [0, 0] +//│ ╙── ^^^^ +//│ ╔══[WARNING] Expression in statement position should have type `()`. +//│ ╟── Use a comma expression `... , ()` to explicitly discard non-unit values, making your intent clearer. +//│ ╟── Type mismatch in if-else block: +//│ ║ l.102: if x is +//│ ║ ^^^^ +//│ ║ l.103: None then [0, 0] +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.104: Some(value) +//│ ║ ^^^^^^^^^^^^^^^ +//│ ╟── tuple literal of type `[0, 0]` does not match type `()` +//│ ║ l.103: None then [0, 0] +//│ ║ ^^^^^^ +//│ ╟── but it flows into expression in statement position with expected type `()` +//│ ║ l.102: if x is +//│ ║ ^^^^ +//│ ║ l.103: None then [0, 0] +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.104: Some(value) +//│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[ERROR] identifier not found: value -//│ ║ l.105: log(value) +//│ ║ l.106: log(value) //│ ╙── ^^^^^ //│ ╔══[ERROR] identifier not found: value -//│ ║ l.106: [value - 1, value + 1] +//│ ║ l.107: [value - 1, value + 1] //│ ╙── ^^^^^ //│ ╔══[ERROR] identifier not found: value -//│ ║ l.106: [value - 1, value + 1] +//│ ║ l.107: [value - 1, value + 1] //│ ╙── ^^^^^ //│ fun test: (x: Option[Int]) -> [Int, Int] //│ Code generation encountered an error: -//│ if expression was not desugared +//│ unresolved symbol value diff --git a/shared/src/test/diff/nu/FlatMonads.mls b/shared/src/test/diff/nu/FlatMonads.mls index 2358f88b..6345cc86 100644 --- a/shared/src/test/diff/nu/FlatMonads.mls +++ b/shared/src/test/diff/nu/FlatMonads.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper declare fun String: anything -> Str @@ -120,7 +120,11 @@ printLine("").bind of () => error //│ res //│ = Bind {} +:w printLine("").bind of (()) => error +//│ ╔══[WARNING] literal patterns are ignored +//│ ║ l.124: printLine("").bind of (()) => error +//│ ╙── ^^ //│ Bind[(), 'B] //│ res //│ = Bind {} @@ -304,13 +308,13 @@ let r = loop(defaultCtx).run :e not(r) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.305: not(r) +//│ ║ l.309: not(r) //│ ║ ^^^^^^ //│ ╟── type `Int` is not an instance of type `Bool` //│ ║ l.34: module readInt extends IO[Int] { fun run: Int = 42 } //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `Bool` -//│ ║ l.305: not(r) +//│ ║ l.309: not(r) //│ ╙── ^ //│ error | false | true //│ res @@ -354,16 +358,20 @@ main //│ = Bind {} +:w :e let r = printLine("").bind of 0 => Pure(1) +//│ ╔══[WARNING] literal patterns are ignored +//│ ║ l.363: let r = printLine("").bind of 0 => Pure(1) +//│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.358: let r = printLine("").bind of 0 => Pure(1) +//│ ║ l.363: let r = printLine("").bind of 0 => Pure(1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `()` does not match type `0` -//│ ║ l.323: class printLine(str: Str) extends IO { fun run = log(str) } +//│ ║ l.327: class printLine(str: Str) extends IO { fun run = log(str) } //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from integer literal: -//│ ║ l.358: let r = printLine("").bind of 0 => Pure(1) +//│ ║ l.363: let r = printLine("").bind of 0 => Pure(1) //│ ╙── ^ //│ let r: Bind[in 0 & 'A out () | 'A, 'B] | error //│ where @@ -376,20 +384,20 @@ let r = printLine("").bind of x => log(x.a) Pure(1) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.375: let r = printLine("").bind of x => +//│ ║ l.383: let r = printLine("").bind of x => //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.376: log(x.a) +//│ ║ l.384: log(x.a) //│ ║ ^^^^^^^^^ -//│ ║ l.377: Pure(1) +//│ ║ l.385: Pure(1) //│ ║ ^^^^^^^ //│ ╟── application of type `()` does not have field 'a' -//│ ║ l.323: class printLine(str: Str) extends IO { fun run = log(str) } +//│ ║ l.327: class printLine(str: Str) extends IO { fun run = log(str) } //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from field selection: -//│ ║ l.376: log(x.a) +//│ ║ l.384: log(x.a) //│ ║ ^^^ //│ ╟── from reference: -//│ ║ l.376: log(x.a) +//│ ║ l.384: log(x.a) //│ ╙── ^ //│ let r: Bind[in {a: anything} & 'A out () | 'A, 'B] | error //│ where diff --git a/shared/src/test/diff/nu/FunnyIndet.mls b/shared/src/test/diff/nu/FunnyIndet.mls index 3cd12c51..f0eb4d45 100644 --- a/shared/src/test/diff/nu/FunnyIndet.mls +++ b/shared/src/test/diff/nu/FunnyIndet.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper 2 + @@ -31,12 +31,12 @@ :e // TODO support 2 is 2 -//│ ╔══[ERROR] illegal pattern +//│ ╔══[ERROR] Unknown pattern {2} //│ ║ l.33: 2 //│ ╙── ^ -//│ error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ true +//│ res +//│ = true if 2 is 2 then true //│ true diff --git a/shared/src/test/diff/nu/GADTMono.mls b/shared/src/test/diff/nu/GADTMono.mls index 0d8323d5..1221d79b 100644 --- a/shared/src/test/diff/nu/GADTMono.mls +++ b/shared/src/test/diff/nu/GADTMono.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper trait Expr[A]: LitInt | LitBool | Add | Cond | Pair | Fst | Snd class LitInt(n: Int) extends Expr[Int] diff --git a/shared/src/test/diff/nu/GenericClasses.mls b/shared/src/test/diff/nu/GenericClasses.mls index 8ce63d9d..64c1ef7f 100644 --- a/shared/src/test/diff/nu/GenericClasses.mls +++ b/shared/src/test/diff/nu/GenericClasses.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper class C diff --git a/shared/src/test/diff/nu/GenericModules.mls b/shared/src/test/diff/nu/GenericModules.mls index 778511bb..75c5b56c 100644 --- a/shared/src/test/diff/nu/GenericModules.mls +++ b/shared/src/test/diff/nu/GenericModules.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper // * TODO generic module definitions need to be restricted so they do not include nay state diff --git a/shared/src/test/diff/nu/HeungTung.mls b/shared/src/test/diff/nu/HeungTung.mls index 3b0c9c24..04143b15 100644 --- a/shared/src/test/diff/nu/HeungTung.mls +++ b/shared/src/test/diff/nu/HeungTung.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper @@ -134,6 +134,9 @@ f(refined if true then 0 else false) // this one can be precise again! //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.133: f(refined if true then 0 else false) // this one can be precise again! //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╔══[ERROR] identifier `refined` not found +//│ ║ l.133: f(refined if true then 0 else false) // this one can be precise again! +//│ ╙── ^^^^^^^ //│ ╔══[ERROR] Illegal use of reserved operator: refined //│ ║ l.133: f(refined if true then 0 else false) // this one can be precise again! //│ ╙── ^^^^^^^ @@ -196,7 +199,7 @@ type T = List[Int] :e // TODO application types type Res = M(T) //│ ╔══[ERROR] Wrong number of type arguments – expected 0, found 1 -//│ ║ l.197: type Res = M(T) +//│ ║ l.200: type Res = M(T) //│ ╙── ^^^^ //│ type Res = M @@ -219,7 +222,7 @@ fun f: Int -> Int fun f: Bool -> Bool fun f = id //│ ╔══[ERROR] A type signature for 'f' was already given -//│ ║ l.219: fun f: Bool -> Bool +//│ ║ l.222: fun f: Bool -> Bool //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ fun f: forall 'a. 'a -> 'a //│ fun f: Int -> Int @@ -227,13 +230,13 @@ fun f = id :e // TODO support f: (Int -> Int) & (Bool -> Bool) //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.228: f: (Int -> Int) & (Bool -> Bool) +//│ ║ l.231: f: (Int -> Int) & (Bool -> Bool) //│ ║ ^ //│ ╟── type `Bool` is not an instance of type `Int` -//│ ║ l.228: f: (Int -> Int) & (Bool -> Bool) +//│ ║ l.231: f: (Int -> Int) & (Bool -> Bool) //│ ║ ^^^^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.218: fun f: Int -> Int +//│ ║ l.221: fun f: Int -> Int //│ ╙── ^^^ //│ Int -> Int & Bool -> Bool //│ res @@ -300,17 +303,20 @@ fun test(x) = refined if x is A then 0 B then 1 //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.299: fun test(x) = refined if x is +//│ ║ l.302: fun test(x) = refined if x is //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.300: A then 0 +//│ ║ l.303: A then 0 //│ ║ ^^^^^^^^^^ -//│ ║ l.301: B then 1 +//│ ║ l.304: B then 1 //│ ╙── ^^^^^^^^^^ +//│ ╔══[ERROR] identifier `refined` not found +//│ ║ l.302: fun test(x) = refined if x is +//│ ╙── ^^^^^^^ //│ ╔══[ERROR] Illegal use of reserved operator: refined -//│ ║ l.299: fun test(x) = refined if x is +//│ ║ l.302: fun test(x) = refined if x is //│ ╙── ^^^^^^^ //│ ╔══[ERROR] identifier not found: refined -//│ ║ l.299: fun test(x) = refined if x is +//│ ║ l.302: fun test(x) = refined if x is //│ ╙── ^^^^^^^ //│ fun test: (A | B) -> error //│ Code generation encountered an error: diff --git a/shared/src/test/diff/nu/Huawei1.mls b/shared/src/test/diff/nu/Huawei1.mls index 7b939fe6..4ca49f19 100644 --- a/shared/src/test/diff/nu/Huawei1.mls +++ b/shared/src/test/diff/nu/Huawei1.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper class C[out A](x: A) { diff --git a/shared/src/test/diff/nu/InterfaceMono.mls b/shared/src/test/diff/nu/InterfaceMono.mls index c19e5930..f5088e03 100644 --- a/shared/src/test/diff/nu/InterfaceMono.mls +++ b/shared/src/test/diff/nu/InterfaceMono.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper trait Showable { @@ -10,6 +10,9 @@ trait Showable { :e trait What0 extends woooo +//│ ╔══[ERROR] could not find definition `woooo` +//│ ║ l.12: trait What0 extends woooo +//│ ╙── ^^^^^ //│ ╔══[ERROR] Could not find definition `woooo` //│ ║ l.12: trait What0 extends woooo //│ ╙── ^^^^^ @@ -30,7 +33,7 @@ class What1(toString: Str) extends Showable :e trait NoShow extends What1("hi") //│ ╔══[ERROR] A trait can only inherit from other traits -//│ ║ l.31: trait NoShow extends What1("hi") +//│ ║ l.34: trait NoShow extends What1("hi") //│ ╙── ^^^^^^^^^^^ //│ trait NoShow extends Showable, What1 @@ -41,19 +44,19 @@ class ErrC2 extends Showable { } class ErrC3(toString: Str -> Str) extends Showable //│ ╔══[ERROR] Member `toString` is declared (or its declaration is inherited) but is not implemented in `ErrC1` -//│ ║ l.38: class ErrC1 extends Showable +//│ ║ l.41: class ErrC1 extends Showable //│ ║ ^^^^^ //│ ╟── Declared here: //│ ║ l.5: fun toString: Str //│ ╙── ^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in definition of method toString: -//│ ║ l.40: fun toString = 114 +//│ ║ l.43: fun toString = 114 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── integer literal of type `114` is not an instance of type `Str` -//│ ║ l.40: fun toString = 114 +//│ ║ l.43: fun toString = 114 //│ ║ ^^^ //│ ╟── but it flows into definition of method toString with expected type `Str` -//│ ║ l.40: fun toString = 114 +//│ ║ l.43: fun toString = 114 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.5: fun toString: Str @@ -62,7 +65,7 @@ class ErrC3(toString: Str -> Str) extends Showable //│ ║ l.5: fun toString: Str //│ ╙── ^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in function type: -//│ ║ l.42: class ErrC3(toString: Str -> Str) extends Showable +//│ ║ l.45: class ErrC3(toString: Str -> Str) extends Showable //│ ║ ^^^^^^^^^^ //│ ╟── type `Str -> Str` is not an instance of type `Str` //│ ╟── Note: constraint arises from type reference: @@ -125,41 +128,41 @@ class Errcity(size: Int) extends SizedStadt { fun bar = "hahaha" } //│ ╔══[ERROR] Type mismatch in definition of method bar: -//│ ║ l.125: fun bar = "hahaha" +//│ ║ l.128: fun bar = "hahaha" //│ ║ ^^^^^^^^^^^^^^ //│ ╟── string literal of type `"hahaha"` is not a function -//│ ║ l.125: fun bar = "hahaha" +//│ ║ l.128: fun bar = "hahaha" //│ ║ ^^^^^^^^ //│ ╟── but it flows into definition of method bar with expected type `Int -> Int` -//│ ║ l.125: fun bar = "hahaha" +//│ ║ l.128: fun bar = "hahaha" //│ ║ ^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from function type: -//│ ║ l.103: fun bar: Int -> Int +//│ ║ l.106: fun bar: Int -> Int //│ ║ ^^^^^^^^^^ //│ ╟── from signature of member `bar`: -//│ ║ l.103: fun bar: Int -> Int +//│ ║ l.106: fun bar: Int -> Int //│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in type reference: -//│ ║ l.124: class Errcity(size: Int) extends SizedStadt { +//│ ║ l.127: class Errcity(size: Int) extends SizedStadt { //│ ║ ^^^ //│ ╟── type `Int` does not match type `1 | 2 | 3` //│ ╟── Note: constraint arises from union type: -//│ ║ l.102: let size: 1 | 2 | 3 +//│ ║ l.105: let size: 1 | 2 | 3 //│ ║ ^^^^^^^^^ //│ ╟── from signature of member `size`: -//│ ║ l.102: let size: 1 | 2 | 3 +//│ ║ l.105: let size: 1 | 2 | 3 //│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Member `name` is declared (or its declaration is inherited) but is not implemented in `Errcity` -//│ ║ l.124: class Errcity(size: Int) extends SizedStadt { +//│ ║ l.127: class Errcity(size: Int) extends SizedStadt { //│ ║ ^^^^^^^ //│ ╟── Declared here: -//│ ║ l.85: let name: Str +//│ ║ l.88: let name: Str //│ ╙── ^^^^^^^^^^^^^ //│ ╔══[ERROR] Member `foo` is declared (or its declaration is inherited) but is not implemented in `Errcity` -//│ ║ l.124: class Errcity(size: Int) extends SizedStadt { +//│ ║ l.127: class Errcity(size: Int) extends SizedStadt { //│ ║ ^^^^^^^ //│ ╟── Declared here: -//│ ║ l.93: fun foo: Bool -> Int +//│ ║ l.96: fun foo: Bool -> Int //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ class Errcity(size: Int) extends RefinedStadt, SizedStadt, Stadt { //│ fun bar: "hahaha" @@ -208,19 +211,19 @@ class Dirtberg extends More, SizedStadt, Fooo { fun size = 4 // this should not check } //│ ╔══[ERROR] Type mismatch in definition of method size: -//│ ║ l.208: fun size = 4 // this should not check +//│ ║ l.211: fun size = 4 // this should not check //│ ║ ^^^^^^^^ //│ ╟── integer literal of type `4` does not match type `1 | 2 | 3` -//│ ║ l.208: fun size = 4 // this should not check +//│ ║ l.211: fun size = 4 // this should not check //│ ║ ^ //│ ╟── but it flows into definition of method size with expected type `1 | 2 | 3` -//│ ║ l.208: fun size = 4 // this should not check +//│ ║ l.211: fun size = 4 // this should not check //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from union type: -//│ ║ l.102: let size: 1 | 2 | 3 +//│ ║ l.105: let size: 1 | 2 | 3 //│ ║ ^^^^^^^^^ //│ ╟── from signature of member `size`: -//│ ║ l.102: let size: 1 | 2 | 3 +//│ ║ l.105: let size: 1 | 2 | 3 //│ ╙── ^^^^^^^^^^^^^^^ //│ class Dirtberg extends RefinedStadt, SizedStadt, Stadt { //│ constructor() @@ -248,19 +251,19 @@ class A { virtual fun x: Int = 1 } :e class B extends A { fun x = "A" } //│ ╔══[ERROR] Type mismatch in definition of method x: -//│ ║ l.249: class B extends A { fun x = "A" } +//│ ║ l.252: class B extends A { fun x = "A" } //│ ║ ^^^^^^^ //│ ╟── string literal of type `"A"` is not an instance of type `Int` -//│ ║ l.249: class B extends A { fun x = "A" } +//│ ║ l.252: class B extends A { fun x = "A" } //│ ║ ^^^ //│ ╟── but it flows into definition of method x with expected type `Int` -//│ ║ l.249: class B extends A { fun x = "A" } +//│ ║ l.252: class B extends A { fun x = "A" } //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.242: class A { virtual fun x: Int = 1 } +//│ ║ l.245: class A { virtual fun x: Int = 1 } //│ ║ ^^^ //│ ╟── from definition of method x: -//│ ║ l.242: class A { virtual fun x: Int = 1 } +//│ ║ l.245: class A { virtual fun x: Int = 1 } //│ ╙── ^^^^^^^^^^ //│ class B extends A { //│ constructor() @@ -270,7 +273,7 @@ class B extends A { fun x = "A" } :e class C1[A] { virtual fun a: A = this.a } //│ ╔══[ERROR] Indirectly-recursive member should have type annotation -//│ ║ l.271: class C1[A] { virtual fun a: A = this.a } +//│ ║ l.274: class C1[A] { virtual fun a: A = this.a } //│ ╙── ^^ //│ class C1[A] { //│ constructor() @@ -304,19 +307,19 @@ class C extends MyTrait[Int] { fun a = 1 } :e class C extends MyTrait[Int] { fun a = false } //│ ╔══[ERROR] Type mismatch in definition of method a: -//│ ║ l.305: class C extends MyTrait[Int] { fun a = false } +//│ ║ l.308: class C extends MyTrait[Int] { fun a = false } //│ ║ ^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of `Int` -//│ ║ l.305: class C extends MyTrait[Int] { fun a = false } +//│ ║ l.308: class C extends MyTrait[Int] { fun a = false } //│ ║ ^^^^^ //│ ╟── but it flows into definition of method a with expected type `Int` -//│ ║ l.305: class C extends MyTrait[Int] { fun a = false } +//│ ║ l.308: class C extends MyTrait[Int] { fun a = false } //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.305: class C extends MyTrait[Int] { fun a = false } +//│ ║ l.308: class C extends MyTrait[Int] { fun a = false } //│ ║ ^^^ //│ ╟── from signature of member `a`: -//│ ║ l.290: trait MyTrait[A] { fun a: A } +//│ ║ l.293: trait MyTrait[A] { fun a: A } //│ ╙── ^^^^ //│ class C extends MyTrait { //│ constructor() @@ -358,19 +361,19 @@ class C3 extends T4{ fun bar = false } //│ ╔══[ERROR] Type mismatch in definition of method foo: -//│ ║ l.357: fun foo = 3 +//│ ║ l.360: fun foo = 3 //│ ║ ^^^^^^^ //│ ╟── integer literal of type `3` does not match type `2` -//│ ║ l.357: fun foo = 3 +//│ ║ l.360: fun foo = 3 //│ ║ ^ //│ ╟── but it flows into definition of method foo with expected type `2` -//│ ║ l.357: fun foo = 3 +//│ ║ l.360: fun foo = 3 //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from literal type: -//│ ║ l.345: fun foo: 2 +//│ ║ l.348: fun foo: 2 //│ ║ ^ //│ ╟── from signature of member `foo`: -//│ ║ l.345: fun foo: 2 +//│ ║ l.348: fun foo: 2 //│ ╙── ^^^^^^ //│ class C3 extends T1, T2, T4 { //│ constructor() @@ -381,24 +384,24 @@ class C3 extends T4{ :e class C2(foo: Int, bar: Str) extends T4 //│ ╔══[ERROR] Type mismatch in type reference: -//│ ║ l.382: class C2(foo: Int, bar: Str) extends T4 +//│ ║ l.385: class C2(foo: Int, bar: Str) extends T4 //│ ║ ^^^ //│ ╟── type `Int` does not match type `2` //│ ╟── Note: constraint arises from literal type: -//│ ║ l.345: fun foo: 2 +//│ ║ l.348: fun foo: 2 //│ ║ ^ //│ ╟── from signature of member `foo`: -//│ ║ l.345: fun foo: 2 +//│ ║ l.348: fun foo: 2 //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type reference: -//│ ║ l.382: class C2(foo: Int, bar: Str) extends T4 +//│ ║ l.385: class C2(foo: Int, bar: Str) extends T4 //│ ║ ^^^ //│ ╟── type `Str` does not match type `Int | false | true` //│ ╟── Note: constraint arises from union type: -//│ ║ l.333: let bar : Int | Bool +//│ ║ l.336: let bar : Int | Bool //│ ║ ^^^^^^^^^^ //│ ╟── from signature of member `bar`: -//│ ║ l.333: let bar : Int | Bool +//│ ║ l.336: let bar : Int | Bool //│ ╙── ^^^^^^^^^^^^^^^^ //│ class C2(foo: Int, bar: Str) extends T1, T2, T4 @@ -407,19 +410,19 @@ trait T5 extends T4 { let foo: 4 } //│ ╔══[ERROR] Type mismatch in signature of member `foo`: -//│ ║ l.407: let foo: 4 +//│ ║ l.410: let foo: 4 //│ ║ ^^^^^^ //│ ╟── type `4` does not match type `2` -//│ ║ l.407: let foo: 4 +//│ ║ l.410: let foo: 4 //│ ║ ^ //│ ╟── but it flows into signature of member `foo` with expected type `2` -//│ ║ l.407: let foo: 4 +//│ ║ l.410: let foo: 4 //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from literal type: -//│ ║ l.345: fun foo: 2 +//│ ║ l.348: fun foo: 2 //│ ║ ^ //│ ╟── from signature of member `foo`: -//│ ║ l.345: fun foo: 2 +//│ ║ l.348: fun foo: 2 //│ ╙── ^^^^^^ //│ trait T5 extends T1, T2, T4 { //│ fun bar: Bool @@ -431,34 +434,34 @@ trait T3 extends T1, T2 { let foo: true } //│ ╔══[ERROR] Type mismatch in signature of member `foo`: -//│ ║ l.431: let foo: true +//│ ║ l.434: let foo: true //│ ║ ^^^^^^^^^ //│ ╟── type `true` does not match type `1 | 2 | 3` -//│ ║ l.431: let foo: true +//│ ║ l.434: let foo: true //│ ║ ^^^^ //│ ╟── but it flows into signature of member `foo` with expected type `1 | 2 | 3` -//│ ║ l.431: let foo: true +//│ ║ l.434: let foo: true //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from union type: -//│ ║ l.328: let foo : 1 | 2 | 3 +//│ ║ l.331: let foo : 1 | 2 | 3 //│ ║ ^^^^^^^^^ //│ ╟── from signature of member `foo`: -//│ ║ l.328: let foo : 1 | 2 | 3 +//│ ║ l.331: let foo : 1 | 2 | 3 //│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in signature of member `foo`: -//│ ║ l.431: let foo: true +//│ ║ l.434: let foo: true //│ ║ ^^^^^^^^^ //│ ╟── type `true` does not match type `2 | 3 | 4` -//│ ║ l.431: let foo: true +//│ ║ l.434: let foo: true //│ ║ ^^^^ //│ ╟── but it flows into signature of member `foo` with expected type `2 | 3 | 4` -//│ ║ l.431: let foo: true +//│ ║ l.434: let foo: true //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from union type: -//│ ║ l.332: let foo : 2 | 3 | 4 +//│ ║ l.335: let foo : 2 | 3 | 4 //│ ║ ^^^^^^^^^ //│ ╟── from signature of member `foo`: -//│ ║ l.332: let foo : 2 | 3 | 4 +//│ ║ l.335: let foo : 2 | 3 | 4 //│ ╙── ^^^^^^^^^^^^^^^ //│ trait T3 extends T1, T2 { //│ fun bar: Bool diff --git a/shared/src/test/diff/nu/Interfaces.mls b/shared/src/test/diff/nu/Interfaces.mls index 995e0eab..11ef7fd1 100644 --- a/shared/src/test/diff/nu/Interfaces.mls +++ b/shared/src/test/diff/nu/Interfaces.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper trait Test { @@ -531,6 +531,9 @@ if b is :e let tt1 = Test +//│ ╔══[ERROR] identifier `Test` is resolved to a type +//│ ║ l.533: let tt1 = Test +//│ ╙── ^^^^ //│ ╔══[ERROR] trait Test cannot be used in term position //│ ║ l.533: let tt1 = Test //│ ╙── ^^^^ @@ -538,14 +541,10 @@ let tt1 = Test //│ Code generation encountered an error: //│ trait used in term position -:e + fun mt(x) = if x is Test then 1 else 0 -//│ ╔══[ERROR] Cannot match on trait `Test` -//│ ║ l.542: fun mt(x) = if x is Test then 1 else 0 -//│ ╙── ^^^^ -//│ fun mt: anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ fun mt: nothing -> error + trait Geo trait ZL extends Geo @@ -607,40 +606,40 @@ z: WP g: ZL e: ZL & WP //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.605: fun fto(w: WP): EM = w +//│ ║ l.604: fun fto(w: WP): EM = w //│ ║ ^ //│ ╟── type `#WP` is not an instance of type `EM` -//│ ║ l.605: fun fto(w: WP): EM = w +//│ ║ l.604: fun fto(w: WP): EM = w //│ ║ ^^ //│ ╟── but it flows into reference with expected type `#EM` -//│ ║ l.605: fun fto(w: WP): EM = w +//│ ║ l.604: fun fto(w: WP): EM = w //│ ║ ^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.605: fun fto(w: WP): EM = w +//│ ║ l.604: fun fto(w: WP): EM = w //│ ╙── ^^ //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.606: z: WP +//│ ║ l.605: z: WP //│ ║ ^ //│ ╟── type `#ZL` is not an instance of type `WP` -//│ ║ l.562: let z: ZL +//│ ║ l.561: let z: ZL //│ ║ ^^ //│ ╟── but it flows into reference with expected type `#WP` -//│ ║ l.606: z: WP +//│ ║ l.605: z: WP //│ ║ ^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.606: z: WP +//│ ║ l.605: z: WP //│ ╙── ^^ //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.607: g: ZL +//│ ║ l.606: g: ZL //│ ║ ^ //│ ╟── type `#Geo` is not an instance of type `ZL` -//│ ║ l.561: let g: Geo +//│ ║ l.560: let g: Geo //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `#ZL` -//│ ║ l.607: g: ZL +//│ ║ l.606: g: ZL //│ ║ ^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.607: g: ZL +//│ ║ l.606: g: ZL //│ ╙── ^^ //│ fun fto: (w: WP) -> EM //│ WP & ZL @@ -696,36 +695,36 @@ class Eh2 extends Bs(true), Ele { fun ce(x) = x } //│ ╔══[ERROR] Type mismatch in definition of method foo: -//│ ║ l.695: fun foo(x) = x && false +//│ ║ l.694: fun foo(x) = x && false //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `Int & ?a` is not an instance of type `Bool` //│ ╟── Note: constraint arises from reference: -//│ ║ l.695: fun foo(x) = x && false +//│ ║ l.694: fun foo(x) = x && false //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition of method foo: -//│ ║ l.695: fun foo(x) = x && false +//│ ║ l.694: fun foo(x) = x && false //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `Int & ?a` does not match type `Bool` //│ ╟── Note: constraint arises from reference: -//│ ║ l.695: fun foo(x) = x && false +//│ ║ l.694: fun foo(x) = x && false //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition of method foo: -//│ ║ l.695: fun foo(x) = x && false +//│ ║ l.694: fun foo(x) = x && false //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `Bool` does not match type `Int | ?a` -//│ ║ l.695: fun foo(x) = x && false +//│ ║ l.694: fun foo(x) = x && false //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from operator application: -//│ ║ l.658: virtual fun foo(x) = x + 1 +//│ ║ l.657: virtual fun foo(x) = x + 1 //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in definition of method foo: -//│ ║ l.695: fun foo(x) = x && false +//│ ║ l.694: fun foo(x) = x && false //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `Bool` does not match type `Int | ?a` -//│ ║ l.695: fun foo(x) = x && false +//│ ║ l.694: fun foo(x) = x && false //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from operator application: -//│ ║ l.658: virtual fun foo(x) = x + 1 +//│ ║ l.657: virtual fun foo(x) = x + 1 //│ ╙── ^^^^^ //│ class Eh2 extends Bs, Ele { //│ constructor() @@ -738,34 +737,34 @@ class Eh extends Bs(1) class Eh1 extends Bs class Eh3 extends Bs(false), Test //│ ╔══[ERROR] Type mismatch in type declaration: -//│ ║ l.737: class Eh extends Bs(1) +//│ ║ l.736: class Eh extends Bs(1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `1` is not an instance of type `Bool` -//│ ║ l.737: class Eh extends Bs(1) +//│ ║ l.736: class Eh extends Bs(1) //│ ║ ^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.657: class Bs(val a: Bool) { +//│ ║ l.656: class Bs(val a: Bool) { //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in type declaration: -//│ ║ l.737: class Eh extends Bs(1) +//│ ║ l.736: class Eh extends Bs(1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `1` does not match type `Bool` -//│ ║ l.737: class Eh extends Bs(1) +//│ ║ l.736: class Eh extends Bs(1) //│ ║ ^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.657: class Bs(val a: Bool) { +//│ ║ l.656: class Bs(val a: Bool) { //│ ╙── ^^^^ //│ ╔══[ERROR] class Bs expects 1 parameter(s); got 0 -//│ ║ l.738: class Eh1 extends Bs +//│ ║ l.737: class Eh1 extends Bs //│ ╙── ^^ //│ ╔══[ERROR] Type mismatch in definition of method foo: -//│ ║ l.658: virtual fun foo(x) = x + 1 +//│ ║ l.657: virtual fun foo(x) = x + 1 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b. ?b)` is not an instance of type `Int` -//│ ║ l.658: virtual fun foo(x) = x + 1 +//│ ║ l.657: virtual fun foo(x) = x + 1 //│ ║ ^^^^^^^^^^^ //│ ╟── but it flows into definition of method foo with expected type `Int` -//│ ║ l.658: virtual fun foo(x) = x + 1 +//│ ║ l.657: virtual fun foo(x) = x + 1 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.5: fun foo: Int @@ -774,7 +773,7 @@ class Eh3 extends Bs(false), Test //│ ║ l.5: fun foo: Int //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Member `bar` is declared (or its declaration is inherited) but is not implemented in `Eh3` -//│ ║ l.739: class Eh3 extends Bs(false), Test +//│ ║ l.738: class Eh3 extends Bs(false), Test //│ ║ ^^^ //│ ╟── Declared here: //│ ║ l.6: fun bar: Bool -> Bool @@ -852,7 +851,7 @@ abstract class Bc3 { :e class Bc12() extends Bc1(1), Bc2(true) //│ ╔══[ERROR] Cannot inherit from more than one base class: Bc1 and Bc2 -//│ ║ l.853: class Bc12() extends Bc1(1), Bc2(true) +//│ ║ l.852: class Bc12() extends Bc1(1), Bc2(true) //│ ╙── ^^^^^^^^^ //│ class Bc12() extends Bc1, Bc2 //│ Code generation encountered an error: @@ -873,24 +872,24 @@ Bc02().foo :e class Bc31(baz: Bool) extends Bc3 //│ ╔══[ERROR] Type mismatch in type reference: -//│ ║ l.874: class Bc31(baz: Bool) extends Bc3 +//│ ║ l.873: class Bc31(baz: Bool) extends Bc3 //│ ║ ^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ╟── Note: constraint arises from type reference: -//│ ║ l.844: let baz : Int +//│ ║ l.843: let baz : Int //│ ║ ^^^ //│ ╟── from signature of member `baz`: -//│ ║ l.844: let baz : Int +//│ ║ l.843: let baz : Int //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in type reference: -//│ ║ l.874: class Bc31(baz: Bool) extends Bc3 +//│ ║ l.873: class Bc31(baz: Bool) extends Bc3 //│ ║ ^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ╟── Note: constraint arises from type reference: -//│ ║ l.844: let baz : Int +//│ ║ l.843: let baz : Int //│ ║ ^^^ //│ ╟── from signature of member `baz`: -//│ ║ l.844: let baz : Int +//│ ║ l.843: let baz : Int //│ ╙── ^^^^^^^^^ //│ class Bc31(baz: Bool) extends Bc3 @@ -925,7 +924,7 @@ trait BInt extends Base[Int] { fun f = error } //│ ╔══[ERROR] Method implementations in traits are not yet supported -//│ ║ l.925: fun f = error +//│ ║ l.924: fun f = error //│ ╙── ^^^^^^^^^^^^^ //│ trait BInt extends Base { //│ fun f: nothing @@ -956,7 +955,7 @@ bp: Base[[Int, Bool]] :e bp: Base[[Int, Int]] //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.957: bp: Base[[Int, Int]] +//│ ║ l.956: bp: Base[[Int, Int]] //│ ║ ^^ //│ ╙── expression of type `true` is not an instance of type `Int` //│ Base[[Int, Int]] @@ -1017,13 +1016,13 @@ trait BInfer2 extends Base { :e class DerBad1 extends Base[Int, Int] //│ ╔══[ERROR] trait Base expects 1 type parameter(s); got 2 -//│ ║ l.1018: class DerBad1 extends Base[Int, Int] +//│ ║ l.1017: class DerBad1 extends Base[Int, Int] //│ ╙── ^^^^^^^^^^^^^ //│ ╔══[ERROR] Member `f` is declared (or its declaration is inherited) but is not implemented in `DerBad1` -//│ ║ l.1018: class DerBad1 extends Base[Int, Int] +//│ ║ l.1017: class DerBad1 extends Base[Int, Int] //│ ║ ^^^^^^^ //│ ╟── Declared here: -//│ ║ l.906: trait Base[A] { fun f: A -> A } +//│ ║ l.905: trait Base[A] { fun f: A -> A } //│ ╙── ^^^^^^^^^^^^^ //│ class DerBad1 extends Base { //│ constructor() @@ -1035,32 +1034,32 @@ class DerBad1 extends Base[Int, Int] :e class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ╔══[ERROR] Type mismatch in definition of method f: -//│ ║ l.1036: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } +//│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `B` does not match type `A` -//│ ║ l.1036: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } +//│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ║ ^ //│ ╟── Note: constraint arises from type parameter: -//│ ║ l.1036: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } +//│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ║ ^ //│ ╟── Note: type parameter B is defined at: -//│ ║ l.1036: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } +//│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition of method f: -//│ ║ l.1036: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } +//│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `A` does not match type `B` -//│ ║ l.1036: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } +//│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ║ ^ //│ ╟── Note: constraint arises from type parameter: -//│ ║ l.1036: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } +//│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ║ ^ //│ ╟── Note: type parameter A is defined at: -//│ ║ l.1036: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } +//│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ╙── ^ //│ class Der2[A, B] extends Base { //│ constructor() -//│ fun f: forall 'a 'b. (['b, 'a]) -> ['a, 'b] +//│ fun f: forall 'a 'b. (['a, 'b]) -> ['b, 'a] //│ } trait Ta[T] { @@ -1108,7 +1107,7 @@ trait Tb extends Ta[Int] { virtual val p = false } //│ ╔══[ERROR] Method implementations in traits are not yet supported -//│ ║ l.1108: virtual val p = false +//│ ║ l.1107: virtual val p = false //│ ╙── ^^^^^^^^^^^^^ //│ trait Tb extends Ta { //│ val g: 'T @@ -1143,24 +1142,24 @@ trait Oz { :e class Fischl(age: Bool) extends Oz //│ ╔══[ERROR] Type mismatch in type reference: -//│ ║ l.1144: class Fischl(age: Bool) extends Oz +//│ ║ l.1143: class Fischl(age: Bool) extends Oz //│ ║ ^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ╟── Note: constraint arises from type reference: -//│ ║ l.1137: let age: Int +//│ ║ l.1136: let age: Int //│ ║ ^^^ //│ ╟── from signature of member `age`: -//│ ║ l.1137: let age: Int +//│ ║ l.1136: let age: Int //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in type reference: -//│ ║ l.1144: class Fischl(age: Bool) extends Oz +//│ ║ l.1143: class Fischl(age: Bool) extends Oz //│ ║ ^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ╟── Note: constraint arises from type reference: -//│ ║ l.1137: let age: Int +//│ ║ l.1136: let age: Int //│ ║ ^^^ //│ ╟── from signature of member `age`: -//│ ║ l.1137: let age: Int +//│ ║ l.1136: let age: Int //│ ╙── ^^^^^^^^ //│ class Fischl(age: Bool) extends Oz @@ -1180,36 +1179,36 @@ class Go extends Fate { fun foo(x) = x && true } //│ ╔══[ERROR] Type mismatch in definition of method foo: -//│ ║ l.1180: fun foo(x) = x && true +//│ ║ l.1179: fun foo(x) = x && true //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `Int & ?a` is not an instance of type `Bool` //│ ╟── Note: constraint arises from reference: -//│ ║ l.1180: fun foo(x) = x && true +//│ ║ l.1179: fun foo(x) = x && true //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition of method foo: -//│ ║ l.1180: fun foo(x) = x && true +//│ ║ l.1179: fun foo(x) = x && true //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `Int & ?a` does not match type `Bool` //│ ╟── Note: constraint arises from reference: -//│ ║ l.1180: fun foo(x) = x && true +//│ ║ l.1179: fun foo(x) = x && true //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition of method foo: -//│ ║ l.1180: fun foo(x) = x && true +//│ ║ l.1179: fun foo(x) = x && true //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `Bool` does not match type `Int | ?a` -//│ ║ l.1180: fun foo(x) = x && true +//│ ║ l.1179: fun foo(x) = x && true //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from operator application: -//│ ║ l.1171: virtual fun foo(x) = x + 1 +//│ ║ l.1170: virtual fun foo(x) = x + 1 //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in definition of method foo: -//│ ║ l.1180: fun foo(x) = x && true +//│ ║ l.1179: fun foo(x) = x && true //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `Bool` does not match type `Int | ?a` -//│ ║ l.1180: fun foo(x) = x && true +//│ ║ l.1179: fun foo(x) = x && true //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from operator application: -//│ ║ l.1171: virtual fun foo(x) = x + 1 +//│ ║ l.1170: virtual fun foo(x) = x + 1 //│ ╙── ^^^^^ //│ class Go extends Fate { //│ constructor() @@ -1228,18 +1227,18 @@ class Haha(x: 1 | 2) extends Ha :e class Ohhh(x: Bool) extends Ha //│ ╔══[ERROR] Type mismatch in type reference: -//│ ║ l.1229: class Ohhh(x: Bool) extends Ha +//│ ║ l.1228: class Ohhh(x: Bool) extends Ha //│ ║ ^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ╟── Note: constraint arises from type reference: -//│ ║ l.1219: class Ha { virtual val x: Int = 1 } +//│ ║ l.1218: class Ha { virtual val x: Int = 1 } //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in type reference: -//│ ║ l.1229: class Ohhh(x: Bool) extends Ha +//│ ║ l.1228: class Ohhh(x: Bool) extends Ha //│ ║ ^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ╟── Note: constraint arises from type reference: -//│ ║ l.1219: class Ha { virtual val x: Int = 1 } +//│ ║ l.1218: class Ha { virtual val x: Int = 1 } //│ ╙── ^^^ //│ class Ohhh(x: Bool) extends Ha diff --git a/shared/src/test/diff/nu/LetRec.mls b/shared/src/test/diff/nu/LetRec.mls index 1ff16fa4..cb0176f7 100644 --- a/shared/src/test/diff/nu/LetRec.mls +++ b/shared/src/test/diff/nu/LetRec.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper :js @@ -145,6 +145,9 @@ let rec f = // :e // FIXME :ge let foo = foo +//│ ╔══[ERROR] identifier `foo` not found +//│ ║ l.147: let foo = foo +//│ ╙── ^^^ //│ let foo: nothing //│ Code generation encountered an error: //│ unguarded recursive use of by-value binding foo diff --git a/shared/src/test/diff/nu/ListConsNil.mls b/shared/src/test/diff/nu/ListConsNil.mls index 7fab3f51..d705f6d2 100644 --- a/shared/src/test/diff/nu/ListConsNil.mls +++ b/shared/src/test/diff/nu/ListConsNil.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper @@ -42,7 +42,7 @@ fun test(x, l) = list_assoc(42, Cons(x, l)) fun test(x, l) = if l is Nil then list_assoc(42, Cons(x, l)) Cons(h, t) then list_assoc(42, Cons(h, t)) -//│ fun test: forall 'A 'A0. ({_1: anything, _2: 'A}, Cons[{_1: anything, _2: 'A0}] | Nil) -> (Cons['A] | Nil | Cons['A0]) +//│ fun test: forall 'A. ({_1: anything, _2: 'A}, Cons[{_1: anything, _2: 'A}] | Nil) -> (Cons['A] | Nil) diff --git a/shared/src/test/diff/nu/LitMatch.mls b/shared/src/test/diff/nu/LitMatch.mls index a2e74b8f..ab8f51e8 100644 --- a/shared/src/test/diff/nu/LitMatch.mls +++ b/shared/src/test/diff/nu/LitMatch.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper let r = false : Bool @@ -22,17 +22,21 @@ b : true | false //│ = false if false is false then 0 +//│ ╙── //│ 0 //│ res //│ = 0 fun foo(x) = if x is false then 0 +//│ ╙── //│ fun foo: false -> 0 fun foo(x) = if x is false then 0 true then 1 +//│ ╙── +//│ ╙── //│ fun foo: Bool -> (0 | 1) diff --git a/shared/src/test/diff/nu/MissingTypeArg.mls b/shared/src/test/diff/nu/MissingTypeArg.mls index 1eba54d9..fe7011e8 100644 --- a/shared/src/test/diff/nu/MissingTypeArg.mls +++ b/shared/src/test/diff/nu/MissingTypeArg.mls @@ -1,6 +1,6 @@ // * This is an example program where the error we get is really not ideal -:NewDefs +:PreTyper // * An example recursive definition: diff --git a/shared/src/test/diff/nu/NamedArgs.mls b/shared/src/test/diff/nu/NamedArgs.mls index 8c4d4606..4933c1d6 100644 --- a/shared/src/test/diff/nu/NamedArgs.mls +++ b/shared/src/test/diff/nu/NamedArgs.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper fun test(x: 'a) = if x is undefined then 0 else x + 1 @@ -150,6 +150,12 @@ fun fff(x: Int, y: Int, z: Int) = (x - y) * z // * Testing renaming :e fff(y: 2, z: y_1 + 1, x: z_1 - 2) +//│ ╔══[ERROR] identifier `y_1` not found +//│ ║ l.152: fff(y: 2, z: y_1 + 1, x: z_1 - 2) +//│ ╙── ^^^ +//│ ╔══[ERROR] identifier `z_1` not found +//│ ║ l.152: fff(y: 2, z: y_1 + 1, x: z_1 - 2) +//│ ╙── ^^^ //│ ╔══[ERROR] identifier not found: y_1 //│ ║ l.152: fff(y: 2, z: y_1 + 1, x: z_1 - 2) //│ ╙── ^^^ @@ -233,7 +239,7 @@ fun print(x) = (y, z) => log([x, y, z]) let p = print(0) p(z: 1, y: 2) //│ ╔══[ERROR] Cannot use named arguments as the function type has untyped arguments -//│ ║ l.234: p(z: 1, y: 2) +//│ ║ l.240: p(z: 1, y: 2) //│ ╙── ^^^^^^^^^^^^ //│ fun print: anything -> (anything, anything) -> () //│ let p: (anything, anything) -> () @@ -288,8 +294,11 @@ z.y :e (f => f(x: a)) +//│ ╔══[ERROR] identifier `a` not found +//│ ║ l.296: (f => f(x: a)) +//│ ╙── ^ //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments -//│ ║ l.290: (f => f(x: a)) +//│ ║ l.296: (f => f(x: a)) //│ ╙── ^ //│ anything -> error //│ Code generation encountered an error: @@ -297,8 +306,11 @@ z.y :e (f => f)(error)(x: a) +//│ ╔══[ERROR] identifier `a` not found +//│ ║ l.308: (f => f)(error)(x: a) +//│ ╙── ^ //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments -//│ ║ l.299: (f => f)(error)(x: a) +//│ ║ l.308: (f => f)(error)(x: a) //│ ╙── ^^^^^^^^^^^^^^^ //│ error //│ Code generation encountered an error: @@ -306,8 +318,11 @@ z.y :e (f => f)(42)(x: a) +//│ ╔══[ERROR] identifier `a` not found +//│ ║ l.320: (f => f)(42)(x: a) +//│ ╙── ^ //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments -//│ ║ l.308: (f => f)(42)(x: a) +//│ ║ l.320: (f => f)(42)(x: a) //│ ╙── ^^^^^^^^^^^^ //│ error //│ Code generation encountered an error: @@ -315,8 +330,11 @@ z.y :e (f => f)(if true then 123 else false)(x: a) +//│ ╔══[ERROR] identifier `a` not found +//│ ║ l.332: (f => f)(if true then 123 else false)(x: a) +//│ ╙── ^ //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments -//│ ║ l.317: (f => f)(if true then 123 else false)(x: a) +//│ ║ l.332: (f => f)(if true then 123 else false)(x: a) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ error //│ Code generation encountered an error: @@ -330,7 +348,7 @@ z.y :e (f => if true then f else id)(if true then (x: Int) => x + 1 else id)(x: 123) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments -//│ ║ l.331: (f => if true then f else id)(if true then (x: Int) => x + 1 else id)(x: 123) +//│ ║ l.349: (f => if true then f else id)(if true then (x: Int) => x + 1 else id)(x: 123) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ error //│ res @@ -339,7 +357,7 @@ z.y :e (f => if true then f else id)(if true then (x: Int) => x + 1 else (x: Int) => x + 1)(x: 123) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments -//│ ║ l.340: (f => if true then f else id)(if true then (x: Int) => x + 1 else (x: Int) => x + 1)(x: 123) +//│ ║ l.358: (f => if true then f else id)(if true then (x: Int) => x + 1 else (x: Int) => x + 1)(x: 123) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ error //│ res @@ -361,7 +379,7 @@ foo((x: Int) => 1) :e fun foo(f: ((x: Int) => Int) | 'a) = f(x: 123) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `'a | (x: Int) -> Int` for applying named arguments -//│ ║ l.362: fun foo(f: ((x: Int) => Int) | 'a) = f(x: 123) +//│ ║ l.380: fun foo(f: ((x: Int) => Int) | 'a) = f(x: 123) //│ ╙── ^ //│ fun foo: (f: anything) -> error @@ -370,7 +388,7 @@ fun foo(f: ((x: Int) => Int) | 'a) = f(x: 123) :e fun foo(x) = (if true then (x: Int) => x + 1 else x)(x: 123) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `(x: Int) -> ?a | ?b` for applying named arguments -//│ ║ l.371: fun foo(x) = (if true then (x: Int) => x + 1 else x)(x: 123) +//│ ║ l.389: fun foo(x) = (if true then (x: Int) => x + 1 else x)(x: 123) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ fun foo: anything -> error @@ -383,7 +401,7 @@ foo((y: Int) => y) :e // TODO later: this could be made to work... fun foo(x) = (if true then (x: Int) => x + 1 else (x: Int) => x + 1)(x: 123) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `(x: Int) -> (?a | ?b)` for applying named arguments -//│ ║ l.384: fun foo(x) = (if true then (x: Int) => x + 1 else (x: Int) => x + 1)(x: 123) +//│ ║ l.402: fun foo(x) = (if true then (x: Int) => x + 1 else (x: Int) => x + 1)(x: 123) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ fun foo: anything -> error @@ -394,7 +412,7 @@ fun foo(x) = if true then (x: Int) => x + 1 else x :e foo((y: Int) => y)(x: 123) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments -//│ ║ l.395: foo((y: Int) => y)(x: 123) +//│ ║ l.413: foo((y: Int) => y)(x: 123) //│ ╙── ^^^^^^^^^^^^^^^^^^ //│ error //│ res @@ -403,7 +421,7 @@ foo((y: Int) => y)(x: 123) :e foo((x: Int) => x - 1)(x: 123) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments -//│ ║ l.404: foo((x: Int) => x - 1)(x: 123) +//│ ║ l.422: foo((x: Int) => x - 1)(x: 123) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ //│ error //│ res @@ -416,7 +434,7 @@ fun foo1(x) = [x + 1, x] :e foo1(x: 123) //│ ╔══[ERROR] Cannot use named arguments as the function type has untyped arguments -//│ ║ l.417: foo1(x: 123) +//│ ║ l.435: foo1(x: 123) //│ ╙── ^^^^^^^^ //│ error //│ res diff --git a/shared/src/test/diff/nu/New.mls b/shared/src/test/diff/nu/New.mls index a145d88f..945effc1 100644 --- a/shared/src/test/diff/nu/New.mls +++ b/shared/src/test/diff/nu/New.mls @@ -13,10 +13,18 @@ let f = Foo(1) // let f = new Foo(1) if f is Foo then 1 else 0 +//│ ╔══[WARNING] old desugarer used +//│ ║ l.15: if f is Foo then 1 else 0 +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ res: 0 | 1 //│ = 1 if f is Foo(a) then a else 0 +//│ ╔══[WARNING] old desugarer used +//│ ║ l.23: if f is Foo(a) then a else 0 +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ res: 0 | 1 //│ = 1 @@ -31,6 +39,10 @@ if f is Foo(a) then a else 0 fun test(x) = if x is Foo(a) then a +//│ ╔══[WARNING] old desugarer used +//│ ║ l.41: fun test(x) = if x is Foo(a) then a +//│ ╙── ^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ test: (Foo & {x: 'x}) -> 'x //│ = [Function: test] diff --git a/shared/src/test/diff/nu/NewNew.mls b/shared/src/test/diff/nu/NewNew.mls index a2e039d7..5cfa6d96 100644 --- a/shared/src/test/diff/nu/NewNew.mls +++ b/shared/src/test/diff/nu/NewNew.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper class Foo(val x: Int) @@ -183,6 +183,9 @@ type T = PoInt[Str] :e new T(0, 0) +//│ ╔══[ERROR] identifier `T` is resolved to a type +//│ ║ l.185: new T(0, 0) +//│ ╙── ^ //│ ╔══[ERROR] Type alias T cannot be used in `new` expression //│ ║ l.185: new T(0, 0) //│ ╙── ^^^^^^^^^^^ @@ -198,7 +201,7 @@ let origin = new PoInt(0, 0) :e // TODO support let origin = PoInt[Int](0, 0) //│ ╔══[ERROR] Type application syntax is not yet supported -//│ ║ l.199: let origin = PoInt[Int](0, 0) +//│ ║ l.202: let origin = PoInt[Int](0, 0) //│ ╙── ^^^^^^^^^^ //│ let origin: PoInt[0] //│ origin @@ -207,7 +210,7 @@ let origin = PoInt[Int](0, 0) :e // TODO support let origin = new PoInt[Int](0, 0) //│ ╔══[ERROR] Type arguments in `new` expressions are not yet supported -//│ ║ l.208: let origin = new PoInt[Int](0, 0) +//│ ║ l.211: let origin = new PoInt[Int](0, 0) //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ let origin: PoInt[0] //│ Code generation encountered an error: @@ -217,7 +220,7 @@ let origin = new PoInt[Int](0, 0) :e // TODO support new {} //│ ╔══[ERROR] Unexpected type `anything` after `new` keyword -//│ ║ l.218: new {} +//│ ║ l.221: new {} //│ ╙── ^^ //│ error //│ Code generation encountered an error: @@ -227,10 +230,10 @@ new {} :e new //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here -//│ ║ l.228: new +//│ ║ l.231: new //│ ╙── ^ //│ ╔══[ERROR] Unexpected type `()` after `new` keyword -//│ ║ l.228: new +//│ ║ l.231: new //│ ╙── ^ //│ error //│ Code generation encountered an error: @@ -241,11 +244,14 @@ new :e new x: 0 +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.246: x: 0 +//│ ╙── ^ //│ ╔══[PARSE ERROR] Not a recognized type -//│ ║ l.243: x: 0 +//│ ║ l.246: x: 0 //│ ╙── ^ //│ ╔══[ERROR] Unexpected type `nothing` after `new` keyword -//│ ║ l.243: x: 0 +//│ ║ l.246: x: 0 //│ ╙── ^ //│ error //│ Code generation encountered an error: @@ -260,7 +266,7 @@ fun f(x) = {x} :e new f(1) //│ ╔══[ERROR] type identifier not found: f -//│ ║ l.261: new f(1) +//│ ║ l.267: new f(1) //│ ╙── ^ //│ error //│ res @@ -273,7 +279,7 @@ module Oops :e new Oops //│ ╔══[ERROR] Module Oops cannot be used in `new` expression -//│ ║ l.274: new Oops +//│ ║ l.280: new Oops //│ ╙── ^^^^ //│ error //│ res @@ -283,8 +289,11 @@ new Oops :e new Oops2 trait Oops2 +//│ ╔══[ERROR] identifier `Oops2` is resolved to a type +//│ ║ l.290: new Oops2 +//│ ╙── ^^^^^ //│ ╔══[ERROR] Trait Oops2 cannot be used in `new` expression -//│ ║ l.284: new Oops2 +//│ ║ l.290: new Oops2 //│ ╙── ^^^^^ //│ trait Oops2 //│ error diff --git a/shared/src/test/diff/nu/Object.mls b/shared/src/test/diff/nu/Object.mls index 9e93dec2..de484994 100644 --- a/shared/src/test/diff/nu/Object.mls +++ b/shared/src/test/diff/nu/Object.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper @@ -88,6 +88,9 @@ fun foo = forall 'a; (x: 'a) => if x is A then true else false :e Object +//│ ╔══[ERROR] identifier `Object` not found +//│ ║ l.90: Object +//│ ╙── ^^^^^^ //│ ╔══[ERROR] Class Object is abstract and cannot be instantiated //│ ║ l.90: Object //│ ╙── ^^^^^^ @@ -100,11 +103,14 @@ Object :e Object() +//│ ╔══[ERROR] identifier `Object` not found +//│ ║ l.105: Object() +//│ ╙── ^^^^^^ //│ ╔══[ERROR] Class Object is abstract and cannot be instantiated -//│ ║ l.102: Object() +//│ ║ l.105: Object() //│ ╙── ^^^^^^ //│ ╔══[ERROR] Class Object cannot be instantiated as it exposes no constructor -//│ ║ l.102: Object() +//│ ║ l.105: Object() //│ ╙── ^^^^^^ //│ error //│ Code generation encountered an error: @@ -112,8 +118,11 @@ Object() :e new Object +//│ ╔══[ERROR] identifier `Object` not found +//│ ║ l.120: new Object +//│ ╙── ^^^^^^ //│ ╔══[ERROR] Class Object is abstract and cannot be instantiated -//│ ║ l.114: new Object +//│ ║ l.120: new Object //│ ╙── ^^^^^^ //│ Object //│ Code generation encountered an error: @@ -123,6 +132,9 @@ new Object // TODO class B() extends Object +//│ ╔══[ERROR] could not find definition `Object` +//│ ║ l.134: class B() extends Object +//│ ╙── ^^^^^^ //│ class B() extends Object //│ Code generation encountered an error: //│ unresolved parent Object. diff --git a/shared/src/test/diff/nu/OpLam.mls b/shared/src/test/diff/nu/OpLam.mls index bedc2cd4..78ce0e69 100644 --- a/shared/src/test/diff/nu/OpLam.mls +++ b/shared/src/test/diff/nu/OpLam.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper x => x is 42 @@ -105,6 +105,12 @@ x => x + 2 :e x => x.y => y +//│ ╔══[ERROR] unsupported pattern shape +//│ ║ l.107: x => x.y => y +//│ ╙── ^^^ +//│ ╔══[ERROR] identifier `y` not found +//│ ║ l.107: x => x.y => y +//│ ╙── ^ //│ ╔══[ERROR] Unsupported pattern shape: //│ ║ l.107: x => x.y => y //│ ╙── ^^^ diff --git a/shared/src/test/diff/nu/OptionFilter.mls b/shared/src/test/diff/nu/OptionFilter.mls index 53072213..31879983 100644 --- a/shared/src/test/diff/nu/OptionFilter.mls +++ b/shared/src/test/diff/nu/OptionFilter.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper // * Minimization of code that used to cause a problem: @@ -32,7 +32,7 @@ module None extends Option[nothing] { //│ fun filter: (p: T -> Bool) -> Option[T] //│ } //│ class Some[T](value: T) extends Option { -//│ fun filter: (T -> Object) -> (None | Some[T]) +//│ fun filter: (T -> Bool) -> (None | Some[T]) //│ } //│ module None extends Option { //│ fun filter: anything -> None diff --git a/shared/src/test/diff/nu/OverrideShorthand.mls b/shared/src/test/diff/nu/OverrideShorthand.mls index 2bd82353..e9e61e59 100644 --- a/shared/src/test/diff/nu/OverrideShorthand.mls +++ b/shared/src/test/diff/nu/OverrideShorthand.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper diff --git a/shared/src/test/diff/nu/ParamPassing.mls b/shared/src/test/diff/nu/ParamPassing.mls index 0fc6a4af..0c08b497 100644 --- a/shared/src/test/diff/nu/ParamPassing.mls +++ b/shared/src/test/diff/nu/ParamPassing.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper class Foo(x: Int) @@ -66,6 +66,12 @@ Foo(1).x :e Foo(1).#x +//│ ╔══[ERROR] identifier `.#` not found +//│ ║ l.68: Foo(1).#x +//│ ╙── ^^ +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.68: Foo(1).#x +//│ ╙── ^ //│ ╔══[ERROR] identifier not found: .# //│ ║ l.68: Foo(1).#x //│ ╙── ^^ @@ -99,20 +105,20 @@ if Foo(1) is Foo(x) then x :e class Bar(x: Int) extends Foo(x) //│ ╔══[ERROR] Inherited parameter named `x` is not virtual and cannot be overridden -//│ ║ l.100: class Bar(x: Int) extends Foo(x) +//│ ║ l.106: class Bar(x: Int) extends Foo(x) //│ ║ ^ //│ ╟── Originally declared here: -//│ ║ l.85: class Foo(val x: Int) +//│ ║ l.91: class Foo(val x: Int) //│ ╙── ^ //│ class Bar(x: Int) extends Foo :e Bar(11).x //│ ╔══[ERROR] Parameter 'x' cannot tbe accessed as a field -//│ ║ l.110: Bar(11).x +//│ ║ l.116: Bar(11).x //│ ║ ^^ //│ ╟── Either make the parameter a `val` or access it through destructuring -//│ ║ l.100: class Bar(x: Int) extends Foo(x) +//│ ║ l.106: class Bar(x: Int) extends Foo(x) //│ ╙── ^ //│ Int | error //│ res @@ -122,10 +128,10 @@ Bar(11).x :e class Bar(val x: Int) extends Foo(x + 1) //│ ╔══[ERROR] Inherited parameter named `x` is not virtual and cannot be overridden -//│ ║ l.123: class Bar(val x: Int) extends Foo(x + 1) +//│ ║ l.129: class Bar(val x: Int) extends Foo(x + 1) //│ ║ ^ //│ ╟── Originally declared here: -//│ ║ l.85: class Foo(val x: Int) +//│ ║ l.91: class Foo(val x: Int) //│ ╙── ^ //│ class Bar(x: Int) extends Foo @@ -137,10 +143,10 @@ Bar(11).x :e class Bar extends Foo(1) { val x: 2 } //│ ╔══[ERROR] Inherited parameter named `x` is not virtual and cannot be overridden -//│ ║ l.138: class Bar extends Foo(1) { val x: 2 } +//│ ║ l.144: class Bar extends Foo(1) { val x: 2 } //│ ║ ^^^^^^^^ //│ ╟── Originally declared here: -//│ ║ l.85: class Foo(val x: Int) +//│ ║ l.91: class Foo(val x: Int) //│ ╙── ^ //│ class Bar extends Foo { //│ constructor() @@ -150,10 +156,10 @@ class Bar extends Foo(1) { val x: 2 } :e module Bar extends Foo(1) { fun x = 2 } //│ ╔══[ERROR] Inherited parameter named `x` is not virtual and cannot be overridden -//│ ║ l.151: module Bar extends Foo(1) { fun x = 2 } +//│ ║ l.157: module Bar extends Foo(1) { fun x = 2 } //│ ║ ^^^^^ //│ ╟── Originally declared here: -//│ ║ l.85: class Foo(val x: Int) +//│ ║ l.91: class Foo(val x: Int) //│ ╙── ^ //│ module Bar extends Foo { //│ fun x: 2 @@ -181,10 +187,10 @@ module B extends A(42) :e B.x //│ ╔══[ERROR] Parameter 'x' cannot tbe accessed as a field -//│ ║ l.182: B.x +//│ ║ l.188: B.x //│ ║ ^^ //│ ╟── Either make the parameter a `val` or access it through destructuring -//│ ║ l.175: class A(x: Int) +//│ ║ l.181: class A(x: Int) //│ ╙── ^ //│ Int | error //│ res @@ -223,16 +229,16 @@ module Bazz extends Foo(0) { val x: 2 } //│ ╔══[ERROR] Inherited parameter named `x` is not virtual and cannot be overridden -//│ ║ l.223: val x: 2 +//│ ║ l.229: val x: 2 //│ ║ ^^^^^^^^ //│ ╟── Originally declared here: -//│ ║ l.195: abstract class Foo[A](val x: A) { fun y = x; fun i: A -> A } +//│ ║ l.201: abstract class Foo[A](val x: A) { fun y = x; fun i: A -> A } //│ ╙── ^ //│ ╔══[ERROR] Member `i` is declared (or its declaration is inherited) but is not implemented in `Bazz` -//│ ║ l.222: module Bazz extends Foo(0) { +//│ ║ l.228: module Bazz extends Foo(0) { //│ ║ ^^^^ //│ ╟── Declared here: -//│ ║ l.195: abstract class Foo[A](val x: A) { fun y = x; fun i: A -> A } +//│ ║ l.201: abstract class Foo[A](val x: A) { fun y = x; fun i: A -> A } //│ ╙── ^^^^^^^^^^^^^ //│ module Bazz extends Foo { //│ fun i: 'A -> 'A diff --git a/shared/src/test/diff/nu/PolymorphicVariants_Alt.mls b/shared/src/test/diff/nu/PolymorphicVariants_Alt.mls index a9355ce6..f636b0f4 100644 --- a/shared/src/test/diff/nu/PolymorphicVariants_Alt.mls +++ b/shared/src/test/diff/nu/PolymorphicVariants_Alt.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper :NoJS @@ -112,7 +112,7 @@ module Test1 extends EvalVar, EvalLambda Test1.eval(Nil(), Var("a")) //│ 'a //│ where -//│ 'a :> App['a] | Abs['a] | Var +//│ 'a :> Abs['a] | Var | App['a] Test1.eval(Nil(), Abs("b", Var("a"))) //│ 'a @@ -127,7 +127,7 @@ Test1.eval(Cons(["c", Var("d")], Nil()), App(Abs("b", Var("b")), Var("c"))) Test1.eval(Cons(["c", Abs("d", Var("d"))], Nil()), App(Abs("b", Var("b")), Var("c"))) //│ 'a //│ where -//│ 'a :> App['a] | Abs['a] | Abs[Var] | Var +//│ 'a :> Abs[Var] | Var | App['a] | Abs['a] class Numb(n: Int) class Add(l: A, r: A) @@ -142,7 +142,7 @@ fun map_expr(f, v) = Numb then v Add(l, r) then Add(f(l), f(r)) Mul(l, r) then Mul(f(l), f(r)) -//│ fun map_expr: forall 'A 'a 'A0 'b. ('a -> 'A0 & 'b -> 'A, Add['a] | Mul['b] | Numb | Var) -> (Add['A0] | Mul['A] | Numb | Var) +//│ fun map_expr: forall 'a 'A 'b 'A0. ('b -> 'A0 & 'a -> 'A, Add['a] | Mul['b] | Numb | Var) -> (Add['A] | Mul['A0] | Numb | Var) mixin EvalExpr { fun eval(sub, v) = diff --git a/shared/src/test/diff/nu/PostHocMixinSignature.mls b/shared/src/test/diff/nu/PostHocMixinSignature.mls index 5c23e3c3..ec7e6a0d 100644 --- a/shared/src/test/diff/nu/PostHocMixinSignature.mls +++ b/shared/src/test/diff/nu/PostHocMixinSignature.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper mixin Foo { @@ -8,7 +8,7 @@ mixin Foo { //│ fun foo: 'a -> 'a //│ } //│ where -//│ 'a <: {a: Object, b: 'a} +//│ 'a <: {a: Bool, b: 'a} module ValA { @@ -34,7 +34,7 @@ module Test extends Foo //│ fun foo: forall 'a. 'a -> 'a //│ } //│ where -//│ 'a <: {a: Object, b: 'a} +//│ 'a <: {a: Bool, b: 'a} [Test.foo(ValA), Test.foo(ValB)] //│ [ValA | ValB, ValA | ValB] diff --git a/shared/src/test/diff/nu/PrivateMemberOverriding.mls b/shared/src/test/diff/nu/PrivateMemberOverriding.mls index e7977a41..a3aa2596 100644 --- a/shared/src/test/diff/nu/PrivateMemberOverriding.mls +++ b/shared/src/test/diff/nu/PrivateMemberOverriding.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper class Foo(x: Int) diff --git a/shared/src/test/diff/nu/RefinedPattern.mls b/shared/src/test/diff/nu/RefinedPattern.mls index a6eec809..c9283801 100644 --- a/shared/src/test/diff/nu/RefinedPattern.mls +++ b/shared/src/test/diff/nu/RefinedPattern.mls @@ -30,7 +30,7 @@ test(D(123)) :e refined -//│ ╔══[ERROR] Variable refined not found in scope +//│ ╔══[ERROR] identifier `refined` not found //│ ║ l.32: refined //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Illegal use of reserved operator: refined diff --git a/shared/src/test/diff/nu/SelfRec.mls b/shared/src/test/diff/nu/SelfRec.mls index 3a68af47..fba85a5d 100644 --- a/shared/src/test/diff/nu/SelfRec.mls +++ b/shared/src/test/diff/nu/SelfRec.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper diff --git a/shared/src/test/diff/nu/Subscripts.mls b/shared/src/test/diff/nu/Subscripts.mls index eb2812db..7668fe6b 100644 --- a/shared/src/test/diff/nu/Subscripts.mls +++ b/shared/src/test/diff/nu/Subscripts.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper let xs = [0, 1, 2] diff --git a/shared/src/test/diff/nu/TODO_Classes.mls b/shared/src/test/diff/nu/TODO_Classes.mls index 8df39440..34699bb1 100644 --- a/shared/src/test/diff/nu/TODO_Classes.mls +++ b/shared/src/test/diff/nu/TODO_Classes.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper diff --git a/shared/src/test/diff/nu/Unapply.mls b/shared/src/test/diff/nu/Unapply.mls index 9dcc6a93..8bf62edc 100644 --- a/shared/src/test/diff/nu/Unapply.mls +++ b/shared/src/test/diff/nu/Unapply.mls @@ -1,6 +1,6 @@ // *** Explicit unapply tests *** // -:NewDefs +:PreTyper // * Unapply's current compilation strategy: diff --git a/shared/src/test/diff/nu/UndefMatching.mls b/shared/src/test/diff/nu/UndefMatching.mls index 57453f5d..3050b227 100644 --- a/shared/src/test/diff/nu/UndefMatching.mls +++ b/shared/src/test/diff/nu/UndefMatching.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper diff --git a/shared/src/test/diff/nu/WeirdUnions.mls b/shared/src/test/diff/nu/WeirdUnions.mls index 89326b75..9194fd1e 100644 --- a/shared/src/test/diff/nu/WeirdUnions.mls +++ b/shared/src/test/diff/nu/WeirdUnions.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper diff --git a/shared/src/test/diff/nu/i180.mls b/shared/src/test/diff/nu/i180.mls index 39013ced..4b1e0330 100644 --- a/shared/src/test/diff/nu/i180.mls +++ b/shared/src/test/diff/nu/i180.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper fun (++) stringConcat(a, b) = concat(a)(b) diff --git a/shared/src/test/diff/nu/repro0.mls b/shared/src/test/diff/nu/repro0.mls index c02b9aa1..61095fca 100644 --- a/shared/src/test/diff/nu/repro0.mls +++ b/shared/src/test/diff/nu/repro0.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper :NoJS @@ -9,6 +9,9 @@ module EvalAddLit { if e is Add then eval(e.lhs) } let res = EvalAddLit.eval(add11) +//│ ╔══[ERROR] identifier `add11` not found +//│ ║ l.6: val add11 = Add(add11) +//│ ╙── ^^^^^ //│ class Add[E](lhs: E) //│ val add11: 'E //│ module EvalAddLit { diff --git a/shared/src/test/diff/nu/repro1.mls b/shared/src/test/diff/nu/repro1.mls index 09c7cb83..7db26f2c 100644 --- a/shared/src/test/diff/nu/repro1.mls +++ b/shared/src/test/diff/nu/repro1.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper :NoJS diff --git a/shared/src/test/diff/nu/repro_EvalNegNeg.mls b/shared/src/test/diff/nu/repro_EvalNegNeg.mls index af761f40..e0aee8d9 100644 --- a/shared/src/test/diff/nu/repro_EvalNegNeg.mls +++ b/shared/src/test/diff/nu/repro_EvalNegNeg.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper class Add(lhs: E, rhs: E) @@ -45,8 +45,8 @@ TestLang.eval(mk(0)) //│ Int //│ res //│ = 0 -//│ constrain calls : 211 -//│ annoying calls : 51 -//│ subtyping calls : 1399 +//│ constrain calls : 299 +//│ annoying calls : 106 +//│ subtyping calls : 1746 diff --git a/shared/src/test/diff/nu/repro_PolymorphicVariants.mls b/shared/src/test/diff/nu/repro_PolymorphicVariants.mls index 461bada4..2c1624a9 100644 --- a/shared/src/test/diff/nu/repro_PolymorphicVariants.mls +++ b/shared/src/test/diff/nu/repro_PolymorphicVariants.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper // Test that reprduces subtle type simplification bug diff --git a/shared/src/test/diff/pretyper/ucs/RecordPattern.mls b/shared/src/test/diff/pretyper/ucs/RecordPattern.mls index beffd4bb..e5414074 100644 --- a/shared/src/test/diff/pretyper/ucs/RecordPattern.mls +++ b/shared/src/test/diff/pretyper/ucs/RecordPattern.mls @@ -5,13 +5,13 @@ fun take_1(p) = if p is { x, y } then x + y else 0 -//│ ╔══[ERROR] Unknown pattern Bra(true,Rcd(List((Var(x),Fld(_,Var(x))), (Var(y),Fld(_,Var(y)))))) +//│ ╔══[ERROR] Unknown pattern '{' {x: x, y: y} '}' //│ ║ l.6: { x, y } then x + y //│ ╙── ^^^^^^^^ -//│ ╔══[ERROR] Variable x not found in scope +//│ ╔══[ERROR] identifier `x` not found //│ ║ l.6: { x, y } then x + y //│ ╙── ^ -//│ ╔══[ERROR] Variable y not found in scope +//│ ╔══[ERROR] identifier `y` not found //│ ║ l.6: { x, y } then x + y //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x diff --git a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls index 563791bf..5675d5d0 100644 --- a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls +++ b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls @@ -27,10 +27,10 @@ fun example2(p) = Pair(x, y) and p1(x) and p1(y) then "both negative" Pair(a, b) and p2(a) and p2(b) then x + y else "nah" -//│ ╔══[ERROR] Variable x not found in scope +//│ ╔══[ERROR] identifier `x` not found //│ ║ l.28: Pair(a, b) and p2(a) and p2(b) then x + y //│ ╙── ^ -//│ ╔══[ERROR] Variable y not found in scope +//│ ╔══[ERROR] identifier `y` not found //│ ║ l.28: Pair(a, b) and p2(a) and p2(b) then x + y //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x diff --git a/shared/src/test/diff/pretyper/ucs/examples/Option.mls b/shared/src/test/diff/pretyper/ucs/examples/Option.mls index 32a2706f..79035757 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/Option.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/Option.mls @@ -13,7 +13,7 @@ module MyNone extends MyOption[nothing] { //│ fun filter: (p: T -> Bool) -> MyOption[T] //│ } //│ class MySome[T](value: T) extends MyOption { -//│ fun filter: (T -> Object) -> (MyNone | MySome[T]) +//│ fun filter: (T -> Bool) -> (MyNone | MySome[T]) //│ } //│ module MyNone extends MyOption { //│ fun filter: anything -> MyNone @@ -81,7 +81,7 @@ module None extends Option[nothing] { //│ fun map: forall 'b0. (T -> 'b0) -> Option['b0] //│ } //│ class Some[T](value: T) extends Option { -//│ fun filter: (T -> Object) -> Option[T] +//│ fun filter: (T -> Bool) -> Option[T] //│ fun flatMap: forall 'c. (T -> 'c) -> 'c //│ fun get: T //│ fun isDefined: true diff --git a/shared/src/test/diff/ucs/AppSplits.mls b/shared/src/test/diff/ucs/AppSplits.mls index f6f81f17..99caea25 100644 --- a/shared/src/test/diff/ucs/AppSplits.mls +++ b/shared/src/test/diff/ucs/AppSplits.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper fun foo(x) = x > 1 @@ -20,14 +20,14 @@ if foo of //│ ╟── Note: 'if' expression starts here: //│ ║ l.9: if foo of //│ ╙── ^^ -//│ ╔══[ERROR] The case when this is false is not handled: foo(0,) -//│ ║ l.9: if foo of -//│ ║ ^^^^^^ +//│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.10: 0 then "a" -//│ ╙── ^^^^ -//│ error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ║ ^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ () +//│ res +//│ Runtime error: +//│ Error: non-exhaustive case expression :pe // TODO :e @@ -47,14 +47,24 @@ if foo of 1, //│ ╟── Note: 'if' expression starts here: //│ ║ l.34: if foo of 1, //│ ╙── ^^ -//│ ╔══[ERROR] The case when this is false is not handled: foo(1, undefined,) +//│ ╔══[ERROR] Type mismatch in application: //│ ║ l.34: if foo of 1, //│ ║ ^^^^^^^^^ //│ ║ l.35: 0 then "a" +//│ ║ ^^ +//│ ╟── argument list of type `[1, ()]` does not match type `[?a]` +//│ ║ l.34: if foo of 1, +//│ ║ ^^ +//│ ║ l.35: 0 then "a" //│ ╙── ^^ -//│ error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.35: 0 then "a" +//│ ║ ^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ () +//│ res +//│ Runtime error: +//│ Error: non-exhaustive case expression :pe // TODO :e @@ -62,15 +72,15 @@ if foo (0) then "a" (1) then "b" //│ ╔══[PARSE ERROR] Unexpected parenthesis section here -//│ ║ l.63: (1) then "b" +//│ ║ l.73: (1) then "b" //│ ╙── ^^^ -//│ ╔══[ERROR] The case when this is false is not handled: foo(0,) -//│ ║ l.61: if foo -//│ ║ ^^^ -//│ ║ l.62: (0) then "a" -//│ ╙── ^^^^^ -//│ error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.72: (0) then "a" +//│ ║ ^^^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ "a" +//│ res +//│ Runtime error: +//│ Error: non-exhaustive case expression diff --git a/shared/src/test/diff/ucs/CrossBranchCapture.mls b/shared/src/test/diff/ucs/CrossBranchCapture.mls index 364aa0c6..f1c60286 100644 --- a/shared/src/test/diff/ucs/CrossBranchCapture.mls +++ b/shared/src/test/diff/ucs/CrossBranchCapture.mls @@ -1,21 +1,32 @@ -:NewDefs +:PreTyper class Numb(n: Int) //│ class Numb(n: Int) -// * FIXME should be rejected +:e +:ge fun process(e) = if e is Numb(n) and n > 0 then n Numb(m) then n -//│ fun process: Numb -> Int - +//│ ╔══[ERROR] identifier `n` not found +//│ ║ l.13: Numb(m) then n +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: n +//│ ║ l.12: Numb(n) and n > 0 then n +//│ ╙── ^ +//│ fun process: Numb -> (Int | error) +//│ Code generation encountered an error: +//│ unresolved symbol n + +:re process(Numb(-10)) -//│ Int +//│ Int | error //│ res -//│ = -10 +//│ Runtime error: +//│ TypeError: process is not a function // * FIXME wrong result @@ -23,12 +34,12 @@ fun process(e, n) = if e is Numb(n) and n > 0 then n Numb(m) then n + m -//│ fun process: (Numb, anything) -> Int +//│ fun process: (Numb, Int) -> Int process(Numb(0), 10) //│ Int //│ res -//│ = 0 +//│ = 10 class Vec(xs: Array[Numb | Vec]) @@ -36,14 +47,27 @@ class Pair[A,B](a: A, b: B) //│ class Vec(xs: Array[Numb | Vec]) //│ class Pair[A, B](a: A, b: B) -// * FIXME should be rejected + +:e +:ge fun process(e) = if e is Pair(Numb(n), Numb(m)) then Numb(n + m) Pair(Vec(xs), Vec(ys)) then n Pair(Vec(n), Numb(n)) then n Pair(Numb(n), Vec(n)) then n -//│ fun process: Pair[Numb | Vec, Numb | Vec] -> (Int | Numb | Array[Numb | Vec]) +//│ ╔══[ERROR] identifier `n` not found +//│ ║ l.56: Pair(Vec(xs), Vec(ys)) then n +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: n +//│ ║ l.55: Pair(Numb(n), Numb(m)) then Numb(n + m) +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: n +//│ ║ l.56: Pair(Vec(xs), Vec(ys)) then n +//│ ╙── ^ +//│ fun process: Pair[Numb | Vec, Numb | Vec] -> (Int | Numb | error | Array[Numb | Vec]) +//│ Code generation encountered an error: +//│ unresolved symbol n // * FIXME should warn, be rejected, or compare both values for equality diff --git a/shared/src/test/diff/ucs/DirectLines.mls b/shared/src/test/diff/ucs/DirectLines.mls index ccfa61f7..5e346f8b 100644 --- a/shared/src/test/diff/ucs/DirectLines.mls +++ b/shared/src/test/diff/ucs/DirectLines.mls @@ -5,6 +5,14 @@ fun f(x, y) = x == 0 then "x" y == 0 then "y" _ then "nah" +//│ ╔══[WARNING] old desugarer used +//│ ║ l.5: x == 0 then "x" +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.6: y == 0 then "y" +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ║ l.7: _ then "nah" +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ f: (number, number,) -> ("nah" | "x" | "y") //│ = [Function: f] @@ -22,6 +30,10 @@ class None: Option //│ = [Function: None1] fun isValid(x) = if x then false else true +//│ ╔══[WARNING] old desugarer used +//│ ║ l.32: fun isValid(x) = if x then false else true +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ isValid: anything -> Bool //│ = [Function: isValid] @@ -30,6 +42,16 @@ fun f(x, allowNone) = is Some(x) and isValid(x) then "good" is None() and allowNone then "okay" is _ then "bad" +//│ ╔══[WARNING] old desugarer used +//│ ║ l.41: if x +//│ ║ ^ +//│ ║ l.42: is Some(x) and isValid(x) then "good" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.43: is None() and allowNone then "okay" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.44: is _ then "bad" +//│ ╙── ^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ f: (anything, anything,) -> ("bad" | "good" | "okay") //│ = [Function: f1] @@ -44,6 +66,26 @@ fun f(x, y, z) = _ then "bruh" 3 then "y = 3" _ then "bruh" +//│ ╔══[WARNING] old desugarer used +//│ ║ l.60: x == 0 then "x" +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.61: y == +//│ ║ ^^^^^^^^ +//│ ║ l.62: 1 then "y = 1" +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.63: 2 and z == +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ║ l.64: 0 then "z = 0" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.65: 9 then "z = 9" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.66: _ then "bruh" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.67: 3 then "y = 3" +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.68: _ then "bruh" +//│ ╙── ^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ f: (number, number, number,) -> ("bruh" | "x" | "y = 1" | "y = 3" | "z = 0" | "z = 9") //│ = [Function: f2] @@ -56,8 +98,21 @@ fun f(a, b) = 2 then 2 _ then 7 else 3 +//│ ╔══[WARNING] old desugarer used +//│ ║ l.95: a == 0 then 0 +//│ ║ ^^^^^^^^^^^^^ +//│ ║ l.96: b == +//│ ║ ^^^^^^^^ +//│ ║ l.97: 1 then 1 +//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.98: 2 then 2 +//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.99: _ then 7 +//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.100: else 3 +//│ ╙── ^^^^^^^^^^ //│ ╔══[WARNING] Found a redundant else branch -//│ ║ l.58: else 3 -//│ ╙── ^ +//│ ║ l.100: else 3 +//│ ╙── ^ //│ f: (number, number,) -> (0 | 1 | 2 | 7) //│ = [Function: f3] diff --git a/shared/src/test/diff/ucs/ElseIf.mls b/shared/src/test/diff/ucs/ElseIf.mls index bcb30cd3..7e9ca42c 100644 --- a/shared/src/test/diff/ucs/ElseIf.mls +++ b/shared/src/test/diff/ucs/ElseIf.mls @@ -1,5 +1,4 @@ -:NewParser -:NewDefs +:PreTyper @@ -26,23 +25,28 @@ module Fals //│ module Fals :e -:ge fun f(x, y) = if x is Tru and y is Tru then true Fals and y is Fals then false -//│ ╔══[ERROR] The match is not exhaustive. -//│ ║ l.31: Tru and y is Tru then true -//│ ║ ^^^^^^^^ -//│ ╟── The scrutinee at this position misses 1 case. -//│ ║ l.31: Tru and y is Tru then true +//│ ╔══[ERROR] When scrutinee `x` is `Tru` +//│ ║ l.29: Tru and y is Tru then true +//│ ║ ^^^ +//│ ╟── Scrutinee `y` has 1 missing case +//│ ║ l.29: Tru and y is Tru then true //│ ║ ^ -//│ ╟── [Missing Case 1/1] `Fals` -//│ ╟── It first appears here. -//│ ║ l.32: Fals and y is Fals then false +//│ ╟── It can be module `Fals` +//│ ║ l.30: Fals and y is Fals then false //│ ╙── ^^^^ -//│ fun f: (anything, anything) -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╔══[ERROR] When scrutinee `x` is `Fals` +//│ ║ l.30: Fals and y is Fals then false +//│ ║ ^^^^ +//│ ╟── Scrutinee `y` has 1 missing case +//│ ║ l.30: Fals and y is Fals then false +//│ ║ ^ +//│ ╟── It can be module `Tru` +//│ ║ l.29: Tru and y is Tru then true +//│ ╙── ^^^ +//│ fun f: (Fals | Tru, nothing) -> Bool // The base case. fun f(x, y) = if x is @@ -82,6 +86,7 @@ fun g(x, y) = if x is _ and y is true then true false then false +//│ ╙── //│ fun g: (Object, Bool) -> Bool // Chained UCS terms @@ -93,12 +98,55 @@ fun f(x, y) = if x is Fals then false //│ fun f: (Object, Fals | Tru) -> Bool +:e fun f(x, y) = if x is Tru and y is Tru then true Fals and y is Fals then false else if y is Tru and x is Fals then true Fals and x is Tru then false +//│ ╔══[ERROR] When scrutinee `y` is `Tru` +//│ ║ l.106: Tru and x is Fals then true +//│ ║ ^^^ +//│ ╟── Scrutinee `x` has 1 missing case +//│ ║ l.106: Tru and x is Fals then true +//│ ║ ^ +//│ ╟── It can be module `Tru` +//│ ║ l.107: Fals and x is Tru then false +//│ ╙── ^^^ +//│ ╔══[ERROR] When scrutinee `y` is `Fals` +//│ ║ l.107: Fals and x is Tru then false +//│ ║ ^^^^ +//│ ╟── Scrutinee `x` has 1 missing case +//│ ║ l.107: Fals and x is Tru then false +//│ ║ ^ +//│ ╟── It can be module `Fals` +//│ ║ l.106: Tru and x is Fals then true +//│ ╙── ^^^^ +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.106: Tru and x is Fals then true +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ╟── class pattern of type `Tru` is not an instance of type `Fals` +//│ ║ l.103: Tru and y is Tru then true +//│ ║ ^^^ +//│ ╟── but it flows into reference with expected type `Fals` +//│ ║ l.106: Tru and x is Fals then true +//│ ║ ^ +//│ ╟── Note: constraint arises from class pattern: +//│ ║ l.106: Tru and x is Fals then true +//│ ╙── ^^^^ +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.107: Fals and x is Tru then false +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ╟── class pattern of type `Fals` is not an instance of type `Tru` +//│ ║ l.104: Fals and y is Fals then false +//│ ║ ^^^^ +//│ ╟── but it flows into reference with expected type `Tru` +//│ ║ l.107: Fals and x is Tru then false +//│ ║ ^ +//│ ╟── Note: constraint arises from class pattern: +//│ ║ l.107: Fals and x is Tru then false +//│ ╙── ^^^ //│ fun f: (Fals | Tru, Fals | Tru) -> Bool fun h(x, y, p) = if @@ -106,4 +154,4 @@ fun h(x, y, p) = if y is Tru then 1 Fals then 2 -//│ fun h: (Object, Fals | Tru, true -> Object) -> (0 | 1 | 2) +//│ fun h: forall 'a. ('a & Bool, Fals | Tru, 'a -> Bool) -> (0 | 1 | 2) diff --git a/shared/src/test/diff/ucs/ErrorMessage.mls b/shared/src/test/diff/ucs/ErrorMessage.mls index aa40b493..70bbd0eb 100644 --- a/shared/src/test/diff/ucs/ErrorMessage.mls +++ b/shared/src/test/diff/ucs/ErrorMessage.mls @@ -10,6 +10,12 @@ class Point(x, y) fun f(p) = if p is Point(x, y, z) then x + y + z +//│ ╔══[WARNING] old desugarer used +//│ ║ l.11: if p is +//│ ║ ^^^^ +//│ ║ l.12: Point(x, y, z) then x + y + z +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ ╔══[ERROR] class Point expects 2 parameters but found 3 parameters //│ ║ l.12: Point(x, y, z) then x + y + z //│ ╙── ^^^^^^^^^^^^^^ @@ -22,8 +28,14 @@ fun f(p) = fun g(xs) = if xs is head :: _ then head +//│ ╔══[WARNING] old desugarer used +//│ ║ l.29: if xs is +//│ ║ ^^^^^ +//│ ║ l.30: head :: _ then head +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ ╔══[ERROR] Cannot find operator `::` in the context -//│ ║ l.24: head :: _ then head +//│ ║ l.30: head :: _ then head //│ ╙── ^^ //│ g: anything -> error //│ Code generation encountered an error: diff --git a/shared/src/test/diff/ucs/Exhaustiveness.mls b/shared/src/test/diff/ucs/Exhaustiveness.mls index eb2377c6..88bf03fd 100644 --- a/shared/src/test/diff/ucs/Exhaustiveness.mls +++ b/shared/src/test/diff/ucs/Exhaustiveness.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper :NoJS class A() @@ -19,21 +19,19 @@ fun f(x, y) = y is B and x is A then 4 -//│ ╔══[ERROR] The match is not exhaustive. -//│ ║ l.20: x is -//│ ║ ^^^^ -//│ ╟── The scrutinee at this position misses 2 cases. +//│ ╔══[ERROR] When scrutinee `y` is `B` +//│ ║ l.19: y is B and +//│ ║ ^ +//│ ╟── Scrutinee `x` has 2 missing cases //│ ║ l.20: x is //│ ║ ^ -//│ ╟── [Missing Case 1/2] `B` -//│ ╟── It first appears here. +//│ ╟── It can be class `B` //│ ║ l.17: B then 1 //│ ║ ^ -//│ ╟── [Missing Case 2/2] `C` -//│ ╟── It first appears here. +//│ ╟── It can be class `C` //│ ║ l.18: C then 2 //│ ╙── ^ -//│ fun f: (anything, anything) -> error +//│ fun f: (A, A | B) -> (0 | 1 | 2 | 4) :e // These operators are uninterpreted. So, it's impossible to reason the @@ -48,19 +46,59 @@ class Node[A](value: int, left: Tree[A], right: Tree[A]) { >= value then right.find(wanted) == value then true } -//│ ╔══[ERROR] The case when this is false is not handled: ==(wanted, value,) -//│ ║ l.46: fun contains(wanted) = if wanted +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.44: fun contains(wanted) = if wanted +//│ ║ ^^^^^^ +//│ ║ l.45: <= value then left.find(wanted) +//│ ║ ^^^^^^^^^^^^ +//│ ╟── type `int` is not an instance of type `Num` +//│ ║ l.43: class Node[A](value: int, left: Tree[A], right: Tree[A]) { +//│ ║ ^^^ +//│ ╟── but it flows into reference with expected type `Num` +//│ ║ l.45: <= value then left.find(wanted) +//│ ╙── ^^^^^ +//│ ╔══[ERROR] Type `#Node & {Node#A = A}` does not contain member `find` +//│ ║ l.45: <= value then left.find(wanted) +//│ ╙── ^^^^^ +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.44: fun contains(wanted) = if wanted +//│ ║ ^^^^^^ +//│ ║ l.45: <= value then left.find(wanted) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.46: >= value then right.find(wanted) +//│ ║ ^^^^^^^^^^^^ +//│ ╟── type `int` is not an instance of type `Num` +//│ ║ l.43: class Node[A](value: int, left: Tree[A], right: Tree[A]) { +//│ ║ ^^^ +//│ ╟── but it flows into reference with expected type `Num` +//│ ║ l.46: >= value then right.find(wanted) +//│ ╙── ^^^^^ +//│ ╔══[ERROR] Type `#Node & {Node#A = A}` does not contain member `find` +//│ ║ l.46: >= value then right.find(wanted) +//│ ╙── ^^^^^ +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.44: fun contains(wanted) = if wanted //│ ║ ^^^^^^ -//│ ║ l.47: <= value then left.find(wanted) +//│ ║ l.45: <= value then left.find(wanted) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.48: >= value then right.find(wanted) +//│ ║ l.46: >= value then right.find(wanted) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.49: == value then true -//│ ╙── ^^^^^^^^^^^^ +//│ ║ l.47: == value then true +//│ ║ ^^^^^^^^^^^^ +//│ ╟── type `int` is not an instance of type `Num` +//│ ║ l.43: class Node[A](value: int, left: Tree[A], right: Tree[A]) { +//│ ║ ^^^ +//│ ╟── but it flows into reference with expected type `Num` +//│ ║ l.47: == value then true +//│ ╙── ^^^^^ +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.47: == value then true +//│ ║ ^^^^ +//│ ╙── expression of type `Bool` is not an instance of type `true` //│ type Tree[A] = Empty | Node[A] //│ module Empty { //│ fun contains: anything -> false //│ } //│ class Node[A](value: int, left: Tree[A], right: Tree[A]) { -//│ fun contains: anything -> error +//│ fun contains: Num -> (error | true) //│ } diff --git a/shared/src/test/diff/ucs/Humiliation.mls b/shared/src/test/diff/ucs/Humiliation.mls index 878fb053..2463ac99 100644 --- a/shared/src/test/diff/ucs/Humiliation.mls +++ b/shared/src/test/diff/ucs/Humiliation.mls @@ -7,10 +7,18 @@ class Foo(x) //│ = [Function: Foo1] if 1 is 1 then 1 else 0 +//│ ╔══[WARNING] old desugarer used +//│ ║ l.9: if 1 is 1 then 1 else 0 +//│ ╙── ^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ res: 0 | 1 //│ = 1 fun test(x) = if x is 1 then 0 else 1 +//│ ╔══[WARNING] old desugarer used +//│ ║ l.17: fun test(x) = if x is 1 then 0 else 1 +//│ ╙── ^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ test: anything -> (0 | 1) //│ = [Function: test] @@ -19,12 +27,19 @@ fun test(x) = if x is 1 then 0 else 1 fun testF(x) = if x is Foo(a) then a Foo(a) then a +//│ ╔══[WARNING] old desugarer used +//│ ║ l.27: fun testF(x) = if x is +//│ ║ ^^^^ +//│ ║ l.28: Foo(a) then a +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.29: Foo(a) then a +//│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[WARNING] Found a duplicated branch //│ ╟── This branch -//│ ║ l.21: Foo(a) then a +//│ ║ l.29: Foo(a) then a //│ ║ ^ //│ ╟── is subsumed by the branch here. -//│ ║ l.20: Foo(a) then a +//│ ║ l.28: Foo(a) then a //│ ╙── ^ //│ testF: (Foo & {x: 'x}) -> 'x //│ = [Function: testF] @@ -37,6 +52,14 @@ class Bar(y, z) fun test(f) = if f is Foo(a) then a Bar(b, c) then b + c +//│ ╔══[WARNING] old desugarer used +//│ ║ l.52: fun test(f) = if f is +//│ ║ ^^^^ +//│ ║ l.53: Foo(a) then a +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.54: Bar(b, c) then b + c +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ test: (Bar & {y: int, z: int} | Foo & {x: 'x}) -> (int | 'x) //│ = [Function: test1] @@ -52,6 +75,18 @@ fun f(x) = Pair(1, 1) then "ones" Pair(y, 1) then x _ then "nah" +//│ ╔══[WARNING] old desugarer used +//│ ║ l.73: if x is +//│ ║ ^^^^ +//│ ║ l.74: Pair(0, 0) then "zeros" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.75: Pair(1, 1) then "ones" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.76: Pair(y, 1) then x +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.77: _ then "nah" +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ f: (Pair & 'a | ~Pair) -> ("nah" | "ones" | "zeros" | 'a) //│ = [Function: f] @@ -70,16 +105,24 @@ class O() fun foo(x) = if x is Pair(Z(), Z()) then "zeros" Pair(O(), O()) then "ones" +//│ ╔══[WARNING] old desugarer used +//│ ║ l.105: fun foo(x) = if x is +//│ ║ ^^^^ +//│ ║ l.106: Pair(Z(), Z()) then "zeros" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.107: Pair(O(), O()) then "ones" +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ ╔══[ERROR] The match is not exhaustive. -//│ ║ l.70: fun foo(x) = if x is -//│ ║ ^^^^ +//│ ║ l.105: fun foo(x) = if x is +//│ ║ ^^^^ //│ ╟── The scrutinee at this position misses 1 case. -//│ ║ l.71: Pair(Z(), Z()) then "zeros" -//│ ║ ^^^ +//│ ║ l.106: Pair(Z(), Z()) then "zeros" +//│ ║ ^^^ //│ ╟── [Missing Case 1/1] `O` //│ ╟── It first appears here. -//│ ║ l.72: Pair(O(), O()) then "ones" -//│ ╙── ^^^ +//│ ║ l.107: Pair(O(), O()) then "ones" +//│ ╙── ^^^ //│ foo: anything -> error //│ Code generation encountered an error: //│ if expression was not desugared @@ -90,16 +133,24 @@ fun foo(x) = if x is fun foo(x) = if x is [Z(), Z()] then "zeros" [O(), O()] then "ones" +//│ ╔══[WARNING] old desugarer used +//│ ║ l.133: fun foo(x) = if x is +//│ ║ ^^^^ +//│ ║ l.134: [Z(), Z()] then "zeros" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.135: [O(), O()] then "ones" +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ ╔══[ERROR] The match is not exhaustive. -//│ ║ l.90: fun foo(x) = if x is -//│ ║ ^^^^ +//│ ║ l.133: fun foo(x) = if x is +//│ ║ ^^^^ //│ ╟── The scrutinee at this position misses 1 case. -//│ ║ l.91: [Z(), Z()] then "zeros" -//│ ║ ^^^ +//│ ║ l.134: [Z(), Z()] then "zeros" +//│ ║ ^^^ //│ ╟── [Missing Case 1/1] `O` //│ ╟── It first appears here. -//│ ║ l.92: [O(), O()] then "ones" -//│ ╙── ^^^ +//│ ║ l.135: [O(), O()] then "ones" +//│ ╙── ^^^ //│ foo: anything -> error //│ Code generation encountered an error: //│ if expression was not desugared @@ -110,6 +161,44 @@ fun foo(x) = if x is Z() then "zeros" O() then if b is O() then "ones" +//│ ╔══[WARNING] old desugarer used +//│ ║ l.158: fun foo(x) = if x is +//│ ║ ^^^^ +//│ ║ l.159: Pair(a, b) then if a is +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.160: Z() then if b is +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.161: Z() then "zeros" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.162: O() then if b is +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.163: O() then "ones" +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning +//│ ╔══[WARNING] old desugarer used +//│ ║ l.159: Pair(a, b) then if a is +//│ ║ ^^^^ +//│ ║ l.160: Z() then if b is +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.161: Z() then "zeros" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.162: O() then if b is +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.163: O() then "ones" +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning +//│ ╔══[WARNING] old desugarer used +//│ ║ l.160: Z() then if b is +//│ ║ ^^^^ +//│ ║ l.161: Z() then "zeros" +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning +//│ ╔══[WARNING] old desugarer used +//│ ║ l.162: O() then if b is +//│ ║ ^^^^ +//│ ║ l.163: O() then "ones" +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ foo: (Pair & {fst: O | Z, snd: nothing}) -> ("ones" | "zeros") //│ = [Function: foo2] @@ -120,6 +209,50 @@ fun foo(x) = if x is else "???" O() then if b is O() then "ones" +//│ ╔══[WARNING] old desugarer used +//│ ║ l.205: fun foo(x) = if x is +//│ ║ ^^^^ +//│ ║ l.206: Pair(a, b) then if a is +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.207: Z() then if b is +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.208: Z() then "zeros" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.209: else "???" +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.210: O() then if b is +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.211: O() then "ones" +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning +//│ ╔══[WARNING] old desugarer used +//│ ║ l.206: Pair(a, b) then if a is +//│ ║ ^^^^ +//│ ║ l.207: Z() then if b is +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.208: Z() then "zeros" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.209: else "???" +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ║ l.210: O() then if b is +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.211: O() then "ones" +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning +//│ ╔══[WARNING] old desugarer used +//│ ║ l.207: Z() then if b is +//│ ║ ^^^^ +//│ ║ l.208: Z() then "zeros" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.209: else "???" +//│ ╙── ^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning +//│ ╔══[WARNING] old desugarer used +//│ ║ l.210: O() then if b is +//│ ║ ^^^^ +//│ ║ l.211: O() then "ones" +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ foo: (Pair & {fst: O | Z, snd: O}) -> ("???" | "ones" | "zeros") //│ = [Function: foo3] @@ -131,6 +264,56 @@ fun foo(x) = if x is O() then if b is O() then "zeros" else "???" +//│ ╔══[WARNING] old desugarer used +//│ ║ l.259: fun foo(x) = if x is +//│ ║ ^^^^ +//│ ║ l.260: Pair(a, b) then if a is +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.261: Z() then if b is +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.262: Z() then "zeros" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.263: else "???" +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.264: O() then if b is +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.265: O() then "zeros" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.266: else "???" +//│ ╙── ^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning +//│ ╔══[WARNING] old desugarer used +//│ ║ l.260: Pair(a, b) then if a is +//│ ║ ^^^^ +//│ ║ l.261: Z() then if b is +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.262: Z() then "zeros" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.263: else "???" +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ║ l.264: O() then if b is +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.265: O() then "zeros" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.266: else "???" +//│ ╙── ^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning +//│ ╔══[WARNING] old desugarer used +//│ ║ l.261: Z() then if b is +//│ ║ ^^^^ +//│ ║ l.262: Z() then "zeros" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.263: else "???" +//│ ╙── ^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning +//│ ╔══[WARNING] old desugarer used +//│ ║ l.264: O() then if b is +//│ ║ ^^^^ +//│ ║ l.265: O() then "zeros" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.266: else "???" +//│ ╙── ^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ foo: (Pair & {fst: O | Z}) -> ("???" | "zeros") //│ = [Function: foo4] @@ -148,6 +331,52 @@ fun foo(x) = if x is O() then if b is O() then "zeros" else "???" +//│ ╔══[WARNING] old desugarer used +//│ ║ l.326: fun foo(x) = if x is +//│ ║ ^^^^ +//│ ║ l.327: Pair(a, b) then if a is +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.328: Z() then if b is +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.329: S(x) then x +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.330: else "???" +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.331: O() then if b is +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.332: O() then "zeros" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.333: else "???" +//│ ╙── ^^^^^^^^^^^^^^^^^ +//│ ╔══[WARNING] old desugarer used +//│ ║ l.327: Pair(a, b) then if a is +//│ ║ ^^^^ +//│ ║ l.328: Z() then if b is +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.329: S(x) then x +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.330: else "???" +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ║ l.331: O() then if b is +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.332: O() then "zeros" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.333: else "???" +//│ ╙── ^^^^^^^^^^^^^^^^ +//│ ╔══[WARNING] old desugarer used +//│ ║ l.328: Z() then if b is +//│ ║ ^^^^ +//│ ║ l.329: S(x) then x +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.330: else "???" +//│ ╙── ^^^^^^^^^^^^^^^^^ +//│ ╔══[WARNING] old desugarer used +//│ ║ l.331: O() then if b is +//│ ║ ^^^^ +//│ ║ l.332: O() then "zeros" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.333: else "???" +//│ ╙── ^^^^^^^^^^^^^^^^^ //│ foo: (Pair & {fst: O | Z, snd: S & {pred: 'pred} | ~S}) -> ("???" | "zeros" | 'pred) //│ = [Function: foo5] @@ -161,21 +390,35 @@ fun foo(x) = if x is Pair(Z(), Z()) then "zeros" Pair(O(), O()) then "ones" Pair(y, O()) then x +//│ ╔══[WARNING] old desugarer used +//│ ║ l.389: fun foo(x) = if x is +//│ ║ ^^^^ +//│ ║ l.390: Pair(Z(), Z()) then "zeros" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.391: Pair(O(), O()) then "ones" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.392: Pair(y, O()) then x +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ ╔══[ERROR] The match is not exhaustive. -//│ ║ l.160: fun foo(x) = if x is +//│ ║ l.389: fun foo(x) = if x is //│ ║ ^^^^ //│ ╟── The scrutinee at this position misses 1 case. -//│ ║ l.161: Pair(Z(), Z()) then "zeros" +//│ ║ l.390: Pair(Z(), Z()) then "zeros" //│ ║ ^^^ //│ ╟── [Missing Case 1/1] `Z` //│ ╟── It first appears here. -//│ ║ l.161: Pair(Z(), Z()) then "zeros" +//│ ║ l.390: Pair(Z(), Z()) then "zeros" //│ ╙── ^^^ //│ foo: anything -> error //│ Code generation encountered an error: //│ if expression was not desugared fun foo(x, y) = if x is Z() and y is O() then 0 else 1 +//│ ╔══[WARNING] old desugarer used +//│ ║ l.417: fun foo(x, y) = if x is Z() and y is O() then 0 else 1 +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ foo: (anything, anything,) -> (0 | 1) //│ = [Function: foo7] @@ -183,5 +426,13 @@ fun foo(x, y) = if x is Z() and y is O() then 0 else 1 +//│ ╔══[WARNING] old desugarer used +//│ ║ l.426: if x is +//│ ║ ^^^^ +//│ ║ l.427: Z() and y is O() then 0 +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.428: else 1 +//│ ╙── ^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ foo: (anything, anything,) -> (0 | 1) //│ = [Function: foo8] diff --git a/shared/src/test/diff/ucs/Hygiene.mls b/shared/src/test/diff/ucs/Hygiene.mls index 10424de6..dd49ef46 100644 --- a/shared/src/test/diff/ucs/Hygiene.mls +++ b/shared/src/test/diff/ucs/Hygiene.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper class Some[out T](value: T) class Left[out T](value: T) @@ -7,18 +7,31 @@ class Right[out T](value: T) //│ class Left[T](value: T) //│ class Right[T](value: T) -// FIXME unhygienic, the `x` in the second branch shadows parameter `x` +:dpt:postprocess.result fun foo(x) = if x is Some(Left(y)) then x Some(x) then x -//│ fun foo: forall 'a. Some['a & (Left[anything] | Object & ~#Left)] -> 'a +//│ | | | | | Post-processed UCS term: +//│ | | | | | case x*‡ of +//│ | | | | | Some*◊ -> +//│ | | | | | let ucs$args_x$Some*† = (Some).unapply(x,) +//│ | | | | | let x$Some_0*‡ = (ucs$args_x$Some).0 +//│ | | | | | case x$Some_0*‡ of +//│ | | | | | Left*◊ -> +//│ | | | | | let ucs$args_x$Some_0$Left*† = (Left).unapply(x$Some_0,) +//│ | | | | | let y*‡ = (ucs$args_x$Some_0$Left).0 +//│ | | | | | x +//│ | | | | | _ -> +//│ | | | | | let x*‡ = (ucs$args_x$Some).0 +//│ | | | | | x +//│ fun foo: forall 'T. Some[(Left[anything] | Object & ~#Left) & 'T] -> (Some['T] | 'T) foo(Some(Left(1))) -//│ Left[1] +//│ Left[1] | Some[Left[1]] //│ res -//│ = Left {} +//│ = Some {} foo(Some(2)) -//│ 2 +//│ 2 | Some[2] //│ res //│ = 2 diff --git a/shared/src/test/diff/ucs/HygienicBindings.mls b/shared/src/test/diff/ucs/HygienicBindings.mls index 5e5d577f..bcfd1b7e 100644 --- a/shared/src/test/diff/ucs/HygienicBindings.mls +++ b/shared/src/test/diff/ucs/HygienicBindings.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper type Option[out T] = None | Some[T] module None @@ -28,14 +28,42 @@ fun h0(a) = a is None then 0 //│ fun h0: forall 'a. (None | Some[Left['a] | Right['a]]) -> (0 | 'a) -// FIXME: Precise scrutinee identification (easy) -// This seems fine. But the subtrees are not merged. +// Precise scrutinee identification (easy) +// ======================================= +// The desugarer should be able to identify pattern matching on the first +// parameter of `Some` even if they are bound to different variables. +:dpt:postprocess.result fun h1(a) = if a is Some(x) and x is Left(y) then y a is Some(y) and y is Right(z) then z a is None then 0 -//│ fun h1: forall 'a. (None | Some[Right['a]]) -> (0 | 'a) +//│ | | | | | | | Post-processed UCS term: +//│ | | | | | | | case a*‡ of +//│ | | | | | | | Some*◊ -> +//│ | | | | | | | let ucs$args_a$Some*† = (Some).unapply(a,) +//│ | | | | | | | let x*‡ = (ucs$args_a$Some).0 +//│ | | | | | | | case x*‡ of +//│ | | | | | | | Left*◊ -> +//│ | | | | | | | let ucs$args_x$Left*† = (Left).unapply(x,) +//│ | | | | | | | let y*‡ = (ucs$args_x$Left).0 +//│ | | | | | | | y +//│ | | | | | | | Right*◊ -> +//│ | | | | | | | let y*‡ = (ucs$args_a$Some).0 +//│ | | | | | | | let ucs$args_y$Right*† = (Right).unapply(y,) +//│ | | | | | | | let z*‡ = (ucs$args_y$Right).0 +//│ | | | | | | | z +//│ | | | | | | | None*† -> 0 +//│ fun h1: forall 'a. (None | Some[Right[anything] & {#rightValue: 'a}]) -> (0 | 'a) + +// FIXME +h1(Some(Left(0))) +//│ ╔══[ERROR] Type `Left[?A]` does not contain member `rightValue` +//│ ║ l.12: class Right[B](val rightValue: B) +//│ ╙── ^^^^^^^^^^ +//│ 0 | error +//│ res +//│ = 0 // This is the desugared version of the test case above. fun h1'(a) = @@ -55,54 +83,36 @@ fun h1'(a) = None then 0 //│ fun h1': forall 'leftValue. (None | Some[Right['leftValue]]) -> (0 | 'leftValue) -// FIXME This seems fine but the desugared term does not merge the cases. -// See the example below. -fun h1''(a) = - if - a is Some(x) and x is Left(y) then y - a is Some(x) and x is Right(z) then z - a is None then 0 -//│ fun h1'': forall 'a. (None | Some[Left['a] | Right['a]]) -> (0 | 'a) - // FIXME -h1(Some(Left(0))) h1'(Some(Left(0))) -h1''(Some(Left(0))) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.68: h1(Some(Left(0))) -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ╟── application of type `Left[?A]` is not an instance of type `Right` -//│ ║ l.68: h1(Some(Left(0))) -//│ ║ ^^^^^^^ -//│ ╟── Note: constraint arises from class pattern: -//│ ║ l.36: a is Some(y) and y is Right(z) then z -//│ ║ ^^^^^ -//│ ╟── from field selection: -//│ ║ l.5: class Some[out T](val value: T) -//│ ║ ^^^^^ -//│ ╟── Note: type parameter T is defined at: -//│ ║ l.5: class Some[out T](val value: T) -//│ ╙── ^ -//│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.69: h1'(Some(Left(0))) +//│ ║ l.87: h1'(Some(Left(0))) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Left[?A]` is not an instance of type `Right` -//│ ║ l.69: h1'(Some(Left(0))) +//│ ║ l.87: h1'(Some(Left(0))) //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from class pattern: -//│ ║ l.52: Right then +//│ ║ l.80: Right then //│ ║ ^^^^^ //│ ╟── from field selection: -//│ ║ l.45: let y = a.value +//│ ║ l.73: let y = a.value //│ ║ ^^^^^^^ //│ ╟── Note: type parameter T is defined at: //│ ║ l.5: class Some[out T](val value: T) //│ ╙── ^ -//│ 0 -//│ res -//│ = 0 +//│ 0 | error //│ res //│ = 0 + +fun h1''(a) = + if + a is Some(x) and x is Left(y) then y + a is Some(x) and x is Right(z) then z + a is None then 0 +//│ fun h1'': forall 'a. (None | Some[Left['a] | Right['a]]) -> (0 | 'a) + +h1''(Some(Left(0))) +//│ 0 //│ res //│ = 0 @@ -114,15 +124,7 @@ fun h2(a) = let y' = y y' is Right(z) then z a is None then 0 -//│ ╔══[ERROR] identifier not found: y -//│ ║ l.114: let y' = y -//│ ╙── ^ -//│ ╔══[ERROR] identifier not found: y -//│ ║ l.114: let y' = y -//│ ╙── ^ -//│ fun h2: forall 'a. (None | Some[Left['a] | Object & ~#Left]) -> (0 | error | 'a) -//│ Code generation encountered an error: -//│ unresolved symbol y +//│ fun h2: forall 'a. (None | Some[Left['a]]) -> (0 | 'a) // FIXME: Some results are wrong. fun h3(x, y, f, p) = @@ -133,32 +135,44 @@ fun h3(x, y, f, p) = h3("anything", "not me", _ => "should be me", _ => true) h3(None, "should be me", _ => "not me", _ => false) h3("anything", "anything", _ => "not me", _ => false) -//│ fun h3: forall 'a 'b. (None | Object & 'a & ~#None, 'b, (None | 'a) -> anything, (None | 'a) -> Object) -> ("anyway" | 'b) -//│ "anything" | "anyway" -//│ res -//│ = 'not me' +//│ fun h3: forall 'a 'b. (Object & 'a, anything, 'a -> 'b, 'a -> Bool) -> ("anyway" | 'b) +//│ "anyway" | "not me" //│ res //│ = 'should be me' //│ res +//│ = 'not me' +//│ res //│ = 'anyway' -// FIXME: Some results are wrong. + +// FIXME: We still haven't fixed all shadowing problems. +:dpt:postprocess.result fun h4(x, y, p) = if x is y and p(x) then y None then y _ then "default" -h4("should be me", "not me", _ => true) // WRONG! -h4(None, "not me", _ => true) // WRONG! +//│ | | | | | | | Post-processed UCS term: +//│ | | | | | | | let y*‡ = x +//│ | | | | | | | let ucs$test$0*† = p(x,) : Bool +//│ | | | | | | | case ucs$test$0*† of +//│ | | | | | | | true*† -> y +//│ | | | | | | | _ -> +//│ | | | | | | | case x*‡ of +//│ | | | | | | | None*† -> y +//│ | | | | | | | _ -> "default" +//│ fun h4: forall 'a. (Object & 'a, anything, 'a -> Bool) -> ("default" | 'a) + +h4("should be me", "not me", _ => true) +h4(None, "not me", _ => true) h4(None, "should be me", _ => false) h4("anything", "not me", _ => false) -//│ fun h4: forall 'a 'b. (None | Object & 'a & ~#None, 'b, (None | 'a) -> Object) -> ("default" | 'b) -//│ "default" | "not me" +//│ "anything" | "default" //│ res -//│ = 'not me' +//│ = 'should be me' //│ res -//│ = 'not me' +//│ = None { class: [class None] } //│ res -//│ = 'should be me' +//│ = None { class: [class None] } //│ res //│ = 'default' diff --git a/shared/src/test/diff/ucs/InterleavedLet.mls b/shared/src/test/diff/ucs/InterleavedLet.mls index 77b2d6ba..6b8d1ade 100644 --- a/shared/src/test/diff/ucs/InterleavedLet.mls +++ b/shared/src/test/diff/ucs/InterleavedLet.mls @@ -5,6 +5,16 @@ fun f(x) = let v = 0 v then v else 0 +//│ ╔══[WARNING] old desugarer used +//│ ║ l.4: if x == +//│ ║ ^^^^ +//│ ║ l.5: let v = 0 +//│ ║ ^^^^^^^^^^^^^ +//│ ║ l.6: v then v +//│ ║ ^^^^^^^^^^^^ +//│ ║ l.7: else 0 +//│ ╙── ^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ f: number -> 0 //│ = [Function: f] @@ -36,6 +46,10 @@ class Right(rightValue): Either fun q(x) = if x is Some and x is Some and x is Some then 0 +//│ ╔══[WARNING] old desugarer used +//│ ║ l.48: x is Some and x is Some and x is Some then 0 +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ q: Some -> 0 //│ = [Function: q] @@ -45,12 +59,19 @@ fun p(x, y) = x is Some and y is None then 0 y is Some and x is Some then 1 x is Some and y is Some then 0 +//│ ╔══[WARNING] old desugarer used +//│ ║ l.59: x is Some and y is None then 0 +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.60: y is Some and x is Some then 1 +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.61: x is Some and y is Some then 0 +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[WARNING] Found a duplicated branch //│ ╟── This branch -//│ ║ l.47: x is Some and y is Some then 0 +//│ ║ l.61: x is Some and y is Some then 0 //│ ║ ^ //│ ╟── is subsumed by the branch here. -//│ ║ l.46: y is Some and x is Some then 1 +//│ ║ l.60: y is Some and x is Some then 1 //│ ╙── ^ //│ p: (Some, None | Some,) -> (0 | 1) //│ = [Function: p] @@ -60,6 +81,16 @@ fun h(x, y) = None then y let y_square = y * y Some(z) then z + y_square +//│ ╔══[WARNING] old desugarer used +//│ ║ l.80: if x is +//│ ║ ^^^^ +//│ ║ l.81: None then y +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.82: let y_square = y * y +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.83: Some(z) then z + y_square +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ h: (None | Some & {value: int}, int,) -> int //│ = [Function: h] @@ -72,6 +103,16 @@ fun h(x, y) = None then y let y_square = y * y Some(y_square) then 0 +//│ ╔══[WARNING] old desugarer used +//│ ║ l.102: if x is +//│ ║ ^^^^ +//│ ║ l.103: None then y +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.104: let y_square = y * y +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.105: Some(y_square) then 0 +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ h: (None | Some, int & 'a,) -> (0 | 'a) //│ = [Function: h1] @@ -82,6 +123,20 @@ fun f(a, y) = let y = v + 1 Right(x) then x + y else 0 +//│ ╔══[WARNING] old desugarer used +//│ ║ l.120: if a is +//│ ║ ^^^^ +//│ ║ l.121: Some(v) and v is +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.122: Left(x) then x +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.123: let y = v + 1 +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ║ l.124: Right(x) then x + y +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.125: else 0 +//│ ╙── ^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ f: (Some & {value: int} | ~Some, anything,) -> int //│ = [Function: f1] @@ -92,10 +147,20 @@ fun q(a) = let y = a + 1 then y //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead -//│ ║ l.92: let y = a + 1 -//│ ║ ^^^^^ -//│ ║ l.93: then y -//│ ╙── ^^^^^^^^^^ +//│ ║ l.147: let y = a + 1 +//│ ║ ^^^^^ +//│ ║ l.148: then y +//│ ╙── ^^^^^^^^^^ +//│ ╔══[WARNING] old desugarer used +//│ ║ l.145: if a is +//│ ║ ^^^^ +//│ ║ l.146: Left(x) then x +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.147: let y = a + 1 +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.148: then y +//│ ╙── ^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ q: (Left & {leftValue: 'leftValue}) -> 'leftValue //│ = [Function: q1] @@ -114,6 +179,16 @@ fun w() = let y = 0 B then "B" else "?" +//│ ╔══[WARNING] old desugarer used +//│ ║ l.178: A then "A" +//│ ║ ^^^^^^^^^^ +//│ ║ l.179: let y = 0 +//│ ║ ^^^^^^^^^^^^^ +//│ ║ l.180: B then "B" +//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.181: else "?" +//│ ╙── ^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ w: () -> ("?" | "A" | "B") //│ = [Function: w] @@ -126,6 +201,16 @@ fun i(x) = A() then "A" let y = 0 B() then "B" +//│ ╔══[WARNING] old desugarer used +//│ ║ l.200: if x is +//│ ║ ^^^^ +//│ ║ l.201: A() then "A" +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ║ l.202: let y = 0 +//│ ║ ^^^^^^^^^^^^^ +//│ ║ l.203: B() then "B" +//│ ╙── ^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ i: (A | B) -> ("A" | "B") //│ = [Function: i] @@ -138,6 +223,16 @@ fun qq(x, z) = let y = inc(z) y * y then 0 else 0 +//│ ╔══[WARNING] old desugarer used +//│ ║ l.222: if x == +//│ ║ ^^^^ +//│ ║ l.223: let y = inc(z) +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.224: y * y then 0 +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ║ l.225: else 0 +//│ ╙── ^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ qq: (number, int,) -> 0 //│ = [Function: qq] @@ -146,6 +241,14 @@ fun bruh(x) = x == 0 then 0 let y = 1 else y +//│ ╔══[WARNING] old desugarer used +//│ ║ l.241: x == 0 then 0 +//│ ║ ^^^^^^^^^^^^^ +//│ ║ l.242: let y = 1 +//│ ║ ^^^^^^^^^^^^^ +//│ ║ l.243: else y +//│ ╙── ^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ bruh: number -> (0 | 1) //│ = [Function: bruh] @@ -164,6 +267,20 @@ fun ff(x) = z == 1 then 1 z == 2 then 2 else 0 +//│ ╔══[WARNING] old desugarer used +//│ ║ l.264: x == 0 then 0 +//│ ║ ^^^^^^^^^^^^^ +//│ ║ l.265: let y = f1(x) +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.266: let z = f2(x, y) +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.267: z == 1 then 1 +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.268: z == 2 then 2 +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.269: else 0 +//│ ╙── ^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ ff: int -> (0 | 1 | 2) //│ = [Function: ff] @@ -172,6 +289,16 @@ fun ip(y) = let z = inc(y) y == z * z then "bruh" else "rocks" +//│ ╔══[WARNING] old desugarer used +//│ ║ l.288: if q(y) and +//│ ║ ^^^^^^^^ +//│ ║ l.289: let z = inc(y) +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.290: y == z * z then "bruh" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.291: else "rocks" +//│ ╙── ^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ ip: nothing -> ("bruh" | "rocks") //│ = [Function: ip] @@ -180,6 +307,16 @@ fun tr(x) = Some(v) then v let tmp = 1 None then tmp +//│ ╔══[WARNING] old desugarer used +//│ ║ l.306: if x is +//│ ║ ^^^^ +//│ ║ l.307: Some(v) then v +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.308: let tmp = 1 +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.309: None then tmp +//│ ╙── ^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ tr: (None | Some & {value: 'value}) -> (1 | 'value) //│ = [Function: tr] @@ -227,6 +364,16 @@ fun showList(xs) = //│ })()); //│ }; //│ // End of generated code +//│ ╔══[WARNING] old desugarer used +//│ ║ l.349: if xs is +//│ ║ ^^^^^ +//│ ║ l.350: Nil then "" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.351: Cons(head, Nil()) then toString(head) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.352: Cons(head, tail) then cat3(toString(head), ", ", showList(tail)) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ showList: (Cons & 'a | Nil) -> string //│ where //│ 'a <: {head: anything, tail: Cons & 'a | Nil} @@ -255,6 +402,24 @@ fun mapPartition(f, xs) = let r = res.snd Left(v) then Pair(Cons(v, l), r) Right(v) then Pair(l, Cons(v, r)) +//│ ╔══[WARNING] old desugarer used +//│ ║ l.397: if xs is +//│ ║ ^^^^^ +//│ ║ l.398: Nil then Pair(Nil(), Nil()) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.399: Cons(x, xs) and f(x) is +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.400: let res = mapPartition(f, xs) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.401: let l = res.fst +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.402: let r = res.snd +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.403: Left(v) then Pair(Cons(v, l), r) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.404: Right(v) then Pair(l, Cons(v, r)) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ mapPartition: ('head -> (Left & {leftValue: 'leftValue} | Right & {rightValue: 'rightValue}), Cons & 'a | Nil,) -> (Pair & {fst: forall 'b 'c. Nil | 'c | 'b, snd: Nil | Cons & {head: 'rightValue, tail: Nil}}) //│ where //│ 'b :> Cons & {head: 'leftValue, tail: forall 'd. Nil | 'd} @@ -263,6 +428,10 @@ fun mapPartition(f, xs) = //│ = [Function: mapPartition] mapPartition(x => (if x % 2 == 0 then Left(x) else Right(x)), zeroToThree) +//│ ╔══[WARNING] old desugarer used +//│ ║ l.430: mapPartition(x => (if x % 2 == 0 then Left(x) else Right(x)), zeroToThree) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ res: Pair & { //│ fst: forall 'a 'b. Nil | 'b | 'a, //│ snd: Nil | Cons & {head: 0 | 1 | 2 | 3, tail: Nil} @@ -282,6 +451,18 @@ fun mapPartition2(f, xs) = Cons(x, xs) and mapPartition(f, xs) is res and res.fst is l and res.snd is r and f(x) is Left(v) then Pair(Cons(v, l), r) Right(v) then Pair(l, Cons(v, r)) +//│ ╔══[WARNING] old desugarer used +//│ ║ l.449: if xs is +//│ ║ ^^^^^ +//│ ║ l.450: Nil then Pair(Nil(), Nil()) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.451: Cons(x, xs) and mapPartition(f, xs) is res and res.fst is l and res.snd is r and f(x) is +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.452: Left(v) then Pair(Cons(v, l), r) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.453: Right(v) then Pair(l, Cons(v, r)) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ mapPartition2: ('head -> (Left & {leftValue: 'leftValue} | Right & {rightValue: 'rightValue}) & 'head0 -> (Left & {leftValue: 'leftValue0} | Right & {rightValue: 'rightValue0}), Cons & {head: 'head0, tail: Cons & 'a | Nil} | Nil,) -> (Pair & { //│ fst: forall 'b. Cons & { //│ head: 'leftValue0, @@ -300,6 +481,10 @@ fun mapPartition2(f, xs) = //│ = [Function: mapPartition2] mapPartition2(x => (if x % 2 == 0 then Left(x) else Right(x)), zeroToThree) +//│ ╔══[WARNING] old desugarer used +//│ ║ l.483: mapPartition2(x => (if x % 2 == 0 then Left(x) else Right(x)), zeroToThree) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ res: Pair & { //│ fst: forall 'a. Cons & { //│ head: 0, @@ -333,6 +518,26 @@ fun mn(a) = 2 then "b is 3" Right(b) then "right-defined" None then "undefined" +//│ ╔══[WARNING] old desugarer used +//│ ║ l.512: if a is +//│ ║ ^^^^ +//│ ║ l.513: Some(x) and x is +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.514: Left(b) and b is +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.515: 0 then "b is 1" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.516: let _ = log(b) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.517: 1 then "b is 2" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.518: 2 then "b is 3" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.519: Right(b) then "right-defined" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.520: None then "undefined" +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ mn: (None | Some & {value: Left & {leftValue: 0 | 1 | 2} | Right}) -> ("b is 1" | "b is 2" | "b is 3" | "right-defined" | "undefined") //│ = [Function: mn] diff --git a/shared/src/test/diff/ucs/JSON.mls b/shared/src/test/diff/ucs/JSON.mls deleted file mode 100644 index 889ff5a2..00000000 --- a/shared/src/test/diff/ucs/JSON.mls +++ /dev/null @@ -1,319 +0,0 @@ -:NewParser -:NewDefs - -:escape -// We need to use some native methods on `String`. -let String: nothing -let asNativeString: anything => { length: Int, charCodeAt: Int => Int, charAt: Int => Str, slice: Int => Str } = String -let StringInstance: { fromCharCode: Int => Str } = String -// We will validate our implementation with the built-in `JSON.parse`. -let JSON: { parse: Str => anything, stringify: anything => Str } -//│ let asNativeString: anything -> {charAt: Int -> Str, charCodeAt: Int -> Int, length: Int, slice: Int -> Str} -//│ let StringInstance: {fromCharCode: Int -> Str} -//│ let JSON: {parse: Str -> anything, stringify: anything -> Str} -//│ let String: nothing -//│ String -//│ = -//│ asNativeString -//│ = [Function: String] -//│ StringInstance -//│ = [Function: String] -//│ JSON -//│ = - -JSON.parse("{ \"xs\": [1, 2, 3], \"yes\": true, \"no\": false, \"insane\": null }") -//│ anything -//│ res -//│ = { xs: [ 1, 2, 3 ], yes: true, no: false, insane: null } - -let getStringOf = toString -fun fromCharCode(n) = StringInstance.fromCharCode(n) -fun firstCharCode(s) = asNativeString(s).charCodeAt(0) -fun getCharAtIndex(s, i) = asNativeString(s).charAt(i) -fun strlen(s) = asNativeString(s).length -fun stringHead(s) = asNativeString(s).charAt(0) -fun stringTail(s) = asNativeString(s).slice(1) -//│ let getStringOf: anything -> Str -//│ fun fromCharCode: Int -> Str -//│ fun firstCharCode: anything -> Int -//│ fun getCharAtIndex: (anything, Int) -> Str -//│ fun strlen: anything -> Int -//│ fun stringHead: anything -> Str -//│ fun stringTail: anything -> Str -//│ getStringOf -//│ = [Function: toString] - -fun isWhiteSpace(ch) = - if (firstCharCode of ch) == - 9 then true // horizontal tab - 10 then true // linefeed - 32 then true // space - _ then false -//│ fun isWhiteSpace: anything -> Bool - -fun isDigit(ch) = - let n = firstCharCode of ch - if 48 <= n and n <= 57 then true else false -//│ fun isDigit: anything -> Bool - -fun isAlphabet(ch) = - let n = firstCharCode of ch - if n <= - 90 and n >= 65 then true - 122 and n >= 97 then true - else false -//│ fun isAlphabet: anything -> Bool - -fun concat2(a, b) = concat(a)(b) -fun concat3(a, b, c) = concat2(a, concat2(b, c)) -fun concat4(a, b, c, d) = concat2(a, concat3(b, c, d)) -fun concat5(a, b, c, d, e) = concat2(a, concat4(b, c, d, e)) -fun concat6(a, b, c, d, e, f) = concat2(a, concat5(b, c, d, e, f)) -fun concat7(a, b, c, d, e, f, g) = concat2(a, concat6(b, c, d, e, f, g)) -fun concat8(a, b, c, d, e, f, g, h) = concat2(a, concat7(b, c, d, e, f, g, h)) -fun par(a) = concat3("(", a, ")") -//│ fun concat2: (Str, Str) -> Str -//│ fun concat3: (Str, Str, Str) -> Str -//│ fun concat4: (Str, Str, Str, Str) -> Str -//│ fun concat5: (Str, Str, Str, Str, Str) -> Str -//│ fun concat6: (Str, Str, Str, Str, Str, Str) -> Str -//│ fun concat7: (Str, Str, Str, Str, Str, Str, Str) -> Str -//│ fun concat8: (Str, Str, Str, Str, Str, Str, Str, Str) -> Str -//│ fun par: Str -> Str - -type Option[A] = Some[A] | None -module None -class Some[A](value: A) -//│ type Option[A] = None | Some[A] -//│ module None -//│ class Some[A](value: A) - -type List[A] = Cons[A] | Nil -module Nil -class Cons[A](head: A, tail: List[A]) -fun listConcat(xs, ys) = - if xs is - Nil then ys - Cons(x, xs') then Cons(x, listConcat(xs', ys)) -fun listJoin(xs, sep) = - if xs is - Nil then "" - Cons(x, xs') and xs' is - Nil then toString(x) - _ then concat3(toString(x), sep, listJoin(xs', sep)) -//│ type List[A] = Cons[A] | Nil -//│ module Nil -//│ class Cons[A](head: A, tail: List[A]) -//│ fun listConcat: forall 'A 'A0 'a. (Cons['A] | Nil, List['A0] & 'a) -> (Cons['A0] | 'a) -//│ fun listJoin: forall 'A1. (Cons['A1] | Nil, Str) -> Str -//│ where -//│ 'A <: 'A0 - -type TreeMap[out A] = Node[A] | Empty -module Empty -class Node[out A](key: Str, value: A, left: TreeMap[A], right: TreeMap[A]) -fun insert(t, k, v) = - if t is - Node(k', _, l, r) and - slt(k, k') then Node(k', v, insert(l, k, v), r) - sgt(k, k') then Node(k', v, l, insert(r, k, v)) - _ then Node(k, v, l, r) - Empty then Node(k, v, Empty, Empty) -fun find(t, k) = - if t is - Node(k', v, l, r) and - slt(k, k') then find(l, k) - sgt(k, k') then find(r, k) - _ then Some(v) - Empty then None -fun traverse(t, f) = - if t is - Empty then Nil - Node(key, value, left, right) then - listConcat(traverse(left, f), Cons(f(key, value), traverse(right, f))) -//│ type TreeMap[A] = Empty | Node[A] -//│ module Empty -//│ class Node[A](key: Str, value: A, left: TreeMap[A], right: TreeMap[A]) -//│ fun insert: forall 'A. (Empty | Node['A], Str, 'A) -> Node['A] -//│ fun find: forall 'A0. (Empty | Node['A0], Str) -> (None | Some['A0]) -//│ fun traverse: forall 'a 'A1. (Empty | Node['a], (Str, 'a) -> 'A1) -> (Cons['A1] | Nil) - -type JsonValue = JsonNull | JsonNumber | JsonString | JsonBoolean | JsonObject | JsonArray -module JsonNull { - fun toString() = "null" -} -class JsonBoolean(value: Bool) { - fun toString() = getStringOf(value) -} -class JsonNumber(value: Num) { - fun toString() = getStringOf(value) -} -class JsonString(value: Str) { - fun toString() = JSON.stringify(value) -} -class JsonObject(entries: TreeMap[JsonValue]) { - fun toString() = - if entries is Empty then "{}" - else concat3("{ ", listJoin(traverse(entries, (k, v) => concat3(k, ": ", getStringOf(v))), ", "), " }") -} -class JsonArray(elements: List[JsonValue]) { - fun toString() = concat3("[", listJoin(elements, ", "), "]") -} -//│ type JsonValue = JsonArray | JsonBoolean | JsonNull | JsonNumber | JsonObject | JsonString -//│ module JsonNull { -//│ fun toString: () -> "null" -//│ } -//│ class JsonBoolean(value: Bool) { -//│ fun toString: () -> Str -//│ } -//│ class JsonNumber(value: Num) { -//│ fun toString: () -> Str -//│ } -//│ class JsonString(value: Str) { -//│ fun toString: () -> Str -//│ } -//│ class JsonObject(entries: TreeMap[JsonValue]) { -//│ fun toString: () -> Str -//│ } -//│ class JsonArray(elements: List[JsonValue]) { -//│ fun toString: () -> Str -//│ } - -toString of JsonNull -toString of JsonBoolean(true) -toString of JsonBoolean(false) -toString of JsonNumber(42) -toString of JsonArray of Nil -toString of JsonArray of Cons(JsonNumber(0), Cons(JsonNull, Cons(JsonNumber(1), Nil))) -toString of JsonObject of Empty -toString of JsonObject of insert(Empty, "hello", JsonString("world")) -//│ Str -//│ res -//│ = 'null' -//│ res -//│ = 'true' -//│ res -//│ = 'false' -//│ res -//│ = '42' -//│ res -//│ = '[]' -//│ res -//│ = '[0, null, 1]' -//│ res -//│ = '{}' -//│ res -//│ = '{ hello: "world" }' - -class Scanner(source: Str, val at: Int) { - fun peek: Option[Str] = - if at < strlen(source) then Some(getCharAtIndex(source, at)) else None - fun advance: Scanner = - if at < strlen(source) then Scanner(source, at + 1) else this -} -fun scan(source) = Scanner(source, 0) -fun skipWhiteSpace(s: Scanner) = - if s.peek is Some(ch) and isWhiteSpace(ch) then - skipWhiteSpace(s.advance) - else - s -//│ class Scanner(source: Str, at: Int) { -//│ fun advance: Scanner -//│ fun peek: Option[Str] -//│ } -//│ fun scan: Str -> Scanner -//│ fun skipWhiteSpace: (s: Scanner) -> Scanner - -type ParseResult[T] = ParseSuccess[T] | ParseFailure -class ParseSuccess[T](value: T, scanner: Scanner) { - fun toString() = concat2("Success: ", getStringOf(value)) -} -class ParseFailure(message: Str, scanner: Scanner) { - fun toString() = concat4("Failure at ", getStringOf(scanner.at), ": ", message) -} -//│ type ParseResult[T] = ParseFailure | ParseSuccess[T] -//│ class ParseSuccess[T](value: T, scanner: Scanner) { -//│ fun toString: () -> Str -//│ } -//│ class ParseFailure(message: Str, scanner: Scanner) { -//│ fun toString: () -> Str -//│ } - -fun expect(scanner, ch) = - if skipWhiteSpace(scanner).peek is - Some(ch') and - eq(ch)(ch') then ParseSuccess((), scanner.advance) - else ParseFailure(concat4("expect '", ch, "' but found ", ch'), scanner) - None then ParseFailure(concat3("expect '", ch, "' but found EOF"), scanner) -//│ fun expect: forall 'T. (Scanner & {advance: Scanner}, Str) -> (ParseFailure | ParseSuccess['T]) -//│ where -//│ 'T :> () - -fun expectWord(scanner, word, result) = - if - strlen(word) > 0 and - let head = stringHead(word) - let tail = stringTail(word) - expect(scanner, head) is - ParseSuccess(_, scanner) then expectWord(scanner, tail, result) - ParseFailure(m, s) then ParseFailure(m, s) - scanner.peek is - Some(ch) and isAlphabet(ch) then - ParseFailure(concat3("there should not be other alphabets after\"", word, "\""), scanner) - else - ParseSuccess(result, scanner) -//│ fun expectWord: forall 'T. (Scanner & {peek: Object & ~#Some | Some[anything], advance: Scanner}, Str, 'T) -> (ParseFailure | ParseSuccess['T]) - -// If we put this function together with the next block, there will be type -// mismatch errors. -fun parseMatched(scanner, closingSymbol, parse, fn) = - if parse(scanner.advance) is - ParseSuccess(outcome, scanner) and expect(scanner, closingSymbol) is - ParseSuccess(_, scanner) then ParseSuccess(fn(outcome), scanner) - ParseFailure(message, scanner) then ParseFailure(message, scanner) - ParseFailure(message, scanner) then ParseFailure(message, scanner) -//│ fun parseMatched: forall 'advance 'a 'T. ({advance: 'advance}, Str, 'advance -> (ParseFailure | ParseSuccess['a]), 'a -> 'T) -> (ParseFailure | ParseSuccess['T]) - -:ng -fun parseEntries(scanner): ParseResult[TreeMap[JsonValue]] = error -fun parseElements(scanner): ParseResult[List[JsonValue]] = - let scanner' = skipWhiteSpace(scanner) - if scanner'.peek is - Some(ch) and - eq(ch)("]") then ParseSuccess(Nil, scanner') - parse(scanner') is - ParseSuccess(head, scanner') and scanner'.peek is - Some(ch) and eq(ch)(",") and parseElements(scanner'.advance) is - ParseSuccess(tail, scanner') then ParseSuccess(Cons(head, tail), scanner') - ParseFailure(m, s) then ParseFailure(m, s) - _ then ParseFailure("expect ']' or ',' instead of EOF", scanner') - ParseFailure(m, s) then ParseFailure(m, s) - None then ParseFailure("unexpected EOF", scanner) -fun parseStringContent(scanner): ParseResult[Str] = error -fun parseNumber(scanner): ParseResult[JsonNumber] = error -fun parse(scanner) = - let scanner' = skipWhiteSpace(scanner) - if scanner'.peek is - None then ParseFailure("expect a JSON value instead of EOF", scanner') - Some(ch) and - eq(ch)("{") then parseMatched(scanner', "}", parseEntries, JsonObject) - eq(ch)("[") then parseMatched(scanner', "]", parseElements, JsonArray) - eq(ch)("\"") then parseMatched(scanner', "\"", parseStringContent, JsonString) - eq(ch)("-") then parseNumber(scanner') - eq(ch)("t") then expectWord(scanner', "true", JsonBoolean(true)) - eq(ch)("f") then expectWord(scanner', "false", JsonBoolean(false)) - eq(ch)("n") then expectWord(scanner', "null", JsonNull) - else - ParseFailure(concat3("unrecognized character '", ch, "'"), scanner) -//│ fun parseEntries: anything -> ParseResult[TreeMap[JsonValue]] -//│ fun parseElements: Scanner -> ParseResult[List[JsonValue]] -//│ fun parseStringContent: anything -> ParseResult[Str] -//│ fun parseNumber: anything -> ParseResult[JsonNumber] -//│ fun parse: forall 'T. Scanner -> (ParseFailure | ParseSuccess[in JsonValue & 'T out JsonNull | 'T | JsonBoolean | JsonString | JsonArray | JsonObject] | ParseResult[JsonNumber]) - -:ng -toString of parse of scan of " true" -toString of parse of scan of " false" -toString of parse of scan of " null" -toString of parse of scan of "[null]" -//│ Str diff --git a/shared/src/test/diff/ucs/LeadingAnd.mls b/shared/src/test/diff/ucs/LeadingAnd.mls index 2303e4eb..ef653e58 100644 --- a/shared/src/test/diff/ucs/LeadingAnd.mls +++ b/shared/src/test/diff/ucs/LeadingAnd.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper @@ -7,41 +7,21 @@ class Some[T](value: T) -// TODO fun f(a, b) = if a is Some(av) and b is Some(bv) then av + bv -//│ ╔══[ERROR] Illegal pattern `and` -//│ ║ l.13: and b is Some(bv) then av + bv -//│ ╙── ^^^ -//│ fun f: (anything, anything) -> error -//│ Code generation encountered an error: -//│ if expression was not desugared - -:p +//│ fun f: (Some[Int], Some[Int]) -> Int + fun f(a, b) = if a is Some(av) and b is Some(bv) then av + bv -//│ |#fun| |f|(|a|,| |b|)| |#=| |#if| |a| |is| |Some|(|av|)|→|and| |b| |is| |Some|(|bv|)|↵|#then| |av| |+| |bv|←| -//│ AST: TypingUnit(List(NuFunDef(None,Var(f),None,List(),Left(Lam(Tup(List((None,Fld(_,Var(a))), (None,Fld(_,Var(b))))),If(IfOpApp(Var(a),Var(is),IfOpsApp(App(Var(Some),Tup(List((None,Fld(_,Var(av)))))),List((Var(and),IfThen(App(Var(is),Tup(List((None,Fld(_,Var(b))), (None,Fld(_,App(Var(Some),Tup(List((None,Fld(_,Var(bv))))))))))),App(Var(+),Tup(List((None,Fld(_,Var(av))), (None,Fld(_,Var(bv))))))))))),None)))))) -//│ Parsed: fun f = (a, b,) => if a is Some(av,) ‹· and (is(b, Some(bv,),)) then +(av, bv,)›; //│ fun f: (Some[Int], Some[Int]) -> Int -// TODO -:p fun f(a, b) = if a is Some(av) and b is Some(bv) then av + bv -//│ |#fun| |f|(|a|,| |b|)| |#=| |#if| |a| |is|→|Some|(|av|)|→|and| |b| |is| |Some|(|bv|)|↵|#then| |av| |+| |bv|←|←| -//│ AST: TypingUnit(List(NuFunDef(None,Var(f),None,List(),Left(Lam(Tup(List((None,Fld(_,Var(a))), (None,Fld(_,Var(b))))),If(IfOpApp(Var(a),Var(is),IfBlock(List(Left(IfOpsApp(App(Var(Some),Tup(List((None,Fld(_,Var(av)))))),List((Var(and),IfThen(App(Var(is),Tup(List((None,Fld(_,Var(b))), (None,Fld(_,App(Var(Some),Tup(List((None,Fld(_,Var(bv))))))))))),App(Var(+),Tup(List((None,Fld(_,Var(av))), (None,Fld(_,Var(bv)))))))))))))),None)))))) -//│ Parsed: fun f = (a, b,) => if a is ‹Some(av,) ‹· and (is(b, Some(bv,),)) then +(av, bv,)››; -//│ ╔══[ERROR] Illegal pattern `and` -//│ ║ l.34: and b is Some(bv) -//│ ╙── ^^^ -//│ fun f: (anything, anything) -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ fun f: (Some[Int], Some[Int]) -> Int diff --git a/shared/src/test/diff/ucs/LitUCS.mls b/shared/src/test/diff/ucs/LitUCS.mls index 6c77687f..24e12367 100644 --- a/shared/src/test/diff/ucs/LitUCS.mls +++ b/shared/src/test/diff/ucs/LitUCS.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper module A //│ module A diff --git a/shared/src/test/diff/ucs/MultiwayIf.mls b/shared/src/test/diff/ucs/MultiwayIf.mls index 6add8f28..d0c8457d 100644 --- a/shared/src/test/diff/ucs/MultiwayIf.mls +++ b/shared/src/test/diff/ucs/MultiwayIf.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper fun f(x) = diff --git a/shared/src/test/diff/ucs/NestedBranches.mls b/shared/src/test/diff/ucs/NestedBranches.mls index 7aa2059b..5add0746 100644 --- a/shared/src/test/diff/ucs/NestedBranches.mls +++ b/shared/src/test/diff/ucs/NestedBranches.mls @@ -1,5 +1,4 @@ -:NewParser -:NewDefs +:PreTyper class Some[out A](val value: A) @@ -34,22 +33,61 @@ let zeroToThree = Cons(0, Cons(1, Cons(2, Cons(3, Nil)))) fun f(x) = if x % 2 == 0 then Left(x) else Right(x) //│ fun f: forall 'A. (Int & 'A) -> (Left['A] | Right['A]) +// FIXME: Looks like a transformation bug. fun mapPartition(f, xs) = if xs is Nil then Pair(Nil, Nil) Cons(x, xs) and mapPartition(f, xs) is Pair(l, r) and f(x) is Left(v) then Pair(Cons(v, l), r) Right(v) then Pair(l, Cons(v, r)) -//│ fun mapPartition: forall 'a 'A 'A0. ('a -> (Left['A] | Right['A0]), Cons['a] | Nil) -> Pair[Cons['A] | Nil, Cons['A0] | Nil] +//│ ╔══[ERROR] identifier `l` not found +//│ ║ l.40: Left(v) then Pair(Cons(v, l), r) +//│ ╙── ^ +//│ ╔══[ERROR] identifier `r` not found +//│ ║ l.40: Left(v) then Pair(Cons(v, l), r) +//│ ╙── ^ +//│ ╔══[ERROR] identifier `l` not found +//│ ║ l.41: Right(v) then Pair(l, Cons(v, r)) +//│ ╙── ^ +//│ ╔══[ERROR] identifier `r` not found +//│ ║ l.41: Right(v) then Pair(l, Cons(v, r)) +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: l +//│ ║ l.40: Left(v) then Pair(Cons(v, l), r) +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: r +//│ ║ l.40: Left(v) then Pair(Cons(v, l), r) +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: l +//│ ║ l.41: Right(v) then Pair(l, Cons(v, r)) +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: r +//│ ║ l.41: Right(v) then Pair(l, Cons(v, r)) +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.39: Cons(x, xs) and mapPartition(f, xs) is Pair(l, r) and f(x) is +//│ ║ ^^^^^^^ +//│ ║ l.40: Left(v) then Pair(Cons(v, l), r) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.41: Right(v) then Pair(l, Cons(v, r)) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ fun mapPartition: forall 'a 'A 'A0. ('a -> (Left['A] | Right['A0]), Cons['a] | Nil) -> Pair[Cons['A] | Nil | error, Cons['A0] | Nil | error] +//│ Code generation encountered an error: +//│ unresolved symbol l +:re mapPartition(x => Left(x + 1), zeroToThree) -//│ Pair[Cons[Int] | Nil, Cons[nothing] | Nil] +//│ Pair[Cons[Int] | Nil | error, Cons[nothing] | Nil | error] //│ res -//│ = Pair {} +//│ Runtime error: +//│ ReferenceError: mapPartition is not defined +:re mapPartition(f, zeroToThree) -//│ Pair[Cons[0 | 1 | 2 | 3] | Nil, Cons[0 | 1 | 2 | 3] | Nil] +//│ Pair[Cons[0 | 1 | 2 | 3] | Nil | error, Cons[0 | 1 | 2 | 3] | Nil | error] //│ res -//│ = Pair {} +//│ Runtime error: +//│ ReferenceError: mapPartition is not defined fun mapPartition(f, xs) = if xs is @@ -90,39 +128,45 @@ fun mapPartition(f, xs) = if xs is Cons(x, xs) and mapPartition(f, xs) is [l, r] and f(x) is Left(v) then [Cons(v, l), r] Right(v) then [l, Cons(v, r)] -//│ ╔══[ERROR] type identifier not found: Tuple#2 -//│ ╙── -//│ ╔══[ERROR] Type mismatch in definition: -//│ ║ l.88: fun mapPartition(f, xs) = if xs is -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.89: Nil then [Nil, Nil] -//│ ║ ^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.90: Cons(x, xs) and mapPartition(f, xs) is [l, r] and f(x) is -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.91: Left(v) then [Cons(v, l), r] -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.92: Right(v) then [l, Cons(v, r)] -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── tuple literal of type `[Nil, Nil]` is not an instance of type `Object` -//│ ║ l.89: Nil then [Nil, Nil] -//│ ║ ^^^^^^^^^^ -//│ ╟── Note: constraint arises from `case` expression: -//│ ║ l.90: Cons(x, xs) and mapPartition(f, xs) is [l, r] and f(x) is -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.91: Left(v) then [Cons(v, l), r] -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.92: Right(v) then [l, Cons(v, r)] -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── from application: -//│ ║ l.90: Cons(x, xs) and mapPartition(f, xs) is [l, r] and f(x) is -//│ ╙── ^^^^^^^^^^^^^^^^^^^ -//│ fun mapPartition: (anything, Cons[anything] | Nil) -> (error | [Nil, Nil]) +//│ ╔══[ERROR] identifier `l` not found +//│ ║ l.129: Left(v) then [Cons(v, l), r] +//│ ╙── ^ +//│ ╔══[ERROR] identifier `r` not found +//│ ║ l.129: Left(v) then [Cons(v, l), r] +//│ ╙── ^ +//│ ╔══[ERROR] identifier `l` not found +//│ ║ l.130: Right(v) then [l, Cons(v, r)] +//│ ╙── ^ +//│ ╔══[ERROR] identifier `r` not found +//│ ║ l.130: Right(v) then [l, Cons(v, r)] +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: l +//│ ║ l.129: Left(v) then [Cons(v, l), r] +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: r +//│ ║ l.129: Left(v) then [Cons(v, l), r] +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: l +//│ ║ l.130: Right(v) then [l, Cons(v, r)] +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: r +//│ ║ l.130: Right(v) then [l, Cons(v, r)] +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.128: Cons(x, xs) and mapPartition(f, xs) is [l, r] and f(x) is +//│ ║ ^^^^^^^ +//│ ║ l.129: Left(v) then [Cons(v, l), r] +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.130: Right(v) then [l, Cons(v, r)] +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ fun mapPartition: forall 'a 'A 'A0. ('a -> (Left['A] | Right['A0]), Cons['a] | Nil) -> [Cons['A] | Nil | error, Cons['A0] | Nil | error] //│ Code generation encountered an error: -//│ unknown match case: Tuple#2 +//│ unresolved symbol l :re // TODO mapPartition(f, zeroToThree) -//│ error | [Nil, Nil] +//│ [Cons[0 | 1 | 2 | 3] | Nil | error, Cons[0 | 1 | 2 | 3] | Nil | error] //│ res //│ Runtime error: //│ ReferenceError: mapPartition3 is not defined @@ -132,47 +176,26 @@ mapPartition(f, zeroToThree) :pe :w :e -:ge fun mapPartition(f, xs) = if xs is Nil then [Nil, Nil] Cons(x, xs) and mapPartition(f, xs) is [l, r] and f(x) is Left(v) then [Cons(v, l), r] Right(v) then [l, Cons(v, r)] //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here -//│ ║ l.140: Right(v) then [l, Cons(v, r)] +//│ ║ l.183: Right(v) then [l, Cons(v, r)] //│ ╙── ^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.139: and f(x) is Left(v) then [Cons(v, l), r] +//│ ║ l.182: and f(x) is Left(v) then [Cons(v, l), r] //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.140: Right(v) then [l, Cons(v, r)] +//│ ║ l.183: Right(v) then [l, Cons(v, r)] //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╔══[ERROR] type identifier not found: Tuple#2 -//│ ╙── -//│ ╔══[ERROR] Type mismatch in definition: -//│ ║ l.136: fun mapPartition(f, xs) = if xs is -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.137: Nil then [Nil, Nil] -//│ ║ ^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.138: Cons(x, xs) and mapPartition(f, xs) is [l, r] -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.139: and f(x) is Left(v) then [Cons(v, l), r] -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.140: Right(v) then [l, Cons(v, r)] -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── tuple literal of type `[Nil, Nil]` is not an instance of type `Object` -//│ ║ l.137: Nil then [Nil, Nil] -//│ ║ ^^^^^^^^^^ -//│ ╟── Note: constraint arises from `case` expression: -//│ ║ l.138: Cons(x, xs) and mapPartition(f, xs) is [l, r] -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.139: and f(x) is Left(v) then [Cons(v, l), r] -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.140: Right(v) then [l, Cons(v, r)] +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.182: and f(x) is Left(v) then [Cons(v, l), r] +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.183: Right(v) then [l, Cons(v, r)] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── from application: -//│ ║ l.138: Cons(x, xs) and mapPartition(f, xs) is [l, r] -//│ ╙── ^^^^^^^^^^^^^^^^^^^ -//│ fun mapPartition: (anything, Cons[anything] | Nil) -> (error | [Nil, Nil]) -//│ Code generation encountered an error: -//│ unknown match case: Tuple#2 +//│ ╟── tuple literal of type `[?a, ?b]` is not a function +//│ ║ l.182: and f(x) is Left(v) then [Cons(v, l), r] +//│ ╙── ^^^^^^^^^^^^^^^ +//│ fun mapPartition: forall 'a. ('a -> Left[anything], Cons['a] | Nil) -> (error | [Nil, Nil]) diff --git a/shared/src/test/diff/ucs/NestedOpSplits.mls b/shared/src/test/diff/ucs/NestedOpSplits.mls index e4432e68..573b59ca 100644 --- a/shared/src/test/diff/ucs/NestedOpSplits.mls +++ b/shared/src/test/diff/ucs/NestedOpSplits.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper // * Note that this always to the left @@ -8,11 +8,9 @@ fun f(x) = 1 + 2 then 0 _ then 1 -//│ ╔══[ERROR] Type mismatch in operator application: -//│ ║ l.7: if x == -//│ ║ ^^^^ +//│ ╔══[ERROR] Type mismatch in application: //│ ║ l.8: 1 + -//│ ║ ^^^^^^^ +//│ ║ ^ //│ ║ l.9: 2 then 0 //│ ║ ^^^^^^^ //│ ╟── operator application of type `Bool` is not an instance of type `Int` @@ -20,6 +18,12 @@ fun f(x) = //│ ║ ^^^^ //│ ║ l.8: 1 + //│ ╙── ^^^^^^ +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.8: 1 + +//│ ║ ^ +//│ ║ l.9: 2 then 0 +//│ ║ ^^^^^^^ +//│ ╙── application of type `Int` is not an instance of type `Bool` //│ fun f: Num -> (0 | 1) diff --git a/shared/src/test/diff/ucs/NestedPattern.mls b/shared/src/test/diff/ucs/NestedPattern.mls index c292de55..33549162 100644 --- a/shared/src/test/diff/ucs/NestedPattern.mls +++ b/shared/src/test/diff/ucs/NestedPattern.mls @@ -1,25 +1,18 @@ -:NewParser -:NoJS +:PreTyper -class Option -class Some(value): Option -class None: Option -//│ Defined class Option -//│ Defined class Some -//│ Defined class None -//│ Option: () -> Option -//│ Some: 'value -> (Some & {value: 'value}) -//│ None: () -> None +abstract class Option[A]: Some[A] | None +class Some[A](value: A) extends Option[A] +module None extends Option +//│ abstract class Option[A]: None | Some[A] +//│ class Some[A](value: A) extends Option +//│ module None extends Option -class Either -class Left(leftValue): Either -class Right(rightValue): Either -//│ Defined class Either -//│ Defined class Left -//│ Defined class Right -//│ Either: () -> Either -//│ Left: 'leftValue -> (Left & {leftValue: 'leftValue}) -//│ Right: 'rightValue -> (Right & {rightValue: 'rightValue}) +abstract class Either[out A, out B]: Left[A] | Right[B] +class Left[A](leftValue: A) extends Either[A, nothing] +class Right[B](rightValue: B) extends Either[nothing, B] +//│ abstract class Either[A, B]: Left[A] | Right[B] +//│ class Left[A](leftValue: A) extends Either +//│ class Right[B](rightValue: B) extends Either fun compute(v) = if v is @@ -27,57 +20,74 @@ fun compute(v) = Left(None()) then 1 Right(Some(x)) then x * 3 Right(None()) then 0 -//│ compute: (Left & {leftValue: None | Some & {value: int}} | Right & {rightValue: None | Some & {value: int}}) -> int +//│ fun compute: (Left[None | Some[Int]] | Right[None | Some[Int]]) -> Int fun crazy(v) = if v is Some(Some(Some(Some(Some(Some(None())))))) then "bruh!" _ then "lol" -//│ crazy: anything -> ("bruh!" | "lol") +//│ fun crazy: (Object & ~#Some | Some[Object & ~#Some | Some[Object & ~#Some | Some[Object & ~#Some | Some[Object & ~#Some | Some[Object & ~#Some | Some[Object]]]]]]) -> ("bruh!" | "lol") // * TODO(@chengluyu) fix these missing locations -:e fun f(x) = if x is [0, 0] then "zeros" [1, 1] then "ones" _ then "bruh" -//│ ╔══[ERROR] type identifier not found: Tuple#2 -//│ ╙── -//│ f: error -> error +//│ fun f: {0: Object, 1: Object} -> ("bruh" | "ones" | "zeros") + +f([0, 0]) +f([1, 1]) +f([1, 0]) +//│ "bruh" | "ones" | "zeros" +//│ res +//│ = 'zeros' +//│ res +//│ = 'ones' +//│ res +//│ = 'bruh' -:e fun f(x) = if x is [0, 0] then "zeros" [1, 1] then "ones" [y, 1] then x _ then "que?" -//│ ╔══[ERROR] type identifier not found: Tuple#2 -//│ ╙── -//│ f: error -> error +//│ fun f: forall 'a. ({0: Object, 1: Object} & 'a) -> ("ones" | "que?" | "zeros" | 'a) + +f([0, 0]) +f([1, 1]) +f([0, 1]) +f([1, 0]) +//│ "ones" | "que?" | "zeros" | [1, 0] +//│ res +//│ = 'zeros' +//│ res +//│ = 'ones' +//│ res +//│ = [ 0, 1 ] +//│ res +//│ = 'que?' -:e fun f(p) = if p is Some([x, y]) then x + y None() then 0 -//│ ╔══[ERROR] type identifier not found: Tuple#2 -//│ ╙── -//│ f: (None | Some) -> (0 | error) +//│ fun f: (None | Some[{0: Int, 1: Int}]) -> Int -class Union(a, b) -//│ Defined class Union -//│ Union: ('a, 'b,) -> (Union & {a: 'a, b: 'b}) +class Union[A, B](a: A, b: B) +//│ class Union[A, B](a: A, b: B) // Name conflict between the scrutinee and the positionals. // Desugar result: let tmp13 = x in case tmp13 of { Union => let x = (tmp13).a in let y = (tmp13).b in x } fun hmm(x) = if x is Union(x, y) then x -//│ hmm: (Union & {a: 'a}) -> 'a +//│ fun hmm: forall 'a. Union['a, anything] -> 'a hmm(Union(1, 2)) -//│ res: 1 +//│ 1 +//│ res +//│ = 1 diff --git a/shared/src/test/diff/ucs/NuPlainConditionals.mls b/shared/src/test/diff/ucs/NuPlainConditionals.mls index 328fbf5c..053e31b8 100644 --- a/shared/src/test/diff/ucs/NuPlainConditionals.mls +++ b/shared/src/test/diff/ucs/NuPlainConditionals.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper class Pair[A](fst: A, snd: A) @@ -53,41 +53,58 @@ fun foo(x) = if x is Pair(a, b) then a > b else false fun foo(x) = x is Pair Int -//│ ╔══[ERROR] illegal pattern +//│ ╔══[ERROR] Unknown pattern {Pair; Int} //│ ║ l.54: Pair //│ ║ ^^^^ //│ ║ l.55: Int //│ ╙── ^^^^^ -//│ fun foo: anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ fun foo: anything -> true // TODO proper error fun foo(x) = x is Pair(a, b) and a > b Int -//│ ╔══[ERROR] illegal pattern -//│ ║ l.67: Pair(a, b) and a > b +//│ ╔══[ERROR] Unknown pattern {and(Pair(a, b,), >(a, b,),); Int} +//│ ║ l.65: Pair(a, b) and a > b //│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.68: Int +//│ ║ l.66: Int //│ ╙── ^^^^^ -//│ fun foo: anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ fun foo: anything -> true // TODO support `|` fun foo1(x) = x is Pair(a, b) | Int fun foo2(x) = x is (Pair(a, b) and a > b) | Int -//│ ╔══[ERROR] Illegal pattern `|` -//│ ║ l.79: fun foo1(x) = x is Pair(a, b) | Int +//│ ╔══[ERROR] Unknown pattern '(' and(Pair(a, b,), >(a, b,),) ')' +//│ ║ l.76: fun foo2(x) = x is (Pair(a, b) and a > b) | Int +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ +//│ ╔══[ERROR] type identifier `|` not found +//│ ║ l.76: fun foo2(x) = x is (Pair(a, b) and a > b) | Int +//│ ╙── ^ +//│ ╔══[ERROR] type identifier `Int` not found +//│ ║ l.76: fun foo2(x) = x is (Pair(a, b) and a > b) | Int +//│ ╙── ^^^ +//│ ╔══[ERROR] identifier `|` not found +//│ ║ l.76: fun foo2(x) = x is (Pair(a, b) and a > b) | Int +//│ ╙── ^ +//│ ╔══[ERROR] type identifier `|` not found +//│ ║ l.75: fun foo1(x) = x is Pair(a, b) | Int +//│ ╙── ^ +//│ ╔══[ERROR] type identifier `Int` not found +//│ ║ l.75: fun foo1(x) = x is Pair(a, b) | Int +//│ ╙── ^^^ +//│ ╔══[ERROR] identifier `|` not found +//│ ║ l.75: fun foo1(x) = x is Pair(a, b) | Int +//│ ╙── ^ +//│ ╔══[ERROR] type identifier not found: | +//│ ║ l.75: fun foo1(x) = x is Pair(a, b) | Int //│ ╙── ^ -//│ ╔══[ERROR] Illegal pattern `|` -//│ ║ l.80: fun foo2(x) = x is (Pair(a, b) and a > b) | Int +//│ ╔══[ERROR] type identifier not found: | +//│ ║ l.76: fun foo2(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^ -//│ fun foo1: anything -> error -//│ fun foo2: anything -> error +//│ fun foo1: nothing -> error +//│ fun foo2: nothing -> error //│ Code generation encountered an error: -//│ if expression was not desugared +//│ unknown match case: Int diff --git a/shared/src/test/diff/ucs/Or.mls b/shared/src/test/diff/ucs/Or.mls index 066a382e..516894f7 100644 --- a/shared/src/test/diff/ucs/Or.mls +++ b/shared/src/test/diff/ucs/Or.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper class Some[T](value: T) @@ -11,11 +11,12 @@ fun f(a, b) = if a is and b is Some(v') then v + v' or b is Some(v) then v else 0 -//│ ╔══[ERROR] Illegal pattern `and` -//│ ║ l.11: and b is Some(v') then v + v' -//│ ╙── ^^^ -//│ fun f: (anything, anything) -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╔══[ERROR] cannot transform due to an illegal split operator or +//│ ║ l.12: or b is Some(v) then v +//│ ║ ^^ +//│ ╟── the following branch will be discarded +//│ ║ l.12: or b is Some(v) then v +//│ ╙── ^^^^^^^^^^^^^^^^^^^^ +//│ fun f: (Object & ~#Some | Some[Int], Object & ~#Some | Some[Int]) -> Int diff --git a/shared/src/test/diff/ucs/OverlappedBranches.mls b/shared/src/test/diff/ucs/OverlappedBranches.mls index 1bac2286..657ca31c 100644 --- a/shared/src/test/diff/ucs/OverlappedBranches.mls +++ b/shared/src/test/diff/ucs/OverlappedBranches.mls @@ -23,6 +23,15 @@ fun f1(x) = if x is Base then "b" Derived1 then "d1" Derived2 then "d2" +//│ ╔══[WARNING] old desugarer used +//│ ║ l.22: fun f1(x) = if x is +//│ ║ ^^^^ +//│ ║ l.23: Base then "b" +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ║ l.24: Derived1 then "d1" +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.25: Derived2 then "d2" +//│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ ╔══[WARNING] Found a duplicated branch //│ ╟── This branch //│ ║ l.24: Derived1 then "d1" @@ -75,6 +84,18 @@ fun f2(x, p) = if x is Derived1 then "d1" Derived2 then "d2" else "otherwise" +//│ ╔══[WARNING] old desugarer used +//│ ║ l.82: fun f2(x, p) = if x is +//│ ║ ^^^^ +//│ ║ l.83: Base and p(x) then "b and p" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.84: Derived1 then "d1" +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.85: Derived2 then "d2" +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.86: else "otherwise" +//│ ╙── ^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ f2: (Base & 'a | ~Base, 'a -> anything,) -> ("b and p" | "d1" | "d2" | "otherwise") //│ = [Function: f2] diff --git a/shared/src/test/diff/ucs/ParseFailures.mls b/shared/src/test/diff/ucs/ParseFailures.mls index 7843c935..889a0cca 100644 --- a/shared/src/test/diff/ucs/ParseFailures.mls +++ b/shared/src/test/diff/ucs/ParseFailures.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper :NoJS // FIXME @@ -33,7 +33,13 @@ fun foo(x, y) = if x is //│ ╔══[PARSE ERROR] Unexpected 'else' keyword here //│ ║ l.32: Z() and y is O() then 0 else 1 //│ ╙── ^^^^ -//│ ╔══[ERROR] Illegal pattern `Z` +//│ ╔══[ERROR] type identifier `Z` not found //│ ║ l.32: Z() and y is O() then 0 else 1 //│ ╙── ^ -//│ fun foo: (anything, anything) -> error +//│ ╔══[ERROR] type identifier `O` not found +//│ ║ l.32: Z() and y is O() then 0 else 1 +//│ ╙── ^ +//│ ╔══[ERROR] type identifier not found: Z +//│ ║ l.32: Z() and y is O() then 0 else 1 +//│ ╙── ^ +//│ fun foo: (nothing, anything) -> error diff --git a/shared/src/test/diff/ucs/PlainConditionals.mls b/shared/src/test/diff/ucs/PlainConditionals.mls index 96a98bb5..c15867ad 100644 --- a/shared/src/test/diff/ucs/PlainConditionals.mls +++ b/shared/src/test/diff/ucs/PlainConditionals.mls @@ -1,103 +1,123 @@ -:NewParser +:PreTyper -class Pair(fst, snd) -//│ Defined class Pair -//│ Pair: ('fst, 'snd,) -> (Pair & {fst: 'fst, snd: 'snd}) -//│ = [Function: Pair1] +class Pair[A, B](fst: A, snd: B) +//│ class Pair[A, B](fst: A, snd: B) Pair(0, 1) is Pair -//│ res: Bool -//│ = true +//│ Bool +//│ res +//│ = true Pair(0, 1) is Pair(a, b) -//│ res: Bool -//│ = true +//│ Bool +//│ res +//│ = true Pair(0, 1) is Pair(0, _) -//│ res: Bool -//│ = true +//│ Bool +//│ res +//│ = true if Pair(0, 1) is Pair(a, b) then true else false -//│ res: Bool -//│ = true +//│ Bool +//│ res +//│ = true fun foo(x) = x is Pair(a, b) -//│ foo: anything -> Bool -//│ = [Function: foo] +//│ fun foo: (Object & ~#Pair | Pair[anything, anything]) -> Bool Pair(0, 1) is Pair(a, b) and a > b -//│ res: bool -//│ = false +//│ Bool +//│ res +//│ = false if Pair(0, 1) is Pair(a, b) then a > b else false -//│ res: bool -//│ = false +//│ Bool +//│ res +//│ = false fun foo(x) = x is Pair(a, b) and a > b -//│ foo: (Pair & {fst: number, snd: number} | ~Pair) -> bool -//│ = [Function: foo1] +//│ fun foo: (Object & ~#Pair | Pair[Num, Num]) -> Bool fun foo(x) = if x is Pair(a, b) then a > b else false -//│ foo: (Pair & {fst: number, snd: number} | ~Pair) -> bool -//│ = [Function: foo2] +//│ fun foo: (Object & ~#Pair | Pair[Num, Num]) -> Bool // TODO proper error fun foo(x) = x is Pair Int -//│ ╔══[ERROR] illegal pattern -//│ ║ l.53: Pair +//│ ╔══[ERROR] Unknown pattern {Pair; Int} +//│ ║ l.54: Pair //│ ║ ^^^^ -//│ ║ l.54: Int +//│ ║ l.55: Int //│ ╙── ^^^^^ -//│ foo: anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ fun foo: anything -> true // TODO proper error fun foo(x) = x is Pair(a, b) and a > b Int -//│ ╔══[ERROR] illegal pattern -//│ ║ l.66: Pair(a, b) and a > b +//│ ╔══[ERROR] Unknown pattern {and(Pair(a, b,), >(a, b,),); Int} +//│ ║ l.65: Pair(a, b) and a > b //│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.67: Int +//│ ║ l.66: Int //│ ╙── ^^^^^ -//│ foo: anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ fun foo: anything -> true // TODO support `|` fun foo(x) = x is Pair(a, b) | Int fun foo(x) = x is (Pair(a, b) and a > b) | Int -//│ ╔══[ERROR] Cannot find operator `|` in the context -//│ ║ l.78: fun foo(x) = x is Pair(a, b) | Int +//│ ╔══[ERROR] Unknown pattern '(' and(Pair(a, b,), >(a, b,),) ')' +//│ ║ l.76: fun foo(x) = x is (Pair(a, b) and a > b) | Int +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ +//│ ╔══[ERROR] type identifier `|` not found +//│ ║ l.76: fun foo(x) = x is (Pair(a, b) and a > b) | Int +//│ ╙── ^ +//│ ╔══[ERROR] type identifier `Int` not found +//│ ║ l.76: fun foo(x) = x is (Pair(a, b) and a > b) | Int +//│ ╙── ^^^ +//│ ╔══[ERROR] identifier `|` not found +//│ ║ l.76: fun foo(x) = x is (Pair(a, b) and a > b) | Int +//│ ╙── ^ +//│ ╔══[ERROR] type identifier `|` not found +//│ ║ l.75: fun foo(x) = x is Pair(a, b) | Int //│ ╙── ^ -//│ foo: anything -> error -//│ ╔══[ERROR] Cannot find operator `|` in the context -//│ ║ l.79: fun foo(x) = x is (Pair(a, b) and a > b) | Int +//│ ╔══[ERROR] type identifier `Int` not found +//│ ║ l.75: fun foo(x) = x is Pair(a, b) | Int +//│ ╙── ^^^ +//│ ╔══[ERROR] identifier `|` not found +//│ ║ l.75: fun foo(x) = x is Pair(a, b) | Int +//│ ╙── ^ +//│ ╔══[ERROR] Refininition of 'foo' +//│ ║ l.76: fun foo(x) = x is (Pair(a, b) and a > b) | Int +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╔══[ERROR] type identifier not found: | +//│ ║ l.75: fun foo(x) = x is Pair(a, b) | Int +//│ ╙── ^ +//│ ╔══[ERROR] type identifier not found: | +//│ ║ l.76: fun foo(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^ -//│ foo: anything -> error +//│ fun foo: nothing -> error +//│ fun foo: nothing -> error //│ Code generation encountered an error: -//│ if expression was not desugared +//│ unknown match case: Int + +class A[T](arg: T) +//│ class A[T](arg: T) -class A(arg) -//│ Defined class A -//│ A: 'arg -> (A & {arg: 'arg}) -//│ = [Function: A1] -// TODO make `is` lower precedence than `=>` x => (x is A(_)) -//│ res: anything -> Bool -//│ = [Function: res] +//│ (A[anything] | Object & ~#A) -> Bool +//│ res +//│ = [Function: res] diff --git a/shared/src/test/diff/ucs/SimpleUCS.mls b/shared/src/test/diff/ucs/SimpleUCS.mls index d74a1b06..20bcad54 100644 --- a/shared/src/test/diff/ucs/SimpleUCS.mls +++ b/shared/src/test/diff/ucs/SimpleUCS.mls @@ -1,71 +1,76 @@ -:NewParser - -class Option -class Some(value): Option -class None: Option -//│ Defined class Option -//│ Defined class Some -//│ Defined class None -//│ Option: () -> Option -//│ = [Function: Option1] -//│ Some: 'value -> (Some & {value: 'value}) -//│ = [Function: Some1] -//│ None: () -> None -//│ = [Function: None1] - -class Either -class Left(leftValue): Either -class Right(rightValue): Either -//│ Defined class Either -//│ Defined class Left -//│ Defined class Right -//│ Either: () -> Either -//│ = [Function: Either1] -//│ Left: 'leftValue -> (Left & {leftValue: 'leftValue}) -//│ = [Function: Left1] -//│ Right: 'rightValue -> (Right & {rightValue: 'rightValue}) -//│ = [Function: Right1] +:PreTyper + +abstract class Option[A]: Some[A] | None +class Some[A](value: A) extends Option[A] +module None extends Option +//│ abstract class Option[A]: None | Some[A] +//│ class Some[A](value: A) extends Option +//│ module None extends Option + +abstract class Either[out A, out B]: Left[A] | Right[B] +class Left[A](leftValue: A) extends Either[A, nothing] +class Right[B](rightValue: B) extends Either[nothing, B] +//│ abstract class Either[A, B]: Left[A] | Right[B] +//│ class Left[A](leftValue: A) extends Either +//│ class Right[B](rightValue: B) extends Either :e -:ge fun f(x, y) = if x is Left(xv) and y is Left(yv) then xv + yv Right(xv) and y is Right(yv) then xv * yv - None() and y is None() then 0 -//│ ╔══[ERROR] The match is not exhaustive. -//│ ║ l.33: Left(xv) and y is Left(yv) then xv + yv -//│ ║ ^^^^^^^^^^^^^ -//│ ╟── The scrutinee at this position misses 2 cases. -//│ ║ l.33: Left(xv) and y is Left(yv) then xv + yv + None and y is None then 0 +//│ ╔══[ERROR] When scrutinee `x` is `Left` +//│ ║ l.20: Left(xv) and y is Left(yv) then xv + yv +//│ ║ ^^^^ +//│ ╟── Scrutinee `y` has 2 missing cases +//│ ║ l.20: Left(xv) and y is Left(yv) then xv + yv //│ ║ ^ -//│ ╟── [Missing Case 1/2] `None` -//│ ╟── It first appears here. -//│ ║ l.35: None() and y is None() then 0 -//│ ║ ^^^^^^ -//│ ╟── [Missing Case 2/2] `Right` -//│ ╟── It first appears here. -//│ ║ l.34: Right(xv) and y is Right(yv) then xv * yv -//│ ╙── ^^^^^^^^^ -//│ f: (anything, anything,) -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╟── It can be module `None` +//│ ║ l.22: None and y is None then 0 +//│ ║ ^^^^ +//│ ╟── It can be class `Right` +//│ ║ l.21: Right(xv) and y is Right(yv) then xv * yv +//│ ╙── ^^^^^ +//│ ╔══[ERROR] When scrutinee `x` is `Right` +//│ ║ l.21: Right(xv) and y is Right(yv) then xv * yv +//│ ║ ^^^^^ +//│ ╟── Scrutinee `y` has 2 missing cases +//│ ║ l.21: Right(xv) and y is Right(yv) then xv * yv +//│ ║ ^ +//│ ╟── It can be class `Left` +//│ ║ l.20: Left(xv) and y is Left(yv) then xv + yv +//│ ║ ^^^^ +//│ ╟── It can be module `None` +//│ ║ l.22: None and y is None then 0 +//│ ╙── ^^^^ +//│ ╔══[ERROR] When scrutinee `x` is `None` +//│ ║ l.22: None and y is None then 0 +//│ ║ ^^^^ +//│ ╟── Scrutinee `y` has 2 missing cases +//│ ║ l.22: None and y is None then 0 +//│ ║ ^ +//│ ╟── It can be class `Left` +//│ ║ l.20: Left(xv) and y is Left(yv) then xv + yv +//│ ║ ^^^^ +//│ ╟── It can be class `Right` +//│ ║ l.21: Right(xv) and y is Right(yv) then xv * yv +//│ ╙── ^^^^^ +//│ fun f: (Left[Int] | None | Right[Int], nothing) -> Int fun f(x, y) = if x is Left(xv) and y is Left(yv) then xv + yv - None() then 0 -//│ f: (Left & {leftValue: int} | None, Left & {leftValue: int},) -> int -//│ = [Function: f1] + None then 0 +//│ fun f: (Left[Int] | None, Left[Int]) -> Int fun f(x, y) = if x is Left(xv) and y is Left(yv) then xv + yv Right(yv) then xv * yv - None() then 0 -//│ f: (Left & {leftValue: int} | None, Left & {leftValue: int} | Right & {rightValue: int},) -> int -//│ = [Function: f2] + None then 0 +//│ fun f: (Left[Int] | None, Left[Int] | Right[Int]) -> Int fun f(x) = if x is @@ -73,25 +78,19 @@ fun f(x) = v < 0 then "negative" v > 0 then "positive" _ then "zero" - None() then "nothing" -//│ f: (None | Some & {value: number}) -> ("negative" | "nothing" | "positive" | "zero") -//│ = [Function: f3] + None then "nothing" +//│ fun f: (None | Some[Num]) -> ("negative" | "nothing" | "positive" | "zero") fun f(x, y) = if x is Some(x) and y is Some(y) then 0 -//│ f: (Some, Some,) -> 0 -//│ = [Function: f4] - -class A(value) -class B(value) -//│ Defined class A -//│ Defined class B -//│ A: 'value -> (A & {value: 'value}) -//│ = [Function: A1] -//│ B: 'value -> (B & {value: 'value}) -//│ = [Function: B1] +//│ fun f: (Some[anything], Some[anything]) -> 0 + +class A[T](value: T) +class B[T](value: T) +//│ class A[T](value: T) +//│ class B[T](value: T) fun f(x, y, u, v) = if x is @@ -102,107 +101,94 @@ fun f(x, y, u, v) = B(0) then 0 B(1) then 1 A(_) then 99 -//│ f: (A, number, number, number,) -> (0 | 1 | 99) -//│ = [Function: f5] +//│ fun f: (A[anything], Num, Num, Num) -> (0 | 1 | 99) fun f(x) = if x is A(_) then "A" B(_) then "B" -//│ f: (A | B) -> ("A" | "B") -//│ = [Function: f6] +//│ fun f: (A[anything] | B[anything]) -> ("A" | "B") :e -:ge fun f(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv - None() and y is None() then 0 -//│ ╔══[ERROR] The match is not exhaustive. -//│ ║ l.119: Some(xv) and y is Some(yv) then xv + yv -//│ ║ ^^^^^^^^^^^^^ -//│ ╟── The scrutinee at this position misses 1 case. -//│ ║ l.119: Some(xv) and y is Some(yv) then xv + yv + None and y is None then 0 +//│ ╔══[ERROR] When scrutinee `x` is `Some` +//│ ║ l.115: Some(xv) and y is Some(yv) then xv + yv +//│ ║ ^^^^ +//│ ╟── Scrutinee `y` has 1 missing case +//│ ║ l.115: Some(xv) and y is Some(yv) then xv + yv //│ ║ ^ -//│ ╟── [Missing Case 1/1] `None` -//│ ╟── It first appears here. -//│ ║ l.120: None() and y is None() then 0 -//│ ╙── ^^^^^^ -//│ f: (anything, anything,) -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╟── It can be module `None` +//│ ║ l.116: None and y is None then 0 +//│ ╙── ^^^^ +//│ ╔══[ERROR] When scrutinee `x` is `None` +//│ ║ l.116: None and y is None then 0 +//│ ║ ^^^^ +//│ ╟── Scrutinee `y` has 1 missing case +//│ ║ l.116: None and y is None then 0 +//│ ║ ^ +//│ ╟── It can be class `Some` +//│ ║ l.115: Some(xv) and y is Some(yv) then xv + yv +//│ ╙── ^^^^ +//│ fun f: (None | Some[Int], nothing) -> Int :e -:ge fun f(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv - None() then xv * 2 - None() and y is + None then xv * 2 + None and y is Some(yv) then yv * 3 -//│ ╔══[ERROR] The match is not exhaustive. -//│ ║ l.142: None() and y is -//│ ║ ^^^^ -//│ ╟── The scrutinee at this position misses 1 case. -//│ ║ l.142: None() and y is -//│ ║ ^ -//│ ╟── [Missing Case 1/1] `None` -//│ ╟── It first appears here. -//│ ║ l.141: None() then xv * 2 -//│ ╙── ^^^^^^ -//│ f: (anything, anything,) -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╔══[ERROR] When scrutinee `x` is `None` +//│ ║ l.143: None and y is +//│ ║ ^^^^ +//│ ╟── Scrutinee `y` has 1 missing case +//│ ║ l.143: None and y is +//│ ║ ^ +//│ ╟── It can be module `None` +//│ ║ l.142: None then xv * 2 +//│ ╙── ^^^^ +//│ fun f: (None | Some[Int], Some[Int]) -> Int fun f(x, y) = if x is A and y is B then "bruh" -//│ f: (A, B,) -> "bruh" -//│ = [Function: f9] +//│ fun f: (A[anything], B[anything]) -> "bruh" fun f(x, y, z) = if x is A and z == 0 and y == 0 and y is B then "bruh" A then "oui" -//│ f: (A, number, number,) -> ("bruh" | "oui") -//│ = [Function: f10] +//│ fun f: (A[anything], Num, Num) -> ("bruh" | "oui") // We do need a syntax to specify default branch in IfOpsApp... :e -:ge fun f(x, y) = if x is Some(x) and y > 0 then "gt" < 0 then "le" == 0 then "eq" -//│ ╔══[ERROR] The case when this is false is not handled: ==(y,)(0,) -//│ ║ l.178: Some(x) and y -//│ ║ ^ -//│ ║ l.179: > 0 then "gt" -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.180: < 0 then "le" -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.181: == 0 then "eq" -//│ ╙── ^^^^^^^^^^ -//│ f: (anything, anything,) -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.176: == 0 then "eq" +//│ ║ ^^^^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ fun f: (Some[anything], Num) -> ("eq" | "gt" | "le") fun isValid(x) = if x then false else true -//│ isValid: anything -> Bool -//│ = [Function: isValid] +//│ fun isValid: Bool -> Bool fun f(x, allowNone) = if x is Some(x) and isValid(x) then "good" - None() and allowNone then "okay" + None and allowNone then "okay" else "bad" -//│ f: (anything, anything,) -> ("bad" | "good" | "okay") -//│ = [Function: f12] +//│ fun f: (Object & ~#Some | Some[Bool], Bool) -> ("bad" | "good" | "okay") fun f(x) = if x is @@ -210,47 +196,44 @@ fun f(x) = Some(x) then "roll" _ and x == 0 then 0 _ then "rock" -//│ f: (None | Some | number) -> ("bruh" | "rock" | "roll" | 0) -//│ = [Function: f13] +//│ fun f: (None | Num | Some[anything]) -> ("bruh" | "rock" | "roll" | 0) fun f(x, a, b) = if x is A(aa) and a then aa B(bb) and b then bb _ then 0 -//│ f: (A & {value: 'value} | B & {value: 'value} | ~A & ~B, anything, anything,) -> (0 | 'value) -//│ = [Function: f14] +//│ fun f: forall 'a. (A['a] | B['a] | Object & ~#A & ~#B, Bool, Bool) -> (0 | 'a) fun f(x, y, b) = if x is Some(xv) and y is Some(yv) then "bruh" - is None() then "bruh" + is None then "bruh" Some(xv) and b then xv + b _ then "roll" -//│ f: (Some & {value: int} | ~Some, anything, ~true,) -> ("bruh" | "roll" | int) -//│ = [Function: f15] +//│ fun f: (Object & ~#Some | Some[Int], Object & ~#Some | Some[anything], nothing) -> ("bruh" | "roll" | Int) fun g(x, y, b) = if x is Some(xv) and y is Some(yv) then yv - is None() then "bruh" + is None then "bruh" Some(xv) and b then xv + b _ then "roll" -//│ g: (Some & {value: int} | ~Some, Some & {value: 'value} | ~Some, ~true,) -> ("bruh" | "roll" | int | 'value) -//│ = [Function: g] +//│ fun g: forall 'a. (Object & ~#Some | Some[Int], Object & ~#Some | Some['a], nothing) -> ("bruh" | "roll" | Int | 'a) fun foo(x, y, z) = - if x - y > 0 then Some(x + y + z) else None() -//│ foo: (int, int, int,) -> (None | Some & {value: int}) -//│ = [Function: foo] + if x - y > 0 then Some(x + y + z) else None +//│ fun foo: forall 'A. (Int, Int, Int) -> (None | Some['A]) +//│ where +//│ 'A :> Int // Uncomment this block to make the following block work. // fun foo(x, y, z) = // if x - y > 0 then Some( // if x % 2 == 0 then Left(x) else Right(x) -// ) else None() +// ) else None :e fun f(u, v, w) = @@ -260,31 +243,30 @@ fun f(u, v, w) = Right(_) then "right-defined" None then "undefined" //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.257: if foo(u, v, w) is -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.258: Some(x) and x is -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.259: Left(_) then "left-defined" +//│ ║ l.241: Some(x) and x is +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ║ l.242: Left(_) then "left-defined" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.260: Right(_) then "right-defined" +//│ ║ l.243: Right(_) then "right-defined" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.261: None then "undefined" +//│ ║ l.244: None then "undefined" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── operator application of type `int` does not match type `Left & ?a | Right & ?b` -//│ ║ l.245: if x - y > 0 then Some(x + y + z) else None() +//│ ╟── operator application of type `Int` does not match type `Left[?A] | Right[?B]` +//│ ║ l.227: if x - y > 0 then Some(x + y + z) else None //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from reference: -//│ ║ l.258: Some(x) and x is +//│ ║ l.241: Some(x) and x is //│ ║ ^ -//│ ╟── from application: -//│ ║ l.257: if foo(u, v, w) is -//│ ╙── ^^^^^^^^^^^^ -//│ f: (int, int, int,) -> ("left-defined" | "right-defined" | "undefined") -//│ = [Function: f16] +//│ ╟── from field selection: +//│ ║ l.4: class Some[A](value: A) extends Option[A] +//│ ║ ^^^^^ +//│ ╟── Note: type parameter A is defined at: +//│ ║ l.4: class Some[A](value: A) extends Option[A] +//│ ╙── ^ +//│ fun f: (Int, Int, Int) -> ("left-defined" | "right-defined" | "undefined") fun p(x) = if x >= 0 then Right(x) else Left(x) -//│ p: (number & 'rightValue) -> (Left & {leftValue: 'rightValue} | Right & {rightValue: 'rightValue}) -//│ = [Function: p] +//│ fun p: forall 'B 'A. (Num & 'B & 'A) -> (Left['A] | Right['B]) fun g(a, b) = if p(a) is @@ -294,42 +276,34 @@ fun g(a, b) = Right(x) and b is Some(y) then x * y None then x -//│ g: (int, None | Some & {value: int},) -> int -//│ = [Function: g1] +//│ fun g: (Int, None | Some[Int]) -> Int -g(5, None()) +g(5, None) g(5, Some(7)) -g(0 - 5, None()) +g(0 - 5, None) g(0 - 5, Some(9)) -//│ res: int -//│ = 5 -//│ res: int -//│ = 35 -//│ res: int -//│ = 25 -//│ res: int -//│ = 4 - -class Var(name) +//│ Int +//│ res +//│ = 5 +//│ res +//│ = 35 +//│ res +//│ = 25 +//│ res +//│ = 4 + +class Var(name: Str) class ValBase -class IntVal(value): ValBase -class BoolVal(value): ValBase -class Lit(value) -//│ Defined class Var -//│ Defined class ValBase -//│ Defined class IntVal -//│ Defined class BoolVal -//│ Defined class Lit -//│ Var: 'name -> (Var & {name: 'name}) -//│ = [Function: Var1] -//│ ValBase: () -> ValBase -//│ = [Function: ValBase1] -//│ IntVal: 'value -> (IntVal & {value: 'value}) -//│ = [Function: IntVal1] -//│ BoolVal: 'value -> (BoolVal & {value: 'value}) -//│ = [Function: BoolVal1] -//│ Lit: 'value -> (Lit & {value: 'value}) -//│ = [Function: Lit1] +class IntVal(value: Int) extends ValBase +class BoolVal(value: Bool) extends ValBase +class Lit[T](value: T) +//│ class Var(name: Str) +//│ class ValBase { +//│ constructor() +//│ } +//│ class IntVal(value: Int) extends ValBase +//│ class BoolVal(value: Bool) extends ValBase +//│ class Lit[T](value: T) fun p(e, context) = if e is @@ -338,72 +312,84 @@ fun p(e, context) = Some(BoolVal(v)) then Right(v) Lit(IntVal(v)) then Left(v) Lit(BoolVal(v)) then Right(v) -//│ p: (Lit & {value: BoolVal & {value: 'value} | IntVal & {value: 'value0}} | Var & {name: 'name}, { -//│ get: 'name -> (Some & {value: BoolVal & {value: 'value} | IntVal & {value: 'value0}}) -//│ },) -> (Left & {leftValue: 'value0} | Right & {rightValue: 'value}) -//│ = [Function: p1] +//│ fun p: forall 'A 'B. (Lit[BoolVal | IntVal] | Var, {get: Str -> Some[BoolVal | IntVal]}) -> (Left[in 'A out Int | 'A] | Right[in 'B out Bool | 'B]) class Nil() -//│ Defined class Nil -//│ Nil: () -> Nil -//│ = [Function: Nil1] +//│ class Nil() // Support operator constructor like :: :e -:ge fun f(x) = if x is 0 :: Nil() then "oh" -//│ ╔══[ERROR] Cannot find operator `::` in the context -//│ ║ l.356: 0 :: +//│ ╔══[ERROR] Syntactic split of patterns are not supported +//│ ║ l.324: 0 :: //│ ╙── ^^ -//│ f: anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╔══[ERROR] Found unexpected empty split +//│ ╙── +//│ fun f: anything -> nothing fun f(x) = if x == 0 and x is A(_) then "A" B(_) then "B" else "bruh" -//│ f: number -> ("A" | "B" | "bruh") -//│ = [Function: f18] +//│ fun f: Num -> ("A" | "B" | "bruh") fun helper(x) = - if x == 0 then None() else Some(x) -//│ helper: (number & 'value) -> (None | Some & {value: 'value}) -//│ = [Function: helper] + if x == 0 then None else Some(x) +//│ fun helper: forall 'A. (Num & 'A) -> (None | Some['A]) fun g(x, y) = if x == 0 and helper(x) is Some(a) and helper(y) is Some(b) then a + b - None() then a + 1 - None() and helper(y) is + None then a + 1 + None and helper(y) is Some(b) then 2 + b - None() then 1 + None then 1 else 0 -//│ g: (int, int,) -> int -//│ = [Function: g2] +//│ fun g: (Int, Int) -> Int fun test(x) = if x then 0 else "oops" -//│ test: anything -> ("oops" | 0) -//│ = [Function: test] +//│ fun test: Bool -> ("oops" | 0) test(true) test(false) +//│ "oops" | 0 +//│ res +//│ = 0 +//│ res +//│ = 'oops' + +:e test(0) test(1) -//│ res: "oops" | 0 -//│ = 0 -//│ res: "oops" | 0 -//│ = 'oops' -//│ res: "oops" | 0 -//│ = 'oops' -//│ res: "oops" | 0 -//│ = 'oops' +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.369: test(0) +//│ ║ ^^^^^^^ +//│ ╟── integer literal of type `0` is not an instance of type `Bool` +//│ ║ l.369: test(0) +//│ ║ ^ +//│ ╟── Note: constraint arises from reference: +//│ ║ l.357: fun test(x) = if x then 0 else "oops" +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.370: test(1) +//│ ║ ^^^^^^^ +//│ ╟── integer literal of type `1` is not an instance of type `Bool` +//│ ║ l.370: test(1) +//│ ║ ^ +//│ ╟── Note: constraint arises from reference: +//│ ║ l.357: fun test(x) = if x then 0 else "oops" +//│ ╙── ^ +//│ "oops" | 0 | error +//│ res +//│ = 'oops' +//│ res +//│ = 'oops' diff --git a/shared/src/test/diff/ucs/SplitAfterOp.mls b/shared/src/test/diff/ucs/SplitAfterOp.mls index 09e82898..8ef383c1 100644 --- a/shared/src/test/diff/ucs/SplitAfterOp.mls +++ b/shared/src/test/diff/ucs/SplitAfterOp.mls @@ -1,168 +1,233 @@ -:NewParser +:PreTyper :e -:ge fun f(x, b) = if x == 0 and b then 0 -//│ ╔══[ERROR] The case when this is false is not handled: ==(x,)(0,) -//│ ║ l.6: if x == -//│ ║ ^^^^^ -//│ ║ l.7: 0 and b then 0 -//│ ╙── ^^^^^^ -//│ f: (anything, anything,) -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.6: 0 and b then 0 +//│ ║ ^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.6: 0 and b then 0 +//│ ║ ^^^^^^^^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ fun f: (Num, Bool) -> 0 :e -:ge -if x == y + - 5 then 0 - 7 then 0 -//│ ╔══[ERROR] The case when this is false is not handled: +(==(x,)(y,),)(7,) -//│ ║ l.19: if x == y + -//│ ║ ^^^^^^^^ -//│ ║ l.20: 5 then 0 -//│ ║ ^^^^^^^^^^ -//│ ║ l.21: 7 then 0 -//│ ╙── ^^^^ -//│ res: error -//│ Code generation encountered an error: -//│ if expression was not desugared - -:e -:ge -if x == y * - 5 then 0 - 6 + 7 then 0 -//│ ╔══[ERROR] The case when this is false is not handled: *(==(x,)(y,),)(+(6,)(7,),) -//│ ║ l.35: if x == y * -//│ ║ ^^^^^^^^ -//│ ║ l.36: 5 then 0 -//│ ║ ^^^^^^^^^^ -//│ ║ l.37: 6 + 7 then 0 -//│ ╙── ^^^^^^^ -//│ res: error -//│ Code generation encountered an error: -//│ if expression was not desugared +fun f(x, y) = + if x == y + + 5 then 0 + 7 then 0 +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.19: if x == y + +//│ ║ ^ +//│ ║ l.20: 5 then 0 +//│ ║ ^^^^^ +//│ ╟── operator application of type `Bool` is not an instance of type `Int` +//│ ║ l.19: if x == y + +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.19: if x == y + +//│ ║ ^ +//│ ║ l.20: 5 then 0 +//│ ║ ^^^^^ +//│ ╙── application of type `Int` is not an instance of type `Bool` +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.19: if x == y + +//│ ║ ^ +//│ ║ l.20: 5 then 0 +//│ ║ ^^^^^^^^^^^^^ +//│ ║ l.21: 7 then 0 +//│ ║ ^^^^^ +//│ ╙── application of type `Int` is not an instance of type `Bool` +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.21: 7 then 0 +//│ ║ ^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ fun f: (Num, Num) -> 0 :e -:ge -if x == - y + +fun f(x, y) = + if x == y * 5 then 0 - 7 then 0 -//│ ╔══[ERROR] The case when this is false is not handled: +(==(x,)(y,),)(7,) -//│ ║ l.51: if x == -//│ ║ ^^^^ -//│ ║ l.52: y + + 6 + 7 then 0 +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.52: if x == y * +//│ ║ ^ +//│ ║ l.53: 5 then 0 +//│ ║ ^^^^^ +//│ ╟── operator application of type `Bool` is not an instance of type `Int` +//│ ║ l.52: if x == y * +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.52: if x == y * +//│ ║ ^ +//│ ║ l.53: 5 then 0 //│ ║ ^^^^^ +//│ ╙── application of type `Int` is not an instance of type `Bool` +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.52: if x == y * +//│ ║ ^ //│ ║ l.53: 5 then 0 -//│ ║ ^^^^^^^^^^^^ -//│ ║ l.54: 7 then 0 -//│ ╙── ^^^^^ -//│ res: error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ║ ^^^^^^^^^^^^^ +//│ ║ l.54: 6 + 7 then 0 +//│ ║ ^^^^^^^^^ +//│ ╟── operator application of type `Bool` is not an instance of type `Int` +//│ ║ l.52: if x == y * +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.52: if x == y * +//│ ║ ^ +//│ ║ l.53: 5 then 0 +//│ ║ ^^^^^^^^^^^^^ +//│ ║ l.54: 6 + 7 then 0 +//│ ║ ^^^^^^^^^ +//│ ╙── application of type `Int` is not an instance of type `Bool` +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.54: 6 + 7 then 0 +//│ ║ ^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ fun f: (Num, Num) -> 0 :e -:ge -if x == - 1 and b then 0 -//│ ╔══[ERROR] The case when this is false is not handled: ==(x,)(1,) -//│ ║ l.70: if x == -//│ ║ ^^^^ -//│ ║ l.71: 1 and b then 0 -//│ ╙── ^^^^ -//│ res: error -//│ Code generation encountered an error: -//│ if expression was not desugared +fun f(x, y) = + if x == + y + + 5 then 0 + 7 then 0 +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.96: y + +//│ ║ ^ +//│ ║ l.97: 5 then 0 +//│ ║ ^^^^^^^ +//│ ╟── operator application of type `Bool` is not an instance of type `Int` +//│ ║ l.95: if x == +//│ ║ ^^^^ +//│ ║ l.96: y + +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.96: y + +//│ ║ ^ +//│ ║ l.97: 5 then 0 +//│ ║ ^^^^^^^ +//│ ╙── application of type `Int` is not an instance of type `Bool` +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.96: y + +//│ ║ ^ +//│ ║ l.97: 5 then 0 +//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.98: 7 then 0 +//│ ║ ^^^^^^^ +//│ ╙── application of type `Int` is not an instance of type `Bool` +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.98: 7 then 0 +//│ ║ ^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ fun f: (Num, Num) -> 0 + +:e +fun f(x, b) = + if x == + 1 and b then 0 +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.132: 1 and b then 0 +//│ ║ ^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.132: 1 and b then 0 +//│ ║ ^^^^^^^^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ fun f: (Num, Bool) -> 0 :e -:ge fun toEnglish(x) = if x == true then "t" 0 then "z" -//│ ╔══[ERROR] The case when this is false is not handled: ==(x,)(0,) -//│ ║ l.85: if x == -//│ ║ ^^^^ -//│ ║ l.86: true then "t" -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.87: 0 then "z" -//│ ╙── ^^^^^^ -//│ toEnglish: anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.146: if x == +//│ ║ ^^^^ +//│ ║ l.147: true then "t" +//│ ║ ^^^^^^^^ +//│ ╟── reference of type `true` is not an instance of `Num` +//│ ║ l.147: true then "t" +//│ ╙── ^^^^ +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.148: 0 then "z" +//│ ║ ^^^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ fun toEnglish: Num -> ("t" | "z") :e -:ge fun toEnglish(x) = if x == 0 then "z" true then "t" -//│ ╔══[ERROR] The case when this is false is not handled: ==(x,)(true,) -//│ ║ l.102: if x == +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.165: if x == //│ ║ ^^^^ -//│ ║ l.103: 0 then "z" +//│ ║ l.166: 0 then "z" //│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.104: true then "t" -//│ ╙── ^^^^^^^^ -//│ toEnglish: anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ║ l.167: true then "t" +//│ ║ ^^^^^^^^ +//│ ╟── reference of type `true` is not an instance of `Num` +//│ ║ l.167: true then "t" +//│ ╙── ^^^^ +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.167: true then "t" +//│ ║ ^^^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ fun toEnglish: Num -> ("t" | "z") :e -:ge fun toEnglish(x) = if x == 1 then "o" 0 then "z" -//│ ╔══[ERROR] The case when this is false is not handled: ==(x,)(0,) -//│ ║ l.119: if x == -//│ ║ ^^^^ -//│ ║ l.120: 1 then "o" -//│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.121: 0 then "z" -//│ ╙── ^^^^^^ -//│ toEnglish: anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.188: 0 then "z" +//│ ║ ^^^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ fun toEnglish: Num -> ("o" | "z") fun toEnglish(x) = if x == 0 then 1 else 1 -//│ toEnglish: number -> 1 -//│ = [Function: toEnglish3] +//│ fun toEnglish: Num -> 1 :pe :e -:ge fun toEnglish(x) = if x == else 1 //│ ╔══[PARSE ERROR] Unexpected indented block in expression position -//│ ║ l.145: else 1 +//│ ║ l.205: else 1 //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here -//│ ║ l.145: else 1 +//│ ║ l.205: else 1 //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead -//│ ║ l.144: if x == +//│ ║ l.204: if x == //│ ║ ^^^^ -//│ ║ l.145: else 1 +//│ ║ l.205: else 1 //│ ║ ^^^^ //│ ╟── Note: 'if' expression starts here: -//│ ║ l.144: if x == +//│ ║ l.204: if x == //│ ╙── ^^ -//│ ╔══[ERROR] The case when this is false is not handled: ==(x,)(undefined,) -//│ ║ l.144: if x == +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.204: if x == //│ ║ ^^^^ -//│ ║ l.145: else 1 -//│ ╙── ^^^^ -//│ toEnglish: anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ║ l.205: else 1 +//│ ║ ^^^^ +//│ ╟── undefined literal of type `()` is not an instance of type `Num` +//│ ║ l.205: else 1 +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.205: else 1 +//│ ║ ^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ fun toEnglish: Num -> () diff --git a/shared/src/test/diff/ucs/SplitAnd.mls b/shared/src/test/diff/ucs/SplitAnd.mls index 3a3c4705..f615affb 100644 --- a/shared/src/test/diff/ucs/SplitAnd.mls +++ b/shared/src/test/diff/ucs/SplitAnd.mls @@ -1,24 +1,19 @@ -:NewParser +:PreTyper fun f(x, y) = if x == 0 and y == 0 then "bruh" y == 1 then "lol" else "okay" -//│ f: (number, number,) -> ("bruh" | "lol" | "okay") -//│ = [Function: f] +//│ fun f: (Num, Num) -> ("bruh" | "lol" | "okay") class A() class B() -//│ Defined class A -//│ Defined class B -//│ A: () -> A -//│ = [Function: A1] -//│ B: () -> B -//│ = [Function: B1] +//│ class A() +//│ class B() :e -:ge +// TODO: Should report missing else branches. fun f(x) = if x == 0 and x is @@ -26,23 +21,31 @@ fun f(x) = B() then "B" x == 0 then "lol" else "bruh" -//│ ╔══[ERROR] The case when this is false is not handled: ==(x,)(0,) -//│ ║ l.23: if x == 0 and -//│ ╙── ^^^^^^ -//│ f: anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.19: x is +//│ ║ ^^^^ +//│ ║ l.20: A() then "A" +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.21: B() then "B" +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.22: x == 0 then "lol" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.23: else "bruh" +//│ ║ ^^^^^^^^^^^^^^^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ fun f: Num -> ("A" | "B" | "bruh" | "lol") :e -:ge +// TODO: Should report missing else branches. fun f(x, y) = if x == 0 and y == 0 then "bruh" else "lol" -//│ ╔══[ERROR] The case when this is false is not handled: ==(x,)(0,) -//│ ║ l.40: x == 0 and -//│ ╙── ^^^^^^ -//│ f: (anything, anything,) -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.43: y == 0 then "bruh" +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.44: else "lol" +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ fun f: (Num, Num) -> ("bruh" | "lol") diff --git a/shared/src/test/diff/ucs/SplitAroundOp.mls b/shared/src/test/diff/ucs/SplitAroundOp.mls index 1cbf9e1d..ddfeb3af 100644 --- a/shared/src/test/diff/ucs/SplitAroundOp.mls +++ b/shared/src/test/diff/ucs/SplitAroundOp.mls @@ -1,5 +1,4 @@ -:NewParser -:NewDefs +:PreTyper fun f(x, b) = if x @@ -12,7 +11,7 @@ fun f(x, b) = "1" then "s1" "2" then "s2" else ":p" -//│ fun f: (Eql["0" | "1" | "2" | 0 | 1 | 2], Object & ~true | true) -> (":p" | "n0" | "n1" | "n2" | "s0" | "s1" | "s2") +//│ fun f: (Eql["0" | "1" | "2" | 0 | 1 | 2], Bool) -> (":p" | "n0" | "n1" | "n2" | "s0" | "s1" | "s2") fun f(x, y, a, b) = if x === 0 @@ -41,9 +40,35 @@ if x is === 0 then 0 > 0 then 1 < 0 then 2 -//│ ╔══[ERROR] Illegal pattern `===` -//│ ║ l.41: === 0 then 0 -//│ ╙── ^^^ -//│ error +//│ ╔══[ERROR] cannot transform due to an illegal split operator === +//│ ║ l.40: === 0 then 0 +//│ ║ ^^^ +//│ ╟── the following branch will be discarded +//│ ║ l.40: === 0 then 0 +//│ ╙── ^^^^^^^^ +//│ ╔══[ERROR] cannot transform due to an illegal split operator > +//│ ║ l.41: > 0 then 1 +//│ ║ ^ +//│ ╟── the following branch will be discarded +//│ ║ l.41: > 0 then 1 +//│ ╙── ^^^^^^^^ +//│ ╔══[ERROR] cannot transform due to an illegal split operator < +//│ ║ l.42: < 0 then 2 +//│ ║ ^ +//│ ╟── the following branch will be discarded +//│ ║ l.42: < 0 then 2 +//│ ╙── ^^^^^^^^ +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.38: if x is +//│ ╙── ^ +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.38: if x is +//│ ╙── ^ +//│ ╔══[ERROR] Found unexpected empty split +//│ ╙── +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.38: if x is +//│ ╙── ^ +//│ nothing //│ Code generation encountered an error: -//│ if expression was not desugared +//│ unresolved symbol x diff --git a/shared/src/test/diff/ucs/SplitBeforeOp.mls b/shared/src/test/diff/ucs/SplitBeforeOp.mls index 6f070687..0c6cc6da 100644 --- a/shared/src/test/diff/ucs/SplitBeforeOp.mls +++ b/shared/src/test/diff/ucs/SplitBeforeOp.mls @@ -1,29 +1,49 @@ -:NewDefs +:PreTyper :e :ge if x == 0 then 0 -//│ ╔══[ERROR] The case when this is false is not handled: ==(x, 0,) +//│ ╔══[ERROR] identifier `x` not found //│ ║ l.5: if x -//│ ║ ^ +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.5: if x +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.6: == 0 then 0 -//│ ╙── ^^^^^^ -//│ error +//│ ║ ^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ 0 //│ Code generation encountered an error: -//│ if expression was not desugared +//│ unresolved symbol x :e :ge if x is A and y then 0 -//│ ╔══[ERROR] Cannot find constructor `A` in scope -//│ ║ l.19: is A and +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.23: if x +//│ ╙── ^ +//│ ╔══[ERROR] type identifier `A` not found +//│ ║ l.24: is A and +//│ ╙── ^ +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.23: if x +//│ ╙── ^ +//│ ╔══[ERROR] identifier `y` not found +//│ ║ l.25: y then 0 +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.23: if x +//│ ╙── ^ +//│ ╔══[ERROR] type identifier not found: A +//│ ║ l.24: is A and //│ ╙── ^ //│ error //│ Code generation encountered an error: -//│ if expression was not desugared +//│ unresolved symbol x :e :ge @@ -31,12 +51,27 @@ if x is A and y then 0 else 1 -//│ ╔══[ERROR] Cannot find constructor `A` in scope -//│ ║ l.31: is A and +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.50: if x +//│ ╙── ^ +//│ ╔══[ERROR] type identifier `A` not found +//│ ║ l.51: is A and +//│ ╙── ^ +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.50: if x +//│ ╙── ^ +//│ ╔══[ERROR] identifier `y` not found +//│ ║ l.52: y then 0 +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.50: if x +//│ ╙── ^ +//│ ╔══[ERROR] type identifier not found: A +//│ ║ l.51: is A and //│ ╙── ^ //│ error //│ Code generation encountered an error: -//│ if expression was not desugared +//│ unresolved symbol x :e :ge @@ -45,9 +80,33 @@ if x is A() then "A" B() then "B" -//│ ╔══[ERROR] Illegal pattern `A` -//│ ║ l.46: A() then "A" +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.78: if x +//│ ╙── ^ +//│ ╔══[ERROR] type identifier `A` not found +//│ ║ l.81: A() then "A" //│ ╙── ^ -//│ error +//│ ╔══[ERROR] type identifier `B` not found +//│ ║ l.82: B() then "B" +//│ ╙── ^ +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.78: if x +//│ ╙── ^ +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.78: if x +//│ ╙── ^ +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.78: if x +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.78: if x +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.78: if x +//│ ╙── ^ +//│ ╔══[ERROR] type identifier not found: A +//│ ║ l.81: A() then "A" +//│ ╙── ^ +//│ 0 | error //│ Code generation encountered an error: -//│ if expression was not desugared +//│ unresolved symbol x diff --git a/shared/src/test/diff/ucs/SplitOps.mls b/shared/src/test/diff/ucs/SplitOps.mls index c966e4b0..c346c509 100644 --- a/shared/src/test/diff/ucs/SplitOps.mls +++ b/shared/src/test/diff/ucs/SplitOps.mls @@ -1,30 +1,18 @@ -:NewParser +:PreTyper -class Option -class Some(value): Option -class None: Option -//│ Defined class Option -//│ Defined class Some -//│ Defined class None -//│ Option: () -> Option -//│ = [Function: Option1] -//│ Some: 'value -> (Some & {value: 'value}) -//│ = [Function: Some1] -//│ None: () -> None -//│ = [Function: None1] +abstract class Option[A]: Some[A] | None +class Some[A](value: A) extends Option[A] +module None extends Option +//│ abstract class Option[A]: None | Some[A] +//│ class Some[A](value: A) extends Option +//│ module None extends Option -class Either -class Left(leftValue): Either -class Right(rightValue): Either -//│ Defined class Either -//│ Defined class Left -//│ Defined class Right -//│ Either: () -> Either -//│ = [Function: Either1] -//│ Left: 'leftValue -> (Left & {leftValue: 'leftValue}) -//│ = [Function: Left1] -//│ Right: 'rightValue -> (Right & {rightValue: 'rightValue}) -//│ = [Function: Right1] +abstract class Either[out A, out B]: Left[A] | Right[B] +class Left[A](leftValue: A) extends Either[A, nothing] +class Right[B](rightValue: B) extends Either[nothing, B] +//│ abstract class Either[A, B]: Left[A] | Right[B] +//│ class Left[A](leftValue: A) extends Either +//│ class Right[B](rightValue: B) extends Either :e :ge @@ -33,18 +21,25 @@ fun f(x) = is Left(v) then 0 is Right(v) then 1 <> undefined then 2 -//│ ╔══[ERROR] The case when this is false is not handled: <>(x,)(undefined,) -//│ ║ l.32: if x +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.20: if x //│ ║ ^ -//│ ║ l.33: is Left(v) then 0 +//│ ║ l.21: is Left(v) then 0 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.34: is Right(v) then 1 +//│ ║ l.22: is Right(v) then 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.35: <> undefined then 2 -//│ ╙── ^^^^^^^^^^^^^^^^ -//│ f: anything -> error +//│ ║ l.23: <> undefined then 2 +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ╟── undefined literal of type `()` is not an instance of type `Num` +//│ ║ l.23: <> undefined then 2 +//│ ╙── ^^^^^^^^^ +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.23: <> undefined then 2 +//│ ║ ^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ fun f: (Left[anything] | Num | Right[anything]) -> (0 | 1 | 2) //│ Code generation encountered an error: -//│ if expression was not desugared +//│ unresolved symbol <> :e :ge @@ -52,66 +47,59 @@ fun f(x) = if x is Some(xv) and y is Some(yv) then xv + yv is None() and y is None() then 0 -//│ ╔══[ERROR] The match is not exhaustive. -//│ ║ l.53: is Some(xv) and y is Some(yv) then xv + yv -//│ ║ ^^^^^^^^^^^^^ -//│ ╟── The scrutinee at this position misses 1 case. -//│ ║ l.53: is Some(xv) and y is Some(yv) then xv + yv -//│ ║ ^ -//│ ╟── [Missing Case 1/1] `None` -//│ ╟── It first appears here. -//│ ║ l.54: is None() and y is None() then 0 -//│ ╙── ^^^^^^ -//│ f: anything -> error +//│ ╔══[ERROR] identifier `y` not found +//│ ║ l.48: is Some(xv) and y is Some(yv) then xv + yv +//│ ╙── ^ +//│ ╔══[ERROR] identifier `y` not found +//│ ║ l.49: is None() and y is None() then 0 +//│ ╙── ^ +//│ ╔══[ERROR] identifier `y` not found +//│ ║ l.48: is Some(xv) and y is Some(yv) then xv + yv +//│ ╙── ^ +//│ ╔══[ERROR] identifier `y` not found +//│ ║ l.48: is Some(xv) and y is Some(yv) then xv + yv +//│ ╙── ^ +//│ ╔══[ERROR] identifier `y` not found +//│ ║ l.49: is None() and y is None() then 0 +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: y +//│ ║ l.48: is Some(xv) and y is Some(yv) then xv + yv +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: y +//│ ║ l.49: is None() and y is None() then 0 +//│ ╙── ^ +//│ fun f: (None | Some[Int]) -> Int //│ Code generation encountered an error: -//│ if expression was not desugared +//│ unresolved symbol y class A() class B() -//│ Defined class A -//│ Defined class B -//│ A: () -> A -//│ = [Function: A1] -//│ B: () -> B -//│ = [Function: B1] +//│ class A() +//│ class B() fun f(a, b) = if a is A() and b is B() then 0 -//│ f: (A, B,) -> 0 -//│ = [Function: f2] +//│ fun f: (A, B) -> 0 class C() -//│ Defined class C -//│ C: () -> C -//│ = [Function: C1] +//│ class C() -:p :e -:ge fun f(a, b, c) = if a == 0 and b is B() and c is C() then 0 -//│ |#fun| |f|(|a|,| |b|,| |c|)| |#=|→|#if| |a|→|==| |0| |and| |b| |is| |B|(||)| |and| |c| |is| |C|(||)| |#then| |0|←|←| -//│ AST: TypingUnit(List(NuFunDef(None,Var(f),None,List(),Left(Lam(Tup(List((None,Fld(_,Var(a))), (None,Fld(_,Var(b))), (None,Fld(_,Var(c))))),Blk(List(If(IfOpsApp(Var(a),List((Var(==),IfThen(App(App(Var(and),Tup(List((None,Fld(_,App(App(Var(and),Tup(List((None,Fld(_,IntLit(0)))))),Tup(List((None,Fld(_,App(App(Var(is),Tup(List((None,Fld(_,Var(b)))))),Tup(List((None,Fld(_,App(Var(B),Tup(List()))))))))))))))))),Tup(List((None,Fld(_,App(App(Var(is),Tup(List((None,Fld(_,Var(c)))))),Tup(List((None,Fld(_,App(Var(C),Tup(List())))))))))))),IntLit(0))))),None)))))))) -//│ Parsed: fun f = (a, b, c,) => {if a ‹· == (and(and(0,)(is(b,)(B(),),),)(is(c,)(C(),),)) then 0›}; -//│ Desugared: rec def f: (a, b, c,) => {if a ‹· == (and(and(0,)(is(b,)(B(),),),)(is(c,)(C(),),)) then 0›} -//│ AST: Def(true,Var(f),Left(Lam(Tup(List((None,Fld(_,Var(a))), (None,Fld(_,Var(b))), (None,Fld(_,Var(c))))),Blk(List(If(IfOpsApp(Var(a),List((Var(==),IfThen(App(App(Var(and),Tup(List((None,Fld(_,App(App(Var(and),Tup(List((None,Fld(_,IntLit(0)))))),Tup(List((None,Fld(_,App(App(Var(is),Tup(List((None,Fld(_,Var(b)))))),Tup(List((None,Fld(_,App(Var(B),Tup(List()))))))))))))))))),Tup(List((None,Fld(_,App(App(Var(is),Tup(List((None,Fld(_,Var(c)))))),Tup(List((None,Fld(_,App(Var(C),Tup(List())))))))))))),IntLit(0))))),None))))),true) -//│ ╔══[ERROR] The case when this is false is not handled: ==(a,)(0,) -//│ ║ l.93: if a -//│ ║ ^ -//│ ║ l.94: == 0 and b is B() and c is C() then 0 -//│ ╙── ^^^^^^^^ -//│ f: (anything, anything, anything,) -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.91: == 0 and b is B() and c is C() then 0 +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ fun f: (Num, B, C) -> 0 fun f(x) = if x is A() then "A" is B() then "B" -//│ f: (A | B) -> ("A" | "B") -//│ = [Function: f4] +//│ fun f: (A | B) -> ("A" | "B") fun sumOpt(x, y) = if x @@ -121,12 +109,10 @@ fun sumOpt(x, y) = is None() and y is Some(yv) then yv None() then 0 -//│ sumOpt: (None | Some & {value: int}, None | Some & {value: int},) -> int -//│ = [Function: sumOpt] +//│ fun sumOpt: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) fun f(x, y, z) = if x is A() and y == z then 1 is B() then 0 -//│ f: (A, nothing, number,) -> (0 | 1) -//│ = [Function: f5] +//│ fun f: (A, nothing, Num) -> (0 | 1) diff --git a/shared/src/test/diff/ucs/SplitScrutinee.mls b/shared/src/test/diff/ucs/SplitScrutinee.mls index 77d06ac9..0ecc291f 100644 --- a/shared/src/test/diff/ucs/SplitScrutinee.mls +++ b/shared/src/test/diff/ucs/SplitScrutinee.mls @@ -1,5 +1,4 @@ -:NewParser -:NoJS +:PreTyper fun f(x) = if x + @@ -7,4 +6,9 @@ fun f(x) = 2 then 1 3 then 2 _ then "I don't know." -//│ f: int -> ("I don't know." | 1 | 2) +//│ fun f: Int -> ("I don't know." | 1 | 2) + +[f(0), f(1), f(2), f(3)] +//│ ["I don't know." | 1 | 2, "I don't know." | 1 | 2, "I don't know." | 1 | 2, "I don't know." | 1 | 2] +//│ res +//│ = [ "I don't know.", 1, 2, "I don't know." ] diff --git a/shared/src/test/diff/ucs/ThenIndent.mls b/shared/src/test/diff/ucs/ThenIndent.mls index 1ca90def..df126db8 100644 --- a/shared/src/test/diff/ucs/ThenIndent.mls +++ b/shared/src/test/diff/ucs/ThenIndent.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper // FIXME @@ -19,13 +19,12 @@ x => if x == //│ ╟── Note: 'if' expression starts here: //│ ║ l.5: x => if x == //│ ╙── ^^ -//│ ╔══[ERROR] The case when this is false is not handled: ==(x, {0},) -//│ ║ l.5: x => if x == -//│ ║ ^^^^ +//│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.6: 0 -//│ ╙── ^^^ -//│ anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ║ ^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ Num -> () +//│ res +//│ = [Function: res] diff --git a/shared/src/test/diff/ucs/Tree.mls b/shared/src/test/diff/ucs/Tree.mls index 28026141..a36c7490 100644 --- a/shared/src/test/diff/ucs/Tree.mls +++ b/shared/src/test/diff/ucs/Tree.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper type Option[out A] = Some[A] | None class Some[out A](value: A) diff --git a/shared/src/test/diff/ucs/TrivialIf.mls b/shared/src/test/diff/ucs/TrivialIf.mls index 0fa422ee..d8cc5fcc 100644 --- a/shared/src/test/diff/ucs/TrivialIf.mls +++ b/shared/src/test/diff/ucs/TrivialIf.mls @@ -2,6 +2,10 @@ :NoJS fun abs(x) = if x < 0 then 0 - x else x +//│ ╔══[WARNING] old desugarer used +//│ ║ l.4: fun abs(x) = if x < 0 then 0 - x else x +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ abs: int -> int class Option @@ -18,6 +22,14 @@ fun getOrElse(opt, default) = if opt is Some(value) then value None then default +//│ ╔══[WARNING] old desugarer used +//│ ║ l.22: if opt is +//│ ║ ^^^^^^ +//│ ║ l.23: Some(value) then value +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.24: None then default +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ getOrElse: (None | Some & {value: 'value}, 'value,) -> 'value getOrElse(None(), 0) @@ -30,6 +42,14 @@ fun map(v, f) = if v is Some(x) then Some(f(x)) None then None() +//│ ╔══[WARNING] old desugarer used +//│ ║ l.42: if v is +//│ ║ ^^^^ +//│ ║ l.43: Some(x) then Some(f(x)) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.44: None then None() +//│ ╙── ^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ map: (None | Some & {value: 'value}, 'value -> 'value0,) -> (None | Some & {value: 'value0}) fun inc(x) = x + 5 @@ -43,8 +63,12 @@ map(None(), inc) :e fun f(a, b) = if a and b then 0 +//│ ╔══[WARNING] old desugarer used +//│ ║ l.65: fun f(a, b) = if a and b then 0 +//│ ╙── ^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ ╔══[ERROR] The case when this is false is not handled: a -//│ ║ l.45: fun f(a, b) = if a and b then 0 +//│ ║ l.65: fun f(a, b) = if a and b then 0 //│ ╙── ^ //│ f: (anything, anything,) -> error @@ -52,8 +76,14 @@ fun f(a, b) = if a and b then 0 fun f(x, y) = if x == y + 5 then 0 else if x == y + 7 then 0 +//│ ╔══[WARNING] old desugarer used +//│ ║ l.77: if x == y + 5 then 0 +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.78: else if x == y + 7 then 0 +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ ╔══[ERROR] The case when this is false is not handled: ==(x,)(+(y,)(7,),) -//│ ║ l.54: else if x == y + 7 then 0 +//│ ║ l.78: else if x == y + 7 then 0 //│ ╙── ^^^^^^^^^^ //│ f: (anything, anything,) -> error @@ -62,12 +92,17 @@ fun foo(x) = if x is Some (0) then 0 (1) then 1 //│ ╔══[PARSE ERROR] Unexpected parenthesis section here -//│ ║ l.63: (1) then 1 +//│ ║ l.93: (1) then 1 //│ ╙── ^^^ +//│ ╔══[WARNING] old desugarer used +//│ ║ l.91: fun foo(x) = if x is Some +//│ ║ ^^^^^^^^^ +//│ ║ l.92: (0) then 0 +//│ ╙── ^^^^^^^^^^^^ //│ ╔══[ERROR] The case when this is false is not handled: is(x,)(Some,)(0,) -//│ ║ l.61: fun foo(x) = if x is Some +//│ ║ l.91: fun foo(x) = if x is Some //│ ║ ^^^^^^^^^ -//│ ║ l.62: (0) then 0 +//│ ║ l.92: (0) then 0 //│ ╙── ^^^^^ //│ foo: anything -> error @@ -76,15 +111,20 @@ fun foo(x) = if x is Some of 0 then 0 1 then 1 //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here -//│ ║ l.76: 0 then 0 -//│ ╙── ^^^^ +//│ ║ l.111: 0 then 0 +//│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead -//│ ║ l.75: fun foo(x) = if x is Some of -//│ ║ ^^^^^^^^^^^^ -//│ ║ l.76: 0 then 0 -//│ ║ ^^^ +//│ ║ l.110: fun foo(x) = if x is Some of +//│ ║ ^^^^^^^^^^^^ +//│ ║ l.111: 0 then 0 +//│ ║ ^^^ //│ ╟── Note: 'if' expression starts here: -//│ ║ l.75: fun foo(x) = if x is Some of -//│ ╙── ^^ +//│ ║ l.110: fun foo(x) = if x is Some of +//│ ╙── ^^ +//│ ╔══[WARNING] old desugarer used +//│ ║ l.110: fun foo(x) = if x is Some of +//│ ║ ^^^^^^^^^^^^ +//│ ║ l.111: 0 then 0 +//│ ╙── ^^^ //│ foo: (Some & {value: 0}) -> undefined diff --git a/shared/src/test/diff/ucs/WeirdIf.mls b/shared/src/test/diff/ucs/WeirdIf.mls index db0705fd..c420f554 100644 --- a/shared/src/test/diff/ucs/WeirdIf.mls +++ b/shared/src/test/diff/ucs/WeirdIf.mls @@ -1,96 +1,66 @@ -:NewDefs +:PreTyper -// Should report duplicated else branches. -:w +// FIXME: Should report duplicated else branches. +// :w if _ then 0 else 0 else 1 -//│ ╔══[WARNING] Found a duplicated branch -//│ ╟── This branch -//│ ║ l.7: else 0 -//│ ║ ^ -//│ ╟── is subsumed by the branch here. -//│ ║ l.6: _ then 0 -//│ ╙── ^ -//│ ╔══[WARNING] Found a duplicated branch -//│ ╟── This branch -//│ ║ l.8: else 1 -//│ ║ ^ -//│ ╟── is subsumed by the branch here. -//│ ║ l.6: _ then 0 -//│ ╙── ^ //│ 0 //│ res //│ = 0 -:w +// FIXME +// :w if else 0 else 1 -//│ ╔══[WARNING] Found a duplicated branch -//│ ╟── This branch -//│ ║ l.28: if else 0 else 1 -//│ ║ ^ -//│ ╟── is subsumed by the branch here. -//│ ║ l.28: if else 0 else 1 -//│ ╙── ^ //│ 0 //│ res //│ = 0 -:w +// FIXME +// :w fun f(x) = if x is else 0 else 1 -//│ ╔══[WARNING] Found a duplicated branch -//│ ╟── This branch -//│ ║ l.41: fun f(x) = if x is else 0 else 1 -//│ ║ ^ -//│ ╟── is subsumed by the branch here. -//│ ║ l.41: fun f(x) = if x is else 0 else 1 -//│ ╙── ^ //│ fun f: anything -> 0 fun f(x) = if x is else 0 //│ fun f: anything -> 0 :e -:ge if true then 0 -//│ ╔══[ERROR] The case when this is false is not handled: true -//│ ║ l.56: if true -//│ ╙── ^^^^ -//│ error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.30: then 0 +//│ ║ ^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ 0 +//│ res +//│ = 0 // This cannot be parsed. But the next one works. :pe :e -:ge fun f(x) = if x === else "bruh" //│ ╔══[PARSE ERROR] Unexpected indented block in expression position -//│ ║ l.71: else "bruh" +//│ ║ l.44: else "bruh" //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here -//│ ║ l.71: else "bruh" +//│ ║ l.44: else "bruh" //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead -//│ ║ l.70: if x === +//│ ║ l.43: if x === //│ ║ ^^^^^ -//│ ║ l.71: else "bruh" +//│ ║ l.44: else "bruh" //│ ║ ^^^^ //│ ╟── Note: 'if' expression starts here: -//│ ║ l.70: if x === +//│ ║ l.43: if x === //│ ╙── ^^ -//│ ╔══[ERROR] The case when this is false is not handled: ===(x, undefined,) -//│ ║ l.70: if x === -//│ ║ ^^^^^ -//│ ║ l.71: else "bruh" -//│ ╙── ^^^^ -//│ fun f: anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.44: else "bruh" +//│ ║ ^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ fun f: Eql[()] -> () // But this works. fun f(x) = @@ -102,6 +72,8 @@ fun boolToStr(x) = if x is true then "yah" false then "nah" +//│ ╙── +//│ ╙── //│ fun boolToStr: Bool -> ("nah" | "yah") boolToStr of true diff --git a/shared/src/test/diff/ucs/WeirdSplit.mls b/shared/src/test/diff/ucs/WeirdSplit.mls index a41d99f6..e74445a1 100644 --- a/shared/src/test/diff/ucs/WeirdSplit.mls +++ b/shared/src/test/diff/ucs/WeirdSplit.mls @@ -14,6 +14,16 @@ fun f(x) = is A then 0 B then 1 +//│ ╔══[WARNING] old desugarer used +//│ ║ l.13: if x +//│ ║ ^ +//│ ║ l.14: is +//│ ║ ^^^^^^ +//│ ║ l.15: A then 0 +//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.16: B then 1 +//│ ╙── ^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ f: (A | B) -> (0 | 1) //│ = [Function: f] @@ -24,17 +34,27 @@ fun f(x) = 1 + 2 then 0 + _ then 1 +//│ ╔══[WARNING] old desugarer used +//│ ║ l.33: if x == +//│ ║ ^^^^ +//│ ║ l.34: 1 +//│ ║ ^^^^^^ +//│ ║ l.35: + 2 then 0 +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ║ l.36: + _ then 1 +//│ ╙── ^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ ╔══[ERROR] Type mismatch in operator application: -//│ ║ l.23: if x == +//│ ║ l.33: if x == //│ ║ ^^^^ -//│ ║ l.24: 1 +//│ ║ l.34: 1 //│ ║ ^^^^^^ -//│ ║ l.25: + 2 then 0 +//│ ║ l.35: + 2 then 0 //│ ║ ^^^^^^^ //│ ╟── operator application of type `bool` is not an instance of type `int` -//│ ║ l.23: if x == +//│ ║ l.33: if x == //│ ║ ^^^^ -//│ ║ l.24: 1 +//│ ║ l.34: 1 //│ ╙── ^^^^^^ //│ f: number -> (0 | 1) //│ = [Function: f1] @@ -45,5 +65,17 @@ fun f(x, s, t) = and t then 0 and s then 0 is _ then 1 +//│ ╔══[WARNING] old desugarer used +//│ ║ l.63: if x +//│ ║ ^ +//│ ║ l.64: is A() +//│ ║ ^^^^^^^^^^ +//│ ║ l.65: and t then 0 +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.66: and s then 0 +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.67: is _ then 1 +//│ ╙── ^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected warning //│ f: (anything, anything, anything,) -> (0 | 1) //│ = [Function: f2] diff --git a/shared/src/test/diff/ucs/Wildcard.mls b/shared/src/test/diff/ucs/Wildcard.mls index 51cdbda6..17d9db40 100644 --- a/shared/src/test/diff/ucs/Wildcard.mls +++ b/shared/src/test/diff/ucs/Wildcard.mls @@ -1,5 +1,4 @@ -:NewParser -:NewDefs +:PreTyper type Option[T] = None | Some[T] module None @@ -15,6 +14,7 @@ class Right[B](val rightValue: B) //│ class Left[A](leftValue: A) //│ class Right[B](rightValue: B) +// FIXME fun w1(x, e_0, e_1) = if x is Left(None) then "Left of None" @@ -23,13 +23,41 @@ fun w1(x, e_0, e_1) = Left(Some(lv)) then concat("Left of Some of ")(toString(lv)) _ and e_1 is y_1 and x is Right(Some(rv)) then concat("Right of Some of ")(toString(rv)) -//│ fun w1: (Left[None | Some[anything]] | Right[None | Some[anything]], anything, anything) -> Str +//│ ╔══[ERROR] Found unexpected empty split +//│ ╙── +//│ ╔══[ERROR] Found unexpected empty split +//│ ╙── +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.23: Left(Some(lv)) then concat("Left of Some of ")(toString(lv)) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.24: _ and e_1 is y_1 and x is +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.25: Right(Some(rv)) then concat("Right of Some of ")(toString(rv)) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.24: _ and e_1 is y_1 and x is +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ║ l.25: Right(Some(rv)) then concat("Right of Some of ")(toString(rv)) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.24: _ and e_1 is y_1 and x is +//│ ║ ^^^^^^^^^^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ fun w1: (Left[None | Object & ~#None & ~#Some | Some[anything]] | Object & ~#Left & ~#Right | Right[None | Some[anything]], anything, anything) -> (Str | error) w1(Left(None), "a", "b") w1(Right(None), "a", "b") w1(Left(Some(0)), "a", "b") w1(Right(Some(0)), "a", "b") -//│ Str +//│ Str | error //│ res //│ = 'Left of None' //│ res @@ -45,7 +73,7 @@ fun w2(x, p) = _ and p(x) then 2 None then 3 _ then 4 -//│ fun w2: forall 'a. (None | Object & 'a & ~#None & ~#Some | Some[anything], (None | 'a) -> Object) -> (1 | 2 | 3 | 4) +//│ fun w2: forall 'a. (Object & 'a & ~#Some | Some[anything], 'a -> Bool) -> (1 | 2 | 3 | 4) w2(Some(0), x => true) w2(None, x => true) @@ -66,7 +94,7 @@ fun w3(x, p) = if x is Some(xv) then concat("r2: ")(toString(xv)) None then "r3" _ then "r4" -//│ fun w3: forall 'a. (None | Object & 'a & ~#None & ~#Some | Some[anything], (None | Some[nothing] | 'a) -> Object) -> Str +//│ fun w3: forall 'a. ('a & (Object & ~#Some | Some[anything]), 'a -> Bool) -> Str // Expect "r1" w3(0, _ => true) @@ -98,15 +126,13 @@ w3(0, _ => false) //│ res //│ = 'r4' -:w +// :w +// FIXME: Should warn this. // Decision paths: // + «tmp2 @ f (x,) is any => 0 // + => 1 fun w3_1(x, f) = if f(x) is _ then 0 else 1 -//│ ╔══[WARNING] Found a redundant else branch -//│ ║ l.106: if f(x) is _ then 0 else 1 -//│ ╙── ^ //│ fun w3_1: forall 'a. ('a, 'a -> anything) -> 0 w3_1(0, _ => true) @@ -117,12 +143,10 @@ w3_1(0, _ => false) //│ res //│ = 0 -:w +// :w +// FIXME: Should warn redundant case fun w3_1_1(x, f) = if f(x) is a then a else 0 -//│ ╔══[WARNING] Found a redundant else branch -//│ ║ l.122: if f(x) is a then a else 0 -//│ ╙── ^ //│ fun w3_1_1: forall 'a 'b. ('a, 'a -> 'b) -> 'b w3_1_1(0, x => x) @@ -142,7 +166,7 @@ fun w4(x, p) = if x is Some(xv) then concat("r2: ")(toString(xv)) None then "r3" _ then "r4" -//│ fun w4: forall 'a. (None | Object & 'a & ~#None & ~#Some | Some[anything], (None | Some[nothing] | 'a) -> Object) -> Str +//│ fun w4: forall 'a. ('a & (Object & ~#Some | Some[anything]), 'a -> Bool) -> Str // Expect "r1" @@ -196,7 +220,7 @@ fun w5(y) = _ and y is Delta then "delta" _ then "unknown" -//│ fun w5: Object -> ("alpha" | "beta" | "delta" | "gamma" | "unknown") +//│ fun w5: (Alpha | Beta | Delta | Gamma | Object & ~#Alpha & ~#Beta & ~#Delta & ~#Gamma) -> ("alpha" | "beta" | "delta" | "gamma" | "unknown") w5(0) w5(Alpha()) @@ -243,19 +267,22 @@ fun w7(x, f) = None then x Left(x) then x + 1 Right(x) then x + 2 -//│ fun w7: forall 'a 'b. (Left[Int] | Object & 'a & ~#Left & ~#Right | Right[Int], 'a -> (None | Some['b])) -> (Int | 'a | 'b) +//│ fun w7: forall 'a 'b. ('a & (Left[Int] | Right[Int]), 'a -> (Object & ~#Some | Some['b])) -> (Int | 'a | 'b) // The results are wrong: w7(Left(99), _ => Some(0)) // => 0 w7(Left(99), _ => None) // => Left(99) w7(Right(99), _ => Some(0)) // => 0 w7(Right(99), _ => None) // => Right(99) -//│ Int +//│ Int | Right['B] +//│ where +//│ 'B :> 99 +//│ <: Int //│ res -//│ = 100 +//│ = 0 //│ res -//│ = 100 +//│ = Left {} //│ res -//│ = 101 +//│ = 0 //│ res -//│ = 101 +//│ = Right {} diff --git a/shared/src/test/diff/ucs/zipWith.mls b/shared/src/test/diff/ucs/zipWith.mls index a92fef38..abc77baa 100644 --- a/shared/src/test/diff/ucs/zipWith.mls +++ b/shared/src/test/diff/ucs/zipWith.mls @@ -1,4 +1,4 @@ -:NewDefs +:PreTyper @@ -78,7 +78,7 @@ fun zipWith_wrong(f, xs, ys) = and ys is Cons(y, ys) and zipWith_wrong(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) else None -//│ fun zipWith_wrong: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (None | Some[Cons['A]]) +//│ fun zipWith_wrong: forall 'A 'a 'b. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (None | Some[Cons['A]]) fun zipWith_wrong(f, xs, ys) = @@ -158,7 +158,7 @@ fun zipWith(f, xs, ys) = Nil then None Nil then if ys is Nil then Some(Nil) else None -//│ fun zipWith: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Nil, Cons['b] | Nil) -> (None | Some[Cons['A] | Nil]) +//│ fun zipWith: forall 'A 'a 'b. (('a, 'b) -> 'A, Cons['a] | Nil, Cons['b] | Nil) -> (None | Some[Cons['A] | Nil]) zipWith(pairup, Nil, Nil).value.toArray //│ Array[anything] diff --git a/todo.md b/todo.md new file mode 100644 index 00000000..cf7fac18 --- /dev/null +++ b/todo.md @@ -0,0 +1,133 @@ +This file will be deleted after we migrate all test cases and fixed all +problems located by test cases. + +- [x] shared/src/test/diff/codegen/AuxiliaryConstructors.mls +- [x] shared/src/test/diff/codegen/Mixin.mls + Fix that `PreTyper` does not traverse type definitions. +- [x] shared/src/test/diff/codegen/MixinCapture.mls +- [x] shared/src/test/diff/codegen/Nested.mls +- [x] shared/src/test/diff/codegen/NewMatching.mls + Destructing unparameterized class no longer causes code generation errors. +- [x] shared/src/test/diff/codegen/ValLet.mls +- [x] shared/src/test/diff/ecoop23/ComparePointPoly.mls + Desugar UCS shorthands in `PreTyper`. +- [x] shared/src/test/diff/ecoop23/ExpressionProblem.mls +- [x] shared/src/test/diff/ecoop23/Intro.mls +- [x] shared/src/test/diff/ecoop23/PolymorphicVariants.mls +- [x] shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls +- [x] shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls +- [x] shared/src/test/diff/fcp/QML_exist_nu.mls +- [x] shared/src/test/diff/gadt/Exp1.mls +- [x] shared/src/test/diff/gadt/Exp2.mls +- [x] shared/src/test/diff/gadt/ThisMatching.mls +- [x] shared/src/test/diff/nu/Andong.mls +- [x] shared/src/test/diff/nu/ArrayProg.mls +- [x] shared/src/test/diff/nu/BadUCS.mls + Add many `:ge` to cases where match scrutinee against mixins. + Add dummy class symbol so that we don't need to throw any + errors during desugaring. +- [x] shared/src/test/diff/nu/BasicClassInheritance.mls +- [x] shared/src/test/diff/nu/BasicClasses.mls +- [x] shared/src/test/diff/nu/CaseExpr.mls +- [x] shared/src/test/diff/nu/ClassSignatures.mls +- [x] shared/src/test/diff/nu/ClassesInMixins.mls + Improve error messages in `PreTyper`. +- [x] shared/src/test/diff/nu/CommaOperator.mls +- [x] shared/src/test/diff/nu/CtorSubtraction.mls +- [x] shared/src/test/diff/nu/Eval.mls +- [x] shared/src/test/diff/nu/EvalNegNeg.mls +- [x] shared/src/test/diff/nu/ExpressionProblem_repro.mls +- [x] shared/src/test/diff/nu/ExpressionProblem_small.mls +- [x] shared/src/test/diff/nu/FilterMap.mls +- [x] shared/src/test/diff/nu/FlatIfThenElse.mls +- [x] shared/src/test/diff/nu/FlatMonads.mls +- [x] shared/src/test/diff/nu/FunnyIndet.mls +- [x] shared/src/test/diff/nu/GADTMono.mls +- [x] shared/src/test/diff/nu/GenericClasses.mls +- [x] shared/src/test/diff/nu/GenericModules.mls +- [x] shared/src/test/diff/nu/HeungTung.mls +- [x] shared/src/test/diff/nu/Huawei1.mls +- [x] shared/src/test/diff/nu/InterfaceMono.mls +- [ ] shared/src/test/diff/nu/Interfaces.mls + What? Traits can't be patterns? +- [x] shared/src/test/diff/nu/LetRec.mls +- [x] shared/src/test/diff/nu/ListConsNil.mls +- [x] shared/src/test/diff/nu/LitMatch.mls +- [x] shared/src/test/diff/nu/MissingTypeArg.mls +- [x] shared/src/test/diff/nu/NamedArgs.mls +- [ ] shared/src/test/diff/nu/New.mls **OLD** +- [x] shared/src/test/diff/nu/NewNew.mls +- [x] shared/src/test/diff/nu/Object.mls +- [x] shared/src/test/diff/nu/OpLam.mls + Function `extractParameters` no longer raise errors. +- [x] shared/src/test/diff/nu/OptionFilter.mls +- [x] shared/src/test/diff/nu/OverrideShorthand.mls +- [x] shared/src/test/diff/nu/ParamPassing.mls +- [x] shared/src/test/diff/nu/PolymorphicVariants_Alt.mls +- [x] shared/src/test/diff/nu/PostHocMixinSignature.mls +- [x] shared/src/test/diff/nu/PrivateMemberOverriding.mls +- [x] shared/src/test/diff/nu/SelfRec.mls +- [x] shared/src/test/diff/nu/Subscripts.mls +- [x] shared/src/test/diff/nu/TODO_Classes.mls +- [x] shared/src/test/diff/nu/Unapply.mls +- [x] shared/src/test/diff/nu/UndefMatching.mls +- [x] shared/src/test/diff/nu/WeirdUnions.mls +- [x] shared/src/test/diff/nu/i180.mls +- [ ] shared/src/test/diff/nu/repro0.mls + - `PreTyper` does not accept top-level `val` bindings. +- [x] shared/src/test/diff/nu/repro1.mls +- [x] shared/src/test/diff/nu/repro_EvalNegNeg.mls +- [x] shared/src/test/diff/nu/repro_PolymorphicVariants.mls +- [x] shared/src/test/diff/pretyper/ucs/examples/JSON.mls +- [x] shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls +- [x] shared/src/test/diff/pretyper/ucs/examples/Option.mls +- [x] shared/src/test/diff/pretyper/ucs/examples/STLC.mls +- [x] shared/src/test/diff/ucs/AppSplits.mls +- [x] shared/src/test/diff/ucs/CrossBranchCapture.mls + Fix the mentioned problems. + TODO: Warn duplicated pattern bindings. +- [ ] shared/src/test/diff/ucs/DirectLines.mls **OLD** +- [x] shared/src/test/diff/ucs/ElseIf.mls +- [ ] shared/src/test/diff/ucs/ErrorMessage.mls **OLD** +- [x] shared/src/test/diff/ucs/Exhaustiveness.mls +- [ ] shared/src/test/diff/ucs/Humiliation.mls **OLD** +- [x] shared/src/test/diff/ucs/Hygiene.mls + Problem fixed! +- [ ] shared/src/test/diff/ucs/HygienicBindings.mls +- [ ] shared/src/test/diff/ucs/InterleavedLet.mls **OLD** +- [x] shared/src/test/diff/ucs/JSON.mls + Deleted. This one is not completed and we have a new version. +- [x] shared/src/test/diff/ucs/LeadingAnd.mls +- [x] shared/src/test/diff/ucs/LitUCS.mls +- [x] shared/src/test/diff/ucs/MultiwayIf.mls +- [ ] shared/src/test/diff/ucs/NestedBranches.mls + Found a bug in transformation. +- [x] shared/src/test/diff/ucs/NestedOpSplits.mls +- [x] shared/src/test/diff/ucs/NestedPattern.mls +- [x] shared/src/test/diff/ucs/NuPlainConditionals.mls +- [x] shared/src/test/diff/ucs/Or.mls +- [ ] shared/src/test/diff/ucs/OverlappedBranches.mls **OLD** +- [x] shared/src/test/diff/ucs/ParseFailures.mls +- [x] shared/src/test/diff/ucs/PlainConditionals.mls + Maybe we should keep this old one... +- [x] shared/src/test/diff/ucs/SimpleUCS.mls + Migrate this test case to new defintion typing. + Remove a `???` and raise error during transformation. +- [x] shared/src/test/diff/ucs/SplitAfterOp.mls + Wrap tests in functions so that errors are clearer. +- [ ] shared/src/test/diff/ucs/SplitAnd.mls + Should report missing else branches. +- [x] shared/src/test/diff/ucs/SplitAroundOp.mls +- [x] shared/src/test/diff/ucs/SplitBeforeOp.mls +- [x] shared/src/test/diff/ucs/SplitOps.mls +- [x] shared/src/test/diff/ucs/SplitScrutinee.mls + Fixed. +- [x] shared/src/test/diff/ucs/ThenIndent.mls +- [x] shared/src/test/diff/ucs/Tree.mls +- [ ] shared/src/test/diff/ucs/TrivialIf.mls **OLD** +- [ ] shared/src/test/diff/ucs/WeirdIf.mls + Should report redundant cases. +- [ ] shared/src/test/diff/ucs/WeirdSplit.mls **OLD** +- [ ] shared/src/test/diff/ucs/Wildcard.mls + Some unexpected empty splits. +- [x] shared/src/test/diff/ucs/zipWith.mls \ No newline at end of file From 71ac9f75803766744d49f1162f0c6ab7dc2470f1 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sun, 14 Jan 2024 19:50:21 +0800 Subject: [PATCH 060/143] Fix more tests and a bug in transformation --- .../main/scala/mlscript/ucs/DesugarUCS.scala | 2 +- .../src/main/scala/mlscript/ucs/display.scala | 15 +- .../src/main/scala/mlscript/ucs/package.scala | 2 + .../mlscript/ucs/stages/Normalization.scala | 2 - .../mlscript/ucs/stages/Transformation.scala | 90 +-- shared/src/test/diff/nu/New.mls | 56 +- shared/src/test/diff/nu/repro0.mls | 2 +- shared/src/test/diff/ucs/DirectLines.mls | 96 +-- shared/src/test/diff/ucs/ErrorMessage.mls | 51 +- shared/src/test/diff/ucs/Humiliation.mls | 417 ++---------- shared/src/test/diff/ucs/InterleavedLet.mls | 624 ++++++------------ shared/src/test/diff/ucs/NestedBranches.mls | 118 +--- .../src/test/diff/ucs/OverlappedBranches.mls | 110 +-- todo.md | 34 +- 14 files changed, 461 insertions(+), 1158 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index 40567bbd..8c2ab208 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -142,7 +142,7 @@ trait DesugarUCS extends Transformation implicit val context: Context = new Context(`if`) try trace("traverseIf") { // Stage 0: Transformation - val transformed = traceWithTopic("ucs.transform") { + val transformed = traceWithTopic("transform") { println("STEP 0") val transformed = transform(`if`) println("Transformed UCS term:") diff --git a/shared/src/main/scala/mlscript/ucs/display.scala b/shared/src/main/scala/mlscript/ucs/display.scala index 7ffffe28..2ce7656b 100644 --- a/shared/src/main/scala/mlscript/ucs/display.scala +++ b/shared/src/main/scala/mlscript/ucs/display.scala @@ -38,11 +38,11 @@ package object display { } def termBranch(branch: s.TermBranch): Lines = branch match { case s.TermBranch.Boolean(test, continuation) => - s"$test" #: termSplit(continuation, true, false) + s"${test.showDbg}" #: termSplit(continuation, true, false) case s.TermBranch.Match(scrutinee, continuation) => - s"$scrutinee is" #: patternSplit(continuation) + s"${scrutinee.showDbg} is" #: patternSplit(continuation) case s.TermBranch.Left(left, continuation) => - s"$left" #: operatorSplit(continuation) + s"${left.showDbg}" #: operatorSplit(continuation) } def patternSplit(split: s.PatternSplit): Lines = split match { case s.Split.Cons(head, tail) => patternBranch(head) ::: patternSplit(tail) @@ -59,7 +59,7 @@ package object display { case s.Split.Nil => Nil } def operatorBranch(branch: s.OperatorBranch): Lines = - s"${branch.operator}" #: (branch match { + s"${branch.operator.name}" #: (branch match { case s.OperatorBranch.Match(_, continuation) => patternSplit(continuation) case s.OperatorBranch.Binary(_, continuation) => termSplit(continuation, true, true) }) @@ -70,7 +70,7 @@ package object display { case lines => (0, pattern.toString) :: lines } } - ("if" #: termSplit(split, true, true)).iterator.map { case (n, line) => " " * n + line }.mkString("\n") + ("if" #: termSplit(split, true, true)).toIndentedString } @inline def showSplit(s: c.Split)(implicit context: Context): Str = showSplit("if", s) @@ -93,8 +93,7 @@ package object display { s"${showVar(scrutinee)} is $pattern" #: split(continuation, true, false) } val lines = split(s, true, true) - (if (prefix.isEmpty) lines else prefix #: lines) - .iterator.map { case (n, line) => " " * n + line }.mkString("\n") + (if (prefix.isEmpty) lines else prefix #: lines).toIndentedString } /** @@ -137,6 +136,6 @@ package object display { val Let(rec, nme, rhs, body) = let (0, s"let ${showVar(nme)} = ${rhs.showDbg}") :: showTerm(body) } - showTerm(term).map { case (n, line) => " " * n + line }.mkString("\n") + showTerm(term).toIndentedString } } diff --git a/shared/src/main/scala/mlscript/ucs/package.scala b/shared/src/main/scala/mlscript/ucs/package.scala index 7cd468c4..1e5a3cac 100644 --- a/shared/src/main/scala/mlscript/ucs/package.scala +++ b/shared/src/main/scala/mlscript/ucs/package.scala @@ -39,5 +39,7 @@ package object ucs { case lines => (0, prefix) :: lines.indent } } + def toIndentedString: String = + lines.iterator.map { case (n, line) => " " * n + line }.mkString("\n") } } diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index ffdf5fc3..d71b3243 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -102,7 +102,6 @@ trait Normalization { self: DesugarUCS with Traceable => case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => raiseError(msg"Unsupported pattern: ${pattern.toString}" -> pattern.toLoc) errorTerm - case Split.Let(rec, Var("_"), rhs, tail) => normalizeToTerm(tail) case Split.Let(_, nme, _, tail) if context.isScrutineeVar(nme) && generatedVars.contains(nme) => println(s"normalizing let binding of generated variable: ${nme.name}") normalizeToTerm(tail) @@ -124,7 +123,6 @@ trait Normalization { self: DesugarUCS with Traceable => split match { // case Split.Cons(head, Split.Nil) => Case(head.pattern, normalizeToTerm(head.continuation), NoCases) case other: Split.Cons => Wildcard(normalizeToTerm(other)) - case Split.Let(rec, Var("_"), rhs, tail) => normalizeToCaseBranches(tail) case Split.Let(_, nme, _, tail) if context.isScrutineeVar(nme) && generatedVars.contains(nme) => normalizeToCaseBranches(tail) case Split.Let(rec, nme, rhs, tail) => diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index 5956e69a..5a6e1560 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -11,6 +11,8 @@ import mlscript.utils._, shorthands._ import mlscript.NuFunDef import mlscript.PlainTup import scala.collection.immutable +import scala.annotation.tailrec +import scala.util.chaining._ /** * Transform the parsed AST into an AST similar to the one in the paper. @@ -124,13 +126,8 @@ trait Transformation { self: DesugarUCS with Traceable => PatternBranch(pattern, Split.default(rhs)).toSplit } case IfOpApp(lhs, Var("and"), rhs) => - println(s"lhs: $lhs") - separatePattern(lhs) match { - case (pattern, S(extraTest)) => - PatternBranch(pattern, TermBranch.Boolean(extraTest, transformIfBody(rhs)).toSplit).toSplit - case (pattern, N) => - PatternBranch(pattern, transformIfBody(rhs)).toSplit - } + val ::(head, tail) = splitAnd(lhs) + PatternBranch(transformPattern(head), transformConjunction(tail, transformIfBody(rhs), false)).toSplit case IfOpApp(lhs, op, rhs) => raiseError(msg"Syntactic split of patterns are not supported" -> op.toLoc) Split.Nil @@ -157,23 +154,16 @@ trait Transformation { self: DesugarUCS with Traceable => // This is parsed as `{ and ( Cons x xs ) [ is ys ( Cons y ys ) ] }`. // I think it's not very well-formed. But I still implement it for not // breaking existing tests. - splitAnd(lhs) match { - case Nil => die // It's impossible to be empty. - case pattern :: tail => - // Here, `pattern` will be `( Cons x xs )` and `tail` will be - // `[ is ys (Cons y ys) ]`. We can make a new `IfOpsApp`. - println(s"lol, pattern is $pattern") - println(s"lol, tail is $tail") - tail match { - case init :+ last => - println(s"lol, init is $init") - println(s"lol, last is $last") - val remake = IfOpsApp(last, opsRhss) - val following = transformConjunction(init, transformIfBody(remake), true) - PatternBranch(transformPattern(pattern), following).toSplit - case _ => // This can only be `Nil`. - PatternBranch(transformPattern(pattern), transformBrokenIfOpsApp(opsRhss)).toSplit - } + val ::(pattern, tail) = splitAnd(lhs) + // Here, `pattern` will be `( Cons x xs )` and `tail` will be + // `[ is ys (Cons y ys) ]`. We can make a new `IfOpsApp`. + tail match { + case init :+ last => + val remake = IfOpsApp(last, opsRhss) + val following = transformConjunction(init, transformIfBody(remake), true) + PatternBranch(transformPattern(pattern), following).toSplit + case _ => // This can only be `Nil`. + PatternBranch(transformPattern(pattern), transformBrokenIfOpsApp(opsRhss)).toSplit } // END TEMPORARY PATCH case IfLet(rec, nme, rhs, body) => die @@ -202,13 +192,15 @@ trait Transformation { self: DesugarUCS with Traceable => private def transformPattern(term: Term): Pattern = term match { case wildcard @ Var("_") => EmptyPattern(wildcard) // The case for wildcard. case nme @ Var("true" | "false") => ConcretePattern(nme) - case nme @ Var(name) if name.headOption.exists(_.isUpper) => ClassPattern(nme, N, refined = false) + case nme @ Var(name) if name.isCapitalized => ClassPattern(nme, N, refined = false) case nme: Var => NamePattern(nme) case literal: Lit => LiteralPattern(literal) case App(Var("refined"), PlainTup(p)) => transformPattern(p) match { case cp: ClassPattern => cp.copy(refined = true).withLocOf(cp) - case _ => ??? // TODO error + case p => + raiseError(msg"only class patterns can be refined" -> p.toLoc) + p } case App(classNme @ Var(_), parameters: Tup) => ClassPattern(classNme, S(transformTupleTerm(parameters)), refined = false) @@ -221,26 +213,29 @@ trait Transformation { self: DesugarUCS with Traceable => private def separatePattern(term: Term): (Pattern, Opt[Term]) = { val (rawPattern, extraTest) = helpers.separatePattern(term, true) - println("rawPattern: " + rawPattern.toString) - println("extraTest: " + extraTest.toString) + println(s"pattern: ${rawPattern.showDbg} ;; test: ${extraTest.fold("_")(_.showDbg)}") (transformPattern(rawPattern), extraTest) } - // TODO: Maybe we can change the return type to `::[Term]` so that it will not - // empty. - private def splitAnd(t: Term): List[Term] = trace(s"splitAnd <== $t") { - t match { - case App( - App(Var("and"), - Tup((_ -> Fld(_, lhs)) :: Nil)), - Tup((_ -> Fld(_, rhs)) :: Nil) - ) => // * Old-style operators - splitAnd(lhs) :+ rhs - case App(Var("and"), PlainTup(lhs, rhs)) => - splitAnd(lhs) :+ rhs - case _ => t :: Nil + /** + * Split a term into a list of terms. Note that the return type is `::[Term]` + * because there should be at least one term even we don't split. It used to + * split right-associate `and` terms, but it turned out that `and` may + * nested in a left-associate manner. Therefore, the function now traverse + * the entire term and split all `and` terms. + */ + private def splitAnd(t: Term): ::[Term] = { + @tailrec def rec(acc: Ls[Term], rest: ::[Term]): ::[Term] = rest.head match { + case lhs and rhs => rec(acc, ::(rhs, lhs :: rest.tail)) + case sole => rest.tail match { + case Nil => ::(sole, acc) + case more @ ::(_, _) => rec(sole :: acc, more) + } + } + rec(Nil, ::(t, Nil)).tap { rs => + println(s"splitAnd ${t.showDbg} ==> ${rs.iterator.map(_.showDbg).mkString(" ∧ ")}") } - }(r => "splitAnd ==> " + r.iterator.map(_.toString).mkString(" ∧ ")) + } } object Transformation { @@ -249,10 +244,19 @@ object Transformation { case _ => die } + /** Matches terms like `x is y`. */ private object is { - def unapply(term: Term): Opt[(Term, Term)] = term match { + def unapply(term: Term): Opt[Term -> Term] = term match { case App(Var("is"), PlainTup(scrutinee, pattern)) => S(scrutinee -> pattern) case _ => N } } + + /** Matches terms like `x and y` */ + private object and { + def unapply(term: Term): Opt[(Term, Term)] = term match { + case App(Var("and"), PlainTup(lhs, rhs)) => S((lhs, rhs)) + case _ => N + } + } } diff --git a/shared/src/test/diff/nu/New.mls b/shared/src/test/diff/nu/New.mls index 945effc1..faae55db 100644 --- a/shared/src/test/diff/nu/New.mls +++ b/shared/src/test/diff/nu/New.mls @@ -1,32 +1,27 @@ -:NewParser +:PreTyper -class Foo(x) -//│ Defined class Foo -//│ Foo: 'x -> (Foo & {x: 'x}) -//│ = [Function: Foo1] +class Foo[A](x: A) +//│ class Foo[A](x: A) let f = Foo(1) -//│ f: Foo & {x: 1} -//│ = Foo { x: 1 } +//│ let f: Foo['A] +//│ where +//│ 'A :> 1 +//│ f +//│ = Foo {} // let f = new Foo(1) if f is Foo then 1 else 0 -//│ ╔══[WARNING] old desugarer used -//│ ║ l.15: if f is Foo then 1 else 0 -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ res: 0 | 1 -//│ = 1 +//│ 0 | 1 +//│ res +//│ = 1 if f is Foo(a) then a else 0 -//│ ╔══[WARNING] old desugarer used -//│ ║ l.23: if f is Foo(a) then a else 0 -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ res: 0 | 1 -//│ = 1 +//│ 0 | 1 +//│ res +//│ = 1 // case f of // { Foo -> @@ -39,22 +34,19 @@ if f is Foo(a) then a else 0 fun test(x) = if x is Foo(a) then a -//│ ╔══[WARNING] old desugarer used -//│ ║ l.41: fun test(x) = if x is Foo(a) then a -//│ ╙── ^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ test: (Foo & {x: 'x}) -> 'x -//│ = [Function: test] +//│ fun test: forall 'a. Foo['a] -> 'a test(f) -//│ res: 1 -//│ = 1 +//│ 1 +//│ res +//│ = 1 -class PoInt(x, y) -//│ Defined class PoInt -//│ PoInt: ('x, 'y,) -> (PoInt & {x: 'x, y: 'y}) -//│ = [Function: PoInt1] +class PoInt(x: Int, y: Int) +//│ class PoInt(x: Int, y: Int) -// let origin = new PoInt(0, 0) +let origin = new PoInt(0, 0) +//│ let origin: PoInt +//│ origin +//│ = PoInt {} diff --git a/shared/src/test/diff/nu/repro0.mls b/shared/src/test/diff/nu/repro0.mls index 61095fca..89ed2dab 100644 --- a/shared/src/test/diff/nu/repro0.mls +++ b/shared/src/test/diff/nu/repro0.mls @@ -1,7 +1,7 @@ :PreTyper :NoJS - +:e class Add[out E](val lhs: E) val add11 = Add(add11) module EvalAddLit { diff --git a/shared/src/test/diff/ucs/DirectLines.mls b/shared/src/test/diff/ucs/DirectLines.mls index 5e346f8b..4450e04e 100644 --- a/shared/src/test/diff/ucs/DirectLines.mls +++ b/shared/src/test/diff/ucs/DirectLines.mls @@ -1,59 +1,28 @@ -:NewParser +:PreTyper fun f(x, y) = if x == 0 then "x" y == 0 then "y" _ then "nah" -//│ ╔══[WARNING] old desugarer used -//│ ║ l.5: x == 0 then "x" -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.6: y == 0 then "y" -//│ ║ ^^^^^^^^^^^^^^^^^^^ -//│ ║ l.7: _ then "nah" -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ f: (number, number,) -> ("nah" | "x" | "y") -//│ = [Function: f] +//│ fun f: (Num, Num) -> ("nah" | "x" | "y") -class Option -class Some(value): Option -class None: Option -//│ Defined class Option -//│ Defined class Some -//│ Defined class None -//│ Option: () -> Option -//│ = [Function: Option1] -//│ Some: 'value -> (Some & {value: 'value}) -//│ = [Function: Some1] -//│ None: () -> None -//│ = [Function: None1] +abstract class Option[A]: Some[A] | None +class Some[A](value: A) extends Option[A] +module None extends Option +//│ abstract class Option[A]: None | Some[A] +//│ class Some[A](value: A) extends Option +//│ module None extends Option fun isValid(x) = if x then false else true -//│ ╔══[WARNING] old desugarer used -//│ ║ l.32: fun isValid(x) = if x then false else true -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ isValid: anything -> Bool -//│ = [Function: isValid] +//│ fun isValid: Bool -> Bool fun f(x, allowNone) = if x is Some(x) and isValid(x) then "good" is None() and allowNone then "okay" is _ then "bad" -//│ ╔══[WARNING] old desugarer used -//│ ║ l.41: if x -//│ ║ ^ -//│ ║ l.42: is Some(x) and isValid(x) then "good" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.43: is None() and allowNone then "okay" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.44: is _ then "bad" -//│ ╙── ^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ f: (anything, anything,) -> ("bad" | "good" | "okay") -//│ = [Function: f1] +//│ fun f: (Object & ~#Some | Some[Bool], Bool) -> ("bad" | "good" | "okay") fun f(x, y, z) = if @@ -66,30 +35,10 @@ fun f(x, y, z) = _ then "bruh" 3 then "y = 3" _ then "bruh" -//│ ╔══[WARNING] old desugarer used -//│ ║ l.60: x == 0 then "x" -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.61: y == -//│ ║ ^^^^^^^^ -//│ ║ l.62: 1 then "y = 1" -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.63: 2 and z == -//│ ║ ^^^^^^^^^^^^^^^^ -//│ ║ l.64: 0 then "z = 0" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.65: 9 then "z = 9" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.66: _ then "bruh" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.67: 3 then "y = 3" -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.68: _ then "bruh" -//│ ╙── ^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ f: (number, number, number,) -> ("bruh" | "x" | "y = 1" | "y = 3" | "z = 0" | "z = 9") -//│ = [Function: f2] +//│ fun f: (Num, Num, Num) -> ("bruh" | "x" | "y = 1" | "y = 3" | "z = 0" | "z = 9") -:w +// :w +// FIXME: Report redundant else branches. fun f(a, b) = if a == 0 then 0 @@ -98,21 +47,4 @@ fun f(a, b) = 2 then 2 _ then 7 else 3 -//│ ╔══[WARNING] old desugarer used -//│ ║ l.95: a == 0 then 0 -//│ ║ ^^^^^^^^^^^^^ -//│ ║ l.96: b == -//│ ║ ^^^^^^^^ -//│ ║ l.97: 1 then 1 -//│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.98: 2 then 2 -//│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.99: _ then 7 -//│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.100: else 3 -//│ ╙── ^^^^^^^^^^ -//│ ╔══[WARNING] Found a redundant else branch -//│ ║ l.100: else 3 -//│ ╙── ^ -//│ f: (number, number,) -> (0 | 1 | 2 | 7) -//│ = [Function: f3] +//│ fun f: (Num, Num) -> (0 | 1 | 2 | 7) diff --git a/shared/src/test/diff/ucs/ErrorMessage.mls b/shared/src/test/diff/ucs/ErrorMessage.mls index 70bbd0eb..a361659c 100644 --- a/shared/src/test/diff/ucs/ErrorMessage.mls +++ b/shared/src/test/diff/ucs/ErrorMessage.mls @@ -1,42 +1,37 @@ -:NewParser +:PreTyper -class Point(x, y) -//│ Defined class Point -//│ Point: ('x, 'y,) -> (Point & {x: 'x, y: 'y}) -//│ = [Function: Point1] +class Point(x: Int, y: Int) +//│ class Point(x: Int, y: Int) :e -:ge fun f(p) = if p is Point(x, y, z) then x + y + z -//│ ╔══[WARNING] old desugarer used -//│ ║ l.11: if p is -//│ ║ ^^^^ -//│ ║ l.12: Point(x, y, z) then x + y + z -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ ╔══[ERROR] class Point expects 2 parameters but found 3 parameters -//│ ║ l.12: Point(x, y, z) then x + y + z -//│ ╙── ^^^^^^^^^^^^^^ -//│ f: anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╔══[ERROR] Type mismatch in field selection: +//│ ╟── tuple literal of type `{0: ?#x, 1: ?#y}` does not have field '2' +//│ ║ l.3: class Point(x: Int, y: Int) +//│ ║ ^^^^^^^^^ +//│ ╟── but it flows into operator application with expected type `{2: ?a}` +//│ ║ l.8: if p is +//│ ║ ^^^^ +//│ ║ l.9: Point(x, y, z) then x + y + z +//│ ╙── ^^^^^^^^^ +//│ fun f: Point -> Int :e :ge fun g(xs) = if xs is head :: _ then head -//│ ╔══[WARNING] old desugarer used -//│ ║ l.29: if xs is -//│ ║ ^^^^^ -//│ ║ l.30: head :: _ then head -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ ╔══[ERROR] Cannot find operator `::` in the context -//│ ║ l.30: head :: _ then head +//│ ╔══[ERROR] type identifier `::` not found +//│ ║ l.25: head :: _ then head +//│ ╙── ^^ +//│ ╔══[ERROR] identifier `::` not found +//│ ║ l.25: head :: _ then head +//│ ╙── ^^ +//│ ╔══[ERROR] type identifier not found: :: +//│ ║ l.25: head :: _ then head //│ ╙── ^^ -//│ g: anything -> error +//│ fun g: nothing -> error //│ Code generation encountered an error: -//│ if expression was not desugared +//│ unresolved symbol :: diff --git a/shared/src/test/diff/ucs/Humiliation.mls b/shared/src/test/diff/ucs/Humiliation.mls index 2463ac99..86bf952f 100644 --- a/shared/src/test/diff/ucs/Humiliation.mls +++ b/shared/src/test/diff/ucs/Humiliation.mls @@ -1,73 +1,35 @@ -:NewParser +:PreTyper -class Foo(x) -//│ Defined class Foo -//│ Foo: 'x -> (Foo & {x: 'x}) -//│ = [Function: Foo1] +class Foo[T](x: T) +//│ class Foo[T](x: T) if 1 is 1 then 1 else 0 -//│ ╔══[WARNING] old desugarer used -//│ ║ l.9: if 1 is 1 then 1 else 0 -//│ ╙── ^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ res: 0 | 1 -//│ = 1 +//│ 0 | 1 +//│ res +//│ = 1 fun test(x) = if x is 1 then 0 else 1 -//│ ╔══[WARNING] old desugarer used -//│ ║ l.17: fun test(x) = if x is 1 then 0 else 1 -//│ ╙── ^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ test: anything -> (0 | 1) -//│ = [Function: test] +//│ fun test: Object -> (0 | 1) -// It should report duplicated branches. -:w +// :w +// FIXME: It should report duplicated branches. fun testF(x) = if x is Foo(a) then a Foo(a) then a -//│ ╔══[WARNING] old desugarer used -//│ ║ l.27: fun testF(x) = if x is -//│ ║ ^^^^ -//│ ║ l.28: Foo(a) then a -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.29: Foo(a) then a -//│ ╙── ^^^^^^^^^^^^^^^ -//│ ╔══[WARNING] Found a duplicated branch -//│ ╟── This branch -//│ ║ l.29: Foo(a) then a -//│ ║ ^ -//│ ╟── is subsumed by the branch here. -//│ ║ l.28: Foo(a) then a -//│ ╙── ^ -//│ testF: (Foo & {x: 'x}) -> 'x -//│ = [Function: testF] +//│ fun testF: forall 'a. Foo['a] -> 'a -class Bar(y, z) -//│ Defined class Bar -//│ Bar: ('y, 'z,) -> (Bar & {y: 'y, z: 'z}) -//│ = [Function: Bar1] +class Bar[Y, Z](y: Y, z: Z) +//│ class Bar[Y, Z](y: Y, z: Z) fun test(f) = if f is Foo(a) then a Bar(b, c) then b + c -//│ ╔══[WARNING] old desugarer used -//│ ║ l.52: fun test(f) = if f is -//│ ║ ^^^^ -//│ ║ l.53: Foo(a) then a -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.54: Bar(b, c) then b + c -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ test: (Bar & {y: int, z: int} | Foo & {x: 'x}) -> (int | 'x) -//│ = [Function: test1] +//│ fun test: forall 'a. (Bar[Int, Int] | Foo['a]) -> (Int | 'a) -class Pair(fst, snd) -//│ Defined class Pair -//│ Pair: ('fst, 'snd,) -> (Pair & {fst: 'fst, snd: 'snd}) -//│ = [Function: Pair1] +class Pair[A, B](fst: A, snd: B) +//│ class Pair[A, B](fst: A, snd: B) fun f(x) = if x is @@ -75,85 +37,53 @@ fun f(x) = Pair(1, 1) then "ones" Pair(y, 1) then x _ then "nah" -//│ ╔══[WARNING] old desugarer used -//│ ║ l.73: if x is -//│ ║ ^^^^ -//│ ║ l.74: Pair(0, 0) then "zeros" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.75: Pair(1, 1) then "ones" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.76: Pair(y, 1) then x -//│ ║ ^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.77: _ then "nah" -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ f: (Pair & 'a | ~Pair) -> ("nah" | "ones" | "zeros" | 'a) -//│ = [Function: f] +//│ fun f: (Object & ~#Pair | Pair[Object, Object]) -> ("nah" | "ones" | "zeros" | Pair[nothing, nothing]) class Z() class O() -//│ Defined class Z -//│ Defined class O -//│ Z: () -> Z -//│ = [Function: Z1] -//│ O: () -> O -//│ = [Function: O1] +//│ class Z() +//│ class O() // This is not exhaustive. :e -:ge fun foo(x) = if x is Pair(Z(), Z()) then "zeros" Pair(O(), O()) then "ones" -//│ ╔══[WARNING] old desugarer used -//│ ║ l.105: fun foo(x) = if x is -//│ ║ ^^^^ -//│ ║ l.106: Pair(Z(), Z()) then "zeros" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.107: Pair(O(), O()) then "ones" -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ ╔══[ERROR] The match is not exhaustive. -//│ ║ l.105: fun foo(x) = if x is -//│ ║ ^^^^ -//│ ╟── The scrutinee at this position misses 1 case. -//│ ║ l.106: Pair(Z(), Z()) then "zeros" -//│ ║ ^^^ -//│ ╟── [Missing Case 1/1] `O` -//│ ╟── It first appears here. -//│ ║ l.107: Pair(O(), O()) then "ones" -//│ ╙── ^^^ -//│ foo: anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╔══[ERROR] When scrutinee `x` is `Pair`, +//│ ║ l.50: Pair(Z(), Z()) then "zeros" +//│ ║ ^^^^ +//│ ╟── scrutinee `x$Pair_0` is `Z`, and +//│ ║ l.50: Pair(Z(), Z()) then "zeros" +//│ ║ ^ +//│ ╟── Scrutinee `x$Pair_1` has 1 missing case +//│ ╟── It can be class `O` +//│ ║ l.51: Pair(O(), O()) then "ones" +//│ ╙── ^ +//│ ╔══[ERROR] When scrutinee `x` is `Pair`, +//│ ║ l.50: Pair(Z(), Z()) then "zeros" +//│ ║ ^^^^ +//│ ╟── scrutinee `x$Pair_0` is `O`, and +//│ ║ l.51: Pair(O(), O()) then "ones" +//│ ║ ^ +//│ ╟── Scrutinee `x$Pair_1` has 1 missing case +//│ ╟── It can be class `Z` +//│ ║ l.50: Pair(Z(), Z()) then "zeros" +//│ ╙── ^ +//│ fun foo: Pair[O | Z, nothing] -> ("ones" | "zeros") // Change `Pair` to a real pair. :e -:ge fun foo(x) = if x is [Z(), Z()] then "zeros" [O(), O()] then "ones" -//│ ╔══[WARNING] old desugarer used -//│ ║ l.133: fun foo(x) = if x is -//│ ║ ^^^^ -//│ ║ l.134: [Z(), Z()] then "zeros" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.135: [O(), O()] then "ones" -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ ╔══[ERROR] The match is not exhaustive. -//│ ║ l.133: fun foo(x) = if x is -//│ ║ ^^^^ -//│ ╟── The scrutinee at this position misses 1 case. -//│ ║ l.134: [Z(), Z()] then "zeros" -//│ ║ ^^^ -//│ ╟── [Missing Case 1/1] `O` -//│ ╟── It first appears here. -//│ ║ l.135: [O(), O()] then "ones" -//│ ╙── ^^^ -//│ foo: anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╔══[ERROR] When scrutinee `x$Tuple$2_0` is `O` +//│ ║ l.78: [O(), O()] then "ones" +//│ ║ ^ +//│ ╟── Scrutinee `x$Tuple$2_1` has 1 missing case +//│ ╟── It can be class `Z` +//│ ║ l.77: [Z(), Z()] then "zeros" +//│ ╙── ^ +//│ fun foo: forall 'a. {0: O | Z, 1: O & 'a} -> ("ones" | "zeros" | 'a) fun foo(x) = if x is Pair(a, b) then if a is @@ -161,46 +91,7 @@ fun foo(x) = if x is Z() then "zeros" O() then if b is O() then "ones" -//│ ╔══[WARNING] old desugarer used -//│ ║ l.158: fun foo(x) = if x is -//│ ║ ^^^^ -//│ ║ l.159: Pair(a, b) then if a is -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.160: Z() then if b is -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.161: Z() then "zeros" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.162: O() then if b is -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.163: O() then "ones" -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ ╔══[WARNING] old desugarer used -//│ ║ l.159: Pair(a, b) then if a is -//│ ║ ^^^^ -//│ ║ l.160: Z() then if b is -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.161: Z() then "zeros" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.162: O() then if b is -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.163: O() then "ones" -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ ╔══[WARNING] old desugarer used -//│ ║ l.160: Z() then if b is -//│ ║ ^^^^ -//│ ║ l.161: Z() then "zeros" -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ ╔══[WARNING] old desugarer used -//│ ║ l.162: O() then if b is -//│ ║ ^^^^ -//│ ║ l.163: O() then "ones" -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ foo: (Pair & {fst: O | Z, snd: nothing}) -> ("ones" | "zeros") -//│ = [Function: foo2] +//│ fun foo: Pair[O | Z, nothing] -> ("ones" | "zeros") fun foo(x) = if x is Pair(a, b) then if a is @@ -209,52 +100,7 @@ fun foo(x) = if x is else "???" O() then if b is O() then "ones" -//│ ╔══[WARNING] old desugarer used -//│ ║ l.205: fun foo(x) = if x is -//│ ║ ^^^^ -//│ ║ l.206: Pair(a, b) then if a is -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.207: Z() then if b is -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.208: Z() then "zeros" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.209: else "???" -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.210: O() then if b is -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.211: O() then "ones" -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ ╔══[WARNING] old desugarer used -//│ ║ l.206: Pair(a, b) then if a is -//│ ║ ^^^^ -//│ ║ l.207: Z() then if b is -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.208: Z() then "zeros" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.209: else "???" -//│ ║ ^^^^^^^^^^^^^^^^ -//│ ║ l.210: O() then if b is -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.211: O() then "ones" -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ ╔══[WARNING] old desugarer used -//│ ║ l.207: Z() then if b is -//│ ║ ^^^^ -//│ ║ l.208: Z() then "zeros" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.209: else "???" -//│ ╙── ^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ ╔══[WARNING] old desugarer used -//│ ║ l.210: O() then if b is -//│ ║ ^^^^ -//│ ║ l.211: O() then "ones" -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ foo: (Pair & {fst: O | Z, snd: O}) -> ("???" | "ones" | "zeros") -//│ = [Function: foo3] +//│ fun foo: Pair[O | Z, O] -> ("???" | "ones" | "zeros") fun foo(x) = if x is Pair(a, b) then if a is @@ -264,63 +110,10 @@ fun foo(x) = if x is O() then if b is O() then "zeros" else "???" -//│ ╔══[WARNING] old desugarer used -//│ ║ l.259: fun foo(x) = if x is -//│ ║ ^^^^ -//│ ║ l.260: Pair(a, b) then if a is -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.261: Z() then if b is -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.262: Z() then "zeros" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.263: else "???" -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.264: O() then if b is -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.265: O() then "zeros" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.266: else "???" -//│ ╙── ^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ ╔══[WARNING] old desugarer used -//│ ║ l.260: Pair(a, b) then if a is -//│ ║ ^^^^ -//│ ║ l.261: Z() then if b is -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.262: Z() then "zeros" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.263: else "???" -//│ ║ ^^^^^^^^^^^^^^^^ -//│ ║ l.264: O() then if b is -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.265: O() then "zeros" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.266: else "???" -//│ ╙── ^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ ╔══[WARNING] old desugarer used -//│ ║ l.261: Z() then if b is -//│ ║ ^^^^ -//│ ║ l.262: Z() then "zeros" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.263: else "???" -//│ ╙── ^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ ╔══[WARNING] old desugarer used -//│ ║ l.264: O() then if b is -//│ ║ ^^^^ -//│ ║ l.265: O() then "zeros" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.266: else "???" -//│ ╙── ^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ foo: (Pair & {fst: O | Z}) -> ("???" | "zeros") -//│ = [Function: foo4] +//│ fun foo: Pair[O | Z, Object] -> ("???" | "zeros") -class S(pred) -//│ Defined class S -//│ S: 'pred -> (S & {pred: 'pred}) -//│ = [Function: S1] +class S(pred: S | Z | O) +//│ class S(pred: O | S | Z) // TODO: Cannot check exhaustiveness of nested UCS yet. fun foo(x) = if x is @@ -331,108 +124,34 @@ fun foo(x) = if x is O() then if b is O() then "zeros" else "???" -//│ ╔══[WARNING] old desugarer used -//│ ║ l.326: fun foo(x) = if x is -//│ ║ ^^^^ -//│ ║ l.327: Pair(a, b) then if a is -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.328: Z() then if b is -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.329: S(x) then x -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.330: else "???" -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.331: O() then if b is -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.332: O() then "zeros" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.333: else "???" -//│ ╙── ^^^^^^^^^^^^^^^^^ -//│ ╔══[WARNING] old desugarer used -//│ ║ l.327: Pair(a, b) then if a is -//│ ║ ^^^^ -//│ ║ l.328: Z() then if b is -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.329: S(x) then x -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.330: else "???" -//│ ║ ^^^^^^^^^^^^^^^^ -//│ ║ l.331: O() then if b is -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.332: O() then "zeros" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.333: else "???" -//│ ╙── ^^^^^^^^^^^^^^^^ -//│ ╔══[WARNING] old desugarer used -//│ ║ l.328: Z() then if b is -//│ ║ ^^^^ -//│ ║ l.329: S(x) then x -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.330: else "???" -//│ ╙── ^^^^^^^^^^^^^^^^^ -//│ ╔══[WARNING] old desugarer used -//│ ║ l.331: O() then if b is -//│ ║ ^^^^ -//│ ║ l.332: O() then "zeros" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.333: else "???" -//│ ╙── ^^^^^^^^^^^^^^^^^ -//│ foo: (Pair & {fst: O | Z, snd: S & {pred: 'pred} | ~S}) -> ("???" | "zeros" | 'pred) -//│ = [Function: foo5] +//│ fun foo: Pair[O | Z, Object] -> ("???" | "zeros" | O | S | Z) foo(Pair(Z(), Z())) -//│ res: "???" | "zeros" -//│ = '???' +//│ "???" | "zeros" | O | S | Z +//│ res +//│ = '???' :e -:ge fun foo(x) = if x is Pair(Z(), Z()) then "zeros" Pair(O(), O()) then "ones" Pair(y, O()) then x -//│ ╔══[WARNING] old desugarer used -//│ ║ l.389: fun foo(x) = if x is -//│ ║ ^^^^ -//│ ║ l.390: Pair(Z(), Z()) then "zeros" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.391: Pair(O(), O()) then "ones" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.392: Pair(y, O()) then x -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ ╔══[ERROR] The match is not exhaustive. -//│ ║ l.389: fun foo(x) = if x is -//│ ║ ^^^^ -//│ ╟── The scrutinee at this position misses 1 case. -//│ ║ l.390: Pair(Z(), Z()) then "zeros" -//│ ║ ^^^ -//│ ╟── [Missing Case 1/1] `Z` -//│ ╟── It first appears here. -//│ ║ l.390: Pair(Z(), Z()) then "zeros" -//│ ╙── ^^^ -//│ foo: anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ ╔══[ERROR] When scrutinee `x` is `Pair` +//│ ║ l.136: Pair(Z(), Z()) then "zeros" +//│ ║ ^^^^ +//│ ╟── Scrutinee `x$Pair_1` has 1 missing case +//│ ╟── It can be class `Z` +//│ ║ l.136: Pair(Z(), Z()) then "zeros" +//│ ╙── ^ +//│ fun foo: forall 'B 'A. Pair['A, O & 'B] -> ("ones" | "zeros" | Pair['A, 'B] | 'A) +//│ where +//│ 'A <: Object fun foo(x, y) = if x is Z() and y is O() then 0 else 1 -//│ ╔══[WARNING] old desugarer used -//│ ║ l.417: fun foo(x, y) = if x is Z() and y is O() then 0 else 1 -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ foo: (anything, anything,) -> (0 | 1) -//│ = [Function: foo7] +//│ fun foo: (Object, Object) -> (0 | 1) fun foo(x, y) = if x is Z() and y is O() then 0 else 1 -//│ ╔══[WARNING] old desugarer used -//│ ║ l.426: if x is -//│ ║ ^^^^ -//│ ║ l.427: Z() and y is O() then 0 -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.428: else 1 -//│ ╙── ^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ foo: (anything, anything,) -> (0 | 1) -//│ = [Function: foo8] +//│ fun foo: (Object, Object) -> (0 | 1) diff --git a/shared/src/test/diff/ucs/InterleavedLet.mls b/shared/src/test/diff/ucs/InterleavedLet.mls index 6b8d1ade..cb41cb72 100644 --- a/shared/src/test/diff/ucs/InterleavedLet.mls +++ b/shared/src/test/diff/ucs/InterleavedLet.mls @@ -1,121 +1,72 @@ -:NewParser +:PreTyper fun f(x) = if x == let v = 0 v then v else 0 -//│ ╔══[WARNING] old desugarer used -//│ ║ l.4: if x == -//│ ║ ^^^^ -//│ ║ l.5: let v = 0 -//│ ║ ^^^^^^^^^^^^^ -//│ ║ l.6: v then v -//│ ║ ^^^^^^^^^^^^ -//│ ║ l.7: else 0 -//│ ╙── ^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ f: number -> 0 -//│ = [Function: f] - -class Option -class Some(value): Option -class None: Option -class Either -class Left(leftValue): Either -class Right(rightValue): Either -//│ Defined class Option -//│ Defined class Some -//│ Defined class None -//│ Defined class Either -//│ Defined class Left -//│ Defined class Right -//│ Option: () -> Option -//│ = [Function: Option1] -//│ Some: 'value -> (Some & {value: 'value}) -//│ = [Function: Some1] -//│ None: () -> None -//│ = [Function: None1] -//│ Either: () -> Either -//│ = [Function: Either1] -//│ Left: 'leftValue -> (Left & {leftValue: 'leftValue}) -//│ = [Function: Left1] -//│ Right: 'rightValue -> (Right & {rightValue: 'rightValue}) -//│ = [Function: Right1] - +//│ fun f: Num -> 0 + +abstract class Option[A]: Some[A] | None +class Some[A](value: A) extends Option[A] +module None extends Option +//│ abstract class Option[A]: None | Some[A] +//│ class Some[A](value: A) extends Option +//│ module None extends Option + +abstract class Either[out A, out B]: Left[A] | Right[B] +class Left[A](leftValue: A) extends Either[A, nothing] +class Right[B](rightValue: B) extends Either[nothing, B] +//│ abstract class Either[A, B]: Left[A] | Right[B] +//│ class Left[A](leftValue: A) extends Either +//│ class Right[B](rightValue: B) extends Either + +:dpt:normalize.result fun q(x) = if x is Some and x is Some and x is Some then 0 -//│ ╔══[WARNING] old desugarer used -//│ ║ l.48: x is Some and x is Some and x is Some then 0 -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ q: Some -> 0 -//│ = [Function: q] - -:w +//│ | | | | | | | Normalized UCS term: +//│ | | | | | | | case x*‡ of +//│ | | | | | | | Some*◊ -> 0 +//│ fun q: Some[anything] -> 0 + +:e +// FIXME: Unexpected empty split. fun p(x, y) = if x is Some and y is None then 0 y is Some and x is Some then 1 x is Some and y is Some then 0 -//│ ╔══[WARNING] old desugarer used -//│ ║ l.59: x is Some and y is None then 0 -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.60: y is Some and x is Some then 1 -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.61: x is Some and y is Some then 0 -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╔══[WARNING] Found a duplicated branch -//│ ╟── This branch -//│ ║ l.61: x is Some and y is Some then 0 -//│ ║ ^ -//│ ╟── is subsumed by the branch here. -//│ ║ l.60: y is Some and x is Some then 1 -//│ ╙── ^ -//│ p: (Some, None | Some,) -> (0 | 1) -//│ = [Function: p] +//│ ╔══[ERROR] Found unexpected empty split +//│ ╙── +//│ ╔══[ERROR] Scrutinee `y` has 1 missing case +//│ ║ l.38: y is Some and x is Some then 1 +//│ ║ ^ +//│ ╟── It can be module `None` +//│ ║ l.37: x is Some and y is None then 0 +//│ ╙── ^^^^ +//│ fun p: (Object & ~#Some | Some[anything], Some[anything]) -> (0 | 1) fun h(x, y) = if x is None then y let y_square = y * y Some(z) then z + y_square -//│ ╔══[WARNING] old desugarer used -//│ ║ l.80: if x is -//│ ║ ^^^^ -//│ ║ l.81: None then y -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.82: let y_square = y * y -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.83: Some(z) then z + y_square -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ h: (None | Some & {value: int}, int,) -> int -//│ = [Function: h] +//│ fun h: (None | Some[Int], Int) -> Int h(Some(5), 6) -//│ res: int -//│ = 41 +//│ Int +//│ res +//│ = 41 fun h(x, y) = if x is None then y let y_square = y * y Some(y_square) then 0 -//│ ╔══[WARNING] old desugarer used -//│ ║ l.102: if x is -//│ ║ ^^^^ -//│ ║ l.103: None then y -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.104: let y_square = y * y -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.105: Some(y_square) then 0 -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ h: (None | Some, int & 'a,) -> (0 | 'a) -//│ = [Function: h1] +//│ fun h: forall 'a. (None | Some[anything], Int & 'a) -> (0 | 'a) +:e fun f(a, y) = if a is Some(v) and v is @@ -123,22 +74,13 @@ fun f(a, y) = let y = v + 1 Right(x) then x + y else 0 -//│ ╔══[WARNING] old desugarer used -//│ ║ l.120: if a is -//│ ║ ^^^^ -//│ ║ l.121: Some(v) and v is -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.122: Left(x) then x -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.123: let y = v + 1 -//│ ║ ^^^^^^^^^^^^^^^^^^^ -//│ ║ l.124: Right(x) then x + y -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.125: else 0 -//│ ╙── ^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ f: (Some & {value: int} | ~Some, anything,) -> int -//│ = [Function: f1] +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.74: let y = v + 1 +//│ ║ ^^^^^ +//│ ╟── reference of type `Right[?B]` is not an instance of type `Int` +//│ ║ l.74: let y = v + 1 +//│ ╙── ^ +//│ fun f: forall 'a. (Object & ~#Some | Some[Int | Left['a] | Right[Int]], anything) -> (Int | 'a) :pe fun q(a) = @@ -147,117 +89,67 @@ fun q(a) = let y = a + 1 then y //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead -//│ ║ l.147: let y = a + 1 -//│ ║ ^^^^^ -//│ ║ l.148: then y -//│ ╙── ^^^^^^^^^^ -//│ ╔══[WARNING] old desugarer used -//│ ║ l.145: if a is -//│ ║ ^^^^ -//│ ║ l.146: Left(x) then x -//│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.147: let y = a + 1 -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.148: then y -//│ ╙── ^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ q: (Left & {leftValue: 'leftValue}) -> 'leftValue -//│ = [Function: q1] +//│ ║ l.89: let y = a + 1 +//│ ║ ^^^^^ +//│ ║ l.90: then y +//│ ╙── ^^^^^^^^^^ +//│ fun q: forall 'a. (Left['a] | Object & ~#Left) -> (() | 'a) class A() class B() -//│ Defined class A -//│ Defined class B -//│ A: () -> A -//│ = [Function: A1] -//│ B: () -> B -//│ = [Function: B1] +//│ class A() +//│ class B() +:e fun w() = if A then "A" let y = 0 B then "B" else "?" -//│ ╔══[WARNING] old desugarer used -//│ ║ l.178: A then "A" -//│ ║ ^^^^^^^^^^ -//│ ║ l.179: let y = 0 -//│ ║ ^^^^^^^^^^^^^ -//│ ║ l.180: B then "B" -//│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.181: else "?" -//│ ╙── ^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ w: () -> ("?" | "A" | "B") -//│ = [Function: w] +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.106: A then "A" +//│ ║ ^ +//│ ╙── reference of type `() -> A` is not an instance of type `Bool` +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.108: B then "B" +//│ ║ ^ +//│ ╙── reference of type `() -> B` is not an instance of type `Bool` +//│ fun w: () -> ("?" | "A" | "B") w() -//│ res: "?" | "A" | "B" -//│ = '?' +//│ "?" | "A" | "B" +//│ res +//│ = '?' fun i(x) = if x is A() then "A" let y = 0 B() then "B" -//│ ╔══[WARNING] old desugarer used -//│ ║ l.200: if x is -//│ ║ ^^^^ -//│ ║ l.201: A() then "A" -//│ ║ ^^^^^^^^^^^^^^^^ -//│ ║ l.202: let y = 0 -//│ ║ ^^^^^^^^^^^^^ -//│ ║ l.203: B() then "B" -//│ ╙── ^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ i: (A | B) -> ("A" | "B") -//│ = [Function: i] +//│ fun i: (A | B) -> ("A" | "B") fun inc(x) = x + 1 -//│ inc: int -> int -//│ = [Function: inc] +//│ fun inc: Int -> Int fun qq(x, z) = if x == let y = inc(z) y * y then 0 else 0 -//│ ╔══[WARNING] old desugarer used -//│ ║ l.222: if x == -//│ ║ ^^^^ -//│ ║ l.223: let y = inc(z) -//│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.224: y * y then 0 -//│ ║ ^^^^^^^^^^^^^^^^ -//│ ║ l.225: else 0 -//│ ╙── ^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ qq: (number, int,) -> 0 -//│ = [Function: qq] +//│ fun qq: (Num, Int) -> 0 fun bruh(x) = if x == 0 then 0 let y = 1 else y -//│ ╔══[WARNING] old desugarer used -//│ ║ l.241: x == 0 then 0 -//│ ║ ^^^^^^^^^^^^^ -//│ ║ l.242: let y = 1 -//│ ║ ^^^^^^^^^^^^^ -//│ ║ l.243: else y -//│ ╙── ^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ bruh: number -> (0 | 1) -//│ = [Function: bruh] +//│ fun bruh: Num -> (0 | 1) fun f1(x) = x + 1 fun f2(x, y) = x + y -//│ f1: int -> int -//│ = [Function: f11] -//│ f2: (int, int,) -> int -//│ = [Function: f2] +//│ fun f1: Int -> Int +//│ fun f2: (Int, Int) -> Int fun ff(x) = if @@ -267,246 +159,126 @@ fun ff(x) = z == 1 then 1 z == 2 then 2 else 0 -//│ ╔══[WARNING] old desugarer used -//│ ║ l.264: x == 0 then 0 -//│ ║ ^^^^^^^^^^^^^ -//│ ║ l.265: let y = f1(x) -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.266: let z = f2(x, y) -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.267: z == 1 then 1 -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.268: z == 2 then 2 -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.269: else 0 -//│ ╙── ^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ ff: int -> (0 | 1 | 2) -//│ = [Function: ff] +//│ fun ff: Int -> (0 | 1 | 2) +:e +// FIXME: Should warn missing else branches. fun ip(y) = if q(y) and let z = inc(y) y == z * z then "bruh" else "rocks" -//│ ╔══[WARNING] old desugarer used -//│ ║ l.288: if q(y) and -//│ ║ ^^^^^^^^ -//│ ║ l.289: let z = inc(y) -//│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.290: y == z * z then "bruh" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.291: else "rocks" -//│ ╙── ^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ ip: nothing -> ("bruh" | "rocks") -//│ = [Function: ip] +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.167: if q(y) and +//│ ║ ^^^^ +//│ ╟── undefined literal of type `()` is not an instance of type `Bool` +//│ ║ l.90: then y +//│ ║ ^ +//│ ╟── but it flows into application with expected type `Bool` +//│ ║ l.167: if q(y) and +//│ ╙── ^^^^ +//│ fun ip: Int -> ("bruh" | "rocks") fun tr(x) = if x is Some(v) then v let tmp = 1 None then tmp -//│ ╔══[WARNING] old desugarer used -//│ ║ l.306: if x is -//│ ║ ^^^^ -//│ ║ l.307: Some(v) then v -//│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.308: let tmp = 1 -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.309: None then tmp -//│ ╙── ^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ tr: (None | Some & {value: 'value}) -> (1 | 'value) -//│ = [Function: tr] - -class Pair(fst, snd) -class List -class Nil: List -class Cons(head, tail): List -//│ Defined class Pair -//│ Defined class List -//│ Defined class Nil -//│ Defined class Cons -//│ Pair: ('fst, 'snd,) -> (Pair & {fst: 'fst, snd: 'snd}) -//│ = [Function: Pair1] -//│ List: () -> List -//│ = [Function: List1] -//│ Nil: () -> Nil -//│ = [Function: Nil1] -//│ Cons: ('head, 'tail,) -> (Cons & {head: 'head, tail: 'tail}) -//│ = [Function: Cons1] - -fun cat2(s, t) = concat(s)(t) -fun cat3(a, b, c) = cat2(cat2(a, b), c) -//│ cat2: (string, string,) -> string -//│ = [Function: cat2] -//│ cat3: (string, string, string,) -> string -//│ = [Function: cat3] - -:js +//│ fun tr: forall 'a. (None | Some['a]) -> (1 | 'a) + +class Pair[A, B](fst: A, snd: B) +abstract class List[out A]: Nil | Cons[A] +module Nil extends List[nothing] +class Cons[out A](head: A, tail: List[A]) extends List[A] +//│ class Pair[A, B](fst: A, snd: B) +//│ abstract class List[A]: Cons[A] | Nil +//│ module Nil extends List +//│ class Cons[A](head: A, tail: List[A]) extends List + +fun (::) cons(h, t) = Cons(h, t) +fun (++) strcat(a, b) = concat(a)(b) +//│ fun (::) cons: forall 'A. ('A, List['A]) -> Cons['A] +//│ fun (++) strcat: (Str, Str) -> Str + fun showList(xs) = if xs is Nil then "" - Cons(head, Nil()) then toString(head) - Cons(head, tail) then cat3(toString(head), ", ", showList(tail)) -//│ // Prelude -//│ function toString(x) { -//│ return String(x); -//│ } -//│ // Query 1 -//│ globalThis.showList = function showList(xs) { -//│ return ((() => { -//│ let a; -//│ return (a = xs, a instanceof Nil ? "" : a instanceof Cons ? ((head) => ((tmp0) => ((tail) => tmp0 instanceof Nil ? toString(head) : cat3(toString(head), ", ", showList(tail)))(xs.tail))(xs.tail))(xs.head) : (() => { -//│ throw new Error("non-exhaustive case expression"); -//│ })()); -//│ })()); -//│ }; -//│ // End of generated code -//│ ╔══[WARNING] old desugarer used -//│ ║ l.349: if xs is -//│ ║ ^^^^^ -//│ ║ l.350: Nil then "" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.351: Cons(head, Nil()) then toString(head) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.352: Cons(head, tail) then cat3(toString(head), ", ", showList(tail)) -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ showList: (Cons & 'a | Nil) -> string -//│ where -//│ 'a <: {head: anything, tail: Cons & 'a | Nil} -//│ = [Function: showList] - -let zeroToThree = Cons(0, Cons(1, Cons(2, Cons(3, Nil())))) -//│ zeroToThree: Cons & { -//│ head: 0, -//│ tail: Cons & {head: 1, tail: Cons & {head: 2, tail: Cons & {head: 3, tail: Nil}}} -//│ } -//│ = Cons { -//│ head: 0, -//│ tail: Cons { head: 1, tail: Cons { head: 2, tail: [Cons] } } -//│ } + Cons(head, Nil) then toString(head) + Cons(head, tail) then toString(head) ++ ", " ++ showList(tail) +//│ fun showList: (Cons[anything] | Nil) -> Str + +let zeroToThree = 0 :: 1 :: 2 :: 3 :: Nil +//│ let zeroToThree: Cons[0 | 1 | 2 | 3] +//│ zeroToThree +//│ = Cons {} showList(zeroToThree) -//│ res: string -//│ = '0, 1, 2, 3' +//│ Str +//│ res +//│ = '0, 1, 2, 3' + +fun evenness(x) = if x % 2 is 0 then Left(x) else Right(x) +//│ fun evenness: forall 'A 'B. (Int & 'A & 'B) -> (Left['A] | Right['B]) fun mapPartition(f, xs) = if xs is - Nil then Pair(Nil(), Nil()) - Cons(x, xs) and f(x) is - let res = mapPartition(f, xs) - let l = res.fst - let r = res.snd - Left(v) then Pair(Cons(v, l), r) + Nil then Pair(Nil, Nil) + Cons(x, xs) and mapPartition(f, xs) is Pair(l, r) and f(x) is + Left(v) then Pair(Cons(v, l), r) Right(v) then Pair(l, Cons(v, r)) -//│ ╔══[WARNING] old desugarer used -//│ ║ l.397: if xs is -//│ ║ ^^^^^ -//│ ║ l.398: Nil then Pair(Nil(), Nil()) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.399: Cons(x, xs) and f(x) is -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.400: let res = mapPartition(f, xs) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.401: let l = res.fst -//│ ║ ^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.402: let r = res.snd -//│ ║ ^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.403: Left(v) then Pair(Cons(v, l), r) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.404: Right(v) then Pair(l, Cons(v, r)) -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ mapPartition: ('head -> (Left & {leftValue: 'leftValue} | Right & {rightValue: 'rightValue}), Cons & 'a | Nil,) -> (Pair & {fst: forall 'b 'c. Nil | 'c | 'b, snd: Nil | Cons & {head: 'rightValue, tail: Nil}}) +//│ fun mapPartition: forall 'A 'A0 'B 'A1 'a 'B0 'A2. ('a -> (Left['A2] | Right['A]), Cons['a] | Nil) -> Pair[in 'A0 & 'A1 out 'A1 | Cons['A2], in 'B0 & 'B out 'B | Cons['A]] //│ where -//│ 'b :> Cons & {head: 'leftValue, tail: forall 'd. Nil | 'd} -//│ 'c :> Cons & {head: 'leftValue, tail: forall 'd. Nil | 'd} -//│ 'a <: {head: 'head, tail: Cons & 'a | Nil} -//│ = [Function: mapPartition] - -mapPartition(x => (if x % 2 == 0 then Left(x) else Right(x)), zeroToThree) -//│ ╔══[WARNING] old desugarer used -//│ ║ l.430: mapPartition(x => (if x % 2 == 0 then Left(x) else Right(x)), zeroToThree) -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ res: Pair & { -//│ fst: forall 'a 'b. Nil | 'b | 'a, -//│ snd: Nil | Cons & {head: 0 | 1 | 2 | 3, tail: Nil} -//│ } -//│ where -//│ 'a :> Cons & {head: 0 | 1 | 2 | 3, tail: forall 'c. Nil | 'c} -//│ 'b :> Cons & {head: 0 | 1 | 2 | 3, tail: forall 'c. Nil | 'c} -//│ = Pair { -//│ fst: Cons { head: 0, tail: Cons { head: 2, tail: Nil {} } }, -//│ snd: Cons { head: 1, tail: Cons { head: 3, tail: Nil {} } } -//│ } +//│ 'B0 <: List['A] & 'B +//│ 'B :> Cons['A] | Nil +//│ <: 'B0 +//│ 'A0 <: List['A2] & 'A1 +//│ 'A1 :> Cons['A2] | Nil +//│ <: 'A0 + +if (mapPartition of evenness, zeroToThree) is + Pair(even, odd) then + log of showList(even) + log of showList(odd) +//│ () +//│ res +//│ = undefined +//│ // Output +//│ 0, 2 +//│ 1, 3 // This should be the desugaring of the above: -fun mapPartition2(f, xs) = +fun mapPartition'(f, xs) = if xs is - Nil then Pair(Nil(), Nil()) - Cons(x, xs) and mapPartition(f, xs) is res and res.fst is l and res.snd is r and f(x) is - Left(v) then Pair(Cons(v, l), r) - Right(v) then Pair(l, Cons(v, r)) -//│ ╔══[WARNING] old desugarer used -//│ ║ l.449: if xs is -//│ ║ ^^^^^ -//│ ║ l.450: Nil then Pair(Nil(), Nil()) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.451: Cons(x, xs) and mapPartition(f, xs) is res and res.fst is l and res.snd is r and f(x) is -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.452: Left(v) then Pair(Cons(v, l), r) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.453: Right(v) then Pair(l, Cons(v, r)) -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ mapPartition2: ('head -> (Left & {leftValue: 'leftValue} | Right & {rightValue: 'rightValue}) & 'head0 -> (Left & {leftValue: 'leftValue0} | Right & {rightValue: 'rightValue0}), Cons & {head: 'head0, tail: Cons & 'a | Nil} | Nil,) -> (Pair & { -//│ fst: forall 'b. Cons & { -//│ head: 'leftValue0, -//│ tail: forall 'c. Nil | 'c | Cons & {head: 'leftValue, tail: forall 'fst. Nil | 'fst} -//│ } | Nil | 'b | Cons & {head: 'leftValue, tail: forall 'fst0. Nil | 'fst0}, -//│ snd: Cons & {head: 'rightValue0, tail: Nil | Cons & {head: 'rightValue, tail: Nil}} | Nil | Cons & {head: 'rightValue, tail: Nil} -//│ }) -//│ where -//│ 'b :> Cons & {head: 'leftValue, tail: forall 'fst0. Nil | 'fst0} -//│ 'fst0 :> forall 'd. Nil | 'd -//│ 'd :> Cons & {head: 'leftValue, tail: forall 'fst0. Nil | 'fst0} -//│ 'c :> Cons & {head: 'leftValue, tail: forall 'fst. Nil | 'fst} -//│ 'fst :> forall 'e. Nil | 'e -//│ 'e :> Cons & {head: 'leftValue, tail: forall 'fst. Nil | 'fst} -//│ 'a <: {head: 'head, tail: Cons & 'a | Nil} -//│ = [Function: mapPartition2] - -mapPartition2(x => (if x % 2 == 0 then Left(x) else Right(x)), zeroToThree) -//│ ╔══[WARNING] old desugarer used -//│ ║ l.483: mapPartition2(x => (if x % 2 == 0 then Left(x) else Right(x)), zeroToThree) -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ res: Pair & { -//│ fst: forall 'a. Cons & { -//│ head: 0, -//│ tail: forall 'b. Nil | 'b | Cons & {head: 1 | 2 | 3, tail: forall 'fst. Nil | 'fst} -//│ } | Nil | 'a | Cons & {head: 1 | 2 | 3, tail: forall 'fst0. Nil | 'fst0}, -//│ snd: Cons & {head: 0, tail: Nil | Cons & {head: 1 | 2 | 3, tail: Nil}} | Nil | Cons & {head: 1 | 2 | 3, tail: Nil} -//│ } -//│ where -//│ 'a :> Cons & {head: 1 | 2 | 3, tail: forall 'fst0. Nil | 'fst0} -//│ 'fst0 :> forall 'c. Nil | 'c -//│ 'c :> Cons & {head: 1 | 2 | 3, tail: forall 'fst0. Nil | 'fst0} -//│ 'b :> Cons & {head: 1 | 2 | 3, tail: forall 'fst. Nil | 'fst} -//│ 'fst :> forall 'd. Nil | 'd -//│ 'd :> Cons & {head: 1 | 2 | 3, tail: forall 'fst. Nil | 'fst} -//│ = Pair { -//│ fst: Cons { head: 0, tail: Cons { head: 2, tail: Nil {} } }, -//│ snd: Cons { head: 1, tail: Cons { head: 3, tail: Nil {} } } -//│ } - -fun log(x) = () -//│ log: anything -> undefined -//│ = [Function: log] + Nil then Pair(Nil, Nil) + Cons(x, xs) then + let temp0 = mapPartition'(f, xs) + let temp1 = Pair.unapply(temp0) + let l = temp1.0 + let r = temp1.1 + if f(x) is + Left(v) then Pair(Cons(v, l), r) + Right(v) then Pair(l, Cons(v, r)) +//│ fun mapPartition': forall 'a 'A '#fst 'A0 'B 'A1 '#snd. ('a -> (Left['A1] | Right['A0]), Cons['a] | Nil) -> Pair[in '#fst & 'A out 'A | Cons['A1], in '#snd & 'B out 'B | Cons['A0]] +//│ where +//│ '#snd <: List['A0] & 'B +//│ 'B :> Cons['A0] | Nil +//│ <: '#snd +//│ '#fst <: List['A1] & 'A +//│ 'A :> Cons['A1] | Nil +//│ <: '#fst + +if (mapPartition' of evenness, zeroToThree) is + Pair(even, odd) then + log of showList(even) + log of showList(odd) +//│ () +//│ res +//│ = undefined +//│ // Output +//│ 0, 2 +//│ 1, 3 + +// This is a very interesting side-effect example! fun mn(a) = if a is @@ -518,41 +290,25 @@ fun mn(a) = 2 then "b is 3" Right(b) then "right-defined" None then "undefined" -//│ ╔══[WARNING] old desugarer used -//│ ║ l.512: if a is -//│ ║ ^^^^ -//│ ║ l.513: Some(x) and x is -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.514: Left(b) and b is -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.515: 0 then "b is 1" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.516: let _ = log(b) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.517: 1 then "b is 2" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.518: 2 then "b is 3" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.519: Right(b) then "right-defined" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.520: None then "undefined" -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ mn: (None | Some & {value: Left & {leftValue: 0 | 1 | 2} | Right}) -> ("b is 1" | "b is 2" | "b is 3" | "right-defined" | "undefined") -//│ = [Function: mn] - -mn(None()) -mn(Some(Left(0))) -mn(Some(Left(1))) -mn(Some(Left(2))) -mn(Some(Right(()))) -//│ res: "b is 1" | "b is 2" | "b is 3" | "right-defined" | "undefined" -//│ = 'undefined' -//│ res: "b is 1" | "b is 2" | "b is 3" | "right-defined" | "undefined" -//│ = 'b is 1' -//│ res: "b is 1" | "b is 2" | "b is 3" | "right-defined" | "undefined" -//│ = 'b is 2' -//│ res: "b is 1" | "b is 2" | "b is 3" | "right-defined" | "undefined" -//│ = 'b is 3' -//│ res: "b is 1" | "b is 2" | "b is 3" | "right-defined" | "undefined" -//│ = 'right-defined' +//│ fun mn: (None | Some[Left[0 | 1 | 2] | Right[anything]]) -> ("b is 1" | "b is 2" | "b is 3" | "right-defined" | "undefined") + +mn of None +mn of Some of Left of 0 +mn of Some of Left of 1 +mn of Some of Left of 2 +mn of Some of Right of () +//│ "b is 1" | "b is 2" | "b is 3" | "right-defined" | "undefined" +//│ res +//│ = 'undefined' +//│ res +//│ = 'b is 1' +//│ res +//│ = 'b is 2' +//│ // Output +//│ 1 +//│ res +//│ = 'b is 3' +//│ // Output +//│ 2 +//│ res +//│ = 'right-defined' diff --git a/shared/src/test/diff/ucs/NestedBranches.mls b/shared/src/test/diff/ucs/NestedBranches.mls index 5add0746..7cb7bd62 100644 --- a/shared/src/test/diff/ucs/NestedBranches.mls +++ b/shared/src/test/diff/ucs/NestedBranches.mls @@ -25,7 +25,10 @@ fun optionApply(x, y, f) = None then None //│ fun optionApply: forall 'a 'b 'A. (None | Some['a], None | Some['b], ('a, 'b) -> 'A) -> (None | Some['A]) -let zeroToThree = Cons(0, Cons(1, Cons(2, Cons(3, Nil)))) +fun (::) cons(h, t) = Cons(h, t) +//│ fun (::) cons: forall 'A. ('A, Cons['A] | Nil) -> Cons['A] + +let zeroToThree = 0 :: 1 :: 2 :: 3 :: Nil //│ let zeroToThree: Cons[0 | 1 | 2 | 3] //│ zeroToThree //│ = Cons {} @@ -33,61 +36,25 @@ let zeroToThree = Cons(0, Cons(1, Cons(2, Cons(3, Nil)))) fun f(x) = if x % 2 == 0 then Left(x) else Right(x) //│ fun f: forall 'A. (Int & 'A) -> (Left['A] | Right['A]) -// FIXME: Looks like a transformation bug. + fun mapPartition(f, xs) = if xs is Nil then Pair(Nil, Nil) Cons(x, xs) and mapPartition(f, xs) is Pair(l, r) and f(x) is Left(v) then Pair(Cons(v, l), r) Right(v) then Pair(l, Cons(v, r)) -//│ ╔══[ERROR] identifier `l` not found -//│ ║ l.40: Left(v) then Pair(Cons(v, l), r) -//│ ╙── ^ -//│ ╔══[ERROR] identifier `r` not found -//│ ║ l.40: Left(v) then Pair(Cons(v, l), r) -//│ ╙── ^ -//│ ╔══[ERROR] identifier `l` not found -//│ ║ l.41: Right(v) then Pair(l, Cons(v, r)) -//│ ╙── ^ -//│ ╔══[ERROR] identifier `r` not found -//│ ║ l.41: Right(v) then Pair(l, Cons(v, r)) -//│ ╙── ^ -//│ ╔══[ERROR] identifier not found: l -//│ ║ l.40: Left(v) then Pair(Cons(v, l), r) -//│ ╙── ^ -//│ ╔══[ERROR] identifier not found: r -//│ ║ l.40: Left(v) then Pair(Cons(v, l), r) -//│ ╙── ^ -//│ ╔══[ERROR] identifier not found: l -//│ ║ l.41: Right(v) then Pair(l, Cons(v, r)) -//│ ╙── ^ -//│ ╔══[ERROR] identifier not found: r -//│ ║ l.41: Right(v) then Pair(l, Cons(v, r)) -//│ ╙── ^ -//│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.39: Cons(x, xs) and mapPartition(f, xs) is Pair(l, r) and f(x) is -//│ ║ ^^^^^^^ -//│ ║ l.40: Left(v) then Pair(Cons(v, l), r) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.41: Right(v) then Pair(l, Cons(v, r)) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╙── expression of type `Bool` is not an instance of type `true` -//│ fun mapPartition: forall 'a 'A 'A0. ('a -> (Left['A] | Right['A0]), Cons['a] | Nil) -> Pair[Cons['A] | Nil | error, Cons['A0] | Nil | error] -//│ Code generation encountered an error: -//│ unresolved symbol l - -:re +//│ fun mapPartition: forall 'A 'A0 'a. ('a -> (Left['A] | Right['A0]), Cons['a] | Nil) -> Pair[Cons['A] | Nil, Cons['A0] | Nil] + + mapPartition(x => Left(x + 1), zeroToThree) -//│ Pair[Cons[Int] | Nil | error, Cons[nothing] | Nil | error] +//│ Pair[Cons[Int] | Nil, Cons[nothing] | Nil] //│ res -//│ Runtime error: -//│ ReferenceError: mapPartition is not defined +//│ = Pair {} + -:re mapPartition(f, zeroToThree) -//│ Pair[Cons[0 | 1 | 2 | 3] | Nil | error, Cons[0 | 1 | 2 | 3] | Nil | error] +//│ Pair[Cons[0 | 1 | 2 | 3] | Nil, Cons[0 | 1 | 2 | 3] | Nil] //│ res -//│ Runtime error: -//│ ReferenceError: mapPartition is not defined +//│ = Pair {} fun mapPartition(f, xs) = if xs is @@ -122,55 +89,18 @@ mapPartition(f, zeroToThree) //│ res //│ = Pair {} -:e // TODO make this one work (needs tuple support) fun mapPartition(f, xs) = if xs is Nil then [Nil, Nil] Cons(x, xs) and mapPartition(f, xs) is [l, r] and f(x) is Left(v) then [Cons(v, l), r] Right(v) then [l, Cons(v, r)] -//│ ╔══[ERROR] identifier `l` not found -//│ ║ l.129: Left(v) then [Cons(v, l), r] -//│ ╙── ^ -//│ ╔══[ERROR] identifier `r` not found -//│ ║ l.129: Left(v) then [Cons(v, l), r] -//│ ╙── ^ -//│ ╔══[ERROR] identifier `l` not found -//│ ║ l.130: Right(v) then [l, Cons(v, r)] -//│ ╙── ^ -//│ ╔══[ERROR] identifier `r` not found -//│ ║ l.130: Right(v) then [l, Cons(v, r)] -//│ ╙── ^ -//│ ╔══[ERROR] identifier not found: l -//│ ║ l.129: Left(v) then [Cons(v, l), r] -//│ ╙── ^ -//│ ╔══[ERROR] identifier not found: r -//│ ║ l.129: Left(v) then [Cons(v, l), r] -//│ ╙── ^ -//│ ╔══[ERROR] identifier not found: l -//│ ║ l.130: Right(v) then [l, Cons(v, r)] -//│ ╙── ^ -//│ ╔══[ERROR] identifier not found: r -//│ ║ l.130: Right(v) then [l, Cons(v, r)] -//│ ╙── ^ -//│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.128: Cons(x, xs) and mapPartition(f, xs) is [l, r] and f(x) is -//│ ║ ^^^^^^^ -//│ ║ l.129: Left(v) then [Cons(v, l), r] -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.130: Right(v) then [l, Cons(v, r)] -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╙── expression of type `Bool` is not an instance of type `true` -//│ fun mapPartition: forall 'a 'A 'A0. ('a -> (Left['A] | Right['A0]), Cons['a] | Nil) -> [Cons['A] | Nil | error, Cons['A0] | Nil | error] -//│ Code generation encountered an error: -//│ unresolved symbol l - -:re // TODO +//│ fun mapPartition: forall 'a 'A 'A0. ('a -> (Left['A] | Right['A0]), Cons['a] | Nil) -> [Cons['A] | Nil, Cons['A0] | Nil] + + mapPartition(f, zeroToThree) -//│ [Cons[0 | 1 | 2 | 3] | Nil | error, Cons[0 | 1 | 2 | 3] | Nil | error] +//│ [Cons[0 | 1 | 2 | 3] | Nil, Cons[0 | 1 | 2 | 3] | Nil] //│ res -//│ Runtime error: -//│ ReferenceError: mapPartition3 is not defined - +//│ = [ Cons {}, Cons {} ] // * Vertical alignment is not allowed! (good) :pe @@ -182,20 +112,20 @@ fun mapPartition(f, xs) = if xs is and f(x) is Left(v) then [Cons(v, l), r] Right(v) then [l, Cons(v, r)] //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here -//│ ║ l.183: Right(v) then [l, Cons(v, r)] +//│ ║ l.113: Right(v) then [l, Cons(v, r)] //│ ╙── ^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.182: and f(x) is Left(v) then [Cons(v, l), r] +//│ ║ l.112: and f(x) is Left(v) then [Cons(v, l), r] //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.183: Right(v) then [l, Cons(v, r)] +//│ ║ l.113: Right(v) then [l, Cons(v, r)] //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.182: and f(x) is Left(v) then [Cons(v, l), r] +//│ ║ l.112: and f(x) is Left(v) then [Cons(v, l), r] //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.183: Right(v) then [l, Cons(v, r)] +//│ ║ l.113: Right(v) then [l, Cons(v, r)] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── tuple literal of type `[?a, ?b]` is not a function -//│ ║ l.182: and f(x) is Left(v) then [Cons(v, l), r] +//│ ║ l.112: and f(x) is Left(v) then [Cons(v, l), r] //│ ╙── ^^^^^^^^^^^^^^^ //│ fun mapPartition: forall 'a. ('a -> Left[anything], Cons['a] | Nil) -> (error | [Nil, Nil]) diff --git a/shared/src/test/diff/ucs/OverlappedBranches.mls b/shared/src/test/diff/ucs/OverlappedBranches.mls index 657ca31c..627bb38d 100644 --- a/shared/src/test/diff/ucs/OverlappedBranches.mls +++ b/shared/src/test/diff/ucs/OverlappedBranches.mls @@ -1,63 +1,33 @@ -:NewParser +:PreTyper -class Base -class Derived1 extends Base -class Derived2 extends Base -class Derived3 extends Derived2 -//│ Defined class Base -//│ Defined class Derived1 -//│ Defined class Derived2 -//│ Defined class Derived3 -//│ Base: () -> Base -//│ = [Function: Base1] -//│ Derived1: () -> Derived1 -//│ = [Function: Derived11] -//│ Derived2: () -> Derived2 -//│ = [Function: Derived21] -//│ Derived3: () -> Derived3 -//│ = [Function: Derived31] +class Base() +class Derived1() extends Base() +class Derived2() extends Base() +class Derived3() extends Derived2() +//│ class Base() +//│ class Derived1() extends Base +//│ class Derived2() extends Base +//│ class Derived3() extends Base, Derived2 // The very basic case. -:w +// :w +// Should warn about that the last two cases are unreachable. fun f1(x) = if x is Base then "b" Derived1 then "d1" Derived2 then "d2" -//│ ╔══[WARNING] old desugarer used -//│ ║ l.22: fun f1(x) = if x is -//│ ║ ^^^^ -//│ ║ l.23: Base then "b" -//│ ║ ^^^^^^^^^^^^^^^^ -//│ ║ l.24: Derived1 then "d1" -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.25: Derived2 then "d2" -//│ ╙── ^^^^^^^^^^^^^^^^^^^^ -//│ ╔══[WARNING] Found a duplicated branch -//│ ╟── This branch -//│ ║ l.24: Derived1 then "d1" -//│ ║ ^^^^ -//│ ╟── is subsumed by the branch here. -//│ ║ l.23: Base then "b" -//│ ╙── ^^^ -//│ ╔══[WARNING] Found a duplicated branch -//│ ╟── This branch -//│ ║ l.25: Derived2 then "d2" -//│ ║ ^^^^ -//│ ╟── is subsumed by the branch here. -//│ ║ l.23: Base then "b" -//│ ╙── ^^^ -//│ f1: Base -> "b" -//│ = [Function: f1] +//│ fun f1: Base -> "b" f1(Base()) f1(Derived1()) f1(Derived2()) -//│ res: "b" -//│ = 'b' -//│ res: "b" -//│ = 'b' -//│ res: "b" -//│ = 'b' +//│ "b" +//│ res +//│ = 'b' +//│ res +//│ = 'b' +//│ res +//│ = 'b' // Decision paths: // + «x is Base» and «p (x,)» => "b and p" @@ -84,38 +54,28 @@ fun f2(x, p) = if x is Derived1 then "d1" Derived2 then "d2" else "otherwise" -//│ ╔══[WARNING] old desugarer used -//│ ║ l.82: fun f2(x, p) = if x is -//│ ║ ^^^^ -//│ ║ l.83: Base and p(x) then "b and p" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.84: Derived1 then "d1" -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.85: Derived2 then "d2" -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.86: else "otherwise" -//│ ╙── ^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ f2: (Base & 'a | ~Base, 'a -> anything,) -> ("b and p" | "d1" | "d2" | "otherwise") -//│ = [Function: f2] +//│ fun f2: forall 'a. ('a & (Base & ~#Derived1 | Derived1), (Base & 'a) -> Bool) -> ("b and p" | "d1" | "d2" | "otherwise") f2(Base(), _ => true) // => b and p f2(Base(), _ => false) // otherwise -//│ res: "b and p" | "d1" | "d2" | "otherwise" -//│ = 'b and p' -//│ res: "b and p" | "d1" | "d2" | "otherwise" -//│ = 'otherwise' +//│ "b and p" | "d1" | "d2" | "otherwise" +//│ res +//│ = 'b and p' +//│ res +//│ = 'otherwise' f2(Derived1(), _ => true) // => b and p f2(Derived2(), _ => true) // => b and p -//│ res: "b and p" | "d1" | "d2" | "otherwise" -//│ = 'b and p' -//│ res: "b and p" | "d1" | "d2" | "otherwise" -//│ = 'b and p' +//│ "b and p" | "d1" | "d2" | "otherwise" +//│ res +//│ = 'b and p' +//│ res +//│ = 'b and p' f2(Derived1(), _ => false) // => d1 f2(Derived2(), _ => false) // => d2 -//│ res: "b and p" | "d1" | "d2" | "otherwise" -//│ = 'd1' -//│ res: "b and p" | "d1" | "d2" | "otherwise" -//│ = 'd2' +//│ "b and p" | "d1" | "d2" | "otherwise" +//│ res +//│ = 'd1' +//│ res +//│ = 'd2' diff --git a/todo.md b/todo.md index cf7fac18..0ecfaa74 100644 --- a/todo.md +++ b/todo.md @@ -55,7 +55,7 @@ problems located by test cases. - [x] shared/src/test/diff/nu/LitMatch.mls - [x] shared/src/test/diff/nu/MissingTypeArg.mls - [x] shared/src/test/diff/nu/NamedArgs.mls -- [ ] shared/src/test/diff/nu/New.mls **OLD** +- [x] shared/src/test/diff/nu/New.mls **OLD** - [x] shared/src/test/diff/nu/NewNew.mls - [x] shared/src/test/diff/nu/Object.mls - [x] shared/src/test/diff/nu/OpLam.mls @@ -73,7 +73,7 @@ problems located by test cases. - [x] shared/src/test/diff/nu/UndefMatching.mls - [x] shared/src/test/diff/nu/WeirdUnions.mls - [x] shared/src/test/diff/nu/i180.mls -- [ ] shared/src/test/diff/nu/repro0.mls +- [x] shared/src/test/diff/nu/repro0.mls - `PreTyper` does not accept top-level `val` bindings. - [x] shared/src/test/diff/nu/repro1.mls - [x] shared/src/test/diff/nu/repro_EvalNegNeg.mls @@ -86,27 +86,43 @@ problems located by test cases. - [x] shared/src/test/diff/ucs/CrossBranchCapture.mls Fix the mentioned problems. TODO: Warn duplicated pattern bindings. -- [ ] shared/src/test/diff/ucs/DirectLines.mls **OLD** +- [x] shared/src/test/diff/ucs/DirectLines.mls **OLD** + TODO: Warn duplicated else branches. - [x] shared/src/test/diff/ucs/ElseIf.mls -- [ ] shared/src/test/diff/ucs/ErrorMessage.mls **OLD** +- [x] shared/src/test/diff/ucs/ErrorMessage.mls **OLD** - [x] shared/src/test/diff/ucs/Exhaustiveness.mls -- [ ] shared/src/test/diff/ucs/Humiliation.mls **OLD** +- [x] shared/src/test/diff/ucs/Humiliation.mls **OLD** + TODO: Improve scrutinee name display. + Generated names should be polished. - [x] shared/src/test/diff/ucs/Hygiene.mls Problem fixed! - [ ] shared/src/test/diff/ucs/HygienicBindings.mls -- [ ] shared/src/test/diff/ucs/InterleavedLet.mls **OLD** + We should fix the shadowing parameters. +- [x] shared/src/test/diff/ucs/InterleavedLet.mls **OLD** + The transformation cannot handle the following case. + ``` + fun mapPartition2(f, xs) = + if xs is + Nil then Pair(Nil, Nil) + Cons(x, xs) and mapPartition(f, xs) is Pair(l, r) and f(x) is + Left(v) then Pair(Cons(v, l), r) + Right(v) then Pair(l, Cons(v, r)) + ``` + To be specific, `Cons(x, xs) and mapPartition(f, xs) is Pair(l, r)` are + not separated. I re-implemented `splitAnd` function. - [x] shared/src/test/diff/ucs/JSON.mls Deleted. This one is not completed and we have a new version. - [x] shared/src/test/diff/ucs/LeadingAnd.mls - [x] shared/src/test/diff/ucs/LitUCS.mls - [x] shared/src/test/diff/ucs/MultiwayIf.mls -- [ ] shared/src/test/diff/ucs/NestedBranches.mls +- [x] shared/src/test/diff/ucs/NestedBranches.mls Found a bug in transformation. - [x] shared/src/test/diff/ucs/NestedOpSplits.mls - [x] shared/src/test/diff/ucs/NestedPattern.mls - [x] shared/src/test/diff/ucs/NuPlainConditionals.mls - [x] shared/src/test/diff/ucs/Or.mls -- [ ] shared/src/test/diff/ucs/OverlappedBranches.mls **OLD** +- [x] shared/src/test/diff/ucs/OverlappedBranches.mls **OLD** + Should report unreachable cases. - [x] shared/src/test/diff/ucs/ParseFailures.mls - [x] shared/src/test/diff/ucs/PlainConditionals.mls Maybe we should keep this old one... @@ -115,7 +131,7 @@ problems located by test cases. Remove a `???` and raise error during transformation. - [x] shared/src/test/diff/ucs/SplitAfterOp.mls Wrap tests in functions so that errors are clearer. -- [ ] shared/src/test/diff/ucs/SplitAnd.mls +- [x] shared/src/test/diff/ucs/SplitAnd.mls Should report missing else branches. - [x] shared/src/test/diff/ucs/SplitAroundOp.mls - [x] shared/src/test/diff/ucs/SplitBeforeOp.mls From 4154157e486d0c6f606c6b719fd0d077a096ceb9 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sun, 14 Jan 2024 20:16:12 +0800 Subject: [PATCH 061/143] Clean up the last tests --- .../scala/mlscript/pretyper/PreTyper.scala | 41 +++--- shared/src/test/diff/ucs/TrivialIf.mls | 132 +++++++----------- shared/src/test/diff/ucs/WeirdSplit.mls | 71 +++------- shared/src/test/diff/ucs/Wildcard.mls | 32 +---- todo.md | 26 +++- 5 files changed, 107 insertions(+), 195 deletions(-) diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index 903b216a..a247e7fa 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -264,23 +264,7 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D trace(s"process <== $name: ${typingUnit.describe}") { traverseStatements(typingUnit.entities, name, scope) }({ case (scope, contents) => s"process ==> ${scope.showLocalSymbols}" }) -} - -object PreTyper { - - def extractSuperTypes(parents: Ls[Term]): Ls[Var] = { - @tailrec - def rec(results: Ls[Var], waitlist: Ls[Term]): Ls[Var] = - waitlist match { - case Nil => results.reverse - case (nme: Var) :: tail => rec(nme :: results, tail) - case (TyApp(ty, _)) :: tail => rec(results, ty :: tail) - case (App(term, Tup(_))) :: tail => rec(results, term :: tail) - case other :: _ => println(s"Unknown parent type: $other"); ??? - } - rec(Nil, parents) - } - + /** * Extract types in class signatures. For example, for this piece of code * ```mls @@ -291,17 +275,36 @@ object PreTyper { * @param ty a type obtained from `NuTypeDef.sig` * @return a list of type names, without any p */ - def extractSignatureTypes(ty: Type): Ls[TypeName] = { + private def extractSignatureTypes(ty: Type): Ls[TypeName] = { @tailrec def rec(acc: Ls[TypeName], ty: Type): Ls[TypeName] = ty match { case tn: TypeName => tn :: acc case AppliedType(tn: TypeName, _) => tn :: acc case Union(lhs, tn: TypeName) => rec(tn :: acc, lhs) case Union(lhs, AppliedType(tn: TypeName, _)) => rec(tn :: acc, lhs) - case other => println(s"Unknown type in signature: $other"); ??? + case other => + // Let's not raise warning for now. + // raiseWarning(msg"unknown type in signature" -> other.toLoc) + Nil } rec(Nil, ty).reverse } +} + +object PreTyper { + + def extractSuperTypes(parents: Ls[Term]): Ls[Var] = { + @tailrec + def rec(results: Ls[Var], waitlist: Ls[Term]): Ls[Var] = + waitlist match { + case Nil => results.reverse + case (nme: Var) :: tail => rec(nme :: results, tail) + case (TyApp(ty, _)) :: tail => rec(results, ty :: tail) + case (App(term, Tup(_))) :: tail => rec(results, term :: tail) + case other :: _ => println(s"Unknown parent type: $other"); ??? + } + rec(Nil, parents) + } def transitiveClosure[A](graph: Map[A, List[A]])(implicit ord: Ordering[A]): SortedMap[A, List[A]] = { def dfs(vertex: A, visited: Set[A]): Set[A] = { diff --git a/shared/src/test/diff/ucs/TrivialIf.mls b/shared/src/test/diff/ucs/TrivialIf.mls index d8cc5fcc..555b4943 100644 --- a/shared/src/test/diff/ucs/TrivialIf.mls +++ b/shared/src/test/diff/ucs/TrivialIf.mls @@ -1,130 +1,92 @@ -:NewParser +:PreTyper :NoJS fun abs(x) = if x < 0 then 0 - x else x -//│ ╔══[WARNING] old desugarer used -//│ ║ l.4: fun abs(x) = if x < 0 then 0 - x else x -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ abs: int -> int +//│ fun abs: Int -> Int -class Option -class Some(value): Option -class None: Option -//│ Defined class Option -//│ Defined class Some -//│ Defined class None -//│ Option: () -> Option -//│ Some: 'value -> (Some & {value: 'value}) -//│ None: () -> None +abstract class Option[A]: Some[A] | None +class Some[A](value: A) extends Option[A] +module None extends Option +//│ abstract class Option[A]: None | Some[A] +//│ class Some[A](value: A) extends Option +//│ module None extends Option fun getOrElse(opt, default) = if opt is Some(value) then value None then default -//│ ╔══[WARNING] old desugarer used -//│ ║ l.22: if opt is -//│ ║ ^^^^^^ -//│ ║ l.23: Some(value) then value -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.24: None then default -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ getOrElse: (None | Some & {value: 'value}, 'value,) -> 'value +//│ fun getOrElse: forall 'a. (None | Some['a], 'a) -> 'a -getOrElse(None(), 0) -//│ res: 0 +getOrElse(None, 0) +//│ 0 getOrElse(Some(42), 0) -//│ res: 0 | 42 +//│ 0 | 42 fun map(v, f) = if v is Some(x) then Some(f(x)) - None then None() -//│ ╔══[WARNING] old desugarer used -//│ ║ l.42: if v is -//│ ║ ^^^^ -//│ ║ l.43: Some(x) then Some(f(x)) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.44: None then None() -//│ ╙── ^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ map: (None | Some & {value: 'value}, 'value -> 'value0,) -> (None | Some & {value: 'value0}) + None then None +//│ fun map: forall 'a 'A. (None | Some['a], 'a -> 'A) -> (None | Some['A]) fun inc(x) = x + 5 -//│ inc: int -> int +//│ fun inc: Int -> Int map(Some(5), x => x + 5) -//│ res: None | Some & {value: int} +//│ None | Some['A] +//│ where +//│ 'A :> Int -map(None(), inc) -//│ res: None | Some & {value: int} +map(None, inc) +//│ None | Some['A] +//│ where +//│ 'A :> Int :e fun f(a, b) = if a and b then 0 -//│ ╔══[WARNING] old desugarer used -//│ ║ l.65: fun f(a, b) = if a and b then 0 -//│ ╙── ^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ ╔══[ERROR] The case when this is false is not handled: a -//│ ║ l.65: fun f(a, b) = if a and b then 0 -//│ ╙── ^ -//│ f: (anything, anything,) -> error +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.46: fun f(a, b) = if a and b then 0 +//│ ║ ^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.46: fun f(a, b) = if a and b then 0 +//│ ║ ^^^^^^^^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ fun f: (Bool, Bool) -> 0 :e fun f(x, y) = if x == y + 5 then 0 else if x == y + 7 then 0 -//│ ╔══[WARNING] old desugarer used -//│ ║ l.77: if x == y + 5 then 0 -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.78: else if x == y + 7 then 0 -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ ╔══[ERROR] The case when this is false is not handled: ==(x,)(+(y,)(7,),) -//│ ║ l.78: else if x == y + 7 then 0 -//│ ╙── ^^^^^^^^^^ -//│ f: (anything, anything,) -> error +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.60: else if x == y + 7 then 0 +//│ ║ ^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ fun f: (Num, Int) -> 0 // TODO support fun foo(x) = if x is Some (0) then 0 (1) then 1 //│ ╔══[PARSE ERROR] Unexpected parenthesis section here -//│ ║ l.93: (1) then 1 +//│ ║ l.70: (1) then 1 //│ ╙── ^^^ -//│ ╔══[WARNING] old desugarer used -//│ ║ l.91: fun foo(x) = if x is Some -//│ ║ ^^^^^^^^^ -//│ ║ l.92: (0) then 0 -//│ ╙── ^^^^^^^^^^^^ -//│ ╔══[ERROR] The case when this is false is not handled: is(x,)(Some,)(0,) -//│ ║ l.91: fun foo(x) = if x is Some -//│ ║ ^^^^^^^^^ -//│ ║ l.92: (0) then 0 -//│ ╙── ^^^^^ -//│ foo: anything -> error +//│ /!!!\ Uncaught error: java.lang.StackOverflowError // TODO support fun foo(x) = if x is Some of 0 then 0 1 then 1 //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here -//│ ║ l.111: 0 then 0 -//│ ╙── ^^^^ +//│ ║ l.79: 0 then 0 +//│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead -//│ ║ l.110: fun foo(x) = if x is Some of -//│ ║ ^^^^^^^^^^^^ -//│ ║ l.111: 0 then 0 -//│ ║ ^^^ +//│ ║ l.78: fun foo(x) = if x is Some of +//│ ║ ^^^^^^^^^^^^ +//│ ║ l.79: 0 then 0 +//│ ║ ^^^ //│ ╟── Note: 'if' expression starts here: -//│ ║ l.110: fun foo(x) = if x is Some of -//│ ╙── ^^ -//│ ╔══[WARNING] old desugarer used -//│ ║ l.110: fun foo(x) = if x is Some of -//│ ║ ^^^^^^^^^^^^ -//│ ║ l.111: 0 then 0 -//│ ╙── ^^^ -//│ foo: (Some & {value: 0}) -> undefined +//│ ║ l.78: fun foo(x) = if x is Some of +//│ ╙── ^^ +//│ fun foo: Some[0] -> () diff --git a/shared/src/test/diff/ucs/WeirdSplit.mls b/shared/src/test/diff/ucs/WeirdSplit.mls index e74445a1..259ec793 100644 --- a/shared/src/test/diff/ucs/WeirdSplit.mls +++ b/shared/src/test/diff/ucs/WeirdSplit.mls @@ -1,31 +1,16 @@ -:NewParser +:PreTyper class A() class B() -//│ Defined class A -//│ Defined class B -//│ A: () -> A -//│ = [Function: A1] -//│ B: () -> B -//│ = [Function: B1] +//│ class A() +//│ class B() fun f(x) = if x is A then 0 B then 1 -//│ ╔══[WARNING] old desugarer used -//│ ║ l.13: if x -//│ ║ ^ -//│ ║ l.14: is -//│ ║ ^^^^^^ -//│ ║ l.15: A then 0 -//│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.16: B then 1 -//│ ╙── ^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ f: (A | B) -> (0 | 1) -//│ = [Function: f] +//│ fun f: (A | B) -> (0 | 1) // Precedence problem: should we restruct terms when push them to the stack? :e @@ -34,30 +19,19 @@ fun f(x) = 1 + 2 then 0 + _ then 1 -//│ ╔══[WARNING] old desugarer used -//│ ║ l.33: if x == +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.20: + 2 then 0 +//│ ║ ^^^ +//│ ╟── operator application of type `Bool` is not an instance of type `Int` +//│ ║ l.18: if x == //│ ║ ^^^^ -//│ ║ l.34: 1 -//│ ║ ^^^^^^ -//│ ║ l.35: + 2 then 0 -//│ ║ ^^^^^^^^^^^^^^^^ -//│ ║ l.36: + _ then 1 -//│ ╙── ^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ ╔══[ERROR] Type mismatch in operator application: -//│ ║ l.33: if x == -//│ ║ ^^^^ -//│ ║ l.34: 1 -//│ ║ ^^^^^^ -//│ ║ l.35: + 2 then 0 -//│ ║ ^^^^^^^ -//│ ╟── operator application of type `bool` is not an instance of type `int` -//│ ║ l.33: if x == -//│ ║ ^^^^ -//│ ║ l.34: 1 +//│ ║ l.19: 1 //│ ╙── ^^^^^^ -//│ f: number -> (0 | 1) -//│ = [Function: f1] +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.20: + 2 then 0 +//│ ║ ^^^ +//│ ╙── application of type `Int` is not an instance of type `Bool` +//│ fun f: Num -> (0 | 1) fun f(x, s, t) = if x @@ -65,17 +39,4 @@ fun f(x, s, t) = and t then 0 and s then 0 is _ then 1 -//│ ╔══[WARNING] old desugarer used -//│ ║ l.63: if x -//│ ║ ^ -//│ ║ l.64: is A() -//│ ║ ^^^^^^^^^^ -//│ ║ l.65: and t then 0 -//│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.66: and s then 0 -//│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.67: is _ then 1 -//│ ╙── ^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected warning -//│ f: (anything, anything, anything,) -> (0 | 1) -//│ = [Function: f2] +//│ fun f: (Object, Bool, Bool) -> (0 | 1) diff --git a/shared/src/test/diff/ucs/Wildcard.mls b/shared/src/test/diff/ucs/Wildcard.mls index 17d9db40..ede97537 100644 --- a/shared/src/test/diff/ucs/Wildcard.mls +++ b/shared/src/test/diff/ucs/Wildcard.mls @@ -23,41 +23,13 @@ fun w1(x, e_0, e_1) = Left(Some(lv)) then concat("Left of Some of ")(toString(lv)) _ and e_1 is y_1 and x is Right(Some(rv)) then concat("Right of Some of ")(toString(rv)) -//│ ╔══[ERROR] Found unexpected empty split -//│ ╙── -//│ ╔══[ERROR] Found unexpected empty split -//│ ╙── -//│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ╙── expression of type `Bool` is not an instance of type `true` -//│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.23: Left(Some(lv)) then concat("Left of Some of ")(toString(lv)) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.24: _ and e_1 is y_1 and x is -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ -//│ ╙── expression of type `Bool` is not an instance of type `true` -//│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.25: Right(Some(rv)) then concat("Right of Some of ")(toString(rv)) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╙── expression of type `Bool` is not an instance of type `true` -//│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.24: _ and e_1 is y_1 and x is -//│ ║ ^^^^^^^^^^^^^^^^^^^ -//│ ║ l.25: Right(Some(rv)) then concat("Right of Some of ")(toString(rv)) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╙── expression of type `Bool` is not an instance of type `true` -//│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ╙── expression of type `Bool` is not an instance of type `true` -//│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.24: _ and e_1 is y_1 and x is -//│ ║ ^^^^^^^^^^ -//│ ╙── expression of type `Bool` is not an instance of type `true` -//│ fun w1: (Left[None | Object & ~#None & ~#Some | Some[anything]] | Object & ~#Left & ~#Right | Right[None | Some[anything]], anything, anything) -> (Str | error) +//│ fun w1: forall 'a. (Left[None | Object & ~#None & ~#Some | Some[anything]] | Object & ~#Left & ~#Right | Right[None | Some[anything]], anything, 'a) -> (Str | 'a) w1(Left(None), "a", "b") w1(Right(None), "a", "b") w1(Left(Some(0)), "a", "b") w1(Right(Some(0)), "a", "b") -//│ Str | error +//│ Str //│ res //│ = 'Left of None' //│ res diff --git a/todo.md b/todo.md index 0ecfaa74..8b42a7f2 100644 --- a/todo.md +++ b/todo.md @@ -1,6 +1,19 @@ This file will be deleted after we migrate all test cases and fixed all problems located by test cases. +### Remaining Tasks + +- Report redundant cases + - shared/src/test/diff/ucs/CrossBranchCapture.mls + - shared/src/test/diff/ucs/DirectLines.mls **OLD** + - shared/src/test/diff/ucs/SplitAnd.mls + - shared/src/test/diff/ucs/WeirdIf.mls + - shared/src/test/diff/ucs/Wildcard.mls +- Hygenic bindings + - shared/src/test/diff/ucs/HygienicBindings.mls + +### Test Checklist + - [x] shared/src/test/diff/codegen/AuxiliaryConstructors.mls - [x] shared/src/test/diff/codegen/Mixin.mls Fix that `PreTyper` does not traverse type definitions. @@ -96,7 +109,7 @@ problems located by test cases. Generated names should be polished. - [x] shared/src/test/diff/ucs/Hygiene.mls Problem fixed! -- [ ] shared/src/test/diff/ucs/HygienicBindings.mls +- [x] shared/src/test/diff/ucs/HygienicBindings.mls We should fix the shadowing parameters. - [x] shared/src/test/diff/ucs/InterleavedLet.mls **OLD** The transformation cannot handle the following case. @@ -140,10 +153,11 @@ problems located by test cases. Fixed. - [x] shared/src/test/diff/ucs/ThenIndent.mls - [x] shared/src/test/diff/ucs/Tree.mls -- [ ] shared/src/test/diff/ucs/TrivialIf.mls **OLD** -- [ ] shared/src/test/diff/ucs/WeirdIf.mls +- [x] shared/src/test/diff/ucs/TrivialIf.mls **OLD** +- [x] shared/src/test/diff/ucs/WeirdIf.mls Should report redundant cases. -- [ ] shared/src/test/diff/ucs/WeirdSplit.mls **OLD** -- [ ] shared/src/test/diff/ucs/Wildcard.mls +- [x] shared/src/test/diff/ucs/WeirdSplit.mls **OLD** +- [x] shared/src/test/diff/ucs/Wildcard.mls Some unexpected empty splits. -- [x] shared/src/test/diff/ucs/zipWith.mls \ No newline at end of file +- [x] shared/src/test/diff/ucs/zipWith.mls + From f6fd0c56cdb248ca390caa1a0e96c04cb085c9f9 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sun, 14 Jan 2024 21:44:28 +0800 Subject: [PATCH 062/143] Fix unhygienic let bindings --- shared/src/main/scala/mlscript/ucs/core.scala | 1 - .../mlscript/ucs/stages/Normalization.scala | 62 ++++-- shared/src/test/diff/ucs/HygienicBindings.mls | 187 ++++++++++-------- todo.md | 4 +- 4 files changed, 153 insertions(+), 101 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/core.scala b/shared/src/main/scala/mlscript/ucs/core.scala index a41f4d63..70633056 100644 --- a/shared/src/main/scala/mlscript/ucs/core.scala +++ b/shared/src/main/scala/mlscript/ucs/core.scala @@ -78,7 +78,6 @@ package object core { override lazy val freeVars: Set[Var] = this match { case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => - // FIXME: It is safe to ignore `pattern` for now. continuation.freeVars ++ tail.freeVars case Split.Let(true, nme, rhs, tail) => tail.freeVars ++ rhs.freeVars - nme case Split.Let(false, nme, rhs, tail) => tail.freeVars - nme ++ rhs.freeVars diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index d71b3243..95032eae 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -29,6 +29,7 @@ trait Normalization { self: DesugarUCS with Traceable => these.copy(head = newHead, tail = fillImpl(tail, those, deep)) } case these @ Split.Let(_, nme, _, tail) => + println(s"fill let binding ${nme.name}") if (those.freeVars contains nme) { val fresh = context.freshShadowed() val thoseWithShadowed = Split.Let(false, nme, fresh, those) @@ -69,23 +70,31 @@ trait Normalization { self: DesugarUCS with Traceable => * @param split the split to normalize * @return the normalized term */ - @inline protected def normalize(split: Split)(implicit context: Context): Term = normalizeToTerm(split)(context, Set.empty) + @inline protected def normalize(split: Split)(implicit + scope: Scope, + context: Context + ): Term = normalizeToTerm(split)(scope, context, Set.empty) private def errorTerm: Term = Var("error") - private def normalizeToTerm(split: Split)(implicit context: Context, generatedVars: Set[Var]): Term = trace("normalizeToTerm <==") { + private def normalizeToTerm(split: Split)(implicit + scope: Scope, + context: Context, + generatedVars: Set[Var], + ): Term = trace("normalizeToTerm <==") { split match { case Split.Cons(Branch(scrutinee, Pattern.Name(nme), continuation), tail) => - println(s"normalizing name pattern ${scrutinee.name} is ${nme.name}") - Let(false, nme, scrutinee, normalizeToTerm(continuation.fill(tail, deep = false))) + println(s"ALIAS: ${scrutinee.name} is ${nme.name}") + val (wrap, realTail) = preventShadowing(nme, tail) + wrap(Let(false, nme, scrutinee, normalizeToTerm(continuation.fill(realTail, deep = false)))) // Skip Boolean conditions as scrutinees, because they only appear once. case Split.Cons(Branch(test, pattern @ Pattern.Class(nme @ Var("true"), _), continuation), tail) => - println(s"normalizing true pattern: ${test.name} is true") + println(s"TRUE: ${test.name} is true") val trueBranch = normalizeToTerm(continuation.fill(tail, deep = false)) val falseBranch = normalizeToCaseBranches(tail) CaseOf(test, Case(nme, trueBranch, falseBranch)(refined = false)) case Split.Cons(Branch(ScrutineeData.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Literal(literal), continuation), tail) => - println(s"normalizing literal pattern: ${scrutineeVar.name} is ${literal.idStr}") + println(s"LITERAL: ${scrutineeVar.name} is ${literal.idStr}") println(s"entire split: ${showSplit(split)}") val concatenatedTrueBranch = continuation.fill(tail, deep = false) // println(s"true branch: ${showSplit(concatenatedTrueBranch)}") @@ -94,31 +103,37 @@ trait Normalization { self: DesugarUCS with Traceable => val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context)) CaseOf(scrutineeVar, Case(literal, trueBranch, falseBranch)(refined = false)) case Split.Cons(Branch(ScrutineeData.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Class(nme, rfd), continuation), tail) => - println(s"normalizing class pattern: ${scrutineeVar.name} is ${nme.name}") + println(s"CLASS: ${scrutineeVar.name} is ${nme.name}") // println(s"match ${scrutineeVar.name} with $nme (has location: ${nme.toLoc.isDefined})") val trueBranch = normalizeToTerm(specialize(continuation.fill(tail, deep = false), true)(scrutineeVar, scrutinee, pattern, context)) val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context)) CaseOf(scrutineeVar, Case(nme, trueBranch, falseBranch)(refined = pattern.refined)) case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => - raiseError(msg"Unsupported pattern: ${pattern.toString}" -> pattern.toLoc) + raiseError(msg"unsupported pattern: ${pattern.toString}" -> pattern.toLoc) errorTerm case Split.Let(_, nme, _, tail) if context.isScrutineeVar(nme) && generatedVars.contains(nme) => - println(s"normalizing let binding of generated variable: ${nme.name}") + println(s"LET: SKIP already declared scrutinee ${nme.name}") normalizeToTerm(tail) + case Split.Let(rec, nme, rhs, tail) if context.isGeneratedVar(nme) => + println(s"LET: generated ${nme.name}") + Let(rec, nme, rhs, normalizeToTerm(tail)(scope, context, generatedVars + nme)) case Split.Let(rec, nme, rhs, tail) => - println(s"normalizing let binding ${nme.name}") - val newDeclaredBindings = if (context.isGeneratedVar(nme)) generatedVars + nme else generatedVars - Let(rec, nme, rhs, normalizeToTerm(tail)(context, newDeclaredBindings)) + println(s"LET: ${nme.name}") + Let(rec, nme, rhs, normalizeToTerm(tail)) case Split.Else(default) => - println(s"normalizing default: ${default.showDbg}") + println(s"DFLT: ${default.showDbg}") default case Split.Nil => - raiseError(msg"Found unexpected empty split" -> N) + raiseError(msg"unexpected empty split found" -> N) errorTerm } }(split => "normalizeToTerm ==> " + showNormalizedTerm(split)) - private def normalizeToCaseBranches(split: Split)(implicit context: Context, generatedVars: Set[Var]): CaseBranches = + private def normalizeToCaseBranches(split: Split)(implicit + scope: Scope, + context: Context, + generatedVars: Set[Var] + ): CaseBranches = trace(s"normalizeToCaseBranches <==") { split match { // case Split.Cons(head, Split.Nil) => Case(head.pattern, normalizeToTerm(head.continuation), NoCases) @@ -127,7 +142,7 @@ trait Normalization { self: DesugarUCS with Traceable => normalizeToCaseBranches(tail) case Split.Let(rec, nme, rhs, tail) => val newDeclaredBindings = if (context.isGeneratedVar(nme)) generatedVars + nme else generatedVars - normalizeToCaseBranches(tail)(context, newDeclaredBindings) match { + normalizeToCaseBranches(tail)(scope, context, newDeclaredBindings) match { case NoCases => Wildcard(rhs) case Wildcard(term) => Wildcard(Let(rec, nme, rhs, term)) case _: Case => die @@ -284,6 +299,21 @@ trait Normalization { self: DesugarUCS with Traceable => } }() // }(showSplit(s"S${if (matchOrNot) "+" else "-"} ==>", _)) + + /** + * If you want to prepend `tail` to another `Split` where the `nme` takes + * effects, you should use this function to prevent shadowing. + */ + private def preventShadowing(nme: Var, tail: Split)(implicit + scope: Scope, + context: Context + ): (Term => Term, Split) = scope.getTermSymbol(nme.name) match { + case S(symbol) if tail.freeVars contains nme => + val stashVar = context.freshShadowed() + ((body: Term) => Let(false, stashVar, nme, body)) -> + Split.Let(false, nme, stashVar, tail) + case S(_) | N => identity[Term] _ -> tail + } } object Normalization diff --git a/shared/src/test/diff/ucs/HygienicBindings.mls b/shared/src/test/diff/ucs/HygienicBindings.mls index bcfd1b7e..622a1f2b 100644 --- a/shared/src/test/diff/ucs/HygienicBindings.mls +++ b/shared/src/test/diff/ucs/HygienicBindings.mls @@ -1,5 +1,8 @@ :PreTyper +fun (~~>) expect(a, b) = if a is b then () else error +//│ fun (~~>) expect: (anything, anything) -> () + type Option[out T] = None | Some[T] module None class Some[out T](val value: T) @@ -21,6 +24,11 @@ class Cons[out A](head: A, tail: List[A]) //│ module Nil //│ class Cons[A](head: A, tail: List[A]) +fun justTrue(_) = true +fun justFalse(_) = false +//│ fun justTrue: anything -> true +//│ fun justFalse: anything -> false + fun h0(a) = if a is Some(Left(y)) then y @@ -28,15 +36,14 @@ fun h0(a) = a is None then 0 //│ fun h0: forall 'a. (None | Some[Left['a] | Right['a]]) -> (0 | 'a) -// Precise scrutinee identification (easy) -// ======================================= -// The desugarer should be able to identify pattern matching on the first -// parameter of `Some` even if they are bound to different variables. +// If a class parameter is bound to the same variable in different branches, +// the bindings can be merged and can be typed and coverage checked. See the +// desugared version below. :dpt:postprocess.result -fun h1(a) = +fun h0'(a) = if a is Some(x) and x is Left(y) then y - a is Some(y) and y is Right(z) then z + a is Some(x) and x is Right(z) then z a is None then 0 //│ | | | | | | | Post-processed UCS term: //│ | | | | | | | case a*‡ of @@ -49,74 +56,33 @@ fun h1(a) = //│ | | | | | | | let y*‡ = (ucs$args_x$Left).0 //│ | | | | | | | y //│ | | | | | | | Right*◊ -> -//│ | | | | | | | let y*‡ = (ucs$args_a$Some).0 -//│ | | | | | | | let ucs$args_y$Right*† = (Right).unapply(y,) -//│ | | | | | | | let z*‡ = (ucs$args_y$Right).0 +//│ | | | | | | | let ucs$args_x$Right*† = (Right).unapply(x,) +//│ | | | | | | | let z*‡ = (ucs$args_x$Right).0 //│ | | | | | | | z //│ | | | | | | | None*† -> 0 +//│ fun h0': forall 'a. (None | Some[Left['a] | Right['a]]) -> (0 | 'a) + +// However, if the class parameter is bound to different variables in different +// branches, the bindings cannot be merged and the type will miss the latter +// branch. See the desugared version below. +fun h1(a) = + if + a is Some(x) and x is Left(y) then y + a is Some(y) and y is Right(z) then z + a is None then 0 //│ fun h1: forall 'a. (None | Some[Right[anything] & {#rightValue: 'a}]) -> (0 | 'a) // FIXME h1(Some(Left(0))) //│ ╔══[ERROR] Type `Left[?A]` does not contain member `rightValue` -//│ ║ l.12: class Right[B](val rightValue: B) +//│ ║ l.15: class Right[B](val rightValue: B) //│ ╙── ^^^^^^^^^^ //│ 0 | error //│ res //│ = 0 -// This is the desugared version of the test case above. -fun h1'(a) = - if a is - Some then - let x = a.value - let y = a.value - if x is - Left then - let y = x.leftValue - y - _ then - if y is - Right then - let z = y.rightValue - z - None then 0 -//│ fun h1': forall 'leftValue. (None | Some[Right['leftValue]]) -> (0 | 'leftValue) - -// FIXME -h1'(Some(Left(0))) -//│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.87: h1'(Some(Left(0))) -//│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ╟── application of type `Left[?A]` is not an instance of type `Right` -//│ ║ l.87: h1'(Some(Left(0))) -//│ ║ ^^^^^^^ -//│ ╟── Note: constraint arises from class pattern: -//│ ║ l.80: Right then -//│ ║ ^^^^^ -//│ ╟── from field selection: -//│ ║ l.73: let y = a.value -//│ ║ ^^^^^^^ -//│ ╟── Note: type parameter T is defined at: -//│ ║ l.5: class Some[out T](val value: T) -//│ ╙── ^ -//│ 0 | error -//│ res -//│ = 0 - -fun h1''(a) = - if - a is Some(x) and x is Left(y) then y - a is Some(x) and x is Right(z) then z - a is None then 0 -//│ fun h1'': forall 'a. (None | Some[Left['a] | Right['a]]) -> (0 | 'a) - -h1''(Some(Left(0))) -//│ 0 -//│ res -//│ = 0 - -// FIXME: Precise scrutinee identification (hard) +// FIXME: It is also impossible to merge bindings of different variables if one +// of them is bound via a let binding. fun h2(a) = if a is Some(x) and x is x' and x' is Left(y) then y @@ -126,26 +92,39 @@ fun h2(a) = a is None then 0 //│ fun h2: forall 'a. (None | Some[Left['a]]) -> (0 | 'a) -// FIXME: Some results are wrong. +:dpt:postprocess.result fun h3(x, y, f, p) = if x is _ and f(x) is y and p(x) then y None then y _ then "anyway" -h3("anything", "not me", _ => "should be me", _ => true) -h3(None, "should be me", _ => "not me", _ => false) -h3("anything", "anything", _ => "not me", _ => false) -//│ fun h3: forall 'a 'b. (Object & 'a, anything, 'a -> 'b, 'a -> Bool) -> ("anyway" | 'b) -//│ "anyway" | "not me" +//│ | | | | | | | Post-processed UCS term: +//│ | | | | | | | let ucs$scrut$0*‡ = f(x,) +//│ | | | | | | | let ucs$shadow$0 = y +//│ | | | | | | | let y*‡ = ucs$scrut$0 +//│ | | | | | | | let ucs$test$0*† = p(x,) : Bool +//│ | | | | | | | case ucs$test$0*† of +//│ | | | | | | | true*† -> y +//│ | | | | | | | _ -> +//│ | | | | | | | let y*‡ = ucs$shadow$0 +//│ | | | | | | | case x*‡ of +//│ | | | | | | | None*† -> y +//│ | | | | | | | _ -> "anyway" +//│ fun h3: forall 'a 'b. (Object & 'a, 'b, 'a -> 'b, 'a -> Bool) -> ("anyway" | 'b) + + +h3("anything", "not me", _ => "should be me", _ => true) ~~> "should be me" +h3(None, "should be me", _ => "not me", _ => false) ~~> "should be me" +h3("anything", "anything", _ => "not me", _ => false) ~~> "anyway" +//│ () //│ res -//│ = 'should be me' +//│ = undefined //│ res -//│ = 'not me' +//│ = undefined //│ res -//│ = 'anyway' +//│ = undefined -// FIXME: We still haven't fixed all shadowing problems. :dpt:postprocess.result fun h4(x, y, p) = if x is @@ -153,26 +132,70 @@ fun h4(x, y, p) = None then y _ then "default" //│ | | | | | | | Post-processed UCS term: +//│ | | | | | | | let ucs$shadow$0 = y //│ | | | | | | | let y*‡ = x //│ | | | | | | | let ucs$test$0*† = p(x,) : Bool //│ | | | | | | | case ucs$test$0*† of //│ | | | | | | | true*† -> y //│ | | | | | | | _ -> +//│ | | | | | | | let y*‡ = ucs$shadow$0 //│ | | | | | | | case x*‡ of //│ | | | | | | | None*† -> y //│ | | | | | | | _ -> "default" -//│ fun h4: forall 'a. (Object & 'a, anything, 'a -> Bool) -> ("default" | 'a) +//│ fun h4: forall 'a 'b. (Object & 'a, 'b, 'a -> Bool) -> ("default" | 'a | 'b) + +h4("should be me", "not me", _ => true) ~~> "should be me" +h4(None, "not me", _ => true) ~~> None +h4(None, "should be me", _ => false) ~~> "should be me" +h4("anything", "not me", _ => false) ~~> "default" +//│ () +//│ res +//│ = undefined +//│ res +//│ = undefined +//│ res +//│ = undefined +//│ res +//│ = undefined + +:dpt:postprocess.result +fun h5(x, y, p) = + if x is + Some(y) and p(x) then y + None then y + _ then y +//│ | | | | | | | Post-processed UCS term: +//│ | | | | | | | case x*‡ of +//│ | | | | | | | Some*◊ -> +//│ | | | | | | | let ucs$args_x$Some*† = (Some).unapply(x,) +//│ | | | | | | | let ucs$shadow$0 = y +//│ | | | | | | | let y*‡ = (ucs$args_x$Some).0 +//│ | | | | | | | let ucs$test$0*† = p(x,) : Bool +//│ | | | | | | | case ucs$test$0*† of +//│ | | | | | | | true*† -> y +//│ | | | | | | | _ -> +//│ | | | | | | | let y*‡ = ucs$shadow$0 +//│ | | | | | | | y +//│ | | | | | | | None*† -> y +//│ | | | | | | | _ -> y +//│ fun h5: forall 'a. (Object & ~#Some | Some['a], 'a, Some[nothing] -> Bool) -> 'a -h4("should be me", "not me", _ => true) -h4(None, "not me", _ => true) -h4(None, "should be me", _ => false) -h4("anything", "not me", _ => false) -//│ "anything" | "default" +h5(Some(1), 2, justTrue) ~~> 1 +h5(Some(1), 2, justFalse) ~~> 2 +h5(None, 0, justTrue) ~~> 0 +h5(None, 0, justFalse) ~~> 0 +h5("foo", 42, justTrue) ~~> 42 +h5("foo", 42, justFalse) ~~> 42 +//│ () +//│ res +//│ = undefined +//│ res +//│ = undefined //│ res -//│ = 'should be me' +//│ = undefined //│ res -//│ = None { class: [class None] } +//│ = undefined //│ res -//│ = None { class: [class None] } +//│ = undefined //│ res -//│ = 'default' +//│ = undefined diff --git a/todo.md b/todo.md index 8b42a7f2..13163bce 100644 --- a/todo.md +++ b/todo.md @@ -3,13 +3,13 @@ problems located by test cases. ### Remaining Tasks -- Report redundant cases +- [ ] Report redundant cases - shared/src/test/diff/ucs/CrossBranchCapture.mls - shared/src/test/diff/ucs/DirectLines.mls **OLD** - shared/src/test/diff/ucs/SplitAnd.mls - shared/src/test/diff/ucs/WeirdIf.mls - shared/src/test/diff/ucs/Wildcard.mls -- Hygenic bindings +- [x] Hygenic bindings - shared/src/test/diff/ucs/HygienicBindings.mls ### Test Checklist From 3caacec1ec8e7576ffb9b7efcee9aec8fc693d58 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sun, 14 Jan 2024 22:42:03 +0800 Subject: [PATCH 063/143] Report unreachable cases and fix stashing shadowed variables --- .../main/scala/mlscript/ucs/DesugarUCS.scala | 33 ++++++++++++++- shared/src/main/scala/mlscript/ucs/core.scala | 11 ----- .../mlscript/ucs/stages/Normalization.scala | 12 ++++-- .../src/test/diff/ucs/CrossBranchCapture.mls | 42 +++++++++---------- shared/src/test/diff/ucs/DirectLines.mls | 11 +++-- todo.md | 14 +++---- 6 files changed, 74 insertions(+), 49 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index 8c2ab208..facff56d 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -1,8 +1,7 @@ package mlscript.ucs import collection.mutable.{Map => MutMap} -import mlscript.ucs.stages._ -import mlscript.ucs.context.{Context, ScrutineeData} +import mlscript.ucs.{syntax => s, core => c}, stages._, context.{Context, ScrutineeData} import mlscript.ucs.display.{showNormalizedTerm, showSplit} import mlscript.pretyper.{PreTyper, Scope} import mlscript.pretyper.symbol._ @@ -138,6 +137,36 @@ trait DesugarUCS extends Transformation { nme.resolveClassLikeSymbol; nme } } + protected implicit class SourceSplitOps[+B <: s.Branch](these: s.Split[B]) { + def ++[BB >: B <: s.Branch](those: s.Split[BB]): s.Split[BB] = these match { + case s.Split.Cons(head, tail) => s.Split.Cons(head, tail ++ those) + case s.Split.Let(rec, nme, rhs, tail) => s.Split.Let(rec, nme, rhs, tail ++ those) + case s.Split.Else(_) => + raiseWarning(msg"unreachable case" -> these.toLoc) + these + case s.Split.Nil => those + } + } + + protected implicit class CoreSplitOps(these: c.Split) { + /** + * Concatenates two splits. Beware that `that` may be discarded if `this` + * has an else branch. Make sure to make diagnostics for discarded `that`. + */ + def ++(those: c.Split): c.Split = + if (those === c.Split.Nil) these else (these match { + case me: c.Split.Cons => me.copy(tail = me.tail ++ those) + case me: c.Split.Let => me.copy(tail = me.tail ++ those) + case _: c.Split.Else => + raiseWarning( + msg"the case is unreachable" -> those.toLoc, + msg"because this branch covers the case" -> these.toLoc + ) + these + case c.Split.Nil => those + }) + } + protected def traverseIf(`if`: If)(implicit scope: Scope): Unit = { implicit val context: Context = new Context(`if`) try trace("traverseIf") { diff --git a/shared/src/main/scala/mlscript/ucs/core.scala b/shared/src/main/scala/mlscript/ucs/core.scala index 70633056..986fec08 100644 --- a/shared/src/main/scala/mlscript/ucs/core.scala +++ b/shared/src/main/scala/mlscript/ucs/core.scala @@ -54,17 +54,6 @@ package object core { sealed abstract class Split extends Located { @inline def ::(head: Branch): Split = Split.Cons(head, this) - - /** - * Concatenates two splits. Beware that `that` may be discarded if `this` - * has an else branch. Make sure to make diagnostics for discarded `that`. - */ - def ++(that: Split): Split = this match { - case me: Split.Cons => me.copy(tail = me.tail ++ that) - case me: Split.Let => me.copy(tail = me.tail ++ that) - case _: Split.Else => this - case Split.Nil => that - } /** * Returns true if the split has an else branch. diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 95032eae..b038862c 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -18,7 +18,11 @@ trait Normalization { self: DesugarUCS with Traceable => import Normalization._ // TODO: We might not need the case where `deep` is `false`. - private def fillImpl(these: Split, those: Split, deep: Bool)(implicit context: Context, generatedVars: Set[Var]): Split = + private def fillImpl(these: Split, those: Split, deep: Bool)(implicit + scope: Scope, + context: Context, + generatedVars: Set[Var] + ): Split = if (these.hasElse) these else (these match { case these @ Split.Cons(head, tail) => if (head.continuation.hasElse || !deep) { @@ -30,13 +34,13 @@ trait Normalization { self: DesugarUCS with Traceable => } case these @ Split.Let(_, nme, _, tail) => println(s"fill let binding ${nme.name}") - if (those.freeVars contains nme) { + if (scope.getTermSymbol(nme.name).isDefined && (those.freeVars contains nme)) { val fresh = context.freshShadowed() val thoseWithShadowed = Split.Let(false, nme, fresh, those) val concatenated = these.copy(tail = fillImpl(tail, thoseWithShadowed, deep)) Split.Let(false, fresh, nme, concatenated) } else { - these.copy(tail = fillImpl(tail, those, deep)(context, generatedVars + nme)) + these.copy(tail = fillImpl(tail, those, deep)(scope, context, generatedVars + nme)) } case _: Split.Else => these case Split.Nil => @@ -45,7 +49,7 @@ trait Normalization { self: DesugarUCS with Traceable => }) private implicit class SplitOps(these: Split) { - def fill(those: Split, deep: Bool)(implicit context: Context, generatedVars: Set[Var]): Split = + def fill(those: Split, deep: Bool)(implicit scope: Scope, context: Context, generatedVars: Set[Var]): Split = trace(s"fill <== ${generatedVars.iterator.map(_.name).mkString("{", ", ", "}")}") { println(s"LHS: ${showSplit(these)}") println(s"RHS: ${showSplit(those)}") diff --git a/shared/src/test/diff/ucs/CrossBranchCapture.mls b/shared/src/test/diff/ucs/CrossBranchCapture.mls index f1c60286..ac96606b 100644 --- a/shared/src/test/diff/ucs/CrossBranchCapture.mls +++ b/shared/src/test/diff/ucs/CrossBranchCapture.mls @@ -1,45 +1,46 @@ :PreTyper +fun (~~>) expect(a, b) = if a is b then () else error +//│ fun (~~>) expect: (anything, anything) -> () class Numb(n: Int) //│ class Numb(n: Int) :e -:ge fun process(e) = if e is Numb(n) and n > 0 then n Numb(m) then n //│ ╔══[ERROR] identifier `n` not found -//│ ║ l.13: Numb(m) then n +//│ ║ l.14: Numb(m) then n //│ ╙── ^ -//│ ╔══[ERROR] identifier not found: n -//│ ║ l.12: Numb(n) and n > 0 then n -//│ ╙── ^ -//│ fun process: Numb -> (Int | error) -//│ Code generation encountered an error: -//│ unresolved symbol n +//│ fun process: Numb -> Int + -:re process(Numb(-10)) -//│ Int | error +//│ Int //│ res -//│ Runtime error: -//│ TypeError: process is not a function +//│ = -10 + -// * FIXME wrong result fun process(e, n) = if e is Numb(n) and n > 0 then n Numb(m) then n + m //│ fun process: (Numb, Int) -> Int -process(Numb(0), 10) -//│ Int +process(Numb(0), 10) ~~> 10 +process(Numb(-1), 10) ~~> 9 +process(Numb(1), 10) ~~> 1 +//│ () +//│ res +//│ = undefined //│ res -//│ = 10 +//│ = undefined +//│ res +//│ = undefined class Vec(xs: Array[Numb | Vec]) @@ -57,15 +58,12 @@ fun process(e) = Pair(Vec(n), Numb(n)) then n Pair(Numb(n), Vec(n)) then n //│ ╔══[ERROR] identifier `n` not found -//│ ║ l.56: Pair(Vec(xs), Vec(ys)) then n +//│ ║ l.57: Pair(Vec(xs), Vec(ys)) then n //│ ╙── ^ //│ ╔══[ERROR] identifier not found: n -//│ ║ l.55: Pair(Numb(n), Numb(m)) then Numb(n + m) -//│ ╙── ^ -//│ ╔══[ERROR] identifier not found: n -//│ ║ l.56: Pair(Vec(xs), Vec(ys)) then n +//│ ║ l.57: Pair(Vec(xs), Vec(ys)) then n //│ ╙── ^ -//│ fun process: Pair[Numb | Vec, Numb | Vec] -> (Int | Numb | error | Array[Numb | Vec]) +//│ fun process: Pair[Numb | Vec, Numb | Vec] -> (Int | Numb | error) //│ Code generation encountered an error: //│ unresolved symbol n diff --git a/shared/src/test/diff/ucs/DirectLines.mls b/shared/src/test/diff/ucs/DirectLines.mls index 4450e04e..22e0c1e3 100644 --- a/shared/src/test/diff/ucs/DirectLines.mls +++ b/shared/src/test/diff/ucs/DirectLines.mls @@ -20,7 +20,7 @@ fun isValid(x) = if x then false else true fun f(x, allowNone) = if x is Some(x) and isValid(x) then "good" - is None() and allowNone then "okay" + is None and allowNone then "okay" is _ then "bad" //│ fun f: (Object & ~#Some | Some[Bool], Bool) -> ("bad" | "good" | "okay") @@ -37,8 +37,7 @@ fun f(x, y, z) = _ then "bruh" //│ fun f: (Num, Num, Num) -> ("bruh" | "x" | "y = 1" | "y = 3" | "z = 0" | "z = 9") -// :w -// FIXME: Report redundant else branches. +:w fun f(a, b) = if a == 0 then 0 @@ -47,4 +46,10 @@ fun f(a, b) = 2 then 2 _ then 7 else 3 +//│ ╔══[WARNING] the case is unreachable +//│ ║ l.48: else 3 +//│ ║ ^ +//│ ╟── because this branch covers the case +//│ ║ l.47: _ then 7 +//│ ╙── ^ //│ fun f: (Num, Num) -> (0 | 1 | 2 | 7) diff --git a/todo.md b/todo.md index 13163bce..6a99be10 100644 --- a/todo.md +++ b/todo.md @@ -3,14 +3,14 @@ problems located by test cases. ### Remaining Tasks -- [ ] Report redundant cases - - shared/src/test/diff/ucs/CrossBranchCapture.mls - - shared/src/test/diff/ucs/DirectLines.mls **OLD** - - shared/src/test/diff/ucs/SplitAnd.mls - - shared/src/test/diff/ucs/WeirdIf.mls - - shared/src/test/diff/ucs/Wildcard.mls +- [ ] Report unreachable or redundant cases + - [x] shared/src/test/diff/ucs/DirectLines.mls **OLD** + - [ ] shared/src/test/diff/ucs/SplitAnd.mls + - [ ] shared/src/test/diff/ucs/WeirdIf.mls + - [ ] shared/src/test/diff/ucs/Wildcard.mls - [x] Hygenic bindings - - shared/src/test/diff/ucs/HygienicBindings.mls + - [ ] shared/src/test/diff/ucs/CrossBranchCapture.mls + - [x] shared/src/test/diff/ucs/HygienicBindings.mls ### Test Checklist From 5e71447720dfa38c09a42b8becce701017f9e1df Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sun, 14 Jan 2024 22:52:49 +0800 Subject: [PATCH 064/143] Append test file changes --- shared/src/test/diff/nu/FunnyIndet.mls | 7 +++-- .../test/diff/pretyper/ucs/RecordPattern.mls | 17 +++++++---- .../pretyper/ucs/SpecilizationCollision.mls | 10 +------ .../pretyper/ucs/coverage/Unreachable.mls | 17 +++++++++++ shared/src/test/diff/ucs/InterleavedLet.mls | 2 +- .../src/test/diff/ucs/NuPlainConditionals.mls | 26 +++++++++-------- .../src/test/diff/ucs/PlainConditionals.mls | 28 +++++++++++-------- shared/src/test/diff/ucs/SimpleUCS.mls | 2 +- shared/src/test/diff/ucs/SplitAroundOp.mls | 2 +- shared/src/test/diff/ucs/TrivialIf.mls | 8 +++--- shared/src/test/diff/ucs/WeirdIf.mls | 23 +++++++++------ shared/src/test/diff/ucs/Wildcard.mls | 9 ++++-- 12 files changed, 94 insertions(+), 57 deletions(-) diff --git a/shared/src/test/diff/nu/FunnyIndet.mls b/shared/src/test/diff/nu/FunnyIndet.mls index f0eb4d45..d01ba73b 100644 --- a/shared/src/test/diff/nu/FunnyIndet.mls +++ b/shared/src/test/diff/nu/FunnyIndet.mls @@ -29,11 +29,14 @@ :e // TODO support +:w 2 is 2 //│ ╔══[ERROR] Unknown pattern {2} -//│ ║ l.33: 2 +//│ ║ l.34: 2 //│ ╙── ^ +//│ ╔══[WARNING] the case is unreachable +//│ ╙── because this branch covers the case //│ true //│ res //│ = true @@ -61,7 +64,7 @@ if 2 is 2 then true 1 then false //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause -//│ ║ l.62: 1 then false +//│ ║ l.65: 1 then false //│ ╙── ^^^^^^^^^^^^ //│ () //│ res diff --git a/shared/src/test/diff/pretyper/ucs/RecordPattern.mls b/shared/src/test/diff/pretyper/ucs/RecordPattern.mls index e5414074..ef6726ea 100644 --- a/shared/src/test/diff/pretyper/ucs/RecordPattern.mls +++ b/shared/src/test/diff/pretyper/ucs/RecordPattern.mls @@ -1,24 +1,31 @@ :PreTyper :e +:w fun take_1(p) = if p is { x, y } then x + y else 0 //│ ╔══[ERROR] Unknown pattern '{' {x: x, y: y} '}' -//│ ║ l.6: { x, y } then x + y +//│ ║ l.7: { x, y } then x + y //│ ╙── ^^^^^^^^ +//│ ╔══[WARNING] the case is unreachable +//│ ║ l.8: else 0 +//│ ║ ^ +//│ ╟── because this branch covers the case +//│ ║ l.7: { x, y } then x + y +//│ ╙── ^^^^^ //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.6: { x, y } then x + y +//│ ║ l.7: { x, y } then x + y //│ ╙── ^ //│ ╔══[ERROR] identifier `y` not found -//│ ║ l.6: { x, y } then x + y +//│ ║ l.7: { x, y } then x + y //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x -//│ ║ l.6: { x, y } then x + y +//│ ║ l.7: { x, y } then x + y //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y -//│ ║ l.6: { x, y } then x + y +//│ ║ l.7: { x, y } then x + y //│ ╙── ^ //│ fun take_1: anything -> Int //│ Code generation encountered an error: diff --git a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls index 5675d5d0..b18f3775 100644 --- a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls +++ b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls @@ -33,15 +33,7 @@ fun example2(p) = //│ ╔══[ERROR] identifier `y` not found //│ ║ l.28: Pair(a, b) and p2(a) and p2(b) then x + y //│ ╙── ^ -//│ ╔══[ERROR] identifier not found: x -//│ ║ l.27: Pair(x, y) and p1(x) and p1(y) then "both negative" -//│ ╙── ^ -//│ ╔══[ERROR] identifier not found: y -//│ ║ l.27: Pair(x, y) and p1(x) and p1(y) then "both negative" -//│ ╙── ^ -//│ fun example2: (Object & ~#Pair | Pair[Num, Num]) -> ("both negative" | "nah" | Int) -//│ Code generation encountered an error: -//│ unresolved symbol y +//│ fun example2: (Object & ~#Pair | Pair[Int, Int]) -> ("both negative" | "nah" | Int) // Next, let's check the name collision between a class and its super class. diff --git a/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls b/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls index a4a1e897..de454fa6 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls @@ -41,6 +41,7 @@ reachable_1(None) //│ res //│ = 'tan' +:w fun unreachable_1(x) = if x is _ and @@ -48,4 +49,20 @@ fun unreachable_1(x) = else "screen" Some(xv) then "sin" None then "tan" +//│ ╔══[WARNING] the case is unreachable +//│ ║ l.46: if x is +//│ ║ ^^^^ +//│ ║ l.47: _ and +//│ ║ ^^^^^^^^^ +//│ ║ l.48: f(x) then "tmux" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.49: else "screen" +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ║ l.50: Some(xv) then "sin" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.51: None then "tan" +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ╟── because this branch covers the case +//│ ║ l.49: else "screen" +//│ ╙── ^^^^^^^^ //│ fun unreachable_1: (Object & ~#Some | Some[Eql[1]]) -> ("screen" | "tmux") diff --git a/shared/src/test/diff/ucs/InterleavedLet.mls b/shared/src/test/diff/ucs/InterleavedLet.mls index cb41cb72..243ac0ad 100644 --- a/shared/src/test/diff/ucs/InterleavedLet.mls +++ b/shared/src/test/diff/ucs/InterleavedLet.mls @@ -37,7 +37,7 @@ fun p(x, y) = x is Some and y is None then 0 y is Some and x is Some then 1 x is Some and y is Some then 0 -//│ ╔══[ERROR] Found unexpected empty split +//│ ╔══[ERROR] unexpected empty split found //│ ╙── //│ ╔══[ERROR] Scrutinee `y` has 1 missing case //│ ║ l.38: y is Some and x is Some then 1 diff --git a/shared/src/test/diff/ucs/NuPlainConditionals.mls b/shared/src/test/diff/ucs/NuPlainConditionals.mls index 053e31b8..9844b82f 100644 --- a/shared/src/test/diff/ucs/NuPlainConditionals.mls +++ b/shared/src/test/diff/ucs/NuPlainConditionals.mls @@ -58,6 +58,8 @@ fun foo(x) = x is //│ ║ ^^^^ //│ ║ l.55: Int //│ ╙── ^^^^^ +//│ ╔══[WARNING] the case is unreachable +//│ ╙── because this branch covers the case //│ fun foo: anything -> true // TODO proper error @@ -65,41 +67,43 @@ fun foo(x) = x is Pair(a, b) and a > b Int //│ ╔══[ERROR] Unknown pattern {and(Pair(a, b,), >(a, b,),); Int} -//│ ║ l.65: Pair(a, b) and a > b +//│ ║ l.67: Pair(a, b) and a > b //│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.66: Int +//│ ║ l.68: Int //│ ╙── ^^^^^ +//│ ╔══[WARNING] the case is unreachable +//│ ╙── because this branch covers the case //│ fun foo: anything -> true // TODO support `|` fun foo1(x) = x is Pair(a, b) | Int fun foo2(x) = x is (Pair(a, b) and a > b) | Int //│ ╔══[ERROR] Unknown pattern '(' and(Pair(a, b,), >(a, b,),) ')' -//│ ║ l.76: fun foo2(x) = x is (Pair(a, b) and a > b) | Int +//│ ║ l.80: fun foo2(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] type identifier `|` not found -//│ ║ l.76: fun foo2(x) = x is (Pair(a, b) and a > b) | Int +//│ ║ l.80: fun foo2(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^ //│ ╔══[ERROR] type identifier `Int` not found -//│ ║ l.76: fun foo2(x) = x is (Pair(a, b) and a > b) | Int +//│ ║ l.80: fun foo2(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^^^ //│ ╔══[ERROR] identifier `|` not found -//│ ║ l.76: fun foo2(x) = x is (Pair(a, b) and a > b) | Int +//│ ║ l.80: fun foo2(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^ //│ ╔══[ERROR] type identifier `|` not found -//│ ║ l.75: fun foo1(x) = x is Pair(a, b) | Int +//│ ║ l.79: fun foo1(x) = x is Pair(a, b) | Int //│ ╙── ^ //│ ╔══[ERROR] type identifier `Int` not found -//│ ║ l.75: fun foo1(x) = x is Pair(a, b) | Int +//│ ║ l.79: fun foo1(x) = x is Pair(a, b) | Int //│ ╙── ^^^ //│ ╔══[ERROR] identifier `|` not found -//│ ║ l.75: fun foo1(x) = x is Pair(a, b) | Int +//│ ║ l.79: fun foo1(x) = x is Pair(a, b) | Int //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: | -//│ ║ l.75: fun foo1(x) = x is Pair(a, b) | Int +//│ ║ l.79: fun foo1(x) = x is Pair(a, b) | Int //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: | -//│ ║ l.76: fun foo2(x) = x is (Pair(a, b) and a > b) | Int +//│ ║ l.80: fun foo2(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^ //│ fun foo1: nothing -> error //│ fun foo2: nothing -> error diff --git a/shared/src/test/diff/ucs/PlainConditionals.mls b/shared/src/test/diff/ucs/PlainConditionals.mls index c15867ad..63e30e6c 100644 --- a/shared/src/test/diff/ucs/PlainConditionals.mls +++ b/shared/src/test/diff/ucs/PlainConditionals.mls @@ -58,6 +58,8 @@ fun foo(x) = x is //│ ║ ^^^^ //│ ║ l.55: Int //│ ╙── ^^^^^ +//│ ╔══[WARNING] the case is unreachable +//│ ╙── because this branch covers the case //│ fun foo: anything -> true // TODO proper error @@ -65,44 +67,46 @@ fun foo(x) = x is Pair(a, b) and a > b Int //│ ╔══[ERROR] Unknown pattern {and(Pair(a, b,), >(a, b,),); Int} -//│ ║ l.65: Pair(a, b) and a > b +//│ ║ l.67: Pair(a, b) and a > b //│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.66: Int +//│ ║ l.68: Int //│ ╙── ^^^^^ +//│ ╔══[WARNING] the case is unreachable +//│ ╙── because this branch covers the case //│ fun foo: anything -> true // TODO support `|` fun foo(x) = x is Pair(a, b) | Int fun foo(x) = x is (Pair(a, b) and a > b) | Int //│ ╔══[ERROR] Unknown pattern '(' and(Pair(a, b,), >(a, b,),) ')' -//│ ║ l.76: fun foo(x) = x is (Pair(a, b) and a > b) | Int +//│ ║ l.80: fun foo(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] type identifier `|` not found -//│ ║ l.76: fun foo(x) = x is (Pair(a, b) and a > b) | Int +//│ ║ l.80: fun foo(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^ //│ ╔══[ERROR] type identifier `Int` not found -//│ ║ l.76: fun foo(x) = x is (Pair(a, b) and a > b) | Int +//│ ║ l.80: fun foo(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^^^ //│ ╔══[ERROR] identifier `|` not found -//│ ║ l.76: fun foo(x) = x is (Pair(a, b) and a > b) | Int +//│ ║ l.80: fun foo(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^ //│ ╔══[ERROR] type identifier `|` not found -//│ ║ l.75: fun foo(x) = x is Pair(a, b) | Int +//│ ║ l.79: fun foo(x) = x is Pair(a, b) | Int //│ ╙── ^ //│ ╔══[ERROR] type identifier `Int` not found -//│ ║ l.75: fun foo(x) = x is Pair(a, b) | Int +//│ ║ l.79: fun foo(x) = x is Pair(a, b) | Int //│ ╙── ^^^ //│ ╔══[ERROR] identifier `|` not found -//│ ║ l.75: fun foo(x) = x is Pair(a, b) | Int +//│ ║ l.79: fun foo(x) = x is Pair(a, b) | Int //│ ╙── ^ //│ ╔══[ERROR] Refininition of 'foo' -//│ ║ l.76: fun foo(x) = x is (Pair(a, b) and a > b) | Int +//│ ║ l.80: fun foo(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] type identifier not found: | -//│ ║ l.75: fun foo(x) = x is Pair(a, b) | Int +//│ ║ l.79: fun foo(x) = x is Pair(a, b) | Int //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: | -//│ ║ l.76: fun foo(x) = x is (Pair(a, b) and a > b) | Int +//│ ║ l.80: fun foo(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^ //│ fun foo: nothing -> error //│ fun foo: nothing -> error diff --git a/shared/src/test/diff/ucs/SimpleUCS.mls b/shared/src/test/diff/ucs/SimpleUCS.mls index 20bcad54..8f76440d 100644 --- a/shared/src/test/diff/ucs/SimpleUCS.mls +++ b/shared/src/test/diff/ucs/SimpleUCS.mls @@ -326,7 +326,7 @@ fun f(x) = //│ ╔══[ERROR] Syntactic split of patterns are not supported //│ ║ l.324: 0 :: //│ ╙── ^^ -//│ ╔══[ERROR] Found unexpected empty split +//│ ╔══[ERROR] unexpected empty split found //│ ╙── //│ fun f: anything -> nothing diff --git a/shared/src/test/diff/ucs/SplitAroundOp.mls b/shared/src/test/diff/ucs/SplitAroundOp.mls index ddfeb3af..cf8c1d17 100644 --- a/shared/src/test/diff/ucs/SplitAroundOp.mls +++ b/shared/src/test/diff/ucs/SplitAroundOp.mls @@ -64,7 +64,7 @@ if x is //│ ╔══[ERROR] identifier `x` not found //│ ║ l.38: if x is //│ ╙── ^ -//│ ╔══[ERROR] Found unexpected empty split +//│ ╔══[ERROR] unexpected empty split found //│ ╙── //│ ╔══[ERROR] identifier not found: x //│ ║ l.38: if x is diff --git a/shared/src/test/diff/ucs/TrivialIf.mls b/shared/src/test/diff/ucs/TrivialIf.mls index 555b4943..5f1c8070 100644 --- a/shared/src/test/diff/ucs/TrivialIf.mls +++ b/shared/src/test/diff/ucs/TrivialIf.mls @@ -78,15 +78,15 @@ fun foo(x) = if x is Some of 0 then 0 1 then 1 //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here -//│ ║ l.79: 0 then 0 +//│ ║ l.78: 0 then 0 //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead -//│ ║ l.78: fun foo(x) = if x is Some of +//│ ║ l.77: fun foo(x) = if x is Some of //│ ║ ^^^^^^^^^^^^ -//│ ║ l.79: 0 then 0 +//│ ║ l.78: 0 then 0 //│ ║ ^^^ //│ ╟── Note: 'if' expression starts here: -//│ ║ l.78: fun foo(x) = if x is Some of +//│ ║ l.77: fun foo(x) = if x is Some of //│ ╙── ^^ //│ fun foo: Some[0] -> () diff --git a/shared/src/test/diff/ucs/WeirdIf.mls b/shared/src/test/diff/ucs/WeirdIf.mls index c420f554..53f0d576 100644 --- a/shared/src/test/diff/ucs/WeirdIf.mls +++ b/shared/src/test/diff/ucs/WeirdIf.mls @@ -17,9 +17,14 @@ if else 0 else 1 //│ res //│ = 0 -// FIXME -// :w +:w fun f(x) = if x is else 0 else 1 +//│ ╔══[WARNING] the case is unreachable +//│ ║ l.21: fun f(x) = if x is else 0 else 1 +//│ ║ ^ +//│ ╟── because this branch covers the case +//│ ║ l.21: fun f(x) = if x is else 0 else 1 +//│ ╙── ^ //│ fun f: anything -> 0 fun f(x) = if x is else 0 @@ -29,7 +34,7 @@ fun f(x) = if x is else 0 if true then 0 //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.30: then 0 +//│ ║ l.35: then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ 0 @@ -43,21 +48,21 @@ fun f(x) = if x === else "bruh" //│ ╔══[PARSE ERROR] Unexpected indented block in expression position -//│ ║ l.44: else "bruh" +//│ ║ l.49: else "bruh" //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here -//│ ║ l.44: else "bruh" +//│ ║ l.49: else "bruh" //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead -//│ ║ l.43: if x === +//│ ║ l.48: if x === //│ ║ ^^^^^ -//│ ║ l.44: else "bruh" +//│ ║ l.49: else "bruh" //│ ║ ^^^^ //│ ╟── Note: 'if' expression starts here: -//│ ║ l.43: if x === +//│ ║ l.48: if x === //│ ╙── ^^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.44: else "bruh" +//│ ║ l.49: else "bruh" //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: Eql[()] -> () diff --git a/shared/src/test/diff/ucs/Wildcard.mls b/shared/src/test/diff/ucs/Wildcard.mls index ede97537..9cc79fd9 100644 --- a/shared/src/test/diff/ucs/Wildcard.mls +++ b/shared/src/test/diff/ucs/Wildcard.mls @@ -98,13 +98,18 @@ w3(0, _ => false) //│ res //│ = 'r4' -// :w -// FIXME: Should warn this. +:w // Decision paths: // + «tmp2 @ f (x,) is any => 0 // + => 1 fun w3_1(x, f) = if f(x) is _ then 0 else 1 +//│ ╔══[WARNING] the case is unreachable +//│ ║ l.106: if f(x) is _ then 0 else 1 +//│ ║ ^ +//│ ╟── because this branch covers the case +//│ ║ l.106: if f(x) is _ then 0 else 1 +//│ ╙── ^ //│ fun w3_1: forall 'a. ('a, 'a -> anything) -> 0 w3_1(0, _ => true) From 3535960173738a467eff94ba5be09b030b7faa55 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sun, 14 Jan 2024 23:10:01 +0800 Subject: [PATCH 065/143] Support a kind of weird pattern --- .../scala/mlscript/ucs/stages/Transformation.scala | 5 +++-- shared/src/test/diff/gadt/Exp1.mls | 2 +- shared/src/test/diff/nu/ClassesInMixins.mls | 2 +- shared/src/test/diff/nu/FunnyIndet.mls | 11 ++--------- shared/src/test/diff/pretyper/ucs/RecordPattern.mls | 2 +- shared/src/test/diff/ucs/NuPlainConditionals.mls | 6 +++--- shared/src/test/diff/ucs/PlainConditionals.mls | 6 +++--- 7 files changed, 14 insertions(+), 20 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index 5a6e1560..0e061849 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -2,7 +2,7 @@ package mlscript.ucs.stages import mlscript.ucs.{DesugarUCS, helpers} import mlscript.{If, IfBody, IfBlock, IfElse, IfLet, IfOpApp, IfOpsApp, IfThen} -import mlscript.{Term, Var, App, Tup, Lit, Fld, Loc} +import mlscript.{Blk, Term, Var, App, Tup, Lit, Fld, Loc} import mlscript.Diagnostic.PreTyping import mlscript.pretyper.Traceable import mlscript.ucs.syntax._ @@ -205,9 +205,10 @@ trait Transformation { self: DesugarUCS with Traceable => case App(classNme @ Var(_), parameters: Tup) => ClassPattern(classNme, S(transformTupleTerm(parameters)), refined = false) case tuple: Tup => TuplePattern(transformTupleTerm(tuple)) + case Blk((term: Term) :: Nil) => transformPattern(term) // A speical case for FunnyIndet.mls case other => println(s"other $other") - raiseError(msg"Unknown pattern ${other.showDbg}" -> other.toLoc) + raiseError(msg"unknown pattern ${other.showDbg}" -> other.toLoc) EmptyPattern(other) } diff --git a/shared/src/test/diff/gadt/Exp1.mls b/shared/src/test/diff/gadt/Exp1.mls index d98bdcab..97db8501 100644 --- a/shared/src/test/diff/gadt/Exp1.mls +++ b/shared/src/test/diff/gadt/Exp1.mls @@ -33,7 +33,7 @@ fun f(e) = if e is :e // TODO support fun f(e) = if e is Pair['a, 'b](l, r) then [l, r] -//│ ╔══[ERROR] Unknown pattern Pair‹'a, 'b›(l, r,) +//│ ╔══[ERROR] unknown pattern Pair‹'a, 'b›(l, r,) //│ ║ l.35: Pair['a, 'b](l, r) then [l, r] //│ ╙── ^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] identifier `l` not found diff --git a/shared/src/test/diff/nu/ClassesInMixins.mls b/shared/src/test/diff/nu/ClassesInMixins.mls index c3258204..17169caf 100644 --- a/shared/src/test/diff/nu/ClassesInMixins.mls +++ b/shared/src/test/diff/nu/ClassesInMixins.mls @@ -41,7 +41,7 @@ M.Foo :e // TODO support fun foo(x) = if x is M.Foo then 1 -//│ ╔══[ERROR] Unknown pattern (M).Foo +//│ ╔══[ERROR] unknown pattern (M).Foo //│ ║ l.43: fun foo(x) = if x is M.Foo then 1 //│ ╙── ^^^^^ //│ fun foo: anything -> 1 diff --git a/shared/src/test/diff/nu/FunnyIndet.mls b/shared/src/test/diff/nu/FunnyIndet.mls index d01ba73b..7f833139 100644 --- a/shared/src/test/diff/nu/FunnyIndet.mls +++ b/shared/src/test/diff/nu/FunnyIndet.mls @@ -28,16 +28,9 @@ //│ = 4 -:e // TODO support -:w 2 is 2 -//│ ╔══[ERROR] Unknown pattern {2} -//│ ║ l.34: 2 -//│ ╙── ^ -//│ ╔══[WARNING] the case is unreachable -//│ ╙── because this branch covers the case -//│ true +//│ Bool //│ res //│ = true @@ -64,7 +57,7 @@ if 2 is 2 then true 1 then false //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause -//│ ║ l.65: 1 then false +//│ ║ l.58: 1 then false //│ ╙── ^^^^^^^^^^^^ //│ () //│ res diff --git a/shared/src/test/diff/pretyper/ucs/RecordPattern.mls b/shared/src/test/diff/pretyper/ucs/RecordPattern.mls index ef6726ea..21f57ff2 100644 --- a/shared/src/test/diff/pretyper/ucs/RecordPattern.mls +++ b/shared/src/test/diff/pretyper/ucs/RecordPattern.mls @@ -6,7 +6,7 @@ fun take_1(p) = if p is { x, y } then x + y else 0 -//│ ╔══[ERROR] Unknown pattern '{' {x: x, y: y} '}' +//│ ╔══[ERROR] unknown pattern '{' {x: x, y: y} '}' //│ ║ l.7: { x, y } then x + y //│ ╙── ^^^^^^^^ //│ ╔══[WARNING] the case is unreachable diff --git a/shared/src/test/diff/ucs/NuPlainConditionals.mls b/shared/src/test/diff/ucs/NuPlainConditionals.mls index 9844b82f..e0406c24 100644 --- a/shared/src/test/diff/ucs/NuPlainConditionals.mls +++ b/shared/src/test/diff/ucs/NuPlainConditionals.mls @@ -53,7 +53,7 @@ fun foo(x) = if x is Pair(a, b) then a > b else false fun foo(x) = x is Pair Int -//│ ╔══[ERROR] Unknown pattern {Pair; Int} +//│ ╔══[ERROR] unknown pattern {Pair; Int} //│ ║ l.54: Pair //│ ║ ^^^^ //│ ║ l.55: Int @@ -66,7 +66,7 @@ fun foo(x) = x is fun foo(x) = x is Pair(a, b) and a > b Int -//│ ╔══[ERROR] Unknown pattern {and(Pair(a, b,), >(a, b,),); Int} +//│ ╔══[ERROR] unknown pattern {and(Pair(a, b,), >(a, b,),); Int} //│ ║ l.67: Pair(a, b) and a > b //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.68: Int @@ -78,7 +78,7 @@ fun foo(x) = x is // TODO support `|` fun foo1(x) = x is Pair(a, b) | Int fun foo2(x) = x is (Pair(a, b) and a > b) | Int -//│ ╔══[ERROR] Unknown pattern '(' and(Pair(a, b,), >(a, b,),) ')' +//│ ╔══[ERROR] unknown pattern '(' and(Pair(a, b,), >(a, b,),) ')' //│ ║ l.80: fun foo2(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] type identifier `|` not found diff --git a/shared/src/test/diff/ucs/PlainConditionals.mls b/shared/src/test/diff/ucs/PlainConditionals.mls index 63e30e6c..ce5a34a5 100644 --- a/shared/src/test/diff/ucs/PlainConditionals.mls +++ b/shared/src/test/diff/ucs/PlainConditionals.mls @@ -53,7 +53,7 @@ fun foo(x) = if x is Pair(a, b) then a > b else false fun foo(x) = x is Pair Int -//│ ╔══[ERROR] Unknown pattern {Pair; Int} +//│ ╔══[ERROR] unknown pattern {Pair; Int} //│ ║ l.54: Pair //│ ║ ^^^^ //│ ║ l.55: Int @@ -66,7 +66,7 @@ fun foo(x) = x is fun foo(x) = x is Pair(a, b) and a > b Int -//│ ╔══[ERROR] Unknown pattern {and(Pair(a, b,), >(a, b,),); Int} +//│ ╔══[ERROR] unknown pattern {and(Pair(a, b,), >(a, b,),); Int} //│ ║ l.67: Pair(a, b) and a > b //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.68: Int @@ -78,7 +78,7 @@ fun foo(x) = x is // TODO support `|` fun foo(x) = x is Pair(a, b) | Int fun foo(x) = x is (Pair(a, b) and a > b) | Int -//│ ╔══[ERROR] Unknown pattern '(' and(Pair(a, b,), >(a, b,),) ')' +//│ ╔══[ERROR] unknown pattern '(' and(Pair(a, b,), >(a, b,),) ')' //│ ║ l.80: fun foo(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] type identifier `|` not found From 12bd4147d3c7348a4a5a81f268afb40ea3e8e5a9 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Mon, 15 Jan 2024 00:10:51 +0800 Subject: [PATCH 066/143] Report unreachable cases and missing else branches --- .../main/scala/mlscript/ucs/DesugarUCS.scala | 20 +-- .../ucs/stages/CoverageChecking.scala | 7 +- .../mlscript/ucs/stages/Normalization.scala | 41 ++++-- .../src/main/scala/mlscript/ucs/syntax.scala | 6 - shared/src/test/diff/ucs/AppSplits.mls | 33 +++-- .../src/test/diff/ucs/CrossBranchCapture.mls | 10 +- shared/src/test/diff/ucs/HygienicBindings.mls | 12 +- shared/src/test/diff/ucs/SplitAfterOp.mls | 125 ++++++++++-------- shared/src/test/diff/ucs/SplitAnd.mls | 32 +++-- shared/src/test/diff/ucs/SplitBeforeOp.mls | 48 ++++--- shared/src/test/diff/ucs/SplitOps.mls | 22 +-- shared/src/test/diff/ucs/ThenIndent.mls | 3 + shared/src/test/diff/ucs/TrivialIf.mls | 18 ++- shared/src/test/diff/ucs/WeirdIf.mls | 39 ++++-- shared/src/test/diff/ucs/Wildcard.mls | 52 ++++++-- todo.md | 8 +- 16 files changed, 301 insertions(+), 175 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index facff56d..1b5d49c7 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -138,14 +138,18 @@ trait DesugarUCS extends Transformation } protected implicit class SourceSplitOps[+B <: s.Branch](these: s.Split[B]) { - def ++[BB >: B <: s.Branch](those: s.Split[BB]): s.Split[BB] = these match { - case s.Split.Cons(head, tail) => s.Split.Cons(head, tail ++ those) - case s.Split.Let(rec, nme, rhs, tail) => s.Split.Let(rec, nme, rhs, tail ++ those) - case s.Split.Else(_) => - raiseWarning(msg"unreachable case" -> these.toLoc) - these - case s.Split.Nil => those - } + def ++[BB >: B <: s.Branch](those: s.Split[BB]): s.Split[BB] = + if (those === s.Split.Nil) these else (these match { + case s.Split.Cons(head, tail) => s.Split.Cons(head, tail ++ those) + case s.Split.Let(rec, nme, rhs, tail) => s.Split.Let(rec, nme, rhs, tail ++ those) + case s.Split.Else(_) => + raiseWarning( + msg"unreachable case" -> those.toLoc, + msg"because this branch covers the case" -> these.toLoc + ) + these + case s.Split.Nil => those + }) } protected implicit class CoreSplitOps(these: c.Split) { diff --git a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala index 1ac91959..b3dfc2a0 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala @@ -3,11 +3,13 @@ package mlscript.ucs.stages import mlscript.{Case, CaseBranches, CaseOf, Let, Lit, Loc, NoCases, Term, Var, Wildcard} import mlscript.{Diagnostic, ErrorReport, WarningReport} import mlscript.Message, Message.MessageContext +import mlscript.ucs.DesugarUCS import mlscript.ucs.context.{Context, CaseSet, NamedScrutineeData, MatchRegistry, ScrutineeData, SeenRegistry} +import mlscript.pretyper.Traceable import mlscript.pretyper.symbol._ import mlscript.utils._, shorthands._ -trait CoverageChecking { self: mlscript.pretyper.Traceable => +trait CoverageChecking { self: DesugarUCS with Traceable => import CoverageChecking._ def checkCoverage(term: Term)(implicit context: Context): Ls[Diagnostic] = { @@ -28,6 +30,9 @@ trait CoverageChecking { self: mlscript.pretyper.Traceable => )) term match { case Let(_, _, _, body) => checkCoverage(body, pending, working, seen) + case CaseOf(scrutineeVar: Var, Case(Var("true"), body, NoCases)) if context.isTestVar(scrutineeVar) => + raiseError(msg"missing else branch" -> body.toLoc) + Nil case CaseOf(ScrutineeData.WithVar(scrutinee, scrutineeVar), cases) => println(s"scrutinee: ${scrutineeVar.name}") // If the scrutinee is still pending (i.e., not matched yet), then we diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index b038862c..07d1ad23 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -21,9 +21,18 @@ trait Normalization { self: DesugarUCS with Traceable => private def fillImpl(these: Split, those: Split, deep: Bool)(implicit scope: Scope, context: Context, - generatedVars: Set[Var] + generatedVars: Set[Var], + shouldReportDiscarded: Bool ): Split = - if (these.hasElse) these else (these match { + if (these.hasElse) { + if (those =/= Split.Nil && shouldReportDiscarded) { + raiseWarning( + msg"the case is unreachable" -> those.toLoc, + msg"because this branch already covers the case" -> these.toLoc + ) + } + these + } else (these match { case these @ Split.Cons(head, tail) => if (head.continuation.hasElse || !deep) { these.copy(tail = fillImpl(tail, those, deep)) @@ -40,7 +49,7 @@ trait Normalization { self: DesugarUCS with Traceable => val concatenated = these.copy(tail = fillImpl(tail, thoseWithShadowed, deep)) Split.Let(false, fresh, nme, concatenated) } else { - these.copy(tail = fillImpl(tail, those, deep)(scope, context, generatedVars + nme)) + these.copy(tail = fillImpl(tail, those, deep)(scope, context, generatedVars + nme, false)) } case _: Split.Else => these case Split.Nil => @@ -49,11 +58,25 @@ trait Normalization { self: DesugarUCS with Traceable => }) private implicit class SplitOps(these: Split) { - def fill(those: Split, deep: Bool)(implicit scope: Scope, context: Context, generatedVars: Set[Var]): Split = + /** + * Fill the split into the previous split. + * + * @param those the split to append + * @param deep whether we should also fill the leading branches + * @param shouldReportDiscarded whether we should raise an error if the given + * split is discarded because of the else branch + * @param generatedVars the generated variables which have been declared + * @return the concatenated split + */ + def fill(those: Split, deep: Bool, shouldReportDiscarded: Bool)(implicit + scope: Scope, + context: Context, + generatedVars: Set[Var], + ): Split = trace(s"fill <== ${generatedVars.iterator.map(_.name).mkString("{", ", ", "}")}") { println(s"LHS: ${showSplit(these)}") println(s"RHS: ${showSplit(those)}") - fillImpl(these, those, deep) + fillImpl(these, those, deep)(scope, context, generatedVars, shouldReportDiscarded) }(sp => s"fill ==> ${showSplit(sp)}") def :++(tail: => Split): Split = { @@ -90,17 +113,17 @@ trait Normalization { self: DesugarUCS with Traceable => case Split.Cons(Branch(scrutinee, Pattern.Name(nme), continuation), tail) => println(s"ALIAS: ${scrutinee.name} is ${nme.name}") val (wrap, realTail) = preventShadowing(nme, tail) - wrap(Let(false, nme, scrutinee, normalizeToTerm(continuation.fill(realTail, deep = false)))) + wrap(Let(false, nme, scrutinee, normalizeToTerm(continuation.fill(realTail, false, true)))) // Skip Boolean conditions as scrutinees, because they only appear once. case Split.Cons(Branch(test, pattern @ Pattern.Class(nme @ Var("true"), _), continuation), tail) => println(s"TRUE: ${test.name} is true") - val trueBranch = normalizeToTerm(continuation.fill(tail, deep = false)) + val trueBranch = normalizeToTerm(continuation.fill(tail, false, false)) val falseBranch = normalizeToCaseBranches(tail) CaseOf(test, Case(nme, trueBranch, falseBranch)(refined = false)) case Split.Cons(Branch(ScrutineeData.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Literal(literal), continuation), tail) => println(s"LITERAL: ${scrutineeVar.name} is ${literal.idStr}") println(s"entire split: ${showSplit(split)}") - val concatenatedTrueBranch = continuation.fill(tail, deep = false) + val concatenatedTrueBranch = continuation.fill(tail, false, false) // println(s"true branch: ${showSplit(concatenatedTrueBranch)}") val trueBranch = normalizeToTerm(specialize(concatenatedTrueBranch, true)(scrutineeVar, scrutinee, pattern, context)) // println(s"false branch: ${showSplit(tail)}") @@ -109,7 +132,7 @@ trait Normalization { self: DesugarUCS with Traceable => case Split.Cons(Branch(ScrutineeData.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Class(nme, rfd), continuation), tail) => println(s"CLASS: ${scrutineeVar.name} is ${nme.name}") // println(s"match ${scrutineeVar.name} with $nme (has location: ${nme.toLoc.isDefined})") - val trueBranch = normalizeToTerm(specialize(continuation.fill(tail, deep = false), true)(scrutineeVar, scrutinee, pattern, context)) + val trueBranch = normalizeToTerm(specialize(continuation.fill(tail, false, false), true)(scrutineeVar, scrutinee, pattern, context)) val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context)) CaseOf(scrutineeVar, Case(nme, trueBranch, falseBranch)(refined = pattern.refined)) case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => diff --git a/shared/src/main/scala/mlscript/ucs/syntax.scala b/shared/src/main/scala/mlscript/ucs/syntax.scala index 5fd25567..afbef74f 100644 --- a/shared/src/main/scala/mlscript/ucs/syntax.scala +++ b/shared/src/main/scala/mlscript/ucs/syntax.scala @@ -52,12 +52,6 @@ package object syntax { } sealed abstract class Split[+SomeBranch <: Branch] extends Located { - def ++[OtherBranch >: SomeBranch <: Branch](that: Split[OtherBranch]): Split[OtherBranch] = this match { - case Split.Cons(head, tail) => Split.Cons(head, tail ++ that) - case Split.Let(rec, nme, rhs, tail) => Split.Let(rec, nme, rhs, tail ++ that) - case Split.Else(_) => this - case Split.Nil => that - } def ::[OtherBranch >: SomeBranch <: Branch](head: OtherBranch): Split[OtherBranch] = Split.Cons(head, this) } object Split { diff --git a/shared/src/test/diff/ucs/AppSplits.mls b/shared/src/test/diff/ucs/AppSplits.mls index 99caea25..fe952e5f 100644 --- a/shared/src/test/diff/ucs/AppSplits.mls +++ b/shared/src/test/diff/ucs/AppSplits.mls @@ -20,6 +20,9 @@ if foo of //│ ╟── Note: 'if' expression starts here: //│ ║ l.9: if foo of //│ ╙── ^^ +//│ ╔══[ERROR] missing else branch +//│ ║ l.10: 0 then "a" +//│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.10: 0 then "a" //│ ║ ^ @@ -35,30 +38,33 @@ if foo of 1, 0 then "a" 1 then "b" //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause -//│ ║ l.35: 0 then "a" +//│ ║ l.38: 0 then "a" //│ ║ ^^^^^^^^^^ -//│ ║ l.36: 1 then "b" +//│ ║ l.39: 1 then "b" //│ ╙── ^^^^^^^^^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application instead -//│ ║ l.34: if foo of 1, +//│ ║ l.37: if foo of 1, //│ ║ ^^^^^^^^^ -//│ ║ l.35: 0 then "a" +//│ ║ l.38: 0 then "a" //│ ║ ^^ //│ ╟── Note: 'if' expression starts here: -//│ ║ l.34: if foo of 1, +//│ ║ l.37: if foo of 1, //│ ╙── ^^ +//│ ╔══[ERROR] missing else branch +//│ ║ l.38: 0 then "a" +//│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.34: if foo of 1, +//│ ║ l.37: if foo of 1, //│ ║ ^^^^^^^^^ -//│ ║ l.35: 0 then "a" +//│ ║ l.38: 0 then "a" //│ ║ ^^ //│ ╟── argument list of type `[1, ()]` does not match type `[?a]` -//│ ║ l.34: if foo of 1, +//│ ║ l.37: if foo of 1, //│ ║ ^^ -//│ ║ l.35: 0 then "a" +//│ ║ l.38: 0 then "a" //│ ╙── ^^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.35: 0 then "a" +//│ ║ l.38: 0 then "a" //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ () @@ -72,10 +78,13 @@ if foo (0) then "a" (1) then "b" //│ ╔══[PARSE ERROR] Unexpected parenthesis section here -//│ ║ l.73: (1) then "b" +//│ ║ l.79: (1) then "b" //│ ╙── ^^^ +//│ ╔══[ERROR] missing else branch +//│ ║ l.78: (0) then "a" +//│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.72: (0) then "a" +//│ ║ l.78: (0) then "a" //│ ║ ^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ "a" diff --git a/shared/src/test/diff/ucs/CrossBranchCapture.mls b/shared/src/test/diff/ucs/CrossBranchCapture.mls index ac96606b..740f0f63 100644 --- a/shared/src/test/diff/ucs/CrossBranchCapture.mls +++ b/shared/src/test/diff/ucs/CrossBranchCapture.mls @@ -1,7 +1,7 @@ :PreTyper -fun (~~>) expect(a, b) = if a is b then () else error -//│ fun (~~>) expect: (anything, anything) -> () +fun (~~>) expect(a, b) = if a === b then () else error +//│ fun (~~>) expect: forall 'a. (Eql['a], 'a) -> () class Numb(n: Int) //│ class Numb(n: Int) @@ -13,7 +13,7 @@ fun process(e) = Numb(n) and n > 0 then n Numb(m) then n //│ ╔══[ERROR] identifier `n` not found -//│ ║ l.14: Numb(m) then n +//│ ║ l.21: Numb(m) then n //│ ╙── ^ //│ fun process: Numb -> Int @@ -58,10 +58,10 @@ fun process(e) = Pair(Vec(n), Numb(n)) then n Pair(Numb(n), Vec(n)) then n //│ ╔══[ERROR] identifier `n` not found -//│ ║ l.57: Pair(Vec(xs), Vec(ys)) then n +//│ ║ l.64: Pair(Vec(xs), Vec(ys)) then n //│ ╙── ^ //│ ╔══[ERROR] identifier not found: n -//│ ║ l.57: Pair(Vec(xs), Vec(ys)) then n +//│ ║ l.64: Pair(Vec(xs), Vec(ys)) then n //│ ╙── ^ //│ fun process: Pair[Numb | Vec, Numb | Vec] -> (Int | Numb | error) //│ Code generation encountered an error: diff --git a/shared/src/test/diff/ucs/HygienicBindings.mls b/shared/src/test/diff/ucs/HygienicBindings.mls index 622a1f2b..e7740ea7 100644 --- a/shared/src/test/diff/ucs/HygienicBindings.mls +++ b/shared/src/test/diff/ucs/HygienicBindings.mls @@ -1,7 +1,7 @@ :PreTyper -fun (~~>) expect(a, b) = if a is b then () else error -//│ fun (~~>) expect: (anything, anything) -> () +fun (~~>) expect(a, b) = if a === b then () else error +//│ fun (~~>) expect: forall 'a. (Eql['a], 'a) -> () type Option[out T] = None | Some[T] module None @@ -145,14 +145,18 @@ fun h4(x, y, p) = //│ fun h4: forall 'a 'b. (Object & 'a, 'b, 'a -> Bool) -> ("default" | 'a | 'b) h4("should be me", "not me", _ => true) ~~> "should be me" -h4(None, "not me", _ => true) ~~> None +h4(None, "not me", _ => true) h4(None, "should be me", _ => false) ~~> "should be me" h4("anything", "not me", _ => false) ~~> "default" +//│ ╔══[ERROR] Module 'None' does not support equality comparison because it does not have a parameter list +//│ ║ l.149: h4(None, "should be me", _ => false) ~~> "should be me" +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ TEST CASE FAILURE: There was an unexpected type error //│ () //│ res //│ = undefined //│ res -//│ = undefined +//│ = None { class: [class None] } //│ res //│ = undefined //│ res diff --git a/shared/src/test/diff/ucs/SplitAfterOp.mls b/shared/src/test/diff/ucs/SplitAfterOp.mls index 8ef383c1..8ff3a4ca 100644 --- a/shared/src/test/diff/ucs/SplitAfterOp.mls +++ b/shared/src/test/diff/ucs/SplitAfterOp.mls @@ -4,6 +4,9 @@ fun f(x, b) = if x == 0 and b then 0 +//│ ╔══[ERROR] missing else branch +//│ ║ l.6: 0 and b then 0 +//│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.6: 0 and b then 0 //│ ║ ^ @@ -20,29 +23,29 @@ fun f(x, y) = 5 then 0 7 then 0 //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.19: if x == y + +//│ ║ l.22: if x == y + //│ ║ ^ -//│ ║ l.20: 5 then 0 +//│ ║ l.23: 5 then 0 //│ ║ ^^^^^ //│ ╟── operator application of type `Bool` is not an instance of type `Int` -//│ ║ l.19: if x == y + +//│ ║ l.22: if x == y + //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.19: if x == y + +//│ ║ l.22: if x == y + //│ ║ ^ -//│ ║ l.20: 5 then 0 +//│ ║ l.23: 5 then 0 //│ ║ ^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.19: if x == y + +//│ ║ l.22: if x == y + //│ ║ ^ -//│ ║ l.20: 5 then 0 +//│ ║ l.23: 5 then 0 //│ ║ ^^^^^^^^^^^^^ -//│ ║ l.21: 7 then 0 +//│ ║ l.24: 7 then 0 //│ ║ ^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.21: 7 then 0 +//│ ║ l.24: 7 then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Num) -> 0 @@ -53,39 +56,39 @@ fun f(x, y) = 5 then 0 6 + 7 then 0 //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.52: if x == y * +//│ ║ l.55: if x == y * //│ ║ ^ -//│ ║ l.53: 5 then 0 +//│ ║ l.56: 5 then 0 //│ ║ ^^^^^ //│ ╟── operator application of type `Bool` is not an instance of type `Int` -//│ ║ l.52: if x == y * +//│ ║ l.55: if x == y * //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.52: if x == y * +//│ ║ l.55: if x == y * //│ ║ ^ -//│ ║ l.53: 5 then 0 +//│ ║ l.56: 5 then 0 //│ ║ ^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.52: if x == y * +//│ ║ l.55: if x == y * //│ ║ ^ -//│ ║ l.53: 5 then 0 +//│ ║ l.56: 5 then 0 //│ ║ ^^^^^^^^^^^^^ -//│ ║ l.54: 6 + 7 then 0 +//│ ║ l.57: 6 + 7 then 0 //│ ║ ^^^^^^^^^ //│ ╟── operator application of type `Bool` is not an instance of type `Int` -//│ ║ l.52: if x == y * +//│ ║ l.55: if x == y * //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.52: if x == y * +//│ ║ l.55: if x == y * //│ ║ ^ -//│ ║ l.53: 5 then 0 +//│ ║ l.56: 5 then 0 //│ ║ ^^^^^^^^^^^^^ -//│ ║ l.54: 6 + 7 then 0 +//│ ║ l.57: 6 + 7 then 0 //│ ║ ^^^^^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.54: 6 + 7 then 0 +//│ ║ l.57: 6 + 7 then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Num) -> 0 @@ -97,32 +100,32 @@ fun f(x, y) = 5 then 0 7 then 0 //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.96: y + +//│ ║ l.99: y + //│ ║ ^ -//│ ║ l.97: 5 then 0 -//│ ║ ^^^^^^^ +//│ ║ l.100: 5 then 0 +//│ ║ ^^^^^^^ //│ ╟── operator application of type `Bool` is not an instance of type `Int` -//│ ║ l.95: if x == +//│ ║ l.98: if x == //│ ║ ^^^^ -//│ ║ l.96: y + +//│ ║ l.99: y + //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.96: y + +//│ ║ l.99: y + //│ ║ ^ -//│ ║ l.97: 5 then 0 -//│ ║ ^^^^^^^ +//│ ║ l.100: 5 then 0 +//│ ║ ^^^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.96: y + +//│ ║ l.99: y + //│ ║ ^ -//│ ║ l.97: 5 then 0 -//│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.98: 7 then 0 -//│ ║ ^^^^^^^ +//│ ║ l.100: 5 then 0 +//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.101: 7 then 0 +//│ ║ ^^^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.98: 7 then 0 -//│ ║ ^ +//│ ║ l.101: 7 then 0 +//│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Num) -> 0 @@ -130,12 +133,15 @@ fun f(x, y) = fun f(x, b) = if x == 1 and b then 0 +//│ ╔══[ERROR] missing else branch +//│ ║ l.135: 1 and b then 0 +//│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.132: 1 and b then 0 +//│ ║ l.135: 1 and b then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.132: 1 and b then 0 +//│ ║ l.135: 1 and b then 0 //│ ║ ^^^^^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Bool) -> 0 @@ -147,15 +153,15 @@ fun toEnglish(x) = true then "t" 0 then "z" //│ ╔══[ERROR] Type mismatch in operator application: -//│ ║ l.146: if x == +//│ ║ l.152: if x == //│ ║ ^^^^ -//│ ║ l.147: true then "t" +//│ ║ l.153: true then "t" //│ ║ ^^^^^^^^ //│ ╟── reference of type `true` is not an instance of `Num` -//│ ║ l.147: true then "t" +//│ ║ l.153: true then "t" //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.148: 0 then "z" +//│ ║ l.154: 0 then "z" //│ ║ ^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun toEnglish: Num -> ("t" | "z") @@ -166,17 +172,17 @@ fun toEnglish(x) = 0 then "z" true then "t" //│ ╔══[ERROR] Type mismatch in operator application: -//│ ║ l.165: if x == +//│ ║ l.171: if x == //│ ║ ^^^^ -//│ ║ l.166: 0 then "z" +//│ ║ l.172: 0 then "z" //│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.167: true then "t" +//│ ║ l.173: true then "t" //│ ║ ^^^^^^^^ //│ ╟── reference of type `true` is not an instance of `Num` -//│ ║ l.167: true then "t" +//│ ║ l.173: true then "t" //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.167: true then "t" +//│ ║ l.173: true then "t" //│ ║ ^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun toEnglish: Num -> ("t" | "z") @@ -187,7 +193,7 @@ fun toEnglish(x) = 1 then "o" 0 then "z" //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.188: 0 then "z" +//│ ║ l.194: 0 then "z" //│ ║ ^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun toEnglish: Num -> ("o" | "z") @@ -204,29 +210,32 @@ fun toEnglish(x) = if x == else 1 //│ ╔══[PARSE ERROR] Unexpected indented block in expression position -//│ ║ l.205: else 1 +//│ ║ l.211: else 1 //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here -//│ ║ l.205: else 1 +//│ ║ l.211: else 1 //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead -//│ ║ l.204: if x == +//│ ║ l.210: if x == //│ ║ ^^^^ -//│ ║ l.205: else 1 +//│ ║ l.211: else 1 //│ ║ ^^^^ //│ ╟── Note: 'if' expression starts here: -//│ ║ l.204: if x == +//│ ║ l.210: if x == //│ ╙── ^^ +//│ ╔══[ERROR] missing else branch +//│ ║ l.211: else 1 +//│ ╙── ^ //│ ╔══[ERROR] Type mismatch in operator application: -//│ ║ l.204: if x == +//│ ║ l.210: if x == //│ ║ ^^^^ -//│ ║ l.205: else 1 +//│ ║ l.211: else 1 //│ ║ ^^^^ //│ ╟── undefined literal of type `()` is not an instance of type `Num` -//│ ║ l.205: else 1 +//│ ║ l.211: else 1 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.205: else 1 +//│ ║ l.211: else 1 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun toEnglish: Num -> () diff --git a/shared/src/test/diff/ucs/SplitAnd.mls b/shared/src/test/diff/ucs/SplitAnd.mls index f615affb..b40269f0 100644 --- a/shared/src/test/diff/ucs/SplitAnd.mls +++ b/shared/src/test/diff/ucs/SplitAnd.mls @@ -13,7 +13,6 @@ class B() //│ class B() :e -// TODO: Should report missing else branches. fun f(x) = if x == 0 and x is @@ -21,31 +20,46 @@ fun f(x) = B() then "B" x == 0 then "lol" else "bruh" +//│ ╔══[ERROR] missing else branch +//│ ║ l.18: x is +//│ ║ ^^^^ +//│ ║ l.19: A() then "A" +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.20: B() then "B" +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.21: x == 0 then "lol" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.22: else "bruh" +//│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.19: x is +//│ ║ l.18: x is //│ ║ ^^^^ -//│ ║ l.20: A() then "A" +//│ ║ l.19: A() then "A" //│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.21: B() then "B" +//│ ║ l.20: B() then "B" //│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.22: x == 0 then "lol" +//│ ║ l.21: x == 0 then "lol" //│ ║ ^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.23: else "bruh" +//│ ║ l.22: else "bruh" //│ ║ ^^^^^^^^^^^^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: Num -> ("A" | "B" | "bruh" | "lol") :e -// TODO: Should report missing else branches. fun f(x, y) = if x == 0 and y == 0 then "bruh" else "lol" +//│ ╔══[ERROR] missing else branch +//│ ║ l.52: y == 0 then "bruh" +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.53: else "lol" +//│ ╙── ^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.43: y == 0 then "bruh" +//│ ║ l.52: y == 0 then "bruh" //│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.44: else "lol" +//│ ║ l.53: else "lol" //│ ║ ^^^^^^^^^^^^^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Num) -> ("bruh" | "lol") diff --git a/shared/src/test/diff/ucs/SplitBeforeOp.mls b/shared/src/test/diff/ucs/SplitBeforeOp.mls index 0c6cc6da..c4bd59a4 100644 --- a/shared/src/test/diff/ucs/SplitBeforeOp.mls +++ b/shared/src/test/diff/ucs/SplitBeforeOp.mls @@ -7,6 +7,9 @@ if x //│ ╔══[ERROR] identifier `x` not found //│ ║ l.5: if x //│ ╙── ^ +//│ ╔══[ERROR] missing else branch +//│ ║ l.6: == 0 then 0 +//│ ╙── ^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.5: if x //│ ╙── ^ @@ -24,22 +27,25 @@ if x is A and y then 0 //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.23: if x +//│ ║ l.26: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier `A` not found -//│ ║ l.24: is A and +//│ ║ l.27: is A and //│ ╙── ^ //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.23: if x +//│ ║ l.26: if x //│ ╙── ^ //│ ╔══[ERROR] identifier `y` not found -//│ ║ l.25: y then 0 +//│ ║ l.28: y then 0 //│ ╙── ^ +//│ ╔══[ERROR] missing else branch +//│ ║ l.28: y then 0 +//│ ╙── ^ //│ ╔══[ERROR] identifier not found: x -//│ ║ l.23: if x +//│ ║ l.26: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: A -//│ ║ l.24: is A and +//│ ║ l.27: is A and //│ ╙── ^ //│ error //│ Code generation encountered an error: @@ -52,22 +58,22 @@ if x y then 0 else 1 //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.50: if x +//│ ║ l.56: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier `A` not found -//│ ║ l.51: is A and +//│ ║ l.57: is A and //│ ╙── ^ //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.50: if x +//│ ║ l.56: if x //│ ╙── ^ //│ ╔══[ERROR] identifier `y` not found -//│ ║ l.52: y then 0 +//│ ║ l.58: y then 0 //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x -//│ ║ l.50: if x +//│ ║ l.56: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: A -//│ ║ l.51: is A and +//│ ║ l.57: is A and //│ ╙── ^ //│ error //│ Code generation encountered an error: @@ -81,31 +87,31 @@ if x A() then "A" B() then "B" //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.78: if x +//│ ║ l.84: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier `A` not found -//│ ║ l.81: A() then "A" +//│ ║ l.87: A() then "A" //│ ╙── ^ //│ ╔══[ERROR] type identifier `B` not found -//│ ║ l.82: B() then "B" +//│ ║ l.88: B() then "B" //│ ╙── ^ //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.78: if x +//│ ║ l.84: if x //│ ╙── ^ //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.78: if x +//│ ║ l.84: if x //│ ╙── ^ //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.78: if x +//│ ║ l.84: if x //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x -//│ ║ l.78: if x +//│ ║ l.84: if x //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x -//│ ║ l.78: if x +//│ ║ l.84: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: A -//│ ║ l.81: A() then "A" +//│ ║ l.87: A() then "A" //│ ╙── ^ //│ 0 | error //│ Code generation encountered an error: diff --git a/shared/src/test/diff/ucs/SplitOps.mls b/shared/src/test/diff/ucs/SplitOps.mls index c346c509..c1f34e29 100644 --- a/shared/src/test/diff/ucs/SplitOps.mls +++ b/shared/src/test/diff/ucs/SplitOps.mls @@ -21,6 +21,9 @@ fun f(x) = is Left(v) then 0 is Right(v) then 1 <> undefined then 2 +//│ ╔══[ERROR] missing else branch +//│ ║ l.23: <> undefined then 2 +//│ ╙── ^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.20: if x //│ ║ ^ @@ -48,25 +51,25 @@ fun f(x) = is Some(xv) and y is Some(yv) then xv + yv is None() and y is None() then 0 //│ ╔══[ERROR] identifier `y` not found -//│ ║ l.48: is Some(xv) and y is Some(yv) then xv + yv +//│ ║ l.51: is Some(xv) and y is Some(yv) then xv + yv //│ ╙── ^ //│ ╔══[ERROR] identifier `y` not found -//│ ║ l.49: is None() and y is None() then 0 +//│ ║ l.52: is None() and y is None() then 0 //│ ╙── ^ //│ ╔══[ERROR] identifier `y` not found -//│ ║ l.48: is Some(xv) and y is Some(yv) then xv + yv +//│ ║ l.51: is Some(xv) and y is Some(yv) then xv + yv //│ ╙── ^ //│ ╔══[ERROR] identifier `y` not found -//│ ║ l.48: is Some(xv) and y is Some(yv) then xv + yv +//│ ║ l.51: is Some(xv) and y is Some(yv) then xv + yv //│ ╙── ^ //│ ╔══[ERROR] identifier `y` not found -//│ ║ l.49: is None() and y is None() then 0 +//│ ║ l.52: is None() and y is None() then 0 //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y -//│ ║ l.48: is Some(xv) and y is Some(yv) then xv + yv +//│ ║ l.51: is Some(xv) and y is Some(yv) then xv + yv //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y -//│ ║ l.49: is None() and y is None() then 0 +//│ ║ l.52: is None() and y is None() then 0 //│ ╙── ^ //│ fun f: (None | Some[Int]) -> Int //│ Code generation encountered an error: @@ -89,8 +92,11 @@ class C() fun f(a, b, c) = if a == 0 and b is B() and c is C() then 0 +//│ ╔══[ERROR] missing else branch +//│ ║ l.94: == 0 and b is B() and c is C() then 0 +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.91: == 0 and b is B() and c is C() then 0 +//│ ║ l.94: == 0 and b is B() and c is C() then 0 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, B, C) -> 0 diff --git a/shared/src/test/diff/ucs/ThenIndent.mls b/shared/src/test/diff/ucs/ThenIndent.mls index df126db8..138c3f02 100644 --- a/shared/src/test/diff/ucs/ThenIndent.mls +++ b/shared/src/test/diff/ucs/ThenIndent.mls @@ -19,6 +19,9 @@ x => if x == //│ ╟── Note: 'if' expression starts here: //│ ║ l.5: x => if x == //│ ╙── ^^ +//│ ╔══[ERROR] missing else branch +//│ ║ l.6: 0 +//│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.6: 0 //│ ║ ^ diff --git a/shared/src/test/diff/ucs/TrivialIf.mls b/shared/src/test/diff/ucs/TrivialIf.mls index 5f1c8070..1a34cf36 100644 --- a/shared/src/test/diff/ucs/TrivialIf.mls +++ b/shared/src/test/diff/ucs/TrivialIf.mls @@ -44,6 +44,9 @@ map(None, inc) :e fun f(a, b) = if a and b then 0 +//│ ╔══[ERROR] missing else branch +//│ ║ l.46: fun f(a, b) = if a and b then 0 +//│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.46: fun f(a, b) = if a and b then 0 //│ ║ ^ @@ -58,8 +61,11 @@ fun f(a, b) = if a and b then 0 fun f(x, y) = if x == y + 5 then 0 else if x == y + 7 then 0 +//│ ╔══[ERROR] missing else branch +//│ ║ l.63: else if x == y + 7 then 0 +//│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.60: else if x == y + 7 then 0 +//│ ║ l.63: else if x == y + 7 then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Int) -> 0 @@ -69,7 +75,7 @@ fun foo(x) = if x is Some (0) then 0 (1) then 1 //│ ╔══[PARSE ERROR] Unexpected parenthesis section here -//│ ║ l.70: (1) then 1 +//│ ║ l.76: (1) then 1 //│ ╙── ^^^ //│ /!!!\ Uncaught error: java.lang.StackOverflowError @@ -78,15 +84,15 @@ fun foo(x) = if x is Some of 0 then 0 1 then 1 //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here -//│ ║ l.78: 0 then 0 +//│ ║ l.84: 0 then 0 //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead -//│ ║ l.77: fun foo(x) = if x is Some of +//│ ║ l.83: fun foo(x) = if x is Some of //│ ║ ^^^^^^^^^^^^ -//│ ║ l.78: 0 then 0 +//│ ║ l.84: 0 then 0 //│ ║ ^^^ //│ ╟── Note: 'if' expression starts here: -//│ ║ l.77: fun foo(x) = if x is Some of +//│ ║ l.83: fun foo(x) = if x is Some of //│ ╙── ^^ //│ fun foo: Some[0] -> () diff --git a/shared/src/test/diff/ucs/WeirdIf.mls b/shared/src/test/diff/ucs/WeirdIf.mls index 53f0d576..d0c107d9 100644 --- a/shared/src/test/diff/ucs/WeirdIf.mls +++ b/shared/src/test/diff/ucs/WeirdIf.mls @@ -1,18 +1,25 @@ :PreTyper -// FIXME: Should report duplicated else branches. -// :w +:w if _ then 0 else 0 else 1 +//│ ╔══[WARNING] unreachable case +//│ ║ l.5: _ then 0 +//│ ╙── ^ +//│ ╔══[WARNING] unreachable case +//│ ║ l.5: _ then 0 +//│ ╙── ^ //│ 0 //│ res //│ = 0 -// FIXME -// :w +:w if else 0 else 1 +//│ ╔══[WARNING] unreachable case +//│ ║ l.19: if else 0 else 1 +//│ ╙── ^ //│ 0 //│ res //│ = 0 @@ -20,10 +27,10 @@ if else 0 else 1 :w fun f(x) = if x is else 0 else 1 //│ ╔══[WARNING] the case is unreachable -//│ ║ l.21: fun f(x) = if x is else 0 else 1 +//│ ║ l.28: fun f(x) = if x is else 0 else 1 //│ ║ ^ //│ ╟── because this branch covers the case -//│ ║ l.21: fun f(x) = if x is else 0 else 1 +//│ ║ l.28: fun f(x) = if x is else 0 else 1 //│ ╙── ^ //│ fun f: anything -> 0 @@ -33,8 +40,11 @@ fun f(x) = if x is else 0 :e if true then 0 +//│ ╔══[ERROR] missing else branch +//│ ║ l.42: then 0 +//│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.35: then 0 +//│ ║ l.42: then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ 0 @@ -48,21 +58,24 @@ fun f(x) = if x === else "bruh" //│ ╔══[PARSE ERROR] Unexpected indented block in expression position -//│ ║ l.49: else "bruh" +//│ ║ l.59: else "bruh" //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here -//│ ║ l.49: else "bruh" +//│ ║ l.59: else "bruh" //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead -//│ ║ l.48: if x === +//│ ║ l.58: if x === //│ ║ ^^^^^ -//│ ║ l.49: else "bruh" +//│ ║ l.59: else "bruh" //│ ║ ^^^^ //│ ╟── Note: 'if' expression starts here: -//│ ║ l.48: if x === +//│ ║ l.58: if x === //│ ╙── ^^ +//│ ╔══[ERROR] missing else branch +//│ ║ l.59: else "bruh" +//│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.49: else "bruh" +//│ ║ l.59: else "bruh" //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: Eql[()] -> () diff --git a/shared/src/test/diff/ucs/Wildcard.mls b/shared/src/test/diff/ucs/Wildcard.mls index 9cc79fd9..2b817b7c 100644 --- a/shared/src/test/diff/ucs/Wildcard.mls +++ b/shared/src/test/diff/ucs/Wildcard.mls @@ -1,5 +1,8 @@ :PreTyper +fun (++) strcat(a, b) = concat(a)(b) +//│ fun (++) strcat: (Str, Str) -> Str + type Option[T] = None | Some[T] module None class Some[T](val value: T) @@ -14,15 +17,15 @@ class Right[B](val rightValue: B) //│ class Left[A](leftValue: A) //│ class Right[B](rightValue: B) -// FIXME + fun w1(x, e_0, e_1) = if x is Left(None) then "Left of None" Right(None) then "Right of None" _ and e_0 is y_0 and x is - Left(Some(lv)) then concat("Left of Some of ")(toString(lv)) + Left(Some(lv)) then "Left of Some of " ++ toString(lv) _ and e_1 is y_1 and x is - Right(Some(rv)) then concat("Right of Some of ")(toString(rv)) + Right(Some(rv)) then "Right of Some of " ++ toString(rv) //│ fun w1: forall 'a. (Left[None | Object & ~#None & ~#Some | Some[anything]] | Object & ~#Left & ~#Right | Right[None | Some[anything]], anything, 'a) -> (Str | 'a) w1(Left(None), "a", "b") @@ -63,7 +66,7 @@ w2(0, x => false) fun w3(x, p) = if x is _ and p(x) then "r1" - Some(xv) then concat("r2: ")(toString(xv)) + Some(xv) then "r2: " ++ toString(xv) None then "r3" _ then "r4" //│ fun w3: forall 'a. ('a & (Object & ~#Some | Some[anything]), 'a -> Bool) -> Str @@ -105,10 +108,10 @@ w3(0, _ => false) fun w3_1(x, f) = if f(x) is _ then 0 else 1 //│ ╔══[WARNING] the case is unreachable -//│ ║ l.106: if f(x) is _ then 0 else 1 +//│ ║ l.109: if f(x) is _ then 0 else 1 //│ ║ ^ //│ ╟── because this branch covers the case -//│ ║ l.106: if f(x) is _ then 0 else 1 +//│ ║ l.109: if f(x) is _ then 0 else 1 //│ ╙── ^ //│ fun w3_1: forall 'a. ('a, 'a -> anything) -> 0 @@ -120,10 +123,15 @@ w3_1(0, _ => false) //│ res //│ = 0 -// :w -// FIXME: Should warn redundant case +:w fun w3_1_1(x, f) = if f(x) is a then a else 0 +//│ ╔══[WARNING] the case is unreachable +//│ ║ l.128: if f(x) is a then a else 0 +//│ ║ ^ +//│ ╟── because this branch already covers the case +//│ ║ l.128: if f(x) is a then a else 0 +//│ ╙── ^ //│ fun w3_1_1: forall 'a 'b. ('a, 'a -> 'b) -> 'b w3_1_1(0, x => x) @@ -140,7 +148,7 @@ w3_1_1(0, x => x + 1) // + «x is None» => "r3" fun w4(x, p) = if x is a and p(x) then "r1" - Some(xv) then concat("r2: ")(toString(xv)) + Some(xv) then "r2: " ++ toString(xv) None then "r3" _ then "r4" //│ fun w4: forall 'a. ('a & (Object & ~#Some | Some[anything]), 'a -> Bool) -> Str @@ -187,6 +195,7 @@ class Delta() // This should generate only one case expression instead of a chain of case // expressions. DO check the desugared term! +:dpt:postprocess.result fun w5(y) = if y is Alpha then "alpha" @@ -197,6 +206,17 @@ fun w5(y) = _ and y is Delta then "delta" _ then "unknown" +//│ | | | | | | | Post-processed UCS term: +//│ | | | | | | | case y*‡ of +//│ | | | | | | | Alpha*◊ -> "alpha" +//│ | | | | | | | Gamma*◊ -> "gamma" +//│ | | | | | | | Delta*◊ -> "delta" +//│ | | | | | | | Beta*◊ -> "beta" +//│ | | | | | | | _ -> +//│ | | | | | | | case y*‡ of +//│ | | | | | | | _ -> +//│ | | | | | | | case y*‡ of +//│ | | | | | | | _ -> "unknown" //│ fun w5: (Alpha | Beta | Delta | Gamma | Object & ~#Alpha & ~#Beta & ~#Delta & ~#Gamma) -> ("alpha" | "beta" | "delta" | "gamma" | "unknown") w5(0) @@ -235,8 +255,7 @@ w6("42", "42") //│ res //│ = '42' -// FIXME -// Should report warnings. + fun w7(x, f) = if x is _ and f(x) is @@ -263,3 +282,14 @@ w7(Right(99), _ => None) // => Right(99) //│ = 0 //│ res //│ = Right {} + +w7(Left(99), _ => "test") +w7(Right(99), _ => "test") +//│ Int | Right['B] +//│ where +//│ 'B :> 99 +//│ <: Int +//│ res +//│ = 100 +//│ res +//│ = 101 diff --git a/todo.md b/todo.md index 6a99be10..9f1ebcbc 100644 --- a/todo.md +++ b/todo.md @@ -3,11 +3,11 @@ problems located by test cases. ### Remaining Tasks -- [ ] Report unreachable or redundant cases +- [x] Report unreachable or redundant cases - [x] shared/src/test/diff/ucs/DirectLines.mls **OLD** - - [ ] shared/src/test/diff/ucs/SplitAnd.mls - - [ ] shared/src/test/diff/ucs/WeirdIf.mls - - [ ] shared/src/test/diff/ucs/Wildcard.mls + - [x] shared/src/test/diff/ucs/SplitAnd.mls + - [x] shared/src/test/diff/ucs/WeirdIf.mls + - [x] shared/src/test/diff/ucs/Wildcard.mls - [x] Hygenic bindings - [ ] shared/src/test/diff/ucs/CrossBranchCapture.mls - [x] shared/src/test/diff/ucs/HygienicBindings.mls From 661d26cce47fa4f7f2cf3b365fb41079702681ab Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Mon, 15 Jan 2024 00:22:07 +0800 Subject: [PATCH 067/143] Append test file changes --- .../src/test/diff/ucs/CrossBranchCapture.mls | 6 ++-- shared/src/test/diff/ucs/HygienicBindings.mls | 18 ++++------ shared/src/test/diff/ucs/WeirdIf.mls | 33 ++++++++++++------- 3 files changed, 31 insertions(+), 26 deletions(-) diff --git a/shared/src/test/diff/ucs/CrossBranchCapture.mls b/shared/src/test/diff/ucs/CrossBranchCapture.mls index 740f0f63..82f0aad1 100644 --- a/shared/src/test/diff/ucs/CrossBranchCapture.mls +++ b/shared/src/test/diff/ucs/CrossBranchCapture.mls @@ -13,7 +13,7 @@ fun process(e) = Numb(n) and n > 0 then n Numb(m) then n //│ ╔══[ERROR] identifier `n` not found -//│ ║ l.21: Numb(m) then n +//│ ║ l.14: Numb(m) then n //│ ╙── ^ //│ fun process: Numb -> Int @@ -58,10 +58,10 @@ fun process(e) = Pair(Vec(n), Numb(n)) then n Pair(Numb(n), Vec(n)) then n //│ ╔══[ERROR] identifier `n` not found -//│ ║ l.64: Pair(Vec(xs), Vec(ys)) then n +//│ ║ l.57: Pair(Vec(xs), Vec(ys)) then n //│ ╙── ^ //│ ╔══[ERROR] identifier not found: n -//│ ║ l.64: Pair(Vec(xs), Vec(ys)) then n +//│ ║ l.57: Pair(Vec(xs), Vec(ys)) then n //│ ╙── ^ //│ fun process: Pair[Numb | Vec, Numb | Vec] -> (Int | Numb | error) //│ Code generation encountered an error: diff --git a/shared/src/test/diff/ucs/HygienicBindings.mls b/shared/src/test/diff/ucs/HygienicBindings.mls index e7740ea7..eba8e945 100644 --- a/shared/src/test/diff/ucs/HygienicBindings.mls +++ b/shared/src/test/diff/ucs/HygienicBindings.mls @@ -144,23 +144,19 @@ fun h4(x, y, p) = //│ | | | | | | | _ -> "default" //│ fun h4: forall 'a 'b. (Object & 'a, 'b, 'a -> Bool) -> ("default" | 'a | 'b) -h4("should be me", "not me", _ => true) ~~> "should be me" +h4("should be me", "not me", _ => true) h4(None, "not me", _ => true) -h4(None, "should be me", _ => false) ~~> "should be me" -h4("anything", "not me", _ => false) ~~> "default" -//│ ╔══[ERROR] Module 'None' does not support equality comparison because it does not have a parameter list -//│ ║ l.149: h4(None, "should be me", _ => false) ~~> "should be me" -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ TEST CASE FAILURE: There was an unexpected type error -//│ () +h4(None, "should be me", _ => false) +h4("anything", "not me", _ => false) +//│ "anything" | "default" | "not me" //│ res -//│ = undefined +//│ = 'should be me' //│ res //│ = None { class: [class None] } //│ res -//│ = undefined +//│ = 'should be me' //│ res -//│ = undefined +//│ = 'default' :dpt:postprocess.result fun h5(x, y, p) = diff --git a/shared/src/test/diff/ucs/WeirdIf.mls b/shared/src/test/diff/ucs/WeirdIf.mls index d0c107d9..a7fed4a0 100644 --- a/shared/src/test/diff/ucs/WeirdIf.mls +++ b/shared/src/test/diff/ucs/WeirdIf.mls @@ -6,9 +6,15 @@ if else 0 else 1 //│ ╔══[WARNING] unreachable case +//│ ║ l.6: else 0 +//│ ║ ^ +//│ ╟── because this branch covers the case //│ ║ l.5: _ then 0 //│ ╙── ^ //│ ╔══[WARNING] unreachable case +//│ ║ l.7: else 1 +//│ ║ ^ +//│ ╟── because this branch covers the case //│ ║ l.5: _ then 0 //│ ╙── ^ //│ 0 @@ -18,7 +24,10 @@ else 1 :w if else 0 else 1 //│ ╔══[WARNING] unreachable case -//│ ║ l.19: if else 0 else 1 +//│ ║ l.25: if else 0 else 1 +//│ ║ ^ +//│ ╟── because this branch covers the case +//│ ║ l.25: if else 0 else 1 //│ ╙── ^ //│ 0 //│ res @@ -27,10 +36,10 @@ if else 0 else 1 :w fun f(x) = if x is else 0 else 1 //│ ╔══[WARNING] the case is unreachable -//│ ║ l.28: fun f(x) = if x is else 0 else 1 +//│ ║ l.37: fun f(x) = if x is else 0 else 1 //│ ║ ^ //│ ╟── because this branch covers the case -//│ ║ l.28: fun f(x) = if x is else 0 else 1 +//│ ║ l.37: fun f(x) = if x is else 0 else 1 //│ ╙── ^ //│ fun f: anything -> 0 @@ -41,10 +50,10 @@ fun f(x) = if x is else 0 if true then 0 //│ ╔══[ERROR] missing else branch -//│ ║ l.42: then 0 +//│ ║ l.51: then 0 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.42: then 0 +//│ ║ l.51: then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ 0 @@ -58,24 +67,24 @@ fun f(x) = if x === else "bruh" //│ ╔══[PARSE ERROR] Unexpected indented block in expression position -//│ ║ l.59: else "bruh" +//│ ║ l.68: else "bruh" //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here -//│ ║ l.59: else "bruh" +//│ ║ l.68: else "bruh" //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead -//│ ║ l.58: if x === +//│ ║ l.67: if x === //│ ║ ^^^^^ -//│ ║ l.59: else "bruh" +//│ ║ l.68: else "bruh" //│ ║ ^^^^ //│ ╟── Note: 'if' expression starts here: -//│ ║ l.58: if x === +//│ ║ l.67: if x === //│ ╙── ^^ //│ ╔══[ERROR] missing else branch -//│ ║ l.59: else "bruh" +//│ ║ l.68: else "bruh" //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.59: else "bruh" +//│ ║ l.68: else "bruh" //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: Eql[()] -> () From 20b86a7501b9c9704b24fca1fe32e13ed8afde31 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Mon, 15 Jan 2024 00:32:15 +0800 Subject: [PATCH 068/143] Fix test cases in the compiler sub-project --- compiler/shared/test/diff/mono.mls | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/compiler/shared/test/diff/mono.mls b/compiler/shared/test/diff/mono.mls index a261d75d..e2c65fdc 100644 --- a/compiler/shared/test/diff/mono.mls +++ b/compiler/shared/test/diff/mono.mls @@ -1,18 +1,18 @@ -:NewDefs +:PreTyper :mono -fun f(x: Int) = if x then 42 else 1337 +fun f(x: Int) = if not of x == 0 then 42 else 1337 //│ //│ Lifted: //│ TypingUnit { -//│ fun f$1 = (x: Int,) => if (x) then 42 else 1337 +//│ fun f$1 = (x: Int,) => if (not(==(x, 0,),)) then 42 else 1337 //│ } //│ Mono: //│ //│ Defunc result: //│ fun f$1(x) = -//│ if x then #42 else #1337 +//│ if not(==(x, #0)) then #42 else #1337 //│ fun f: (x: Int) -> (1337 | 42) :mono @@ -47,7 +47,7 @@ let b = foo(23, false) //│ if b then x else #1337 //│ fun a$1() = //│ foo$3(#42, true) -//│ fun foo: forall 'a. ('a, Object) -> (1337 | 'a) +//│ fun foo: forall 'a. ('a, Bool) -> (1337 | 'a) //│ let a: 1337 | 42 //│ let b: 1337 | 23 //│ a @@ -116,19 +116,19 @@ if 1+1 > 1 then 1 - 1 else 1*1 //│ = 0 :mono -if(b) then 1 else 2 +if not of b == 0 then 1 else 2 //│ //│ Lifted: -//│ TypingUnit {Code(List(if ('(' b ')') then 1 else 2))} +//│ TypingUnit {Code(List(if (not(==(b, 0,),)) then 1 else 2))} //│ Mono: //│ //│ Defunc result: //│ main$$0() //│ fun main$$0() = -//│ if b then #1 else #2 +//│ if not(==(b, #0)) then #1 else #2 //│ 1 | 2 //│ res -//│ = 2 +//│ = 1 :mono ((f, g) => f(g))(f => f, true) @@ -263,7 +263,7 @@ count(new List(new List(new Nil(undefined, false), true), true)) //│ fun count: forall 'a. 'a -> Int //│ Int //│ where -//│ 'a <: {hasTail: Object, l: Object & 'a & ~() | ()} +//│ 'a <: {hasTail: Bool, l: Object & 'a & ~() | ()} //│ res //│ = 2 @@ -952,13 +952,13 @@ class OneInt(a: Int){ class OneBool(b: Bool){ fun get = () -> b } -(if b then new OneInt(1) else new OneBool(true)).get() +(if not of b == 0 then new OneInt(1) else new OneBool(true)).get() //│ //│ Lifted: //│ TypingUnit { //│ class OneInt$1([a: Int,]) {fun get = () => (this).a} //│ class OneBool$2([b: Bool,]) {fun get = () => (this).b} -//│ Code(List(('(' if (b) then new OneInt$1([1,]) {} else new OneBool$2([true,]) {} ')').get())) +//│ Code(List(('(' if (not(==(b, 0,),)) then new OneInt$1([1,]) {} else new OneBool$2([true,]) {} ')').get())) //│ } //│ Mono: //│ @@ -969,7 +969,7 @@ class OneBool(b: Bool){ //│ fun get$OneBool$2(this) = //│ this.b //│ fun main$$2() = -//│ if b then new OneInt$1 (#1) else new OneBool$2 (true) match {case obj: OneInt$1 => get$OneInt$1(obj); case obj: OneBool$2 => get$OneBool$2(obj)} +//│ if not(==(b, #0)) then new OneInt$1 (#1) else new OneBool$2 (true) match {case obj: OneInt$1 => get$OneInt$1(obj); case obj: OneBool$2 => get$OneBool$2(obj)} //│ class OneInt$1(a) { //│ } //│ class OneBool$2(b) { @@ -982,7 +982,7 @@ class OneBool(b: Bool){ //│ } //│ Int | false | true //│ res -//│ = true +//│ = 1 :mono class Bar(x: Int) { From 0dd68c063ac0b589657bef5e8c45742c7d68f790 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Mon, 15 Jan 2024 02:45:13 +0800 Subject: [PATCH 069/143] Reorganize files and prepare for finalization --- .../main/scala/mlscript/ucs/DesugarUCS.scala | 7 ++++--- .../src/main/scala/mlscript/ucs/display.scala | 3 ++- .../src/main/scala/mlscript/ucs/helpers.scala | 19 ------------------- .../main/scala/mlscript/ucs/old/helpers.scala | 19 +++++++++++++++++++ .../mlscript/ucs/stages/Desugaring.scala | 3 ++- .../mlscript/ucs/stages/Normalization.scala | 17 +++++++++-------- .../mlscript/ucs/stages/Transformation.scala | 2 +- .../mlscript/ucs/{ => syntax}/core.scala | 2 +- .../scala/mlscript/ucs/syntax/package.scala | 3 +++ .../ucs/{syntax.scala => syntax/source.scala} | 4 ++-- 10 files changed, 43 insertions(+), 36 deletions(-) rename shared/src/main/scala/mlscript/ucs/{ => syntax}/core.scala (99%) create mode 100644 shared/src/main/scala/mlscript/ucs/syntax/package.scala rename shared/src/main/scala/mlscript/ucs/{syntax.scala => syntax/source.scala} (99%) diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index 1b5d49c7..a9b861a5 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -1,12 +1,13 @@ package mlscript.ucs import collection.mutable.{Map => MutMap} -import mlscript.ucs.{syntax => s, core => c}, stages._, context.{Context, ScrutineeData} +import syntax.{source => s, core => c}, stages._, context.{Context, ScrutineeData} import mlscript.ucs.display.{showNormalizedTerm, showSplit} import mlscript.pretyper.{PreTyper, Scope} import mlscript.pretyper.symbol._ import mlscript.{If, Loc, Message, Var}, Message.MessageContext, mlscript.Diagnostic.PreTyping import mlscript.utils._, shorthands._ +import syntax.core.{Branch, Split} // TODO: Rename to `Desugarer` once the old desugarer is removed. trait DesugarUCS extends Transformation @@ -230,9 +231,9 @@ trait DesugarUCS extends Transformation } } - private def traverseSplit(split: core.Split)(implicit scope: Scope): Unit = + private def traverseSplit(split: syntax.core.Split)(implicit scope: Scope): Unit = trace(s"traverseSplit <== [${scope.showLocalSymbols}]") { - import core._ + split match { case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => traverseTerm(scrutinee) diff --git a/shared/src/main/scala/mlscript/ucs/display.scala b/shared/src/main/scala/mlscript/ucs/display.scala index 2ce7656b..288c1434 100644 --- a/shared/src/main/scala/mlscript/ucs/display.scala +++ b/shared/src/main/scala/mlscript/ucs/display.scala @@ -1,11 +1,12 @@ package mlscript.ucs -import mlscript.ucs.{core => c, syntax => s} +import mlscript.ucs.syntax.{core => c, source => s} import mlscript.ucs.context.{Context} import mlscript.pretyper.symbol.{TermSymbol, TypeSymbol} import mlscript.{App, CaseOf, Fld, FldFlags, Let, Loc, Sel, SimpleTerm, Term, Tup, Var} import mlscript.{CaseBranches, Case, Wildcard, NoCases} import mlscript.utils._, shorthands._ +import syntax.core.{Branch, Split} /** All the pretty-printing stuff go here. */ package object display { diff --git a/shared/src/main/scala/mlscript/ucs/helpers.scala b/shared/src/main/scala/mlscript/ucs/helpers.scala index 74491123..f8ed428b 100644 --- a/shared/src/main/scala/mlscript/ucs/helpers.scala +++ b/shared/src/main/scala/mlscript/ucs/helpers.scala @@ -30,25 +30,6 @@ object helpers { if (newDefs) App(op, PlainTup(lhs, rhs)) else App(App(op, mkMonuple(lhs)), mkMonuple(rhs)) - /** - * Split a term joined by `and` into a list of terms. - * E.g. `x and y and z` will be split into `x`, `y`, and `z`. - * - * @return a list of sub-terms of `t` - */ - def splitAnd(t: Term): Ls[Term] = - t match { - case App( - App(Var("and"), - Tup((_ -> Fld(_, lhs)) :: Nil)), - Tup((_ -> Fld(_, rhs)) :: Nil) - ) => // * Old-style operators - splitAnd(lhs) :+ rhs - case App(Var("and"), PlainTup(lhs, rhs)) => - splitAnd(lhs) :+ rhs - case _ => t :: Nil - } - /** * Split a term into two parts: the pattern and the extra test. * This is used to extract patterns from UCS conjunctions. For example, diff --git a/shared/src/main/scala/mlscript/ucs/old/helpers.scala b/shared/src/main/scala/mlscript/ucs/old/helpers.scala index bbfc7989..4d2df1e7 100644 --- a/shared/src/main/scala/mlscript/ucs/old/helpers.scala +++ b/shared/src/main/scala/mlscript/ucs/old/helpers.scala @@ -6,6 +6,25 @@ import mlscript._ import mlscript.utils.shorthands._ object helpers { + /** + * Split a term joined by `and` into a list of terms. + * E.g. `x and y and z` will be split into `x`, `y`, and `z`. + * + * @return a list of sub-terms of `t` + */ + def splitAnd(t: Term): Ls[Term] = + t match { + case App( + App(Var("and"), + Tup((_ -> Fld(_, lhs)) :: Nil)), + Tup((_ -> Fld(_, rhs)) :: Nil) + ) => // * Old-style operators + splitAnd(lhs) :+ rhs + case App(Var("and"), PlainTup(lhs, rhs)) => + splitAnd(lhs) :+ rhs + case _ => t :: Nil + } + /** * Generate a chain of `Let` from a list of bindings. * diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index d7e81961..62505cc9 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -1,7 +1,8 @@ package mlscript.ucs.stages import mlscript.{App, Asc, Fld, FldFlags, Lit, Sel, Term, Tup, TypeName, Var} -import mlscript.ucs.{syntax => s, core => c, PartialTerm} +import mlscript.ucs.PartialTerm +import mlscript.ucs.syntax.{core => c, source => s} import mlscript.ucs.context.{Context, ScrutineeData} import mlscript.ucs.helpers.mkBinOp import mlscript.utils._, shorthands._ diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 07d1ad23..d48c95fe 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -1,18 +1,19 @@ package mlscript.ucs.stages -import mlscript.ucs.{DesugarUCS, Lines, LinesOps, VariableGenerator} -import mlscript.ucs.context.{Context, ScrutineeData} -import mlscript.ucs.core._ -import mlscript.ucs.display.{showNormalizedTerm, showSplit} -import mlscript.ucs.helpers._ -import mlscript.pretyper.Scope -import mlscript.pretyper.symbol._ import mlscript.{App, CaseOf, Fld, FldFlags, Let, Loc, Sel, Term, Tup, Var, StrLit} import mlscript.{CaseBranches, Case, Wildcard, NoCases} import mlscript.Message, Message.MessageContext import mlscript.utils._, shorthands._ -import mlscript.pretyper.{Diagnosable, Traceable} +import mlscript.ucs, mlscript.pretyper +import ucs.{DesugarUCS, Lines, LinesOps, VariableGenerator} +import ucs.context.{Context, ScrutineeData} +import ucs.display.{showNormalizedTerm, showSplit} +import ucs.helpers._ +import ucs.syntax.core.{Pattern, Branch, Split} +import pretyper.Scope +import pretyper.symbol._ +import pretyper.{Diagnosable, Traceable} trait Normalization { self: DesugarUCS with Traceable => import Normalization._ diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index 0e061849..29b7fbe3 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -5,7 +5,7 @@ import mlscript.{If, IfBody, IfBlock, IfElse, IfLet, IfOpApp, IfOpsApp, IfThen} import mlscript.{Blk, Term, Var, App, Tup, Lit, Fld, Loc} import mlscript.Diagnostic.PreTyping import mlscript.pretyper.Traceable -import mlscript.ucs.syntax._ +import mlscript.ucs.syntax.source._ import mlscript.Message, Message._ import mlscript.utils._, shorthands._ import mlscript.NuFunDef diff --git a/shared/src/main/scala/mlscript/ucs/core.scala b/shared/src/main/scala/mlscript/ucs/syntax/core.scala similarity index 99% rename from shared/src/main/scala/mlscript/ucs/core.scala rename to shared/src/main/scala/mlscript/ucs/syntax/core.scala index 986fec08..92e3d12a 100644 --- a/shared/src/main/scala/mlscript/ucs/core.scala +++ b/shared/src/main/scala/mlscript/ucs/syntax/core.scala @@ -1,4 +1,4 @@ -package mlscript.ucs +package mlscript.ucs.syntax import mlscript.{Diagnostic, Lit, Loc, Located, Message, Term, Var} import mlscript.utils._, shorthands._ diff --git a/shared/src/main/scala/mlscript/ucs/syntax/package.scala b/shared/src/main/scala/mlscript/ucs/syntax/package.scala new file mode 100644 index 00000000..bfe2aec7 --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/syntax/package.scala @@ -0,0 +1,3 @@ +package mlscript.ucs + +package object syntax diff --git a/shared/src/main/scala/mlscript/ucs/syntax.scala b/shared/src/main/scala/mlscript/ucs/syntax/source.scala similarity index 99% rename from shared/src/main/scala/mlscript/ucs/syntax.scala rename to shared/src/main/scala/mlscript/ucs/syntax/source.scala index afbef74f..043eff9b 100644 --- a/shared/src/main/scala/mlscript/ucs/syntax.scala +++ b/shared/src/main/scala/mlscript/ucs/syntax/source.scala @@ -1,11 +1,11 @@ -package mlscript.ucs +package mlscript.ucs.syntax import mlscript.{Lit, Located, Term, Var} import mlscript.utils._, shorthands._ import scala.annotation.tailrec import scala.collection.immutable -package object syntax { +package object source { sealed abstract class Pattern extends Located { override def toString(): String = this match { case AliasPattern(nme, pattern) => s"$nme @ $pattern" From d9a666509fc230f6eb45ce86cc71e071599f1576 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Mon, 15 Jan 2024 14:56:35 +0800 Subject: [PATCH 070/143] Change UCS desugarer flag to `ucs` --- .../src/test/diff/pretyper/ucs/coverage/SealedClasses.mls | 4 ++-- shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls | 2 +- .../src/test/diff/pretyper/ucs/stages/Normalization.mls | 2 +- .../src/test/diff/pretyper/ucs/stages/PostProcessing.mls | 8 ++++---- shared/src/test/diff/ucs/Hygiene.mls | 2 +- shared/src/test/diff/ucs/HygienicBindings.mls | 8 ++++---- shared/src/test/diff/ucs/InterleavedLet.mls | 2 +- shared/src/test/diff/ucs/Wildcard.mls | 2 +- shared/src/test/scala/mlscript/DiffTests.scala | 3 +-- 9 files changed, 16 insertions(+), 17 deletions(-) diff --git a/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls b/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls index 2d74e704..c1db6d34 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls @@ -9,7 +9,7 @@ class App(func: Term, arg: Term) extends Term //│ class Abs(param: Str, body: Term) extends Term //│ class App(func: Term, arg: Term) extends Term -:dpt:desugar.result +:ucs:desugar.result fun is_value_explicit_refinement(term) = if term is refined(Term) and term is Abs(_, _) then true @@ -22,7 +22,7 @@ fun is_value_explicit_refinement(term) = //│ | | | | | | | term*‡ is App then false //│ fun is_value_explicit_refinement: (Abs | App | Var) -> Bool -:dpt:normalize.result,postprocess.result +:ucs:normalize.result,postprocess.result fun is_value_automatic_refinement(term) = if term is Term and term is Abs(_, _) then true diff --git a/shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls b/shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls index dd5dcf0e..363b7b52 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls @@ -262,7 +262,7 @@ fun bf(t) = // than placed in parallel, otherwise, the later branches will appear in the // else branch of all its previous branches. **WORK IN PROGRESS** // -// :dpt:postprocess +// :ucs:postprocess fun balance(t: Tree['A]): Tree['A] = if t is Node(x, l, r, _) and height(r) - height(l) diff --git a/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls index 6179b442..ff4ba25e 100644 --- a/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls +++ b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls @@ -22,7 +22,7 @@ fun sum(acc, xs) = // FIXME: Remove redundant `_ -> (ucs$args_xs$Some_0$Cons).1`. // Why are they everywhere? -:dpt:postprocess.result +:ucs:postprocess.result fun test(xs) = if xs is Some(Cons("add", Cons(x, Cons(y, Nil)))) then x + y diff --git a/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls b/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls index f32ad182..c4033b64 100644 --- a/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls +++ b/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls @@ -1,6 +1,6 @@ :PreTyper -:dpt:postprocess.result,desugared +:ucs:postprocess.result,desugared fun mixed_literals(v) = if v is true then "true" @@ -18,7 +18,7 @@ fun mixed_literals(v) = //│ ╙── //│ fun mixed_literals: (1 | 2 | false | true) -> ("1" | "2" | "false" | "true") -:dpt:postprocess.result +:ucs:postprocess.result fun separated_by_and(v) = if v is true then "true" @@ -32,7 +32,7 @@ fun separated_by_and(v) = //│ ╙── //│ fun separated_by_and: Bool -> ("false" | "true") -:dpt:postprocess.result +:ucs:postprocess.result fun dual_patterns(x, y) = if x is "some" and y is "none" then 0 @@ -51,7 +51,7 @@ fun dual_patterns(x, y) = //│ | | | | | | | "none" -> 3 //│ fun dual_patterns: ("none" | "some", "none" | "some") -> (0 | 1 | 2 | 3) -:dpt:postprocess.result +:ucs:postprocess.result fun unordered_dual_patterns(x, y) = if x is "some" and y is "none" then 0 diff --git a/shared/src/test/diff/ucs/Hygiene.mls b/shared/src/test/diff/ucs/Hygiene.mls index dd49ef46..bd7b4cd1 100644 --- a/shared/src/test/diff/ucs/Hygiene.mls +++ b/shared/src/test/diff/ucs/Hygiene.mls @@ -7,7 +7,7 @@ class Right[out T](value: T) //│ class Left[T](value: T) //│ class Right[T](value: T) -:dpt:postprocess.result +:ucs:postprocess.result fun foo(x) = if x is Some(Left(y)) then x Some(x) then x diff --git a/shared/src/test/diff/ucs/HygienicBindings.mls b/shared/src/test/diff/ucs/HygienicBindings.mls index eba8e945..73f43980 100644 --- a/shared/src/test/diff/ucs/HygienicBindings.mls +++ b/shared/src/test/diff/ucs/HygienicBindings.mls @@ -39,7 +39,7 @@ fun h0(a) = // If a class parameter is bound to the same variable in different branches, // the bindings can be merged and can be typed and coverage checked. See the // desugared version below. -:dpt:postprocess.result +:ucs:postprocess.result fun h0'(a) = if a is Some(x) and x is Left(y) then y @@ -92,7 +92,7 @@ fun h2(a) = a is None then 0 //│ fun h2: forall 'a. (None | Some[Left['a]]) -> (0 | 'a) -:dpt:postprocess.result +:ucs:postprocess.result fun h3(x, y, f, p) = if x is _ and f(x) is y and p(x) then y @@ -125,7 +125,7 @@ h3("anything", "anything", _ => "not me", _ => false) ~~> "anyway" //│ = undefined -:dpt:postprocess.result +:ucs:postprocess.result fun h4(x, y, p) = if x is y and p(x) then y @@ -158,7 +158,7 @@ h4("anything", "not me", _ => false) //│ res //│ = 'default' -:dpt:postprocess.result +:ucs:postprocess.result fun h5(x, y, p) = if x is Some(y) and p(x) then y diff --git a/shared/src/test/diff/ucs/InterleavedLet.mls b/shared/src/test/diff/ucs/InterleavedLet.mls index 243ac0ad..4f3d90b8 100644 --- a/shared/src/test/diff/ucs/InterleavedLet.mls +++ b/shared/src/test/diff/ucs/InterleavedLet.mls @@ -21,7 +21,7 @@ class Right[B](rightValue: B) extends Either[nothing, B] //│ class Left[A](leftValue: A) extends Either //│ class Right[B](rightValue: B) extends Either -:dpt:normalize.result +:ucs:normalize.result fun q(x) = if x is Some and x is Some and x is Some then 0 diff --git a/shared/src/test/diff/ucs/Wildcard.mls b/shared/src/test/diff/ucs/Wildcard.mls index 2b817b7c..ef826d9f 100644 --- a/shared/src/test/diff/ucs/Wildcard.mls +++ b/shared/src/test/diff/ucs/Wildcard.mls @@ -195,7 +195,7 @@ class Delta() // This should generate only one case expression instead of a chain of case // expressions. DO check the desugared term! -:dpt:postprocess.result +:ucs:postprocess.result fun w5(y) = if y is Alpha then "alpha" diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index a627a0f6..ee15d575 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -6,7 +6,6 @@ import fastparse.Parsed.Success import sourcecode.Line import scala.collection.mutable import scala.collection.mutable.{Map => MutMap} -import scala.collection.immutable import mlscript.utils._, shorthands._ import mlscript.codegen.typescript.TsTypegenCodeBuilder import org.scalatest.{funsuite, ParallelTestExecution} @@ -1136,7 +1135,7 @@ object DiffTests { } object PreTyperFlags { - private val pattern = "^dpt(?::\\s*([A-Za-z\\.-]+)(,\\s*[A-Za-z\\.-]+)*)?$".r + private val pattern = "^ucs(?::\\s*([A-Za-z\\.-]+)(,\\s*[A-Za-z\\.-]+)*)?$".r def unapply(flags: Str): Opt[Set[Str]] = flags match { case pattern(head, tail) => From e015482f276ad04fb6a57ef546487efc6b7f946c Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Tue, 16 Jan 2024 17:17:06 +0800 Subject: [PATCH 071/143] Make `PreTyper` compulsory but hide error message by default - Show error messages from `PreTyper` by `:ShowPreTyperErrors`. - Remove useless types in `pretyper` package. - Distinguish `Diagnostic`s from `ucs` and create a new `Source`. - Add built-in base classes to `Scope.global`. - Adjust functions of `Traceable`. --- .../src/main/scala/mlscript/Diagnostic.scala | 1 + .../scala/mlscript/pretyper/DataTypes.scala | 10 - .../scala/mlscript/pretyper/Diagnosable.scala | 5 +- .../scala/mlscript/pretyper/PreTyper.scala | 103 +-- .../main/scala/mlscript/pretyper/Scope.scala | 25 +- .../main/scala/mlscript/pretyper/Symbol.scala | 7 +- .../scala/mlscript/pretyper/Traceable.scala | 47 +- .../mlscript/pretyper/TypeContents.scala | 5 - .../main/scala/mlscript/ucs/DesugarUCS.scala | 101 ++- .../ucs/stages/CoverageChecking.scala | 10 +- .../mlscript/ucs/stages/Desugaring.scala | 11 +- .../mlscript/ucs/stages/Normalization.scala | 16 +- .../mlscript/ucs/stages/Transformation.scala | 23 +- .../diff/codegen/AuxiliaryConstructors.mls | 2 +- shared/src/test/diff/codegen/Mixin.mls | 5 +- shared/src/test/diff/codegen/MixinCapture.mls | 2 +- shared/src/test/diff/codegen/Nested.mls | 5 +- shared/src/test/diff/codegen/NewMatching.mls | 2 +- shared/src/test/diff/codegen/ValLet.mls | 2 +- .../test/diff/ecoop23/ComparePointPoly.mls | 2 +- .../test/diff/ecoop23/ExpressionProblem.mls | 2 +- shared/src/test/diff/ecoop23/Intro.mls | 2 +- .../test/diff/ecoop23/PolymorphicVariants.mls | 2 +- .../diff/ecoop23/SimpleRegionDSL_annot.mls | 2 +- .../test/diff/ecoop23/SimpleRegionDSL_raw.mls | 2 +- shared/src/test/diff/fcp/QML_exist_nu.mls | 2 +- shared/src/test/diff/gadt/Exp1.mls | 15 +- shared/src/test/diff/gadt/Exp2.mls | 2 +- shared/src/test/diff/gadt/ThisMatching.mls | 77 +- shared/src/test/diff/nu/Andong.mls | 2 +- shared/src/test/diff/nu/ArrayProg.mls | 2 +- shared/src/test/diff/nu/BadUCS.mls | 2 +- .../test/diff/nu/BasicClassInheritance.mls | 2 +- shared/src/test/diff/nu/BasicClasses.mls | 2 +- shared/src/test/diff/nu/CaseExpr.mls | 43 +- shared/src/test/diff/nu/ClassSignatures.mls | 2 +- shared/src/test/diff/nu/ClassesInMixins.mls | 13 +- shared/src/test/diff/nu/CommaOperator.mls | 51 +- shared/src/test/diff/nu/CtorSubtraction.mls | 2 +- shared/src/test/diff/nu/Eval.mls | 2 +- shared/src/test/diff/nu/EvalNegNeg.mls | 2 +- .../test/diff/nu/ExpressionProblem_repro.mls | 2 +- .../test/diff/nu/ExpressionProblem_small.mls | 2 +- shared/src/test/diff/nu/FilterMap.mls | 2 +- shared/src/test/diff/nu/FlatIfThenElse.mls | 11 +- shared/src/test/diff/nu/FlatMonads.mls | 33 +- shared/src/test/diff/nu/FunnyIndet.mls | 2 +- shared/src/test/diff/nu/GADTMono.mls | 2 +- shared/src/test/diff/nu/GenericClasses.mls | 2 +- shared/src/test/diff/nu/GenericModules.mls | 2 +- shared/src/test/diff/nu/HeungTung.mls | 28 +- shared/src/test/diff/nu/Huawei1.mls | 2 +- shared/src/test/diff/nu/InterfaceMono.mls | 125 ++- shared/src/test/diff/nu/Interfaces.mls | 153 ++-- shared/src/test/diff/nu/LetRec.mls | 5 +- shared/src/test/diff/nu/ListConsNil.mls | 2 +- shared/src/test/diff/nu/LitMatch.mls | 2 +- shared/src/test/diff/nu/MissingTypeArg.mls | 2 +- shared/src/test/diff/nu/NamedArgs.mls | 46 +- shared/src/test/diff/nu/New.mls | 2 +- shared/src/test/diff/nu/NewNew.mls | 31 +- shared/src/test/diff/nu/Object.mls | 20 +- shared/src/test/diff/nu/OpLam.mls | 8 +- shared/src/test/diff/nu/OptionFilter.mls | 2 +- shared/src/test/diff/nu/OverrideShorthand.mls | 2 +- shared/src/test/diff/nu/ParamPassing.mls | 40 +- .../test/diff/nu/PolymorphicVariants_Alt.mls | 2 +- .../test/diff/nu/PostHocMixinSignature.mls | 2 +- .../test/diff/nu/PrivateMemberOverriding.mls | 2 +- shared/src/test/diff/nu/RefinedPattern.mls | 5 +- shared/src/test/diff/nu/SelfRec.mls | 2 +- shared/src/test/diff/nu/Subscripts.mls | 2 +- shared/src/test/diff/nu/TODO_Classes.mls | 2 +- shared/src/test/diff/nu/Unapply.mls | 2 +- shared/src/test/diff/nu/UndefMatching.mls | 2 +- shared/src/test/diff/nu/WeirdUnions.mls | 2 +- shared/src/test/diff/nu/i180.mls | 2 +- shared/src/test/diff/nu/repro0.mls | 7 +- shared/src/test/diff/nu/repro1.mls | 2 +- shared/src/test/diff/nu/repro_EvalNegNeg.mls | 2 +- .../diff/nu/repro_PolymorphicVariants.mls | 2 +- .../src/test/diff/pretyper/Declarations.mls | 1 - shared/src/test/diff/pretyper/Errors.mls | 722 ++++++++++++++++++ shared/src/test/diff/pretyper/Repro.mls | 2 +- .../src/test/diff/pretyper/ucs/DualOption.mls | 2 +- .../test/diff/pretyper/ucs/NamePattern.mls | 2 +- .../test/diff/pretyper/ucs/RecordPattern.mls | 8 +- .../pretyper/ucs/SpecilizationCollision.mls | 12 +- shared/src/test/diff/pretyper/ucs/Unapply.mls | 1 - .../test/diff/pretyper/ucs/Unconditional.mls | 2 +- .../pretyper/ucs/coverage/MissingCases.mls | 2 +- .../diff/pretyper/ucs/coverage/Refinement.mls | 2 +- .../pretyper/ucs/coverage/SealedClasses.mls | 2 +- .../diff/pretyper/ucs/coverage/Tautology.mls | 2 +- .../pretyper/ucs/coverage/Unreachable.mls | 2 +- .../diff/pretyper/ucs/examples/AVLTree.mls | 2 +- .../ucs/examples/BinarySearchTree.mls | 2 +- .../diff/pretyper/ucs/examples/Calculator.mls | 2 +- .../pretyper/ucs/examples/EitherOrBoth.mls | 2 +- .../test/diff/pretyper/ucs/examples/JSON.mls | 2 +- .../pretyper/ucs/examples/LeftistTree.mls | 2 +- .../pretyper/ucs/examples/LispInterpreter.mls | 2 +- .../test/diff/pretyper/ucs/examples/List.mls | 2 +- .../diff/pretyper/ucs/examples/ListFold.mls | 2 +- .../diff/pretyper/ucs/examples/Option.mls | 2 +- .../pretyper/ucs/examples/Permutations.mls | 2 +- .../test/diff/pretyper/ucs/examples/STLC.mls | 2 +- .../diff/pretyper/ucs/examples/SimpleLisp.mls | 2 +- .../diff/pretyper/ucs/examples/SimpleList.mls | 2 +- .../diff/pretyper/ucs/examples/SimpleTree.mls | 2 +- .../test/diff/pretyper/ucs/examples/ULC.mls | 2 +- .../diff/pretyper/ucs/patterns/Literals.mls | 2 +- .../pretyper/ucs/patterns/SimpleTuple.mls | 2 +- .../pretyper/ucs/stages/Normalization.mls | 2 +- .../pretyper/ucs/stages/PostProcessing.mls | 2 +- .../diff/pretyper/ucs/stages/TransfromUCS.mls | 2 +- shared/src/test/diff/ucs/AppSplits.mls | 2 +- .../src/test/diff/ucs/CrossBranchCapture.mls | 14 +- shared/src/test/diff/ucs/DirectLines.mls | 2 +- shared/src/test/diff/ucs/ElseIf.mls | 2 +- shared/src/test/diff/ucs/ErrorMessage.mls | 5 +- shared/src/test/diff/ucs/Exhaustiveness.mls | 2 +- shared/src/test/diff/ucs/Humiliation.mls | 2 +- shared/src/test/diff/ucs/Hygiene.mls | 2 +- shared/src/test/diff/ucs/HygienicBindings.mls | 2 +- shared/src/test/diff/ucs/InterleavedLet.mls | 2 +- shared/src/test/diff/ucs/LeadingAnd.mls | 2 +- shared/src/test/diff/ucs/LitUCS.mls | 2 +- shared/src/test/diff/ucs/MultiwayIf.mls | 2 +- shared/src/test/diff/ucs/NestedBranches.mls | 2 +- shared/src/test/diff/ucs/NestedOpSplits.mls | 2 +- shared/src/test/diff/ucs/NestedPattern.mls | 2 +- .../src/test/diff/ucs/NuPlainConditionals.mls | 14 +- shared/src/test/diff/ucs/Or.mls | 2 +- .../src/test/diff/ucs/OverlappedBranches.mls | 2 +- shared/src/test/diff/ucs/ParseFailures.mls | 2 +- .../src/test/diff/ucs/PlainConditionals.mls | 14 +- shared/src/test/diff/ucs/SimpleUCS.mls | 2 +- shared/src/test/diff/ucs/SplitAfterOp.mls | 2 +- shared/src/test/diff/ucs/SplitAnd.mls | 2 +- shared/src/test/diff/ucs/SplitAroundOp.mls | 5 +- shared/src/test/diff/ucs/SplitBeforeOp.mls | 56 +- shared/src/test/diff/ucs/SplitOps.mls | 15 +- shared/src/test/diff/ucs/SplitScrutinee.mls | 2 +- shared/src/test/diff/ucs/ThenIndent.mls | 2 +- shared/src/test/diff/ucs/Tree.mls | 2 +- shared/src/test/diff/ucs/TrivialIf.mls | 2 +- shared/src/test/diff/ucs/WeirdIf.mls | 2 +- shared/src/test/diff/ucs/WeirdSplit.mls | 2 +- shared/src/test/diff/ucs/Wildcard.mls | 2 +- shared/src/test/diff/ucs/zipWith.mls | 2 +- .../src/test/scala/mlscript/DiffTests.scala | 29 +- 152 files changed, 1375 insertions(+), 818 deletions(-) delete mode 100644 shared/src/main/scala/mlscript/pretyper/DataTypes.scala delete mode 100644 shared/src/main/scala/mlscript/pretyper/TypeContents.scala create mode 100644 shared/src/test/diff/pretyper/Errors.mls diff --git a/shared/src/main/scala/mlscript/Diagnostic.scala b/shared/src/main/scala/mlscript/Diagnostic.scala index 00d59a3b..e1eaefd8 100644 --- a/shared/src/main/scala/mlscript/Diagnostic.scala +++ b/shared/src/main/scala/mlscript/Diagnostic.scala @@ -20,6 +20,7 @@ object Diagnostic { case object Lexing extends Source case object Parsing extends Source case object PreTyping extends Source + case object Desugaring extends Source case object Typing extends Source case object Compilation extends Source case object Runtime extends Source diff --git a/shared/src/main/scala/mlscript/pretyper/DataTypes.scala b/shared/src/main/scala/mlscript/pretyper/DataTypes.scala deleted file mode 100644 index aaebd901..00000000 --- a/shared/src/main/scala/mlscript/pretyper/DataTypes.scala +++ /dev/null @@ -1,10 +0,0 @@ -package mlscript.pretyper - -import mlscript.{NuFunDef, NuTypeDef, Cls, Trt, Mxn, Als, Mod, Var, TypeName} -import collection.mutable.{Buffer, Map => MutMap, Set => MutSet} -import mlscript.utils._, shorthands._ -import scala.annotation.tailrec - -trait DataTypes { self: PreTyper => - -} \ No newline at end of file diff --git a/shared/src/main/scala/mlscript/pretyper/Diagnosable.scala b/shared/src/main/scala/mlscript/pretyper/Diagnosable.scala index 332da9f9..36cc6406 100644 --- a/shared/src/main/scala/mlscript/pretyper/Diagnosable.scala +++ b/shared/src/main/scala/mlscript/pretyper/Diagnosable.scala @@ -22,5 +22,8 @@ trait Diagnosable { protected def raiseWarning(source: Source, messages: (Message -> Opt[Loc])*): Unit = raise(WarningReport(messages.toList, newDefs = true, source)) - def getDiagnostics: Ls[Diagnostic] = diagnosticBuffer.toList + @inline final def filterDiagnostics(f: Diagnostic => Bool): Ls[Diagnostic] = + diagnosticBuffer.iterator.filter(f).toList + + @inline final def getDiagnostics: Ls[Diagnostic] = diagnosticBuffer.toList } diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index a247e7fa..a835a190 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -1,20 +1,17 @@ package mlscript.pretyper -import collection.mutable.{Set => MutSet} -import collection.immutable.SortedMap -import symbol._ -import mlscript._, utils._, shorthands._, Diagnostic.PreTyping, Message.MessageContext, ucs.DesugarUCS -import scala.annotation.tailrec +import annotation.tailrec, collection.mutable.{Set => MutSet}, collection.immutable.SortedMap, util.chaining._ +import mlscript._, utils._, shorthands._, Diagnostic.PreTyping, Message.MessageContext, symbol._, ucs.DesugarUCS -class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with Diagnosable with DesugarUCS { +class PreTyper extends Traceable with Diagnosable with DesugarUCS { import PreTyper._ /** A shorthand function to raise errors without specifying the source. */ - protected override def raiseError(messages: (Message -> Opt[Loc])*): Unit = + protected def raiseError(messages: (Message -> Opt[Loc])*): Unit = raiseError(PreTyping, messages: _*) /** A shorthand function to raise warnings without specifying the source. */ - protected override def raiseWarning(messages: (Message -> Opt[Loc])*): Unit = + protected def raiseWarning(messages: (Message -> Opt[Loc])*): Unit = raiseWarning(PreTyping, messages: _*) private def extractParameters(fields: Term): Ls[LocalTermSymbol] = { @@ -38,9 +35,9 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D raiseError(msg"unsupported pattern shape" -> other.toLoc) Iterator.empty } - val rs = rec(fields).toList - println(s"extractParameters ${fields.showDbg} ==> ${rs.iterator.map(_.name).mkString(", ")}") - rs + rec(fields).tap { rs => + println(s"extractParameters ${fields.showDbg} ==> ${rs.map(_.name).mkString(", ")}") + }.toList } protected def resolveVar(v: Var)(implicit scope: Scope): Unit = @@ -60,9 +57,9 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D println(s"resolveVar `${v.name}` ==> class") v.symbol = sym case S(_) => - raiseError(PreTyping, msg"identifier `${v.name}` is resolved to a type" -> v.toLoc) + raiseError(msg"identifier `${v.name}` is resolved to a type" -> v.toLoc) case N => - raiseError(PreTyping, msg"identifier `${v.name}` not found" -> v.toLoc) + raiseError(msg"identifier `${v.name}` not found" -> v.toLoc) } } @@ -71,7 +68,7 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D v.symbolOption match { case N => resolveVar(v) case S(symbol) => scope.getSymbols(v.name) match { - case Nil => raiseError(PreTyping, msg"identifier `${v.name}` not found" -> v.toLoc) + case Nil => raiseError(msg"identifier `${v.name}` not found" -> v.toLoc) case symbols if symbols.contains(symbol) => () case symbols => def toNameLoc(symbol: Symbol): (Str, Opt[Loc]) = symbol match { @@ -81,7 +78,7 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D case dt: DefinedTermSymbol => s"`${dt.name}`" -> dt.defn.toLoc } val (name, loc) = toNameLoc(symbol) - raiseError(PreTyping, + raiseError( (msg"identifier ${v.name} refers to different symbols." -> v.toLoc :: msg"it is resolved to $name" -> loc :: symbols.map { s => @@ -106,16 +103,26 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D traverseTerm(body)(scope + new LocalTermSymbol(nme)) case New(head, body) => // `new C(...)` or `new C(){...}` or `new{...}` - case Tup(fields) => fields.foreach { case (_, Fld(_, t)) => traverseTerm(t) } + case Tup(fields) => fields.foreach { + case (S(t), Fld(_, _)) => traverseTerm(t) + case (N, Fld(_, t)) => traverseTerm(t) + } case Asc(trm, ty) => traverseTerm(trm) case ef @ If(_, _) => traverseIf(ef)(scope) - case TyApp(lhs, targs) => // TODO: When? - case Eqn(lhs, rhs) => ??? // TODO: How? + case TyApp(lhs, targs) => + // Be consistent with the error message from `Typer`. + raiseError(msg"type application syntax is not yet supported" -> term.toLoc) + case Eqn(lhs, rhs) => + raiseWarning(msg"unsupported `Eqn`: ${term.showDbg}" -> term.toLoc) case Blk(stmts) => traverseStatements(stmts, "block", scope) () - case Subs(arr, idx) => traverseTerm(arr); traverseTerm(idx) - case Bind(lhs, rhs) => traverseTerm(lhs); traverseTerm(rhs) + case Subs(arr, idx) => + traverseTerm(arr) + traverseTerm(idx) + case Bind(lhs, rhs) => + traverseTerm(lhs) + traverseTerm(rhs) case Splc(fields) => fields.foreach { case L(t) => traverseTerm(t) case R(Fld(_, t)) => traverseTerm(t) @@ -124,8 +131,9 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D case Rcd(fields) => fields.foreach { case (_, Fld(_, t)) => traverseTerm(t) } case CaseOf(trm, cases) => case With(trm, fields) => traverseTerm(trm); traverseTerm(fields) - case Where(body, where) => ??? // TODO: When? - // begin UCS shorthands + case Where(body, where) => + raiseWarning(msg"unsupported `Where`: ${term.showDbg}" -> term.toLoc) + // begin UCS shorthands ================================================ // * Old-style operators case App(App(Var("is"), _), _) => println(s"found UCS shorthand: ${term.showDbg}") @@ -148,12 +156,16 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D val desugared = If(IfThen(lhs, rhs), S(Var("false"))) traverseIf(desugared) term.desugaredTerm = desugared.desugaredTerm - // end UCS shorthands + // end UCS shorthands ================================================== + case App(lhs, Tup(fields)) => + traverseTerm(lhs) + fields.foreach { _._2.value |> traverseTerm } case App(lhs, rhs) => traverseTerm(lhs); traverseTerm(rhs) case Test(trm, ty) => traverseTerm(trm) case _: Lit | _: Super => () case v: Var => traverseVar(v) - case AdtMatchWith(cond, arms) => ??? // TODO: How? + case AdtMatchWith(cond, arms) => + raiseWarning(msg"unsupported `AdtMatchWith`: ${term.showDbg}" -> term.toLoc) case Inst(body) => traverseTerm(body) case NuNew(cls) => traverseTerm(cls) case Rft(base, decls) => // For object refinement @@ -169,7 +181,7 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D () }(_ => s"traverseTypeDefinition <== ${defn.describe}") - private def traverseStatements(statements: Ls[Statement], name: Str, parentScope: Scope): (Scope, TypeContents) = + private def traverseStatements(statements: Ls[Statement], name: Str, parentScope: Scope): Scope = trace(s"traverseStatements <== $name: ${"statement".pluralize(statements.size, true)}") { // Pass 1: Build a scope with type symbols only. val filterNuTypeDef = { (_: Statement) match { case t: NuTypeDef => S(t); case _ => N } } @@ -190,7 +202,7 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D acc + (self -> extractSuperTypes(self.defn.parents).flatMap { nme => val maybeSymbol = scopeWithTypes.getTypeSymbol(nme.name) if (maybeSymbol.isEmpty) { - raiseError(PreTyping, msg"could not find definition `${nme.name}`" -> nme.toLoc) + raiseError(msg"could not find definition `${nme.name}`" -> nme.toLoc) } maybeSymbol }) @@ -208,11 +220,7 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D val derivedTypes = try extractSignatureTypes(unions) catch { case _: NotImplementedError => Nil } symbol.sealedDerivedTypes = derivedTypes.flatMap { derivedType => val maybeSymbol = scopeWithTypes.getTypeSymbol(derivedType.name) - if (maybeSymbol.isEmpty) raise(ErrorReport( - msg"Undefined type $derivedType" -> derivedType.toLoc :: Nil, - true, - Diagnostic.PreTyping - )) + if (maybeSymbol.isEmpty) raiseError(msg"Undefined type $derivedType" -> derivedType.toLoc) maybeSymbol } println(s">>> $name: ${symbol.sealedDerivedTypes.iterator.map(_.name).mkString(", ")}") @@ -257,13 +265,13 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D case R(_) => () } } - (completeScope, new TypeContents) - }({ case (scope, contents) => s"traverseStatements ==> Scope {${scope.showLocalSymbols}}" }) + completeScope + }({ scope => s"traverseStatements ==> Scope {${scope.showLocalSymbols}}" }) - def process(typingUnit: TypingUnit, scope: Scope, name: Str): (Scope, TypeContents) = - trace(s"process <== $name: ${typingUnit.describe}") { + def apply(typingUnit: TypingUnit, scope: Scope, name: Str): Scope = + trace(s"PreTyper <== $name: ${typingUnit.describe}") { traverseStatements(typingUnit.entities, name, scope) - }({ case (scope, contents) => s"process ==> ${scope.showLocalSymbols}" }) + }({ scope => s"PreTyper ==> ${scope.showLocalSymbols}" }) /** * Extract types in class signatures. For example, for this piece of code @@ -289,23 +297,24 @@ class PreTyper(override val debugTopics: Opt[Set[Str]]) extends Traceable with D } rec(Nil, ty).reverse } -} - -object PreTyper { def extractSuperTypes(parents: Ls[Term]): Ls[Var] = { @tailrec - def rec(results: Ls[Var], waitlist: Ls[Term]): Ls[Var] = - waitlist match { - case Nil => results.reverse - case (nme: Var) :: tail => rec(nme :: results, tail) - case (TyApp(ty, _)) :: tail => rec(results, ty :: tail) - case (App(term, Tup(_))) :: tail => rec(results, term :: tail) - case other :: _ => println(s"Unknown parent type: $other"); ??? + def rec(acc: Ls[Var], rest: Ls[Term]): Ls[Var] = + rest match { + case Nil => acc.reverse + case (nme: Var) :: tail => rec(nme :: acc, tail) + case (TyApp(ty, _)) :: tail => rec(acc, ty :: tail) + case (App(term, Tup(_))) :: tail => rec(acc, term :: tail) + case head :: tail => + raiseWarning(msg"unknown type in parent types: ${head.showDbg}" -> head.toLoc) + rec(acc, tail) } rec(Nil, parents) } +} +object PreTyper { def transitiveClosure[A](graph: Map[A, List[A]])(implicit ord: Ordering[A]): SortedMap[A, List[A]] = { def dfs(vertex: A, visited: Set[A]): Set[A] = { if (visited.contains(vertex)) visited @@ -330,4 +339,4 @@ object PreTyper { }) } } -} \ No newline at end of file +} diff --git a/shared/src/main/scala/mlscript/pretyper/Scope.scala b/shared/src/main/scala/mlscript/pretyper/Scope.scala index 89b30bd6..794fc414 100644 --- a/shared/src/main/scala/mlscript/pretyper/Scope.scala +++ b/shared/src/main/scala/mlscript/pretyper/Scope.scala @@ -92,24 +92,31 @@ object Scope { } val global: Scope = { - val trueDefn = NuTypeDef(Mod, TypeName("true"), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, N) - val falseDefn = NuTypeDef(Mod, TypeName("false"), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, N) - val nothingDefn = NuTypeDef(Als, TypeName("nothing"), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, N) - val trueSymbol = new ModuleSymbol(trueDefn) - val falseSymbol = new ModuleSymbol(falseDefn) - val nothingSymbol = new TypeAliasSymbol(nothingDefn) + def mod(name: Str) = NuTypeDef(Mod, TypeName(name), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, N) + // def cls(name: Str) = NuTypeDef(Trt, TypeName(name), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, N) + def als(name: Str) = NuTypeDef(Als, TypeName(name), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, N) + val builtinTypes = Ls( + new ModuleSymbol(mod("true")), + new ModuleSymbol(mod("false")), + new TypeAliasSymbol(als("nothing")), + new DummyClassSymbol(Var("Int")), + new DummyClassSymbol(Var("Num")), + new DummyClassSymbol(Var("Str")), + new DummyClassSymbol(Var("Bool")), + ) val commaSymbol = new LocalTermSymbol(Var(",")) Scope.from( """true,false,document,window,typeof,toString,not,succ,log,discard,negate, - |round,add,sub,mul,div,sqrt,lt,le,gt,ge,slt,sle,sgt,sge,length,concat,eq, - |ne,error,id,if,emptyArray,+,-,*,%,/,**,<,>,<=,>=,==,===,<>,&&,||,and, + |round,add,sub,mul,div,sqrt,lt,le,gt,ge,slt,sle,sgt,sge,length,concat, + |join,eq,ne,error,id,if,emptyArray, + |+,-,*,%,/,**,<,>,<=,>=,==,===,<>,&&,||,and, |numAdd,numSub,numMul,NaN""" .stripMargin .split(",") .iterator .map(_.trim) .map(name => new LocalTermSymbol(Var(name))) - .concat(trueSymbol :: falseSymbol :: nothingSymbol :: commaSymbol :: Nil) + .concat(commaSymbol :: builtinTypes) ) } } diff --git a/shared/src/main/scala/mlscript/pretyper/Symbol.scala b/shared/src/main/scala/mlscript/pretyper/Symbol.scala index 41bad997..d08cc7b6 100644 --- a/shared/src/main/scala/mlscript/pretyper/Symbol.scala +++ b/shared/src/main/scala/mlscript/pretyper/Symbol.scala @@ -25,11 +25,6 @@ package object symbol { override def name: Str = defn.name - def scope: Scope = ??? - def contents: Map[Str, Symbol] = ??? - - def complete(scope: Scope, contents: Map[Str, Symbol]): Unit = ??? - var baseTypes: Ls[TypeSymbol] = Nil var sealedDerivedTypes: Ls[TypeSymbol] = Nil @@ -60,6 +55,8 @@ package object symbol { override def defn: NuTypeDef = die override def name: Str = nme.name + + override def showDbg: Str = s"dummy class $name" } final class ClassSymbol(override val defn: NuTypeDef) extends TypeSymbol { diff --git a/shared/src/main/scala/mlscript/pretyper/Traceable.scala b/shared/src/main/scala/mlscript/pretyper/Traceable.scala index c2ffd59c..7d318d3c 100644 --- a/shared/src/main/scala/mlscript/pretyper/Traceable.scala +++ b/shared/src/main/scala/mlscript/pretyper/Traceable.scala @@ -4,41 +4,52 @@ import mlscript.Diagnostic import mlscript.utils._, shorthands._ trait Traceable { - /** The set of topics to debug. Empty set indicates all topics. */ - protected val debugTopics: Opt[Set[Str]] = N - protected var indent = 0 - private var currentTopic: Opt[Str] = N + /** + * The set of topics to debug. Explanation of possible values. + * - `N`: Show nothing. + * - `S(Set.empty)`: Show everything. + * - `S(someTopics)`: Show only the topics in `someTopics`. + * Override this function to enable debugging. + */ + protected def debugTopicFilters: Opt[Set[Str]] = N + private var debugIndent = 0 + private var currentDebugTopics: Opt[Str] = N - def emitString(str: String): Unit = scala.Predef.println(str) + private def matchTopicFilters: Boolean = + debugTopicFilters match { + case S(topics) if topics.isEmpty => true + case S(topics) => currentDebugTopics.fold(false)(topics) + case N => false + } + + /** Override this function to redirect debug messages. */ + protected def emitString(str: Str): Unit = scala.Predef.println(str) @inline private def printLineByLine(x: => Any): Unit = - x.toString.linesIterator.foreach { line => emitString("| " * indent + line) } + x.toString.linesIterator.foreach { line => emitString("| " * debugIndent + line) } - def trace[T](pre: => String)(thunk: => T)(post: T => String = Traceable.noPostTrace): T = { + protected def trace[T](pre: => Str)(thunk: => T)(post: T => Str = Traceable.noPostTrace): T = { println(pre) - indent += 1 - val res = try thunk finally indent -= 1 + debugIndent += 1 + val res = try thunk finally debugIndent -= 1 if (post isnt Traceable.noPostTrace) println(post(res)) res } - def traceWithTopic[T](currentTopic: Str)(thunk: => T): T = { - this.currentTopic = S(currentTopic) + protected def traceWithTopic[T](currentDebugTopics: Str)(thunk: => T): T = { + this.currentDebugTopics = S(currentDebugTopics) val res = thunk - this.currentTopic = N + this.currentDebugTopics = N res } - @inline def traceNot[T](pre: => String)(thunk: => T)(post: T => String = Traceable.noPostTrace): T = + @inline def traceNot[T](pre: => Str)(thunk: => T)(post: T => Str = Traceable.noPostTrace): T = thunk @inline protected def println(x: => Any): Unit = - debugTopics match { - case S(someTopics) if someTopics.isEmpty || currentTopic.fold(false)(someTopics) => printLineByLine(x) - case N | S(_) => () - } + if (matchTopicFilters) printLineByLine(x) } object Traceable { - val noPostTrace: Any => String = _ => "" + val noPostTrace: Any => Str = _ => "" } diff --git a/shared/src/main/scala/mlscript/pretyper/TypeContents.scala b/shared/src/main/scala/mlscript/pretyper/TypeContents.scala deleted file mode 100644 index fc9e0a9c..00000000 --- a/shared/src/main/scala/mlscript/pretyper/TypeContents.scala +++ /dev/null @@ -1,5 +0,0 @@ -package mlscript.pretyper - -class TypeContents { - // Stub -} diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala index a9b861a5..35ff3779 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala @@ -5,24 +5,27 @@ import syntax.{source => s, core => c}, stages._, context.{Context, ScrutineeDat import mlscript.ucs.display.{showNormalizedTerm, showSplit} import mlscript.pretyper.{PreTyper, Scope} import mlscript.pretyper.symbol._ -import mlscript.{If, Loc, Message, Var}, Message.MessageContext, mlscript.Diagnostic.PreTyping +import mlscript.{If, Loc, Message, Var}, Message.MessageContext, mlscript.Diagnostic import mlscript.utils._, shorthands._ import syntax.core.{Branch, Split} -// TODO: Rename to `Desugarer` once the old desugarer is removed. +/** + * The main class of the UCS desugaring. + * TODO: Rename to `Desugarer` once the old desugarer is removed. + */ trait DesugarUCS extends Transformation with Desugaring with Normalization with PostProcessing with CoverageChecking { self: PreTyper => - /** A shorthand function to raise errors without specifying the source. */ - protected def raiseError(messages: (Message -> Opt[Loc])*): Unit = - raiseError(PreTyping, messages: _*) + /** A shorthand function to raise _desugaring_ errors without specifying the source. */ + protected def raiseDesugaringError(messages: (Message -> Opt[Loc])*): Unit = + raiseError(Diagnostic.Desugaring, messages: _*) - /** A shorthand function to raise warnings without specifying the source. */ - protected def raiseWarning(messages: (Message -> Opt[Loc])*): Unit = - raiseWarning(PreTyping, messages: _*) + /** A shorthand function to raise _desugaring_ warnings without specifying the source. */ + protected def raiseDesugaringWarning(messages: (Message -> Opt[Loc])*): Unit = + raiseWarning(Diagnostic.Desugaring, messages: _*) /** Create a fresh local symbol for the given `Var`. */ protected def freshSymbol(nme: Var): LocalTermSymbol = new LocalTermSymbol(nme) @@ -40,10 +43,10 @@ trait DesugarUCS extends Transformation private def requireClassLikeSymbol(symbol: TypeSymbol)(implicit context: Context): TypeSymbol = symbol match { case symbol @ (_: TraitSymbol | _: ClassSymbol | _: ModuleSymbol | _: DummyClassSymbol) => symbol case symbol: MixinSymbol => - raiseError(msg"Mixins are not allowed in pattern" -> nme.toLoc) + raiseDesugaringError(msg"Mixins are not allowed in pattern" -> nme.toLoc) context.getOrCreateDummyClassSymbol(nme) case symbol: TypeAliasSymbol => - raiseError(msg"Type alias is not allowed in pattern" -> nme.toLoc) + raiseDesugaringError(msg"Type alias is not allowed in pattern" -> nme.toLoc) context.getOrCreateDummyClassSymbol(nme) } @@ -56,10 +59,10 @@ trait DesugarUCS extends Transformation val symbol = nme.symbolOption match { case S(symbol: TypeSymbol) => requireClassLikeSymbol(symbol) case S(symbol: TermSymbol) => - raiseError(msg"variable ${nme.name} is not associated with a class symbol" -> nme.toLoc) + raiseDesugaringError(msg"variable ${nme.name} is not associated with a class symbol" -> nme.toLoc) context.getOrCreateDummyClassSymbol(nme) case N => - raiseError(msg"variable ${nme.name} is not associated with any symbols" -> nme.toLoc) + raiseDesugaringError(msg"variable ${nme.name} is not associated with any symbols" -> nme.toLoc) context.getOrCreateDummyClassSymbol(nme) } println(s"getClassLikeSymbol: ${nme.name} ==> ${symbol.showDbg}") @@ -100,7 +103,7 @@ trait DesugarUCS extends Transformation case N => resolveTermSymbol case S(symbol: TermSymbol) => symbol case S(otherSymbol) => - raiseError(msg"identifier `${nme.name}` should be a term" -> nme.toLoc) + raiseDesugaringError(msg"identifier `${nme.name}` should be a term" -> nme.toLoc) freshSymbol(nme) // TODO: Maybe we should maintain a "lost" symbol map. } } @@ -108,7 +111,7 @@ trait DesugarUCS extends Transformation /** Associate the `Var` with a term symbol and returns the term symbol. */ def resolveTermSymbol(implicit scope: Scope): TermSymbol = { val symbol = scope.getTermSymbol(nme.name).getOrElse { - raiseError(msg"identifier `${nme.name}` not found" -> nme.toLoc) + raiseDesugaringError(msg"identifier `${nme.name}` not found" -> nme.toLoc) freshSymbol(nme) // TODO: Maybe we should maintain a "lost" symbol map. } nme.symbol = symbol @@ -123,7 +126,7 @@ trait DesugarUCS extends Transformation val symbol = scope.getTypeSymbol(nme.name) match { case S(symbol) => requireClassLikeSymbol(symbol) case N => - raiseError(msg"type identifier `${nme.name}` not found" -> nme.toLoc) + raiseDesugaringError(msg"type identifier `${nme.name}` not found" -> nme.toLoc) context.getOrCreateDummyClassSymbol(nme) } nme.symbol = symbol @@ -138,13 +141,23 @@ trait DesugarUCS extends Transformation { nme.resolveClassLikeSymbol; nme } } + /** + * This class defines common operations on _splits_ in source abstract syntax + * (i.e., `ucs.syntax.source.Split`). + */ protected implicit class SourceSplitOps[+B <: s.Branch](these: s.Split[B]) { + /** + * Concatenate two splits and raise a warning if the latter is discarded. + * + * @param those the right-hand side `ucs.syntax.source.Split` + * @return a new split which is the concatenation of LHS and RHS + */ def ++[BB >: B <: s.Branch](those: s.Split[BB]): s.Split[BB] = if (those === s.Split.Nil) these else (these match { case s.Split.Cons(head, tail) => s.Split.Cons(head, tail ++ those) case s.Split.Let(rec, nme, rhs, tail) => s.Split.Let(rec, nme, rhs, tail ++ those) case s.Split.Else(_) => - raiseWarning( + raiseDesugaringWarning( msg"unreachable case" -> those.toLoc, msg"because this branch covers the case" -> these.toLoc ) @@ -153,17 +166,23 @@ trait DesugarUCS extends Transformation }) } + /** + * This class defines common operations on _splits_ in _core_ abstract syntax + * (i.e., `ucs.syntax.core.Split`). + */ protected implicit class CoreSplitOps(these: c.Split) { /** - * Concatenates two splits. Beware that `that` may be discarded if `this` - * has an else branch. Make sure to make diagnostics for discarded `that`. + * Concatenate two splits and raise a warning if the latter is discarded. + * + * @param those the right-hand side `ucs.syntax.core.Split` + * @return a new split which is the concatenation of LHS and RHS */ def ++(those: c.Split): c.Split = if (those === c.Split.Nil) these else (these match { case me: c.Split.Cons => me.copy(tail = me.tail ++ those) case me: c.Split.Let => me.copy(tail = me.tail ++ those) case _: c.Split.Else => - raiseWarning( + raiseDesugaringWarning( msg"the case is unreachable" -> those.toLoc, msg"because this branch covers the case" -> these.toLoc ) @@ -172,6 +191,15 @@ trait DesugarUCS extends Transformation }) } + /** + * The entry-point of desugaring a UCS syntax tree (`If` node) to a normal + * MLscript syntax tree made of `CaseOf` and `Let` nodes. `PreTyper` is + * supposed to call this function. Note that the caller doesn't need to + * resolve symbols and bindings inside the UCS tree. + * + * @param if the UCS syntax tree to be desugared + * @param scope the scope of the `If` node + */ protected def traverseIf(`if`: If)(implicit scope: Scope): Unit = { implicit val context: Context = new Context(`if`) try trace("traverseIf") { @@ -227,25 +255,26 @@ trait DesugarUCS extends Transformation // Epilogue `if`.desugaredTerm = S(postProcessed) }(_ => "traverseIf ==> ()") catch { - case e: DesugaringException => raiseError(e.messages: _*) + case e: DesugaringException => raiseDesugaringError(e.messages: _*) } } + /** + * Traverse a desugared _core abstract syntax_ tree. The function takes care + * of let bindings and resolves variables. + */ private def traverseSplit(split: syntax.core.Split)(implicit scope: Scope): Unit = - trace(s"traverseSplit <== [${scope.showLocalSymbols}]") { - - split match { - case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => - traverseTerm(scrutinee) - val patternSymbols = pattern.declaredVars.map(nme => nme -> nme.symbol) - traverseSplit(continuation)(scope.withEntries(patternSymbols)) - traverseSplit(tail) - case Split.Let(isRec, name, rhs, tail) => - val recScope = scope + name.symbol - traverseTerm(rhs)(if (isRec) recScope else scope) - traverseSplit(tail)(recScope) - case Split.Else(default) => traverseTerm(default) - case Split.Nil => () - } - }() + split match { + case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => + traverseTerm(scrutinee) + val patternSymbols = pattern.declaredVars.map(nme => nme -> nme.symbol) + traverseSplit(continuation)(scope.withEntries(patternSymbols)) + traverseSplit(tail) + case Split.Let(isRec, name, rhs, tail) => + val recScope = scope + name.symbol + traverseTerm(rhs)(if (isRec) recScope else scope) + traverseSplit(tail)(recScope) + case Split.Else(default) => traverseTerm(default) + case Split.Nil => () + } } diff --git a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala index b3dfc2a0..5a7a368a 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala @@ -31,7 +31,7 @@ trait CoverageChecking { self: DesugarUCS with Traceable => term match { case Let(_, _, _, body) => checkCoverage(body, pending, working, seen) case CaseOf(scrutineeVar: Var, Case(Var("true"), body, NoCases)) if context.isTestVar(scrutineeVar) => - raiseError(msg"missing else branch" -> body.toLoc) + raiseDesugaringError(msg"missing else branch" -> body.toLoc) Nil case CaseOf(ScrutineeData.WithVar(scrutinee, scrutineeVar), cases) => println(s"scrutinee: ${scrutineeVar.name}") @@ -87,9 +87,9 @@ trait CoverageChecking { self: DesugarUCS with Traceable => ) case N => unseenPatterns -> (diagnostics :+ (seen.get(namedScrutinee) match { - case S((`classSymbol`, _, _)) => WarningReport("tautology", Nil, Diagnostic.PreTyping) - case S(_) => ErrorReport("contradiction", Nil, Diagnostic.PreTyping) - case N => ErrorReport("unvisited scrutinee", Nil, Diagnostic.PreTyping) + case S((`classSymbol`, _, _)) => WarningReport("tautology", Nil, Diagnostic.Desugaring) + case S(_) => ErrorReport("contradiction", Nil, Diagnostic.Desugaring) + case N => ErrorReport("unvisited scrutinee", Nil, Diagnostic.Desugaring) })) } case ((unseenPatterns, diagnostics), (literal: Lit) -> body) => @@ -131,7 +131,7 @@ object CoverageChecking { msg"${prologue}scrutinee `${scrutinee._1.name}` is `${classSymbol.name}`$epilogue" -> locations.headOption }.toList ::: lines } - }, true, Diagnostic.PreTyping)) + }, true, Diagnostic.Desugaring)) } /** A helper function that prints entries from the given registry line by line. */ diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 62505cc9..01b649db 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -8,7 +8,6 @@ import mlscript.ucs.helpers.mkBinOp import mlscript.utils._, shorthands._ import mlscript.pretyper.symbol._ import mlscript.pretyper.{PreTyper, Scope} -import mlscript.Diagnostic.PreTyping import mlscript.Message, Message.MessageContext /** @@ -148,7 +147,7 @@ trait Desugaring { self: PreTyper => symbol.addScrutinee(classPattern.getParameter(index).withAlias(subScrutineeVar)) S(subScrutineeVar.withSymbol(symbol) -> S(parameterPattern)) case (pattern, _) => - raiseError(PreTyping, msg"unsupported pattern" -> pattern.toLoc) + raiseDesugaringError(msg"unsupported pattern" -> pattern.toLoc) N }.toList }(r => s"flattenClassParameters ==> ${r.mkString(", ")}") @@ -255,7 +254,7 @@ trait Desugaring { self: PreTyper => (scopeWithNestedAll, bindNestedAll.andThen(bindPrevious)) // Well, other patterns are not supported yet. case (acc, S(nme -> S(pattern))) => - raiseError(PreTyping, msg"unsupported pattern is" -> pattern.toLoc) + raiseDesugaringError(msg"unsupported pattern is" -> pattern.toLoc) acc // If this parameter is empty (e.g. produced by wildcard), then we do // nothing and pass on scope and binder. @@ -277,7 +276,7 @@ trait Desugaring { self: PreTyper => symbol.addScrutinee(tuplePattern.getField(index).withAlias(subScrutineeVar)) S(subScrutineeVar.withSymbol(symbol) -> S(parameterPattern)) case (pattern, _) => - raiseError(PreTyping, msg"unsupported pattern" -> pattern.toLoc) + raiseDesugaringError(msg"unsupported pattern" -> pattern.toLoc) N }.toList } @@ -303,7 +302,7 @@ trait Desugaring { self: PreTyper => case s.Split.Cons(head, tail) => head.pattern match { case pattern @ s.AliasPattern(_, _) => - raiseError(PreTyping, msg"alias pattern is not supported for now" -> pattern.toLoc) + raiseDesugaringError(msg"alias pattern is not supported for now" -> pattern.toLoc) rec(scrutineeVar, tail) case s.LiteralPattern(literal) => scrutineeVar.getOrCreateScrutinee.withAlias(scrutineeVar).getOrCreateLiteralPattern(literal).addLocation(literal) @@ -359,7 +358,7 @@ trait Desugaring { self: PreTyper => withBindings ++ rec(scrutineeVar, tail) } case pattern @ s.RecordPattern(_) => - raiseError(PreTyping, msg"record pattern is not supported for now" -> pattern.toLoc) + raiseDesugaringError(msg"record pattern is not supported for now" -> pattern.toLoc) rec(scrutineeVar, tail) } case s.Split.Let(isRec, nme, rhs, tail) => diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index d48c95fe..184b36cd 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -27,7 +27,7 @@ trait Normalization { self: DesugarUCS with Traceable => ): Split = if (these.hasElse) { if (those =/= Split.Nil && shouldReportDiscarded) { - raiseWarning( + raiseDesugaringWarning( msg"the case is unreachable" -> those.toLoc, msg"because this branch already covers the case" -> these.toLoc ) @@ -83,7 +83,7 @@ trait Normalization { self: DesugarUCS with Traceable => def :++(tail: => Split): Split = { if (these.hasElse) { println("tail is discarded") - // raiseWarning(msg"Discarded split because of else branch" -> these.toLoc) + // raiseDesugaringWarning(msg"Discarded split because of else branch" -> these.toLoc) these } else { these ++ tail @@ -137,7 +137,7 @@ trait Normalization { self: DesugarUCS with Traceable => val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context)) CaseOf(scrutineeVar, Case(nme, trueBranch, falseBranch)(refined = pattern.refined)) case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => - raiseError(msg"unsupported pattern: ${pattern.toString}" -> pattern.toLoc) + raiseDesugaringError(msg"unsupported pattern: ${pattern.toString}" -> pattern.toLoc) errorTerm case Split.Let(_, nme, _, tail) if context.isScrutineeVar(nme) && generatedVars.contains(nme) => println(s"LET: SKIP already declared scrutinee ${nme.name}") @@ -152,7 +152,7 @@ trait Normalization { self: DesugarUCS with Traceable => println(s"DFLT: ${default.showDbg}") default case Split.Nil => - raiseError(msg"unexpected empty split found" -> N) + raiseDesugaringError(msg"unexpected empty split found" -> N) errorTerm } }(split => "normalizeToTerm ==> " + showNormalizedTerm(split)) @@ -211,7 +211,7 @@ trait Normalization { self: DesugarUCS with Traceable => println(s"Case 1: class name: ${className.name} === ${otherClassName.name}") if (otherRefined =/= refined) { def be(value: Bool): Str = if (value) "is" else "is not" - raiseWarning( + raiseDesugaringWarning( msg"inconsistent refined case branches" -> pattern.toLoc, msg"class pattern ${className.name} ${be(refined)} refined" -> className.toLoc, msg"but class pattern ${otherClassName.name} ${be(otherRefined)} refined" -> otherClassName.toLoc @@ -230,7 +230,7 @@ trait Normalization { self: DesugarUCS with Traceable => } case _ => // TODO: Make a case for this. Check if this is a valid case. - raiseError( + raiseDesugaringError( msg"pattern ${pattern.toString}" -> pattern.toLoc, msg"is incompatible with class pattern ${otherClassName.name}" -> otherClassName.toLoc, ) @@ -253,7 +253,7 @@ trait Normalization { self: DesugarUCS with Traceable => println("both of them are class patterns") if (otherRefined =/= refined) { def be(value: Bool): Str = if (value) "is" else "is not" - raiseWarning( + raiseDesugaringWarning( msg"inconsistent refined case branches" -> pattern.toLoc, msg"class pattern ${className.name} ${be(refined)} refined" -> className.toLoc, msg"but class pattern ${otherClassName.name} ${be(otherRefined)} refined" -> otherClassName.toLoc @@ -317,7 +317,7 @@ trait Normalization { self: DesugarUCS with Traceable => } // Other patterns. Not implemented. case (_, split @ Split.Cons(Branch(otherScrutineeVar, pattern, continuation), tail)) => - raiseError(msg"found unsupported pattern: ${pattern.toString}" -> pattern.toLoc) + raiseDesugaringError(msg"found unsupported pattern: ${pattern.toString}" -> pattern.toLoc) split case (_, let @ Split.Let(_, nme, _, tail)) => println(s"let binding ${nme.name}, go next") diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index 29b7fbe3..5fa77fba 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -1,18 +1,13 @@ package mlscript.ucs.stages +import mlscript.ucs.syntax.source._ import mlscript.ucs.{DesugarUCS, helpers} import mlscript.{If, IfBody, IfBlock, IfElse, IfLet, IfOpApp, IfOpsApp, IfThen} -import mlscript.{Blk, Term, Var, App, Tup, Lit, Fld, Loc} -import mlscript.Diagnostic.PreTyping +import mlscript.{Blk, Term, Var, App, Tup, Lit, Fld, Loc, NuFunDef, PlainTup} import mlscript.pretyper.Traceable -import mlscript.ucs.syntax.source._ import mlscript.Message, Message._ import mlscript.utils._, shorthands._ -import mlscript.NuFunDef -import mlscript.PlainTup -import scala.collection.immutable -import scala.annotation.tailrec -import scala.util.chaining._ +import scala.collection.immutable, scala.annotation.tailrec, scala.util.chaining._ /** * Transform the parsed AST into an AST similar to the one in the paper. @@ -72,7 +67,7 @@ trait Transformation { self: DesugarUCS with Traceable => case (acc, R(NuFunDef(S(rec), nme, _, _, L(rhs)))) => acc ++ Split.Let(rec, nme, rhs, Split.Nil) case (acc, R(statement)) => - raiseError(msg"Unexpected statement in an if block" -> statement.toLoc) + raiseDesugaringError(msg"Unexpected statement in an if block" -> statement.toLoc) acc } case IfOpsApp(lhs, opsRhss) => @@ -99,7 +94,7 @@ trait Transformation { self: DesugarUCS with Traceable => opsRhss.iterator.flatMap { case Var("and") -> rhs => S(transformIfBody(rhs)) case op -> rhs => - raiseError( + raiseDesugaringError( msg"cannot transform due to an illegal split operator ${op.name}" -> op.toLoc, msg"the following branch will be discarded" -> rhs.toLoc) N @@ -129,7 +124,7 @@ trait Transformation { self: DesugarUCS with Traceable => val ::(head, tail) = splitAnd(lhs) PatternBranch(transformPattern(head), transformConjunction(tail, transformIfBody(rhs), false)).toSplit case IfOpApp(lhs, op, rhs) => - raiseError(msg"Syntactic split of patterns are not supported" -> op.toLoc) + raiseDesugaringError(msg"Syntactic split of patterns are not supported" -> op.toLoc) Split.Nil case IfOpsApp(lhs, opsRhss) => // BEGIN TEMPORARY PATCH @@ -172,7 +167,7 @@ trait Transformation { self: DesugarUCS with Traceable => case (acc, R(NuFunDef(S(rec), nme, _, _, L(rhs)))) => acc ++ Split.Let(rec, nme, rhs, Split.Nil) case (acc, R(statement)) => - raiseError(msg"Unexpected statement in an if block" -> statement.toLoc) + raiseDesugaringError(msg"Unexpected statement in an if block" -> statement.toLoc) acc } case IfElse(expr) => Split.default(expr) @@ -199,7 +194,7 @@ trait Transformation { self: DesugarUCS with Traceable => transformPattern(p) match { case cp: ClassPattern => cp.copy(refined = true).withLocOf(cp) case p => - raiseError(msg"only class patterns can be refined" -> p.toLoc) + raiseDesugaringError(msg"only class patterns can be refined" -> p.toLoc) p } case App(classNme @ Var(_), parameters: Tup) => @@ -208,7 +203,7 @@ trait Transformation { self: DesugarUCS with Traceable => case Blk((term: Term) :: Nil) => transformPattern(term) // A speical case for FunnyIndet.mls case other => println(s"other $other") - raiseError(msg"unknown pattern ${other.showDbg}" -> other.toLoc) + raiseDesugaringError(msg"unknown pattern ${other.showDbg}" -> other.toLoc) EmptyPattern(other) } diff --git a/shared/src/test/diff/codegen/AuxiliaryConstructors.mls b/shared/src/test/diff/codegen/AuxiliaryConstructors.mls index ac763dbf..5190d2ed 100644 --- a/shared/src/test/diff/codegen/AuxiliaryConstructors.mls +++ b/shared/src/test/diff/codegen/AuxiliaryConstructors.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs :js class A(x: Int) {} diff --git a/shared/src/test/diff/codegen/Mixin.mls b/shared/src/test/diff/codegen/Mixin.mls index 4462228f..d1655638 100644 --- a/shared/src/test/diff/codegen/Mixin.mls +++ b/shared/src/test/diff/codegen/Mixin.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs :js class Add(lhs: E, rhs: E) @@ -367,9 +367,6 @@ Barr.y mixin Base { fun x = y } -//│ ╔══[ERROR] identifier `y` not found -//│ ║ l.368: fun x = y -//│ ╙── ^ //│ ╔══[ERROR] identifier not found: y //│ ║ l.368: fun x = y //│ ╙── ^ diff --git a/shared/src/test/diff/codegen/MixinCapture.mls b/shared/src/test/diff/codegen/MixinCapture.mls index 8bad474e..1137c594 100644 --- a/shared/src/test/diff/codegen/MixinCapture.mls +++ b/shared/src/test/diff/codegen/MixinCapture.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs :js class Lit(n: Int) diff --git a/shared/src/test/diff/codegen/Nested.mls b/shared/src/test/diff/codegen/Nested.mls index 59fe95ae..b4f07150 100644 --- a/shared/src/test/diff/codegen/Nested.mls +++ b/shared/src/test/diff/codegen/Nested.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs :js module A { @@ -1262,9 +1262,6 @@ fun main = else g(x - 1) let g(x: Int): Int = f(x) f -//│ ╔══[ERROR] identifier `g` not found -//│ ║ l.1262: else g(x - 1) -//│ ╙── ^ //│ fun main: (x: Int) -> Int //│ // Prelude //│ class TypingUnit24 {} diff --git a/shared/src/test/diff/codegen/NewMatching.mls b/shared/src/test/diff/codegen/NewMatching.mls index 40b5a0bb..ea173a88 100644 --- a/shared/src/test/diff/codegen/NewMatching.mls +++ b/shared/src/test/diff/codegen/NewMatching.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class V0() class V1(val x: Int) diff --git a/shared/src/test/diff/codegen/ValLet.mls b/shared/src/test/diff/codegen/ValLet.mls index e1b04b7e..6cdfebc4 100644 --- a/shared/src/test/diff/codegen/ValLet.mls +++ b/shared/src/test/diff/codegen/ValLet.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs :js class A(x0: Int) { diff --git a/shared/src/test/diff/ecoop23/ComparePointPoly.mls b/shared/src/test/diff/ecoop23/ComparePointPoly.mls index e3cd651e..c90dfde5 100644 --- a/shared/src/test/diff/ecoop23/ComparePointPoly.mls +++ b/shared/src/test/diff/ecoop23/ComparePointPoly.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Some[out A](val value: A) diff --git a/shared/src/test/diff/ecoop23/ExpressionProblem.mls b/shared/src/test/diff/ecoop23/ExpressionProblem.mls index 8b6b16d7..7745355a 100644 --- a/shared/src/test/diff/ecoop23/ExpressionProblem.mls +++ b/shared/src/test/diff/ecoop23/ExpressionProblem.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs // * Motivating paper example, demonstrating the expression problem solution diff --git a/shared/src/test/diff/ecoop23/Intro.mls b/shared/src/test/diff/ecoop23/Intro.mls index 880c19b9..dda08211 100644 --- a/shared/src/test/diff/ecoop23/Intro.mls +++ b/shared/src/test/diff/ecoop23/Intro.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs // * Examples from paper intro diff --git a/shared/src/test/diff/ecoop23/PolymorphicVariants.mls b/shared/src/test/diff/ecoop23/PolymorphicVariants.mls index 66da4d52..4ca877b1 100644 --- a/shared/src/test/diff/ecoop23/PolymorphicVariants.mls +++ b/shared/src/test/diff/ecoop23/PolymorphicVariants.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs // * Adapted example from Code reuse through polymorphic variants (FOSE 2000) diff --git a/shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls b/shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls index df937b92..deffc2a4 100644 --- a/shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls +++ b/shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs // * Adapted example from Compositional Embeddings of Domain-Specific Languages (OOPSLA 2022) diff --git a/shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls b/shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls index 21c6d8ee..beb48255 100644 --- a/shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls +++ b/shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs // * Adapted example from Compositional Embeddings of Domain-Specific Languages (OOPSLA 2022) diff --git a/shared/src/test/diff/fcp/QML_exist_nu.mls b/shared/src/test/diff/fcp/QML_exist_nu.mls index 2c0986bf..39fa4e19 100644 --- a/shared/src/test/diff/fcp/QML_exist_nu.mls +++ b/shared/src/test/diff/fcp/QML_exist_nu.mls @@ -1,6 +1,6 @@ // * TODO also a GADT version of this where we use `Arrays[A]: ArraysImpl[A, ?]` -:PreTyper +:NewDefs :DontDistributeForalls // * Also works without this diff --git a/shared/src/test/diff/gadt/Exp1.mls b/shared/src/test/diff/gadt/Exp1.mls index 97db8501..8daa75e1 100644 --- a/shared/src/test/diff/gadt/Exp1.mls +++ b/shared/src/test/diff/gadt/Exp1.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs abstract class Exp[A]: Pair | Lit @@ -36,12 +36,6 @@ fun f(e) = if e is //│ ╔══[ERROR] unknown pattern Pair‹'a, 'b›(l, r,) //│ ║ l.35: Pair['a, 'b](l, r) then [l, r] //│ ╙── ^^^^^^^^^^^^^^^^^^ -//│ ╔══[ERROR] identifier `l` not found -//│ ║ l.35: Pair['a, 'b](l, r) then [l, r] -//│ ╙── ^ -//│ ╔══[ERROR] identifier `r` not found -//│ ║ l.35: Pair['a, 'b](l, r) then [l, r] -//│ ╙── ^ //│ ╔══[ERROR] identifier not found: l //│ ║ l.35: Pair['a, 'b](l, r) then [l, r] //│ ╙── ^ @@ -58,14 +52,11 @@ fun f(e) = if e is Pair(l: a, r) then let f(x: a) = x f(l) -//│ ╔══[ERROR] identifier `l` not found -//│ ║ l.60: f(l) -//│ ╙── ^ //│ ╔══[ERROR] type identifier not found: a -//│ ║ l.59: let f(x: a) = x +//│ ║ l.53: let f(x: a) = x //│ ╙── ^ //│ ╔══[ERROR] identifier not found: l -//│ ║ l.60: f(l) +//│ ║ l.54: f(l) //│ ╙── ^ //│ fun f: Pair[anything, anything] -> error //│ Code generation encountered an error: diff --git a/shared/src/test/diff/gadt/Exp2.mls b/shared/src/test/diff/gadt/Exp2.mls index c8f3abc4..79ea376a 100644 --- a/shared/src/test/diff/gadt/Exp2.mls +++ b/shared/src/test/diff/gadt/Exp2.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs diff --git a/shared/src/test/diff/gadt/ThisMatching.mls b/shared/src/test/diff/gadt/ThisMatching.mls index 0d58169a..7e7cd4d5 100644 --- a/shared/src/test/diff/gadt/ThisMatching.mls +++ b/shared/src/test/diff/gadt/ThisMatching.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs :e @@ -28,9 +28,6 @@ Dummy.introspect abstract class Funny: Int { fun test = this + 1 } -//│ ╔══[ERROR] Undefined type Int -//│ ║ l.30: abstract class Funny: Int { fun test = this + 1 } -//│ ╙── ^^^ //│ abstract class Funny: Int { //│ fun test: Int //│ } @@ -38,10 +35,10 @@ abstract class Funny: Int { fun test = this + 1 } :e class Unfunny { fun test = this + 1 } //│ ╔══[ERROR] Type mismatch in operator application: -//│ ║ l.39: class Unfunny { fun test = this + 1 } +//│ ║ l.36: class Unfunny { fun test = this + 1 } //│ ║ ^^^^^^^^ //│ ╟── reference of type `#Unfunny` is not an instance of type `Int` -//│ ║ l.39: class Unfunny { fun test = this + 1 } +//│ ║ l.36: class Unfunny { fun test = this + 1 } //│ ╙── ^^^^ //│ class Unfunny { //│ constructor() @@ -90,15 +87,15 @@ abstract class Exp: (() | Wrap) { } class Wrap(n: Exp) extends Exp //│ ╔══[ERROR] Unhandled cyclic definition -//│ ║ l.86: abstract class Exp: (() | Wrap) { +//│ ║ l.83: abstract class Exp: (() | Wrap) { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.87: fun test = if this is +//│ ║ l.84: fun test = if this is //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.88: Wrap(a) then 0 +//│ ║ l.85: Wrap(a) then 0 //│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.89: () then 1 +//│ ║ l.86: () then 1 //│ ║ ^^^^^^^^^^^^^ -//│ ║ l.90: } +//│ ║ l.87: } //│ ╙── ^ //│ abstract class Exp: Wrap | () { //│ fun test: 0 | 1 @@ -116,17 +113,17 @@ abstract class Exp: (Pair | Lit) { class Lit(n: Int) extends Exp class Pair(lhs: Exp, rhs: Exp) extends Exp //│ ╔══[ERROR] Unhandled cyclic definition -//│ ║ l.110: abstract class Exp: (Pair | Lit) { +//│ ║ l.107: abstract class Exp: (Pair | Lit) { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.111: fun test: Int +//│ ║ l.108: fun test: Int //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.112: fun test = if this is +//│ ║ l.109: fun test = if this is //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.113: Lit then 0 +//│ ║ l.110: Lit then 0 //│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.114: Pair(l, r) then 1 +//│ ║ l.111: Pair(l, r) then 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.115: } +//│ ║ l.112: } //│ ╙── ^ //│ abstract class Exp: Lit | Pair { //│ fun test: Int @@ -139,7 +136,7 @@ class Pair(lhs: Exp, rhs: Exp) extends Exp :e // TODO Pair(Lit(1), Lit(2)).test //│ ╔══[ERROR] Type `Pair` does not contain member `test` -//│ ║ l.140: Pair(Lit(1), Lit(2)).test +//│ ║ l.137: Pair(Lit(1), Lit(2)).test //│ ╙── ^^^^^ //│ error //│ res @@ -155,21 +152,21 @@ abstract class Exp: (Pair | Lit) { class Lit(n: Int) extends Exp class Pair(lhs: Exp, rhs: Exp) extends Exp //│ ╔══[ERROR] Unhandled cyclic definition -//│ ║ l.150: abstract class Exp: (Pair | Lit) { +//│ ║ l.147: abstract class Exp: (Pair | Lit) { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.151: fun test = if this is +//│ ║ l.148: fun test = if this is //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.152: Lit then 0 +//│ ║ l.149: Lit then 0 //│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.153: Pair(l, r) then l.test + r.test +//│ ║ l.150: Pair(l, r) then l.test + r.test //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.154: } +//│ ║ l.151: } //│ ╙── ^ //│ ╔══[ERROR] Indirectly-recursive member should have type annotation -//│ ║ l.153: Pair(l, r) then l.test + r.test +//│ ║ l.150: Pair(l, r) then l.test + r.test //│ ╙── ^^^^^ //│ ╔══[ERROR] Indirectly-recursive member should have type annotation -//│ ║ l.153: Pair(l, r) then l.test + r.test +//│ ║ l.150: Pair(l, r) then l.test + r.test //│ ╙── ^^^^^ //│ abstract class Exp: Lit | Pair { //│ fun test: Int @@ -190,17 +187,17 @@ abstract class Exp: (Pair | Lit) { class Lit(n: Int) extends Exp class Pair(lhs: Exp, rhs: Exp) extends Exp //│ ╔══[ERROR] Unhandled cyclic definition -//│ ║ l.184: abstract class Exp: (Pair | Lit) { +//│ ║ l.181: abstract class Exp: (Pair | Lit) { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.185: fun test : Int +//│ ║ l.182: fun test : Int //│ ║ ^^^^^^^^^^^^^^^^ -//│ ║ l.186: fun test = if this is +//│ ║ l.183: fun test = if this is //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.187: Lit then 0 +//│ ║ l.184: Lit then 0 //│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.188: Pair(l, r) then l.test + r.test +//│ ║ l.185: Pair(l, r) then l.test + r.test //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.189: } +//│ ║ l.186: } //│ ╙── ^ //│ abstract class Exp: Lit | Pair { //│ fun test: Int @@ -220,25 +217,25 @@ abstract class Exp[A]: (Pair | Lit) { class Lit(n: Int) extends Exp[Int] class Pair[L, R](lhs: L, rhs: R) extends Exp[[L, R]] //│ ╔══[ERROR] Unhandled cyclic definition -//│ ║ l.215: abstract class Exp[A]: (Pair | Lit) { +//│ ║ l.212: abstract class Exp[A]: (Pair | Lit) { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.216: fun test = if this is +//│ ║ l.213: fun test = if this is //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.217: Lit then 0 +//│ ║ l.214: Lit then 0 //│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.218: Pair then 1 +//│ ║ l.215: Pair then 1 //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.219: } +//│ ║ l.216: } //│ ╙── ^ //│ ╔══[ERROR] Type error in `case` expression -//│ ║ l.216: fun test = if this is +//│ ║ l.213: fun test = if this is //│ ║ ^^^^^^^ -//│ ║ l.217: Lit then 0 +//│ ║ l.214: Lit then 0 //│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.218: Pair then 1 +//│ ║ l.215: Pair then 1 //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── type variable `L` leaks out of its scope -//│ ║ l.221: class Pair[L, R](lhs: L, rhs: R) extends Exp[[L, R]] +//│ ║ l.218: class Pair[L, R](lhs: L, rhs: R) extends Exp[[L, R]] //│ ╙── ^ //│ abstract class Exp[A]: Lit | Pair[anything, anything] { //│ fun test: 0 | 1 diff --git a/shared/src/test/diff/nu/Andong.mls b/shared/src/test/diff/nu/Andong.mls index fa997a58..e2f1e77e 100644 --- a/shared/src/test/diff/nu/Andong.mls +++ b/shared/src/test/diff/nu/Andong.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Union(a: Region, b: Region) diff --git a/shared/src/test/diff/nu/ArrayProg.mls b/shared/src/test/diff/nu/ArrayProg.mls index 57e8f50f..a0b7553f 100644 --- a/shared/src/test/diff/nu/ArrayProg.mls +++ b/shared/src/test/diff/nu/ArrayProg.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs diff --git a/shared/src/test/diff/nu/BadUCS.mls b/shared/src/test/diff/nu/BadUCS.mls index c4fd3013..d5bd9333 100644 --- a/shared/src/test/diff/nu/BadUCS.mls +++ b/shared/src/test/diff/nu/BadUCS.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Foo diff --git a/shared/src/test/diff/nu/BasicClassInheritance.mls b/shared/src/test/diff/nu/BasicClassInheritance.mls index b365a2b0..29907d33 100644 --- a/shared/src/test/diff/nu/BasicClassInheritance.mls +++ b/shared/src/test/diff/nu/BasicClassInheritance.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class A diff --git a/shared/src/test/diff/nu/BasicClasses.mls b/shared/src/test/diff/nu/BasicClasses.mls index 0877423d..a24dc50f 100644 --- a/shared/src/test/diff/nu/BasicClasses.mls +++ b/shared/src/test/diff/nu/BasicClasses.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class A(val n: Int) diff --git a/shared/src/test/diff/nu/CaseExpr.mls b/shared/src/test/diff/nu/CaseExpr.mls index 0cb1b78b..cc358e0f 100644 --- a/shared/src/test/diff/nu/CaseExpr.mls +++ b/shared/src/test/diff/nu/CaseExpr.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs case 0 then true @@ -66,9 +66,6 @@ fun map(f) = case //│ ╔══[ERROR] type identifier `as` not found //│ ║ l.65: None as n then n //│ ╙── ^^ -//│ ╔══[ERROR] identifier `as` not found -//│ ║ l.65: None as n then n -//│ ╙── ^^ //│ ╔══[ERROR] type identifier not found: as //│ ║ l.65: None as n then n //│ ╙── ^^ @@ -80,10 +77,10 @@ fun map(f) = case :pe case 1 //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'case'; found integer literal instead -//│ ║ l.81: case 1 +//│ ║ l.78: case 1 //│ ║ ^ //│ ╟── Note: 'case' expression starts here: -//│ ║ l.81: case 1 +//│ ║ l.78: case 1 //│ ╙── ^^^^ //│ anything -> 1 //│ res @@ -92,13 +89,13 @@ case 1 :pe case (1 then true) //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here -//│ ║ l.93: case (1 then true) +//│ ║ l.90: case (1 then true) //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'case'; found integer literal instead -//│ ║ l.93: case (1 then true) +//│ ║ l.90: case (1 then true) //│ ║ ^^^^^^^^^^^^^ //│ ╟── Note: 'case' expression starts here: -//│ ║ l.93: case (1 then true) +//│ ║ l.90: case (1 then true) //│ ╙── ^^^^ //│ anything -> 1 //│ res @@ -112,16 +109,16 @@ case else 0 :pe case then 1 else 0 //│ ╔══[PARSE ERROR] Unexpected 'then' keyword in expression position -//│ ║ l.113: case then 1 else 0 +//│ ║ l.110: case then 1 else 0 //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'case'; found integer literal instead -//│ ║ l.113: case then 1 else 0 +//│ ║ l.110: case then 1 else 0 //│ ║ ^ //│ ╟── Note: 'case' expression starts here: -//│ ║ l.113: case then 1 else 0 +//│ ║ l.110: case then 1 else 0 //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected end of input; found 'else' keyword instead -//│ ║ l.113: case then 1 else 0 +//│ ║ l.110: case then 1 else 0 //│ ╙── ^^^^ //│ anything -> 1 //│ res @@ -135,19 +132,16 @@ case then 1 else 0 :e case x, y then x + y //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead -//│ ║ l.136: case x, y then x + y +//│ ║ l.133: case x, y then x + y //│ ╙── ^^^^^^^^^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'case'; found operator application instead -//│ ║ l.136: case x, y then x + y +//│ ║ l.133: case x, y then x + y //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── Note: 'case' expression starts here: -//│ ║ l.136: case x, y then x + y +//│ ║ l.133: case x, y then x + y //│ ╙── ^^^^ -//│ ╔══[ERROR] identifier `x` not found -//│ ║ l.136: case x, y then x + y -//│ ╙── ^ //│ ╔══[ERROR] identifier not found: x -//│ ║ l.136: case x, y then x + y +//│ ║ l.133: case x, y then x + y //│ ╙── ^ //│ anything -> () //│ Code generation encountered an error: @@ -156,15 +150,10 @@ case x, y then x + y :e case (x, y) then x + y //│ ╔══[ERROR] type identifier `,` not found -//│ ║ l.157: case (x, y) then x + y +//│ ║ l.151: case (x, y) then x + y //│ ╙── ^^^^ -//│ ╔══[ERROR] identifier , refers to different symbols. -//│ ║ l.157: case (x, y) then x + y -//│ ║ ^^^^ -//│ ╟── it is resolved to `,` -//│ ╙── it was previously resolved to local `,` //│ ╔══[ERROR] type identifier not found: , -//│ ║ l.157: case (x, y) then x + y +//│ ║ l.151: case (x, y) then x + y //│ ╙── ^^^^ //│ nothing -> error //│ Code generation encountered an error: diff --git a/shared/src/test/diff/nu/ClassSignatures.mls b/shared/src/test/diff/nu/ClassSignatures.mls index f4fe3759..20c59c94 100644 --- a/shared/src/test/diff/nu/ClassSignatures.mls +++ b/shared/src/test/diff/nu/ClassSignatures.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs abstract class C0: C1 | C2 diff --git a/shared/src/test/diff/nu/ClassesInMixins.mls b/shared/src/test/diff/nu/ClassesInMixins.mls index 17169caf..38201807 100644 --- a/shared/src/test/diff/nu/ClassesInMixins.mls +++ b/shared/src/test/diff/nu/ClassesInMixins.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs @@ -48,9 +48,6 @@ fun foo(x) = if x is M.Foo then 1 :e mixin Test2 { let f = Foo(1) } -//│ ╔══[ERROR] identifier `Foo` not found -//│ ║ l.50: mixin Test2 { let f = Foo(1) } -//│ ╙── ^^^ //│ ╔══[ERROR] identifier not found: Foo //│ ║ l.50: mixin Test2 { let f = Foo(1) } //│ ╙── ^^^ @@ -63,10 +60,10 @@ mixin Test2 { let f = Foo(1) } :e mixin Test3 { fun f(x) = if x is Foo then 1 } //│ ╔══[ERROR] type identifier `Foo` not found -//│ ║ l.64: mixin Test3 { fun f(x) = if x is Foo then 1 } +//│ ║ l.61: mixin Test3 { fun f(x) = if x is Foo then 1 } //│ ╙── ^^^ //│ ╔══[ERROR] type identifier not found: Foo -//│ ║ l.64: mixin Test3 { fun f(x) = if x is Foo then 1 } +//│ ║ l.61: mixin Test3 { fun f(x) = if x is Foo then 1 } //│ ╙── ^^^ //│ mixin Test3() { //│ fun f: nothing -> error @@ -87,10 +84,10 @@ mixin Test { Add(l, r) then this.size(l) + this.size(r) } //│ ╔══[ERROR] Type error in application -//│ ║ l.84: fun cached = size(this) +//│ ║ l.81: fun cached = size(this) //│ ║ ^^^^^^^^^^ //│ ╟── type variable `A` leaks out of its scope -//│ ║ l.82: class Add(lhs: A, rhs: A) { +//│ ║ l.79: class Add(lhs: A, rhs: A) { //│ ╙── ^ //│ mixin Test() { //│ this: {size: (??A | 'a) -> Int} diff --git a/shared/src/test/diff/nu/CommaOperator.mls b/shared/src/test/diff/nu/CommaOperator.mls index 4c13a824..6d7e46db 100644 --- a/shared/src/test/diff/nu/CommaOperator.mls +++ b/shared/src/test/diff/nu/CommaOperator.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs 1; 2 @@ -208,9 +208,6 @@ foo(1), 2 :ge // FIXME let rec x() = x() in x -//│ ╔══[ERROR] identifier `x` not found -//│ ║ l.210: let rec x() = x() in x -//│ ╙── ^ //│ nothing //│ Code generation encountered an error: //│ recursive non-function definition x is not supported @@ -219,7 +216,7 @@ let rec x() = x() in x :pe let x[T] = 1 in x //│ ╔══[PARSE ERROR] Expected function parameter list; found square bracket section instead -//│ ║ l.220: let x[T] = 1 in x +//│ ║ l.217: let x[T] = 1 in x //│ ╙── ^^^ //│ 1 //│ res @@ -284,16 +281,16 @@ foo( :e foo(log(1);2) //│ ╔══[PARSE ERROR] Unexpected semicolon here -//│ ║ l.285: foo(log(1);2) +//│ ║ l.282: foo(log(1);2) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.285: foo(log(1);2) +//│ ║ l.282: foo(log(1);2) //│ ║ ^^^^^^^^^^^^^ //│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` -//│ ║ l.285: foo(log(1);2) +//│ ║ l.282: foo(log(1);2) //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: -//│ ║ l.248: fun foo(x, y) = [x, y] +//│ ║ l.245: fun foo(x, y) = [x, y] //│ ╙── ^^^^^^ //│ error | [nothing, nothing] //│ res @@ -304,13 +301,13 @@ foo(log(1);2) :e foo((log(1),2)) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.305: foo((log(1),2)) +//│ ║ l.302: foo((log(1),2)) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` -//│ ║ l.305: foo((log(1),2)) +//│ ║ l.302: foo((log(1),2)) //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: -//│ ║ l.248: fun foo(x, y) = [x, y] +//│ ║ l.245: fun foo(x, y) = [x, y] //│ ╙── ^^^^^^ //│ error | [nothing, nothing] //│ res @@ -329,13 +326,13 @@ foo((let x = log(0), 1; log(x), x + 1), 2) :e foo(let x = log(0), 1; log(x), x + 1) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.330: foo(let x = log(0), 1; log(x), x + 1) +//│ ║ l.327: foo(let x = log(0), 1; log(x), x + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` -//│ ║ l.330: foo(let x = log(0), 1; log(x), x + 1) +//│ ║ l.327: foo(let x = log(0), 1; log(x), x + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: -//│ ║ l.248: fun foo(x, y) = [x, y] +//│ ║ l.245: fun foo(x, y) = [x, y] //│ ╙── ^^^^^^ //│ error | [nothing, nothing] //│ res @@ -347,13 +344,13 @@ foo(let x = log(0), 1; log(x), x + 1) :e foo(let x = log(0), 1 in log(x), x + 1) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.348: foo(let x = log(0), 1 in log(x), x + 1) +//│ ║ l.345: foo(let x = log(0), 1 in log(x), x + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` -//│ ║ l.348: foo(let x = log(0), 1 in log(x), x + 1) +//│ ║ l.345: foo(let x = log(0), 1 in log(x), x + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: -//│ ║ l.248: fun foo(x, y) = [x, y] +//│ ║ l.245: fun foo(x, y) = [x, y] //│ ╙── ^^^^^^ //│ error | [nothing, nothing] //│ res @@ -365,13 +362,13 @@ foo(let x = log(0), 1 in log(x), x + 1) :e foo(let x = log(0), 1; log(x), 1 + 1) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.366: foo(let x = log(0), 1; log(x), 1 + 1) +//│ ║ l.363: foo(let x = log(0), 1; log(x), 1 + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` -//│ ║ l.366: foo(let x = log(0), 1; log(x), 1 + 1) +//│ ║ l.363: foo(let x = log(0), 1; log(x), 1 + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: -//│ ║ l.248: fun foo(x, y) = [x, y] +//│ ║ l.245: fun foo(x, y) = [x, y] //│ ╙── ^^^^^^ //│ error | [nothing, nothing] //│ res @@ -383,13 +380,13 @@ foo(let x = log(0), 1; log(x), 1 + 1) :e foo(if true then 1 else 2, 3) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.384: foo(if true then 1 else 2, 3) +//│ ║ l.381: foo(if true then 1 else 2, 3) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── argument of type `[1 | ?a]` does not match type `[?b, ?c]` -//│ ║ l.384: foo(if true then 1 else 2, 3) +//│ ║ l.381: foo(if true then 1 else 2, 3) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: -//│ ║ l.248: fun foo(x, y) = [x, y] +//│ ║ l.245: fun foo(x, y) = [x, y] //│ ╙── ^^^^^^ //│ error | [nothing, nothing] //│ res @@ -403,13 +400,13 @@ foo((if true then 1 else 2), 3) :e foo(if true then log("ok"), 1 else log("nok"), 2, 3) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.404: foo(if true then log("ok"), 1 else log("nok"), 2, 3) +//│ ║ l.401: foo(if true then log("ok"), 1 else log("nok"), 2, 3) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── argument of type `[?a | ?b]` does not match type `[?c, ?d]` -//│ ║ l.404: foo(if true then log("ok"), 1 else log("nok"), 2, 3) +//│ ║ l.401: foo(if true then log("ok"), 1 else log("nok"), 2, 3) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: -//│ ║ l.248: fun foo(x, y) = [x, y] +//│ ║ l.245: fun foo(x, y) = [x, y] //│ ╙── ^^^^^^ //│ error | [nothing, nothing] //│ res diff --git a/shared/src/test/diff/nu/CtorSubtraction.mls b/shared/src/test/diff/nu/CtorSubtraction.mls index 06555d1e..9a58ce68 100644 --- a/shared/src/test/diff/nu/CtorSubtraction.mls +++ b/shared/src/test/diff/nu/CtorSubtraction.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Cls diff --git a/shared/src/test/diff/nu/Eval.mls b/shared/src/test/diff/nu/Eval.mls index 1fdb1d62..b187fd83 100644 --- a/shared/src/test/diff/nu/Eval.mls +++ b/shared/src/test/diff/nu/Eval.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs diff --git a/shared/src/test/diff/nu/EvalNegNeg.mls b/shared/src/test/diff/nu/EvalNegNeg.mls index 2b8f7d52..4e3fbbab 100644 --- a/shared/src/test/diff/nu/EvalNegNeg.mls +++ b/shared/src/test/diff/nu/EvalNegNeg.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Add(lhs: E, rhs: E) diff --git a/shared/src/test/diff/nu/ExpressionProblem_repro.mls b/shared/src/test/diff/nu/ExpressionProblem_repro.mls index d6b883fe..70a8a32e 100644 --- a/shared/src/test/diff/nu/ExpressionProblem_repro.mls +++ b/shared/src/test/diff/nu/ExpressionProblem_repro.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs :NoJS diff --git a/shared/src/test/diff/nu/ExpressionProblem_small.mls b/shared/src/test/diff/nu/ExpressionProblem_small.mls index db03532e..128c1eee 100644 --- a/shared/src/test/diff/nu/ExpressionProblem_small.mls +++ b/shared/src/test/diff/nu/ExpressionProblem_small.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs :NoJS diff --git a/shared/src/test/diff/nu/FilterMap.mls b/shared/src/test/diff/nu/FilterMap.mls index b926fc85..bd4274b3 100644 --- a/shared/src/test/diff/nu/FilterMap.mls +++ b/shared/src/test/diff/nu/FilterMap.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs // * From https://arxiv.org/abs/2302.12783 diff --git a/shared/src/test/diff/nu/FlatIfThenElse.mls b/shared/src/test/diff/nu/FlatIfThenElse.mls index 37e3a3da..752aa4ee 100644 --- a/shared/src/test/diff/nu/FlatIfThenElse.mls +++ b/shared/src/test/diff/nu/FlatIfThenElse.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs type Option[out A] = Some[A] | None @@ -108,15 +108,6 @@ fun test(x: Option[Int]) = //│ ╔══[PARSE ERROR] Unexpected 'then' keyword in expression position //│ ║ l.105: then //│ ╙── ^^^^ -//│ ╔══[ERROR] identifier `value` not found -//│ ║ l.107: [value - 1, value + 1] -//│ ╙── ^^^^^ -//│ ╔══[ERROR] identifier `value` not found -//│ ║ l.107: [value - 1, value + 1] -//│ ╙── ^^^^^ -//│ ╔══[ERROR] identifier `value` not found -//│ ║ l.106: log(value) -//│ ╙── ^^^^^ //│ ╔══[ERROR] Unexpected statement in an if block //│ ║ l.104: Some(value) //│ ╙── ^^^^^^^^^^^ diff --git a/shared/src/test/diff/nu/FlatMonads.mls b/shared/src/test/diff/nu/FlatMonads.mls index 6345cc86..8ff1324b 100644 --- a/shared/src/test/diff/nu/FlatMonads.mls +++ b/shared/src/test/diff/nu/FlatMonads.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs declare fun String: anything -> Str @@ -120,11 +120,8 @@ printLine("").bind of () => error //│ res //│ = Bind {} -:w + printLine("").bind of (()) => error -//│ ╔══[WARNING] literal patterns are ignored -//│ ║ l.124: printLine("").bind of (()) => error -//│ ╙── ^^ //│ Bind[(), 'B] //│ res //│ = Bind {} @@ -308,13 +305,13 @@ let r = loop(defaultCtx).run :e not(r) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.309: not(r) +//│ ║ l.306: not(r) //│ ║ ^^^^^^ //│ ╟── type `Int` is not an instance of type `Bool` //│ ║ l.34: module readInt extends IO[Int] { fun run: Int = 42 } //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `Bool` -//│ ║ l.309: not(r) +//│ ║ l.306: not(r) //│ ╙── ^ //│ error | false | true //│ res @@ -358,20 +355,16 @@ main //│ = Bind {} -:w :e let r = printLine("").bind of 0 => Pure(1) -//│ ╔══[WARNING] literal patterns are ignored -//│ ║ l.363: let r = printLine("").bind of 0 => Pure(1) -//│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.363: let r = printLine("").bind of 0 => Pure(1) +//│ ║ l.359: let r = printLine("").bind of 0 => Pure(1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `()` does not match type `0` -//│ ║ l.327: class printLine(str: Str) extends IO { fun run = log(str) } +//│ ║ l.324: class printLine(str: Str) extends IO { fun run = log(str) } //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from integer literal: -//│ ║ l.363: let r = printLine("").bind of 0 => Pure(1) +//│ ║ l.359: let r = printLine("").bind of 0 => Pure(1) //│ ╙── ^ //│ let r: Bind[in 0 & 'A out () | 'A, 'B] | error //│ where @@ -384,20 +377,20 @@ let r = printLine("").bind of x => log(x.a) Pure(1) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.383: let r = printLine("").bind of x => +//│ ║ l.376: let r = printLine("").bind of x => //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.384: log(x.a) +//│ ║ l.377: log(x.a) //│ ║ ^^^^^^^^^ -//│ ║ l.385: Pure(1) +//│ ║ l.378: Pure(1) //│ ║ ^^^^^^^ //│ ╟── application of type `()` does not have field 'a' -//│ ║ l.327: class printLine(str: Str) extends IO { fun run = log(str) } +//│ ║ l.324: class printLine(str: Str) extends IO { fun run = log(str) } //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from field selection: -//│ ║ l.384: log(x.a) +//│ ║ l.377: log(x.a) //│ ║ ^^^ //│ ╟── from reference: -//│ ║ l.384: log(x.a) +//│ ║ l.377: log(x.a) //│ ╙── ^ //│ let r: Bind[in {a: anything} & 'A out () | 'A, 'B] | error //│ where diff --git a/shared/src/test/diff/nu/FunnyIndet.mls b/shared/src/test/diff/nu/FunnyIndet.mls index 7f833139..0ad3e5aa 100644 --- a/shared/src/test/diff/nu/FunnyIndet.mls +++ b/shared/src/test/diff/nu/FunnyIndet.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs 2 + diff --git a/shared/src/test/diff/nu/GADTMono.mls b/shared/src/test/diff/nu/GADTMono.mls index 1221d79b..0d8323d5 100644 --- a/shared/src/test/diff/nu/GADTMono.mls +++ b/shared/src/test/diff/nu/GADTMono.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs trait Expr[A]: LitInt | LitBool | Add | Cond | Pair | Fst | Snd class LitInt(n: Int) extends Expr[Int] diff --git a/shared/src/test/diff/nu/GenericClasses.mls b/shared/src/test/diff/nu/GenericClasses.mls index 64c1ef7f..8ce63d9d 100644 --- a/shared/src/test/diff/nu/GenericClasses.mls +++ b/shared/src/test/diff/nu/GenericClasses.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class C diff --git a/shared/src/test/diff/nu/GenericModules.mls b/shared/src/test/diff/nu/GenericModules.mls index 75c5b56c..778511bb 100644 --- a/shared/src/test/diff/nu/GenericModules.mls +++ b/shared/src/test/diff/nu/GenericModules.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs // * TODO generic module definitions need to be restricted so they do not include nay state diff --git a/shared/src/test/diff/nu/HeungTung.mls b/shared/src/test/diff/nu/HeungTung.mls index 04143b15..3b0c9c24 100644 --- a/shared/src/test/diff/nu/HeungTung.mls +++ b/shared/src/test/diff/nu/HeungTung.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs @@ -134,9 +134,6 @@ f(refined if true then 0 else false) // this one can be precise again! //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.133: f(refined if true then 0 else false) // this one can be precise again! //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╔══[ERROR] identifier `refined` not found -//│ ║ l.133: f(refined if true then 0 else false) // this one can be precise again! -//│ ╙── ^^^^^^^ //│ ╔══[ERROR] Illegal use of reserved operator: refined //│ ║ l.133: f(refined if true then 0 else false) // this one can be precise again! //│ ╙── ^^^^^^^ @@ -199,7 +196,7 @@ type T = List[Int] :e // TODO application types type Res = M(T) //│ ╔══[ERROR] Wrong number of type arguments – expected 0, found 1 -//│ ║ l.200: type Res = M(T) +//│ ║ l.197: type Res = M(T) //│ ╙── ^^^^ //│ type Res = M @@ -222,7 +219,7 @@ fun f: Int -> Int fun f: Bool -> Bool fun f = id //│ ╔══[ERROR] A type signature for 'f' was already given -//│ ║ l.222: fun f: Bool -> Bool +//│ ║ l.219: fun f: Bool -> Bool //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ fun f: forall 'a. 'a -> 'a //│ fun f: Int -> Int @@ -230,13 +227,13 @@ fun f = id :e // TODO support f: (Int -> Int) & (Bool -> Bool) //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.231: f: (Int -> Int) & (Bool -> Bool) +//│ ║ l.228: f: (Int -> Int) & (Bool -> Bool) //│ ║ ^ //│ ╟── type `Bool` is not an instance of type `Int` -//│ ║ l.231: f: (Int -> Int) & (Bool -> Bool) +//│ ║ l.228: f: (Int -> Int) & (Bool -> Bool) //│ ║ ^^^^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.221: fun f: Int -> Int +//│ ║ l.218: fun f: Int -> Int //│ ╙── ^^^ //│ Int -> Int & Bool -> Bool //│ res @@ -303,20 +300,17 @@ fun test(x) = refined if x is A then 0 B then 1 //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.302: fun test(x) = refined if x is +//│ ║ l.299: fun test(x) = refined if x is //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.303: A then 0 +//│ ║ l.300: A then 0 //│ ║ ^^^^^^^^^^ -//│ ║ l.304: B then 1 +//│ ║ l.301: B then 1 //│ ╙── ^^^^^^^^^^ -//│ ╔══[ERROR] identifier `refined` not found -//│ ║ l.302: fun test(x) = refined if x is -//│ ╙── ^^^^^^^ //│ ╔══[ERROR] Illegal use of reserved operator: refined -//│ ║ l.302: fun test(x) = refined if x is +//│ ║ l.299: fun test(x) = refined if x is //│ ╙── ^^^^^^^ //│ ╔══[ERROR] identifier not found: refined -//│ ║ l.302: fun test(x) = refined if x is +//│ ║ l.299: fun test(x) = refined if x is //│ ╙── ^^^^^^^ //│ fun test: (A | B) -> error //│ Code generation encountered an error: diff --git a/shared/src/test/diff/nu/Huawei1.mls b/shared/src/test/diff/nu/Huawei1.mls index 4ca49f19..7b939fe6 100644 --- a/shared/src/test/diff/nu/Huawei1.mls +++ b/shared/src/test/diff/nu/Huawei1.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class C[out A](x: A) { diff --git a/shared/src/test/diff/nu/InterfaceMono.mls b/shared/src/test/diff/nu/InterfaceMono.mls index f5088e03..c19e5930 100644 --- a/shared/src/test/diff/nu/InterfaceMono.mls +++ b/shared/src/test/diff/nu/InterfaceMono.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs trait Showable { @@ -10,9 +10,6 @@ trait Showable { :e trait What0 extends woooo -//│ ╔══[ERROR] could not find definition `woooo` -//│ ║ l.12: trait What0 extends woooo -//│ ╙── ^^^^^ //│ ╔══[ERROR] Could not find definition `woooo` //│ ║ l.12: trait What0 extends woooo //│ ╙── ^^^^^ @@ -33,7 +30,7 @@ class What1(toString: Str) extends Showable :e trait NoShow extends What1("hi") //│ ╔══[ERROR] A trait can only inherit from other traits -//│ ║ l.34: trait NoShow extends What1("hi") +//│ ║ l.31: trait NoShow extends What1("hi") //│ ╙── ^^^^^^^^^^^ //│ trait NoShow extends Showable, What1 @@ -44,19 +41,19 @@ class ErrC2 extends Showable { } class ErrC3(toString: Str -> Str) extends Showable //│ ╔══[ERROR] Member `toString` is declared (or its declaration is inherited) but is not implemented in `ErrC1` -//│ ║ l.41: class ErrC1 extends Showable +//│ ║ l.38: class ErrC1 extends Showable //│ ║ ^^^^^ //│ ╟── Declared here: //│ ║ l.5: fun toString: Str //│ ╙── ^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in definition of method toString: -//│ ║ l.43: fun toString = 114 +//│ ║ l.40: fun toString = 114 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── integer literal of type `114` is not an instance of type `Str` -//│ ║ l.43: fun toString = 114 +//│ ║ l.40: fun toString = 114 //│ ║ ^^^ //│ ╟── but it flows into definition of method toString with expected type `Str` -//│ ║ l.43: fun toString = 114 +//│ ║ l.40: fun toString = 114 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.5: fun toString: Str @@ -65,7 +62,7 @@ class ErrC3(toString: Str -> Str) extends Showable //│ ║ l.5: fun toString: Str //│ ╙── ^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in function type: -//│ ║ l.45: class ErrC3(toString: Str -> Str) extends Showable +//│ ║ l.42: class ErrC3(toString: Str -> Str) extends Showable //│ ║ ^^^^^^^^^^ //│ ╟── type `Str -> Str` is not an instance of type `Str` //│ ╟── Note: constraint arises from type reference: @@ -128,41 +125,41 @@ class Errcity(size: Int) extends SizedStadt { fun bar = "hahaha" } //│ ╔══[ERROR] Type mismatch in definition of method bar: -//│ ║ l.128: fun bar = "hahaha" +//│ ║ l.125: fun bar = "hahaha" //│ ║ ^^^^^^^^^^^^^^ //│ ╟── string literal of type `"hahaha"` is not a function -//│ ║ l.128: fun bar = "hahaha" +//│ ║ l.125: fun bar = "hahaha" //│ ║ ^^^^^^^^ //│ ╟── but it flows into definition of method bar with expected type `Int -> Int` -//│ ║ l.128: fun bar = "hahaha" +//│ ║ l.125: fun bar = "hahaha" //│ ║ ^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from function type: -//│ ║ l.106: fun bar: Int -> Int +//│ ║ l.103: fun bar: Int -> Int //│ ║ ^^^^^^^^^^ //│ ╟── from signature of member `bar`: -//│ ║ l.106: fun bar: Int -> Int +//│ ║ l.103: fun bar: Int -> Int //│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in type reference: -//│ ║ l.127: class Errcity(size: Int) extends SizedStadt { +//│ ║ l.124: class Errcity(size: Int) extends SizedStadt { //│ ║ ^^^ //│ ╟── type `Int` does not match type `1 | 2 | 3` //│ ╟── Note: constraint arises from union type: -//│ ║ l.105: let size: 1 | 2 | 3 +//│ ║ l.102: let size: 1 | 2 | 3 //│ ║ ^^^^^^^^^ //│ ╟── from signature of member `size`: -//│ ║ l.105: let size: 1 | 2 | 3 +//│ ║ l.102: let size: 1 | 2 | 3 //│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Member `name` is declared (or its declaration is inherited) but is not implemented in `Errcity` -//│ ║ l.127: class Errcity(size: Int) extends SizedStadt { +//│ ║ l.124: class Errcity(size: Int) extends SizedStadt { //│ ║ ^^^^^^^ //│ ╟── Declared here: -//│ ║ l.88: let name: Str +//│ ║ l.85: let name: Str //│ ╙── ^^^^^^^^^^^^^ //│ ╔══[ERROR] Member `foo` is declared (or its declaration is inherited) but is not implemented in `Errcity` -//│ ║ l.127: class Errcity(size: Int) extends SizedStadt { +//│ ║ l.124: class Errcity(size: Int) extends SizedStadt { //│ ║ ^^^^^^^ //│ ╟── Declared here: -//│ ║ l.96: fun foo: Bool -> Int +//│ ║ l.93: fun foo: Bool -> Int //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ class Errcity(size: Int) extends RefinedStadt, SizedStadt, Stadt { //│ fun bar: "hahaha" @@ -211,19 +208,19 @@ class Dirtberg extends More, SizedStadt, Fooo { fun size = 4 // this should not check } //│ ╔══[ERROR] Type mismatch in definition of method size: -//│ ║ l.211: fun size = 4 // this should not check +//│ ║ l.208: fun size = 4 // this should not check //│ ║ ^^^^^^^^ //│ ╟── integer literal of type `4` does not match type `1 | 2 | 3` -//│ ║ l.211: fun size = 4 // this should not check +//│ ║ l.208: fun size = 4 // this should not check //│ ║ ^ //│ ╟── but it flows into definition of method size with expected type `1 | 2 | 3` -//│ ║ l.211: fun size = 4 // this should not check +//│ ║ l.208: fun size = 4 // this should not check //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from union type: -//│ ║ l.105: let size: 1 | 2 | 3 +//│ ║ l.102: let size: 1 | 2 | 3 //│ ║ ^^^^^^^^^ //│ ╟── from signature of member `size`: -//│ ║ l.105: let size: 1 | 2 | 3 +//│ ║ l.102: let size: 1 | 2 | 3 //│ ╙── ^^^^^^^^^^^^^^^ //│ class Dirtberg extends RefinedStadt, SizedStadt, Stadt { //│ constructor() @@ -251,19 +248,19 @@ class A { virtual fun x: Int = 1 } :e class B extends A { fun x = "A" } //│ ╔══[ERROR] Type mismatch in definition of method x: -//│ ║ l.252: class B extends A { fun x = "A" } +//│ ║ l.249: class B extends A { fun x = "A" } //│ ║ ^^^^^^^ //│ ╟── string literal of type `"A"` is not an instance of type `Int` -//│ ║ l.252: class B extends A { fun x = "A" } +//│ ║ l.249: class B extends A { fun x = "A" } //│ ║ ^^^ //│ ╟── but it flows into definition of method x with expected type `Int` -//│ ║ l.252: class B extends A { fun x = "A" } +//│ ║ l.249: class B extends A { fun x = "A" } //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.245: class A { virtual fun x: Int = 1 } +//│ ║ l.242: class A { virtual fun x: Int = 1 } //│ ║ ^^^ //│ ╟── from definition of method x: -//│ ║ l.245: class A { virtual fun x: Int = 1 } +//│ ║ l.242: class A { virtual fun x: Int = 1 } //│ ╙── ^^^^^^^^^^ //│ class B extends A { //│ constructor() @@ -273,7 +270,7 @@ class B extends A { fun x = "A" } :e class C1[A] { virtual fun a: A = this.a } //│ ╔══[ERROR] Indirectly-recursive member should have type annotation -//│ ║ l.274: class C1[A] { virtual fun a: A = this.a } +//│ ║ l.271: class C1[A] { virtual fun a: A = this.a } //│ ╙── ^^ //│ class C1[A] { //│ constructor() @@ -307,19 +304,19 @@ class C extends MyTrait[Int] { fun a = 1 } :e class C extends MyTrait[Int] { fun a = false } //│ ╔══[ERROR] Type mismatch in definition of method a: -//│ ║ l.308: class C extends MyTrait[Int] { fun a = false } +//│ ║ l.305: class C extends MyTrait[Int] { fun a = false } //│ ║ ^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of `Int` -//│ ║ l.308: class C extends MyTrait[Int] { fun a = false } +//│ ║ l.305: class C extends MyTrait[Int] { fun a = false } //│ ║ ^^^^^ //│ ╟── but it flows into definition of method a with expected type `Int` -//│ ║ l.308: class C extends MyTrait[Int] { fun a = false } +//│ ║ l.305: class C extends MyTrait[Int] { fun a = false } //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.308: class C extends MyTrait[Int] { fun a = false } +//│ ║ l.305: class C extends MyTrait[Int] { fun a = false } //│ ║ ^^^ //│ ╟── from signature of member `a`: -//│ ║ l.293: trait MyTrait[A] { fun a: A } +//│ ║ l.290: trait MyTrait[A] { fun a: A } //│ ╙── ^^^^ //│ class C extends MyTrait { //│ constructor() @@ -361,19 +358,19 @@ class C3 extends T4{ fun bar = false } //│ ╔══[ERROR] Type mismatch in definition of method foo: -//│ ║ l.360: fun foo = 3 +//│ ║ l.357: fun foo = 3 //│ ║ ^^^^^^^ //│ ╟── integer literal of type `3` does not match type `2` -//│ ║ l.360: fun foo = 3 +//│ ║ l.357: fun foo = 3 //│ ║ ^ //│ ╟── but it flows into definition of method foo with expected type `2` -//│ ║ l.360: fun foo = 3 +//│ ║ l.357: fun foo = 3 //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from literal type: -//│ ║ l.348: fun foo: 2 +//│ ║ l.345: fun foo: 2 //│ ║ ^ //│ ╟── from signature of member `foo`: -//│ ║ l.348: fun foo: 2 +//│ ║ l.345: fun foo: 2 //│ ╙── ^^^^^^ //│ class C3 extends T1, T2, T4 { //│ constructor() @@ -384,24 +381,24 @@ class C3 extends T4{ :e class C2(foo: Int, bar: Str) extends T4 //│ ╔══[ERROR] Type mismatch in type reference: -//│ ║ l.385: class C2(foo: Int, bar: Str) extends T4 +//│ ║ l.382: class C2(foo: Int, bar: Str) extends T4 //│ ║ ^^^ //│ ╟── type `Int` does not match type `2` //│ ╟── Note: constraint arises from literal type: -//│ ║ l.348: fun foo: 2 +//│ ║ l.345: fun foo: 2 //│ ║ ^ //│ ╟── from signature of member `foo`: -//│ ║ l.348: fun foo: 2 +//│ ║ l.345: fun foo: 2 //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type reference: -//│ ║ l.385: class C2(foo: Int, bar: Str) extends T4 +//│ ║ l.382: class C2(foo: Int, bar: Str) extends T4 //│ ║ ^^^ //│ ╟── type `Str` does not match type `Int | false | true` //│ ╟── Note: constraint arises from union type: -//│ ║ l.336: let bar : Int | Bool +//│ ║ l.333: let bar : Int | Bool //│ ║ ^^^^^^^^^^ //│ ╟── from signature of member `bar`: -//│ ║ l.336: let bar : Int | Bool +//│ ║ l.333: let bar : Int | Bool //│ ╙── ^^^^^^^^^^^^^^^^ //│ class C2(foo: Int, bar: Str) extends T1, T2, T4 @@ -410,19 +407,19 @@ trait T5 extends T4 { let foo: 4 } //│ ╔══[ERROR] Type mismatch in signature of member `foo`: -//│ ║ l.410: let foo: 4 +//│ ║ l.407: let foo: 4 //│ ║ ^^^^^^ //│ ╟── type `4` does not match type `2` -//│ ║ l.410: let foo: 4 +//│ ║ l.407: let foo: 4 //│ ║ ^ //│ ╟── but it flows into signature of member `foo` with expected type `2` -//│ ║ l.410: let foo: 4 +//│ ║ l.407: let foo: 4 //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from literal type: -//│ ║ l.348: fun foo: 2 +//│ ║ l.345: fun foo: 2 //│ ║ ^ //│ ╟── from signature of member `foo`: -//│ ║ l.348: fun foo: 2 +//│ ║ l.345: fun foo: 2 //│ ╙── ^^^^^^ //│ trait T5 extends T1, T2, T4 { //│ fun bar: Bool @@ -434,34 +431,34 @@ trait T3 extends T1, T2 { let foo: true } //│ ╔══[ERROR] Type mismatch in signature of member `foo`: -//│ ║ l.434: let foo: true +//│ ║ l.431: let foo: true //│ ║ ^^^^^^^^^ //│ ╟── type `true` does not match type `1 | 2 | 3` -//│ ║ l.434: let foo: true +//│ ║ l.431: let foo: true //│ ║ ^^^^ //│ ╟── but it flows into signature of member `foo` with expected type `1 | 2 | 3` -//│ ║ l.434: let foo: true +//│ ║ l.431: let foo: true //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from union type: -//│ ║ l.331: let foo : 1 | 2 | 3 +//│ ║ l.328: let foo : 1 | 2 | 3 //│ ║ ^^^^^^^^^ //│ ╟── from signature of member `foo`: -//│ ║ l.331: let foo : 1 | 2 | 3 +//│ ║ l.328: let foo : 1 | 2 | 3 //│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in signature of member `foo`: -//│ ║ l.434: let foo: true +//│ ║ l.431: let foo: true //│ ║ ^^^^^^^^^ //│ ╟── type `true` does not match type `2 | 3 | 4` -//│ ║ l.434: let foo: true +//│ ║ l.431: let foo: true //│ ║ ^^^^ //│ ╟── but it flows into signature of member `foo` with expected type `2 | 3 | 4` -//│ ║ l.434: let foo: true +//│ ║ l.431: let foo: true //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from union type: -//│ ║ l.335: let foo : 2 | 3 | 4 +//│ ║ l.332: let foo : 2 | 3 | 4 //│ ║ ^^^^^^^^^ //│ ╟── from signature of member `foo`: -//│ ║ l.335: let foo : 2 | 3 | 4 +//│ ║ l.332: let foo : 2 | 3 | 4 //│ ╙── ^^^^^^^^^^^^^^^ //│ trait T3 extends T1, T2 { //│ fun bar: Bool diff --git a/shared/src/test/diff/nu/Interfaces.mls b/shared/src/test/diff/nu/Interfaces.mls index 11ef7fd1..fe76a104 100644 --- a/shared/src/test/diff/nu/Interfaces.mls +++ b/shared/src/test/diff/nu/Interfaces.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs trait Test { @@ -531,9 +531,6 @@ if b is :e let tt1 = Test -//│ ╔══[ERROR] identifier `Test` is resolved to a type -//│ ║ l.533: let tt1 = Test -//│ ╙── ^^^^ //│ ╔══[ERROR] trait Test cannot be used in term position //│ ║ l.533: let tt1 = Test //│ ╙── ^^^^ @@ -606,40 +603,40 @@ z: WP g: ZL e: ZL & WP //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.604: fun fto(w: WP): EM = w +//│ ║ l.601: fun fto(w: WP): EM = w //│ ║ ^ //│ ╟── type `#WP` is not an instance of type `EM` -//│ ║ l.604: fun fto(w: WP): EM = w +//│ ║ l.601: fun fto(w: WP): EM = w //│ ║ ^^ //│ ╟── but it flows into reference with expected type `#EM` -//│ ║ l.604: fun fto(w: WP): EM = w +//│ ║ l.601: fun fto(w: WP): EM = w //│ ║ ^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.604: fun fto(w: WP): EM = w +//│ ║ l.601: fun fto(w: WP): EM = w //│ ╙── ^^ //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.605: z: WP +//│ ║ l.602: z: WP //│ ║ ^ //│ ╟── type `#ZL` is not an instance of type `WP` -//│ ║ l.561: let z: ZL +//│ ║ l.558: let z: ZL //│ ║ ^^ //│ ╟── but it flows into reference with expected type `#WP` -//│ ║ l.605: z: WP +//│ ║ l.602: z: WP //│ ║ ^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.605: z: WP +//│ ║ l.602: z: WP //│ ╙── ^^ //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.606: g: ZL +//│ ║ l.603: g: ZL //│ ║ ^ //│ ╟── type `#Geo` is not an instance of type `ZL` -//│ ║ l.560: let g: Geo +//│ ║ l.557: let g: Geo //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `#ZL` -//│ ║ l.606: g: ZL +//│ ║ l.603: g: ZL //│ ║ ^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.606: g: ZL +//│ ║ l.603: g: ZL //│ ╙── ^^ //│ fun fto: (w: WP) -> EM //│ WP & ZL @@ -695,36 +692,36 @@ class Eh2 extends Bs(true), Ele { fun ce(x) = x } //│ ╔══[ERROR] Type mismatch in definition of method foo: -//│ ║ l.694: fun foo(x) = x && false +//│ ║ l.691: fun foo(x) = x && false //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `Int & ?a` is not an instance of type `Bool` //│ ╟── Note: constraint arises from reference: -//│ ║ l.694: fun foo(x) = x && false +//│ ║ l.691: fun foo(x) = x && false //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition of method foo: -//│ ║ l.694: fun foo(x) = x && false +//│ ║ l.691: fun foo(x) = x && false //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `Int & ?a` does not match type `Bool` //│ ╟── Note: constraint arises from reference: -//│ ║ l.694: fun foo(x) = x && false +//│ ║ l.691: fun foo(x) = x && false //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition of method foo: -//│ ║ l.694: fun foo(x) = x && false +//│ ║ l.691: fun foo(x) = x && false //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `Bool` does not match type `Int | ?a` -//│ ║ l.694: fun foo(x) = x && false +//│ ║ l.691: fun foo(x) = x && false //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from operator application: -//│ ║ l.657: virtual fun foo(x) = x + 1 +//│ ║ l.654: virtual fun foo(x) = x + 1 //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in definition of method foo: -//│ ║ l.694: fun foo(x) = x && false +//│ ║ l.691: fun foo(x) = x && false //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `Bool` does not match type `Int | ?a` -//│ ║ l.694: fun foo(x) = x && false +//│ ║ l.691: fun foo(x) = x && false //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from operator application: -//│ ║ l.657: virtual fun foo(x) = x + 1 +//│ ║ l.654: virtual fun foo(x) = x + 1 //│ ╙── ^^^^^ //│ class Eh2 extends Bs, Ele { //│ constructor() @@ -737,34 +734,34 @@ class Eh extends Bs(1) class Eh1 extends Bs class Eh3 extends Bs(false), Test //│ ╔══[ERROR] Type mismatch in type declaration: -//│ ║ l.736: class Eh extends Bs(1) +//│ ║ l.733: class Eh extends Bs(1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `1` is not an instance of type `Bool` -//│ ║ l.736: class Eh extends Bs(1) +//│ ║ l.733: class Eh extends Bs(1) //│ ║ ^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.656: class Bs(val a: Bool) { +//│ ║ l.653: class Bs(val a: Bool) { //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in type declaration: -//│ ║ l.736: class Eh extends Bs(1) +//│ ║ l.733: class Eh extends Bs(1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `1` does not match type `Bool` -//│ ║ l.736: class Eh extends Bs(1) +//│ ║ l.733: class Eh extends Bs(1) //│ ║ ^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.656: class Bs(val a: Bool) { +//│ ║ l.653: class Bs(val a: Bool) { //│ ╙── ^^^^ //│ ╔══[ERROR] class Bs expects 1 parameter(s); got 0 -//│ ║ l.737: class Eh1 extends Bs +//│ ║ l.734: class Eh1 extends Bs //│ ╙── ^^ //│ ╔══[ERROR] Type mismatch in definition of method foo: -//│ ║ l.657: virtual fun foo(x) = x + 1 +//│ ║ l.654: virtual fun foo(x) = x + 1 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b. ?b)` is not an instance of type `Int` -//│ ║ l.657: virtual fun foo(x) = x + 1 +//│ ║ l.654: virtual fun foo(x) = x + 1 //│ ║ ^^^^^^^^^^^ //│ ╟── but it flows into definition of method foo with expected type `Int` -//│ ║ l.657: virtual fun foo(x) = x + 1 +//│ ║ l.654: virtual fun foo(x) = x + 1 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.5: fun foo: Int @@ -773,7 +770,7 @@ class Eh3 extends Bs(false), Test //│ ║ l.5: fun foo: Int //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Member `bar` is declared (or its declaration is inherited) but is not implemented in `Eh3` -//│ ║ l.738: class Eh3 extends Bs(false), Test +//│ ║ l.735: class Eh3 extends Bs(false), Test //│ ║ ^^^ //│ ╟── Declared here: //│ ║ l.6: fun bar: Bool -> Bool @@ -851,7 +848,7 @@ abstract class Bc3 { :e class Bc12() extends Bc1(1), Bc2(true) //│ ╔══[ERROR] Cannot inherit from more than one base class: Bc1 and Bc2 -//│ ║ l.852: class Bc12() extends Bc1(1), Bc2(true) +//│ ║ l.849: class Bc12() extends Bc1(1), Bc2(true) //│ ╙── ^^^^^^^^^ //│ class Bc12() extends Bc1, Bc2 //│ Code generation encountered an error: @@ -872,24 +869,24 @@ Bc02().foo :e class Bc31(baz: Bool) extends Bc3 //│ ╔══[ERROR] Type mismatch in type reference: -//│ ║ l.873: class Bc31(baz: Bool) extends Bc3 +//│ ║ l.870: class Bc31(baz: Bool) extends Bc3 //│ ║ ^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ╟── Note: constraint arises from type reference: -//│ ║ l.843: let baz : Int +//│ ║ l.840: let baz : Int //│ ║ ^^^ //│ ╟── from signature of member `baz`: -//│ ║ l.843: let baz : Int +//│ ║ l.840: let baz : Int //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in type reference: -//│ ║ l.873: class Bc31(baz: Bool) extends Bc3 +//│ ║ l.870: class Bc31(baz: Bool) extends Bc3 //│ ║ ^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ╟── Note: constraint arises from type reference: -//│ ║ l.843: let baz : Int +//│ ║ l.840: let baz : Int //│ ║ ^^^ //│ ╟── from signature of member `baz`: -//│ ║ l.843: let baz : Int +//│ ║ l.840: let baz : Int //│ ╙── ^^^^^^^^^ //│ class Bc31(baz: Bool) extends Bc3 @@ -924,7 +921,7 @@ trait BInt extends Base[Int] { fun f = error } //│ ╔══[ERROR] Method implementations in traits are not yet supported -//│ ║ l.924: fun f = error +//│ ║ l.921: fun f = error //│ ╙── ^^^^^^^^^^^^^ //│ trait BInt extends Base { //│ fun f: nothing @@ -955,7 +952,7 @@ bp: Base[[Int, Bool]] :e bp: Base[[Int, Int]] //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.956: bp: Base[[Int, Int]] +//│ ║ l.953: bp: Base[[Int, Int]] //│ ║ ^^ //│ ╙── expression of type `true` is not an instance of type `Int` //│ Base[[Int, Int]] @@ -1016,13 +1013,13 @@ trait BInfer2 extends Base { :e class DerBad1 extends Base[Int, Int] //│ ╔══[ERROR] trait Base expects 1 type parameter(s); got 2 -//│ ║ l.1017: class DerBad1 extends Base[Int, Int] +//│ ║ l.1014: class DerBad1 extends Base[Int, Int] //│ ╙── ^^^^^^^^^^^^^ //│ ╔══[ERROR] Member `f` is declared (or its declaration is inherited) but is not implemented in `DerBad1` -//│ ║ l.1017: class DerBad1 extends Base[Int, Int] +//│ ║ l.1014: class DerBad1 extends Base[Int, Int] //│ ║ ^^^^^^^ //│ ╟── Declared here: -//│ ║ l.905: trait Base[A] { fun f: A -> A } +//│ ║ l.902: trait Base[A] { fun f: A -> A } //│ ╙── ^^^^^^^^^^^^^ //│ class DerBad1 extends Base { //│ constructor() @@ -1034,28 +1031,28 @@ class DerBad1 extends Base[Int, Int] :e class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ╔══[ERROR] Type mismatch in definition of method f: -//│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } +//│ ║ l.1032: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `B` does not match type `A` -//│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } +//│ ║ l.1032: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ║ ^ //│ ╟── Note: constraint arises from type parameter: -//│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } +//│ ║ l.1032: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ║ ^ //│ ╟── Note: type parameter B is defined at: -//│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } +//│ ║ l.1032: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition of method f: -//│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } +//│ ║ l.1032: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `A` does not match type `B` -//│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } +//│ ║ l.1032: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ║ ^ //│ ╟── Note: constraint arises from type parameter: -//│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } +//│ ║ l.1032: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ║ ^ //│ ╟── Note: type parameter A is defined at: -//│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } +//│ ║ l.1032: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ╙── ^ //│ class Der2[A, B] extends Base { //│ constructor() @@ -1107,7 +1104,7 @@ trait Tb extends Ta[Int] { virtual val p = false } //│ ╔══[ERROR] Method implementations in traits are not yet supported -//│ ║ l.1107: virtual val p = false +//│ ║ l.1104: virtual val p = false //│ ╙── ^^^^^^^^^^^^^ //│ trait Tb extends Ta { //│ val g: 'T @@ -1142,24 +1139,24 @@ trait Oz { :e class Fischl(age: Bool) extends Oz //│ ╔══[ERROR] Type mismatch in type reference: -//│ ║ l.1143: class Fischl(age: Bool) extends Oz +//│ ║ l.1140: class Fischl(age: Bool) extends Oz //│ ║ ^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ╟── Note: constraint arises from type reference: -//│ ║ l.1136: let age: Int +//│ ║ l.1133: let age: Int //│ ║ ^^^ //│ ╟── from signature of member `age`: -//│ ║ l.1136: let age: Int +//│ ║ l.1133: let age: Int //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in type reference: -//│ ║ l.1143: class Fischl(age: Bool) extends Oz +//│ ║ l.1140: class Fischl(age: Bool) extends Oz //│ ║ ^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ╟── Note: constraint arises from type reference: -//│ ║ l.1136: let age: Int +//│ ║ l.1133: let age: Int //│ ║ ^^^ //│ ╟── from signature of member `age`: -//│ ║ l.1136: let age: Int +//│ ║ l.1133: let age: Int //│ ╙── ^^^^^^^^ //│ class Fischl(age: Bool) extends Oz @@ -1179,36 +1176,36 @@ class Go extends Fate { fun foo(x) = x && true } //│ ╔══[ERROR] Type mismatch in definition of method foo: -//│ ║ l.1179: fun foo(x) = x && true +//│ ║ l.1176: fun foo(x) = x && true //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `Int & ?a` is not an instance of type `Bool` //│ ╟── Note: constraint arises from reference: -//│ ║ l.1179: fun foo(x) = x && true +//│ ║ l.1176: fun foo(x) = x && true //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition of method foo: -//│ ║ l.1179: fun foo(x) = x && true +//│ ║ l.1176: fun foo(x) = x && true //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `Int & ?a` does not match type `Bool` //│ ╟── Note: constraint arises from reference: -//│ ║ l.1179: fun foo(x) = x && true +//│ ║ l.1176: fun foo(x) = x && true //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition of method foo: -//│ ║ l.1179: fun foo(x) = x && true +//│ ║ l.1176: fun foo(x) = x && true //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `Bool` does not match type `Int | ?a` -//│ ║ l.1179: fun foo(x) = x && true +//│ ║ l.1176: fun foo(x) = x && true //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from operator application: -//│ ║ l.1170: virtual fun foo(x) = x + 1 +//│ ║ l.1167: virtual fun foo(x) = x + 1 //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in definition of method foo: -//│ ║ l.1179: fun foo(x) = x && true +//│ ║ l.1176: fun foo(x) = x && true //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `Bool` does not match type `Int | ?a` -//│ ║ l.1179: fun foo(x) = x && true +//│ ║ l.1176: fun foo(x) = x && true //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from operator application: -//│ ║ l.1170: virtual fun foo(x) = x + 1 +//│ ║ l.1167: virtual fun foo(x) = x + 1 //│ ╙── ^^^^^ //│ class Go extends Fate { //│ constructor() @@ -1227,18 +1224,18 @@ class Haha(x: 1 | 2) extends Ha :e class Ohhh(x: Bool) extends Ha //│ ╔══[ERROR] Type mismatch in type reference: -//│ ║ l.1228: class Ohhh(x: Bool) extends Ha +//│ ║ l.1225: class Ohhh(x: Bool) extends Ha //│ ║ ^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ╟── Note: constraint arises from type reference: -//│ ║ l.1218: class Ha { virtual val x: Int = 1 } +//│ ║ l.1215: class Ha { virtual val x: Int = 1 } //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in type reference: -//│ ║ l.1228: class Ohhh(x: Bool) extends Ha +//│ ║ l.1225: class Ohhh(x: Bool) extends Ha //│ ║ ^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ╟── Note: constraint arises from type reference: -//│ ║ l.1218: class Ha { virtual val x: Int = 1 } +//│ ║ l.1215: class Ha { virtual val x: Int = 1 } //│ ╙── ^^^ //│ class Ohhh(x: Bool) extends Ha diff --git a/shared/src/test/diff/nu/LetRec.mls b/shared/src/test/diff/nu/LetRec.mls index cb0176f7..1ff16fa4 100644 --- a/shared/src/test/diff/nu/LetRec.mls +++ b/shared/src/test/diff/nu/LetRec.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs :js @@ -145,9 +145,6 @@ let rec f = // :e // FIXME :ge let foo = foo -//│ ╔══[ERROR] identifier `foo` not found -//│ ║ l.147: let foo = foo -//│ ╙── ^^^ //│ let foo: nothing //│ Code generation encountered an error: //│ unguarded recursive use of by-value binding foo diff --git a/shared/src/test/diff/nu/ListConsNil.mls b/shared/src/test/diff/nu/ListConsNil.mls index d705f6d2..da5ad89b 100644 --- a/shared/src/test/diff/nu/ListConsNil.mls +++ b/shared/src/test/diff/nu/ListConsNil.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs diff --git a/shared/src/test/diff/nu/LitMatch.mls b/shared/src/test/diff/nu/LitMatch.mls index ab8f51e8..45581782 100644 --- a/shared/src/test/diff/nu/LitMatch.mls +++ b/shared/src/test/diff/nu/LitMatch.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs let r = false : Bool diff --git a/shared/src/test/diff/nu/MissingTypeArg.mls b/shared/src/test/diff/nu/MissingTypeArg.mls index fe7011e8..1eba54d9 100644 --- a/shared/src/test/diff/nu/MissingTypeArg.mls +++ b/shared/src/test/diff/nu/MissingTypeArg.mls @@ -1,6 +1,6 @@ // * This is an example program where the error we get is really not ideal -:PreTyper +:NewDefs // * An example recursive definition: diff --git a/shared/src/test/diff/nu/NamedArgs.mls b/shared/src/test/diff/nu/NamedArgs.mls index 4933c1d6..8c4d4606 100644 --- a/shared/src/test/diff/nu/NamedArgs.mls +++ b/shared/src/test/diff/nu/NamedArgs.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs fun test(x: 'a) = if x is undefined then 0 else x + 1 @@ -150,12 +150,6 @@ fun fff(x: Int, y: Int, z: Int) = (x - y) * z // * Testing renaming :e fff(y: 2, z: y_1 + 1, x: z_1 - 2) -//│ ╔══[ERROR] identifier `y_1` not found -//│ ║ l.152: fff(y: 2, z: y_1 + 1, x: z_1 - 2) -//│ ╙── ^^^ -//│ ╔══[ERROR] identifier `z_1` not found -//│ ║ l.152: fff(y: 2, z: y_1 + 1, x: z_1 - 2) -//│ ╙── ^^^ //│ ╔══[ERROR] identifier not found: y_1 //│ ║ l.152: fff(y: 2, z: y_1 + 1, x: z_1 - 2) //│ ╙── ^^^ @@ -239,7 +233,7 @@ fun print(x) = (y, z) => log([x, y, z]) let p = print(0) p(z: 1, y: 2) //│ ╔══[ERROR] Cannot use named arguments as the function type has untyped arguments -//│ ║ l.240: p(z: 1, y: 2) +//│ ║ l.234: p(z: 1, y: 2) //│ ╙── ^^^^^^^^^^^^ //│ fun print: anything -> (anything, anything) -> () //│ let p: (anything, anything) -> () @@ -294,11 +288,8 @@ z.y :e (f => f(x: a)) -//│ ╔══[ERROR] identifier `a` not found -//│ ║ l.296: (f => f(x: a)) -//│ ╙── ^ //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments -//│ ║ l.296: (f => f(x: a)) +//│ ║ l.290: (f => f(x: a)) //│ ╙── ^ //│ anything -> error //│ Code generation encountered an error: @@ -306,11 +297,8 @@ z.y :e (f => f)(error)(x: a) -//│ ╔══[ERROR] identifier `a` not found -//│ ║ l.308: (f => f)(error)(x: a) -//│ ╙── ^ //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments -//│ ║ l.308: (f => f)(error)(x: a) +//│ ║ l.299: (f => f)(error)(x: a) //│ ╙── ^^^^^^^^^^^^^^^ //│ error //│ Code generation encountered an error: @@ -318,11 +306,8 @@ z.y :e (f => f)(42)(x: a) -//│ ╔══[ERROR] identifier `a` not found -//│ ║ l.320: (f => f)(42)(x: a) -//│ ╙── ^ //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments -//│ ║ l.320: (f => f)(42)(x: a) +//│ ║ l.308: (f => f)(42)(x: a) //│ ╙── ^^^^^^^^^^^^ //│ error //│ Code generation encountered an error: @@ -330,11 +315,8 @@ z.y :e (f => f)(if true then 123 else false)(x: a) -//│ ╔══[ERROR] identifier `a` not found -//│ ║ l.332: (f => f)(if true then 123 else false)(x: a) -//│ ╙── ^ //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments -//│ ║ l.332: (f => f)(if true then 123 else false)(x: a) +//│ ║ l.317: (f => f)(if true then 123 else false)(x: a) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ error //│ Code generation encountered an error: @@ -348,7 +330,7 @@ z.y :e (f => if true then f else id)(if true then (x: Int) => x + 1 else id)(x: 123) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments -//│ ║ l.349: (f => if true then f else id)(if true then (x: Int) => x + 1 else id)(x: 123) +//│ ║ l.331: (f => if true then f else id)(if true then (x: Int) => x + 1 else id)(x: 123) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ error //│ res @@ -357,7 +339,7 @@ z.y :e (f => if true then f else id)(if true then (x: Int) => x + 1 else (x: Int) => x + 1)(x: 123) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments -//│ ║ l.358: (f => if true then f else id)(if true then (x: Int) => x + 1 else (x: Int) => x + 1)(x: 123) +//│ ║ l.340: (f => if true then f else id)(if true then (x: Int) => x + 1 else (x: Int) => x + 1)(x: 123) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ error //│ res @@ -379,7 +361,7 @@ foo((x: Int) => 1) :e fun foo(f: ((x: Int) => Int) | 'a) = f(x: 123) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `'a | (x: Int) -> Int` for applying named arguments -//│ ║ l.380: fun foo(f: ((x: Int) => Int) | 'a) = f(x: 123) +//│ ║ l.362: fun foo(f: ((x: Int) => Int) | 'a) = f(x: 123) //│ ╙── ^ //│ fun foo: (f: anything) -> error @@ -388,7 +370,7 @@ fun foo(f: ((x: Int) => Int) | 'a) = f(x: 123) :e fun foo(x) = (if true then (x: Int) => x + 1 else x)(x: 123) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `(x: Int) -> ?a | ?b` for applying named arguments -//│ ║ l.389: fun foo(x) = (if true then (x: Int) => x + 1 else x)(x: 123) +//│ ║ l.371: fun foo(x) = (if true then (x: Int) => x + 1 else x)(x: 123) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ fun foo: anything -> error @@ -401,7 +383,7 @@ foo((y: Int) => y) :e // TODO later: this could be made to work... fun foo(x) = (if true then (x: Int) => x + 1 else (x: Int) => x + 1)(x: 123) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `(x: Int) -> (?a | ?b)` for applying named arguments -//│ ║ l.402: fun foo(x) = (if true then (x: Int) => x + 1 else (x: Int) => x + 1)(x: 123) +//│ ║ l.384: fun foo(x) = (if true then (x: Int) => x + 1 else (x: Int) => x + 1)(x: 123) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ fun foo: anything -> error @@ -412,7 +394,7 @@ fun foo(x) = if true then (x: Int) => x + 1 else x :e foo((y: Int) => y)(x: 123) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments -//│ ║ l.413: foo((y: Int) => y)(x: 123) +//│ ║ l.395: foo((y: Int) => y)(x: 123) //│ ╙── ^^^^^^^^^^^^^^^^^^ //│ error //│ res @@ -421,7 +403,7 @@ foo((y: Int) => y)(x: 123) :e foo((x: Int) => x - 1)(x: 123) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments -//│ ║ l.422: foo((x: Int) => x - 1)(x: 123) +//│ ║ l.404: foo((x: Int) => x - 1)(x: 123) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ //│ error //│ res @@ -434,7 +416,7 @@ fun foo1(x) = [x + 1, x] :e foo1(x: 123) //│ ╔══[ERROR] Cannot use named arguments as the function type has untyped arguments -//│ ║ l.435: foo1(x: 123) +//│ ║ l.417: foo1(x: 123) //│ ╙── ^^^^^^^^ //│ error //│ res diff --git a/shared/src/test/diff/nu/New.mls b/shared/src/test/diff/nu/New.mls index faae55db..470f177c 100644 --- a/shared/src/test/diff/nu/New.mls +++ b/shared/src/test/diff/nu/New.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Foo[A](x: A) diff --git a/shared/src/test/diff/nu/NewNew.mls b/shared/src/test/diff/nu/NewNew.mls index 5cfa6d96..a2e039d7 100644 --- a/shared/src/test/diff/nu/NewNew.mls +++ b/shared/src/test/diff/nu/NewNew.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Foo(val x: Int) @@ -183,9 +183,6 @@ type T = PoInt[Str] :e new T(0, 0) -//│ ╔══[ERROR] identifier `T` is resolved to a type -//│ ║ l.185: new T(0, 0) -//│ ╙── ^ //│ ╔══[ERROR] Type alias T cannot be used in `new` expression //│ ║ l.185: new T(0, 0) //│ ╙── ^^^^^^^^^^^ @@ -201,7 +198,7 @@ let origin = new PoInt(0, 0) :e // TODO support let origin = PoInt[Int](0, 0) //│ ╔══[ERROR] Type application syntax is not yet supported -//│ ║ l.202: let origin = PoInt[Int](0, 0) +//│ ║ l.199: let origin = PoInt[Int](0, 0) //│ ╙── ^^^^^^^^^^ //│ let origin: PoInt[0] //│ origin @@ -210,7 +207,7 @@ let origin = PoInt[Int](0, 0) :e // TODO support let origin = new PoInt[Int](0, 0) //│ ╔══[ERROR] Type arguments in `new` expressions are not yet supported -//│ ║ l.211: let origin = new PoInt[Int](0, 0) +//│ ║ l.208: let origin = new PoInt[Int](0, 0) //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ let origin: PoInt[0] //│ Code generation encountered an error: @@ -220,7 +217,7 @@ let origin = new PoInt[Int](0, 0) :e // TODO support new {} //│ ╔══[ERROR] Unexpected type `anything` after `new` keyword -//│ ║ l.221: new {} +//│ ║ l.218: new {} //│ ╙── ^^ //│ error //│ Code generation encountered an error: @@ -230,10 +227,10 @@ new {} :e new //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here -//│ ║ l.231: new +//│ ║ l.228: new //│ ╙── ^ //│ ╔══[ERROR] Unexpected type `()` after `new` keyword -//│ ║ l.231: new +//│ ║ l.228: new //│ ╙── ^ //│ error //│ Code generation encountered an error: @@ -244,14 +241,11 @@ new :e new x: 0 -//│ ╔══[ERROR] identifier `x` not found -//│ ║ l.246: x: 0 -//│ ╙── ^ //│ ╔══[PARSE ERROR] Not a recognized type -//│ ║ l.246: x: 0 +//│ ║ l.243: x: 0 //│ ╙── ^ //│ ╔══[ERROR] Unexpected type `nothing` after `new` keyword -//│ ║ l.246: x: 0 +//│ ║ l.243: x: 0 //│ ╙── ^ //│ error //│ Code generation encountered an error: @@ -266,7 +260,7 @@ fun f(x) = {x} :e new f(1) //│ ╔══[ERROR] type identifier not found: f -//│ ║ l.267: new f(1) +//│ ║ l.261: new f(1) //│ ╙── ^ //│ error //│ res @@ -279,7 +273,7 @@ module Oops :e new Oops //│ ╔══[ERROR] Module Oops cannot be used in `new` expression -//│ ║ l.280: new Oops +//│ ║ l.274: new Oops //│ ╙── ^^^^ //│ error //│ res @@ -289,11 +283,8 @@ new Oops :e new Oops2 trait Oops2 -//│ ╔══[ERROR] identifier `Oops2` is resolved to a type -//│ ║ l.290: new Oops2 -//│ ╙── ^^^^^ //│ ╔══[ERROR] Trait Oops2 cannot be used in `new` expression -//│ ║ l.290: new Oops2 +//│ ║ l.284: new Oops2 //│ ╙── ^^^^^ //│ trait Oops2 //│ error diff --git a/shared/src/test/diff/nu/Object.mls b/shared/src/test/diff/nu/Object.mls index de484994..9e93dec2 100644 --- a/shared/src/test/diff/nu/Object.mls +++ b/shared/src/test/diff/nu/Object.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs @@ -88,9 +88,6 @@ fun foo = forall 'a; (x: 'a) => if x is A then true else false :e Object -//│ ╔══[ERROR] identifier `Object` not found -//│ ║ l.90: Object -//│ ╙── ^^^^^^ //│ ╔══[ERROR] Class Object is abstract and cannot be instantiated //│ ║ l.90: Object //│ ╙── ^^^^^^ @@ -103,14 +100,11 @@ Object :e Object() -//│ ╔══[ERROR] identifier `Object` not found -//│ ║ l.105: Object() -//│ ╙── ^^^^^^ //│ ╔══[ERROR] Class Object is abstract and cannot be instantiated -//│ ║ l.105: Object() +//│ ║ l.102: Object() //│ ╙── ^^^^^^ //│ ╔══[ERROR] Class Object cannot be instantiated as it exposes no constructor -//│ ║ l.105: Object() +//│ ║ l.102: Object() //│ ╙── ^^^^^^ //│ error //│ Code generation encountered an error: @@ -118,11 +112,8 @@ Object() :e new Object -//│ ╔══[ERROR] identifier `Object` not found -//│ ║ l.120: new Object -//│ ╙── ^^^^^^ //│ ╔══[ERROR] Class Object is abstract and cannot be instantiated -//│ ║ l.120: new Object +//│ ║ l.114: new Object //│ ╙── ^^^^^^ //│ Object //│ Code generation encountered an error: @@ -132,9 +123,6 @@ new Object // TODO class B() extends Object -//│ ╔══[ERROR] could not find definition `Object` -//│ ║ l.134: class B() extends Object -//│ ╙── ^^^^^^ //│ class B() extends Object //│ Code generation encountered an error: //│ unresolved parent Object. diff --git a/shared/src/test/diff/nu/OpLam.mls b/shared/src/test/diff/nu/OpLam.mls index 78ce0e69..bedc2cd4 100644 --- a/shared/src/test/diff/nu/OpLam.mls +++ b/shared/src/test/diff/nu/OpLam.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs x => x is 42 @@ -105,12 +105,6 @@ x => x + 2 :e x => x.y => y -//│ ╔══[ERROR] unsupported pattern shape -//│ ║ l.107: x => x.y => y -//│ ╙── ^^^ -//│ ╔══[ERROR] identifier `y` not found -//│ ║ l.107: x => x.y => y -//│ ╙── ^ //│ ╔══[ERROR] Unsupported pattern shape: //│ ║ l.107: x => x.y => y //│ ╙── ^^^ diff --git a/shared/src/test/diff/nu/OptionFilter.mls b/shared/src/test/diff/nu/OptionFilter.mls index 31879983..d27e8492 100644 --- a/shared/src/test/diff/nu/OptionFilter.mls +++ b/shared/src/test/diff/nu/OptionFilter.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs // * Minimization of code that used to cause a problem: diff --git a/shared/src/test/diff/nu/OverrideShorthand.mls b/shared/src/test/diff/nu/OverrideShorthand.mls index e9e61e59..2bd82353 100644 --- a/shared/src/test/diff/nu/OverrideShorthand.mls +++ b/shared/src/test/diff/nu/OverrideShorthand.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs diff --git a/shared/src/test/diff/nu/ParamPassing.mls b/shared/src/test/diff/nu/ParamPassing.mls index 0c08b497..0fc6a4af 100644 --- a/shared/src/test/diff/nu/ParamPassing.mls +++ b/shared/src/test/diff/nu/ParamPassing.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Foo(x: Int) @@ -66,12 +66,6 @@ Foo(1).x :e Foo(1).#x -//│ ╔══[ERROR] identifier `.#` not found -//│ ║ l.68: Foo(1).#x -//│ ╙── ^^ -//│ ╔══[ERROR] identifier `x` not found -//│ ║ l.68: Foo(1).#x -//│ ╙── ^ //│ ╔══[ERROR] identifier not found: .# //│ ║ l.68: Foo(1).#x //│ ╙── ^^ @@ -105,20 +99,20 @@ if Foo(1) is Foo(x) then x :e class Bar(x: Int) extends Foo(x) //│ ╔══[ERROR] Inherited parameter named `x` is not virtual and cannot be overridden -//│ ║ l.106: class Bar(x: Int) extends Foo(x) +//│ ║ l.100: class Bar(x: Int) extends Foo(x) //│ ║ ^ //│ ╟── Originally declared here: -//│ ║ l.91: class Foo(val x: Int) +//│ ║ l.85: class Foo(val x: Int) //│ ╙── ^ //│ class Bar(x: Int) extends Foo :e Bar(11).x //│ ╔══[ERROR] Parameter 'x' cannot tbe accessed as a field -//│ ║ l.116: Bar(11).x +//│ ║ l.110: Bar(11).x //│ ║ ^^ //│ ╟── Either make the parameter a `val` or access it through destructuring -//│ ║ l.106: class Bar(x: Int) extends Foo(x) +//│ ║ l.100: class Bar(x: Int) extends Foo(x) //│ ╙── ^ //│ Int | error //│ res @@ -128,10 +122,10 @@ Bar(11).x :e class Bar(val x: Int) extends Foo(x + 1) //│ ╔══[ERROR] Inherited parameter named `x` is not virtual and cannot be overridden -//│ ║ l.129: class Bar(val x: Int) extends Foo(x + 1) +//│ ║ l.123: class Bar(val x: Int) extends Foo(x + 1) //│ ║ ^ //│ ╟── Originally declared here: -//│ ║ l.91: class Foo(val x: Int) +//│ ║ l.85: class Foo(val x: Int) //│ ╙── ^ //│ class Bar(x: Int) extends Foo @@ -143,10 +137,10 @@ Bar(11).x :e class Bar extends Foo(1) { val x: 2 } //│ ╔══[ERROR] Inherited parameter named `x` is not virtual and cannot be overridden -//│ ║ l.144: class Bar extends Foo(1) { val x: 2 } +//│ ║ l.138: class Bar extends Foo(1) { val x: 2 } //│ ║ ^^^^^^^^ //│ ╟── Originally declared here: -//│ ║ l.91: class Foo(val x: Int) +//│ ║ l.85: class Foo(val x: Int) //│ ╙── ^ //│ class Bar extends Foo { //│ constructor() @@ -156,10 +150,10 @@ class Bar extends Foo(1) { val x: 2 } :e module Bar extends Foo(1) { fun x = 2 } //│ ╔══[ERROR] Inherited parameter named `x` is not virtual and cannot be overridden -//│ ║ l.157: module Bar extends Foo(1) { fun x = 2 } +//│ ║ l.151: module Bar extends Foo(1) { fun x = 2 } //│ ║ ^^^^^ //│ ╟── Originally declared here: -//│ ║ l.91: class Foo(val x: Int) +//│ ║ l.85: class Foo(val x: Int) //│ ╙── ^ //│ module Bar extends Foo { //│ fun x: 2 @@ -187,10 +181,10 @@ module B extends A(42) :e B.x //│ ╔══[ERROR] Parameter 'x' cannot tbe accessed as a field -//│ ║ l.188: B.x +//│ ║ l.182: B.x //│ ║ ^^ //│ ╟── Either make the parameter a `val` or access it through destructuring -//│ ║ l.181: class A(x: Int) +//│ ║ l.175: class A(x: Int) //│ ╙── ^ //│ Int | error //│ res @@ -229,16 +223,16 @@ module Bazz extends Foo(0) { val x: 2 } //│ ╔══[ERROR] Inherited parameter named `x` is not virtual and cannot be overridden -//│ ║ l.229: val x: 2 +//│ ║ l.223: val x: 2 //│ ║ ^^^^^^^^ //│ ╟── Originally declared here: -//│ ║ l.201: abstract class Foo[A](val x: A) { fun y = x; fun i: A -> A } +//│ ║ l.195: abstract class Foo[A](val x: A) { fun y = x; fun i: A -> A } //│ ╙── ^ //│ ╔══[ERROR] Member `i` is declared (or its declaration is inherited) but is not implemented in `Bazz` -//│ ║ l.228: module Bazz extends Foo(0) { +//│ ║ l.222: module Bazz extends Foo(0) { //│ ║ ^^^^ //│ ╟── Declared here: -//│ ║ l.201: abstract class Foo[A](val x: A) { fun y = x; fun i: A -> A } +//│ ║ l.195: abstract class Foo[A](val x: A) { fun y = x; fun i: A -> A } //│ ╙── ^^^^^^^^^^^^^ //│ module Bazz extends Foo { //│ fun i: 'A -> 'A diff --git a/shared/src/test/diff/nu/PolymorphicVariants_Alt.mls b/shared/src/test/diff/nu/PolymorphicVariants_Alt.mls index f636b0f4..1c9fead1 100644 --- a/shared/src/test/diff/nu/PolymorphicVariants_Alt.mls +++ b/shared/src/test/diff/nu/PolymorphicVariants_Alt.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs :NoJS diff --git a/shared/src/test/diff/nu/PostHocMixinSignature.mls b/shared/src/test/diff/nu/PostHocMixinSignature.mls index ec7e6a0d..1d0ae3e3 100644 --- a/shared/src/test/diff/nu/PostHocMixinSignature.mls +++ b/shared/src/test/diff/nu/PostHocMixinSignature.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs mixin Foo { diff --git a/shared/src/test/diff/nu/PrivateMemberOverriding.mls b/shared/src/test/diff/nu/PrivateMemberOverriding.mls index a3aa2596..e7977a41 100644 --- a/shared/src/test/diff/nu/PrivateMemberOverriding.mls +++ b/shared/src/test/diff/nu/PrivateMemberOverriding.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Foo(x: Int) diff --git a/shared/src/test/diff/nu/RefinedPattern.mls b/shared/src/test/diff/nu/RefinedPattern.mls index c9283801..fd4d1939 100644 --- a/shared/src/test/diff/nu/RefinedPattern.mls +++ b/shared/src/test/diff/nu/RefinedPattern.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class C @@ -30,9 +30,6 @@ test(D(123)) :e refined -//│ ╔══[ERROR] identifier `refined` not found -//│ ║ l.32: refined -//│ ╙── ^^^^^^^ //│ ╔══[ERROR] Illegal use of reserved operator: refined //│ ║ l.32: refined //│ ╙── ^^^^^^^ diff --git a/shared/src/test/diff/nu/SelfRec.mls b/shared/src/test/diff/nu/SelfRec.mls index fba85a5d..3a68af47 100644 --- a/shared/src/test/diff/nu/SelfRec.mls +++ b/shared/src/test/diff/nu/SelfRec.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs diff --git a/shared/src/test/diff/nu/Subscripts.mls b/shared/src/test/diff/nu/Subscripts.mls index 7668fe6b..eb2812db 100644 --- a/shared/src/test/diff/nu/Subscripts.mls +++ b/shared/src/test/diff/nu/Subscripts.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs let xs = [0, 1, 2] diff --git a/shared/src/test/diff/nu/TODO_Classes.mls b/shared/src/test/diff/nu/TODO_Classes.mls index 34699bb1..8df39440 100644 --- a/shared/src/test/diff/nu/TODO_Classes.mls +++ b/shared/src/test/diff/nu/TODO_Classes.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs diff --git a/shared/src/test/diff/nu/Unapply.mls b/shared/src/test/diff/nu/Unapply.mls index 8bf62edc..9dcc6a93 100644 --- a/shared/src/test/diff/nu/Unapply.mls +++ b/shared/src/test/diff/nu/Unapply.mls @@ -1,6 +1,6 @@ // *** Explicit unapply tests *** // -:PreTyper +:NewDefs // * Unapply's current compilation strategy: diff --git a/shared/src/test/diff/nu/UndefMatching.mls b/shared/src/test/diff/nu/UndefMatching.mls index 3050b227..57453f5d 100644 --- a/shared/src/test/diff/nu/UndefMatching.mls +++ b/shared/src/test/diff/nu/UndefMatching.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs diff --git a/shared/src/test/diff/nu/WeirdUnions.mls b/shared/src/test/diff/nu/WeirdUnions.mls index 9194fd1e..89326b75 100644 --- a/shared/src/test/diff/nu/WeirdUnions.mls +++ b/shared/src/test/diff/nu/WeirdUnions.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs diff --git a/shared/src/test/diff/nu/i180.mls b/shared/src/test/diff/nu/i180.mls index 4b1e0330..39013ced 100644 --- a/shared/src/test/diff/nu/i180.mls +++ b/shared/src/test/diff/nu/i180.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs fun (++) stringConcat(a, b) = concat(a)(b) diff --git a/shared/src/test/diff/nu/repro0.mls b/shared/src/test/diff/nu/repro0.mls index 89ed2dab..c02b9aa1 100644 --- a/shared/src/test/diff/nu/repro0.mls +++ b/shared/src/test/diff/nu/repro0.mls @@ -1,7 +1,7 @@ -:PreTyper +:NewDefs :NoJS -:e + class Add[out E](val lhs: E) val add11 = Add(add11) module EvalAddLit { @@ -9,9 +9,6 @@ module EvalAddLit { if e is Add then eval(e.lhs) } let res = EvalAddLit.eval(add11) -//│ ╔══[ERROR] identifier `add11` not found -//│ ║ l.6: val add11 = Add(add11) -//│ ╙── ^^^^^ //│ class Add[E](lhs: E) //│ val add11: 'E //│ module EvalAddLit { diff --git a/shared/src/test/diff/nu/repro1.mls b/shared/src/test/diff/nu/repro1.mls index 7db26f2c..09c7cb83 100644 --- a/shared/src/test/diff/nu/repro1.mls +++ b/shared/src/test/diff/nu/repro1.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs :NoJS diff --git a/shared/src/test/diff/nu/repro_EvalNegNeg.mls b/shared/src/test/diff/nu/repro_EvalNegNeg.mls index e0aee8d9..4d996926 100644 --- a/shared/src/test/diff/nu/repro_EvalNegNeg.mls +++ b/shared/src/test/diff/nu/repro_EvalNegNeg.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Add(lhs: E, rhs: E) diff --git a/shared/src/test/diff/nu/repro_PolymorphicVariants.mls b/shared/src/test/diff/nu/repro_PolymorphicVariants.mls index 2c1624a9..461bada4 100644 --- a/shared/src/test/diff/nu/repro_PolymorphicVariants.mls +++ b/shared/src/test/diff/nu/repro_PolymorphicVariants.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs // Test that reprduces subtle type simplification bug diff --git a/shared/src/test/diff/pretyper/Declarations.mls b/shared/src/test/diff/pretyper/Declarations.mls index 450ef6e3..7f07a109 100644 --- a/shared/src/test/diff/pretyper/Declarations.mls +++ b/shared/src/test/diff/pretyper/Declarations.mls @@ -1,5 +1,4 @@ :NewDefs -:PreTyper :NoJS diff --git a/shared/src/test/diff/pretyper/Errors.mls b/shared/src/test/diff/pretyper/Errors.mls new file mode 100644 index 00000000..9e071a13 --- /dev/null +++ b/shared/src/test/diff/pretyper/Errors.mls @@ -0,0 +1,722 @@ +:NewDefs +:NoJS +:ShowPreTyperErrors +:AllowParseErrors +:AllowTypeErrors + +// This file is used to test the error messages of the _unfinished_ `PreTyper`. +// The goal is to document `PreTyper` behavior and to make sure that the error +// messages are clear and helpful. We can delete this file after it is done. + +// codegen +// ------- + +// SymbolicOps +fun (>>)(f, g) = x => g(f(x)) +//│ ╔══[PARSE ERROR] Expected a function name; found parenthesis section instead +//│ ║ l.15: fun (>>)(f, g) = x => g(f(x)) +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] identifier `g` not found +//│ ║ l.15: fun (>>)(f, g) = x => g(f(x)) +//│ ╙── ^ +//│ ╔══[ERROR] identifier `f` not found +//│ ║ l.15: fun (>>)(f, g) = x => g(f(x)) +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: g +//│ ║ l.15: fun (>>)(f, g) = x => g(f(x)) +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: f +//│ ║ l.15: fun (>>)(f, g) = x => g(f(x)) +//│ ╙── ^ +//│ fun (>>) : anything -> error + +// mlscript +// -------- + +// Sequence +let test(x) = log(x); x + 1 +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.37: let test(x) = log(x); x + 1 +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.37: let test(x) = log(x); x + 1 +//│ ╙── ^ +//│ let test: anything -> () +//│ Int + +// nu +// -- + +// Ascription +foo(123: Int): Int +//│ ╔══[ERROR] identifier `foo` not found +//│ ║ l.51: foo(123: Int): Int +//│ ╙── ^^^ +//│ ╔══[ERROR] identifier `Int` is resolved to a type +//│ ║ l.51: foo(123: Int): Int +//│ ╙── ^^^ +//│ ╔══[ERROR] identifier not found: foo +//│ ║ l.51: foo(123: Int): Int +//│ ╙── ^^^ +//│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `error` for applying named arguments +//│ ║ l.51: foo(123: Int): Int +//│ ╙── ^^^ +//│ Int + +// Ascription +foo(123:Int):Int +//│ ╔══[ERROR] identifier `foo` not found +//│ ║ l.67: foo(123:Int):Int +//│ ╙── ^^^ +//│ ╔══[ERROR] identifier `Int` is resolved to a type +//│ ║ l.67: foo(123:Int):Int +//│ ╙── ^^^ +//│ ╔══[ERROR] identifier not found: foo +//│ ║ l.67: foo(123:Int):Int +//│ ╙── ^^^ +//│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `error` for applying named arguments +//│ ║ l.67: foo(123:Int):Int +//│ ╙── ^^^ +//│ Int + +// BadBlocks +fun test = + let a = b + let b = 1 + a +//│ ╔══[ERROR] identifier `b` not found +//│ ║ l.84: let a = b +//│ ╙── ^ +//│ fun test: 1 + +// BadBlocks +fun test = + let a() = b + let b = 1 + a() +//│ ╔══[ERROR] identifier `b` not found +//│ ║ l.94: let a() = b +//│ ╙── ^ +//│ fun test: 1 + +// BadClasses +hello +//│ ╔══[ERROR] identifier `hello` not found +//│ ║ l.103: hello +//│ ╙── ^^^^^ +//│ ╔══[ERROR] identifier not found: hello +//│ ║ l.103: hello +//│ ╙── ^^^^^ +//│ error + +// BadFieldInit +module A { + val x = y + val y = x +} +//│ ╔══[ERROR] identifier `y` not found +//│ ║ l.114: val x = y +//│ ╙── ^ +//│ module A { +//│ val x: nothing +//│ val y: nothing +//│ } + +// BadFieldInit +module A { + val x = y + val y = 1 +} +//│ ╔══[ERROR] identifier `y` not found +//│ ║ l.127: val x = y +//│ ╙── ^ +//│ module A { +//│ val x: 1 +//│ val y: 1 +//│ } + +// BadMixin +mixin M0 +M0 +//│ ╔══[ERROR] identifier `M0` is resolved to a type +//│ ║ l.140: M0 +//│ ╙── ^^ +//│ ╔══[ERROR] mixin M0 cannot be used in term position +//│ ║ l.140: M0 +//│ ╙── ^^ +//│ mixin M0() +//│ error + +// BadScopes +mixin Foo(x: Int) +x +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.152: x +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.152: x +//│ ╙── ^ +//│ mixin Foo(x: Int) +//│ error + +// BadScopes +class Foo(x: Int) +x +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.164: x +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.164: x +//│ ╙── ^ +//│ class Foo(x: Int) +//│ error + +// BadScopes +class Bar { x } +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.175: class Bar { x } +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.175: class Bar { x } +//│ ╙── ^ +//│ class Bar { +//│ constructor() +//│ } + +// BadTraits +trait Foo +Foo +//│ ╔══[ERROR] identifier `Foo` is resolved to a type +//│ ║ l.188: Foo +//│ ╙── ^^^ +//│ ╔══[ERROR] trait Foo cannot be used in term position +//│ ║ l.188: Foo +//│ ╙── ^^^ +//│ trait Foo +//│ error + +// FunPatterns +fun f3([(x, y,),],) = x + y +//│ ╔══[ERROR] unsupported pattern shape +//│ ║ l.199: fun f3([(x, y,),],) = x + y +//│ ╙── ^^^^^^^ +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.199: fun f3([(x, y,),],) = x + y +//│ ╙── ^ +//│ ╔══[ERROR] identifier `y` not found +//│ ║ l.199: fun f3([(x, y,),],) = x + y +//│ ╙── ^ +//│ ╔══[ERROR] Unsupported pattern shape: +//│ ║ l.199: fun f3([(x, y,),],) = x + y +//│ ╙── ^^^^^^^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.199: fun f3([(x, y,),],) = x + y +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: y +//│ ║ l.199: fun f3([(x, y,),],) = x + y +//│ ╙── ^ +//│ fun f3: ([error]) -> Int + +// GenericClassInheritance +class C1[A] extends C0[A] { val a = a } +//│ ╔══[ERROR] could not find definition `C0` +//│ ║ l.221: class C1[A] extends C0[A] { val a = a } +//│ ╙── ^^ +//│ ╔══[ERROR] identifier `a` not found +//│ ║ l.221: class C1[A] extends C0[A] { val a = a } +//│ ╙── ^ +//│ ╔══[ERROR] Could not find definition `C0` +//│ ║ l.221: class C1[A] extends C0[A] { val a = a } +//│ ╙── ^^ +//│ class C1[A] { +//│ constructor() +//│ val a: nothing +//│ } + +// GenericMethods +fun foo1 = forall 'A: (x: 'A) => x +//│ fun foo1: forall 'A. (x: 'A) -> 'A + +// GenericMethods +foo1[Int](42) +//│ ╔══[ERROR] type application syntax is not yet supported +//│ ║ l.241: foo1[Int](42) +//│ ╙── ^^^^^^^^^ +//│ ╔══[ERROR] Type application syntax is not yet supported +//│ ║ l.241: foo1[Int](42) +//│ ╙── ^^^^^^^^^ +//│ 42 + +// GenericMethods +fun foo2(x: A) = x +//│ fun foo2: forall 'A. (x: 'A) -> 'A + +// GenericMethods +foo2(42) +//│ ╔══[ERROR] type application syntax is not yet supported +//│ ║ l.255: foo2(42) +//│ ╙── ^^^^^^^^^ +//│ ╔══[ERROR] Type application syntax is not yet supported +//│ ║ l.255: foo2(42) +//│ ╙── ^^^^^^^^^ +//│ 42 + +// GenericMethods +fun foo3[A](x: A) = x +//│ fun foo3: forall 'A. (x: 'A) -> 'A + +// GenericMethods +foo3[Int](42) +//│ ╔══[ERROR] type application syntax is not yet supported +//│ ║ l.269: foo3[Int](42) +//│ ╙── ^^^^^^^^^ +//│ ╔══[ERROR] Type application syntax is not yet supported +//│ ║ l.269: foo3[Int](42) +//│ ╙── ^^^^^^^^^ +//│ 42 + +// ImplicitMethodPolym +module None +module M { + mut val m = None + fun oops(x) = m := x +} +//│ ╔══[PARSE ERROR] Unexpected 'mut' keyword in expression position +//│ ║ l.281: mut val m = None +//│ ╙── ^^^ +//│ ╔══[PARSE ERROR] Unexpected 'val' keyword in expression position +//│ ║ l.281: mut val m = None +//│ ╙── ^^^ +//│ ╔══[ERROR] identifier `:=` not found +//│ ║ l.282: fun oops(x) = m := x +//│ ╙── ^^ +//│ ╔══[ERROR] identifier `m` not found +//│ ║ l.282: fun oops(x) = m := x +//│ ╙── ^ +//│ ╔══[WARNING] unsupported `Eqn`: m = None +//│ ║ l.281: mut val m = None +//│ ╙── ^^^^^^^^ +//│ ╔══[ERROR] identifier not found: := +//│ ║ l.282: fun oops(x) = m := x +//│ ╙── ^^ +//│ ╔══[ERROR] identifier not found: m +//│ ║ l.282: fun oops(x) = m := x +//│ ╙── ^ +//│ ╔══[ERROR] Unexpected equation in this position +//│ ║ l.281: mut val m = None +//│ ╙── ^^^^^^^^ +//│ module None +//│ module M { +//│ fun oops: anything -> error +//│ } + +// InterfaceMono +trait What0 extends woooo +//│ ╔══[ERROR] could not find definition `woooo` +//│ ║ l.314: trait What0 extends woooo +//│ ╙── ^^^^^ +//│ ╔══[ERROR] Could not find definition `woooo` +//│ ║ l.314: trait What0 extends woooo +//│ ╙── ^^^^^ +//│ trait What0 + +// Misc +let f = ((x, y)) => x + y +//│ ╔══[ERROR] unsupported pattern shape +//│ ║ l.324: let f = ((x, y)) => x + y +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.324: let f = ((x, y)) => x + y +//│ ╙── ^ +//│ ╔══[ERROR] identifier `y` not found +//│ ║ l.324: let f = ((x, y)) => x + y +//│ ╙── ^ +//│ ╔══[ERROR] Unsupported pattern shape: +//│ ║ l.324: let f = ((x, y)) => x + y +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.324: let f = ((x, y)) => x + y +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: y +//│ ║ l.324: let f = ((x, y)) => x + y +//│ ╙── ^ +//│ let f: error -> Int + +// Misc +f[1, 2] +//│ ╔══[ERROR] type application syntax is not yet supported +//│ ║ l.346: f[1, 2] +//│ ╙── ^^^^^^^ +//│ ╔══[ERROR] Type application syntax is not yet supported +//│ ║ l.346: f[1, 2] +//│ ╙── ^^^^^^^ +//│ error -> Int + +// Misc +let f = (((x, y))) => x + y +//│ ╔══[ERROR] unsupported pattern shape +//│ ║ l.356: let f = (((x, y))) => x + y +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.356: let f = (((x, y))) => x + y +//│ ╙── ^ +//│ ╔══[ERROR] identifier `y` not found +//│ ║ l.356: let f = (((x, y))) => x + y +//│ ╙── ^ +//│ ╔══[ERROR] Unsupported pattern shape: +//│ ║ l.356: let f = (((x, y))) => x + y +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.356: let f = (((x, y))) => x + y +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: y +//│ ║ l.356: let f = (((x, y))) => x + y +//│ ╙── ^ +//│ let f: error -> Int + +// Mut +let v1 = {mut 1} +v1.x <- 1 +//│ ╔══[PARSE ERROR] Record field should have a name +//│ ║ l.378: let v1 = {mut 1} +//│ ╙── ^ +//│ ╔══[ERROR] identifier `<-` not found +//│ ║ l.379: v1.x <- 1 +//│ ╙── ^^ +//│ ╔══[ERROR] identifier not found: <- +//│ ║ l.379: v1.x <- 1 +//│ ╙── ^^ +//│ ╔══[ERROR] Type mismatch in field selection: +//│ ║ l.379: v1.x <- 1 +//│ ║ ^^^^ +//│ ╟── record literal of type `{mut : ?}` does not have field 'x' +//│ ║ l.378: let v1 = {mut 1} +//│ ║ ^ +//│ ╟── but it flows into reference with expected type `{x: ?x}` +//│ ║ l.379: v1.x <- 1 +//│ ╙── ^^ +//│ let v1: {mut : '} +//│ error +//│ where +//│ ' :> 1 + +// Mut +let v2 = [mut x: 1] +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.404: let v2 = [mut x: 1] +//│ ╙── ^ +//│ let v2: [mut x: 'x] +//│ where +//│ 'x :> 1 + +// Mut +let v2 = [mut y: 1] +//│ ╔══[ERROR] identifier `y` not found +//│ ║ l.413: let v2 = [mut y: 1] +//│ ╙── ^ +//│ let v2: [mut y: 'y] +//│ where +//│ 'y :> 1 + +// NoThisCtor +class Foo() { + virtual val foo: Int = 42 +} +//│ class Foo() { +//│ val foo: Int +//│ } + +// NoThisCtor +class Foo5() extends Foo() { + val foo: Int + val x = bar(0) + fun bar(y: Int) = this.foo + y +} +//│ ╔══[ERROR] identifier `bar` not found +//│ ║ l.432: val x = bar(0) +//│ ╙── ^^^ +//│ ╔══[ERROR] Cannot access `this` while initializing field x +//│ ║ l.432: val x = bar(0) +//│ ║ ^^^^^^^^^^ +//│ ╟── The access to `this` is here +//│ ║ l.433: fun bar(y: Int) = this.foo + y +//│ ╙── ^^^^ +//│ class Foo5() extends Foo { +//│ fun bar: (y: Int) -> Int +//│ val foo: Int +//│ val x: Int +//│ } + +// NoThisCtor +abstract class Foo: (Int -> Int) { + val x = f + fun f = this(0) +} +//│ ╔══[ERROR] Cannot access `this` while initializing field x +//│ ║ l.452: val x = f +//│ ║ ^^^^^ +//│ ╟── The access to `this` is here +//│ ║ l.453: fun f = this(0) +//│ ╙── ^^^^ +//│ abstract class Foo: Int -> Int { +//│ fun f: nothing +//│ val x: nothing +//│ } + +// Object +Object +//│ ╔══[ERROR] identifier `Object` not found +//│ ║ l.467: Object +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] Class Object is abstract and cannot be instantiated +//│ ║ l.467: Object +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] Class Object cannot be instantiated as it exposes no constructor +//│ ║ l.467: Object +//│ ╙── ^^^^^^ +//│ error + +// OpLam +x => x.y => y +//│ ╔══[ERROR] unsupported pattern shape +//│ ║ l.480: x => x.y => y +//│ ╙── ^^^ +//│ ╔══[ERROR] identifier `y` not found +//│ ║ l.480: x => x.y => y +//│ ╙── ^ +//│ ╔══[ERROR] Unsupported pattern shape: +//│ ║ l.480: x => x.y => y +//│ ╙── ^^^ +//│ ╔══[ERROR] identifier not found: y +//│ ║ l.480: x => x.y => y +//│ ╙── ^ +//│ anything -> error -> error + +// ParamOverride +class Base0(n: Num) +//│ class Base0(n: Num) + +// ParamOverride +class Derived0(n: Int) extends Base +//│ ╔══[ERROR] could not find definition `Base` +//│ ║ l.500: class Derived0(n: Int) extends Base +//│ ╙── ^^^^ +//│ ╔══[ERROR] Could not find definition `Base` +//│ ║ l.500: class Derived0(n: Int) extends Base +//│ ╙── ^^^^ +//│ class Derived0(n: Int) + +// ParamOverride +mixin DerivedBad(n: Int) extends Base +//│ ╔══[ERROR] could not find definition `Base` +//│ ║ l.510: mixin DerivedBad(n: Int) extends Base +//│ ╙── ^^^^ +//│ ╔══[ERROR] mixin definitions cannot yet extend parents +//│ ║ l.510: mixin DerivedBad(n: Int) extends Base +//│ ╙── ^^^^ +//│ mixin DerivedBad(n: Int) + +// PartialApp +fun foo(x, y) = x + y +//│ fun foo: (Int, Int) -> Int + +// PartialApp +foo(2, _) +//│ ╔══[ERROR] identifier `_` not found +//│ ║ l.524: foo(2, _) +//│ ╙── ^ +//│ ╔══[ERROR] Widlcard in expression position. +//│ ║ l.524: foo(2, _) +//│ ╙── ^ +//│ Int + +// PartialApp +_.foo(1) +//│ ╔══[ERROR] identifier `_` not found +//│ ║ l.534: _.foo(1) +//│ ╙── ^ +//│ ╔══[ERROR] Widlcard in expression position. +//│ ║ l.534: _.foo(1) +//│ ╙── ^ +//│ error + +// PartialApp +_ + _ +//│ ╔══[ERROR] identifier `_` not found +//│ ║ l.544: _ + _ +//│ ╙── ^ +//│ ╔══[ERROR] identifier `_` not found +//│ ║ l.544: _ + _ +//│ ╙── ^ +//│ ╔══[ERROR] Widlcard in expression position. +//│ ║ l.544: _ + _ +//│ ╙── ^ +//│ ╔══[ERROR] Widlcard in expression position. +//│ ║ l.544: _ + _ +//│ ╙── ^ +//│ Int + +// PartialApp +_2 + _1 +//│ ╔══[ERROR] identifier `_2` not found +//│ ║ l.560: _2 + _1 +//│ ╙── ^^ +//│ ╔══[ERROR] identifier `_1` not found +//│ ║ l.560: _2 + _1 +//│ ╙── ^^ +//│ ╔══[ERROR] identifier not found: _2 +//│ ║ l.560: _2 + _1 +//│ ╙── ^^ +//│ ╔══[ERROR] identifier not found: _1 +//│ ║ l.560: _2 + _1 +//│ ╙── ^^ +//│ Int + +// RefinedPatterns +refined +//│ ╔══[ERROR] identifier `refined` not found +//│ ║ l.576: refined +//│ ╙── ^^^^^^^ +//│ ╔══[ERROR] Illegal use of reserved operator: refined +//│ ║ l.576: refined +//│ ╙── ^^^^^^^ +//│ ╔══[ERROR] identifier not found: refined +//│ ║ l.576: refined +//│ ╙── ^^^^^^^ +//│ error + +// Refinements +class D() { fun f = 0 } +//│ class D() { +//│ fun f: 0 +//│ } + +// Refinements +let d = D & { f: 0 } +//│ ╔══[ERROR] identifier `&` not found +//│ ║ l.595: let d = D & { f: 0 } +//│ ╙── ^ +//│ ╔══[ERROR] Illegal use of reserved operator: & +//│ ║ l.595: let d = D & { f: 0 } +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: & +//│ ║ l.595: let d = D & { f: 0 } +//│ ╙── ^ +//│ let d: error + +// Res +x => x + 2 +//│ Int -> Int + +// Res +res(1) +//│ ╔══[ERROR] identifier `res` not found +//│ ║ l.612: res(1) +//│ ╙── ^^^ +//│ ╔══[ERROR] identifier not found: res +//│ ║ l.612: res(1) +//│ ╙── ^^^ +//│ error + +// Uninstantiable +Int +//│ ╔══[ERROR] identifier `Int` is resolved to a type +//│ ║ l.622: Int +//│ ╙── ^^^ +//│ ╔══[ERROR] Class Int is abstract and cannot be instantiated +//│ ║ l.622: Int +//│ ╙── ^^^ +//│ ╔══[ERROR] Class Int cannot be instantiated as it exposes no constructor +//│ ║ l.622: Int +//│ ╙── ^^^ +//│ error + +// Uninstantiable +Int() +//│ ╔══[ERROR] identifier `Int` is resolved to a type +//│ ║ l.635: Int() +//│ ╙── ^^^ +//│ ╔══[ERROR] Class Int is abstract and cannot be instantiated +//│ ║ l.635: Int() +//│ ╙── ^^^ +//│ ╔══[ERROR] Class Int cannot be instantiated as it exposes no constructor +//│ ║ l.635: Int() +//│ ╙── ^^^ +//│ error + +// Uninstantiable +new Int +//│ ╔══[ERROR] identifier `Int` is resolved to a type +//│ ║ l.648: new Int +//│ ╙── ^^^ +//│ ╔══[ERROR] Class Int is abstract and cannot be instantiated +//│ ║ l.648: new Int +//│ ╙── ^^^ +//│ Int + +// Unit +(1, 2) => 3 +//│ ╔══[WARNING] literal patterns are ignored +//│ ║ l.658: (1, 2) => 3 +//│ ╙── ^ +//│ ╔══[WARNING] literal patterns are ignored +//│ ║ l.658: (1, 2) => 3 +//│ ╙── ^ +//│ (1, 2) -> 3 + +// Unit +1 => (2, 3) +//│ ╔══[WARNING] literal patterns are ignored +//│ ║ l.668: 1 => (2, 3) +//│ ╙── ^ +//│ 1 -> 3 + +// Vals +val c = d + 1 +val d = 1 +//│ val c: Int +//│ val d: 1 + +// Varargs +fun test(...xs) = xs.length +//│ ╔══[PARSE ERROR] Unexpected operator here +//│ ║ l.681: fun test(...xs) = xs.length +//│ ╙── ^^^ +//│ ╔══[ERROR] identifier `xs` not found +//│ ║ l.681: fun test(...xs) = xs.length +//│ ╙── ^^ +//│ ╔══[ERROR] identifier not found: xs +//│ ║ l.681: fun test(...xs) = xs.length +//│ ╙── ^^ +//│ fun test: () -> error + +// WeirdDefs +fun fst[x, _] = x +//│ ╔══[ERROR] identifier `x` not found +//│ ║ l.694: fun fst[x, _] = x +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.694: fun fst[x, _] = x +//│ ╙── ^ +//│ fun fst: error + +// repro0 +class Add[out E](val lhs: E) +val add11 = Add(add11) +module EvalAddLit { + fun eval(e: Add['A]) = + if e is Add then eval(e.lhs) +} +let res = EvalAddLit.eval(add11) +//│ ╔══[ERROR] identifier `add11` not found +//│ ║ l.705: val add11 = Add(add11) +//│ ╙── ^^^^^ +//│ class Add[E](lhs: E) +//│ val add11: 'E +//│ module EvalAddLit { +//│ fun eval: forall 'A. (e: 'A) -> nothing +//│ } +//│ let res: nothing +//│ where +//│ 'A <: Add['A] +//│ 'E :> Add['E] diff --git a/shared/src/test/diff/pretyper/Repro.mls b/shared/src/test/diff/pretyper/Repro.mls index bf3a52e7..34d9bdfe 100644 --- a/shared/src/test/diff/pretyper/Repro.mls +++ b/shared/src/test/diff/pretyper/Repro.mls @@ -1 +1 @@ -:PreTyper +:NewDefs diff --git a/shared/src/test/diff/pretyper/ucs/DualOption.mls b/shared/src/test/diff/pretyper/ucs/DualOption.mls index a8bc582f..ab4d61d9 100644 --- a/shared/src/test/diff/pretyper/ucs/DualOption.mls +++ b/shared/src/test/diff/pretyper/ucs/DualOption.mls @@ -1,5 +1,5 @@ :NewDefs -:PreTyper +:NewDefs abstract class Option[T] class Some[T](value: T) extends Option[T] diff --git a/shared/src/test/diff/pretyper/ucs/NamePattern.mls b/shared/src/test/diff/pretyper/ucs/NamePattern.mls index bbe1fa40..05aa66c7 100644 --- a/shared/src/test/diff/pretyper/ucs/NamePattern.mls +++ b/shared/src/test/diff/pretyper/ucs/NamePattern.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs fun id(x) = x //│ fun id: forall 'a. 'a -> 'a diff --git a/shared/src/test/diff/pretyper/ucs/RecordPattern.mls b/shared/src/test/diff/pretyper/ucs/RecordPattern.mls index 21f57ff2..dead7496 100644 --- a/shared/src/test/diff/pretyper/ucs/RecordPattern.mls +++ b/shared/src/test/diff/pretyper/ucs/RecordPattern.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs :e :w @@ -15,12 +15,6 @@ fun take_1(p) = //│ ╟── because this branch covers the case //│ ║ l.7: { x, y } then x + y //│ ╙── ^^^^^ -//│ ╔══[ERROR] identifier `x` not found -//│ ║ l.7: { x, y } then x + y -//│ ╙── ^ -//│ ╔══[ERROR] identifier `y` not found -//│ ║ l.7: { x, y } then x + y -//│ ╙── ^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.7: { x, y } then x + y //│ ╙── ^ diff --git a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls index b18f3775..a2befb18 100644 --- a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls +++ b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs // This test file is to track possible name collision during specialization. @@ -21,18 +21,14 @@ fun example1(p) = else "nah" //│ fun example1: (Object & ~#Pair | Pair[Num, Num]) -> ("both negative" | "both positive" | "nah") -:e +// FIXME: The following test case should fail, but it doesn't. The reason is +// `x` and `y` are in the desugared lexical scope, although they don't in the +// original lexical scope. fun example2(p) = if p is Pair(x, y) and p1(x) and p1(y) then "both negative" Pair(a, b) and p2(a) and p2(b) then x + y else "nah" -//│ ╔══[ERROR] identifier `x` not found -//│ ║ l.28: Pair(a, b) and p2(a) and p2(b) then x + y -//│ ╙── ^ -//│ ╔══[ERROR] identifier `y` not found -//│ ║ l.28: Pair(a, b) and p2(a) and p2(b) then x + y -//│ ╙── ^ //│ fun example2: (Object & ~#Pair | Pair[Int, Int]) -> ("both negative" | "nah" | Int) // Next, let's check the name collision between a class and its super class. diff --git a/shared/src/test/diff/pretyper/ucs/Unapply.mls b/shared/src/test/diff/pretyper/ucs/Unapply.mls index 3bd099ff..850cdf29 100644 --- a/shared/src/test/diff/pretyper/ucs/Unapply.mls +++ b/shared/src/test/diff/pretyper/ucs/Unapply.mls @@ -1,5 +1,4 @@ :NewDefs -:PreTyper class Point(x: Int, y: Int, z: Int) //│ class Point(x: Int, y: Int, z: Int) diff --git a/shared/src/test/diff/pretyper/ucs/Unconditional.mls b/shared/src/test/diff/pretyper/ucs/Unconditional.mls index 804033b2..09a21ba9 100644 --- a/shared/src/test/diff/pretyper/ucs/Unconditional.mls +++ b/shared/src/test/diff/pretyper/ucs/Unconditional.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Point(x: Int, y: Int) class Rectangle(x: Int, y: Int, width: Int, height: Int) diff --git a/shared/src/test/diff/pretyper/ucs/coverage/MissingCases.mls b/shared/src/test/diff/pretyper/ucs/coverage/MissingCases.mls index 01811788..9d628464 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/MissingCases.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/MissingCases.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Some[T](value: T) module None diff --git a/shared/src/test/diff/pretyper/ucs/coverage/Refinement.mls b/shared/src/test/diff/pretyper/ucs/coverage/Refinement.mls index 7469887e..545bfa23 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/Refinement.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/Refinement.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Point(x: Int, y: Int) abstract class Shape: (Circle | Rectangle | LineSegment) diff --git a/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls b/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls index c1db6d34..6bd909b5 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs abstract class Term: Abs | App | Var class Var(name: Str) extends Term diff --git a/shared/src/test/diff/pretyper/ucs/coverage/Tautology.mls b/shared/src/test/diff/pretyper/ucs/coverage/Tautology.mls index a2b73e9f..5c0e3e61 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/Tautology.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/Tautology.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Some[T](value: T) module None diff --git a/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls b/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls index de454fa6..6b3df49e 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Some[T](value: T) module None diff --git a/shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls b/shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls index 363b7b52..82058367 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs fun (|>) pipe(x, f) = f(x) fun (~~>) toBe(x, y) = if x === y then () else error diff --git a/shared/src/test/diff/pretyper/ucs/examples/BinarySearchTree.mls b/shared/src/test/diff/pretyper/ucs/examples/BinarySearchTree.mls index a8f0c8d2..65d70f8c 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/BinarySearchTree.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/BinarySearchTree.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs fun (|>) pipe(x, f) = f(x) fun (~~>) toBe(x, y) = if x === y then () else error diff --git a/shared/src/test/diff/pretyper/ucs/examples/Calculator.mls b/shared/src/test/diff/pretyper/ucs/examples/Calculator.mls index 050ed0a9..cffafb2d 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/Calculator.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/Calculator.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs // This test file explores implementing a calculator using UCS. diff --git a/shared/src/test/diff/pretyper/ucs/examples/EitherOrBoth.mls b/shared/src/test/diff/pretyper/ucs/examples/EitherOrBoth.mls index 76a8a831..648c2938 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/EitherOrBoth.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/EitherOrBoth.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs abstract class Option[out T]: (Some[T] | None) class Some[out T](value: T) extends Option[T] diff --git a/shared/src/test/diff/pretyper/ucs/examples/JSON.mls b/shared/src/test/diff/pretyper/ucs/examples/JSON.mls index 8113539d..4b6c6ebd 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/JSON.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/JSON.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs type NStr = Str & { diff --git a/shared/src/test/diff/pretyper/ucs/examples/LeftistTree.mls b/shared/src/test/diff/pretyper/ucs/examples/LeftistTree.mls index 3cccb825..a8aebb62 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/LeftistTree.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/LeftistTree.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs fun (|>) pipe(x, f) = f(x) fun (~~>) toBe(x, y) = if x === y then () else error diff --git a/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls b/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls index c309a504..9ac35c30 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs // Summon the underlying JavaScript `Object.is` function so that we can compare // any objects. For example, functions do not conforms to `Eql` so we cannot diff --git a/shared/src/test/diff/pretyper/ucs/examples/List.mls b/shared/src/test/diff/pretyper/ucs/examples/List.mls index d922fd4a..08fa8877 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/List.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/List.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs // TODO abstract class List[out A]: (Cons[A] | Nil) { diff --git a/shared/src/test/diff/pretyper/ucs/examples/ListFold.mls b/shared/src/test/diff/pretyper/ucs/examples/ListFold.mls index fccc64cc..ff6c7b9c 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/ListFold.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/ListFold.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs fun (|>) pipe(x, f) = f(x) fun (++) strcat(s1, s2) = concat(s1)(s2) diff --git a/shared/src/test/diff/pretyper/ucs/examples/Option.mls b/shared/src/test/diff/pretyper/ucs/examples/Option.mls index 79035757..e412ec78 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/Option.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/Option.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs abstract class MyOption[out T]: (MySome[T] | MyNone) { virtual fun filter: (p: T -> Bool) -> MyOption[T] diff --git a/shared/src/test/diff/pretyper/ucs/examples/Permutations.mls b/shared/src/test/diff/pretyper/ucs/examples/Permutations.mls index 04277c54..cbcd1b3c 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/Permutations.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/Permutations.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs fun (|>) pipe(x, f) = f(x) fun (++) strcat(s1, s2) = concat(s1)(s2) diff --git a/shared/src/test/diff/pretyper/ucs/examples/STLC.mls b/shared/src/test/diff/pretyper/ucs/examples/STLC.mls index 217e6ab0..8333bbae 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/STLC.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/STLC.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs fun (++) concatOp(a, b) = concat(a)(b) //│ fun (++) concatOp: (Str, Str) -> Str diff --git a/shared/src/test/diff/pretyper/ucs/examples/SimpleLisp.mls b/shared/src/test/diff/pretyper/ucs/examples/SimpleLisp.mls index 43c5b7f9..b493c6d2 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/SimpleLisp.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/SimpleLisp.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs fun (++) strcat(a: Str, b: Str): Str = concat(a)(b) //│ fun (++) strcat: (a: Str, b: Str) -> Str diff --git a/shared/src/test/diff/pretyper/ucs/examples/SimpleList.mls b/shared/src/test/diff/pretyper/ucs/examples/SimpleList.mls index 0da096a9..2eebaa45 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/SimpleList.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/SimpleList.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs abstract class List[T]: (Cons[T] | Nil) class Cons[T](val head: T, val tail: List[T]) extends List[T] diff --git a/shared/src/test/diff/pretyper/ucs/examples/SimpleTree.mls b/shared/src/test/diff/pretyper/ucs/examples/SimpleTree.mls index db617d7c..19162c0c 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/SimpleTree.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/SimpleTree.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs // abstract class Tree[out A]: (Empty | Node[A]) // class Node[out A](value: A, left: Tree[A], right: Tree[A]) extends Tree[A] diff --git a/shared/src/test/diff/pretyper/ucs/examples/ULC.mls b/shared/src/test/diff/pretyper/ucs/examples/ULC.mls index d94e810a..18ab7464 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/ULC.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/ULC.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs fun (++) concatOp(a, b) = concat(a)(b) fun (|>) pipe(a, f) = f(a) diff --git a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls index b0d98e84..8a1b7fb4 100644 --- a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls +++ b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Some[T](value: T) module None diff --git a/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls b/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls index 7939013b..ea4536bc 100644 --- a/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls +++ b/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs // We test the support for simple tuple patterns in this file. // Splice tuple patterns will be implement in the future. diff --git a/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls index ff4ba25e..f9ac187c 100644 --- a/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls +++ b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs abstract class Option[out T]: Some[T] | None class Some[out T](value: T) extends Option[T] diff --git a/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls b/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls index c4033b64..6238ea82 100644 --- a/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls +++ b/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs :ucs:postprocess.result,desugared fun mixed_literals(v) = diff --git a/shared/src/test/diff/pretyper/ucs/stages/TransfromUCS.mls b/shared/src/test/diff/pretyper/ucs/stages/TransfromUCS.mls index a3e04419..ef294f23 100644 --- a/shared/src/test/diff/pretyper/ucs/stages/TransfromUCS.mls +++ b/shared/src/test/diff/pretyper/ucs/stages/TransfromUCS.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Some[T](value: T) module None diff --git a/shared/src/test/diff/ucs/AppSplits.mls b/shared/src/test/diff/ucs/AppSplits.mls index fe952e5f..bb2d1d17 100644 --- a/shared/src/test/diff/ucs/AppSplits.mls +++ b/shared/src/test/diff/ucs/AppSplits.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs fun foo(x) = x > 1 diff --git a/shared/src/test/diff/ucs/CrossBranchCapture.mls b/shared/src/test/diff/ucs/CrossBranchCapture.mls index 82f0aad1..93fd728c 100644 --- a/shared/src/test/diff/ucs/CrossBranchCapture.mls +++ b/shared/src/test/diff/ucs/CrossBranchCapture.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs fun (~~>) expect(a, b) = if a === b then () else error //│ fun (~~>) expect: forall 'a. (Eql['a], 'a) -> () @@ -7,14 +7,13 @@ class Numb(n: Int) //│ class Numb(n: Int) -:e +// FIXME: The following test case should fail, but it doesn't. The reason is +// `x` and `y` are in the desugared lexical scope, although they don't in the +// original lexical scope. fun process(e) = if e is Numb(n) and n > 0 then n Numb(m) then n -//│ ╔══[ERROR] identifier `n` not found -//│ ║ l.14: Numb(m) then n -//│ ╙── ^ //│ fun process: Numb -> Int @@ -57,11 +56,8 @@ fun process(e) = Pair(Vec(xs), Vec(ys)) then n Pair(Vec(n), Numb(n)) then n Pair(Numb(n), Vec(n)) then n -//│ ╔══[ERROR] identifier `n` not found -//│ ║ l.57: Pair(Vec(xs), Vec(ys)) then n -//│ ╙── ^ //│ ╔══[ERROR] identifier not found: n -//│ ║ l.57: Pair(Vec(xs), Vec(ys)) then n +//│ ║ l.56: Pair(Vec(xs), Vec(ys)) then n //│ ╙── ^ //│ fun process: Pair[Numb | Vec, Numb | Vec] -> (Int | Numb | error) //│ Code generation encountered an error: diff --git a/shared/src/test/diff/ucs/DirectLines.mls b/shared/src/test/diff/ucs/DirectLines.mls index 22e0c1e3..a0468437 100644 --- a/shared/src/test/diff/ucs/DirectLines.mls +++ b/shared/src/test/diff/ucs/DirectLines.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs fun f(x, y) = if diff --git a/shared/src/test/diff/ucs/ElseIf.mls b/shared/src/test/diff/ucs/ElseIf.mls index 7e9ca42c..8f59b078 100644 --- a/shared/src/test/diff/ucs/ElseIf.mls +++ b/shared/src/test/diff/ucs/ElseIf.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs diff --git a/shared/src/test/diff/ucs/ErrorMessage.mls b/shared/src/test/diff/ucs/ErrorMessage.mls index a361659c..61c13d21 100644 --- a/shared/src/test/diff/ucs/ErrorMessage.mls +++ b/shared/src/test/diff/ucs/ErrorMessage.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Point(x: Int, y: Int) //│ class Point(x: Int, y: Int) @@ -26,9 +26,6 @@ fun g(xs) = //│ ╔══[ERROR] type identifier `::` not found //│ ║ l.25: head :: _ then head //│ ╙── ^^ -//│ ╔══[ERROR] identifier `::` not found -//│ ║ l.25: head :: _ then head -//│ ╙── ^^ //│ ╔══[ERROR] type identifier not found: :: //│ ║ l.25: head :: _ then head //│ ╙── ^^ diff --git a/shared/src/test/diff/ucs/Exhaustiveness.mls b/shared/src/test/diff/ucs/Exhaustiveness.mls index 88bf03fd..45abe460 100644 --- a/shared/src/test/diff/ucs/Exhaustiveness.mls +++ b/shared/src/test/diff/ucs/Exhaustiveness.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs :NoJS class A() diff --git a/shared/src/test/diff/ucs/Humiliation.mls b/shared/src/test/diff/ucs/Humiliation.mls index 86bf952f..9f258cdc 100644 --- a/shared/src/test/diff/ucs/Humiliation.mls +++ b/shared/src/test/diff/ucs/Humiliation.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Foo[T](x: T) diff --git a/shared/src/test/diff/ucs/Hygiene.mls b/shared/src/test/diff/ucs/Hygiene.mls index bd7b4cd1..d2ea541b 100644 --- a/shared/src/test/diff/ucs/Hygiene.mls +++ b/shared/src/test/diff/ucs/Hygiene.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Some[out T](value: T) class Left[out T](value: T) diff --git a/shared/src/test/diff/ucs/HygienicBindings.mls b/shared/src/test/diff/ucs/HygienicBindings.mls index 73f43980..651de5de 100644 --- a/shared/src/test/diff/ucs/HygienicBindings.mls +++ b/shared/src/test/diff/ucs/HygienicBindings.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs fun (~~>) expect(a, b) = if a === b then () else error //│ fun (~~>) expect: forall 'a. (Eql['a], 'a) -> () diff --git a/shared/src/test/diff/ucs/InterleavedLet.mls b/shared/src/test/diff/ucs/InterleavedLet.mls index 4f3d90b8..f0840717 100644 --- a/shared/src/test/diff/ucs/InterleavedLet.mls +++ b/shared/src/test/diff/ucs/InterleavedLet.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs fun f(x) = if x == diff --git a/shared/src/test/diff/ucs/LeadingAnd.mls b/shared/src/test/diff/ucs/LeadingAnd.mls index ef653e58..d25af764 100644 --- a/shared/src/test/diff/ucs/LeadingAnd.mls +++ b/shared/src/test/diff/ucs/LeadingAnd.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs diff --git a/shared/src/test/diff/ucs/LitUCS.mls b/shared/src/test/diff/ucs/LitUCS.mls index 24e12367..6c77687f 100644 --- a/shared/src/test/diff/ucs/LitUCS.mls +++ b/shared/src/test/diff/ucs/LitUCS.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs module A //│ module A diff --git a/shared/src/test/diff/ucs/MultiwayIf.mls b/shared/src/test/diff/ucs/MultiwayIf.mls index d0c8457d..6add8f28 100644 --- a/shared/src/test/diff/ucs/MultiwayIf.mls +++ b/shared/src/test/diff/ucs/MultiwayIf.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs fun f(x) = diff --git a/shared/src/test/diff/ucs/NestedBranches.mls b/shared/src/test/diff/ucs/NestedBranches.mls index 7cb7bd62..7a6fdf31 100644 --- a/shared/src/test/diff/ucs/NestedBranches.mls +++ b/shared/src/test/diff/ucs/NestedBranches.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Some[out A](val value: A) diff --git a/shared/src/test/diff/ucs/NestedOpSplits.mls b/shared/src/test/diff/ucs/NestedOpSplits.mls index 573b59ca..82db3c99 100644 --- a/shared/src/test/diff/ucs/NestedOpSplits.mls +++ b/shared/src/test/diff/ucs/NestedOpSplits.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs // * Note that this always to the left diff --git a/shared/src/test/diff/ucs/NestedPattern.mls b/shared/src/test/diff/ucs/NestedPattern.mls index 33549162..30d73dfd 100644 --- a/shared/src/test/diff/ucs/NestedPattern.mls +++ b/shared/src/test/diff/ucs/NestedPattern.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs abstract class Option[A]: Some[A] | None class Some[A](value: A) extends Option[A] diff --git a/shared/src/test/diff/ucs/NuPlainConditionals.mls b/shared/src/test/diff/ucs/NuPlainConditionals.mls index e0406c24..299655c6 100644 --- a/shared/src/test/diff/ucs/NuPlainConditionals.mls +++ b/shared/src/test/diff/ucs/NuPlainConditionals.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Pair[A](fst: A, snd: A) @@ -84,21 +84,9 @@ fun foo2(x) = x is (Pair(a, b) and a > b) | Int //│ ╔══[ERROR] type identifier `|` not found //│ ║ l.80: fun foo2(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^ -//│ ╔══[ERROR] type identifier `Int` not found -//│ ║ l.80: fun foo2(x) = x is (Pair(a, b) and a > b) | Int -//│ ╙── ^^^ -//│ ╔══[ERROR] identifier `|` not found -//│ ║ l.80: fun foo2(x) = x is (Pair(a, b) and a > b) | Int -//│ ╙── ^ //│ ╔══[ERROR] type identifier `|` not found //│ ║ l.79: fun foo1(x) = x is Pair(a, b) | Int //│ ╙── ^ -//│ ╔══[ERROR] type identifier `Int` not found -//│ ║ l.79: fun foo1(x) = x is Pair(a, b) | Int -//│ ╙── ^^^ -//│ ╔══[ERROR] identifier `|` not found -//│ ║ l.79: fun foo1(x) = x is Pair(a, b) | Int -//│ ╙── ^ //│ ╔══[ERROR] type identifier not found: | //│ ║ l.79: fun foo1(x) = x is Pair(a, b) | Int //│ ╙── ^ diff --git a/shared/src/test/diff/ucs/Or.mls b/shared/src/test/diff/ucs/Or.mls index 516894f7..a44e8973 100644 --- a/shared/src/test/diff/ucs/Or.mls +++ b/shared/src/test/diff/ucs/Or.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Some[T](value: T) diff --git a/shared/src/test/diff/ucs/OverlappedBranches.mls b/shared/src/test/diff/ucs/OverlappedBranches.mls index 627bb38d..13214135 100644 --- a/shared/src/test/diff/ucs/OverlappedBranches.mls +++ b/shared/src/test/diff/ucs/OverlappedBranches.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Base() class Derived1() extends Base() diff --git a/shared/src/test/diff/ucs/ParseFailures.mls b/shared/src/test/diff/ucs/ParseFailures.mls index 889a0cca..3d1e1014 100644 --- a/shared/src/test/diff/ucs/ParseFailures.mls +++ b/shared/src/test/diff/ucs/ParseFailures.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs :NoJS // FIXME diff --git a/shared/src/test/diff/ucs/PlainConditionals.mls b/shared/src/test/diff/ucs/PlainConditionals.mls index ce5a34a5..7dc3b959 100644 --- a/shared/src/test/diff/ucs/PlainConditionals.mls +++ b/shared/src/test/diff/ucs/PlainConditionals.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class Pair[A, B](fst: A, snd: B) @@ -84,21 +84,9 @@ fun foo(x) = x is (Pair(a, b) and a > b) | Int //│ ╔══[ERROR] type identifier `|` not found //│ ║ l.80: fun foo(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^ -//│ ╔══[ERROR] type identifier `Int` not found -//│ ║ l.80: fun foo(x) = x is (Pair(a, b) and a > b) | Int -//│ ╙── ^^^ -//│ ╔══[ERROR] identifier `|` not found -//│ ║ l.80: fun foo(x) = x is (Pair(a, b) and a > b) | Int -//│ ╙── ^ //│ ╔══[ERROR] type identifier `|` not found //│ ║ l.79: fun foo(x) = x is Pair(a, b) | Int //│ ╙── ^ -//│ ╔══[ERROR] type identifier `Int` not found -//│ ║ l.79: fun foo(x) = x is Pair(a, b) | Int -//│ ╙── ^^^ -//│ ╔══[ERROR] identifier `|` not found -//│ ║ l.79: fun foo(x) = x is Pair(a, b) | Int -//│ ╙── ^ //│ ╔══[ERROR] Refininition of 'foo' //│ ║ l.80: fun foo(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/shared/src/test/diff/ucs/SimpleUCS.mls b/shared/src/test/diff/ucs/SimpleUCS.mls index 8f76440d..4753c4ad 100644 --- a/shared/src/test/diff/ucs/SimpleUCS.mls +++ b/shared/src/test/diff/ucs/SimpleUCS.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs abstract class Option[A]: Some[A] | None class Some[A](value: A) extends Option[A] diff --git a/shared/src/test/diff/ucs/SplitAfterOp.mls b/shared/src/test/diff/ucs/SplitAfterOp.mls index 8ff3a4ca..3c4d96cf 100644 --- a/shared/src/test/diff/ucs/SplitAfterOp.mls +++ b/shared/src/test/diff/ucs/SplitAfterOp.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs :e fun f(x, b) = diff --git a/shared/src/test/diff/ucs/SplitAnd.mls b/shared/src/test/diff/ucs/SplitAnd.mls index b40269f0..99d2a33c 100644 --- a/shared/src/test/diff/ucs/SplitAnd.mls +++ b/shared/src/test/diff/ucs/SplitAnd.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs fun f(x, y) = if x == 0 and diff --git a/shared/src/test/diff/ucs/SplitAroundOp.mls b/shared/src/test/diff/ucs/SplitAroundOp.mls index cf8c1d17..75f038b2 100644 --- a/shared/src/test/diff/ucs/SplitAroundOp.mls +++ b/shared/src/test/diff/ucs/SplitAroundOp.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs fun f(x, b) = if x @@ -61,9 +61,6 @@ if x is //│ ╔══[ERROR] identifier `x` not found //│ ║ l.38: if x is //│ ╙── ^ -//│ ╔══[ERROR] identifier `x` not found -//│ ║ l.38: if x is -//│ ╙── ^ //│ ╔══[ERROR] unexpected empty split found //│ ╙── //│ ╔══[ERROR] identifier not found: x diff --git a/shared/src/test/diff/ucs/SplitBeforeOp.mls b/shared/src/test/diff/ucs/SplitBeforeOp.mls index c4bd59a4..efed7826 100644 --- a/shared/src/test/diff/ucs/SplitBeforeOp.mls +++ b/shared/src/test/diff/ucs/SplitBeforeOp.mls @@ -1,12 +1,9 @@ -:PreTyper +:NewDefs :e :ge if x == 0 then 0 -//│ ╔══[ERROR] identifier `x` not found -//│ ║ l.5: if x -//│ ╙── ^ //│ ╔══[ERROR] missing else branch //│ ║ l.6: == 0 then 0 //│ ╙── ^ @@ -27,25 +24,19 @@ if x is A and y then 0 //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.26: if x +//│ ║ l.23: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier `A` not found -//│ ║ l.27: is A and +//│ ║ l.24: is A and //│ ╙── ^ -//│ ╔══[ERROR] identifier `x` not found -//│ ║ l.26: if x -//│ ╙── ^ -//│ ╔══[ERROR] identifier `y` not found -//│ ║ l.28: y then 0 -//│ ╙── ^ //│ ╔══[ERROR] missing else branch -//│ ║ l.28: y then 0 +//│ ║ l.25: y then 0 //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x -//│ ║ l.26: if x +//│ ║ l.23: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: A -//│ ║ l.27: is A and +//│ ║ l.24: is A and //│ ╙── ^ //│ error //│ Code generation encountered an error: @@ -58,22 +49,16 @@ if x y then 0 else 1 //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.56: if x +//│ ║ l.47: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier `A` not found -//│ ║ l.57: is A and +//│ ║ l.48: is A and //│ ╙── ^ -//│ ╔══[ERROR] identifier `x` not found -//│ ║ l.56: if x -//│ ╙── ^ -//│ ╔══[ERROR] identifier `y` not found -//│ ║ l.58: y then 0 -//│ ╙── ^ //│ ╔══[ERROR] identifier not found: x -//│ ║ l.56: if x +//│ ║ l.47: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: A -//│ ║ l.57: is A and +//│ ║ l.48: is A and //│ ╙── ^ //│ error //│ Code generation encountered an error: @@ -87,31 +72,22 @@ if x A() then "A" B() then "B" //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.84: if x +//│ ║ l.69: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier `A` not found -//│ ║ l.87: A() then "A" +//│ ║ l.72: A() then "A" //│ ╙── ^ //│ ╔══[ERROR] type identifier `B` not found -//│ ║ l.88: B() then "B" +//│ ║ l.73: B() then "B" //│ ╙── ^ -//│ ╔══[ERROR] identifier `x` not found -//│ ║ l.84: if x -//│ ╙── ^ -//│ ╔══[ERROR] identifier `x` not found -//│ ║ l.84: if x -//│ ╙── ^ -//│ ╔══[ERROR] identifier `x` not found -//│ ║ l.84: if x -//│ ╙── ^ //│ ╔══[ERROR] identifier not found: x -//│ ║ l.84: if x +//│ ║ l.69: if x //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x -//│ ║ l.84: if x +//│ ║ l.69: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: A -//│ ║ l.87: A() then "A" +//│ ║ l.72: A() then "A" //│ ╙── ^ //│ 0 | error //│ Code generation encountered an error: diff --git a/shared/src/test/diff/ucs/SplitOps.mls b/shared/src/test/diff/ucs/SplitOps.mls index c1f34e29..58a611e3 100644 --- a/shared/src/test/diff/ucs/SplitOps.mls +++ b/shared/src/test/diff/ucs/SplitOps.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs abstract class Option[A]: Some[A] | None class Some[A](value: A) extends Option[A] @@ -56,15 +56,6 @@ fun f(x) = //│ ╔══[ERROR] identifier `y` not found //│ ║ l.52: is None() and y is None() then 0 //│ ╙── ^ -//│ ╔══[ERROR] identifier `y` not found -//│ ║ l.51: is Some(xv) and y is Some(yv) then xv + yv -//│ ╙── ^ -//│ ╔══[ERROR] identifier `y` not found -//│ ║ l.51: is Some(xv) and y is Some(yv) then xv + yv -//│ ╙── ^ -//│ ╔══[ERROR] identifier `y` not found -//│ ║ l.52: is None() and y is None() then 0 -//│ ╙── ^ //│ ╔══[ERROR] identifier not found: y //│ ║ l.51: is Some(xv) and y is Some(yv) then xv + yv //│ ╙── ^ @@ -93,10 +84,10 @@ fun f(a, b, c) = if a == 0 and b is B() and c is C() then 0 //│ ╔══[ERROR] missing else branch -//│ ║ l.94: == 0 and b is B() and c is C() then 0 +//│ ║ l.85: == 0 and b is B() and c is C() then 0 //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.94: == 0 and b is B() and c is C() then 0 +//│ ║ l.85: == 0 and b is B() and c is C() then 0 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, B, C) -> 0 diff --git a/shared/src/test/diff/ucs/SplitScrutinee.mls b/shared/src/test/diff/ucs/SplitScrutinee.mls index 0ecc291f..a5f217a6 100644 --- a/shared/src/test/diff/ucs/SplitScrutinee.mls +++ b/shared/src/test/diff/ucs/SplitScrutinee.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs fun f(x) = if x + diff --git a/shared/src/test/diff/ucs/ThenIndent.mls b/shared/src/test/diff/ucs/ThenIndent.mls index 138c3f02..91e3812e 100644 --- a/shared/src/test/diff/ucs/ThenIndent.mls +++ b/shared/src/test/diff/ucs/ThenIndent.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs // FIXME diff --git a/shared/src/test/diff/ucs/Tree.mls b/shared/src/test/diff/ucs/Tree.mls index a36c7490..28026141 100644 --- a/shared/src/test/diff/ucs/Tree.mls +++ b/shared/src/test/diff/ucs/Tree.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs type Option[out A] = Some[A] | None class Some[out A](value: A) diff --git a/shared/src/test/diff/ucs/TrivialIf.mls b/shared/src/test/diff/ucs/TrivialIf.mls index 1a34cf36..d4c83dc9 100644 --- a/shared/src/test/diff/ucs/TrivialIf.mls +++ b/shared/src/test/diff/ucs/TrivialIf.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs :NoJS fun abs(x) = if x < 0 then 0 - x else x diff --git a/shared/src/test/diff/ucs/WeirdIf.mls b/shared/src/test/diff/ucs/WeirdIf.mls index a7fed4a0..2c1c5c1f 100644 --- a/shared/src/test/diff/ucs/WeirdIf.mls +++ b/shared/src/test/diff/ucs/WeirdIf.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs :w if diff --git a/shared/src/test/diff/ucs/WeirdSplit.mls b/shared/src/test/diff/ucs/WeirdSplit.mls index 259ec793..cca0a62c 100644 --- a/shared/src/test/diff/ucs/WeirdSplit.mls +++ b/shared/src/test/diff/ucs/WeirdSplit.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs class A() class B() diff --git a/shared/src/test/diff/ucs/Wildcard.mls b/shared/src/test/diff/ucs/Wildcard.mls index ef826d9f..006b99be 100644 --- a/shared/src/test/diff/ucs/Wildcard.mls +++ b/shared/src/test/diff/ucs/Wildcard.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs fun (++) strcat(a, b) = concat(a)(b) //│ fun (++) strcat: (Str, Str) -> Str diff --git a/shared/src/test/diff/ucs/zipWith.mls b/shared/src/test/diff/ucs/zipWith.mls index abc77baa..b26e9e8e 100644 --- a/shared/src/test/diff/ucs/zipWith.mls +++ b/shared/src/test/diff/ucs/zipWith.mls @@ -1,4 +1,4 @@ -:PreTyper +:NewDefs diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index ee15d575..e164c352 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -150,7 +150,7 @@ class DiffTests explainErrors: Bool = false, dbg: Bool = false, dbgParsing: Bool = false, - dbgPreTyper: Opt[Set[Str]] = N, + dbgNuUCS: Opt[Set[Str]] = N, dbgSimplif: Bool = false, dbgUCS: Bool = false, fullExceptionStack: Bool = false, @@ -189,7 +189,8 @@ class DiffTests var noRecursiveTypes = false var constrainedTypes = false var irregularTypes = false - var usePreTyper = false + // Enable this to see the errors from unfinished `PreTyper`. + var showPreTyperErrors = false // * This option makes some test cases pass which assume generalization should happen in arbitrary arguments // * but it's way too aggressive to be ON by default, as it leads to more extrusion, cycle errors, etc. @@ -214,7 +215,7 @@ class DiffTests case "p" => mode.copy(showParse = true) case "d" => mode.copy(dbg = true) case "dp" => mode.copy(dbgParsing = true) - case PreTyperFlags(ts) => mode.copy(dbgPreTyper = mode.dbgPreTyper.fold(S(ts))(ts0 => S(ts0 ++ ts))) + case DebugUCSFlags(ts) => mode.copy(dbgNuUCS = mode.dbgNuUCS.fold(S(ts))(ts0 => S(ts0 ++ ts))) case "ds" => mode.copy(dbgSimplif = true) case "ducs" => mode.copy(dbg = true, dbgUCS = true) case "s" => mode.copy(fullExceptionStack = true) @@ -259,9 +260,8 @@ class DiffTests // println("'"+line.drop(str.length + 2)+"'") typer.startingFuel = line.drop(str.length + 2).toInt; mode case "ResetFuel" => typer.startingFuel = typer.defaultStartingFuel; mode - // I believe `PreTyper` will become a part of new definition typing. - // So, this will be removed after `PreTyper` is done. - case "PreTyper" => newParser = true; newDefs = true; usePreTyper = true; mode + // Enable this to see the errors from unfinished `PreTyper`. + case "ShowPreTyperErrors" => newParser = true; newDefs = true; showPreTyperErrors = true; mode case "ne" => mode.copy(noExecution = true) case "ng" => mode.copy(noGeneration = true) case "js" => mode.copy(showGeneratedJS = true) @@ -524,14 +524,15 @@ class DiffTests val (typeDefs, stmts, newDefsResults) = if (newDefs) { val vars: Map[Str, typer.SimpleType] = Map.empty val rootTypingUnit = TypingUnit(p.tops) - if (usePreTyper) { - val preTyper = new PreTyper(mode.dbgPreTyper) { - override def emitString(str: String): Unit = output(str) - } - // This should be passed to code generation somehow. - preTyperScope = preTyper.process(rootTypingUnit, preTyperScope, "")._1 - report(preTyper.getDiagnostics) + val preTyper = new PreTyper { + override def debugTopicFilters = mode.dbgNuUCS + override def emitString(str: String): Unit = output(str) } + // This scope will be passed to typer and code generator after + // pretyper is completed. + preTyperScope = preTyper(rootTypingUnit, preTyperScope, "") + report(preTyper.filterDiagnostics(_.source is Diagnostic.Desugaring)) + if (showPreTyperErrors) report(preTyper.filterDiagnostics(_.source is Diagnostic.PreTyping)) val tpd = typer.typeTypingUnit(rootTypingUnit, N)(ctx, raise, vars) def showTTU(ttu: typer.TypedTypingUnit, ind: Int): Unit = { @@ -1134,7 +1135,7 @@ object DiffTests { // file.segments.toList.init.lastOption.contains("parser") } - object PreTyperFlags { + object DebugUCSFlags { private val pattern = "^ucs(?::\\s*([A-Za-z\\.-]+)(,\\s*[A-Za-z\\.-]+)*)?$".r def unapply(flags: Str): Opt[Set[Str]] = flags match { From 5a1c3957ce6b0e31fe48fb8ce0b8feaee664e166 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Tue, 16 Jan 2024 18:25:15 +0800 Subject: [PATCH 072/143] Update test files in compiler sub-project --- compiler/shared/test/diff/mono.mls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/shared/test/diff/mono.mls b/compiler/shared/test/diff/mono.mls index e2c65fdc..5719a273 100644 --- a/compiler/shared/test/diff/mono.mls +++ b/compiler/shared/test/diff/mono.mls @@ -1,5 +1,5 @@ -:PreTyper +:NewDefs :mono fun f(x: Int) = if not of x == 0 then 42 else 1337 From bab3fab54b676cdb32a7c28bc691536cc21ad40a Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Tue, 16 Jan 2024 18:27:09 +0800 Subject: [PATCH 073/143] Woo-hoo! Eject the old UCS desugarer --- shared/src/main/scala/mlscript/Typer.scala | 6 +- .../scala/mlscript/pretyper/PreTyper.scala | 4 +- .../ucs/{DesugarUCS.scala => Desugarer.scala} | 11 +- .../mlscript/ucs/DesugaringException.scala | 8 - .../main/scala/mlscript/ucs/PartialTerm.scala | 65 - .../src/main/scala/mlscript/ucs/helpers.scala | 24 +- .../main/scala/mlscript/ucs/old/Clause.scala | 78 -- .../scala/mlscript/ucs/old/Conjunction.scala | 128 -- .../scala/mlscript/ucs/old/Desugarer.scala | 1043 ----------------- .../scala/mlscript/ucs/old/LetBinding.scala | 46 - .../scala/mlscript/ucs/old/MutCaseOf.scala | 590 ---------- .../scala/mlscript/ucs/old/Scrutinee.scala | 30 - .../main/scala/mlscript/ucs/old/helpers.scala | 47 - .../src/main/scala/mlscript/ucs/package.scala | 19 +- .../ucs/stages/CoverageChecking.scala | 4 +- .../mlscript/ucs/stages/Desugaring.scala | 18 +- .../mlscript/ucs/stages/Normalization.scala | 4 +- .../mlscript/ucs/stages/PartialTerm.scala | 48 + .../mlscript/ucs/stages/PostProcessing.scala | 4 +- .../mlscript/ucs/stages/Transformation.scala | 4 +- .../scala/mlscript/ucs/stages/package.scala | 28 +- .../pretyper/ucs/coverage/SealedClasses.mls | 4 +- .../diff/pretyper/ucs/examples/AVLTree.mls | 2 +- .../pretyper/ucs/stages/Normalization.mls | 2 +- .../pretyper/ucs/stages/PostProcessing.mls | 8 +- shared/src/test/diff/ucs/Hygiene.mls | 2 +- shared/src/test/diff/ucs/HygienicBindings.mls | 8 +- shared/src/test/diff/ucs/InterleavedLet.mls | 2 +- shared/src/test/diff/ucs/Wildcard.mls | 2 +- .../src/test/scala/mlscript/DiffTests.scala | 14 +- 30 files changed, 134 insertions(+), 2119 deletions(-) rename shared/src/main/scala/mlscript/ucs/{DesugarUCS.scala => Desugarer.scala} (97%) delete mode 100644 shared/src/main/scala/mlscript/ucs/DesugaringException.scala delete mode 100644 shared/src/main/scala/mlscript/ucs/PartialTerm.scala delete mode 100644 shared/src/main/scala/mlscript/ucs/old/Clause.scala delete mode 100644 shared/src/main/scala/mlscript/ucs/old/Conjunction.scala delete mode 100644 shared/src/main/scala/mlscript/ucs/old/Desugarer.scala delete mode 100644 shared/src/main/scala/mlscript/ucs/old/LetBinding.scala delete mode 100644 shared/src/main/scala/mlscript/ucs/old/MutCaseOf.scala delete mode 100644 shared/src/main/scala/mlscript/ucs/old/Scrutinee.scala delete mode 100644 shared/src/main/scala/mlscript/ucs/old/helpers.scala create mode 100644 shared/src/main/scala/mlscript/ucs/stages/PartialTerm.scala diff --git a/shared/src/main/scala/mlscript/Typer.scala b/shared/src/main/scala/mlscript/Typer.scala index da37ad1c..402d58c8 100644 --- a/shared/src/main/scala/mlscript/Typer.scala +++ b/shared/src/main/scala/mlscript/Typer.scala @@ -15,7 +15,7 @@ import mlscript.Message._ * In order to turn the resulting CompactType into a mlscript.Type, we use `expandCompactType`. */ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val newDefs: Bool) - extends mlscript.ucs.old.Desugarer with TypeSimplifier { + extends TypeDefs with TypeSimplifier { def funkyTuples: Bool = false def doFactorize: Bool = false @@ -1269,9 +1269,7 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne case elf: If => elf.desugaredTerm match { case S(desugared) => typeTerm(desugared) - case N => try typeTerm(desugarIf(elf)) catch { - case e: ucs.DesugaringException => err(e.messages) - } + case N => err(msg"not desugared UCS term found", elf.toLoc) } case AdtMatchWith(cond, arms) => println(s"typed condition term ${cond}") diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index a835a190..072c57b9 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -1,9 +1,9 @@ package mlscript.pretyper import annotation.tailrec, collection.mutable.{Set => MutSet}, collection.immutable.SortedMap, util.chaining._ -import mlscript._, utils._, shorthands._, Diagnostic.PreTyping, Message.MessageContext, symbol._, ucs.DesugarUCS +import mlscript._, utils._, shorthands._, Diagnostic.PreTyping, Message.MessageContext, symbol._, ucs.Desugarer -class PreTyper extends Traceable with Diagnosable with DesugarUCS { +class PreTyper extends Traceable with Diagnosable with Desugarer { import PreTyper._ /** A shorthand function to raise errors without specifying the source. */ diff --git a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala b/shared/src/main/scala/mlscript/ucs/Desugarer.scala similarity index 97% rename from shared/src/main/scala/mlscript/ucs/DesugarUCS.scala rename to shared/src/main/scala/mlscript/ucs/Desugarer.scala index 35ff3779..bbf4effb 100644 --- a/shared/src/main/scala/mlscript/ucs/DesugarUCS.scala +++ b/shared/src/main/scala/mlscript/ucs/Desugarer.scala @@ -11,13 +11,12 @@ import syntax.core.{Branch, Split} /** * The main class of the UCS desugaring. - * TODO: Rename to `Desugarer` once the old desugarer is removed. */ -trait DesugarUCS extends Transformation - with Desugaring - with Normalization - with PostProcessing - with CoverageChecking { self: PreTyper => +trait Desugarer extends Transformation + with Desugaring + with Normalization + with PostProcessing + with CoverageChecking { self: PreTyper => /** A shorthand function to raise _desugaring_ errors without specifying the source. */ protected def raiseDesugaringError(messages: (Message -> Opt[Loc])*): Unit = diff --git a/shared/src/main/scala/mlscript/ucs/DesugaringException.scala b/shared/src/main/scala/mlscript/ucs/DesugaringException.scala deleted file mode 100644 index 1e99cdcc..00000000 --- a/shared/src/main/scala/mlscript/ucs/DesugaringException.scala +++ /dev/null @@ -1,8 +0,0 @@ -package mlscript.ucs - -import mlscript.{Diagnostic, Loc, Message, Typer} -import mlscript.utils.shorthands._ - -class DesugaringException(val messages: Ls[Message -> Opt[Loc]]) extends Throwable { - def this(message: Message, location: Opt[Loc]) = this(message -> location :: Nil) -} diff --git a/shared/src/main/scala/mlscript/ucs/PartialTerm.scala b/shared/src/main/scala/mlscript/ucs/PartialTerm.scala deleted file mode 100644 index 40a9767a..00000000 --- a/shared/src/main/scala/mlscript/ucs/PartialTerm.scala +++ /dev/null @@ -1,65 +0,0 @@ -package mlscript.ucs - -import mlscript._ -import mlscript.utils.shorthands._ - -import helpers._ -import mlscript.ucs.PartialTerm.Empty -import mlscript.ucs.PartialTerm.Total -import mlscript.ucs.PartialTerm.Half - -class PartialTermError(term: PartialTerm, message: Str) extends Error(message) - -/** - * A `PartialTerm` represents a possibly incomplete term. - * We'd better precisely track detailed locations of each parts. - * - * @param fragments fragment terms that used to build this `PartialTerm`. - */ -sealed abstract class PartialTerm { - val fragments: Ls[Term] - def addTerm(term: Term, newDefs: Bool): PartialTerm.Total - def addOp(op: Var): PartialTerm.Half - def addTermOp(term: Term, op: Var, newDefs: Bool): PartialTerm.Half = - this.addTerm(term, newDefs).addOp(op) - def addOpTerm(op: Var, term: Term, newDefs: Bool): PartialTerm.Total = - this.addOp(op).addTerm(term, newDefs) - def get: Term = this match { - case Empty => throw new PartialTermError(this, "expect a term but nothing was given") - case Total(term, fragments) => term - case Half(lhs, op, fragments) => throw new PartialTermError(this, "incomplete term") - } - override def toString(): String = this match { - case Empty => "" - case Total(term, fragments) => s" $term" - case Half(lhs, op, fragments) => s" $lhs $op" - } -} - -object PartialTerm { - final case object Empty extends PartialTerm { - val fragments: Ls[Term] = Nil - def addTerm(term: Term, newDefs: Bool): Total = Total(term, term :: Nil) - def addOp(op: Var): Half = - throw new PartialTermError(this, s"expect a term but operator $op was given") - } - - final case class Total(term: Term, fragments: Ls[Term]) extends PartialTerm { - def addTerm(term: Term, newDefs: Bool): Total = - throw new PartialTermError(this, s"expect an operator but term $term was given") - def addOp(op: Var): Half = Half(term, op, op :: fragments) - } - - final case class Half(lhs: Term, op: Var, fragments: Ls[Term]) extends PartialTerm { - def addTerm(rhs: Term, newDefs: Bool): Total = { - val (realRhs, extraExprOpt) = separatePattern(rhs, newDefs) - val leftmost = mkBinOp(lhs, op, realRhs, newDefs) - extraExprOpt match { - case N => Total(leftmost, fragments) - case S(extraExpr) => Total(mkBinOp(leftmost, Var("and"), extraExpr, newDefs), extraExpr :: fragments) - } - } - def addOp(op: Var): Half = - throw new PartialTermError(this, s"expect a term but operator $op was given") - } -} diff --git a/shared/src/main/scala/mlscript/ucs/helpers.scala b/shared/src/main/scala/mlscript/ucs/helpers.scala index f8ed428b..da9b3029 100644 --- a/shared/src/main/scala/mlscript/ucs/helpers.scala +++ b/shared/src/main/scala/mlscript/ucs/helpers.scala @@ -6,29 +6,7 @@ import mlscript._ import mlscript.utils.shorthands._ object helpers { - /** - * Make a tuple with only one element. For example, - * - * ```scala - * mkMonuple(t) = Tup(N -> Fld(false, false, t) :: Nil) - * ``` - * - * @param t the sole element - * @return a tuple term with the only element - */ - def mkMonuple(t: Term): Tup = Tup(N -> Fld(FldFlags.empty, t) :: Nil) - - /** - * Make a binary operation. - * - * @param lhs the left-hand side term - * @param op the operator - * @param rhs the right-hand side term - * @return something like `App(App(op, lhs), rhs)` - */ - def mkBinOp(lhs: Term, op: Var, rhs: Term, newDefs: Bool): Term = - if (newDefs) App(op, PlainTup(lhs, rhs)) - else App(App(op, mkMonuple(lhs)), mkMonuple(rhs)) + import stages.mkBinOp /** * Split a term into two parts: the pattern and the extra test. diff --git a/shared/src/main/scala/mlscript/ucs/old/Clause.scala b/shared/src/main/scala/mlscript/ucs/old/Clause.scala deleted file mode 100644 index c9f6243a..00000000 --- a/shared/src/main/scala/mlscript/ucs/old/Clause.scala +++ /dev/null @@ -1,78 +0,0 @@ -package mlscript.ucs.old - -import mlscript._ -import mlscript.utils._ -import mlscript.utils.shorthands._ -import scala.collection.mutable.Buffer - -/** - * A `Clause` represents a minimal unit of logical predicate in the UCS. - * There are three kinds of clauses: boolean test, class match, and tuple match. - */ -sealed abstract class Clause { - /** - * Local interleaved let bindings declared before this condition. - */ - var bindings: Ls[LetBinding] = Nil - - /** - * Locations of terms that build this `Clause`. - * - * @return - */ - val locations: Ls[Loc] - - protected final def bindingsToString: String = - if (bindings.isEmpty) "" else " with " + (bindings match { - case Nil => "" - case bindings => bindings.map(_.name.name).mkString("(", ", ", ")") - }) -} - -sealed abstract class MatchClause extends Clause { - val scrutinee: Scrutinee -} - -object Clause { - final case class MatchLiteral( - override val scrutinee: Scrutinee, - literal: SimpleTerm - )(override val locations: Ls[Loc]) extends MatchClause { - override def toString: String = s"«$scrutinee is $literal" + bindingsToString - } - - final case class MatchAny(override val scrutinee: Scrutinee)(override val locations: Ls[Loc]) extends MatchClause { - override def toString: String = s"«$scrutinee is any" + bindingsToString - } - - final case class MatchClass( - override val scrutinee: Scrutinee, - className: Var, - fields: Ls[Str -> Var] - )(override val locations: Ls[Loc]) extends MatchClause { - 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 def toString: String = s"«$scrutinee is Tuple#$arity»" + bindingsToString - } - - final case class BooleanTest(test: Term)( - override val locations: Ls[Loc] - ) extends Clause { - override def toString: String = s"«$test»" + bindingsToString - } - - /** - * @param isField whether this binding is extracting a class field - */ - final case class Binding(name: Var, term: Term, isField: Bool)( - override val locations: Ls[Loc] - ) extends Clause { - override def toString: String = s"«$name = $term»" + bindingsToString - } -} diff --git a/shared/src/main/scala/mlscript/ucs/old/Conjunction.scala b/shared/src/main/scala/mlscript/ucs/old/Conjunction.scala deleted file mode 100644 index 1cfbf1f6..00000000 --- a/shared/src/main/scala/mlscript/ucs/old/Conjunction.scala +++ /dev/null @@ -1,128 +0,0 @@ -package mlscript.ucs.old - -import mlscript._, utils._, shorthands._ -import Clause._, helpers._ -import scala.collection.mutable.Buffer -import scala.annotation.tailrec - -/** - * A `Conjunction` represents a list of `Clause`s. - */ -final case class Conjunction(clauses: Ls[Clause], trailingBindings: Ls[LetBinding]) { - override def toString: String = - clauses.mkString("", " and ", "") + { - (if (trailingBindings.isEmpty) "" else " ") + - (trailingBindings match { - case Nil => "" - case bindings => bindings.map(_.name.name).mkString("(", ", ", ")") - }) - } - - /** - * Concatenate two `Conjunction` together. - * - * The trailing bindings of the first `Conjunction` will be added to the - * first `Clause` of the second `Conjunction` - * - * @param lhs the left hand side value - * @param rhs the right hand side value - * @return the sititched `Conjunction` - */ - def +(rhs: Conjunction): Conjunction = { - val Conjunction(lhsClauses, lhsTailBindings) = this - val Conjunction(rhsClauses, rhsTailBindings) = rhs - rhsClauses match { - case Nil => Conjunction(lhsClauses, lhsTailBindings ::: rhsTailBindings) - case head :: _ => - head.bindings = lhsTailBindings ::: head.bindings - Conjunction(lhsClauses ::: rhsClauses, rhsTailBindings) - } - } - - /** - * This is a shorthand if you only have clauses. - * - * @param suffix the list of clauses to append to this conjunction - * @return a new conjunction with clauses from `this` and `suffix` - */ - def +(suffix: Ls[Clause]): Conjunction = { - suffix match { - case Nil => this - case head :: _ => - head.bindings = trailingBindings ::: head.bindings - Conjunction(clauses ::: suffix, Nil) - } - } - - /** - * This is a shorthand if you only have one clause. - * - * @param last the list of clauses to append to this conjunction - * @return a new conjunction with clauses from `this` and `last` - */ - def +(last: Clause): Conjunction = { - last.bindings = trailingBindings ::: last.bindings - Conjunction(clauses :+ last, Nil) - } - - /** - * This is a shorthand if you only have the last binding. - * - * @param suffix the list of clauses to append to this conjunction - * @return a new conjunction with clauses from `this` and `suffix` - */ - def +(lastBinding: LetBinding): Conjunction = - Conjunction(clauses, trailingBindings :+ lastBinding) - - def findClauseMatches(expectedScrutinee: Scrutinee): Opt[(MatchClause, Conjunction)] = { - @tailrec - def rec(past: Ls[Clause], upcoming: Ls[Clause], firstAny: Opt[(Ls[Clause], MatchAny, Ls[Clause])]): Opt[(Ls[Clause], MatchClause, Ls[Clause])] = { - upcoming match { - case Nil => firstAny - case (head @ MatchLiteral(scrutinee, _)) :: tail => - if (scrutinee === expectedScrutinee) { - S((past, head, tail)) - } else { - rec(past :+ head, tail, firstAny) - } - case (head @ MatchClass(scrutinee, _, _)) :: tail => - if (scrutinee === expectedScrutinee) { - S((past, head, tail)) - } else { - rec(past :+ head, tail, firstAny) - } - case (head @ MatchAny(scrutinee)) :: tail => - if (scrutinee === expectedScrutinee) { - rec(past, tail, firstAny.orElse(S((past, head, tail)))) - } else { - rec(past :+ head, tail, firstAny) - } - case head :: tail => - rec(past :+ head, tail, firstAny) - } - } - - rec(Nil, clauses, None).map { case (past, wanted, remaining) => - (wanted, Conjunction(past ::: remaining, trailingBindings)) - } - } - - /** - * Prepend bindings to the first condition of this conjunction. - * - * @param interleavedLets the buffer of let bindings in the current context - * @return idential to `conditions` - */ - def withBindings(implicit interleavedLets: Buffer[LetBinding]): Conjunction = { - clauses match { - case Nil => Conjunction(Nil, interleavedLets.toList ::: trailingBindings) - case head :: _ => - head.bindings = head.bindings ::: interleavedLets.toList - this - } - } -} - -object Conjunction { - def empty: Conjunction = Conjunction(Nil, Nil) -} diff --git a/shared/src/main/scala/mlscript/ucs/old/Desugarer.scala b/shared/src/main/scala/mlscript/ucs/old/Desugarer.scala deleted file mode 100644 index 92b18dd3..00000000 --- a/shared/src/main/scala/mlscript/ucs/old/Desugarer.scala +++ /dev/null @@ -1,1043 +0,0 @@ -package mlscript.ucs.old - -import scala.collection.mutable.{Map => MutMap, HashMap} -import scala.collection.mutable.Buffer - -import mlscript._, utils._, shorthands._ -import mlscript.ucs.{DesugaringException, PartialTerm} -import mlscript.ucs.helpers._, helpers._ -import Message.MessageContext -import MutCaseOf.MutCase.Constructor -import scala.collection.mutable.ListBuffer - -/** - * This class contains main desugaring methods. - */ -class Desugarer extends TypeDefs { self: Typer => - var dbgUCS: Bool = false - private def printlnUCS(msg: => Any): Unit = if (dbgUCS) println(msg) - private def traceUCS[T](pre: => String)(thunk: => T)(post: T => String = noPostTrace) = - if (dbgUCS) trace(pre)(thunk)(post) else thunk - - import Desugarer.{ExhaustivenessMap, SubClassMap, SuperClassMap} - import Clause.{MatchClass, MatchTuple, BooleanTest} - - type FieldAliasMap = MutMap[SimpleTerm, MutMap[Str, Var]] - - private var idLength: Int = 0 - - private def makeName: String = { - val name = s"tmp$idLength" - idLength += 1 - name - } - - private def freshName(implicit ctx: Ctx): String = { - var res = makeName - while (ctx.env.contains(res)) { - res = makeName - } - res - } - - private type MutExhaustivenessMap = MutMap[Str \/ Int, MutMap[Either[Int, SimpleTerm], Buffer[Loc]]] - - private def addToExhaustivenessMap(scrutinee: Scrutinee, loc: Iterable[Loc]) - (implicit ctx: Ctx, raise: Raise, map: MutExhaustivenessMap) = { - map.getOrElseUpdate(getScurtineeKey(scrutinee), MutMap.empty) - } - - private def addToExhaustivenessMap(scrutinee: Scrutinee, tupleArity: Int, loc: Iterable[Loc]) - (implicit ctx: Ctx, raise: Raise, map: MutExhaustivenessMap) = { - map.getOrElseUpdate(getScurtineeKey(scrutinee), MutMap.empty) - .getOrElseUpdate(L(tupleArity), Buffer.empty) ++= loc - } - private def addToExhaustivenessMap(scrutinee: Scrutinee, litOrCls: SimpleTerm, loc: Iterable[Loc]) - (implicit ctx: Ctx, raise: Raise, map: MutExhaustivenessMap) = { - map.getOrElseUpdate(getScurtineeKey(scrutinee), MutMap.empty) - .getOrElseUpdate(R(litOrCls), Buffer.empty) ++= loc - } - - /** - * - * - * @param scrutinee the scrutinee of the pattern matching - * @param params parameters provided by the - * @param positionals the corresponding field names of each parameter - * @param aliasMap a map used to cache each the alias of each field - * @param matchRootLoc the location to the root of the match - * @return two mappings: one is (variable -> sub-pattern), the other is (positional name -> variable) - */ - private def desugarPositionals - (scrutinee: Scrutinee, params: IterableOnce[Term], positionals: Ls[Str]) - (implicit ctx: Ctx, aliasMap: FieldAliasMap): (Ls[Var -> Term], Ls[Str -> Var]) = { - val subPatterns = Buffer.empty[(Var, Term)] - val bindings = params.iterator.zip(positionals).flatMap { - // `x is A(_)`: ignore this binding - case (Var("_"), fieldName) => S(fieldName -> Var("_")) - // `x is A(value)`: generate bindings directly - case (nameVar @ Var(n), fieldName) if (n.headOption.exists(_.isLower)) => - S(fieldName -> nameVar) - // `x is B(A(x))`: generate a temporary name - // use the name in the binding, and destruct sub-patterns - case (pattern: Term, fieldName) => - // We should always use the same temporary for the same `fieldName`. - // This uniqueness is decided by (scrutinee, fieldName). - val alias = aliasMap - .getOrElseUpdate(scrutinee.reference, MutMap.empty) - .getOrElseUpdate(fieldName, Var(freshName).desugaredFrom(pattern)) - subPatterns += ((alias, pattern)) - S(fieldName -> alias) - }.toList - (subPatterns.toList, bindings) - } - - /** - * Desugar sub-patterns from fields to conditions. - * - * @param subPatterns a list of field name -> pattern term - * @param ctx the typing context - * @param aliasMap the field alias map - * @return desugared conditions representing the sub-patterns - */ - private def destructSubPatterns(scrutinee: Scrutinee, subPatterns: Iterable[Var -> Term]) - (implicit ctx: Ctx, raise: Raise, exhaustivenessMap: MutExhaustivenessMap, aliasMap: FieldAliasMap): Ls[Clause] = { - subPatterns.iterator.flatMap[Clause] { case (subScrutinee, subPattern) => - destructPattern(makeScrutinee(subScrutinee, scrutinee.matchRootLoc), subPattern, false) - }.toList - } - - // `IdentityHashMap` is a workaround. - private val localizedScrutineeMap = new java.util.IdentityHashMap[Term, Var] - - /** - * Create a `Scrutinee`. If the `term` is a simple expression (e.g. `Var` or - * `Lit`), we do not create a local alias. Otherwise, we create a local alias - * to avoid unnecessary computations. - * - * @param term the term in the local scrutinee position - * @param matchRootLoc the caller is expect to be in a match environment, - * this parameter indicates the location of the match root - */ - private def makeScrutinee(term: Term, matchRootLoc: Opt[Loc])(implicit ctx: Ctx): Scrutinee = - traceUCS(s"Making a scrutinee for `$term`") { - term match { - 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) - } - }() - - /** - * Create a fresh name for scrutinee to be localized. - * - * @param scrutinee the term of the scrutinee - * @param ctx the context - * @return the fresh name, as `Var` - */ - private def makeLocalizedName(scrutinee: Term)(implicit ctx: Ctx): Var = - if (localizedScrutineeMap.containsKey(scrutinee)) { - localizedScrutineeMap.get(scrutinee) - } else { - val v = Var(freshName).desugaredFrom(scrutinee) - localizedScrutineeMap.put(scrutinee, v) - v - } - - /** - * Destruct nested patterns to a list of simple condition with bindings. - * - * @param scrutinee the scrutinee of the pattern matching - * @param pattern the pattern we will destruct - * @param isTopLevel whether this pattern just follows the `is` operator - * @param raise the `Raise` function - * @param aliasMap the field alias map - * @param matchRootLoc the location of the root of the pattern matching - * @param fragments fragment term that used to construct the given pattern. - * It is used to tracking locations. - * @return a list of simple condition with bindings. This method does not - * return `ConjunctedCondition` because conditions built from nested patterns - * do not contain interleaved let bindings. - */ - private def destructPattern - (scrutinee: Scrutinee, pattern: Term, isTopLevel: Bool) - (implicit ctx: Ctx, - raise: Raise, - exhaustivenessMap: MutExhaustivenessMap, - aliasMap: FieldAliasMap, - fragments: Ls[Term] = Nil): Ls[Clause] = - trace(s"[Desugarer.destructPattern] scrutinee = ${scrutinee.term}; pattern = $pattern") { - // This piece of code is use in two match cases. - def desugarTuplePattern(tuple: Tup): Ls[Clause] = { - val (subPatterns, bindings) = desugarPositionals( - scrutinee, - tuple.fields.iterator.map(_._2.value), - 1.to(tuple.fields.length).map("_" + _).toList - ) - addToExhaustivenessMap(scrutinee, tuple.fields.length, tuple.toLoc) - Clause.MatchTuple( - scrutinee, - tuple.fields.length, - bindings - )(collectLocations(scrutinee.term)) :: destructSubPatterns(scrutinee, subPatterns) - } - pattern match { - // This case handles top-level wildcard `Var`. - // We don't make any conditions in this level. - case wildcard @ Var("_") if isTopLevel => - addToExhaustivenessMap(scrutinee, wildcard.toLoc) - Clause.MatchAny(scrutinee)(wildcard.toLoc.toList) :: Nil - // If it's not top-level, wildcard means we don't care. - case Var("_") => Nil - // This case handles literals. - // x is true | x is false | x is 0 | x is "text" | ... - case literal: Var if literal.name === "true" || literal.name === "false" => - addToExhaustivenessMap(scrutinee, literal, literal.toLoc) - 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 => - addToExhaustivenessMap(scrutinee, literal, literal.toLoc) - 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 - // This case handles name binding. - // x is a - case bindingVar @ Var(bindingName) if bindingName.headOption.exists(_.isLower) => - val locations = scrutinee.term.toLoc.toList ::: bindingVar.toLoc.toList - if (isTopLevel) { - // If the binding name is at the top-level. We create decision path like - // ... /\ x is any /\ a = x /\ ... - addToExhaustivenessMap(scrutinee, bindingVar.toLoc) - Clause.MatchAny(scrutinee)(locations) :: - Clause.Binding(bindingVar, scrutinee.reference, !isTopLevel)(locations) :: - Nil - } else { - // Otherwise, we just create the binding. - Clause.Binding(bindingVar, scrutinee.term, !isTopLevel)(locations) :: Nil - } - // This case handles simple class tests. - // x is A - case classNameVar @ Var(className) => - ctx.tyDefs.get(className).orElse(ctx.get(className)) match { - case S(ti: LazyTypeInfo) if (ti.kind is Cls) || (ti.kind is Mod) => - case S(ti: LazyTypeInfo) if (ti.kind is Trt) => throw new DesugaringException({ - msg"Cannot match on trait `$className`" - }, classNameVar.toLoc) - case S(_: TypeDef) => - case _ => throw new DesugaringException({ - msg"Cannot find constructor `$className` in scope" - }, classNameVar.toLoc) - } - printlnUCS(s"Build a Clause.MatchClass from $scrutinee where pattern is $classNameVar") - addToExhaustivenessMap(scrutinee, classNameVar, classNameVar.toLoc) - Clause.MatchClass(scrutinee, classNameVar, Nil)(collectLocations(scrutinee.term)) :: Nil - // This case handles classes with destruction. - // x is A(r, s, t) - case app @ App(classNameVar @ Var(className), Tup(args)) => - ctx.tyDefs.get(className).map(td => (td.kind, td.positionals)) - .orElse(ctx.get(className) match { - case S(ti: DelayedTypeInfo) if ti.decl.kind is Cls => - S((ti.decl.kind, ti.typedParams.getOrElse(Nil).map(_._1.name))) // * Error should be caught before if this doesn't take params - case S(CompletedTypeInfo(td: TypedNuCls)) => - S((td.decl.kind, td.params.getOrElse(Nil).map(_._1.name))) // * Error should be caught before if this doesn't take params - case _ => throw new DesugaringException(msg"Illegal pattern `$className`", classNameVar.toLoc) - }) match { - case N => - throw new DesugaringException({ - msg"Cannot find class `$className` in scope" - }, classNameVar.toLoc) - case S((kind, positionals)) => - if (args.length === positionals.length) { - val (subPatterns, bindings) = desugarPositionals( - scrutinee, - args.iterator.map(_._2.value), - positionals - ) - addToExhaustivenessMap(scrutinee, classNameVar, app.toLoc) - val clause = Clause.MatchClass(scrutinee, classNameVar, bindings)(pattern.toLoc.toList ::: collectLocations(scrutinee.term)) - printlnUCS(s"Build a Clause.MatchClass from $scrutinee where pattern is $pattern") - printlnUCS(s"Fragments: $fragments") - printlnUCS(s"The locations of the clause: ${clause.locations}") - clause :: destructSubPatterns(scrutinee, subPatterns) - } else { - throw new DesugaringException({ - val expected = positionals.length - val actual = args.length - msg"${kind.str} $className expects ${ - "parameter".pluralize(expected, true) - } but found ${ - "parameter".pluralize(args.length, true) - }" - }, app.toLoc) - } - } - // This case handles operator-like constructors. - // x is head :: Nil - case app @ App( - App( - opVar @ Var(op), - Tup((_ -> Fld(_, lhs)) :: Nil) - ), - Tup((_ -> Fld(_, rhs)) :: Nil) - ) => - ctx.tyDefs.get(op) match { - case N => - throw new DesugaringException({ - msg"Cannot find operator `$op` in the context" - }, opVar.toLoc) - case S(td) if td.positionals.length === 2 => - val (subPatterns, fields) = desugarPositionals( - scrutinee, - lhs :: rhs :: Nil, - td.positionals - ) - addToExhaustivenessMap(scrutinee, opVar, app.toLoc) - val clause = Clause.MatchClass(scrutinee, opVar, fields)(collectLocations(scrutinee.term)) - printlnUCS(s"Build a Clause.MatchClass from $scrutinee where operator is $opVar") - clause :: destructSubPatterns(scrutinee, subPatterns) - case S(td) => - val num = td.positionals.length - throw new DesugaringException({ - val expected = td.positionals.length - msg"${td.kind.str} `$op` expects ${ - "parameter".pluralize(expected, true) - } but found two parameters" - }, app.toLoc) - } - // This case handles **direct** tuple destructions. - // x is (a, b, c) - case Bra(_, tuple: Tup) => desugarTuplePattern(tuple) - // This case handles **nested** tuple destructions. - // x is Cons((x, y), Nil) - case tuple: Tup => desugarTuplePattern(tuple) - // What else? - case _ => throw new DesugaringException(msg"illegal pattern", pattern.toLoc) - } - }("[Desugarer.destructPattern] Result: " + _.mkString(", ")) - - /** - * Collect `Loc`s from a synthetic term. - * - * @param term the root of the synthetic term - * @param fragments the fragment terms - * @return all original locations - */ - private def collectLocations(term: Term)(implicit fragments: Ls[Term]): Ls[Loc] = { - val locations = Buffer.empty[Loc] - def rec(term: Term): Unit = term.children.foreach { located => - if (fragments.contains(located)) locations ++= located.toLoc - } - locations.toList - } - - private def unfoldNestedIf(elf: If, acc: Ls[IfBody] = Nil): (IfBody, Opt[Term]) = - traceUCS("[unfoldNestedIf]") { - elf.els match { - case S(innerElf: If) => unfoldNestedIf(innerElf, elf.body :: acc) - case default if acc.isEmpty => (elf.body, default) - case default => - val lines = (elf.body :: acc).reverseIterator.flatMap { - case IfBlock(subLines) => subLines - case other => Iterable.single(L(other)) - }.toList - (IfBlock(lines), default) - } - }(r => s"[unfoldNestedIf] (${r._1.getClass().getSimpleName()}, ${r._2})") - - /** - * The entry point of UCS desugarer. - * - * @param elf the root `If` term - * @param ctx the typing context - * @param raise the function to raise errors - * @return the desugared term - */ - def desugarIf(elf: If)(implicit ctx: Ctx, raise: Raise): Term = traceUCS("[desugarIf]") { - raise(WarningReport(msg"old desugarer used" -> elf.toLoc :: Nil, false)) - val superClassMap = getClassHierarchy() - Desugarer.printGraph(superClassMap, printlnUCS, "Super-class map", "<:") - val subClassMap = Desugarer.reverseGraph(superClassMap) - Desugarer.printGraph(subClassMap, printlnUCS, "Sub-class map", ":>") - val (body, els) = unfoldNestedIf(elf) - val exhaustivenessMap: MutExhaustivenessMap = MutMap.empty - printlnUCS("### Desugar the UCS to decision paths ###") - val paths = desugarIf(body, els)(ctx, raise, exhaustivenessMap) - printlnUCS("Exhaustiveness map") - if (exhaustivenessMap.isEmpty) - printlnUCS(" * ") - else - exhaustivenessMap.foreachEntry { (symbol, patternMap) => - printlnUCS(s" * Patterns of $symbol") - if (patternMap.isEmpty) - printlnUCS(s" + ") - else - patternMap.foreachEntry { (pattern, locations) => - val first = pattern match { - case Left(tupleArity) => s"()^$tupleArity" - case Right(litOrCls) => litOrCls.showDbg - } - val second = locations.mkString("[", ", ", "]") - printlnUCS(s" + $first -> $second") - } - } - printlnUCS("### Build a case tree from decision paths ###") - val imExhaustivenessMap = Map.from(exhaustivenessMap.iterator.map { case (k, m) => k -> Map.from(m) }) - val caseTree = buildCaseTree(paths)(raise, getScurtineeKey, imExhaustivenessMap, superClassMap) - printlnUCS("### Checking exhaustiveness of the case tree ###") - checkExhaustive(caseTree, N)(ctx, raise, imExhaustivenessMap, subClassMap) - printlnUCS("### Construct a term from the case tree ###") - val desugared = constructTerm(caseTree) - println(s"Desugared term: ${desugared.print(false)}") - elf.desugaredTerm = S(desugared) - desugared - }() - - - private def desugarIf - (body: IfBody, fallback: Opt[Term]) - (implicit ctx: Ctx, raise: Raise, exhaustivenessMap: MutExhaustivenessMap) - : Ls[Conjunction -> Term] = traceUCS(s"[desugarIf] with fallback $fallback") { - // We allocate temporary variable names for nested patterns. - // This prevents aliasing problems. - implicit val scrutineeFieldAliasMap: FieldAliasMap = MutMap.empty - // A list of flattened if-branches. - val branches = Buffer.empty[Conjunction -> Term] - - /** - * Translate a list of atomic UCS conditions. - * What is atomic? No "and". - * - * @param ts a list of atomic UCS conditions - * @return a list of `Condition` - */ - def desugarConditions(ts: Ls[Term])(implicit fragments: Ls[Term] = Nil): Ls[Clause] = - ts.flatMap { - case isApp @ App( - App(Var("is"), - Tup(_ -> Fld(_, scrutinee) :: Nil)), - Tup(_ -> Fld(_, pattern) :: Nil) - ) if !newDefs => // * Old-style operators - // This is an inline `x is Class` match test. - val inlineMatchLoc = isApp.toLoc - val inlineScrutinee = makeScrutinee(scrutinee, inlineMatchLoc) - destructPattern(inlineScrutinee, pattern, true)(ctx, raise, exhaustivenessMap, scrutineeFieldAliasMap) - case isApp @ App(Var("is"), PlainTup(scrutinee, pattern)) => - // This is an inline `x is Class` match test. - val inlineMatchLoc = isApp.toLoc - val inlineScrutinee = makeScrutinee(scrutinee, inlineMatchLoc) - destructPattern(inlineScrutinee, pattern, true)(ctx, raise, exhaustivenessMap, scrutineeFieldAliasMap) - case test => - val clause = Clause.BooleanTest(test)(collectLocations(test)) - Iterable.single(clause) - } - - /** - * Recursively desugar a pattern matching branch. - * - * @param scrutinee the scrutinee of this pattern matching - * @param body one element of `lines` of the `IfBlock` - * @param pat the accumulated pattern, since patterns can be split - * @param acc the accumulated conditions so far - * @param ctx the typing context - * @param interleavedLets interleaved let bindings before this branch - * @param rootLoc the location of the `IfOpApp` - */ - def desugarMatchBranch( - scrutinee: Scrutinee, - body: IfBody \/ Statement, - partialPattern: PartialTerm, - collectedConditions: Conjunction, - )(implicit interleavedLets: Buffer[LetBinding]): Unit = traceUCS[Unit]("[desugarMatchBranch]") { - body match { - // This case handles default branches. For example, - // if x is - // A(...) then ... - // else ... - case L(els @ IfElse(consequent)) => - // Because this pattern matching is incomplete, it's not included in - // `acc`. This means that we discard this incomplete pattern matching. - // branches += (collectedConditions + Clause.MatchNot(scrutinee)(els.toLoc.toList) -> consequent) - branches += (collectedConditions -> consequent) - // This case handles default branches indicated by wildcards. - // if x is - // A(...) then ... - // _ then ... - case L(IfThen(wildcard @ Var("_"), consequent)) => - // branches += (collectedConditions + Clause.MatchNot(scrutinee)(wildcard.toLoc.toList) -> consequent) - branches += (collectedConditions -> consequent) - // if x is - // A(...) then ... // Case 1: no conjunctions - // B(...) and ... then ... // Case 2: more conjunctions - case L(IfThen(patTest, consequent)) => - val (patternPart, extraTestOpt) = separatePattern(patTest, newDefs) - val clauses = destructPattern(scrutinee, partialPattern.addTerm(patternPart, newDefs).term, true) - val conditions = collectedConditions + Conjunction(clauses, Nil).withBindings - printlnUCS(s"Result: " + conditions.clauses.mkString(", ")) - extraTestOpt match { - // Case 1. Just a pattern. Easy! - case N => - branches += (conditions -> consequent) - // Case 2. A pattern and an extra test - case S(extraTest) => - desugarIfBody(IfThen(extraTest, consequent), PartialTerm.Empty, conditions) - } - // if x is - // A(...) and t <> // => IfOpApp(A(...), "and", IfOpApp(...)) - // a then ... - // b then ... - // A(...) and y is // => IfOpApp(A(...), "and", IfOpApp(...)) - // B(...) then ... - // B(...) then ... - case L(IfOpApp(patLhs, Var("and"), consequent)) => - val (pattern, optTests) = separatePattern(patLhs, newDefs) - val patternConditions = destructPattern(scrutinee, pattern, true) - val tailTestConditions = optTests.fold(Nil: Ls[Clause])(x => desugarConditions(splitAnd(x))) - val conditions = - collectedConditions + Conjunction(patternConditions ::: tailTestConditions, Nil).withBindings - desugarIfBody(consequent, PartialTerm.Empty, conditions) - case L(IfOpApp(patLhs, op, consequent)) => - separatePattern(patLhs, newDefs) match { - // Case 1. - // The pattern is completed. There is also a conjunction. - // So, we need to separate the pattern from remaining parts. - case (pattern, S(extraTests)) => - val patternConditions = destructPattern(scrutinee, pattern, true) - val extraConditions = desugarConditions(splitAnd(extraTests)) - val conditions = - collectedConditions + Conjunction(patternConditions ::: extraConditions, Nil).withBindings - desugarIfBody(consequent, PartialTerm.Empty, conditions) - // Case 2. - // The pattern is incomplete. Remaining parts are at next lines. - // if x is - // head :: - // Nil then ... // do something with head - // tail then ... // do something with head and tail - case (patternPart, N) => - desugarMatchBranch(scrutinee, L(consequent), partialPattern.addTermOp(patternPart, op, newDefs), collectedConditions) - } - case L(IfOpsApp(patLhs, opsRhss)) => - separatePattern(patLhs, newDefs) match { - case (patternPart, N) => - val partialPattern2 = partialPattern.addTerm(patternPart, newDefs) - opsRhss.foreach { case op -> consequent => - desugarMatchBranch(scrutinee, L(consequent), partialPattern2.addOp(op), collectedConditions) - } - case (patternPart, S(extraTests)) => - val patternConditions = destructPattern(scrutinee, partialPattern.addTerm(patternPart, newDefs).term, true) - val testTerms = splitAnd(extraTests) - val middleConditions = desugarConditions(testTerms.init) - val conditions = - collectedConditions + Conjunction(patternConditions ::: middleConditions, Nil).withBindings - opsRhss.foreach { case op -> consequent => - // TODO: Use lastOption - val last = testTerms.last - val partialTerm = PartialTerm.Total(last, last :: Nil) - desugarIfBody(consequent, partialTerm, conditions) - } - } - // This case usually happens with pattern split by linefeed. - case L(IfBlock(lines)) => - lines.foreach { desugarMatchBranch(scrutinee, _, partialPattern, collectedConditions) } - // This case is rare. Let's put it aside. - case L(IfLet(_, _, _, _)) => - TODO("please add this rare case to test files") - // This case handles interleaved lets. - case R(NuFunDef(S(isRec), nameVar, _, _, L(term))) => - interleavedLets += (LetBinding(LetBinding.Kind.InterleavedLet, isRec, nameVar, term)) - // Other statements are considered to be ill-formed. - case R(statement) => throw new DesugaringException({ - msg"Illegal interleaved statement ${statement.toString}" - }, statement.toLoc) - } - }(_ => "[desugarMatchBranch]") - - def desugarIfBody - (body: IfBody, expr: PartialTerm, acc: Conjunction) - (implicit interleavedLets: Buffer[LetBinding]) - : Unit = traceUCS[Unit]("[desugarIfBody]") { - body match { - case IfOpsApp(exprPart, opsRhss) => - val exprStart = expr.addTerm(exprPart, newDefs) - opsRhss.foreach { case opVar -> contBody => - desugarIfBody(contBody, exprStart.addOp(opVar), acc) - } - case IfThen(Var("_"), consequent) => - branches += (acc -> consequent) - // The termination case. - case IfThen(term, consequent) => - val totalTerm = expr.addTerm(term, newDefs) - // “Atomic” means terms that do not contain `and`. - val atomicTerms = splitAnd(totalTerm.term) - val fragments = atomicTerms ::: totalTerm.fragments - val newClauses = desugarConditions(atomicTerms)(fragments) - branches += ((acc + newClauses).withBindings -> consequent) - // This is the entrance of the Simple UCS. - case IfOpApp(scrutineePart, isVar @ Var("is"), IfBlock(lines)) => - // Create a synthetic scrutinee term by combining accumulated partial - // term with the new part. - val scrutineeTerm = expr.addTerm(scrutineePart, newDefs).term - // We don't need to include the entire `IfOpApp` because it might be - // very long... Indicating the beginning of the match is enough. - val matchRootLoc = (scrutineeTerm.toLoc, isVar.toLoc) match { - case (S(first), S(second)) => S(first ++ second) - case (_, _) => N - } - val scrutinee = makeScrutinee(scrutineeTerm, matchRootLoc) - // If there is an alias, we should add the let bindings to clauses. - val conjunction = scrutinee.local match { - case S(alias) => acc - case N => acc - } - // We need to make a snapshot because the sub-branches mutate the buffer. - // But these changes should not affect sibling branches. - val interleavedLetsSnapshot = interleavedLets.clone() - // Iterate each match case. - lines.foreach { - desugarMatchBranch(scrutinee, _, PartialTerm.Empty, conjunction)(interleavedLetsSnapshot) - } - // For example: "if x == 0 and y is \n ..." - case IfOpApp(testPart, Var("and"), consequent) => - val conditions = acc + (desugarConditions(expr.addTerm(testPart, newDefs).term :: Nil)) - desugarIfBody(consequent, PartialTerm.Empty, conditions) - // Otherwise, this is not a pattern matching. - // We create a partial term from `lhs` and `op` and dive deeper. - case IfOpApp(lhs, op, body) => - desugarIfBody(body, expr.addTermOp(lhs, op, newDefs), acc) - // This case is rare. Let's put it aside. - case IfLet(isRec, name, rhs, body) => - TODO("please add this rare case to test files") - // In this case, the accumulated partial term is discarded. - // We create a branch directly from accumulated conditions. - case IfElse(term) => branches += (acc.withBindings -> term) - case IfBlock(lines) => - lines.foreach { - case L(subBody) => desugarIfBody(subBody, expr, acc) - case R(NuFunDef(S(isRec), nameVar, _, _, L(term))) => - printlnUCS(s"Found interleaved binding ${nameVar.name}") - interleavedLets += LetBinding(LetBinding.Kind.InterleavedLet, isRec, nameVar, term) - case R(_) => - throw new Error("unexpected statements at desugarIfBody") - } - } - }(_ => "[desugarIfBody]") - - // Top-level interleaved let bindings. - val interleavedLets = Buffer.empty[LetBinding] - desugarIfBody(body, PartialTerm.Empty, Conjunction.empty)(interleavedLets) - // Add the fallback case to conjunctions if there is any. - fallback.foreach { branches += Conjunction.empty -> _ } - printlnUCS("Decision paths:") - branches.foreach { case conjunction -> term => - printlnUCS(s"+ $conjunction => $term") - } - branches.toList - }(r => s"[desugarIf] produces ${r.size} ${"path".pluralize(r.size)}") - - import MutCaseOf.{MutCase, IfThenElse, Match, MissingCase, Consequent} - - /** - * 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 - */ - private 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`. - * - * @param t the mutable `CaseOf` of - * @param parentOpt the parent `MutCaseOf` - * @param scrutineePatternMap the exhaustiveness map - */ - private def checkExhaustive - (t: MutCaseOf, parentOpt: Opt[MutCaseOf]) - (implicit ctx: Ctx, - raise: Raise, - exhaustivenessMap: ExhaustivenessMap, - subClassMap: SubClassMap) - : Unit = traceUCS(s"[checkExhaustive] ${t.describe}") { - t match { - case _: Consequent => () - case MissingCase => - parentOpt match { - case S(IfThenElse(test, whenTrue, whenFalse)) => - if (whenFalse === t) - throw new DesugaringException(msg"The case when this is false is not handled: ${test.showDbg}", test.toLoc) - else - lastWords("`MissingCase` are not supposed to be the true branch of `IfThenElse`") - case S(Match(_, _, _)) => - lastWords("`MissingCase` are not supposed to be a case of `Match`") - case S(Consequent(_)) | S(MissingCase) | N => die // unreachable - } - case IfThenElse(condition, whenTrue, whenFalse) => - checkExhaustive(whenFalse, S(t)) - checkExhaustive(whenTrue, S(t)) - case Match(scrutinee, branches, default) => - exhaustivenessMap.get(getScurtineeKey(scrutinee)) match { - case N => lastWords(s"unreachable case: unknown scrutinee ${scrutinee.term}") - 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") - exhaustivenessMap.foreachEntry { (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) - printlnUCS("") - else - patternMap.foreachEntry { (key, mutCase) => printlnUCS(s"- $key => $mutCase")} - // Compute all classes that can be covered by this match. - val coveredClassNames = Set.from[String](branches.iterator.flatMap { - case MutCase.Literal(_, _) => Nil - case Constructor(Var(className) -> _, _) => - subClassMap.get(className).fold[List[String]](Nil)(identity) - }) - printlnUCS("The match can cover following classes") - printlnUCS(coveredClassNames.mkString("{", ", ", "}")) - // Filter out missing cases in `branches`. - val missingCases = patternMap.removedAll(branches.iterator.map { - case MutCase.Literal(lit, _) => R(lit) - case MutCase.Constructor(classNameVar -> _, _) => - classNameVar.name.split('#').toList match { - case "Tuple" :: ns :: Nil => - ns.toIntOption match { - case N => R(classNameVar) - case S(arity) => L(arity) - } - case _ => R(classNameVar) - } - }).filter { // Remove classes subsumed by super classes. - case R(Var(className)) -> _ => - !coveredClassNames.contains(className) - case L(_) -> _ => true // Tuple. Don't remove. - case R(_) -> _ => true // Literals. Don't remove. - } - printlnUCS("Missing cases") - missingCases.foreachEntry { (key, m) => - printlnUCS(s"- $key -> ${m}") - } - if (!missingCases.isEmpty) { - throw new DesugaringException({ - val numMissingCases = missingCases.size - (msg"The match is not exhaustive." -> scrutinee.matchRootLoc) :: - (msg"The scrutinee at this position misses ${ - "case".pluralize(numMissingCases, true) - }." -> scrutinee.term.toLoc) :: - missingCases.iterator.zipWithIndex.flatMap { case ((pattern, locations), index) => - val patternName = pattern match { - case L(tupleArity) => s"$tupleArity-ary tuple" - case R(litOrCls) => litOrCls.showDbg - } - val progress = s"[Missing Case ${index + 1}/$numMissingCases]" - (msg"$progress `$patternName`" -> N) :: - locations.iterator.zipWithIndex.map { case (loc, index) => - (if (index === 0) msg"It first appears here." else msg"And here.") -> S(loc) - }.toList - }.toList - }) - } - } - default.foreach(checkExhaustive(_, S(t))) - branches.foreach { branch => - checkExhaustive(branch.consequent, S(t)) - } - } - }(_ => s"[checkExhaustive] ${t.describe}") - - /** - * Make a term from a mutable case tree. - * This should be called after exhaustiveness checking. - * - * @param m the mutable case tree - * @param ctx the context - * @return the case expression - */ - private def constructTerm(m: MutCaseOf)(implicit ctx: Ctx): Term = traceUCS("[constructTerm]") { - /** - * Reconstruct case branches. - */ - def rec2(xs: Ls[MutCase])( - implicit defs: Set[Var], scrutinee: Scrutinee, wildcard: Option[MutCaseOf] - ): CaseBranches = { - xs match { - case MutCase.Constructor(className -> fields, cases) :: next => - printlnUCS(s"• Constructor pattern: $className(${fields.iterator.map(x => s"${x._1} -> ${x._2}").mkString(", ")})") - val consequent = rec(cases)(defs ++ fields.iterator.map(_._2)) - val unapplyMtd = ctx.get(className.name) match { - case S(CompletedTypeInfo(nd: TypedNuTypeDef)) => nd.td.genUnapply // Declarations from other typing units - case S(ti: DelayedTypeInfo) => ti.decl.genUnapply // Declarations in the same typing units - case S(_: AbstractConstructor) | S(_: LazyTypeInfo) | S(_: VarSymbol) | N => N // Not found or not a class - } - val body = (scrutinee.reference, unapplyMtd) match { - case (v: Var, S(unapplyMtd)) if !fields.isEmpty => - val visited = new HashMap[Str, Str]() - val extraAlias = new ListBuffer[(Str, Str)]() - fields.foreach { - case (field -> Var(alias)) => visited.get(field) match { - case S(prev) => extraAlias.addOne((prev, alias)) - case _ => visited.put(field, alias) - } - } - - // Well, reading the following piece of code is somewhat overwhelming for me. - App(Lam(Tup( /* begin params */ - N -> Fld(FldFlags.empty, Tup( - fields.distinctBy(_._1).map { - case (_ -> Var(alias)) => - if (alias === "_") N -> Fld(FldFlags.empty, Var(freshName)) - else N -> Fld(FldFlags.empty, Var(alias)) - }.toList - )) :: Nil - ) /* end params */, extraAlias.toList.foldRight(consequent)((lt, rs) => Let(false, Var(lt._2), Var(lt._1), rs))), - Tup(N -> Fld(FldFlags.empty, App(Sel(className, Var(unapplyMtd.name) /* ClassName.unapply */), - Tup(N -> Fld(FldFlags.empty, scrutinee.reference) :: Nil)) /* ClassName.unapply(scrutinee) */ - ) :: Nil) - ) - case _ => mkLetFromFields(scrutinee, fields.filter(_._2.name =/= "_").toList, consequent) - } - Case(className, body, rec2(next))(refined = false) - case MutCase.Literal(literal, cases) :: next => - printlnUCS(s"• Literal pattern: $literal") - Case(literal, rec(cases), rec2(next))(refined = false) - case Nil => - wildcard match { - case None => - printlnUCS("• No wildcard branch") - NoCases - case Some(value) => - printlnUCS("• Wildcard branch") - Wildcard(rec(value)) - } - } - } - /** - * Reconstruct the entire match. - */ - def rec(m: MutCaseOf)(implicit defs: Set[Var]): Term = traceUCS(s"[rec] ${m.describe} -| {${defs.mkString(", ")}}") { - m match { - case Consequent(term) => - mkBindings(m.getBindings.toList, term, defs) - case Match(scrutinee, branches, wildcard) => - printlnUCS("• Owned let bindings") - val ownedBindings = m.getBindings.iterator.filterNot { - _.kind === LetBinding.Kind.InterleavedLet - }.toList - if (ownedBindings.isEmpty) - printlnUCS(" * ") - else - ownedBindings.foreach { case LetBinding(kind, _, name, value) => - printlnUCS(s" * ($kind) $name = $value") - } - // Collect interleaved let bindings from case branches. - // Because they should be declared before - val interleavedBindings = branches.iterator.map(_.consequent).concat(wildcard).flatMap(_.getBindings).filter { - _.kind === LetBinding.Kind.InterleavedLet - }.toList - printlnUCS("• Collect interleaved let bindings from case branches") - if (interleavedBindings.isEmpty) - printlnUCS(" * ") - else - interleavedBindings.foreach { case LetBinding(_, _, name, value) => - printlnUCS(s" * $name = $value") - } - val resultTerm = if (branches.isEmpty) { - // If the match does not have any branches. - wildcard match { - case None => - // Internal error! - printlnUCS("• The match has neither branches nor default case") - throw new DesugaringException({ - import Message.MessageContext - msg"found an empty match" - }, scrutinee.term.toLoc) - case Some(default) => - printlnUCS("• Degenerated case: the match only has a wildcard") - val subTerm = rec(default) - scrutinee.local match { - case N => subTerm - case S(aliasVar) => Let(false, aliasVar, scrutinee.term, subTerm) - } - } - } else { - // If the match has some branches. - printlnUCS("• The match has some case branches") - val cases = traceUCS("• For each case branch"){ - rec2(branches.toList)(defs, scrutinee, wildcard) - }(_ => "• End for each") - scrutinee.local match { - case N => CaseOf(scrutinee.term, cases) - case S(aliasVar) => Let(false, aliasVar, scrutinee.term, CaseOf(aliasVar, cases)) - } - } - mkBindings(ownedBindings, mkBindings(interleavedBindings, resultTerm, defs), defs) - case MissingCase => - import Message.MessageContext - throw new DesugaringException(msg"missing a default branch", N) - case IfThenElse(condition, whenTrue, whenFalse) => - val falseBody = mkBindings(whenFalse.getBindings.toList, rec(whenFalse)(defs ++ whenFalse.getBindings.iterator.map(_.name)), defs) - val trueBody = mkBindings(whenTrue.getBindings.toList, rec(whenTrue)(defs ++ whenTrue.getBindings.iterator.map(_.name)), defs) - val falseBranch = Wildcard(falseBody) - val trueBranch = Case(Var("true"), trueBody, falseBranch)(refined = false) - CaseOf(condition, trueBranch) - } - }() - val term = rec(m)(Set.from(m.getBindings.iterator.map(_.name))) - // Create immutable map from the mutable map. - mkBindings(m.getBindings.toList, term, Set.empty) - }(_ => "[constructTerm]") - - /** - * Generate a chain of field selection to the given scrutinee. - * - * @param scrutinee the pattern matching scrutinee - * @param fields a list of pairs from field names to binding names - * @param body the final body - */ - private def mkLetFromFields(scrutinee: Scrutinee, fields: Ls[Str -> Var], body: Term)(implicit ctx: Ctx): Term = { - def rec(scrutineeReference: SimpleTerm, fields: Ls[Str -> Var]): Term = - fields match { - case Nil => body - case (field -> (aliasVar @ Var(alias))) :: tail => - scrutinee.term match { - // Check if the scrutinee is a `Var` and its name conflicts with - // one of the positionals. If so, we create an alias and extract - // fields by selecting the alias. - case Var(scrutineeName) if alias === scrutineeName => - val scrutineeAlias = Var(freshName) - Let( - false, - scrutineeAlias, - scrutinee.reference, - Let( - false, - aliasVar, - Sel(scrutineeAlias, Var(field)).desugaredFrom(scrutinee.term), - rec(scrutineeAlias, tail) - ) - ) - case _ => - Let( - false, - aliasVar, - Sel(scrutineeReference, Var(field)).desugaredFrom(scrutinee.term), - rec(scrutineeReference, tail) - ) - } - } - rec(scrutinee.reference, fields) - } - - private def buildCaseTree - (paths: Ls[Conjunction -> Term]) - (implicit raise: Diagnostic => Unit, - getScrutineeKey: Scrutinee => Str \/ Int, - exhaustivenessMap: ExhaustivenessMap, - superClassMap: SuperClassMap) - : MutCaseOf = traceUCS("[buildCaseTree]") { - paths match { - case Nil => MissingCase - case (conditions -> term) :: remaining => - val root = MutCaseOf.buildFirst(conditions, term) - traceUCS("*** Initial tree ***") { - MutCaseOf.show(root).foreach(printlnUCS(_)) - }() - remaining.foreach { path => - root.merge(path) - printlnUCS(s"*** Merging `${path._1} => ${path._2}` ***") - traceUCS("*** Updated tree ***") { - MutCaseOf.show(root).foreach(printlnUCS(_)) - }() - } - root - } - }(_ => "[buildCaseTree]") - - private def getClassHierarchy()(implicit ctx: Ctx): SuperClassMap = - traceUCS("[getClassHierarchy]") { - // ctx.tyDefs - val superClassMap = ctx.tyDefs.iterator - .filter(_._2.toLoc.isDefined) - .map { case (className, td) => - className -> td.baseClasses.iterator.map(_.name).toList - } |> Map.from - Desugarer.transitiveClosure(superClassMap) - }(_ => "[getClassHierarchy]") -} - -object Desugarer { - /** - * A map from each scrutinee term to all its cases and the first `MutCase`. - */ - type ExhaustivenessMap = Map[Str \/ Int, Map[Either[Int, SimpleTerm], Buffer[Loc]]] - - type SuperClassMap = Map[String, List[String]] - - type SubClassMap = Map[String, List[String]] - - def reverseGraph(graph: Map[String, List[String]]): Map[String, List[String]] = { - graph.iterator.flatMap { case (source, targets) => targets.iterator.map(_ -> source) } - .foldLeft(Map.empty[String, List[String]]) { case (map, target -> source) => - map.updatedWith(target) { - case None => Some(source :: Nil) - case Some(sources) => Some(source :: sources) - } - } - } - - def transitiveClosure(graph: Map[String, List[String]]): Map[String, List[String]] = { - def dfs(vertex: String, visited: Set[String]): Set[String] = { - if (visited.contains(vertex)) visited - else graph.getOrElse(vertex, List()) - .foldLeft(visited + vertex)((acc, v) => dfs(v, acc)) - } - - graph.keys.map { vertex => - val closure = dfs(vertex, Set()) - vertex -> (closure - vertex).toList - }.toMap - } - - def printGraph(graph: Map[String, List[String]], print: (=> Any) => Unit, title: String, arrow: String): Unit = { - print(s"• $title") - if (graph.isEmpty) - print(" + ") - else - graph.foreachEntry { (source, targets) => - print(s" + $source $arrow " + { - if (targets.isEmpty) s"{}" - else targets.mkString("{ ", ", ", " }") - }) - } - } -} \ No newline at end of file diff --git a/shared/src/main/scala/mlscript/ucs/old/LetBinding.scala b/shared/src/main/scala/mlscript/ucs/old/LetBinding.scala deleted file mode 100644 index fb217c41..00000000 --- a/shared/src/main/scala/mlscript/ucs/old/LetBinding.scala +++ /dev/null @@ -1,46 +0,0 @@ -package mlscript.ucs.old - -import mlscript._ -import mlscript.utils._ -import mlscript.utils.shorthands._ -import scala.collection.immutable.Set -import scala.collection.mutable.{Set => MutSet, Buffer} - -final case class LetBinding(val kind: LetBinding.Kind, val recursive: Bool, val name: Var, val term: Term) - -object LetBinding { - sealed abstract class Kind - - object Kind { - case object ScrutineeAlias extends Kind { - override def toString: String = "scrutinee alias" - } - case object FieldExtraction extends Kind { - override def toString: String = "pattern destruction" - } - case object InterleavedLet extends Kind { - override def toString: String = "interleaved let" - } - } -} - -trait WithBindings { this: MutCaseOf => - private val bindingsSet: MutSet[LetBinding] = MutSet.empty - private val bindings: Buffer[LetBinding] = Buffer.empty - - def addBindings(newBindings: IterableOnce[LetBinding]): Unit = { - newBindings.iterator.foreach { - case binding if bindingsSet.contains(binding) => () - case binding => - bindingsSet += binding - bindings += binding - } - } - - def getBindings: Iterable[LetBinding] = bindings - - def withBindings(newBindings: IterableOnce[LetBinding]): MutCaseOf = { - addBindings(newBindings) - this - } -} diff --git a/shared/src/main/scala/mlscript/ucs/old/MutCaseOf.scala b/shared/src/main/scala/mlscript/ucs/old/MutCaseOf.scala deleted file mode 100644 index eac05684..00000000 --- a/shared/src/main/scala/mlscript/ucs/old/MutCaseOf.scala +++ /dev/null @@ -1,590 +0,0 @@ -package mlscript.ucs.old - -import mlscript._ -import mlscript.utils._ -import mlscript.utils.shorthands._ -import scala.collection.immutable.Set -import scala.collection.mutable.{Map => MutMap, Set => MutSet, Buffer} - -import mlscript.ucs.helpers._ -import MutCaseOf.Consequent -import scala.collection.immutable -import Desugarer.{ExhaustivenessMap, SuperClassMap} -import Clause.MatchAny - -sealed abstract class MutCaseOf extends WithBindings { - def kind: Str = { - import MutCaseOf._ - this match { - case Consequent(_) => "Consequent" - case MissingCase => "MissingCase" - case IfThenElse(_, _, _) => "IfThenElse" - case Match(_, _, _) => "Match" - } - } - - def duplicate(): MutCaseOf - - def fill(subTree: MutCaseOf): Unit - - def describe: Str - - def isComplete: Bool - - def isExhaustive(implicit getScrutineeKey: Scrutinee => Str \/ Int, - exhaustivenessMap: ExhaustivenessMap): Bool - - def tryMerge - (branch: Conjunction -> Term) - (implicit raise: Diagnostic => Unit, - getScrutineeKey: Scrutinee => Str \/ Int, - exhaustivenessMap: ExhaustivenessMap, - superClassMap: SuperClassMap): Unit = - merge(branch)(_ => (), getScrutineeKey, exhaustivenessMap, superClassMap) - - def merge - (branch: Conjunction -> Term) - (implicit raise: Diagnostic => Unit, - getScrutineeKey: Scrutinee => Str \/ Int, - exhaustivenessMap: ExhaustivenessMap, - superClassMap: SuperClassMap): Unit - - def mergeDefault - (bindings: Ls[LetBinding], default: Term) - (implicit raise: Diagnostic => Unit, - getScrutineeKey: Scrutinee => Str \/ Int, - exhaustivenessMap: ExhaustivenessMap, - superClassMap: SuperClassMap): Int - - // TODO: Make it immutable. - var locations: Ls[Loc] = Nil -} - -object MutCaseOf { - def showScrutinee(scrutinee: Scrutinee): Str = - s"«${scrutinee.term.showDbg}»" + (scrutinee.local match { - case N => "" - case S(Var(alias)) => s" as $alias" - }) - - def show(t: MutCaseOf): Ls[Str] = { - val lines = Buffer.empty[String] - def rec(t: MutCaseOf, indent: Int): Unit = { - val baseIndent = " " * indent - lazy val bindingLines = t.getBindings.iterator.map { - case LetBinding(_, recursive, Var(name), term) => - // Show bindings - s"[binding $name = ${term.showDbg}]" - }.toList - t match { - case IfThenElse(condition, whenTrue, whenFalse) => - // Output the `whenTrue` with the prefix "if". - bindingLines.foreach { lines += baseIndent + _ } - lines += baseIndent + s"if «${condition.showDbg}»" - rec(whenTrue, indent + 1) - // Output the `whenFalse` case with the prefix "else". - lines += s"${baseIndent}else" - rec(whenFalse, indent + 1) - case Match(scrutinee, branches, default) => - bindingLines.foreach { lines += baseIndent + _ } - lines += baseIndent + showScrutinee(scrutinee) + " match" - branches.foreach { - case MutCase.Literal(literal, consequent) => - lines += s"$baseIndent case ${literal.showDbg} =>" - rec(consequent, indent + 1) - case MutCase.Constructor(Var(className) -> fields, consequent) => - lines += s"$baseIndent case $className =>" - fields.foreach { case (field, Var(alias)) => - // Show pattern bindings. - lines += s"$baseIndent [pattern $alias = ${scrutinee.reference.showDbg}.$field]" - } - rec(consequent, indent + 2) - } - default.foreach { consequent => - lines += s"$baseIndent default" - rec(consequent, indent + 2) - } - case Consequent(term) => - bindingLines.foreach { lines += baseIndent + _ } - lines += s"$baseIndent«${term.showDbg}»" - case MissingCase => - bindingLines.foreach { lines += baseIndent + _ } - lines += s"$baseIndent" - } - } - rec(t, 0) - lines.toList - } - - sealed abstract class MutCase { - var consequent: MutCaseOf - - @inline - def isComplete: Bool = consequent.isComplete - - def duplicate(): MutCase - - /** - * Check whether this case can cover the expected class or literal. - * - * @param expected the expected class name or literal - * @param superClassMap a map from each class to its super classes - * @return whether the given pattern can be covered by this case - */ - def covers(expected: SimpleTerm)(implicit superClassMap: SuperClassMap): Bool - - // Note 1 - // ====== - // A `MutCase` may come from one of two origins. - // Direct patterns. - // E.g. if x is Y then "aha" else "meh" - // ^^^^^^ - // Nested patterns. - // E.g. if x is Right(Some(x)) then ... - // ^^^^^^^ - // The goal is to accurately indicate where the pattern is declared. - // - // Note 2 - // ====== - // A `MutCase` may come from multiple locations. - // That is why I'm using a `Set`. - // - val locations: MutSet[Loc] = MutSet.empty[Loc] - def withLocation(locOpt: Opt[Loc]): MutCase = { - locations ++= locOpt - this - } - def withLocations(locs: IterableOnce[Loc]): MutCase = { - locations ++= locs - this - } - } - - object MutCase { - final case class Literal( - val literal: SimpleTerm, - var consequent: MutCaseOf, - ) extends MutCase { - override def duplicate(): MutCase = - Literal(literal, consequent.duplicate()).withLocations(locations) - override def covers(expected: SimpleTerm)(implicit superClassMap: SuperClassMap): Bool = - expected match { - case _: Lit | Var("true") | Var("false") => expected === literal - case Var(_) => false - } - } - - /** - * 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 duplicate(): MutCase = - Constructor(patternFields.copy(_2 = patternFields._2.clone()), consequent.duplicate()) - .withLocations(locations) - override def covers(expected: SimpleTerm)(implicit superClassMap: SuperClassMap): Bool = - expected match { - case lit: Lit => false - case Var(tof) if tof === "true" || tof === "false" => false - case Var(expectedClassName) if expectedClassName === patternFields._1.name => true - case Var(expectedClassName) => - (superClassMap.get(expectedClassName) match { - case Some(superClasses) => superClasses.contains(patternFields._1.name) - case None => - // Should we raise? - false - }) - } - def addFields(fields: Iterable[Str -> Var]): Unit = - patternFields._2 ++= fields.iterator.filter(!patternFields._2.contains(_)) - } - } - - import Clause.{MatchLiteral, MatchAny, 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 { - def describe: Str = - s"IfThenElse(${condition.showDbg}, whenTrue = ${whenTrue.kind}, whenFalse = ${whenFalse.kind})" - - def duplicate(): MutCaseOf = - IfThenElse(condition, whenTrue.duplicate(), whenFalse.duplicate()) - .withBindings(getBindings) - - override def fill(subTree: MutCaseOf): Unit = { - whenTrue.fill(subTree) - if (whenFalse === MissingCase) - whenFalse = subTree - else - whenFalse.fill(subTree) - } - - def isComplete: Bool = whenTrue.isComplete && whenFalse.isComplete - - def isExhaustive(implicit getScrutineeKey: Scrutinee => Str \/ Int, - exhaustivenessMap: ExhaustivenessMap): Bool = - whenTrue.isExhaustive && whenFalse.isExhaustive - - def merge(branch: Conjunction -> Term) - (implicit raise: Diagnostic => Unit, - getScrutineeKey: Scrutinee => Str \/ Int, - exhaustivenessMap: ExhaustivenessMap, - superClassMap: SuperClassMap): Unit = - branch match { - // The CC is a wildcard. So, we call `mergeDefault`. - case Conjunction(Nil, trailingBindings) -> term => - if (mergeDefault(trailingBindings, term) === 0) { - import Message.MessageContext - raise(WarningReport( - msg"Found a redundant else branch" -> term.toLoc :: Nil, newDefs = true)) - } - // The CC is an if-then-else. We create a pattern match of true/false. - case Conjunction((head @ BooleanTest(test)) :: tail, trailingBindings) -> term if test === condition => - // If the test is the same. So, we can insert the path to the true branch. - whenTrue.addBindings(head.bindings) - whenTrue.merge(Conjunction(tail, trailingBindings) -> term) - // Otherwise, we try to insert to the true branch. - case Conjunction(head :: _, _) -> _ => - whenTrue.tryMerge(branch) - whenFalse match { - case Consequent(_) => - raise(WarningReport(Message.fromStr("duplicated else in the if-then-else") -> N :: Nil, - newDefs = true)) - case MissingCase => - whenFalse = buildFirst(branch._1, branch._2) - whenFalse.addBindings(head.bindings) - case _ => whenFalse.merge(branch) - } - } - - def mergeDefault(bindings: Ls[LetBinding], default: Term) - (implicit raise: Diagnostic => Unit, - getScrutineeKey: Scrutinee => Str \/ Int, - exhaustivenessMap: ExhaustivenessMap, - superClassMap: SuperClassMap): Int = { - whenTrue.mergeDefault(bindings, default) + { - whenFalse match { - case Consequent(term) => 0 - case MissingCase => - whenFalse = Consequent(default).withBindings(bindings) - 1 - case _: IfThenElse | _: Match => whenFalse.mergeDefault(bindings, default) - } - } - } - } - final case class Match( - scrutinee: Scrutinee, - val branches: Buffer[MutCase], - var wildcard: Opt[MutCaseOf] - ) extends MutCaseOf { - def describe: Str = { - val n = branches.length - s"Match($scrutinee, ${"branch".pluralize(n, true, true)}, ${ - wildcard.fold("no wildcard")(n => s"wildcard = ${n.kind}") - })" - } - - def duplicate(): MutCaseOf = - Match(scrutinee, branches.map(_.duplicate()), wildcard.map(_.duplicate())) - .withBindings(getBindings) - - override def fill(subTree: MutCaseOf): Unit = { - branches.foreach(_.consequent.fill(subTree)) - wildcard.foreach(_.fill(subTree)) - } - - def isComplete: Bool = - branches.forall(_.consequent.isComplete) && wildcard.forall(_.isComplete) - - def isExhaustive(implicit getScrutineeKey: Scrutinee => Str \/ Int, - exhaustivenessMap: ExhaustivenessMap): Bool = { - exhaustivenessMap.get(getScrutineeKey(scrutinee)) match { - case None => ??? // TODO: Raise. - case Some(patternLocationsMap) => - // Find patterns that are not included in `branches`. - patternLocationsMap.keysIterator.filterNot { - case L(tupleArity) => branches.iterator.exists { - case MutCase.Literal(_, _) => false - case MutCase.Constructor(Var(className) -> _, _) => - className === s"Tuple#$tupleArity" - } - case R(litOrCls) => branches.iterator.exists { - case MutCase.Literal(lit, _) => litOrCls === lit - case MutCase.Constructor(cls -> _, _) => litOrCls === cls - } - }.isEmpty - } - } - - def merge(originalBranch: Conjunction -> Term) - (implicit raise: Diagnostic => Unit, - getScrutineeKey: Scrutinee => Str \/ Int, - exhaustivenessMap: ExhaustivenessMap, - superClassMap: SuperClassMap): Unit = { - // Remove let bindings that already has been declared. - val branch = originalBranch._1.copy(clauses = originalBranch._1.clauses.filter { - case Binding(name, value, false) if (getBindings.exists { - case LetBinding(LetBinding.Kind.ScrutineeAlias, _, n, v) => - n === name && v === value - case _ => false - }) => false - case _ => true - }) -> originalBranch._2 - // Promote the match against the same scrutinee. - branch._1.findClauseMatches(scrutinee) match { - // No conditions against the same scrutinee. - case N => - branch match { - case Conjunction((head @ MatchTuple(scrutinee2, arity, fields)) :: tail, trailingBindings) -> term - if scrutinee2 === scrutinee => // Same scrutinee! - val tupleClassName = Var(s"Tuple#$arity") // TODO: Find a name known by Typer. - branches.find(_.covers(tupleClassName)) match { - // No such pattern. We should create a new one. - case N | S(MutCase.Literal(_, _)) => - val newBranch = buildFirst(Conjunction(tail, trailingBindings), term) - newBranch.addBindings(head.bindings) - branches += MutCase.Constructor(tupleClassName -> Buffer.from(fields), newBranch) - .withLocations(head.locations) - // Found existing pattern. - 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 => - if (mergeDefault(trailingBindings, term) === 0) { - import Message.MessageContext - raise(WarningReport( - msg"Found a redundant else branch" -> term.toLoc :: Nil, - newDefs = true)) - } - // The conditions to be inserted does not overlap with me. - case conjunction -> term => - branches.foreach { - _.consequent.tryMerge(conjunction -> term) - } - wildcard match { - // No wildcard. We will create a new one. - case N => wildcard = S(buildFirst(conjunction, term)) - // There is a wildcard case. Just merge! - case S(consequent) => consequent.merge(conjunction -> term) - } - } - // Found a match condition against the same scrutinee - case S((head @ MatchClass(_, className, fields)) -> remainingConditions) => - // Find all branches which can cover the `className`. - val inclusiveBranches = branches.iterator.filter(_.covers(className)) - if (inclusiveBranches.isEmpty) { - // No such pattern. We should create a new one. - wildcard match { - // If the wildcard branch is incomplete, there might be some - // preemptive branches in front of this branch. - case Some(default) if !default.isComplete => - val subTree = default.duplicate() - subTree.fill(buildFirst(remainingConditions, branch._2)) - subTree.addBindings(head.bindings) - branches += MutCase.Constructor(className -> Buffer.from(fields), subTree) - .withLocations(head.locations) - case Some(_) | None => - val newBranch = buildFirst(remainingConditions, branch._2) - newBranch.addBindings(head.bindings) - branches += MutCase.Constructor(className -> Buffer.from(fields), newBranch) - .withLocations(head.locations) - } - } else { - // Found some branches that can cover the `className`. - inclusiveBranches.foreach { - case MutCase.Literal(_, _) => () // This shouldn't happen. - case matchedCase @ MutCase.Constructor(Var(branchClassName) -> _, _) => - if (branchClassName === className.name) { - // This branch exactly matches the given class name. - // So, we just do a simple merge. - // Merge interleaved bindings. - matchedCase.consequent.addBindings(head.bindings) - matchedCase.addFields(fields) - matchedCase.consequent.merge(remainingConditions -> branch._2) - } else { - // This branch matches the super classes of the given class name. - // There will be refinement matches inside the consequent. - // Therefore, we should not merge with `remainingConditions`. - // Instead, we should use the original conjunction. - matchedCase.consequent.addBindings(head.bindings) - matchedCase.addFields(fields) - matchedCase.consequent.merge(branch) - } - } - } - case S((head @ MatchLiteral(_, literal)) -> remainingConditions) => - branches.find(_.covers(literal)) match { - // No such pattern. We should create a new one. - case N | S(MutCase.Constructor(_, _)) => - 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) - } - case S((head @ MatchAny(_)) -> remainingConditions) => - // Existing branches may be complete but not exhaustive. - // Find inexhaustiveness branches and try to merge. - branches.iterator.filterNot(_.consequent.isExhaustive).foreach { - _.consequent.tryMerge(remainingConditions -> branch._2) - } - // Then, let's consider the wildcard branch. - wildcard match { - // No wildcard. We will create a new one. - case N => wildcard = S(buildFirst(remainingConditions, branch._2)) - // There is a wildcard case. Just merge! - case S(consequent) => consequent.merge(remainingConditions -> branch._2) - } - } - } - - def mergeDefault(bindings: Ls[LetBinding], default: Term) - (implicit raise: Diagnostic => Unit, - getScrutineeKey: Scrutinee => Str \/ Int, - exhaustivenessMap: ExhaustivenessMap, - superClassMap: SuperClassMap): Int = { - branches.iterator.map { - case MutCase.Constructor(_, consequent) => consequent.mergeDefault(bindings, default) - case MutCase.Literal(_, consequent) => consequent.mergeDefault(bindings, default) - }.sum + { - wildcard match { - case N => - wildcard = S(Consequent(default).withBindings(bindings)) - 1 - case S(consequent) => consequent.mergeDefault(bindings, default) - } - } - } - } - final case class Consequent(term: Term) extends MutCaseOf { - def describe: Str = s"Consequent(${term.showDbg})" - - override def fill(subTree: MutCaseOf): Unit = () - - override def duplicate(): MutCaseOf = Consequent(term).withBindings(getBindings) - - def isComplete: Bool = true - - def isExhaustive(implicit getScrutineeKey: Scrutinee => Str \/ Int, - exhaustivenessMap: ExhaustivenessMap): Bool = true - - def merge(branch: Conjunction -> Term) - (implicit raise: Diagnostic => Unit, - getScrutineeKey: Scrutinee => Str \/ Int, - exhaustivenessMap: ExhaustivenessMap, - superClassMap: SuperClassMap): Unit = - raise { - import scala.collection.mutable.ListBuffer - val buffer = ListBuffer.empty[Message -> Opt[Loc]] - buffer += Message.fromStr("Found a duplicated branch") -> N - buffer += Message.fromStr("This branch") -> { - val (Conjunction(clauses, _) -> consequent) = branch - consequent.toLoc - // TODO: Make a complete location. - // clauses match { - // case head :: _ => head. - // case Nil => consequent.toLoc - // } - } - buffer += Message.fromStr("is subsumed by the branch here.") -> term.toLoc - WarningReport(buffer.toList, newDefs = true) - } - - def mergeDefault(bindings: Ls[LetBinding], default: Term) - (implicit raise: Diagnostic => Unit, - getScrutineeKey: Scrutinee => Str \/ Int, - exhaustivenessMap: ExhaustivenessMap, - superClassMap: SuperClassMap): Int = 0 - } - final case object MissingCase extends MutCaseOf { - def describe: Str = "MissingCase" - - override def duplicate() = MissingCase - - override def fill(subTree: MutCaseOf): Unit = () - - def isComplete: Bool = false - - def isExhaustive(implicit getScrutineeKey: Scrutinee => Str \/ Int, - exhaustivenessMap: ExhaustivenessMap): Bool = false - - def merge(branch: Conjunction -> Term) - (implicit raise: Diagnostic => Unit, - getScrutineeKey: Scrutinee => Str \/ Int, - exhaustivenessMap: ExhaustivenessMap, - superClassMap: SuperClassMap): Unit = - lastWords("`MissingCase` is a placeholder and cannot be merged") - - def mergeDefault(bindings: Ls[LetBinding], default: Term) - (implicit raise: Diagnostic => Unit, - getScrutineeKey: Scrutinee => Str \/ Int, - exhaustivenessMap: ExhaustivenessMap, - superClassMap: SuperClassMap): Int = 0 - } - - def buildFirst(conjunction: Conjunction, term: Term) - (implicit getScrutineeKey: Scrutinee => Str \/ Int, - exhaustivenessMap: ExhaustivenessMap, - superClassMap: SuperClassMap): MutCaseOf = { - def rec(conjunction: Conjunction): MutCaseOf = conjunction match { - case Conjunction(head :: tail, trailingBindings) => - lazy val (beforeHeadBindings, afterHeadBindings) = head.bindings.partition { - case LetBinding(LetBinding.Kind.InterleavedLet, _, _, _) => false - case LetBinding(_, _, _, _) => true - } - val consequentTree = rec(Conjunction(tail, trailingBindings)) - (head match { - case MatchLiteral(scrutinee, literal) => - val branches = Buffer[MutCase]( - MutCase.Literal(literal, consequentTree.withBindings(afterHeadBindings)).withLocation(literal.toLoc) - ) - Match(scrutinee, branches, N) - .withBindings(beforeHeadBindings) - case MatchAny(scrutinee) => - Match(scrutinee, Buffer.empty, S(consequentTree.withBindings(afterHeadBindings))) - .withBindings(beforeHeadBindings) - case MatchClass(scrutinee, className, fields) => - val branches = Buffer[MutCase]( - MutCase.Constructor(className -> Buffer.from(fields), consequentTree.withBindings(afterHeadBindings)) - .withLocations(head.locations) - ) - Match(scrutinee, branches, N).withBindings(beforeHeadBindings) - case MatchTuple(scrutinee, arity, fields) => - val branches = Buffer[MutCase]( - MutCase.Constructor(Var(s"Tuple#$arity") -> Buffer.from(fields), consequentTree.withBindings(afterHeadBindings)) - .withLocations(head.locations) - ) - Match(scrutinee, branches, N).withBindings(beforeHeadBindings) - case BooleanTest(test) => - IfThenElse(test, consequentTree, MissingCase) - .withBindings(beforeHeadBindings) - .withBindings(afterHeadBindings) - case Binding(name, term, isField) => - val kind = if (isField) - LetBinding.Kind.FieldExtraction - else - LetBinding.Kind.ScrutineeAlias - consequentTree - .withBindings(beforeHeadBindings) - .withBindings(LetBinding(kind, false, name, term) :: Nil) - .withBindings(afterHeadBindings) - }) - case Conjunction(Nil, trailingBindings) => - Consequent(term).withBindings(trailingBindings) - } - - rec(conjunction) - } -} diff --git a/shared/src/main/scala/mlscript/ucs/old/Scrutinee.scala b/shared/src/main/scala/mlscript/ucs/old/Scrutinee.scala deleted file mode 100644 index 9c9a4623..00000000 --- a/shared/src/main/scala/mlscript/ucs/old/Scrutinee.scala +++ /dev/null @@ -1,30 +0,0 @@ -package mlscript.ucs.old - -import mlscript.{Loc, SimpleTerm, Term, Var} -import mlscript.utils.lastWords -import mlscript.utils.shorthands._ - -// The point is to remember where the scrutinee comes from. -// Is it from nested patterns? Or is it from a `IfBody`? -final case class Scrutinee(var local: Opt[Var], term: Term)(val matchRootLoc: Opt[Loc]) { - def reference: SimpleTerm = local.getOrElse(term match { - case term: SimpleTerm => term - case _ => lastWords("`term` must be a `SimpleTerm` when `local` is empty") - }) - - /** - * Create a binding for the scrutinee. If the scrutinee is a `SimpleTerm`, - * it returns `None`. - * - * @return `Some` if the scrutinee is localized, otherwise, `None`. - */ - def asBinding: Opt[LetBinding] = local.map { - LetBinding(LetBinding.Kind.ScrutineeAlias, false, _, term) - } - - override def toString: String = - (local match { - case N => "" - case S(Var(alias)) => s"$alias @ " - }) + s"${term.showDbg}" -} diff --git a/shared/src/main/scala/mlscript/ucs/old/helpers.scala b/shared/src/main/scala/mlscript/ucs/old/helpers.scala deleted file mode 100644 index 4d2df1e7..00000000 --- a/shared/src/main/scala/mlscript/ucs/old/helpers.scala +++ /dev/null @@ -1,47 +0,0 @@ -package mlscript.ucs.old - -import scala.collection.mutable.{Set => MutSet} - -import mlscript._ -import mlscript.utils.shorthands._ - -object helpers { - /** - * Split a term joined by `and` into a list of terms. - * E.g. `x and y and z` will be split into `x`, `y`, and `z`. - * - * @return a list of sub-terms of `t` - */ - def splitAnd(t: Term): Ls[Term] = - t match { - case App( - App(Var("and"), - Tup((_ -> Fld(_, lhs)) :: Nil)), - Tup((_ -> Fld(_, rhs)) :: Nil) - ) => // * Old-style operators - splitAnd(lhs) :+ rhs - case App(Var("and"), PlainTup(lhs, rhs)) => - splitAnd(lhs) :+ rhs - case _ => t :: Nil - } - - /** - * Generate a chain of `Let` from a list of bindings. - * - * @param bindings a list of bindings, - * @param body the final body - */ - def mkBindings(bindings: Ls[LetBinding], body: Term, defs: Set[Var]): Term = { - def rec(bindings: Ls[LetBinding], defs: Set[Var]): Term = - bindings match { - case Nil => body - case LetBinding(_, isRec, nameVar, value) :: tail => - if (defs.contains(nameVar)) { - rec(tail, defs) - } else { - Let(isRec, nameVar, value, rec(tail, defs + nameVar)) - } - } - rec(bindings, defs) - } -} diff --git a/shared/src/main/scala/mlscript/ucs/package.scala b/shared/src/main/scala/mlscript/ucs/package.scala index 1e5a3cac..4e285538 100644 --- a/shared/src/main/scala/mlscript/ucs/package.scala +++ b/shared/src/main/scala/mlscript/ucs/package.scala @@ -1,9 +1,10 @@ package mlscript import scala.annotation.tailrec +import utils._, shorthands._ package object ucs { - class VariableGenerator(prefix: String) { + class VariableGenerator(prefix: Str) { private var nextIndex = 0 def apply(): Var = { @@ -15,7 +16,7 @@ package object ucs { def reset(): Unit = nextIndex = 0 } - type Lines = List[(Int, String)] + type Lines = Ls[(Int, Str)] implicit class LinesOps(private val lines: Lines) extends AnyVal { def indent: Lines = { @@ -26,20 +27,26 @@ package object ucs { } rec(Nil, lines) } - def ##:(prefix: String): Lines = (0, prefix) :: lines.indent - def #:(prefix: String): Lines = { + def ##:(prefix: Str): Lines = (0, prefix) :: lines.indent + def #:(prefix: Str): Lines = { lines match { case (0, line) :: lines if lines.forall(_._1 > 0) => (0, s"$prefix $line") :: lines case lines => (0, prefix) :: lines.indent } } - def @:(prefix: String): Lines = { + def @:(prefix: Str): Lines = { lines match { case (_, line) :: Nil => (0, prefix + " " + line) :: Nil case lines => (0, prefix) :: lines.indent } } - def toIndentedString: String = + def toIndentedString: Str = lines.iterator.map { case (n, line) => " " * n + line }.mkString("\n") } + + // TODO: Remove this exception. The desugarer should work in a non-fatal way. + // We may call `lastWords` if unrecoverable errors are found. + class DesugaringException(val messages: Ls[Message -> Opt[Loc]]) extends Throwable { + def this(message: Message, location: Opt[Loc]) = this(message -> location :: Nil) + } } diff --git a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala index 5a7a368a..d85019bb 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala @@ -3,13 +3,13 @@ package mlscript.ucs.stages import mlscript.{Case, CaseBranches, CaseOf, Let, Lit, Loc, NoCases, Term, Var, Wildcard} import mlscript.{Diagnostic, ErrorReport, WarningReport} import mlscript.Message, Message.MessageContext -import mlscript.ucs.DesugarUCS +import mlscript.ucs.Desugarer import mlscript.ucs.context.{Context, CaseSet, NamedScrutineeData, MatchRegistry, ScrutineeData, SeenRegistry} import mlscript.pretyper.Traceable import mlscript.pretyper.symbol._ import mlscript.utils._, shorthands._ -trait CoverageChecking { self: DesugarUCS with Traceable => +trait CoverageChecking { self: Desugarer with Traceable => import CoverageChecking._ def checkCoverage(term: Term)(implicit context: Context): Ls[Diagnostic] = { diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 01b649db..c67176d3 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -1,10 +1,8 @@ package mlscript.ucs.stages import mlscript.{App, Asc, Fld, FldFlags, Lit, Sel, Term, Tup, TypeName, Var} -import mlscript.ucs.PartialTerm import mlscript.ucs.syntax.{core => c, source => s} import mlscript.ucs.context.{Context, ScrutineeData} -import mlscript.ucs.helpers.mkBinOp import mlscript.utils._, shorthands._ import mlscript.pretyper.symbol._ import mlscript.pretyper.{PreTyper, Scope} @@ -71,7 +69,7 @@ trait Desugaring { self: PreTyper => private def falsePattern(implicit scope: Scope, context: Context) = c.Pattern.Class(Var("false").withResolvedClassLikeSymbol, false) - private def desugarTermSplit(split: s.TermSplit)(implicit termPart: PartialTerm, scope: Scope, context: Context): c.Split = + private def desugarTermSplit(split: s.TermSplit)(implicit termPart: PartialTerm.Incomplete, scope: Scope, context: Context): c.Split = split match { case s.Split.Cons(head, tail) => desugarTermBranch(head) ++ desugarTermSplit(tail) case s.Split.Let(rec, nme, rhs, tail) => @@ -82,7 +80,7 @@ trait Desugaring { self: PreTyper => // This function does not need to can `withCachedTermPart` because all branches assume that // `termPart` is either empty or waiting for an RHS. - private def desugarTermBranch(branch: s.TermBranch)(implicit termPart: PartialTerm, scope: Scope, context: Context): c.Split = + private def desugarTermBranch(branch: s.TermBranch)(implicit termPart: PartialTerm.Incomplete, scope: Scope, context: Context): c.Split = trace(s"desugarTermBranch <== $termPart") { branch match { case s.TermBranch.Boolean(testPart, continuation) => @@ -90,17 +88,17 @@ trait Desugaring { self: PreTyper => c.Split.Let( rec = false, name = test, - term = Asc(termPart.addTerm(testPart, true).get, TypeName("Bool")), + term = Asc(termPart.addTerm(testPart).get, TypeName("Bool")), tail = c.Branch(test, truePattern, desugarTermSplit(continuation)(PartialTerm.Empty, scope + test.symbol, context)) :: c.Split.Nil ) case s.TermBranch.Match(scrutinee, split) => - desugarPatternSplit(termPart.addTerm(scrutinee, true).get, split) + desugarPatternSplit(termPart.addTerm(scrutinee).get, split) case s.TermBranch.Left(left, continuation) => - desugarOperatorSplit(continuation)(termPart.addTerm(left, true), scope, context) + desugarOperatorSplit(continuation)(termPart.addTerm(left), scope, context) } }() - private def withCachedTermPart[B <: s.Branch](desugar: (PartialTerm, Scope) => c.Split)(implicit termPart: PartialTerm, scope: Scope, context: Context): c.Split = + private def withCachedTermPart[B <: s.Branch](desugar: (PartialTerm.Total, Scope) => c.Split)(implicit termPart: PartialTerm.Total, scope: Scope, context: Context): c.Split = termPart.get match { case v: Var => desugar(termPart, scope) // No need to cache variables. case rhs => @@ -108,7 +106,7 @@ trait Desugaring { self: PreTyper => c.Split.Let(false, cache, rhs, desugar(PartialTerm.Total(cache, Nil), scope + cache.symbol)) } - private def desugarOperatorSplit(split: s.OperatorSplit)(implicit termPart: PartialTerm, scope: Scope, context: Context): c.Split = + private def desugarOperatorSplit(split: s.OperatorSplit)(implicit termPart: PartialTerm.Total, scope: Scope, context: Context): c.Split = withCachedTermPart { (termPart, scope) => split match { case s.Split.Cons(head, tail) => desugarOperatorBranch(head)(termPart, scope, context) ++ desugarOperatorSplit(tail)(termPart, scope, context) case s.Split.Let(rec, nme, rhs, tail) => @@ -117,7 +115,7 @@ trait Desugaring { self: PreTyper => case s.Split.Nil => c.Split.Nil }} - private def desugarOperatorBranch(branch: s.OperatorBranch)(implicit termPart: PartialTerm, scope: Scope, context: Context): c.Split = + private def desugarOperatorBranch(branch: s.OperatorBranch)(implicit termPart: PartialTerm.Total, scope: Scope, context: Context): c.Split = trace(s"desugarOperatorBranch <== $termPart") { branch match { case s.OperatorBranch.Binary(op, split) => desugarTermSplit(split)(termPart.addOp(op), scope, context) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 184b36cd..6ffc8305 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -6,7 +6,7 @@ import mlscript.{CaseBranches, Case, Wildcard, NoCases} import mlscript.Message, Message.MessageContext import mlscript.utils._, shorthands._ import mlscript.ucs, mlscript.pretyper -import ucs.{DesugarUCS, Lines, LinesOps, VariableGenerator} +import ucs.{Desugarer, Lines, LinesOps, VariableGenerator} import ucs.context.{Context, ScrutineeData} import ucs.display.{showNormalizedTerm, showSplit} import ucs.helpers._ @@ -15,7 +15,7 @@ import pretyper.Scope import pretyper.symbol._ import pretyper.{Diagnosable, Traceable} -trait Normalization { self: DesugarUCS with Traceable => +trait Normalization { self: Desugarer with Traceable => import Normalization._ // TODO: We might not need the case where `deep` is `false`. diff --git a/shared/src/main/scala/mlscript/ucs/stages/PartialTerm.scala b/shared/src/main/scala/mlscript/ucs/stages/PartialTerm.scala new file mode 100644 index 00000000..c9f8c0e5 --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/stages/PartialTerm.scala @@ -0,0 +1,48 @@ +package mlscript.ucs.stages + +import mlscript.{Term, Var}, mlscript.utils, utils.shorthands._ +import mlscript.ucs.helpers._ + +/** + * A `PartialTerm` represents a possibly incomplete term. + * We'd better precisely track detailed locations of each parts. + */ +sealed abstract class PartialTerm { + /** Individual terms that used to build this `PartialTerm`. */ + def terms: Iterator[Term] + + override def toString(): String = this match { + case PartialTerm.Empty => "" + case PartialTerm.Total(term, _) => s" ${term.showDbg}" + case PartialTerm.Half(lhs, op, _) => s" ${lhs.showDbg} ${op.name}" + } +} + +object PartialTerm { + sealed abstract class Incomplete extends PartialTerm { + def addTerm(term: Term): PartialTerm.Total + } + + final case object Empty extends Incomplete { + override def terms: Iterator[Term] = Iterator.empty + def addTerm(term: Term): Total = Total(term, term :: Nil) + } + + final case class Total(term: Term, parts: Ls[Term]) extends PartialTerm { + override def terms: Iterator[Term] = parts.reverseIterator + def addOp(op: Var): Half = Half(term, op, op :: parts) + def get: Term = term + } + + final case class Half(lhs: Term, op: Var, parts: Ls[Term]) extends Incomplete { + override def terms: Iterator[Term] = parts.reverseIterator + def addTerm(rhs: Term): Total = { + val (realRhs, extraExprOpt) = separatePattern(rhs, true) + val leftmost = mkBinOp(lhs, op, realRhs, true) + extraExprOpt match { + case N => Total(leftmost, parts) + case S(extraExpr) => Total(mkBinOp(leftmost, Var("and"), extraExpr, true), extraExpr :: parts) + } + } + } +} diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index adf791cf..9cfd1f26 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -1,14 +1,14 @@ package mlscript.ucs.stages import mlscript.{Case, CaseBranches, CaseOf, Let, Lit, Loc, NoCases, Term, Var, Wildcard} -import mlscript.ucs.DesugarUCS +import mlscript.ucs.Desugarer import mlscript.ucs.context.{Context, PatternInfo, ScrutineeData} import mlscript.pretyper.symbol._ import mlscript.utils._, shorthands._ import mlscript.Message, Message.MessageContext import scala.annotation.tailrec -trait PostProcessing { self: DesugarUCS with mlscript.pretyper.Traceable => +trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => import PostProcessing._ def postProcess(term: Term)(implicit context: Context): Term = trace(s"postProcess <== ${term.showDbg}") { diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index 5fa77fba..384bd919 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -1,7 +1,7 @@ package mlscript.ucs.stages import mlscript.ucs.syntax.source._ -import mlscript.ucs.{DesugarUCS, helpers} +import mlscript.ucs.{Desugarer, helpers} import mlscript.{If, IfBody, IfBlock, IfElse, IfLet, IfOpApp, IfOpsApp, IfThen} import mlscript.{Blk, Term, Var, App, Tup, Lit, Fld, Loc, NuFunDef, PlainTup} import mlscript.pretyper.Traceable @@ -16,7 +16,7 @@ import scala.collection.immutable, scala.annotation.tailrec, scala.util.chaining * The AST in the paper is more flexible. For example, it allows interleaved * `let` bindings in operator splits. */ -trait Transformation { self: DesugarUCS with Traceable => +trait Transformation { self: Desugarer with Traceable => import Transformation._ /** The entry point of transformation. */ diff --git a/shared/src/main/scala/mlscript/ucs/stages/package.scala b/shared/src/main/scala/mlscript/ucs/stages/package.scala index 0e091845..eb442f83 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/package.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/package.scala @@ -1,7 +1,31 @@ package mlscript.ucs -import mlscript.{Lit, Term, Var} +import mlscript.{App, Fld, FldFlags, Lit, PlainTup, Term, Tup, Var} import mlscript.pretyper.symbol._ import mlscript.utils._, shorthands._ -package object stages +package object stages { + /** + * Make a tuple with only one element. For example, + * + * ```scala + * mkMonuple(t) = Tup(N -> Fld(false, false, t) :: Nil) + * ``` + * + * @param t the sole element + * @return a tuple term with the only element + */ + def mkMonuple(t: Term): Tup = Tup(N -> Fld(FldFlags.empty, t) :: Nil) + + /** + * Make a binary operation. + * + * @param lhs the left-hand side term + * @param op the operator + * @param rhs the right-hand side term + * @return something like `App(App(op, lhs), rhs)` + */ + def mkBinOp(lhs: Term, op: Var, rhs: Term, newDefs: Bool): Term = + if (newDefs) App(op, PlainTup(lhs, rhs)) + else App(App(op, mkMonuple(lhs)), mkMonuple(rhs)) +} diff --git a/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls b/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls index 6bd909b5..f81108c8 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls @@ -9,7 +9,7 @@ class App(func: Term, arg: Term) extends Term //│ class Abs(param: Str, body: Term) extends Term //│ class App(func: Term, arg: Term) extends Term -:ucs:desugar.result +:ducs:desugar.result fun is_value_explicit_refinement(term) = if term is refined(Term) and term is Abs(_, _) then true @@ -22,7 +22,7 @@ fun is_value_explicit_refinement(term) = //│ | | | | | | | term*‡ is App then false //│ fun is_value_explicit_refinement: (Abs | App | Var) -> Bool -:ucs:normalize.result,postprocess.result +:ducs:normalize.result,postprocess.result fun is_value_automatic_refinement(term) = if term is Term and term is Abs(_, _) then true diff --git a/shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls b/shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls index 82058367..2ca0944e 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls @@ -262,7 +262,7 @@ fun bf(t) = // than placed in parallel, otherwise, the later branches will appear in the // else branch of all its previous branches. **WORK IN PROGRESS** // -// :ucs:postprocess +// :ducs:postprocess fun balance(t: Tree['A]): Tree['A] = if t is Node(x, l, r, _) and height(r) - height(l) diff --git a/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls index f9ac187c..7de5c13a 100644 --- a/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls +++ b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls @@ -22,7 +22,7 @@ fun sum(acc, xs) = // FIXME: Remove redundant `_ -> (ucs$args_xs$Some_0$Cons).1`. // Why are they everywhere? -:ucs:postprocess.result +:ducs:postprocess.result fun test(xs) = if xs is Some(Cons("add", Cons(x, Cons(y, Nil)))) then x + y diff --git a/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls b/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls index 6238ea82..89d276af 100644 --- a/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls +++ b/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls @@ -1,6 +1,6 @@ :NewDefs -:ucs:postprocess.result,desugared +:ducs:postprocess.result,desugared fun mixed_literals(v) = if v is true then "true" @@ -18,7 +18,7 @@ fun mixed_literals(v) = //│ ╙── //│ fun mixed_literals: (1 | 2 | false | true) -> ("1" | "2" | "false" | "true") -:ucs:postprocess.result +:ducs:postprocess.result fun separated_by_and(v) = if v is true then "true" @@ -32,7 +32,7 @@ fun separated_by_and(v) = //│ ╙── //│ fun separated_by_and: Bool -> ("false" | "true") -:ucs:postprocess.result +:ducs:postprocess.result fun dual_patterns(x, y) = if x is "some" and y is "none" then 0 @@ -51,7 +51,7 @@ fun dual_patterns(x, y) = //│ | | | | | | | "none" -> 3 //│ fun dual_patterns: ("none" | "some", "none" | "some") -> (0 | 1 | 2 | 3) -:ucs:postprocess.result +:ducs:postprocess.result fun unordered_dual_patterns(x, y) = if x is "some" and y is "none" then 0 diff --git a/shared/src/test/diff/ucs/Hygiene.mls b/shared/src/test/diff/ucs/Hygiene.mls index d2ea541b..87fba27a 100644 --- a/shared/src/test/diff/ucs/Hygiene.mls +++ b/shared/src/test/diff/ucs/Hygiene.mls @@ -7,7 +7,7 @@ class Right[out T](value: T) //│ class Left[T](value: T) //│ class Right[T](value: T) -:ucs:postprocess.result +:ducs:postprocess.result fun foo(x) = if x is Some(Left(y)) then x Some(x) then x diff --git a/shared/src/test/diff/ucs/HygienicBindings.mls b/shared/src/test/diff/ucs/HygienicBindings.mls index 651de5de..c7b88ca4 100644 --- a/shared/src/test/diff/ucs/HygienicBindings.mls +++ b/shared/src/test/diff/ucs/HygienicBindings.mls @@ -39,7 +39,7 @@ fun h0(a) = // If a class parameter is bound to the same variable in different branches, // the bindings can be merged and can be typed and coverage checked. See the // desugared version below. -:ucs:postprocess.result +:ducs:postprocess.result fun h0'(a) = if a is Some(x) and x is Left(y) then y @@ -92,7 +92,7 @@ fun h2(a) = a is None then 0 //│ fun h2: forall 'a. (None | Some[Left['a]]) -> (0 | 'a) -:ucs:postprocess.result +:ducs:postprocess.result fun h3(x, y, f, p) = if x is _ and f(x) is y and p(x) then y @@ -125,7 +125,7 @@ h3("anything", "anything", _ => "not me", _ => false) ~~> "anyway" //│ = undefined -:ucs:postprocess.result +:ducs:postprocess.result fun h4(x, y, p) = if x is y and p(x) then y @@ -158,7 +158,7 @@ h4("anything", "not me", _ => false) //│ res //│ = 'default' -:ucs:postprocess.result +:ducs:postprocess.result fun h5(x, y, p) = if x is Some(y) and p(x) then y diff --git a/shared/src/test/diff/ucs/InterleavedLet.mls b/shared/src/test/diff/ucs/InterleavedLet.mls index f0840717..1af6c8f4 100644 --- a/shared/src/test/diff/ucs/InterleavedLet.mls +++ b/shared/src/test/diff/ucs/InterleavedLet.mls @@ -21,7 +21,7 @@ class Right[B](rightValue: B) extends Either[nothing, B] //│ class Left[A](leftValue: A) extends Either //│ class Right[B](rightValue: B) extends Either -:ucs:normalize.result +:ducs:normalize.result fun q(x) = if x is Some and x is Some and x is Some then 0 diff --git a/shared/src/test/diff/ucs/Wildcard.mls b/shared/src/test/diff/ucs/Wildcard.mls index 006b99be..f3ab4a05 100644 --- a/shared/src/test/diff/ucs/Wildcard.mls +++ b/shared/src/test/diff/ucs/Wildcard.mls @@ -195,7 +195,7 @@ class Delta() // This should generate only one case expression instead of a chain of case // expressions. DO check the desugared term! -:ucs:postprocess.result +:ducs:postprocess.result fun w5(y) = if y is Alpha then "alpha" diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index e164c352..5bceb06c 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -25,7 +25,7 @@ abstract class ModeType { def dbg: Bool def dbgParsing: Bool def dbgSimplif: Bool - def dbgUCS: Bool + def dbgUCS: Opt[Set[Str]] def fullExceptionStack: Bool def stats: Bool def stdout: Bool @@ -150,9 +150,8 @@ class DiffTests explainErrors: Bool = false, dbg: Bool = false, dbgParsing: Bool = false, - dbgNuUCS: Opt[Set[Str]] = N, dbgSimplif: Bool = false, - dbgUCS: Bool = false, + dbgUCS: Opt[Set[Str]] = N, fullExceptionStack: Bool = false, stats: Bool = false, stdout: Bool = false, @@ -215,9 +214,8 @@ class DiffTests case "p" => mode.copy(showParse = true) case "d" => mode.copy(dbg = true) case "dp" => mode.copy(dbgParsing = true) - case DebugUCSFlags(ts) => mode.copy(dbgNuUCS = mode.dbgNuUCS.fold(S(ts))(ts0 => S(ts0 ++ ts))) + case DebugUCSFlags(x) => mode.copy(dbgUCS = mode.dbgUCS.fold(S(x))(y => S(y ++ x))) case "ds" => mode.copy(dbgSimplif = true) - case "ducs" => mode.copy(dbg = true, dbgUCS = true) case "s" => mode.copy(fullExceptionStack = true) case "v" | "verbose" => mode.copy(verbose = true) case "ex" | "explain" => mode.copy(expectTypeErrors = true, explainErrors = true) @@ -471,7 +469,6 @@ class DiffTests // if (mode.isDebugging) typer.resetState() if (mode.stats) typer.resetStats() typer.dbg = mode.dbg - typer.dbgUCS = mode.dbgUCS // typer.recordProvenances = !noProvs typer.recordProvenances = !noProvs && !mode.dbg && !mode.dbgSimplif || mode.explainErrors typer.generalizeCurriedFunctions = generalizeCurriedFunctions @@ -525,7 +522,7 @@ class DiffTests val vars: Map[Str, typer.SimpleType] = Map.empty val rootTypingUnit = TypingUnit(p.tops) val preTyper = new PreTyper { - override def debugTopicFilters = mode.dbgNuUCS + override def debugTopicFilters = mode.dbgUCS override def emitString(str: String): Unit = output(str) } // This scope will be passed to typer and code generator after @@ -1136,7 +1133,8 @@ object DiffTests { } object DebugUCSFlags { - private val pattern = "^ucs(?::\\s*([A-Za-z\\.-]+)(,\\s*[A-Za-z\\.-]+)*)?$".r + // E.g. "ducs", "ducs:foo", "ducs:foo,bar", "ducs:a.b.c,foo" + private val pattern = "^ducs(?::\\s*([A-Za-z\\.-]+)(,\\s*[A-Za-z\\.-]+)*)?$".r def unapply(flags: Str): Opt[Set[Str]] = flags match { case pattern(head, tail) => From 559a9672490c39d83c0050e9aedc8c18d67c354d Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Tue, 16 Jan 2024 18:34:47 +0800 Subject: [PATCH 074/143] Remove indentation of intermediate results from desugarer --- .../scala/mlscript/pretyper/PreTyper.scala | 2 +- .../scala/mlscript/pretyper/Traceable.scala | 13 ++- .../main/scala/mlscript/ucs/Desugarer.scala | 23 ++-- .../pretyper/ucs/coverage/SealedClasses.mls | 46 ++++---- .../pretyper/ucs/stages/Normalization.mls | 96 ++++++++-------- .../pretyper/ucs/stages/PostProcessing.mls | 62 +++++------ shared/src/test/diff/ucs/Hygiene.mls | 26 ++--- shared/src/test/diff/ucs/HygienicBindings.mls | 104 +++++++++--------- shared/src/test/diff/ucs/InterleavedLet.mls | 6 +- shared/src/test/diff/ucs/Wildcard.mls | 22 ++-- 10 files changed, 203 insertions(+), 197 deletions(-) diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index 072c57b9..c479536b 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -207,7 +207,7 @@ class PreTyper extends Traceable with Diagnosable with Desugarer { maybeSymbol }) } - printGraph(edges, println, "inheritance relations", "->") + printGraph(edges, println(_), "inheritance relations", "->") transitiveClosure(edges).foreachEntry { (self, bases) => self.baseTypes = bases println(s"base types of `${self.name}`: ${bases.iterator.map(_.name).mkString(", ")}") diff --git a/shared/src/main/scala/mlscript/pretyper/Traceable.scala b/shared/src/main/scala/mlscript/pretyper/Traceable.scala index 7d318d3c..1c4dc675 100644 --- a/shared/src/main/scala/mlscript/pretyper/Traceable.scala +++ b/shared/src/main/scala/mlscript/pretyper/Traceable.scala @@ -25,8 +25,13 @@ trait Traceable { /** Override this function to redirect debug messages. */ protected def emitString(str: Str): Unit = scala.Predef.println(str) - @inline private def printLineByLine(x: => Any): Unit = - x.toString.linesIterator.foreach { line => emitString("| " * debugIndent + line) } + @inline private def printLineByLine(x: => Any, withIndent: Bool): Unit = + x.toString.linesIterator.foreach( + if (withIndent) + line => emitString("| " * debugIndent + line) + else + emitString + ) protected def trace[T](pre: => Str)(thunk: => T)(post: T => Str = Traceable.noPostTrace): T = { println(pre) @@ -46,8 +51,8 @@ trait Traceable { @inline def traceNot[T](pre: => Str)(thunk: => T)(post: T => Str = Traceable.noPostTrace): T = thunk - @inline protected def println(x: => Any): Unit = - if (matchTopicFilters) printLineByLine(x) + @inline protected def println(x: => Any, withIndent: Bool = true): Unit = + if (matchTopicFilters) printLineByLine(x, withIndent) } object Traceable { diff --git a/shared/src/main/scala/mlscript/ucs/Desugarer.scala b/shared/src/main/scala/mlscript/ucs/Desugarer.scala index bbf4effb..8d3ccf33 100644 --- a/shared/src/main/scala/mlscript/ucs/Desugarer.scala +++ b/shared/src/main/scala/mlscript/ucs/Desugarer.scala @@ -205,10 +205,11 @@ trait Desugarer extends Transformation // Stage 0: Transformation val transformed = traceWithTopic("transform") { println("STEP 0") - val transformed = transform(`if`) - println("Transformed UCS term:") - println(showSplit(transformed)) - transformed + transform(`if`) + } + traceWithTopic("transform.result") { + println("Transformed UCS term:", withIndent = false) + println(showSplit(transformed), withIndent = false) } // Stage 1: Desugaring val desugared = traceWithTopic("desugar") { @@ -216,8 +217,8 @@ trait Desugarer extends Transformation desugar(transformed) } traceWithTopic("desugar.result") { - println("Desugared UCS term:") - println(showSplit(desugared)) + println("Desugared UCS term:", withIndent = false) + println(showSplit(desugared), withIndent = false) } traceWithTopic("traverse") { println("STEP 1.5") @@ -229,8 +230,8 @@ trait Desugarer extends Transformation normalize(desugared) } traceWithTopic("normalize.result") { - println("Normalized UCS term:") - println(showNormalizedTerm(normalized)) + println("Normalized UCS term:", withIndent = false) + println(showNormalizedTerm(normalized), withIndent = false) } // Stage 3: Post-processing val postProcessed = traceWithTopic("postprocess") { @@ -238,8 +239,8 @@ trait Desugarer extends Transformation postProcess(normalized) } traceWithTopic("postprocess.result") { - println("Post-processed UCS term:") - println(showNormalizedTerm(postProcessed)) + println("Post-processed UCS term:", withIndent = false) + println(showNormalizedTerm(postProcessed), withIndent = false) } // Stage 4: Coverage checking traceWithTopic("coverage") { @@ -249,7 +250,7 @@ trait Desugarer extends Transformation raiseMany(diagnostics) } traceWithTopic("desugared") { - println(s"Desugared term: ${postProcessed.showDbg}") + println(s"Desugared term: ${postProcessed.showDbg}", withIndent = false) } // Epilogue `if`.desugaredTerm = S(postProcessed) diff --git a/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls b/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls index f81108c8..d852038b 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls @@ -15,11 +15,11 @@ fun is_value_explicit_refinement(term) = Abs(_, _) then true Var(_) then false App(_, _) then false -//│ | | | | | | | Desugared UCS term: -//│ | | | | | | | if term*‡ is refined Term -//│ | | | | | | | term*‡ is Abs then true -//│ | | | | | | | term*‡ is Var then false -//│ | | | | | | | term*‡ is App then false +//│ Desugared UCS term: +//│ if term*‡ is refined Term +//│ term*‡ is Abs then true +//│ term*‡ is Var then false +//│ term*‡ is App then false //│ fun is_value_explicit_refinement: (Abs | App | Var) -> Bool :ducs:normalize.result,postprocess.result @@ -28,24 +28,24 @@ fun is_value_automatic_refinement(term) = Abs(_, _) then true Var(_) then false App(_, _) then false -//│ | | | | | | | Normalized UCS term: -//│ | | | | | | | case term*‡ of -//│ | | | | | | | refined Term*◊ -> -//│ | | | | | | | case term*‡ of -//│ | | | | | | | Abs*◊ -> true -//│ | | | | | | | _ -> -//│ | | | | | | | case term*‡ of -//│ | | | | | | | Var*◊ -> false -//│ | | | | | | | _ -> -//│ | | | | | | | case term*‡ of -//│ | | | | | | | App*◊ -> false -//│ | | | | | | | Post-processed UCS term: -//│ | | | | | | | case term*‡ of -//│ | | | | | | | refined Term*◊ -> -//│ | | | | | | | case term*‡ of -//│ | | | | | | | Abs*◊ -> true -//│ | | | | | | | Var*◊ -> false -//│ | | | | | | | App*◊ -> false +//│ Normalized UCS term: +//│ case term*‡ of +//│ refined Term*◊ -> +//│ case term*‡ of +//│ Abs*◊ -> true +//│ _ -> +//│ case term*‡ of +//│ Var*◊ -> false +//│ _ -> +//│ case term*‡ of +//│ App*◊ -> false +//│ Post-processed UCS term: +//│ case term*‡ of +//│ refined Term*◊ -> +//│ case term*‡ of +//│ Abs*◊ -> true +//│ Var*◊ -> false +//│ App*◊ -> false //│ fun is_value_automatic_refinement: (Abs | App | Var) -> Bool :e diff --git a/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls index 7de5c13a..cbef36d4 100644 --- a/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls +++ b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls @@ -30,52 +30,52 @@ fun test(xs) = Some(Cons("sum", xs)) then sum(0, xs) Some(Nil) then "nothing" None then "nothing" -//│ | | | | | | | Post-processed UCS term: -//│ | | | | | | | case xs*‡ of -//│ | | | | | | | Some*◊ -> -//│ | | | | | | | let ucs$args_xs$Some*† = (Some).unapply(xs,) -//│ | | | | | | | let xs$Some_0*‡ = (ucs$args_xs$Some).0 -//│ | | | | | | | case xs$Some_0*‡ of -//│ | | | | | | | Cons*◊ -> -//│ | | | | | | | let ucs$args_xs$Some_0$Cons*† = (Cons).unapply(xs$Some_0,) -//│ | | | | | | | let xs$Some_0$Cons_0*‡ = (ucs$args_xs$Some_0$Cons).0 -//│ | | | | | | | let xs$Some_0$Cons_1*‡ = (ucs$args_xs$Some_0$Cons).1 -//│ | | | | | | | case xs$Some_0$Cons_0*‡ of -//│ | | | | | | | "add" -> -//│ | | | | | | | case xs$Some_0$Cons_1*‡ of -//│ | | | | | | | Cons*◊ -> -//│ | | | | | | | let ucs$args_xs$Some_0$Cons_1$Cons*† = (Cons).unapply(xs$Some_0$Cons_1,) -//│ | | | | | | | let x*‡ = (ucs$args_xs$Some_0$Cons_1$Cons).0 -//│ | | | | | | | let xs$Some_0$Cons_1$Cons_1*‡ = (ucs$args_xs$Some_0$Cons_1$Cons).1 -//│ | | | | | | | case xs$Some_0$Cons_1$Cons_1*‡ of -//│ | | | | | | | Cons*◊ -> -//│ | | | | | | | let ucs$args_xs$Some_0$Cons_1$Cons_1$Cons*† = (Cons).unapply(xs$Some_0$Cons_1$Cons_1,) -//│ | | | | | | | let y*‡ = (ucs$args_xs$Some_0$Cons_1$Cons_1$Cons).0 -//│ | | | | | | | let xs$Some_0$Cons_1$Cons_1$Cons_1*‡ = (ucs$args_xs$Some_0$Cons_1$Cons_1$Cons).1 -//│ | | | | | | | case xs$Some_0$Cons_1$Cons_1$Cons_1*‡ of -//│ | | | | | | | Nil*† -> +(x, y,) -//│ | | | | | | | _ -> (ucs$args_xs$Some_0$Cons).1 -//│ | | | | | | | _ -> (ucs$args_xs$Some_0$Cons).1 -//│ | | | | | | | _ -> (ucs$args_xs$Some_0$Cons).1 -//│ | | | | | | | "sum" -> -//│ | | | | | | | let xs*‡ = (ucs$args_xs$Some_0$Cons).1 -//│ | | | | | | | sum(0, xs,) -//│ | | | | | | | "mul" -> -//│ | | | | | | | case xs$Some_0$Cons_1*‡ of -//│ | | | | | | | Cons*◊ -> -//│ | | | | | | | let ucs$args_xs$Some_0$Cons_1$Cons*† = (Cons).unapply(xs$Some_0$Cons_1,) -//│ | | | | | | | let x*‡ = (ucs$args_xs$Some_0$Cons_1$Cons).0 -//│ | | | | | | | let xs$Some_0$Cons_1$Cons_1*‡ = (ucs$args_xs$Some_0$Cons_1$Cons).1 -//│ | | | | | | | case xs$Some_0$Cons_1$Cons_1*‡ of -//│ | | | | | | | Cons*◊ -> -//│ | | | | | | | let ucs$args_xs$Some_0$Cons_1$Cons_1$Cons*† = (Cons).unapply(xs$Some_0$Cons_1$Cons_1,) -//│ | | | | | | | let y*‡ = (ucs$args_xs$Some_0$Cons_1$Cons_1$Cons).0 -//│ | | | | | | | let xs$Some_0$Cons_1$Cons_1$Cons_1*‡ = (ucs$args_xs$Some_0$Cons_1$Cons_1$Cons).1 -//│ | | | | | | | case xs$Some_0$Cons_1$Cons_1$Cons_1*‡ of -//│ | | | | | | | Nil*† -> *(x, y,) -//│ | | | | | | | _ -> (ucs$args_xs$Some_0$Cons).1 -//│ | | | | | | | _ -> (ucs$args_xs$Some_0$Cons).1 -//│ | | | | | | | _ -> (ucs$args_xs$Some_0$Cons).1 -//│ | | | | | | | Nil*† -> "nothing" -//│ | | | | | | | None*† -> "nothing" +//│ Post-processed UCS term: +//│ case xs*‡ of +//│ Some*◊ -> +//│ let ucs$args_xs$Some*† = (Some).unapply(xs,) +//│ let xs$Some_0*‡ = (ucs$args_xs$Some).0 +//│ case xs$Some_0*‡ of +//│ Cons*◊ -> +//│ let ucs$args_xs$Some_0$Cons*† = (Cons).unapply(xs$Some_0,) +//│ let xs$Some_0$Cons_0*‡ = (ucs$args_xs$Some_0$Cons).0 +//│ let xs$Some_0$Cons_1*‡ = (ucs$args_xs$Some_0$Cons).1 +//│ case xs$Some_0$Cons_0*‡ of +//│ "add" -> +//│ case xs$Some_0$Cons_1*‡ of +//│ Cons*◊ -> +//│ let ucs$args_xs$Some_0$Cons_1$Cons*† = (Cons).unapply(xs$Some_0$Cons_1,) +//│ let x*‡ = (ucs$args_xs$Some_0$Cons_1$Cons).0 +//│ let xs$Some_0$Cons_1$Cons_1*‡ = (ucs$args_xs$Some_0$Cons_1$Cons).1 +//│ case xs$Some_0$Cons_1$Cons_1*‡ of +//│ Cons*◊ -> +//│ let ucs$args_xs$Some_0$Cons_1$Cons_1$Cons*† = (Cons).unapply(xs$Some_0$Cons_1$Cons_1,) +//│ let y*‡ = (ucs$args_xs$Some_0$Cons_1$Cons_1$Cons).0 +//│ let xs$Some_0$Cons_1$Cons_1$Cons_1*‡ = (ucs$args_xs$Some_0$Cons_1$Cons_1$Cons).1 +//│ case xs$Some_0$Cons_1$Cons_1$Cons_1*‡ of +//│ Nil*† -> +(x, y,) +//│ _ -> (ucs$args_xs$Some_0$Cons).1 +//│ _ -> (ucs$args_xs$Some_0$Cons).1 +//│ _ -> (ucs$args_xs$Some_0$Cons).1 +//│ "sum" -> +//│ let xs*‡ = (ucs$args_xs$Some_0$Cons).1 +//│ sum(0, xs,) +//│ "mul" -> +//│ case xs$Some_0$Cons_1*‡ of +//│ Cons*◊ -> +//│ let ucs$args_xs$Some_0$Cons_1$Cons*† = (Cons).unapply(xs$Some_0$Cons_1,) +//│ let x*‡ = (ucs$args_xs$Some_0$Cons_1$Cons).0 +//│ let xs$Some_0$Cons_1$Cons_1*‡ = (ucs$args_xs$Some_0$Cons_1$Cons).1 +//│ case xs$Some_0$Cons_1$Cons_1*‡ of +//│ Cons*◊ -> +//│ let ucs$args_xs$Some_0$Cons_1$Cons_1$Cons*† = (Cons).unapply(xs$Some_0$Cons_1$Cons_1,) +//│ let y*‡ = (ucs$args_xs$Some_0$Cons_1$Cons_1$Cons).0 +//│ let xs$Some_0$Cons_1$Cons_1$Cons_1*‡ = (ucs$args_xs$Some_0$Cons_1$Cons_1$Cons).1 +//│ case xs$Some_0$Cons_1$Cons_1$Cons_1*‡ of +//│ Nil*† -> *(x, y,) +//│ _ -> (ucs$args_xs$Some_0$Cons).1 +//│ _ -> (ucs$args_xs$Some_0$Cons).1 +//│ _ -> (ucs$args_xs$Some_0$Cons).1 +//│ Nil*† -> "nothing" +//│ None*† -> "nothing" //│ fun test: (None | Some[Cons[nothing] | Nil]) -> ("nothing" | Int | List[nothing]) diff --git a/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls b/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls index 89d276af..b8b9db5b 100644 --- a/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls +++ b/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls @@ -7,13 +7,13 @@ fun mixed_literals(v) = false then "false" 1 then "1" 2 then "2" -//│ | | | | | | | Post-processed UCS term: -//│ | | | | | | | case v*‡ of -//│ | | | | | | | true*† -> "true" -//│ | | | | | | | false*† -> "false" -//│ | | | | | | | 2 -> "2" -//│ | | | | | | | 1 -> "1" -//│ | | | | | | | Desugared term: case v of { true => "true"; false => "false"; 2 => "2"; 1 => "1" } +//│ Post-processed UCS term: +//│ case v*‡ of +//│ true*† -> "true" +//│ false*† -> "false" +//│ 2 -> "2" +//│ 1 -> "1" +//│ Desugared term: case v of { true => "true"; false => "false"; 2 => "2"; 1 => "1" } //│ ╙── //│ ╙── //│ fun mixed_literals: (1 | 2 | false | true) -> ("1" | "2" | "false" | "true") @@ -24,10 +24,10 @@ fun separated_by_and(v) = true then "true" _ and v is false then "false" -//│ | | | | | | | Post-processed UCS term: -//│ | | | | | | | case v*‡ of -//│ | | | | | | | true*† -> "true" -//│ | | | | | | | false*† -> "false" +//│ Post-processed UCS term: +//│ case v*‡ of +//│ true*† -> "true" +//│ false*† -> "false" //│ ╙── //│ ╙── //│ fun separated_by_and: Bool -> ("false" | "true") @@ -39,16 +39,16 @@ fun dual_patterns(x, y) = x is "none" and y is "some" then 1 x is "some" and y is "some" then 2 x is "none" and y is "none" then 3 -//│ | | | | | | | Post-processed UCS term: -//│ | | | | | | | case x*‡ of -//│ | | | | | | | "some" -> -//│ | | | | | | | case y*‡ of -//│ | | | | | | | "none" -> 0 -//│ | | | | | | | "some" -> 2 -//│ | | | | | | | "none" -> -//│ | | | | | | | case y*‡ of -//│ | | | | | | | "some" -> 1 -//│ | | | | | | | "none" -> 3 +//│ Post-processed UCS term: +//│ case x*‡ of +//│ "some" -> +//│ case y*‡ of +//│ "none" -> 0 +//│ "some" -> 2 +//│ "none" -> +//│ case y*‡ of +//│ "some" -> 1 +//│ "none" -> 3 //│ fun dual_patterns: ("none" | "some", "none" | "some") -> (0 | 1 | 2 | 3) :ducs:postprocess.result @@ -58,14 +58,14 @@ fun unordered_dual_patterns(x, y) = y is "some" and x is "none" then 1 y is "some" and x is "some" then 2 x is "none" and y is "none" then 3 -//│ | | | | | | | Post-processed UCS term: -//│ | | | | | | | case x*‡ of -//│ | | | | | | | "some" -> -//│ | | | | | | | case y*‡ of -//│ | | | | | | | "none" -> 0 -//│ | | | | | | | "some" -> 2 -//│ | | | | | | | "none" -> -//│ | | | | | | | case y*‡ of -//│ | | | | | | | "some" -> 1 -//│ | | | | | | | "none" -> 3 +//│ Post-processed UCS term: +//│ case x*‡ of +//│ "some" -> +//│ case y*‡ of +//│ "none" -> 0 +//│ "some" -> 2 +//│ "none" -> +//│ case y*‡ of +//│ "some" -> 1 +//│ "none" -> 3 //│ fun unordered_dual_patterns: ("none" | "some", "none" | "some") -> (0 | 1 | 2 | 3) diff --git a/shared/src/test/diff/ucs/Hygiene.mls b/shared/src/test/diff/ucs/Hygiene.mls index 87fba27a..dbcde8e8 100644 --- a/shared/src/test/diff/ucs/Hygiene.mls +++ b/shared/src/test/diff/ucs/Hygiene.mls @@ -11,19 +11,19 @@ class Right[out T](value: T) fun foo(x) = if x is Some(Left(y)) then x Some(x) then x -//│ | | | | | Post-processed UCS term: -//│ | | | | | case x*‡ of -//│ | | | | | Some*◊ -> -//│ | | | | | let ucs$args_x$Some*† = (Some).unapply(x,) -//│ | | | | | let x$Some_0*‡ = (ucs$args_x$Some).0 -//│ | | | | | case x$Some_0*‡ of -//│ | | | | | Left*◊ -> -//│ | | | | | let ucs$args_x$Some_0$Left*† = (Left).unapply(x$Some_0,) -//│ | | | | | let y*‡ = (ucs$args_x$Some_0$Left).0 -//│ | | | | | x -//│ | | | | | _ -> -//│ | | | | | let x*‡ = (ucs$args_x$Some).0 -//│ | | | | | x +//│ Post-processed UCS term: +//│ case x*‡ of +//│ Some*◊ -> +//│ let ucs$args_x$Some*† = (Some).unapply(x,) +//│ let x$Some_0*‡ = (ucs$args_x$Some).0 +//│ case x$Some_0*‡ of +//│ Left*◊ -> +//│ let ucs$args_x$Some_0$Left*† = (Left).unapply(x$Some_0,) +//│ let y*‡ = (ucs$args_x$Some_0$Left).0 +//│ x +//│ _ -> +//│ let x*‡ = (ucs$args_x$Some).0 +//│ x //│ fun foo: forall 'T. Some[(Left[anything] | Object & ~#Left) & 'T] -> (Some['T] | 'T) foo(Some(Left(1))) diff --git a/shared/src/test/diff/ucs/HygienicBindings.mls b/shared/src/test/diff/ucs/HygienicBindings.mls index c7b88ca4..60bbe085 100644 --- a/shared/src/test/diff/ucs/HygienicBindings.mls +++ b/shared/src/test/diff/ucs/HygienicBindings.mls @@ -45,21 +45,21 @@ fun h0'(a) = a is Some(x) and x is Left(y) then y a is Some(x) and x is Right(z) then z a is None then 0 -//│ | | | | | | | Post-processed UCS term: -//│ | | | | | | | case a*‡ of -//│ | | | | | | | Some*◊ -> -//│ | | | | | | | let ucs$args_a$Some*† = (Some).unapply(a,) -//│ | | | | | | | let x*‡ = (ucs$args_a$Some).0 -//│ | | | | | | | case x*‡ of -//│ | | | | | | | Left*◊ -> -//│ | | | | | | | let ucs$args_x$Left*† = (Left).unapply(x,) -//│ | | | | | | | let y*‡ = (ucs$args_x$Left).0 -//│ | | | | | | | y -//│ | | | | | | | Right*◊ -> -//│ | | | | | | | let ucs$args_x$Right*† = (Right).unapply(x,) -//│ | | | | | | | let z*‡ = (ucs$args_x$Right).0 -//│ | | | | | | | z -//│ | | | | | | | None*† -> 0 +//│ Post-processed UCS term: +//│ case a*‡ of +//│ Some*◊ -> +//│ let ucs$args_a$Some*† = (Some).unapply(a,) +//│ let x*‡ = (ucs$args_a$Some).0 +//│ case x*‡ of +//│ Left*◊ -> +//│ let ucs$args_x$Left*† = (Left).unapply(x,) +//│ let y*‡ = (ucs$args_x$Left).0 +//│ y +//│ Right*◊ -> +//│ let ucs$args_x$Right*† = (Right).unapply(x,) +//│ let z*‡ = (ucs$args_x$Right).0 +//│ z +//│ None*† -> 0 //│ fun h0': forall 'a. (None | Some[Left['a] | Right['a]]) -> (0 | 'a) // However, if the class parameter is bound to different variables in different @@ -98,18 +98,18 @@ fun h3(x, y, f, p) = _ and f(x) is y and p(x) then y None then y _ then "anyway" -//│ | | | | | | | Post-processed UCS term: -//│ | | | | | | | let ucs$scrut$0*‡ = f(x,) -//│ | | | | | | | let ucs$shadow$0 = y -//│ | | | | | | | let y*‡ = ucs$scrut$0 -//│ | | | | | | | let ucs$test$0*† = p(x,) : Bool -//│ | | | | | | | case ucs$test$0*† of -//│ | | | | | | | true*† -> y -//│ | | | | | | | _ -> -//│ | | | | | | | let y*‡ = ucs$shadow$0 -//│ | | | | | | | case x*‡ of -//│ | | | | | | | None*† -> y -//│ | | | | | | | _ -> "anyway" +//│ Post-processed UCS term: +//│ let ucs$scrut$0*‡ = f(x,) +//│ let ucs$shadow$0 = y +//│ let y*‡ = ucs$scrut$0 +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true*† -> y +//│ _ -> +//│ let y*‡ = ucs$shadow$0 +//│ case x*‡ of +//│ None*† -> y +//│ _ -> "anyway" //│ fun h3: forall 'a 'b. (Object & 'a, 'b, 'a -> 'b, 'a -> Bool) -> ("anyway" | 'b) @@ -131,17 +131,17 @@ fun h4(x, y, p) = y and p(x) then y None then y _ then "default" -//│ | | | | | | | Post-processed UCS term: -//│ | | | | | | | let ucs$shadow$0 = y -//│ | | | | | | | let y*‡ = x -//│ | | | | | | | let ucs$test$0*† = p(x,) : Bool -//│ | | | | | | | case ucs$test$0*† of -//│ | | | | | | | true*† -> y -//│ | | | | | | | _ -> -//│ | | | | | | | let y*‡ = ucs$shadow$0 -//│ | | | | | | | case x*‡ of -//│ | | | | | | | None*† -> y -//│ | | | | | | | _ -> "default" +//│ Post-processed UCS term: +//│ let ucs$shadow$0 = y +//│ let y*‡ = x +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true*† -> y +//│ _ -> +//│ let y*‡ = ucs$shadow$0 +//│ case x*‡ of +//│ None*† -> y +//│ _ -> "default" //│ fun h4: forall 'a 'b. (Object & 'a, 'b, 'a -> Bool) -> ("default" | 'a | 'b) h4("should be me", "not me", _ => true) @@ -164,20 +164,20 @@ fun h5(x, y, p) = Some(y) and p(x) then y None then y _ then y -//│ | | | | | | | Post-processed UCS term: -//│ | | | | | | | case x*‡ of -//│ | | | | | | | Some*◊ -> -//│ | | | | | | | let ucs$args_x$Some*† = (Some).unapply(x,) -//│ | | | | | | | let ucs$shadow$0 = y -//│ | | | | | | | let y*‡ = (ucs$args_x$Some).0 -//│ | | | | | | | let ucs$test$0*† = p(x,) : Bool -//│ | | | | | | | case ucs$test$0*† of -//│ | | | | | | | true*† -> y -//│ | | | | | | | _ -> -//│ | | | | | | | let y*‡ = ucs$shadow$0 -//│ | | | | | | | y -//│ | | | | | | | None*† -> y -//│ | | | | | | | _ -> y +//│ Post-processed UCS term: +//│ case x*‡ of +//│ Some*◊ -> +//│ let ucs$args_x$Some*† = (Some).unapply(x,) +//│ let ucs$shadow$0 = y +//│ let y*‡ = (ucs$args_x$Some).0 +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true*† -> y +//│ _ -> +//│ let y*‡ = ucs$shadow$0 +//│ y +//│ None*† -> y +//│ _ -> y //│ fun h5: forall 'a. (Object & ~#Some | Some['a], 'a, Some[nothing] -> Bool) -> 'a h5(Some(1), 2, justTrue) ~~> 1 diff --git a/shared/src/test/diff/ucs/InterleavedLet.mls b/shared/src/test/diff/ucs/InterleavedLet.mls index 1af6c8f4..b17f8fd4 100644 --- a/shared/src/test/diff/ucs/InterleavedLet.mls +++ b/shared/src/test/diff/ucs/InterleavedLet.mls @@ -25,9 +25,9 @@ class Right[B](rightValue: B) extends Either[nothing, B] fun q(x) = if x is Some and x is Some and x is Some then 0 -//│ | | | | | | | Normalized UCS term: -//│ | | | | | | | case x*‡ of -//│ | | | | | | | Some*◊ -> 0 +//│ Normalized UCS term: +//│ case x*‡ of +//│ Some*◊ -> 0 //│ fun q: Some[anything] -> 0 :e diff --git a/shared/src/test/diff/ucs/Wildcard.mls b/shared/src/test/diff/ucs/Wildcard.mls index f3ab4a05..3c6f2e8a 100644 --- a/shared/src/test/diff/ucs/Wildcard.mls +++ b/shared/src/test/diff/ucs/Wildcard.mls @@ -206,17 +206,17 @@ fun w5(y) = _ and y is Delta then "delta" _ then "unknown" -//│ | | | | | | | Post-processed UCS term: -//│ | | | | | | | case y*‡ of -//│ | | | | | | | Alpha*◊ -> "alpha" -//│ | | | | | | | Gamma*◊ -> "gamma" -//│ | | | | | | | Delta*◊ -> "delta" -//│ | | | | | | | Beta*◊ -> "beta" -//│ | | | | | | | _ -> -//│ | | | | | | | case y*‡ of -//│ | | | | | | | _ -> -//│ | | | | | | | case y*‡ of -//│ | | | | | | | _ -> "unknown" +//│ Post-processed UCS term: +//│ case y*‡ of +//│ Alpha*◊ -> "alpha" +//│ Gamma*◊ -> "gamma" +//│ Delta*◊ -> "delta" +//│ Beta*◊ -> "beta" +//│ _ -> +//│ case y*‡ of +//│ _ -> +//│ case y*‡ of +//│ _ -> "unknown" //│ fun w5: (Alpha | Beta | Delta | Gamma | Object & ~#Alpha & ~#Beta & ~#Delta & ~#Gamma) -> ("alpha" | "beta" | "delta" | "gamma" | "unknown") w5(0) From 33b33f57730f9d1b529bc9d2e5a33da1a140a8d9 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Tue, 16 Jan 2024 20:03:56 +0800 Subject: [PATCH 075/143] Remove `mlscript.ucs.helpers` object --- .../scala/mlscript/pretyper/Traceable.scala | 4 +- .../src/main/scala/mlscript/ucs/helpers.scala | 47 ------------------- .../mlscript/ucs/stages/Normalization.scala | 1 - .../mlscript/ucs/stages/PartialTerm.scala | 3 +- .../mlscript/ucs/stages/Transformation.scala | 19 ++------ .../scala/mlscript/ucs/stages/package.scala | 37 +++++++++++++++ .../{TransfromUCS.mls => Transformation.mls} | 33 ++++++++++++- 7 files changed, 76 insertions(+), 68 deletions(-) delete mode 100644 shared/src/main/scala/mlscript/ucs/helpers.scala rename shared/src/test/diff/pretyper/ucs/stages/{TransfromUCS.mls => Transformation.mls} (60%) diff --git a/shared/src/main/scala/mlscript/pretyper/Traceable.scala b/shared/src/main/scala/mlscript/pretyper/Traceable.scala index 1c4dc675..df7aee9d 100644 --- a/shared/src/main/scala/mlscript/pretyper/Traceable.scala +++ b/shared/src/main/scala/mlscript/pretyper/Traceable.scala @@ -51,8 +51,8 @@ trait Traceable { @inline def traceNot[T](pre: => Str)(thunk: => T)(post: T => Str = Traceable.noPostTrace): T = thunk - @inline protected def println(x: => Any, withIndent: Bool = true): Unit = - if (matchTopicFilters) printLineByLine(x, withIndent) + @inline protected def println(x: => Any, withIndent: Bool = true, force: Bool = false): Unit = + if (force || matchTopicFilters) printLineByLine(x, withIndent) } object Traceable { diff --git a/shared/src/main/scala/mlscript/ucs/helpers.scala b/shared/src/main/scala/mlscript/ucs/helpers.scala deleted file mode 100644 index da9b3029..00000000 --- a/shared/src/main/scala/mlscript/ucs/helpers.scala +++ /dev/null @@ -1,47 +0,0 @@ -package mlscript.ucs - -import scala.collection.mutable.{Set => MutSet} - -import mlscript._ -import mlscript.utils.shorthands._ - -object helpers { - import stages.mkBinOp - - /** - * Split a term into two parts: the pattern and the extra test. - * This is used to extract patterns from UCS conjunctions. For example, - * the second line results in `IfThen(Some(xv) and xv == 0, ...)` - * in the following case. - * - * ``` - * if x is - * Some(xv) and xv == 0 then ... - * ``` - * - * We must separate `Some(xv)` from the term to complete the pattern - * `x is Some(xv)`. - * - * @param term a term which might contains a pattern and an extra test - * @return a tuple, whose the first element is the pattern and the second - * element is the extra test - */ - def separatePattern(term: Term, newDefs: Bool): (Term, Opt[Term]) = - term match { - case App( - App(and @ Var("and"), - Tup((_ -> Fld(_, lhs)) :: Nil)), - Tup((_ -> Fld(_, rhs)) :: Nil) - ) => // * Old-style operators - separatePattern(lhs, newDefs) match { - case (pattern, N) => (pattern, S(rhs)) - case (pattern, S(lshRhs)) => (pattern, S(mkBinOp(lshRhs, and, rhs, newDefs))) - } - case App(and @ Var("and"), PlainTup(lhs, rhs)) => - separatePattern(lhs, newDefs) match { - case (pattern, N) => (pattern, S(rhs)) - case (pattern, S(lshRhs)) => (pattern, S(mkBinOp(lshRhs, and, rhs, newDefs))) - } - case _ => (term, N) - } -} diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 6ffc8305..9fcc757f 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -9,7 +9,6 @@ import mlscript.ucs, mlscript.pretyper import ucs.{Desugarer, Lines, LinesOps, VariableGenerator} import ucs.context.{Context, ScrutineeData} import ucs.display.{showNormalizedTerm, showSplit} -import ucs.helpers._ import ucs.syntax.core.{Pattern, Branch, Split} import pretyper.Scope import pretyper.symbol._ diff --git a/shared/src/main/scala/mlscript/ucs/stages/PartialTerm.scala b/shared/src/main/scala/mlscript/ucs/stages/PartialTerm.scala index c9f8c0e5..7dac085b 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PartialTerm.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PartialTerm.scala @@ -1,7 +1,6 @@ package mlscript.ucs.stages -import mlscript.{Term, Var}, mlscript.utils, utils.shorthands._ -import mlscript.ucs.helpers._ +import mlscript.{Term, Var}, mlscript.utils._, shorthands._ /** * A `PartialTerm` represents a possibly incomplete term. diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index 384bd919..26f4ab0c 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -1,13 +1,12 @@ package mlscript.ucs.stages -import mlscript.ucs.syntax.source._ -import mlscript.ucs.{Desugarer, helpers} +import mlscript.ucs, ucs.Desugarer, ucs.syntax.source._ import mlscript.{If, IfBody, IfBlock, IfElse, IfLet, IfOpApp, IfOpsApp, IfThen} import mlscript.{Blk, Term, Var, App, Tup, Lit, Fld, Loc, NuFunDef, PlainTup} import mlscript.pretyper.Traceable import mlscript.Message, Message._ import mlscript.utils._, shorthands._ -import scala.collection.immutable, scala.annotation.tailrec, scala.util.chaining._ +import collection.immutable, annotation.tailrec, util.chaining._ /** * Transform the parsed AST into an AST similar to the one in the paper. @@ -114,12 +113,8 @@ trait Transformation { self: Desugarer with Traceable => trace(s"transformPatternMatching <== ${body.showDbg}") { body match { case IfThen(expr, rhs) => - separatePattern(expr) match { - case (pattern, S(extraTest)) => - PatternBranch(pattern, transformIfBody(IfThen(extraTest, rhs))).toSplit - case (pattern, N) => - PatternBranch(pattern, Split.default(rhs)).toSplit - } + val ::(head, tail) = splitAnd(expr) + PatternBranch(transformPattern(head), transformConjunction(tail, Split.then(rhs), false)).toSplit case IfOpApp(lhs, Var("and"), rhs) => val ::(head, tail) = splitAnd(lhs) PatternBranch(transformPattern(head), transformConjunction(tail, transformIfBody(rhs), false)).toSplit @@ -207,12 +202,6 @@ trait Transformation { self: Desugarer with Traceable => EmptyPattern(other) } - private def separatePattern(term: Term): (Pattern, Opt[Term]) = { - val (rawPattern, extraTest) = helpers.separatePattern(term, true) - println(s"pattern: ${rawPattern.showDbg} ;; test: ${extraTest.fold("_")(_.showDbg)}") - (transformPattern(rawPattern), extraTest) - } - /** * Split a term into a list of terms. Note that the return type is `::[Term]` * because there should be at least one term even we don't split. It used to diff --git a/shared/src/main/scala/mlscript/ucs/stages/package.scala b/shared/src/main/scala/mlscript/ucs/stages/package.scala index eb442f83..3be1c555 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/package.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/package.scala @@ -28,4 +28,41 @@ package object stages { def mkBinOp(lhs: Term, op: Var, rhs: Term, newDefs: Bool): Term = if (newDefs) App(op, PlainTup(lhs, rhs)) else App(App(op, mkMonuple(lhs)), mkMonuple(rhs)) + + /** + * Split a term into two parts: the pattern and the extra test. + * This is used to extract patterns from UCS conjunctions. For example, + * the second line results in `IfThen(Some(xv) and xv == 0, ...)` + * in the following case. + * + * ``` + * if x is + * Some(xv) and xv == 0 then ... + * ``` + * + * We must separate `Some(xv)` from the term to complete the pattern + * `x is Some(xv)`. + * + * @param term a term which might contains a pattern and an extra test + * @return a tuple, whose the first element is the pattern and the second + * element is the extra test + */ + def separatePattern(term: Term, newDefs: Bool): (Term, Opt[Term]) = + term match { + case App( + App(and @ Var("and"), + Tup((_ -> Fld(_, lhs)) :: Nil)), + Tup((_ -> Fld(_, rhs)) :: Nil) + ) => // * Old-style operators + separatePattern(lhs, newDefs) match { + case (pattern, N) => (pattern, S(rhs)) + case (pattern, S(lshRhs)) => (pattern, S(mkBinOp(lshRhs, and, rhs, newDefs))) + } + case App(and @ Var("and"), PlainTup(lhs, rhs)) => + separatePattern(lhs, newDefs) match { + case (pattern, N) => (pattern, S(rhs)) + case (pattern, S(lshRhs)) => (pattern, S(mkBinOp(lshRhs, and, rhs, newDefs))) + } + case _ => (term, N) + } } diff --git a/shared/src/test/diff/pretyper/ucs/stages/TransfromUCS.mls b/shared/src/test/diff/pretyper/ucs/stages/Transformation.mls similarity index 60% rename from shared/src/test/diff/pretyper/ucs/stages/TransfromUCS.mls rename to shared/src/test/diff/pretyper/ucs/stages/Transformation.mls index ef294f23..2a721769 100644 --- a/shared/src/test/diff/pretyper/ucs/stages/TransfromUCS.mls +++ b/shared/src/test/diff/pretyper/ucs/stages/Transformation.mls @@ -30,16 +30,47 @@ class Pair[A, B](x: A, y: B) { //│ fun mapSecond: forall 'C0. (f: B -> 'C0) -> Pair[A, 'C0] //│ } +:ducs:transform.result fun zipWith(f, xs, ys) = if xs is Cons(x, xs) and ys is Cons(y, ys) and zipWith(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) Nil and ys is Nil then Some(Nil) else None +//│ Transformed UCS term: +//│ if +//│ xs is +//│ Cons(x, xs) and ys is Cons(y, ys) and zipWith(f, xs, ys,) is Some(tail) then Some(Cons(f(x, y,), tail,),) +//│ Nil and ys is Nil then Some(Nil,) +//│ else None //│ fun zipWith: forall 'T 'T0 'T1 'T2. (('T0, 'T1) -> 'T2, Cons['T0] | Object & ~#Cons, Cons['T1] | Object & ~#Cons) -> (None | Some[in List['T2] & 'T out Nil | 'T | Cons['T2]]) - +:ducs:transform.result fun getOrElse[T](x: Option[T], default: T): T = if x is Some(value) then value None then default +//│ Transformed UCS term: +//│ if x is +//│ Some(value) then value +//│ None then default //│ fun getOrElse: forall 'T. (x: Option['T], default: 'T) -> 'T + +fun m3(x: Int): Bool = x % 3 == 0 +fun m5(x: Int): Bool = x % 5 == 0 +//│ fun m3: (x: Int) -> Bool +//│ fun m5: (x: Int) -> Bool + +:ducs:transform.result +fun f(x) = + if x is + Some(v) and m3(v) and m5(v) then "FizzBuzz" + Some(v) and m3(v) then "Fizz" + Some(v) and m5(v) then "Buzz" + else "meh" +//│ Transformed UCS term: +//│ if x is +//│ Some(v) and m3(v,) and m5(v,) then "FizzBuzz" +//│ Some(v) and m3(v,) then "Fizz" +//│ Some(v) and m5(v,) then "Buzz" +//│ else "meh" +//│ fun f: (Object & ~#Some | Some[Int]) -> ("Buzz" | "Fizz" | "FizzBuzz" | "meh") From 5c79d80e6158ea9bf231515d1749b8ed605a94d0 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Tue, 16 Jan 2024 20:07:08 +0800 Subject: [PATCH 076/143] Rename `ScrutineeData` to `Scrutinee` --- .../src/main/scala/mlscript/ucs/Desugarer.scala | 6 +++--- .../scala/mlscript/ucs/context/Context.scala | 10 +++++----- .../scala/mlscript/ucs/context/Matchable.scala | 10 +++++----- .../scala/mlscript/ucs/context/PatternInfo.scala | 12 ++++++------ .../{ScrutineeData.scala => Scrutinee.scala} | 14 +++++++------- .../scala/mlscript/ucs/context/package.scala | 6 +++--- .../mlscript/ucs/stages/CoverageChecking.scala | 6 +++--- .../scala/mlscript/ucs/stages/Desugaring.scala | 2 +- .../mlscript/ucs/stages/Normalization.scala | 16 ++++++++-------- .../mlscript/ucs/stages/PostProcessing.scala | 16 ++++++++-------- 10 files changed, 49 insertions(+), 49 deletions(-) rename shared/src/main/scala/mlscript/ucs/context/{ScrutineeData.scala => Scrutinee.scala} (95%) diff --git a/shared/src/main/scala/mlscript/ucs/Desugarer.scala b/shared/src/main/scala/mlscript/ucs/Desugarer.scala index 8d3ccf33..ca29850a 100644 --- a/shared/src/main/scala/mlscript/ucs/Desugarer.scala +++ b/shared/src/main/scala/mlscript/ucs/Desugarer.scala @@ -1,7 +1,7 @@ package mlscript.ucs import collection.mutable.{Map => MutMap} -import syntax.{source => s, core => c}, stages._, context.{Context, ScrutineeData} +import syntax.{source => s, core => c}, stages._, context.{Context, Scrutinee} import mlscript.ucs.display.{showNormalizedTerm, showSplit} import mlscript.pretyper.{PreTyper, Scope} import mlscript.pretyper.symbol._ @@ -72,7 +72,7 @@ trait Desugarer extends Transformation * A short hand for `nme.symbol.getScrutinee` but add a diagnostic message * to a local diagnostic archive (TODO) if there's any error. */ - def getOrCreateScrutinee(implicit context: Context): ScrutineeData = nme.symbolOption match { + def getOrCreateScrutinee(implicit context: Context): Scrutinee = nme.symbolOption match { case S(symbol: TermSymbol) => symbol.getOrCreateScrutinee case S(otherSymbol) => throw new DesugaringException( msg"Expected scrutinee symbol, found ${nme.symbol.name}" -> nme.toLoc :: Nil @@ -81,7 +81,7 @@ trait Desugarer extends Transformation } /** Associate the `Var` with a scrutinee and returns the same `Var`. */ - def withScrutinee(scrutinee: ScrutineeData)(implicit context: Context): Var = nme.symbolOption match { + def withScrutinee(scrutinee: Scrutinee)(implicit context: Context): Var = nme.symbolOption match { case S(symbol: TermSymbol) => symbol.addScrutinee(scrutinee) nme diff --git a/shared/src/main/scala/mlscript/ucs/context/Context.scala b/shared/src/main/scala/mlscript/ucs/context/Context.scala index 3e594157..63ccc906 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Context.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Context.scala @@ -43,16 +43,16 @@ class Context(originalTerm: If) { // ==================== /** The buffer contains all `ScrutineeData` created within this context. */ - private val scrutineeBuffer: Buffer[ScrutineeData] = Buffer.empty + private val scrutineeBuffer: Buffer[Scrutinee] = Buffer.empty - def freshScrutinee: ScrutineeData = { - val scrutinee = new ScrutineeData(this, N) + def freshScrutinee: Scrutinee = { + val scrutinee = new Scrutinee(this, N) scrutineeBuffer += scrutinee scrutinee } - private[context] def freshScrutinee(parent: ScrutineeData): ScrutineeData = { - val scrutinee = new ScrutineeData(this, S(parent)) + private[context] def freshScrutinee(parent: Scrutinee): Scrutinee = { + val scrutinee = new Scrutinee(this, S(parent)) scrutineeBuffer += scrutinee scrutinee } diff --git a/shared/src/main/scala/mlscript/ucs/context/Matchable.scala b/shared/src/main/scala/mlscript/ucs/context/Matchable.scala index ab85e6de..6fec7016 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Matchable.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Matchable.scala @@ -14,7 +14,7 @@ trait Matchable { * context. The reason why we need this map is that the same symbol may be * matched in different UCS terms. Each context corresponds to a UCS term. */ - private val scrutinees: MutMap[Context, ScrutineeData] = MutMap.empty + private val scrutinees: MutMap[Context, Scrutinee] = MutMap.empty /** * Get the scrutinee associated with the symbol in the given context. If @@ -22,23 +22,23 @@ trait Matchable { * given context. **Note**: This function should only be called from the * UCS desugarer. */ - private[ucs] def getOrCreateScrutinee(implicit context: Context): ScrutineeData = + private[ucs] def getOrCreateScrutinee(implicit context: Context): Scrutinee = scrutinees.getOrElseUpdate(context, context.freshScrutinee) /** Get the scrutinee associated with the symbol in the given context. */ - def getScrutinee(implicit context: Context): Opt[ScrutineeData] = scrutinees.get(context) + def getScrutinee(implicit context: Context): Opt[Scrutinee] = scrutinees.get(context) /** Check if the symbol is associated with a scrutinee in the given context. */ def isScrutinee(implicit context: Context): Bool = scrutinees.contains(context) /** Associate the symbol with a scrutinee in the given context. */ - private[ucs] def addScrutinee(scrutinee: ScrutineeData)(implicit context: Context): Unit = { + private[ucs] def addScrutinee(scrutinee: Scrutinee)(implicit context: Context): Unit = { require(!isScrutinee) // It should be impossible to add a scrutinee twice. scrutinees += context -> scrutinee } /** Associate the symbol with a scrutinee in the given context and returns the current object. */ - private[ucs] def withScrutinee(scrutinee: ScrutineeData)(implicit context: Context): this.type = { + private[ucs] def withScrutinee(scrutinee: Scrutinee)(implicit context: Context): this.type = { addScrutinee(scrutinee) this } diff --git a/shared/src/main/scala/mlscript/ucs/context/PatternInfo.scala b/shared/src/main/scala/mlscript/ucs/context/PatternInfo.scala index 9d6f15e6..07362bed 100644 --- a/shared/src/main/scala/mlscript/ucs/context/PatternInfo.scala +++ b/shared/src/main/scala/mlscript/ucs/context/PatternInfo.scala @@ -32,9 +32,9 @@ abstract class PatternInfo { } object PatternInfo { - class ClassLike(val classLikeSymbol: TypeSymbol, scrutinee: ScrutineeData) extends PatternInfo { + class ClassLike(val classLikeSymbol: TypeSymbol, scrutinee: Scrutinee) extends PatternInfo { private var unappliedVarOpt: Opt[Var] = N - private val parameters: MutSortedMap[Int, ScrutineeData] = MutSortedMap.empty + private val parameters: MutSortedMap[Int, Scrutinee] = MutSortedMap.empty /** * Get or create a sub-scrutinee for the given parameter index. @@ -43,7 +43,7 @@ object PatternInfo { * @return a `ScrutineeData` for the parameter whose parent scrutinee is the * current scrutinee */ - def getParameter(index: Int): ScrutineeData = { + def getParameter(index: Int): Scrutinee = { require(index >= 0) parameters.getOrElseUpdate(index, scrutinee.freshSubScrutinee) } @@ -73,10 +73,10 @@ object PatternInfo { Var(classLikeSymbol.name).withLoc(firstOccurrence).withSymbol(classLikeSymbol) } - class Tuple(scrutinee: ScrutineeData) extends PatternInfo { - private val fields: MutSortedMap[Int, ScrutineeData] = MutSortedMap.empty + class Tuple(scrutinee: Scrutinee) extends PatternInfo { + private val fields: MutSortedMap[Int, Scrutinee] = MutSortedMap.empty - def getField(index: Int): ScrutineeData = + def getField(index: Int): Scrutinee = fields.getOrElseUpdate(index, scrutinee.freshSubScrutinee) override def arity: Opt[Int] = fields.keysIterator.maxOption.map(_ + 1) diff --git a/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala similarity index 95% rename from shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala rename to shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala index 4ca6aede..315d2f06 100644 --- a/shared/src/main/scala/mlscript/ucs/context/ScrutineeData.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala @@ -10,8 +10,8 @@ import mlscript.IntLit import mlscript.StrLit import mlscript.UnitLit -class ScrutineeData(val context: Context, parent: Opt[ScrutineeData]) { - import ScrutineeData._ +class Scrutinee(val context: Context, parent: Opt[Scrutinee]) { + import Scrutinee._ private val locations: Buffer[Loc] = Buffer.empty private var generatedVarOpt: Opt[Var] = N @@ -34,7 +34,7 @@ class ScrutineeData(val context: Context, parent: Opt[ScrutineeData]) { def +=(alias: Var): Unit = alisesSet += alias - def withAlias(alias: Var): ScrutineeData = { this += alias; this } + def withAlias(alias: Var): Scrutinee = { this += alias; this } def aliasesIterator: Iterator[Var] = alisesSet.iterator @@ -101,7 +101,7 @@ class ScrutineeData(val context: Context, parent: Opt[ScrutineeData]) { (classLikePatternsStr ++ tuplePatternStr).mkString(", ") } - def freshSubScrutinee: ScrutineeData = context.freshScrutinee(this) + def freshSubScrutinee: Scrutinee = context.freshScrutinee(this) def toCaseSet: CaseSet = { import mlscript.ucs.context.Pattern @@ -118,14 +118,14 @@ class ScrutineeData(val context: Context, parent: Opt[ScrutineeData]) { } } -object ScrutineeData { +object Scrutinee { // We might need to move these method to a private `VarOps` because they may // emit diagnostics. import mlscript.Term import mlscript.pretyper.symbol.TermSymbol - def unapply(term: Term)(implicit context: Context): Opt[ScrutineeData] = term match { + def unapply(term: Term)(implicit context: Context): Opt[Scrutinee] = term match { case v: Var => v.symbol match { case symbol: TermSymbol => symbol.getScrutinee case _ => N @@ -134,7 +134,7 @@ object ScrutineeData { } object WithVar { - def unapply(term: Term)(implicit context: Context): Opt[(ScrutineeData, Var)] = term match { + def unapply(term: Term)(implicit context: Context): Opt[(Scrutinee, Var)] = term match { case v @ Var(_) => v.symbol match { case symbol: TermSymbol => symbol.getScrutinee.map(_ -> v) case _ => N diff --git a/shared/src/main/scala/mlscript/ucs/context/package.scala b/shared/src/main/scala/mlscript/ucs/context/package.scala index 03a1e46b..bc61762f 100644 --- a/shared/src/main/scala/mlscript/ucs/context/package.scala +++ b/shared/src/main/scala/mlscript/ucs/context/package.scala @@ -5,9 +5,9 @@ import mlscript.pretyper.symbol.TypeSymbol import mlscript.utils._, shorthands._ package object context { - type NamedScrutineeData = (Var -> ScrutineeData) + type NamedScrutinee = (Var -> Scrutinee) - type MatchRegistry = Map[NamedScrutineeData, CaseSet] + type MatchRegistry = Map[NamedScrutinee, CaseSet] - type SeenRegistry = Map[NamedScrutineeData, (TypeSymbol, Ls[Loc], CaseSet)] + type SeenRegistry = Map[NamedScrutinee, (TypeSymbol, Ls[Loc], CaseSet)] } diff --git a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala index d85019bb..3bb3c6ff 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala @@ -4,7 +4,7 @@ import mlscript.{Case, CaseBranches, CaseOf, Let, Lit, Loc, NoCases, Term, Var, import mlscript.{Diagnostic, ErrorReport, WarningReport} import mlscript.Message, Message.MessageContext import mlscript.ucs.Desugarer -import mlscript.ucs.context.{Context, CaseSet, NamedScrutineeData, MatchRegistry, ScrutineeData, SeenRegistry} +import mlscript.ucs.context.{Context, CaseSet, NamedScrutinee, MatchRegistry, Scrutinee, SeenRegistry} import mlscript.pretyper.Traceable import mlscript.pretyper.symbol._ import mlscript.utils._, shorthands._ @@ -33,7 +33,7 @@ trait CoverageChecking { self: Desugarer with Traceable => case CaseOf(scrutineeVar: Var, Case(Var("true"), body, NoCases)) if context.isTestVar(scrutineeVar) => raiseDesugaringError(msg"missing else branch" -> body.toLoc) Nil - case CaseOf(ScrutineeData.WithVar(scrutinee, scrutineeVar), cases) => + case CaseOf(Scrutinee.WithVar(scrutinee, scrutineeVar), cases) => println(s"scrutinee: ${scrutineeVar.name}") // If the scrutinee is still pending (i.e., not matched yet), then we // remove it from the pending list. If the scrutinee is matched, and @@ -113,7 +113,7 @@ trait CoverageChecking { self: Desugarer with Traceable => object CoverageChecking { /** Create an `ErrorReport` that explains missing cases. */ - private def explainMissingCases(scrutinee: NamedScrutineeData, seen: SeenRegistry, missingCases: CaseSet): Opt[ErrorReport] = + private def explainMissingCases(scrutinee: NamedScrutinee, seen: SeenRegistry, missingCases: CaseSet): Opt[ErrorReport] = if (missingCases.isEmpty) { N } else { diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index c67176d3..f634b171 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -2,7 +2,7 @@ package mlscript.ucs.stages import mlscript.{App, Asc, Fld, FldFlags, Lit, Sel, Term, Tup, TypeName, Var} import mlscript.ucs.syntax.{core => c, source => s} -import mlscript.ucs.context.{Context, ScrutineeData} +import mlscript.ucs.context.{Context, Scrutinee} import mlscript.utils._, shorthands._ import mlscript.pretyper.symbol._ import mlscript.pretyper.{PreTyper, Scope} diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 9fcc757f..87af99b3 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -7,7 +7,7 @@ import mlscript.Message, Message.MessageContext import mlscript.utils._, shorthands._ import mlscript.ucs, mlscript.pretyper import ucs.{Desugarer, Lines, LinesOps, VariableGenerator} -import ucs.context.{Context, ScrutineeData} +import ucs.context.{Context, Scrutinee} import ucs.display.{showNormalizedTerm, showSplit} import ucs.syntax.core.{Pattern, Branch, Split} import pretyper.Scope @@ -120,7 +120,7 @@ trait Normalization { self: Desugarer with Traceable => val trueBranch = normalizeToTerm(continuation.fill(tail, false, false)) val falseBranch = normalizeToCaseBranches(tail) CaseOf(test, Case(nme, trueBranch, falseBranch)(refined = false)) - case Split.Cons(Branch(ScrutineeData.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Literal(literal), continuation), tail) => + case Split.Cons(Branch(Scrutinee.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Literal(literal), continuation), tail) => println(s"LITERAL: ${scrutineeVar.name} is ${literal.idStr}") println(s"entire split: ${showSplit(split)}") val concatenatedTrueBranch = continuation.fill(tail, false, false) @@ -129,7 +129,7 @@ trait Normalization { self: Desugarer with Traceable => // println(s"false branch: ${showSplit(tail)}") val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context)) CaseOf(scrutineeVar, Case(literal, trueBranch, falseBranch)(refined = false)) - case Split.Cons(Branch(ScrutineeData.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Class(nme, rfd), continuation), tail) => + case Split.Cons(Branch(Scrutinee.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Class(nme, rfd), continuation), tail) => println(s"CLASS: ${scrutineeVar.name} is ${nme.name}") // println(s"match ${scrutineeVar.name} with $nme (has location: ${nme.toLoc.isDefined})") val trueBranch = normalizeToTerm(specialize(continuation.fill(tail, false, false), true)(scrutineeVar, scrutinee, pattern, context)) @@ -187,7 +187,7 @@ trait Normalization { self: Desugarer with Traceable => */ private def specialize (split: Split, matchOrNot: Bool) - (implicit scrutineeVar: Var, scrutinee: ScrutineeData, pattern: Pattern, context: Context): Split = + (implicit scrutineeVar: Var, scrutinee: Scrutinee, pattern: Pattern, context: Context): Split = trace[Split](s"S${if (matchOrNot) "+" else "-"} <== ${scrutineeVar.name} is ${pattern}") { (matchOrNot, split) match { // Name patterns are translated to let bindings. @@ -199,7 +199,7 @@ trait Normalization { self: Desugarer with Traceable => val falseBranch = specialize(tail, matchOrNot) split.copy(head = head.copy(continuation = trueBranch), tail = falseBranch) // Class pattern. Positive. - case (true, split @ Split.Cons(head @ Branch(ScrutineeData.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Class(otherClassName, otherRefined), continuation), tail)) => + case (true, split @ Split.Cons(head @ Branch(Scrutinee.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Class(otherClassName, otherRefined), continuation), tail)) => val otherClassSymbol = otherClassName.getClassLikeSymbol if (scrutinee === otherScrutinee) { println(s"scrutinee: ${scrutineeVar.name} === ${otherScrutineeVar.name}") @@ -243,7 +243,7 @@ trait Normalization { self: Desugarer with Traceable => ) } // Class pattern. Negative - case (false, split @ Split.Cons(head @ Branch(ScrutineeData.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Class(otherClassName, otherRefined), continuation), tail)) => + case (false, split @ Split.Cons(head @ Branch(Scrutinee.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Class(otherClassName, otherRefined), continuation), tail)) => val otherClassSymbol = otherClassName.getClassLikeSymbol if (scrutinee === otherScrutinee) { println(s"scrutinee: ${scrutineeVar.name} === ${otherScrutineeVar.name}") @@ -281,7 +281,7 @@ trait Normalization { self: Desugarer with Traceable => ) } // Literal pattern. Positive. - case (true, split @ Split.Cons(head @ Branch(ScrutineeData.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Literal(otherLiteral), continuation), tail)) => + case (true, split @ Split.Cons(head @ Branch(Scrutinee.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Literal(otherLiteral), continuation), tail)) => if (scrutinee === otherScrutinee) { println(s"scrutinee: ${scrutineeVar.name} is ${otherScrutineeVar.name}") pattern match { @@ -297,7 +297,7 @@ trait Normalization { self: Desugarer with Traceable => ) } // Literal pattern. Negative. - case (false, split @ Split.Cons(head @ Branch(ScrutineeData.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Literal(otherLiteral), continuation), tail)) => + case (false, split @ Split.Cons(head @ Branch(Scrutinee.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Literal(otherLiteral), continuation), tail)) => if (scrutinee === otherScrutinee) { println(s"scrutinee: ${scrutineeVar.name} is ${otherScrutineeVar.name}") pattern match { diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index 9cfd1f26..fb496df0 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -2,7 +2,7 @@ package mlscript.ucs.stages import mlscript.{Case, CaseBranches, CaseOf, Let, Lit, Loc, NoCases, Term, Var, Wildcard} import mlscript.ucs.Desugarer -import mlscript.ucs.context.{Context, PatternInfo, ScrutineeData} +import mlscript.ucs.context.{Context, PatternInfo, Scrutinee} import mlscript.pretyper.symbol._ import mlscript.utils._, shorthands._ import mlscript.Message, Message.MessageContext @@ -14,11 +14,11 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => def postProcess(term: Term)(implicit context: Context): Term = trace(s"postProcess <== ${term.showDbg}") { // Normalized terms are constructed using `Let` and `CaseOf`. term match { - case top @ CaseOf(ScrutineeData(_), Wildcard(body)) => body - case top @ CaseOf(ScrutineeData.WithVar(scrutinee, scrutineeVar), fst @ Case(className: Var, body, NoCases)) => + case top @ CaseOf(Scrutinee(_), Wildcard(body)) => body + case top @ CaseOf(Scrutinee.WithVar(scrutinee, scrutineeVar), fst @ Case(className: Var, body, NoCases)) => println(s"UNARY: ${scrutineeVar.name} is ${className.name}") top.copy(cases = fst.copy(body = postProcess(body))(refined = fst.refined)) - case top @ CaseOf(ScrutineeData.WithVar(scrutinee, scrutineeVar), fst @ Case(pat, trueBranch, Wildcard(falseBranch))) => + case top @ CaseOf(Scrutinee.WithVar(scrutinee, scrutineeVar), fst @ Case(pat, trueBranch, Wildcard(falseBranch))) => println(s"BINARY: ${scrutineeVar.name} is ${pat.showDbg}") println(s"patterns of `${scrutineeVar.name}`: ${scrutinee.showPatternsDbg}") // Post-process the true branch. @@ -125,12 +125,12 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => private def disentangleTerm(term: Term)(implicit context: Context, scrutineeVar: Var, - scrutinee: ScrutineeData, + scrutinee: Scrutinee, pattern: PatternInfo ): (Term, Opt[Term]) = { def rec(term: Term): (Term, Opt[Term]) = term match { - case top @ CaseOf(ScrutineeData.WithVar(otherScrutinee, otherScrutineeVar), cases) => + case top @ CaseOf(Scrutinee.WithVar(otherScrutinee, otherScrutineeVar), cases) => if (scrutinee === otherScrutinee) { println(s"found a `CaseOf` that matches on `${scrutineeVar.name}`") val (n, y) = disentangleMatchedCaseBranches(cases) @@ -158,7 +158,7 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => private def disentangleMatchedCaseBranches(cases: CaseBranches)(implicit context: Context, scrutineeVar: Var, - scrutinee: ScrutineeData, + scrutinee: Scrutinee, pattern: PatternInfo ): (CaseBranches, Opt[Term]) = cases match { @@ -187,7 +187,7 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => private def disentangleUnmatchedCaseBranches(cases: CaseBranches)(implicit context: Context, scrutineeVar: Var, - scrutinee: ScrutineeData, + scrutinee: Scrutinee, pattern: PatternInfo ): (CaseBranches, CaseBranches) = cases match { From 2896a698581093e86c08800ded00299f2e24bba1 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Tue, 16 Jan 2024 20:40:30 +0800 Subject: [PATCH 077/143] Remove `TODO`s and fix lack unreachable case warning --- .../mlscript/ucs/context/Scrutinee.scala | 8 +- .../src/main/scala/mlscript/ucs/display.scala | 2 - .../mlscript/ucs/stages/Desugaring.scala | 89 ++++++++----------- .../pretyper/ucs/patterns/SimpleTuple.mls | 48 ++++++---- 4 files changed, 73 insertions(+), 74 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala index 315d2f06..685cbb50 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala @@ -23,7 +23,7 @@ class Scrutinee(val context: Context, parent: Opt[Scrutinee]) { // If we support tuple pattern splice, we need a more expressive key in the // map's type. private var tuplePatternOpt: Opt[PatternInfo.Tuple] = N - private var alisesSet: MutSortedSet[Var] = MutSortedSet.empty + private var aliasVarSet: MutSortedSet[Var] = MutSortedSet.empty private val literalPatterns: MutSortedMap[Lit, PatternInfo.Literal] = MutSortedMap.empty(literalOrdering) /** @@ -32,11 +32,11 @@ class Scrutinee(val context: Context, parent: Opt[Scrutinee]) { */ private val booleanPatterns: MutSortedMap[Var, PatternInfo.Boolean] = MutSortedMap.empty(varNameOrdering) - def +=(alias: Var): Unit = alisesSet += alias + def addAliasVar(alias: Var): Unit = aliasVarSet += alias - def withAlias(alias: Var): Scrutinee = { this += alias; this } + def withAliasVar(alias: Var): Scrutinee = { addAliasVar(alias); this } - def aliasesIterator: Iterator[Var] = alisesSet.iterator + def aliasesIterator: Iterator[Var] = aliasVarSet.iterator /** * If there is already a `PatternInfo.ClassLike` for the given symbol, return it. diff --git a/shared/src/main/scala/mlscript/ucs/display.scala b/shared/src/main/scala/mlscript/ucs/display.scala index 288c1434..2811baee 100644 --- a/shared/src/main/scala/mlscript/ucs/display.scala +++ b/shared/src/main/scala/mlscript/ucs/display.scala @@ -25,7 +25,6 @@ package object display { } def showSplit(split: s.TermSplit)(implicit context: Context): Str = { - // TODO: tailrec def termSplit(split: s.TermSplit, isFirst: Bool, isAfterAnd: Bool): Lines = split match { case s.Split.Cons(head, tail) => (termBranch(head) match { case (n, line) :: tail => (n, (if (isAfterAnd) "" else "and ") + s"$line") :: tail @@ -77,7 +76,6 @@ package object display { @inline def showSplit(s: c.Split)(implicit context: Context): Str = showSplit("if", s) def showSplit(prefix: Str, s: c.Split)(implicit context: Context): Str = { - // TODO: tailrec def split(s: c.Split, isFirst: Bool, isTopLevel: Bool): Lines = s match { case c.Split.Cons(head, tail) => (branch(head) match { case (n, line) :: tail => (n, (if (isTopLevel) "" else "") + s"$line") :: tail diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index f634b171..d82a2f18 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -137,12 +137,12 @@ trait Desugaring { self: PreTyper => parameters.iterator.zipWithIndex.map { case (_: s.EmptyPattern, _) => N case (s.NamePattern(name), index) => - val subScrutinee = classPattern.getParameter(index).withAlias(name) + val subScrutinee = classPattern.getParameter(index).withAliasVar(name) S(name.withFreshSymbol.withScrutinee(subScrutinee) -> N) case (parameterPattern @ (s.ClassPattern(_, _, _) | s.LiteralPattern(_) | s.TuplePattern(_)), index) => val subScrutineeVar = freshSubScrutineeVar(parentScrutineeVar, parentClassLikeSymbol.name, index) val symbol = new LocalTermSymbol(subScrutineeVar) - symbol.addScrutinee(classPattern.getParameter(index).withAlias(subScrutineeVar)) + symbol.addScrutinee(classPattern.getParameter(index).withAliasVar(subScrutineeVar)) S(subScrutineeVar.withSymbol(symbol) -> S(parameterPattern)) case (pattern, _) => raiseDesugaringError(msg"unsupported pattern" -> pattern.toLoc) @@ -173,7 +173,7 @@ trait Desugaring { self: PreTyper => scrutineeVar: Var, initialScope: Scope, refined: Bool)(implicit context: Context): (Scope, c.Split => c.Branch) = { - val scrutinee = scrutineeVar.getOrCreateScrutinee.withAlias(scrutineeVar) + val scrutinee = scrutineeVar.getOrCreateScrutinee.withAliasVar(scrutineeVar) val patternClassSymbol = pattern.nme.resolveClassLikeSymbol(initialScope, context) val classPattern = scrutinee.getOrCreateClassPattern(patternClassSymbol) println(s"desugarClassPattern: ${scrutineeVar.name} is ${pattern.nme.name}") @@ -243,7 +243,7 @@ trait Desugaring { self: PreTyper => (scopeWithNestedAll, split => bindPrevious(bindNestedAll(split) :: c.Split.Nil)) case ((scope, bindPrevious), S(nme -> S(pattern: s.LiteralPattern))) => nme.getOrCreateScrutinee - .withAlias(nme) + .withAliasVar(nme) .getOrCreateLiteralPattern(pattern.literal) .addLocation(pattern.literal) (scope, makeLiteralTest(nme, pattern.literal)(scope).andThen(bindPrevious)) @@ -271,7 +271,7 @@ trait Desugaring { self: PreTyper => val arity = fields.length val subScrutineeVar = freshSubScrutineeVar(parentScrutineeVar, s"Tuple$$$arity", index) val symbol = new LocalTermSymbol(subScrutineeVar) - symbol.addScrutinee(tuplePattern.getField(index).withAlias(subScrutineeVar)) + symbol.addScrutinee(tuplePattern.getField(index).withAliasVar(subScrutineeVar)) S(subScrutineeVar.withSymbol(symbol) -> S(parameterPattern)) case (pattern, _) => raiseDesugaringError(msg"unsupported pattern" -> pattern.toLoc) @@ -280,7 +280,7 @@ trait Desugaring { self: PreTyper => } private def desugarTuplePattern(fields: Ls[s.Pattern], scrutineeVar: Var, initialScope: Scope)(implicit context: Context): (Scope, c.Split => c.Split) = { - val scrutinee = scrutineeVar.getOrCreateScrutinee.withAlias(scrutineeVar) + val scrutinee = scrutineeVar.getOrCreateScrutinee.withAliasVar(scrutineeVar) val nestedPatterns = flattenTupleFields(scrutineeVar, fields) val bindFields = nestedPatterns.iterator.zipWithIndex.foldRight[c.Split => c.Split](identity) { case ((N, _), bindNextField) => bindNextField @@ -292,72 +292,55 @@ trait Desugaring { self: PreTyper => desugarNestedPatterns(nestedPatterns, scopeWithFields, bindFields) } - private def desugarPatternSplit(scrutineeTerm: Term, split: s.PatternSplit)(implicit scope: Scope, context: Context): c.Split = { + private def desugarPatternSplit( + scrutineeTerm: Term, + split: s.PatternSplit + )(implicit scope: Scope, context: Context): c.Split = { def rec(scrutineeVar: Var, split: s.PatternSplit)(implicit scope: Scope): c.Split = split match { - // TODO: We should resolve `scrutineeVar`'s symbol and make sure it is a term symbol in the - // beginning. So that we can call methods on the symbol directly. Now there are too many - // helper functions on `VarOps`. case s.Split.Cons(head, tail) => + val scrutineeSymbol = scrutineeVar.getOrResolveTermSymbol + val scrutinee = scrutineeSymbol.getOrCreateScrutinee + scrutinee.addAliasVar(scrutineeVar) + // Some functions that allow me to write less code, this function was + // very long before I introduced them. + def desugarRight(implicit scope: Scope) = + desugarTermSplit(head.continuation)(PartialTerm.Empty, scope, context) + def desugarTail(implicit scope: Scope) = rec(scrutineeVar, tail) head.pattern match { case pattern @ s.AliasPattern(_, _) => raiseDesugaringError(msg"alias pattern is not supported for now" -> pattern.toLoc) - rec(scrutineeVar, tail) + desugarTail case s.LiteralPattern(literal) => - scrutineeVar.getOrCreateScrutinee.withAlias(scrutineeVar).getOrCreateLiteralPattern(literal).addLocation(literal) - c.Branch( - scrutinee = scrutineeVar, - pattern = c.Pattern.Literal(literal), - continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope, context) - ) :: rec(scrutineeVar, tail) + scrutinee.getOrCreateLiteralPattern(literal).addLocation(literal) + c.Branch(scrutineeVar, c.Pattern.Literal(literal), desugarRight) :: desugarTail case s.ConcretePattern(nme @ (Var("true") | Var("false"))) => - scrutineeVar.getOrCreateScrutinee.withAlias(scrutineeVar).getOrCreateBooleanPattern(nme).addLocation(nme) + scrutinee.getOrCreateBooleanPattern(nme).addLocation(nme) c.Branch( scrutinee = scrutineeVar, pattern = c.Pattern.Class(nme.withResolvedClassLikeSymbol, false), - continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope, context) - ) :: rec(scrutineeVar, tail) + continuation = desugarRight + ) :: desugarTail case s.ConcretePattern(nme) => - val test = context.freshTest().withFreshSymbol - c.Split.Let( - rec = false, - name = test, - term = mkBinOp(scrutineeVar, Var("==="), nme, true), - tail = c.Branch( - scrutinee = test, - pattern = truePattern, - continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope + test.symbol, context) - ) :: rec(scrutineeVar, tail) - ) + val testVar = context.freshTest().withFreshSymbol + val testTerm = mkBinOp(scrutineeVar, Var("==="), nme, true) + c.Split.Let(false, testVar, testTerm, + c.Branch(testVar, truePattern, desugarRight(scope + testVar.symbol)) :: desugarTail) case s.EmptyPattern(_) | s.NamePattern(Var("_")) => - desugarTermSplit(head.continuation)(PartialTerm.Empty, scope, context) ++ rec(scrutineeVar, tail) + desugarRight ++ desugarTail case s.NamePattern(nme) => - // Create a symbol for the binding. - val symbol = new LocalTermSymbol(nme) - // Share the scrutineeVar's symbol with its aliases. - symbol.addScrutinee(scrutineeVar.getOrCreateScrutinee.withAlias(nme)) - // Associate the symbol with the binding. - nme.symbol = symbol - // Whoosh! Done. - val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope + nme.symbol, context) - c.Branch(scrutineeVar, c.Pattern.Name(nme), continuation) :: rec(scrutineeVar, tail)(scope + nme.symbol) + val symbol = freshSymbol(nme).withScrutinee(scrutinee) + val scopeWithSymbol = scope + symbol + c.Branch(scrutineeVar, c.Pattern.Name(nme.withSymbol(symbol)), + desugarRight(scopeWithSymbol)) :: desugarTail(scopeWithSymbol) case pattern @ s.ClassPattern(nme, fields, rfd) => - val scrutineeSymbol = scrutineeVar.getOrResolveTermSymbol // TODO: Useless. val (scopeWithAll, bindAll) = desugarClassPattern(pattern, scrutineeVar, scope, rfd) - val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scopeWithAll, context) - bindAll(continuation) :: rec(scrutineeVar, tail) + bindAll(desugarRight(scopeWithAll)) :: desugarTail case s.TuplePattern(fields) => - val scrutineeSymbol = scrutineeVar.getOrResolveTermSymbol // TODO: Useless. val (scopeWithAll, bindAll) = desugarTuplePattern(fields, scrutineeVar, scope) - val continuation = desugarTermSplit(head.continuation)(PartialTerm.Empty, scopeWithAll, context) - val withBindings = bindAll(continuation) - if (withBindings.hasElse) { - withBindings - } else { - withBindings ++ rec(scrutineeVar, tail) - } + bindAll(desugarRight(scopeWithAll)) ++ desugarTail case pattern @ s.RecordPattern(_) => raiseDesugaringError(msg"record pattern is not supported for now" -> pattern.toLoc) - rec(scrutineeVar, tail) + desugarTail } case s.Split.Let(isRec, nme, rhs, tail) => c.Split.Let(isRec, nme, rhs, rec(scrutineeVar, tail)(scope + nme.withFreshSymbol.symbol)) // <-- Weird use. diff --git a/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls b/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls index ea4536bc..0a0144e3 100644 --- a/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls +++ b/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls @@ -39,6 +39,7 @@ test(12) //│ res //│ = 0 +:w // Since pattern destruction is desugared to let bindings, matching with other // classes after the tuple pattern will not work. class Point(x: Int, y: Int) @@ -46,13 +47,23 @@ fun discarded_cases(thing) = if thing is [x, y] then x + y Point(x, y) then x + y +//│ ╔══[WARNING] the case is unreachable +//│ ║ l.47: if thing is +//│ ║ ^^^^^^^^ +//│ ║ l.48: [x, y] then x + y +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.49: Point(x, y) then x + y +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── because this branch covers the case +//│ ║ l.48: [x, y] then x + y +//│ ╙── ^^^^^ //│ class Point(x: Int, y: Int) //│ fun discarded_cases: {0: Int, 1: Int} -> Int :e discarded_cases(Point(0, 0)) //│ ╔══[ERROR] Type `Point` does not contain member `1` -//│ ║ l.47: [x, y] then x + y +//│ ║ l.48: [x, y] then x + y //│ ╙── ^ //│ Int | error //│ res @@ -74,31 +85,38 @@ working_cases(Point(0, 0)) :e working_cases([0, 0]) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.75: working_cases([0, 0]) +//│ ║ l.86: working_cases([0, 0]) //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── tuple literal of type `[0, 0]` is not an instance of type `Object` -//│ ║ l.75: working_cases([0, 0]) +//│ ║ l.86: working_cases([0, 0]) //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from `case` expression: -//│ ║ l.63: if thing is +//│ ║ l.74: if thing is //│ ║ ^^^^^^^^ -//│ ║ l.64: Point(x, y) then x + y +//│ ║ l.75: Point(x, y) then x + y //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.65: [x, y] then x + y +//│ ║ l.76: [x, y] then x + y //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── from reference: -//│ ║ l.63: if thing is +//│ ║ l.74: if thing is //│ ╙── ^^^^^ //│ Int | error //│ res //│ = 0 +:w fun not_working(x) = if x is [a, b, c] then a + b + c else 0 +//│ ╔══[WARNING] the case is unreachable +//│ ║ l.113: 0 +//│ ║ ^ +//│ ╟── because this branch covers the case +//│ ║ l.111: a + b + c +//│ ╙── ^^^^^^^^^ //│ fun not_working: {0: Int, 1: Int, 2: Int} -> Int not_working([1, 2, 3]) @@ -109,19 +127,19 @@ not_working([1, 2, 3]) :e not_working([1, 2]) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.110: not_working([1, 2]) +//│ ║ l.128: not_working([1, 2]) //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── tuple literal of type `{0: 1, 1: 2}` does not have field '2' -//│ ║ l.110: not_working([1, 2]) +//│ ║ l.128: not_working([1, 2]) //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from field selection: -//│ ║ l.97: if x is -//│ ║ ^^^^ -//│ ║ l.98: [a, b, c] then -//│ ║ ^^^^^^^^^^^^ +//│ ║ l.109: if x is +//│ ║ ^^^^ +//│ ║ l.110: [a, b, c] then +//│ ║ ^^^^^^^^^^^^ //│ ╟── from reference: -//│ ║ l.97: if x is -//│ ╙── ^ +//│ ║ l.109: if x is +//│ ╙── ^ //│ Int | error //│ res //│ = NaN From fbf7519471808ebad9c36af2acf4b89d838ca8b0 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Tue, 16 Jan 2024 20:59:58 +0800 Subject: [PATCH 078/143] Remove `DesugaringException` --- .../main/scala/mlscript/ucs/Desugarer.scala | 24 ++-- .../src/main/scala/mlscript/ucs/package.scala | 6 - shared/src/test/diff/mlscript/Repro.mls | 40 +----- shared/src/test/diff/pretyper/SymbolKind.mls | 118 ++++++++++++++++++ 4 files changed, 129 insertions(+), 59 deletions(-) create mode 100644 shared/src/test/diff/pretyper/SymbolKind.mls diff --git a/shared/src/main/scala/mlscript/ucs/Desugarer.scala b/shared/src/main/scala/mlscript/ucs/Desugarer.scala index ca29850a..35af9094 100644 --- a/shared/src/main/scala/mlscript/ucs/Desugarer.scala +++ b/shared/src/main/scala/mlscript/ucs/Desugarer.scala @@ -74,10 +74,10 @@ trait Desugarer extends Transformation */ def getOrCreateScrutinee(implicit context: Context): Scrutinee = nme.symbolOption match { case S(symbol: TermSymbol) => symbol.getOrCreateScrutinee - case S(otherSymbol) => throw new DesugaringException( - msg"Expected scrutinee symbol, found ${nme.symbol.name}" -> nme.toLoc :: Nil - ) - case N => throw new DesugaringException(msg"Scrutinee symbol not found" -> nme.toLoc :: Nil) + case S(otherSymbol) => + raiseDesugaringError(msg"cannot identifier `${nme.name}` with a scrutinee" -> nme.toLoc) + context.freshScrutinee // TODO + case N => lastWords(s"`${nme.name}` should be assoicated with a symbol") } /** Associate the `Var` with a scrutinee and returns the same `Var`. */ @@ -85,12 +85,10 @@ trait Desugarer extends Transformation case S(symbol: TermSymbol) => symbol.addScrutinee(scrutinee) nme - case S(otherSymbol) => throw new DesugaringException( - msg"Expected scrutinee symbol, found ${nme.symbol.name}" -> nme.toLoc :: Nil - ) - case N => throw new DesugaringException( - msg"Scrutinee symbol not found" -> nme.toLoc :: Nil - ) + case S(otherSymbol) => + raiseDesugaringError(msg"cannot identifier `${nme.name}` with a scrutinee" -> nme.toLoc) + nme + case N => lastWords(s"`${nme.name}` should be assoicated with a symbol") } /** @@ -201,7 +199,7 @@ trait Desugarer extends Transformation */ protected def traverseIf(`if`: If)(implicit scope: Scope): Unit = { implicit val context: Context = new Context(`if`) - try trace("traverseIf") { + trace("traverseIf") { // Stage 0: Transformation val transformed = traceWithTopic("transform") { println("STEP 0") @@ -254,9 +252,7 @@ trait Desugarer extends Transformation } // Epilogue `if`.desugaredTerm = S(postProcessed) - }(_ => "traverseIf ==> ()") catch { - case e: DesugaringException => raiseDesugaringError(e.messages: _*) - } + }(_ => "traverseIf ==> ()") } /** diff --git a/shared/src/main/scala/mlscript/ucs/package.scala b/shared/src/main/scala/mlscript/ucs/package.scala index 4e285538..dd2ddf92 100644 --- a/shared/src/main/scala/mlscript/ucs/package.scala +++ b/shared/src/main/scala/mlscript/ucs/package.scala @@ -43,10 +43,4 @@ package object ucs { def toIndentedString: Str = lines.iterator.map { case (n, line) => " " * n + line }.mkString("\n") } - - // TODO: Remove this exception. The desugarer should work in a non-fatal way. - // We may call `lastWords` if unrecoverable errors are found. - class DesugaringException(val messages: Ls[Message -> Opt[Loc]]) extends Throwable { - def this(message: Message, location: Opt[Loc]) = this(message -> location :: Nil) - } } diff --git a/shared/src/test/diff/mlscript/Repro.mls b/shared/src/test/diff/mlscript/Repro.mls index 272dfe5e..34d9bdfe 100644 --- a/shared/src/test/diff/mlscript/Repro.mls +++ b/shared/src/test/diff/mlscript/Repro.mls @@ -1,39 +1 @@ -type Tree = Node | Empty -class Node: { value: int; left: Tree; right: Tree } -class Empty -//│ Defined type alias Tree -//│ Defined class Node -//│ Defined class Empty - -rec def insert(t, v) = case t of - { Node -> - let v' = t.value in - let l = t.left in - let r = t.right in - let ucs__test__0 = v < v' : bool in - case ucs__test__0 of - { true -> Node { value = v'; left = insert(l, v,); right = r } - | _ -> - let ucs__test__1 = v > v' : bool in - case ucs__test__1 of - { true -> Node { value = v'; left = l; right = insert(r, v,) } - | _ -> t - } - } - | Empty -> Node { value = v; left = Empty {}; right = Empty {} } - } -//│ insert: (Empty | Node & 'a, int & 'value,) -> ((Node with {left: Empty, right: Empty, value: 'value}) | 'a | 'b) -//│ where -//│ 'b :> (Node with { -//│ left: (Node with {left: Empty, right: Empty, value: 'value}) | 'a | 'b, -//│ right: 'right, -//│ value: 'value0 -//│ }) | (Node with { -//│ left: 'left, -//│ right: (Node with {left: Empty, right: Empty, value: 'value}) | 'a | 'b, -//│ value: 'value0 -//│ }) -//│ 'a <: Tree & {left: 'left, right: 'right, value: int & 'value0} -//│ 'right <: Empty | Node & 'a -//│ 'left <: Empty | Node & 'a -//│ = [Function: insert] +:NewDefs diff --git a/shared/src/test/diff/pretyper/SymbolKind.mls b/shared/src/test/diff/pretyper/SymbolKind.mls new file mode 100644 index 00000000..69c88cd5 --- /dev/null +++ b/shared/src/test/diff/pretyper/SymbolKind.mls @@ -0,0 +1,118 @@ +:NewDefs +:ShowPreTyperErrors + +type Type = Int +mixin Mixin1 +mixin Mixin2() +trait Trait // Trait doesn't have parameters. +class Class1 +class Class2() +module Module +//│ type Type = Int +//│ mixin Mixin1() +//│ mixin Mixin2() +//│ trait Trait +//│ class Class1 { +//│ constructor() +//│ } +//│ class Class2() +//│ module Module + +fun f(g) = g() +//│ fun f: forall 'a. (() -> 'a) -> 'a + +:e +f(Type) +//│ ╔══[ERROR] identifier `Type` is resolved to a type +//│ ║ l.25: f(Type) +//│ ╙── ^^^^ +//│ ╔══[ERROR] type alias Type cannot be used in term position +//│ ║ l.25: f(Type) +//│ ╙── ^^^^ +//│ error +//│ Code generation encountered an error: +//│ type alias Type is not a valid expression + +:e +id(Mixin1) +//│ ╔══[ERROR] identifier `Mixin1` is resolved to a type +//│ ║ l.37: id(Mixin1) +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] mixin Mixin1 cannot be used in term position +//│ ║ l.37: id(Mixin1) +//│ ╙── ^^^^^^ +//│ error +//│ res +//│ = [Function (anonymous)] + +:e +f(Mixin2) +//│ ╔══[ERROR] identifier `Mixin2` is resolved to a type +//│ ║ l.49: f(Mixin2) +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] mixin Mixin2 cannot be used in term position +//│ ║ l.49: f(Mixin2) +//│ ╙── ^^^^^^ +//│ error +//│ res +//│ Runtime error: +//│ TypeError: Class extends value undefined is not a constructor or null + +:e +f(Trait) +//│ ╔══[ERROR] identifier `Trait` is resolved to a type +//│ ║ l.62: f(Trait) +//│ ╙── ^^^^^ +//│ ╔══[ERROR] trait Trait cannot be used in term position +//│ ║ l.62: f(Trait) +//│ ╙── ^^^^^ +//│ error +//│ Code generation encountered an error: +//│ trait used in term position + +:e +f(Class1) +//│ ╔══[ERROR] Construction of unparameterized class Class1 should use the `new` keyword +//│ ║ l.74: f(Class1) +//│ ╙── ^^^^^^ +//│ Class1 +//│ res +//│ Runtime error: +//│ TypeError: Class constructor Class1 cannot be invoked without 'new' + +f(Class2) +//│ Class2 +//│ res +//│ = Class2 {} + +id(Class2) +//│ () -> Class2 +//│ res +//│ = [Function (anonymous)] { +//│ class: [class Class2], +//│ unapply: [Function: unapply] +//│ } + +:e +f(Module) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.97: f(Module) +//│ ║ ^^^^^^^^^ +//│ ╟── reference of type `Module` is not a function +//│ ║ l.97: f(Module) +//│ ║ ^^^^^^ +//│ ╟── Note: constraint arises from application: +//│ ║ l.21: fun f(g) = g() +//│ ║ ^^^ +//│ ╟── from reference: +//│ ║ l.21: fun f(g) = g() +//│ ╙── ^ +//│ error +//│ res +//│ Runtime error: +//│ TypeError: g is not a function + +id(Module) +//│ Module +//│ res +//│ = Module { class: [class Module] } From e23e7a1ff6f1d0a57addecb42e6aaf86cd676ed2 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Tue, 16 Jan 2024 21:05:18 +0800 Subject: [PATCH 079/143] Add a test for `PreTyper` --- shared/src/test/diff/pretyper/Errors.mls | 280 ++++++++++++----------- 1 file changed, 143 insertions(+), 137 deletions(-) diff --git a/shared/src/test/diff/pretyper/Errors.mls b/shared/src/test/diff/pretyper/Errors.mls index 9e071a13..28349ca0 100644 --- a/shared/src/test/diff/pretyper/Errors.mls +++ b/shared/src/test/diff/pretyper/Errors.mls @@ -11,24 +11,30 @@ // codegen // ------- +// Nested +fun main = + let f(x: Int): Int = if x is + 0 then 1 + else g(x - 1) + let g(x: Int): Int = f(x) + f +//│ ╔══[ERROR] identifier `g` not found +//│ ║ l.18: else g(x - 1) +//│ ╙── ^ +//│ fun main: (x: Int) -> Int + // SymbolicOps fun (>>)(f, g) = x => g(f(x)) //│ ╔══[PARSE ERROR] Expected a function name; found parenthesis section instead -//│ ║ l.15: fun (>>)(f, g) = x => g(f(x)) +//│ ║ l.27: fun (>>)(f, g) = x => g(f(x)) //│ ╙── ^^^^^^ //│ ╔══[ERROR] identifier `g` not found -//│ ║ l.15: fun (>>)(f, g) = x => g(f(x)) +//│ ║ l.27: fun (>>)(f, g) = x => g(f(x)) //│ ╙── ^ //│ ╔══[ERROR] identifier `f` not found -//│ ║ l.15: fun (>>)(f, g) = x => g(f(x)) +//│ ║ l.27: fun (>>)(f, g) = x => g(f(x)) //│ ╙── ^ -//│ ╔══[ERROR] identifier not found: g -//│ ║ l.15: fun (>>)(f, g) = x => g(f(x)) -//│ ╙── ^ -//│ ╔══[ERROR] identifier not found: f -//│ ║ l.15: fun (>>)(f, g) = x => g(f(x)) -//│ ╙── ^ -//│ fun (>>) : anything -> error +//│ fun (>>) : Int -> Int // mlscript // -------- @@ -36,10 +42,10 @@ fun (>>)(f, g) = x => g(f(x)) // Sequence let test(x) = log(x); x + 1 //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.37: let test(x) = log(x); x + 1 +//│ ║ l.43: let test(x) = log(x); x + 1 //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x -//│ ║ l.37: let test(x) = log(x); x + 1 +//│ ║ l.43: let test(x) = log(x); x + 1 //│ ╙── ^ //│ let test: anything -> () //│ Int @@ -50,32 +56,32 @@ let test(x) = log(x); x + 1 // Ascription foo(123: Int): Int //│ ╔══[ERROR] identifier `foo` not found -//│ ║ l.51: foo(123: Int): Int +//│ ║ l.57: foo(123: Int): Int //│ ╙── ^^^ //│ ╔══[ERROR] identifier `Int` is resolved to a type -//│ ║ l.51: foo(123: Int): Int +//│ ║ l.57: foo(123: Int): Int //│ ╙── ^^^ //│ ╔══[ERROR] identifier not found: foo -//│ ║ l.51: foo(123: Int): Int +//│ ║ l.57: foo(123: Int): Int //│ ╙── ^^^ //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `error` for applying named arguments -//│ ║ l.51: foo(123: Int): Int +//│ ║ l.57: foo(123: Int): Int //│ ╙── ^^^ //│ Int // Ascription foo(123:Int):Int //│ ╔══[ERROR] identifier `foo` not found -//│ ║ l.67: foo(123:Int):Int +//│ ║ l.73: foo(123:Int):Int //│ ╙── ^^^ //│ ╔══[ERROR] identifier `Int` is resolved to a type -//│ ║ l.67: foo(123:Int):Int +//│ ║ l.73: foo(123:Int):Int //│ ╙── ^^^ //│ ╔══[ERROR] identifier not found: foo -//│ ║ l.67: foo(123:Int):Int +//│ ║ l.73: foo(123:Int):Int //│ ╙── ^^^ //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `error` for applying named arguments -//│ ║ l.67: foo(123:Int):Int +//│ ║ l.73: foo(123:Int):Int //│ ╙── ^^^ //│ Int @@ -85,7 +91,7 @@ fun test = let b = 1 a //│ ╔══[ERROR] identifier `b` not found -//│ ║ l.84: let a = b +//│ ║ l.90: let a = b //│ ╙── ^ //│ fun test: 1 @@ -95,17 +101,17 @@ fun test = let b = 1 a() //│ ╔══[ERROR] identifier `b` not found -//│ ║ l.94: let a() = b -//│ ╙── ^ +//│ ║ l.100: let a() = b +//│ ╙── ^ //│ fun test: 1 // BadClasses hello //│ ╔══[ERROR] identifier `hello` not found -//│ ║ l.103: hello +//│ ║ l.109: hello //│ ╙── ^^^^^ //│ ╔══[ERROR] identifier not found: hello -//│ ║ l.103: hello +//│ ║ l.109: hello //│ ╙── ^^^^^ //│ error @@ -115,7 +121,7 @@ module A { val y = x } //│ ╔══[ERROR] identifier `y` not found -//│ ║ l.114: val x = y +//│ ║ l.120: val x = y //│ ╙── ^ //│ module A { //│ val x: nothing @@ -128,7 +134,7 @@ module A { val y = 1 } //│ ╔══[ERROR] identifier `y` not found -//│ ║ l.127: val x = y +//│ ║ l.133: val x = y //│ ╙── ^ //│ module A { //│ val x: 1 @@ -139,10 +145,10 @@ module A { mixin M0 M0 //│ ╔══[ERROR] identifier `M0` is resolved to a type -//│ ║ l.140: M0 +//│ ║ l.146: M0 //│ ╙── ^^ //│ ╔══[ERROR] mixin M0 cannot be used in term position -//│ ║ l.140: M0 +//│ ║ l.146: M0 //│ ╙── ^^ //│ mixin M0() //│ error @@ -151,10 +157,10 @@ M0 mixin Foo(x: Int) x //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.152: x +//│ ║ l.158: x //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x -//│ ║ l.152: x +//│ ║ l.158: x //│ ╙── ^ //│ mixin Foo(x: Int) //│ error @@ -163,10 +169,10 @@ x class Foo(x: Int) x //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.164: x +//│ ║ l.170: x //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x -//│ ║ l.164: x +//│ ║ l.170: x //│ ╙── ^ //│ class Foo(x: Int) //│ error @@ -174,10 +180,10 @@ x // BadScopes class Bar { x } //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.175: class Bar { x } +//│ ║ l.181: class Bar { x } //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x -//│ ║ l.175: class Bar { x } +//│ ║ l.181: class Bar { x } //│ ╙── ^ //│ class Bar { //│ constructor() @@ -187,10 +193,10 @@ class Bar { x } trait Foo Foo //│ ╔══[ERROR] identifier `Foo` is resolved to a type -//│ ║ l.188: Foo +//│ ║ l.194: Foo //│ ╙── ^^^ //│ ╔══[ERROR] trait Foo cannot be used in term position -//│ ║ l.188: Foo +//│ ║ l.194: Foo //│ ╙── ^^^ //│ trait Foo //│ error @@ -198,35 +204,35 @@ Foo // FunPatterns fun f3([(x, y,),],) = x + y //│ ╔══[ERROR] unsupported pattern shape -//│ ║ l.199: fun f3([(x, y,),],) = x + y +//│ ║ l.205: fun f3([(x, y,),],) = x + y //│ ╙── ^^^^^^^ //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.199: fun f3([(x, y,),],) = x + y +//│ ║ l.205: fun f3([(x, y,),],) = x + y //│ ╙── ^ //│ ╔══[ERROR] identifier `y` not found -//│ ║ l.199: fun f3([(x, y,),],) = x + y +//│ ║ l.205: fun f3([(x, y,),],) = x + y //│ ╙── ^ //│ ╔══[ERROR] Unsupported pattern shape: -//│ ║ l.199: fun f3([(x, y,),],) = x + y +//│ ║ l.205: fun f3([(x, y,),],) = x + y //│ ╙── ^^^^^^^ //│ ╔══[ERROR] identifier not found: x -//│ ║ l.199: fun f3([(x, y,),],) = x + y +//│ ║ l.205: fun f3([(x, y,),],) = x + y //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y -//│ ║ l.199: fun f3([(x, y,),],) = x + y +//│ ║ l.205: fun f3([(x, y,),],) = x + y //│ ╙── ^ //│ fun f3: ([error]) -> Int // GenericClassInheritance class C1[A] extends C0[A] { val a = a } //│ ╔══[ERROR] could not find definition `C0` -//│ ║ l.221: class C1[A] extends C0[A] { val a = a } +//│ ║ l.227: class C1[A] extends C0[A] { val a = a } //│ ╙── ^^ //│ ╔══[ERROR] identifier `a` not found -//│ ║ l.221: class C1[A] extends C0[A] { val a = a } +//│ ║ l.227: class C1[A] extends C0[A] { val a = a } //│ ╙── ^ //│ ╔══[ERROR] Could not find definition `C0` -//│ ║ l.221: class C1[A] extends C0[A] { val a = a } +//│ ║ l.227: class C1[A] extends C0[A] { val a = a } //│ ╙── ^^ //│ class C1[A] { //│ constructor() @@ -240,10 +246,10 @@ fun foo1 = forall 'A: (x: 'A) => x // GenericMethods foo1[Int](42) //│ ╔══[ERROR] type application syntax is not yet supported -//│ ║ l.241: foo1[Int](42) +//│ ║ l.247: foo1[Int](42) //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Type application syntax is not yet supported -//│ ║ l.241: foo1[Int](42) +//│ ║ l.247: foo1[Int](42) //│ ╙── ^^^^^^^^^ //│ 42 @@ -254,10 +260,10 @@ fun foo2(x: A) = x // GenericMethods foo2(42) //│ ╔══[ERROR] type application syntax is not yet supported -//│ ║ l.255: foo2(42) +//│ ║ l.261: foo2(42) //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Type application syntax is not yet supported -//│ ║ l.255: foo2(42) +//│ ║ l.261: foo2(42) //│ ╙── ^^^^^^^^^ //│ 42 @@ -268,10 +274,10 @@ fun foo3[A](x: A) = x // GenericMethods foo3[Int](42) //│ ╔══[ERROR] type application syntax is not yet supported -//│ ║ l.269: foo3[Int](42) +//│ ║ l.275: foo3[Int](42) //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Type application syntax is not yet supported -//│ ║ l.269: foo3[Int](42) +//│ ║ l.275: foo3[Int](42) //│ ╙── ^^^^^^^^^ //│ 42 @@ -282,28 +288,28 @@ module M { fun oops(x) = m := x } //│ ╔══[PARSE ERROR] Unexpected 'mut' keyword in expression position -//│ ║ l.281: mut val m = None +//│ ║ l.287: mut val m = None //│ ╙── ^^^ //│ ╔══[PARSE ERROR] Unexpected 'val' keyword in expression position -//│ ║ l.281: mut val m = None +//│ ║ l.287: mut val m = None //│ ╙── ^^^ //│ ╔══[ERROR] identifier `:=` not found -//│ ║ l.282: fun oops(x) = m := x +//│ ║ l.288: fun oops(x) = m := x //│ ╙── ^^ //│ ╔══[ERROR] identifier `m` not found -//│ ║ l.282: fun oops(x) = m := x +//│ ║ l.288: fun oops(x) = m := x //│ ╙── ^ //│ ╔══[WARNING] unsupported `Eqn`: m = None -//│ ║ l.281: mut val m = None +//│ ║ l.287: mut val m = None //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] identifier not found: := -//│ ║ l.282: fun oops(x) = m := x +//│ ║ l.288: fun oops(x) = m := x //│ ╙── ^^ //│ ╔══[ERROR] identifier not found: m -//│ ║ l.282: fun oops(x) = m := x +//│ ║ l.288: fun oops(x) = m := x //│ ╙── ^ //│ ╔══[ERROR] Unexpected equation in this position -//│ ║ l.281: mut val m = None +//│ ║ l.287: mut val m = None //│ ╙── ^^^^^^^^ //│ module None //│ module M { @@ -313,64 +319,64 @@ module M { // InterfaceMono trait What0 extends woooo //│ ╔══[ERROR] could not find definition `woooo` -//│ ║ l.314: trait What0 extends woooo +//│ ║ l.320: trait What0 extends woooo //│ ╙── ^^^^^ //│ ╔══[ERROR] Could not find definition `woooo` -//│ ║ l.314: trait What0 extends woooo +//│ ║ l.320: trait What0 extends woooo //│ ╙── ^^^^^ //│ trait What0 // Misc let f = ((x, y)) => x + y //│ ╔══[ERROR] unsupported pattern shape -//│ ║ l.324: let f = ((x, y)) => x + y +//│ ║ l.330: let f = ((x, y)) => x + y //│ ╙── ^^^^^^ //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.324: let f = ((x, y)) => x + y +//│ ║ l.330: let f = ((x, y)) => x + y //│ ╙── ^ //│ ╔══[ERROR] identifier `y` not found -//│ ║ l.324: let f = ((x, y)) => x + y +//│ ║ l.330: let f = ((x, y)) => x + y //│ ╙── ^ //│ ╔══[ERROR] Unsupported pattern shape: -//│ ║ l.324: let f = ((x, y)) => x + y +//│ ║ l.330: let f = ((x, y)) => x + y //│ ╙── ^^^^^^ //│ ╔══[ERROR] identifier not found: x -//│ ║ l.324: let f = ((x, y)) => x + y +//│ ║ l.330: let f = ((x, y)) => x + y //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y -//│ ║ l.324: let f = ((x, y)) => x + y +//│ ║ l.330: let f = ((x, y)) => x + y //│ ╙── ^ //│ let f: error -> Int // Misc f[1, 2] //│ ╔══[ERROR] type application syntax is not yet supported -//│ ║ l.346: f[1, 2] +//│ ║ l.352: f[1, 2] //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Type application syntax is not yet supported -//│ ║ l.346: f[1, 2] +//│ ║ l.352: f[1, 2] //│ ╙── ^^^^^^^ //│ error -> Int // Misc let f = (((x, y))) => x + y //│ ╔══[ERROR] unsupported pattern shape -//│ ║ l.356: let f = (((x, y))) => x + y +//│ ║ l.362: let f = (((x, y))) => x + y //│ ╙── ^^^^^^ //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.356: let f = (((x, y))) => x + y +//│ ║ l.362: let f = (((x, y))) => x + y //│ ╙── ^ //│ ╔══[ERROR] identifier `y` not found -//│ ║ l.356: let f = (((x, y))) => x + y +//│ ║ l.362: let f = (((x, y))) => x + y //│ ╙── ^ //│ ╔══[ERROR] Unsupported pattern shape: -//│ ║ l.356: let f = (((x, y))) => x + y +//│ ║ l.362: let f = (((x, y))) => x + y //│ ╙── ^^^^^^ //│ ╔══[ERROR] identifier not found: x -//│ ║ l.356: let f = (((x, y))) => x + y +//│ ║ l.362: let f = (((x, y))) => x + y //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y -//│ ║ l.356: let f = (((x, y))) => x + y +//│ ║ l.362: let f = (((x, y))) => x + y //│ ╙── ^ //│ let f: error -> Int @@ -378,22 +384,22 @@ let f = (((x, y))) => x + y let v1 = {mut 1} v1.x <- 1 //│ ╔══[PARSE ERROR] Record field should have a name -//│ ║ l.378: let v1 = {mut 1} +//│ ║ l.384: let v1 = {mut 1} //│ ╙── ^ //│ ╔══[ERROR] identifier `<-` not found -//│ ║ l.379: v1.x <- 1 +//│ ║ l.385: v1.x <- 1 //│ ╙── ^^ //│ ╔══[ERROR] identifier not found: <- -//│ ║ l.379: v1.x <- 1 +//│ ║ l.385: v1.x <- 1 //│ ╙── ^^ //│ ╔══[ERROR] Type mismatch in field selection: -//│ ║ l.379: v1.x <- 1 +//│ ║ l.385: v1.x <- 1 //│ ║ ^^^^ //│ ╟── record literal of type `{mut : ?}` does not have field 'x' -//│ ║ l.378: let v1 = {mut 1} +//│ ║ l.384: let v1 = {mut 1} //│ ║ ^ //│ ╟── but it flows into reference with expected type `{x: ?x}` -//│ ║ l.379: v1.x <- 1 +//│ ║ l.385: v1.x <- 1 //│ ╙── ^^ //│ let v1: {mut : '} //│ error @@ -403,7 +409,7 @@ v1.x <- 1 // Mut let v2 = [mut x: 1] //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.404: let v2 = [mut x: 1] +//│ ║ l.410: let v2 = [mut x: 1] //│ ╙── ^ //│ let v2: [mut x: 'x] //│ where @@ -412,7 +418,7 @@ let v2 = [mut x: 1] // Mut let v2 = [mut y: 1] //│ ╔══[ERROR] identifier `y` not found -//│ ║ l.413: let v2 = [mut y: 1] +//│ ║ l.419: let v2 = [mut y: 1] //│ ╙── ^ //│ let v2: [mut y: 'y] //│ where @@ -433,13 +439,13 @@ class Foo5() extends Foo() { fun bar(y: Int) = this.foo + y } //│ ╔══[ERROR] identifier `bar` not found -//│ ║ l.432: val x = bar(0) +//│ ║ l.438: val x = bar(0) //│ ╙── ^^^ //│ ╔══[ERROR] Cannot access `this` while initializing field x -//│ ║ l.432: val x = bar(0) +//│ ║ l.438: val x = bar(0) //│ ║ ^^^^^^^^^^ //│ ╟── The access to `this` is here -//│ ║ l.433: fun bar(y: Int) = this.foo + y +//│ ║ l.439: fun bar(y: Int) = this.foo + y //│ ╙── ^^^^ //│ class Foo5() extends Foo { //│ fun bar: (y: Int) -> Int @@ -453,10 +459,10 @@ abstract class Foo: (Int -> Int) { fun f = this(0) } //│ ╔══[ERROR] Cannot access `this` while initializing field x -//│ ║ l.452: val x = f +//│ ║ l.458: val x = f //│ ║ ^^^^^ //│ ╟── The access to `this` is here -//│ ║ l.453: fun f = this(0) +//│ ║ l.459: fun f = this(0) //│ ╙── ^^^^ //│ abstract class Foo: Int -> Int { //│ fun f: nothing @@ -466,29 +472,29 @@ abstract class Foo: (Int -> Int) { // Object Object //│ ╔══[ERROR] identifier `Object` not found -//│ ║ l.467: Object +//│ ║ l.473: Object //│ ╙── ^^^^^^ //│ ╔══[ERROR] Class Object is abstract and cannot be instantiated -//│ ║ l.467: Object +//│ ║ l.473: Object //│ ╙── ^^^^^^ //│ ╔══[ERROR] Class Object cannot be instantiated as it exposes no constructor -//│ ║ l.467: Object +//│ ║ l.473: Object //│ ╙── ^^^^^^ //│ error // OpLam x => x.y => y //│ ╔══[ERROR] unsupported pattern shape -//│ ║ l.480: x => x.y => y +//│ ║ l.486: x => x.y => y //│ ╙── ^^^ //│ ╔══[ERROR] identifier `y` not found -//│ ║ l.480: x => x.y => y +//│ ║ l.486: x => x.y => y //│ ╙── ^ //│ ╔══[ERROR] Unsupported pattern shape: -//│ ║ l.480: x => x.y => y +//│ ║ l.486: x => x.y => y //│ ╙── ^^^ //│ ╔══[ERROR] identifier not found: y -//│ ║ l.480: x => x.y => y +//│ ║ l.486: x => x.y => y //│ ╙── ^ //│ anything -> error -> error @@ -499,20 +505,20 @@ class Base0(n: Num) // ParamOverride class Derived0(n: Int) extends Base //│ ╔══[ERROR] could not find definition `Base` -//│ ║ l.500: class Derived0(n: Int) extends Base +//│ ║ l.506: class Derived0(n: Int) extends Base //│ ╙── ^^^^ //│ ╔══[ERROR] Could not find definition `Base` -//│ ║ l.500: class Derived0(n: Int) extends Base +//│ ║ l.506: class Derived0(n: Int) extends Base //│ ╙── ^^^^ //│ class Derived0(n: Int) // ParamOverride mixin DerivedBad(n: Int) extends Base //│ ╔══[ERROR] could not find definition `Base` -//│ ║ l.510: mixin DerivedBad(n: Int) extends Base +//│ ║ l.516: mixin DerivedBad(n: Int) extends Base //│ ╙── ^^^^ //│ ╔══[ERROR] mixin definitions cannot yet extend parents -//│ ║ l.510: mixin DerivedBad(n: Int) extends Base +//│ ║ l.516: mixin DerivedBad(n: Int) extends Base //│ ╙── ^^^^ //│ mixin DerivedBad(n: Int) @@ -523,65 +529,65 @@ fun foo(x, y) = x + y // PartialApp foo(2, _) //│ ╔══[ERROR] identifier `_` not found -//│ ║ l.524: foo(2, _) +//│ ║ l.530: foo(2, _) //│ ╙── ^ //│ ╔══[ERROR] Widlcard in expression position. -//│ ║ l.524: foo(2, _) +//│ ║ l.530: foo(2, _) //│ ╙── ^ //│ Int // PartialApp _.foo(1) //│ ╔══[ERROR] identifier `_` not found -//│ ║ l.534: _.foo(1) +//│ ║ l.540: _.foo(1) //│ ╙── ^ //│ ╔══[ERROR] Widlcard in expression position. -//│ ║ l.534: _.foo(1) +//│ ║ l.540: _.foo(1) //│ ╙── ^ //│ error // PartialApp _ + _ //│ ╔══[ERROR] identifier `_` not found -//│ ║ l.544: _ + _ +//│ ║ l.550: _ + _ //│ ╙── ^ //│ ╔══[ERROR] identifier `_` not found -//│ ║ l.544: _ + _ +//│ ║ l.550: _ + _ //│ ╙── ^ //│ ╔══[ERROR] Widlcard in expression position. -//│ ║ l.544: _ + _ +//│ ║ l.550: _ + _ //│ ╙── ^ //│ ╔══[ERROR] Widlcard in expression position. -//│ ║ l.544: _ + _ +//│ ║ l.550: _ + _ //│ ╙── ^ //│ Int // PartialApp _2 + _1 //│ ╔══[ERROR] identifier `_2` not found -//│ ║ l.560: _2 + _1 +//│ ║ l.566: _2 + _1 //│ ╙── ^^ //│ ╔══[ERROR] identifier `_1` not found -//│ ║ l.560: _2 + _1 +//│ ║ l.566: _2 + _1 //│ ╙── ^^ //│ ╔══[ERROR] identifier not found: _2 -//│ ║ l.560: _2 + _1 +//│ ║ l.566: _2 + _1 //│ ╙── ^^ //│ ╔══[ERROR] identifier not found: _1 -//│ ║ l.560: _2 + _1 +//│ ║ l.566: _2 + _1 //│ ╙── ^^ //│ Int // RefinedPatterns refined //│ ╔══[ERROR] identifier `refined` not found -//│ ║ l.576: refined +//│ ║ l.582: refined //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Illegal use of reserved operator: refined -//│ ║ l.576: refined +//│ ║ l.582: refined //│ ╙── ^^^^^^^ //│ ╔══[ERROR] identifier not found: refined -//│ ║ l.576: refined +//│ ║ l.582: refined //│ ╙── ^^^^^^^ //│ error @@ -594,13 +600,13 @@ class D() { fun f = 0 } // Refinements let d = D & { f: 0 } //│ ╔══[ERROR] identifier `&` not found -//│ ║ l.595: let d = D & { f: 0 } +//│ ║ l.601: let d = D & { f: 0 } //│ ╙── ^ //│ ╔══[ERROR] Illegal use of reserved operator: & -//│ ║ l.595: let d = D & { f: 0 } +//│ ║ l.601: let d = D & { f: 0 } //│ ╙── ^ //│ ╔══[ERROR] identifier not found: & -//│ ║ l.595: let d = D & { f: 0 } +//│ ║ l.601: let d = D & { f: 0 } //│ ╙── ^ //│ let d: error @@ -611,63 +617,63 @@ x => x + 2 // Res res(1) //│ ╔══[ERROR] identifier `res` not found -//│ ║ l.612: res(1) +//│ ║ l.618: res(1) //│ ╙── ^^^ //│ ╔══[ERROR] identifier not found: res -//│ ║ l.612: res(1) +//│ ║ l.618: res(1) //│ ╙── ^^^ //│ error // Uninstantiable Int //│ ╔══[ERROR] identifier `Int` is resolved to a type -//│ ║ l.622: Int +//│ ║ l.628: Int //│ ╙── ^^^ //│ ╔══[ERROR] Class Int is abstract and cannot be instantiated -//│ ║ l.622: Int +//│ ║ l.628: Int //│ ╙── ^^^ //│ ╔══[ERROR] Class Int cannot be instantiated as it exposes no constructor -//│ ║ l.622: Int +//│ ║ l.628: Int //│ ╙── ^^^ //│ error // Uninstantiable Int() //│ ╔══[ERROR] identifier `Int` is resolved to a type -//│ ║ l.635: Int() +//│ ║ l.641: Int() //│ ╙── ^^^ //│ ╔══[ERROR] Class Int is abstract and cannot be instantiated -//│ ║ l.635: Int() +//│ ║ l.641: Int() //│ ╙── ^^^ //│ ╔══[ERROR] Class Int cannot be instantiated as it exposes no constructor -//│ ║ l.635: Int() +//│ ║ l.641: Int() //│ ╙── ^^^ //│ error // Uninstantiable new Int //│ ╔══[ERROR] identifier `Int` is resolved to a type -//│ ║ l.648: new Int +//│ ║ l.654: new Int //│ ╙── ^^^ //│ ╔══[ERROR] Class Int is abstract and cannot be instantiated -//│ ║ l.648: new Int +//│ ║ l.654: new Int //│ ╙── ^^^ //│ Int // Unit (1, 2) => 3 //│ ╔══[WARNING] literal patterns are ignored -//│ ║ l.658: (1, 2) => 3 +//│ ║ l.664: (1, 2) => 3 //│ ╙── ^ //│ ╔══[WARNING] literal patterns are ignored -//│ ║ l.658: (1, 2) => 3 +//│ ║ l.664: (1, 2) => 3 //│ ╙── ^ //│ (1, 2) -> 3 // Unit 1 => (2, 3) //│ ╔══[WARNING] literal patterns are ignored -//│ ║ l.668: 1 => (2, 3) +//│ ║ l.674: 1 => (2, 3) //│ ╙── ^ //│ 1 -> 3 @@ -680,23 +686,23 @@ val d = 1 // Varargs fun test(...xs) = xs.length //│ ╔══[PARSE ERROR] Unexpected operator here -//│ ║ l.681: fun test(...xs) = xs.length +//│ ║ l.687: fun test(...xs) = xs.length //│ ╙── ^^^ //│ ╔══[ERROR] identifier `xs` not found -//│ ║ l.681: fun test(...xs) = xs.length +//│ ║ l.687: fun test(...xs) = xs.length //│ ╙── ^^ //│ ╔══[ERROR] identifier not found: xs -//│ ║ l.681: fun test(...xs) = xs.length +//│ ║ l.687: fun test(...xs) = xs.length //│ ╙── ^^ //│ fun test: () -> error // WeirdDefs fun fst[x, _] = x //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.694: fun fst[x, _] = x +//│ ║ l.700: fun fst[x, _] = x //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x -//│ ║ l.694: fun fst[x, _] = x +//│ ║ l.700: fun fst[x, _] = x //│ ╙── ^ //│ fun fst: error @@ -709,7 +715,7 @@ module EvalAddLit { } let res = EvalAddLit.eval(add11) //│ ╔══[ERROR] identifier `add11` not found -//│ ║ l.705: val add11 = Add(add11) +//│ ║ l.711: val add11 = Add(add11) //│ ╙── ^^^^^ //│ class Add[E](lhs: E) //│ val add11: 'E From 681f294debc4341f4b312a58ff9ebfb545d1e316 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 17 Jan 2024 01:11:02 +0800 Subject: [PATCH 080/143] Remove useless files and fix many minor issues mentioned in PR --- .../src/main/scala/mlscript/JSBackend.scala | 16 -- .../main/scala/mlscript/pretyper/Symbol.scala | 2 +- .../scala/mlscript/pretyper/package.scala | 5 - .../mlscript/ucs/stages/PostProcessing.scala | 4 - .../main/scala/mlscript/ucs/syntax/core.scala | 2 +- .../scala/mlscript/ucs/syntax/package.scala | 3 - shared/src/test/diff/codegen/NewDefsBug.mls | 72 -------- .../src/test/diff/pretyper/Declarations.mls | 20 --- shared/src/test/diff/pretyper/Repro.mls | 1 + todo.md | 163 ------------------ 10 files changed, 3 insertions(+), 285 deletions(-) delete mode 100644 shared/src/main/scala/mlscript/pretyper/package.scala delete mode 100644 shared/src/main/scala/mlscript/ucs/syntax/package.scala delete mode 100644 shared/src/test/diff/codegen/NewDefsBug.mls delete mode 100644 shared/src/test/diff/pretyper/Declarations.mls delete mode 100644 todo.md diff --git a/shared/src/main/scala/mlscript/JSBackend.scala b/shared/src/main/scala/mlscript/JSBackend.scala index febdf926..f1805482 100644 --- a/shared/src/main/scala/mlscript/JSBackend.scala +++ b/shared/src/main/scala/mlscript/JSBackend.scala @@ -116,7 +116,6 @@ abstract class JSBackend(allowUnresolvedSymbols: Bool) { else throw new UnimplementedError(sym) case S(sym: ValueSymbol) => - // Temporary disable this line of code because it invalidates many working test cases. if (sym.isByvalueRec.getOrElse(false) && !sym.isLam) throw CodeGenError(s"unguarded recursive use of by-value binding $name") sym.visited = true val ident = JSIdent(sym.runtimeName) @@ -200,8 +199,6 @@ abstract class JSBackend(allowUnresolvedSymbols: Bool) { JSRecord(fields map { case (key, Fld(_, value)) => key.name -> translateTerm(value) }) - case Sel(receiver, JSBackend.TupleIndex(n)) => - JSField(translateTerm(receiver), n.toString) case Sel(receiver, fieldName) => JSField(translateTerm(receiver), fieldName.name) // Turn let into an IIFE. @@ -1596,17 +1593,4 @@ object JSBackend { def isSafeInteger(value: BigInt): Boolean = MinimalSafeInteger <= value && value <= MaximalSafeInteger - - // Temporary measure until we adopt the new tuple index. - object TupleIndex { - def unapply(fieldName: Var): Opt[Int] = { - val name = fieldName.name - if (name.startsWith("_") && name.forall(_.isDigit)) - name.drop(1).toIntOption match { - case S(n) if n > 0 => S(n - 1) - case _ => N - } - else N - } - } } diff --git a/shared/src/main/scala/mlscript/pretyper/Symbol.scala b/shared/src/main/scala/mlscript/pretyper/Symbol.scala index d08cc7b6..5a774d0a 100644 --- a/shared/src/main/scala/mlscript/pretyper/Symbol.scala +++ b/shared/src/main/scala/mlscript/pretyper/Symbol.scala @@ -98,4 +98,4 @@ package object symbol { class LocalTermSymbol(val nme: Var) extends TermSymbol { override def name: Str = nme.name } -} \ No newline at end of file +} diff --git a/shared/src/main/scala/mlscript/pretyper/package.scala b/shared/src/main/scala/mlscript/pretyper/package.scala deleted file mode 100644 index addb94ce..00000000 --- a/shared/src/main/scala/mlscript/pretyper/package.scala +++ /dev/null @@ -1,5 +0,0 @@ -package mlscript - -import mlscript.utils._, shorthands._ - -package object pretyper diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index fb496df0..9e26575c 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -176,10 +176,6 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => val (n2, y2) = disentangleMatchedCaseBranches(rest) (kase.copy(body = n1, rest = n2)(kase.refined), mergeTerms(y1, y2)) } - // case kase @ Case(otherClassName, body, rest) => - // println(s"found another case branch matching against $otherClassName") - // val (n, y) = disentangleMatchedCaseBranches(rest) - // kase.copy(rest = n) -> y } diff --git a/shared/src/main/scala/mlscript/ucs/syntax/core.scala b/shared/src/main/scala/mlscript/ucs/syntax/core.scala index 92e3d12a..4b39414b 100644 --- a/shared/src/main/scala/mlscript/ucs/syntax/core.scala +++ b/shared/src/main/scala/mlscript/ucs/syntax/core.scala @@ -99,4 +99,4 @@ package object core { final case class Else(default: Term) extends Split final case object Nil extends Split } -} \ No newline at end of file +} diff --git a/shared/src/main/scala/mlscript/ucs/syntax/package.scala b/shared/src/main/scala/mlscript/ucs/syntax/package.scala deleted file mode 100644 index bfe2aec7..00000000 --- a/shared/src/main/scala/mlscript/ucs/syntax/package.scala +++ /dev/null @@ -1,3 +0,0 @@ -package mlscript.ucs - -package object syntax diff --git a/shared/src/test/diff/codegen/NewDefsBug.mls b/shared/src/test/diff/codegen/NewDefsBug.mls deleted file mode 100644 index 2f004173..00000000 --- a/shared/src/test/diff/codegen/NewDefsBug.mls +++ /dev/null @@ -1,72 +0,0 @@ -:NewDefs - -:js -fun foo: Int -> Int -fun foo = x => x + 1 -class Bar { - fun calc(x) = foo(x) -} -//│ fun foo: Int -> Int -//│ class Bar { -//│ constructor() -//│ fun calc: Int -> Int -//│ } -//│ fun foo: Int -> Int -//│ // Prelude -//│ let res; -//│ class TypingUnit { -//│ #Bar; -//│ constructor() { -//│ } -//│ get Bar() { -//│ const qualifier = this; -//│ if (this.#Bar === undefined) { -//│ class Bar { -//│ constructor() { -//│ } -//│ calc(x) { -//│ return foo(x); -//│ } -//│ }; -//│ this.#Bar = Bar; -//│ } -//│ return this.#Bar; -//│ } -//│ } -//│ const typing_unit = new TypingUnit; -//│ globalThis.Bar = typing_unit.Bar; -//│ // Query 1 is empty -//│ // Query 2 -//│ globalThis.foo = function foo(x) { -//│ return x + 1; -//│ }; -//│ // End of generated code - -// Note: This test case looks trivial but it was like: -// -// ``` -// :re -// (new Bar()).calc(0) -// //│ Int -// //│ res -// //│ Runtime error: -// //│ ReferenceError: foo is not defined -// ``` -// -// My fix is a little bit hacky. The root of the problem is: when generating -// code within a class, we need all top-level bindings to be accessible. This -// part of implementation of new-definition-typing chose to declare all term -// `NuFunDef` as `ValueSymbol` in advance, but this can lead to the fact that -// the same symbol is declared multiple times, thus wasting some runtime names. -// Consequently, the code that references these wasted runtime names are invalid. -// -// Actually, I have a better solution, but it requires adjusting the order of -// translation, and I don't have much time to spend on this at the moment. So, -// my current fix is rather hacky. But I will complete this part when `PreTyper` -// is finished, when replacing the old Scope with the new Scope. -// -// Luyu Cheng on 2023/12/30 -(new Bar()).calc(0) -//│ Int -//│ res -//│ = 1 diff --git a/shared/src/test/diff/pretyper/Declarations.mls b/shared/src/test/diff/pretyper/Declarations.mls deleted file mode 100644 index 7f07a109..00000000 --- a/shared/src/test/diff/pretyper/Declarations.mls +++ /dev/null @@ -1,20 +0,0 @@ -:NewDefs -:NoJS - - -fun test(x, y) = x + y -//│ fun test: (Int, Int) -> Int - - -// Functions are hoisted. -let y = id(42) -fun id(x) = x -//│ let y: 42 | 'a -//│ fun id: forall 'b. ('a & 'b) -> (42 | 'b) - - -// Function bodies can access variables declare after them. -fun q(x) = x + p -let p = 0 -//│ fun q: Int -> Int -//│ let p: 0 diff --git a/shared/src/test/diff/pretyper/Repro.mls b/shared/src/test/diff/pretyper/Repro.mls index 34d9bdfe..c6607647 100644 --- a/shared/src/test/diff/pretyper/Repro.mls +++ b/shared/src/test/diff/pretyper/Repro.mls @@ -1 +1,2 @@ :NewDefs +:ShowPreTyperErrors diff --git a/todo.md b/todo.md deleted file mode 100644 index 9f1ebcbc..00000000 --- a/todo.md +++ /dev/null @@ -1,163 +0,0 @@ -This file will be deleted after we migrate all test cases and fixed all -problems located by test cases. - -### Remaining Tasks - -- [x] Report unreachable or redundant cases - - [x] shared/src/test/diff/ucs/DirectLines.mls **OLD** - - [x] shared/src/test/diff/ucs/SplitAnd.mls - - [x] shared/src/test/diff/ucs/WeirdIf.mls - - [x] shared/src/test/diff/ucs/Wildcard.mls -- [x] Hygenic bindings - - [ ] shared/src/test/diff/ucs/CrossBranchCapture.mls - - [x] shared/src/test/diff/ucs/HygienicBindings.mls - -### Test Checklist - -- [x] shared/src/test/diff/codegen/AuxiliaryConstructors.mls -- [x] shared/src/test/diff/codegen/Mixin.mls - Fix that `PreTyper` does not traverse type definitions. -- [x] shared/src/test/diff/codegen/MixinCapture.mls -- [x] shared/src/test/diff/codegen/Nested.mls -- [x] shared/src/test/diff/codegen/NewMatching.mls - Destructing unparameterized class no longer causes code generation errors. -- [x] shared/src/test/diff/codegen/ValLet.mls -- [x] shared/src/test/diff/ecoop23/ComparePointPoly.mls - Desugar UCS shorthands in `PreTyper`. -- [x] shared/src/test/diff/ecoop23/ExpressionProblem.mls -- [x] shared/src/test/diff/ecoop23/Intro.mls -- [x] shared/src/test/diff/ecoop23/PolymorphicVariants.mls -- [x] shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls -- [x] shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls -- [x] shared/src/test/diff/fcp/QML_exist_nu.mls -- [x] shared/src/test/diff/gadt/Exp1.mls -- [x] shared/src/test/diff/gadt/Exp2.mls -- [x] shared/src/test/diff/gadt/ThisMatching.mls -- [x] shared/src/test/diff/nu/Andong.mls -- [x] shared/src/test/diff/nu/ArrayProg.mls -- [x] shared/src/test/diff/nu/BadUCS.mls - Add many `:ge` to cases where match scrutinee against mixins. - Add dummy class symbol so that we don't need to throw any - errors during desugaring. -- [x] shared/src/test/diff/nu/BasicClassInheritance.mls -- [x] shared/src/test/diff/nu/BasicClasses.mls -- [x] shared/src/test/diff/nu/CaseExpr.mls -- [x] shared/src/test/diff/nu/ClassSignatures.mls -- [x] shared/src/test/diff/nu/ClassesInMixins.mls - Improve error messages in `PreTyper`. -- [x] shared/src/test/diff/nu/CommaOperator.mls -- [x] shared/src/test/diff/nu/CtorSubtraction.mls -- [x] shared/src/test/diff/nu/Eval.mls -- [x] shared/src/test/diff/nu/EvalNegNeg.mls -- [x] shared/src/test/diff/nu/ExpressionProblem_repro.mls -- [x] shared/src/test/diff/nu/ExpressionProblem_small.mls -- [x] shared/src/test/diff/nu/FilterMap.mls -- [x] shared/src/test/diff/nu/FlatIfThenElse.mls -- [x] shared/src/test/diff/nu/FlatMonads.mls -- [x] shared/src/test/diff/nu/FunnyIndet.mls -- [x] shared/src/test/diff/nu/GADTMono.mls -- [x] shared/src/test/diff/nu/GenericClasses.mls -- [x] shared/src/test/diff/nu/GenericModules.mls -- [x] shared/src/test/diff/nu/HeungTung.mls -- [x] shared/src/test/diff/nu/Huawei1.mls -- [x] shared/src/test/diff/nu/InterfaceMono.mls -- [ ] shared/src/test/diff/nu/Interfaces.mls - What? Traits can't be patterns? -- [x] shared/src/test/diff/nu/LetRec.mls -- [x] shared/src/test/diff/nu/ListConsNil.mls -- [x] shared/src/test/diff/nu/LitMatch.mls -- [x] shared/src/test/diff/nu/MissingTypeArg.mls -- [x] shared/src/test/diff/nu/NamedArgs.mls -- [x] shared/src/test/diff/nu/New.mls **OLD** -- [x] shared/src/test/diff/nu/NewNew.mls -- [x] shared/src/test/diff/nu/Object.mls -- [x] shared/src/test/diff/nu/OpLam.mls - Function `extractParameters` no longer raise errors. -- [x] shared/src/test/diff/nu/OptionFilter.mls -- [x] shared/src/test/diff/nu/OverrideShorthand.mls -- [x] shared/src/test/diff/nu/ParamPassing.mls -- [x] shared/src/test/diff/nu/PolymorphicVariants_Alt.mls -- [x] shared/src/test/diff/nu/PostHocMixinSignature.mls -- [x] shared/src/test/diff/nu/PrivateMemberOverriding.mls -- [x] shared/src/test/diff/nu/SelfRec.mls -- [x] shared/src/test/diff/nu/Subscripts.mls -- [x] shared/src/test/diff/nu/TODO_Classes.mls -- [x] shared/src/test/diff/nu/Unapply.mls -- [x] shared/src/test/diff/nu/UndefMatching.mls -- [x] shared/src/test/diff/nu/WeirdUnions.mls -- [x] shared/src/test/diff/nu/i180.mls -- [x] shared/src/test/diff/nu/repro0.mls - - `PreTyper` does not accept top-level `val` bindings. -- [x] shared/src/test/diff/nu/repro1.mls -- [x] shared/src/test/diff/nu/repro_EvalNegNeg.mls -- [x] shared/src/test/diff/nu/repro_PolymorphicVariants.mls -- [x] shared/src/test/diff/pretyper/ucs/examples/JSON.mls -- [x] shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls -- [x] shared/src/test/diff/pretyper/ucs/examples/Option.mls -- [x] shared/src/test/diff/pretyper/ucs/examples/STLC.mls -- [x] shared/src/test/diff/ucs/AppSplits.mls -- [x] shared/src/test/diff/ucs/CrossBranchCapture.mls - Fix the mentioned problems. - TODO: Warn duplicated pattern bindings. -- [x] shared/src/test/diff/ucs/DirectLines.mls **OLD** - TODO: Warn duplicated else branches. -- [x] shared/src/test/diff/ucs/ElseIf.mls -- [x] shared/src/test/diff/ucs/ErrorMessage.mls **OLD** -- [x] shared/src/test/diff/ucs/Exhaustiveness.mls -- [x] shared/src/test/diff/ucs/Humiliation.mls **OLD** - TODO: Improve scrutinee name display. - Generated names should be polished. -- [x] shared/src/test/diff/ucs/Hygiene.mls - Problem fixed! -- [x] shared/src/test/diff/ucs/HygienicBindings.mls - We should fix the shadowing parameters. -- [x] shared/src/test/diff/ucs/InterleavedLet.mls **OLD** - The transformation cannot handle the following case. - ``` - fun mapPartition2(f, xs) = - if xs is - Nil then Pair(Nil, Nil) - Cons(x, xs) and mapPartition(f, xs) is Pair(l, r) and f(x) is - Left(v) then Pair(Cons(v, l), r) - Right(v) then Pair(l, Cons(v, r)) - ``` - To be specific, `Cons(x, xs) and mapPartition(f, xs) is Pair(l, r)` are - not separated. I re-implemented `splitAnd` function. -- [x] shared/src/test/diff/ucs/JSON.mls - Deleted. This one is not completed and we have a new version. -- [x] shared/src/test/diff/ucs/LeadingAnd.mls -- [x] shared/src/test/diff/ucs/LitUCS.mls -- [x] shared/src/test/diff/ucs/MultiwayIf.mls -- [x] shared/src/test/diff/ucs/NestedBranches.mls - Found a bug in transformation. -- [x] shared/src/test/diff/ucs/NestedOpSplits.mls -- [x] shared/src/test/diff/ucs/NestedPattern.mls -- [x] shared/src/test/diff/ucs/NuPlainConditionals.mls -- [x] shared/src/test/diff/ucs/Or.mls -- [x] shared/src/test/diff/ucs/OverlappedBranches.mls **OLD** - Should report unreachable cases. -- [x] shared/src/test/diff/ucs/ParseFailures.mls -- [x] shared/src/test/diff/ucs/PlainConditionals.mls - Maybe we should keep this old one... -- [x] shared/src/test/diff/ucs/SimpleUCS.mls - Migrate this test case to new defintion typing. - Remove a `???` and raise error during transformation. -- [x] shared/src/test/diff/ucs/SplitAfterOp.mls - Wrap tests in functions so that errors are clearer. -- [x] shared/src/test/diff/ucs/SplitAnd.mls - Should report missing else branches. -- [x] shared/src/test/diff/ucs/SplitAroundOp.mls -- [x] shared/src/test/diff/ucs/SplitBeforeOp.mls -- [x] shared/src/test/diff/ucs/SplitOps.mls -- [x] shared/src/test/diff/ucs/SplitScrutinee.mls - Fixed. -- [x] shared/src/test/diff/ucs/ThenIndent.mls -- [x] shared/src/test/diff/ucs/Tree.mls -- [x] shared/src/test/diff/ucs/TrivialIf.mls **OLD** -- [x] shared/src/test/diff/ucs/WeirdIf.mls - Should report redundant cases. -- [x] shared/src/test/diff/ucs/WeirdSplit.mls **OLD** -- [x] shared/src/test/diff/ucs/Wildcard.mls - Some unexpected empty splits. -- [x] shared/src/test/diff/ucs/zipWith.mls - From 2086b7f174dd27f81c5d610ede613df43e9c040e Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 17 Jan 2024 01:52:14 +0800 Subject: [PATCH 081/143] Make readable names for generated variables --- .../mlscript/ucs/context/PatternInfo.scala | 3 ++ .../mlscript/ucs/context/Scrutinee.scala | 14 ++++++ .../ucs/stages/CoverageChecking.scala | 20 +++++--- .../main/scala/mlscript/utils/package.scala | 17 +++++++ shared/src/test/diff/nu/ArrayProg.mls | 24 +++++----- .../pretyper/ucs/coverage/MissingCases.mls | 48 +++++++++---------- .../pretyper/ucs/coverage/SealedClasses.mls | 6 +-- shared/src/test/diff/ucs/ElseIf.mls | 24 +++++----- shared/src/test/diff/ucs/Exhaustiveness.mls | 8 ++-- shared/src/test/diff/ucs/Humiliation.mls | 28 +++++------ shared/src/test/diff/ucs/InterleavedLet.mls | 4 +- shared/src/test/diff/ucs/SimpleUCS.mls | 42 ++++++++-------- 12 files changed, 139 insertions(+), 99 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/context/PatternInfo.scala b/shared/src/main/scala/mlscript/ucs/context/PatternInfo.scala index 07362bed..5d232e55 100644 --- a/shared/src/main/scala/mlscript/ucs/context/PatternInfo.scala +++ b/shared/src/main/scala/mlscript/ucs/context/PatternInfo.scala @@ -36,6 +36,9 @@ object PatternInfo { private var unappliedVarOpt: Opt[Var] = N private val parameters: MutSortedMap[Int, Scrutinee] = MutSortedMap.empty + private[context] def findSubScrutinee(scrutinee: Scrutinee): Opt[Int] = + parameters.find(_._2 === scrutinee).map(_._1) + /** * Get or create a sub-scrutinee for the given parameter index. * diff --git a/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala index 685cbb50..e45dec19 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala @@ -116,6 +116,20 @@ class Scrutinee(val context: Context, parent: Opt[Scrutinee]) { }.toMap[Pattern, Ls[Loc]] CaseSet(cases ++ tuplePattern) } + + def getReadableName(scrutineeVar: Var): Str = { + parent match { + case N if context.isGeneratedVar(scrutineeVar) => "term" + case N => s"`${scrutineeVar.name}`" + case S(parentScrutinee) => + parentScrutinee.classLikePatterns.iterator.flatMap { case (symbol, pattern) => + pattern.findSubScrutinee(this).map(_ -> symbol.name) + }.nextOption() match { + case S(index -> typeName) => s"${index.toOrdinalWord} argument of `${typeName}`" + case N => s"`${scrutineeVar.name}`" // Still not the best. + } + } + } } object Scrutinee { diff --git a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala index 3bb3c6ff..2ef80662 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala @@ -113,22 +113,28 @@ trait CoverageChecking { self: Desugarer with Traceable => object CoverageChecking { /** Create an `ErrorReport` that explains missing cases. */ - private def explainMissingCases(scrutinee: NamedScrutinee, seen: SeenRegistry, missingCases: CaseSet): Opt[ErrorReport] = + private def explainMissingCases( + scrutinee: NamedScrutinee, + seen: SeenRegistry, + missingCases: CaseSet + )(implicit context: Context): Opt[ErrorReport] = if (missingCases.isEmpty) { N } else { S(ErrorReport({ - val lines = (msg"Scrutinee `${scrutinee._1.name}` has ${"missing case".pluralize(missingCases.size, true)}" -> scrutinee._1.toLoc) :: + val readableName = scrutinee._2.getReadableName(scrutinee._1) + val lines = (msg"$readableName has ${"missing case".pluralize(missingCases.size, true)}" -> scrutinee._1.toLoc) :: (missingCases.cases.iterator.flatMap { case (pattern, locations) => - (msg"It can be ${pattern.toString}" -> locations.headOption) :: Nil + (msg"it can be ${pattern.toString}" -> locations.headOption) :: Nil }.toList) if (seen.isEmpty) { lines } else { - seen.iterator.zipWithIndex.map { case ((scrutinee, (classSymbol, locations, cases)), i) => - val prologue = if (i === 0) "When " else "" - val epilogue = if (seen.size === 1) "" else if (i === seen.size - 1) ", and" else "," - msg"${prologue}scrutinee `${scrutinee._1.name}` is `${classSymbol.name}`$epilogue" -> locations.headOption + seen.iterator.zipWithIndex.map { case ((scrutineeVar -> scrutinee, (classSymbol, locations, cases)), i) => + val prologue = if (i === 0) "when " else "" + val epilogue = if (seen.size === 1) "" else if (i === seen.size - 2) ", and" else "," + val scrutineeName = scrutinee.getReadableName(scrutineeVar) + msg"$prologue$scrutineeName is `${classSymbol.name}`$epilogue" -> locations.headOption }.toList ::: lines } }, true, Diagnostic.Desugaring)) diff --git a/shared/src/main/scala/mlscript/utils/package.scala b/shared/src/main/scala/mlscript/utils/package.scala index adb607a6..e8bb4c46 100644 --- a/shared/src/main/scala/mlscript/utils/package.scala +++ b/shared/src/main/scala/mlscript/utils/package.scala @@ -21,6 +21,23 @@ package object utils { def in(xs: A => Bool): Bool = xs(self) def in(xs: Seq[_ >: A]): Bool = xs.exists(_ === self) } + + implicit class IntOps(private val self: Int) extends AnyVal { + def toOrdinalWord: String = { + require(self >= 0) + self + 1 match { + case 1 => "first" + case 2 => "second" + case 3 => "third" + case n => self.toString + (n % 10 match { + case 1 => "st" + case 2 => "nd" + case 3 => "rd" + case _ => "th" + }) + } + } + } implicit class StringOps(private val self: String) extends AnyVal { import collection.mutable diff --git a/shared/src/test/diff/nu/ArrayProg.mls b/shared/src/test/diff/nu/ArrayProg.mls index a0b7553f..89d7e919 100644 --- a/shared/src/test/diff/nu/ArrayProg.mls +++ b/shared/src/test/diff/nu/ArrayProg.mls @@ -189,14 +189,14 @@ fun add(e) = Pair(Numbr(n), Numbr(m)) then 0 Pair(Vectr(xs), Vectr(ys)) then 1 Pair(Vectr(xs), Numbr(n)) then 2 -//│ ╔══[ERROR] When scrutinee `e` is `Pair`, +//│ ╔══[ERROR] when `e` is `Pair`, and //│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 //│ ║ ^^^^ -//│ ╟── scrutinee `e$Pair_0` is `Numbr`, and +//│ ╟── first argument of `Pair` is `Numbr`, //│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 //│ ║ ^^^^^ -//│ ╟── Scrutinee `e$Pair_1` has 1 missing case -//│ ╟── It can be class `Vectr` +//│ ╟── second argument of `Pair` has 1 missing case +//│ ╟── it can be class `Vectr` //│ ║ l.+4: Pair(Vectr(xs), Vectr(ys)) then 1 //│ ╙── ^^^^^ //│ fun add: Pair[Numbr | Vectr, Numbr] -> (0 | 1 | 2) @@ -206,24 +206,24 @@ fun add(e) = if e is Pair(Numbr(n), Numbr(m)) then 0 Pair(Vectr(xs), Vectr(ys)) then 1 -//│ ╔══[ERROR] When scrutinee `e` is `Pair`, +//│ ╔══[ERROR] when `e` is `Pair`, and //│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 //│ ║ ^^^^ -//│ ╟── scrutinee `e$Pair_0` is `Numbr`, and +//│ ╟── first argument of `Pair` is `Numbr`, //│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 //│ ║ ^^^^^ -//│ ╟── Scrutinee `e$Pair_1` has 1 missing case -//│ ╟── It can be class `Vectr` +//│ ╟── second argument of `Pair` has 1 missing case +//│ ╟── it can be class `Vectr` //│ ║ l.+4: Pair(Vectr(xs), Vectr(ys)) then 1 //│ ╙── ^^^^^ -//│ ╔══[ERROR] When scrutinee `e` is `Pair`, +//│ ╔══[ERROR] when `e` is `Pair`, and //│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 //│ ║ ^^^^ -//│ ╟── scrutinee `e$Pair_0` is `Vectr`, and +//│ ╟── first argument of `Pair` is `Vectr`, //│ ║ l.+4: Pair(Vectr(xs), Vectr(ys)) then 1 //│ ║ ^^^^^ -//│ ╟── Scrutinee `e$Pair_1` has 1 missing case -//│ ╟── It can be class `Numbr` +//│ ╟── second argument of `Pair` has 1 missing case +//│ ╟── it can be class `Numbr` //│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 //│ ╙── ^^^^^ //│ fun add: Pair[Numbr | Vectr, nothing] -> (0 | 1) diff --git a/shared/src/test/diff/pretyper/ucs/coverage/MissingCases.mls b/shared/src/test/diff/pretyper/ucs/coverage/MissingCases.mls index 9d628464..d7f68e79 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/MissingCases.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/MissingCases.mls @@ -23,13 +23,13 @@ fun bad_add_missing_SS(x, y) = x is Some(xv) and y is None then xv x is None and y is Some(yv) then yv x is None and y is None then 0 -//│ ╔══[ERROR] When scrutinee `x` is `Some` +//│ ╔══[ERROR] when `x` is `Some` //│ ║ l.23: x is Some(xv) and y is None then xv //│ ║ ^^^^ -//│ ╟── Scrutinee `y` has 1 missing case +//│ ╟── `y` has 1 missing case //│ ║ l.23: x is Some(xv) and y is None then xv //│ ║ ^ -//│ ╟── It can be class `Some` +//│ ╟── it can be class `Some` //│ ║ l.24: x is None and y is Some(yv) then yv //│ ╙── ^^^^ //│ fun bad_add_missing_SS: forall 'a. (None | Some['a], None) -> (0 | 'a) @@ -40,13 +40,13 @@ fun bad_add_missing_SN(x, y) = x is Some(xv) and y is Some(yv) then xv + yv x is None and y is Some(yv) then yv x is None and y is None then 0 -//│ ╔══[ERROR] When scrutinee `x` is `Some` +//│ ╔══[ERROR] when `x` is `Some` //│ ║ l.40: x is Some(xv) and y is Some(yv) then xv + yv //│ ║ ^^^^ -//│ ╟── Scrutinee `y` has 1 missing case +//│ ╟── `y` has 1 missing case //│ ║ l.40: x is Some(xv) and y is Some(yv) then xv + yv //│ ║ ^ -//│ ╟── It can be module `None` +//│ ╟── it can be module `None` //│ ║ l.42: x is None and y is None then 0 //│ ╙── ^^^^ //│ fun bad_add_missing_SN: forall 'a. (None | Some[Int], Some[Int & 'a]) -> (Int | 'a) @@ -57,13 +57,13 @@ fun bad_add_missing_NS(x, y) = x is Some(xv) and y is Some(yv) then xv + yv x is Some(xv) and y is None then xv x is None and y is None then 0 -//│ ╔══[ERROR] When scrutinee `x` is `None` +//│ ╔══[ERROR] when `x` is `None` //│ ║ l.59: x is None and y is None then 0 //│ ║ ^^^^ -//│ ╟── Scrutinee `y` has 1 missing case +//│ ╟── `y` has 1 missing case //│ ║ l.59: x is None and y is None then 0 //│ ║ ^ -//│ ╟── It can be class `Some` +//│ ╟── it can be class `Some` //│ ║ l.57: x is Some(xv) and y is Some(yv) then xv + yv //│ ╙── ^^^^ //│ fun bad_add_missing_NS: (None | Some[Int], None) -> Int @@ -74,13 +74,13 @@ fun bad_add_missing_NN(x, y) = x is Some(xv) and y is Some(yv) then xv + yv x is Some(xv) and y is None then xv x is None and y is Some(yv) then yv -//│ ╔══[ERROR] When scrutinee `x` is `None` +//│ ╔══[ERROR] when `x` is `None` //│ ║ l.76: x is None and y is Some(yv) then yv //│ ║ ^^^^ -//│ ╟── Scrutinee `y` has 1 missing case +//│ ╟── `y` has 1 missing case //│ ║ l.76: x is None and y is Some(yv) then yv //│ ║ ^ -//│ ╟── It can be module `None` +//│ ╟── it can be module `None` //│ ║ l.75: x is Some(xv) and y is None then xv //│ ╙── ^^^^ //│ fun bad_add_missing_NN: forall 'a. (None | Some[Int], Some[Int & 'a]) -> (Int | 'a) @@ -90,22 +90,22 @@ fun bad_add_missing_SS_NN(x, y) = if x is Some(xv) and y is None then xv x is None and y is Some(yv) then yv -//│ ╔══[ERROR] When scrutinee `x` is `Some` +//│ ╔══[ERROR] when `x` is `Some` //│ ║ l.91: x is Some(xv) and y is None then xv //│ ║ ^^^^ -//│ ╟── Scrutinee `y` has 1 missing case +//│ ╟── `y` has 1 missing case //│ ║ l.91: x is Some(xv) and y is None then xv //│ ║ ^ -//│ ╟── It can be class `Some` +//│ ╟── it can be class `Some` //│ ║ l.92: x is None and y is Some(yv) then yv //│ ╙── ^^^^ -//│ ╔══[ERROR] When scrutinee `x` is `None` +//│ ╔══[ERROR] when `x` is `None` //│ ║ l.92: x is None and y is Some(yv) then yv //│ ║ ^^^^ -//│ ╟── Scrutinee `y` has 1 missing case +//│ ╟── `y` has 1 missing case //│ ║ l.92: x is None and y is Some(yv) then yv //│ ║ ^ -//│ ╟── It can be module `None` +//│ ╟── it can be module `None` //│ ║ l.91: x is Some(xv) and y is None then xv //│ ╙── ^^^^ //│ fun bad_add_missing_SS_NN: forall 'a. (None | Some['a], nothing) -> 'a @@ -115,22 +115,22 @@ fun bad_add_missing_SN_NS(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv x is None and y is None then 0 -//│ ╔══[ERROR] When scrutinee `x` is `Some` +//│ ╔══[ERROR] when `x` is `Some` //│ ║ l.116: x is Some(xv) and y is Some(yv) then xv + yv //│ ║ ^^^^ -//│ ╟── Scrutinee `y` has 1 missing case +//│ ╟── `y` has 1 missing case //│ ║ l.116: x is Some(xv) and y is Some(yv) then xv + yv //│ ║ ^ -//│ ╟── It can be module `None` +//│ ╟── it can be module `None` //│ ║ l.117: x is None and y is None then 0 //│ ╙── ^^^^ -//│ ╔══[ERROR] When scrutinee `x` is `None` +//│ ╔══[ERROR] when `x` is `None` //│ ║ l.117: x is None and y is None then 0 //│ ║ ^^^^ -//│ ╟── Scrutinee `y` has 1 missing case +//│ ╟── `y` has 1 missing case //│ ║ l.117: x is None and y is None then 0 //│ ║ ^ -//│ ╟── It can be class `Some` +//│ ╟── it can be class `Some` //│ ║ l.116: x is Some(xv) and y is Some(yv) then xv + yv //│ ╙── ^^^^ //│ fun bad_add_missing_SN_NS: (None | Some[Int], nothing) -> Int diff --git a/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls b/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls index d852038b..73af7dad 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls @@ -53,13 +53,13 @@ fun is_value'(term) = if term is Term and term is Abs(_, _) then true Var(_) then false -//│ ╔══[ERROR] When scrutinee `term` is `Term` +//│ ╔══[ERROR] when `term` is `Term` //│ ║ l.53: if term is Term and term is //│ ║ ^^^^ -//│ ╟── Scrutinee `term` has 1 missing case +//│ ╟── `term` has 1 missing case //│ ║ l.53: if term is Term and term is //│ ║ ^^^^ -//│ ╟── It can be class `App` +//│ ╟── it can be class `App` //│ ║ l.6: class App(func: Term, arg: Term) extends Term //│ ╙── ^^^ //│ fun is_value': (Abs | Var) -> Bool diff --git a/shared/src/test/diff/ucs/ElseIf.mls b/shared/src/test/diff/ucs/ElseIf.mls index 8f59b078..2f52f9b9 100644 --- a/shared/src/test/diff/ucs/ElseIf.mls +++ b/shared/src/test/diff/ucs/ElseIf.mls @@ -28,22 +28,22 @@ module Fals fun f(x, y) = if x is Tru and y is Tru then true Fals and y is Fals then false -//│ ╔══[ERROR] When scrutinee `x` is `Tru` +//│ ╔══[ERROR] when `x` is `Tru` //│ ║ l.29: Tru and y is Tru then true //│ ║ ^^^ -//│ ╟── Scrutinee `y` has 1 missing case +//│ ╟── `y` has 1 missing case //│ ║ l.29: Tru and y is Tru then true //│ ║ ^ -//│ ╟── It can be module `Fals` +//│ ╟── it can be module `Fals` //│ ║ l.30: Fals and y is Fals then false //│ ╙── ^^^^ -//│ ╔══[ERROR] When scrutinee `x` is `Fals` +//│ ╔══[ERROR] when `x` is `Fals` //│ ║ l.30: Fals and y is Fals then false //│ ║ ^^^^ -//│ ╟── Scrutinee `y` has 1 missing case +//│ ╟── `y` has 1 missing case //│ ║ l.30: Fals and y is Fals then false //│ ║ ^ -//│ ╟── It can be module `Tru` +//│ ╟── it can be module `Tru` //│ ║ l.29: Tru and y is Tru then true //│ ╙── ^^^ //│ fun f: (Fals | Tru, nothing) -> Bool @@ -105,22 +105,22 @@ fun f(x, y) = if x is else if y is Tru and x is Fals then true Fals and x is Tru then false -//│ ╔══[ERROR] When scrutinee `y` is `Tru` +//│ ╔══[ERROR] when `y` is `Tru` //│ ║ l.106: Tru and x is Fals then true //│ ║ ^^^ -//│ ╟── Scrutinee `x` has 1 missing case +//│ ╟── `x` has 1 missing case //│ ║ l.106: Tru and x is Fals then true //│ ║ ^ -//│ ╟── It can be module `Tru` +//│ ╟── it can be module `Tru` //│ ║ l.107: Fals and x is Tru then false //│ ╙── ^^^ -//│ ╔══[ERROR] When scrutinee `y` is `Fals` +//│ ╔══[ERROR] when `y` is `Fals` //│ ║ l.107: Fals and x is Tru then false //│ ║ ^^^^ -//│ ╟── Scrutinee `x` has 1 missing case +//│ ╟── `x` has 1 missing case //│ ║ l.107: Fals and x is Tru then false //│ ║ ^ -//│ ╟── It can be module `Fals` +//│ ╟── it can be module `Fals` //│ ║ l.106: Tru and x is Fals then true //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: diff --git a/shared/src/test/diff/ucs/Exhaustiveness.mls b/shared/src/test/diff/ucs/Exhaustiveness.mls index 45abe460..1bf9bd51 100644 --- a/shared/src/test/diff/ucs/Exhaustiveness.mls +++ b/shared/src/test/diff/ucs/Exhaustiveness.mls @@ -19,16 +19,16 @@ fun f(x, y) = y is B and x is A then 4 -//│ ╔══[ERROR] When scrutinee `y` is `B` +//│ ╔══[ERROR] when `y` is `B` //│ ║ l.19: y is B and //│ ║ ^ -//│ ╟── Scrutinee `x` has 2 missing cases +//│ ╟── `x` has 2 missing cases //│ ║ l.20: x is //│ ║ ^ -//│ ╟── It can be class `B` +//│ ╟── it can be class `B` //│ ║ l.17: B then 1 //│ ║ ^ -//│ ╟── It can be class `C` +//│ ╟── it can be class `C` //│ ║ l.18: C then 2 //│ ╙── ^ //│ fun f: (A, A | B) -> (0 | 1 | 2 | 4) diff --git a/shared/src/test/diff/ucs/Humiliation.mls b/shared/src/test/diff/ucs/Humiliation.mls index 9f258cdc..d61bdb2d 100644 --- a/shared/src/test/diff/ucs/Humiliation.mls +++ b/shared/src/test/diff/ucs/Humiliation.mls @@ -49,24 +49,24 @@ class O() fun foo(x) = if x is Pair(Z(), Z()) then "zeros" Pair(O(), O()) then "ones" -//│ ╔══[ERROR] When scrutinee `x` is `Pair`, +//│ ╔══[ERROR] when `x` is `Pair`, and //│ ║ l.50: Pair(Z(), Z()) then "zeros" //│ ║ ^^^^ -//│ ╟── scrutinee `x$Pair_0` is `Z`, and +//│ ╟── first argument of `Pair` is `Z`, //│ ║ l.50: Pair(Z(), Z()) then "zeros" //│ ║ ^ -//│ ╟── Scrutinee `x$Pair_1` has 1 missing case -//│ ╟── It can be class `O` +//│ ╟── second argument of `Pair` has 1 missing case +//│ ╟── it can be class `O` //│ ║ l.51: Pair(O(), O()) then "ones" //│ ╙── ^ -//│ ╔══[ERROR] When scrutinee `x` is `Pair`, +//│ ╔══[ERROR] when `x` is `Pair`, and //│ ║ l.50: Pair(Z(), Z()) then "zeros" //│ ║ ^^^^ -//│ ╟── scrutinee `x$Pair_0` is `O`, and +//│ ╟── first argument of `Pair` is `O`, //│ ║ l.51: Pair(O(), O()) then "ones" //│ ║ ^ -//│ ╟── Scrutinee `x$Pair_1` has 1 missing case -//│ ╟── It can be class `Z` +//│ ╟── second argument of `Pair` has 1 missing case +//│ ╟── it can be class `Z` //│ ║ l.50: Pair(Z(), Z()) then "zeros" //│ ╙── ^ //│ fun foo: Pair[O | Z, nothing] -> ("ones" | "zeros") @@ -76,11 +76,11 @@ fun foo(x) = if x is fun foo(x) = if x is [Z(), Z()] then "zeros" [O(), O()] then "ones" -//│ ╔══[ERROR] When scrutinee `x$Tuple$2_0` is `O` +//│ ╔══[ERROR] when `x$Tuple$2_0` is `O` //│ ║ l.78: [O(), O()] then "ones" //│ ║ ^ -//│ ╟── Scrutinee `x$Tuple$2_1` has 1 missing case -//│ ╟── It can be class `Z` +//│ ╟── `x$Tuple$2_1` has 1 missing case +//│ ╟── it can be class `Z` //│ ║ l.77: [Z(), Z()] then "zeros" //│ ╙── ^ //│ fun foo: forall 'a. {0: O | Z, 1: O & 'a} -> ("ones" | "zeros" | 'a) @@ -136,11 +136,11 @@ fun foo(x) = if x is Pair(Z(), Z()) then "zeros" Pair(O(), O()) then "ones" Pair(y, O()) then x -//│ ╔══[ERROR] When scrutinee `x` is `Pair` +//│ ╔══[ERROR] when `x` is `Pair` //│ ║ l.136: Pair(Z(), Z()) then "zeros" //│ ║ ^^^^ -//│ ╟── Scrutinee `x$Pair_1` has 1 missing case -//│ ╟── It can be class `Z` +//│ ╟── second argument of `Pair` has 1 missing case +//│ ╟── it can be class `Z` //│ ║ l.136: Pair(Z(), Z()) then "zeros" //│ ╙── ^ //│ fun foo: forall 'B 'A. Pair['A, O & 'B] -> ("ones" | "zeros" | Pair['A, 'B] | 'A) diff --git a/shared/src/test/diff/ucs/InterleavedLet.mls b/shared/src/test/diff/ucs/InterleavedLet.mls index b17f8fd4..13830824 100644 --- a/shared/src/test/diff/ucs/InterleavedLet.mls +++ b/shared/src/test/diff/ucs/InterleavedLet.mls @@ -39,10 +39,10 @@ fun p(x, y) = x is Some and y is Some then 0 //│ ╔══[ERROR] unexpected empty split found //│ ╙── -//│ ╔══[ERROR] Scrutinee `y` has 1 missing case +//│ ╔══[ERROR] `y` has 1 missing case //│ ║ l.38: y is Some and x is Some then 1 //│ ║ ^ -//│ ╟── It can be module `None` +//│ ╟── it can be module `None` //│ ║ l.37: x is Some and y is None then 0 //│ ╙── ^^^^ //│ fun p: (Object & ~#Some | Some[anything], Some[anything]) -> (0 | 1) diff --git a/shared/src/test/diff/ucs/SimpleUCS.mls b/shared/src/test/diff/ucs/SimpleUCS.mls index 4753c4ad..90291d2f 100644 --- a/shared/src/test/diff/ucs/SimpleUCS.mls +++ b/shared/src/test/diff/ucs/SimpleUCS.mls @@ -20,40 +20,40 @@ fun f(x, y) = Left(xv) and y is Left(yv) then xv + yv Right(xv) and y is Right(yv) then xv * yv None and y is None then 0 -//│ ╔══[ERROR] When scrutinee `x` is `Left` +//│ ╔══[ERROR] when `x` is `Left` //│ ║ l.20: Left(xv) and y is Left(yv) then xv + yv //│ ║ ^^^^ -//│ ╟── Scrutinee `y` has 2 missing cases +//│ ╟── `y` has 2 missing cases //│ ║ l.20: Left(xv) and y is Left(yv) then xv + yv //│ ║ ^ -//│ ╟── It can be module `None` +//│ ╟── it can be module `None` //│ ║ l.22: None and y is None then 0 //│ ║ ^^^^ -//│ ╟── It can be class `Right` +//│ ╟── it can be class `Right` //│ ║ l.21: Right(xv) and y is Right(yv) then xv * yv //│ ╙── ^^^^^ -//│ ╔══[ERROR] When scrutinee `x` is `Right` +//│ ╔══[ERROR] when `x` is `Right` //│ ║ l.21: Right(xv) and y is Right(yv) then xv * yv //│ ║ ^^^^^ -//│ ╟── Scrutinee `y` has 2 missing cases +//│ ╟── `y` has 2 missing cases //│ ║ l.21: Right(xv) and y is Right(yv) then xv * yv //│ ║ ^ -//│ ╟── It can be class `Left` +//│ ╟── it can be class `Left` //│ ║ l.20: Left(xv) and y is Left(yv) then xv + yv //│ ║ ^^^^ -//│ ╟── It can be module `None` +//│ ╟── it can be module `None` //│ ║ l.22: None and y is None then 0 //│ ╙── ^^^^ -//│ ╔══[ERROR] When scrutinee `x` is `None` +//│ ╔══[ERROR] when `x` is `None` //│ ║ l.22: None and y is None then 0 //│ ║ ^^^^ -//│ ╟── Scrutinee `y` has 2 missing cases +//│ ╟── `y` has 2 missing cases //│ ║ l.22: None and y is None then 0 //│ ║ ^ -//│ ╟── It can be class `Left` +//│ ╟── it can be class `Left` //│ ║ l.20: Left(xv) and y is Left(yv) then xv + yv //│ ║ ^^^^ -//│ ╟── It can be class `Right` +//│ ╟── it can be class `Right` //│ ║ l.21: Right(xv) and y is Right(yv) then xv * yv //│ ╙── ^^^^^ //│ fun f: (Left[Int] | None | Right[Int], nothing) -> Int @@ -114,22 +114,22 @@ fun f(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv None and y is None then 0 -//│ ╔══[ERROR] When scrutinee `x` is `Some` +//│ ╔══[ERROR] when `x` is `Some` //│ ║ l.115: Some(xv) and y is Some(yv) then xv + yv //│ ║ ^^^^ -//│ ╟── Scrutinee `y` has 1 missing case +//│ ╟── `y` has 1 missing case //│ ║ l.115: Some(xv) and y is Some(yv) then xv + yv //│ ║ ^ -//│ ╟── It can be module `None` +//│ ╟── it can be module `None` //│ ║ l.116: None and y is None then 0 //│ ╙── ^^^^ -//│ ╔══[ERROR] When scrutinee `x` is `None` +//│ ╔══[ERROR] when `x` is `None` //│ ║ l.116: None and y is None then 0 //│ ║ ^^^^ -//│ ╟── Scrutinee `y` has 1 missing case +//│ ╟── `y` has 1 missing case //│ ║ l.116: None and y is None then 0 //│ ║ ^ -//│ ╟── It can be class `Some` +//│ ╟── it can be class `Some` //│ ║ l.115: Some(xv) and y is Some(yv) then xv + yv //│ ╙── ^^^^ //│ fun f: (None | Some[Int], nothing) -> Int @@ -142,13 +142,13 @@ fun f(x, y) = None then xv * 2 None and y is Some(yv) then yv * 3 -//│ ╔══[ERROR] When scrutinee `x` is `None` +//│ ╔══[ERROR] when `x` is `None` //│ ║ l.143: None and y is //│ ║ ^^^^ -//│ ╟── Scrutinee `y` has 1 missing case +//│ ╟── `y` has 1 missing case //│ ║ l.143: None and y is //│ ║ ^ -//│ ╟── It can be module `None` +//│ ╟── it can be module `None` //│ ║ l.142: None then xv * 2 //│ ╙── ^^^^ //│ fun f: (None | Some[Int], Some[Int]) -> Int From 318d6ebe5e857dcae6a0837aea9580f9be26dbe2 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 17 Jan 2024 02:04:25 +0800 Subject: [PATCH 082/143] Warn about the use of type parameters in patterns --- .../mlscript/ucs/stages/Transformation.scala | 5 ++++- shared/src/test/diff/gadt/Exp1.mls | 20 ++++++------------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index 26f4ab0c..fcc7ce9f 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -2,7 +2,7 @@ package mlscript.ucs.stages import mlscript.ucs, ucs.Desugarer, ucs.syntax.source._ import mlscript.{If, IfBody, IfBlock, IfElse, IfLet, IfOpApp, IfOpsApp, IfThen} -import mlscript.{Blk, Term, Var, App, Tup, Lit, Fld, Loc, NuFunDef, PlainTup} +import mlscript.{Blk, Term, Var, App, Tup, Lit, Fld, Loc, NuFunDef, TyApp, PlainTup} import mlscript.pretyper.Traceable import mlscript.Message, Message._ import mlscript.utils._, shorthands._ @@ -192,6 +192,9 @@ trait Transformation { self: Desugarer with Traceable => raiseDesugaringError(msg"only class patterns can be refined" -> p.toLoc) p } + case App(TyApp(classNme @ Var(_), targs), parameters: Tup) => + raiseDesugaringWarning(msg"type parameters in patterns are currently ignored" -> Loc(targs)) + ClassPattern(classNme, S(transformTupleTerm(parameters)), refined = false) case App(classNme @ Var(_), parameters: Tup) => ClassPattern(classNme, S(transformTupleTerm(parameters)), refined = false) case tuple: Tup => TuplePattern(transformTupleTerm(tuple)) diff --git a/shared/src/test/diff/gadt/Exp1.mls b/shared/src/test/diff/gadt/Exp1.mls index 8daa75e1..43b223d3 100644 --- a/shared/src/test/diff/gadt/Exp1.mls +++ b/shared/src/test/diff/gadt/Exp1.mls @@ -30,21 +30,13 @@ fun f(e) = if e is //│ = [Function: res] -:e // TODO support +:w fun f(e) = if e is Pair['a, 'b](l, r) then [l, r] -//│ ╔══[ERROR] unknown pattern Pair‹'a, 'b›(l, r,) -//│ ║ l.35: Pair['a, 'b](l, r) then [l, r] -//│ ╙── ^^^^^^^^^^^^^^^^^^ -//│ ╔══[ERROR] identifier not found: l -//│ ║ l.35: Pair['a, 'b](l, r) then [l, r] -//│ ╙── ^ -//│ ╔══[ERROR] identifier not found: r +//│ ╔══[WARNING] type parameters in patterns are currently ignored //│ ║ l.35: Pair['a, 'b](l, r) then [l, r] -//│ ╙── ^ -//│ fun f: anything -> [error, error] -//│ Code generation encountered an error: -//│ unresolved symbol l +//│ ╙── ^^^^^^ +//│ fun f: forall 'a 'b. Pair['a, 'b] -> ['a, 'b] :e // TODO support @@ -53,10 +45,10 @@ fun f(e) = if e is let f(x: a) = x f(l) //│ ╔══[ERROR] type identifier not found: a -//│ ║ l.53: let f(x: a) = x +//│ ║ l.45: let f(x: a) = x //│ ╙── ^ //│ ╔══[ERROR] identifier not found: l -//│ ║ l.54: f(l) +//│ ║ l.46: f(l) //│ ╙── ^ //│ fun f: Pair[anything, anything] -> error //│ Code generation encountered an error: From f266f3af8599fb5cc00fc1b3c5fb6c794a15065b Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 17 Jan 2024 02:25:27 +0800 Subject: [PATCH 083/143] Document functions in `LinesOps` --- .../src/main/scala/mlscript/ucs/package.scala | 53 ++++++++++++++----- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/package.scala b/shared/src/main/scala/mlscript/ucs/package.scala index dd2ddf92..5976667f 100644 --- a/shared/src/main/scala/mlscript/ucs/package.scala +++ b/shared/src/main/scala/mlscript/ucs/package.scala @@ -19,27 +19,54 @@ package object ucs { type Lines = Ls[(Int, Str)] implicit class LinesOps(private val lines: Lines) extends AnyVal { - def indent: Lines = { - @tailrec - def rec(acc: Lines, lines: Lines): Lines = lines match { - case (n, line) :: tail => rec((n + 1, line) :: acc, tail) - case Nil => acc.reverse - } - rec(Nil, lines) - } + /** Increase the indentation of all lines by one. */ + def indent: Lines = lines.map { case (n, line) => (n + 1, line) } + + /** + * Prepend a new line and indent the remaining lines. When you want to add + * a "title" to several lines and indent them, you should use this function. + * + * Suppose we have the following `Lines` representing case branches. + * ``` + * A -> 0 + * B -> 0 + * ``` + * We can prepend string `case x of` to lines and get the following result. + * ``` + * case x of + * A -> 0 + * B -> 0 + * ``` + */ def ##:(prefix: Str): Lines = (0, prefix) :: lines.indent + + /** + * If the first line does not have indentation and the remaining lines are + * indented, prepend the given string to the first line. Otherwise, prepend + * the given string to the first line and indent all remaining lines. + * + * When you want to amend the title of lines, you should use this function. + */ def #:(prefix: Str): Lines = { lines match { case (0, line) :: lines if lines.forall(_._1 > 0) => (0, s"$prefix $line") :: lines case lines => (0, prefix) :: lines.indent } } - def @:(prefix: Str): Lines = { - lines match { - case (_, line) :: Nil => (0, prefix + " " + line) :: Nil - case lines => (0, prefix) :: lines.indent - } + /** + * If there is only one line, prepend the given string to the beginning of + * this line. Otherwise, use the given string as the first line and indent + * the remaining lines. + * + * Similar to `##:`, except this function does not indent if there is only + * one line. + */ + def @:(prefix: Str): Lines = lines match { + case (_, line) :: Nil => (0, prefix + " " + line) :: Nil + case lines => (0, prefix) :: lines.indent } + + /** Make a multi-line string. */ def toIndentedString: Str = lines.iterator.map { case (n, line) => " " * n + line }.mkString("\n") } From 7d0e1f45cda4e69938d0e4e682d592a7890ce8dc Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 17 Jan 2024 02:41:24 +0800 Subject: [PATCH 084/143] Support `as` patterns --- .../mlscript/ucs/stages/Desugaring.scala | 9 ++-- .../mlscript/ucs/stages/Transformation.scala | 7 ++++ shared/src/test/diff/nu/CaseExpr.mls | 42 ++++++++----------- .../pretyper/ucs/examples/LispInterpreter.mls | 7 ++-- 4 files changed, 32 insertions(+), 33 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index d82a2f18..db238f49 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -306,10 +306,10 @@ trait Desugaring { self: PreTyper => def desugarRight(implicit scope: Scope) = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope, context) def desugarTail(implicit scope: Scope) = rec(scrutineeVar, tail) - head.pattern match { - case pattern @ s.AliasPattern(_, _) => - raiseDesugaringError(msg"alias pattern is not supported for now" -> pattern.toLoc) - desugarTail + def desugarPatternBranch(pattern: s.Pattern): c.Split = pattern match { + case pattern @ s.AliasPattern(aliasVar, nestedPattern) => + scrutinee.addAliasVar(aliasVar.withFreshSymbol) + c.Split.Let(false, aliasVar, scrutineeVar, desugarPatternBranch(nestedPattern)) case s.LiteralPattern(literal) => scrutinee.getOrCreateLiteralPattern(literal).addLocation(literal) c.Branch(scrutineeVar, c.Pattern.Literal(literal), desugarRight) :: desugarTail @@ -342,6 +342,7 @@ trait Desugaring { self: PreTyper => raiseDesugaringError(msg"record pattern is not supported for now" -> pattern.toLoc) desugarTail } + desugarPatternBranch(head.pattern) case s.Split.Let(isRec, nme, rhs, tail) => c.Split.Let(isRec, nme, rhs, rec(scrutineeVar, tail)(scope + nme.withFreshSymbol.symbol)) // <-- Weird use. case s.Split.Else(default) => c.Split.Else(default) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index fcc7ce9f..98b5a5e8 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -192,6 +192,13 @@ trait Transformation { self: Desugarer with Traceable => raiseDesugaringError(msg"only class patterns can be refined" -> p.toLoc) p } + case App(Var("as"), PlainTup(pattern, alias)) => + alias match { + case nme @ Var(_) => AliasPattern(nme, transformPattern(pattern)) + case other => + raiseDesugaringError(msg"the pattern alias must be a variable" -> other.toLoc) + transformPattern(pattern) + } case App(TyApp(classNme @ Var(_), targs), parameters: Tup) => raiseDesugaringWarning(msg"type parameters in patterns are currently ignored" -> Loc(targs)) ClassPattern(classNme, S(transformTupleTerm(parameters)), refined = false) diff --git a/shared/src/test/diff/nu/CaseExpr.mls b/shared/src/test/diff/nu/CaseExpr.mls index cc358e0f..35d31e67 100644 --- a/shared/src/test/diff/nu/CaseExpr.mls +++ b/shared/src/test/diff/nu/CaseExpr.mls @@ -59,28 +59,20 @@ map(succ) of None //│ = None { class: [class None extends Option] } -:e // TODO support + fun map(f) = case Some(x) then Some(f(x)) None as n then n -//│ ╔══[ERROR] type identifier `as` not found -//│ ║ l.65: None as n then n -//│ ╙── ^^ -//│ ╔══[ERROR] type identifier not found: as -//│ ║ l.65: None as n then n -//│ ╙── ^^ -//│ fun map: forall 'a 'A. ('a -> 'A) -> Some['a] -> (Some['A] | error) -//│ Code generation encountered an error: -//│ unresolved symbol as +//│ fun map: forall 'a 'A. ('a -> 'A) -> (None | Some['a]) -> (None | Some['A]) :pe case 1 //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'case'; found integer literal instead -//│ ║ l.78: case 1 +//│ ║ l.70: case 1 //│ ║ ^ //│ ╟── Note: 'case' expression starts here: -//│ ║ l.78: case 1 +//│ ║ l.70: case 1 //│ ╙── ^^^^ //│ anything -> 1 //│ res @@ -89,13 +81,13 @@ case 1 :pe case (1 then true) //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here -//│ ║ l.90: case (1 then true) +//│ ║ l.82: case (1 then true) //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'case'; found integer literal instead -//│ ║ l.90: case (1 then true) +//│ ║ l.82: case (1 then true) //│ ║ ^^^^^^^^^^^^^ //│ ╟── Note: 'case' expression starts here: -//│ ║ l.90: case (1 then true) +//│ ║ l.82: case (1 then true) //│ ╙── ^^^^ //│ anything -> 1 //│ res @@ -109,16 +101,16 @@ case else 0 :pe case then 1 else 0 //│ ╔══[PARSE ERROR] Unexpected 'then' keyword in expression position -//│ ║ l.110: case then 1 else 0 +//│ ║ l.102: case then 1 else 0 //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'case'; found integer literal instead -//│ ║ l.110: case then 1 else 0 +//│ ║ l.102: case then 1 else 0 //│ ║ ^ //│ ╟── Note: 'case' expression starts here: -//│ ║ l.110: case then 1 else 0 +//│ ║ l.102: case then 1 else 0 //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected end of input; found 'else' keyword instead -//│ ║ l.110: case then 1 else 0 +//│ ║ l.102: case then 1 else 0 //│ ╙── ^^^^ //│ anything -> 1 //│ res @@ -132,16 +124,16 @@ case then 1 else 0 :e case x, y then x + y //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead -//│ ║ l.133: case x, y then x + y +//│ ║ l.125: case x, y then x + y //│ ╙── ^^^^^^^^^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'case'; found operator application instead -//│ ║ l.133: case x, y then x + y +//│ ║ l.125: case x, y then x + y //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── Note: 'case' expression starts here: -//│ ║ l.133: case x, y then x + y +//│ ║ l.125: case x, y then x + y //│ ╙── ^^^^ //│ ╔══[ERROR] identifier not found: x -//│ ║ l.133: case x, y then x + y +//│ ║ l.125: case x, y then x + y //│ ╙── ^ //│ anything -> () //│ Code generation encountered an error: @@ -150,10 +142,10 @@ case x, y then x + y :e case (x, y) then x + y //│ ╔══[ERROR] type identifier `,` not found -//│ ║ l.151: case (x, y) then x + y +//│ ║ l.143: case (x, y) then x + y //│ ╙── ^^^^ //│ ╔══[ERROR] type identifier not found: , -//│ ║ l.151: case (x, y) then x + y +//│ ║ l.143: case (x, y) then x + y //│ ╙── ^^^^ //│ nothing -> error //│ Code generation encountered an error: diff --git a/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls b/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls index 9ac35c30..9d965bfd 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls @@ -459,8 +459,8 @@ fun asList(x: Data): List[Data] = if x is DataList(ys) then ys else error //│ fun asList: (x: Data) -> List[Data] fun eval(x: Data, env: Str -> Data): Data = if x is - Literal(StrLit(x)) then Literal(StrLit(x)) - Literal(IntLit(x)) then Literal(IntLit(x)) + Literal(StrLit(_)) as lit then lit + Literal(IntLit(_)) as lit then lit Symbol(name) then env(name) DataList(Cons(Symbol("val"), tail)) and tail is Cons(param, Cons(expr, Cons(rest, Nil))) then @@ -477,8 +477,7 @@ fun eval(x: Data, env: Str -> Data): Data = if x is let ps = map(toName, asList(params)) Literal(Lambda(args => eval(body, extendParameters(env, ps, args)))) DataList(Cons(operator, operands)) and eval(operator, env) is - Literal(Lambda(f)) then - f of map((x) => eval(x, env), operands) + Literal(Lambda(f)) then f of map((x) => eval(x, env), operands) else error // application of a non-function else error // unrecognized program //│ fun eval: (x: Data, env: Str -> Data) -> Data From 5a9eb6d28ab28e104b2b69afed544ee82c0fad08 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Thu, 18 Jan 2024 22:38:33 +0800 Subject: [PATCH 085/143] Update shared/src/test/diff/pretyper/ucs/DualOption.mls Co-authored-by: Fa1sePRoMiSe --- shared/src/test/diff/pretyper/ucs/DualOption.mls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/src/test/diff/pretyper/ucs/DualOption.mls b/shared/src/test/diff/pretyper/ucs/DualOption.mls index ab4d61d9..52a9ffa3 100644 --- a/shared/src/test/diff/pretyper/ucs/DualOption.mls +++ b/shared/src/test/diff/pretyper/ucs/DualOption.mls @@ -1,5 +1,5 @@ :NewDefs -:NewDefs + abstract class Option[T] class Some[T](value: T) extends Option[T] From a6625feb367a12ca6bee8335d504b215517642ee Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Fri, 19 Jan 2024 22:32:01 +0800 Subject: [PATCH 086/143] Improve coverage checking and rename `PatternInfo` as `Pattern` --- .../main/scala/mlscript/pretyper/Symbol.scala | 3 +- .../scala/mlscript/ucs/context/CaseSet.scala | 96 +++++++------- .../{PatternInfo.scala => Pattern.scala} | 38 ++++-- .../mlscript/ucs/context/Scrutinee.scala | 58 ++++----- .../scala/mlscript/ucs/context/package.scala | 11 +- .../ucs/stages/CoverageChecking.scala | 65 ++++++---- .../mlscript/ucs/stages/Normalization.scala | 4 +- .../mlscript/ucs/stages/PostProcessing.scala | 16 +-- shared/src/test/diff/nu/LitMatch.mls | 9 +- .../pretyper/ucs/SpecilizationCollision.mls | 49 +++++++ .../pretyper/ucs/coverage/SealedClasses.mls | 10 -- .../diff/pretyper/ucs/patterns/Literals.mls | 11 +- .../pretyper/ucs/stages/PostProcessing.mls | 4 - shared/src/test/diff/ucs/ElseIf.mls | 47 ++++--- shared/src/test/diff/ucs/Exhaustiveness.mls | 3 + shared/src/test/diff/ucs/SimpleUCS.mls | 29 +++-- shared/src/test/diff/ucs/SplitAfterOp.mls | 120 ++++++++++-------- shared/src/test/diff/ucs/WeirdIf.mls | 2 - .../src/test/scala/mlscript/DiffTests.scala | 13 +- 19 files changed, 337 insertions(+), 251 deletions(-) rename shared/src/main/scala/mlscript/ucs/context/{PatternInfo.scala => Pattern.scala} (76%) diff --git a/shared/src/main/scala/mlscript/pretyper/Symbol.scala b/shared/src/main/scala/mlscript/pretyper/Symbol.scala index 5a774d0a..9ef593ee 100644 --- a/shared/src/main/scala/mlscript/pretyper/Symbol.scala +++ b/shared/src/main/scala/mlscript/pretyper/Symbol.scala @@ -28,7 +28,8 @@ package object symbol { var baseTypes: Ls[TypeSymbol] = Nil var sealedDerivedTypes: Ls[TypeSymbol] = Nil - @inline def hasSuperType(superType: TypeSymbol): Bool = baseTypes.exists(_ === superType) + @inline def hasBaseClass(baseClassLikeSymbol: TypeSymbol): Bool = + baseTypes.exists(_ === baseClassLikeSymbol) def showDbg: Str = s"${defn.kind.str} $name" } diff --git a/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala b/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala index b31f0928..aff9d24e 100644 --- a/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala +++ b/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala @@ -1,40 +1,37 @@ package mlscript.ucs.context -import mlscript.{Lit, Loc} +import mlscript.{Lit, Loc, Var} import mlscript.pretyper.symbol.TypeSymbol import mlscript.utils._, shorthands._ import mlscript.pretyper.symbol.DummyClassSymbol -sealed abstract class Pattern { - override def toString(): String = this match { - case Pattern.ClassLike(symbol) => - val kind = symbol match { - case _: DummyClassSymbol => "dummy class" - case _ => symbol.defn.kind.str - } - s"$kind `${symbol.name}`" - case Pattern.Tuple() => "tuple" - case Pattern.Literal(literal) => s"literal $literal" - } -} - -object Pattern { - final case class ClassLike(symbol: TypeSymbol) extends Pattern - // Currently, there is only simple tuple pattern, so we cannot differentiate - // between tuple patterns of different arity. That's why the parameter list - // is empty for now. - final case class Tuple() extends Pattern - final case class Literal(literal: Lit) extends Pattern -} - /** * A `CaseSet` represents all patterns that a particular scrutinee is - * being matched with within a UCS expression. Each Pattern is associated - * with the locations where these patterns appear. + * being matched with within a UCS expression. * - * @param patterns a set of patterns that the scrutinee is matched with. + * @param patterns a list of patterns. */ -final case class CaseSet(val cases: Map[Pattern, Ls[Loc]]) { +final case class CaseSet(val patterns: List[Pattern]) { + def showInDiagnostics: Str = + patterns.iterator.map(_.showInDiagnostics).mkString("[", ", ", "]") + + /** Get a iterator of all class-like patterns. */ + def classLikePatterns: Iterator[Pattern.ClassLike] = patterns.iterator.flatMap { + case pattern: Pattern.ClassLike => S(pattern) + case _: Pattern.Boolean | _: Pattern.Literal | _: Pattern.Tuple => N + } + + /** Separate a class-like pattern if it appears in `patterns`. */ + def separate(classLikeSymbol: TypeSymbol): Opt[(Pattern.ClassLike, Ls[Pattern.ClassLike])] = { + classLikePatterns.foldRight[(Opt[Pattern.ClassLike], Ls[Pattern.ClassLike])]((N, Nil)) { + case (pattern, (S(separated), rest)) => (S(separated), pattern :: rest) + case (pattern, (N, rest)) if pattern.classLikeSymbol === classLikeSymbol => (S(pattern), rest) + case (pattern, (N, rest)) => (N, pattern :: rest) + } match { + case (N, _) => N + case (S(separated), rest) => S((separated, rest)) + } + } /** * Split the pattern set into two pattern sets. * @@ -59,37 +56,30 @@ final case class CaseSet(val cases: Map[Pattern, Ls[Loc]]) { * locations where the pattern appears, the related patterns, and * unrelated patterns. */ - def split(classLikeSymbol: TypeSymbol): Opt[(Ls[Loc], CaseSet, CaseSet)] = { - val classLikePattern = Pattern.ClassLike(classLikeSymbol) - cases.get(classLikePattern).map { locations => - val withoutSymbol = cases - classLikePattern - val relatedPatterns = withoutSymbol.filter { - case (Pattern.ClassLike(otherSymbol), _) => otherSymbol.baseTypes.contains(classLikeSymbol) - case ((_: Pattern.Tuple | _: Pattern.Literal), _) => false - } ++ classLikeSymbol.sealedDerivedTypes.iterator.map { symbol => - Pattern.ClassLike(symbol) -> symbol.defn.nme.toLoc.toList - } - val unrelatedPatterns = withoutSymbol.filter { - case (Pattern.ClassLike(otherSymbol), _) => !otherSymbol.baseTypes.contains(classLikeSymbol) - case ((_: Pattern.Tuple | _: Pattern.Literal), _) => true - } - (locations, copy(relatedPatterns), copy(unrelatedPatterns)) + def split(classLikeSymbol: TypeSymbol): Opt[(Pattern.ClassLike, CaseSet, CaseSet)] = { + separate(classLikeSymbol) match { + case N => N + case S((pattern, patterns)) => + val (unrelated, related) = patterns.partitionMap { pattern => + if (pattern.classLikeSymbol hasBaseClass classLikeSymbol) { + R(pattern) + } else { + L(pattern) + } + } + S((pattern, CaseSet(related), CaseSet(unrelated))) } } - def remove(literal: Lit): CaseSet = { - val literalPattern = Pattern.Literal(literal) - copy(cases - literalPattern) + @inline def remove(boolLit: Var): CaseSet = { + require(boolLit.name === "true" || boolLit.name === "false") + CaseSet(patterns.filter(!_.matches(boolLit))) } - /** Get an iterator of only patterns. */ - @inline def patterns: Iterator[Pattern] = cases.iterator.map(_._1) - - @inline def isEmpty: Bool = cases.isEmpty + @inline def remove(literal: Lit): CaseSet = + CaseSet(patterns.filter(!_.matches(literal))) - @inline def size: Int = cases.size -} + @inline def isEmpty: Bool = patterns.isEmpty -object CaseSet { - def empty: CaseSet = CaseSet(Map.empty) + @inline def size: Int = patterns.size } diff --git a/shared/src/main/scala/mlscript/ucs/context/PatternInfo.scala b/shared/src/main/scala/mlscript/ucs/context/Pattern.scala similarity index 76% rename from shared/src/main/scala/mlscript/ucs/context/PatternInfo.scala rename to shared/src/main/scala/mlscript/ucs/context/Pattern.scala index 5d232e55..5e209e0b 100644 --- a/shared/src/main/scala/mlscript/ucs/context/PatternInfo.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Pattern.scala @@ -4,8 +4,9 @@ import collection.mutable.{Buffer, SortedMap => MutSortedMap} import mlscript.{Lit, Loc, Located, SimpleTerm, TypeName, Var} import mlscript.pretyper.symbol.TypeSymbol import mlscript.utils._, shorthands._ +import mlscript.pretyper.symbol.DummyClassSymbol -abstract class PatternInfo { +sealed abstract class Pattern { private val locationsBuffer: Buffer[Loc] = Buffer.empty def addLocation(located: Located): Unit = located.getLoc.foreach(locationsBuffer += _) @@ -21,6 +22,9 @@ abstract class PatternInfo { def showDbg: Str + /** Get a string suitable for diagnostics. */ + def showInDiagnostics: Str + /** * Checks if the pattern is same as expressed by the given `SimpleTerm`. Note * that we should pass `pat` of `Case` to this function. @@ -31,8 +35,8 @@ abstract class PatternInfo { def toCasePattern: SimpleTerm } -object PatternInfo { - class ClassLike(val classLikeSymbol: TypeSymbol, scrutinee: Scrutinee) extends PatternInfo { +object Pattern { + final case class ClassLike(val classLikeSymbol: TypeSymbol, scrutinee: Scrutinee) extends Pattern { private var unappliedVarOpt: Opt[Var] = N private val parameters: MutSortedMap[Int, Scrutinee] = MutSortedMap.empty @@ -62,11 +66,16 @@ object PatternInfo { override def showDbg: Str = s"${classLikeSymbol.name}" + override def showInDiagnostics: Str = s"${(classLikeSymbol match { + case dummySymbol: DummyClassSymbol => "class" + case otherSymbol: TypeSymbol => otherSymbol.defn.kind.str + })} `${classLikeSymbol.name}`" + override def matches(pat: SimpleTerm): Bool = pat match { case pat: Var => pat.symbolOption match { case S(patternSymbol: TypeSymbol) => - patternSymbol === classLikeSymbol || patternSymbol.hasSuperType(classLikeSymbol) + patternSymbol === classLikeSymbol || patternSymbol.hasBaseClass(classLikeSymbol) case S(_) | N => false } case _: Lit => false @@ -76,7 +85,7 @@ object PatternInfo { Var(classLikeSymbol.name).withLoc(firstOccurrence).withSymbol(classLikeSymbol) } - class Tuple(scrutinee: Scrutinee) extends PatternInfo { + final case class Tuple(scrutinee: Scrutinee) extends Pattern { private val fields: MutSortedMap[Int, Scrutinee] = MutSortedMap.empty def getField(index: Int): Scrutinee = @@ -86,6 +95,13 @@ object PatternInfo { override def showDbg: Str = s"tuple#${arity.getOrElse("?")}" + override def showInDiagnostics: Str = + "tuple of " + (arity match { + case N => "certain number of elements" + case S(1) => "1 element" + case S(n) => s"${n} elements" + }) + override def matches(pat: SimpleTerm): Bool = false /** @@ -96,11 +112,13 @@ object PatternInfo { override def toCasePattern: SimpleTerm = ??? } - class Literal(val literal: Lit) extends PatternInfo { + final case class Literal(val literal: Lit) extends Pattern { override def arity: Opt[Int] = N override def showDbg: Str = literal.idStr + override def showInDiagnostics: Str = s"literal ${literal.idStr}" + override def matches(pat: SimpleTerm): Bool = pat match { case _: Var => false @@ -111,17 +129,19 @@ object PatternInfo { } /** - * This can be actually merged with `LiteralPatternInfo`. However, there's no + * This can be actually merged with `Pattern.Literal`. However, there's no * `Lit` sub-classes for Boolean types, so the representation is a little bit * awkward, also, it makes sense to consider Boolean patterns separately * because we can check the Boolean exhaustiveness with them. */ - class Boolean(val value: Var) extends PatternInfo { + final case class Boolean(val value: Var) extends Pattern { require(value.name === "true" || value.name === "false") override def arity: Opt[Int] = N - override def showDbg: Str = value.toString + override def showDbg: Str = value.name + + override def showInDiagnostics: Str = s"Boolean value ${value.name}" override def matches(pat: SimpleTerm): Bool = pat match { diff --git a/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala index e45dec19..05422bf2 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala @@ -15,22 +15,22 @@ class Scrutinee(val context: Context, parent: Opt[Scrutinee]) { private val locations: Buffer[Loc] = Buffer.empty private var generatedVarOpt: Opt[Var] = N - private val classLikePatterns: MutSortedMap[TypeSymbol, PatternInfo.ClassLike] = MutSortedMap.empty(classLikeSymbolOrdering) + private val classLikePatterns: MutSortedMap[TypeSymbol, Pattern.ClassLike] = MutSortedMap.empty(classLikeSymbolOrdering) // Currently we only support simple tuple patterns, so there is only _one_ // slot for tuple patterns. After we support complex tuple patterns, we need - // to extend this fields to a map from tuple arity to `PatternInfo.Tuple`. - // private val tuplePatterns: MutMap[Int, PatternInfo.Tuple] = MutMap.empty + // to extend this fields to a map from tuple arity to `Pattern.Tuple`. + // private val tuplePatterns: MutMap[Int, Pattern.Tuple] = MutMap.empty // If we support tuple pattern splice, we need a more expressive key in the // map's type. - private var tuplePatternOpt: Opt[PatternInfo.Tuple] = N + private var tuplePatternOpt: Opt[Pattern.Tuple] = N private var aliasVarSet: MutSortedSet[Var] = MutSortedSet.empty - private val literalPatterns: MutSortedMap[Lit, PatternInfo.Literal] = MutSortedMap.empty(literalOrdering) + private val literalPatterns: MutSortedMap[Lit, Pattern.Literal] = MutSortedMap.empty(literalOrdering) /** * The key should be either `Var("true")` or `Var("false")`. We want to keep * the type symbol of the variable so that it still work in following stages. */ - private val booleanPatterns: MutSortedMap[Var, PatternInfo.Boolean] = MutSortedMap.empty(varNameOrdering) + private val booleanPatterns: MutSortedMap[Var, Pattern.Boolean] = MutSortedMap.empty(varNameOrdering) def addAliasVar(alias: Var): Unit = aliasVarSet += alias @@ -39,23 +39,23 @@ class Scrutinee(val context: Context, parent: Opt[Scrutinee]) { def aliasesIterator: Iterator[Var] = aliasVarSet.iterator /** - * If there is already a `PatternInfo.ClassLike` for the given symbol, return it. - * Otherwise, create a new `PatternInfo.ClassLike` and return it. + * If there is already a `Pattern.ClassLike` for the given symbol, return it. + * Otherwise, create a new `Pattern.ClassLike` and return it. */ - def getOrCreateClassPattern(classLikeSymbol: TypeSymbol): PatternInfo.ClassLike = - classLikePatterns.getOrElseUpdate(classLikeSymbol, new PatternInfo.ClassLike(classLikeSymbol, this)) + def getOrCreateClassPattern(classLikeSymbol: TypeSymbol): Pattern.ClassLike = + classLikePatterns.getOrElseUpdate(classLikeSymbol, Pattern.ClassLike(classLikeSymbol, this)) /** * Get the class pattern but DO NOT create a new one if there isn't. This * function is mainly used in post-processing because we don't want to * accidentally create new patterns. */ - def getClassPattern(classLikeSymbol: TypeSymbol): Opt[PatternInfo.ClassLike] = + def getClassPattern(classLikeSymbol: TypeSymbol): Opt[Pattern.ClassLike] = classLikePatterns.get(classLikeSymbol) /** - * If there is already a `PatternInfo.Tuple`, return it. Otherwise, create a - * new `PatternInfo.Tuple` and return it. + * If there is already a `Pattern.Tuple`, return it. Otherwise, create a + * new `Pattern.Tuple` and return it. * * **NOTE**: There's only one slot for tuple patterns because we cannot * differentiate tuple types in underlying MLscript case terms. In the future, @@ -63,31 +63,31 @@ class Scrutinee(val context: Context, parent: Opt[Scrutinee]) { * a signature like this. * * ```scala - * def getOrCreateTuplePattern(dimension: TupleDimension): PatternInfo.Tuple + * def getOrCreateTuplePattern(dimension: TupleDimension): Pattern.Tuple * case class TupleDimension(knownArity: Int, hasSplice: Bool) * ``` */ - def getOrCreateTuplePattern: PatternInfo.Tuple = + def getOrCreateTuplePattern: Pattern.Tuple = tuplePatternOpt.getOrElse { - val tuplePattern = new PatternInfo.Tuple(this) + val tuplePattern = Pattern.Tuple(this) tuplePatternOpt = S(tuplePattern) tuplePattern } /** Get the tuple pattern and create a new one if there isn't. */ - def getOrCreateLiteralPattern(literal: Lit): PatternInfo.Literal = - literalPatterns.getOrElseUpdate(literal, new PatternInfo.Literal(literal)) + def getOrCreateLiteralPattern(literal: Lit): Pattern.Literal = + literalPatterns.getOrElseUpdate(literal, Pattern.Literal(literal)) /** * The key should be either `Var("true")` or `Var("false")`. We want to keep * the type symbol of the variable so that it still work in following stages. */ - def getOrCreateBooleanPattern(value: Var): PatternInfo.Boolean = - booleanPatterns.getOrElseUpdate(value, new PatternInfo.Boolean(value)) + def getOrCreateBooleanPattern(value: Var): Pattern.Boolean = + booleanPatterns.getOrElseUpdate(value, Pattern.Boolean(value)) - def classLikePatternsIterator: Iterator[PatternInfo.ClassLike] = classLikePatterns.valuesIterator + def classLikePatternsIterator: Iterator[Pattern.ClassLike] = classLikePatterns.valuesIterator - def patternsIterator: Iterator[PatternInfo] = + def patternsIterator: Iterator[Pattern] = classLikePatterns.valuesIterator ++ literalPatterns.valuesIterator ++ booleanPatterns.valuesIterator /** Get a list of string representation of patterns. Only for debugging. */ @@ -103,19 +103,7 @@ class Scrutinee(val context: Context, parent: Opt[Scrutinee]) { def freshSubScrutinee: Scrutinee = context.freshScrutinee(this) - def toCaseSet: CaseSet = { - import mlscript.ucs.context.Pattern - val cases = classLikePatterns.iterator.map { case (symbol, pattern) => - Pattern.ClassLike(symbol) -> pattern.locations - }.toMap[Pattern, Ls[Loc]] - val tuplePattern = tuplePatternOpt.map { tuplePattern => - Pattern.Tuple() -> tuplePattern.locations - }.toMap[Pattern, Ls[Loc]] - val literalPatterns = this.literalPatterns.iterator.map { case (literal, pattern) => - Pattern.Literal(literal) -> pattern.locations - }.toMap[Pattern, Ls[Loc]] - CaseSet(cases ++ tuplePattern) - } + def toCaseSet: CaseSet = CaseSet(patternsIterator.toList) def getReadableName(scrutineeVar: Var): Str = { parent match { diff --git a/shared/src/main/scala/mlscript/ucs/context/package.scala b/shared/src/main/scala/mlscript/ucs/context/package.scala index bc61762f..6b5d31d9 100644 --- a/shared/src/main/scala/mlscript/ucs/context/package.scala +++ b/shared/src/main/scala/mlscript/ucs/context/package.scala @@ -9,5 +9,14 @@ package object context { type MatchRegistry = Map[NamedScrutinee, CaseSet] - type SeenRegistry = Map[NamedScrutinee, (TypeSymbol, Ls[Loc], CaseSet)] + // implicit class MatchRegistryOps(val self: MatchRegistry) extends AnyVal { + // def showDbg: Str = + // } + + type SeenRegistry = Map[NamedScrutinee, (TypeSymbol, Ls[Loc], CaseSet)] + + implicit class SeenRegistryOps(val self: SeenRegistry) extends AnyVal { + def showDbg: Str = if (self.isEmpty) "empty" else + self.iterator.map { case ((k, _), (s, _, _)) => s"${k.name} is ${s.name}" }.mkString(", ") + } } diff --git a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala index 2ef80662..82f2e231 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala @@ -23,18 +23,22 @@ trait CoverageChecking { self: Desugarer with Traceable => pending: MatchRegistry, working: MatchRegistry, seen: SeenRegistry - )(implicit context: Context): Ls[Diagnostic] = - trace(s"checkCoverage <== ${term.showDbg}, ${pending.size} pending, ${working.size} working, ${seen.size} seen") { - println(s"seen: " + (if (seen.isEmpty) "empty" else - seen.iterator.map { case ((k, _), (s, _, _)) => s"${k.name} is ${s.name}" }.mkString(", ") - )) - term match { - case Let(_, _, _, body) => checkCoverage(body, pending, working, seen) - case CaseOf(scrutineeVar: Var, Case(Var("true"), body, NoCases)) if context.isTestVar(scrutineeVar) => - raiseDesugaringError(msg"missing else branch" -> body.toLoc) - Nil - case CaseOf(Scrutinee.WithVar(scrutinee, scrutineeVar), cases) => - println(s"scrutinee: ${scrutineeVar.name}") + )(implicit context: Context): Ls[Diagnostic] = { + term match { + case Let(_, nme, _, body) => + println(s"checkCoverage <== LET `${nme.name}`") + checkCoverage(body, pending, working, seen) + case CaseOf(scrutineeVar: Var, Case(Var("true"), body, NoCases)) if context.isTestVar(scrutineeVar) => + raiseDesugaringError(msg"missing else branch" -> body.toLoc) + Nil + case CaseOf(scrutineeVar: Var, Case(Var("true"), whenTrue, Wildcard(whenFalse))) if context.isTestVar(scrutineeVar) => + println(s"checkCoverage <== TEST `${scrutineeVar.name}`") + checkCoverage(whenTrue, pending, working, seen) ++ + checkCoverage(whenFalse, pending, working, seen) + case CaseOf(Scrutinee.WithVar(scrutinee, scrutineeVar), cases) => + trace(s"checkCoverage <== ${pending.size} pending, ${working.size} working, ${seen.size} seen") { + println(s"CASE ${scrutineeVar.name}") + println(s"SEEN: ${seen.showDbg}") // If the scrutinee is still pending (i.e., not matched yet), then we // remove it from the pending list. If the scrutinee is matched, and // there are still classes to be matched, then we find the remaining @@ -64,28 +68,35 @@ trait CoverageChecking { self: Desugarer with Traceable => // unseen pattern set. // Meanwhile, we keep adding diagnostics if we meet warnings and errors. cases.foldLeft((unseenPatterns, Nil: Ls[Diagnostic]))({ + case ((unseenPatterns, diagnostics), (boolLit: Var) -> body) + if boolLit.name === "true" || boolLit.name === "false" => + ( + unseenPatterns.remove(boolLit), + diagnostics ++ checkCoverage(body, newPending, working - namedScrutinee, seen) + ) case ((unseenPatterns, diagnostics), (className: Var) -> body) => val classSymbol = className.symbolOption.flatMap(_.typeSymbolOption).getOrElse { throw new Exception(s"$className is not associated with a type symbol") } - println(s"class symbol: ${classSymbol.name}") + println(s"class symbol: `${classSymbol.name}`") unseenPatterns.split(classSymbol) match { - case S((locations, refiningPatterns, remainingPatterns)) => - println(s"remove ${className} and it's unrelated patterns from working") - println("unseen patterns: " + unseenPatterns.patterns.mkString("[", ", ", "]")) - println("remaining patterns: " + remainingPatterns.patterns.mkString("[", ", ", "]")) + case S((pattern, refiningPatterns, remainingPatterns)) => + println(s"REMOVE `${className.name}` from working") + println(s"unseen: ${unseenPatterns.showInDiagnostics}") + println(s"remaining: ${remainingPatterns.showInDiagnostics}") // Remove the scrutinee from the working list. val newWorking = if (remainingPatterns.isEmpty) working - namedScrutinee else working.updated(namedScrutinee, remainingPatterns) // Add "`scrutinee` is `className`" to the seen registry. - val newSeen = seen + (namedScrutinee -> (classSymbol, locations, refiningPatterns)) + val newSeen = seen + (namedScrutinee -> (classSymbol, pattern.locations, refiningPatterns)) ( remainingPatterns, diagnostics ++ checkCoverage(body, newPending, newWorking, newSeen) ) case N => + println(s"cannot split the set by ${classSymbol.name}") unseenPatterns -> (diagnostics :+ (seen.get(namedScrutinee) match { case S((`classSymbol`, _, _)) => WarningReport("tautology", Nil, Diagnostic.Desugaring) case S(_) => ErrorReport("contradiction", Nil, Diagnostic.Desugaring) @@ -99,16 +110,18 @@ trait CoverageChecking { self: Desugarer with Traceable => ) }) { case ((missingCases, diagnostics), N) => - println("remaining cases should are not covered") - println("MISSING cases: " + missingCases.patterns.mkString("[", ", ", "]")) + println(s"remaining cases are not covered: ${missingCases.showInDiagnostics}") diagnostics ++ explainMissingCases(namedScrutinee, seen, missingCases) case ((remainingCases, diagnostics), S(default)) => println("remaining cases should be covered by the wildcard") checkCoverage(default, newPending, working.updated(namedScrutinee, remainingCases), seen) } - case other => println("STOP"); Nil - } - }(ls => s"checkCoverage ==> ${ls.length} diagnostics") + }(ls => s"checkCoverage ==> ${ls.length} diagnostics") + case other => + println(s"checkCoverage <== TERM ${other.showDbg}") + Nil + } + } } object CoverageChecking { @@ -124,8 +137,8 @@ object CoverageChecking { S(ErrorReport({ val readableName = scrutinee._2.getReadableName(scrutinee._1) val lines = (msg"$readableName has ${"missing case".pluralize(missingCases.size, true)}" -> scrutinee._1.toLoc) :: - (missingCases.cases.iterator.flatMap { case (pattern, locations) => - (msg"it can be ${pattern.toString}" -> locations.headOption) :: Nil + (missingCases.patterns.iterator.flatMap { pattern => + (msg"it can be ${pattern.showInDiagnostics}" -> pattern.firstOccurrence) :: Nil }.toList) if (seen.isEmpty) { lines @@ -144,6 +157,6 @@ object CoverageChecking { private def showRegistry(registry: MatchRegistry): Str = if (registry.isEmpty) "empty" else registry.iterator.map { case (scrutineeVar -> scrutinee, matchedClasses) => - matchedClasses.patterns.mkString(s">>> ${scrutineeVar.name} => [", ", ", "]") + matchedClasses.patterns.iterator.map(_.showInDiagnostics).mkString(s">>> ${scrutineeVar.name} => [", ", ", "]") }.mkString("\n", "\n", "") } diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 87af99b3..bbcee158 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -115,7 +115,7 @@ trait Normalization { self: Desugarer with Traceable => val (wrap, realTail) = preventShadowing(nme, tail) wrap(Let(false, nme, scrutinee, normalizeToTerm(continuation.fill(realTail, false, true)))) // Skip Boolean conditions as scrutinees, because they only appear once. - case Split.Cons(Branch(test, pattern @ Pattern.Class(nme @ Var("true"), _), continuation), tail) => + case Split.Cons(Branch(test, pattern @ Pattern.Class(nme @ Var("true"), _), continuation), tail) if context.isTestVar(test) => println(s"TRUE: ${test.name} is true") val trueBranch = normalizeToTerm(continuation.fill(tail, false, false)) val falseBranch = normalizeToCaseBranches(tail) @@ -217,7 +217,7 @@ trait Normalization { self: Desugarer with Traceable => ) } specialize(continuation, true) :++ specialize(tail, true) - } else if (otherClassSymbol hasSuperType classSymbol) { + } else if (otherClassSymbol hasBaseClass classSymbol) { println(s"Case 2: ${otherClassName.name} <: ${className.name}") println(s"${otherClassSymbol.name} is refining ${className.name}") // We should mark `pattern` as refined. diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index 9e26575c..dfb3485c 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -2,7 +2,7 @@ package mlscript.ucs.stages import mlscript.{Case, CaseBranches, CaseOf, Let, Lit, Loc, NoCases, Term, Var, Wildcard} import mlscript.ucs.Desugarer -import mlscript.ucs.context.{Context, PatternInfo, Scrutinee} +import mlscript.ucs.context.{Context, Pattern, Scrutinee} import mlscript.pretyper.symbol._ import mlscript.utils._, shorthands._ import mlscript.Message, Message.MessageContext @@ -27,11 +27,11 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => // Post-process the false branch. println("FALSE") // Get all patterns, except the current one `pat`. - val patternInfoList = scrutinee.patternsIterator.filter(!_.matches(pat)).toList - println(s"found ${patternInfoList.length} patterns to distentangle: ${patternInfoList.iterator.map(_.showDbg).mkString(", ")}") - val (default, cases) = patternInfoList + val patternList = scrutinee.patternsIterator.filter(!_.matches(pat)).toList + println(s"found ${patternList.length} patterns to distentangle: ${patternList.iterator.map(_.showDbg).mkString(", ")}") + val (default, cases) = patternList // For each case class name, distangle case branch body terms from the false branch. - .foldLeft[Opt[Term] -> Ls[(PatternInfo, Opt[Loc], Term)]](S(falseBranch) -> Nil) { + .foldLeft[Opt[Term] -> Ls[(Pattern, Opt[Loc], Term)]](S(falseBranch) -> Nil) { case ((S(remainingTerm), cases), pattern) => println(s"searching for case: ${pattern.showDbg}") val (leftoverTerm, extracted) = disentangleTerm(remainingTerm)( @@ -126,7 +126,7 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => context: Context, scrutineeVar: Var, scrutinee: Scrutinee, - pattern: PatternInfo + pattern: Pattern ): (Term, Opt[Term]) = { def rec(term: Term): (Term, Opt[Term]) = term match { @@ -159,7 +159,7 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => context: Context, scrutineeVar: Var, scrutinee: Scrutinee, - pattern: PatternInfo + pattern: Pattern ): (CaseBranches, Opt[Term]) = cases match { case NoCases => NoCases -> N @@ -184,7 +184,7 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => context: Context, scrutineeVar: Var, scrutinee: Scrutinee, - pattern: PatternInfo + pattern: Pattern ): (CaseBranches, CaseBranches) = cases match { case NoCases => NoCases -> NoCases diff --git a/shared/src/test/diff/nu/LitMatch.mls b/shared/src/test/diff/nu/LitMatch.mls index 45581782..24a76a66 100644 --- a/shared/src/test/diff/nu/LitMatch.mls +++ b/shared/src/test/diff/nu/LitMatch.mls @@ -22,21 +22,20 @@ b : true | false //│ = false if false is false then 0 -//│ ╙── //│ 0 //│ res //│ = 0 fun foo(x) = if x is false then 0 -//│ ╙── //│ fun foo: false -> 0 fun foo(x) = if x is false then 0 true then 1 -//│ ╙── -//│ ╙── //│ fun foo: Bool -> (0 | 1) - +fun foo(x) = if x is + 0 then "zero" + true then "true" +//│ fun foo: (0 | true) -> ("true" | "zero") diff --git a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls index a2befb18..c8be5625 100644 --- a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls +++ b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls @@ -38,11 +38,60 @@ class Derived(y: Int) extends Base(y + 1) //│ class Base(x: Int) //│ class Derived(y: Int) extends Base +:ducs:postprocess.result,coverage fun example3(t) = if t is Base(x) and p1(x) then x Derived(y) then y else 42 +//│ Post-processed UCS term: +//│ case t*‡ of +//│ refined Base*◊ -> +//│ let ucs$args_t$Base*† = (Base).unapply(t,) +//│ let x*‡ = (ucs$args_t$Base).0 +//│ let ucs$test$0*† = p1(x,) : Bool +//│ case ucs$test$0*† of +//│ true*† -> x +//│ _ -> +//│ case t*‡ of +//│ Derived*◊ -> +//│ let ucs$args_t$Derived*† = (Derived).unapply(t,) +//│ let y*‡ = (ucs$args_t$Derived).0 +//│ y +//│ _ -> 42 +//│ | | | | | | | STEP 4 +//│ | | | | | | | collected match registry: +//│ | | | | | | | >>> t => [class `Base`, class `Derived`] +//│ | | | | | | | >>> x => [] +//│ | | | | | | | >>> y => [] +//│ | | | | | | | checkCoverage <== 0 pending, 3 working, 0 seen +//│ | | | | | | | | CASE t +//│ | | | | | | | | SEEN: empty +//│ | | | | | | | | class symbol: `Base` +//│ | | | | | | | | REMOVE `Base` from working +//│ | | | | | | | | unseen: [class `Base`, class `Derived`] +//│ | | | | | | | | remaining: [] +//│ | | | | | | | | checkCoverage <== LET `ucs$args_t$Base` +//│ | | | | | | | | checkCoverage <== LET `x` +//│ | | | | | | | | checkCoverage <== LET `ucs$test$0` +//│ | | | | | | | | checkCoverage <== TEST `ucs$test$0` +//│ | | | | | | | | checkCoverage <== TERM x +//│ | | | | | | | | checkCoverage <== 0 pending, 2 working, 1 seen +//│ | | | | | | | | | CASE t +//│ | | | | | | | | | SEEN: t is Base +//│ | | | | | | | | | class symbol: `Derived` +//│ | | | | | | | | | REMOVE `Derived` from working +//│ | | | | | | | | | unseen: [class `Derived`] +//│ | | | | | | | | | remaining: [] +//│ | | | | | | | | | checkCoverage <== LET `ucs$args_t$Derived` +//│ | | | | | | | | | checkCoverage <== LET `y` +//│ | | | | | | | | | checkCoverage <== TERM y +//│ | | | | | | | | | remaining cases should be covered by the wildcard +//│ | | | | | | | | | checkCoverage <== TERM 42 +//│ | | | | | | | | checkCoverage ==> 0 diagnostics +//│ | | | | | | | | remaining cases are not covered: [] +//│ | | | | | | | checkCoverage ==> 0 diagnostics +//│ | | | | | | | Coverage checking result: 0 errors //│ fun example3: forall 'a. (Base & {#x: Num & 'a}) -> (Int | 'a) fun example4(t, x) = diff --git a/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls b/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls index 73af7dad..a90cc600 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls @@ -48,18 +48,8 @@ fun is_value_automatic_refinement(term) = //│ App*◊ -> false //│ fun is_value_automatic_refinement: (Abs | App | Var) -> Bool -:e fun is_value'(term) = if term is Term and term is Abs(_, _) then true Var(_) then false -//│ ╔══[ERROR] when `term` is `Term` -//│ ║ l.53: if term is Term and term is -//│ ║ ^^^^ -//│ ╟── `term` has 1 missing case -//│ ║ l.53: if term is Term and term is -//│ ║ ^^^^ -//│ ╟── it can be class `App` -//│ ║ l.6: class App(func: Term, arg: Term) extends Term -//│ ╙── ^^^ //│ fun is_value': (Abs | Var) -> Bool diff --git a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls index 8a1b7fb4..1b89a4b1 100644 --- a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls +++ b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls @@ -61,7 +61,6 @@ fun mix(x) = if x is true then "true" Some(value) then "Some" 0 then "zero" -//│ ╙── //│ fun mix: (0 | Some[anything] | true) -> ("Some" | "true" | "zero") [mix(true), mix(Some(1)), mix(0)] @@ -72,19 +71,19 @@ fun mix(x) = if x is :e [mix(false), mix(None)] //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.73: [mix(false), mix(None)] +//│ ║ l.72: [mix(false), mix(None)] //│ ║ ^^^^^^^^^^ //│ ╟── reference of type `false` does not match type `0 | Some[?T] | true` -//│ ║ l.73: [mix(false), mix(None)] +//│ ║ l.72: [mix(false), mix(None)] //│ ║ ^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.60: fun mix(x) = if x is //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.73: [mix(false), mix(None)] +//│ ║ l.72: [mix(false), mix(None)] //│ ║ ^^^^^^^^^ //│ ╟── reference of type `None` does not match type `0 | Some[?T] | true` -//│ ║ l.73: [mix(false), mix(None)] +//│ ║ l.72: [mix(false), mix(None)] //│ ║ ^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.60: fun mix(x) = if x is @@ -117,8 +116,6 @@ fun bool_patterns(x) = if x is true then 1 false then 2 -//│ ╙── -//│ ╙── //│ fun bool_patterns: Bool -> (1 | 2) fun dual_patterns(x, y) = diff --git a/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls b/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls index b8b9db5b..11f8763e 100644 --- a/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls +++ b/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls @@ -14,8 +14,6 @@ fun mixed_literals(v) = //│ 2 -> "2" //│ 1 -> "1" //│ Desugared term: case v of { true => "true"; false => "false"; 2 => "2"; 1 => "1" } -//│ ╙── -//│ ╙── //│ fun mixed_literals: (1 | 2 | false | true) -> ("1" | "2" | "false" | "true") :ducs:postprocess.result @@ -28,8 +26,6 @@ fun separated_by_and(v) = //│ case v*‡ of //│ true*† -> "true" //│ false*† -> "false" -//│ ╙── -//│ ╙── //│ fun separated_by_and: Bool -> ("false" | "true") :ducs:postprocess.result diff --git a/shared/src/test/diff/ucs/ElseIf.mls b/shared/src/test/diff/ucs/ElseIf.mls index 2f52f9b9..a24afcc3 100644 --- a/shared/src/test/diff/ucs/ElseIf.mls +++ b/shared/src/test/diff/ucs/ElseIf.mls @@ -79,6 +79,24 @@ f(Fals, Fals) //│ res //│ = false +:e +fun g(x, y) = if x is + true and y is true then true + false and y is false then false +//│ ╔══[ERROR] `y` has 1 missing case +//│ ║ l.84: true and y is true then true +//│ ║ ^ +//│ ╟── it can be Boolean value false +//│ ║ l.85: false and y is false then false +//│ ╙── ^^^^^ +//│ ╔══[ERROR] `y` has 1 missing case +//│ ║ l.85: false and y is false then false +//│ ║ ^ +//│ ╟── it can be Boolean value true +//│ ║ l.84: true and y is true then true +//│ ╙── ^^^^ +//│ fun g: (Bool, nothing) -> Bool + // Test with real booleans fun g(x, y) = if x is true and y is true then true @@ -86,7 +104,6 @@ fun g(x, y) = if x is _ and y is true then true false then false -//│ ╙── //│ fun g: (Object, Bool) -> Bool // Chained UCS terms @@ -106,46 +123,46 @@ fun f(x, y) = if x is Tru and x is Fals then true Fals and x is Tru then false //│ ╔══[ERROR] when `y` is `Tru` -//│ ║ l.106: Tru and x is Fals then true +//│ ║ l.123: Tru and x is Fals then true //│ ║ ^^^ //│ ╟── `x` has 1 missing case -//│ ║ l.106: Tru and x is Fals then true +//│ ║ l.123: Tru and x is Fals then true //│ ║ ^ //│ ╟── it can be module `Tru` -//│ ║ l.107: Fals and x is Tru then false +//│ ║ l.124: Fals and x is Tru then false //│ ╙── ^^^ //│ ╔══[ERROR] when `y` is `Fals` -//│ ║ l.107: Fals and x is Tru then false +//│ ║ l.124: Fals and x is Tru then false //│ ║ ^^^^ //│ ╟── `x` has 1 missing case -//│ ║ l.107: Fals and x is Tru then false +//│ ║ l.124: Fals and x is Tru then false //│ ║ ^ //│ ╟── it can be module `Fals` -//│ ║ l.106: Tru and x is Fals then true +//│ ║ l.123: Tru and x is Fals then true //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.106: Tru and x is Fals then true +//│ ║ l.123: Tru and x is Fals then true //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── class pattern of type `Tru` is not an instance of type `Fals` -//│ ║ l.103: Tru and y is Tru then true +//│ ║ l.120: Tru and y is Tru then true //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `Fals` -//│ ║ l.106: Tru and x is Fals then true +//│ ║ l.123: Tru and x is Fals then true //│ ║ ^ //│ ╟── Note: constraint arises from class pattern: -//│ ║ l.106: Tru and x is Fals then true +//│ ║ l.123: Tru and x is Fals then true //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.107: Fals and x is Tru then false +//│ ║ l.124: Fals and x is Tru then false //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── class pattern of type `Fals` is not an instance of type `Tru` -//│ ║ l.104: Fals and y is Fals then false +//│ ║ l.121: Fals and y is Fals then false //│ ║ ^^^^ //│ ╟── but it flows into reference with expected type `Tru` -//│ ║ l.107: Fals and x is Tru then false +//│ ║ l.124: Fals and x is Tru then false //│ ║ ^ //│ ╟── Note: constraint arises from class pattern: -//│ ║ l.107: Fals and x is Tru then false +//│ ║ l.124: Fals and x is Tru then false //│ ╙── ^^^ //│ fun f: (Fals | Tru, Fals | Tru) -> Bool diff --git a/shared/src/test/diff/ucs/Exhaustiveness.mls b/shared/src/test/diff/ucs/Exhaustiveness.mls index 1bf9bd51..720a50ce 100644 --- a/shared/src/test/diff/ucs/Exhaustiveness.mls +++ b/shared/src/test/diff/ucs/Exhaustiveness.mls @@ -46,6 +46,9 @@ class Node[A](value: int, left: Tree[A], right: Tree[A]) { >= value then right.find(wanted) == value then true } +//│ ╔══[ERROR] missing else branch +//│ ║ l.47: == value then true +//│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.44: fun contains(wanted) = if wanted //│ ║ ^^^^^^ diff --git a/shared/src/test/diff/ucs/SimpleUCS.mls b/shared/src/test/diff/ucs/SimpleUCS.mls index 90291d2f..ecf9c176 100644 --- a/shared/src/test/diff/ucs/SimpleUCS.mls +++ b/shared/src/test/diff/ucs/SimpleUCS.mls @@ -174,6 +174,9 @@ fun f(x, y) = > 0 then "gt" < 0 then "le" == 0 then "eq" +//│ ╔══[ERROR] missing else branch +//│ ║ l.176: == 0 then "eq" +//│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.176: == 0 then "eq" //│ ║ ^^^^ @@ -243,19 +246,19 @@ fun f(u, v, w) = Right(_) then "right-defined" None then "undefined" //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.241: Some(x) and x is +//│ ║ l.244: Some(x) and x is //│ ║ ^^^^^^^^^^^^^^^^ -//│ ║ l.242: Left(_) then "left-defined" +//│ ║ l.245: Left(_) then "left-defined" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.243: Right(_) then "right-defined" +//│ ║ l.246: Right(_) then "right-defined" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.244: None then "undefined" +//│ ║ l.247: None then "undefined" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `Int` does not match type `Left[?A] | Right[?B]` -//│ ║ l.227: if x - y > 0 then Some(x + y + z) else None +//│ ║ l.230: if x - y > 0 then Some(x + y + z) else None //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from reference: -//│ ║ l.241: Some(x) and x is +//│ ║ l.244: Some(x) and x is //│ ║ ^ //│ ╟── from field selection: //│ ║ l.4: class Some[A](value: A) extends Option[A] @@ -324,7 +327,7 @@ fun f(x) = 0 :: Nil() then "oh" //│ ╔══[ERROR] Syntactic split of patterns are not supported -//│ ║ l.324: 0 :: +//│ ║ l.327: 0 :: //│ ╙── ^^ //│ ╔══[ERROR] unexpected empty split found //│ ╙── @@ -369,22 +372,22 @@ test(false) test(0) test(1) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.369: test(0) +//│ ║ l.372: test(0) //│ ║ ^^^^^^^ //│ ╟── integer literal of type `0` is not an instance of type `Bool` -//│ ║ l.369: test(0) +//│ ║ l.372: test(0) //│ ║ ^ //│ ╟── Note: constraint arises from reference: -//│ ║ l.357: fun test(x) = if x then 0 else "oops" +//│ ║ l.360: fun test(x) = if x then 0 else "oops" //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.370: test(1) +//│ ║ l.373: test(1) //│ ║ ^^^^^^^ //│ ╟── integer literal of type `1` is not an instance of type `Bool` -//│ ║ l.370: test(1) +//│ ║ l.373: test(1) //│ ║ ^ //│ ╟── Note: constraint arises from reference: -//│ ║ l.357: fun test(x) = if x then 0 else "oops" +//│ ║ l.360: fun test(x) = if x then 0 else "oops" //│ ╙── ^ //│ "oops" | 0 | error //│ res diff --git a/shared/src/test/diff/ucs/SplitAfterOp.mls b/shared/src/test/diff/ucs/SplitAfterOp.mls index 3c4d96cf..f80f90a3 100644 --- a/shared/src/test/diff/ucs/SplitAfterOp.mls +++ b/shared/src/test/diff/ucs/SplitAfterOp.mls @@ -22,6 +22,9 @@ fun f(x, y) = if x == y + 5 then 0 7 then 0 +//│ ╔══[ERROR] missing else branch +//│ ║ l.24: 7 then 0 +//│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.22: if x == y + //│ ║ ^ @@ -55,40 +58,43 @@ fun f(x, y) = if x == y * 5 then 0 6 + 7 then 0 +//│ ╔══[ERROR] missing else branch +//│ ║ l.60: 6 + 7 then 0 +//│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.55: if x == y * +//│ ║ l.58: if x == y * //│ ║ ^ -//│ ║ l.56: 5 then 0 +//│ ║ l.59: 5 then 0 //│ ║ ^^^^^ //│ ╟── operator application of type `Bool` is not an instance of type `Int` -//│ ║ l.55: if x == y * +//│ ║ l.58: if x == y * //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.55: if x == y * +//│ ║ l.58: if x == y * //│ ║ ^ -//│ ║ l.56: 5 then 0 +//│ ║ l.59: 5 then 0 //│ ║ ^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.55: if x == y * +//│ ║ l.58: if x == y * //│ ║ ^ -//│ ║ l.56: 5 then 0 +//│ ║ l.59: 5 then 0 //│ ║ ^^^^^^^^^^^^^ -//│ ║ l.57: 6 + 7 then 0 +//│ ║ l.60: 6 + 7 then 0 //│ ║ ^^^^^^^^^ //│ ╟── operator application of type `Bool` is not an instance of type `Int` -//│ ║ l.55: if x == y * +//│ ║ l.58: if x == y * //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.55: if x == y * +//│ ║ l.58: if x == y * //│ ║ ^ -//│ ║ l.56: 5 then 0 +//│ ║ l.59: 5 then 0 //│ ║ ^^^^^^^^^^^^^ -//│ ║ l.57: 6 + 7 then 0 +//│ ║ l.60: 6 + 7 then 0 //│ ║ ^^^^^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.57: 6 + 7 then 0 +//│ ║ l.60: 6 + 7 then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Num) -> 0 @@ -99,32 +105,35 @@ fun f(x, y) = y + 5 then 0 7 then 0 +//│ ╔══[ERROR] missing else branch +//│ ║ l.107: 7 then 0 +//│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.99: y + -//│ ║ ^ -//│ ║ l.100: 5 then 0 +//│ ║ l.105: y + +//│ ║ ^ +//│ ║ l.106: 5 then 0 //│ ║ ^^^^^^^ //│ ╟── operator application of type `Bool` is not an instance of type `Int` -//│ ║ l.98: if x == -//│ ║ ^^^^ -//│ ║ l.99: y + -//│ ╙── ^^^^^^ +//│ ║ l.104: if x == +//│ ║ ^^^^ +//│ ║ l.105: y + +//│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.99: y + -//│ ║ ^ -//│ ║ l.100: 5 then 0 +//│ ║ l.105: y + +//│ ║ ^ +//│ ║ l.106: 5 then 0 //│ ║ ^^^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.99: y + -//│ ║ ^ -//│ ║ l.100: 5 then 0 +//│ ║ l.105: y + +//│ ║ ^ +//│ ║ l.106: 5 then 0 //│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.101: 7 then 0 +//│ ║ l.107: 7 then 0 //│ ║ ^^^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.101: 7 then 0 +//│ ║ l.107: 7 then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Num) -> 0 @@ -134,14 +143,14 @@ fun f(x, b) = if x == 1 and b then 0 //│ ╔══[ERROR] missing else branch -//│ ║ l.135: 1 and b then 0 +//│ ║ l.144: 1 and b then 0 //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.135: 1 and b then 0 +//│ ║ l.144: 1 and b then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.135: 1 and b then 0 +//│ ║ l.144: 1 and b then 0 //│ ║ ^^^^^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Bool) -> 0 @@ -152,16 +161,19 @@ fun toEnglish(x) = if x == true then "t" 0 then "z" +//│ ╔══[ERROR] missing else branch +//│ ║ l.163: 0 then "z" +//│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in operator application: -//│ ║ l.152: if x == +//│ ║ l.161: if x == //│ ║ ^^^^ -//│ ║ l.153: true then "t" +//│ ║ l.162: true then "t" //│ ║ ^^^^^^^^ //│ ╟── reference of type `true` is not an instance of `Num` -//│ ║ l.153: true then "t" +//│ ║ l.162: true then "t" //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.154: 0 then "z" +//│ ║ l.163: 0 then "z" //│ ║ ^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun toEnglish: Num -> ("t" | "z") @@ -171,18 +183,21 @@ fun toEnglish(x) = if x == 0 then "z" true then "t" +//│ ╔══[ERROR] missing else branch +//│ ║ l.185: true then "t" +//│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in operator application: -//│ ║ l.171: if x == +//│ ║ l.183: if x == //│ ║ ^^^^ -//│ ║ l.172: 0 then "z" +//│ ║ l.184: 0 then "z" //│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.173: true then "t" +//│ ║ l.185: true then "t" //│ ║ ^^^^^^^^ //│ ╟── reference of type `true` is not an instance of `Num` -//│ ║ l.173: true then "t" +//│ ║ l.185: true then "t" //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.173: true then "t" +//│ ║ l.185: true then "t" //│ ║ ^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun toEnglish: Num -> ("t" | "z") @@ -192,8 +207,11 @@ fun toEnglish(x) = if x == 1 then "o" 0 then "z" +//│ ╔══[ERROR] missing else branch +//│ ║ l.209: 0 then "z" +//│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.194: 0 then "z" +//│ ║ l.209: 0 then "z" //│ ║ ^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun toEnglish: Num -> ("o" | "z") @@ -210,32 +228,32 @@ fun toEnglish(x) = if x == else 1 //│ ╔══[PARSE ERROR] Unexpected indented block in expression position -//│ ║ l.211: else 1 +//│ ║ l.229: else 1 //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here -//│ ║ l.211: else 1 +//│ ║ l.229: else 1 //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead -//│ ║ l.210: if x == +//│ ║ l.228: if x == //│ ║ ^^^^ -//│ ║ l.211: else 1 +//│ ║ l.229: else 1 //│ ║ ^^^^ //│ ╟── Note: 'if' expression starts here: -//│ ║ l.210: if x == +//│ ║ l.228: if x == //│ ╙── ^^ //│ ╔══[ERROR] missing else branch -//│ ║ l.211: else 1 +//│ ║ l.229: else 1 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in operator application: -//│ ║ l.210: if x == +//│ ║ l.228: if x == //│ ║ ^^^^ -//│ ║ l.211: else 1 +//│ ║ l.229: else 1 //│ ║ ^^^^ //│ ╟── undefined literal of type `()` is not an instance of type `Num` -//│ ║ l.211: else 1 +//│ ║ l.229: else 1 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.211: else 1 +//│ ║ l.229: else 1 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun toEnglish: Num -> () diff --git a/shared/src/test/diff/ucs/WeirdIf.mls b/shared/src/test/diff/ucs/WeirdIf.mls index 2c1c5c1f..1f34f67b 100644 --- a/shared/src/test/diff/ucs/WeirdIf.mls +++ b/shared/src/test/diff/ucs/WeirdIf.mls @@ -99,8 +99,6 @@ fun boolToStr(x) = if x is true then "yah" false then "nah" -//│ ╙── -//│ ╙── //│ fun boolToStr: Bool -> ("nah" | "yah") boolToStr of true diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index 5bceb06c..60758f26 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -1134,15 +1134,10 @@ object DiffTests { object DebugUCSFlags { // E.g. "ducs", "ducs:foo", "ducs:foo,bar", "ducs:a.b.c,foo" - private val pattern = "^ducs(?::\\s*([A-Za-z\\.-]+)(,\\s*[A-Za-z\\.-]+)*)?$".r - def unapply(flags: Str): Opt[Set[Str]] = - flags match { - case pattern(head, tail) => - (Option(head), Option(tail)) match { - case (N, _) => S(Set.empty) - case (S(head), N) => S(Set.single(head)) - case (S(head), S(tail)) => S(Set.from(head :: tail.split(",").drop(1).toList)) - } + private val pattern = "^ducs(?::(\\s*(?:[A-Za-z\\.-]+)(?:,\\s*[A-Za-z\\.-]+)*))?$".r + def unapply(flagsString: Str): Opt[Set[Str]] = + flagsString match { + case pattern(flags) => Option(flags).map(_.split(",\\s*").toSet) case _ => N } } From bdb5b0eedd04036e675120746d26c908be056715 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Fri, 19 Jan 2024 23:39:19 +0800 Subject: [PATCH 087/143] Fix `filterMap` test --- .../mlscript/ucs/stages/Desugaring.scala | 13 ++++- shared/src/test/diff/nu/FilterMap.mls | 56 +++++++++++++++---- 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index db238f49..428e86f0 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -241,6 +241,10 @@ trait Desugaring { self: PreTyper => println(s"${nme.name} is ${pattern.nme.name}") val (scopeWithNestedAll, bindNestedAll) = desugarClassPattern(pattern, nme, scope, pattern.refined) (scopeWithNestedAll, split => bindPrevious(bindNestedAll(split) :: c.Split.Nil)) + case ((scope, bindPrevious), S(nme -> S(pattern @ s.ConcretePattern(Var("true") | Var("false"))))) => + println(s"${nme.name} is ${pattern.nme.name}") + val className = pattern.nme.withResolvedClassLikeSymbol(scope, context) + (scope, split => bindPrevious(c.Branch(nme, c.Pattern.Class(className, false), (split)) :: c.Split.Nil)) case ((scope, bindPrevious), S(nme -> S(pattern: s.LiteralPattern))) => nme.getOrCreateScrutinee .withAliasVar(nme) @@ -252,7 +256,7 @@ trait Desugaring { self: PreTyper => (scopeWithNestedAll, bindNestedAll.andThen(bindPrevious)) // Well, other patterns are not supported yet. case (acc, S(nme -> S(pattern))) => - raiseDesugaringError(msg"unsupported pattern is" -> pattern.toLoc) + raiseDesugaringError(msg"unsupported pattern" -> pattern.toLoc) acc // If this parameter is empty (e.g. produced by wildcard), then we do // nothing and pass on scope and binder. @@ -273,8 +277,13 @@ trait Desugaring { self: PreTyper => val symbol = new LocalTermSymbol(subScrutineeVar) symbol.addScrutinee(tuplePattern.getField(index).withAliasVar(subScrutineeVar)) S(subScrutineeVar.withSymbol(symbol) -> S(parameterPattern)) + case (parameterPattern @ s.ConcretePattern(nme @ (Var("true") | Var("false"))), index) => + val subScrutineeVar = freshSubScrutineeVar(parentScrutineeVar, s"Tuple$$${fields.length}", index) + val symbol = new LocalTermSymbol(subScrutineeVar) + symbol.addScrutinee(tuplePattern.getField(index).withAliasVar(subScrutineeVar)) + S(subScrutineeVar.withSymbol(symbol) -> S(parameterPattern)) case (pattern, _) => - raiseDesugaringError(msg"unsupported pattern" -> pattern.toLoc) + raiseDesugaringError(msg"unsupported pattern ${pattern.getClass().getSimpleName()}" -> pattern.toLoc) N }.toList } diff --git a/shared/src/test/diff/nu/FilterMap.mls b/shared/src/test/diff/nu/FilterMap.mls index bd4274b3..0ad2fce4 100644 --- a/shared/src/test/diff/nu/FilterMap.mls +++ b/shared/src/test/diff/nu/FilterMap.mls @@ -14,24 +14,58 @@ // 9 { true , Y } -> [ Y | filtermap (F , XS )] // 10 end. - -module Nil +type List[A] = Cons[A] | Nil class Cons[out A](head: A, tail: Cons[A] | Nil) -//│ module Nil +module Nil +//│ type List[A] = Cons[A] | Nil //│ class Cons[A](head: A, tail: Cons[A] | Nil) +//│ module Nil +fun (::) cons(h, t) = Cons(h, t) +//│ fun (::) cons: forall 'A. ('A, Cons['A] | Nil) -> Cons['A] -// FIXME UCS -fun filtermap(f, xs) = if xs is +fun filtermap(f, xs: List['A]) = if xs is Nil then Nil - Cons(y, ys) and f(ys) is + Cons(y, ys) and f(y) is false then filtermap(f, ys) true then Cons(y, filtermap(f, ys)) - [true, z] then Cons(y, filtermap(f, ys)) -//│ ╔══[ERROR] unsupported pattern -//│ ║ l.30: [true, z] then Cons(y, filtermap(f, ys)) -//│ ╙── ^^^^ -//│ fun filtermap: ((Cons[nothing] | Nil) -> (Object & {1: anything} & ~false & ~true | false | true), Cons[anything] | Nil) -> (Cons[nothing] | Nil) + [true, z] then Cons(z, filtermap(f, ys)) +//│ fun filtermap: forall 'A 'A0. ('A -> (Object & {0: true, 1: 'A0} & ~false & ~true | false | true), xs: List['A]) -> (Cons['A0] | Nil) +//│ where +//│ 'A <: 'A0 + +let xs = 0 :: 1 :: 2 :: 3 :: 4 :: 5 :: 6 :: 7 :: Nil +//│ let xs: Cons[0 | 1 | 2 | 3 | 4 | 5 | 6 | 7] +//│ xs +//│ = Cons {} + +fun f(x) = if x % 3 is + 0 then [true, -x] + 1 then false + _ then true +//│ fun f: Int -> (Bool | [true, Int]) + +:e // TODO: Records are not `Object`s +filtermap(f, xs) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.49: filtermap(f, xs) +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ╟── tuple literal of type `[true, ?a]` is not an instance of type `Object` +//│ ║ l.43: 0 then [true, -x] +//│ ║ ^^^^^^^^^^ +//│ ╟── Note: constraint arises from `case` expression: +//│ ║ l.30: false then filtermap(f, ys) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.31: true then Cons(y, filtermap(f, ys)) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.32: [true, z] then Cons(z, filtermap(f, ys)) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── from application: +//│ ║ l.29: Cons(y, ys) and f(y) is +//│ ╙── ^^^^ +//│ Cons[Int] | Nil | error +//│ res +//│ = Cons {} module Tru From 37a508cba6fe7efc387c69da11c0a3aae243b19a Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Fri, 19 Jan 2024 23:49:07 +0800 Subject: [PATCH 088/143] No longer use implicit `Set[String]` --- .../mlscript/ucs/stages/Normalization.scala | 49 +++++++++---------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index bbcee158..516c183c 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -68,15 +68,14 @@ trait Normalization { self: Desugarer with Traceable => * @param generatedVars the generated variables which have been declared * @return the concatenated split */ - def fill(those: Split, deep: Bool, shouldReportDiscarded: Bool)(implicit + def fill(those: Split, deep: Bool, declaredVars: Set[Var], shouldReportDiscarded: Bool)(implicit scope: Scope, context: Context, - generatedVars: Set[Var], ): Split = - trace(s"fill <== ${generatedVars.iterator.map(_.name).mkString("{", ", ", "}")}") { + trace(s"fill <== ${declaredVars.iterator.map(_.name).mkString("{", ", ", "}")}") { println(s"LHS: ${showSplit(these)}") println(s"RHS: ${showSplit(those)}") - fillImpl(these, those, deep)(scope, context, generatedVars, shouldReportDiscarded) + fillImpl(these, those, deep)(scope, context, declaredVars, shouldReportDiscarded) }(sp => s"fill ==> ${showSplit(sp)}") def :++(tail: => Split): Split = { @@ -100,53 +99,52 @@ trait Normalization { self: Desugarer with Traceable => @inline protected def normalize(split: Split)(implicit scope: Scope, context: Context - ): Term = normalizeToTerm(split)(scope, context, Set.empty) + ): Term = normalizeToTerm(split, Set.empty)(scope, context) private def errorTerm: Term = Var("error") - private def normalizeToTerm(split: Split)(implicit + private def normalizeToTerm(split: Split, declaredVars: Set[Var])(implicit scope: Scope, context: Context, - generatedVars: Set[Var], ): Term = trace("normalizeToTerm <==") { split match { case Split.Cons(Branch(scrutinee, Pattern.Name(nme), continuation), tail) => println(s"ALIAS: ${scrutinee.name} is ${nme.name}") val (wrap, realTail) = preventShadowing(nme, tail) - wrap(Let(false, nme, scrutinee, normalizeToTerm(continuation.fill(realTail, false, true)))) + wrap(Let(false, nme, scrutinee, normalizeToTerm(continuation.fill(realTail, false, declaredVars, true), declaredVars))) // Skip Boolean conditions as scrutinees, because they only appear once. case Split.Cons(Branch(test, pattern @ Pattern.Class(nme @ Var("true"), _), continuation), tail) if context.isTestVar(test) => println(s"TRUE: ${test.name} is true") - val trueBranch = normalizeToTerm(continuation.fill(tail, false, false)) - val falseBranch = normalizeToCaseBranches(tail) + val trueBranch = normalizeToTerm(continuation.fill(tail, false, declaredVars, false), declaredVars) + val falseBranch = normalizeToCaseBranches(tail, declaredVars) CaseOf(test, Case(nme, trueBranch, falseBranch)(refined = false)) case Split.Cons(Branch(Scrutinee.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Literal(literal), continuation), tail) => println(s"LITERAL: ${scrutineeVar.name} is ${literal.idStr}") println(s"entire split: ${showSplit(split)}") - val concatenatedTrueBranch = continuation.fill(tail, false, false) + val concatenatedTrueBranch = continuation.fill(tail, false, declaredVars, false) // println(s"true branch: ${showSplit(concatenatedTrueBranch)}") - val trueBranch = normalizeToTerm(specialize(concatenatedTrueBranch, true)(scrutineeVar, scrutinee, pattern, context)) + val trueBranch = normalizeToTerm(specialize(concatenatedTrueBranch, true)(scrutineeVar, scrutinee, pattern, context), declaredVars) // println(s"false branch: ${showSplit(tail)}") - val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context)) + val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context), declaredVars) CaseOf(scrutineeVar, Case(literal, trueBranch, falseBranch)(refined = false)) case Split.Cons(Branch(Scrutinee.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Class(nme, rfd), continuation), tail) => println(s"CLASS: ${scrutineeVar.name} is ${nme.name}") // println(s"match ${scrutineeVar.name} with $nme (has location: ${nme.toLoc.isDefined})") - val trueBranch = normalizeToTerm(specialize(continuation.fill(tail, false, false), true)(scrutineeVar, scrutinee, pattern, context)) - val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context)) + val trueBranch = normalizeToTerm(specialize(continuation.fill(tail, false, declaredVars, false), true)(scrutineeVar, scrutinee, pattern, context), declaredVars) + val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context), declaredVars) CaseOf(scrutineeVar, Case(nme, trueBranch, falseBranch)(refined = pattern.refined)) case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => raiseDesugaringError(msg"unsupported pattern: ${pattern.toString}" -> pattern.toLoc) errorTerm - case Split.Let(_, nme, _, tail) if context.isScrutineeVar(nme) && generatedVars.contains(nme) => + case Split.Let(_, nme, _, tail) if context.isScrutineeVar(nme) && declaredVars.contains(nme) => println(s"LET: SKIP already declared scrutinee ${nme.name}") - normalizeToTerm(tail) + normalizeToTerm(tail, declaredVars) case Split.Let(rec, nme, rhs, tail) if context.isGeneratedVar(nme) => println(s"LET: generated ${nme.name}") - Let(rec, nme, rhs, normalizeToTerm(tail)(scope, context, generatedVars + nme)) + Let(rec, nme, rhs, normalizeToTerm(tail, declaredVars + nme)(scope, context)) case Split.Let(rec, nme, rhs, tail) => println(s"LET: ${nme.name}") - Let(rec, nme, rhs, normalizeToTerm(tail)) + Let(rec, nme, rhs, normalizeToTerm(tail, declaredVars)) case Split.Else(default) => println(s"DFLT: ${default.showDbg}") default @@ -156,20 +154,19 @@ trait Normalization { self: Desugarer with Traceable => } }(split => "normalizeToTerm ==> " + showNormalizedTerm(split)) - private def normalizeToCaseBranches(split: Split)(implicit + private def normalizeToCaseBranches(split: Split, declaredVars: Set[Var])(implicit scope: Scope, context: Context, - generatedVars: Set[Var] ): CaseBranches = trace(s"normalizeToCaseBranches <==") { split match { // case Split.Cons(head, Split.Nil) => Case(head.pattern, normalizeToTerm(head.continuation), NoCases) - case other: Split.Cons => Wildcard(normalizeToTerm(other)) - case Split.Let(_, nme, _, tail) if context.isScrutineeVar(nme) && generatedVars.contains(nme) => - normalizeToCaseBranches(tail) + case other: Split.Cons => Wildcard(normalizeToTerm(other, declaredVars)) + case Split.Let(_, nme, _, tail) if context.isScrutineeVar(nme) && declaredVars.contains(nme) => + normalizeToCaseBranches(tail, declaredVars) case Split.Let(rec, nme, rhs, tail) => - val newDeclaredBindings = if (context.isGeneratedVar(nme)) generatedVars + nme else generatedVars - normalizeToCaseBranches(tail)(scope, context, newDeclaredBindings) match { + val newDeclaredVars = if (context.isGeneratedVar(nme)) declaredVars + nme else declaredVars + normalizeToCaseBranches(tail, newDeclaredVars)(scope, context) match { case NoCases => Wildcard(rhs) case Wildcard(term) => Wildcard(Let(rec, nme, rhs, term)) case _: Case => die From 1b275ad7f170b85b4150104acbede5f6aa05e886 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Fri, 19 Jan 2024 23:55:58 +0800 Subject: [PATCH 089/143] Normalization's `fill` does not need to be `deep` --- .../mlscript/ucs/stages/Normalization.scala | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 516c183c..800cebb0 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -1,4 +1,3 @@ - package mlscript.ucs.stages import mlscript.{App, CaseOf, Fld, FldFlags, Let, Loc, Sel, Term, Tup, Var, StrLit} @@ -10,15 +9,13 @@ import ucs.{Desugarer, Lines, LinesOps, VariableGenerator} import ucs.context.{Context, Scrutinee} import ucs.display.{showNormalizedTerm, showSplit} import ucs.syntax.core.{Pattern, Branch, Split} -import pretyper.Scope import pretyper.symbol._ -import pretyper.{Diagnosable, Traceable} +import pretyper.{Diagnosable, Scope, Traceable} trait Normalization { self: Desugarer with Traceable => import Normalization._ - // TODO: We might not need the case where `deep` is `false`. - private def fillImpl(these: Split, those: Split, deep: Bool)(implicit + private def fillImpl(these: Split, those: Split)(implicit scope: Scope, context: Context, generatedVars: Set[Var], @@ -34,22 +31,16 @@ trait Normalization { self: Desugarer with Traceable => these } else (these match { case these @ Split.Cons(head, tail) => - if (head.continuation.hasElse || !deep) { - these.copy(tail = fillImpl(tail, those, deep)) - } else { - // println(s"found a branch without default ${showSplit(head.continuation)}") - val newHead = head.copy(continuation = fillImpl(head.continuation, those, deep)) - these.copy(head = newHead, tail = fillImpl(tail, those, deep)) - } + these.copy(tail = fillImpl(tail, those)) case these @ Split.Let(_, nme, _, tail) => println(s"fill let binding ${nme.name}") if (scope.getTermSymbol(nme.name).isDefined && (those.freeVars contains nme)) { val fresh = context.freshShadowed() val thoseWithShadowed = Split.Let(false, nme, fresh, those) - val concatenated = these.copy(tail = fillImpl(tail, thoseWithShadowed, deep)) + val concatenated = these.copy(tail = fillImpl(tail, thoseWithShadowed)) Split.Let(false, fresh, nme, concatenated) } else { - these.copy(tail = fillImpl(tail, those, deep)(scope, context, generatedVars + nme, false)) + these.copy(tail = fillImpl(tail, those)(scope, context, generatedVars + nme, false)) } case _: Split.Else => these case Split.Nil => @@ -62,20 +53,19 @@ trait Normalization { self: Desugarer with Traceable => * Fill the split into the previous split. * * @param those the split to append - * @param deep whether we should also fill the leading branches * @param shouldReportDiscarded whether we should raise an error if the given * split is discarded because of the else branch * @param generatedVars the generated variables which have been declared * @return the concatenated split */ - def fill(those: Split, deep: Bool, declaredVars: Set[Var], shouldReportDiscarded: Bool)(implicit + def fill(those: Split, declaredVars: Set[Var], shouldReportDiscarded: Bool)(implicit scope: Scope, context: Context, ): Split = trace(s"fill <== ${declaredVars.iterator.map(_.name).mkString("{", ", ", "}")}") { println(s"LHS: ${showSplit(these)}") println(s"RHS: ${showSplit(those)}") - fillImpl(these, those, deep)(scope, context, declaredVars, shouldReportDiscarded) + fillImpl(these, those)(scope, context, declaredVars, shouldReportDiscarded) }(sp => s"fill ==> ${showSplit(sp)}") def :++(tail: => Split): Split = { @@ -111,17 +101,17 @@ trait Normalization { self: Desugarer with Traceable => case Split.Cons(Branch(scrutinee, Pattern.Name(nme), continuation), tail) => println(s"ALIAS: ${scrutinee.name} is ${nme.name}") val (wrap, realTail) = preventShadowing(nme, tail) - wrap(Let(false, nme, scrutinee, normalizeToTerm(continuation.fill(realTail, false, declaredVars, true), declaredVars))) + wrap(Let(false, nme, scrutinee, normalizeToTerm(continuation.fill(realTail, declaredVars, true), declaredVars))) // Skip Boolean conditions as scrutinees, because they only appear once. case Split.Cons(Branch(test, pattern @ Pattern.Class(nme @ Var("true"), _), continuation), tail) if context.isTestVar(test) => println(s"TRUE: ${test.name} is true") - val trueBranch = normalizeToTerm(continuation.fill(tail, false, declaredVars, false), declaredVars) + val trueBranch = normalizeToTerm(continuation.fill(tail, declaredVars, false), declaredVars) val falseBranch = normalizeToCaseBranches(tail, declaredVars) CaseOf(test, Case(nme, trueBranch, falseBranch)(refined = false)) case Split.Cons(Branch(Scrutinee.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Literal(literal), continuation), tail) => println(s"LITERAL: ${scrutineeVar.name} is ${literal.idStr}") println(s"entire split: ${showSplit(split)}") - val concatenatedTrueBranch = continuation.fill(tail, false, declaredVars, false) + val concatenatedTrueBranch = continuation.fill(tail, declaredVars, false) // println(s"true branch: ${showSplit(concatenatedTrueBranch)}") val trueBranch = normalizeToTerm(specialize(concatenatedTrueBranch, true)(scrutineeVar, scrutinee, pattern, context), declaredVars) // println(s"false branch: ${showSplit(tail)}") @@ -130,7 +120,7 @@ trait Normalization { self: Desugarer with Traceable => case Split.Cons(Branch(Scrutinee.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Class(nme, rfd), continuation), tail) => println(s"CLASS: ${scrutineeVar.name} is ${nme.name}") // println(s"match ${scrutineeVar.name} with $nme (has location: ${nme.toLoc.isDefined})") - val trueBranch = normalizeToTerm(specialize(continuation.fill(tail, false, declaredVars, false), true)(scrutineeVar, scrutinee, pattern, context), declaredVars) + val trueBranch = normalizeToTerm(specialize(continuation.fill(tail, declaredVars, false), true)(scrutineeVar, scrutinee, pattern, context), declaredVars) val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context), declaredVars) CaseOf(scrutineeVar, Case(nme, trueBranch, falseBranch)(refined = pattern.refined)) case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => From d360660ec8e8cd889699444cccfda53ffcd82546 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sat, 20 Jan 2024 05:08:34 +0800 Subject: [PATCH 090/143] Fix alias patterns --- .../scala/mlscript/ucs/context/Context.scala | 4 +- .../mlscript/ucs/stages/Desugaring.scala | 181 +++++++++++------- .../scala/mlscript/ucs/syntax/source.scala | 2 +- .../pretyper/ucs/examples/LispInterpreter.mls | 8 +- .../pretyper/ucs/patterns/AliasPattern.mls | 57 ++++++ 5 files changed, 179 insertions(+), 73 deletions(-) create mode 100644 shared/src/test/diff/pretyper/ucs/patterns/AliasPattern.mls diff --git a/shared/src/main/scala/mlscript/ucs/context/Context.scala b/shared/src/main/scala/mlscript/ucs/context/Context.scala index 63ccc906..9a62d9f8 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Context.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Context.scala @@ -68,7 +68,5 @@ class Context(originalTerm: If) { } object Context { - // TODO: Generate fresh prefix in a determinstic way. I tried to use a counter, - // but the produced value is not stable across different runs. - def freshPrefix(): Str = "ucs" + private def freshPrefix(): Str = "ucs" } diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 428e86f0..8b89f5ed 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -128,27 +128,46 @@ trait Desugaring { self: PreTyper => private def flattenClassParameters( parentScrutineeVar: Var, + parentScrutinee: Scrutinee, parentClassLikeSymbol: TypeSymbol, parameters: Ls[s.Pattern] - )(implicit context: Context): Ls[Opt[Var -> Opt[s.Pattern]]] = - trace(s"flattenClassParameters <== ${parentScrutineeVar.name} is ${parentClassLikeSymbol.name}") { - // Make it `lazy` so that it will not be created if all fields are wildcards. - lazy val classPattern = parentScrutineeVar.getOrCreateScrutinee.getOrCreateClassPattern(parentClassLikeSymbol) - parameters.iterator.zipWithIndex.map { - case (_: s.EmptyPattern, _) => N - case (s.NamePattern(name), index) => + )(implicit context: Context): Ls[Opt[(Var, Opt[s.Pattern], Ls[Var])]] = { + // Make it `lazy` so that it will not be created if all fields are wildcards. + lazy val classPattern = parentScrutinee.getOrCreateClassPattern(parentClassLikeSymbol) + def flattenPattern( + parameterPattern: s.Pattern, + index: Int, + subScrutineeVarOpt: Opt[(Var, Scrutinee)], + aliasVars: Ls[Var], + ): Opt[(Var, Opt[s.Pattern], Ls[Var])] = { // scrutineeVar, subPattern, aliasVars + lazy val (subScrutineeVar, subScrutinee) = subScrutineeVarOpt.getOrElse { + val subScrutineeVar = freshSubScrutineeVar(parentScrutineeVar, parentClassLikeSymbol.name, index) + val symbol = new LocalTermSymbol(subScrutineeVar) + val subScrutinee = classPattern.getParameter(index).withAliasVar(subScrutineeVar.withSymbol(symbol)) + symbol.addScrutinee(subScrutinee) + (subScrutineeVar, subScrutinee) + } + parameterPattern match { + case _: s.EmptyPattern => N + case s.NamePattern(name) => val subScrutinee = classPattern.getParameter(index).withAliasVar(name) - S(name.withFreshSymbol.withScrutinee(subScrutinee) -> N) - case (parameterPattern @ (s.ClassPattern(_, _, _) | s.LiteralPattern(_) | s.TuplePattern(_)), index) => - val subScrutineeVar = freshSubScrutineeVar(parentScrutineeVar, parentClassLikeSymbol.name, index) - val symbol = new LocalTermSymbol(subScrutineeVar) - symbol.addScrutinee(classPattern.getParameter(index).withAliasVar(subScrutineeVar)) - S(subScrutineeVar.withSymbol(symbol) -> S(parameterPattern)) - case (pattern, _) => + S((name.withFreshSymbol.withScrutinee(subScrutinee), N, aliasVars.reverse)) + case parameterPattern @ (s.ClassPattern(_, _, _) | s.LiteralPattern(_) | s.TuplePattern(_)) => + S((subScrutineeVar, S(parameterPattern), aliasVars.reverse)) + case parameterPattern @ s.AliasPattern(aliasVar, innerPattern) => + println(s"alias pattern found ${subScrutineeVar.name} -> ${aliasVar.name}") + flattenPattern(innerPattern, index, S((subScrutineeVar, subScrutinee)), aliasVar.withFreshSymbol.withScrutinee(subScrutinee) :: aliasVars) + case pattern => raiseDesugaringError(msg"unsupported pattern" -> pattern.toLoc) N + } + } + trace(s"flattenClassParameters <== ${parentScrutineeVar.name} is ${parentClassLikeSymbol.name}") { + parameters.iterator.zipWithIndex.map { + case (pattern, index) => flattenPattern(pattern, index, N, Nil) }.toList }(r => s"flattenClassParameters ==> ${r.mkString(", ")}") + } /** * Recursively decompose and flatten a possibly nested class pattern. Any @@ -188,13 +207,13 @@ trait Desugaring { self: PreTyper => val vari = makeUnappliedVar(scrutineeVar, pattern.nme) vari.withSymbol(new LocalTermSymbol(vari)) } - val nestedPatterns = flattenClassParameters(scrutineeVar, patternClassSymbol, parameters) + val nestedPatterns = flattenClassParameters(scrutineeVar, scrutinee, patternClassSymbol, parameters) println(s"nestedPatterns = $nestedPatterns") // First, handle bindings of parameters of the current class pattern. val identity = (split: c.Split) => split val bindParameters = nestedPatterns.iterator.zipWithIndex.foldRight[c.Split => c.Split](identity) { case ((N, _), bindNextParameter) => bindNextParameter - case ((S(parameter -> _), index), bindNextParameter) => + case ((S((parameter, _, _)), index), bindNextParameter) => bindNextParameter.andThen { c.Split.Let(false, parameter, Sel(unapp, Var(index.toString)), _) } } println(s"bindParameters === identity: ${bindParameters === identity}") @@ -223,77 +242,109 @@ trait Desugaring { self: PreTyper => * @param bindScrutinees a function that adds all bindings to a split */ private def desugarNestedPatterns( - nestedPatterns: Ls[Opt[Var -> Opt[s.Pattern]]], + nestedPatterns: Ls[Opt[(Var, Opt[s.Pattern], Ls[Var])]], scopeWithScrutinees: Scope, bindScrutinees: c.Split => c.Split )(implicit context: Context): (Scope, c.Split => c.Split) = trace("desugarNestedPatterns") { nestedPatterns.foldLeft((scopeWithScrutinees, bindScrutinees)) { - // If this parameter is not matched with a sub-pattern, then we do + // If this parameter is empty (e.g. produced by wildcard), then we do // nothing and pass on scope and binder. - case (acc, S(_ -> N)) => acc + case (acc, N) => acc // If this sub-pattern is a class pattern, we need to recursively flatten // the class pattern. We will get a scope with all bindings and a function // that adds all bindings to a split. The scope can be passed on to the // next sub-pattern. The binder needs to be composed with the previous // binder. - case ((scope, bindPrevious), S(nme -> S(pattern: s.ClassPattern))) => - println(s"${nme.name} is ${pattern.nme.name}") - val (scopeWithNestedAll, bindNestedAll) = desugarClassPattern(pattern, nme, scope, pattern.refined) - (scopeWithNestedAll, split => bindPrevious(bindNestedAll(split) :: c.Split.Nil)) - case ((scope, bindPrevious), S(nme -> S(pattern @ s.ConcretePattern(Var("true") | Var("false"))))) => - println(s"${nme.name} is ${pattern.nme.name}") - val className = pattern.nme.withResolvedClassLikeSymbol(scope, context) - (scope, split => bindPrevious(c.Branch(nme, c.Pattern.Class(className, false), (split)) :: c.Split.Nil)) - case ((scope, bindPrevious), S(nme -> S(pattern: s.LiteralPattern))) => - nme.getOrCreateScrutinee - .withAliasVar(nme) - .getOrCreateLiteralPattern(pattern.literal) - .addLocation(pattern.literal) - (scope, makeLiteralTest(nme, pattern.literal)(scope).andThen(bindPrevious)) - case ((scope, bindPrevious), S(nme -> S(s.TuplePattern(fields)))) => - val (scopeWithNestedAll, bindNestedAll) = desugarTuplePattern(fields, nme, scope) - (scopeWithNestedAll, bindNestedAll.andThen(bindPrevious)) - // Well, other patterns are not supported yet. - case (acc, S(nme -> S(pattern))) => - raiseDesugaringError(msg"unsupported pattern" -> pattern.toLoc) - acc - // If this parameter is empty (e.g. produced by wildcard), then we do - // nothing and pass on scope and binder. - case (acc, N) => acc + case (acc @ (scope, bindPrevious), S((nme, patternOpt, aliasVars))) => + println(s"subScrut = ${nme.name}; aliasVars = ${aliasVars.iterator.map(_.name).mkString("[", ", ", "]")}") + val bindAliasVars = aliasVars.foldRight[c.Split => c.Split](identity[c.Split]) { + case (aliasVar, bindNext) => (inner: c.Split) => c.Split.Let(false, aliasVar, nme, bindNext(inner)) + } + patternOpt match { + // If this parameter is not matched with a sub-pattern, then we do + // nothing and pass on scope and binder. + case N => (scope, bindAliasVars.andThen(bindPrevious)) + case S(pattern) => pattern match { + case pattern: s.ClassPattern => + println(s"${nme.name} is ${pattern.nme.name}") + val (scopeWithNestedAll, bindNestedAll) = desugarClassPattern(pattern, nme, scope, pattern.refined) + (scopeWithNestedAll, split => bindPrevious(bindNestedAll(bindAliasVars(split)) :: c.Split.Nil)) + case pattern @ s.ConcretePattern(Var("true") | Var("false")) => + println(s"${nme.name} is ${pattern.nme.name}") + val className = pattern.nme.withResolvedClassLikeSymbol(scope, context) + (scope, split => bindPrevious(c.Branch(nme, c.Pattern.Class(className, false), bindAliasVars(split)) :: c.Split.Nil)) + case s.LiteralPattern(literal) => + nme.getOrCreateScrutinee + .withAliasVar(nme) + .getOrCreateLiteralPattern(literal) + .addLocation(literal) + (scope, bindAliasVars.andThen(makeLiteralTest(nme, literal)(scope)).andThen(bindPrevious)) + case s.TuplePattern(fields) => + val (scopeWithNestedAll, bindNestedAll) = desugarTuplePattern(fields, nme, scope) + (scopeWithNestedAll, bindAliasVars.andThen(bindNestedAll).andThen(bindPrevious)) + // Well, other patterns are not supported yet. + case _ => + raiseDesugaringError(msg"unsupported pattern" -> pattern.toLoc) + acc + } + } } }() - private def flattenTupleFields(parentScrutineeVar: Var, fields: Ls[s.Pattern])(implicit context: Context): Ls[Opt[Var -> Opt[s.Pattern]]] = { + // TODO: `aliasVars` not computed + private def flattenTupleFields( + parentScrutineeVar: Var, + parentScrutinee: Scrutinee, + fields: Ls[s.Pattern] + )(implicit context: Context): Ls[Opt[(Var, Opt[s.Pattern], Ls[Var])]] = { // Make it `lazy` so that it will not be created if all fields are wildcards. - lazy val tuplePattern = parentScrutineeVar.getOrCreateScrutinee.getOrCreateTuplePattern - fields.iterator.zipWithIndex.map { - case (_: s.EmptyPattern, _) => N - case (s.NamePattern(name), index) => - S(name.withFreshSymbol.withScrutinee(tuplePattern.getField(index)) -> N) - case (parameterPattern @ (s.ClassPattern(_, _, _) | s.LiteralPattern(_) | s.TuplePattern(_)), index) => - val arity = fields.length - val subScrutineeVar = freshSubScrutineeVar(parentScrutineeVar, s"Tuple$$$arity", index) - val symbol = new LocalTermSymbol(subScrutineeVar) - symbol.addScrutinee(tuplePattern.getField(index).withAliasVar(subScrutineeVar)) - S(subScrutineeVar.withSymbol(symbol) -> S(parameterPattern)) - case (parameterPattern @ s.ConcretePattern(nme @ (Var("true") | Var("false"))), index) => - val subScrutineeVar = freshSubScrutineeVar(parentScrutineeVar, s"Tuple$$${fields.length}", index) + lazy val tuplePattern = parentScrutinee.getOrCreateTuplePattern + lazy val tupleDummyClassName = s"Tuple$$${fields.length}" + def flattenPattern( + pattern: s.Pattern, + index: Int, + subScrutineeVarOpt: Opt[(Var, Scrutinee)], + aliasVars: Ls[Var], + ): Opt[(Var, Opt[s.Pattern], Ls[Var])] = { + lazy val (subScrutineeVar, subScrutinee) = subScrutineeVarOpt.getOrElse { + val subScrutineeVar = freshSubScrutineeVar(parentScrutineeVar, tupleDummyClassName, index) val symbol = new LocalTermSymbol(subScrutineeVar) - symbol.addScrutinee(tuplePattern.getField(index).withAliasVar(subScrutineeVar)) - S(subScrutineeVar.withSymbol(symbol) -> S(parameterPattern)) - case (pattern, _) => - raiseDesugaringError(msg"unsupported pattern ${pattern.getClass().getSimpleName()}" -> pattern.toLoc) - N - }.toList + val subScrutinee = tuplePattern.getField(index).withAliasVar(subScrutineeVar.withSymbol(symbol)) + symbol.addScrutinee(subScrutinee) + (subScrutineeVar, subScrutinee) + } + pattern match { + case _: s.EmptyPattern => N + case s.NamePattern(name) => + S((name.withFreshSymbol.withScrutinee(tuplePattern.getField(index)), N, aliasVars.reverse)) + case parameterPattern @ (s.ClassPattern(_, _, _) | s.LiteralPattern(_) | s.TuplePattern(_)) => + val subScrutineeVar = freshSubScrutineeVar(parentScrutineeVar, tupleDummyClassName, index) + val symbol = new LocalTermSymbol(subScrutineeVar) + symbol.addScrutinee(tuplePattern.getField(index).withAliasVar(subScrutineeVar)) + S((subScrutineeVar.withSymbol(symbol), S(parameterPattern), aliasVars.reverse)) + case parameterPattern @ s.ConcretePattern(nme @ (Var("true") | Var("false"))) => + val subScrutineeVar = freshSubScrutineeVar(parentScrutineeVar, tupleDummyClassName, index) + val symbol = new LocalTermSymbol(subScrutineeVar) + symbol.addScrutinee(tuplePattern.getField(index).withAliasVar(subScrutineeVar)) + S((subScrutineeVar.withSymbol(symbol), S(parameterPattern), aliasVars.reverse)) + case parameterPattern @ s.AliasPattern(aliasVar, innerPattern) => + println(s"alias pattern found ${subScrutineeVar.name} -> ${aliasVar.name}") + flattenPattern(innerPattern, index, S((subScrutineeVar, subScrutinee)), aliasVar.withFreshSymbol.withScrutinee(subScrutinee) :: aliasVars) + case pattern => + raiseDesugaringError(msg"unsupported pattern" -> pattern.toLoc) + N + } + } + fields.iterator.zipWithIndex.map { case (pattern, index) => flattenPattern(pattern, index, N, Nil) }.toList } private def desugarTuplePattern(fields: Ls[s.Pattern], scrutineeVar: Var, initialScope: Scope)(implicit context: Context): (Scope, c.Split => c.Split) = { val scrutinee = scrutineeVar.getOrCreateScrutinee.withAliasVar(scrutineeVar) - val nestedPatterns = flattenTupleFields(scrutineeVar, fields) + val nestedPatterns = flattenTupleFields(scrutineeVar, scrutinee, fields) val bindFields = nestedPatterns.iterator.zipWithIndex.foldRight[c.Split => c.Split](identity) { case ((N, _), bindNextField) => bindNextField - case ((S(parameter -> _), index), bindNextField) => + case ((S((parameter, _, _)), index), bindNextField) => val indexVar = Var(index.toString).withLoc(parameter.toLoc) bindNextField.andThen { c.Split.Let(false, parameter, Sel(scrutineeVar, indexVar), _) } } diff --git a/shared/src/main/scala/mlscript/ucs/syntax/source.scala b/shared/src/main/scala/mlscript/ucs/syntax/source.scala index 043eff9b..21643975 100644 --- a/shared/src/main/scala/mlscript/ucs/syntax/source.scala +++ b/shared/src/main/scala/mlscript/ucs/syntax/source.scala @@ -8,7 +8,7 @@ import scala.collection.immutable package object source { sealed abstract class Pattern extends Located { override def toString(): String = this match { - case AliasPattern(nme, pattern) => s"$nme @ $pattern" + case AliasPattern(nme, pattern) => s"${nme.name} @ $pattern" case LiteralPattern(literal) => literal.idStr case ConcretePattern(nme) => s"`${nme.name}`" case NamePattern(nme) => nme.name diff --git a/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls b/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls index 9d965bfd..9553e0c3 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls @@ -258,7 +258,7 @@ isDigits("bruh") abstract class ParseResult[out A]: Success[A] | Failure class Success[out A](value: A) extends ParseResult[A] -class Failure(error: Str) extends ParseResult +class Failure(error: Str) extends ParseResult[nothing] //│ abstract class ParseResult[A]: Failure | Success[A] //│ class Success[A](value: A) extends ParseResult //│ class Failure(error: Str) extends ParseResult @@ -272,15 +272,15 @@ fun parseExpr(tokens: List[Str]): [ParseResult[Data], List[Str]] = Cons("(", tail) then parseList(tail) Cons(")", _) then [Failure("Unmatched closing parenthesis."), tokens] Cons(token, tail) and - isDigits(token) then [Success(Literal(IntLit(parseInt(token, 10)))), tail] - else [Success(Symbol(token)), tail] + isDigits(token) then [(Success of Literal of IntLit of parseInt(token, 10)), tail] + else [(Success of Symbol of token), tail] Nil then [Failure("Unexpected end of input, expect either `)` or more tokens."), Nil] fun parseList(tokens: List[Str]): [ParseResult[DataList], List[Str]] = let rec collect(acc, ts) = if ts is Cons(")", tail) then [(Success of DataList of reverse of acc), tail] Cons and parseExpr(ts) is [Success(data), rest] then collect(data :: acc, rest) - [Failure(error), rest] then [Failure(error), rest] + [Failure(_) as failure, rest] then [failure, rest] Nil then [Failure("Unexpected end of input, expect either `)` or more tokens."), Nil] collect(Nil, tokens) //│ fun parseExpr: (tokens: List[Str]) -> [ParseResult[Data], List[Str]] diff --git a/shared/src/test/diff/pretyper/ucs/patterns/AliasPattern.mls b/shared/src/test/diff/pretyper/ucs/patterns/AliasPattern.mls new file mode 100644 index 00000000..d18adc4b --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/patterns/AliasPattern.mls @@ -0,0 +1,57 @@ +:NewDefs + +abstract class Option[out A] +class Some[out A](val value: A) extends Option[A] +module None extends Option[nothing] +//│ abstract class Option[A] +//│ class Some[A](value: A) extends Option +//│ module None extends Option + +:ducs:postprocess.result +fun map(f) = case + Some(x) then Some(f(x)) + None as n then n +//│ Post-processed UCS term: +//│ case case$scrut*‡ of +//│ Some*◊ -> +//│ let ucs$args_case$scrut$Some*† = (Some).unapply(case$scrut,) +//│ let x*‡ = (ucs$args_case$scrut$Some).0 +//│ Some(f(x,),) +//│ None*† -> +//│ let n*† = case$scrut +//│ n +//│ fun map: forall 'a 'A. ('a -> 'A) -> (None | Some['a]) -> (None | Some['A]) + +:ducs:postprocess.result +fun map(f) = case + Some(x as n) then Some of f(n) + None as n then n +//│ Post-processed UCS term: +//│ case case$scrut*‡ of +//│ Some*◊ -> +//│ let ucs$args_case$scrut$Some*† = (Some).unapply(case$scrut,) +//│ let x*‡ = (ucs$args_case$scrut$Some).0 +//│ let n*‡ = x +//│ Some(f(n,),) +//│ None*† -> +//│ let n*† = case$scrut +//│ n +//│ fun map: forall 'a 'A. ('a -> 'A) -> (None | Some['a]) -> (None | Some['A]) + +:ducs:postprocess.result +fun foo = case + Some(Some(a as b) as c) as d then [a, b, c, d] +//│ Post-processed UCS term: +//│ let d*† = case$scrut +//│ case case$scrut*‡ of +//│ Some*◊ -> +//│ let ucs$args_case$scrut$Some*† = (Some).unapply(case$scrut,) +//│ let case$scrut$Some_0*‡ = (ucs$args_case$scrut$Some).0 +//│ case case$scrut$Some_0*‡ of +//│ Some*◊ -> +//│ let ucs$args_case$scrut$Some_0$Some*† = (Some).unapply(case$scrut$Some_0,) +//│ let a*‡ = (ucs$args_case$scrut$Some_0$Some).0 +//│ let b*‡ = a +//│ let c*‡ = case$scrut$Some_0 +//│ [a, b, c, d,] +//│ fun foo: forall 'A 'a. (Some[Some['A]] & 'a) -> ['A, 'A, Some['A], 'a] From 357e518afbf97b57cbaebbaf85b9629a628e235d Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sat, 20 Jan 2024 05:18:23 +0800 Subject: [PATCH 091/143] Fix possible chaining of wildcards --- .../main/scala/mlscript/ucs/stages/PostProcessing.scala | 8 +++++++- shared/src/test/diff/codegen/NewMatching.mls | 4 ++-- shared/src/test/diff/ecoop23/PolymorphicVariants.mls | 2 +- shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls | 2 +- shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls | 4 ++-- shared/src/test/diff/pretyper/ucs/examples/ULC.mls | 2 +- shared/src/test/diff/ucs/Wildcard.mls | 8 ++------ 7 files changed, 16 insertions(+), 14 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index dfb3485c..01953833 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -47,6 +47,7 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => case ((N, cases), _) => (N, cases) } println(s"found ${cases.length} case branches") + println(s"default branch: ${default.fold("")(_.showDbg)}") val postProcessedDefault = default.map(postProcess) // Assemble a `CaseBranches`. val actualFalseBranch = cases.foldRight[CaseBranches]( @@ -65,7 +66,12 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => }(res => s"postProcess ==> ${res.showDbg}") private def trimEmptyTerm(term: Term): Opt[Term] = term match { - case k @ CaseOf(_, cases) => trimEmptyCaseBranches(cases).map(c => k.copy(cases = c)) + case k @ CaseOf(_, cases) => + trimEmptyCaseBranches(cases).flatMap(_ match { + case cases: Case => S(k.copy(cases = cases)) + case NoCases => N + case Wildcard(body) => S(body) + }) case let @ Let(_, _, _, body) => trimEmptyTerm(body).map(t => let.copy(body = t)) case _ => S(term) } diff --git a/shared/src/test/diff/codegen/NewMatching.mls b/shared/src/test/diff/codegen/NewMatching.mls index ea173a88..526c280d 100644 --- a/shared/src/test/diff/codegen/NewMatching.mls +++ b/shared/src/test/diff/codegen/NewMatching.mls @@ -31,7 +31,7 @@ fun sum(v) = Half(_, x) then x None(_) then 0 _ then -1 -//│ fun sum: (Half | None | Object & ~#Half & ~#None & ~#Pos & ~#V0 & ~#V1 & ~#V2 & ~#V22 | Pos | V0 | V1 | V22 | V2) -> Int +//│ fun sum: Object -> Int //│ // Prelude //│ class TypingUnit2 {} //│ const typing_unit2 = new TypingUnit2; @@ -39,7 +39,7 @@ fun sum(v) = //│ globalThis.sum = function sum(v) { //│ return ((() => { //│ let a; -//│ return a = v, a instanceof V0.class ? 0 : a instanceof V22.class ? ((ucs$args_v$V22) => ((v$V22_0) => ((v$V22_1) => v$V22_0 instanceof V2.class ? ((ucs$args_v$V22_0$V2) => ((x1) => ((y1) => v$V22_1 instanceof V2.class ? ((ucs$args_v$V22_1$V2) => ((x2) => ((y2) => x1 + y1 + x2 + y2)(ucs$args_v$V22_1$V2[1]))(ucs$args_v$V22_1$V2[0]))(V2.unapply(v$V22_1)) : -1)(ucs$args_v$V22_0$V2[1]))(ucs$args_v$V22_0$V2[0]))(V2.unapply(v$V22_0)) : -1)(ucs$args_v$V22[1]))(ucs$args_v$V22[0]))(V22.unapply(v)) : a instanceof V2.class ? ((ucs$args_v$V2) => ((a) => ((b) => a + b)(ucs$args_v$V2[1]))(ucs$args_v$V2[0]))(V2.unapply(v)) : a instanceof V1.class ? ((ucs$args_v$V1) => ((a) => a)(ucs$args_v$V1[0]))(V1.unapply(v)) : a instanceof Pos.class ? ((ucs$args_v$Pos) => ((x) => ((ucs$test$0) => ucs$test$0 === true ? x : -1)(x > 0))(ucs$args_v$Pos[0]))(Pos.unapply(v)) : a instanceof None.class ? 0 : a instanceof Half.class ? ((ucs$args_v$Half) => ((x) => x)(ucs$args_v$Half[1]))(Half.unapply(v)) : (v, v, v, v, v, -1); +//│ return a = v, a instanceof V0.class ? 0 : a instanceof V22.class ? ((ucs$args_v$V22) => ((v$V22_0) => ((v$V22_1) => v$V22_0 instanceof V2.class ? ((ucs$args_v$V22_0$V2) => ((x1) => ((y1) => v$V22_1 instanceof V2.class ? ((ucs$args_v$V22_1$V2) => ((x2) => ((y2) => x1 + y1 + x2 + y2)(ucs$args_v$V22_1$V2[1]))(ucs$args_v$V22_1$V2[0]))(V2.unapply(v$V22_1)) : -1)(ucs$args_v$V22_0$V2[1]))(ucs$args_v$V22_0$V2[0]))(V2.unapply(v$V22_0)) : -1)(ucs$args_v$V22[1]))(ucs$args_v$V22[0]))(V22.unapply(v)) : a instanceof V2.class ? ((ucs$args_v$V2) => ((a) => ((b) => a + b)(ucs$args_v$V2[1]))(ucs$args_v$V2[0]))(V2.unapply(v)) : a instanceof V1.class ? ((ucs$args_v$V1) => ((a) => a)(ucs$args_v$V1[0]))(V1.unapply(v)) : a instanceof Pos.class ? ((ucs$args_v$Pos) => ((x) => ((ucs$test$0) => ucs$test$0 === true ? x : -1)(x > 0))(ucs$args_v$Pos[0]))(Pos.unapply(v)) : a instanceof None.class ? 0 : a instanceof Half.class ? ((ucs$args_v$Half) => ((x) => x)(ucs$args_v$Half[1]))(Half.unapply(v)) : -1; //│ })()); //│ }; //│ // End of generated code diff --git a/shared/src/test/diff/ecoop23/PolymorphicVariants.mls b/shared/src/test/diff/ecoop23/PolymorphicVariants.mls index 4ca877b1..f2712eed 100644 --- a/shared/src/test/diff/ecoop23/PolymorphicVariants.mls +++ b/shared/src/test/diff/ecoop23/PolymorphicVariants.mls @@ -206,7 +206,7 @@ module Test3 extends EvalVar, EvalExpr, EvalLambda Test3.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) //│ 'a //│ where -//│ 'a :> Abs[Var] | Numb | Var | App['a] | Abs['a] +//│ 'a :> App['a] | Abs['a] | Abs[Var] | Numb | Var //│ res //│ = Abs {} diff --git a/shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls b/shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls index deffc2a4..003be4a4 100644 --- a/shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls +++ b/shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls @@ -346,7 +346,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 'a. (1 | 2 | 3 | 4 | Object & ~1 & ~2 & ~3 & ~4) -> 'a +//│ fun mk: forall 'a. Object -> 'a //│ where //│ 'a :> Outside['a] | Translate['a] | Intersect['a] | Union['a] | Scale['a] diff --git a/shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls b/shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls index beb48255..9063a7c4 100644 --- a/shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls +++ b/shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls @@ -325,9 +325,9 @@ 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 'a. (1 | 2 | 3 | 4 | Object & ~1 & ~2 & ~3 & ~4) -> 'a +//│ fun mk: forall 'a. Object -> 'a //│ where -//│ 'a :> Outside['a] | Union['a] | Scale['a] | Translate['a] | Intersect['a] +//│ 'a :> Outside['a] | Translate['a] | Intersect['a] | Union['a] | Scale['a] :re TestElim.eliminate(mk(100)) diff --git a/shared/src/test/diff/pretyper/ucs/examples/ULC.mls b/shared/src/test/diff/pretyper/ucs/examples/ULC.mls index 18ab7464..89e4d315 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/ULC.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/ULC.mls @@ -236,7 +236,7 @@ fun hasFree(t, x) = Abs(Var(_), body) then hasFree(body, x) App(lhs, rhs) then hasFree(lhs, x) || hasFree(rhs, x) _ then false -//│ fun hasFree: (Abs | App | Object & ~#Abs & ~#App & ~#Var | Var, Eql[Str]) -> Bool +//│ fun hasFree: (Object, Eql[Str]) -> Bool fun showHasFree(t, n) = showTerm(t) ++ (if hasFree(t, n) then " has " else " DOES NOT have ") ++ "free variable " ++ n diff --git a/shared/src/test/diff/ucs/Wildcard.mls b/shared/src/test/diff/ucs/Wildcard.mls index 3c6f2e8a..738e8b87 100644 --- a/shared/src/test/diff/ucs/Wildcard.mls +++ b/shared/src/test/diff/ucs/Wildcard.mls @@ -212,12 +212,8 @@ fun w5(y) = //│ Gamma*◊ -> "gamma" //│ Delta*◊ -> "delta" //│ Beta*◊ -> "beta" -//│ _ -> -//│ case y*‡ of -//│ _ -> -//│ case y*‡ of -//│ _ -> "unknown" -//│ fun w5: (Alpha | Beta | Delta | Gamma | Object & ~#Alpha & ~#Beta & ~#Delta & ~#Gamma) -> ("alpha" | "beta" | "delta" | "gamma" | "unknown") +//│ _ -> "unknown" +//│ fun w5: Object -> ("alpha" | "beta" | "delta" | "gamma" | "unknown") w5(0) w5(Alpha()) From 21d40e6b4be4a5aba8c0d7a140d4bb861462ddad Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sun, 21 Jan 2024 00:11:52 +0800 Subject: [PATCH 092/143] Improve post-processing and warning of unreachable cases --- .../main/scala/mlscript/ucs/Desugarer.scala | 24 +++--- .../mlscript/ucs/stages/Normalization.scala | 8 +- .../mlscript/ucs/stages/PostProcessing.scala | 80 +++++++++++-------- .../test/diff/pretyper/ucs/RecordPattern.mls | 4 +- .../pretyper/ucs/coverage/Unreachable.mls | 4 +- .../pretyper/ucs/patterns/SimpleTuple.mls | 8 +- shared/src/test/diff/ucs/DirectLines.mls | 4 +- .../src/test/diff/ucs/NuPlainConditionals.mls | 8 +- .../src/test/diff/ucs/PlainConditionals.mls | 8 +- shared/src/test/diff/ucs/WeirdIf.mls | 16 ++-- shared/src/test/diff/ucs/Wildcard.mls | 8 +- 11 files changed, 87 insertions(+), 85 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/Desugarer.scala b/shared/src/main/scala/mlscript/ucs/Desugarer.scala index 35af9094..8c3a7a21 100644 --- a/shared/src/main/scala/mlscript/ucs/Desugarer.scala +++ b/shared/src/main/scala/mlscript/ucs/Desugarer.scala @@ -5,7 +5,7 @@ import syntax.{source => s, core => c}, stages._, context.{Context, Scrutinee} import mlscript.ucs.display.{showNormalizedTerm, showSplit} import mlscript.pretyper.{PreTyper, Scope} import mlscript.pretyper.symbol._ -import mlscript.{If, Loc, Message, Var}, Message.MessageContext, mlscript.Diagnostic +import mlscript.{If, Loc, Located, Message, Var}, Message.MessageContext, mlscript.Diagnostic import mlscript.utils._, shorthands._ import syntax.core.{Branch, Split} @@ -22,6 +22,14 @@ trait Desugarer extends Transformation protected def raiseDesugaringError(messages: (Message -> Opt[Loc])*): Unit = raiseError(Diagnostic.Desugaring, messages: _*) + protected def reportUnreachableCase[T <: Located](unreachable: Located, subsumedBy: T, onlyIf: Bool = true): T = { + if (onlyIf) raiseDesugaringWarning( + msg"this case is unreachable" -> unreachable.toLoc, + msg"because it is subsumed by the branch" -> subsumedBy.toLoc + ) + subsumedBy + } + /** A shorthand function to raise _desugaring_ warnings without specifying the source. */ protected def raiseDesugaringWarning(messages: (Message -> Opt[Loc])*): Unit = raiseWarning(Diagnostic.Desugaring, messages: _*) @@ -153,12 +161,7 @@ trait Desugarer extends Transformation if (those === s.Split.Nil) these else (these match { case s.Split.Cons(head, tail) => s.Split.Cons(head, tail ++ those) case s.Split.Let(rec, nme, rhs, tail) => s.Split.Let(rec, nme, rhs, tail ++ those) - case s.Split.Else(_) => - raiseDesugaringWarning( - msg"unreachable case" -> those.toLoc, - msg"because this branch covers the case" -> these.toLoc - ) - these + case s.Split.Else(_) => reportUnreachableCase(those, these) case s.Split.Nil => those }) } @@ -178,12 +181,7 @@ trait Desugarer extends Transformation if (those === c.Split.Nil) these else (these match { case me: c.Split.Cons => me.copy(tail = me.tail ++ those) case me: c.Split.Let => me.copy(tail = me.tail ++ those) - case _: c.Split.Else => - raiseDesugaringWarning( - msg"the case is unreachable" -> those.toLoc, - msg"because this branch covers the case" -> these.toLoc - ) - these + case _: c.Split.Else => reportUnreachableCase(those, these) case c.Split.Nil => those }) } diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 800cebb0..c0e26a4e 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -22,13 +22,7 @@ trait Normalization { self: Desugarer with Traceable => shouldReportDiscarded: Bool ): Split = if (these.hasElse) { - if (those =/= Split.Nil && shouldReportDiscarded) { - raiseDesugaringWarning( - msg"the case is unreachable" -> those.toLoc, - msg"because this branch already covers the case" -> these.toLoc - ) - } - these + reportUnreachableCase(those, these, onlyIf = those =/= Split.Nil && shouldReportDiscarded) } else (these match { case these @ Split.Cons(head, tail) => these.copy(tail = fillImpl(tail, those)) diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index 01953833..a955e9f2 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -27,24 +27,31 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => // Post-process the false branch. println("FALSE") // Get all patterns, except the current one `pat`. - val patternList = scrutinee.patternsIterator.filter(!_.matches(pat)).toList - println(s"found ${patternList.length} patterns to distentangle: ${patternList.iterator.map(_.showDbg).mkString(", ")}") - val (default, cases) = patternList - // For each case class name, distangle case branch body terms from the false branch. + val patterns = scrutinee.patternsIterator.filter(!_.matches(pat)).toList + println(s"found ${patterns.length} patterns to distentangle: ${patterns.iterator.map(_.showDbg).mkString(", ")}") + val (default, cases) = patterns + // Search each pattern in `falseBranch`. If there exists a branch for + // the given pattern, we separate the branch body from the term. The + // leftover term will be used in next iteration, until there is no + // term left (i.e., `leftoverTerm` is `N`). .foldLeft[Opt[Term] -> Ls[(Pattern, Opt[Loc], Term)]](S(falseBranch) -> Nil) { + // If the remaining term is not empty, we continue our search. case ((S(remainingTerm), cases), pattern) => - println(s"searching for case: ${pattern.showDbg}") + println(s"searching for branches of pattern ${pattern.showDbg}") val (leftoverTerm, extracted) = disentangleTerm(remainingTerm)( context, scrutineeVar, scrutinee, pattern) trimEmptyTerm(leftoverTerm) -> (extracted match { + // `remainingTerm` does not have branches for `pattern`. case N => - println(s"no extracted term about ${pattern.showDbg}") + println(s"cannot extract pattern ${pattern.showDbg}") cases + // We extracted a term and it needs to be further post-processed. case terms @ S(extractedTerm) => - println(s"extracted a term about ${pattern.showDbg}") + println(s"extracted a term of pattern ${pattern.showDbg}") (pattern, pattern.firstOccurrence, postProcess(extractedTerm)) :: cases }) - case ((N, cases), _) => (N, cases) + // If no terms are left, we pass on `acc` until the iteration ends. + case (acc @ (N, _), _) => acc } println(s"found ${cases.length} case branches") println(s"default branch: ${default.fold("")(_.showDbg)}") @@ -58,7 +65,7 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => // Assemble the final `CaseOf`. top.copy(cases = fst.copy(body = processedTrueBranch, rest = actualFalseBranch) (refined = fst.refined)) - // We recursively process the body of as`Let` bindings. + // We recursively process the body of `Let` bindings. case let @ Let(_, _, _, body) => let.copy(body = postProcess(body)) // Otherwise, this is not a part of a normalized term. case other => println(s"CANNOT post-process ${other.showDbg}"); other @@ -88,6 +95,10 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => } } + /** + * Merge two optional terms. If both are not empty we will call the other + * `mergeTerms`. + */ private def mergeTerms(t1: Opt[Term], t2: Opt[Term]): Opt[Term] = (t1, t2) match { case (N, N) => N @@ -96,27 +107,29 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => case (S(t1), S(t2)) => S(mergeTerms(t1, t2)) } - private def mergeTerms(t1: Term, t2: Term): Term = - trace(s"mergeTerms <== ${t1.showDbg} ${t2.showDbg}") { - t1 match { - case t1 @ Let(_, _, _, body) => t1.copy(body = mergeTerms(body, t2)) - case t1 @ CaseOf(scrutinee: Var, cases) => - t1.copy(cases = mergeTermIntoCaseBranches(t2, cases)) - case _ => - println(s"CANNOT merge. Discard ${t2.showDbg}.") - t1 - } + /** + * Merge two terms. In most cases, two terms cannot be merged. This function + * replaces `Wildcard` in `t1` with `t2`. + */ + private def mergeTerms(t1: Term, t2: Term): Term = { + def recTerm(lhs: Term, rhs: Term): Term = lhs match { + case lhs @ Let(_, _, _, body) => lhs.copy(body = mergeTerms(body, rhs)) + case lhs @ CaseOf(scrutinee: Var, cases) => + lhs.copy(cases = recCaseBranches(cases, rhs)) + case _ => reportUnreachableCase(rhs, lhs) + } + def recCaseBranches(lhs: CaseBranches, rhs: Term): CaseBranches = lhs match { + case NoCases => Wildcard(rhs).withLocOf(rhs) + case Wildcard(body) => Wildcard(mergeTerms(body, rhs)) + case lhs @ Case(_, _, rest) => + lhs.copy(rest = recCaseBranches(rest, rhs))(refined = lhs.refined) + } + trace(s"mergeTerms <==") { + println(s"LHS: ${t1.showDbg}") + println(s"RHS: ${t2.showDbg}") + recTerm(t1, t2) }(merged => s"mergedTerms ==> ${merged.showDbg}") - - private def mergeTermIntoCaseBranches(term: Term, cases: CaseBranches): CaseBranches = - trace(s"mergeTermIntoCaseBranches <== ${term.describe} ${cases}") { - cases match { - case NoCases => Wildcard(term).withLocOf(term) - case Wildcard(body) => Wildcard(mergeTerms(body, term)) - case cases @ Case(_, _, rest) => - cases.copy(rest = mergeTermIntoCaseBranches(term, rest))(refined = cases.refined) - } - }() + } /** * Disentangle case branches that match `scrutinee` against `className` from `term`. @@ -160,7 +173,9 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => }({ case (n, y) => s"disentangleTerm ==> `${n.showDbg}` and `${y.fold("")(_.showDbg)}`" }) } - /** Helper function for `disentangleTerm`. */ + /** + * Helper function for `disentangleTerm`. + */ private def disentangleMatchedCaseBranches(cases: CaseBranches)(implicit context: Context, scrutineeVar: Var, @@ -184,7 +199,6 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => } } - /** Helper function for `disentangleTerm`. */ private def disentangleUnmatchedCaseBranches(cases: CaseBranches)(implicit context: Context, @@ -210,10 +224,6 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => } object PostProcessing { - class PostProcessingException(val messages: Ls[Message -> Opt[Loc]]) extends Throwable { - def this(message: Message, location: Opt[Loc]) = this(message -> location :: Nil) - } - private object typeSymbolOrdering extends Ordering[TypeSymbol] { override def compare(x: TypeSymbol, y: TypeSymbol): Int = { (x.defn.toLoc, y.defn.toLoc) match { diff --git a/shared/src/test/diff/pretyper/ucs/RecordPattern.mls b/shared/src/test/diff/pretyper/ucs/RecordPattern.mls index dead7496..68f99b1b 100644 --- a/shared/src/test/diff/pretyper/ucs/RecordPattern.mls +++ b/shared/src/test/diff/pretyper/ucs/RecordPattern.mls @@ -9,10 +9,10 @@ fun take_1(p) = //│ ╔══[ERROR] unknown pattern '{' {x: x, y: y} '}' //│ ║ l.7: { x, y } then x + y //│ ╙── ^^^^^^^^ -//│ ╔══[WARNING] the case is unreachable +//│ ╔══[WARNING] this case is unreachable //│ ║ l.8: else 0 //│ ║ ^ -//│ ╟── because this branch covers the case +//│ ╟── because it is subsumed by the branch //│ ║ l.7: { x, y } then x + y //│ ╙── ^^^^^ //│ ╔══[ERROR] identifier not found: x diff --git a/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls b/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls index 6b3df49e..ecf896c4 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls @@ -49,7 +49,7 @@ fun unreachable_1(x) = else "screen" Some(xv) then "sin" None then "tan" -//│ ╔══[WARNING] the case is unreachable +//│ ╔══[WARNING] this case is unreachable //│ ║ l.46: if x is //│ ║ ^^^^ //│ ║ l.47: _ and @@ -62,7 +62,7 @@ fun unreachable_1(x) = //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.51: None then "tan" //│ ║ ^^^^^^^^^^^^^^^^^^^ -//│ ╟── because this branch covers the case +//│ ╟── because it is subsumed by the branch //│ ║ l.49: else "screen" //│ ╙── ^^^^^^^^ //│ fun unreachable_1: (Object & ~#Some | Some[Eql[1]]) -> ("screen" | "tmux") diff --git a/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls b/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls index 0a0144e3..57d37d53 100644 --- a/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls +++ b/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls @@ -47,14 +47,14 @@ fun discarded_cases(thing) = if thing is [x, y] then x + y Point(x, y) then x + y -//│ ╔══[WARNING] the case is unreachable +//│ ╔══[WARNING] this case is unreachable //│ ║ l.47: if thing is //│ ║ ^^^^^^^^ //│ ║ l.48: [x, y] then x + y //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.49: Point(x, y) then x + y //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── because this branch covers the case +//│ ╟── because it is subsumed by the branch //│ ║ l.48: [x, y] then x + y //│ ╙── ^^^^^ //│ class Point(x: Int, y: Int) @@ -111,10 +111,10 @@ fun not_working(x) = a + b + c else 0 -//│ ╔══[WARNING] the case is unreachable +//│ ╔══[WARNING] this case is unreachable //│ ║ l.113: 0 //│ ║ ^ -//│ ╟── because this branch covers the case +//│ ╟── because it is subsumed by the branch //│ ║ l.111: a + b + c //│ ╙── ^^^^^^^^^ //│ fun not_working: {0: Int, 1: Int, 2: Int} -> Int diff --git a/shared/src/test/diff/ucs/DirectLines.mls b/shared/src/test/diff/ucs/DirectLines.mls index a0468437..5028b2ca 100644 --- a/shared/src/test/diff/ucs/DirectLines.mls +++ b/shared/src/test/diff/ucs/DirectLines.mls @@ -46,10 +46,10 @@ fun f(a, b) = 2 then 2 _ then 7 else 3 -//│ ╔══[WARNING] the case is unreachable +//│ ╔══[WARNING] this case is unreachable //│ ║ l.48: else 3 //│ ║ ^ -//│ ╟── because this branch covers the case +//│ ╟── because it is subsumed by the branch //│ ║ l.47: _ then 7 //│ ╙── ^ //│ fun f: (Num, Num) -> (0 | 1 | 2 | 7) diff --git a/shared/src/test/diff/ucs/NuPlainConditionals.mls b/shared/src/test/diff/ucs/NuPlainConditionals.mls index 299655c6..4f24071e 100644 --- a/shared/src/test/diff/ucs/NuPlainConditionals.mls +++ b/shared/src/test/diff/ucs/NuPlainConditionals.mls @@ -58,8 +58,8 @@ fun foo(x) = x is //│ ║ ^^^^ //│ ║ l.55: Int //│ ╙── ^^^^^ -//│ ╔══[WARNING] the case is unreachable -//│ ╙── because this branch covers the case +//│ ╔══[WARNING] this case is unreachable +//│ ╙── because it is subsumed by the branch //│ fun foo: anything -> true // TODO proper error @@ -71,8 +71,8 @@ fun foo(x) = x is //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.68: Int //│ ╙── ^^^^^ -//│ ╔══[WARNING] the case is unreachable -//│ ╙── because this branch covers the case +//│ ╔══[WARNING] this case is unreachable +//│ ╙── because it is subsumed by the branch //│ fun foo: anything -> true // TODO support `|` diff --git a/shared/src/test/diff/ucs/PlainConditionals.mls b/shared/src/test/diff/ucs/PlainConditionals.mls index 7dc3b959..56fdc6d1 100644 --- a/shared/src/test/diff/ucs/PlainConditionals.mls +++ b/shared/src/test/diff/ucs/PlainConditionals.mls @@ -58,8 +58,8 @@ fun foo(x) = x is //│ ║ ^^^^ //│ ║ l.55: Int //│ ╙── ^^^^^ -//│ ╔══[WARNING] the case is unreachable -//│ ╙── because this branch covers the case +//│ ╔══[WARNING] this case is unreachable +//│ ╙── because it is subsumed by the branch //│ fun foo: anything -> true // TODO proper error @@ -71,8 +71,8 @@ fun foo(x) = x is //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.68: Int //│ ╙── ^^^^^ -//│ ╔══[WARNING] the case is unreachable -//│ ╙── because this branch covers the case +//│ ╔══[WARNING] this case is unreachable +//│ ╙── because it is subsumed by the branch //│ fun foo: anything -> true // TODO support `|` diff --git a/shared/src/test/diff/ucs/WeirdIf.mls b/shared/src/test/diff/ucs/WeirdIf.mls index 1f34f67b..bfb09a65 100644 --- a/shared/src/test/diff/ucs/WeirdIf.mls +++ b/shared/src/test/diff/ucs/WeirdIf.mls @@ -5,16 +5,16 @@ if _ then 0 else 0 else 1 -//│ ╔══[WARNING] unreachable case +//│ ╔══[WARNING] this case is unreachable //│ ║ l.6: else 0 //│ ║ ^ -//│ ╟── because this branch covers the case +//│ ╟── because it is subsumed by the branch //│ ║ l.5: _ then 0 //│ ╙── ^ -//│ ╔══[WARNING] unreachable case +//│ ╔══[WARNING] this case is unreachable //│ ║ l.7: else 1 //│ ║ ^ -//│ ╟── because this branch covers the case +//│ ╟── because it is subsumed by the branch //│ ║ l.5: _ then 0 //│ ╙── ^ //│ 0 @@ -23,10 +23,10 @@ else 1 :w if else 0 else 1 -//│ ╔══[WARNING] unreachable case +//│ ╔══[WARNING] this case is unreachable //│ ║ l.25: if else 0 else 1 //│ ║ ^ -//│ ╟── because this branch covers the case +//│ ╟── because it is subsumed by the branch //│ ║ l.25: if else 0 else 1 //│ ╙── ^ //│ 0 @@ -35,10 +35,10 @@ if else 0 else 1 :w fun f(x) = if x is else 0 else 1 -//│ ╔══[WARNING] the case is unreachable +//│ ╔══[WARNING] this case is unreachable //│ ║ l.37: fun f(x) = if x is else 0 else 1 //│ ║ ^ -//│ ╟── because this branch covers the case +//│ ╟── because it is subsumed by the branch //│ ║ l.37: fun f(x) = if x is else 0 else 1 //│ ╙── ^ //│ fun f: anything -> 0 diff --git a/shared/src/test/diff/ucs/Wildcard.mls b/shared/src/test/diff/ucs/Wildcard.mls index 738e8b87..5dc4fa10 100644 --- a/shared/src/test/diff/ucs/Wildcard.mls +++ b/shared/src/test/diff/ucs/Wildcard.mls @@ -107,10 +107,10 @@ w3(0, _ => false) // + => 1 fun w3_1(x, f) = if f(x) is _ then 0 else 1 -//│ ╔══[WARNING] the case is unreachable +//│ ╔══[WARNING] this case is unreachable //│ ║ l.109: if f(x) is _ then 0 else 1 //│ ║ ^ -//│ ╟── because this branch covers the case +//│ ╟── because it is subsumed by the branch //│ ║ l.109: if f(x) is _ then 0 else 1 //│ ╙── ^ //│ fun w3_1: forall 'a. ('a, 'a -> anything) -> 0 @@ -126,10 +126,10 @@ w3_1(0, _ => false) :w fun w3_1_1(x, f) = if f(x) is a then a else 0 -//│ ╔══[WARNING] the case is unreachable +//│ ╔══[WARNING] this case is unreachable //│ ║ l.128: if f(x) is a then a else 0 //│ ║ ^ -//│ ╟── because this branch already covers the case +//│ ╟── because it is subsumed by the branch //│ ║ l.128: if f(x) is a then a else 0 //│ ╙── ^ //│ fun w3_1_1: forall 'a 'b. ('a, 'a -> 'b) -> 'b From 9dc3e1cfb6566d726b5e856e1213e950170ca18b Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sun, 21 Jan 2024 02:55:23 +0800 Subject: [PATCH 093/143] Support parsing `else` and `_ then` in `IfOpsApp` --- .../src/main/scala/mlscript/NewParser.scala | 33 ++++++++-- shared/src/test/diff/parser/IfThenElse.mls | 2 +- .../ucs/examples/BinarySearchTree.mls | 7 +- shared/src/test/diff/ucs/LeadingAnd.mls | 2 +- shared/src/test/diff/ucs/ParseFailures.mls | 64 +++++++++---------- shared/src/test/diff/ucs/ParserFailures.mls | 32 +++++++++- 6 files changed, 92 insertions(+), 48 deletions(-) diff --git a/shared/src/main/scala/mlscript/NewParser.scala b/shared/src/main/scala/mlscript/NewParser.scala index 90926da3..c302824b 100644 --- a/shared/src/main/scala/mlscript/NewParser.scala +++ b/shared/src/main/scala/mlscript/NewParser.scala @@ -1058,14 +1058,26 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], newDefs: Bo R(res) } case L(rhs) => - L(IfOpsApp(acc, opIfBlock(opv -> rhs :: Nil))) + val (opsRhss, els) = opIfBlock(opv -> rhs :: Nil) + val opsApp = IfOpsApp(acc, opsRhss) + L(els.fold[IfBody](opsApp)(trm => IfBlock(L(opsApp) :: L(IfElse(trm)) :: Nil))) } } - final def opIfBlock(acc: Ls[Var -> IfBody])(implicit et: ExpectThen, fe: FoundErr): Ls[Var -> IfBody] = wrap(acc) { l => + final def opIfBlock(acc: Ls[Var -> IfBody])(implicit et: ExpectThen, fe: FoundErr): (Ls[Var -> IfBody], Opt[Term]) = wrap(acc) { l => cur match { case (NEWLINE, _) :: c => // TODO allow let bindings... consume c match { + case (IDENT("_", false), wcLoc) :: _ => + exprOrIf(0) match { + case R(rhs) => + err(msg"expect an operator branch" -> S(wcLoc) :: Nil) + (acc.reverse, N) + case L(IfThen(_, els)) => (acc.reverse, S(els)) + case L(rhs) => + err(msg"expect 'then' after the wildcard" -> rhs.toLoc :: Nil) + (acc.reverse, N) + } case (IDENT(opStr2, true), opLoc2) :: _ => consume val rhs = exprOrIf(0) @@ -1075,12 +1087,23 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], newDefs: Bo case L(rhs) => opIfBlock(Var(opStr2).withLoc(S(opLoc2)) -> rhs :: acc) } - case _ => + case (KEYWORD("else"), elseLoc) :: tail => + consume + exprOrIf(0) match { + case R(rhs) => (acc.reverse, S(rhs)) + case L(rhs) => + err(msg"expect a term" -> rhs.toLoc :: Nil) + (acc.reverse, N) + } + case (_, headLoc) :: _ => // printDbg(c) - ??? + err(msg"expect an operator" -> S(headLoc) :: Nil) + (acc.reverse, N) + case Nil => + (acc.reverse, N) } case _ => - acc.reverse + (acc.reverse, N) } } diff --git a/shared/src/test/diff/parser/IfThenElse.mls b/shared/src/test/diff/parser/IfThenElse.mls index 7255013b..32633f8b 100644 --- a/shared/src/test/diff/parser/IfThenElse.mls +++ b/shared/src/test/diff/parser/IfThenElse.mls @@ -128,7 +128,7 @@ if a == 1 then "true" _ then "true" //│ |#if| |a|→|>| |0| |#then| |"false"|↵|==| |1| |#then| |"true"|↵|_| |#then| |"true"|←| -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ Parsed: {if ‹a ‹· > (0) then "false"; · == (1) then "true"›; else "true"›} if a == 0 then "false" diff --git a/shared/src/test/diff/pretyper/ucs/examples/BinarySearchTree.mls b/shared/src/test/diff/pretyper/ucs/examples/BinarySearchTree.mls index 65d70f8c..16f5d3b5 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/BinarySearchTree.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/BinarySearchTree.mls @@ -85,14 +85,13 @@ fun insert(t, v) = if t is Empty then Node(v, Empty, Empty) //│ fun insert: forall 'A. (Empty | Node[Num & 'A], Num & 'A) -> (Node[nothing] | Node['A]) -// FIXME fun insert'(t, v) = if t is Node(v', l, r) and v < v' then Node(v', insert(l, v), r) > v' then Node(v', l, insert(r, v)) else t Empty then Node(v, Empty, Empty) -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ fun insert': forall 'A. (Empty | Node[Num & 'A], Num & 'A) -> (Node[nothing] | Node['A]) insert(Empty, 0) |> show insert(Node(0, Empty, Empty), 0) |> show @@ -356,9 +355,9 @@ fun extractMin(t) = extractMin(l) is Pair(m, l') then Pair(m, Node(v, l', r)) Empty then Pair(None, Empty) -//│ fun extractMin: forall 'A 'A0 'B 'T 'A1. (Empty | Node['A0 & 'T & 'A1]) -> Pair[in 'A out 'A | None, in Tree['A1] & 'B out Empty | 'B | Node['A1] | Tree['A0]] +//│ fun extractMin: forall 'A 'B 'T 'A0 'A1. (Empty | Node['A1 & 'T & 'A]) -> Pair[in 'A0 out 'A0 | None, in Tree['A] & 'B out Empty | 'B | Node['A] | Tree['A1]] //│ where -//│ 'A :> None | Some['T] +//│ 'A0 :> None | Some['T] extractMin(example1).first ?? "not found" extractMin(example1).second |> show diff --git a/shared/src/test/diff/ucs/LeadingAnd.mls b/shared/src/test/diff/ucs/LeadingAnd.mls index d25af764..8df970fa 100644 --- a/shared/src/test/diff/ucs/LeadingAnd.mls +++ b/shared/src/test/diff/ucs/LeadingAnd.mls @@ -30,5 +30,5 @@ fun f(a, b) = if a is Some(av) and b is Some(bv) then av + bv -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ fun f: (Some[Int], Some[Int]) -> Int diff --git a/shared/src/test/diff/ucs/ParseFailures.mls b/shared/src/test/diff/ucs/ParseFailures.mls index 3d1e1014..6ff6af09 100644 --- a/shared/src/test/diff/ucs/ParseFailures.mls +++ b/shared/src/test/diff/ucs/ParseFailures.mls @@ -1,45 +1,39 @@ :NewDefs :NoJS -// FIXME type Tree[A] = Node[A] | Empty -module Empty { - fun contains(wanted) = false -} -class Node[A](value: int, left: Tree[A], right: Tree[A]) { - fun contains(wanted) = if wanted - <= value then left.find(wanted) - >= value then right.find(wanted) - else true -} -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +module Empty +class Node[A](value: Int, left: Tree[A], right: Tree[A]) +//│ type Tree[A] = Empty | Node[A] +//│ module Empty +//│ class Node[A](value: Int, left: Tree[A], right: Tree[A]) -// FIXME -type Tree[A] = Node[A] | Empty -module Empty { - fun contains(wanted) = false -} -class Node[A](value: int, left: Tree[A], right: Tree[A]) { - fun contains(wanted) = if wanted - <= value then left.find(wanted) - >= value then right.find(wanted) - _ true -} -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +fun contains(node, wanted) = + if node is + Node(value, left, right) and wanted + <= value then contains(left, wanted) + >= value then contains(right, wanted) + else true + Empty then false +//│ fun contains: forall 'A. (Empty | Node['A], Num) -> Bool + +fun contains'(node, wanted) = + if node is + Node(value, left, right) and wanted + <= value then contains'(left, wanted) + >= value then contains'(right, wanted) + _ then true + Empty then false +//│ fun contains': forall 'A. (Empty | Node['A], Num) -> Bool -// FIXME +:pe +class Z() +class O() fun foo(x, y) = if x is Z() and y is O() then 0 else 1 //│ ╔══[PARSE ERROR] Unexpected 'else' keyword here -//│ ║ l.32: Z() and y is O() then 0 else 1 +//│ ║ l.33: Z() and y is O() then 0 else 1 //│ ╙── ^^^^ -//│ ╔══[ERROR] type identifier `Z` not found -//│ ║ l.32: Z() and y is O() then 0 else 1 -//│ ╙── ^ -//│ ╔══[ERROR] type identifier `O` not found -//│ ║ l.32: Z() and y is O() then 0 else 1 -//│ ╙── ^ -//│ ╔══[ERROR] type identifier not found: Z -//│ ║ l.32: Z() and y is O() then 0 else 1 -//│ ╙── ^ -//│ fun foo: (nothing, anything) -> error +//│ class Z() +//│ class O() +//│ fun foo: (Z, O) -> 0 diff --git a/shared/src/test/diff/ucs/ParserFailures.mls b/shared/src/test/diff/ucs/ParserFailures.mls index fca068c5..8cad1cc3 100644 --- a/shared/src/test/diff/ucs/ParserFailures.mls +++ b/shared/src/test/diff/ucs/ParserFailures.mls @@ -6,7 +6,23 @@ if x == 0 then "bad" let y = f(z) == y * y then 0 -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ ╔══[PARSE ERROR] expect an operator +//│ ║ l.7: let y = f(z) +//│ ╙── ^^^ +//│ ╔══[PARSE ERROR] Unexpected 'let' keyword here +//│ ║ l.7: let y = f(z) +//│ ╙── ^^^ +//│ ╔══[ERROR] missing else branch +//│ ║ l.6: == 0 then "bad" +//│ ╙── ^^^^^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.5: if x +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.6: == 0 then "bad" +//│ ║ ^^^^^ +//│ ╙── expression of type `Bool` is not an instance of type `true` +//│ "bad" // FIXME: Interleaved let bindings are not implemented in `IfOpsApp`. fun tt(x) = @@ -14,5 +30,17 @@ fun tt(x) = is A() then "A" let y = 0 is B() then "B" -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ ╔══[PARSE ERROR] expect an operator +//│ ║ l.31: let y = 0 +//│ ╙── ^^^ +//│ ╔══[PARSE ERROR] Unexpected 'let' keyword here +//│ ║ l.31: let y = 0 +//│ ╙── ^^^ +//│ ╔══[ERROR] type identifier `A` not found +//│ ║ l.30: is A() then "A" +//│ ╙── ^ +//│ ╔══[ERROR] type identifier not found: A +//│ ║ l.30: is A() then "A" +//│ ╙── ^ +//│ fun tt: nothing -> error From 1cbe59f7e07d27808dc3a413b5572239dea69767 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sun, 21 Jan 2024 03:18:25 +0800 Subject: [PATCH 094/143] Improve the implementation of `freeVars` --- shared/src/main/scala/mlscript/helpers.scala | 64 +++++++++++++++----- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/shared/src/main/scala/mlscript/helpers.scala b/shared/src/main/scala/mlscript/helpers.scala index 8d9d2578..01936a8e 100644 --- a/shared/src/main/scala/mlscript/helpers.scala +++ b/shared/src/main/scala/mlscript/helpers.scala @@ -802,21 +802,57 @@ trait FieldImpl extends Located { self: Field => trait Located { def children: List[Located] - lazy val freeVars: Set[Var] = this match { - case v: Var => Set.single(v) - case Sel(receiver, _) => receiver.freeVars - case Let(true, nme, rhs, body) => body.freeVars ++ rhs.freeVars - nme - case Let(false, nme, rhs, body) => body.freeVars - nme ++ rhs.freeVars - case Lam(tup: Tup, body) => body.freeVars -- tup.freeVars - case Tup(fields) => fields.iterator.flatMap(_._2.value.freeVars.iterator).toSet - case Blk(stmts) => stmts.iterator.foldRight(Set.empty[Var]) { - case (NuFunDef(isLetRec, nme, _, _, L(rhs)), fvs) => fvs - nme ++ (isLetRec match { - case N | S(true) => rhs.freeVars - nme - case S(false) => rhs.freeVars - }) - case (statement, fvs) => fvs ++ statement.freeVars + lazy val freeVars: Set[Var] = { + def statements(stmts: Ls[Statement]): Set[Var] = + stmts.iterator.foldRight(Set.empty[Var]) { + case (NuFunDef(isLetRec, nme, _, _, L(rhs)), fvs) => fvs - nme ++ (isLetRec match { + case N | S(true) => rhs.freeVars - nme + case S(false) => rhs.freeVars + }) + case (td: NuTypeDef, fvs) => + if (td.kind === Mod || td.kind === Cls || td.kind === Trt) + fvs - td.nameVar + else + fvs + case (statement, fvs) => fvs ++ statement.freeVars + } + this match { + // TypingUnit + case TypingUnit(entities) => statements(entities) + // Terms + case v: Var => Set.single(v) + case Lam(tup: Tup, body) => body.freeVars -- tup.freeVars + case App(lhs, rhs) => lhs.freeVars ++ rhs.freeVars + case Tup(fields) => fields.iterator.flatMap(_._2.value.freeVars.iterator).toSet + case Rcd(fields) => fields.iterator.flatMap(_._2.value.freeVars.iterator).toSet + case Sel(receiver, _) => receiver.freeVars + case Let(true, nme, rhs, body) => body.freeVars ++ rhs.freeVars - nme + case Let(false, nme, rhs, body) => body.freeVars - nme ++ rhs.freeVars + case Blk(stmts) => statements(stmts) + case Bra(_, trm) => trm.freeVars + case Asc(trm, _) => trm.freeVars + case Bind(lhs, rhs) => lhs.freeVars ++ rhs.freeVars + case Test(trm, _) => trm.freeVars + case With(trm, fields) => trm.freeVars ++ fields.freeVars + case CaseOf(trm, cases) => cases.foldLeft(trm.freeVars)(_ ++ _._2.freeVars)(_ ++ _.fold(Set.empty[Var])(_.freeVars)) + case Subs(arr, idx) => arr.freeVars ++ idx.freeVars + case Assign(lhs, rhs) => lhs.freeVars ++ rhs.freeVars + case Splc(fields) => fields.iterator.flatMap(_.fold(_.freeVars, _.value.freeVars)).toSet + case New(head, body) => head.fold(Set.empty[Var])(_._2.freeVars) ++ body.freeVars + case NuNew(cls) => cls.freeVars + // Because `IfBody` uses the term to represent the pattern, direct + // traversal is not correct. + case If(_, _) => Set.empty + case TyApp(lhs, _) => lhs.freeVars + case Where(body, where) => body.freeVars ++ statements(where) + case Forall(_, body) => body.freeVars + case Inst(body) => body.freeVars + case Super() => Set.empty + case Eqn(lhs, rhs) => lhs.freeVars ++ rhs.freeVars + case Rft(base, decls) => base.freeVars ++ decls.freeVars + // Fallback for unsupported terms which is incorrect most of the time. + case _ => children.iterator.flatMap(_.freeVars.iterator).toSet } - case _ => children.iterator.flatMap(_.freeVars.iterator).toSet } private var spanStart: Int = -1 From 4e977d36cb1ef761f035b5ddeb1c028247ebaff9 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sun, 21 Jan 2024 03:29:50 +0800 Subject: [PATCH 095/143] Warn if an outer binding is shadowed by a top-level name pattern --- .../main/scala/mlscript/pretyper/Symbol.scala | 12 +++++++++++- .../scala/mlscript/ucs/stages/Desugaring.scala | 12 ++++++++++++ shared/src/test/diff/nu/BadUCS.mls | 16 +++++++++++----- shared/src/test/diff/ucs/HygienicBindings.mls | 14 ++++++++++++++ 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/shared/src/main/scala/mlscript/pretyper/Symbol.scala b/shared/src/main/scala/mlscript/pretyper/Symbol.scala index 9ef593ee..3e3785ff 100644 --- a/shared/src/main/scala/mlscript/pretyper/Symbol.scala +++ b/shared/src/main/scala/mlscript/pretyper/Symbol.scala @@ -78,13 +78,19 @@ package object symbol { final class ModuleSymbol(override val defn: NuTypeDef) extends TypeSymbol with TermSymbol { require(defn.kind === Mod) + + override def nameVar: Var = defn.nameVar } - sealed trait TermSymbol extends Symbol with Matchable + sealed trait TermSymbol extends Symbol with Matchable { + def nameVar: Var + } class DefinedTermSymbol(val defn: NuFunDef) extends TermSymbol { override def name: Str = defn.name + override def nameVar: Var = defn.nme + def body: Term \/ Type = defn.rhs def isFunction: Bool = defn.isLetRec.isEmpty @@ -94,9 +100,13 @@ package object symbol { def isDeclaration: Bool = defn.rhs.isRight def operatorAlias: Opt[Var] = defn.symbolicNme + + def declaredLoc: Opt[Loc] = defn.nme.toLoc } class LocalTermSymbol(val nme: Var) extends TermSymbol { override def name: Str = nme.name + + override def nameVar: Var = nme } } diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 8b89f5ed..5f8ecf71 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -388,6 +388,18 @@ trait Desugaring { self: PreTyper => case s.EmptyPattern(_) | s.NamePattern(Var("_")) => desugarRight ++ desugarTail case s.NamePattern(nme) => + // If the top-level pattern is a name pattern, we need to check if + // `nme` shadows any variables in the scope. For example, code + // `fun f(x, y) = if x is y then "yes" else "no"` may read like + // `if x === y then "yes" else "no"`. + scope.getTermSymbol(nme.name) match { + case S(shadowed) => + raiseDesugaringWarning( + msg"the outer binding `${nme.name}`" -> shadowed.nameVar.toLoc, + msg"is shadowed by inner binding `${nme.name}`" -> nme.toLoc + ) + case N => () + } val symbol = freshSymbol(nme).withScrutinee(scrutinee) val scopeWithSymbol = scope + symbol c.Branch(scrutineeVar, c.Pattern.Name(nme.withSymbol(symbol)), diff --git a/shared/src/test/diff/nu/BadUCS.mls b/shared/src/test/diff/nu/BadUCS.mls index d5bd9333..6a176b3c 100644 --- a/shared/src/test/diff/nu/BadUCS.mls +++ b/shared/src/test/diff/nu/BadUCS.mls @@ -95,8 +95,14 @@ fun foo(x) = if x is M() then 0 //│ Code generation encountered an error: //│ unknown match case: M - +:w fun foo0(x, y) = if x is y then 0 +//│ ╔══[WARNING] the outer binding `y` +//│ ║ l.99: fun foo0(x, y) = if x is y then 0 +//│ ║ ^ +//│ ╟── is shadowed by inner binding `y` +//│ ║ l.99: fun foo0(x, y) = if x is y then 0 +//│ ╙── ^ //│ fun foo0: (anything, anything) -> 0 @@ -107,10 +113,10 @@ fun foo = 0 :ge fun foo0(x) = if x is foo() then 0 //│ ╔══[ERROR] type identifier `foo` not found -//│ ║ l.108: fun foo0(x) = if x is foo() then 0 +//│ ║ l.114: fun foo0(x) = if x is foo() then 0 //│ ╙── ^^^ //│ ╔══[ERROR] can only match on classes and traits -//│ ║ l.108: fun foo0(x) = if x is foo() then 0 +//│ ║ l.114: fun foo0(x) = if x is foo() then 0 //│ ╙── ^^^ //│ fun foo0: nothing -> error //│ Code generation encountered an error: @@ -121,10 +127,10 @@ fun foo0(x) = if x is foo() then 0 // FIXME: Typer.scala:1497 fun foo(x) = if x is foo() then 0 //│ ╔══[ERROR] type identifier `foo` not found -//│ ║ l.122: fun foo(x) = if x is foo() then 0 +//│ ║ l.128: fun foo(x) = if x is foo() then 0 //│ ╙── ^^^ //│ ╔══[ERROR] can only match on classes and traits -//│ ║ l.122: fun foo(x) = if x is foo() then 0 +//│ ║ l.128: fun foo(x) = if x is foo() then 0 //│ ╙── ^^^ //│ /!!!\ Uncaught error: java.lang.Exception: Internal Error: Program reached and unexpected state. diff --git a/shared/src/test/diff/ucs/HygienicBindings.mls b/shared/src/test/diff/ucs/HygienicBindings.mls index 60bbe085..56a1ae40 100644 --- a/shared/src/test/diff/ucs/HygienicBindings.mls +++ b/shared/src/test/diff/ucs/HygienicBindings.mls @@ -93,6 +93,7 @@ fun h2(a) = //│ fun h2: forall 'a. (None | Some[Left['a]]) -> (0 | 'a) :ducs:postprocess.result +:w fun h3(x, y, f, p) = if x is _ and f(x) is y and p(x) then y @@ -110,6 +111,12 @@ fun h3(x, y, f, p) = //│ case x*‡ of //│ None*† -> y //│ _ -> "anyway" +//│ ╔══[WARNING] the outer binding `y` +//│ ║ l.97: fun h3(x, y, f, p) = +//│ ║ ^ +//│ ╟── is shadowed by inner binding `y` +//│ ║ l.99: _ and f(x) is y and p(x) then y +//│ ╙── ^ //│ fun h3: forall 'a 'b. (Object & 'a, 'b, 'a -> 'b, 'a -> Bool) -> ("anyway" | 'b) @@ -126,6 +133,7 @@ h3("anything", "anything", _ => "not me", _ => false) ~~> "anyway" :ducs:postprocess.result +:w fun h4(x, y, p) = if x is y and p(x) then y @@ -142,6 +150,12 @@ fun h4(x, y, p) = //│ case x*‡ of //│ None*† -> y //│ _ -> "default" +//│ ╔══[WARNING] the outer binding `y` +//│ ║ l.137: fun h4(x, y, p) = +//│ ║ ^ +//│ ╟── is shadowed by inner binding `y` +//│ ║ l.139: y and p(x) then y +//│ ╙── ^ //│ fun h4: forall 'a 'b. (Object & 'a, 'b, 'a -> Bool) -> ("default" | 'a | 'b) h4("should be me", "not me", _ => true) From 0f3cdb8f27a7d07802aa49eb102667a99bda412b Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sun, 21 Jan 2024 03:34:13 +0800 Subject: [PATCH 096/143] Improve warning for shadowed outer bindings --- shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala | 2 +- shared/src/test/diff/nu/BadUCS.mls | 2 +- shared/src/test/diff/ucs/HygienicBindings.mls | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 5f8ecf71..42d56c0f 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -396,7 +396,7 @@ trait Desugaring { self: PreTyper => case S(shadowed) => raiseDesugaringWarning( msg"the outer binding `${nme.name}`" -> shadowed.nameVar.toLoc, - msg"is shadowed by inner binding `${nme.name}`" -> nme.toLoc + msg"is shadowed by name pattern `${nme.name}`" -> nme.toLoc ) case N => () } diff --git a/shared/src/test/diff/nu/BadUCS.mls b/shared/src/test/diff/nu/BadUCS.mls index 6a176b3c..e0bba638 100644 --- a/shared/src/test/diff/nu/BadUCS.mls +++ b/shared/src/test/diff/nu/BadUCS.mls @@ -100,7 +100,7 @@ fun foo0(x, y) = if x is y then 0 //│ ╔══[WARNING] the outer binding `y` //│ ║ l.99: fun foo0(x, y) = if x is y then 0 //│ ║ ^ -//│ ╟── is shadowed by inner binding `y` +//│ ╟── is shadowed by name pattern `y` //│ ║ l.99: fun foo0(x, y) = if x is y then 0 //│ ╙── ^ //│ fun foo0: (anything, anything) -> 0 diff --git a/shared/src/test/diff/ucs/HygienicBindings.mls b/shared/src/test/diff/ucs/HygienicBindings.mls index 56a1ae40..5c743559 100644 --- a/shared/src/test/diff/ucs/HygienicBindings.mls +++ b/shared/src/test/diff/ucs/HygienicBindings.mls @@ -114,7 +114,7 @@ fun h3(x, y, f, p) = //│ ╔══[WARNING] the outer binding `y` //│ ║ l.97: fun h3(x, y, f, p) = //│ ║ ^ -//│ ╟── is shadowed by inner binding `y` +//│ ╟── is shadowed by name pattern `y` //│ ║ l.99: _ and f(x) is y and p(x) then y //│ ╙── ^ //│ fun h3: forall 'a 'b. (Object & 'a, 'b, 'a -> 'b, 'a -> Bool) -> ("anyway" | 'b) @@ -153,7 +153,7 @@ fun h4(x, y, p) = //│ ╔══[WARNING] the outer binding `y` //│ ║ l.137: fun h4(x, y, p) = //│ ║ ^ -//│ ╟── is shadowed by inner binding `y` +//│ ╟── is shadowed by name pattern `y` //│ ║ l.139: y and p(x) then y //│ ╙── ^ //│ fun h4: forall 'a 'b. (Object & 'a, 'b, 'a -> Bool) -> ("default" | 'a | 'b) From 60f97b73aa014a557872d6be2f5ac092f174d922 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Tue, 23 Jan 2024 15:42:18 +0800 Subject: [PATCH 097/143] Clean up and make three cases which we failed to support --- .../src/main/scala/mlscript/JSBackend.scala | 14 +++-- .../mlscript/ucs/stages/Normalization.scala | 11 ++-- .../pretyper/ucs/stages/Normalization.mls | 52 ++++++++++++++++++- .../src/test/scala/mlscript/DiffTests.scala | 2 +- 4 files changed, 66 insertions(+), 13 deletions(-) diff --git a/shared/src/main/scala/mlscript/JSBackend.scala b/shared/src/main/scala/mlscript/JSBackend.scala index f1805482..6725218c 100644 --- a/shared/src/main/scala/mlscript/JSBackend.scala +++ b/shared/src/main/scala/mlscript/JSBackend.scala @@ -358,6 +358,14 @@ abstract class JSBackend(allowUnresolvedSymbols: Bool) { pat match { case Var("int") => JSInvoke(JSField(JSIdent("Number"), "isInteger"), scrut :: Nil) + case Var("Int") if !oldDefs => + JSInvoke(JSField(JSIdent("Number"), "isInteger"), scrut :: Nil) + case Var("Num") if !oldDefs => + JSBinary("===", scrut.typeof(), JSLit("number")) + case Var("Bool") if !oldDefs => + JSBinary("===", scrut.typeof(), JSLit("boolean")) + case Var("Str") if !oldDefs => + JSBinary("===", scrut.typeof(), JSLit("string")) case Var("bool") => JSBinary("===", scrut.member("constructor"), JSLit("Boolean")) case Var(s @ ("true" | "false")) => @@ -1145,7 +1153,7 @@ abstract class JSBackend(allowUnresolvedSymbols: Bool) { } class JSWebBackend extends JSBackend(allowUnresolvedSymbols = true) { - def oldDefs = false + override def oldDefs: Bool = false // Name of the array that contains execution results val resultsName: Str = topLevelScope declareRuntimeSymbol "results" @@ -1271,8 +1279,8 @@ class JSWebBackend extends JSBackend(allowUnresolvedSymbols = true) { (JSImmEvalFn(N, Nil, R(polyfill.emit() ::: stmts ::: epilogue), Nil).toSourceCode.toLines, resultNames.toList) } - def apply(pgrm: Pgrm, newDefs: Bool): (Ls[Str], Ls[Str]) = - if (newDefs) generateNewDef(pgrm) else generate(pgrm) + def apply(pgrm: Pgrm): (Ls[Str], Ls[Str]) = + if (!oldDefs) generateNewDef(pgrm) else generate(pgrm) } abstract class JSTestBackend extends JSBackend(allowUnresolvedSymbols = false) { diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index c0e26a4e..3773ed6b 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -18,7 +18,7 @@ trait Normalization { self: Desugarer with Traceable => private def fillImpl(these: Split, those: Split)(implicit scope: Scope, context: Context, - generatedVars: Set[Var], + declaredVars: Set[Var], shouldReportDiscarded: Bool ): Split = if (these.hasElse) { @@ -34,12 +34,10 @@ trait Normalization { self: Desugarer with Traceable => val concatenated = these.copy(tail = fillImpl(tail, thoseWithShadowed)) Split.Let(false, fresh, nme, concatenated) } else { - these.copy(tail = fillImpl(tail, those)(scope, context, generatedVars + nme, false)) + these.copy(tail = fillImpl(tail, those)(scope, context, declaredVars + nme, false)) } case _: Split.Else => these - case Split.Nil => - // println(s"END, generated vars: ${generatedVars.iterator.map(_.name).mkString(", ")}") - those.withoutBindings(generatedVars) + case Split.Nil => those.withoutBindings(declaredVars) }) private implicit class SplitOps(these: Split) { @@ -49,7 +47,7 @@ trait Normalization { self: Desugarer with Traceable => * @param those the split to append * @param shouldReportDiscarded whether we should raise an error if the given * split is discarded because of the else branch - * @param generatedVars the generated variables which have been declared + * @param declaredVars the generated variables which have been declared * @return the concatenated split */ def fill(those: Split, declaredVars: Set[Var], shouldReportDiscarded: Bool)(implicit @@ -306,7 +304,6 @@ trait Normalization { self: Desugarer with Traceable => case (_, end @ (Split.Else(_) | Split.Nil)) => println("the end"); end } }() - // }(showSplit(s"S${if (matchOrNot) "+" else "-"} ==>", _)) /** * If you want to prepend `tail` to another `Split` where the `nme` takes diff --git a/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls index cbef36d4..f0c52cf1 100644 --- a/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls +++ b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls @@ -20,8 +20,6 @@ fun sum(acc, xs) = Nil then acc //│ fun sum: (Int, Cons[Int] | Nil) -> Int -// FIXME: Remove redundant `_ -> (ucs$args_xs$Some_0$Cons).1`. -// Why are they everywhere? :ducs:postprocess.result fun test(xs) = if xs is @@ -79,3 +77,53 @@ fun test(xs) = //│ Nil*† -> "nothing" //│ None*† -> "nothing" //│ fun test: (None | Some[Cons[nothing] | Nil]) -> ("nothing" | Int | List[nothing]) + +:ducs:postprocess.result +fun test(x, p) = if x is + Int and p(x) then "foo" + 0 then "bar" + else "qax" +//│ Post-processed UCS term: +//│ case x*‡ of +//│ Int*◊ -> +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true*† -> "foo" +//│ _ -> "qax" +//│ 0 -> "bar" +//│ _ -> "qax" +//│ fun test: (Object, Int -> Bool) -> ("bar" | "foo" | "qax") + +:ducs:postprocess.result +fun test(x, p) = if x is + Str and p(x) then "foo" + "lol" then "bar" + else "qax" +//│ Post-processed UCS term: +//│ case x*‡ of +//│ Str*◊ -> +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true*† -> "foo" +//│ _ -> "qax" +//│ "lol" -> "bar" +//│ _ -> "qax" +//│ fun test: (Object, Str -> Bool) -> ("bar" | "foo" | "qax") + +:ducs:postprocess.result +fun test(x, p) = if x is + Num and p(x) then "great" + 2.71828 then "E" + 3.14159 then "PI" + else "other" +//│ Post-processed UCS term: +//│ case x*‡ of +//│ Num*◊ -> +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true*† -> "great" +//│ _ -> "other" +//│ 3.14159 -> "PI" +//│ 2.71828 -> "E" +//│ _ -> "other" +//│ fun test: (Object, Num -> Bool) -> ("E" | "PI" | "great" | "other") diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index 60758f26..05f78d9d 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -198,7 +198,7 @@ class DiffTests var newParser = basePath.headOption.contains("parser") || basePath.headOption.contains("compiler") val backend = new JSTestBackend { - def oldDefs = !newDefs + override def oldDefs: Bool = !newDefs } val host = ReplHost() val codeGenTestHelpers = new CodeGenTestHelpers(file, output) From d767fc15f8a2ef1af2e0955b239e6c3516a508b9 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Tue, 23 Jan 2024 23:46:51 +0800 Subject: [PATCH 098/143] Simplify normalization and fix refinement to literal patterns --- .../mlscript/ucs/stages/Desugaring.scala | 23 +- .../mlscript/ucs/stages/Normalization.scala | 212 +++++++----------- .../main/scala/mlscript/ucs/syntax/core.scala | 5 +- .../pretyper/ucs/stages/Normalization.mls | 56 +++-- .../src/test/diff/ucs/NuPlainConditionals.mls | 2 +- .../src/test/diff/ucs/PlainConditionals.mls | 2 +- 6 files changed, 141 insertions(+), 159 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 42d56c0f..7cb2b884 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -64,10 +64,16 @@ trait Desugaring { self: PreTyper => * A shorthand for making a true pattern, which is useful in desugaring * Boolean conditions. */ - private def truePattern(implicit scope: Scope, context: Context) = - c.Pattern.Class(Var("true").withResolvedClassLikeSymbol, false) - private def falsePattern(implicit scope: Scope, context: Context) = - c.Pattern.Class(Var("false").withResolvedClassLikeSymbol, false) + private def truePattern(implicit scope: Scope, context: Context) = { + val className = Var("true") + val classSymbol = className.resolveClassLikeSymbol + c.Pattern.Class(className, classSymbol, false) + } + private def falsePattern(implicit scope: Scope, context: Context) = { + val className = Var("false") + val classSymbol = className.resolveClassLikeSymbol + c.Pattern.Class(className, classSymbol, false) + } private def desugarTermSplit(split: s.TermSplit)(implicit termPart: PartialTerm.Incomplete, scope: Scope, context: Context): c.Split = split match { @@ -227,7 +233,7 @@ trait Desugaring { self: PreTyper => } println(s"${scrutineeVar.name}: ${scrutinee.showPatternsDbg}") // Last, return the scope with all bindings and a function that adds all matches and bindings to a split. - (scopeWithAll, split => c.Branch(scrutineeVar, c.Pattern.Class(pattern.nme, refined), bindAll(split))) + (scopeWithAll, split => c.Branch(scrutineeVar, c.Pattern.Class(pattern.nme, patternClassSymbol, refined), bindAll(split))) } /** @@ -272,8 +278,8 @@ trait Desugaring { self: PreTyper => (scopeWithNestedAll, split => bindPrevious(bindNestedAll(bindAliasVars(split)) :: c.Split.Nil)) case pattern @ s.ConcretePattern(Var("true") | Var("false")) => println(s"${nme.name} is ${pattern.nme.name}") - val className = pattern.nme.withResolvedClassLikeSymbol(scope, context) - (scope, split => bindPrevious(c.Branch(nme, c.Pattern.Class(className, false), bindAliasVars(split)) :: c.Split.Nil)) + val classSymbol = pattern.nme.resolveClassLikeSymbol(scope, context) + (scope, split => bindPrevious(c.Branch(nme, c.Pattern.Class(pattern.nme, classSymbol, false), bindAliasVars(split)) :: c.Split.Nil)) case s.LiteralPattern(literal) => nme.getOrCreateScrutinee .withAliasVar(nme) @@ -375,9 +381,10 @@ trait Desugaring { self: PreTyper => c.Branch(scrutineeVar, c.Pattern.Literal(literal), desugarRight) :: desugarTail case s.ConcretePattern(nme @ (Var("true") | Var("false"))) => scrutinee.getOrCreateBooleanPattern(nme).addLocation(nme) + val classSymbol = nme.resolveClassLikeSymbol c.Branch( scrutinee = scrutineeVar, - pattern = c.Pattern.Class(nme.withResolvedClassLikeSymbol, false), + pattern = c.Pattern.Class(nme, classSymbol, false), continuation = desugarRight ) :: desugarTail case s.ConcretePattern(nme) => diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 3773ed6b..6dbf532e 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -1,6 +1,6 @@ package mlscript.ucs.stages -import mlscript.{App, CaseOf, Fld, FldFlags, Let, Loc, Sel, Term, Tup, Var, StrLit} +import mlscript.{App, CaseOf, DecLit, Fld, FldFlags, IntLit, Let, Loc, Sel, Term, Tup, Var, StrLit} import mlscript.{CaseBranches, Case, Wildcard, NoCases} import mlscript.Message, Message.MessageContext import mlscript.utils._, shorthands._ @@ -13,8 +13,6 @@ import pretyper.symbol._ import pretyper.{Diagnosable, Scope, Traceable} trait Normalization { self: Desugarer with Traceable => - import Normalization._ - private def fillImpl(these: Split, those: Split)(implicit scope: Scope, context: Context, @@ -70,6 +68,46 @@ trait Normalization { self: Desugarer with Traceable => } } } + + /** We don't care about `Pattern.Name` because they won't appear in `specialize`. */ + private implicit class PatternOps(val self: Pattern) extends AnyRef { + /** Checks if two patterns are the same. */ + def =:=(other: Pattern): Bool = (self, other) match { + case (Pattern.Class(_, s1, _), Pattern.Class(_, s2, _)) => s1 === s2 + case (Pattern.Literal(l1), Pattern.Literal(l2)) => l1 === l2 + case (_, _) => false + } + /** Hard-code sub-typing relations. */ + def <:<(other: Pattern): Bool = (self, other) match { + case (Pattern.Class(Var("true"), _, _), Pattern.Class(Var("Bool"), _, _)) => true + case (Pattern.Class(Var("false"), _, _), Pattern.Class(Var("Bool"), _, _)) => true + case (Pattern.Class(Var("Int"), _, _), Pattern.Class(Var("Num"), _, _)) => true + case (Pattern.Class(_, s1, _), Pattern.Class(_, s2, _)) => s1 hasBaseClass s2 + case (Pattern.Literal(IntLit(_)), Pattern.Class(Var("Int" | "Num"), _, _)) => true + case (Pattern.Literal(StrLit(_)), Pattern.Class(Var("Str"), _, _)) => true + case (Pattern.Literal(DecLit(_)), Pattern.Class(Var("Num"), _, _)) => true + case (_, _) => false + } + /** + * If two class-like patterns has different `refined` flag. Report the + * inconsistency as a warning. + */ + def reportInconsistentRefinedWith(other: Pattern): Unit = (self, other) match { + case (Pattern.Class(n1, _, r1), Pattern.Class(n2, _, r2)) if r1 =/= r2 => + def be(value: Bool): Str = if (value) "is" else "is not" + raiseDesugaringWarning( + msg"inconsistent refined pattern" -> other.toLoc, + msg"pattern `${n1.name}` ${be(r1)} refined" -> n1.toLoc, + msg"but pattern `${n2.name}` ${be(r2)} refined" -> n2.toLoc + ) + case (_, _) => () + } + /** If the pattern is a class-like pattern, override its `refined` flag. */ + def markAsRefined: Unit = self match { + case self: Pattern.Class => self.refined = true + case _ => () + } + } /** @@ -95,7 +133,7 @@ trait Normalization { self: Desugarer with Traceable => val (wrap, realTail) = preventShadowing(nme, tail) wrap(Let(false, nme, scrutinee, normalizeToTerm(continuation.fill(realTail, declaredVars, true), declaredVars))) // Skip Boolean conditions as scrutinees, because they only appear once. - case Split.Cons(Branch(test, pattern @ Pattern.Class(nme @ Var("true"), _), continuation), tail) if context.isTestVar(test) => + case Split.Cons(Branch(test, pattern @ Pattern.Class(nme @ Var("true"), _, _), continuation), tail) if context.isTestVar(test) => println(s"TRUE: ${test.name} is true") val trueBranch = normalizeToTerm(continuation.fill(tail, declaredVars, false), declaredVars) val falseBranch = normalizeToCaseBranches(tail, declaredVars) @@ -109,7 +147,7 @@ trait Normalization { self: Desugarer with Traceable => // println(s"false branch: ${showSplit(tail)}") val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context), declaredVars) CaseOf(scrutineeVar, Case(literal, trueBranch, falseBranch)(refined = false)) - case Split.Cons(Branch(Scrutinee.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Class(nme, rfd), continuation), tail) => + case Split.Cons(Branch(Scrutinee.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Class(nme, _, rfd), continuation), tail) => println(s"CLASS: ${scrutineeVar.name} is ${nme.name}") // println(s"match ${scrutineeVar.name} with $nme (has location: ${nme.toLoc.isDefined})") val trueBranch = normalizeToTerm(specialize(continuation.fill(tail, declaredVars, false), true)(scrutineeVar, scrutinee, pattern, context), declaredVars) @@ -160,8 +198,8 @@ trait Normalization { self: Desugarer with Traceable => /** * Specialize `split` with the assumption that `scrutinee` matches `pattern`. - * If `matchOrNot` is `true`, the function keeps branches that agree on - * `scrutinee` matches `pattern`. Otherwise, the function removes branches + * If `matchOrNot` is `true`, the function _keeps_ branches that agree on + * `scrutinee` matches `pattern`. Otherwise, the function _removes_ branches * that agree on `scrutinee` matches `pattern`. */ private def specialize @@ -170,137 +208,51 @@ trait Normalization { self: Desugarer with Traceable => trace[Split](s"S${if (matchOrNot) "+" else "-"} <== ${scrutineeVar.name} is ${pattern}") { (matchOrNot, split) match { // Name patterns are translated to let bindings. - case (_, Split.Cons(Branch(otherScrutineeVar, Pattern.Name(alias), continuation), tail)) => - Split.Let(false, alias, otherScrutineeVar, specialize(continuation, matchOrNot)) - case (_, split @ Split.Cons(head @ Branch(test, Pattern.Class(Var("true"), _), continuation), tail)) if context.isTestVar(test) => - println(s"found a Boolean test: ${test.showDbg} is true") - val trueBranch = specialize(continuation, matchOrNot) - val falseBranch = specialize(tail, matchOrNot) - split.copy(head = head.copy(continuation = trueBranch), tail = falseBranch) - // Class pattern. Positive. - case (true, split @ Split.Cons(head @ Branch(Scrutinee.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Class(otherClassName, otherRefined), continuation), tail)) => - val otherClassSymbol = otherClassName.getClassLikeSymbol + case (m, Split.Cons(Branch(otherScrutineeVar, Pattern.Name(alias), continuation), tail)) => + Split.Let(false, alias, otherScrutineeVar, specialize(continuation, m)) + case (m, Split.Cons(head @ Branch(test, Pattern.Class(Var("true"), _, _), continuation), tail)) if context.isTestVar(test) => + println(s"TEST: ${test.name} is true") + head.copy(continuation = specialize(continuation, m)) :: specialize(tail, m) + case (true, split @ Split.Cons(head @ Branch(Scrutinee.WithVar(otherScrutinee, otherScrutineeVar), otherPattern, continuation), tail)) => if (scrutinee === otherScrutinee) { - println(s"scrutinee: ${scrutineeVar.name} === ${otherScrutineeVar.name}") - pattern match { - case classPattern @ Pattern.Class(className, refined) => - val classSymbol = className.getClassLikeSymbol - if (classSymbol === otherClassSymbol) { - println(s"Case 1: class name: ${className.name} === ${otherClassName.name}") - if (otherRefined =/= refined) { - def be(value: Bool): Str = if (value) "is" else "is not" - raiseDesugaringWarning( - msg"inconsistent refined case branches" -> pattern.toLoc, - msg"class pattern ${className.name} ${be(refined)} refined" -> className.toLoc, - msg"but class pattern ${otherClassName.name} ${be(otherRefined)} refined" -> otherClassName.toLoc - ) - } - specialize(continuation, true) :++ specialize(tail, true) - } else if (otherClassSymbol hasBaseClass classSymbol) { - println(s"Case 2: ${otherClassName.name} <: ${className.name}") - println(s"${otherClassSymbol.name} is refining ${className.name}") - // We should mark `pattern` as refined. - classPattern.refined = true - split - } else { - println(s"Case 3: ${className.name} and ${otherClassName.name} are unrelated") - specialize(tail, true) - } - case _ => - // TODO: Make a case for this. Check if this is a valid case. - raiseDesugaringError( - msg"pattern ${pattern.toString}" -> pattern.toLoc, - msg"is incompatible with class pattern ${otherClassName.name}" -> otherClassName.toLoc, - ) - split + println(s"Case 1: ${scrutineeVar.name} === ${otherScrutineeVar.name}") + if (otherPattern =:= pattern) { + println(s"Case 2.1: $pattern =:= $otherPattern") + otherPattern reportInconsistentRefinedWith pattern + specialize(continuation, true) :++ specialize(tail, true) + } else if (otherPattern <:< pattern) { + println(s"Case 2.2: $pattern <:< $otherPattern") + pattern.markAsRefined; split + } else { + println(s"Case 2.3: $pattern are unrelated with $otherPattern") + specialize(tail, true) } } else { - // println(s"scrutinee: ${scrutineeVar.name} =/= ${otherScrutineeVar.name}") - split.copy( - head = head.copy(continuation = specialize(continuation, true)), - tail = specialize(tail, true) - ) + println(s"Case 2: ${scrutineeVar.name} === ${otherScrutineeVar.name}") + head.copy(continuation = specialize(continuation, true)) :: specialize(tail, true) } - // Class pattern. Negative - case (false, split @ Split.Cons(head @ Branch(Scrutinee.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Class(otherClassName, otherRefined), continuation), tail)) => - val otherClassSymbol = otherClassName.getClassLikeSymbol + case (false, split @ Split.Cons(head @ Branch(Scrutinee.WithVar(otherScrutinee, otherScrutineeVar), otherPattern, continuation), tail)) => if (scrutinee === otherScrutinee) { - println(s"scrutinee: ${scrutineeVar.name} === ${otherScrutineeVar.name}") - pattern match { - case Pattern.Class(className, refined) => - println("both of them are class patterns") - if (otherRefined =/= refined) { - def be(value: Bool): Str = if (value) "is" else "is not" - raiseDesugaringWarning( - msg"inconsistent refined case branches" -> pattern.toLoc, - msg"class pattern ${className.name} ${be(refined)} refined" -> className.toLoc, - msg"but class pattern ${otherClassName.name} ${be(otherRefined)} refined" -> otherClassName.toLoc - ) - } - val classSymbol = className.getClassLikeSymbol - if (className === otherClassName) { - println(s"Case 1: class name: ${otherClassName.name} === ${className.name}") - specialize(tail, false) - } else if (otherClassSymbol.baseTypes contains classSymbol) { - println(s"Case 2: class name: ${otherClassName.name} <: ${className.name}") - Split.Nil - } else { - println(s"Case 3: class name: ${otherClassName.name} and ${className.name} are unrelated") - split.copy(tail = specialize(tail, false)) - } - case _ => - println(s"different patterns: ${otherClassName.name} and $pattern.toString") - split.copy(tail = specialize(tail, false)) + println(s"Case 1: ${scrutineeVar.name} === ${otherScrutineeVar.name}") + otherPattern reportInconsistentRefinedWith pattern + if (otherPattern =:= pattern) { + println(s"Case 2.1: $pattern =:= $otherPattern") + specialize(tail, false) + } else if (otherPattern <:< pattern) { + println(s"Case 2.2: $pattern <:< $otherPattern") + Split.Nil + } else { + println(s"Case 2.3: $pattern are unrelated with $otherPattern") + split.copy(tail = specialize(tail, false)) } } else { - println(s"scrutinee: ${scrutineeVar.name} =/= ${otherScrutineeVar.name}") - split.copy( - head = head.copy(continuation = specialize(continuation, matchOrNot)), - tail = specialize(tail, matchOrNot) - ) + println(s"Case 2: ${scrutineeVar.name} === ${otherScrutineeVar.name}") + head.copy(continuation = specialize(continuation, false)) :: specialize(tail, false) } - // Literal pattern. Positive. - case (true, split @ Split.Cons(head @ Branch(Scrutinee.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Literal(otherLiteral), continuation), tail)) => - if (scrutinee === otherScrutinee) { - println(s"scrutinee: ${scrutineeVar.name} is ${otherScrutineeVar.name}") - pattern match { - case Pattern.Literal(literal) if literal === otherLiteral => - specialize(continuation, true) :++ specialize(tail, true) - case _ => specialize(tail, true) - } - } else { - println(s"scrutinee: ${scrutineeVar.name} is NOT ${otherScrutineeVar.name}") - split.copy( - head = head.copy(continuation = specialize(continuation, true)), - tail = specialize(tail, true) - ) - } - // Literal pattern. Negative. - case (false, split @ Split.Cons(head @ Branch(Scrutinee.WithVar(otherScrutinee, otherScrutineeVar), Pattern.Literal(otherLiteral), continuation), tail)) => - if (scrutinee === otherScrutinee) { - println(s"scrutinee: ${scrutineeVar.name} is ${otherScrutineeVar.name}") - pattern match { - case Pattern.Literal(literal) if literal === otherLiteral => - specialize(tail, false) - case _ => - // No need to check `continuation` because literals don't overlap. - split.copy(tail = specialize(tail, false)) - } - } else { - println(s"scrutinee: ${scrutineeVar.name} is NOT ${otherScrutineeVar.name}") - split.copy( - head = head.copy(continuation = specialize(continuation, false)), - tail = specialize(tail, false) - ) - } - // Other patterns. Not implemented. - case (_, split @ Split.Cons(Branch(otherScrutineeVar, pattern, continuation), tail)) => - raiseDesugaringError(msg"found unsupported pattern: ${pattern.toString}" -> pattern.toLoc) + case (_, split @ Split.Cons(Branch(_, pattern, _), _)) => + raiseDesugaringError(msg"unsupported pattern" -> pattern.toLoc) split - case (_, let @ Split.Let(_, nme, _, tail)) => - println(s"let binding ${nme.name}, go next") - let.copy(tail = specialize(tail, matchOrNot)) - // Ending cases remain the same. + case (m, let @ Split.Let(_, nme, _, tail)) => let.copy(tail = specialize(tail, m)) case (_, end @ (Split.Else(_) | Split.Nil)) => println("the end"); end } }() @@ -320,5 +272,3 @@ trait Normalization { self: Desugarer with Traceable => case S(_) | N => identity[Term] _ -> tail } } - -object Normalization diff --git a/shared/src/main/scala/mlscript/ucs/syntax/core.scala b/shared/src/main/scala/mlscript/ucs/syntax/core.scala index 4b39414b..db52d4e7 100644 --- a/shared/src/main/scala/mlscript/ucs/syntax/core.scala +++ b/shared/src/main/scala/mlscript/ucs/syntax/core.scala @@ -2,6 +2,7 @@ package mlscript.ucs.syntax import mlscript.{Diagnostic, Lit, Loc, Located, Message, Term, Var} import mlscript.utils._, shorthands._ +import mlscript.pretyper.symbol.TypeSymbol package object core { sealed abstract class Pattern extends Located { @@ -12,7 +13,7 @@ package object core { override def toString(): String = this match { case Pattern.Literal(literal) => literal.idStr case Pattern.Name(Var(name)) => name - case Pattern.Class(Var(name), rfd) => (if (rfd) "refined " else "") + name + case Pattern.Class(Var(name), _, rfd) => (if (rfd) "refined " else "") + name } } object Pattern { @@ -28,7 +29,7 @@ package object core { * @param originallyRefined whether the class is marked as refined from * in source code */ - final case class Class(nme: Var, val originallyRefined: Bool) extends Pattern { + final case class Class(nme: Var, symbol: TypeSymbol, val originallyRefined: Bool) extends Pattern { override def children: Ls[Located] = nme :: Nil var refined: Bool = originallyRefined diff --git a/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls index f0c52cf1..e3a24573 100644 --- a/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls +++ b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls @@ -85,14 +85,15 @@ fun test(x, p) = if x is else "qax" //│ Post-processed UCS term: //│ case x*‡ of -//│ Int*◊ -> +//│ refined Int*◊ -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> "foo" -//│ _ -> "qax" -//│ 0 -> "bar" -//│ _ -> "qax" -//│ fun test: (Object, Int -> Bool) -> ("bar" | "foo" | "qax") +//│ _ -> +//│ case x*‡ of +//│ 0 -> "bar" +//│ _ -> "qax" +//│ fun test: forall 'a. (Int & 'a, (Int & 'a) -> Bool) -> ("bar" | "foo" | "qax") :ducs:postprocess.result fun test(x, p) = if x is @@ -101,14 +102,15 @@ fun test(x, p) = if x is else "qax" //│ Post-processed UCS term: //│ case x*‡ of -//│ Str*◊ -> +//│ refined Str*◊ -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> "foo" -//│ _ -> "qax" -//│ "lol" -> "bar" -//│ _ -> "qax" -//│ fun test: (Object, Str -> Bool) -> ("bar" | "foo" | "qax") +//│ _ -> +//│ case x*‡ of +//│ "lol" -> "bar" +//│ _ -> "qax" +//│ fun test: forall 'a. (Str & 'a, (Str & 'a) -> Bool) -> ("bar" | "foo" | "qax") :ducs:postprocess.result fun test(x, p) = if x is @@ -118,12 +120,34 @@ fun test(x, p) = if x is else "other" //│ Post-processed UCS term: //│ case x*‡ of -//│ Num*◊ -> +//│ refined Num*◊ -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> "great" -//│ _ -> "other" -//│ 3.14159 -> "PI" -//│ 2.71828 -> "E" -//│ _ -> "other" -//│ fun test: (Object, Num -> Bool) -> ("E" | "PI" | "great" | "other") +//│ _ -> +//│ case x*‡ of +//│ 2.71828 -> "E" +//│ _ -> +//│ case x*‡ of +//│ 3.14159 -> "PI" +//│ _ -> "other" +//│ fun test: forall 'a. ('a & (2.71828 | Num & ~2.71828), (Num & 'a) -> Bool) -> ("E" | "PI" | "great" | "other") + +:ducs:postprocess.result +fun test(x, p) = if x is + Bool and p(x) then "great" + true then "false" + false then "true" +//│ Post-processed UCS term: +//│ case x*‡ of +//│ refined Bool*◊ -> +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true*† -> "great" +//│ _ -> +//│ case x*‡ of +//│ true*† -> "false" +//│ _ -> +//│ case x*‡ of +//│ false*† -> "true" +//│ fun test: forall 'a. ('a & Bool, (Bool & 'a) -> Bool) -> ("false" | "great" | "true") diff --git a/shared/src/test/diff/ucs/NuPlainConditionals.mls b/shared/src/test/diff/ucs/NuPlainConditionals.mls index 4f24071e..4862ce95 100644 --- a/shared/src/test/diff/ucs/NuPlainConditionals.mls +++ b/shared/src/test/diff/ucs/NuPlainConditionals.mls @@ -96,7 +96,7 @@ fun foo2(x) = x is (Pair(a, b) and a > b) | Int //│ fun foo1: nothing -> error //│ fun foo2: nothing -> error //│ Code generation encountered an error: -//│ unknown match case: Int +//│ unresolved symbol | diff --git a/shared/src/test/diff/ucs/PlainConditionals.mls b/shared/src/test/diff/ucs/PlainConditionals.mls index 56fdc6d1..a90d235b 100644 --- a/shared/src/test/diff/ucs/PlainConditionals.mls +++ b/shared/src/test/diff/ucs/PlainConditionals.mls @@ -99,7 +99,7 @@ fun foo(x) = x is (Pair(a, b) and a > b) | Int //│ fun foo: nothing -> error //│ fun foo: nothing -> error //│ Code generation encountered an error: -//│ unknown match case: Int +//│ unresolved symbol | From 860d6c8b0c3470889ba9282c29c9b2ea322f9b6b Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 24 Jan 2024 01:53:34 +0800 Subject: [PATCH 099/143] Make coverage checking and normalization consistent --- .../main/scala/mlscript/pretyper/Scope.scala | 1 + .../scala/mlscript/ucs/context/CaseSet.scala | 85 -------------- .../scala/mlscript/ucs/context/Context.scala | 9 +- .../scala/mlscript/ucs/context/Pattern.scala | 26 +---- .../mlscript/ucs/context/Scrutinee.scala | 17 +-- .../scala/mlscript/ucs/context/package.scala | 13 --- .../ucs/stages/CoverageChecking.scala | 107 +++++++++++++++++- .../mlscript/ucs/stages/Desugaring.scala | 2 +- .../mlscript/ucs/stages/Normalization.scala | 20 ++-- .../scala/mlscript/ucs/stages/package.scala | 33 +++++- shared/src/test/diff/pretyper/Errors.mls | 2 +- .../pretyper/ucs/stages/Normalization.mls | 29 +++++ .../pretyper/ucs/stages/PostProcessing.mls | 4 +- shared/src/test/diff/ucs/ElseIf.mls | 4 +- 14 files changed, 182 insertions(+), 170 deletions(-) delete mode 100644 shared/src/main/scala/mlscript/ucs/context/CaseSet.scala diff --git a/shared/src/main/scala/mlscript/pretyper/Scope.scala b/shared/src/main/scala/mlscript/pretyper/Scope.scala index 794fc414..78323d07 100644 --- a/shared/src/main/scala/mlscript/pretyper/Scope.scala +++ b/shared/src/main/scala/mlscript/pretyper/Scope.scala @@ -99,6 +99,7 @@ object Scope { new ModuleSymbol(mod("true")), new ModuleSymbol(mod("false")), new TypeAliasSymbol(als("nothing")), + new DummyClassSymbol(Var("Object")), new DummyClassSymbol(Var("Int")), new DummyClassSymbol(Var("Num")), new DummyClassSymbol(Var("Str")), diff --git a/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala b/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala deleted file mode 100644 index aff9d24e..00000000 --- a/shared/src/main/scala/mlscript/ucs/context/CaseSet.scala +++ /dev/null @@ -1,85 +0,0 @@ -package mlscript.ucs.context - -import mlscript.{Lit, Loc, Var} -import mlscript.pretyper.symbol.TypeSymbol -import mlscript.utils._, shorthands._ -import mlscript.pretyper.symbol.DummyClassSymbol - -/** - * A `CaseSet` represents all patterns that a particular scrutinee is - * being matched with within a UCS expression. - * - * @param patterns a list of patterns. - */ -final case class CaseSet(val patterns: List[Pattern]) { - def showInDiagnostics: Str = - patterns.iterator.map(_.showInDiagnostics).mkString("[", ", ", "]") - - /** Get a iterator of all class-like patterns. */ - def classLikePatterns: Iterator[Pattern.ClassLike] = patterns.iterator.flatMap { - case pattern: Pattern.ClassLike => S(pattern) - case _: Pattern.Boolean | _: Pattern.Literal | _: Pattern.Tuple => N - } - - /** Separate a class-like pattern if it appears in `patterns`. */ - def separate(classLikeSymbol: TypeSymbol): Opt[(Pattern.ClassLike, Ls[Pattern.ClassLike])] = { - classLikePatterns.foldRight[(Opt[Pattern.ClassLike], Ls[Pattern.ClassLike])]((N, Nil)) { - case (pattern, (S(separated), rest)) => (S(separated), pattern :: rest) - case (pattern, (N, rest)) if pattern.classLikeSymbol === classLikeSymbol => (S(pattern), rest) - case (pattern, (N, rest)) => (N, pattern :: rest) - } match { - case (N, _) => N - case (S(separated), rest) => S((separated, rest)) - } - } - /** - * Split the pattern set into two pattern sets. - * - * For example, let A be the base type of B, C, and D. Plus, class `Z` is - * unrelated to any of them. Suppose the initial pattern set is - * `{ A, B, C, Z }`. Splitting the set results in two sets, one set - * contains classes that are compatible with `A`, and the other set - * contains classes that are unrelated to `A`. - * - * For example, if we split the set with `A`, then we get `{ B, C }` and - * set `{ Z }`. Set `{ B, C }` represents that the scrutinee can be further - * refined to class `B` or `class C`. Set `{ Z }` represents that if the - * scrutinee is not `A`, then it can be `Z`. - * - * If `A` is sealed to `B`, `C`, and `D`, then we get `{ B, C, D }` and - * `{ Z }`. Because if the scrutinee is assumed to be `A`, then it can also - * be `D` other than `B`, `C`. - * - * @param classLikeSymbol the type symbol represents the class like type - * @return If the pattern set doesn't include the given type symbol, this - * returns `None`. Otherwise, the function returns a triplet of the - * locations where the pattern appears, the related patterns, and - * unrelated patterns. - */ - def split(classLikeSymbol: TypeSymbol): Opt[(Pattern.ClassLike, CaseSet, CaseSet)] = { - separate(classLikeSymbol) match { - case N => N - case S((pattern, patterns)) => - val (unrelated, related) = patterns.partitionMap { pattern => - if (pattern.classLikeSymbol hasBaseClass classLikeSymbol) { - R(pattern) - } else { - L(pattern) - } - } - S((pattern, CaseSet(related), CaseSet(unrelated))) - } - } - - @inline def remove(boolLit: Var): CaseSet = { - require(boolLit.name === "true" || boolLit.name === "false") - CaseSet(patterns.filter(!_.matches(boolLit))) - } - - @inline def remove(literal: Lit): CaseSet = - CaseSet(patterns.filter(!_.matches(literal))) - - @inline def isEmpty: Bool = patterns.isEmpty - - @inline def size: Int = patterns.size -} diff --git a/shared/src/main/scala/mlscript/ucs/context/Context.scala b/shared/src/main/scala/mlscript/ucs/context/Context.scala index 9a62d9f8..c3f21d37 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Context.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Context.scala @@ -57,14 +57,7 @@ class Context(originalTerm: If) { scrutinee } - /** - * Create a `MatchRegistry` from the current context. - */ - def toMatchRegistry: MatchRegistry = - scrutineeBuffer.iterator.flatMap { scrutinee => - val caseSet = scrutinee.toCaseSet - scrutinee.aliasesIterator.map(alias => (alias -> scrutinee) -> caseSet) - }.toMap + def scrutinees: Iterator[Scrutinee] = scrutineeBuffer.iterator } object Context { diff --git a/shared/src/main/scala/mlscript/ucs/context/Pattern.scala b/shared/src/main/scala/mlscript/ucs/context/Pattern.scala index 5e209e0b..cfb1d45f 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Pattern.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Pattern.scala @@ -5,6 +5,7 @@ import mlscript.{Lit, Loc, Located, SimpleTerm, TypeName, Var} import mlscript.pretyper.symbol.TypeSymbol import mlscript.utils._, shorthands._ import mlscript.pretyper.symbol.DummyClassSymbol +import mlscript.pretyper.symbol.ModuleSymbol sealed abstract class Pattern { private val locationsBuffer: Buffer[Loc] = Buffer.empty @@ -68,6 +69,7 @@ object Pattern { override def showInDiagnostics: Str = s"${(classLikeSymbol match { case dummySymbol: DummyClassSymbol => "class" + case s: ModuleSymbol if s.name === "true" || s.name === "false" => s"Boolean value" case otherSymbol: TypeSymbol => otherSymbol.defn.kind.str })} `${classLikeSymbol.name}`" @@ -127,28 +129,4 @@ object Pattern { override def toCasePattern: SimpleTerm = literal.withLoc(firstOccurrence) } - - /** - * This can be actually merged with `Pattern.Literal`. However, there's no - * `Lit` sub-classes for Boolean types, so the representation is a little bit - * awkward, also, it makes sense to consider Boolean patterns separately - * because we can check the Boolean exhaustiveness with them. - */ - final case class Boolean(val value: Var) extends Pattern { - require(value.name === "true" || value.name === "false") - - override def arity: Opt[Int] = N - - override def showDbg: Str = value.name - - override def showInDiagnostics: Str = s"Boolean value ${value.name}" - - override def matches(pat: SimpleTerm): Bool = - pat match { - case Var(name) => value.name === name - case _ => false - } - - override def toCasePattern: SimpleTerm = value.withLoc(firstOccurrence) - } } diff --git a/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala index 05422bf2..b8b03805 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala @@ -4,7 +4,6 @@ import collection.mutable.{Buffer, SortedMap => MutSortedMap, SortedSet => MutSo import mlscript.{Lit, Loc, Var} import mlscript.pretyper.symbol.TypeSymbol import mlscript.utils._, shorthands._ -import mlscript.ucs.context.CaseSet import mlscript.DecLit import mlscript.IntLit import mlscript.StrLit @@ -26,11 +25,6 @@ class Scrutinee(val context: Context, parent: Opt[Scrutinee]) { private var aliasVarSet: MutSortedSet[Var] = MutSortedSet.empty private val literalPatterns: MutSortedMap[Lit, Pattern.Literal] = MutSortedMap.empty(literalOrdering) - /** - * The key should be either `Var("true")` or `Var("false")`. We want to keep - * the type symbol of the variable so that it still work in following stages. - */ - private val booleanPatterns: MutSortedMap[Var, Pattern.Boolean] = MutSortedMap.empty(varNameOrdering) def addAliasVar(alias: Var): Unit = aliasVarSet += alias @@ -78,17 +72,10 @@ class Scrutinee(val context: Context, parent: Opt[Scrutinee]) { def getOrCreateLiteralPattern(literal: Lit): Pattern.Literal = literalPatterns.getOrElseUpdate(literal, Pattern.Literal(literal)) - /** - * The key should be either `Var("true")` or `Var("false")`. We want to keep - * the type symbol of the variable so that it still work in following stages. - */ - def getOrCreateBooleanPattern(value: Var): Pattern.Boolean = - booleanPatterns.getOrElseUpdate(value, Pattern.Boolean(value)) - def classLikePatternsIterator: Iterator[Pattern.ClassLike] = classLikePatterns.valuesIterator def patternsIterator: Iterator[Pattern] = - classLikePatterns.valuesIterator ++ literalPatterns.valuesIterator ++ booleanPatterns.valuesIterator + classLikePatterns.valuesIterator ++ literalPatterns.valuesIterator /** Get a list of string representation of patterns. Only for debugging. */ private[ucs] def showPatternsDbg: Str = { @@ -103,8 +90,6 @@ class Scrutinee(val context: Context, parent: Opt[Scrutinee]) { def freshSubScrutinee: Scrutinee = context.freshScrutinee(this) - def toCaseSet: CaseSet = CaseSet(patternsIterator.toList) - def getReadableName(scrutineeVar: Var): Str = { parent match { case N if context.isGeneratedVar(scrutineeVar) => "term" diff --git a/shared/src/main/scala/mlscript/ucs/context/package.scala b/shared/src/main/scala/mlscript/ucs/context/package.scala index 6b5d31d9..d9c6e319 100644 --- a/shared/src/main/scala/mlscript/ucs/context/package.scala +++ b/shared/src/main/scala/mlscript/ucs/context/package.scala @@ -6,17 +6,4 @@ import mlscript.utils._, shorthands._ package object context { type NamedScrutinee = (Var -> Scrutinee) - - type MatchRegistry = Map[NamedScrutinee, CaseSet] - - // implicit class MatchRegistryOps(val self: MatchRegistry) extends AnyVal { - // def showDbg: Str = - // } - - type SeenRegistry = Map[NamedScrutinee, (TypeSymbol, Ls[Loc], CaseSet)] - - implicit class SeenRegistryOps(val self: SeenRegistry) extends AnyVal { - def showDbg: Str = if (self.isEmpty) "empty" else - self.iterator.map { case ((k, _), (s, _, _)) => s"${k.name} is ${s.name}" }.mkString(", ") - } } diff --git a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala index 82f2e231..45e92149 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala @@ -4,7 +4,7 @@ import mlscript.{Case, CaseBranches, CaseOf, Let, Lit, Loc, NoCases, Term, Var, import mlscript.{Diagnostic, ErrorReport, WarningReport} import mlscript.Message, Message.MessageContext import mlscript.ucs.Desugarer -import mlscript.ucs.context.{Context, CaseSet, NamedScrutinee, MatchRegistry, Scrutinee, SeenRegistry} +import mlscript.ucs.context.{Context, NamedScrutinee, Pattern, Scrutinee} import mlscript.pretyper.Traceable import mlscript.pretyper.symbol._ import mlscript.utils._, shorthands._ @@ -13,15 +13,19 @@ trait CoverageChecking { self: Desugarer with Traceable => import CoverageChecking._ def checkCoverage(term: Term)(implicit context: Context): Ls[Diagnostic] = { - val registry = context.toMatchRegistry + // Collect an immutable map from scrutinees to patterns. + val registry: ScrutineePatternSetMap = context.scrutinees.flatMap { scrutinee => + val caseSet = CaseSet(scrutinee.patternsIterator.toList) + scrutinee.aliasesIterator.map(alias => (alias -> scrutinee) -> caseSet) + }.toMap println("collected match registry: " + showRegistry(registry)) checkCoverage(term, Map.empty, registry, Map.empty) } private def checkCoverage( term: Term, - pending: MatchRegistry, - working: MatchRegistry, + pending: ScrutineePatternSetMap, + working: ScrutineePatternSetMap, seen: SeenRegistry )(implicit context: Context): Ls[Diagnostic] = { term match { @@ -154,9 +158,102 @@ object CoverageChecking { } /** A helper function that prints entries from the given registry line by line. */ - private def showRegistry(registry: MatchRegistry): Str = + private def showRegistry(registry: ScrutineePatternSetMap): Str = if (registry.isEmpty) "empty" else registry.iterator.map { case (scrutineeVar -> scrutinee, matchedClasses) => matchedClasses.patterns.iterator.map(_.showInDiagnostics).mkString(s">>> ${scrutineeVar.name} => [", ", ", "]") }.mkString("\n", "\n", "") + + type ScrutineePatternSetMap = Map[NamedScrutinee, CaseSet] + + type SeenRegistry = Map[NamedScrutinee, (TypeSymbol, Ls[Loc], CaseSet)] + + implicit class SeenRegistryOps(val self: SeenRegistry) extends AnyVal { + def showDbg: Str = if (self.isEmpty) "empty" else + self.iterator.map { case ((k, _), (s, _, _)) => s"${k.name} is ${s.name}" }.mkString(", ") + } + + /** + * A `CaseSet` represents all patterns that a particular scrutinee is + * being matched with within a UCS expression. + * + * @param patterns a list of patterns. + */ + final case class CaseSet(val patterns: List[Pattern]) { + def showInDiagnostics: Str = + patterns.iterator.map(_.showInDiagnostics).mkString("[", ", ", "]") + + /** Get a iterator of all class-like patterns. */ + def classLikePatterns: Iterator[Pattern.ClassLike] = patterns.iterator.flatMap { + case pattern: Pattern.ClassLike => S(pattern) + case _: Pattern.Literal | _: Pattern.Tuple => N + } + + /** Separate a class-like pattern if it appears in `patterns`. */ + def separate(classLikeSymbol: TypeSymbol): Opt[(Pattern.ClassLike, Ls[Pattern.ClassLike])] = { + classLikePatterns.foldRight[(Opt[Pattern.ClassLike], Ls[Pattern.ClassLike])]((N, Nil)) { + case (pattern, (S(separated), rest)) => (S(separated), pattern :: rest) + case (pattern, (N, rest)) if pattern.classLikeSymbol === classLikeSymbol => (S(pattern), rest) + case (pattern, (N, rest)) => (N, pattern :: rest) + } match { + case (N, _) => N + case (S(separated), rest) => S((separated, rest)) + } + } + /** + * Split the pattern set into two pattern sets. + * + * For example, let A be the base type of B, C, and D. Plus, class `Z` is + * unrelated to any of them. Suppose the initial pattern set is + * `{ A, B, C, Z }`. Splitting the set results in two sets, one set + * contains classes that are compatible with `A`, and the other set + * contains classes that are unrelated to `A`. + * + * For example, if we split the set with `A`, then we get `{ B, C }` and + * set `{ Z }`. Set `{ B, C }` represents that the scrutinee can be further + * refined to class `B` or `class C`. Set `{ Z }` represents that if the + * scrutinee is not `A`, then it can be `Z`. + * + * If `A` is sealed to `B`, `C`, and `D`, then we get `{ B, C, D }` and + * `{ Z }`. Because if the scrutinee is assumed to be `A`, then it can also + * be `D` other than `B`, `C`. + * + * @param classLikeSymbol the type symbol represents the class like type + * @return If the pattern set doesn't include the given type symbol, this + * returns `None`. Otherwise, the function returns a triplet of the + * locations where the pattern appears, the related patterns, and + * unrelated patterns. + */ + def split(classLikeSymbol: TypeSymbol): Opt[(Pattern.ClassLike, CaseSet, CaseSet)] = { + def mk(pattern: Pattern): Opt[Lit \/ TypeSymbol] = pattern match { + case Pattern.ClassLike(classLikeSymbol, _) => S(R(classLikeSymbol)) + case Pattern.Literal(literal) => S(L(literal)) + case _ => N + } + separate(classLikeSymbol) match { + case N => N + case S((pattern, patterns)) => + val (unrelated, related) = patterns.partitionMap { otherPattern => + if (compareCasePattern(mk(otherPattern), mk(pattern))) { + R(otherPattern) + } else { + L(otherPattern) + } + } + S((pattern, CaseSet(related), CaseSet(unrelated))) + } + } + + @inline def remove(boolLit: Var): CaseSet = { + require(boolLit.name === "true" || boolLit.name === "false") + CaseSet(patterns.filter(!_.matches(boolLit))) + } + + @inline def remove(literal: Lit): CaseSet = + CaseSet(patterns.filter(!_.matches(literal))) + + @inline def isEmpty: Bool = patterns.isEmpty + + @inline def size: Int = patterns.size + } } diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 7cb2b884..d4ef2fd6 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -380,8 +380,8 @@ trait Desugaring { self: PreTyper => scrutinee.getOrCreateLiteralPattern(literal).addLocation(literal) c.Branch(scrutineeVar, c.Pattern.Literal(literal), desugarRight) :: desugarTail case s.ConcretePattern(nme @ (Var("true") | Var("false"))) => - scrutinee.getOrCreateBooleanPattern(nme).addLocation(nme) val classSymbol = nme.resolveClassLikeSymbol + scrutinee.getOrCreateClassPattern(classSymbol).addLocation(nme) c.Branch( scrutinee = scrutineeVar, pattern = c.Pattern.Class(nme, classSymbol, false), diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 6dbf532e..cb3f2c84 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -1,6 +1,6 @@ package mlscript.ucs.stages -import mlscript.{App, CaseOf, DecLit, Fld, FldFlags, IntLit, Let, Loc, Sel, Term, Tup, Var, StrLit} +import mlscript.{App, CaseOf, DecLit, Fld, FldFlags, IntLit, Let, Lit, Loc, Sel, Term, Tup, Var, StrLit} import mlscript.{CaseBranches, Case, Wildcard, NoCases} import mlscript.Message, Message.MessageContext import mlscript.utils._, shorthands._ @@ -77,16 +77,14 @@ trait Normalization { self: Desugarer with Traceable => case (Pattern.Literal(l1), Pattern.Literal(l2)) => l1 === l2 case (_, _) => false } - /** Hard-code sub-typing relations. */ - def <:<(other: Pattern): Bool = (self, other) match { - case (Pattern.Class(Var("true"), _, _), Pattern.Class(Var("Bool"), _, _)) => true - case (Pattern.Class(Var("false"), _, _), Pattern.Class(Var("Bool"), _, _)) => true - case (Pattern.Class(Var("Int"), _, _), Pattern.Class(Var("Num"), _, _)) => true - case (Pattern.Class(_, s1, _), Pattern.Class(_, s2, _)) => s1 hasBaseClass s2 - case (Pattern.Literal(IntLit(_)), Pattern.Class(Var("Int" | "Num"), _, _)) => true - case (Pattern.Literal(StrLit(_)), Pattern.Class(Var("Str"), _, _)) => true - case (Pattern.Literal(DecLit(_)), Pattern.Class(Var("Num"), _, _)) => true - case (_, _) => false + /** Checks if `self` can be subsumed under `other`. */ + def <:<(other: Pattern): Bool = { + def mk(pattern: Pattern): Opt[Lit \/ TypeSymbol] = pattern match { + case Pattern.Class(_, s, _) => S(R(s)) + case Pattern.Literal(l) => S(L(l)) + case _ => N + } + compareCasePattern(mk(self), mk(other)) } /** * If two class-like patterns has different `refined` flag. Report the diff --git a/shared/src/main/scala/mlscript/ucs/stages/package.scala b/shared/src/main/scala/mlscript/ucs/stages/package.scala index 3be1c555..15c98af6 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/package.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/package.scala @@ -1,7 +1,7 @@ package mlscript.ucs -import mlscript.{App, Fld, FldFlags, Lit, PlainTup, Term, Tup, Var} -import mlscript.pretyper.symbol._ +import mlscript.{App, DecLit, Fld, FldFlags, IntLit, Lit, PlainTup, StrLit, Term, Tup, Var} +import mlscript.pretyper.symbol.TypeSymbol import mlscript.utils._, shorthands._ package object stages { @@ -65,4 +65,33 @@ package object stages { } case _ => (term, N) } + + /** + * Hard-coded subtyping relations used in normalization and coverage checking. + * Pass literals in `L` and pass variables together with `TypeSymbol` in `R`. + */ + private[ucs] def compareCasePattern( + lhs: Opt[Lit \/ TypeSymbol], + rhs: Opt[Lit \/ TypeSymbol] + ): Bool = (lhs, rhs) match { + case (S(lhs), S(rhs)) => compareCasePattern(lhs, rhs) + case (_, _) => false + } + /** + * Hard-coded subtyping relations used in normalization and coverage checking. + * Pass literals in `L` and pass variables together with `TypeSymbol` in `R`. + */ + private[stages] def compareCasePattern( + lhs: Lit \/ TypeSymbol, + rhs: Lit \/ TypeSymbol + ): Bool = (lhs, rhs) match { + case (_, R(s)) if s.name === "Object" => true + case (R(s1), R(s2)) if (s1.name === "true" || s1.name === "false") && s2.name === "Bool" => true + case (R(s1), R(s2)) if s1.name === "Int" && s2.name === "Num" => true + case (R(s1), R(s2)) => s1 hasBaseClass s2 + case (L(IntLit(_)), R(s)) if s.name === "Int" || s.name === "Num" => true + case (L(StrLit(_)), R(s)) if s.name === "Str" => true + case (L(DecLit(_)), R(s)) if s.name === "Num" => true + case (_, _) => false + } } diff --git a/shared/src/test/diff/pretyper/Errors.mls b/shared/src/test/diff/pretyper/Errors.mls index 28349ca0..c6226359 100644 --- a/shared/src/test/diff/pretyper/Errors.mls +++ b/shared/src/test/diff/pretyper/Errors.mls @@ -471,7 +471,7 @@ abstract class Foo: (Int -> Int) { // Object Object -//│ ╔══[ERROR] identifier `Object` not found +//│ ╔══[ERROR] identifier `Object` is resolved to a type //│ ║ l.473: Object //│ ╙── ^^^^^^ //│ ╔══[ERROR] Class Object is abstract and cannot be instantiated diff --git a/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls index e3a24573..0225a01b 100644 --- a/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls +++ b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls @@ -151,3 +151,32 @@ fun test(x, p) = if x is //│ case x*‡ of //│ false*† -> "true" //│ fun test: forall 'a. ('a & Bool, (Bool & 'a) -> Bool) -> ("false" | "great" | "true") + +:ducs:postprocess.result +:ge +fun test(x, p) = if x is + Object and p(x) then "great" + Bool and p(x) then "great, again" + true then "false" + false then "true" +//│ Post-processed UCS term: +//│ case x*‡ of +//│ refined Object*◊ -> +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true*† -> "great" +//│ _ -> +//│ case x*‡ of +//│ refined Bool*◊ -> +//│ let ucs$test$1*† = p(x,) : Bool +//│ case ucs$test$1*† of +//│ true*† -> "great, again" +//│ _ -> +//│ case x*‡ of +//│ true*† -> "false" +//│ _ -> +//│ case x*‡ of +//│ false*† -> "true" +//│ fun test: forall 'a. ('a & Bool, (Object & 'a) -> Bool) -> ("false" | "great" | "great, again" | "true") +//│ Code generation encountered an error: +//│ unknown match case: Object diff --git a/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls b/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls index 11f8763e..5ce3d311 100644 --- a/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls +++ b/shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls @@ -10,10 +10,10 @@ fun mixed_literals(v) = //│ Post-processed UCS term: //│ case v*‡ of //│ true*† -> "true" -//│ false*† -> "false" //│ 2 -> "2" //│ 1 -> "1" -//│ Desugared term: case v of { true => "true"; false => "false"; 2 => "2"; 1 => "1" } +//│ false*† -> "false" +//│ Desugared term: case v of { true => "true"; 2 => "2"; 1 => "1"; false => "false" } //│ fun mixed_literals: (1 | 2 | false | true) -> ("1" | "2" | "false" | "true") :ducs:postprocess.result diff --git a/shared/src/test/diff/ucs/ElseIf.mls b/shared/src/test/diff/ucs/ElseIf.mls index a24afcc3..99df8ea0 100644 --- a/shared/src/test/diff/ucs/ElseIf.mls +++ b/shared/src/test/diff/ucs/ElseIf.mls @@ -86,13 +86,13 @@ fun g(x, y) = if x is //│ ╔══[ERROR] `y` has 1 missing case //│ ║ l.84: true and y is true then true //│ ║ ^ -//│ ╟── it can be Boolean value false +//│ ╟── it can be Boolean value `false` //│ ║ l.85: false and y is false then false //│ ╙── ^^^^^ //│ ╔══[ERROR] `y` has 1 missing case //│ ║ l.85: false and y is false then false //│ ║ ^ -//│ ╟── it can be Boolean value true +//│ ╟── it can be Boolean value `true` //│ ║ l.84: true and y is true then true //│ ╙── ^^^^ //│ fun g: (Bool, nothing) -> Bool From 89a9c6f71978085bcf3c38dfdfe2b3a37eaa1928 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 24 Jan 2024 02:26:46 +0800 Subject: [PATCH 100/143] Fix post-processing cannot handle test branches --- .../mlscript/ucs/stages/PostProcessing.scala | 3 +++ .../diff/pretyper/ucs/stages/Normalization.mls | 16 +++++----------- shared/src/test/diff/ucs/OverlappedBranches.mls | 2 +- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index a955e9f2..5d435d33 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -14,6 +14,9 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => def postProcess(term: Term)(implicit context: Context): Term = trace(s"postProcess <== ${term.showDbg}") { // Normalized terms are constructed using `Let` and `CaseOf`. term match { + case top @ CaseOf(testVar: Var, fst @ Case(Var("true"), trueBranch, Wildcard(falseBranch))) if context.isTestVar(testVar) => + println(s"TEST: ${testVar.name}") + top.copy(cases = fst.copy(body = postProcess(trueBranch), rest = Wildcard(postProcess(falseBranch)))(refined = fst.refined)) case top @ CaseOf(Scrutinee(_), Wildcard(body)) => body case top @ CaseOf(Scrutinee.WithVar(scrutinee, scrutineeVar), fst @ Case(className: Var, body, NoCases)) => println(s"UNARY: ${scrutineeVar.name} is ${className.name}") diff --git a/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls index 0225a01b..4c6c885d 100644 --- a/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls +++ b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls @@ -127,11 +127,9 @@ fun test(x, p) = if x is //│ _ -> //│ case x*‡ of //│ 2.71828 -> "E" -//│ _ -> -//│ case x*‡ of -//│ 3.14159 -> "PI" -//│ _ -> "other" -//│ fun test: forall 'a. ('a & (2.71828 | Num & ~2.71828), (Num & 'a) -> Bool) -> ("E" | "PI" | "great" | "other") +//│ 3.14159 -> "PI" +//│ _ -> "other" +//│ fun test: forall 'a. ('a & (2.71828 | 3.14159 | Num & ~2.71828 & ~3.14159), (Num & 'a) -> Bool) -> ("E" | "PI" | "great" | "other") :ducs:postprocess.result fun test(x, p) = if x is @@ -147,9 +145,7 @@ fun test(x, p) = if x is //│ _ -> //│ case x*‡ of //│ true*† -> "false" -//│ _ -> -//│ case x*‡ of -//│ false*† -> "true" +//│ false*† -> "true" //│ fun test: forall 'a. ('a & Bool, (Bool & 'a) -> Bool) -> ("false" | "great" | "true") :ducs:postprocess.result @@ -174,9 +170,7 @@ fun test(x, p) = if x is //│ _ -> //│ case x*‡ of //│ true*† -> "false" -//│ _ -> -//│ case x*‡ of -//│ false*† -> "true" +//│ false*† -> "true" //│ fun test: forall 'a. ('a & Bool, (Object & 'a) -> Bool) -> ("false" | "great" | "great, again" | "true") //│ Code generation encountered an error: //│ unknown match case: Object diff --git a/shared/src/test/diff/ucs/OverlappedBranches.mls b/shared/src/test/diff/ucs/OverlappedBranches.mls index 13214135..00a44446 100644 --- a/shared/src/test/diff/ucs/OverlappedBranches.mls +++ b/shared/src/test/diff/ucs/OverlappedBranches.mls @@ -54,7 +54,7 @@ fun f2(x, p) = if x is Derived1 then "d1" Derived2 then "d2" else "otherwise" -//│ fun f2: forall 'a. ('a & (Base & ~#Derived1 | Derived1), (Base & 'a) -> Bool) -> ("b and p" | "d1" | "d2" | "otherwise") +//│ fun f2: forall 'a. ('a & (Base & ~#Derived1 & ~#Derived2 | Derived1 | Derived2), (Base & 'a) -> Bool) -> ("b and p" | "d1" | "d2" | "otherwise") f2(Base(), _ => true) // => b and p f2(Base(), _ => false) // otherwise From 357d7385445d265d9fe1148ea56032e0c44daa33 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 24 Jan 2024 02:30:50 +0800 Subject: [PATCH 101/143] Fix a mistake from a previous commit --- js/src/main/scala/Main.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/main/scala/Main.scala b/js/src/main/scala/Main.scala index a67f8ce8..1cfa2096 100644 --- a/js/src/main/scala/Main.scala +++ b/js/src/main/scala/Main.scala @@ -159,7 +159,7 @@ object Main { |""".stripMargin val backend = new JSWebBackend() - val (lines, resNames) = backend(pgrm, true) + val (lines, resNames) = backend(pgrm) val code = lines.mkString("\n") // TODO: add a toggle button to show js code From 03ead0e2c386d4bb6ffdc615a9e9e4b198e0a052 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 24 Jan 2024 02:44:59 +0800 Subject: [PATCH 102/143] Clean up `TODO` and document mutable variables --- .../main/scala/mlscript/ucs/Desugarer.scala | 8 +++--- .../mlscript/ucs/stages/Desugaring.scala | 1 - .../main/scala/mlscript/ucs/syntax/core.scala | 26 +++++++------------ 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/Desugarer.scala b/shared/src/main/scala/mlscript/ucs/Desugarer.scala index 8c3a7a21..d5b7719c 100644 --- a/shared/src/main/scala/mlscript/ucs/Desugarer.scala +++ b/shared/src/main/scala/mlscript/ucs/Desugarer.scala @@ -78,13 +78,13 @@ trait Desugarer extends Transformation /** * A short hand for `nme.symbol.getScrutinee` but add a diagnostic message - * to a local diagnostic archive (TODO) if there's any error. + * to a local diagnostic archive if there's any error. */ def getOrCreateScrutinee(implicit context: Context): Scrutinee = nme.symbolOption match { case S(symbol: TermSymbol) => symbol.getOrCreateScrutinee case S(otherSymbol) => raiseDesugaringError(msg"cannot identifier `${nme.name}` with a scrutinee" -> nme.toLoc) - context.freshScrutinee // TODO + context.freshScrutinee case N => lastWords(s"`${nme.name}` should be assoicated with a symbol") } @@ -109,7 +109,7 @@ trait Desugarer extends Transformation case S(symbol: TermSymbol) => symbol case S(otherSymbol) => raiseDesugaringError(msg"identifier `${nme.name}` should be a term" -> nme.toLoc) - freshSymbol(nme) // TODO: Maybe we should maintain a "lost" symbol map. + freshSymbol(nme) } } @@ -117,7 +117,7 @@ trait Desugarer extends Transformation def resolveTermSymbol(implicit scope: Scope): TermSymbol = { val symbol = scope.getTermSymbol(nme.name).getOrElse { raiseDesugaringError(msg"identifier `${nme.name}` not found" -> nme.toLoc) - freshSymbol(nme) // TODO: Maybe we should maintain a "lost" symbol map. + freshSymbol(nme) } nme.symbol = symbol symbol diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index d4ef2fd6..5fe5e569 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -298,7 +298,6 @@ trait Desugaring { self: PreTyper => } }() - // TODO: `aliasVars` not computed private def flattenTupleFields( parentScrutineeVar: Var, parentScrutinee: Scrutinee, diff --git a/shared/src/main/scala/mlscript/ucs/syntax/core.scala b/shared/src/main/scala/mlscript/ucs/syntax/core.scala index db52d4e7..fa73d74e 100644 --- a/shared/src/main/scala/mlscript/ucs/syntax/core.scala +++ b/shared/src/main/scala/mlscript/ucs/syntax/core.scala @@ -24,28 +24,21 @@ package object core { override def children: Ls[Located] = nme :: Nil } /** - * TODO: Consider make `refined` immutable. * @param nme the name of the class-like symbol * @param originallyRefined whether the class is marked as refined from - * in source code + * in source AST */ - final case class Class(nme: Var, symbol: TypeSymbol, val originallyRefined: Bool) extends Pattern { + final case class Class(nme: Var, symbol: TypeSymbol, originallyRefined: Bool) extends Pattern { override def children: Ls[Located] = nme :: Nil + /** + * A mutable field to override the refinement status of the class. + * During normalization, if a case can be further refined in its + * descendants branches, we should mark it as `refined`. See relevant + * tests in `Normalization.mls`. + */ var refined: Bool = originallyRefined } - - def getParametersLoc(parameters: List[Opt[Var]]): Opt[Loc] = - parameters.foldLeft(None: Opt[Loc]) { - case (acc, N) => acc - case (N, S(nme)) => nme.toLoc - case (S(loc), S(nme)) => S(nme.toLoc.fold(loc)(loc ++ _)) - } - def getParametersLoc(parameters: Opt[List[Opt[Var]]]): Opt[Loc] = - parameters.fold(None: Opt[Loc])(getParametersLoc) - - def showParameters(parameters: Opt[List[Opt[Var]]]): Str = - parameters.fold("empty")(_.map(_.fold("_")(_.name)).mkString("[", ", ", "]")) } final case class Branch(scrutinee: Var, pattern: Pattern, continuation: Split) extends Located { @@ -53,8 +46,7 @@ package object core { } sealed abstract class Split extends Located { - @inline - def ::(head: Branch): Split = Split.Cons(head, this) + @inline def ::(head: Branch): Split = Split.Cons(head, this) /** * Returns true if the split has an else branch. From 52b4505ed32f590f65d4e65d11f087aa8e1308a6 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 24 Jan 2024 03:07:31 +0800 Subject: [PATCH 103/143] Remove useless helper functions --- .../mlscript/ucs/context/Scrutinee.scala | 6 ++- .../scala/mlscript/ucs/context/package.scala | 9 ---- .../ucs/stages/CoverageChecking.scala | 10 ++-- .../mlscript/ucs/stages/Desugaring.scala | 4 +- .../mlscript/ucs/stages/Normalization.scala | 8 ++-- .../mlscript/ucs/stages/PartialTerm.scala | 8 ++-- .../mlscript/ucs/stages/PostProcessing.scala | 6 +-- .../scala/mlscript/ucs/stages/package.scala | 47 +++---------------- 8 files changed, 28 insertions(+), 70 deletions(-) delete mode 100644 shared/src/main/scala/mlscript/ucs/context/package.scala diff --git a/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala index b8b03805..9d8dc7b6 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala @@ -120,10 +120,12 @@ object Scrutinee { case _ => N } + type WithVar = Var -> Scrutinee + object WithVar { - def unapply(term: Term)(implicit context: Context): Opt[(Scrutinee, Var)] = term match { + def unapply(term: Term)(implicit context: Context): Opt[WithVar] = term match { case v @ Var(_) => v.symbol match { - case symbol: TermSymbol => symbol.getScrutinee.map(_ -> v) + case symbol: TermSymbol => symbol.getScrutinee.map(v -> _) case _ => N } case _ => N diff --git a/shared/src/main/scala/mlscript/ucs/context/package.scala b/shared/src/main/scala/mlscript/ucs/context/package.scala deleted file mode 100644 index d9c6e319..00000000 --- a/shared/src/main/scala/mlscript/ucs/context/package.scala +++ /dev/null @@ -1,9 +0,0 @@ -package mlscript.ucs - -import mlscript.{Loc, Var} -import mlscript.pretyper.symbol.TypeSymbol -import mlscript.utils._, shorthands._ - -package object context { - type NamedScrutinee = (Var -> Scrutinee) -} diff --git a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala index 45e92149..2639c99a 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala @@ -4,7 +4,7 @@ import mlscript.{Case, CaseBranches, CaseOf, Let, Lit, Loc, NoCases, Term, Var, import mlscript.{Diagnostic, ErrorReport, WarningReport} import mlscript.Message, Message.MessageContext import mlscript.ucs.Desugarer -import mlscript.ucs.context.{Context, NamedScrutinee, Pattern, Scrutinee} +import mlscript.ucs.context.{Context, Pattern, Scrutinee} import mlscript.pretyper.Traceable import mlscript.pretyper.symbol._ import mlscript.utils._, shorthands._ @@ -39,7 +39,7 @@ trait CoverageChecking { self: Desugarer with Traceable => println(s"checkCoverage <== TEST `${scrutineeVar.name}`") checkCoverage(whenTrue, pending, working, seen) ++ checkCoverage(whenFalse, pending, working, seen) - case CaseOf(Scrutinee.WithVar(scrutinee, scrutineeVar), cases) => + case CaseOf(Scrutinee.WithVar(scrutineeVar, scrutinee), cases) => trace(s"checkCoverage <== ${pending.size} pending, ${working.size} working, ${seen.size} seen") { println(s"CASE ${scrutineeVar.name}") println(s"SEEN: ${seen.showDbg}") @@ -131,7 +131,7 @@ trait CoverageChecking { self: Desugarer with Traceable => object CoverageChecking { /** Create an `ErrorReport` that explains missing cases. */ private def explainMissingCases( - scrutinee: NamedScrutinee, + scrutinee: Scrutinee.WithVar, seen: SeenRegistry, missingCases: CaseSet )(implicit context: Context): Opt[ErrorReport] = @@ -164,9 +164,9 @@ object CoverageChecking { matchedClasses.patterns.iterator.map(_.showInDiagnostics).mkString(s">>> ${scrutineeVar.name} => [", ", ", "]") }.mkString("\n", "\n", "") - type ScrutineePatternSetMap = Map[NamedScrutinee, CaseSet] + type ScrutineePatternSetMap = Map[Scrutinee.WithVar, CaseSet] - type SeenRegistry = Map[NamedScrutinee, (TypeSymbol, Ls[Loc], CaseSet)] + type SeenRegistry = Map[Scrutinee.WithVar, (TypeSymbol, Ls[Loc], CaseSet)] implicit class SeenRegistryOps(val self: SeenRegistry) extends AnyVal { def showDbg: Str = if (self.isEmpty) "empty" else diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 5fe5e569..1a22952b 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -1,6 +1,6 @@ package mlscript.ucs.stages -import mlscript.{App, Asc, Fld, FldFlags, Lit, Sel, Term, Tup, TypeName, Var} +import mlscript.{App, Asc, Fld, FldFlags, Lit, Sel, PlainTup, Term, Tup, TypeName, Var} import mlscript.ucs.syntax.{core => c, source => s} import mlscript.ucs.context.{Context, Scrutinee} import mlscript.utils._, shorthands._ @@ -388,7 +388,7 @@ trait Desugaring { self: PreTyper => ) :: desugarTail case s.ConcretePattern(nme) => val testVar = context.freshTest().withFreshSymbol - val testTerm = mkBinOp(scrutineeVar, Var("==="), nme, true) + val testTerm = App(Var("==="), PlainTup(scrutineeVar, nme)) c.Split.Let(false, testVar, testTerm, c.Branch(testVar, truePattern, desugarRight(scope + testVar.symbol)) :: desugarTail) case s.EmptyPattern(_) | s.NamePattern(Var("_")) => diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index cb3f2c84..8871c34c 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -136,7 +136,7 @@ trait Normalization { self: Desugarer with Traceable => val trueBranch = normalizeToTerm(continuation.fill(tail, declaredVars, false), declaredVars) val falseBranch = normalizeToCaseBranches(tail, declaredVars) CaseOf(test, Case(nme, trueBranch, falseBranch)(refined = false)) - case Split.Cons(Branch(Scrutinee.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Literal(literal), continuation), tail) => + case Split.Cons(Branch(Scrutinee.WithVar(scrutineeVar, scrutinee), pattern @ Pattern.Literal(literal), continuation), tail) => println(s"LITERAL: ${scrutineeVar.name} is ${literal.idStr}") println(s"entire split: ${showSplit(split)}") val concatenatedTrueBranch = continuation.fill(tail, declaredVars, false) @@ -145,7 +145,7 @@ trait Normalization { self: Desugarer with Traceable => // println(s"false branch: ${showSplit(tail)}") val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context), declaredVars) CaseOf(scrutineeVar, Case(literal, trueBranch, falseBranch)(refined = false)) - case Split.Cons(Branch(Scrutinee.WithVar(scrutinee, scrutineeVar), pattern @ Pattern.Class(nme, _, rfd), continuation), tail) => + case Split.Cons(Branch(Scrutinee.WithVar(scrutineeVar, scrutinee), pattern @ Pattern.Class(nme, _, rfd), continuation), tail) => println(s"CLASS: ${scrutineeVar.name} is ${nme.name}") // println(s"match ${scrutineeVar.name} with $nme (has location: ${nme.toLoc.isDefined})") val trueBranch = normalizeToTerm(specialize(continuation.fill(tail, declaredVars, false), true)(scrutineeVar, scrutinee, pattern, context), declaredVars) @@ -211,7 +211,7 @@ trait Normalization { self: Desugarer with Traceable => case (m, Split.Cons(head @ Branch(test, Pattern.Class(Var("true"), _, _), continuation), tail)) if context.isTestVar(test) => println(s"TEST: ${test.name} is true") head.copy(continuation = specialize(continuation, m)) :: specialize(tail, m) - case (true, split @ Split.Cons(head @ Branch(Scrutinee.WithVar(otherScrutinee, otherScrutineeVar), otherPattern, continuation), tail)) => + case (true, split @ Split.Cons(head @ Branch(Scrutinee.WithVar(otherScrutineeVar, otherScrutinee), otherPattern, continuation), tail)) => if (scrutinee === otherScrutinee) { println(s"Case 1: ${scrutineeVar.name} === ${otherScrutineeVar.name}") if (otherPattern =:= pattern) { @@ -229,7 +229,7 @@ trait Normalization { self: Desugarer with Traceable => println(s"Case 2: ${scrutineeVar.name} === ${otherScrutineeVar.name}") head.copy(continuation = specialize(continuation, true)) :: specialize(tail, true) } - case (false, split @ Split.Cons(head @ Branch(Scrutinee.WithVar(otherScrutinee, otherScrutineeVar), otherPattern, continuation), tail)) => + case (false, split @ Split.Cons(head @ Branch(Scrutinee.WithVar(otherScrutineeVar, otherScrutinee), otherPattern, continuation), tail)) => if (scrutinee === otherScrutinee) { println(s"Case 1: ${scrutineeVar.name} === ${otherScrutineeVar.name}") otherPattern reportInconsistentRefinedWith pattern diff --git a/shared/src/main/scala/mlscript/ucs/stages/PartialTerm.scala b/shared/src/main/scala/mlscript/ucs/stages/PartialTerm.scala index 7dac085b..dc13f689 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PartialTerm.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PartialTerm.scala @@ -1,6 +1,6 @@ package mlscript.ucs.stages -import mlscript.{Term, Var}, mlscript.utils._, shorthands._ +import mlscript.{App, PlainTup, Term, Var}, mlscript.utils._, shorthands._ /** * A `PartialTerm` represents a possibly incomplete term. @@ -36,11 +36,11 @@ object PartialTerm { final case class Half(lhs: Term, op: Var, parts: Ls[Term]) extends Incomplete { override def terms: Iterator[Term] = parts.reverseIterator def addTerm(rhs: Term): Total = { - val (realRhs, extraExprOpt) = separatePattern(rhs, true) - val leftmost = mkBinOp(lhs, op, realRhs, true) + val (realRhs, extraExprOpt) = separatePattern(rhs) + val leftmost = App(op, PlainTup(lhs, realRhs)) extraExprOpt match { case N => Total(leftmost, parts) - case S(extraExpr) => Total(mkBinOp(leftmost, Var("and"), extraExpr, true), extraExpr :: parts) + case S(extraExpr) => Total(App(Var("and"), PlainTup(leftmost, extraExpr)), extraExpr :: parts) } } } diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index 5d435d33..52d385b1 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -18,10 +18,10 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => println(s"TEST: ${testVar.name}") top.copy(cases = fst.copy(body = postProcess(trueBranch), rest = Wildcard(postProcess(falseBranch)))(refined = fst.refined)) case top @ CaseOf(Scrutinee(_), Wildcard(body)) => body - case top @ CaseOf(Scrutinee.WithVar(scrutinee, scrutineeVar), fst @ Case(className: Var, body, NoCases)) => + case top @ CaseOf(Scrutinee.WithVar(scrutineeVar, scrutinee), fst @ Case(className: Var, body, NoCases)) => println(s"UNARY: ${scrutineeVar.name} is ${className.name}") top.copy(cases = fst.copy(body = postProcess(body))(refined = fst.refined)) - case top @ CaseOf(Scrutinee.WithVar(scrutinee, scrutineeVar), fst @ Case(pat, trueBranch, Wildcard(falseBranch))) => + case top @ CaseOf(Scrutinee.WithVar(scrutineeVar, scrutinee), fst @ Case(pat, trueBranch, Wildcard(falseBranch))) => println(s"BINARY: ${scrutineeVar.name} is ${pat.showDbg}") println(s"patterns of `${scrutineeVar.name}`: ${scrutinee.showPatternsDbg}") // Post-process the true branch. @@ -152,7 +152,7 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => ): (Term, Opt[Term]) = { def rec(term: Term): (Term, Opt[Term]) = term match { - case top @ CaseOf(Scrutinee.WithVar(otherScrutinee, otherScrutineeVar), cases) => + case top @ CaseOf(Scrutinee.WithVar(otherScrutineeVar, otherScrutinee), cases) => if (scrutinee === otherScrutinee) { println(s"found a `CaseOf` that matches on `${scrutineeVar.name}`") val (n, y) = disentangleMatchedCaseBranches(cases) diff --git a/shared/src/main/scala/mlscript/ucs/stages/package.scala b/shared/src/main/scala/mlscript/ucs/stages/package.scala index 15c98af6..fe25dd00 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/package.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/package.scala @@ -5,30 +5,6 @@ import mlscript.pretyper.symbol.TypeSymbol import mlscript.utils._, shorthands._ package object stages { - /** - * Make a tuple with only one element. For example, - * - * ```scala - * mkMonuple(t) = Tup(N -> Fld(false, false, t) :: Nil) - * ``` - * - * @param t the sole element - * @return a tuple term with the only element - */ - def mkMonuple(t: Term): Tup = Tup(N -> Fld(FldFlags.empty, t) :: Nil) - - /** - * Make a binary operation. - * - * @param lhs the left-hand side term - * @param op the operator - * @param rhs the right-hand side term - * @return something like `App(App(op, lhs), rhs)` - */ - def mkBinOp(lhs: Term, op: Var, rhs: Term, newDefs: Bool): Term = - if (newDefs) App(op, PlainTup(lhs, rhs)) - else App(App(op, mkMonuple(lhs)), mkMonuple(rhs)) - /** * Split a term into two parts: the pattern and the extra test. * This is used to extract patterns from UCS conjunctions. For example, @@ -47,24 +23,13 @@ package object stages { * @return a tuple, whose the first element is the pattern and the second * element is the extra test */ - def separatePattern(term: Term, newDefs: Bool): (Term, Opt[Term]) = - term match { - case App( - App(and @ Var("and"), - Tup((_ -> Fld(_, lhs)) :: Nil)), - Tup((_ -> Fld(_, rhs)) :: Nil) - ) => // * Old-style operators - separatePattern(lhs, newDefs) match { - case (pattern, N) => (pattern, S(rhs)) - case (pattern, S(lshRhs)) => (pattern, S(mkBinOp(lshRhs, and, rhs, newDefs))) - } - case App(and @ Var("and"), PlainTup(lhs, rhs)) => - separatePattern(lhs, newDefs) match { - case (pattern, N) => (pattern, S(rhs)) - case (pattern, S(lshRhs)) => (pattern, S(mkBinOp(lshRhs, and, rhs, newDefs))) - } - case _ => (term, N) + def separatePattern(term: Term): (Term, Opt[Term]) = term match { + case App(and @ Var("and"), PlainTup(lhs, rhs)) => separatePattern(lhs) match { + case (pattern, N) => (pattern, S(rhs)) + case (pattern, S(lhsRhs)) => (pattern, S(App(and, PlainTup(lhsRhs, rhs)))) } + case _ => (term, N) + } /** * Hard-coded subtyping relations used in normalization and coverage checking. From 2061c9499393e9edbfd65fdc9bd2621fb29e0813 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sat, 3 Feb 2024 14:26:24 +0800 Subject: [PATCH 104/143] Found a bug in specialization --- .../pretyper/ucs/stages/Normalization.mls | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls index 4c6c885d..ec861498 100644 --- a/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls +++ b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls @@ -112,6 +112,27 @@ fun test(x, p) = if x is //│ _ -> "qax" //│ fun test: forall 'a. (Str & 'a, (Str & 'a) -> Bool) -> ("bar" | "foo" | "qax") +// This should work, but it doesn't. +test(0, _ => true) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.116: test(0, _ => true) +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ╟── integer literal of type `0` is not an instance of type `Str` +//│ ║ l.116: test(0, _ => true) +//│ ║ ^ +//│ ╟── Note: constraint arises from class pattern: +//│ ║ l.100: Str and p(x) then "foo" +//│ ║ ^^^ +//│ ╟── from reference: +//│ ║ l.99: fun test(x, p) = if x is +//│ ╙── ^ +//│ TEST CASE FAILURE: There was an unexpected type error +//│ "bar" | "foo" | "qax" | error +//│ res +//│ TEST CASE FAILURE: There was an unexpected runtime error +//│ Runtime error: +//│ ReferenceError: string is not defined + :ducs:postprocess.result fun test(x, p) = if x is Num and p(x) then "great" From 832858efeeff34fb16f4c0c28142af4843ba3843 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sat, 3 Feb 2024 14:28:49 +0800 Subject: [PATCH 105/143] Fix the specialization bug indicated by previous commit --- .../src/main/scala/mlscript/JSBackend.scala | 6 ++-- .../mlscript/ucs/stages/Normalization.scala | 2 +- .../pretyper/ucs/SpecilizationCollision.mls | 8 +++-- .../pretyper/ucs/stages/Normalization.mls | 29 +++++-------------- .../src/test/diff/ucs/OverlappedBranches.mls | 2 +- 5 files changed, 18 insertions(+), 29 deletions(-) diff --git a/shared/src/main/scala/mlscript/JSBackend.scala b/shared/src/main/scala/mlscript/JSBackend.scala index b6cd1875..9c10f8e0 100644 --- a/shared/src/main/scala/mlscript/JSBackend.scala +++ b/shared/src/main/scala/mlscript/JSBackend.scala @@ -364,11 +364,11 @@ abstract class JSBackend(allowUnresolvedSymbols: Bool) { case Var("Int") if !oldDefs => JSInvoke(JSField(JSIdent("Number"), "isInteger"), scrut :: Nil) case Var("Num") if !oldDefs => - JSBinary("===", scrut.typeof(), JSLit("number")) + JSBinary("===", scrut.typeof(), JSLit(JSLit.makeStringLiteral("number"))) case Var("Bool") if !oldDefs => - JSBinary("===", scrut.typeof(), JSLit("boolean")) + JSBinary("===", scrut.typeof(), JSLit(JSLit.makeStringLiteral("boolean"))) case Var("Str") if !oldDefs => - JSBinary("===", scrut.typeof(), JSLit("string")) + JSBinary("===", scrut.typeof(), JSLit(JSLit.makeStringLiteral("string"))) case Var("bool") => JSBinary("===", scrut.member("constructor"), JSLit("Boolean")) case Var(s @ ("true" | "false")) => diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 8871c34c..e1362746 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -238,7 +238,7 @@ trait Normalization { self: Desugarer with Traceable => specialize(tail, false) } else if (otherPattern <:< pattern) { println(s"Case 2.2: $pattern <:< $otherPattern") - Split.Nil + specialize(tail, false) } else { println(s"Case 2.3: $pattern are unrelated with $otherPattern") split.copy(tail = specialize(tail, false)) diff --git a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls index c8be5625..5caea1d3 100644 --- a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls +++ b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls @@ -59,6 +59,7 @@ fun example3(t) = //│ let y*‡ = (ucs$args_t$Derived).0 //│ y //│ _ -> 42 +//│ _ -> 42 //│ | | | | | | | STEP 4 //│ | | | | | | | collected match registry: //│ | | | | | | | >>> t => [class `Base`, class `Derived`] @@ -89,10 +90,11 @@ fun example3(t) = //│ | | | | | | | | | remaining cases should be covered by the wildcard //│ | | | | | | | | | checkCoverage <== TERM 42 //│ | | | | | | | | checkCoverage ==> 0 diagnostics -//│ | | | | | | | | remaining cases are not covered: [] +//│ | | | | | | | | remaining cases should be covered by the wildcard +//│ | | | | | | | | checkCoverage <== TERM 42 //│ | | | | | | | checkCoverage ==> 0 diagnostics //│ | | | | | | | Coverage checking result: 0 errors -//│ fun example3: forall 'a. (Base & {#x: Num & 'a}) -> (Int | 'a) +//│ fun example3: forall 'a. (Base & {#x: Num & 'a} | Object & ~#Base) -> (Int | 'a) fun example4(t, x) = if t is @@ -103,7 +105,7 @@ fun example4(t, x) = // previous branch shadows the variable `x`, a correct implementation // should restore the original value of `x` in this branch. else 42 -//│ fun example4: forall 'a. (Base & {#x: Num & 'a}, Int) -> (Int | 'a) +//│ fun example4: forall 'a. (Base & {#x: Num & 'a} | Object & ~#Base, Int) -> (Int | 'a) example4(Base(-1), 0) ~~> -1 example4(Base(1), 2) ~~> 42 diff --git a/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls index ec861498..b2f41c39 100644 --- a/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls +++ b/shared/src/test/diff/pretyper/ucs/stages/Normalization.mls @@ -93,7 +93,8 @@ fun test(x, p) = if x is //│ case x*‡ of //│ 0 -> "bar" //│ _ -> "qax" -//│ fun test: forall 'a. (Int & 'a, (Int & 'a) -> Bool) -> ("bar" | "foo" | "qax") +//│ _ -> "qax" +//│ fun test: forall 'a. (Int & 'a | Object & ~Int, (Int & 'a) -> Bool) -> ("bar" | "foo" | "qax") :ducs:postprocess.result fun test(x, p) = if x is @@ -110,28 +111,13 @@ fun test(x, p) = if x is //│ case x*‡ of //│ "lol" -> "bar" //│ _ -> "qax" -//│ fun test: forall 'a. (Str & 'a, (Str & 'a) -> Bool) -> ("bar" | "foo" | "qax") +//│ _ -> "qax" +//│ fun test: forall 'a. (Object & ~Str | Str & 'a, (Str & 'a) -> Bool) -> ("bar" | "foo" | "qax") -// This should work, but it doesn't. test(0, _ => true) -//│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.116: test(0, _ => true) -//│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ╟── integer literal of type `0` is not an instance of type `Str` -//│ ║ l.116: test(0, _ => true) -//│ ║ ^ -//│ ╟── Note: constraint arises from class pattern: -//│ ║ l.100: Str and p(x) then "foo" -//│ ║ ^^^ -//│ ╟── from reference: -//│ ║ l.99: fun test(x, p) = if x is -//│ ╙── ^ -//│ TEST CASE FAILURE: There was an unexpected type error -//│ "bar" | "foo" | "qax" | error +//│ "bar" | "foo" | "qax" //│ res -//│ TEST CASE FAILURE: There was an unexpected runtime error -//│ Runtime error: -//│ ReferenceError: string is not defined +//│ = 'qax' :ducs:postprocess.result fun test(x, p) = if x is @@ -150,7 +136,8 @@ fun test(x, p) = if x is //│ 2.71828 -> "E" //│ 3.14159 -> "PI" //│ _ -> "other" -//│ fun test: forall 'a. ('a & (2.71828 | 3.14159 | Num & ~2.71828 & ~3.14159), (Num & 'a) -> Bool) -> ("E" | "PI" | "great" | "other") +//│ _ -> "other" +//│ fun test: forall 'a. (Object & ~Num | 'a & (2.71828 | 3.14159 | Num & ~2.71828 & ~3.14159), (Num & 'a) -> Bool) -> ("E" | "PI" | "great" | "other") :ducs:postprocess.result fun test(x, p) = if x is diff --git a/shared/src/test/diff/ucs/OverlappedBranches.mls b/shared/src/test/diff/ucs/OverlappedBranches.mls index 00a44446..679ccddc 100644 --- a/shared/src/test/diff/ucs/OverlappedBranches.mls +++ b/shared/src/test/diff/ucs/OverlappedBranches.mls @@ -54,7 +54,7 @@ fun f2(x, p) = if x is Derived1 then "d1" Derived2 then "d2" else "otherwise" -//│ fun f2: forall 'a. ('a & (Base & ~#Derived1 & ~#Derived2 | Derived1 | Derived2), (Base & 'a) -> Bool) -> ("b and p" | "d1" | "d2" | "otherwise") +//│ fun f2: forall 'a. (Object & ~#Base | 'a & (Base & ~#Derived1 & ~#Derived2 | Derived1 | Derived2), (Base & 'a) -> Bool) -> ("b and p" | "d1" | "d2" | "otherwise") f2(Base(), _ => true) // => b and p f2(Base(), _ => false) // otherwise From dd3c35ed9ea5c87260d82a34401ed1cb7a02246c Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sat, 3 Feb 2024 14:32:14 +0800 Subject: [PATCH 106/143] Simplify specialization implementation --- .../main/scala/mlscript/ucs/stages/Normalization.scala | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index e1362746..62a4036c 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -233,14 +233,11 @@ trait Normalization { self: Desugarer with Traceable => if (scrutinee === otherScrutinee) { println(s"Case 1: ${scrutineeVar.name} === ${otherScrutineeVar.name}") otherPattern reportInconsistentRefinedWith pattern - if (otherPattern =:= pattern) { - println(s"Case 2.1: $pattern =:= $otherPattern") - specialize(tail, false) - } else if (otherPattern <:< pattern) { - println(s"Case 2.2: $pattern <:< $otherPattern") + if (otherPattern =:= pattern || otherPattern <:< pattern) { + println(s"Case 1.1: $pattern =:= (or <:<) $otherPattern") specialize(tail, false) } else { - println(s"Case 2.3: $pattern are unrelated with $otherPattern") + println(s"Case 1.2: $pattern are unrelated with $otherPattern") split.copy(tail = specialize(tail, false)) } } else { From 5783766719c2b049ba5564d37bf8ecada34efe50 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Mon, 19 Feb 2024 17:35:21 +0800 Subject: [PATCH 107/143] Fix refinement information is not passed to post-processing --- .../scala/mlscript/ucs/context/Pattern.scala | 14 +++++- .../mlscript/ucs/context/Scrutinee.scala | 4 +- .../mlscript/ucs/stages/Desugaring.scala | 14 +++--- .../mlscript/ucs/stages/PostProcessing.scala | 3 +- .../src/test/diff/pretyper/ucs/Refinement.mls | 49 +++++++++++++++++++ 5 files changed, 73 insertions(+), 11 deletions(-) create mode 100644 shared/src/test/diff/pretyper/ucs/Refinement.mls diff --git a/shared/src/main/scala/mlscript/ucs/context/Pattern.scala b/shared/src/main/scala/mlscript/ucs/context/Pattern.scala index cfb1d45f..8fa96aec 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Pattern.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Pattern.scala @@ -34,10 +34,15 @@ sealed abstract class Pattern { /** Create a `SimpleTerm` which can be used as `pat` of `Case`. */ def toCasePattern: SimpleTerm + + def refined: Bool } object Pattern { - final case class ClassLike(val classLikeSymbol: TypeSymbol, scrutinee: Scrutinee) extends Pattern { + final case class ClassLike( + val classLikeSymbol: TypeSymbol, + scrutinee: Scrutinee + )(override val refined: Bool) extends Pattern { private var unappliedVarOpt: Opt[Var] = N private val parameters: MutSortedMap[Int, Scrutinee] = MutSortedMap.empty @@ -65,7 +70,8 @@ object Pattern { override def arity: Opt[Int] = parameters.keysIterator.maxOption.map(_ + 1) - override def showDbg: Str = s"${classLikeSymbol.name}" + override def showDbg: Str = + (if (refined) "refined " else "") + s"${classLikeSymbol.name}" override def showInDiagnostics: Str = s"${(classLikeSymbol match { case dummySymbol: DummyClassSymbol => "class" @@ -112,6 +118,8 @@ object Pattern { * find an instance of this class. */ override def toCasePattern: SimpleTerm = ??? + + override def refined: Bool = ??? } final case class Literal(val literal: Lit) extends Pattern { @@ -128,5 +136,7 @@ object Pattern { } override def toCasePattern: SimpleTerm = literal.withLoc(firstOccurrence) + + override def refined: Bool = false } } diff --git a/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala index 9d8dc7b6..999c2cd9 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala @@ -36,8 +36,8 @@ class Scrutinee(val context: Context, parent: Opt[Scrutinee]) { * If there is already a `Pattern.ClassLike` for the given symbol, return it. * Otherwise, create a new `Pattern.ClassLike` and return it. */ - def getOrCreateClassPattern(classLikeSymbol: TypeSymbol): Pattern.ClassLike = - classLikePatterns.getOrElseUpdate(classLikeSymbol, Pattern.ClassLike(classLikeSymbol, this)) + def getOrCreateClassPattern(classLikeSymbol: TypeSymbol, refined: Bool): Pattern.ClassLike = + classLikePatterns.getOrElseUpdate(classLikeSymbol, Pattern.ClassLike(classLikeSymbol, this)(refined)) /** * Get the class pattern but DO NOT create a new one if there isn't. This diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 1a22952b..7301ce60 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -136,10 +136,11 @@ trait Desugaring { self: PreTyper => parentScrutineeVar: Var, parentScrutinee: Scrutinee, parentClassLikeSymbol: TypeSymbol, - parameters: Ls[s.Pattern] + parentRefined: Bool, + parameters: Ls[s.Pattern], )(implicit context: Context): Ls[Opt[(Var, Opt[s.Pattern], Ls[Var])]] = { // Make it `lazy` so that it will not be created if all fields are wildcards. - lazy val classPattern = parentScrutinee.getOrCreateClassPattern(parentClassLikeSymbol) + lazy val classPattern = parentScrutinee.getOrCreateClassPattern(parentClassLikeSymbol, parentRefined) def flattenPattern( parameterPattern: s.Pattern, index: Int, @@ -197,10 +198,11 @@ trait Desugaring { self: PreTyper => pattern: s.ClassPattern, scrutineeVar: Var, initialScope: Scope, - refined: Bool)(implicit context: Context): (Scope, c.Split => c.Branch) = { + refined: Bool + )(implicit context: Context): (Scope, c.Split => c.Branch) = { val scrutinee = scrutineeVar.getOrCreateScrutinee.withAliasVar(scrutineeVar) val patternClassSymbol = pattern.nme.resolveClassLikeSymbol(initialScope, context) - val classPattern = scrutinee.getOrCreateClassPattern(patternClassSymbol) + val classPattern = scrutinee.getOrCreateClassPattern(patternClassSymbol, refined) println(s"desugarClassPattern: ${scrutineeVar.name} is ${pattern.nme.name}") classPattern.addLocation(pattern.nme) val (scopeWithAll, bindAll) = pattern.parameters match { @@ -213,7 +215,7 @@ trait Desugaring { self: PreTyper => val vari = makeUnappliedVar(scrutineeVar, pattern.nme) vari.withSymbol(new LocalTermSymbol(vari)) } - val nestedPatterns = flattenClassParameters(scrutineeVar, scrutinee, patternClassSymbol, parameters) + val nestedPatterns = flattenClassParameters(scrutineeVar, scrutinee, patternClassSymbol, refined, parameters) println(s"nestedPatterns = $nestedPatterns") // First, handle bindings of parameters of the current class pattern. val identity = (split: c.Split) => split @@ -380,7 +382,7 @@ trait Desugaring { self: PreTyper => c.Branch(scrutineeVar, c.Pattern.Literal(literal), desugarRight) :: desugarTail case s.ConcretePattern(nme @ (Var("true") | Var("false"))) => val classSymbol = nme.resolveClassLikeSymbol - scrutinee.getOrCreateClassPattern(classSymbol).addLocation(nme) + scrutinee.getOrCreateClassPattern(classSymbol, false).addLocation(nme) c.Branch( scrutinee = scrutineeVar, pattern = c.Pattern.Class(nme, classSymbol, false), diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index 52d385b1..2974e307 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -7,6 +7,7 @@ import mlscript.pretyper.symbol._ import mlscript.utils._, shorthands._ import mlscript.Message, Message.MessageContext import scala.annotation.tailrec +import mlscript.ucs.context.Pattern.ClassLike trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => import PostProcessing._ @@ -63,7 +64,7 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => val actualFalseBranch = cases.foldRight[CaseBranches]( postProcessedDefault.fold[CaseBranches](NoCases)(Wildcard(_)) ) { case ((pattern, loc, body), rest) => - Case(pattern.toCasePattern, body, rest)(refined = false/*FIXME?*/) + Case(pattern.toCasePattern, body, rest)(refined = pattern.refined) } // Assemble the final `CaseOf`. top.copy(cases = fst.copy(body = processedTrueBranch, rest = actualFalseBranch) diff --git a/shared/src/test/diff/pretyper/ucs/Refinement.mls b/shared/src/test/diff/pretyper/ucs/Refinement.mls new file mode 100644 index 00000000..95b2b48a --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/Refinement.mls @@ -0,0 +1,49 @@ +:NewDefs + +module None +class Some[out A](val value: A) +//│ module None +//│ class Some[A](value: A) + +// OK +:ducs:desugar.result,postprocess.result +x => if x is + refined(None) then x +//│ Desugared UCS term: +//│ if x*‡ is refined None then x +//│ Post-processed UCS term: +//│ case x*‡ of +//│ refined None*† -> x +//│ forall 'a. (None & 'a) -> (None & 'a) +//│ res +//│ = [Function: res] + +// OK +:ducs:desugar.result,postprocess.result +x => if x is + refined(Some) then x +//│ Desugared UCS term: +//│ if x*‡ is refined Some then x +//│ Post-processed UCS term: +//│ case x*‡ of +//│ refined Some*◊ -> x +//│ forall 'a. (Some[anything] & 'a) -> (Some[anything] & 'a) +//│ res +//│ = [Function: res] + +// NOT OK +:ducs:desugar.result,postprocess.result +x => if x is + refined(None) then x + refined(Some) then x +//│ Desugared UCS term: +//│ if +//│ x*‡ is refined None then x +//│ x*‡ is refined Some then x +//│ Post-processed UCS term: +//│ case x*‡ of +//│ refined None*† -> x +//│ refined Some*◊ -> x +//│ (None | Some[anything]) -> nothing +//│ res +//│ = [Function: res] From 6040c9e892aa4d7b9f9d12b29f1a14c9e7f6e23e Mon Sep 17 00:00:00 2001 From: Lionel Parreaux Date: Mon, 19 Feb 2024 22:35:03 +0800 Subject: [PATCH 108/143] Add interesting UnboxedOptions test --- shared/src/test/diff/ex/UnboxedOptions.mls | 302 +++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 shared/src/test/diff/ex/UnboxedOptions.mls diff --git a/shared/src/test/diff/ex/UnboxedOptions.mls b/shared/src/test/diff/ex/UnboxedOptions.mls new file mode 100644 index 00000000..3c809aed --- /dev/null +++ b/shared/src/test/diff/ex/UnboxedOptions.mls @@ -0,0 +1,302 @@ +:NewDefs + + +// * Similar in spirit to https://github.com/sjrd/scala-unboxed-option + +// * In these tests, we need to use an `Object` bound to allow pattern-matching on unboxed option values. +// * This bound is needed by the type system in order to preserve parametricity in the general case. +// * However, a real implementation could keep the definition of Opt abstract and do away with the `Object` bound +// * through unchecked casts without compromising safety or parametricity. + + +module None +class Some[out A](val value: A) +//│ module None +//│ class Some[A](value: A) + +type Opt[out A] = Some[A] | None | A & ~Some & ~None +//│ type Opt[A] = None | Some[A] | A & ~None & ~#Some + + +fun some(x) = if x is + refined(None) then Some(x) + refined(Some) then Some(x) + else x +//│ fun some: forall 'a 'b 'c. (None & 'b | Object & 'c & ~#None & ~#Some | Some[anything] & 'a) -> (Some[None & 'b | Some[anything] & 'a] | 'c) + +let some_ = some : forall 'a: (Object & 'a) -> Opt['a] +//│ let some_: forall 'a. (Object & 'a) -> Opt['a] +//│ some_ +//│ = [Function: some] + + +fun fold(opt, k, d) = if opt is + refined(None) then d + refined(Some) then k(opt.value) + else k(opt) +//│ fun fold: forall 'value 'a. (None | Object & 'value & ~#None & ~#Some | Some[anything] & {value: 'value}, 'value -> 'a, 'a) -> 'a + +let fold_ = fold : forall 'a, 'b: (Opt['a] & Object, 'a -> 'b, 'b) -> 'b +//│ let fold_: forall 'a 'b. (Opt['a] & Object, 'a -> 'b, 'b) -> 'b +//│ fold_ +//│ = [Function: fold] + +let fold_ = fold : forall 'a, 'b: (Opt['a & Object], 'a -> 'b, 'b) -> 'b +//│ let fold_: forall 'a 'b. (Opt[Object & 'a], 'a -> 'b, 'b) -> 'b +//│ fold_ +//│ = [Function: fold] + + +// * Examples using the annotated definitions, which is what things will look like from the outside world. + +let s = some_(42) +//│ let s: Opt[42] +//│ s +//│ = 42 + +s : Opt[Int] +//│ Opt[Int] +//│ res +//│ = 42 + +fold_(s, id, 0) +//│ 0 | 42 +//│ res +//│ = 42 + +// * This wont work when `Opt` is made opaque to the outside: +s : Opt[Opt[Int]] +//│ Opt[Opt[Int]] +//│ res +//│ = 42 + +let ss = some_(some_(42)) +//│ let ss: Opt[Opt[42]] +//│ ss +//│ = 42 + +:e +ss : Opt[Int] +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.79: ss : Opt[Int] +//│ ║ ^^ +//│ ╟── type `Some['a]` is not an instance of type `Int` +//│ ║ l.17: type Opt[out A] = Some[A] | None | A & ~Some & ~None +//│ ║ ^^^^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.79: ss : Opt[Int] +//│ ╙── ^^^ +//│ Opt[Int] +//│ res +//│ = 42 + +ss : Opt[Opt[Int]] +//│ Opt[Opt[Int]] +//│ res +//│ = 42 + +fold_(ss, o => fold_(o, id, -1), 0) +//│ -1 | 0 | 42 +//│ res +//│ = 42 + + +let s = some_(None) +//│ let s: Opt[None] +//│ s +//│ = Some {} + +:e +s : Opt[Int] +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.110: s : Opt[Int] +//│ ║ ^ +//│ ╟── reference of type `None` is not an instance of `Int` +//│ ║ l.104: let s = some_(None) +//│ ║ ^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.110: s : Opt[Int] +//│ ╙── ^^^ +//│ Opt[Int] +//│ res +//│ = Some {} + +s : Opt[Opt[Int]] +//│ Opt[Opt[Int]] +//│ res +//│ = Some {} + +fold_(s, o => fold_(o, id, -1), 0) +//│ -1 | 0 +//│ res +//│ = -1 + +some_(some_(None)) +//│ Opt[Opt[None]] +//│ res +//│ = Some {} + +:e +ss : Opt[Int] +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.140: ss : Opt[Int] +//│ ║ ^^ +//│ ╟── type `Some['a]` is not an instance of type `Int` +//│ ║ l.17: type Opt[out A] = Some[A] | None | A & ~Some & ~None +//│ ║ ^^^^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.140: ss : Opt[Int] +//│ ╙── ^^^ +//│ Opt[Int] +//│ res +//│ = 42 + +ss : Opt[Opt[Int]] +//│ Opt[Opt[Int]] +//│ res +//│ = 42 + +fold_(ss, o => fold_(o, id, -1), 0) +//│ -1 | 0 | 42 +//│ res +//│ = 42 + + +// * Here we use the precise, unannotated types directly to showcase precise inferred types. + +let s = some(42) +//│ let s: 42 | Some[nothing] +//│ s +//│ = 42 + +s : Opt[Int] +//│ Opt[Int] +//│ res +//│ = 42 + +fold(s, id, 0) +//│ 0 | 42 +//│ res +//│ = 42 + +s : Opt[Opt[Int]] +//│ Opt[Opt[Int]] +//│ res +//│ = 42 + +let ss = some(some(42)) +//│ let ss: 42 | Some[Some[nothing]] +//│ ss +//│ = 42 + +:e +ss : Opt[Int] +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.193: ss : Opt[Int] +//│ ║ ^^ +//│ ╟── application of type `Some[?A]` does not match type `Int | ~Some[anything]` +//│ ║ l.23: refined(Some) then Some(x) +//│ ╙── ^^^^^^^ +//│ Opt[Int] +//│ res +//│ = 42 + +ss : Opt[Opt[Int]] +//│ Opt[Opt[Int]] +//│ res +//│ = 42 + +fold(ss, o => fold(o, id, -1), 0) +//│ -1 | 0 | 42 +//│ res +//│ = 42 + + +let s = some(None) +//│ let s: Some[None] +//│ s +//│ = Some {} + +:e +s : Opt[Int] +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.221: s : Opt[Int] +//│ ║ ^ +//│ ╟── reference of type `None` does not match type `Int | ~None` +//│ ║ l.215: let s = some(None) +//│ ╙── ^^^^ +//│ Opt[Int] +//│ res +//│ = Some {} + +s : Opt[Opt[Int]] +//│ Opt[Opt[Int]] +//│ res +//│ = Some {} + +fold(s, o => fold(o, id, -1), 0) +//│ -1 | 0 +//│ res +//│ = -1 + +some(some(None)) +//│ Some[Some[None]] +//│ res +//│ = Some {} + +:e +ss : Opt[Int] +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.248: ss : Opt[Int] +//│ ║ ^^ +//│ ╟── application of type `Some[?A]` does not match type `Int | ~Some[anything]` +//│ ║ l.23: refined(Some) then Some(x) +//│ ╙── ^^^^^^^ +//│ Opt[Int] +//│ res +//│ = 42 + +ss : Opt[Opt[Int]] +//│ Opt[Opt[Int]] +//│ res +//│ = 42 + +fold(ss, o => fold(o, id, -1), 0) +//│ -1 | 0 | 42 +//│ res +//│ = 42 + + +// * Demonstration that we can't lift a parametric value into an unboxed option without casts. + +fun mk(x: Object) = some(x) +//│ fun mk: forall 'a. (x: Object) -> (Object & ~#None & ~#Some | Some[None | Some[anything]] | 'a) + +:e +fun mk[A](x: A) = some(x) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.276: fun mk[A](x: A) = some(x) +//│ ║ ^^^^^^^ +//│ ╟── reference of type `A` is not an instance of type `Object` +//│ ║ l.276: fun mk[A](x: A) = some(x) +//│ ║ ^ +//│ ╟── Note: constraint arises from `case` expression: +//│ ║ l.21: fun some(x) = if x is +//│ ║ ^^^^ +//│ ║ l.22: refined(None) then Some(x) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.23: refined(Some) then Some(x) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.24: else x +//│ ║ ^^^^^^^^ +//│ ╟── from reference: +//│ ║ l.21: fun some(x) = if x is +//│ ║ ^ +//│ ╟── Note: method type parameter A is defined at: +//│ ║ l.276: fun mk[A](x: A) = some(x) +//│ ╙── ^ +//│ fun mk: forall 'A 'a 'b. (x: 'A) -> (Some[Some[anything] & 'a | 'A & (None | None & ~'a & ~'b | Some[anything])] | error | 'A & ~#None & ~#Some | 'b) + +fun mk[A](x: A & Object) = some(x) +//│ fun mk: forall 'A 'a 'b. (x: Object & 'A) -> (Object & 'A & ~#None & ~#Some | Some[Some[anything] & 'a | 'A & (None | None & ~'a & ~'b | Some[anything])] | 'b) + From d4ae8e212350103b844fa2b521c3424b54d5b850 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Tue, 19 Mar 2024 18:46:10 +0800 Subject: [PATCH 109/143] Improve debug messages --- shared/src/main/scala/mlscript/ucs/display.scala | 8 ++++---- .../main/scala/mlscript/ucs/stages/Normalization.scala | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/display.scala b/shared/src/main/scala/mlscript/ucs/display.scala index 2811baee..dd343984 100644 --- a/shared/src/main/scala/mlscript/ucs/display.scala +++ b/shared/src/main/scala/mlscript/ucs/display.scala @@ -77,8 +77,8 @@ package object display { def showSplit(prefix: Str, s: c.Split)(implicit context: Context): Str = { def split(s: c.Split, isFirst: Bool, isTopLevel: Bool): Lines = s match { - case c.Split.Cons(head, tail) => (branch(head) match { - case (n, line) :: tail => (n, (if (isTopLevel) "" else "") + s"$line") :: tail + case c.Split.Cons(head, tail) => (branch(head, isTopLevel) match { + case (n, line) :: tail => (n, (if (isTopLevel) "" else "and ") + s"$line") :: tail case Nil => Nil }) ::: split(tail, false, isTopLevel) case c.Split.Let(_, nme, rhs, tail) => @@ -87,9 +87,9 @@ package object display { (if (isFirst) (0, s"then ${term.showDbg}") else (0, s"else ${term.showDbg}")) :: Nil case c.Split.Nil => Nil } - def branch(b: c.Branch): Lines = { + def branch(b: c.Branch, isTopLevel: Bool): Lines = { val c.Branch(scrutinee, pattern, continuation) = b - s"${showVar(scrutinee)} is $pattern" #: split(continuation, true, false) + s"${showVar(scrutinee)} is $pattern" #: split(continuation, true, isTopLevel) } val lines = split(s, true, true) (if (prefix.isEmpty) lines else prefix #: lines).toIndentedString diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 62a4036c..efb77b7f 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -215,18 +215,18 @@ trait Normalization { self: Desugarer with Traceable => if (scrutinee === otherScrutinee) { println(s"Case 1: ${scrutineeVar.name} === ${otherScrutineeVar.name}") if (otherPattern =:= pattern) { - println(s"Case 2.1: $pattern =:= $otherPattern") + println(s"Case 1.1: $pattern =:= $otherPattern") otherPattern reportInconsistentRefinedWith pattern specialize(continuation, true) :++ specialize(tail, true) } else if (otherPattern <:< pattern) { - println(s"Case 2.2: $pattern <:< $otherPattern") + println(s"Case 1.2: $pattern <:< $otherPattern") pattern.markAsRefined; split } else { - println(s"Case 2.3: $pattern are unrelated with $otherPattern") + println(s"Case 1.3: $pattern are unrelated with $otherPattern") specialize(tail, true) } } else { - println(s"Case 2: ${scrutineeVar.name} === ${otherScrutineeVar.name}") + println(s"Case 2: ${scrutineeVar.name} =/= ${otherScrutineeVar.name}") head.copy(continuation = specialize(continuation, true)) :: specialize(tail, true) } case (false, split @ Split.Cons(head @ Branch(Scrutinee.WithVar(otherScrutineeVar, otherScrutinee), otherPattern, continuation), tail)) => @@ -241,7 +241,7 @@ trait Normalization { self: Desugarer with Traceable => split.copy(tail = specialize(tail, false)) } } else { - println(s"Case 2: ${scrutineeVar.name} === ${otherScrutineeVar.name}") + println(s"Case 2: ${scrutineeVar.name} =/= ${otherScrutineeVar.name}") head.copy(continuation = specialize(continuation, false)) :: specialize(tail, false) } case (_, split @ Split.Cons(Branch(_, pattern, _), _)) => From 3ff3f0af3b3877cad5604235488b83aec7848462 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Thu, 21 Mar 2024 14:39:00 +0800 Subject: [PATCH 110/143] Fix post-processing Boolean tests --- .../mlscript/ucs/stages/PostProcessing.scala | 19 +++- shared/src/test/diff/ucs/Wildcard.mls | 95 ++++++++++++++++++- 2 files changed, 108 insertions(+), 6 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index 2974e307..6a5e3c25 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -120,7 +120,9 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => case lhs @ Let(_, _, _, body) => lhs.copy(body = mergeTerms(body, rhs)) case lhs @ CaseOf(scrutinee: Var, cases) => lhs.copy(cases = recCaseBranches(cases, rhs)) - case _ => reportUnreachableCase(rhs, lhs) + case _ => + println("unreachable: " + rhs.describe) + reportUnreachableCase(rhs, lhs) } def recCaseBranches(lhs: CaseBranches, rhs: Term): CaseBranches = lhs match { case NoCases => Wildcard(rhs).withLocOf(rhs) @@ -153,6 +155,7 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => ): (Term, Opt[Term]) = { def rec(term: Term): (Term, Opt[Term]) = term match { + // Disentangle pattern matching case top @ CaseOf(Scrutinee.WithVar(otherScrutineeVar, otherScrutinee), cases) => if (scrutinee === otherScrutinee) { println(s"found a `CaseOf` that matches on `${scrutineeVar.name}`") @@ -163,6 +166,20 @@ trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => val (n, y) = disentangleUnmatchedCaseBranches(cases) (top.copy(cases = n), (if (y === NoCases) N else S(top.copy(cases = y)))) } + // Disentangle tests with two case branches + case top @ CaseOf(testVar: Var, Case(Var("true"), whenTrue, Wildcard(whenFalse))) if context.isTestVar(testVar) => + println(s"TEST `${testVar.name}`") + val (n1, y1) = disentangleTerm(whenTrue) + val (n2, y2) = disentangleTerm(whenFalse) + ( + CaseOf(testVar, Case(Var("true"), n1, Wildcard(n2))(false)), + (y1, y2) match { + case (N, N) => N + case (S(t1), N) => S(CaseOf(testVar, Case(Var("true"), t1, Wildcard(n2))(false))) + case (N, S(t2)) => S(CaseOf(testVar, Case(Var("true"), n1, Wildcard(t2))(false))) + case (S(t1), S(t2)) => S(CaseOf(testVar, Case(Var("true"), t1, Wildcard(t2))(false))) + } + ) // For let bindings, we just go deeper. case let @ Let(_, _, _, body) => val (n, y) = rec(body) diff --git a/shared/src/test/diff/ucs/Wildcard.mls b/shared/src/test/diff/ucs/Wildcard.mls index 5dc4fa10..f6946d2e 100644 --- a/shared/src/test/diff/ucs/Wildcard.mls +++ b/shared/src/test/diff/ucs/Wildcard.mls @@ -42,13 +42,98 @@ w1(Right(Some(0)), "a", "b") //│ res //│ = 'Right of Some of 0' +// Should be +// case x of +// Some -> 1 +// None -> +// case p(x) of +// true -> 2 +// _ -> 4 +// _ -> +// case p(x) of +// true -> 3 +// _ -> 5 +:ducs:normalize.result,postprocess.result +fun w2(x, p) = + if x is + Some then 1 + _ and p(x) and + x is None then 2 + _ then 3 + None then 4 + _ then 5 +//│ Normalized UCS term: +//│ case x*‡ of +//│ Some*◊ -> 1 +//│ _ -> +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true*† -> +//│ case x*‡ of +//│ None*† -> 2 +//│ _ -> 3 +//│ _ -> +//│ case x*‡ of +//│ None*† -> 4 +//│ _ -> 5 +//│ Post-processed UCS term: +//│ case x*‡ of +//│ Some*◊ -> 1 +//│ None*† -> +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true -> 2 +//│ _ -> 4 +//│ _ -> +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true -> 3 +//│ _ -> 5 +//│ fun w2: forall 'a. (None | Object & 'a & ~#None & ~#Some | Some[anything], (None | 'a) -> Bool) -> (1 | 2 | 3 | 4 | 5) + +// Should be +// case x of +// Some -> 1 +// None -> +// case p(x) of +// true -> 2 +// _ -> 3 +// _ -> +// case p(x) of +// true -> 2 +// _ -> 4 +:ducs:normalize.result,postprocess.result fun w2(x, p) = if x is Some then 1 _ and p(x) then 2 None then 3 _ then 4 -//│ fun w2: forall 'a. (Object & 'a & ~#Some | Some[anything], 'a -> Bool) -> (1 | 2 | 3 | 4) +//│ Normalized UCS term: +//│ case x*‡ of +//│ Some*◊ -> 1 +//│ _ -> +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true*† -> 2 +//│ _ -> +//│ case x*‡ of +//│ None*† -> 3 +//│ _ -> 4 +//│ Post-processed UCS term: +//│ case x*‡ of +//│ Some*◊ -> 1 +//│ None*† -> +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true -> 2 +//│ _ -> 3 +//│ _ -> +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true -> 2 +//│ _ -> 4 +//│ fun w2: forall 'a. (None | Object & 'a & ~#None & ~#Some | Some[anything], (None | 'a) -> Bool) -> (1 | 2 | 3 | 4) w2(Some(0), x => true) w2(None, x => true) @@ -108,10 +193,10 @@ w3(0, _ => false) fun w3_1(x, f) = if f(x) is _ then 0 else 1 //│ ╔══[WARNING] this case is unreachable -//│ ║ l.109: if f(x) is _ then 0 else 1 +//│ ║ l.194: if f(x) is _ then 0 else 1 //│ ║ ^ //│ ╟── because it is subsumed by the branch -//│ ║ l.109: if f(x) is _ then 0 else 1 +//│ ║ l.194: if f(x) is _ then 0 else 1 //│ ╙── ^ //│ fun w3_1: forall 'a. ('a, 'a -> anything) -> 0 @@ -127,10 +212,10 @@ w3_1(0, _ => false) fun w3_1_1(x, f) = if f(x) is a then a else 0 //│ ╔══[WARNING] this case is unreachable -//│ ║ l.128: if f(x) is a then a else 0 +//│ ║ l.213: if f(x) is a then a else 0 //│ ║ ^ //│ ╟── because it is subsumed by the branch -//│ ║ l.128: if f(x) is a then a else 0 +//│ ║ l.213: if f(x) is a then a else 0 //│ ╙── ^ //│ fun w3_1_1: forall 'a 'b. ('a, 'a -> 'b) -> 'b From 587d3382b9e77aa43dd67b88069587d342f90476 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Thu, 21 Mar 2024 14:50:35 +0800 Subject: [PATCH 111/143] Add cases about redundancy and failing to warn of unreachable code) --- shared/src/test/diff/mlscript/Repro.mls | 66 ++++++++++ .../src/test/diff/pretyper/ucs/DualOption.mls | 117 ++++++++++++++++++ 2 files changed, 183 insertions(+) diff --git a/shared/src/test/diff/mlscript/Repro.mls b/shared/src/test/diff/mlscript/Repro.mls index 34d9bdfe..6a07da5d 100644 --- a/shared/src/test/diff/mlscript/Repro.mls +++ b/shared/src/test/diff/mlscript/Repro.mls @@ -1 +1,67 @@ :NewDefs + +class A() +class B() extends A() +//│ class A() +//│ class B() extends A + +fun p(x) = true +//│ fun p: anything -> true + +fun f(x) = if + x is B and + x is A then 1 + p(x) then 2 + x is A then 31 + x is B then 3 + else 4 +//│ fun f: (B | Object & ~#B) -> (2 | 31 | 3 | 4) + + + +// FIXME should warn about unreachable code (3 disappears) +:ducs:postprocess.result +fun f(x) = if + x is A and + x is B then 1 + p(x) then 2 + x is B then 3 + else 4 +//│ Post-processed UCS term: +//│ case x*‡ of +//│ refined A*◊ -> +//│ case x*‡ of +//│ B*◊ -> 1 +//│ _ -> +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true*† -> 2 +//│ _ -> 4 +//│ _ -> 4 +//│ fun f: Object -> (1 | 2 | 4) + + +class X() +class Y() +//│ class X() +//│ class Y() + +// FIXME should warn about unreachable code (1 disappears) +:ducs:postprocess.result +fun f(x) = if + x is X and + x is Y then 1 + p(x) then 2 + x is Y then 3 + else 4 +//│ Post-processed UCS term: +//│ case x*‡ of +//│ X*◊ -> +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true*† -> 2 +//│ _ -> 4 +//│ Y*◊ -> 3 +//│ _ -> 4 +//│ fun f: Object -> (2 | 3 | 4) + diff --git a/shared/src/test/diff/pretyper/ucs/DualOption.mls b/shared/src/test/diff/pretyper/ucs/DualOption.mls index 52a9ffa3..5c792198 100644 --- a/shared/src/test/diff/pretyper/ucs/DualOption.mls +++ b/shared/src/test/diff/pretyper/ucs/DualOption.mls @@ -162,3 +162,120 @@ add_6(Some(5), Some(9)) //│ = 9 //│ res //│ = 14 + + +fun p(x) = true +//│ fun p: anything -> true + + +// FIXME Remove `case p(x) of true -> 0; _ -> 0` +:ducs:postprocess.result +fun add_6(x, y) = + if + x is Some(xv) and y is Some(yv) then xv + yv + y is None and p(x) and x is Some(xv) then 42 + y is None and x is Some(xv) then xv + x is None and y is Some(yv) then yv + y is None and x is None then 0 +//│ Post-processed UCS term: +//│ case x*‡ of +//│ Some*◊ -> +//│ let ucs$args_x$Some*† = (Some).unapply(x,) +//│ let xv*‡ = (ucs$args_x$Some).0 +//│ case y*‡ of +//│ Some*◊ -> +//│ let ucs$args_y$Some*† = (Some).unapply(y,) +//│ let yv*‡ = (ucs$args_y$Some).0 +//│ +(xv, yv,) +//│ None*† -> +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true*† -> 42 +//│ _ -> xv +//│ None*† -> +//│ case y*‡ of +//│ None*† -> +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true -> 0 +//│ _ -> 0 +//│ Some*◊ -> +//│ let ucs$args_y$Some*† = (Some).unapply(y,) +//│ let yv*‡ = (ucs$args_y$Some).0 +//│ yv +//│ fun add_6: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) + + +:ducs:postprocess.result +fun add_7(x, y) = + if + x is Some(xv) and y is Some(yv) then xv + yv + // y is None and p(x) and x is Some(xv) then 42 + y is None and x is Some(xv) then xv + y is Some(yv) and p(yv) and x is None then 36 + y is Some(yv) and x is None then yv + y is None and x is None then 0 +//│ Post-processed UCS term: +//│ case x*‡ of +//│ Some*◊ -> +//│ let ucs$args_x$Some*† = (Some).unapply(x,) +//│ let xv*‡ = (ucs$args_x$Some).0 +//│ case y*‡ of +//│ Some*◊ -> +//│ let ucs$args_y$Some*† = (Some).unapply(y,) +//│ let yv*‡ = (ucs$args_y$Some).0 +//│ +(xv, yv,) +//│ None*† -> xv +//│ None*† -> +//│ case y*‡ of +//│ None*† -> 0 +//│ Some*◊ -> +//│ let ucs$args_y$Some*† = (Some).unapply(y,) +//│ let yv*‡ = (ucs$args_y$Some).0 +//│ let ucs$test$0*† = p(yv,) : Bool +//│ case ucs$test$0*† of +//│ true -> 36 +//│ _ -> yv +//│ fun add_7: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) + + +// FIXME Remove `case p(x) of true -> 0; _ -> 0` +:ducs:postprocess.result +fun add_8(x, y) = + if + x is Some(xv) and y is Some(yv) then xv + yv + y is None and p(x) and x is Some(xv) then 42 + y is None and x is Some(xv) then xv + y is Some(yv) and p(yv) and x is None then 36 + y is Some(yv) and x is None then yv + y is None and x is None then 0 +//│ Post-processed UCS term: +//│ case x*‡ of +//│ Some*◊ -> +//│ let ucs$args_x$Some*† = (Some).unapply(x,) +//│ let xv*‡ = (ucs$args_x$Some).0 +//│ case y*‡ of +//│ Some*◊ -> +//│ let ucs$args_y$Some*† = (Some).unapply(y,) +//│ let yv*‡ = (ucs$args_y$Some).0 +//│ +(xv, yv,) +//│ None*† -> +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true*† -> 42 +//│ _ -> xv +//│ None*† -> +//│ case y*‡ of +//│ None*† -> +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true -> 0 +//│ _ -> 0 +//│ Some*◊ -> +//│ let ucs$args_y$Some*† = (Some).unapply(y,) +//│ let yv*‡ = (ucs$args_y$Some).0 +//│ let ucs$test$1*† = p(yv,) : Bool +//│ case ucs$test$1*† of +//│ true -> 36 +//│ _ -> yv +//│ fun add_8: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) From d22ba92cecfb0a987bf29bde3c35372aa6c72285 Mon Sep 17 00:00:00 2001 From: Lionel Parreaux Date: Wed, 3 Apr 2024 11:51:13 +0800 Subject: [PATCH 112/143] Fix flattening for multi-arg lambdas and improve some tests --- .../src/main/scala/mlscript/NewParser.scala | 4 + .../src/test/diff/parser/FlatMultiArgLams.mls | 16 ++++ .../test/diff/pretyper/ucs/examples/JSON.mls | 88 ++++++++++--------- .../pretyper/ucs/examples/LispInterpreter.mls | 25 +++--- 4 files changed, 77 insertions(+), 56 deletions(-) create mode 100644 shared/src/test/diff/parser/FlatMultiArgLams.mls diff --git a/shared/src/main/scala/mlscript/NewParser.scala b/shared/src/main/scala/mlscript/NewParser.scala index a2a54e75..23366eea 100644 --- a/shared/src/main/scala/mlscript/NewParser.scala +++ b/shared/src/main/scala/mlscript/NewParser.scala @@ -794,6 +794,10 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], newDefs: Bo Bra(false, elt) case (Round, _) => yeetSpaces match { + case (KEYWORD(opStr @ "=>"), l1) :: (NEWLINE, l2) :: _ /* if opPrec(opStr)._1 > prec */ => + consume + val rhs = Blk(typingUnit.entities) + Lam(Tup(res), rhs) case (KEYWORD("=>"), l1) :: _ => consume val e = expr(NewParser.opPrec("=>")._2) diff --git a/shared/src/test/diff/parser/FlatMultiArgLams.mls b/shared/src/test/diff/parser/FlatMultiArgLams.mls new file mode 100644 index 00000000..5dc0f634 --- /dev/null +++ b/shared/src/test/diff/parser/FlatMultiArgLams.mls @@ -0,0 +1,16 @@ +:NewDefs + + +// Extracted frrom JSON.mls + +parseNegative(state).flatMap of (negative, state) => +parseIntegral(state).flatMap of (integral, state) => +parseFraction(state).flatMap of (fraction, state) => +parseExponent(state).flatMap of (exponent, state) => +let value = (integral +. fraction) *. exponent +Success of (if negative then (0 -. value) else value), state +//│ |parseNegative|(|state|)|.flatMap| |#of| |(|negative|,| |state|)| |#=>|↵|parseIntegral|(|state|)|.flatMap| |#of| |(|integral|,| |state|)| |#=>|↵|parseFraction|(|state|)|.flatMap| |#of| |(|fraction|,| |state|)| |#=>|↵|parseExponent|(|state|)|.flatMap| |#of| |(|exponent|,| |state|)| |#=>|↵|#let| |value| |#=| |(|integral| |+.| |fraction|)| |*.| |exponent|↵|Success| |#of| |(|#if| |negative| |#then| |(|0| |-.| |value|)| |#else| |value|)|,| |state| +//│ Parsed: {(parseNegative(state,)).flatMap((negative, state,) => {(parseIntegral(state,)).flatMap((integral, state,) => {(parseFraction(state,)).flatMap((fraction, state,) => {(parseExponent(state,)).flatMap((exponent, state,) => {let value = *.('(' +.(integral, fraction,) ')', exponent,); Success('(' if (negative) then '(' -.(0, value,) ')' else value ')', state,)},)},)},)},)} +//│ + + diff --git a/shared/src/test/diff/pretyper/ucs/examples/JSON.mls b/shared/src/test/diff/pretyper/ucs/examples/JSON.mls index 6d5cc743..f37bdf03 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/JSON.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/JSON.mls @@ -53,20 +53,6 @@ fun (*>) strgt(a: NStr, b: NStr) = a.localeCompare(b) > 0 declare fun Math: { log10: Num -> Num, floor: Num -> Num, ceil: Num -> Num } //│ fun Math: {ceil: Num -> Num, floor: Num -> Num, log10: Num -> Num} -// Steal operators from OCaml: -let (+.) numAdd' = numAdd -let (-.) numSub' = numSub -let (*.) numMul' = numMul -//│ let (+.) numAdd': (Num, Num) -> Num -//│ let (-.) numSub': (Num, Num) -> Num -//│ let (*.) numMul': (Num, Num) -> Num -//│ numAdd' -//│ = [Function: numAdd] -//│ numSub' -//│ = [Function: numSub] -//│ numMul' -//│ = [Function: numMul] - fun (!==) notEqual(x, y) = not(x === y) declare fun parseInt: (Str, Int) -> Int //│ fun (!==) notEqual: forall 'a. (Eql['a], 'a) -> Bool @@ -116,15 +102,18 @@ fun (->) makePair(a, b) = [a, b] abstract class ListMap[K, out V]: (ConsMap[K, V] | NilMap) class ConsMap[K, out V](head: [K, V], tail: ListMap[K, V]) extends ListMap[K, V] module NilMap extends ListMap + fun containsKey(map: ListMap['K, 'V], key: 'K): Bool = if map is ConsMap([k, _], _) and k === key then true ConsMap(_, tail) then containsKey(tail, key) NilMap then false + fun (:+) insert(map, entry) = if map is ConsMap(entry', map) and entry'.0 === entry.0 then ConsMap(entry, map) else ConsMap(entry', insert(map, entry)) NilMap then ConsMap(entry, NilMap) + fun showMap(map) = let showEntry([k, v]) = toString(k) ++ " -> " ++ toString(v) let rec aux(map) = if map is @@ -192,6 +181,7 @@ fun success: forall 't: ('t, ParserState) -> ParseResult['t] fun success = (value, state) => Success(value, state) fun failure: forall 't: Str -> ParseResult[nothing] fun failure = error => Failure(error) + abstract class ParseResult[out T]: (Success[T] | Failure) { virtual fun flatMap(f: (T, ParserState) -> ParseResult['U]): ParseResult['U] virtual fun map(f: T -> 'U): ParseResult['U] @@ -256,6 +246,7 @@ fun parseNumber(state: ParserState): ParseResult[Num] = let parseNegative(state): ParseResult[Bool] = if state.peek is Some("-") then Success(true, state.next) else Success(false, state) + // Parse one or more decimal digits // -------------------------------- let parseDigits(state): ParseResult[Num] = @@ -268,11 +259,13 @@ fun parseNumber(state: ParserState): ParseResult[Num] = Some([digit, state']) and aux(digit, state') is [num, state''] then Success(num, state'') None then Failure("expected one or more decimal digits") + // Parse the integral part of the number // ------------------------------------- let parseIntegral(state): ParseResult[Num] = if state.nextDigit is Some([0, state']) then Success(0, state') else parseDigits(state) + // Parse the fractional part of the number // --------------------------------------- let parseFraction(state): ParseResult[Num] = if state.peek is @@ -283,18 +276,18 @@ fun parseNumber(state: ParserState): ParseResult[Num] = Some("-") then Success(true, state.next) Some("+") then Success(false, state.next) else Success(false, state) - if state.peek is Some(e) and (e === "e") || (e === "E") then - parseSign(state.next).flatMap of (sign, state) => - parseDigits(state).map of exponent => - if sign then 10 ** (0 -. exponent) else 10 ** exponent - else - Success(1, state) + if state.peek is Some(e) and (e === "e") || (e === "E") + then parseSign(state.next).flatMap of (sign, state) => + parseDigits(state).map of exponent => + if sign then 10 ** (0 -. exponent) else 10 ** exponent + else Success(1, state) + parseNegative(state).flatMap of (negative, state) => - parseIntegral(state).flatMap of (integral, state) => - parseFraction(state).flatMap of (fraction, state) => - parseExponent(state).flatMap of (exponent, state) => - let value = (integral +. fraction) *. exponent - Success of (if negative then (0 -. value) else value), state + parseIntegral(state).flatMap of (integral, state) => + parseFraction(state).flatMap of (fraction, state) => + parseExponent(state).flatMap of (exponent, state) => + let value = (integral +. fraction) *. exponent + Success of (if negative then (0 -. value) else value), state //│ fun parseNumber: (state: ParserState) -> ParseResult[Num] showParseResult of parseNumber of ParserState of String("0"), 0 @@ -324,6 +317,7 @@ showParseResult of parseNumber of ParserState of String("1E+1"), 0 //│ = 'Success after 4: 10' fun parseString(state: ParserState): ParseResult[Str] = + let rec parseCodePoint(n, acc, state) = if n === 0 then Success(acc, state) state.peekCode is Some(code) and @@ -332,6 +326,7 @@ fun parseString(state: ParserState): ParseResult[Str] = 97 <= code and code <= 102 then parseCodePoint(n - 1, acc * 16 + code - 87, state.next) else Failure("expect " ++ toString(n) ++ " hex digit(s) instead of '" ++ String.fromCodePoint(code) ++ "'") else Failure("expect " ++ toString(n) ++ " hex digit(s) instead of end of input") + let rec parseContent(acc, state) = if state.peek is Some("\"") then Success(acc, state.next) Some("\\") and @@ -347,12 +342,13 @@ fun parseString(state: ParserState): ParseResult[Str] = Some("t") then parseContent(acc ++ "\t", state'.next) Some("u") then parseCodePoint(4, 0, state'.next).flatMap of (codePoint, state) => - if codePoint < 0xD800 || 0xDFFF < codePoint then - parseContent(acc ++ String.fromCodePoint(codePoint), state) - else Failure("invalid code point") + if codePoint < 0xD800 || 0xDFFF < codePoint + then parseContent(acc ++ String.fromCodePoint(codePoint), state) + else Failure("invalid code point") else Failure("invalid escape sequence") Some(ch) then parseContent(acc ++ ch, state.next) None then Failure("expected '\"' instead of end of input") + if state.peek is Some("\"") then parseContent("", state.next) Some(ch) then Failure("expected '\"' instead of '" ++ ch ++ "'") @@ -395,10 +391,12 @@ fun parseTrue(state: ParserState): ParseResult[Bool] = if state.match("true") is Some(state) then Success(true, state) None then Failure("expected 'true'") + fun parseFalse(state: ParserState): ParseResult[Bool] = if state.match("false") is Some(state) then Success(false, state) None then Failure("expected 'false'") + fun parseNull(state: ParserState): ParseResult[()] = if state.match("null") is Some(state) then Success((), state) @@ -410,24 +408,25 @@ fun parseNull(state: ParserState): ParseResult[()] = fun parseObjectEntry(state: ParserState): ParseResult[[Str, JsonValue]] = let state' = skipWhiteSpace(state) parseString(state').flatMap of (key, state) => - let state' = skipWhiteSpace(state) - if state'.peek is - Some(":") then - parseValue(state'.next).flatMap of (value, state') => - Success([key, value], state') - Some(ch) then Failure("expected ':' instead of '" ++ ch ++ "'") - None then Failure("expected ':' instead of end of input") - else Failure("expected ':' instead of end of input") + let state' = skipWhiteSpace(state) + if state'.peek is + Some(":") then + parseValue(state'.next).flatMap of (value, state') => + Success([key, value], state') + Some(ch) then Failure("expected ':' instead of '" ++ ch ++ "'") + None then Failure("expected ':' instead of end of input") + else Failure("expected ':' instead of end of input") + fun parseObject(state: ParserState): ParseResult[ListMap[Str, JsonValue]] = let rec parseObjectTail(acc: ListMap[Str, JsonValue], state: ParserState) = let state' = skipWhiteSpace(state) if state'.peek is Some(",") then parseObjectEntry(state'.next).flatMap of (entry, state') => - if containsKey(acc, entry.0) then - Failure("duplicate key '" ++ toString(entry.0) ++ "'") - else - parseObjectTail(ConsMap(entry, acc), state') + if containsKey(acc, entry.0) then + Failure("duplicate key '" ++ toString(entry.0) ++ "'") + else + parseObjectTail(ConsMap(entry, acc), state') Some("}") then Success(acc, state'.next) Some(ch) then Failure("expected ',' or ']' instead of " ++ ch) None then Failure("expected ',' or ']' instead of end of input") @@ -437,14 +436,15 @@ fun parseObject(state: ParserState): ParseResult[ListMap[Str, JsonValue]] = None then Failure("expected ',' or ']' instead of end of input") else parseObjectEntry(state').flatMap of (head, state) => - parseObjectTail(ConsMap(head, NilMap), state) + parseObjectTail(ConsMap(head, NilMap), state) + fun parseArray(state: ParserState): ParseResult[List[JsonValue]] = let rec parseArrayTail(acc, state) = let state' = skipWhiteSpace(state) if state'.peek is Some(",") then parseValue(state'.next).flatMap of (value, state') => - parseArrayTail(value :: acc, state') + parseArrayTail(value :: acc, state') Some("]") then Success(reverse(acc), state'.next) Some(ch) then Failure("expected ',' or ']' instead of " ++ ch) None then Failure("expected ',' or ']' instead of end of input") @@ -454,7 +454,8 @@ fun parseArray(state: ParserState): ParseResult[List[JsonValue]] = None then Failure("expected ',' or ']' instead of end of input") else parseValue(state').flatMap of (head, state) => - parseArrayTail(head :: Nil, state) + parseArrayTail(head :: Nil, state) + fun parseValue(state: ParserState): ParseResult[JsonValue] = let state' = skipWhiteSpace(state) if state'.peek is @@ -490,6 +491,7 @@ fun stringify(value: JsonValue): Str = ConsMap(head, tail) then showEntry(head) ++ ", " ++ aux(tail) NilMap then "" if map is NilMap then String("{}") else "{ " ++ aux(map) ++ " }" + if value is JsonNumber(n) then toString(n) JsonString(s) then "\"" ++ s ++ "\"" diff --git a/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls b/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls index 9553e0c3..241a8dd2 100644 --- a/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls +++ b/shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls @@ -181,10 +181,9 @@ fun skipBlank(s: NStr, i: Int): Int = fun scanWhile(s: NStr, i: Int, p: Str -> Bool): Option[[Str, Int]] = let rec aux(acc, i) = - if i < s.length and s.charAt(i) is ch and p of ch then - aux(acc ++ ch, i + 1) - else - [acc, i] + if i < s.length and s.charAt(i) is ch and p of ch + then aux(acc ++ ch, i + 1) + else [acc, i] if aux("", i) is ["", _] then None [acc, i] then Some([acc, i]) @@ -238,10 +237,9 @@ fun isDigit(n) = 48 <= n and n <= 57 fun isDigits(s: Str): Bool = let s' = String(s) let rec aux(i) = - if i < s'.length and isDigit of s'.charCodeAt(i) then - aux(i + 1) - else - i === s'.length + if i < s'.length and isDigit of s'.charCodeAt(i) + then aux(i + 1) + else i === s'.length aux(0) isDigits("123") isDigits("123jump") @@ -275,6 +273,7 @@ fun parseExpr(tokens: List[Str]): [ParseResult[Data], List[Str]] = isDigits(token) then [(Success of Literal of IntLit of parseInt(token, 10)), tail] else [(Success of Symbol of token), tail] Nil then [Failure("Unexpected end of input, expect either `)` or more tokens."), Nil] + fun parseList(tokens: List[Str]): [ParseResult[DataList], List[Str]] = let rec collect(acc, ts) = if ts is Cons(")", tail) then [(Success of DataList of reverse of acc), tail] @@ -442,12 +441,12 @@ fun builtinNull(args: List[Data]): Data = if args is //│ builtinNil //│ = DataList {} -let globalEnv = emptyEnv ++: ( +let globalEnv = emptyEnv ++: ["eq", Literal(Lambda(builtinEq))] :: ["+", Literal(Lambda(builtinAdd))] :: - ["-", Literal(Lambda(builtinSub))] :: ["*", Literal(Lambda(builtinMul))] :: - ["nil", builtinNil] :: ["cons", Literal(Lambda(builtinCons))] :: - ["car", Literal(Lambda(builtinCar))] :: ["cdr", Literal(Lambda(builtinCdr))] :: - ["null", Literal(Lambda(builtinNull))] :: Nil) + ["-", Literal(Lambda(builtinSub))] :: ["*", Literal(Lambda(builtinMul))] :: + ["nil", builtinNil] :: ["cons", Literal(Lambda(builtinCons))] :: + ["car", Literal(Lambda(builtinCar))] :: ["cdr", Literal(Lambda(builtinCdr))] :: + ["null", Literal(Lambda(builtinNull))] :: Nil //│ let globalEnv: (name': Str) -> (DataList | Literal) //│ globalEnv //│ = [Function (anonymous)] From fbfd775f721a62c84470b30d7b5b607d6c5252ce Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 24 Apr 2024 16:55:29 +0800 Subject: [PATCH 113/143] Remove class seal validation --- .../scala/mlscript/pretyper/PreTyper.scala | 39 ------------------- .../main/scala/mlscript/pretyper/Symbol.scala | 1 - .../ucs/stages/CoverageChecking.scala | 4 -- 3 files changed, 44 deletions(-) diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index a89271d7..4ac95772 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -218,20 +218,6 @@ class PreTyper extends Traceable with Diagnosable with Desugarer { self.baseTypes = bases println(s"base types of `${self.name}`: ${bases.iterator.map(_.name).mkString(", ")}") } - // Pass 1.2: Resolve signature types for collecting sealed derived types. - println("Resolve sealed signature types") - typeSymbols.foreach { - case _: MixinSymbol | _: TypeAliasSymbol | _: ModuleSymbol => () - case symbol => symbol.defn.sig.foreach { unions => - val derivedTypes = try extractSignatureTypes(unions) catch { case _: NotImplementedError => Nil } - symbol.sealedDerivedTypes = derivedTypes.flatMap { derivedType => - val maybeSymbol = scopeWithTypes.getTypeSymbol(derivedType.name) - if (maybeSymbol.isEmpty) raiseError(msg"Undefined type $derivedType" -> derivedType.toLoc) - maybeSymbol - } - println(s">>> $name: ${symbol.sealedDerivedTypes.iterator.map(_.name).mkString(", ")}") - } - } // Pass 2: Build a complete scope and collect definitional terms and terms to be traversed. val (completeScope, thingsToTraverse) = statements.foldLeft[(Scope, Ls[(Term \/ DefinedTermSymbol, Scope)])](scopeWithTypes, Nil) { case ((scope, acc), term: Term) => (scope, (L(term), scope) :: acc) @@ -278,31 +264,6 @@ class PreTyper extends Traceable with Diagnosable with Desugarer { trace(s"PreTyper <== $name: ${typingUnit.describe}") { traverseStatements(typingUnit.entities, name, scope) }({ scope => s"PreTyper ==> ${scope.showLocalSymbols}" }) - - /** - * Extract types in class signatures. For example, for this piece of code - * ```mls - * abstract class Option[A]: Some[A] | None - * ``` - * this function returns, `Some` and `None`. - * - * @param ty a type obtained from `NuTypeDef.sig` - * @return a list of type names, without any p - */ - private def extractSignatureTypes(ty: Type): Ls[TypeName] = { - @tailrec - def rec(acc: Ls[TypeName], ty: Type): Ls[TypeName] = ty match { - case tn: TypeName => tn :: acc - case AppliedType(tn: TypeName, _) => tn :: acc - case Union(lhs, tn: TypeName) => rec(tn :: acc, lhs) - case Union(lhs, AppliedType(tn: TypeName, _)) => rec(tn :: acc, lhs) - case other => - // Let's not raise warning for now. - // raiseWarning(msg"unknown type in signature" -> other.toLoc) - Nil - } - rec(Nil, ty).reverse - } def extractSuperTypes(parents: Ls[Term]): Ls[Var] = { @tailrec diff --git a/shared/src/main/scala/mlscript/pretyper/Symbol.scala b/shared/src/main/scala/mlscript/pretyper/Symbol.scala index 3e3785ff..afbeaf86 100644 --- a/shared/src/main/scala/mlscript/pretyper/Symbol.scala +++ b/shared/src/main/scala/mlscript/pretyper/Symbol.scala @@ -26,7 +26,6 @@ package object symbol { override def name: Str = defn.name var baseTypes: Ls[TypeSymbol] = Nil - var sealedDerivedTypes: Ls[TypeSymbol] = Nil @inline def hasBaseClass(baseClassLikeSymbol: TypeSymbol): Bool = baseTypes.exists(_ === baseClassLikeSymbol) diff --git a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala index 2639c99a..ed8f9f8c 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala @@ -213,10 +213,6 @@ object CoverageChecking { * set `{ Z }`. Set `{ B, C }` represents that the scrutinee can be further * refined to class `B` or `class C`. Set `{ Z }` represents that if the * scrutinee is not `A`, then it can be `Z`. - * - * If `A` is sealed to `B`, `C`, and `D`, then we get `{ B, C, D }` and - * `{ Z }`. Because if the scrutinee is assumed to be `A`, then it can also - * be `D` other than `B`, `C`. * * @param classLikeSymbol the type symbol represents the class like type * @return If the pattern set doesn't include the given type symbol, this From 8ca2b3c94dc8faa4d2404a9f0a24762eaf2f3536 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 24 Apr 2024 17:11:13 +0800 Subject: [PATCH 114/143] Remove a piece of code introduced in merging --- shared/src/main/scala/mlscript/Typer.scala | 6 ------ 1 file changed, 6 deletions(-) diff --git a/shared/src/main/scala/mlscript/Typer.scala b/shared/src/main/scala/mlscript/Typer.scala index 9d271dc9..b487476c 100644 --- a/shared/src/main/scala/mlscript/Typer.scala +++ b/shared/src/main/scala/mlscript/Typer.scala @@ -1684,12 +1684,6 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne err("type identifier not found: " + nme, pat.toLoc)(raise) bail() } - case Some(td) => - td.kind match { - case Als | Mod | Mxn => val t = err(msg"can only match on classes and traits", pat.toLoc)(raise); t -> t - case Cls => val t = clsNameToNomTag(td)(tp(pat.toLoc, "class pattern"), ctx); t -> t - case Trt => val t = trtNameToNomTag(td)(tp(pat.toLoc, "trait pattern"), ctx); t -> t - } } } val newCtx = if (ctx.inQuote) ctx.enterQuotedScope else ctx.nest From 107e054b1930142193ec4db934223bf37e036e32 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Thu, 25 Apr 2024 10:02:30 +0800 Subject: [PATCH 115/143] Fix old code introduced in merging --- js/src/main/scala/Main.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/main/scala/Main.scala b/js/src/main/scala/Main.scala index f8a7f9b1..e6c59c21 100644 --- a/js/src/main/scala/Main.scala +++ b/js/src/main/scala/Main.scala @@ -159,7 +159,7 @@ object Main { |""".stripMargin val backend = new JSWebBackend() - val (lines, resNames) = backend(pgrm, newDefs = true) + val (lines, resNames) = backend(pgrm) val code = lines.mkString("\n") // TODO: add a toggle button to show js code From a11e61cad5548d41b5602c34dda21222ca0db423 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Thu, 25 Apr 2024 10:41:15 +0800 Subject: [PATCH 116/143] Record symbol bugs in compiler tests --- .../test/diff/Defunctionalize/Lambdas.mls | 2 +- .../test/diff/Defunctionalize/Modules.mls | 6 ++-- .../test/diff/Defunctionalize/OldMonoList.mls | 24 +++++++------- .../test/diff/Defunctionalize/SimpleFunc.mls | 8 ++--- shared/src/main/scala/mlscript/helpers.scala | 17 +--------- .../main/scala/mlscript/pretyper/Symbol.scala | 18 +++++++++++ shared/src/test/diff/pretyper/ucs/Symbol.mls | 32 +++++++++++++++++++ 7 files changed, 71 insertions(+), 36 deletions(-) create mode 100644 shared/src/test/diff/pretyper/ucs/Symbol.mls diff --git a/compiler/shared/test/diff/Defunctionalize/Lambdas.mls b/compiler/shared/test/diff/Defunctionalize/Lambdas.mls index d0539087..50471c0c 100644 --- a/compiler/shared/test/diff/Defunctionalize/Lambdas.mls +++ b/compiler/shared/test/diff/Defunctionalize/Lambdas.mls @@ -41,7 +41,7 @@ //│ Code(List(main$$1())) //│ } //│ class Lambda1$1$1() -//│ fun apply$Lambda1$1$1: (anything, Object) -> Bool +//│ fun apply$Lambda1$1$1: (anything, Bool) -> Bool //│ fun main$$1: () -> Bool //│ Bool //│ res diff --git a/compiler/shared/test/diff/Defunctionalize/Modules.mls b/compiler/shared/test/diff/Defunctionalize/Modules.mls index a5d1c673..48f5a754 100644 --- a/compiler/shared/test/diff/Defunctionalize/Modules.mls +++ b/compiler/shared/test/diff/Defunctionalize/Modules.mls @@ -20,8 +20,10 @@ x.y.f //│ fun main$$2 = () => let obj = let obj = x$2 in if obj is ‹(x$2) then y$x$2(obj,); else error› in if obj is ‹(Foo$1) then f$Foo$1(obj,); else error› //│ Code(List(main$$2())) //│ } -//│ ╔══[WARNING] Found a redundant else branch -//│ ╙── +//│ ╔══[WARNING] the outer binding `x$2` +//│ ╙── is shadowed by name pattern `x$2` +//│ ╔══[WARNING] this case is unreachable +//│ ╙── because it is subsumed by the branch //│ module x$2 //│ class Foo$1() //│ let y$x$2: anything -> Foo$1 diff --git a/compiler/shared/test/diff/Defunctionalize/OldMonoList.mls b/compiler/shared/test/diff/Defunctionalize/OldMonoList.mls index 532fede8..856ea86a 100644 --- a/compiler/shared/test/diff/Defunctionalize/OldMonoList.mls +++ b/compiler/shared/test/diff/Defunctionalize/OldMonoList.mls @@ -42,19 +42,17 @@ fun add2(x) = x+2 //│ fun apply$Lambda1$2$3 = (this, x,) => +(x, 1,) //│ Code(List(main$$5())) //│ } -//│ class Lambda1$3$4() -//│ class Nil$2() -//│ class List$1(e: Int, tail: List$1 | Nil$2) -//│ class Lambda1$2$3() -//│ fun map$List$1: (Object, Object) -> List$1 -//│ fun add2$1: Int -> Int -//│ fun main$$5: () -> List$1 -//│ fun apply$Lambda1$3$4: (anything, Int) -> Int -//│ fun map$Nil$2: forall 'a. ('a & (List$1 | Nil$2), anything) -> (Nil$2 | 'a) -//│ fun apply$Lambda1$2$3: (anything, Int) -> Int -//│ List$1 -//│ res -//│ = List$1 {} +//│ /!!!\ Uncaught error: java.lang.Exception: Internal Error: Symbol already set for obj +//│ at: mlscript.utils.package$.lastWords(package.scala:227) +//│ at: mlscript.VarImpl.symbol_$eq(helpers.scala:870) +//│ at: mlscript.VarImpl.symbol_$eq$(helpers.scala:864) +//│ at: mlscript.Var.symbol_$eq(syntax.scala:67) +//│ at: mlscript.ucs.Desugarer$VarOps.resolveTermSymbol(Desugarer.scala:124) +//│ at: mlscript.ucs.Desugarer$VarOps.withResolvedTermSymbol(Desugarer.scala:129) +//│ at: mlscript.ucs.stages.Desugaring.desugarPatternSplit(Desugaring.scala:432) +//│ at: mlscript.ucs.stages.Desugaring.$anonfun$desugarTermBranch$2(Desugaring.scala:101) +//│ at: mlscript.pretyper.Traceable.trace(Traceable.scala:39) +//│ at: mlscript.pretyper.Traceable.trace$(Traceable.scala:36) :mono class List(e: Int, tail: List | Nil) { diff --git a/compiler/shared/test/diff/Defunctionalize/SimpleFunc.mls b/compiler/shared/test/diff/Defunctionalize/SimpleFunc.mls index e3ec697b..dbf54555 100644 --- a/compiler/shared/test/diff/Defunctionalize/SimpleFunc.mls +++ b/compiler/shared/test/diff/Defunctionalize/SimpleFunc.mls @@ -1,16 +1,16 @@ :NewDefs :mono -fun f(x: Int) = if x then 42 else 1337 +fun f(x: Bool) = if x then 42 else 1337 //│ Lifted: //│ TypingUnit { -//│ fun f$1 = (x: Int,) => if (x) then 42 else 1337 +//│ fun f$1 = (x: Bool,) => if (x) then 42 else 1337 //│ } //│ Mono: //│ TypingUnit { -//│ fun f$1 = (x: Int,) => if (x) then 42 else 1337 +//│ fun f$1 = (x: Bool,) => if (x) then 42 else 1337 //│ } -//│ fun f$1: (x: Int) -> (1337 | 42) +//│ fun f$1: (x: Bool) -> (1337 | 42) :mono fun foo() = 42 diff --git a/shared/src/main/scala/mlscript/helpers.scala b/shared/src/main/scala/mlscript/helpers.scala index cd904098..aa9505c3 100644 --- a/shared/src/main/scala/mlscript/helpers.scala +++ b/shared/src/main/scala/mlscript/helpers.scala @@ -841,7 +841,7 @@ trait LitImpl { self: Lit => } } -trait VarImpl { self: Var => +trait VarImpl extends pretyper.symbol.Symbolic { self: Var => /** Check if the variable name is an integer. */ def isIndex: Bool = name.headOption match { case S('0') => name.length === 1 @@ -854,21 +854,6 @@ trait VarImpl { self: Var => (name.head.isLetter && name.head.isLower || name.head === '_' || name.head === '$') && name =/= "true" && name =/= "false" def toVar: Var = this var uid: Opt[Int] = N - - // PreTyper additions - import pretyper.symbol.Symbol - - private var _symbol: Opt[Symbol] = N - def symbolOption: Opt[Symbol] = _symbol - def symbol: Symbol = _symbol.getOrElse(???) - def symbol_=(symbol: Symbol): Unit = - _symbol match { - case N => _symbol = S(symbol) - case S(`symbol`) => () - case S(_) => ??? - } - // def withSymbol: Var = { symbol = S(new ValueSymbol(this, false)); this } - def withSymbol(symbol: Symbol): Var = { this.symbol = symbol; this } } trait TupImpl { self: Tup => diff --git a/shared/src/main/scala/mlscript/pretyper/Symbol.scala b/shared/src/main/scala/mlscript/pretyper/Symbol.scala index afbeaf86..35c2feb9 100644 --- a/shared/src/main/scala/mlscript/pretyper/Symbol.scala +++ b/shared/src/main/scala/mlscript/pretyper/Symbol.scala @@ -108,4 +108,22 @@ package object symbol { override def nameVar: Var = nme } + + trait Symbolic { + val name: String + + private var _symbol: Opt[Symbol] = N + + def symbolOption: Opt[Symbol] = _symbol + def symbol: Symbol = _symbol.getOrElse(lastWords(s"Symbol not set for $name")) + def symbol_=(symbol: Symbol): Unit = + _symbol match { + case N => _symbol = S(symbol) + case S(`symbol`) => () + case S(current) => + println(s"symbol: old ${current.name} vs new ${symbol.name}") + lastWords(s"Symbol already set for $name") + } + def withSymbol(symbol: Symbol): this.type = { this.symbol = symbol; this } + } } diff --git a/shared/src/test/diff/pretyper/ucs/Symbol.mls b/shared/src/test/diff/pretyper/ucs/Symbol.mls new file mode 100644 index 00000000..bff06e35 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/Symbol.mls @@ -0,0 +1,32 @@ +:NewDefs + +type List[A] = Cons[A] | Nil +class Cons[A](head: A, tail: List[A]) +module Nil +//│ type List[A] = Cons[A] | Nil +//│ class Cons[A](head: A, tail: List[A]) +//│ module Nil + +fun (::) cons(head, tail) = Cons(head, tail) +//│ fun (::) cons: forall 'A. ('A, List['A]) -> Cons['A] + +fun map(f, xs) = if xs is + Cons(head, tail) then f(head) :: map(f, tail) + Nil then Nil +//│ fun map: forall 'A 'A0. ('A -> 'A0, Cons['A] | Nil) -> (Cons['A0] | Nil) + +// fun main$$5 = () => +// let obj = +// let obj = '(' (new List$1)(1, (new List$1)(2, (new Nil$2)(),),) ')' in +// if obj is ‹(List$1) then map$List$1(obj, {Lambda1$2$3()},); else error› +// in +// if obj is ‹(List$1) then map$List$1(obj, {Lambda1$3$4()},); else error› +// Got: Internal Error: Symbol already set for obj +fun main(f) = + let obj = (let obj = Cons(1, Cons(2, Nil)) in (if obj is Cons(head, tail) then f(head) :: tail else Nil)) in + if obj is Cons(head, tail) then f(head) :: tail else Nil +//│ fun main: forall 'A 'A0. ((1 | 2 | 'A) -> 'A) -> (Cons['A0] | Nil) +//│ where +//│ 'A <: 'A0 +//│ 'A0 :> 1 | 2 +//│ <: 'A From 1d4b486521bc74553c611b05aba0a4b93ef9651a Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Thu, 25 Apr 2024 10:44:37 +0800 Subject: [PATCH 117/143] Amend a case which should emit warnings --- .../src/test/diff/pretyper/ucs/Refinement.mls | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/shared/src/test/diff/pretyper/ucs/Refinement.mls b/shared/src/test/diff/pretyper/ucs/Refinement.mls index 95b2b48a..c9283270 100644 --- a/shared/src/test/diff/pretyper/ucs/Refinement.mls +++ b/shared/src/test/diff/pretyper/ucs/Refinement.mls @@ -31,7 +31,32 @@ x => if x is //│ res //│ = [Function: res] -// NOT OK +:w +:ducs:desugar.result,postprocess.result +x => if x is + refined(None) then x + Some then x +//│ Desugared UCS term: +//│ if +//│ x*‡ is refined None then x +//│ x*‡ is Some then x +//│ Post-processed UCS term: +//│ case x*‡ of +//│ refined None*† -> x +//│ Some*◊ -> x +//│ ╔══[WARNING] inconsistent refined pattern +//│ ║ l.37: refined(None) then x +//│ ║ ^^^^ +//│ ╟── pattern `Some` is not refined +//│ ║ l.38: Some then x +//│ ║ ^^^^ +//│ ╟── but pattern `None` is refined +//│ ║ l.37: refined(None) then x +//│ ╙── ^^^^ +//│ (None | Some[anything]) -> Some[nothing] +//│ res +//│ = [Function: res] + :ducs:desugar.result,postprocess.result x => if x is refined(None) then x From b56cacbdc629713469c4139eeb0e25650676fd30 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Sat, 27 Apr 2024 12:51:44 +0800 Subject: [PATCH 118/143] Make base type resolution for class-like symbols lazy --- .../scala/mlscript/pretyper/PreTyper.scala | 85 +++---------------- .../main/scala/mlscript/pretyper/Scope.scala | 4 +- .../main/scala/mlscript/pretyper/Symbol.scala | 73 +++++++++++++--- .../main/scala/mlscript/ucs/Desugarer.scala | 6 +- .../scala/mlscript/ucs/context/Pattern.scala | 12 +-- .../mlscript/ucs/context/Scrutinee.scala | 9 +- .../ucs/stages/CoverageChecking.scala | 6 +- .../mlscript/ucs/stages/Desugaring.scala | 2 +- .../scala/mlscript/ucs/stages/package.scala | 3 +- .../src/main/scala/mlscript/utils/Lazy.scala | 36 ++++++++ shared/src/test/diff/pretyper/Errors.mls | 77 ++++++++--------- .../src/test/scala/mlscript/DiffTests.scala | 1 + 12 files changed, 164 insertions(+), 150 deletions(-) create mode 100644 shared/src/main/scala/mlscript/utils/Lazy.scala diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index 4ac95772..f46752d1 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -4,8 +4,6 @@ import annotation.tailrec, collection.mutable.{Set => MutSet}, collection.immuta import mlscript._, utils._, shorthands._, Diagnostic.PreTyping, Message.MessageContext, symbol._, ucs.Desugarer class PreTyper extends Traceable with Diagnosable with Desugarer { - import PreTyper._ - /** A shorthand function to raise errors without specifying the source. */ protected def raiseError(messages: (Message -> Opt[Loc])*): Unit = raiseError(PreTyping, messages: _*) @@ -117,12 +115,8 @@ class PreTyper extends Traceable with Diagnosable with Desugarer { case Blk(stmts) => traverseStatements(stmts, "block", scope) () - case Subs(arr, idx) => - traverseTerm(arr) - traverseTerm(idx) - case Bind(lhs, rhs) => - traverseTerm(lhs) - traverseTerm(rhs) + case Subs(arr, idx) => traverseTerm(arr); traverseTerm(idx) + case Bind(lhs, rhs) => traverseTerm(lhs); traverseTerm(rhs) case Splc(fields) => fields.foreach { case L(t) => traverseTerm(t) case R(Fld(_, t)) => traverseTerm(t) @@ -190,33 +184,18 @@ class PreTyper extends Traceable with Diagnosable with Desugarer { private def traverseStatements(statements: Ls[Statement], name: Str, parentScope: Scope): Scope = trace(s"traverseStatements <== $name: ${"statement".pluralize(statements.size, true)}") { // Pass 1: Build a scope with type symbols only. - val filterNuTypeDef = { (_: Statement) match { case t: NuTypeDef => S(t); case _ => N } } - val typeSymbols = statements.iterator.flatMap(filterNuTypeDef).map(TypeSymbol(_)).toList + val typeSymbols = statements.collect { case t: NuTypeDef => TypeSymbol(t) } val scopeWithTypes = parentScope.derive ++ typeSymbols println(typeSymbols.iterator.map(_.name).mkString("type symbols: {", ", ", "}")) - // val scopeWithTypes = statements.iterator.flatMap(filterNuTypeDef).foldLeft(parentScope.derive)(_ + TypeSymbol(_)) - // Pass 1.1: Resolve subtyping relations. Build a graph and compute base types of each type. - // Keep a stable ordering of type symbols when printing the graph. - implicit val ord: Ordering[TypeSymbol] = new Ordering[TypeSymbol] { - override def compare(x: TypeSymbol, y: TypeSymbol): Int = - x.name.compareTo(y.name) - } - // Collect inheritance relations, represented by a map from a type symbol - // to its base types. If a type symbol is not found, we will ignore it - // and report the error (but not fatal). - val edges = typeSymbols.foldLeft(SortedMap.empty[TypeSymbol, Ls[TypeSymbol]]) { case (acc, self) => - acc + (self -> extractSuperTypes(self.defn.parents).flatMap { nme => - val maybeSymbol = scopeWithTypes.getTypeSymbol(nme.name) - if (maybeSymbol.isEmpty) { - raiseError(msg"could not find definition `${nme.name}`" -> nme.toLoc) + // Pass 1.1: Resolve parent type symbols. + typeSymbols.foreach { + case s: ClassLikeSymbol => s.parentTypeNames.foreach { nme => + scopeWithTypes.getTypeSymbol(nme.name) match { + case S(symbol) => nme.symbol = symbol + case N => raiseError(msg"could not find definition `${nme.name}`" -> nme.toLoc) } - maybeSymbol - }) - } - printGraph(edges, println(_), "inheritance relations", "->") - transitiveClosure(edges).foreachEntry { (self, bases) => - self.baseTypes = bases - println(s"base types of `${self.name}`: ${bases.iterator.map(_.name).mkString(", ")}") + } + case _ => () } // Pass 2: Build a complete scope and collect definitional terms and terms to be traversed. val (completeScope, thingsToTraverse) = statements.foldLeft[(Scope, Ls[(Term \/ DefinedTermSymbol, Scope)])](scopeWithTypes, Nil) { @@ -264,46 +243,4 @@ class PreTyper extends Traceable with Diagnosable with Desugarer { trace(s"PreTyper <== $name: ${typingUnit.describe}") { traverseStatements(typingUnit.entities, name, scope) }({ scope => s"PreTyper ==> ${scope.showLocalSymbols}" }) - - def extractSuperTypes(parents: Ls[Term]): Ls[Var] = { - @tailrec - def rec(acc: Ls[Var], rest: Ls[Term]): Ls[Var] = - rest match { - case Nil => acc.reverse - case (nme: Var) :: tail => rec(nme :: acc, tail) - case (TyApp(ty, _)) :: tail => rec(acc, ty :: tail) - case (App(term, Tup(_))) :: tail => rec(acc, term :: tail) - case head :: tail => - raiseWarning(msg"unknown type in parent types: ${head.showDbg}" -> head.toLoc) - rec(acc, tail) - } - rec(Nil, parents) - } -} - -object PreTyper { - def transitiveClosure[A](graph: Map[A, List[A]])(implicit ord: Ordering[A]): SortedMap[A, List[A]] = { - def dfs(vertex: A, visited: Set[A]): Set[A] = { - if (visited.contains(vertex)) visited - else graph.getOrElse(vertex, List()) - .foldLeft(visited + vertex)((acc, v) => dfs(v, acc)) - } - graph.keys.map { vertex => - val closure = dfs(vertex, Set()) - vertex -> (closure - vertex).toList - }.toSortedMap - } - - def printGraph(graph: Map[TypeSymbol, List[TypeSymbol]], print: (=> Any) => Unit, title: String, arrow: String): Unit = { - print(s"• $title") - if (graph.isEmpty) - print(" + ") - else - graph.foreachEntry { (source, targets) => - print(s" + ${source.name} $arrow " + { - if (targets.isEmpty) s"{}" - else targets.iterator.map(_.name).mkString("{ ", ", ", " }") - }) - } - } } diff --git a/shared/src/main/scala/mlscript/pretyper/Scope.scala b/shared/src/main/scala/mlscript/pretyper/Scope.scala index b2a50343..1baa04a5 100644 --- a/shared/src/main/scala/mlscript/pretyper/Scope.scala +++ b/shared/src/main/scala/mlscript/pretyper/Scope.scala @@ -96,8 +96,8 @@ object Scope { // def cls(name: Str) = NuTypeDef(Trt, TypeName(name), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, N) def als(name: Str) = NuTypeDef(Als, TypeName(name), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, N, Nil) val builtinTypes = Ls( - new ModuleSymbol(mod("true")), - new ModuleSymbol(mod("false")), + new ModuleSymbol(mod("true"), Nil), + new ModuleSymbol(mod("false"), Nil), new TypeAliasSymbol(als("nothing")), new DummyClassSymbol(Var("Object")), new DummyClassSymbol(Var("Int")), diff --git a/shared/src/main/scala/mlscript/pretyper/Symbol.scala b/shared/src/main/scala/mlscript/pretyper/Symbol.scala index 35c2feb9..451b89a0 100644 --- a/shared/src/main/scala/mlscript/pretyper/Symbol.scala +++ b/shared/src/main/scala/mlscript/pretyper/Symbol.scala @@ -2,9 +2,12 @@ package mlscript.pretyper import collection.mutable.{Buffer, Map => MutMap, Set => MutSet} import mlscript.{Loc, NuFunDef, NuTypeDef, Term, Type, TypeName, Var} +import mlscript.{App, TyApp, Tup} import mlscript.{Cls, Trt, Mxn, Als, Mod} import mlscript.utils._, shorthands._ import mlscript.ucs.context.Matchable +import scala.annotation.tailrec +import scala.collection.immutable.SortedSet package object symbol { sealed trait Symbol { @@ -25,24 +28,33 @@ package object symbol { override def name: Str = defn.name - var baseTypes: Ls[TypeSymbol] = Nil - - @inline def hasBaseClass(baseClassLikeSymbol: TypeSymbol): Bool = - baseTypes.exists(_ === baseClassLikeSymbol) - def showDbg: Str = s"${defn.kind.str} $name" } object TypeSymbol { def apply(defn: NuTypeDef): TypeSymbol = defn.kind match { - case Cls => new ClassSymbol(defn) + case Cls => new ClassSymbol(defn, extractSuperTypes(defn.parents)) case Als => new TypeAliasSymbol(defn) case Mxn => new MixinSymbol(defn) - case Trt => new TraitSymbol(defn) - case Mod => new ModuleSymbol(defn) + case Trt => new TraitSymbol(defn, extractSuperTypes(defn.parents)) + case Mod => new ModuleSymbol(defn, extractSuperTypes(defn.parents)) } def unapply(symbol: TypeSymbol): Opt[NuTypeDef] = S(symbol.defn) + + private def extractSuperTypes(parents: Ls[Term]): Ls[Var] = { + @tailrec + def rec(acc: Ls[Var], rest: Ls[Term]): Ls[Var] = + rest match { + case Nil => acc.reverse + case (nme: Var) :: tail => rec(nme :: acc, tail) + case (TyApp(ty, _)) :: tail => rec(acc, ty :: tail) + case (App(term, Tup(_))) :: tail => rec(acc, term :: tail) + case head :: _ => + lastWords(s"unknown type in parent types: ${head.showDbg}") + } + rec(Nil, parents) + } } /** @@ -51,7 +63,9 @@ package object symbol { * * @param nme the name of the expect type symbol. */ - final class DummyClassSymbol(val nme: Var) extends TypeSymbol { + final class DummyClassSymbol(val nme: Var) extends ClassLikeSymbol { + override val parentTypeNames: Ls[Var] = Nil + override def defn: NuTypeDef = die override def name: Str = nme.name @@ -59,11 +73,39 @@ package object symbol { override def showDbg: Str = s"dummy class $name" } - final class ClassSymbol(override val defn: NuTypeDef) extends TypeSymbol { + trait ClassLikeSymbol extends TypeSymbol { + val parentTypeNames: Ls[Var] + + private val _baseClassLikeSymbols: Lazy[SortedSet[ClassLikeSymbol]] = new mlscript.utils.Lazy({ + implicit val ord: Ordering[ClassLikeSymbol] = new Ordering[ClassLikeSymbol] { + override def compare(x: ClassLikeSymbol, y: ClassLikeSymbol): Int = + x.name.compareTo(y.name) + } + val parentClassLikeSymbols = parentTypeNames.iterator.map(_.symbol).collect { + case s: ClassLikeSymbol => s + }.toList + SortedSet.from( + parentClassLikeSymbols.iterator ++ + parentClassLikeSymbols.iterator.flatMap(_.baseClassLikeSymbols)) + }) + + lazy val baseClassLikeSymbols: SortedSet[ClassLikeSymbol] = _baseClassLikeSymbols.get_! + + def <:<(that: ClassLikeSymbol): Bool = + this === that || baseClassLikeSymbols.contains(that) + } + + final class ClassSymbol( + override val defn: NuTypeDef, + override val parentTypeNames: Ls[Var] + ) extends ClassLikeSymbol { require(defn.kind === Cls) } - final class TraitSymbol(override val defn: NuTypeDef) extends TypeSymbol { + final class TraitSymbol( + override val defn: NuTypeDef, + override val parentTypeNames: Ls[Var] + ) extends ClassLikeSymbol { require(defn.kind === Trt) } @@ -75,7 +117,10 @@ package object symbol { require(defn.kind === Als) } - final class ModuleSymbol(override val defn: NuTypeDef) extends TypeSymbol with TermSymbol { + final class ModuleSymbol( + override val defn: NuTypeDef, + override val parentTypeNames: Ls[Var] + ) extends ClassLikeSymbol with TermSymbol { require(defn.kind === Mod) override def nameVar: Var = defn.nameVar @@ -125,5 +170,9 @@ package object symbol { lastWords(s"Symbol already set for $name") } def withSymbol(symbol: Symbol): this.type = { this.symbol = symbol; this } + + def getClassLikeSymbol: Opt[ClassLikeSymbol] = _symbol.collectFirst { + case symbol: ClassLikeSymbol => symbol + } } } diff --git a/shared/src/main/scala/mlscript/ucs/Desugarer.scala b/shared/src/main/scala/mlscript/ucs/Desugarer.scala index d5b7719c..6b5f14b3 100644 --- a/shared/src/main/scala/mlscript/ucs/Desugarer.scala +++ b/shared/src/main/scala/mlscript/ucs/Desugarer.scala @@ -47,8 +47,8 @@ trait Desugarer extends Transformation * get or create a dummy class symbol for it. The desugaring can continue * and `Typer` will throw an error for this miuse. */ - private def requireClassLikeSymbol(symbol: TypeSymbol)(implicit context: Context): TypeSymbol = symbol match { - case symbol @ (_: TraitSymbol | _: ClassSymbol | _: ModuleSymbol | _: DummyClassSymbol) => symbol + private def requireClassLikeSymbol(symbol: TypeSymbol)(implicit context: Context): ClassLikeSymbol = symbol match { + case symbol: ClassLikeSymbol => symbol case symbol: MixinSymbol => raiseDesugaringError(msg"Mixins are not allowed in pattern" -> nme.toLoc) context.getOrCreateDummyClassSymbol(nme) @@ -127,7 +127,7 @@ trait Desugarer extends Transformation def withResolvedTermSymbol(implicit scope: Scope): Var = { nme.resolveTermSymbol; nme } /** Associate the `Var` with a class like symbol and returns the class like symbol. */ - def resolveClassLikeSymbol(implicit scope: Scope, context: Context): TypeSymbol = { + def resolveClassLikeSymbol(implicit scope: Scope, context: Context): ClassLikeSymbol = { val symbol = scope.getTypeSymbol(nme.name) match { case S(symbol) => requireClassLikeSymbol(symbol) case N => diff --git a/shared/src/main/scala/mlscript/ucs/context/Pattern.scala b/shared/src/main/scala/mlscript/ucs/context/Pattern.scala index 8fa96aec..dce9622c 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Pattern.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Pattern.scala @@ -4,8 +4,7 @@ import collection.mutable.{Buffer, SortedMap => MutSortedMap} import mlscript.{Lit, Loc, Located, SimpleTerm, TypeName, Var} import mlscript.pretyper.symbol.TypeSymbol import mlscript.utils._, shorthands._ -import mlscript.pretyper.symbol.DummyClassSymbol -import mlscript.pretyper.symbol.ModuleSymbol +import mlscript.pretyper.symbol.{ClassLikeSymbol, DummyClassSymbol, ModuleSymbol} sealed abstract class Pattern { private val locationsBuffer: Buffer[Loc] = Buffer.empty @@ -40,7 +39,7 @@ sealed abstract class Pattern { object Pattern { final case class ClassLike( - val classLikeSymbol: TypeSymbol, + val classLikeSymbol: ClassLikeSymbol, scrutinee: Scrutinee )(override val refined: Bool) extends Pattern { private var unappliedVarOpt: Opt[Var] = N @@ -79,13 +78,10 @@ object Pattern { case otherSymbol: TypeSymbol => otherSymbol.defn.kind.str })} `${classLikeSymbol.name}`" + /** Checks whether this pattern can cover the given pattern. */ override def matches(pat: SimpleTerm): Bool = pat match { - case pat: Var => pat.symbolOption match { - case S(patternSymbol: TypeSymbol) => - patternSymbol === classLikeSymbol || patternSymbol.hasBaseClass(classLikeSymbol) - case S(_) | N => false - } + case pat: Var => pat.getClassLikeSymbol.fold(false)(_ <:< classLikeSymbol) case _: Lit => false } diff --git a/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala index 999c2cd9..a34808ce 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala @@ -2,12 +2,9 @@ package mlscript.ucs.context import collection.mutable.{Buffer, SortedMap => MutSortedMap, SortedSet => MutSortedSet} import mlscript.{Lit, Loc, Var} -import mlscript.pretyper.symbol.TypeSymbol +import mlscript.pretyper.symbol.{ClassLikeSymbol, TypeSymbol} import mlscript.utils._, shorthands._ -import mlscript.DecLit -import mlscript.IntLit -import mlscript.StrLit -import mlscript.UnitLit +import mlscript.{DecLit, IntLit, StrLit, UnitLit} class Scrutinee(val context: Context, parent: Opt[Scrutinee]) { import Scrutinee._ @@ -36,7 +33,7 @@ class Scrutinee(val context: Context, parent: Opt[Scrutinee]) { * If there is already a `Pattern.ClassLike` for the given symbol, return it. * Otherwise, create a new `Pattern.ClassLike` and return it. */ - def getOrCreateClassPattern(classLikeSymbol: TypeSymbol, refined: Bool): Pattern.ClassLike = + def getOrCreateClassPattern(classLikeSymbol: ClassLikeSymbol, refined: Bool): Pattern.ClassLike = classLikePatterns.getOrElseUpdate(classLikeSymbol, Pattern.ClassLike(classLikeSymbol, this)(refined)) /** diff --git a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala index ed8f9f8c..e570aa5a 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala @@ -79,7 +79,7 @@ trait CoverageChecking { self: Desugarer with Traceable => diagnostics ++ checkCoverage(body, newPending, working - namedScrutinee, seen) ) case ((unseenPatterns, diagnostics), (className: Var) -> body) => - val classSymbol = className.symbolOption.flatMap(_.typeSymbolOption).getOrElse { + val classSymbol = className.getClassLikeSymbol.getOrElse { throw new Exception(s"$className is not associated with a type symbol") } println(s"class symbol: `${classSymbol.name}`") @@ -190,7 +190,7 @@ object CoverageChecking { } /** Separate a class-like pattern if it appears in `patterns`. */ - def separate(classLikeSymbol: TypeSymbol): Opt[(Pattern.ClassLike, Ls[Pattern.ClassLike])] = { + def separate(classLikeSymbol: ClassLikeSymbol): Opt[(Pattern.ClassLike, Ls[Pattern.ClassLike])] = { classLikePatterns.foldRight[(Opt[Pattern.ClassLike], Ls[Pattern.ClassLike])]((N, Nil)) { case (pattern, (S(separated), rest)) => (S(separated), pattern :: rest) case (pattern, (N, rest)) if pattern.classLikeSymbol === classLikeSymbol => (S(pattern), rest) @@ -220,7 +220,7 @@ object CoverageChecking { * locations where the pattern appears, the related patterns, and * unrelated patterns. */ - def split(classLikeSymbol: TypeSymbol): Opt[(Pattern.ClassLike, CaseSet, CaseSet)] = { + def split(classLikeSymbol: ClassLikeSymbol): Opt[(Pattern.ClassLike, CaseSet, CaseSet)] = { def mk(pattern: Pattern): Opt[Lit \/ TypeSymbol] = pattern match { case Pattern.ClassLike(classLikeSymbol, _) => S(R(classLikeSymbol)) case Pattern.Literal(literal) => S(L(literal)) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 7301ce60..0d651bf8 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -135,7 +135,7 @@ trait Desugaring { self: PreTyper => private def flattenClassParameters( parentScrutineeVar: Var, parentScrutinee: Scrutinee, - parentClassLikeSymbol: TypeSymbol, + parentClassLikeSymbol: ClassLikeSymbol, parentRefined: Bool, parameters: Ls[s.Pattern], )(implicit context: Context): Ls[Opt[(Var, Opt[s.Pattern], Ls[Var])]] = { diff --git a/shared/src/main/scala/mlscript/ucs/stages/package.scala b/shared/src/main/scala/mlscript/ucs/stages/package.scala index fe25dd00..3d232a87 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/package.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/package.scala @@ -3,6 +3,7 @@ package mlscript.ucs import mlscript.{App, DecLit, Fld, FldFlags, IntLit, Lit, PlainTup, StrLit, Term, Tup, Var} import mlscript.pretyper.symbol.TypeSymbol import mlscript.utils._, shorthands._ +import mlscript.pretyper.symbol.ClassLikeSymbol package object stages { /** @@ -53,7 +54,7 @@ package object stages { case (_, R(s)) if s.name === "Object" => true case (R(s1), R(s2)) if (s1.name === "true" || s1.name === "false") && s2.name === "Bool" => true case (R(s1), R(s2)) if s1.name === "Int" && s2.name === "Num" => true - case (R(s1), R(s2)) => s1 hasBaseClass s2 + case (R(s1: ClassLikeSymbol), R(s2: ClassLikeSymbol)) => s1 <:< s2 case (L(IntLit(_)), R(s)) if s.name === "Int" || s.name === "Num" => true case (L(StrLit(_)), R(s)) if s.name === "Str" => true case (L(DecLit(_)), R(s)) if s.name === "Num" => true diff --git a/shared/src/main/scala/mlscript/utils/Lazy.scala b/shared/src/main/scala/mlscript/utils/Lazy.scala new file mode 100644 index 00000000..5f333039 --- /dev/null +++ b/shared/src/main/scala/mlscript/utils/Lazy.scala @@ -0,0 +1,36 @@ +package mlscript.utils + +import shorthands._ + +abstract class Box[+A] { + def get: Opt[A] + def get_! : A + def isComputing: Bool +} + +class Eager[+A](val value: A) extends Box[A] { + def isComputing = false + lazy val get = S(value) + def get_! = value +} + +class Lazy[A](thunk: => A) extends Box[A] { + def isComputing = _isComputing + private var _isComputing = false + private var _value: Opt[A] = N + def get = if (_isComputing) N else S(get_!) + def get_! = { + assert(!_isComputing) + _compute + } + private def _compute = { + _isComputing = true + try { + val v = thunk + _value = S(v) + v + } finally { + _isComputing = false + } + } +} diff --git a/shared/src/test/diff/pretyper/Errors.mls b/shared/src/test/diff/pretyper/Errors.mls index c3f4ff33..810df17d 100644 --- a/shared/src/test/diff/pretyper/Errors.mls +++ b/shared/src/test/diff/pretyper/Errors.mls @@ -497,9 +497,6 @@ class Derived0(n: Int) extends Base // ParamOverride mixin DerivedBad(n: Int) extends Base -//│ ╔══[ERROR] could not find definition `Base` -//│ ║ l.499: mixin DerivedBad(n: Int) extends Base -//│ ╙── ^^^^ //│ ╔══[ERROR] mixin definitions cannot yet extend parents //│ ║ l.499: mixin DerivedBad(n: Int) extends Base //│ ╙── ^^^^ @@ -512,65 +509,65 @@ fun foo(x, y) = x + y // PartialApp foo(2, _) //│ ╔══[ERROR] identifier `_` not found -//│ ║ l.513: foo(2, _) +//│ ║ l.510: foo(2, _) //│ ╙── ^ //│ ╔══[ERROR] Widlcard in expression position. -//│ ║ l.513: foo(2, _) +//│ ║ l.510: foo(2, _) //│ ╙── ^ //│ Int // PartialApp _.foo(1) //│ ╔══[ERROR] identifier `_` not found -//│ ║ l.523: _.foo(1) +//│ ║ l.520: _.foo(1) //│ ╙── ^ //│ ╔══[ERROR] Widlcard in expression position. -//│ ║ l.523: _.foo(1) +//│ ║ l.520: _.foo(1) //│ ╙── ^ //│ error // PartialApp _ + _ //│ ╔══[ERROR] identifier `_` not found -//│ ║ l.533: _ + _ +//│ ║ l.530: _ + _ //│ ╙── ^ //│ ╔══[ERROR] identifier `_` not found -//│ ║ l.533: _ + _ +//│ ║ l.530: _ + _ //│ ╙── ^ //│ ╔══[ERROR] Widlcard in expression position. -//│ ║ l.533: _ + _ +//│ ║ l.530: _ + _ //│ ╙── ^ //│ ╔══[ERROR] Widlcard in expression position. -//│ ║ l.533: _ + _ +//│ ║ l.530: _ + _ //│ ╙── ^ //│ Int // PartialApp _2 + _1 //│ ╔══[ERROR] identifier `_2` not found -//│ ║ l.549: _2 + _1 +//│ ║ l.546: _2 + _1 //│ ╙── ^^ //│ ╔══[ERROR] identifier `_1` not found -//│ ║ l.549: _2 + _1 +//│ ║ l.546: _2 + _1 //│ ╙── ^^ //│ ╔══[ERROR] identifier not found: _2 -//│ ║ l.549: _2 + _1 +//│ ║ l.546: _2 + _1 //│ ╙── ^^ //│ ╔══[ERROR] identifier not found: _1 -//│ ║ l.549: _2 + _1 +//│ ║ l.546: _2 + _1 //│ ╙── ^^ //│ Int // RefinedPatterns refined //│ ╔══[ERROR] identifier `refined` not found -//│ ║ l.565: refined +//│ ║ l.562: refined //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Illegal use of reserved operator: refined -//│ ║ l.565: refined +//│ ║ l.562: refined //│ ╙── ^^^^^^^ //│ ╔══[ERROR] identifier not found: refined -//│ ║ l.565: refined +//│ ║ l.562: refined //│ ╙── ^^^^^^^ //│ error @@ -583,13 +580,13 @@ class D() { fun f = 0 } // Refinements let d = D & { f: 0 } //│ ╔══[ERROR] identifier `&` not found -//│ ║ l.584: let d = D & { f: 0 } +//│ ║ l.581: let d = D & { f: 0 } //│ ╙── ^ //│ ╔══[ERROR] Illegal use of reserved operator: & -//│ ║ l.584: let d = D & { f: 0 } +//│ ║ l.581: let d = D & { f: 0 } //│ ╙── ^ //│ ╔══[ERROR] identifier not found: & -//│ ║ l.584: let d = D & { f: 0 } +//│ ║ l.581: let d = D & { f: 0 } //│ ╙── ^ //│ let d: error @@ -600,63 +597,63 @@ x => x + 2 // Res res(1) //│ ╔══[ERROR] identifier `res` not found -//│ ║ l.601: res(1) +//│ ║ l.598: res(1) //│ ╙── ^^^ //│ ╔══[ERROR] identifier not found: res -//│ ║ l.601: res(1) +//│ ║ l.598: res(1) //│ ╙── ^^^ //│ error // Uninstantiable Int //│ ╔══[ERROR] identifier `Int` is resolved to a type -//│ ║ l.611: Int +//│ ║ l.608: Int //│ ╙── ^^^ //│ ╔══[ERROR] Class Int is abstract and cannot be instantiated -//│ ║ l.611: Int +//│ ║ l.608: Int //│ ╙── ^^^ //│ ╔══[ERROR] Class Int cannot be instantiated as it exposes no constructor -//│ ║ l.611: Int +//│ ║ l.608: Int //│ ╙── ^^^ //│ error // Uninstantiable Int() //│ ╔══[ERROR] identifier `Int` is resolved to a type -//│ ║ l.624: Int() +//│ ║ l.621: Int() //│ ╙── ^^^ //│ ╔══[ERROR] Class Int is abstract and cannot be instantiated -//│ ║ l.624: Int() +//│ ║ l.621: Int() //│ ╙── ^^^ //│ ╔══[ERROR] Class Int cannot be instantiated as it exposes no constructor -//│ ║ l.624: Int() +//│ ║ l.621: Int() //│ ╙── ^^^ //│ error // Uninstantiable new Int //│ ╔══[ERROR] identifier `Int` is resolved to a type -//│ ║ l.637: new Int +//│ ║ l.634: new Int //│ ╙── ^^^ //│ ╔══[ERROR] Class Int is abstract and cannot be instantiated -//│ ║ l.637: new Int +//│ ║ l.634: new Int //│ ╙── ^^^ //│ Int // Unit (1, 2) => 3 //│ ╔══[WARNING] literal patterns are ignored -//│ ║ l.647: (1, 2) => 3 +//│ ║ l.644: (1, 2) => 3 //│ ╙── ^ //│ ╔══[WARNING] literal patterns are ignored -//│ ║ l.647: (1, 2) => 3 +//│ ║ l.644: (1, 2) => 3 //│ ╙── ^ //│ (1, 2) -> 3 // Unit 1 => (2, 3) //│ ╔══[WARNING] literal patterns are ignored -//│ ║ l.657: 1 => (2, 3) +//│ ║ l.654: 1 => (2, 3) //│ ╙── ^ //│ 1 -> 3 @@ -669,23 +666,23 @@ val d = 1 // Varargs fun test(...xs) = xs.length //│ ╔══[PARSE ERROR] Unexpected operator here -//│ ║ l.670: fun test(...xs) = xs.length +//│ ║ l.667: fun test(...xs) = xs.length //│ ╙── ^^^ //│ ╔══[ERROR] identifier `xs` not found -//│ ║ l.670: fun test(...xs) = xs.length +//│ ║ l.667: fun test(...xs) = xs.length //│ ╙── ^^ //│ ╔══[ERROR] identifier not found: xs -//│ ║ l.670: fun test(...xs) = xs.length +//│ ║ l.667: fun test(...xs) = xs.length //│ ╙── ^^ //│ fun test: () -> error // WeirdDefs fun fst[x, _] = x //│ ╔══[ERROR] identifier `x` not found -//│ ║ l.683: fun fst[x, _] = x +//│ ║ l.680: fun fst[x, _] = x //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x -//│ ║ l.683: fun fst[x, _] = x +//│ ║ l.680: fun fst[x, _] = x //│ ╙── ^ //│ fun fst: error @@ -698,7 +695,7 @@ module EvalAddLit { } let res = EvalAddLit.eval(add11) //│ ╔══[ERROR] identifier `add11` not found -//│ ║ l.694: val add11 = Add(add11) +//│ ║ l.691: val add11 = Add(add11) //│ ╙── ^^^^^ //│ class Add[E](lhs: E) //│ val add11: 'E diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index 746cdee4..709b7eea 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -1170,6 +1170,7 @@ object DiffTests { private val pattern = "^ducs(?::(\\s*(?:[A-Za-z\\.-]+)(?:,\\s*[A-Za-z\\.-]+)*))?$".r def unapply(flagsString: Str): Opt[Set[Str]] = flagsString match { + case "ducs" => S(Set.empty) case pattern(flags) => Option(flags).map(_.split(",\\s*").toSet) case _ => N } From 3a1a8839e3c478327d30a64f583d50f34c7ac424 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Mon, 29 Apr 2024 03:27:29 +0800 Subject: [PATCH 119/143] Report desugaring errors if syntax tree nodes are reused --- .../test/diff/Defunctionalize/OldMonoList.mls | 28 +++++++++++-------- .../main/scala/mlscript/ucs/Desugarer.scala | 9 ++++++ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/compiler/shared/test/diff/Defunctionalize/OldMonoList.mls b/compiler/shared/test/diff/Defunctionalize/OldMonoList.mls index 856ea86a..0f87a857 100644 --- a/compiler/shared/test/diff/Defunctionalize/OldMonoList.mls +++ b/compiler/shared/test/diff/Defunctionalize/OldMonoList.mls @@ -42,17 +42,23 @@ fun add2(x) = x+2 //│ fun apply$Lambda1$2$3 = (this, x,) => +(x, 1,) //│ Code(List(main$$5())) //│ } -//│ /!!!\ Uncaught error: java.lang.Exception: Internal Error: Symbol already set for obj -//│ at: mlscript.utils.package$.lastWords(package.scala:227) -//│ at: mlscript.VarImpl.symbol_$eq(helpers.scala:870) -//│ at: mlscript.VarImpl.symbol_$eq$(helpers.scala:864) -//│ at: mlscript.Var.symbol_$eq(syntax.scala:67) -//│ at: mlscript.ucs.Desugarer$VarOps.resolveTermSymbol(Desugarer.scala:124) -//│ at: mlscript.ucs.Desugarer$VarOps.withResolvedTermSymbol(Desugarer.scala:129) -//│ at: mlscript.ucs.stages.Desugaring.desugarPatternSplit(Desugaring.scala:432) -//│ at: mlscript.ucs.stages.Desugaring.$anonfun$desugarTermBranch$2(Desugaring.scala:101) -//│ at: mlscript.pretyper.Traceable.trace(Traceable.scala:39) -//│ at: mlscript.pretyper.Traceable.trace$(Traceable.scala:36) +//│ ╔══[ERROR] the `if` expression has already been desugared +//│ ║ l.7: fun map(f)= new List(f(e), tail.map(f)) +//│ ║ ^ +//│ ╙── please make sure that the objects are copied +//│ class Lambda1$3$4() +//│ class Nil$2() +//│ class List$1(e: Int, tail: List$1 | Nil$2) +//│ class Lambda1$2$3() +//│ fun map$List$1: (Object, Object) -> List$1 +//│ fun add2$1: Int -> Int +//│ fun main$$5: () -> List$1 +//│ fun apply$Lambda1$3$4: (anything, Int) -> Int +//│ fun map$Nil$2: forall 'a. ('a & (List$1 | Nil$2), anything) -> (Nil$2 | 'a) +//│ fun apply$Lambda1$2$3: (anything, Int) -> Int +//│ List$1 +//│ res +//│ = List$1 {} :mono class List(e: Int, tail: List | Nil) { diff --git a/shared/src/main/scala/mlscript/ucs/Desugarer.scala b/shared/src/main/scala/mlscript/ucs/Desugarer.scala index 6b5f14b3..cc42ad36 100644 --- a/shared/src/main/scala/mlscript/ucs/Desugarer.scala +++ b/shared/src/main/scala/mlscript/ucs/Desugarer.scala @@ -196,6 +196,15 @@ trait Desugarer extends Transformation * @param scope the scope of the `If` node */ protected def traverseIf(`if`: If)(implicit scope: Scope): Unit = { + `if`.desugaredTerm match { + case S(desugaredTerm) => + raiseDesugaringError( + msg"the `if` expression has already been desugared" -> `if`.getLoc, + msg"please make sure that the objects are copied" -> N, + ) + return + case N => () + } implicit val context: Context = new Context(`if`) trace("traverseIf") { // Stage 0: Transformation From 21ee9739182f94d4f857a501d7749aaa14be5cbb Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Mon, 29 Apr 2024 03:44:24 +0800 Subject: [PATCH 120/143] Rewrite package declarations to simplify import statements --- .../scala/mlscript/pretyper/Diagnosable.scala | 5 +++-- .../scala/mlscript/pretyper/PreTyper.scala | 5 +++-- .../main/scala/mlscript/pretyper/Scope.scala | 10 ++++------ .../main/scala/mlscript/pretyper/Symbol.scala | 15 +++++---------- .../scala/mlscript/pretyper/Traceable.scala | 6 +++--- .../main/scala/mlscript/ucs/Desugarer.scala | 18 +++++++++--------- .../scala/mlscript/ucs/context/Context.scala | 15 +++++++-------- .../scala/mlscript/ucs/context/Pattern.scala | 9 ++++----- .../scala/mlscript/ucs/context/Scrutinee.scala | 12 +++--------- .../src/main/scala/mlscript/ucs/display.scala | 14 ++++++-------- .../mlscript/ucs/stages/CoverageChecking.scala | 17 +++++++---------- .../scala/mlscript/ucs/stages/Desugaring.scala | 15 +++++++-------- .../mlscript/ucs/stages/Normalization.scala | 17 +++++++---------- .../mlscript/ucs/stages/PartialTerm.scala | 5 +++-- .../mlscript/ucs/stages/PostProcessing.scala | 16 +++++++--------- .../mlscript/ucs/stages/Transformation.scala | 12 +++++------- .../scala/mlscript/ucs/stages/package.scala | 9 ++++----- 17 files changed, 87 insertions(+), 113 deletions(-) diff --git a/shared/src/main/scala/mlscript/pretyper/Diagnosable.scala b/shared/src/main/scala/mlscript/pretyper/Diagnosable.scala index 36cc6406..8757c662 100644 --- a/shared/src/main/scala/mlscript/pretyper/Diagnosable.scala +++ b/shared/src/main/scala/mlscript/pretyper/Diagnosable.scala @@ -1,7 +1,8 @@ -package mlscript.pretyper +package mlscript +package pretyper import scala.collection.mutable.Buffer -import mlscript.{Diagnostic, ErrorReport, Loc, Message, WarningReport}, Diagnostic.Source, Message.MessageContext +import Diagnostic.Source, Message.MessageContext import mlscript.utils._, shorthands._ /** diff --git a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala index f46752d1..da58f282 100644 --- a/shared/src/main/scala/mlscript/pretyper/PreTyper.scala +++ b/shared/src/main/scala/mlscript/pretyper/PreTyper.scala @@ -1,7 +1,8 @@ -package mlscript.pretyper +package mlscript +package pretyper import annotation.tailrec, collection.mutable.{Set => MutSet}, collection.immutable.SortedMap, util.chaining._ -import mlscript._, utils._, shorthands._, Diagnostic.PreTyping, Message.MessageContext, symbol._, ucs.Desugarer +import utils._, shorthands._, Diagnostic.PreTyping, Message.MessageContext, symbol._, ucs.Desugarer class PreTyper extends Traceable with Diagnosable with Desugarer { /** A shorthand function to raise errors without specifying the source. */ diff --git a/shared/src/main/scala/mlscript/pretyper/Scope.scala b/shared/src/main/scala/mlscript/pretyper/Scope.scala index 1baa04a5..678e4a6d 100644 --- a/shared/src/main/scala/mlscript/pretyper/Scope.scala +++ b/shared/src/main/scala/mlscript/pretyper/Scope.scala @@ -1,10 +1,8 @@ -package mlscript.pretyper +package mlscript +package pretyper -import collection.immutable.Map -import mlscript.utils._, shorthands._ -import mlscript.{Als, Mod, NuTypeDef, TypeName, TypingUnit, Var} -import scala.annotation.tailrec -import symbol._ +import annotation.tailrec, collection.immutable.Map +import utils._, shorthands._, symbol._ final class Scope(val enclosing: Opt[Scope], val types: Map[Str, TypeSymbol], val terms: Map[Str, TermSymbol]) { import Scope._ diff --git a/shared/src/main/scala/mlscript/pretyper/Symbol.scala b/shared/src/main/scala/mlscript/pretyper/Symbol.scala index 451b89a0..aead66b9 100644 --- a/shared/src/main/scala/mlscript/pretyper/Symbol.scala +++ b/shared/src/main/scala/mlscript/pretyper/Symbol.scala @@ -1,13 +1,8 @@ -package mlscript.pretyper - -import collection.mutable.{Buffer, Map => MutMap, Set => MutSet} -import mlscript.{Loc, NuFunDef, NuTypeDef, Term, Type, TypeName, Var} -import mlscript.{App, TyApp, Tup} -import mlscript.{Cls, Trt, Mxn, Als, Mod} -import mlscript.utils._, shorthands._ -import mlscript.ucs.context.Matchable -import scala.annotation.tailrec -import scala.collection.immutable.SortedSet +package mlscript +package pretyper + +import annotation.tailrec, collection.immutable.SortedSet +import utils._, shorthands._, ucs.context.Matchable package object symbol { sealed trait Symbol { diff --git a/shared/src/main/scala/mlscript/pretyper/Traceable.scala b/shared/src/main/scala/mlscript/pretyper/Traceable.scala index df7aee9d..49f6827f 100644 --- a/shared/src/main/scala/mlscript/pretyper/Traceable.scala +++ b/shared/src/main/scala/mlscript/pretyper/Traceable.scala @@ -1,7 +1,7 @@ -package mlscript.pretyper +package mlscript +package pretyper -import mlscript.Diagnostic -import mlscript.utils._, shorthands._ +import utils._, shorthands._ trait Traceable { /** diff --git a/shared/src/main/scala/mlscript/ucs/Desugarer.scala b/shared/src/main/scala/mlscript/ucs/Desugarer.scala index cc42ad36..461df9ae 100644 --- a/shared/src/main/scala/mlscript/ucs/Desugarer.scala +++ b/shared/src/main/scala/mlscript/ucs/Desugarer.scala @@ -1,13 +1,13 @@ -package mlscript.ucs +package mlscript +package ucs import collection.mutable.{Map => MutMap} import syntax.{source => s, core => c}, stages._, context.{Context, Scrutinee} import mlscript.ucs.display.{showNormalizedTerm, showSplit} import mlscript.pretyper.{PreTyper, Scope} import mlscript.pretyper.symbol._ -import mlscript.{If, Loc, Located, Message, Var}, Message.MessageContext, mlscript.Diagnostic -import mlscript.utils._, shorthands._ -import syntax.core.{Branch, Split} +import Message.MessageContext +import utils._, shorthands._ /** * The main class of the UCS desugaring. @@ -266,18 +266,18 @@ trait Desugarer extends Transformation * Traverse a desugared _core abstract syntax_ tree. The function takes care * of let bindings and resolves variables. */ - private def traverseSplit(split: syntax.core.Split)(implicit scope: Scope): Unit = + private def traverseSplit(split: c.Split)(implicit scope: Scope): Unit = split match { - case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => + case c.Split.Cons(c.Branch(scrutinee, pattern, continuation), tail) => traverseTerm(scrutinee) val patternSymbols = pattern.declaredVars.map(nme => nme -> nme.symbol) traverseSplit(continuation)(scope.withEntries(patternSymbols)) traverseSplit(tail) - case Split.Let(isRec, name, rhs, tail) => + case c.Split.Let(isRec, name, rhs, tail) => val recScope = scope + name.symbol traverseTerm(rhs)(if (isRec) recScope else scope) traverseSplit(tail)(recScope) - case Split.Else(default) => traverseTerm(default) - case Split.Nil => () + case c.Split.Else(default) => traverseTerm(default) + case c.Split.Nil => () } } diff --git a/shared/src/main/scala/mlscript/ucs/context/Context.scala b/shared/src/main/scala/mlscript/ucs/context/Context.scala index c3f21d37..65fa9a58 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Context.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Context.scala @@ -1,12 +1,11 @@ -package mlscript.ucs.context +package mlscript +package ucs +package context -import collection.mutable.{Buffer, Map => MutMap, SortedMap => MutSortedMap} -import mlscript.{If, Loc, Var} -import mlscript.pretyper.symbol.TypeSymbol -import mlscript.pretyper.Scope -import mlscript.ucs.VariableGenerator -import mlscript.utils._, shorthands._ -import mlscript.pretyper.symbol.DummyClassSymbol +import collection.mutable.{Buffer, Map => MutMap} +import utils._, shorthands._ +import pretyper.symbol.{DummyClassSymbol, TypeSymbol} +import pretyper.Scope class Context(originalTerm: If) { /** The prefix of all prefixes. */ diff --git a/shared/src/main/scala/mlscript/ucs/context/Pattern.scala b/shared/src/main/scala/mlscript/ucs/context/Pattern.scala index dce9622c..d5a7e65d 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Pattern.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Pattern.scala @@ -1,10 +1,9 @@ -package mlscript.ucs.context +package mlscript +package ucs.context import collection.mutable.{Buffer, SortedMap => MutSortedMap} -import mlscript.{Lit, Loc, Located, SimpleTerm, TypeName, Var} -import mlscript.pretyper.symbol.TypeSymbol -import mlscript.utils._, shorthands._ -import mlscript.pretyper.symbol.{ClassLikeSymbol, DummyClassSymbol, ModuleSymbol} +import utils._, shorthands._ +import pretyper.symbol.{ClassLikeSymbol, DummyClassSymbol, ModuleSymbol, TypeSymbol} sealed abstract class Pattern { private val locationsBuffer: Buffer[Loc] = Buffer.empty diff --git a/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala index a34808ce..b8777655 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala @@ -1,10 +1,8 @@ -package mlscript.ucs.context +package mlscript +package ucs.context import collection.mutable.{Buffer, SortedMap => MutSortedMap, SortedSet => MutSortedSet} -import mlscript.{Lit, Loc, Var} -import mlscript.pretyper.symbol.{ClassLikeSymbol, TypeSymbol} -import mlscript.utils._, shorthands._ -import mlscript.{DecLit, IntLit, StrLit, UnitLit} +import pretyper.symbol.{ClassLikeSymbol, TermSymbol, TypeSymbol}, utils._, shorthands._ class Scrutinee(val context: Context, parent: Opt[Scrutinee]) { import Scrutinee._ @@ -105,10 +103,6 @@ class Scrutinee(val context: Context, parent: Opt[Scrutinee]) { object Scrutinee { // We might need to move these method to a private `VarOps` because they may // emit diagnostics. - - import mlscript.Term - import mlscript.pretyper.symbol.TermSymbol - def unapply(term: Term)(implicit context: Context): Opt[Scrutinee] = term match { case v: Var => v.symbol match { case symbol: TermSymbol => symbol.getScrutinee diff --git a/shared/src/main/scala/mlscript/ucs/display.scala b/shared/src/main/scala/mlscript/ucs/display.scala index dd343984..f6ff2952 100644 --- a/shared/src/main/scala/mlscript/ucs/display.scala +++ b/shared/src/main/scala/mlscript/ucs/display.scala @@ -1,12 +1,10 @@ -package mlscript.ucs +package mlscript +package ucs -import mlscript.ucs.syntax.{core => c, source => s} -import mlscript.ucs.context.{Context} -import mlscript.pretyper.symbol.{TermSymbol, TypeSymbol} -import mlscript.{App, CaseOf, Fld, FldFlags, Let, Loc, Sel, SimpleTerm, Term, Tup, Var} -import mlscript.{CaseBranches, Case, Wildcard, NoCases} -import mlscript.utils._, shorthands._ -import syntax.core.{Branch, Split} +import syntax.{core => c, source => s} +import context.{Context} +import pretyper.symbol.{TermSymbol, TypeSymbol} +import utils._, shorthands._ /** All the pretty-printing stuff go here. */ package object display { diff --git a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala index e570aa5a..8f3d3e83 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala @@ -1,13 +1,10 @@ -package mlscript.ucs.stages - -import mlscript.{Case, CaseBranches, CaseOf, Let, Lit, Loc, NoCases, Term, Var, Wildcard} -import mlscript.{Diagnostic, ErrorReport, WarningReport} -import mlscript.Message, Message.MessageContext -import mlscript.ucs.Desugarer -import mlscript.ucs.context.{Context, Pattern, Scrutinee} -import mlscript.pretyper.Traceable -import mlscript.pretyper.symbol._ -import mlscript.utils._, shorthands._ +package mlscript +package ucs +package stages + +import utils._, shorthands._, Message.MessageContext +import ucs.context.{Context, Pattern, Scrutinee} +import pretyper.Traceable, pretyper.symbol._ trait CoverageChecking { self: Desugarer with Traceable => import CoverageChecking._ diff --git a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala index 0d651bf8..fe452ed9 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala @@ -1,12 +1,11 @@ -package mlscript.ucs.stages +package mlscript +package ucs +package stages -import mlscript.{App, Asc, Fld, FldFlags, Lit, Sel, PlainTup, Term, Tup, TypeName, Var} -import mlscript.ucs.syntax.{core => c, source => s} -import mlscript.ucs.context.{Context, Scrutinee} -import mlscript.utils._, shorthands._ -import mlscript.pretyper.symbol._ -import mlscript.pretyper.{PreTyper, Scope} -import mlscript.Message, Message.MessageContext +import syntax.{core => c, source => s} +import context.{Context, Scrutinee} +import utils._, shorthands._, Message.MessageContext +import pretyper.symbol._, pretyper.{PreTyper, Scope} /** * The desugaring stage of UCS. In this stage, we transform the source abstract diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index efb77b7f..92ba1512 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -1,14 +1,11 @@ -package mlscript.ucs.stages +package mlscript +package ucs +package stages -import mlscript.{App, CaseOf, DecLit, Fld, FldFlags, IntLit, Let, Lit, Loc, Sel, Term, Tup, Var, StrLit} -import mlscript.{CaseBranches, Case, Wildcard, NoCases} -import mlscript.Message, Message.MessageContext -import mlscript.utils._, shorthands._ -import mlscript.ucs, mlscript.pretyper -import ucs.{Desugarer, Lines, LinesOps, VariableGenerator} -import ucs.context.{Context, Scrutinee} -import ucs.display.{showNormalizedTerm, showSplit} -import ucs.syntax.core.{Pattern, Branch, Split} +import mlscript.utils._, shorthands._, Message.MessageContext +import context.{Context, Scrutinee} +import display.{showNormalizedTerm, showSplit} +import syntax.core.{Pattern, Branch, Split} import pretyper.symbol._ import pretyper.{Diagnosable, Scope, Traceable} diff --git a/shared/src/main/scala/mlscript/ucs/stages/PartialTerm.scala b/shared/src/main/scala/mlscript/ucs/stages/PartialTerm.scala index dc13f689..04be763f 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PartialTerm.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PartialTerm.scala @@ -1,6 +1,7 @@ -package mlscript.ucs.stages +package mlscript +package ucs.stages -import mlscript.{App, PlainTup, Term, Var}, mlscript.utils._, shorthands._ +import utils._, shorthands._ /** * A `PartialTerm` represents a possibly incomplete term. diff --git a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala index 6a5e3c25..232726b3 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala @@ -1,13 +1,11 @@ -package mlscript.ucs.stages +package mlscript +package ucs +package stages -import mlscript.{Case, CaseBranches, CaseOf, Let, Lit, Loc, NoCases, Term, Var, Wildcard} -import mlscript.ucs.Desugarer -import mlscript.ucs.context.{Context, Pattern, Scrutinee} -import mlscript.pretyper.symbol._ -import mlscript.utils._, shorthands._ -import mlscript.Message, Message.MessageContext -import scala.annotation.tailrec -import mlscript.ucs.context.Pattern.ClassLike +import annotation.tailrec +import context.{Context, Pattern, Scrutinee} +import pretyper.symbol._ +import utils._, shorthands._, Message.MessageContext trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => import PostProcessing._ diff --git a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala index d5d3a967..be070bc0 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Transformation.scala @@ -1,12 +1,10 @@ -package mlscript.ucs.stages +package mlscript +package ucs +package stages -import mlscript.ucs, ucs.Desugarer, ucs.syntax.source._ -import mlscript.{If, IfBody, IfBlock, IfElse, IfLet, IfOpApp, IfOpsApp, IfThen} -import mlscript.{Blk, Bra, Term, Var, App, Tup, Lit, Fld, Loc, NuFunDef, TyApp, PlainTup} -import mlscript.pretyper.Traceable -import mlscript.Message, Message._ -import mlscript.utils._, shorthands._ import collection.immutable, annotation.tailrec, util.chaining._ +import utils._, shorthands._, Message._ +import syntax.source._, pretyper.Traceable /** * Transform the parsed AST into an AST similar to the one in the paper. diff --git a/shared/src/main/scala/mlscript/ucs/stages/package.scala b/shared/src/main/scala/mlscript/ucs/stages/package.scala index 3d232a87..b6a5abf7 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/package.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/package.scala @@ -1,9 +1,8 @@ -package mlscript.ucs +package mlscript +package ucs -import mlscript.{App, DecLit, Fld, FldFlags, IntLit, Lit, PlainTup, StrLit, Term, Tup, Var} -import mlscript.pretyper.symbol.TypeSymbol -import mlscript.utils._, shorthands._ -import mlscript.pretyper.symbol.ClassLikeSymbol +import utils._, shorthands._ +import pretyper.symbol.{ClassLikeSymbol, TypeSymbol} package object stages { /** From ee4d5357856adf6ddeeda530a079c1e02e4180d1 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Mon, 6 May 2024 23:33:41 +0800 Subject: [PATCH 121/143] Refactor `specialize` & disable duplication check --- .../src/main/scala/mlscript/ucs/display.scala | 15 +- .../mlscript/ucs/stages/Normalization.scala | 165 ++++++++++------ .../main/scala/mlscript/ucs/syntax/core.scala | 22 +++ shared/src/test/diff/mlscript/Repro.mls | 66 ------- .../ucs/coverage/ConflictedCoveredCases.mls | 81 ++++++++ .../ucs/coverage/ConflictedPatterns.mls | 121 ++++++++++++ .../pretyper/ucs/coverage/CoveredCases.mls | 77 ++++++++ .../pretyper/ucs/coverage/DuplicatedCases.mls | 183 ++++++++++++++++++ shared/src/test/diff/ucs/InterleavedLet.mls | 20 +- shared/src/test/diff/ucs/SimpleUCS.mls | 14 +- shared/src/test/diff/ucs/SplitAroundOp.mls | 2 - 11 files changed, 619 insertions(+), 147 deletions(-) create mode 100644 shared/src/test/diff/pretyper/ucs/coverage/ConflictedCoveredCases.mls create mode 100644 shared/src/test/diff/pretyper/ucs/coverage/ConflictedPatterns.mls create mode 100644 shared/src/test/diff/pretyper/ucs/coverage/CoveredCases.mls create mode 100644 shared/src/test/diff/pretyper/ucs/coverage/DuplicatedCases.mls diff --git a/shared/src/main/scala/mlscript/ucs/display.scala b/shared/src/main/scala/mlscript/ucs/display.scala index f6ff2952..77650f7f 100644 --- a/shared/src/main/scala/mlscript/ucs/display.scala +++ b/shared/src/main/scala/mlscript/ucs/display.scala @@ -71,9 +71,10 @@ package object display { ("if" #: termSplit(split, true, true)).toIndentedString } - @inline def showSplit(s: c.Split)(implicit context: Context): Str = showSplit("if", s) + @inline def showSplit(s: c.Split, showFirstLevel: Bool = false)(implicit context: Context): Str = + showSplit("if", s, showFirstLevel) - def showSplit(prefix: Str, s: c.Split)(implicit context: Context): Str = { + def showSplit(prefix: Str, s: c.Split, showFirstLevel: Bool)(implicit context: Context): Str = { def split(s: c.Split, isFirst: Bool, isTopLevel: Bool): Lines = s match { case c.Split.Cons(head, tail) => (branch(head, isTopLevel) match { case (n, line) :: tail => (n, (if (isTopLevel) "" else "and ") + s"$line") :: tail @@ -87,7 +88,15 @@ package object display { } def branch(b: c.Branch, isTopLevel: Bool): Lines = { val c.Branch(scrutinee, pattern, continuation) = b - s"${showVar(scrutinee)} is $pattern" #: split(continuation, true, isTopLevel) + if (showFirstLevel) { + val continuation = b.continuation match { + case c.Split.Nil => "empty" + case c.Split.Else(_) => "then ..." + case _ => "and ..." + } + (0, s"${showVar(scrutinee)} is $pattern " + continuation) :: Nil + } + else s"${showVar(scrutinee)} is $pattern" #: split(continuation, true, isTopLevel) } val lines = split(s, true, true) (if (prefix.isEmpty) lines else prefix #: lines).toIndentedString diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 92ba1512..e02794e3 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -32,7 +32,7 @@ trait Normalization { self: Desugarer with Traceable => these.copy(tail = fillImpl(tail, those)(scope, context, declaredVars + nme, false)) } case _: Split.Else => these - case Split.Nil => those.withoutBindings(declaredVars) + case Split.Nil => those.withoutBindings(declaredVars).markAsFallback() }) private implicit class SplitOps(these: Split) { @@ -49,7 +49,7 @@ trait Normalization { self: Desugarer with Traceable => scope: Scope, context: Context, ): Split = - trace(s"fill <== ${declaredVars.iterator.map(_.name).mkString("{", ", ", "}")}") { + trace(s"fill <== vars = ${declaredVars.iterator.map(_.name).mkString("{", ", ", "}")}") { println(s"LHS: ${showSplit(these)}") println(s"RHS: ${showSplit(those)}") fillImpl(these, those)(scope, context, declaredVars, shouldReportDiscarded) @@ -121,7 +121,7 @@ trait Normalization { self: Desugarer with Traceable => private def normalizeToTerm(split: Split, declaredVars: Set[Var])(implicit scope: Scope, context: Context, - ): Term = trace("normalizeToTerm <==") { + ): Term = trace(s"normalizeToTerm <== ${showSplit(split)}") { split match { case Split.Cons(Branch(scrutinee, Pattern.Name(nme), continuation), tail) => println(s"ALIAS: ${scrutinee.name} is ${nme.name}") @@ -138,15 +138,15 @@ trait Normalization { self: Desugarer with Traceable => println(s"entire split: ${showSplit(split)}") val concatenatedTrueBranch = continuation.fill(tail, declaredVars, false) // println(s"true branch: ${showSplit(concatenatedTrueBranch)}") - val trueBranch = normalizeToTerm(specialize(concatenatedTrueBranch, true)(scrutineeVar, scrutinee, pattern, context), declaredVars) + val trueBranch = normalizeToTerm(specialize(concatenatedTrueBranch, true, scrutineeVar, scrutinee, pattern), declaredVars) // println(s"false branch: ${showSplit(tail)}") - val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context), declaredVars) + val falseBranch = normalizeToCaseBranches(specialize(tail, false, scrutineeVar, scrutinee, pattern), declaredVars) CaseOf(scrutineeVar, Case(literal, trueBranch, falseBranch)(refined = false)) case Split.Cons(Branch(Scrutinee.WithVar(scrutineeVar, scrutinee), pattern @ Pattern.Class(nme, _, rfd), continuation), tail) => println(s"CLASS: ${scrutineeVar.name} is ${nme.name}") // println(s"match ${scrutineeVar.name} with $nme (has location: ${nme.toLoc.isDefined})") - val trueBranch = normalizeToTerm(specialize(continuation.fill(tail, declaredVars, false), true)(scrutineeVar, scrutinee, pattern, context), declaredVars) - val falseBranch = normalizeToCaseBranches(specialize(tail, false)(scrutineeVar, scrutinee, pattern, context), declaredVars) + val trueBranch = normalizeToTerm(specialize(continuation.fill(tail, declaredVars, false), true, scrutineeVar, scrutinee, pattern), declaredVars) + val falseBranch = normalizeToCaseBranches(specialize(tail, false, scrutineeVar, scrutinee, pattern), declaredVars) CaseOf(scrutineeVar, Case(nme, trueBranch, falseBranch)(refined = pattern.refined)) case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => raiseDesugaringError(msg"unsupported pattern: ${pattern.toString}" -> pattern.toLoc) @@ -164,7 +164,7 @@ trait Normalization { self: Desugarer with Traceable => println(s"DFLT: ${default.showDbg}") default case Split.Nil => - raiseDesugaringError(msg"unexpected empty split found" -> N) + // raiseDesugaringError(msg"unexpected empty split found" -> N) errorTerm } }(split => "normalizeToTerm ==> " + showNormalizedTerm(split)) @@ -197,57 +197,108 @@ trait Normalization { self: Desugarer with Traceable => * `scrutinee` matches `pattern`. Otherwise, the function _removes_ branches * that agree on `scrutinee` matches `pattern`. */ - private def specialize - (split: Split, matchOrNot: Bool) - (implicit scrutineeVar: Var, scrutinee: Scrutinee, pattern: Pattern, context: Context): Split = - trace[Split](s"S${if (matchOrNot) "+" else "-"} <== ${scrutineeVar.name} is ${pattern}") { - (matchOrNot, split) match { - // Name patterns are translated to let bindings. - case (m, Split.Cons(Branch(otherScrutineeVar, Pattern.Name(alias), continuation), tail)) => - Split.Let(false, alias, otherScrutineeVar, specialize(continuation, m)) - case (m, Split.Cons(head @ Branch(test, Pattern.Class(Var("true"), _, _), continuation), tail)) if context.isTestVar(test) => - println(s"TEST: ${test.name} is true") - head.copy(continuation = specialize(continuation, m)) :: specialize(tail, m) - case (true, split @ Split.Cons(head @ Branch(Scrutinee.WithVar(otherScrutineeVar, otherScrutinee), otherPattern, continuation), tail)) => - if (scrutinee === otherScrutinee) { - println(s"Case 1: ${scrutineeVar.name} === ${otherScrutineeVar.name}") - if (otherPattern =:= pattern) { - println(s"Case 1.1: $pattern =:= $otherPattern") - otherPattern reportInconsistentRefinedWith pattern - specialize(continuation, true) :++ specialize(tail, true) - } else if (otherPattern <:< pattern) { - println(s"Case 1.2: $pattern <:< $otherPattern") - pattern.markAsRefined; split - } else { - println(s"Case 1.3: $pattern are unrelated with $otherPattern") - specialize(tail, true) - } - } else { - println(s"Case 2: ${scrutineeVar.name} =/= ${otherScrutineeVar.name}") - head.copy(continuation = specialize(continuation, true)) :: specialize(tail, true) - } - case (false, split @ Split.Cons(head @ Branch(Scrutinee.WithVar(otherScrutineeVar, otherScrutinee), otherPattern, continuation), tail)) => - if (scrutinee === otherScrutinee) { - println(s"Case 1: ${scrutineeVar.name} === ${otherScrutineeVar.name}") - otherPattern reportInconsistentRefinedWith pattern - if (otherPattern =:= pattern || otherPattern <:< pattern) { - println(s"Case 1.1: $pattern =:= (or <:<) $otherPattern") - specialize(tail, false) + private def specialize( + split: Split, + matchOrNot: Bool, + scrutineeVar: Var, + scrutinee: Scrutinee, + pattern: Pattern + )(implicit context: Context): Split = + trace(s"S${if (matchOrNot) "+" else "-"} <== ${scrutineeVar.name} is ${pattern} : ${showSplit(split, true)}"){ + val specialized = specializeImpl(split)(matchOrNot, scrutineeVar, scrutinee, pattern, context) + // if (split =/= Split.Nil && specialized === Split.Nil && !split.isFallback) { + // raiseDesugaringWarning(msg"the case is unreachable" -> split.toLoc) + // } + specialized + }(r => s"S${if (matchOrNot) "+" else "-"} ==> ${showSplit(r, true)}") + + /** This function does not trace. Call it when handling `tail`s. */ + private def specializeImpl(split: Split)(implicit + keepOrRemove: Bool, + scrutineeVar: Var, + scrutinee: Scrutinee, + pattern: Pattern, + context: Context): Split =split match { + case split @ Split.Cons(head, tail) => + println(s"CASE Cons ${head.showDbg}") + lazy val continuation = specialize(head.continuation, keepOrRemove, scrutineeVar, scrutinee, pattern) + head match { + case Branch(otherScrutineeVar, Pattern.Name(alias), _) => + Split.Let(false, alias, otherScrutineeVar, continuation) + case Branch(test, Pattern.Class(Var("true"), _, _), _) if context.isTestVar(test) => + head.copy(continuation = continuation) :: specializeImpl(tail) + case Branch(Scrutinee.WithVar(otherScrutineeVar, otherScrutinee), otherPattern, _) => + if (scrutinee === otherScrutinee) { + if (keepOrRemove) { + println(s"Case 1.1: ${scrutineeVar.name} === ${otherScrutineeVar.name}") + if (otherPattern =:= pattern) { + println(s"Case 1.1.1: $pattern =:= $otherPattern") + otherPattern reportInconsistentRefinedWith pattern + continuation :++ specializeImpl(tail) + } else if (otherPattern <:< pattern) { + println(s"Case 1.1.2: $pattern <:< $otherPattern") + pattern.markAsRefined; split + } else { + println(s"Case 1.1.3: $pattern is unrelated with $otherPattern") + // The `continuation` is discarded because `otherPattern` is unrelated + // to the specialization topic. + if (!split.isFallback) { + println(s"report warning") + if (pattern <:< otherPattern) { + raiseDesugaringWarning( + msg"the pattern always matches" -> otherPattern.toLoc, + msg"the scrutinee was matched against ${pattern.toString}" -> pattern.toLoc, + msg"which is a subtype of ${otherPattern.toString}" -> (pattern match { + case Pattern.Class(_, symbol, _) => symbol.defn.toLoc + case _ => otherPattern.getLoc + })) + continuation :++ specializeImpl(tail) + } else { + raiseDesugaringWarning( + msg"possibly conflicted patterns" -> otherPattern.toLoc, + msg"the scrutinee was matched against ${pattern.toString}" -> pattern.toLoc, + msg"which is unrelated with ${otherPattern.toString}" -> otherPattern.toLoc) + specializeImpl(tail) + } + } else { + specializeImpl(tail) + } + } + } else { + println(s"Case 1.2: ${scrutineeVar.name} === ${otherScrutineeVar.name}") + otherPattern reportInconsistentRefinedWith pattern + if (otherPattern =:= pattern || otherPattern <:< pattern) { + println(s"Case 1.2.1: $pattern =:= (or <:<) $otherPattern") + // The `continuation` is discarded because `otherPattern` is related + // to the specialization topic. + // println(s"is fallback = ${split.isFallback}") + // if (!split.isFallback) { + // println(s"report warning") + // raiseDesugaringWarning( + // msg"possibly duplicated cases" -> otherPattern.toLoc, + // msg"the case can be covered by ${pattern.toString}" -> pattern.toLoc, + // ) + // } + specializeImpl(tail) + } else { + println(s"Case 1.2.2: $pattern are unrelated with $otherPattern") + split.copy(tail = specializeImpl(tail)) + } + } } else { - println(s"Case 1.2: $pattern are unrelated with $otherPattern") - split.copy(tail = specialize(tail, false)) + println(s"Case 2: ${scrutineeVar.name} =/= ${otherScrutineeVar.name}") + head.copy(continuation = continuation) :: specializeImpl(tail) } - } else { - println(s"Case 2: ${scrutineeVar.name} =/= ${otherScrutineeVar.name}") - head.copy(continuation = specialize(continuation, false)) :: specialize(tail, false) - } - case (_, split @ Split.Cons(Branch(_, pattern, _), _)) => - raiseDesugaringError(msg"unsupported pattern" -> pattern.toLoc) - split - case (m, let @ Split.Let(_, nme, _, tail)) => let.copy(tail = specialize(tail, m)) - case (_, end @ (Split.Else(_) | Split.Nil)) => println("the end"); end - } - }() + case _ => + raiseDesugaringError(msg"unsupported pattern" -> pattern.toLoc) + split + } + case split @ Split.Let(_, nme, _, tail) => + println(s"CASE Let ${nme.name}") + split.copy(tail = specializeImpl(tail)) + case Split.Else(_) => println("CASE Else"); split + case Split.Nil => println("CASE Nil"); split + } /** * If you want to prepend `tail` to another `Split` where the `nme` takes diff --git a/shared/src/main/scala/mlscript/ucs/syntax/core.scala b/shared/src/main/scala/mlscript/ucs/syntax/core.scala index fa73d74e..3d6fe9c9 100644 --- a/shared/src/main/scala/mlscript/ucs/syntax/core.scala +++ b/shared/src/main/scala/mlscript/ucs/syntax/core.scala @@ -43,6 +43,8 @@ package object core { final case class Branch(scrutinee: Var, pattern: Pattern, continuation: Split) extends Located { override def children: List[Located] = scrutinee :: pattern :: continuation :: Nil + + def showDbg: String = s"${scrutinee.showDbg} is $pattern" } sealed abstract class Split extends Located { @@ -84,6 +86,26 @@ package object core { case Split.Else(default) => default :: Nil case Split.Nil => Nil } + + // TODO: Make the following flag temporary. It is only meaningful in a + // single run of specialization. The flag is useless outside specialization + // functions. + + private var _fallback: Bool = false + + def isFallback: Bool = _fallback + + def markAsFallback(): this.type = { + _fallback = true; + this match { + case Split.Cons(head, tail) => + head.continuation.markAsFallback() + tail.markAsFallback() + case Split.Let(_, _, _, tail) => tail.markAsFallback() + case _: Split.Else | Split.Nil => () + } + this + } } object Split { diff --git a/shared/src/test/diff/mlscript/Repro.mls b/shared/src/test/diff/mlscript/Repro.mls index 5f633779..34d9bdfe 100644 --- a/shared/src/test/diff/mlscript/Repro.mls +++ b/shared/src/test/diff/mlscript/Repro.mls @@ -1,67 +1 @@ :NewDefs - -class A() -class B() extends A() -//│ class A() -//│ class B() extends A - -fun p(x) = true -//│ fun p: anything -> true - -fun f(x) = if - x is B and - x is A then 1 - p(x) then 2 - x is A then 31 - x is B then 3 - else 4 -//│ fun f: (B | Object & ~#B) -> (2 | 3 | 31 | 4) - - - -// FIXME should warn about unreachable code (3 disappears) -:ducs:postprocess.result -fun f(x) = if - x is A and - x is B then 1 - p(x) then 2 - x is B then 3 - else 4 -//│ Post-processed UCS term: -//│ case x*‡ of -//│ refined A*◊ -> -//│ case x*‡ of -//│ B*◊ -> 1 -//│ _ -> -//│ let ucs$test$0*† = p(x,) : Bool -//│ case ucs$test$0*† of -//│ true*† -> 2 -//│ _ -> 4 -//│ _ -> 4 -//│ fun f: Object -> (1 | 2 | 4) - - -class X() -class Y() -//│ class X() -//│ class Y() - -// FIXME should warn about unreachable code (1 disappears) -:ducs:postprocess.result -fun f(x) = if - x is X and - x is Y then 1 - p(x) then 2 - x is Y then 3 - else 4 -//│ Post-processed UCS term: -//│ case x*‡ of -//│ X*◊ -> -//│ let ucs$test$0*† = p(x,) : Bool -//│ case ucs$test$0*† of -//│ true*† -> 2 -//│ _ -> 4 -//│ Y*◊ -> 3 -//│ _ -> 4 -//│ fun f: Object -> (2 | 3 | 4) - diff --git a/shared/src/test/diff/pretyper/ucs/coverage/ConflictedCoveredCases.mls b/shared/src/test/diff/pretyper/ucs/coverage/ConflictedCoveredCases.mls new file mode 100644 index 00000000..fcd0d521 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/coverage/ConflictedCoveredCases.mls @@ -0,0 +1,81 @@ +:NewDefs + +class A() +class B() extends A() +//│ class A() +//│ class B() extends A + +fun p(x) = true +//│ fun p: anything -> true + +:w +:ducs:normalize.result +fun f(x) = if + x is B and + x is A then 1 + x is A then 31 +//│ Normalized UCS term: +//│ case x*‡ of +//│ B*◊ -> 1 +//│ _ -> +//│ case x*‡ of +//│ A*◊ -> 31 +//│ ╔══[WARNING] the pattern always matches +//│ ║ l.15: x is A then 1 +//│ ║ ^ +//│ ╟── the scrutinee was matched against B +//│ ║ l.14: x is B and +//│ ║ ^ +//│ ╟── which is a subtype of A +//│ ║ l.4: class B() extends A() +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//│ fun f: (A & ~#B | B) -> (1 | 31) + +// FIXME: wrong missing cases +:w +:ducs:normalize.result +fun f(x) = if + x is 1 and + x is Int then true + x is 2 then false +//│ Normalized UCS term: +//│ case x*‡ of +//│ 1 -> true +//│ _ -> +//│ case x*‡ of +//│ 2 -> false +//│ ╔══[WARNING] the pattern always matches +//│ ║ l.39: x is Int then true +//│ ║ ^^^ +//│ ╟── the scrutinee was matched against 1 +//│ ║ l.38: x is 1 and +//│ ║ ^ +//│ ╟── which is a subtype of Int +//│ ║ l.39: x is Int then true +//│ ╙── ^^^ +//│ ╔══[ERROR] `x` has 1 missing case +//│ ║ l.38: x is 1 and +//│ ║ ^ +//│ ╟── it can be class `Int` +//│ ║ l.39: x is Int then true +//│ ╙── ^^^ +//│ fun f: (1 | 2) -> Bool + +:w +fun f(x) = if + x is B and + x is A then 1 + p(x) then 2 + x is A then 31 + x is B then 3 + else 4 +//│ ╔══[WARNING] the pattern always matches +//│ ║ l.67: x is A then 1 +//│ ║ ^ +//│ ╟── the scrutinee was matched against B +//│ ║ l.66: x is B and +//│ ║ ^ +//│ ╟── which is a subtype of A +//│ ║ l.4: class B() extends A() +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//│ fun f: (B | Object & ~#B) -> (1 | 31 | 4) diff --git a/shared/src/test/diff/pretyper/ucs/coverage/ConflictedPatterns.mls b/shared/src/test/diff/pretyper/ucs/coverage/ConflictedPatterns.mls new file mode 100644 index 00000000..727a3ab2 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/coverage/ConflictedPatterns.mls @@ -0,0 +1,121 @@ +:NewDefs + +// This test file contains the cases where inner cases are conflicting with outer cases. + +class X +class Y +class Z +//│ class X { +//│ constructor() +//│ } +//│ class Y { +//│ constructor() +//│ } +//│ class Z { +//│ constructor() +//│ } + +:w +:ducs:normalize.result +fun f(x) = if + x is X and x is Y then 1 + else 2 +//│ Normalized UCS term: +//│ case x*‡ of +//│ X*◊ -> 2 +//│ _ -> 2 +//│ ╔══[WARNING] possibly conflicted patterns +//│ ║ l.21: x is X and x is Y then 1 +//│ ║ ^ +//│ ╟── the scrutinee was matched against X +//│ ║ l.21: x is X and x is Y then 1 +//│ ║ ^ +//│ ╟── which is unrelated with Y +//│ ║ l.21: x is X and x is Y then 1 +//│ ╙── ^ +//│ fun f: Object -> 2 + + +:w +:ducs:normalize.result +fun f(x) = if + x is X and + x is Y then 1 + x is Y then 2 + else 3 +//│ Normalized UCS term: +//│ case x*‡ of +//│ X*◊ -> 3 +//│ _ -> 3 +//│ ╔══[WARNING] possibly conflicted patterns +//│ ║ l.43: x is Y then 1 +//│ ║ ^ +//│ ╟── the scrutinee was matched against X +//│ ║ l.42: x is X and +//│ ║ ^ +//│ ╟── which is unrelated with Y +//│ ║ l.43: x is Y then 1 +//│ ╙── ^ +//│ ╔══[WARNING] possibly conflicted patterns +//│ ║ l.44: x is Y then 2 +//│ ║ ^ +//│ ╟── the scrutinee was matched against X +//│ ║ l.42: x is X and +//│ ║ ^ +//│ ╟── which is unrelated with Y +//│ ║ l.44: x is Y then 2 +//│ ╙── ^ +//│ fun f: Object -> 3 + +:w +:ducs:normalize.result +fun f(x) = if + x is X and + x is Y then 1 + x is Z then 2 + else 3 +//│ Normalized UCS term: +//│ case x*‡ of +//│ X*◊ -> 3 +//│ _ -> 3 +//│ ╔══[WARNING] possibly conflicted patterns +//│ ║ l.74: x is Y then 1 +//│ ║ ^ +//│ ╟── the scrutinee was matched against X +//│ ║ l.73: x is X and +//│ ║ ^ +//│ ╟── which is unrelated with Y +//│ ║ l.74: x is Y then 1 +//│ ╙── ^ +//│ ╔══[WARNING] possibly conflicted patterns +//│ ║ l.75: x is Z then 2 +//│ ║ ^ +//│ ╟── the scrutinee was matched against X +//│ ║ l.73: x is X and +//│ ║ ^ +//│ ╟── which is unrelated with Z +//│ ║ l.75: x is Z then 2 +//│ ╙── ^ +//│ fun f: Object -> 3 + +:w +:ducs:normalize.result +fun f(x) = if + x is X and + x is Y and + x is Z then 1 + else 2 +//│ Normalized UCS term: +//│ case x*‡ of +//│ X*◊ -> 2 +//│ _ -> 2 +//│ ╔══[WARNING] possibly conflicted patterns +//│ ║ l.105: x is Y and +//│ ║ ^ +//│ ╟── the scrutinee was matched against X +//│ ║ l.104: x is X and +//│ ║ ^ +//│ ╟── which is unrelated with Y +//│ ║ l.105: x is Y and +//│ ╙── ^ +//│ fun f: Object -> 2 diff --git a/shared/src/test/diff/pretyper/ucs/coverage/CoveredCases.mls b/shared/src/test/diff/pretyper/ucs/coverage/CoveredCases.mls new file mode 100644 index 00000000..12119e25 --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/coverage/CoveredCases.mls @@ -0,0 +1,77 @@ +:NewDefs + +// This test file contains the cases where latter cases are covered by the former cases. + +// B <: A +class A() +class B() extends A() +//│ class A() +//│ class B() extends A + +:w +fun f(x) = if + x is A then 1 + x is A then 2 +//│ fun f: A -> 1 +//│ TEST CASE FAILURE: There was an unexpected lack of warning + +:w +:ducs:normalize.result +fun f(x) = if + x is A then 1 + x is B then 2 +//│ Normalized UCS term: +//│ case x*‡ of +//│ A*◊ -> 1 +//│ fun f: A -> 1 +//│ TEST CASE FAILURE: There was an unexpected lack of warning + +:ducs:normalize.result +fun f(x) = if + x is A and + x is B then 1 +//│ Normalized UCS term: +//│ case x*‡ of +//│ refined A*◊ -> +//│ case x*‡ of +//│ B*◊ -> 1 +//│ fun f: B -> 1 + +:w +:ducs:normalize.result +fun f(x) = if + x is A and + x is B then 1 + x is B then 2 +//│ Normalized UCS term: +//│ case x*‡ of +//│ refined A*◊ -> +//│ case x*‡ of +//│ B*◊ -> 1 +//│ fun f: B -> 1 +//│ TEST CASE FAILURE: There was an unexpected lack of warning + +fun p(x) = true: Bool +//│ fun p: anything -> Bool + +:w +:ducs:normalize.result +fun f(x) = if + x is A and + x is B then 1 + p(x) then 2 + x is B then 3 + else 4 +//│ Normalized UCS term: +//│ case x*‡ of +//│ refined A*◊ -> +//│ case x*‡ of +//│ B*◊ -> 1 +//│ _ -> +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true*† -> 2 +//│ _ -> 4 +//│ _ -> 4 +//│ fun f: Object -> (1 | 2 | 4) +//│ TEST CASE FAILURE: There was an unexpected lack of warning diff --git a/shared/src/test/diff/pretyper/ucs/coverage/DuplicatedCases.mls b/shared/src/test/diff/pretyper/ucs/coverage/DuplicatedCases.mls new file mode 100644 index 00000000..3fd1bb8c --- /dev/null +++ b/shared/src/test/diff/pretyper/ucs/coverage/DuplicatedCases.mls @@ -0,0 +1,183 @@ +:NewDefs + +// This test file contains the cases containing cases with identical patterns. + +:w +:ducs:normalize.result +fun f(x) = if x is + "a" then 1 + "b" then 2 + "a" then 3 +//│ Normalized UCS term: +//│ case x*‡ of +//│ "a" -> 1 +//│ _ -> +//│ case x*‡ of +//│ "b" -> 2 +//│ fun f: ("a" | "b") -> (1 | 2) +//│ TEST CASE FAILURE: There was an unexpected lack of warning + +class X +class Y +//│ class X { +//│ constructor() +//│ } +//│ class Y { +//│ constructor() +//│ } + +:w +:ducs:normalize.result +fun f(x) = if x is + X then 1 + X then 2 +//│ Normalized UCS term: +//│ case x*‡ of +//│ X*◊ -> 1 +//│ fun f: X -> 1 +//│ TEST CASE FAILURE: There was an unexpected lack of warning + +:w +:ducs:normalize.result +fun f(x) = if x is + X then 1 + Y then 2 + X then 3 +//│ Normalized UCS term: +//│ case x*‡ of +//│ X*◊ -> 1 +//│ _ -> +//│ case x*‡ of +//│ Y*◊ -> 2 +//│ fun f: (X | Y) -> (1 | 2) +//│ TEST CASE FAILURE: There was an unexpected lack of warning + +class Box[T](value: T) +//│ class Box[T](value: T) + +:ducs:normalize,normalize.result +fun f(x) = if x is + Box(1) then true + Box then false +//│ | | | | | STEP 2 +//│ | | | | | normalizeToTerm <== if +//│ | | | | | x*‡ is Box +//│ | | | | | let ucs$args_x$Box*† = (Box).unapply(x,) +//│ | | | | | let x$Box_0*‡ = (ucs$args_x$Box).0 +//│ | | | | | x$Box_0*‡ is 1 then true +//│ | | | | | x*‡ is Box then false +//│ | | | | | | CLASS: x is Box +//│ | | | | | | fill <== vars = {} +//│ | | | | | | | LHS: if +//│ | | | | | | | let ucs$args_x$Box*† = (Box).unapply(x,) +//│ | | | | | | | let x$Box_0*‡ = (ucs$args_x$Box).0 +//│ | | | | | | | x$Box_0*‡ is 1 then true +//│ | | | | | | | RHS: if x*‡ is Box then false +//│ | | | | | | | fill let binding ucs$args_x$Box +//│ | | | | | | | fill let binding x$Box_0 +//│ | | | | | | fill ==> if +//│ | | | | | | let ucs$args_x$Box*† = (Box).unapply(x,) +//│ | | | | | | let x$Box_0*‡ = (ucs$args_x$Box).0 +//│ | | | | | | x$Box_0*‡ is 1 then true +//│ | | | | | | x*‡ is Box then false +//│ | | | | | | S+ <== x is Box : if +//│ | | | | | | let ucs$args_x$Box*† = (Box).unapply(x,) +//│ | | | | | | let x$Box_0*‡ = (ucs$args_x$Box).0 +//│ | | | | | | x$Box_0*‡ is 1 then ... +//│ | | | | | | x*‡ is Box then ... +//│ | | | | | | | CASE Let ucs$args_x$Box +//│ | | | | | | | CASE Let x$Box_0 +//│ | | | | | | | CASE Cons x$Box_0 is 1 +//│ | | | | | | | Case 2: x =/= x$Box_0 +//│ | | | | | | | S+ <== x is Box : if then true +//│ | | | | | | | | CASE Else +//│ | | | | | | | S+ ==> if then true +//│ | | | | | | | CASE Cons x is Box +//│ | | | | | | | Case 1.1: x === x +//│ | | | | | | | Case 1.1.1: Box =:= Box +//│ | | | | | | | S+ <== x is Box : if then false +//│ | | | | | | | | CASE Else +//│ | | | | | | | S+ ==> if then false +//│ | | | | | | | tail is discarded +//│ | | | | | | S+ ==> if +//│ | | | | | | let ucs$args_x$Box*† = (Box).unapply(x,) +//│ | | | | | | let x$Box_0*‡ = (ucs$args_x$Box).0 +//│ | | | | | | x$Box_0*‡ is 1 then ... +//│ | | | | | | else false +//│ | | | | | | normalizeToTerm <== if +//│ | | | | | | let ucs$args_x$Box*† = (Box).unapply(x,) +//│ | | | | | | let x$Box_0*‡ = (ucs$args_x$Box).0 +//│ | | | | | | x$Box_0*‡ is 1 then true +//│ | | | | | | else false +//│ | | | | | | | LET: generated ucs$args_x$Box +//│ | | | | | | | normalizeToTerm <== if +//│ | | | | | | | let x$Box_0*‡ = (ucs$args_x$Box).0 +//│ | | | | | | | x$Box_0*‡ is 1 then true +//│ | | | | | | | else false +//│ | | | | | | | | LET: x$Box_0 +//│ | | | | | | | | normalizeToTerm <== if +//│ | | | | | | | | x$Box_0*‡ is 1 then true +//│ | | | | | | | | else false +//│ | | | | | | | | | LITERAL: x$Box_0 is 1 +//│ | | | | | | | | | entire split: if +//│ | | | | | | | | | x$Box_0*‡ is 1 then true +//│ | | | | | | | | | else false +//│ | | | | | | | | | fill <== vars = {ucs$args_x$Box} +//│ | | | | | | | | | | LHS: if then true +//│ | | | | | | | | | | RHS: if then false +//│ | | | | | | | | | fill ==> if then true +//│ | | | | | | | | | S+ <== x$Box_0 is 1 : if then true +//│ | | | | | | | | | | CASE Else +//│ | | | | | | | | | S+ ==> if then true +//│ | | | | | | | | | normalizeToTerm <== if then true +//│ | | | | | | | | | | DFLT: true +//│ | | | | | | | | | normalizeToTerm ==> true +//│ | | | | | | | | | S- <== x$Box_0 is 1 : if then false +//│ | | | | | | | | | | CASE Else +//│ | | | | | | | | | S- ==> if then false +//│ | | | | | | | | | normalizeToCaseBranches <== +//│ | | | | | | | | | normalizeToCaseBranches ==> +//│ | | | | | | | | normalizeToTerm ==> case x$Box_0*‡ of +//│ | | | | | | | | 1 -> true +//│ | | | | | | | | _ -> false +//│ | | | | | | | normalizeToTerm ==> let x$Box_0*‡ = (ucs$args_x$Box).0 +//│ | | | | | | | case x$Box_0*‡ of +//│ | | | | | | | 1 -> true +//│ | | | | | | | _ -> false +//│ | | | | | | normalizeToTerm ==> let ucs$args_x$Box*† = (Box).unapply(x,) +//│ | | | | | | let x$Box_0*‡ = (ucs$args_x$Box).0 +//│ | | | | | | case x$Box_0*‡ of +//│ | | | | | | 1 -> true +//│ | | | | | | _ -> false +//│ | | | | | | S- <== x is Box : if x*‡ is Box then ... +//│ | | | | | | | CASE Cons x is Box +//│ | | | | | | | Case 1.2: x === x +//│ | | | | | | | Case 1.2.1: Box =:= (or <:<) Box +//│ | | | | | | | CASE Nil +//│ | | | | | | S- ==> if +//│ | | | | | | normalizeToCaseBranches <== +//│ | | | | | | normalizeToCaseBranches ==> +//│ | | | | | normalizeToTerm ==> case x*‡ of +//│ | | | | | Box*◊ -> +//│ | | | | | let ucs$args_x$Box*† = (Box).unapply(x,) +//│ | | | | | let x$Box_0*‡ = (ucs$args_x$Box).0 +//│ | | | | | case x$Box_0*‡ of +//│ | | | | | 1 -> true +//│ | | | | | _ -> false +//│ Normalized UCS term: +//│ case x*‡ of +//│ Box*◊ -> +//│ let ucs$args_x$Box*† = (Box).unapply(x,) +//│ let x$Box_0*‡ = (ucs$args_x$Box).0 +//│ case x$Box_0*‡ of +//│ 1 -> true +//│ _ -> false +//│ fun f: Box[Object] -> Bool + +f(Box(0)) +f(Box(1)) +//│ Bool +//│ res +//│ = false +//│ res +//│ = true diff --git a/shared/src/test/diff/ucs/InterleavedLet.mls b/shared/src/test/diff/ucs/InterleavedLet.mls index 7f8eb6e3..22f6a2aa 100644 --- a/shared/src/test/diff/ucs/InterleavedLet.mls +++ b/shared/src/test/diff/ucs/InterleavedLet.mls @@ -37,8 +37,6 @@ fun p(x, y) = x is Some and y is None then 0 y is Some and x is Some then 1 x is Some and y is Some then 0 -//│ ╔══[ERROR] unexpected empty split found -//│ ╙── //│ ╔══[ERROR] `y` has 1 missing case //│ ║ l.38: y is Some and x is Some then 1 //│ ║ ^ @@ -75,10 +73,10 @@ fun f(a, y) = Right(x) then x + y else 0 //│ ╔══[ERROR] Type mismatch in operator application: -//│ ║ l.74: let y = v + 1 +//│ ║ l.72: let y = v + 1 //│ ║ ^^^^^ //│ ╟── reference of type `Right[?B]` is not an instance of type `Int` -//│ ║ l.74: let y = v + 1 +//│ ║ l.72: let y = v + 1 //│ ╙── ^ //│ fun f: forall 'a. (Object & ~#Some | Some[Int | Left['a] | Right[Int]], anything) -> (Int | 'a) @@ -89,9 +87,9 @@ fun q(a) = let y = a + 1 then y //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead -//│ ║ l.89: let y = a + 1 +//│ ║ l.87: let y = a + 1 //│ ║ ^^^^^ -//│ ║ l.90: then y +//│ ║ l.88: then y //│ ╙── ^^^^^^^^^^ //│ fun q: forall 'a. (Left['a] | Object & ~#Left) -> (() | 'a) @@ -108,11 +106,11 @@ fun w() = B then "B" else "?" //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.106: A then "A" +//│ ║ l.104: A then "A" //│ ║ ^ //│ ╙── reference of type `() -> A` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.108: B then "B" +//│ ║ l.106: B then "B" //│ ║ ^ //│ ╙── reference of type `() -> B` is not an instance of type `Bool` //│ fun w: () -> ("?" | "A" | "B") @@ -169,13 +167,13 @@ fun ip(y) = y == z * z then "bruh" else "rocks" //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.167: if q(y) and +//│ ║ l.165: if q(y) and //│ ║ ^^^^ //│ ╟── undefined literal of type `()` is not an instance of type `Bool` -//│ ║ l.90: then y +//│ ║ l.88: then y //│ ║ ^ //│ ╟── but it flows into application with expected type `Bool` -//│ ║ l.167: if q(y) and +//│ ║ l.165: if q(y) and //│ ╙── ^^^^ //│ fun ip: Int -> ("bruh" | "rocks") diff --git a/shared/src/test/diff/ucs/SimpleUCS.mls b/shared/src/test/diff/ucs/SimpleUCS.mls index b09105b2..c233a88c 100644 --- a/shared/src/test/diff/ucs/SimpleUCS.mls +++ b/shared/src/test/diff/ucs/SimpleUCS.mls @@ -327,8 +327,6 @@ fun f(x) = //│ ╔══[ERROR] Syntactic split of patterns are not supported //│ ║ l.325: 0 :: //│ ╙── ^^ -//│ ╔══[ERROR] unexpected empty split found -//│ ╙── //│ fun f: anything -> nothing fun f(x) = @@ -370,22 +368,22 @@ test(false) test(0) test(1) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.370: test(0) +//│ ║ l.368: test(0) //│ ║ ^^^^^^^ //│ ╟── integer literal of type `0` is not an instance of type `Bool` -//│ ║ l.370: test(0) +//│ ║ l.368: test(0) //│ ║ ^ //│ ╟── Note: constraint arises from reference: -//│ ║ l.358: fun test(x) = if x then 0 else "oops" +//│ ║ l.356: fun test(x) = if x then 0 else "oops" //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.371: test(1) +//│ ║ l.369: test(1) //│ ║ ^^^^^^^ //│ ╟── integer literal of type `1` is not an instance of type `Bool` -//│ ║ l.371: test(1) +//│ ║ l.369: test(1) //│ ║ ^ //│ ╟── Note: constraint arises from reference: -//│ ║ l.358: fun test(x) = if x then 0 else "oops" +//│ ║ l.356: fun test(x) = if x then 0 else "oops" //│ ╙── ^ //│ "oops" | 0 | error //│ res diff --git a/shared/src/test/diff/ucs/SplitAroundOp.mls b/shared/src/test/diff/ucs/SplitAroundOp.mls index 75f038b2..3b0619ae 100644 --- a/shared/src/test/diff/ucs/SplitAroundOp.mls +++ b/shared/src/test/diff/ucs/SplitAroundOp.mls @@ -61,8 +61,6 @@ if x is //│ ╔══[ERROR] identifier `x` not found //│ ║ l.38: if x is //│ ╙── ^ -//│ ╔══[ERROR] unexpected empty split found -//│ ╙── //│ ╔══[ERROR] identifier not found: x //│ ║ l.38: if x is //│ ╙── ^ From 67793371388121f4e603940bd08545a547b5778d Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Tue, 7 May 2024 00:07:21 +0800 Subject: [PATCH 122/143] Refine `Pattern.Class` constructor parameter types --- shared/src/main/scala/mlscript/ucs/syntax/core.scala | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/syntax/core.scala b/shared/src/main/scala/mlscript/ucs/syntax/core.scala index 3d6fe9c9..996d960c 100644 --- a/shared/src/main/scala/mlscript/ucs/syntax/core.scala +++ b/shared/src/main/scala/mlscript/ucs/syntax/core.scala @@ -1,8 +1,7 @@ -package mlscript.ucs.syntax +package mlscript +package ucs.syntax -import mlscript.{Diagnostic, Lit, Loc, Located, Message, Term, Var} -import mlscript.utils._, shorthands._ -import mlscript.pretyper.symbol.TypeSymbol +import utils._, shorthands._, pretyper.symbol.ClassLikeSymbol package object core { sealed abstract class Pattern extends Located { @@ -28,7 +27,7 @@ package object core { * @param originallyRefined whether the class is marked as refined from * in source AST */ - final case class Class(nme: Var, symbol: TypeSymbol, originallyRefined: Bool) extends Pattern { + final case class Class(nme: Var, symbol: ClassLikeSymbol, originallyRefined: Bool) extends Pattern { override def children: Ls[Located] = nme :: Nil /** From 67ca7637a55316e671d460ace73ea496d663e2f2 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Tue, 7 May 2024 00:12:52 +0800 Subject: [PATCH 123/143] Function `hasElse` is renamed `isFull` --- .../src/main/scala/mlscript/ucs/stages/Normalization.scala | 4 ++-- shared/src/main/scala/mlscript/ucs/syntax/core.scala | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index e02794e3..454a187f 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -16,7 +16,7 @@ trait Normalization { self: Desugarer with Traceable => declaredVars: Set[Var], shouldReportDiscarded: Bool ): Split = - if (these.hasElse) { + if (these.isFull) { reportUnreachableCase(those, these, onlyIf = those =/= Split.Nil && shouldReportDiscarded) } else (these match { case these @ Split.Cons(head, tail) => @@ -56,7 +56,7 @@ trait Normalization { self: Desugarer with Traceable => }(sp => s"fill ==> ${showSplit(sp)}") def :++(tail: => Split): Split = { - if (these.hasElse) { + if (these.isFull) { println("tail is discarded") // raiseDesugaringWarning(msg"Discarded split because of else branch" -> these.toLoc) these diff --git a/shared/src/main/scala/mlscript/ucs/syntax/core.scala b/shared/src/main/scala/mlscript/ucs/syntax/core.scala index 996d960c..c752e624 100644 --- a/shared/src/main/scala/mlscript/ucs/syntax/core.scala +++ b/shared/src/main/scala/mlscript/ucs/syntax/core.scala @@ -52,9 +52,9 @@ package object core { /** * Returns true if the split has an else branch. */ - lazy val hasElse: Bool = this match { - case Split.Cons(_, tail) => tail.hasElse - case Split.Let(_, _, _, tail) => tail.hasElse + lazy val isFull: Bool = this match { + case Split.Cons(_, tail) => tail.isFull + case Split.Let(_, _, _, tail) => tail.isFull case Split.Else(_) => true case Split.Nil => false } From 10fc090bf43704ba65721773cbc6615829430dc6 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 8 May 2024 03:13:26 +0800 Subject: [PATCH 124/143] Cherry-picked minor fixes --- .../main/scala/mlscript/ucs/context/Scrutinee.scala | 2 +- shared/src/main/scala/mlscript/ucs/display.scala | 2 +- shared/src/main/scala/mlscript/utils/package.scala | 1 + shared/src/test/diff/nu/ArrayProg.mls | 12 ++++++------ shared/src/test/diff/ucs/Humiliation.mls | 10 +++++----- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala index b8777655..464926e9 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala @@ -93,7 +93,7 @@ class Scrutinee(val context: Context, parent: Opt[Scrutinee]) { parentScrutinee.classLikePatterns.iterator.flatMap { case (symbol, pattern) => pattern.findSubScrutinee(this).map(_ -> symbol.name) }.nextOption() match { - case S(index -> typeName) => s"${index.toOrdinalWord} argument of `${typeName}`" + case S(index -> typeName) => s"the ${index.toOrdinalWord} argument of `${typeName}`" case N => s"`${scrutineeVar.name}`" // Still not the best. } } diff --git a/shared/src/main/scala/mlscript/ucs/display.scala b/shared/src/main/scala/mlscript/ucs/display.scala index 77650f7f..38d8c148 100644 --- a/shared/src/main/scala/mlscript/ucs/display.scala +++ b/shared/src/main/scala/mlscript/ucs/display.scala @@ -77,7 +77,7 @@ package object display { def showSplit(prefix: Str, s: c.Split, showFirstLevel: Bool)(implicit context: Context): Str = { def split(s: c.Split, isFirst: Bool, isTopLevel: Bool): Lines = s match { case c.Split.Cons(head, tail) => (branch(head, isTopLevel) match { - case (n, line) :: tail => (n, (if (isTopLevel) "" else "and ") + s"$line") :: tail + case (n, line) :: tail => (n, (if (isTopLevel) "" else "and ") + line) :: tail case Nil => Nil }) ::: split(tail, false, isTopLevel) case c.Split.Let(_, nme, rhs, tail) => diff --git a/shared/src/main/scala/mlscript/utils/package.scala b/shared/src/main/scala/mlscript/utils/package.scala index e8bb4c46..309ddb4f 100644 --- a/shared/src/main/scala/mlscript/utils/package.scala +++ b/shared/src/main/scala/mlscript/utils/package.scala @@ -29,6 +29,7 @@ package object utils { case 1 => "first" case 2 => "second" case 3 => "third" + case n @ (11 | 12 | 13) => s"${n}th" case n => self.toString + (n % 10 match { case 1 => "st" case 2 => "nd" diff --git a/shared/src/test/diff/nu/ArrayProg.mls b/shared/src/test/diff/nu/ArrayProg.mls index 89d7e919..e75f0fdd 100644 --- a/shared/src/test/diff/nu/ArrayProg.mls +++ b/shared/src/test/diff/nu/ArrayProg.mls @@ -192,10 +192,10 @@ fun add(e) = //│ ╔══[ERROR] when `e` is `Pair`, and //│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 //│ ║ ^^^^ -//│ ╟── first argument of `Pair` is `Numbr`, +//│ ╟── the first argument of `Pair` is `Numbr`, //│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 //│ ║ ^^^^^ -//│ ╟── second argument of `Pair` has 1 missing case +//│ ╟── the second argument of `Pair` has 1 missing case //│ ╟── it can be class `Vectr` //│ ║ l.+4: Pair(Vectr(xs), Vectr(ys)) then 1 //│ ╙── ^^^^^ @@ -209,20 +209,20 @@ fun add(e) = //│ ╔══[ERROR] when `e` is `Pair`, and //│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 //│ ║ ^^^^ -//│ ╟── first argument of `Pair` is `Numbr`, +//│ ╟── the first argument of `Pair` is `Numbr`, //│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 //│ ║ ^^^^^ -//│ ╟── second argument of `Pair` has 1 missing case +//│ ╟── the second argument of `Pair` has 1 missing case //│ ╟── it can be class `Vectr` //│ ║ l.+4: Pair(Vectr(xs), Vectr(ys)) then 1 //│ ╙── ^^^^^ //│ ╔══[ERROR] when `e` is `Pair`, and //│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 //│ ║ ^^^^ -//│ ╟── first argument of `Pair` is `Vectr`, +//│ ╟── the first argument of `Pair` is `Vectr`, //│ ║ l.+4: Pair(Vectr(xs), Vectr(ys)) then 1 //│ ║ ^^^^^ -//│ ╟── second argument of `Pair` has 1 missing case +//│ ╟── the second argument of `Pair` has 1 missing case //│ ╟── it can be class `Numbr` //│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 //│ ╙── ^^^^^ diff --git a/shared/src/test/diff/ucs/Humiliation.mls b/shared/src/test/diff/ucs/Humiliation.mls index e4b0a5d9..da7dfbbc 100644 --- a/shared/src/test/diff/ucs/Humiliation.mls +++ b/shared/src/test/diff/ucs/Humiliation.mls @@ -52,20 +52,20 @@ fun foo(x) = if x is //│ ╔══[ERROR] when `x` is `Pair`, and //│ ║ l.50: Pair(Z(), Z()) then "zeros" //│ ║ ^^^^ -//│ ╟── first argument of `Pair` is `Z`, +//│ ╟── the first argument of `Pair` is `Z`, //│ ║ l.50: Pair(Z(), Z()) then "zeros" //│ ║ ^ -//│ ╟── second argument of `Pair` has 1 missing case +//│ ╟── the second argument of `Pair` has 1 missing case //│ ╟── it can be class `O` //│ ║ l.51: Pair(O(), O()) then "ones" //│ ╙── ^ //│ ╔══[ERROR] when `x` is `Pair`, and //│ ║ l.50: Pair(Z(), Z()) then "zeros" //│ ║ ^^^^ -//│ ╟── first argument of `Pair` is `O`, +//│ ╟── the first argument of `Pair` is `O`, //│ ║ l.51: Pair(O(), O()) then "ones" //│ ║ ^ -//│ ╟── second argument of `Pair` has 1 missing case +//│ ╟── the second argument of `Pair` has 1 missing case //│ ╟── it can be class `Z` //│ ║ l.50: Pair(Z(), Z()) then "zeros" //│ ╙── ^ @@ -139,7 +139,7 @@ fun foo(x) = if x is //│ ╔══[ERROR] when `x` is `Pair` //│ ║ l.136: Pair(Z(), Z()) then "zeros" //│ ║ ^^^^ -//│ ╟── second argument of `Pair` has 1 missing case +//│ ╟── the second argument of `Pair` has 1 missing case //│ ╟── it can be class `Z` //│ ║ l.136: Pair(Z(), Z()) then "zeros" //│ ╙── ^ From 4a45caa1b136a598e43030531934adc23f792eae Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 8 May 2024 03:39:03 +0800 Subject: [PATCH 125/143] A still imperfect solution --- .../mlscript/ucs/context/Scrutinee.scala | 7 + .../src/main/scala/mlscript/ucs/display.scala | 2 +- .../mlscript/ucs/stages/Normalization.scala | 184 +++-- .../main/scala/mlscript/ucs/syntax/core.scala | 70 +- .../pretyper/ucs/coverage/CoveredCases.mls | 14 +- .../pretyper/ucs/coverage/DuplicatedCases.mls | 747 +++++++++++++++--- shared/src/test/diff/ucs/Humiliation.mls | 29 +- shared/src/test/diff/ucs/InterleavedLet.mls | 24 +- .../src/test/diff/ucs/OverlappedBranches.mls | 20 +- 9 files changed, 880 insertions(+), 217 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala index 464926e9..9fba232a 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala @@ -3,6 +3,7 @@ package ucs.context import collection.mutable.{Buffer, SortedMap => MutSortedMap, SortedSet => MutSortedSet} import pretyper.symbol.{ClassLikeSymbol, TermSymbol, TypeSymbol}, utils._, shorthands._ +import scala.annotation.tailrec class Scrutinee(val context: Context, parent: Opt[Scrutinee]) { import Scrutinee._ @@ -98,6 +99,12 @@ class Scrutinee(val context: Context, parent: Opt[Scrutinee]) { } } } + + @tailrec + final def isSubScrutineeOf(scrutinee: Scrutinee): Bool = this === scrutinee || (parent match { + case Some(parentScrutinee) => parentScrutinee.isSubScrutineeOf(scrutinee) + case N => false + }) } object Scrutinee { diff --git a/shared/src/main/scala/mlscript/ucs/display.scala b/shared/src/main/scala/mlscript/ucs/display.scala index 38d8c148..56cb9720 100644 --- a/shared/src/main/scala/mlscript/ucs/display.scala +++ b/shared/src/main/scala/mlscript/ucs/display.scala @@ -77,7 +77,7 @@ package object display { def showSplit(prefix: Str, s: c.Split, showFirstLevel: Bool)(implicit context: Context): Str = { def split(s: c.Split, isFirst: Bool, isTopLevel: Bool): Lines = s match { case c.Split.Cons(head, tail) => (branch(head, isTopLevel) match { - case (n, line) :: tail => (n, (if (isTopLevel) "" else "and ") + line) :: tail + case (n, line) :: tail => (n, (if (isTopLevel) "" else "and ") + (if (s.isFallback) "?" else "") + line) :: tail case Nil => Nil }) ::: split(tail, false, isTopLevel) case c.Split.Let(_, nme, rhs, tail) => diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 454a187f..7efa0212 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -10,6 +10,8 @@ import pretyper.symbol._ import pretyper.{Diagnosable, Scope, Traceable} trait Normalization { self: Desugarer with Traceable => + import Normalization._ + private def fillImpl(these: Split, those: Split)(implicit scope: Scope, context: Context, @@ -45,7 +47,11 @@ trait Normalization { self: Desugarer with Traceable => * @param declaredVars the generated variables which have been declared * @return the concatenated split */ - def fill(those: Split, declaredVars: Set[Var], shouldReportDiscarded: Bool)(implicit + def fill( + those: Split, + declaredVars: Set[Var], + shouldReportDiscarded: Bool, + )(implicit scope: Scope, context: Context, ): Split = @@ -122,44 +128,47 @@ trait Normalization { self: Desugarer with Traceable => scope: Scope, context: Context, ): Term = trace(s"normalizeToTerm <== ${showSplit(split)}") { + normalizeToTermImpl(split, declaredVars) + }(split => "normalizeToTerm ==> " + showNormalizedTerm(split)) + + private def normalizeToTermImpl(split: Split, declaredVars: Set[Var])(implicit + scope: Scope, + context: Context, + ): Term = split match { - case Split.Cons(Branch(scrutinee, Pattern.Name(nme), continuation), tail) => - println(s"ALIAS: ${scrutinee.name} is ${nme.name}") - val (wrap, realTail) = preventShadowing(nme, tail) - wrap(Let(false, nme, scrutinee, normalizeToTerm(continuation.fill(realTail, declaredVars, true), declaredVars))) - // Skip Boolean conditions as scrutinees, because they only appear once. - case Split.Cons(Branch(test, pattern @ Pattern.Class(nme @ Var("true"), _, _), continuation), tail) if context.isTestVar(test) => - println(s"TRUE: ${test.name} is true") - val trueBranch = normalizeToTerm(continuation.fill(tail, declaredVars, false), declaredVars) - val falseBranch = normalizeToCaseBranches(tail, declaredVars) - CaseOf(test, Case(nme, trueBranch, falseBranch)(refined = false)) - case Split.Cons(Branch(Scrutinee.WithVar(scrutineeVar, scrutinee), pattern @ Pattern.Literal(literal), continuation), tail) => - println(s"LITERAL: ${scrutineeVar.name} is ${literal.idStr}") - println(s"entire split: ${showSplit(split)}") - val concatenatedTrueBranch = continuation.fill(tail, declaredVars, false) - // println(s"true branch: ${showSplit(concatenatedTrueBranch)}") - val trueBranch = normalizeToTerm(specialize(concatenatedTrueBranch, true, scrutineeVar, scrutinee, pattern), declaredVars) - // println(s"false branch: ${showSplit(tail)}") - val falseBranch = normalizeToCaseBranches(specialize(tail, false, scrutineeVar, scrutinee, pattern), declaredVars) - CaseOf(scrutineeVar, Case(literal, trueBranch, falseBranch)(refined = false)) - case Split.Cons(Branch(Scrutinee.WithVar(scrutineeVar, scrutinee), pattern @ Pattern.Class(nme, _, rfd), continuation), tail) => - println(s"CLASS: ${scrutineeVar.name} is ${nme.name}") - // println(s"match ${scrutineeVar.name} with $nme (has location: ${nme.toLoc.isDefined})") - val trueBranch = normalizeToTerm(specialize(continuation.fill(tail, declaredVars, false), true, scrutineeVar, scrutinee, pattern), declaredVars) - val falseBranch = normalizeToCaseBranches(specialize(tail, false, scrutineeVar, scrutinee, pattern), declaredVars) - CaseOf(scrutineeVar, Case(nme, trueBranch, falseBranch)(refined = pattern.refined)) - case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => - raiseDesugaringError(msg"unsupported pattern: ${pattern.toString}" -> pattern.toLoc) - errorTerm + case Split.Cons(head, tail) => + head match { + case Branch(scrutinee, Pattern.Name(nme), _) => + println(s"ALIAS: ${scrutinee.name} is ${nme.name}") + val (wrap, realTail) = preventShadowing(nme, tail) + val continuation = head.continuation.fill(realTail, declaredVars, true) + wrap(Let(false, nme, scrutinee, normalizeToTermImpl(continuation, declaredVars))) + // Skip Boolean conditions as scrutinees, because they only appear once. + case Branch(test, pattern @ Pattern.Class(nme @ Var("true"), _, _), _) if context.isTestVar(test) => + println(s"TRUE: ${test.name} is true") + val continuation = head.continuation.fill(tail, declaredVars, false) + val whenTrue = normalizeToTerm(continuation, declaredVars) + val whenFalse = normalizeToCaseBranches(tail, declaredVars) + CaseOf(test, Case(nme, whenTrue, whenFalse)(refined = false)) + case Branch(Scrutinee.WithVar(scrutineeVar, scrutinee), pattern @ (Pattern.Literal(_) | Pattern.Class(_, _, _)), _) => + println(s"CONS: ${scrutineeVar.name} is $pattern") + val continuation = head.continuation.fill(tail, declaredVars, false) + val whenTrue = normalizeToTerm(specialize(continuation, +, scrutineeVar, scrutinee, pattern), declaredVars) + val whenFalse = normalizeToCaseBranches(specialize(tail, `-`(head.continuation.isFull), scrutineeVar, scrutinee, pattern).clearFallback(), declaredVars) + CaseOf(scrutineeVar, Case(pattern.toSimpleTerm, whenTrue, whenFalse)(refined = pattern.refined)) + case Branch(scrutinee, pattern, _) => + raiseDesugaringError(msg"unsupported pattern matching: ${scrutinee.name} is ${pattern.toString}" -> pattern.toLoc) + errorTerm + } case Split.Let(_, nme, _, tail) if context.isScrutineeVar(nme) && declaredVars.contains(nme) => println(s"LET: SKIP already declared scrutinee ${nme.name}") - normalizeToTerm(tail, declaredVars) + normalizeToTermImpl(tail, declaredVars) case Split.Let(rec, nme, rhs, tail) if context.isGeneratedVar(nme) => println(s"LET: generated ${nme.name}") - Let(rec, nme, rhs, normalizeToTerm(tail, declaredVars + nme)(scope, context)) + Let(rec, nme, rhs, normalizeToTermImpl(tail, declaredVars + nme)(scope, context)) case Split.Let(rec, nme, rhs, tail) => println(s"LET: ${nme.name}") - Let(rec, nme, rhs, normalizeToTerm(tail, declaredVars)) + Let(rec, nme, rhs, normalizeToTermImpl(tail, declaredVars)) case Split.Else(default) => println(s"DFLT: ${default.showDbg}") default @@ -167,7 +176,6 @@ trait Normalization { self: Desugarer with Traceable => // raiseDesugaringError(msg"unexpected empty split found" -> N) errorTerm } - }(split => "normalizeToTerm ==> " + showNormalizedTerm(split)) private def normalizeToCaseBranches(split: Split, declaredVars: Set[Var])(implicit scope: Scope, @@ -199,94 +207,103 @@ trait Normalization { self: Desugarer with Traceable => */ private def specialize( split: Split, - matchOrNot: Bool, + mode: Mode, scrutineeVar: Var, scrutinee: Scrutinee, pattern: Pattern )(implicit context: Context): Split = - trace(s"S${if (matchOrNot) "+" else "-"} <== ${scrutineeVar.name} is ${pattern} : ${showSplit(split, true)}"){ - val specialized = specializeImpl(split)(matchOrNot, scrutineeVar, scrutinee, pattern, context) + trace(s"S$mode <== ${scrutineeVar.name} is ${pattern} : ${showSplit(split, true)}"){ + val specialized = specializeImpl(split)(mode, scrutineeVar, scrutinee, pattern, context) // if (split =/= Split.Nil && specialized === Split.Nil && !split.isFallback) { // raiseDesugaringWarning(msg"the case is unreachable" -> split.toLoc) // } specialized - }(r => s"S${if (matchOrNot) "+" else "-"} ==> ${showSplit(r, true)}") + }(r => s"S$mode ==> ${showSplit(r, true)}") - /** This function does not trace. Call it when handling `tail`s. */ + /** + * This function does not trace. Call it when handling `tail`s, so we can get + * flattened debug logs. + * @param keepOrRemove `N` if S+ or `S(...)` if S-. If we don't need to track + * duplication information, this parameter can be denoted + * by just a Boolean variable. + */ private def specializeImpl(split: Split)(implicit - keepOrRemove: Bool, + mode: Mode, scrutineeVar: Var, scrutinee: Scrutinee, pattern: Pattern, - context: Context): Split =split match { + context: Context): Split = split match { case split @ Split.Cons(head, tail) => println(s"CASE Cons ${head.showDbg}") - lazy val continuation = specialize(head.continuation, keepOrRemove, scrutineeVar, scrutinee, pattern) + lazy val continuation = specialize(head.continuation, mode, scrutineeVar, scrutinee, pattern) head match { - case Branch(otherScrutineeVar, Pattern.Name(alias), _) => - Split.Let(false, alias, otherScrutineeVar, continuation) + case Branch(thatScrutineeVar, Pattern.Name(alias), _) => + Split.Let(false, alias, thatScrutineeVar, continuation) case Branch(test, Pattern.Class(Var("true"), _, _), _) if context.isTestVar(test) => head.copy(continuation = continuation) :: specializeImpl(tail) - case Branch(Scrutinee.WithVar(otherScrutineeVar, otherScrutinee), otherPattern, _) => - if (scrutinee === otherScrutinee) { - if (keepOrRemove) { - println(s"Case 1.1: ${scrutineeVar.name} === ${otherScrutineeVar.name}") - if (otherPattern =:= pattern) { - println(s"Case 1.1.1: $pattern =:= $otherPattern") - otherPattern reportInconsistentRefinedWith pattern + case Branch(Scrutinee.WithVar(thatScrutineeVar, thatScrutinee), thatPattern, _) => + if (scrutinee === thatScrutinee) mode match { + case + => + println(s"Case 1.1: ${scrutineeVar.name} === ${thatScrutineeVar.name}") + if (thatPattern =:= pattern) { + println(s"Case 1.1.1: $pattern =:= $thatPattern") + thatPattern reportInconsistentRefinedWith pattern continuation :++ specializeImpl(tail) - } else if (otherPattern <:< pattern) { - println(s"Case 1.1.2: $pattern <:< $otherPattern") + } else if (thatPattern <:< pattern) { + println(s"Case 1.1.2: $pattern <:< $thatPattern") pattern.markAsRefined; split } else { - println(s"Case 1.1.3: $pattern is unrelated with $otherPattern") - // The `continuation` is discarded because `otherPattern` is unrelated + println(s"Case 1.1.3: $pattern is unrelated with $thatPattern") + // The `continuation` is discarded because `thatPattern` is unrelated // to the specialization topic. if (!split.isFallback) { println(s"report warning") - if (pattern <:< otherPattern) { + if (pattern <:< thatPattern) { raiseDesugaringWarning( - msg"the pattern always matches" -> otherPattern.toLoc, + msg"the pattern always matches" -> thatPattern.toLoc, msg"the scrutinee was matched against ${pattern.toString}" -> pattern.toLoc, - msg"which is a subtype of ${otherPattern.toString}" -> (pattern match { + msg"which is a subtype of ${thatPattern.toString}" -> (pattern match { case Pattern.Class(_, symbol, _) => symbol.defn.toLoc - case _ => otherPattern.getLoc + case _ => thatPattern.getLoc })) continuation :++ specializeImpl(tail) } else { raiseDesugaringWarning( - msg"possibly conflicted patterns" -> otherPattern.toLoc, + msg"possibly conflicted patterns" -> thatPattern.toLoc, msg"the scrutinee was matched against ${pattern.toString}" -> pattern.toLoc, - msg"which is unrelated with ${otherPattern.toString}" -> otherPattern.toLoc) + msg"which is unrelated with ${thatPattern.toString}" -> thatPattern.toLoc) specializeImpl(tail) } } else { specializeImpl(tail) } } - } else { - println(s"Case 1.2: ${scrutineeVar.name} === ${otherScrutineeVar.name}") - otherPattern reportInconsistentRefinedWith pattern - if (otherPattern =:= pattern || otherPattern <:< pattern) { - println(s"Case 1.2.1: $pattern =:= (or <:<) $otherPattern") - // The `continuation` is discarded because `otherPattern` is related + case -(full) => + println(s"Case 1.2: ${scrutineeVar.name} === ${thatScrutineeVar.name}") + thatPattern reportInconsistentRefinedWith pattern + val samePattern = thatPattern =:= pattern + if (samePattern || thatPattern <:< pattern) { + println(s"Case 1.2.1: $pattern =:= (or <:<) $thatPattern") + // The `continuation` is discarded because `thatPattern` is related // to the specialization topic. - // println(s"is fallback = ${split.isFallback}") - // if (!split.isFallback) { - // println(s"report warning") - // raiseDesugaringWarning( - // msg"possibly duplicated cases" -> otherPattern.toLoc, - // msg"the case can be covered by ${pattern.toString}" -> pattern.toLoc, - // ) - // } - specializeImpl(tail) + println(s"`${thatScrutineeVar.name} is ${thatPattern}` is${if (split.isFallback) " " else " not "}fallback") + println(s"already removed = $full") + if (!split.isFallback && full && !head.continuation.isEmpty) { + println(s"report warning") + raiseDesugaringWarning( + msg"found a duplicated case" -> thatPattern.toLoc, + msg"the case is covered by pattern ${pattern.toString}" -> pattern.toLoc, + ) + } + specializeImpl(tail)( + `-`(if (samePattern) continuation.isFull else full), + scrutineeVar, scrutinee, pattern, context) } else { - println(s"Case 1.2.2: $pattern are unrelated with $otherPattern") + println(s"Case 1.2.2: $pattern are unrelated with $thatPattern") split.copy(tail = specializeImpl(tail)) } - } } else { - println(s"Case 2: ${scrutineeVar.name} =/= ${otherScrutineeVar.name}") + println(s"Case 2: ${scrutineeVar.name} =/= ${thatScrutineeVar.name}") head.copy(continuation = continuation) :: specializeImpl(tail) } case _ => @@ -315,3 +332,14 @@ trait Normalization { self: Desugarer with Traceable => case S(_) | N => identity[Term] _ -> tail } } + +object Normalization { + /** Specialization mode */ + sealed abstract class Mode + final case object + extends Mode { + override def toString(): String = "+" + } + final case class -(full: Bool) extends Mode { + override def toString(): String = s"-($full)" + } +} diff --git a/shared/src/main/scala/mlscript/ucs/syntax/core.scala b/shared/src/main/scala/mlscript/ucs/syntax/core.scala index c752e624..403ea2f8 100644 --- a/shared/src/main/scala/mlscript/ucs/syntax/core.scala +++ b/shared/src/main/scala/mlscript/ucs/syntax/core.scala @@ -1,10 +1,18 @@ package mlscript -package ucs.syntax +package ucs +package syntax -import utils._, shorthands._, pretyper.symbol.ClassLikeSymbol +import collection.mutable.{Set => MutSet} +import utils._, shorthands._, pretyper.symbol.ClassLikeSymbol, context.Scrutinee package object core { sealed abstract class Pattern extends Located { + def refined: Bool + def toSimpleTerm: SimpleTerm = this match { + case Pattern.Literal(literal) => literal + case Pattern.Class(nme, _, _) => nme + case Pattern.Name(_) => die + } def declaredVars: Iterator[Var] = this match { case _: Pattern.Literal | _: Pattern.Class => Iterator.empty case Pattern.Name(nme) => Iterator.single(nme) @@ -17,9 +25,11 @@ package object core { } object Pattern { final case class Literal(literal: Lit) extends Pattern { + override def refined: Bool = false override def children: Ls[Located] = literal :: Nil } final case class Name(nme: Var) extends Pattern { + override def refined: Bool = false override def children: Ls[Located] = nme :: Nil } /** @@ -47,7 +57,11 @@ package object core { } sealed abstract class Split extends Located { - @inline def ::(head: Branch): Split = Split.Cons(head, this) + @inline def ::(head: Branch): Split = { + val res = Split.Cons(head, this) + // res.withFallbackOf(head.continuation) + if (head.continuation.isFallback) res.markAsFallback() else res + } /** * Returns true if the split has an else branch. @@ -59,6 +73,12 @@ package object core { case Split.Nil => false } + lazy val isEmpty: Bool = this match { + case Split.Let(_, _, _, tail) => tail.isEmpty + case Split.Else(_) | Split.Cons(_, _) => false + case Split.Nil => true + } + override lazy val freeVars: Set[Var] = this match { case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => continuation.freeVars ++ tail.freeVars @@ -90,12 +110,17 @@ package object core { // single run of specialization. The flag is useless outside specialization // functions. + /** + * The split is concatenated as a fallback when specializing the given + * scrutinee and pattern. + */ + private var _fallback: Bool = false def isFallback: Bool = _fallback def markAsFallback(): this.type = { - _fallback = true; + _fallback = true this match { case Split.Cons(head, tail) => head.continuation.markAsFallback() @@ -105,6 +130,43 @@ package object core { } this } + + def clearFallback(): this.type = { + _fallback = false + this match { + case Split.Cons(head, tail) => + head.continuation.clearFallback() + tail.clearFallback() + case Split.Let(_, _, _, tail) => tail.clearFallback() + case _: Split.Else | Split.Nil => () + } + this + } + + // private val _fallbackSource = MutSet.empty[(Scrutinee, Pattern)] + + // def isFallbackOf(scrutinee: Scrutinee, pattern: Pattern): Bool = + // _fallbackSource.contains((scrutinee, pattern)) + + // def markAsFallback(target: Opt[(Scrutinee, Pattern)]): this.type = + // target.fold[this.type](this)(this.markAsFallback) + + // def markAsFallback(target: (Scrutinee, Pattern)): this.type = { + // _fallbackSource += target + // this match { + // case Split.Cons(head, tail) => + // head.continuation.markAsFallback(target) + // tail.markAsFallback(target) + // case Split.Let(_, _, _, tail) => tail.markAsFallback(target) + // case _: Split.Else | Split.Nil => () + // } + // this + // } + + // def withFallbackOf(that: Split): this.type = { + // _fallbackSource ++= that._fallbackSource + // this + // } } object Split { diff --git a/shared/src/test/diff/pretyper/ucs/coverage/CoveredCases.mls b/shared/src/test/diff/pretyper/ucs/coverage/CoveredCases.mls index 12119e25..cfccce1f 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/CoveredCases.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/CoveredCases.mls @@ -12,8 +12,13 @@ class B() extends A() fun f(x) = if x is A then 1 x is A then 2 +//│ ╔══[WARNING] found a duplicated case +//│ ║ l.14: x is A then 2 +//│ ║ ^ +//│ ╟── the case is covered by pattern A +//│ ║ l.13: x is A then 1 +//│ ╙── ^ //│ fun f: A -> 1 -//│ TEST CASE FAILURE: There was an unexpected lack of warning :w :ducs:normalize.result @@ -23,8 +28,13 @@ fun f(x) = if //│ Normalized UCS term: //│ case x*‡ of //│ A*◊ -> 1 +//│ ╔══[WARNING] found a duplicated case +//│ ║ l.27: x is B then 2 +//│ ║ ^ +//│ ╟── the case is covered by pattern A +//│ ║ l.26: x is A then 1 +//│ ╙── ^ //│ fun f: A -> 1 -//│ TEST CASE FAILURE: There was an unexpected lack of warning :ducs:normalize.result fun f(x) = if diff --git a/shared/src/test/diff/pretyper/ucs/coverage/DuplicatedCases.mls b/shared/src/test/diff/pretyper/ucs/coverage/DuplicatedCases.mls index 3fd1bb8c..97509585 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/DuplicatedCases.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/DuplicatedCases.mls @@ -14,8 +14,13 @@ fun f(x) = if x is //│ _ -> //│ case x*‡ of //│ "b" -> 2 +//│ ╔══[WARNING] found a duplicated case +//│ ║ l.10: "a" then 3 +//│ ║ ^^^ +//│ ╟── the case is covered by pattern "a" +//│ ║ l.8: "a" then 1 +//│ ╙── ^^^ //│ fun f: ("a" | "b") -> (1 | 2) -//│ TEST CASE FAILURE: There was an unexpected lack of warning class X class Y @@ -34,8 +39,13 @@ fun f(x) = if x is //│ Normalized UCS term: //│ case x*‡ of //│ X*◊ -> 1 +//│ ╔══[WARNING] found a duplicated case +//│ ║ l.38: X then 2 +//│ ║ ^ +//│ ╟── the case is covered by pattern X +//│ ║ l.37: X then 1 +//│ ╙── ^ //│ fun f: X -> 1 -//│ TEST CASE FAILURE: There was an unexpected lack of warning :w :ducs:normalize.result @@ -49,121 +59,21 @@ fun f(x) = if x is //│ _ -> //│ case x*‡ of //│ Y*◊ -> 2 +//│ ╔══[WARNING] found a duplicated case +//│ ║ l.55: X then 3 +//│ ║ ^ +//│ ╟── the case is covered by pattern X +//│ ║ l.53: X then 1 +//│ ╙── ^ //│ fun f: (X | Y) -> (1 | 2) -//│ TEST CASE FAILURE: There was an unexpected lack of warning class Box[T](value: T) //│ class Box[T](value: T) -:ducs:normalize,normalize.result +:ducs:normalize.result fun f(x) = if x is Box(1) then true Box then false -//│ | | | | | STEP 2 -//│ | | | | | normalizeToTerm <== if -//│ | | | | | x*‡ is Box -//│ | | | | | let ucs$args_x$Box*† = (Box).unapply(x,) -//│ | | | | | let x$Box_0*‡ = (ucs$args_x$Box).0 -//│ | | | | | x$Box_0*‡ is 1 then true -//│ | | | | | x*‡ is Box then false -//│ | | | | | | CLASS: x is Box -//│ | | | | | | fill <== vars = {} -//│ | | | | | | | LHS: if -//│ | | | | | | | let ucs$args_x$Box*† = (Box).unapply(x,) -//│ | | | | | | | let x$Box_0*‡ = (ucs$args_x$Box).0 -//│ | | | | | | | x$Box_0*‡ is 1 then true -//│ | | | | | | | RHS: if x*‡ is Box then false -//│ | | | | | | | fill let binding ucs$args_x$Box -//│ | | | | | | | fill let binding x$Box_0 -//│ | | | | | | fill ==> if -//│ | | | | | | let ucs$args_x$Box*† = (Box).unapply(x,) -//│ | | | | | | let x$Box_0*‡ = (ucs$args_x$Box).0 -//│ | | | | | | x$Box_0*‡ is 1 then true -//│ | | | | | | x*‡ is Box then false -//│ | | | | | | S+ <== x is Box : if -//│ | | | | | | let ucs$args_x$Box*† = (Box).unapply(x,) -//│ | | | | | | let x$Box_0*‡ = (ucs$args_x$Box).0 -//│ | | | | | | x$Box_0*‡ is 1 then ... -//│ | | | | | | x*‡ is Box then ... -//│ | | | | | | | CASE Let ucs$args_x$Box -//│ | | | | | | | CASE Let x$Box_0 -//│ | | | | | | | CASE Cons x$Box_0 is 1 -//│ | | | | | | | Case 2: x =/= x$Box_0 -//│ | | | | | | | S+ <== x is Box : if then true -//│ | | | | | | | | CASE Else -//│ | | | | | | | S+ ==> if then true -//│ | | | | | | | CASE Cons x is Box -//│ | | | | | | | Case 1.1: x === x -//│ | | | | | | | Case 1.1.1: Box =:= Box -//│ | | | | | | | S+ <== x is Box : if then false -//│ | | | | | | | | CASE Else -//│ | | | | | | | S+ ==> if then false -//│ | | | | | | | tail is discarded -//│ | | | | | | S+ ==> if -//│ | | | | | | let ucs$args_x$Box*† = (Box).unapply(x,) -//│ | | | | | | let x$Box_0*‡ = (ucs$args_x$Box).0 -//│ | | | | | | x$Box_0*‡ is 1 then ... -//│ | | | | | | else false -//│ | | | | | | normalizeToTerm <== if -//│ | | | | | | let ucs$args_x$Box*† = (Box).unapply(x,) -//│ | | | | | | let x$Box_0*‡ = (ucs$args_x$Box).0 -//│ | | | | | | x$Box_0*‡ is 1 then true -//│ | | | | | | else false -//│ | | | | | | | LET: generated ucs$args_x$Box -//│ | | | | | | | normalizeToTerm <== if -//│ | | | | | | | let x$Box_0*‡ = (ucs$args_x$Box).0 -//│ | | | | | | | x$Box_0*‡ is 1 then true -//│ | | | | | | | else false -//│ | | | | | | | | LET: x$Box_0 -//│ | | | | | | | | normalizeToTerm <== if -//│ | | | | | | | | x$Box_0*‡ is 1 then true -//│ | | | | | | | | else false -//│ | | | | | | | | | LITERAL: x$Box_0 is 1 -//│ | | | | | | | | | entire split: if -//│ | | | | | | | | | x$Box_0*‡ is 1 then true -//│ | | | | | | | | | else false -//│ | | | | | | | | | fill <== vars = {ucs$args_x$Box} -//│ | | | | | | | | | | LHS: if then true -//│ | | | | | | | | | | RHS: if then false -//│ | | | | | | | | | fill ==> if then true -//│ | | | | | | | | | S+ <== x$Box_0 is 1 : if then true -//│ | | | | | | | | | | CASE Else -//│ | | | | | | | | | S+ ==> if then true -//│ | | | | | | | | | normalizeToTerm <== if then true -//│ | | | | | | | | | | DFLT: true -//│ | | | | | | | | | normalizeToTerm ==> true -//│ | | | | | | | | | S- <== x$Box_0 is 1 : if then false -//│ | | | | | | | | | | CASE Else -//│ | | | | | | | | | S- ==> if then false -//│ | | | | | | | | | normalizeToCaseBranches <== -//│ | | | | | | | | | normalizeToCaseBranches ==> -//│ | | | | | | | | normalizeToTerm ==> case x$Box_0*‡ of -//│ | | | | | | | | 1 -> true -//│ | | | | | | | | _ -> false -//│ | | | | | | | normalizeToTerm ==> let x$Box_0*‡ = (ucs$args_x$Box).0 -//│ | | | | | | | case x$Box_0*‡ of -//│ | | | | | | | 1 -> true -//│ | | | | | | | _ -> false -//│ | | | | | | normalizeToTerm ==> let ucs$args_x$Box*† = (Box).unapply(x,) -//│ | | | | | | let x$Box_0*‡ = (ucs$args_x$Box).0 -//│ | | | | | | case x$Box_0*‡ of -//│ | | | | | | 1 -> true -//│ | | | | | | _ -> false -//│ | | | | | | S- <== x is Box : if x*‡ is Box then ... -//│ | | | | | | | CASE Cons x is Box -//│ | | | | | | | Case 1.2: x === x -//│ | | | | | | | Case 1.2.1: Box =:= (or <:<) Box -//│ | | | | | | | CASE Nil -//│ | | | | | | S- ==> if -//│ | | | | | | normalizeToCaseBranches <== -//│ | | | | | | normalizeToCaseBranches ==> -//│ | | | | | normalizeToTerm ==> case x*‡ of -//│ | | | | | Box*◊ -> -//│ | | | | | let ucs$args_x$Box*† = (Box).unapply(x,) -//│ | | | | | let x$Box_0*‡ = (ucs$args_x$Box).0 -//│ | | | | | case x$Box_0*‡ of -//│ | | | | | 1 -> true -//│ | | | | | _ -> false //│ Normalized UCS term: //│ case x*‡ of //│ Box*◊ -> @@ -181,3 +91,622 @@ f(Box(1)) //│ = false //│ res //│ = true + +:ducs:postprocess.result +fun a_tale_of_scrutinees(x, y) = + if + x is "A" and y is "B" then "AB" + y is "A" and x is "B" then "BA" + y is "A" and x is "A" then "AA" + x is "B" and y is "B" then "BB" +//│ Post-processed UCS term: +//│ case x*‡ of +//│ "A" -> +//│ case y*‡ of +//│ "B" -> "AB" +//│ "A" -> "AA" +//│ "B" -> +//│ case y*‡ of +//│ "A" -> "BA" +//│ "B" -> "BB" +//│ fun a_tale_of_scrutinees: ("A" | "B", "A" | "B") -> ("AA" | "AB" | "BA" | "BB") + +:ducs:normalize.result +fun test(x, p) = if x is + Bool and p(x) then "great" + true then "false" + false then "true" +//│ Normalized UCS term: +//│ case x*‡ of +//│ refined Bool*◊ -> +//│ let ucs$test$0*† = p(x,) : Bool +//│ case ucs$test$0*† of +//│ true*† -> "great" +//│ _ -> +//│ case x*‡ of +//│ true*† -> "false" +//│ _ -> +//│ case x*‡ of +//│ false*† -> "true" +//│ fun test: forall 'a. ('a & Bool, (Bool & 'a) -> Bool) -> ("false" | "great" | "true") + +class P[A](x: A) +class Q[A, B](x: A, y: B) +//│ class P[A](x: A) +//│ class Q[A, B](x: A, y: B) + +fun f(x) = + if x is + P(1) then 1 + P(y) then 2 +//│ fun f: P[Object] -> (1 | 2) + +:ducs:normalize.result +fun f(x) = + if x is + Q(a, b) and a is 1 and b is 1 then 1 + Q(a, b) and b is 1 then 2 +//│ Normalized UCS term: +//│ case x*‡ of +//│ Q*◊ -> +//│ let ucs$args_x$Q*† = (Q).unapply(x,) +//│ let a*‡ = (ucs$args_x$Q).0 +//│ let b*‡ = (ucs$args_x$Q).1 +//│ case a*‡ of +//│ 1 -> +//│ case b*‡ of +//│ 1 -> 1 +//│ _ -> +//│ case b*‡ of +//│ 1 -> 2 +//│ fun f: Q[Object, 1] -> (1 | 2) + +:e +fun f(x) = + if x is + Q(a, b) and a is 1 and b is 2 then 1 + Q(a, b) and b is 1 then 2 +//│ ╔══[ERROR] when `x` is `Q` +//│ ║ l.167: Q(a, b) and a is 1 and b is 2 then 1 +//│ ║ ^ +//│ ╟── the second argument of `Q` has 1 missing case +//│ ║ l.168: Q(a, b) and b is 1 then 2 +//│ ║ ^ +//│ ╟── it can be literal 2 +//│ ║ l.167: Q(a, b) and a is 1 and b is 2 then 1 +//│ ╙── ^ +//│ fun f: Q[Object, 1] -> (1 | 2) + +:ducs:normalize.result +fun f(x) = + if x is + Q(1, 1) then 1 + Q(y, 1) then 2 +//│ Normalized UCS term: +//│ case x*‡ of +//│ Q*◊ -> +//│ let ucs$args_x$Q*† = (Q).unapply(x,) +//│ let x$Q_0*‡ = (ucs$args_x$Q).0 +//│ let x$Q_1*‡ = (ucs$args_x$Q).1 +//│ case x$Q_0*‡ of +//│ 1 -> +//│ case x$Q_1*‡ of +//│ 1 -> 1 +//│ _ -> (ucs$args_x$Q).0 +//│ _ -> +//│ let y*‡ = (ucs$args_x$Q).0 +//│ case x$Q_1*‡ of +//│ 1 -> 2 +//│ fun f: forall 'a. Q[Object & 'a, 1] -> (1 | 2 | 'a) + +:ducs:normalize +fun f(x) = + if x is + Q(0, 0) then 1 + Q(1, 1) then 2 + Q(y, 1) then 3 + _ then 4 +//│ | | | | | | | STEP 2 +//│ | | | | | | | normalizeToTerm <== if +//│ | | | | | | | x*‡ is Q +//│ | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) +//│ | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 +//│ | | | | | | | x$Q_0*‡ is 0 x$Q_1*‡ is 0 then 1 +//│ | | | | | | | x*‡ is Q +//│ | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) +//│ | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 +//│ | | | | | | | x$Q_0*‡ is 1 x$Q_1*‡ is 1 then 2 +//│ | | | | | | | x*‡ is Q +//│ | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) +//│ | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 +//│ | | | | | | | x$Q_1*‡ is 1 then 3 +//│ | | | | | | | else 4 +//│ | | | | | | | | CONS: x is Q +//│ | | | | | | | | fill <== vars = {} +//│ | | | | | | | | | LHS: if +//│ | | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) +//│ | | | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 +//│ | | | | | | | | | x$Q_0*‡ is 0 x$Q_1*‡ is 0 then 1 +//│ | | | | | | | | | RHS: if +//│ | | | | | | | | | x*‡ is Q +//│ | | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) +//│ | | | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 +//│ | | | | | | | | | x$Q_0*‡ is 1 x$Q_1*‡ is 1 then 2 +//│ | | | | | | | | | x*‡ is Q +//│ | | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) +//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 +//│ | | | | | | | | | x$Q_1*‡ is 1 then 3 +//│ | | | | | | | | | else 4 +//│ | | | | | | | | | fill let binding ucs$args_x$Q +//│ | | | | | | | | | fill let binding x$Q_0 +//│ | | | | | | | | | fill let binding x$Q_1 +//│ | | | | | | | | fill ==> if +//│ | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) +//│ | | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 +//│ | | | | | | | | x$Q_0*‡ is 0 x$Q_1*‡ is 0 then 1 +//│ | | | | | | | | ?x*‡ is Q ?x$Q_0*‡ is 1 ?x$Q_1*‡ is 1 then 2 +//│ | | | | | | | | ?x*‡ is Q +//│ | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | ?x$Q_1*‡ is 1 then 3 +//│ | | | | | | | | else 4 +//│ | | | | | | | | S+ <== x is Q : if +//│ | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) +//│ | | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 +//│ | | | | | | | | x$Q_0*‡ is 0 and ... +//│ | | | | | | | | ?x*‡ is Q and ... +//│ | | | | | | | | ?x*‡ is Q and ... +//│ | | | | | | | | else 4 +//│ | | | | | | | | | CASE Let ucs$args_x$Q +//│ | | | | | | | | | CASE Let x$Q_0 +//│ | | | | | | | | | CASE Let x$Q_1 +//│ | | | | | | | | | CASE Cons x$Q_0 is 0 +//│ | | | | | | | | | Case 2: x =/= x$Q_0 +//│ | | | | | | | | | S+ <== x is Q : if x$Q_1*‡ is 0 then ... +//│ | | | | | | | | | | CASE Cons x$Q_1 is 0 +//│ | | | | | | | | | | Case 2: x =/= x$Q_1 +//│ | | | | | | | | | | S+ <== x is Q : if then 1 +//│ | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | S+ ==> if then 1 +//│ | | | | | | | | | | CASE Nil +//│ | | | | | | | | | S+ ==> if x$Q_1*‡ is 0 then ... +//│ | | | | | | | | | CASE Cons x is Q +//│ | | | | | | | | | Case 1.1: x === x +//│ | | | | | | | | | Case 1.1.1: Q =:= Q +//│ | | | | | | | | | S+ <== x is Q : if ?x$Q_0*‡ is 1 and ... +//│ | | | | | | | | | | CASE Cons x$Q_0 is 1 +//│ | | | | | | | | | | Case 2: x =/= x$Q_0 +//│ | | | | | | | | | | S+ <== x is Q : if ?x$Q_1*‡ is 1 then ... +//│ | | | | | | | | | | | CASE Cons x$Q_1 is 1 +//│ | | | | | | | | | | | Case 2: x =/= x$Q_1 +//│ | | | | | | | | | | | S+ <== x is Q : if then 2 +//│ | | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | | S+ ==> if then 2 +//│ | | | | | | | | | | | CASE Nil +//│ | | | | | | | | | | S+ ==> if ?x$Q_1*‡ is 1 then ... +//│ | | | | | | | | | | CASE Nil +//│ | | | | | | | | | S+ ==> if ?x$Q_0*‡ is 1 and ... +//│ | | | | | | | | | CASE Cons x is Q +//│ | | | | | | | | | Case 1.1: x === x +//│ | | | | | | | | | Case 1.1.1: Q =:= Q +//│ | | | | | | | | | S+ <== x is Q : if +//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | ?x$Q_1*‡ is 1 then ... +//│ | | | | | | | | | | CASE Let y +//│ | | | | | | | | | | CASE Cons x$Q_1 is 1 +//│ | | | | | | | | | | Case 2: x =/= x$Q_1 +//│ | | | | | | | | | | S+ <== x is Q : if then 3 +//│ | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | S+ ==> if then 3 +//│ | | | | | | | | | | CASE Nil +//│ | | | | | | | | | S+ ==> if +//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | ?x$Q_1*‡ is 1 then ... +//│ | | | | | | | | | CASE Else +//│ | | | | | | | | S+ ==> if +//│ | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) +//│ | | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 +//│ | | | | | | | | x$Q_0*‡ is 0 and ... +//│ | | | | | | | | x$Q_0*‡ is 1 and ... +//│ | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | x$Q_1*‡ is 1 then ... +//│ | | | | | | | | else 4 +//│ | | | | | | | | normalizeToTerm <== if +//│ | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) +//│ | | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 +//│ | | | | | | | | x$Q_0*‡ is 0 x$Q_1*‡ is 0 then 1 +//│ | | | | | | | | x$Q_0*‡ is 1 ?x$Q_1*‡ is 1 then 2 +//│ | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | x$Q_1*‡ is 1 then 3 +//│ | | | | | | | | else 4 +//│ | | | | | | | | | LET: generated ucs$args_x$Q +//│ | | | | | | | | | LET: x$Q_0 +//│ | | | | | | | | | LET: x$Q_1 +//│ | | | | | | | | | CONS: x$Q_0 is 0 +//│ | | | | | | | | | fill <== vars = {ucs$args_x$Q} +//│ | | | | | | | | | | LHS: if x$Q_1*‡ is 0 then 1 +//│ | | | | | | | | | | RHS: if +//│ | | | | | | | | | | x$Q_0*‡ is 1 ?x$Q_1*‡ is 1 then 2 +//│ | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | | x$Q_1*‡ is 1 then 3 +//│ | | | | | | | | | | else 4 +//│ | | | | | | | | | fill ==> if +//│ | | | | | | | | | x$Q_1*‡ is 0 then 1 +//│ | | | | | | | | | ?x$Q_0*‡ is 1 ?x$Q_1*‡ is 1 then 2 +//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | ?x$Q_1*‡ is 1 then 3 +//│ | | | | | | | | | else 4 +//│ | | | | | | | | | S+ <== x$Q_0 is 0 : if +//│ | | | | | | | | | x$Q_1*‡ is 0 then ... +//│ | | | | | | | | | ?x$Q_0*‡ is 1 and ... +//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | ?x$Q_1*‡ is 1 then ... +//│ | | | | | | | | | else 4 +//│ | | | | | | | | | | CASE Cons x$Q_1 is 0 +//│ | | | | | | | | | | Case 2: x$Q_0 =/= x$Q_1 +//│ | | | | | | | | | | S+ <== x$Q_0 is 0 : if then 1 +//│ | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | S+ ==> if then 1 +//│ | | | | | | | | | | CASE Cons x$Q_0 is 1 +//│ | | | | | | | | | | Case 1.1: x$Q_0 === x$Q_0 +//│ | | | | | | | | | | Case 1.1.3: 0 is unrelated with 1 +//│ | | | | | | | | | | CASE Let y +//│ | | | | | | | | | | CASE Cons x$Q_1 is 1 +//│ | | | | | | | | | | Case 2: x$Q_0 =/= x$Q_1 +//│ | | | | | | | | | | S+ <== x$Q_0 is 0 : if then 3 +//│ | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | S+ ==> if then 3 +//│ | | | | | | | | | | CASE Else +//│ | | | | | | | | | S+ ==> if +//│ | | | | | | | | | x$Q_1*‡ is 0 then ... +//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | ?x$Q_1*‡ is 1 then ... +//│ | | | | | | | | | else 4 +//│ | | | | | | | | | normalizeToTerm <== if +//│ | | | | | | | | | x$Q_1*‡ is 0 then 1 +//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | ?x$Q_1*‡ is 1 then 3 +//│ | | | | | | | | | else 4 +//│ | | | | | | | | | | CONS: x$Q_1 is 0 +//│ | | | | | | | | | | fill <== vars = {ucs$args_x$Q} +//│ | | | | | | | | | | | LHS: if then 1 +//│ | | | | | | | | | | | RHS: if +//│ | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | | | ?x$Q_1*‡ is 1 then 3 +//│ | | | | | | | | | | | else 4 +//│ | | | | | | | | | | fill ==> if then 1 +//│ | | | | | | | | | | S+ <== x$Q_1 is 0 : if then 1 +//│ | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | S+ ==> if then 1 +//│ | | | | | | | | | | normalizeToTerm <== if then 1 +//│ | | | | | | | | | | | DFLT: 1 +//│ | | | | | | | | | | normalizeToTerm ==> 1 +//│ | | | | | | | | | | S-(true) <== x$Q_1 is 0 : if +//│ | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | | ?x$Q_1*‡ is 1 then ... +//│ | | | | | | | | | | else 4 +//│ | | | | | | | | | | | CASE Let y +//│ | | | | | | | | | | | CASE Cons x$Q_1 is 1 +//│ | | | | | | | | | | | Case 1.2: x$Q_1 === x$Q_1 +//│ | | | | | | | | | | | Case 1.2.2: 0 are unrelated with 1 +//│ | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | S-(true) ==> if +//│ | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | | x$Q_1*‡ is 1 then ... +//│ | | | | | | | | | | else 4 +//│ | | | | | | | | | | normalizeToCaseBranches <== +//│ | | | | | | | | | | | normalizeToCaseBranches <== +//│ | | | | | | | | | | | | normalizeToTerm <== if +//│ | | | | | | | | | | | | x$Q_1*‡ is 1 then 3 +//│ | | | | | | | | | | | | else 4 +//│ | | | | | | | | | | | | | CONS: x$Q_1 is 1 +//│ | | | | | | | | | | | | | fill <== vars = {ucs$args_x$Q} +//│ | | | | | | | | | | | | | | LHS: if then 3 +//│ | | | | | | | | | | | | | | RHS: if then 4 +//│ | | | | | | | | | | | | | fill ==> if then 3 +//│ | | | | | | | | | | | | | S+ <== x$Q_1 is 1 : if then 3 +//│ | | | | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | | | | S+ ==> if then 3 +//│ | | | | | | | | | | | | | normalizeToTerm <== if then 3 +//│ | | | | | | | | | | | | | | DFLT: 3 +//│ | | | | | | | | | | | | | normalizeToTerm ==> 3 +//│ | | | | | | | | | | | | | S-(true) <== x$Q_1 is 1 : if then 4 +//│ | | | | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | | | | S-(true) ==> if then 4 +//│ | | | | | | | | | | | | | normalizeToCaseBranches <== +//│ | | | | | | | | | | | | | normalizeToCaseBranches ==> +//│ | | | | | | | | | | | | normalizeToTerm ==> case x$Q_1*‡ of +//│ | | | | | | | | | | | | 1 -> 3 +//│ | | | | | | | | | | | | _ -> 4 +//│ | | | | | | | | | | | normalizeToCaseBranches ==> +//│ | | | | | | | | | | normalizeToCaseBranches ==> +//│ | | | | | | | | | normalizeToTerm ==> case x$Q_1*‡ of +//│ | | | | | | | | | 0 -> 1 +//│ | | | | | | | | | _ -> +//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | case x$Q_1*‡ of +//│ | | | | | | | | | 1 -> 3 +//│ | | | | | | | | | _ -> 4 +//│ | | | | | | | | | S-(false) <== x$Q_0 is 0 : if +//│ | | | | | | | | | x$Q_0*‡ is 1 and ... +//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | x$Q_1*‡ is 1 then ... +//│ | | | | | | | | | else 4 +//│ | | | | | | | | | | CASE Cons x$Q_0 is 1 +//│ | | | | | | | | | | Case 1.2: x$Q_0 === x$Q_0 +//│ | | | | | | | | | | Case 1.2.2: 0 are unrelated with 1 +//│ | | | | | | | | | | CASE Let y +//│ | | | | | | | | | | CASE Cons x$Q_1 is 1 +//│ | | | | | | | | | | Case 2: x$Q_0 =/= x$Q_1 +//│ | | | | | | | | | | S-(false) <== x$Q_0 is 0 : if then 3 +//│ | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | S-(false) ==> if then 3 +//│ | | | | | | | | | | CASE Else +//│ | | | | | | | | | S-(false) ==> if +//│ | | | | | | | | | x$Q_0*‡ is 1 and ... +//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | x$Q_1*‡ is 1 then ... +//│ | | | | | | | | | else 4 +//│ | | | | | | | | | normalizeToCaseBranches <== +//│ | | | | | | | | | | normalizeToTerm <== if +//│ | | | | | | | | | | x$Q_0*‡ is 1 x$Q_1*‡ is 1 then 2 +//│ | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | | x$Q_1*‡ is 1 then 3 +//│ | | | | | | | | | | else 4 +//│ | | | | | | | | | | | CONS: x$Q_0 is 1 +//│ | | | | | | | | | | | fill <== vars = {ucs$args_x$Q} +//│ | | | | | | | | | | | | LHS: if x$Q_1*‡ is 1 then 2 +//│ | | | | | | | | | | | | RHS: if +//│ | | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | | | | x$Q_1*‡ is 1 then 3 +//│ | | | | | | | | | | | | else 4 +//│ | | | | | | | | | | | fill ==> if +//│ | | | | | | | | | | | x$Q_1*‡ is 1 then 2 +//│ | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | | | ?x$Q_1*‡ is 1 then 3 +//│ | | | | | | | | | | | else 4 +//│ | | | | | | | | | | | S+ <== x$Q_0 is 1 : if +//│ | | | | | | | | | | | x$Q_1*‡ is 1 then ... +//│ | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | | | ?x$Q_1*‡ is 1 then ... +//│ | | | | | | | | | | | else 4 +//│ | | | | | | | | | | | | CASE Cons x$Q_1 is 1 +//│ | | | | | | | | | | | | Case 2: x$Q_0 =/= x$Q_1 +//│ | | | | | | | | | | | | S+ <== x$Q_0 is 1 : if then 2 +//│ | | | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | | | S+ ==> if then 2 +//│ | | | | | | | | | | | | CASE Let y +//│ | | | | | | | | | | | | CASE Cons x$Q_1 is 1 +//│ | | | | | | | | | | | | Case 2: x$Q_0 =/= x$Q_1 +//│ | | | | | | | | | | | | S+ <== x$Q_0 is 1 : if then 3 +//│ | | | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | | | S+ ==> if then 3 +//│ | | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | | S+ ==> if +//│ | | | | | | | | | | | x$Q_1*‡ is 1 then ... +//│ | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | | | ?x$Q_1*‡ is 1 then ... +//│ | | | | | | | | | | | else 4 +//│ | | | | | | | | | | | normalizeToTerm <== if +//│ | | | | | | | | | | | x$Q_1*‡ is 1 then 2 +//│ | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | | | ?x$Q_1*‡ is 1 then 3 +//│ | | | | | | | | | | | else 4 +//│ | | | | | | | | | | | | CONS: x$Q_1 is 1 +//│ | | | | | | | | | | | | fill <== vars = {ucs$args_x$Q} +//│ | | | | | | | | | | | | | LHS: if then 2 +//│ | | | | | | | | | | | | | RHS: if +//│ | | | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | | | | | ?x$Q_1*‡ is 1 then 3 +//│ | | | | | | | | | | | | | else 4 +//│ | | | | | | | | | | | | fill ==> if then 2 +//│ | | | | | | | | | | | | S+ <== x$Q_1 is 1 : if then 2 +//│ | | | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | | | S+ ==> if then 2 +//│ | | | | | | | | | | | | normalizeToTerm <== if then 2 +//│ | | | | | | | | | | | | | DFLT: 2 +//│ | | | | | | | | | | | | normalizeToTerm ==> 2 +//│ | | | | | | | | | | | | S-(true) <== x$Q_1 is 1 : if +//│ | | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | | | | ?x$Q_1*‡ is 1 then ... +//│ | | | | | | | | | | | | else 4 +//│ | | | | | | | | | | | | | CASE Let y +//│ | | | | | | | | | | | | | CASE Cons x$Q_1 is 1 +//│ | | | | | | | | | | | | | Case 1.2: x$Q_1 === x$Q_1 +//│ | | | | | | | | | | | | | Case 1.2.1: 1 =:= (or <:<) 1 +//│ | | | | | | | | | | | | | `x$Q_1 is 1` is fallback +//│ | | | | | | | | | | | | | already removed = true +//│ | | | | | | | | | | | | | S-(true) <== x$Q_1 is 1 : if then 3 +//│ | | | | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | | | | S-(true) ==> if then 3 +//│ | | | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | | | S-(true) ==> if +//│ | | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | | | | else 4 +//│ | | | | | | | | | | | | normalizeToCaseBranches <== +//│ | | | | | | | | | | | | | normalizeToCaseBranches <== +//│ | | | | | | | | | | | | | normalizeToCaseBranches ==> +//│ | | | | | | | | | | | | normalizeToCaseBranches ==> +//│ | | | | | | | | | | | normalizeToTerm ==> case x$Q_1*‡ of +//│ | | | | | | | | | | | 1 -> 2 +//│ | | | | | | | | | | | _ -> +//│ | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | | | 4 +//│ | | | | | | | | | | | S-(false) <== x$Q_0 is 1 : if +//│ | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | | | x$Q_1*‡ is 1 then ... +//│ | | | | | | | | | | | else 4 +//│ | | | | | | | | | | | | CASE Let y +//│ | | | | | | | | | | | | CASE Cons x$Q_1 is 1 +//│ | | | | | | | | | | | | Case 2: x$Q_0 =/= x$Q_1 +//│ | | | | | | | | | | | | S-(false) <== x$Q_0 is 1 : if then 3 +//│ | | | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | | | S-(false) ==> if then 3 +//│ | | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | | S-(false) ==> if +//│ | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | | | ?x$Q_1*‡ is 1 then ... +//│ | | | | | | | | | | | else 4 +//│ | | | | | | | | | | | normalizeToCaseBranches <== +//│ | | | | | | | | | | | | normalizeToCaseBranches <== +//│ | | | | | | | | | | | | | normalizeToTerm <== if +//│ | | | | | | | | | | | | | x$Q_1*‡ is 1 then 3 +//│ | | | | | | | | | | | | | else 4 +//│ | | | | | | | | | | | | | | CONS: x$Q_1 is 1 +//│ | | | | | | | | | | | | | | fill <== vars = {ucs$args_x$Q} +//│ | | | | | | | | | | | | | | | LHS: if then 3 +//│ | | | | | | | | | | | | | | | RHS: if then 4 +//│ | | | | | | | | | | | | | | fill ==> if then 3 +//│ | | | | | | | | | | | | | | S+ <== x$Q_1 is 1 : if then 3 +//│ | | | | | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | | | | | S+ ==> if then 3 +//│ | | | | | | | | | | | | | | normalizeToTerm <== if then 3 +//│ | | | | | | | | | | | | | | | DFLT: 3 +//│ | | | | | | | | | | | | | | normalizeToTerm ==> 3 +//│ | | | | | | | | | | | | | | S-(true) <== x$Q_1 is 1 : if then 4 +//│ | | | | | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | | | | | S-(true) ==> if then 4 +//│ | | | | | | | | | | | | | | normalizeToCaseBranches <== +//│ | | | | | | | | | | | | | | normalizeToCaseBranches ==> +//│ | | | | | | | | | | | | | normalizeToTerm ==> case x$Q_1*‡ of +//│ | | | | | | | | | | | | | 1 -> 3 +//│ | | | | | | | | | | | | | _ -> 4 +//│ | | | | | | | | | | | | normalizeToCaseBranches ==> +//│ | | | | | | | | | | | normalizeToCaseBranches ==> +//│ | | | | | | | | | | normalizeToTerm ==> case x$Q_0*‡ of +//│ | | | | | | | | | | 1 -> +//│ | | | | | | | | | | case x$Q_1*‡ of +//│ | | | | | | | | | | 1 -> 2 +//│ | | | | | | | | | | _ -> +//│ | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | | 4 +//│ | | | | | | | | | | _ -> +//│ | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | | case x$Q_1*‡ of +//│ | | | | | | | | | | 1 -> 3 +//│ | | | | | | | | | | _ -> 4 +//│ | | | | | | | | | normalizeToCaseBranches ==> +//│ | | | | | | | | normalizeToTerm ==> let ucs$args_x$Q*† = (Q).unapply(x,) +//│ | | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 +//│ | | | | | | | | case x$Q_0*‡ of +//│ | | | | | | | | 0 -> +//│ | | | | | | | | case x$Q_1*‡ of +//│ | | | | | | | | 0 -> 1 +//│ | | | | | | | | _ -> +//│ | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | case x$Q_1*‡ of +//│ | | | | | | | | 1 -> 3 +//│ | | | | | | | | _ -> 4 +//│ | | | | | | | | _ -> +//│ | | | | | | | | case x$Q_0*‡ of +//│ | | | | | | | | 1 -> +//│ | | | | | | | | case x$Q_1*‡ of +//│ | | | | | | | | 1 -> 2 +//│ | | | | | | | | _ -> +//│ | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | 4 +//│ | | | | | | | | _ -> +//│ | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | case x$Q_1*‡ of +//│ | | | | | | | | 1 -> 3 +//│ | | | | | | | | _ -> 4 +//│ | | | | | | | | S-(false) <== x is Q : if +//│ | | | | | | | | x*‡ is Q and ... +//│ | | | | | | | | x*‡ is Q and ... +//│ | | | | | | | | else 4 +//│ | | | | | | | | | CASE Cons x is Q +//│ | | | | | | | | | Case 1.2: x === x +//│ | | | | | | | | | Case 1.2.1: Q =:= (or <:<) Q +//│ | | | | | | | | | `x is Q` is not fallback +//│ | | | | | | | | | already removed = false +//│ | | | | | | | | | S-(false) <== x is Q : if +//│ | | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) +//│ | | | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 +//│ | | | | | | | | | x$Q_0*‡ is 1 and ... +//│ | | | | | | | | | | CASE Let ucs$args_x$Q +//│ | | | | | | | | | | CASE Let x$Q_0 +//│ | | | | | | | | | | CASE Let x$Q_1 +//│ | | | | | | | | | | CASE Cons x$Q_0 is 1 +//│ | | | | | | | | | | Case 2: x =/= x$Q_0 +//│ | | | | | | | | | | S-(false) <== x is Q : if x$Q_1*‡ is 1 then ... +//│ | | | | | | | | | | | CASE Cons x$Q_1 is 1 +//│ | | | | | | | | | | | Case 2: x =/= x$Q_1 +//│ | | | | | | | | | | | S-(false) <== x is Q : if then 2 +//│ | | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | | S-(false) ==> if then 2 +//│ | | | | | | | | | | | CASE Nil +//│ | | | | | | | | | | S-(false) ==> if x$Q_1*‡ is 1 then ... +//│ | | | | | | | | | | CASE Nil +//│ | | | | | | | | | S-(false) ==> if +//│ | | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) +//│ | | | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 +//│ | | | | | | | | | x$Q_0*‡ is 1 and ... +//│ | | | | | | | | | CASE Cons x is Q +//│ | | | | | | | | | Case 1.2: x === x +//│ | | | | | | | | | Case 1.2.1: Q =:= (or <:<) Q +//│ | | | | | | | | | `x is Q` is not fallback +//│ | | | | | | | | | already removed = false +//│ | | | | | | | | | S-(false) <== x is Q : if +//│ | | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) +//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 +//│ | | | | | | | | | x$Q_1*‡ is 1 then ... +//│ | | | | | | | | | | CASE Let ucs$args_x$Q +//│ | | | | | | | | | | CASE Let y +//│ | | | | | | | | | | CASE Let x$Q_1 +//│ | | | | | | | | | | CASE Cons x$Q_1 is 1 +//│ | | | | | | | | | | Case 2: x =/= x$Q_1 +//│ | | | | | | | | | | S-(false) <== x is Q : if then 3 +//│ | | | | | | | | | | | CASE Else +//│ | | | | | | | | | | S-(false) ==> if then 3 +//│ | | | | | | | | | | CASE Nil +//│ | | | | | | | | | S-(false) ==> if +//│ | | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) +//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 +//│ | | | | | | | | | x$Q_1*‡ is 1 then ... +//│ | | | | | | | | | CASE Else +//│ | | | | | | | | S-(false) ==> if then 4 +//│ | | | | | | | | normalizeToCaseBranches <== +//│ | | | | | | | | normalizeToCaseBranches ==> +//│ | | | | | | | normalizeToTerm ==> case x*‡ of +//│ | | | | | | | Q*◊ -> +//│ | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) +//│ | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 +//│ | | | | | | | case x$Q_0*‡ of +//│ | | | | | | | 0 -> +//│ | | | | | | | case x$Q_1*‡ of +//│ | | | | | | | 0 -> 1 +//│ | | | | | | | _ -> +//│ | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | case x$Q_1*‡ of +//│ | | | | | | | 1 -> 3 +//│ | | | | | | | _ -> 4 +//│ | | | | | | | _ -> +//│ | | | | | | | case x$Q_0*‡ of +//│ | | | | | | | 1 -> +//│ | | | | | | | case x$Q_1*‡ of +//│ | | | | | | | 1 -> 2 +//│ | | | | | | | _ -> +//│ | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | 4 +//│ | | | | | | | _ -> +//│ | | | | | | | let y*‡ = (ucs$args_x$Q).0 +//│ | | | | | | | case x$Q_1*‡ of +//│ | | | | | | | 1 -> 3 +//│ | | | | | | | _ -> 4 +//│ | | | | | | | _ -> 4 +//│ fun f: (Object & ~#Q | Q[Object, Object]) -> (1 | 2 | 3 | 4) diff --git a/shared/src/test/diff/ucs/Humiliation.mls b/shared/src/test/diff/ucs/Humiliation.mls index da7dfbbc..34fb50a5 100644 --- a/shared/src/test/diff/ucs/Humiliation.mls +++ b/shared/src/test/diff/ucs/Humiliation.mls @@ -12,11 +12,16 @@ if 1 is 1 then 1 else 0 fun test(x) = if x is 1 then 0 else 1 //│ fun test: Object -> (0 | 1) -// :w -// FIXME: It should report duplicated branches. +:w fun testF(x) = if x is Foo(a) then a Foo(a) then a +//│ ╔══[WARNING] found a duplicated case +//│ ║ l.18: Foo(a) then a +//│ ║ ^^^ +//│ ╟── the case is covered by pattern Foo +//│ ║ l.17: Foo(a) then a +//│ ╙── ^^^ //│ fun testF: forall 'a. Foo['a] -> 'a class Bar[Y, Z](y: Y, z: Z) @@ -50,24 +55,24 @@ fun foo(x) = if x is Pair(Z(), Z()) then "zeros" Pair(O(), O()) then "ones" //│ ╔══[ERROR] when `x` is `Pair`, and -//│ ║ l.50: Pair(Z(), Z()) then "zeros" +//│ ║ l.55: Pair(Z(), Z()) then "zeros" //│ ║ ^^^^ //│ ╟── the first argument of `Pair` is `Z`, -//│ ║ l.50: Pair(Z(), Z()) then "zeros" +//│ ║ l.55: Pair(Z(), Z()) then "zeros" //│ ║ ^ //│ ╟── the second argument of `Pair` has 1 missing case //│ ╟── it can be class `O` -//│ ║ l.51: Pair(O(), O()) then "ones" +//│ ║ l.56: Pair(O(), O()) then "ones" //│ ╙── ^ //│ ╔══[ERROR] when `x` is `Pair`, and -//│ ║ l.50: Pair(Z(), Z()) then "zeros" +//│ ║ l.55: Pair(Z(), Z()) then "zeros" //│ ║ ^^^^ //│ ╟── the first argument of `Pair` is `O`, -//│ ║ l.51: Pair(O(), O()) then "ones" +//│ ║ l.56: Pair(O(), O()) then "ones" //│ ║ ^ //│ ╟── the second argument of `Pair` has 1 missing case //│ ╟── it can be class `Z` -//│ ║ l.50: Pair(Z(), Z()) then "zeros" +//│ ║ l.55: Pair(Z(), Z()) then "zeros" //│ ╙── ^ //│ fun foo: Pair[O | Z, nothing] -> ("ones" | "zeros") @@ -77,11 +82,11 @@ fun foo(x) = if x is [Z(), Z()] then "zeros" [O(), O()] then "ones" //│ ╔══[ERROR] when `x$Tuple$2_0` is `O` -//│ ║ l.78: [O(), O()] then "ones" +//│ ║ l.83: [O(), O()] then "ones" //│ ║ ^ //│ ╟── `x$Tuple$2_1` has 1 missing case //│ ╟── it can be class `Z` -//│ ║ l.77: [Z(), Z()] then "zeros" +//│ ║ l.82: [Z(), Z()] then "zeros" //│ ╙── ^ //│ fun foo: forall 'a. {0: O | Z, 1: O & 'a} -> ("ones" | "zeros" | 'a) @@ -137,11 +142,11 @@ fun foo(x) = if x is Pair(O(), O()) then "ones" Pair(y, O()) then x //│ ╔══[ERROR] when `x` is `Pair` -//│ ║ l.136: Pair(Z(), Z()) then "zeros" +//│ ║ l.141: Pair(Z(), Z()) then "zeros" //│ ║ ^^^^ //│ ╟── the second argument of `Pair` has 1 missing case //│ ╟── it can be class `Z` -//│ ║ l.136: Pair(Z(), Z()) then "zeros" +//│ ║ l.141: Pair(Z(), Z()) then "zeros" //│ ╙── ^ //│ fun foo: forall 'A 'B. Pair['A, O & 'B] -> ("ones" | "zeros" | Pair['A, 'B] | 'A) //│ where diff --git a/shared/src/test/diff/ucs/InterleavedLet.mls b/shared/src/test/diff/ucs/InterleavedLet.mls index 22f6a2aa..1366c278 100644 --- a/shared/src/test/diff/ucs/InterleavedLet.mls +++ b/shared/src/test/diff/ucs/InterleavedLet.mls @@ -37,6 +37,12 @@ fun p(x, y) = x is Some and y is None then 0 y is Some and x is Some then 1 x is Some and y is Some then 0 +//│ ╔══[WARNING] found a duplicated case +//│ ║ l.39: x is Some and y is Some then 0 +//│ ║ ^^^^ +//│ ╟── the case is covered by pattern Some +//│ ║ l.38: y is Some and x is Some then 1 +//│ ╙── ^^^^ //│ ╔══[ERROR] `y` has 1 missing case //│ ║ l.38: y is Some and x is Some then 1 //│ ║ ^ @@ -73,10 +79,10 @@ fun f(a, y) = Right(x) then x + y else 0 //│ ╔══[ERROR] Type mismatch in operator application: -//│ ║ l.72: let y = v + 1 +//│ ║ l.78: let y = v + 1 //│ ║ ^^^^^ //│ ╟── reference of type `Right[?B]` is not an instance of type `Int` -//│ ║ l.72: let y = v + 1 +//│ ║ l.78: let y = v + 1 //│ ╙── ^ //│ fun f: forall 'a. (Object & ~#Some | Some[Int | Left['a] | Right[Int]], anything) -> (Int | 'a) @@ -87,9 +93,9 @@ fun q(a) = let y = a + 1 then y //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead -//│ ║ l.87: let y = a + 1 +//│ ║ l.93: let y = a + 1 //│ ║ ^^^^^ -//│ ║ l.88: then y +//│ ║ l.94: then y //│ ╙── ^^^^^^^^^^ //│ fun q: forall 'a. (Left['a] | Object & ~#Left) -> (() | 'a) @@ -106,11 +112,11 @@ fun w() = B then "B" else "?" //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.104: A then "A" +//│ ║ l.110: A then "A" //│ ║ ^ //│ ╙── reference of type `() -> A` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.106: B then "B" +//│ ║ l.112: B then "B" //│ ║ ^ //│ ╙── reference of type `() -> B` is not an instance of type `Bool` //│ fun w: () -> ("?" | "A" | "B") @@ -167,13 +173,13 @@ fun ip(y) = y == z * z then "bruh" else "rocks" //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.165: if q(y) and +//│ ║ l.171: if q(y) and //│ ║ ^^^^ //│ ╟── undefined literal of type `()` is not an instance of type `Bool` -//│ ║ l.88: then y +//│ ║ l.94: then y //│ ║ ^ //│ ╟── but it flows into application with expected type `Bool` -//│ ║ l.165: if q(y) and +//│ ║ l.171: if q(y) and //│ ╙── ^^^^ //│ fun ip: Int -> ("bruh" | "rocks") diff --git a/shared/src/test/diff/ucs/OverlappedBranches.mls b/shared/src/test/diff/ucs/OverlappedBranches.mls index 679ccddc..a6ed55f8 100644 --- a/shared/src/test/diff/ucs/OverlappedBranches.mls +++ b/shared/src/test/diff/ucs/OverlappedBranches.mls @@ -10,12 +10,28 @@ class Derived3() extends Derived2() //│ class Derived3() extends Base, Derived2 // The very basic case. -// :w -// Should warn about that the last two cases are unreachable. +// It should warn about that the last two cases are unreachable. +:w +:ducs:normalize.result fun f1(x) = if x is Base then "b" Derived1 then "d1" Derived2 then "d2" +//│ Normalized UCS term: +//│ case x*‡ of +//│ Base*◊ -> "b" +//│ ╔══[WARNING] found a duplicated case +//│ ║ l.18: Derived1 then "d1" +//│ ║ ^^^^^^^^ +//│ ╟── the case is covered by pattern Base +//│ ║ l.17: Base then "b" +//│ ╙── ^^^^ +//│ ╔══[WARNING] found a duplicated case +//│ ║ l.19: Derived2 then "d2" +//│ ║ ^^^^^^^^ +//│ ╟── the case is covered by pattern Base +//│ ║ l.17: Base then "b" +//│ ╙── ^^^^ //│ fun f1: Base -> "b" f1(Base()) From 022094728259d2ce66fdaec8e3d6f55440d0f8b6 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 8 May 2024 04:23:13 +0800 Subject: [PATCH 126/143] Improve warning messages --- .../mlscript/ucs/stages/Normalization.scala | 19 +- .../pretyper/ucs/coverage/CoveredCases.mls | 8 +- .../pretyper/ucs/coverage/DuplicatedCases.mls | 517 +----------------- shared/src/test/diff/ucs/Humiliation.mls | 2 +- shared/src/test/diff/ucs/InterleavedLet.mls | 2 +- .../src/test/diff/ucs/OverlappedBranches.mls | 10 +- 6 files changed, 41 insertions(+), 517 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 7efa0212..69fc7a00 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -290,10 +290,21 @@ trait Normalization { self: Desugarer with Traceable => println(s"already removed = $full") if (!split.isFallback && full && !head.continuation.isEmpty) { println(s"report warning") - raiseDesugaringWarning( - msg"found a duplicated case" -> thatPattern.toLoc, - msg"the case is covered by pattern ${pattern.toString}" -> pattern.toLoc, - ) + if (pattern === thatPattern) { + raiseDesugaringWarning( + msg"found a duplicated case" -> thatPattern.toLoc, + msg"there is an identical pattern ${pattern.toString}" -> pattern.toLoc, + ) + } else { + raiseDesugaringWarning( + msg"found a duplicated case" -> thatPattern.toLoc, + msg"the case is covered by pattern ${pattern.toString}" -> pattern.toLoc, + msg"due to the subtyping relation" -> (thatPattern match { + case Pattern.Class(_, symbol, _) => symbol.defn.getLoc + case _ => thatPattern.toLoc + }) + ) + } } specializeImpl(tail)( `-`(if (samePattern) continuation.isFull else full), diff --git a/shared/src/test/diff/pretyper/ucs/coverage/CoveredCases.mls b/shared/src/test/diff/pretyper/ucs/coverage/CoveredCases.mls index cfccce1f..6895da0d 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/CoveredCases.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/CoveredCases.mls @@ -15,7 +15,7 @@ fun f(x) = if //│ ╔══[WARNING] found a duplicated case //│ ║ l.14: x is A then 2 //│ ║ ^ -//│ ╟── the case is covered by pattern A +//│ ╟── there is an identical pattern A //│ ║ l.13: x is A then 1 //│ ╙── ^ //│ fun f: A -> 1 @@ -33,7 +33,10 @@ fun f(x) = if //│ ║ ^ //│ ╟── the case is covered by pattern A //│ ║ l.26: x is A then 1 -//│ ╙── ^ +//│ ║ ^ +//│ ╟── due to the subtyping relation +//│ ║ l.7: class B() extends A() +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ fun f: A -> 1 :ducs:normalize.result @@ -85,3 +88,4 @@ fun f(x) = if //│ _ -> 4 //│ fun f: Object -> (1 | 2 | 4) //│ TEST CASE FAILURE: There was an unexpected lack of warning + diff --git a/shared/src/test/diff/pretyper/ucs/coverage/DuplicatedCases.mls b/shared/src/test/diff/pretyper/ucs/coverage/DuplicatedCases.mls index 97509585..261ca4db 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/DuplicatedCases.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/DuplicatedCases.mls @@ -17,7 +17,7 @@ fun f(x) = if x is //│ ╔══[WARNING] found a duplicated case //│ ║ l.10: "a" then 3 //│ ║ ^^^ -//│ ╟── the case is covered by pattern "a" +//│ ╟── there is an identical pattern "a" //│ ║ l.8: "a" then 1 //│ ╙── ^^^ //│ fun f: ("a" | "b") -> (1 | 2) @@ -42,7 +42,7 @@ fun f(x) = if x is //│ ╔══[WARNING] found a duplicated case //│ ║ l.38: X then 2 //│ ║ ^ -//│ ╟── the case is covered by pattern X +//│ ╟── there is an identical pattern X //│ ║ l.37: X then 1 //│ ╙── ^ //│ fun f: X -> 1 @@ -62,7 +62,7 @@ fun f(x) = if x is //│ ╔══[WARNING] found a duplicated case //│ ║ l.55: X then 3 //│ ║ ^ -//│ ╟── the case is covered by pattern X +//│ ╟── there is an identical pattern X //│ ║ l.53: X then 1 //│ ╙── ^ //│ fun f: (X | Y) -> (1 | 2) @@ -199,514 +199,17 @@ fun f(x) = //│ 1 -> 2 //│ fun f: forall 'a. Q[Object & 'a, 1] -> (1 | 2 | 'a) -:ducs:normalize fun f(x) = if x is Q(0, 0) then 1 Q(1, 1) then 2 Q(y, 1) then 3 _ then 4 -//│ | | | | | | | STEP 2 -//│ | | | | | | | normalizeToTerm <== if -//│ | | | | | | | x*‡ is Q -//│ | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) -//│ | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 -//│ | | | | | | | x$Q_0*‡ is 0 x$Q_1*‡ is 0 then 1 -//│ | | | | | | | x*‡ is Q -//│ | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) -//│ | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 -//│ | | | | | | | x$Q_0*‡ is 1 x$Q_1*‡ is 1 then 2 -//│ | | | | | | | x*‡ is Q -//│ | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) -//│ | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 -//│ | | | | | | | x$Q_1*‡ is 1 then 3 -//│ | | | | | | | else 4 -//│ | | | | | | | | CONS: x is Q -//│ | | | | | | | | fill <== vars = {} -//│ | | | | | | | | | LHS: if -//│ | | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) -//│ | | | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 -//│ | | | | | | | | | x$Q_0*‡ is 0 x$Q_1*‡ is 0 then 1 -//│ | | | | | | | | | RHS: if -//│ | | | | | | | | | x*‡ is Q -//│ | | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) -//│ | | | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 -//│ | | | | | | | | | x$Q_0*‡ is 1 x$Q_1*‡ is 1 then 2 -//│ | | | | | | | | | x*‡ is Q -//│ | | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) -//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 -//│ | | | | | | | | | x$Q_1*‡ is 1 then 3 -//│ | | | | | | | | | else 4 -//│ | | | | | | | | | fill let binding ucs$args_x$Q -//│ | | | | | | | | | fill let binding x$Q_0 -//│ | | | | | | | | | fill let binding x$Q_1 -//│ | | | | | | | | fill ==> if -//│ | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) -//│ | | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 -//│ | | | | | | | | x$Q_0*‡ is 0 x$Q_1*‡ is 0 then 1 -//│ | | | | | | | | ?x*‡ is Q ?x$Q_0*‡ is 1 ?x$Q_1*‡ is 1 then 2 -//│ | | | | | | | | ?x*‡ is Q -//│ | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | ?x$Q_1*‡ is 1 then 3 -//│ | | | | | | | | else 4 -//│ | | | | | | | | S+ <== x is Q : if -//│ | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) -//│ | | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 -//│ | | | | | | | | x$Q_0*‡ is 0 and ... -//│ | | | | | | | | ?x*‡ is Q and ... -//│ | | | | | | | | ?x*‡ is Q and ... -//│ | | | | | | | | else 4 -//│ | | | | | | | | | CASE Let ucs$args_x$Q -//│ | | | | | | | | | CASE Let x$Q_0 -//│ | | | | | | | | | CASE Let x$Q_1 -//│ | | | | | | | | | CASE Cons x$Q_0 is 0 -//│ | | | | | | | | | Case 2: x =/= x$Q_0 -//│ | | | | | | | | | S+ <== x is Q : if x$Q_1*‡ is 0 then ... -//│ | | | | | | | | | | CASE Cons x$Q_1 is 0 -//│ | | | | | | | | | | Case 2: x =/= x$Q_1 -//│ | | | | | | | | | | S+ <== x is Q : if then 1 -//│ | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | S+ ==> if then 1 -//│ | | | | | | | | | | CASE Nil -//│ | | | | | | | | | S+ ==> if x$Q_1*‡ is 0 then ... -//│ | | | | | | | | | CASE Cons x is Q -//│ | | | | | | | | | Case 1.1: x === x -//│ | | | | | | | | | Case 1.1.1: Q =:= Q -//│ | | | | | | | | | S+ <== x is Q : if ?x$Q_0*‡ is 1 and ... -//│ | | | | | | | | | | CASE Cons x$Q_0 is 1 -//│ | | | | | | | | | | Case 2: x =/= x$Q_0 -//│ | | | | | | | | | | S+ <== x is Q : if ?x$Q_1*‡ is 1 then ... -//│ | | | | | | | | | | | CASE Cons x$Q_1 is 1 -//│ | | | | | | | | | | | Case 2: x =/= x$Q_1 -//│ | | | | | | | | | | | S+ <== x is Q : if then 2 -//│ | | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | | S+ ==> if then 2 -//│ | | | | | | | | | | | CASE Nil -//│ | | | | | | | | | | S+ ==> if ?x$Q_1*‡ is 1 then ... -//│ | | | | | | | | | | CASE Nil -//│ | | | | | | | | | S+ ==> if ?x$Q_0*‡ is 1 and ... -//│ | | | | | | | | | CASE Cons x is Q -//│ | | | | | | | | | Case 1.1: x === x -//│ | | | | | | | | | Case 1.1.1: Q =:= Q -//│ | | | | | | | | | S+ <== x is Q : if -//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | ?x$Q_1*‡ is 1 then ... -//│ | | | | | | | | | | CASE Let y -//│ | | | | | | | | | | CASE Cons x$Q_1 is 1 -//│ | | | | | | | | | | Case 2: x =/= x$Q_1 -//│ | | | | | | | | | | S+ <== x is Q : if then 3 -//│ | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | S+ ==> if then 3 -//│ | | | | | | | | | | CASE Nil -//│ | | | | | | | | | S+ ==> if -//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | ?x$Q_1*‡ is 1 then ... -//│ | | | | | | | | | CASE Else -//│ | | | | | | | | S+ ==> if -//│ | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) -//│ | | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 -//│ | | | | | | | | x$Q_0*‡ is 0 and ... -//│ | | | | | | | | x$Q_0*‡ is 1 and ... -//│ | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | x$Q_1*‡ is 1 then ... -//│ | | | | | | | | else 4 -//│ | | | | | | | | normalizeToTerm <== if -//│ | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) -//│ | | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 -//│ | | | | | | | | x$Q_0*‡ is 0 x$Q_1*‡ is 0 then 1 -//│ | | | | | | | | x$Q_0*‡ is 1 ?x$Q_1*‡ is 1 then 2 -//│ | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | x$Q_1*‡ is 1 then 3 -//│ | | | | | | | | else 4 -//│ | | | | | | | | | LET: generated ucs$args_x$Q -//│ | | | | | | | | | LET: x$Q_0 -//│ | | | | | | | | | LET: x$Q_1 -//│ | | | | | | | | | CONS: x$Q_0 is 0 -//│ | | | | | | | | | fill <== vars = {ucs$args_x$Q} -//│ | | | | | | | | | | LHS: if x$Q_1*‡ is 0 then 1 -//│ | | | | | | | | | | RHS: if -//│ | | | | | | | | | | x$Q_0*‡ is 1 ?x$Q_1*‡ is 1 then 2 -//│ | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | | x$Q_1*‡ is 1 then 3 -//│ | | | | | | | | | | else 4 -//│ | | | | | | | | | fill ==> if -//│ | | | | | | | | | x$Q_1*‡ is 0 then 1 -//│ | | | | | | | | | ?x$Q_0*‡ is 1 ?x$Q_1*‡ is 1 then 2 -//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | ?x$Q_1*‡ is 1 then 3 -//│ | | | | | | | | | else 4 -//│ | | | | | | | | | S+ <== x$Q_0 is 0 : if -//│ | | | | | | | | | x$Q_1*‡ is 0 then ... -//│ | | | | | | | | | ?x$Q_0*‡ is 1 and ... -//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | ?x$Q_1*‡ is 1 then ... -//│ | | | | | | | | | else 4 -//│ | | | | | | | | | | CASE Cons x$Q_1 is 0 -//│ | | | | | | | | | | Case 2: x$Q_0 =/= x$Q_1 -//│ | | | | | | | | | | S+ <== x$Q_0 is 0 : if then 1 -//│ | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | S+ ==> if then 1 -//│ | | | | | | | | | | CASE Cons x$Q_0 is 1 -//│ | | | | | | | | | | Case 1.1: x$Q_0 === x$Q_0 -//│ | | | | | | | | | | Case 1.1.3: 0 is unrelated with 1 -//│ | | | | | | | | | | CASE Let y -//│ | | | | | | | | | | CASE Cons x$Q_1 is 1 -//│ | | | | | | | | | | Case 2: x$Q_0 =/= x$Q_1 -//│ | | | | | | | | | | S+ <== x$Q_0 is 0 : if then 3 -//│ | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | S+ ==> if then 3 -//│ | | | | | | | | | | CASE Else -//│ | | | | | | | | | S+ ==> if -//│ | | | | | | | | | x$Q_1*‡ is 0 then ... -//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | ?x$Q_1*‡ is 1 then ... -//│ | | | | | | | | | else 4 -//│ | | | | | | | | | normalizeToTerm <== if -//│ | | | | | | | | | x$Q_1*‡ is 0 then 1 -//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | ?x$Q_1*‡ is 1 then 3 -//│ | | | | | | | | | else 4 -//│ | | | | | | | | | | CONS: x$Q_1 is 0 -//│ | | | | | | | | | | fill <== vars = {ucs$args_x$Q} -//│ | | | | | | | | | | | LHS: if then 1 -//│ | | | | | | | | | | | RHS: if -//│ | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | | | ?x$Q_1*‡ is 1 then 3 -//│ | | | | | | | | | | | else 4 -//│ | | | | | | | | | | fill ==> if then 1 -//│ | | | | | | | | | | S+ <== x$Q_1 is 0 : if then 1 -//│ | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | S+ ==> if then 1 -//│ | | | | | | | | | | normalizeToTerm <== if then 1 -//│ | | | | | | | | | | | DFLT: 1 -//│ | | | | | | | | | | normalizeToTerm ==> 1 -//│ | | | | | | | | | | S-(true) <== x$Q_1 is 0 : if -//│ | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | | ?x$Q_1*‡ is 1 then ... -//│ | | | | | | | | | | else 4 -//│ | | | | | | | | | | | CASE Let y -//│ | | | | | | | | | | | CASE Cons x$Q_1 is 1 -//│ | | | | | | | | | | | Case 1.2: x$Q_1 === x$Q_1 -//│ | | | | | | | | | | | Case 1.2.2: 0 are unrelated with 1 -//│ | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | S-(true) ==> if -//│ | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | | x$Q_1*‡ is 1 then ... -//│ | | | | | | | | | | else 4 -//│ | | | | | | | | | | normalizeToCaseBranches <== -//│ | | | | | | | | | | | normalizeToCaseBranches <== -//│ | | | | | | | | | | | | normalizeToTerm <== if -//│ | | | | | | | | | | | | x$Q_1*‡ is 1 then 3 -//│ | | | | | | | | | | | | else 4 -//│ | | | | | | | | | | | | | CONS: x$Q_1 is 1 -//│ | | | | | | | | | | | | | fill <== vars = {ucs$args_x$Q} -//│ | | | | | | | | | | | | | | LHS: if then 3 -//│ | | | | | | | | | | | | | | RHS: if then 4 -//│ | | | | | | | | | | | | | fill ==> if then 3 -//│ | | | | | | | | | | | | | S+ <== x$Q_1 is 1 : if then 3 -//│ | | | | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | | | | S+ ==> if then 3 -//│ | | | | | | | | | | | | | normalizeToTerm <== if then 3 -//│ | | | | | | | | | | | | | | DFLT: 3 -//│ | | | | | | | | | | | | | normalizeToTerm ==> 3 -//│ | | | | | | | | | | | | | S-(true) <== x$Q_1 is 1 : if then 4 -//│ | | | | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | | | | S-(true) ==> if then 4 -//│ | | | | | | | | | | | | | normalizeToCaseBranches <== -//│ | | | | | | | | | | | | | normalizeToCaseBranches ==> -//│ | | | | | | | | | | | | normalizeToTerm ==> case x$Q_1*‡ of -//│ | | | | | | | | | | | | 1 -> 3 -//│ | | | | | | | | | | | | _ -> 4 -//│ | | | | | | | | | | | normalizeToCaseBranches ==> -//│ | | | | | | | | | | normalizeToCaseBranches ==> -//│ | | | | | | | | | normalizeToTerm ==> case x$Q_1*‡ of -//│ | | | | | | | | | 0 -> 1 -//│ | | | | | | | | | _ -> -//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | case x$Q_1*‡ of -//│ | | | | | | | | | 1 -> 3 -//│ | | | | | | | | | _ -> 4 -//│ | | | | | | | | | S-(false) <== x$Q_0 is 0 : if -//│ | | | | | | | | | x$Q_0*‡ is 1 and ... -//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | x$Q_1*‡ is 1 then ... -//│ | | | | | | | | | else 4 -//│ | | | | | | | | | | CASE Cons x$Q_0 is 1 -//│ | | | | | | | | | | Case 1.2: x$Q_0 === x$Q_0 -//│ | | | | | | | | | | Case 1.2.2: 0 are unrelated with 1 -//│ | | | | | | | | | | CASE Let y -//│ | | | | | | | | | | CASE Cons x$Q_1 is 1 -//│ | | | | | | | | | | Case 2: x$Q_0 =/= x$Q_1 -//│ | | | | | | | | | | S-(false) <== x$Q_0 is 0 : if then 3 -//│ | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | S-(false) ==> if then 3 -//│ | | | | | | | | | | CASE Else -//│ | | | | | | | | | S-(false) ==> if -//│ | | | | | | | | | x$Q_0*‡ is 1 and ... -//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | x$Q_1*‡ is 1 then ... -//│ | | | | | | | | | else 4 -//│ | | | | | | | | | normalizeToCaseBranches <== -//│ | | | | | | | | | | normalizeToTerm <== if -//│ | | | | | | | | | | x$Q_0*‡ is 1 x$Q_1*‡ is 1 then 2 -//│ | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | | x$Q_1*‡ is 1 then 3 -//│ | | | | | | | | | | else 4 -//│ | | | | | | | | | | | CONS: x$Q_0 is 1 -//│ | | | | | | | | | | | fill <== vars = {ucs$args_x$Q} -//│ | | | | | | | | | | | | LHS: if x$Q_1*‡ is 1 then 2 -//│ | | | | | | | | | | | | RHS: if -//│ | | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | | | | x$Q_1*‡ is 1 then 3 -//│ | | | | | | | | | | | | else 4 -//│ | | | | | | | | | | | fill ==> if -//│ | | | | | | | | | | | x$Q_1*‡ is 1 then 2 -//│ | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | | | ?x$Q_1*‡ is 1 then 3 -//│ | | | | | | | | | | | else 4 -//│ | | | | | | | | | | | S+ <== x$Q_0 is 1 : if -//│ | | | | | | | | | | | x$Q_1*‡ is 1 then ... -//│ | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | | | ?x$Q_1*‡ is 1 then ... -//│ | | | | | | | | | | | else 4 -//│ | | | | | | | | | | | | CASE Cons x$Q_1 is 1 -//│ | | | | | | | | | | | | Case 2: x$Q_0 =/= x$Q_1 -//│ | | | | | | | | | | | | S+ <== x$Q_0 is 1 : if then 2 -//│ | | | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | | | S+ ==> if then 2 -//│ | | | | | | | | | | | | CASE Let y -//│ | | | | | | | | | | | | CASE Cons x$Q_1 is 1 -//│ | | | | | | | | | | | | Case 2: x$Q_0 =/= x$Q_1 -//│ | | | | | | | | | | | | S+ <== x$Q_0 is 1 : if then 3 -//│ | | | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | | | S+ ==> if then 3 -//│ | | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | | S+ ==> if -//│ | | | | | | | | | | | x$Q_1*‡ is 1 then ... -//│ | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | | | ?x$Q_1*‡ is 1 then ... -//│ | | | | | | | | | | | else 4 -//│ | | | | | | | | | | | normalizeToTerm <== if -//│ | | | | | | | | | | | x$Q_1*‡ is 1 then 2 -//│ | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | | | ?x$Q_1*‡ is 1 then 3 -//│ | | | | | | | | | | | else 4 -//│ | | | | | | | | | | | | CONS: x$Q_1 is 1 -//│ | | | | | | | | | | | | fill <== vars = {ucs$args_x$Q} -//│ | | | | | | | | | | | | | LHS: if then 2 -//│ | | | | | | | | | | | | | RHS: if -//│ | | | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | | | | | ?x$Q_1*‡ is 1 then 3 -//│ | | | | | | | | | | | | | else 4 -//│ | | | | | | | | | | | | fill ==> if then 2 -//│ | | | | | | | | | | | | S+ <== x$Q_1 is 1 : if then 2 -//│ | | | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | | | S+ ==> if then 2 -//│ | | | | | | | | | | | | normalizeToTerm <== if then 2 -//│ | | | | | | | | | | | | | DFLT: 2 -//│ | | | | | | | | | | | | normalizeToTerm ==> 2 -//│ | | | | | | | | | | | | S-(true) <== x$Q_1 is 1 : if -//│ | | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | | | | ?x$Q_1*‡ is 1 then ... -//│ | | | | | | | | | | | | else 4 -//│ | | | | | | | | | | | | | CASE Let y -//│ | | | | | | | | | | | | | CASE Cons x$Q_1 is 1 -//│ | | | | | | | | | | | | | Case 1.2: x$Q_1 === x$Q_1 -//│ | | | | | | | | | | | | | Case 1.2.1: 1 =:= (or <:<) 1 -//│ | | | | | | | | | | | | | `x$Q_1 is 1` is fallback -//│ | | | | | | | | | | | | | already removed = true -//│ | | | | | | | | | | | | | S-(true) <== x$Q_1 is 1 : if then 3 -//│ | | | | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | | | | S-(true) ==> if then 3 -//│ | | | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | | | S-(true) ==> if -//│ | | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | | | | else 4 -//│ | | | | | | | | | | | | normalizeToCaseBranches <== -//│ | | | | | | | | | | | | | normalizeToCaseBranches <== -//│ | | | | | | | | | | | | | normalizeToCaseBranches ==> -//│ | | | | | | | | | | | | normalizeToCaseBranches ==> -//│ | | | | | | | | | | | normalizeToTerm ==> case x$Q_1*‡ of -//│ | | | | | | | | | | | 1 -> 2 -//│ | | | | | | | | | | | _ -> -//│ | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | | | 4 -//│ | | | | | | | | | | | S-(false) <== x$Q_0 is 1 : if -//│ | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | | | x$Q_1*‡ is 1 then ... -//│ | | | | | | | | | | | else 4 -//│ | | | | | | | | | | | | CASE Let y -//│ | | | | | | | | | | | | CASE Cons x$Q_1 is 1 -//│ | | | | | | | | | | | | Case 2: x$Q_0 =/= x$Q_1 -//│ | | | | | | | | | | | | S-(false) <== x$Q_0 is 1 : if then 3 -//│ | | | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | | | S-(false) ==> if then 3 -//│ | | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | | S-(false) ==> if -//│ | | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | | | ?x$Q_1*‡ is 1 then ... -//│ | | | | | | | | | | | else 4 -//│ | | | | | | | | | | | normalizeToCaseBranches <== -//│ | | | | | | | | | | | | normalizeToCaseBranches <== -//│ | | | | | | | | | | | | | normalizeToTerm <== if -//│ | | | | | | | | | | | | | x$Q_1*‡ is 1 then 3 -//│ | | | | | | | | | | | | | else 4 -//│ | | | | | | | | | | | | | | CONS: x$Q_1 is 1 -//│ | | | | | | | | | | | | | | fill <== vars = {ucs$args_x$Q} -//│ | | | | | | | | | | | | | | | LHS: if then 3 -//│ | | | | | | | | | | | | | | | RHS: if then 4 -//│ | | | | | | | | | | | | | | fill ==> if then 3 -//│ | | | | | | | | | | | | | | S+ <== x$Q_1 is 1 : if then 3 -//│ | | | | | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | | | | | S+ ==> if then 3 -//│ | | | | | | | | | | | | | | normalizeToTerm <== if then 3 -//│ | | | | | | | | | | | | | | | DFLT: 3 -//│ | | | | | | | | | | | | | | normalizeToTerm ==> 3 -//│ | | | | | | | | | | | | | | S-(true) <== x$Q_1 is 1 : if then 4 -//│ | | | | | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | | | | | S-(true) ==> if then 4 -//│ | | | | | | | | | | | | | | normalizeToCaseBranches <== -//│ | | | | | | | | | | | | | | normalizeToCaseBranches ==> -//│ | | | | | | | | | | | | | normalizeToTerm ==> case x$Q_1*‡ of -//│ | | | | | | | | | | | | | 1 -> 3 -//│ | | | | | | | | | | | | | _ -> 4 -//│ | | | | | | | | | | | | normalizeToCaseBranches ==> -//│ | | | | | | | | | | | normalizeToCaseBranches ==> -//│ | | | | | | | | | | normalizeToTerm ==> case x$Q_0*‡ of -//│ | | | | | | | | | | 1 -> -//│ | | | | | | | | | | case x$Q_1*‡ of -//│ | | | | | | | | | | 1 -> 2 -//│ | | | | | | | | | | _ -> -//│ | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | | 4 -//│ | | | | | | | | | | _ -> -//│ | | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | | case x$Q_1*‡ of -//│ | | | | | | | | | | 1 -> 3 -//│ | | | | | | | | | | _ -> 4 -//│ | | | | | | | | | normalizeToCaseBranches ==> -//│ | | | | | | | | normalizeToTerm ==> let ucs$args_x$Q*† = (Q).unapply(x,) -//│ | | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 -//│ | | | | | | | | case x$Q_0*‡ of -//│ | | | | | | | | 0 -> -//│ | | | | | | | | case x$Q_1*‡ of -//│ | | | | | | | | 0 -> 1 -//│ | | | | | | | | _ -> -//│ | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | case x$Q_1*‡ of -//│ | | | | | | | | 1 -> 3 -//│ | | | | | | | | _ -> 4 -//│ | | | | | | | | _ -> -//│ | | | | | | | | case x$Q_0*‡ of -//│ | | | | | | | | 1 -> -//│ | | | | | | | | case x$Q_1*‡ of -//│ | | | | | | | | 1 -> 2 -//│ | | | | | | | | _ -> -//│ | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | 4 -//│ | | | | | | | | _ -> -//│ | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | case x$Q_1*‡ of -//│ | | | | | | | | 1 -> 3 -//│ | | | | | | | | _ -> 4 -//│ | | | | | | | | S-(false) <== x is Q : if -//│ | | | | | | | | x*‡ is Q and ... -//│ | | | | | | | | x*‡ is Q and ... -//│ | | | | | | | | else 4 -//│ | | | | | | | | | CASE Cons x is Q -//│ | | | | | | | | | Case 1.2: x === x -//│ | | | | | | | | | Case 1.2.1: Q =:= (or <:<) Q -//│ | | | | | | | | | `x is Q` is not fallback -//│ | | | | | | | | | already removed = false -//│ | | | | | | | | | S-(false) <== x is Q : if -//│ | | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) -//│ | | | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 -//│ | | | | | | | | | x$Q_0*‡ is 1 and ... -//│ | | | | | | | | | | CASE Let ucs$args_x$Q -//│ | | | | | | | | | | CASE Let x$Q_0 -//│ | | | | | | | | | | CASE Let x$Q_1 -//│ | | | | | | | | | | CASE Cons x$Q_0 is 1 -//│ | | | | | | | | | | Case 2: x =/= x$Q_0 -//│ | | | | | | | | | | S-(false) <== x is Q : if x$Q_1*‡ is 1 then ... -//│ | | | | | | | | | | | CASE Cons x$Q_1 is 1 -//│ | | | | | | | | | | | Case 2: x =/= x$Q_1 -//│ | | | | | | | | | | | S-(false) <== x is Q : if then 2 -//│ | | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | | S-(false) ==> if then 2 -//│ | | | | | | | | | | | CASE Nil -//│ | | | | | | | | | | S-(false) ==> if x$Q_1*‡ is 1 then ... -//│ | | | | | | | | | | CASE Nil -//│ | | | | | | | | | S-(false) ==> if -//│ | | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) -//│ | | | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 -//│ | | | | | | | | | x$Q_0*‡ is 1 and ... -//│ | | | | | | | | | CASE Cons x is Q -//│ | | | | | | | | | Case 1.2: x === x -//│ | | | | | | | | | Case 1.2.1: Q =:= (or <:<) Q -//│ | | | | | | | | | `x is Q` is not fallback -//│ | | | | | | | | | already removed = false -//│ | | | | | | | | | S-(false) <== x is Q : if -//│ | | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) -//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 -//│ | | | | | | | | | x$Q_1*‡ is 1 then ... -//│ | | | | | | | | | | CASE Let ucs$args_x$Q -//│ | | | | | | | | | | CASE Let y -//│ | | | | | | | | | | CASE Let x$Q_1 -//│ | | | | | | | | | | CASE Cons x$Q_1 is 1 -//│ | | | | | | | | | | Case 2: x =/= x$Q_1 -//│ | | | | | | | | | | S-(false) <== x is Q : if then 3 -//│ | | | | | | | | | | | CASE Else -//│ | | | | | | | | | | S-(false) ==> if then 3 -//│ | | | | | | | | | | CASE Nil -//│ | | | | | | | | | S-(false) ==> if -//│ | | | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) -//│ | | | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 -//│ | | | | | | | | | x$Q_1*‡ is 1 then ... -//│ | | | | | | | | | CASE Else -//│ | | | | | | | | S-(false) ==> if then 4 -//│ | | | | | | | | normalizeToCaseBranches <== -//│ | | | | | | | | normalizeToCaseBranches ==> -//│ | | | | | | | normalizeToTerm ==> case x*‡ of -//│ | | | | | | | Q*◊ -> -//│ | | | | | | | let ucs$args_x$Q*† = (Q).unapply(x,) -//│ | | | | | | | let x$Q_0*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | let x$Q_1*‡ = (ucs$args_x$Q).1 -//│ | | | | | | | case x$Q_0*‡ of -//│ | | | | | | | 0 -> -//│ | | | | | | | case x$Q_1*‡ of -//│ | | | | | | | 0 -> 1 -//│ | | | | | | | _ -> -//│ | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | case x$Q_1*‡ of -//│ | | | | | | | 1 -> 3 -//│ | | | | | | | _ -> 4 -//│ | | | | | | | _ -> -//│ | | | | | | | case x$Q_0*‡ of -//│ | | | | | | | 1 -> -//│ | | | | | | | case x$Q_1*‡ of -//│ | | | | | | | 1 -> 2 -//│ | | | | | | | _ -> -//│ | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | 4 -//│ | | | | | | | _ -> -//│ | | | | | | | let y*‡ = (ucs$args_x$Q).0 -//│ | | | | | | | case x$Q_1*‡ of -//│ | | | | | | | 1 -> 3 -//│ | | | | | | | _ -> 4 -//│ | | | | | | | _ -> 4 //│ fun f: (Object & ~#Q | Q[Object, Object]) -> (1 | 2 | 3 | 4) + +fun f(x) = + if x is + P(P(P(1))) then 1 + P(P(1)) then 2 + P(1) then 3 +//│ fun f: P[1 | P[1 | P[1]]] -> (1 | 2 | 3) diff --git a/shared/src/test/diff/ucs/Humiliation.mls b/shared/src/test/diff/ucs/Humiliation.mls index 34fb50a5..37850b4e 100644 --- a/shared/src/test/diff/ucs/Humiliation.mls +++ b/shared/src/test/diff/ucs/Humiliation.mls @@ -19,7 +19,7 @@ fun testF(x) = if x is //│ ╔══[WARNING] found a duplicated case //│ ║ l.18: Foo(a) then a //│ ║ ^^^ -//│ ╟── the case is covered by pattern Foo +//│ ╟── there is an identical pattern Foo //│ ║ l.17: Foo(a) then a //│ ╙── ^^^ //│ fun testF: forall 'a. Foo['a] -> 'a diff --git a/shared/src/test/diff/ucs/InterleavedLet.mls b/shared/src/test/diff/ucs/InterleavedLet.mls index 1366c278..a39cc5f1 100644 --- a/shared/src/test/diff/ucs/InterleavedLet.mls +++ b/shared/src/test/diff/ucs/InterleavedLet.mls @@ -40,7 +40,7 @@ fun p(x, y) = //│ ╔══[WARNING] found a duplicated case //│ ║ l.39: x is Some and y is Some then 0 //│ ║ ^^^^ -//│ ╟── the case is covered by pattern Some +//│ ╟── there is an identical pattern Some //│ ║ l.38: y is Some and x is Some then 1 //│ ╙── ^^^^ //│ ╔══[ERROR] `y` has 1 missing case diff --git a/shared/src/test/diff/ucs/OverlappedBranches.mls b/shared/src/test/diff/ucs/OverlappedBranches.mls index a6ed55f8..8d06b583 100644 --- a/shared/src/test/diff/ucs/OverlappedBranches.mls +++ b/shared/src/test/diff/ucs/OverlappedBranches.mls @@ -25,13 +25,19 @@ fun f1(x) = if x is //│ ║ ^^^^^^^^ //│ ╟── the case is covered by pattern Base //│ ║ l.17: Base then "b" -//│ ╙── ^^^^ +//│ ║ ^^^^ +//│ ╟── due to the subtyping relation +//│ ║ l.4: class Derived1() extends Base() +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[WARNING] found a duplicated case //│ ║ l.19: Derived2 then "d2" //│ ║ ^^^^^^^^ //│ ╟── the case is covered by pattern Base //│ ║ l.17: Base then "b" -//│ ╙── ^^^^ +//│ ║ ^^^^ +//│ ╟── due to the subtyping relation +//│ ║ l.5: class Derived2() extends Base() +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ fun f1: Base -> "b" f1(Base()) From 3fb6755c134ab960fa7ddbf94b4bf19f5ac2702d Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 8 May 2024 04:24:13 +0800 Subject: [PATCH 127/143] Mark the cases that are powerless to deal with --- .../src/test/diff/pretyper/ucs/coverage/CoveredCases.mls | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shared/src/test/diff/pretyper/ucs/coverage/CoveredCases.mls b/shared/src/test/diff/pretyper/ucs/coverage/CoveredCases.mls index 6895da0d..50d8b312 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/CoveredCases.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/CoveredCases.mls @@ -50,7 +50,8 @@ fun f(x) = if //│ B*◊ -> 1 //│ fun f: B -> 1 -:w +// :w +// FIXME :ducs:normalize.result fun f(x) = if x is A and @@ -62,12 +63,12 @@ fun f(x) = if //│ case x*‡ of //│ B*◊ -> 1 //│ fun f: B -> 1 -//│ TEST CASE FAILURE: There was an unexpected lack of warning fun p(x) = true: Bool //│ fun p: anything -> Bool -:w +// :w +// FIXME :ducs:normalize.result fun f(x) = if x is A and @@ -87,5 +88,4 @@ fun f(x) = if //│ _ -> 4 //│ _ -> 4 //│ fun f: Object -> (1 | 2 | 4) -//│ TEST CASE FAILURE: There was an unexpected lack of warning From af6966ec718f6a018cad598182fe245909e23716 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Wed, 8 May 2024 04:29:33 +0800 Subject: [PATCH 128/143] Remove the cyclic node warning and mark the case --- .../test/diff/Defunctionalize/OldMonoList.mls | 19 ++----------------- .../main/scala/mlscript/ucs/Desugarer.scala | 7 +------ 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/compiler/shared/test/diff/Defunctionalize/OldMonoList.mls b/compiler/shared/test/diff/Defunctionalize/OldMonoList.mls index 0f87a857..93b44c0b 100644 --- a/compiler/shared/test/diff/Defunctionalize/OldMonoList.mls +++ b/compiler/shared/test/diff/Defunctionalize/OldMonoList.mls @@ -1,6 +1,7 @@ :NewDefs :AllowRuntimeErrors +// FIXME :mono class List(e: Int, tail: List | Nil) { fun map: (Int -> Int) -> List @@ -42,23 +43,7 @@ fun add2(x) = x+2 //│ fun apply$Lambda1$2$3 = (this, x,) => +(x, 1,) //│ Code(List(main$$5())) //│ } -//│ ╔══[ERROR] the `if` expression has already been desugared -//│ ║ l.7: fun map(f)= new List(f(e), tail.map(f)) -//│ ║ ^ -//│ ╙── please make sure that the objects are copied -//│ class Lambda1$3$4() -//│ class Nil$2() -//│ class List$1(e: Int, tail: List$1 | Nil$2) -//│ class Lambda1$2$3() -//│ fun map$List$1: (Object, Object) -> List$1 -//│ fun add2$1: Int -> Int -//│ fun main$$5: () -> List$1 -//│ fun apply$Lambda1$3$4: (anything, Int) -> Int -//│ fun map$Nil$2: forall 'a. ('a & (List$1 | Nil$2), anything) -> (Nil$2 | 'a) -//│ fun apply$Lambda1$2$3: (anything, Int) -> Int -//│ List$1 -//│ res -//│ = List$1 {} +//│ /!!!\ Uncaught error: java.lang.Exception: Internal Error: the `if` expression has already been desugared, please make sure that the objects are copied :mono class List(e: Int, tail: List | Nil) { diff --git a/shared/src/main/scala/mlscript/ucs/Desugarer.scala b/shared/src/main/scala/mlscript/ucs/Desugarer.scala index 461df9ae..a3b70eb2 100644 --- a/shared/src/main/scala/mlscript/ucs/Desugarer.scala +++ b/shared/src/main/scala/mlscript/ucs/Desugarer.scala @@ -197,12 +197,7 @@ trait Desugarer extends Transformation */ protected def traverseIf(`if`: If)(implicit scope: Scope): Unit = { `if`.desugaredTerm match { - case S(desugaredTerm) => - raiseDesugaringError( - msg"the `if` expression has already been desugared" -> `if`.getLoc, - msg"please make sure that the objects are copied" -> N, - ) - return + case S(desugaredTerm) => lastWords("the `if` expression has already been desugared, please make sure that the objects are copied") case N => () } implicit val context: Context = new Context(`if`) From e7580b1956a938cf947f9f1ff29f759c3b4aacee Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Thu, 9 May 2024 04:08:57 +0800 Subject: [PATCH 129/143] Improve messages by applying suggestions Co-authored-by: Lionel Parreaux --- shared/src/main/scala/mlscript/ucs/stages/Normalization.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 69fc7a00..85712c64 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -269,7 +269,7 @@ trait Normalization { self: Desugarer with Traceable => continuation :++ specializeImpl(tail) } else { raiseDesugaringWarning( - msg"possibly conflicted patterns" -> thatPattern.toLoc, + msg"possibly conflicting patterns" -> thatPattern.toLoc, msg"the scrutinee was matched against ${pattern.toString}" -> pattern.toLoc, msg"which is unrelated with ${thatPattern.toString}" -> thatPattern.toLoc) specializeImpl(tail) @@ -310,7 +310,7 @@ trait Normalization { self: Desugarer with Traceable => `-`(if (samePattern) continuation.isFull else full), scrutineeVar, scrutinee, pattern, context) } else { - println(s"Case 1.2.2: $pattern are unrelated with $thatPattern") + println(s"Case 1.2.2: $pattern are unrelated to $thatPattern") split.copy(tail = specializeImpl(tail)) } } else { From cc2ea600a1149a9a63e6e0392ab8c677cc49427f Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Thu, 9 May 2024 04:16:17 +0800 Subject: [PATCH 130/143] Commit changes to the test output --- .../pretyper/ucs/coverage/ConflictedPatterns.mls | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/shared/src/test/diff/pretyper/ucs/coverage/ConflictedPatterns.mls b/shared/src/test/diff/pretyper/ucs/coverage/ConflictedPatterns.mls index 727a3ab2..77194e9a 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/ConflictedPatterns.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/ConflictedPatterns.mls @@ -24,7 +24,7 @@ fun f(x) = if //│ case x*‡ of //│ X*◊ -> 2 //│ _ -> 2 -//│ ╔══[WARNING] possibly conflicted patterns +//│ ╔══[WARNING] possibly conflicting patterns //│ ║ l.21: x is X and x is Y then 1 //│ ║ ^ //│ ╟── the scrutinee was matched against X @@ -47,7 +47,7 @@ fun f(x) = if //│ case x*‡ of //│ X*◊ -> 3 //│ _ -> 3 -//│ ╔══[WARNING] possibly conflicted patterns +//│ ╔══[WARNING] possibly conflicting patterns //│ ║ l.43: x is Y then 1 //│ ║ ^ //│ ╟── the scrutinee was matched against X @@ -56,7 +56,7 @@ fun f(x) = if //│ ╟── which is unrelated with Y //│ ║ l.43: x is Y then 1 //│ ╙── ^ -//│ ╔══[WARNING] possibly conflicted patterns +//│ ╔══[WARNING] possibly conflicting patterns //│ ║ l.44: x is Y then 2 //│ ║ ^ //│ ╟── the scrutinee was matched against X @@ -78,7 +78,7 @@ fun f(x) = if //│ case x*‡ of //│ X*◊ -> 3 //│ _ -> 3 -//│ ╔══[WARNING] possibly conflicted patterns +//│ ╔══[WARNING] possibly conflicting patterns //│ ║ l.74: x is Y then 1 //│ ║ ^ //│ ╟── the scrutinee was matched against X @@ -87,7 +87,7 @@ fun f(x) = if //│ ╟── which is unrelated with Y //│ ║ l.74: x is Y then 1 //│ ╙── ^ -//│ ╔══[WARNING] possibly conflicted patterns +//│ ╔══[WARNING] possibly conflicting patterns //│ ║ l.75: x is Z then 2 //│ ║ ^ //│ ╟── the scrutinee was matched against X @@ -109,7 +109,7 @@ fun f(x) = if //│ case x*‡ of //│ X*◊ -> 2 //│ _ -> 2 -//│ ╔══[WARNING] possibly conflicted patterns +//│ ╔══[WARNING] possibly conflicting patterns //│ ║ l.105: x is Y and //│ ║ ^ //│ ╟── the scrutinee was matched against X From 6030ae7230477e8ae8367ce738fbcfd69e5771f7 Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Thu, 9 May 2024 10:39:16 +0800 Subject: [PATCH 131/143] Rm repeated locations in warning messages --- .../mlscript/ucs/stages/Normalization.scala | 2 +- .../ucs/coverage/ConflictedPatterns.mls | 34 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala index 85712c64..f6037647 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/Normalization.scala @@ -269,7 +269,7 @@ trait Normalization { self: Desugarer with Traceable => continuation :++ specializeImpl(tail) } else { raiseDesugaringWarning( - msg"possibly conflicting patterns" -> thatPattern.toLoc, + msg"possibly conflicting patterns for this scrutinee" -> scrutineeVar.toLoc, msg"the scrutinee was matched against ${pattern.toString}" -> pattern.toLoc, msg"which is unrelated with ${thatPattern.toString}" -> thatPattern.toLoc) specializeImpl(tail) diff --git a/shared/src/test/diff/pretyper/ucs/coverage/ConflictedPatterns.mls b/shared/src/test/diff/pretyper/ucs/coverage/ConflictedPatterns.mls index 77194e9a..be741c8d 100644 --- a/shared/src/test/diff/pretyper/ucs/coverage/ConflictedPatterns.mls +++ b/shared/src/test/diff/pretyper/ucs/coverage/ConflictedPatterns.mls @@ -24,9 +24,9 @@ fun f(x) = if //│ case x*‡ of //│ X*◊ -> 2 //│ _ -> 2 -//│ ╔══[WARNING] possibly conflicting patterns +//│ ╔══[WARNING] possibly conflicting patterns for this scrutinee //│ ║ l.21: x is X and x is Y then 1 -//│ ║ ^ +//│ ║ ^ //│ ╟── the scrutinee was matched against X //│ ║ l.21: x is X and x is Y then 1 //│ ║ ^ @@ -47,18 +47,18 @@ fun f(x) = if //│ case x*‡ of //│ X*◊ -> 3 //│ _ -> 3 -//│ ╔══[WARNING] possibly conflicting patterns -//│ ║ l.43: x is Y then 1 -//│ ║ ^ +//│ ╔══[WARNING] possibly conflicting patterns for this scrutinee +//│ ║ l.42: x is X and +//│ ║ ^ //│ ╟── the scrutinee was matched against X //│ ║ l.42: x is X and //│ ║ ^ //│ ╟── which is unrelated with Y //│ ║ l.43: x is Y then 1 //│ ╙── ^ -//│ ╔══[WARNING] possibly conflicting patterns -//│ ║ l.44: x is Y then 2 -//│ ║ ^ +//│ ╔══[WARNING] possibly conflicting patterns for this scrutinee +//│ ║ l.42: x is X and +//│ ║ ^ //│ ╟── the scrutinee was matched against X //│ ║ l.42: x is X and //│ ║ ^ @@ -78,18 +78,18 @@ fun f(x) = if //│ case x*‡ of //│ X*◊ -> 3 //│ _ -> 3 -//│ ╔══[WARNING] possibly conflicting patterns -//│ ║ l.74: x is Y then 1 -//│ ║ ^ +//│ ╔══[WARNING] possibly conflicting patterns for this scrutinee +//│ ║ l.73: x is X and +//│ ║ ^ //│ ╟── the scrutinee was matched against X //│ ║ l.73: x is X and //│ ║ ^ //│ ╟── which is unrelated with Y //│ ║ l.74: x is Y then 1 //│ ╙── ^ -//│ ╔══[WARNING] possibly conflicting patterns -//│ ║ l.75: x is Z then 2 -//│ ║ ^ +//│ ╔══[WARNING] possibly conflicting patterns for this scrutinee +//│ ║ l.73: x is X and +//│ ║ ^ //│ ╟── the scrutinee was matched against X //│ ║ l.73: x is X and //│ ║ ^ @@ -109,9 +109,9 @@ fun f(x) = if //│ case x*‡ of //│ X*◊ -> 2 //│ _ -> 2 -//│ ╔══[WARNING] possibly conflicting patterns -//│ ║ l.105: x is Y and -//│ ║ ^ +//│ ╔══[WARNING] possibly conflicting patterns for this scrutinee +//│ ║ l.104: x is X and +//│ ║ ^ //│ ╟── the scrutinee was matched against X //│ ║ l.104: x is X and //│ ║ ^ From f8cd23f3a462b2d27221b7e3efd94c835721959a Mon Sep 17 00:00:00 2001 From: Luyu Cheng Date: Thu, 23 May 2024 11:36:08 +0800 Subject: [PATCH 132/143] Address remaining issues mentioned in the PR --- .../scala/mlscript/ucs/context/Context.scala | 4 +- .../ucs/stages/CoverageChecking.scala | 2 +- .../pretyper/ucs/SpecilizationCollision.mls | 55 +++++++------------ 3 files changed, 23 insertions(+), 38 deletions(-) diff --git a/shared/src/main/scala/mlscript/ucs/context/Context.scala b/shared/src/main/scala/mlscript/ucs/context/Context.scala index 65fa9a58..870113ed 100644 --- a/shared/src/main/scala/mlscript/ucs/context/Context.scala +++ b/shared/src/main/scala/mlscript/ucs/context/Context.scala @@ -9,7 +9,7 @@ import pretyper.Scope class Context(originalTerm: If) { /** The prefix of all prefixes. */ - private val prefix = Context.freshPrefix() + private val prefix = Context.freshPrefix private val cachePrefix = prefix + "$cache$" private val scrutineePrefix = prefix + "$scrut$" private val testPrefix = prefix + "$test$" @@ -60,5 +60,5 @@ class Context(originalTerm: If) { } object Context { - private def freshPrefix(): Str = "ucs" + private val freshPrefix: Str = "ucs" } diff --git a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala index 8f3d3e83..13baf2ea 100644 --- a/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala +++ b/shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala @@ -3,7 +3,7 @@ package ucs package stages import utils._, shorthands._, Message.MessageContext -import ucs.context.{Context, Pattern, Scrutinee} +import context.{Context, Pattern, Scrutinee} import pretyper.Traceable, pretyper.symbol._ trait CoverageChecking { self: Desugarer with Traceable => diff --git a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls index 5caea1d3..3d900e5f 100644 --- a/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls +++ b/shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls @@ -38,7 +38,8 @@ class Derived(y: Int) extends Base(y + 1) //│ class Base(x: Int) //│ class Derived(y: Int) extends Base -:ducs:postprocess.result,coverage +// Notice that Derived is not in the inferred type. +:ducs:postprocess.result fun example3(t) = if t is Base(x) and p1(x) then x @@ -60,42 +61,13 @@ fun example3(t) = //│ y //│ _ -> 42 //│ _ -> 42 -//│ | | | | | | | STEP 4 -//│ | | | | | | | collected match registry: -//│ | | | | | | | >>> t => [class `Base`, class `Derived`] -//│ | | | | | | | >>> x => [] -//│ | | | | | | | >>> y => [] -//│ | | | | | | | checkCoverage <== 0 pending, 3 working, 0 seen -//│ | | | | | | | | CASE t -//│ | | | | | | | | SEEN: empty -//│ | | | | | | | | class symbol: `Base` -//│ | | | | | | | | REMOVE `Base` from working -//│ | | | | | | | | unseen: [class `Base`, class `Derived`] -//│ | | | | | | | | remaining: [] -//│ | | | | | | | | checkCoverage <== LET `ucs$args_t$Base` -//│ | | | | | | | | checkCoverage <== LET `x` -//│ | | | | | | | | checkCoverage <== LET `ucs$test$0` -//│ | | | | | | | | checkCoverage <== TEST `ucs$test$0` -//│ | | | | | | | | checkCoverage <== TERM x -//│ | | | | | | | | checkCoverage <== 0 pending, 2 working, 1 seen -//│ | | | | | | | | | CASE t -//│ | | | | | | | | | SEEN: t is Base -//│ | | | | | | | | | class symbol: `Derived` -//│ | | | | | | | | | REMOVE `Derived` from working -//│ | | | | | | | | | unseen: [class `Derived`] -//│ | | | | | | | | | remaining: [] -//│ | | | | | | | | | checkCoverage <== LET `ucs$args_t$Derived` -//│ | | | | | | | | | checkCoverage <== LET `y` -//│ | | | | | | | | | checkCoverage <== TERM y -//│ | | | | | | | | | remaining cases should be covered by the wildcard -//│ | | | | | | | | | checkCoverage <== TERM 42 -//│ | | | | | | | | checkCoverage ==> 0 diagnostics -//│ | | | | | | | | remaining cases should be covered by the wildcard -//│ | | | | | | | | checkCoverage <== TERM 42 -//│ | | | | | | | checkCoverage ==> 0 diagnostics -//│ | | | | | | | Coverage checking result: 0 errors //│ fun example3: forall 'a. (Base & {#x: Num & 'a} | Object & ~#Base) -> (Int | 'a) +example3(Derived(1)) +//│ Int +//│ res +//│ = 1 + fun example4(t, x) = if t is Base(x) and p1(x) then x @@ -119,3 +91,16 @@ example4(Derived(1), 4) ~~> 5 //│ "passed" //│ res //│ = 'passed' + +class Base(x: Int) +class Derived[A](y: A) extends Base(1) +//│ class Base(x: Int) +//│ class Derived[A](y: A) extends Base + +// Notice that now Derived is generic, so it's appear in the inferred type. +fun example5(t) = + if t is + Base(x) and p1(x) then x + Derived(y) then y + else 42 +//│ fun example5: forall 'a 'b. (Base & {#x: Num & 'a} & ~#Derived | Derived['b] & {#x: Num & 'a} | Object & ~#Base) -> (42 | 'a | 'b) From 69738faa577ca3052a590fae931acd44bd60cf89 Mon Sep 17 00:00:00 2001 From: Lionel Parreaux Date: Thu, 23 May 2024 21:53:11 +0800 Subject: [PATCH 133/143] Add test to point out current limitation in `unapply` support --- shared/src/test/diff/nu/Unapply.mls | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/shared/src/test/diff/nu/Unapply.mls b/shared/src/test/diff/nu/Unapply.mls index ea8edd65..a74556a8 100644 --- a/shared/src/test/diff/nu/Unapply.mls +++ b/shared/src/test/diff/nu/Unapply.mls @@ -66,6 +66,20 @@ DT.unapply({ x: 42 }) //│ TypeError: Cannot read private member #x from an object whose class did not declare it +// * Currently, support for unapply is pretty broken: it accesses an _unqualified_ private field +// * although the same private field may be defined in different classes of the same hierarchy, +// * which leads to unsoundness: + +class DS(x: Int) extends DT[Str]("a") +//│ class DS(x: Int) extends DT + +// * Wrong type! +DT.unapply(DS(42)) +//│ [Int] +//│ res +//│ = [ 'a' ] + + // * TODO improve `unapply` logic: currently it picks up shadowing fields/methods class Foo(x: Int) { @@ -98,8 +112,8 @@ if Foo(123) is Foo(a) then a fun D(x: Int) = {x} module D { fun unapply(a) = [a.x] } //│ ╔══[ERROR] Redefinition of 'D' -//│ ║ l.99: module D { fun unapply(a) = [a.x] } -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.113: module D { fun unapply(a) = [a.x] } +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ fun D: (x: Int) -> {x: Int} //│ module D { //│ fun unapply: forall 'x. {x: 'x} -> ['x] From 211430d1ada45861743d3840020e2676c0e4ed18 Mon Sep 17 00:00:00 2001 From: HarrisL2 Date: Tue, 18 Jun 2024 13:27:06 +0800 Subject: [PATCH 134/143] Defunctionalization with simple-sub Control-flow Analysis (#222) --- .../scala/mlscript/compiler/ClassLifter.scala | 21 +- .../scala/mlscript/compiler/Helpers.scala | 70 --- .../mlscript/compiler/mono/Monomorph.scala | 268 ----------- .../compiler/mono/MonomorphError.scala | 3 - .../mono/specializer/BoundedTerm.scala | 180 ------- .../mono/specializer/Specializer.scala | 248 ---------- .../compiler/simpledef/Simpledef.scala | 455 ++++++++++++++++++ .../mlscript/compiler/simpledef/Uid.scala | 18 + .../diff/Defunctionalize/ClassConstructor.mls | 82 ++++ .../test/diff/Defunctionalize/Classes.mls | 70 +-- .../diff/Defunctionalize/ClosureCapture.mls | 65 +-- .../test/diff/Defunctionalize/Constructor.mls | 57 +-- .../Defunctionalize/DelayedEvaluation.mls | 67 +-- .../diff/Defunctionalize/Differentiation.mls | 160 +++--- .../diff/Defunctionalize/FreeVariables.mls | 29 +- .../diff/Defunctionalize/FuncsWithParams.mls | 53 +- .../test/diff/Defunctionalize/Inheritance.mls | 45 +- .../test/diff/Defunctionalize/Lambda.mls | 22 + .../test/diff/Defunctionalize/Lambdas.mls | 47 +- .../diff/Defunctionalize/ListConstruction.mls | 80 ++- .../test/diff/Defunctionalize/Modules.mls | 38 +- .../diff/Defunctionalize/MonoNonLambda.mls | 68 ++- .../diff/Defunctionalize/MonoTupSelect.mls | 35 +- .../diff/Defunctionalize/MutableParams.mls | 4 - .../test/diff/Defunctionalize/MutualRec.mls | 35 +- .../test/diff/Defunctionalize/NewOperator.mls | 29 +- .../test/diff/Defunctionalize/NuMono.mls | 83 ++-- .../diff/Defunctionalize/ObjFieldAccess.mls | 214 ++++---- .../test/diff/Defunctionalize/ObjFields.mls | 34 ++ .../diff/Defunctionalize/ObjMultiFields.mls | 40 ++ .../diff/Defunctionalize/ObjsSelection.mls | 42 ++ .../test/diff/Defunctionalize/OldMonoList.mls | 252 ++++------ .../test/diff/Defunctionalize/Polymorphic.mls | 49 +- .../test/diff/Defunctionalize/Record.mls | 31 ++ .../diff/Defunctionalize/RecursiveFunc.mls | 45 +- .../diff/Defunctionalize/SelfReference.mls | 26 +- .../diff/Defunctionalize/SimpleClasses.mls | 63 +-- .../Defunctionalize/SimpleConditionals.mls | 46 +- .../test/diff/Defunctionalize/SimpleFunc.mls | 73 ++- .../test/diff/Defunctionalize/Simpledef.mls | 82 ++++ .../test/diff/Defunctionalize/TupleSelect.mls | 12 + .../diff/Lifter/FunctionTypeAnnotations.mls | 34 +- compiler/shared/test/diff/Lifter/LambLift.mls | 39 +- compiler/shared/test/diff/Lifter/LiftNew.mls | 4 - compiler/shared/test/diff/Lifter/LiftType.mls | 5 + compiler/shared/test/diff/Lifter/Lifter.mls | 25 +- .../shared/test/diff/Lifter/LifterBlks.mls | 36 +- .../shared/test/diff/Lifter/NestedClasses.mls | 4 - .../shared/test/diff/Lifter/NestedFuncs.mls | 4 - .../diff/Lifter/ParameterizedInheritance.mls | 54 +-- .../test/diff/Lifter/TypedClassParams.mls | 1 - .../test/scala/mlscript/compiler/Test.scala | 45 +- .../src/test/scala/mlscript/DiffTests.scala | 23 +- 53 files changed, 1603 insertions(+), 2012 deletions(-) delete mode 100644 compiler/shared/main/scala/mlscript/compiler/Helpers.scala delete mode 100644 compiler/shared/main/scala/mlscript/compiler/mono/Monomorph.scala delete mode 100644 compiler/shared/main/scala/mlscript/compiler/mono/MonomorphError.scala delete mode 100644 compiler/shared/main/scala/mlscript/compiler/mono/specializer/BoundedTerm.scala delete mode 100644 compiler/shared/main/scala/mlscript/compiler/mono/specializer/Specializer.scala create mode 100644 compiler/shared/main/scala/mlscript/compiler/simpledef/Simpledef.scala create mode 100644 compiler/shared/main/scala/mlscript/compiler/simpledef/Uid.scala create mode 100644 compiler/shared/test/diff/Defunctionalize/ClassConstructor.mls create mode 100644 compiler/shared/test/diff/Defunctionalize/Lambda.mls create mode 100644 compiler/shared/test/diff/Defunctionalize/ObjFields.mls create mode 100644 compiler/shared/test/diff/Defunctionalize/ObjMultiFields.mls create mode 100644 compiler/shared/test/diff/Defunctionalize/ObjsSelection.mls create mode 100644 compiler/shared/test/diff/Defunctionalize/Record.mls create mode 100644 compiler/shared/test/diff/Defunctionalize/Simpledef.mls create mode 100644 compiler/shared/test/diff/Defunctionalize/TupleSelect.mls diff --git a/compiler/shared/main/scala/mlscript/compiler/ClassLifter.scala b/compiler/shared/main/scala/mlscript/compiler/ClassLifter.scala index 1447af75..533ab83b 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ClassLifter.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ClassLifter.scala @@ -8,7 +8,8 @@ import scala.collection.mutable.Map as MutMap import scala.collection.mutable.Set as MutSet import scala.collection.mutable.ArrayBuffer as ArrayBuffer import mlscript.codegen.CodeGenError -import mlscript.compiler.mono.MonomorphError + +class CompilerError(error: String) extends Error(error) class ClassLifter(logDebugMsg: Boolean = false) { type ClassName = String @@ -249,7 +250,7 @@ class ClassLifter(logDebugMsg: Boolean = false) { val nE = liftTerm(expr) val nR = liftTerm(rhs) (IfThen(nE._1, nR._1), nE._2 ++ nR._2) - case _ => throw MonomorphError(s"Unknown IfBody: ${body}") + case _ => throw CompilerError(s"Unknown IfBody: ${body}") } private def liftTuple(tup: Tup)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (Tup, LocalContext) = { @@ -399,7 +400,7 @@ class ClassLifter(logDebugMsg: Boolean = false) { case Sel(receiver, fieldName) => val nRec = liftTerm(receiver) (Sel(nRec._1, fieldName), nRec._2) - case Splc(fields) => throw MonomorphError(s"Unimplemented liftTerm: ${target}") + case Splc(fields) => throw CompilerError(s"Unimplemented liftTerm: ${target}") case Subs(arr, idx) => val (ltrm, lctx) = liftTerm(arr) val (rtrm, rctx) = liftTerm(idx) @@ -412,7 +413,7 @@ class ClassLifter(logDebugMsg: Boolean = false) { val ret = liftTerm(lhs) val nTs = targs.map(liftType).unzip (TyApp(ret._1, nTs._1), nTs._2.fold(ret._2)(_ ++ _)) - case With(trm, fields) => throw MonomorphError(s"Unimplemented liftTerm: ${target}") + case With(trm, fields) => throw CompilerError(s"Unimplemented liftTerm: ${target}") case New(Some((t: TypeName, prm: Tup)), TypingUnit(Nil)) => val ret = liftConstr(t, prm) (New(Some((ret._1, ret._2)), TypingUnit(Nil)), ret._3) @@ -432,7 +433,7 @@ class ClassLifter(logDebugMsg: Boolean = false) { val nSta = New(Some((nTpNm, Tup(Nil))), TypingUnit(Nil)) val ret = liftEntities(List(anoCls, nSta)) (Blk(ret._1), ret._2) - case New(head, body) => throw MonomorphError(s"Unimplemented liftTerm: ${target}") + case New(head, body) => throw CompilerError(s"Unimplemented liftTerm: ${target}") case Blk(stmts) => val ret = liftEntities(stmts) (Blk(ret._1), ret._2) @@ -447,7 +448,7 @@ class ClassLifter(logDebugMsg: Boolean = false) { val (bod2, ctx) = liftTerm(bod) val (sts2, ctx2) = liftEntities(sts) (Where(bod2, sts2), ctx2) - case _: Eqn | _: Super | _: Rft | _: While | _: Quoted | _: Unquoted | _: Ann => throw MonomorphError(s"Unimplemented liftTerm: ${target}") // TODO + case _: Eqn | _: Super | _: Rft | _: While | _: Quoted | _: Unquoted | _: Ann => throw CompilerError(s"Unimplemented liftTerm: ${target}") // TODO case patmat: AdtMatchWith => lastWords(s"Cannot liftTermNew ${patmat}") } @@ -484,7 +485,7 @@ class ClassLifter(logDebugMsg: Boolean = false) { ((v, Fld(flags, tmp._1)), tmp._2) }.unzip (Rcd(ret._1), ret._2.fold(emptyCtx)(_ ++ _)) - case _ => throw MonomorphError(s"Unimplemented liftTermAsType: ${target}") + case _ => throw CompilerError(s"Unimplemented liftTermAsType: ${target}") } private def liftTypeName(target: TypeName)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (TypeName, LocalContext) = { @@ -578,7 +579,7 @@ class ClassLifter(logDebugMsg: Boolean = false) { val (body2, ctx) = liftType(body) PolyType(targs, body2) -> ctx case Top | Bot | _: Literal | _: TypeTag | _: TypeVar => target.asInstanceOf[Type] -> emptyCtx - case _: Selection => throw MonomorphError(s"Unimplemented liftType: ${target}") // TODO + case _: Selection => throw CompilerError(s"Unimplemented liftType: ${target}") // TODO } @@ -602,7 +603,7 @@ class ClassLifter(logDebugMsg: Boolean = false) { }.unzip (func.copy(rhs = Right(PolyType(nTargs._1, nBody._1)))(func.declareLoc, func.virtualLoc, func.mutLoc, func.signature, func.outer, func.genField, func.annotations), nTargs._2.fold(nBody._2)(_ ++ _)) - case _ => throw MonomorphError(s"Unimplemented liftMemberFunc: ${func}") // TODO + case _ => throw CompilerError(s"Unimplemented liftMemberFunc: ${func}") // TODO } } @@ -629,7 +630,7 @@ class ClassLifter(logDebugMsg: Boolean = false) { case (tn, ctx) => (L(tn), ctx) case R(tv) => R(tv) -> emptyCtx}).unzip NuFunDef(rec, globFuncs.get(nm).get._1, N, nTpVs, Right(PolyType(nTargs._1, nBody._1)))(N, N, N, N, N, true, Nil) - case _ => throw MonomorphError(s"Unimplemented liftGlobalFunc: ${func}") + case _ => throw CompilerError(s"Unimplemented liftGlobalFunc: ${func}") }) } diff --git a/compiler/shared/main/scala/mlscript/compiler/Helpers.scala b/compiler/shared/main/scala/mlscript/compiler/Helpers.scala deleted file mode 100644 index 627b2b79..00000000 --- a/compiler/shared/main/scala/mlscript/compiler/Helpers.scala +++ /dev/null @@ -1,70 +0,0 @@ -package mlscript -package compiler - -import mlscript.compiler.mono.* -import scala.collection.mutable.ArrayBuffer - -type NuParameter = (FldFlags, Var, Option[Term]) - -object Helpers: - /** - * Extract parameters for monomorphization from a `Tup`. - */ - def extractLamParams(term: Term): Option[List[NuParameter]] = term match - case Lam(Tup(fields), _) => Some(fields.flatMap { - case (_, Fld(FldFlags(_, _, _), UnitLit(true))) => None - case (None, Fld(flags, name: Var)) => Some((flags, name, None)) - case (None, Fld(flags, Bra(_, name: Var))) => Some((flags, name, None)) - case (Some(name: Var), Fld(flags, typename: Term)) => Some((flags, name, Some(typename))) - case _ => throw MonomorphError( - s"Encountered unexpected structure when extracting parameters: ${term}" - ) - }) - case _ => None //FIXME: Silent failure? Improve semantics - - def extractObjParams(term: Term): Iterator[NuParameter] = term match - case Tup(fields) => fields.iterator.flatMap { - case (_, Fld(FldFlags(_, _, _), UnitLit(true))) => None - case (None, Fld(flags, name: Var)) => Some((flags, name, None)) - case (None, Fld(flags, Bra(_, name: Var))) => Some((flags, name, None)) - case (Some(name: Var), Fld(flags, typename: Term)) => Some((flags, name, Some(typename))) - case _ => throw MonomorphError(s"Encountered unexpected structure when extracting parameters: ${term}") - } - case _ => throw MonomorphError(s"Encountered unexpected structure when extracting parameters: ${term}") - - def extractLamBody(term: Term): Term = term match - case Lam(_, body) => body - case _ => throw MonomorphError(s"Attempted to extract Lambda Body from ${term}") - - def toTuple(args: List[Term]): Tup = - Tup(args.map{term => (None, Fld(FldFlags.empty, term))}) - - def paramToTuple(args: List[NuParameter]): Tup = - Tup(args.map{ - case (flags, name, None) => (None, Fld(flags, name)) - case (flags, name, Some(typename)) => (Some(name), Fld(flags, typename)) - }) - - /* - * If term is not a Lambda, turn it into a lambda with a "this" parameter - * If term is already a Lambda, add a "this" parameter to the front of the parameter list - */ - def addThisParam(term: Term): Term = term match - case Lam(Tup(fields), rhs) => Lam(Tup((None, Fld(FldFlags.empty, Var("this")))::fields), rhs) - case rhs => Lam(Tup((None, Fld(FldFlags.empty, Var("this")))::Nil), rhs) - - // OLD FIXME: Loses tuple information in conversion - def toFuncArgs(term: Term): IterableOnce[Term] = term match - // The new parser generates `(undefined, )` when no arguments. - // Let's do this temporary fix. - case Tup((_, Fld(FldFlags(_, _, _), UnitLit(true))) :: Nil) => Iterable.empty - case Tup(fields) => fields.iterator.map(_._2.value) - case _ => Some(term) - - def extractFuncArgs(term: Term): List[Term] = term match - // The new parser generates `(undefined, )` when no arguments. - // Let's do this temporary fix. - case Tup((_, Fld(FldFlags(_, _, _), UnitLit(true))) :: Nil) => ??? - case Tup(fields) => fields.map(_._2.value) - case _ => throw MonomorphError("Unknown structure in FuncArgs") - diff --git a/compiler/shared/main/scala/mlscript/compiler/mono/Monomorph.scala b/compiler/shared/main/scala/mlscript/compiler/mono/Monomorph.scala deleted file mode 100644 index 930a3f0d..00000000 --- a/compiler/shared/main/scala/mlscript/compiler/mono/Monomorph.scala +++ /dev/null @@ -1,268 +0,0 @@ -package mlscript -package compiler -package mono - -import scala.collection.immutable.{HashMap, ListMap} -import scala.collection.mutable.{Map as MutMap, Set as MutSet} -import scala.collection.mutable.ListBuffer -import java.util.IdentityHashMap -import scala.collection.JavaConverters._ -import mlscript.Mod - -class Monomorph(debug: Debug = DummyDebug): - import Helpers._ - import Monomorph._ - - /** - * Specialized implementations of function declarations. - * function name -> (function, mutable parameters (?), parameters, output value) - */ - private val funImpls = MutMap[String, (NuFunDef, MutMap[String, NuFunDef], Option[List[BoundedTerm]], VarVal)]() - - private val funDependence = MutMap[String, Set[String]]() - - val evalQueue = MutSet[String]() - val evalCnt = MutMap[String, Int]() - - private val allTypeImpls = MutMap[String, NuTypeDef]() - - private def addType(typeDef: NuTypeDef) = - allTypeImpls.addOne(typeDef.name, typeDef) - - val specializer = new Specializer(this)(using debug) - - - object VarValMap { - var vxCnt: Int = 0 - val vMap: MutMap[Int, BoundedTerm] = MutMap[Int, BoundedTerm]() - def get(v: VarVal): BoundedTerm = vMap.get(v.vx) match - case Some(value) => value - case None => ??? - def refresh(): VarVal = { - vxCnt += 1 - val ret = VarVal(vxCnt, 0, get) - vMap.addOne(vxCnt -> BoundedTerm(ret)) - ret - } - def update(v: VarVal, s: BoundedTerm): Unit = { - debug.writeLine(s"Updating vMap ${v.vx} with ${s}") - vMap.update(v.vx, s) - } - } - - val evaluationMap = new IdentityHashMap[Term, BoundedTerm]().asScala - def getRes(term: Term): BoundedTerm = evaluationMap.getOrElse(term, throw MonomorphError(s"Bounds for ${term} not found.")) - - private def addFunction(func: NuFunDef): Unit = { - debug.writeLine(s"Adding Function ${func}") - funImpls.addOne(func.name, (func, MutMap(), func.body match - case Lam(Tup(params), body) => Some(params.map(_ => BoundedTerm())) - case _ => None - , VarValMap.refresh())) - funDependence.addOne(func.name, Set()) - } - - private def getResult(mains: List[Statement]): List[Statement] = - List[Statement]() - .concat(allTypeImpls.values.map(_.copy(body = TypingUnit(Nil))(None, None, Nil))) - .concat(funImpls.values.map(_._1)) - .concat(mains) - - def defunctionalize(tu: TypingUnit): TypingUnit = - val nuTerms = tu.entities.zipWithIndex.flatMap { - case (term: Term, i) => - val mainFunc = NuFunDef(None, Var(s"main$$$$$i"), None, Nil, Left(Lam(Tup(Nil), term)))(None, None, None, None, None, false, Nil) - addFunction(mainFunc) - evalQueue.add(mainFunc.name) - Some(App(Var(s"main$$$$$i"), Tup(Nil))) - case (tyDef: NuTypeDef, _) => - addType(tyDef) - None - case (funDef: NuFunDef, _) => - funDef.rhs match - case Left(value) => addFunction(funDef) - case Right(value) => ??? - None - case _ => ??? - } - while (!evalQueue.isEmpty) { - debug.writeLine(s"Queue: ${evalQueue}") - val next = evalQueue.head - evalQueue.remove(next) - updateFunc(next) - } - debug.writeLine(s"${PrettyPrinter.showTypingUnit(TypingUnit(getResult(nuTerms)))}") - debug.writeLine(s"========DEFUNC PHASE========") - funImpls.mapValuesInPlace{ - case (_, (func@NuFunDef(isLetRec, nm, sn, tp, rhs), mp, la, lr)) => - rhs match - case Left(Lam(lhs, rhs)) => - (NuFunDef(isLetRec, nm, sn, tp, Left(Lam(lhs,specializer.defunctionalize(rhs)(using evaluationMap))))(None, None, None, None, None, false, Nil), mp, la, lr) - case Left(body) => - (NuFunDef(isLetRec, nm, sn, tp, Left(specializer.defunctionalize(body)(using evaluationMap)))(None, None, None, None, None, false, Nil), mp, la, lr) - case Right(tp) => ??? - } - val ret = getResult(nuTerms) - TypingUnit(ret) - - def getFuncRetVal(name: String, args: Option[List[BoundedTerm]])(using evalCtx: Map[String, BoundedTerm], callingStack: List[String]): BoundedTerm = { - funImpls.get(name) match - case None => throw MonomorphError(s"Function ${name} does not exist") - case Some((funDef, mp, oldArgs, vals)) => - funDef.rhs match - case Left(body) => - funDependence.update(name, funDependence.getOrElse(name, Set()) ++ callingStack.headOption) - val hasArgs = oldArgs.isDefined - val params = extractLamParams(body) - val mergedArgs = oldArgs.map(old => (old zip (args.get)).map(_ ++ _).zip(params.get).map( - (x,y) => /*if(y._1.spec) then x else x.literals2Prims*/ x // TODO: Specialization for literals - )) - debug.writeLine(s"old args ${oldArgs}") - debug.writeLine(s"new args ${args}") - debug.writeLine(s"merged args ${mergedArgs}") - // FIXME: do paramless funcs need multiple evaluations? currently only eval paramless funcs once - if (!evalCnt.contains(name) || (hasArgs && (oldArgs.get zip mergedArgs.get).exists(x => x._1.compare(x._2)))) { - funImpls.update(name, (funDef, mp, mergedArgs, vals)) - if(!evalQueue.contains(name)) - if(!evalCnt.contains(name)) - then - debug.writeLine(s"first time eval function ${name}") - updateFunc(name) - else - debug.writeLine(s"new arg eval function ${name}") - evalQueue.add(name) - } - BoundedTerm(funImpls.get(name).get._4) - case Right(tp) => ??? - } - - private def updateFunc(funcName: String): Unit = { - val updateCount = evalCnt.get(funcName).getOrElse(0) - if(updateCount > 10){ - throw new MonomorphError("stack overflow!!!") - } - debug.writeLine(s"updating ${funcName} for ${updateCount}th time") - evalCnt.update(funcName, updateCount+1) - debug.writeLine(s"Evaluating ${funcName}") - val (func, mps, args, res) = funImpls.get(funcName).get - debug.writeLine(s"args = ${args}") - func.rhs match - case Left(value) => - val params = extractLamParams(value) - val body = params match - case Some(_) => extractLamBody(value) - case None => value - val ctx = params match - case Some(p) => - if p.length != args.get.length - then throw MonomorphError("Argument length mismatch in function update") - else (p.map(_._2.name) zip args.get).toMap - case None => Map() - specializer.evaluate(body)(using ctx, List(func.name), evaluationMap) - evaluationMap.addOne(value, getRes(body)) - val oldExp = VarValMap.get(funImpls.get(funcName).get._4) - if (oldExp.compare(getRes(value))) { - debug.writeLine(s"Bounds of ${funcName} changed, adding dependent functions to evalQueue") - debug.writeLine(s"Before: ${oldExp}") - debug.writeLine(s"After : ${getRes(value)}") - funDependence.get(funcName).get.foreach(x => if !evalQueue.contains(x) then { - debug.writeLine(s"Added ${x}") - evalQueue.add(x) - }) - } else { - debug.writeLine(s"No change in bounds of ${funcName}") - } - //debug.writeLine(s"old body: ${showStructure(value)} => new body: ${showStructure(nuBody)}") - funImpls.updateWith(funcName)(_.map(x => { - VarValMap.update(x._4, getRes(value)) - (x._1, x._2, x._3, x._4) - })) - case Right(value) => throw MonomorphError("Should not update function typeDef") - } - - /* - Find a variable in the global env - */ - def findVar(name: String)(using evalCtx: Map[String, BoundedTerm], callingStack: List[String]): BoundedTerm = - funImpls.get(name) match - case Some(res) => - val (func, _, _, _) = res - funDependence.update(name, funDependence.get(name).get ++ callingStack.headOption) - val params = func.rhs match - case Left(body) => extractLamParams(body).map(_.map(_._2.name).toList) - case Right(tp) => ??? - params match - case Some(p) => BoundedTerm(FuncVal(name, params, Nil)) - case None => - val res = getFuncRetVal(name, None) - res - case None => - allTypeImpls.get(name) match - case Some(res) if res.kind == Cls => BoundedTerm(TypeVal(name)) - case Some(res) if res.kind == Mod => BoundedTerm(createObjValue(name, Nil)) - case None => throw MonomorphError(s"Variable ${name} not found during evaluation") - case _ => throw MonomorphError(s"Variable ${name} unhandled") - - def createObjValue(tpName: String, args: List[BoundedTerm]): MonoVal = - allTypeImpls.get(tpName) match - case Some(NuTypeDef(kind, nme, tparams, params, ctor, sig, parents, _, _, body)) => - debug.writeLine(s"Creating Object Value for ${tpName} with arguments ${args}") - debug.indent() - val ags = (params match - case Some(p) => - if (extractObjParams(p).length != args.length) throw MonomorphError("ObjValue param mismatch") - extractObjParams(p).map(_._2.name).zip(args).toList // FIXME: Different structure for Obj Params - case None => Nil) - val obj = ObjVal(tpName, ags.map((p, _) => p), MutMap(ags: _*)) // TODO: parent object fields - debug.writeLine(s"Parents term is ${parents}") - val parentObjs = parents.map{ - case Var(name) => BoundedTerm(createObjValue(name, Nil)) - case App(Var(name), t: Tup) => - specializer.evaluate(t)(using Map("this"->BoundedTerm(obj)) ++ ags, List(tpName), evaluationMap) - BoundedTerm(createObjValue(name, extractFuncArgs(t).map(getRes))) - case other => throw MonomorphError(s"Unexpected parent object format ${other}") - } - debug.writeLine(s"Parent objects are ${parentObjs}") - obj.fields.addAll(parentObjs.zipWithIndex.map((e, i) => s"sup$$$i" -> e)) - debug.writeLine(s"Created Object Value ${obj}") - debug.outdent() - obj - case None => throw MonomorphError(s"TypeName ${tpName} not found in implementations ${allTypeImpls}") - - def createTupVal(fields: List[BoundedTerm]): TupVal = - TupVal(fields.zipWithIndex.map((term, i) => (Var(i.toString()) -> term)).toMap) - - def getFieldVal(obj: ObjVal, field: String): BoundedTerm = - debug.writeLine(s"select ${field} from ${obj.name}") - allTypeImpls.get(obj.name) match - case None => throw MonomorphError(s"ObjectValue ${obj} not found in implementations ${allTypeImpls}") - case Some(typeDef) => - typeDef.body.entities.flatMap{ - case func@NuFunDef(_, Var(nme), _, _, Left(_)) if nme.equals(field) => Some(func) - case _ => None - }.headOption match - case Some(NuFunDef(isLetRec, Var(nme), sn, tp, Left(body))) => - debug.writeLine(s"found some func") - val nuFuncName = s"${nme}$$${obj.name}" - if (!funImpls.contains(nuFuncName)) { - addFunction(NuFunDef(isLetRec, Var(nuFuncName), sn, tp, Left(addThisParam(body)))(None, None, None, None, None, false, Nil)) - } - BoundedTerm(FuncVal(nuFuncName, extractLamParams(body).map(_.map(_._2.name).toList), List("this" -> BoundedTerm(obj)))) - case _ => - debug.writeLine(s"did not find func, try obj fields") - obj.fields.get(field) match - case Some(value) => value - case None => - debug.writeLine(s"did not find in fields, try superclass") - obj.fields.flatMap(x => { - if (x._1.matches("sup\\$[0-9]+")) { - x._2.asValue match{ - case Some(o: ObjVal) => - Some(getFieldVal(o, field)) - case _ => None - } - } - else None - }).headOption match - case Some(value) => value - case None => throw MonomorphError(s"Field value ${field} not found in ObjectValue ${obj}") \ No newline at end of file diff --git a/compiler/shared/main/scala/mlscript/compiler/mono/MonomorphError.scala b/compiler/shared/main/scala/mlscript/compiler/mono/MonomorphError.scala deleted file mode 100644 index e7ef0305..00000000 --- a/compiler/shared/main/scala/mlscript/compiler/mono/MonomorphError.scala +++ /dev/null @@ -1,3 +0,0 @@ -package mlscript.compiler.mono - -class MonomorphError(message: String) extends Error(message) diff --git a/compiler/shared/main/scala/mlscript/compiler/mono/specializer/BoundedTerm.scala b/compiler/shared/main/scala/mlscript/compiler/mono/specializer/BoundedTerm.scala deleted file mode 100644 index 301c2c1a..00000000 --- a/compiler/shared/main/scala/mlscript/compiler/mono/specializer/BoundedTerm.scala +++ /dev/null @@ -1,180 +0,0 @@ -package mlscript.compiler.mono -import mlscript.{Var, Lit} -import scala.collection.mutable.{Map => MutMap} -import scala.collection.immutable.ListMap - - -sealed abstract class MonoVal extends MonoValImpl -final case class TypeVal(name: String) extends MonoVal -final case class ObjVal(name: String, params: List[String], fields: MutMap[String, BoundedTerm]) extends MonoVal with ObjValImpl -final case class FuncVal(name: String, params: Option[List[String]], ctx: List[(String, BoundedTerm)]) extends MonoVal -final case class UnknownVal() extends MonoVal -/* - * VarVal represents the evaluation bounds of a function, which can change - * as we progress through evaluation. - * TODO: Terribly unintuitive implementation, should attempt to refactor. - */ -final case class VarVal(vx: Int, version: Int, val getter: VarVal => BoundedTerm) extends MonoVal with VarValImpl -final case class LiteralVal(i: Lit | Boolean) extends MonoVal with LitValImpl -final case class PrimVal() extends MonoVal -final case class TupVal(fields: Map[Var, BoundedTerm]) extends MonoVal - - -class BoundedTerm(val values: Set[MonoVal]) extends BoundedTermImpl - -object BoundedTerm { - def apply(): BoundedTerm = new BoundedTerm(Set()) - def apply(singleVal: MonoVal): BoundedTerm = new BoundedTerm(Set(singleVal)) - def apply(valSet: Set[MonoVal]): BoundedTerm = new BoundedTerm(valSet) -} - -trait MonoValImpl { self: MonoVal => - override def toString: String = this match { - case FuncVal(name, params, ctx) => s"FuncVal(${name}, ${params.map(_.mkString("(",",",")")).getOrElse("None")}, ${ctx})" - case LiteralVal(i) => s"LiteralVal(${i})" - case ObjVal(name, params, fields) => s"ObjVal(${name}, ${fields})" - case TypeVal(name) => s"TypeVal(${name})" - case TupVal(fields) => s"TupVal(${fields})" - case UnknownVal() => s"UnknownVal" - case PrimVal() => s"PrimVal()" - case VarVal(vx, version, _) => s"VarVal(${vx})" - } -} - -trait VarValImpl { - -} - -trait LitValImpl { self: LiteralVal => - def asBoolean(): Option[Boolean] = self match { - case LiteralVal(b: Boolean) => Some(b) - case _ => None - } -} - -trait ObjValImpl { self: ObjVal => - def merge(other: ObjVal)(implicit instackExps: Set[Int]): ObjVal = { - val allKeys = self.fields.keys - val nFlds = allKeys.map(k => { - val s1 = self.fields.get(k).get - val s2 = other.fields.get(k).get - if(instackExps.contains(s1.hashCode()) && instackExps.contains(s2.hashCode())) - (k -> s1) - else (k -> (s1 ++ s2)) - }) - ObjVal(self.name, self.params, MutMap(nFlds.toList: _*)) - } - -} - -trait BoundedTermImpl { self: BoundedTerm => - override def toString: String = self.values.map(_.toString).mkString(";") - def getObjNames(): Set[String] = self.values.flatMap{ - case ObjVal(name, _, _) => Some(name) - case _ => None - } - - private def splitSpecifiedObjects(vs: Set[MonoVal], nms: Set[String]): (Set[MonoVal], Map[String, ObjVal]) = { - val ret = vs.map{ - case o@ObjVal(name, params, fields) => - if (nms.contains(name)) { - (None, Some(name -> o)) - } else { - (Some(o), None) - } - case x => (Some(x), None) - }.unzip - val ret1 = ret._1.flatten - val ret2 = ret._2.flatten.toMap - (ret1, ret2) - } - - /* - * Unfold VarVals into primitive values recursively. - */ - def unfoldVars(implicit instackExps: Set[Int] = Set()): BoundedTerm = { - val (vars, others) = self.values.toList.map{ - case vx: VarVal => (Some(vx), None) - case others => (None, Some(others)) - }.unzip - val varSets: List[BoundedTerm] = vars.flatten.map(x => { - val vSet = x.getter(x) - if(!instackExps.contains(vSet.hashCode())) { - vSet.unfoldVars(instackExps + vSet.hashCode()) - } - else BoundedTerm(x) - }) - varSets.foldLeft(BoundedTerm(others.flatten.toSet))((x, y) => (x ++ y)(instackExps + y.hashCode)) - } - - def asValue: Option[MonoVal] = { - val tmp = this.unfoldVars - if (tmp.values.size == 1) { - Some(tmp.values.head) - } - else None - } - - def getValue: Set[MonoVal] = { - unfoldVars.values.filterNot(_.isInstanceOf[VarVal]) - } - - def literals2Prims: BoundedTerm = { - val hasPrim = values.find(x => x.isInstanceOf[PrimVal] || x.isInstanceOf[LiteralVal]).isDefined - if(hasPrim) - BoundedTerm(values.filterNot(x => x.isInstanceOf[PrimVal] || x.isInstanceOf[LiteralVal])/* + PrimVal()*/) - else this - } - - /* - * Merge two BoundedTerms together recursively, including its component values & objects. - */ - def ++(other: BoundedTerm)(implicit instackExps: Set[Int] = Set()): BoundedTerm = { - if (this == other) this - else { - val mergingValNms = self.getObjNames().intersect(other.getObjNames()) - val (restVals1, mergingVals1) = splitSpecifiedObjects(self.values, mergingValNms) - val (restVals2, mergingVals2) = splitSpecifiedObjects(other.values, mergingValNms) - - val ret = mergingValNms.map(nm => (mergingVals1.get(nm), mergingVals2.get(nm)) match { - case (Some(x1: ObjVal), Some(x2: ObjVal)) => x1.merge(x2)(instackExps ++ Set(self.hashCode(), other.hashCode())) - case _ => ??? - }) - - var ret2 = restVals1 ++ restVals2 - // TODO: eliminate redundant values - val retVals = BoundedTerm(ret ++ ret2) - retVals - } - } - - def size: Int = values.size - - /* - * Returns true if the bounds of other is larger than this. - */ - def compare(other: BoundedTerm)(implicit instackExps: Set[Int] = Set()): Boolean = { - if (instackExps.contains(this.hashCode()) && instackExps.contains(other.hashCode())) - false - else { - if (this.values.find(_.isInstanceOf[PrimVal]).isEmpty && other.values.find(_.isInstanceOf[PrimVal]).isDefined) - true - else if (this.size != other.size) - this.size < other.size - else { - val nms1 = this.getObjNames() - val nms2 = other.getObjNames() - if(nms1.equals(nms2)){ - val (rests1, objs1) = splitSpecifiedObjects(this.values, nms1) - val (rests2, objs2) = splitSpecifiedObjects(other.values, nms1) - nms1.find(nm => { - val v1s = objs1.get(nm).get.fields - val v2s = objs2.get(nm).get.fields - v1s.keySet.find(k => v1s.get(k).get.compare(v2s.get(k).get)(using instackExps + this.hashCode() + other.hashCode())).isDefined - }).isDefined - } - else true - } - } - } -} \ No newline at end of file diff --git a/compiler/shared/main/scala/mlscript/compiler/mono/specializer/Specializer.scala b/compiler/shared/main/scala/mlscript/compiler/mono/specializer/Specializer.scala deleted file mode 100644 index be98f2ee..00000000 --- a/compiler/shared/main/scala/mlscript/compiler/mono/specializer/Specializer.scala +++ /dev/null @@ -1,248 +0,0 @@ -package mlscript -package compiler -package mono - -import scala.collection.mutable.ArrayBuffer -import scala.collection.mutable.Map as MutMap -import scala.collection.mutable.ArrayBuffer -import mlscript.compiler.Helpers.* -class Specializer(monoer: Monomorph)(using debug: Debug){ - - /* - Evaluate a Term given an evaluation Context and update its result in the term map - */ - val builtInOps: Set[String] = Set("+", "-", ">", "<", "*", "==", "concat", "toString", "log") - def evaluate(term: Term)(using evalCtx: Map[String, BoundedTerm], callingStack: List[String], termMap: MutMap[Term, BoundedTerm]): Unit = - def getRes(term: Term): BoundedTerm = termMap.getOrElse(term, throw MonomorphError(s"Bounds for ${term} not found during eval.")) - debug.writeLine(s"╓Eval ${term}:") - debug.indent() - term match - case lit: Lit => - termMap.addOne(term, BoundedTerm(LiteralVal(lit))) - term - case Var(name) => - debug.writeLine(s"evalCtx: ${evalCtx}") - termMap.addOne(term, - if name == "true" - then BoundedTerm(LiteralVal(true)) - else if name == "false" - then BoundedTerm(LiteralVal(false)) - else evalCtx.getOrElse(name, monoer.findVar(name))) - term - // case Lam(lhs, rhs) => throw MonomorphError("Should not encounter lambda during evaluation process") - case App(lhs@Var(name), rhs) if builtInOps.contains(name) => - evaluate(rhs) - termMap.addOne(term, extractFuncArgs(rhs).map(getRes).fold(BoundedTerm())(_ ++ _)) - case App(lhs@NuNew(cls), args) => - (cls, args) match { - case (v: Var, args: Tup) => - evaluate(args) - termMap.addOne(term, BoundedTerm(monoer.createObjValue(v.name, extractFuncArgs(args).map(getRes)))) - case _ => ??? - } - case App(lhs, rhs) => - evaluate(lhs) - evaluate(rhs) - termMap.addOne(term, getRes(lhs).getValue.map{ - case FuncVal(name, prm, ctx) => - debug.writeLine(s"Apply Function ${name}") - monoer.getFuncRetVal(name, Some(ctx.unzip._2 ++ extractFuncArgs(rhs).map(getRes))) - case o: ObjVal => monoer.getFieldVal(o, "apply").asValue match - case Some(FuncVal(name, prm, ctx)) => - monoer.getFuncRetVal(name, Some(ctx.unzip._2 ++ extractFuncArgs(rhs).map(getRes))) // Unzipping ctx gives implicit "this" - case other => throw MonomorphError(s"Encountered unknown value ${other} when evaluating object application") - case TypeVal(name) => - BoundedTerm(monoer.createObjValue(name, extractFuncArgs(rhs).map(getRes).toList)) - case l@LiteralVal(i) => BoundedTerm(l) - case _ => utils.die - }.fold(BoundedTerm())(_ ++ _)) - case New(Some((constructor, args)), body) => - evaluate(args) - termMap.addOne(term, BoundedTerm(monoer.createObjValue(constructor.base.name, extractFuncArgs(args).map(getRes)))) - case Sel(receiver, fieldName) => - evaluate(receiver) - termMap.addOne(term, getRes(receiver).getValue.map{ - case obj: ObjVal => - obj.fields.get(fieldName.name) match - case Some(fld) => - debug.writeLine("direct select") - fld - case None => - debug.writeLine("other select") - debug.indent() - val res = monoer.getFieldVal(obj, fieldName.name).getValue.map { - case f@FuncVal(name, None, ctx) => - debug.writeLine(s"got paramless func ${f}") - monoer.getFuncRetVal(name, Some(ctx.unzip._2)) - case other => BoundedTerm(other) - }.fold(BoundedTerm())(_ ++ _) - debug.outdent() - res - case tup: TupVal => - tup.fields.get(fieldName) match - case Some(fld) => fld - case None => throw MonomorphError(s"Invalid selction ${fieldName} from Tuple") - // case func: FuncVal => - // monoer.nuGetFuncRetVal(func.name, None) - case other => throw MonomorphError(s"Cannot select from non-object value ${other}") - }.fold(BoundedTerm())(_ ++ _)) - case Let(rec, Var(name), rhs, body) => - if !rec - then - evaluate(rhs) - evaluate(body)(using evalCtx + (name -> getRes(rhs))) - termMap.addOne(term, getRes(body)) - else ??? //TODO: letrec - case Blk(stmts) => - stmts.map{ - case t: Term => evaluate(t) - case other => throw MonomorphError(s"Encountered unlifted non-term ${other} in block ") - } - termMap.addOne(term, { - if stmts.length == 0 - then BoundedTerm(LiteralVal(UnitLit(false))) - else stmts.reverse.head match - case t: Term => getRes(t) - case _ => utils.die - }) - case If(body, alternate) => - val res = body match - case IfThen(condition, consequent) => - evaluate(consequent) - alternate.map(alt => evaluate(alt)) - evaluate(condition) - termMap.addOne(term, getRes(condition).asValue match { - // TODO: redundant branch elimination - // case Some(x: LiteralVal) if x.asBoolean().isDefined => - // if x.asBoolean().get - // then nuConsequent.evaledTerm - // else nuAlternate.map(_.evaledTerm).getOrElse(BoundedTerm(UnknownVal())) - case _ => getRes(consequent) ++ alternate.map(getRes).getOrElse(BoundedTerm(UnknownVal())) - }) - case other => throw MonomorphError(s"IfBody ${body} not handled") - res - case Asc(t, ty) => - evaluate(t) - termMap.addOne(term, getRes(t)) - case Tup(fields) => - fields.map{ - case (name, Fld(flags, value)) => evaluate(value) - } - termMap.addOne(term, BoundedTerm(monoer.createTupVal(fields.map{case (name, Fld(flags, value)) => getRes(value)}))) - case Bra(rcd, t) => - evaluate(t) - termMap.addOne(term, getRes(t)) - // case _: Bind => ??? - // case _: Test => ??? - // case With(term, Rcd(fields)) => ??? - // case CaseOf(term, cases) => ??? - // case Subs(array, index) => ??? - // case Assign(lhs, rhs) => ??? - // case New(None, body) => ??? - // case Rcd(fields) => ??? - case _ => utils.die - debug.outdent() - debug.writeLine(s"╙Result ${getRes(term).getValue.map(_.toString).toList}:") - - def defunctionalize(term: Term)(using termMap: MutMap[Term, BoundedTerm]): Term = { - def getRes(term: Term): BoundedTerm = termMap.getOrElse(term, throw MonomorphError(s"Bounds for ${term} not found during defunc.")) - // TODO: Change to use basic pattern match instead of UCS - def valSetToBranches(vals: List[MonoVal], acc: List[Either[IfBody,Statement]] = List(Left(IfElse(Var("error")))))(using field: Var, args: Option[List[Term]]): List[Either[IfBody,Statement]] = - debug.writeLine(s"Expanding ${vals}") - vals match - case Nil => acc - case head :: next => head match - case o@ObjVal(name, params, fields) => - val selValue = monoer.getFieldVal(o, field.name) - debug.writeLine(s"selected value: ${selValue.asValue}") - val branchCase = selValue.asValue match { - case Some(f: FuncVal) => - IfThen(Var(name), App(Var(f.name), toTuple(Var("obj") :: args.getOrElse(Nil)))) - case Some(o@ObjVal(subName, subParams, subFields)) => - args match - case Some(a) => //FIXME: Unverified - val scrut = Sel(Var("obj"), field) - val branches = selValue.getValue.toList.map(_.asInstanceOf[ObjVal]) - .flatMap(o => { - val lambdaMemFunc = monoer.getFieldVal(o, "apply").asValue.get.asInstanceOf[FuncVal] - val caseVarNm: Var = Var(s"obj$$${o.name}") - Right(NuFunDef(Some(false), Var("obj"), None, Nil, Left(Var(o.name)))(None, None, None, None, None, false, Nil)) :: - List[Either[IfBody,Statement]](Left(IfThen(Var(o.name), App(Var(lambdaMemFunc.name), toTuple(caseVarNm :: a))))) - }) - IfOpApp(scrut, Var("is"), IfBlock(branches)) - case None => - IfThen(App(Var(name), toTuple(params.map(k => Var(k)).toList)), field) - case Some(LiteralVal(v)) => - IfThen(Var(name), v match - case lit: Lit => lit - case bool: Boolean => if bool then Var("true") else Var("false")) - case None => - if selValue.getValue.size > 1 - then - IfThen(App(Var(name), toTuple(params.map(k => Var(k)).toList)), field) - else throw MonomorphError(s"Selection of field ${field} from object ${o} results in no values") - case _ => utils.die - } - valSetToBranches(next, Left(branchCase) :: acc) - // case t@TupVal(fields) => - // val selValue = fields.getOrElse(field, throw MonomorphError(s"Invalid field selection ${field} from Tuple")) - case _ => utils.die - - - val ret = term match - case x: (Lit | Var) => x - case App(sel@Sel(receiver, field), args) => - debug.writeLine(s"Specializing ${term}") - debug.indent() - val nuReceiver = defunctionalize(receiver) - val nuArgs = defunctionalize(args) - val ifBlockLines = valSetToBranches(getRes(receiver).getValue.toList)(using field, Some(extractFuncArgs(nuArgs))) - val ifBlk = IfBlock(ifBlockLines) - val res = Let(false, Var("obj"), nuReceiver, - If(IfOpApp(Var("obj"), Var("is"), ifBlk), None)) - debug.writeLine(s"Result: ${res}") - debug.outdent() - res - case App(op@Var(name), args) if builtInOps.contains(name) => - App(op, defunctionalize(args)) - case App(lhs@NuNew(cls), args) => - (cls, args) match { - case (v: Var, args: Tup) => - App(lhs, defunctionalize(args)) - case _ => ??? - } - case App(callee, args) => - if(getRes(callee).getValue.find(_.isInstanceOf[ObjVal]).isDefined) - defunctionalize(App(Sel(callee, Var("apply")), args)) - else - App(defunctionalize(callee), defunctionalize(args)) - case Tup(fields) => Tup(fields.map{ - case (name, Fld(flags, value)) => (name, Fld(flags, defunctionalize(value)))}) - case If(IfThen(expr, rhs), els) => If(IfThen(defunctionalize(expr), defunctionalize(rhs)), els.map(defunctionalize)) - case New(Some((constructor, args)), body) => New(Some((constructor, defunctionalize(args))), body) - case Sel(receiver, fieldName) => - val nuReceiver = defunctionalize(receiver) - if (getRes(receiver).getValue.forall(_.isInstanceOf[ObjVal])) - then - debug.writeLine(s"Specializing ${term}") - debug.indent() - val ifBlockLines = valSetToBranches(getRes(receiver).getValue.toList.sortWith((x, y) => (x.toString > y.toString)))(using fieldName, None) - val ifBlk = IfBlock(ifBlockLines) - val res = Let(false, Var("obj"), nuReceiver, - If(IfOpApp(Var("obj"), Var("is"), ifBlk), None) - ) - debug.writeLine(s"Result: ${res}") - debug.outdent() - res - else - Sel(nuReceiver, fieldName) - case Blk(stmts) => - Blk(stmts.map{ - case t: Term => defunctionalize(t) - case other => other - }) - case Bra(false, term) => Bra(false, defunctionalize(term)) - case _ => throw MonomorphError(s"Cannot Defunctionalize ${term}") - ret - } -} diff --git a/compiler/shared/main/scala/mlscript/compiler/simpledef/Simpledef.scala b/compiler/shared/main/scala/mlscript/compiler/simpledef/Simpledef.scala new file mode 100644 index 00000000..4b59f694 --- /dev/null +++ b/compiler/shared/main/scala/mlscript/compiler/simpledef/Simpledef.scala @@ -0,0 +1,455 @@ +package mlscript +package compiler +package simpledef + +import mlscript.utils.*, shorthands.* +import scala.collection.mutable +import java.util.IdentityHashMap +import scala.collection.JavaConverters._ + +type TypeVar +type TermId = Uid[Term] +type TypeVarId = Uid[TypeVar] +type Cnstr = ProdStrat -> ConsStrat + +/** Performs defunctionalization on selections on objects using simple-sub as for control-flow analysis. + * First we traverse the program and process all terms, constraining them to Producers and Consumers. + * During the constraining, we keep track of the input points of selection terms. + * Lastly, we rewrite selection terms by generating pattern matches on their possible inputs. + */ + +enum ProdStrat(using val euid: TermId) { + case NoProd()(using TermId) + case ProdObj(ctor: Option[Var], fields: Ls[Var -> ProdStrat], parents: Ls[ProdStrat] = Nil)(using TermId) extends ProdStrat, ProdObjImpl + case ProdFun(lhs: ConsStrat, rhs: ProdStrat)(using TermId) + case ProdVar(uid: TypeVarId, name: String)(boundary: Option[Var] = None)(using TermId) + case ProdTup(fields: Ls[ProdStrat])(using TermId) +} +enum ConsStrat(using val euid: TermId) { + case NoCons()(using TermId) + case ConsObj(name: Option[Var], fields: Ls[Var -> ConsStrat])(using TermId) extends ConsStrat, ConsObjImpl + case ConsFun(lhs: ProdStrat, rhs: ConsStrat)(using TermId) + case ConsVar(uid: TypeVarId, name: String)(boundary: Option[Var] = None)(using TermId) + case ConsTup(fields: Ls[ConsStrat])(using TermId) +} +import ProdStrat.*, ConsStrat.* + +trait ConsObjImpl { self: ConsObj => + var selectionSource: Set[ProdStrat] = Set() +} + +trait ProdObjImpl { self: ProdObj => + var objDestination: Set[ConsStrat] = Set() +} + +class Context( + variables: Map[Var, ProdVar], + classes: Map[Var, ProdObj], +) { + def apply(v: Var): ProdVar = + variables(v) + def ++(other: IterableOnce[(Var, ProdVar)]): Context = + Context(variables ++ other, classes) + def +(other: (Var -> ProdVar)): Context = + Context(variables + other, classes) + override def toString(): String = + s"${variables}" +} + +class SimpleDef(debug: Debug) { + + extension (t: Term) { + def uid = termMap.getOrElse(t, { + val id = euid.nextUid + termMap.addOne((t, euid.nextUid)) + id + }) + } + + val termMap = new IdentityHashMap[Term, TermId]().asScala + val varsName = mutable.Map.empty[TypeVarId, Str] + val vuid = Uid.TypeVar.State() + val euid = Uid.Term.State() + val noExprId = euid.nextUid + + def freshVar(n: String)(using TermId): (ProdVar, ConsVar) = + val vid = vuid.nextUid + val pv = ProdVar(vid, n)() + val cv = ConsVar(vid, n)() + varsName += vid -> n + (pv, cv) + def freshVar(n: Var)(using TermId): (ProdVar, ConsVar) = + freshVar(n.name) + + def apply(p: TypingUnit)(using ctx: Context = Context(Map(), Map())): (Ls[Var -> ProdStrat], ProdStrat) = + // Top-level def prototypes + val vars: Map[Var, ProdVar] = p.rawEntities.collect { + case fun: NuFunDef => + fun.nme -> freshVar(fun.name)(using noExprId)._1 + }.toMap + // Top-level constructor prototypes + val constructorPrototypes: Map[Var, Cnstr] = p.rawEntities.collect { + case ty: NuTypeDef => + ty.nameVar -> freshVar(ty.nameVar)(using noExprId) + }.toMap + // Prototypes of constructor outputs, used for inheritance + val objectPrototypes: Map[Var, Cnstr] = p.rawEntities.collect { + case ty: NuTypeDef => + ty.nameVar -> freshVar(ty.nameVar)(using noExprId) + }.toMap + val fullCtx = ctx ++ vars ++ constructorPrototypes.map((v, s) => (v, s._1.asInstanceOf[ProdVar])) + val classes: Map[Var, ProdObj] = p.rawEntities.collect { + case ty: NuTypeDef => + debug.writeLine(s"Completing type info for class ${ty.nameVar} with ctors ${constructorPrototypes.map((v, s) => (v, s._1))}") + given TermId = noExprId + val argTup = ty.params.getOrElse(Tup(Nil)) + val (pList, cList) = argTup.fields.map{ + case (Some(v), Fld(flags, _)) if flags.genGetter => + val fldVar = freshVar(s"${argTup.uid}_${v.name}")(using noExprId) + ((v, fldVar._1), (v, fldVar._2)) + case (Some(v), Fld(flags, _)) if !flags.genGetter => + lastWords(s"Non val ${v} class parameter is not supported.") + case other => + lastWords(s"${other} class parameter is not supported.") + }.unzip + val bodyStrats = apply(ty.body)(using fullCtx ++ pList.toMap) + val parents = ty.parents.map{ + case v: Var => objectPrototypes(v)._1 + case App(v: Var, _) => objectPrototypes(v)._1 + case other => lastWords(s"Unsupported inheritance pattern ${other}") + } + if parents.length > 1 then lastWords(s"Multiple Inheritance at ${ty} not supported yet") + ty.kind match + case Cls => + ty.ctor match + case None => + val obj = ProdObj(Some(ty.nameVar), bodyStrats._1 ++ pList, parents) + val func = ProdFun(ConsTup(cList.map(_._2)),obj) + constrain(func, constructorPrototypes(ty.nameVar)._2) + constrain(obj, objectPrototypes(ty.nameVar)._2) + ty.nameVar -> obj + case Some(Constructor(t @ Tup(params), body)) => + val mapping = params.map{ + case (None, Fld(_, v: Var)) => + (v, freshVar(s"${t.uid}_${v.name}")(using noExprId)) + case (Some(v1: Var), Fld(_, v2: Var)) => + (v1, freshVar(s"${t.uid}_${v1.name}")(using noExprId)) + case other => + lastWords(s"Unsupported term ${other}") + } + process(body)(using fullCtx ++ pList.toMap ++ mapping.map((i, s) => (i, s._1))) + val obj = ProdObj(Some(ty.nameVar), bodyStrats._1 ++ pList, parents) + val func = ProdFun(ConsTup(mapping.map(_._2._2)),obj) + constrain(func, constructorPrototypes(ty.nameVar)._2) + constrain(obj, objectPrototypes(ty.nameVar)._2) + ty.nameVar -> obj + case Mod => + val obj = ProdObj(Some(ty.nameVar), bodyStrats._1 ++ pList, parents) + constrain(obj, constructorPrototypes(ty.nameVar)._2) + constrain(obj, objectPrototypes(ty.nameVar)._2) + ty.nameVar -> obj + case other => + lastWords(s"Unsupported class kind ${other}") + }.toMap + val tys = p.rawEntities.flatMap{ + case f: NuFunDef => { + f.rhs match + case Left(value) => + val p = process(value)(using fullCtx) + val v = vars(f.nme) + constrain(p, ConsVar(v.uid, v.name)()(using noExprId)) + Some(p) + case Right(value) => None + } + case t: Term => { + val topLevelProd = process(t)(using fullCtx) + Some(topLevelProd) + } + case other => { + debug.writeLine(s"Skipping ${other}") + None + } + } + (vars.toList, tys.lastOption.getOrElse(ProdObj(Some(Var("prim$Unit")), Nil)(using noExprId))) + + val termToProdType = mutable.Map.empty[TermId, ProdStrat] + // Selection terms -> Object types that they Consume + val selTermToType = mutable.Map.empty[TermId, ConsObj] + + def builtinOps: Map[Var, ProdFun] = { + given TermId = noExprId + Map( + (Var("+") -> ProdFun(ConsTup(List(ConsObj(Some(Var("prim$Int")), Nil),ConsObj(Some(Var("prim$Int")), Nil))), ProdObj(Some(Var("prim$Int")), Nil))), + (Var("-") -> ProdFun(ConsTup(List(ConsObj(Some(Var("prim$Int")), Nil),ConsObj(Some(Var("prim$Int")), Nil))), ProdObj(Some(Var("prim$Int")), Nil))), + (Var("*") -> ProdFun(ConsTup(List(ConsObj(Some(Var("prim$Int")), Nil),ConsObj(Some(Var("prim$Int")), Nil))), ProdObj(Some(Var("prim$Int")), Nil))), + (Var(">") -> ProdFun(ConsTup(List(ConsObj(Some(Var("prim$Int")), Nil),ConsObj(Some(Var("prim$Int")), Nil))), ProdObj(Some(Var("prim$Bool")), Nil))), + (Var("==") -> ProdFun(ConsTup(List(ConsObj(Some(Var("prim$Int")), Nil),ConsObj(Some(Var("prim$Int")), Nil))), ProdObj(Some(Var("prim$Bool")), Nil))), + (Var("is") -> ProdFun(ConsTup(List(freshVar("is$rhs")._2,freshVar("is$lhs")._2)), ProdObj(Some(Var("prim$Bool")), Nil))), + (Var("concat") -> ProdFun(ConsTup(List(ConsObj(Some(Var("prim$String")), Nil))), ProdFun(ConsTup(List(ConsObj(Some(Var("prim$String")), Nil))), ProdObj(Some(Var("prim$String")), Nil)))), + (Var("log") -> ProdFun(ConsTup(List(ConsObj(Some(Var("prim$String")), Nil))), ProdObj(Some(Var("prim$Unit")), Nil))), + (Var("toString") -> ProdFun(ConsTup(List(ConsObj(Some(Var("prim$Int")), Nil))), ProdObj(Some(Var("prim$String")), Nil))) + ) + } + + def process(t: Term)(using ctx: Context): ProdStrat = + debug.writeLine(s"Processing term ${t}") + debug.indent() + val res: ProdStrat = t match + case IntLit(_) => ProdObj(Some(Var("prim$Int")), Nil)(using t.uid) + case StrLit(_) => ProdObj(Some(Var("prim$String")), Nil)(using t.uid) + case UnitLit(_) => ProdObj(Some(Var("prim$Unit")), Nil)(using t.uid) + case Var("true") | Var("false") => ProdObj(Some(Var("prim$Bool")), Nil)(using t.uid) + case v @ Var(id) if builtinOps.contains(v) => + builtinOps(v) + case v @ Var(id) => + ctx(v).copy()(Some(v))(using t.uid) + case Asc(trm, ty) => + // TODO: Enforce type ascription? + process(trm) + case Let(isRec, nme, rhs, body) => + val rhsRes = process(rhs) + val sv = freshVar(s"${t.uid}_let")(using t.uid) + constrain(rhsRes, sv._2) + process(body)(using ctx + (nme -> sv._1)) + case NuNew(cls) => + process(App(NuNew(cls), Tup(Nil).withLoc(t.toLoc.map(_.right)))) + case App(NuNew(cls), arg) => + val clsRes = process(cls) + val argRes = process(arg) + val sv = freshVar(s"${t.uid}_callres")(using t.uid) + constrain(clsRes, ConsFun(argRes, sv._2)(using noExprId)) + sv._1 + case App(func, arg) => + val funcRes = process(func) + val argRes = process(arg) + val sv = freshVar(s"${t.uid}_callres")(using t.uid) + constrain(funcRes, ConsFun(argRes, sv._2)(using noExprId)) + sv._1 + case Lam(t @ Tup(args), body) => + val mapping = args.map{ + case (None, Fld(_, v: Var)) => + (v, freshVar(s"${t.uid}_${v.name}")(using noExprId)) + case (Some(v1: Var), Fld(_, v2: Var)) => + (v1, freshVar(s"${t.uid}_${v1.name}")(using noExprId)) + case other => + lastWords(s"Unsupported term ${other}") + } + ProdFun(ConsTup(mapping.map(_._2._2))(using t.uid), + process(body)(using ctx ++ mapping.map((i, s) => (i, s._1))))(using t.uid) + case If(IfThen(scrut, thenn), S(elze)) => + constrain(process(scrut), ConsObj(Some(Var("prim$Bool")), Nil)(using noExprId)) + val res = freshVar(s"${t.uid}_ifres")(using t.uid) + constrain(process(thenn), res._2) + constrain(process(elze), res._2) + res._1 + case elf: If => + elf.desugaredTerm match { + case S(desugared) => process(desugared) + case N => lastWords(s"Undesugared UCS term ${t} found") + } + case Tup(fields) => + val mapping = fields.map{ + case (None, Fld(_, fieldTerm: Term)) => + process(fieldTerm) + case other => lastWords(s"Unsupported tuple structure ${other}") + } + ProdTup(mapping)(using t.uid) + case Sel(receiver, fieldName) => + val selRes = freshVar(s"${t.uid}_selres")(using t.uid) + val selector = ConsObj(None, List(fieldName -> selRes._2))(using t.uid) + constrain(process(receiver), selector) + selTermToType += (t.uid -> selector.asInstanceOf[ConsObj]) + selRes._1 + case Bra(true, t) => + process(t) + case Rcd(fields) => + ProdObj(None, fields.map{ + case (v, Fld(_, t)) => (v -> process(t)) + })(using t.uid) + case Blk(stmts) => + apply(TypingUnit(stmts))._2 + case Bra(false, term) => + process(term) + case CaseOf(trm, cases) => // TODO: Complete constraining in conjunction with processCases + ??? + case Eqn(lhs, rhs) => + process(lhs) + process(rhs) + case other => lastWords(s"Unsupported term ${other}") + + debug.outdent() + registerTermToType(t, res) + + // TODO: Complete constraining for CaseBranches after implementing negative types and intersections + def processCases(scrut: ProdVar, cs: CaseBranches)(using ctx: Context, resCons: ConsVar): Unit = + cs match + case Wildcard(body) => + constrain(process(body), resCons) + case NoCases => () + case Case(pat, body, rest) => ??? + + + val constraintCache = mutable.Set.empty[(ProdStrat, ConsStrat)] + val upperBounds = mutable.Map.empty[TypeVarId, Ls[ConsStrat]].withDefaultValue(Nil) + val lowerBounds = mutable.Map.empty[TypeVarId, Ls[ProdStrat]].withDefaultValue(Nil) + + def constrain(prod: ProdStrat, cons: ConsStrat): Unit = { + debug.writeLine(s"constraining ${prod} -> ${cons}") + if (constraintCache.contains(prod -> cons)) return () else constraintCache += (prod -> cons) + + (prod, cons) match + case (ProdVar(v, pn), ConsVar(w, cn)) + if v === w => () + case (pv@ProdVar(v, _), _) => + cons match { + case c: ConsObj if lowerBounds(v).isEmpty => + c.selectionSource = c.selectionSource + pv + case _ => () + } + upperBounds += v -> (cons :: upperBounds(v)) + lowerBounds(v).foreach(lb_strat => constrain(lb_strat, cons)) + case (_, cv@ConsVar(v, _)) => + prod match { + case p: ProdObj if upperBounds(v).isEmpty => + p.objDestination = p.objDestination + cv + case _ => () + } + lowerBounds += v -> (prod :: lowerBounds(v)) + upperBounds(v).foreach(ub_strat => constrain(prod, ub_strat)) + case (ProdFun(lhs1, rhs1), ConsFun(lhs2, rhs2)) => + constrain(lhs2, lhs1) + constrain(rhs1, rhs2) + case (pt@ProdTup(fields1), ct@ConsTup(fields2)) => + if pt.fields.length != ct.fields.length + then lastWords("Tuple size mismatch") + (fields1 zip fields2).map((p, c) => + constrain(p, c) + ) + case (pv@ProdObj(nme1, fields1, parents), cv@ConsObj(nme2, fields2)) => + nme2 match + case Some(name) if name != nme1.get => lastWords(s"Could not constrain ${(prod -> cons)}") + case _ => () + fields2.map((key, res2) => { + fields1.find(_._1 == key) match + case None => + debug.writeLine("field not found, try parent") + // TODO: Handle multiple inheritance properly, currently assume only one parent + parents match + case head :: next => + constrain(head, cv) + case Nil => + lastWords(s"Could not constrain ${(prod -> cons)}") + case Some((_, res1)) => + cv.selectionSource = cv.selectionSource + pv + pv.objDestination = pv.objDestination + cv + constrain(res1, res2) + }) + case (pv@ProdTup(fields1), cv@ConsObj(nme2, fields2)) => + nme2 match + case Some(name) => lastWords(s"Could not constrain ${(prod -> cons)}") + case _ => () + fields2.map((key, res2) => { + cv.selectionSource = cv.selectionSource + pv + constrain(fields1(key.name.toInt), res2) + }) + case other => lastWords(s"Could not constrain ${other}") + } + + // Selection terms -> Producers that they consume + lazy val selToResTypes: Map[TermId, Set[ProdStrat]] = selTermToType.map((termId, cons) => + (termId, cons.selectionSource) + ).toMap + + // Rewrite terms, replacing selections with pattern matches if they only select on objects + def rewriteTerm(t: Term): Term = + def objSetToMatchBranches(receiver: Var, fieldName: Var, objSet: List[ProdObj], acc: CaseBranches = NoCases)(using funcApp: Option[Term] = None): CaseBranches = + objSet match + case Nil => acc + case (p :: rest) if p.ctor.isDefined => + if funcApp.isDefined + then objSetToMatchBranches(receiver, fieldName, rest, Case(p.ctor.get, App(Sel(receiver, fieldName), funcApp.get), acc)(false)) + else objSetToMatchBranches(receiver, fieldName, rest, Case(p.ctor.get, Sel(receiver, fieldName), acc)(false)) + case other => lastWords(s"Unexpected ${other}") + t match + case Var(_) | IntLit(_) | UnitLit(_) | StrLit(_) => t + // TODO: Remove the following case when eta expansion is supported, currently a workaround. + case App(t @ Sel(receiver, fieldName), arg) => + if (selToResTypes(t.uid).forall{ + case _: ProdVar => true + case x: ProdObj if x.ctor.isDefined => true + case _ => false + }) { + val letName = Var(s"selRes$$${t.uid}") + Let(false, letName, rewriteTerm(receiver), + CaseOf(letName, objSetToMatchBranches(letName, fieldName, selToResTypes(t.uid).collect{case x: ProdObj => x}.toList)(using Some(rewriteTerm(arg)))) + ) + } else { + debug.writeLine(s"${selToResTypes(t.uid)}") + Sel(rewriteTerm(receiver), fieldName) + } + case App(func, arg) => + App(rewriteTerm(func), rewriteTerm(arg)) + case Lam(t @ Tup(args), body) => + Lam(rewriteTerm(t), rewriteTerm(body)) + case If(IfThen(scrut, thenn), S(elze)) => + If(IfThen(rewriteTerm(scrut), rewriteTerm(thenn)), S(rewriteTerm(elze))) + case Tup(fields) => + Tup(fields.map{ + case (x, Fld(flags, fieldTerm: Term)) => + (x, Fld(flags, rewriteTerm(fieldTerm))) + }) + case Sel(receiver, fieldName) => + if (selToResTypes(t.uid).forall{ + case _: ProdVar => true + case x: ProdObj if x.ctor.isDefined => true + case _ => false + }) { + val letName = Var(s"selRes$$${t.uid}") + Let(false, letName, rewriteTerm(receiver), + CaseOf(letName, objSetToMatchBranches(letName, fieldName, selToResTypes(t.uid).collect{case x: ProdObj => x}.toList)) + ) + } else { + debug.writeLine(s"${selToResTypes(t.uid)}") + Sel(rewriteTerm(receiver), fieldName) + } + case Bra(true, t) => + Bra(true, rewriteTerm(t)) + case Rcd(fields) => + Rcd(fields.map{ + case (v, Fld(flags, t)) => (v, Fld(flags, rewriteTerm(t))) + }) + case Blk(stmts) => + Blk(rewriteStatements(stmts)) + case Bra(false, term) => + Bra(false, rewriteTerm(term)) + case NuNew(cls) => + NuNew(rewriteTerm(cls)) + case other => lastWords(s"Unsupported term ${other}") + + def rewriteStatements(stmts: List[Statement]): List[Statement] = + stmts.map{ + case ty: NuTypeDef => + ty.copy(body = rewriteProgram(ty.body))(ty.declareLoc, ty.abstractLoc, ty.annotations) + case f: NuFunDef => + f.copy(rhs = f.rhs match + case Left(t) => Left(rewriteTerm(t)) + case Right(_) => f.rhs + )(f.declareLoc, f.virtualLoc, f.mutLoc, f.signature, f.outer, f.genField, f.annotations) + case t: Term => + rewriteTerm(t) + case other => lastWords(s"Unsupported term ${other}") + } + def rewriteProgram(t: TypingUnit): TypingUnit = + TypingUnit(rewriteStatements(t.rawEntities)) + + private def registerTermToType(t: Term, s: ProdStrat) = { + termToProdType.get(t.uid) match { + case None => { + termToProdType += t.uid -> s + s + } + case Some(value) => + lastWords(s"${t} registered two prod strategies:\n already has ${value}, but got ${s}") + } + } +} diff --git a/compiler/shared/main/scala/mlscript/compiler/simpledef/Uid.scala b/compiler/shared/main/scala/mlscript/compiler/simpledef/Uid.scala new file mode 100644 index 00000000..2d2c4ade --- /dev/null +++ b/compiler/shared/main/scala/mlscript/compiler/simpledef/Uid.scala @@ -0,0 +1,18 @@ +package mlscript +package compiler +package simpledef + +opaque type Uid[T] = Int + +object Uid: + class Handler[T]: + class State: + private val uidStore = scala.collection.mutable.Map.empty[String, Uid[T]] + def nextUid: Uid[T] = nextUid("") + def nextUid(key: String): Uid[T] = + uidStore.updateWith(key) { + case None => Some(0) + case Some(v) => Some(v + 1) + }.get + object TypeVar extends Handler[TypeVar] + object Term extends Handler[Term] \ No newline at end of file diff --git a/compiler/shared/test/diff/Defunctionalize/ClassConstructor.mls b/compiler/shared/test/diff/Defunctionalize/ClassConstructor.mls new file mode 100644 index 00000000..b511441e --- /dev/null +++ b/compiler/shared/test/diff/Defunctionalize/ClassConstructor.mls @@ -0,0 +1,82 @@ +:NewDefs + +class X { + val num = 5 +} +class Y(val num: Int) { + constructor(y: Int) { + num = y+5 + } +} +let y = new Y(6) +let x = new X +(if true then y else x).num +//│ class X { +//│ constructor() +//│ val num: 5 +//│ } +//│ class Y(num: Int) { +//│ constructor(y: Int) +//│ } +//│ let y: Y +//│ let x: X +//│ Int +//│ +//│ Simpledef: +//│ {class X {let num = 5} +//│ class Y(val num: Int,) {} +//│ let y = (new Y)(6,) +//│ let x = new X +//│ let selRes$40 = '(' if (true) then y else x ')' in case selRes$40 of { Y => (selRes$40).num +//│ X => (selRes$40).num }} +//│ End simpledef +//│ +//│ y +//│ = Y {} +//│ x +//│ = X {} +//│ res +//│ = 11 + +class Z(val num1: Int) { + constructor(y, x) { + num1 = y+x + } +} +class W(val num1: Int, val num2: Int) { + constructor(w) { + num1 = w + num2 = w + } +} +val w = new W(3) +val z = new Z(6, 11) +(if true then w else z).num1 +//│ class Z(num1: Int) { +//│ constructor(y: Int, x: Int) +//│ } +//│ class W(num1: Int, num2: Int) { +//│ constructor(w: Int) +//│ } +//│ val w: W +//│ val z: Z +//│ Int +//│ +//│ Simpledef: +//│ {class Z(val num1: Int,) {} +//│ class W(val num1: Int, val num2: Int,) {} +//│ let w = (new W)(3,) +//│ let z = (new Z)(6, 11,) +//│ let selRes$58 = '(' if (true) then w else z ')' in case selRes$58 of { W => (selRes$58).num1 +//│ Z => (selRes$58).num1 }} +//│ End simpledef +//│ +//│ w +//│ = W {} +//│ z +//│ = Z {} +//│ res +//│ = 3 + + + diff --git a/compiler/shared/test/diff/Defunctionalize/Classes.mls b/compiler/shared/test/diff/Defunctionalize/Classes.mls index 880e8993..2074af8a 100644 --- a/compiler/shared/test/diff/Defunctionalize/Classes.mls +++ b/compiler/shared/test/diff/Defunctionalize/Classes.mls @@ -1,12 +1,11 @@ :NewDefs -:mono -class Bar(x: Int) { +class Bar(val x: Int) { fun foo(x) = x fun FooMinus(y: Int) = x + y fun car = foo(2) } -class Car { +class Car() { fun da(b: Bar) = b.foo(2) } fun baz(b: Bar) = b.foo(2) @@ -14,49 +13,32 @@ let bar = Bar(42) baz(bar) (Car()).da(Bar(1337)) bar.car -//│ Lifted: -//│ TypingUnit { -//│ class Bar$1([x: Int,]) { -//│ fun foo = (x,) => x -//│ fun FooMinus = (y: Int,) => +((this).x, y,) -//│ fun car = (this).foo(2,) -//│ } -//│ class Car$2([]) {fun da = (b: Bar$1,) => (b).foo(2,)} -//│ fun baz$2 = (b: Bar$1,) => (b).foo(2,) -//│ let bar$1 = Bar$1(42,) -//│ Code(List(baz$2(bar$1,))) -//│ Code(List(('(' Car$2() ')').da(Bar$1(1337,),))) -//│ Code(List((bar$1).car)) +//│ class Bar(x: Int) { +//│ fun FooMinus: (y: Int) -> Int +//│ fun car: 2 +//│ fun foo: forall 'a. 'a -> 'a //│ } -//│ Mono: -//│ TypingUnit { -//│ class Bar$1([x: Int,]) {} -//│ class Car$2([]) {} -//│ let bar$1 = Bar$1(42,) -//│ fun da$Car$2 = (this, b: Bar$1,) => let obj = b in if obj is ‹(Bar$1) then foo$Bar$1(obj, 2,); else error› -//│ fun main$$6 = () => let obj = bar$1 in if obj is ‹(Bar$1) then car$Bar$1(obj,); else error› -//│ fun foo$Bar$1 = (this, x,) => x -//│ fun baz$2 = (b: Bar$1,) => let obj = b in if obj is ‹(Bar$1) then foo$Bar$1(obj, 2,); else error› -//│ fun main$$5 = () => let obj = '(' Car$2() ')' in if obj is ‹(Car$2) then da$Car$2(obj, Bar$1(1337,),); else error› -//│ fun main$$4 = () => baz$2(bar$1,) -//│ fun car$Bar$1 = (this,) => let obj = this in if obj is ‹(Bar$1) then foo$Bar$1(obj, 2,); else error› -//│ Code(List(main$$4())) -//│ Code(List(main$$5())) -//│ Code(List(main$$6())) +//│ class Car() { +//│ fun da: (b: Bar) -> 2 //│ } -//│ class Bar$1(x: Int) -//│ class Car$2() -//│ let bar$1: Bar$1 -//│ fun da$Car$2: forall 'a. (anything, b: Bar$1) -> (2 | 'a) -//│ fun main$$6: forall 'a. () -> (2 | 'a) -//│ fun foo$Bar$1: forall 'a. (anything, 'a) -> (2 | 'a) -//│ fun baz$2: (b: Bar$1) -> 2 -//│ fun main$$5: () -> 2 -//│ fun main$$4: () -> 2 -//│ fun car$Bar$1: forall 'a. Object -> (2 | 'a) -//│ forall 'a. 2 | 'a -//│ bar$1 -//│ = Bar$1 {} +//│ fun baz: (b: Bar) -> 2 +//│ let bar: Bar +//│ 2 +//│ +//│ Simpledef: +//│ {class Bar(val x: Int,) {fun foo = (x::0,) => x +//│ fun FooMinus = (y: Int,) => +(x, y,) +//│ fun car = foo(2,)} +//│ class Car() {fun da = (b: Bar,) => let selRes$34 = b in case selRes$34 of { Bar => (selRes$34).foo(2,) }} +//│ fun baz = (b: Bar,) => let selRes$48 = b in case selRes$48 of { Bar => (selRes$48).foo(2,) } +//│ let bar = Bar(42,) +//│ baz(bar,) +//│ let selRes$76 = '(' Car() ')' in case selRes$76 of { Car => (selRes$76).da(Bar(1337,),) } +//│ let selRes$98 = bar in case selRes$98 of { Bar => (selRes$98).car }} +//│ End simpledef +//│ +//│ bar +//│ = Bar {} //│ res //│ = 2 //│ res diff --git a/compiler/shared/test/diff/Defunctionalize/ClosureCapture.mls b/compiler/shared/test/diff/Defunctionalize/ClosureCapture.mls index 70023100..cecf289a 100644 --- a/compiler/shared/test/diff/Defunctionalize/ClosureCapture.mls +++ b/compiler/shared/test/diff/Defunctionalize/ClosureCapture.mls @@ -1,68 +1,31 @@ :NewDefs :AllowRuntimeErrors -// FIXME: Class parameters not annotated -:mono fun foo(x) = (f => f(x))(z => z+1) foo(2) -//│ Lifted: -//│ TypingUnit { -//│ class Lambda1$2$1([x,]) {fun apply = (f,) => f((this).x,)} -//│ class Lambda1$3$2([]) {fun apply = (z,) => +(z, 1,)} -//│ fun foo$1 = (x,) => {'(' {Lambda1$2$1(x,)} ')'({Lambda1$3$2()},)} -//│ Code(List(foo$1(2,))) -//│ } -//│ Mono: -//│ TypingUnit { -//│ class Lambda1$2$1([x,]) {} -//│ class Lambda1$3$2([]) {} -//│ fun apply$Lambda1$2$1 = (this, f,) => let obj = f in if obj is ‹(Lambda1$3$2) then apply$Lambda1$3$2(obj, let obj = this in if obj is ‹(Lambda1$2$1) then 2; else error›,); else error› -//│ fun foo$1 = (x,) => {let obj = '(' {Lambda1$2$1(x,)} ')' in if obj is ‹(Lambda1$2$1) then apply$Lambda1$2$1(obj, {Lambda1$3$2()},); else error›} -//│ fun main$$3 = () => foo$1(2,) -//│ fun apply$Lambda1$3$2 = (this, z,) => +(z, 1,) -//│ Code(List(main$$3())) -//│ } -//│ ╔══[ERROR] Class parameters currently need type annotations -//│ ║ l.7: (f => f(x))(z => z+1) -//│ ╙── ^ -//│ class Lambda1$2$1(x: error) -//│ class Lambda1$3$2() -//│ fun apply$Lambda1$2$1: (Object, Object) -> Int -//│ fun foo$1: error -> Int -//│ fun main$$3: () -> Int -//│ fun apply$Lambda1$3$2: (anything, Int) -> Int +//│ fun foo: Int -> Int //│ Int +//│ +//│ Simpledef: +//│ {fun foo = (x::0,) => {'(' (f::1,) => f(x,) ')'((z::2,) => +(z, 1,),)} +//│ foo(2,)} +//│ End simpledef +//│ //│ res //│ = 3 -// FIXME: Class parameters not annotated -:mono fun f(x) = (y => f(x+y))(x+1) f(1) -//│ Lifted: -//│ TypingUnit { -//│ class Lambda1$2$1([x,]) {fun apply = (y,) => f$1(+((this).x, y,),)} -//│ fun f$1 = (x,) => {'(' {Lambda1$2$1(x,)} ')'(+(x, 1,),)} -//│ Code(List(f$1(1,))) -//│ } -//│ Mono: -//│ TypingUnit { -//│ class Lambda1$2$1([x,]) {} -//│ fun apply$Lambda1$2$1 = (this, y,) => f$1(+(let obj = this in if obj is ‹(Lambda1$2$1) then 1; else error›, y,),) -//│ fun f$1 = (x,) => {let obj = '(' {Lambda1$2$1(x,)} ')' in if obj is ‹(Lambda1$2$1) then apply$Lambda1$2$1(obj, +(x, 1,),); else error›} -//│ fun main$$2 = () => f$1(1,) -//│ Code(List(main$$2())) -//│ } -//│ ╔══[ERROR] Class parameters currently need type annotations -//│ ║ l.42: (y => f(x+y))(x+1) -//│ ╙── ^ -//│ class Lambda1$2$1(x: error) -//│ fun apply$Lambda1$2$1: (Object, Int) -> nothing -//│ fun f$1: nothing -> nothing -//│ fun main$$2: () -> nothing +//│ fun f: Int -> nothing //│ nothing +//│ +//│ Simpledef: +//│ {fun f = (x::3,) => {'(' (y::4,) => f(+(x, y,),) ')'(+(x, 1,),)} +//│ f(1,)} +//│ End simpledef +//│ //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded diff --git a/compiler/shared/test/diff/Defunctionalize/Constructor.mls b/compiler/shared/test/diff/Defunctionalize/Constructor.mls index 0df2adb2..3c29d5fe 100644 --- a/compiler/shared/test/diff/Defunctionalize/Constructor.mls +++ b/compiler/shared/test/diff/Defunctionalize/Constructor.mls @@ -1,7 +1,5 @@ :NewDefs -// FIXME: Preserve local state in classes -:mono class X() { val a = log("ok") @@ -12,43 +10,24 @@ val object = X() (new X()).a object.a object.a -//│ Lifted: -//│ TypingUnit { -//│ class X$1([]) {let a = {log("ok",); 6}} -//│ let object$1 = X$1() -//│ Code(List(('(' (new X$1)() ')').a)) -//│ Code(List(('(' (new X$1)() ')').a)) -//│ Code(List((object$1).a)) -//│ Code(List((object$1).a)) +//│ class X() { +//│ val a: 6 //│ } -//│ Mono: -//│ TypingUnit { -//│ class X$1([]) {} -//│ let object$1 = X$1() -//│ let a$X$1 = (this,) => {log("ok",); 6} -//│ fun main$$5 = () => let obj = object$1 in if obj is ‹(X$1) then a$X$1(obj,); else error› -//│ fun main$$4 = () => let obj = object$1 in if obj is ‹(X$1) then a$X$1(obj,); else error› -//│ fun main$$3 = () => let obj = '(' (new X$1)() ')' in if obj is ‹(X$1) then a$X$1(obj,); else error› -//│ fun main$$2 = () => let obj = '(' (new X$1)() ')' in if obj is ‹(X$1) then a$X$1(obj,); else error› -//│ Code(List(main$$2())) -//│ Code(List(main$$3())) -//│ Code(List(main$$4())) -//│ Code(List(main$$5())) -//│ } -//│ class X$1() -//│ let object$1: X$1 -//│ let a$X$1: anything -> 6 -//│ fun main$$5: () -> 6 -//│ fun main$$4: () -> 6 -//│ fun main$$3: () -> 6 -//│ fun main$$2: () -> 6 +//│ val object: X //│ 6 -//│ object$1 -//│ = X$1 {} -//│ a$X$1 -//│ = [Function: a$X$1] -//│ res -//│ = 6 +//│ +//│ Simpledef: +//│ {class X() {let a = {log("ok",) +//│ 6}} +//│ let object = X() +//│ let selRes$20 = '(' (new X)() ')' in case selRes$20 of { X => (selRes$20).a } +//│ let selRes$30 = '(' (new X)() ')' in case selRes$30 of { X => (selRes$30).a } +//│ let selRes$40 = object in case selRes$40 of { X => (selRes$40).a } +//│ let selRes$44 = object in case selRes$44 of { X => (selRes$44).a }} +//│ End simpledef +//│ +//│ object +//│ = X {} //│ // Output //│ ok //│ res @@ -61,5 +40,5 @@ object.a //│ ok //│ res //│ = 6 -//│ // Output -//│ ok +//│ res +//│ = 6 diff --git a/compiler/shared/test/diff/Defunctionalize/DelayedEvaluation.mls b/compiler/shared/test/diff/Defunctionalize/DelayedEvaluation.mls index abe59b8b..4e413361 100644 --- a/compiler/shared/test/diff/Defunctionalize/DelayedEvaluation.mls +++ b/compiler/shared/test/diff/Defunctionalize/DelayedEvaluation.mls @@ -1,7 +1,5 @@ :NewDefs -// FIXME: Preserve local state in classes -:mono class G(val num: Int) { val x = log("once on construction") @@ -15,61 +13,38 @@ g.y() g.x g.y() g.x -//│ Lifted: -//│ TypingUnit { -//│ class G$1([val num: Int,]) { -//│ let x = {log("once on construction",); +((this).num, 2,)} -//│ let y = () => {log("once every call",); +((this).num, 2,)} -//│ } -//│ let g$1 = (new G$1)(6,) -//│ Code(List((g$1).y())) -//│ Code(List((g$1).x)) -//│ Code(List((g$1).y())) -//│ Code(List((g$1).x)) +//│ class G(num: Int) { +//│ val x: Int +//│ val y: () -> Int //│ } -//│ Mono: -//│ TypingUnit { -//│ class G$1([val num: Int,]) {} -//│ let y$G$1 = (this,) => {log("once every call",); +(let obj = this in if obj is ‹(G$1) then 6; else error›, 2,)} -//│ let x$G$1 = (this,) => {log("once on construction",); +(let obj = this in if obj is ‹(G$1) then 6; else error›, 2,)} -//│ let g$1 = (new G$1)(6,) -//│ fun main$$5 = () => let obj = g$1 in if obj is ‹(G$1) then x$G$1(obj,); else error› -//│ fun main$$4 = () => let obj = g$1 in if obj is ‹(G$1) then y$G$1(obj,); else error› -//│ fun main$$3 = () => let obj = g$1 in if obj is ‹(G$1) then x$G$1(obj,); else error› -//│ fun main$$2 = () => let obj = g$1 in if obj is ‹(G$1) then y$G$1(obj,); else error› -//│ Code(List(main$$2())) -//│ Code(List(main$$3())) -//│ Code(List(main$$4())) -//│ Code(List(main$$5())) -//│ } -//│ class G$1(num: Int) -//│ let y$G$1: Object -> Int -//│ let x$G$1: Object -> Int -//│ let g$1: G$1 -//│ fun main$$5: () -> Int -//│ fun main$$4: () -> Int -//│ fun main$$3: () -> Int -//│ fun main$$2: () -> Int +//│ val g: G //│ Int -//│ y$G$1 -//│ = [Function: y$G$1] -//│ x$G$1 -//│ = [Function: x$G$1] -//│ g$1 -//│ = G$1 {} +//│ +//│ Simpledef: +//│ {class G(val num: Int,) {let x = {log("once on construction",) +//│ +(num, 2,)} +//│ let y = () => {log("once every call",) +//│ +(num, 2,)}} +//│ let g = (new G)(6,) +//│ let selRes$56 = g in case selRes$56 of { G => (selRes$56).y() } +//│ let selRes$64 = g in case selRes$64 of { G => (selRes$64).x } +//│ let selRes$68 = g in case selRes$68 of { G => (selRes$68).y() } +//│ let selRes$76 = g in case selRes$76 of { G => (selRes$76).x }} +//│ End simpledef +//│ +//│ g +//│ = G {} +//│ // Output +//│ once on construction //│ res //│ = 8 //│ // Output //│ once every call //│ res //│ = 8 -//│ // Output -//│ once on construction //│ res //│ = 8 //│ // Output //│ once every call //│ res //│ = 8 -//│ // Output -//│ once on construction diff --git a/compiler/shared/test/diff/Defunctionalize/Differentiation.mls b/compiler/shared/test/diff/Defunctionalize/Differentiation.mls index 0386e7d9..56a42ef6 100644 --- a/compiler/shared/test/diff/Defunctionalize/Differentiation.mls +++ b/compiler/shared/test/diff/Defunctionalize/Differentiation.mls @@ -2,31 +2,29 @@ :NewDefs -:mono + abstract class Exp() { virtual fun derive(): Exp - virtual fun derive() = Exp() virtual fun getVal: Str - virtual fun getVal = "" } -class Numeric(i: Int) extends Exp { +class Numeric(val i: Int) extends Exp { fun derive() = Numeric(0) fun getNum = i fun getVal = toString(i) } -class Variable(nm: Str) extends Exp { +class Variable(val nm: Str) extends Exp { fun derive() = Numeric(1) fun getVal = nm } -class Sum(lhs: Exp, rhs: Exp) extends Exp { +class Sum(val lhs: Exp, val rhs: Exp) extends Exp { fun derive() = Sum(lhs.derive(), rhs.derive()) fun getVal = concat("(")(concat(concat(concat(lhs.getVal)(" + "))(rhs.getVal))(")")) } -class Mul(lhs: Exp, rhs: Exp) extends Exp { +class Mul(val lhs: Exp, val rhs: Exp) extends Exp { fun derive() = Sum(Mul(lhs.derive(), rhs), Mul(lhs, rhs.derive())) fun getVal = concat("(")(concat(concat(concat(lhs.getVal)(" * "))(rhs.getVal))(")")) } -class Pow(lhs: Variable, rhs: Numeric) extends Exp { +class Pow(val lhs: Variable, val rhs: Numeric) extends Exp { fun derive() = Mul(rhs, Pow(lhs, Numeric(rhs.getNum - 1))) fun getVal = concat("(")(concat(concat(concat(lhs.getVal)(" ^ "))(rhs.getVal))(")")) } @@ -34,87 +32,77 @@ Sum(Variable("x"), Numeric(3)).derive().getVal Mul(Variable("x"), Numeric(3)).derive().getVal Pow(Variable("x"), Numeric(3)).derive().getVal Mul(Pow(Variable("x"), Numeric(2)), Pow(Variable("y"), Numeric(2))).derive().getVal -//│ Lifted: -//│ TypingUnit { -//│ class Exp$1([]) { -//│ fun derive = () -> Exp$1 -//│ fun derive = () => Exp$1() -//│ fun getVal = Str -//│ fun getVal = "" -//│ } -//│ class Numeric$2([i: Int,]): Exp$1 { -//│ fun derive = () => Numeric$2(0,) -//│ fun getNum = (this).i -//│ fun getVal = toString((this).i,) -//│ } -//│ class Variable$3([nm: Str,]): Exp$1 {fun derive = () => Numeric$2(1,); fun getVal = (this).nm} -//│ class Sum$4([lhs: Exp$1, rhs: Exp$1,]): Exp$1 { -//│ fun derive = () => Sum$4(((this).lhs).derive(), ((this).rhs).derive(),) -//│ fun getVal = concat("(",)(concat(concat(concat(((this).lhs).getVal,)(" + ",),)(((this).rhs).getVal,),)(")",),) -//│ } -//│ class Mul$5([lhs: Exp$1, rhs: Exp$1,]): Exp$1 { -//│ fun derive = () => Sum$4(Mul$5(((this).lhs).derive(), (this).rhs,), Mul$5((this).lhs, ((this).rhs).derive(),),) -//│ fun getVal = concat("(",)(concat(concat(concat(((this).lhs).getVal,)(" * ",),)(((this).rhs).getVal,),)(")",),) -//│ } -//│ class Pow$6([lhs: Variable$3, rhs: Numeric$2,]): Exp$1 { -//│ fun derive = () => Mul$5((this).rhs, Pow$6((this).lhs, Numeric$2(-(((this).rhs).getNum, 1,),),),) -//│ fun getVal = concat("(",)(concat(concat(concat(((this).lhs).getVal,)(" ^ ",),)(((this).rhs).getVal,),)(")",),) -//│ } -//│ Code(List(((Sum$4(Variable$3("x",), Numeric$2(3,),)).derive()).getVal)) -//│ Code(List(((Mul$5(Variable$3("x",), Numeric$2(3,),)).derive()).getVal)) -//│ Code(List(((Pow$6(Variable$3("x",), Numeric$2(3,),)).derive()).getVal)) -//│ Code(List(((Mul$5(Pow$6(Variable$3("x",), Numeric$2(2,),), Pow$6(Variable$3("y",), Numeric$2(2,),),)).derive()).getVal)) +//│ abstract class Exp() { +//│ fun derive: () -> Exp +//│ fun getVal: Str +//│ } +//│ class Numeric(i: Int) extends Exp { +//│ fun derive: () -> Numeric +//│ fun getNum: Int +//│ fun getVal: Str +//│ } +//│ class Variable(nm: Str) extends Exp { +//│ fun derive: () -> Numeric +//│ fun getVal: Str +//│ } +//│ class Sum(lhs: Exp, rhs: Exp) extends Exp { +//│ fun derive: () -> Sum +//│ fun getVal: Str +//│ } +//│ class Mul(lhs: Exp, rhs: Exp) extends Exp { +//│ fun derive: () -> Sum +//│ fun getVal: Str //│ } -//│ Mono: -//│ TypingUnit { -//│ class Numeric$2([i: Int,]): Exp$1 {} -//│ class Pow$6([lhs: Variable$3, rhs: Numeric$2,]): Exp$1 {} -//│ class Sum$4([lhs: Exp$1, rhs: Exp$1,]): Exp$1 {} -//│ class Mul$5([lhs: Exp$1, rhs: Exp$1,]): Exp$1 {} -//│ class Variable$3([nm: Str,]): Exp$1 {} -//│ class Exp$1([]) {} -//│ fun getVal$Sum$4 = (this,) => concat("(",)(concat(concat(concat(let obj = let obj = this in if obj is ‹(Sum$4(lhs, rhs,)) then lhs; else error› in if obj is ‹(Mul$5) then getVal$Mul$5(obj,); (Numeric$2) then getVal$Numeric$2(obj,); else error›,)(" + ",),)(let obj = let obj = this in if obj is ‹(Sum$4(lhs, rhs,)) then rhs; else error› in if obj is ‹(Mul$5) then getVal$Mul$5(obj,); (Numeric$2) then getVal$Numeric$2(obj,); else error›,),)(")",),) -//│ fun derive$Pow$6 = (this,) => Mul$5(let obj = this in if obj is ‹(Pow$6(lhs, rhs,)) then rhs; else error›, Pow$6(let obj = this in if obj is ‹(Pow$6(lhs, rhs,)) then lhs; else error›, Numeric$2(-(let obj = let obj = this in if obj is ‹(Pow$6(lhs, rhs,)) then rhs; else error› in if obj is ‹(Numeric$2) then getNum$Numeric$2(obj,); else error›, 1,),),),) -//│ fun getVal$Pow$6 = (this,) => concat("(",)(concat(concat(concat(let obj = let obj = this in if obj is ‹(Pow$6(lhs, rhs,)) then lhs; else error› in if obj is ‹(Variable$3) then getVal$Variable$3(obj,); else error›,)(" ^ ",),)(let obj = let obj = this in if obj is ‹(Pow$6(lhs, rhs,)) then rhs; else error› in if obj is ‹(Numeric$2) then getVal$Numeric$2(obj,); else error›,),)(")",),) -//│ fun derive$Sum$4 = (this,) => Sum$4(let obj = let obj = this in if obj is ‹(Sum$4(lhs, rhs,)) then lhs; else error› in if obj is ‹(Variable$3) then derive$Variable$3(obj,); else error›, let obj = let obj = this in if obj is ‹(Sum$4(lhs, rhs,)) then rhs; else error› in if obj is ‹(Numeric$2) then derive$Numeric$2(obj,); else error›,) -//│ fun main$$9 = () => let obj = let obj = Mul$5(Pow$6(Variable$3("x",), Numeric$2(2,),), Pow$6(Variable$3("y",), Numeric$2(2,),),) in if obj is ‹(Mul$5) then derive$Mul$5(obj,); else error› in if obj is ‹(Sum$4) then getVal$Sum$4(obj,); else error› -//│ fun derive$Numeric$2 = (this,) => Numeric$2(0,) -//│ fun getNum$Numeric$2 = (this,) => let obj = this in if obj is ‹(Numeric$2(i,)) then i; else error› -//│ fun derive$Variable$3 = (this,) => Numeric$2(1,) -//│ fun getVal$Mul$5 = (this,) => concat("(",)(concat(concat(concat(let obj = let obj = this in if obj is ‹(Mul$5(lhs, rhs,)) then lhs; else error› in if obj is ‹(Mul$5) then getVal$Mul$5(obj,); (Numeric$2) then getVal$Numeric$2(obj,); (Pow$6) then getVal$Pow$6(obj,); (Variable$3) then getVal$Variable$3(obj,); else error›,)(" * ",),)(let obj = let obj = this in if obj is ‹(Mul$5(lhs, rhs,)) then rhs; else error› in if obj is ‹(Mul$5) then getVal$Mul$5(obj,); (Numeric$2) then getVal$Numeric$2(obj,); (Pow$6) then getVal$Pow$6(obj,); else error›,),)(")",),) -//│ fun main$$8 = () => let obj = let obj = Pow$6(Variable$3("x",), Numeric$2(3,),) in if obj is ‹(Pow$6) then derive$Pow$6(obj,); else error› in if obj is ‹(Mul$5) then getVal$Mul$5(obj,); else error› -//│ fun main$$7 = () => let obj = let obj = Mul$5(Variable$3("x",), Numeric$2(3,),) in if obj is ‹(Mul$5) then derive$Mul$5(obj,); else error› in if obj is ‹(Sum$4) then getVal$Sum$4(obj,); else error› -//│ fun main$$6 = () => let obj = let obj = Sum$4(Variable$3("x",), Numeric$2(3,),) in if obj is ‹(Sum$4) then derive$Sum$4(obj,); else error› in if obj is ‹(Sum$4) then getVal$Sum$4(obj,); else error› -//│ fun getVal$Variable$3 = (this,) => let obj = this in if obj is ‹(Variable$3(nm,)) then nm; else error› -//│ fun getVal$Numeric$2 = (this,) => toString(let obj = this in if obj is ‹(Numeric$2(i,)) then i; else error›,) -//│ fun derive$Mul$5 = (this,) => Sum$4(Mul$5(let obj = let obj = this in if obj is ‹(Mul$5(lhs, rhs,)) then lhs; else error› in if obj is ‹(Variable$3) then derive$Variable$3(obj,); (Pow$6) then derive$Pow$6(obj,); else error›, let obj = this in if obj is ‹(Mul$5(lhs, rhs,)) then rhs; else error›,), Mul$5(let obj = this in if obj is ‹(Mul$5(lhs, rhs,)) then lhs; else error›, let obj = let obj = this in if obj is ‹(Mul$5(lhs, rhs,)) then rhs; else error› in if obj is ‹(Numeric$2) then derive$Numeric$2(obj,); (Pow$6) then derive$Pow$6(obj,); else error›,),) -//│ Code(List(main$$6())) -//│ Code(List(main$$7())) -//│ Code(List(main$$8())) -//│ Code(List(main$$9())) +//│ class Pow(lhs: Variable, rhs: Numeric) extends Exp { +//│ fun derive: () -> Mul +//│ fun getVal: Str //│ } -//│ class Numeric$2(i: Int) extends Exp$1 -//│ class Pow$6(lhs: Variable$3, rhs: Numeric$2) extends Exp$1 -//│ class Sum$4(lhs: Exp$1, rhs: Exp$1) extends Exp$1 -//│ class Mul$5(lhs: Exp$1, rhs: Exp$1) extends Exp$1 -//│ class Variable$3(nm: Str) extends Exp$1 -//│ class Exp$1() -//│ fun getVal$Sum$4: Object -> Str -//│ fun derive$Pow$6: Object -> Mul$5 -//│ fun getVal$Pow$6: Object -> Str -//│ fun derive$Sum$4: Object -> Sum$4 -//│ fun main$$9: () -> Str -//│ fun derive$Numeric$2: anything -> Numeric$2 -//│ fun getNum$Numeric$2: Object -> Int -//│ fun derive$Variable$3: anything -> Numeric$2 -//│ fun getVal$Mul$5: Object -> Str -//│ fun main$$8: () -> Str -//│ fun main$$7: () -> Str -//│ fun main$$6: () -> Str -//│ fun getVal$Variable$3: Object -> Str -//│ fun getVal$Numeric$2: Object -> Str -//│ fun derive$Mul$5: Object -> Sum$4 //│ Str +//│ +//│ Simpledef: +//│ {class Exp() {fun derive: () -> Exp +//│ fun getVal: Str} +//│ class Numeric(val i: Int,): Exp {fun derive = () => Numeric(0,) +//│ fun getNum = i +//│ fun getVal = toString(i,)} +//│ class Variable(val nm: Str,): Exp {fun derive = () => Numeric(1,) +//│ fun getVal = nm} +//│ class Sum(val lhs: Exp, val rhs: Exp,): Exp {fun derive = () => Sum(let selRes$48 = lhs in case selRes$48 of { Variable => (selRes$48).derive() +//│ Mul => (selRes$48).derive() +//│ Sum => (selRes$48).derive() +//│ Numeric => (selRes$48).derive() }, let selRes$56 = rhs in case selRes$56 of { Numeric => (selRes$56).derive() +//│ Sum => (selRes$56).derive() +//│ Mul => (selRes$56).derive() },) +//│ fun getVal = concat("(",)(concat(concat(concat(let selRes$84 = lhs in case selRes$84 of { Variable => (selRes$84).getVal +//│ Mul => (selRes$84).getVal +//│ Sum => (selRes$84).getVal +//│ Numeric => (selRes$84).getVal },)(" + ",),)(let selRes$102 = rhs in case selRes$102 of { Numeric => (selRes$102).getVal +//│ Sum => (selRes$102).getVal +//│ Mul => (selRes$102).getVal },),)(")",),)} +//│ class Mul(val lhs: Exp, val rhs: Exp,): Exp {fun derive = () => Sum(Mul(let selRes$132 = lhs in case selRes$132 of { Variable => (selRes$132).derive() +//│ Mul => (selRes$132).derive() +//│ Sum => (selRes$132).derive() +//│ Numeric => (selRes$132).derive() +//│ Pow => (selRes$132).derive() }, rhs,), Mul(lhs, let selRes$150 = rhs in case selRes$150 of { Mul => (selRes$150).derive() +//│ Sum => (selRes$150).derive() +//│ Numeric => (selRes$150).derive() +//│ Pow => (selRes$150).derive() },),) +//│ fun getVal = concat("(",)(concat(concat(concat(let selRes$182 = lhs in case selRes$182 of { Variable => (selRes$182).getVal +//│ Mul => (selRes$182).getVal +//│ Sum => (selRes$182).getVal +//│ Numeric => (selRes$182).getVal +//│ Pow => (selRes$182).getVal },)(" * ",),)(let selRes$200 = rhs in case selRes$200 of { Mul => (selRes$200).getVal +//│ Sum => (selRes$200).getVal +//│ Numeric => (selRes$200).getVal +//│ Pow => (selRes$200).getVal },),)(")",),)} +//│ class Pow(val lhs: Variable, val rhs: Numeric,): Exp {fun derive = () => Mul(rhs, Pow(lhs, Numeric(-(let selRes$238 = rhs in case selRes$238 of { Numeric => (selRes$238).getNum }, 1,),),),) +//│ fun getVal = concat("(",)(concat(concat(concat(let selRes$276 = lhs in case selRes$276 of { Variable => (selRes$276).getVal },)(" ^ ",),)(let selRes$294 = rhs in case selRes$294 of { Numeric => (selRes$294).getVal },),)(")",),)} +//│ let selRes$316 = let selRes$318 = Sum(Variable("x",), Numeric(3,),) in case selRes$318 of { Sum => (selRes$318).derive() } in case selRes$316 of { Sum => (selRes$316).getVal } +//│ let selRes$346 = let selRes$348 = Mul(Variable("x",), Numeric(3,),) in case selRes$348 of { Mul => (selRes$348).derive() } in case selRes$346 of { Sum => (selRes$346).getVal } +//│ let selRes$376 = let selRes$378 = Pow(Variable("x",), Numeric(3,),) in case selRes$378 of { Pow => (selRes$378).derive() } in case selRes$376 of { Mul => (selRes$376).getVal } +//│ let selRes$406 = let selRes$408 = Mul(Pow(Variable("x",), Numeric(2,),), Pow(Variable("y",), Numeric(2,),),) in case selRes$408 of { Mul => (selRes$408).derive() } in case selRes$406 of { Sum => (selRes$406).getVal }} +//│ End simpledef +//│ //│ res //│ = '(1 + 0)' //│ res diff --git a/compiler/shared/test/diff/Defunctionalize/FreeVariables.mls b/compiler/shared/test/diff/Defunctionalize/FreeVariables.mls index d1590eec..67551605 100644 --- a/compiler/shared/test/diff/Defunctionalize/FreeVariables.mls +++ b/compiler/shared/test/diff/Defunctionalize/FreeVariables.mls @@ -1,30 +1,21 @@ :NewDefs -:mono class X() { val num = 5 fun get() = num } X().get() -//│ Lifted: -//│ TypingUnit { -//│ class X$1([]) {let num = 5; fun get = () => (this).num} -//│ Code(List((X$1()).get())) +//│ class X() { +//│ fun get: () -> 5 +//│ val num: 5 //│ } -//│ Mono: -//│ TypingUnit { -//│ class X$1([]) {} -//│ let num$X$1 = (this,) => 5 -//│ fun get$X$1 = (this,) => let obj = this in if obj is ‹(X$1) then num$X$1(obj,); else error› -//│ fun main$$1 = () => let obj = X$1() in if obj is ‹(X$1) then get$X$1(obj,); else error› -//│ Code(List(main$$1())) -//│ } -//│ class X$1() -//│ let num$X$1: anything -> 5 -//│ fun get$X$1: Object -> 5 -//│ fun main$$1: () -> 5 //│ 5 -//│ num$X$1 -//│ = [Function: num$X$1] +//│ +//│ Simpledef: +//│ {class X() {let num = 5 +//│ fun get = () => num} +//│ let selRes$10 = X() in case selRes$10 of { X => (selRes$10).get() }} +//│ End simpledef +//│ //│ res //│ = 5 diff --git a/compiler/shared/test/diff/Defunctionalize/FuncsWithParams.mls b/compiler/shared/test/diff/Defunctionalize/FuncsWithParams.mls index b87a3902..6713e101 100644 --- a/compiler/shared/test/diff/Defunctionalize/FuncsWithParams.mls +++ b/compiler/shared/test/diff/Defunctionalize/FuncsWithParams.mls @@ -1,9 +1,8 @@ :NewDefs -:mono -class Arithmetic() { - fun use(num1, num2) = 0 +abstract class Arithmetic() { + virtual fun use(num1: Int, num2: Int): Int } class Add() extends Arithmetic { fun use(num1, num2) = num1+num2 @@ -14,37 +13,29 @@ class Sub() extends Arithmetic { fun getArith(choice) = if choice == 1 then Add() else Sub() getArith(1).use(4,6) getArith(2).use(4,6) -//│ Lifted: -//│ TypingUnit { -//│ class Arithmetic$1([]) {fun use = (num1, num2,) => 0} -//│ class Add$2([]): Arithmetic$1 {fun use = (num1, num2,) => +(num1, num2,)} -//│ class Sub$3([]): Arithmetic$1 {fun use = (num1, num2,) => -(num1, num2,)} -//│ fun getArith$1 = (choice,) => if (==(choice, 1,)) then Add$2() else Sub$3() -//│ Code(List((getArith$1(1,)).use(4, 6,))) -//│ Code(List((getArith$1(2,)).use(4, 6,))) +//│ abstract class Arithmetic() { +//│ fun use: (num1: Int, num2: Int) -> Int //│ } -//│ Mono: -//│ TypingUnit { -//│ class Arithmetic$1([]) {} -//│ class Sub$3([]): Arithmetic$1 {} -//│ class Add$2([]): Arithmetic$1 {} -//│ fun getArith$1 = (choice,) => if (==(choice, 1,)) then Add$2() else Sub$3() -//│ fun use$Add$2 = (this, num1, num2,) => +(num1, num2,) -//│ fun use$Sub$3 = (this, num1, num2,) => -(num1, num2,) -//│ fun main$$5 = () => let obj = getArith$1(2,) in if obj is ‹(Sub$3) then use$Sub$3(obj, 4, 6,); (Add$2) then use$Add$2(obj, 4, 6,); else error› -//│ fun main$$4 = () => let obj = getArith$1(1,) in if obj is ‹(Sub$3) then use$Sub$3(obj, 4, 6,); (Add$2) then use$Add$2(obj, 4, 6,); else error› -//│ Code(List(main$$4())) -//│ Code(List(main$$5())) +//│ class Add() extends Arithmetic { +//│ fun use: (Int, Int) -> Int //│ } -//│ class Arithmetic$1() -//│ class Sub$3() extends Arithmetic$1 -//│ class Add$2() extends Arithmetic$1 -//│ fun getArith$1: Num -> (Add$2 | Sub$3) -//│ fun use$Add$2: (anything, Int, Int) -> Int -//│ fun use$Sub$3: (anything, Int, Int) -> Int -//│ fun main$$5: () -> Int -//│ fun main$$4: () -> Int +//│ class Sub() extends Arithmetic { +//│ fun use: (Int, Int) -> Int +//│ } +//│ fun getArith: Num -> (Add | Sub) //│ Int +//│ +//│ Simpledef: +//│ {class Arithmetic() {fun use: (num1: Int, num2: Int) -> Int} +//│ class Add(): Arithmetic {fun use = (num1::0, num2::1,) => +(num1, num2,)} +//│ class Sub(): Arithmetic {fun use = (num1::2, num2::3,) => -(num1, num2,)} +//│ fun getArith = (choice::4,) => if (==(choice, 1,)) then Add() else Sub() +//│ let selRes$58 = getArith(1,) in case selRes$58 of { Sub => (selRes$58).use(4, 6,) +//│ Add => (selRes$58).use(4, 6,) } +//│ let selRes$76 = getArith(2,) in case selRes$76 of { Sub => (selRes$76).use(4, 6,) +//│ Add => (selRes$76).use(4, 6,) }} +//│ End simpledef +//│ //│ res //│ = 10 //│ res diff --git a/compiler/shared/test/diff/Defunctionalize/Inheritance.mls b/compiler/shared/test/diff/Defunctionalize/Inheritance.mls index aa12c5fb..b0954aa1 100644 --- a/compiler/shared/test/diff/Defunctionalize/Inheritance.mls +++ b/compiler/shared/test/diff/Defunctionalize/Inheritance.mls @@ -1,39 +1,32 @@ :NewDefs -:mono -class Sup { +// FIXME: Pattern matches on superclass instead of subclass +class Sup() { fun add(num1, num2) = num1+num2 } -class Sub1() extends Sup {} +class Sub1() extends Sup() {} class Sub2() extends Sub1() {} Sub1().add(3,4) Sub2().add(5,6) -//│ Lifted: -//│ TypingUnit { -//│ class Sup$1([]) {fun add = (num1, num2,) => +(num1, num2,)} -//│ class Sub1$2([]): Sup$1 {} -//│ class Sub2$3([]): Sub1$2() {} -//│ Code(List((Sub1$2()).add(3, 4,))) -//│ Code(List((Sub2$3()).add(5, 6,))) +//│ class Sup() { +//│ fun add: (Int, Int) -> Int //│ } -//│ Mono: -//│ TypingUnit { -//│ class Sub2$3([]): Sub1$2() {} -//│ class Sup$1([]) {} -//│ class Sub1$2([]): Sup$1 {} -//│ fun add$Sup$1 = (this, num1, num2,) => +(num1, num2,) -//│ fun main$$4 = () => let obj = Sub2$3() in if obj is ‹(Sub2$3) then add$Sup$1(obj, 5, 6,); else error› -//│ fun main$$3 = () => let obj = Sub1$2() in if obj is ‹(Sub1$2) then add$Sup$1(obj, 3, 4,); else error› -//│ Code(List(main$$3())) -//│ Code(List(main$$4())) +//│ class Sub1() extends Sup { +//│ fun add: (Int, Int) -> Int +//│ } +//│ class Sub2() extends Sub1, Sup { +//│ fun add: (Int, Int) -> Int //│ } -//│ class Sub2$3() extends Sub1$2, Sup$1 -//│ class Sup$1() -//│ class Sub1$2() extends Sup$1 -//│ fun add$Sup$1: (anything, Int, Int) -> Int -//│ fun main$$4: () -> Int -//│ fun main$$3: () -> Int //│ Int +//│ +//│ Simpledef: +//│ {class Sup() {fun add = (num1::0, num2::1,) => +(num1, num2,)} +//│ class Sub1(): Sup() {} +//│ class Sub2(): Sub1() {} +//│ let selRes$16 = Sub1() in case selRes$16 of { Sup => (selRes$16).add(3, 4,) } +//│ let selRes$32 = Sub2() in case selRes$32 of { Sup => (selRes$32).add(5, 6,) }} +//│ End simpledef +//│ //│ res //│ = 7 //│ res diff --git a/compiler/shared/test/diff/Defunctionalize/Lambda.mls b/compiler/shared/test/diff/Defunctionalize/Lambda.mls new file mode 100644 index 00000000..c20c3305 --- /dev/null +++ b/compiler/shared/test/diff/Defunctionalize/Lambda.mls @@ -0,0 +1,22 @@ +:NewDefs + + +class X(val foo: Int => Int){} +class Y(val foo: Int => Bool){} +fun x(pred) = if pred then X(x => x+1) else Y(x => true) +x(true).foo(5) +//│ class X(foo: Int -> Int) +//│ class Y(foo: Int -> Bool) +//│ fun x: Bool -> (X | Y) +//│ Int | false | true +//│ +//│ Simpledef: +//│ {class X(val foo: (Int,) => Int,) {} +//│ class Y(val foo: (Int,) => Bool,) {} +//│ fun x = (pred::0,) => if (pred) then X((x::1,) => +(x, 1,),) else Y((x::2,) => true,) +//│ let selRes$46 = x(true,) in case selRes$46 of { Y => (selRes$46).foo(5,) +//│ X => (selRes$46).foo(5,) }} +//│ End simpledef +//│ +//│ res +//│ = 6 diff --git a/compiler/shared/test/diff/Defunctionalize/Lambdas.mls b/compiler/shared/test/diff/Defunctionalize/Lambdas.mls index 50471c0c..8244f601 100644 --- a/compiler/shared/test/diff/Defunctionalize/Lambdas.mls +++ b/compiler/shared/test/diff/Defunctionalize/Lambdas.mls @@ -1,49 +1,22 @@ :NewDefs -:mono ((f, g) => f(g))(f => f, true) -//│ Lifted: -//│ TypingUnit { -//│ class Lambda2$1$1([]) {fun apply = (f, g,) => f(g,)} -//│ class Lambda1$2$2([]) {fun apply = (f,) => f} -//│ Code(List('(' {Lambda2$1$1()} ')'({Lambda1$2$2()}, true,))) -//│ } -//│ Mono: -//│ TypingUnit { -//│ class Lambda2$1$1([]) {} -//│ class Lambda1$2$2([]) {} -//│ fun apply$Lambda2$1$1 = (this, f, g,) => let obj = f in if obj is ‹(Lambda1$2$2) then apply$Lambda1$2$2(obj, g,); else error› -//│ fun main$$2 = () => let obj = '(' {Lambda2$1$1()} ')' in if obj is ‹(Lambda2$1$1) then apply$Lambda2$1$1(obj, {Lambda1$2$2()}, true,); else error› -//│ fun apply$Lambda1$2$2 = (this, f,) => f -//│ Code(List(main$$2())) -//│ } -//│ class Lambda2$1$1() -//│ class Lambda1$2$2() -//│ fun apply$Lambda2$1$1: forall 'a. (anything, Object, 'a) -> 'a -//│ fun main$$2: () -> true -//│ fun apply$Lambda1$2$2: forall 'a. (anything, 'a) -> 'a //│ true +//│ +//│ Simpledef: +//│ {'(' (f::0, g::1,) => f(g,) ')'((f::2,) => f, true,)} +//│ End simpledef +//│ //│ res //│ = true -:mono (b => if b then true else false) (true) -//│ Lifted: -//│ TypingUnit { -//│ class Lambda1$1$1([]) {fun apply = (b,) => if (b) then true else false} -//│ Code(List('(' {Lambda1$1$1()} ')'(true,))) -//│ } -//│ Mono: -//│ TypingUnit { -//│ class Lambda1$1$1([]) {} -//│ fun apply$Lambda1$1$1 = (this, b,) => if (b) then true else false -//│ fun main$$1 = () => let obj = '(' {Lambda1$1$1()} ')' in if obj is ‹(Lambda1$1$1) then apply$Lambda1$1$1(obj, true,); else error› -//│ Code(List(main$$1())) -//│ } -//│ class Lambda1$1$1() -//│ fun apply$Lambda1$1$1: (anything, Bool) -> Bool -//│ fun main$$1: () -> Bool //│ Bool +//│ +//│ Simpledef: +//│ {'(' (b::3,) => if (b) then true else false ')'(true,)} +//│ End simpledef +//│ //│ res //│ = true diff --git a/compiler/shared/test/diff/Defunctionalize/ListConstruction.mls b/compiler/shared/test/diff/Defunctionalize/ListConstruction.mls index 2f40742d..62197c0d 100644 --- a/compiler/shared/test/diff/Defunctionalize/ListConstruction.mls +++ b/compiler/shared/test/diff/Defunctionalize/ListConstruction.mls @@ -1,12 +1,11 @@ :NewDefs -:mono -class List { + +abstract class List() { fun getRes: Str - fun getRes = "" - fun map(f) = error + fun map: error } -class Cons(x: Int, xs: List) extends List { +class Cons(val x: Int, val xs: List) extends List { fun getRes = concat(concat(toString(x))(" :: "))(xs.getRes) fun map(f) = Cons(f(x), xs.map(f)) } @@ -14,52 +13,39 @@ class Nil() extends List { fun getRes = "Nil" fun map(f) = Nil() } -fun mkList(len: Int) = +fun mkList(len) = if len == 0 then Nil() else Cons(len, mkList(len-1)) -mkList(5).map((x) => x*2).getRes -//│ Lifted: -//│ TypingUnit { -//│ class List$1([]) { -//│ fun getRes = Str -//│ fun getRes = "" -//│ fun map = (f,) => error -//│ } -//│ class Cons$2([x: Int, xs: List$1,]): List$1 { -//│ fun getRes = concat(concat(toString((this).x,),)(" :: ",),)(((this).xs).getRes,) -//│ fun map = (f,) => Cons$2(f((this).x,), ((this).xs).map(f,),) -//│ } -//│ class Nil$3([]): List$1 {fun getRes = "Nil"; fun map = (f,) => Nil$3()} -//│ class Lambda1$2$4([]) {fun apply = ('(' x ')',) => *(x, 2,)} -//│ fun mkList$1 = (len: Int,) => {if (==(len, 0,)) then Nil$3() else Cons$2(len, mkList$1(-(len, 1,),),)} -//│ Code(List(((mkList$1(5,)).map({Lambda1$2$4()},)).getRes)) +mkList(5).map(x => x*2).getRes +//│ abstract class List() { +//│ fun getRes: Str +//│ fun map: error +//│ } +//│ class Cons(x: Int, xs: List) extends List { +//│ fun getRes: Str +//│ fun map: (Int -> Int) -> Cons //│ } -//│ Mono: -//│ TypingUnit { -//│ class Lambda1$2$4([]) {} -//│ class List$1([]) {} -//│ class Cons$2([x: Int, xs: List$1,]): List$1 {} -//│ class Nil$3([]): List$1 {} -//│ fun mkList$1 = (len: Int,) => {if (==(len, 0,)) then Nil$3() else Cons$2(len, mkList$1(-(len, 1,),),)} -//│ fun map$Nil$3 = (this, f,) => Nil$3() -//│ fun getRes$Nil$3 = (this,) => "Nil" -//│ fun getRes$Cons$2 = (this,) => concat(concat(toString(let obj = this in if obj is ‹(Cons$2(x, xs,)) then x; else error›,),)(" :: ",),)(let obj = let obj = this in if obj is ‹(Cons$2(x, xs,)) then xs; else error› in if obj is ‹(Cons$2) then getRes$Cons$2(obj,); (Nil$3) then getRes$Nil$3(obj,); else error›,) -//│ fun main$$5 = () => let obj = let obj = mkList$1(5,) in if obj is ‹(Cons$2) then map$Cons$2(obj, {Lambda1$2$4()},); (Nil$3) then map$Nil$3(obj, {Lambda1$2$4()},); else error› in if obj is ‹(Cons$2) then getRes$Cons$2(obj,); (Nil$3) then getRes$Nil$3(obj,); else error› -//│ fun map$Cons$2 = (this, f,) => Cons$2(let obj = f in if obj is ‹(Lambda1$2$4) then apply$Lambda1$2$4(obj, let obj = this in if obj is ‹(Cons$2(x, xs,)) then x; else error›,); else error›, let obj = let obj = this in if obj is ‹(Cons$2(x, xs,)) then xs; else error› in if obj is ‹(Cons$2) then map$Cons$2(obj, f,); (Nil$3) then map$Nil$3(obj, f,); else error›,) -//│ fun apply$Lambda1$2$4 = (this, '(' x ')',) => *(x, 2,) -//│ Code(List(main$$5())) +//│ class Nil() extends List { +//│ fun getRes: "Nil" +//│ fun map: anything -> Nil //│ } -//│ class Lambda1$2$4() -//│ class List$1() -//│ class Cons$2(x: Int, xs: List$1) extends List$1 -//│ class Nil$3() extends List$1 -//│ fun mkList$1: (len: Int) -> (Cons$2 | Nil$3) -//│ fun map$Nil$3: (anything, anything) -> Nil$3 -//│ fun getRes$Nil$3: anything -> "Nil" -//│ fun getRes$Cons$2: Object -> Str -//│ fun main$$5: () -> Str -//│ fun map$Cons$2: (Object, Object) -> Cons$2 -//│ fun apply$Lambda1$2$4: (anything, Int) -> Int +//│ fun mkList: Int -> (Cons | Nil) //│ Str +//│ +//│ Simpledef: +//│ {class List() {fun getRes: Str +//│ fun map: error} +//│ class Cons(val x: Int, val xs: List,): List {fun getRes = concat(concat(toString(x,),)(" :: ",),)(let selRes$30 = xs in case selRes$30 of { Nil => (selRes$30).getRes +//│ Cons => (selRes$30).getRes },) +//│ fun map = (f::0,) => Cons(f(x,), let selRes$50 = xs in case selRes$50 of { Nil => (selRes$50).map(f,) +//│ Cons => (selRes$50).map(f,) },)} +//│ class Nil(): List {fun getRes = "Nil" +//│ fun map = (f::1,) => Nil()} +//│ fun mkList = (len::2,) => {if (==(len, 0,)) then Nil() else Cons(len, mkList(-(len, 1,),),)} +//│ let selRes$126 = let selRes$128 = mkList(5,) in case selRes$128 of { Cons => (selRes$128).map((x::3,) => *(x, 2,),) +//│ Nil => (selRes$128).map((x::3,) => *(x, 2,),) } in case selRes$126 of { Cons => (selRes$126).getRes +//│ Nil => (selRes$126).getRes }} +//│ End simpledef +//│ //│ res //│ = '10 :: 8 :: 6 :: 4 :: 2 :: Nil' diff --git a/compiler/shared/test/diff/Defunctionalize/Modules.mls b/compiler/shared/test/diff/Defunctionalize/Modules.mls index 48f5a754..6967753a 100644 --- a/compiler/shared/test/diff/Defunctionalize/Modules.mls +++ b/compiler/shared/test/diff/Defunctionalize/Modules.mls @@ -1,36 +1,22 @@ :NewDefs -:w -:mono + class Foo() {fun f = 0} module x { val y = Foo() } x.y.f -//│ Lifted: -//│ TypingUnit { -//│ class Foo$1([]) {fun f = 0} -//│ module x$2 {let y = Foo$1()} -//│ Code(List(((x$2).y).f)) +//│ class Foo() { +//│ fun f: 0 //│ } -//│ Mono: -//│ TypingUnit { -//│ module x$2 {} -//│ class Foo$1([]) {} -//│ let y$x$2 = (this,) => Foo$1() -//│ fun f$Foo$1 = (this,) => 0 -//│ fun main$$2 = () => let obj = let obj = x$2 in if obj is ‹(x$2) then y$x$2(obj,); else error› in if obj is ‹(Foo$1) then f$Foo$1(obj,); else error› -//│ Code(List(main$$2())) +//│ module x { +//│ val y: Foo //│ } -//│ ╔══[WARNING] the outer binding `x$2` -//│ ╙── is shadowed by name pattern `x$2` -//│ ╔══[WARNING] this case is unreachable -//│ ╙── because it is subsumed by the branch -//│ module x$2 -//│ class Foo$1() -//│ let y$x$2: anything -> Foo$1 -//│ fun f$Foo$1: anything -> 0 -//│ fun main$$2: () -> 0 //│ 0 -//│ y$x$2 -//│ = [Function: y$x$2] +//│ +//│ Simpledef: +//│ {class Foo() {fun f = 0} +//│ module x {let y = Foo()} +//│ let selRes$10 = let selRes$12 = x in case selRes$12 of { x => (selRes$12).y } in case selRes$10 of { Foo => (selRes$10).f }} +//│ End simpledef +//│ //│ res //│ = 0 diff --git a/compiler/shared/test/diff/Defunctionalize/MonoNonLambda.mls b/compiler/shared/test/diff/Defunctionalize/MonoNonLambda.mls index 847f6dd5..53181086 100644 --- a/compiler/shared/test/diff/Defunctionalize/MonoNonLambda.mls +++ b/compiler/shared/test/diff/Defunctionalize/MonoNonLambda.mls @@ -1,7 +1,5 @@ :NewDefs -:ge // TODO: Wrap resulting statements in module -:mono class A() { val x = 2 val y() = 3 @@ -13,42 +11,34 @@ a.x a.y() a.z a.w() -//│ Lifted: -//│ TypingUnit { -//│ class A$1([]) {let x = 2; let y = () => 3; fun z = 4; fun w = () => 5} -//│ let a$1 = A$1() -//│ Code(List((a$1).x)) -//│ Code(List((a$1).y())) -//│ Code(List((a$1).z)) -//│ Code(List((a$1).w())) +//│ class A() { +//│ fun w: () -> 5 +//│ val x: 2 +//│ val y: () -> 3 +//│ fun z: 4 //│ } -//│ Mono: -//│ TypingUnit { -//│ class A$1([]) {} -//│ let y$A$1 = (this,) => 3 -//│ fun main$$5 = () => let obj = a$1 in if obj is ‹(A$1) then w$A$1(obj,); else error› -//│ fun z$A$1 = (this,) => 4 -//│ fun main$$4 = () => let obj = a$1 in if obj is ‹(A$1) then z$A$1(obj,); else error› -//│ fun main$$3 = () => let obj = a$1 in if obj is ‹(A$1) then y$A$1(obj,); else error› -//│ let x$A$1 = (this,) => 2 -//│ fun main$$2 = () => let obj = a$1 in if obj is ‹(A$1) then x$A$1(obj,); else error› -//│ let a$1 = A$1() -//│ fun w$A$1 = (this,) => 5 -//│ Code(List(main$$2())) -//│ Code(List(main$$3())) -//│ Code(List(main$$4())) -//│ Code(List(main$$5())) -//│ } -//│ class A$1() -//│ let y$A$1: anything -> 3 -//│ fun main$$5: () -> 5 -//│ fun z$A$1: anything -> 4 -//│ fun main$$4: () -> 4 -//│ fun main$$3: () -> 3 -//│ let x$A$1: anything -> 2 -//│ fun main$$2: () -> 2 -//│ let a$1: A$1 -//│ fun w$A$1: anything -> 5 +//│ val a: A //│ 5 -//│ Code generation encountered an error: -//│ unguarded recursive use of by-value binding a$1 +//│ +//│ Simpledef: +//│ {class A() {let x = 2 +//│ let y = () => 3 +//│ fun z = 4 +//│ fun w = () => 5} +//│ let a = A() +//│ let selRes$24 = a in case selRes$24 of { A => (selRes$24).x } +//│ let selRes$28 = a in case selRes$28 of { A => (selRes$28).y() } +//│ let selRes$36 = a in case selRes$36 of { A => (selRes$36).z } +//│ let selRes$40 = a in case selRes$40 of { A => (selRes$40).w() }} +//│ End simpledef +//│ +//│ a +//│ = A {} +//│ res +//│ = 2 +//│ res +//│ = 3 +//│ res +//│ = 4 +//│ res +//│ = 5 diff --git a/compiler/shared/test/diff/Defunctionalize/MonoTupSelect.mls b/compiler/shared/test/diff/Defunctionalize/MonoTupSelect.mls index 6ce35afc..a3f5cd82 100644 --- a/compiler/shared/test/diff/Defunctionalize/MonoTupSelect.mls +++ b/compiler/shared/test/diff/Defunctionalize/MonoTupSelect.mls @@ -1,35 +1,24 @@ :NewDefs -:mono class Foo() {fun f() = 0} class Bar() {fun f = 0} [Foo(), Bar()].0.f() [Foo(), Bar()].1.f -//│ Lifted: -//│ TypingUnit { -//│ class Foo$1([]) {fun f = () => 0} -//│ class Bar$2([]) {fun f = 0} -//│ Code(List((([Foo$1(), Bar$2(),]).0).f())) -//│ Code(List((([Foo$1(), Bar$2(),]).1).f)) +//│ class Foo() { +//│ fun f: () -> 0 //│ } -//│ Mono: -//│ TypingUnit { -//│ class Foo$1([]) {} -//│ class Bar$2([]) {} -//│ fun f$Bar$2 = (this,) => 0 -//│ fun main$$3 = () => let obj = ([Foo$1(), Bar$2(),]).1 in if obj is ‹(Bar$2) then f$Bar$2(obj,); else error› -//│ fun f$Foo$1 = (this,) => 0 -//│ fun main$$2 = () => let obj = ([Foo$1(), Bar$2(),]).0 in if obj is ‹(Foo$1) then f$Foo$1(obj,); else error› -//│ Code(List(main$$2())) -//│ Code(List(main$$3())) +//│ class Bar() { +//│ fun f: 0 //│ } -//│ class Foo$1() -//│ class Bar$2() -//│ fun f$Bar$2: anything -> 0 -//│ fun main$$3: () -> 0 -//│ fun f$Foo$1: anything -> 0 -//│ fun main$$2: () -> 0 //│ 0 +//│ +//│ Simpledef: +//│ {class Foo() {fun f = () => 0} +//│ class Bar() {fun f = 0} +//│ let selRes$10 = ([Foo(), Bar(),]).0 in case selRes$10 of { Foo => (selRes$10).f() } +//│ let selRes$32 = ([Foo(), Bar(),]).1 in case selRes$32 of { Bar => (selRes$32).f }} +//│ End simpledef +//│ //│ res //│ = 0 //│ res diff --git a/compiler/shared/test/diff/Defunctionalize/MutableParams.mls b/compiler/shared/test/diff/Defunctionalize/MutableParams.mls index d652f6d0..871b563d 100644 --- a/compiler/shared/test/diff/Defunctionalize/MutableParams.mls +++ b/compiler/shared/test/diff/Defunctionalize/MutableParams.mls @@ -1,25 +1,21 @@ :NewDefs // TODO: Mutable Parameters -//:mono //class Bar(#x) //fun foo(#b) = b //let a = foo(new Bar(1)) //let b = foo(new Bar(2)) -//:mono //class OneInt(#a){ // fun inc() = a+1 //} //(new OneInt(1)).inc() -//:mono //class OneInt(#a){ // fun add(x) = // new OneInt(a+x.a) //} //(new OneInt(1)).add(new OneInt(2)) -//:mono //trait AnyFoo { //} //class FooPlus(#a): AnyFoo { diff --git a/compiler/shared/test/diff/Defunctionalize/MutualRec.mls b/compiler/shared/test/diff/Defunctionalize/MutualRec.mls index d1f31ec1..d0d30678 100644 --- a/compiler/shared/test/diff/Defunctionalize/MutualRec.mls +++ b/compiler/shared/test/diff/Defunctionalize/MutualRec.mls @@ -1,7 +1,6 @@ :NewDefs :AllowRuntimeErrors -:mono val any = -20 fun f(x) = if x > any then 0 @@ -10,28 +9,20 @@ fun g(x) = if x > any then g(x - 1) else f(x - 2) g(1) -//│ Lifted: -//│ TypingUnit { -//│ let any$3 = -20 -//│ fun f$1 = (x,) => {if (>(x, any$3,)) then 0 else g$2(-(x, 1,),)} -//│ fun g$2 = (x,) => {if (>(x, any$3,)) then g$2(-(x, 1,),) else f$1(-(x, 2,),)} -//│ Code(List(g$2(1,))) -//│ } -//│ Mono: -//│ TypingUnit { -//│ let any$3 = -20 -//│ fun f$1 = (x,) => {if (>(x, any$3,)) then 0 else g$2(-(x, 1,),)} -//│ fun g$2 = (x,) => {if (>(x, any$3,)) then g$2(-(x, 1,),) else f$1(-(x, 2,),)} -//│ fun main$$3 = () => g$2(1,) -//│ Code(List(main$$3())) -//│ } -//│ let any$3: -20 -//│ fun f$1: Int -> 0 -//│ fun g$2: Int -> 0 -//│ fun main$$3: () -> 0 +//│ val any: -20 +//│ fun f: Int -> 0 +//│ fun g: Int -> 0 //│ 0 -//│ any$3 -//│ = -20 +//│ +//│ Simpledef: +//│ {let any = -20 +//│ fun f = (x::0,) => {if (>(x, any,)) then 0 else g(-(x, 1,),)} +//│ fun g = (x::1,) => {if (>(x, any,)) then g(-(x, 1,),) else f(-(x, 2,),)} +//│ g(1,)} +//│ End simpledef +//│ +//│ any +//│ = -20 //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded diff --git a/compiler/shared/test/diff/Defunctionalize/NewOperator.mls b/compiler/shared/test/diff/Defunctionalize/NewOperator.mls index 773bc72b..fbb80431 100644 --- a/compiler/shared/test/diff/Defunctionalize/NewOperator.mls +++ b/compiler/shared/test/diff/Defunctionalize/NewOperator.mls @@ -1,22 +1,15 @@ :NewDefs -:mono -class Foo(x: Int) { +class Foo(val x: Int) { } -new Foo(5) -//│ Lifted: -//│ TypingUnit { -//│ class Foo$1([x: Int,]) {} -//│ Code(List((new Foo$1)(5,))) -//│ } -//│ Mono: -//│ TypingUnit { -//│ class Foo$1([x: Int,]) {} -//│ fun main$$1 = () => (new Foo$1)(5,) -//│ Code(List(main$$1())) -//│ } -//│ class Foo$1(x: Int) -//│ fun main$$1: () -> Foo$1 -//│ Foo$1 +new Foo(5).x +//│ class Foo(x: Int) +//│ Int +//│ +//│ Simpledef: +//│ {class Foo(val x: Int,) {} +//│ let selRes$4 = (new Foo)(5,) in case selRes$4 of { Foo => (selRes$4).x }} +//│ End simpledef +//│ //│ res -//│ = Foo$1 {} +//│ = 5 diff --git a/compiler/shared/test/diff/Defunctionalize/NuMono.mls b/compiler/shared/test/diff/Defunctionalize/NuMono.mls index 5d820d3e..41469997 100644 --- a/compiler/shared/test/diff/Defunctionalize/NuMono.mls +++ b/compiler/shared/test/diff/Defunctionalize/NuMono.mls @@ -1,7 +1,6 @@ :NewDefs // old "new" syntax -//:mono //class A() { // val num() = 0 //} @@ -12,7 +11,6 @@ //foo(10).num() -:mono class A() { val num() = 0 } @@ -21,39 +19,27 @@ class B() { } fun foo(num: Int) = if num > 5 then A() else B() foo(10).num() -//│ Lifted: -//│ TypingUnit { -//│ class A$1([]) {let num = () => 0} -//│ class B$2([]) {let num = () => 1} -//│ fun foo$1 = (num: Int,) => if (>(num, 5,)) then A$1() else B$2() -//│ Code(List((foo$1(10,)).num())) +//│ class A() { +//│ val num: () -> 0 //│ } -//│ Mono: -//│ TypingUnit { -//│ class B$2([]) {} -//│ class A$1([]) {} -//│ fun foo$1 = (num: Int,) => if (>(num, 5,)) then A$1() else B$2() -//│ let num$A$1 = (this,) => 0 -//│ let num$B$2 = (this,) => 1 -//│ fun main$$3 = () => let obj = foo$1(10,) in if obj is ‹(B$2) then num$B$2(obj,); (A$1) then num$A$1(obj,); else error› -//│ Code(List(main$$3())) +//│ class B() { +//│ val num: () -> 1 //│ } -//│ class B$2() -//│ class A$1() -//│ fun foo$1: (num: Int) -> (A$1 | B$2) -//│ let num$A$1: anything -> 0 -//│ let num$B$2: anything -> 1 -//│ fun main$$3: () -> (0 | 1) +//│ fun foo: (num: Int) -> (A | B) //│ 0 | 1 -//│ num$A$1 -//│ = [Function: num$A$1] -//│ num$B$2 -//│ = [Function: num$B$2] +//│ +//│ Simpledef: +//│ {class A() {let num = () => 0} +//│ class B() {let num = () => 1} +//│ fun foo = (num: Int,) => if (>(num, 5,)) then A() else B() +//│ let selRes$42 = foo(10,) in case selRes$42 of { B => (selRes$42).num() +//│ A => (selRes$42).num() }} +//│ End simpledef +//│ //│ res //│ = 0 -:mono class A(val num1: Int, val num2: Int) { fun foo() = num1-num2 } @@ -63,34 +49,25 @@ class B(val num1: Int, val num2: Int) { fun foo(num: Int) = if num > 5 then A(10,6) else B(8,7) foo(10).foo() foo(0).foo() -//│ Lifted: -//│ TypingUnit { -//│ class A$1([val num1: Int, val num2: Int,]) {fun foo = () => -((this).num1, (this).num2,)} -//│ class B$2([val num1: Int, val num2: Int,]) {fun foo = () => +((this).num1, (this).num2,)} -//│ fun foo$1 = (num: Int,) => if (>(num, 5,)) then A$1(10, 6,) else B$2(8, 7,) -//│ Code(List((foo$1(10,)).foo())) -//│ Code(List((foo$1(0,)).foo())) +//│ class A(num1: Int, num2: Int) { +//│ fun foo: () -> Int //│ } -//│ Mono: -//│ TypingUnit { -//│ class B$2([val num1: Int, val num2: Int,]) {} -//│ class A$1([val num1: Int, val num2: Int,]) {} -//│ fun foo$B$2 = (this,) => +(let obj = this in if obj is ‹(B$2) then 8; else error›, let obj = this in if obj is ‹(B$2) then 7; else error›,) -//│ fun foo$1 = (num: Int,) => if (>(num, 5,)) then A$1(10, 6,) else B$2(8, 7,) -//│ fun foo$A$1 = (this,) => -(let obj = this in if obj is ‹(A$1) then 10; else error›, let obj = this in if obj is ‹(A$1) then 6; else error›,) -//│ fun main$$4 = () => let obj = foo$1(0,) in if obj is ‹(B$2) then foo$B$2(obj,); (A$1) then foo$A$1(obj,); else error› -//│ fun main$$3 = () => let obj = foo$1(10,) in if obj is ‹(B$2) then foo$B$2(obj,); (A$1) then foo$A$1(obj,); else error› -//│ Code(List(main$$3())) -//│ Code(List(main$$4())) +//│ class B(num1: Int, num2: Int) { +//│ fun foo: () -> Int //│ } -//│ class B$2(num1: Int, num2: Int) -//│ class A$1(num1: Int, num2: Int) -//│ fun foo$B$2: Object -> Int -//│ fun foo$1: (num: Int) -> (A$1 | B$2) -//│ fun foo$A$1: Object -> Int -//│ fun main$$4: () -> Int -//│ fun main$$3: () -> Int +//│ fun foo: (num: Int) -> (A | B) //│ Int +//│ +//│ Simpledef: +//│ {class A(val num1: Int, val num2: Int,) {fun foo = () => -(num1, num2,)} +//│ class B(val num1: Int, val num2: Int,) {fun foo = () => +(num1, num2,)} +//│ fun foo = (num: Int,) => if (>(num, 5,)) then A(10, 6,) else B(8, 7,) +//│ let selRes$70 = foo(10,) in case selRes$70 of { B => (selRes$70).foo() +//│ A => (selRes$70).foo() } +//│ let selRes$84 = foo(0,) in case selRes$84 of { B => (selRes$84).foo() +//│ A => (selRes$84).foo() }} +//│ End simpledef +//│ //│ res //│ = 4 //│ res diff --git a/compiler/shared/test/diff/Defunctionalize/ObjFieldAccess.mls b/compiler/shared/test/diff/Defunctionalize/ObjFieldAccess.mls index 3020a56d..5a2d163c 100644 --- a/compiler/shared/test/diff/Defunctionalize/ObjFieldAccess.mls +++ b/compiler/shared/test/diff/Defunctionalize/ObjFieldAccess.mls @@ -1,164 +1,126 @@ :NewDefs -:ge // TODO: Wrap resulting statements in module -:mono -class A(i: Int) { +class A(val i: Int) { fun get1() = i fun get2 = i } val a = A(6) a.get1() a.get2 -//│ Lifted: -//│ TypingUnit { -//│ class A$1([i: Int,]) {fun get1 = () => (this).i; fun get2 = (this).i} -//│ let a$1 = A$1(6,) -//│ Code(List((a$1).get1())) -//│ Code(List((a$1).get2)) +//│ class A(i: Int) { +//│ fun get1: () -> Int +//│ fun get2: Int //│ } -//│ Mono: -//│ TypingUnit { -//│ class A$1([i: Int,]) {} -//│ fun get1$A$1 = (this,) => let obj = this in if obj is ‹(A$1) then 6; else error› -//│ fun get2$A$1 = (this,) => let obj = this in if obj is ‹(A$1) then 6; else error› -//│ fun main$$3 = () => let obj = a$1 in if obj is ‹(A$1) then get2$A$1(obj,); else error› -//│ fun main$$2 = () => let obj = a$1 in if obj is ‹(A$1) then get1$A$1(obj,); else error› -//│ let a$1 = A$1(6,) -//│ Code(List(main$$2())) -//│ Code(List(main$$3())) -//│ } -//│ class A$1(i: Int) -//│ fun get1$A$1: Object -> 6 -//│ fun get2$A$1: Object -> 6 -//│ fun main$$3: () -> 6 -//│ fun main$$2: () -> 6 -//│ let a$1: A$1 -//│ 6 -//│ Code generation encountered an error: -//│ unguarded recursive use of by-value binding a$1 +//│ val a: A +//│ Int +//│ +//│ Simpledef: +//│ {class A(val i: Int,) {fun get1 = () => i +//│ fun get2 = i} +//│ let a = A(6,) +//│ let selRes$20 = a in case selRes$20 of { A => (selRes$20).get1() } +//│ let selRes$28 = a in case selRes$28 of { A => (selRes$28).get2 }} +//│ End simpledef +//│ +//│ a +//│ = A {} +//│ res +//│ = 6 +//│ res +//│ = 6 -:ge // TODO: Wrap resulting statements in module -:mono -class A(i: Str) { +class A(val i: Str) { fun get1() = i fun get2 = i } val a = A("6") a.get1() a.get2 -//│ Lifted: -//│ TypingUnit { -//│ class A$1([i: Str,]) {fun get1 = () => (this).i; fun get2 = (this).i} -//│ let a$1 = A$1("6",) -//│ Code(List((a$1).get1())) -//│ Code(List((a$1).get2)) +//│ class A(i: Str) { +//│ fun get1: () -> Str +//│ fun get2: Str //│ } -//│ Mono: -//│ TypingUnit { -//│ class A$1([i: Str,]) {} -//│ fun get1$A$1 = (this,) => let obj = this in if obj is ‹(A$1) then "6"; else error› -//│ fun get2$A$1 = (this,) => let obj = this in if obj is ‹(A$1) then "6"; else error› -//│ fun main$$3 = () => let obj = a$1 in if obj is ‹(A$1) then get2$A$1(obj,); else error› -//│ fun main$$2 = () => let obj = a$1 in if obj is ‹(A$1) then get1$A$1(obj,); else error› -//│ let a$1 = A$1("6",) -//│ Code(List(main$$2())) -//│ Code(List(main$$3())) -//│ } -//│ class A$1(i: Str) -//│ fun get1$A$1: Object -> "6" -//│ fun get2$A$1: Object -> "6" -//│ fun main$$3: () -> "6" -//│ fun main$$2: () -> "6" -//│ let a$1: A$1 -//│ "6" -//│ Code generation encountered an error: -//│ unguarded recursive use of by-value binding a$1 +//│ val a: A +//│ Str +//│ +//│ Simpledef: +//│ {class A(val i: Str,) {fun get1 = () => i +//│ fun get2 = i} +//│ let a = A("6",) +//│ let selRes$20 = a in case selRes$20 of { A => (selRes$20).get1() } +//│ let selRes$28 = a in case selRes$28 of { A => (selRes$28).get2 }} +//│ End simpledef +//│ +//│ a +//│ = A {} +//│ res +//│ = '6' +//│ res +//│ = '6' -:ge //TODO: Wrap resulting statements in module -:mono class X() -class Y(foo: X) { +class Y(val foo: X) { fun get1() = foo fun get2 = foo } val a = Y(X()) a.get1() a.get2 -//│ Lifted: -//│ TypingUnit { -//│ class X$1([]) {} -//│ class Y$2([foo: X$1,]) {fun get1 = () => (this).foo; fun get2 = (this).foo} -//│ let a$1 = Y$2(X$1(),) -//│ Code(List((a$1).get1())) -//│ Code(List((a$1).get2)) -//│ } -//│ Mono: -//│ TypingUnit { -//│ class X$1([]) {} -//│ class Y$2([foo: X$1,]) {} -//│ fun get1$Y$2 = (this,) => let obj = this in if obj is ‹(Y$2(foo,)) then foo; else error› -//│ fun main$$4 = () => let obj = a$1 in if obj is ‹(Y$2) then get2$Y$2(obj,); else error› -//│ fun main$$3 = () => let obj = a$1 in if obj is ‹(Y$2) then get1$Y$2(obj,); else error› -//│ fun get2$Y$2 = (this,) => let obj = this in if obj is ‹(Y$2(foo,)) then foo; else error› -//│ let a$1 = Y$2(X$1(),) -//│ Code(List(main$$3())) -//│ Code(List(main$$4())) +//│ class X() +//│ class Y(foo: X) { +//│ fun get1: () -> X +//│ fun get2: X //│ } -//│ class X$1() -//│ class Y$2(foo: X$1) -//│ fun get1$Y$2: Object -> X$1 -//│ fun main$$4: () -> X$1 -//│ fun main$$3: () -> X$1 -//│ fun get2$Y$2: Object -> X$1 -//│ let a$1: Y$2 -//│ X$1 -//│ Code generation encountered an error: -//│ unguarded recursive use of by-value binding a$1 +//│ val a: Y +//│ X +//│ +//│ Simpledef: +//│ {class X() {} +//│ class Y(val foo: X,) {fun get1 = () => foo +//│ fun get2 = foo} +//│ let a = Y(X(),) +//│ let selRes$24 = a in case selRes$24 of { Y => (selRes$24).get1() } +//│ let selRes$32 = a in case selRes$32 of { Y => (selRes$32).get2 }} +//│ End simpledef +//│ +//│ a +//│ = Y {} +//│ res +//│ = X {} +//│ res +//│ = X {} -:mono class I() {} class J() {} -class K(foo: I, bar: J) { +class K(val foo: I, val bar: J) { fun getFoo = foo fun getBar = bar } val k = K(I(), J()) k.getFoo k.getBar -//│ Lifted: -//│ TypingUnit { -//│ class I$1([]) {} -//│ class J$2([]) {} -//│ class K$3([foo: I$1, bar: J$2,]) {fun getFoo = (this).foo; fun getBar = (this).bar} -//│ let k$1 = K$3(I$1(), J$2(),) -//│ Code(List((k$1).getFoo)) -//│ Code(List((k$1).getBar)) -//│ } -//│ Mono: -//│ TypingUnit { -//│ class I$1([]) {} -//│ class J$2([]) {} -//│ class K$3([foo: I$1, bar: J$2,]) {} -//│ fun getFoo$K$3 = (this,) => let obj = this in if obj is ‹(K$3(foo, bar,)) then foo; else error› -//│ let k$1 = K$3(I$1(), J$2(),) -//│ fun main$$5 = () => let obj = k$1 in if obj is ‹(K$3) then getBar$K$3(obj,); else error› -//│ fun main$$4 = () => let obj = k$1 in if obj is ‹(K$3) then getFoo$K$3(obj,); else error› -//│ fun getBar$K$3 = (this,) => let obj = this in if obj is ‹(K$3(foo, bar,)) then bar; else error› -//│ Code(List(main$$4())) -//│ Code(List(main$$5())) +//│ class I() +//│ class J() +//│ class K(foo: I, bar: J) { +//│ fun getBar: J +//│ fun getFoo: I //│ } -//│ class I$1() -//│ class J$2() -//│ class K$3(foo: I$1, bar: J$2) -//│ fun getFoo$K$3: Object -> I$1 -//│ let k$1: K$3 -//│ fun main$$5: () -> J$2 -//│ fun main$$4: () -> I$1 -//│ fun getBar$K$3: Object -> J$2 -//│ J$2 -//│ k$1 -//│ = K$3 {} +//│ val k: K +//│ J +//│ +//│ Simpledef: +//│ {class I() {} +//│ class J() {} +//│ class K(val foo: I, val bar: J,) {fun getFoo = foo +//│ fun getBar = bar} +//│ let k = K(I(), J(),) +//│ let selRes$26 = k in case selRes$26 of { K => (selRes$26).getFoo } +//│ let selRes$30 = k in case selRes$30 of { K => (selRes$30).getBar }} +//│ End simpledef +//│ +//│ k +//│ = K {} //│ res -//│ = I$1 {} +//│ = I {} //│ res -//│ = J$2 {} +//│ = J {} diff --git a/compiler/shared/test/diff/Defunctionalize/ObjFields.mls b/compiler/shared/test/diff/Defunctionalize/ObjFields.mls new file mode 100644 index 00000000..970a87b4 --- /dev/null +++ b/compiler/shared/test/diff/Defunctionalize/ObjFields.mls @@ -0,0 +1,34 @@ +:NewDefs + + +class X(val bar: Int) {} +class Y(val bar: Str) {} +class A(val foo: X) {} +class B(val foo: Y) {} +fun getObj(pred) = if pred then A(X(1)) else B(Y("abc")) +val x = getObj(true) +x.foo.bar +//│ class X(bar: Int) +//│ class Y(bar: Str) +//│ class A(foo: X) +//│ class B(foo: Y) +//│ fun getObj: Bool -> (A | B) +//│ val x: A | B +//│ Int | Str +//│ +//│ Simpledef: +//│ {class X(val bar: Int,) {} +//│ class Y(val bar: Str,) {} +//│ class A(val foo: X,) {} +//│ class B(val foo: Y,) {} +//│ fun getObj = (pred::0,) => if (pred) then A(X(1,),) else B(Y("abc",),) +//│ let x = getObj(true,) +//│ let selRes$54 = let selRes$56 = x in case selRes$56 of { A => (selRes$56).foo +//│ B => (selRes$56).foo } in case selRes$54 of { Y => (selRes$54).bar +//│ X => (selRes$54).bar }} +//│ End simpledef +//│ +//│ x +//│ = A {} +//│ res +//│ = 1 diff --git a/compiler/shared/test/diff/Defunctionalize/ObjMultiFields.mls b/compiler/shared/test/diff/Defunctionalize/ObjMultiFields.mls new file mode 100644 index 00000000..5e4d46a1 --- /dev/null +++ b/compiler/shared/test/diff/Defunctionalize/ObjMultiFields.mls @@ -0,0 +1,40 @@ +:NewDefs + + +class X(val foo: Int, val bar: Bool) {} +class Y(val foo: Str, val bar: Int) {} +class A(val foo: X) {} +class B(val foo: Y) {} +fun foo(pred) = if pred then A(X(1, false)) else B(Y("abc", 5)) +val x = foo(true) +x.foo.bar +foo(false).foo.bar +//│ class X(foo: Int, bar: Bool) +//│ class Y(foo: Str, bar: Int) +//│ class A(foo: X) +//│ class B(foo: Y) +//│ fun foo: Bool -> (A | B) +//│ val x: A | B +//│ Int | false | true +//│ +//│ Simpledef: +//│ {class X(val foo: Int, val bar: Bool,) {} +//│ class Y(val foo: Str, val bar: Int,) {} +//│ class A(val foo: X,) {} +//│ class B(val foo: Y,) {} +//│ fun foo = (pred::0,) => if (pred) then A(X(1, false,),) else B(Y("abc", 5,),) +//│ let x = foo(true,) +//│ let selRes$58 = let selRes$60 = x in case selRes$60 of { A => (selRes$60).foo +//│ B => (selRes$60).foo } in case selRes$58 of { Y => (selRes$58).bar +//│ X => (selRes$58).bar } +//│ let selRes$64 = let selRes$66 = foo(false,) in case selRes$66 of { B => (selRes$66).foo +//│ A => (selRes$66).foo } in case selRes$64 of { X => (selRes$64).bar +//│ Y => (selRes$64).bar }} +//│ End simpledef +//│ +//│ x +//│ = A {} +//│ res +//│ = false +//│ res +//│ = 5 diff --git a/compiler/shared/test/diff/Defunctionalize/ObjsSelection.mls b/compiler/shared/test/diff/Defunctionalize/ObjsSelection.mls new file mode 100644 index 00000000..8fa4f5e5 --- /dev/null +++ b/compiler/shared/test/diff/Defunctionalize/ObjsSelection.mls @@ -0,0 +1,42 @@ +:NewDefs + +class X() { + val num = 6 +} +class Y() { + val num = true +} +fun foo(pred) = if pred then X() else Y() +fun id(x) = x +val a = foo(true) +val b = id(a) +b.num +//│ class X() { +//│ val num: 6 +//│ } +//│ class Y() { +//│ val num: true +//│ } +//│ fun foo: Bool -> (X | Y) +//│ fun id: forall 'a. 'a -> 'a +//│ val a: X | Y +//│ val b: X | Y +//│ 6 | true +//│ +//│ Simpledef: +//│ {class X() {let num = 6} +//│ class Y() {let num = true} +//│ fun foo = (pred::0,) => if (pred) then X() else Y() +//│ fun id = (x::1,) => x +//│ let a = foo(true,) +//│ let b = id(a,) +//│ let selRes$48 = b in case selRes$48 of { Y => (selRes$48).num +//│ X => (selRes$48).num }} +//│ End simpledef +//│ +//│ a +//│ = X {} +//│ b +//│ = X {} +//│ res +//│ = 6 diff --git a/compiler/shared/test/diff/Defunctionalize/OldMonoList.mls b/compiler/shared/test/diff/Defunctionalize/OldMonoList.mls index 93b44c0b..82a68fd5 100644 --- a/compiler/shared/test/diff/Defunctionalize/OldMonoList.mls +++ b/compiler/shared/test/diff/Defunctionalize/OldMonoList.mls @@ -1,52 +1,45 @@ :NewDefs -:AllowRuntimeErrors -// FIXME -:mono -class List(e: Int, tail: List | Nil) { +class List(val e: Int, val tail: List | Nil) { fun map: (Int -> Int) -> List - fun map(f)= new List(f(e), tail.map(f)) + fun map(f)= List(f(e), tail.map(f)) fun count(): Int fun count() = 1 + tail.count() } class Nil() { - fun map(f) = this + fun map(f) = Nil() fun count() = 0 } fun add2(x) = x+2 -(new List(1, new List(2, new Nil()))).map(x => x+1).map(x => add2(x)) -//│ Lifted: -//│ TypingUnit { -//│ class List$1([e: Int, tail: |(List$1, Nil$2,),]) { -//│ fun map = (Int -> Int) -> List$1 -//│ fun map = (f,) => (new List$1)(f((this).e,), ((this).tail).map(f,),) -//│ fun count = () -> Int -//│ fun count = () => +(1, ((this).tail).count(),) -//│ } -//│ class Nil$2([]) {fun map = (f,) => this; fun count = () => 0} -//│ class Lambda1$2$3([]) {fun apply = (x,) => +(x, 1,)} -//│ class Lambda1$3$4([]) {fun apply = (x,) => add2$1(x,)} -//│ fun add2$1 = (x,) => +(x, 2,) -//│ Code(List((('(' (new List$1)(1, (new List$1)(2, (new Nil$2)(),),) ')').map({Lambda1$2$3()},)).map({Lambda1$3$4()},))) +(List(1, List(2, Nil()))).map(x => x+1).map(x => add2(x)) +//│ class List(e: Int, tail: List | Nil) { +//│ fun count: () -> Int +//│ fun map: (Int -> Int) -> List //│ } -//│ Mono: -//│ TypingUnit { -//│ class Lambda1$3$4([]) {} -//│ class Nil$2([]) {} -//│ class List$1([e: Int, tail: |(List$1, Nil$2,),]) {} -//│ class Lambda1$2$3([]) {} -//│ fun map$List$1 = (this, f,) => (new List$1)(let obj = f in if obj is ‹(Lambda1$3$4) then apply$Lambda1$3$4(obj, let obj = this in if obj is ‹(List$1(e, tail,)) then e; else error›,); (Lambda1$2$3) then apply$Lambda1$2$3(obj, let obj = this in if obj is ‹(List$1(e, tail,)) then e; else error›,); else error›, let obj = let obj = this in if obj is ‹(List$1(e, tail,)) then tail; else error› in if obj is ‹(List$1) then map$List$1(obj, f,); (Nil$2) then map$Nil$2(obj, f,); else error›,) -//│ fun add2$1 = (x,) => +(x, 2,) -//│ fun main$$5 = () => let obj = let obj = '(' (new List$1)(1, (new List$1)(2, (new Nil$2)(),),) ')' in if obj is ‹(List$1) then map$List$1(obj, {Lambda1$2$3()},); else error› in if obj is ‹(List$1) then map$List$1(obj, {Lambda1$3$4()},); else error› -//│ fun apply$Lambda1$3$4 = (this, x,) => add2$1(x,) -//│ fun map$Nil$2 = (this, f,) => this -//│ fun apply$Lambda1$2$3 = (this, x,) => +(x, 1,) -//│ Code(List(main$$5())) +//│ class Nil() { +//│ fun count: () -> 0 +//│ fun map: anything -> Nil //│ } -//│ /!!!\ Uncaught error: java.lang.Exception: Internal Error: the `if` expression has already been desugared, please make sure that the objects are copied +//│ fun add2: Int -> Int +//│ List +//│ +//│ Simpledef: +//│ {class List(val e: Int, val tail: |(List, Nil,),) {fun map: (Int -> Int) -> List +//│ fun map = (f::0,) => List(f(e,), let selRes$16 = tail in case selRes$16 of { List => (selRes$16).map(f,) +//│ Nil => (selRes$16).map(f,) },) +//│ fun count: () -> Int +//│ fun count = () => +(1, let selRes$38 = tail in case selRes$38 of { List => (selRes$38).count() +//│ Nil => (selRes$38).count() },)} +//│ class Nil() {fun map = (f::1,) => Nil() +//│ fun count = () => 0} +//│ fun add2 = (x::2,) => +(x, 2,) +//│ let selRes$82 = let selRes$84 = '(' List(1, List(2, Nil(),),) ')' in case selRes$84 of { List => (selRes$84).map((x::3,) => +(x, 1,),) } in case selRes$82 of { List => (selRes$82).map((x::4,) => add2(x,),) }} +//│ End simpledef +//│ +//│ res +//│ = List {} -:mono -class List(e: Int, tail: List | Nil) { +class List(val e: Int, val tail: List | Nil) { fun count(): Int fun count() = 1 + tail.count() } @@ -55,51 +48,37 @@ class Nil() { } fun foo(x) = x.count() fun generate(x) = - if x > 0 then new List(x, generate(x+1)) else new Nil() + if x > 0 then List(x, generate(x-1)) else Nil() foo(List(1, List(2, Nil()))) -foo(generate(1)) -//│ Lifted: -//│ TypingUnit { -//│ class List$1([e: Int, tail: |(List$1, Nil$2,),]) { -//│ fun count = () -> Int -//│ fun count = () => +(1, ((this).tail).count(),) -//│ } -//│ class Nil$2([]) {fun count = () => 0} -//│ fun foo$1 = (x,) => (x).count() -//│ fun generate$2 = (x,) => {if (>(x, 0,)) then (new List$1)(x, generate$2(+(x, 1,),),) else (new Nil$2)()} -//│ Code(List(foo$1(List$1(1, List$1(2, Nil$2(),),),))) -//│ Code(List(foo$1(generate$2(1,),))) +foo(generate(50)) +//│ class List(e: Int, tail: List | Nil) { +//│ fun count: () -> Int //│ } -//│ Mono: -//│ TypingUnit { -//│ class Nil$2([]) {} -//│ class List$1([e: Int, tail: |(List$1, Nil$2,),]) {} -//│ fun foo$1 = (x,) => let obj = x in if obj is ‹(Nil$2) then count$Nil$2(obj,); (List$1) then count$List$1(obj,); else error› -//│ fun count$Nil$2 = (this,) => 0 -//│ fun count$List$1 = (this,) => +(1, let obj = let obj = this in if obj is ‹(List$1(e, tail,)) then tail; else error› in if obj is ‹(Nil$2) then count$Nil$2(obj,); (List$1) then count$List$1(obj,); else error›,) -//│ fun generate$2 = (x,) => {if (>(x, 0,)) then (new List$1)(x, generate$2(+(x, 1,),),) else (new Nil$2)()} -//│ fun main$$5 = () => foo$1(generate$2(1,),) -//│ fun main$$4 = () => foo$1(List$1(1, List$1(2, Nil$2(),),),) -//│ Code(List(main$$4())) -//│ Code(List(main$$5())) +//│ class Nil() { +//│ fun count: () -> 0 //│ } -//│ class Nil$2() -//│ class List$1(e: Int, tail: List$1 | Nil$2) -//│ fun foo$1: Object -> Int -//│ fun count$Nil$2: anything -> 0 -//│ fun count$List$1: Object -> Int -//│ fun generate$2: Int -> (List$1 | Nil$2) -//│ fun main$$5: () -> Int -//│ fun main$$4: () -> Int +//│ fun foo: forall 'a. {count: () -> 'a} -> 'a +//│ fun generate: Int -> (List | Nil) //│ Int +//│ +//│ Simpledef: +//│ {class List(val e: Int, val tail: |(List, Nil,),) {fun count: () -> Int +//│ fun count = () => +(1, let selRes$10 = tail in case selRes$10 of { List => (selRes$10).count() +//│ Nil => (selRes$10).count() },)} +//│ class Nil() {fun count = () => 0} +//│ fun foo = (x::5,) => let selRes$32 = x in case selRes$32 of { Nil => (selRes$32).count() +//│ List => (selRes$32).count() } +//│ fun generate = (x::6,) => {if (>(x, 0,)) then List(x, generate(-(x, 1,),),) else Nil()} +//│ foo(List(1, List(2, Nil(),),),) +//│ foo(generate(50,),)} +//│ End simpledef +//│ //│ res //│ = 2 //│ res -//│ Runtime error: -//│ RangeError: Maximum call stack size exceeded +//│ = 50 -:mono -class Cons(e: 'A, tail: Cons | Nil) { +class Cons(val e: 'A, val tail: Cons | Nil) { fun count(): Int fun count() = 1 + tail.count() } @@ -110,7 +89,7 @@ class Lambda(){ fun apply(l) = l.count() } -class Lambda2(a: Int){ +class Lambda2(val a: Int){ fun apply(l) = ( Cons(a, l)).count() } @@ -118,56 +97,42 @@ fun foo(x) = x.apply(Cons(1, Nil())) + x.apply(Nil()) foo(Lambda()) foo(Lambda2(2)) -//│ Lifted: -//│ TypingUnit { -//│ class Cons$1([e: 'A, tail: |(Cons$1, Nil$2,),]) { -//│ fun count = () -> Int -//│ fun count = () => +(1, ((this).tail).count(),) -//│ } -//│ class Nil$2([]) {fun count = () => 0} -//│ class Lambda$3([]) {fun apply = (l,) => {(l).count()}} -//│ class Lambda2$4([a: Int,]) { -//│ fun apply = (l,) => {('(' Cons$1((this).a, l,) ')').count()} -//│ } -//│ fun foo$1 = (x,) => {+((x).apply(Cons$1(1, Nil$2(),),), (x).apply(Nil$2(),),)} -//│ Code(List(foo$1(Lambda$3(),))) -//│ Code(List(foo$1(Lambda2$4(2,),))) +//│ class Cons(e: nothing, tail: Cons | Nil) { +//│ fun count: () -> Int +//│ } +//│ class Nil() { +//│ fun count: () -> 0 +//│ } +//│ class Lambda() { +//│ fun apply: forall 'a. {count: () -> 'a} -> 'a //│ } -//│ Mono: -//│ TypingUnit { -//│ class Nil$2([]) {} -//│ class Lambda2$4([a: Int,]) {} -//│ class Cons$1([e: 'A, tail: |(Cons$1, Nil$2,),]) {} -//│ class Lambda$3([]) {} -//│ fun count$Cons$1 = (this,) => +(1, let obj = let obj = this in if obj is ‹(Cons$1(e, tail,)) then tail; else error› in if obj is ‹(Nil$2) then count$Nil$2(obj,); (Cons$1) then count$Cons$1(obj,); else error›,) -//│ fun foo$1 = (x,) => {+(let obj = x in if obj is ‹(Lambda$3) then apply$Lambda$3(obj, Cons$1(1, Nil$2(),),); (Lambda2$4) then apply$Lambda2$4(obj, Cons$1(1, Nil$2(),),); else error›, let obj = x in if obj is ‹(Lambda$3) then apply$Lambda$3(obj, Nil$2(),); (Lambda2$4) then apply$Lambda2$4(obj, Nil$2(),); else error›,)} -//│ fun apply$Lambda$3 = (this, l,) => {let obj = l in if obj is ‹(Nil$2) then count$Nil$2(obj,); (Cons$1) then count$Cons$1(obj,); else error›} -//│ fun count$Nil$2 = (this,) => 0 -//│ fun apply$Lambda2$4 = (this, l,) => {let obj = '(' Cons$1(let obj = this in if obj is ‹(Lambda2$4) then 2; else error›, l,) ')' in if obj is ‹(Cons$1) then count$Cons$1(obj,); else error›} -//│ fun main$$6 = () => foo$1(Lambda2$4(2,),) -//│ fun main$$5 = () => foo$1(Lambda$3(),) -//│ Code(List(main$$5())) -//│ Code(List(main$$6())) +//│ class Lambda2(a: Int) { +//│ fun apply: (Cons | Nil) -> Int //│ } -//│ class Nil$2() -//│ class Lambda2$4(a: Int) -//│ class Cons$1(e: nothing, tail: Cons$1 | Nil$2) -//│ class Lambda$3() -//│ fun count$Cons$1: Object -> Int -//│ fun foo$1: Object -> Int -//│ fun apply$Lambda$3: (anything, Object) -> Int -//│ fun count$Nil$2: anything -> 0 -//│ fun apply$Lambda2$4: (Object, Cons$1 | Nil$2) -> Int -//│ fun main$$6: () -> Int -//│ fun main$$5: () -> Int +//│ fun foo: {apply: (Cons | Nil) -> Int} -> Int //│ Int +//│ +//│ Simpledef: +//│ {class Cons(val e: 'A, val tail: |(Cons, Nil,),) {fun count: () -> Int +//│ fun count = () => +(1, let selRes$10 = tail in case selRes$10 of { Cons => (selRes$10).count() +//│ Nil => (selRes$10).count() },)} +//│ class Nil() {fun count = () => 0} +//│ class Lambda() {fun apply = (l::7,) => {let selRes$32 = l in case selRes$32 of { Cons => (selRes$32).count() +//│ Nil => (selRes$32).count() }}} +//│ class Lambda2(val a: Int,) {fun apply = (l::8,) => {let selRes$48 = '(' Cons(a, l,) ')' in case selRes$48 of { Cons => (selRes$48).count() }}} +//│ fun foo = (x::9,) => {+(let selRes$74 = x in case selRes$74 of { Lambda2 => (selRes$74).apply(Cons(1, Nil(),),) +//│ Lambda => (selRes$74).apply(Cons(1, Nil(),),) }, let selRes$96 = x in case selRes$96 of { Lambda2 => (selRes$96).apply(Nil(),) +//│ Lambda => (selRes$96).apply(Nil(),) },)} +//│ foo(Lambda(),) +//│ foo(Lambda2(2,),)} +//│ End simpledef +//│ //│ res //│ = 1 //│ res //│ = 3 -:mono -class Cons(e: Int, tail: Cons | Nil) { +class Cons(val e: Int, val tail: Cons | Nil) { fun count(): Int fun count() = 1 + tail.count() } @@ -178,47 +143,26 @@ fun foo(x) = x(Cons(1, Nil())) + x(Nil()) foo(l => l.count()) foo(l => (Cons(2, l)).count()) -//│ Lifted: -//│ TypingUnit { -//│ class Cons$1([e: Int, tail: |(Cons$1, Nil$2,),]) { -//│ fun count = () -> Int -//│ fun count = () => +(1, ((this).tail).count(),) -//│ } -//│ class Nil$2([]) {fun count = () => 0} -//│ class Lambda1$2$3([]) {fun apply = (l,) => (l).count()} -//│ class Lambda1$3$4([]) {fun apply = (l,) => ('(' Cons$1(2, l,) ')').count()} -//│ fun foo$1 = (x,) => {+(x(Cons$1(1, Nil$2(),),), x(Nil$2(),),)} -//│ Code(List(foo$1({Lambda1$2$3()},))) -//│ Code(List(foo$1({Lambda1$3$4()},))) +//│ class Cons(e: Int, tail: Cons | Nil) { +//│ fun count: () -> Int //│ } -//│ Mono: -//│ TypingUnit { -//│ class Lambda1$3$4([]) {} -//│ class Nil$2([]) {} -//│ class Cons$1([e: Int, tail: |(Cons$1, Nil$2,),]) {} -//│ class Lambda1$2$3([]) {} -//│ fun count$Cons$1 = (this,) => +(1, let obj = let obj = this in if obj is ‹(Cons$1(e, tail,)) then tail; else error› in if obj is ‹(Nil$2) then count$Nil$2(obj,); (Cons$1) then count$Cons$1(obj,); else error›,) -//│ fun foo$1 = (x,) => {+(let obj = x in if obj is ‹(Lambda1$2$3) then apply$Lambda1$2$3(obj, Cons$1(1, Nil$2(),),); (Lambda1$3$4) then apply$Lambda1$3$4(obj, Cons$1(1, Nil$2(),),); else error›, let obj = x in if obj is ‹(Lambda1$2$3) then apply$Lambda1$2$3(obj, Nil$2(),); (Lambda1$3$4) then apply$Lambda1$3$4(obj, Nil$2(),); else error›,)} -//│ fun count$Nil$2 = (this,) => 0 -//│ fun main$$6 = () => foo$1({Lambda1$3$4()},) -//│ fun main$$5 = () => foo$1({Lambda1$2$3()},) -//│ fun apply$Lambda1$3$4 = (this, l,) => let obj = '(' Cons$1(2, l,) ')' in if obj is ‹(Cons$1) then count$Cons$1(obj,); else error› -//│ fun apply$Lambda1$2$3 = (this, l,) => let obj = l in if obj is ‹(Nil$2) then count$Nil$2(obj,); (Cons$1) then count$Cons$1(obj,); else error› -//│ Code(List(main$$5())) -//│ Code(List(main$$6())) +//│ class Nil() { +//│ fun count: () -> 0 //│ } -//│ class Lambda1$3$4() -//│ class Nil$2() -//│ class Cons$1(e: Int, tail: Cons$1 | Nil$2) -//│ class Lambda1$2$3() -//│ fun count$Cons$1: Object -> Int -//│ fun foo$1: Object -> Int -//│ fun count$Nil$2: anything -> 0 -//│ fun main$$6: () -> Int -//│ fun main$$5: () -> Int -//│ fun apply$Lambda1$3$4: (anything, Cons$1 | Nil$2) -> Int -//│ fun apply$Lambda1$2$3: (anything, Object) -> Int +//│ fun foo: ((Cons | Nil) -> Int) -> Int //│ Int +//│ +//│ Simpledef: +//│ {class Cons(val e: Int, val tail: |(Cons, Nil,),) {fun count: () -> Int +//│ fun count = () => +(1, let selRes$10 = tail in case selRes$10 of { Cons => (selRes$10).count() +//│ Nil => (selRes$10).count() },)} +//│ class Nil() {fun count = () => 0} +//│ fun foo = (x::10,) => {+(x(Cons(1, Nil(),),), x(Nil(),),)} +//│ foo((l::11,) => let selRes$78 = l in case selRes$78 of { Cons => (selRes$78).count() +//│ Nil => (selRes$78).count() },) +//│ foo((l::12,) => let selRes$96 = '(' Cons(2, l,) ')' in case selRes$96 of { Cons => (selRes$96).count() },)} +//│ End simpledef +//│ //│ res //│ = 1 //│ res diff --git a/compiler/shared/test/diff/Defunctionalize/Polymorphic.mls b/compiler/shared/test/diff/Defunctionalize/Polymorphic.mls index 7ea50696..9f288de5 100644 --- a/compiler/shared/test/diff/Defunctionalize/Polymorphic.mls +++ b/compiler/shared/test/diff/Defunctionalize/Polymorphic.mls @@ -1,38 +1,31 @@ :NewDefs -:ge //TODO: Wrap resulting statements in module -:mono let b = true -class OneInt(a: Int){ +class OneInt(val a: Int){ fun get = () -> a } -class OneBool(b: Bool){ +class OneBool(val b: Bool){ fun get = () -> b } (if b then OneInt(1) else OneBool(true)).get() -//│ Lifted: -//│ TypingUnit { -//│ class OneInt$1([a: Int,]) {fun get = () => (this).a} -//│ class OneBool$2([b: Bool,]) {fun get = () => b$1} -//│ let b$1 = true -//│ Code(List(('(' if (b$1) then OneInt$1(1,) else OneBool$2(true,) ')').get())) +//│ let b: true +//│ class OneInt(a: Int) { +//│ fun get: () -> Int //│ } -//│ Mono: -//│ TypingUnit { -//│ class OneInt$1([a: Int,]) {} -//│ class OneBool$2([b: Bool,]) {} -//│ fun get$OneInt$1 = (this,) => let obj = this in if obj is ‹(OneInt$1) then 1; else error› -//│ fun get$OneBool$2 = (this,) => b$1 -//│ fun main$$3 = () => let obj = '(' if (b$1) then OneInt$1(1,) else OneBool$2(true,) ')' in if obj is ‹(OneBool$2) then get$OneBool$2(obj,); (OneInt$1) then get$OneInt$1(obj,); else error› -//│ let b$1 = true -//│ Code(List(main$$3())) +//│ class OneBool(b: Bool) { +//│ fun get: () -> Bool //│ } -//│ class OneInt$1(a: Int) -//│ class OneBool$2(b: Bool) -//│ fun get$OneInt$1: Object -> 1 -//│ fun get$OneBool$2: anything -> true -//│ fun main$$3: () -> (1 | true) -//│ let b$1: true -//│ 1 | true -//│ Code generation encountered an error: -//│ unguarded recursive use of by-value binding b$1 +//│ Int | false | true +//│ +//│ Simpledef: +//│ {let b = true +//│ class OneInt(val a: Int,) {fun get = () => a} +//│ class OneBool(val b: Bool,) {fun get = () => b} +//│ let selRes$20 = '(' if (b) then OneInt(1,) else OneBool(true,) ')' in case selRes$20 of { OneInt => (selRes$20).get() +//│ OneBool => (selRes$20).get() }} +//│ End simpledef +//│ +//│ b +//│ = true +//│ res +//│ = 1 diff --git a/compiler/shared/test/diff/Defunctionalize/Record.mls b/compiler/shared/test/diff/Defunctionalize/Record.mls new file mode 100644 index 00000000..999b5be0 --- /dev/null +++ b/compiler/shared/test/diff/Defunctionalize/Record.mls @@ -0,0 +1,31 @@ +:NewDefs + + +class K() { + val f = true +} +val x = { + 5: "five", + 7: "seven", + f: 6 +} +fun muddle(pred) = if pred then K() else x +muddle(false).f +//│ class K() { +//│ val f: true +//│ } +//│ val x: {5: "five", 7: "seven", f: 6} +//│ fun muddle: Bool -> (K | {5: "five", 7: "seven", f: 6}) +//│ 6 | true +//│ +//│ Simpledef: +//│ {class K() {let f = true} +//│ let x = '{' {5: "five", 7: "seven", f: 6} '}' +//│ fun muddle = (pred::0,) => if (pred) then K() else x +//│ (muddle(false,)).f} +//│ End simpledef +//│ +//│ x +//│ = { '5': 'five', '7': 'seven', f: 6 } +//│ res +//│ = 6 diff --git a/compiler/shared/test/diff/Defunctionalize/RecursiveFunc.mls b/compiler/shared/test/diff/Defunctionalize/RecursiveFunc.mls index efa36b2e..0953f491 100644 --- a/compiler/shared/test/diff/Defunctionalize/RecursiveFunc.mls +++ b/compiler/shared/test/diff/Defunctionalize/RecursiveFunc.mls @@ -1,28 +1,22 @@ :NewDefs -:mono fun fac(n) = if (n > 1) then fac(n - 1) * n else 1 fac(5) -//│ Lifted: -//│ TypingUnit { -//│ fun fac$1 = (n,) => {if ('(' >(n, 1,) ')') then *(fac$1(-(n, 1,),), n,) else 1} -//│ Code(List(fac$1(5,))) -//│ } -//│ Mono: -//│ TypingUnit { -//│ fun fac$1 = (n,) => {if ('(' >(n, 1,) ')') then *(fac$1(-(n, 1,),), n,) else 1} -//│ fun main$$1 = () => fac$1(5,) -//│ Code(List(main$$1())) -//│ } -//│ fun fac$1: Int -> Int -//│ fun main$$1: () -> Int +//│ fun fac: Int -> Int //│ Int +//│ +//│ Simpledef: +//│ {fun fac = (n::0,) => {if ('(' >(n, 1,) ')') then *(fac(-(n, 1,),), n,) else 1} +//│ fac(5,)} +//│ End simpledef +//│ //│ res //│ = 120 -// FIXME: Strange syntax -:mono +// TODO: Support for specialized pattern matching types +// In this example, the type of l in count(l) would need to be constrained to +// object & ~(), which requires implementing neg types, intersections, etc. class List(val l: List | Nil | undefined, val hasTail: Bool) {} class Nil(val l: List | Nil | undefined, val hasTail: Bool) {} fun count(lst) = @@ -31,15 +25,12 @@ fun count(lst) = if l is undefined then 1 else count(l)+1 else 0 count(new List(new List(new Nil(undefined, false), true), true)) -//│ Lifted: -//│ TypingUnit { -//│ class List$1([val l: |(|(List$1, Nil$2,), undefined,), val hasTail: Bool,]) {} -//│ class Nil$2([val l: |(|(List$1, Nil$2,), undefined,), val hasTail: Bool,]) {} -//│ let l$2 = (lst).l -//│ fun count$1 = (lst,) => {if ((lst).hasTail) then {if (is(l$2, undefined,)) then 1 else +(count$1(l$2,), 1,)} else 0} -//│ Code(List(count$1((new List$1)((new List$1)((new Nil$2)(undefined, false,), true,), true,),))) -//│ } -//│ Mono: -//│ ╔══[ERROR] Post-process failed to produce AST. -//│ ╙── +//│ class List(l: List | Nil | (), hasTail: Bool) +//│ class Nil(l: List | Nil | (), hasTail: Bool) +//│ fun count: forall 'a. 'a -> Int +//│ Int +//│ where +//│ 'a <: {hasTail: Bool, l: Object & 'a & ~() | ()} //│ +//│ Simpledef: +//│ /!!!\ Uncaught error: java.lang.Exception: Internal Error: Could not constrain (ProdObj(Some(Var(prim$Unit)),List(),List()),ConsObj(None,List((Var(l),ConsVar(15,13_selres))))) diff --git a/compiler/shared/test/diff/Defunctionalize/SelfReference.mls b/compiler/shared/test/diff/Defunctionalize/SelfReference.mls index 6f1a2ceb..06e711e6 100644 --- a/compiler/shared/test/diff/Defunctionalize/SelfReference.mls +++ b/compiler/shared/test/diff/Defunctionalize/SelfReference.mls @@ -1,28 +1,18 @@ :NewDefs :AllowRuntimeErrors -:mono fun f(x) = f(x) f(0) f(1) -//│ Lifted: -//│ TypingUnit { -//│ fun f$1 = (x,) => f$1(x,) -//│ Code(List(f$1(0,))) -//│ Code(List(f$1(1,))) -//│ } -//│ Mono: -//│ TypingUnit { -//│ fun f$1 = (x,) => f$1(x,) -//│ fun main$$2 = () => f$1(1,) -//│ fun main$$1 = () => f$1(0,) -//│ Code(List(main$$1())) -//│ Code(List(main$$2())) -//│ } -//│ fun f$1: anything -> nothing -//│ fun main$$2: () -> nothing -//│ fun main$$1: () -> nothing +//│ fun f: anything -> nothing //│ nothing +//│ +//│ Simpledef: +//│ {fun f = (x::0,) => f(x,) +//│ f(0,) +//│ f(1,)} +//│ End simpledef +//│ //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded diff --git a/compiler/shared/test/diff/Defunctionalize/SimpleClasses.mls b/compiler/shared/test/diff/Defunctionalize/SimpleClasses.mls index a5bf3936..1ae858a6 100644 --- a/compiler/shared/test/diff/Defunctionalize/SimpleClasses.mls +++ b/compiler/shared/test/diff/Defunctionalize/SimpleClasses.mls @@ -1,60 +1,41 @@ :NewDefs -:mono -class Foo(x: Int){ +class Foo(val x: Int){ fun bar(y) = x+y fun boo(z) = bar(z)+x } (Foo(1)).boo(2) -//│ Lifted: -//│ TypingUnit { -//│ class Foo$1([x: Int,]) { -//│ fun bar = (y,) => +((this).x, y,) -//│ fun boo = (z,) => +((this).bar(z,), (this).x,) -//│ } -//│ Code(List(('(' Foo$1(1,) ')').boo(2,))) +//│ class Foo(x: Int) { +//│ fun bar: Int -> Int +//│ fun boo: Int -> Int //│ } -//│ Mono: -//│ TypingUnit { -//│ class Foo$1([x: Int,]) {} -//│ fun boo$Foo$1 = (this, z,) => +(let obj = this in if obj is ‹(Foo$1) then bar$Foo$1(obj, z,); else error›, let obj = this in if obj is ‹(Foo$1) then 1; else error›,) -//│ fun bar$Foo$1 = (this, y,) => +(let obj = this in if obj is ‹(Foo$1) then 1; else error›, y,) -//│ fun main$$1 = () => let obj = '(' Foo$1(1,) ')' in if obj is ‹(Foo$1) then boo$Foo$1(obj, 2,); else error› -//│ Code(List(main$$1())) -//│ } -//│ class Foo$1(x: Int) -//│ fun boo$Foo$1: (Object, Int) -> Int -//│ fun bar$Foo$1: (Object, Int) -> Int -//│ fun main$$1: () -> Int //│ Int +//│ +//│ Simpledef: +//│ {class Foo(val x: Int,) {fun bar = (y::0,) => +(x, y,) +//│ fun boo = (z::1,) => +(bar(z,), x,)} +//│ let selRes$38 = '(' Foo(1,) ')' in case selRes$38 of { Foo => (selRes$38).boo(2,) }} +//│ End simpledef +//│ //│ res //│ = 4 -:mono -class OneInt(a: Int){ +class OneInt(val a: Int){ fun fac: () -> Int fun fac = () -> if(a > 0) then (OneInt(a - 1)).fac() else 1 } (OneInt(10)).fac() -//│ Lifted: -//│ TypingUnit { -//│ class OneInt$1([a: Int,]) { -//│ fun fac = () -> Int -//│ fun fac = () => {if ('(' >((this).a, 0,) ')') then ('(' OneInt$1(-((this).a, 1,),) ')').fac() else 1} -//│ } -//│ Code(List(('(' OneInt$1(10,) ')').fac())) -//│ } -//│ Mono: -//│ TypingUnit { -//│ class OneInt$1([a: Int,]) {} -//│ fun fac$OneInt$1 = (this,) => {if ('(' >(let obj = this in if obj is ‹(OneInt$1(a,)) then a; else error›, 0,) ')') then let obj = '(' OneInt$1(-(let obj = this in if obj is ‹(OneInt$1(a,)) then a; else error›, 1,),) ')' in if obj is ‹(OneInt$1) then fac$OneInt$1(obj,); else error› else 1} -//│ fun main$$1 = () => let obj = '(' OneInt$1(10,) ')' in if obj is ‹(OneInt$1) then fac$OneInt$1(obj,); else error› -//│ Code(List(main$$1())) +//│ class OneInt(a: Int) { +//│ fun fac: () -> Int //│ } -//│ class OneInt$1(a: Int) -//│ fun fac$OneInt$1: Object -> 1 -//│ fun main$$1: () -> 1 -//│ 1 +//│ Int +//│ +//│ Simpledef: +//│ {class OneInt(val a: Int,) {fun fac: () -> Int +//│ fun fac = () => {if ('(' >(a, 0,) ')') then let selRes$20 = '(' OneInt(-(a, 1,),) ')' in case selRes$20 of { OneInt => (selRes$20).fac() } else 1}} +//│ let selRes$50 = '(' OneInt(10,) ')' in case selRes$50 of { OneInt => (selRes$50).fac() }} +//│ End simpledef +//│ //│ res //│ = 1 diff --git a/compiler/shared/test/diff/Defunctionalize/SimpleConditionals.mls b/compiler/shared/test/diff/Defunctionalize/SimpleConditionals.mls index 275a86a1..9813462e 100644 --- a/compiler/shared/test/diff/Defunctionalize/SimpleConditionals.mls +++ b/compiler/shared/test/diff/Defunctionalize/SimpleConditionals.mls @@ -1,45 +1,29 @@ :NewDefs -:mono if true then 1 else 0 if 1+1 > 1 then 1 - 1 else 1*1 -//│ Lifted: -//│ TypingUnit { -//│ Code(List(if (true) then 1 else 0)) -//│ Code(List(if (>(+(1, 1,), 1,)) then -(1, 1,) else *(1, 1,))) -//│ } -//│ Mono: -//│ TypingUnit { -//│ fun main$$0 = () => if (true) then 1 else 0 -//│ fun main$$1 = () => if (>(+(1, 1,), 1,)) then -(1, 1,) else *(1, 1,) -//│ Code(List(main$$0())) -//│ Code(List(main$$1())) -//│ } -//│ fun main$$0: () -> (0 | 1) -//│ fun main$$1: () -> Int //│ Int +//│ +//│ Simpledef: +//│ {if (true) then 1 else 0 +//│ if (>(+(1, 1,), 1,)) then -(1, 1,) else *(1, 1,)} +//│ End simpledef +//│ //│ res //│ = 1 //│ res //│ = 0 -:mono let b = true if(b) then 1 else 2 -//│ Lifted: -//│ TypingUnit { -//│ let b$1 = true -//│ Code(List(if ('(' b$1 ')') then 1 else 2)) -//│ } -//│ Mono: -//│ TypingUnit { -//│ let b$1 = true -//│ fun main$$1 = () => if ('(' b$1 ')') then 1 else 2 -//│ Code(List(main$$1())) -//│ } -//│ let b$1: true -//│ fun main$$1: () -> (1 | 2) +//│ let b: true //│ 1 | 2 -//│ b$1 -//│ = true +//│ +//│ Simpledef: +//│ {let b = true +//│ if ('(' b ')') then 1 else 2} +//│ End simpledef +//│ +//│ b +//│ = true //│ res //│ = 1 diff --git a/compiler/shared/test/diff/Defunctionalize/SimpleFunc.mls b/compiler/shared/test/diff/Defunctionalize/SimpleFunc.mls index dbf54555..e39c192c 100644 --- a/compiler/shared/test/diff/Defunctionalize/SimpleFunc.mls +++ b/compiler/shared/test/diff/Defunctionalize/SimpleFunc.mls @@ -1,58 +1,49 @@ :NewDefs -:mono fun f(x: Bool) = if x then 42 else 1337 -//│ Lifted: -//│ TypingUnit { -//│ fun f$1 = (x: Bool,) => if (x) then 42 else 1337 -//│ } -//│ Mono: -//│ TypingUnit { -//│ fun f$1 = (x: Bool,) => if (x) then 42 else 1337 -//│ } -//│ fun f$1: (x: Bool) -> (1337 | 42) +//│ fun f: (x: Bool) -> (1337 | 42) +//│ +//│ Simpledef: +//│ {fun f = (x: Bool,) => if (x) then 42 else 1337} +//│ End simpledef +//│ -:mono fun foo() = 42 -//│ Lifted: -//│ TypingUnit {fun foo$1 = () => 42} -//│ Mono: -//│ TypingUnit {fun foo$1 = () => 42} -//│ fun foo$1: () -> 42 +//│ fun foo: () -> 42 +//│ +//│ Simpledef: +//│ {fun foo = () => 42} +//│ End simpledef +//│ -:mono fun f(x) = if(x > 0) then x+1 else x - 1 f(2)+3 -//│ Lifted: -//│ TypingUnit { -//│ fun f$1 = (x,) => {if ('(' >(x, 0,) ')') then +(x, 1,) else -(x, 1,)} -//│ Code(List(+(f$1(2,), 3,))) -//│ } -//│ Mono: -//│ TypingUnit { -//│ fun f$1 = (x,) => {if ('(' >(x, 0,) ')') then +(x, 1,) else -(x, 1,)} -//│ fun main$$1 = () => +(f$1(2,), 3,) -//│ Code(List(main$$1())) -//│ } -//│ fun f$1: Int -> Int -//│ fun main$$1: () -> Int +//│ fun f: Int -> Int //│ Int +//│ +//│ Simpledef: +//│ {fun f = (x::0,) => {if ('(' >(x, 0,) ')') then +(x, 1,) else -(x, 1,)} +//│ +(f(2,), 3,)} +//│ End simpledef +//│ //│ res //│ = 6 -// TODO: Evaluate unused terms -:mono fun foo(x, #b) = if b then x else 1337 let a = foo(42, true) let b = foo(23, false) -//│ Lifted: -//│ TypingUnit { -//│ fun foo$3 = (x, #b$2,) => if (b$2) then x else 1337 -//│ let a$1 = foo$3(42, true,) -//│ let b$2 = foo$3(23, false,) -//│ } -//│ Mono: -//│ ╔══[ERROR] Post-process failed to produce AST. -//│ ╙── +//│ fun foo: forall 'a. ('a, Bool) -> (1337 | 'a) +//│ let a: 1337 | 42 +//│ let b: 1337 | 23 +//│ +//│ Simpledef: +//│ {fun foo = (x::1, #b::2,) => if (b) then x else 1337 +//│ let a = foo(42, true,) +//│ let b = foo(23, false,)} +//│ End simpledef //│ +//│ a +//│ = 42 +//│ b +//│ = 1337 diff --git a/compiler/shared/test/diff/Defunctionalize/Simpledef.mls b/compiler/shared/test/diff/Defunctionalize/Simpledef.mls new file mode 100644 index 00000000..5a9efa24 --- /dev/null +++ b/compiler/shared/test/diff/Defunctionalize/Simpledef.mls @@ -0,0 +1,82 @@ +:NewDefs + +class X() { + val num = 6 +} +class Y() { + val num = true +} +fun foo(pred) = if pred then X() else Y() +foo(true).num +//│ class X() { +//│ val num: 6 +//│ } +//│ class Y() { +//│ val num: true +//│ } +//│ fun foo: Bool -> (X | Y) +//│ 6 | true +//│ +//│ Simpledef: +//│ {class X() {let num = 6} +//│ class Y() {let num = true} +//│ fun foo = (pred::0,) => if (pred) then X() else Y() +//│ let selRes$26 = foo(true,) in case selRes$26 of { Y => (selRes$26).num +//│ X => (selRes$26).num }} +//│ End simpledef +//│ +//│ res +//│ = 6 + +class X() { + val num = 6 +} +class Y() { + val num = true +} +class A() { + val num = X() +} +class B() { + val num = Y() +} +class C() { + val num = X() +} +fun foo(pred) = + if pred == 1 then A() else + if pred == 2 then B() else C() +foo(5).num.num +//│ class X() { +//│ val num: 6 +//│ } +//│ class Y() { +//│ val num: true +//│ } +//│ class A() { +//│ val num: X +//│ } +//│ class B() { +//│ val num: Y +//│ } +//│ class C() { +//│ val num: X +//│ } +//│ fun foo: Num -> (A | B | C) +//│ 6 | true +//│ +//│ Simpledef: +//│ {class X() {let num = 6} +//│ class Y() {let num = true} +//│ class A() {let num = X()} +//│ class B() {let num = Y()} +//│ class C() {let num = X()} +//│ fun foo = (pred::1,) => {if (==(pred, 1,)) then A() else {if (==(pred, 2,)) then B() else C()}} +//│ let selRes$74 = let selRes$76 = foo(5,) in case selRes$76 of { B => (selRes$76).num +//│ C => (selRes$76).num +//│ A => (selRes$76).num } in case selRes$74 of { X => (selRes$74).num +//│ Y => (selRes$74).num }} +//│ End simpledef +//│ +//│ res +//│ = 6 diff --git a/compiler/shared/test/diff/Defunctionalize/TupleSelect.mls b/compiler/shared/test/diff/Defunctionalize/TupleSelect.mls new file mode 100644 index 00000000..b5b33254 --- /dev/null +++ b/compiler/shared/test/diff/Defunctionalize/TupleSelect.mls @@ -0,0 +1,12 @@ +:NewDefs + + +[0,"abc",()].2 +//│ () +//│ +//│ Simpledef: +//│ {([0, "abc", undefined,]).2} +//│ End simpledef +//│ +//│ res +//│ = undefined diff --git a/compiler/shared/test/diff/Lifter/FunctionTypeAnnotations.mls b/compiler/shared/test/diff/Lifter/FunctionTypeAnnotations.mls index 6e84cfca..4eeb76d3 100644 --- a/compiler/shared/test/diff/Lifter/FunctionTypeAnnotations.mls +++ b/compiler/shared/test/diff/Lifter/FunctionTypeAnnotations.mls @@ -1,38 +1,18 @@ :NewDefs +:ParseOnly -:e // TODO: Preserve type annotations in lifting -:mono -fun foo(x: Int) = +:lift +fun foo(x) = (y) => x+y foo(1)(2) foo(2)(2) +//│ |#fun| |foo|(|x|)| |#=|→|(|y|)| |#=>| |x|+|y|←|↵|foo|(|1|)|(|2|)|↵|foo|(|2|)|(|2|)| +//│ Parsed: {fun foo = (x,) => {('(' y ')',) => +(x, y,)}; foo(1,)(2,); foo(2,)(2,)} //│ Lifted: //│ TypingUnit { //│ class Lambda1$2$1([x,]) {fun apply = ('(' y ')',) => +((this).x, y,)} -//│ fun foo$1 = (x: Int,) => {{Lambda1$2$1(x,)}} +//│ fun foo$1 = (x,) => {{Lambda1$2$1(x,)}} //│ Code(List(foo$1(1,)(2,))) //│ Code(List(foo$1(2,)(2,))) //│ } -//│ Mono: -//│ TypingUnit { -//│ class Lambda1$2$1([x,]) {} -//│ fun apply$Lambda1$2$1 = (this, '(' y ')',) => +(let obj = this in if obj is ‹(Lambda1$2$1(x,)) then x; else error›, y,) -//│ fun foo$1 = (x: Int,) => {{Lambda1$2$1(x,)}} -//│ fun main$$3 = () => let obj = foo$1(2,) in if obj is ‹(Lambda1$2$1) then apply$Lambda1$2$1(obj, 2,); else error› -//│ fun main$$2 = () => let obj = foo$1(1,) in if obj is ‹(Lambda1$2$1) then apply$Lambda1$2$1(obj, 2,); else error› -//│ Code(List(main$$2())) -//│ Code(List(main$$3())) -//│ } -//│ ╔══[ERROR] Class parameters currently need type annotations -//│ ║ l.6: (y) => x+y -//│ ╙── ^ -//│ class Lambda1$2$1(x: error) -//│ fun apply$Lambda1$2$1: (Object, Int) -> Int -//│ fun foo$1: (x: Int) -> Lambda1$2$1 -//│ fun main$$3: () -> Int -//│ fun main$$2: () -> Int -//│ Int -//│ res -//│ = 3 -//│ res -//│ = 4 +//│ diff --git a/compiler/shared/test/diff/Lifter/LambLift.mls b/compiler/shared/test/diff/Lifter/LambLift.mls index 31916a6f..403013ae 100644 --- a/compiler/shared/test/diff/Lifter/LambLift.mls +++ b/compiler/shared/test/diff/Lifter/LambLift.mls @@ -1,6 +1,7 @@ :NewDefs -:AllowRuntimeErrors +:ParseOnly +:lift fun foo() = let local(x) = class Foo { @@ -9,6 +10,8 @@ fun foo() = (new Foo()).bar local(1) foo() +//│ |#fun| |foo|(||)| |#=|→|#let| |local|(|x|)| |#=|→|#class| |Foo| |{|→|#fun| |bar| |#=| |x| |+| |foo|(||)|←|↵|}|↵|(|#new| |Foo|(||)|)|.bar|←|↵|local|(|1|)|←|↵|foo|(||)| +//│ Parsed: {fun foo = () => {let local = (x,) => {class Foo {fun bar = +(x, foo(),)}; ('(' (new Foo)() ')').bar}; local(1,)}; foo()} //│ Lifted: //│ TypingUnit { //│ class Foo$1([x,]) {fun bar = +((this).x, foo$1(),)} @@ -16,31 +19,30 @@ foo() //│ fun foo$1 = () => {local$2(1,)} //│ Code(List(foo$1())) //│ } -//│ fun foo: () -> Int -//│ Int -//│ res -//│ Runtime error: -//│ RangeError: Maximum call stack size exceeded +//│ +:lift fun foo(f) = f(1) foo(x => x+1) +//│ |#fun| |foo|(|f|)| |#=| |→|f|(|1|)|←|↵|foo|(|x| |#=>| |x|+|1|)| +//│ Parsed: {fun foo = (f,) => {f(1,)}; foo((x,) => +(x, 1,),)} //│ Lifted: //│ TypingUnit { //│ class Lambda1$2$1([]) {fun apply = (x,) => +(x, 1,)} //│ fun foo$1 = (f,) => {f(1,)} //│ Code(List(foo$1({Lambda1$2$1()},))) //│ } -//│ fun foo: forall 'a. (1 -> 'a) -> 'a -//│ Int -//│ res -//│ = 2 +//│ +:lift fun foo(x) = let bar(f) = f(x) bar(y => y+x) foo(1) +//│ |#fun| |foo|(|x|)| |#=| |→|#let| |bar|(|f|)| |#=| |→|f|(|x|)|←|↵|bar|(|y| |#=>| |y|+|x|)|←|↵|foo|(|1|)| +//│ Parsed: {fun foo = (x,) => {let bar = (f,) => {f(x,)}; bar((y,) => +(y, x,),)}; foo(1,)} //│ Lifted: //│ TypingUnit { //│ class Lambda1$3$1([x,]) {fun apply = (y,) => +(y, (this).x,)} @@ -48,11 +50,9 @@ foo(1) //│ fun foo$1 = (x,) => {bar$2({Lambda1$3$1(x,)}, x,)} //│ Code(List(foo$1(1,))) //│ } -//│ fun foo: Int -> Int -//│ Int -//│ res -//│ = 2 +//│ +:lift fun foo(f) = f(1) class A(y: Int){ @@ -61,6 +61,8 @@ class A(y: Int){ fun app(a) = foo(z => a.bar(z)) app(new A(1)) +//│ |#fun| |foo|(|f|)| |#=| |→|f|(|1|)|←|↵|#class| |A|(|y|#:| |Int|)|{|→|#fun| |bar|(|z|)| |#=| |y|+|z|←|↵|}|↵|#fun| |app|(|a|)| |#=| |→|foo|(|z| |#=>| |a|.bar|(|z|)|)|←|↵|app|(|#new| |A|(|1|)|)| +//│ Parsed: {fun foo = (f,) => {f(1,)}; class A(y: Int,) {fun bar = (z,) => +(y, z,)}; fun app = (a,) => {foo((z,) => (a).bar(z,),)}; app((new A)(1,),)} //│ Lifted: //│ TypingUnit { //│ class A$1([y: Int,]) {fun bar = (z,) => +((this).y, z,)} @@ -69,11 +71,4 @@ app(new A(1)) //│ fun app$1 = (a,) => {foo$2({Lambda1$3$2(a,)},)} //│ Code(List(app$1((new A$1)(1,),))) //│ } -//│ fun foo: forall 'a. (1 -> 'a) -> 'a -//│ class A(y: Int) { -//│ fun bar: Int -> Int -//│ } -//│ fun app: forall 'b. {bar: 1 -> 'b} -> 'b -//│ Int -//│ res -//│ = 2 +//│ diff --git a/compiler/shared/test/diff/Lifter/LiftNew.mls b/compiler/shared/test/diff/Lifter/LiftNew.mls index 62188389..2058910b 100644 --- a/compiler/shared/test/diff/Lifter/LiftNew.mls +++ b/compiler/shared/test/diff/Lifter/LiftNew.mls @@ -4,10 +4,6 @@ class A(num: Int) { } new A(5) -//│ TypingUnit { -//│ class A([num: Int,]) {} -//│ Code(List((new A)(5,))) -//│ } //│ Lifted: //│ TypingUnit { //│ class A$1([num: Int,]) {} diff --git a/compiler/shared/test/diff/Lifter/LiftType.mls b/compiler/shared/test/diff/Lifter/LiftType.mls index dab94231..27312aba 100644 --- a/compiler/shared/test/diff/Lifter/LiftType.mls +++ b/compiler/shared/test/diff/Lifter/LiftType.mls @@ -1,6 +1,7 @@ :NewDefs :ParseOnly +:lift class CTX{ class A {} fun foo(f: A => A): (A => A) => A = f(new A) @@ -16,6 +17,7 @@ class CTX{ //│ } //│ +:lift class CTX(x, y){ class A{ fun foo = x} class B: A { fun foo = y} @@ -33,6 +35,7 @@ class CTX(x, y){ //│ } //│ +:lift class CTX(x, y){ class A{ fun foo = x} class B: A { fun foo = y} @@ -50,6 +53,7 @@ class CTX(x, y){ //│ } //│ +:lift class CTX(x, y){ class A{ fun foo = x} class B { fun foo = y} @@ -67,6 +71,7 @@ class CTX(x, y){ //│ } //│ +:lift class CTX{ fun ctx(x,y) = class A{ fun foo = x } diff --git a/compiler/shared/test/diff/Lifter/Lifter.mls b/compiler/shared/test/diff/Lifter/Lifter.mls index 41030a10..4ba2f651 100644 --- a/compiler/shared/test/diff/Lifter/Lifter.mls +++ b/compiler/shared/test/diff/Lifter/Lifter.mls @@ -1,6 +1,7 @@ :NewDefs :ParseOnly +:lift class A(x) { class B(y) { fun getX = x @@ -56,6 +57,7 @@ class A(x) { //│ } //│ +:lift class A(x) { class B(y) { class C(z) { @@ -75,7 +77,7 @@ class A(x) { //│ } //│ - +:lift class A(x) { class B{ fun foo = 1 @@ -108,7 +110,7 @@ new C{ //│ - +:lift class Parent(x) { fun foo(x: Int): T = x+1 class Inner(y: Int){ @@ -128,7 +130,7 @@ class Parent(x) { //│ } //│ - +:lift class B {} class C {} class D(y: Int) {} @@ -149,6 +151,7 @@ class A(x: Int): ({a1: Int} & B & D(x)) { //│ // │ TypingUnit(NuTypeDef(class, B, (TypeName(T)), Tup(), (), TypingUnit()), NuTypeDef(class, C, (), Tup(), (), TypingUnit()), NuTypeDef(class, A, (TypeName(T), TypeName(U)), Tup(x: Var(Int)), (App(App(Var(&), Tup(_: Bra(rcd = true, Rcd(Var(a1) = Var(Int)})))), Tup(_: TyApp(Var(B), List(TypeName(T)))))), TypingUnit(NuFunDef(None, getA, [], Lam(Tup(), New(Some((TypeName(C),)), TypingUnit(List(fun foo = x: T, => x)))))))) +:lift class B {} class C {} class D(y: Int) {} @@ -168,6 +171,7 @@ class A(x: Int) extends {a1: Int}, B, D(x){ //│ } //│ +:lift class Child(x): ({ age: T } & { name: String}) { class Inner{ fun foo = age @@ -187,7 +191,7 @@ class Child(x): ({ age: T } & { name: String}) { //│ } //│ - +:lift class A(x: Int) { fun getA: Int = 0 fun getA1 = 1 @@ -206,7 +210,7 @@ new A(0) { //│ } //│ - +:lift class A(x) { class B(y) { } } @@ -228,7 +232,7 @@ new A(1) { //│ - +:lift class A { fun getA = 0 fun funcA = 10 @@ -260,7 +264,7 @@ new B{ //│ } //│ - +:lift class A{ class B{ fun funB = 1 @@ -296,7 +300,7 @@ class A{ //│ } //│ - +:lift class A{ class B{ fun funB = 1 @@ -349,7 +353,7 @@ class A{ //│ } //│ - +:lift class A{ class B{ fun foo = 1 @@ -367,7 +371,7 @@ new A //│ } //│ - +:lift class A(x) { fun foo = 0 fun bar = x @@ -389,6 +393,7 @@ let x = new A{ //│ } //│ +:lift class A {} new A{ fun foo = 1 diff --git a/compiler/shared/test/diff/Lifter/LifterBlks.mls b/compiler/shared/test/diff/Lifter/LifterBlks.mls index 2b1736fc..d6ee6a1f 100644 --- a/compiler/shared/test/diff/Lifter/LifterBlks.mls +++ b/compiler/shared/test/diff/Lifter/LifterBlks.mls @@ -1,6 +1,7 @@ :NewDefs :ParseOnly +:lift fun foo = print("ok") print("ko") @@ -10,6 +11,7 @@ fun foo = //│ TypingUnit {fun foo$1 = {print("ok",); print("ko",)}} //│ +:lift class A{ class B {} fun foo(x: B) = (x : B) @@ -23,6 +25,7 @@ class A{ //│ } //│ +:lift fun foo = let local(x) = class Foo { @@ -45,6 +48,7 @@ fun foo = //│ } //│ +:lift class A(y){} let f = x => new A(0){fun bar = x+y} f(0) @@ -59,6 +63,7 @@ f(0) //│ } //│ +:lift class A(x){ fun w = x fun foo(y) = @@ -82,6 +87,7 @@ class A(x){ //│ } //│ +:lift fun f(x,y,z) = class A{ fun foo = new B @@ -111,6 +117,7 @@ fun f(x,y,z) = //│ } //│ +:lift fun f(x,y,z) = class C{ class A{ @@ -142,33 +149,25 @@ fun f(x,y,z) = //│ } //│ +:lift fun f(y) = let g(x) = x + y + 1 class Foo(x) { fun h = g(x) } -//│ |#fun| |f|(|y|)| |#=|→|#let| |g|(|x|)| |#=| |x| |+| |y| |+| |1|↵|#class| |Foo|(|x|)| |{|→|#fun| |h| |#=| |g|(|x|)|←|↵|}|←| -//│ Parsed: {fun f = (y,) => {let g = (x,) => +(+(x, y,), 1,); class Foo(x,) {fun h = g(x,)}}} + Foo(1).h + Foo(x).h +//│ |#fun| |f|(|y|)| |#=|→|#let| |g|(|x|)| |#=| |x| |+| |y| |+| |1|↵|#class| |Foo|(|x|)| |{|→|#fun| |h| |#=| |g|(|x|)|←|↵|}|↵|Foo|(|1|)|.h| |↵|Foo|(|x|)|.h|←| +//│ Parsed: {fun f = (y,) => {let g = (x,) => +(+(x, y,), 1,); class Foo(x,) {fun h = g(x,)}; (Foo(1,)).h; (Foo(x,)).h}} //│ Lifted: //│ TypingUnit { //│ class Foo$1([x, y,]) {fun h = g$2((this).x, y,)} //│ let g$2 = (x, y,) => +(+(x, y,), 1,) -//│ fun f$1 = (y,) => {} +//│ fun f$1 = (y,) => {(Foo$1(1, y,)).h; (Foo$1(x, y,)).h} //│ } -//│ - Foo(1).h -//│ | |Foo|(|1|)|.h| -//│ Parsed: {(Foo(1,)).h} -//│ Lifted: -//│ TypingUnit {Code(List((Foo(1,)).h))} -//│ - Foo(x).h -//│ | |Foo|(|x|)|.h| -//│ Parsed: {(Foo(x,)).h} -//│ Lifted: -//│ TypingUnit {Code(List((Foo(x,)).h))} //│ +:lift fun f(x) = let g(x) = let h(x) = x + 2 @@ -188,6 +187,7 @@ fun f(x) = //│ } //│ +:lift class Foo(x, y) extends Bar(y, x), Baz(x + y) //│ |#class| |Foo|(|x|,| |y|)| |#extends| |Bar|(|y|,| |x|)|,| |Baz|(|x| |+| |y|)| //│ Parsed: {class Foo(x, y,): Bar(y, x,), Baz(+(x, y,),) {}} @@ -197,6 +197,7 @@ class Foo(x, y) extends Bar(y, x), Baz(x + y) //│ } //│ +:lift fun foo(x: T): string = class A(y) extends B, C(y: U) { fun bar = this @@ -211,6 +212,7 @@ fun foo(x: T): string = //│ } //│ +:lift class A{ class B{ fun f = x => y => x @@ -230,6 +232,7 @@ class A{ //│ } //│ +:lift class Foo{ class RectangleBox: Box & { breadth: T } class StackedRectangleBoxes : RectangleBox & { size: N } @@ -246,6 +249,7 @@ class Foo{ //│ } //│ +:lift class Func { fun apply: T => U } @@ -267,6 +271,7 @@ fun ctx(a,b) = //│ } //│ +:lift fun f(T) = new T() f(MyClass) @@ -276,6 +281,7 @@ f(MyClass) //│ Lifting failed: mlscript.codegen.CodeGenError: Cannot find type T. Class values are not supported in lifter. //│ +:lift class A { fun foo = fun bar = foo() diff --git a/compiler/shared/test/diff/Lifter/NestedClasses.mls b/compiler/shared/test/diff/Lifter/NestedClasses.mls index 73eefcbb..ab0ca6df 100644 --- a/compiler/shared/test/diff/Lifter/NestedClasses.mls +++ b/compiler/shared/test/diff/Lifter/NestedClasses.mls @@ -6,10 +6,6 @@ class X() { class Y() {} } X.Y() -//│ TypingUnit { -//│ class X([]) {class Y([]) {}} -//│ Code(List((X).Y())) -//│ } //│ Lifted: //│ TypingUnit { //│ class X$1_Y$2([par$X$1,]) {} diff --git a/compiler/shared/test/diff/Lifter/NestedFuncs.mls b/compiler/shared/test/diff/Lifter/NestedFuncs.mls index 09d7c9e5..fc2b38b5 100644 --- a/compiler/shared/test/diff/Lifter/NestedFuncs.mls +++ b/compiler/shared/test/diff/Lifter/NestedFuncs.mls @@ -6,10 +6,6 @@ fun test(a) = let f(x) = a + x f test(6)(4) -//│ TypingUnit { -//│ fun test = (a,) => {let f = (x,) => +(a, x,); f} -//│ Code(List(test(6,)(4,))) -//│ } //│ Lifted: //│ TypingUnit { //│ let f$2 = (x, a,) => +(a, x,) diff --git a/compiler/shared/test/diff/Lifter/ParameterizedInheritance.mls b/compiler/shared/test/diff/Lifter/ParameterizedInheritance.mls index 2c68f1f1..da0a7394 100644 --- a/compiler/shared/test/diff/Lifter/ParameterizedInheritance.mls +++ b/compiler/shared/test/diff/Lifter/ParameterizedInheritance.mls @@ -1,63 +1,41 @@ :NewDefs -:mono -:e +// FIXME: correctly lift parameterized inheritance +:lift val c = 5 -class Sup(a: Int){ +class Sup(val a: Int){ virtual fun foo = () -> a } class Sub(b: Int) extends Sup(b+b){ } class Sub2(c: Int) extends Sub(c+c){ - fun foo = () -> a+c + fun foo = () -> c+c } (Sub(10)).foo() (Sub2(c)).foo() //│ Lifted: //│ TypingUnit { -//│ class Sup$1([a: Int,]) {fun foo = () => (this).a} +//│ class Sup$1([val a: Int,]) {fun foo = () => (this).a} //│ class Sub$2([b: Int,]): Sup$1(+((this).b, (this).b,),) {} -//│ class Sub2$3([c: Int,]): Sub$2(+(c$1, c$1,),) {fun foo = () => +((this).a, c$1,)} +//│ class Sub2$3([c: Int,]): Sub$2(+(c$1, c$1,),) {fun foo = () => +(c$1, c$1,)} //│ let c$1 = 5 //│ Code(List(('(' Sub$2(10,) ')').foo())) //│ Code(List(('(' Sub2$3(c$1,) ')').foo())) //│ } -//│ Mono: -//│ TypingUnit { -//│ class Sub2$3([c: Int,]): Sub$2(+(c$1, c$1,),) {} -//│ class Sup$1([a: Int,]) {} -//│ class Sub$2([b: Int,]): Sup$1(+((this).b, (this).b,),) {} -//│ let c$1 = 5 -//│ fun main$$5 = () => let obj = '(' Sub2$3(c$1,) ')' in if obj is ‹(Sub2$3) then foo$Sub2$3(obj,); else error› -//│ fun main$$4 = () => let obj = '(' Sub$2(10,) ')' in if obj is ‹(Sub$2) then foo$Sup$1(obj,); else error› -//│ fun foo$Sup$1 = (this,) => let obj = this in if obj is ‹(Sup$1) then 10; else error› -//│ fun foo$Sub2$3 = (this,) => +(let obj = this in if obj is ‹(Sub2$3) then 5; else error›, c$1,) -//│ Code(List(main$$4())) -//│ Code(List(main$$5())) -//│ } //│ ╔══[ERROR] identifier not found: this //│ ╙── //│ ╔══[ERROR] identifier not found: this //│ ╙── -//│ class Sub2$3(c: Int) extends Sub$2, Sup$1 -//│ class Sup$1(a: Int) -//│ class Sub$2(b: Int) extends Sup$1 -//│ let c$1: 5 -//│ fun main$$5: () -> Int -//│ fun main$$4: () -> 10 -//│ fun foo$Sup$1: Object -> 10 -//│ fun foo$Sub2$3: Object -> Int +//│ class Sup$1(a: Int) { +//│ fun foo: () -> Int +//│ } +//│ class Sub$2(b: Int) extends Sup$1 { +//│ fun foo: () -> Int +//│ } +//│ class Sub2$3(c: Int) extends Sub$2, Sup$1 { +//│ fun foo: () -> Int +//│ } +//│ val c$1: 5 //│ Int //│ Code generation encountered an error: //│ unguarded recursive use of by-value binding c$1 - -// TODO: Handle recursive references in closure -//:mono -//:e -//class Foo(f: Int -> Int){ -// fun foo = () -> f(1) -//} -//class F1() extends Foo(x => x+1){} -//class F2() extends Foo(x => x+2){} -//(new F1()).foo() -//(new F2()).foo() diff --git a/compiler/shared/test/diff/Lifter/TypedClassParams.mls b/compiler/shared/test/diff/Lifter/TypedClassParams.mls index 7b32b6b4..b8b006e1 100644 --- a/compiler/shared/test/diff/Lifter/TypedClassParams.mls +++ b/compiler/shared/test/diff/Lifter/TypedClassParams.mls @@ -3,7 +3,6 @@ :lift class A() {} class B(foo: A) {} -//│ TypingUnit {class A([]) {}; class B([foo: A,]) {}} //│ Lifted: //│ TypingUnit {class A$1([]) {}; class B$2([foo: A$1,]) {}} //│ class A$1() diff --git a/compiler/shared/test/scala/mlscript/compiler/Test.scala b/compiler/shared/test/scala/mlscript/compiler/Test.scala index 2738c2f0..2875eb95 100644 --- a/compiler/shared/test/scala/mlscript/compiler/Test.scala +++ b/compiler/shared/test/scala/mlscript/compiler/Test.scala @@ -5,52 +5,49 @@ import scala.util.control.NonFatal import scala.collection.mutable.StringBuilder import mlscript.{DiffTests, ModeType, TypingUnit} import mlscript.compiler.TreeDebug -import mlscript.compiler.mono.Monomorph -import mlscript.compiler.mono.MonomorphError +import mlscript.Polyfill +import simpledef.SimpleDef class DiffTestCompiler extends DiffTests { import DiffTestCompiler.* override def postProcess(mode: ModeType, basePath: List[Str], testName: Str, unit: TypingUnit, output: Str => Unit): (List[Str], Option[TypingUnit]) = val outputBuilder = StringBuilder() - if (mode.lift) output(PrettyPrinter.showTypingUnit(unit)) - output("Lifted:") var rstUnit = unit; try val lifter = ClassLifter(mode.fullExceptionStack) - if (!mode.nolift) rstUnit = lifter.liftTypingUnit(unit) + if (mode.lift) { + output("Lifted:") + rstUnit = lifter.liftTypingUnit(unit) + output(PrettyPrinter.showTypingUnit(rstUnit)) + } if (mode.showParse) output(rstUnit.toString()) - output(PrettyPrinter.showTypingUnit(rstUnit)) if (mode.dbgLifting) output(lifter.getLog) catch case NonFatal(err) => output("Lifting failed: " ++ err.toString()) if mode.fullExceptionStack then - output("\n" ++ err.getStackTrace().map(_.toString()).mkString("\n")) - if(mode.mono){ - output("Mono:") - val treeDebug = new TreeDebug(if mode.dbgDefunc then output else (str) => ()) - try{ - val monomorph = new Monomorph(treeDebug) - val defuncAST = monomorph.defunctionalize(rstUnit) - if (mode.showParse) output(defuncAST.toString()) - output(PrettyPrinter.showTypingUnit(defuncAST)) - return (outputBuilder.toString().linesIterator.toList, Some(defuncAST)) - }catch{ - case error: MonomorphError => - if (mode.expectCodeGenErrors) - output(error.getMessage() ++ "\n" ++ (error.getStackTrace().take(10).map(_.toString()).toList).mkString("\n")) - return (Nil, None) - // case error: StackOverflowError => outputBuilder ++= (error.getMessage() :: error.getStackTrace().take(40).map(_.toString()).toList).mkString("\n") - } - } + outputBuilder ++= "\n" ++ err.getStackTrace().map(_.toString()).mkString("\n") if (mode.lift) { (outputBuilder.toString().linesIterator.toList, Some(rstUnit)) } else { (outputBuilder.toString().linesIterator.toList, None) } + override def postTypingProcess(mode: ModeType, basePath: List[Str], testName: Str, unit: TypingUnit, output: Str => Unit): Option[TypingUnit] = + if(mode.simpledef || basePath.contains("Defunctionalize")) { + output("\nSimpledef:") + val treeDebug = new TreeDebug(if mode.dbgSimpledef then output else (str) => ()) + val pd = SimpleDef(treeDebug) + pd(unit) + val defuncAST = pd.rewriteProgram(unit) + output(defuncAST.showDbg.replace(";", "\n")) + output("End simpledef\n") + return Some(defuncAST) + } + None + override protected lazy val files = allFiles.filter { file => val fileName = file.baseName validExt(file.ext) && filter(file.relativeTo(pwd)) diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index 709b7eea..4e765b58 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -28,6 +28,7 @@ abstract class ModeType { def dbgUCS: Opt[Set[Str]] def dbgLifting: Bool def dbgDefunc: Bool + def dbgSimpledef: Bool def fullExceptionStack: Bool def stats: Bool def stdout: Bool @@ -40,10 +41,10 @@ abstract class ModeType { def expectCodeGenErrors: Bool def showRepl: Bool def allowEscape: Bool - def mono: Bool def useIR: Bool def interpIR: Bool def irVerbose: Bool + def simpledef: Bool def lift: Bool def nolift: Bool } @@ -57,6 +58,7 @@ class DiffTests /** Hook for dependent projects, like the monomorphizer. */ def postProcess(mode: ModeType, basePath: Ls[Str], testName: Str, unit: TypingUnit, output: Str => Unit): (Ls[Str], Option[TypingUnit]) = (Nil, None) + def postTypingProcess(mode: ModeType, basePath: Ls[Str], testName: Str, unit: TypingUnit, output: Str => Unit): Option[TypingUnit] = None @SuppressWarnings(Array("org.wartremover.warts.RedundantIsInstanceOf")) @@ -161,6 +163,7 @@ class DiffTests dbgUCS: Opt[Set[Str]] = N, dbgLifting: Bool = false, dbgDefunc: Bool = false, + dbgSimpledef: Bool = false, fullExceptionStack: Bool = false, stats: Bool = false, stdout: Bool = false, @@ -174,7 +177,7 @@ class DiffTests expectCodeGenErrors: Bool = false, showRepl: Bool = false, allowEscape: Bool = false, - mono: Bool = false, + simpledef: Bool = false, lift: Bool = false, nolift: Bool = false, // noProvs: Bool = false, @@ -235,6 +238,7 @@ class DiffTests case "ds" => mode.copy(dbgSimplif = true) case "dl" => mode.copy(dbgLifting = true) case "dd" => mode.copy(dbgDefunc = true) + case "dsd" => mode.copy(dbgSimpledef = true) case "s" => mode.copy(fullExceptionStack = true) case "v" | "verbose" => mode.copy(verbose = true) case "ex" | "explain" => mode.copy(expectTypeErrors = true, explainErrors = true) @@ -289,7 +293,7 @@ class DiffTests case "re" => mode.copy(expectRuntimeErrors = true) case "r" | "showRepl" => mode.copy(showRepl = true) case "escape" => mode.copy(allowEscape = true) - case "mono" => {mode.copy(mono = true)} + case "sd" => {mode.copy(simpledef = true)} case "lift" => {mode.copy(lift = true)} case "nolift" => {mode.copy(nolift = true)} case "exit" => @@ -463,14 +467,14 @@ class DiffTests if (mode.showParse) output(s"AST: $res") - val newMode = if (useIR) { mode.copy(useIR = true) } else mode - val (postLines, nuRes) = postProcess(newMode, basePath, testName, res, output) - postLines.foreach(output) + val (postLines, nuRes) = + postProcess(newMode, basePath, testName, res, output) + postLines.foreach(output) if (parseOnly) Success(Pgrm(Nil), 0) - else if (mode.mono || mode.lift) { + else if (mode.lift) { import Message._ Success(Pgrm(nuRes.getOrElse({ raise(ErrorReport(msg"Post-process failed to produce AST." -> None :: Nil, true, Diagnostic.Compilation)) @@ -902,6 +906,11 @@ class DiffTests val executionResults: Result \/ Ls[(ReplHost.Reply, Str)] = if (!allowTypeErrors && file.ext =:= "mls" && !mode.noGeneration && !noJavaScript) { import codeGenTestHelpers._ + val pp = + postTypingProcess(mode, basePath, testName, TypingUnit(p.tops), output) match { + case Some(stmts) => Pgrm(stmts.entities) + case _ => p + } backend(p, mode.allowEscape, newDefs && newParser, prettyPrintQQ) match { case testCode @ TestCode(prelude, queries) => { // Display the generated code. From 13ba521ef821d5b2c0b1713a6e9280b13b3a3894 Mon Sep 17 00:00:00 2001 From: Mark Ng <55091936+CAG2Mark@users.noreply.github.com> Date: Thu, 20 Jun 2024 11:56:24 +0800 Subject: [PATCH 135/143] Tail Recursion Optimization (#218) --- .../scala/mlscript/compiler/ir/Builder.scala | 106 +- .../compiler/ir/DefnRefResolver.scala | 2 +- .../main/scala/mlscript/compiler/ir/IR.scala | 40 +- .../scala/mlscript/compiler/ir/Interp.scala | 29 +- .../mlscript/compiler/ir/Validator.scala | 2 +- .../compiler/optimizer/Analysis.scala | 11 +- .../compiler/optimizer/TailRecOpt.scala | 1085 +++++++ compiler/shared/test/diff-ir/IR.mls | 333 +- compiler/shared/test/diff-ir/IRComplex.mls | 255 +- compiler/shared/test/diff-ir/IRRec.mls | 989 +----- compiler/shared/test/diff-ir/IRTailRec.mls | 2669 +++++++++++++++++ compiler/shared/test/diff-ir/NuScratch.mls | 3 + .../test/scala/mlscript/compiler/Test.scala | 9 +- .../test/scala/mlscript/compiler/TestIR.scala | 43 +- .../src/main/scala/mlscript/NewParser.scala | 5 +- shared/src/main/scala/mlscript/Typer.scala | 6 +- shared/src/test/diff/nu/Annotations.mls | 34 + .../src/test/scala/mlscript/DiffTests.scala | 24 +- 18 files changed, 4074 insertions(+), 1571 deletions(-) create mode 100644 compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala create mode 100644 compiler/shared/test/diff-ir/IRTailRec.mls create mode 100644 compiler/shared/test/diff-ir/NuScratch.mls diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala index 59227ca6..c80a698f 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala @@ -4,11 +4,12 @@ import mlscript.compiler.optimizer.FreeVarAnalysis import mlscript.utils.shorthands._ import mlscript.utils._ import mlscript._ +import mlscript.Message._ import collection.mutable.ListBuffer final val ops = Set("+", "-", "*", "/", ">", "<", ">=", "<=", "!=", "==") -final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): +final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diagnostic => Unit): import Node._ import Expr._ @@ -72,6 +73,33 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres tm private def buildResultFromTerm(using ctx: Ctx)(tm: Term)(k: Node => Node): Node = + def buildLetCall(f: Term, xs: Tup, ann: Option[Term]) = + buildResultFromTerm(f) { node => node match + case Result(Ref(g) :: Nil) if ctx.fnCtx.contains(g.str) => buildResultFromTerm(xs) { + case Result(args) => + val v = fresh.make + + ann match + case Some(ann @ Var(nme)) => + if nme === "tailcall" then + LetCall(List(v), DefnRef(Right(g.str)), args, true, v |> ref |> sresult |> k)(f.toLoc).attachTag(tag) + else + if nme === "tailrec" then + raise(ErrorReport(List(msg"@tailrec is for annotating functions; try @tailcall instead" -> ann.toLoc), true, Diagnostic.Compilation)) + LetCall(List(v), DefnRef(Right(g.str)), args, false, v |> ref |> sresult |> k)(f.toLoc).attachTag(tag) + case Some(_) => node |> unexpectedNode + case None => LetCall(List(v), DefnRef(Right(g.str)), args, false, v |> ref |> sresult |> k)(f.toLoc).attachTag(tag) + + case node @ _ => node |> unexpectedNode + } + case Result(Ref(f) :: Nil) => buildResultFromTerm(xs) { + case Result(args) => + throw IRError(s"not supported: apply") + case node @ _ => node |> unexpectedNode + } + case node @ _ => node |> unexpectedNode + } + val res = tm match case lit: Lit => Literal(lit) |> sresult |> k case v @ Var(name) => @@ -114,21 +142,16 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres case node @ _ => node |> unexpectedNode } - case App(f, xs @ Tup(_)) => - buildResultFromTerm(f) { - case Result(Ref(f) :: Nil) if ctx.fnCtx.contains(f.str) => buildResultFromTerm(xs) { - case Result(args) => - val v = fresh.make - LetCall(List(v), DefnRef(Right(f.str)), args, v |> ref |> sresult |> k).attachTag(tag) - case node @ _ => node |> unexpectedNode - } - case Result(Ref(f) :: Nil) => buildResultFromTerm(xs) { - case Result(args) => - throw IRError(s"not supported: apply") - case node @ _ => node |> unexpectedNode - } - case node @ _ => node |> unexpectedNode - } + case App(f, xs @ Tup(_)) => buildLetCall(f, xs, None) + case Ann(ann, App(f, xs @ Tup(_))) => buildLetCall(f, xs, Some(ann)) + + case Ann(ann @ Var(name), recv) => + if name === "tailcall" then + raise(ErrorReport(List(msg"@tailcall may only be used to annotate function calls" -> ann.toLoc), true, Diagnostic.Compilation)) + else if name === "tailrec" then + raise(ErrorReport(List(msg"@tailrec may only be used to annotate functions" -> ann.toLoc), true, Diagnostic.Compilation)) + + buildResultFromTerm(recv)(k) case Let(false, Var(name), rhs, body) => buildBinding(name, rhs, body)(k) @@ -147,7 +170,9 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres jp.str, params = res :: fvs.map(x => Name(x)), resultNum = 1, - jpbody + jpbody, + false, + None ) ctx.jpAcc.addOne(jpdef) val tru2 = buildResultFromTerm(tru) { @@ -180,6 +205,8 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres params = res :: fvs.map(x => Name(x)), resultNum = 1, jpbody, + false, + None ) ctx.jpAcc.addOne(jpdef) val cases: Ls[(ClassInfo, Node)] = lines map { @@ -234,20 +261,31 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres res private def buildDefFromNuFunDef(using ctx: Ctx)(nfd: Statement): Defn = nfd match - case NuFunDef(_, Var(name), None, Nil, L(Lam(Tup(fields), body))) => - val strs = fields map { - case N -> Fld(FldFlags.empty, Var(x)) => x - case _ => throw IRError("unsupported field") - } - val names = strs map (fresh.make(_)) - given Ctx = ctx.copy(nameCtx = ctx.nameCtx ++ (strs zip names)) - Defn( - fnUid.make, - name, - params = names, - resultNum = 1, - buildResultFromTerm(body) { x => x } - ) + case nfd: NuFunDef => nfd match + case NuFunDef(_, Var(name), None, Nil, L(Lam(Tup(fields), body))) => + val strs = fields map { + case N -> Fld(FldFlags.empty, Var(x)) => x + case _ => throw IRError("unsupported field") + } + val names = strs map (fresh.make(_)) + given Ctx = ctx.copy(nameCtx = ctx.nameCtx ++ (strs zip names)) + val trAnn = nfd.annotations.find { + case Var("tailrec") => true + case ann @ Var("tailcall") => + raise(ErrorReport(List(msg"@tailcall is for annotating function calls; try @tailrec instead" -> ann.toLoc), true, Diagnostic.Compilation)) + false + case _ => false } + + Defn( + fnUid.make, + name, + params = names, + resultNum = 1, + buildResultFromTerm(body) { x => x }, + trAnn.isDefined, + trAnn.flatMap(_.toLoc) + ) + case _ => throw IRError("unsupported NuFunDef") case _ => throw IRError("unsupported NuFunDef") private def buildClassInfo(ntd: Statement): ClassInfo = ntd match @@ -288,7 +326,11 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres import scala.collection.mutable.{ HashSet => MutHSet } - val cls = grouped.getOrElse(0, Nil).map(buildClassInfo) + // TODO: properly add prelude classes such as "True" and "False" rather than this hacky method + val cls = ClassInfo(classUid.make, "True", List()) + :: ClassInfo(classUid.make, "False", List()) + :: grouped.getOrElse(0, Nil).map(buildClassInfo) + cls.foldLeft(Set.empty)(checkDuplicateField(_, _)) val clsinfo = cls.toSet diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala b/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala index d49e1fb3..96ee1d63 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala @@ -11,7 +11,7 @@ private final class DefnRefResolver(defs: Set[Defn], allowInlineJp: Bool): case Result(res) => case Case(scrut, cases) => cases map { (_, body) => f(body) } case LetExpr(name, expr, body) => f(body) - case LetCall(resultNames, defnref, args, body) => + case LetCall(resultNames, defnref, args, _, body) => defs.find{_.getName == defnref.getName} match case Some(defn) => defnref.defn = Left(defn) case None => throw IRError(f"unknown function ${defnref.getName} in ${defs.map{_.getName}.mkString(",")}") diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala index e699379d..a84f48fd 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala @@ -6,6 +6,8 @@ import mlscript.utils.shorthands._ import mlscript.compiler.ir._ import mlscript.compiler.optimizer._ +import mlscript.Loc + import collection.mutable.{Map as MutMap, Set as MutSet, HashMap, ListBuffer} import annotation.unused import util.Sorting @@ -67,7 +69,9 @@ case class Defn( val name: Str, val params: Ls[Name], val resultNum: Int, - val body: Node + val body: Node, + val isTailRec: Bool, + val loc: Opt[Loc] = None ): override def hashCode: Int = id def getName: String = name @@ -96,6 +100,7 @@ enum Expr: case CtorApp(name: ClassInfo, args: Ls[TrivialExpr]) case Select(name: Name, cls: ClassInfo, field: Str) case BasicOp(name: Str, args: Ls[TrivialExpr]) + case AssignField(assignee: Name, clsInfo: ClassInfo, fieldName: Str, value: TrivialExpr) override def toString: String = show @@ -107,13 +112,20 @@ enum Expr: case Literal(IntLit(lit)) => s"$lit" |> raw case Literal(DecLit(lit)) => s"$lit" |> raw case Literal(StrLit(lit)) => s"$lit" |> raw - case Literal(UnitLit(lit)) => s"$lit" |> raw + case Literal(UnitLit(lit)) => (if lit then "undefined" else "null") |> raw case CtorApp(ClassInfo(_, name, _), args) => raw(name) <#> raw("(") <#> raw(args |> show_args) <#> raw(")") case Select(s, _, fld) => raw(s.toString) <#> raw(".") <#> raw(fld) case BasicOp(name: Str, args) => raw(name) <#> raw("(") <#> raw(args |> show_args) <#> raw(")") + case AssignField(assignee, clsInfo, fieldName, value) => + stack( + raw("assign") + <:> raw(assignee.toString + "." + fieldName) + <:> raw(":=") + <:> value.toDocument + ) def mapName(f: Name => Name): Expr = this match case Ref(name) => Ref(f(name)) @@ -121,6 +133,7 @@ enum Expr: case CtorApp(cls, args) => CtorApp(cls, args.map(_.mapNameOfTrivialExpr(f))) case Select(x, cls, field) => Select(f(x), cls, field) case BasicOp(name, args) => BasicOp(name, args.map(_.mapNameOfTrivialExpr(f))) + case AssignField(assignee, clsInfo, fieldName, value) => AssignField(f(assignee), clsInfo, fieldName, value.mapNameOfTrivialExpr(f)) def locMarker: LocMarker = this match case Ref(name) => LocMarker.MRef(name.str) @@ -128,7 +141,7 @@ enum Expr: case CtorApp(name, args) => LocMarker.MCtorApp(name, args.map(_.toExpr.locMarker)) case Select(name, cls, field) => LocMarker.MSelect(name.str, cls, field) case BasicOp(name, args) => LocMarker.MBasicOp(name, args.map(_.toExpr.locMarker)) - + case AssignField(assignee, clsInfo, fieldName, value) => LocMarker.MAssignField(assignee.str, fieldName, value.toExpr.locMarker) enum Node: // Terminal forms: @@ -137,7 +150,7 @@ enum Node: case Case(scrut: Name, cases: Ls[(ClassInfo, Node)]) // Intermediate forms: case LetExpr(name: Name, expr: Expr, body: Node) - case LetCall(names: Ls[Name], defn: DefnRef, args: Ls[TrivialExpr], body: Node) + case LetCall(names: Ls[Name], defn: DefnRef, args: Ls[TrivialExpr], isTailRec: Bool, body: Node)(val loc: Opt[Loc] = None) var tag = DefnTag(-1) @@ -160,7 +173,9 @@ enum Node: case Jump(defn, args) => Jump(defn, args.map(_.mapNameOfTrivialExpr(f))) case Case(scrut, cases) => Case(f(scrut), cases.map { (cls, arm) => (cls, arm.mapName(f)) }) case LetExpr(name, expr, body) => LetExpr(f(name), expr.mapName(f), body.mapName(f)) - case LetCall(names, defn, args, body) => LetCall(names.map(f), defn, args.map(_.mapNameOfTrivialExpr(f)), body.mapName(f)) + case x: LetCall => + val LetCall(names, defn, args, isTailRec, body) = x + LetCall(names.map(f), defn, args.map(_.mapNameOfTrivialExpr(f)), isTailRec, body.mapName(f))(x.loc) def copy(ctx: Map[Str, Name]): Node = this match case Result(res) => Result(res.map(_.mapNameOfTrivialExpr(_.trySubst(ctx)))) @@ -169,9 +184,10 @@ enum Node: case LetExpr(name, expr, body) => val name_copy = name.copy LetExpr(name_copy, expr.mapName(_.trySubst(ctx)), body.copy(ctx + (name_copy.str -> name_copy))) - case LetCall(names, defn, args, body) => + case x: LetCall => + val LetCall(names, defn, args, isTailRec, body) = x val names_copy = names.map(_.copy) - LetCall(names_copy, defn, args.map(_.mapNameOfTrivialExpr(_.trySubst(ctx))), body.copy(ctx ++ names_copy.map(x => x.str -> x))) + LetCall(names_copy, defn, args.map(_.mapNameOfTrivialExpr(_.trySubst(ctx))), isTailRec, body.copy(ctx ++ names_copy.map(x => x.str -> x)))(x.loc) private def toDocument: Document = this match case Result(res) => raw(res |> show_args) <:> raw(s"-- $tag") @@ -203,28 +219,27 @@ enum Node: <:> raw("in") <:> raw(s"-- $tag"), body.toDocument) - case LetCall(xs, defn, args, body) => + case LetCall(xs, defn, args, isTailRec, body) => stack( raw("let*") <:> raw("(") <#> raw(xs.map(_.toString).mkString(",")) <#> raw(")") <:> raw("=") - <:> raw(defn.getName) + <:> raw((if isTailRec then "@tailcall " else "") + defn.getName) <#> raw("(") <#> raw(args.map{ x => x.toString }.mkString(",")) <#> raw(")") <:> raw("in") <:> raw(s"-- $tag"), body.toDocument) - def locMarker: LocMarker = val marker = this match case Result(res) => LocMarker.MResult(res.map(_.toExpr.locMarker)) case Jump(defn, args) => LocMarker.MJump(defn.getName, args.map(_.toExpr.locMarker)) case Case(scrut, cases) => LocMarker.MCase(scrut.str, cases.map(_._1)) case LetExpr(name, expr, _) => LocMarker.MLetExpr(name.str, expr.locMarker) - case LetCall(names, defn, args, _) => LocMarker.MLetCall(names.map(_.str), defn.getName, args.map(_.toExpr.locMarker)) + case LetCall(names, defn, args, _, _) => LocMarker.MLetCall(names.map(_.str), defn.getName, args.map(_.toExpr.locMarker)) marker.tag = this.tag marker @@ -252,6 +267,7 @@ enum LocMarker: case MCase(scrut: Str, cases: Ls[ClassInfo]) case MLetExpr(name: Str, expr: LocMarker) case MLetCall(names: Ls[Str], defn: Str, args: Ls[LocMarker]) + case MAssignField(assignee: Str, field: Str, value: LocMarker) var tag = DefnTag(-1) def toDocument: Document = this match @@ -281,7 +297,7 @@ enum LocMarker: case MLit(IntLit(lit)) => s"$lit" |> raw case MLit(DecLit(lit)) => s"$lit" |> raw case MLit(StrLit(lit)) => s"$lit" |> raw - case MLit(UnitLit(lit)) => s"$lit" |> raw + case MLit(UnitLit(lit)) => (if lit then "undefined" else "null") |> raw case _ => raw("...") def show = s"$tag-" + toDocument.print diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala index fb4bce25..016ab099 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala @@ -39,9 +39,10 @@ class Interpreter(verbose: Bool): private enum Expr: case Ref(name: Name) case Literal(lit: Lit) - case CtorApp(name: ClassInfo, args: Ls[Expr]) + case CtorApp(name: ClassInfo, var args: Ls[Expr]) case Select(name: Name, cls: ClassInfo, field: Str) case BasicOp(name: Str, args: Ls[Expr]) + case AssignField(assignee: Name, clsInfo: ClassInfo, fieldName: Str, value: Expr) def show: Str = document.print @@ -60,6 +61,15 @@ class Interpreter(verbose: Bool): raw(s) <#> raw(".") <#> raw(fld) case BasicOp(name: Str, args) => raw(name) <#> raw("(") <#> raw(args |> show_args) <#> raw(")") + case AssignField(Name(assignee), clsInfo, fieldName, value) => + stack( + raw("assign") + <:> raw(assignee) + <#> raw(".") + <#> raw(fieldName) + <:> raw("=") + <:> value.document, + ) private enum Node: case Result(res: Ls[Expr]) @@ -147,13 +157,14 @@ class Interpreter(verbose: Bool): case IExpr.CtorApp(name, args) => CtorApp(name, args |> convertArgs) case IExpr.Select(name, cls, field) => Select(name, cls, field) case IExpr.BasicOp(name, args) => BasicOp(name, args |> convertArgs) + case IExpr.AssignField(assignee, clsInfo, fieldName, value) => AssignField(assignee, clsInfo, fieldName, value |> convert) private def convert(node: INode): Node = node match case INode.Result(xs) => Result(xs |> convertArgs) case INode.Jump(defnref, args) => Jump(DefnRef(Right(defnref.getName)), args |> convertArgs) case INode.Case(scrut, cases) => Case(scrut, cases.map{(cls, node) => (cls, node |> convert)}) case INode.LetExpr(name, expr, body) => LetExpr(name, expr |> convert, body |> convert) - case INode.LetCall(xs, defnref, args, body) => + case INode.LetCall(xs, defnref, args, _, body) => LetCall(xs, DefnRef(Right(defnref.getName)), args |> convertArgs, body |> convert) private def convert(defn: IDefn): Defn = @@ -210,6 +221,7 @@ class Interpreter(verbose: Bool): private def evalArgs(using ctx: Ctx, clsctx: ClassCtx)(exprs: Ls[Expr]): Either[Ls[Expr], Ls[Expr]] = var changed = false + val xs = exprs.map { arg => eval(arg) match case Left(expr) => changed = true; expr @@ -230,7 +242,7 @@ class Interpreter(verbose: Bool): case CtorApp(name, args) => evalArgs(args) match case Left(xs) => Left(CtorApp(name, xs)) - case _ => Right(expr) + case Right(xs) => Right(CtorApp(name, xs)) // TODO: This makes recursion modulo cons work, but should be investigated further. case Select(name, cls, field) => ctx.get(name.str).map { case CtorApp(cls2, xs) if cls == cls2 => @@ -246,6 +258,17 @@ class Interpreter(verbose: Bool): eval(using ctx, clsctx)(name, xs.head, xs.tail.head) case _ => throw IRInterpreterError("unexpected basic operation") x.toLeft(expr) + case AssignField(assignee, clsInfo, fieldName, expr) => + val value = evalMayNotProgress(expr) + ctx.get(assignee.str) match + case Some(x: CtorApp) => + val CtorApp(cls, args) = x + val idx = cls.fields.indexOf(fieldName) + val newArgs = args.updated(idx, value) + x.args = newArgs + Left(x) + case Some(_) => throw IRInterpreterError("tried to assign a field of a non-ctor") + case None => throw IRInterpreterError("could not find value " + assignee) private def expectDefn(r: DefnRef) = r.defn match case Left(value) => value diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala index 8977a2eb..ce7f09c2 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala @@ -11,7 +11,7 @@ private final class DefnRefInSet(defs: Set[Defn]): case Jump(defn, args) => case Case(scrut, cases) => cases map { (_, body) => f(body) } case LetExpr(name, expr, body) => f(body) - case LetCall(res, defnref, args, body) => + case LetCall(res, defnref, args, _, body) => defnref.getDefn match { case Some(real_defn) => if (!defs.exists(_ eq real_defn)) throw IRError("ref is not in the set") case _ => diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala index e277196e..f0b02d27 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala @@ -39,13 +39,16 @@ class UsefulnessAnalysis(verbose: Bool = false): case CtorApp(name, args) => args.foreach(f) case Select(name, cls, field) => addUse(name) case BasicOp(name, args) => args.foreach(f) + case AssignField(assignee, _, _, value) => + addUse(assignee) + f(value) private def f(x: Node): Unit = x match case Result(res) => res.foreach(f) case Jump(defn, args) => args.foreach(f) case Case(scrut, cases) => addUse(scrut); cases.foreach { case (cls, body) => 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 LetCall(names, defn, args, _, body) => args.foreach(f); names.foreach(addDef); f(body) def run(x: Defn) = x.params.foreach(addDef) @@ -66,6 +69,10 @@ class FreeVarAnalysis(extended_scope: Bool = true, verbose: Bool = false): case CtorApp(name, args) => args.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) case Select(name, cls, field) => if (defined.contains(name.str)) fv else fv + name.str case BasicOp(name, args) => args.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) + case AssignField(assignee, _, _, value) => f(using defined)( + value.toExpr, + if defined.contains(assignee.str) then fv + assignee.str else fv + ) private def f(using defined: Set[Str])(node: Node, fv: Set[Str]): Set[Str] = node match case Result(res) => res.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) case Jump(defnref, args) => @@ -85,7 +92,7 @@ class FreeVarAnalysis(extended_scope: Bool = true, verbose: Bool = false): val fv2 = f(using defined)(expr, fv) val defined2 = defined + name.str f(using defined2)(body, fv2) - case LetCall(resultNames, defnref, args, body) => + case LetCall(resultNames, defnref, args, _, body) => var fv2 = args.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) val defined2 = resultNames.foldLeft(defined)((acc, name) => acc + name.str) if (extended_scope && !visited.contains(defnref.getName)) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala new file mode 100644 index 00000000..1405e457 --- /dev/null +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -0,0 +1,1085 @@ +package mlscript +package compiler.optimizer + +import scala.annotation.tailrec + +import utils.shorthands._ +import Message.MessageContext + +import compiler.ir._ +import compiler.ir.Node._ + +/* + +DOCUMENTATION OF SEMANTICS OF @tailcall and @tailrec + +@tailcall: Used to annotate specific function calls. Calls annotated with @tailcall +must be tail calls or tail modulo-cons calls. These calls must be optimized to not +consume additional stack space. If such an optimization is not possible, then the +compiler will throw an error. + +If there are multiple possible candidates for tail modulo-cons calls in a single +branch of an expression, then @tailcall can be uesd to indicate which one will be +optimized. For instance in + +fun foo() = + A(foo(), bar()) + +we can use @tailcall to annotate the call foo() or bar(). If a call other than the +last call is annotated with @tailcall, then the remaining functions must be pure +to ensure that reordering computations does not change the result. + +If bar() is impure but you still want to optimize the call foo(), then you can do + +fun foo() = + let b = bar() + let a = @tailcall foo() + A(a, b) + +because here, you are taking responsibility for the reordering of the computations. + +@tailrec: Used to annotate functions. When this annotation is used on a function, say +@tailrec fun foo(), the compiler will ensure no sequence of direct recursive calls back +to foo() consume stack space, i.e. they are all tail calls. Note that a call to foo() +may consume an arbitrary amount of stack space as long as foo() is only consuming finite +stack space. For example, + +@tailrec fun foo() = bar() +fun bar() = + bar() + bar() + +is valid. However, + +@tailrec fun foo() = bar() +fun bar() = + foo() + bar() + +is invalid. If we swap the position of foo() and bar() in the body of bar, it is still invalid. + +Equivalently, if fun foo() is annotated with @tailrec, let S be the largest strongly +connected component in the call-graph of the program that contains foo. Then an error +will be thrown unless all edges (calls) connecting the nodes of the strongly +connected component are tail calls or tail modulo-cons calls. + +*/ + +// fnUid should be the same FreshInt that was used to build the graph being passed into this class +class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diagnostic => Unit): + case class LetCtorNodeInfo(node: LetExpr, ctor: Expr.CtorApp, cls: ClassInfo, ctorValName: Name, fieldName: String, idx: Int) + + enum CallInfo: + case NormalCallInfo(src: Defn, defn: Defn)(val loc: Option[Loc]) extends CallInfo + case TailCallInfo(src: Defn, defn: Defn) extends CallInfo + case ModConsCallInfo(src: Defn, startNode: Node, defn: Defn, letCallNode: LetCall, letCtorNode: LetCtorNodeInfo, retName: Name, retNode: Node) extends CallInfo + + override def toString(): String = this match + case NormalCallInfo(src, defn) => + f"Call { ${src.name}$$${src.id} -> ${defn.name}$$${defn.id} }" + case TailCallInfo(src, defn) => + f"TailCall { ${src.name}$$${src.id} -> ${defn.name}$$${defn.id} }" + case ModConsCallInfo(src, startNode, defn, letCallNode, letCtorNode, _, _) => + f"ModConsCall { ${src.name}$$${src.id} -> ${defn.name}$$${defn.id}, class: ${letCtorNode.cls.ident}, field: ${letCtorNode.fieldName} }" + + def getSrc = this match + case NormalCallInfo(src, _) => src + case TailCallInfo(src, _) => src + case ModConsCallInfo(src, _, _, _, _, _, _) => src + + def getDefn = this match + case NormalCallInfo(_, defn) => defn + case TailCallInfo(_, defn) => defn + case ModConsCallInfo(_, _, defn, _, _, _, _) => defn + + private class DefnGraph(val nodes: Set[DefnNode], val edges: Set[CallInfo], val joinPoints: Set[Defn]): + def removeMetadata: ScComponent = ScComponent(nodes.map(_.defn), edges, joinPoints) + + private class ScComponent(val nodes: Set[Defn], val edges: Set[CallInfo], val joinPoints: Set[Defn]) + + import CallInfo._ + + def filterOptCalls(calls: Iterable[CallInfo]) = + calls.collect { case c: TailCallInfo => c; case c: ModConsCallInfo => c } + + def filterNormalCalls(calls: Iterable[CallInfo]) = + calls.collect { case c: NormalCallInfo => c } + + // Hack to make scala think discoverJoinPoints is tail recursive and be + // partially optimized :P + def casesToJps(cases: List[(ClassInfo, Node)], acc: Set[Defn]): Set[Defn] = + cases.foldLeft(acc)((jps, branch) => discoverJoinPoints(branch._2, jps)) + + def discoverJoinPointsCont(defn: Defn, acc: Set[Defn]) = + discoverJoinPoints(defn.body, acc) + defn + + // TODO: implement proper purity checking. This is a very simple purity check that only allows the last + // parameter of a mod cons call to be optimised. + private val pureCache: scala.collection.mutable.Map[Int, Bool] = scala.collection.mutable.Map[Int, Bool]() + private def isPure(node: Node): Bool = + pureCache.get(node.tag.inner) match + case None => + val ret = node match + case Jump(defn, args) => isIdentityJp(defn.expectDefn) + case _: LetCall => false + case Case(scrut, cases) => cases.foldLeft(true)((value, branch) => value && isPure(branch._2)) + case LetExpr(name, expr: Expr.AssignField, body) => false + case x: LetExpr => true + case Result(res) => true + pureCache.put(node.tag.inner, ret) + ret + + case Some(value) => value + + + + + // do a DFS to discover join points + @tailrec + private def discoverJoinPoints(node: Node, acc: Set[Defn]): Set[Defn] = + node match + case Result(res) => Set() + case Jump(defn_, args) => + val defn = defn_.expectDefn + if isIdentityJp(defn) then acc + else if acc.contains(defn) then acc + else discoverJoinPointsCont(defn, acc + defn) + case Case(scrut, cases) => casesToJps(cases, acc) + case LetExpr(name, expr, body) => discoverJoinPoints(body, acc) + case LetCall(names, defn, args, isTailRec, body) => discoverJoinPoints(body, acc) + + private def getRetName(names: Set[Name], retVals: List[TrivialExpr]): Option[Name] = + val names = retVals.collect { case Expr.Ref(nme) => nme } + if names.length != 1 then None + else + val nme = names.head + if names.contains(nme) then Some(nme) + else None + + // would prefer to have this inside discoverOptCalls, but scala does not support partially tail recursive functions directly + def shadowAndCont(next: Node, nme: Name)(implicit + acc: Set[CallInfo], + src: Defn, + scc: Set[Defn], + start: Node, + calledDefn: Option[Defn], + letCallNode: Option[LetCall], + letCtorNode: Option[LetCtorNodeInfo], + containingCtors: Set[Name] + ) = searchOptCalls(next)(acc, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors - nme) + + // same here... + def invalidateAndCont(body: Node)(implicit + acc: Set[CallInfo], + src: Defn, + scc: Set[Defn], + start: Node, + calledDefn: Option[Defn], + letCallNode: Option[LetCall], + letCtorNode: Option[LetCtorNodeInfo], + containingCtors: Set[Name] + ) = + letCallNode match + case None => searchOptCalls(body)(acc, src, scc, start, None, None, None, Set()) // invalidate everything that's been discovered + case Some(x: LetCall) => + val LetCall(_, defn, _, isTailRec, _) = x + if isTailRec then + raise(ErrorReport(List(msg"not a tail call" -> x.loc), true, Diagnostic.Compilation)) + + val newAcc = acc + NormalCallInfo(src, defn.expectDefn)(x.loc) + searchOptCalls(body)(newAcc, src, scc, start, None, None, None, Set()) // invalidate everything that's been discovered + + @tailrec + private def searchOptCalls(node: Node)(implicit + acc: Set[CallInfo], + src: Defn, + scc: Set[Defn], + start: Node, + calledDefn: Option[Defn], // The definition that was called in a tailrec mod cons call + letCallNode: Option[LetCall], // The place where that definition was called + letCtorNode: Option[LetCtorNodeInfo], // The place where the result from that call was put into a constructor + containingCtors: Set[Name], // Value names of ctors containing the constructor containing the result from the call + ): Either[Set[CallInfo], List[Node]] = + + def updateMapSimple(c: CallInfo) = acc + c + + def returnNoneCont = calledDefn match + case None => Left(acc) + case Some(dest) => + Left(updateMapSimple(NormalCallInfo(src, dest)(letCallNode.flatMap(_.loc)))) // treat the discovered call as a normal call + + def returnNone = letCallNode match + case Some(x: LetCall) => + val LetCall(_, _, _, isTailRec, _) = x + if isTailRec then + raise(ErrorReport(List(msg"not a tail call" -> x.loc), true, Diagnostic.Compilation)) + returnNoneCont + case _ => returnNoneCont + + node match // Left if mod cons call found, Right if none was found -- we return the next nodes to be scanned + case Result(res) => + (calledDefn, letCallNode, letCtorNode) match + case (Some(defn), Some(letCallNode), Some(letCtorName)) => + getRetName(containingCtors, res) match + case None => returnNone + case Some(value) => Left(updateMapSimple(ModConsCallInfo(src, start, defn, letCallNode, letCtorName, value, node))) + case _ => returnNone + case Jump(jp, args) => + // different cases + (calledDefn, letCallNode, letCtorNode) match + case (Some(defn), Some(letCallNode), Some(letCtorName)) => + getRetName(containingCtors, args) match + case Some(value) if isIdentityJp(jp.expectDefn) => + Left(updateMapSimple(ModConsCallInfo(src, start, defn, letCallNode, letCtorName, value, node))) + case _ => returnNone + case _ => returnNone + + case Case(scrut, cases) => Right(cases.map(_._2)) + case x @ LetExpr(name, expr, body) => + expr match + // Check if this let binding references the mod cons call. + case Expr.Ref(name) => + letCallNode match + case None => + shadowAndCont(body, name) // OK + case Some(LetCall(names, _, _, isTailRec, _)) => + // for it to be mod cons, other values cannot use the return value from the call. + if names.contains(name) then + // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, + // invalidate the discovered call and continue + invalidateAndCont(body) + else + shadowAndCont(body, name) // OK + + case Expr.Literal(lit) => shadowAndCont(body, name) // OK + case y @ Expr.CtorApp(clsInfo, ctorArgs) => + // if expr is a constructor with a call to some function as a parameter + letCallNode match + case None => shadowAndCont(body, name) // OK + case Some(LetCall(letCallNames, _, _, isTailRec, _)) => // there was a previous call + // 1. Check if the ctor application contains this call + val argNames = ctorArgs.collect { case Expr.Ref(name) => name }.toSet + val namesSet = letCallNames.toSet + val inters = argNames.intersect(namesSet) + + if inters.isEmpty then + // OK, this constructor does not use the mod cons call + // Now check if the constructor uses any previous ctor containing the call. + // If it does, then add this name to the list of constructors containing the call + val inters = containingCtors.intersect(argNames) + + if inters.isEmpty then + shadowAndCont(body, name) // does not use, OK to ignore this one + else + // add this name to the list of constructors containing the call + searchOptCalls(body)(acc, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors + name) + else + // it does use it, further analyse + letCtorNode match + case None => + // First constructor discovered using this call as a parameter. + // This is OK. Add this discovered information + + // TODO: for now, assume functions return only one value. handling multiple + // values is a bit more complicated + val ctorArgName = inters.head + val ctorArgIndex = ctorArgs.indexWhere { + case Expr.Ref(nme) => nme == ctorArgName + case _ => false + } + + val fieldName = clsInfo.fields(ctorArgIndex) + + // populate required values + searchOptCalls(body)(acc, src, scc, start, calledDefn, letCallNode, Some(LetCtorNodeInfo(x, y, clsInfo, name, fieldName, ctorArgIndex)), Set(name)) + case Some(_) => + // another constructor is already using the call. Not OK + + // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, + // invalidate the discovered call and continue + invalidateAndCont(body) + + case Expr.Select(name, cls, field) => + letCallNode match + case None => shadowAndCont(body, name) // OK + case Some(LetCall(names, _, _, isTailRec, _)) => + // for it to be mod cons, other values cannot use the return value from the call. + if names.contains(name) then + // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, + // invalidate the discovered call and continue + invalidateAndCont(body) + else + shadowAndCont(body, name) // OK + case Expr.BasicOp(_, args) => + letCallNode match + case None => shadowAndCont(body, name) // OK + case Some(LetCall(names, _, _, isTailRec, _)) => + // for it to be mod cons, other values cannot use the return value from the call. + val argNames = args.collect { case Expr.Ref(name) => name }.toSet + val namesSet = names.toSet + val inters = argNames.intersect(namesSet) + + if inters.isEmpty then + shadowAndCont(body, name) // OK + else + // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, + // invalidate the discovered call and continue + invalidateAndCont(body) + case Expr.AssignField(assignee, clsInfo, assignmentFieldName, value) => + // make sure `value` is not the mod cons call + letCallNode match + case None => searchOptCalls(body) // OK + case Some(LetCall(names, defn, args, isTailRec, _)) => + value match + case Expr.Ref(name) => + invalidateAndCont(body) + case _ => + letCtorNode match + case None => searchOptCalls(body) // OK + case Some(LetCtorNodeInfo(_, ctor, _, name, fieldName, _)) => + // If this assignment overwrites the mod cons value, forget it + if containingCtors.contains(assignee) then invalidateAndCont(body) + else searchOptCalls(body) + case x @ LetCall(names, defn, args, isTailRec, body) => + val callInScc = scc.contains(defn.expectDefn) + + // Only deal with calls in the scc + if callInScc && isTailCall(x) then + // If there is an old call marked as @tailcall, it cannot be a tail call, error + + val updatedMap = letCallNode match + case Some(y) => + // If both these calls are marked @tailrec, error + if y.isTailRec && x.isTailRec then + raise(ErrorReport( + List( + msg"multiple calls in the same branch marked with @tailcall" -> None, + msg"first call" -> y.loc, + msg"second call" -> x.loc, + ), + true, + Diagnostic.Compilation + ) + ) + if y.isTailRec then + raise(ErrorReport(List(msg"not a tail call" -> y.loc), true, Diagnostic.Compilation)) + + updateMapSimple(NormalCallInfo(src, y.defn.expectDefn)(y.loc)) + + case None => acc + + Left(updatedMap + TailCallInfo(src, defn.expectDefn)) + else + val restIsPure = isPure(body) + letCallNode match + case None => // OK, we may use this LetCall as the mod cons + // For now, only optimize functions which return one value + if callInScc && defn.expectDefn.resultNum == 1 && restIsPure then + searchOptCalls(body)(acc, src, scc, start, Some(defn.expectDefn), Some(x), None, Set()) + else + if isTailRec then + if !restIsPure then + raise(ErrorReport(List(msg"not a tail call, as the remaining functions may be impure" -> x.loc), true, Diagnostic.Compilation)) + else + raise(ErrorReport(List(msg"not a tail call" -> x.loc), true, Diagnostic.Compilation)) + + // Treat this as a normal call + val newMap = updateMapSimple(NormalCallInfo(src, defn.expectDefn)(x.loc)) + searchOptCalls(body)(newMap, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors) + case Some(y: LetCall) => + val LetCall(namesOld, defnOld, argsOld, isTailRecOld, bodyOld) = y + if isTailRecOld then + // 1. If both the old and newly discovered call are marked with tailrec, error + if isTailRec then + raise(ErrorReport( + List( + msg"multiple calls in the same branch marked with @tailcall" -> None, + msg"first call" -> y.loc, + msg"second call" -> x.loc, + ), + true, + Diagnostic.Compilation + ) + ) + // 2. old call is marked as tailrec so we must continue using it as the mod cons call. + // make sure the newly discovered call does not use the current call as a parameter + val argNames = args.collect { case Expr.Ref(name) => name }.toSet + val namesSet = namesOld.toSet + val inters = argNames.intersect(namesSet) + + if !inters.isEmpty then + raise(ErrorReport(List(msg"not a tail call" -> y.loc), true, Diagnostic.Compilation)) + // Treat new call as a normal call + val newMap = updateMapSimple(NormalCallInfo(src, defn.expectDefn)(x.loc)) + searchOptCalls(body)(newMap, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors) // OK + else + // only include mod cons calls that have one return value + if callInScc && defn.expectDefn.resultNum == 1 && restIsPure then + // old call is not tailrec, so we can override it however we want + // we take a lucky guess and mark this as the mod cons call, but the + // user really should mark which calls should be tailrec + + // Treat the old call as a normal call + val newMap = updateMapSimple(NormalCallInfo(src, defnOld.expectDefn)(y.loc)) + searchOptCalls(body)(newMap, src, scc, start, Some(defn.expectDefn), Some(x), None, Set()) + else + if isTailRec then + if !restIsPure then + raise(ErrorReport(List(msg"not a tail call, as the remaining functions may be impure" -> x.loc), true, Diagnostic.Compilation)) + else + raise(ErrorReport(List(msg"not a tail call" -> x.loc), true, Diagnostic.Compilation)) + // shadow all the variables in this letcall + + // Treat this as a normal call + val newMap = updateMapSimple(NormalCallInfo(src, defn.expectDefn)(x.loc)) + searchOptCalls(body)(acc, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors -- names) + + // checks whether a list of names is equal to a list of trivial expressions referencing those names + private def argsListEqual(names: List[Name], exprs: List[TrivialExpr]) = + if names.length == exprs.length then + val results = exprs.collect { case Expr.Ref(name) => name } + names == results + else + false + + private def isIdentityJp(d: Defn): Bool = d.body match + case Result(res) => argsListEqual(d.params, res) + case Jump(defn, args) => argsListEqual(d.params, args) && isIdentityJp(defn.expectDefn) + case _ => false + + private def isTailCall(node: Node): Boolean = node match + case LetCall(names, defn, args, _, body) => + body match + case Result(res) => argsListEqual(names, res) + case Jump(defn, args) => argsListEqual(names, args) && isIdentityJp(defn.expectDefn) + case _ => false + case _ => false + + private def discoverOptCallsNode(node: Node)(implicit src: Defn, scc: Set[Defn], acc: Set[CallInfo]): Set[CallInfo] = + searchOptCalls(node)(acc, src, scc, node, None, None, None, Set()) match + case Left(acc) => acc + case Right(nodes) => nodes.foldLeft(acc)((acc, node) => discoverOptCallsNode(node)(src, scc, acc)) + + private def discoverOptCalls(defn: Defn, jps: Set[Defn])(implicit scc: Set[Defn], acc: Set[CallInfo]): Set[CallInfo] = + val combined = jps + defn + combined.foldLeft(acc)((acc, defn_) => discoverOptCallsNode(defn_.body)(defn, scc, acc)) + + private def searchCalls(node: Node)(implicit src: Defn, acc: Map[Int, Set[Defn]]): Map[Int, Set[Defn]] = + node match + case Result(res) => acc + case Jump(defn, args) => acc + case Case(scrut, cases) => cases.foldLeft(acc)((acc, item) => searchCalls(item._2)(src, acc)) + case LetExpr(name, expr, body) => searchCalls(body) + case LetCall(names, defn, args, isTailRec, body) => + val newSet = acc.get(src.id) match + case None => Set(defn.expectDefn) + case Some(defns) => defns + defn.expectDefn + searchCalls(body)(src, acc + (src.id -> newSet)) + + + private def discoverCalls(defn: Defn, jps: Set[Defn])(implicit acc: Map[Int, Set[Defn]]): Map[Int, Set[Defn]] = + val combined = jps + defn + combined.foldLeft(acc)((acc, defn_) => searchCalls(defn_.body)(defn, acc)) + + // Partions a tail recursive call graph into strongly connected components + // Refernece: https://en.wikipedia.org/wiki/Strongly_connected_component + + // Implements Tarjan's algorithm. + // Wikipedia: https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm + // Implementation Reference: https://www.baeldung.com/cs/scc-tarjans-algorithm + + private class DefnNode(val defn: Defn): + override def hashCode(): Int = defn.hashCode + + var num: Int = Int.MaxValue + var lowest: Int = Int.MaxValue + var visited: Boolean = false + var processed: Boolean = false + + private def partitionNodes(implicit nodeMap: Map[Int, DefnNode]): List[DefnGraph] = + val defns = nodeMap.values.toSet + val inital = Map[Int, Set[Defn]]() + val joinPoints = defns.map(d => (d.defn.id -> discoverJoinPoints(d.defn.body, Set()))).toMap + val allJoinPoints = joinPoints.values.flatMap(x => x).toSet + val edges = defns.foldLeft(inital)((acc, defn) => discoverCalls(defn.defn, joinPoints(defn.defn.id))(acc)).withDefaultValue(Set()) + + var ctr = 0 + // nodes, edges + var stack: List[DefnNode] = Nil + var sccs: List[DefnGraph] = Nil + + def dfs(src: DefnNode): Unit = + src.num = ctr + src.lowest = ctr + ctr += 1 + src.visited = true + + val tailCalls = edges(src.defn.id) + stack = src :: stack + for u <- tailCalls do + val neighbour = nodeMap(u.id) + if (neighbour.visited) then + if (!neighbour.processed) + src.lowest = neighbour.num.min(src.lowest) + else + dfs(neighbour) + src.lowest = neighbour.lowest.min(src.lowest) + + + src.processed = true + + if (src.num == src.lowest) then + var scc: Set[DefnNode] = Set() + + def pop(): DefnNode = + val ret = stack.head + stack = stack.tail + ret + + + var vertex = pop() + + while (vertex != src) { + scc = scc + vertex + + val next = pop() + vertex = next + } + + scc = scc + vertex + + val sccIds = scc.map { d => d.defn.id } + + val sccJoinPoints = scc.foldLeft(Set[Defn]())((jps, defn) => joinPoints(defn.defn.id)) + + val sccDefns = scc.map(d => d.defn) + + val categorizedEdges = scc + .foldLeft(Set[CallInfo]())( + (calls, defn) => discoverOptCalls(defn.defn, joinPoints(defn.defn.id))(sccDefns, calls) + ) + .filter(c => sccDefns.contains(c.getDefn)) + + sccs = DefnGraph(scc, categorizedEdges, sccJoinPoints) :: sccs + + for v <- defns do + if !allJoinPoints.contains(v.defn) && !v.visited then + dfs(v) + + sccs + + + private case class DefnInfo(defn: Defn, stackFrameIdx: Int) + + def asLit(x: Int) = Expr.Literal(IntLit(x)) + + private def makeSwitch(scrutName: Name, cases: List[(Int, Node)], default: Node)(implicit trueClass: ClassInfo, falseClass: ClassInfo): Node = + // given expressions value, e1, e2, transform it into + // let scrut = tailrecBranch == value + // in case scrut of True -> e1 + // False -> e2 + def makeCaseBranch(value: Int, e1: Node, e2: Node): Node = + val name = Name("scrut") + val cases = Case(name, List((trueClass, e1), (falseClass, e2))).attachTag(tag) + LetExpr( + name, + Expr.BasicOp("==", List(asLit(value), Expr.Ref(scrutName))), + cases + ).attachTag(tag) + + cases.foldLeft(default)((elz, item) => + val cmpValue = item._1 + val nodeIfTrue = item._2 + makeCaseBranch(cmpValue, nodeIfTrue, elz) + ) + + // TAIL RECURSION MOD CONS + // Uses the ideas in section 2.2 of the paper `Tail Recursion Modulo Context` + // by Leijen and Lorenzen: https://dl.acm.org/doi/abs/10.1145/3571233 + // of whom attribute the method to Risch, Friedman, Wise, Minamide. + + final val ID_CONTEXT_NAME = "_IdContext" + final val CONTEXT_NAME = "_Context" + + // `ctx` class for tailrec mod cons. + // The paper uses two values `res: T` and `hole: ptr` to represent the context. + // We represent the context as three values instead of two to avoid needing pointers: + // + // acc: The accumulated value. This is the same as `res` in the paper. If the functions f1, ..., fn + // in the compoennt return type T1, ..., Tn, then acc has type T1 | ... | Tn. + // + // The following together represent `hole` in the paper: + // ptr: Represents the object containing the "hole" to be written to. + // field: Integer representing which class and field the "hole" belongs to. Which class and field this + // represents is different for each strongly connected component. + // + // The idea to use `ptr` and `field` to represent a pointer is by @LPTK. + final val ID_CTX_CLASS = ClassInfo(classUid.make, ID_CONTEXT_NAME, Nil) + final val CTX_CLASS = ClassInfo(classUid.make, CONTEXT_NAME, List("acc", "ptr", "field")) + + // Given a strongly connected component `defns` of mutually + // tail recursive functions, returns a strongly connected component contaning the + // optimized functions and their associated join points, and also + // new function definitions not in this component, such as the + // original functions pointing to an optimized function and the context + // composition and application functions. + private def optimizeModCons(component: ScComponent, classes: Set[ClassInfo]): (ScComponent, Set[Defn]) = + val modConsCalls = component.edges.collect { case x: ModConsCallInfo => x } + val defns = component.nodes + val defnsIdSet = defns.map(_.id).toSet + + // no mod cons, just return the original + if modConsCalls.isEmpty then + (component, Set()) + else + val trueClass = classes.find(c => c.ident == "True").get + val falseClass = classes.find(c => c.ident == "False").get + + // CONTEXT APPLICATION + + val mergedNames = defns.foldLeft("")(_ + "_" + _.name) + + val ctxAppId = fnUid.make + val ctxAppName = mergedNames + "_ctx_app$" + ctxAppId + val ctxCompId = fnUid.make + val ctxCompName = mergedNames + "_ctx_comp$" + ctxCompId + + // map integers to classes and fields which will be assigned to + val classIdMap = classes.map(c => c.id -> c).toMap + val possibleAssigns = modConsCalls.map(call => (call.letCtorNode.cls.id, call.letCtorNode.fieldName)).toSet + val possibleAssignsIdxes = possibleAssigns.toList.zipWithIndex + + val assignToIdx = possibleAssignsIdxes.map((item, idx) => item -> idx).toMap + + // fun app(ctx, x: T): T + val appCtxName = Name("ctx") + val appValName = Name("x") + + val assignmentCases = possibleAssignsIdxes.map((item, idx) => + val clsId = item._1 + val fieldName = item._2 + val cls = classIdMap(clsId) + + // let ptr = ctx.ptr in + // ptr. = x in + // let acc = ctx.acc + // acc + val node = LetExpr( + Name("ptr"), + Expr.Select(appCtxName, CTX_CLASS, "ptr"), + LetExpr( + Name("_"), + Expr.AssignField( + Name("ptr"), + cls, + fieldName, + Expr.Ref(appValName) + ), + LetExpr( + Name("acc"), + Expr.Select(appCtxName, CTX_CLASS, "acc"), // this could be a join point but it's not that bad + Result( + List(Expr.Ref(Name("acc"))) + ).attachTag(tag) + ).attachTag(tag) + ).attachTag(tag) + ).attachTag(tag) + + (idx, node) + ) + + + val ctxBranch = LetExpr( + Name("field"), Expr.Select(appCtxName, CTX_CLASS, "field"), + makeSwitch(Name("field"), assignmentCases.tail, assignmentCases.head._2)(trueClass, falseClass) + ).attachTag(tag) + + val idBranch = Result(List(Expr.Ref(appValName))).attachTag(tag) + + val appNode = Case(appCtxName, + List( + (ID_CTX_CLASS, idBranch), + (CTX_CLASS, ctxBranch) + ) + ).attachTag(tag) + + val appDefn = Defn(ctxAppId, ctxAppName, List(appCtxName, appValName), 1, appNode, false) + + // CONTEXT COMPOSITION + val cmpCtx1Name = Name("ctx1") + val cmpCtx2Name = Name("ctx2") + + // Note that ctx2 may never be an identity context. If we ever want to compose ctx1 and ctx2 + // where ctx2 is the identity, just use ctx1 directly. + + // Ctx(app(ctx1, ctx2), ctx2.ptr, ctx2.field) -> + // let ctx2acc = ctx2.acc in + // let ctx2ptr = ctx2.ptr in + // let ctx2field = ctx2.field in + // let newAcc = app(ctx1, ctx2acc) in + // let ret = Ctx(newAcc, ctx2ptr, ctx2field) in + // ret + val cmpNode = LetExpr( + Name("ctx2acc"), + Expr.Select(cmpCtx2Name, CTX_CLASS, "acc"), + LetExpr( + Name("ctx2ptr"), + Expr.Select(cmpCtx2Name, CTX_CLASS, "ptr"), + LetExpr( + Name("ctx2field"), + Expr.Select(cmpCtx2Name, CTX_CLASS, "field"), + LetCall( + List(Name("newAcc")), + DefnRef(Left(appDefn)), List(Expr.Ref(cmpCtx1Name), Expr.Ref(Name("ctx2acc"))), + false, + LetExpr( + Name("ret"), + Expr.CtorApp(CTX_CLASS, List("newAcc", "ctx2ptr", "ctx2field").map(n => Expr.Ref(Name(n)))), + Result( + List(Expr.Ref(Name("ret"))) + ).attachTag(tag) + ).attachTag(tag), + )().attachTag(tag) + ).attachTag(tag) + ).attachTag(tag) + ).attachTag(tag) + + val cmpDefn = Defn(ctxCompId, ctxCompName, List(cmpCtx1Name, cmpCtx2Name), 1, cmpNode, false) + + // We use tags to identify nodes + // a bit hacky but it's the most elegant way + // First, build a map of all branches that contain a mod cons call + val modConsBranches = modConsCalls.toList.map(call => (call.startNode.tag.inner -> call)).toMap + + val modConsRefs = defns.map(d => d.id -> DefnRef(Right(d.name + "_modcons"))).toMap + val jpRefs = component.joinPoints.map(jp => jp.id -> DefnRef(Right(jp.name + "_modcons"))).toMap + + def makeRet(ret: TrivialExpr): Node = + LetCall( + List(Name("res")), + DefnRef(Left(appDefn)), + List(Expr.Ref(Name("ctx")), ret), + false, + Result(List(Expr.Ref(Name("res")))).attachTag(tag) + )().attachTag(tag) + + // Here, we assume we are inside the modcons version of the function and hence have an extra + // `ctx` parameter at the start. + def transformNode(node: Node): Node = + modConsBranches.get(node.tag.inner) match + case Some(call) => transformModConsBranch(node)(call) + case None => node match + case Result(res) => + makeRet(res.head) + case Jump(defn, args) => + if isIdentityJp(defn.expectDefn) then makeRet(args.head) + else jpRefs.get(defn.expectDefn.id) match + case None => throw IRError("could not find jump point with id" + defn.expectDefn.id) + case Some(value) => Jump(value, Expr.Ref(Name("ctx")) :: args) + + case Case(scrut, cases) => Case(scrut, cases.map { (cls, body) => (cls, transformNode(body)) }).attachTag(tag) + case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)).attachTag(tag) + case LetCall(names, defn, args, isTailRec, body) => + // Handle the case when we see a tail call. + // This case is not handled by the paper. The way to transform this is: + // let res = foo(*args) in res + // --> let res = foo_modcons(ctx, *args) in res + if isTailCall(node) && defnsIdSet.contains(defn.expectDefn.id) then + // Transform it into a tail recursive call where we pass on the current context + LetCall( + List(Name("res")), + modConsRefs(defn.expectDefn.id), Expr.Ref(Name("ctx")) :: args, + isTailRec, + Result(List(Expr.Ref(Name("res")))).attachTag(tag) + )().attachTag(tag) + else + LetCall(names, defn, args, isTailRec, transformNode(body))().attachTag(tag) + + def transformModConsBranch(node: Node)(implicit call: ModConsCallInfo): Node = + def makeCall = + val field = assignToIdx((call.letCtorNode.cls.id, call.letCtorNode.fieldName)) + + // let composed = comp(ctx, Ctx(retVal, ptr, field)) in + // f(composed, *args) + LetExpr( + Name("ctx2"), + Expr.CtorApp(CTX_CLASS, List(Expr.Ref(call.retName), Expr.Ref(call.letCtorNode.ctorValName), asLit(field))), + LetCall( + List(Name("composed")), + DefnRef(Left(cmpDefn)), + List("ctx", "ctx2").map(n => Expr.Ref(Name(n))), + false, + LetCall( + List(Name("res")), + modConsRefs(call.defn.id), + Expr.Ref(Name("composed")) :: call.letCallNode.args, + false, + Result( + List(Expr.Ref(Name("res"))) + ).attachTag(tag) + )().attachTag(tag) + )().attachTag(tag) + ).attachTag(tag) + + node match + case Result(res) if node.tag.inner == call.retNode.tag.inner => + makeCall + case Jump(defn, args) if node.tag.inner == call.retNode.tag.inner => + makeCall + case LetExpr(name, expr, body) => + if node.tag.inner == call.letCtorNode.node.tag.inner then + // rewrite the ctor, but set the field containing the call as to 0 + val idx = call.letCtorNode.idx + val argsList = call.letCtorNode.ctor.args.updated(idx, asLit(0)) + LetExpr(name, Expr.CtorApp(call.letCtorNode.cls, argsList), transformModConsBranch(body)).attachTag(tag) + else + LetExpr(name, expr, transformModConsBranch(body)).attachTag(tag) + case LetCall(names, defn, args, isTailRec, body) => + if node.tag.inner == call.letCallNode.tag.inner then + // discard it + transformModConsBranch(body) + else + LetCall(names, defn, args, isTailRec, transformModConsBranch(body))().attachTag(tag) + case _ => throw IRError("unreachable case when transforming mod cons call") + + def rewriteDefn(d: Defn): Defn = + val transformed = transformNode(d.body) + val id = fnUid.make + Defn(id, d.name + "_modcons$" + id, Name("ctx") :: d.params, d.resultNum, transformed, d.isTailRec) + + // returns (new defn, mod cons defn) + // where new defn has the same signature and ids as the original, but immediately calls the mod cons defn + // and mod cons defn is the rewritten definition + def replaceDefn(d: Defn): (Defn, Defn) = + val modConsDefn = rewriteDefn(d) + val modConsCall = + LetExpr( + Name("idCtx"), + Expr.CtorApp(ID_CTX_CLASS, Nil), + LetCall( + List(Name("res")), + DefnRef(Left(modConsDefn)), + Expr.Ref(Name("idCtx")) :: d.params.map(Expr.Ref(_)), + false, + Result(List(Expr.Ref(Name("res")))).attachTag(tag) + )().attachTag(tag) + ).attachTag(tag) + val newDefn = Defn(d.id, d.name, d.params, d.resultNum, modConsCall, false) + (newDefn, modConsDefn) + + val jpsTransformed = component.joinPoints.map(d => d.id -> rewriteDefn(d)).toMap + val defnsTransformed = component.nodes.map(d => d.id -> replaceDefn(d)).toMap + + // update defn refs + for (id, ref) <- jpRefs do + ref.defn = Left(jpsTransformed(id)) + + for (id, ref) <- modConsRefs do + ref.defn = Left(defnsTransformed(id)._2) // set it to the mod cons defn, not the one with the original signature + + val jps = jpsTransformed.values.toSet + val modConsDefs = defnsTransformed.values.map((a, b) => b).toSet + val normalDefs = defnsTransformed.values.map((a, b) => a).toSet + appDefn + cmpDefn + + // the edges are not used later, but still, rewrite them for correctness + val newEdges = component.edges.map { c => + val src = c.getSrc + val defn = c.getDefn + TailCallInfo(defnsTransformed(src.id)._2, defnsTransformed(defn.id)._2) + } + + (ScComponent(modConsDefs, newEdges, jps), normalDefs) + + // Given a strongly connected component `defns` of mutually + // tail recursive functions, returns a set containing the optimized function and the + // original functions pointing to an optimized function. + // Explicitly returns the merged function in case tailrec needs to be checked. + private def optimizeTailRec(component: ScComponent, classes: Set[ClassInfo]): (Set[Defn], Defn) = + // To build the case block, we need to compare integers and check if the result is "True" + val trueClass = classes.find(c => c.ident == "True").get + val falseClass = classes.find(c => c.ident == "False").get + // undefined for dummy values + val dummyVal = Expr.Literal(UnitLit(true)) + + // join points need to be rewritten. For now, just combine them into the rest of the function. They will be inlined anyways + val defns = component.nodes ++ component.joinPoints + val defnsNoJp = component.nodes + val edges = component.edges + + // dummy case, should not happen + if (defns.size == 0) + throw IRError("strongly connected component was empty") + + // for single tail recursive functions, just move the body into a join point + if (defns.size <= 1) + val defn = defns.head + + // if the function does not even tail call itself, just return + if filterOptCalls(edges).size == 0 then + return (defns, defns.head) + + val jpName = defn.name + "_jp" + val jpDefnRef = DefnRef(Right(jpName)) + + def transformNode(node: Node): Node = node match + case Result(res) => node.attachTag(tag) + case Jump(defn, args) => node.attachTag(tag) + case Case(scrut, cases) => Case(scrut, cases.map((cls, body) => (cls, transformNode(body)))).attachTag(tag) + case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)).attachTag(tag) + case LetCall(names, defn_, args, isTailRec, body) => + if isTailCall(node) && defn_.expectDefn.id == defn.id then + Jump(jpDefnRef, args).attachTag(tag) + else + LetCall(names, defn_, args, isTailRec, transformNode(body))().attachTag(tag) + + val jpDef = Defn(fnUid.make, jpName, defn.params, defn.resultNum, transformNode(defn.body), false) + + val rets = (0 until defn.resultNum).map(n => Name("r" + n.toString)).toList + val callJpNode = LetCall( + rets, + DefnRef(Left(jpDef)), + defn.params.map(Expr.Ref(_)), + false, + Result(rets.map(Expr.Ref(_))).attachTag(tag), + )().attachTag(tag) + + val newDefn = Defn(fnUid.make, defn.name, defn.params, defn.resultNum, callJpNode, true) + (Set(newDefn, jpDef), newDefn) + + else + // Note that we do not use the actual edges in ScCompoennt here. + // We assume the only things we can optimize are tail calls, which + // are cheap to identify, and nothing else. + + // concretely order the functions as soon as possible, since the order of the functions matter + val defnsList = defns.toList + + // assume all defns have the same number of results + // in fact, they should theoretically have the same return type if the program type checked + val resultNum = defnsList.head.resultNum + + val trName = Name("tailrecBranch$"); + + // To be used to replace variable names inside a definition to avoid variable name clashes + val nameMaps: Map[Int, Map[Name, Name]] = defnsList.map(defn => defn.id -> defn.params.map(n => n -> Name(defn.name + "_" + n.str)).toMap).toMap + + val stackFrameIdxes = defnsList.foldLeft(1 :: Nil)((ls, defn) => defn.params.size + ls.head :: ls).drop(1).reverse + + val defnInfoMap: Map[Int, DefnInfo] = (defnsList zip stackFrameIdxes) + .foldLeft(Map.empty)((map, item) => map + (item._1.id -> DefnInfo(item._1, item._2))) + + val stackFrame = trName :: defnsList.flatMap(d => d.params.map(n => nameMaps(d.id)(n))) // take union of stack frames + + val newId = fnUid.make + val newName = defns.foldLeft("")(_ + "_" + _.name) + "_opt$" + newId + val jpId = fnUid.make + val jpName = defns.foldLeft("")(_ + "_" + _.name) + "_opt_jp$" + jpId + + val newDefnRef = DefnRef(Right(newName)) + val jpDefnRef = DefnRef(Right(jpName)) + + def transformStackFrame(args: List[TrivialExpr], info: DefnInfo) = + val start = stackFrame.take(info.stackFrameIdx).drop(1).map { Expr.Ref(_) } // we drop tailrecBranch and replace it with the defn id + val end = stackFrame.drop(info.stackFrameIdx + args.size).map { Expr.Ref(_) } + asLit(info.defn.id) :: start ::: args ::: end + + // Build the node which will be contained inside the jump point. + def transformNode(node: Node): Node = node match + case Jump(defn, args) => + if defnInfoMap.contains(defn.expectDefn.id) then + Jump(jpDefnRef, transformStackFrame(args, defnInfoMap(defn.expectDefn.id))).attachTag(tag) + else + node.attachTag(tag) + case Result(_) => node.attachTag(tag) + case Case(scrut, cases) => Case(scrut, cases.map(n => (n._1, transformNode(n._2)))).attachTag(tag) + case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)).attachTag(tag) + case LetCall(names, defn, args, isTailRec, body) => + if isTailCall(node) && defnInfoMap.contains(defn.expectDefn.id) then + Jump(jpDefnRef, transformStackFrame(args, defnInfoMap(defn.expectDefn.id))).attachTag(tag) + else LetCall(names, defn, args, isTailRec, transformNode(body))().attachTag(tag) + + // Tail calls to another function in the component will be replaced with a call + // to the merged function + // i.e. for mutually tailrec functions f(a, b) and g(c, d), + // f's body will be replaced with a call f_g(a, b, *, *), where * is a dummy value + def transformDefn(defn: Defn): Defn = + val info = defnInfoMap(defn.id) + + val start = + stackFrame.take(info.stackFrameIdx).drop(1).map { _ => dummyVal } // we drop tailrecBranch and replace it with the defn id + val end = stackFrame.drop(info.stackFrameIdx + defn.params.size).map { _ => dummyVal } + val args = asLit(info.defn.id) :: start ::: defn.params.map(Expr.Ref(_)) ::: end + + // We use a let call instead of a jump to avoid newDefn from being turned into a join point, + // which would cause it to be inlined and result in code duplication. + val names = (0 until resultNum).map(i => Name("r" + i.toString())).toList + val namesExpr = names.map(Expr.Ref(_)) + val res = Result(namesExpr).attachTag(tag) + val call = LetCall(names, newDefnRef, args, false, res)().attachTag(tag) + Defn(defn.id, defn.name, defn.params, defn.resultNum, call, false) + + def getOrKey[T](m: Map[T, T])(key: T): T = m.get(key) match + case None => key + case Some(value) => value + + val first = defnsList.head; + val firstMap = nameMaps(first.id) + val firstBodyRenamed = first.body.mapName(getOrKey(firstMap)) + val firstNode = transformNode(firstBodyRenamed) + + val valsAndNodes = defnsList.map(defn => + val nmeMap = nameMaps(defn.id) + val renamed = defn.body.mapName(getOrKey(nmeMap)) + val transformed = transformNode(renamed) + (defn.id, transformed) + ) + + val newNode = makeSwitch(trName, valsAndNodes.tail, valsAndNodes.head._2)(trueClass, falseClass) + + val jpDefn = Defn(jpId, jpName, stackFrame, resultNum, newNode, false) + + val jmp = Jump(jpDefnRef, stackFrame.map(Expr.Ref(_))).attachTag(tag) + val newDefn = Defn(newId, newName, stackFrame, resultNum, jmp, defnsNoJp.find { _.isTailRec }.isDefined ) + + jpDefnRef.defn = Left(jpDefn) + newDefnRef.defn = Left(newDefn) + + (defnsNoJp.map { d => transformDefn(d) } + newDefn + jpDefn, newDefn) + + private def partition(defns: Set[Defn]): List[ScComponent] = + val nodeMap: Map[Int, DefnNode] = defns.foldLeft(Map.empty)((m, d) => m + (d.id -> DefnNode(d))) + partitionNodes(nodeMap).map(_.removeMetadata) + + private def optimizeParition(component: ScComponent, classes: Set[ClassInfo]): Set[Defn] = + val trFn = component.nodes.find { _.isTailRec }.headOption + val normalCall = filterNormalCalls(component.edges).headOption + + (trFn, normalCall) match + case (Some(fn), Some(call)) => + raise(ErrorReport( + List( + msg"function `${fn.name}` is not tail-recursive, but is marked as @tailrec" -> fn.loc, + msg"it could self-recurse through this call, which may not be a tail-call" -> call.loc + ), + true, Diagnostic.Compilation) + ) + case _ => + + val (modConsComp, other) = optimizeModCons(component, classes) + val (trOpt, mergedDefn) = optimizeTailRec(modConsComp, classes) + other ++ trOpt + + def apply(p: Program) = run(p) + + def run_debug(p: Program): (Program, List[Set[String]]) = + val partitions = partition(p.defs) + + val newDefs = partitions.flatMap { optimizeParition(_, p.classes) }.toSet + + // update the definition refs + newDefs.foreach { defn => resolveDefnRef(defn.body, newDefs, true) } + resolveDefnRef(p.main, newDefs, true) + + (Program(p.classes + ID_CTX_CLASS + CTX_CLASS, newDefs, p.main), partitions.map(t => t.nodes.map(f => f.name))) + + def run(p: Program): Program = run_debug(p)._1 \ No newline at end of file diff --git a/compiler/shared/test/diff-ir/IR.mls b/compiler/shared/test/diff-ir/IR.mls index 1900d8e7..b5b44bbc 100644 --- a/compiler/shared/test/diff-ir/IR.mls +++ b/compiler/shared/test/diff-ir/IR.mls @@ -1,6 +1,7 @@ :NewParser :ParseOnly :UseIR +:NoTailRec :interpIR class Pair(x, y) @@ -12,30 +13,8 @@ foo() //│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |mktup2|(|x|,| |y|)| |#=| |mktup|(|x|,| |y|)|↵|#fun| |mktup|(|x|,| |y|)| |#=| |Pair|(|x|,| |y|)|↵|#fun| |foo|(||)| |#=|→|mktup2|(|1|,| |2|)|←|↵|foo|(||)| //│ Parsed: {class Pair(x, y,) {}; fun mktup2 = (x, y,) => mktup(x, y,); fun mktup = (x, y,) => Pair(x, y,); fun foo = () => {mktup2(1, 2,)}; foo()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, Pair, [x,y])}, { -//│ Def(0, mktup2, [x$0,y$0], -//│ 1, -//│ let* (x$1) = mktup(x$0,y$0) in -- #7 -//│ x$1 -- #6 -//│ ) -//│ Def(1, mktup, [x$2,y$1], -//│ 1, -//│ let x$3 = Pair(x$2,y$1) in -- #14 -//│ x$3 -- #13 -//│ ) -//│ Def(2, foo, [], -//│ 1, -//│ let* (x$4) = mktup2(1,2) in -- #22 -//│ x$4 -- #21 -//│ ) -//│ }, -//│ let* (x$5) = foo() in -- #26 -//│ x$5 -- #25) -//│ //│ Promoted: -//│ Program({ClassInfo(0, Pair, [x,y])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, mktup2, [x$0,y$0], //│ 1, //│ let* (x$1) = mktup(x$0,y$0) in -- #7 @@ -69,34 +48,8 @@ bar() //│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |foo|(|pair|)| |#=|→|#if| |pair| |is|→|Pair|(|x|,| |y|)| |#then| |Pair|(|x|,| |y|)|←|←|↵|#fun| |bar|(||)| |#=|→|foo|(|Pair|(|1|,| |2|)|)|←|↵|bar|(||)| //│ Parsed: {class Pair(x, y,) {}; fun foo = (pair,) => {if pair is ‹(Pair(x, y,)) then Pair(x, y,)›}; fun bar = () => {foo(Pair(1, 2,),)}; bar()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, Pair, [x,y])}, { -//│ Def(0, foo, [pair$0], -//│ 1, -//│ case pair$0 of -- #16 -//│ Pair => -//│ let x$1 = pair$0.y in -- #15 -//│ let x$2 = pair$0.x in -- #14 -//│ let x$3 = Pair(x$2,x$1) in -- #13 -//│ jump j$0(x$3) -- #12 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, bar, [], -//│ 1, -//│ let x$4 = Pair(1,2) in -- #28 -//│ let* (x$5) = foo(x$4) in -- #27 -//│ x$5 -- #26 -//│ ) -//│ }, -//│ let* (x$6) = bar() in -- #32 -//│ x$6 -- #31) -//│ //│ Promoted: -//│ Program({ClassInfo(0, Pair, [x,y])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, foo, [pair$0], //│ 1, //│ case pair$0 of -- #16 @@ -142,44 +95,8 @@ foo() //│ |#class| |Pair|(|x|,| |y|)| |{||}|↵|#fun| |silly|(|pair|)| |#=|→|#let| |_| |#=| |0|↵|#let| |n| |#=| |#if| |pair| |is|→|Pair|(|x1|,| |x2|)| |#then|→|#if| |pair| |is|→|Pair| |(|x3|,| |x4|)| |#then| |x3| |+| |1|←|←|←|↵|n| |+| |1|←|↵|#fun| |foo|(||)| |#=|→|#let| |a| |#=| |Pair|(|0|,| |1|)|↵|#let| |b| |#=| |silly|(|a|)|↵|b|←|↵|foo|(||)| //│ Parsed: {class Pair(x, y,) {}; fun silly = (pair,) => {let _ = 0; let n = if pair is ‹(Pair(x1, x2,)) then {if pair is ‹(Pair(x3, x4,)) then +(x3,)(1,)›}›; +(n,)(1,)}; fun foo = () => {let a = Pair(0, 1,); let b = silly(a,); b}; foo()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, Pair, [x,y])}, { -//│ Def(0, silly, [pair$0], -//│ 1, -//│ let x$0 = 0 in -- #29 -//│ case pair$0 of -- #28 -//│ Pair => -//│ let x$3 = pair$0.y in -- #27 -//│ let x$4 = pair$0.x in -- #26 -//│ case pair$0 of -- #25 -//│ Pair => -//│ let x$6 = pair$0.y in -- #24 -//│ let x$7 = pair$0.x in -- #23 -//│ let x$8 = +(x$7,1) in -- #22 -//│ jump j$1(x$8) -- #21 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ let x$2 = +(x$1,1) in -- #6 -//│ x$2 -- #5 -//│ ) -//│ Def(2, j$1, [x$5], -//│ 1, -//│ jump j$0(x$5) -- #13 -//│ ) -//│ Def(3, foo, [], -//│ 1, -//│ let x$9 = Pair(0,1) in -- #43 -//│ let* (x$10) = silly(x$9) in -- #42 -//│ x$10 -- #41 -//│ ) -//│ }, -//│ let* (x$11) = foo() in -- #47 -//│ x$11 -- #46) -//│ //│ Promoted: -//│ Program({ClassInfo(0, Pair, [x,y])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, silly, [pair$0], //│ 1, //│ let x$0 = 0 in -- #29 @@ -233,35 +150,8 @@ foo() //│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |inc_fst|(|pair|)| |#=|→|#let| |c| |#=| |2|↵|#if| |pair| |is|→|Pair|(|x1|,| |x2|)| |#then| |x1| |+| |c|←|←|↵|#fun| |foo|(||)| |#=|→|#let| |a| |#=| |Pair|(|0|,| |1|)|↵|#let| |b| |#=| |inc_fst|(|a|)|↵|b|←|↵|foo|(||)| //│ Parsed: {class Pair(x, y,) {}; fun inc_fst = (pair,) => {let c = 2; if pair is ‹(Pair(x1, x2,)) then +(x1,)(c,)›}; fun foo = () => {let a = Pair(0, 1,); let b = inc_fst(a,); b}; foo()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, Pair, [x,y])}, { -//│ Def(0, inc_fst, [pair$0], -//│ 1, -//│ let x$0 = 2 in -- #15 -//│ case pair$0 of -- #14 -//│ Pair => -//│ let x$2 = pair$0.y in -- #13 -//│ let x$3 = pair$0.x in -- #12 -//│ let x$4 = +(x$3,x$0) in -- #11 -//│ jump j$0(x$4) -- #10 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #2 -//│ ) -//│ Def(2, foo, [], -//│ 1, -//│ let x$5 = Pair(0,1) in -- #29 -//│ let* (x$6) = inc_fst(x$5) in -- #28 -//│ x$6 -- #27 -//│ ) -//│ }, -//│ let* (x$7) = foo() in -- #33 -//│ x$7 -- #32) -//│ //│ Promoted: -//│ Program({ClassInfo(0, Pair, [x,y])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, inc_fst, [pair$0], //│ 1, //│ let x$0 = 2 in -- #15 @@ -302,35 +192,8 @@ foo() //│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |inc_fst|(|pair|)| |#=|→|#let| |_| |#=| |0|↵|#if| |pair| |is|→|Pair|(|x1|,| |x2|)| |#then| |x2| |+| |1|←|←|↵|#fun| |foo|(||)| |#=|→|#let| |b| |#=| |inc_fst|(|Pair|(|0|,| |1|)|)|↵|b|←|↵|foo|(||)| //│ Parsed: {class Pair(x, y,) {}; fun inc_fst = (pair,) => {let _ = 0; if pair is ‹(Pair(x1, x2,)) then +(x2,)(1,)›}; fun foo = () => {let b = inc_fst(Pair(0, 1,),); b}; foo()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, Pair, [x,y])}, { -//│ Def(0, inc_fst, [pair$0], -//│ 1, -//│ let x$0 = 0 in -- #15 -//│ case pair$0 of -- #14 -//│ Pair => -//│ let x$2 = pair$0.y in -- #13 -//│ let x$3 = pair$0.x in -- #12 -//│ let x$4 = +(x$2,1) in -- #11 -//│ jump j$0(x$4) -- #10 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #2 -//│ ) -//│ Def(2, foo, [], -//│ 1, -//│ let x$5 = Pair(0,1) in -- #28 -//│ let* (x$6) = inc_fst(x$5) in -- #27 -//│ x$6 -- #26 -//│ ) -//│ }, -//│ let* (x$7) = foo() in -- #32 -//│ x$7 -- #31) -//│ //│ Promoted: -//│ Program({ClassInfo(0, Pair, [x,y])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, inc_fst, [pair$0], //│ 1, //│ let x$0 = 0 in -- #15 @@ -374,48 +237,8 @@ bar() //│ |#class| |Left|(|x|)|↵|#class| |Right|(|y|)|↵|#fun| |foo|(|a|,| |b|)| |#=|→|#let| |t| |#=| |#if| |a| |is|→|Left|(|x|)| |#then| |Left|(|x| |+| |1|)|↵|Right|(|y|)| |#then| |Right|(|b|)|←|↵|#if| |t| |is|→|Left|(|x|)| |#then| |x|↵|Right|(|y|)| |#then| |y|←|←|↵|#fun| |bar|(||)| |#=|→|foo|(|Right|(|2|)|,| |2|)|←|↵|bar|(||)| //│ Parsed: {class Left(x,) {}; class Right(y,) {}; fun foo = (a, b,) => {let t = if a is ‹(Left(x,)) then Left(+(x,)(1,),); (Right(y,)) then Right(b,)›; if t is ‹(Left(x,)) then x; (Right(y,)) then y›}; fun bar = () => {foo(Right(2,), 2,)}; bar()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, Left, [x]),ClassInfo(1, Right, [y])}, { -//│ Def(0, foo, [a$0,b$0], -//│ 1, -//│ case a$0 of -- #36 -//│ Left => -//│ let x$4 = a$0.x in -- #26 -//│ let x$5 = +(x$4,1) in -- #25 -//│ let x$6 = Left(x$5) in -- #24 -//│ jump j$0(x$6) -- #23 -//│ Right => -//│ let x$7 = a$0.y in -- #35 -//│ let x$8 = Right(b$0) in -- #34 -//│ jump j$0(x$8) -- #33 -//│ ) -//│ Def(1, j$1, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ Def(2, j$0, [x$0], -//│ 1, -//│ case x$0 of -- #14 -//│ Left => -//│ let x$2 = x$0.x in -- #8 -//│ jump j$1(x$2) -- #7 -//│ Right => -//│ let x$3 = x$0.y in -- #13 -//│ jump j$1(x$3) -- #12 -//│ ) -//│ Def(3, bar, [], -//│ 1, -//│ let x$9 = Right(2) in -- #48 -//│ let* (x$10) = foo(x$9,2) in -- #47 -//│ x$10 -- #46 -//│ ) -//│ }, -//│ let* (x$11) = bar() in -- #52 -//│ x$11 -- #51) -//│ //│ Promoted: -//│ Program({ClassInfo(0, Left, [x]),ClassInfo(1, Right, [y])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Left, [x]),ClassInfo(3, Right, [y])}, { //│ Def(0, foo, [a$0,b$0], //│ 1, //│ case a$0 of -- #36 @@ -457,35 +280,13 @@ bar() //│ 2 :interpIR -class True -class False class Pair(x, y) fun foo(a) = a.x + a.y fun bar() = foo(Pair(1, 0)) bar() -//│ |#class| |True|↵|#class| |False|↵|#class| |Pair|(|x|,| |y|)|↵|#fun| |foo|(|a|)| |#=| |a|.x| |+| |a|.y|↵|#fun| |bar|(||)| |#=|→|foo|(|Pair|(|1|,| |0|)|)|←|↵|bar|(||)| -//│ Parsed: {class True {}; class False {}; class Pair(x, y,) {}; fun foo = (a,) => +((a).x,)((a).y,); fun bar = () => {foo(Pair(1, 0,),)}; bar()} -//│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { -//│ Def(0, foo, [a$0], -//│ 1, -//│ let x$0 = a$0.x in -- #7 -//│ let x$1 = a$0.y in -- #6 -//│ let x$2 = +(x$0,x$1) in -- #5 -//│ x$2 -- #4 -//│ ) -//│ Def(1, bar, [], -//│ 1, -//│ let x$3 = Pair(1,0) in -- #19 -//│ let* (x$4) = foo(x$3) in -- #18 -//│ x$4 -- #17 -//│ ) -//│ }, -//│ let* (x$5) = bar() in -- #23 -//│ x$5 -- #22) +//│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |foo|(|a|)| |#=| |a|.x| |+| |a|.y|↵|#fun| |bar|(||)| |#=|→|foo|(|Pair|(|1|,| |0|)|)|←|↵|bar|(||)| +//│ Parsed: {class Pair(x, y,) {}; fun foo = (a,) => +((a).x,)((a).y,); fun bar = () => {foo(Pair(1, 0,),)}; bar()} //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { @@ -522,36 +323,8 @@ bar() //│ |#class| |C1|(|x|,| |y|)|↵|#class| |C2|(|z|)|↵|#fun| |foo|(|a|)| |#=| |#if| |a| |is|→|C1|(|x|,| |y|)| |#then| |x|↵|C2|(|z|)| |#then| |z|←|↵|#fun| |bar|(||)| |#=|→|foo|(|C1|(|0|,| |1|)|)|←|↵|bar|(||)| //│ Parsed: {class C1(x, y,) {}; class C2(z,) {}; fun foo = (a,) => if a is ‹(C1(x, y,)) then x; (C2(z,)) then z›; fun bar = () => {foo(C1(0, 1,),)}; bar()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, C1, [x,y]),ClassInfo(1, C2, [z])}, { -//│ Def(0, foo, [a$0], -//│ 1, -//│ case a$0 of -- #15 -//│ C1 => -//│ let x$1 = a$0.y in -- #9 -//│ let x$2 = a$0.x in -- #8 -//│ jump j$0(x$2) -- #7 -//│ C2 => -//│ let x$3 = a$0.z in -- #14 -//│ jump j$0(x$3) -- #13 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, bar, [], -//│ 1, -//│ let x$4 = C1(0,1) in -- #27 -//│ let* (x$5) = foo(x$4) in -- #26 -//│ x$5 -- #25 -//│ ) -//│ }, -//│ let* (x$6) = bar() in -- #31 -//│ x$6 -- #30) -//│ //│ Promoted: -//│ Program({ClassInfo(0, C1, [x,y]),ClassInfo(1, C2, [z])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, C1, [x,y]),ClassInfo(3, C2, [z])}, { //│ Def(0, foo, [a$0], //│ 1, //│ case a$0 of -- #15 @@ -598,43 +371,8 @@ baz() //│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |foo|(|a|,| |b|)| |#=|→|#let| |x1| |#=| |a|.x|↵|#let| |y1| |#=| |a|.y|↵|#let| |x2| |#=| |b|.x|↵|#let| |y2| |#=| |b|.y|↵|x1| |+| |y1| |+| |x2| |+| |y2|←|↵|#fun| |bar|(|c|)| |#=|→|foo|(|Pair|(|0|,| |1|)|,| |c|)|↵|foo|(|c|,| |Pair|(|2|,| |3|)|)|↵|foo|(|Pair|(|0|,| |1|)|,| |Pair|(|2|,| |3|)|)|←|↵|#fun| |baz|(||)| |#=|→|bar|(|Pair|(|4|,|5|)|)|←|↵|baz|(||)| //│ Parsed: {class Pair(x, y,) {}; fun foo = (a, b,) => {let x1 = (a).x; let y1 = (a).y; let x2 = (b).x; let y2 = (b).y; +(+(+(x1,)(y1,),)(x2,),)(y2,)}; fun bar = (c,) => {foo(Pair(0, 1,), c,); foo(c, Pair(2, 3,),); foo(Pair(0, 1,), Pair(2, 3,),)}; fun baz = () => {bar(Pair(4, 5,),)}; baz()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, Pair, [x,y])}, { -//│ Def(0, foo, [a$0,b$0], -//│ 1, -//│ let x$0 = a$0.x in -- #21 -//│ let x$1 = a$0.y in -- #20 -//│ let x$2 = b$0.x in -- #19 -//│ let x$3 = b$0.y in -- #18 -//│ let x$4 = +(x$0,x$1) in -- #17 -//│ let x$5 = +(x$4,x$2) in -- #16 -//│ let x$6 = +(x$5,x$3) in -- #15 -//│ x$6 -- #14 -//│ ) -//│ Def(1, bar, [c$0], -//│ 1, -//│ let x$7 = Pair(0,1) in -- #69 -//│ let* (x$8) = foo(x$7,c$0) in -- #68 -//│ let x$9 = Pair(2,3) in -- #67 -//│ let* (x$10) = foo(c$0,x$9) in -- #66 -//│ let x$11 = Pair(0,1) in -- #65 -//│ let x$12 = Pair(2,3) in -- #64 -//│ let* (x$13) = foo(x$11,x$12) in -- #63 -//│ x$13 -- #62 -//│ ) -//│ Def(2, baz, [], -//│ 1, -//│ let x$14 = Pair(4,5) in -- #81 -//│ let* (x$15) = bar(x$14) in -- #80 -//│ x$15 -- #79 -//│ ) -//│ }, -//│ let* (x$16) = baz() in -- #85 -//│ x$16 -- #84) -//│ //│ Promoted: -//│ Program({ClassInfo(0, Pair, [x,y])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, foo, [a$0,b$0], //│ 1, //│ let x$0 = a$0.x in -- #21 @@ -680,21 +418,8 @@ foo() //│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |foo|(||)| |#=|→|#let| |p| |#=| |Pair|(|0|,| |1|)|↵|#let| |b| |#=| |p|.x|↵|b|←|↵|foo|(||)| //│ Parsed: {class Pair(x, y,) {}; fun foo = () => {let p = Pair(0, 1,); let b = (p).x; b}; foo()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, Pair, [x,y])}, { -//│ Def(0, foo, [], -//│ 1, -//│ let x$0 = Pair(0,1) in -- #10 -//│ let x$1 = x$0.x in -- #9 -//│ x$1 -- #8 -//│ ) -//│ }, -//│ let* (x$2) = foo() in -- #14 -//│ x$2 -- #13) -//│ //│ Promoted: -//│ Program({ClassInfo(0, Pair, [x,y])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, foo, [], //│ 1, //│ let x$0 = Pair(0,1) in -- #10 @@ -723,40 +448,8 @@ foo() //│ |#class| |S|(|s|)|↵|#class| |O|↵|#fun| |foo|(||)| |#=|→|bar|(|S|(|O|)|)|←|↵|#fun| |bar|(|x|)| |#=|→|baz|(|x|)|←|↵|#fun| |baz|(|x|)| |#=|→|#if| |x| |is|→|S|(|s|)| |#then| |s|↵|O| |#then| |x|←|←|↵|foo|(||)| //│ Parsed: {class S(s,) {}; class O {}; fun foo = () => {bar(S(O,),)}; fun bar = (x,) => {baz(x,)}; fun baz = (x,) => {if x is ‹(S(s,)) then s; (O) then x›}; foo()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, S, [s]),ClassInfo(1, O, [])}, { -//│ Def(0, foo, [], -//│ 1, -//│ let x$0 = O() in -- #10 -//│ let x$1 = S(x$0) in -- #9 -//│ let* (x$2) = bar(x$1) in -- #8 -//│ x$2 -- #7 -//│ ) -//│ Def(1, bar, [x$3], -//│ 1, -//│ let* (x$4) = baz(x$3) in -- #16 -//│ x$4 -- #15 -//│ ) -//│ Def(2, baz, [x$5], -//│ 1, -//│ case x$5 of -- #26 -//│ S => -//│ let x$7 = x$5.s in -- #23 -//│ jump j$0(x$7) -- #22 -//│ O => -//│ jump j$0(x$5) -- #25 -//│ ) -//│ Def(3, j$0, [x$6], -//│ 1, -//│ x$6 -- #18 -//│ ) -//│ }, -//│ let* (x$8) = foo() in -- #30 -//│ x$8 -- #29) -//│ //│ Promoted: -//│ Program({ClassInfo(0, S, [s]),ClassInfo(1, O, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { //│ Def(0, foo, [], //│ 1, //│ let x$0 = O() in -- #10 diff --git a/compiler/shared/test/diff-ir/IRComplex.mls b/compiler/shared/test/diff-ir/IRComplex.mls index 5e8d6ebb..e6df8a88 100644 --- a/compiler/shared/test/diff-ir/IRComplex.mls +++ b/compiler/shared/test/diff-ir/IRComplex.mls @@ -1,6 +1,7 @@ :NewParser :ParseOnly :UseIR +:NoTailRec :interpIR class A(x, y, z) @@ -21,58 +22,8 @@ bar() //│ |#class| |A|(|x|,| |y|,| |z|)|↵|#class| |B|(|m|,| |n|)|↵|#fun| |complex_foo|(|t|)| |#=|→|#let| |r| |#=| |#if| |t| |is|→|A|(|x|,| |y|,| |z|)| |#then| |x| |+| |y| |*| |z|↵|B|(|m|,| |n|)| |#then| |m| |-| |n|←|↵|#let| |s| |#=| |B|(|1|,| |2|)|↵|#let| |u| |#=| |#if| |s| |is|→|A|(|x|,| |y|,| |z|)| |#then| |3|↵|B|(|m|,| |n|)| |#then| |4|←|↵|r| |+| |u|←|↵|#fun| |bar|(||)| |#=|→|complex_foo|(|A|(|6|,| |7|,| |8|)|)|↵|complex_foo|(|B|(|9|,| |10|)|)|←|↵|bar|(||)| //│ Parsed: {class A(x, y, z,) {}; class B(m, n,) {}; fun complex_foo = (t,) => {let r = if t is ‹(A(x, y, z,)) then +(x,)(*(y,)(z,),); (B(m, n,)) then -(m,)(n,)›; let s = B(1, 2,); let u = if s is ‹(A(x, y, z,)) then 3; (B(m, n,)) then 4›; +(r,)(u,)}; fun bar = () => {complex_foo(A(6, 7, 8,),); complex_foo(B(9, 10,),)}; bar()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, A, [x,y,z]),ClassInfo(1, B, [m,n])}, { -//│ Def(0, complex_foo, [t$0], -//│ 1, -//│ case t$0 of -- #63 -//│ A => -//│ let x$9 = t$0.z in -- #51 -//│ let x$10 = t$0.y in -- #50 -//│ let x$11 = t$0.x in -- #49 -//│ let x$12 = *(x$10,x$9) in -- #48 -//│ let x$13 = +(x$11,x$12) in -- #47 -//│ jump j$0(x$13) -- #46 -//│ B => -//│ let x$14 = t$0.n in -- #62 -//│ let x$15 = t$0.m in -- #61 -//│ let x$16 = -(x$15,x$14) in -- #60 -//│ jump j$0(x$16) -- #59 -//│ ) -//│ Def(1, j$1, [x$2,x$0], -//│ 1, -//│ let x$3 = +(x$0,x$2) in -- #13 -//│ x$3 -- #12 -//│ ) -//│ Def(2, j$0, [x$0], -//│ 1, -//│ let x$1 = B(1,2) in -- #34 -//│ case x$1 of -- #33 -//│ A => -//│ let x$4 = x$1.z in -- #24 -//│ let x$5 = x$1.y in -- #23 -//│ let x$6 = x$1.x in -- #22 -//│ jump j$1(3,x$0) -- #21 -//│ B => -//│ let x$7 = x$1.n in -- #32 -//│ let x$8 = x$1.m in -- #31 -//│ jump j$1(4,x$0) -- #30 -//│ ) -//│ Def(3, bar, [], -//│ 1, -//│ let x$17 = A(6,7,8) in -- #89 -//│ let* (x$18) = complex_foo(x$17) in -- #88 -//│ let x$19 = B(9,10) in -- #87 -//│ let* (x$20) = complex_foo(x$19) in -- #86 -//│ x$20 -- #85 -//│ ) -//│ }, -//│ let* (x$21) = bar() in -- #93 -//│ x$21 -- #92) -//│ //│ Promoted: -//│ Program({ClassInfo(0, A, [x,y,z]),ClassInfo(1, B, [m,n])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [x,y,z]),ClassInfo(3, B, [m,n])}, { //│ Def(0, complex_foo, [t$0], //│ 1, //│ case t$0 of -- #63 @@ -159,106 +110,8 @@ bar() //│ |#class| |A|(|w|,| |x|)|↵|#class| |B|(|y|)|↵|#class| |C|(|z|)|↵|#fun| |complex_foo|(|t|)| |#=|→|#let| |a| |#=| |1| |+| |2|↵|#let| |b| |#=| |1| |*| |2|↵|#let| |x| |#=| |#if| |t| |is|→|A|(|x|,| |y|)| |#then| |y|↵|B|(|x|)| |#then| |B|(|x| |+| |b|)|↵|C|(|x|)| |#then| |C|(|0|)|←|↵|#let| |z| |#=| |A|(|5|,| |x|)|↵|#let| |v| |#=| |B|(|6|)|↵|#let| |y| |#=| |#if| |x| |is|→|A|(|x|,| |y|)| |#then|→|#let| |m| |#=| |x| |+| |a| |+| |b|↵|#if| |y| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |m|↵|C|(|x|)| |#then| |0|←|←|↵|B|(|x|)| |#then| |2|↵|C|(|x|)| |#then| |3|←|↵|#if| |z| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |4|↵|C|(|x|)| |#then|→|#if| |v| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |7|↵|C|(|x|)| |#then| |8|←|←|←|←|↵|#fun| |bar|(||)| |#=|→|complex_foo|(|A|(|10|,| |A|(|9|,| |B|(|10|)|)|)|)|←|↵|bar|(||)| //│ Parsed: {class A(w, x,) {}; class B(y,) {}; class C(z,) {}; fun complex_foo = (t,) => {let a = +(1,)(2,); let b = *(1,)(2,); let x = if t is ‹(A(x, y,)) then y; (B(x,)) then B(+(x,)(b,),); (C(x,)) then C(0,)›; let z = A(5, x,); let v = B(6,); let y = if x is ‹(A(x, y,)) then {let m = +(+(x,)(a,),)(b,); if y is ‹(A(x, y,)) then x; (B(x,)) then m; (C(x,)) then 0›}; (B(x,)) then 2; (C(x,)) then 3›; if z is ‹(A(x, y,)) then x; (B(x,)) then 4; (C(x,)) then {if v is ‹(A(x, y,)) then x; (B(x,)) then 7; (C(x,)) then 8›}›}; fun bar = () => {complex_foo(A(10, A(9, B(10,),),),)}; bar()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, A, [w,x]),ClassInfo(1, B, [y]),ClassInfo(2, C, [z])}, { -//│ Def(0, complex_foo, [t$0], -//│ 1, -//│ let x$0 = +(1,2) in -- #140 -//│ let x$1 = *(1,2) in -- #139 -//│ case t$0 of -- #138 -//│ A => -//│ let x$27 = t$0.x in -- #116 -//│ let x$28 = t$0.w in -- #115 -//│ jump j$0(x$27,x$0,x$1) -- #114 -//│ B => -//│ let x$29 = t$0.y in -- #128 -//│ let x$30 = +(x$29,x$1) in -- #127 -//│ let x$31 = B(x$30) in -- #126 -//│ jump j$0(x$31,x$0,x$1) -- #125 -//│ C => -//│ let x$32 = t$0.z in -- #137 -//│ let x$33 = C(0) in -- #136 -//│ jump j$0(x$33,x$0,x$1) -- #135 -//│ ) -//│ Def(1, j$2, [x$6], -//│ 1, -//│ x$6 -- #21 -//│ ) -//│ Def(2, j$3, [x$11], -//│ 1, -//│ jump j$2(x$11) -- #39 -//│ ) -//│ Def(3, j$1, [x$5,x$3,x$4], -//│ 1, -//│ case x$3 of -- #60 -//│ A => -//│ let x$7 = x$3.x in -- #29 -//│ let x$8 = x$3.w in -- #28 -//│ jump j$2(x$8) -- #27 -//│ B => -//│ let x$9 = x$3.y in -- #34 -//│ jump j$2(4) -- #33 -//│ C => -//│ let x$10 = x$3.z in -- #59 -//│ case x$4 of -- #58 -//│ A => -//│ let x$12 = x$4.x in -- #47 -//│ let x$13 = x$4.w in -- #46 -//│ jump j$3(x$13) -- #45 -//│ B => -//│ let x$14 = x$4.y in -- #52 -//│ jump j$3(7) -- #51 -//│ C => -//│ let x$15 = x$4.z in -- #57 -//│ jump j$3(8) -- #56 -//│ ) -//│ Def(4, j$4, [x$20,x$3,x$4], -//│ 1, -//│ jump j$1(x$20,x$3,x$4) -- #72 -//│ ) -//│ Def(5, j$0, [x$2,x$0,x$1], -//│ 1, -//│ let x$3 = A(5,x$2) in -- #108 -//│ let x$4 = B(6) in -- #107 -//│ case x$2 of -- #106 -//│ A => -//│ let x$16 = x$2.x in -- #95 -//│ let x$17 = x$2.w in -- #94 -//│ let x$18 = +(x$17,x$0) in -- #93 -//│ let x$19 = +(x$18,x$1) in -- #92 -//│ case x$16 of -- #91 -//│ A => -//│ let x$21 = x$16.x in -- #80 -//│ let x$22 = x$16.w in -- #79 -//│ jump j$4(x$22,x$3,x$4) -- #78 -//│ B => -//│ let x$23 = x$16.y in -- #85 -//│ jump j$4(x$19,x$3,x$4) -- #84 -//│ C => -//│ let x$24 = x$16.z in -- #90 -//│ jump j$4(0,x$3,x$4) -- #89 -//│ B => -//│ let x$25 = x$2.y in -- #100 -//│ jump j$1(2,x$3,x$4) -- #99 -//│ C => -//│ let x$26 = x$2.z in -- #105 -//│ jump j$1(3,x$3,x$4) -- #104 -//│ ) -//│ Def(6, bar, [], -//│ 1, -//│ let x$34 = B(10) in -- #162 -//│ let x$35 = A(9,x$34) in -- #161 -//│ let x$36 = A(10,x$35) in -- #160 -//│ let* (x$37) = complex_foo(x$36) in -- #159 -//│ x$37 -- #158 -//│ ) -//│ }, -//│ let* (x$38) = bar() in -- #166 -//│ x$38 -- #165) -//│ //│ Promoted: -//│ Program({ClassInfo(0, A, [w,x]),ClassInfo(1, B, [y]),ClassInfo(2, C, [z])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [w,x]),ClassInfo(3, B, [y]),ClassInfo(4, C, [z])}, { //│ Def(0, complex_foo, [t$0], //│ 1, //│ let x$0 = +(1,2) in -- #140 @@ -393,108 +246,8 @@ bar() //│ |#class| |A|(|w|,| |x|)|↵|#class| |B|(|y|)|↵|#class| |C|(|z|)|↵|#fun| |complex_foo|(|t|)| |#=|→|#let| |a| |#=| |1| |+| |2|↵|#let| |b| |#=| |1| |*| |2|↵|#let| |x| |#=| |#if| |t| |is|→|A|(|x|,| |y|)| |#then| |A|(|x|,| |C|(|0|)|)|↵|B|(|x|)| |#then| |B|(|x| |+| |b|)|↵|C|(|x|)| |#then| |C|(|0|)|←|↵|#let| |z| |#=| |A|(|5|,| |x|)|↵|#let| |v| |#=| |B|(|6|)|↵|#let| |y| |#=| |#if| |x| |is|→|A|(|x|,| |y|)| |#then|→|#let| |m| |#=| |x| |+| |a| |+| |b|↵|#if| |y| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |m|↵|C|(|x|)| |#then| |0|←|←|↵|B|(|x|)| |#then| |2|↵|C|(|x|)| |#then| |3|←|↵|#if| |z| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |4|↵|C|(|x|)| |#then|→|#if| |v| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |7|↵|C|(|x|)| |#then| |8|←|←|←|←|↵|#fun| |bar|(||)| |#=|→|complex_foo|(|A|(|10|,| |A|(|9|,| |B|(|10|)|)|)|)|←|↵|bar|(||)| //│ Parsed: {class A(w, x,) {}; class B(y,) {}; class C(z,) {}; fun complex_foo = (t,) => {let a = +(1,)(2,); let b = *(1,)(2,); let x = if t is ‹(A(x, y,)) then A(x, C(0,),); (B(x,)) then B(+(x,)(b,),); (C(x,)) then C(0,)›; let z = A(5, x,); let v = B(6,); let y = if x is ‹(A(x, y,)) then {let m = +(+(x,)(a,),)(b,); if y is ‹(A(x, y,)) then x; (B(x,)) then m; (C(x,)) then 0›}; (B(x,)) then 2; (C(x,)) then 3›; if z is ‹(A(x, y,)) then x; (B(x,)) then 4; (C(x,)) then {if v is ‹(A(x, y,)) then x; (B(x,)) then 7; (C(x,)) then 8›}›}; fun bar = () => {complex_foo(A(10, A(9, B(10,),),),)}; bar()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, A, [w,x]),ClassInfo(1, B, [y]),ClassInfo(2, C, [z])}, { -//│ Def(0, complex_foo, [t$0], -//│ 1, -//│ let x$0 = +(1,2) in -- #150 -//│ let x$1 = *(1,2) in -- #149 -//│ case t$0 of -- #148 -//│ A => -//│ let x$27 = t$0.x in -- #126 -//│ let x$28 = t$0.w in -- #125 -//│ let x$29 = C(0) in -- #124 -//│ let x$30 = A(x$28,x$29) in -- #123 -//│ jump j$0(x$30,x$0,x$1) -- #122 -//│ B => -//│ let x$31 = t$0.y in -- #138 -//│ let x$32 = +(x$31,x$1) in -- #137 -//│ let x$33 = B(x$32) in -- #136 -//│ jump j$0(x$33,x$0,x$1) -- #135 -//│ C => -//│ let x$34 = t$0.z in -- #147 -//│ let x$35 = C(0) in -- #146 -//│ jump j$0(x$35,x$0,x$1) -- #145 -//│ ) -//│ Def(1, j$2, [x$6], -//│ 1, -//│ x$6 -- #21 -//│ ) -//│ Def(2, j$3, [x$11], -//│ 1, -//│ jump j$2(x$11) -- #39 -//│ ) -//│ Def(3, j$1, [x$5,x$3,x$4], -//│ 1, -//│ case x$3 of -- #60 -//│ A => -//│ let x$7 = x$3.x in -- #29 -//│ let x$8 = x$3.w in -- #28 -//│ jump j$2(x$8) -- #27 -//│ B => -//│ let x$9 = x$3.y in -- #34 -//│ jump j$2(4) -- #33 -//│ C => -//│ let x$10 = x$3.z in -- #59 -//│ case x$4 of -- #58 -//│ A => -//│ let x$12 = x$4.x in -- #47 -//│ let x$13 = x$4.w in -- #46 -//│ jump j$3(x$13) -- #45 -//│ B => -//│ let x$14 = x$4.y in -- #52 -//│ jump j$3(7) -- #51 -//│ C => -//│ let x$15 = x$4.z in -- #57 -//│ jump j$3(8) -- #56 -//│ ) -//│ Def(4, j$4, [x$20,x$3,x$4], -//│ 1, -//│ jump j$1(x$20,x$3,x$4) -- #72 -//│ ) -//│ Def(5, j$0, [x$2,x$0,x$1], -//│ 1, -//│ let x$3 = A(5,x$2) in -- #108 -//│ let x$4 = B(6) in -- #107 -//│ case x$2 of -- #106 -//│ A => -//│ let x$16 = x$2.x in -- #95 -//│ let x$17 = x$2.w in -- #94 -//│ let x$18 = +(x$17,x$0) in -- #93 -//│ let x$19 = +(x$18,x$1) in -- #92 -//│ case x$16 of -- #91 -//│ A => -//│ let x$21 = x$16.x in -- #80 -//│ let x$22 = x$16.w in -- #79 -//│ jump j$4(x$22,x$3,x$4) -- #78 -//│ B => -//│ let x$23 = x$16.y in -- #85 -//│ jump j$4(x$19,x$3,x$4) -- #84 -//│ C => -//│ let x$24 = x$16.z in -- #90 -//│ jump j$4(0,x$3,x$4) -- #89 -//│ B => -//│ let x$25 = x$2.y in -- #100 -//│ jump j$1(2,x$3,x$4) -- #99 -//│ C => -//│ let x$26 = x$2.z in -- #105 -//│ jump j$1(3,x$3,x$4) -- #104 -//│ ) -//│ Def(6, bar, [], -//│ 1, -//│ let x$36 = B(10) in -- #172 -//│ let x$37 = A(9,x$36) in -- #171 -//│ let x$38 = A(10,x$37) in -- #170 -//│ let* (x$39) = complex_foo(x$38) in -- #169 -//│ x$39 -- #168 -//│ ) -//│ }, -//│ let* (x$40) = bar() in -- #176 -//│ x$40 -- #175) -//│ //│ Promoted: -//│ Program({ClassInfo(0, A, [w,x]),ClassInfo(1, B, [y]),ClassInfo(2, C, [z])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [w,x]),ClassInfo(3, B, [y]),ClassInfo(4, C, [z])}, { //│ Def(0, complex_foo, [t$0], //│ 1, //│ let x$0 = +(1,2) in -- #150 diff --git a/compiler/shared/test/diff-ir/IRRec.mls b/compiler/shared/test/diff-ir/IRRec.mls index 0275612d..a56f53a9 100644 --- a/compiler/shared/test/diff-ir/IRRec.mls +++ b/compiler/shared/test/diff-ir/IRRec.mls @@ -1,6 +1,7 @@ :NewParser :ParseOnly :UseIR +:NoTailRec :interpIR class True @@ -10,33 +11,8 @@ fib(20) //│ |#class| |True|↵|#class| |False|↵|#fun| |fib|(|n|)| |#=| |#if| |n| |<| |2| |#then| |n| |#else| |fib|(|n|-|1|)| |+| |fib|(|n|-|2|)|↵|fib|(|20|)| //│ Parsed: {class True {}; class False {}; fun fib = (n,) => if (<(n,)(2,)) then n else +(fib(-(n,)(1,),),)(fib(-(n,)(2,),),); fib(20,)} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, fib, [n$0], -//│ 1, -//│ let x$0 = <(n$0,2) in -- #28 -//│ if x$0 -- #27 -//│ true => -//│ jump j$0(n$0) -- #5 -//│ false => -//│ let x$2 = -(n$0,1) in -- #26 -//│ let* (x$3) = fib(x$2) in -- #25 -//│ let x$4 = -(n$0,2) in -- #24 -//│ let* (x$5) = fib(x$4) in -- #23 -//│ let x$6 = +(x$3,x$5) in -- #22 -//│ jump j$0(x$6) -- #21 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ }, -//│ let* (x$7) = fib(20) in -- #34 -//│ x$7 -- #33) -//│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, [])}, { //│ Def(0, fib, [n$0], //│ 1, //│ let x$0 = <(n$0,2) in -- #28 @@ -72,52 +48,8 @@ foo() //│ |#class| |True|↵|#class| |False|↵|#fun| |odd|(|x|)| |#=| |#if| |x| |==| |0| |#then| |False| |#else| |even|(|x|-|1|)|↵|#fun| |even|(|x|)| |#=| |#if| |x| |==| |0| |#then| |True| |#else| |odd|(|x|-|1|)|↵|#fun| |foo|(||)| |#=| |odd|(|10|)|↵|foo|(||)| //│ Parsed: {class True {}; class False {}; fun odd = (x,) => if (==(x,)(0,)) then False else even(-(x,)(1,),); fun even = (x,) => if (==(x,)(0,)) then True else odd(-(x,)(1,),); fun foo = () => odd(10,); foo()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, odd, [x$0], -//│ 1, -//│ let x$1 = ==(x$0,0) in -- #18 -//│ if x$1 -- #17 -//│ true => -//│ let x$3 = False() in -- #6 -//│ jump j$0(x$3) -- #5 -//│ false => -//│ let x$4 = -(x$0,1) in -- #16 -//│ let* (x$5) = even(x$4) in -- #15 -//│ jump j$0(x$5) -- #14 -//│ ) -//│ Def(1, j$0, [x$2], -//│ 1, -//│ x$2 -- #3 -//│ ) -//│ Def(2, even, [x$6], -//│ 1, -//│ let x$7 = ==(x$6,0) in -- #37 -//│ if x$7 -- #36 -//│ true => -//│ let x$9 = True() in -- #25 -//│ jump j$1(x$9) -- #24 -//│ false => -//│ let x$10 = -(x$6,1) in -- #35 -//│ let* (x$11) = odd(x$10) in -- #34 -//│ jump j$1(x$11) -- #33 -//│ ) -//│ Def(3, j$1, [x$8], -//│ 1, -//│ x$8 -- #22 -//│ ) -//│ Def(4, foo, [], -//│ 1, -//│ let* (x$12) = odd(10) in -- #43 -//│ x$12 -- #42 -//│ ) -//│ }, -//│ let* (x$13) = foo() in -- #47 -//│ x$13 -- #46) -//│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, [])}, { //│ Def(0, odd, [x$0], //│ 1, //│ let x$1 = ==(x$0,0) in -- #18 @@ -177,51 +109,8 @@ main() //│ |#class| |True|↵|#class| |False|↵|#class| |A|↵|#class| |B|(|b|)|↵|#fun| |not|(|x|)| |#=|→|#if| |x| |#then| |False| |#else| |True|←|↵|#fun| |foo|(|x|)| |#=|→|#if| |x| |#then| |A|→|#else| |B|(|foo|(|not|(|x|)|)|)|←|←|↵|#fun| |main|(||)| |#=| |foo|(|False|)|↵|main|(||)| //│ Parsed: {class True {}; class False {}; class A {}; class B(b,) {}; fun not = (x,) => {if (x) then False else True}; fun foo = (x,) => {if (x) then A else B(foo(not(x,),),)}; fun main = () => foo(False,); main()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { -//│ Def(0, not, [x$0], -//│ 1, -//│ if x$0 -- #8 -//│ true => -//│ let x$2 = False() in -- #4 -//│ jump j$0(x$2) -- #3 -//│ false => -//│ let x$3 = True() in -- #7 -//│ jump j$0(x$3) -- #6 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, foo, [x$4], -//│ 1, -//│ if x$4 -- #30 -//│ true => -//│ let x$6 = A() in -- #13 -//│ jump j$1(x$6) -- #12 -//│ false => -//│ let* (x$7) = not(x$4) in -- #29 -//│ let* (x$8) = foo(x$7) in -- #28 -//│ let x$9 = B(x$8) in -- #27 -//│ jump j$1(x$9) -- #26 -//│ ) -//│ Def(3, j$1, [x$5], -//│ 1, -//│ x$5 -- #10 -//│ ) -//│ Def(4, main, [], -//│ 1, -//│ let x$10 = False() in -- #37 -//│ let* (x$11) = foo(x$10) in -- #36 -//│ x$11 -- #35 -//│ ) -//│ }, -//│ let* (x$12) = main() in -- #41 -//│ x$12 -- #40) -//│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, A, []),ClassInfo(5, B, [b])}, { //│ Def(0, not, [x$0], //│ 1, //│ if x$0 -- #8 @@ -293,80 +182,8 @@ main() //│ |#class| |True|↵|#class| |False|↵|#class| |A|(||)|↵|#class| |B|(|b|)|↵|#fun| |aaa|(||)| |#=|→|#let| |m| |#=| |1|↵|#let| |n| |#=| |2|↵|#let| |p| |#=| |3|↵|#let| |q| |#=| |4|↵|m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |bbb|(||)| |#=|→|#let| |x| |#=| |aaa|(||)|↵|x| |*| |100| |+| |4|←|↵|#fun| |not|(|x|)| |#=|→|#if| |x| |#then| |False| |#else| |True|←|↵|#fun| |foo|(|x|)| |#=|→|#if| |x| |#then| |A|→|#else| |B|(|foo|(|not|(|x|)|)|)|←|←|↵|#fun| |main|(||)| |#=|→|#let| |x| |#=| |foo|(|False|)|↵|#if| |x| |is|→|A| |#then| |aaa|(||)|↵|B|(|b1|)| |#then| |bbb|(||)|←|←|↵|main|(||)| //│ Parsed: {class True {}; class False {}; class A() {}; class B(b,) {}; fun aaa = () => {let m = 1; let n = 2; let p = 3; let q = 4; +(-(+(m,)(n,),)(p,),)(q,)}; fun bbb = () => {let x = aaa(); +(*(x,)(100,),)(4,)}; fun not = (x,) => {if (x) then False else True}; fun foo = (x,) => {if (x) then A else B(foo(not(x,),),)}; fun main = () => {let x = foo(False,); if x is ‹(A) then aaa(); (B(b1,)) then bbb()›}; main()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { -//│ Def(0, aaa, [], -//│ 1, -//│ let x$0 = 1 in -- #17 -//│ let x$1 = 2 in -- #16 -//│ let x$2 = 3 in -- #15 -//│ let x$3 = 4 in -- #14 -//│ let x$4 = +(x$0,x$1) in -- #13 -//│ let x$5 = -(x$4,x$2) in -- #12 -//│ let x$6 = +(x$5,x$3) in -- #11 -//│ x$6 -- #10 -//│ ) -//│ Def(1, bbb, [], -//│ 1, -//│ let* (x$7) = aaa() in -- #28 -//│ let x$8 = *(x$7,100) in -- #27 -//│ let x$9 = +(x$8,4) in -- #26 -//│ x$9 -- #25 -//│ ) -//│ Def(2, not, [x$10], -//│ 1, -//│ if x$10 -- #37 -//│ true => -//│ let x$12 = False() in -- #33 -//│ jump j$0(x$12) -- #32 -//│ false => -//│ let x$13 = True() in -- #36 -//│ jump j$0(x$13) -- #35 -//│ ) -//│ Def(3, j$0, [x$11], -//│ 1, -//│ x$11 -- #30 -//│ ) -//│ Def(4, foo, [x$14], -//│ 1, -//│ if x$14 -- #59 -//│ true => -//│ let x$16 = A() in -- #42 -//│ jump j$1(x$16) -- #41 -//│ false => -//│ let* (x$17) = not(x$14) in -- #58 -//│ let* (x$18) = foo(x$17) in -- #57 -//│ let x$19 = B(x$18) in -- #56 -//│ jump j$1(x$19) -- #55 -//│ ) -//│ Def(5, j$1, [x$15], -//│ 1, -//│ x$15 -- #39 -//│ ) -//│ Def(6, main, [], -//│ 1, -//│ let x$20 = False() in -- #82 -//│ let* (x$21) = foo(x$20) in -- #81 -//│ case x$21 of -- #80 -//│ A => -//│ let* (x$23) = aaa() in -- #71 -//│ jump j$2(x$23) -- #70 -//│ B => -//│ let x$24 = x$21.b in -- #79 -//│ let* (x$25) = bbb() in -- #78 -//│ jump j$2(x$25) -- #77 -//│ ) -//│ Def(7, j$2, [x$22], -//│ 1, -//│ x$22 -- #66 -//│ ) -//│ }, -//│ let* (x$26) = main() in -- #86 -//│ x$26 -- #85) -//│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, A, []),ClassInfo(5, B, [b])}, { //│ Def(0, aaa, [], //│ 1, //│ let x$0 = 1 in -- #17 @@ -458,54 +275,8 @@ foo() //│ |#class| |True|↵|#class| |False|↵|#class| |S|(|s|)|↵|#class| |O|↵|#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |foo|(||)| |#=| |odd|(|S|(|S|(|S|(|O|)|)|)|)|↵|foo|(||)| //│ Parsed: {class True {}; class False {}; class S(s,) {}; class O {}; fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun foo = () => odd(S(S(S(O,),),),); foo()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { -//│ Def(0, odd, [x$0], -//│ 1, -//│ case x$0 of -- #15 -//│ O => -//│ let x$2 = False() in -- #4 -//│ jump j$0(x$2) -- #3 -//│ S => -//│ let x$3 = x$0.s in -- #14 -//│ let* (x$4) = even(x$3) in -- #13 -//│ jump j$0(x$4) -- #12 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, even, [x$5], -//│ 1, -//│ case x$5 of -- #31 -//│ O => -//│ let x$7 = True() in -- #20 -//│ jump j$1(x$7) -- #19 -//│ S => -//│ let x$8 = x$5.s in -- #30 -//│ let* (x$9) = odd(x$8) in -- #29 -//│ jump j$1(x$9) -- #28 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #17 -//│ ) -//│ Def(4, foo, [], -//│ 1, -//│ let x$10 = O() in -- #50 -//│ let x$11 = S(x$10) in -- #49 -//│ let x$12 = S(x$11) in -- #48 -//│ let x$13 = S(x$12) in -- #47 -//│ let* (x$14) = odd(x$13) in -- #46 -//│ x$14 -- #45 -//│ ) -//│ }, -//│ let* (x$15) = foo() in -- #54 -//│ x$15 -- #53) -//│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, S, [s]),ClassInfo(5, O, [])}, { //│ Def(0, odd, [x$0], //│ 1, //│ case x$0 of -- #15 @@ -571,9 +342,8 @@ foo() //│ |#class| |True|↵|#class| |False|↵|#class| |S|(|s|)|↵|#class| |O|↵|#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |mk|(|n|)| |#=| |#if| |n| |>| |0| |#then| |S|(|mk|(|n| |-| |1|)|)| |#else| |O|↵|#fun| |foo|(||)| |#=| |odd|(|mk|(|10|)|)|↵|foo|(||)| | //│ Parsed: {class True {}; class False {}; class S(s,) {}; class O {}; fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun mk = (n,) => if (>(n,)(0,)) then S(mk(-(n,)(1,),),) else O; fun foo = () => odd(mk(10,),); foo()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, S, [s]),ClassInfo(5, O, [])}, { //│ Def(0, odd, [x$0], //│ 1, //│ case x$0 of -- #15 @@ -631,8 +401,31 @@ foo() //│ let* (x$18) = foo() in -- #69 //│ x$18 -- #68) //│ +//│ Interpreted: +//│ +//│ IR Processing Failed: can not find the matched case, ClassInfo(0, True, []) expected + +:interpIR +class True +class False +class S(s) +class O +fun odd(x) = + if x is + O then False + S(s) then even(s) +fun even(x) = + if x is + O then True + S(s) then odd(s) +fun mk(n) = if n > 0 then S(mk(n - 1)) else O +fun foo() = odd(S(S(mk(10)))) +foo() +//│ |#class| |True|↵|#class| |False|↵|#class| |S|(|s|)|↵|#class| |O|↵|#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |mk|(|n|)| |#=| |#if| |n| |>| |0| |#then| |S|(|mk|(|n| |-| |1|)|)| |#else| |O|↵|#fun| |foo|(||)| |#=| |odd|(|S|(|S|(|mk|(|10|)|)|)|)|↵|foo|(||)| +//│ Parsed: {class True {}; class False {}; class S(s,) {}; class O {}; fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun mk = (n,) => if (>(n,)(0,)) then S(mk(-(n,)(1,),),) else O; fun foo = () => odd(S(S(mk(10,),),),); foo()} +//│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, S, [s]),ClassInfo(5, O, [])}, { //│ Def(0, odd, [x$0], //│ 1, //│ case x$0 of -- #15 @@ -682,161 +475,19 @@ foo() //│ ) //│ Def(6, foo, [], //│ 1, -//│ let* (x$16) = mk(10) in -- #65 -//│ let* (x$17) = odd(x$16) in -- #64 -//│ x$17 -- #63 +//│ let* (x$16) = mk(10) in -- #73 +//│ let x$17 = S(x$16) in -- #72 +//│ let x$18 = S(x$17) in -- #71 +//│ let* (x$19) = odd(x$18) in -- #70 +//│ x$19 -- #69 //│ ) //│ }, -//│ let* (x$18) = foo() in -- #69 -//│ x$18 -- #68) +//│ let* (x$20) = foo() in -- #77 +//│ x$20 -- #76) //│ //│ Interpreted: -//│ False() - -:interpIR -class True -class False -class S(s) -class O -fun odd(x) = - if x is - O then False - S(s) then even(s) -fun even(x) = - if x is - O then True - S(s) then odd(s) -fun mk(n) = if n > 0 then S(mk(n - 1)) else O -fun foo() = odd(S(S(mk(10)))) -foo() -//│ |#class| |True|↵|#class| |False|↵|#class| |S|(|s|)|↵|#class| |O|↵|#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |mk|(|n|)| |#=| |#if| |n| |>| |0| |#then| |S|(|mk|(|n| |-| |1|)|)| |#else| |O|↵|#fun| |foo|(||)| |#=| |odd|(|S|(|S|(|mk|(|10|)|)|)|)|↵|foo|(||)| -//│ Parsed: {class True {}; class False {}; class S(s,) {}; class O {}; fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun mk = (n,) => if (>(n,)(0,)) then S(mk(-(n,)(1,),),) else O; fun foo = () => odd(S(S(mk(10,),),),); foo()} -//│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { -//│ Def(0, odd, [x$0], -//│ 1, -//│ case x$0 of -- #15 -//│ O => -//│ let x$2 = False() in -- #4 -//│ jump j$0(x$2) -- #3 -//│ S => -//│ let x$3 = x$0.s in -- #14 -//│ let* (x$4) = even(x$3) in -- #13 -//│ jump j$0(x$4) -- #12 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, even, [x$5], -//│ 1, -//│ case x$5 of -- #31 -//│ O => -//│ let x$7 = True() in -- #20 -//│ jump j$1(x$7) -- #19 -//│ S => -//│ let x$8 = x$5.s in -- #30 -//│ let* (x$9) = odd(x$8) in -- #29 -//│ jump j$1(x$9) -- #28 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #17 -//│ ) -//│ Def(4, mk, [n$0], -//│ 1, -//│ let x$10 = >(n$0,0) in -- #54 -//│ if x$10 -- #53 -//│ true => -//│ let x$12 = -(n$0,1) in -- #49 -//│ let* (x$13) = mk(x$12) in -- #48 -//│ let x$14 = S(x$13) in -- #47 -//│ jump j$2(x$14) -- #46 -//│ false => -//│ let x$15 = O() in -- #52 -//│ jump j$2(x$15) -- #51 -//│ ) -//│ Def(5, j$2, [x$11], -//│ 1, -//│ x$11 -- #35 -//│ ) -//│ Def(6, foo, [], -//│ 1, -//│ let* (x$16) = mk(10) in -- #73 -//│ let x$17 = S(x$16) in -- #72 -//│ let x$18 = S(x$17) in -- #71 -//│ let* (x$19) = odd(x$18) in -- #70 -//│ x$19 -- #69 -//│ ) -//│ }, -//│ let* (x$20) = foo() in -- #77 -//│ x$20 -- #76) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { -//│ Def(0, odd, [x$0], -//│ 1, -//│ case x$0 of -- #15 -//│ O => -//│ let x$2 = False() in -- #4 -//│ jump j$0(x$2) -- #3 -//│ S => -//│ let x$3 = x$0.s in -- #14 -//│ let* (x$4) = even(x$3) in -- #13 -//│ jump j$0(x$4) -- #12 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, even, [x$5], -//│ 1, -//│ case x$5 of -- #31 -//│ O => -//│ let x$7 = True() in -- #20 -//│ jump j$1(x$7) -- #19 -//│ S => -//│ let x$8 = x$5.s in -- #30 -//│ let* (x$9) = odd(x$8) in -- #29 -//│ jump j$1(x$9) -- #28 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #17 -//│ ) -//│ Def(4, mk, [n$0], -//│ 1, -//│ let x$10 = >(n$0,0) in -- #54 -//│ if x$10 -- #53 -//│ true => -//│ let x$12 = -(n$0,1) in -- #49 -//│ let* (x$13) = mk(x$12) in -- #48 -//│ let x$14 = S(x$13) in -- #47 -//│ jump j$2(x$14) -- #46 -//│ false => -//│ let x$15 = O() in -- #52 -//│ jump j$2(x$15) -- #51 -//│ ) -//│ Def(5, j$2, [x$11], -//│ 1, -//│ x$11 -- #35 -//│ ) -//│ Def(6, foo, [], -//│ 1, -//│ let* (x$16) = mk(10) in -- #73 -//│ let x$17 = S(x$16) in -- #72 -//│ let x$18 = S(x$17) in -- #71 -//│ let* (x$19) = odd(x$18) in -- #70 -//│ x$19 -- #69 -//│ ) -//│ }, -//│ let* (x$20) = foo() in -- #77 -//│ x$20 -- #76) //│ -//│ Interpreted: -//│ False() +//│ IR Processing Failed: can not find the matched case, ClassInfo(0, True, []) expected :interpIR class True @@ -860,86 +511,8 @@ main() //│ |#class| |True|↵|#class| |False|↵|#class| |S|(|s|)|↵|#class| |O|↵|#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |foo|(||)| |#=| |odd|(|#if| |10| |>| |0| |#then| |S|(|O|)| |#else| |O|)|↵|#fun| |bar|(||)| |#=| |#if| |10| |>| |0| |#then| |odd|(|S|(|O|)|)| |#else| |odd|(|O|)|↵|#fun| |main|(||)| |#=|→|foo|(||)|↵|bar|(||)|←|↵|main|(||)| //│ Parsed: {class True {}; class False {}; class S(s,) {}; class O {}; fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun foo = () => odd(if (>(10,)(0,)) then S(O,) else O,); fun bar = () => if (>(10,)(0,)) then odd(S(O,),) else odd(O,); fun main = () => {foo(); bar()}; main()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { -//│ Def(0, odd, [x$0], -//│ 1, -//│ case x$0 of -- #15 -//│ O => -//│ let x$2 = False() in -- #4 -//│ jump j$0(x$2) -- #3 -//│ S => -//│ let x$3 = x$0.s in -- #14 -//│ let* (x$4) = even(x$3) in -- #13 -//│ jump j$0(x$4) -- #12 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, even, [x$5], -//│ 1, -//│ case x$5 of -- #31 -//│ O => -//│ let x$7 = True() in -- #20 -//│ jump j$1(x$7) -- #19 -//│ S => -//│ let x$8 = x$5.s in -- #30 -//│ let* (x$9) = odd(x$8) in -- #29 -//│ jump j$1(x$9) -- #28 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #17 -//│ ) -//│ Def(4, foo, [], -//│ 1, -//│ let x$10 = >(10,0) in -- #52 -//│ if x$10 -- #51 -//│ true => -//│ let x$13 = O() in -- #47 -//│ let x$14 = S(x$13) in -- #46 -//│ jump j$2(x$14) -- #45 -//│ false => -//│ let x$15 = O() in -- #50 -//│ jump j$2(x$15) -- #49 -//│ ) -//│ Def(5, j$2, [x$11], -//│ 1, -//│ let* (x$12) = odd(x$11) in -- #40 -//│ x$12 -- #39 -//│ ) -//│ Def(6, bar, [], -//│ 1, -//│ let x$16 = >(10,0) in -- #78 -//│ if x$16 -- #77 -//│ true => -//│ let x$18 = O() in -- #68 -//│ let x$19 = S(x$18) in -- #67 -//│ let* (x$20) = odd(x$19) in -- #66 -//│ jump j$3(x$20) -- #65 -//│ false => -//│ let x$21 = O() in -- #76 -//│ let* (x$22) = odd(x$21) in -- #75 -//│ jump j$3(x$22) -- #74 -//│ ) -//│ Def(7, j$3, [x$17], -//│ 1, -//│ x$17 -- #56 -//│ ) -//│ Def(8, main, [], -//│ 1, -//│ let* (x$23) = foo() in -- #86 -//│ let* (x$24) = bar() in -- #85 -//│ x$24 -- #84 -//│ ) -//│ }, -//│ let* (x$25) = main() in -- #90 -//│ x$25 -- #89) -//│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, S, [s]),ClassInfo(5, O, [])}, { //│ Def(0, odd, [x$0], //│ 1, //│ case x$0 of -- #15 @@ -1016,7 +589,8 @@ main() //│ x$25 -- #89) //│ //│ Interpreted: -//│ True() +//│ +//│ IR Processing Failed: can not find the matched case, ClassInfo(0, True, []) expected :interpIR class True @@ -1045,80 +619,8 @@ main(False) //│ |#class| |True|↵|#class| |False|↵|#class| |A|(||)|↵|#class| |B|(|b|)|↵|#fun| |aaa|(||)| |#=|→|#let| |m| |#=| |1|↵|#let| |n| |#=| |2|↵|#let| |p| |#=| |3|↵|#let| |q| |#=| |4|↵|m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |bbb|(||)| |#=|→|#let| |x| |#=| |aaa|(||)|↵|x| |*| |100| |+| |4|←|↵|#fun| |not|(|x|)| |#=|→|#if| |x| |#then| |False| |#else| |True|←|↵|#fun| |foo|(|x|)| |#=|→|#if| |x| |#then| |A| |#else| |B|(|foo|(|not|(|x|)|)|)|←|↵|#fun| |main|(|flag|)| |#=|→|#let| |x| |#=| |foo|(|flag|)|↵|#if| |x| |is|→|A| |#then| |aaa|(||)|↵|B|(|b1|)| |#then| |bbb|(||)|←|←|↵|main|(|False|)| //│ Parsed: {class True {}; class False {}; class A() {}; class B(b,) {}; fun aaa = () => {let m = 1; let n = 2; let p = 3; let q = 4; +(-(+(m,)(n,),)(p,),)(q,)}; fun bbb = () => {let x = aaa(); +(*(x,)(100,),)(4,)}; fun not = (x,) => {if (x) then False else True}; fun foo = (x,) => {if (x) then A else B(foo(not(x,),),)}; fun main = (flag,) => {let x = foo(flag,); if x is ‹(A) then aaa(); (B(b1,)) then bbb()›}; main(False,)} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { -//│ Def(0, aaa, [], -//│ 1, -//│ let x$0 = 1 in -- #17 -//│ let x$1 = 2 in -- #16 -//│ let x$2 = 3 in -- #15 -//│ let x$3 = 4 in -- #14 -//│ let x$4 = +(x$0,x$1) in -- #13 -//│ let x$5 = -(x$4,x$2) in -- #12 -//│ let x$6 = +(x$5,x$3) in -- #11 -//│ x$6 -- #10 -//│ ) -//│ Def(1, bbb, [], -//│ 1, -//│ let* (x$7) = aaa() in -- #28 -//│ let x$8 = *(x$7,100) in -- #27 -//│ let x$9 = +(x$8,4) in -- #26 -//│ x$9 -- #25 -//│ ) -//│ Def(2, not, [x$10], -//│ 1, -//│ if x$10 -- #37 -//│ true => -//│ let x$12 = False() in -- #33 -//│ jump j$0(x$12) -- #32 -//│ false => -//│ let x$13 = True() in -- #36 -//│ jump j$0(x$13) -- #35 -//│ ) -//│ Def(3, j$0, [x$11], -//│ 1, -//│ x$11 -- #30 -//│ ) -//│ Def(4, foo, [x$14], -//│ 1, -//│ if x$14 -- #59 -//│ true => -//│ let x$16 = A() in -- #42 -//│ jump j$1(x$16) -- #41 -//│ false => -//│ let* (x$17) = not(x$14) in -- #58 -//│ let* (x$18) = foo(x$17) in -- #57 -//│ let x$19 = B(x$18) in -- #56 -//│ jump j$1(x$19) -- #55 -//│ ) -//│ Def(5, j$1, [x$15], -//│ 1, -//│ x$15 -- #39 -//│ ) -//│ Def(6, main, [flag$0], -//│ 1, -//│ let* (x$20) = foo(flag$0) in -- #81 -//│ case x$20 of -- #80 -//│ A => -//│ let* (x$22) = aaa() in -- #71 -//│ jump j$2(x$22) -- #70 -//│ B => -//│ let x$23 = x$20.b in -- #79 -//│ let* (x$24) = bbb() in -- #78 -//│ jump j$2(x$24) -- #77 -//│ ) -//│ Def(7, j$2, [x$21], -//│ 1, -//│ x$21 -- #66 -//│ ) -//│ }, -//│ let x$25 = False() in -- #88 -//│ let* (x$26) = main(x$25) in -- #87 -//│ x$26 -- #86) -//│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, A, []),ClassInfo(5, B, [b])}, { //│ Def(0, aaa, [], //│ 1, //│ let x$0 = 1 in -- #17 @@ -1215,60 +717,8 @@ main() //│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#class| |Some|(|x|)|↵|#class| |None|↵|#fun| |head_opt|(|l|)| |#=|→|#if| |l| |is|→|Nil| |#then| |None|↵|Cons|(|h|,| |t|)| |#then| |Some|(|h|)|←|←|↵|#fun| |is_none|(|o|)| |#=|→|#if| |o| |is|→|None| |#then| |True|↵|Some|(|x|)| |#then| |False|←|←|↵|#fun| |is_empty|(|l|)| |#=|→|is_none|(|head_opt|(|l|)|)|←|↵|#fun| |main|(||)| |#=|→|is_empty|(|Cons|(|1|,| |Cons|(|2|,| |Nil|)|)|)|←|↵|main|(||)| //│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; class Some(x,) {}; class None {}; fun head_opt = (l,) => {if l is ‹(Nil) then None; (Cons(h, t,)) then Some(h,)›}; fun is_none = (o,) => {if o is ‹(None) then True; (Some(x,)) then False›}; fun is_empty = (l,) => {is_none(head_opt(l,),)}; fun main = () => {is_empty(Cons(1, Cons(2, Nil,),),)}; main()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { -//│ Def(0, head_opt, [l$0], -//│ 1, -//│ case l$0 of -- #17 -//│ Nil => -//│ let x$1 = None() in -- #4 -//│ jump j$0(x$1) -- #3 -//│ Cons => -//│ let x$2 = l$0.t in -- #16 -//│ let x$3 = l$0.h in -- #15 -//│ let x$4 = Some(x$3) in -- #14 -//│ jump j$0(x$4) -- #13 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, is_none, [o$0], -//│ 1, -//│ case o$0 of -- #29 -//│ None => -//│ let x$6 = True() in -- #22 -//│ jump j$1(x$6) -- #21 -//│ Some => -//│ let x$7 = o$0.x in -- #28 -//│ let x$8 = False() in -- #27 -//│ jump j$1(x$8) -- #26 -//│ ) -//│ Def(3, j$1, [x$5], -//│ 1, -//│ x$5 -- #19 -//│ ) -//│ Def(4, is_empty, [l$1], -//│ 1, -//│ let* (x$9) = head_opt(l$1) in -- #40 -//│ let* (x$10) = is_none(x$9) in -- #39 -//│ x$10 -- #38 -//│ ) -//│ Def(5, main, [], -//│ 1, -//│ let x$11 = Nil() in -- #59 -//│ let x$12 = Cons(2,x$11) in -- #58 -//│ let x$13 = Cons(1,x$12) in -- #57 -//│ let* (x$14) = is_empty(x$13) in -- #56 -//│ x$14 -- #55 -//│ ) -//│ }, -//│ let* (x$15) = main() in -- #63 -//│ x$15 -- #62) -//│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, Cons, [h,t]),ClassInfo(5, Nil, []),ClassInfo(6, Some, [x]),ClassInfo(7, None, [])}, { //│ Def(0, head_opt, [l$0], //│ 1, //│ case l$0 of -- #17 @@ -1338,83 +788,16 @@ fun is_none(o) = if o is None then True Some(x) then False -fun is_empty(l) = - is_none(head_opt(l)) -fun main() = - is_empty(mk_list(10)) -main() -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#class| |Some|(|x|)|↵|#class| |None|↵|#fun| |mk_list|(|n|)| |#=|→|#if| |n| |==| |0| |#then| |Nil| |#else| |Cons|(|n|,| |mk_list|(|n| |-| |1|)|)|←|↵|#fun| |head_opt|(|l|)| |#=|→|#if| |l| |is|→|Nil| |#then| |None|↵|Cons|(|h|,| |t|)| |#then| |Some|(|h|)|←|←|↵|#fun| |is_none|(|o|)| |#=|→|#if| |o| |is|→|None| |#then| |True|↵|Some|(|x|)| |#then| |False|←|←|↵|#fun| |is_empty|(|l|)| |#=|→|is_none|(|head_opt|(|l|)|)|←|↵|#fun| |main|(||)| |#=|→|is_empty|(|mk_list|(|10|)|)|←|↵|main|(||)| -//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; class Some(x,) {}; class None {}; fun mk_list = (n,) => {if (==(n,)(0,)) then Nil else Cons(n, mk_list(-(n,)(1,),),)}; fun head_opt = (l,) => {if l is ‹(Nil) then None; (Cons(h, t,)) then Some(h,)›}; fun is_none = (o,) => {if o is ‹(None) then True; (Some(x,)) then False›}; fun is_empty = (l,) => {is_none(head_opt(l,),)}; fun main = () => {is_empty(mk_list(10,),)}; main()} -//│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { -//│ Def(0, mk_list, [n$0], -//│ 1, -//│ let x$0 = ==(n$0,0) in -- #24 -//│ if x$0 -- #23 -//│ true => -//│ let x$2 = Nil() in -- #6 -//│ jump j$0(x$2) -- #5 -//│ false => -//│ let x$3 = -(n$0,1) in -- #22 -//│ let* (x$4) = mk_list(x$3) in -- #21 -//│ let x$5 = Cons(n$0,x$4) in -- #20 -//│ jump j$0(x$5) -- #19 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ Def(2, head_opt, [l$0], -//│ 1, -//│ case l$0 of -- #42 -//│ Nil => -//│ let x$7 = None() in -- #29 -//│ jump j$1(x$7) -- #28 -//│ Cons => -//│ let x$8 = l$0.t in -- #41 -//│ let x$9 = l$0.h in -- #40 -//│ let x$10 = Some(x$9) in -- #39 -//│ jump j$1(x$10) -- #38 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #26 -//│ ) -//│ Def(4, is_none, [o$0], -//│ 1, -//│ case o$0 of -- #54 -//│ None => -//│ let x$12 = True() in -- #47 -//│ jump j$2(x$12) -- #46 -//│ Some => -//│ let x$13 = o$0.x in -- #53 -//│ let x$14 = False() in -- #52 -//│ jump j$2(x$14) -- #51 -//│ ) -//│ Def(5, j$2, [x$11], -//│ 1, -//│ x$11 -- #44 -//│ ) -//│ Def(6, is_empty, [l$1], -//│ 1, -//│ let* (x$15) = head_opt(l$1) in -- #65 -//│ let* (x$16) = is_none(x$15) in -- #64 -//│ x$16 -- #63 -//│ ) -//│ Def(7, main, [], -//│ 1, -//│ let* (x$17) = mk_list(10) in -- #76 -//│ let* (x$18) = is_empty(x$17) in -- #75 -//│ x$18 -- #74 -//│ ) -//│ }, -//│ let* (x$19) = main() in -- #80 -//│ x$19 -- #79) +fun is_empty(l) = + is_none(head_opt(l)) +fun main() = + is_empty(mk_list(10)) +main() +//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#class| |Some|(|x|)|↵|#class| |None|↵|#fun| |mk_list|(|n|)| |#=|→|#if| |n| |==| |0| |#then| |Nil| |#else| |Cons|(|n|,| |mk_list|(|n| |-| |1|)|)|←|↵|#fun| |head_opt|(|l|)| |#=|→|#if| |l| |is|→|Nil| |#then| |None|↵|Cons|(|h|,| |t|)| |#then| |Some|(|h|)|←|←|↵|#fun| |is_none|(|o|)| |#=|→|#if| |o| |is|→|None| |#then| |True|↵|Some|(|x|)| |#then| |False|←|←|↵|#fun| |is_empty|(|l|)| |#=|→|is_none|(|head_opt|(|l|)|)|←|↵|#fun| |main|(||)| |#=|→|is_empty|(|mk_list|(|10|)|)|←|↵|main|(||)| +//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; class Some(x,) {}; class None {}; fun mk_list = (n,) => {if (==(n,)(0,)) then Nil else Cons(n, mk_list(-(n,)(1,),),)}; fun head_opt = (l,) => {if l is ‹(Nil) then None; (Cons(h, t,)) then Some(h,)›}; fun is_none = (o,) => {if o is ‹(None) then True; (Some(x,)) then False›}; fun is_empty = (l,) => {is_none(head_opt(l,),)}; fun main = () => {is_empty(mk_list(10,),)}; main()} //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, Cons, [h,t]),ClassInfo(5, Nil, []),ClassInfo(6, Some, [x]),ClassInfo(7, None, [])}, { //│ Def(0, mk_list, [n$0], //│ 1, //│ let x$0 = ==(n$0,0) in -- #24 @@ -1480,7 +863,8 @@ main() //│ x$19 -- #79) //│ //│ Interpreted: -//│ False() +//│ +//│ IR Processing Failed: can not find the matched case, ClassInfo(1, False, []) expected :interpIR class True @@ -1504,65 +888,8 @@ main() //│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#class| |Some|(|x|)|↵|#class| |None|↵|#fun| |mk_list|(|n|)| |#=|→|#if| |n| |==| |0| |#then| |Nil| |#else| |Cons|(|n|,| |mk_list|(|n| |-| |1|)|)|←|↵|#fun| |last_opt|(|l|)| |#=|→|#if| |l| |is|→|Nil| |#then| |None|↵|Cons|(|h|,| |t|)| |#then|→|#if| |t| |is|→|Nil| |#then| |Some|(|h|)|↵|Cons|(|h2|,| |t2|)| |#then| |last_opt|(|t|)|←|←|←|←|↵|#fun| |main|(||)| |#=|→|last_opt|(|mk_list|(|10|)|)|←|↵|main|(||)| //│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; class Some(x,) {}; class None {}; fun mk_list = (n,) => {if (==(n,)(0,)) then Nil else Cons(n, mk_list(-(n,)(1,),),)}; fun last_opt = (l,) => {if l is ‹(Nil) then None; (Cons(h, t,)) then {if t is ‹(Nil) then Some(h,); (Cons(h2, t2,)) then last_opt(t,)›}›}; fun main = () => {last_opt(mk_list(10,),)}; main()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { -//│ Def(0, mk_list, [n$0], -//│ 1, -//│ let x$0 = ==(n$0,0) in -- #24 -//│ if x$0 -- #23 -//│ true => -//│ let x$2 = Nil() in -- #6 -//│ jump j$0(x$2) -- #5 -//│ false => -//│ let x$3 = -(n$0,1) in -- #22 -//│ let* (x$4) = mk_list(x$3) in -- #21 -//│ let x$5 = Cons(n$0,x$4) in -- #20 -//│ jump j$0(x$5) -- #19 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ Def(2, last_opt, [l$0], -//│ 1, -//│ case l$0 of -- #59 -//│ Nil => -//│ let x$7 = None() in -- #29 -//│ jump j$1(x$7) -- #28 -//│ Cons => -//│ let x$8 = l$0.t in -- #58 -//│ let x$9 = l$0.h in -- #57 -//│ case x$8 of -- #56 -//│ Nil => -//│ let x$11 = Some(x$9) in -- #42 -//│ jump j$2(x$11) -- #41 -//│ Cons => -//│ let x$12 = x$8.t in -- #55 -//│ let x$13 = x$8.h in -- #54 -//│ let* (x$14) = last_opt(x$8) in -- #53 -//│ jump j$2(x$14) -- #52 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #26 -//│ ) -//│ Def(4, j$2, [x$10], -//│ 1, -//│ jump j$1(x$10) -- #36 -//│ ) -//│ Def(5, main, [], -//│ 1, -//│ let* (x$15) = mk_list(10) in -- #70 -//│ let* (x$16) = last_opt(x$15) in -- #69 -//│ x$16 -- #68 -//│ ) -//│ }, -//│ let* (x$17) = main() in -- #74 -//│ x$17 -- #73) -//│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, Cons, [h,t]),ClassInfo(5, Nil, []),ClassInfo(6, Some, [x]),ClassInfo(7, None, [])}, { //│ Def(0, mk_list, [n$0], //│ 1, //│ let x$0 = ==(n$0,0) in -- #24 @@ -1618,7 +945,8 @@ main() //│ x$17 -- #73) //│ //│ Interpreted: -//│ Some(1) +//│ +//│ IR Processing Failed: can not find the matched case, ClassInfo(1, False, []) expected :interpIR class True @@ -1656,101 +984,8 @@ main() //│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#class| |Some|(|x|)|↵|#class| |None|↵|#fun| |is_some|(|o|)| |#=|→|#if| |o| |is|→|Some|(|x|)| |#then| |True|↵|None| |#then| |False|←|←|↵|#fun| |e0|(|w|)| |#=|→|w| |+| |8| |+| |9| |+| |10|←|↵|#fun| |e1|(|a|,| |c|)| |#=|→|a| |+| |1| |+| |2| |+| |3| |+| |4|←|↵|#fun| |e3|(|c|)| |#=|→|#let| |m| |#=| |4|↵|#let| |n| |#=| |5|↵|#let| |p| |#=| |6|↵|#let| |q| |#=| |7|↵|#if| |c| |#then| |m| |+| |n| |+| |p| |+| |q| |#else| |m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |e2|(|x|)| |#=|→|x| |+| |12| |+| |13| |+| |14|←|↵|#fun| |f|(|x|)| |#=|→|#let| |c1| |#=| |is_some|(|x|)|↵|#let| |z| |#=| |e3|(|c1|)|↵|#let| |w| |#=| |#if| |x| |is|→|Some|(|a|)| |#then| |e1|(|a|,| |z|)|↵|None| |#then| |e2|(|z|)|←|↵|e0|(|w|)|←|↵|#fun| |main|(||)| |#=|→|f|(|Some|(|2|)|)| |+| |f|(|None|)|←|↵|main|(||)| //│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; class Some(x,) {}; class None {}; fun is_some = (o,) => {if o is ‹(Some(x,)) then True; (None) then False›}; fun e0 = (w,) => {+(+(+(w,)(8,),)(9,),)(10,)}; fun e1 = (a, c,) => {+(+(+(+(a,)(1,),)(2,),)(3,),)(4,)}; fun e3 = (c,) => {let m = 4; let n = 5; let p = 6; let q = 7; if (c) then +(+(+(m,)(n,),)(p,),)(q,) else +(-(+(m,)(n,),)(p,),)(q,)}; fun e2 = (x,) => {+(+(+(x,)(12,),)(13,),)(14,)}; fun f = (x,) => {let c1 = is_some(x,); let z = e3(c1,); let w = if x is ‹(Some(a,)) then e1(a, z,); (None) then e2(z,)›; e0(w,)}; fun main = () => {+(f(Some(2,),),)(f(None,),)}; main()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { -//│ Def(0, is_some, [o$0], -//│ 1, -//│ case o$0 of -- #11 -//│ Some => -//│ let x$1 = o$0.x in -- #7 -//│ let x$2 = True() in -- #6 -//│ jump j$0(x$2) -- #5 -//│ None => -//│ let x$3 = False() in -- #10 -//│ jump j$0(x$3) -- #9 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, e0, [w$0], -//│ 1, -//│ let x$4 = +(w$0,8) in -- #21 -//│ let x$5 = +(x$4,9) in -- #20 -//│ let x$6 = +(x$5,10) in -- #19 -//│ x$6 -- #18 -//│ ) -//│ Def(3, e1, [a$0,c$0], -//│ 1, -//│ let x$7 = +(a$0,1) in -- #34 -//│ let x$8 = +(x$7,2) in -- #33 -//│ let x$9 = +(x$8,3) in -- #32 -//│ let x$10 = +(x$9,4) in -- #31 -//│ x$10 -- #30 -//│ ) -//│ Def(4, e3, [c$1], -//│ 1, -//│ let x$11 = 4 in -- #67 -//│ let x$12 = 5 in -- #66 -//│ let x$13 = 6 in -- #65 -//│ let x$14 = 7 in -- #64 -//│ if c$1 -- #63 -//│ true => -//│ let x$16 = +(x$11,x$12) in -- #51 -//│ let x$17 = +(x$16,x$13) in -- #50 -//│ let x$18 = +(x$17,x$14) in -- #49 -//│ jump j$1(x$18) -- #48 -//│ false => -//│ let x$19 = +(x$11,x$12) in -- #62 -//│ let x$20 = -(x$19,x$13) in -- #61 -//│ let x$21 = +(x$20,x$14) in -- #60 -//│ jump j$1(x$21) -- #59 -//│ ) -//│ Def(5, j$1, [x$15], -//│ 1, -//│ x$15 -- #40 -//│ ) -//│ Def(6, e2, [x$22], -//│ 1, -//│ let x$23 = +(x$22,12) in -- #77 -//│ let x$24 = +(x$23,13) in -- #76 -//│ let x$25 = +(x$24,14) in -- #75 -//│ x$25 -- #74 -//│ ) -//│ Def(7, f, [x$26], -//│ 1, -//│ let* (x$27) = is_some(x$26) in -- #117 -//│ let* (x$28) = e3(x$27) in -- #116 -//│ case x$26 of -- #115 -//│ Some => -//│ let x$31 = x$26.x in -- #107 -//│ let* (x$32) = e1(x$31,x$28) in -- #106 -//│ jump j$2(x$32) -- #105 -//│ None => -//│ let* (x$33) = e2(x$28) in -- #114 -//│ jump j$2(x$33) -- #113 -//│ ) -//│ Def(8, j$2, [x$29], -//│ 1, -//│ let* (x$30) = e0(x$29) in -- #95 -//│ x$30 -- #94 -//│ ) -//│ Def(9, main, [], -//│ 1, -//│ let x$34 = Some(2) in -- #136 -//│ let* (x$35) = f(x$34) in -- #135 -//│ let x$36 = None() in -- #134 -//│ let* (x$37) = f(x$36) in -- #133 -//│ let x$38 = +(x$35,x$37) in -- #132 -//│ x$38 -- #131 -//│ ) -//│ }, -//│ let* (x$39) = main() in -- #140 -//│ x$39 -- #139) -//│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, Cons, [h,t]),ClassInfo(5, Nil, []),ClassInfo(6, Some, [x]),ClassInfo(7, None, [])}, { //│ Def(0, is_some, [o$0], //│ 1, //│ case o$0 of -- #11 @@ -1880,109 +1115,8 @@ main() //│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#class| |Some|(|x|)|↵|#class| |None|↵|#fun| |is_some|(|o|)| |#=|→|#if| |o| |is|→|Some|(|x|)| |#then| |True|↵|None| |#then| |False|←|←|↵|#fun| |e0|(|w|)| |#=|→|w| |+| |8| |+| |9| |+| |10|←|↵|#fun| |e1|(|a|,| |z|)| |#=|→|#if| |a| |>| |0| |#then| |f|(|Some|(|a| |-| |1|)|)| |#else| |z|←|↵|#fun| |e3|(|c|)| |#=|→|#let| |m| |#=| |4|↵|#let| |n| |#=| |5|↵|#let| |p| |#=| |6|↵|#let| |q| |#=| |7|↵|#if| |c| |#then| |m| |+| |n| |+| |p| |+| |q| |#else| |m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |e2|(|x|)| |#=|→|x| |+| |12| |+| |13| |+| |14|←|↵|#fun| |f|(|x|)| |#=|→|#let| |c1| |#=| |is_some|(|x|)|↵|#let| |z| |#=| |e3|(|c1|)|↵|#let| |w| |#=| |#if| |x| |is|→|Some|(|a|)| |#then| |e1|(|a|,| |z|)|↵|None| |#then| |e2|(|z|)|←|↵|e0|(|w|)|←|↵|#fun| |main|(||)| |#=|→|f|(|Some|(|2|)|)| |+| |f|(|None|)|←|↵|main|(||)| //│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; class Some(x,) {}; class None {}; fun is_some = (o,) => {if o is ‹(Some(x,)) then True; (None) then False›}; fun e0 = (w,) => {+(+(+(w,)(8,),)(9,),)(10,)}; fun e1 = (a, z,) => {if (>(a,)(0,)) then f(Some(-(a,)(1,),),) else z}; fun e3 = (c,) => {let m = 4; let n = 5; let p = 6; let q = 7; if (c) then +(+(+(m,)(n,),)(p,),)(q,) else +(-(+(m,)(n,),)(p,),)(q,)}; fun e2 = (x,) => {+(+(+(x,)(12,),)(13,),)(14,)}; fun f = (x,) => {let c1 = is_some(x,); let z = e3(c1,); let w = if x is ‹(Some(a,)) then e1(a, z,); (None) then e2(z,)›; e0(w,)}; fun main = () => {+(f(Some(2,),),)(f(None,),)}; main()} //│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { -//│ Def(0, is_some, [o$0], -//│ 1, -//│ case o$0 of -- #11 -//│ Some => -//│ let x$1 = o$0.x in -- #7 -//│ let x$2 = True() in -- #6 -//│ jump j$0(x$2) -- #5 -//│ None => -//│ let x$3 = False() in -- #10 -//│ jump j$0(x$3) -- #9 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, e0, [w$0], -//│ 1, -//│ let x$4 = +(w$0,8) in -- #21 -//│ let x$5 = +(x$4,9) in -- #20 -//│ let x$6 = +(x$5,10) in -- #19 -//│ x$6 -- #18 -//│ ) -//│ Def(3, e1, [a$0,z$0], -//│ 1, -//│ let x$7 = >(a$0,0) in -- #43 -//│ if x$7 -- #42 -//│ true => -//│ let x$9 = -(a$0,1) in -- #39 -//│ let x$10 = Some(x$9) in -- #38 -//│ let* (x$11) = f(x$10) in -- #37 -//│ jump j$1(x$11) -- #36 -//│ false => -//│ jump j$1(z$0) -- #41 -//│ ) -//│ Def(4, j$1, [x$8], -//│ 1, -//│ x$8 -- #25 -//│ ) -//│ Def(5, e3, [c$0], -//│ 1, -//│ let x$12 = 4 in -- #76 -//│ let x$13 = 5 in -- #75 -//│ let x$14 = 6 in -- #74 -//│ let x$15 = 7 in -- #73 -//│ if c$0 -- #72 -//│ true => -//│ let x$17 = +(x$12,x$13) in -- #60 -//│ let x$18 = +(x$17,x$14) in -- #59 -//│ let x$19 = +(x$18,x$15) in -- #58 -//│ jump j$2(x$19) -- #57 -//│ false => -//│ let x$20 = +(x$12,x$13) in -- #71 -//│ let x$21 = -(x$20,x$14) in -- #70 -//│ let x$22 = +(x$21,x$15) in -- #69 -//│ jump j$2(x$22) -- #68 -//│ ) -//│ Def(6, j$2, [x$16], -//│ 1, -//│ x$16 -- #49 -//│ ) -//│ Def(7, e2, [x$23], -//│ 1, -//│ let x$24 = +(x$23,12) in -- #86 -//│ let x$25 = +(x$24,13) in -- #85 -//│ let x$26 = +(x$25,14) in -- #84 -//│ x$26 -- #83 -//│ ) -//│ Def(8, f, [x$27], -//│ 1, -//│ let* (x$28) = is_some(x$27) in -- #126 -//│ let* (x$29) = e3(x$28) in -- #125 -//│ case x$27 of -- #124 -//│ Some => -//│ let x$32 = x$27.x in -- #116 -//│ let* (x$33) = e1(x$32,x$29) in -- #115 -//│ jump j$3(x$33) -- #114 -//│ None => -//│ let* (x$34) = e2(x$29) in -- #123 -//│ jump j$3(x$34) -- #122 -//│ ) -//│ Def(9, j$3, [x$30], -//│ 1, -//│ let* (x$31) = e0(x$30) in -- #104 -//│ x$31 -- #103 -//│ ) -//│ Def(10, main, [], -//│ 1, -//│ let x$35 = Some(2) in -- #145 -//│ let* (x$36) = f(x$35) in -- #144 -//│ let x$37 = None() in -- #143 -//│ let* (x$38) = f(x$37) in -- #142 -//│ let x$39 = +(x$36,x$38) in -- #141 -//│ x$39 -- #140 -//│ ) -//│ }, -//│ let* (x$40) = main() in -- #149 -//│ x$40 -- #148) -//│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, Cons, [h,t]),ClassInfo(5, Nil, []),ClassInfo(6, Some, [x]),ClassInfo(7, None, [])}, { //│ Def(0, is_some, [o$0], //│ 1, //│ case o$0 of -- #11 @@ -2082,4 +1216,5 @@ main() //│ x$40 -- #148) //│ //│ Interpreted: -//│ 179 +//│ +//│ IR Processing Failed: can not find the matched case, ClassInfo(0, True, []) expected diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls new file mode 100644 index 00000000..de308508 --- /dev/null +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -0,0 +1,2669 @@ +:NewParser +:ParseOnly +:UseIR + +:noTailRec +:interpIR +fun fact(acc, n) = + if n == 0 then acc + else fact(acc * n, n - 1) +fact(1, 5) +//│ |#fun| |fact|(|acc|,| |n|)| |#=|→|#if| |n| |==| |0| |#then| |acc|↵|#else| |fact|(|acc| |*| |n|,| |n| |-| |1|)|←|↵|fact|(|1|,| |5|)| +//│ Parsed: {fun fact = (acc, n,) => {if (==(n,)(0,)) then acc else fact(*(acc,)(n,), -(n,)(1,),)}; fact(1, 5,)} +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, fact, [acc$0,n$0], +//│ 1, +//│ let x$0 = ==(n$0,0) in -- #22 +//│ if x$0 -- #21 +//│ true => +//│ jump j$0(acc$0) -- #5 +//│ false => +//│ let x$2 = *(acc$0,n$0) in -- #20 +//│ let x$3 = -(n$0,1) in -- #19 +//│ let* (x$4) = fact(x$2,x$3) in -- #18 +//│ jump j$0(x$4) -- #17 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #3 +//│ ) +//│ }, +//│ let* (x$5) = fact(1,5) in -- #30 +//│ x$5 -- #29) +//│ +//│ Interpreted: +//│ 120 + +:interpIR +@tailrec +fun fact(acc, n) = + if n == 0 then acc + else fact(acc * n, n - 1) +fact(1, 5) +//│ |@|tailrec|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#if| |n| |==| |0| |#then| |acc|↵|#else| |fact|(|acc| |*| |n|,| |n| |-| |1|)|←|↵|fact|(|1|,| |5|)| +//│ Parsed: {fun fact = (acc, n,) => {if (==(n,)(0,)) then acc else fact(*(acc,)(n,), -(n,)(1,),)}; fact(1, 5,)} +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, fact, [acc$0,n$0], +//│ 1, +//│ let x$0 = ==(n$0,0) in -- #22 +//│ if x$0 -- #21 +//│ true => +//│ jump j$0(acc$0) -- #5 +//│ false => +//│ let x$2 = *(acc$0,n$0) in -- #20 +//│ let x$3 = -(n$0,1) in -- #19 +//│ let* (x$4) = fact(x$2,x$3) in -- #18 +//│ jump j$0(x$4) -- #17 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #3 +//│ ) +//│ }, +//│ let* (x$5) = fact(1,5) in -- #30 +//│ x$5 -- #29) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$0), Set(fact)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #3 +//│ ) +//│ Def(2, fact_jp, [acc$0,n$0], +//│ 1, +//│ let x$0 = ==(n$0,0) in -- #36 +//│ if x$0 -- #35 +//│ true => +//│ jump j$0(acc$0) -- #31 +//│ false => +//│ let x$2 = *(acc$0,n$0) in -- #34 +//│ let x$3 = -(n$0,1) in -- #33 +//│ jump fact_jp(x$2,x$3) -- #32 +//│ ) +//│ Def(3, fact, [acc$0,n$0], +//│ 1, +//│ let* (r0) = fact_jp(acc$0,n$0) in -- #38 +//│ r0 -- #37 +//│ ) +//│ }, +//│ let* (x$5) = fact(1,5) in -- #30 +//│ x$5 -- #29) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #3 +//│ ) +//│ Def(2, fact_jp, [acc$0,n$0], +//│ 1, +//│ let x$0 = ==(n$0,0) in -- #36 +//│ if x$0 -- #35 +//│ true => +//│ jump j$0(acc$0) -- #31 +//│ false => +//│ let x$2 = *(acc$0,n$0) in -- #34 +//│ let x$3 = -(n$0,1) in -- #33 +//│ jump fact_jp(x$2,x$3) -- #32 +//│ ) +//│ Def(3, fact, [acc$0,n$0], +//│ 1, +//│ let* (r0) = fact_jp(acc$0,n$0) in -- #38 +//│ r0 -- #37 +//│ ) +//│ }, +//│ let* (x$5) = fact(1,5) in -- #30 +//│ x$5 -- #29) +//│ +//│ Interpreted: +//│ 120 + +:interpIR +@tailrec +fun fact(acc, n) = + val x = if n > 0 then n - 1 + else 0 + if x <= 0 then + acc + else + @tailcall fact(n * acc, x) +fact(1, 5) +//│ |@|tailrec|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#val| |x| |#=| |#if| |n| |>| |0| |#then| |n| |-| |1|→|#else| |0|←|↵|#if| |x| |<=| |0| |#then|→|acc|←|↵|#else| |→|@|tailcall| |fact|(|n| |*| |acc|,| |x|)| |←|←|↵|fact|(|1|,| |5|)| +//│ Parsed: {fun fact = (acc, n,) => {let x = if (>(n,)(0,)) then -(n,)(1,) else 0; if (<=(x,)(0,)) then {acc} else {@tailcall fact(*(n,)(acc,), x,)}}; fact(1, 5,)} +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, fact, [acc$0,n$0], +//│ 1, +//│ let x$0 = >(n$0,0) in -- #32 +//│ if x$0 -- #31 +//│ true => +//│ let x$6 = -(n$0,1) in -- #28 +//│ jump j$0(x$6,acc$0,n$0) -- #27 +//│ false => +//│ jump j$0(0,acc$0,n$0) -- #30 +//│ ) +//│ Def(1, j$1, [x$3], +//│ 1, +//│ x$3 -- #7 +//│ ) +//│ Def(2, j$0, [x$1,acc$0,n$0], +//│ 1, +//│ let x$2 = <=(x$1,0) in -- #23 +//│ if x$2 -- #22 +//│ true => +//│ jump j$1(acc$0) -- #9 +//│ false => +//│ let x$4 = *(n$0,acc$0) in -- #21 +//│ let* (x$5) = @tailcall fact(x$4,x$1) in -- #20 +//│ jump j$1(x$5) -- #19 +//│ ) +//│ }, +//│ let* (x$7) = fact(1,5) in -- #40 +//│ x$7 -- #39) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$1), Set(fact)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, fact, [acc$0,n$0], +//│ 1, +//│ let* (r0) = _fact_j$0_opt$3(0,acc$0,n$0,undefined,undefined,undefined) in -- #60 +//│ r0 -- #59 +//│ ) +//│ Def(1, j$1, [x$3], +//│ 1, +//│ x$3 -- #7 +//│ ) +//│ Def(3, _fact_j$0_opt$3, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], +//│ 1, +//│ jump _fact_j$0_opt_jp$4(tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0) -- #58 +//│ ) +//│ Def(4, _fact_j$0_opt_jp$4, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], +//│ 1, +//│ let scrut = ==(2,tailrecBranch$) in -- #57 +//│ if scrut -- #56 +//│ true => +//│ let x$2 = <=(j$0_x$1,0) in -- #55 +//│ if x$2 -- #54 +//│ true => +//│ jump j$1(j$0_acc$0) -- #51 +//│ false => +//│ let x$4 = *(j$0_n$0,j$0_acc$0) in -- #53 +//│ jump _fact_j$0_opt_jp$4(0,x$4,j$0_x$1,j$0_x$1,j$0_acc$0,j$0_n$0) -- #52 +//│ false => +//│ let x$0 = >(fact_n$0,0) in -- #50 +//│ if x$0 -- #49 +//│ true => +//│ let x$6 = -(fact_n$0,1) in -- #47 +//│ jump _fact_j$0_opt_jp$4(2,fact_acc$0,fact_n$0,x$6,fact_acc$0,fact_n$0) -- #46 +//│ false => +//│ jump _fact_j$0_opt_jp$4(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #48 +//│ ) +//│ }, +//│ let* (x$7) = fact(1,5) in -- #40 +//│ x$7 -- #39) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, fact, [acc$0,n$0], +//│ 1, +//│ let* (r0) = _fact_j$0_opt$3(0,acc$0,n$0,undefined,undefined,undefined) in -- #60 +//│ r0 -- #59 +//│ ) +//│ Def(1, j$1, [x$3], +//│ 1, +//│ x$3 -- #7 +//│ ) +//│ Def(3, _fact_j$0_opt$3, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], +//│ 1, +//│ jump _fact_j$0_opt_jp$4(tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0) -- #58 +//│ ) +//│ Def(4, _fact_j$0_opt_jp$4, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], +//│ 1, +//│ let scrut = ==(2,tailrecBranch$) in -- #57 +//│ if scrut -- #56 +//│ true => +//│ let x$2 = <=(j$0_x$1,0) in -- #55 +//│ if x$2 -- #54 +//│ true => +//│ jump j$1(j$0_acc$0) -- #51 +//│ false => +//│ let x$4 = *(j$0_n$0,j$0_acc$0) in -- #53 +//│ jump _fact_j$0_opt_jp$4(0,x$4,j$0_x$1,j$0_x$1,j$0_acc$0,j$0_n$0) -- #52 +//│ false => +//│ let x$0 = >(fact_n$0,0) in -- #50 +//│ if x$0 -- #49 +//│ true => +//│ let x$6 = -(fact_n$0,1) in -- #47 +//│ jump _fact_j$0_opt_jp$4(2,fact_acc$0,fact_n$0,x$6,fact_acc$0,fact_n$0) -- #46 +//│ false => +//│ jump _fact_j$0_opt_jp$4(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #48 +//│ ) +//│ }, +//│ let* (x$7) = fact(1,5) in -- #40 +//│ x$7 -- #39) +//│ +//│ Interpreted: +//│ 120 + +:noTailRec +:interpIR +fun double(x) = x * 2 +fun f(n, acc) = if n == 0 then double(acc) else g(n - 1, acc + 1) +fun g(m, acc) = if m == 0 then -double(acc) else f(m - 1, acc + 1) +g(6, 0) +//│ |#fun| |double|(|x|)| |#=| |x| |*| |2|↵|#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|6|,| |0|)| +//│ Parsed: {fun double = (x,) => *(x,)(2,); fun f = (n, acc,) => if (==(n,)(0,)) then double(acc,) else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(double(acc,),) else f(-(m,)(1,), +(acc,)(1,),); g(6, 0,)} +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, double, [x$0], +//│ 1, +//│ let x$1 = *(x$0,2) in -- #3 +//│ x$1 -- #2 +//│ ) +//│ Def(1, f, [n$0,acc$0], +//│ 1, +//│ let x$2 = ==(n$0,0) in -- #31 +//│ if x$2 -- #30 +//│ true => +//│ let* (x$4) = double(acc$0) in -- #14 +//│ jump j$0(x$4) -- #13 +//│ false => +//│ let x$5 = -(n$0,1) in -- #29 +//│ let x$6 = +(acc$0,1) in -- #28 +//│ let* (x$7) = g(x$5,x$6) in -- #27 +//│ jump j$0(x$7) -- #26 +//│ ) +//│ Def(2, j$0, [x$3], +//│ 1, +//│ x$3 -- #7 +//│ ) +//│ Def(3, g, [m$0,acc$1], +//│ 1, +//│ let x$8 = ==(m$0,0) in -- #62 +//│ if x$8 -- #61 +//│ true => +//│ let* (x$10) = double(acc$1) in -- #45 +//│ let x$11 = -(0,x$10) in -- #44 +//│ jump j$1(x$11) -- #43 +//│ false => +//│ let x$12 = -(m$0,1) in -- #60 +//│ let x$13 = +(acc$1,1) in -- #59 +//│ let* (x$14) = f(x$12,x$13) in -- #58 +//│ jump j$1(x$14) -- #57 +//│ ) +//│ Def(4, j$1, [x$9], +//│ 1, +//│ x$9 -- #35 +//│ ) +//│ }, +//│ let* (x$15) = g(6,0) in -- #70 +//│ x$15 -- #69) +//│ +//│ Interpreted: +//│ -12 + +:interpIR +fun double(x) = x * 2 +@tailrec fun f(n, acc) = if n == 0 then double(acc) else g(n - 1, acc + 1) +@tailrec fun g(m, acc) = if m == 0 then -double(acc) else f(m - 1, acc + 1) +g(6, 0) +//│ |#fun| |double|(|x|)| |#=| |x| |*| |2|↵|@|tailrec| |#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|@|tailrec| |#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|6|,| |0|)| +//│ Parsed: {fun double = (x,) => *(x,)(2,); fun f = (n, acc,) => if (==(n,)(0,)) then double(acc,) else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(double(acc,),) else f(-(m,)(1,), +(acc,)(1,),); g(6, 0,)} +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, double, [x$0], +//│ 1, +//│ let x$1 = *(x$0,2) in -- #3 +//│ x$1 -- #2 +//│ ) +//│ Def(1, f, [n$0,acc$0], +//│ 1, +//│ let x$2 = ==(n$0,0) in -- #31 +//│ if x$2 -- #30 +//│ true => +//│ let* (x$4) = double(acc$0) in -- #14 +//│ jump j$0(x$4) -- #13 +//│ false => +//│ let x$5 = -(n$0,1) in -- #29 +//│ let x$6 = +(acc$0,1) in -- #28 +//│ let* (x$7) = g(x$5,x$6) in -- #27 +//│ jump j$0(x$7) -- #26 +//│ ) +//│ Def(2, j$0, [x$3], +//│ 1, +//│ x$3 -- #7 +//│ ) +//│ Def(3, g, [m$0,acc$1], +//│ 1, +//│ let x$8 = ==(m$0,0) in -- #62 +//│ if x$8 -- #61 +//│ true => +//│ let* (x$10) = double(acc$1) in -- #45 +//│ let x$11 = -(0,x$10) in -- #44 +//│ jump j$1(x$11) -- #43 +//│ false => +//│ let x$12 = -(m$0,1) in -- #60 +//│ let x$13 = +(acc$1,1) in -- #59 +//│ let* (x$14) = f(x$12,x$13) in -- #58 +//│ jump j$1(x$14) -- #57 +//│ ) +//│ Def(4, j$1, [x$9], +//│ 1, +//│ x$9 -- #35 +//│ ) +//│ }, +//│ let* (x$15) = g(6,0) in -- #70 +//│ x$15 -- #69) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$1), Set(j$0), Set(g, f), Set(double)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, double, [x$0], +//│ 1, +//│ let x$1 = *(x$0,2) in -- #3 +//│ x$1 -- #2 +//│ ) +//│ Def(1, f, [n$0,acc$0], +//│ 1, +//│ let* (r0) = _g_f_opt$5(1,undefined,undefined,n$0,acc$0) in -- #100 +//│ r0 -- #99 +//│ ) +//│ Def(2, j$0, [x$3], +//│ 1, +//│ x$3 -- #7 +//│ ) +//│ Def(3, g, [m$0,acc$1], +//│ 1, +//│ let* (r0) = _g_f_opt$5(3,m$0,acc$1,undefined,undefined) in -- #98 +//│ r0 -- #97 +//│ ) +//│ Def(4, j$1, [x$9], +//│ 1, +//│ x$9 -- #35 +//│ ) +//│ Def(5, _g_f_opt$5, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ 1, +//│ jump _g_f_opt_jp$6(tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #96 +//│ ) +//│ Def(6, _g_f_opt_jp$6, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ 1, +//│ let scrut = ==(1,tailrecBranch$) in -- #95 +//│ if scrut -- #94 +//│ true => +//│ let x$2 = ==(f_n$0,0) in -- #93 +//│ if x$2 -- #92 +//│ true => +//│ let* (x$4) = double(f_acc$0) in -- #88 +//│ jump j$0(x$4) -- #87 +//│ false => +//│ let x$5 = -(f_n$0,1) in -- #91 +//│ let x$6 = +(f_acc$0,1) in -- #90 +//│ jump _g_f_opt_jp$6(3,x$5,x$6,f_n$0,f_acc$0) -- #89 +//│ false => +//│ let x$8 = ==(g_m$0,0) in -- #86 +//│ if x$8 -- #85 +//│ true => +//│ let* (x$10) = double(g_acc$1) in -- #81 +//│ let x$11 = -(0,x$10) in -- #80 +//│ jump j$1(x$11) -- #79 +//│ false => +//│ let x$12 = -(g_m$0,1) in -- #84 +//│ let x$13 = +(g_acc$1,1) in -- #83 +//│ jump _g_f_opt_jp$6(1,g_m$0,g_acc$1,x$12,x$13) -- #82 +//│ ) +//│ }, +//│ let* (x$15) = g(6,0) in -- #70 +//│ x$15 -- #69) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, double, [x$0], +//│ 1, +//│ let x$1 = *(x$0,2) in -- #3 +//│ x$1 -- #2 +//│ ) +//│ Def(1, f, [n$0,acc$0], +//│ 1, +//│ let* (r0) = _g_f_opt$5(1,undefined,undefined,n$0,acc$0) in -- #100 +//│ r0 -- #99 +//│ ) +//│ Def(2, j$0, [x$3], +//│ 1, +//│ x$3 -- #7 +//│ ) +//│ Def(3, g, [m$0,acc$1], +//│ 1, +//│ let* (r0) = _g_f_opt$5(3,m$0,acc$1,undefined,undefined) in -- #98 +//│ r0 -- #97 +//│ ) +//│ Def(4, j$1, [x$9], +//│ 1, +//│ x$9 -- #35 +//│ ) +//│ Def(5, _g_f_opt$5, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ 1, +//│ jump _g_f_opt_jp$6(tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #96 +//│ ) +//│ Def(6, _g_f_opt_jp$6, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ 1, +//│ let scrut = ==(1,tailrecBranch$) in -- #95 +//│ if scrut -- #94 +//│ true => +//│ let x$2 = ==(f_n$0,0) in -- #93 +//│ if x$2 -- #92 +//│ true => +//│ let* (x$4) = double(f_acc$0) in -- #88 +//│ jump j$0(x$4) -- #87 +//│ false => +//│ let x$5 = -(f_n$0,1) in -- #91 +//│ let x$6 = +(f_acc$0,1) in -- #90 +//│ jump _g_f_opt_jp$6(3,x$5,x$6,f_n$0,f_acc$0) -- #89 +//│ false => +//│ let x$8 = ==(g_m$0,0) in -- #86 +//│ if x$8 -- #85 +//│ true => +//│ let* (x$10) = double(g_acc$1) in -- #81 +//│ let x$11 = -(0,x$10) in -- #80 +//│ jump j$1(x$11) -- #79 +//│ false => +//│ let x$12 = -(g_m$0,1) in -- #84 +//│ let x$13 = +(g_acc$1,1) in -- #83 +//│ jump _g_f_opt_jp$6(1,g_m$0,g_acc$1,x$12,x$13) -- #82 +//│ ) +//│ }, +//│ let* (x$15) = g(6,0) in -- #70 +//│ x$15 -- #69) +//│ +//│ Interpreted: +//│ -12 + +@tailrec fun f(a, b, c) = g(0, 0) +@tailrec fun g(d, e) = h(0, 0, 0, 0) +@tailrec fun h(p, q, r, s) = f(0, 0, 0) +2 +//│ |@|tailrec| |#fun| |f|(|a|,| |b|,| |c|)| |#=| |g|(|0|,| |0|)|↵|@|tailrec| |#fun| |g|(|d|,| |e|)| |#=| |h|(|0|,| |0|,| |0|,| |0|)|↵|@|tailrec| |#fun| |h|(|p|,| |q|,| |r|,| |s|)| |#=| |f|(|0|,| |0|,| |0|)|↵|2| | +//│ Parsed: {fun f = (a, b, c,) => g(0, 0,); fun g = (d, e,) => h(0, 0, 0, 0,); fun h = (p, q, r, s,) => f(0, 0, 0,); 2} +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, f, [a$0,b$0,c$0], +//│ 1, +//│ let* (x$0) = g(0,0) in -- #7 +//│ x$0 -- #6 +//│ ) +//│ Def(1, g, [d$0,e$0], +//│ 1, +//│ let* (x$1) = h(0,0,0,0) in -- #19 +//│ x$1 -- #18 +//│ ) +//│ Def(2, h, [p$0,q$0,r$0,s$0], +//│ 1, +//│ let* (x$2) = f(0,0,0) in -- #29 +//│ x$2 -- #28 +//│ ) +//│ }, +//│ 2 -- #30) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(h, g, f)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, f, [a$0,b$0,c$0], +//│ 1, +//│ let* (r0) = _h_g_f_opt$3(0,undefined,undefined,undefined,undefined,undefined,undefined,a$0,b$0,c$0) in -- #45 +//│ r0 -- #44 +//│ ) +//│ Def(1, g, [d$0,e$0], +//│ 1, +//│ let* (r0) = _h_g_f_opt$3(1,undefined,undefined,undefined,undefined,d$0,e$0,undefined,undefined,undefined) in -- #43 +//│ r0 -- #42 +//│ ) +//│ Def(2, h, [p$0,q$0,r$0,s$0], +//│ 1, +//│ let* (r0) = _h_g_f_opt$3(2,p$0,q$0,r$0,s$0,undefined,undefined,undefined,undefined,undefined) in -- #41 +//│ r0 -- #40 +//│ ) +//│ Def(3, _h_g_f_opt$3, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], +//│ 1, +//│ jump _h_g_f_opt_jp$4(tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #39 +//│ ) +//│ Def(4, _h_g_f_opt_jp$4, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], +//│ 1, +//│ let scrut = ==(0,tailrecBranch$) in -- #38 +//│ if scrut -- #37 +//│ true => +//│ jump _h_g_f_opt_jp$4(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #34 +//│ false => +//│ let scrut = ==(1,tailrecBranch$) in -- #36 +//│ if scrut -- #35 +//│ true => +//│ jump _h_g_f_opt_jp$4(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #33 +//│ false => +//│ jump _h_g_f_opt_jp$4(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #32 +//│ ) +//│ }, +//│ 2 -- #30) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, f, [a$0,b$0,c$0], +//│ 1, +//│ let* (r0) = _h_g_f_opt$3(0,undefined,undefined,undefined,undefined,undefined,undefined,a$0,b$0,c$0) in -- #45 +//│ r0 -- #44 +//│ ) +//│ Def(1, g, [d$0,e$0], +//│ 1, +//│ let* (r0) = _h_g_f_opt$3(1,undefined,undefined,undefined,undefined,d$0,e$0,undefined,undefined,undefined) in -- #43 +//│ r0 -- #42 +//│ ) +//│ Def(2, h, [p$0,q$0,r$0,s$0], +//│ 1, +//│ let* (r0) = _h_g_f_opt$3(2,p$0,q$0,r$0,s$0,undefined,undefined,undefined,undefined,undefined) in -- #41 +//│ r0 -- #40 +//│ ) +//│ Def(3, _h_g_f_opt$3, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], +//│ 1, +//│ jump _h_g_f_opt_jp$4(tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #39 +//│ ) +//│ Def(4, _h_g_f_opt_jp$4, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], +//│ 1, +//│ let scrut = ==(0,tailrecBranch$) in -- #38 +//│ if scrut -- #37 +//│ true => +//│ jump _h_g_f_opt_jp$4(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #34 +//│ false => +//│ let scrut = ==(1,tailrecBranch$) in -- #36 +//│ if scrut -- #35 +//│ true => +//│ jump _h_g_f_opt_jp$4(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #33 +//│ false => +//│ jump _h_g_f_opt_jp$4(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #32 +//│ ) +//│ }, +//│ 2 -- #30) + +:ce +fun hello() = + @tailcall hello() + @tailcall hello() + 2 +hello() +//│ |#fun| |hello|(||)| |#=|→|@|tailcall| |hello|(||)|↵|@|tailcall| |hello|(||)|↵|2|←|↵|hello|(||)| | +//│ Parsed: {fun hello = () => {@tailcall hello(); @tailcall hello(); 2}; hello()} +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, hello, [], +//│ 1, +//│ let* (x$0) = @tailcall hello() in -- #8 +//│ let* (x$1) = @tailcall hello() in -- #7 +//│ 2 -- #6 +//│ ) +//│ }, +//│ let* (x$2) = hello() in -- #12 +//│ x$2 -- #11) +//│ ╔══[COMPILATION ERROR] not a tail call, as the remaining functions may be impure +//│ ║ l.594: @tailcall hello() +//│ ╙── ^^^^^ +//│ ╔══[COMPILATION ERROR] not a tail call +//│ ║ l.595: @tailcall hello() +//│ ╙── ^^^^^ +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(hello)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, hello, [], +//│ 1, +//│ let* (x$0) = @tailcall hello() in -- #8 +//│ let* (x$1) = @tailcall hello() in -- #7 +//│ 2 -- #6 +//│ ) +//│ }, +//│ let* (x$2) = hello() in -- #12 +//│ x$2 -- #11) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, hello, [], +//│ 1, +//│ let* (x$0) = @tailcall hello() in -- #8 +//│ let* (x$1) = @tailcall hello() in -- #7 +//│ 2 -- #6 +//│ ) +//│ }, +//│ let* (x$2) = hello() in -- #12 +//│ x$2 -- #11) + +:ce +fun hello() = + @tailcall hello() + 2 +hello() +//│ |#fun| |hello|(||)| |#=|→|@|tailcall| |hello|(||)|↵|2|←|↵|hello|(||)| | +//│ Parsed: {fun hello = () => {@tailcall hello(); 2}; hello()} +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, hello, [], +//│ 1, +//│ let* (x$0) = @tailcall hello() in -- #4 +//│ 2 -- #3 +//│ ) +//│ }, +//│ let* (x$1) = hello() in -- #8 +//│ x$1 -- #7) +//│ ╔══[COMPILATION ERROR] not a tail call +//│ ║ l.646: @tailcall hello() +//│ ╙── ^^^^^ +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(hello)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, hello, [], +//│ 1, +//│ let* (x$0) = @tailcall hello() in -- #4 +//│ 2 -- #3 +//│ ) +//│ }, +//│ let* (x$1) = hello() in -- #8 +//│ x$1 -- #7) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, hello, [], +//│ 1, +//│ let* (x$0) = @tailcall hello() in -- #4 +//│ 2 -- #3 +//│ ) +//│ }, +//│ let* (x$1) = hello() in -- #8 +//│ x$1 -- #7) + +:interpIR +class Cons(h, t) +class Nil +@tailrec fun addOne(xs) = + if xs is + Cons(h, t) then Cons(h + 1, @tailcall addOne(t)) + Nil then Nil +addOne(Cons(1, Cons(2, Cons(3, Nil)))) +//│ |#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is|→|Cons|(|h|,| |t|)| |#then| |Cons|(|h| |+| |1|,| |@|tailcall| |addOne|(|t|)|)|↵|Nil| |#then| |Nil|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| +//│ Parsed: {class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then Cons(+(h,)(1,), @tailcall addOne(t,),); (Nil) then Nil›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Def(0, addOne, [xs$0], +//│ 1, +//│ case xs$0 of -- #27 +//│ Cons => +//│ let x$1 = xs$0.t in -- #23 +//│ let x$2 = xs$0.h in -- #22 +//│ let x$3 = +(x$2,1) in -- #21 +//│ let* (x$4) = @tailcall addOne(x$1) in -- #20 +//│ let x$5 = Cons(x$3,x$4) in -- #19 +//│ jump j$0(x$5) -- #18 +//│ Nil => +//│ let x$6 = Nil() in -- #26 +//│ jump j$0(x$6) -- #25 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ }, +//│ let x$7 = Nil() in -- #52 +//│ let x$8 = Cons(3,x$7) in -- #51 +//│ let x$9 = Cons(2,x$8) in -- #50 +//│ let x$10 = Cons(1,x$9) in -- #49 +//│ let* (x$11) = addOne(x$10) in -- #48 +//│ x$11 -- #47) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$0), Set(addOne)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, addOne, [xs$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #80 +//│ let* (res) = addOne_modcons$4(idCtx,xs$0) in -- #79 +//│ res -- #78 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, _addOne_ctx_app$2, [ctx,x], +//│ 1, +//│ case ctx of -- #59 +//│ _IdContext => +//│ x -- #58 +//│ _Context => +//│ let field = ctx.field in -- #57 +//│ let ptr = ctx.ptr in -- #56 +//│ let _ = assign ptr.t := x in -- #55 +//│ let acc = ctx.acc in -- #54 +//│ acc -- #53 +//│ ) +//│ Def(3, _addOne_ctx_comp$3, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #65 +//│ let ctx2ptr = ctx2.ptr in -- #64 +//│ let ctx2field = ctx2.field in -- #63 +//│ let* (newAcc) = _addOne_ctx_app$2(ctx1,ctx2acc) in -- #62 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #61 +//│ ret -- #60 +//│ ) +//│ Def(5, addOne_modcons$4_jp, [ctx,xs$0], +//│ 1, +//│ case xs$0 of -- #91 +//│ Cons => +//│ let x$1 = xs$0.t in -- #87 +//│ let x$2 = xs$0.h in -- #86 +//│ let x$3 = +(x$2,1) in -- #85 +//│ let x$5 = Cons(x$3,0) in -- #84 +//│ let ctx2 = _Context(x$5,x$5,0) in -- #83 +//│ let* (composed) = _addOne_ctx_comp$3(ctx,ctx2) in -- #82 +//│ jump addOne_modcons$4_jp(composed,x$1) -- #81 +//│ Nil => +//│ let x$6 = Nil() in -- #90 +//│ let* (res) = _addOne_ctx_app$2(ctx,x$6) in -- #89 +//│ res -- #88 +//│ ) +//│ Def(6, addOne_modcons$4, [ctx,xs$0], +//│ 1, +//│ let* (r0) = addOne_modcons$4_jp(ctx,xs$0) in -- #93 +//│ r0 -- #92 +//│ ) +//│ }, +//│ let x$7 = Nil() in -- #52 +//│ let x$8 = Cons(3,x$7) in -- #51 +//│ let x$9 = Cons(2,x$8) in -- #50 +//│ let x$10 = Cons(1,x$9) in -- #49 +//│ let* (x$11) = addOne(x$10) in -- #48 +//│ x$11 -- #47) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, addOne, [xs$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #80 +//│ let* (res) = addOne_modcons$4(idCtx,xs$0) in -- #79 +//│ res -- #78 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, _addOne_ctx_app$2, [ctx,x], +//│ 1, +//│ case ctx of -- #59 +//│ _IdContext => +//│ x -- #58 +//│ _Context => +//│ let field = ctx.field in -- #57 +//│ let ptr = ctx.ptr in -- #56 +//│ let _ = assign ptr.t := x in -- #55 +//│ let acc = ctx.acc in -- #54 +//│ acc -- #53 +//│ ) +//│ Def(3, _addOne_ctx_comp$3, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #65 +//│ let ctx2ptr = ctx2.ptr in -- #64 +//│ let ctx2field = ctx2.field in -- #63 +//│ let* (newAcc) = _addOne_ctx_app$2(ctx1,ctx2acc) in -- #62 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #61 +//│ ret -- #60 +//│ ) +//│ Def(5, addOne_modcons$4_jp, [ctx,xs$0], +//│ 1, +//│ case xs$0 of -- #91 +//│ Cons => +//│ let x$1 = xs$0.t in -- #87 +//│ let x$2 = xs$0.h in -- #86 +//│ let x$3 = +(x$2,1) in -- #85 +//│ let x$5 = Cons(x$3,0) in -- #84 +//│ let ctx2 = _Context(x$5,x$5,0) in -- #83 +//│ let* (composed) = _addOne_ctx_comp$3(ctx,ctx2) in -- #82 +//│ jump addOne_modcons$4_jp(composed,x$1) -- #81 +//│ Nil => +//│ let x$6 = Nil() in -- #90 +//│ let* (res) = _addOne_ctx_app$2(ctx,x$6) in -- #89 +//│ res -- #88 +//│ ) +//│ Def(6, addOne_modcons$4, [ctx,xs$0], +//│ 1, +//│ let* (r0) = addOne_modcons$4_jp(ctx,xs$0) in -- #93 +//│ r0 -- #92 +//│ ) +//│ }, +//│ let x$7 = Nil() in -- #52 +//│ let x$8 = Cons(3,x$7) in -- #51 +//│ let x$9 = Cons(2,x$8) in -- #50 +//│ let x$10 = Cons(1,x$9) in -- #49 +//│ let* (x$11) = addOne(x$10) in -- #48 +//│ x$11 -- #47) +//│ +//│ Interpreted: +//│ Cons(2,Cons(3,Cons(4,Nil()))) + +:noTailRec +:interpIR +class Zero +class S(x) +fun a(n) = + if n is + S(x) then S(@tailcall b(x)) + Zero then S(Zero) +fun b(n) = + if n is + S(x) then S(S(@tailcall a(x))) + Zero then S(S(Zero)) +a(S(S(S(Zero)))) +//│ |#class| |Zero|↵|#class| |S|(|x|)|↵|#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailcall| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailcall| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| +//│ Parsed: {class Zero {}; class S(x,) {}; fun a = (n,) => {if n is ‹(S(x,)) then S(@tailcall b(x,),); (Zero) then S(Zero,)›}; fun b = (n,) => {if n is ‹(S(x,)) then S(S(@tailcall a(x,),),); (Zero) then S(S(Zero,),)›}; a(S(S(S(Zero,),),),)} +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { +//│ Def(0, a, [n$0], +//│ 1, +//│ case n$0 of -- #23 +//│ S => +//│ let x$1 = n$0.x in -- #15 +//│ let* (x$2) = @tailcall b(x$1) in -- #14 +//│ let x$3 = S(x$2) in -- #13 +//│ jump j$0(x$3) -- #12 +//│ Zero => +//│ let x$4 = Zero() in -- #22 +//│ let x$5 = S(x$4) in -- #21 +//│ jump j$0(x$5) -- #20 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, b, [n$1], +//│ 1, +//│ case n$1 of -- #55 +//│ S => +//│ let x$7 = n$1.x in -- #43 +//│ let* (x$8) = @tailcall a(x$7) in -- #42 +//│ let x$9 = S(x$8) in -- #41 +//│ let x$10 = S(x$9) in -- #40 +//│ jump j$1(x$10) -- #39 +//│ Zero => +//│ let x$11 = Zero() in -- #54 +//│ let x$12 = S(x$11) in -- #53 +//│ let x$13 = S(x$12) in -- #52 +//│ jump j$1(x$13) -- #51 +//│ ) +//│ Def(3, j$1, [x$6], +//│ 1, +//│ x$6 -- #25 +//│ ) +//│ }, +//│ let x$14 = Zero() in -- #74 +//│ let x$15 = S(x$14) in -- #73 +//│ let x$16 = S(x$15) in -- #72 +//│ let x$17 = S(x$16) in -- #71 +//│ let* (x$18) = a(x$17) in -- #70 +//│ x$18 -- #69) +//│ +//│ Interpreted: +//│ S(S(S(S(S(S(Zero())))))) + +:interpIR +class Zero +class S(x) +@tailrec fun a(n) = + if n is + S(x) then S(@tailcall b(x)) + Zero then S(Zero) +@tailrec fun b(n) = + if n is + S(x) then S(S(@tailcall a(x))) + Zero then S(S(Zero)) +a(S(S(S(Zero)))) +//│ |#class| |Zero|↵|#class| |S|(|x|)|↵|@|tailrec| |#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailcall| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|@|tailrec| |#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailcall| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| +//│ Parsed: {class Zero {}; class S(x,) {}; fun a = (n,) => {if n is ‹(S(x,)) then S(@tailcall b(x,),); (Zero) then S(Zero,)›}; fun b = (n,) => {if n is ‹(S(x,)) then S(S(@tailcall a(x,),),); (Zero) then S(S(Zero,),)›}; a(S(S(S(Zero,),),),)} +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { +//│ Def(0, a, [n$0], +//│ 1, +//│ case n$0 of -- #23 +//│ S => +//│ let x$1 = n$0.x in -- #15 +//│ let* (x$2) = @tailcall b(x$1) in -- #14 +//│ let x$3 = S(x$2) in -- #13 +//│ jump j$0(x$3) -- #12 +//│ Zero => +//│ let x$4 = Zero() in -- #22 +//│ let x$5 = S(x$4) in -- #21 +//│ jump j$0(x$5) -- #20 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, b, [n$1], +//│ 1, +//│ case n$1 of -- #55 +//│ S => +//│ let x$7 = n$1.x in -- #43 +//│ let* (x$8) = @tailcall a(x$7) in -- #42 +//│ let x$9 = S(x$8) in -- #41 +//│ let x$10 = S(x$9) in -- #40 +//│ jump j$1(x$10) -- #39 +//│ Zero => +//│ let x$11 = Zero() in -- #54 +//│ let x$12 = S(x$11) in -- #53 +//│ let x$13 = S(x$12) in -- #52 +//│ jump j$1(x$13) -- #51 +//│ ) +//│ Def(3, j$1, [x$6], +//│ 1, +//│ x$6 -- #25 +//│ ) +//│ }, +//│ let x$14 = Zero() in -- #74 +//│ let x$15 = S(x$14) in -- #73 +//│ let x$16 = S(x$15) in -- #72 +//│ let x$17 = S(x$16) in -- #71 +//│ let* (x$18) = a(x$17) in -- #70 +//│ x$18 -- #69) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$1), Set(j$0), Set(b, a)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [n$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #117 +//│ let* (res) = a_modcons$7(idCtx,n$0) in -- #116 +//│ res -- #115 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, b, [n$1], +//│ 1, +//│ let idCtx = _IdContext() in -- #103 +//│ let* (res) = b_modcons$6(idCtx,n$1) in -- #102 +//│ res -- #101 +//│ ) +//│ Def(3, j$1, [x$6], +//│ 1, +//│ x$6 -- #25 +//│ ) +//│ Def(4, _b_a_ctx_app$4, [ctx,x], +//│ 1, +//│ case ctx of -- #81 +//│ _IdContext => +//│ x -- #80 +//│ _Context => +//│ let field = ctx.field in -- #79 +//│ let ptr = ctx.ptr in -- #78 +//│ let _ = assign ptr.x := x in -- #77 +//│ let acc = ctx.acc in -- #76 +//│ acc -- #75 +//│ ) +//│ Def(5, _b_a_ctx_comp$5, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #87 +//│ let ctx2ptr = ctx2.ptr in -- #86 +//│ let ctx2field = ctx2.field in -- #85 +//│ let* (newAcc) = _b_a_ctx_app$4(ctx1,ctx2acc) in -- #84 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #83 +//│ ret -- #82 +//│ ) +//│ Def(6, b_modcons$6, [ctx,n$1], +//│ 1, +//│ let* (r0) = _b_modcons$6_a_modcons$7_opt$8(6,ctx,n$1,undefined,undefined) in -- #156 +//│ r0 -- #155 +//│ ) +//│ Def(7, a_modcons$7, [ctx,n$0], +//│ 1, +//│ let* (r0) = _b_modcons$6_a_modcons$7_opt$8(7,undefined,undefined,ctx,n$0) in -- #158 +//│ r0 -- #157 +//│ ) +//│ Def(8, _b_modcons$6_a_modcons$7_opt$8, [tailrecBranch$,b_modcons$6_ctx,b_modcons$6_n$1,a_modcons$7_ctx,a_modcons$7_n$0], +//│ 1, +//│ jump _b_modcons$6_a_modcons$7_opt_jp$9(tailrecBranch$,b_modcons$6_ctx,b_modcons$6_n$1,a_modcons$7_ctx,a_modcons$7_n$0) -- #154 +//│ ) +//│ Def(9, _b_modcons$6_a_modcons$7_opt_jp$9, [tailrecBranch$,b_modcons$6_ctx,b_modcons$6_n$1,a_modcons$7_ctx,a_modcons$7_n$0], +//│ 1, +//│ let scrut = ==(7,tailrecBranch$) in -- #153 +//│ if scrut -- #152 +//│ true => +//│ case a_modcons$7_n$0 of -- #151 +//│ S => +//│ let x$1 = a_modcons$7_n$0.x in -- #146 +//│ let x$3 = S(0) in -- #145 +//│ let ctx2 = _Context(x$3,x$3,0) in -- #144 +//│ let* (composed) = _b_a_ctx_comp$5(a_modcons$7_ctx,ctx2) in -- #143 +//│ jump _b_modcons$6_a_modcons$7_opt_jp$9(6,composed,x$1,a_modcons$7_ctx,a_modcons$7_n$0) -- #142 +//│ Zero => +//│ let x$4 = Zero() in -- #150 +//│ let x$5 = S(x$4) in -- #149 +//│ let* (res) = _b_a_ctx_app$4(a_modcons$7_ctx,x$5) in -- #148 +//│ res -- #147 +//│ false => +//│ case b_modcons$6_n$1 of -- #141 +//│ S => +//│ let x$7 = b_modcons$6_n$1.x in -- #135 +//│ let x$9 = S(0) in -- #134 +//│ let x$10 = S(x$9) in -- #133 +//│ let ctx2 = _Context(x$10,x$9,0) in -- #132 +//│ let* (composed) = _b_a_ctx_comp$5(b_modcons$6_ctx,ctx2) in -- #131 +//│ jump _b_modcons$6_a_modcons$7_opt_jp$9(7,b_modcons$6_ctx,b_modcons$6_n$1,composed,x$7) -- #130 +//│ Zero => +//│ let x$11 = Zero() in -- #140 +//│ let x$12 = S(x$11) in -- #139 +//│ let x$13 = S(x$12) in -- #138 +//│ let* (res) = _b_a_ctx_app$4(b_modcons$6_ctx,x$13) in -- #137 +//│ res -- #136 +//│ ) +//│ }, +//│ let x$14 = Zero() in -- #74 +//│ let x$15 = S(x$14) in -- #73 +//│ let x$16 = S(x$15) in -- #72 +//│ let x$17 = S(x$16) in -- #71 +//│ let* (x$18) = a(x$17) in -- #70 +//│ x$18 -- #69) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [n$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #117 +//│ let* (res) = a_modcons$7(idCtx,n$0) in -- #116 +//│ res -- #115 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, b, [n$1], +//│ 1, +//│ let idCtx = _IdContext() in -- #103 +//│ let* (res) = b_modcons$6(idCtx,n$1) in -- #102 +//│ res -- #101 +//│ ) +//│ Def(3, j$1, [x$6], +//│ 1, +//│ x$6 -- #25 +//│ ) +//│ Def(4, _b_a_ctx_app$4, [ctx,x], +//│ 1, +//│ case ctx of -- #81 +//│ _IdContext => +//│ x -- #80 +//│ _Context => +//│ let field = ctx.field in -- #79 +//│ let ptr = ctx.ptr in -- #78 +//│ let _ = assign ptr.x := x in -- #77 +//│ let acc = ctx.acc in -- #76 +//│ acc -- #75 +//│ ) +//│ Def(5, _b_a_ctx_comp$5, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #87 +//│ let ctx2ptr = ctx2.ptr in -- #86 +//│ let ctx2field = ctx2.field in -- #85 +//│ let* (newAcc) = _b_a_ctx_app$4(ctx1,ctx2acc) in -- #84 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #83 +//│ ret -- #82 +//│ ) +//│ Def(6, b_modcons$6, [ctx,n$1], +//│ 1, +//│ let* (r0) = _b_modcons$6_a_modcons$7_opt$8(6,ctx,n$1,undefined,undefined) in -- #156 +//│ r0 -- #155 +//│ ) +//│ Def(7, a_modcons$7, [ctx,n$0], +//│ 1, +//│ let* (r0) = _b_modcons$6_a_modcons$7_opt$8(7,undefined,undefined,ctx,n$0) in -- #158 +//│ r0 -- #157 +//│ ) +//│ Def(8, _b_modcons$6_a_modcons$7_opt$8, [tailrecBranch$,b_modcons$6_ctx,b_modcons$6_n$1,a_modcons$7_ctx,a_modcons$7_n$0], +//│ 1, +//│ jump _b_modcons$6_a_modcons$7_opt_jp$9(tailrecBranch$,b_modcons$6_ctx,b_modcons$6_n$1,a_modcons$7_ctx,a_modcons$7_n$0) -- #154 +//│ ) +//│ Def(9, _b_modcons$6_a_modcons$7_opt_jp$9, [tailrecBranch$,b_modcons$6_ctx,b_modcons$6_n$1,a_modcons$7_ctx,a_modcons$7_n$0], +//│ 1, +//│ let scrut = ==(7,tailrecBranch$) in -- #153 +//│ if scrut -- #152 +//│ true => +//│ case a_modcons$7_n$0 of -- #151 +//│ S => +//│ let x$1 = a_modcons$7_n$0.x in -- #146 +//│ let x$3 = S(0) in -- #145 +//│ let ctx2 = _Context(x$3,x$3,0) in -- #144 +//│ let* (composed) = _b_a_ctx_comp$5(a_modcons$7_ctx,ctx2) in -- #143 +//│ jump _b_modcons$6_a_modcons$7_opt_jp$9(6,composed,x$1,a_modcons$7_ctx,a_modcons$7_n$0) -- #142 +//│ Zero => +//│ let x$4 = Zero() in -- #150 +//│ let x$5 = S(x$4) in -- #149 +//│ let* (res) = _b_a_ctx_app$4(a_modcons$7_ctx,x$5) in -- #148 +//│ res -- #147 +//│ false => +//│ case b_modcons$6_n$1 of -- #141 +//│ S => +//│ let x$7 = b_modcons$6_n$1.x in -- #135 +//│ let x$9 = S(0) in -- #134 +//│ let x$10 = S(x$9) in -- #133 +//│ let ctx2 = _Context(x$10,x$9,0) in -- #132 +//│ let* (composed) = _b_a_ctx_comp$5(b_modcons$6_ctx,ctx2) in -- #131 +//│ jump _b_modcons$6_a_modcons$7_opt_jp$9(7,b_modcons$6_ctx,b_modcons$6_n$1,composed,x$7) -- #130 +//│ Zero => +//│ let x$11 = Zero() in -- #140 +//│ let x$12 = S(x$11) in -- #139 +//│ let x$13 = S(x$12) in -- #138 +//│ let* (res) = _b_a_ctx_app$4(b_modcons$6_ctx,x$13) in -- #137 +//│ res -- #136 +//│ ) +//│ }, +//│ let x$14 = Zero() in -- #74 +//│ let x$15 = S(x$14) in -- #73 +//│ let x$16 = S(x$15) in -- #72 +//│ let x$17 = S(x$16) in -- #71 +//│ let* (x$18) = a(x$17) in -- #70 +//│ x$18 -- #69) +//│ +//│ Interpreted: +//│ S(S(S(S(S(S(Zero())))))) + +:interpIR +class Cons(h, t) +class Nil +@tailrec fun addOne(xs) = + if xs is + Cons(h, t) then + val next = @tailcall addOne(t) + val ret = Cons(h + 1, next) + val rett = ret + rett + Nil then + Nil +addOne(Cons(1, Cons(2, Cons(3, Nil)))) +//│ |#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#val| |next| |#=| |@|tailcall| |addOne|(|t|)|↵|#val| |ret| |#=| |Cons|(|h| |+| |1|,| |next|)|↵|#val| |rett| |#=| |ret|↵|rett|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| +//│ Parsed: {class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then {let next = @tailcall addOne(t,); let ret = Cons(+(h,)(1,), next,); let rett = ret; rett}; (Nil) then {Nil}›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Def(0, addOne, [xs$0], +//│ 1, +//│ case xs$0 of -- #30 +//│ Cons => +//│ let x$1 = xs$0.t in -- #26 +//│ let x$2 = xs$0.h in -- #25 +//│ let* (x$3) = @tailcall addOne(x$1) in -- #24 +//│ let x$4 = +(x$2,1) in -- #23 +//│ let x$5 = Cons(x$4,x$3) in -- #22 +//│ jump j$0(x$5) -- #21 +//│ Nil => +//│ let x$6 = Nil() in -- #29 +//│ jump j$0(x$6) -- #28 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ }, +//│ let x$7 = Nil() in -- #55 +//│ let x$8 = Cons(3,x$7) in -- #54 +//│ let x$9 = Cons(2,x$8) in -- #53 +//│ let x$10 = Cons(1,x$9) in -- #52 +//│ let* (x$11) = addOne(x$10) in -- #51 +//│ x$11 -- #50) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$0), Set(addOne)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, addOne, [xs$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #83 +//│ let* (res) = addOne_modcons$4(idCtx,xs$0) in -- #82 +//│ res -- #81 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, _addOne_ctx_app$2, [ctx,x], +//│ 1, +//│ case ctx of -- #62 +//│ _IdContext => +//│ x -- #61 +//│ _Context => +//│ let field = ctx.field in -- #60 +//│ let ptr = ctx.ptr in -- #59 +//│ let _ = assign ptr.t := x in -- #58 +//│ let acc = ctx.acc in -- #57 +//│ acc -- #56 +//│ ) +//│ Def(3, _addOne_ctx_comp$3, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #68 +//│ let ctx2ptr = ctx2.ptr in -- #67 +//│ let ctx2field = ctx2.field in -- #66 +//│ let* (newAcc) = _addOne_ctx_app$2(ctx1,ctx2acc) in -- #65 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #64 +//│ ret -- #63 +//│ ) +//│ Def(5, addOne_modcons$4_jp, [ctx,xs$0], +//│ 1, +//│ case xs$0 of -- #94 +//│ Cons => +//│ let x$1 = xs$0.t in -- #90 +//│ let x$2 = xs$0.h in -- #89 +//│ let x$4 = +(x$2,1) in -- #88 +//│ let x$5 = Cons(x$4,0) in -- #87 +//│ let ctx2 = _Context(x$5,x$5,0) in -- #86 +//│ let* (composed) = _addOne_ctx_comp$3(ctx,ctx2) in -- #85 +//│ jump addOne_modcons$4_jp(composed,x$1) -- #84 +//│ Nil => +//│ let x$6 = Nil() in -- #93 +//│ let* (res) = _addOne_ctx_app$2(ctx,x$6) in -- #92 +//│ res -- #91 +//│ ) +//│ Def(6, addOne_modcons$4, [ctx,xs$0], +//│ 1, +//│ let* (r0) = addOne_modcons$4_jp(ctx,xs$0) in -- #96 +//│ r0 -- #95 +//│ ) +//│ }, +//│ let x$7 = Nil() in -- #55 +//│ let x$8 = Cons(3,x$7) in -- #54 +//│ let x$9 = Cons(2,x$8) in -- #53 +//│ let x$10 = Cons(1,x$9) in -- #52 +//│ let* (x$11) = addOne(x$10) in -- #51 +//│ x$11 -- #50) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, addOne, [xs$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #83 +//│ let* (res) = addOne_modcons$4(idCtx,xs$0) in -- #82 +//│ res -- #81 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, _addOne_ctx_app$2, [ctx,x], +//│ 1, +//│ case ctx of -- #62 +//│ _IdContext => +//│ x -- #61 +//│ _Context => +//│ let field = ctx.field in -- #60 +//│ let ptr = ctx.ptr in -- #59 +//│ let _ = assign ptr.t := x in -- #58 +//│ let acc = ctx.acc in -- #57 +//│ acc -- #56 +//│ ) +//│ Def(3, _addOne_ctx_comp$3, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #68 +//│ let ctx2ptr = ctx2.ptr in -- #67 +//│ let ctx2field = ctx2.field in -- #66 +//│ let* (newAcc) = _addOne_ctx_app$2(ctx1,ctx2acc) in -- #65 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #64 +//│ ret -- #63 +//│ ) +//│ Def(5, addOne_modcons$4_jp, [ctx,xs$0], +//│ 1, +//│ case xs$0 of -- #94 +//│ Cons => +//│ let x$1 = xs$0.t in -- #90 +//│ let x$2 = xs$0.h in -- #89 +//│ let x$4 = +(x$2,1) in -- #88 +//│ let x$5 = Cons(x$4,0) in -- #87 +//│ let ctx2 = _Context(x$5,x$5,0) in -- #86 +//│ let* (composed) = _addOne_ctx_comp$3(ctx,ctx2) in -- #85 +//│ jump addOne_modcons$4_jp(composed,x$1) -- #84 +//│ Nil => +//│ let x$6 = Nil() in -- #93 +//│ let* (res) = _addOne_ctx_app$2(ctx,x$6) in -- #92 +//│ res -- #91 +//│ ) +//│ Def(6, addOne_modcons$4, [ctx,xs$0], +//│ 1, +//│ let* (r0) = addOne_modcons$4_jp(ctx,xs$0) in -- #96 +//│ r0 -- #95 +//│ ) +//│ }, +//│ let x$7 = Nil() in -- #55 +//│ let x$8 = Cons(3,x$7) in -- #54 +//│ let x$9 = Cons(2,x$8) in -- #53 +//│ let x$10 = Cons(1,x$9) in -- #52 +//│ let* (x$11) = addOne(x$10) in -- #51 +//│ x$11 -- #50) +//│ +//│ Interpreted: +//│ Cons(2,Cons(3,Cons(4,Nil()))) + +:interpIR +class Nil +class Cons(m, n) +@tailrec fun a(x) = + if x is + Cons(m, n) then + if m < 0 then + Cons(-1, Nil) + else + Cons(m * 4, b(m - 2)) + Nil then Nil +@tailrec fun b(n) = + if n <= 0 then + Cons(0, Nil) + else + a(Cons(n, Nil)) +b(16) +//│ |#class| |Nil|↵|#class| |Cons|(|m|,| |n|)|↵|@|tailrec| |#fun| |a|(|x|)| |#=|→|#if| |x| |is|→|Cons|(|m|,| |n|)| |#then|→|#if| |m| |<| |0| |#then|→|Cons|(|-|1|,| |Nil|)|←|↵|#else| |→|Cons|(|m| |*| |4|,| |b|(|m| |-| |2|)|)|←|←|↵|Nil| |#then| |Nil|←|←|↵|@|tailrec| |#fun| |b|(|n|)| |#=|→|#if| |n| |<=| |0| |#then| |→|Cons|(|0|,| |Nil|)|←|↵|#else| |→|a|(|Cons|(|n|,| |Nil|)|)|←|←|↵|b|(|16|)| +//│ Parsed: {class Nil {}; class Cons(m, n,) {}; fun a = (x,) => {if x is ‹(Cons(m, n,)) then {if (<(m,)(0,)) then {Cons(-1, Nil,)} else {Cons(*(m,)(4,), b(-(m,)(2,),),)}}; (Nil) then Nil›}; fun b = (n,) => {if (<=(n,)(0,)) then {Cons(0, Nil,)} else {a(Cons(n, Nil,),)}}; b(16,)} +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n])}, { +//│ Def(0, a, [x$0], +//│ 1, +//│ case x$0 of -- #46 +//│ Cons => +//│ let x$2 = x$0.n in -- #42 +//│ let x$3 = x$0.m in -- #41 +//│ let x$4 = <(x$3,0) in -- #40 +//│ if x$4 -- #39 +//│ true => +//│ let x$6 = Nil() in -- #19 +//│ let x$7 = Cons(-1,x$6) in -- #18 +//│ jump j$1(x$7) -- #17 +//│ false => +//│ let x$8 = *(x$3,4) in -- #38 +//│ let x$9 = -(x$3,2) in -- #37 +//│ let* (x$10) = b(x$9) in -- #36 +//│ let x$11 = Cons(x$8,x$10) in -- #35 +//│ jump j$1(x$11) -- #34 +//│ Nil => +//│ let x$12 = Nil() in -- #45 +//│ jump j$0(x$12) -- #44 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #1 +//│ ) +//│ Def(2, j$1, [x$5], +//│ 1, +//│ jump j$0(x$5) -- #10 +//│ ) +//│ Def(3, b, [n$0], +//│ 1, +//│ let x$13 = <=(n$0,0) in -- #75 +//│ if x$13 -- #74 +//│ true => +//│ let x$15 = Nil() in -- #59 +//│ let x$16 = Cons(0,x$15) in -- #58 +//│ jump j$2(x$16) -- #57 +//│ false => +//│ let x$17 = Nil() in -- #73 +//│ let x$18 = Cons(n$0,x$17) in -- #72 +//│ let* (x$19) = a(x$18) in -- #71 +//│ jump j$2(x$19) -- #70 +//│ ) +//│ Def(4, j$2, [x$14], +//│ 1, +//│ x$14 -- #50 +//│ ) +//│ }, +//│ let* (x$20) = b(16) in -- #81 +//│ x$20 -- #80) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$2), Set(j$1), Set(j$0), Set(b, a)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [x$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #129 +//│ let* (res) = a_modcons$8(idCtx,x$0) in -- #128 +//│ res -- #127 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #1 +//│ ) +//│ Def(2, j$1, [x$5], +//│ 1, +//│ jump j$0(x$5) -- #10 +//│ ) +//│ Def(3, b, [n$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #107 +//│ let* (res) = b_modcons$7(idCtx,n$0) in -- #106 +//│ res -- #105 +//│ ) +//│ Def(4, j$2, [x$14], +//│ 1, +//│ x$14 -- #50 +//│ ) +//│ Def(5, _b_a_ctx_app$5, [ctx,x], +//│ 1, +//│ case ctx of -- #88 +//│ _IdContext => +//│ x -- #87 +//│ _Context => +//│ let field = ctx.field in -- #86 +//│ let ptr = ctx.ptr in -- #85 +//│ let _ = assign ptr.n := x in -- #84 +//│ let acc = ctx.acc in -- #83 +//│ acc -- #82 +//│ ) +//│ Def(6, _b_a_ctx_comp$6, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #94 +//│ let ctx2ptr = ctx2.ptr in -- #93 +//│ let ctx2field = ctx2.field in -- #92 +//│ let* (newAcc) = _b_a_ctx_app$5(ctx1,ctx2acc) in -- #91 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #90 +//│ ret -- #89 +//│ ) +//│ Def(7, b_modcons$7, [ctx,n$0], +//│ 1, +//│ let* (r0) = _b_modcons$7_a_modcons$8_opt$9(7,ctx,n$0,undefined,undefined) in -- #170 +//│ r0 -- #169 +//│ ) +//│ Def(8, a_modcons$8, [ctx,x$0], +//│ 1, +//│ let* (r0) = _b_modcons$7_a_modcons$8_opt$9(8,undefined,undefined,ctx,x$0) in -- #172 +//│ r0 -- #171 +//│ ) +//│ Def(9, _b_modcons$7_a_modcons$8_opt$9, [tailrecBranch$,b_modcons$7_ctx,b_modcons$7_n$0,a_modcons$8_ctx,a_modcons$8_x$0], +//│ 1, +//│ jump _b_modcons$7_a_modcons$8_opt_jp$10(tailrecBranch$,b_modcons$7_ctx,b_modcons$7_n$0,a_modcons$8_ctx,a_modcons$8_x$0) -- #168 +//│ ) +//│ Def(10, _b_modcons$7_a_modcons$8_opt_jp$10, [tailrecBranch$,b_modcons$7_ctx,b_modcons$7_n$0,a_modcons$8_ctx,a_modcons$8_x$0], +//│ 1, +//│ let scrut = ==(8,tailrecBranch$) in -- #167 +//│ if scrut -- #166 +//│ true => +//│ case a_modcons$8_x$0 of -- #165 +//│ Cons => +//│ let x$2 = a_modcons$8_x$0.n in -- #161 +//│ let x$3 = a_modcons$8_x$0.m in -- #160 +//│ let x$4 = <(x$3,0) in -- #159 +//│ if x$4 -- #158 +//│ true => +//│ let x$6 = Nil() in -- #151 +//│ let x$7 = Cons(-1,x$6) in -- #150 +//│ let* (res) = _b_a_ctx_app$5(a_modcons$8_ctx,x$7) in -- #149 +//│ res -- #148 +//│ false => +//│ let x$8 = *(x$3,4) in -- #157 +//│ let x$9 = -(x$3,2) in -- #156 +//│ let x$11 = Cons(x$8,0) in -- #155 +//│ let ctx2 = _Context(x$11,x$11,0) in -- #154 +//│ let* (composed) = _b_a_ctx_comp$6(a_modcons$8_ctx,ctx2) in -- #153 +//│ jump _b_modcons$7_a_modcons$8_opt_jp$10(7,composed,x$9,a_modcons$8_ctx,a_modcons$8_x$0) -- #152 +//│ Nil => +//│ let x$12 = Nil() in -- #164 +//│ let* (res) = _b_a_ctx_app$5(a_modcons$8_ctx,x$12) in -- #163 +//│ res -- #162 +//│ false => +//│ let x$13 = <=(b_modcons$7_n$0,0) in -- #147 +//│ if x$13 -- #146 +//│ true => +//│ let x$15 = Nil() in -- #142 +//│ let x$16 = Cons(0,x$15) in -- #141 +//│ let* (res) = _b_a_ctx_app$5(b_modcons$7_ctx,x$16) in -- #140 +//│ res -- #139 +//│ false => +//│ let x$17 = Nil() in -- #145 +//│ let x$18 = Cons(b_modcons$7_n$0,x$17) in -- #144 +//│ jump _b_modcons$7_a_modcons$8_opt_jp$10(8,b_modcons$7_ctx,b_modcons$7_n$0,b_modcons$7_ctx,x$18) -- #143 +//│ ) +//│ }, +//│ let* (x$20) = b(16) in -- #81 +//│ x$20 -- #80) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [x$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #129 +//│ let* (res) = a_modcons$8(idCtx,x$0) in -- #128 +//│ res -- #127 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #1 +//│ ) +//│ Def(2, j$1, [x$5], +//│ 1, +//│ jump j$0(x$5) -- #10 +//│ ) +//│ Def(3, b, [n$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #107 +//│ let* (res) = b_modcons$7(idCtx,n$0) in -- #106 +//│ res -- #105 +//│ ) +//│ Def(4, j$2, [x$14], +//│ 1, +//│ x$14 -- #50 +//│ ) +//│ Def(5, _b_a_ctx_app$5, [ctx,x], +//│ 1, +//│ case ctx of -- #88 +//│ _IdContext => +//│ x -- #87 +//│ _Context => +//│ let field = ctx.field in -- #86 +//│ let ptr = ctx.ptr in -- #85 +//│ let _ = assign ptr.n := x in -- #84 +//│ let acc = ctx.acc in -- #83 +//│ acc -- #82 +//│ ) +//│ Def(6, _b_a_ctx_comp$6, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #94 +//│ let ctx2ptr = ctx2.ptr in -- #93 +//│ let ctx2field = ctx2.field in -- #92 +//│ let* (newAcc) = _b_a_ctx_app$5(ctx1,ctx2acc) in -- #91 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #90 +//│ ret -- #89 +//│ ) +//│ Def(7, b_modcons$7, [ctx,n$0], +//│ 1, +//│ let* (r0) = _b_modcons$7_a_modcons$8_opt$9(7,ctx,n$0,undefined,undefined) in -- #170 +//│ r0 -- #169 +//│ ) +//│ Def(8, a_modcons$8, [ctx,x$0], +//│ 1, +//│ let* (r0) = _b_modcons$7_a_modcons$8_opt$9(8,undefined,undefined,ctx,x$0) in -- #172 +//│ r0 -- #171 +//│ ) +//│ Def(9, _b_modcons$7_a_modcons$8_opt$9, [tailrecBranch$,b_modcons$7_ctx,b_modcons$7_n$0,a_modcons$8_ctx,a_modcons$8_x$0], +//│ 1, +//│ jump _b_modcons$7_a_modcons$8_opt_jp$10(tailrecBranch$,b_modcons$7_ctx,b_modcons$7_n$0,a_modcons$8_ctx,a_modcons$8_x$0) -- #168 +//│ ) +//│ Def(10, _b_modcons$7_a_modcons$8_opt_jp$10, [tailrecBranch$,b_modcons$7_ctx,b_modcons$7_n$0,a_modcons$8_ctx,a_modcons$8_x$0], +//│ 1, +//│ let scrut = ==(8,tailrecBranch$) in -- #167 +//│ if scrut -- #166 +//│ true => +//│ case a_modcons$8_x$0 of -- #165 +//│ Cons => +//│ let x$2 = a_modcons$8_x$0.n in -- #161 +//│ let x$3 = a_modcons$8_x$0.m in -- #160 +//│ let x$4 = <(x$3,0) in -- #159 +//│ if x$4 -- #158 +//│ true => +//│ let x$6 = Nil() in -- #151 +//│ let x$7 = Cons(-1,x$6) in -- #150 +//│ let* (res) = _b_a_ctx_app$5(a_modcons$8_ctx,x$7) in -- #149 +//│ res -- #148 +//│ false => +//│ let x$8 = *(x$3,4) in -- #157 +//│ let x$9 = -(x$3,2) in -- #156 +//│ let x$11 = Cons(x$8,0) in -- #155 +//│ let ctx2 = _Context(x$11,x$11,0) in -- #154 +//│ let* (composed) = _b_a_ctx_comp$6(a_modcons$8_ctx,ctx2) in -- #153 +//│ jump _b_modcons$7_a_modcons$8_opt_jp$10(7,composed,x$9,a_modcons$8_ctx,a_modcons$8_x$0) -- #152 +//│ Nil => +//│ let x$12 = Nil() in -- #164 +//│ let* (res) = _b_a_ctx_app$5(a_modcons$8_ctx,x$12) in -- #163 +//│ res -- #162 +//│ false => +//│ let x$13 = <=(b_modcons$7_n$0,0) in -- #147 +//│ if x$13 -- #146 +//│ true => +//│ let x$15 = Nil() in -- #142 +//│ let x$16 = Cons(0,x$15) in -- #141 +//│ let* (res) = _b_a_ctx_app$5(b_modcons$7_ctx,x$16) in -- #140 +//│ res -- #139 +//│ false => +//│ let x$17 = Nil() in -- #145 +//│ let x$18 = Cons(b_modcons$7_n$0,x$17) in -- #144 +//│ jump _b_modcons$7_a_modcons$8_opt_jp$10(8,b_modcons$7_ctx,b_modcons$7_n$0,b_modcons$7_ctx,x$18) -- #143 +//│ ) +//│ }, +//│ let* (x$20) = b(16) in -- #81 +//│ x$20 -- #80) +//│ +//│ Interpreted: +//│ Cons(64,Cons(56,Cons(48,Cons(40,Cons(32,Cons(24,Cons(16,Cons(8,Cons(0,Nil()))))))))) + +:noTailRec +:interpIR +class Cons(h, t) +class Nil +fun foo(xs) = + if xs is + Cons(h, t) then + if h > 5 then foo(t) + else + val item = if h < 3 then -1 else 100 + Cons(item, Cons(h, foo(t))) + Nil then + Nil +foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) +//│ |#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| +//│ Parsed: {class Cons(h, t,) {}; class Nil {}; fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Def(0, foo, [xs$0], +//│ 1, +//│ case xs$0 of -- #54 +//│ Cons => +//│ let x$1 = xs$0.t in -- #50 +//│ let x$2 = xs$0.h in -- #49 +//│ let x$3 = >(x$2,5) in -- #48 +//│ if x$3 -- #47 +//│ true => +//│ let* (x$5) = foo(x$1) in -- #17 +//│ jump j$1(x$5) -- #16 +//│ false => +//│ let x$6 = <(x$2,3) in -- #46 +//│ if x$6 -- #45 +//│ true => +//│ jump j$2(-1,x$1,x$2) -- #42 +//│ false => +//│ jump j$2(100,x$1,x$2) -- #44 +//│ Nil => +//│ let x$11 = Nil() in -- #53 +//│ jump j$0(x$11) -- #52 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, j$1, [x$4], +//│ 1, +//│ jump j$0(x$4) -- #10 +//│ ) +//│ Def(3, j$2, [x$7,x$1,x$2], +//│ 1, +//│ let* (x$8) = foo(x$1) in -- #40 +//│ let x$9 = Cons(x$2,x$8) in -- #39 +//│ let x$10 = Cons(x$7,x$9) in -- #38 +//│ jump j$1(x$10) -- #37 +//│ ) +//│ }, +//│ let x$12 = Nil() in -- #103 +//│ let x$13 = Cons(9,x$12) in -- #102 +//│ let x$14 = Cons(3,x$13) in -- #101 +//│ let x$15 = Cons(2,x$14) in -- #100 +//│ let x$16 = Cons(4,x$15) in -- #99 +//│ let x$17 = Cons(7,x$16) in -- #98 +//│ let x$18 = Cons(6,x$17) in -- #97 +//│ let x$19 = Cons(1,x$18) in -- #96 +//│ let* (x$20) = foo(x$19) in -- #95 +//│ x$20 -- #94) +//│ +//│ Interpreted: +//│ Cons(-1,Cons(1,Cons(100,Cons(4,Cons(-1,Cons(2,Cons(100,Cons(3,Nil())))))))) + +:interpIR +class Cons(h, t) +class Nil +@tailrec fun foo(xs) = + if xs is + Cons(h, t) then + if h > 5 then @tailcall foo(t) + else + val item = if h < 3 then -1 else 100 + Cons(item, Cons(h, @tailcall foo(t))) + Nil then + Nil +foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) +//│ |#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |@|tailcall| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |@|tailcall| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| +//│ Parsed: {class Cons(h, t,) {}; class Nil {}; fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then @tailcall foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, @tailcall foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Def(0, foo, [xs$0], +//│ 1, +//│ case xs$0 of -- #54 +//│ Cons => +//│ let x$1 = xs$0.t in -- #50 +//│ let x$2 = xs$0.h in -- #49 +//│ let x$3 = >(x$2,5) in -- #48 +//│ if x$3 -- #47 +//│ true => +//│ let* (x$5) = @tailcall foo(x$1) in -- #17 +//│ jump j$1(x$5) -- #16 +//│ false => +//│ let x$6 = <(x$2,3) in -- #46 +//│ if x$6 -- #45 +//│ true => +//│ jump j$2(-1,x$1,x$2) -- #42 +//│ false => +//│ jump j$2(100,x$1,x$2) -- #44 +//│ Nil => +//│ let x$11 = Nil() in -- #53 +//│ jump j$0(x$11) -- #52 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, j$1, [x$4], +//│ 1, +//│ jump j$0(x$4) -- #10 +//│ ) +//│ Def(3, j$2, [x$7,x$1,x$2], +//│ 1, +//│ let* (x$8) = @tailcall foo(x$1) in -- #40 +//│ let x$9 = Cons(x$2,x$8) in -- #39 +//│ let x$10 = Cons(x$7,x$9) in -- #38 +//│ jump j$1(x$10) -- #37 +//│ ) +//│ }, +//│ let x$12 = Nil() in -- #103 +//│ let x$13 = Cons(9,x$12) in -- #102 +//│ let x$14 = Cons(3,x$13) in -- #101 +//│ let x$15 = Cons(2,x$14) in -- #100 +//│ let x$16 = Cons(4,x$15) in -- #99 +//│ let x$17 = Cons(7,x$16) in -- #98 +//│ let x$18 = Cons(6,x$17) in -- #97 +//│ let x$19 = Cons(1,x$18) in -- #96 +//│ let* (x$20) = foo(x$19) in -- #95 +//│ x$20 -- #94) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$1), Set(j$0), Set(foo)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, foo, [xs$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #137 +//│ let* (res) = foo_modcons$7(idCtx,xs$0) in -- #136 +//│ res -- #135 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, j$1, [x$4], +//│ 1, +//│ jump j$0(x$4) -- #10 +//│ ) +//│ Def(4, _foo_ctx_app$4, [ctx,x], +//│ 1, +//│ case ctx of -- #110 +//│ _IdContext => +//│ x -- #109 +//│ _Context => +//│ let field = ctx.field in -- #108 +//│ let ptr = ctx.ptr in -- #107 +//│ let _ = assign ptr.t := x in -- #106 +//│ let acc = ctx.acc in -- #105 +//│ acc -- #104 +//│ ) +//│ Def(5, _foo_ctx_comp$5, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #116 +//│ let ctx2ptr = ctx2.ptr in -- #115 +//│ let ctx2field = ctx2.field in -- #114 +//│ let* (newAcc) = _foo_ctx_app$4(ctx1,ctx2acc) in -- #113 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #112 +//│ ret -- #111 +//│ ) +//│ Def(7, foo_modcons$7, [ctx,xs$0], +//│ 1, +//│ let* (r0) = _foo_modcons$7_j$2_modcons$6_opt$8(7,ctx,xs$0,undefined,undefined,undefined,undefined) in -- #173 +//│ r0 -- #172 +//│ ) +//│ Def(8, _foo_modcons$7_j$2_modcons$6_opt$8, [tailrecBranch$,foo_modcons$7_ctx,foo_modcons$7_xs$0,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2], +//│ 1, +//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(tailrecBranch$,foo_modcons$7_ctx,foo_modcons$7_xs$0,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2) -- #171 +//│ ) +//│ Def(9, _foo_modcons$7_j$2_modcons$6_opt_jp$9, [tailrecBranch$,foo_modcons$7_ctx,foo_modcons$7_xs$0,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2], +//│ 1, +//│ let scrut = ==(6,tailrecBranch$) in -- #170 +//│ if scrut -- #169 +//│ true => +//│ let x$9 = Cons(j$2_modcons$6_x$2,0) in -- #168 +//│ let x$10 = Cons(j$2_modcons$6_x$7,x$9) in -- #167 +//│ let ctx2 = _Context(x$10,x$9,0) in -- #166 +//│ let* (composed) = _foo_ctx_comp$5(j$2_modcons$6_ctx,ctx2) in -- #165 +//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(7,composed,j$2_modcons$6_x$1,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2) -- #164 +//│ false => +//│ case foo_modcons$7_xs$0 of -- #163 +//│ Cons => +//│ let x$1 = foo_modcons$7_xs$0.t in -- #159 +//│ let x$2 = foo_modcons$7_xs$0.h in -- #158 +//│ let x$3 = >(x$2,5) in -- #157 +//│ if x$3 -- #156 +//│ true => +//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(7,foo_modcons$7_ctx,x$1,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2) -- #151 +//│ false => +//│ let x$6 = <(x$2,3) in -- #155 +//│ if x$6 -- #154 +//│ true => +//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(6,foo_modcons$7_ctx,foo_modcons$7_xs$0,foo_modcons$7_ctx,-1,x$1,x$2) -- #152 +//│ false => +//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(6,foo_modcons$7_ctx,foo_modcons$7_xs$0,foo_modcons$7_ctx,100,x$1,x$2) -- #153 +//│ Nil => +//│ let x$11 = Nil() in -- #162 +//│ let* (res) = _foo_ctx_app$4(foo_modcons$7_ctx,x$11) in -- #161 +//│ res -- #160 +//│ ) +//│ }, +//│ let x$12 = Nil() in -- #103 +//│ let x$13 = Cons(9,x$12) in -- #102 +//│ let x$14 = Cons(3,x$13) in -- #101 +//│ let x$15 = Cons(2,x$14) in -- #100 +//│ let x$16 = Cons(4,x$15) in -- #99 +//│ let x$17 = Cons(7,x$16) in -- #98 +//│ let x$18 = Cons(6,x$17) in -- #97 +//│ let x$19 = Cons(1,x$18) in -- #96 +//│ let* (x$20) = foo(x$19) in -- #95 +//│ x$20 -- #94) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, foo, [xs$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #137 +//│ let* (res) = foo_modcons$7(idCtx,xs$0) in -- #136 +//│ res -- #135 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, j$1, [x$4], +//│ 1, +//│ jump j$0(x$4) -- #10 +//│ ) +//│ Def(4, _foo_ctx_app$4, [ctx,x], +//│ 1, +//│ case ctx of -- #110 +//│ _IdContext => +//│ x -- #109 +//│ _Context => +//│ let field = ctx.field in -- #108 +//│ let ptr = ctx.ptr in -- #107 +//│ let _ = assign ptr.t := x in -- #106 +//│ let acc = ctx.acc in -- #105 +//│ acc -- #104 +//│ ) +//│ Def(5, _foo_ctx_comp$5, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #116 +//│ let ctx2ptr = ctx2.ptr in -- #115 +//│ let ctx2field = ctx2.field in -- #114 +//│ let* (newAcc) = _foo_ctx_app$4(ctx1,ctx2acc) in -- #113 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #112 +//│ ret -- #111 +//│ ) +//│ Def(7, foo_modcons$7, [ctx,xs$0], +//│ 1, +//│ let* (r0) = _foo_modcons$7_j$2_modcons$6_opt$8(7,ctx,xs$0,undefined,undefined,undefined,undefined) in -- #173 +//│ r0 -- #172 +//│ ) +//│ Def(8, _foo_modcons$7_j$2_modcons$6_opt$8, [tailrecBranch$,foo_modcons$7_ctx,foo_modcons$7_xs$0,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2], +//│ 1, +//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(tailrecBranch$,foo_modcons$7_ctx,foo_modcons$7_xs$0,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2) -- #171 +//│ ) +//│ Def(9, _foo_modcons$7_j$2_modcons$6_opt_jp$9, [tailrecBranch$,foo_modcons$7_ctx,foo_modcons$7_xs$0,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2], +//│ 1, +//│ let scrut = ==(6,tailrecBranch$) in -- #170 +//│ if scrut -- #169 +//│ true => +//│ let x$9 = Cons(j$2_modcons$6_x$2,0) in -- #168 +//│ let x$10 = Cons(j$2_modcons$6_x$7,x$9) in -- #167 +//│ let ctx2 = _Context(x$10,x$9,0) in -- #166 +//│ let* (composed) = _foo_ctx_comp$5(j$2_modcons$6_ctx,ctx2) in -- #165 +//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(7,composed,j$2_modcons$6_x$1,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2) -- #164 +//│ false => +//│ case foo_modcons$7_xs$0 of -- #163 +//│ Cons => +//│ let x$1 = foo_modcons$7_xs$0.t in -- #159 +//│ let x$2 = foo_modcons$7_xs$0.h in -- #158 +//│ let x$3 = >(x$2,5) in -- #157 +//│ if x$3 -- #156 +//│ true => +//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(7,foo_modcons$7_ctx,x$1,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2) -- #151 +//│ false => +//│ let x$6 = <(x$2,3) in -- #155 +//│ if x$6 -- #154 +//│ true => +//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(6,foo_modcons$7_ctx,foo_modcons$7_xs$0,foo_modcons$7_ctx,-1,x$1,x$2) -- #152 +//│ false => +//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(6,foo_modcons$7_ctx,foo_modcons$7_xs$0,foo_modcons$7_ctx,100,x$1,x$2) -- #153 +//│ Nil => +//│ let x$11 = Nil() in -- #162 +//│ let* (res) = _foo_ctx_app$4(foo_modcons$7_ctx,x$11) in -- #161 +//│ res -- #160 +//│ ) +//│ }, +//│ let x$12 = Nil() in -- #103 +//│ let x$13 = Cons(9,x$12) in -- #102 +//│ let x$14 = Cons(3,x$13) in -- #101 +//│ let x$15 = Cons(2,x$14) in -- #100 +//│ let x$16 = Cons(4,x$15) in -- #99 +//│ let x$17 = Cons(7,x$16) in -- #98 +//│ let x$18 = Cons(6,x$17) in -- #97 +//│ let x$19 = Cons(1,x$18) in -- #96 +//│ let* (x$20) = foo(x$19) in -- #95 +//│ x$20 -- #94) +//│ +//│ Interpreted: +//│ Cons(-1,Cons(1,Cons(100,Cons(4,Cons(-1,Cons(2,Cons(100,Cons(3,Nil())))))))) + +:ce +fun b() = + a() + a() +@tailrec +fun a() = + if 0 < 1 then a() + else b() +a() +//│ |#fun| |b|(||)| |#=|→|a|(||)|↵|a|(||)|←|↵|@|tailrec| |↵|#fun| |a|(||)| |#=| |→|#if| |0| |<| |1| |#then| |a|(||)|↵|#else| |b|(||)|←|↵|a|(||)| +//│ Parsed: {fun b = () => {a(); a()}; fun a = () => {if (<(0,)(1,)) then a() else b()}; a()} +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, b, [], +//│ 1, +//│ let* (x$0) = a() in -- #7 +//│ let* (x$1) = a() in -- #6 +//│ x$1 -- #5 +//│ ) +//│ Def(1, a, [], +//│ 1, +//│ let x$2 = <(0,1) in -- #23 +//│ if x$2 -- #22 +//│ true => +//│ let* (x$4) = a() in -- #16 +//│ jump j$0(x$4) -- #15 +//│ false => +//│ let* (x$5) = b() in -- #21 +//│ jump j$0(x$5) -- #20 +//│ ) +//│ Def(2, j$0, [x$3], +//│ 1, +//│ x$3 -- #11 +//│ ) +//│ }, +//│ let* (x$6) = a() in -- #27 +//│ x$6 -- #26) +//│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec +//│ ║ l.1966: @tailrec +//│ ║ ^^^^^^^ +//│ ╟── it could self-recurse through this call, which may not be a tail-call +//│ ║ l.1964: a() +//│ ╙── ^ +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$0), Set(a, b)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, b, [], +//│ 1, +//│ let* (r0) = _a_b_opt$3(0) in -- #44 +//│ r0 -- #43 +//│ ) +//│ Def(1, a, [], +//│ 1, +//│ let* (r0) = _a_b_opt$3(1) in -- #42 +//│ r0 -- #41 +//│ ) +//│ Def(2, j$0, [x$3], +//│ 1, +//│ x$3 -- #11 +//│ ) +//│ Def(3, _a_b_opt$3, [tailrecBranch$], +//│ 1, +//│ jump _a_b_opt_jp$4(tailrecBranch$) -- #40 +//│ ) +//│ Def(4, _a_b_opt_jp$4, [tailrecBranch$], +//│ 1, +//│ let scrut = ==(0,tailrecBranch$) in -- #39 +//│ if scrut -- #38 +//│ true => +//│ let* (x$0) = a() in -- #37 +//│ jump _a_b_opt_jp$4(1) -- #36 +//│ false => +//│ let x$2 = <(0,1) in -- #35 +//│ if x$2 -- #34 +//│ true => +//│ jump _a_b_opt_jp$4(1) -- #32 +//│ false => +//│ jump _a_b_opt_jp$4(0) -- #33 +//│ ) +//│ }, +//│ let* (x$6) = a() in -- #27 +//│ x$6 -- #26) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, b, [], +//│ 1, +//│ let* (r0) = _a_b_opt$3(0) in -- #44 +//│ r0 -- #43 +//│ ) +//│ Def(1, a, [], +//│ 1, +//│ let* (r0) = _a_b_opt$3(1) in -- #42 +//│ r0 -- #41 +//│ ) +//│ Def(2, j$0, [x$3], +//│ 1, +//│ x$3 -- #11 +//│ ) +//│ Def(3, _a_b_opt$3, [tailrecBranch$], +//│ 1, +//│ jump _a_b_opt_jp$4(tailrecBranch$) -- #40 +//│ ) +//│ Def(4, _a_b_opt_jp$4, [tailrecBranch$], +//│ 1, +//│ let scrut = ==(0,tailrecBranch$) in -- #39 +//│ if scrut -- #38 +//│ true => +//│ let* (x$0) = a() in -- #37 +//│ jump _a_b_opt_jp$4(1) -- #36 +//│ false => +//│ let x$2 = <(0,1) in -- #35 +//│ if x$2 -- #34 +//│ true => +//│ jump _a_b_opt_jp$4(1) -- #32 +//│ false => +//│ jump _a_b_opt_jp$4(0) -- #33 +//│ ) +//│ }, +//│ let* (x$6) = a() in -- #27 +//│ x$6 -- #26) + +:ce +class A(a, b) +@tailrec +fun a() = A(b(), 1) +fun b() = A(c(), @tailcall a()) +fun c() = A(b(), 1) +a() +//│ |#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|c|(||)|,| |@|tailcall| |a|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|a|(||)| +//│ Parsed: {class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(c(), @tailcall a(),); fun c = () => A(b(), 1,); a()} +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b])}, { +//│ Def(0, a, [], +//│ 1, +//│ let* (x$0) = b() in -- #9 +//│ let x$1 = A(x$0,1) in -- #8 +//│ x$1 -- #7 +//│ ) +//│ Def(1, b, [], +//│ 1, +//│ let* (x$2) = c() in -- #22 +//│ let* (x$3) = @tailcall a() in -- #21 +//│ let x$4 = A(x$2,x$3) in -- #20 +//│ x$4 -- #19 +//│ ) +//│ Def(2, c, [], +//│ 1, +//│ let* (x$5) = b() in -- #32 +//│ let x$6 = A(x$5,1) in -- #31 +//│ x$6 -- #30 +//│ ) +//│ }, +//│ let* (x$7) = a() in -- #36 +//│ x$7 -- #35) +//│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec +//│ ║ l.2088: @tailrec +//│ ║ ^^^^^^^ +//│ ╟── it could self-recurse through this call, which may not be a tail-call +//│ ║ l.2090: fun b() = A(c(), @tailcall a()) +//│ ╙── ^ +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(c, b, a)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #80 +//│ let* (res) = a_modcons$7(idCtx) in -- #79 +//│ res -- #78 +//│ ) +//│ Def(1, b, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #72 +//│ let* (res) = b_modcons$6(idCtx) in -- #71 +//│ res -- #70 +//│ ) +//│ Def(2, c, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #63 +//│ let* (res) = c_modcons$5(idCtx) in -- #62 +//│ res -- #61 +//│ ) +//│ Def(3, _c_b_a_ctx_app$3, [ctx,x], +//│ 1, +//│ case ctx of -- #49 +//│ _IdContext => +//│ x -- #48 +//│ _Context => +//│ let field = ctx.field in -- #47 +//│ let scrut = ==(1,field) in -- #46 +//│ if scrut -- #45 +//│ true => +//│ let ptr = ctx.ptr in -- #44 +//│ let _ = assign ptr.b := x in -- #43 +//│ let acc = ctx.acc in -- #42 +//│ acc -- #41 +//│ false => +//│ let ptr = ctx.ptr in -- #40 +//│ let _ = assign ptr.a := x in -- #39 +//│ let acc = ctx.acc in -- #38 +//│ acc -- #37 +//│ ) +//│ Def(4, _c_b_a_ctx_comp$4, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #55 +//│ let ctx2ptr = ctx2.ptr in -- #54 +//│ let ctx2field = ctx2.field in -- #53 +//│ let* (newAcc) = _c_b_a_ctx_app$3(ctx1,ctx2acc) in -- #52 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #51 +//│ ret -- #50 +//│ ) +//│ Def(5, c_modcons$5, [ctx], +//│ 1, +//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(5,ctx,undefined,undefined) in -- #104 +//│ r0 -- #103 +//│ ) +//│ Def(6, b_modcons$6, [ctx], +//│ 1, +//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(6,undefined,ctx,undefined) in -- #106 +//│ r0 -- #105 +//│ ) +//│ Def(7, a_modcons$7, [ctx], +//│ 1, +//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(7,undefined,undefined,ctx) in -- #108 +//│ r0 -- #107 +//│ ) +//│ Def(8, _c_modcons$5_b_modcons$6_a_modcons$7_opt$8, [tailrecBranch$,c_modcons$5_ctx,b_modcons$6_ctx,a_modcons$7_ctx], +//│ 1, +//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(tailrecBranch$,c_modcons$5_ctx,b_modcons$6_ctx,a_modcons$7_ctx) -- #102 +//│ ) +//│ Def(9, _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9, [tailrecBranch$,c_modcons$5_ctx,b_modcons$6_ctx,a_modcons$7_ctx], +//│ 1, +//│ let scrut = ==(7,tailrecBranch$) in -- #101 +//│ if scrut -- #100 +//│ true => +//│ let x$1 = A(0,1) in -- #97 +//│ let ctx2 = _Context(x$1,x$1,0) in -- #96 +//│ let* (composed) = _c_b_a_ctx_comp$4(a_modcons$7_ctx,ctx2) in -- #95 +//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(6,c_modcons$5_ctx,composed,a_modcons$7_ctx) -- #94 +//│ false => +//│ let scrut = ==(6,tailrecBranch$) in -- #99 +//│ if scrut -- #98 +//│ true => +//│ let* (x$2) = c() in -- #93 +//│ let x$4 = A(x$2,0) in -- #92 +//│ let ctx2 = _Context(x$4,x$4,1) in -- #91 +//│ let* (composed) = _c_b_a_ctx_comp$4(b_modcons$6_ctx,ctx2) in -- #90 +//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(7,c_modcons$5_ctx,b_modcons$6_ctx,composed) -- #89 +//│ false => +//│ let x$6 = A(0,1) in -- #88 +//│ let ctx2 = _Context(x$6,x$6,0) in -- #87 +//│ let* (composed) = _c_b_a_ctx_comp$4(c_modcons$5_ctx,ctx2) in -- #86 +//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(6,c_modcons$5_ctx,composed,a_modcons$7_ctx) -- #85 +//│ ) +//│ }, +//│ let* (x$7) = a() in -- #36 +//│ x$7 -- #35) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #80 +//│ let* (res) = a_modcons$7(idCtx) in -- #79 +//│ res -- #78 +//│ ) +//│ Def(1, b, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #72 +//│ let* (res) = b_modcons$6(idCtx) in -- #71 +//│ res -- #70 +//│ ) +//│ Def(2, c, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #63 +//│ let* (res) = c_modcons$5(idCtx) in -- #62 +//│ res -- #61 +//│ ) +//│ Def(3, _c_b_a_ctx_app$3, [ctx,x], +//│ 1, +//│ case ctx of -- #49 +//│ _IdContext => +//│ x -- #48 +//│ _Context => +//│ let field = ctx.field in -- #47 +//│ let scrut = ==(1,field) in -- #46 +//│ if scrut -- #45 +//│ true => +//│ let ptr = ctx.ptr in -- #44 +//│ let _ = assign ptr.b := x in -- #43 +//│ let acc = ctx.acc in -- #42 +//│ acc -- #41 +//│ false => +//│ let ptr = ctx.ptr in -- #40 +//│ let _ = assign ptr.a := x in -- #39 +//│ let acc = ctx.acc in -- #38 +//│ acc -- #37 +//│ ) +//│ Def(4, _c_b_a_ctx_comp$4, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #55 +//│ let ctx2ptr = ctx2.ptr in -- #54 +//│ let ctx2field = ctx2.field in -- #53 +//│ let* (newAcc) = _c_b_a_ctx_app$3(ctx1,ctx2acc) in -- #52 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #51 +//│ ret -- #50 +//│ ) +//│ Def(5, c_modcons$5, [ctx], +//│ 1, +//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(5,ctx,undefined,undefined) in -- #104 +//│ r0 -- #103 +//│ ) +//│ Def(6, b_modcons$6, [ctx], +//│ 1, +//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(6,undefined,ctx,undefined) in -- #106 +//│ r0 -- #105 +//│ ) +//│ Def(7, a_modcons$7, [ctx], +//│ 1, +//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(7,undefined,undefined,ctx) in -- #108 +//│ r0 -- #107 +//│ ) +//│ Def(8, _c_modcons$5_b_modcons$6_a_modcons$7_opt$8, [tailrecBranch$,c_modcons$5_ctx,b_modcons$6_ctx,a_modcons$7_ctx], +//│ 1, +//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(tailrecBranch$,c_modcons$5_ctx,b_modcons$6_ctx,a_modcons$7_ctx) -- #102 +//│ ) +//│ Def(9, _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9, [tailrecBranch$,c_modcons$5_ctx,b_modcons$6_ctx,a_modcons$7_ctx], +//│ 1, +//│ let scrut = ==(7,tailrecBranch$) in -- #101 +//│ if scrut -- #100 +//│ true => +//│ let x$1 = A(0,1) in -- #97 +//│ let ctx2 = _Context(x$1,x$1,0) in -- #96 +//│ let* (composed) = _c_b_a_ctx_comp$4(a_modcons$7_ctx,ctx2) in -- #95 +//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(6,c_modcons$5_ctx,composed,a_modcons$7_ctx) -- #94 +//│ false => +//│ let scrut = ==(6,tailrecBranch$) in -- #99 +//│ if scrut -- #98 +//│ true => +//│ let* (x$2) = c() in -- #93 +//│ let x$4 = A(x$2,0) in -- #92 +//│ let ctx2 = _Context(x$4,x$4,1) in -- #91 +//│ let* (composed) = _c_b_a_ctx_comp$4(b_modcons$6_ctx,ctx2) in -- #90 +//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(7,c_modcons$5_ctx,b_modcons$6_ctx,composed) -- #89 +//│ false => +//│ let x$6 = A(0,1) in -- #88 +//│ let ctx2 = _Context(x$6,x$6,0) in -- #87 +//│ let* (composed) = _c_b_a_ctx_comp$4(c_modcons$5_ctx,ctx2) in -- #86 +//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(6,c_modcons$5_ctx,composed,a_modcons$7_ctx) -- #85 +//│ ) +//│ }, +//│ let* (x$7) = a() in -- #36 +//│ x$7 -- #35) + +// TODO: Purity check +class A(a, b) +@tailrec +fun a() = A(b(), 1) +fun b() = A(@tailcall a(), c()) +fun c() = A(0, 1) +a() +//│ |#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|@|tailcall| |a|(||)|,| |c|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|0|,| |1|)|↵|a|(||)| +//│ Parsed: {class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(@tailcall a(), c(),); fun c = () => A(0, 1,); a()} +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b])}, { +//│ Def(0, a, [], +//│ 1, +//│ let* (x$0) = b() in -- #9 +//│ let x$1 = A(x$0,1) in -- #8 +//│ x$1 -- #7 +//│ ) +//│ Def(1, b, [], +//│ 1, +//│ let* (x$2) = @tailcall a() in -- #22 +//│ let* (x$3) = c() in -- #21 +//│ let x$4 = A(x$2,x$3) in -- #20 +//│ x$4 -- #19 +//│ ) +//│ Def(2, c, [], +//│ 1, +//│ let x$5 = A(0,1) in -- #29 +//│ x$5 -- #28 +//│ ) +//│ }, +//│ let* (x$6) = a() in -- #33 +//│ x$6 -- #32) +//│ ╔══[COMPILATION ERROR] not a tail call, as the remaining functions may be impure +//│ ║ l.2324: fun b() = A(@tailcall a(), c()) +//│ ╙── ^ +//│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec +//│ ║ l.2322: @tailrec +//│ ║ ^^^^^^^ +//│ ╟── it could self-recurse through this call, which may not be a tail-call +//│ ║ l.2324: fun b() = A(@tailcall a(), c()) +//│ ╙── ^ +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(b, a), Set(c)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #62 +//│ let* (res) = a_modcons$6(idCtx) in -- #61 +//│ res -- #60 +//│ ) +//│ Def(1, b, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #54 +//│ let* (res) = b_modcons$5(idCtx) in -- #53 +//│ res -- #52 +//│ ) +//│ Def(2, c, [], +//│ 1, +//│ let x$5 = A(0,1) in -- #29 +//│ x$5 -- #28 +//│ ) +//│ Def(3, _b_a_ctx_app$3, [ctx,x], +//│ 1, +//│ case ctx of -- #40 +//│ _IdContext => +//│ x -- #39 +//│ _Context => +//│ let field = ctx.field in -- #38 +//│ let ptr = ctx.ptr in -- #37 +//│ let _ = assign ptr.a := x in -- #36 +//│ let acc = ctx.acc in -- #35 +//│ acc -- #34 +//│ ) +//│ Def(4, _b_a_ctx_comp$4, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #46 +//│ let ctx2ptr = ctx2.ptr in -- #45 +//│ let ctx2field = ctx2.field in -- #44 +//│ let* (newAcc) = _b_a_ctx_app$3(ctx1,ctx2acc) in -- #43 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #42 +//│ ret -- #41 +//│ ) +//│ Def(5, b_modcons$5, [ctx], +//│ 1, +//│ let* (r0) = _b_modcons$5_a_modcons$6_opt$7(5,ctx,undefined) in -- #81 +//│ r0 -- #80 +//│ ) +//│ Def(6, a_modcons$6, [ctx], +//│ 1, +//│ let* (r0) = _b_modcons$5_a_modcons$6_opt$7(6,undefined,ctx) in -- #83 +//│ r0 -- #82 +//│ ) +//│ Def(7, _b_modcons$5_a_modcons$6_opt$7, [tailrecBranch$,b_modcons$5_ctx,a_modcons$6_ctx], +//│ 1, +//│ jump _b_modcons$5_a_modcons$6_opt_jp$8(tailrecBranch$,b_modcons$5_ctx,a_modcons$6_ctx) -- #79 +//│ ) +//│ Def(8, _b_modcons$5_a_modcons$6_opt_jp$8, [tailrecBranch$,b_modcons$5_ctx,a_modcons$6_ctx], +//│ 1, +//│ let scrut = ==(6,tailrecBranch$) in -- #78 +//│ if scrut -- #77 +//│ true => +//│ let x$1 = A(0,1) in -- #76 +//│ let ctx2 = _Context(x$1,x$1,0) in -- #75 +//│ let* (composed) = _b_a_ctx_comp$4(a_modcons$6_ctx,ctx2) in -- #74 +//│ jump _b_modcons$5_a_modcons$6_opt_jp$8(5,composed,a_modcons$6_ctx) -- #73 +//│ false => +//│ let* (x$2) = @tailcall a() in -- #72 +//│ let* (x$3) = c() in -- #71 +//│ let x$4 = A(x$2,x$3) in -- #70 +//│ let* (res) = _b_a_ctx_app$3(b_modcons$5_ctx,x$4) in -- #69 +//│ res -- #68 +//│ ) +//│ }, +//│ let* (x$6) = a() in -- #33 +//│ x$6 -- #32) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #62 +//│ let* (res) = a_modcons$6(idCtx) in -- #61 +//│ res -- #60 +//│ ) +//│ Def(1, b, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #54 +//│ let* (res) = b_modcons$5(idCtx) in -- #53 +//│ res -- #52 +//│ ) +//│ Def(2, c, [], +//│ 1, +//│ let x$5 = A(0,1) in -- #29 +//│ x$5 -- #28 +//│ ) +//│ Def(3, _b_a_ctx_app$3, [ctx,x], +//│ 1, +//│ case ctx of -- #40 +//│ _IdContext => +//│ x -- #39 +//│ _Context => +//│ let field = ctx.field in -- #38 +//│ let ptr = ctx.ptr in -- #37 +//│ let _ = assign ptr.a := x in -- #36 +//│ let acc = ctx.acc in -- #35 +//│ acc -- #34 +//│ ) +//│ Def(4, _b_a_ctx_comp$4, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #46 +//│ let ctx2ptr = ctx2.ptr in -- #45 +//│ let ctx2field = ctx2.field in -- #44 +//│ let* (newAcc) = _b_a_ctx_app$3(ctx1,ctx2acc) in -- #43 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #42 +//│ ret -- #41 +//│ ) +//│ Def(5, b_modcons$5, [ctx], +//│ 1, +//│ let* (r0) = _b_modcons$5_a_modcons$6_opt$7(5,ctx,undefined) in -- #81 +//│ r0 -- #80 +//│ ) +//│ Def(6, a_modcons$6, [ctx], +//│ 1, +//│ let* (r0) = _b_modcons$5_a_modcons$6_opt$7(6,undefined,ctx) in -- #83 +//│ r0 -- #82 +//│ ) +//│ Def(7, _b_modcons$5_a_modcons$6_opt$7, [tailrecBranch$,b_modcons$5_ctx,a_modcons$6_ctx], +//│ 1, +//│ jump _b_modcons$5_a_modcons$6_opt_jp$8(tailrecBranch$,b_modcons$5_ctx,a_modcons$6_ctx) -- #79 +//│ ) +//│ Def(8, _b_modcons$5_a_modcons$6_opt_jp$8, [tailrecBranch$,b_modcons$5_ctx,a_modcons$6_ctx], +//│ 1, +//│ let scrut = ==(6,tailrecBranch$) in -- #78 +//│ if scrut -- #77 +//│ true => +//│ let x$1 = A(0,1) in -- #76 +//│ let ctx2 = _Context(x$1,x$1,0) in -- #75 +//│ let* (composed) = _b_a_ctx_comp$4(a_modcons$6_ctx,ctx2) in -- #74 +//│ jump _b_modcons$5_a_modcons$6_opt_jp$8(5,composed,a_modcons$6_ctx) -- #73 +//│ false => +//│ let* (x$2) = @tailcall a() in -- #72 +//│ let* (x$3) = c() in -- #71 +//│ let x$4 = A(x$2,x$3) in -- #70 +//│ let* (res) = _b_a_ctx_app$3(b_modcons$5_ctx,x$4) in -- #69 +//│ res -- #68 +//│ ) +//│ }, +//│ let* (x$6) = a() in -- #33 +//│ x$6 -- #32) + +:ce +@tailcall 1 +//│ |@|tailcall| |1| +//│ Parsed: {@tailcall 1} +//│ ╔══[COMPILATION ERROR] @tailcall may only be used to annotate function calls +//│ ║ l.2513: @tailcall 1 +//│ ╙── ^^^^^^^^ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ +//│ }, +//│ 1 -- #0) +//│ +//│ Strongly Connected Tail Calls: +//│ List() +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ +//│ }, +//│ 1 -- #0) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ +//│ }, +//│ 1 -- #0) + +:ce +@tailrec 1 +//│ |@|tailrec| |1| +//│ Parsed: {@tailrec 1} +//│ ╔══[COMPILATION ERROR] @tailrec may only be used to annotate functions +//│ ║ l.2540: @tailrec 1 +//│ ╙── ^^^^^^^ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ +//│ }, +//│ 1 -- #0) +//│ +//│ Strongly Connected Tail Calls: +//│ List() +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ +//│ }, +//│ 1 -- #0) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ +//│ }, +//│ 1 -- #0) + +:ce +fun foo() = + @tailrec foo() +foo() +//│ |#fun| |foo|(||)| |#=|→|@|tailrec| |foo|(||)|←|↵|foo|(||)| +//│ Parsed: {fun foo = () => {@tailrec foo()}; foo()} +//│ ╔══[COMPILATION ERROR] @tailrec is for annotating functions; try @tailcall instead +//│ ║ l.2568: @tailrec foo() +//│ ╙── ^^^^^^^ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, foo, [], +//│ 1, +//│ let* (x$0) = foo() in -- #3 +//│ x$0 -- #2 +//│ ) +//│ }, +//│ let* (x$1) = foo() in -- #7 +//│ x$1 -- #6) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(foo)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(1, foo_jp, [], +//│ 1, +//│ jump foo_jp() -- #8 +//│ ) +//│ Def(2, foo, [], +//│ 1, +//│ let* (r0) = foo_jp() in -- #10 +//│ r0 -- #9 +//│ ) +//│ }, +//│ let* (x$1) = foo() in -- #7 +//│ x$1 -- #6) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(1, foo_jp, [], +//│ 1, +//│ jump foo_jp() -- #8 +//│ ) +//│ Def(2, foo, [], +//│ 1, +//│ let* (r0) = foo_jp() in -- #10 +//│ r0 -- #9 +//│ ) +//│ }, +//│ let* (x$1) = foo() in -- #7 +//│ x$1 -- #6) + +:ce +@tailcall +fun foo() = + foo() +foo() +//│ |@|tailcall|↵|#fun| |foo|(||)| |#=|→|foo|(||)|←|↵|foo|(||)| +//│ Parsed: {fun foo = () => {foo()}; foo()} +//│ ╔══[COMPILATION ERROR] @tailcall is for annotating function calls; try @tailrec instead +//│ ║ l.2619: @tailcall +//│ ╙── ^^^^^^^^ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, foo, [], +//│ 1, +//│ let* (x$0) = foo() in -- #3 +//│ x$0 -- #2 +//│ ) +//│ }, +//│ let* (x$1) = foo() in -- #7 +//│ x$1 -- #6) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(foo)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(1, foo_jp, [], +//│ 1, +//│ jump foo_jp() -- #8 +//│ ) +//│ Def(2, foo, [], +//│ 1, +//│ let* (r0) = foo_jp() in -- #10 +//│ r0 -- #9 +//│ ) +//│ }, +//│ let* (x$1) = foo() in -- #7 +//│ x$1 -- #6) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(1, foo_jp, [], +//│ 1, +//│ jump foo_jp() -- #8 +//│ ) +//│ Def(2, foo, [], +//│ 1, +//│ let* (r0) = foo_jp() in -- #10 +//│ r0 -- #9 +//│ ) +//│ }, +//│ let* (x$1) = foo() in -- #7 +//│ x$1 -- #6) diff --git a/compiler/shared/test/diff-ir/NuScratch.mls b/compiler/shared/test/diff-ir/NuScratch.mls new file mode 100644 index 00000000..90701274 --- /dev/null +++ b/compiler/shared/test/diff-ir/NuScratch.mls @@ -0,0 +1,3 @@ +:NewParser +:ParseOnly +:UseIR diff --git a/compiler/shared/test/scala/mlscript/compiler/Test.scala b/compiler/shared/test/scala/mlscript/compiler/Test.scala index 2875eb95..3707a546 100644 --- a/compiler/shared/test/scala/mlscript/compiler/Test.scala +++ b/compiler/shared/test/scala/mlscript/compiler/Test.scala @@ -1,16 +1,15 @@ -package mlscript.compiler +package mlscript +package compiler -import mlscript.utils.shorthands.* +import utils.shorthands.* import scala.util.control.NonFatal import scala.collection.mutable.StringBuilder -import mlscript.{DiffTests, ModeType, TypingUnit} import mlscript.compiler.TreeDebug -import mlscript.Polyfill import simpledef.SimpleDef class DiffTestCompiler extends DiffTests { import DiffTestCompiler.* - override def postProcess(mode: ModeType, basePath: List[Str], testName: Str, unit: TypingUnit, output: Str => Unit): (List[Str], Option[TypingUnit]) = + override def postProcess(mode: ModeType, basePath: List[Str], testName: Str, unit: TypingUnit, output: Str => Unit, raise: Diagnostic => Unit): (List[Str], Option[TypingUnit]) = val outputBuilder = StringBuilder() var rstUnit = unit; diff --git a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala index 1d6ac40f..94dcacae 100644 --- a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala +++ b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala @@ -1,23 +1,42 @@ -package mlscript.compiler - +package mlscript +package compiler import mlscript.utils.shorthands._ import mlscript.compiler.ir._ import scala.collection.mutable.StringBuilder -import mlscript.{DiffTests, ModeType, TypingUnit} -import mlscript.compiler.ir.{Interpreter, Fresh, FreshInt, Builder} +import mlscript.compiler.optimizer.TailRecOpt class IRDiffTestCompiler extends DiffTests { import IRDiffTestCompiler.* - override def postProcess(mode: ModeType, basePath: List[Str], testName: Str, unit: TypingUnit, output: Str => Unit): (List[Str], Option[TypingUnit]) = + override def postProcess(mode: ModeType, basePath: List[Str], testName: Str, unit: TypingUnit, output: Str => Unit, raise: Diagnostic => Unit): (List[Str], Option[TypingUnit]) = val outputBuilder = StringBuilder() if (mode.useIR || mode.irVerbose) try - output("\n\nIR:") - val gb = Builder(Fresh(), FreshInt(), FreshInt(), FreshInt()) - val graph = gb.buildGraph(unit) - output(graph.toString()) + val fnUid = FreshInt() + val classUid = FreshInt() + val tag = FreshInt() + + val gb = Builder(Fresh(), fnUid, classUid, tag, raise) + val graph_ = gb.buildGraph(unit) + + if !mode.noTailRecOpt then + output("\nIR:") + output(graph_.toString()) + + val graph = + if !mode.noTailRecOpt then + val tailRecOpt = new TailRecOpt(fnUid, classUid, tag, raise) + val (g, comps) = tailRecOpt.run_debug(graph_) + output("\nStrongly Connected Tail Calls:") + output(comps.toString) + g + else + graph_ + + if !mode.noTailRecOpt then + output(graph.toString()) + output("\nPromoted:") output(graph.toString()) var interp_result: Opt[Str] = None @@ -30,10 +49,12 @@ class IRDiffTestCompiler extends DiffTests { catch case err: Exception => output(s"\nIR Processing Failed: ${err.getMessage()}") - output("\n" ++ err.getStackTrace().map(_.toString()).mkString("\n")) + if (mode.irVerbose) then + output("\n" ++ err.getStackTrace().map(_.toString()).mkString("\n")) case err: StackOverflowError => output(s"\nIR Processing Failed: ${err.getMessage()}") - output("\n" ++ err.getStackTrace().map(_.toString()).mkString("\n")) + if (mode.irVerbose) then + output("\n" ++ err.getStackTrace().map(_.toString()).mkString("\n")) (outputBuilder.toString().linesIterator.toList, None) diff --git a/shared/src/main/scala/mlscript/NewParser.scala b/shared/src/main/scala/mlscript/NewParser.scala index 23366eea..4024d653 100644 --- a/shared/src/main/scala/mlscript/NewParser.scala +++ b/shared/src/main/scala/mlscript/NewParser.scala @@ -1385,6 +1385,8 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], newDefs: Bo final def argsOrIf(acc: Ls[Opt[Var] -> (IfBody \/ Fld)], seqAcc: Ls[Statement], allowNewlines: Bool, prec: Int = NoElsePrec) (implicit fe: FoundErr, et: ExpectThen): Ls[Opt[Var] -> (IfBody \/ Fld)] = wrap(acc, seqAcc) { l => + + val anns = parseAnnotations(false) cur match { case Nil => @@ -1436,8 +1438,9 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], newDefs: Bo S(Var(i.toString).withLoc(S(l0))) case _ => N } + // val e = expr(NoElsePrec) -> argMut.isDefined - val e = exprOrIf(prec).map(Fld(FldFlags(argMut.isDefined, argSpec.isDefined, argVal.isDefined), _)) + val e = exprOrIf(prec, true, anns).map(Fld(FldFlags(argMut.isDefined, argSpec.isDefined, argVal.isDefined), _)) def mkSeq = if (seqAcc.isEmpty) argName -> e else e match { case L(_) => ??? diff --git a/shared/src/main/scala/mlscript/Typer.scala b/shared/src/main/scala/mlscript/Typer.scala index b487476c..ad79b34a 100644 --- a/shared/src/main/scala/mlscript/Typer.scala +++ b/shared/src/main/scala/mlscript/Typer.scala @@ -301,9 +301,9 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne NuTypeDef(Als, TN("null"), Nil, N, N, S(Literal(UnitLit(false))), Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil), NuTypeDef(Cls, TN("Annotation"), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil), NuTypeDef(Cls, TN("Code"), (S(VarianceInfo.co) -> TN("T")) :: (S(VarianceInfo.co) -> TN("C")) :: Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil), - NuTypeDef(Cls, TN("Var"), (S(VarianceInfo.in) -> TN("T")) :: (S(VarianceInfo.in) -> TN("C")) :: Nil, N, N, N, TyApp(Var("Code"), TN("T") :: TN("C") :: Nil) :: Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil) - // Not yet implemented, so we do not define it yet - // NuTypeDef(Mod, TN("tailrec"), Nil, N, N, N, Var("Annotation") :: Nil, N, N, TypingUnit(Nil))(N, N, Nil), + NuTypeDef(Cls, TN("Var"), (S(VarianceInfo.in) -> TN("T")) :: (S(VarianceInfo.in) -> TN("C")) :: Nil, N, N, N, TyApp(Var("Code"), TN("T") :: TN("C") :: Nil) :: Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil), + NuTypeDef(Mod, TN("tailrec"), Nil, N, N, N, Var("Annotation") :: Nil, N, N, TypingUnit(Nil))(N, N, Nil), + NuTypeDef(Mod, TN("tailcall"), Nil, N, N, N, Var("Annotation") :: Nil, N, N, TypingUnit(Nil))(N, N, Nil), ) val builtinTypes: Ls[TypeDef] = TypeDef(Cls, TN("?"), Nil, TopType, Nil, Nil, Set.empty, N, Nil) :: // * Dummy for pretty-printing unknown type locations diff --git a/shared/src/test/diff/nu/Annotations.mls b/shared/src/test/diff/nu/Annotations.mls index be99f9cf..e85bb285 100644 --- a/shared/src/test/diff/nu/Annotations.mls +++ b/shared/src/test/diff/nu/Annotations.mls @@ -40,6 +40,40 @@ Foo //│ res //│ = 5 +:e +let x = 1 +@x 2 +//│ ╔══[ERROR] Type mismatch in annotated integer literal: +//│ ║ l.45: @x 2 +//│ ║ ^^^ +//│ ╟── integer literal of type `1` is not an instance of type `Annotation` +//│ ║ l.44: let x = 1 +//│ ║ ^ +//│ ╟── but it flows into reference with expected type `Annotation` +//│ ║ l.45: @x 2 +//│ ╙── ^ +//│ let x: 1 +//│ 2 +//│ x +//│ = 1 +//│ res +//│ = 2 + +:e +let x = 1 +@x +fun foo(x) = 1 +//│ ╔══[ERROR] Type mismatch in definition: +//│ ║ l.65: fun foo(x) = 1 +//│ ║ ^^^^^^^^^^ +//│ ╟── integer literal of type `1` is not an instance of type `Annotation` +//│ ║ l.63: let x = 1 +//│ ╙── ^ +//│ let x: 1 +//│ fun foo: anything -> 1 +//│ x +//│ = 1 + fun foo(n) = if n > 0.5 then log(join of "hi ", String(n)) diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index 4e765b58..e309a415 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -42,6 +42,7 @@ abstract class ModeType { def showRepl: Bool def allowEscape: Bool def useIR: Bool + def noTailRecOpt: Bool def interpIR: Bool def irVerbose: Bool def simpledef: Bool @@ -57,7 +58,7 @@ class DiffTests /** Hook for dependent projects, like the monomorphizer. */ - def postProcess(mode: ModeType, basePath: Ls[Str], testName: Str, unit: TypingUnit, output: Str => Unit): (Ls[Str], Option[TypingUnit]) = (Nil, None) + def postProcess(mode: ModeType, basePath: Ls[Str], testName: Str, unit: TypingUnit, output: Str => Unit, raise: Diagnostic => Unit): (Ls[Str], Option[TypingUnit]) = (Nil, None) def postTypingProcess(mode: ModeType, basePath: Ls[Str], testName: Str, unit: TypingUnit, output: Str => Unit): Option[TypingUnit] = None @@ -150,6 +151,7 @@ class DiffTests case class Mode( expectTypeErrors: Bool = false, + expectCompileErrors: Bool = false, expectWarnings: Bool = false, expectParseErrors: Bool = false, fixme: Bool = false, @@ -181,6 +183,7 @@ class DiffTests lift: Bool = false, nolift: Bool = false, // noProvs: Bool = false, + noTailRecOpt: Bool = false, useIR: Bool = false, interpIR: Bool = false, irVerbose: Bool = false, @@ -191,6 +194,7 @@ class DiffTests var parseOnly = basePath.headOption.contains("parser") var allowTypeErrors = false + var allowCompileErrors = false var allowParseErrors = false var showRelativeLineNums = false var noJavaScript = false @@ -209,6 +213,7 @@ class DiffTests var useIR = false // Enable this to see the errors from unfinished `PreTyper`. var showPreTyperErrors = false + var noTailRec = false // * This option makes some test cases pass which assume generalization should happen in arbitrary arguments // * but it's way too aggressive to be ON by default, as it leads to more extrusion, cycle errors, etc. @@ -229,6 +234,7 @@ class DiffTests out.println(line) val newMode = line.tail.takeWhile(!_.isWhitespace) match { case "e" => mode.copy(expectTypeErrors = true) + case "ce" => mode.copy(expectCompileErrors = true) case "w" => mode.copy(expectWarnings = true) case "pe" => mode.copy(expectParseErrors = true) case "p" => mode.copy(showParse = true) @@ -249,12 +255,14 @@ class DiffTests case "precise-rec-typing" => mode.copy(preciselyTypeRecursion = true) case "ParseOnly" => parseOnly = true; mode case "AllowTypeErrors" => allowTypeErrors = true; mode + case "AllowCompileErrors" => allowCompileErrors = true; mode case "AllowParseErrors" => allowParseErrors = true; mode case "AllowRuntimeErrors" => allowRuntimeErrors = true; mode case "ShowRelativeLineNums" => showRelativeLineNums = true; mode case "NewParser" => newParser = true; mode case "NewDefs" => newParser = true; newDefs = true; mode case "NoJS" => noJavaScript = true; mode + case "NoTailRec" => noTailRec = true; mode case "NoProvs" => noProvs = true; mode case "GeneralizeCurriedFunctions" => generalizeCurriedFunctions = true; mode case "DontGeneralizeCurriedFunctions" => generalizeCurriedFunctions = false; mode @@ -295,6 +303,7 @@ class DiffTests case "escape" => mode.copy(allowEscape = true) case "sd" => {mode.copy(simpledef = true)} case "lift" => {mode.copy(lift = true)} + case "noTailRec" => mode.copy(noTailRecOpt = true) case "nolift" => {mode.copy(nolift = true)} case "exit" => out.println(exitMarker) @@ -359,6 +368,7 @@ class DiffTests var totalTypeErrors = 0 var totalParseErrors = 0 + var totalCompileErrors = 0 var totalWarnings = 0 var totalRuntimeErrors = 0 var totalCodeGenErrors = 0 @@ -376,6 +386,9 @@ class DiffTests case Diagnostic.Parsing => totalParseErrors += 1 s"╔══[PARSE ERROR] " + case Diagnostic.Compilation => + totalCompileErrors += 1 + s"╔══[COMPILATION ERROR] " case _ => // TODO customize too totalTypeErrors += 1 s"╔══[ERROR] " @@ -432,6 +445,9 @@ class DiffTests if (!allowParseErrors && !mode.expectParseErrors && diag.isInstanceOf[ErrorReport] && (diag.source =:= Diagnostic.Lexing || diag.source =:= Diagnostic.Parsing)) { output("TEST CASE FAILURE: There was an unexpected parse error"); failures += globalLineNum } + if (!allowCompileErrors + && !mode.expectCompileErrors && diag.isInstanceOf[ErrorReport] && diag.source =:= Diagnostic.Compilation) + { output("TEST CASE FAILURE: There was an unexpected compilation error"); failures += globalLineNum } if (!allowTypeErrors && !allowParseErrors && !mode.expectWarnings && diag.isInstanceOf[WarningReport]) { output("TEST CASE FAILURE: There was an unexpected warning"); failures += globalLineNum } @@ -468,8 +484,10 @@ class DiffTests if (mode.showParse) output(s"AST: $res") val newMode = if (useIR) { mode.copy(useIR = true) } else mode + val newNewMode = if (noTailRec) { newMode.copy(noTailRecOpt = true) } else newMode + val (postLines, nuRes) = - postProcess(newMode, basePath, testName, res, output) + postProcess(newNewMode, basePath, testName, res, output, raise) postLines.foreach(output) if (parseOnly) @@ -1062,6 +1080,8 @@ class DiffTests { output("TEST CASE FAILURE: There was an unexpected lack of parse error"); failures += blockLineNum } if (mode.expectTypeErrors && totalTypeErrors =:= 0) { output("TEST CASE FAILURE: There was an unexpected lack of type error"); failures += blockLineNum } + if (mode.expectCompileErrors && totalCompileErrors =:= 0) + { output("TEST CASE FAILURE: There was an unexpected lack of compilation error"); failures += blockLineNum } if (mode.expectWarnings && totalWarnings =:= 0) { output("TEST CASE FAILURE: There was an unexpected lack of warning"); failures += blockLineNum } if (mode.expectCodeGenErrors && totalCodeGenErrors =:= 0) From f990dfee2878f111adb0e5ad6406270260608119 Mon Sep 17 00:00:00 2001 From: Lionel Parreaux Date: Tue, 30 Jul 2024 16:20:09 +0800 Subject: [PATCH 136/143] Explicitly derive example of known unsoundness in normal forms --- shared/src/test/diff/mlscript/BooleanFail.mls | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/shared/src/test/diff/mlscript/BooleanFail.mls b/shared/src/test/diff/mlscript/BooleanFail.mls index fc8016dc..c75abe87 100644 --- a/shared/src/test/diff/mlscript/BooleanFail.mls +++ b/shared/src/test/diff/mlscript/BooleanFail.mls @@ -78,3 +78,92 @@ f (forall 'a. fun (x: 'a) -> x) //│ = [Function (anonymous)] +// * Example 2 + +def g(x: 'a | {f: nothing}) = x.f(0) +//│ g: {f: 0 -> 'a} -> 'a +//│ = [Function: g] + +foo = forall 'x. fun (x: 'x) -> g(x) +//│ foo: anything -> nothing +//│ = [Function: foo] + +:re +foo 0 +//│ res: nothing +//│ Runtime error: +//│ TypeError: x.f is not a function + + + +// * Now let's consider why functions and classes can't intersect to nothing due to distributivity + + +class Foo: { x: anything } +//│ Defined class Foo + + +// * These two types should be equivalent, but they visibly aren't: + +def a: (int -> int | {x: int}) & Foo +def b: int -> int & Foo | {x: int} & Foo +//│ a: Foo +//│ = +//│ b: Foo & {x: int} +//│ = + +:ne +ax = a.x +bx = b.x +//│ ax: anything +//│ bx: int + + +// * Yet, this does not immediately lead to unsoundness due to the aggressive normalization +// * performed during constraint solving: + +:ne +a = b +//│ Foo & {x: int} +//│ <: a: +//│ Foo + +:e +:ne +b = a +//│ Foo +//│ <: b: +//│ Foo & {x: int} +//│ ╔══[ERROR] Type mismatch in def definition: +//│ ║ l.133: b = a +//│ ║ ^^^^^ +//│ ╟── expression of type `anything` is not an instance of type `int` +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.109: def b: int -> int & Foo | {x: int} & Foo +//│ ╙── ^^^ + + +// * To expose the unsoundness, we need some indirection with abstract types +// * that prevent eagerly distributing the intersection type: + +class Test1[A, B]: { f: A & Foo } + method M: B & Foo | {x: int} & Foo +//│ Defined class Test1[+A, +B] +//│ Declared Test1.M: Test1[?, 'B] -> (Foo & 'B | Foo & {x: int}) + +class Test2[B]: Test1[B | {x: int}, B] + method M = this.f : B & Foo | {x: int} & Foo +//│ Defined class Test2[+B] +//│ Defined Test2.M: Test2['B] -> (Foo & 'B | Foo & {x: int}) + +oops = (Test2{f = Foo{x = "oops"}} : Test1[anything, int -> int]).M +//│ oops: Foo & {x: int} +//│ = Foo { x: 'oops' } + +// * Notice the type confusion: +oops.x + 1 +//│ res: int +//│ = 'oops1' + + + From 05831320a5d6de263f5cbf4560ab35ae9534317f Mon Sep 17 00:00:00 2001 From: Lionel Parreaux Date: Tue, 30 Jul 2024 16:30:05 +0800 Subject: [PATCH 137/143] Remove unsound simplification of negated record types in positive positions --- .../main/scala/mlscript/TyperHelpers.scala | 3 - shared/src/test/diff/fcp/Overloads.mls | 30 +++-- shared/src/test/diff/mlscript/Annoying.mls | 2 +- shared/src/test/diff/mlscript/BooleanFail.mls | 87 ++++++++---- shared/src/test/diff/mlscript/ExprProb.mls | 126 +++++++++++------- .../src/test/diff/mlscript/ExprProb_Inv.mls | 126 +++++++++++------- shared/src/test/diff/mlscript/Neg.mls | 22 ++- .../src/test/diff/mlscript/StressTraits.mls | 24 ++-- shared/src/test/diff/mlscript/StressUgly.mls | 6 +- .../src/test/diff/mlscript/TraitMatching.mls | 21 ++- shared/src/test/diff/nu/HeungTung.mls | 2 +- 11 files changed, 281 insertions(+), 168 deletions(-) diff --git a/shared/src/main/scala/mlscript/TyperHelpers.scala b/shared/src/main/scala/mlscript/TyperHelpers.scala index e76d3a5a..9b967a1d 100644 --- a/shared/src/main/scala/mlscript/TyperHelpers.scala +++ b/shared/src/main/scala/mlscript/TyperHelpers.scala @@ -646,9 +646,6 @@ abstract class TyperHelpers { Typer: Typer => case ComposedType(false, l, r) => l.negNormPos(f, p) | r.negNormPos(f, p) case NegType(n) => f(n).withProv(p) case tr: TypeRef if !preserveTypeRefs && tr.canExpand => tr.expandOrCrash.negNormPos(f, p) - case _: RecordType | _: FunctionType => BotType // Only valid in positive positions! - // Because Top<:{x:S}|{y:T}, any record type negation neg{x:S}<:{y:T} for any y=/=x, - // meaning negated records are basically bottoms. case rw => NegType(f(rw))(p) } def withProvOf(ty: SimpleType): ST = withProv(ty.prov) diff --git a/shared/src/test/diff/fcp/Overloads.mls b/shared/src/test/diff/fcp/Overloads.mls index 71a328bb..2e0e51a7 100644 --- a/shared/src/test/diff/fcp/Overloads.mls +++ b/shared/src/test/diff/fcp/Overloads.mls @@ -93,33 +93,43 @@ if true then IISS else BBNN //│ res: (0 | 1 | true) -> number -// * Note that type normalization is currently very aggressive at approximating negative non-tag types, to simplify the result: +// * Note that type normalization used to be very aggressive at approximating non-tag type negations, +// * to simplify the result, but this was changed as it was unsound def test: ~(int -> int) -//│ test: in ~(int -> int) out nothing +//│ test: ~(int -> int) -// * Note about this known unsoundness: see test file BooleanFail.mls +// * See also test file BooleanFail.mls about this previous unsoundness +:e test = 42 not test //│ 42 //│ <: test: //│ ~(int -> int) -//│ res: bool +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.105: not test +//│ ║ ^^^^^^^^ +//│ ╟── type `~(int -> int)` is not an instance of type `bool` +//│ ║ l.99: def test: ~(int -> int) +//│ ║ ^^^^^^^^^^^^^ +//│ ╟── but it flows into reference with expected type `bool` +//│ ║ l.105: not test +//│ ╙── ^^^^ +//│ res: bool | error -// :ds def test: ~(int -> int) & ~bool -//│ test: in ~bool & ~(int -> int) out nothing +//│ test: ~bool & ~(int -> int) def test: ~(int -> int) & bool -//│ test: in bool out nothing +//│ test: bool def test: ~(int -> int) & ~(bool -> bool) -//│ test: in ~(nothing -> (bool | int)) out nothing +//│ test: ~(nothing -> (bool | int)) def test: ~(int -> int | bool -> bool) -//│ test: in ~(nothing -> (bool | int)) out nothing +//│ test: ~(nothing -> (bool | int)) def test: ~(int -> int & string -> string) & ~(bool -> bool & number -> number) -//│ test: in ~(nothing -> (number | string) & int -> number & nothing -> (bool | string) & nothing -> (bool | int)) out nothing +//│ test: in ~(nothing -> (number | string) & int -> number & nothing -> (bool | string) & nothing -> (bool | int)) out ~(nothing -> (bool | int) & nothing -> (bool | string) & int -> number & nothing -> (number | string)) diff --git a/shared/src/test/diff/mlscript/Annoying.mls b/shared/src/test/diff/mlscript/Annoying.mls index 2a08eae7..7babae3c 100644 --- a/shared/src/test/diff/mlscript/Annoying.mls +++ b/shared/src/test/diff/mlscript/Annoying.mls @@ -190,7 +190,7 @@ id (error: A) with { x = 1 } : A | { x: 'a } //│ res: A | {x: nothing} def negWeird: ~(~(~(A & { x: int }))) -//│ negWeird: in ~(A & {x: int}) out ~A +//│ negWeird: ~(A & {x: int}) def v = negWeird with { x = 1 } //│ v: ~A\x & {x: 1} | {x: 1} & ~{x: int} diff --git a/shared/src/test/diff/mlscript/BooleanFail.mls b/shared/src/test/diff/mlscript/BooleanFail.mls index c75abe87..aaccada6 100644 --- a/shared/src/test/diff/mlscript/BooleanFail.mls +++ b/shared/src/test/diff/mlscript/BooleanFail.mls @@ -3,28 +3,52 @@ // * The MLscript subtyping system is currently ill-formed in some corner cases. // * Notably, it considers functions and classes to intersect to nothing -// * and also considers positive negated function/record types equivalent to nothing. +// * and also used to considers positive negated function/record types equivalent to nothing. // * (This isn't the case in MLstruct, which has a sound subtyping lattice.) -// * Example 1 + +// * Example 1 – now fixed + oops = 42 : ~(int -> int) -not oops -//│ oops: nothing +//│ oops: ~(int -> int) //│ = 42 -//│ res: bool + +:e +not oops +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.19: not oops +//│ ║ ^^^^^^^^ +//│ ╟── type `~(int -> int)` is not an instance of type `bool` +//│ ║ l.14: oops = 42 : ~(int -> int) +//│ ║ ^^^^^^^^^^^^^ +//│ ╟── but it flows into reference with expected type `bool` +//│ ║ l.19: not oops +//│ ╙── ^^^^ +//│ res: bool | error //│ = false -// * OTOH, this doesn't lead to immediate unsoundness: +// * This was accepted but didn't immediately lead to immediate unsoundness: def f: (~{x: int}) -> 'a -f = id -//│ f: in nothing -> nothing out ~{x: int} -> nothing +//│ f: ~{x: int} -> nothing //│ = + +:e +f = id //│ 'a -> 'a //│ <: f: -//│ nothing -> nothing +//│ ~{x: int} -> nothing +//│ ╔══[ERROR] Type mismatch in def definition: +//│ ║ l.39: f = id +//│ ║ ^^^^^^ +//│ ╟── type `~{x: int}` does not match type `'a` +//│ ║ l.34: def f: (~{x: int}) -> 'a +//│ ║ ^^^^^^^^^^^ +//│ ╟── Note: constraint arises from type variable: +//│ ║ l.34: def f: (~{x: int}) -> 'a +//│ ╙── ^^ //│ = [Function: id] :e @@ -33,64 +57,75 @@ f id f {} f (forall 'a. fun (x: 'a) -> x) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.31: f 0 +//│ ║ l.55: f 0 //│ ║ ^^^ //│ ╟── integer literal of type `0` does not match type `~{x: int}` -//│ ║ l.31: f 0 +//│ ║ l.55: f 0 //│ ║ ^ //│ ╟── Note: constraint arises from type negation: -//│ ║ l.21: def f: (~{x: int}) -> 'a +//│ ║ l.34: def f: (~{x: int}) -> 'a //│ ╙── ^^^^^^^^^^^ //│ res: error //│ = 0 //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.32: f id +//│ ║ l.56: f id //│ ║ ^^^^ //│ ╟── reference of type `?a -> ?a` does not match type `~{x: int}` -//│ ║ l.32: f id +//│ ║ l.56: f id //│ ║ ^^ //│ ╟── Note: constraint arises from type negation: -//│ ║ l.21: def f: (~{x: int}) -> 'a +//│ ║ l.34: def f: (~{x: int}) -> 'a //│ ╙── ^^^^^^^^^^^ //│ res: error //│ = [Function: id] //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.33: f {} +//│ ║ l.57: f {} //│ ║ ^^^^ //│ ╟── record literal of type `anything` does not match type `~{x: int}` -//│ ║ l.33: f {} +//│ ║ l.57: f {} //│ ║ ^^ //│ ╟── Note: constraint arises from type negation: -//│ ║ l.21: def f: (~{x: int}) -> 'a +//│ ║ l.34: def f: (~{x: int}) -> 'a //│ ╙── ^^^^^^^^^^^ //│ res: error //│ = {} //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.34: f (forall 'a. fun (x: 'a) -> x) +//│ ║ l.58: f (forall 'a. fun (x: 'a) -> x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `'a -> 'a` does not match type `~{x: int}` -//│ ║ l.34: f (forall 'a. fun (x: 'a) -> x) +//│ ║ l.58: f (forall 'a. fun (x: 'a) -> x) //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type negation: -//│ ║ l.21: def f: (~{x: int}) -> 'a +//│ ║ l.34: def f: (~{x: int}) -> 'a //│ ╙── ^^^^^^^^^^^ //│ res: error //│ = [Function (anonymous)] -// * Example 2 +// * Example 2 – now fixed def g(x: 'a | {f: nothing}) = x.f(0) //│ g: {f: 0 -> 'a} -> 'a //│ = [Function: g] +:e foo = forall 'x. fun (x: 'x) -> g(x) -//│ foo: anything -> nothing +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.112: foo = forall 'x. fun (x: 'x) -> g(x) +//│ ║ ^^^^ +//│ ╟── expression of type `'x & ~{f: nothing}` does not have field 'f' +//│ ╟── Note: constraint arises from field selection: +//│ ║ l.107: def g(x: 'a | {f: nothing}) = x.f(0) +//│ ║ ^^^ +//│ ╟── from type variable: +//│ ║ l.107: def g(x: 'a | {f: nothing}) = x.f(0) +//│ ╙── ^^ +//│ foo: anything -> error //│ = [Function: foo] :re foo 0 -//│ res: nothing +//│ res: error //│ Runtime error: //│ TypeError: x.f is not a function @@ -135,11 +170,11 @@ b = a //│ <: b: //│ Foo & {x: int} //│ ╔══[ERROR] Type mismatch in def definition: -//│ ║ l.133: b = a +//│ ║ l.168: b = a //│ ║ ^^^^^ //│ ╟── expression of type `anything` is not an instance of type `int` //│ ╟── Note: constraint arises from type reference: -//│ ║ l.109: def b: int -> int & Foo | {x: int} & Foo +//│ ║ l.144: def b: int -> int & Foo | {x: int} & Foo //│ ╙── ^^^ diff --git a/shared/src/test/diff/mlscript/ExprProb.mls b/shared/src/test/diff/mlscript/ExprProb.mls index d960f926..d092b113 100644 --- a/shared/src/test/diff/mlscript/ExprProb.mls +++ b/shared/src/test/diff/mlscript/ExprProb.mls @@ -142,20 +142,6 @@ eval1_ty_ugly //│ = //│ eval1_ty_ugly is not implemented -:stats -def eval1_ty_ugly = eval1 -//│ ('a -> int) -> 'b -> int -//│ where -//│ 'b <: Add[?] & {lhs: 'b, rhs: 'b} | Lit | 'a & ~#Add & ~#Lit -//│ <: eval1_ty_ugly: -//│ ('a -> int) -> 'b -> int -//│ where -//│ 'b <: Add['b] | Lit | 'a & ~Add[?] & ~Lit -//│ = [Function: eval1_ty_ugly] -//│ constrain calls : 71 -//│ annoying calls : 37 -//│ subtyping calls : 580 - :ns def eval1_ty: ('a -> int) -> (Lit | Add['b] | 'a & ~lit & ~add as 'b) -> int //│ eval1_ty: forall 'a 'b. ('a -> int) -> 'b -> int @@ -184,20 +170,6 @@ def eval1_ty = eval1 //│ annoying calls : 37 //│ subtyping calls : 576 -:stats -eval1_ty_ugly = eval1_ty -//│ ('a -> int) -> 'b -> int -//│ where -//│ 'b <: Add['b] | Lit | 'a & ~#Add & ~#Lit -//│ <: eval1_ty_ugly: -//│ ('a -> int) -> 'b -> int -//│ where -//│ 'b <: Add['b] | Lit | 'a & ~Add[?] & ~Lit -//│ = [Function: eval1] -//│ constrain calls : 36 -//│ annoying calls : 33 -//│ subtyping calls : 372 - :stats eval1_ty = eval1_ty_ugly //│ ('a -> int) -> 'b -> int @@ -207,10 +179,11 @@ eval1_ty = eval1_ty_ugly //│ ('a -> int) -> 'b -> int //│ where //│ 'b <: Add['b] | Lit | 'a & ~#Add & ~#Lit -//│ = [Function: eval1] -//│ constrain calls : 208 -//│ annoying calls : 529 -//│ subtyping calls : 2710 +//│ = +//│ eval1_ty_ugly is not implemented +//│ constrain calls : 238 +//│ annoying calls : 565 +//│ subtyping calls : 5737 // Workaround: @@ -233,7 +206,7 @@ def eval1_ty = eval1 //│ 'b <: Add[?] & {lhs: 'b, rhs: 'b} | Lit | 'a & ~#Add & ~#Lit //│ <: eval1_ty: //│ ('a -> int) -> E1['a] -> int -//│ = [Function: eval1_ty2] +//│ = [Function: eval1_ty1] //│ constrain calls : 67 //│ annoying calls : 37 //│ subtyping calls : 471 @@ -570,13 +543,64 @@ prettier22 done (eval2 done) d2 :ShowRelativeLineNums +:stats +:e +def eval1_ty_ugly = eval1 +//│ ('a -> int) -> 'b -> int +//│ where +//│ 'b <: Add[?] & {lhs: 'b, rhs: 'b} | Lit | 'a & ~#Add & ~#Lit +//│ <: eval1_ty_ugly: +//│ ('a -> int) -> 'b -> int +//│ where +//│ 'b <: Add['b] | Lit | 'a & ~Add[?] & ~Lit +//│ ╔══[ERROR] Type mismatch in def definition: +//│ ║ l.+1: def eval1_ty_ugly = eval1 +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── expression of type `'a & (Add[?] & ~{val: int} & ~#Add | Lit & ~{val: int} | ~{val: int} & ~#Add & ~?a)` does not have field 'val' +//│ ╟── Note: constraint arises from field selection: +//│ ║ l.71: | Lit -> e.val +//│ ║ ^^^^^ +//│ ╟── from refined scrutinee: +//│ ║ l.70: rec def eval1 k e = case e of { +//│ ╙── ^ +//│ = [Function: eval1_ty_ugly] +//│ constrain calls : 105 +//│ annoying calls : 121 +//│ subtyping calls : 3819 + +:stats +:e +eval1_ty_ugly = eval1_ty +//│ ('a -> int) -> E1['a] -> int +//│ <: eval1_ty_ugly: +//│ ('a -> int) -> 'b -> int +//│ where +//│ 'b <: Add['b] | Lit | 'a & ~Add[?] & ~Lit +//│ ╔══[ERROR] Type mismatch in def definition: +//│ ║ l.+1: eval1_ty_ugly = eval1_ty +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type `'a & ~Add[?] & ~Lit` does not match type `Add[E1['a0]] | Lit | 'a0 & ~#Add & ~#Lit` +//│ ║ l.132: def eval1_ty_ugly: ('a -> int) -> (Lit | Add['b] | 'a & ~Lit & ~Add[?] as 'b) -> int +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from union type: +//│ ║ l.191: type E1[A] = Lit | Add[E1[A]] | A & ~lit & ~add +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── from applied type reference: +//│ ║ l.192: def eval1_ty: ('a -> int) -> E1['a] -> int +//│ ╙── ^^^^^^ +//│ = [Function: eval1] +//│ constrain calls : 53 +//│ annoying calls : 182 +//│ subtyping calls : 1809 + + :e eval1 done e2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: eval1 done e2 //│ ║ ^^^^^^^^^^^^^ //│ ╟── application of type `Nega[?E] & {Nega#E = ?E, arg: ?arg}` does not match type `nothing` -//│ ║ l.370: def nega arg = Nega { arg } +//│ ║ l.343: def nega arg = Nega { arg } //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.4: def done x = case x of {} @@ -606,7 +630,7 @@ prettier2 done eval1 e1 //│ ║ l.74: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.262: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval1 e1 @@ -618,7 +642,7 @@ prettier2 done eval1 e1 //│ ║ l.73: | _ -> k e //│ ║ ^^^ //│ ╟── from field selection: -//│ ║ l.262: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error //│ = '123' @@ -631,13 +655,13 @@ prettier2 done (eval1 done) e2 //│ ║ l.+1: prettier2 done (eval1 done) e2 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Nega[?E] & {Nega#E = ?E, arg: ?arg}` does not match type `nothing` -//│ ║ l.370: def nega arg = Nega { arg } +//│ ║ l.343: def nega arg = Nega { arg } //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.4: def done x = case x of {} //│ ║ ^ //│ ╟── from field selection: -//│ ║ l.262: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error | string //│ Runtime error: @@ -664,7 +688,7 @@ prettier2 done eval2 //│ ║ l.74: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.262: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ res: error | 'a -> string //│ where @@ -694,7 +718,7 @@ prettier2 done eval2 e1 //│ ║ l.74: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.262: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval2 e1 @@ -703,10 +727,10 @@ prettier2 done eval2 e1 //│ ║ l.18: def lit val = Lit { val } //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.379: | _ -> k x +//│ ║ l.352: | _ -> k x //│ ║ ^^^ //│ ╟── from field selection: -//│ ║ l.262: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error //│ = '123' @@ -732,7 +756,7 @@ prettier2 done eval2 e2 //│ ║ l.74: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.262: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval2 e2 @@ -741,10 +765,10 @@ prettier2 done eval2 e2 //│ ║ l.18: def lit val = Lit { val } //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.379: | _ -> k x +//│ ║ l.352: | _ -> k x //│ ║ ^^^ //│ ╟── from field selection: -//│ ║ l.262: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error //│ = '1-123' @@ -770,19 +794,19 @@ prettier2 done eval2 d2 //│ ║ l.74: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.262: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval2 d2 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Nega[?E] & {Nega#E = ?E, arg: ?arg}` is not a function -//│ ║ l.370: def nega arg = Nega { arg } +//│ ║ l.343: def nega arg = Nega { arg } //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.379: | _ -> k x +//│ ║ l.352: | _ -> k x //│ ║ ^^^ //│ ╟── from field selection: -//│ ║ l.262: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error | string //│ = '-1-1' @@ -808,7 +832,7 @@ prettier2 done eval1 e2 //│ ║ l.74: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.262: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval1 e2 @@ -820,7 +844,7 @@ prettier2 done eval1 e2 //│ ║ l.73: | _ -> k e //│ ║ ^^^ //│ ╟── from field selection: -//│ ║ l.262: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error //│ = '1-123' diff --git a/shared/src/test/diff/mlscript/ExprProb_Inv.mls b/shared/src/test/diff/mlscript/ExprProb_Inv.mls index 782e1a91..b9a37de6 100644 --- a/shared/src/test/diff/mlscript/ExprProb_Inv.mls +++ b/shared/src/test/diff/mlscript/ExprProb_Inv.mls @@ -144,20 +144,6 @@ eval1_ty_ugly //│ = //│ eval1_ty_ugly is not implemented -:stats -def eval1_ty_ugly = eval1 -//│ ('a -> int) -> 'b -> int -//│ where -//│ 'b <: (Add[?] with {lhs: 'b, rhs: 'b}) | Lit | 'a & ~#Add & ~#Lit -//│ <: eval1_ty_ugly: -//│ ('a -> int) -> 'b -> int -//│ where -//│ 'b := Add['b] | Lit | 'a & ~Add[?] & ~Lit -//│ = [Function: eval1_ty_ugly] -//│ constrain calls : 71 -//│ annoying calls : 37 -//│ subtyping calls : 596 - :ns def eval1_ty: ('a -> int) -> (Lit | Add['b] | 'a & ~lit & ~add as 'b) -> int //│ eval1_ty: forall 'a 'b. ('a -> int) -> 'b -> int @@ -186,20 +172,6 @@ def eval1_ty = eval1 //│ annoying calls : 37 //│ subtyping calls : 588 -:stats -eval1_ty_ugly = eval1_ty -//│ ('a -> int) -> 'b -> int -//│ where -//│ 'b := Add['b] | Lit | 'a & ~#Add & ~#Lit -//│ <: eval1_ty_ugly: -//│ ('a -> int) -> 'b -> int -//│ where -//│ 'b := Add['b] | Lit | 'a & ~Add[?] & ~Lit -//│ = [Function: eval1] -//│ constrain calls : 150 -//│ annoying calls : 1810 -//│ subtyping calls : 2699 - :stats eval1_ty = eval1_ty_ugly //│ ('a -> int) -> 'b -> int @@ -209,10 +181,11 @@ eval1_ty = eval1_ty_ugly //│ ('a -> int) -> 'b -> int //│ where //│ 'b := Add['b] | Lit | 'a & ~#Add & ~#Lit -//│ = [Function: eval1] -//│ constrain calls : 752 -//│ annoying calls : 674 -//│ subtyping calls : 71116 +//│ = +//│ eval1_ty_ugly is not implemented +//│ constrain calls : 4870 +//│ annoying calls : 1519 +//│ subtyping calls : 668110 // Workaround: @@ -235,7 +208,7 @@ def eval1_ty = eval1 //│ 'b <: (Add[?] with {lhs: 'b, rhs: 'b}) | Lit | 'a & ~#Add & ~#Lit //│ <: eval1_ty: //│ ('a -> int) -> E1['a] -> int -//│ = [Function: eval1_ty2] +//│ = [Function: eval1_ty1] //│ constrain calls : 67 //│ annoying calls : 37 //│ subtyping calls : 487 @@ -571,13 +544,64 @@ prettier22 done (eval2 done) d2 :ShowRelativeLineNums +:stats +:e +def eval1_ty_ugly = eval1 +//│ ('a -> int) -> 'b -> int +//│ where +//│ 'b <: (Add[?] with {lhs: 'b, rhs: 'b}) | Lit | 'a & ~#Add & ~#Lit +//│ <: eval1_ty_ugly: +//│ ('a -> int) -> 'b -> int +//│ where +//│ 'b := Add['b] | Lit | 'a & ~Add[?] & ~Lit +//│ ╔══[ERROR] Type mismatch in def definition: +//│ ║ l.+1: def eval1_ty_ugly = eval1 +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── expression of type `'a & (Add[?] & ~{val: int} & ~#Add | Lit & ~{val: int} | ~{val: int} & ~#Add & ~?a)` does not have field 'val' +//│ ╟── Note: constraint arises from field selection: +//│ ║ l.73: | Lit -> e.val +//│ ║ ^^^^^ +//│ ╟── from refined scrutinee: +//│ ║ l.72: rec def eval1 k e = case e of { +//│ ╙── ^ +//│ = [Function: eval1_ty_ugly] +//│ constrain calls : 105 +//│ annoying calls : 121 +//│ subtyping calls : 3763 + +:stats +:e +eval1_ty_ugly = eval1_ty +//│ ('a -> int) -> E1['a] -> int +//│ <: eval1_ty_ugly: +//│ ('a -> int) -> 'b -> int +//│ where +//│ 'b := Add['b] | Lit | 'a & ~Add[?] & ~Lit +//│ ╔══[ERROR] Type mismatch in def definition: +//│ ║ l.+1: eval1_ty_ugly = eval1_ty +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type `'a & ~Add[?] & ~Lit` does not match type `Add[E1['a0]] | Lit | 'a0 & ~#Add & ~#Lit` +//│ ║ l.134: def eval1_ty_ugly: ('a -> int) -> (Lit | Add['b] | 'a & ~Lit & ~Add[?] as 'b) -> int +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from union type: +//│ ║ l.193: type E1[A] = Lit | Add[E1[A]] | A & ~lit & ~add +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── from applied type reference: +//│ ║ l.194: def eval1_ty: ('a -> int) -> E1['a] -> int +//│ ╙── ^^^^^^ +//│ = [Function: eval1] +//│ constrain calls : 200 +//│ annoying calls : 9801 +//│ subtyping calls : 21512 + + :e eval1 done e2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: eval1 done e2 //│ ║ ^^^^^^^^^^^^^ //│ ╟── application of type `Nega[?E] & {Nega#E = ?E, arg: ?arg}` does not match type `nothing` -//│ ║ l.372: def nega arg = Nega { arg } +//│ ║ l.345: def nega arg = Nega { arg } //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.4: def done x = case x of {} @@ -607,7 +631,7 @@ prettier2 done eval1 e1 //│ ║ l.76: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.264: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval1 e1 @@ -619,7 +643,7 @@ prettier2 done eval1 e1 //│ ║ l.75: | _ -> k e //│ ║ ^^^ //│ ╟── from field selection: -//│ ║ l.264: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error //│ = '123' @@ -632,13 +656,13 @@ prettier2 done (eval1 done) e2 //│ ║ l.+1: prettier2 done (eval1 done) e2 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Nega[?E] & {Nega#E = ?E, arg: ?arg}` does not match type `nothing` -//│ ║ l.372: def nega arg = Nega { arg } +//│ ║ l.345: def nega arg = Nega { arg } //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.4: def done x = case x of {} //│ ║ ^ //│ ╟── from field selection: -//│ ║ l.264: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error | string //│ Runtime error: @@ -665,7 +689,7 @@ prettier2 done eval2 //│ ║ l.76: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.264: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ res: error | 'a -> string //│ where @@ -695,7 +719,7 @@ prettier2 done eval2 e1 //│ ║ l.76: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.264: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval2 e1 @@ -704,10 +728,10 @@ prettier2 done eval2 e1 //│ ║ l.19: def lit val = Lit { val } //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.381: | _ -> k x +//│ ║ l.354: | _ -> k x //│ ║ ^^^ //│ ╟── from field selection: -//│ ║ l.264: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error //│ = '123' @@ -733,7 +757,7 @@ prettier2 done eval2 e2 //│ ║ l.76: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.264: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval2 e2 @@ -742,10 +766,10 @@ prettier2 done eval2 e2 //│ ║ l.19: def lit val = Lit { val } //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.381: | _ -> k x +//│ ║ l.354: | _ -> k x //│ ║ ^^^ //│ ╟── from field selection: -//│ ║ l.264: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error //│ = '1-123' @@ -771,19 +795,19 @@ prettier2 done eval2 d2 //│ ║ l.76: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.264: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval2 d2 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Nega[?E] & {Nega#E = ?E, arg: ?arg}` is not a function -//│ ║ l.372: def nega arg = Nega { arg } +//│ ║ l.345: def nega arg = Nega { arg } //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.381: | _ -> k x +//│ ║ l.354: | _ -> k x //│ ║ ^^^ //│ ╟── from field selection: -//│ ║ l.264: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error | string //│ = '-1-1' @@ -809,7 +833,7 @@ prettier2 done eval1 e2 //│ ║ l.76: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.264: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval1 e2 @@ -821,7 +845,7 @@ prettier2 done eval1 e2 //│ ║ l.75: | _ -> k e //│ ║ ^^^ //│ ╟── from field selection: -//│ ║ l.264: else if ev e.rhs == 0 then prettier1 k ev e.lhs +//│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error //│ = '1-123' diff --git a/shared/src/test/diff/mlscript/Neg.mls b/shared/src/test/diff/mlscript/Neg.mls index e3f02f97..dca2ae63 100644 --- a/shared/src/test/diff/mlscript/Neg.mls +++ b/shared/src/test/diff/mlscript/Neg.mls @@ -128,25 +128,37 @@ def f: (~Foo[1 | 2] & 'a | ~Foo[2 | 3] & 'a) -> 'a //│ f: ('a & ~Foo[?]) -> 'a //│ = -// * Notice the weird negative (in) type, and also see BooleanFail.mls +// * Type ~{x: 1 | 2} & 'a | ~{x: 2 | 3} & 'a +// * is ~({x: 1 | 2} & {x: 2 | 3}) & 'a +// * is ~{x: 2}) & 'a def f: (~{x: 1 | 2} & 'a | ~{x: 2 | 3} & 'a) -> 'a -//│ f: in forall 'a. nothing -> 'a out forall 'a. ('a & ~{x: 2}) -> 'a +//│ f: ('a & ~{x: 2}) -> 'a //│ = f = id //│ 'a -> 'a //│ <: f: -//│ nothing -> nothing +//│ ('a & ~{x: 2}) -> 'a //│ = [Function: id] +:e f x = case x of {} //│ nothing -> nothing //│ <: f: -//│ nothing -> nothing +//│ ('a & ~{x: 2}) -> 'a +//│ ╔══[ERROR] Type mismatch in def definition: +//│ ║ l.145: f x = case x of {} +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ╟── type `'a & ~{x: 1 | 2}` does not match type `nothing` +//│ ║ l.134: def f: (~{x: 1 | 2} & 'a | ~{x: 2 | 3} & 'a) -> 'a +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from reference: +//│ ║ l.145: f x = case x of {} +//│ ╙── ^ //│ = [Function: f2] def f: (~{x: 1 | 2} & ~lit & 'a | ~{x: 2 | 3} & ~lit & 'a) -> 'a -//│ f: in forall 'a. nothing -> 'a out forall 'a. ('a & ~{x: 2} & ~#Lit) -> 'a +//│ f: ('a & ~{x: 2} & ~#Lit) -> 'a //│ = diff --git a/shared/src/test/diff/mlscript/StressTraits.mls b/shared/src/test/diff/mlscript/StressTraits.mls index df63bffe..93910bee 100644 --- a/shared/src/test/diff/mlscript/StressTraits.mls +++ b/shared/src/test/diff/mlscript/StressTraits.mls @@ -63,7 +63,7 @@ foo arg //│ res: error | int //│ constrain calls : 36 //│ annoying calls : 31 -//│ subtyping calls : 698 +//│ subtyping calls : 724 :stats :e @@ -83,7 +83,7 @@ foo arg //│ res: error | int //│ constrain calls : 65 //│ annoying calls : 90 -//│ subtyping calls : 3208 +//│ subtyping calls : 3498 :stats :e @@ -103,7 +103,7 @@ foo arg //│ res: error //│ constrain calls : 82 //│ annoying calls : 209 -//│ subtyping calls : 16245 +//│ subtyping calls : 17621 // ====== 2 ====== // @@ -131,7 +131,7 @@ foo arg //│ res: error | int //│ constrain calls : 44 //│ annoying calls : 31 -//│ subtyping calls : 494 +//│ subtyping calls : 520 // ====== 3 ====== // @@ -160,7 +160,7 @@ foo arg //│ res: error | int //│ constrain calls : 76 //│ annoying calls : 90 -//│ subtyping calls : 2989 +//│ subtyping calls : 3279 // ====== 4 ====== // @@ -190,7 +190,7 @@ foo arg //│ res: error //│ constrain calls : 94 //│ annoying calls : 131 -//│ subtyping calls : 3984 +//│ subtyping calls : 4350 :stats :e @@ -208,7 +208,7 @@ foo (arg with { x = 1} with { y = 2 }) //│ res: error //│ constrain calls : 69 //│ annoying calls : 128 -//│ subtyping calls : 3450 +//│ subtyping calls : 3820 :stats :e @@ -226,7 +226,7 @@ foo (arg with { x = 1; y = 2; z = 3 }) //│ res: error //│ constrain calls : 69 //│ annoying calls : 128 -//│ subtyping calls : 3450 +//│ subtyping calls : 3820 // ====== 5 ====== // @@ -257,7 +257,7 @@ foo arg //│ res: error //│ constrain calls : 98 //│ annoying calls : 131 -//│ subtyping calls : 4272 +//│ subtyping calls : 4638 // ====== 6 ====== // @@ -289,7 +289,7 @@ foo arg //│ res: error //│ constrain calls : 102 //│ annoying calls : 131 -//│ subtyping calls : 4618 +//│ subtyping calls : 4984 // ====== 7 ====== // @@ -322,7 +322,7 @@ foo arg //│ res: error //│ constrain calls : 106 //│ annoying calls : 131 -//│ subtyping calls : 5027 +//│ subtyping calls : 5393 def foo_manual: ({fA: 'a} & a | {fB: 'a} & b & ~a | {fC: 'a} & c & ~a & ~b | {fD: 'a} & d & ~a & ~b & ~c | {fE: 'a} & e & ~a & ~b & ~c & ~d | {fF: 'a} & f & ~a & ~b & ~c & ~d & ~e | {fG: 'a} & g & ~a & ~b & ~c & ~d & ~e & ~f) -> 'a //│ foo_manual: ({fA: 'a} & #A | ~#A & ({fB: 'a} & #B | ~#B & ({fC: 'a} & #C | ~#C & ({fD: 'a} & #D | ~#D & ({fE: 'a} & #E | ~#E & ({fF: 'a} & #F | {fG: 'a} & #G & ~#F)))))) -> 'a @@ -388,6 +388,6 @@ foo arg //│ res: error //│ constrain calls : 110 //│ annoying calls : 131 -//│ subtyping calls : 5504 +//│ subtyping calls : 5870 diff --git a/shared/src/test/diff/mlscript/StressUgly.mls b/shared/src/test/diff/mlscript/StressUgly.mls index e5d4c568..b25c5562 100644 --- a/shared/src/test/diff/mlscript/StressUgly.mls +++ b/shared/src/test/diff/mlscript/StressUgly.mls @@ -44,7 +44,7 @@ eval1_ty = eval1_ty_ugly //│ ╙── ^^^ //│ = //│ eval1_ty_ugly is not implemented -//│ constrain calls : 49 -//│ annoying calls : 42 -//│ subtyping calls : 397 +//│ constrain calls : 54 +//│ annoying calls : 46 +//│ subtyping calls : 433 diff --git a/shared/src/test/diff/mlscript/TraitMatching.mls b/shared/src/test/diff/mlscript/TraitMatching.mls index 9604faa4..e95a3cdd 100644 --- a/shared/src/test/diff/mlscript/TraitMatching.mls +++ b/shared/src/test/diff/mlscript/TraitMatching.mls @@ -144,8 +144,19 @@ def strawman: C2 & ~MyTrait[anything] //│ strawman: C2 & ~MyTrait[?] //│ = +:e test2 strawman -//│ res: string +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.148: test2 strawman +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── expression of type `C2 & ~{value: anything} & ~?a | C2 & #MyTrait & ~{value: anything}` does not have field 'value' +//│ ╟── Note: constraint arises from field selection: +//│ ║ l.45: def test2 x = case x of { MyTrait -> x.value | _ -> x.default } +//│ ║ ^^^^^^^ +//│ ╟── from refined scrutinee: +//│ ║ l.45: def test2 x = case x of { MyTrait -> x.value | _ -> x.default } +//│ ╙── ^ +//│ res: error | string //│ = //│ strawman is not implemented @@ -157,18 +168,18 @@ strawman: C2 :e strawman: ~{ value: anything } //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.158: strawman: ~{ value: anything } +//│ ║ l.169: strawman: ~{ value: anything } //│ ║ ^^^^^^^^ //│ ╟── type `C2 & ~MyTrait[?]` does not match type `~{value: anything}` //│ ║ l.143: def strawman: C2 & ~MyTrait[anything] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `~{value: anything}` -//│ ║ l.158: strawman: ~{ value: anything } +//│ ║ l.169: strawman: ~{ value: anything } //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from type negation: -//│ ║ l.158: strawman: ~{ value: anything } +//│ ║ l.169: strawman: ~{ value: anything } //│ ╙── ^^^^^^^^^^^^^^^^^^^^ -//│ res: nothing +//│ res: ~{value: anything} //│ = //│ strawman is not implemented diff --git a/shared/src/test/diff/nu/HeungTung.mls b/shared/src/test/diff/nu/HeungTung.mls index 3b0c9c24..cace8c82 100644 --- a/shared/src/test/diff/nu/HeungTung.mls +++ b/shared/src/test/diff/nu/HeungTung.mls @@ -251,7 +251,7 @@ f: (Int -> Int) & (Bool -> Bool) // * Notice: in positive position, this is equivalent to Bottom fun x: ~{ a: Int } -//│ fun x: nothing +//│ fun x: ~{a: Int} class A() From dd9a0cdac766f08aab0a39f3cf7e039cab0fd62c Mon Sep 17 00:00:00 2001 From: Lionel Parreaux Date: Tue, 30 Jul 2024 16:55:10 +0800 Subject: [PATCH 138/143] Update NodeTests --- shared/src/test/scala/mlscript/NodeTest.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/shared/src/test/scala/mlscript/NodeTest.scala b/shared/src/test/scala/mlscript/NodeTest.scala index f10b7d89..f0de650d 100644 --- a/shared/src/test/scala/mlscript/NodeTest.scala +++ b/shared/src/test/scala/mlscript/NodeTest.scala @@ -18,6 +18,7 @@ class NodeTests extends org.scalatest.funsuite.AnyFunSuite { || v.startsWith("v19") || v.startsWith("v20") || v.startsWith("v21") + || v.startsWith("v22") ) } From 4a5a0387a6bd12c93de153a6c510f868f7342aa2 Mon Sep 17 00:00:00 2001 From: Lionel Parreaux Date: Tue, 30 Jul 2024 17:32:14 +0800 Subject: [PATCH 139/143] Clean up DiffTests and fix its use in `compiler` subproject --- build.sbt | 2 + .../test/scala/mlscript/compiler/Test.scala | 20 ++-- .../test/scala/mlscript/compiler/TestIR.scala | 19 +--- .../src/test/scala/mlscript/DiffTests.scala | 95 ++++++++----------- 4 files changed, 51 insertions(+), 85 deletions(-) diff --git a/build.sbt b/build.sbt index 458788c5..883cb517 100644 --- a/build.sbt +++ b/build.sbt @@ -83,6 +83,8 @@ lazy val compiler = crossProject(JSPlatform, JVMPlatform).in(file("compiler")) sourceDirectory := baseDirectory.value.getParentFile()/"shared", watchSources += WatchSource( baseDirectory.value.getParentFile()/"shared"/"test"/"diff", "*.mls", NothingFilter), + watchSources += WatchSource( + baseDirectory.value.getParentFile()/"shared"/"test"/"diff-ir", "*.mls", NothingFilter), ) .dependsOn(mlscript % "compile->compile;test->test") diff --git a/compiler/shared/test/scala/mlscript/compiler/Test.scala b/compiler/shared/test/scala/mlscript/compiler/Test.scala index 3707a546..752dc909 100644 --- a/compiler/shared/test/scala/mlscript/compiler/Test.scala +++ b/compiler/shared/test/scala/mlscript/compiler/Test.scala @@ -7,8 +7,10 @@ import scala.collection.mutable.StringBuilder import mlscript.compiler.TreeDebug import simpledef.SimpleDef -class DiffTestCompiler extends DiffTests { - import DiffTestCompiler.* +import DiffTestCompiler.* + +class DiffTestCompiler extends DiffTests(State) { + override def postProcess(mode: ModeType, basePath: List[Str], testName: Str, unit: TypingUnit, output: Str => Unit, raise: Diagnostic => Unit): (List[Str], Option[TypingUnit]) = val outputBuilder = StringBuilder() @@ -47,21 +49,11 @@ class DiffTestCompiler extends DiffTests { } None - override protected lazy val files = allFiles.filter { file => - val fileName = file.baseName - validExt(file.ext) && filter(file.relativeTo(pwd)) - } } object DiffTestCompiler { - - private val pwd = os.pwd - private val dir = pwd/"compiler"/"shared"/"test"/"diff" - private val allFiles = os.walk(dir).filter(_.toIO.isFile) - - private val validExt = Set("fun", "mls") - - private def filter(file: os.RelPath) = DiffTests.filter(file) + lazy val State = + new DiffTests.State(DiffTests.pwd/"compiler"/"shared"/"test"/"diff") } diff --git a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala index 94dcacae..565769be 100644 --- a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala +++ b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala @@ -6,8 +6,9 @@ import mlscript.compiler.ir._ import scala.collection.mutable.StringBuilder import mlscript.compiler.optimizer.TailRecOpt -class IRDiffTestCompiler extends DiffTests { - import IRDiffTestCompiler.* +import IRDiffTestCompiler.* + +class IRDiffTestCompiler extends DiffTests(State) { override def postProcess(mode: ModeType, basePath: List[Str], testName: Str, unit: TypingUnit, output: Str => Unit, raise: Diagnostic => Unit): (List[Str], Option[TypingUnit]) = val outputBuilder = StringBuilder() @@ -58,21 +59,11 @@ class IRDiffTestCompiler extends DiffTests { (outputBuilder.toString().linesIterator.toList, None) - override protected lazy val files = allFiles.filter { file => - val fileName = file.baseName - validExt(file.ext) && filter(file.relativeTo(pwd)) - } } object IRDiffTestCompiler { - - private val pwd = os.pwd - private val dir = pwd/"compiler"/"shared"/"test"/"diff-ir" - private val allFiles = os.walk(dir).filter(_.toIO.isFile) - - private val validExt = Set("fun", "mls") - - private def filter(file: os.RelPath) = DiffTests.filter(file) + lazy val State = + new DiffTests.State(DiffTests.pwd/"compiler"/"shared"/"test"/"diff-ir") } diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index e309a415..8fb9ad1c 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -12,6 +12,7 @@ import org.scalatest.{funsuite, ParallelTestExecution} import org.scalatest.time._ import org.scalatest.concurrent.{TimeLimitedTests, Signaler} import pretyper.PreTyper +import os.Path abstract class ModeType { def expectTypeErrors: Bool @@ -50,12 +51,15 @@ abstract class ModeType { def nolift: Bool } -class DiffTests +class DiffTests(state: DiffTests.State) extends funsuite.AnyFunSuite with ParallelTestExecution with TimeLimitedTests { + def this() = this(DiffTests.State) + + import state._ /** Hook for dependent projects, like the monomorphizer. */ def postProcess(mode: ModeType, basePath: Ls[Str], testName: Str, unit: TypingUnit, output: Str => Unit, raise: Diagnostic => Unit): (Ls[Str], Option[TypingUnit]) = (Nil, None) @@ -65,14 +69,12 @@ class DiffTests @SuppressWarnings(Array("org.wartremover.warts.RedundantIsInstanceOf")) private val inParallel = isInstanceOf[ParallelTestExecution] - import DiffTests._ - // scala test will not execute a test if the test class has constructor parameters. // override this to get the correct paths of test files. protected lazy val files = allFiles.filter { file => val fileName = file.baseName // validExt(file.ext) && filter(fileName) - validExt(file.ext) && filter(file.relativeTo(pwd)) + validExt(file.ext) && filter(file.relativeTo(DiffTests.pwd)) } val timeLimit = TimeLimit @@ -240,7 +242,7 @@ class DiffTests case "p" => mode.copy(showParse = true) case "d" => mode.copy(dbg = true) case "dp" => mode.copy(dbgParsing = true) - case DebugUCSFlags(x) => mode.copy(dbgUCS = mode.dbgUCS.fold(S(x))(y => S(y ++ x))) + case DiffTests.DebugUCSFlags(x) => mode.copy(dbgUCS = mode.dbgUCS.fold(S(x))(y => S(y ++ x))) case "ds" => mode.copy(dbgSimplif = true) case "dl" => mode.copy(dbgLifting = true) case "dd" => mode.copy(dbgDefunc = true) @@ -1139,61 +1141,40 @@ class DiffTests object DiffTests { - private val TimeLimit = - if (sys.env.get("CI").isDefined) Span(60, Seconds) - else Span(30, Seconds) - - private val pwd = os.pwd - private val dir = pwd/"shared"/"src"/"test"/"diff" - - private val allFiles = os.walk(dir).filter(_.toIO.isFile) + val pwd: Path = os.pwd - private val validExt = Set("fun", "mls") + lazy val State = new State(pwd/"shared"/"src"/"test"/"diff") - // Aggregate unstaged modified files to only run the tests on them, if there are any - private val modified: Set[os.RelPath] = - try os.proc("git", "status", "--porcelain", dir).call().out.lines().iterator.flatMap { gitStr => - println(" [git] " + gitStr) - val prefix = gitStr.take(2) - val filePath = os.RelPath(gitStr.drop(3)) - if (prefix =:= "A " || prefix =:= "M " || prefix =:= "R " || prefix =:= "D ") - N // * Disregard modified files that are staged - else S(filePath) - }.toSet catch { - case err: Throwable => System.err.println("/!\\ git command failed with: " + err) - Set.empty - } - - // Allow overriding which specific tests to run, sometimes easier for development: - private val focused = Set[Str]( - // "LetRec" - // "Ascribe", - // "Repro", - // "RecursiveTypes", - // "Simple", - // "Inherit", - // "Basics", - // "Paper", - // "Negations", - // "RecFuns", - // "With", - // "Annoying", - // "Tony", - // "Lists", - // "Traits", - // "BadTraits", - // "TraitMatching", - // "Subsume", - // "Methods", - ).map(os.RelPath(_)) - // private def filter(name: Str): Bool = - def filter(file: os.RelPath): Bool = { - if (focused.nonEmpty) focused(file) else modified(file) || modified.isEmpty && - true - // name.startsWith("new/") - // file.segments.toList.init.lastOption.contains("parser") + class State(val dir: Path) { + + val TimeLimit: Span = + if (sys.env.get("CI").isDefined) Span(60, Seconds) + else Span(30, Seconds) + + val allFiles: IndexedSeq[Path] = os.walk(dir).filter(_.toIO.isFile) + + val validExt: Set[String] = Set("fun", "mls") + + // Aggregate unstaged modified files to only run the tests on them, if there are any + val modified: Set[os.RelPath] = + try os.proc("git", "status", "--porcelain", dir).call().out.lines().iterator.flatMap { gitStr => + println(" [git] " + gitStr) + val prefix = gitStr.take(2) + val filePath = os.RelPath(gitStr.drop(3)) + if (prefix =:= "A " || prefix =:= "M " || prefix =:= "R " || prefix =:= "D ") + N // * Disregard modified files that are staged + else S(filePath) + }.toSet catch { + case err: Throwable => System.err.println("/!\\ git command failed with: " + err) + Set.empty + } + + // private def filter(name: Str): Bool = + def filter(file: os.RelPath): Bool = + modified(file) || modified.isEmpty + } - + object DebugUCSFlags { // E.g. "ducs", "ducs:foo", "ducs:foo,bar", "ducs:a.b.c,foo" private val pattern = "^ducs(?::(\\s*(?:[A-Za-z\\.-]+)(?:,\\s*[A-Za-z\\.-]+)*))?$".r From aa182b165c56126d3aed2a59dc703da5b0389d25 Mon Sep 17 00:00:00 2001 From: Lionel Parreaux Date: Thu, 12 Sep 2024 21:41:21 +0800 Subject: [PATCH 140/143] Add test from ICFP interaction --- shared/src/test/diff/nu/Sidney.mls | 53 ++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 shared/src/test/diff/nu/Sidney.mls diff --git a/shared/src/test/diff/nu/Sidney.mls b/shared/src/test/diff/nu/Sidney.mls new file mode 100644 index 00000000..164fa7d0 --- /dev/null +++ b/shared/src/test/diff/nu/Sidney.mls @@ -0,0 +1,53 @@ +:NewDefs + + +fun swapMaybe(x, y) = + if true then [x, y] else [y, x] +//│ fun swapMaybe: forall 'a. ('a, 'a) -> ['a, 'a] + +swapMaybe : forall 'a, 'b: ('a, 'b) -> ['a | 'b, 'b | 'a] +//│ forall 'a. ('a, 'a) -> ['a, 'a] +//│ res +//│ = [Function: swapMaybe] + + +fun test(x, y, z) = + let xy = swapMaybe(x, y) + let yz = swapMaybe(xy.1, z) + [xy.0, yz.0, yz.1] +//│ fun test: forall 'a 'b. ('a, 'a, 'b) -> ['a, 'a | 'b, 'b | 'a] + +:e +test : forall 'a, 'b: ('a, 'b, 'b) -> ['a | 'b, 'a | 'b, 'b] +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.21: test : forall 'a, 'b: ('a, 'b, 'b) -> ['a | 'b, 'a | 'b, 'b] +//│ ║ ^^^^ +//│ ╟── type `'a` does not match type `'b` +//│ ║ l.21: test : forall 'a, 'b: ('a, 'b, 'b) -> ['a | 'b, 'a | 'b, 'b] +//│ ║ ^^ +//│ ╟── Note: constraint arises from type variable: +//│ ║ l.21: test : forall 'a, 'b: ('a, 'b, 'b) -> ['a | 'b, 'a | 'b, 'b] +//│ ║ ^^ +//│ ╟── Note: quantified type variable 'a is defined at: +//│ ║ l.21: test : forall 'a, 'b: ('a, 'b, 'b) -> ['a | 'b, 'a | 'b, 'b] +//│ ╙── ^^ +//│ forall 'a 'b. ('a, 'b, 'b) -> ['a | 'b, 'a | 'b, 'b] +//│ res +//│ = [Function: test] + + +fun test(x, y, z) = + if true then + let xy = swapMaybe(x, y) + [xy.0, xy.1, z] + else + let yz = swapMaybe(y, z) + [x, yz.0, yz.1] +//│ fun test: forall 'a 'b. ('a, 'a & 'b, 'b) -> ['a, 'a | 'b, 'b] + +test : forall 'a: ('a, 'a, 'a) -> ['a, 'a, 'a] +//│ forall 'a. ('a, 'a, 'a) -> ['a, 'a, 'a] +//│ res +//│ = [Function: test1] + + From c389926fa4ec63779bd0906c67a0ef1261baf4bf Mon Sep 17 00:00:00 2001 From: Waterlens Date: Thu, 3 Oct 2024 17:25:33 +0800 Subject: [PATCH 141/143] Sync IR changes (#225) --- .github/workflows/nix.yml | 23 + .github/workflows/scala.yml | 27 - build.sbt | 6 +- .../compiler/{optimizer => }/Document.scala | 4 +- .../mlscript/compiler/codegen/CppAst.scala | 210 + .../compiler/codegen/CppCodeGen.scala | 234 ++ .../compiler/codegen/CppCompilerHost.scala | 44 + .../scala/mlscript/compiler/ir/Builder.scala | 779 +++- .../compiler/ir/DefnRefResolver.scala | 33 - .../scala/mlscript/compiler/ir/Fresh.scala | 5 +- .../main/scala/mlscript/compiler/ir/IR.scala | 438 ++- .../scala/mlscript/compiler/ir/Interp.scala | 475 +-- .../mlscript/compiler/ir/RefResolver.scala | 55 + .../mlscript/compiler/ir/Validator.scala | 31 +- .../compiler/optimizer/Analysis.scala | 16 +- .../compiler/optimizer/TailRecOpt.scala | 85 +- .../compiler/simpledef/Simpledef.scala | 2 +- compiler/shared/test/diff-ir/Class.mls | 182 + compiler/shared/test/diff-ir/Currying.mls | 95 + compiler/shared/test/diff-ir/IR.mls | 635 ++- compiler/shared/test/diff-ir/IRComplex.mls | 473 +-- compiler/shared/test/diff-ir/IRRec.mls | 1845 +++++---- compiler/shared/test/diff-ir/IRTailRec.mls | 3455 ++++++----------- compiler/shared/test/diff-ir/LiftClass.mls | 158 + compiler/shared/test/diff-ir/LiftFun.mls | 186 + compiler/shared/test/diff-ir/LiftLambda.mls | 87 + compiler/shared/test/diff-ir/Override.mls | 48 + compiler/shared/test/diff-ir/cpp/Makefile | 26 + compiler/shared/test/diff-ir/cpp/mlsprelude.h | 568 +++ compiler/shared/test/diff-ir/gcd.mls | 823 ++++ .../test/scala/mlscript/compiler/Test.scala | 2 +- .../test/scala/mlscript/compiler/TestIR.scala | 90 +- flake.lock | 97 + flake.nix | 34 + project/build.properties | 2 +- project/plugins.sbt | 4 +- shared/src/main/scala/mlscript/NewLexer.scala | 54 + .../src/test/scala/mlscript/DiffTests.scala | 29 +- 38 files changed, 6780 insertions(+), 4580 deletions(-) create mode 100644 .github/workflows/nix.yml delete mode 100644 .github/workflows/scala.yml rename compiler/shared/main/scala/mlscript/compiler/{optimizer => }/Document.scala (95%) create mode 100644 compiler/shared/main/scala/mlscript/compiler/codegen/CppAst.scala create mode 100644 compiler/shared/main/scala/mlscript/compiler/codegen/CppCodeGen.scala create mode 100644 compiler/shared/main/scala/mlscript/compiler/codegen/CppCompilerHost.scala delete mode 100644 compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala create mode 100644 compiler/shared/main/scala/mlscript/compiler/ir/RefResolver.scala create mode 100644 compiler/shared/test/diff-ir/Class.mls create mode 100644 compiler/shared/test/diff-ir/Currying.mls create mode 100644 compiler/shared/test/diff-ir/LiftClass.mls create mode 100644 compiler/shared/test/diff-ir/LiftFun.mls create mode 100644 compiler/shared/test/diff-ir/LiftLambda.mls create mode 100644 compiler/shared/test/diff-ir/Override.mls create mode 100644 compiler/shared/test/diff-ir/cpp/Makefile create mode 100644 compiler/shared/test/diff-ir/cpp/mlsprelude.h create mode 100644 compiler/shared/test/diff-ir/gcd.mls create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml new file mode 100644 index 00000000..2623f85c --- /dev/null +++ b/.github/workflows/nix.yml @@ -0,0 +1,23 @@ +name: Cpp Backend CI with Nix + +on: + pull_request: + push: + branches: [ mlscript ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@main + - uses: rrbutani/use-nix-shell-action@v1 + with: + devShell: .#default + - name: Install TypeScript + run: npm ci + - name: Run test + run: sbt -J-Xmx4096M -J-Xss4M test + - name: Check no changes + run: git diff-files -p --exit-code diff --git a/.github/workflows/scala.yml b/.github/workflows/scala.yml deleted file mode 100644 index 42559c2d..00000000 --- a/.github/workflows/scala.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Scala CI - -on: - push: - branches: [ mlscript ] - pull_request: - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up JDK 1.8 - uses: actions/setup-java@v1 - with: - java-version: 1.8 - - uses: actions/setup-node@v3 - with: - node-version: '17.x' - - name: Install TypeScript - run: npm ci - - name: Run tests - run: sbt -J-Xmx4096M -J-Xss4M test - - name: Check no changes - run: git diff-files -p --exit-code diff --git a/build.sbt b/build.sbt index 883cb517..bf2fb754 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ import Wart._ enablePlugins(ScalaJSPlugin) -ThisBuild / scalaVersion := "2.13.12" +ThisBuild / scalaVersion := "2.13.14" ThisBuild / version := "0.1.0-SNAPSHOT" ThisBuild / organization := "io.lptk" ThisBuild / organizationName := "LPTK" @@ -52,7 +52,7 @@ lazy val mlscript = crossProject(JSPlatform, JVMPlatform).in(file(".")) ) .jsSettings( scalaJSUseMainModuleInitializer := true, - libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "2.1.0", + libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "2.2.0", ) lazy val mlscriptJVM = mlscript.jvm @@ -79,7 +79,7 @@ lazy val ts2mlsTest = project.in(file("ts2mls")) lazy val compiler = crossProject(JSPlatform, JVMPlatform).in(file("compiler")) .settings( name := "mlscript-compiler", - scalaVersion := "3.1.3", + scalaVersion := "3.3.3", sourceDirectory := baseDirectory.value.getParentFile()/"shared", watchSources += WatchSource( baseDirectory.value.getParentFile()/"shared"/"test"/"diff", "*.mls", NothingFilter), diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/Document.scala b/compiler/shared/main/scala/mlscript/compiler/Document.scala similarity index 95% rename from compiler/shared/main/scala/mlscript/compiler/optimizer/Document.scala rename to compiler/shared/main/scala/mlscript/compiler/Document.scala index d5add561..22c938e4 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/Document.scala +++ b/compiler/shared/main/scala/mlscript/compiler/Document.scala @@ -1,4 +1,4 @@ -package mlscript.compiler.optimizer +package mlscript.compiler.utils enum Document: case Indented(content: Document) @@ -10,7 +10,7 @@ enum Document: def <:>(other: Document) = line(List(this, other)) def <#>(other: Document) = line(List(this, other), sep = "") - override def toString(): String = print + override def toString: String = print def print: String = { val sb = StringBuffer() diff --git a/compiler/shared/main/scala/mlscript/compiler/codegen/CppAst.scala b/compiler/shared/main/scala/mlscript/compiler/codegen/CppAst.scala new file mode 100644 index 00000000..ef054b02 --- /dev/null +++ b/compiler/shared/main/scala/mlscript/compiler/codegen/CppAst.scala @@ -0,0 +1,210 @@ +package mlscript.compiler.codegen.cpp + +import mlscript._ +import mlscript.utils._ +import mlscript.utils.shorthands._ +import mlscript.compiler.utils._ + +import scala.language.implicitConversions + +given Conversion[String, Document] = raw + +enum Specifier: + case Extern + case Static + case Inline + + def toDocument = raw: + this match + case Extern => "extern" + case Static => "static" + case Inline => "inline" + + override def toString: Str = toDocument.print + +object Type: + def toDocuments(args: Ls[Type], sep: Document, extraTypename: Bool = false): Document = + args.iterator.zipWithIndex.map { + case (x, 0) => + x.toDocument(extraTypename) + case (x, _) => + sep <#> x.toDocument(extraTypename) + }.fold(raw(""))(_ <#> _) + + def toDocuments(args: Ls[(Str, Type)], sep: Document): Document = + args.iterator.zipWithIndex.map { + case (x, 0) => + x._2.toDocument() <:> raw(x._1) + case (x, _) => + sep <#> x._2.toDocument() <:> raw(x._1) + }.fold(raw(""))(_ <#> _) + +enum Type: + case Prim(name: Str) + case Ptr(inner: Type) + case Ref(inner: Type) + case Array(inner: Type, size: Opt[Int]) + case FuncPtr(ret: Type, args: List[Type]) + case Struct(name: Str) + case Enum(name: Str) + case Template(name: Str, args: List[Type]) + case Var(name: Str) + case Qualifier(inner: Type, qual: Str) + + def toDocument(extraTypename: Bool = false): Document = + def aux(x: Type): Document = x match + case Prim(name) => name + case Ptr(inner) => aux(inner) <#> "*" + case Ref(inner) => aux(inner) <#> "&" + case Array(inner, size) => aux(inner) <#> "[" <#> size.fold(raw(""))(x => x.toString) <#> "]" + case FuncPtr(ret, args) => aux(ret) <#> "(" <#> Type.toDocuments(args, sep = ", ") <#> ")" + case Struct(name) => s"struct $name" + case Enum(name) => s"enum $name" + case Template(name, args) => s"$name" <#> "<" <#> Type.toDocuments(args, sep = ", ") <#> ">" + case Var(name) => name + case Qualifier(inner, qual) => aux(inner) <:> qual + aux(this) + + override def toString: Str = toDocument().print + +object Stmt: + def toDocuments(decl: Ls[Decl], stmts: Ls[Stmt]): Document = + stack_list(decl.map(_.toDocument) ++ stmts.map(_.toDocument)) + +enum Stmt: + case AutoBind(lhs: Ls[Str], rhs: Expr) + case Assign(lhs: Str, rhs: Expr) + case Return(expr: Expr) + case If(cond: Expr, thenStmt: Stmt, elseStmt: Opt[Stmt]) + case While(cond: Expr, body: Stmt) + case For(init: Stmt, cond: Expr, update: Stmt, body: Stmt) + case ExprStmt(expr: Expr) + case Break + case Continue + case Block(decl: Ls[Decl], stmts: Ls[Stmt]) + case Switch(expr: Expr, cases: Ls[(Expr, Stmt)]) + case Raw(stmt: Str) + + def toDocument: Document = + def aux(x: Stmt): Document = x match + case AutoBind(lhs, rhs) => + lhs match + case Nil => rhs.toDocument + case x :: Nil => "auto" <:> x <:> "=" <:> rhs.toDocument <#> ";" + case _ => "auto" <:> lhs.mkString("[", ",", "]") <:> "=" <:> rhs.toDocument <#> ";" + case Assign(lhs, rhs) => lhs <#> " = " <#> rhs.toDocument <#> ";" + case Return(expr) => "return " <#> expr.toDocument <#> ";" + case If(cond, thenStmt, elseStmt) => + "if (" <#> cond.toDocument <#> ")" <#> thenStmt.toDocument <:> elseStmt.fold(raw(""))(x => "else" <:> x.toDocument) + case While(cond, body) => + "while (" <#> cond.toDocument <#> ")" <#> body.toDocument + case For(init, cond, update, body) => + "for (" <#> init.toDocument <#> "; " <#> cond.toDocument <#> "; " <#> update.toDocument <#> ")" <#> body.toDocument + case ExprStmt(expr) => expr.toDocument <#> ";" + case Break => "break;" + case Continue => "continue;" + case Block(decl, stmts) => + stack( + "{", + Stmt.toDocuments(decl, stmts) |> indent, + "}") + case Switch(expr, cases) => + "switch (" <#> expr.toDocument <#> ")" <#> "{" <#> stack_list(cases.map { + case (cond, stmt) => "case " <#> cond.toDocument <#> ":" <#> stmt.toDocument + }) <#> "}" + case Raw(stmt) => stmt + aux(this) + +object Expr: + def toDocuments(args: Ls[Expr], sep: Document): Document = + args.zipWithIndex.map { + case (x, i) => + if i == 0 then x.toDocument + else sep <#> x.toDocument + }.fold(raw(""))(_ <#> _) + +enum Expr: + case Var(name: Str) + case IntLit(value: BigInt) + case DoubleLit(value: Double) + case StrLit(value: Str) + case CharLit(value: Char) + case Call(func: Expr, args: Ls[Expr]) + case Member(expr: Expr, member: Str) + case Index(expr: Expr, index: Expr) + case Unary(op: Str, expr: Expr) + case Binary(op: Str, lhs: Expr, rhs: Expr) + case Initializer(exprs: Ls[Expr]) + case Constructor(name: Str, init: Expr) + + def toDocument: Document = + def aux(x: Expr): Document = x match + case Var(name) => name + case IntLit(value) => value.toString + case DoubleLit(value) => value.toString + case StrLit(value) => s"\"$value\"" // need more reliable escape utils + case CharLit(value) => value.toInt.toString + case Call(func, args) => aux(func) <#> "(" <#> Expr.toDocuments(args, sep = ", ") <#> ")" + case Member(expr, member) => aux(expr) <#> "->" <#> member + case Index(expr, index) => aux(expr) <#> "[" <#> aux(index) <#> "]" + case Unary(op, expr) => "(" <#> op <#> aux(expr) <#> ")" + case Binary(op, lhs, rhs) => "(" <#> aux(lhs) <#> op <#> aux(rhs) <#> ")" + case Initializer(exprs) => "{" <#> Expr.toDocuments(exprs, sep = ", ") <#> "}" + case Constructor(name, init) => name <#> init.toDocument + aux(this) + +case class CompilationUnit(includes: Ls[Str], decls: Ls[Decl], defs: Ls[Def]): + def toDocument: Document = + stack_list(includes.map(x => raw(x)) ++ decls.map(_.toDocument) ++ defs.map(_.toDocument)) + def toDocumentWithoutHidden: Document = + val hiddenNames = Set( + "HiddenTheseEntities", "True", "False", "Callable", "List", "Cons", "Nil", "Option", "Some", "None", "Pair", "Tuple2", "Tuple3", "Nat", "S", "O" + ) + stack_list(defs.filterNot { + case Def.StructDef(name, _, _, _) => hiddenNames.contains(name.stripPrefix("_mls_")) + case _ => false + }.map(_.toDocument)) + +enum Decl: + case StructDecl(name: Str) + case EnumDecl(name: Str) + case FuncDecl(ret: Type, name: Str, args: Ls[Type]) + case VarDecl(name: Str, typ: Type) + + def toDocument: Document = + def aux(x: Decl): Document = x match + case StructDecl(name) => s"struct $name;" + case EnumDecl(name) => s"enum $name;" + case FuncDecl(ret, name, args) => ret.toDocument() <#> s" $name(" <#> Type.toDocuments(args, sep = ", ") <#> ");" + case VarDecl(name, typ) => typ.toDocument() <#> s" $name;" + aux(this) + +enum Def: + case StructDef(name: Str, fields: Ls[(Str, Type)], inherit: Opt[Ls[Str]], methods: Ls[Def] = Ls.empty) + case EnumDef(name: Str, fields: Ls[(Str, Opt[Int])]) + case FuncDef(specret: Type, name: Str, args: Ls[(Str, Type)], body: Stmt.Block, or: Bool = false, virt: Bool = false) + case VarDef(typ: Type, name: Str, init: Opt[Expr]) + case RawDef(raw: Str) + + def toDocument: Document = + def aux(x: Def): Document = x match + case StructDef(name, fields, inherit, defs) => + stack( + s"struct $name" <#> (if inherit.nonEmpty then ": public" <:> inherit.get.mkString(", ") else "" ) <:> "{", + stack_list(fields.map { + case (name, typ) => typ.toDocument() <#> " " <#> name <#> ";" + }) |> indent, + stack_list(defs.map(_.toDocument)) |> indent, + "};" + ) + case EnumDef(name, fields) => + s"enum $name" <:> "{" <#> stack_list(fields.map { + case (name, value) => value.fold(s"$name")(x => s"$name = $x") + }) <#> "};" + case FuncDef(specret, name, args, body, or, virt) => + (if virt then "virtual " else "") + <#> specret.toDocument() <#> s" $name(" <#> Type.toDocuments(args, sep = ", ") <#> ")" <#> (if or then " override" else "") <#> body.toDocument + case VarDef(typ, name, init) => + typ.toDocument() <#> s" $name" <#> init.fold(raw(""))(x => " = " <#> x.toDocument) <#> raw(";") + case RawDef(x) => x + aux(this) \ No newline at end of file diff --git a/compiler/shared/main/scala/mlscript/compiler/codegen/CppCodeGen.scala b/compiler/shared/main/scala/mlscript/compiler/codegen/CppCodeGen.scala new file mode 100644 index 00000000..20d08050 --- /dev/null +++ b/compiler/shared/main/scala/mlscript/compiler/codegen/CppCodeGen.scala @@ -0,0 +1,234 @@ +package mlscript.compiler.codegen.cpp + +import mlscript.compiler.ir.{Expr => IExpr, _} +import mlscript.compiler.utils._ +import mlscript.utils._ +import mlscript.utils.shorthands._ +import scala.collection.mutable.ListBuffer + +def codegen(prog: Program): CompilationUnit = + val codegen = CppCodeGen() + codegen.codegen(prog) + +private class CppCodeGen: + def mapName(name: Name): Str = "_mls_" + name.str.replace('$', '_').replace('\'', '_') + def mapName(name: Str): Str = "_mls_" + name.replace('$', '_').replace('\'', '_') + val freshName = Fresh(div = '_'); + val mlsValType = Type.Prim("_mlsValue") + val mlsUnitValue = Expr.Call(Expr.Var("_mlsValue::create<_mls_Unit>"), Ls()); + val mlsRetValue = "_mls_retval" + val mlsRetValueDecl = Decl.VarDecl(mlsRetValue, mlsValType) + val mlsMainName = "_mlsMain" + val mlsPrelude = "#include \"mlsprelude.h\"" + val mlsPreludeImpl = "#include \"mlsprelude.cpp\"" + val mlsInternalClass = Set("True", "False", "Boolean", "Callable") + val mlsObject = "_mlsObject" + val mlsBuiltin = "builtin" + val mlsEntryPoint = s"int main() { return _mlsLargeStack(_mlsMainWrapper); }"; + def mlsIntLit(x: BigInt) = Expr.Call(Expr.Var("_mlsValue::fromIntLit"), Ls(Expr.IntLit(x))) + def mlsStrLit(x: Str) = Expr.Call(Expr.Var("_mlsValue::fromStrLit"), Ls(Expr.StrLit(x))) + def mlsCharLit(x: Char) = Expr.Call(Expr.Var("_mlsValue::fromIntLit"), Ls(Expr.CharLit(x))) + def mlsNewValue(cls: Str, args: Ls[Expr]) = Expr.Call(Expr.Var(s"_mlsValue::create<$cls>"), args) + def mlsIsValueOf(cls: Str, scrut: Expr) = Expr.Call(Expr.Var(s"_mlsValue::isValueOf<$cls>"), Ls(scrut)) + def mlsIsIntLit(scrut: Expr, lit: mlscript.IntLit) = Expr.Call(Expr.Var("_mlsValue::isIntLit"), Ls(scrut, Expr.IntLit(lit.value))) + def mlsDebugPrint(x: Expr) = Expr.Call(Expr.Var("_mlsValue::print"), Ls(x)) + def mlsTupleValue(init: Expr) = Expr.Constructor("_mlsValue::tuple", init) + def mlsAs(name: Str, cls: Str) = Expr.Var(s"_mlsValue::as<$cls>($name)") + def mlsAsUnchecked(name: Str, cls: Str) = Expr.Var(s"_mlsValue::cast<$cls>($name)") + def mlsObjectNameMethod(name: Str) = s"constexpr static inline const char *typeName = \"${name}\";" + def mlsTypeTag() = s"constexpr static inline uint32_t typeTag = nextTypeTag();" + def mlsTypeTag(n: Int) = s"constexpr static inline uint32_t typeTag = $n;" + def mlsCommonCreateMethod(cls: Str, fields: Ls[Str], id: Int) = + val parameters = fields.map{x => s"_mlsValue $x"}.mkString(", ") + val fieldsAssignment = fields.map{x => s"_mlsVal->$x = $x; "}.mkString + s"static _mlsValue create($parameters) { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) $cls; _mlsVal->refCount = 1; _mlsVal->tag = typeTag; $fieldsAssignment return _mlsValue(_mlsVal); }" + def mlsCommonPrintMethod(fields: Ls[Str]) = + if fields.isEmpty then s"virtual void print() const override { std::printf(\"%s\", typeName); }" + else + val fieldsPrint = fields.map{x => s"this->$x.print(); "}.mkString("std::printf(\", \"); ") + s"virtual void print() const override { std::printf(\"%s\", typeName); std::printf(\"(\"); $fieldsPrint std::printf(\")\"); }" + def mlsCommonDestructorMethod(cls: Str, fields: Ls[Str]) = + val fieldsDeletion = fields.map{x => s"_mlsValue::destroy(this->$x); "}.mkString + s"virtual void destroy() override { $fieldsDeletion operator delete (this, std::align_val_t(_mlsAlignment)); }" + def mlsThrowNonExhaustiveMatch = Stmt.Raw("_mlsNonExhaustiveMatch();"); + def mlsCall(fn: Str, args: Ls[Expr]) = Expr.Call(Expr.Var("_mlsCall"), Expr.Var(fn) :: args) + def mlsMethodCall(cls: ClassRef, method: Str, args: Ls[Expr]) = + Expr.Call(Expr.Member(Expr.Call(Expr.Var(s"_mlsMethodCall<${cls.name |> mapName}>"), Ls(args.head)), method), args.tail) + def mlsFnWrapperName(fn: Str) = s"_mlsFn_$fn" + def mlsFnCreateMethod(fn: Str) = s"static _mlsValue create() { static _mlsFn_$fn mlsFn alignas(_mlsAlignment); mlsFn.refCount = stickyRefCount; mlsFn.tag = typeTag; return _mlsValue(&mlsFn); }" + def mlsNeverValue(n: Int) = if (n <= 1) then Expr.Call(Expr.Var(s"_mlsValue::never"), Ls()) else Expr.Call(Expr.Var(s"_mlsValue::never<$n>"), Ls()) + + case class Ctx( + defnCtx: Set[Str], + ) + + def codegenClassInfo(using ctx: Ctx)(cls: ClassInfo): (Opt[Def], Decl) = + val fields = cls.fields.map{x => (x |> mapName, mlsValType)} + val parents = if cls.parents.nonEmpty then cls.parents.toList.map(mapName) else mlsObject :: Nil + val decl = Decl.StructDecl(cls.name |> mapName) + if mlsInternalClass.contains(cls.name) then return (None, decl) + val theDef = Def.StructDef( + cls.name |> mapName, fields, + if parents.nonEmpty then Some(parents) else None, + Ls(Def.RawDef(mlsObjectNameMethod(cls.name)), + Def.RawDef(mlsTypeTag()), + Def.RawDef(mlsCommonPrintMethod(cls.fields.map(mapName))), + Def.RawDef(mlsCommonDestructorMethod(cls.name |> mapName, cls.fields.map(mapName))), + Def.RawDef(mlsCommonCreateMethod(cls.name |> mapName, cls.fields.map(mapName), cls.id))) + ++ cls.methods.map{case (name, defn) => { + val (theDef, decl) = codegenDefn(using Ctx(ctx.defnCtx + cls.name))(defn) + theDef match + case x @ Def.FuncDef(_, name, _, _, _, _) => x.copy(virt = true) + case _ => theDef + }} + ) + (S(theDef), decl) + + def toExpr(texpr: TrivialExpr, reifyUnit: Bool = false)(using ctx: Ctx): Opt[Expr] = texpr match + case IExpr.Ref(name) => S(Expr.Var(name |> mapName)) + case IExpr.Literal(mlscript.IntLit(x)) => S(mlsIntLit(x)) + case IExpr.Literal(mlscript.DecLit(x)) => S(mlsIntLit(x.toBigInt)) + case IExpr.Literal(mlscript.StrLit(x)) => S(mlsStrLit(x)) + case IExpr.Literal(mlscript.UnitLit(_)) => if reifyUnit then S(mlsUnitValue) else None + + def toExpr(texpr: TrivialExpr)(using ctx: Ctx): Expr = texpr match + case IExpr.Ref(name) => Expr.Var(name |> mapName) + case IExpr.Literal(mlscript.IntLit(x)) => mlsIntLit(x) + case IExpr.Literal(mlscript.DecLit(x)) => mlsIntLit(x.toBigInt) + case IExpr.Literal(mlscript.StrLit(x)) => mlsStrLit(x) + case IExpr.Literal(mlscript.UnitLit(_)) => mlsUnitValue + + + def wrapMultiValues(exprs: Ls[TrivialExpr])(using ctx: Ctx): Expr = exprs match + case x :: Nil => toExpr(x, reifyUnit = true).get + case _ => + val init = Expr.Initializer(exprs.map{x => toExpr(x)}) + mlsTupleValue(init) + + def codegenCaseWithIfs(scrut: Name, cases: Ls[(Pat, Node)], default: Opt[Node], storeInto: Str)(using decls: Ls[Decl], stmts: Ls[Stmt])(using ctx: Ctx): (Ls[Decl], Ls[Stmt]) = + val scrutName = mapName(scrut) + val init: Stmt = + default.fold(mlsThrowNonExhaustiveMatch)(x => { + val (decls2, stmts2) = codegen(x, storeInto)(using Ls.empty, Ls.empty[Stmt]) + Stmt.Block(decls2, stmts2) + }) + val stmt = cases.foldRight(S(init)) { + case ((Pat.Class(cls), arm), nextarm) => + val (decls2, stmts2) = codegen(arm, storeInto)(using Ls.empty, Ls.empty[Stmt]) + val stmt = Stmt.If(mlsIsValueOf(cls.name |> mapName, Expr.Var(scrutName)), Stmt.Block(decls2, stmts2), nextarm) + S(stmt) + case ((Pat.Lit(i @ mlscript.IntLit(_)), arm), nextarm) => + val (decls2, stmts2) = codegen(arm, storeInto)(using Ls.empty, Ls.empty[Stmt]) + val stmt = Stmt.If(mlsIsIntLit(Expr.Var(scrutName), i), Stmt.Block(decls2, stmts2), nextarm) + S(stmt) + case _ => ??? + } + (decls, stmt.fold(stmts)(x => stmts :+ x)) + + def codegenJumpWithCall(defn: DefnRef, args: Ls[TrivialExpr], storeInto: Opt[Str])(using decls: Ls[Decl], stmts: Ls[Stmt])(using ctx: Ctx): (Ls[Decl], Ls[Stmt]) = + val call = Expr.Call(Expr.Var(defn.name |> mapName), args.map(toExpr)) + val stmts2 = stmts ++ Ls(storeInto.fold(Stmt.Return(call))(x => Stmt.Assign(x, call))) + (decls, stmts2) + + def codegenOps(op: Str, args: Ls[TrivialExpr])(using ctx: Ctx) = op match + case "+" => Expr.Binary("+", toExpr(args(0)), toExpr(args(1))) + case "-" => Expr.Binary("-", toExpr(args(0)), toExpr(args(1))) + case "*" => Expr.Binary("*", toExpr(args(0)), toExpr(args(1))) + case "/" => Expr.Binary("/", toExpr(args(0)), toExpr(args(1))) + case "%" => Expr.Binary("%", toExpr(args(0)), toExpr(args(1))) + case "==" => Expr.Binary("==", toExpr(args(0)), toExpr(args(1))) + case "!=" => Expr.Binary("!=", toExpr(args(0)), toExpr(args(1))) + case "<" => Expr.Binary("<", toExpr(args(0)), toExpr(args(1))) + case "<=" => Expr.Binary("<=", toExpr(args(0)), toExpr(args(1))) + case ">" => Expr.Binary(">", toExpr(args(0)), toExpr(args(1))) + case ">=" => Expr.Binary(">=", toExpr(args(0)), toExpr(args(1))) + case "&&" => Expr.Binary("&&", toExpr(args(0)), toExpr(args(1))) + case "||" => Expr.Binary("||", toExpr(args(0)), toExpr(args(1))) + case "!" => Expr.Unary("!", toExpr(args(0))) + case _ => mlscript.utils.TODO("codegenOps") + + + def codegen(expr: IExpr)(using ctx: Ctx): Expr = expr match + case x @ (IExpr.Ref(_) | IExpr.Literal(_)) => toExpr(x, reifyUnit = true).get + case IExpr.CtorApp(cls, args) => mlsNewValue(cls.name |> mapName, args.map(toExpr)) + case IExpr.Select(name, cls, field) => Expr.Member(mlsAsUnchecked(name |> mapName, cls.name |> mapName), field |> mapName) + case IExpr.BasicOp(name, args) => codegenOps(name, args) + case IExpr.AssignField(assignee, cls, field, value) => mlscript.utils.TODO("Assign field in the backend") + + def codegenBuiltin(names: Ls[Name], builtin: Str, args: Ls[TrivialExpr])(using ctx: Ctx): Ls[Stmt] = builtin match + case "error" => Ls(Stmt.Raw("throw std::runtime_error(\"Error\");"), Stmt.AutoBind(names.map(mapName), mlsNeverValue(names.size))) + case _ => Ls(Stmt.AutoBind(names.map(mapName), Expr.Call(Expr.Var("_mls_builtin_" + builtin), args.map(toExpr)))) + + def codegen(body: Node, storeInto: Str)(using decls: Ls[Decl], stmts: Ls[Stmt])(using ctx: Ctx): (Ls[Decl], Ls[Stmt]) = body match + case Node.Result(res) => + val expr = wrapMultiValues(res) + val stmts2 = stmts ++ Ls(Stmt.Assign(storeInto, expr)) + (decls, stmts2) + case Node.Jump(defn, args) => + codegenJumpWithCall(defn, args, S(storeInto)) + case Node.LetExpr(name, expr, body) => + val stmts2 = stmts ++ Ls(Stmt.AutoBind(Ls(name |> mapName), codegen(expr))) + codegen(body, storeInto)(using decls, stmts2) + case Node.LetMethodCall(names, cls, method, IExpr.Ref(Name("builtin")) :: args, body) => + val stmts2 = stmts ++ codegenBuiltin(names, args.head.toString.replace("\"", ""), args.tail) + codegen(body, storeInto)(using decls, stmts2) + case Node.LetMethodCall(names, cls, method, args, body) => + val call = mlsMethodCall(cls, method.str |> mapName, args.map(toExpr)) + val stmts2 = stmts ++ Ls(Stmt.AutoBind(names.map(mapName), call)) + codegen(body, storeInto)(using decls, stmts2) + case Node.LetCall(names, defn, args, _, body) => + val call = Expr.Call(Expr.Var(defn.name |> mapName), args.map(toExpr)) + val stmts2 = stmts ++ Ls(Stmt.AutoBind(names.map(mapName), call)) + codegen(body, storeInto)(using decls, stmts2) + case Node.Case(scrut, cases, default) => + codegenCaseWithIfs(scrut, cases, default, storeInto) + + private def codegenDefn(using ctx: Ctx)(defn: Defn): (Def, Decl) = defn match + case Defn(id, name, params, resultNum, body, _, _) => + val decls = Ls(mlsRetValueDecl) + val stmts = Ls.empty[Stmt] + val (decls2, stmts2) = codegen(body, mlsRetValue)(using decls, stmts) + val stmtsWithReturn = stmts2 :+ Stmt.Return(Expr.Var(mlsRetValue)) + val theDef = Def.FuncDef(mlsValType, name |> mapName, params.map(x => (x |> mapName, mlsValType)), Stmt.Block(decls2, stmtsWithReturn)) + val decl = Decl.FuncDecl(mlsValType, name |> mapName, params.map(x => mlsValType)) + (theDef, decl) + + def codegenTopNode(node: Node)(using ctx: Ctx): (Def, Decl) = + val decls = Ls(mlsRetValueDecl) + val stmts = Ls.empty[Stmt] + val (decls2, stmts2) = codegen(node, mlsRetValue)(using decls, stmts) + val stmtsWithReturn = stmts2 :+ Stmt.Return(Expr.Var(mlsRetValue)) + val theDef = Def.FuncDef(mlsValType, mlsMainName, Ls(), Stmt.Block(decls2, stmtsWithReturn)) + val decl = Decl.FuncDecl(mlsValType, mlsMainName, Ls()) + (theDef, decl) + + // Topological sort of classes based on inheritance relationships + def sortClasses(prog: Program): Ls[ClassInfo] = + var depgraph = prog.classes.map(x => (x.name, x.parents)).toMap + var degree = depgraph.view.mapValues(_.size).toMap + def removeNode(node: Str) = + degree -= node + depgraph -= node + depgraph = depgraph.view.mapValues(_.filter(_ != node)).toMap + degree = depgraph.view.mapValues(_.size).toMap + val sorted = ListBuffer.empty[ClassInfo] + var work = degree.filter(_._2 == 0).keys.toSet + while work.nonEmpty do + val node = work.head + work -= node + sorted.addOne(prog.classes.find(_.name == node).get) + removeNode(node) + val next = degree.filter(_._2 == 0).keys + work ++= next + if depgraph.nonEmpty then + val cycle = depgraph.keys.mkString(", ") + throw new Exception(s"Cycle detected in class hierarchy: $cycle") + sorted.toList + + def codegen(prog: Program): CompilationUnit = + val sortedClasses = sortClasses(prog) + val defnCtx = prog.defs.map(_.name) + val (defs, decls) = sortedClasses.map(codegenClassInfo(using Ctx(defnCtx))).unzip + val (defs2, decls2) = prog.defs.map(codegenDefn(using Ctx(defnCtx))).unzip + val (defMain, declMain) = codegenTopNode(prog.main)(using Ctx(defnCtx)) + CompilationUnit(Ls(mlsPrelude), decls ++ decls2 :+ declMain, defs.flatten ++ defs2 :+ defMain :+ Def.RawDef(mlsEntryPoint)) diff --git a/compiler/shared/main/scala/mlscript/compiler/codegen/CppCompilerHost.scala b/compiler/shared/main/scala/mlscript/compiler/codegen/CppCompilerHost.scala new file mode 100644 index 00000000..3897648d --- /dev/null +++ b/compiler/shared/main/scala/mlscript/compiler/codegen/CppCompilerHost.scala @@ -0,0 +1,44 @@ +package mlscript.compiler.codegen.cpp + +import mlscript._ +import mlscript.utils.shorthands._ +import scala.collection.mutable.ListBuffer + +final class CppCompilerHost(val auxPath: Str): + import scala.sys.process._ + private def ifAnyCppCompilerExists(): Boolean = + Seq("g++", "--version").! == 0 || Seq("clang++", "--version").! == 0 + + private def isMakeExists(): Boolean = + import scala.sys.process._ + Seq("make", "--version").! == 0 + + val ready = ifAnyCppCompilerExists() && isMakeExists() + + def compileAndRun(src: Str, output: Str => Unit): Unit = + if !ready then + return + val srcPath = os.temp(contents = src, suffix = ".cpp") + val binPath = os.temp(suffix = ".mls.out") + var stdout = ListBuffer[Str]() + var stderr = ListBuffer[Str]() + val buildLogger = ProcessLogger(stdout :+= _, stderr :+= _) + val buildResult = Seq("make", "-B", "-C", auxPath, "auto", s"SRC=$srcPath", s"DST=$binPath") ! buildLogger + if buildResult != 0 then + output("Compilation failed: ") + for line <- stdout do output(line) + for line <- stderr do output(line) + return + + stdout.clear() + stderr.clear() + val runCmd = Seq(binPath.toString) + val runResult = runCmd ! buildLogger + if runResult != 0 then + output("Execution failed: ") + for line <- stdout do output(line) + for line <- stderr do output(line) + return + + output("Execution succeeded: ") + for line <- stdout do output(line) \ No newline at end of file diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala index c80a698f..86fb4986 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala @@ -4,35 +4,72 @@ import mlscript.compiler.optimizer.FreeVarAnalysis import mlscript.utils.shorthands._ import mlscript.utils._ import mlscript._ -import mlscript.Message._ -import collection.mutable.ListBuffer +import mlscript.Message.MessageContext +import scala.collection.mutable.ListBuffer final val ops = Set("+", "-", "*", "/", ">", "<", ">=", "<=", "!=", "==") +final val builtin = Set("builtin") -final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diagnostic => Unit): +final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diagnostic => Unit, verbose: Boolean = false): import Node._ import Expr._ - private type NameCtx = Map[Str, Name] - private type ClassCtx = Map[Str, ClassInfo] - private type FieldCtx = Map[Str, (Str, ClassInfo)] - private type FnCtx = Set[Str] - private type OpCtx = Set[Str] + private def log(x: Any) = if verbose then println(x) + + private final case class ClassInfoPartial(name: Str, fields: Ls[Str], methods: Set[Str]): + var freeVars = Ls.empty[Str] + var ctx = ctxEmpty + + private final case class DefnInfoPartial(name: Str, params: Ls[Str]): + var freeVars = Ls.empty[Str] + var ctx = ctxEmpty + + private type NameCtx = Map[Str, Name] // name -> new name + private type ClassCtx = Map[Str, ClassInfoPartial] // class name -> partial class info + private type FnCtx = Map[Str, DefnInfoPartial] // fn name -> partial defn info + private type OpCtx = Set[Str] // op names private final case class Ctx( - val nameCtx: NameCtx = Map.empty, - val classCtx: ClassCtx = Map.empty, - val fieldCtx: FieldCtx = Map.empty, - val fnCtx: FnCtx = Set.empty, - val opCtx: OpCtx = Set.empty, - var jpAcc: ListBuffer[Defn], + nameCtx: NameCtx = Map.empty, + tyNameCtx: NameCtx = Map.empty, + classCtx: ClassCtx = Map.empty, + fnCtx: FnCtx = Map.empty, + opCtx: OpCtx = Set.empty, + jpAcc: ListBuffer[Defn], + defAcc: ListBuffer[NuFunDef], + lcAcc: ListBuffer[NuTypeDef], + ): + def hasClassLifted = lcAcc.nonEmpty + def hasLifted = jpAcc.nonEmpty || defAcc.nonEmpty || lcAcc.nonEmpty + + private def ctxEmpty = Ctx( + nameCtx = Map.empty, + tyNameCtx = Map.empty, + classCtx = Map.empty, + fnCtx = Map.empty, + opCtx = ops, + jpAcc = ListBuffer.empty, + defAcc = ListBuffer.empty, + lcAcc = ListBuffer.empty, ) + private def ctxJoin(c1: Ctx, c2: Ctx) = + Ctx( + nameCtx = c1.nameCtx ++ c2.nameCtx, + tyNameCtx = c1.tyNameCtx ++ c2.tyNameCtx, + classCtx = c1.classCtx ++ c2.classCtx, + fnCtx = c1.fnCtx ++ c2.fnCtx, + opCtx = c1.opCtx ++ c2.opCtx, + jpAcc = c1.jpAcc, + defAcc = c1.defAcc, + lcAcc = c1.lcAcc, + ) + private def ref(x: Name) = Ref(x) private def result(x: Ls[TrivialExpr]) = Result(x).attachTag(tag) private def sresult(x: TrivialExpr) = Result(Ls(x)).attachTag(tag) private def unexpectedNode(x: Node) = throw IRError(s"unsupported node $x") - private def unexpectedTerm(x: Term) = throw IRError(s"unsupported term $x") + private def unexpectedTerm(x: Statement) = throw IRError(s"unsupported term $x") private def buildBinding(using ctx: Ctx)(name: Str, e: Term, body: Term)(k: Node => Node): Node = buildResultFromTerm(e) { @@ -60,64 +97,212 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres } case Tup(Nil) => Nil |> result |> k - private def bindingPatternVariables(scrut: Str, tup: Tup, cls: ClassInfo, rhs: Term): Term = - val params = tup.fields.map { - case N -> Fld(FldFlags.empty, Var(name)) => name - case _ => throw IRError("unsupported field") - } + private def bindingPatternVariables(scrut: Str, tup: Tup, cls: ClassInfoPartial, rhs: Term): Term = + val params = tup |> getTupleFields val fields = cls.fields val tm = params.zip(fields).foldLeft(rhs) { case (tm, (param, field)) => - Let(false, Var(param), Sel(Var(scrut), Var(field)), tm) + Let(false, Var(param), App(Sel(Var(cls.name), Var(field)), Tup(Ls(N -> Fld(FldFlags.empty, Var(scrut))))), tm) } tm - private def buildResultFromTerm(using ctx: Ctx)(tm: Term)(k: Node => Node): Node = - def buildLetCall(f: Term, xs: Tup, ann: Option[Term]) = - buildResultFromTerm(f) { node => node match - case Result(Ref(g) :: Nil) if ctx.fnCtx.contains(g.str) => buildResultFromTerm(xs) { - case Result(args) => - val v = fresh.make - - ann match - case Some(ann @ Var(nme)) => - if nme === "tailcall" then - LetCall(List(v), DefnRef(Right(g.str)), args, true, v |> ref |> sresult |> k)(f.toLoc).attachTag(tag) - else - if nme === "tailrec" then - raise(ErrorReport(List(msg"@tailrec is for annotating functions; try @tailcall instead" -> ann.toLoc), true, Diagnostic.Compilation)) - LetCall(List(v), DefnRef(Right(g.str)), args, false, v |> ref |> sresult |> k)(f.toLoc).attachTag(tag) - case Some(_) => node |> unexpectedNode - case None => LetCall(List(v), DefnRef(Right(g.str)), args, false, v |> ref |> sresult |> k)(f.toLoc).attachTag(tag) - - case node @ _ => node |> unexpectedNode - } - case Result(Ref(f) :: Nil) => buildResultFromTerm(xs) { - case Result(args) => - throw IRError(s"not supported: apply") - case node @ _ => node |> unexpectedNode + private def freeVariablesIf(ucs: If): Set[Str] = + val ifbody: IfBody = ucs.body + val els: Opt[Term] = ucs.els + + def f(ifbody: IfBody): Set[Str] = ifbody match + case IfBlock(lines) => lines.foldLeft(Set.empty[Str]) { + case (acc, Left(ifbody2)) => acc ++ f(ifbody2) + case (acc, Right(rhs)) => acc ++ freeVariables(rhs)._2 + } + case IfElse(expr) => freeVariables(expr) + case IfLet(isRec, name, rhs, body) => + if isRec then + (freeVariables(rhs) -- freeVariables(name)) ++ (f(body) -- freeVariables(name)) + else + freeVariables(rhs) ++ (f(body) -- freeVariables(name)) + case IfOpApp(lhs, op, rhs) => freeVariables(lhs) ++ f(rhs) + case IfOpsApp(lhs, opsRhss) => freeVariables(lhs) ++ opsRhss.foldLeft(Set.empty[Str]) { + case (acc, (op, rhs)) => acc ++ f(rhs) + } + case IfThen(expr, rhs) => freeVariables(rhs) -- freeVariables(expr) + + val fvs1 = f(ifbody) + val fvs2 = els.fold(Set.empty[Str]) { freeVariables } + + fvs1 ++ fvs2 + + private def freeVariables(tu: TypingUnit): Set[Str] = + var all_defined = tu.rawEntities.foldLeft(Set.empty[Str]) { + case (defined, stmt) => defined ++ freeVariables(stmt)._1 + } + tu.rawEntities.foldLeft(Set.empty[Str]) { + case (acc, stmt) => acc ++ (freeVariables(stmt)._2 -- all_defined) + } + + // for this fv analysis, we also need to return the variables that are defined in this statement + private def freeVariables(stmt: Statement): (Set[Str], Set[Str]) = stmt match + case DataDefn(body) => throw IRError("unsupported DataDefn") + case DatatypeDefn(head, body) => throw IRError("unsupported DatatypeDefn") + case LetS(isRec, pat, rhs) => throw IRError("unsupported LetS") + case ntd: NuTypeDef => + val fvs = freeVariables(ntd.body) -- ntd.params.fold(Set.empty)(freeVariables) + (Set(ntd.nme.name), fvs) + case Constructor(params, body) => throw IRError("unsupported Constructor") + case nfd: NuFunDef => + val fvs = nfd.isLetRec match + case None | Some(true) => nfd.rhs.fold(tm => freeVariables(tm) -- freeVariables(nfd.nme), ty => Set.empty) + case Some(false) => nfd.rhs.fold(tm => freeVariables(tm), ty => Set.empty) + (freeVariables(nfd.nme), fvs) + case Def(rec, nme, rhs, isByname) => throw IRError("unsupported Def") + case TypeDef(kind, nme, tparams, body, mthDecls, mthDefs, positionals, adtInfo) => throw IRError("unsupported TypeDef") + case x: Term => + val fvs = freeVariables(x) + (Set.empty, fvs) + + private def freeVariables(tm: Term): Set[Str] = tm match + case AdtMatchWith(cond, arms) => + val inner: Set[Str] = arms.foldLeft(Set.empty){ + case (acc, AdtMatchPat(pat, body)) => acc ++ freeVariables(body) -- freeVariables(pat) + } + freeVariables(cond) ++ inner + case Ann(ann, receiver) => freeVariables(receiver) + case App(lhs, rhs) => freeVariables(lhs) ++ freeVariables(rhs) + case Asc(trm, ty) => freeVariables(trm) + case Assign(lhs, rhs) => freeVariables(lhs) ++ freeVariables(rhs) + case Bind(lhs, rhs) => freeVariables(lhs) + case Blk(stmts) => + var fvs = Set.empty[Str] + var defined = Set.empty[Str] + stmts.foreach { + stmt => { + val stmt_fvs = freeVariables(stmt) + fvs ++= stmt_fvs._2 + fvs --= defined + defined ++= stmt_fvs._1 } + } + fvs + case Bra(rcd, trm) => freeVariables(trm) + case CaseOf(trm, cases) => + import mlscript.{Case => CCase} + def f(pat: CaseBranches): Set[Str] = + pat match + case CCase(pat, body, rest) => freeVariables(body) -- freeVariables(pat) ++ f(rest) + case NoCases => Set.empty + case Wildcard(body) => freeVariables(body) + freeVariables(trm) ++ f(cases) + case Eqn(lhs, rhs) => freeVariables(rhs) + case Forall(params, body) => freeVariables(body) + case x: If => freeVariablesIf(x) + case Inst(body) => freeVariables(body) + case Lam(lhs, rhs) => + freeVariables(rhs) -- freeVariables(lhs) + case Let(isRec, name, rhs, body) => + if isRec then + (freeVariables(rhs) -- freeVariables(name)) ++ (freeVariables(body) -- freeVariables(name)) + else + freeVariables(rhs) ++ (freeVariables(body) -- freeVariables(name)) + case New(head, body) => throw IRError("unsupported New") + case NuNew(cls) => freeVariables(cls) + case Quoted(body) => throw IRError("unsupported Quoted") + case Rcd(fields) => fields.foldLeft(Set.empty[Str]) { + case (acc, (_, Fld(_, trm))) => acc ++ freeVariables(trm) + } + case Rft(base, decls) => throw IRError("unsupported Rft") + case Sel(receiver, fieldName) => freeVariables(receiver) + case Splc(fields) => fields.foldLeft(Set.empty[Str]) { + case (acc, Left(trm)) => acc ++ freeVariables(trm) + case (acc, Right(Fld(_, trm))) => acc ++ freeVariables(trm) + } + case Subs(arr, idx) => freeVariables(arr) ++ freeVariables(idx) + case Super() => Set.empty + case Test(trm, ty) => freeVariables(trm) + case Tup(fields) => fields.foldLeft(Set.empty[Str]) { + case (acc, (_, Fld(_, trm))) => acc ++ freeVariables(trm) + } + case TyApp(lhs, targs) => freeVariables(lhs) + case Unquoted(body) => throw IRError("unsupported Unquoted") + case Where(body, where) => throw IRError("unsupported Where") + case While(cond, body) => freeVariables(cond) ++ freeVariables(body) + case With(trm, fields) => freeVariables(trm) ++ freeVariables(fields: Term) + case Var(name) => Set(name) + case DecLit(value) => Set.empty + case IntLit(value) => Set.empty + case StrLit(value) => Set.empty + case UnitLit(undefinedOrNull) => Set.empty + + private def buildLetCall(using ctx: Ctx)(f: Var, xs: Tup, ann: Option[Term])(k: Node => Node) = + buildResultFromTup(xs) { + case Result(args) => + val v = fresh.make + ctx.nameCtx.get(f.name) match + case None => throw IRError(s"unknown name $f in $ctx") + case Some(f2) => + ctx.fnCtx.get(f2.str) match + case None => throw IRError(s"unknown function $f2 in $ctx") + case Some(dInfo) => + val args2 = + if args.size != dInfo.params.size then + args ++ dInfo.freeVars.map(x => Ref(ctx.nameCtx(x))) // it's possible that the free vars as parameters have been filled when do eta expansion + else + args + ann match + case Some(ann @ Var(nme)) => + if nme === "tailcall" then + LetCall(List(v), DefnRef(Right(f2.str)), args2, true, v |> ref |> sresult |> k)(f.toLoc).attachTag(tag) + else + if nme === "tailrec" then + raise(ErrorReport(List(msg"@tailrec is for annotating functions; try @tailcall instead" -> ann.toLoc), true, Diagnostic.Compilation)) + LetCall(List(v), DefnRef(Right(f2.str)), args2, false, v |> ref |> sresult |> k)(f.toLoc).attachTag(tag) + case Some(_) => throw IRError("unsupported annotation") + case None => LetCall(List(v), DefnRef(Right(f2.str)), args2, false, v |> ref |> sresult |> k)(f.toLoc).attachTag(tag) case node @ _ => node |> unexpectedNode } - + private def buildResultFromTerm(using ctx: Ctx)(tm: Statement)(k: Node => Node): Node = val res = tm match case lit: Lit => Literal(lit) |> sresult |> k case v @ Var(name) => if (name.isCapitalized) - val v = fresh.make - LetExpr(v, - CtorApp(ctx.classCtx(name), Nil), - v |> ref |> sresult |> k).attachTag(tag) + ctx.tyNameCtx.get(name) match + case Some(x) => + val v = fresh.make + ctx.classCtx.get(x.str) match + case Some(clsinfo) => + val args = (if clsinfo.freeVars.isEmpty then Nil else clsinfo.freeVars.map(x => Ref(ctx.nameCtx(x)))) + LetExpr(v, + CtorApp(ClassRef(Right(x.str)), args), + v |> ref |> sresult |> k).attachTag(tag) + case None => throw IRError(s"unknown class $name in $ctx") + case None => throw IRError(s"unknown type name $name in $ctx") else ctx.nameCtx.get(name) match { - case Some(x) => x |> ref |> sresult |> k - case _ => throw IRError(s"unknown name $name in $ctx") + case Some(x) => + if (ctx.fnCtx.contains(x.str)) + val info = ctx.fnCtx(x.str) + val arity = info.params.size - info.freeVars.size + val range = 0 until arity + val xs = range.map(_ => fresh.make.str).toList + val params = Tup(xs.map(x => N -> Fld(FldFlags.empty, Var(x)))) + val args = Tup(params.fields ++ info.freeVars.map(x => N -> Fld(FldFlags.empty, Var(x)))) + val lam = Lam(params, App(Var(x.str), args)) + buildResultFromTerm(lam)(k) + else + x |> ref |> sresult |> k + case None => throw IRError(s"unknown name $name in $ctx") } + case lam @ Lam(Tup(fields), body) => + val tmp = fresh.make + val lambdaName = fresh.make("Lambda") + val result = Var(lambdaName.str) + val localCls = Blk( + NuTypeDef(Cls, TypeName(lambdaName.str), Nil, N, N, N, Ls(Var("Callable")), N, N, TypingUnit( + NuFunDef(N, Var(s"apply${fields.size}"), None, Nil, L(Lam(Tup(fields), body)))(tm.getLoc, N, N, N, N, false, Nil) :: Nil + ))(tm.getLoc, tm.getLoc, Nil) :: result :: Nil) + buildResultFromTerm(localCls)(k) - case Lam(Tup(fields), body) => - throw IRError("not supported: lambda") case App( - App(Var(name), Tup((_ -> Fld(_, e1)) :: Nil)), + App(Var(name), Tup((_ -> Fld(_, e1)) :: Nil)), Tup((_ -> Fld(_, e2)) :: Nil)) if ctx.opCtx.contains(name) => buildResultFromTerm(e1) { case Result(v1 :: Nil) => @@ -131,19 +316,68 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres } case node @ _ => node |> unexpectedNode } - - case App(Var(name), xs @ Tup(_)) if name.isCapitalized => - buildResultFromTerm(xs) { - case Result(args) => - val v = fresh.make - LetExpr(v, - CtorApp(ctx.classCtx(name), args), - v |> ref |> sresult |> k).attachTag(tag) + case x @ App(Var(name), xs @ Tup(_)) if name.isCapitalized || ctx.opCtx.contains(name) => + if name.isCapitalized then + buildResultFromTerm(xs) { + case Result(args) => + ctx.tyNameCtx.get(name) match + case Some(x) => + val v = fresh.make + ctx.classCtx.get(x.str) match + case Some(clsinfo) => + val args2 = args ++ clsinfo.freeVars.map(x => Ref(ctx.nameCtx(x))) + LetExpr(v, + CtorApp(ClassRef(Right(x.str)), args2), + v |> ref |> sresult |> k).attachTag(tag) + case None => throw IRError(s"unknown class $name in $ctx") + case None => throw IRError(s"unknown type name $name in $ctx") + case node @ _ => node |> unexpectedNode + } + else + buildResultFromTerm(xs) { + case Result(args) => + val v = fresh.make + LetExpr(v, + BasicOp(name, args), + v |> ref |> sresult |> k).attachTag(tag) + case node @ _ => node |> unexpectedNode + } + case App( + member @ Sel(Var(clsName), Var(fld)), + xs @ Tup((_ -> Fld(_, Var(s))) :: _)) if clsName.isCapitalized => + buildResultFromTup(xs) { + case Result(xs @ (Ref(name) :: args)) => + ctx.tyNameCtx.get(clsName) match + case Some(x) => + val v = fresh.make + val cls = ctx.classCtx(x.str) + val isField = cls.fields.contains(fld) + if isField then + LetExpr(v, Select(name, ClassRef(Right(x.str)), fld), + v |> ref |> sresult |> k).attachTag(tag) + else + if cls.methods.contains(fld) then + LetMethodCall(Ls(v), ClassRef(Right(x.str)), Name(fld), xs, v |> ref |> sresult |> k).attachTag(tag) + else + throw IRError(s"unknown field or method $fld in $cls") + case None => throw IRError(s"unknown type name $clsName in $ctx") case node @ _ => node |> unexpectedNode } - - case App(f, xs @ Tup(_)) => buildLetCall(f, xs, None) - case Ann(ann, App(f, xs @ Tup(_))) => buildLetCall(f, xs, Some(ann)) + case App(vf @ Var(f), xs @ Tup(_)) if ctx.fnCtx.contains(f) || ctx.nameCtx.get(f).fold(false)(x => ctx.fnCtx.contains(x.str)) => + buildLetCall(vf, xs, None)(k) + case Ann(ann, App(vf @ Var(f), xs @ Tup(_))) if ctx.fnCtx.contains(f) || ctx.nameCtx.get(f).fold(false)(x => ctx.fnCtx.contains(x.str)) => + buildLetCall(vf, xs, Some(ann))(k) + case App(f, xs @ Tup(_)) => + buildResultFromTerm(f) { + case Result(Ref(f) :: Nil) => buildResultFromTerm(xs) { + case Result(args) => + val v = fresh.make + // LetApply(List(v), f, args, v |> ref |> sresult |> k).attachTag(tag) + LetMethodCall(List(v), ClassRef(R("Callable")), Name("apply" + args.length), (Ref(f): TrivialExpr) :: args, v |> ref |> sresult |> k).attachTag(tag) + case node @ _ => node |> unexpectedNode + } + case node @ _ => node |> unexpectedNode + } case Ann(ann @ Var(name), recv) => if name === "tailcall" then @@ -152,7 +386,6 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres raise(ErrorReport(List(msg"@tailrec may only be used to annotate functions" -> ann.toLoc), true, Diagnostic.Compilation)) buildResultFromTerm(recv)(k) - case Let(false, Var(name), rhs, body) => buildBinding(name, rhs, body)(k) @@ -183,16 +416,13 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres case Result(xs) => Jump(DefnRef(Right(jp.str)), xs ++ fvs.map(x => Ref(Name(x)))).attachTag(tag) case node @ _ => node |> unexpectedNode } - Case(cond, Ls((ctx.classCtx("True"), tru2), (ctx.classCtx("False"), fls2))).attachTag(tag) + Case(cond, Ls( + (Pat.Class(ClassRef(Right("True"))), tru2), + (Pat.Class(ClassRef(Right("False"))), fls2)), None).attachTag(tag) case node @ _ => node |> unexpectedNode } case If(IfOpApp(lhs, Var("is"), IfBlock(lines)), N) - if lines forall { - case L(IfThen(App(Var(ctor), Tup((N -> Fld(FldFlags.empty, _: Var)) :: _)), _)) => ctor.isCapitalized - case L(IfThen(Var(ctor), _)) => ctor.isCapitalized || ctor == "_" - case _ => false - } => buildResultFromTerm(lhs) { case Result(Ref(scrut) :: Nil) => val jp = fresh make "j" @@ -206,12 +436,13 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres resultNum = 1, jpbody, false, - None + None, ) ctx.jpAcc.addOne(jpdef) - val cases: Ls[(ClassInfo, Node)] = lines map { + var defaultCase: Opt[Node] = None + val cases: Ls[(Pat, Node)] = lines flatMap { case L(IfThen(App(Var(ctor), params: Tup), rhs)) => - ctx.classCtx(ctor) -> { + S(Pat.Class(ClassRef(Right(ctor))) -> { // need this because we have built terms (selections in case arms) containing names that are not in the original term given Ctx = ctx.copy(nameCtx = ctx.nameCtx + (scrut.str -> scrut)) buildResultFromTerm( @@ -219,151 +450,303 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres case Result(xs) => Jump(DefnRef(Right(jp.str)), xs ++ fvs.map(x => Ref(Name(x)))).attachTag(tag) case node @ _ => node |> unexpectedNode } - } + }) + case L(IfThen(lit @ IntLit(_), rhs)) => + S(Pat.Lit(lit) -> buildResultFromTerm(rhs) { + case Result(xs) => Jump(DefnRef(Right(jp.str)), xs ++ fvs.map(x => Ref(Name(x)))).attachTag(tag) + case node @ _ => node |> unexpectedNode + }) + case L(IfThen(Var("_"), rhs)) => + defaultCase = Some(buildResultFromTerm(rhs) { + case Result(xs) => Jump(DefnRef(Right(jp.str)), xs ++ fvs.map(x => Ref(Name(x)))).attachTag(tag) + case node @ _ => node |> unexpectedNode + }) + N case L(IfThen(Var(ctor), rhs)) => - ctx.classCtx(ctor) -> buildResultFromTerm(rhs) { + S(Pat.Class(ClassRef(Right(ctor))) -> buildResultFromTerm(rhs) { case Result(xs) => Jump(DefnRef(Right(jp.str)), xs ++ fvs.map(x => Ref(Name(x)))).attachTag(tag) case node @ _ => node |> unexpectedNode - } + }) case _ => throw IRError(s"not supported UCS") } - Case(scrut, cases).attachTag(tag) + Case(scrut, cases, defaultCase).attachTag(tag) case node @ _ => node |> unexpectedNode } case Bra(false, tm) => buildResultFromTerm(tm)(k) - case Blk((tm: Term) :: Nil) => buildResultFromTerm(tm)(k) - - case Blk((tm: Term) :: xs) => buildBinding("_", tm, Blk(xs))(k) - - case Blk(NuFunDef(S(false), Var(name), None, _, L(tm)) :: Nil) => - buildBinding(name, tm, Var(name))(k) - - case Blk(NuFunDef(S(false), Var(name), None, _, L(tm)) :: xs) => - buildBinding(name, tm, Blk(xs))(k) - - case Sel(tm @ Var(name), Var(fld)) => - buildResultFromTerm(tm) { - case Result(Ref(res) :: Nil) => - val v = fresh.make - val cls = ctx.fieldCtx(fld)._2 - LetExpr(v, - Select(res, cls, fld), - v |> ref |> sresult |> k).attachTag(tag) - case node @ _ => node |> unexpectedNode - } + case Blk(stmts) => + val (nfds, ntds, terms) = collectInfo(stmts) + val (ctx2, nfds2, ntds2) = renameToBeLifted(nfds, ntds) + val ctx3 = initContextForStatementsFrom( + nfds2, ntds2, terms, scanNamesInThisScope(stmts) + )(using ctx2) + + ctx.lcAcc.addAll(ntds2) + ctx.defAcc.addAll(nfds2) + + buildResultFromTerms(using ctx3)(terms)(k) case tup: Tup => buildResultFromTup(tup)(k) case term => term |> unexpectedTerm res - - private def buildDefFromNuFunDef(using ctx: Ctx)(nfd: Statement): Defn = nfd match - case nfd: NuFunDef => nfd match - case NuFunDef(_, Var(name), None, Nil, L(Lam(Tup(fields), body))) => - val strs = fields map { - case N -> Fld(FldFlags.empty, Var(x)) => x - case _ => throw IRError("unsupported field") + + private def buildResultFromTerms(using ctx: Ctx)(tms: Ls[Statement])(k: Node => Node): Node = + tms match + case Nil => throw IRError("empty term list") + case NuFunDef(S(false), Var(name), _, _, Left(tm)) :: xs => + xs match + case Nil => throw IRError("unsupported NuFunDef at tail position") + case _ => buildResultFromTerm(tm) { + case Result((r: Ref) :: Nil) => + given Ctx = ctx.copy(nameCtx = ctx.nameCtx + (name -> r.name)) + buildResultFromTerms(xs)(k) + case Result((lit: Literal) :: Nil) => + val v = fresh.make + given Ctx = ctx.copy(nameCtx = ctx.nameCtx + (name -> v)) + LetExpr(v, + lit, + buildResultFromTerms(xs)(k)).attachTag(tag) + case node @ _ => node |> unexpectedNode } - val names = strs map (fresh.make(_)) - given Ctx = ctx.copy(nameCtx = ctx.nameCtx ++ (strs zip names)) - val trAnn = nfd.annotations.find { - case Var("tailrec") => true - case ann @ Var("tailcall") => - raise(ErrorReport(List(msg"@tailcall is for annotating function calls; try @tailrec instead" -> ann.toLoc), true, Diagnostic.Compilation)) - false - case _ => false } - - Defn( - fnUid.make, - name, - params = names, - resultNum = 1, - buildResultFromTerm(body) { x => x }, - trAnn.isDefined, - trAnn.flatMap(_.toLoc) - ) - case _ => throw IRError("unsupported NuFunDef") - case _ => throw IRError("unsupported NuFunDef") - - private def buildClassInfo(ntd: Statement): ClassInfo = ntd match - case NuTypeDef(Cls, TypeName(name), Nil, S(Tup(args)), N, N, Nil, N, N, TypingUnit(Nil)) => - ClassInfo( - classUid.make, - name, - args map { - case N -> Fld(FldFlags.empty, Var(name)) => name - case _ => throw IRError("unsupported field") - } - ) - case NuTypeDef(Cls, TypeName(name), Nil, N, N, N, Nil, N, N, TypingUnit(Nil)) => - ClassInfo( - classUid.make, + case x :: Nil => buildResultFromTerm(x)(k) + case x :: xs => buildResultFromTerm(x) { + case _ => buildResultFromTerms(xs)(k) + } + + private def getTupleFields(tup: Tup) = tup.fields.map { + case N -> Fld(FldFlags.empty, Var(name)) => name + case S(Var(name)) -> Fld(_, ty) => name + case _ => throw IRError("unsupported field") + } + + private def buildDefFromMethod(using ctx: Ctx)(fields: List[Str], nfd: Statement): Defn = nfd match + case nfd @ NuFunDef(_, Var(name), None, Nil, L(Lam(xs @ Tup(_), body))) => + val defnInfoPartial = getDefnInfoPartial(ctx.fnCtx.keySet ++ ctx.classCtx.keySet ++ fields, ctx)(nfd).get + val params = defnInfoPartial.params + val names = params map (fresh.make(_)) + val ctx2 = ctxJoin(ctx, defnInfoPartial.ctx) + given Ctx = ctx2.copy(nameCtx = ctx2.nameCtx ++ (params zip names)) + Defn( + fnUid.make, name, - Ls(), + params = names, + resultNum = 1, + body = buildResultFromTerm(body) { x => x }, + isTailRec = false, + loc = nfd.getLoc, ) - case x @ _ => throw IRError(f"unsupported NuTypeDef $x") + case fd @ _ => throw IRError("unsupported NuFunDef " + fd.toString) - private def checkDuplicateField(ctx: Set[Str], cls: ClassInfo): Set[Str] = - val u = cls.fields.toSet intersect ctx - if (u.nonEmpty) throw IRError(f"duplicate class field $u") - cls.fields.toSet union ctx - - private def getDefinitionName(nfd: Statement): Str = nfd match - case NuFunDef(_, Var(name), _, _, _) => name - case _ => throw IRError("unsupported NuFunDef") - - def buildGraph(unit: TypingUnit): Program = unit match - case TypingUnit(entities) => - val grouped = entities groupBy { - case ntd: NuTypeDef => 0 - case nfd: NuFunDef => 1 - case tm: Term => 2 - case _ => throw IRError("unsupported entity") + private def buildDefFromNuFunDef(using ctx: Ctx)(nfd: Statement): Defn = nfd match + case nfd @ NuFunDef(_, Var(name), None, Nil, L(Lam(xs @ Tup(_), body))) => + val defnInfoPartial = getDefnInfoPartial(ctx.fnCtx.keySet ++ ctx.classCtx.keySet, ctx)(nfd).get + val params = defnInfoPartial.params + val names = params map (fresh.make(_)) + val ctx2 = ctxJoin(ctx, defnInfoPartial.ctx) + given Ctx = ctx2.copy(nameCtx = ctx2.nameCtx ++ (params zip names)) + val trAnn = nfd.annotations.find { + case Var("tailrec") => true + case ann @ Var("tailcall") => + raise(ErrorReport(List(msg"@tailcall is for annotating function calls; try @tailrec instead" -> ann.toLoc), true, Diagnostic.Compilation)) + false + case _ => false } + Defn( + fnUid.make, + name, + params = names, + resultNum = 1, + buildResultFromTerm(body) { x => x }, + trAnn.isDefined, + trAnn.flatMap(_.toLoc) + ) + case fd @ _ => throw IRError("unsupported NuFunDef " + fd.toString) + + private def buildClassInfo(using ctx: Ctx)(ntd: Statement): ClassInfo = ntd match + case ntd @ NuTypeDef(Cls | Mod, TypeName(name), _, params, N, _, parents, N, N, TypingUnit(methods)) => + val clsInfoPartial = getClassInfoPartial(ctx.classCtx.keySet ++ ctx.fnCtx.keySet, ctx)(ntd) + val cls = ClassInfo(classUid.make, name, clsInfoPartial.fields) + val ctx2 = ctxJoin(ctx, clsInfoPartial.ctx) + given Ctx = ctx2.copy( + nameCtx = ctx2.nameCtx ++ clsInfoPartial.fields.map(x => x -> Name(x)), + classCtx = ctx2.classCtx + (name -> clsInfoPartial) + ) + def resolveParentName(parent: Term): String = parent match { + case Var(name) if name.isCapitalized => name + case TyApp(lhs, _) => resolveParentName(lhs) + case App(lhs, _) => resolveParentName(lhs) + case _ => throw IRError("unsupported parent") } + cls.parents = parents.map(resolveParentName).toSet + cls.methods = methods.map { + case x: NuFunDef => x.name -> buildDefFromMethod(clsInfoPartial.fields, x) + case x @ _ => throw IRError(f"unsupported method $x") + }.toMap + cls + case x @ _ => throw IRError(f"unsupported NuTypeDef $x") - import scala.collection.mutable.{ HashSet => MutHSet } - - // TODO: properly add prelude classes such as "True" and "False" rather than this hacky method - val cls = ClassInfo(classUid.make, "True", List()) - :: ClassInfo(classUid.make, "False", List()) - :: grouped.getOrElse(0, Nil).map(buildClassInfo) - - cls.foldLeft(Set.empty)(checkDuplicateField(_, _)) - - val clsinfo = cls.toSet - val defn_names = grouped.getOrElse(1, Nil).map(getDefinitionName) - val class_ctx: ClassCtx = cls.map { case ClassInfo(_, name, _) => name }.zip(cls).toMap - val field_ctx: FieldCtx = cls.flatMap { case ClassInfo(_, name, fields) => fields.map((_, (name, class_ctx(name)))) }.toMap - val fn_ctx: FnCtx = defn_names.toSet - var name_ctx: NameCtx = defn_names.zip(defn_names.map(Name(_))).toMap ++ ops.map { op => (op, Name(op)) }.toList - - val jp_acc = ListBuffer.empty[Defn] - given Ctx = Ctx( - nameCtx = name_ctx, - classCtx = class_ctx, - fieldCtx = field_ctx, - fnCtx = fn_ctx, - opCtx = ops, - jpAcc = jp_acc, - ) - var defs: Set[Defn] = grouped.getOrElse(1, Nil).map(buildDefFromNuFunDef).toSet - val terms: Ls[Term] = grouped.getOrElse(2, Nil).map { - case tm: Term => tm - case _ => ??? /* unreachable */ + private def getDefnInfoPartial(names: Set[Str], ctx: Ctx)(nfd: NuFunDef): Opt[DefnInfoPartial] = nfd match + case NuFunDef(_, Var(name), _, _, Left(Lam(Var(x), _))) => + val originalFvs = freeVariables(nfd)._2 + val fvs = (originalFvs -- builtin -- ops -- names).toList + val params = x :: fvs + val dip = DefnInfoPartial(name, params) + dip.freeVars = fvs + dip.ctx = ctx + S(dip) + case NuFunDef(_, Var(name), _, _, Left(Lam(tp @ Tup(fields), _))) => + val originalFvs = freeVariables(nfd)._2 + val fvs = (originalFvs -- builtin -- ops -- names).toList + val params = getTupleFields(tp) ++ fvs + val dip = DefnInfoPartial(name, params) + dip.freeVars = fvs + dip.ctx = ctx + S(dip) + case NuFunDef(_, Var(name), _, _, _) => N + + + private def getClassInfoPartial(names: Set[Str], ctx: Ctx)(ntd: NuTypeDef): ClassInfoPartial = ntd match + case ntd @ NuTypeDef(Cls | Mod, TypeName(name), _, params, N, _, parents, N, N, TypingUnit(other)) => + val originalFvs = freeVariables(ntd)._2 + log(s"getClassInfoPartial $name") + log(originalFvs) + log(names) + val fvs = (originalFvs -- builtin -- ops -- names).toList + val fields = params.fold(fvs)(xs => getTupleFields(xs) ++ fvs) + val methods = other.map { + case x: NuFunDef => x.name + case x @ _ => throw IRError(f"unsupported method $x") } + val cls = ClassInfoPartial(name, fields, methods.toSet) + cls.freeVars = fvs + cls.ctx = ctx + cls + case x @ _ => throw IRError(f"unsupported NuTypeDef $x") + + private def collectInfo(stmts: Ls[Statement]): (Ls[NuFunDef], Ls[NuTypeDef], Ls[Statement]) = + var nfds = ListBuffer.empty[NuFunDef] + var ntds = ListBuffer.empty[NuTypeDef] + var terms = ListBuffer.empty[Statement] + stmts.foreach { + case tm @ NuFunDef(S(false), Var(_), _, _, Left(_)) => + terms.addOne(tm) + case nfd: NuFunDef => nfds.addOne(nfd) + case ntd: NuTypeDef => ntds.addOne(ntd) + case tm: Term => terms.addOne(tm) + case _ => throw IRError("unsupported statement") + } + (nfds.toList, ntds.toList, terms.toList) - val main = buildResultFromTerm (terms match { - case x :: Nil => x - case _ => throw IRError("only one term is allowed in the top level scope") - }) { k => k } - - defs ++= jp_acc.toList + private def makeNameMap(str: IterableOnce[Str]) = str.iterator.map(x => (x, Name(x))).toMap - resolveDefnRef(main, defs, true) - validate(main, defs) - - Program(clsinfo, defs, main) + private def scanNamesInThisScope(stmt: Ls[Statement]): Set[Str] = + val names = stmt flatMap { + case NuTypeDef(_, TypeName(name), _, _, _, _, _, _, _, _) => S(name) + case NuFunDef(_, Var(name), _, _, _) => S(name) + case _ => Nil + } + names.toSet + + private def renameToBeLifted(nfds: Ls[NuFunDef], ntds: Ls[NuTypeDef])(using ctx: Ctx): (Ctx, Ls[NuFunDef], Ls[NuTypeDef]) = + val oldFnNames = scanNamesInThisScope(nfds).toList + val oldTyNames = scanNamesInThisScope(ntds).toList + // TODO: currently, rename cause bugs + //val newFnNames = oldFnNames.map(x => fresh.make(x)) + //val newTyNames = oldTyNames.map(x => if x.startsWith("Lambda$") then Name(x) else fresh.make(x)) + val newFnNames = oldFnNames.map(Name(_)) + val newTyNames = oldTyNames.map(Name(_)) + val nameCtx = oldFnNames.zip(newFnNames).toMap + val tyNameCtx = oldTyNames.zip(newTyNames).toMap + val nfds2 = nfds.map(x => x.copy(nme = Var(nameCtx(x.name).str))(x.declareLoc, x.virtualLoc, x.mutLoc, x.signature, x.outer, x.genField, x.annotations)) + val ntds2 = ntds.map(x => x.copy(nme = TypeName(tyNameCtx(x.name).str))(x.declareLoc, x.abstractLoc, x.annotations)) + + (ctx.copy(nameCtx = ctx.nameCtx ++ nameCtx, tyNameCtx = ctx.tyNameCtx ++ tyNameCtx), nfds2, ntds2) + + private def initContextForStatementsFrom(nfds: Ls[NuFunDef], ntds: Ls[NuTypeDef], terms: Ls[Statement], excluded: Set[Str])(using ctx: Ctx): Ctx = + // they are in the same mutual group or higher group, mustn't capture them + val excludedNames = excluded ++ scanNamesInThisScope(nfds) ++ scanNamesInThisScope(ntds) ++ ctx.fnCtx.keySet ++ ctx.classCtx.keySet + val partialDefnInfo = nfds flatMap getDefnInfoPartial(excludedNames, ctx) + val partialClassInfo = ntds map getClassInfoPartial(excludedNames, ctx) + val fnNames = partialDefnInfo.map(_.name) + val fnCtx = fnNames.zip(partialDefnInfo).toMap + val nameCtx = makeNameMap(builtin) ++ makeNameMap(fnNames) ++ makeNameMap(ops) + val tyNames = partialClassInfo.map(_.name) + val tyNameCtx = makeNameMap(tyNames) + val classCtx = tyNames.zip(partialClassInfo).toMap + + ctx.copy( + tyNameCtx = ctx.tyNameCtx ++ tyNameCtx, + nameCtx = ctx.nameCtx ++ nameCtx, + classCtx = ctx.classCtx ++ classCtx, + fnCtx = ctx.fnCtx ++ fnCtx, + ) + + private def initContextForStatements(nfds: Ls[NuFunDef], ntds: Ls[NuTypeDef], terms: Ls[Statement]): Ctx = + val ctx = Ctx( + nameCtx = Map.empty, + tyNameCtx = Map.empty, + classCtx = Map.empty, + fnCtx = Map.empty, + opCtx = ops, + jpAcc = ListBuffer.empty, + defAcc = ListBuffer.empty, + lcAcc = ListBuffer.empty, + ) + initContextForStatementsFrom(nfds, ntds, terms, Set.empty)(using ctx) + + def getHiddenNames(prelude: TypingUnit): Set[Str] = + def resolveTypeName(x: Term): Str = x match + case Var(name) => name + case TyApp(lhs, _) => resolveTypeName(lhs) + case App(lhs, _) => resolveTypeName(lhs) + case _ => throw IRError("unsupported type name") + val hidden = prelude.rawEntities.flatMap { + case NuFunDef(_, Var(name), _, _, _) => Nil + case NuTypeDef(_, TypeName(name), _, params, _, _, _, _, _, _) if name == "HiddenTheseEntities" => + params.fold{Nil}{ + x => x.fields.flatMap { + case S(Var(name)) -> Fld(FldFlags.empty, ty) => resolveTypeName(ty) :: Nil + case _ => Nil + } + } + case NuTypeDef(_, TypeName(name), _, _, _, _, _, _, _, _) => Nil + case _ => Nil + } + hidden.toSet + + def buildGraph(unit: TypingUnit, prelude: TypingUnit, addPrelude: Boolean = true): Program = + val unit2 = if addPrelude then TypingUnit(prelude.rawEntities ++ unit.rawEntities) else unit + val hiddenNames = getHiddenNames(unit2) + val (nfds, ntds, terms) = collectInfo(unit2.rawEntities) + var ctx = initContextForStatements(nfds, ntds, terms) + + val definitions = ListBuffer.empty[Defn] + val classes = ListBuffer.empty[ClassInfo] + + var first = true + + var curNfds = nfds + var curNtds = ntds + val main = buildResultFromTerm(using ctx)(Blk(terms)){ k => k } + while first || ctx.hasLifted do + first = false + ctx.jpAcc.clear() + ctx.defAcc.clear() + ctx.lcAcc.clear() + definitions.addAll(curNfds.map(buildDefFromNuFunDef(using ctx))) + definitions.addAll(ctx.jpAcc) + classes.addAll(curNtds.map(buildClassInfo(using ctx))) + curNfds = ctx.defAcc.toList + curNtds = ctx.lcAcc.toList + ctx = initContextForStatementsFrom(curNfds, curNtds, Nil, Set.empty)(using ctx) + + val clsInfo = classes.toSet + val defs = definitions.toSet + + resolveRef(main, defs, clsInfo, true) + validate(main, defs, clsInfo) + + Program(clsInfo, defs, main) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala b/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala deleted file mode 100644 index 96ee1d63..00000000 --- a/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala +++ /dev/null @@ -1,33 +0,0 @@ -package mlscript.compiler.ir - -import mlscript.utils.shorthands._ -import mlscript.compiler.ir._ - -import Node._ - -// Resolves the definition references by turning them from Right(name) to Left(Defn). -private final class DefnRefResolver(defs: Set[Defn], allowInlineJp: Bool): - private def f(x: Node): Unit = x match - case Result(res) => - case Case(scrut, cases) => cases map { (_, body) => f(body) } - case LetExpr(name, expr, body) => f(body) - case LetCall(resultNames, defnref, args, _, body) => - defs.find{_.getName == defnref.getName} match - case Some(defn) => defnref.defn = Left(defn) - case None => throw IRError(f"unknown function ${defnref.getName} in ${defs.map{_.getName}.mkString(",")}") - f(body) - case Jump(defnref, _) => - // maybe not promoted yet - defs.find{_.getName == defnref.getName} match - case Some(defn) => defnref.defn = Left(defn) - case None => - if (!allowInlineJp) - throw IRError(f"unknown function ${defnref.getName} in ${defs.map{_.getName}.mkString(",")}") - def run(node: Node) = f(node) - def run(node: Defn) = f(node.body) - - -def resolveDefnRef(entry: Node, defs: Set[Defn], allowInlineJp: Bool = false): Unit = - val rl = DefnRefResolver(defs, allowInlineJp) - rl.run(entry) - defs.foreach(rl.run(_)) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Fresh.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Fresh.scala index de2ed437..2ea438dc 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Fresh.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Fresh.scala @@ -3,13 +3,12 @@ package mlscript.compiler.ir import collection.mutable.{HashMap => MutHMap} import mlscript.utils.shorthands._ -final class Fresh: +final class Fresh(div : Char = '$'): private val counter = MutHMap[Str, Int]() - private val div = '$' private def gensym(s: Str) = { val n = s.lastIndexOf(div) val (ts, suffix) = s.splitAt(if n == -1 then s.length() else n) - var x = if suffix.stripPrefix(div.toString()).forall(_.isDigit) then ts else s + var x = if suffix.stripPrefix(div.toString).forall(_.isDigit) then ts else s val count = counter.getOrElse(x, 0) val tmp = s"$x$div$count" counter.update(x, count + 1) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala index a84f48fd..5bc51bc5 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala @@ -3,8 +3,9 @@ package mlscript.compiler.ir import mlscript._ import mlscript.utils._ import mlscript.utils.shorthands._ -import mlscript.compiler.ir._ +import mlscript.compiler.utils._ import mlscript.compiler.optimizer._ +import mlscript.compiler.ir._ import mlscript.Loc @@ -12,6 +13,7 @@ import collection.mutable.{Map as MutMap, Set as MutSet, HashMap, ListBuffer} import annotation.unused import util.Sorting import scala.collection.immutable.SortedSet +import scala.language.implicitConversions final case class IRError(message: String) extends Exception(message) @@ -25,7 +27,22 @@ case class Program( val t2 = defs.toArray Sorting.quickSort(t1) Sorting.quickSort(t2) - s"Program({${t1.mkString(",")}}, {\n${t2.mkString("\n")}\n},\n$main)" + s"Program({${t1.mkString(",\n")}}, {\n${t2.mkString("\n")}\n},\n$main)" + + def show(hiddenNames: Set[Str] = Set.empty) = toDocument(hiddenNames).print + + def toDocument(hiddenNames: Set[Str] = Set.empty) : Document = + val t1 = classes.toArray + val t2 = defs.toArray + Sorting.quickSort(t1) + Sorting.quickSort(t2) + given Conversion[String, Document] = raw + stack( + "Program:", + stack_list(t1.filter(x => !hiddenNames.contains(x.name)).map(_.toDocument).toList) |> indent, + stack_list(t2.map(_.toDocument).toList) |> indent, + main.toDocument |> indent + ) implicit object ClassInfoOrdering extends Ordering[ClassInfo] { def compare(a: ClassInfo, b: ClassInfo) = a.id.compare(b.id) @@ -33,99 +50,132 @@ implicit object ClassInfoOrdering extends Ordering[ClassInfo] { case class ClassInfo( id: Int, - ident: Str, + name: Str, fields: Ls[Str], ): + var parents: Set[Str] = Set.empty + var methods: Map[Str, Defn] = Map.empty override def hashCode: Int = id override def toString: String = - s"ClassInfo($id, $ident, [${fields mkString ","}])" + s"ClassInfo($id, $name, [${fields mkString ","}], parents: ${parents mkString ","}, methods:\n${methods mkString ",\n"})" + + def show = toDocument.print + def toDocument: Document = + given Conversion[String, Document] = raw + val extension = if parents.isEmpty then "" else " extends " + parents.mkString(", ") + if methods.isEmpty then + "class" <:> name <#> "(" <#> fields.mkString(",") <#> ")" <#> extension + else + stack( + "class" <:> name <#> "(" <#> fields.mkString(",") <#> ")" <#> extension <:> "{", + stack_list( methods.map { (_, defn) => defn.toDocument |> indent }.toList), + "}" + ) -case class Name(str: Str): +case class Name(val str: Str): def copy: Name = Name(str) def trySubst(map: Map[Str, Name]) = map.getOrElse(str, this) - override def toString: String = str class DefnRef(var defn: Either[Defn, Str]): - def getName: String = defn.fold(_.getName, x => x) + def name: String = defn.fold(_.name, x => x) def expectDefn: Defn = defn match { - case Left(godef) => godef + case Left(defn) => defn case Right(name) => throw Exception(s"Expected a def, but got $name") } def getDefn: Opt[Defn] = defn.left.toOption override def equals(o: Any): Bool = o match { - case o: DefnRef => o.getName == this.getName + case o: DefnRef => o.name == this.name case _ => false } +class ClassRef(var cls: Either[ClassInfo, Str]): + def name: String = cls.fold(_.name, x => x) + def expectClass: ClassInfo = cls match { + case Left(cls) => cls + case Right(name) => throw Exception(s"Expected a class, but got $name") + } + def getClass: Opt[ClassInfo] = cls.left.toOption + override def equals(o: Any): Bool = o match { + case o: ClassRef => o.name == this.name + case _ => false + } implicit object DefOrdering extends Ordering[Defn] { def compare(a: Defn, b: Defn) = a.id.compare(b.id) } case class Defn( - val id: Int, - val name: Str, - val params: Ls[Name], - val resultNum: Int, - val body: Node, - val isTailRec: Bool, - val loc: Opt[Loc] = None + id: Int, + name: Str, + params: Ls[Name], + resultNum: Int, + body: Node, + isTailRec: Bool, + loc: Opt[Loc] = None ): override def hashCode: Int = id - def getName: String = name override def toString: String = val ps = params.map(_.toString).mkString("[", ",", "]") s"Def($id, $name, $ps,\n$resultNum, \n$body\n)" + def show = toDocument.print + + def toDocument: Document = + given Conversion[String, Document] = raw + stack( + "def" <:> name <#> "(" <#> params.map(_.toString).mkString(",") <#> ")" <:> "=", + body.toDocument |> indent + ) + sealed trait TrivialExpr: import Expr._ override def toString: String def show: String def toDocument: Document - def mapNameOfTrivialExpr(f: Name => Name): TrivialExpr = this match case x: Ref => Ref(f(x.name)) case x: Literal => x - def toExpr: Expr = this match { case x: Expr => x } -private def show_args(args: Ls[TrivialExpr]) = args map (_.show) mkString "," +private def showArguments(args: Ls[TrivialExpr]) = args map (_.show) mkString "," enum Expr: case Ref(name: Name) extends Expr, TrivialExpr case Literal(lit: Lit) extends Expr, TrivialExpr - case CtorApp(name: ClassInfo, args: Ls[TrivialExpr]) - case Select(name: Name, cls: ClassInfo, field: Str) + case CtorApp(cls: ClassRef, args: Ls[TrivialExpr]) + case Select(name: Name, cls: ClassRef, field: Str) case BasicOp(name: Str, args: Ls[TrivialExpr]) - case AssignField(assignee: Name, clsInfo: ClassInfo, fieldName: Str, value: TrivialExpr) + case AssignField(assignee: Name, cls: ClassRef, field: Str, value: TrivialExpr) override def toString: String = show def show: String = toDocument.print - def toDocument: Document = this match - case Ref(s) => s.toString |> raw - case Literal(IntLit(lit)) => s"$lit" |> raw - case Literal(DecLit(lit)) => s"$lit" |> raw - case Literal(StrLit(lit)) => s"$lit" |> raw - case Literal(UnitLit(lit)) => (if lit then "undefined" else "null") |> raw - case CtorApp(ClassInfo(_, name, _), args) => - raw(name) <#> raw("(") <#> raw(args |> show_args) <#> raw(")") - case Select(s, _, fld) => - raw(s.toString) <#> raw(".") <#> raw(fld) + def toDocument: Document = + given Conversion[String, Document] = raw + this match + case Ref(s) => s.toString + case Literal(IntLit(lit)) => s"$lit" + case Literal(DecLit(lit)) => s"$lit" + case Literal(StrLit(lit)) => s"$lit" + case Literal(UnitLit(lit)) => s"$lit" + case CtorApp(cls, args) => + cls.name <#> "(" <#> (args |> showArguments) <#> ")" + case Select(s, cls, fld) => + cls.name <#> "." <#> fld <#> "(" <#> s.toString <#> ")" case BasicOp(name: Str, args) => - raw(name) <#> raw("(") <#> raw(args |> show_args) <#> raw(")") + name <#> "(" <#> (args |> showArguments) <#> ")" case AssignField(assignee, clsInfo, fieldName, value) => stack( - raw("assign") - <:> raw(assignee.toString + "." + fieldName) - <:> raw(":=") - <:> value.toDocument - ) + "assign" + <:> (assignee.toString + "." + fieldName) + <:> ":=" + <:> value.toDocument + ) def mapName(f: Name => Name): Expr = this match case Ref(name) => Ref(f(name)) @@ -138,18 +188,38 @@ enum Expr: def locMarker: LocMarker = this match case Ref(name) => LocMarker.MRef(name.str) case Literal(lit) => LocMarker.MLit(lit) - case CtorApp(name, args) => LocMarker.MCtorApp(name, args.map(_.toExpr.locMarker)) + case CtorApp(cls, args) => LocMarker.MCtorApp(cls, args.map(_.toExpr.locMarker)) case Select(name, cls, field) => LocMarker.MSelect(name.str, cls, field) case BasicOp(name, args) => LocMarker.MBasicOp(name, args.map(_.toExpr.locMarker)) case AssignField(assignee, clsInfo, fieldName, value) => LocMarker.MAssignField(assignee.str, fieldName, value.toExpr.locMarker) +enum Pat: + case Lit(lit: mlscript.Lit) + case Class(cls: ClassRef) + + def isTrue = this match + case Class(cls) => cls.name == "True" + case _ => false + + def isFalse = this match + case Class(cls) => cls.name == "False" + case _ => false + + override def toString: String = this match + case Lit(lit) => s"$lit" + case Class(cls) => s"${cls.name}" + enum Node: // Terminal forms: case Result(res: Ls[TrivialExpr]) case Jump(defn: DefnRef, args: Ls[TrivialExpr]) - case Case(scrut: Name, cases: Ls[(ClassInfo, Node)]) + case Case(scrut: Name, cases: Ls[(Pat, Node)], default: Opt[Node]) // Intermediate forms: case LetExpr(name: Name, expr: Expr, body: Node) + case LetMethodCall(names: Ls[Name], cls: ClassRef, method: Name, args: Ls[TrivialExpr], body: Node) + // Deprecated: + // LetApply(names, fn, args, body) => LetMethodCall(names, ClassRef(R("Callable")), Name("apply" + args.length), (Ref(fn): TrivialExpr) :: args, body) + // case LetApply(names: Ls[Name], fn: Name, args: Ls[TrivialExpr], body: Node) case LetCall(names: Ls[Name], defn: DefnRef, args: Ls[TrivialExpr], isTailRec: Bool, body: Node)(val loc: Opt[Loc] = None) var tag = DefnTag(-1) @@ -171,78 +241,197 @@ enum Node: def mapName(f: Name => Name): Node = this match case Result(res) => Result(res.map(_.mapNameOfTrivialExpr(f))) case Jump(defn, args) => Jump(defn, args.map(_.mapNameOfTrivialExpr(f))) - case Case(scrut, cases) => Case(f(scrut), cases.map { (cls, arm) => (cls, arm.mapName(f)) }) + case Case(scrut, cases, default) => Case(f(scrut), cases.map { (cls, arm) => (cls, arm.mapName(f)) }, default.map(_.mapName(f))) case LetExpr(name, expr, body) => LetExpr(f(name), expr.mapName(f), body.mapName(f)) - case x: LetCall => - val LetCall(names, defn, args, isTailRec, body) = x - LetCall(names.map(f), defn, args.map(_.mapNameOfTrivialExpr(f)), isTailRec, body.mapName(f))(x.loc) + case LetMethodCall(names, cls, method, args, body) => LetMethodCall(names.map(f), cls, f(method), args.map(_.mapNameOfTrivialExpr(f)), body.mapName(f)) + case lc @ LetCall(names, defn, args, itr, body) => LetCall(names.map(f), defn, args.map(_.mapNameOfTrivialExpr(f)), itr, body.mapName(f))(lc.loc) def copy(ctx: Map[Str, Name]): Node = this match case Result(res) => Result(res.map(_.mapNameOfTrivialExpr(_.trySubst(ctx)))) case Jump(defn, args) => Jump(defn, args.map(_.mapNameOfTrivialExpr(_.trySubst(ctx)))) - case Case(scrut, cases) => Case(ctx(scrut.str), cases.map { (cls, arm) => (cls, arm.copy(ctx)) }) + case Case(scrut, cases, default) => Case(ctx(scrut.str), cases.map { (cls, arm) => (cls, arm.copy(ctx)) }, default.map(_.copy(ctx))) case LetExpr(name, expr, body) => val name_copy = name.copy LetExpr(name_copy, expr.mapName(_.trySubst(ctx)), body.copy(ctx + (name_copy.str -> name_copy))) - case x: LetCall => - val LetCall(names, defn, args, isTailRec, body) = x + case LetMethodCall(names, cls, method, args, body) => val names_copy = names.map(_.copy) - LetCall(names_copy, defn, args.map(_.mapNameOfTrivialExpr(_.trySubst(ctx))), isTailRec, body.copy(ctx ++ names_copy.map(x => x.str -> x)))(x.loc) + LetMethodCall(names_copy, cls, method.copy, args.map(_.mapNameOfTrivialExpr(_.trySubst(ctx))), body.copy(ctx ++ names_copy.map(x => x.str -> x))) + case lc @ LetCall(names, defn, args, itr, body) => + val names_copy = names.map(_.copy) + LetCall(names_copy, defn, args.map(_.mapNameOfTrivialExpr(_.trySubst(ctx))), itr, body.copy(ctx ++ names_copy.map(x => x.str -> x)))(lc.loc) - private def toDocument: Document = this match - case Result(res) => raw(res |> show_args) <:> raw(s"-- $tag") + def toDocument: Document = + given Conversion[String, Document] = raw + this match + case Result(res) => (res |> showArguments) <:> s"-- $tag" case Jump(jp, args) => - raw("jump") - <:> raw(jp.getName) - <#> raw("(") - <#> raw(args |> show_args) - <#> raw(")") - <:> raw(s"-- $tag") - case Case(x, Ls((tcls, tru), (fcls, fls))) if tcls.ident == "True" && fcls.ident == "False" => - val first = raw("if") <:> raw(x.toString) <:> raw(s"-- $tag") - val tru2 = indent(stack(raw("true") <:> raw ("=>"), tru.toDocument |> indent)) - val fls2 = indent(stack(raw("false") <:> raw ("=>"), fls.toDocument |> indent)) + "jump" + <:> jp.name + <#> "(" + <#> (args |> showArguments) + <#> ")" + <:> s"-- $tag" + case Case(x, Ls((tpat, tru), (fpat, fls)), N) if tpat.isTrue && fpat.isFalse => + val first = "if" <:> x.toString <:> s"-- $tag" + val tru2 = indent(stack("true" <:> "=>", tru.toDocument |> indent)) + val fls2 = indent(stack("false" <:> "=>", fls.toDocument |> indent)) Document.Stacked(Ls(first, tru2, fls2)) - case Case(x, cases) => - val first = raw("case") <:> raw(x.toString) <:> raw("of") <:> raw(s"-- $tag") - val other = cases map { - case (ClassInfo(_, name, _), node) => - indent(stack(raw(name) <:> raw("=>"), node.toDocument |> indent)) + case Case(x, cases, default) => + val first = "case" <:> x.toString <:> "of" <:> s"-- $tag" + val other = cases flatMap { + case (pat, node) => + Ls(pat.toString <:> "=>", node.toDocument |> indent) } - Document.Stacked(first :: other) + default match + case N => stack(first, (Document.Stacked(other) |> indent)) + case S(dc) => + val default = Ls("_" <:> "=>", dc.toDocument |> indent) + stack(first, (Document.Stacked(other ++ default) |> indent)) case LetExpr(x, expr, body) => stack( - raw("let") - <:> raw(x.toString) - <:> raw("=") + "let" + <:> x.toString + <:> "=" <:> expr.toDocument - <:> raw("in") - <:> raw(s"-- $tag"), + <:> "in" + <:> s"-- $tag", + body.toDocument) + case LetMethodCall(xs, cls, method, args, body) => + stack( + "let" + <:> xs.map(_.toString).mkString(",") + <:> "=" + <:> cls.name + <#> "." + <#> method.toString + <#> "(" + <#> args.map{ x => x.toString }.mkString(",") + <#> ")" + <:> "in" + <:> s"-- $tag", body.toDocument) - case LetCall(xs, defn, args, isTailRec, body) => + case LetCall(xs, defn, args, itr, body) => stack( - raw("let*") - <:> raw("(") - <#> raw(xs.map(_.toString).mkString(",")) - <#> raw(")") - <:> raw("=") - <:> raw((if isTailRec then "@tailcall " else "") + defn.getName) - <#> raw("(") - <#> raw(args.map{ x => x.toString }.mkString(",")) - <#> raw(")") - <:> raw("in") - <:> raw(s"-- $tag"), + "let*" + <:> "(" + <#> xs.map(_.toString).mkString(",") + <#> ")" + <:> "=" + <:> (if itr then "@tailcall " else "") + defn.name + <#> "(" + <#> args.map{ x => x.toString }.mkString(",") + <#> ")" + <:> "in" + <:> s"-- $tag", body.toDocument) + def locMarker: LocMarker = val marker = this match case Result(res) => LocMarker.MResult(res.map(_.toExpr.locMarker)) - case Jump(defn, args) => LocMarker.MJump(defn.getName, args.map(_.toExpr.locMarker)) - case Case(scrut, cases) => LocMarker.MCase(scrut.str, cases.map(_._1)) + case Jump(defn, args) => LocMarker.MJump(defn.name, args.map(_.toExpr.locMarker)) + case Case(scrut, cases, default) => LocMarker.MCase(scrut.str, cases.map(_._1), default.isDefined) case LetExpr(name, expr, _) => LocMarker.MLetExpr(name.str, expr.locMarker) - case LetCall(names, defn, args, _, _) => LocMarker.MLetCall(names.map(_.str), defn.getName, args.map(_.toExpr.locMarker)) + case LetMethodCall(names, cls, method, args, _) => LocMarker.MLetMethodCall(names.map(_.str), cls, method.str, args.map(_.toExpr.locMarker)) + case LetCall(names, defn, args, _, _) => LocMarker.MLetCall(names.map(_.str), defn.name, args.map(_.toExpr.locMarker)) marker.tag = this.tag marker + +trait DefTraversalOrdering: + def ordered(entry: Node, defs: Set[Defn]): Ls[Defn] + def orderedNames(entry: Node, defs: Set[Defn]): Ls[Str] + +object DefDfs: + import Node._ + + private object Successors: + def find(node: Node)(using acc: Ls[Defn]): Ls[Defn] = + node match + case Result(res) => acc + case Jump(defn, args) => defn.expectDefn :: acc + case Case(scrut, cases, default) => + val acc2 = cases.map(_._2) ++ default.toList + acc2.foldLeft(acc)((acc, x) => find(x)(using acc)) + case LetExpr(name, expr, body) => find(body) + case LetMethodCall(names, cls, method, args, body) => find(body) + case LetCall(names, defn, args, _, body) => find(body)(using defn.expectDefn :: acc) + + def find(defn: Defn)(using acc: Ls[Defn]): Ls[Defn] = find(defn.body) + def findNames(node: Node)(using acc: Ls[Str]): Ls[Str] = + node match + case Result(res) => acc + case Jump(defn, args) => defn.name :: acc + case Case(scrut, cases, default) => + val acc2 = cases.map(_._2) ++ default.toList + acc2.foldLeft(acc)((acc, x) => findNames(x)(using acc)) + case LetExpr(name, expr, body) => findNames(body) + case LetMethodCall(names, cls, method, args, body) => findNames(body) + case LetCall(names, defn, args, _, body) => findNames(body)(using defn.name :: acc) + + def findNames(defn: Defn)(using acc: Ls[Str]): Ls[Str] = findNames(defn.body) + + private def dfs(using visited: HashMap[Str, Bool], out: ListBuffer[Defn], postfix: Bool)(x: Defn): Unit = + visited.update(x.name, true) + if (!postfix) + out += x + Successors.find(x)(using Nil).foreach { y => + if (!visited(y.name)) + dfs(y) + } + if (postfix) + out += x + + private def dfs(using visited: HashMap[Str, Bool], out: ListBuffer[Defn], postfix: Bool)(x: Node): Unit = + Successors.find(x)(using Nil).foreach { y => + if (!visited(y.name)) + dfs(y) + } + + private def dfsNames(using visited: HashMap[Str, Bool], defs: Set[Defn], out: ListBuffer[Str], postfix: Bool)(x: Defn): Unit = + visited.update(x.name, true) + if (!postfix) + out += x.name + Successors.findNames(x)(using Nil).foreach { y => + if (!visited(y)) + dfsNames(defs.find(_.name == y).get) + } + if (postfix) + out += x.name + + private def dfsNames(using visited: HashMap[Str, Bool], defs: Set[Defn], out: ListBuffer[Str], postfix: Bool)(x: Node): Unit = + Successors.findNames(x)(using Nil).foreach { y => + if (!visited(y)) + dfsNames(defs.find(_.name == y).get) + } + + def dfs(entry: Node, defs: Set[Defn], postfix: Bool): Ls[Defn] = + val visited = HashMap[Str, Bool]() + visited ++= defs.map(_.name -> false) + val out = ListBuffer[Defn]() + dfs(using visited, out, postfix)(entry) + out.toList + + def dfsNames(entry: Node, defs: Set[Defn], postfix: Bool): Ls[Str] = + val visited = HashMap[Str, Bool]() + visited ++= defs.map(_.name -> false) + val out = ListBuffer[Str]() + dfsNames(using visited, defs, out, postfix)(entry) + out.toList + +object DefRevPostOrdering extends DefTraversalOrdering: + def ordered(entry: Node, defs: Set[Defn]): Ls[Defn] = + DefDfs.dfs(entry, defs, true).reverse + def orderedNames(entry: Node, defs: Set[Defn]): Ls[Str] = + DefDfs.dfsNames(entry, defs, true).reverse + +object DefRevPreOrdering extends DefTraversalOrdering: + def ordered(entry: Node, defs: Set[Defn]): Ls[Defn] = + DefDfs.dfs(entry, defs, false).reverse + def orderedNames(entry: Node, defs: Set[Defn]): Ls[Str] = + DefDfs.dfsNames(entry, defs, false).reverse + + + case class DefnTag(inner: Int): def is_valid = inner >= 0 override def equals(x: Any): Bool = x match @@ -250,7 +439,7 @@ case class DefnTag(inner: Int): (this, o) match case (DefnTag(a), DefnTag(b)) => this.is_valid && o.is_valid && a == b case _ => false - override def toString(): String = if is_valid then s"#$inner" else "#x" + override def toString: String = if is_valid then s"#$inner" else "#x" case class DefnLocMarker(val defn: Str, val marker: LocMarker): override def toString: String = s"$defn:$marker" @@ -259,49 +448,60 @@ case class DefnLocMarker(val defn: Str, val marker: LocMarker): enum LocMarker: case MRef(name: Str) case MLit(lit: Lit) - case MCtorApp(name: ClassInfo, args: Ls[LocMarker]) - case MSelect(name: Str, cls: ClassInfo, field: Str) + case MCtorApp(name: ClassRef, args: Ls[LocMarker]) + case MSelect(name: Str, cls: ClassRef, field: Str) case MBasicOp(name: Str, args: Ls[LocMarker]) + case MAssignField(assignee: Str, field: Str, value: LocMarker) case MResult(res: Ls[LocMarker]) case MJump(name: Str, args: Ls[LocMarker]) - case MCase(scrut: Str, cases: Ls[ClassInfo]) + case MCase(scrut: Str, cases: Ls[Pat], default: Bool) case MLetExpr(name: Str, expr: LocMarker) + case MLetMethodCall(names: Ls[Str], cls: ClassRef, method: Str, args: Ls[LocMarker]) case MLetCall(names: Ls[Str], defn: Str, args: Ls[LocMarker]) - case MAssignField(assignee: Str, field: Str, value: LocMarker) var tag = DefnTag(-1) - def toDocument: Document = this match - case MResult(res) => raw("...") + def toDocument: Document = + given Conversion[String, Document] = raw + this match + case MResult(res) => "..." case MJump(jp, args) => - raw("jump") - <:> raw(jp) - <:> raw("...") - case MCase(x, Ls(tcls, fcls)) if tcls.ident == "True" && fcls.ident == "False" => - raw("if") <:> raw(x.toString) <:> raw("...") - case MCase(x, cases) => - raw("case") <:> raw(x.toString) <:> raw("of") <:> raw("...") + "jump" + <:> jp + <:> "..." + case MCase(x, Ls(tpat, fpat), false) if tpat.isTrue && fpat.isFalse => + "if" <:> x.toString <:> "..." + case MCase(x, cases, default) => + "case" <:> x.toString <:> "of" <:> "..." case MLetExpr(x, expr) => - raw("let") - <:> raw(x.toString) - <:> raw("=") - <:> raw("...") + "let" + <:> x.toString + <:> "=" + <:> "..." + case MLetMethodCall(xs, cls, method, args) => + "let" + <:> xs.map(_.toString).mkString(",") + <:> "=" + <:> cls.name + <:> "." + <:> method + <:> "..." case MLetCall(xs, defn, args) => - raw("let*") - <:> raw("(") - <#> raw(xs.map(_.toString).mkString(",")) - <#> raw(")") - <:> raw("=") - <:> raw(defn) - <:> raw("...") - case MRef(s) => s.toString |> raw - case MLit(IntLit(lit)) => s"$lit" |> raw - case MLit(DecLit(lit)) => s"$lit" |> raw - case MLit(StrLit(lit)) => s"$lit" |> raw - case MLit(UnitLit(lit)) => (if lit then "undefined" else "null") |> raw - case _ => raw("...") + "let*" + <:> "(" + <#> xs.map(_.toString).mkString(",") + <#> ")" + <:> "=" + <:> defn + <:> "..." + case MRef(s) => s.toString + case MLit(IntLit(lit)) => s"$lit" + case MLit(DecLit(lit)) => s"$lit" + case MLit(StrLit(lit)) => s"$lit" + case MLit(UnitLit(undefinedOrNull)) => if undefinedOrNull then "undefined" else "null" + case _ => "..." def show = s"$tag-" + toDocument.print - override def toString(): String = show + override def toString: String = show def matches(x: Node): Bool = this.tag == x.tag diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala index 016ab099..8ff52e4f 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala @@ -1,324 +1,207 @@ package mlscript.compiler.ir import mlscript._ -import mlscript.compiler._ -import mlscript.compiler.ir.{Node => INode, Expr => IExpr, Program => IProgram, Defn => IDefn, DefnRef => IDefnRef} +import mlscript.compiler.ir._ +import mlscript.compiler.utils._ import mlscript.compiler.optimizer._ import mlscript.utils._ import scala.collection.immutable._ import scala.annotation._ import shorthands._ +import scala.collection.mutable.ListBuffer +import scala.util.boundary, boundary.break -final case class IRInterpreterError(message: String) extends Exception(message) +enum Stuck: + case StuckExpr(expr: Expr, msg: Str) + case StuckNode(node: Node, msg: Str) + + override def toString: String = + this match + case StuckExpr(expr, msg) => s"StuckExpr(${expr.show}, $msg)" + case StuckNode(node, msg) => s"StuckNode(${node.show}, $msg)" + +final case class InterpreterError(message: String) extends Exception(message) class Interpreter(verbose: Bool): private def log(x: Any) = if verbose then println(x) + import Stuck._ - // We have a similar definition of IR here to represent the result of the interpreter. - // This is because IR itself is in A-normal form. - // It represent terms, e.g. "Pair(True, False)", like: - // let x = CtorApp(True, []) in - // let y = CtorApp(False, []) in - // let z = CtorApp(Pair, [x, y]) in - // z - // But I don't want the result of an interpreter to be like this. - // So we release the limitation of the ANF IR here and allow expressions in argument position. - - private case class Program( - classes: Set[ClassInfo], - defs: Set[Defn], - main: Node, + private case class Configuration( + var context: Ctx ) - private case class Defn( - val name: Str, - val params: Ls[Name], - val body: Node, - ) + private type Result[T] = Either[Stuck, T] - private enum Expr: - case Ref(name: Name) + private enum Value: + case Class(cls: ClassInfo, var fields: Ls[Value]) case Literal(lit: Lit) - case CtorApp(name: ClassInfo, var args: Ls[Expr]) - case Select(name: Name, cls: ClassInfo, field: Str) - case BasicOp(name: Str, args: Ls[Expr]) - case AssignField(assignee: Name, clsInfo: ClassInfo, fieldName: Str, value: Expr) - - def show: Str = - document.print - - private def show_args(args: Ls[Expr]) = args map { x => x.show } mkString "," - - def document: Document = this match - case Ref(Name(s)) => s |> raw - case Literal(IntLit(lit)) => s"$lit" |> raw - case Literal(DecLit(lit)) => s"$lit" |> raw - case Literal(StrLit(lit)) => s"$lit" |> raw - case Literal(UnitLit(lit)) => s"$lit" |> raw - case CtorApp(ClassInfo(_, name, _), args) => - raw(name) <#> raw("(") <#> raw(args |> show_args) <#> raw(")") - case Select(Name(s), _, fld) => - raw(s) <#> raw(".") <#> raw(fld) - case BasicOp(name: Str, args) => - raw(name) <#> raw("(") <#> raw(args |> show_args) <#> raw(")") - case AssignField(Name(assignee), clsInfo, fieldName, value) => - stack( - raw("assign") - <:> raw(assignee) - <#> raw(".") - <#> raw(fieldName) - <:> raw("=") - <:> value.document, - ) - - private enum Node: - case Result(res: Ls[Expr]) - case Jump(defn: DefnRef, args: Ls[Expr]) - case Case(scrut: Name, cases: Ls[(ClassInfo, Node)]) - case LetExpr(name: Name, expr: Expr, body: Node) - case LetJoin(joinName: Name, params: Ls[Name], rhs: Node, body: Node) - case LetCall(resultNames: Ls[Name], defn: DefnRef, args: Ls[Expr], body: Node) - - def show: Str = - document.print - - private def showArgs(args: Ls[Expr]) = args map { x => x.show } mkString "," - def document: Document = this match - case Result(res) => raw(res |> showArgs) - case Jump(jp, args) => - raw("jump") - <:> raw(jp.name) - <#> raw("(") - <#> raw(args |> showArgs) - <#> raw(")") - case Case(Name(x), Ls((tcls, tru), (fcls, fls))) if tcls.ident == "True" && fcls.ident == "False" => - val first = raw("if") <:> raw(x) - val tru2 = indent(raw("true") <:> raw ("=>") <:> tru.document) - val fls2 = indent(raw("false") <:> raw ("=>") <:> fls.document) - Document.Stacked(Ls(first, tru2, fls2)) - case Case(Name(x), cases) => - val first = raw("case") <:> raw(x) <:> raw("of") - val other = cases map { - case (ClassInfo(_, name, _), node) => - indent(raw(name) <:> raw("=>") <:> node.document) - } - Document.Stacked(first :: other) - case LetExpr(Name(x), expr, body) => - stack( - raw("let") - <:> raw(x) - <:> raw("=") - <:> expr.document, - raw("in") <:> body.document |> indent) - case LetJoin(Name(x), params, rhs, body) => - stack( - raw("let") - <:> raw("join") - <:> raw(x) - <#> raw("(") - <#> raw(params.map{ x => x.toString }.mkString(",")) - <#> raw(")") - <:> raw("=") - <:> (rhs.document |> indent |> indent), - raw("in") <:> body.document |> indent - ) - case LetCall(resultNames, defn, args, body) => - stack( - raw("let*") - <:> raw("(") - <#> raw(resultNames.map{ x => x.toString }.mkString(",")) - <#> raw(")") - <:> raw("=") - <:> raw(defn.name) - <#> raw("(") - <#> raw(args.map{ x => x.toString }.mkString(",")) - <#> raw(")"), - raw("in") <:> body.document |> indent - ) + override def toString: String = + this match + case Class(cls, fields) => s"${cls.name}(${fields.mkString(",")})" + case Literal(IntLit(lit)) => lit.toString + case Literal(DecLit(lit)) => lit.toString + case Literal(StrLit(lit)) => lit.toString + case Literal(UnitLit(undefinedOrNull)) => if undefinedOrNull then "undefined" else "null" + + private final case class Ctx( + bindingCtx: Map[Str, Value], + classCtx: Map[Str, ClassInfo], + defnCtx: Map[Str, Defn], + ) - private class DefnRef(var defn: Either[Defn, Str]): - def name = defn match - case Left(defn) => defn.name - case Right(name) => name - import Node._ import Expr._ - private def convert(texpr: ir.TrivialExpr): Expr = texpr match - case IExpr.Ref(x) => Ref(x) - case IExpr.Literal(x) => Literal(x) - - private def convertArgs(xs: Ls[ir.TrivialExpr]): Ls[Expr] = xs.map(convert) - - private def convert(expr: IExpr): Expr = expr match - case IExpr.Ref(x) => Ref(x) - case IExpr.Literal(x) => Literal(x) - case IExpr.CtorApp(name, args) => CtorApp(name, args |> convertArgs) - case IExpr.Select(name, cls, field) => Select(name, cls, field) - case IExpr.BasicOp(name, args) => BasicOp(name, args |> convertArgs) - case IExpr.AssignField(assignee, clsInfo, fieldName, value) => AssignField(assignee, clsInfo, fieldName, value |> convert) - - private def convert(node: INode): Node = node match - case INode.Result(xs) => Result(xs |> convertArgs) - case INode.Jump(defnref, args) => Jump(DefnRef(Right(defnref.getName)), args |> convertArgs) - case INode.Case(scrut, cases) => Case(scrut, cases.map{(cls, node) => (cls, node |> convert)}) - case INode.LetExpr(name, expr, body) => LetExpr(name, expr |> convert, body |> convert) - case INode.LetCall(xs, defnref, args, _, body) => - LetCall(xs, DefnRef(Right(defnref.getName)), args |> convertArgs, body |> convert) - - private def convert(defn: IDefn): Defn = - Defn(defn.name, defn.params, defn.body |> convert) - - private def resolveDefnRef(defs: Set[Defn], node: Node): Unit = node match - case Case(_, cases) => cases.foreach { case (cls, node) => resolveDefnRef(defs, node) } - case LetExpr(name, expr, body) => resolveDefnRef(defs, body) - case LetJoin(joinName, params, rhs, body) => resolveDefnRef(defs, body) - case Jump(defnref, args) => defnref.defn = Left(defs.find(_.name == defnref.name).get) - case LetCall(xs, defnref, args, body) => - defnref.defn = Left(defs.find(_.name == defnref.name).get) - resolveDefnRef(defs, body) - case _ => - - private def convert(prog: IProgram): Program = - val classes = prog.classes - val old_defs = prog.defs - val old_main = prog.main - - val defs: Set[Defn] = old_defs.map(convert) - defs.foreach { - case Defn(_, _, body) => resolveDefnRef(defs, body) - } - - val main = convert(old_main) - resolveDefnRef(defs, main) - - Program(classes, defs, main) - - private type Ctx = Map[Str, Expr] - private type ClassCtx = Map[Str, ClassInfo] - - private def getTrue(using clsctx: ClassCtx) = CtorApp(clsctx("True"), Ls.empty) - private def getFalse(using clsctx: ClassCtx) = CtorApp(clsctx("False"), Ls.empty) - - private def eval(using ctx: Ctx, clsctx: ClassCtx)(op: Str, x1: Expr, x2: Expr): Opt[Expr] = (op, x1, x2) match - case ("+", Literal(IntLit(x)), Literal(IntLit(y))) => Some(Literal(IntLit(x + y))) - case ("-", Literal(IntLit(x)), Literal(IntLit(y))) => Some(Literal(IntLit(x - y))) - case ("*", Literal(IntLit(x)), Literal(IntLit(y))) => Some(Literal(IntLit(x * y))) - case ("/", Literal(IntLit(x)), Literal(IntLit(y))) => Some(Literal(IntLit(x / y))) - case ("==", Literal(IntLit(x)), Literal(IntLit(y))) => Some(if x == y then getTrue else getFalse) - case ("!=", Literal(IntLit(x)), Literal(IntLit(y))) => Some(if x != y then getTrue else getFalse) - case ("<=", Literal(IntLit(x)), Literal(IntLit(y))) => Some(if x <= y then getTrue else getFalse) - case (">=", Literal(IntLit(x)), Literal(IntLit(y))) => Some(if x >= y then getTrue else getFalse) - case (">", Literal(IntLit(x)), Literal(IntLit(y))) => Some(if x > y then getTrue else getFalse) - case ("<", Literal(IntLit(x)), Literal(IntLit(y))) => Some(if x < y then getTrue else getFalse) - case _ => None - - private def unifyEvalResult[A](x: Either[A, A]) = - x match - case Left(expr) => expr - case Right(expr) => expr - - private def evalArgs(using ctx: Ctx, clsctx: ClassCtx)(exprs: Ls[Expr]): Either[Ls[Expr], Ls[Expr]] = - var changed = false - - val xs = exprs.map { - arg => eval(arg) match - case Left(expr) => changed = true; expr - case Right(expr) => expr - } - if (changed) Left(xs) else Right(exprs) - - private def evalArgsMayNotProgress(using ctx: Ctx, clsctx: ClassCtx)(exprs: Ls[Expr]) = - exprs.map { - arg => eval(arg) |> unifyEvalResult - } - - private def evalMayNotProgress(using ctx: Ctx, clsctx: ClassCtx)(expr: Expr) = eval(expr) |> unifyEvalResult - - private def eval(using ctx: Ctx, clsctx: ClassCtx)(expr: Expr): Either[Expr, Expr] = expr match - case Ref(Name(x)) => ctx.get(x).toLeft(expr) - case Literal(x) => Right(expr) - case CtorApp(name, args) => - evalArgs(args) match - case Left(xs) => Left(CtorApp(name, xs)) - case Right(xs) => Right(CtorApp(name, xs)) // TODO: This makes recursion modulo cons work, but should be investigated further. - case Select(name, cls, field) => - ctx.get(name.str).map { - case CtorApp(cls2, xs) if cls == cls2 => + private def getTrue(using ctx: Ctx) = Value.Class(ctx.classCtx("True"), Ls.empty) + private def getFalse(using ctx: Ctx) = Value.Class(ctx.classCtx("False"), Ls.empty) + + private def eval(op: Str, x1: Value, x2: Value)(using ctx: Ctx): Opt[Value] = + import Value.{Literal => Li} + (op, x1, x2) match + case ("+", Li(IntLit(x)), Li(IntLit(y))) => S(Li(IntLit(x + y))) + case ("-", Li(IntLit(x)), Li(IntLit(y))) => S(Li(IntLit(x - y))) + case ("*", Li(IntLit(x)), Li(IntLit(y))) => S(Li(IntLit(x * y))) + case ("/", Li(IntLit(x)), Li(IntLit(y))) => S(Li(IntLit(x / y))) + case ("==", Li(IntLit(x)), Li(IntLit(y))) => S(if x == y then getTrue else getFalse) + case ("!=", Li(IntLit(x)), Li(IntLit(y))) => S(if x != y then getTrue else getFalse) + case ("<=", Li(IntLit(x)), Li(IntLit(y))) => S(if x <= y then getTrue else getFalse) + case (">=", Li(IntLit(x)), Li(IntLit(y))) => S(if x >= y then getTrue else getFalse) + case (">", Li(IntLit(x)), Li(IntLit(y))) => S(if x > y then getTrue else getFalse) + case ("<", Li(IntLit(x)), Li(IntLit(y))) => S(if x < y then getTrue else getFalse) + case _ => N + + private def evalArgs(using ctx: Ctx)(exprs: Ls[TrivialExpr]): Result[Ls[Value]] = + var values = ListBuffer.empty[Value] + var stuck: Opt[Stuck] = None + exprs foreach { expr => + stuck match + case None => eval(expr) match + case L(x) => stuck = Some(x) + case R(x) => values += x + case _ => () + } + stuck.toLeft(values.toList) + + private def eval(expr: TrivialExpr)(using ctx: Ctx): Result[Value] = expr match + case e @ Ref(name) => ctx.bindingCtx.get(name.str).toRight(StuckExpr(e, s"undefined variable $name")) + case Literal(lit) => R(Value.Literal(lit)) + + private def eval(expr: Expr)(using ctx: Ctx): Result[Value] = expr match + case Ref(Name(x)) => ctx.bindingCtx.get(x).toRight(StuckExpr(expr, s"undefined variable $x")) + case Literal(x) => R(Value.Literal(x)) + case CtorApp(cls, args) => for { + xs <- evalArgs(args) + cls <- ctx.classCtx.get(cls.name).toRight(StuckExpr(expr, s"undefined class ${cls.name}")) + } yield Value.Class(cls, xs) + case Select(name, cls, field) => + ctx.bindingCtx.get(name.str).toRight(StuckExpr(expr, s"undefined variable $name")).flatMap { + case Value.Class(cls2, xs) if cls.name == cls2.name => xs.zip(cls2.fields).find{_._2 == field} match - case Some((x, _)) => x - case None => throw IRInterpreterError("unable to find selected field") - case x @ _ => throw IRInterpreterError(s"unexpected node: select $field but got $x when eval $expr") - }.toLeft(expr) - case BasicOp(name, args) => - val xs = evalArgsMayNotProgress(args) - val x = name match - case "+" | "-" | "*" | "/" | "==" | "!=" | "<=" | ">=" | "<" | ">" => - eval(using ctx, clsctx)(name, xs.head, xs.tail.head) - case _ => throw IRInterpreterError("unexpected basic operation") - x.toLeft(expr) - case AssignField(assignee, clsInfo, fieldName, expr) => - val value = evalMayNotProgress(expr) - ctx.get(assignee.str) match - case Some(x: CtorApp) => - val CtorApp(cls, args) = x - val idx = cls.fields.indexOf(fieldName) - val newArgs = args.updated(idx, value) - x.args = newArgs - Left(x) - case Some(_) => throw IRInterpreterError("tried to assign a field of a non-ctor") - case None => throw IRInterpreterError("could not find value " + assignee) - - private def expectDefn(r: DefnRef) = r.defn match - case Left(value) => value - case Right(value) => throw IRInterpreterError("only has the name of definition") - - private def evalMayNotProgress(using ctx: Ctx, clsctx: ClassCtx)(node: Node) = eval(node) |> unifyEvalResult - - @tailrec - private def eval(using ctx: Ctx, clsctx: ClassCtx)(node: Node): Either[Node, Node] = node match - case Result(xs) => - xs |> evalArgs match - case Left(xs) => Left(Result(xs)) - case _ => Right(node) - case Jump(defnref, args) => - val xs = args |> evalArgsMayNotProgress - val defn = defnref |> expectDefn - val ctx1 = ctx ++ defn.params.map{_.str}.zip(xs) - eval(using ctx1, clsctx)(defn.body) - case Case(scrut, cases) => - val CtorApp(cls, xs) = (Ref(scrut):Expr) |> evalMayNotProgress(using ctx, clsctx) match { - case x: CtorApp => x - case x => throw IRInterpreterError(s"not a class $x") + case Some((x, _)) => R(x) + case None => L(StuckExpr(expr, s"unable to find selected field $field")) + case Value.Class(cls2, xs) => L(StuckExpr(expr, s"unexpected class $cls2")) + case x => L(StuckExpr(expr, s"unexpected value $x")) } - val arm = cases.find{_._1 == cls} match { - case Some((_, x)) => x - case _ => throw IRInterpreterError(s"can not find the matched case, $cls expected") + case BasicOp(name, args) => boundary: + evalArgs(args).flatMap( + xs => + name match + case "+" | "-" | "*" | "/" | "==" | "!=" | "<=" | ">=" | "<" | ">" => + if xs.length < 2 then break: + L(StuckExpr(expr, s"not enough arguments for basic operation $name")) + else eval(name, xs.head, xs.tail.head).toRight(StuckExpr(expr, s"unable to evaluate basic operation")) + case _ => L(StuckExpr(expr, s"unexpected basic operation $name"))) + case AssignField(assignee, cls, field, value) => + for { + x <- eval(Ref(assignee): TrivialExpr) + y <- eval(value) + res <- x match + case obj @ Value.Class(cls2, xs) if cls.name == cls2.name => + xs.zip(cls2.fields).find{_._2 == field} match + case Some((_, _)) => + obj.fields = xs.map(x => if x == obj then y else x) + // Ideally, we should return a unit value here, but here we return the assignee value for simplicity. + R(obj) + case None => L(StuckExpr(expr, s"unable to find selected field $field")) + case Value.Class(cls2, xs) => L(StuckExpr(expr, s"unexpected class $cls2")) + case x => L(StuckExpr(expr, s"unexpected value $x")) + } yield res + + private def eval(node: Node)(using ctx: Ctx): Result[Ls[Value]] = node match + case Result(res) => evalArgs(res) + case Jump(defn, args) => for { + xs <- evalArgs(args) + defn <- ctx.defnCtx.get(defn.name).toRight(StuckNode(node, s"undefined function ${defn.name}")) + ctx1 = ctx.copy(bindingCtx = ctx.bindingCtx ++ defn.params.map{_.str}.zip(xs)) + res <- eval(defn.body)(using ctx1) + } yield res + case Case(scrut, cases, default) => + eval(Ref(scrut): Expr) flatMap { + case Value.Class(cls, fields) => + cases.find { + case (Pat.Class(cls2), _) => cls.name == cls2.name + case _ => false + } match { + case Some((_, x)) => eval(x) + case None => + default match + case S(x) => eval(x) + case N => L(StuckNode(node, s"can not find the matched case, ${cls.name} expected")) + } + case Value.Literal(lit) => + cases.find { + case (Pat.Lit(lit2), _) => lit == lit2 + case _ => false + } match { + case Some((_, x)) => eval(x) + case None => + default match + case S(x) => eval(x) + case N => L(StuckNode(node, s"can not find the matched case, $lit expected")) + } } - eval(arm) case LetExpr(name, expr, body) => - val x = evalMayNotProgress(expr) - val ctx1 = ctx + (name.str -> x) - eval(using ctx1, clsctx)(body) - case LetCall(xs, defnref, args, body) => - val defn = defnref |> expectDefn - val ys = args |> evalArgsMayNotProgress - val ctx1 = ctx ++ defn.params.map{_.str}.zip(ys) - val res = evalMayNotProgress(using ctx1, clsctx)(defn.body) match { - case Result(xs) => xs - case _ => throw IRInterpreterError("unexpected node") - } - val ctx2 = ctx ++ xs.map{_.str}.zip(res) - eval(using ctx2, clsctx)(body) - case _ => throw IRInterpreterError("unexpected node") - - private def interpret(prog: Program): Node = + for { + x <- eval(expr) + ctx1 = ctx.copy(bindingCtx = ctx.bindingCtx + (name.str -> x)) + res <- eval(body)(using ctx1) + } yield res + case LetMethodCall(names, cls, method, args, body) => + for { + ys <- evalArgs(args).flatMap { + case Value.Class(cls2, xs) :: args => + cls2.methods.get(method.str).toRight(StuckNode(node, s"undefined method ${method.str}")).flatMap { method => + val ctx1 = ctx.copy(bindingCtx = ctx.bindingCtx ++ cls2.fields.zip(xs) ++ method.params.map{_.str}.zip(args)) + eval(method.body)(using ctx1) + } + case _ => L(StuckNode(node, s"not enough arguments for method call, or the first argument is not a class")) + } + ctx2 = ctx.copy(bindingCtx = ctx.bindingCtx ++ names.map{_.str}.zip(ys)) + res <- eval(body)(using ctx2) + } yield res + // case LetApply(names, fn, args, body) => eval(LetMethodCall(names, ClassRef(R("Callable")), Name("apply" + args.length), (Ref(fn): TrivialExpr) :: args, body)) + case LetCall(names, defn, args, _, body) => + for { + xs <- evalArgs(args) + defn <- ctx.defnCtx.get(defn.name).toRight(StuckNode(node, s"undefined function ${defn.name}")) + ctx1 = ctx.copy(bindingCtx = ctx.bindingCtx ++ defn.params.map{_.str}.zip(xs)) + ys <- eval(defn.body)(using ctx1) + ctx2 = ctx.copy(bindingCtx = ctx.bindingCtx ++ names.map{_.str}.zip(ys)) + res <- eval(body)(using ctx2) + } yield res + + private def f(prog: Program): Ls[Value] = val Program(classes, defs, main) = prog - val clsctx = classes.map(x => x.ident -> x).toMap - evalMayNotProgress(using Map.empty, clsctx)(main) + given Ctx = Ctx( + bindingCtx = Map.empty, + classCtx = classes.map{cls => cls.name -> cls}.toMap, + defnCtx = defs.map{defn => defn.name -> defn}.toMap, + ) + eval(main) match + case R(x) => x + case L(x) => throw InterpreterError("Stuck evaluation: " + x.toString) - def interpret(irprog: ir.Program): Str = - val prog = convert(irprog) - val node = interpret(prog) - node.show + def interpret(prog: Program): Str = + val node = f(prog) + node.map(_.toString).mkString(",") diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/RefResolver.scala b/compiler/shared/main/scala/mlscript/compiler/ir/RefResolver.scala new file mode 100644 index 00000000..a1574137 --- /dev/null +++ b/compiler/shared/main/scala/mlscript/compiler/ir/RefResolver.scala @@ -0,0 +1,55 @@ +package mlscript.compiler.ir + +import mlscript.utils.shorthands._ +import mlscript.compiler.ir._ + +import Node._ + +// Resolves the definition references by turning them from Right(name) to Left(Defn). +private final class RefResolver(defs: Map[Str, Defn], classes: Map[Str, ClassInfo], allowInlineJp: Bool): + private def f(x: Expr): Unit = x match + case Expr.Ref(name) => + case Expr.Literal(lit) => + case Expr.CtorApp(cls, args) => classes.get(cls.name) match + case None => throw IRError(f"unknown class ${cls.name} in ${classes.keySet.mkString(",")}") + case Some(value) => cls.cls = Left(value) + case Expr.Select(name, cls, field) => classes.get(cls.name) match + case None => throw IRError(f"unknown class ${cls.name} in ${classes.keySet.mkString(",")}") + case Some(value) => cls.cls = Left(value) + case Expr.BasicOp(name, args) => + case Expr.AssignField(assigneee, cls, field, value) => classes.get(cls.name) match + case None => throw IRError(f"unknown class ${cls.name} in ${classes.keySet.mkString(",")}") + case Some(value) => cls.cls = Left(value) + + private def f(x: Pat): Unit = x match + case Pat.Lit(lit) => + case Pat.Class(cls) => classes.get(cls.name) match + case None => throw IRError(f"unknown class ${cls.name} in ${classes.keySet.mkString(",")}") + case Some(value) => cls.cls = Left(value) + + private def f(x: Node): Unit = x match + case Result(res) => + case Case(scrut, cases, default) => cases foreach { (_, body) => f(body) }; default foreach f + case LetExpr(name, expr, body) => f(expr); f(body) + case LetMethodCall(names, cls, method, args, body) => f(body) + case LetCall(resultNames, defnref, args, _, body) => + defs.get(defnref.name) match + case Some(defn) => defnref.defn = Left(defn) + case None => throw IRError(f"unknown function ${defnref.name} in ${defs.keySet.mkString(",")}") + f(body) + case Jump(defnref, _) => + // maybe not promoted yet + defs.get(defnref.name) match + case Some(defn) => defnref.defn = Left(defn) + case None => + if (!allowInlineJp) + throw IRError(f"unknown function ${defnref.name} in ${defs.keySet.mkString(",")}") + def run(node: Node) = f(node) + def run(node: Defn) = f(node.body) + +def resolveRef(entry: Node, defs: Set[Defn], classes: Set[ClassInfo], allowInlineJp: Bool = false): Unit = + val defsMap = defs.map(x => x.name -> x).toMap + val classesMap = classes.map(x => x.name -> x).toMap + val rl = RefResolver(defsMap, classesMap, allowInlineJp) + rl.run(entry) + defs.foreach(rl.run(_)) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala index ce7f09c2..2a9695c3 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala @@ -3,14 +3,31 @@ package mlscript.compiler.ir import mlscript.utils.shorthands._ import mlscript.compiler.ir._ -import Node._ -private final class DefnRefInSet(defs: Set[Defn]): +private final class DefnRefInSet(defs: Set[Defn], classes: Set[ClassInfo]): + import Node._ + import Expr._ + + private def f(x: Expr): Unit = x match + case Ref(name) => + case Literal(lit) => + case CtorApp(name, args) => + case Select(name, clsref, field) => clsref.getClass match { + case Some(real_class) => if (!classes.exists(_ eq real_class)) throw IRError("ref is not in the set") + case _ => + } + case BasicOp(name, args) => + case AssignField(assignee, clsref, _, value) => clsref.getClass match { + case Some(real_class) => if (!classes.exists(_ eq real_class)) throw IRError("ref is not in the set") + case _ => + } + private def f(x: Node): Unit = x match case Result(res) => case Jump(defn, args) => - case Case(scrut, cases) => cases map { (_, body) => f(body) } + case Case(scrut, cases, default) => cases foreach { (_, body) => f(body) }; default foreach f case LetExpr(name, expr, body) => f(body) + case LetMethodCall(names, cls, method, args, body) => f(body) case LetCall(res, defnref, args, _, body) => defnref.getDefn match { case Some(real_defn) => if (!defs.exists(_ eq real_defn)) throw IRError("ref is not in the set") @@ -20,10 +37,10 @@ private final class DefnRefInSet(defs: Set[Defn]): def run(node: Node) = f(node) def run(defn: Defn) = f(defn.body) -def validateDefnRefInSet(entry: Node, defs: Set[Defn]): Unit = - val dris = DefnRefInSet(defs) +def validateRefInSet(entry: Node, defs: Set[Defn], classes: Set[ClassInfo]): Unit = + val dris = DefnRefInSet(defs, classes) defs.foreach(dris.run(_)) -def validate(entry: Node, defs: Set[Defn]): Unit = - validateDefnRefInSet(entry, defs) +def validate(entry: Node, defs: Set[Defn], classes: Set[ClassInfo]): Unit = + validateRefInSet(entry, defs, classes) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala index f0b02d27..b320a87f 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala @@ -46,7 +46,8 @@ class UsefulnessAnalysis(verbose: Bool = false): private def f(x: Node): Unit = x match case Result(res) => res.foreach(f) case Jump(defn, args) => args.foreach(f) - case Case(scrut, cases) => addUse(scrut); cases.foreach { case (cls, body) => f(body) } + case Case(scrut, cases, default) => addUse(scrut); cases.foreach { case (cls, body) => f(body) }; default.foreach(f) + 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) @@ -77,17 +78,22 @@ class FreeVarAnalysis(extended_scope: Bool = true, verbose: Bool = false): case Result(res) => res.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) case Jump(defnref, args) => var fv2 = args.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) - if (extended_scope && !visited.contains(defnref.getName)) + if (extended_scope && !visited.contains(defnref.name)) val defn = defnref.expectDefn visited.add(defn.name) val defined2 = defn.params.foldLeft(defined)((acc, param) => acc + param.str) fv2 = f(using defined2)(defn, fv2) fv2 - case Case(scrut, cases) => + case Case(scrut, cases, default) => val fv2 = if (defined.contains(scrut.str)) fv else fv + scrut.str - cases.foldLeft(fv2) { + val fv3 = cases.foldLeft(fv2) { case (acc, (cls, body)) => f(using defined)(body, acc) } + fv3 + case LetMethodCall(resultNames, cls, method, args, body) => + var fv2 = args.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) + val defined2 = resultNames.foldLeft(defined)((acc, name) => acc + name.str) + f(using defined2)(body, fv2) case LetExpr(name, expr, body) => val fv2 = f(using defined)(expr, fv) val defined2 = defined + name.str @@ -95,7 +101,7 @@ class FreeVarAnalysis(extended_scope: Bool = true, verbose: Bool = false): case LetCall(resultNames, defnref, args, _, body) => var fv2 = args.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) val defined2 = resultNames.foldLeft(defined)((acc, name) => acc + name.str) - if (extended_scope && !visited.contains(defnref.getName)) + if (extended_scope && !visited.contains(defnref.name)) val defn = defnref.expectDefn visited.add(defn.name) val defined2 = defn.params.foldLeft(defined)((acc, param) => acc + param.str) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 1405e457..3019421f 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -80,7 +80,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag case TailCallInfo(src, defn) => f"TailCall { ${src.name}$$${src.id} -> ${defn.name}$$${defn.id} }" case ModConsCallInfo(src, startNode, defn, letCallNode, letCtorNode, _, _) => - f"ModConsCall { ${src.name}$$${src.id} -> ${defn.name}$$${defn.id}, class: ${letCtorNode.cls.ident}, field: ${letCtorNode.fieldName} }" + f"ModConsCall { ${src.name}$$${src.id} -> ${defn.name}$$${defn.id}, class: ${letCtorNode.cls.name}, field: ${letCtorNode.fieldName} }" def getSrc = this match case NormalCallInfo(src, _) => src @@ -107,8 +107,8 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag // Hack to make scala think discoverJoinPoints is tail recursive and be // partially optimized :P - def casesToJps(cases: List[(ClassInfo, Node)], acc: Set[Defn]): Set[Defn] = - cases.foldLeft(acc)((jps, branch) => discoverJoinPoints(branch._2, jps)) + def casesToJps(cases: List[(Pat, Node)], default: Opt[Node], acc: Set[Defn]): Set[Defn] = + cases.foldLeft(default.fold(acc)(x => discoverJoinPoints(x, acc)))((jps, branch) => discoverJoinPoints(branch._2, jps)) def discoverJoinPointsCont(defn: Defn, acc: Set[Defn]) = discoverJoinPoints(defn.body, acc) + defn @@ -122,7 +122,8 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag val ret = node match case Jump(defn, args) => isIdentityJp(defn.expectDefn) case _: LetCall => false - case Case(scrut, cases) => cases.foldLeft(true)((value, branch) => value && isPure(branch._2)) + case LetMethodCall(names, cls, method, args, body) => false + case Case(scrut, cases, default) => cases.foldLeft(default.fold(false)(isPure))((value, branch) => value && isPure(branch._2)) case LetExpr(name, expr: Expr.AssignField, body) => false case x: LetExpr => true case Result(res) => true @@ -144,8 +145,9 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag if isIdentityJp(defn) then acc else if acc.contains(defn) then acc else discoverJoinPointsCont(defn, acc + defn) - case Case(scrut, cases) => casesToJps(cases, acc) + case Case(scrut, cases, default) => casesToJps(cases, default, acc) case LetExpr(name, expr, body) => discoverJoinPoints(body, acc) + case LetMethodCall(names, cls, method, args, body) => discoverJoinPoints(body, acc) case LetCall(names, defn, args, isTailRec, body) => discoverJoinPoints(body, acc) private def getRetName(names: Set[Name], retVals: List[TrivialExpr]): Option[Name] = @@ -234,7 +236,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag case _ => returnNone case _ => returnNone - case Case(scrut, cases) => Right(cases.map(_._2)) + case Case(scrut, cases, default) => Right(cases.map(_._2) ++ default.toList) case x @ LetExpr(name, expr, body) => expr match // Check if this let binding references the mod cons call. @@ -288,10 +290,10 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag case _ => false } - val fieldName = clsInfo.fields(ctorArgIndex) + val fieldName = clsInfo.expectClass.fields(ctorArgIndex) // populate required values - searchOptCalls(body)(acc, src, scc, start, calledDefn, letCallNode, Some(LetCtorNodeInfo(x, y, clsInfo, name, fieldName, ctorArgIndex)), Set(name)) + searchOptCalls(body)(acc, src, scc, start, calledDefn, letCallNode, Some(LetCtorNodeInfo(x, y, clsInfo.expectClass, name, fieldName, ctorArgIndex)), Set(name)) case Some(_) => // another constructor is already using the call. Not OK @@ -340,6 +342,10 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag // If this assignment overwrites the mod cons value, forget it if containingCtors.contains(assignee) then invalidateAndCont(body) else searchOptCalls(body) + case LetMethodCall(names, cls, method, args, body) => + // method call is unresolved, just ignore it + // `containingCtors -- names.toSet` takes care of variable shadowing + searchOptCalls(body)(acc, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors -- names.toSet) case x @ LetCall(names, defn, args, isTailRec, body) => val callInScc = scc.contains(defn.expectDefn) @@ -468,8 +474,9 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag node match case Result(res) => acc case Jump(defn, args) => acc - case Case(scrut, cases) => cases.foldLeft(acc)((acc, item) => searchCalls(item._2)(src, acc)) + case Case(scrut, cases, default) => cases.foldLeft(default.fold(acc)(x => searchCalls(x)(src, acc)))((acc, item) => searchCalls(item._2)(src, acc)) case LetExpr(name, expr, body) => searchCalls(body) + case LetMethodCall(names, cls, method, args, body) => searchCalls(body) case LetCall(names, defn, args, isTailRec, body) => val newSet = acc.get(src.id) match case None => Set(defn.expectDefn) @@ -580,7 +587,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag // False -> e2 def makeCaseBranch(value: Int, e1: Node, e2: Node): Node = val name = Name("scrut") - val cases = Case(name, List((trueClass, e1), (falseClass, e2))).attachTag(tag) + val cases = Case(name, List((Pat.Class(ClassRef(L(trueClass))), e1), (Pat.Class(ClassRef(L(falseClass))), e2)), None).attachTag(tag) LetExpr( name, Expr.BasicOp("==", List(asLit(value), Expr.Ref(scrutName))), @@ -616,6 +623,8 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag // The idea to use `ptr` and `field` to represent a pointer is by @LPTK. final val ID_CTX_CLASS = ClassInfo(classUid.make, ID_CONTEXT_NAME, Nil) final val CTX_CLASS = ClassInfo(classUid.make, CONTEXT_NAME, List("acc", "ptr", "field")) + final val ID_CTX_CLASS_REF = ClassRef(L(ID_CTX_CLASS)) + final val CTX_CLASS_REF = ClassRef(L(CTX_CLASS)) // Given a strongly connected component `defns` of mutually // tail recursive functions, returns a strongly connected component contaning the @@ -632,8 +641,8 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag if modConsCalls.isEmpty then (component, Set()) else - val trueClass = classes.find(c => c.ident == "True").get - val falseClass = classes.find(c => c.ident == "False").get + val trueClass = classes.find(c => c.name == "True").get + val falseClass = classes.find(c => c.name == "False").get // CONTEXT APPLICATION @@ -666,18 +675,18 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag // acc val node = LetExpr( Name("ptr"), - Expr.Select(appCtxName, CTX_CLASS, "ptr"), + Expr.Select(appCtxName, CTX_CLASS_REF, "ptr"), LetExpr( Name("_"), Expr.AssignField( Name("ptr"), - cls, + ClassRef(L(cls)), fieldName, Expr.Ref(appValName) ), LetExpr( Name("acc"), - Expr.Select(appCtxName, CTX_CLASS, "acc"), // this could be a join point but it's not that bad + Expr.Select(appCtxName, CTX_CLASS_REF, "acc"), // this could be a join point but it's not that bad Result( List(Expr.Ref(Name("acc"))) ).attachTag(tag) @@ -690,7 +699,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag val ctxBranch = LetExpr( - Name("field"), Expr.Select(appCtxName, CTX_CLASS, "field"), + Name("field"), Expr.Select(appCtxName, CTX_CLASS_REF, "field"), makeSwitch(Name("field"), assignmentCases.tail, assignmentCases.head._2)(trueClass, falseClass) ).attachTag(tag) @@ -698,9 +707,10 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag val appNode = Case(appCtxName, List( - (ID_CTX_CLASS, idBranch), - (CTX_CLASS, ctxBranch) - ) + (Pat.Class(ID_CTX_CLASS_REF), idBranch), + (Pat.Class(CTX_CLASS_REF), ctxBranch) + ), + None ).attachTag(tag) val appDefn = Defn(ctxAppId, ctxAppName, List(appCtxName, appValName), 1, appNode, false) @@ -721,20 +731,20 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag // ret val cmpNode = LetExpr( Name("ctx2acc"), - Expr.Select(cmpCtx2Name, CTX_CLASS, "acc"), + Expr.Select(cmpCtx2Name, CTX_CLASS_REF, "acc"), LetExpr( Name("ctx2ptr"), - Expr.Select(cmpCtx2Name, CTX_CLASS, "ptr"), + Expr.Select(cmpCtx2Name, CTX_CLASS_REF, "ptr"), LetExpr( Name("ctx2field"), - Expr.Select(cmpCtx2Name, CTX_CLASS, "field"), + Expr.Select(cmpCtx2Name, CTX_CLASS_REF, "field"), LetCall( List(Name("newAcc")), DefnRef(Left(appDefn)), List(Expr.Ref(cmpCtx1Name), Expr.Ref(Name("ctx2acc"))), false, LetExpr( Name("ret"), - Expr.CtorApp(CTX_CLASS, List("newAcc", "ctx2ptr", "ctx2field").map(n => Expr.Ref(Name(n)))), + Expr.CtorApp(CTX_CLASS_REF, List("newAcc", "ctx2ptr", "ctx2field").map(n => Expr.Ref(Name(n)))), Result( List(Expr.Ref(Name("ret"))) ).attachTag(tag) @@ -777,8 +787,9 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag case None => throw IRError("could not find jump point with id" + defn.expectDefn.id) case Some(value) => Jump(value, Expr.Ref(Name("ctx")) :: args) - case Case(scrut, cases) => Case(scrut, cases.map { (cls, body) => (cls, transformNode(body)) }).attachTag(tag) + case Case(scrut, cases, default) => Case(scrut, cases.map { (cls, body) => (cls, transformNode(body)) }, default.map(transformNode)).attachTag(tag) case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)).attachTag(tag) + case LetMethodCall(names, cls, method, args, body) => LetMethodCall(names, cls, method, args, transformNode(body)).attachTag(tag) case LetCall(names, defn, args, isTailRec, body) => // Handle the case when we see a tail call. // This case is not handled by the paper. The way to transform this is: @@ -803,7 +814,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag // f(composed, *args) LetExpr( Name("ctx2"), - Expr.CtorApp(CTX_CLASS, List(Expr.Ref(call.retName), Expr.Ref(call.letCtorNode.ctorValName), asLit(field))), + Expr.CtorApp(CTX_CLASS_REF, List(Expr.Ref(call.retName), Expr.Ref(call.letCtorNode.ctorValName), asLit(field))), LetCall( List(Name("composed")), DefnRef(Left(cmpDefn)), @@ -831,7 +842,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag // rewrite the ctor, but set the field containing the call as to 0 val idx = call.letCtorNode.idx val argsList = call.letCtorNode.ctor.args.updated(idx, asLit(0)) - LetExpr(name, Expr.CtorApp(call.letCtorNode.cls, argsList), transformModConsBranch(body)).attachTag(tag) + LetExpr(name, Expr.CtorApp(ClassRef(L(call.letCtorNode.cls)), argsList), transformModConsBranch(body)).attachTag(tag) else LetExpr(name, expr, transformModConsBranch(body)).attachTag(tag) case LetCall(names, defn, args, isTailRec, body) => @@ -855,7 +866,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag val modConsCall = LetExpr( Name("idCtx"), - Expr.CtorApp(ID_CTX_CLASS, Nil), + Expr.CtorApp(ID_CTX_CLASS_REF, Nil), LetCall( List(Name("res")), DefnRef(Left(modConsDefn)), @@ -896,8 +907,8 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag // Explicitly returns the merged function in case tailrec needs to be checked. private def optimizeTailRec(component: ScComponent, classes: Set[ClassInfo]): (Set[Defn], Defn) = // To build the case block, we need to compare integers and check if the result is "True" - val trueClass = classes.find(c => c.ident == "True").get - val falseClass = classes.find(c => c.ident == "False").get + val trueClass = classes.find(c => c.name == "True").get + val falseClass = classes.find(c => c.name == "False").get // undefined for dummy values val dummyVal = Expr.Literal(UnitLit(true)) @@ -924,8 +935,9 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag def transformNode(node: Node): Node = node match case Result(res) => node.attachTag(tag) case Jump(defn, args) => node.attachTag(tag) - case Case(scrut, cases) => Case(scrut, cases.map((cls, body) => (cls, transformNode(body)))).attachTag(tag) + case Case(scrut, cases, default) => Case(scrut, cases.map((cls, body) => (cls, transformNode(body))), default.map(transformNode)).attachTag(tag) case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)).attachTag(tag) + case LetMethodCall(names, cls, method, args, body) => LetMethodCall(names, cls, method, args, transformNode(body)).attachTag(tag) case LetCall(names, defn_, args, isTailRec, body) => if isTailCall(node) && defn_.expectDefn.id == defn.id then Jump(jpDefnRef, args).attachTag(tag) @@ -990,9 +1002,11 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag Jump(jpDefnRef, transformStackFrame(args, defnInfoMap(defn.expectDefn.id))).attachTag(tag) else node.attachTag(tag) - case Result(_) => node.attachTag(tag) - case Case(scrut, cases) => Case(scrut, cases.map(n => (n._1, transformNode(n._2)))).attachTag(tag) + case Result(_) => node.attachTag(tag) + case Case(scrut, cases, default) => Case(scrut, cases.map(n => (n._1, transformNode(n._2))), default.map(transformNode)).attachTag(tag) case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)).attachTag(tag) + case LetMethodCall(names, cls, method, args, body) => + LetMethodCall(names, cls, method, args, transformNode(body)).attachTag(tag) case LetCall(names, defn, args, isTailRec, body) => if isTailCall(node) && defnInfoMap.contains(defn.expectDefn.id) then Jump(jpDefnRef, transformStackFrame(args, defnInfoMap(defn.expectDefn.id))).attachTag(tag) @@ -1075,11 +1089,12 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag val partitions = partition(p.defs) val newDefs = partitions.flatMap { optimizeParition(_, p.classes) }.toSet + val newClasses = p.classes + ID_CTX_CLASS + CTX_CLASS // update the definition refs - newDefs.foreach { defn => resolveDefnRef(defn.body, newDefs, true) } - resolveDefnRef(p.main, newDefs, true) + newDefs.foreach { defn => resolveRef(defn.body, newDefs, newClasses, true) } + resolveRef(p.main, newDefs, newClasses, true) - (Program(p.classes + ID_CTX_CLASS + CTX_CLASS, newDefs, p.main), partitions.map(t => t.nodes.map(f => f.name))) + (Program(newClasses, newDefs, p.main), partitions.map(t => t.nodes.map(f => f.name))) def run(p: Program): Program = run_debug(p)._1 \ No newline at end of file diff --git a/compiler/shared/main/scala/mlscript/compiler/simpledef/Simpledef.scala b/compiler/shared/main/scala/mlscript/compiler/simpledef/Simpledef.scala index 4b59f694..3b05c096 100644 --- a/compiler/shared/main/scala/mlscript/compiler/simpledef/Simpledef.scala +++ b/compiler/shared/main/scala/mlscript/compiler/simpledef/Simpledef.scala @@ -5,7 +5,7 @@ package simpledef import mlscript.utils.*, shorthands.* import scala.collection.mutable import java.util.IdentityHashMap -import scala.collection.JavaConverters._ +import scala.jdk.CollectionConverters.* type TypeVar type TermId = Uid[Term] diff --git a/compiler/shared/test/diff-ir/Class.mls b/compiler/shared/test/diff-ir/Class.mls new file mode 100644 index 00000000..9db12fc4 --- /dev/null +++ b/compiler/shared/test/diff-ir/Class.mls @@ -0,0 +1,182 @@ +:NewDefs +:ParseOnly +:UseIR +:NoTailRec + +:prelude +module True +module False +module Callable { + fun apply0() = 0 + fun apply1(x0) = 0 + fun apply2(x0,x1) = 0 + fun apply3(x0,x1,x2) = 0 + fun apply4(x0,x1,x2,x3) = 0 + fun apply5(x0,x1,x2,x3,x4) = 0 +} +module List[A, B] +class Cons[A, B](h: A, t: Cons[A, B]) extends List[A, B] +module Nil[A, B] extends List[A, B] +module Option[A] +class Some[A](x: A) extends Option[A] +module None[A] extends Option[A] +class Pair[A, B](x: A, y: B) +class Tuple2[A, B](x: A, y: B) +class Tuple3[A, B, C](x: A, y: B, z: C) +module Nat +class S(s: Nat) extends Nat +module O extends Nat +class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O) +//│ |#module| |True|↵|#module| |False|↵|#module| |Callable| |{|→|#fun| |apply0|(||)| |#=| |0|↵|#fun| |apply1|(|x0|)| |#=| |0|↵|#fun| |apply2|(|x0|,|x1|)| |#=| |0|↵|#fun| |apply3|(|x0|,|x1|,|x2|)| |#=| |0|↵|#fun| |apply4|(|x0|,|x1|,|x2|,|x3|)| |#=| |0|↵|#fun| |apply5|(|x0|,|x1|,|x2|,|x3|,|x4|)| |#=| |0|←|↵|}|↵|#module| |List|[|A|,| |B|]|↵|#class| |Cons|[|A|,| |B|]|(|h|#:| |A|,| |t|#:| |Cons|[|A|,| |B|]|)| |#extends| |List|[|A|,| |B|]|↵|#module| |Nil|[|A|,| |B|]| |#extends| |List|[|A|,| |B|]|↵|#module| |Option|[|A|]|↵|#class| |Some|[|A|]|(|x|#:| |A|)| |#extends| |Option|[|A|]|↵|#module| |None|[|A|]| |#extends| |Option|[|A|]|↵|#class| |Pair|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple2|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple3|[|A|,| |B|,| |C|]|(|x|#:| |A|,| |y|#:| |B|,| |z|#:| |C|)|↵|#module| |Nat|↵|#class| |S|(|s|#:| |Nat|)| |#extends| |Nat|↵|#module| |O| |#extends| |Nat|↵|#class| |HiddenTheseEntities|(|_0|#:| |HiddenTheseEntities|,| |_1|#:| |True|,| |_2|#:| |False|,| |_3|#:| |Callable|,| |_4|#:| |List|,| |_5|#:| |Cons|,| |_6|#:| |Nil|,| |_7|#:| |Option|,| |_8|#:| |Some|,| |_9|#:| |None|,| |_10|#:| |Pair|,| |_11|#:| |Tuple2|,| |_12|#:| |Tuple3|,| |_13|#:| |Nat|,| |_14|#:| |S|,| |_15|#:| |O|)| +//│ Parsed: {module True {}; module False {}; module Callable {fun apply0 = () => 0; fun apply1 = (x0,) => 0; fun apply2 = (x0, x1,) => 0; fun apply3 = (x0, x1, x2,) => 0; fun apply4 = (x0, x1, x2, x3,) => 0; fun apply5 = (x0, x1, x2, x3, x4,) => 0}; module List‹A, B› {}; class Cons‹A, B›(h: A, t: Cons‹A, B›,): List‹A, B› {}; module Nil‹A, B›: List‹A, B› {}; module Option‹A› {}; class Some‹A›(x: A,): Option‹A› {}; module None‹A›: Option‹A› {}; class Pair‹A, B›(x: A, y: B,) {}; class Tuple2‹A, B›(x: A, y: B,) {}; class Tuple3‹A, B, C›(x: A, y: B, z: C,) {}; module Nat {}; class S(s: Nat,): Nat {}; module O: Nat {}; class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O,) {}} +//│ +//│ Preluded. +//│ + +:genCpp +:runCpp +:showCpp +module Fn extends Callable { + fun apply1(x) = builtin("println", x) +} +class Fn2(a) extends Callable { + fun apply1(x) = + builtin("println", a) + builtin("println", x) +} +class Demo(n) { + fun x() = n +} +fun f(fn) = fn(1) +fun main() = + let d1 = Demo(2) + Demo.x(d1) + let print = Fn() + Fn.apply1(print, 3) + f(print) + let print2 = Fn2(4) + Fn2.apply1(print2, 5) + print2(6) + f(print2) +main() +//│ |#module| |Fn| |#extends| |Callable| |{|→|#fun| |apply1|(|x|)| |#=| |builtin|(|"println"|,| |x|)|←|↵|}|↵|#class| |Fn2|(|a|)| |#extends| |Callable| |{|→|#fun| |apply1|(|x|)| |#=|→|builtin|(|"println"|,| |a|)|↵|builtin|(|"println"|,| |x|)|←|←|↵|}|↵|#class| |Demo|(|n|)| |{|→|#fun| |x|(||)| |#=| |n|←|↵|}|↵|#fun| |f|(|fn|)| |#=| |fn|(|1|)|↵|#fun| |main|(||)| |#=|→|#let| |d1| |#=| |Demo|(|2|)|↵|Demo|.x|(|d1|)|↵|#let| |print| |#=| |Fn|(||)|↵|Fn|.apply1|(|print|,| |3|)|↵|f|(|print|)|↵|#let| |print2| |#=| |Fn2|(|4|)|↵|Fn2|.apply1|(|print2|,| |5|)|↵|print2|(|6|)|↵|f|(|print2|)|←|↵|main|(||)| +//│ Parsed: {module Fn: Callable {fun apply1 = (x,) => builtin("println", x,)}; class Fn2(a,): Callable {fun apply1 = (x,) => {builtin("println", a,); builtin("println", x,)}}; class Demo(n,) {fun x = () => n}; fun f = (fn,) => fn(1,); fun main = () => {let d1 = Demo(2,); (Demo).x(d1,); let print = Fn(); (Fn).apply1(print, 3,); f(print,); let print2 = Fn2(4,); (Fn2).apply1(print2, 5,); print2(6,); f(print2,)}; main()} +//│ +//│ +//│ IR: +//│ Program: +//│ class Fn() extends Callable { +//│ def apply1(x$11) = +//│ let x$12 = Callable.apply2(builtin,println,x$11) in -- #64 +//│ x$12 -- #63 +//│ } +//│ class Fn2(a) extends Callable { +//│ def apply1(x$13) = +//│ let x$14 = Callable.apply2(builtin,println,a) in -- #80 +//│ let x$15 = Callable.apply2(builtin,println,x$13) in -- #79 +//│ x$15 -- #78 +//│ } +//│ class Demo(n) { +//│ def x() = +//│ n -- #81 +//│ } +//│ def f(fn$0) = +//│ let x$1 = Callable.apply1(fn$0,1) in -- #8 +//│ x$1 -- #7 +//│ def main() = +//│ let x$2 = Demo(2) in -- #56 +//│ let x$3 = Demo.x(x$2) in -- #55 +//│ let x$4 = Fn() in -- #54 +//│ let x$5 = Fn.apply1(x$4,3) in -- #53 +//│ let* (x$6) = f(x$4) in -- #52 +//│ let x$7 = Fn2(4) in -- #51 +//│ let x$8 = Fn2.apply1(x$7,5) in -- #50 +//│ let x$9 = Callable.apply1(x$7,6) in -- #49 +//│ let* (x$10) = f(x$7) in -- #48 +//│ x$10 -- #47 +//│ let* (x$0) = main() in -- #2 +//│ x$0 -- #1 +//│ +//│ Cpp: +//│ struct _mls_Demo: public _mlsObject { +//│ _mlsValue _mls_n; +//│ constexpr static inline const char *typeName = "Demo"; +//│ constexpr static inline uint32_t typeTag = nextTypeTag(); +//│ virtual void print() const override { std::printf("%s", typeName); std::printf("("); this->_mls_n.print(); std::printf(")"); } +//│ virtual void destroy() override { _mlsValue::destroy(this->_mls_n); operator delete (this, std::align_val_t(_mlsAlignment)); } +//│ static _mlsValue create(_mlsValue _mls_n) { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_Demo; _mlsVal->refCount = 1; _mlsVal->tag = typeTag; _mlsVal->_mls_n = _mls_n; return _mlsValue(_mlsVal); } +//│ virtual _mlsValue _mls_x(){ +//│ _mlsValue _mls_retval; +//│ _mls_retval = _mls_n; +//│ return _mls_retval; +//│ } +//│ }; +//│ struct _mls_Fn: public _mls_Callable { +//│ +//│ constexpr static inline const char *typeName = "Fn"; +//│ constexpr static inline uint32_t typeTag = nextTypeTag(); +//│ virtual void print() const override { std::printf("%s", typeName); } +//│ virtual void destroy() override { operator delete (this, std::align_val_t(_mlsAlignment)); } +//│ static _mlsValue create() { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_Fn; _mlsVal->refCount = 1; _mlsVal->tag = typeTag; return _mlsValue(_mlsVal); } +//│ virtual _mlsValue _mls_apply1(_mlsValue _mls_x_11){ +//│ _mlsValue _mls_retval; +//│ auto _mls_x_12 = _mls_builtin_println(_mls_x_11); +//│ _mls_retval = _mls_x_12; +//│ return _mls_retval; +//│ } +//│ }; +//│ struct _mls_Fn2: public _mls_Callable { +//│ _mlsValue _mls_a; +//│ constexpr static inline const char *typeName = "Fn2"; +//│ constexpr static inline uint32_t typeTag = nextTypeTag(); +//│ virtual void print() const override { std::printf("%s", typeName); std::printf("("); this->_mls_a.print(); std::printf(")"); } +//│ virtual void destroy() override { _mlsValue::destroy(this->_mls_a); operator delete (this, std::align_val_t(_mlsAlignment)); } +//│ static _mlsValue create(_mlsValue _mls_a) { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_Fn2; _mlsVal->refCount = 1; _mlsVal->tag = typeTag; _mlsVal->_mls_a = _mls_a; return _mlsValue(_mlsVal); } +//│ virtual _mlsValue _mls_apply1(_mlsValue _mls_x_13){ +//│ _mlsValue _mls_retval; +//│ auto _mls_x_14 = _mls_builtin_println(_mls_a); +//│ auto _mls_x_15 = _mls_builtin_println(_mls_x_13); +//│ _mls_retval = _mls_x_15; +//│ return _mls_retval; +//│ } +//│ }; +//│ _mlsValue _mls_f(_mlsValue _mls_fn_0){ +//│ _mlsValue _mls_retval; +//│ auto _mls_x_1 = _mlsMethodCall<_mls_Callable>(_mls_fn_0)->_mls_apply1(_mlsValue::fromIntLit(1)); +//│ _mls_retval = _mls_x_1; +//│ return _mls_retval; +//│ } +//│ _mlsValue _mls_main(){ +//│ _mlsValue _mls_retval; +//│ auto _mls_x_2 = _mlsValue::create<_mls_Demo>(_mlsValue::fromIntLit(2)); +//│ auto _mls_x_3 = _mlsMethodCall<_mls_Demo>(_mls_x_2)->_mls_x(); +//│ auto _mls_x_4 = _mlsValue::create<_mls_Fn>(); +//│ auto _mls_x_5 = _mlsMethodCall<_mls_Fn>(_mls_x_4)->_mls_apply1(_mlsValue::fromIntLit(3)); +//│ auto _mls_x_6 = _mls_f(_mls_x_4); +//│ auto _mls_x_7 = _mlsValue::create<_mls_Fn2>(_mlsValue::fromIntLit(4)); +//│ auto _mls_x_8 = _mlsMethodCall<_mls_Fn2>(_mls_x_7)->_mls_apply1(_mlsValue::fromIntLit(5)); +//│ auto _mls_x_9 = _mlsMethodCall<_mls_Callable>(_mls_x_7)->_mls_apply1(_mlsValue::fromIntLit(6)); +//│ auto _mls_x_10 = _mls_f(_mls_x_7); +//│ _mls_retval = _mls_x_10; +//│ return _mls_retval; +//│ } +//│ _mlsValue _mlsMain(){ +//│ _mlsValue _mls_retval; +//│ auto _mls_x_0 = _mls_main(); +//│ _mls_retval = _mls_x_0; +//│ return _mls_retval; +//│ } +//│ int main() { return _mlsLargeStack(_mlsMainWrapper); } +//│ +//│ +//│ Execution succeeded: +//│ 3 +//│ 1 +//│ 4 +//│ 5 +//│ 4 +//│ 6 +//│ 4 +//│ 1 +//│ Unit +//│ diff --git a/compiler/shared/test/diff-ir/Currying.mls b/compiler/shared/test/diff-ir/Currying.mls new file mode 100644 index 00000000..313e1ea9 --- /dev/null +++ b/compiler/shared/test/diff-ir/Currying.mls @@ -0,0 +1,95 @@ +:NewDefs +:ParseOnly +:UseIR +:NoTailRec + +:prelude +module True +module False +module Callable { + fun apply0() = 0 + fun apply1(x0) = 0 + fun apply2(x0,x1) = 0 + fun apply3(x0,x1,x2) = 0 + fun apply4(x0,x1,x2,x3) = 0 + fun apply5(x0,x1,x2,x3,x4) = 0 +} +module List[A, B] +class Cons[A, B](h: A, t: Cons[A, B]) extends List[A, B] +module Nil[A, B] extends List[A, B] +module Option[A] +class Some[A](x: A) extends Option[A] +module None[A] extends Option[A] +class Pair[A, B](x: A, y: B) +class Tuple2[A, B](x: A, y: B) +class Tuple3[A, B, C](x: A, y: B, z: C) +module Nat +class S(s: Nat) extends Nat +module O extends Nat +class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O) +//│ |#module| |True|↵|#module| |False|↵|#module| |Callable| |{|→|#fun| |apply0|(||)| |#=| |0|↵|#fun| |apply1|(|x0|)| |#=| |0|↵|#fun| |apply2|(|x0|,|x1|)| |#=| |0|↵|#fun| |apply3|(|x0|,|x1|,|x2|)| |#=| |0|↵|#fun| |apply4|(|x0|,|x1|,|x2|,|x3|)| |#=| |0|↵|#fun| |apply5|(|x0|,|x1|,|x2|,|x3|,|x4|)| |#=| |0|←|↵|}|↵|#module| |List|[|A|,| |B|]|↵|#class| |Cons|[|A|,| |B|]|(|h|#:| |A|,| |t|#:| |Cons|[|A|,| |B|]|)| |#extends| |List|[|A|,| |B|]|↵|#module| |Nil|[|A|,| |B|]| |#extends| |List|[|A|,| |B|]|↵|#module| |Option|[|A|]|↵|#class| |Some|[|A|]|(|x|#:| |A|)| |#extends| |Option|[|A|]|↵|#module| |None|[|A|]| |#extends| |Option|[|A|]|↵|#class| |Pair|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple2|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple3|[|A|,| |B|,| |C|]|(|x|#:| |A|,| |y|#:| |B|,| |z|#:| |C|)|↵|#module| |Nat|↵|#class| |S|(|s|#:| |Nat|)| |#extends| |Nat|↵|#module| |O| |#extends| |Nat|↵|#class| |HiddenTheseEntities|(|_0|#:| |HiddenTheseEntities|,| |_1|#:| |True|,| |_2|#:| |False|,| |_3|#:| |Callable|,| |_4|#:| |List|,| |_5|#:| |Cons|,| |_6|#:| |Nil|,| |_7|#:| |Option|,| |_8|#:| |Some|,| |_9|#:| |None|,| |_10|#:| |Pair|,| |_11|#:| |Tuple2|,| |_12|#:| |Tuple3|,| |_13|#:| |Nat|,| |_14|#:| |S|,| |_15|#:| |O|)| +//│ Parsed: {module True {}; module False {}; module Callable {fun apply0 = () => 0; fun apply1 = (x0,) => 0; fun apply2 = (x0, x1,) => 0; fun apply3 = (x0, x1, x2,) => 0; fun apply4 = (x0, x1, x2, x3,) => 0; fun apply5 = (x0, x1, x2, x3, x4,) => 0}; module List‹A, B› {}; class Cons‹A, B›(h: A, t: Cons‹A, B›,): List‹A, B› {}; module Nil‹A, B›: List‹A, B› {}; module Option‹A› {}; class Some‹A›(x: A,): Option‹A› {}; module None‹A›: Option‹A› {}; class Pair‹A, B›(x: A, y: B,) {}; class Tuple2‹A, B›(x: A, y: B,) {}; class Tuple3‹A, B, C›(x: A, y: B, z: C,) {}; module Nat {}; class S(s: Nat,): Nat {}; module O: Nat {}; class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O,) {}} +//│ +//│ Preluded. +//│ + +:interpIR +:genCpp +:runCpp +fun add2c(a)(b) = a + b +fun add2(a, b) = a + b +fun add3c(a)(b)(c) = a + b + c +fun main() = + add2c(1)(2) + add2(1, 2) + add3c(1)(2)(3) +main() +//│ |#fun| |add2c|(|a|)|(|b|)| |#=| |a| |+| |b|↵|#fun| |add2|(|a|,| |b|)| |#=| |a| |+| |b|↵|#fun| |add3c|(|a|)|(|b|)|(|c|)| |#=| |a| |+| |b| |+| |c|↵|#fun| |main|(||)| |#=|→|add2c|(|1|)|(|2|)|↵|add2|(|1|,| |2|)|↵|add3c|(|1|)|(|2|)|(|3|)|←|↵|main|(||)| +//│ Parsed: {fun add2c = (a,) => (b,) => +(a, b,); fun add2 = (a, b,) => +(a, b,); fun add3c = (a,) => (b,) => (c,) => +(+(a, b,), c,); fun main = () => {add2c(1,)(2,); add2(1, 2,); add3c(1,)(2,)(3,)}; main()} +//│ +//│ +//│ IR: +//│ Program: +//│ class Lambda$0(a) extends Callable { +//│ def apply1(b$1) = +//│ let x$12 = +(a,b$1) in -- #58 +//│ x$12 -- #57 +//│ } +//│ class Lambda$1(a) extends Callable { +//│ def apply1(b$2) = +//│ let x$14 = Lambda$2(a,b$2) in -- #60 +//│ x$14 -- #59 +//│ } +//│ class Lambda$2(a,b) extends Callable { +//│ def apply1(c$0) = +//│ let x$15 = +(a,b) in -- #73 +//│ let x$16 = +(x$15,c$0) in -- #72 +//│ x$16 -- #71 +//│ } +//│ def add2c(a$0) = +//│ let x$2 = Lambda$0(a$0) in -- #4 +//│ x$2 -- #3 +//│ def add2(a$1,b$0) = +//│ let x$3 = +(a$1,b$0) in -- #11 +//│ x$3 -- #10 +//│ def add3c(a$2) = +//│ let x$5 = Lambda$1(a$2) in -- #13 +//│ x$5 -- #12 +//│ def main() = +//│ let* (x$6) = add2c(1) in -- #45 +//│ let x$7 = Callable.apply1(x$6,2) in -- #44 +//│ let* (x$8) = add2(1,2) in -- #43 +//│ let* (x$9) = add3c(1) in -- #42 +//│ let x$10 = Callable.apply1(x$9,2) in -- #41 +//│ let x$11 = Callable.apply1(x$10,3) in -- #40 +//│ x$11 -- #39 +//│ let* (x$0) = main() in -- #2 +//│ x$0 -- #1 +//│ +//│ Interpreted: +//│ 6 +//│ +//│ +//│ Execution succeeded: +//│ 6 +//│ diff --git a/compiler/shared/test/diff-ir/IR.mls b/compiler/shared/test/diff-ir/IR.mls index b5b44bbc..ce5dbd16 100644 --- a/compiler/shared/test/diff-ir/IR.mls +++ b/compiler/shared/test/diff-ir/IR.mls @@ -1,85 +1,108 @@ -:NewParser +:NewDefs :ParseOnly :UseIR :NoTailRec +:prelude +module True +module False +module Callable { + fun apply0() = 0 + fun apply1(x0) = 0 + fun apply2(x0,x1) = 0 + fun apply3(x0,x1,x2) = 0 + fun apply4(x0,x1,x2,x3) = 0 + fun apply5(x0,x1,x2,x3,x4) = 0 +} +module List[A, B] +class Cons[A, B](h: A, t: Cons[A, B]) extends List[A, B] +module Nil[A, B] extends List[A, B] +module Option[A] +class Some[A](x: A) extends Option[A] +module None[A] extends Option[A] +class Pair[A, B](x: A, y: B) +class Tuple2[A, B](x: A, y: B) +class Tuple3[A, B, C](x: A, y: B, z: C) +module Nat +class S(s: Nat) extends Nat +module O extends Nat +module Either[A, B] +class Left[A, B](x: A) extends Either[A, B] +class Right[A, B](y: B) extends Either[A, B] +class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O, _16: Either, _17: Left, _18: Right) +//│ |#module| |True|↵|#module| |False|↵|#module| |Callable| |{|→|#fun| |apply0|(||)| |#=| |0|↵|#fun| |apply1|(|x0|)| |#=| |0|↵|#fun| |apply2|(|x0|,|x1|)| |#=| |0|↵|#fun| |apply3|(|x0|,|x1|,|x2|)| |#=| |0|↵|#fun| |apply4|(|x0|,|x1|,|x2|,|x3|)| |#=| |0|↵|#fun| |apply5|(|x0|,|x1|,|x2|,|x3|,|x4|)| |#=| |0|←|↵|}|↵|#module| |List|[|A|,| |B|]|↵|#class| |Cons|[|A|,| |B|]|(|h|#:| |A|,| |t|#:| |Cons|[|A|,| |B|]|)| |#extends| |List|[|A|,| |B|]|↵|#module| |Nil|[|A|,| |B|]| |#extends| |List|[|A|,| |B|]|↵|#module| |Option|[|A|]|↵|#class| |Some|[|A|]|(|x|#:| |A|)| |#extends| |Option|[|A|]|↵|#module| |None|[|A|]| |#extends| |Option|[|A|]|↵|#class| |Pair|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple2|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple3|[|A|,| |B|,| |C|]|(|x|#:| |A|,| |y|#:| |B|,| |z|#:| |C|)|↵|#module| |Nat|↵|#class| |S|(|s|#:| |Nat|)| |#extends| |Nat|↵|#module| |O| |#extends| |Nat|↵|#module| |Either|[|A|,| |B|]|↵|#class| |Left|[|A|,| |B|]|(|x|#:| |A|)| |#extends| |Either|[|A|,| |B|]|↵|#class| |Right|[|A|,| |B|]|(|y|#:| |B|)| |#extends| |Either|[|A|,| |B|]|↵|#class| |HiddenTheseEntities|(|_0|#:| |HiddenTheseEntities|,| |_1|#:| |True|,| |_2|#:| |False|,| |_3|#:| |Callable|,| |_4|#:| |List|,| |_5|#:| |Cons|,| |_6|#:| |Nil|,| |_7|#:| |Option|,| |_8|#:| |Some|,| |_9|#:| |None|,| |_10|#:| |Pair|,| |_11|#:| |Tuple2|,| |_12|#:| |Tuple3|,| |_13|#:| |Nat|,| |_14|#:| |S|,| |_15|#:| |O|,| |_16|#:| |Either|,| |_17|#:| |Left|,| |_18|#:| |Right|)| +//│ Parsed: {module True {}; module False {}; module Callable {fun apply0 = () => 0; fun apply1 = (x0,) => 0; fun apply2 = (x0, x1,) => 0; fun apply3 = (x0, x1, x2,) => 0; fun apply4 = (x0, x1, x2, x3,) => 0; fun apply5 = (x0, x1, x2, x3, x4,) => 0}; module List‹A, B› {}; class Cons‹A, B›(h: A, t: Cons‹A, B›,): List‹A, B› {}; module Nil‹A, B›: List‹A, B› {}; module Option‹A› {}; class Some‹A›(x: A,): Option‹A› {}; module None‹A›: Option‹A› {}; class Pair‹A, B›(x: A, y: B,) {}; class Tuple2‹A, B›(x: A, y: B,) {}; class Tuple3‹A, B, C›(x: A, y: B, z: C,) {}; module Nat {}; class S(s: Nat,): Nat {}; module O: Nat {}; module Either‹A, B› {}; class Left‹A, B›(x: A,): Either‹A, B› {}; class Right‹A, B›(y: B,): Either‹A, B› {}; class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O, _16: Either, _17: Left, _18: Right,) {}} +//│ +//│ Preluded. +//│ + + :interpIR -class Pair(x, y) fun mktup2(x, y) = mktup(x, y) fun mktup(x, y) = Pair(x, y) fun foo() = mktup2(1, 2) foo() -//│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |mktup2|(|x|,| |y|)| |#=| |mktup|(|x|,| |y|)|↵|#fun| |mktup|(|x|,| |y|)| |#=| |Pair|(|x|,| |y|)|↵|#fun| |foo|(||)| |#=|→|mktup2|(|1|,| |2|)|←|↵|foo|(||)| -//│ Parsed: {class Pair(x, y,) {}; fun mktup2 = (x, y,) => mktup(x, y,); fun mktup = (x, y,) => Pair(x, y,); fun foo = () => {mktup2(1, 2,)}; foo()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { -//│ Def(0, mktup2, [x$0,y$0], -//│ 1, -//│ let* (x$1) = mktup(x$0,y$0) in -- #7 -//│ x$1 -- #6 -//│ ) -//│ Def(1, mktup, [x$2,y$1], -//│ 1, -//│ let x$3 = Pair(x$2,y$1) in -- #14 -//│ x$3 -- #13 -//│ ) -//│ Def(2, foo, [], -//│ 1, -//│ let* (x$4) = mktup2(1,2) in -- #22 -//│ x$4 -- #21 -//│ ) -//│ }, -//│ let* (x$5) = foo() in -- #26 -//│ x$5 -- #25) +//│ |#fun| |mktup2|(|x|,| |y|)| |#=| |mktup|(|x|,| |y|)|↵|#fun| |mktup|(|x|,| |y|)| |#=| |Pair|(|x|,| |y|)|↵|#fun| |foo|(||)| |#=|→|mktup2|(|1|,| |2|)|←|↵|foo|(||)| +//│ Parsed: {fun mktup2 = (x, y,) => mktup(x, y,); fun mktup = (x, y,) => Pair(x, y,); fun foo = () => {mktup2(1, 2,)}; foo()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def mktup2(x$1,y$0) = +//│ let* (x$2) = mktup(x$1,y$0) in -- #9 +//│ x$2 -- #8 +//│ def mktup(x$3,y$1) = +//│ let x$4 = Pair(x$3,y$1) in -- #16 +//│ x$4 -- #15 +//│ def foo() = +//│ let* (x$5) = mktup2(1,2) in -- #23 +//│ x$5 -- #22 +//│ let* (x$0) = foo() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: //│ Pair(1,2) +//│ :interpIR -class Pair(x, y) fun foo(pair) = if pair is Pair(x, y) then Pair(x, y) fun bar() = foo(Pair(1, 2)) bar() -//│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |foo|(|pair|)| |#=|→|#if| |pair| |is|→|Pair|(|x|,| |y|)| |#then| |Pair|(|x|,| |y|)|←|←|↵|#fun| |bar|(||)| |#=|→|foo|(|Pair|(|1|,| |2|)|)|←|↵|bar|(||)| -//│ Parsed: {class Pair(x, y,) {}; fun foo = (pair,) => {if pair is ‹(Pair(x, y,)) then Pair(x, y,)›}; fun bar = () => {foo(Pair(1, 2,),)}; bar()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { -//│ Def(0, foo, [pair$0], -//│ 1, -//│ case pair$0 of -- #16 -//│ Pair => -//│ let x$1 = pair$0.y in -- #15 -//│ let x$2 = pair$0.x in -- #14 -//│ let x$3 = Pair(x$2,x$1) in -- #13 -//│ jump j$0(x$3) -- #12 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, bar, [], -//│ 1, -//│ let x$4 = Pair(1,2) in -- #28 -//│ let* (x$5) = foo(x$4) in -- #27 -//│ x$5 -- #26 -//│ ) -//│ }, -//│ let* (x$6) = bar() in -- #32 -//│ x$6 -- #31) +//│ |#fun| |foo|(|pair|)| |#=|→|#if| |pair| |is|→|Pair|(|x|,| |y|)| |#then| |Pair|(|x|,| |y|)|←|←|↵|#fun| |bar|(||)| |#=|→|foo|(|Pair|(|1|,| |2|)|)|←|↵|bar|(||)| +//│ Parsed: {fun foo = (pair,) => {if pair is ‹(Pair(x, y,)) then Pair(x, y,)›}; fun bar = () => {foo(Pair(1, 2,),)}; bar()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def foo(pair$0) = +//│ case pair$0 of -- #23 +//│ Pair => +//│ let x$2 = Pair.y(pair$0) in -- #22 +//│ let x$3 = Pair.x(pair$0) in -- #21 +//│ let x$4 = Pair(x$3,x$2) in -- #20 +//│ jump j$0(x$4) -- #19 +//│ def j$0(x$1) = +//│ x$1 -- #4 +//│ def bar() = +//│ let x$5 = Pair(1,2) in -- #34 +//│ let* (x$6) = foo(x$5) in -- #33 +//│ x$6 -- #32 +//│ let* (x$0) = bar() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: //│ Pair(1,2) +//│ :interpIR -class Pair(x, y) {} fun silly(pair) = let _ = 0 let n = if pair is @@ -92,52 +115,45 @@ fun foo() = let b = silly(a) b foo() -//│ |#class| |Pair|(|x|,| |y|)| |{||}|↵|#fun| |silly|(|pair|)| |#=|→|#let| |_| |#=| |0|↵|#let| |n| |#=| |#if| |pair| |is|→|Pair|(|x1|,| |x2|)| |#then|→|#if| |pair| |is|→|Pair| |(|x3|,| |x4|)| |#then| |x3| |+| |1|←|←|←|↵|n| |+| |1|←|↵|#fun| |foo|(||)| |#=|→|#let| |a| |#=| |Pair|(|0|,| |1|)|↵|#let| |b| |#=| |silly|(|a|)|↵|b|←|↵|foo|(||)| -//│ Parsed: {class Pair(x, y,) {}; fun silly = (pair,) => {let _ = 0; let n = if pair is ‹(Pair(x1, x2,)) then {if pair is ‹(Pair(x3, x4,)) then +(x3,)(1,)›}›; +(n,)(1,)}; fun foo = () => {let a = Pair(0, 1,); let b = silly(a,); b}; foo()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { -//│ Def(0, silly, [pair$0], -//│ 1, -//│ let x$0 = 0 in -- #29 -//│ case pair$0 of -- #28 -//│ Pair => -//│ let x$3 = pair$0.y in -- #27 -//│ let x$4 = pair$0.x in -- #26 -//│ case pair$0 of -- #25 +//│ |#fun| |silly|(|pair|)| |#=|→|#let| |_| |#=| |0|↵|#let| |n| |#=| |#if| |pair| |is|→|Pair|(|x1|,| |x2|)| |#then|→|#if| |pair| |is|→|Pair| |(|x3|,| |x4|)| |#then| |x3| |+| |1|←|←|←|↵|n| |+| |1|←|↵|#fun| |foo|(||)| |#=|→|#let| |a| |#=| |Pair|(|0|,| |1|)|↵|#let| |b| |#=| |silly|(|a|)|↵|b|←|↵|foo|(||)| +//│ Parsed: {fun silly = (pair,) => {let _ = 0; let n = if pair is ‹(Pair(x1, x2,)) then {if pair is ‹(Pair(x3, x4,)) then +(x3, 1,)›}›; +(n, 1,)}; fun foo = () => {let a = Pair(0, 1,); let b = silly(a,); b}; foo()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def silly(pair$0) = +//│ let x$1 = 0 in -- #46 +//│ case pair$0 of -- #45 //│ Pair => -//│ let x$6 = pair$0.y in -- #24 -//│ let x$7 = pair$0.x in -- #23 -//│ let x$8 = +(x$7,1) in -- #22 -//│ jump j$1(x$8) -- #21 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ let x$2 = +(x$1,1) in -- #6 -//│ x$2 -- #5 -//│ ) -//│ Def(2, j$1, [x$5], -//│ 1, -//│ jump j$0(x$5) -- #13 -//│ ) -//│ Def(3, foo, [], -//│ 1, -//│ let x$9 = Pair(0,1) in -- #43 -//│ let* (x$10) = silly(x$9) in -- #42 -//│ x$10 -- #41 -//│ ) -//│ }, -//│ let* (x$11) = foo() in -- #47 -//│ x$11 -- #46) +//│ let x$4 = Pair.y(pair$0) in -- #44 +//│ let x$5 = Pair.x(pair$0) in -- #43 +//│ case pair$0 of -- #42 +//│ Pair => +//│ let x$7 = Pair.y(pair$0) in -- #41 +//│ let x$8 = Pair.x(pair$0) in -- #40 +//│ let x$9 = +(x$8,1) in -- #39 +//│ jump j$1(x$9) -- #38 +//│ def j$0(x$2) = +//│ let x$3 = +(x$2,1) in -- #12 +//│ x$3 -- #11 +//│ def j$1(x$6) = +//│ jump j$0(x$6) -- #23 +//│ def foo() = +//│ let x$10 = Pair(0,1) in -- #59 +//│ let* (x$11) = silly(x$10) in -- #58 +//│ x$11 -- #57 +//│ let* (x$0) = foo() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: //│ 2 +//│ :interpIR -class Pair(x, y) fun inc_fst(pair) = let c = 2 if pair is @@ -147,40 +163,35 @@ fun foo() = let b = inc_fst(a) b foo() -//│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |inc_fst|(|pair|)| |#=|→|#let| |c| |#=| |2|↵|#if| |pair| |is|→|Pair|(|x1|,| |x2|)| |#then| |x1| |+| |c|←|←|↵|#fun| |foo|(||)| |#=|→|#let| |a| |#=| |Pair|(|0|,| |1|)|↵|#let| |b| |#=| |inc_fst|(|a|)|↵|b|←|↵|foo|(||)| -//│ Parsed: {class Pair(x, y,) {}; fun inc_fst = (pair,) => {let c = 2; if pair is ‹(Pair(x1, x2,)) then +(x1,)(c,)›}; fun foo = () => {let a = Pair(0, 1,); let b = inc_fst(a,); b}; foo()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { -//│ Def(0, inc_fst, [pair$0], -//│ 1, -//│ let x$0 = 2 in -- #15 -//│ case pair$0 of -- #14 -//│ Pair => -//│ let x$2 = pair$0.y in -- #13 -//│ let x$3 = pair$0.x in -- #12 -//│ let x$4 = +(x$3,x$0) in -- #11 -//│ jump j$0(x$4) -- #10 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #2 -//│ ) -//│ Def(2, foo, [], -//│ 1, -//│ let x$5 = Pair(0,1) in -- #29 -//│ let* (x$6) = inc_fst(x$5) in -- #28 -//│ x$6 -- #27 -//│ ) -//│ }, -//│ let* (x$7) = foo() in -- #33 -//│ x$7 -- #32) +//│ |#fun| |inc_fst|(|pair|)| |#=|→|#let| |c| |#=| |2|↵|#if| |pair| |is|→|Pair|(|x1|,| |x2|)| |#then| |x1| |+| |c|←|←|↵|#fun| |foo|(||)| |#=|→|#let| |a| |#=| |Pair|(|0|,| |1|)|↵|#let| |b| |#=| |inc_fst|(|a|)|↵|b|←|↵|foo|(||)| +//│ Parsed: {fun inc_fst = (pair,) => {let c = 2; if pair is ‹(Pair(x1, x2,)) then +(x1, c,)›}; fun foo = () => {let a = Pair(0, 1,); let b = inc_fst(a,); b}; foo()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def inc_fst(pair$0) = +//│ let x$1 = 2 in -- #25 +//│ case pair$0 of -- #24 +//│ Pair => +//│ let x$3 = Pair.y(pair$0) in -- #23 +//│ let x$4 = Pair.x(pair$0) in -- #22 +//│ let x$5 = +(x$4,x$1) in -- #21 +//│ jump j$0(x$5) -- #20 +//│ def j$0(x$2) = +//│ x$2 -- #5 +//│ def foo() = +//│ let x$6 = Pair(0,1) in -- #38 +//│ let* (x$7) = inc_fst(x$6) in -- #37 +//│ x$7 -- #36 +//│ let* (x$0) = foo() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: //│ 2 +//│ :interpIR -class Pair(x, y) fun inc_fst(pair) = let _ = 0 if pair is @@ -189,41 +200,35 @@ fun foo() = let b = inc_fst(Pair(0, 1)) b foo() -//│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |inc_fst|(|pair|)| |#=|→|#let| |_| |#=| |0|↵|#if| |pair| |is|→|Pair|(|x1|,| |x2|)| |#then| |x2| |+| |1|←|←|↵|#fun| |foo|(||)| |#=|→|#let| |b| |#=| |inc_fst|(|Pair|(|0|,| |1|)|)|↵|b|←|↵|foo|(||)| -//│ Parsed: {class Pair(x, y,) {}; fun inc_fst = (pair,) => {let _ = 0; if pair is ‹(Pair(x1, x2,)) then +(x2,)(1,)›}; fun foo = () => {let b = inc_fst(Pair(0, 1,),); b}; foo()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { -//│ Def(0, inc_fst, [pair$0], -//│ 1, -//│ let x$0 = 0 in -- #15 -//│ case pair$0 of -- #14 -//│ Pair => -//│ let x$2 = pair$0.y in -- #13 -//│ let x$3 = pair$0.x in -- #12 -//│ let x$4 = +(x$2,1) in -- #11 -//│ jump j$0(x$4) -- #10 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #2 -//│ ) -//│ Def(2, foo, [], -//│ 1, -//│ let x$5 = Pair(0,1) in -- #28 -//│ let* (x$6) = inc_fst(x$5) in -- #27 -//│ x$6 -- #26 -//│ ) -//│ }, -//│ let* (x$7) = foo() in -- #32 -//│ x$7 -- #31) +//│ |#fun| |inc_fst|(|pair|)| |#=|→|#let| |_| |#=| |0|↵|#if| |pair| |is|→|Pair|(|x1|,| |x2|)| |#then| |x2| |+| |1|←|←|↵|#fun| |foo|(||)| |#=|→|#let| |b| |#=| |inc_fst|(|Pair|(|0|,| |1|)|)|↵|b|←|↵|foo|(||)| +//│ Parsed: {fun inc_fst = (pair,) => {let _ = 0; if pair is ‹(Pair(x1, x2,)) then +(x2, 1,)›}; fun foo = () => {let b = inc_fst(Pair(0, 1,),); b}; foo()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def inc_fst(pair$0) = +//│ let x$1 = 0 in -- #25 +//│ case pair$0 of -- #24 +//│ Pair => +//│ let x$3 = Pair.y(pair$0) in -- #23 +//│ let x$4 = Pair.x(pair$0) in -- #22 +//│ let x$5 = +(x$3,1) in -- #21 +//│ jump j$0(x$5) -- #20 +//│ def j$0(x$2) = +//│ x$2 -- #5 +//│ def foo() = +//│ let x$6 = Pair(0,1) in -- #37 +//│ let* (x$7) = inc_fst(x$6) in -- #36 +//│ x$7 -- #35 +//│ let* (x$0) = foo() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: //│ 2 +//│ :interpIR -class Left(x) -class Right(y) fun foo(a, b) = let t = if a is Left(x) then Left(x + 1) @@ -234,132 +239,81 @@ fun foo(a, b) = fun bar() = foo(Right(2), 2) bar() -//│ |#class| |Left|(|x|)|↵|#class| |Right|(|y|)|↵|#fun| |foo|(|a|,| |b|)| |#=|→|#let| |t| |#=| |#if| |a| |is|→|Left|(|x|)| |#then| |Left|(|x| |+| |1|)|↵|Right|(|y|)| |#then| |Right|(|b|)|←|↵|#if| |t| |is|→|Left|(|x|)| |#then| |x|↵|Right|(|y|)| |#then| |y|←|←|↵|#fun| |bar|(||)| |#=|→|foo|(|Right|(|2|)|,| |2|)|←|↵|bar|(||)| -//│ Parsed: {class Left(x,) {}; class Right(y,) {}; fun foo = (a, b,) => {let t = if a is ‹(Left(x,)) then Left(+(x,)(1,),); (Right(y,)) then Right(b,)›; if t is ‹(Left(x,)) then x; (Right(y,)) then y›}; fun bar = () => {foo(Right(2,), 2,)}; bar()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Left, [x]),ClassInfo(3, Right, [y])}, { -//│ Def(0, foo, [a$0,b$0], -//│ 1, -//│ case a$0 of -- #36 -//│ Left => -//│ let x$4 = a$0.x in -- #26 -//│ let x$5 = +(x$4,1) in -- #25 -//│ let x$6 = Left(x$5) in -- #24 -//│ jump j$0(x$6) -- #23 -//│ Right => -//│ let x$7 = a$0.y in -- #35 -//│ let x$8 = Right(b$0) in -- #34 -//│ jump j$0(x$8) -- #33 -//│ ) -//│ Def(1, j$1, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ Def(2, j$0, [x$0], -//│ 1, -//│ case x$0 of -- #14 -//│ Left => -//│ let x$2 = x$0.x in -- #8 -//│ jump j$1(x$2) -- #7 -//│ Right => -//│ let x$3 = x$0.y in -- #13 -//│ jump j$1(x$3) -- #12 -//│ ) -//│ Def(3, bar, [], -//│ 1, -//│ let x$9 = Right(2) in -- #48 -//│ let* (x$10) = foo(x$9,2) in -- #47 -//│ x$10 -- #46 -//│ ) -//│ }, -//│ let* (x$11) = bar() in -- #52 -//│ x$11 -- #51) +//│ |#fun| |foo|(|a|,| |b|)| |#=|→|#let| |t| |#=| |#if| |a| |is|→|Left|(|x|)| |#then| |Left|(|x| |+| |1|)|↵|Right|(|y|)| |#then| |Right|(|b|)|←|↵|#if| |t| |is|→|Left|(|x|)| |#then| |x|↵|Right|(|y|)| |#then| |y|←|←|↵|#fun| |bar|(||)| |#=|→|foo|(|Right|(|2|)|,| |2|)|←|↵|bar|(||)| +//│ Parsed: {fun foo = (a, b,) => {let t = if a is ‹(Left(x,)) then Left(+(x, 1,),); (Right(y,)) then Right(b,)›; if t is ‹(Left(x,)) then x; (Right(y,)) then y›}; fun bar = () => {foo(Right(2,), 2,)}; bar()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def foo(a$0,b$0) = +//│ case a$0 of -- #50 +//│ Left => +//│ let x$5 = Left.x(a$0) in -- #38 +//│ let x$6 = +(x$5,1) in -- #37 +//│ let x$7 = Left(x$6) in -- #36 +//│ jump j$0(x$7) -- #35 +//│ Right => +//│ let x$8 = Right.y(a$0) in -- #49 +//│ let x$9 = Right(b$0) in -- #48 +//│ jump j$0(x$9) -- #47 +//│ def j$1(x$2) = +//│ x$2 -- #6 +//│ def j$0(x$1) = +//│ case x$1 of -- #21 +//│ Left => +//│ let x$3 = Left.x(x$1) in -- #13 +//│ jump j$1(x$3) -- #12 +//│ Right => +//│ let x$4 = Right.y(x$1) in -- #20 +//│ jump j$1(x$4) -- #19 +//│ def bar() = +//│ let x$10 = Right(2) in -- #61 +//│ let* (x$11) = foo(x$10,2) in -- #60 +//│ x$11 -- #59 +//│ let* (x$0) = bar() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: //│ 2 +//│ :interpIR -class Pair(x, y) -fun foo(a) = a.x + a.y +fun foo(a) = Pair.x(a) + Pair.y(a) fun bar() = foo(Pair(1, 0)) bar() -//│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |foo|(|a|)| |#=| |a|.x| |+| |a|.y|↵|#fun| |bar|(||)| |#=|→|foo|(|Pair|(|1|,| |0|)|)|←|↵|bar|(||)| -//│ Parsed: {class Pair(x, y,) {}; fun foo = (a,) => +((a).x,)((a).y,); fun bar = () => {foo(Pair(1, 0,),)}; bar()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { -//│ Def(0, foo, [a$0], -//│ 1, -//│ let x$0 = a$0.x in -- #7 -//│ let x$1 = a$0.y in -- #6 -//│ let x$2 = +(x$0,x$1) in -- #5 -//│ x$2 -- #4 -//│ ) -//│ Def(1, bar, [], -//│ 1, -//│ let x$3 = Pair(1,0) in -- #19 -//│ let* (x$4) = foo(x$3) in -- #18 -//│ x$4 -- #17 -//│ ) -//│ }, -//│ let* (x$5) = bar() in -- #23 -//│ x$5 -- #22) +//│ |#fun| |foo|(|a|)| |#=| |Pair|.x|(|a|)| |+| |Pair|.y|(|a|)|↵|#fun| |bar|(||)| |#=|→|foo|(|Pair|(|1|,| |0|)|)|←|↵|bar|(||)| +//│ Parsed: {fun foo = (a,) => +((Pair).x(a,), (Pair).y(a,),); fun bar = () => {foo(Pair(1, 0,),)}; bar()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def foo(a$0) = +//│ let x$1 = Pair.x(a$0) in -- #17 +//│ let x$2 = Pair.y(a$0) in -- #16 +//│ let x$3 = +(x$1,x$2) in -- #15 +//│ x$3 -- #14 +//│ def bar() = +//│ let x$4 = Pair(1,0) in -- #28 +//│ let* (x$5) = foo(x$4) in -- #27 +//│ x$5 -- #26 +//│ let* (x$0) = bar() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: //│ 1 +//│ -:interpIR -class C1(x, y) -class C2(z) -fun foo(a) = if a is - C1(x, y) then x - C2(z) then z -fun bar() = - foo(C1(0, 1)) -bar() -//│ |#class| |C1|(|x|,| |y|)|↵|#class| |C2|(|z|)|↵|#fun| |foo|(|a|)| |#=| |#if| |a| |is|→|C1|(|x|,| |y|)| |#then| |x|↵|C2|(|z|)| |#then| |z|←|↵|#fun| |bar|(||)| |#=|→|foo|(|C1|(|0|,| |1|)|)|←|↵|bar|(||)| -//│ Parsed: {class C1(x, y,) {}; class C2(z,) {}; fun foo = (a,) => if a is ‹(C1(x, y,)) then x; (C2(z,)) then z›; fun bar = () => {foo(C1(0, 1,),)}; bar()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, C1, [x,y]),ClassInfo(3, C2, [z])}, { -//│ Def(0, foo, [a$0], -//│ 1, -//│ case a$0 of -- #15 -//│ C1 => -//│ let x$1 = a$0.y in -- #9 -//│ let x$2 = a$0.x in -- #8 -//│ jump j$0(x$2) -- #7 -//│ C2 => -//│ let x$3 = a$0.z in -- #14 -//│ jump j$0(x$3) -- #13 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, bar, [], -//│ 1, -//│ let x$4 = C1(0,1) in -- #27 -//│ let* (x$5) = foo(x$4) in -- #26 -//│ x$5 -- #25 -//│ ) -//│ }, -//│ let* (x$6) = bar() in -- #31 -//│ x$6 -- #30) -//│ -//│ Interpreted: -//│ 0 :interpIR -class Pair(x, y) fun foo(a, b) = - let x1 = a.x - let y1 = a.y - let x2 = b.x - let y2 = b.y + let x1 = Pair.x(a) + let y1 = Pair.y(a) + let x2 = Pair.x(b) + let y2 = Pair.y(b) x1 + y1 + x2 + y2 fun bar(c) = foo(Pair(0, 1), c) @@ -368,74 +322,67 @@ fun bar(c) = fun baz() = bar(Pair(4,5)) baz() -//│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |foo|(|a|,| |b|)| |#=|→|#let| |x1| |#=| |a|.x|↵|#let| |y1| |#=| |a|.y|↵|#let| |x2| |#=| |b|.x|↵|#let| |y2| |#=| |b|.y|↵|x1| |+| |y1| |+| |x2| |+| |y2|←|↵|#fun| |bar|(|c|)| |#=|→|foo|(|Pair|(|0|,| |1|)|,| |c|)|↵|foo|(|c|,| |Pair|(|2|,| |3|)|)|↵|foo|(|Pair|(|0|,| |1|)|,| |Pair|(|2|,| |3|)|)|←|↵|#fun| |baz|(||)| |#=|→|bar|(|Pair|(|4|,|5|)|)|←|↵|baz|(||)| -//│ Parsed: {class Pair(x, y,) {}; fun foo = (a, b,) => {let x1 = (a).x; let y1 = (a).y; let x2 = (b).x; let y2 = (b).y; +(+(+(x1,)(y1,),)(x2,),)(y2,)}; fun bar = (c,) => {foo(Pair(0, 1,), c,); foo(c, Pair(2, 3,),); foo(Pair(0, 1,), Pair(2, 3,),)}; fun baz = () => {bar(Pair(4, 5,),)}; baz()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { -//│ Def(0, foo, [a$0,b$0], -//│ 1, -//│ let x$0 = a$0.x in -- #21 -//│ let x$1 = a$0.y in -- #20 -//│ let x$2 = b$0.x in -- #19 -//│ let x$3 = b$0.y in -- #18 -//│ let x$4 = +(x$0,x$1) in -- #17 -//│ let x$5 = +(x$4,x$2) in -- #16 -//│ let x$6 = +(x$5,x$3) in -- #15 -//│ x$6 -- #14 -//│ ) -//│ Def(1, bar, [c$0], -//│ 1, -//│ let x$7 = Pair(0,1) in -- #69 -//│ let* (x$8) = foo(x$7,c$0) in -- #68 -//│ let x$9 = Pair(2,3) in -- #67 -//│ let* (x$10) = foo(c$0,x$9) in -- #66 -//│ let x$11 = Pair(0,1) in -- #65 -//│ let x$12 = Pair(2,3) in -- #64 -//│ let* (x$13) = foo(x$11,x$12) in -- #63 -//│ x$13 -- #62 -//│ ) -//│ Def(2, baz, [], -//│ 1, -//│ let x$14 = Pair(4,5) in -- #81 -//│ let* (x$15) = bar(x$14) in -- #80 -//│ x$15 -- #79 -//│ ) -//│ }, -//│ let* (x$16) = baz() in -- #85 -//│ x$16 -- #84) +//│ |#fun| |foo|(|a|,| |b|)| |#=|→|#let| |x1| |#=| |Pair|.x|(|a|)|↵|#let| |y1| |#=| |Pair|.y|(|a|)|↵|#let| |x2| |#=| |Pair|.x|(|b|)|↵|#let| |y2| |#=| |Pair|.y|(|b|)|↵|x1| |+| |y1| |+| |x2| |+| |y2|←|↵|#fun| |bar|(|c|)| |#=|→|foo|(|Pair|(|0|,| |1|)|,| |c|)|↵|foo|(|c|,| |Pair|(|2|,| |3|)|)|↵|foo|(|Pair|(|0|,| |1|)|,| |Pair|(|2|,| |3|)|)|←|↵|#fun| |baz|(||)| |#=|→|bar|(|Pair|(|4|,|5|)|)|←|↵|baz|(||)| +//│ Parsed: {fun foo = (a, b,) => {let x1 = (Pair).x(a,); let y1 = (Pair).y(a,); let x2 = (Pair).x(b,); let y2 = (Pair).y(b,); +(+(+(x1, y1,), x2,), y2,)}; fun bar = (c,) => {foo(Pair(0, 1,), c,); foo(c, Pair(2, 3,),); foo(Pair(0, 1,), Pair(2, 3,),)}; fun baz = () => {bar(Pair(4, 5,),)}; baz()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def foo(a$0,b$0) = +//│ let x$1 = Pair.x(a$0) in -- #41 +//│ let x$2 = Pair.y(a$0) in -- #40 +//│ let x$3 = Pair.x(b$0) in -- #39 +//│ let x$4 = Pair.y(b$0) in -- #38 +//│ let x$5 = +(x$1,x$2) in -- #37 +//│ let x$6 = +(x$5,x$3) in -- #36 +//│ let x$7 = +(x$6,x$4) in -- #35 +//│ x$7 -- #34 +//│ def bar(c$0) = +//│ let x$8 = Pair(0,1) in -- #86 +//│ let* (x$9) = foo(x$8,c$0) in -- #85 +//│ let x$10 = Pair(2,3) in -- #84 +//│ let* (x$11) = foo(c$0,x$10) in -- #83 +//│ let x$12 = Pair(0,1) in -- #82 +//│ let x$13 = Pair(2,3) in -- #81 +//│ let* (x$14) = foo(x$12,x$13) in -- #80 +//│ x$14 -- #79 +//│ def baz() = +//│ let x$15 = Pair(4,5) in -- #97 +//│ let* (x$16) = bar(x$15) in -- #96 +//│ x$16 -- #95 +//│ let* (x$0) = baz() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: //│ 6 +//│ :interpIR -class Pair(x, y) fun foo() = let p = Pair(0, 1) - let b = p.x + let b = Pair.x(p) b foo() -//│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |foo|(||)| |#=|→|#let| |p| |#=| |Pair|(|0|,| |1|)|↵|#let| |b| |#=| |p|.x|↵|b|←|↵|foo|(||)| -//│ Parsed: {class Pair(x, y,) {}; fun foo = () => {let p = Pair(0, 1,); let b = (p).x; b}; foo()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { -//│ Def(0, foo, [], -//│ 1, -//│ let x$0 = Pair(0,1) in -- #10 -//│ let x$1 = x$0.x in -- #9 -//│ x$1 -- #8 -//│ ) -//│ }, -//│ let* (x$2) = foo() in -- #14 -//│ x$2 -- #13) +//│ |#fun| |foo|(||)| |#=|→|#let| |p| |#=| |Pair|(|0|,| |1|)|↵|#let| |b| |#=| |Pair|.x|(|p|)|↵|b|←|↵|foo|(||)| +//│ Parsed: {fun foo = () => {let p = Pair(0, 1,); let b = (Pair).x(p,); b}; foo()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def foo() = +//│ let x$1 = Pair(0,1) in -- #15 +//│ let x$2 = Pair.x(x$1) in -- #14 +//│ x$2 -- #13 +//│ let* (x$0) = foo() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: //│ 0 +//│ :interpIR -class S(s) -class O fun foo() = bar(S(O)) fun bar(x) = @@ -445,39 +392,33 @@ fun baz(x) = S(s) then s O then x foo() -//│ |#class| |S|(|s|)|↵|#class| |O|↵|#fun| |foo|(||)| |#=|→|bar|(|S|(|O|)|)|←|↵|#fun| |bar|(|x|)| |#=|→|baz|(|x|)|←|↵|#fun| |baz|(|x|)| |#=|→|#if| |x| |is|→|S|(|s|)| |#then| |s|↵|O| |#then| |x|←|←|↵|foo|(||)| -//│ Parsed: {class S(s,) {}; class O {}; fun foo = () => {bar(S(O,),)}; fun bar = (x,) => {baz(x,)}; fun baz = (x,) => {if x is ‹(S(s,)) then s; (O) then x›}; foo()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { -//│ Def(0, foo, [], -//│ 1, -//│ let x$0 = O() in -- #10 -//│ let x$1 = S(x$0) in -- #9 -//│ let* (x$2) = bar(x$1) in -- #8 -//│ x$2 -- #7 -//│ ) -//│ Def(1, bar, [x$3], -//│ 1, -//│ let* (x$4) = baz(x$3) in -- #16 -//│ x$4 -- #15 -//│ ) -//│ Def(2, baz, [x$5], -//│ 1, -//│ case x$5 of -- #26 -//│ S => -//│ let x$7 = x$5.s in -- #23 -//│ jump j$0(x$7) -- #22 -//│ O => -//│ jump j$0(x$5) -- #25 -//│ ) -//│ Def(3, j$0, [x$6], -//│ 1, -//│ x$6 -- #18 -//│ ) -//│ }, -//│ let* (x$8) = foo() in -- #30 -//│ x$8 -- #29) +//│ |#fun| |foo|(||)| |#=|→|bar|(|S|(|O|)|)|←|↵|#fun| |bar|(|x|)| |#=|→|baz|(|x|)|←|↵|#fun| |baz|(|x|)| |#=|→|#if| |x| |is|→|S|(|s|)| |#then| |s|↵|O| |#then| |x|←|←|↵|foo|(||)| +//│ Parsed: {fun foo = () => {bar(S(O,),)}; fun bar = (x,) => {baz(x,)}; fun baz = (x,) => {if x is ‹(S(s,)) then s; (O) then x›}; foo()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def foo() = +//│ let x$1 = O() in -- #12 +//│ let x$2 = S(x$1) in -- #11 +//│ let* (x$3) = bar(x$2) in -- #10 +//│ x$3 -- #9 +//│ def bar(x$4) = +//│ let* (x$5) = baz(x$4) in -- #17 +//│ x$5 -- #16 +//│ def baz(x$6) = +//│ case x$6 of -- #29 +//│ S => +//│ let x$8 = S.s(x$6) in -- #26 +//│ jump j$0(x$8) -- #25 +//│ O => +//│ jump j$0(x$6) -- #28 +//│ def j$0(x$7) = +//│ x$7 -- #19 +//│ let* (x$0) = foo() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: //│ O() +//│ diff --git a/compiler/shared/test/diff-ir/IRComplex.mls b/compiler/shared/test/diff-ir/IRComplex.mls index e6df8a88..5e270c3d 100644 --- a/compiler/shared/test/diff-ir/IRComplex.mls +++ b/compiler/shared/test/diff-ir/IRComplex.mls @@ -1,8 +1,38 @@ -:NewParser +:NewDefs :ParseOnly :UseIR :NoTailRec +:prelude +module True +module False +module Callable { + fun apply0() = 0 + fun apply1(x0) = 0 + fun apply2(x0,x1) = 0 + fun apply3(x0,x1,x2) = 0 + fun apply4(x0,x1,x2,x3) = 0 + fun apply5(x0,x1,x2,x3,x4) = 0 +} +module List[A, B] +class Cons[A, B](h: A, t: Cons[A, B]) extends List[A, B] +module Nil[A, B] extends List[A, B] +module Option[A] +class Some[A](x: A) extends Option[A] +module None[A] extends Option[A] +class Pair[A, B](x: A, y: B) +class Tuple2[A, B](x: A, y: B) +class Tuple3[A, B, C](x: A, y: B, z: C) +module Nat +class S(s: Nat) extends Nat +module O extends Nat +class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O) +//│ |#module| |True|↵|#module| |False|↵|#module| |Callable| |{|→|#fun| |apply0|(||)| |#=| |0|↵|#fun| |apply1|(|x0|)| |#=| |0|↵|#fun| |apply2|(|x0|,|x1|)| |#=| |0|↵|#fun| |apply3|(|x0|,|x1|,|x2|)| |#=| |0|↵|#fun| |apply4|(|x0|,|x1|,|x2|,|x3|)| |#=| |0|↵|#fun| |apply5|(|x0|,|x1|,|x2|,|x3|,|x4|)| |#=| |0|←|↵|}|↵|#module| |List|[|A|,| |B|]|↵|#class| |Cons|[|A|,| |B|]|(|h|#:| |A|,| |t|#:| |Cons|[|A|,| |B|]|)| |#extends| |List|[|A|,| |B|]|↵|#module| |Nil|[|A|,| |B|]| |#extends| |List|[|A|,| |B|]|↵|#module| |Option|[|A|]|↵|#class| |Some|[|A|]|(|x|#:| |A|)| |#extends| |Option|[|A|]|↵|#module| |None|[|A|]| |#extends| |Option|[|A|]|↵|#class| |Pair|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple2|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple3|[|A|,| |B|,| |C|]|(|x|#:| |A|,| |y|#:| |B|,| |z|#:| |C|)|↵|#module| |Nat|↵|#class| |S|(|s|#:| |Nat|)| |#extends| |Nat|↵|#module| |O| |#extends| |Nat|↵|#class| |HiddenTheseEntities|(|_0|#:| |HiddenTheseEntities|,| |_1|#:| |True|,| |_2|#:| |False|,| |_3|#:| |Callable|,| |_4|#:| |List|,| |_5|#:| |Cons|,| |_6|#:| |Nil|,| |_7|#:| |Option|,| |_8|#:| |Some|,| |_9|#:| |None|,| |_10|#:| |Pair|,| |_11|#:| |Tuple2|,| |_12|#:| |Tuple3|,| |_13|#:| |Nat|,| |_14|#:| |S|,| |_15|#:| |O|)| +//│ Parsed: {module True {}; module False {}; module Callable {fun apply0 = () => 0; fun apply1 = (x0,) => 0; fun apply2 = (x0, x1,) => 0; fun apply3 = (x0, x1, x2,) => 0; fun apply4 = (x0, x1, x2, x3,) => 0; fun apply5 = (x0, x1, x2, x3, x4,) => 0}; module List‹A, B› {}; class Cons‹A, B›(h: A, t: Cons‹A, B›,): List‹A, B› {}; module Nil‹A, B›: List‹A, B› {}; module Option‹A› {}; class Some‹A›(x: A,): Option‹A› {}; module None‹A›: Option‹A› {}; class Pair‹A, B›(x: A, y: B,) {}; class Tuple2‹A, B›(x: A, y: B,) {}; class Tuple3‹A, B, C›(x: A, y: B, z: C,) {}; module Nat {}; class S(s: Nat,): Nat {}; module O: Nat {}; class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O,) {}} +//│ +//│ Preluded. +//│ + :interpIR class A(x, y, z) class B(m, n) @@ -20,59 +50,54 @@ fun bar() = complex_foo(B(9, 10)) bar() //│ |#class| |A|(|x|,| |y|,| |z|)|↵|#class| |B|(|m|,| |n|)|↵|#fun| |complex_foo|(|t|)| |#=|→|#let| |r| |#=| |#if| |t| |is|→|A|(|x|,| |y|,| |z|)| |#then| |x| |+| |y| |*| |z|↵|B|(|m|,| |n|)| |#then| |m| |-| |n|←|↵|#let| |s| |#=| |B|(|1|,| |2|)|↵|#let| |u| |#=| |#if| |s| |is|→|A|(|x|,| |y|,| |z|)| |#then| |3|↵|B|(|m|,| |n|)| |#then| |4|←|↵|r| |+| |u|←|↵|#fun| |bar|(||)| |#=|→|complex_foo|(|A|(|6|,| |7|,| |8|)|)|↵|complex_foo|(|B|(|9|,| |10|)|)|←|↵|bar|(||)| -//│ Parsed: {class A(x, y, z,) {}; class B(m, n,) {}; fun complex_foo = (t,) => {let r = if t is ‹(A(x, y, z,)) then +(x,)(*(y,)(z,),); (B(m, n,)) then -(m,)(n,)›; let s = B(1, 2,); let u = if s is ‹(A(x, y, z,)) then 3; (B(m, n,)) then 4›; +(r,)(u,)}; fun bar = () => {complex_foo(A(6, 7, 8,),); complex_foo(B(9, 10,),)}; bar()} +//│ Parsed: {class A(x, y, z,) {}; class B(m, n,) {}; fun complex_foo = (t,) => {let r = if t is ‹(A(x, y, z,)) then +(x, *(y, z,),); (B(m, n,)) then -(m, n,)›; let s = B(1, 2,); let u = if s is ‹(A(x, y, z,)) then 3; (B(m, n,)) then 4›; +(r, u,)}; fun bar = () => {complex_foo(A(6, 7, 8,),); complex_foo(B(9, 10,),)}; bar()} //│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [x,y,z]),ClassInfo(3, B, [m,n])}, { -//│ Def(0, complex_foo, [t$0], -//│ 1, -//│ case t$0 of -- #63 -//│ A => -//│ let x$9 = t$0.z in -- #51 -//│ let x$10 = t$0.y in -- #50 -//│ let x$11 = t$0.x in -- #49 -//│ let x$12 = *(x$10,x$9) in -- #48 -//│ let x$13 = +(x$11,x$12) in -- #47 -//│ jump j$0(x$13) -- #46 -//│ B => -//│ let x$14 = t$0.n in -- #62 -//│ let x$15 = t$0.m in -- #61 -//│ let x$16 = -(x$15,x$14) in -- #60 -//│ jump j$0(x$16) -- #59 -//│ ) -//│ Def(1, j$1, [x$2,x$0], -//│ 1, -//│ let x$3 = +(x$0,x$2) in -- #13 -//│ x$3 -- #12 -//│ ) -//│ Def(2, j$0, [x$0], -//│ 1, -//│ let x$1 = B(1,2) in -- #34 -//│ case x$1 of -- #33 -//│ A => -//│ let x$4 = x$1.z in -- #24 -//│ let x$5 = x$1.y in -- #23 -//│ let x$6 = x$1.x in -- #22 -//│ jump j$1(3,x$0) -- #21 -//│ B => -//│ let x$7 = x$1.n in -- #32 -//│ let x$8 = x$1.m in -- #31 -//│ jump j$1(4,x$0) -- #30 -//│ ) -//│ Def(3, bar, [], -//│ 1, -//│ let x$17 = A(6,7,8) in -- #89 -//│ let* (x$18) = complex_foo(x$17) in -- #88 -//│ let x$19 = B(9,10) in -- #87 -//│ let* (x$20) = complex_foo(x$19) in -- #86 -//│ x$20 -- #85 -//│ ) -//│ }, -//│ let* (x$21) = bar() in -- #93 -//│ x$21 -- #92) +//│ +//│ IR: +//│ Program: +//│ class A(x,y,z) +//│ class B(m,n) +//│ def complex_foo(t$0) = +//│ case t$0 of -- #98 +//│ A => +//│ let x$10 = A.z(t$0) in -- #79 +//│ let x$11 = A.y(t$0) in -- #78 +//│ let x$12 = A.x(t$0) in -- #77 +//│ let x$13 = *(x$11,x$10) in -- #76 +//│ let x$14 = +(x$12,x$13) in -- #75 +//│ jump j$0(x$14) -- #74 +//│ B => +//│ let x$15 = B.n(t$0) in -- #97 +//│ let x$16 = B.m(t$0) in -- #96 +//│ let x$17 = -(x$16,x$15) in -- #95 +//│ jump j$0(x$17) -- #94 +//│ def j$1(x$3,x$1) = +//│ let x$4 = +(x$1,x$3) in -- #19 +//│ x$4 -- #18 +//│ def j$0(x$1) = +//│ let x$2 = B(1,2) in -- #50 +//│ case x$2 of -- #49 +//│ A => +//│ let x$5 = A.z(x$2) in -- #36 +//│ let x$6 = A.y(x$2) in -- #35 +//│ let x$7 = A.x(x$2) in -- #34 +//│ jump j$1(3,x$1) -- #33 +//│ B => +//│ let x$8 = B.n(x$2) in -- #48 +//│ let x$9 = B.m(x$2) in -- #47 +//│ jump j$1(4,x$1) -- #46 +//│ def bar() = +//│ let x$18 = A(6,7,8) in -- #122 +//│ let* (x$19) = complex_foo(x$18) in -- #121 +//│ let x$20 = B(9,10) in -- #120 +//│ let* (x$21) = complex_foo(x$20) in -- #119 +//│ x$21 -- #118 +//│ let* (x$0) = bar() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: //│ 3 +//│ :interpIR class A(w, x) @@ -108,107 +133,97 @@ fun bar() = complex_foo(A(10, A(9, B(10)))) bar() //│ |#class| |A|(|w|,| |x|)|↵|#class| |B|(|y|)|↵|#class| |C|(|z|)|↵|#fun| |complex_foo|(|t|)| |#=|→|#let| |a| |#=| |1| |+| |2|↵|#let| |b| |#=| |1| |*| |2|↵|#let| |x| |#=| |#if| |t| |is|→|A|(|x|,| |y|)| |#then| |y|↵|B|(|x|)| |#then| |B|(|x| |+| |b|)|↵|C|(|x|)| |#then| |C|(|0|)|←|↵|#let| |z| |#=| |A|(|5|,| |x|)|↵|#let| |v| |#=| |B|(|6|)|↵|#let| |y| |#=| |#if| |x| |is|→|A|(|x|,| |y|)| |#then|→|#let| |m| |#=| |x| |+| |a| |+| |b|↵|#if| |y| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |m|↵|C|(|x|)| |#then| |0|←|←|↵|B|(|x|)| |#then| |2|↵|C|(|x|)| |#then| |3|←|↵|#if| |z| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |4|↵|C|(|x|)| |#then|→|#if| |v| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |7|↵|C|(|x|)| |#then| |8|←|←|←|←|↵|#fun| |bar|(||)| |#=|→|complex_foo|(|A|(|10|,| |A|(|9|,| |B|(|10|)|)|)|)|←|↵|bar|(||)| -//│ Parsed: {class A(w, x,) {}; class B(y,) {}; class C(z,) {}; fun complex_foo = (t,) => {let a = +(1,)(2,); let b = *(1,)(2,); let x = if t is ‹(A(x, y,)) then y; (B(x,)) then B(+(x,)(b,),); (C(x,)) then C(0,)›; let z = A(5, x,); let v = B(6,); let y = if x is ‹(A(x, y,)) then {let m = +(+(x,)(a,),)(b,); if y is ‹(A(x, y,)) then x; (B(x,)) then m; (C(x,)) then 0›}; (B(x,)) then 2; (C(x,)) then 3›; if z is ‹(A(x, y,)) then x; (B(x,)) then 4; (C(x,)) then {if v is ‹(A(x, y,)) then x; (B(x,)) then 7; (C(x,)) then 8›}›}; fun bar = () => {complex_foo(A(10, A(9, B(10,),),),)}; bar()} +//│ Parsed: {class A(w, x,) {}; class B(y,) {}; class C(z,) {}; fun complex_foo = (t,) => {let a = +(1, 2,); let b = *(1, 2,); let x = if t is ‹(A(x, y,)) then y; (B(x,)) then B(+(x, b,),); (C(x,)) then C(0,)›; let z = A(5, x,); let v = B(6,); let y = if x is ‹(A(x, y,)) then {let m = +(+(x, a,), b,); if y is ‹(A(x, y,)) then x; (B(x,)) then m; (C(x,)) then 0›}; (B(x,)) then 2; (C(x,)) then 3›; if z is ‹(A(x, y,)) then x; (B(x,)) then 4; (C(x,)) then {if v is ‹(A(x, y,)) then x; (B(x,)) then 7; (C(x,)) then 8›}›}; fun bar = () => {complex_foo(A(10, A(9, B(10,),),),)}; bar()} +//│ //│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [w,x]),ClassInfo(3, B, [y]),ClassInfo(4, C, [z])}, { -//│ Def(0, complex_foo, [t$0], -//│ 1, -//│ let x$0 = +(1,2) in -- #140 -//│ let x$1 = *(1,2) in -- #139 -//│ case t$0 of -- #138 -//│ A => -//│ let x$27 = t$0.x in -- #116 -//│ let x$28 = t$0.w in -- #115 -//│ jump j$0(x$27,x$0,x$1) -- #114 -//│ B => -//│ let x$29 = t$0.y in -- #128 -//│ let x$30 = +(x$29,x$1) in -- #127 -//│ let x$31 = B(x$30) in -- #126 -//│ jump j$0(x$31,x$0,x$1) -- #125 -//│ C => -//│ let x$32 = t$0.z in -- #137 -//│ let x$33 = C(0) in -- #136 -//│ jump j$0(x$33,x$0,x$1) -- #135 -//│ ) -//│ Def(1, j$2, [x$6], -//│ 1, -//│ x$6 -- #21 -//│ ) -//│ Def(2, j$3, [x$11], -//│ 1, -//│ jump j$2(x$11) -- #39 -//│ ) -//│ Def(3, j$1, [x$5,x$3,x$4], -//│ 1, -//│ case x$3 of -- #60 -//│ A => -//│ let x$7 = x$3.x in -- #29 -//│ let x$8 = x$3.w in -- #28 -//│ jump j$2(x$8) -- #27 -//│ B => -//│ let x$9 = x$3.y in -- #34 -//│ jump j$2(4) -- #33 -//│ C => -//│ let x$10 = x$3.z in -- #59 -//│ case x$4 of -- #58 +//│ IR: +//│ Program: +//│ class A(w,x) +//│ class B(y) +//│ class C(z) +//│ def complex_foo(t$0) = +//│ let x$1 = +(1,2) in -- #198 +//│ let x$2 = *(1,2) in -- #197 +//│ case t$0 of -- #196 //│ A => -//│ let x$12 = x$4.x in -- #47 -//│ let x$13 = x$4.w in -- #46 -//│ jump j$3(x$13) -- #45 +//│ let x$28 = A.x(t$0) in -- #167 +//│ let x$29 = A.w(t$0) in -- #166 +//│ jump j$0(x$28,x$1,x$2) -- #165 //│ B => -//│ let x$14 = x$4.y in -- #52 -//│ jump j$3(7) -- #51 +//│ let x$30 = B.y(t$0) in -- #184 +//│ let x$31 = +(x$30,x$2) in -- #183 +//│ let x$32 = B(x$31) in -- #182 +//│ jump j$0(x$32,x$1,x$2) -- #181 //│ C => -//│ let x$15 = x$4.z in -- #57 -//│ jump j$3(8) -- #56 -//│ ) -//│ Def(4, j$4, [x$20,x$3,x$4], -//│ 1, -//│ jump j$1(x$20,x$3,x$4) -- #72 -//│ ) -//│ Def(5, j$0, [x$2,x$0,x$1], -//│ 1, -//│ let x$3 = A(5,x$2) in -- #108 -//│ let x$4 = B(6) in -- #107 -//│ case x$2 of -- #106 -//│ A => -//│ let x$16 = x$2.x in -- #95 -//│ let x$17 = x$2.w in -- #94 -//│ let x$18 = +(x$17,x$0) in -- #93 -//│ let x$19 = +(x$18,x$1) in -- #92 -//│ case x$16 of -- #91 +//│ let x$33 = C.z(t$0) in -- #195 +//│ let x$34 = C(0) in -- #194 +//│ jump j$0(x$34,x$1,x$2) -- #193 +//│ def j$2(x$7) = +//│ x$7 -- #30 +//│ def j$3(x$12) = +//│ jump j$2(x$12) -- #56 +//│ def j$1(x$6,x$4,x$5) = +//│ case x$4 of -- #85 //│ A => -//│ let x$21 = x$16.x in -- #80 -//│ let x$22 = x$16.w in -- #79 -//│ jump j$4(x$22,x$3,x$4) -- #78 +//│ let x$8 = A.x(x$4) in -- #42 +//│ let x$9 = A.w(x$4) in -- #41 +//│ jump j$2(x$9) -- #40 //│ B => -//│ let x$23 = x$16.y in -- #85 -//│ jump j$4(x$19,x$3,x$4) -- #84 +//│ let x$10 = B.y(x$4) in -- #49 +//│ jump j$2(4) -- #48 //│ C => -//│ let x$24 = x$16.z in -- #90 -//│ jump j$4(0,x$3,x$4) -- #89 -//│ B => -//│ let x$25 = x$2.y in -- #100 -//│ jump j$1(2,x$3,x$4) -- #99 -//│ C => -//│ let x$26 = x$2.z in -- #105 -//│ jump j$1(3,x$3,x$4) -- #104 -//│ ) -//│ Def(6, bar, [], -//│ 1, -//│ let x$34 = B(10) in -- #162 -//│ let x$35 = A(9,x$34) in -- #161 -//│ let x$36 = A(10,x$35) in -- #160 -//│ let* (x$37) = complex_foo(x$36) in -- #159 -//│ x$37 -- #158 -//│ ) -//│ }, -//│ let* (x$38) = bar() in -- #166 -//│ x$38 -- #165) +//│ let x$11 = C.z(x$4) in -- #84 +//│ case x$5 of -- #83 +//│ A => +//│ let x$13 = A.x(x$5) in -- #68 +//│ let x$14 = A.w(x$5) in -- #67 +//│ jump j$3(x$14) -- #66 +//│ B => +//│ let x$15 = B.y(x$5) in -- #75 +//│ jump j$3(7) -- #74 +//│ C => +//│ let x$16 = C.z(x$5) in -- #82 +//│ jump j$3(8) -- #81 +//│ def j$4(x$21,x$4,x$5) = +//│ jump j$1(x$21,x$4,x$5) -- #107 +//│ def j$0(x$3,x$1,x$2) = +//│ let x$4 = A(5,x$3) in -- #155 +//│ let x$5 = B(6) in -- #154 +//│ case x$3 of -- #153 +//│ A => +//│ let x$17 = A.x(x$3) in -- #138 +//│ let x$18 = A.w(x$3) in -- #137 +//│ let x$19 = +(x$18,x$1) in -- #136 +//│ let x$20 = +(x$19,x$2) in -- #135 +//│ case x$17 of -- #134 +//│ A => +//│ let x$22 = A.x(x$17) in -- #119 +//│ let x$23 = A.w(x$17) in -- #118 +//│ jump j$4(x$23,x$4,x$5) -- #117 +//│ B => +//│ let x$24 = B.y(x$17) in -- #126 +//│ jump j$4(x$20,x$4,x$5) -- #125 +//│ C => +//│ let x$25 = C.z(x$17) in -- #133 +//│ jump j$4(0,x$4,x$5) -- #132 +//│ B => +//│ let x$26 = B.y(x$3) in -- #145 +//│ jump j$1(2,x$4,x$5) -- #144 +//│ C => +//│ let x$27 = C.z(x$3) in -- #152 +//│ jump j$1(3,x$4,x$5) -- #151 +//│ def bar() = +//│ let x$35 = B(10) in -- #219 +//│ let x$36 = A(9,x$35) in -- #218 +//│ let x$37 = A(10,x$36) in -- #217 +//│ let* (x$38) = complex_foo(x$37) in -- #216 +//│ x$38 -- #215 +//│ let* (x$0) = bar() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: //│ 5 +//│ :interpIR class A(w, x) @@ -244,106 +259,96 @@ fun bar() = complex_foo(A(10, A(9, B(10)))) bar() //│ |#class| |A|(|w|,| |x|)|↵|#class| |B|(|y|)|↵|#class| |C|(|z|)|↵|#fun| |complex_foo|(|t|)| |#=|→|#let| |a| |#=| |1| |+| |2|↵|#let| |b| |#=| |1| |*| |2|↵|#let| |x| |#=| |#if| |t| |is|→|A|(|x|,| |y|)| |#then| |A|(|x|,| |C|(|0|)|)|↵|B|(|x|)| |#then| |B|(|x| |+| |b|)|↵|C|(|x|)| |#then| |C|(|0|)|←|↵|#let| |z| |#=| |A|(|5|,| |x|)|↵|#let| |v| |#=| |B|(|6|)|↵|#let| |y| |#=| |#if| |x| |is|→|A|(|x|,| |y|)| |#then|→|#let| |m| |#=| |x| |+| |a| |+| |b|↵|#if| |y| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |m|↵|C|(|x|)| |#then| |0|←|←|↵|B|(|x|)| |#then| |2|↵|C|(|x|)| |#then| |3|←|↵|#if| |z| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |4|↵|C|(|x|)| |#then|→|#if| |v| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |7|↵|C|(|x|)| |#then| |8|←|←|←|←|↵|#fun| |bar|(||)| |#=|→|complex_foo|(|A|(|10|,| |A|(|9|,| |B|(|10|)|)|)|)|←|↵|bar|(||)| -//│ Parsed: {class A(w, x,) {}; class B(y,) {}; class C(z,) {}; fun complex_foo = (t,) => {let a = +(1,)(2,); let b = *(1,)(2,); let x = if t is ‹(A(x, y,)) then A(x, C(0,),); (B(x,)) then B(+(x,)(b,),); (C(x,)) then C(0,)›; let z = A(5, x,); let v = B(6,); let y = if x is ‹(A(x, y,)) then {let m = +(+(x,)(a,),)(b,); if y is ‹(A(x, y,)) then x; (B(x,)) then m; (C(x,)) then 0›}; (B(x,)) then 2; (C(x,)) then 3›; if z is ‹(A(x, y,)) then x; (B(x,)) then 4; (C(x,)) then {if v is ‹(A(x, y,)) then x; (B(x,)) then 7; (C(x,)) then 8›}›}; fun bar = () => {complex_foo(A(10, A(9, B(10,),),),)}; bar()} +//│ Parsed: {class A(w, x,) {}; class B(y,) {}; class C(z,) {}; fun complex_foo = (t,) => {let a = +(1, 2,); let b = *(1, 2,); let x = if t is ‹(A(x, y,)) then A(x, C(0,),); (B(x,)) then B(+(x, b,),); (C(x,)) then C(0,)›; let z = A(5, x,); let v = B(6,); let y = if x is ‹(A(x, y,)) then {let m = +(+(x, a,), b,); if y is ‹(A(x, y,)) then x; (B(x,)) then m; (C(x,)) then 0›}; (B(x,)) then 2; (C(x,)) then 3›; if z is ‹(A(x, y,)) then x; (B(x,)) then 4; (C(x,)) then {if v is ‹(A(x, y,)) then x; (B(x,)) then 7; (C(x,)) then 8›}›}; fun bar = () => {complex_foo(A(10, A(9, B(10,),),),)}; bar()} +//│ //│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [w,x]),ClassInfo(3, B, [y]),ClassInfo(4, C, [z])}, { -//│ Def(0, complex_foo, [t$0], -//│ 1, -//│ let x$0 = +(1,2) in -- #150 -//│ let x$1 = *(1,2) in -- #149 -//│ case t$0 of -- #148 -//│ A => -//│ let x$27 = t$0.x in -- #126 -//│ let x$28 = t$0.w in -- #125 -//│ let x$29 = C(0) in -- #124 -//│ let x$30 = A(x$28,x$29) in -- #123 -//│ jump j$0(x$30,x$0,x$1) -- #122 -//│ B => -//│ let x$31 = t$0.y in -- #138 -//│ let x$32 = +(x$31,x$1) in -- #137 -//│ let x$33 = B(x$32) in -- #136 -//│ jump j$0(x$33,x$0,x$1) -- #135 -//│ C => -//│ let x$34 = t$0.z in -- #147 -//│ let x$35 = C(0) in -- #146 -//│ jump j$0(x$35,x$0,x$1) -- #145 -//│ ) -//│ Def(1, j$2, [x$6], -//│ 1, -//│ x$6 -- #21 -//│ ) -//│ Def(2, j$3, [x$11], -//│ 1, -//│ jump j$2(x$11) -- #39 -//│ ) -//│ Def(3, j$1, [x$5,x$3,x$4], -//│ 1, -//│ case x$3 of -- #60 -//│ A => -//│ let x$7 = x$3.x in -- #29 -//│ let x$8 = x$3.w in -- #28 -//│ jump j$2(x$8) -- #27 -//│ B => -//│ let x$9 = x$3.y in -- #34 -//│ jump j$2(4) -- #33 -//│ C => -//│ let x$10 = x$3.z in -- #59 -//│ case x$4 of -- #58 +//│ IR: +//│ Program: +//│ class A(w,x) +//│ class B(y) +//│ class C(z) +//│ def complex_foo(t$0) = +//│ let x$1 = +(1,2) in -- #208 +//│ let x$2 = *(1,2) in -- #207 +//│ case t$0 of -- #206 //│ A => -//│ let x$12 = x$4.x in -- #47 -//│ let x$13 = x$4.w in -- #46 -//│ jump j$3(x$13) -- #45 +//│ let x$28 = A.x(t$0) in -- #177 +//│ let x$29 = A.w(t$0) in -- #176 +//│ let x$30 = C(0) in -- #175 +//│ let x$31 = A(x$29,x$30) in -- #174 +//│ jump j$0(x$31,x$1,x$2) -- #173 //│ B => -//│ let x$14 = x$4.y in -- #52 -//│ jump j$3(7) -- #51 +//│ let x$32 = B.y(t$0) in -- #194 +//│ let x$33 = +(x$32,x$2) in -- #193 +//│ let x$34 = B(x$33) in -- #192 +//│ jump j$0(x$34,x$1,x$2) -- #191 //│ C => -//│ let x$15 = x$4.z in -- #57 -//│ jump j$3(8) -- #56 -//│ ) -//│ Def(4, j$4, [x$20,x$3,x$4], -//│ 1, -//│ jump j$1(x$20,x$3,x$4) -- #72 -//│ ) -//│ Def(5, j$0, [x$2,x$0,x$1], -//│ 1, -//│ let x$3 = A(5,x$2) in -- #108 -//│ let x$4 = B(6) in -- #107 -//│ case x$2 of -- #106 -//│ A => -//│ let x$16 = x$2.x in -- #95 -//│ let x$17 = x$2.w in -- #94 -//│ let x$18 = +(x$17,x$0) in -- #93 -//│ let x$19 = +(x$18,x$1) in -- #92 -//│ case x$16 of -- #91 +//│ let x$35 = C.z(t$0) in -- #205 +//│ let x$36 = C(0) in -- #204 +//│ jump j$0(x$36,x$1,x$2) -- #203 +//│ def j$2(x$7) = +//│ x$7 -- #30 +//│ def j$3(x$12) = +//│ jump j$2(x$12) -- #56 +//│ def j$1(x$6,x$4,x$5) = +//│ case x$4 of -- #85 //│ A => -//│ let x$21 = x$16.x in -- #80 -//│ let x$22 = x$16.w in -- #79 -//│ jump j$4(x$22,x$3,x$4) -- #78 +//│ let x$8 = A.x(x$4) in -- #42 +//│ let x$9 = A.w(x$4) in -- #41 +//│ jump j$2(x$9) -- #40 //│ B => -//│ let x$23 = x$16.y in -- #85 -//│ jump j$4(x$19,x$3,x$4) -- #84 +//│ let x$10 = B.y(x$4) in -- #49 +//│ jump j$2(4) -- #48 //│ C => -//│ let x$24 = x$16.z in -- #90 -//│ jump j$4(0,x$3,x$4) -- #89 -//│ B => -//│ let x$25 = x$2.y in -- #100 -//│ jump j$1(2,x$3,x$4) -- #99 -//│ C => -//│ let x$26 = x$2.z in -- #105 -//│ jump j$1(3,x$3,x$4) -- #104 -//│ ) -//│ Def(6, bar, [], -//│ 1, -//│ let x$36 = B(10) in -- #172 -//│ let x$37 = A(9,x$36) in -- #171 -//│ let x$38 = A(10,x$37) in -- #170 -//│ let* (x$39) = complex_foo(x$38) in -- #169 -//│ x$39 -- #168 -//│ ) -//│ }, -//│ let* (x$40) = bar() in -- #176 -//│ x$40 -- #175) +//│ let x$11 = C.z(x$4) in -- #84 +//│ case x$5 of -- #83 +//│ A => +//│ let x$13 = A.x(x$5) in -- #68 +//│ let x$14 = A.w(x$5) in -- #67 +//│ jump j$3(x$14) -- #66 +//│ B => +//│ let x$15 = B.y(x$5) in -- #75 +//│ jump j$3(7) -- #74 +//│ C => +//│ let x$16 = C.z(x$5) in -- #82 +//│ jump j$3(8) -- #81 +//│ def j$4(x$21,x$4,x$5) = +//│ jump j$1(x$21,x$4,x$5) -- #107 +//│ def j$0(x$3,x$1,x$2) = +//│ let x$4 = A(5,x$3) in -- #155 +//│ let x$5 = B(6) in -- #154 +//│ case x$3 of -- #153 +//│ A => +//│ let x$17 = A.x(x$3) in -- #138 +//│ let x$18 = A.w(x$3) in -- #137 +//│ let x$19 = +(x$18,x$1) in -- #136 +//│ let x$20 = +(x$19,x$2) in -- #135 +//│ case x$17 of -- #134 +//│ A => +//│ let x$22 = A.x(x$17) in -- #119 +//│ let x$23 = A.w(x$17) in -- #118 +//│ jump j$4(x$23,x$4,x$5) -- #117 +//│ B => +//│ let x$24 = B.y(x$17) in -- #126 +//│ jump j$4(x$20,x$4,x$5) -- #125 +//│ C => +//│ let x$25 = C.z(x$17) in -- #133 +//│ jump j$4(0,x$4,x$5) -- #132 +//│ B => +//│ let x$26 = B.y(x$3) in -- #145 +//│ jump j$1(2,x$4,x$5) -- #144 +//│ C => +//│ let x$27 = C.z(x$3) in -- #152 +//│ jump j$1(3,x$4,x$5) -- #151 +//│ def bar() = +//│ let x$37 = B(10) in -- #229 +//│ let x$38 = A(9,x$37) in -- #228 +//│ let x$39 = A(10,x$38) in -- #227 +//│ let* (x$40) = complex_foo(x$39) in -- #226 +//│ x$40 -- #225 +//│ let* (x$0) = bar() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: //│ 5 +//│ diff --git a/compiler/shared/test/diff-ir/IRRec.mls b/compiler/shared/test/diff-ir/IRRec.mls index a56f53a9..45aaa77e 100644 --- a/compiler/shared/test/diff-ir/IRRec.mls +++ b/compiler/shared/test/diff-ir/IRRec.mls @@ -1,164 +1,168 @@ -:NewParser +:NewDefs :ParseOnly :UseIR :NoTailRec +:prelude +module True +module False +module Callable { + fun apply0() = 0 + fun apply1(x0) = 0 + fun apply2(x0,x1) = 0 + fun apply3(x0,x1,x2) = 0 + fun apply4(x0,x1,x2,x3) = 0 + fun apply5(x0,x1,x2,x3,x4) = 0 +} +module List[A, B] +class Cons[A, B](h: A, t: Cons[A, B]) extends List[A, B] +module Nil[A, B] extends List[A, B] +module Option[A] +class Some[A](x: A) extends Option[A] +module None[A] extends Option[A] +class Pair[A, B](x: A, y: B) +class Tuple2[A, B](x: A, y: B) +class Tuple3[A, B, C](x: A, y: B, z: C) +module Nat +class S(s: Nat) extends Nat +module O extends Nat +class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O) +//│ |#module| |True|↵|#module| |False|↵|#module| |Callable| |{|→|#fun| |apply0|(||)| |#=| |0|↵|#fun| |apply1|(|x0|)| |#=| |0|↵|#fun| |apply2|(|x0|,|x1|)| |#=| |0|↵|#fun| |apply3|(|x0|,|x1|,|x2|)| |#=| |0|↵|#fun| |apply4|(|x0|,|x1|,|x2|,|x3|)| |#=| |0|↵|#fun| |apply5|(|x0|,|x1|,|x2|,|x3|,|x4|)| |#=| |0|←|↵|}|↵|#module| |List|[|A|,| |B|]|↵|#class| |Cons|[|A|,| |B|]|(|h|#:| |A|,| |t|#:| |Cons|[|A|,| |B|]|)| |#extends| |List|[|A|,| |B|]|↵|#module| |Nil|[|A|,| |B|]| |#extends| |List|[|A|,| |B|]|↵|#module| |Option|[|A|]|↵|#class| |Some|[|A|]|(|x|#:| |A|)| |#extends| |Option|[|A|]|↵|#module| |None|[|A|]| |#extends| |Option|[|A|]|↵|#class| |Pair|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple2|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple3|[|A|,| |B|,| |C|]|(|x|#:| |A|,| |y|#:| |B|,| |z|#:| |C|)|↵|#module| |Nat|↵|#class| |S|(|s|#:| |Nat|)| |#extends| |Nat|↵|#module| |O| |#extends| |Nat|↵|#class| |HiddenTheseEntities|(|_0|#:| |HiddenTheseEntities|,| |_1|#:| |True|,| |_2|#:| |False|,| |_3|#:| |Callable|,| |_4|#:| |List|,| |_5|#:| |Cons|,| |_6|#:| |Nil|,| |_7|#:| |Option|,| |_8|#:| |Some|,| |_9|#:| |None|,| |_10|#:| |Pair|,| |_11|#:| |Tuple2|,| |_12|#:| |Tuple3|,| |_13|#:| |Nat|,| |_14|#:| |S|,| |_15|#:| |O|)| +//│ Parsed: {module True {}; module False {}; module Callable {fun apply0 = () => 0; fun apply1 = (x0,) => 0; fun apply2 = (x0, x1,) => 0; fun apply3 = (x0, x1, x2,) => 0; fun apply4 = (x0, x1, x2, x3,) => 0; fun apply5 = (x0, x1, x2, x3, x4,) => 0}; module List‹A, B› {}; class Cons‹A, B›(h: A, t: Cons‹A, B›,): List‹A, B› {}; module Nil‹A, B›: List‹A, B› {}; module Option‹A› {}; class Some‹A›(x: A,): Option‹A› {}; module None‹A›: Option‹A› {}; class Pair‹A, B›(x: A, y: B,) {}; class Tuple2‹A, B›(x: A, y: B,) {}; class Tuple3‹A, B, C›(x: A, y: B, z: C,) {}; module Nat {}; class S(s: Nat,): Nat {}; module O: Nat {}; class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O,) {}} +//│ +//│ Preluded. +//│ + :interpIR -class True -class False +:genCpp fun fib(n) = if n < 2 then n else fib(n-1) + fib(n-2) fib(20) -//│ |#class| |True|↵|#class| |False|↵|#fun| |fib|(|n|)| |#=| |#if| |n| |<| |2| |#then| |n| |#else| |fib|(|n|-|1|)| |+| |fib|(|n|-|2|)|↵|fib|(|20|)| -//│ Parsed: {class True {}; class False {}; fun fib = (n,) => if (<(n,)(2,)) then n else +(fib(-(n,)(1,),),)(fib(-(n,)(2,),),); fib(20,)} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, [])}, { -//│ Def(0, fib, [n$0], -//│ 1, -//│ let x$0 = <(n$0,2) in -- #28 -//│ if x$0 -- #27 -//│ true => -//│ jump j$0(n$0) -- #5 -//│ false => -//│ let x$2 = -(n$0,1) in -- #26 -//│ let* (x$3) = fib(x$2) in -- #25 -//│ let x$4 = -(n$0,2) in -- #24 -//│ let* (x$5) = fib(x$4) in -- #23 -//│ let x$6 = +(x$3,x$5) in -- #22 -//│ jump j$0(x$6) -- #21 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ }, -//│ let* (x$7) = fib(20) in -- #34 -//│ x$7 -- #33) +//│ |#fun| |fib|(|n|)| |#=| |#if| |n| |<| |2| |#then| |n| |#else| |fib|(|n|-|1|)| |+| |fib|(|n|-|2|)|↵|fib|(|20|)| +//│ Parsed: {fun fib = (n,) => if (<(n, 2,)) then n else +(fib(-(n, 1,),), fib(-(n, 2,),),); fib(20,)} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def fib(n$0) = +//│ let x$1 = <(n$0,2) in -- #43 +//│ if x$1 -- #42 +//│ true => +//│ jump j$0(n$0) -- #13 +//│ false => +//│ let x$3 = -(n$0,1) in -- #41 +//│ let* (x$4) = fib(x$3) in -- #40 +//│ let x$5 = -(n$0,2) in -- #39 +//│ let* (x$6) = fib(x$5) in -- #38 +//│ let x$7 = +(x$4,x$6) in -- #37 +//│ jump j$0(x$7) -- #36 +//│ def j$0(x$2) = +//│ x$2 -- #11 +//│ let* (x$0) = fib(20) in -- #4 +//│ x$0 -- #3 //│ //│ Interpreted: //│ 6765 +//│ :interpIR -class True -class False +:genCpp fun odd(x) = if x == 0 then False else even(x-1) fun even(x) = if x == 0 then True else odd(x-1) fun foo() = odd(10) foo() -//│ |#class| |True|↵|#class| |False|↵|#fun| |odd|(|x|)| |#=| |#if| |x| |==| |0| |#then| |False| |#else| |even|(|x|-|1|)|↵|#fun| |even|(|x|)| |#=| |#if| |x| |==| |0| |#then| |True| |#else| |odd|(|x|-|1|)|↵|#fun| |foo|(||)| |#=| |odd|(|10|)|↵|foo|(||)| -//│ Parsed: {class True {}; class False {}; fun odd = (x,) => if (==(x,)(0,)) then False else even(-(x,)(1,),); fun even = (x,) => if (==(x,)(0,)) then True else odd(-(x,)(1,),); fun foo = () => odd(10,); foo()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, [])}, { -//│ Def(0, odd, [x$0], -//│ 1, -//│ let x$1 = ==(x$0,0) in -- #18 -//│ if x$1 -- #17 -//│ true => -//│ let x$3 = False() in -- #6 -//│ jump j$0(x$3) -- #5 -//│ false => -//│ let x$4 = -(x$0,1) in -- #16 -//│ let* (x$5) = even(x$4) in -- #15 -//│ jump j$0(x$5) -- #14 -//│ ) -//│ Def(1, j$0, [x$2], -//│ 1, -//│ x$2 -- #3 -//│ ) -//│ Def(2, even, [x$6], -//│ 1, -//│ let x$7 = ==(x$6,0) in -- #37 -//│ if x$7 -- #36 -//│ true => -//│ let x$9 = True() in -- #25 -//│ jump j$1(x$9) -- #24 -//│ false => -//│ let x$10 = -(x$6,1) in -- #35 -//│ let* (x$11) = odd(x$10) in -- #34 -//│ jump j$1(x$11) -- #33 -//│ ) -//│ Def(3, j$1, [x$8], -//│ 1, -//│ x$8 -- #22 -//│ ) -//│ Def(4, foo, [], -//│ 1, -//│ let* (x$12) = odd(10) in -- #43 -//│ x$12 -- #42 -//│ ) -//│ }, -//│ let* (x$13) = foo() in -- #47 -//│ x$13 -- #46) +//│ |#fun| |odd|(|x|)| |#=| |#if| |x| |==| |0| |#then| |False| |#else| |even|(|x|-|1|)|↵|#fun| |even|(|x|)| |#=| |#if| |x| |==| |0| |#then| |True| |#else| |odd|(|x|-|1|)|↵|#fun| |foo|(||)| |#=| |odd|(|10|)|↵|foo|(||)| +//│ Parsed: {fun odd = (x,) => if (==(x, 0,)) then False else even(-(x, 1,),); fun even = (x,) => if (==(x, 0,)) then True else odd(-(x, 1,),); fun foo = () => odd(10,); foo()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def odd(x$1) = +//│ let x$2 = ==(x$1,0) in -- #26 +//│ if x$2 -- #25 +//│ true => +//│ let x$4 = False() in -- #12 +//│ jump j$0(x$4) -- #11 +//│ false => +//│ let x$5 = -(x$1,1) in -- #24 +//│ let* (x$6) = even(x$5) in -- #23 +//│ jump j$0(x$6) -- #22 +//│ def j$0(x$3) = +//│ x$3 -- #9 +//│ def even(x$7) = +//│ let x$8 = ==(x$7,0) in -- #50 +//│ if x$8 -- #49 +//│ true => +//│ let x$10 = True() in -- #36 +//│ jump j$1(x$10) -- #35 +//│ false => +//│ let x$11 = -(x$7,1) in -- #48 +//│ let* (x$12) = odd(x$11) in -- #47 +//│ jump j$1(x$12) -- #46 +//│ def j$1(x$9) = +//│ x$9 -- #33 +//│ def foo() = +//│ let* (x$13) = odd(10) in -- #55 +//│ x$13 -- #54 +//│ let* (x$0) = foo() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: //│ False() +//│ :interpIR -class True -class False -class A -class B(b) +:genCpp fun not(x) = if x then False else True fun foo(x) = - if x then A - else B(foo(not(x))) + if x then None + else Some(foo(not(x))) fun main() = foo(False) main() -//│ |#class| |True|↵|#class| |False|↵|#class| |A|↵|#class| |B|(|b|)|↵|#fun| |not|(|x|)| |#=|→|#if| |x| |#then| |False| |#else| |True|←|↵|#fun| |foo|(|x|)| |#=|→|#if| |x| |#then| |A|→|#else| |B|(|foo|(|not|(|x|)|)|)|←|←|↵|#fun| |main|(||)| |#=| |foo|(|False|)|↵|main|(||)| -//│ Parsed: {class True {}; class False {}; class A {}; class B(b,) {}; fun not = (x,) => {if (x) then False else True}; fun foo = (x,) => {if (x) then A else B(foo(not(x,),),)}; fun main = () => foo(False,); main()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, A, []),ClassInfo(5, B, [b])}, { -//│ Def(0, not, [x$0], -//│ 1, -//│ if x$0 -- #8 -//│ true => -//│ let x$2 = False() in -- #4 -//│ jump j$0(x$2) -- #3 -//│ false => -//│ let x$3 = True() in -- #7 -//│ jump j$0(x$3) -- #6 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, foo, [x$4], -//│ 1, -//│ if x$4 -- #30 -//│ true => -//│ let x$6 = A() in -- #13 -//│ jump j$1(x$6) -- #12 -//│ false => -//│ let* (x$7) = not(x$4) in -- #29 -//│ let* (x$8) = foo(x$7) in -- #28 -//│ let x$9 = B(x$8) in -- #27 -//│ jump j$1(x$9) -- #26 -//│ ) -//│ Def(3, j$1, [x$5], -//│ 1, -//│ x$5 -- #10 -//│ ) -//│ Def(4, main, [], -//│ 1, -//│ let x$10 = False() in -- #37 -//│ let* (x$11) = foo(x$10) in -- #36 -//│ x$11 -- #35 -//│ ) -//│ }, -//│ let* (x$12) = main() in -- #41 -//│ x$12 -- #40) +//│ |#fun| |not|(|x|)| |#=|→|#if| |x| |#then| |False| |#else| |True|←|↵|#fun| |foo|(|x|)| |#=|→|#if| |x| |#then| |None|→|#else| |Some|(|foo|(|not|(|x|)|)|)|←|←|↵|#fun| |main|(||)| |#=| |foo|(|False|)|↵|main|(||)| +//│ Parsed: {fun not = (x,) => {if (x) then False else True}; fun foo = (x,) => {if (x) then None else Some(foo(not(x,),),)}; fun main = () => foo(False,); main()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def not(x$1) = +//│ if x$1 -- #11 +//│ true => +//│ let x$3 = False() in -- #7 +//│ jump j$0(x$3) -- #6 +//│ false => +//│ let x$4 = True() in -- #10 +//│ jump j$0(x$4) -- #9 +//│ def j$0(x$2) = +//│ x$2 -- #4 +//│ def foo(x$5) = +//│ if x$5 -- #31 +//│ true => +//│ let x$7 = None() in -- #16 +//│ jump j$1(x$7) -- #15 +//│ false => +//│ let* (x$8) = not(x$5) in -- #30 +//│ let* (x$9) = foo(x$8) in -- #29 +//│ let x$10 = Some(x$9) in -- #28 +//│ jump j$1(x$10) -- #27 +//│ def j$1(x$6) = +//│ x$6 -- #13 +//│ def main() = +//│ let x$11 = False() in -- #37 +//│ let* (x$12) = foo(x$11) in -- #36 +//│ x$12 -- #35 +//│ let* (x$0) = main() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: -//│ B(A()) +//│ Some(None()) +//│ :interpIR -class True -class False -class A() -class B(b) +:genCpp fun aaa() = let m = 1 let n = 2 @@ -171,97 +175,79 @@ fun bbb() = fun not(x) = if x then False else True fun foo(x) = - if x then A - else B(foo(not(x))) + if x then None + else Some(foo(not(x))) fun main() = let x = foo(False) if x is - A then aaa() - B(b1) then bbb() + None then aaa() + Some(b1) then bbb() main() -//│ |#class| |True|↵|#class| |False|↵|#class| |A|(||)|↵|#class| |B|(|b|)|↵|#fun| |aaa|(||)| |#=|→|#let| |m| |#=| |1|↵|#let| |n| |#=| |2|↵|#let| |p| |#=| |3|↵|#let| |q| |#=| |4|↵|m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |bbb|(||)| |#=|→|#let| |x| |#=| |aaa|(||)|↵|x| |*| |100| |+| |4|←|↵|#fun| |not|(|x|)| |#=|→|#if| |x| |#then| |False| |#else| |True|←|↵|#fun| |foo|(|x|)| |#=|→|#if| |x| |#then| |A|→|#else| |B|(|foo|(|not|(|x|)|)|)|←|←|↵|#fun| |main|(||)| |#=|→|#let| |x| |#=| |foo|(|False|)|↵|#if| |x| |is|→|A| |#then| |aaa|(||)|↵|B|(|b1|)| |#then| |bbb|(||)|←|←|↵|main|(||)| -//│ Parsed: {class True {}; class False {}; class A() {}; class B(b,) {}; fun aaa = () => {let m = 1; let n = 2; let p = 3; let q = 4; +(-(+(m,)(n,),)(p,),)(q,)}; fun bbb = () => {let x = aaa(); +(*(x,)(100,),)(4,)}; fun not = (x,) => {if (x) then False else True}; fun foo = (x,) => {if (x) then A else B(foo(not(x,),),)}; fun main = () => {let x = foo(False,); if x is ‹(A) then aaa(); (B(b1,)) then bbb()›}; main()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, A, []),ClassInfo(5, B, [b])}, { -//│ Def(0, aaa, [], -//│ 1, -//│ let x$0 = 1 in -- #17 -//│ let x$1 = 2 in -- #16 -//│ let x$2 = 3 in -- #15 -//│ let x$3 = 4 in -- #14 -//│ let x$4 = +(x$0,x$1) in -- #13 -//│ let x$5 = -(x$4,x$2) in -- #12 -//│ let x$6 = +(x$5,x$3) in -- #11 -//│ x$6 -- #10 -//│ ) -//│ Def(1, bbb, [], -//│ 1, -//│ let* (x$7) = aaa() in -- #28 -//│ let x$8 = *(x$7,100) in -- #27 -//│ let x$9 = +(x$8,4) in -- #26 -//│ x$9 -- #25 -//│ ) -//│ Def(2, not, [x$10], -//│ 1, -//│ if x$10 -- #37 -//│ true => -//│ let x$12 = False() in -- #33 -//│ jump j$0(x$12) -- #32 -//│ false => -//│ let x$13 = True() in -- #36 -//│ jump j$0(x$13) -- #35 -//│ ) -//│ Def(3, j$0, [x$11], -//│ 1, -//│ x$11 -- #30 -//│ ) -//│ Def(4, foo, [x$14], -//│ 1, -//│ if x$14 -- #59 -//│ true => -//│ let x$16 = A() in -- #42 -//│ jump j$1(x$16) -- #41 -//│ false => -//│ let* (x$17) = not(x$14) in -- #58 -//│ let* (x$18) = foo(x$17) in -- #57 -//│ let x$19 = B(x$18) in -- #56 -//│ jump j$1(x$19) -- #55 -//│ ) -//│ Def(5, j$1, [x$15], -//│ 1, -//│ x$15 -- #39 -//│ ) -//│ Def(6, main, [], -//│ 1, -//│ let x$20 = False() in -- #82 -//│ let* (x$21) = foo(x$20) in -- #81 -//│ case x$21 of -- #80 -//│ A => -//│ let* (x$23) = aaa() in -- #71 -//│ jump j$2(x$23) -- #70 -//│ B => -//│ let x$24 = x$21.b in -- #79 -//│ let* (x$25) = bbb() in -- #78 -//│ jump j$2(x$25) -- #77 -//│ ) -//│ Def(7, j$2, [x$22], -//│ 1, -//│ x$22 -- #66 -//│ ) -//│ }, -//│ let* (x$26) = main() in -- #86 -//│ x$26 -- #85) +//│ |#fun| |aaa|(||)| |#=|→|#let| |m| |#=| |1|↵|#let| |n| |#=| |2|↵|#let| |p| |#=| |3|↵|#let| |q| |#=| |4|↵|m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |bbb|(||)| |#=|→|#let| |x| |#=| |aaa|(||)|↵|x| |*| |100| |+| |4|←|↵|#fun| |not|(|x|)| |#=|→|#if| |x| |#then| |False| |#else| |True|←|↵|#fun| |foo|(|x|)| |#=|→|#if| |x| |#then| |None|→|#else| |Some|(|foo|(|not|(|x|)|)|)|←|←|↵|#fun| |main|(||)| |#=|→|#let| |x| |#=| |foo|(|False|)|↵|#if| |x| |is|→|None| |#then| |aaa|(||)|↵|Some|(|b1|)| |#then| |bbb|(||)|←|←|↵|main|(||)| +//│ Parsed: {fun aaa = () => {let m = 1; let n = 2; let p = 3; let q = 4; +(-(+(m, n,), p,), q,)}; fun bbb = () => {let x = aaa(); +(*(x, 100,), 4,)}; fun not = (x,) => {if (x) then False else True}; fun foo = (x,) => {if (x) then None else Some(foo(not(x,),),)}; fun main = () => {let x = foo(False,); if x is ‹(None) then aaa(); (Some(b1,)) then bbb()›}; main()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def aaa() = +//│ let x$1 = 1 in -- #29 +//│ let x$2 = 2 in -- #28 +//│ let x$3 = 3 in -- #27 +//│ let x$4 = 4 in -- #26 +//│ let x$5 = +(x$1,x$2) in -- #25 +//│ let x$6 = -(x$5,x$3) in -- #24 +//│ let x$7 = +(x$6,x$4) in -- #23 +//│ x$7 -- #22 +//│ def bbb() = +//│ let* (x$8) = aaa() in -- #45 +//│ let x$9 = *(x$8,100) in -- #44 +//│ let x$10 = +(x$9,4) in -- #43 +//│ x$10 -- #42 +//│ def not(x$11) = +//│ if x$11 -- #54 +//│ true => +//│ let x$13 = False() in -- #50 +//│ jump j$0(x$13) -- #49 +//│ false => +//│ let x$14 = True() in -- #53 +//│ jump j$0(x$14) -- #52 +//│ def j$0(x$12) = +//│ x$12 -- #47 +//│ def foo(x$15) = +//│ if x$15 -- #74 +//│ true => +//│ let x$17 = None() in -- #59 +//│ jump j$1(x$17) -- #58 +//│ false => +//│ let* (x$18) = not(x$15) in -- #73 +//│ let* (x$19) = foo(x$18) in -- #72 +//│ let x$20 = Some(x$19) in -- #71 +//│ jump j$1(x$20) -- #70 +//│ def j$1(x$16) = +//│ x$16 -- #56 +//│ def main() = +//│ let x$21 = False() in -- #96 +//│ let* (x$22) = foo(x$21) in -- #95 +//│ case x$22 of -- #94 +//│ None => +//│ let* (x$24) = aaa() in -- #84 +//│ jump j$2(x$24) -- #83 +//│ Some => +//│ let x$25 = Some.x(x$22) in -- #93 +//│ let* (x$26) = bbb() in -- #92 +//│ jump j$2(x$26) -- #91 +//│ def j$2(x$23) = +//│ x$23 -- #80 +//│ let* (x$0) = main() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: //│ 404 +//│ :interpIR -class True -class False -class S(s) -class O fun odd(x) = if x is O then False @@ -272,62 +258,51 @@ fun even(x) = S(s) then odd(s) fun foo() = odd(S(S(S(O)))) foo() -//│ |#class| |True|↵|#class| |False|↵|#class| |S|(|s|)|↵|#class| |O|↵|#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |foo|(||)| |#=| |odd|(|S|(|S|(|S|(|O|)|)|)|)|↵|foo|(||)| -//│ Parsed: {class True {}; class False {}; class S(s,) {}; class O {}; fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun foo = () => odd(S(S(S(O,),),),); foo()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, S, [s]),ClassInfo(5, O, [])}, { -//│ Def(0, odd, [x$0], -//│ 1, -//│ case x$0 of -- #15 -//│ O => -//│ let x$2 = False() in -- #4 -//│ jump j$0(x$2) -- #3 -//│ S => -//│ let x$3 = x$0.s in -- #14 -//│ let* (x$4) = even(x$3) in -- #13 -//│ jump j$0(x$4) -- #12 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, even, [x$5], -//│ 1, -//│ case x$5 of -- #31 -//│ O => -//│ let x$7 = True() in -- #20 -//│ jump j$1(x$7) -- #19 -//│ S => -//│ let x$8 = x$5.s in -- #30 -//│ let* (x$9) = odd(x$8) in -- #29 -//│ jump j$1(x$9) -- #28 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #17 -//│ ) -//│ Def(4, foo, [], -//│ 1, -//│ let x$10 = O() in -- #50 -//│ let x$11 = S(x$10) in -- #49 -//│ let x$12 = S(x$11) in -- #48 -//│ let x$13 = S(x$12) in -- #47 -//│ let* (x$14) = odd(x$13) in -- #46 -//│ x$14 -- #45 -//│ ) -//│ }, -//│ let* (x$15) = foo() in -- #54 -//│ x$15 -- #53) +//│ |#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |foo|(||)| |#=| |odd|(|S|(|S|(|S|(|O|)|)|)|)|↵|foo|(||)| +//│ Parsed: {fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun foo = () => odd(S(S(S(O,),),),); foo()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def odd(x$1) = +//│ case x$1 of -- #19 +//│ O => +//│ let x$3 = False() in -- #7 +//│ jump j$0(x$3) -- #6 +//│ S => +//│ let x$4 = S.s(x$1) in -- #18 +//│ let* (x$5) = even(x$4) in -- #17 +//│ jump j$0(x$5) -- #16 +//│ def j$0(x$2) = +//│ x$2 -- #4 +//│ def even(x$6) = +//│ case x$6 of -- #36 +//│ O => +//│ let x$8 = True() in -- #24 +//│ jump j$1(x$8) -- #23 +//│ S => +//│ let x$9 = S.s(x$6) in -- #35 +//│ let* (x$10) = odd(x$9) in -- #34 +//│ jump j$1(x$10) -- #33 +//│ def j$1(x$7) = +//│ x$7 -- #21 +//│ def foo() = +//│ let x$11 = O() in -- #54 +//│ let x$12 = S(x$11) in -- #53 +//│ let x$13 = S(x$12) in -- #52 +//│ let x$14 = S(x$13) in -- #51 +//│ let* (x$15) = odd(x$14) in -- #50 +//│ x$15 -- #49 +//│ let* (x$0) = foo() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: //│ True() +//│ :interpIR -class True -class False -class S(s) -class O +:genCpp fun odd(x) = if x is O then False @@ -339,77 +314,61 @@ fun even(x) = fun mk(n) = if n > 0 then S(mk(n - 1)) else O fun foo() = odd(mk(10)) foo() -//│ |#class| |True|↵|#class| |False|↵|#class| |S|(|s|)|↵|#class| |O|↵|#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |mk|(|n|)| |#=| |#if| |n| |>| |0| |#then| |S|(|mk|(|n| |-| |1|)|)| |#else| |O|↵|#fun| |foo|(||)| |#=| |odd|(|mk|(|10|)|)|↵|foo|(||)| | -//│ Parsed: {class True {}; class False {}; class S(s,) {}; class O {}; fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun mk = (n,) => if (>(n,)(0,)) then S(mk(-(n,)(1,),),) else O; fun foo = () => odd(mk(10,),); foo()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, S, [s]),ClassInfo(5, O, [])}, { -//│ Def(0, odd, [x$0], -//│ 1, -//│ case x$0 of -- #15 -//│ O => -//│ let x$2 = False() in -- #4 -//│ jump j$0(x$2) -- #3 -//│ S => -//│ let x$3 = x$0.s in -- #14 -//│ let* (x$4) = even(x$3) in -- #13 -//│ jump j$0(x$4) -- #12 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, even, [x$5], -//│ 1, -//│ case x$5 of -- #31 -//│ O => -//│ let x$7 = True() in -- #20 -//│ jump j$1(x$7) -- #19 -//│ S => -//│ let x$8 = x$5.s in -- #30 -//│ let* (x$9) = odd(x$8) in -- #29 -//│ jump j$1(x$9) -- #28 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #17 -//│ ) -//│ Def(4, mk, [n$0], -//│ 1, -//│ let x$10 = >(n$0,0) in -- #54 -//│ if x$10 -- #53 -//│ true => -//│ let x$12 = -(n$0,1) in -- #49 -//│ let* (x$13) = mk(x$12) in -- #48 -//│ let x$14 = S(x$13) in -- #47 -//│ jump j$2(x$14) -- #46 -//│ false => -//│ let x$15 = O() in -- #52 -//│ jump j$2(x$15) -- #51 -//│ ) -//│ Def(5, j$2, [x$11], -//│ 1, -//│ x$11 -- #35 -//│ ) -//│ Def(6, foo, [], -//│ 1, -//│ let* (x$16) = mk(10) in -- #65 -//│ let* (x$17) = odd(x$16) in -- #64 -//│ x$17 -- #63 -//│ ) -//│ }, -//│ let* (x$18) = foo() in -- #69 -//│ x$18 -- #68) +//│ |#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |mk|(|n|)| |#=| |#if| |n| |>| |0| |#then| |S|(|mk|(|n| |-| |1|)|)| |#else| |O|↵|#fun| |foo|(||)| |#=| |odd|(|mk|(|10|)|)|↵|foo|(||)| | +//│ Parsed: {fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun mk = (n,) => if (>(n, 0,)) then S(mk(-(n, 1,),),) else O; fun foo = () => odd(mk(10,),); foo()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def odd(x$1) = +//│ case x$1 of -- #19 +//│ O => +//│ let x$3 = False() in -- #7 +//│ jump j$0(x$3) -- #6 +//│ S => +//│ let x$4 = S.s(x$1) in -- #18 +//│ let* (x$5) = even(x$4) in -- #17 +//│ jump j$0(x$5) -- #16 +//│ def j$0(x$2) = +//│ x$2 -- #4 +//│ def even(x$6) = +//│ case x$6 of -- #36 +//│ O => +//│ let x$8 = True() in -- #24 +//│ jump j$1(x$8) -- #23 +//│ S => +//│ let x$9 = S.s(x$6) in -- #35 +//│ let* (x$10) = odd(x$9) in -- #34 +//│ jump j$1(x$10) -- #33 +//│ def j$1(x$7) = +//│ x$7 -- #21 +//│ def mk(n$0) = +//│ let x$11 = >(n$0,0) in -- #64 +//│ if x$11 -- #63 +//│ true => +//│ let x$13 = -(n$0,1) in -- #59 +//│ let* (x$14) = mk(x$13) in -- #58 +//│ let x$15 = S(x$14) in -- #57 +//│ jump j$2(x$15) -- #56 +//│ false => +//│ let x$16 = O() in -- #62 +//│ jump j$2(x$16) -- #61 +//│ def j$2(x$12) = +//│ x$12 -- #43 +//│ def foo() = +//│ let* (x$17) = mk(10) in -- #73 +//│ let* (x$18) = odd(x$17) in -- #72 +//│ x$18 -- #71 +//│ let* (x$0) = foo() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: +//│ False() //│ -//│ IR Processing Failed: can not find the matched case, ClassInfo(0, True, []) expected :interpIR -class True -class False -class S(s) -class O +:genCpp fun odd(x) = if x is O then False @@ -421,79 +380,63 @@ fun even(x) = fun mk(n) = if n > 0 then S(mk(n - 1)) else O fun foo() = odd(S(S(mk(10)))) foo() -//│ |#class| |True|↵|#class| |False|↵|#class| |S|(|s|)|↵|#class| |O|↵|#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |mk|(|n|)| |#=| |#if| |n| |>| |0| |#then| |S|(|mk|(|n| |-| |1|)|)| |#else| |O|↵|#fun| |foo|(||)| |#=| |odd|(|S|(|S|(|mk|(|10|)|)|)|)|↵|foo|(||)| -//│ Parsed: {class True {}; class False {}; class S(s,) {}; class O {}; fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun mk = (n,) => if (>(n,)(0,)) then S(mk(-(n,)(1,),),) else O; fun foo = () => odd(S(S(mk(10,),),),); foo()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, S, [s]),ClassInfo(5, O, [])}, { -//│ Def(0, odd, [x$0], -//│ 1, -//│ case x$0 of -- #15 -//│ O => -//│ let x$2 = False() in -- #4 -//│ jump j$0(x$2) -- #3 -//│ S => -//│ let x$3 = x$0.s in -- #14 -//│ let* (x$4) = even(x$3) in -- #13 -//│ jump j$0(x$4) -- #12 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, even, [x$5], -//│ 1, -//│ case x$5 of -- #31 -//│ O => -//│ let x$7 = True() in -- #20 -//│ jump j$1(x$7) -- #19 -//│ S => -//│ let x$8 = x$5.s in -- #30 -//│ let* (x$9) = odd(x$8) in -- #29 -//│ jump j$1(x$9) -- #28 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #17 -//│ ) -//│ Def(4, mk, [n$0], -//│ 1, -//│ let x$10 = >(n$0,0) in -- #54 -//│ if x$10 -- #53 -//│ true => -//│ let x$12 = -(n$0,1) in -- #49 -//│ let* (x$13) = mk(x$12) in -- #48 -//│ let x$14 = S(x$13) in -- #47 -//│ jump j$2(x$14) -- #46 -//│ false => -//│ let x$15 = O() in -- #52 -//│ jump j$2(x$15) -- #51 -//│ ) -//│ Def(5, j$2, [x$11], -//│ 1, -//│ x$11 -- #35 -//│ ) -//│ Def(6, foo, [], -//│ 1, -//│ let* (x$16) = mk(10) in -- #73 -//│ let x$17 = S(x$16) in -- #72 -//│ let x$18 = S(x$17) in -- #71 -//│ let* (x$19) = odd(x$18) in -- #70 -//│ x$19 -- #69 -//│ ) -//│ }, -//│ let* (x$20) = foo() in -- #77 -//│ x$20 -- #76) +//│ |#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |mk|(|n|)| |#=| |#if| |n| |>| |0| |#then| |S|(|mk|(|n| |-| |1|)|)| |#else| |O|↵|#fun| |foo|(||)| |#=| |odd|(|S|(|S|(|mk|(|10|)|)|)|)|↵|foo|(||)| +//│ Parsed: {fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun mk = (n,) => if (>(n, 0,)) then S(mk(-(n, 1,),),) else O; fun foo = () => odd(S(S(mk(10,),),),); foo()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def odd(x$1) = +//│ case x$1 of -- #19 +//│ O => +//│ let x$3 = False() in -- #7 +//│ jump j$0(x$3) -- #6 +//│ S => +//│ let x$4 = S.s(x$1) in -- #18 +//│ let* (x$5) = even(x$4) in -- #17 +//│ jump j$0(x$5) -- #16 +//│ def j$0(x$2) = +//│ x$2 -- #4 +//│ def even(x$6) = +//│ case x$6 of -- #36 +//│ O => +//│ let x$8 = True() in -- #24 +//│ jump j$1(x$8) -- #23 +//│ S => +//│ let x$9 = S.s(x$6) in -- #35 +//│ let* (x$10) = odd(x$9) in -- #34 +//│ jump j$1(x$10) -- #33 +//│ def j$1(x$7) = +//│ x$7 -- #21 +//│ def mk(n$0) = +//│ let x$11 = >(n$0,0) in -- #64 +//│ if x$11 -- #63 +//│ true => +//│ let x$13 = -(n$0,1) in -- #59 +//│ let* (x$14) = mk(x$13) in -- #58 +//│ let x$15 = S(x$14) in -- #57 +//│ jump j$2(x$15) -- #56 +//│ false => +//│ let x$16 = O() in -- #62 +//│ jump j$2(x$16) -- #61 +//│ def j$2(x$12) = +//│ x$12 -- #43 +//│ def foo() = +//│ let* (x$17) = mk(10) in -- #81 +//│ let x$18 = S(x$17) in -- #80 +//│ let x$19 = S(x$18) in -- #79 +//│ let* (x$20) = odd(x$19) in -- #78 +//│ x$20 -- #77 +//│ let* (x$0) = foo() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: +//│ False() //│ -//│ IR Processing Failed: can not find the matched case, ClassInfo(0, True, []) expected :interpIR -class True -class False -class S(s) -class O +:genCpp fun odd(x) = if x is O then False @@ -508,95 +451,75 @@ fun main() = foo() bar() main() -//│ |#class| |True|↵|#class| |False|↵|#class| |S|(|s|)|↵|#class| |O|↵|#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |foo|(||)| |#=| |odd|(|#if| |10| |>| |0| |#then| |S|(|O|)| |#else| |O|)|↵|#fun| |bar|(||)| |#=| |#if| |10| |>| |0| |#then| |odd|(|S|(|O|)|)| |#else| |odd|(|O|)|↵|#fun| |main|(||)| |#=|→|foo|(||)|↵|bar|(||)|←|↵|main|(||)| -//│ Parsed: {class True {}; class False {}; class S(s,) {}; class O {}; fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun foo = () => odd(if (>(10,)(0,)) then S(O,) else O,); fun bar = () => if (>(10,)(0,)) then odd(S(O,),) else odd(O,); fun main = () => {foo(); bar()}; main()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, S, [s]),ClassInfo(5, O, [])}, { -//│ Def(0, odd, [x$0], -//│ 1, -//│ case x$0 of -- #15 -//│ O => -//│ let x$2 = False() in -- #4 -//│ jump j$0(x$2) -- #3 -//│ S => -//│ let x$3 = x$0.s in -- #14 -//│ let* (x$4) = even(x$3) in -- #13 -//│ jump j$0(x$4) -- #12 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, even, [x$5], -//│ 1, -//│ case x$5 of -- #31 -//│ O => -//│ let x$7 = True() in -- #20 -//│ jump j$1(x$7) -- #19 -//│ S => -//│ let x$8 = x$5.s in -- #30 -//│ let* (x$9) = odd(x$8) in -- #29 -//│ jump j$1(x$9) -- #28 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #17 -//│ ) -//│ Def(4, foo, [], -//│ 1, -//│ let x$10 = >(10,0) in -- #52 -//│ if x$10 -- #51 -//│ true => -//│ let x$13 = O() in -- #47 -//│ let x$14 = S(x$13) in -- #46 -//│ jump j$2(x$14) -- #45 -//│ false => -//│ let x$15 = O() in -- #50 -//│ jump j$2(x$15) -- #49 -//│ ) -//│ Def(5, j$2, [x$11], -//│ 1, -//│ let* (x$12) = odd(x$11) in -- #40 -//│ x$12 -- #39 -//│ ) -//│ Def(6, bar, [], -//│ 1, -//│ let x$16 = >(10,0) in -- #78 -//│ if x$16 -- #77 -//│ true => -//│ let x$18 = O() in -- #68 -//│ let x$19 = S(x$18) in -- #67 -//│ let* (x$20) = odd(x$19) in -- #66 -//│ jump j$3(x$20) -- #65 -//│ false => -//│ let x$21 = O() in -- #76 -//│ let* (x$22) = odd(x$21) in -- #75 -//│ jump j$3(x$22) -- #74 -//│ ) -//│ Def(7, j$3, [x$17], -//│ 1, -//│ x$17 -- #56 -//│ ) -//│ Def(8, main, [], -//│ 1, -//│ let* (x$23) = foo() in -- #86 -//│ let* (x$24) = bar() in -- #85 -//│ x$24 -- #84 -//│ ) -//│ }, -//│ let* (x$25) = main() in -- #90 -//│ x$25 -- #89) +//│ |#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |foo|(||)| |#=| |odd|(|#if| |10| |>| |0| |#then| |S|(|O|)| |#else| |O|)|↵|#fun| |bar|(||)| |#=| |#if| |10| |>| |0| |#then| |odd|(|S|(|O|)|)| |#else| |odd|(|O|)|↵|#fun| |main|(||)| |#=|→|foo|(||)|↵|bar|(||)|←|↵|main|(||)| +//│ Parsed: {fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun foo = () => odd(if (>(10, 0,)) then S(O,) else O,); fun bar = () => if (>(10, 0,)) then odd(S(O,),) else odd(O,); fun main = () => {foo(); bar()}; main()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def odd(x$1) = +//│ case x$1 of -- #19 +//│ O => +//│ let x$3 = False() in -- #7 +//│ jump j$0(x$3) -- #6 +//│ S => +//│ let x$4 = S.s(x$1) in -- #18 +//│ let* (x$5) = even(x$4) in -- #17 +//│ jump j$0(x$5) -- #16 +//│ def j$0(x$2) = +//│ x$2 -- #4 +//│ def even(x$6) = +//│ case x$6 of -- #36 +//│ O => +//│ let x$8 = True() in -- #24 +//│ jump j$1(x$8) -- #23 +//│ S => +//│ let x$9 = S.s(x$6) in -- #35 +//│ let* (x$10) = odd(x$9) in -- #34 +//│ jump j$1(x$10) -- #33 +//│ def j$1(x$7) = +//│ x$7 -- #21 +//│ def foo() = +//│ let x$11 = >(10,0) in -- #59 +//│ if x$11 -- #58 +//│ true => +//│ let x$14 = O() in -- #54 +//│ let x$15 = S(x$14) in -- #53 +//│ jump j$2(x$15) -- #52 +//│ false => +//│ let x$16 = O() in -- #57 +//│ jump j$2(x$16) -- #56 +//│ def j$2(x$12) = +//│ let* (x$13) = odd(x$12) in -- #47 +//│ x$13 -- #46 +//│ def bar() = +//│ let x$17 = >(10,0) in -- #86 +//│ if x$17 -- #85 +//│ true => +//│ let x$19 = O() in -- #77 +//│ let x$20 = S(x$19) in -- #76 +//│ let* (x$21) = odd(x$20) in -- #75 +//│ jump j$3(x$21) -- #74 +//│ false => +//│ let x$22 = O() in -- #84 +//│ let* (x$23) = odd(x$22) in -- #83 +//│ jump j$3(x$23) -- #82 +//│ def j$3(x$18) = +//│ x$18 -- #66 +//│ def main() = +//│ let* (x$24) = foo() in -- #92 +//│ let* (x$25) = bar() in -- #91 +//│ x$25 -- #90 +//│ let* (x$0) = main() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: +//│ True() //│ -//│ IR Processing Failed: can not find the matched case, ClassInfo(0, True, []) expected :interpIR -class True -class False -class A() -class B(b) +:genCpp fun aaa() = let m = 1 let n = 2 @@ -609,98 +532,79 @@ fun bbb() = fun not(x) = if x then False else True fun foo(x) = - if x then A else B(foo(not(x))) + if x then None else Some(foo(not(x))) fun main(flag) = let x = foo(flag) if x is - A then aaa() - B(b1) then bbb() + None then aaa() + Some(b1) then bbb() main(False) -//│ |#class| |True|↵|#class| |False|↵|#class| |A|(||)|↵|#class| |B|(|b|)|↵|#fun| |aaa|(||)| |#=|→|#let| |m| |#=| |1|↵|#let| |n| |#=| |2|↵|#let| |p| |#=| |3|↵|#let| |q| |#=| |4|↵|m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |bbb|(||)| |#=|→|#let| |x| |#=| |aaa|(||)|↵|x| |*| |100| |+| |4|←|↵|#fun| |not|(|x|)| |#=|→|#if| |x| |#then| |False| |#else| |True|←|↵|#fun| |foo|(|x|)| |#=|→|#if| |x| |#then| |A| |#else| |B|(|foo|(|not|(|x|)|)|)|←|↵|#fun| |main|(|flag|)| |#=|→|#let| |x| |#=| |foo|(|flag|)|↵|#if| |x| |is|→|A| |#then| |aaa|(||)|↵|B|(|b1|)| |#then| |bbb|(||)|←|←|↵|main|(|False|)| -//│ Parsed: {class True {}; class False {}; class A() {}; class B(b,) {}; fun aaa = () => {let m = 1; let n = 2; let p = 3; let q = 4; +(-(+(m,)(n,),)(p,),)(q,)}; fun bbb = () => {let x = aaa(); +(*(x,)(100,),)(4,)}; fun not = (x,) => {if (x) then False else True}; fun foo = (x,) => {if (x) then A else B(foo(not(x,),),)}; fun main = (flag,) => {let x = foo(flag,); if x is ‹(A) then aaa(); (B(b1,)) then bbb()›}; main(False,)} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, A, []),ClassInfo(5, B, [b])}, { -//│ Def(0, aaa, [], -//│ 1, -//│ let x$0 = 1 in -- #17 -//│ let x$1 = 2 in -- #16 -//│ let x$2 = 3 in -- #15 -//│ let x$3 = 4 in -- #14 -//│ let x$4 = +(x$0,x$1) in -- #13 -//│ let x$5 = -(x$4,x$2) in -- #12 -//│ let x$6 = +(x$5,x$3) in -- #11 -//│ x$6 -- #10 -//│ ) -//│ Def(1, bbb, [], -//│ 1, -//│ let* (x$7) = aaa() in -- #28 -//│ let x$8 = *(x$7,100) in -- #27 -//│ let x$9 = +(x$8,4) in -- #26 -//│ x$9 -- #25 -//│ ) -//│ Def(2, not, [x$10], -//│ 1, -//│ if x$10 -- #37 -//│ true => -//│ let x$12 = False() in -- #33 -//│ jump j$0(x$12) -- #32 -//│ false => -//│ let x$13 = True() in -- #36 -//│ jump j$0(x$13) -- #35 -//│ ) -//│ Def(3, j$0, [x$11], -//│ 1, -//│ x$11 -- #30 -//│ ) -//│ Def(4, foo, [x$14], -//│ 1, -//│ if x$14 -- #59 -//│ true => -//│ let x$16 = A() in -- #42 -//│ jump j$1(x$16) -- #41 -//│ false => -//│ let* (x$17) = not(x$14) in -- #58 -//│ let* (x$18) = foo(x$17) in -- #57 -//│ let x$19 = B(x$18) in -- #56 -//│ jump j$1(x$19) -- #55 -//│ ) -//│ Def(5, j$1, [x$15], -//│ 1, -//│ x$15 -- #39 -//│ ) -//│ Def(6, main, [flag$0], -//│ 1, -//│ let* (x$20) = foo(flag$0) in -- #81 -//│ case x$20 of -- #80 -//│ A => -//│ let* (x$22) = aaa() in -- #71 -//│ jump j$2(x$22) -- #70 -//│ B => -//│ let x$23 = x$20.b in -- #79 -//│ let* (x$24) = bbb() in -- #78 -//│ jump j$2(x$24) -- #77 -//│ ) -//│ Def(7, j$2, [x$21], -//│ 1, -//│ x$21 -- #66 -//│ ) -//│ }, -//│ let x$25 = False() in -- #88 -//│ let* (x$26) = main(x$25) in -- #87 -//│ x$26 -- #86) +//│ |#fun| |aaa|(||)| |#=|→|#let| |m| |#=| |1|↵|#let| |n| |#=| |2|↵|#let| |p| |#=| |3|↵|#let| |q| |#=| |4|↵|m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |bbb|(||)| |#=|→|#let| |x| |#=| |aaa|(||)|↵|x| |*| |100| |+| |4|←|↵|#fun| |not|(|x|)| |#=|→|#if| |x| |#then| |False| |#else| |True|←|↵|#fun| |foo|(|x|)| |#=|→|#if| |x| |#then| |None| |#else| |Some|(|foo|(|not|(|x|)|)|)|←|↵|#fun| |main|(|flag|)| |#=|→|#let| |x| |#=| |foo|(|flag|)|↵|#if| |x| |is|→|None| |#then| |aaa|(||)|↵|Some|(|b1|)| |#then| |bbb|(||)|←|←|↵|main|(|False|)| +//│ Parsed: {fun aaa = () => {let m = 1; let n = 2; let p = 3; let q = 4; +(-(+(m, n,), p,), q,)}; fun bbb = () => {let x = aaa(); +(*(x, 100,), 4,)}; fun not = (x,) => {if (x) then False else True}; fun foo = (x,) => {if (x) then None else Some(foo(not(x,),),)}; fun main = (flag,) => {let x = foo(flag,); if x is ‹(None) then aaa(); (Some(b1,)) then bbb()›}; main(False,)} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def aaa() = +//│ let x$2 = 1 in -- #32 +//│ let x$3 = 2 in -- #31 +//│ let x$4 = 3 in -- #30 +//│ let x$5 = 4 in -- #29 +//│ let x$6 = +(x$2,x$3) in -- #28 +//│ let x$7 = -(x$6,x$4) in -- #27 +//│ let x$8 = +(x$7,x$5) in -- #26 +//│ x$8 -- #25 +//│ def bbb() = +//│ let* (x$9) = aaa() in -- #48 +//│ let x$10 = *(x$9,100) in -- #47 +//│ let x$11 = +(x$10,4) in -- #46 +//│ x$11 -- #45 +//│ def not(x$12) = +//│ if x$12 -- #57 +//│ true => +//│ let x$14 = False() in -- #53 +//│ jump j$0(x$14) -- #52 +//│ false => +//│ let x$15 = True() in -- #56 +//│ jump j$0(x$15) -- #55 +//│ def j$0(x$13) = +//│ x$13 -- #50 +//│ def foo(x$16) = +//│ if x$16 -- #77 +//│ true => +//│ let x$18 = None() in -- #62 +//│ jump j$1(x$18) -- #61 +//│ false => +//│ let* (x$19) = not(x$16) in -- #76 +//│ let* (x$20) = foo(x$19) in -- #75 +//│ let x$21 = Some(x$20) in -- #74 +//│ jump j$1(x$21) -- #73 +//│ def j$1(x$17) = +//│ x$17 -- #59 +//│ def main(flag$0) = +//│ let* (x$22) = foo(flag$0) in -- #98 +//│ case x$22 of -- #97 +//│ None => +//│ let* (x$24) = aaa() in -- #87 +//│ jump j$2(x$24) -- #86 +//│ Some => +//│ let x$25 = Some.x(x$22) in -- #96 +//│ let* (x$26) = bbb() in -- #95 +//│ jump j$2(x$26) -- #94 +//│ def j$2(x$23) = +//│ x$23 -- #83 +//│ let x$0 = False() in -- #5 +//│ let* (x$1) = main(x$0) in -- #4 +//│ x$1 -- #3 //│ //│ Interpreted: //│ 404 +//│ :interpIR -class True -class False -class Cons(h, t) -class Nil -class Some(x) -class None +:genCpp fun head_opt(l) = if l is Nil then None @@ -714,70 +618,55 @@ fun is_empty(l) = fun main() = is_empty(Cons(1, Cons(2, Nil))) main() -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#class| |Some|(|x|)|↵|#class| |None|↵|#fun| |head_opt|(|l|)| |#=|→|#if| |l| |is|→|Nil| |#then| |None|↵|Cons|(|h|,| |t|)| |#then| |Some|(|h|)|←|←|↵|#fun| |is_none|(|o|)| |#=|→|#if| |o| |is|→|None| |#then| |True|↵|Some|(|x|)| |#then| |False|←|←|↵|#fun| |is_empty|(|l|)| |#=|→|is_none|(|head_opt|(|l|)|)|←|↵|#fun| |main|(||)| |#=|→|is_empty|(|Cons|(|1|,| |Cons|(|2|,| |Nil|)|)|)|←|↵|main|(||)| -//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; class Some(x,) {}; class None {}; fun head_opt = (l,) => {if l is ‹(Nil) then None; (Cons(h, t,)) then Some(h,)›}; fun is_none = (o,) => {if o is ‹(None) then True; (Some(x,)) then False›}; fun is_empty = (l,) => {is_none(head_opt(l,),)}; fun main = () => {is_empty(Cons(1, Cons(2, Nil,),),)}; main()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, Cons, [h,t]),ClassInfo(5, Nil, []),ClassInfo(6, Some, [x]),ClassInfo(7, None, [])}, { -//│ Def(0, head_opt, [l$0], -//│ 1, -//│ case l$0 of -- #17 -//│ Nil => -//│ let x$1 = None() in -- #4 -//│ jump j$0(x$1) -- #3 -//│ Cons => -//│ let x$2 = l$0.t in -- #16 -//│ let x$3 = l$0.h in -- #15 -//│ let x$4 = Some(x$3) in -- #14 -//│ jump j$0(x$4) -- #13 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, is_none, [o$0], -//│ 1, -//│ case o$0 of -- #29 -//│ None => -//│ let x$6 = True() in -- #22 -//│ jump j$1(x$6) -- #21 -//│ Some => -//│ let x$7 = o$0.x in -- #28 -//│ let x$8 = False() in -- #27 -//│ jump j$1(x$8) -- #26 -//│ ) -//│ Def(3, j$1, [x$5], -//│ 1, -//│ x$5 -- #19 -//│ ) -//│ Def(4, is_empty, [l$1], -//│ 1, -//│ let* (x$9) = head_opt(l$1) in -- #40 -//│ let* (x$10) = is_none(x$9) in -- #39 -//│ x$10 -- #38 -//│ ) -//│ Def(5, main, [], -//│ 1, -//│ let x$11 = Nil() in -- #59 -//│ let x$12 = Cons(2,x$11) in -- #58 -//│ let x$13 = Cons(1,x$12) in -- #57 -//│ let* (x$14) = is_empty(x$13) in -- #56 -//│ x$14 -- #55 -//│ ) -//│ }, -//│ let* (x$15) = main() in -- #63 -//│ x$15 -- #62) +//│ |#fun| |head_opt|(|l|)| |#=|→|#if| |l| |is|→|Nil| |#then| |None|↵|Cons|(|h|,| |t|)| |#then| |Some|(|h|)|←|←|↵|#fun| |is_none|(|o|)| |#=|→|#if| |o| |is|→|None| |#then| |True|↵|Some|(|x|)| |#then| |False|←|←|↵|#fun| |is_empty|(|l|)| |#=|→|is_none|(|head_opt|(|l|)|)|←|↵|#fun| |main|(||)| |#=|→|is_empty|(|Cons|(|1|,| |Cons|(|2|,| |Nil|)|)|)|←|↵|main|(||)| +//│ Parsed: {fun head_opt = (l,) => {if l is ‹(Nil) then None; (Cons(h, t,)) then Some(h,)›}; fun is_none = (o,) => {if o is ‹(None) then True; (Some(x,)) then False›}; fun is_empty = (l,) => {is_none(head_opt(l,),)}; fun main = () => {is_empty(Cons(1, Cons(2, Nil,),),)}; main()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def head_opt(l$0) = +//│ case l$0 of -- #24 +//│ Nil => +//│ let x$2 = None() in -- #7 +//│ jump j$0(x$2) -- #6 +//│ Cons => +//│ let x$3 = Cons.t(l$0) in -- #23 +//│ let x$4 = Cons.h(l$0) in -- #22 +//│ let x$5 = Some(x$4) in -- #21 +//│ jump j$0(x$5) -- #20 +//│ def j$0(x$1) = +//│ x$1 -- #4 +//│ def is_none(o$0) = +//│ case o$0 of -- #38 +//│ None => +//│ let x$7 = True() in -- #29 +//│ jump j$1(x$7) -- #28 +//│ Some => +//│ let x$8 = Some.x(o$0) in -- #37 +//│ let x$9 = False() in -- #36 +//│ jump j$1(x$9) -- #35 +//│ def j$1(x$6) = +//│ x$6 -- #26 +//│ def is_empty(l$1) = +//│ let* (x$10) = head_opt(l$1) in -- #47 +//│ let* (x$11) = is_none(x$10) in -- #46 +//│ x$11 -- #45 +//│ def main() = +//│ let x$12 = Nil() in -- #65 +//│ let x$13 = Cons(2,x$12) in -- #64 +//│ let x$14 = Cons(1,x$13) in -- #63 +//│ let* (x$15) = is_empty(x$14) in -- #62 +//│ x$15 -- #61 +//│ let* (x$0) = main() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: //│ False() +//│ :interpIR -class True -class False -class Cons(h, t) -class Nil -class Some(x) -class None +:genCpp fun mk_list(n) = if n == 0 then Nil else Cons(n, mk_list(n - 1)) fun head_opt(l) = @@ -793,86 +682,66 @@ fun is_empty(l) = fun main() = is_empty(mk_list(10)) main() -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#class| |Some|(|x|)|↵|#class| |None|↵|#fun| |mk_list|(|n|)| |#=|→|#if| |n| |==| |0| |#then| |Nil| |#else| |Cons|(|n|,| |mk_list|(|n| |-| |1|)|)|←|↵|#fun| |head_opt|(|l|)| |#=|→|#if| |l| |is|→|Nil| |#then| |None|↵|Cons|(|h|,| |t|)| |#then| |Some|(|h|)|←|←|↵|#fun| |is_none|(|o|)| |#=|→|#if| |o| |is|→|None| |#then| |True|↵|Some|(|x|)| |#then| |False|←|←|↵|#fun| |is_empty|(|l|)| |#=|→|is_none|(|head_opt|(|l|)|)|←|↵|#fun| |main|(||)| |#=|→|is_empty|(|mk_list|(|10|)|)|←|↵|main|(||)| -//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; class Some(x,) {}; class None {}; fun mk_list = (n,) => {if (==(n,)(0,)) then Nil else Cons(n, mk_list(-(n,)(1,),),)}; fun head_opt = (l,) => {if l is ‹(Nil) then None; (Cons(h, t,)) then Some(h,)›}; fun is_none = (o,) => {if o is ‹(None) then True; (Some(x,)) then False›}; fun is_empty = (l,) => {is_none(head_opt(l,),)}; fun main = () => {is_empty(mk_list(10,),)}; main()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, Cons, [h,t]),ClassInfo(5, Nil, []),ClassInfo(6, Some, [x]),ClassInfo(7, None, [])}, { -//│ Def(0, mk_list, [n$0], -//│ 1, -//│ let x$0 = ==(n$0,0) in -- #24 -//│ if x$0 -- #23 -//│ true => -//│ let x$2 = Nil() in -- #6 -//│ jump j$0(x$2) -- #5 -//│ false => -//│ let x$3 = -(n$0,1) in -- #22 -//│ let* (x$4) = mk_list(x$3) in -- #21 -//│ let x$5 = Cons(n$0,x$4) in -- #20 -//│ jump j$0(x$5) -- #19 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ Def(2, head_opt, [l$0], -//│ 1, -//│ case l$0 of -- #42 -//│ Nil => -//│ let x$7 = None() in -- #29 -//│ jump j$1(x$7) -- #28 -//│ Cons => -//│ let x$8 = l$0.t in -- #41 -//│ let x$9 = l$0.h in -- #40 -//│ let x$10 = Some(x$9) in -- #39 -//│ jump j$1(x$10) -- #38 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #26 -//│ ) -//│ Def(4, is_none, [o$0], -//│ 1, -//│ case o$0 of -- #54 -//│ None => -//│ let x$12 = True() in -- #47 -//│ jump j$2(x$12) -- #46 -//│ Some => -//│ let x$13 = o$0.x in -- #53 -//│ let x$14 = False() in -- #52 -//│ jump j$2(x$14) -- #51 -//│ ) -//│ Def(5, j$2, [x$11], -//│ 1, -//│ x$11 -- #44 -//│ ) -//│ Def(6, is_empty, [l$1], -//│ 1, -//│ let* (x$15) = head_opt(l$1) in -- #65 -//│ let* (x$16) = is_none(x$15) in -- #64 -//│ x$16 -- #63 -//│ ) -//│ Def(7, main, [], -//│ 1, -//│ let* (x$17) = mk_list(10) in -- #76 -//│ let* (x$18) = is_empty(x$17) in -- #75 -//│ x$18 -- #74 -//│ ) -//│ }, -//│ let* (x$19) = main() in -- #80 -//│ x$19 -- #79) +//│ |#fun| |mk_list|(|n|)| |#=|→|#if| |n| |==| |0| |#then| |Nil| |#else| |Cons|(|n|,| |mk_list|(|n| |-| |1|)|)|←|↵|#fun| |head_opt|(|l|)| |#=|→|#if| |l| |is|→|Nil| |#then| |None|↵|Cons|(|h|,| |t|)| |#then| |Some|(|h|)|←|←|↵|#fun| |is_none|(|o|)| |#=|→|#if| |o| |is|→|None| |#then| |True|↵|Some|(|x|)| |#then| |False|←|←|↵|#fun| |is_empty|(|l|)| |#=|→|is_none|(|head_opt|(|l|)|)|←|↵|#fun| |main|(||)| |#=|→|is_empty|(|mk_list|(|10|)|)|←|↵|main|(||)| +//│ Parsed: {fun mk_list = (n,) => {if (==(n, 0,)) then Nil else Cons(n, mk_list(-(n, 1,),),)}; fun head_opt = (l,) => {if l is ‹(Nil) then None; (Cons(h, t,)) then Some(h,)›}; fun is_none = (o,) => {if o is ‹(None) then True; (Some(x,)) then False›}; fun is_empty = (l,) => {is_none(head_opt(l,),)}; fun main = () => {is_empty(mk_list(10,),)}; main()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def mk_list(n$0) = +//│ let x$1 = ==(n$0,0) in -- #32 +//│ if x$1 -- #31 +//│ true => +//│ let x$3 = Nil() in -- #12 +//│ jump j$0(x$3) -- #11 +//│ false => +//│ let x$4 = -(n$0,1) in -- #30 +//│ let* (x$5) = mk_list(x$4) in -- #29 +//│ let x$6 = Cons(n$0,x$5) in -- #28 +//│ jump j$0(x$6) -- #27 +//│ def j$0(x$2) = +//│ x$2 -- #9 +//│ def head_opt(l$0) = +//│ case l$0 of -- #54 +//│ Nil => +//│ let x$8 = None() in -- #37 +//│ jump j$1(x$8) -- #36 +//│ Cons => +//│ let x$9 = Cons.t(l$0) in -- #53 +//│ let x$10 = Cons.h(l$0) in -- #52 +//│ let x$11 = Some(x$10) in -- #51 +//│ jump j$1(x$11) -- #50 +//│ def j$1(x$7) = +//│ x$7 -- #34 +//│ def is_none(o$0) = +//│ case o$0 of -- #68 +//│ None => +//│ let x$13 = True() in -- #59 +//│ jump j$2(x$13) -- #58 +//│ Some => +//│ let x$14 = Some.x(o$0) in -- #67 +//│ let x$15 = False() in -- #66 +//│ jump j$2(x$15) -- #65 +//│ def j$2(x$12) = +//│ x$12 -- #56 +//│ def is_empty(l$1) = +//│ let* (x$16) = head_opt(l$1) in -- #77 +//│ let* (x$17) = is_none(x$16) in -- #76 +//│ x$17 -- #75 +//│ def main() = +//│ let* (x$18) = mk_list(10) in -- #86 +//│ let* (x$19) = is_empty(x$18) in -- #85 +//│ x$19 -- #84 +//│ let* (x$0) = main() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: +//│ False() //│ -//│ IR Processing Failed: can not find the matched case, ClassInfo(1, False, []) expected :interpIR -class True -class False -class Cons(h, t) -class Nil -class Some(x) -class None +:genCpp fun mk_list(n) = if n == 0 then Nil else Cons(n, mk_list(n - 1)) fun last_opt(l) = @@ -885,76 +754,60 @@ fun last_opt(l) = fun main() = last_opt(mk_list(10)) main() -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#class| |Some|(|x|)|↵|#class| |None|↵|#fun| |mk_list|(|n|)| |#=|→|#if| |n| |==| |0| |#then| |Nil| |#else| |Cons|(|n|,| |mk_list|(|n| |-| |1|)|)|←|↵|#fun| |last_opt|(|l|)| |#=|→|#if| |l| |is|→|Nil| |#then| |None|↵|Cons|(|h|,| |t|)| |#then|→|#if| |t| |is|→|Nil| |#then| |Some|(|h|)|↵|Cons|(|h2|,| |t2|)| |#then| |last_opt|(|t|)|←|←|←|←|↵|#fun| |main|(||)| |#=|→|last_opt|(|mk_list|(|10|)|)|←|↵|main|(||)| -//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; class Some(x,) {}; class None {}; fun mk_list = (n,) => {if (==(n,)(0,)) then Nil else Cons(n, mk_list(-(n,)(1,),),)}; fun last_opt = (l,) => {if l is ‹(Nil) then None; (Cons(h, t,)) then {if t is ‹(Nil) then Some(h,); (Cons(h2, t2,)) then last_opt(t,)›}›}; fun main = () => {last_opt(mk_list(10,),)}; main()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, Cons, [h,t]),ClassInfo(5, Nil, []),ClassInfo(6, Some, [x]),ClassInfo(7, None, [])}, { -//│ Def(0, mk_list, [n$0], -//│ 1, -//│ let x$0 = ==(n$0,0) in -- #24 -//│ if x$0 -- #23 -//│ true => -//│ let x$2 = Nil() in -- #6 -//│ jump j$0(x$2) -- #5 -//│ false => -//│ let x$3 = -(n$0,1) in -- #22 -//│ let* (x$4) = mk_list(x$3) in -- #21 -//│ let x$5 = Cons(n$0,x$4) in -- #20 -//│ jump j$0(x$5) -- #19 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ Def(2, last_opt, [l$0], -//│ 1, -//│ case l$0 of -- #59 -//│ Nil => -//│ let x$7 = None() in -- #29 -//│ jump j$1(x$7) -- #28 -//│ Cons => -//│ let x$8 = l$0.t in -- #58 -//│ let x$9 = l$0.h in -- #57 -//│ case x$8 of -- #56 +//│ |#fun| |mk_list|(|n|)| |#=|→|#if| |n| |==| |0| |#then| |Nil| |#else| |Cons|(|n|,| |mk_list|(|n| |-| |1|)|)|←|↵|#fun| |last_opt|(|l|)| |#=|→|#if| |l| |is|→|Nil| |#then| |None|↵|Cons|(|h|,| |t|)| |#then|→|#if| |t| |is|→|Nil| |#then| |Some|(|h|)|↵|Cons|(|h2|,| |t2|)| |#then| |last_opt|(|t|)|←|←|←|←|↵|#fun| |main|(||)| |#=|→|last_opt|(|mk_list|(|10|)|)|←|↵|main|(||)| +//│ Parsed: {fun mk_list = (n,) => {if (==(n, 0,)) then Nil else Cons(n, mk_list(-(n, 1,),),)}; fun last_opt = (l,) => {if l is ‹(Nil) then None; (Cons(h, t,)) then {if t is ‹(Nil) then Some(h,); (Cons(h2, t2,)) then last_opt(t,)›}›}; fun main = () => {last_opt(mk_list(10,),)}; main()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def mk_list(n$0) = +//│ let x$1 = ==(n$0,0) in -- #32 +//│ if x$1 -- #31 +//│ true => +//│ let x$3 = Nil() in -- #12 +//│ jump j$0(x$3) -- #11 +//│ false => +//│ let x$4 = -(n$0,1) in -- #30 +//│ let* (x$5) = mk_list(x$4) in -- #29 +//│ let x$6 = Cons(n$0,x$5) in -- #28 +//│ jump j$0(x$6) -- #27 +//│ def j$0(x$2) = +//│ x$2 -- #9 +//│ def last_opt(l$0) = +//│ case l$0 of -- #74 //│ Nil => -//│ let x$11 = Some(x$9) in -- #42 -//│ jump j$2(x$11) -- #41 +//│ let x$8 = None() in -- #37 +//│ jump j$1(x$8) -- #36 //│ Cons => -//│ let x$12 = x$8.t in -- #55 -//│ let x$13 = x$8.h in -- #54 -//│ let* (x$14) = last_opt(x$8) in -- #53 -//│ jump j$2(x$14) -- #52 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #26 -//│ ) -//│ Def(4, j$2, [x$10], -//│ 1, -//│ jump j$1(x$10) -- #36 -//│ ) -//│ Def(5, main, [], -//│ 1, -//│ let* (x$15) = mk_list(10) in -- #70 -//│ let* (x$16) = last_opt(x$15) in -- #69 -//│ x$16 -- #68 -//│ ) -//│ }, -//│ let* (x$17) = main() in -- #74 -//│ x$17 -- #73) +//│ let x$9 = Cons.t(l$0) in -- #73 +//│ let x$10 = Cons.h(l$0) in -- #72 +//│ case x$9 of -- #71 +//│ Nil => +//│ let x$12 = Some(x$10) in -- #54 +//│ jump j$2(x$12) -- #53 +//│ Cons => +//│ let x$13 = Cons.t(x$9) in -- #70 +//│ let x$14 = Cons.h(x$9) in -- #69 +//│ let* (x$15) = last_opt(x$9) in -- #68 +//│ jump j$2(x$15) -- #67 +//│ def j$1(x$7) = +//│ x$7 -- #34 +//│ def j$2(x$11) = +//│ jump j$1(x$11) -- #48 +//│ def main() = +//│ let* (x$16) = mk_list(10) in -- #83 +//│ let* (x$17) = last_opt(x$16) in -- #82 +//│ x$17 -- #81 +//│ let* (x$0) = main() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: +//│ Some(1) //│ -//│ IR Processing Failed: can not find the matched case, ClassInfo(1, False, []) expected :interpIR -class True -class False -class Cons(h, t) -class Nil -class Some(x) -class None +:genCpp fun is_some(o) = if o is Some(x) then True @@ -981,111 +834,88 @@ fun f(x) = fun main() = f(Some(2)) + f(None) main() -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#class| |Some|(|x|)|↵|#class| |None|↵|#fun| |is_some|(|o|)| |#=|→|#if| |o| |is|→|Some|(|x|)| |#then| |True|↵|None| |#then| |False|←|←|↵|#fun| |e0|(|w|)| |#=|→|w| |+| |8| |+| |9| |+| |10|←|↵|#fun| |e1|(|a|,| |c|)| |#=|→|a| |+| |1| |+| |2| |+| |3| |+| |4|←|↵|#fun| |e3|(|c|)| |#=|→|#let| |m| |#=| |4|↵|#let| |n| |#=| |5|↵|#let| |p| |#=| |6|↵|#let| |q| |#=| |7|↵|#if| |c| |#then| |m| |+| |n| |+| |p| |+| |q| |#else| |m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |e2|(|x|)| |#=|→|x| |+| |12| |+| |13| |+| |14|←|↵|#fun| |f|(|x|)| |#=|→|#let| |c1| |#=| |is_some|(|x|)|↵|#let| |z| |#=| |e3|(|c1|)|↵|#let| |w| |#=| |#if| |x| |is|→|Some|(|a|)| |#then| |e1|(|a|,| |z|)|↵|None| |#then| |e2|(|z|)|←|↵|e0|(|w|)|←|↵|#fun| |main|(||)| |#=|→|f|(|Some|(|2|)|)| |+| |f|(|None|)|←|↵|main|(||)| -//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; class Some(x,) {}; class None {}; fun is_some = (o,) => {if o is ‹(Some(x,)) then True; (None) then False›}; fun e0 = (w,) => {+(+(+(w,)(8,),)(9,),)(10,)}; fun e1 = (a, c,) => {+(+(+(+(a,)(1,),)(2,),)(3,),)(4,)}; fun e3 = (c,) => {let m = 4; let n = 5; let p = 6; let q = 7; if (c) then +(+(+(m,)(n,),)(p,),)(q,) else +(-(+(m,)(n,),)(p,),)(q,)}; fun e2 = (x,) => {+(+(+(x,)(12,),)(13,),)(14,)}; fun f = (x,) => {let c1 = is_some(x,); let z = e3(c1,); let w = if x is ‹(Some(a,)) then e1(a, z,); (None) then e2(z,)›; e0(w,)}; fun main = () => {+(f(Some(2,),),)(f(None,),)}; main()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, Cons, [h,t]),ClassInfo(5, Nil, []),ClassInfo(6, Some, [x]),ClassInfo(7, None, [])}, { -//│ Def(0, is_some, [o$0], -//│ 1, -//│ case o$0 of -- #11 -//│ Some => -//│ let x$1 = o$0.x in -- #7 -//│ let x$2 = True() in -- #6 -//│ jump j$0(x$2) -- #5 -//│ None => -//│ let x$3 = False() in -- #10 -//│ jump j$0(x$3) -- #9 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, e0, [w$0], -//│ 1, -//│ let x$4 = +(w$0,8) in -- #21 -//│ let x$5 = +(x$4,9) in -- #20 -//│ let x$6 = +(x$5,10) in -- #19 -//│ x$6 -- #18 -//│ ) -//│ Def(3, e1, [a$0,c$0], -//│ 1, -//│ let x$7 = +(a$0,1) in -- #34 -//│ let x$8 = +(x$7,2) in -- #33 -//│ let x$9 = +(x$8,3) in -- #32 -//│ let x$10 = +(x$9,4) in -- #31 -//│ x$10 -- #30 -//│ ) -//│ Def(4, e3, [c$1], -//│ 1, -//│ let x$11 = 4 in -- #67 -//│ let x$12 = 5 in -- #66 -//│ let x$13 = 6 in -- #65 -//│ let x$14 = 7 in -- #64 -//│ if c$1 -- #63 -//│ true => -//│ let x$16 = +(x$11,x$12) in -- #51 -//│ let x$17 = +(x$16,x$13) in -- #50 -//│ let x$18 = +(x$17,x$14) in -- #49 -//│ jump j$1(x$18) -- #48 -//│ false => -//│ let x$19 = +(x$11,x$12) in -- #62 -//│ let x$20 = -(x$19,x$13) in -- #61 -//│ let x$21 = +(x$20,x$14) in -- #60 -//│ jump j$1(x$21) -- #59 -//│ ) -//│ Def(5, j$1, [x$15], -//│ 1, -//│ x$15 -- #40 -//│ ) -//│ Def(6, e2, [x$22], -//│ 1, -//│ let x$23 = +(x$22,12) in -- #77 -//│ let x$24 = +(x$23,13) in -- #76 -//│ let x$25 = +(x$24,14) in -- #75 -//│ x$25 -- #74 -//│ ) -//│ Def(7, f, [x$26], -//│ 1, -//│ let* (x$27) = is_some(x$26) in -- #117 -//│ let* (x$28) = e3(x$27) in -- #116 -//│ case x$26 of -- #115 -//│ Some => -//│ let x$31 = x$26.x in -- #107 -//│ let* (x$32) = e1(x$31,x$28) in -- #106 -//│ jump j$2(x$32) -- #105 -//│ None => -//│ let* (x$33) = e2(x$28) in -- #114 -//│ jump j$2(x$33) -- #113 -//│ ) -//│ Def(8, j$2, [x$29], -//│ 1, -//│ let* (x$30) = e0(x$29) in -- #95 -//│ x$30 -- #94 -//│ ) -//│ Def(9, main, [], -//│ 1, -//│ let x$34 = Some(2) in -- #136 -//│ let* (x$35) = f(x$34) in -- #135 -//│ let x$36 = None() in -- #134 -//│ let* (x$37) = f(x$36) in -- #133 -//│ let x$38 = +(x$35,x$37) in -- #132 -//│ x$38 -- #131 -//│ ) -//│ }, -//│ let* (x$39) = main() in -- #140 -//│ x$39 -- #139) +//│ |#fun| |is_some|(|o|)| |#=|→|#if| |o| |is|→|Some|(|x|)| |#then| |True|↵|None| |#then| |False|←|←|↵|#fun| |e0|(|w|)| |#=|→|w| |+| |8| |+| |9| |+| |10|←|↵|#fun| |e1|(|a|,| |c|)| |#=|→|a| |+| |1| |+| |2| |+| |3| |+| |4|←|↵|#fun| |e3|(|c|)| |#=|→|#let| |m| |#=| |4|↵|#let| |n| |#=| |5|↵|#let| |p| |#=| |6|↵|#let| |q| |#=| |7|↵|#if| |c| |#then| |m| |+| |n| |+| |p| |+| |q| |#else| |m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |e2|(|x|)| |#=|→|x| |+| |12| |+| |13| |+| |14|←|↵|#fun| |f|(|x|)| |#=|→|#let| |c1| |#=| |is_some|(|x|)|↵|#let| |z| |#=| |e3|(|c1|)|↵|#let| |w| |#=| |#if| |x| |is|→|Some|(|a|)| |#then| |e1|(|a|,| |z|)|↵|None| |#then| |e2|(|z|)|←|↵|e0|(|w|)|←|↵|#fun| |main|(||)| |#=|→|f|(|Some|(|2|)|)| |+| |f|(|None|)|←|↵|main|(||)| +//│ Parsed: {fun is_some = (o,) => {if o is ‹(Some(x,)) then True; (None) then False›}; fun e0 = (w,) => {+(+(+(w, 8,), 9,), 10,)}; fun e1 = (a, c,) => {+(+(+(+(a, 1,), 2,), 3,), 4,)}; fun e3 = (c,) => {let m = 4; let n = 5; let p = 6; let q = 7; if (c) then +(+(+(m, n,), p,), q,) else +(-(+(m, n,), p,), q,)}; fun e2 = (x,) => {+(+(+(x, 12,), 13,), 14,)}; fun f = (x,) => {let c1 = is_some(x,); let z = e3(c1,); let w = if x is ‹(Some(a,)) then e1(a, z,); (None) then e2(z,)›; e0(w,)}; fun main = () => {+(f(Some(2,),), f(None,),)}; main()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def is_some(o$0) = +//│ case o$0 of -- #16 +//│ Some => +//│ let x$2 = Some.x(o$0) in -- #12 +//│ let x$3 = True() in -- #11 +//│ jump j$0(x$3) -- #10 +//│ None => +//│ let x$4 = False() in -- #15 +//│ jump j$0(x$4) -- #14 +//│ def j$0(x$1) = +//│ x$1 -- #4 +//│ def e0(w$0) = +//│ let x$5 = +(w$0,8) in -- #35 +//│ let x$6 = +(x$5,9) in -- #34 +//│ let x$7 = +(x$6,10) in -- #33 +//│ x$7 -- #32 +//│ def e1(a$0,c$0) = +//│ let x$8 = +(a$0,1) in -- #60 +//│ let x$9 = +(x$8,2) in -- #59 +//│ let x$10 = +(x$9,3) in -- #58 +//│ let x$11 = +(x$10,4) in -- #57 +//│ x$11 -- #56 +//│ def e3(c$1) = +//│ let x$12 = 4 in -- #111 +//│ let x$13 = 5 in -- #110 +//│ let x$14 = 6 in -- #109 +//│ let x$15 = 7 in -- #108 +//│ if c$1 -- #107 +//│ true => +//│ let x$17 = +(x$12,x$13) in -- #86 +//│ let x$18 = +(x$17,x$14) in -- #85 +//│ let x$19 = +(x$18,x$15) in -- #84 +//│ jump j$1(x$19) -- #83 +//│ false => +//│ let x$20 = +(x$12,x$13) in -- #106 +//│ let x$21 = -(x$20,x$14) in -- #105 +//│ let x$22 = +(x$21,x$15) in -- #104 +//│ jump j$1(x$22) -- #103 +//│ def j$1(x$16) = +//│ x$16 -- #66 +//│ def e2(x$23) = +//│ let x$24 = +(x$23,12) in -- #130 +//│ let x$25 = +(x$24,13) in -- #129 +//│ let x$26 = +(x$25,14) in -- #128 +//│ x$26 -- #127 +//│ def f(x$27) = +//│ let* (x$28) = is_some(x$27) in -- #167 +//│ let* (x$29) = e3(x$28) in -- #166 +//│ case x$27 of -- #165 +//│ Some => +//│ let x$32 = Some.x(x$27) in -- #158 +//│ let* (x$33) = e1(x$32,x$29) in -- #157 +//│ jump j$2(x$33) -- #156 +//│ None => +//│ let* (x$34) = e2(x$29) in -- #164 +//│ jump j$2(x$34) -- #163 +//│ def j$2(x$30) = +//│ let* (x$31) = e0(x$30) in -- #145 +//│ x$31 -- #144 +//│ def main() = +//│ let x$35 = Some(2) in -- #187 +//│ let* (x$36) = f(x$35) in -- #186 +//│ let x$37 = None() in -- #185 +//│ let* (x$38) = f(x$37) in -- #184 +//│ let x$39 = +(x$36,x$38) in -- #183 +//│ x$39 -- #182 +//│ let* (x$0) = main() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: //│ 115 +//│ :interpIR -class True -class False -class Cons(h, t) -class Nil -class Some(x) -class None +:genCpp fun is_some(o) = if o is Some(x) then True @@ -1112,109 +942,198 @@ fun f(x) = fun main() = f(Some(2)) + f(None) main() -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#class| |Some|(|x|)|↵|#class| |None|↵|#fun| |is_some|(|o|)| |#=|→|#if| |o| |is|→|Some|(|x|)| |#then| |True|↵|None| |#then| |False|←|←|↵|#fun| |e0|(|w|)| |#=|→|w| |+| |8| |+| |9| |+| |10|←|↵|#fun| |e1|(|a|,| |z|)| |#=|→|#if| |a| |>| |0| |#then| |f|(|Some|(|a| |-| |1|)|)| |#else| |z|←|↵|#fun| |e3|(|c|)| |#=|→|#let| |m| |#=| |4|↵|#let| |n| |#=| |5|↵|#let| |p| |#=| |6|↵|#let| |q| |#=| |7|↵|#if| |c| |#then| |m| |+| |n| |+| |p| |+| |q| |#else| |m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |e2|(|x|)| |#=|→|x| |+| |12| |+| |13| |+| |14|←|↵|#fun| |f|(|x|)| |#=|→|#let| |c1| |#=| |is_some|(|x|)|↵|#let| |z| |#=| |e3|(|c1|)|↵|#let| |w| |#=| |#if| |x| |is|→|Some|(|a|)| |#then| |e1|(|a|,| |z|)|↵|None| |#then| |e2|(|z|)|←|↵|e0|(|w|)|←|↵|#fun| |main|(||)| |#=|→|f|(|Some|(|2|)|)| |+| |f|(|None|)|←|↵|main|(||)| -//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; class Some(x,) {}; class None {}; fun is_some = (o,) => {if o is ‹(Some(x,)) then True; (None) then False›}; fun e0 = (w,) => {+(+(+(w,)(8,),)(9,),)(10,)}; fun e1 = (a, z,) => {if (>(a,)(0,)) then f(Some(-(a,)(1,),),) else z}; fun e3 = (c,) => {let m = 4; let n = 5; let p = 6; let q = 7; if (c) then +(+(+(m,)(n,),)(p,),)(q,) else +(-(+(m,)(n,),)(p,),)(q,)}; fun e2 = (x,) => {+(+(+(x,)(12,),)(13,),)(14,)}; fun f = (x,) => {let c1 = is_some(x,); let z = e3(c1,); let w = if x is ‹(Some(a,)) then e1(a, z,); (None) then e2(z,)›; e0(w,)}; fun main = () => {+(f(Some(2,),),)(f(None,),)}; main()} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, Cons, [h,t]),ClassInfo(5, Nil, []),ClassInfo(6, Some, [x]),ClassInfo(7, None, [])}, { -//│ Def(0, is_some, [o$0], -//│ 1, -//│ case o$0 of -- #11 -//│ Some => -//│ let x$1 = o$0.x in -- #7 -//│ let x$2 = True() in -- #6 -//│ jump j$0(x$2) -- #5 -//│ None => -//│ let x$3 = False() in -- #10 -//│ jump j$0(x$3) -- #9 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, e0, [w$0], -//│ 1, -//│ let x$4 = +(w$0,8) in -- #21 -//│ let x$5 = +(x$4,9) in -- #20 -//│ let x$6 = +(x$5,10) in -- #19 -//│ x$6 -- #18 -//│ ) -//│ Def(3, e1, [a$0,z$0], -//│ 1, -//│ let x$7 = >(a$0,0) in -- #43 -//│ if x$7 -- #42 -//│ true => -//│ let x$9 = -(a$0,1) in -- #39 -//│ let x$10 = Some(x$9) in -- #38 -//│ let* (x$11) = f(x$10) in -- #37 -//│ jump j$1(x$11) -- #36 -//│ false => -//│ jump j$1(z$0) -- #41 -//│ ) -//│ Def(4, j$1, [x$8], -//│ 1, -//│ x$8 -- #25 -//│ ) -//│ Def(5, e3, [c$0], -//│ 1, -//│ let x$12 = 4 in -- #76 -//│ let x$13 = 5 in -- #75 -//│ let x$14 = 6 in -- #74 -//│ let x$15 = 7 in -- #73 -//│ if c$0 -- #72 -//│ true => -//│ let x$17 = +(x$12,x$13) in -- #60 -//│ let x$18 = +(x$17,x$14) in -- #59 -//│ let x$19 = +(x$18,x$15) in -- #58 -//│ jump j$2(x$19) -- #57 -//│ false => -//│ let x$20 = +(x$12,x$13) in -- #71 -//│ let x$21 = -(x$20,x$14) in -- #70 -//│ let x$22 = +(x$21,x$15) in -- #69 -//│ jump j$2(x$22) -- #68 -//│ ) -//│ Def(6, j$2, [x$16], -//│ 1, -//│ x$16 -- #49 -//│ ) -//│ Def(7, e2, [x$23], -//│ 1, -//│ let x$24 = +(x$23,12) in -- #86 -//│ let x$25 = +(x$24,13) in -- #85 -//│ let x$26 = +(x$25,14) in -- #84 -//│ x$26 -- #83 -//│ ) -//│ Def(8, f, [x$27], -//│ 1, -//│ let* (x$28) = is_some(x$27) in -- #126 -//│ let* (x$29) = e3(x$28) in -- #125 -//│ case x$27 of -- #124 -//│ Some => -//│ let x$32 = x$27.x in -- #116 -//│ let* (x$33) = e1(x$32,x$29) in -- #115 -//│ jump j$3(x$33) -- #114 -//│ None => -//│ let* (x$34) = e2(x$29) in -- #123 -//│ jump j$3(x$34) -- #122 -//│ ) -//│ Def(9, j$3, [x$30], -//│ 1, -//│ let* (x$31) = e0(x$30) in -- #104 -//│ x$31 -- #103 -//│ ) -//│ Def(10, main, [], -//│ 1, -//│ let x$35 = Some(2) in -- #145 -//│ let* (x$36) = f(x$35) in -- #144 -//│ let x$37 = None() in -- #143 -//│ let* (x$38) = f(x$37) in -- #142 -//│ let x$39 = +(x$36,x$38) in -- #141 -//│ x$39 -- #140 -//│ ) -//│ }, -//│ let* (x$40) = main() in -- #149 -//│ x$40 -- #148) +//│ |#fun| |is_some|(|o|)| |#=|→|#if| |o| |is|→|Some|(|x|)| |#then| |True|↵|None| |#then| |False|←|←|↵|#fun| |e0|(|w|)| |#=|→|w| |+| |8| |+| |9| |+| |10|←|↵|#fun| |e1|(|a|,| |z|)| |#=|→|#if| |a| |>| |0| |#then| |f|(|Some|(|a| |-| |1|)|)| |#else| |z|←|↵|#fun| |e3|(|c|)| |#=|→|#let| |m| |#=| |4|↵|#let| |n| |#=| |5|↵|#let| |p| |#=| |6|↵|#let| |q| |#=| |7|↵|#if| |c| |#then| |m| |+| |n| |+| |p| |+| |q| |#else| |m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |e2|(|x|)| |#=|→|x| |+| |12| |+| |13| |+| |14|←|↵|#fun| |f|(|x|)| |#=|→|#let| |c1| |#=| |is_some|(|x|)|↵|#let| |z| |#=| |e3|(|c1|)|↵|#let| |w| |#=| |#if| |x| |is|→|Some|(|a|)| |#then| |e1|(|a|,| |z|)|↵|None| |#then| |e2|(|z|)|←|↵|e0|(|w|)|←|↵|#fun| |main|(||)| |#=|→|f|(|Some|(|2|)|)| |+| |f|(|None|)|←|↵|main|(||)| +//│ Parsed: {fun is_some = (o,) => {if o is ‹(Some(x,)) then True; (None) then False›}; fun e0 = (w,) => {+(+(+(w, 8,), 9,), 10,)}; fun e1 = (a, z,) => {if (>(a, 0,)) then f(Some(-(a, 1,),),) else z}; fun e3 = (c,) => {let m = 4; let n = 5; let p = 6; let q = 7; if (c) then +(+(+(m, n,), p,), q,) else +(-(+(m, n,), p,), q,)}; fun e2 = (x,) => {+(+(+(x, 12,), 13,), 14,)}; fun f = (x,) => {let c1 = is_some(x,); let z = e3(c1,); let w = if x is ‹(Some(a,)) then e1(a, z,); (None) then e2(z,)›; e0(w,)}; fun main = () => {+(f(Some(2,),), f(None,),)}; main()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def is_some(o$0) = +//│ case o$0 of -- #16 +//│ Some => +//│ let x$2 = Some.x(o$0) in -- #12 +//│ let x$3 = True() in -- #11 +//│ jump j$0(x$3) -- #10 +//│ None => +//│ let x$4 = False() in -- #15 +//│ jump j$0(x$4) -- #14 +//│ def j$0(x$1) = +//│ x$1 -- #4 +//│ def e0(w$0) = +//│ let x$5 = +(w$0,8) in -- #35 +//│ let x$6 = +(x$5,9) in -- #34 +//│ let x$7 = +(x$6,10) in -- #33 +//│ x$7 -- #32 +//│ def e1(a$0,z$0) = +//│ let x$8 = >(a$0,0) in -- #62 +//│ if x$8 -- #61 +//│ true => +//│ let x$10 = -(a$0,1) in -- #58 +//│ let x$11 = Some(x$10) in -- #57 +//│ let* (x$12) = f(x$11) in -- #56 +//│ jump j$1(x$12) -- #55 +//│ false => +//│ jump j$1(z$0) -- #60 +//│ def j$1(x$9) = +//│ x$9 -- #42 +//│ def e3(c$0) = +//│ let x$13 = 4 in -- #113 +//│ let x$14 = 5 in -- #112 +//│ let x$15 = 6 in -- #111 +//│ let x$16 = 7 in -- #110 +//│ if c$0 -- #109 +//│ true => +//│ let x$18 = +(x$13,x$14) in -- #88 +//│ let x$19 = +(x$18,x$15) in -- #87 +//│ let x$20 = +(x$19,x$16) in -- #86 +//│ jump j$2(x$20) -- #85 +//│ false => +//│ let x$21 = +(x$13,x$14) in -- #108 +//│ let x$22 = -(x$21,x$15) in -- #107 +//│ let x$23 = +(x$22,x$16) in -- #106 +//│ jump j$2(x$23) -- #105 +//│ def j$2(x$17) = +//│ x$17 -- #68 +//│ def e2(x$24) = +//│ let x$25 = +(x$24,12) in -- #132 +//│ let x$26 = +(x$25,13) in -- #131 +//│ let x$27 = +(x$26,14) in -- #130 +//│ x$27 -- #129 +//│ def f(x$28) = +//│ let* (x$29) = is_some(x$28) in -- #169 +//│ let* (x$30) = e3(x$29) in -- #168 +//│ case x$28 of -- #167 +//│ Some => +//│ let x$33 = Some.x(x$28) in -- #160 +//│ let* (x$34) = e1(x$33,x$30) in -- #159 +//│ jump j$3(x$34) -- #158 +//│ None => +//│ let* (x$35) = e2(x$30) in -- #166 +//│ jump j$3(x$35) -- #165 +//│ def j$3(x$31) = +//│ let* (x$32) = e0(x$31) in -- #147 +//│ x$32 -- #146 +//│ def main() = +//│ let x$36 = Some(2) in -- #189 +//│ let* (x$37) = f(x$36) in -- #188 +//│ let x$38 = None() in -- #187 +//│ let* (x$39) = f(x$38) in -- #186 +//│ let x$40 = +(x$37,x$39) in -- #185 +//│ x$40 -- #184 +//│ let* (x$0) = main() in -- #2 +//│ x$0 -- #1 //│ //│ Interpreted: +//│ 179 +//│ + +:genCpp +fun pred(n) = + if n is + S(p) then p + O then O +fun plus(n1, n2) = + if n1 is + O then n2 + S(p) then S(plus(p, n2)) +fun fib(n) = + if n is + O then S(O) + S(p) then + if p is + O then S(O) + S(q) then plus(fib(p), fib(q)) +fun to_int(n) = + if n is + O then 0 + S(p) then 1 + to_int(p) +fun to_nat(n) = + if n == 0 then O + else S(to_nat(n - 1)) +fun main() = + to_int(fib(to_nat(30))) +main() +//│ |#fun| |pred|(|n|)| |#=|→|#if| |n| |is|→|S|(|p|)| |#then| |p|↵|O| |#then| |O|←|←|↵|#fun| |plus|(|n1|,| |n2|)| |#=|→|#if| |n1| |is|→|O| |#then| |n2|↵|S|(|p|)| |#then| |S|(|plus|(|p|,| |n2|)|)|←|←|↵|#fun| |fib|(|n|)| |#=|→|#if| |n| |is|→|O| |#then| |S|(|O|)|↵|S|(|p|)| |#then|→|#if| |p| |is|→|O| |#then| |S|(|O|)|↵|S|(|q|)| |#then| |plus|(|fib|(|p|)|,| |fib|(|q|)|)|←|←|←|←|↵|#fun| |to_int|(|n|)| |#=|→|#if| |n| |is|→|O| |#then| |0|↵|S|(|p|)| |#then| |1| |+| |to_int|(|p|)|←|←|↵|#fun| |to_nat|(|n|)| |#=|→|#if| |n| |==| |0| |#then| |O|↵|#else| |S|(|to_nat|(|n| |-| |1|)|)|←|↵|#fun| |main|(||)| |#=|→|to_int|(|fib|(|to_nat|(|30|)|)|)|←|↵|main|(||)| +//│ Parsed: {fun pred = (n,) => {if n is ‹(S(p,)) then p; (O) then O›}; fun plus = (n1, n2,) => {if n1 is ‹(O) then n2; (S(p,)) then S(plus(p, n2,),)›}; fun fib = (n,) => {if n is ‹(O) then S(O,); (S(p,)) then {if p is ‹(O) then S(O,); (S(q,)) then plus(fib(p,), fib(q,),)›}›}; fun to_int = (n,) => {if n is ‹(O) then 0; (S(p,)) then +(1, to_int(p,),)›}; fun to_nat = (n,) => {if (==(n, 0,)) then O else S(to_nat(-(n, 1,),),)}; fun main = () => {to_int(fib(to_nat(30,),),)}; main()} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def pred(n$0) = +//│ case n$0 of -- #15 +//│ S => +//│ let x$2 = S.s(n$0) in -- #11 +//│ jump j$0(x$2) -- #10 +//│ O => +//│ let x$3 = O() in -- #14 +//│ jump j$0(x$3) -- #13 +//│ def j$0(x$1) = +//│ x$1 -- #4 +//│ def plus(n1$0,n2$0) = +//│ case n1$0 of -- #37 +//│ O => +//│ jump j$1(n2$0) -- #19 +//│ S => +//│ let x$5 = S.s(n1$0) in -- #36 +//│ let* (x$6) = plus(x$5,n2$0) in -- #35 +//│ let x$7 = S(x$6) in -- #34 +//│ jump j$1(x$7) -- #33 +//│ def j$1(x$4) = +//│ x$4 -- #17 +//│ def fib(n$1) = +//│ case n$1 of -- #84 +//│ O => +//│ let x$9 = O() in -- #46 +//│ let x$10 = S(x$9) in -- #45 +//│ jump j$2(x$10) -- #44 +//│ S => +//│ let x$11 = S.s(n$1) in -- #83 +//│ case x$11 of -- #82 +//│ O => +//│ let x$13 = O() in -- #60 +//│ let x$14 = S(x$13) in -- #59 +//│ jump j$3(x$14) -- #58 +//│ S => +//│ let x$15 = S.s(x$11) in -- #81 +//│ let* (x$16) = fib(x$11) in -- #80 +//│ let* (x$17) = fib(x$15) in -- #79 +//│ let* (x$18) = plus(x$16,x$17) in -- #78 +//│ jump j$3(x$18) -- #77 +//│ def j$2(x$8) = +//│ x$8 -- #39 +//│ def j$3(x$12) = +//│ jump j$2(x$12) -- #53 +//│ def to_int(n$2) = +//│ case n$2 of -- #106 +//│ O => +//│ jump j$4(0) -- #88 +//│ S => +//│ let x$20 = S.s(n$2) in -- #105 +//│ let* (x$21) = to_int(x$20) in -- #104 +//│ let x$22 = +(1,x$21) in -- #103 +//│ jump j$4(x$22) -- #102 +//│ def j$4(x$19) = +//│ x$19 -- #86 +//│ def to_nat(n$3) = +//│ let x$23 = ==(n$3,0) in -- #134 +//│ if x$23 -- #133 +//│ true => +//│ let x$25 = O() in -- #116 +//│ jump j$5(x$25) -- #115 +//│ false => +//│ let x$26 = -(n$3,1) in -- #132 +//│ let* (x$27) = to_nat(x$26) in -- #131 +//│ let x$28 = S(x$27) in -- #130 +//│ jump j$5(x$28) -- #129 +//│ def j$5(x$24) = +//│ x$24 -- #113 +//│ def main() = +//│ let* (x$29) = to_nat(30) in -- #147 +//│ let* (x$30) = fib(x$29) in -- #146 +//│ let* (x$31) = to_int(x$30) in -- #145 +//│ x$31 -- #144 +//│ let* (x$0) = main() in -- #2 +//│ x$0 -- #1 //│ -//│ IR Processing Failed: can not find the matched case, ClassInfo(0, True, []) expected diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index de308508..4fb0b7e8 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -2,6 +2,35 @@ :ParseOnly :UseIR +:prelude +module True +module False +module Callable { + fun apply0() = 0 + fun apply1(x0) = 0 + fun apply2(x0,x1) = 0 + fun apply3(x0,x1,x2) = 0 + fun apply4(x0,x1,x2,x3) = 0 + fun apply5(x0,x1,x2,x3,x4) = 0 +} +module List[A, B] +class Cons[A, B](h: A, t: Cons[A, B]) extends List[A, B] +module Nil[A, B] extends List[A, B] +module Option[A] +class Some[A](x: A) extends Option[A] +module None[A] extends Option[A] +class Pair[A, B](x: A, y: B) +class Tuple2[A, B](x: A, y: B) +class Tuple3[A, B, C](x: A, y: B, z: C) +module Nat +class S(s: Nat) extends Nat +module O extends Nat +class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O) +//│ |#module| |True|↵|#module| |False|↵|#module| |Callable| |{|→|#fun| |apply0|(||)| |#=| |0|↵|#fun| |apply1|(|x0|)| |#=| |0|↵|#fun| |apply2|(|x0|,|x1|)| |#=| |0|↵|#fun| |apply3|(|x0|,|x1|,|x2|)| |#=| |0|↵|#fun| |apply4|(|x0|,|x1|,|x2|,|x3|)| |#=| |0|↵|#fun| |apply5|(|x0|,|x1|,|x2|,|x3|,|x4|)| |#=| |0|←|↵|}|↵|#module| |List|[|A|,| |B|]|↵|#class| |Cons|[|A|,| |B|]|(|h|#:| |A|,| |t|#:| |Cons|[|A|,| |B|]|)| |#extends| |List|[|A|,| |B|]|↵|#module| |Nil|[|A|,| |B|]| |#extends| |List|[|A|,| |B|]|↵|#module| |Option|[|A|]|↵|#class| |Some|[|A|]|(|x|#:| |A|)| |#extends| |Option|[|A|]|↵|#module| |None|[|A|]| |#extends| |Option|[|A|]|↵|#class| |Pair|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple2|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple3|[|A|,| |B|,| |C|]|(|x|#:| |A|,| |y|#:| |B|,| |z|#:| |C|)|↵|#module| |Nat|↵|#class| |S|(|s|#:| |Nat|)| |#extends| |Nat|↵|#module| |O| |#extends| |Nat|↵|#class| |HiddenTheseEntities|(|_0|#:| |HiddenTheseEntities|,| |_1|#:| |True|,| |_2|#:| |False|,| |_3|#:| |Callable|,| |_4|#:| |List|,| |_5|#:| |Cons|,| |_6|#:| |Nil|,| |_7|#:| |Option|,| |_8|#:| |Some|,| |_9|#:| |None|,| |_10|#:| |Pair|,| |_11|#:| |Tuple2|,| |_12|#:| |Tuple3|,| |_13|#:| |Nat|,| |_14|#:| |S|,| |_15|#:| |O|)| +//│ Parsed: {module True {}; module False {}; module Callable {fun apply0 = () => 0; fun apply1 = (x0,) => 0; fun apply2 = (x0, x1,) => 0; fun apply3 = (x0, x1, x2,) => 0; fun apply4 = (x0, x1, x2, x3,) => 0; fun apply5 = (x0, x1, x2, x3, x4,) => 0}; module List‹A, B› {}; class Cons‹A, B›(h: A, t: Cons‹A, B›,): List‹A, B› {}; module Nil‹A, B›: List‹A, B› {}; module Option‹A› {}; class Some‹A›(x: A,): Option‹A› {}; module None‹A›: Option‹A› {}; class Pair‹A, B›(x: A, y: B,) {}; class Tuple2‹A, B›(x: A, y: B,) {}; class Tuple3‹A, B, C›(x: A, y: B, z: C,) {}; module Nat {}; class S(s: Nat,): Nat {}; module O: Nat {}; class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O,) {}} +//│ +//│ Preluded. + :noTailRec :interpIR fun fact(acc, n) = @@ -11,27 +40,24 @@ fact(1, 5) //│ |#fun| |fact|(|acc|,| |n|)| |#=|→|#if| |n| |==| |0| |#then| |acc|↵|#else| |fact|(|acc| |*| |n|,| |n| |-| |1|)|←|↵|fact|(|1|,| |5|)| //│ Parsed: {fun fact = (acc, n,) => {if (==(n,)(0,)) then acc else fact(*(acc,)(n,), -(n,)(1,),)}; fact(1, 5,)} //│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, fact, [acc$0,n$0], -//│ 1, -//│ let x$0 = ==(n$0,0) in -- #22 -//│ if x$0 -- #21 -//│ true => -//│ jump j$0(acc$0) -- #5 -//│ false => -//│ let x$2 = *(acc$0,n$0) in -- #20 -//│ let x$3 = -(n$0,1) in -- #19 -//│ let* (x$4) = fact(x$2,x$3) in -- #18 -//│ jump j$0(x$4) -- #17 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ }, -//│ let* (x$5) = fact(1,5) in -- #30 -//│ x$5 -- #29) +//│ +//│ IR: +//│ Program: +//│ +//│ def fact(acc$0,n$0) = +//│ let x$1 = ==(n$0,0) in -- #28 +//│ if x$1 -- #27 +//│ true => +//│ jump j$0(acc$0) -- #12 +//│ false => +//│ let x$3 = *(acc$0,n$0) in -- #26 +//│ let x$4 = -(n$0,1) in -- #25 +//│ let* (x$5) = fact(x$3,x$4) in -- #24 +//│ jump j$0(x$5) -- #23 +//│ def j$0(x$2) = +//│ x$2 -- #10 +//│ let* (x$0) = fact(1,5) in -- #6 +//│ x$0 -- #5 //│ //│ Interpreted: //│ 120 @@ -45,80 +71,47 @@ fact(1, 5) //│ |@|tailrec|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#if| |n| |==| |0| |#then| |acc|↵|#else| |fact|(|acc| |*| |n|,| |n| |-| |1|)|←|↵|fact|(|1|,| |5|)| //│ Parsed: {fun fact = (acc, n,) => {if (==(n,)(0,)) then acc else fact(*(acc,)(n,), -(n,)(1,),)}; fact(1, 5,)} //│ +//│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, fact, [acc$0,n$0], -//│ 1, -//│ let x$0 = ==(n$0,0) in -- #22 -//│ if x$0 -- #21 -//│ true => -//│ jump j$0(acc$0) -- #5 -//│ false => -//│ let x$2 = *(acc$0,n$0) in -- #20 -//│ let x$3 = -(n$0,1) in -- #19 -//│ let* (x$4) = fact(x$2,x$3) in -- #18 -//│ jump j$0(x$4) -- #17 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ }, -//│ let* (x$5) = fact(1,5) in -- #30 -//│ x$5 -- #29) +//│ Program: +//│ +//│ def fact(acc$0,n$0) = +//│ let x$1 = ==(n$0,0) in -- #28 +//│ if x$1 -- #27 +//│ true => +//│ jump j$0(acc$0) -- #12 +//│ false => +//│ let x$3 = *(acc$0,n$0) in -- #26 +//│ let x$4 = -(n$0,1) in -- #25 +//│ let* (x$5) = fact(x$3,x$4) in -- #24 +//│ jump j$0(x$5) -- #23 +//│ def j$0(x$2) = +//│ x$2 -- #10 +//│ let* (x$0) = fact(1,5) in -- #6 +//│ x$0 -- #5 +//│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(fact)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ Def(2, fact_jp, [acc$0,n$0], -//│ 1, -//│ let x$0 = ==(n$0,0) in -- #36 -//│ if x$0 -- #35 -//│ true => -//│ jump j$0(acc$0) -- #31 -//│ false => -//│ let x$2 = *(acc$0,n$0) in -- #34 -//│ let x$3 = -(n$0,1) in -- #33 -//│ jump fact_jp(x$2,x$3) -- #32 -//│ ) -//│ Def(3, fact, [acc$0,n$0], -//│ 1, -//│ let* (r0) = fact_jp(acc$0,n$0) in -- #38 -//│ r0 -- #37 -//│ ) -//│ }, -//│ let* (x$5) = fact(1,5) in -- #30 -//│ x$5 -- #29) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ Def(2, fact_jp, [acc$0,n$0], -//│ 1, -//│ let x$0 = ==(n$0,0) in -- #36 -//│ if x$0 -- #35 -//│ true => -//│ jump j$0(acc$0) -- #31 -//│ false => -//│ let x$2 = *(acc$0,n$0) in -- #34 -//│ let x$3 = -(n$0,1) in -- #33 -//│ jump fact_jp(x$2,x$3) -- #32 -//│ ) -//│ Def(3, fact, [acc$0,n$0], -//│ 1, -//│ let* (r0) = fact_jp(acc$0,n$0) in -- #38 -//│ r0 -- #37 -//│ ) -//│ }, -//│ let* (x$5) = fact(1,5) in -- #30 -//│ x$5 -- #29) +//│ Program: +//│ class _IdContext() +//│ class _Context(acc,ptr,field) +//│ def j$0(x$2) = +//│ x$2 -- #10 +//│ def fact_jp(acc$0,n$0) = +//│ let x$1 = ==(n$0,0) in -- #40 +//│ if x$1 -- #39 +//│ true => +//│ jump j$0(acc$0) -- #35 +//│ false => +//│ let x$3 = *(acc$0,n$0) in -- #38 +//│ let x$4 = -(n$0,1) in -- #37 +//│ jump fact_jp(x$3,x$4) -- #36 +//│ def fact(acc$0,n$0) = +//│ let* (r0) = fact_jp(acc$0,n$0) in -- #42 +//│ r0 -- #41 +//│ let* (x$0) = fact(1,5) in -- #6 +//│ x$0 -- #5 //│ //│ Interpreted: //│ 120 @@ -136,117 +129,66 @@ fact(1, 5) //│ |@|tailrec|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#val| |x| |#=| |#if| |n| |>| |0| |#then| |n| |-| |1|→|#else| |0|←|↵|#if| |x| |<=| |0| |#then|→|acc|←|↵|#else| |→|@|tailcall| |fact|(|n| |*| |acc|,| |x|)| |←|←|↵|fact|(|1|,| |5|)| //│ Parsed: {fun fact = (acc, n,) => {let x = if (>(n,)(0,)) then -(n,)(1,) else 0; if (<=(x,)(0,)) then {acc} else {@tailcall fact(*(n,)(acc,), x,)}}; fact(1, 5,)} //│ +//│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, fact, [acc$0,n$0], -//│ 1, -//│ let x$0 = >(n$0,0) in -- #32 -//│ if x$0 -- #31 -//│ true => -//│ let x$6 = -(n$0,1) in -- #28 -//│ jump j$0(x$6,acc$0,n$0) -- #27 -//│ false => -//│ jump j$0(0,acc$0,n$0) -- #30 -//│ ) -//│ Def(1, j$1, [x$3], -//│ 1, -//│ x$3 -- #7 -//│ ) -//│ Def(2, j$0, [x$1,acc$0,n$0], -//│ 1, -//│ let x$2 = <=(x$1,0) in -- #23 -//│ if x$2 -- #22 -//│ true => -//│ jump j$1(acc$0) -- #9 -//│ false => -//│ let x$4 = *(n$0,acc$0) in -- #21 -//│ let* (x$5) = @tailcall fact(x$4,x$1) in -- #20 -//│ jump j$1(x$5) -- #19 -//│ ) -//│ }, -//│ let* (x$7) = fact(1,5) in -- #40 -//│ x$7 -- #39) +//│ Program: //│ -//│ Strongly Connected Tail Calls: -//│ List(Set(j$1), Set(fact)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, fact, [acc$0,n$0], -//│ 1, -//│ let* (r0) = _fact_j$0_opt$3(0,acc$0,n$0,undefined,undefined,undefined) in -- #60 -//│ r0 -- #59 -//│ ) -//│ Def(1, j$1, [x$3], -//│ 1, -//│ x$3 -- #7 -//│ ) -//│ Def(3, _fact_j$0_opt$3, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], -//│ 1, -//│ jump _fact_j$0_opt_jp$4(tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0) -- #58 -//│ ) -//│ Def(4, _fact_j$0_opt_jp$4, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], -//│ 1, -//│ let scrut = ==(2,tailrecBranch$) in -- #57 -//│ if scrut -- #56 -//│ true => -//│ let x$2 = <=(j$0_x$1,0) in -- #55 -//│ if x$2 -- #54 -//│ true => -//│ jump j$1(j$0_acc$0) -- #51 -//│ false => -//│ let x$4 = *(j$0_n$0,j$0_acc$0) in -- #53 -//│ jump _fact_j$0_opt_jp$4(0,x$4,j$0_x$1,j$0_x$1,j$0_acc$0,j$0_n$0) -- #52 -//│ false => -//│ let x$0 = >(fact_n$0,0) in -- #50 -//│ if x$0 -- #49 +//│ def fact(acc$0,n$0) = +//│ let x$1 = >(n$0,0) in -- #38 +//│ if x$1 -- #37 //│ true => -//│ let x$6 = -(fact_n$0,1) in -- #47 -//│ jump _fact_j$0_opt_jp$4(2,fact_acc$0,fact_n$0,x$6,fact_acc$0,fact_n$0) -- #46 +//│ let x$7 = -(n$0,1) in -- #34 +//│ jump j$0(x$7,acc$0,n$0) -- #33 //│ false => -//│ jump _fact_j$0_opt_jp$4(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #48 -//│ ) -//│ }, -//│ let* (x$7) = fact(1,5) in -- #40 -//│ x$7 -- #39) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, fact, [acc$0,n$0], -//│ 1, -//│ let* (r0) = _fact_j$0_opt$3(0,acc$0,n$0,undefined,undefined,undefined) in -- #60 -//│ r0 -- #59 -//│ ) -//│ Def(1, j$1, [x$3], -//│ 1, -//│ x$3 -- #7 -//│ ) -//│ Def(3, _fact_j$0_opt$3, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], -//│ 1, -//│ jump _fact_j$0_opt_jp$4(tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0) -- #58 -//│ ) -//│ Def(4, _fact_j$0_opt_jp$4, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], -//│ 1, -//│ let scrut = ==(2,tailrecBranch$) in -- #57 -//│ if scrut -- #56 -//│ true => -//│ let x$2 = <=(j$0_x$1,0) in -- #55 -//│ if x$2 -- #54 +//│ jump j$0(0,acc$0,n$0) -- #36 +//│ def j$1(x$4) = +//│ x$4 -- #14 +//│ def j$0(x$2,acc$0,n$0) = +//│ let x$3 = <=(x$2,0) in -- #29 +//│ if x$3 -- #28 //│ true => -//│ jump j$1(j$0_acc$0) -- #51 +//│ jump j$1(acc$0) -- #16 //│ false => -//│ let x$4 = *(j$0_n$0,j$0_acc$0) in -- #53 -//│ jump _fact_j$0_opt_jp$4(0,x$4,j$0_x$1,j$0_x$1,j$0_acc$0,j$0_n$0) -- #52 -//│ false => -//│ let x$0 = >(fact_n$0,0) in -- #50 -//│ if x$0 -- #49 +//│ let x$5 = *(n$0,acc$0) in -- #27 +//│ let* (x$6) = @tailcall fact(x$5,x$2) in -- #26 +//│ jump j$1(x$6) -- #25 +//│ let* (x$0) = fact(1,5) in -- #6 +//│ x$0 -- #5 +//│ +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$1), Set(fact)) +//│ Program: +//│ class _IdContext() +//│ class _Context(acc,ptr,field) +//│ def fact(acc$0,n$0) = +//│ let* (r0) = _fact_j$0_opt$9(0,acc$0,n$0,true,true,true) in -- #64 +//│ r0 -- #63 +//│ def j$1(x$4) = +//│ x$4 -- #14 +//│ def _fact_j$0_opt$9(tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$2,j$0_acc$0,j$0_n$0) = +//│ jump _fact_j$0_opt_jp$10(tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$2,j$0_acc$0,j$0_n$0) -- #62 +//│ def _fact_j$0_opt_jp$10(tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$2,j$0_acc$0,j$0_n$0) = +//│ let scrut = ==(2,tailrecBranch$) in -- #61 +//│ if scrut -- #60 //│ true => -//│ let x$6 = -(fact_n$0,1) in -- #47 -//│ jump _fact_j$0_opt_jp$4(2,fact_acc$0,fact_n$0,x$6,fact_acc$0,fact_n$0) -- #46 +//│ let x$3 = <=(j$0_x$2,0) in -- #59 +//│ if x$3 -- #58 +//│ true => +//│ jump j$1(j$0_acc$0) -- #55 +//│ false => +//│ let x$5 = *(j$0_n$0,j$0_acc$0) in -- #57 +//│ jump _fact_j$0_opt_jp$10(0,x$5,j$0_x$2,j$0_x$2,j$0_acc$0,j$0_n$0) -- #56 //│ false => -//│ jump _fact_j$0_opt_jp$4(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #48 -//│ ) -//│ }, -//│ let* (x$7) = fact(1,5) in -- #40 -//│ x$7 -- #39) +//│ let x$1 = >(fact_n$0,0) in -- #54 +//│ if x$1 -- #53 +//│ true => +//│ let x$7 = -(fact_n$0,1) in -- #51 +//│ jump _fact_j$0_opt_jp$10(2,fact_acc$0,fact_n$0,x$7,fact_acc$0,fact_n$0) -- #50 +//│ false => +//│ jump _fact_j$0_opt_jp$10(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #52 +//│ let* (x$0) = fact(1,5) in -- #6 +//│ x$0 -- #5 //│ //│ Interpreted: //│ 120 @@ -260,51 +202,42 @@ g(6, 0) //│ |#fun| |double|(|x|)| |#=| |x| |*| |2|↵|#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|6|,| |0|)| //│ Parsed: {fun double = (x,) => *(x,)(2,); fun f = (n, acc,) => if (==(n,)(0,)) then double(acc,) else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(double(acc,),) else f(-(m,)(1,), +(acc,)(1,),); g(6, 0,)} //│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, double, [x$0], -//│ 1, -//│ let x$1 = *(x$0,2) in -- #3 -//│ x$1 -- #2 -//│ ) -//│ Def(1, f, [n$0,acc$0], -//│ 1, -//│ let x$2 = ==(n$0,0) in -- #31 -//│ if x$2 -- #30 -//│ true => -//│ let* (x$4) = double(acc$0) in -- #14 -//│ jump j$0(x$4) -- #13 -//│ false => -//│ let x$5 = -(n$0,1) in -- #29 -//│ let x$6 = +(acc$0,1) in -- #28 -//│ let* (x$7) = g(x$5,x$6) in -- #27 -//│ jump j$0(x$7) -- #26 -//│ ) -//│ Def(2, j$0, [x$3], -//│ 1, -//│ x$3 -- #7 -//│ ) -//│ Def(3, g, [m$0,acc$1], -//│ 1, -//│ let x$8 = ==(m$0,0) in -- #62 -//│ if x$8 -- #61 -//│ true => -//│ let* (x$10) = double(acc$1) in -- #45 -//│ let x$11 = -(0,x$10) in -- #44 -//│ jump j$1(x$11) -- #43 -//│ false => -//│ let x$12 = -(m$0,1) in -- #60 -//│ let x$13 = +(acc$1,1) in -- #59 -//│ let* (x$14) = f(x$12,x$13) in -- #58 -//│ jump j$1(x$14) -- #57 -//│ ) -//│ Def(4, j$1, [x$9], -//│ 1, -//│ x$9 -- #35 -//│ ) -//│ }, -//│ let* (x$15) = g(6,0) in -- #70 -//│ x$15 -- #69) +//│ +//│ IR: +//│ Program: +//│ +//│ def double(x$1) = +//│ let x$2 = *(x$1,2) in -- #10 +//│ x$2 -- #9 +//│ def f(n$0,acc$0) = +//│ let x$3 = ==(n$0,0) in -- #36 +//│ if x$3 -- #35 +//│ true => +//│ let* (x$5) = double(acc$0) in -- #20 +//│ jump j$0(x$5) -- #19 +//│ false => +//│ let x$6 = -(n$0,1) in -- #34 +//│ let x$7 = +(acc$0,1) in -- #33 +//│ let* (x$8) = g(x$6,x$7) in -- #32 +//│ jump j$0(x$8) -- #31 +//│ def j$0(x$4) = +//│ x$4 -- #14 +//│ def g(m$0,acc$1) = +//│ let x$9 = ==(m$0,0) in -- #65 +//│ if x$9 -- #64 +//│ true => +//│ let* (x$11) = double(acc$1) in -- #49 +//│ let x$12 = -(0,x$11) in -- #48 +//│ jump j$1(x$12) -- #47 +//│ false => +//│ let x$13 = -(m$0,1) in -- #63 +//│ let x$14 = +(acc$1,1) in -- #62 +//│ let* (x$15) = f(x$13,x$14) in -- #61 +//│ jump j$1(x$15) -- #60 +//│ def j$1(x$10) = +//│ x$10 -- #40 +//│ let* (x$0) = g(6,0) in -- #6 +//│ x$0 -- #5 //│ //│ Interpreted: //│ -12 @@ -317,170 +250,90 @@ g(6, 0) //│ |#fun| |double|(|x|)| |#=| |x| |*| |2|↵|@|tailrec| |#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|@|tailrec| |#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|6|,| |0|)| //│ Parsed: {fun double = (x,) => *(x,)(2,); fun f = (n, acc,) => if (==(n,)(0,)) then double(acc,) else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(double(acc,),) else f(-(m,)(1,), +(acc,)(1,),); g(6, 0,)} //│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, double, [x$0], -//│ 1, -//│ let x$1 = *(x$0,2) in -- #3 -//│ x$1 -- #2 -//│ ) -//│ Def(1, f, [n$0,acc$0], -//│ 1, -//│ let x$2 = ==(n$0,0) in -- #31 -//│ if x$2 -- #30 -//│ true => -//│ let* (x$4) = double(acc$0) in -- #14 -//│ jump j$0(x$4) -- #13 -//│ false => -//│ let x$5 = -(n$0,1) in -- #29 -//│ let x$6 = +(acc$0,1) in -- #28 -//│ let* (x$7) = g(x$5,x$6) in -- #27 -//│ jump j$0(x$7) -- #26 -//│ ) -//│ Def(2, j$0, [x$3], -//│ 1, -//│ x$3 -- #7 -//│ ) -//│ Def(3, g, [m$0,acc$1], -//│ 1, -//│ let x$8 = ==(m$0,0) in -- #62 -//│ if x$8 -- #61 -//│ true => -//│ let* (x$10) = double(acc$1) in -- #45 -//│ let x$11 = -(0,x$10) in -- #44 -//│ jump j$1(x$11) -- #43 -//│ false => -//│ let x$12 = -(m$0,1) in -- #60 -//│ let x$13 = +(acc$1,1) in -- #59 -//│ let* (x$14) = f(x$12,x$13) in -- #58 -//│ jump j$1(x$14) -- #57 -//│ ) -//│ Def(4, j$1, [x$9], -//│ 1, -//│ x$9 -- #35 -//│ ) -//│ }, -//│ let* (x$15) = g(6,0) in -- #70 -//│ x$15 -- #69) //│ -//│ Strongly Connected Tail Calls: -//│ List(Set(j$1), Set(j$0), Set(g, f), Set(double)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, double, [x$0], -//│ 1, -//│ let x$1 = *(x$0,2) in -- #3 -//│ x$1 -- #2 -//│ ) -//│ Def(1, f, [n$0,acc$0], -//│ 1, -//│ let* (r0) = _g_f_opt$5(1,undefined,undefined,n$0,acc$0) in -- #100 -//│ r0 -- #99 -//│ ) -//│ Def(2, j$0, [x$3], -//│ 1, -//│ x$3 -- #7 -//│ ) -//│ Def(3, g, [m$0,acc$1], -//│ 1, -//│ let* (r0) = _g_f_opt$5(3,m$0,acc$1,undefined,undefined) in -- #98 -//│ r0 -- #97 -//│ ) -//│ Def(4, j$1, [x$9], -//│ 1, -//│ x$9 -- #35 -//│ ) -//│ Def(5, _g_f_opt$5, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], -//│ 1, -//│ jump _g_f_opt_jp$6(tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #96 -//│ ) -//│ Def(6, _g_f_opt_jp$6, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], -//│ 1, -//│ let scrut = ==(1,tailrecBranch$) in -- #95 -//│ if scrut -- #94 -//│ true => -//│ let x$2 = ==(f_n$0,0) in -- #93 -//│ if x$2 -- #92 -//│ true => -//│ let* (x$4) = double(f_acc$0) in -- #88 -//│ jump j$0(x$4) -- #87 -//│ false => -//│ let x$5 = -(f_n$0,1) in -- #91 -//│ let x$6 = +(f_acc$0,1) in -- #90 -//│ jump _g_f_opt_jp$6(3,x$5,x$6,f_n$0,f_acc$0) -- #89 -//│ false => -//│ let x$8 = ==(g_m$0,0) in -- #86 -//│ if x$8 -- #85 +//│ IR: +//│ Program: +//│ +//│ def double(x$1) = +//│ let x$2 = *(x$1,2) in -- #10 +//│ x$2 -- #9 +//│ def f(n$0,acc$0) = +//│ let x$3 = ==(n$0,0) in -- #36 +//│ if x$3 -- #35 //│ true => -//│ let* (x$10) = double(g_acc$1) in -- #81 -//│ let x$11 = -(0,x$10) in -- #80 -//│ jump j$1(x$11) -- #79 +//│ let* (x$5) = double(acc$0) in -- #20 +//│ jump j$0(x$5) -- #19 //│ false => -//│ let x$12 = -(g_m$0,1) in -- #84 -//│ let x$13 = +(g_acc$1,1) in -- #83 -//│ jump _g_f_opt_jp$6(1,g_m$0,g_acc$1,x$12,x$13) -- #82 -//│ ) -//│ }, -//│ let* (x$15) = g(6,0) in -- #70 -//│ x$15 -- #69) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, double, [x$0], -//│ 1, -//│ let x$1 = *(x$0,2) in -- #3 -//│ x$1 -- #2 -//│ ) -//│ Def(1, f, [n$0,acc$0], -//│ 1, -//│ let* (r0) = _g_f_opt$5(1,undefined,undefined,n$0,acc$0) in -- #100 -//│ r0 -- #99 -//│ ) -//│ Def(2, j$0, [x$3], -//│ 1, -//│ x$3 -- #7 -//│ ) -//│ Def(3, g, [m$0,acc$1], -//│ 1, -//│ let* (r0) = _g_f_opt$5(3,m$0,acc$1,undefined,undefined) in -- #98 -//│ r0 -- #97 -//│ ) -//│ Def(4, j$1, [x$9], -//│ 1, -//│ x$9 -- #35 -//│ ) -//│ Def(5, _g_f_opt$5, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], -//│ 1, -//│ jump _g_f_opt_jp$6(tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #96 -//│ ) -//│ Def(6, _g_f_opt_jp$6, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], -//│ 1, -//│ let scrut = ==(1,tailrecBranch$) in -- #95 -//│ if scrut -- #94 -//│ true => -//│ let x$2 = ==(f_n$0,0) in -- #93 -//│ if x$2 -- #92 +//│ let x$6 = -(n$0,1) in -- #34 +//│ let x$7 = +(acc$0,1) in -- #33 +//│ let* (x$8) = g(x$6,x$7) in -- #32 +//│ jump j$0(x$8) -- #31 +//│ def j$0(x$4) = +//│ x$4 -- #14 +//│ def g(m$0,acc$1) = +//│ let x$9 = ==(m$0,0) in -- #65 +//│ if x$9 -- #64 //│ true => -//│ let* (x$4) = double(f_acc$0) in -- #88 -//│ jump j$0(x$4) -- #87 +//│ let* (x$11) = double(acc$1) in -- #49 +//│ let x$12 = -(0,x$11) in -- #48 +//│ jump j$1(x$12) -- #47 //│ false => -//│ let x$5 = -(f_n$0,1) in -- #91 -//│ let x$6 = +(f_acc$0,1) in -- #90 -//│ jump _g_f_opt_jp$6(3,x$5,x$6,f_n$0,f_acc$0) -- #89 -//│ false => -//│ let x$8 = ==(g_m$0,0) in -- #86 -//│ if x$8 -- #85 +//│ let x$13 = -(m$0,1) in -- #63 +//│ let x$14 = +(acc$1,1) in -- #62 +//│ let* (x$15) = f(x$13,x$14) in -- #61 +//│ jump j$1(x$15) -- #60 +//│ def j$1(x$10) = +//│ x$10 -- #40 +//│ let* (x$0) = g(6,0) in -- #6 +//│ x$0 -- #5 +//│ +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$1), Set(j$0), Set(g, f), Set(double)) +//│ Program: +//│ class _IdContext() +//│ class _Context(acc,ptr,field) +//│ def double(x$1) = +//│ let x$2 = *(x$1,2) in -- #10 +//│ x$2 -- #9 +//│ def f(n$0,acc$0) = +//│ let* (r0) = _g_f_opt$11(1,true,true,n$0,acc$0) in -- #101 +//│ r0 -- #100 +//│ def j$0(x$4) = +//│ x$4 -- #14 +//│ def g(m$0,acc$1) = +//│ let* (r0) = _g_f_opt$11(3,m$0,acc$1,true,true) in -- #99 +//│ r0 -- #98 +//│ def j$1(x$10) = +//│ x$10 -- #40 +//│ def _g_f_opt$11(tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0) = +//│ jump _g_f_opt_jp$12(tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #97 +//│ def _g_f_opt_jp$12(tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0) = +//│ let scrut = ==(1,tailrecBranch$) in -- #96 +//│ if scrut -- #95 //│ true => -//│ let* (x$10) = double(g_acc$1) in -- #81 -//│ let x$11 = -(0,x$10) in -- #80 -//│ jump j$1(x$11) -- #79 +//│ let x$3 = ==(f_n$0,0) in -- #94 +//│ if x$3 -- #93 +//│ true => +//│ let* (x$5) = double(f_acc$0) in -- #89 +//│ jump j$0(x$5) -- #88 +//│ false => +//│ let x$6 = -(f_n$0,1) in -- #92 +//│ let x$7 = +(f_acc$0,1) in -- #91 +//│ jump _g_f_opt_jp$12(3,x$6,x$7,f_n$0,f_acc$0) -- #90 //│ false => -//│ let x$12 = -(g_m$0,1) in -- #84 -//│ let x$13 = +(g_acc$1,1) in -- #83 -//│ jump _g_f_opt_jp$6(1,g_m$0,g_acc$1,x$12,x$13) -- #82 -//│ ) -//│ }, -//│ let* (x$15) = g(6,0) in -- #70 -//│ x$15 -- #69) +//│ let x$9 = ==(g_m$0,0) in -- #87 +//│ if x$9 -- #86 +//│ true => +//│ let* (x$11) = double(g_acc$1) in -- #82 +//│ let x$12 = -(0,x$11) in -- #81 +//│ jump j$1(x$12) -- #80 +//│ false => +//│ let x$13 = -(g_m$0,1) in -- #85 +//│ let x$14 = +(g_acc$1,1) in -- #84 +//│ jump _g_f_opt_jp$12(1,g_m$0,g_acc$1,x$13,x$14) -- #83 +//│ let* (x$0) = g(6,0) in -- #6 +//│ x$0 -- #5 //│ //│ Interpreted: //│ -12 @@ -492,102 +345,51 @@ g(6, 0) //│ |@|tailrec| |#fun| |f|(|a|,| |b|,| |c|)| |#=| |g|(|0|,| |0|)|↵|@|tailrec| |#fun| |g|(|d|,| |e|)| |#=| |h|(|0|,| |0|,| |0|,| |0|)|↵|@|tailrec| |#fun| |h|(|p|,| |q|,| |r|,| |s|)| |#=| |f|(|0|,| |0|,| |0|)|↵|2| | //│ Parsed: {fun f = (a, b, c,) => g(0, 0,); fun g = (d, e,) => h(0, 0, 0, 0,); fun h = (p, q, r, s,) => f(0, 0, 0,); 2} //│ +//│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, f, [a$0,b$0,c$0], -//│ 1, -//│ let* (x$0) = g(0,0) in -- #7 -//│ x$0 -- #6 -//│ ) -//│ Def(1, g, [d$0,e$0], -//│ 1, -//│ let* (x$1) = h(0,0,0,0) in -- #19 -//│ x$1 -- #18 -//│ ) -//│ Def(2, h, [p$0,q$0,r$0,s$0], -//│ 1, -//│ let* (x$2) = f(0,0,0) in -- #29 -//│ x$2 -- #28 -//│ ) -//│ }, -//│ 2 -- #30) +//│ Program: +//│ +//│ def f(a$0,b$0,c$0) = +//│ let* (x$0) = g(0,0) in -- #7 +//│ x$0 -- #6 +//│ def g(d$0,e$0) = +//│ let* (x$1) = h(0,0,0,0) in -- #18 +//│ x$1 -- #17 +//│ def h(p$0,q$0,r$0,s$0) = +//│ let* (x$2) = f(0,0,0) in -- #27 +//│ x$2 -- #26 +//│ 2 -- #0 +//│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(h, g, f)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, f, [a$0,b$0,c$0], -//│ 1, -//│ let* (r0) = _h_g_f_opt$3(0,undefined,undefined,undefined,undefined,undefined,undefined,a$0,b$0,c$0) in -- #45 -//│ r0 -- #44 -//│ ) -//│ Def(1, g, [d$0,e$0], -//│ 1, -//│ let* (r0) = _h_g_f_opt$3(1,undefined,undefined,undefined,undefined,d$0,e$0,undefined,undefined,undefined) in -- #43 -//│ r0 -- #42 -//│ ) -//│ Def(2, h, [p$0,q$0,r$0,s$0], -//│ 1, -//│ let* (r0) = _h_g_f_opt$3(2,p$0,q$0,r$0,s$0,undefined,undefined,undefined,undefined,undefined) in -- #41 -//│ r0 -- #40 -//│ ) -//│ Def(3, _h_g_f_opt$3, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], -//│ 1, -//│ jump _h_g_f_opt_jp$4(tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #39 -//│ ) -//│ Def(4, _h_g_f_opt_jp$4, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], -//│ 1, -//│ let scrut = ==(0,tailrecBranch$) in -- #38 -//│ if scrut -- #37 -//│ true => -//│ jump _h_g_f_opt_jp$4(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #34 -//│ false => -//│ let scrut = ==(1,tailrecBranch$) in -- #36 -//│ if scrut -- #35 -//│ true => -//│ jump _h_g_f_opt_jp$4(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #33 -//│ false => -//│ jump _h_g_f_opt_jp$4(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #32 -//│ ) -//│ }, -//│ 2 -- #30) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, f, [a$0,b$0,c$0], -//│ 1, -//│ let* (r0) = _h_g_f_opt$3(0,undefined,undefined,undefined,undefined,undefined,undefined,a$0,b$0,c$0) in -- #45 -//│ r0 -- #44 -//│ ) -//│ Def(1, g, [d$0,e$0], -//│ 1, -//│ let* (r0) = _h_g_f_opt$3(1,undefined,undefined,undefined,undefined,d$0,e$0,undefined,undefined,undefined) in -- #43 -//│ r0 -- #42 -//│ ) -//│ Def(2, h, [p$0,q$0,r$0,s$0], -//│ 1, -//│ let* (r0) = _h_g_f_opt$3(2,p$0,q$0,r$0,s$0,undefined,undefined,undefined,undefined,undefined) in -- #41 -//│ r0 -- #40 -//│ ) -//│ Def(3, _h_g_f_opt$3, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], -//│ 1, -//│ jump _h_g_f_opt_jp$4(tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #39 -//│ ) -//│ Def(4, _h_g_f_opt_jp$4, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], -//│ 1, -//│ let scrut = ==(0,tailrecBranch$) in -- #38 -//│ if scrut -- #37 -//│ true => -//│ jump _h_g_f_opt_jp$4(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #34 -//│ false => -//│ let scrut = ==(1,tailrecBranch$) in -- #36 -//│ if scrut -- #35 +//│ Program: +//│ class _IdContext() +//│ class _Context(acc,ptr,field) +//│ def f(a$0,b$0,c$0) = +//│ let* (r0) = _h_g_f_opt$9(0,true,true,true,true,true,true,a$0,b$0,c$0) in -- #48 +//│ r0 -- #47 +//│ def g(d$0,e$0) = +//│ let* (r0) = _h_g_f_opt$9(1,true,true,true,true,d$0,e$0,true,true,true) in -- #46 +//│ r0 -- #45 +//│ def h(p$0,q$0,r$0,s$0) = +//│ let* (r0) = _h_g_f_opt$9(2,p$0,q$0,r$0,s$0,true,true,true,true,true) in -- #44 +//│ r0 -- #43 +//│ def _h_g_f_opt$9(tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) = +//│ jump _h_g_f_opt_jp$10(tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #42 +//│ def _h_g_f_opt_jp$10(tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) = +//│ let scrut = ==(0,tailrecBranch$) in -- #41 +//│ if scrut -- #40 //│ true => -//│ jump _h_g_f_opt_jp$4(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #33 +//│ jump _h_g_f_opt_jp$10(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #37 //│ false => -//│ jump _h_g_f_opt_jp$4(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #32 -//│ ) -//│ }, -//│ 2 -- #30) +//│ let scrut = ==(1,tailrecBranch$) in -- #39 +//│ if scrut -- #38 +//│ true => +//│ jump _h_g_f_opt_jp$10(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #36 +//│ false => +//│ jump _h_g_f_opt_jp$10(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #35 +//│ 2 -- #0 :ce fun hello() = @@ -598,48 +400,35 @@ hello() //│ |#fun| |hello|(||)| |#=|→|@|tailcall| |hello|(||)|↵|@|tailcall| |hello|(||)|↵|2|←|↵|hello|(||)| | //│ Parsed: {fun hello = () => {@tailcall hello(); @tailcall hello(); 2}; hello()} //│ +//│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, hello, [], -//│ 1, -//│ let* (x$0) = @tailcall hello() in -- #8 -//│ let* (x$1) = @tailcall hello() in -- #7 -//│ 2 -- #6 -//│ ) -//│ }, -//│ let* (x$2) = hello() in -- #12 -//│ x$2 -- #11) +//│ Program: +//│ +//│ def hello() = +//│ let* (x$1) = @tailcall hello() in -- #9 +//│ let* (x$2) = @tailcall hello() in -- #8 +//│ 2 -- #7 +//│ let* (x$0) = hello() in -- #2 +//│ x$0 -- #1 //│ ╔══[COMPILATION ERROR] not a tail call, as the remaining functions may be impure -//│ ║ l.594: @tailcall hello() +//│ ║ l.396: @tailcall hello() //│ ╙── ^^^^^ //│ ╔══[COMPILATION ERROR] not a tail call -//│ ║ l.595: @tailcall hello() +//│ ║ l.397: @tailcall hello() //│ ╙── ^^^^^ //│ +//│ //│ Strongly Connected Tail Calls: //│ List(Set(hello)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, hello, [], -//│ 1, -//│ let* (x$0) = @tailcall hello() in -- #8 -//│ let* (x$1) = @tailcall hello() in -- #7 -//│ 2 -- #6 -//│ ) -//│ }, -//│ let* (x$2) = hello() in -- #12 -//│ x$2 -- #11) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, hello, [], -//│ 1, -//│ let* (x$0) = @tailcall hello() in -- #8 -//│ let* (x$1) = @tailcall hello() in -- #7 -//│ 2 -- #6 -//│ ) -//│ }, -//│ let* (x$2) = hello() in -- #12 -//│ x$2 -- #11) +//│ Program: +//│ class _IdContext() +//│ class _Context(acc,ptr,field) +//│ def hello() = +//│ let* (x$1) = @tailcall hello() in -- #9 +//│ let* (x$2) = @tailcall hello() in -- #8 +//│ 2 -- #7 +//│ let* (x$0) = hello() in -- #2 +//│ x$0 -- #1 :ce fun hello() = @@ -649,209 +438,120 @@ hello() //│ |#fun| |hello|(||)| |#=|→|@|tailcall| |hello|(||)|↵|2|←|↵|hello|(||)| | //│ Parsed: {fun hello = () => {@tailcall hello(); 2}; hello()} //│ +//│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, hello, [], -//│ 1, -//│ let* (x$0) = @tailcall hello() in -- #4 -//│ 2 -- #3 -//│ ) -//│ }, -//│ let* (x$1) = hello() in -- #8 -//│ x$1 -- #7) +//│ Program: +//│ +//│ def hello() = +//│ let* (x$1) = @tailcall hello() in -- #6 +//│ 2 -- #5 +//│ let* (x$0) = hello() in -- #2 +//│ x$0 -- #1 //│ ╔══[COMPILATION ERROR] not a tail call -//│ ║ l.646: @tailcall hello() +//│ ║ l.435: @tailcall hello() //│ ╙── ^^^^^ //│ +//│ //│ Strongly Connected Tail Calls: //│ List(Set(hello)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, hello, [], -//│ 1, -//│ let* (x$0) = @tailcall hello() in -- #4 -//│ 2 -- #3 -//│ ) -//│ }, -//│ let* (x$1) = hello() in -- #8 -//│ x$1 -- #7) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, hello, [], -//│ 1, -//│ let* (x$0) = @tailcall hello() in -- #4 -//│ 2 -- #3 -//│ ) -//│ }, -//│ let* (x$1) = hello() in -- #8 -//│ x$1 -- #7) +//│ Program: +//│ class _IdContext() +//│ class _Context(acc,ptr,field) +//│ def hello() = +//│ let* (x$1) = @tailcall hello() in -- #6 +//│ 2 -- #5 +//│ let* (x$0) = hello() in -- #2 +//│ x$0 -- #1 :interpIR -class Cons(h, t) -class Nil @tailrec fun addOne(xs) = if xs is Cons(h, t) then Cons(h + 1, @tailcall addOne(t)) Nil then Nil addOne(Cons(1, Cons(2, Cons(3, Nil)))) -//│ |#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is|→|Cons|(|h|,| |t|)| |#then| |Cons|(|h| |+| |1|,| |@|tailcall| |addOne|(|t|)|)|↵|Nil| |#then| |Nil|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| -//│ Parsed: {class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then Cons(+(h,)(1,), @tailcall addOne(t,),); (Nil) then Nil›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} +//│ |@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is|→|Cons|(|h|,| |t|)| |#then| |Cons|(|h| |+| |1|,| |@|tailcall| |addOne|(|t|)|)|↵|Nil| |#then| |Nil|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| +//│ Parsed: {fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then Cons(+(h,)(1,), @tailcall addOne(t,),); (Nil) then Nil›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} +//│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { -//│ Def(0, addOne, [xs$0], -//│ 1, -//│ case xs$0 of -- #27 -//│ Cons => -//│ let x$1 = xs$0.t in -- #23 -//│ let x$2 = xs$0.h in -- #22 -//│ let x$3 = +(x$2,1) in -- #21 -//│ let* (x$4) = @tailcall addOne(x$1) in -- #20 -//│ let x$5 = Cons(x$3,x$4) in -- #19 -//│ jump j$0(x$5) -- #18 -//│ Nil => -//│ let x$6 = Nil() in -- #26 -//│ jump j$0(x$6) -- #25 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ }, -//│ let x$7 = Nil() in -- #52 -//│ let x$8 = Cons(3,x$7) in -- #51 -//│ let x$9 = Cons(2,x$8) in -- #50 -//│ let x$10 = Cons(1,x$9) in -- #49 -//│ let* (x$11) = addOne(x$10) in -- #48 -//│ x$11 -- #47) +//│ Program: +//│ +//│ def addOne(xs$0) = +//│ case xs$0 of -- #54 +//│ Cons => +//│ let x$6 = Cons.t(xs$0) in -- #50 +//│ let x$7 = Cons.h(xs$0) in -- #49 +//│ let x$8 = +(x$7,1) in -- #48 +//│ let* (x$9) = @tailcall addOne(x$6) in -- #47 +//│ let x$10 = Cons(x$8,x$9) in -- #46 +//│ jump j$0(x$10) -- #45 +//│ Nil => +//│ let x$11 = Nil() in -- #53 +//│ jump j$0(x$11) -- #52 +//│ def j$0(x$5) = +//│ x$5 -- #25 +//│ let x$0 = Nil() in -- #23 +//│ let x$1 = Cons(3,x$0) in -- #22 +//│ let x$2 = Cons(2,x$1) in -- #21 +//│ let x$3 = Cons(1,x$2) in -- #20 +//│ let* (x$4) = addOne(x$3) in -- #19 +//│ x$4 -- #18 +//│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(addOne)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { -//│ Def(0, addOne, [xs$0], -//│ 1, -//│ let idCtx = _IdContext() in -- #80 -//│ let* (res) = addOne_modcons$4(idCtx,xs$0) in -- #79 -//│ res -- #78 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, _addOne_ctx_app$2, [ctx,x], -//│ 1, -//│ case ctx of -- #59 -//│ _IdContext => -//│ x -- #58 -//│ _Context => -//│ let field = ctx.field in -- #57 -//│ let ptr = ctx.ptr in -- #56 -//│ let _ = assign ptr.t := x in -- #55 -//│ let acc = ctx.acc in -- #54 -//│ acc -- #53 -//│ ) -//│ Def(3, _addOne_ctx_comp$3, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #65 -//│ let ctx2ptr = ctx2.ptr in -- #64 -//│ let ctx2field = ctx2.field in -- #63 -//│ let* (newAcc) = _addOne_ctx_app$2(ctx1,ctx2acc) in -- #62 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #61 -//│ ret -- #60 -//│ ) -//│ Def(5, addOne_modcons$4_jp, [ctx,xs$0], -//│ 1, -//│ case xs$0 of -- #91 -//│ Cons => -//│ let x$1 = xs$0.t in -- #87 -//│ let x$2 = xs$0.h in -- #86 -//│ let x$3 = +(x$2,1) in -- #85 -//│ let x$5 = Cons(x$3,0) in -- #84 -//│ let ctx2 = _Context(x$5,x$5,0) in -- #83 -//│ let* (composed) = _addOne_ctx_comp$3(ctx,ctx2) in -- #82 -//│ jump addOne_modcons$4_jp(composed,x$1) -- #81 -//│ Nil => -//│ let x$6 = Nil() in -- #90 -//│ let* (res) = _addOne_ctx_app$2(ctx,x$6) in -- #89 -//│ res -- #88 -//│ ) -//│ Def(6, addOne_modcons$4, [ctx,xs$0], -//│ 1, -//│ let* (r0) = addOne_modcons$4_jp(ctx,xs$0) in -- #93 -//│ r0 -- #92 -//│ ) -//│ }, -//│ let x$7 = Nil() in -- #52 -//│ let x$8 = Cons(3,x$7) in -- #51 -//│ let x$9 = Cons(2,x$8) in -- #50 -//│ let x$10 = Cons(1,x$9) in -- #49 -//│ let* (x$11) = addOne(x$10) in -- #48 -//│ x$11 -- #47) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { -//│ Def(0, addOne, [xs$0], -//│ 1, -//│ let idCtx = _IdContext() in -- #80 -//│ let* (res) = addOne_modcons$4(idCtx,xs$0) in -- #79 -//│ res -- #78 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, _addOne_ctx_app$2, [ctx,x], -//│ 1, -//│ case ctx of -- #59 -//│ _IdContext => -//│ x -- #58 -//│ _Context => -//│ let field = ctx.field in -- #57 -//│ let ptr = ctx.ptr in -- #56 -//│ let _ = assign ptr.t := x in -- #55 -//│ let acc = ctx.acc in -- #54 -//│ acc -- #53 -//│ ) -//│ Def(3, _addOne_ctx_comp$3, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #65 -//│ let ctx2ptr = ctx2.ptr in -- #64 -//│ let ctx2field = ctx2.field in -- #63 -//│ let* (newAcc) = _addOne_ctx_app$2(ctx1,ctx2acc) in -- #62 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #61 -//│ ret -- #60 -//│ ) -//│ Def(5, addOne_modcons$4_jp, [ctx,xs$0], -//│ 1, -//│ case xs$0 of -- #91 -//│ Cons => -//│ let x$1 = xs$0.t in -- #87 -//│ let x$2 = xs$0.h in -- #86 -//│ let x$3 = +(x$2,1) in -- #85 -//│ let x$5 = Cons(x$3,0) in -- #84 -//│ let ctx2 = _Context(x$5,x$5,0) in -- #83 -//│ let* (composed) = _addOne_ctx_comp$3(ctx,ctx2) in -- #82 -//│ jump addOne_modcons$4_jp(composed,x$1) -- #81 -//│ Nil => -//│ let x$6 = Nil() in -- #90 -//│ let* (res) = _addOne_ctx_app$2(ctx,x$6) in -- #89 -//│ res -- #88 -//│ ) -//│ Def(6, addOne_modcons$4, [ctx,xs$0], -//│ 1, -//│ let* (r0) = addOne_modcons$4_jp(ctx,xs$0) in -- #93 -//│ r0 -- #92 -//│ ) -//│ }, -//│ let x$7 = Nil() in -- #52 -//│ let x$8 = Cons(3,x$7) in -- #51 -//│ let x$9 = Cons(2,x$8) in -- #50 -//│ let x$10 = Cons(1,x$9) in -- #49 -//│ let* (x$11) = addOne(x$10) in -- #48 -//│ x$11 -- #47) +//│ Program: +//│ class _IdContext() +//│ class _Context(acc,ptr,field) +//│ def addOne(xs$0) = +//│ let idCtx = _IdContext() in -- #88 +//│ let* (res) = addOne_modcons$10(idCtx,xs$0) in -- #87 +//│ res -- #86 +//│ def j$0(x$5) = +//│ x$5 -- #25 +//│ def _addOne_ctx_app$8(ctx,x) = +//│ case ctx of -- #67 +//│ _IdContext => +//│ x -- #66 +//│ _Context => +//│ let field = _Context.field(ctx) in -- #65 +//│ let ptr = _Context.ptr(ctx) in -- #64 +//│ let _ = assign ptr.t := x in -- #63 +//│ let acc = _Context.acc(ctx) in -- #62 +//│ acc -- #61 +//│ def _addOne_ctx_comp$9(ctx1,ctx2) = +//│ let ctx2acc = _Context.acc(ctx2) in -- #73 +//│ let ctx2ptr = _Context.ptr(ctx2) in -- #72 +//│ let ctx2field = _Context.field(ctx2) in -- #71 +//│ let* (newAcc) = _addOne_ctx_app$8(ctx1,ctx2acc) in -- #70 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #69 +//│ ret -- #68 +//│ def addOne_modcons$10_jp(ctx,xs$0) = +//│ case xs$0 of -- #99 +//│ Cons => +//│ let x$6 = Cons.t(xs$0) in -- #95 +//│ let x$7 = Cons.h(xs$0) in -- #94 +//│ let x$8 = +(x$7,1) in -- #93 +//│ let x$10 = Cons(x$8,0) in -- #92 +//│ let ctx2 = _Context(x$10,x$10,0) in -- #91 +//│ let* (composed) = _addOne_ctx_comp$9(ctx,ctx2) in -- #90 +//│ jump addOne_modcons$10_jp(composed,x$6) -- #89 +//│ Nil => +//│ let x$11 = Nil() in -- #98 +//│ let* (res) = _addOne_ctx_app$8(ctx,x$11) in -- #97 +//│ res -- #96 +//│ def addOne_modcons$10(ctx,xs$0) = +//│ let* (r0) = addOne_modcons$10_jp(ctx,xs$0) in -- #101 +//│ r0 -- #100 +//│ let x$0 = Nil() in -- #23 +//│ let x$1 = Cons(3,x$0) in -- #22 +//│ let x$2 = Cons(2,x$1) in -- #21 +//│ let x$3 = Cons(1,x$2) in -- #20 +//│ let* (x$4) = addOne(x$3) in -- #19 +//│ x$4 -- #18 //│ //│ Interpreted: -//│ Cons(2,Cons(3,Cons(4,Nil()))) +//│ Cons(2,0) :noTailRec :interpIR @@ -869,51 +569,44 @@ a(S(S(S(Zero)))) //│ |#class| |Zero|↵|#class| |S|(|x|)|↵|#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailcall| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailcall| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| //│ Parsed: {class Zero {}; class S(x,) {}; fun a = (n,) => {if n is ‹(S(x,)) then S(@tailcall b(x,),); (Zero) then S(Zero,)›}; fun b = (n,) => {if n is ‹(S(x,)) then S(S(@tailcall a(x,),),); (Zero) then S(S(Zero,),)›}; a(S(S(S(Zero,),),),)} //│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { -//│ Def(0, a, [n$0], -//│ 1, -//│ case n$0 of -- #23 -//│ S => -//│ let x$1 = n$0.x in -- #15 -//│ let* (x$2) = @tailcall b(x$1) in -- #14 -//│ let x$3 = S(x$2) in -- #13 -//│ jump j$0(x$3) -- #12 -//│ Zero => -//│ let x$4 = Zero() in -- #22 -//│ let x$5 = S(x$4) in -- #21 -//│ jump j$0(x$5) -- #20 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, b, [n$1], -//│ 1, -//│ case n$1 of -- #55 -//│ S => -//│ let x$7 = n$1.x in -- #43 -//│ let* (x$8) = @tailcall a(x$7) in -- #42 -//│ let x$9 = S(x$8) in -- #41 -//│ let x$10 = S(x$9) in -- #40 -//│ jump j$1(x$10) -- #39 -//│ Zero => -//│ let x$11 = Zero() in -- #54 -//│ let x$12 = S(x$11) in -- #53 -//│ let x$13 = S(x$12) in -- #52 -//│ jump j$1(x$13) -- #51 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #25 -//│ ) -//│ }, -//│ let x$14 = Zero() in -- #74 -//│ let x$15 = S(x$14) in -- #73 -//│ let x$16 = S(x$15) in -- #72 -//│ let x$17 = S(x$16) in -- #71 -//│ let* (x$18) = a(x$17) in -- #70 -//│ x$18 -- #69) +//│ +//│ IR: +//│ Program: +//│ class Zero() +//│ def a(n$0) = +//│ case n$0 of -- #42 +//│ S => +//│ let x$6 = S.s(n$0) in -- #34 +//│ let* (x$7) = @tailcall b(x$6) in -- #33 +//│ let x$8 = S(x$7) in -- #32 +//│ jump j$0(x$8) -- #31 +//│ Zero => +//│ let x$9 = Zero() in -- #41 +//│ let x$10 = S(x$9) in -- #40 +//│ jump j$0(x$10) -- #39 +//│ def j$0(x$5) = +//│ x$5 -- #19 +//│ def b(n$1) = +//│ case n$1 of -- #75 +//│ S => +//│ let x$12 = S.s(n$1) in -- #63 +//│ let* (x$13) = @tailcall a(x$12) in -- #62 +//│ let x$14 = S(x$13) in -- #61 +//│ let x$15 = S(x$14) in -- #60 +//│ jump j$1(x$15) -- #59 +//│ Zero => +//│ let x$16 = Zero() in -- #74 +//│ let x$17 = S(x$16) in -- #73 +//│ let x$18 = S(x$17) in -- #72 +//│ jump j$1(x$18) -- #71 +//│ def j$1(x$11) = +//│ x$11 -- #44 +//│ let x$0 = Zero() in -- #17 +//│ let x$1 = S(x$0) in -- #16 +//│ let x$2 = S(x$1) in -- #15 +//│ let x$3 = S(x$2) in -- #14 +//│ let* (x$4) = a(x$3) in -- #13 +//│ x$4 -- #12 //│ //│ Interpreted: //│ S(S(S(S(S(S(Zero())))))) @@ -933,255 +626,131 @@ a(S(S(S(Zero)))) //│ |#class| |Zero|↵|#class| |S|(|x|)|↵|@|tailrec| |#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailcall| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|@|tailrec| |#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailcall| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| //│ Parsed: {class Zero {}; class S(x,) {}; fun a = (n,) => {if n is ‹(S(x,)) then S(@tailcall b(x,),); (Zero) then S(Zero,)›}; fun b = (n,) => {if n is ‹(S(x,)) then S(S(@tailcall a(x,),),); (Zero) then S(S(Zero,),)›}; a(S(S(S(Zero,),),),)} //│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { -//│ Def(0, a, [n$0], -//│ 1, -//│ case n$0 of -- #23 -//│ S => -//│ let x$1 = n$0.x in -- #15 -//│ let* (x$2) = @tailcall b(x$1) in -- #14 -//│ let x$3 = S(x$2) in -- #13 -//│ jump j$0(x$3) -- #12 -//│ Zero => -//│ let x$4 = Zero() in -- #22 -//│ let x$5 = S(x$4) in -- #21 -//│ jump j$0(x$5) -- #20 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, b, [n$1], -//│ 1, -//│ case n$1 of -- #55 -//│ S => -//│ let x$7 = n$1.x in -- #43 -//│ let* (x$8) = @tailcall a(x$7) in -- #42 -//│ let x$9 = S(x$8) in -- #41 -//│ let x$10 = S(x$9) in -- #40 -//│ jump j$1(x$10) -- #39 -//│ Zero => -//│ let x$11 = Zero() in -- #54 -//│ let x$12 = S(x$11) in -- #53 -//│ let x$13 = S(x$12) in -- #52 -//│ jump j$1(x$13) -- #51 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #25 -//│ ) -//│ }, -//│ let x$14 = Zero() in -- #74 -//│ let x$15 = S(x$14) in -- #73 -//│ let x$16 = S(x$15) in -- #72 -//│ let x$17 = S(x$16) in -- #71 -//│ let* (x$18) = a(x$17) in -- #70 -//│ x$18 -- #69) //│ -//│ Strongly Connected Tail Calls: -//│ List(Set(j$1), Set(j$0), Set(b, a)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { -//│ Def(0, a, [n$0], -//│ 1, -//│ let idCtx = _IdContext() in -- #117 -//│ let* (res) = a_modcons$7(idCtx,n$0) in -- #116 -//│ res -- #115 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, b, [n$1], -//│ 1, -//│ let idCtx = _IdContext() in -- #103 -//│ let* (res) = b_modcons$6(idCtx,n$1) in -- #102 -//│ res -- #101 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #25 -//│ ) -//│ Def(4, _b_a_ctx_app$4, [ctx,x], -//│ 1, -//│ case ctx of -- #81 -//│ _IdContext => -//│ x -- #80 -//│ _Context => -//│ let field = ctx.field in -- #79 -//│ let ptr = ctx.ptr in -- #78 -//│ let _ = assign ptr.x := x in -- #77 -//│ let acc = ctx.acc in -- #76 -//│ acc -- #75 -//│ ) -//│ Def(5, _b_a_ctx_comp$5, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #87 -//│ let ctx2ptr = ctx2.ptr in -- #86 -//│ let ctx2field = ctx2.field in -- #85 -//│ let* (newAcc) = _b_a_ctx_app$4(ctx1,ctx2acc) in -- #84 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #83 -//│ ret -- #82 -//│ ) -//│ Def(6, b_modcons$6, [ctx,n$1], -//│ 1, -//│ let* (r0) = _b_modcons$6_a_modcons$7_opt$8(6,ctx,n$1,undefined,undefined) in -- #156 -//│ r0 -- #155 -//│ ) -//│ Def(7, a_modcons$7, [ctx,n$0], -//│ 1, -//│ let* (r0) = _b_modcons$6_a_modcons$7_opt$8(7,undefined,undefined,ctx,n$0) in -- #158 -//│ r0 -- #157 -//│ ) -//│ Def(8, _b_modcons$6_a_modcons$7_opt$8, [tailrecBranch$,b_modcons$6_ctx,b_modcons$6_n$1,a_modcons$7_ctx,a_modcons$7_n$0], -//│ 1, -//│ jump _b_modcons$6_a_modcons$7_opt_jp$9(tailrecBranch$,b_modcons$6_ctx,b_modcons$6_n$1,a_modcons$7_ctx,a_modcons$7_n$0) -- #154 -//│ ) -//│ Def(9, _b_modcons$6_a_modcons$7_opt_jp$9, [tailrecBranch$,b_modcons$6_ctx,b_modcons$6_n$1,a_modcons$7_ctx,a_modcons$7_n$0], -//│ 1, -//│ let scrut = ==(7,tailrecBranch$) in -- #153 -//│ if scrut -- #152 -//│ true => -//│ case a_modcons$7_n$0 of -- #151 -//│ S => -//│ let x$1 = a_modcons$7_n$0.x in -- #146 -//│ let x$3 = S(0) in -- #145 -//│ let ctx2 = _Context(x$3,x$3,0) in -- #144 -//│ let* (composed) = _b_a_ctx_comp$5(a_modcons$7_ctx,ctx2) in -- #143 -//│ jump _b_modcons$6_a_modcons$7_opt_jp$9(6,composed,x$1,a_modcons$7_ctx,a_modcons$7_n$0) -- #142 -//│ Zero => -//│ let x$4 = Zero() in -- #150 -//│ let x$5 = S(x$4) in -- #149 -//│ let* (res) = _b_a_ctx_app$4(a_modcons$7_ctx,x$5) in -- #148 -//│ res -- #147 -//│ false => -//│ case b_modcons$6_n$1 of -- #141 -//│ S => -//│ let x$7 = b_modcons$6_n$1.x in -- #135 -//│ let x$9 = S(0) in -- #134 -//│ let x$10 = S(x$9) in -- #133 -//│ let ctx2 = _Context(x$10,x$9,0) in -- #132 -//│ let* (composed) = _b_a_ctx_comp$5(b_modcons$6_ctx,ctx2) in -- #131 -//│ jump _b_modcons$6_a_modcons$7_opt_jp$9(7,b_modcons$6_ctx,b_modcons$6_n$1,composed,x$7) -- #130 -//│ Zero => -//│ let x$11 = Zero() in -- #140 -//│ let x$12 = S(x$11) in -- #139 -//│ let x$13 = S(x$12) in -- #138 -//│ let* (res) = _b_a_ctx_app$4(b_modcons$6_ctx,x$13) in -- #137 -//│ res -- #136 -//│ ) -//│ }, -//│ let x$14 = Zero() in -- #74 -//│ let x$15 = S(x$14) in -- #73 -//│ let x$16 = S(x$15) in -- #72 -//│ let x$17 = S(x$16) in -- #71 -//│ let* (x$18) = a(x$17) in -- #70 -//│ x$18 -- #69) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { -//│ Def(0, a, [n$0], -//│ 1, -//│ let idCtx = _IdContext() in -- #117 -//│ let* (res) = a_modcons$7(idCtx,n$0) in -- #116 -//│ res -- #115 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, b, [n$1], -//│ 1, -//│ let idCtx = _IdContext() in -- #103 -//│ let* (res) = b_modcons$6(idCtx,n$1) in -- #102 -//│ res -- #101 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #25 -//│ ) -//│ Def(4, _b_a_ctx_app$4, [ctx,x], -//│ 1, -//│ case ctx of -- #81 -//│ _IdContext => -//│ x -- #80 -//│ _Context => -//│ let field = ctx.field in -- #79 -//│ let ptr = ctx.ptr in -- #78 -//│ let _ = assign ptr.x := x in -- #77 -//│ let acc = ctx.acc in -- #76 -//│ acc -- #75 -//│ ) -//│ Def(5, _b_a_ctx_comp$5, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #87 -//│ let ctx2ptr = ctx2.ptr in -- #86 -//│ let ctx2field = ctx2.field in -- #85 -//│ let* (newAcc) = _b_a_ctx_app$4(ctx1,ctx2acc) in -- #84 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #83 -//│ ret -- #82 -//│ ) -//│ Def(6, b_modcons$6, [ctx,n$1], -//│ 1, -//│ let* (r0) = _b_modcons$6_a_modcons$7_opt$8(6,ctx,n$1,undefined,undefined) in -- #156 -//│ r0 -- #155 -//│ ) -//│ Def(7, a_modcons$7, [ctx,n$0], -//│ 1, -//│ let* (r0) = _b_modcons$6_a_modcons$7_opt$8(7,undefined,undefined,ctx,n$0) in -- #158 -//│ r0 -- #157 -//│ ) -//│ Def(8, _b_modcons$6_a_modcons$7_opt$8, [tailrecBranch$,b_modcons$6_ctx,b_modcons$6_n$1,a_modcons$7_ctx,a_modcons$7_n$0], -//│ 1, -//│ jump _b_modcons$6_a_modcons$7_opt_jp$9(tailrecBranch$,b_modcons$6_ctx,b_modcons$6_n$1,a_modcons$7_ctx,a_modcons$7_n$0) -- #154 -//│ ) -//│ Def(9, _b_modcons$6_a_modcons$7_opt_jp$9, [tailrecBranch$,b_modcons$6_ctx,b_modcons$6_n$1,a_modcons$7_ctx,a_modcons$7_n$0], -//│ 1, -//│ let scrut = ==(7,tailrecBranch$) in -- #153 -//│ if scrut -- #152 -//│ true => -//│ case a_modcons$7_n$0 of -- #151 +//│ IR: +//│ Program: +//│ class Zero() +//│ def a(n$0) = +//│ case n$0 of -- #42 //│ S => -//│ let x$1 = a_modcons$7_n$0.x in -- #146 -//│ let x$3 = S(0) in -- #145 -//│ let ctx2 = _Context(x$3,x$3,0) in -- #144 -//│ let* (composed) = _b_a_ctx_comp$5(a_modcons$7_ctx,ctx2) in -- #143 -//│ jump _b_modcons$6_a_modcons$7_opt_jp$9(6,composed,x$1,a_modcons$7_ctx,a_modcons$7_n$0) -- #142 +//│ let x$6 = S.s(n$0) in -- #34 +//│ let* (x$7) = @tailcall b(x$6) in -- #33 +//│ let x$8 = S(x$7) in -- #32 +//│ jump j$0(x$8) -- #31 //│ Zero => -//│ let x$4 = Zero() in -- #150 -//│ let x$5 = S(x$4) in -- #149 -//│ let* (res) = _b_a_ctx_app$4(a_modcons$7_ctx,x$5) in -- #148 -//│ res -- #147 -//│ false => -//│ case b_modcons$6_n$1 of -- #141 +//│ let x$9 = Zero() in -- #41 +//│ let x$10 = S(x$9) in -- #40 +//│ jump j$0(x$10) -- #39 +//│ def j$0(x$5) = +//│ x$5 -- #19 +//│ def b(n$1) = +//│ case n$1 of -- #75 //│ S => -//│ let x$7 = b_modcons$6_n$1.x in -- #135 -//│ let x$9 = S(0) in -- #134 -//│ let x$10 = S(x$9) in -- #133 -//│ let ctx2 = _Context(x$10,x$9,0) in -- #132 -//│ let* (composed) = _b_a_ctx_comp$5(b_modcons$6_ctx,ctx2) in -- #131 -//│ jump _b_modcons$6_a_modcons$7_opt_jp$9(7,b_modcons$6_ctx,b_modcons$6_n$1,composed,x$7) -- #130 +//│ let x$12 = S.s(n$1) in -- #63 +//│ let* (x$13) = @tailcall a(x$12) in -- #62 +//│ let x$14 = S(x$13) in -- #61 +//│ let x$15 = S(x$14) in -- #60 +//│ jump j$1(x$15) -- #59 //│ Zero => -//│ let x$11 = Zero() in -- #140 -//│ let x$12 = S(x$11) in -- #139 -//│ let x$13 = S(x$12) in -- #138 -//│ let* (res) = _b_a_ctx_app$4(b_modcons$6_ctx,x$13) in -- #137 -//│ res -- #136 -//│ ) -//│ }, -//│ let x$14 = Zero() in -- #74 -//│ let x$15 = S(x$14) in -- #73 -//│ let x$16 = S(x$15) in -- #72 -//│ let x$17 = S(x$16) in -- #71 -//│ let* (x$18) = a(x$17) in -- #70 -//│ x$18 -- #69) +//│ let x$16 = Zero() in -- #74 +//│ let x$17 = S(x$16) in -- #73 +//│ let x$18 = S(x$17) in -- #72 +//│ jump j$1(x$18) -- #71 +//│ def j$1(x$11) = +//│ x$11 -- #44 +//│ let x$0 = Zero() in -- #17 +//│ let x$1 = S(x$0) in -- #16 +//│ let x$2 = S(x$1) in -- #15 +//│ let x$3 = S(x$2) in -- #14 +//│ let* (x$4) = a(x$3) in -- #13 +//│ x$4 -- #12 +//│ +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$1), Set(j$0), Set(b, a)) +//│ Program: +//│ class Zero() +//│ class _IdContext() +//│ class _Context(acc,ptr,field) +//│ def a(n$0) = +//│ let idCtx = _IdContext() in -- #124 +//│ let* (res) = a_modcons$13(idCtx,n$0) in -- #123 +//│ res -- #122 +//│ def j$0(x$5) = +//│ x$5 -- #19 +//│ def b(n$1) = +//│ let idCtx = _IdContext() in -- #110 +//│ let* (res) = b_modcons$12(idCtx,n$1) in -- #109 +//│ res -- #108 +//│ def j$1(x$11) = +//│ x$11 -- #44 +//│ def _b_a_ctx_app$10(ctx,x) = +//│ case ctx of -- #88 +//│ _IdContext => +//│ x -- #87 +//│ _Context => +//│ let field = _Context.field(ctx) in -- #86 +//│ let ptr = _Context.ptr(ctx) in -- #85 +//│ let _ = assign ptr.s := x in -- #84 +//│ let acc = _Context.acc(ctx) in -- #83 +//│ acc -- #82 +//│ def _b_a_ctx_comp$11(ctx1,ctx2) = +//│ let ctx2acc = _Context.acc(ctx2) in -- #94 +//│ let ctx2ptr = _Context.ptr(ctx2) in -- #93 +//│ let ctx2field = _Context.field(ctx2) in -- #92 +//│ let* (newAcc) = _b_a_ctx_app$10(ctx1,ctx2acc) in -- #91 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #90 +//│ ret -- #89 +//│ def b_modcons$12(ctx,n$1) = +//│ let* (r0) = _b_modcons$12_a_modcons$13_opt$14(12,ctx,n$1,true,true) in -- #163 +//│ r0 -- #162 +//│ def a_modcons$13(ctx,n$0) = +//│ let* (r0) = _b_modcons$12_a_modcons$13_opt$14(13,true,true,ctx,n$0) in -- #165 +//│ r0 -- #164 +//│ def _b_modcons$12_a_modcons$13_opt$14(tailrecBranch$,b_modcons$12_ctx,b_modcons$12_n$1,a_modcons$13_ctx,a_modcons$13_n$0) = +//│ jump _b_modcons$12_a_modcons$13_opt_jp$15(tailrecBranch$,b_modcons$12_ctx,b_modcons$12_n$1,a_modcons$13_ctx,a_modcons$13_n$0) -- #161 +//│ def _b_modcons$12_a_modcons$13_opt_jp$15(tailrecBranch$,b_modcons$12_ctx,b_modcons$12_n$1,a_modcons$13_ctx,a_modcons$13_n$0) = +//│ let scrut = ==(13,tailrecBranch$) in -- #160 +//│ if scrut -- #159 +//│ true => +//│ case a_modcons$13_n$0 of -- #158 +//│ S => +//│ let x$6 = S.s(a_modcons$13_n$0) in -- #153 +//│ let x$8 = S(0) in -- #152 +//│ let ctx2 = _Context(x$8,x$8,0) in -- #151 +//│ let* (composed) = _b_a_ctx_comp$11(a_modcons$13_ctx,ctx2) in -- #150 +//│ jump _b_modcons$12_a_modcons$13_opt_jp$15(12,composed,x$6,a_modcons$13_ctx,a_modcons$13_n$0) -- #149 +//│ Zero => +//│ let x$9 = Zero() in -- #157 +//│ let x$10 = S(x$9) in -- #156 +//│ let* (res) = _b_a_ctx_app$10(a_modcons$13_ctx,x$10) in -- #155 +//│ res -- #154 +//│ false => +//│ case b_modcons$12_n$1 of -- #148 +//│ S => +//│ let x$12 = S.s(b_modcons$12_n$1) in -- #142 +//│ let x$14 = S(0) in -- #141 +//│ let x$15 = S(x$14) in -- #140 +//│ let ctx2 = _Context(x$15,x$14,0) in -- #139 +//│ let* (composed) = _b_a_ctx_comp$11(b_modcons$12_ctx,ctx2) in -- #138 +//│ jump _b_modcons$12_a_modcons$13_opt_jp$15(13,b_modcons$12_ctx,b_modcons$12_n$1,composed,x$12) -- #137 +//│ Zero => +//│ let x$16 = Zero() in -- #147 +//│ let x$17 = S(x$16) in -- #146 +//│ let x$18 = S(x$17) in -- #145 +//│ let* (res) = _b_a_ctx_app$10(b_modcons$12_ctx,x$18) in -- #144 +//│ res -- #143 +//│ let x$0 = Zero() in -- #17 +//│ let x$1 = S(x$0) in -- #16 +//│ let x$2 = S(x$1) in -- #15 +//│ let x$3 = S(x$2) in -- #14 +//│ let* (x$4) = a(x$3) in -- #13 +//│ x$4 -- #12 //│ //│ Interpreted: -//│ S(S(S(S(S(S(Zero())))))) +//│ S(0) :interpIR -class Cons(h, t) -class Nil @tailrec fun addOne(xs) = if xs is Cons(h, t) then @@ -1192,168 +761,91 @@ class Nil Nil then Nil addOne(Cons(1, Cons(2, Cons(3, Nil)))) -//│ |#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#val| |next| |#=| |@|tailcall| |addOne|(|t|)|↵|#val| |ret| |#=| |Cons|(|h| |+| |1|,| |next|)|↵|#val| |rett| |#=| |ret|↵|rett|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| -//│ Parsed: {class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then {let next = @tailcall addOne(t,); let ret = Cons(+(h,)(1,), next,); let rett = ret; rett}; (Nil) then {Nil}›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} +//│ |@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#val| |next| |#=| |@|tailcall| |addOne|(|t|)|↵|#val| |ret| |#=| |Cons|(|h| |+| |1|,| |next|)|↵|#val| |rett| |#=| |ret|↵|rett|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| +//│ Parsed: {fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then {let next = @tailcall addOne(t,); let ret = Cons(+(h,)(1,), next,); let rett = ret; rett}; (Nil) then {Nil}›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} +//│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { -//│ Def(0, addOne, [xs$0], -//│ 1, -//│ case xs$0 of -- #30 -//│ Cons => -//│ let x$1 = xs$0.t in -- #26 -//│ let x$2 = xs$0.h in -- #25 -//│ let* (x$3) = @tailcall addOne(x$1) in -- #24 -//│ let x$4 = +(x$2,1) in -- #23 -//│ let x$5 = Cons(x$4,x$3) in -- #22 -//│ jump j$0(x$5) -- #21 -//│ Nil => -//│ let x$6 = Nil() in -- #29 -//│ jump j$0(x$6) -- #28 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ }, -//│ let x$7 = Nil() in -- #55 -//│ let x$8 = Cons(3,x$7) in -- #54 -//│ let x$9 = Cons(2,x$8) in -- #53 -//│ let x$10 = Cons(1,x$9) in -- #52 -//│ let* (x$11) = addOne(x$10) in -- #51 -//│ x$11 -- #50) +//│ Program: +//│ +//│ def addOne(xs$0) = +//│ case xs$0 of -- #57 +//│ Cons => +//│ let x$6 = Cons.t(xs$0) in -- #53 +//│ let x$7 = Cons.h(xs$0) in -- #52 +//│ let* (x$8) = @tailcall addOne(x$6) in -- #51 +//│ let x$9 = +(x$7,1) in -- #50 +//│ let x$10 = Cons(x$9,x$8) in -- #49 +//│ jump j$0(x$10) -- #48 +//│ Nil => +//│ let x$11 = Nil() in -- #56 +//│ jump j$0(x$11) -- #55 +//│ def j$0(x$5) = +//│ x$5 -- #25 +//│ let x$0 = Nil() in -- #23 +//│ let x$1 = Cons(3,x$0) in -- #22 +//│ let x$2 = Cons(2,x$1) in -- #21 +//│ let x$3 = Cons(1,x$2) in -- #20 +//│ let* (x$4) = addOne(x$3) in -- #19 +//│ x$4 -- #18 +//│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(addOne)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { -//│ Def(0, addOne, [xs$0], -//│ 1, -//│ let idCtx = _IdContext() in -- #83 -//│ let* (res) = addOne_modcons$4(idCtx,xs$0) in -- #82 -//│ res -- #81 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, _addOne_ctx_app$2, [ctx,x], -//│ 1, -//│ case ctx of -- #62 -//│ _IdContext => -//│ x -- #61 -//│ _Context => -//│ let field = ctx.field in -- #60 -//│ let ptr = ctx.ptr in -- #59 -//│ let _ = assign ptr.t := x in -- #58 -//│ let acc = ctx.acc in -- #57 -//│ acc -- #56 -//│ ) -//│ Def(3, _addOne_ctx_comp$3, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #68 -//│ let ctx2ptr = ctx2.ptr in -- #67 -//│ let ctx2field = ctx2.field in -- #66 -//│ let* (newAcc) = _addOne_ctx_app$2(ctx1,ctx2acc) in -- #65 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #64 -//│ ret -- #63 -//│ ) -//│ Def(5, addOne_modcons$4_jp, [ctx,xs$0], -//│ 1, -//│ case xs$0 of -- #94 -//│ Cons => -//│ let x$1 = xs$0.t in -- #90 -//│ let x$2 = xs$0.h in -- #89 -//│ let x$4 = +(x$2,1) in -- #88 -//│ let x$5 = Cons(x$4,0) in -- #87 -//│ let ctx2 = _Context(x$5,x$5,0) in -- #86 -//│ let* (composed) = _addOne_ctx_comp$3(ctx,ctx2) in -- #85 -//│ jump addOne_modcons$4_jp(composed,x$1) -- #84 -//│ Nil => -//│ let x$6 = Nil() in -- #93 -//│ let* (res) = _addOne_ctx_app$2(ctx,x$6) in -- #92 -//│ res -- #91 -//│ ) -//│ Def(6, addOne_modcons$4, [ctx,xs$0], -//│ 1, -//│ let* (r0) = addOne_modcons$4_jp(ctx,xs$0) in -- #96 -//│ r0 -- #95 -//│ ) -//│ }, -//│ let x$7 = Nil() in -- #55 -//│ let x$8 = Cons(3,x$7) in -- #54 -//│ let x$9 = Cons(2,x$8) in -- #53 -//│ let x$10 = Cons(1,x$9) in -- #52 -//│ let* (x$11) = addOne(x$10) in -- #51 -//│ x$11 -- #50) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { -//│ Def(0, addOne, [xs$0], -//│ 1, -//│ let idCtx = _IdContext() in -- #83 -//│ let* (res) = addOne_modcons$4(idCtx,xs$0) in -- #82 -//│ res -- #81 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, _addOne_ctx_app$2, [ctx,x], -//│ 1, -//│ case ctx of -- #62 -//│ _IdContext => -//│ x -- #61 -//│ _Context => -//│ let field = ctx.field in -- #60 -//│ let ptr = ctx.ptr in -- #59 -//│ let _ = assign ptr.t := x in -- #58 -//│ let acc = ctx.acc in -- #57 -//│ acc -- #56 -//│ ) -//│ Def(3, _addOne_ctx_comp$3, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #68 -//│ let ctx2ptr = ctx2.ptr in -- #67 -//│ let ctx2field = ctx2.field in -- #66 -//│ let* (newAcc) = _addOne_ctx_app$2(ctx1,ctx2acc) in -- #65 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #64 -//│ ret -- #63 -//│ ) -//│ Def(5, addOne_modcons$4_jp, [ctx,xs$0], -//│ 1, -//│ case xs$0 of -- #94 -//│ Cons => -//│ let x$1 = xs$0.t in -- #90 -//│ let x$2 = xs$0.h in -- #89 -//│ let x$4 = +(x$2,1) in -- #88 -//│ let x$5 = Cons(x$4,0) in -- #87 -//│ let ctx2 = _Context(x$5,x$5,0) in -- #86 -//│ let* (composed) = _addOne_ctx_comp$3(ctx,ctx2) in -- #85 -//│ jump addOne_modcons$4_jp(composed,x$1) -- #84 -//│ Nil => -//│ let x$6 = Nil() in -- #93 -//│ let* (res) = _addOne_ctx_app$2(ctx,x$6) in -- #92 -//│ res -- #91 -//│ ) -//│ Def(6, addOne_modcons$4, [ctx,xs$0], -//│ 1, -//│ let* (r0) = addOne_modcons$4_jp(ctx,xs$0) in -- #96 -//│ r0 -- #95 -//│ ) -//│ }, -//│ let x$7 = Nil() in -- #55 -//│ let x$8 = Cons(3,x$7) in -- #54 -//│ let x$9 = Cons(2,x$8) in -- #53 -//│ let x$10 = Cons(1,x$9) in -- #52 -//│ let* (x$11) = addOne(x$10) in -- #51 -//│ x$11 -- #50) +//│ Program: +//│ class _IdContext() +//│ class _Context(acc,ptr,field) +//│ def addOne(xs$0) = +//│ let idCtx = _IdContext() in -- #91 +//│ let* (res) = addOne_modcons$10(idCtx,xs$0) in -- #90 +//│ res -- #89 +//│ def j$0(x$5) = +//│ x$5 -- #25 +//│ def _addOne_ctx_app$8(ctx,x) = +//│ case ctx of -- #70 +//│ _IdContext => +//│ x -- #69 +//│ _Context => +//│ let field = _Context.field(ctx) in -- #68 +//│ let ptr = _Context.ptr(ctx) in -- #67 +//│ let _ = assign ptr.t := x in -- #66 +//│ let acc = _Context.acc(ctx) in -- #65 +//│ acc -- #64 +//│ def _addOne_ctx_comp$9(ctx1,ctx2) = +//│ let ctx2acc = _Context.acc(ctx2) in -- #76 +//│ let ctx2ptr = _Context.ptr(ctx2) in -- #75 +//│ let ctx2field = _Context.field(ctx2) in -- #74 +//│ let* (newAcc) = _addOne_ctx_app$8(ctx1,ctx2acc) in -- #73 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #72 +//│ ret -- #71 +//│ def addOne_modcons$10_jp(ctx,xs$0) = +//│ case xs$0 of -- #102 +//│ Cons => +//│ let x$6 = Cons.t(xs$0) in -- #98 +//│ let x$7 = Cons.h(xs$0) in -- #97 +//│ let x$9 = +(x$7,1) in -- #96 +//│ let x$10 = Cons(x$9,0) in -- #95 +//│ let ctx2 = _Context(x$10,x$10,0) in -- #94 +//│ let* (composed) = _addOne_ctx_comp$9(ctx,ctx2) in -- #93 +//│ jump addOne_modcons$10_jp(composed,x$6) -- #92 +//│ Nil => +//│ let x$11 = Nil() in -- #101 +//│ let* (res) = _addOne_ctx_app$8(ctx,x$11) in -- #100 +//│ res -- #99 +//│ def addOne_modcons$10(ctx,xs$0) = +//│ let* (r0) = addOne_modcons$10_jp(ctx,xs$0) in -- #104 +//│ r0 -- #103 +//│ let x$0 = Nil() in -- #23 +//│ let x$1 = Cons(3,x$0) in -- #22 +//│ let x$2 = Cons(2,x$1) in -- #21 +//│ let x$3 = Cons(1,x$2) in -- #20 +//│ let* (x$4) = addOne(x$3) in -- #19 +//│ x$4 -- #18 //│ //│ Interpreted: -//│ Cons(2,Cons(3,Cons(4,Nil()))) +//│ Cons(2,0) :interpIR -class Nil -class Cons(m, n) @tailrec fun a(x) = if x is Cons(m, n) then @@ -1368,281 +860,145 @@ class Cons(m, n) else a(Cons(n, Nil)) b(16) -//│ |#class| |Nil|↵|#class| |Cons|(|m|,| |n|)|↵|@|tailrec| |#fun| |a|(|x|)| |#=|→|#if| |x| |is|→|Cons|(|m|,| |n|)| |#then|→|#if| |m| |<| |0| |#then|→|Cons|(|-|1|,| |Nil|)|←|↵|#else| |→|Cons|(|m| |*| |4|,| |b|(|m| |-| |2|)|)|←|←|↵|Nil| |#then| |Nil|←|←|↵|@|tailrec| |#fun| |b|(|n|)| |#=|→|#if| |n| |<=| |0| |#then| |→|Cons|(|0|,| |Nil|)|←|↵|#else| |→|a|(|Cons|(|n|,| |Nil|)|)|←|←|↵|b|(|16|)| -//│ Parsed: {class Nil {}; class Cons(m, n,) {}; fun a = (x,) => {if x is ‹(Cons(m, n,)) then {if (<(m,)(0,)) then {Cons(-1, Nil,)} else {Cons(*(m,)(4,), b(-(m,)(2,),),)}}; (Nil) then Nil›}; fun b = (n,) => {if (<=(n,)(0,)) then {Cons(0, Nil,)} else {a(Cons(n, Nil,),)}}; b(16,)} +//│ |@|tailrec| |#fun| |a|(|x|)| |#=|→|#if| |x| |is|→|Cons|(|m|,| |n|)| |#then|→|#if| |m| |<| |0| |#then|→|Cons|(|-|1|,| |Nil|)|←|↵|#else| |→|Cons|(|m| |*| |4|,| |b|(|m| |-| |2|)|)|←|←|↵|Nil| |#then| |Nil|←|←|↵|@|tailrec| |#fun| |b|(|n|)| |#=|→|#if| |n| |<=| |0| |#then| |→|Cons|(|0|,| |Nil|)|←|↵|#else| |→|a|(|Cons|(|n|,| |Nil|)|)|←|←|↵|b|(|16|)| +//│ Parsed: {fun a = (x,) => {if x is ‹(Cons(m, n,)) then {if (<(m,)(0,)) then {Cons(-1, Nil,)} else {Cons(*(m,)(4,), b(-(m,)(2,),),)}}; (Nil) then Nil›}; fun b = (n,) => {if (<=(n,)(0,)) then {Cons(0, Nil,)} else {a(Cons(n, Nil,),)}}; b(16,)} +//│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n])}, { -//│ Def(0, a, [x$0], -//│ 1, -//│ case x$0 of -- #46 -//│ Cons => -//│ let x$2 = x$0.n in -- #42 -//│ let x$3 = x$0.m in -- #41 -//│ let x$4 = <(x$3,0) in -- #40 -//│ if x$4 -- #39 -//│ true => -//│ let x$6 = Nil() in -- #19 -//│ let x$7 = Cons(-1,x$6) in -- #18 -//│ jump j$1(x$7) -- #17 -//│ false => -//│ let x$8 = *(x$3,4) in -- #38 -//│ let x$9 = -(x$3,2) in -- #37 -//│ let* (x$10) = b(x$9) in -- #36 -//│ let x$11 = Cons(x$8,x$10) in -- #35 -//│ jump j$1(x$11) -- #34 -//│ Nil => -//│ let x$12 = Nil() in -- #45 -//│ jump j$0(x$12) -- #44 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, j$1, [x$5], -//│ 1, -//│ jump j$0(x$5) -- #10 -//│ ) -//│ Def(3, b, [n$0], -//│ 1, -//│ let x$13 = <=(n$0,0) in -- #75 -//│ if x$13 -- #74 -//│ true => -//│ let x$15 = Nil() in -- #59 -//│ let x$16 = Cons(0,x$15) in -- #58 -//│ jump j$2(x$16) -- #57 -//│ false => -//│ let x$17 = Nil() in -- #73 -//│ let x$18 = Cons(n$0,x$17) in -- #72 -//│ let* (x$19) = a(x$18) in -- #71 -//│ jump j$2(x$19) -- #70 -//│ ) -//│ Def(4, j$2, [x$14], -//│ 1, -//│ x$14 -- #50 -//│ ) -//│ }, -//│ let* (x$20) = b(16) in -- #81 -//│ x$20 -- #80) +//│ Program: //│ -//│ Strongly Connected Tail Calls: -//│ List(Set(j$2), Set(j$1), Set(j$0), Set(b, a)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { -//│ Def(0, a, [x$0], -//│ 1, -//│ let idCtx = _IdContext() in -- #129 -//│ let* (res) = a_modcons$8(idCtx,x$0) in -- #128 -//│ res -- #127 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, j$1, [x$5], -//│ 1, -//│ jump j$0(x$5) -- #10 -//│ ) -//│ Def(3, b, [n$0], -//│ 1, -//│ let idCtx = _IdContext() in -- #107 -//│ let* (res) = b_modcons$7(idCtx,n$0) in -- #106 -//│ res -- #105 -//│ ) -//│ Def(4, j$2, [x$14], -//│ 1, -//│ x$14 -- #50 -//│ ) -//│ Def(5, _b_a_ctx_app$5, [ctx,x], -//│ 1, -//│ case ctx of -- #88 -//│ _IdContext => -//│ x -- #87 -//│ _Context => -//│ let field = ctx.field in -- #86 -//│ let ptr = ctx.ptr in -- #85 -//│ let _ = assign ptr.n := x in -- #84 -//│ let acc = ctx.acc in -- #83 -//│ acc -- #82 -//│ ) -//│ Def(6, _b_a_ctx_comp$6, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #94 -//│ let ctx2ptr = ctx2.ptr in -- #93 -//│ let ctx2field = ctx2.field in -- #92 -//│ let* (newAcc) = _b_a_ctx_app$5(ctx1,ctx2acc) in -- #91 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #90 -//│ ret -- #89 -//│ ) -//│ Def(7, b_modcons$7, [ctx,n$0], -//│ 1, -//│ let* (r0) = _b_modcons$7_a_modcons$8_opt$9(7,ctx,n$0,undefined,undefined) in -- #170 -//│ r0 -- #169 -//│ ) -//│ Def(8, a_modcons$8, [ctx,x$0], -//│ 1, -//│ let* (r0) = _b_modcons$7_a_modcons$8_opt$9(8,undefined,undefined,ctx,x$0) in -- #172 -//│ r0 -- #171 -//│ ) -//│ Def(9, _b_modcons$7_a_modcons$8_opt$9, [tailrecBranch$,b_modcons$7_ctx,b_modcons$7_n$0,a_modcons$8_ctx,a_modcons$8_x$0], -//│ 1, -//│ jump _b_modcons$7_a_modcons$8_opt_jp$10(tailrecBranch$,b_modcons$7_ctx,b_modcons$7_n$0,a_modcons$8_ctx,a_modcons$8_x$0) -- #168 -//│ ) -//│ Def(10, _b_modcons$7_a_modcons$8_opt_jp$10, [tailrecBranch$,b_modcons$7_ctx,b_modcons$7_n$0,a_modcons$8_ctx,a_modcons$8_x$0], -//│ 1, -//│ let scrut = ==(8,tailrecBranch$) in -- #167 -//│ if scrut -- #166 -//│ true => -//│ case a_modcons$8_x$0 of -- #165 +//│ def a(x$1) = +//│ case x$1 of -- #54 //│ Cons => -//│ let x$2 = a_modcons$8_x$0.n in -- #161 -//│ let x$3 = a_modcons$8_x$0.m in -- #160 -//│ let x$4 = <(x$3,0) in -- #159 -//│ if x$4 -- #158 +//│ let x$3 = Cons.t(x$1) in -- #50 +//│ let x$4 = Cons.h(x$1) in -- #49 +//│ let x$5 = <(x$4,0) in -- #48 +//│ if x$5 -- #47 //│ true => -//│ let x$6 = Nil() in -- #151 -//│ let x$7 = Cons(-1,x$6) in -- #150 -//│ let* (res) = _b_a_ctx_app$5(a_modcons$8_ctx,x$7) in -- #149 -//│ res -- #148 +//│ let x$7 = Nil() in -- #28 +//│ let x$8 = Cons(-1,x$7) in -- #27 +//│ jump j$1(x$8) -- #26 //│ false => -//│ let x$8 = *(x$3,4) in -- #157 -//│ let x$9 = -(x$3,2) in -- #156 -//│ let x$11 = Cons(x$8,0) in -- #155 -//│ let ctx2 = _Context(x$11,x$11,0) in -- #154 -//│ let* (composed) = _b_a_ctx_comp$6(a_modcons$8_ctx,ctx2) in -- #153 -//│ jump _b_modcons$7_a_modcons$8_opt_jp$10(7,composed,x$9,a_modcons$8_ctx,a_modcons$8_x$0) -- #152 +//│ let x$9 = *(x$4,4) in -- #46 +//│ let x$10 = -(x$4,2) in -- #45 +//│ let* (x$11) = b(x$10) in -- #44 +//│ let x$12 = Cons(x$9,x$11) in -- #43 +//│ jump j$1(x$12) -- #42 //│ Nil => -//│ let x$12 = Nil() in -- #164 -//│ let* (res) = _b_a_ctx_app$5(a_modcons$8_ctx,x$12) in -- #163 -//│ res -- #162 -//│ false => -//│ let x$13 = <=(b_modcons$7_n$0,0) in -- #147 -//│ if x$13 -- #146 +//│ let x$13 = Nil() in -- #53 +//│ jump j$0(x$13) -- #52 +//│ def j$0(x$2) = +//│ x$2 -- #6 +//│ def j$1(x$6) = +//│ jump j$0(x$6) -- #19 +//│ def b(n$0) = +//│ let x$14 = <=(n$0,0) in -- #82 +//│ if x$14 -- #81 //│ true => -//│ let x$15 = Nil() in -- #142 -//│ let x$16 = Cons(0,x$15) in -- #141 -//│ let* (res) = _b_a_ctx_app$5(b_modcons$7_ctx,x$16) in -- #140 -//│ res -- #139 +//│ let x$16 = Nil() in -- #67 +//│ let x$17 = Cons(0,x$16) in -- #66 +//│ jump j$2(x$17) -- #65 //│ false => -//│ let x$17 = Nil() in -- #145 -//│ let x$18 = Cons(b_modcons$7_n$0,x$17) in -- #144 -//│ jump _b_modcons$7_a_modcons$8_opt_jp$10(8,b_modcons$7_ctx,b_modcons$7_n$0,b_modcons$7_ctx,x$18) -- #143 -//│ ) -//│ }, -//│ let* (x$20) = b(16) in -- #81 -//│ x$20 -- #80) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { -//│ Def(0, a, [x$0], -//│ 1, -//│ let idCtx = _IdContext() in -- #129 -//│ let* (res) = a_modcons$8(idCtx,x$0) in -- #128 -//│ res -- #127 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, j$1, [x$5], -//│ 1, -//│ jump j$0(x$5) -- #10 -//│ ) -//│ Def(3, b, [n$0], -//│ 1, -//│ let idCtx = _IdContext() in -- #107 -//│ let* (res) = b_modcons$7(idCtx,n$0) in -- #106 -//│ res -- #105 -//│ ) -//│ Def(4, j$2, [x$14], -//│ 1, -//│ x$14 -- #50 -//│ ) -//│ Def(5, _b_a_ctx_app$5, [ctx,x], -//│ 1, -//│ case ctx of -- #88 -//│ _IdContext => -//│ x -- #87 -//│ _Context => -//│ let field = ctx.field in -- #86 -//│ let ptr = ctx.ptr in -- #85 -//│ let _ = assign ptr.n := x in -- #84 -//│ let acc = ctx.acc in -- #83 -//│ acc -- #82 -//│ ) -//│ Def(6, _b_a_ctx_comp$6, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #94 -//│ let ctx2ptr = ctx2.ptr in -- #93 -//│ let ctx2field = ctx2.field in -- #92 -//│ let* (newAcc) = _b_a_ctx_app$5(ctx1,ctx2acc) in -- #91 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #90 -//│ ret -- #89 -//│ ) -//│ Def(7, b_modcons$7, [ctx,n$0], -//│ 1, -//│ let* (r0) = _b_modcons$7_a_modcons$8_opt$9(7,ctx,n$0,undefined,undefined) in -- #170 -//│ r0 -- #169 -//│ ) -//│ Def(8, a_modcons$8, [ctx,x$0], -//│ 1, -//│ let* (r0) = _b_modcons$7_a_modcons$8_opt$9(8,undefined,undefined,ctx,x$0) in -- #172 -//│ r0 -- #171 -//│ ) -//│ Def(9, _b_modcons$7_a_modcons$8_opt$9, [tailrecBranch$,b_modcons$7_ctx,b_modcons$7_n$0,a_modcons$8_ctx,a_modcons$8_x$0], -//│ 1, -//│ jump _b_modcons$7_a_modcons$8_opt_jp$10(tailrecBranch$,b_modcons$7_ctx,b_modcons$7_n$0,a_modcons$8_ctx,a_modcons$8_x$0) -- #168 -//│ ) -//│ Def(10, _b_modcons$7_a_modcons$8_opt_jp$10, [tailrecBranch$,b_modcons$7_ctx,b_modcons$7_n$0,a_modcons$8_ctx,a_modcons$8_x$0], -//│ 1, -//│ let scrut = ==(8,tailrecBranch$) in -- #167 -//│ if scrut -- #166 -//│ true => -//│ case a_modcons$8_x$0 of -- #165 -//│ Cons => -//│ let x$2 = a_modcons$8_x$0.n in -- #161 -//│ let x$3 = a_modcons$8_x$0.m in -- #160 -//│ let x$4 = <(x$3,0) in -- #159 -//│ if x$4 -- #158 -//│ true => -//│ let x$6 = Nil() in -- #151 -//│ let x$7 = Cons(-1,x$6) in -- #150 -//│ let* (res) = _b_a_ctx_app$5(a_modcons$8_ctx,x$7) in -- #149 -//│ res -- #148 -//│ false => -//│ let x$8 = *(x$3,4) in -- #157 -//│ let x$9 = -(x$3,2) in -- #156 -//│ let x$11 = Cons(x$8,0) in -- #155 -//│ let ctx2 = _Context(x$11,x$11,0) in -- #154 -//│ let* (composed) = _b_a_ctx_comp$6(a_modcons$8_ctx,ctx2) in -- #153 -//│ jump _b_modcons$7_a_modcons$8_opt_jp$10(7,composed,x$9,a_modcons$8_ctx,a_modcons$8_x$0) -- #152 -//│ Nil => -//│ let x$12 = Nil() in -- #164 -//│ let* (res) = _b_a_ctx_app$5(a_modcons$8_ctx,x$12) in -- #163 -//│ res -- #162 -//│ false => -//│ let x$13 = <=(b_modcons$7_n$0,0) in -- #147 -//│ if x$13 -- #146 +//│ let x$18 = Nil() in -- #80 +//│ let x$19 = Cons(n$0,x$18) in -- #79 +//│ let* (x$20) = a(x$19) in -- #78 +//│ jump j$2(x$20) -- #77 +//│ def j$2(x$15) = +//│ x$15 -- #58 +//│ let* (x$0) = b(16) in -- #4 +//│ x$0 -- #3 +//│ +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$2), Set(j$1), Set(j$0), Set(b, a)) +//│ Program: +//│ class _IdContext() +//│ class _Context(acc,ptr,field) +//│ def a(x$1) = +//│ let idCtx = _IdContext() in -- #136 +//│ let* (res) = a_modcons$14(idCtx,x$1) in -- #135 +//│ res -- #134 +//│ def j$0(x$2) = +//│ x$2 -- #6 +//│ def j$1(x$6) = +//│ jump j$0(x$6) -- #19 +//│ def b(n$0) = +//│ let idCtx = _IdContext() in -- #114 +//│ let* (res) = b_modcons$13(idCtx,n$0) in -- #113 +//│ res -- #112 +//│ def j$2(x$15) = +//│ x$15 -- #58 +//│ def _b_a_ctx_app$11(ctx,x) = +//│ case ctx of -- #95 +//│ _IdContext => +//│ x -- #94 +//│ _Context => +//│ let field = _Context.field(ctx) in -- #93 +//│ let ptr = _Context.ptr(ctx) in -- #92 +//│ let _ = assign ptr.t := x in -- #91 +//│ let acc = _Context.acc(ctx) in -- #90 +//│ acc -- #89 +//│ def _b_a_ctx_comp$12(ctx1,ctx2) = +//│ let ctx2acc = _Context.acc(ctx2) in -- #101 +//│ let ctx2ptr = _Context.ptr(ctx2) in -- #100 +//│ let ctx2field = _Context.field(ctx2) in -- #99 +//│ let* (newAcc) = _b_a_ctx_app$11(ctx1,ctx2acc) in -- #98 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #97 +//│ ret -- #96 +//│ def b_modcons$13(ctx,n$0) = +//│ let* (r0) = _b_modcons$13_a_modcons$14_opt$15(13,ctx,n$0,true,true) in -- #177 +//│ r0 -- #176 +//│ def a_modcons$14(ctx,x$1) = +//│ let* (r0) = _b_modcons$13_a_modcons$14_opt$15(14,true,true,ctx,x$1) in -- #179 +//│ r0 -- #178 +//│ def _b_modcons$13_a_modcons$14_opt$15(tailrecBranch$,b_modcons$13_ctx,b_modcons$13_n$0,a_modcons$14_ctx,a_modcons$14_x$1) = +//│ jump _b_modcons$13_a_modcons$14_opt_jp$16(tailrecBranch$,b_modcons$13_ctx,b_modcons$13_n$0,a_modcons$14_ctx,a_modcons$14_x$1) -- #175 +//│ def _b_modcons$13_a_modcons$14_opt_jp$16(tailrecBranch$,b_modcons$13_ctx,b_modcons$13_n$0,a_modcons$14_ctx,a_modcons$14_x$1) = +//│ let scrut = ==(14,tailrecBranch$) in -- #174 +//│ if scrut -- #173 //│ true => -//│ let x$15 = Nil() in -- #142 -//│ let x$16 = Cons(0,x$15) in -- #141 -//│ let* (res) = _b_a_ctx_app$5(b_modcons$7_ctx,x$16) in -- #140 -//│ res -- #139 +//│ case a_modcons$14_x$1 of -- #172 +//│ Cons => +//│ let x$3 = Cons.t(a_modcons$14_x$1) in -- #168 +//│ let x$4 = Cons.h(a_modcons$14_x$1) in -- #167 +//│ let x$5 = <(x$4,0) in -- #166 +//│ if x$5 -- #165 +//│ true => +//│ let x$7 = Nil() in -- #158 +//│ let x$8 = Cons(-1,x$7) in -- #157 +//│ let* (res) = _b_a_ctx_app$11(a_modcons$14_ctx,x$8) in -- #156 +//│ res -- #155 +//│ false => +//│ let x$9 = *(x$4,4) in -- #164 +//│ let x$10 = -(x$4,2) in -- #163 +//│ let x$12 = Cons(x$9,0) in -- #162 +//│ let ctx2 = _Context(x$12,x$12,0) in -- #161 +//│ let* (composed) = _b_a_ctx_comp$12(a_modcons$14_ctx,ctx2) in -- #160 +//│ jump _b_modcons$13_a_modcons$14_opt_jp$16(13,composed,x$10,a_modcons$14_ctx,a_modcons$14_x$1) -- #159 +//│ Nil => +//│ let x$13 = Nil() in -- #171 +//│ let* (res) = _b_a_ctx_app$11(a_modcons$14_ctx,x$13) in -- #170 +//│ res -- #169 //│ false => -//│ let x$17 = Nil() in -- #145 -//│ let x$18 = Cons(b_modcons$7_n$0,x$17) in -- #144 -//│ jump _b_modcons$7_a_modcons$8_opt_jp$10(8,b_modcons$7_ctx,b_modcons$7_n$0,b_modcons$7_ctx,x$18) -- #143 -//│ ) -//│ }, -//│ let* (x$20) = b(16) in -- #81 -//│ x$20 -- #80) +//│ let x$14 = <=(b_modcons$13_n$0,0) in -- #154 +//│ if x$14 -- #153 +//│ true => +//│ let x$16 = Nil() in -- #149 +//│ let x$17 = Cons(0,x$16) in -- #148 +//│ let* (res) = _b_a_ctx_app$11(b_modcons$13_ctx,x$17) in -- #147 +//│ res -- #146 +//│ false => +//│ let x$18 = Nil() in -- #152 +//│ let x$19 = Cons(b_modcons$13_n$0,x$18) in -- #151 +//│ jump _b_modcons$13_a_modcons$14_opt_jp$16(14,b_modcons$13_ctx,b_modcons$13_n$0,b_modcons$13_ctx,x$19) -- #150 +//│ let* (x$0) = b(16) in -- #4 +//│ x$0 -- #3 //│ //│ Interpreted: -//│ Cons(64,Cons(56,Cons(48,Cons(40,Cons(32,Cons(24,Cons(16,Cons(8,Cons(0,Nil()))))))))) +//│ Cons(64,0) :noTailRec :interpIR -class Cons(h, t) -class Nil fun foo(xs) = if xs is Cons(h, t) then @@ -1653,66 +1009,57 @@ fun foo(xs) = Nil then Nil foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) -//│ |#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| -//│ Parsed: {class Cons(h, t,) {}; class Nil {}; fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { -//│ Def(0, foo, [xs$0], -//│ 1, -//│ case xs$0 of -- #54 -//│ Cons => -//│ let x$1 = xs$0.t in -- #50 -//│ let x$2 = xs$0.h in -- #49 -//│ let x$3 = >(x$2,5) in -- #48 -//│ if x$3 -- #47 -//│ true => -//│ let* (x$5) = foo(x$1) in -- #17 -//│ jump j$1(x$5) -- #16 -//│ false => -//│ let x$6 = <(x$2,3) in -- #46 -//│ if x$6 -- #45 +//│ |#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| +//│ Parsed: {fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def foo(xs$0) = +//│ case xs$0 of -- #104 +//│ Cons => +//│ let x$10 = Cons.t(xs$0) in -- #100 +//│ let x$11 = Cons.h(xs$0) in -- #99 +//│ let x$12 = >(x$11,5) in -- #98 +//│ if x$12 -- #97 //│ true => -//│ jump j$2(-1,x$1,x$2) -- #42 +//│ let* (x$14) = foo(x$10) in -- #68 +//│ jump j$1(x$14) -- #67 //│ false => -//│ jump j$2(100,x$1,x$2) -- #44 -//│ Nil => -//│ let x$11 = Nil() in -- #53 -//│ jump j$0(x$11) -- #52 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, j$1, [x$4], -//│ 1, -//│ jump j$0(x$4) -- #10 -//│ ) -//│ Def(3, j$2, [x$7,x$1,x$2], -//│ 1, -//│ let* (x$8) = foo(x$1) in -- #40 -//│ let x$9 = Cons(x$2,x$8) in -- #39 -//│ let x$10 = Cons(x$7,x$9) in -- #38 -//│ jump j$1(x$10) -- #37 -//│ ) -//│ }, -//│ let x$12 = Nil() in -- #103 -//│ let x$13 = Cons(9,x$12) in -- #102 -//│ let x$14 = Cons(3,x$13) in -- #101 -//│ let x$15 = Cons(2,x$14) in -- #100 -//│ let x$16 = Cons(4,x$15) in -- #99 -//│ let x$17 = Cons(7,x$16) in -- #98 -//│ let x$18 = Cons(6,x$17) in -- #97 -//│ let x$19 = Cons(1,x$18) in -- #96 -//│ let* (x$20) = foo(x$19) in -- #95 -//│ x$20 -- #94) +//│ let x$15 = <(x$11,3) in -- #96 +//│ if x$15 -- #95 +//│ true => +//│ jump j$2(-1,x$10,x$11) -- #92 +//│ false => +//│ jump j$2(100,x$10,x$11) -- #94 +//│ Nil => +//│ let x$20 = Nil() in -- #103 +//│ jump j$0(x$20) -- #102 +//│ def j$0(x$9) = +//│ x$9 -- #49 +//│ def j$1(x$13) = +//│ jump j$0(x$13) -- #62 +//│ def j$2(x$16,x$10,x$11) = +//│ let* (x$17) = foo(x$10) in -- #90 +//│ let x$18 = Cons(x$11,x$17) in -- #89 +//│ let x$19 = Cons(x$16,x$18) in -- #88 +//│ jump j$1(x$19) -- #87 +//│ let x$0 = Nil() in -- #47 +//│ let x$1 = Cons(9,x$0) in -- #46 +//│ let x$2 = Cons(3,x$1) in -- #45 +//│ let x$3 = Cons(2,x$2) in -- #44 +//│ let x$4 = Cons(4,x$3) in -- #43 +//│ let x$5 = Cons(7,x$4) in -- #42 +//│ let x$6 = Cons(6,x$5) in -- #41 +//│ let x$7 = Cons(1,x$6) in -- #40 +//│ let* (x$8) = foo(x$7) in -- #39 +//│ x$8 -- #38 //│ //│ Interpreted: //│ Cons(-1,Cons(1,Cons(100,Cons(4,Cons(-1,Cons(2,Cons(100,Cons(3,Nil())))))))) :interpIR -class Cons(h, t) -class Nil @tailrec fun foo(xs) = if xs is Cons(h, t) then @@ -1723,241 +1070,131 @@ class Nil Nil then Nil foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) -//│ |#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |@|tailcall| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |@|tailcall| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| -//│ Parsed: {class Cons(h, t,) {}; class Nil {}; fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then @tailcall foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, @tailcall foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} +//│ |@|tailrec| |#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |@|tailcall| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |@|tailcall| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| +//│ Parsed: {fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then @tailcall foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, @tailcall foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} +//│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { -//│ Def(0, foo, [xs$0], -//│ 1, -//│ case xs$0 of -- #54 -//│ Cons => -//│ let x$1 = xs$0.t in -- #50 -//│ let x$2 = xs$0.h in -- #49 -//│ let x$3 = >(x$2,5) in -- #48 -//│ if x$3 -- #47 -//│ true => -//│ let* (x$5) = @tailcall foo(x$1) in -- #17 -//│ jump j$1(x$5) -- #16 -//│ false => -//│ let x$6 = <(x$2,3) in -- #46 -//│ if x$6 -- #45 -//│ true => -//│ jump j$2(-1,x$1,x$2) -- #42 -//│ false => -//│ jump j$2(100,x$1,x$2) -- #44 -//│ Nil => -//│ let x$11 = Nil() in -- #53 -//│ jump j$0(x$11) -- #52 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, j$1, [x$4], -//│ 1, -//│ jump j$0(x$4) -- #10 -//│ ) -//│ Def(3, j$2, [x$7,x$1,x$2], -//│ 1, -//│ let* (x$8) = @tailcall foo(x$1) in -- #40 -//│ let x$9 = Cons(x$2,x$8) in -- #39 -//│ let x$10 = Cons(x$7,x$9) in -- #38 -//│ jump j$1(x$10) -- #37 -//│ ) -//│ }, -//│ let x$12 = Nil() in -- #103 -//│ let x$13 = Cons(9,x$12) in -- #102 -//│ let x$14 = Cons(3,x$13) in -- #101 -//│ let x$15 = Cons(2,x$14) in -- #100 -//│ let x$16 = Cons(4,x$15) in -- #99 -//│ let x$17 = Cons(7,x$16) in -- #98 -//│ let x$18 = Cons(6,x$17) in -- #97 -//│ let x$19 = Cons(1,x$18) in -- #96 -//│ let* (x$20) = foo(x$19) in -- #95 -//│ x$20 -- #94) +//│ Program: //│ -//│ Strongly Connected Tail Calls: -//│ List(Set(j$1), Set(j$0), Set(foo)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { -//│ Def(0, foo, [xs$0], -//│ 1, -//│ let idCtx = _IdContext() in -- #137 -//│ let* (res) = foo_modcons$7(idCtx,xs$0) in -- #136 -//│ res -- #135 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, j$1, [x$4], -//│ 1, -//│ jump j$0(x$4) -- #10 -//│ ) -//│ Def(4, _foo_ctx_app$4, [ctx,x], -//│ 1, -//│ case ctx of -- #110 -//│ _IdContext => -//│ x -- #109 -//│ _Context => -//│ let field = ctx.field in -- #108 -//│ let ptr = ctx.ptr in -- #107 -//│ let _ = assign ptr.t := x in -- #106 -//│ let acc = ctx.acc in -- #105 -//│ acc -- #104 -//│ ) -//│ Def(5, _foo_ctx_comp$5, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #116 -//│ let ctx2ptr = ctx2.ptr in -- #115 -//│ let ctx2field = ctx2.field in -- #114 -//│ let* (newAcc) = _foo_ctx_app$4(ctx1,ctx2acc) in -- #113 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #112 -//│ ret -- #111 -//│ ) -//│ Def(7, foo_modcons$7, [ctx,xs$0], -//│ 1, -//│ let* (r0) = _foo_modcons$7_j$2_modcons$6_opt$8(7,ctx,xs$0,undefined,undefined,undefined,undefined) in -- #173 -//│ r0 -- #172 -//│ ) -//│ Def(8, _foo_modcons$7_j$2_modcons$6_opt$8, [tailrecBranch$,foo_modcons$7_ctx,foo_modcons$7_xs$0,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2], -//│ 1, -//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(tailrecBranch$,foo_modcons$7_ctx,foo_modcons$7_xs$0,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2) -- #171 -//│ ) -//│ Def(9, _foo_modcons$7_j$2_modcons$6_opt_jp$9, [tailrecBranch$,foo_modcons$7_ctx,foo_modcons$7_xs$0,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2], -//│ 1, -//│ let scrut = ==(6,tailrecBranch$) in -- #170 -//│ if scrut -- #169 -//│ true => -//│ let x$9 = Cons(j$2_modcons$6_x$2,0) in -- #168 -//│ let x$10 = Cons(j$2_modcons$6_x$7,x$9) in -- #167 -//│ let ctx2 = _Context(x$10,x$9,0) in -- #166 -//│ let* (composed) = _foo_ctx_comp$5(j$2_modcons$6_ctx,ctx2) in -- #165 -//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(7,composed,j$2_modcons$6_x$1,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2) -- #164 -//│ false => -//│ case foo_modcons$7_xs$0 of -- #163 +//│ def foo(xs$0) = +//│ case xs$0 of -- #104 //│ Cons => -//│ let x$1 = foo_modcons$7_xs$0.t in -- #159 -//│ let x$2 = foo_modcons$7_xs$0.h in -- #158 -//│ let x$3 = >(x$2,5) in -- #157 -//│ if x$3 -- #156 +//│ let x$10 = Cons.t(xs$0) in -- #100 +//│ let x$11 = Cons.h(xs$0) in -- #99 +//│ let x$12 = >(x$11,5) in -- #98 +//│ if x$12 -- #97 //│ true => -//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(7,foo_modcons$7_ctx,x$1,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2) -- #151 +//│ let* (x$14) = @tailcall foo(x$10) in -- #68 +//│ jump j$1(x$14) -- #67 //│ false => -//│ let x$6 = <(x$2,3) in -- #155 -//│ if x$6 -- #154 +//│ let x$15 = <(x$11,3) in -- #96 +//│ if x$15 -- #95 //│ true => -//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(6,foo_modcons$7_ctx,foo_modcons$7_xs$0,foo_modcons$7_ctx,-1,x$1,x$2) -- #152 +//│ jump j$2(-1,x$10,x$11) -- #92 //│ false => -//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(6,foo_modcons$7_ctx,foo_modcons$7_xs$0,foo_modcons$7_ctx,100,x$1,x$2) -- #153 +//│ jump j$2(100,x$10,x$11) -- #94 //│ Nil => -//│ let x$11 = Nil() in -- #162 -//│ let* (res) = _foo_ctx_app$4(foo_modcons$7_ctx,x$11) in -- #161 -//│ res -- #160 -//│ ) -//│ }, -//│ let x$12 = Nil() in -- #103 -//│ let x$13 = Cons(9,x$12) in -- #102 -//│ let x$14 = Cons(3,x$13) in -- #101 -//│ let x$15 = Cons(2,x$14) in -- #100 -//│ let x$16 = Cons(4,x$15) in -- #99 -//│ let x$17 = Cons(7,x$16) in -- #98 -//│ let x$18 = Cons(6,x$17) in -- #97 -//│ let x$19 = Cons(1,x$18) in -- #96 -//│ let* (x$20) = foo(x$19) in -- #95 -//│ x$20 -- #94) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { -//│ Def(0, foo, [xs$0], -//│ 1, -//│ let idCtx = _IdContext() in -- #137 -//│ let* (res) = foo_modcons$7(idCtx,xs$0) in -- #136 -//│ res -- #135 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, j$1, [x$4], -//│ 1, -//│ jump j$0(x$4) -- #10 -//│ ) -//│ Def(4, _foo_ctx_app$4, [ctx,x], -//│ 1, -//│ case ctx of -- #110 -//│ _IdContext => -//│ x -- #109 -//│ _Context => -//│ let field = ctx.field in -- #108 -//│ let ptr = ctx.ptr in -- #107 -//│ let _ = assign ptr.t := x in -- #106 -//│ let acc = ctx.acc in -- #105 -//│ acc -- #104 -//│ ) -//│ Def(5, _foo_ctx_comp$5, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #116 -//│ let ctx2ptr = ctx2.ptr in -- #115 -//│ let ctx2field = ctx2.field in -- #114 -//│ let* (newAcc) = _foo_ctx_app$4(ctx1,ctx2acc) in -- #113 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #112 -//│ ret -- #111 -//│ ) -//│ Def(7, foo_modcons$7, [ctx,xs$0], -//│ 1, -//│ let* (r0) = _foo_modcons$7_j$2_modcons$6_opt$8(7,ctx,xs$0,undefined,undefined,undefined,undefined) in -- #173 -//│ r0 -- #172 -//│ ) -//│ Def(8, _foo_modcons$7_j$2_modcons$6_opt$8, [tailrecBranch$,foo_modcons$7_ctx,foo_modcons$7_xs$0,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2], -//│ 1, -//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(tailrecBranch$,foo_modcons$7_ctx,foo_modcons$7_xs$0,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2) -- #171 -//│ ) -//│ Def(9, _foo_modcons$7_j$2_modcons$6_opt_jp$9, [tailrecBranch$,foo_modcons$7_ctx,foo_modcons$7_xs$0,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2], -//│ 1, -//│ let scrut = ==(6,tailrecBranch$) in -- #170 -//│ if scrut -- #169 -//│ true => -//│ let x$9 = Cons(j$2_modcons$6_x$2,0) in -- #168 -//│ let x$10 = Cons(j$2_modcons$6_x$7,x$9) in -- #167 -//│ let ctx2 = _Context(x$10,x$9,0) in -- #166 -//│ let* (composed) = _foo_ctx_comp$5(j$2_modcons$6_ctx,ctx2) in -- #165 -//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(7,composed,j$2_modcons$6_x$1,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2) -- #164 -//│ false => -//│ case foo_modcons$7_xs$0 of -- #163 -//│ Cons => -//│ let x$1 = foo_modcons$7_xs$0.t in -- #159 -//│ let x$2 = foo_modcons$7_xs$0.h in -- #158 -//│ let x$3 = >(x$2,5) in -- #157 -//│ if x$3 -- #156 -//│ true => -//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(7,foo_modcons$7_ctx,x$1,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2) -- #151 -//│ false => -//│ let x$6 = <(x$2,3) in -- #155 -//│ if x$6 -- #154 +//│ let x$20 = Nil() in -- #103 +//│ jump j$0(x$20) -- #102 +//│ def j$0(x$9) = +//│ x$9 -- #49 +//│ def j$1(x$13) = +//│ jump j$0(x$13) -- #62 +//│ def j$2(x$16,x$10,x$11) = +//│ let* (x$17) = @tailcall foo(x$10) in -- #90 +//│ let x$18 = Cons(x$11,x$17) in -- #89 +//│ let x$19 = Cons(x$16,x$18) in -- #88 +//│ jump j$1(x$19) -- #87 +//│ let x$0 = Nil() in -- #47 +//│ let x$1 = Cons(9,x$0) in -- #46 +//│ let x$2 = Cons(3,x$1) in -- #45 +//│ let x$3 = Cons(2,x$2) in -- #44 +//│ let x$4 = Cons(4,x$3) in -- #43 +//│ let x$5 = Cons(7,x$4) in -- #42 +//│ let x$6 = Cons(6,x$5) in -- #41 +//│ let x$7 = Cons(1,x$6) in -- #40 +//│ let* (x$8) = foo(x$7) in -- #39 +//│ x$8 -- #38 +//│ +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$1), Set(j$0), Set(foo)) +//│ Program: +//│ class _IdContext() +//│ class _Context(acc,ptr,field) +//│ def foo(xs$0) = +//│ let idCtx = _IdContext() in -- #144 +//│ let* (res) = foo_modcons$13(idCtx,xs$0) in -- #143 +//│ res -- #142 +//│ def j$0(x$9) = +//│ x$9 -- #49 +//│ def j$1(x$13) = +//│ jump j$0(x$13) -- #62 +//│ def _foo_ctx_app$10(ctx,x) = +//│ case ctx of -- #117 +//│ _IdContext => +//│ x -- #116 +//│ _Context => +//│ let field = _Context.field(ctx) in -- #115 +//│ let ptr = _Context.ptr(ctx) in -- #114 +//│ let _ = assign ptr.t := x in -- #113 +//│ let acc = _Context.acc(ctx) in -- #112 +//│ acc -- #111 +//│ def _foo_ctx_comp$11(ctx1,ctx2) = +//│ let ctx2acc = _Context.acc(ctx2) in -- #123 +//│ let ctx2ptr = _Context.ptr(ctx2) in -- #122 +//│ let ctx2field = _Context.field(ctx2) in -- #121 +//│ let* (newAcc) = _foo_ctx_app$10(ctx1,ctx2acc) in -- #120 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #119 +//│ ret -- #118 +//│ def foo_modcons$13(ctx,xs$0) = +//│ let* (r0) = _foo_modcons$13_j$2_modcons$12_opt$14(13,ctx,xs$0,true,true,true,true) in -- #180 +//│ r0 -- #179 +//│ def _foo_modcons$13_j$2_modcons$12_opt$14(tailrecBranch$,foo_modcons$13_ctx,foo_modcons$13_xs$0,j$2_modcons$12_ctx,j$2_modcons$12_x$16,j$2_modcons$12_x$10,j$2_modcons$12_x$11) = +//│ jump _foo_modcons$13_j$2_modcons$12_opt_jp$15(tailrecBranch$,foo_modcons$13_ctx,foo_modcons$13_xs$0,j$2_modcons$12_ctx,j$2_modcons$12_x$16,j$2_modcons$12_x$10,j$2_modcons$12_x$11) -- #178 +//│ def _foo_modcons$13_j$2_modcons$12_opt_jp$15(tailrecBranch$,foo_modcons$13_ctx,foo_modcons$13_xs$0,j$2_modcons$12_ctx,j$2_modcons$12_x$16,j$2_modcons$12_x$10,j$2_modcons$12_x$11) = +//│ let scrut = ==(12,tailrecBranch$) in -- #177 +//│ if scrut -- #176 +//│ true => +//│ let x$18 = Cons(j$2_modcons$12_x$11,0) in -- #175 +//│ let x$19 = Cons(j$2_modcons$12_x$16,x$18) in -- #174 +//│ let ctx2 = _Context(x$19,x$18,0) in -- #173 +//│ let* (composed) = _foo_ctx_comp$11(j$2_modcons$12_ctx,ctx2) in -- #172 +//│ jump _foo_modcons$13_j$2_modcons$12_opt_jp$15(13,composed,j$2_modcons$12_x$10,j$2_modcons$12_ctx,j$2_modcons$12_x$16,j$2_modcons$12_x$10,j$2_modcons$12_x$11) -- #171 +//│ false => +//│ case foo_modcons$13_xs$0 of -- #170 +//│ Cons => +//│ let x$10 = Cons.t(foo_modcons$13_xs$0) in -- #166 +//│ let x$11 = Cons.h(foo_modcons$13_xs$0) in -- #165 +//│ let x$12 = >(x$11,5) in -- #164 +//│ if x$12 -- #163 //│ true => -//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(6,foo_modcons$7_ctx,foo_modcons$7_xs$0,foo_modcons$7_ctx,-1,x$1,x$2) -- #152 +//│ jump _foo_modcons$13_j$2_modcons$12_opt_jp$15(13,foo_modcons$13_ctx,x$10,j$2_modcons$12_ctx,j$2_modcons$12_x$16,j$2_modcons$12_x$10,j$2_modcons$12_x$11) -- #158 //│ false => -//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(6,foo_modcons$7_ctx,foo_modcons$7_xs$0,foo_modcons$7_ctx,100,x$1,x$2) -- #153 -//│ Nil => -//│ let x$11 = Nil() in -- #162 -//│ let* (res) = _foo_ctx_app$4(foo_modcons$7_ctx,x$11) in -- #161 -//│ res -- #160 -//│ ) -//│ }, -//│ let x$12 = Nil() in -- #103 -//│ let x$13 = Cons(9,x$12) in -- #102 -//│ let x$14 = Cons(3,x$13) in -- #101 -//│ let x$15 = Cons(2,x$14) in -- #100 -//│ let x$16 = Cons(4,x$15) in -- #99 -//│ let x$17 = Cons(7,x$16) in -- #98 -//│ let x$18 = Cons(6,x$17) in -- #97 -//│ let x$19 = Cons(1,x$18) in -- #96 -//│ let* (x$20) = foo(x$19) in -- #95 -//│ x$20 -- #94) +//│ let x$15 = <(x$11,3) in -- #162 +//│ if x$15 -- #161 +//│ true => +//│ jump _foo_modcons$13_j$2_modcons$12_opt_jp$15(12,foo_modcons$13_ctx,foo_modcons$13_xs$0,foo_modcons$13_ctx,-1,x$10,x$11) -- #159 +//│ false => +//│ jump _foo_modcons$13_j$2_modcons$12_opt_jp$15(12,foo_modcons$13_ctx,foo_modcons$13_xs$0,foo_modcons$13_ctx,100,x$10,x$11) -- #160 +//│ Nil => +//│ let x$20 = Nil() in -- #169 +//│ let* (res) = _foo_ctx_app$10(foo_modcons$13_ctx,x$20) in -- #168 +//│ res -- #167 +//│ let x$0 = Nil() in -- #47 +//│ let x$1 = Cons(9,x$0) in -- #46 +//│ let x$2 = Cons(3,x$1) in -- #45 +//│ let x$3 = Cons(2,x$2) in -- #44 +//│ let x$4 = Cons(4,x$3) in -- #43 +//│ let x$5 = Cons(7,x$4) in -- #42 +//│ let x$6 = Cons(6,x$5) in -- #41 +//│ let x$7 = Cons(1,x$6) in -- #40 +//│ let* (x$8) = foo(x$7) in -- #39 +//│ x$8 -- #38 //│ //│ Interpreted: -//│ Cons(-1,Cons(1,Cons(100,Cons(4,Cons(-1,Cons(2,Cons(100,Cons(3,Nil())))))))) +//│ Cons(-1,Cons(1,0)) :ce fun b() = @@ -1971,117 +1208,65 @@ a() //│ |#fun| |b|(||)| |#=|→|a|(||)|↵|a|(||)|←|↵|@|tailrec| |↵|#fun| |a|(||)| |#=| |→|#if| |0| |<| |1| |#then| |a|(||)|↵|#else| |b|(||)|←|↵|a|(||)| //│ Parsed: {fun b = () => {a(); a()}; fun a = () => {if (<(0,)(1,)) then a() else b()}; a()} //│ +//│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, b, [], -//│ 1, -//│ let* (x$0) = a() in -- #7 -//│ let* (x$1) = a() in -- #6 -//│ x$1 -- #5 -//│ ) -//│ Def(1, a, [], -//│ 1, -//│ let x$2 = <(0,1) in -- #23 -//│ if x$2 -- #22 -//│ true => -//│ let* (x$4) = a() in -- #16 -//│ jump j$0(x$4) -- #15 -//│ false => -//│ let* (x$5) = b() in -- #21 -//│ jump j$0(x$5) -- #20 -//│ ) -//│ Def(2, j$0, [x$3], -//│ 1, -//│ x$3 -- #11 -//│ ) -//│ }, -//│ let* (x$6) = a() in -- #27 -//│ x$6 -- #26) +//│ Program: +//│ +//│ def b() = +//│ let* (x$1) = a() in -- #8 +//│ let* (x$2) = a() in -- #7 +//│ x$2 -- #6 +//│ def a() = +//│ let x$3 = <(0,1) in -- #22 +//│ if x$3 -- #21 +//│ true => +//│ let* (x$5) = a() in -- #16 +//│ jump j$0(x$5) -- #15 +//│ false => +//│ let* (x$6) = b() in -- #20 +//│ jump j$0(x$6) -- #19 +//│ def j$0(x$4) = +//│ x$4 -- #12 +//│ let* (x$0) = a() in -- #2 +//│ x$0 -- #1 //│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec -//│ ║ l.1966: @tailrec +//│ ║ l.1203: @tailrec //│ ║ ^^^^^^^ //│ ╟── it could self-recurse through this call, which may not be a tail-call -//│ ║ l.1964: a() +//│ ║ l.1201: a() //│ ╙── ^ //│ +//│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(a, b)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, b, [], -//│ 1, -//│ let* (r0) = _a_b_opt$3(0) in -- #44 -//│ r0 -- #43 -//│ ) -//│ Def(1, a, [], -//│ 1, -//│ let* (r0) = _a_b_opt$3(1) in -- #42 -//│ r0 -- #41 -//│ ) -//│ Def(2, j$0, [x$3], -//│ 1, -//│ x$3 -- #11 -//│ ) -//│ Def(3, _a_b_opt$3, [tailrecBranch$], -//│ 1, -//│ jump _a_b_opt_jp$4(tailrecBranch$) -- #40 -//│ ) -//│ Def(4, _a_b_opt_jp$4, [tailrecBranch$], -//│ 1, -//│ let scrut = ==(0,tailrecBranch$) in -- #39 -//│ if scrut -- #38 -//│ true => -//│ let* (x$0) = a() in -- #37 -//│ jump _a_b_opt_jp$4(1) -- #36 -//│ false => -//│ let x$2 = <(0,1) in -- #35 -//│ if x$2 -- #34 +//│ Program: +//│ class _IdContext() +//│ class _Context(acc,ptr,field) +//│ def b() = +//│ let* (r0) = _a_b_opt$9(0) in -- #45 +//│ r0 -- #44 +//│ def a() = +//│ let* (r0) = _a_b_opt$9(1) in -- #43 +//│ r0 -- #42 +//│ def j$0(x$4) = +//│ x$4 -- #12 +//│ def _a_b_opt$9(tailrecBranch$) = +//│ jump _a_b_opt_jp$10(tailrecBranch$) -- #41 +//│ def _a_b_opt_jp$10(tailrecBranch$) = +//│ let scrut = ==(0,tailrecBranch$) in -- #40 +//│ if scrut -- #39 //│ true => -//│ jump _a_b_opt_jp$4(1) -- #32 +//│ let* (x$1) = a() in -- #38 +//│ jump _a_b_opt_jp$10(1) -- #37 //│ false => -//│ jump _a_b_opt_jp$4(0) -- #33 -//│ ) -//│ }, -//│ let* (x$6) = a() in -- #27 -//│ x$6 -- #26) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, b, [], -//│ 1, -//│ let* (r0) = _a_b_opt$3(0) in -- #44 -//│ r0 -- #43 -//│ ) -//│ Def(1, a, [], -//│ 1, -//│ let* (r0) = _a_b_opt$3(1) in -- #42 -//│ r0 -- #41 -//│ ) -//│ Def(2, j$0, [x$3], -//│ 1, -//│ x$3 -- #11 -//│ ) -//│ Def(3, _a_b_opt$3, [tailrecBranch$], -//│ 1, -//│ jump _a_b_opt_jp$4(tailrecBranch$) -- #40 -//│ ) -//│ Def(4, _a_b_opt_jp$4, [tailrecBranch$], -//│ 1, -//│ let scrut = ==(0,tailrecBranch$) in -- #39 -//│ if scrut -- #38 -//│ true => -//│ let* (x$0) = a() in -- #37 -//│ jump _a_b_opt_jp$4(1) -- #36 -//│ false => -//│ let x$2 = <(0,1) in -- #35 -//│ if x$2 -- #34 -//│ true => -//│ jump _a_b_opt_jp$4(1) -- #32 -//│ false => -//│ jump _a_b_opt_jp$4(0) -- #33 -//│ ) -//│ }, -//│ let* (x$6) = a() in -- #27 -//│ x$6 -- #26) +//│ let x$3 = <(0,1) in -- #36 +//│ if x$3 -- #35 +//│ true => +//│ jump _a_b_opt_jp$10(1) -- #33 +//│ false => +//│ jump _a_b_opt_jp$10(0) -- #34 +//│ let* (x$0) = a() in -- #2 +//│ x$0 -- #1 :ce class A(a, b) @@ -2093,229 +1278,111 @@ a() //│ |#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|c|(||)|,| |@|tailcall| |a|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|a|(||)| //│ Parsed: {class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(c(), @tailcall a(),); fun c = () => A(b(), 1,); a()} //│ +//│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b])}, { -//│ Def(0, a, [], -//│ 1, -//│ let* (x$0) = b() in -- #9 -//│ let x$1 = A(x$0,1) in -- #8 -//│ x$1 -- #7 -//│ ) -//│ Def(1, b, [], -//│ 1, -//│ let* (x$2) = c() in -- #22 -//│ let* (x$3) = @tailcall a() in -- #21 -//│ let x$4 = A(x$2,x$3) in -- #20 -//│ x$4 -- #19 -//│ ) -//│ Def(2, c, [], -//│ 1, -//│ let* (x$5) = b() in -- #32 -//│ let x$6 = A(x$5,1) in -- #31 -//│ x$6 -- #30 -//│ ) -//│ }, -//│ let* (x$7) = a() in -- #36 -//│ x$7 -- #35) +//│ Program: +//│ class A(a,b) +//│ def a() = +//│ let* (x$1) = b() in -- #11 +//│ let x$2 = A(x$1,1) in -- #10 +//│ x$2 -- #9 +//│ def b() = +//│ let* (x$3) = c() in -- #22 +//│ let* (x$4) = @tailcall a() in -- #21 +//│ let x$5 = A(x$3,x$4) in -- #20 +//│ x$5 -- #19 +//│ def c() = +//│ let* (x$6) = b() in -- #31 +//│ let x$7 = A(x$6,1) in -- #30 +//│ x$7 -- #29 +//│ let* (x$0) = a() in -- #2 +//│ x$0 -- #1 //│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec -//│ ║ l.2088: @tailrec +//│ ║ l.1273: @tailrec //│ ║ ^^^^^^^ //│ ╟── it could self-recurse through this call, which may not be a tail-call -//│ ║ l.2090: fun b() = A(c(), @tailcall a()) +//│ ║ l.1275: fun b() = A(c(), @tailcall a()) //│ ╙── ^ //│ +//│ //│ Strongly Connected Tail Calls: //│ List(Set(c, b, a)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { -//│ Def(0, a, [], -//│ 1, -//│ let idCtx = _IdContext() in -- #80 -//│ let* (res) = a_modcons$7(idCtx) in -- #79 -//│ res -- #78 -//│ ) -//│ Def(1, b, [], -//│ 1, -//│ let idCtx = _IdContext() in -- #72 -//│ let* (res) = b_modcons$6(idCtx) in -- #71 -//│ res -- #70 -//│ ) -//│ Def(2, c, [], -//│ 1, -//│ let idCtx = _IdContext() in -- #63 -//│ let* (res) = c_modcons$5(idCtx) in -- #62 -//│ res -- #61 -//│ ) -//│ Def(3, _c_b_a_ctx_app$3, [ctx,x], -//│ 1, -//│ case ctx of -- #49 -//│ _IdContext => -//│ x -- #48 -//│ _Context => -//│ let field = ctx.field in -- #47 -//│ let scrut = ==(1,field) in -- #46 -//│ if scrut -- #45 -//│ true => -//│ let ptr = ctx.ptr in -- #44 -//│ let _ = assign ptr.b := x in -- #43 -//│ let acc = ctx.acc in -- #42 -//│ acc -- #41 -//│ false => -//│ let ptr = ctx.ptr in -- #40 -//│ let _ = assign ptr.a := x in -- #39 -//│ let acc = ctx.acc in -- #38 -//│ acc -- #37 -//│ ) -//│ Def(4, _c_b_a_ctx_comp$4, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #55 -//│ let ctx2ptr = ctx2.ptr in -- #54 -//│ let ctx2field = ctx2.field in -- #53 -//│ let* (newAcc) = _c_b_a_ctx_app$3(ctx1,ctx2acc) in -- #52 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #51 -//│ ret -- #50 -//│ ) -//│ Def(5, c_modcons$5, [ctx], -//│ 1, -//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(5,ctx,undefined,undefined) in -- #104 -//│ r0 -- #103 -//│ ) -//│ Def(6, b_modcons$6, [ctx], -//│ 1, -//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(6,undefined,ctx,undefined) in -- #106 -//│ r0 -- #105 -//│ ) -//│ Def(7, a_modcons$7, [ctx], -//│ 1, -//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(7,undefined,undefined,ctx) in -- #108 -//│ r0 -- #107 -//│ ) -//│ Def(8, _c_modcons$5_b_modcons$6_a_modcons$7_opt$8, [tailrecBranch$,c_modcons$5_ctx,b_modcons$6_ctx,a_modcons$7_ctx], -//│ 1, -//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(tailrecBranch$,c_modcons$5_ctx,b_modcons$6_ctx,a_modcons$7_ctx) -- #102 -//│ ) -//│ Def(9, _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9, [tailrecBranch$,c_modcons$5_ctx,b_modcons$6_ctx,a_modcons$7_ctx], -//│ 1, -//│ let scrut = ==(7,tailrecBranch$) in -- #101 -//│ if scrut -- #100 -//│ true => -//│ let x$1 = A(0,1) in -- #97 -//│ let ctx2 = _Context(x$1,x$1,0) in -- #96 -//│ let* (composed) = _c_b_a_ctx_comp$4(a_modcons$7_ctx,ctx2) in -- #95 -//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(6,c_modcons$5_ctx,composed,a_modcons$7_ctx) -- #94 -//│ false => -//│ let scrut = ==(6,tailrecBranch$) in -- #99 -//│ if scrut -- #98 -//│ true => -//│ let* (x$2) = c() in -- #93 -//│ let x$4 = A(x$2,0) in -- #92 -//│ let ctx2 = _Context(x$4,x$4,1) in -- #91 -//│ let* (composed) = _c_b_a_ctx_comp$4(b_modcons$6_ctx,ctx2) in -- #90 -//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(7,c_modcons$5_ctx,b_modcons$6_ctx,composed) -- #89 -//│ false => -//│ let x$6 = A(0,1) in -- #88 -//│ let ctx2 = _Context(x$6,x$6,0) in -- #87 -//│ let* (composed) = _c_b_a_ctx_comp$4(c_modcons$5_ctx,ctx2) in -- #86 -//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(6,c_modcons$5_ctx,composed,a_modcons$7_ctx) -- #85 -//│ ) -//│ }, -//│ let* (x$7) = a() in -- #36 -//│ x$7 -- #35) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { -//│ Def(0, a, [], -//│ 1, -//│ let idCtx = _IdContext() in -- #80 -//│ let* (res) = a_modcons$7(idCtx) in -- #79 -//│ res -- #78 -//│ ) -//│ Def(1, b, [], -//│ 1, -//│ let idCtx = _IdContext() in -- #72 -//│ let* (res) = b_modcons$6(idCtx) in -- #71 -//│ res -- #70 -//│ ) -//│ Def(2, c, [], -//│ 1, -//│ let idCtx = _IdContext() in -- #63 -//│ let* (res) = c_modcons$5(idCtx) in -- #62 -//│ res -- #61 -//│ ) -//│ Def(3, _c_b_a_ctx_app$3, [ctx,x], -//│ 1, -//│ case ctx of -- #49 -//│ _IdContext => -//│ x -- #48 -//│ _Context => -//│ let field = ctx.field in -- #47 -//│ let scrut = ==(1,field) in -- #46 -//│ if scrut -- #45 -//│ true => -//│ let ptr = ctx.ptr in -- #44 -//│ let _ = assign ptr.b := x in -- #43 -//│ let acc = ctx.acc in -- #42 -//│ acc -- #41 -//│ false => -//│ let ptr = ctx.ptr in -- #40 -//│ let _ = assign ptr.a := x in -- #39 -//│ let acc = ctx.acc in -- #38 -//│ acc -- #37 -//│ ) -//│ Def(4, _c_b_a_ctx_comp$4, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #55 -//│ let ctx2ptr = ctx2.ptr in -- #54 -//│ let ctx2field = ctx2.field in -- #53 -//│ let* (newAcc) = _c_b_a_ctx_app$3(ctx1,ctx2acc) in -- #52 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #51 -//│ ret -- #50 -//│ ) -//│ Def(5, c_modcons$5, [ctx], -//│ 1, -//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(5,ctx,undefined,undefined) in -- #104 -//│ r0 -- #103 -//│ ) -//│ Def(6, b_modcons$6, [ctx], -//│ 1, -//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(6,undefined,ctx,undefined) in -- #106 -//│ r0 -- #105 -//│ ) -//│ Def(7, a_modcons$7, [ctx], -//│ 1, -//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(7,undefined,undefined,ctx) in -- #108 -//│ r0 -- #107 -//│ ) -//│ Def(8, _c_modcons$5_b_modcons$6_a_modcons$7_opt$8, [tailrecBranch$,c_modcons$5_ctx,b_modcons$6_ctx,a_modcons$7_ctx], -//│ 1, -//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(tailrecBranch$,c_modcons$5_ctx,b_modcons$6_ctx,a_modcons$7_ctx) -- #102 -//│ ) -//│ Def(9, _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9, [tailrecBranch$,c_modcons$5_ctx,b_modcons$6_ctx,a_modcons$7_ctx], -//│ 1, -//│ let scrut = ==(7,tailrecBranch$) in -- #101 -//│ if scrut -- #100 -//│ true => -//│ let x$1 = A(0,1) in -- #97 -//│ let ctx2 = _Context(x$1,x$1,0) in -- #96 -//│ let* (composed) = _c_b_a_ctx_comp$4(a_modcons$7_ctx,ctx2) in -- #95 -//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(6,c_modcons$5_ctx,composed,a_modcons$7_ctx) -- #94 -//│ false => -//│ let scrut = ==(6,tailrecBranch$) in -- #99 -//│ if scrut -- #98 +//│ Program: +//│ class A(a,b) +//│ class _IdContext() +//│ class _Context(acc,ptr,field) +//│ def a() = +//│ let idCtx = _IdContext() in -- #81 +//│ let* (res) = a_modcons$13(idCtx) in -- #80 +//│ res -- #79 +//│ def b() = +//│ let idCtx = _IdContext() in -- #73 +//│ let* (res) = b_modcons$12(idCtx) in -- #72 +//│ res -- #71 +//│ def c() = +//│ let idCtx = _IdContext() in -- #64 +//│ let* (res) = c_modcons$11(idCtx) in -- #63 +//│ res -- #62 +//│ def _c_b_a_ctx_app$9(ctx,x) = +//│ case ctx of -- #50 +//│ _IdContext => +//│ x -- #49 +//│ _Context => +//│ let field = _Context.field(ctx) in -- #48 +//│ let scrut = ==(1,field) in -- #47 +//│ if scrut -- #46 +//│ true => +//│ let ptr = _Context.ptr(ctx) in -- #45 +//│ let _ = assign ptr.b := x in -- #44 +//│ let acc = _Context.acc(ctx) in -- #43 +//│ acc -- #42 +//│ false => +//│ let ptr = _Context.ptr(ctx) in -- #41 +//│ let _ = assign ptr.a := x in -- #40 +//│ let acc = _Context.acc(ctx) in -- #39 +//│ acc -- #38 +//│ def _c_b_a_ctx_comp$10(ctx1,ctx2) = +//│ let ctx2acc = _Context.acc(ctx2) in -- #56 +//│ let ctx2ptr = _Context.ptr(ctx2) in -- #55 +//│ let ctx2field = _Context.field(ctx2) in -- #54 +//│ let* (newAcc) = _c_b_a_ctx_app$9(ctx1,ctx2acc) in -- #53 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #52 +//│ ret -- #51 +//│ def c_modcons$11(ctx) = +//│ let* (r0) = _c_modcons$11_b_modcons$12_a_modcons$13_opt$14(11,ctx,true,true) in -- #105 +//│ r0 -- #104 +//│ def b_modcons$12(ctx) = +//│ let* (r0) = _c_modcons$11_b_modcons$12_a_modcons$13_opt$14(12,true,ctx,true) in -- #107 +//│ r0 -- #106 +//│ def a_modcons$13(ctx) = +//│ let* (r0) = _c_modcons$11_b_modcons$12_a_modcons$13_opt$14(13,true,true,ctx) in -- #109 +//│ r0 -- #108 +//│ def _c_modcons$11_b_modcons$12_a_modcons$13_opt$14(tailrecBranch$,c_modcons$11_ctx,b_modcons$12_ctx,a_modcons$13_ctx) = +//│ jump _c_modcons$11_b_modcons$12_a_modcons$13_opt_jp$15(tailrecBranch$,c_modcons$11_ctx,b_modcons$12_ctx,a_modcons$13_ctx) -- #103 +//│ def _c_modcons$11_b_modcons$12_a_modcons$13_opt_jp$15(tailrecBranch$,c_modcons$11_ctx,b_modcons$12_ctx,a_modcons$13_ctx) = +//│ let scrut = ==(13,tailrecBranch$) in -- #102 +//│ if scrut -- #101 //│ true => -//│ let* (x$2) = c() in -- #93 -//│ let x$4 = A(x$2,0) in -- #92 -//│ let ctx2 = _Context(x$4,x$4,1) in -- #91 -//│ let* (composed) = _c_b_a_ctx_comp$4(b_modcons$6_ctx,ctx2) in -- #90 -//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(7,c_modcons$5_ctx,b_modcons$6_ctx,composed) -- #89 +//│ let x$2 = A(0,1) in -- #98 +//│ let ctx2 = _Context(x$2,x$2,0) in -- #97 +//│ let* (composed) = _c_b_a_ctx_comp$10(a_modcons$13_ctx,ctx2) in -- #96 +//│ jump _c_modcons$11_b_modcons$12_a_modcons$13_opt_jp$15(12,c_modcons$11_ctx,composed,a_modcons$13_ctx) -- #95 //│ false => -//│ let x$6 = A(0,1) in -- #88 -//│ let ctx2 = _Context(x$6,x$6,0) in -- #87 -//│ let* (composed) = _c_b_a_ctx_comp$4(c_modcons$5_ctx,ctx2) in -- #86 -//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(6,c_modcons$5_ctx,composed,a_modcons$7_ctx) -- #85 -//│ ) -//│ }, -//│ let* (x$7) = a() in -- #36 -//│ x$7 -- #35) +//│ let scrut = ==(12,tailrecBranch$) in -- #100 +//│ if scrut -- #99 +//│ true => +//│ let* (x$3) = c() in -- #94 +//│ let x$5 = A(x$3,0) in -- #93 +//│ let ctx2 = _Context(x$5,x$5,1) in -- #92 +//│ let* (composed) = _c_b_a_ctx_comp$10(b_modcons$12_ctx,ctx2) in -- #91 +//│ jump _c_modcons$11_b_modcons$12_a_modcons$13_opt_jp$15(13,c_modcons$11_ctx,b_modcons$12_ctx,composed) -- #90 +//│ false => +//│ let x$7 = A(0,1) in -- #89 +//│ let ctx2 = _Context(x$7,x$7,0) in -- #88 +//│ let* (composed) = _c_b_a_ctx_comp$10(c_modcons$11_ctx,ctx2) in -- #87 +//│ jump _c_modcons$11_b_modcons$12_a_modcons$13_opt_jp$15(12,c_modcons$11_ctx,composed,a_modcons$13_ctx) -- #86 +//│ let* (x$0) = a() in -- #2 +//│ x$0 -- #1 // TODO: Purity check class A(a, b) @@ -2327,241 +1394,141 @@ a() //│ |#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|@|tailcall| |a|(||)|,| |c|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|0|,| |1|)|↵|a|(||)| //│ Parsed: {class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(@tailcall a(), c(),); fun c = () => A(0, 1,); a()} //│ +//│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b])}, { -//│ Def(0, a, [], -//│ 1, -//│ let* (x$0) = b() in -- #9 -//│ let x$1 = A(x$0,1) in -- #8 -//│ x$1 -- #7 -//│ ) -//│ Def(1, b, [], -//│ 1, -//│ let* (x$2) = @tailcall a() in -- #22 -//│ let* (x$3) = c() in -- #21 -//│ let x$4 = A(x$2,x$3) in -- #20 -//│ x$4 -- #19 -//│ ) -//│ Def(2, c, [], -//│ 1, -//│ let x$5 = A(0,1) in -- #29 -//│ x$5 -- #28 -//│ ) -//│ }, -//│ let* (x$6) = a() in -- #33 -//│ x$6 -- #32) +//│ Program: +//│ class A(a,b) +//│ def a() = +//│ let* (x$1) = b() in -- #11 +//│ let x$2 = A(x$1,1) in -- #10 +//│ x$2 -- #9 +//│ def b() = +//│ let* (x$3) = @tailcall a() in -- #22 +//│ let* (x$4) = c() in -- #21 +//│ let x$5 = A(x$3,x$4) in -- #20 +//│ x$5 -- #19 +//│ def c() = +//│ let x$6 = A(0,1) in -- #29 +//│ x$6 -- #28 +//│ let* (x$0) = a() in -- #2 +//│ x$0 -- #1 //│ ╔══[COMPILATION ERROR] not a tail call, as the remaining functions may be impure -//│ ║ l.2324: fun b() = A(@tailcall a(), c()) +//│ ║ l.1391: fun b() = A(@tailcall a(), c()) //│ ╙── ^ //│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec -//│ ║ l.2322: @tailrec +//│ ║ l.1389: @tailrec //│ ║ ^^^^^^^ //│ ╟── it could self-recurse through this call, which may not be a tail-call -//│ ║ l.2324: fun b() = A(@tailcall a(), c()) +//│ ║ l.1391: fun b() = A(@tailcall a(), c()) //│ ╙── ^ //│ +//│ //│ Strongly Connected Tail Calls: //│ List(Set(b, a), Set(c)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { -//│ Def(0, a, [], -//│ 1, -//│ let idCtx = _IdContext() in -- #62 -//│ let* (res) = a_modcons$6(idCtx) in -- #61 -//│ res -- #60 -//│ ) -//│ Def(1, b, [], -//│ 1, -//│ let idCtx = _IdContext() in -- #54 -//│ let* (res) = b_modcons$5(idCtx) in -- #53 -//│ res -- #52 -//│ ) -//│ Def(2, c, [], -//│ 1, -//│ let x$5 = A(0,1) in -- #29 -//│ x$5 -- #28 -//│ ) -//│ Def(3, _b_a_ctx_app$3, [ctx,x], -//│ 1, -//│ case ctx of -- #40 -//│ _IdContext => -//│ x -- #39 -//│ _Context => -//│ let field = ctx.field in -- #38 -//│ let ptr = ctx.ptr in -- #37 -//│ let _ = assign ptr.a := x in -- #36 -//│ let acc = ctx.acc in -- #35 -//│ acc -- #34 -//│ ) -//│ Def(4, _b_a_ctx_comp$4, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #46 -//│ let ctx2ptr = ctx2.ptr in -- #45 -//│ let ctx2field = ctx2.field in -- #44 -//│ let* (newAcc) = _b_a_ctx_app$3(ctx1,ctx2acc) in -- #43 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #42 -//│ ret -- #41 -//│ ) -//│ Def(5, b_modcons$5, [ctx], -//│ 1, -//│ let* (r0) = _b_modcons$5_a_modcons$6_opt$7(5,ctx,undefined) in -- #81 -//│ r0 -- #80 -//│ ) -//│ Def(6, a_modcons$6, [ctx], -//│ 1, -//│ let* (r0) = _b_modcons$5_a_modcons$6_opt$7(6,undefined,ctx) in -- #83 -//│ r0 -- #82 -//│ ) -//│ Def(7, _b_modcons$5_a_modcons$6_opt$7, [tailrecBranch$,b_modcons$5_ctx,a_modcons$6_ctx], -//│ 1, -//│ jump _b_modcons$5_a_modcons$6_opt_jp$8(tailrecBranch$,b_modcons$5_ctx,a_modcons$6_ctx) -- #79 -//│ ) -//│ Def(8, _b_modcons$5_a_modcons$6_opt_jp$8, [tailrecBranch$,b_modcons$5_ctx,a_modcons$6_ctx], -//│ 1, -//│ let scrut = ==(6,tailrecBranch$) in -- #78 -//│ if scrut -- #77 -//│ true => -//│ let x$1 = A(0,1) in -- #76 -//│ let ctx2 = _Context(x$1,x$1,0) in -- #75 -//│ let* (composed) = _b_a_ctx_comp$4(a_modcons$6_ctx,ctx2) in -- #74 -//│ jump _b_modcons$5_a_modcons$6_opt_jp$8(5,composed,a_modcons$6_ctx) -- #73 -//│ false => -//│ let* (x$2) = @tailcall a() in -- #72 -//│ let* (x$3) = c() in -- #71 -//│ let x$4 = A(x$2,x$3) in -- #70 -//│ let* (res) = _b_a_ctx_app$3(b_modcons$5_ctx,x$4) in -- #69 -//│ res -- #68 -//│ ) -//│ }, -//│ let* (x$6) = a() in -- #33 -//│ x$6 -- #32) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { -//│ Def(0, a, [], -//│ 1, -//│ let idCtx = _IdContext() in -- #62 -//│ let* (res) = a_modcons$6(idCtx) in -- #61 -//│ res -- #60 -//│ ) -//│ Def(1, b, [], -//│ 1, -//│ let idCtx = _IdContext() in -- #54 -//│ let* (res) = b_modcons$5(idCtx) in -- #53 -//│ res -- #52 -//│ ) -//│ Def(2, c, [], -//│ 1, -//│ let x$5 = A(0,1) in -- #29 -//│ x$5 -- #28 -//│ ) -//│ Def(3, _b_a_ctx_app$3, [ctx,x], -//│ 1, -//│ case ctx of -- #40 -//│ _IdContext => -//│ x -- #39 -//│ _Context => -//│ let field = ctx.field in -- #38 -//│ let ptr = ctx.ptr in -- #37 -//│ let _ = assign ptr.a := x in -- #36 -//│ let acc = ctx.acc in -- #35 -//│ acc -- #34 -//│ ) -//│ Def(4, _b_a_ctx_comp$4, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #46 -//│ let ctx2ptr = ctx2.ptr in -- #45 -//│ let ctx2field = ctx2.field in -- #44 -//│ let* (newAcc) = _b_a_ctx_app$3(ctx1,ctx2acc) in -- #43 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #42 -//│ ret -- #41 -//│ ) -//│ Def(5, b_modcons$5, [ctx], -//│ 1, -//│ let* (r0) = _b_modcons$5_a_modcons$6_opt$7(5,ctx,undefined) in -- #81 -//│ r0 -- #80 -//│ ) -//│ Def(6, a_modcons$6, [ctx], -//│ 1, -//│ let* (r0) = _b_modcons$5_a_modcons$6_opt$7(6,undefined,ctx) in -- #83 -//│ r0 -- #82 -//│ ) -//│ Def(7, _b_modcons$5_a_modcons$6_opt$7, [tailrecBranch$,b_modcons$5_ctx,a_modcons$6_ctx], -//│ 1, -//│ jump _b_modcons$5_a_modcons$6_opt_jp$8(tailrecBranch$,b_modcons$5_ctx,a_modcons$6_ctx) -- #79 -//│ ) -//│ Def(8, _b_modcons$5_a_modcons$6_opt_jp$8, [tailrecBranch$,b_modcons$5_ctx,a_modcons$6_ctx], -//│ 1, -//│ let scrut = ==(6,tailrecBranch$) in -- #78 -//│ if scrut -- #77 -//│ true => -//│ let x$1 = A(0,1) in -- #76 -//│ let ctx2 = _Context(x$1,x$1,0) in -- #75 -//│ let* (composed) = _b_a_ctx_comp$4(a_modcons$6_ctx,ctx2) in -- #74 -//│ jump _b_modcons$5_a_modcons$6_opt_jp$8(5,composed,a_modcons$6_ctx) -- #73 -//│ false => -//│ let* (x$2) = @tailcall a() in -- #72 -//│ let* (x$3) = c() in -- #71 -//│ let x$4 = A(x$2,x$3) in -- #70 -//│ let* (res) = _b_a_ctx_app$3(b_modcons$5_ctx,x$4) in -- #69 -//│ res -- #68 -//│ ) -//│ }, -//│ let* (x$6) = a() in -- #33 -//│ x$6 -- #32) +//│ Program: +//│ class A(a,b) +//│ class _IdContext() +//│ class _Context(acc,ptr,field) +//│ def a() = +//│ let idCtx = _IdContext() in -- #64 +//│ let* (res) = a_modcons$12(idCtx) in -- #63 +//│ res -- #62 +//│ def b() = +//│ let idCtx = _IdContext() in -- #56 +//│ let* (res) = b_modcons$11(idCtx) in -- #55 +//│ res -- #54 +//│ def c() = +//│ let x$6 = A(0,1) in -- #29 +//│ x$6 -- #28 +//│ def _b_a_ctx_app$9(ctx,x) = +//│ case ctx of -- #42 +//│ _IdContext => +//│ x -- #41 +//│ _Context => +//│ let field = _Context.field(ctx) in -- #40 +//│ let ptr = _Context.ptr(ctx) in -- #39 +//│ let _ = assign ptr.a := x in -- #38 +//│ let acc = _Context.acc(ctx) in -- #37 +//│ acc -- #36 +//│ def _b_a_ctx_comp$10(ctx1,ctx2) = +//│ let ctx2acc = _Context.acc(ctx2) in -- #48 +//│ let ctx2ptr = _Context.ptr(ctx2) in -- #47 +//│ let ctx2field = _Context.field(ctx2) in -- #46 +//│ let* (newAcc) = _b_a_ctx_app$9(ctx1,ctx2acc) in -- #45 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #44 +//│ ret -- #43 +//│ def b_modcons$11(ctx) = +//│ let* (r0) = _b_modcons$11_a_modcons$12_opt$13(11,ctx,true) in -- #83 +//│ r0 -- #82 +//│ def a_modcons$12(ctx) = +//│ let* (r0) = _b_modcons$11_a_modcons$12_opt$13(12,true,ctx) in -- #85 +//│ r0 -- #84 +//│ def _b_modcons$11_a_modcons$12_opt$13(tailrecBranch$,b_modcons$11_ctx,a_modcons$12_ctx) = +//│ jump _b_modcons$11_a_modcons$12_opt_jp$14(tailrecBranch$,b_modcons$11_ctx,a_modcons$12_ctx) -- #81 +//│ def _b_modcons$11_a_modcons$12_opt_jp$14(tailrecBranch$,b_modcons$11_ctx,a_modcons$12_ctx) = +//│ let scrut = ==(12,tailrecBranch$) in -- #80 +//│ if scrut -- #79 +//│ true => +//│ let x$2 = A(0,1) in -- #78 +//│ let ctx2 = _Context(x$2,x$2,0) in -- #77 +//│ let* (composed) = _b_a_ctx_comp$10(a_modcons$12_ctx,ctx2) in -- #76 +//│ jump _b_modcons$11_a_modcons$12_opt_jp$14(11,composed,a_modcons$12_ctx) -- #75 +//│ false => +//│ let* (x$3) = @tailcall a() in -- #74 +//│ let* (x$4) = c() in -- #73 +//│ let x$5 = A(x$3,x$4) in -- #72 +//│ let* (res) = _b_a_ctx_app$9(b_modcons$11_ctx,x$5) in -- #71 +//│ res -- #70 +//│ let* (x$0) = a() in -- #2 +//│ x$0 -- #1 :ce @tailcall 1 //│ |@|tailcall| |1| //│ Parsed: {@tailcall 1} //│ ╔══[COMPILATION ERROR] @tailcall may only be used to annotate function calls -//│ ║ l.2513: @tailcall 1 +//│ ║ l.1486: @tailcall 1 //│ ╙── ^^^^^^^^ //│ +//│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Program: //│ -//│ }, -//│ 1 -- #0) //│ -//│ Strongly Connected Tail Calls: -//│ List() -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ 1 -- #0 //│ -//│ }, -//│ 1 -- #0) //│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Strongly Connected Tail Calls: +//│ List() +//│ Program: +//│ class _IdContext() +//│ class _Context(acc,ptr,field) //│ -//│ }, -//│ 1 -- #0) +//│ 1 -- #0 :ce @tailrec 1 //│ |@|tailrec| |1| //│ Parsed: {@tailrec 1} //│ ╔══[COMPILATION ERROR] @tailrec may only be used to annotate functions -//│ ║ l.2540: @tailrec 1 +//│ ║ l.1510: @tailrec 1 //│ ╙── ^^^^^^^ //│ +//│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Program: //│ -//│ }, -//│ 1 -- #0) //│ -//│ Strongly Connected Tail Calls: -//│ List() -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ 1 -- #0 //│ -//│ }, -//│ 1 -- #0) //│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Strongly Connected Tail Calls: +//│ List() +//│ Program: +//│ class _IdContext() +//│ class _Context(acc,ptr,field) //│ -//│ }, -//│ 1 -- #0) +//│ 1 -- #0 :ce fun foo() = @@ -2570,50 +1537,32 @@ foo() //│ |#fun| |foo|(||)| |#=|→|@|tailrec| |foo|(||)|←|↵|foo|(||)| //│ Parsed: {fun foo = () => {@tailrec foo()}; foo()} //│ ╔══[COMPILATION ERROR] @tailrec is for annotating functions; try @tailcall instead -//│ ║ l.2568: @tailrec foo() +//│ ║ l.1535: @tailrec foo() //│ ╙── ^^^^^^^ //│ +//│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, foo, [], -//│ 1, -//│ let* (x$0) = foo() in -- #3 -//│ x$0 -- #2 -//│ ) -//│ }, -//│ let* (x$1) = foo() in -- #7 -//│ x$1 -- #6) +//│ Program: +//│ +//│ def foo() = +//│ let* (x$1) = foo() in -- #5 +//│ x$1 -- #4 +//│ let* (x$0) = foo() in -- #2 +//│ x$0 -- #1 +//│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(foo)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(1, foo_jp, [], -//│ 1, -//│ jump foo_jp() -- #8 -//│ ) -//│ Def(2, foo, [], -//│ 1, -//│ let* (r0) = foo_jp() in -- #10 -//│ r0 -- #9 -//│ ) -//│ }, -//│ let* (x$1) = foo() in -- #7 -//│ x$1 -- #6) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(1, foo_jp, [], -//│ 1, -//│ jump foo_jp() -- #8 -//│ ) -//│ Def(2, foo, [], -//│ 1, -//│ let* (r0) = foo_jp() in -- #10 -//│ r0 -- #9 -//│ ) -//│ }, -//│ let* (x$1) = foo() in -- #7 -//│ x$1 -- #6) +//│ Program: +//│ class _IdContext() +//│ class _Context(acc,ptr,field) +//│ def foo_jp() = +//│ jump foo_jp() -- #12 +//│ def foo() = +//│ let* (r0) = foo_jp() in -- #14 +//│ r0 -- #13 +//│ let* (x$0) = foo() in -- #2 +//│ x$0 -- #1 :ce @tailcall @@ -2623,47 +1572,29 @@ foo() //│ |@|tailcall|↵|#fun| |foo|(||)| |#=|→|foo|(||)|←|↵|foo|(||)| //│ Parsed: {fun foo = () => {foo()}; foo()} //│ ╔══[COMPILATION ERROR] @tailcall is for annotating function calls; try @tailrec instead -//│ ║ l.2619: @tailcall +//│ ║ l.1568: @tailcall //│ ╙── ^^^^^^^^ //│ +//│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, foo, [], -//│ 1, -//│ let* (x$0) = foo() in -- #3 -//│ x$0 -- #2 -//│ ) -//│ }, -//│ let* (x$1) = foo() in -- #7 -//│ x$1 -- #6) +//│ Program: +//│ +//│ def foo() = +//│ let* (x$1) = foo() in -- #5 +//│ x$1 -- #4 +//│ let* (x$0) = foo() in -- #2 +//│ x$0 -- #1 +//│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(foo)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(1, foo_jp, [], -//│ 1, -//│ jump foo_jp() -- #8 -//│ ) -//│ Def(2, foo, [], -//│ 1, -//│ let* (r0) = foo_jp() in -- #10 -//│ r0 -- #9 -//│ ) -//│ }, -//│ let* (x$1) = foo() in -- #7 -//│ x$1 -- #6) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(1, foo_jp, [], -//│ 1, -//│ jump foo_jp() -- #8 -//│ ) -//│ Def(2, foo, [], -//│ 1, -//│ let* (r0) = foo_jp() in -- #10 -//│ r0 -- #9 -//│ ) -//│ }, -//│ let* (x$1) = foo() in -- #7 -//│ x$1 -- #6) +//│ Program: +//│ class _IdContext() +//│ class _Context(acc,ptr,field) +//│ def foo_jp() = +//│ jump foo_jp() -- #12 +//│ def foo() = +//│ let* (r0) = foo_jp() in -- #14 +//│ r0 -- #13 +//│ let* (x$0) = foo() in -- #2 +//│ x$0 -- #1 diff --git a/compiler/shared/test/diff-ir/LiftClass.mls b/compiler/shared/test/diff-ir/LiftClass.mls new file mode 100644 index 00000000..05b2267a --- /dev/null +++ b/compiler/shared/test/diff-ir/LiftClass.mls @@ -0,0 +1,158 @@ +:NewDefs +:ParseOnly +:UseIR +:NoTailRec + +:prelude +module True +module False +module Callable { + fun apply0() = 0 + fun apply1(x0) = 0 + fun apply2(x0,x1) = 0 + fun apply3(x0,x1,x2) = 0 + fun apply4(x0,x1,x2,x3) = 0 + fun apply5(x0,x1,x2,x3,x4) = 0 +} +module List[A, B] +class Cons[A, B](h: A, t: Cons[A, B]) extends List[A, B] +module Nil[A, B] extends List[A, B] +module Option[A] +class Some[A](x: A) extends Option[A] +module None[A] extends Option[A] +class Pair[A, B](x: A, y: B) +class Tuple2[A, B](x: A, y: B) +class Tuple3[A, B, C](x: A, y: B, z: C) +module Nat +class S(s: Nat) extends Nat +module O extends Nat +class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O) +//│ |#module| |True|↵|#module| |False|↵|#module| |Callable| |{|→|#fun| |apply0|(||)| |#=| |0|↵|#fun| |apply1|(|x0|)| |#=| |0|↵|#fun| |apply2|(|x0|,|x1|)| |#=| |0|↵|#fun| |apply3|(|x0|,|x1|,|x2|)| |#=| |0|↵|#fun| |apply4|(|x0|,|x1|,|x2|,|x3|)| |#=| |0|↵|#fun| |apply5|(|x0|,|x1|,|x2|,|x3|,|x4|)| |#=| |0|←|↵|}|↵|#module| |List|[|A|,| |B|]|↵|#class| |Cons|[|A|,| |B|]|(|h|#:| |A|,| |t|#:| |Cons|[|A|,| |B|]|)| |#extends| |List|[|A|,| |B|]|↵|#module| |Nil|[|A|,| |B|]| |#extends| |List|[|A|,| |B|]|↵|#module| |Option|[|A|]|↵|#class| |Some|[|A|]|(|x|#:| |A|)| |#extends| |Option|[|A|]|↵|#module| |None|[|A|]| |#extends| |Option|[|A|]|↵|#class| |Pair|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple2|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple3|[|A|,| |B|,| |C|]|(|x|#:| |A|,| |y|#:| |B|,| |z|#:| |C|)|↵|#module| |Nat|↵|#class| |S|(|s|#:| |Nat|)| |#extends| |Nat|↵|#module| |O| |#extends| |Nat|↵|#class| |HiddenTheseEntities|(|_0|#:| |HiddenTheseEntities|,| |_1|#:| |True|,| |_2|#:| |False|,| |_3|#:| |Callable|,| |_4|#:| |List|,| |_5|#:| |Cons|,| |_6|#:| |Nil|,| |_7|#:| |Option|,| |_8|#:| |Some|,| |_9|#:| |None|,| |_10|#:| |Pair|,| |_11|#:| |Tuple2|,| |_12|#:| |Tuple3|,| |_13|#:| |Nat|,| |_14|#:| |S|,| |_15|#:| |O|)| +//│ Parsed: {module True {}; module False {}; module Callable {fun apply0 = () => 0; fun apply1 = (x0,) => 0; fun apply2 = (x0, x1,) => 0; fun apply3 = (x0, x1, x2,) => 0; fun apply4 = (x0, x1, x2, x3,) => 0; fun apply5 = (x0, x1, x2, x3, x4,) => 0}; module List‹A, B› {}; class Cons‹A, B›(h: A, t: Cons‹A, B›,): List‹A, B› {}; module Nil‹A, B›: List‹A, B› {}; module Option‹A› {}; class Some‹A›(x: A,): Option‹A› {}; module None‹A›: Option‹A› {}; class Pair‹A, B›(x: A, y: B,) {}; class Tuple2‹A, B›(x: A, y: B,) {}; class Tuple3‹A, B, C›(x: A, y: B, z: C,) {}; module Nat {}; class S(s: Nat,): Nat {}; module O: Nat {}; class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O,) {}} +//│ +//│ Preluded. +//│ + +:genCpp +:runCpp +:interpIR +fun main(x) = + class InnerClass(y) extends Callable { + fun apply1(z) = x + y + z + } + let ic = InnerClass(1) + ic(2) + ic(3) +main(4) +//│ |#fun| |main|(|x|)| |#=|→|#class| |InnerClass|(|y|)| |#extends| |Callable| |{|→|#fun| |apply1|(|z|)| |#=| |x| |+| |y| |+| |z|←|↵|}|↵|#let| |ic| |#=| |InnerClass|(|1|)|↵|ic|(|2|)| |+| |ic|(|3|)|←|↵|main|(|4|)| +//│ Parsed: {fun main = (x,) => {class InnerClass(y,): Callable {fun apply1 = (z,) => +(+(x, y,), z,)}; let ic = InnerClass(1,); +(ic(2,), ic(3,),)}; main(4,)} +//│ +//│ +//│ IR: +//│ Program: +//│ class InnerClass(y,x) extends Callable { +//│ def apply1(z$0) = +//│ let x$6 = +(x,y) in -- #45 +//│ let x$7 = +(x$6,z$0) in -- #44 +//│ x$7 -- #43 +//│ } +//│ def main(x$1) = +//│ let x$2 = InnerClass(1,x$1) in -- #26 +//│ let x$3 = Callable.apply1(x$2,2) in -- #25 +//│ let x$4 = Callable.apply1(x$2,3) in -- #24 +//│ let x$5 = +(x$3,x$4) in -- #23 +//│ x$5 -- #22 +//│ let* (x$0) = main(4) in -- #4 +//│ x$0 -- #3 +//│ +//│ Interpreted: +//│ 15 +//│ +//│ +//│ Execution succeeded: +//│ 15 +//│ + +:genCpp +:runCpp +:interpIR +fun main(x) = + class InnerClass(y) extends Callable { + fun apply1(z) = + module InnerClass2 extends Callable { + fun apply1(w) = w + z + } + InnerClass2 + } + let ic = InnerClass(1) + ic(2)(2) + ic(3)(1) +main(4) +//│ |#fun| |main|(|x|)| |#=|→|#class| |InnerClass|(|y|)| |#extends| |Callable| |{|→|#fun| |apply1|(|z|)| |#=|→|#module| |InnerClass2| |#extends| |Callable| |{|→|#fun| |apply1|(|w|)| |#=| |w| |+| |z|←|↵|}|↵|InnerClass2|←|←|↵|}|↵|#let| |ic| |#=| |InnerClass|(|1|)|↵|ic|(|2|)|(|2|)| |+| |ic|(|3|)|(|1|)|←|↵|main|(|4|)| +//│ Parsed: {fun main = (x,) => {class InnerClass(y,): Callable {fun apply1 = (z,) => {module InnerClass2: Callable {fun apply1 = (w,) => +(w, z,)}; InnerClass2}}; let ic = InnerClass(1,); +(ic(2,)(2,), ic(3,)(1,),)}; main(4,)} +//│ +//│ +//│ IR: +//│ Program: +//│ class InnerClass(y) extends Callable { +//│ def apply1(z$0) = +//│ let x$8 = InnerClass2(z$0) in -- #44 +//│ x$8 -- #43 +//│ } +//│ class InnerClass2(z) extends Callable { +//│ def apply1(w$0) = +//│ let x$9 = +(w$0,z) in -- #51 +//│ x$9 -- #50 +//│ } +//│ def main(x$1) = +//│ let x$2 = InnerClass(1) in -- #36 +//│ let x$3 = Callable.apply1(x$2,2) in -- #35 +//│ let x$4 = Callable.apply1(x$3,2) in -- #34 +//│ let x$5 = Callable.apply1(x$2,3) in -- #33 +//│ let x$6 = Callable.apply1(x$5,1) in -- #32 +//│ let x$7 = +(x$4,x$6) in -- #31 +//│ x$7 -- #30 +//│ let* (x$0) = main(4) in -- #4 +//│ x$0 -- #3 +//│ +//│ Interpreted: +//│ 8 +//│ +//│ +//│ Execution succeeded: +//│ 8 +//│ + +:genCpp +:runCpp +:interpIR +fun main(x) = + class InnerClass(y) extends Callable { + fun f(x) = y + } + let ic = InnerClass(1) + InnerClass.f(ic, Nil) +main(2) +//│ |#fun| |main|(|x|)| |#=|→|#class| |InnerClass|(|y|)| |#extends| |Callable| |{|→|#fun| |f|(|x|)| |#=| |y|←|↵|}|↵|#let| |ic| |#=| |InnerClass|(|1|)|↵|InnerClass|.f|(|ic|,| |Nil|)|←|↵|main|(|2|)| +//│ Parsed: {fun main = (x,) => {class InnerClass(y,): Callable {fun f = (x,) => y}; let ic = InnerClass(1,); (InnerClass).f(ic, Nil,)}; main(2,)} +//│ +//│ +//│ IR: +//│ Program: +//│ class InnerClass(y) extends Callable { +//│ def f(x$5) = +//│ y -- #24 +//│ } +//│ def main(x$1) = +//│ let x$2 = InnerClass(1) in -- #17 +//│ let x$3 = Nil() in -- #16 +//│ let x$4 = InnerClass.f(x$2,x$3) in -- #15 +//│ x$4 -- #14 +//│ let* (x$0) = main(2) in -- #4 +//│ x$0 -- #3 +//│ +//│ Interpreted: +//│ 1 +//│ +//│ +//│ Execution succeeded: +//│ 1 +//│ diff --git a/compiler/shared/test/diff-ir/LiftFun.mls b/compiler/shared/test/diff-ir/LiftFun.mls new file mode 100644 index 00000000..1d148b45 --- /dev/null +++ b/compiler/shared/test/diff-ir/LiftFun.mls @@ -0,0 +1,186 @@ +:NewDefs +:ParseOnly +:UseIR +:NoTailRec + +:prelude +module True +module False +module Callable { + fun apply0() = 0 + fun apply1(x0) = 0 + fun apply2(x0,x1) = 0 + fun apply3(x0,x1,x2) = 0 + fun apply4(x0,x1,x2,x3) = 0 + fun apply5(x0,x1,x2,x3,x4) = 0 +} +module List[A, B] +class Cons[A, B](h: A, t: Cons[A, B]) extends List[A, B] +module Nil[A, B] extends List[A, B] +module Option[A] +class Some[A](x: A) extends Option[A] +module None[A] extends Option[A] +class Pair[A, B](x: A, y: B) +class Tuple2[A, B](x: A, y: B) +class Tuple3[A, B, C](x: A, y: B, z: C) +module Nat +class S(s: Nat) extends Nat +module O extends Nat +class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O) +//│ |#module| |True|↵|#module| |False|↵|#module| |Callable| |{|→|#fun| |apply0|(||)| |#=| |0|↵|#fun| |apply1|(|x0|)| |#=| |0|↵|#fun| |apply2|(|x0|,|x1|)| |#=| |0|↵|#fun| |apply3|(|x0|,|x1|,|x2|)| |#=| |0|↵|#fun| |apply4|(|x0|,|x1|,|x2|,|x3|)| |#=| |0|↵|#fun| |apply5|(|x0|,|x1|,|x2|,|x3|,|x4|)| |#=| |0|←|↵|}|↵|#module| |List|[|A|,| |B|]|↵|#class| |Cons|[|A|,| |B|]|(|h|#:| |A|,| |t|#:| |Cons|[|A|,| |B|]|)| |#extends| |List|[|A|,| |B|]|↵|#module| |Nil|[|A|,| |B|]| |#extends| |List|[|A|,| |B|]|↵|#module| |Option|[|A|]|↵|#class| |Some|[|A|]|(|x|#:| |A|)| |#extends| |Option|[|A|]|↵|#module| |None|[|A|]| |#extends| |Option|[|A|]|↵|#class| |Pair|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple2|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple3|[|A|,| |B|,| |C|]|(|x|#:| |A|,| |y|#:| |B|,| |z|#:| |C|)|↵|#module| |Nat|↵|#class| |S|(|s|#:| |Nat|)| |#extends| |Nat|↵|#module| |O| |#extends| |Nat|↵|#class| |HiddenTheseEntities|(|_0|#:| |HiddenTheseEntities|,| |_1|#:| |True|,| |_2|#:| |False|,| |_3|#:| |Callable|,| |_4|#:| |List|,| |_5|#:| |Cons|,| |_6|#:| |Nil|,| |_7|#:| |Option|,| |_8|#:| |Some|,| |_9|#:| |None|,| |_10|#:| |Pair|,| |_11|#:| |Tuple2|,| |_12|#:| |Tuple3|,| |_13|#:| |Nat|,| |_14|#:| |S|,| |_15|#:| |O|)| +//│ Parsed: {module True {}; module False {}; module Callable {fun apply0 = () => 0; fun apply1 = (x0,) => 0; fun apply2 = (x0, x1,) => 0; fun apply3 = (x0, x1, x2,) => 0; fun apply4 = (x0, x1, x2, x3,) => 0; fun apply5 = (x0, x1, x2, x3, x4,) => 0}; module List‹A, B› {}; class Cons‹A, B›(h: A, t: Cons‹A, B›,): List‹A, B› {}; module Nil‹A, B›: List‹A, B› {}; module Option‹A› {}; class Some‹A›(x: A,): Option‹A› {}; module None‹A›: Option‹A› {}; class Pair‹A, B›(x: A, y: B,) {}; class Tuple2‹A, B›(x: A, y: B,) {}; class Tuple3‹A, B, C›(x: A, y: B, z: C,) {}; module Nat {}; class S(s: Nat,): Nat {}; module O: Nat {}; class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O,) {}} +//│ +//│ Preluded. +//│ + +:genCpp +:runCpp +:interpIR +fun main(init, key) = + fun r(x) = if x <= 0 then key else r(x - 1) + r(init) +main(1, 42) +//│ |#fun| |main|(|init|,| |key|)| |#=|→|#fun| |r|(|x|)| |#=| |#if| |x| |<=| |0| |#then| |key| |#else| |r|(|x| |-| |1|)|↵|r|(|init|)|←|↵|main|(|1|,| |42|)| +//│ Parsed: {fun main = (init, key,) => {fun r = (x,) => if (<=(x, 0,)) then key else r(-(x, 1,),); r(init,)}; main(1, 42,)} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def main(init$0,key$0) = +//│ let* (x$1) = r(init$0,key$0) in -- #11 +//│ x$1 -- #10 +//│ def r(x$2,key$1) = +//│ let x$3 = <=(x$2,0) in -- #40 +//│ if x$3 -- #39 +//│ true => +//│ jump j$0(key$1) -- #26 +//│ false => +//│ let x$5 = -(x$2,1) in -- #38 +//│ let* (x$6) = r(x$5,key$1) in -- #37 +//│ jump j$0(x$6) -- #36 +//│ def j$0(x$4) = +//│ x$4 -- #24 +//│ let* (x$0) = main(1,42) in -- #6 +//│ x$0 -- #5 +//│ +//│ Interpreted: +//│ 42 +//│ +//│ +//│ Execution succeeded: +//│ 42 +//│ + +:genCpp +:runCpp +:interpIR +fun main(init, key) = + fun ping(x) = if x <= 0 then key + 1 else pong(x - 1) + fun pong(x) = if x <= 0 then key + 2 else ping(x - 1) + ping(init) +main(1, 42) +//│ |#fun| |main|(|init|,| |key|)| |#=|→|#fun| |ping|(|x|)| |#=| |#if| |x| |<=| |0| |#then| |key| |+| |1| |#else| |pong|(|x| |-| |1|)|↵|#fun| |pong|(|x|)| |#=| |#if| |x| |<=| |0| |#then| |key| |+| |2| |#else| |ping|(|x| |-| |1|)|↵|ping|(|init|)|←|↵|main|(|1|,| |42|)| +//│ Parsed: {fun main = (init, key,) => {fun ping = (x,) => if (<=(x, 0,)) then +(key, 1,) else pong(-(x, 1,),); fun pong = (x,) => if (<=(x, 0,)) then +(key, 2,) else ping(-(x, 1,),); ping(init,)}; main(1, 42,)} +//│ +//│ +//│ IR: +//│ Program: +//│ +//│ def main(init$0,key$0) = +//│ let* (x$1) = ping(init$0,key$0) in -- #11 +//│ x$1 -- #10 +//│ def ping(x$2,key$1) = +//│ let x$3 = <=(x$2,0) in -- #46 +//│ if x$3 -- #45 +//│ true => +//│ let x$5 = +(key$1,1) in -- #32 +//│ jump j$0(x$5) -- #31 +//│ false => +//│ let x$6 = -(x$2,1) in -- #44 +//│ let* (x$7) = pong(x$6,key$1) in -- #43 +//│ jump j$0(x$7) -- #42 +//│ def j$0(x$4) = +//│ x$4 -- #24 +//│ def pong(x$8,key$2) = +//│ let x$9 = <=(x$8,0) in -- #75 +//│ if x$9 -- #74 +//│ true => +//│ let x$11 = +(key$2,2) in -- #61 +//│ jump j$1(x$11) -- #60 +//│ false => +//│ let x$12 = -(x$8,1) in -- #73 +//│ let* (x$13) = ping(x$12,key$2) in -- #72 +//│ jump j$1(x$13) -- #71 +//│ def j$1(x$10) = +//│ x$10 -- #53 +//│ let* (x$0) = main(1,42) in -- #6 +//│ x$0 -- #5 +//│ +//│ Interpreted: +//│ 44 +//│ +//│ +//│ Execution succeeded: +//│ 44 +//│ + +:genCpp +:runCpp +:interpIR +fun main(init, key) = + let ping = + fun ping(x) = if x <= 0 then key + 1 else pong(x - 1) + fun pong(x) = if x <= 0 then key + 2 else ping(x - 1) + ping + ping(init) +main(1, 42) +//│ |#fun| |main|(|init|,| |key|)| |#=|→|#let| |ping| |#=|→|#fun| |ping|(|x|)| |#=| |#if| |x| |<=| |0| |#then| |key| |+| |1| |#else| |pong|(|x| |-| |1|)|↵|#fun| |pong|(|x|)| |#=| |#if| |x| |<=| |0| |#then| |key| |+| |2| |#else| |ping|(|x| |-| |1|)|↵|ping|←|↵|ping|(|init|)|←|↵|main|(|1|,| |42|)| +//│ Parsed: {fun main = (init, key,) => {let ping = {fun ping = (x,) => if (<=(x, 0,)) then +(key, 1,) else pong(-(x, 1,),); fun pong = (x,) => if (<=(x, 0,)) then +(key, 2,) else ping(-(x, 1,),); ping}; ping(init,)}; main(1, 42,)} +//│ +//│ +//│ IR: +//│ Program: +//│ class Lambda$0(key) extends Callable { +//│ def apply1(x$17) = +//│ let* (x$18) = ping(x$17,key) in -- #85 +//│ x$18 -- #84 +//│ } +//│ def main(init$0,key$0) = +//│ let x$3 = Lambda$0(key$0) in -- #14 +//│ let x$4 = Callable.apply1(x$3,init$0) in -- #13 +//│ x$4 -- #12 +//│ def ping(x$5,key$1) = +//│ let x$6 = <=(x$5,0) in -- #49 +//│ if x$6 -- #48 +//│ true => +//│ let x$8 = +(key$1,1) in -- #35 +//│ jump j$0(x$8) -- #34 +//│ false => +//│ let x$9 = -(x$5,1) in -- #47 +//│ let* (x$10) = pong(x$9,key$1) in -- #46 +//│ jump j$0(x$10) -- #45 +//│ def j$0(x$7) = +//│ x$7 -- #27 +//│ def pong(x$11,key$2) = +//│ let x$12 = <=(x$11,0) in -- #78 +//│ if x$12 -- #77 +//│ true => +//│ let x$14 = +(key$2,2) in -- #64 +//│ jump j$1(x$14) -- #63 +//│ false => +//│ let x$15 = -(x$11,1) in -- #76 +//│ let* (x$16) = ping(x$15,key$2) in -- #75 +//│ jump j$1(x$16) -- #74 +//│ def j$1(x$13) = +//│ x$13 -- #56 +//│ let* (x$0) = main(1,42) in -- #6 +//│ x$0 -- #5 +//│ +//│ Interpreted: +//│ 44 +//│ +//│ +//│ Execution succeeded: +//│ 44 +//│ diff --git a/compiler/shared/test/diff-ir/LiftLambda.mls b/compiler/shared/test/diff-ir/LiftLambda.mls new file mode 100644 index 00000000..9f397b31 --- /dev/null +++ b/compiler/shared/test/diff-ir/LiftLambda.mls @@ -0,0 +1,87 @@ +:NewDefs +:ParseOnly +:UseIR +:NoTailRec + +:prelude +module True +module False +module Callable { + fun apply0() = 0 + fun apply1(x0) = 0 + fun apply2(x0,x1) = 0 + fun apply3(x0,x1,x2) = 0 + fun apply4(x0,x1,x2,x3) = 0 + fun apply5(x0,x1,x2,x3,x4) = 0 +} +module List[A, B] +class Cons[A, B](h: A, t: Cons[A, B]) extends List[A, B] +module Nil[A, B] extends List[A, B] +module Option[A] +class Some[A](x: A) extends Option[A] +module None[A] extends Option[A] +class Pair[A, B](x: A, y: B) +class Tuple2[A, B](x: A, y: B) +class Tuple3[A, B, C](x: A, y: B, z: C) +module Nat +class S(s: Nat) extends Nat +module O extends Nat +class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O) +//│ |#module| |True|↵|#module| |False|↵|#module| |Callable| |{|→|#fun| |apply0|(||)| |#=| |0|↵|#fun| |apply1|(|x0|)| |#=| |0|↵|#fun| |apply2|(|x0|,|x1|)| |#=| |0|↵|#fun| |apply3|(|x0|,|x1|,|x2|)| |#=| |0|↵|#fun| |apply4|(|x0|,|x1|,|x2|,|x3|)| |#=| |0|↵|#fun| |apply5|(|x0|,|x1|,|x2|,|x3|,|x4|)| |#=| |0|←|↵|}|↵|#module| |List|[|A|,| |B|]|↵|#class| |Cons|[|A|,| |B|]|(|h|#:| |A|,| |t|#:| |Cons|[|A|,| |B|]|)| |#extends| |List|[|A|,| |B|]|↵|#module| |Nil|[|A|,| |B|]| |#extends| |List|[|A|,| |B|]|↵|#module| |Option|[|A|]|↵|#class| |Some|[|A|]|(|x|#:| |A|)| |#extends| |Option|[|A|]|↵|#module| |None|[|A|]| |#extends| |Option|[|A|]|↵|#class| |Pair|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple2|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple3|[|A|,| |B|,| |C|]|(|x|#:| |A|,| |y|#:| |B|,| |z|#:| |C|)|↵|#module| |Nat|↵|#class| |S|(|s|#:| |Nat|)| |#extends| |Nat|↵|#module| |O| |#extends| |Nat|↵|#class| |HiddenTheseEntities|(|_0|#:| |HiddenTheseEntities|,| |_1|#:| |True|,| |_2|#:| |False|,| |_3|#:| |Callable|,| |_4|#:| |List|,| |_5|#:| |Cons|,| |_6|#:| |Nil|,| |_7|#:| |Option|,| |_8|#:| |Some|,| |_9|#:| |None|,| |_10|#:| |Pair|,| |_11|#:| |Tuple2|,| |_12|#:| |Tuple3|,| |_13|#:| |Nat|,| |_14|#:| |S|,| |_15|#:| |O|)| +//│ Parsed: {module True {}; module False {}; module Callable {fun apply0 = () => 0; fun apply1 = (x0,) => 0; fun apply2 = (x0, x1,) => 0; fun apply3 = (x0, x1, x2,) => 0; fun apply4 = (x0, x1, x2, x3,) => 0; fun apply5 = (x0, x1, x2, x3, x4,) => 0}; module List‹A, B› {}; class Cons‹A, B›(h: A, t: Cons‹A, B›,): List‹A, B› {}; module Nil‹A, B›: List‹A, B› {}; module Option‹A› {}; class Some‹A›(x: A,): Option‹A› {}; module None‹A›: Option‹A› {}; class Pair‹A, B›(x: A, y: B,) {}; class Tuple2‹A, B›(x: A, y: B,) {}; class Tuple3‹A, B, C›(x: A, y: B, z: C,) {}; module Nat {}; class S(s: Nat,): Nat {}; module O: Nat {}; class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O,) {}} +//│ +//│ Preluded. +//│ + +:genCpp +:runCpp +:interpIR +fun compose(f)(g)(x) = f(g(x)) +fun main(x) = + let y = 1 + let lam = z => x + y + z + compose(lam)(lam)(2) +main(3) +//│ |#fun| |compose|(|f|)|(|g|)|(|x|)| |#=| |f|(|g|(|x|)|)|↵|#fun| |main|(|x|)| |#=|→|#let| |y| |#=| |1|↵|#let| |lam| |#=| |z| |#=>| |x| |+| |y| |+| |z|↵|compose|(|lam|)|(|lam|)|(|2|)|←|↵|main|(|3|)| +//│ Parsed: {fun compose = (f,) => (g,) => (x,) => f(g(x,),); fun main = (x,) => {let y = 1; let lam = (z,) => +(+(x, y,), z,); compose(lam,)(lam,)(2,)}; main(3,)} +//│ +//│ +//│ IR: +//│ Program: +//│ class Lambda$0(f) extends Callable { +//│ def apply1(g$0) = +//│ let x$11 = Lambda$2(f,g$0) in -- #33 +//│ x$11 -- #32 +//│ } +//│ class Lambda$1(x,y) extends Callable { +//│ def apply1(z$0) = +//│ let x$12 = +(x,y) in -- #46 +//│ let x$13 = +(x$12,z$0) in -- #45 +//│ x$13 -- #44 +//│ } +//│ class Lambda$2(f,g) extends Callable { +//│ def apply1(x$14) = +//│ let x$15 = Callable.apply1(g,x$14) in -- #57 +//│ let x$16 = Callable.apply1(f,x$15) in -- #56 +//│ x$16 -- #55 +//│ } +//│ def compose(f$0) = +//│ let x$2 = Lambda$0(f$0) in -- #6 +//│ x$2 -- #5 +//│ def main(x$3) = +//│ let x$4 = 1 in -- #25 +//│ let x$6 = Lambda$1(x$3,x$4) in -- #24 +//│ let* (x$7) = compose(x$6) in -- #23 +//│ let x$8 = Callable.apply1(x$7,x$6) in -- #22 +//│ let x$9 = Callable.apply1(x$8,2) in -- #21 +//│ x$9 -- #20 +//│ let* (x$0) = main(3) in -- #4 +//│ x$0 -- #3 +//│ +//│ Interpreted: +//│ 10 +//│ +//│ +//│ Execution succeeded: +//│ 10 +//│ diff --git a/compiler/shared/test/diff-ir/Override.mls b/compiler/shared/test/diff-ir/Override.mls new file mode 100644 index 00000000..7658c8a5 --- /dev/null +++ b/compiler/shared/test/diff-ir/Override.mls @@ -0,0 +1,48 @@ +:NewDefs +:ParseOnly +:UseIR +:NoTailRec + +:genCpp +:runCpp +:interpIR +module Base { + fun f() = 1 +} +module Child extends Base { + fun f() = 2 +} +fun main() = + let c = Child() + Base.f(c) + Child.f(c) +main() +//│ |#module| |Base| |{|→|#fun| |f|(||)| |#=| |1|←|↵|}|↵|#module| |Child| |#extends| |Base| |{|→|#fun| |f|(||)| |#=| |2|←|↵|}|↵|#fun| |main|(||)| |#=|→|#let| |c| |#=| |Child|(||)|↵|Base|.f|(|c|)|↵|Child|.f|(|c|)|←|↵|main|(||)| +//│ Parsed: {module Base {fun f = () => 1}; module Child: Base {fun f = () => 2}; fun main = () => {let c = Child(); (Base).f(c,); (Child).f(c,)}; main()} +//│ +//│ +//│ IR: +//│ Program: +//│ class Base() { +//│ def f() = +//│ 1 -- #16 +//│ } +//│ class Child() extends Base { +//│ def f() = +//│ 2 -- #17 +//│ } +//│ def main() = +//│ let x$1 = Child() in -- #15 +//│ let x$2 = Base.f(x$1) in -- #14 +//│ let x$3 = Child.f(x$1) in -- #13 +//│ x$3 -- #12 +//│ let* (x$0) = main() in -- #2 +//│ x$0 -- #1 +//│ +//│ Interpreted: +//│ 2 +//│ +//│ +//│ Execution succeeded: +//│ 2 +//│ diff --git a/compiler/shared/test/diff-ir/cpp/Makefile b/compiler/shared/test/diff-ir/cpp/Makefile new file mode 100644 index 00000000..082a04fb --- /dev/null +++ b/compiler/shared/test/diff-ir/cpp/Makefile @@ -0,0 +1,26 @@ +CXX := g++ +CFLAGS := $(CFLAGS) -O3 -Wall -Wextra -std=c++20 -I. -Wno-inconsistent-missing-override +LDFLAGS := $(LDFLAGS) -lmimalloc -lgmp +SRC := +INCLUDES = mlsprelude.h +DST := +DEFAULT_TARGET := mls +TARGET := $(or $(DST),$(DEFAULT_TARGET)) + +.PHONY: pre all run clean auto + +all: $(TARGET) + +run: $(TARGET) + ./$(TARGET) + +pre: $(SRC) + sed -i '' 's#^//│ ##g' $(SRC) + +clean: + rm -r $(TARGET) $(TARGET).dSYM + +auto: $(TARGET) + +$(TARGET): $(SRC) $(INCLUDES) + $(CXX) $(CFLAGS) $(LDFLAGS) $(SRC) -o $(TARGET) diff --git a/compiler/shared/test/diff-ir/cpp/mlsprelude.h b/compiler/shared/test/diff-ir/cpp/mlsprelude.h new file mode 100644 index 00000000..8415951c --- /dev/null +++ b/compiler/shared/test/diff-ir/cpp/mlsprelude.h @@ -0,0 +1,568 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +constexpr std::size_t _mlsAlignment = 8; + +template class tuple_type { + template > struct impl; + template struct impl> { + template using wrap = T; + using type = std::tuple...>; + }; + +public: + using type = typename impl<>::type; +}; +template struct counter { + using tag = counter; + + struct generator { + friend consteval auto is_defined(tag) { return true; } + }; + friend consteval auto is_defined(tag); + + template + static consteval auto exists(auto) { + return true; + } + + static consteval auto exists(...) { return generator(), false; } +}; + +template +consteval auto nextTypeTag() { + if constexpr (not counter::exists(Id)) + return Id; + else + return nextTypeTag(); +} + +struct _mlsObject { + uint32_t refCount; + uint32_t tag; + constexpr static inline uint32_t stickyRefCount = + std::numeric_limits::max(); + + void incRef() { + if (refCount != stickyRefCount) + ++refCount; + } + bool decRef() { + if (refCount != stickyRefCount && --refCount == 0) + return true; + return false; + } + + virtual void print() const = 0; + virtual void destroy() = 0; +}; + +struct _mls_True; +struct _mls_False; + +class _mlsValue { + using uintptr_t = std::uintptr_t; + using uint64_t = std::uint64_t; + + void *value alignas(_mlsAlignment); + + bool isInt63() const { return (reinterpret_cast(value) & 1) == 1; } + + bool isPtr() const { return (reinterpret_cast(value) & 1) == 0; } + + uint64_t asInt63() const { return reinterpret_cast(value) >> 1; } + + uintptr_t asRawInt() const { return reinterpret_cast(value); } + + static _mlsValue fromRawInt(uintptr_t i) { + return _mlsValue(reinterpret_cast(i)); + } + + static _mlsValue fromInt63(uint64_t i) { + return _mlsValue(reinterpret_cast((i << 1) | 1)); + } + + void *asPtr() const { + assert(!isInt63()); + return value; + } + + _mlsObject *asObject() const { + assert(isPtr()); + return static_cast<_mlsObject *>(value); + } + + bool eqInt63(const _mlsValue &other) const { + return asRawInt() == other.asRawInt(); + } + + _mlsValue addInt63(const _mlsValue &other) const { + return fromRawInt(asRawInt() + other.asRawInt() - 1); + } + + _mlsValue subInt63(const _mlsValue &other) const { + return fromRawInt(asRawInt() - other.asRawInt() + 1); + } + + _mlsValue mulInt63(const _mlsValue &other) const { + return fromInt63(asInt63() * other.asInt63()); + } + + _mlsValue divInt63(const _mlsValue &other) const { + return fromInt63(asInt63() / other.asInt63()); + } + + _mlsValue gtInt63(const _mlsValue &other) const { + return asInt63() > other.asInt63() ? _mlsValue::create<_mls_True>() + : _mlsValue::create<_mls_False>(); + } + + _mlsValue ltInt63(const _mlsValue &other) const { + return asInt63() < other.asInt63() ? _mlsValue::create<_mls_True>() + : _mlsValue::create<_mls_False>(); + } + + _mlsValue geInt63(const _mlsValue &other) const { + return asInt63() >= other.asInt63() ? _mlsValue::create<_mls_True>() + : _mlsValue::create<_mls_False>(); + } + + _mlsValue leInt63(const _mlsValue &other) const { + return asInt63() <= other.asInt63() ? _mlsValue::create<_mls_True>() + : _mlsValue::create<_mls_False>(); + } + +public: + explicit _mlsValue() : value(nullptr) {} + explicit _mlsValue(void *value) : value(value) {} + _mlsValue(const _mlsValue &other) : value(other.value) { + if (isPtr()) + asObject()->incRef(); + } + + _mlsValue &operator=(const _mlsValue &other) { + if (value != nullptr && isPtr()) + asObject()->decRef(); + value = other.value; + if (isPtr()) + asObject()->incRef(); + return *this; + } + + ~_mlsValue() { + if (isPtr()) + if (asObject()->decRef()) { + asObject()->destroy(); + value = nullptr; + } + } + + uint64_t asInt() const { + assert(isInt63()); + return asInt63(); + } + + static _mlsValue fromIntLit(uint64_t i) { return fromInt63(i); } + + template static tuple_type<_mlsValue, N> never() { + __builtin_unreachable(); + } + static _mlsValue never() { __builtin_unreachable(); } + + template static _mlsValue create(U... args) { + return _mlsValue(T::create(args...)); + } + + static void destroy(_mlsValue &v) { v.~_mlsValue(); } + + template static bool isValueOf(const _mlsValue &v) { + return v.asObject()->tag == T::typeTag; + } + + static bool isIntLit(const _mlsValue &v, uint64_t n) { + return v.asInt63() == n; + } + + static bool isIntLit(const _mlsValue &v) { return v.isInt63(); } + + template static T *as(const _mlsValue &v) { + return dynamic_cast(v.asObject()); + } + + template static T *cast(_mlsValue &v) { + return static_cast(v.asObject()); + } + + // Operators + + _mlsValue operator==(const _mlsValue &other) const { + if (isInt63() && other.isInt63()) + return eqInt63(other) ? _mlsValue::create<_mls_True>() + : _mlsValue::create<_mls_False>(); + assert(false); + } + + _mlsValue operator+(const _mlsValue &other) const { + if (isInt63() && other.isInt63()) + return addInt63(other); + assert(false); + } + + _mlsValue operator-(const _mlsValue &other) const { + if (isInt63() && other.isInt63()) + return subInt63(other); + assert(false); + } + + _mlsValue operator*(const _mlsValue &other) const { + if (isInt63() && other.isInt63()) + return mulInt63(other); + assert(false); + } + + _mlsValue operator/(const _mlsValue &other) const { + if (isInt63() && other.isInt63()) + return divInt63(other); + assert(false); + } + + _mlsValue operator>(const _mlsValue &other) const { + if (isInt63() && other.isInt63()) + return gtInt63(other); + assert(false); + } + + _mlsValue operator<(const _mlsValue &other) const { + if (isInt63() && other.isInt63()) + return ltInt63(other); + assert(false); + } + + _mlsValue operator>=(const _mlsValue &other) const { + if (isInt63() && other.isInt63()) + return geInt63(other); + assert(false); + } + + _mlsValue operator<=(const _mlsValue &other) const { + if (isInt63() && other.isInt63()) + return leInt63(other); + assert(false); + } + + // Auxiliary functions + + void print() const { + if (isInt63()) + std::printf("%" PRIu64, asInt63()); + else if (isPtr() && asObject()) + asObject()->print(); + } +}; + +struct _mls_Callable : public _mlsObject { + virtual _mlsValue _mls_apply0() { throw std::runtime_error("Not implemented"); } + virtual _mlsValue _mls_apply1(_mlsValue) { + throw std::runtime_error("Not implemented"); + } + virtual _mlsValue _mls_apply2(_mlsValue, _mlsValue) { + throw std::runtime_error("Not implemented"); + } + virtual _mlsValue _mls_apply3(_mlsValue, _mlsValue, _mlsValue) { + throw std::runtime_error("Not implemented"); + } + virtual _mlsValue _mls_apply4(_mlsValue, _mlsValue, _mlsValue, _mlsValue) { + throw std::runtime_error("Not implemented"); + } + virtual void destroy() override {} +}; + +inline static _mls_Callable *_mlsToCallable(_mlsValue fn) { + auto *ptr = _mlsValue::as<_mls_Callable>(fn); + if (!ptr) + throw std::runtime_error("Not a callable object"); + return ptr; +} + +template +inline static _mlsValue _mlsCall(_mlsValue f, U... args) { + static_assert(sizeof...(U) <= 4, "Too many arguments"); + if constexpr (sizeof...(U) == 0) + return _mlsToCallable(f)->_mls_apply0(); + else if constexpr (sizeof...(U) == 1) + return _mlsToCallable(f)->_mls_apply1(args...); + else if constexpr (sizeof...(U) == 2) + return _mlsToCallable(f)->_mls_apply2(args...); + else if constexpr (sizeof...(U) == 3) + return _mlsToCallable(f)->_mls_apply3(args...); + else if constexpr (sizeof...(U) == 4) + return _mlsToCallable(f)->_mls_apply4(args...); +} + +template +inline static T *_mlsMethodCall(_mlsValue self) { + auto *ptr = _mlsValue::as(self); + if (!ptr) + throw std::runtime_error("unable to convert object for method calls"); + return ptr; +} + +inline int _mlsLargeStack(void *(*fn)(void *)) { + pthread_t thread; + pthread_attr_t attr; + + size_t stacksize = 512 * 1024 * 1024; + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, stacksize); + + int rc = pthread_create(&thread, &attr, fn, nullptr); + if (rc) { + printf("ERROR: return code from pthread_create() is %d\n", rc); + return 1; + } + pthread_join(thread, NULL); + return 0; +} + +_mlsValue _mlsMain(); + +inline void *_mlsMainWrapper(void *) { + _mlsValue res = _mlsMain(); + res.print(); + return nullptr; +} + +struct _mls_Unit final : public _mlsObject { + constexpr static inline const char *typeName = "Unit"; + constexpr static inline uint32_t typeTag = nextTypeTag(); + virtual void print() const override { std::printf(typeName); } + static _mlsValue create() { + static _mls_Unit mlsUnit alignas(_mlsAlignment); + mlsUnit.refCount = stickyRefCount; + mlsUnit.tag = typeTag; + return _mlsValue(&mlsUnit); + } + virtual void destroy() override {} +}; + +struct _mls_Boolean : public _mlsObject {}; + +struct _mls_True final : public _mls_Boolean { + constexpr static inline const char *typeName = "True"; + constexpr static inline uint32_t typeTag = nextTypeTag(); + virtual void print() const override { std::printf(typeName); } + static _mlsValue create() { + static _mls_True mlsTrue alignas(_mlsAlignment); + mlsTrue.refCount = stickyRefCount; + mlsTrue.tag = typeTag; + return _mlsValue(&mlsTrue); + } + virtual void destroy() override {} +}; + +struct _mls_False final : public _mls_Boolean { + constexpr static inline const char *typeName = "False"; + constexpr static inline uint32_t typeTag = nextTypeTag(); + virtual void print() const override { std::printf(typeName); } + static _mlsValue create() { + static _mls_False mlsFalse alignas(_mlsAlignment); + mlsFalse.refCount = stickyRefCount; + mlsFalse.tag = typeTag; + return _mlsValue(&mlsFalse); + } + virtual void destroy() override {} +}; + +#include + +struct _mls_ZInt final : public _mlsObject { + boost::multiprecision::mpz_int z; + constexpr static inline const char *typeName = "Z"; + constexpr static inline uint32_t typeTag = nextTypeTag(); + virtual void print() const override { + std::printf(typeName); + std::printf("("); + std::printf("%s", z.str().c_str()); + std::printf(")"); + } + virtual void destroy() override { + z.~number(); + operator delete(this, std::align_val_t(_mlsAlignment)); + } + static _mlsValue create() { + auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_ZInt; + _mlsVal->refCount = 1; + _mlsVal->tag = typeTag; + return _mlsValue(_mlsVal); + } + static _mlsValue create(_mlsValue z) { + auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_ZInt; + _mlsVal->z = z.asInt(); + _mlsVal->refCount = 1; + _mlsVal->tag = typeTag; + return _mlsValue(_mlsVal); + } + _mlsValue operator+(const _mls_ZInt &other) const { + auto _mlsVal = _mlsValue::create<_mls_ZInt>(); + _mlsValue::cast<_mls_ZInt>(_mlsVal)->z = z + other.z; + return _mlsVal; + } + + _mlsValue operator-(const _mls_ZInt &other) const { + auto _mlsVal = _mlsValue::create<_mls_ZInt>(); + _mlsValue::cast<_mls_ZInt>(_mlsVal)->z = z - other.z; + return _mlsVal; + } + + _mlsValue operator*(const _mls_ZInt &other) const { + auto _mlsVal = _mlsValue::create<_mls_ZInt>(); + _mlsValue::cast<_mls_ZInt>(_mlsVal)->z = z * other.z; + return _mlsVal; + } + + _mlsValue operator/(const _mls_ZInt &other) const { + auto _mlsVal = _mlsValue::create<_mls_ZInt>(); + _mlsValue::cast<_mls_ZInt>(_mlsVal)->z = z / other.z; + return _mlsVal; + } + + _mlsValue operator%(const _mls_ZInt &other) const { + auto _mlsVal = _mlsValue::create<_mls_ZInt>(); + _mlsValue::cast<_mls_ZInt>(_mlsVal)->z = z % other.z; + return _mlsVal; + } + + _mlsValue operator==(const _mls_ZInt &other) const { + return z == other.z ? _mlsValue::create<_mls_True>() + : _mlsValue::create<_mls_False>(); + } + + _mlsValue operator>(const _mls_ZInt &other) const { + return z > other.z ? _mlsValue::create<_mls_True>() + : _mlsValue::create<_mls_False>(); + } + + _mlsValue operator<(const _mls_ZInt &other) const { + return z < other.z ? _mlsValue::create<_mls_True>() + : _mlsValue::create<_mls_False>(); + } + + _mlsValue operator>=(const _mls_ZInt &other) const { + return z >= other.z ? _mlsValue::create<_mls_True>() + : _mlsValue::create<_mls_False>(); + } + + _mlsValue operator<=(const _mls_ZInt &other) const { + return z <= other.z ? _mlsValue::create<_mls_True>() + : _mlsValue::create<_mls_False>(); + } + + _mlsValue toInt() const { + return _mlsValue::fromIntLit(z.convert_to()); + } + + static _mlsValue fromInt(uint64_t i) { + return _mlsValue::create<_mls_ZInt>(_mlsValue::fromIntLit(i)); + } +}; + +__attribute__((noinline)) inline void _mlsNonExhaustiveMatch() { + throw std::runtime_error("Non-exhaustive match"); +} + +inline _mlsValue _mls_builtin_z_add(_mlsValue a, _mlsValue b) { + assert(_mlsValue::isValueOf<_mls_ZInt>(a)); + assert(_mlsValue::isValueOf<_mls_ZInt>(b)); + return *_mlsValue::cast<_mls_ZInt>(a) + *_mlsValue::cast<_mls_ZInt>(b); +} + +inline _mlsValue _mls_builtin_z_sub(_mlsValue a, _mlsValue b) { + assert(_mlsValue::isValueOf<_mls_ZInt>(a)); + assert(_mlsValue::isValueOf<_mls_ZInt>(b)); + return *_mlsValue::cast<_mls_ZInt>(a) - *_mlsValue::cast<_mls_ZInt>(b); +} + +inline _mlsValue _mls_builtin_z_mul(_mlsValue a, _mlsValue b) { + assert(_mlsValue::isValueOf<_mls_ZInt>(a)); + assert(_mlsValue::isValueOf<_mls_ZInt>(b)); + return *_mlsValue::cast<_mls_ZInt>(a) * *_mlsValue::cast<_mls_ZInt>(b); +} + +inline _mlsValue _mls_builtin_z_div(_mlsValue a, _mlsValue b) { + assert(_mlsValue::isValueOf<_mls_ZInt>(a)); + assert(_mlsValue::isValueOf<_mls_ZInt>(b)); + return *_mlsValue::cast<_mls_ZInt>(a) / *_mlsValue::cast<_mls_ZInt>(b); +} + +inline _mlsValue _mls_builtin_z_mod(_mlsValue a, _mlsValue b) { + assert(_mlsValue::isValueOf<_mls_ZInt>(a)); + assert(_mlsValue::isValueOf<_mls_ZInt>(b)); + return *_mlsValue::cast<_mls_ZInt>(a) % *_mlsValue::cast<_mls_ZInt>(b); +} + +inline _mlsValue _mls_builtin_z_equal(_mlsValue a, _mlsValue b) { + assert(_mlsValue::isValueOf<_mls_ZInt>(a)); + assert(_mlsValue::isValueOf<_mls_ZInt>(b)); + return *_mlsValue::cast<_mls_ZInt>(a) == *_mlsValue::cast<_mls_ZInt>(b); +} + +inline _mlsValue _mls_builtin_z_gt(_mlsValue a, _mlsValue b) { + assert(_mlsValue::isValueOf<_mls_ZInt>(a)); + assert(_mlsValue::isValueOf<_mls_ZInt>(b)); + return *_mlsValue::cast<_mls_ZInt>(a) > *_mlsValue::cast<_mls_ZInt>(b); +} + +inline _mlsValue _mls_builtin_z_lt(_mlsValue a, _mlsValue b) { + assert(_mlsValue::isValueOf<_mls_ZInt>(a)); + assert(_mlsValue::isValueOf<_mls_ZInt>(b)); + return *_mlsValue::cast<_mls_ZInt>(a) < *_mlsValue::cast<_mls_ZInt>(b); +} + +inline _mlsValue _mls_builtin_z_geq(_mlsValue a, _mlsValue b) { + assert(_mlsValue::isValueOf<_mls_ZInt>(a)); + assert(_mlsValue::isValueOf<_mls_ZInt>(b)); + return *_mlsValue::cast<_mls_ZInt>(a) >= *_mlsValue::cast<_mls_ZInt>(b); +} + +inline _mlsValue _mls_builtin_z_leq(_mlsValue a, _mlsValue b) { + assert(_mlsValue::isValueOf<_mls_ZInt>(a)); + assert(_mlsValue::isValueOf<_mls_ZInt>(b)); + return *_mlsValue::cast<_mls_ZInt>(a) <= *_mlsValue::cast<_mls_ZInt>(b); +} + +inline _mlsValue _mls_builtin_z_to_int(_mlsValue a) { + assert(_mlsValue::isValueOf<_mls_ZInt>(a)); + return _mlsValue::cast<_mls_ZInt>(a)->toInt(); +} + +inline _mlsValue _mls_builtin_z_of_int(_mlsValue a) { + assert(_mlsValue::isIntLit(a)); + return _mlsValue::create<_mls_ZInt>(a); +} + +inline _mlsValue _mls_builtin_print(_mlsValue a) { + a.print(); + return _mlsValue::create<_mls_Unit>(); +} + +inline _mlsValue _mls_builtin_println(_mlsValue a) { + a.print(); + std::puts(""); + return _mlsValue::create<_mls_Unit>(); +} + +inline _mlsValue _mls_builtin_debug(_mlsValue a) { + a.print(); + std::puts(""); + return a; +} diff --git a/compiler/shared/test/diff-ir/gcd.mls b/compiler/shared/test/diff-ir/gcd.mls new file mode 100644 index 00000000..1a1c3b6c --- /dev/null +++ b/compiler/shared/test/diff-ir/gcd.mls @@ -0,0 +1,823 @@ +:NewDefs +:ParseOnly +:UseIR +:NoTailRec + +:prelude +module True +module False +module Callable { + fun apply0() = 0 + fun apply1(x0) = 0 + fun apply2(x0,x1) = 0 + fun apply3(x0,x1,x2) = 0 + fun apply4(x0,x1,x2,x3) = 0 + fun apply5(x0,x1,x2,x3,x4) = 0 +} +module List[A, B] +class Cons[A, B](h: A, t: Cons[A, B]) extends List[A, B] +module Nil[A, B] extends List[A, B] +module Option[A] +class Some[A](x: A) extends Option[A] +module None[A] extends Option[A] +class Pair[A, B](x: A, y: B) +class Tuple2[A, B](x: A, y: B) +class Tuple3[A, B, C](x: A, y: B, z: C) +module Nat +class S(s: Nat) extends Nat +module O extends Nat +class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O) +//│ |#module| |True|↵|#module| |False|↵|#module| |Callable| |{|→|#fun| |apply0|(||)| |#=| |0|↵|#fun| |apply1|(|x0|)| |#=| |0|↵|#fun| |apply2|(|x0|,|x1|)| |#=| |0|↵|#fun| |apply3|(|x0|,|x1|,|x2|)| |#=| |0|↵|#fun| |apply4|(|x0|,|x1|,|x2|,|x3|)| |#=| |0|↵|#fun| |apply5|(|x0|,|x1|,|x2|,|x3|,|x4|)| |#=| |0|←|↵|}|↵|#module| |List|[|A|,| |B|]|↵|#class| |Cons|[|A|,| |B|]|(|h|#:| |A|,| |t|#:| |Cons|[|A|,| |B|]|)| |#extends| |List|[|A|,| |B|]|↵|#module| |Nil|[|A|,| |B|]| |#extends| |List|[|A|,| |B|]|↵|#module| |Option|[|A|]|↵|#class| |Some|[|A|]|(|x|#:| |A|)| |#extends| |Option|[|A|]|↵|#module| |None|[|A|]| |#extends| |Option|[|A|]|↵|#class| |Pair|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple2|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple3|[|A|,| |B|,| |C|]|(|x|#:| |A|,| |y|#:| |B|,| |z|#:| |C|)|↵|#module| |Nat|↵|#class| |S|(|s|#:| |Nat|)| |#extends| |Nat|↵|#module| |O| |#extends| |Nat|↵|#class| |HiddenTheseEntities|(|_0|#:| |HiddenTheseEntities|,| |_1|#:| |True|,| |_2|#:| |False|,| |_3|#:| |Callable|,| |_4|#:| |List|,| |_5|#:| |Cons|,| |_6|#:| |Nil|,| |_7|#:| |Option|,| |_8|#:| |Some|,| |_9|#:| |None|,| |_10|#:| |Pair|,| |_11|#:| |Tuple2|,| |_12|#:| |Tuple3|,| |_13|#:| |Nat|,| |_14|#:| |S|,| |_15|#:| |O|)| +//│ Parsed: {module True {}; module False {}; module Callable {fun apply0 = () => 0; fun apply1 = (x0,) => 0; fun apply2 = (x0, x1,) => 0; fun apply3 = (x0, x1, x2,) => 0; fun apply4 = (x0, x1, x2, x3,) => 0; fun apply5 = (x0, x1, x2, x3, x4,) => 0}; module List‹A, B› {}; class Cons‹A, B›(h: A, t: Cons‹A, B›,): List‹A, B› {}; module Nil‹A, B›: List‹A, B› {}; module Option‹A› {}; class Some‹A›(x: A,): Option‹A› {}; module None‹A›: Option‹A› {}; class Pair‹A, B›(x: A, y: B,) {}; class Tuple2‹A, B›(x: A, y: B,) {}; class Tuple3‹A, B, C›(x: A, y: B, z: C,) {}; module Nat {}; class S(s: Nat,): Nat {}; module O: Nat {}; class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O,) {}} +//│ +//│ Preluded. +//│ + +:genCpp +:runCpp +fun error() = builtin("error") +fun z_of_int(x) = builtin("z_of_int", x) +fun z_to_int(x) = builtin("z_to_int", x) +fun z_add(x, y) = builtin("z_add", x, y) +fun z_sub(x, y) = builtin("z_sub", x, y) +fun z_div(x, y) = builtin("z_div", x, y) +fun z_mul(x, y) = builtin("z_mul", x, y) +fun z_mod(x, y) = builtin("z_mod", x, y) +fun z_lt(x, y) = builtin("z_lt", x, y) +fun z_leq(x, y) = builtin("z_leq", x, y) +fun z_equal(x, y) = builtin("z_equal", x, y) +fun z_gt(x, y) = builtin("z_gt", x, y) +fun z_geq(x, y) = builtin("z_geq", x, y) +fun println(x) = builtin("println", x) +fun print(x) = builtin("print", x) +fun debug(x) = builtin("debug", x) +fun map(f, ls) = + if ls is + Cons (h, t) then + Cons (f(h), map(f, t)) + Nil then + Nil +fun filter(f_2, ls_2) = + if ls_2 is + Cons (h_2, t_2) then + if f_2(h_2) then + Cons (h_2, filter(f_2, t_2)) + else + (filter(f_2, t_2)) + Nil then + Nil +fun foldl(f_4, i, ls_4) = + if ls_4 is + Cons (h_4, t_4) then + foldl(f_4, f_4(i, h_4), t_4) + Nil then + i +fun foldr(f_5, i_1, ls_5) = + if ls_5 is + Cons (h_5, t_5) then + f_5(h_5, foldr(f_5, i_1, t_5)) + Nil then + i_1 +fun zip(xs, ys) = + if xs is + Cons (hx, tx) then + if ys is + Cons (hy, ty) then + Cons (Tuple2 (hx, hy), zip(tx, ty)) + Nil then + Nil + Nil then + Nil +fun zipWith(f_7, xs_4, ys_4) = + if xs_4 is + Cons (hx_4, tx_4) then + if ys_4 is + Cons (hy_4, ty_4) then + Cons (f_7(hx_4, hy_4), zipWith(f_7, tx_4, ty_4)) + Nil then + Nil + Nil then + Nil +fun head(ls_7) = + if ls_7 is + Cons (h_7, t_7) then + h_7 + Nil then + error +fun tail(ls_9) = + if ls_9 is + Cons (h_9, t_9) then + t_9 + Nil then + error +fun enumFromTo(a, b) = + if a <= b then + Cons (a, enumFromTo(a + 1, b)) + else + (Nil) +fun enumFromThenTo(a_1, t_11, b_1) = + if a_1 <= b_1 then + Cons (a_1, enumFromThenTo(t_11, 2 * t_11 - a_1, b_1)) + else + (Nil) +fun take(n, ls_11) = + if n > 0 then + if ls_11 is + Cons (h_11, t_13) then + Cons (h_11, take(n - 1, t_13)) + Nil then + Nil + else + (Nil) +fun length(ls_13) = + if ls_13 is + Cons (h_13, t_15) then + 1 + (length(t_15)) + Nil then + 0 +fun mappend(xs_8, ys_8) = + if xs_8 is + Cons (h_14, t_16) then + Cons (h_14, mappend(t_16, ys_8)) + Nil then + ys_8 +fun sum(ls_14) = + sumAux(ls_14, 0) +fun sumAux(ls_15, a_4) = + if ls_15 is + Nil then + a_4 + Cons (h_15, t_17) then + sumAux(t_17, a_4 + h_15) +fun atIndex(n_2, ls_16) = + if n_2 < 0 then + error + else + if ls_16 is + Cons (h_16, t_18) then + if n_2 == 0 then + h_16 + else + (atIndex(n_2 - 1, t_18)) + Nil then + error +fun concat(lss) = + if lss is + Cons (h_18, t_20) then + mappend(h_18, concat(t_20)) + Nil then + Nil +fun reverse(ls_18) = + reverse_helper(ls_18, Nil) +fun reverse_helper(ls_19, a_5) = + if ls_19 is + Cons (h_19, t_21) then + reverse_helper(t_21, Cons (h_19, a_5)) + Nil then + a_5 +fun listcomp_fun1(ms, listcomp_fun_para) = + if listcomp_fun_para is + Cons(listcomp_fun_ls_h, listcomp_fun_ls_t) then + listcomp_fun2(ms, listcomp_fun_ls_h, listcomp_fun_ls_t, ms) + Nil then + Nil +fun listcomp_fun2(ms, listcomp_fun_ls_h_out, listcomp_fun_ls_t_out, listcomp_fun_para) = + if listcomp_fun_para is + Cons(listcomp_fun_ls_h, listcomp_fun_ls_t) then + Cons(Tuple2 (listcomp_fun_ls_h_out, listcomp_fun_ls_h), listcomp_fun2(ms, listcomp_fun_ls_h_out, listcomp_fun_ls_t_out, listcomp_fun_ls_t)) + Nil then + listcomp_fun1(ms, listcomp_fun_ls_t_out) +fun test(test_arg1) = + let ns = z_enumFromTo(const5000(), z_add(const5000(), test_arg1)) + let ms = z_enumFromTo(const10000(), z_add(const10000(), test_arg1)) + let tripls = map(f1, listcomp_fun1(ms, ns)) + let rs = map(f2, tripls) + max'(rs) +fun const10000() = + z_of_int(10000) +fun f1(f1_arg1) = + if f1_arg1 is + Tuple2 (f1_Tuple2_0, f1_Tuple2_1) then + Tuple3 (f1_Tuple2_0, f1_Tuple2_1, gcdE(f1_Tuple2_0, f1_Tuple2_1)) +fun quotRem(quotRem_arg1, quotRem_arg2) = + Tuple2 (z_div(quotRem_arg1, quotRem_arg2), z_mod(quotRem_arg1, quotRem_arg2)) +fun max'(max'_arg1) = + if max'_arg1 is + Cons (max'_Cons_0, max'_Cons_1) then + if max'_Cons_1 is + Nil then + max'_Cons_0 + Cons (max'_Cons_0_1, max'_Cons_1_1) then + if z_lt(max'_Cons_0, max'_Cons_0_1) then + max'(Cons (max'_Cons_0_1, max'_Cons_1_1)) + else + (max'(Cons (max'_Cons_0, max'_Cons_1_1))) +fun g(g_arg1, g_arg2) = + if g_arg1 is + Tuple3 (g_Tuple3_0, g_Tuple3_1, g_Tuple3_2) then + if g_arg2 is + Tuple3 (g_Tuple3_0_1, g_Tuple3_1_1, g_Tuple3_2_1) then + if z_equal(g_Tuple3_2_1, const0()) then + Tuple3 (g_Tuple3_2, g_Tuple3_0, g_Tuple3_1) + else + let matchIdent = quotRem(g_Tuple3_2, g_Tuple3_2_1) + if matchIdent is + Tuple2 (g_Tuple2_0, g_Tuple2_1) then + g(Tuple3 (g_Tuple3_0_1, g_Tuple3_1_1, g_Tuple3_2_1), Tuple3 (z_sub(g_Tuple3_0, z_mul(g_Tuple2_0, g_Tuple3_0_1)), z_sub(g_Tuple3_1, z_mul(g_Tuple2_0, g_Tuple3_1_1)), g_Tuple2_1)) +fun abs(abs_arg1) = + if z_lt(abs_arg1, const0()) then + z_sub(const0(), abs_arg1) + else + abs_arg1 +fun f2(f2_arg1) = + if f2_arg1 is + Tuple3 (f2_Tuple3_0, f2_Tuple3_1, f2_Tuple3_2) then + if f2_Tuple3_2 is + Tuple3 (f2_Tuple3_0_1, f2_Tuple3_1_1, f2_Tuple3_2_1) then + abs(z_add(z_add(f2_Tuple3_0_1, f2_Tuple3_1_1), f2_Tuple3_2_1)) +fun const0() = + z_of_int(0) +fun gcdE(gcdE_arg1, gcdE_arg2) = + if z_equal(gcdE_arg1, const0()) then + Tuple3 (gcdE_arg2, const0(), const1()) + else + (g(Tuple3 (const1(), const0(), gcdE_arg1), Tuple3 (const0(), const1(), gcdE_arg2))) +fun const1() = + z_of_int(1) +fun const5000() = + z_of_int(5000) +fun testGcd_nofib(testGcd_nofib_arg1) = + test(testGcd_nofib_arg1) +fun z_enumFromTo(z_enumFromTo_arg1, z_enumFromTo_arg2) = + if z_leq(z_enumFromTo_arg1, z_enumFromTo_arg2) then + Cons (z_enumFromTo_arg1, z_enumFromTo(z_add(z_enumFromTo_arg1, const1()), z_enumFromTo_arg2)) + else + (Nil) +testGcd_nofib(z_of_int(400)) +//│ |#fun| |error|(||)| |#=| |builtin|(|"error"|)|↵|#fun| |z_of_int|(|x|)| |#=| |builtin|(|"z_of_int"|,| |x|)|↵|#fun| |z_to_int|(|x|)| |#=| |builtin|(|"z_to_int"|,| |x|)|↵|#fun| |z_add|(|x|,| |y|)| |#=| |builtin|(|"z_add"|,| |x|,| |y|)|↵|#fun| |z_sub|(|x|,| |y|)| |#=| |builtin|(|"z_sub"|,| |x|,| |y|)|↵|#fun| |z_div|(|x|,| |y|)| |#=| |builtin|(|"z_div"|,| |x|,| |y|)|↵|#fun| |z_mul|(|x|,| |y|)| |#=| |builtin|(|"z_mul"|,| |x|,| |y|)|↵|#fun| |z_mod|(|x|,| |y|)| |#=| |builtin|(|"z_mod"|,| |x|,| |y|)|↵|#fun| |z_lt|(|x|,| |y|)| |#=| |builtin|(|"z_lt"|,| |x|,| |y|)|↵|#fun| |z_leq|(|x|,| |y|)| |#=| |builtin|(|"z_leq"|,| |x|,| |y|)|↵|#fun| |z_equal|(|x|,| |y|)| |#=| |builtin|(|"z_equal"|,| |x|,| |y|)|↵|#fun| |z_gt|(|x|,| |y|)| |#=| |builtin|(|"z_gt"|,| |x|,| |y|)|↵|#fun| |z_geq|(|x|,| |y|)| |#=| |builtin|(|"z_geq"|,| |x|,| |y|)|↵|#fun| |println|(|x|)| |#=| |builtin|(|"println"|,| |x|)|↵|#fun| |print|(|x|)| |#=| |builtin|(|"print"|,| |x|)|↵|#fun| |debug|(|x|)| |#=| |builtin|(|"debug"|,| |x|)|↵|#fun| |map|(|f|,| |ls|)| |#=|→|#if| |ls| |is|→|Cons| |(|h|,| |t|)| |#then|→|Cons| |(|f|(|h|)|,| |map|(|f|,| |t|)|)|←|↵|Nil| |#then|→|Nil|←|←|←|↵|#fun| |filter|(|f_2|,| |ls_2|)| |#=|→|#if| |ls_2| |is|→|Cons| |(|h_2|,| |t_2|)| |#then|→|#if| |f_2|(|h_2|)| |#then|→|Cons| |(|h_2|,| |filter|(|f_2|,| |t_2|)|)|←|↵|#else|→|(|filter|(|f_2|,| |t_2|)|)|←|←|↵|Nil| |#then|→|Nil|←|←|←|↵|#fun| |foldl|(|f_4|,| |i|,| |ls_4|)| |#=|→|#if| |ls_4| |is|→|Cons| |(|h_4|,| |t_4|)| |#then|→|foldl|(|f_4|,| |f_4|(|i|,| |h_4|)|,| |t_4|)|←|↵|Nil| |#then|→|i|←|←|←|↵|#fun| |foldr|(|f_5|,| |i_1|,| |ls_5|)| |#=|→|#if| |ls_5| |is|→|Cons| |(|h_5|,| |t_5|)| |#then|→|f_5|(|h_5|,| |foldr|(|f_5|,| |i_1|,| |t_5|)|)|←|↵|Nil| |#then|→|i_1|←|←|←|↵|#fun| |zip|(|xs|,| |ys|)| |#=|→|#if| |xs| |is|→|Cons| |(|hx|,| |tx|)| |#then|→|#if| |ys| |is|→|Cons| |(|hy|,| |ty|)| |#then|→|Cons| |(|Tuple2| |(|hx|,| |hy|)|,| |zip|(|tx|,| |ty|)|)|←|↵|Nil| |#then|→|Nil|←|←|←|↵|Nil| |#then|→|Nil|←|←|←|↵|#fun| |zipWith|(|f_7|,| |xs_4|,| |ys_4|)| |#=|→|#if| |xs_4| |is|→|Cons| |(|hx_4|,| |tx_4|)| |#then|→|#if| |ys_4| |is|→|Cons| |(|hy_4|,| |ty_4|)| |#then|→|Cons| |(|f_7|(|hx_4|,| |hy_4|)|,| |zipWith|(|f_7|,| |tx_4|,| |ty_4|)|)|←|↵|Nil| |#then|→|Nil|←|←|←|↵|Nil| |#then|→|Nil|←|←|←|↵|#fun| |head|(|ls_7|)| |#=|→|#if| |ls_7| |is|→|Cons| |(|h_7|,| |t_7|)| |#then|→|h_7|←|↵|Nil| |#then|→|error|←|←|←|↵|#fun| |tail|(|ls_9|)| |#=|→|#if| |ls_9| |is|→|Cons| |(|h_9|,| |t_9|)| |#then|→|t_9|←|↵|Nil| |#then|→|error|←|←|←|↵|#fun| |enumFromTo|(|a|,| |b|)| |#=|→|#if| |a| |<=| |b| |#then|→|Cons| |(|a|,| |enumFromTo|(|a| |+| |1|,| |b|)|)|←|↵|#else|→|(|Nil|)|←|←|↵|#fun| |enumFromThenTo|(|a_1|,| |t_11|,| |b_1|)| |#=|→|#if| |a_1| |<=| |b_1| |#then|→|Cons| |(|a_1|,| |enumFromThenTo|(|t_11|,| |2| |*| |t_11| |-| |a_1|,| |b_1|)|)|←|↵|#else|→|(|Nil|)|←|←|↵|#fun| |take|(|n|,| |ls_11|)| |#=|→|#if| |n| |>| |0| |#then|→|#if| |ls_11| |is|→|Cons| |(|h_11|,| |t_13|)| |#then|→|Cons| |(|h_11|,| |take|(|n| |-| |1|,| |t_13|)|)|←|↵|Nil| |#then|→|Nil|←|←|←|↵|#else|→|(|Nil|)|←|←|↵|#fun| |length|(|ls_13|)| |#=|→|#if| |ls_13| |is|→|Cons| |(|h_13|,| |t_15|)| |#then|→|1| |+| |(|length|(|t_15|)|)|←|↵|Nil| |#then|→|0|←|←|←|↵|#fun| |mappend|(|xs_8|,| |ys_8|)| |#=|→|#if| |xs_8| |is|→|Cons| |(|h_14|,| |t_16|)| |#then|→|Cons| |(|h_14|,| |mappend|(|t_16|,| |ys_8|)|)|←|↵|Nil| |#then|→|ys_8|←|←|←|↵|#fun| |sum|(|ls_14|)| |#=|→|sumAux|(|ls_14|,| |0|)|←|↵|#fun| |sumAux|(|ls_15|,| |a_4|)| |#=|→|#if| |ls_15| |is|→|Nil| |#then|→|a_4|←|↵|Cons| |(|h_15|,| |t_17|)| |#then|→|sumAux|(|t_17|,| |a_4| |+| |h_15|)|←|←|←|↵|#fun| |atIndex|(|n_2|,| |ls_16|)| |#=|→|#if| |n_2| |<| |0| |#then|→|error|←|↵|#else|→|#if| |ls_16| |is|→|Cons| |(|h_16|,| |t_18|)| |#then|→|#if| |n_2| |==| |0| |#then|→|h_16|←|↵|#else|→|(|atIndex|(|n_2| |-| |1|,| |t_18|)|)|←|←|↵|Nil| |#then|→|error|←|←|←|←|↵|#fun| |concat|(|lss|)| |#=|→|#if| |lss| |is|→|Cons| |(|h_18|,| |t_20|)| |#then|→|mappend|(|h_18|,| |concat|(|t_20|)|)|←|↵|Nil| |#then|→|Nil|←|←|←|↵|#fun| |reverse|(|ls_18|)| |#=|→|reverse_helper|(|ls_18|,| |Nil|)|←|↵|#fun| |reverse_helper|(|ls_19|,| |a_5|)| |#=|→|#if| |ls_19| |is|→|Cons| |(|h_19|,| |t_21|)| |#then|→|reverse_helper|(|t_21|,| |Cons| |(|h_19|,| |a_5|)|)|←|↵|Nil| |#then|→|a_5|←|←|←|↵|#fun| |listcomp_fun1|(|ms|,| |listcomp_fun_para|)| |#=|→|#if| |listcomp_fun_para| |is|→|Cons|(|listcomp_fun_ls_h|,| |listcomp_fun_ls_t|)| |#then|→|listcomp_fun2|(|ms|,| |listcomp_fun_ls_h|,| |listcomp_fun_ls_t|,| |ms|)|←|↵|Nil| |#then|→|Nil|←|←|←|↵|#fun| |listcomp_fun2|(|ms|,| |listcomp_fun_ls_h_out|,| |listcomp_fun_ls_t_out|,| |listcomp_fun_para|)| |#=|→|#if| |listcomp_fun_para| |is|→|Cons|(|listcomp_fun_ls_h|,| |listcomp_fun_ls_t|)| |#then|→|Cons|(|Tuple2| |(|listcomp_fun_ls_h_out|,| |listcomp_fun_ls_h|)|,| |listcomp_fun2|(|ms|,| |listcomp_fun_ls_h_out|,| |listcomp_fun_ls_t_out|,| |listcomp_fun_ls_t|)|)|←|↵|Nil| |#then|→|listcomp_fun1|(|ms|,| |listcomp_fun_ls_t_out|)|←|←|←|↵|#fun| |test|(|test_arg1|)| |#=|→|#let| |ns| |#=| |z_enumFromTo|(|const5000|(||)|,| |z_add|(|const5000|(||)|,| |test_arg1|)|)|↵|#let| |ms| |#=| |z_enumFromTo|(|const10000|(||)|,| |z_add|(|const10000|(||)|,| |test_arg1|)|)|↵|#let| |tripls| |#=| |map|(|f1|,| |listcomp_fun1|(|ms|,| |ns|)|)|↵|#let| |rs| |#=| |map|(|f2|,| |tripls|)|↵|max'|(|rs|)|←|↵|#fun| |const10000|(||)| |#=|→|z_of_int|(|10000|)|←|↵|#fun| |f1|(|f1_arg1|)| |#=|→|#if| |f1_arg1| |is|→|Tuple2| |(|f1_Tuple2_0|,| |f1_Tuple2_1|)| |#then|→|Tuple3| |(|f1_Tuple2_0|,| |f1_Tuple2_1|,| |gcdE|(|f1_Tuple2_0|,| |f1_Tuple2_1|)|)|←|←|←|↵|#fun| |quotRem|(|quotRem_arg1|,| |quotRem_arg2|)| |#=|→|Tuple2| |(|z_div|(|quotRem_arg1|,| |quotRem_arg2|)|,| |z_mod|(|quotRem_arg1|,| |quotRem_arg2|)|)|←|↵|#fun| |max'|(|max'_arg1|)| |#=|→|#if| |max'_arg1| |is|→|Cons| |(|max'_Cons_0|,| |max'_Cons_1|)| |#then|→|#if| |max'_Cons_1| |is|→|Nil| |#then|→|max'_Cons_0|←|↵|Cons| |(|max'_Cons_0_1|,| |max'_Cons_1_1|)| |#then|→|#if| |z_lt|(|max'_Cons_0|,| |max'_Cons_0_1|)| |#then|→|max'|(|Cons| |(|max'_Cons_0_1|,| |max'_Cons_1_1|)|)|←|↵|#else|→|(|max'|(|Cons| |(|max'_Cons_0|,| |max'_Cons_1_1|)|)|)|←|←|←|←|←|←|↵|#fun| |g|(|g_arg1|,| |g_arg2|)| |#=|→|#if| |g_arg1| |is|→|Tuple3| |(|g_Tuple3_0|,| |g_Tuple3_1|,| |g_Tuple3_2|)| |#then|→|#if| |g_arg2| |is|→|Tuple3| |(|g_Tuple3_0_1|,| |g_Tuple3_1_1|,| |g_Tuple3_2_1|)| |#then|→|#if| |z_equal|(|g_Tuple3_2_1|,| |const0|(||)|)| |#then|→|Tuple3| |(|g_Tuple3_2|,| |g_Tuple3_0|,| |g_Tuple3_1|)|←|↵|#else|→|#let| |matchIdent| |#=| |quotRem|(|g_Tuple3_2|,| |g_Tuple3_2_1|)|↵|#if| |matchIdent| |is|→|Tuple2| |(|g_Tuple2_0|,| |g_Tuple2_1|)| |#then|→|g|(|Tuple3| |(|g_Tuple3_0_1|,| |g_Tuple3_1_1|,| |g_Tuple3_2_1|)|,| |Tuple3| |(|z_sub|(|g_Tuple3_0|,| |z_mul|(|g_Tuple2_0|,| |g_Tuple3_0_1|)|)|,| |z_sub|(|g_Tuple3_1|,| |z_mul|(|g_Tuple2_0|,| |g_Tuple3_1_1|)|)|,| |g_Tuple2_1|)|)|←|←|←|←|←|←|←|←|↵|#fun| |abs|(|abs_arg1|)| |#=|→|#if| |z_lt|(|abs_arg1|,| |const0|(||)|)| |#then|→|z_sub|(|const0|(||)|,| |abs_arg1|)|←|↵|#else|→|abs_arg1|←|←|↵|#fun| |f2|(|f2_arg1|)| |#=|→|#if| |f2_arg1| |is|→|Tuple3| |(|f2_Tuple3_0|,| |f2_Tuple3_1|,| |f2_Tuple3_2|)| |#then|→|#if| |f2_Tuple3_2| |is|→|Tuple3| |(|f2_Tuple3_0_1|,| |f2_Tuple3_1_1|,| |f2_Tuple3_2_1|)| |#then|→|abs|(|z_add|(|z_add|(|f2_Tuple3_0_1|,| |f2_Tuple3_1_1|)|,| |f2_Tuple3_2_1|)|)|←|←|←|←|←|↵|#fun| |const0|(||)| |#=|→|z_of_int|(|0|)|←|↵|#fun| |gcdE|(|gcdE_arg1|,| |gcdE_arg2|)| |#=|→|#if| |z_equal|(|gcdE_arg1|,| |const0|(||)|)| |#then|→|Tuple3| |(|gcdE_arg2|,| |const0|(||)|,| |const1|(||)|)|←|↵|#else|→|(|g|(|Tuple3| |(|const1|(||)|,| |const0|(||)|,| |gcdE_arg1|)|,| |Tuple3| |(|const0|(||)|,| |const1|(||)|,| |gcdE_arg2|)|)|)|←|←|↵|#fun| |const1|(||)| |#=|→|z_of_int|(|1|)|←|↵|#fun| |const5000|(||)| |#=|→|z_of_int|(|5000|)|←|↵|#fun| |testGcd_nofib|(|testGcd_nofib_arg1|)| |#=|→|test|(|testGcd_nofib_arg1|)|←|↵|#fun| |z_enumFromTo|(|z_enumFromTo_arg1|,| |z_enumFromTo_arg2|)| |#=|→|#if| |z_leq|(|z_enumFromTo_arg1|,| |z_enumFromTo_arg2|)| |#then|→|Cons| |(|z_enumFromTo_arg1|,| |z_enumFromTo|(|z_add|(|z_enumFromTo_arg1|,| |const1|(||)|)|,| |z_enumFromTo_arg2|)|)|←|↵|#else|→|(|Nil|)|←|←|↵|testGcd_nofib|(|z_of_int|(|400|)|)| +//│ Parsed: {fun error = () => builtin("error",); fun z_of_int = (x,) => builtin("z_of_int", x,); fun z_to_int = (x,) => builtin("z_to_int", x,); fun z_add = (x, y,) => builtin("z_add", x, y,); fun z_sub = (x, y,) => builtin("z_sub", x, y,); fun z_div = (x, y,) => builtin("z_div", x, y,); fun z_mul = (x, y,) => builtin("z_mul", x, y,); fun z_mod = (x, y,) => builtin("z_mod", x, y,); fun z_lt = (x, y,) => builtin("z_lt", x, y,); fun z_leq = (x, y,) => builtin("z_leq", x, y,); fun z_equal = (x, y,) => builtin("z_equal", x, y,); fun z_gt = (x, y,) => builtin("z_gt", x, y,); fun z_geq = (x, y,) => builtin("z_geq", x, y,); fun println = (x,) => builtin("println", x,); fun print = (x,) => builtin("print", x,); fun debug = (x,) => builtin("debug", x,); fun map = (f, ls,) => {if ls is ‹(Cons(h, t,)) then {Cons(f(h,), map(f, t,),)}; (Nil) then {Nil}›}; fun filter = (f_2, ls_2,) => {if ls_2 is ‹(Cons(h_2, t_2,)) then {if (f_2(h_2,)) then {Cons(h_2, filter(f_2, t_2,),)} else {'(' filter(f_2, t_2,) ')'}}; (Nil) then {Nil}›}; fun foldl = (f_4, i, ls_4,) => {if ls_4 is ‹(Cons(h_4, t_4,)) then {foldl(f_4, f_4(i, h_4,), t_4,)}; (Nil) then {i}›}; fun foldr = (f_5, i_1, ls_5,) => {if ls_5 is ‹(Cons(h_5, t_5,)) then {f_5(h_5, foldr(f_5, i_1, t_5,),)}; (Nil) then {i_1}›}; fun zip = (xs, ys,) => {if xs is ‹(Cons(hx, tx,)) then {if ys is ‹(Cons(hy, ty,)) then {Cons(Tuple2(hx, hy,), zip(tx, ty,),)}; (Nil) then {Nil}›}; (Nil) then {Nil}›}; fun zipWith = (f_7, xs_4, ys_4,) => {if xs_4 is ‹(Cons(hx_4, tx_4,)) then {if ys_4 is ‹(Cons(hy_4, ty_4,)) then {Cons(f_7(hx_4, hy_4,), zipWith(f_7, tx_4, ty_4,),)}; (Nil) then {Nil}›}; (Nil) then {Nil}›}; fun head = (ls_7,) => {if ls_7 is ‹(Cons(h_7, t_7,)) then {h_7}; (Nil) then {error}›}; fun tail = (ls_9,) => {if ls_9 is ‹(Cons(h_9, t_9,)) then {t_9}; (Nil) then {error}›}; fun enumFromTo = (a, b,) => {if (<=(a, b,)) then {Cons(a, enumFromTo(+(a, 1,), b,),)} else {'(' Nil ')'}}; fun enumFromThenTo = (a_1, t_11, b_1,) => {if (<=(a_1, b_1,)) then {Cons(a_1, enumFromThenTo(t_11, -(*(2, t_11,), a_1,), b_1,),)} else {'(' Nil ')'}}; fun take = (n, ls_11,) => {if (>(n, 0,)) then {if ls_11 is ‹(Cons(h_11, t_13,)) then {Cons(h_11, take(-(n, 1,), t_13,),)}; (Nil) then {Nil}›} else {'(' Nil ')'}}; fun length = (ls_13,) => {if ls_13 is ‹(Cons(h_13, t_15,)) then {+(1, '(' length(t_15,) ')',)}; (Nil) then {0}›}; fun mappend = (xs_8, ys_8,) => {if xs_8 is ‹(Cons(h_14, t_16,)) then {Cons(h_14, mappend(t_16, ys_8,),)}; (Nil) then {ys_8}›}; fun sum = (ls_14,) => {sumAux(ls_14, 0,)}; fun sumAux = (ls_15, a_4,) => {if ls_15 is ‹(Nil) then {a_4}; (Cons(h_15, t_17,)) then {sumAux(t_17, +(a_4, h_15,),)}›}; fun atIndex = (n_2, ls_16,) => {if (<(n_2, 0,)) then {error} else {if ls_16 is ‹(Cons(h_16, t_18,)) then {if (==(n_2, 0,)) then {h_16} else {'(' atIndex(-(n_2, 1,), t_18,) ')'}}; (Nil) then {error}›}}; fun concat = (lss,) => {if lss is ‹(Cons(h_18, t_20,)) then {mappend(h_18, concat(t_20,),)}; (Nil) then {Nil}›}; fun reverse = (ls_18,) => {reverse_helper(ls_18, Nil,)}; fun reverse_helper = (ls_19, a_5,) => {if ls_19 is ‹(Cons(h_19, t_21,)) then {reverse_helper(t_21, Cons(h_19, a_5,),)}; (Nil) then {a_5}›}; fun listcomp_fun1 = (ms, listcomp_fun_para,) => {if listcomp_fun_para is ‹(Cons(listcomp_fun_ls_h, listcomp_fun_ls_t,)) then {listcomp_fun2(ms, listcomp_fun_ls_h, listcomp_fun_ls_t, ms,)}; (Nil) then {Nil}›}; fun listcomp_fun2 = (ms, listcomp_fun_ls_h_out, listcomp_fun_ls_t_out, listcomp_fun_para,) => {if listcomp_fun_para is ‹(Cons(listcomp_fun_ls_h, listcomp_fun_ls_t,)) then {Cons(Tuple2(listcomp_fun_ls_h_out, listcomp_fun_ls_h,), listcomp_fun2(ms, listcomp_fun_ls_h_out, listcomp_fun_ls_t_out, listcomp_fun_ls_t,),)}; (Nil) then {listcomp_fun1(ms, listcomp_fun_ls_t_out,)}›}; fun test = (test_arg1,) => {let ns = z_enumFromTo(const5000(), z_add(const5000(), test_arg1,),); let ms = z_enumFromTo(const10000(), z_add(const10000(), test_arg1,),); let tripls = map(f1, listcomp_fun1(ms, ns,),); let rs = map(f2, tripls,); max'(rs,)}; fun const10000 = () => {z_of_int(10000,)}; fun f1 = (f1_arg1,) => {if f1_arg1 is ‹(Tuple2(f1_Tuple2_0, f1_Tuple2_1,)) then {Tuple3(f1_Tuple2_0, f1_Tuple2_1, gcdE(f1_Tuple2_0, f1_Tuple2_1,),)}›}; fun quotRem = (quotRem_arg1, quotRem_arg2,) => {Tuple2(z_div(quotRem_arg1, quotRem_arg2,), z_mod(quotRem_arg1, quotRem_arg2,),)}; fun max' = (max'_arg1,) => {if max'_arg1 is ‹(Cons(max'_Cons_0, max'_Cons_1,)) then {if max'_Cons_1 is ‹(Nil) then {max'_Cons_0}; (Cons(max'_Cons_0_1, max'_Cons_1_1,)) then {if (z_lt(max'_Cons_0, max'_Cons_0_1,)) then {max'(Cons(max'_Cons_0_1, max'_Cons_1_1,),)} else {'(' max'(Cons(max'_Cons_0, max'_Cons_1_1,),) ')'}}›}›}; fun g = (g_arg1, g_arg2,) => {if g_arg1 is ‹(Tuple3(g_Tuple3_0, g_Tuple3_1, g_Tuple3_2,)) then {if g_arg2 is ‹(Tuple3(g_Tuple3_0_1, g_Tuple3_1_1, g_Tuple3_2_1,)) then {if (z_equal(g_Tuple3_2_1, const0(),)) then {Tuple3(g_Tuple3_2, g_Tuple3_0, g_Tuple3_1,)} else {let matchIdent = quotRem(g_Tuple3_2, g_Tuple3_2_1,); if matchIdent is ‹(Tuple2(g_Tuple2_0, g_Tuple2_1,)) then {g(Tuple3(g_Tuple3_0_1, g_Tuple3_1_1, g_Tuple3_2_1,), Tuple3(z_sub(g_Tuple3_0, z_mul(g_Tuple2_0, g_Tuple3_0_1,),), z_sub(g_Tuple3_1, z_mul(g_Tuple2_0, g_Tuple3_1_1,),), g_Tuple2_1,),)}›}}›}›}; fun abs = (abs_arg1,) => {if (z_lt(abs_arg1, const0(),)) then {z_sub(const0(), abs_arg1,)} else {abs_arg1}}; fun f2 = (f2_arg1,) => {if f2_arg1 is ‹(Tuple3(f2_Tuple3_0, f2_Tuple3_1, f2_Tuple3_2,)) then {if f2_Tuple3_2 is ‹(Tuple3(f2_Tuple3_0_1, f2_Tuple3_1_1, f2_Tuple3_2_1,)) then {abs(z_add(z_add(f2_Tuple3_0_1, f2_Tuple3_1_1,), f2_Tuple3_2_1,),)}›}›}; fun const0 = () => {z_of_int(0,)}; fun gcdE = (gcdE_arg1, gcdE_arg2,) => {if (z_equal(gcdE_arg1, const0(),)) then {Tuple3(gcdE_arg2, const0(), const1(),)} else {'(' g(Tuple3(const1(), const0(), gcdE_arg1,), Tuple3(const0(), const1(), gcdE_arg2,),) ')'}}; fun const1 = () => {z_of_int(1,)}; fun const5000 = () => {z_of_int(5000,)}; fun testGcd_nofib = (testGcd_nofib_arg1,) => {test(testGcd_nofib_arg1,)}; fun z_enumFromTo = (z_enumFromTo_arg1, z_enumFromTo_arg2,) => {if (z_leq(z_enumFromTo_arg1, z_enumFromTo_arg2,)) then {Cons(z_enumFromTo_arg1, z_enumFromTo(z_add(z_enumFromTo_arg1, const1(),), z_enumFromTo_arg2,),)} else {'(' Nil ')'}}; testGcd_nofib(z_of_int(400,),)} +//│ +//│ +//│ IR: +//│ Program: +//│ class Lambda$0() extends Callable { +//│ def apply0() = +//│ let* (x$270) = error() in -- #1354 +//│ x$270 -- #1353 +//│ } +//│ class Lambda$1() extends Callable { +//│ def apply0() = +//│ let* (x$271) = error() in -- #1357 +//│ x$271 -- #1356 +//│ } +//│ class Lambda$2() extends Callable { +//│ def apply0() = +//│ let* (x$272) = error() in -- #1360 +//│ x$272 -- #1359 +//│ } +//│ class Lambda$3() extends Callable { +//│ def apply0() = +//│ let* (x$273) = error() in -- #1363 +//│ x$273 -- #1362 +//│ } +//│ class Lambda$4() extends Callable { +//│ def apply1(x$274) = +//│ let* (x$275) = f1(x$274) in -- #1368 +//│ x$275 -- #1367 +//│ } +//│ class Lambda$5() extends Callable { +//│ def apply1(x$276) = +//│ let* (x$277) = f2(x$276) in -- #1373 +//│ x$277 -- #1372 +//│ } +//│ def error() = +//│ let x$2 = Callable.apply1(builtin,error) in -- #14 +//│ x$2 -- #13 +//│ def z_of_int(x$3) = +//│ let x$4 = Callable.apply2(builtin,z_of_int,x$3) in -- #22 +//│ x$4 -- #21 +//│ def z_to_int(x$5) = +//│ let x$6 = Callable.apply2(builtin,z_to_int,x$5) in -- #30 +//│ x$6 -- #29 +//│ def z_add(x$7,y$0) = +//│ let x$8 = Callable.apply3(builtin,z_add,x$7,y$0) in -- #40 +//│ x$8 -- #39 +//│ def z_sub(x$9,y$1) = +//│ let x$10 = Callable.apply3(builtin,z_sub,x$9,y$1) in -- #50 +//│ x$10 -- #49 +//│ def z_div(x$11,y$2) = +//│ let x$12 = Callable.apply3(builtin,z_div,x$11,y$2) in -- #60 +//│ x$12 -- #59 +//│ def z_mul(x$13,y$3) = +//│ let x$14 = Callable.apply3(builtin,z_mul,x$13,y$3) in -- #70 +//│ x$14 -- #69 +//│ def z_mod(x$15,y$4) = +//│ let x$16 = Callable.apply3(builtin,z_mod,x$15,y$4) in -- #80 +//│ x$16 -- #79 +//│ def z_lt(x$17,y$5) = +//│ let x$18 = Callable.apply3(builtin,z_lt,x$17,y$5) in -- #90 +//│ x$18 -- #89 +//│ def z_leq(x$19,y$6) = +//│ let x$20 = Callable.apply3(builtin,z_leq,x$19,y$6) in -- #100 +//│ x$20 -- #99 +//│ def z_equal(x$21,y$7) = +//│ let x$22 = Callable.apply3(builtin,z_equal,x$21,y$7) in -- #110 +//│ x$22 -- #109 +//│ def z_gt(x$23,y$8) = +//│ let x$24 = Callable.apply3(builtin,z_gt,x$23,y$8) in -- #120 +//│ x$24 -- #119 +//│ def z_geq(x$25,y$9) = +//│ let x$26 = Callable.apply3(builtin,z_geq,x$25,y$9) in -- #130 +//│ x$26 -- #129 +//│ def println(x$27) = +//│ let x$28 = Callable.apply2(builtin,println,x$27) in -- #138 +//│ x$28 -- #137 +//│ def print(x$29) = +//│ let x$30 = Callable.apply2(builtin,print,x$29) in -- #146 +//│ x$30 -- #145 +//│ def debug(x$31) = +//│ let x$32 = Callable.apply2(builtin,debug,x$31) in -- #154 +//│ x$32 -- #153 +//│ def map(f$0,ls$0) = +//│ case ls$0 of -- #189 +//│ Cons => +//│ let x$34 = Cons.t(ls$0) in -- #185 +//│ let x$35 = Cons.h(ls$0) in -- #184 +//│ let x$36 = Callable.apply1(f$0,x$35) in -- #183 +//│ let* (x$37) = map(f$0,x$34) in -- #182 +//│ let x$38 = Cons(x$36,x$37) in -- #181 +//│ jump j$0(x$38) -- #180 +//│ Nil => +//│ let x$39 = Nil() in -- #188 +//│ jump j$0(x$39) -- #187 +//│ def j$0(x$33) = +//│ x$33 -- #156 +//│ def filter(f_2$0,ls_2$0) = +//│ case ls_2$0 of -- #236 +//│ Cons => +//│ let x$41 = Cons.t(ls_2$0) in -- #232 +//│ let x$42 = Cons.h(ls_2$0) in -- #231 +//│ let x$43 = Callable.apply1(f_2$0,x$42) in -- #230 +//│ if x$43 -- #229 +//│ true => +//│ let* (x$45) = filter(f_2$0,x$41) in -- #220 +//│ let x$46 = Cons(x$42,x$45) in -- #219 +//│ jump j$2(x$46) -- #218 +//│ false => +//│ let* (x$47) = filter(f_2$0,x$41) in -- #228 +//│ jump j$2(x$47) -- #227 +//│ Nil => +//│ let x$48 = Nil() in -- #235 +//│ jump j$1(x$48) -- #234 +//│ def j$1(x$40) = +//│ x$40 -- #191 +//│ def j$2(x$44) = +//│ jump j$1(x$44) -- #206 +//│ def foldl(f_4$0,i$0,ls_4$0) = +//│ case ls_4$0 of -- #268 +//│ Cons => +//│ let x$50 = Cons.t(ls_4$0) in -- #265 +//│ let x$51 = Cons.h(ls_4$0) in -- #264 +//│ let x$52 = Callable.apply2(f_4$0,i$0,x$51) in -- #263 +//│ let* (x$53) = foldl(f_4$0,x$52,x$50) in -- #262 +//│ jump j$3(x$53) -- #261 +//│ Nil => +//│ jump j$3(i$0) -- #267 +//│ def j$3(x$49) = +//│ x$49 -- #238 +//│ def foldr(f_5$0,i_1$0,ls_5$0) = +//│ case ls_5$0 of -- #300 +//│ Cons => +//│ let x$55 = Cons.t(ls_5$0) in -- #297 +//│ let x$56 = Cons.h(ls_5$0) in -- #296 +//│ let* (x$57) = foldr(f_5$0,i_1$0,x$55) in -- #295 +//│ let x$58 = Callable.apply2(f_5$0,x$56,x$57) in -- #294 +//│ jump j$4(x$58) -- #293 +//│ Nil => +//│ jump j$4(i_1$0) -- #299 +//│ def j$4(x$54) = +//│ x$54 -- #270 +//│ def zip(xs$0,ys$0) = +//│ case xs$0 of -- #353 +//│ Cons => +//│ let x$60 = Cons.t(xs$0) in -- #349 +//│ let x$61 = Cons.h(xs$0) in -- #348 +//│ case ys$0 of -- #347 +//│ Cons => +//│ let x$63 = Cons.t(ys$0) in -- #343 +//│ let x$64 = Cons.h(ys$0) in -- #342 +//│ let x$65 = Tuple2(x$61,x$64) in -- #341 +//│ let* (x$66) = zip(x$60,x$63) in -- #340 +//│ let x$67 = Cons(x$65,x$66) in -- #339 +//│ jump j$6(x$67) -- #338 +//│ Nil => +//│ let x$68 = Nil() in -- #346 +//│ jump j$6(x$68) -- #345 +//│ Nil => +//│ let x$69 = Nil() in -- #352 +//│ jump j$5(x$69) -- #351 +//│ def j$5(x$59) = +//│ x$59 -- #302 +//│ def j$6(x$62) = +//│ jump j$5(x$62) -- #313 +//│ def zipWith(f_7$0,xs_4$0,ys_4$0) = +//│ case xs_4$0 of -- #409 +//│ Cons => +//│ let x$71 = Cons.t(xs_4$0) in -- #405 +//│ let x$72 = Cons.h(xs_4$0) in -- #404 +//│ case ys_4$0 of -- #403 +//│ Cons => +//│ let x$74 = Cons.t(ys_4$0) in -- #399 +//│ let x$75 = Cons.h(ys_4$0) in -- #398 +//│ let x$76 = Callable.apply2(f_7$0,x$72,x$75) in -- #397 +//│ let* (x$77) = zipWith(f_7$0,x$71,x$74) in -- #396 +//│ let x$78 = Cons(x$76,x$77) in -- #395 +//│ jump j$8(x$78) -- #394 +//│ Nil => +//│ let x$79 = Nil() in -- #402 +//│ jump j$8(x$79) -- #401 +//│ Nil => +//│ let x$80 = Nil() in -- #408 +//│ jump j$7(x$80) -- #407 +//│ def j$7(x$70) = +//│ x$70 -- #355 +//│ def j$8(x$73) = +//│ jump j$7(x$73) -- #366 +//│ def head(ls_7$0) = +//│ case ls_7$0 of -- #427 +//│ Cons => +//│ let x$82 = Cons.t(ls_7$0) in -- #423 +//│ let x$83 = Cons.h(ls_7$0) in -- #422 +//│ jump j$9(x$83) -- #421 +//│ Nil => +//│ let x$85 = Lambda$0() in -- #426 +//│ jump j$9(x$85) -- #425 +//│ def j$9(x$81) = +//│ x$81 -- #411 +//│ def tail(ls_9$0) = +//│ case ls_9$0 of -- #445 +//│ Cons => +//│ let x$87 = Cons.t(ls_9$0) in -- #441 +//│ let x$88 = Cons.h(ls_9$0) in -- #440 +//│ jump j$10(x$87) -- #439 +//│ Nil => +//│ let x$90 = Lambda$1() in -- #444 +//│ jump j$10(x$90) -- #443 +//│ def j$10(x$86) = +//│ x$86 -- #429 +//│ def enumFromTo(a$0,b$0) = +//│ let x$91 = <=(a$0,b$0) in -- #477 +//│ if x$91 -- #476 +//│ true => +//│ let x$93 = +(a$0,1) in -- #472 +//│ let* (x$94) = enumFromTo(x$93,b$0) in -- #471 +//│ let x$95 = Cons(a$0,x$94) in -- #470 +//│ jump j$11(x$95) -- #469 +//│ false => +//│ let x$96 = Nil() in -- #475 +//│ jump j$11(x$96) -- #474 +//│ def j$11(x$92) = +//│ x$92 -- #452 +//│ def enumFromThenTo(a_1$0,t_11$0,b_1$0) = +//│ let x$97 = <=(a_1$0,b_1$0) in -- #517 +//│ if x$97 -- #516 +//│ true => +//│ let x$99 = *(2,t_11$0) in -- #512 +//│ let x$100 = -(x$99,a_1$0) in -- #511 +//│ let* (x$101) = enumFromThenTo(t_11$0,x$100,b_1$0) in -- #510 +//│ let x$102 = Cons(a_1$0,x$101) in -- #509 +//│ jump j$12(x$102) -- #508 +//│ false => +//│ let x$103 = Nil() in -- #515 +//│ jump j$12(x$103) -- #514 +//│ def j$12(x$98) = +//│ x$98 -- #484 +//│ def take(n$0,ls_11$0) = +//│ let x$104 = >(n$0,0) in -- #566 +//│ if x$104 -- #565 +//│ true => +//│ case ls_11$0 of -- #561 +//│ Cons => +//│ let x$107 = Cons.t(ls_11$0) in -- #557 +//│ let x$108 = Cons.h(ls_11$0) in -- #556 +//│ let x$109 = -(n$0,1) in -- #555 +//│ let* (x$110) = take(x$109,x$107) in -- #554 +//│ let x$111 = Cons(x$108,x$110) in -- #553 +//│ jump j$14(x$111) -- #552 +//│ Nil => +//│ let x$112 = Nil() in -- #560 +//│ jump j$14(x$112) -- #559 +//│ false => +//│ let x$113 = Nil() in -- #564 +//│ jump j$13(x$113) -- #563 +//│ def j$13(x$105) = +//│ x$105 -- #524 +//│ def j$14(x$106) = +//│ jump j$13(x$106) -- #527 +//│ def length(ls_13$0) = +//│ case ls_13$0 of -- #593 +//│ Cons => +//│ let x$115 = Cons.t(ls_13$0) in -- #590 +//│ let x$116 = Cons.h(ls_13$0) in -- #589 +//│ let* (x$117) = length(x$115) in -- #588 +//│ let x$118 = +(1,x$117) in -- #587 +//│ jump j$15(x$118) -- #586 +//│ Nil => +//│ jump j$15(0) -- #592 +//│ def j$15(x$114) = +//│ x$114 -- #568 +//│ def mappend(xs_8$0,ys_8$0) = +//│ case xs_8$0 of -- #622 +//│ Cons => +//│ let x$120 = Cons.t(xs_8$0) in -- #619 +//│ let x$121 = Cons.h(xs_8$0) in -- #618 +//│ let* (x$122) = mappend(x$120,ys_8$0) in -- #617 +//│ let x$123 = Cons(x$121,x$122) in -- #616 +//│ jump j$16(x$123) -- #615 +//│ Nil => +//│ jump j$16(ys_8$0) -- #621 +//│ def j$16(x$119) = +//│ x$119 -- #595 +//│ def sum(ls_14$0) = +//│ let* (x$124) = sumAux(ls_14$0,0) in -- #629 +//│ x$124 -- #628 +//│ def sumAux(ls_15$0,a_4$0) = +//│ case ls_15$0 of -- #658 +//│ Nil => +//│ jump j$17(a_4$0) -- #633 +//│ Cons => +//│ let x$126 = Cons.t(ls_15$0) in -- #657 +//│ let x$127 = Cons.h(ls_15$0) in -- #656 +//│ let x$128 = +(a_4$0,x$127) in -- #655 +//│ let* (x$129) = sumAux(x$126,x$128) in -- #654 +//│ jump j$17(x$129) -- #653 +//│ def j$17(x$125) = +//│ x$125 -- #631 +//│ def atIndex(n_2$0,ls_16$0) = +//│ let x$130 = <(n_2$0,0) in -- #713 +//│ if x$130 -- #712 +//│ true => +//│ let x$133 = Lambda$2() in -- #668 +//│ jump j$18(x$133) -- #667 +//│ false => +//│ case ls_16$0 of -- #711 +//│ Cons => +//│ let x$135 = Cons.t(ls_16$0) in -- #707 +//│ let x$136 = Cons.h(ls_16$0) in -- #706 +//│ let x$137 = ==(n_2$0,0) in -- #705 +//│ if x$137 -- #704 +//│ true => +//│ jump j$20(x$136) -- #689 +//│ false => +//│ let x$139 = -(n_2$0,1) in -- #703 +//│ let* (x$140) = atIndex(x$139,x$135) in -- #702 +//│ jump j$20(x$140) -- #701 +//│ Nil => +//│ let x$142 = Lambda$3() in -- #710 +//│ jump j$19(x$142) -- #709 +//│ def j$18(x$131) = +//│ x$131 -- #665 +//│ def j$19(x$134) = +//│ jump j$18(x$134) -- #671 +//│ def j$20(x$138) = +//│ jump j$19(x$138) -- #687 +//│ def concat(lss$0) = +//│ case lss$0 of -- #741 +//│ Cons => +//│ let x$144 = Cons.t(lss$0) in -- #737 +//│ let x$145 = Cons.h(lss$0) in -- #736 +//│ let* (x$146) = concat(x$144) in -- #735 +//│ let* (x$147) = mappend(x$145,x$146) in -- #734 +//│ jump j$21(x$147) -- #733 +//│ Nil => +//│ let x$148 = Nil() in -- #740 +//│ jump j$21(x$148) -- #739 +//│ def j$21(x$143) = +//│ x$143 -- #715 +//│ def reverse(ls_18$0) = +//│ let x$149 = Nil() in -- #749 +//│ let* (x$150) = reverse_helper(ls_18$0,x$149) in -- #748 +//│ x$150 -- #747 +//│ def reverse_helper(ls_19$0,a_5$0) = +//│ case ls_19$0 of -- #778 +//│ Cons => +//│ let x$152 = Cons.t(ls_19$0) in -- #775 +//│ let x$153 = Cons.h(ls_19$0) in -- #774 +//│ let x$154 = Cons(x$153,a_5$0) in -- #773 +//│ let* (x$155) = reverse_helper(x$152,x$154) in -- #772 +//│ jump j$22(x$155) -- #771 +//│ Nil => +//│ jump j$22(a_5$0) -- #777 +//│ def j$22(x$151) = +//│ x$151 -- #751 +//│ def listcomp_fun1(ms$0,listcomp_fun_para$0) = +//│ case listcomp_fun_para$0 of -- #806 +//│ Cons => +//│ let x$157 = Cons.t(listcomp_fun_para$0) in -- #802 +//│ let x$158 = Cons.h(listcomp_fun_para$0) in -- #801 +//│ let* (x$159) = listcomp_fun2(ms$0,x$158,x$157,ms$0) in -- #800 +//│ jump j$23(x$159) -- #799 +//│ Nil => +//│ let x$160 = Nil() in -- #805 +//│ jump j$23(x$160) -- #804 +//│ def j$23(x$156) = +//│ x$156 -- #780 +//│ def listcomp_fun2(ms$1,listcomp_fun_ls_h_out$0,listcomp_fun_ls_t_out$0,listcomp_fun_para$1) = +//│ case listcomp_fun_para$1 of -- #851 +//│ Cons => +//│ let x$162 = Cons.t(listcomp_fun_para$1) in -- #842 +//│ let x$163 = Cons.h(listcomp_fun_para$1) in -- #841 +//│ let x$164 = Tuple2(listcomp_fun_ls_h_out$0,x$163) in -- #840 +//│ let* (x$165) = listcomp_fun2(ms$1,listcomp_fun_ls_h_out$0,listcomp_fun_ls_t_out$0,x$162) in -- #839 +//│ let x$166 = Cons(x$164,x$165) in -- #838 +//│ jump j$24(x$166) -- #837 +//│ Nil => +//│ let* (x$167) = listcomp_fun1(ms$1,listcomp_fun_ls_t_out$0) in -- #850 +//│ jump j$24(x$167) -- #849 +//│ def j$24(x$161) = +//│ x$161 -- #808 +//│ def test(test_arg1$0) = +//│ let* (x$168) = const5000() in -- #912 +//│ let* (x$169) = const5000() in -- #911 +//│ let* (x$170) = z_add(x$169,test_arg1$0) in -- #910 +//│ let* (x$171) = z_enumFromTo(x$168,x$170) in -- #909 +//│ let* (x$172) = const10000() in -- #908 +//│ let* (x$173) = const10000() in -- #907 +//│ let* (x$174) = z_add(x$173,test_arg1$0) in -- #906 +//│ let* (x$175) = z_enumFromTo(x$172,x$174) in -- #905 +//│ let x$178 = Lambda$4() in -- #904 +//│ let* (x$179) = listcomp_fun1(x$175,x$171) in -- #903 +//│ let* (x$180) = map(x$178,x$179) in -- #902 +//│ let x$183 = Lambda$5() in -- #901 +//│ let* (x$184) = map(x$183,x$180) in -- #900 +//│ let* (x$185) = max'(x$184) in -- #899 +//│ x$185 -- #898 +//│ def const10000() = +//│ let* (x$186) = z_of_int(10000) in -- #917 +//│ x$186 -- #916 +//│ def f1(f1_arg1$0) = +//│ case f1_arg1$0 of -- #946 +//│ Tuple2 => +//│ let x$188 = Tuple2.y(f1_arg1$0) in -- #945 +//│ let x$189 = Tuple2.x(f1_arg1$0) in -- #944 +//│ let* (x$190) = gcdE(x$189,x$188) in -- #943 +//│ let x$191 = Tuple3(x$189,x$188,x$190) in -- #942 +//│ jump j$25(x$191) -- #941 +//│ def j$25(x$187) = +//│ x$187 -- #919 +//│ def quotRem(quotRem_arg1$0,quotRem_arg2$0) = +//│ let* (x$192) = z_div(quotRem_arg1$0,quotRem_arg2$0) in -- #965 +//│ let* (x$193) = z_mod(quotRem_arg1$0,quotRem_arg2$0) in -- #964 +//│ let x$194 = Tuple2(x$192,x$193) in -- #963 +//│ x$194 -- #962 +//│ def max'(max'_arg1$0) = +//│ case max'_arg1$0 of -- #1028 +//│ Cons => +//│ let x$196 = Cons.t(max'_arg1$0) in -- #1027 +//│ let x$197 = Cons.h(max'_arg1$0) in -- #1026 +//│ case x$196 of -- #1025 +//│ Nil => +//│ jump j$27(x$197) -- #980 +//│ Cons => +//│ let x$199 = Cons.t(x$196) in -- #1024 +//│ let x$200 = Cons.h(x$196) in -- #1023 +//│ let* (x$201) = z_lt(x$197,x$200) in -- #1022 +//│ if x$201 -- #1021 +//│ true => +//│ let x$203 = Cons(x$200,x$199) in -- #1008 +//│ let* (x$204) = max'(x$203) in -- #1007 +//│ jump j$28(x$204) -- #1006 +//│ false => +//│ let x$205 = Cons(x$197,x$199) in -- #1020 +//│ let* (x$206) = max'(x$205) in -- #1019 +//│ jump j$28(x$206) -- #1018 +//│ def j$26(x$195) = +//│ x$195 -- #967 +//│ def j$27(x$198) = +//│ jump j$26(x$198) -- #978 +//│ def j$28(x$202) = +//│ jump j$27(x$202) -- #996 +//│ def g(g_arg1$0,g_arg2$0) = +//│ case g_arg1$0 of -- #1156 +//│ Tuple3 => +//│ let x$208 = Tuple3.z(g_arg1$0) in -- #1155 +//│ let x$209 = Tuple3.y(g_arg1$0) in -- #1154 +//│ let x$210 = Tuple3.x(g_arg1$0) in -- #1153 +//│ case g_arg2$0 of -- #1152 +//│ Tuple3 => +//│ let x$212 = Tuple3.z(g_arg2$0) in -- #1151 +//│ let x$213 = Tuple3.y(g_arg2$0) in -- #1150 +//│ let x$214 = Tuple3.x(g_arg2$0) in -- #1149 +//│ let* (x$215) = const0() in -- #1148 +//│ let* (x$216) = z_equal(x$212,x$215) in -- #1147 +//│ if x$216 -- #1146 +//│ true => +//│ let x$218 = Tuple3(x$208,x$210,x$209) in -- #1076 +//│ jump j$31(x$218) -- #1075 +//│ false => +//│ let* (x$219) = quotRem(x$208,x$212) in -- #1145 +//│ case x$219 of -- #1144 +//│ Tuple2 => +//│ let x$221 = Tuple2.y(x$219) in -- #1143 +//│ let x$222 = Tuple2.x(x$219) in -- #1142 +//│ let x$223 = Tuple3(x$214,x$213,x$212) in -- #1141 +//│ let* (x$224) = z_mul(x$222,x$214) in -- #1140 +//│ let* (x$225) = z_sub(x$210,x$224) in -- #1139 +//│ let* (x$226) = z_mul(x$222,x$213) in -- #1138 +//│ let* (x$227) = z_sub(x$209,x$226) in -- #1137 +//│ let x$228 = Tuple3(x$225,x$227,x$221) in -- #1136 +//│ let* (x$229) = g(x$223,x$228) in -- #1135 +//│ jump j$32(x$229) -- #1134 +//│ def j$29(x$207) = +//│ x$207 -- #1030 +//│ def j$30(x$211) = +//│ jump j$29(x$211) -- #1045 +//│ def j$31(x$217) = +//│ jump j$30(x$217) -- #1066 +//│ def j$32(x$220) = +//│ jump j$31(x$220) -- #1085 +//│ def abs(abs_arg1$0) = +//│ let* (x$230) = const0() in -- #1179 +//│ let* (x$231) = z_lt(abs_arg1$0,x$230) in -- #1178 +//│ if x$231 -- #1177 +//│ true => +//│ let* (x$233) = const0() in -- #1174 +//│ let* (x$234) = z_sub(x$233,abs_arg1$0) in -- #1173 +//│ jump j$33(x$234) -- #1172 +//│ false => +//│ jump j$33(abs_arg1$0) -- #1176 +//│ def j$33(x$232) = +//│ x$232 -- #1164 +//│ def f2(f2_arg1$0) = +//│ case f2_arg1$0 of -- #1234 +//│ Tuple3 => +//│ let x$236 = Tuple3.z(f2_arg1$0) in -- #1233 +//│ let x$237 = Tuple3.y(f2_arg1$0) in -- #1232 +//│ let x$238 = Tuple3.x(f2_arg1$0) in -- #1231 +//│ case x$236 of -- #1230 +//│ Tuple3 => +//│ let x$240 = Tuple3.z(x$236) in -- #1229 +//│ let x$241 = Tuple3.y(x$236) in -- #1228 +//│ let x$242 = Tuple3.x(x$236) in -- #1227 +//│ let* (x$243) = z_add(x$242,x$241) in -- #1226 +//│ let* (x$244) = z_add(x$243,x$240) in -- #1225 +//│ let* (x$245) = abs(x$244) in -- #1224 +//│ jump j$35(x$245) -- #1223 +//│ def j$34(x$235) = +//│ x$235 -- #1181 +//│ def j$35(x$239) = +//│ jump j$34(x$239) -- #1196 +//│ def const0() = +//│ let* (x$246) = z_of_int(0) in -- #1239 +//│ x$246 -- #1238 +//│ def gcdE(gcdE_arg1$0,gcdE_arg2$0) = +//│ let* (x$247) = const0() in -- #1296 +//│ let* (x$248) = z_equal(gcdE_arg1$0,x$247) in -- #1295 +//│ if x$248 -- #1294 +//│ true => +//│ let* (x$250) = const0() in -- #1261 +//│ let* (x$251) = const1() in -- #1260 +//│ let x$252 = Tuple3(gcdE_arg2$0,x$250,x$251) in -- #1259 +//│ jump j$36(x$252) -- #1258 +//│ false => +//│ let* (x$253) = const1() in -- #1293 +//│ let* (x$254) = const0() in -- #1292 +//│ let x$255 = Tuple3(x$253,x$254,gcdE_arg1$0) in -- #1291 +//│ let* (x$256) = const0() in -- #1290 +//│ let* (x$257) = const1() in -- #1289 +//│ let x$258 = Tuple3(x$256,x$257,gcdE_arg2$0) in -- #1288 +//│ let* (x$259) = g(x$255,x$258) in -- #1287 +//│ jump j$36(x$259) -- #1286 +//│ def j$36(x$249) = +//│ x$249 -- #1247 +//│ def const1() = +//│ let* (x$260) = z_of_int(1) in -- #1301 +//│ x$260 -- #1300 +//│ def const5000() = +//│ let* (x$261) = z_of_int(5000) in -- #1306 +//│ x$261 -- #1305 +//│ def testGcd_nofib(testGcd_nofib_arg1$0) = +//│ let* (x$262) = test(testGcd_nofib_arg1$0) in -- #1311 +//│ x$262 -- #1310 +//│ def z_enumFromTo(z_enumFromTo_arg1$0,z_enumFromTo_arg2$0) = +//│ let* (x$263) = z_leq(z_enumFromTo_arg1$0,z_enumFromTo_arg2$0) in -- #1345 +//│ if x$263 -- #1344 +//│ true => +//│ let* (x$265) = const1() in -- #1340 +//│ let* (x$266) = z_add(z_enumFromTo_arg1$0,x$265) in -- #1339 +//│ let* (x$267) = z_enumFromTo(x$266,z_enumFromTo_arg2$0) in -- #1338 +//│ let x$268 = Cons(z_enumFromTo_arg1$0,x$267) in -- #1337 +//│ jump j$37(x$268) -- #1336 +//│ false => +//│ let x$269 = Nil() in -- #1343 +//│ jump j$37(x$269) -- #1342 +//│ def j$37(x$264) = +//│ x$264 -- #1318 +//│ let* (x$0) = z_of_int(400) in -- #8 +//│ let* (x$1) = testGcd_nofib(x$0) in -- #7 +//│ x$1 -- #6 +//│ +//│ +//│ Execution succeeded: +//│ Z(5201) +//│ diff --git a/compiler/shared/test/scala/mlscript/compiler/Test.scala b/compiler/shared/test/scala/mlscript/compiler/Test.scala index 752dc909..343de6b9 100644 --- a/compiler/shared/test/scala/mlscript/compiler/Test.scala +++ b/compiler/shared/test/scala/mlscript/compiler/Test.scala @@ -1,7 +1,7 @@ package mlscript package compiler -import utils.shorthands.* +import mlscript.utils.shorthands._ import scala.util.control.NonFatal import scala.collection.mutable.StringBuilder import mlscript.compiler.TreeDebug diff --git a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala index 565769be..29f74f71 100644 --- a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala +++ b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala @@ -1,63 +1,83 @@ -package mlscript -package compiler +package mlscript.compiler + import mlscript.utils.shorthands._ import mlscript.compiler.ir._ -import scala.collection.mutable.StringBuilder +import scala.collection.mutable.{ListBuffer, StringBuilder} +import mlscript.Statement +import mlscript.{DiffTests, ModeType, TypingUnit} +import mlscript.compiler.ir.{Fresh, FreshInt, Builder} +import mlscript.compiler.codegen.cpp._ +import mlscript.Diagnostic import mlscript.compiler.optimizer.TailRecOpt import IRDiffTestCompiler.* class IRDiffTestCompiler extends DiffTests(State) { + def printToFile(f: java.io.File)(op: java.io.PrintWriter => Unit) = { + val p = new java.io.PrintWriter(f) + try { op(p) } finally { p.close() } + } + + val preludeSource = ListBuffer[Statement]() - override def postProcess(mode: ModeType, basePath: List[Str], testName: Str, unit: TypingUnit, output: Str => Unit, raise: Diagnostic => Unit): (List[Str], Option[TypingUnit]) = + override def postProcess(mode: ModeType, basePath: List[Str], testName: Str, originalUnit: TypingUnit, output: Str => Unit, raise: Diagnostic => Unit): (List[Str], Option[TypingUnit]) = val outputBuilder = StringBuilder() - if (mode.useIR || mode.irVerbose) + if (mode.prelude) + preludeSource.addAll(originalUnit.rawEntities) + output("\nPreluded.") + else if (mode.useIR || mode.irVerbose) try - val fnUid = FreshInt() - val classUid = FreshInt() - val tag = FreshInt() - - val gb = Builder(Fresh(), fnUid, classUid, tag, raise) - val graph_ = gb.buildGraph(unit) - - if !mode.noTailRecOpt then - output("\nIR:") - output(graph_.toString()) - - val graph = - if !mode.noTailRecOpt then - val tailRecOpt = new TailRecOpt(fnUid, classUid, tag, raise) - val (g, comps) = tailRecOpt.run_debug(graph_) - output("\nStrongly Connected Tail Calls:") - output(comps.toString) - g - else - graph_ + val (fresh, freshFnId, freshClassId, freshTag) = (Fresh(), FreshInt(), FreshInt(), FreshInt()) + val gb = Builder(fresh, freshFnId, freshClassId, freshTag, raise, mode.irVerbose) + val prelude = TypingUnit(preludeSource.toList) + var graph = gb.buildGraph(prelude, originalUnit) + val hiddenNames = gb.getHiddenNames(prelude) + output("\n\nIR:") + output(graph.show(hiddenNames)) if !mode.noTailRecOpt then - output(graph.toString()) - - output("\nPromoted:") - output(graph.toString()) + val tailRecOpt = new TailRecOpt(freshFnId, freshClassId, freshTag, raise) + val (g, comps) = tailRecOpt.run_debug(graph) + output("\n\nStrongly Connected Tail Calls:") + output(comps.toString) + graph = g + output(graph.show(hiddenNames)) var interp_result: Opt[Str] = None if (mode.interpIR) output("\nInterpreted:") val ir = Interpreter(mode.irVerbose).interpret(graph) interp_result = Some(ir) output(ir) - + if (mode.genCpp) + val cpp = codegen(graph) + if (mode.showCpp) + output("\nCpp:") + if (mode.irVerbose) + output(cpp.toDocument.print) + else + output(cpp.toDocumentWithoutHidden.print) + if (mode.writeCpp) + printToFile(java.io.File(s"compiler/shared/test/diff-ir/cpp/${testName}.cpp")) { p => + p.println(cpp.toDocument.print) + } + if (mode.runCpp) + val auxPath = os.pwd/"compiler"/"shared"/"test"/"diff-ir"/"cpp" + val cppHost = CppCompilerHost(auxPath.toString) + if !cppHost.ready then + output("\nCpp Compilation Failed: Cpp compiler or GNU Make not found") + else + output("\n") + cppHost.compileAndRun(cpp.toDocument.print, output) catch case err: Exception => output(s"\nIR Processing Failed: ${err.getMessage()}") - if (mode.irVerbose) then - output("\n" ++ err.getStackTrace().map(_.toString()).mkString("\n")) + output("\n" ++ err.getStackTrace().mkString("\n")) case err: StackOverflowError => output(s"\nIR Processing Failed: ${err.getMessage()}") - if (mode.irVerbose) then - output("\n" ++ err.getStackTrace().map(_.toString()).mkString("\n")) + output("\n" ++ err.getStackTrace().mkString("\n")) - (outputBuilder.toString().linesIterator.toList, None) + (outputBuilder.toString.linesIterator.toList, None) } diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..f4f8a7fa --- /dev/null +++ b/flake.lock @@ -0,0 +1,97 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1723256269, + "narHash": "sha256-9jxxtPKq4n+F8+BPt706gtbdpDt8+DtmY8oDMi9Vh9Q=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "36d2587498cbb61b129149fb9050361d73e1f7c4", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "sbt-deriv": "sbt-deriv" + } + }, + "sbt-deriv": { + "inputs": { + "flake-utils": "flake-utils_2", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1698464090, + "narHash": "sha256-Pnej7WZIPomYWg8f/CZ65sfW85IfIUjYhphMMg7/LT0=", + "owner": "zaninime", + "repo": "sbt-derivation", + "rev": "6762cf2c31de50efd9ff905cbcc87239995a4ef9", + "type": "github" + }, + "original": { + "owner": "zaninime", + "repo": "sbt-derivation", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..5619a841 --- /dev/null +++ b/flake.nix @@ -0,0 +1,34 @@ +{ + description = "mlscript"; + + inputs.nixpkgs.url = "github:NixOS/nixpkgs"; + inputs.flake-utils.url = "github:numtide/flake-utils"; + inputs.sbt-deriv.url = "github:zaninime/sbt-derivation"; + inputs.sbt-deriv.inputs.nixpkgs.follows = "nixpkgs"; + + outputs = { self, nixpkgs, flake-utils, sbt-deriv }: + flake-utils.lib.eachDefaultSystem + (system: + let + sbtOverlay = self: super: { + sbt = super.sbt.override { jre = super.jdk8_headless; }; + }; + pkgs = import nixpkgs { + inherit system; + overlays = [ sbtOverlay ]; + }; + in with pkgs; { + devShells.default = mkShell { + buildInputs = [ + clang + gcc + gnumake + boost + gmp + mimalloc + sbt + nodejs_22 + ]; + }; + }); +} \ No newline at end of file diff --git a/project/build.properties b/project/build.properties index e8a1e246..ee4c672c 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.9.7 +sbt.version=1.10.1 diff --git a/project/plugins.sbt b/project/plugins.sbt index 3d01a254..6012dc30 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,3 +1,3 @@ -addSbtPlugin("org.wartremover" % "sbt-wartremover" % "3.1.5") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.11.0") +addSbtPlugin("org.wartremover" % "sbt-wartremover" % "3.2.0") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0") addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.2.0") diff --git a/shared/src/main/scala/mlscript/NewLexer.scala b/shared/src/main/scala/mlscript/NewLexer.scala index 0c6bc7d8..a42ffb7c 100644 --- a/shared/src/main/scala/mlscript/NewLexer.scala +++ b/shared/src/main/scala/mlscript/NewLexer.scala @@ -161,6 +161,60 @@ class NewLexer(origin: Origin, raise: Diagnostic => Unit, dbg: Bool) { } } + final + def char(i: Int): (Char, Int) = { + if (i < length) { + bytes(i) match { + case '\\' => { + val j = i + 1 + if (j < length) + bytes(j) match { + case 'n' => ('\n', j + 1) + case 't' => ('\t', j + 1) + case 'r' => ('\r', j + 1) + case 'b' => ('\b', j + 1) + case 'f' => ('\f', j + 1) + case '\'' => ('\'', j + 1) + case '"' => ('"', j + 1) + case '\\' => ('\\', j + 1) + case ch => + raise(ErrorReport(msg"Invalid escape character" -> S(loc(j, j + 1)) :: Nil, + newDefs = true, source = Lexing)) + ('\u0000', j + 1) + } + else { + raise(ErrorReport(msg"Expect an escape character" -> S(loc(i, i + 1)) :: Nil, + newDefs = true, source = Lexing)) + ('\u0000', i + 1) + } + } + case '\n' | '\r' => + raise(ErrorReport(msg"Unexpected newline in a char literal" -> S(loc(i, i + 1)) :: Nil, + newDefs = true, source = Lexing)) + ('\u0000', i + 1) + case '\"' => + raise(ErrorReport(msg"Empty character literal" -> S(loc(i, i + 1)) :: Nil, + newDefs = true, source = Lexing)) + ('\u0000', i + 1) + case ch => + (ch, i + 1) + } + } + else { + raise(ErrorReport(msg"Expect a character literal" -> S(loc(i, i + 1)) :: Nil, + newDefs = true, source = Lexing)) + ('\u0000', i) + } + } + + final def closeChar(i: Int): Int = + if (bytes.lift(i) === Some('\"')) i + 1 + else { + raise(ErrorReport(msg"Unclosed character literal" -> S(loc(i, i + 1)) :: Nil, + newDefs = true, source = Lexing)) + i + } + // * Check the end of a string (either single quotation or triple quotation) final def closeStr(i: Int, isTriple: Bool): Int = if (!isTriple && bytes.lift(i) === Some('"')) i + 1 diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index 8fb9ad1c..5e3076d2 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -43,12 +43,17 @@ abstract class ModeType { def showRepl: Bool def allowEscape: Bool def useIR: Bool - def noTailRecOpt: Bool def interpIR: Bool def irVerbose: Bool + def genCpp: Bool + def showCpp: Bool + def runCpp: Bool + def writeCpp: Bool + def noTailRecOpt: Bool def simpledef: Bool def lift: Bool def nolift: Bool + def prelude: Bool } class DiffTests(state: DiffTests.State) @@ -185,10 +190,17 @@ class DiffTests(state: DiffTests.State) lift: Bool = false, nolift: Bool = false, // noProvs: Bool = false, - noTailRecOpt: Bool = false, useIR: Bool = false, interpIR: Bool = false, irVerbose: Bool = false, + irOpt: Bool = false, + irOptFuel: Int = 10, + genCpp: Bool = false, + showCpp: Bool = false, + runCpp: Bool = false, + writeCpp: Bool = false, + noTailRecOpt: Bool = false, + prelude: Bool = false, ) extends ModeType { def isDebugging: Bool = dbg || dbgSimplif } @@ -319,6 +331,11 @@ class DiffTests(state: DiffTests.State) case "useIR" => mode.copy(useIR = true) case "interpIR" => mode.copy(interpIR = true) case "irVerbose" => mode.copy(irVerbose = true) + case "genCpp" => mode.copy(genCpp = true) + case "showCpp" => mode.copy(showCpp = true) + case "runCpp" => mode.copy(runCpp = true) + case "writeCpp" => mode.copy(writeCpp = true) + case "prelude" => mode.copy(prelude = true) case _ => failures += allLines.size - lines.size output("/!\\ Unrecognized option " + line) @@ -485,13 +502,13 @@ class DiffTests(state: DiffTests.State) if (mode.showParse) output(s"AST: $res") + val newMode = if (useIR) { mode.copy(useIR = true) } else mode val newNewMode = if (noTailRec) { newMode.copy(noTailRecOpt = true) } else newMode - val (postLines, nuRes) = - postProcess(newNewMode, basePath, testName, res, output, raise) - postLines.foreach(output) - + val (postLines, nuRes) = postProcess(newNewMode, basePath, testName, res, output, raise) + postLines.foreach(output) + if (parseOnly) Success(Pgrm(Nil), 0) else if (mode.lift) { From 5841627c91e3fe246cf33fb31ae491ec437a654e Mon Sep 17 00:00:00 2001 From: auht <101095686+auht@users.noreply.github.com> Date: Fri, 4 Oct 2024 22:45:15 +0800 Subject: [PATCH 142/143] Constraint solving for function overloading (#213) Co-authored-by: Lionel Parreaux --- .../scala/mlscript/compiler/ClassLifter.scala | 16 +- .../scala/mlscript/ConstraintSolver.scala | 88 ++++ .../src/main/scala/mlscript/NuTypeDefs.scala | 14 +- .../main/scala/mlscript/TypeSimplifier.scala | 48 ++- shared/src/main/scala/mlscript/Typer.scala | 44 +- .../main/scala/mlscript/TyperDatatypes.scala | 118 ++++- .../main/scala/mlscript/TyperHelpers.scala | 48 +-- .../codegen/typescript/TsTypegen.scala | 2 +- shared/src/main/scala/mlscript/helpers.scala | 14 +- shared/src/main/scala/mlscript/syntax.scala | 2 +- shared/src/test/diff/fcp/Overloads.mls | 63 ++- .../src/test/diff/fcp/Overloads_Precise.mls | 197 +++++++++ shared/src/test/diff/nu/HeungTung.mls | 402 +++++++++++++++++- .../src/test/scala/mlscript/DiffTests.scala | 5 +- 14 files changed, 969 insertions(+), 92 deletions(-) create mode 100644 shared/src/test/diff/fcp/Overloads_Precise.mls diff --git a/compiler/shared/main/scala/mlscript/compiler/ClassLifter.scala b/compiler/shared/main/scala/mlscript/compiler/ClassLifter.scala index 533ab83b..b6aa7bcc 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ClassLifter.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ClassLifter.scala @@ -510,7 +510,7 @@ class ClassLifter(logDebugMsg: Boolean = false) { val nlhs = liftType(lb) val nrhs = liftType(ub) Bounds(nlhs._1, nrhs._1) -> (nlhs._2 ++ nrhs._2) - case Constrained(base: Type, bounds, where) => + case Constrained(base: Type, bounds, where, tscs) => val (nTargs, nCtx) = bounds.map { case (tv, Bounds(lb, ub)) => val nlhs = liftType(lb) val nrhs = liftType(ub) @@ -521,10 +521,18 @@ class ClassLifter(logDebugMsg: Boolean = false) { val nrhs = liftType(ub) Bounds(nlhs._1, nrhs._1) -> (nlhs._2 ++ nrhs._2) }.unzip + val (tscs0, nCtx3) = tscs.map { case (tvs, cs) => + val (ntvs,c0) = tvs.map { case (p,v) => + val (nv, c) = liftType(v) + (p,nv) -> c + }.unzip + val (ncs,c1) = cs.map(_.map(liftType).unzip).unzip + (ntvs,ncs) -> (c0 ++ c1.flatten) + }.unzip val (nBase, bCtx) = liftType(base) - Constrained(nBase, nTargs, bounds2) -> - ((nCtx ++ nCtx2).fold(emptyCtx)(_ ++ _) ++ bCtx) - case Constrained(_, _, _) => die + Constrained(nBase, nTargs, bounds2, tscs0) -> + ((nCtx ++ nCtx2 ++ nCtx3.flatten).fold(emptyCtx)(_ ++ _) ++ bCtx) + case Constrained(_, _, _, _) => die case Function(lhs, rhs) => val nlhs = liftType(lhs) val nrhs = liftType(rhs) diff --git a/shared/src/main/scala/mlscript/ConstraintSolver.scala b/shared/src/main/scala/mlscript/ConstraintSolver.scala index 54b7e912..5d60ac1f 100644 --- a/shared/src/main/scala/mlscript/ConstraintSolver.scala +++ b/shared/src/main/scala/mlscript/ConstraintSolver.scala @@ -627,6 +627,35 @@ class ConstraintSolver extends NormalForms { self: Typer => recLb(ar.inner, b.inner) rec(b.inner.ub, ar.inner.ub, false) case (LhsRefined(S(b: ArrayBase), ts, r, _), _) => reportError() + case (LhsRefined(S(ov: Overload), ts, r, trs), RhsBases(_, S(L(f: FunctionType)), _)) if noApproximateOverload => + TupleSetConstraints.mk(ov, f) match { + case S(tsc) => + if (tsc.tvs.nonEmpty) { + tsc.tvs.mapValuesIter(_.unwrapProxies).zipWithIndex.flatMap { + case ((true, tv: TV), i) => tv.lowerBounds.iterator.map((_,tv,i,true)) + case ((false, tv: TV), i) => tv.upperBounds.iterator.map((_,tv,i,false)) + case _ => Nil + }.find { + case (b,_,i,_) => + tsc.updateImpl(i,b) + tsc.constraints.isEmpty + }.foreach { + case (b,tv,_,p) => if (p) rec(b,tv,false) else rec(tv,b,false) + } + if (tsc.constraints.sizeCompare(1) === 0) { + tsc.tvs.values.map(_.unwrapProxies).foreach { + case tv: TV => tv.tsc.remove(tsc) + case _ => () + } + tsc.constraints.head.iterator.zip(tsc.tvs).foreach { + case (c, (pol, t)) => + if (!pol) rec(c, t, false) + if (pol) rec(t, c, false) + } + } + } + case N => reportError(S(msg"is not an instance of `${f.expNeg}`")) + } case (LhsRefined(S(ov: Overload), ts, r, trs), _) => annoying(Nil, LhsRefined(S(ov.approximatePos), ts, r, trs), Nil, done_rs) // TODO remove approx. with ambiguous constraints case (LhsRefined(S(Without(b, ns)), ts, r, _), RhsBases(pts, N | S(L(_)), _)) => @@ -832,6 +861,28 @@ class ConstraintSolver extends NormalForms { self: Typer => val newBound = (cctx._1 ::: cctx._2.reverse).foldRight(rhs)((c, ty) => if (c.prov is noProv) ty else mkProxy(ty, c.prov)) lhs.upperBounds ::= newBound // update the bound + if (noApproximateOverload) { + lhs.tsc.foreachEntry { (tsc, v) => + v.foreach { i => + if (!tsc.tvs(i)._1) { + tsc.updateOn(i, rhs) + if (tsc.constraints.isEmpty) reportError() + } + } + } + val u = lhs.tsc.keysIterator.filter(_.constraints.sizeCompare(1)===0).duplicate + u._1.foreach { k => + k.tvs.mapValuesIter(_.unwrapProxies).foreach { + case (_,tv: TV) => tv.tsc.remove(k) + case _ => () + } + } + u._2.foreach { k => + k.constraints.head.iterator.zip(k.tvs).foreach { + case (c, (pol, t)) => if (pol) rec(t, c, false) else rec(c, t, false) + } + } + } lhs.lowerBounds.foreach(rec(_, rhs, true)) // propagate from the bound case (lhs, rhs: TypeVariable) if lhs.level <= rhs.level => @@ -839,6 +890,28 @@ class ConstraintSolver extends NormalForms { self: Typer => val newBound = (cctx._1 ::: cctx._2.reverse).foldLeft(lhs)((ty, c) => if (c.prov is noProv) ty else mkProxy(ty, c.prov)) rhs.lowerBounds ::= newBound // update the bound + if (noApproximateOverload) { + rhs.tsc.foreachEntry { (tsc, v) => + v.foreach { i => + if(tsc.tvs(i)._1) { + tsc.updateOn(i, lhs) + if (tsc.constraints.isEmpty) reportError() + } + } + } + val u = rhs.tsc.keysIterator.filter(_.constraints.sizeCompare(1)===0).duplicate + u._1.foreach { k => + k.tvs.mapValuesIter(_.unwrapProxies).foreach { + case (_,tv: TV) => tv.tsc.remove(k) + case _ => () + } + } + u._2.foreach { k => + k.constraints.head.iterator.zip(k.tvs).foreach { + case (c, (pol, t)) => if (pol) rec(t, c, false) else rec(c, t, false) + } + } + } rhs.upperBounds.foreach(rec(lhs, _, true)) // propagate from the bound @@ -1562,9 +1635,24 @@ class ConstraintSolver extends NormalForms { self: Typer => assert(lvl <= below, "this condition should be false for the result to be correct") lvl }) + val freshentsc = tv.tsc.flatMap { case (tsc,_) => + if (tsc.tvs.values.map(_.unwrapProxies).forall { + case tv: TV => !freshened.contains(tv) + case _ => true + }) S(tsc) else N + } freshened += tv -> v v.lowerBounds = tv.lowerBounds.mapConserve(freshen) v.upperBounds = tv.upperBounds.mapConserve(freshen) + freshentsc.foreach { tsc => + val t = new TupleSetConstraints(tsc.constraints, tsc.tvs) + t.constraints = t.constraints.map(_.map(freshen)) + t.tvs = t.tvs.map(x => (x._1,freshen(x._2))) + t.tvs.values.map(_.unwrapProxies).zipWithIndex.foreach { + case (tv: TV, i) => tv.tsc.updateWith(t)(_.map(_ + i).orElse(S(Set(i)))) + case _ => () + } + } v } diff --git a/shared/src/main/scala/mlscript/NuTypeDefs.scala b/shared/src/main/scala/mlscript/NuTypeDefs.scala index 3470c680..45a19986 100644 --- a/shared/src/main/scala/mlscript/NuTypeDefs.scala +++ b/shared/src/main/scala/mlscript/NuTypeDefs.scala @@ -1165,7 +1165,19 @@ class NuTypeDefs extends ConstraintSolver { self: Typer => ctx.nextLevel { implicit ctx: Ctx => assert(fd.tparams.sizeCompare(tparamsSkolems) === 0, (fd.tparams, tparamsSkolems)) vars ++ tparamsSkolems |> { implicit vars => - typeTerm(body) + val ty = typeTerm(body) + if (noApproximateOverload) { + val ambiguous = ty.getVars.unsorted.flatMap(_.tsc.keys.flatMap(_.tvs)) + .groupBy(_._2) + .filter { case (v,pvs) => pvs.sizeIs > 1 } + if (ambiguous.nonEmpty) raise(ErrorReport( + msg"ambiguous" -> N :: + ambiguous.map { case (v,_) => + msg"cannot determine satisfiability of type ${v.expPos}" -> v.prov.loco + }.toList + , true)) + } + ty } } } else { diff --git a/shared/src/main/scala/mlscript/TypeSimplifier.scala b/shared/src/main/scala/mlscript/TypeSimplifier.scala index f444498d..f5a0bd81 100644 --- a/shared/src/main/scala/mlscript/TypeSimplifier.scala +++ b/shared/src/main/scala/mlscript/TypeSimplifier.scala @@ -25,6 +25,7 @@ trait TypeSimplifier { self: Typer => println(s"allVarPols: ${printPols(allVarPols)}") val renewed = MutMap.empty[TypeVariable, TypeVariable] + val renewedtsc = MutMap.empty[TupleSetConstraints, TupleSetConstraints] def renew(tv: TypeVariable): TypeVariable = renewed.getOrElseUpdate(tv, @@ -78,8 +79,17 @@ trait TypeSimplifier { self: Typer => ).map(process(_, S(false -> tv))) .reduceOption(_ &- _).filterNot(_.isTop).toList else Nil + if (noApproximateOverload) + nv.tsc ++= tv.tsc.iterator.map { case (tsc, i) => renewedtsc.get(tsc) match { + case S(tsc) => (tsc, i) + case N if inPlace => (tsc, i) + case N => + val t = new TupleSetConstraints(tsc.constraints, tsc.tvs) + renewedtsc += tsc -> t + t.tvs = t.tvs.map(x => (x._1, process(x._2, N))) + (t, i) + }} } - nv case ComposedType(true, l, r) => @@ -549,9 +559,17 @@ trait TypeSimplifier { self: Typer => analyzed1.setAndIfUnset(tv -> pol(tv).getOrElse(false)) { apply(pol)(ty) } case N => if (pol(tv) =/= S(false)) - analyzed1.setAndIfUnset(tv -> true) { tv.lowerBounds.foreach(apply(pol.at(tv.level, true))) } + analyzed1.setAndIfUnset(tv -> true) { + tv.lowerBounds.foreach(apply(pol.at(tv.level, true))) + if (noApproximateOverload) + tv.tsc.keys.flatMap(_.tvs).foreach(u => apply(pol.at(tv.level,u._1))(u._2)) + } if (pol(tv) =/= S(true)) - analyzed1.setAndIfUnset(tv -> false) { tv.upperBounds.foreach(apply(pol.at(tv.level, false))) } + analyzed1.setAndIfUnset(tv -> false) { + tv.upperBounds.foreach(apply(pol.at(tv.level, false))) + if (noApproximateOverload) + tv.tsc.keys.flatMap(_.tvs).foreach(u => apply(pol.at(tv.level,u._1))(u._2)) + } } case _ => super.apply(pol)(st) @@ -643,8 +661,11 @@ trait TypeSimplifier { self: Typer => case tv: TypeVariable => pol(tv) match { case S(pol_tv) => - if (analyzed2.add(pol_tv -> tv)) + if (analyzed2.add(pol_tv -> tv)) { processImpl(st, pol, pol_tv) + if (noApproximateOverload) + tv.tsc.keys.flatMap(_.tvs).foreach(u => processImpl(u._2,pol.at(tv.level,u._1),pol_tv)) + } case N => if (analyzed2.add(true -> tv)) // * To compute the positive co-occurrences @@ -690,6 +711,7 @@ trait TypeSimplifier { self: Typer => case S(p) => (if (p) tv2.lowerBounds else tv2.upperBounds).foreach(go) // (if (p) getLbs(tv2) else getUbs(tv2)).foreach(go) + if (noApproximateOverload) tv2.tsc.keys.flatMap(_.tvs).foreach(u => go(u._2)) case N => trace(s"Analyzing invar-occ of $tv2") { analyze2(tv2, pol) @@ -789,7 +811,7 @@ trait TypeSimplifier { self: Typer => // * Remove variables that are 'dominated' by another type or variable // * A variable v dominated by T if T is in both of v's positive and negative cooccurrences - allVars.foreach { case v => if (v.assignedTo.isEmpty && !varSubst.contains(v)) { + allVars.foreach { case v => if (v.assignedTo.isEmpty && !varSubst.contains(v) && v.tsc.isEmpty) { println(s"2[v] $v ${coOccurrences.get(true -> v)} ${coOccurrences.get(false -> v)}") coOccurrences.get(true -> v).iterator.flatMap(_.iterator).foreach { @@ -807,6 +829,7 @@ trait TypeSimplifier { self: Typer => case w: TV if !(w is v) && !varSubst.contains(w) && !varSubst.contains(v) && !recVars(v) && coOccurrences.get(false -> v).exists(_(w)) + && w.tsc.isEmpty => // * Here we know that v is 'dominated' by w, so v can be inlined. // * Note that we don't want to unify the two variables here @@ -833,7 +856,7 @@ trait TypeSimplifier { self: Typer => // * Unify equivalent variables based on polar co-occurrence analysis: allVars.foreach { case v => - if (!v.assignedTo.isDefined && !varSubst.contains(v)) // TODO also handle v.assignedTo.isDefined? + if (!v.assignedTo.isDefined && !varSubst.contains(v) && v.tsc.isEmpty) // TODO also handle v.assignedTo.isDefined? trace(s"3[v] $v +${coOccurrences.get(true -> v).mkString} -${coOccurrences.get(false -> v).mkString}") { def go(pol: Bool): Unit = coOccurrences.get(pol -> v).iterator.flatMap(_.iterator).foreach { @@ -850,6 +873,7 @@ trait TypeSimplifier { self: Typer => ) && (v.level === w.level) // ^ Don't merge variables of differing levels + && w.tsc.isEmpty => trace(s"[w] $w ${printPol(S(pol))}${coOccurrences.get(pol -> w).mkString}") { @@ -923,6 +947,7 @@ trait TypeSimplifier { self: Typer => println(s"[rec] ${recVars}") val renewals = MutMap.empty[TypeVariable, TypeVariable] + val renewaltsc = MutMap.empty[TupleSetConstraints, TupleSetConstraints] val semp = Set.empty[TV] @@ -999,7 +1024,7 @@ trait TypeSimplifier { self: Typer => nv }) pol(tv) match { - case S(p) if inlineBounds && !occursInvariantly(tv) && !recVars.contains(tv) => + case S(p) if inlineBounds && !occursInvariantly(tv) && !recVars.contains(tv) && tv.tsc.isEmpty => // * Inline the bounds of non-rec non-invar-occ type variables println(s"Inlining [${printPol(p)}] bounds of $tv (~> $res)") // if (p) mergeTransform(true, pol, tv, Set.single(tv), canDistribForall) | res @@ -1017,6 +1042,15 @@ trait TypeSimplifier { self: Typer => res.lowerBounds = tv.lowerBounds.map(transform(_, pol.at(tv.level, true), Set.single(tv))) if (occNums.contains(false -> tv)) res.upperBounds = tv.upperBounds.map(transform(_, pol.at(tv.level, false), Set.single(tv))) + if (noApproximateOverload) + res.tsc ++= tv.tsc.map { case (tsc, i) => renewaltsc.get(tsc) match { + case S(tsc) => (tsc, i) + case N => + val t = new TupleSetConstraints(tsc.constraints, tsc.tvs) + renewaltsc += tsc -> t + t.tvs = t.tvs.map(x => (x._1, transform(x._2, PolMap.neu, Set.empty))) + (t, i) + }} } res }() diff --git a/shared/src/main/scala/mlscript/Typer.scala b/shared/src/main/scala/mlscript/Typer.scala index ad79b34a..af88e803 100644 --- a/shared/src/main/scala/mlscript/Typer.scala +++ b/shared/src/main/scala/mlscript/Typer.scala @@ -34,6 +34,7 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne var constrainedTypes: Boolean = false var recordProvenances: Boolean = true + var noApproximateOverload: Boolean = false type Binding = Str -> SimpleType type Bindings = Map[Str, SimpleType] @@ -168,7 +169,17 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne assert(b.level > lvl) if (p) (b, tv) else (tv, b) } }.toList, innerTy) - + + if (noApproximateOverload) { + val ambiguous = innerTy.getVars.unsorted.flatMap(_.tsc.keys.flatMap(_.tvs)) + .groupBy(_._2) + .filter { case (v,pvs) => pvs.sizeIs > 1 } + if (ambiguous.nonEmpty) raise(ErrorReport( + msg"ambiguous" -> N :: + ambiguous.map { case (v,_) => msg"cannot determine satisfiability of type ${v.expPos}" -> v.prov.loco }.toList + , true)) + } + println(s"Inferred poly constr: $cty —— where ${cty.showBounds}") val cty_fresh = @@ -662,7 +673,7 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne tv.assignedTo = S(bod) tv case Rem(base, fs) => Without(rec(base), fs.toSortedSet)(tyTp(ty.toLoc, "field removal type")) - case Constrained(base, tvbs, where) => + case Constrained(base, tvbs, where, tscs) => val res = rec(base match { case ty: Type => ty case _ => die @@ -675,6 +686,14 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne constrain(rec(lo), rec(hi))(raise, tp(mergeOptions(lo.toLoc, hi.toLoc)(_ ++ _), "constraint specifiation"), ctx) } + tscs.foreach { case (typevars, constrs) => + val tvs = typevars.map(x => (x._1, rec(x._2))) + val tsc = new TupleSetConstraints(constrs.map(_.map(rec)), tvs) + tvs.values.map(_.unwrapProxies).zipWithIndex.foreach { + case (tv: TV, i) => tv.tsc.updateWith(tsc)(_.map(_ + i).orElse(S(Set(i)))) + case _ => () + } + } res case PolyType(vars, ty) => val oldLvl = ctx.lvl @@ -1860,8 +1879,10 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne val expandType = () var bounds: Ls[TypeVar -> Bounds] = Nil + var tscs: Ls[Ls[(Bool, Type)] -> Ls[Ls[Type]]] = Nil val seenVars = mutable.Set.empty[TV] + val seenTscs = mutable.Set.empty[TupleSetConstraints] def field(ft: FieldType)(implicit ectx: ExpCtx): Field = ft match { case FieldType(S(l: TV), u: TV) if l === u => @@ -1969,6 +1990,14 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne if (l =/= Bot || u =/= Top) bounds ::= nv -> Bounds(l, u) } + tv.tsc.foreachEntry { + case (tsc, i) => + if (seenTscs.add(tsc)) { + val tvs = tsc.tvs.map(x => (x._1,go(x._2))) + val constrs = tsc.constraints.map(_.map(go)) + tscs ::= tvs -> constrs + } + } } nv }) @@ -2018,17 +2047,20 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne case Overload(as) => as.map(go).reduce(Inter) case PolymorphicType(lvl, bod) => val boundsSize = bounds.size + val tscsSize = tscs.size val b = go(bod) // This is not completely correct: if we've already traversed TVs as part of a previous sibling PolymorphicType, // the bounds of these TVs won't be registered again... // FIXME in principle we'd want to compute a transitive closure... val newBounds = bounds.reverseIterator.drop(boundsSize).toBuffer + val newTscs = tscs.reverseIterator.drop(tscsSize).toBuffer val qvars = bod.varsBetween(lvl, MaxLevel).iterator val ftvs = b.freeTypeVariables ++ newBounds.iterator.map(_._1) ++ - newBounds.iterator.flatMap(_._2.freeTypeVariables) + newBounds.iterator.flatMap(_._2.freeTypeVariables) ++ + newTscs.iterator.flatMap(_._1.map(_._2)) val fvars = qvars.filter(tv => ftvs.contains(tv.asTypeVar)) if (fvars.isEmpty) b else PolyType(fvars @@ -2042,7 +2074,7 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne val lbs = groups2.toList val bounds = (ubs.mapValues(_.reduce(_ &- _)) ++ lbs.mapValues(_.reduce(_ | _)).map(_.swap)) val processed = bounds.map { case (lo, hi) => Bounds(go(lo), go(hi)) } - Constrained(go(bod), Nil, processed) + Constrained(go(bod), Nil, processed, Nil) // case DeclType(lvl, info) => @@ -2050,8 +2082,8 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne // }(r => s"~> $r") val res = goLike(st)(new ExpCtx(Map.empty)) - if (bounds.isEmpty) res - else Constrained(res, bounds, Nil) + if (bounds.isEmpty && tscs.isEmpty) res + else Constrained(res, bounds, Nil, tscs) // goLike(st) } diff --git a/shared/src/main/scala/mlscript/TyperDatatypes.scala b/shared/src/main/scala/mlscript/TyperDatatypes.scala index 755ba9bf..17a95fc8 100644 --- a/shared/src/main/scala/mlscript/TyperDatatypes.scala +++ b/shared/src/main/scala/mlscript/TyperDatatypes.scala @@ -1,7 +1,7 @@ package mlscript import scala.collection.mutable -import scala.collection.mutable.{Map => MutMap, Set => MutSet, Buffer} +import scala.collection.mutable.{Map => MutMap, Set => MutSet, Buffer, LinkedHashMap} import scala.collection.immutable.{SortedSet, SortedMap} import scala.util.chaining._ import scala.annotation.tailrec @@ -558,6 +558,8 @@ abstract class TyperDatatypes extends TyperHelpers { Typer: Typer => require(value.forall(_.level <= level)) _assignedTo = value } + + val tsc: LinkedHashMap[TupleSetConstraints, Set[Int]] = LinkedHashMap.empty // * Bounds should always be disregarded when `equatedTo` is defined, as they are then irrelevant: def lowerBounds: List[SimpleType] = { require(assignedTo.isEmpty, this); _lowerBounds } @@ -670,5 +672,117 @@ abstract class TyperDatatypes extends TyperHelpers { Typer: Typer => lazy val underlying: SimpleType = tt.neg() val prov = noProv } - + + class TupleSetConstraints(var constraints: Ls[Ls[ST]], var tvs: Ls[(Bool, ST)]) { + def updateImpl(index: Int, bound: ST)(implicit raise: Raise, ctx: Ctx) : Unit = { + val u0 = constraints.flatMap { c => + TupleSetConstraints.lcg(tvs(index)._1, bound, c(index)).map(tvs.zip(c)++_) + } + val u = u0.map { x => + x.groupMap(_._1)(_._2).map { case (u@(p,_),l) => + (u,l.reduce((x,y) => ComposedType(!p,x,y)(noProv))) + } + } + if (!u.isEmpty) { + tvs.values.map(_.unwrapProxies).foreach { + case tv: TV => tv.tsc += this -> Set.empty + case _ => () + } + tvs = u.flatMap(_.keys).distinct + constraints = tvs.map(x => u.map(_.getOrElse(x,if (x._1) TopType else BotType))).transpose + tvs.values.map(_.unwrapProxies).zipWithIndex.foreach { + case (tv: TV, i) => tv.tsc.updateWith(this)(_.map(_ + i).orElse(S(Set(i)))) + case _ => () + } + } else { + constraints = Nil + } + } + def updateOn(index: Int, bound: ST)(implicit raise: Raise, ctx: Ctx) : Unit = { + updateImpl(index, bound) + println(s"TSC update: $tvs in $constraints") + } + } + object TupleSetConstraints { + def lcgField(pol: Bool, first: FieldType, rest: FieldType)(implicit ctx: Ctx) + : Opt[Ls[(Bool, ST) -> ST]] = { + for { + ubm <- lcg(pol, first.ub, rest.ub) + lbm <- { + if (first.lb.isEmpty && rest.lb.isEmpty) + S(Nil) + else + lcg(!pol, first.lb.getOrElse(BotType), rest.lb.getOrElse(BotType)) + } + } yield { + ubm ++ lbm + } + } + def lcg(pol: Bool, first: ST, rest: ST)(implicit ctx: Ctx) + : Opt[Ls[(Bool, ST) -> ST]] = (first.unwrapProxies, rest.unwrapProxies) match { + case (a, ExtrType(p)) if p =/= pol => S(Nil) + case (a, ComposedType(p,l,r)) if p =/= pol => + for { + lm <- lcg(pol,a,l) + rm <- lcg(pol,a,r) + } yield { + lm ++ rm + } + case (a: TV, b: TV) if a.compare(b) === 0 => S(Nil) + case (a: TV, b) => S(List((pol, first) -> rest)) + case (a, b: TV) => S(List((pol, first) -> rest)) + case (a: FT, b: FT) => lcgFunction(pol, a, b) + case (a: ArrayType, b: ArrayType) => lcgField(pol, a.inner, b.inner) + case (a: TupleType, b: TupleType) if a.fields.sizeCompare(b.fields) === 0 => + val fs = a.fields.map(_._2).zip(b.fields.map(_._2)).map(u => lcgField(pol, u._1, u._2)) + if (!fs.contains(N)) { + S(fs.flatten.reduce(_++_)) + } else N + case (a: TupleType, b: RecordType) if pol => lcg(pol, a.toRecord, b) + case (a: RecordType, b: RecordType) => + val default = FieldType(N, if (pol) TopType else BotType)(noProv) + if (b.fields.map(_._1).forall(a.fields.map(_._1).contains)) { + val u = a.fields.map { + case (v, f) => lcgField(pol, f, b.fields.find(_._1 === v).fold(default)(_._2)) + } + if (!u.contains(N)) { + S(u.flatten.reduce(_++_)) + } else N + } else N + case (a, b) if a === b => S(Nil) + case (a, b) => + val dnf = DNF.mk(MaxLevel, Nil, if (pol) a & b.neg() else b & a.neg(), true) + if (dnf.isBot) + S(Nil) + else if (dnf.cs.forall(c => !(c.vars.isEmpty && c.nvars.isEmpty))) + S(List((pol, first) -> rest)) + else N + } + def lcgFunction(pol: Bool, first: FT, rest: FT)(implicit ctx: Ctx) + : Opt[Ls[(Bool, ST) -> ST]] = { + for { + lm <- lcg(!pol, first.lhs, rest.lhs) + rm <- lcg(pol, first.rhs, rest.rhs) + } yield { + lm ++ rm + } + } + def mk(ov: Overload, f: FT)(implicit raise: Raise, ctx: Ctx): Opt[TupleSetConstraints] = { + val u = ov.alts.flatMap(lcgFunction(false, f, _)).map { x => + x.groupMap(_._1)(_._2).map { case (u@(p,_),l) => + (u,l.reduce((x,y) => ComposedType(!p,x,y)(noProv))) + } + } + if (u.isEmpty) { return N } + val tvs = u.flatMap(_.keys).distinct + val m = tvs.map(x => u.map(_.getOrElse(x,if (x._1) TopType else BotType))) + val tsc = new TupleSetConstraints(m.transpose, tvs) + tvs.values.map(_.unwrapProxies).zipWithIndex.foreach { + case (tv: TV, i) => tv.tsc.updateWith(tsc)(_.map(_ + i).orElse(S(Set(i)))) + case _ => () + } + println(s"TSC mk: ${tsc.tvs} in ${tsc.constraints}") + S(tsc) + } + } } diff --git a/shared/src/main/scala/mlscript/TyperHelpers.scala b/shared/src/main/scala/mlscript/TyperHelpers.scala index 9b967a1d..c4e9c229 100644 --- a/shared/src/main/scala/mlscript/TyperHelpers.scala +++ b/shared/src/main/scala/mlscript/TyperHelpers.scala @@ -695,36 +695,6 @@ abstract class TyperHelpers { Typer: Typer => case _ => this :: Nil } - def childrenPol(pol: Opt[Bool])(implicit ctx: Ctx): List[Opt[Bool] -> SimpleType] = { - def childrenPolField(fld: FieldType): List[Opt[Bool] -> SimpleType] = - fld.lb.map(pol.map(!_) -> _).toList ::: pol -> fld.ub :: Nil - this match { - case tv @ AssignedVariable(ty) => - pol -> ty :: Nil - case tv: TypeVariable => - (if (pol =/= S(false)) tv.lowerBounds.map(S(true) -> _) else Nil) ::: - (if (pol =/= S(true)) tv.upperBounds.map(S(false) -> _) else Nil) - case FunctionType(l, r) => pol.map(!_) -> l :: pol -> r :: Nil - case Overload(as) => as.map(pol -> _) - case ComposedType(_, l, r) => pol -> l :: pol -> r :: Nil - case RecordType(fs) => fs.unzip._2.flatMap(childrenPolField) - case TupleType(fs) => fs.unzip._2.flatMap(childrenPolField) - case ArrayType(fld) => childrenPolField(fld) - case SpliceType(elems) => elems flatMap {case L(l) => pol -> l :: Nil case R(r) => childrenPolField(r)} - case NegType(n) => pol.map(!_) -> n :: Nil - case ExtrType(_) => Nil - case ProxyType(und) => pol -> und :: Nil - // case _: TypeTag => Nil - case _: ObjectTag | _: Extruded => Nil - case SkolemTag(id) => pol -> id :: Nil - case tr: TypeRef => tr.mapTargs(pol)(_ -> _) - case Without(b, ns) => pol -> b :: Nil - case TypeBounds(lb, ub) => S(false) -> lb :: S(true) -> ub :: Nil - case PolymorphicType(_, und) => pol -> und :: Nil - case ConstrainedType(cs, bod) => - cs.flatMap(vbs => S(true) -> vbs._1 :: S(false) -> vbs._2 :: Nil) ::: pol -> bod :: Nil - }} - /** (exclusive, inclusive) */ def varsBetween(lb: Level, ub: Level): Set[TV] = { val res = MutSet.empty[TypeVariable] @@ -789,7 +759,8 @@ abstract class TyperHelpers { Typer: Typer => case tv: TypeVariable => val poltv = pol(tv) (if (poltv =/= S(false)) tv.lowerBounds.map(pol.at(tv.level, true) -> _) else Nil) ::: - (if (poltv =/= S(true)) tv.upperBounds.map(pol.at(tv.level, false) -> _) else Nil) + (if (poltv =/= S(true)) tv.upperBounds.map(pol.at(tv.level, false) -> _) else Nil) ++ + tv.tsc.keys.flatMap(_.tvs).map(u => pol.at(tv.level,u._1) -> u._2) case FunctionType(l, r) => pol.contravar -> l :: pol.covar -> r :: Nil case Overload(as) => as.map(pol -> _) case ComposedType(_, l, r) => pol -> l :: pol -> r :: Nil @@ -946,7 +917,7 @@ abstract class TyperHelpers { Typer: Typer => } def children(includeBounds: Bool): List[SimpleType] = this match { case tv @ AssignedVariable(ty) => if (includeBounds) ty :: Nil else Nil - case tv: TypeVariable => if (includeBounds) tv.lowerBounds ::: tv.upperBounds else Nil + case tv: TypeVariable => if (includeBounds) tv.lowerBounds ::: tv.upperBounds ++ tv.tsc.keys.flatMap(_.tvs.values) else Nil case FunctionType(l, r) => l :: r :: Nil case Overload(as) => as case ComposedType(_, l, r) => l :: r :: Nil @@ -990,8 +961,16 @@ abstract class TyperHelpers { Typer: Typer => case tv => ("\n\t\t" + tv.toString + (if (tv.lowerBounds.isEmpty) "" else " :> " + tv.lowerBounds.mkString(" | ")) + (if (tv.upperBounds.isEmpty) "" else " <: " + tv.upperBounds.mkString(" & "))) - }.mkString - + }.mkString + { + val visited: MutSet[TupleSetConstraints] = MutSet.empty + getVars.iterator.flatMap(_.tsc).map { case (tsc, i) => + if (visited.add(tsc)) + ("\n\t\t[ " + + tsc.tvs.map(t => s"${printPol(t._1)}${t._2}").mkString(", ") + + " ] in { " + tsc.constraints.mkString(", ") + " }") + else "" + }.mkString + } } @@ -1336,6 +1315,7 @@ abstract class TyperHelpers { Typer: Typer => val poltv = pol(tv) if (poltv =/= S(false)) tv.lowerBounds.foreach(apply(pol.at(tv.level, true))) if (poltv =/= S(true)) tv.upperBounds.foreach(apply(pol.at(tv.level, false))) + tv.tsc.keys.flatMap(_.tvs).foreach(u => apply(pol.at(tv.level,u._1))(u._2)) case FunctionType(l, r) => apply(pol.contravar)(l); apply(pol)(r) case Overload(as) => as.foreach(apply(pol)) case ComposedType(_, l, r) => apply(pol)(l); apply(pol)(r) diff --git a/shared/src/main/scala/mlscript/codegen/typescript/TsTypegen.scala b/shared/src/main/scala/mlscript/codegen/typescript/TsTypegen.scala index 51a41cfa..c75f8025 100644 --- a/shared/src/main/scala/mlscript/codegen/typescript/TsTypegen.scala +++ b/shared/src/main/scala/mlscript/codegen/typescript/TsTypegen.scala @@ -572,7 +572,7 @@ final class TsTypegenCodeBuilder { typeScope.getTypeAliasSymbol(tvarName).map { taliasInfo => SourceCode(taliasInfo.lexicalName) ++ SourceCode.paramList(taliasInfo.params.map(SourceCode(_))) }.getOrElse(SourceCode(tvarName)) - case Constrained(base, tvbs, where) => + case Constrained(base, tvbs, where, _) => throw CodeGenError(s"Cannot generate type for `where` clause $tvbs $where") case _: Splice | _: TypeTag | _: PolyType | _: Selection => throw CodeGenError(s"Cannot yet generate type for: $mlType") diff --git a/shared/src/main/scala/mlscript/helpers.scala b/shared/src/main/scala/mlscript/helpers.scala index aa9505c3..8131ff24 100644 --- a/shared/src/main/scala/mlscript/helpers.scala +++ b/shared/src/main/scala/mlscript/helpers.scala @@ -1,5 +1,4 @@ package mlscript - import scala.util.chaining._ import scala.collection.mutable.{Map => MutMap, SortedMap => SortedMutMap, Set => MutSet, Buffer} import scala.collection.immutable.SortedMap @@ -115,7 +114,7 @@ trait TypeLikeImpl extends Located { self: TypeLike => .mkString("forall ", " ", ".")} ${body.showIn(0)}", outerPrec > 1 // or 0? ) - case Constrained(b, bs, ws) => + case Constrained(b, bs, ws, tscs) => val oldCtx = ctx val bStr = b.showIn(0).stripSuffix("\n") val multiline = bStr.contains('\n') @@ -138,6 +137,13 @@ trait TypeLikeImpl extends Located { self: TypeLike => }.mkString }${ws.map{ case Bounds(lo, hi) => s"\n${ctx.indStr}${lo.showIn(0)} <: ${hi.showIn(0)}" // TODO print differently from bs? + }.mkString + }${tscs.map{ + case (tvs, constrs) => + val s = tvs.map(u => (if (u._1) "+" else "-") ++ u._2.showIn(0)) + .mkString("[", ", ", "]") + s"\n${ctx.indStr}" + s + + s" in ${constrs.map(_.map(_.showIn(0)).mkString("[", ", ", "]")).mkString("{", ", ", "}")}" }.mkString}" }, outerPrec > 0) case fd @ NuFunDef(isLetRec, nme, snme, targs, rhs) => @@ -207,7 +213,7 @@ trait TypeLikeImpl extends Located { self: TypeLike => case WithExtension(b, r) => b :: r :: Nil case PolyType(targs, body) => targs.map(_.fold(identity, identity)) :+ body case Splice(fs) => fs.flatMap{ case L(l) => l :: Nil case R(r) => r.in.toList ++ (r.out :: Nil) } - case Constrained(b, bs, ws) => b :: bs.flatMap(c => c._1 :: c._2 :: Nil) ::: ws.flatMap(c => c.lb :: c.ub :: Nil) + case Constrained(b, bs, ws, tscs) => b :: bs.flatMap(c => c._1 :: c._2 :: Nil) ::: ws.flatMap(c => c.lb :: c.ub :: Nil) ::: tscs.flatMap(tsc => tsc._1.map(_._2) ::: tsc._2.flatten) case Signature(xs, res) => xs ::: res.toList case NuFunDef(isLetRec, nme, snme, targs, rhs) => targs ::: rhs.toOption.toList case NuTypeDef(kind, nme, tparams, params, ctor, sig, parents, sup, ths, body) => @@ -782,7 +788,7 @@ trait TermImpl extends StatementImpl { self: Term => Constrained(body.toType_!, Nil, where.map { case Asc(l, r) => Bounds(l.toType_!, r) case s => throw new NotAType(s) - }) + }, Nil) case Forall(ps, bod) => PolyType(ps.map(R(_)), bod.toType_!) // diff --git a/shared/src/main/scala/mlscript/syntax.scala b/shared/src/main/scala/mlscript/syntax.scala index 5cab302d..44a57eee 100644 --- a/shared/src/main/scala/mlscript/syntax.scala +++ b/shared/src/main/scala/mlscript/syntax.scala @@ -161,7 +161,7 @@ final case class Rem(base: Type, names: Ls[Var]) extends Type final case class Bounds(lb: Type, ub: Type) extends Type final case class WithExtension(base: Type, rcd: Record) extends Type final case class Splice(fields: Ls[Either[Type, Field]]) extends Type -final case class Constrained(base: TypeLike, tvBounds: Ls[TypeVar -> Bounds], where: Ls[Bounds]) extends Type +final case class Constrained(base: TypeLike, tvBounds: Ls[TypeVar -> Bounds], where: Ls[Bounds], tscs: Ls[Ls[(Bool, Type)] -> Ls[Ls[Type]]]) extends Type // final case class FirstClassDefn(defn: NuTypeDef) extends Type // TODO // final case class Refinement(base: Type, decls: TypingUnit) extends Type // TODO diff --git a/shared/src/test/diff/fcp/Overloads.mls b/shared/src/test/diff/fcp/Overloads.mls index 2e0e51a7..770079c2 100644 --- a/shared/src/test/diff/fcp/Overloads.mls +++ b/shared/src/test/diff/fcp/Overloads.mls @@ -70,8 +70,51 @@ IISS 0 (if true then IISS else BBNN) 0 //│ res: bool | number | string -fun x -> (if true then IISS else BBNN) x -//│ res: int -> (bool | number | string) +def f = fun x -> (if true then IISS else BBNN) x +//│ f: int -> (bool | number | string) + +f(0) +//│ res: bool | number | string + +:e +f(0) + 1 +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.80: f(0) + 1 +//│ ║ ^^^^^^ +//│ ╟── type `bool` is not an instance of type `int` +//│ ║ l.13: def BBNN: bool -> bool & number -> number +//│ ║ ^^^^ +//│ ╟── but it flows into application with expected type `int` +//│ ║ l.80: f(0) + 1 +//│ ╙── ^^^^ +//│ res: error | int + +:e +f : int -> number +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.93: f : int -> number +//│ ║ ^ +//│ ╟── type `bool` is not an instance of type `number` +//│ ║ l.13: def BBNN: bool -> bool & number -> number +//│ ║ ^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.93: f : int -> number +//│ ╙── ^^^^^^ +//│ res: int -> number + +:e +f : number -> int +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.106: f : number -> int +//│ ║ ^ +//│ ╟── type `number` does not match type `int | string` +//│ ║ l.106: f : number -> int +//│ ║ ^^^^^^ +//│ ╟── Note: constraint arises from reference: +//│ ║ l.73: def f = fun x -> (if true then IISS else BBNN) x +//│ ╙── ^ +//│ res: number -> int + if true then IISS else BBNN //│ res: bool -> bool & number -> number | int -> int & string -> string @@ -85,11 +128,11 @@ if true then IISS else BBNN :e (if true then IISS else BBNN) : (0 | 1 | true) -> number //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.86: (if true then IISS else BBNN) : (0 | 1 | true) -> number -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.129: (if true then IISS else BBNN) : (0 | 1 | true) -> number +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `true` does not match type `int | string` -//│ ║ l.86: (if true then IISS else BBNN) : (0 | 1 | true) -> number -//│ ╙── ^^^^ +//│ ║ l.129: (if true then IISS else BBNN) : (0 | 1 | true) -> number +//│ ╙── ^^^^ //│ res: (0 | 1 | true) -> number @@ -107,13 +150,13 @@ not test //│ <: test: //│ ~(int -> int) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.105: not test +//│ ║ l.148: not test //│ ║ ^^^^^^^^ //│ ╟── type `~(int -> int)` is not an instance of type `bool` -//│ ║ l.99: def test: ~(int -> int) -//│ ║ ^^^^^^^^^^^^^ +//│ ║ l.142: def test: ~(int -> int) +//│ ║ ^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `bool` -//│ ║ l.105: not test +//│ ║ l.148: not test //│ ╙── ^^^^ //│ res: bool | error diff --git a/shared/src/test/diff/fcp/Overloads_Precise.mls b/shared/src/test/diff/fcp/Overloads_Precise.mls new file mode 100644 index 00000000..d24fc5df --- /dev/null +++ b/shared/src/test/diff/fcp/Overloads_Precise.mls @@ -0,0 +1,197 @@ + +:NoJS +:NoApproximateOverload + +type IISS = int -> int & string -> string +type BBNN = bool -> bool & number -> number +type ZZII = 0 -> 0 & int -> int +//│ Defined type alias IISS +//│ Defined type alias BBNN +//│ Defined type alias ZZII + +def IISS: int -> int & string -> string +def BBNN: bool -> bool & number -> number +def ZZII: 0 -> 0 & int -> int +//│ IISS: int -> int & string -> string +//│ BBNN: bool -> bool & number -> number +//│ ZZII: 0 -> 0 & int -> int + + +IISS : IISS +//│ res: IISS + +IISS : int -> int & string -> string +//│ res: int -> int & string -> string + +IISS : IISS | BBNN +//│ res: BBNN | IISS + +:e +IISS : ZZII +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.30: IISS : ZZII +//│ ║ ^^^^ +//│ ╟── type `int -> int & string -> string` is not an instance of `0 -> 0` +//│ ║ l.12: def IISS: int -> int & string -> string +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── but it flows into reference with expected type `0 -> 0` +//│ ║ l.30: IISS : ZZII +//│ ║ ^^^^ +//│ ╟── Note: constraint arises from function type: +//│ ║ l.7: type ZZII = 0 -> 0 & int -> int +//│ ║ ^^^^^^ +//│ ╟── from type reference: +//│ ║ l.30: IISS : ZZII +//│ ╙── ^^^^ +//│ res: ZZII + +:e +IISS : BBNN +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.49: IISS : BBNN +//│ ║ ^^^^ +//│ ╟── type `int -> int & string -> string` is not an instance of `bool -> bool` +//│ ║ l.12: def IISS: int -> int & string -> string +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── but it flows into reference with expected type `bool -> bool` +//│ ║ l.49: IISS : BBNN +//│ ║ ^^^^ +//│ ╟── Note: constraint arises from function type: +//│ ║ l.6: type BBNN = bool -> bool & number -> number +//│ ║ ^^^^^^^^^^^^ +//│ ╟── from type reference: +//│ ║ l.49: IISS : BBNN +//│ ╙── ^^^^ +//│ res: BBNN + + +// * These tests show that we currently throw away information when constraining LHS overloading sets: + +IISS : int -> int +//│ res: int -> int + +IISS : (0 | 1) -> number +//│ res: (0 | 1) -> number + +IISS : 'a -> 'a +//│ res: 'a -> 'a +//│ where +//│ [-'a, +'a] in {[int, int], [string, string]} + +IISS 0 +//│ res: int + +(IISS : int -> int) 0 +//│ res: int + +(if true then IISS else BBNN) 0 +//│ res: number + +// * Note that this is not considered ambiguous +// * because the type variable occurrences are polar, +// * meaning that the TSCs are always trivially satisfiable +// * and thus the code is well-typed. +// * Conceptually, we'd expect this inferred type to reduce to `int -> number`, +// * but it's tricky to do such simplifications in general. +def f = fun x -> (if true then IISS else BBNN) x +//│ f: 'a -> 'b +//│ where +//│ [+'a, -'b] in {[int, int], [string, string]} +//│ [+'a, -'b] in {[bool, bool], [number, number]} + +f(0) +//│ res: number + +:e +f(0) + 1 +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.106: f(0) + 1 +//│ ║ ^^^^^^ +//│ ╟── type `number` is not an instance of type `int` +//│ ║ l.13: def BBNN: bool -> bool & number -> number +//│ ║ ^^^^^^ +//│ ╟── but it flows into application with expected type `int` +//│ ║ l.106: f(0) + 1 +//│ ╙── ^^^^ +//│ res: error | int + +f : int -> number +//│ res: int -> number + +:e +f : number -> int +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.122: f : number -> int +//│ ║ ^ +//│ ╟── type `number` does not match type `?a` +//│ ║ l.122: f : number -> int +//│ ╙── ^^^^^^ +//│ res: number -> int + + +if true then IISS else BBNN +//│ res: bool -> bool & number -> number | int -> int & string -> string + +(if true then IISS else ZZII) : int -> int +//│ res: int -> int + +(if true then IISS else BBNN) : (0 | 1) -> number +//│ res: (0 | 1) -> number + +:e +(if true then IISS else BBNN) : (0 | 1 | true) -> number +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.142: (if true then IISS else BBNN) : (0 | 1 | true) -> number +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type `int -> int & string -> string` is not an instance of `(0 | 1 | true) -> number` +//│ ║ l.12: def IISS: int -> int & string -> string +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── but it flows into reference with expected type `(0 | 1 | true) -> number` +//│ ║ l.142: (if true then IISS else BBNN) : (0 | 1 | true) -> number +//│ ║ ^^^^ +//│ ╟── Note: constraint arises from function type: +//│ ║ l.142: (if true then IISS else BBNN) : (0 | 1 | true) -> number +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ +//│ res: (0 | 1 | true) -> number + + +// * Note that type normalization used to be very aggressive at approximating non-tag type negations, +// * to simplify the result, but this was changed as it was unsound + +def test: ~(int -> int) +//│ test: ~(int -> int) + +// * See also test file BooleanFail.mls about this previous unsoundness +:e +test = 42 +not test +//│ 42 +//│ <: test: +//│ ~(int -> int) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.167: not test +//│ ║ ^^^^^^^^ +//│ ╟── type `~(int -> int)` is not an instance of type `bool` +//│ ║ l.161: def test: ~(int -> int) +//│ ║ ^^^^^^^^^^^^^ +//│ ╟── but it flows into reference with expected type `bool` +//│ ║ l.167: not test +//│ ╙── ^^^^ +//│ res: bool | error + +def test: ~(int -> int) & ~bool +//│ test: ~bool & ~(int -> int) + +def test: ~(int -> int) & bool +//│ test: bool + +def test: ~(int -> int) & ~(bool -> bool) +//│ test: ~(nothing -> (bool | int)) + +def test: ~(int -> int | bool -> bool) +//│ test: ~(nothing -> (bool | int)) + +def test: ~(int -> int & string -> string) & ~(bool -> bool & number -> number) +//│ test: in ~(nothing -> (number | string) & int -> number & nothing -> (bool | string) & nothing -> (bool | int)) out ~(nothing -> (bool | int) & nothing -> (bool | string) & int -> number & nothing -> (number | string)) + + diff --git a/shared/src/test/diff/nu/HeungTung.mls b/shared/src/test/diff/nu/HeungTung.mls index cace8c82..3785f32d 100644 --- a/shared/src/test/diff/nu/HeungTung.mls +++ b/shared/src/test/diff/nu/HeungTung.mls @@ -1,4 +1,5 @@ :NewDefs +:NoApproximateOverload @@ -66,8 +67,21 @@ fun g = h //│ fun g: (Int | false | true) -> (Int | false | true) // * In one step +:e // TODO: argument of union type fun g: (Int | Bool) -> (Int | Bool) fun g = f +//│ ╔══[ERROR] Type mismatch in definition: +//│ ║ l.72: fun g = f +//│ ║ ^^^^^ +//│ ╟── type `Int -> Int & Bool -> Bool` is not an instance of `(Int | false | true) -> (Int | false | true)` +//│ ║ l.51: fun f: (Int -> Int) & (Bool -> Bool) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── but it flows into reference with expected type `(Int | false | true) -> (Int | false | true)` +//│ ║ l.72: fun g = f +//│ ║ ^ +//│ ╟── Note: constraint arises from function type: +//│ ║ l.71: fun g: (Int | Bool) -> (Int | Bool) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ fun g: Int -> Int & Bool -> Bool //│ fun g: (Int | false | true) -> (Int | false | true) @@ -88,9 +102,17 @@ fun j = i fun j: (Int & Bool) -> (Int & Bool) fun j = f //│ ╔══[ERROR] Type mismatch in definition: -//│ ║ l.89: fun j = f -//│ ║ ^^^^^ -//│ ╙── expression of type `Int` does not match type `nothing` +//│ ║ l.103: fun j = f +//│ ║ ^^^^^ +//│ ╟── type `Int -> Int & Bool -> Bool` is not an instance of `nothing -> nothing` +//│ ║ l.51: fun f: (Int -> Int) & (Bool -> Bool) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── but it flows into reference with expected type `nothing -> nothing` +//│ ║ l.103: fun j = f +//│ ║ ^ +//│ ╟── Note: constraint arises from function type: +//│ ║ l.102: fun j: (Int & Bool) -> (Int & Bool) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ fun j: Int -> Int & Bool -> Bool //│ fun j: nothing -> nothing @@ -106,7 +128,7 @@ fun g = f // * With match-type-based constraint solving, we could return Int here f(0) -//│ Int | false | true +//│ Int //│ res //│ = 0 @@ -114,15 +136,26 @@ f(0) x => f(x) -//│ (Int | false | true) -> (Int | false | true) +//│ forall 'a 'b. 'a -> 'b +//│ where +//│ [+'a, -'b] in {[Int, Int], [Bool, Bool]} //│ res //│ = [Function: res] // : forall 'a: 'a -> case 'a of { Int => Int; Bool => Bool } where 'a <: Int | Bool - +:e f(if true then 0 else false) -//│ Int | false | true +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.148: f(if true then 0 else false) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type `Int -> Int & Bool -> Bool` is not an instance of `(0 | false) -> ?a` +//│ ║ l.51: fun f: (Int -> Int) & (Bool -> Bool) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── but it flows into reference with expected type `(0 | false) -> ?a` +//│ ║ l.148: f(if true then 0 else false) +//│ ╙── ^ +//│ error //│ res //│ = 0 @@ -132,15 +165,25 @@ f(if true then 0 else false) :w f(refined if true then 0 else false) // this one can be precise again! //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.133: f(refined if true then 0 else false) // this one can be precise again! +//│ ║ l.166: f(refined if true then 0 else false) // this one can be precise again! //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Illegal use of reserved operator: refined -//│ ║ l.133: f(refined if true then 0 else false) // this one can be precise again! +//│ ║ l.166: f(refined if true then 0 else false) // this one can be precise again! //│ ╙── ^^^^^^^ //│ ╔══[ERROR] identifier not found: refined -//│ ║ l.133: f(refined if true then 0 else false) // this one can be precise again! +//│ ║ l.166: f(refined if true then 0 else false) // this one can be precise again! //│ ╙── ^^^^^^^ -//│ Int | false | true +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.166: f(refined if true then 0 else false) // this one can be precise again! +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── application of type `error` does not match type `?a` +//│ ║ l.166: f(refined if true then 0 else false) // this one can be precise again! +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ 'a +//│ where +//│ 'b :> error +//│ 'a :> error +//│ [+'b, -'a] in {} //│ Code generation encountered an error: //│ unresolved symbol refined @@ -196,7 +239,7 @@ type T = List[Int] :e // TODO application types type Res = M(T) //│ ╔══[ERROR] Wrong number of type arguments – expected 0, found 1 -//│ ║ l.197: type Res = M(T) +//│ ║ l.240: type Res = M(T) //│ ╙── ^^^^ //│ type Res = M @@ -219,7 +262,7 @@ fun f: Int -> Int fun f: Bool -> Bool fun f = id //│ ╔══[ERROR] A type signature for 'f' was already given -//│ ║ l.219: fun f: Bool -> Bool +//│ ║ l.262: fun f: Bool -> Bool //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ fun f: forall 'a. 'a -> 'a //│ fun f: Int -> Int @@ -227,13 +270,13 @@ fun f = id :e // TODO support f: (Int -> Int) & (Bool -> Bool) //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.228: f: (Int -> Int) & (Bool -> Bool) +//│ ║ l.271: f: (Int -> Int) & (Bool -> Bool) //│ ║ ^ //│ ╟── type `Bool` is not an instance of type `Int` -//│ ║ l.228: f: (Int -> Int) & (Bool -> Bool) +//│ ║ l.271: f: (Int -> Int) & (Bool -> Bool) //│ ║ ^^^^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.218: fun f: Int -> Int +//│ ║ l.261: fun f: Int -> Int //│ ╙── ^^^ //│ Int -> Int & Bool -> Bool //│ res @@ -300,17 +343,17 @@ fun test(x) = refined if x is A then 0 B then 1 //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.299: fun test(x) = refined if x is +//│ ║ l.342: fun test(x) = refined if x is //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.300: A then 0 +//│ ║ l.343: A then 0 //│ ║ ^^^^^^^^^^ -//│ ║ l.301: B then 1 +//│ ║ l.344: B then 1 //│ ╙── ^^^^^^^^^^ //│ ╔══[ERROR] Illegal use of reserved operator: refined -//│ ║ l.299: fun test(x) = refined if x is +//│ ║ l.342: fun test(x) = refined if x is //│ ╙── ^^^^^^^ //│ ╔══[ERROR] identifier not found: refined -//│ ║ l.299: fun test(x) = refined if x is +//│ ║ l.342: fun test(x) = refined if x is //│ ╙── ^^^^^^^ //│ fun test: (A | B) -> error //│ Code generation encountered an error: @@ -320,3 +363,320 @@ fun test(x) = refined if x is +fun q: (0|1) -> true & (1|2) -> false +//│ fun q: (0 | 1) -> true & (1 | 2) -> false + +q(0) +//│ true +//│ res +//│ = +//│ q is not implemented + +q(0) : true +//│ true +//│ res +//│ = +//│ q is not implemented + +q(1) +//│ 'a +//│ where +//│ [-'a] in {[true], [false]} +//│ res +//│ = +//│ q is not implemented + +q(1) : Bool +//│ Bool +//│ res +//│ = +//│ q is not implemented + +x => q(x): true +//│ (0 | 1) -> true +//│ res +//│ = +//│ q is not implemented + +x => q(x) +//│ forall 'a 'b. 'a -> 'b +//│ where +//│ [+'a, -'b] in {[0 | 1, true], [1 | 2, false]} +//│ res +//│ = +//│ q is not implemented + +:e +(x => q(x))(1):Int +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.410: (x => q(x))(1):Int +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── application of type `?a` does not match type `Int` +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.410: (x => q(x))(1):Int +//│ ╙── ^^^ +//│ Int +//│ res +//│ = +//│ q is not implemented + +:e +q(1):int +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.424: q(1):int +//│ ║ ^^^^ +//│ ╟── application of type `?a` does not match type `int` +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.424: q(1):int +//│ ╙── ^^^ +//│ int +//│ res +//│ = +//│ q is not implemented + +fun w = x => q(x) +//│ fun w: forall 'a 'b. 'a -> 'b +//│ where +//│ [+'a, -'b] in {[0 | 1, true], [1 | 2, false]} + +w(0) +//│ true +//│ res +//│ = +//│ w and q are not implemented + +x => (f: forall a: ((0, Int) -> 'a & (1, Str) -> ['a])) => f(0, x) + 1 +//│ Int -> (f: (0, Int) -> Int & (1, Str) -> [Int]) -> Int +//│ res +//│ = [Function: res] + +fun r: Int -> Int & Bool -> Bool +//│ fun r: Int -> Int & Bool -> Bool + +:e +x => r(r(x)) +//│ ╔══[ERROR] ambiguous +//│ ╟── cannot determine satisfiability of type ?a +//│ ║ l.457: x => r(r(x)) +//│ ╙── ^^^^ +//│ forall 'a 'b 'c. 'a -> 'c +//│ where +//│ [+'a, -'b] in {[Int, Int], [Bool, Bool]} +//│ [-'c, +'b] in {[Int, Int], [Bool, Bool]} +//│ res +//│ = +//│ r is not implemented + + +r(r(0)) +//│ Int +//│ res +//│ = +//│ r is not implemented + +x => r(r(x))+1 +//│ Int -> Int +//│ res +//│ = +//│ r is not implemented + +fun u: {x:0, y:Int} -> Int & {x:1, z: Str} -> Str +//│ fun u: {x: 0, y: Int} -> Int & {x: 1, z: Str} -> Str + +(a, b, c) => u({x: a, y: b, z: c}) +//│ forall 'a 'b 'c 'd. ('a, 'c, 'd) -> 'b +//│ where +//│ [-'b, +'a, +'c, +'d] in {[Int, 0, Int, anything], [Str, 1, anything, Str]} +//│ res +//│ = +//│ u is not implemented + +(a, b) => u({x: a, y: "abc", z: b}) +//│ (1, Str) -> Str +//│ res +//│ = +//│ u is not implemented + +fun s: Str -> Str & AA -> AA +//│ fun s: Str -> Str & AA -> AA + +:e +let g = x => s(r(x)) +//│ ╔══[ERROR] ambiguous +//│ ╟── cannot determine satisfiability of type ?a +//│ ║ l.504: let g = x => s(r(x)) +//│ ╙── ^^^^ +//│ let g: forall 'a 'b 'c. 'a -> 'c +//│ where +//│ [+'a, -'b] in {[Int, Int], [Bool, Bool]} +//│ [+'b, -'c] in {[Str, Str], [AA, AA]} +//│ g +//│ = +//│ s is not implemented + +:e +fun g(x) = s(r(x)) +//│ ╔══[ERROR] ambiguous +//│ ╟── cannot determine satisfiability of type ?a +//│ ║ l.518: fun g(x) = s(r(x)) +//│ ╙── ^^^^ +//│ fun g: forall 'a 'b 'c. 'a -> 'c +//│ where +//│ [+'a, -'b] in {[Int, Int], [Bool, Bool]} +//│ [-'c, +'b] in {[Str, Str], [AA, AA]} + +:e +x => s(r(x)) +//│ ╔══[ERROR] ambiguous +//│ ╟── cannot determine satisfiability of type ?a +//│ ║ l.529: x => s(r(x)) +//│ ╙── ^^^^ +//│ forall 'a 'b 'c. 'a -> 'c +//│ where +//│ [+'a, -'b] in {[Int, Int], [Bool, Bool]} +//│ [-'c, +'b] in {[Str, Str], [AA, AA]} +//│ res +//│ = +//│ s is not implemented + +:e +g(0) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.543: g(0) +//│ ║ ^^^^ +//│ ╟── expression of type `Int` does not match type `?a` +//│ ╟── Note: constraint arises from application: +//│ ║ l.518: fun g(x) = s(r(x)) +//│ ╙── ^^^^ +//│ error +//│ res +//│ = +//│ g and s are not implemented + +fun rt: {0: Int} -> Int & {0: Str} -> Str +//│ fun rt: {0: Int} -> Int & {0: Str} -> Str + +rt([1,"str"]) +//│ Int +//│ res +//│ = +//│ rt is not implemented + +rt(["str",1]) +//│ Str +//│ res +//│ = +//│ rt is not implemented + +fun app2: ('a -> 'a -> 'a) -> 'a -> 'a +//│ fun app2: forall 'a. ('a -> 'a -> 'a) -> 'a -> 'a + +fun snd: A -> Int -> Int & Str -> Str -> Str +//│ fun snd: A -> Int -> Int & Str -> Str -> Str + +:e +x => app2(snd)(x):Int +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.578: x => app2(snd)(x):Int +//│ ║ ^^^^^^^^^^^^ +//│ ╟── type `Int` is not an instance of type `A` +//│ ║ l.571: fun app2: ('a -> 'a -> 'a) -> 'a -> 'a +//│ ║ ^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.574: fun snd: A -> Int -> Int & Str -> Str -> Str +//│ ╙── ^ +//│ nothing -> Int +//│ res +//│ = +//│ app2 is not implemented + +fun app2_ (f:'a -> 'a -> 'a)(x) = f(x)(x) +//│ fun app2_: forall 'a. (f: 'a -> 'a -> 'a) -> 'a -> 'a + +app2_(snd) +//│ 'a -> 'b +//│ where +//│ 'a <: 'b +//│ [-'b, -'a, +'a] in {[Int, Int, A & Int], [Str, Str, Str]} +//│ res +//│ = +//│ snd is not implemented + +// * Example from WeirdUnions.mls. +// * This type merges the input tuples: +fun f: (Str => Str) & ((Str, Int) => Str) +//│ fun f: (...Array[Int | Str] & {0: Str}) -> Str + +f("abc", "abc") +//│ Str +//│ res +//│ = +//│ f is not implemented + +fun f: (Str => Str) & ((Str, Int) => Int) +//│ fun f: Str -> Str & (Str, Int) -> Int + +// * Different from WeirdUnions.mls: +:e +f("abc", "abc") +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.621: f("abc", "abc") +//│ ║ ^^^^^^^^^^^^^^^ +//│ ╟── type `Str -> Str & (Str, Int) -> Int` is not an instance of `("abc", "abc") -> ?a` +//│ ║ l.616: fun f: (Str => Str) & ((Str, Int) => Int) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── but it flows into reference with expected type `("abc", "abc") -> ?a` +//│ ║ l.621: f("abc", "abc") +//│ ╙── ^ +//│ error +//│ res +//│ = +//│ f is not implemented + +f("abcabc") +//│ Str +//│ res +//│ = +//│ f is not implemented + +:e +x => rt([not(x)]) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.643: x => rt([not(x)]) +//│ ║ ^^^^^^^^^^^^ +//│ ╟── application of type `Bool` does not match type `?a` +//│ ║ l.643: x => rt([not(x)]) +//│ ╙── ^^^^^^ +//│ forall 'a 'b. Bool -> 'a +//│ where +//│ 'b :> Bool +//│ 'a :> error +//│ [-'a, +'b] in {} +//│ res +//│ = +//│ rt is not implemented + +:e +rt(0) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.660: rt(0) +//│ ║ ^^^^^ +//│ ╟── type `{0: Int} -> Int & {0: Str} -> Str` is not an instance of `0 -> ?a` +//│ ║ l.556: fun rt: {0: Int} -> Int & {0: Str} -> Str +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── but it flows into reference with expected type `0 -> ?a` +//│ ║ l.660: rt(0) +//│ ╙── ^^ +//│ error +//│ res +//│ = +//│ rt is not implemented + +fun z: {0:Int} -> nothing & Str -> Str +//│ fun z: {0: Int} -> nothing & Str -> Str + +z([1]) +//│ nothing +//│ res +//│ = +//│ z is not implemented diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index 5e3076d2..6965d73c 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -228,6 +228,7 @@ class DiffTests(state: DiffTests.State) // Enable this to see the errors from unfinished `PreTyper`. var showPreTyperErrors = false var noTailRec = false + var noApproximateOverload = false // * This option makes some test cases pass which assume generalization should happen in arbitrary arguments // * but it's way too aggressive to be ON by default, as it leads to more extrusion, cycle errors, etc. @@ -299,6 +300,7 @@ class DiffTests(state: DiffTests.State) case "GeneralizeArguments" => generalizeArguments = true; mode case "DontGeneralizeArguments" => generalizeArguments = false; mode case "IrregularTypes" => irregularTypes = true; mode + case "NoApproximateOverload" => noApproximateOverload = true; mode case str @ "Fuel" => // println("'"+line.drop(str.length + 2)+"'") typer.startingFuel = line.drop(str.length + 2).toInt; mode @@ -559,6 +561,7 @@ class DiffTests(state: DiffTests.State) typer.explainErrors = mode.explainErrors stdout = mode.stdout typer.preciselyTypeRecursion = mode.preciselyTypeRecursion + typer.noApproximateOverload = noApproximateOverload val oldCtx = ctx @@ -588,7 +591,7 @@ class DiffTests(state: DiffTests.State) exp match { // * Strip top-level implicitly-quantified type variables case pt: PolyType => stripPoly(pt) - case Constrained(pt: PolyType, bs, cs) => Constrained(stripPoly(pt), bs, cs) + case Constrained(pt: PolyType, bs, cs, tscs) => Constrained(stripPoly(pt), bs, cs, tscs) case ty => ty } } From a7504bb818f21a802f2fc8a338888b67a7c87da5 Mon Sep 17 00:00:00 2001 From: Fa1sePRoMiSe Date: Thu, 10 Oct 2024 13:36:12 +0800 Subject: [PATCH 143/143] Fix missing annotation (#226) --- shared/src/main/scala/mlscript/NewParser.scala | 2 +- shared/src/test/diff/nu/Ascription.mls | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/shared/src/main/scala/mlscript/NewParser.scala b/shared/src/main/scala/mlscript/NewParser.scala index 4024d653..2b260200 100644 --- a/shared/src/main/scala/mlscript/NewParser.scala +++ b/shared/src/main/scala/mlscript/NewParser.scala @@ -522,7 +522,7 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], newDefs: Bo consume if (tparams.nonEmpty) err(msg"Unsupported type parameters on 'let' binding" -> S(l1) :: Nil) val rest = expr(0) - R(Let(isLetRec.getOrElse(die), v, body, rest).withLoc(S(l0 ++ annotatedBody.toLoc))) + R(Let(isLetRec.getOrElse(die), v, annotatedBody, rest).withLoc(S(l0 ++ annotatedBody.toLoc))) case _ => R(NuFunDef( isLetRec, v, opStr, tparams, L(ps.foldRight(annotatedBody)((i, acc) => Lam(i, acc))) diff --git a/shared/src/test/diff/nu/Ascription.mls b/shared/src/test/diff/nu/Ascription.mls index 7bc04929..69c7ce76 100644 --- a/shared/src/test/diff/nu/Ascription.mls +++ b/shared/src/test/diff/nu/Ascription.mls @@ -49,3 +49,10 @@ foo(123:Int):Int //│ Code generation encountered an error: //│ unresolved symbol Int +fun foo(f) = + let g = (x => f(x)): forall 'a : 'a -> 'a in g(123) +//│ fun foo: (??a -> ??a0) -> 123 + +fun foo(f) = + let g: forall 'a : 'a -> 'a = x => f(x) in g(123) +//│ fun foo: (??a -> ??a0) -> 123