diff --git a/.github/workflows/scala.yml b/.github/workflows/scala.yml index 0ea2d51609..42559c2da7 100644 --- a/.github/workflows/scala.yml +++ b/.github/workflows/scala.yml @@ -4,7 +4,6 @@ on: push: branches: [ mlscript ] pull_request: - branches: [ mlscript ] jobs: build: diff --git a/.gitignore b/.gitignore index 1e05d21e31..4a989b4c7d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ metals.sbt project/Dependencies.scala project/metals.sbt **.worksheet.sc +.DS_Store diff --git a/.vscode/settings.json b/.vscode/settings.json index ed59a9aa71..90f3019795 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,5 +13,6 @@ "other": "off", "strings": "off" } - } + }, + "files.autoSave": "off" } diff --git a/build.sbt b/build.sbt index 66cf668450..8f4ff503f5 100644 --- a/build.sbt +++ b/build.sbt @@ -2,10 +2,15 @@ import Wart._ enablePlugins(ScalaJSPlugin) -ThisBuild / scalaVersion := "2.13.9" +ThisBuild / scalaVersion := "2.13.12" ThisBuild / version := "0.1.0-SNAPSHOT" ThisBuild / organization := "io.lptk" ThisBuild / organizationName := "LPTK" +ThisBuild / scalacOptions ++= Seq( + "-deprecation", + "-feature", + "-unchecked", +) lazy val root = project.in(file(".")) .aggregate(mlscriptJS, mlscriptJVM, ts2mlsTest, compilerJVM) @@ -18,9 +23,6 @@ lazy val mlscript = crossProject(JSPlatform, JVMPlatform).in(file(".")) .settings( name := "mlscript", scalacOptions ++= Seq( - "-deprecation", - "-feature", - "-unchecked", "-language:higherKinds", "-Ywarn-value-discard", "-Ypatmat-exhaust-depth:160", @@ -36,7 +38,8 @@ lazy val mlscript = crossProject(JSPlatform, JVMPlatform).in(file(".")) StringPlusAny, Any, ToString, JavaSerializable, Serializable, Product, ToString, LeakingSealed, Overloading, - Option2Iterable, IterableOps, ListAppend + Option2Iterable, IterableOps, ListAppend, SeqApply, + TripleQuestionMark, ), libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.12" % Test, libraryDependencies += "com.lihaoyi" %%% "sourcecode" % "0.3.0", @@ -60,10 +63,6 @@ lazy val mlscriptJS = mlscript.js lazy val ts2mls = crossProject(JSPlatform, JVMPlatform).in(file("ts2mls")) .settings( name := "ts2mls", - scalaVersion := "2.13.8", - scalacOptions ++= Seq( - "-deprecation" - ) ) .jvmSettings() .jsSettings( @@ -76,7 +75,6 @@ lazy val ts2mlsJVM = ts2mls.jvm lazy val ts2mlsTest = project.in(file("ts2mls")) .settings( - scalaVersion := "2.13.8", Test / test := ((ts2mlsJVM / Test / test) dependsOn (ts2mlsJS / Test / test)).value ) diff --git a/compiler/shared/main/scala/mlscript/compiler/ClassLifter.scala b/compiler/shared/main/scala/mlscript/compiler/ClassLifter.scala index d0a63324a8..559bc59578 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ClassLifter.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ClassLifter.scala @@ -6,8 +6,9 @@ import mlscript.utils.shorthands.* import scala.collection.mutable.StringBuilder as StringBuilder import scala.collection.mutable.Map as MutMap import scala.collection.mutable.Set as MutSet -import mlscript.codegen.Helpers.inspect as showStructure +import scala.collection.mutable.ArrayBuffer as ArrayBuffer import mlscript.codegen.CodeGenError +import mlscript.compiler.mono.MonomorphError class ClassLifter(logDebugMsg: Boolean = false) { type ClassName = String @@ -28,6 +29,7 @@ class ClassLifter(logDebugMsg: Boolean = false) { LocalContext(vSet ++ inters.map(x => Var(x.name)), tSet -- inters) } def intersect(rst: LocalContext) = LocalContext(vSet intersect rst.vSet, tSet intersect rst.tSet) + def intersectV(rst: Set[Var]) = LocalContext(vSet.intersect(rst), tSet) def contains(v: Var) = vSet.contains(v) || tSet.contains(TypeName(v.name)) def contains(tv: TypeName) = vSet.contains(Var(tv.name)) || tSet.contains(tv) override def toString(): String = "(" ++ vSet.mkString(", ") ++ "; " ++ tSet.mkString(", ") ++ ")" @@ -37,6 +39,7 @@ class ClassLifter(logDebugMsg: Boolean = false) { private def asContext(t: TypeName) = LocalContext(Set(), Set(t)) private def asContextT(tS: IterableOnce[TypeName]) = LocalContext(Set(), tS.iterator.toSet) private def emptyCtx = LocalContext(Set(), Set()) + private def emptyCtxObj = LocalContext(Set(Var("this")), Set()) case class ClassInfoCache( originNm: TypeName, @@ -55,10 +58,11 @@ class ClassLifter(logDebugMsg: Boolean = false) { type NamePath = List[String] var retSeq: List[NuTypeDef] = Nil + val globalFunctions: ArrayBuffer[NuFunDef] = ArrayBuffer() var anonymCnt: Int = 0 var clsCnt: Int = 0 val logOutput: StringBuilder = new StringBuilder - val primiTypes = new mlscript.Typer(false, false, false).primitiveTypes + val primiTypes = new mlscript.Typer(false, false, false, true).primitiveTypes private def log(str: String): Unit = { logOutput.append(str) @@ -81,15 +85,15 @@ class ClassLifter(logDebugMsg: Boolean = false) { } private def tupleEntityToVar(fld: (Option[Var], Fld)): Option[Var] = fld match{ - case (None, Fld(_, _, v: Var)) => Some(v) + case (None, Fld(_, v: Var)) => Some(v) case (Some(v: Var), _) => Some(v) case _ => None } private def getFields(etts: List[Statement]): Set[Var] = { etts.flatMap{ - case NuFunDef(_, nm, _, _) => Some(nm) - case NuTypeDef(_, TypeName(nm), _, _, _, _) => Some(Var(nm)) + case NuFunDef(_, nm, _, _, _) => Some(nm) + case nuty: NuTypeDef => Some(Var(nuty.name)) case Let(_, name, _, _) => Some(name) case _ => None }.toSet @@ -115,7 +119,7 @@ class ClassLifter(logDebugMsg: Boolean = false) { selPath2Term(l.map(x => genParName(x.name)).updated(0, "this").reverse, v) }) } - private def toFldsEle(trm: Term): (Option[Var], Fld) = (None, Fld(false, false, trm)) + private def toFldsEle(trm: Term): (Option[Var], Fld) = (None, Fld(FldFlags(false, false, false), trm)) def getSupClsInfoByTerm(parentTerm: Term): (List[TypeName], List[(Var, Fld)]) = parentTerm match{ case Var(nm) => List(TypeName(nm)) -> Nil @@ -144,7 +148,7 @@ class ClassLifter(logDebugMsg: Boolean = false) { (tmp._1.flatten, tmp._2.flatten, tmp._3.flatten) } - private def genClassNm(orgNm: String)(using ctx: LocalContext, cache: ClassCache, outer: Option[ClassInfoCache]): TypeName = { + private def genClassNm(orgNm: String)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): TypeName = { TypeName(outer match{ case None => clsCnt = clsCnt+1 @@ -153,17 +157,21 @@ class ClassLifter(logDebugMsg: Boolean = false) { }) } - private def getFreeVars(stmt: Located)(using ctx: LocalContext, cache: ClassCache, outer: Option[ClassInfoCache]): LocalContext = stmt match{ - case v:Var => - log(s"get free var find $v: ${ctx.vSet.contains(v)}/${buildPathToVar(v).isDefined}/${cache.contains(TypeName(v.name))}/${v.name.equals("this")}") - if(ctx.vSet.contains(v) || buildPathToVar(v).isDefined || cache.contains(TypeName(v.name)) || v.name.equals("this") || primiTypes.contains(v.name)) then emptyCtx else asContext(v) - case t: NamedType => + private def getFreeVars(stmt: Located)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): LocalContext = stmt match{ + case v:Var => + val caseEmpty = ctx.vSet.contains(v) || cache.contains(TypeName(v.name)) || globFuncs.contains(v) || primiTypes.contains(v.name) + val caseThis = buildPathToVar(v).isDefined && !ctx.vSet.contains(Var("this")) + log(s"get free var find $v: $caseEmpty/$caseThis") + if(caseEmpty) then emptyCtx + else if(caseThis) asContext(Var("this")) + else asContext(v) + case t: NamedType => log(s"get type $t under $ctx, $cache, $outer") asContextT(t.collectTypeNames.map(TypeName(_)).filterNot(x => ctx.contains(x) || cache.contains(x) || primiTypes.contains(x.name))) case Lam(lhs, rhs) => val lhsVs = getFreeVars(lhs) getFreeVars(rhs)(using ctx ++ lhsVs) -+ lhsVs - case NuFunDef(_, vm, tps, Left(trm)) => + case NuFunDef(_, vm, _, tps, Left(trm)) => getFreeVars(trm).extV(vm).extT(tps) case OpApp(_, trm) => getFreeVars(trm) case Sel(trm, _) => getFreeVars(trm) @@ -171,22 +179,22 @@ class ClassLifter(logDebugMsg: Boolean = false) { getFreeVars(trm) ++ getFreeVars(tp) case Tup(tupLst) => tupLst.map{ - case (Some(v), Fld(_, _, trm)) => getFreeVars(trm).vSet2tSet.addV(v) - case (_, Fld(_, _, rhs)) => getFreeVars(rhs) + case (Some(v), Fld(_, trm)) => getFreeVars(trm).vSet2tSet.addV(v) + case (_, Fld(_, rhs)) => getFreeVars(rhs) }.fold(emptyCtx)(_ ++ _) case Rcd(fields) => fields.map{ - case (v, Fld(_, _, trm)) => getFreeVars(trm).vSet2tSet + case (v, Fld(_, trm)) => getFreeVars(trm).vSet2tSet }.fold(emptyCtx)(_ ++ _) case TyApp(trm, tpLst) => getFreeVars(trm).addT(tpLst.flatMap(_.collectTypeNames.map(TypeName(_)))) - case NuTypeDef(_, nm, tps, param, pars, body) => - val prmVs = getFreeVars(param)(using emptyCtx, Map(), None) + case NuTypeDef(_, nm, tps, param, _, _, pars, _, _, body) => + val prmVs = getFreeVars(param.getOrElse(Tup(Nil)))(using emptyCtx, Map(), globFuncs, None) val newVs = prmVs.vSet ++ getFields(body.entities) + Var(nm.name) - val nCtx = ctx.addV(newVs).addT(nm).addT(tps) + val nCtx = ctx.addV(newVs).addT(nm).addT(tps.map(_._2)) val parVs = pars.map(getFreeVars(_)(using nCtx)).fold(emptyCtx)(_ ++ _) val bodyVs = body.entities.map(getFreeVars(_)(using nCtx)).fold(emptyCtx)(_ ++ _) - (bodyVs ++ parVs -+ prmVs).extT(tps) + (bodyVs ++ parVs -+ prmVs).extT(tps.map(_._2)) case Blk(stmts) => val newVs = getFields(stmts) stmts.map(getFreeVars(_)(using ctx.addV(newVs))).fold(emptyCtx)(_ ++ _) @@ -196,20 +204,20 @@ class ClassLifter(logDebugMsg: Boolean = false) { others.children.map(getFreeVars).fold(emptyCtx)(_ ++ _) } - private def collectClassInfo(cls: NuTypeDef, preClss: Set[TypeName])(using ctx: LocalContext, cache: ClassCache, outer: Option[ClassInfoCache]): ClassInfoCache = { - val NuTypeDef(_, nm, tps, param, pars, body) = cls - log(s"grep context of ${cls.nme.name} under {\n$ctx\n$cache\n$outer\n}\n") + private def collectClassInfo(cls: NuTypeDef, preClss: Set[TypeName])(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): ClassInfoCache = { + val NuTypeDef(_, nm, tps, param, _, _, pars, _, _, body) = cls + log(s"grep context of ${cls.nme.name} under $ctx # $cache # $globFuncs # $outer ") val (clses, funcs, trms) = splitEntities(cls.body.entities) val (supNms, rcdFlds) = pars.map(getSupClsInfoByTerm).unzip val flds = rcdFlds.flatten.map{ - case (v, Fld(_, _, trm)) => - val tmp = getFreeVars(trm)(using emptyCtx) + case (v, Fld(_, trm)) => + val tmp = getFreeVars(trm)(using emptyCtxObj) val ret = tmp.tSet ++ tmp.vSet.map(x => TypeName(x.name)) (v, ret) }.unzip log(s"par record: ${flds._2.flatten}") - val fields = (param.fields.flatMap(tupleEntityToVar) ++ funcs.map(_.nme) ++ clses.map(x => Var(x.nme.name)) ++ trms.flatMap(grepFieldsInTrm) ++ flds._1).toSet - val nCtx = ctx.addV(fields).addV(flds._1).extT(tps) + val fields = (param.fold(Nil)(t => t.fields).flatMap(tupleEntityToVar) ++ funcs.map(_.nme) ++ clses.map(x => Var(x.nme.name)) ++ trms.flatMap(grepFieldsInTrm) ++ flds._1).toSet + val nCtx = ctx.addV(fields).addV(flds._1).extT(tps.map(_._2)).addV(Var("this")) val tmpCtx = ((body.entities.map(getFreeVars(_)(using nCtx)) ++ pars.map(getFreeVars(_)(using nCtx))).fold(emptyCtx)(_ ++ _).moveT2V(preClss) ).addT(flds._2.flatten.toSet).extV(supNms.flatten.map(x => Var(x.name))) @@ -218,8 +226,8 @@ class ClassLifter(logDebugMsg: Boolean = false) { ret } - private def liftCaseBranch(brn: CaseBranches)(using ctx: LocalContext, cache: ClassCache, outer: Option[ClassInfoCache]): (CaseBranches, LocalContext) = brn match{ - case Case(v: Var, body, rest) => + private def liftCaseBranch(brn: CaseBranches)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (CaseBranches, LocalContext) = brn match{ + case Case(v: Var, body, rest) => val nTrm = liftTerm(body)(using ctx.addV(v)) val nRest = liftCaseBranch(rest) (Case(v, nTrm._1, nRest._1), nTrm._2 ++ nRest._2) @@ -233,30 +241,30 @@ class ClassLifter(logDebugMsg: Boolean = false) { case NoCases => (brn, emptyCtx) } - private def liftIf(body: IfBody)(using ctx: LocalContext, cache: ClassCache, outer: Option[ClassInfoCache]): (IfBody, LocalContext) = body match{ - case IfElse(expr) => + private def liftIf(body: IfBody)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (IfBody, LocalContext) = body match{ + case IfElse(expr) => val ret = liftTerm(expr) (IfElse(ret._1), ret._2) case IfThen(expr, rhs) => val nE = liftTerm(expr) val nR = liftTerm(rhs) (IfThen(nE._1, nR._1), nE._2 ++ nR._2) - case _ => ??? + case _ => throw MonomorphError(s"Unknown IfBody: ${body}") } - private def liftTuple(tup: Tup)(using ctx: LocalContext, cache: ClassCache, outer: Option[ClassInfoCache]): (Tup, LocalContext) = { + private def liftTuple(tup: Tup)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (Tup, LocalContext) = { val ret = tup.fields.map{ - case (None, Fld(b1, b2, trm)) => + case (None, Fld(flags, trm)) => val tmp = liftTerm(trm) - ((None, Fld(b1, b2, tmp._1)), tmp._2) - case (Some(v), Fld(b1, b2, trm)) => + ((None, Fld(flags, tmp._1)), tmp._2) + case (Some(v), Fld(flags, trm)) => val nTrm = liftTermAsType(trm) - ((Some(v), Fld(b1, b2, nTrm._1)), nTrm._2) + ((Some(v), Fld(flags, nTrm._1)), nTrm._2) }.unzip (Tup(ret._1), ret._2.fold(emptyCtx)(_ ++ _)) } - private def liftConstr(tp: TypeName, prm: Tup)(using ctx: LocalContext, cache: ClassCache, outer: Option[ClassInfoCache]): (TypeName, Tup, LocalContext) = { + private def liftConstr(tp: TypeName, prm: Tup)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (TypeName, Tup, LocalContext) = { def findAncestor(crt: ClassInfoCache, target: Option[ClassInfoCache]): Option[(List[String], Option[String])] = { (crt.outerCls, target) match{ case (None, None) => None @@ -290,8 +298,13 @@ class ClassLifter(logDebugMsg: Boolean = false) { } } - private def liftTerm(target: Term)(using ctx: LocalContext, cache: ClassCache, outer: Option[ClassInfoCache]): (Term, LocalContext) = target match { - case v: Var => + private def newLambObj(lhs: Term, rhs: Term) = + New(None, TypingUnit(List(NuFunDef(None, Var("apply"), None, Nil, Left(Lam(lhs, rhs)))(N, N, N, N, false)))) //TODO: Use Proper Arguments + + private def liftTerm(target: Term)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (Term, LocalContext) = + log(s"liftTermNew $target in $ctx, $cache, $globFuncs, $outer") + target match { + case v: Var => if(ctx.contains(v) || v.name.equals("this") || primiTypes.contains(v.name)) (v, emptyCtx) else if(cache.contains(TypeName(v.name))){ val ret = liftConstr(TypeName(v.name), Tup(Nil)) @@ -303,28 +316,49 @@ class ClassLifter(logDebugMsg: Boolean = false) { case None => (v, asContext(v)) } } - case Lam(lhs, rhs) => - val lctx = getFreeVars(lhs)(using emptyCtx, cache, None) - val (ltrm, _) = liftTerm(lhs)(using ctx.addV(lctx.vSet)) - val (rtrm, rctx) = liftTerm(rhs)(using ctx.addV(lctx.vSet)) - (Lam(ltrm, rtrm), rctx -+ lctx) - case t: Tup => + case Lam(lhs, rhs) => + val prmCnt = getFreeVars(lhs)(using emptyCtx, cache, globFuncs, None).vSet.size + val nTpNm = TypeName(genAnoName("Lambda"+prmCnt)) + val anoCls = NuTypeDef( + Cls, nTpNm, Nil, S(Tup(Nil)), N, N, Nil, N, N, + TypingUnit(List(NuFunDef(None, Var("apply"), N, Nil, Left(Lam(lhs, rhs)))(N, N, N, N, false))))(N, N) //TODO: Use Proper Arguments + val nSta = New(Some((nTpNm, Tup(Nil))), TypingUnit(Nil)) + val ret = liftEntities(List(anoCls, nSta)) + (Blk(ret._1), ret._2) + case t: Tup => liftTuple(t) case Rcd(fields) => val ret = fields.map{ - case (v, Fld(b1, b2, trm)) => + case (v, Fld(flags, trm)) => val tmp = liftTermAsType(trm) - ((v, Fld(b1, b2, tmp._1)), tmp._2) + ((v, Fld(flags, tmp._1)), tmp._2) }.unzip (Rcd(ret._1), ret._2.fold(emptyCtx)(_ ++ _)) case Asc(trm, ty) => val ret = liftTerm(trm) val nTy = liftType(ty) (Asc(ret._1, nTy._1), ret._2 ++ nTy._2) + case NuNew(cls) => + liftTerm(App(NuNew(cls), Tup(Nil))) + case App(NuNew(cls), args) => + liftTerm(Rft(App(NuNew(cls), args), TypingUnit(Nil))) + case Rft(NuNew(cls), tu) => + liftTerm(Rft(App(NuNew(cls), Tup(Nil)), TypingUnit(Nil))) + case Rft(App(NuNew(cls), args), tu) => + (cls, args) match { + case (v: Var, args: Tup) => + liftTerm(New(Some((TypeName(v.name), args)), tu)) + case _ => ??? + } case App(v: Var, prm: Tup) if cache.contains(TypeName(v.name)) => val ret = liftConstr(TypeName(v.name), prm) (App(Var(ret._1.name), ret._2), ret._3) - case App(lhs, rhs) => + case App(v: Var, prm: Tup) if globFuncs.contains(v) => + val (nFuncName, nCtxs) = globFuncs.get(v).get + val addiArgs = nCtxs.vSet.toList.map(toFldsEle(_)) + val nPrm = liftTuple(prm) + (App(nFuncName, Tup(nPrm._1.fields ++ addiArgs)), nPrm._2) + case App(lhs, rhs) => val (ltrm, lctx) = liftTerm(lhs) val (rtrm, rctx) = liftTerm(rhs) (App(ltrm, rtrm), lctx ++ rctx) @@ -357,7 +391,7 @@ class ClassLifter(logDebugMsg: Boolean = false) { case Sel(receiver, fieldName) => val nRec = liftTerm(receiver) (Sel(nRec._1, fieldName), nRec._2) - case Splc(fields) => ??? + case Splc(fields) => throw MonomorphError(s"Unimplemented liftTerm: ${target}") case Subs(arr, idx) => val (ltrm, lctx) = liftTerm(arr) val (rtrm, rctx) = liftTerm(idx) @@ -370,7 +404,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) => ??? + case With(trm, fields) => throw MonomorphError(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) @@ -378,18 +412,19 @@ class ClassLifter(logDebugMsg: Boolean = false) { log(s"new $t in ctx $ctx, $cache, $outer") val nTpNm = TypeName(genAnoName(t.name)) val cls = cache.get(t).get - val supArgs = Tup(cls.body.params.fields.flatMap(tupleEntityToVar).map(toFldsEle)) - val anoCls = NuTypeDef(Cls, nTpNm, Nil, cls.body.params, List(App(Var(t.name), supArgs)), tu) + val supArgs = Tup(cls.body.params.fold(Nil)(t => t.fields).flatMap(tupleEntityToVar).map(toFldsEle)) + val anoCls = NuTypeDef(Cls, nTpNm, Nil, cls.body.params, None, None, + List(App(Var(t.name), supArgs)), None, None, tu)(None, None) val nSta = New(Some((nTpNm, prm)), TypingUnit(Nil)) val ret = liftEntities(List(anoCls, nSta)) (Blk(ret._1), ret._2) case New(None, tu) => val nTpNm = TypeName(genAnoName()) - val anoCls = NuTypeDef(Cls, nTpNm, Nil, Tup(Nil), Nil, tu) + val anoCls = NuTypeDef(Cls, nTpNm, Nil, None, None, None, Nil, None, None, tu)(None, None) val nSta = New(Some((nTpNm, Tup(Nil))), TypingUnit(Nil)) val ret = liftEntities(List(anoCls, nSta)) (Blk(ret._1), ret._2) - case New(head, body) => ??? + case New(head, body) => throw MonomorphError(s"Unimplemented liftTerm: ${target}") case Blk(stmts) => val ret = liftEntities(stmts) (Blk(ret._1), ret._2) @@ -404,11 +439,12 @@ class ClassLifter(logDebugMsg: Boolean = false) { val (bod2, ctx) = liftTerm(bod) val (sts2, ctx2) = liftEntities(sts) (Where(bod2, sts2), ctx2) - case patmat: AdtMatchWith => lastWords(s"Cannot liftTerm ${patmat}") + case _: Eqn | _: Super | _: Rft => throw MonomorphError(s"Unimplemented liftTerm: ${target}") // TODO + case patmat: AdtMatchWith => lastWords(s"Cannot liftTermNew ${patmat}") } //serves for lifting Tup(Some(_), Fld(_, _, trm)), where trm refers to a type - private def liftTermAsType(target: Term)(using ctx: LocalContext, cache: ClassCache, outer: Option[ClassInfoCache]): (Term, LocalContext) = + private def liftTermAsType(target: Term)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (Term, LocalContext) = log(s"liftTermAsType $target in $ctx, $cache") target match{ case v: Var => @@ -425,9 +461,9 @@ class ClassLifter(logDebugMsg: Boolean = false) { TyApp(lret._1, tRets._1) -> (tRets._2.fold(lret._2)(_ ++ _)) case Tup(fields) => val ret = fields.map{ - case (oV, Fld(b1, b2, trm)) => + case (oV, Fld(flags, trm)) => val tmp = liftTermAsType(trm) - (oV, Fld(b1, b2, tmp._1)) -> tmp._2 + (oV, Fld(flags, tmp._1)) -> tmp._2 }.unzip Tup(ret._1) -> ret._2.fold(emptyCtx)(_ ++ _) case Bra(rcd, trm) => @@ -435,29 +471,29 @@ class ClassLifter(logDebugMsg: Boolean = false) { Bra(rcd, ret._1) -> ret._2 case Rcd(fields) => val ret = fields.map{ - case (v, Fld(b1, b2, trm)) => + case (v, Fld(flags, trm)) => val tmp = liftTermAsType(trm) - ((v, Fld(b1, b2, tmp._1)), tmp._2) + ((v, Fld(flags, tmp._1)), tmp._2) }.unzip (Rcd(ret._1), ret._2.fold(emptyCtx)(_ ++ _)) - case _ => ??? + case _ => throw MonomorphError(s"Unimplemented liftTermAsType: ${target}") } - private def liftTypeName(target: TypeName)(using ctx: LocalContext, cache: ClassCache, outer: Option[ClassInfoCache]): (TypeName, LocalContext) = { + private def liftTypeName(target: TypeName)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (TypeName, LocalContext) = { if(ctx.contains(target) || primiTypes.contains(target.name)) { target -> emptyCtx } else { cache.get(target).map(x => (x.liftedNm -> emptyCtx)).getOrElse(target -> asContext(target)) } } - private def liftTypeField(target: Field)(using ctx: LocalContext, cache: ClassCache, outer: Option[ClassInfoCache]): (Field, LocalContext) = { + private def liftTypeField(target: Field)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (Field, LocalContext) = { val (inT, iCtx) = target.in.map(liftType).unzip val (outT, oCtx) = liftType(target.out) Field(inT, outT) -> (iCtx.getOrElse(emptyCtx) ++ oCtx) } - private def liftType(target: Type)(using ctx: LocalContext, cache: ClassCache, outer: Option[ClassInfoCache]): (Type, LocalContext) = target match{ - case AppliedType(base, targs) => + private def liftType(target: Type)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (Type, LocalContext) = target match{ + case AppliedType(base, targs) => val (nTargs, nCtx) = targs.map(liftType).unzip val (nBase, bCtx) = liftTypeName(base) AppliedType(nBase, nTargs) -> (nCtx.fold(emptyCtx)(_ ++ _) ++ bCtx) @@ -465,7 +501,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, bounds, where) => + case Constrained(base: Type, bounds, where) => val (nTargs, nCtx) = bounds.map { case (tv, Bounds(lb, ub)) => val nlhs = liftType(lb) val nrhs = liftType(ub) @@ -479,6 +515,7 @@ class ClassLifter(logDebugMsg: Boolean = false) { val (nBase, bCtx) = liftType(base) Constrained(nBase, nTargs, bounds2) -> ((nCtx ++ nCtx2).fold(emptyCtx)(_ ++ _) ++ bCtx) + case Constrained(_, _, _) => die case Function(lhs, rhs) => val nlhs = liftType(lhs) val nrhs = liftType(rhs) @@ -532,26 +569,65 @@ class ClassLifter(logDebugMsg: Boolean = false) { case PolyType(targs, body) => val (body2, ctx) = liftType(body) PolyType(targs, body2) -> ctx - case Top | Bot | _: Literal | _: TypeTag | _: TypeVar => target -> emptyCtx + case Top | Bot | _: Literal | _: TypeTag | _: TypeVar => target.asInstanceOf[Type] -> emptyCtx + case _: Selection => throw MonomorphError(s"Unimplemented liftType: ${target}") // TODO } - private def liftFunc(func: NuFunDef)(using ctx: LocalContext, cache: ClassCache, outer: Option[ClassInfoCache]): (NuFunDef, LocalContext) = { - log(s"liftFunc $func under $ctx # $cache # $outer") - val NuFunDef(rec, nm, tpVs, body) = func + private def liftMemberFunc(func: NuFunDef)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (NuFunDef, LocalContext) = { + log(s"liftMemberFunc $func under $ctx # $cache # $globFuncs # $outer") + val NuFunDef(rec, nm, sn, tpVs, body) = func body match{ - case Left(value) => - val ret = liftTerm(value)(using ctx.addV(nm).addT(tpVs)) - (func.copy(rhs = Left(ret._1)), ret._2) - case Right(PolyType(targs, body)) => + case Left(Lam(lhs@Tup(etts), rhs)) => + val lctx = getFreeVars(lhs)(using emptyCtx, cache, globFuncs, None) + val lret = liftTuple(lhs)(using ctx.addV(lctx.vSet)) + val ret = liftTerm(rhs)(using ctx.addV(lctx.vSet).addT(tpVs)) + (func.copy(rhs = Left(Lam(lret._1, ret._1)))(func.declareLoc, func.virtualLoc, func.signature, func.outer, func.genField), ret._2 -+ lret._2) //TODO: Check correctness + case Left(value) => + // will be treated as Lam(Tup(Nil), rhs) + val ret = liftTerm(value)(using ctx.addT(tpVs)) + (func.copy(rhs = Left(Lam(Tup(Nil), ret._1)))(func.declareLoc, func.virtualLoc, func.signature, func.outer, func.genField), ret._2) //TODO: Check correctness + case Right(PolyType(targs, body)) => val nBody = liftType(body)(using ctx.addT(tpVs)) val nTargs = targs.map { case L(tp) => liftTypeName(tp)(using ctx.addT(tpVs)).mapFirst(Left.apply) case R(tv) => R(tv) -> emptyCtx }.unzip - (func.copy(rhs = Right(PolyType(nTargs._1, nBody._1))), nTargs._2.fold(nBody._2)(_ ++ _)) + (func.copy(rhs = Right(PolyType(nTargs._1, nBody._1)))(func.declareLoc, func.virtualLoc, func.signature, func.outer, func.genField), + nTargs._2.fold(nBody._2)(_ ++ _)) + case _ => throw MonomorphError(s"Unimplemented liftMemberFunc: ${func}") // TODO } } + + private def liftGlobalFunc(func: NuFunDef)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): Unit = { + log(s"liftGlobalFunc $func under $ctx # $cache # $globFuncs # $outer") + val NuFunDef(rec, nm, _, tpVs, body) = func + val nTpVs = tpVs ++ globFuncs.get(nm).get._2.tSet.toList + globalFunctions.addOne(body match{ + case Left(Lam(lhs@Tup(etts), rhs)) => + val tmp = globFuncs.get(nm).get._2.vSet.toList.map(toFldsEle) + val lctx = getFreeVars(lhs)(using emptyCtx, cache, globFuncs, None) + val lret = liftTuple(lhs)(using ctx.addV(lctx.vSet) ++ globFuncs.get(nm).get._2, cache, globFuncs) + val ret = liftTerm(rhs)(using ctx.addV(lctx.vSet) ++ globFuncs.get(nm).get._2, cache, globFuncs) + NuFunDef(rec, globFuncs.get(nm).get._1, N, nTpVs, Left(Lam(Tup(lret._1.fields ++ tmp), ret._1)))(N, N, N, N, true) //TODO: Use proper arguments + case Left(rhs) => + // will be treated as Lam(Tup(Nil), rhs) + val tmp = globFuncs.get(nm).get._2.vSet.toList.map(toFldsEle) + val ret = liftTerm(rhs)(using ctx ++ globFuncs.get(nm).get._2, cache, globFuncs) + NuFunDef(rec, globFuncs.get(nm).get._1, N, nTpVs, Left(Lam(Tup(tmp), ret._1)))(N, N, N, N, true) //TODO: Use proper arguments + // val ret = liftTermNew(value)(using ctx.addV(nm) ++ globFuncs.get(nm).get._2, cache, globFuncs) + // NuFunDef(rec, globFuncs.get(nm).get._1, nTpVs, Left(ret._1)) + case Right(PolyType(targs, body)) => + val nBody = liftType(body)(using ctx ++ globFuncs.get(nm).get._2, cache, globFuncs, None) + val nTargs = targs.map({ + case L(tn) => + liftTypeName(tn)(using ctx.addT(nTpVs), cache, globFuncs, None) match + 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, true) //TODO: Use proper arguments + case _ => throw MonomorphError(s"Unimplemented liftGlobalFunc: ${func}") + }) + } private def grepFieldsInTrm(trm: Term): Option[Var] = trm match{ @@ -559,35 +635,48 @@ class ClassLifter(logDebugMsg: Boolean = false) { case _ => None } - private def mixClsInfos(clsInfos: Map[TypeName, ClassInfoCache], newClsNms: Set[Var])(using cache: ClassCache): Map[TypeName, ClassInfoCache] = { - val nameInfoMap: MutMap[TypeName, ClassInfoCache] = MutMap(clsInfos.toSeq: _*) - log(s"mix cls infos $nameInfoMap") + private def mixClsInfos(clsInfos: Map[String, ClassInfoCache], funcInfos: Map[String, LocalContext])(using cache: ClassCache): (Map[String, ClassInfoCache], Map[String, LocalContext]) = { + val nameInfoMap: MutMap[String, ClassInfoCache] = MutMap(clsInfos.toSeq: _*) + val nameFuncMap: MutMap[String, LocalContext] = MutMap(funcInfos.toSeq: _*) + log(s"mix cls infos $nameInfoMap, $nameFuncMap") // val fullMp = cache ++ nameInfoMap - val clsNmsAsTypeNm = newClsNms.map(x => TypeName(x.name)) - val len = clsInfos.size - for(_ <- 1 to len){ - val tmp = nameInfoMap.toList - tmp.foreach{case (nmOfCls, infoOfCls@ClassInfoCache(_, _, ctx, flds, inners, sups, _, _, _)) => { - val usedClsNmList = ctx.vSet.map(x => TypeName(x.name)).intersect(clsNmsAsTypeNm) - val newCtxForCls = usedClsNmList.foldLeft(ctx)((c1, c2) => c1 ++ nameInfoMap.get(c2).get.capturedParams) + val clsNmsAsTypeNm = clsInfos.keySet.map(x => TypeName(x)) + val len = clsInfos.size + nameFuncMap.size + for(_ <- 0 to len){ + nameInfoMap.toList.foreach{case (nmOfCls, infoOfCls@ClassInfoCache(_, _, ctx, flds, inners, sups, _, _, _)) => { + val usedClsNmList = ctx.vSet.map(_.name).intersect(clsInfos.keySet) + val newCtxForCls_tmp = usedClsNmList.foldLeft(ctx)((c1, c2) => c1 ++ nameInfoMap.get(c2).get.capturedParams) + + val usedFuncNmList = ctx.vSet.map(_.name).intersect(funcInfos.keySet) + val newCtxForCls = usedFuncNmList.foldLeft(newCtxForCls_tmp)((c, x) => c ++ nameFuncMap.get(x).get) + val supClsNmList = infoOfCls.supClses - val newFields = supClsNmList.foreach(c2 => flds.addAll( - nameInfoMap.get(c2).map(_.fields).getOrElse(cache.get(c2).map(_.fields).getOrElse(Nil)) + supClsNmList.foreach(c2 => flds.addAll( + nameInfoMap.get(c2.name).map(_.fields).getOrElse(cache.get(c2).map(_.fields).getOrElse(Nil)) )) - val newInners = supClsNmList.foreach(c2 => inners.addAll( - nameInfoMap.get(c2).map(_.innerClses).getOrElse(cache.get(c2).map(_.innerClses).getOrElse(Nil)) + supClsNmList.foreach(c2 => inners.addAll( + nameInfoMap.get(c2.name).map(_.innerClses).getOrElse(cache.get(c2).map(_.innerClses).getOrElse(Nil)) )) - val newCtxFromSup = supClsNmList.map(c2 => - nameInfoMap.get(c2).map(_.capturedParams).getOrElse(cache.get(c2).map(_.capturedParams).getOrElse(emptyCtx)) + val newCtxFromSup = supClsNmList.map(c2 => + nameInfoMap.get(c2.name).map(_.capturedParams).getOrElse(cache.get(c2).map(_.capturedParams).getOrElse(emptyCtx)) ).fold(emptyCtx)(_ ++ _) infoOfCls.capturedParams = newCtxForCls ++ newCtxFromSup }} + nameFuncMap.toList.foreach((nm, ctx) => { + val usedClsNmList = ctx.vSet.map(_.name).intersect(clsInfos.keySet) + val usedFuncNmList = ctx.vSet.map(_.name).intersect(funcInfos.keySet) + val nCtx = (usedClsNmList.map(x => nameInfoMap.get(x).get.capturedParams) ++ usedFuncNmList.map(x => nameFuncMap.get(x).get)).foldLeft(ctx)(_ ++ _) + nameFuncMap.update(nm, nCtx) + }) } - nameInfoMap.foreach((x1, x2) => x2.capturedParams = (x2.capturedParams extV newClsNms).extT(x2.innerClses.keySet)) - nameInfoMap.toMap + nameInfoMap.foreach((x1, x2) => x2.capturedParams = (x2.capturedParams.extV(clsInfos.keySet.map(Var(_)))).extV(funcInfos.keySet.map(Var(_))).extT(x2.innerClses.keySet)) + nameFuncMap.toList.foreach((x, c) => nameFuncMap.update(x, c.extV(clsInfos.keySet.map(Var(_))).extV(funcInfos.keySet.map(Var(_))))) + log(s"mix result: $nameInfoMap, $nameFuncMap") + nameInfoMap.toMap -> nameFuncMap.toMap } + - private def liftEntities(etts: List[Statement])(using ctx: LocalContext, cache: ClassCache, outer: Option[ClassInfoCache]): (List[Statement], LocalContext) = { + private def liftEntities(etts: List[Statement])(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (List[Statement], LocalContext) = { log("liftEntities: " ++ etts.headOption.map(_.toString()).getOrElse("")) val (newCls, newFuncs, rstTrms) = splitEntities(etts) val newClsNms = newCls.map(x => Var(x.nme.name)).toSet @@ -595,63 +684,75 @@ class ClassLifter(logDebugMsg: Boolean = false) { val nmsInTrm = rstTrms.flatMap(grepFieldsInTrm) val clsInfos = newCls.map(x => { val infos = collectClassInfo(x, newCls.map(_.nme).toSet)(using emptyCtx) - infos.capturedParams = infos.capturedParams.copy(vSet = infos.capturedParams.vSet.intersect(ctx.vSet ++ newClsNms ++ newFuncNms ++ nmsInTrm)) - x.nme -> infos}).toMap + infos.capturedParams = infos.capturedParams.intersect(ctx.addT(infos.capturedParams.tSet).addV(newClsNms ++ newFuncNms ++ nmsInTrm -- globFuncs.keySet ++ outer.map(_ => Var("this")))) + x.nme.name -> infos}).toMap + val funcInfos = + newFuncs.map(x => x.nme.name -> (x.rhs match { + case Left(trm) => getFreeVars(trm)(using emptyCtx) + .intersect(ctx.addV(newClsNms ++ newFuncNms ++ nmsInTrm -- globFuncs.keySet ++ outer.map(_ => Var("this")))) + .extT(x.tparams) + case _ => emptyCtx}) + ).toMap log("captured cls infos: \n" ++ clsInfos.toString()) - val refinedInfo = mixClsInfos(clsInfos, newClsNms) - val newCache = cache ++ refinedInfo - refinedInfo.foreach((_, clsi) => completeClsInfo(clsi)(using newCache)) - - newCls.foreach(x => liftTypeDefNew(x)(using newCache)) - val (liftedFuns, funVs) = newFuncs.map(liftFunc(_)(using ctx.addV(newFuncNms), newCache)).unzip - val (liftedTerms, termVs) = rstTrms.map(liftTerm(_)(using ctx.addV(newFuncNms), newCache)).unzip - (liftedFuns ++ liftedTerms, (funVs ++ termVs).fold(emptyCtx)(_ ++ _)) + log("captured func infos: \n" ++ funcInfos.toString()) + val (refinedClsInfo, refinedFuncInfo) = mixClsInfos(clsInfos, funcInfos) + val newCache = cache ++ refinedClsInfo.map(x => (TypeName(x._1) -> x._2)) + refinedClsInfo.foreach((_, clsi) => completeClsInfo(clsi)(using newCache)) + val newGlobalFuncs = refinedFuncInfo.map((nm, vs) => (Var(nm) -> (Var(genAnoName(nm)), vs))) + + newCls.foreach(x => liftTypeDef(x)(using newCache, globFuncs ++ newGlobalFuncs)) + (newFuncs zip refinedFuncInfo).foreach((f, c) => liftGlobalFunc(f)(using ctx, newCache, globFuncs ++ newGlobalFuncs)) + val (liftedTerms, termVs) = rstTrms.map(liftTerm(_)(using ctx.addV(newFuncNms), newCache, globFuncs ++ newGlobalFuncs)).unzip + (liftedTerms, (termVs).fold(emptyCtx)(_ ++ _)) } - private def completeClsInfo(clsInfo: ClassInfoCache)(using cache: ClassCache): Unit = { + private def completeClsInfo(clsInfo: ClassInfoCache)(using cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)]): Unit = { val ClassInfoCache(_, nName, freeVs, flds, inners, _, _, cls, _) = clsInfo val (clsList, _, _) = splitEntities(cls.body.entities) val innerClsNmSet = clsList.map(_.nme).toSet - val innerClsInfos = clsList.map(x => x.nme -> collectClassInfo(x, innerClsNmSet)(using asContextV(freeVs.vSet ++ flds), cache, Some(clsInfo))).toMap - val refinedInfos = mixClsInfos(innerClsInfos, innerClsNmSet.map(x => Var(x.name))) + val innerClsInfos = clsList.map(x => x.nme.name -> collectClassInfo(x, innerClsNmSet)(using asContextV(freeVs.vSet ++ flds), cache, globFuncs, Some(clsInfo))).toMap + val refinedInfos = mixClsInfos(innerClsInfos, Map())._1.map(x => (TypeName(x._1) -> x._2)) refinedInfos.foreach((_, info) => completeClsInfo(info)(using cache ++ refinedInfos)) inners.addAll(refinedInfos) } - private def liftTypeDefNew(target: NuTypeDef)(using cache: ClassCache, outer: Option[ClassInfoCache]): Unit = { + private def liftTypeDef(target: NuTypeDef)(using cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): Unit = { def getAllInners(sups: Set[TypeName]): ClassCache = { sups.flatMap( t => cache.get(t).map(x => getAllInners(x.supClses) ++ x.innerClses) ).flatten.toMap } log("lift type " + target.toString() + " with cache " + cache.toString()) - val NuTypeDef(kind, nme, tps, params, pars, body) = target + val NuTypeDef(kind, nme, tps, params, _, sig, pars, supAnn, thisAnn, body) = target val nOuter = cache.get(nme) val ClassInfoCache(_, nName, freeVs, flds, inners, sups, _, _, _) = nOuter.get val (clsList, funcList, termList) = splitEntities(body.entities) val innerNmsSet = clsList.map(_.nme).toSet val nCache = cache ++ inners ++ getAllInners(sups) - val nTps = (tps ++ (freeVs.tSet -- nCache.keySet).toList).distinct + val nTps = (tps.map(_._2) ++ (freeVs.tSet -- nCache.keySet).toList).distinct val nCtx = freeVs.addT(nTps) val nParams = outer.map(x => List(toFldsEle(Var(genParName(x.liftedNm.name))))).getOrElse(Nil) - ++ params.fields + ++ params.fold(Nil)(t => t.fields) ++ freeVs.vSet.map(toFldsEle) - val nPars = pars.map(liftTerm(_)(using emptyCtx, nCache, nOuter)).unzip - val nFuncs = funcList.map(liftFunc(_)(using emptyCtx, nCache, nOuter)).unzip - val nTerms = termList.map(liftTerm(_)(using emptyCtx, nCache, nOuter)).unzip - clsList.foreach(x => liftTypeDefNew(x)(using nCache, nOuter)) - retSeq = retSeq.appended(NuTypeDef(kind, nName, nTps, Tup(nParams), nPars._1, TypingUnit(nFuncs._1 ++ nTerms._1))) + val nPars = pars.map(liftTerm(_)(using emptyCtx, nCache, globFuncs, nOuter)).unzip + val nFuncs = funcList.map(liftMemberFunc(_)(using emptyCtx, nCache, globFuncs, nOuter)).unzip + val nTerms = termList.map(liftTerm(_)(using emptyCtx, nCache, globFuncs, nOuter)).unzip + clsList.foreach(x => liftTypeDef(x)(using nCache, globFuncs, nOuter)) + retSeq = retSeq.appended(NuTypeDef( + kind, nName, nTps.map((None, _)), S(Tup(nParams)), None, None, nPars._1, + None, None, TypingUnit(nFuncs._1 ++ nTerms._1))(None, None)) } def liftTypingUnit(rawUnit: TypingUnit): TypingUnit = { log("=========================\n") - log(s"lifting: \n${showStructure(rawUnit)}\n") + log(s"lifting: \n$rawUnit\n") retSeq = Nil - val re = liftEntities(rawUnit.entities)(using emptyCtx, Map(), None) + globalFunctions.clear() + val re = liftEntities(rawUnit.entities)(using emptyCtx, Map(), Map(), None) log(s"freeVars: ${re._2}") // println(logOutput.toString()) - TypingUnit(retSeq.toList++re._1) + TypingUnit(retSeq.toList ++ globalFunctions.toList ++ re._1) } } diff --git a/compiler/shared/main/scala/mlscript/compiler/DataType.scala b/compiler/shared/main/scala/mlscript/compiler/DataType.scala new file mode 100644 index 0000000000..ca3ba27b08 --- /dev/null +++ b/compiler/shared/main/scala/mlscript/compiler/DataType.scala @@ -0,0 +1,36 @@ +package mlscript.compiler + +abstract class DataType + +object DataType: + sealed class Singleton(value: Expr.Literal, dataType: DataType) extends DataType: + override def toString(): String = value.toString() + + enum Primitive(name: String) extends DataType: + case Integer extends Primitive("int") + case Decimal extends Primitive("real") + case Boolean extends Primitive("bool") + case String extends Primitive("str") + override def toString(): String = this.name + end Primitive + + sealed case class Tuple(elementTypes: List[DataType]) extends DataType: + override def toString(): String = elementTypes.mkString("(", ", ", ")") + + sealed case class Class(declaration: Item.TypeDecl) extends DataType: + override def toString(): String = s"class ${declaration.name.name}" + + sealed case class Function(parameterTypes: List[DataType], returnType: DataType) extends DataType: + def this(returnType: DataType, parameterTypes: DataType*) = + this(parameterTypes.toList, returnType) + override def toString(): String = + val parameterList = parameterTypes.mkString("(", ", ", ")") + s"$parameterList -> $returnType" + + sealed case class Record(fields: Map[String, DataType]) extends DataType: + def this(fields: (String, DataType)*) = this(Map.from(fields)) + override def toString(): String = + fields.iterator.map { (name, ty) => s"$name: $ty" }.mkString("{", ", ", "}") + + case object Unknown extends DataType: + override def toString(): String = "unknown" diff --git a/compiler/shared/main/scala/mlscript/compiler/DataTypeInferer.scala b/compiler/shared/main/scala/mlscript/compiler/DataTypeInferer.scala new file mode 100644 index 0000000000..117a8a3b75 --- /dev/null +++ b/compiler/shared/main/scala/mlscript/compiler/DataTypeInferer.scala @@ -0,0 +1,18 @@ +package mlscript.compiler +import mlscript.compiler.mono.MonomorphError + +trait DataTypeInferer: + import DataType._ + + def findClassByName(name: String): Option[Item.TypeDecl] + + def infer(expr: Expr, compatiableType: Option[DataType]): DataType = + expr match + case Expr.Tuple(elements) => DataType.Tuple(elements.map(infer(_, None))) + case lit @ Expr.Literal(value: BigInt) => Singleton(lit, Primitive.Integer) + case lit @ Expr.Literal(value: BigDecimal) => Singleton(lit, Primitive.Decimal) + case lit @ Expr.Literal(value: String) => Singleton(lit, Primitive.String) + case lit @ Expr.Literal(value: Boolean) => Singleton(lit, Primitive.Boolean) + case Expr.Apply(Expr.Ref(name), args) => + findClassByName(name).fold(DataType.Unknown)(DataType.Class(_)) + case _ => throw MonomorphError(s"I can't infer the type of $expr now") \ No newline at end of file diff --git a/compiler/shared/main/scala/mlscript/compiler/Helpers.scala b/compiler/shared/main/scala/mlscript/compiler/Helpers.scala new file mode 100644 index 0000000000..41515a772f --- /dev/null +++ b/compiler/shared/main/scala/mlscript/compiler/Helpers.scala @@ -0,0 +1,177 @@ +package mlscript +package compiler + +import mlscript.compiler.mono.{Monomorph, MonomorphError} +import scala.collection.mutable.ArrayBuffer + +object Helpers: + /** + * Extract parameters for monomorphization from a `Tup`. + */ + def toFuncParams(term: Term): Iterator[Parameter] = term match + case Tup(fields) => fields.iterator.flatMap { + // The new parser emits `Tup(_: UnitLit(true))` from `fun f() = x`. + case (_, Fld(FldFlags(_, _, _), UnitLit(true))) => None + case (None, Fld(FldFlags(_, spec, _), Var(name))) => Some((spec, Expr.Ref(name))) + case (Some(Var(name)), Fld(FldFlags(_, spec, _), _)) => Some((spec, Expr.Ref(name))) + case _ => throw new MonomorphError( + s"only `Var` can be parameters but we meet $term" + ) + } + case _ => throw MonomorphError("expect the list of parameters to be a `Tup`") + + 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 term2Expr(term: Term): Expr = { + term match + case Var(name) => Expr.Ref(name) + case Lam(lhs, rhs) => + val params = toFuncParams(lhs).toList + Expr.Lambda(params, term2Expr(rhs)) + case App(App(Var("=>"), Bra(false, args: Tup)), body) => + val params = toFuncParams(args).toList + Expr.Lambda(params, term2Expr(body)) + case App(App(Var("."), self), App(Var(method), args: Tup)) => + Expr.Apply(Expr.Select(term2Expr(self), Expr.Ref(method)), List.from(toFuncArgs(args).iterator.map(term2Expr))) + case App(lhs, rhs) => + val callee = term2Expr(lhs) + val arguments = toFuncArgs(rhs).iterator.map(term2Expr).toList + Expr.Apply(callee, arguments) + case Tup(fields) => + Expr.Tuple(fields.map { + case (_, Fld(FldFlags(mut, spec, genGetter), value)) => term2Expr(value) + }) + case Rcd(fields) => + Expr.Record(fields.map { + case (name, Fld(FldFlags(mut, spec, genGetter), value)) => (Expr.Ref(name.name), term2Expr(value)) + }) + case Sel(receiver, fieldName) => + Expr.Select(term2Expr(receiver), Expr.Ref(fieldName.name)) + case Let(rec, Var(name), rhs, body) => + val exprRhs = term2Expr(rhs) + val exprBody = term2Expr(body) + Expr.LetIn(rec, Expr.Ref(name), exprRhs, exprBody) + case Blk(stmts) => Expr.Block(stmts.flatMap[Expr | Item.FuncDecl | Item.FuncDefn] { + case term: Term => Some(term2Expr(term)) + case tyDef: NuTypeDef => throw MonomorphError(s"Unimplemented term2Expr ${term}") + case funDef: NuFunDef => + val NuFunDef(_, nme, sn, targs, rhs) = funDef + val ret: Item.FuncDecl | Item.FuncDefn = rhs match + case Left(Lam(params, body)) => + Item.FuncDecl(Expr.Ref(nme.name), toFuncParams(params).toList, term2Expr(body)) + case Left(body: Term) => Item.FuncDecl(Expr.Ref(nme.name), Nil, term2Expr(body)) + case Right(tp) => Item.FuncDefn(Expr.Ref(nme.name), targs, PolyType(Nil, tp)) //TODO: Check correctness in Type -> Polytype conversion + Some(ret) + case mlscript.DataDefn(_) => throw MonomorphError("unsupported DataDefn") + case mlscript.DatatypeDefn(_, _) => throw MonomorphError("unsupported DatatypeDefn") + case mlscript.TypeDef(_, _, _, _, _, _, _, _) => throw MonomorphError("unsupported TypeDef") + case mlscript.Def(_, _, _, _) => throw MonomorphError("unsupported Def") + case mlscript.LetS(_, _, _) => throw MonomorphError("unsupported LetS") + case mlscript.Constructor(_, _) => throw MonomorphError("unsupported Constructor") + }) + case Bra(rcd, term) => term2Expr(term) + case Asc(term, ty) => Expr.As(term2Expr(term), ty) + case _: Bind => throw MonomorphError("cannot monomorphize `Bind`") + case _: Test => throw MonomorphError("cannot monomorphize `Test`") + case With(term, Rcd(fields)) => + Expr.With(term2Expr(term), Expr.Record(fields.map { + case (name, Fld(FldFlags(mut, spec, getGetter), value)) => (Expr.Ref(name.name), term2Expr(term)) + })) + case CaseOf(term, cases) => + def rec(bra: CaseBranches)(using buffer: ArrayBuffer[CaseBranch]): Unit = bra match + case Case(pat, body, rest) => + val newCase = pat match + case Var(name) => CaseBranch.Instance(Expr.Ref(name), Expr.Ref("_"), term2Expr(body)) + case DecLit(value) => CaseBranch.Constant(Expr.Literal(value), term2Expr(body)) + case IntLit(value) => CaseBranch.Constant(Expr.Literal(value), term2Expr(body)) + case StrLit(value) => CaseBranch.Constant(Expr.Literal(value), term2Expr(body)) + case UnitLit(undefinedOrNull) => CaseBranch.Constant(Expr.Literal(UnitValue.Undefined), term2Expr(body)) + buffer.addOne(newCase) + rec(rest) + case NoCases => () + case Wildcard(body) => + buffer.addOne(CaseBranch.Wildcard(term2Expr(body))) + val branchBuffer = ArrayBuffer[CaseBranch]() + rec(cases)(using branchBuffer) + Expr.Match(term2Expr(term), branchBuffer) + + case Subs(array, index) => + Expr.Subscript(term2Expr(array), term2Expr(index)) + case Assign(lhs, rhs) => + Expr.Assign(term2Expr(lhs), term2Expr(rhs)) + case New(None, body) => + throw MonomorphError(s"Unimplemented term2Expr ${term}") + case New(Some((constructor, args)), body) => + val typeName = constructor match + case AppliedType(TypeName(name), _) => name + case TypeName(name) => name + Expr.New(TypeName(typeName), toFuncArgs(args).iterator.map(term2Expr).toList) + // case Blk(unit) => Expr.Isolated(trans2Expr(TypingUnit(unit))) + case If(body, alternate) => body match + case IfThen(condition, consequent) => + Expr.IfThenElse( + term2Expr(condition), + term2Expr(consequent), + alternate.map(term2Expr) + ) + case term: IfElse => throw MonomorphError("unsupported IfElse") + case term: IfLet => throw MonomorphError("unsupported IfLet") + case term: IfOpApp => throw MonomorphError("unsupported IfOpApp") + case term: IfOpsApp => throw MonomorphError("unsupported IfOpsApp") + case term: IfBlock => throw MonomorphError("unsupported IfBlock") + case IntLit(value) => Expr.Literal(value) + case DecLit(value) => Expr.Literal(value) + case StrLit(value) => Expr.Literal(value) + case UnitLit(undefinedOrNull) => + Expr.Literal(if undefinedOrNull + then UnitValue.Undefined + else UnitValue.Null) + case _ => throw MonomorphError("unsupported term"+ term.toString) + } + + def func2Item(funDef: NuFunDef): Item.FuncDecl | Item.FuncDefn = + val NuFunDef(_, nme, sn, targs, rhs) = funDef + rhs match + case Left(Lam(params, body)) => + Item.FuncDecl(Expr.Ref(nme.name), toFuncParams(params).toList, term2Expr(body)) + case Left(body: Term) => Item.FuncDecl(Expr.Ref(nme.name), Nil, term2Expr(body)) + case Right(tp) => Item.FuncDefn(Expr.Ref(nme.name), targs, PolyType(Nil, tp)) //TODO: Check correctness in Type -> Polytype conversion + + def type2Item(tyDef: NuTypeDef): Item.TypeDecl = + val NuTypeDef(kind, className, tparams, params, _, _, parents, _, _, body) = tyDef + val isolation = Isolation(body.entities.flatMap { + // Question: Will there be pure terms in class body? + case term: Term => + Some(term2Expr(term)) + case subTypeDef: NuTypeDef => throw MonomorphError(s"Unimplemented func2Item ${tyDef}") + case subFunDef: NuFunDef => + Some(func2Item(subFunDef)) + case term => throw MonomorphError(term.toString) + }) + val typeDecl: Item.TypeDecl = Item.TypeDecl( + Expr.Ref(className.name), // name + kind match // kind + case Als => TypeDeclKind.Alias + case Cls => TypeDeclKind.Class + case Trt => TypeDeclKind.Trait + case _ => throw MonomorphError(s"Unsupported TypeDefKind conversion ${kind}") + , + tparams.map(_._2), // typeParams + toFuncParams(params.getOrElse(Tup(Nil))).toList, // params + parents.map { + case Var(name) => (TypeName(name), Nil) + case App(Var(name), args) => (TypeName(name), term2Expr(args) match{ + case Expr.Tuple(fields) => fields + case _ => Nil + }) + case _ => throw MonomorphError("unsupported parent term") + }, // parents + isolation // body + ) + typeDecl + diff --git a/compiler/shared/main/scala/mlscript/compiler/PrettyPrinter.scala b/compiler/shared/main/scala/mlscript/compiler/PrettyPrinter.scala index 9fbc95877b..14e91ccf48 100644 --- a/compiler/shared/main/scala/mlscript/compiler/PrettyPrinter.scala +++ b/compiler/shared/main/scala/mlscript/compiler/PrettyPrinter.scala @@ -1,11 +1,11 @@ package mlscript.compiler -import mlscript.{TypingUnit, NuFunDef, NuTypeDef, Term} +import mlscript.{TypingUnit, NuFunDef, NuTypeDef, Term, Tup} import mlscript.compiler.debug.DebugOutput // For pretty printing terms in debug output. object PrettyPrinter: - def show(term: Term): DebugOutput = DebugOutput.Code(term.toString.linesIterator.toList) + def show(term: Term): DebugOutput = DebugOutput.Code(term.showDbg.linesIterator.toList) def show(unit: TypingUnit): DebugOutput = DebugOutput.Code(showTypingUnit(unit, 0).linesIterator.toList) def show(funDef: NuFunDef): DebugOutput = DebugOutput.Code(showFunDef(funDef).linesIterator.toList) def show(tyDef: NuTypeDef): DebugOutput = DebugOutput.Code(showTypeDef(tyDef, 0).linesIterator.toList) @@ -16,7 +16,7 @@ object PrettyPrinter: case term: Term => show(term) case tyDef: NuTypeDef => showTypeDef(tyDef) case funDef: NuFunDef => showFunDef(funDef) - case others => others.toString() + case others => others.showDbg }.mkString("{", "; ", "}") if (singleLine.length < 60) singleLine @@ -26,7 +26,7 @@ object PrettyPrinter: case term: Term => show(term) case tyDef: NuTypeDef => showTypeDef(tyDef) case funDef: NuFunDef => showFunDef(funDef) - case others => others.toString() + case others => others.showDbg }.map(indentStr + " " + _).mkString("{\n", "\n", s"\n$indentStr}") def showFunDef(funDef: NuFunDef): String = @@ -36,19 +36,19 @@ object PrettyPrinter: case Some(true) => "let'" } s"$st ${funDef.nme.name}" - + (if funDef.targs.isEmpty + + (if funDef.tparams.isEmpty then "" - else funDef.targs.map(_.name).mkString("[", ", ", "]")) + else funDef.tparams.map(_.name).mkString("[", ", ", "]")) + " = " - + funDef.rhs.fold(_.toString, _.body.show) + + funDef.rhs.fold(_.showDbg, _.show(newDefs = true)) def showTypeDef(tyDef: NuTypeDef, indent: Int = 0): String = s"${tyDef.kind.str} ${tyDef.nme.name}" + (if tyDef.tparams.isEmpty then "" - else tyDef.tparams.map(_.name).mkString("[", ",", "]")) - + "(" + tyDef.params + ")" + else tyDef.tparams.map(_._2.name).mkString("[", ",", "]")) + + tyDef.params.fold("")(params => s"(${params.showDbg})") + (if tyDef.parents.isEmpty then "" - else ": " + tyDef.parents.map(_.toString).mkString(", ")) + else ": " + tyDef.parents.map(_.showDbg).mkString(", ")) + showTypingUnit(tyDef.body, indent + 1) diff --git a/compiler/shared/main/scala/mlscript/compiler/mono/Monomorph.scala b/compiler/shared/main/scala/mlscript/compiler/mono/Monomorph.scala new file mode 100644 index 0000000000..fde9c93ede --- /dev/null +++ b/compiler/shared/main/scala/mlscript/compiler/mono/Monomorph.scala @@ -0,0 +1,334 @@ +package mlscript.compiler.mono + +import mlscript.compiler.debug.{Debug, DummyDebug} +import mlscript.{TypingUnit, NuTypeDef, NuFunDef} +import mlscript.{AppliedType, TypeName} +import mlscript.{App, Asc, Assign, Bind, Blk, Bra, CaseOf, Lam, Let, Lit, + New, Rcd, Sel, Subs, Term, Test, Tup, With, Var, Fld, If} +import mlscript.{IfThen, IfElse, IfLet, IfOpApp, IfOpsApp, IfBlock} +import mlscript.{IntLit, DecLit, StrLit, UnitLit} +import scala.collection.immutable.{HashMap} +import scala.collection.mutable.{Map as MutMap, Set as MutSet} +import scala.collection.mutable.ListBuffer +import mlscript.Cls +import mlscript.CaseBranches +import mlscript.TypeDefKind +import mlscript.AppliedType.apply +import mlscript.compiler.mono.specializer.Builtin +import mlscript.compiler.mono.specializer.Context +import mlscript.compiler.* + +import mlscript.compiler.printer.ExprPrinter +import mlscript.compiler.mono.specializer.BoundedExpr +import mlscript.compiler.mono.specializer.{MonoValue, ObjectValue, UnknownValue, FunctionValue, VariableValue} + +class Monomorph(debug: Debug = DummyDebug) extends DataTypeInferer: + import Helpers._ + import Monomorph._ + + /** + * Specialized implementations of function declarations. + */ + private val funImpls = MutMap[String, (Item.FuncDecl, MutMap[String, Item.FuncDecl], List[BoundedExpr], VariableValue)]() + + private def getfunInfo(nm: String): String = + val info = funImpls.get(nm).get + s"$nm: (${info._3.mkString(" X ")}) -> ${info._4} @${funDependence.get(nm).get.mkString("{", ", ", "}")}" + + private val funDependence = MutMap[String, Set[String]]() + + val evalQueue = MutSet[String]() + val evalCnt = MutMap[String, Int]() + + /** + * Specialized implementations of each type declarations. + */ + private val tyImpls = MutMap[String, SpecializationMap[Item.TypeDecl]]() + private val allTypeImpls = MutMap[String, Item.TypeDecl]() + /** + * Add a prototype type declaration. + */ + private def addPrototypeTypeDecl(typeDecl: Item.TypeDecl) = + tyImpls.addOne(typeDecl.name.name, SpecializationMap(typeDecl)) + allTypeImpls.addOne(typeDecl.name.name, typeDecl) + /** + * An iterator going through all type declarations. + */ + private def allTypeDecls: IterableOnce[Item.TypeDecl] = + tyImpls.values.flatMap { _.iterator } + + /** + * A global store of monomorphized lambda classes. + */ + private val lamTyDefs = MutMap[String, Item.TypeDecl]() + /** + * A global store of anonymous classes. For example, `new { ... }`. + */ + private val anonymTyDefs = MutMap[String, Item.TypeDecl]() + + def findClassByName(name: String): Option[mlscript.compiler.Item.TypeDecl] = + allTypeImpls.get(name) + + val specializer = mono.specializer.Specializer(this)(using debug) + + private def addNewFunction(func: Item.FuncDecl): Unit = { + funImpls.addOne(func.name.name, (func, MutMap(), func.params.map(_ => BoundedExpr()), VariableValue.refresh())) + funDependence.addOne(func.name.name, Set()) + } + + private def getResult(exps: List[Expr]) = mlscript.compiler.ModuleUnit(exps.concat[Expr | Item](funImpls.map(x => x._2._1)) + .concat(allTypeImpls.values.map(x => x.copy(body = Isolation(Nil)))) + .concat(lamTyDefs.values) + .concat(anonymTyDefs.values) + .toList) + + /** + * This function defunctionalizes the top-level `TypingUnit` into a `Module`. + */ + def defunctionalize(tu: TypingUnit): ModuleUnit = + // debug.trace("MONO MODL", PrettyPrinter.show(tu)) { + val exps = tu.entities.zipWithIndex.flatMap[Expr] { + case (term: Term, i) => + val exp = term2Expr(term) + val funcName = s"main$$$$$i" + val asFunc: Item.FuncDecl = Item.FuncDecl(Expr.Ref(funcName), Nil, exp) + addNewFunction(asFunc) + evalQueue.addOne(funcName) + Some(Expr.Apply(Expr.Ref(funcName), Nil)) + case (tyDef: NuTypeDef, _) => + val ret = type2Item(tyDef) + addPrototypeTypeDecl(ret) + None + case (funDef: NuFunDef, _) => + val funcItem = func2Item(funDef) + funcItem match + case funcDecl: Item.FuncDecl => + addNewFunction(funcDecl) + case _ => () + None + case (other, _) => throw MonomorphError(s"Unknown Statement in TypingUnit: ${other}") + }; + debug.log(getResult(exps).getDebugOutput.toLines(using false).mkString("\n")) + while(!evalQueue.isEmpty){ + val crt = evalQueue.head + evalQueue.remove(crt) + updateFunction(crt) + } + funImpls.mapValuesInPlace{ + case (_, (Item.FuncDecl(nm, as, body), mp, la, lr)) => + (Item.FuncDecl(nm, as, specializer.defunctionalize(body)), mp, la, lr) + } + val ret = getResult(exps) + debug.log("") + debug.log("==============final function signatures==================") + funImpls.foreach( + (nm, info) => { + debug.log(s"$nm: (${info._3.mkString(" X ")}) -> ${info._4}") + } + ) + + ret + // }() + + private def updateFunction(crt: String): Unit = { + debug.log(s"evaluating $crt, rests: ${evalQueue}") + val cnt = evalCnt.get(crt).getOrElse(0) + if(cnt <= 10){ + evalCnt.update(crt, cnt+1) + debug.log("=" * 10 + s" updating $crt " + "=" * 10) + debug.log(getfunInfo(crt)) + updateFunc(crt) + debug.log(getfunInfo(crt)) + } + else{ + throw new MonomorphError("stack overflow!!!") + } + } + + /** + * This function monomorphizes the nested `TypingUnit` into a `Isolation`. + */ + private def trans2Expr(body: TypingUnit): Isolation = + debug.trace("MONO BODY", PrettyPrinter.show(body)) { + Isolation(body.entities.flatMap[Expr | Item.FuncDecl | Item.FuncDefn] { + case term: Term => + Some(term2Expr(term)) + case tyDef: NuTypeDef => + val ret = type2Item(tyDef) + addPrototypeTypeDecl(ret) + None + case funDef: NuFunDef => + Some(func2Item(funDef)) + case other => throw MonomorphError(s"Unknown Statement in TypingUnit: ${other}") + }) + }(identity) + + def getFuncRetVal(name: String, args: List[BoundedExpr])(using evalCtx: Context, callingStack: List[String]): BoundedExpr = { + debug.trace[BoundedExpr]("SPEC CALL", name + args.mkString(" with (", ", ", ")")) { + if(funImpls.contains(name)){ + val (funcdecl, mps, oldArgs, oldVs) = funImpls.get(name).get + val old = funDependence.get(name).get + funDependence.update(name, old ++ callingStack.headOption) + // debug.log(s"adding dependence ${callingStack.headOption}") + val nArgs = (oldArgs zip (args.map(_.unfoldVars))).map(_ ++ _).zip(funcdecl.params).map( + (x,y) => if(y._1) then x else x.literals2Prims + ) + + debug.log(s"comparing ${oldArgs.mkString("(", ", ", ")")} with ${nArgs.map(_.getDebugOutput).mkString("(", ", ", ")")}") + if(evalCnt.get(name).isEmpty || (oldArgs zip nArgs).find(x => x._1.compare(x._2)).isDefined){ + funImpls.update(name, (funcdecl, mps, nArgs, oldVs)) + if(!evalQueue.contains(name)){ + if(evalCnt.get(name).isEmpty){ + debug.log(s"first time encounter $name") + updateFunction(name) + } + else{ + debug.log(s"find finer args") + evalQueue.add(name) + } + } + } + BoundedExpr(funImpls.get(name).get._4) + } + else { + debug.log(s"calling unknown function $name(${args.mkString(",")})") + debug.log(funImpls.keySet.toString()) + BoundedExpr(UnknownValue()) + } + }(identity) + } + + private def updateFunc(name: String): Unit = { + val (funcdecl, mps, args, _) = funImpls.get(name).get + val ctx = (funcdecl.params.map(_._2.name) zip args).toMap + val nBody = specializer.evaluate(funcdecl.body)(using Context()++ctx, List(funcdecl.name.name)) + val nVs = nBody.expValue + val oldVs = VariableValue.get(funImpls.get(name).get._4) + debug.log(s"comparing ${oldVs} with ${nVs}") + if(oldVs.compare(nVs)){ + debug.log(s"adding these funcs to queue: ${funDependence.get(name).get}") + funDependence.get(name).get.foreach(x => if !evalQueue.contains(x) then evalQueue.add(x)) + } + funImpls.updateWith(name)(_.map(x => { + val nFuncDecl: Item.FuncDecl = x._1.copy(body = nBody) + VariableValue.update(x._4, nVs) + (nFuncDecl, x._2, x._3, x._4) + })) + } + + def findVar(name: String)(using evalCtx: Context, callingStack: List[String]): MonoValue = { + if(funImpls.contains(name)){ + val funcBody = funImpls.get(name).get + funDependence.update(name, funDependence.get(name).get ++ callingStack.headOption) + FunctionValue(name, funcBody._1.params.map(_._2.name), Nil) + } + else{ + UnknownValue() + } + } + + private def partitationArguments(name: String, params: List[Parameter], args: List[Expr]): (List[Expr], List[Expr]) = + if (args.length != params.length) { + debug.log("") + throw MonomorphError(s"$name expect ${params.length} arguments but ${args.length} were given") + } + val staticArguments = params.iterator.zip(args).flatMap({ + case ((true, _), value) => Some(value) + case _ => None + }).toList + val dynamicArguments = params.iterator.zip(args).flatMap({ + case ((false, _), value) => Some(value) + case _ => None + }).toList + (staticArguments, dynamicArguments) + + private def generateSignature(staticArguments: List[Expr])(using MonomorphContext): String = + staticArguments.iterator.map(infer(_, None)).mkString("__", "_", "") + + private def specializeClassCall(name: String, args: List[Expr])(using MonomorphContext): Option[Expr] = + debug.trace("SPEC CALL", "class " + name + args.mkString(" with (", ", ", ")")) { + ??? + }(_.fold(Debug.noPostTrace)(identity)) + + def createObjValue(tpName: String, args: List[BoundedExpr]): MonoValue = + debug.trace("SPEC NEW", s"$tpName($args)"){ + if(allTypeImpls.contains(tpName)){ + val tp = allTypeImpls.get(tpName).get + val ags = (tp.params.map(_._2.name) zip args) + val ret = ObjectValue(tpName, MutMap(ags: _*)) + val pars = tp.parents.map((supTp, prms) => { + val evArgs = prms.map(specializer.evaluate(_)(using Context() ++ (("this"->BoundedExpr(ret)) :: ags), List(tpName)).expValue) + BoundedExpr(createObjValue(supTp.base.name, evArgs)) + }) + val parObjs = pars.zipWithIndex.map((e, i) => s"sup$$$i" -> e) + debug.log(s"par objs: $parObjs") + ret.fields.addAll(parObjs) + ret + } + else throw MonomorphError(s"tpName ${tpName} not found in implementations ${allTypeImpls}") + }(identity) + + def getFieldVal(obj: ObjectValue, field: String): BoundedExpr = + debug.trace("SPEC SEL", s"$obj :: $field"){ + if(allTypeImpls.contains(obj.name)){ + val tpDef = allTypeImpls.get(obj.name).get + val func = tpDef.body.items.flatMap{ + case funcDecl@Item.FuncDecl(Expr.Ref(nm), prms, bd) if nm.equals(field) => + Some(funcDecl) + case _ => None + }.headOption + if(func.isDefined){ + debug.log("defined") + val Item.FuncDecl(nm, prms, bd) = func.get + val nFuncName = s"${nm.name}$$${obj.name}" + if(!funImpls.contains(nFuncName)){ + val nFunc: Item.FuncDecl = Item.FuncDecl(Expr.Ref(nFuncName), (false, Expr.Ref("this")) :: prms, bd) + addNewFunction(nFunc) + } + BoundedExpr(FunctionValue(nFuncName, prms.map(_._2.name), List("this" -> BoundedExpr(obj)))) + } + else if(obj.fields.contains(field)) + debug.log("contains") + obj.fields.get(field).get + else{ + debug.log("else") + obj.fields.flatMap(x => { + if (x._1.matches("sup\\$[0-9]+")) { + x._2.asValue match{ + case Some(o: ObjectValue) => + Some(getFieldVal(o, field)) + case _ => None + } + } + else None + }).headOption.getOrElse( + throw MonomorphError(s"Field ${field} not Found in"+obj.toString()) + ) + } + } + else { + throw MonomorphError(s"ObjectValue ${obj} not found in implementations ${allTypeImpls}") + } + }(identity) + +object Monomorph: + class SpecializationMap[T <: Item](val prototype: T): + private var basePrototype: Option[T] = None + private val implementations = MutMap[String, T]() + + inline def getOrInsert(signature: String, op: => T): T = + implementations.getOrElseUpdate(signature, op) + inline def size: Int = implementations.size + // signature + item + inline def +=(entry: (String, T)): T = + implementations.addOne(entry) + entry._2 + inline def base: Option[T] = basePrototype + inline def base_=(op: => T): Unit = + basePrototype match + case None => basePrototype = Some(op) + case Some(_) => () + inline def isEmpty: Boolean = implementations.isEmpty + inline def iterator: Iterator[T] = + if implementations.isEmpty then Iterator.empty else + basePrototype.iterator.concat(implementations.values) \ No newline at end of file diff --git a/compiler/shared/main/scala/mlscript/compiler/mono/MonomorphContext.scala b/compiler/shared/main/scala/mlscript/compiler/mono/MonomorphContext.scala new file mode 100644 index 0000000000..b3eef13425 --- /dev/null +++ b/compiler/shared/main/scala/mlscript/compiler/mono/MonomorphContext.scala @@ -0,0 +1,33 @@ +package mlscript.compiler.mono + +import mlscript.compiler.debug.DebugOutput +import scala.collection.immutable.SeqMap +import mlscript.compiler.debug.Printable +import mlscript.compiler.* + +class MonomorphContext(context: List[Map[String, DataType]]) extends Printable: + def +(entry: (String, DataType)): MonomorphContext = + MonomorphContext(context match { + case Nil => Nil + case head :: tail => (head + entry) :: tail + }) + + def :+(entry: (String, DataType)): MonomorphContext = + MonomorphContext((Map.empty + entry) :: context) + + def unary_+ : MonomorphContext = + MonomorphContext(Map.empty :: context) + + def get(key: String): Option[DataType] = + context.iterator.flatMap(_.get(key)).nextOption() + + def getDebugOutput: DebugOutput = + DebugOutput.Map(context.foldRight(SeqMap.empty[String, String]) { (entries, map) => + entries.foldLeft(map) { (map, entry) => + map + (entry._1 -> entry._2.toString) + } + }.toList) + + +object MonomorphContext: + def empty: MonomorphContext = MonomorphContext(Nil) \ 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 new file mode 100644 index 0000000000..e7ef0305e5 --- /dev/null +++ b/compiler/shared/main/scala/mlscript/compiler/mono/MonomorphError.scala @@ -0,0 +1,3 @@ +package mlscript.compiler.mono + +class MonomorphError(message: String) extends Error(message) diff --git a/compiler/shared/main/scala/mlscript/compiler/mono/specializer/BoundedExpr.scala b/compiler/shared/main/scala/mlscript/compiler/mono/specializer/BoundedExpr.scala new file mode 100644 index 0000000000..fc60c04dae --- /dev/null +++ b/compiler/shared/main/scala/mlscript/compiler/mono/specializer/BoundedExpr.scala @@ -0,0 +1,226 @@ +package mlscript.compiler.mono.specializer + +import mlscript.compiler.{Expr, UnitValue} +import mlscript.compiler.debug.Printable +import mlscript.compiler.debug.DebugOutput +import scala.collection.mutable.Map as MutMap +import scala.collection.mutable.Set as MutSet +import mlscript.Var +import scala.collection.immutable +import mlscript.compiler.mono.MonomorphError + +abstract class MonoValue { + def toBoundedExpr = BoundedExpr(this) + def toStringSafe(using Set[Int]) = this.toString() +} +case class ObjectValue(name: String, fields: MutMap[String, BoundedExpr]) extends MonoValue{ + override def toString(): String = fields.map(x => (s"${x._1}: ${x._2.toStringSafe}")).mkString(s"$name@{", ", ", "}") + override def toStringSafe(using Set[Int]): String = fields.map(x => (s"${x._1}: ${x._2.toStringSafe}")).mkString(s"$name@{", ", ", "}") + def merge(other: ObjectValue)(using inStackExps: Set[Int]): ObjectValue = { + val allKeys = fields.keySet + val nFlds = allKeys.map(k => { + val s1 = 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)) + }) + ObjectValue(name, MutMap(nFlds.toSeq: _*)) + } + override def equals(x: Any): Boolean = { + x match { + case ObjectValue(xName, _) => name.equals(xName) + case _ => false + } + } +} +case class FunctionValue(name: String, prm: List[String], ctx: List[(String, BoundedExpr)]) extends MonoValue{ + override def toString(): String = prm.mkString(s"$name(", ", ", ")") + ctx.map(x => (s"${x._1}: ${x._2.toStringSafe}")).mkString(" given {", ", ", "}") + override def toStringSafe(using Set[Int]): String = prm.mkString(s"$name(", ", ", ")") + ctx.map(x => (s"${x._1}: ${x._2.toStringSafe}")).mkString(" given {", ", ", "}") + override def equals(x: Any): Boolean = x match{ + case FunctionValue(xName, _, _) => name.equals(xName) + case _ => false + } +} +case class UnknownValue() extends MonoValue{ + val idValue = UnknownValue.refresh() + override def toString(): String = s"?$idValue?" +} +object UnknownValue{ + var unknownCnt: Int = 0 + def refresh() = { + unknownCnt += 1 + unknownCnt + } +} +case class VariableValue(vx: Int, version: Int) extends MonoValue{ + override def toStringSafe(using Set[Int]): String = s"*$vx*=${VariableValue.get(this).toStringSafe}" + override def toString(): String = toStringSafe(using Set()) + def refresh() = VariableValue(vx, version+1) +} +object VariableValue{ + var vxCnt = 0 + val vMap = MutMap[Int, BoundedExpr]() + def refresh(): VariableValue = { + vxCnt += 1 + val ret = VariableValue(vxCnt, 0) + vMap.addOne(vxCnt -> BoundedExpr(ret)) + ret + } + def get(v: VariableValue): BoundedExpr = vMap.get(v.vx).get + def update(v: VariableValue, s: BoundedExpr): Unit = { + vMap.update(v.vx, s) + } +} + +case class LiteralValue(i: BigInt | BigDecimal | Boolean | String | UnitValue) extends MonoValue{ + def asBoolean(): Option[Boolean] = i match{ + case x: Boolean => Some(x) + case _ => None + } + override def toString(): String = i.toString() +} +case class PrimitiveValue() extends MonoValue{ + override def toString(): String = "*LIT*" +} + +class BoundedExpr(private val values: Set[MonoValue]) extends Printable { + def this(singleVal: MonoValue) = this(Set(singleVal)) + def this() = this(Set()) + def getDebugOutput: DebugOutput = DebugOutput.Plain(toStringSafe) + def getObjNames() = values.flatMap{ + // case FunctionValue(name, body, prm, ctx) => Some(name) + case ObjectValue(name, _) => Some(name) + case _ => None + }.toSet + // override def hashCode(): Int = values.hashCode() + override def toString(): String = toStringSafe + var updateCnt: Int = 0 + def toStringSafe(using printed: Set[Int] = Set()): String = { + if(printed.contains(this.hashCode())) s"..." + else values.map(_.toStringSafe(using printed + this.hashCode())).mkString("[", " | ", s"]") + } + def asValue: Option[MonoValue] = { + val tmp = this.unfoldVars + if(tmp.values.size == 1) { + Some(tmp.values.head) + } + else None + } + def getValue: Set[MonoValue] = { + unfoldVars.values.toSet.filterNot(_.isInstanceOf[VariableValue]) + } + + private def splitSpecifiedObjects(vs: Set[MonoValue], nms: Set[String]): (Set[MonoValue], Map[String, ObjectValue]) = { + val ret = vs.map{ + case o@ObjectValue(name, fields) => + if nms.contains(name) then { + (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) + } + + def unfoldVars(using instackExps: Set[Int] = Set()): BoundedExpr = { + val vars = values.toList.map{ + case vx: VariableValue => (Some(vx), None) + case others => (None, Some(others)) + }.unzip + val varSets: List[BoundedExpr] = vars._1.flatten.map(x => { + val vSet = VariableValue.get(x) + if(!instackExps.contains(vSet.hashCode())){ + vSet.unfoldVars(using instackExps + vSet.hashCode()) + } + else BoundedExpr(x) + }) + varSets.foldLeft(BoundedExpr(vars._2.flatten.toSet))((x, y) => (x ++ y)(using instackExps + y.hashCode())) + } + + def literals2Prims: BoundedExpr = { + val hasPrim = values.find(x => x.isInstanceOf[PrimitiveValue] || x.isInstanceOf[LiteralValue]).isDefined + if(hasPrim) + BoundedExpr(values.filterNot(x => x.isInstanceOf[PrimitiveValue] || x.isInstanceOf[LiteralValue]) + PrimitiveValue()) + else this + } + + def ++(other: BoundedExpr)(using instackExps: Set[Int] = Set()): BoundedExpr = { + if(this == other) this + else { + // unfoldVars + // other.unfoldVars + val mergingValNms = getObjNames().intersect(other.getObjNames()) + val (restVals1, mergingVals1) = splitSpecifiedObjects(values.toSet, mergingValNms) + val (restVals2, mergingVals2) = splitSpecifiedObjects(other.values.toSet, mergingValNms) + // val map2 = other.values.flatMap(x => if(values.fin(x)) then None else Some(x)) + val ret = mergingValNms.map(nm => (mergingVals1.get(nm), mergingVals2.get(nm)) match + case (Some(x1: ObjectValue), Some(x2: ObjectValue)) => x1.merge(x2)(using instackExps ++ Set(this.hashCode(), other.hashCode())) + case _ => throw MonomorphError(s"Name ${nm} not found in BoundedExpr merging") + ) + // println(s"get ${BoundedExpr(restVals1 ++ restVals2 ++ ret)}") + var ret2 = restVals1 ++ restVals2 + if(ret2.count(x => (x.isInstanceOf[LiteralValue] || x.isInstanceOf[PrimitiveValue])) > 1){ + ret2 = ret2.filterNot(_.isInstanceOf[LiteralValue]) + PrimitiveValue() + } + val retVals = BoundedExpr(ret2 ++ ret) + retVals.updateCnt = this.updateCnt + if(this.compare(retVals)) retVals.updateCnt += 1 + retVals + } + } + + def size = values.size + // lazy val eleCnt: Int = countEles + def eleCnt(using instackExps: Set[Int] = Set()): Int = { + if(values.size == 0) { + 0 + } + else { + val seperated = values.map{ + case o: ObjectValue => (None, Some(o)) + case f: FunctionValue => (Some(1), None) + case _: LiteralValue => (Some(1), None) + case _: PrimitiveValue => (Some(100000), None) + case UnknownValue() => (Some(1), None) + case vx: VariableValue => (Some(VariableValue.get(vx).eleCnt(using instackExps + VariableValue.get(vx).hashCode())), None) + }.unzip + val (lits, objs) = (seperated._1.flatten, seperated._2.flatten) + val objn = objs.map{ + case ObjectValue(name, fields) => + fields.map(x => { + if(instackExps.contains(x._2.hashCode())) 1 + else x._2.eleCnt(using instackExps + x._2.hashCode()) + }).fold(0)(_ + _) + 1 + }.fold(0)(_ + _) + lits.fold(0)(_ + _) + objn + } + } + def compare(other: BoundedExpr)(using instackExps: Set[Int] = Set()): Boolean = { + if(instackExps.contains(this.hashCode()) && instackExps.contains(other.hashCode())) + false + else { + if(values.find(_.isInstanceOf[PrimitiveValue]).isEmpty && other.values.find(_.isInstanceOf[PrimitiveValue]).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.toSet, nms1) + val (rests2, objs2) = splitSpecifiedObjects(other.values.toSet, 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/Builtin.scala b/compiler/shared/main/scala/mlscript/compiler/mono/specializer/Builtin.scala new file mode 100644 index 0000000000..a1a7b12c13 --- /dev/null +++ b/compiler/shared/main/scala/mlscript/compiler/mono/specializer/Builtin.scala @@ -0,0 +1,95 @@ +package mlscript.compiler.mono.specializer + +import mlscript.compiler.Expr +import mlscript.compiler.mono.MonomorphError + +object Builtin: + val builtinRefs = Set(">", "-", "+", "*", "&&", "||", "==", "true", "false") + + private val builtinBinaryOperations = Map[String, (Expr, Expr) => Option[Expr]]( + (">", { + case (Expr.Literal(lhs: BigInt), Expr.Literal(rhs: BigInt)) => + Some(Expr.Literal(lhs > rhs)) + case (Expr.Literal(lhs: BigDecimal), Expr.Literal(rhs: BigDecimal)) => + Some(Expr.Literal(lhs > rhs)) + case (_, _) => None + }), + ("-", { + case (Expr.Literal(lhs: BigInt), Expr.Literal(rhs: BigInt)) => + Some(Expr.Literal(lhs - rhs)) + case (Expr.Literal(lhs: BigDecimal), Expr.Literal(rhs: BigDecimal)) => + Some(Expr.Literal(lhs - rhs)) + case (_, _) => None + }), + ("+", { + case (Expr.Literal(lhs: BigInt), Expr.Literal(rhs: BigInt)) => + Some(Expr.Literal(lhs + rhs)) + case (Expr.Literal(lhs: BigDecimal), Expr.Literal(rhs: BigDecimal)) => + Some(Expr.Literal(lhs + rhs)) + case (_, _) => None + }), + ("*", { + case (Expr.Literal(lhs: BigInt), Expr.Literal(rhs: BigInt)) => + Some(Expr.Literal(lhs * rhs)) + case (Expr.Literal(lhs: BigDecimal), Expr.Literal(rhs: BigDecimal)) => + Some(Expr.Literal(lhs * rhs)) + case (_, _) => None + }) + ) + + private val builtinBinaryOperationsValue = Map[String, (MonoValue, MonoValue) => Option[MonoValue]]( + (">", { + case (LiteralValue(lhs: BigInt), LiteralValue(rhs: BigInt)) => + Some(LiteralValue(lhs > rhs)) + case (LiteralValue(lhs: BigDecimal), LiteralValue(rhs: BigDecimal)) => + Some(LiteralValue(lhs > rhs)) + case (_, _) => None + }), + ("-", { + case (LiteralValue(lhs: BigInt), LiteralValue(rhs: BigInt)) => + Some(LiteralValue(lhs - rhs)) + case (LiteralValue(lhs: BigDecimal), LiteralValue(rhs: BigDecimal)) => + Some(LiteralValue(lhs - rhs)) + case (_, _) => None + }), + ("+", { + case (LiteralValue(lhs: BigInt), LiteralValue(rhs: BigInt)) => + Some(LiteralValue(lhs + rhs)) + case (LiteralValue(lhs: BigDecimal), LiteralValue(rhs: BigDecimal)) => + Some(LiteralValue(lhs + rhs)) + case (_, _) => None + }), + ("*", { + case (LiteralValue(lhs: BigInt), LiteralValue(rhs: BigInt)) => + Some(LiteralValue(lhs * rhs)) + case (LiteralValue(lhs: BigDecimal), LiteralValue(rhs: BigDecimal)) => + Some(LiteralValue(lhs * rhs)) + case (_, _) => None + }), + ("&&", { + case (LiteralValue(lhs: Boolean), LiteralValue(rhs: Boolean)) => + Some(LiteralValue(lhs && rhs)) + case (_, _) => None + }), + ("||", { + case (LiteralValue(lhs: Boolean), LiteralValue(rhs: Boolean)) => + Some(LiteralValue(lhs || rhs)) + case (_, _) => None + }), + ("==", { + case (LiteralValue(lhs: BigInt), LiteralValue(rhs: BigInt)) => + Some(LiteralValue(lhs == rhs)) + case (LiteralValue(lhs: Boolean), LiteralValue(rhs: Boolean)) => + Some(LiteralValue(lhs == rhs)) + case (_, _) => None + }) + ) + + def isBinaryOperator(name: String): Boolean = + builtinBinaryOperations.contains(name) + + def evalulateBinaryOperation(name: String, lhs: Expr, rhs: Expr): Option[Expr] = + builtinBinaryOperations(name)(lhs, rhs) + + def evaluateBinaryOpValue(name: String, lhs: MonoValue, rhs: MonoValue): Option[MonoValue] = + builtinBinaryOperationsValue(name)(lhs, rhs) diff --git a/compiler/shared/main/scala/mlscript/compiler/mono/specializer/Context.scala b/compiler/shared/main/scala/mlscript/compiler/mono/specializer/Context.scala new file mode 100644 index 0000000000..bd1d8faecb --- /dev/null +++ b/compiler/shared/main/scala/mlscript/compiler/mono/specializer/Context.scala @@ -0,0 +1,21 @@ +package mlscript.compiler.mono.specializer + +import mlscript.compiler.Expr +import mlscript.compiler.debug.{DebugOutput, Printable} +import mlscript.compiler.mono.specializer.BoundedExpr + +class Context(private val entries: Map[String, BoundedExpr]) extends Printable: + def this() = this(Map("true" -> BoundedExpr(LiteralValue(true)), "false" -> BoundedExpr(LiteralValue(false)))) + inline def get(name: String): BoundedExpr = entries.get(name).getOrElse(BoundedExpr(UnknownValue())) + inline def +(entry: (String, BoundedExpr)): Context = Context(entries + entry) + inline def ++(other: Context): Context = Context(entries ++ other.entries) + inline def ++(other: IterableOnce[(String, BoundedExpr)]) = Context(entries ++ other) + inline def isEmpty: Boolean = entries.isEmpty + inline def contains(name: String): Boolean = entries.contains(name) + def getDebugOutput: DebugOutput = + DebugOutput.Map(entries.iterator.map { + (key, value) => (key, value.toString) + }.toList) +object Context{ + def toCtx(entries: IterableOnce[(String, BoundedExpr)]) = Context(Map.from(entries)) +} \ No newline at end of file diff --git a/compiler/shared/main/scala/mlscript/compiler/mono/specializer/Predicates.scala b/compiler/shared/main/scala/mlscript/compiler/mono/specializer/Predicates.scala new file mode 100644 index 0000000000..80a75fa1a3 --- /dev/null +++ b/compiler/shared/main/scala/mlscript/compiler/mono/specializer/Predicates.scala @@ -0,0 +1,6 @@ +package mlscript.compiler.mono.specializer + +import mlscript.compiler.Expr + +object Predicates: + \ 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 new file mode 100644 index 0000000000..6a8adea774 --- /dev/null +++ b/compiler/shared/main/scala/mlscript/compiler/mono/specializer/Specializer.scala @@ -0,0 +1,228 @@ +package mlscript.compiler.mono.specializer + +import scala.collection.mutable.ArrayBuffer +import scala.collection.mutable.Map as MutMap +import mlscript.compiler.debug.Debug +import mlscript.compiler.mono.MonomorphError +import mlscript.compiler.mono.Monomorph +import mlscript.compiler.UnitValue +import mlscript.TypeName +import mlscript.compiler.Item +import mlscript.compiler.CaseBranch +import mlscript.compiler.Expr + +class Specializer(monoer: Monomorph)(using debug: Debug){ + + def evaluate(rawExpr: Expr)(using evalCtx: Context, callingStack: List[String]): Expr = + // debug.trace[Expr]("EVAL ", rawExpr.toString()) { + rawExpr match{ + case Expr.Ref(name) => + rawExpr.expValue = + if evalCtx.contains(name) then evalCtx.get(name) else BoundedExpr(monoer.findVar(name)) + rawExpr + case Expr.Apply(Expr.Apply(opE@Expr.Ref(op), a1), a2) if Builtin.isBinaryOperator(op) => + if(a1.length == 1 && a2.length == 1) + { + val a1E = evaluate(a1.head) + val a2E = evaluate(a2.head) + val pairedAV = (a1E.expValue.asValue, a2E.expValue.asValue) match { + case (Some(i1: LiteralValue), Some(i2: LiteralValue)) => + Builtin.evaluateBinaryOpValue(op, i1, i2) match{ + case Some(value) => value + case None => PrimitiveValue() + } + case _ => PrimitiveValue() + } + val retExp = Expr.Apply(Expr.Apply(opE, List(a1E)), List(a2E)) + retExp.expValue = BoundedExpr(pairedAV) + retExp + } + else throw MonomorphError(s"Malformed Expr: ${rawExpr}") + + case other@Expr.Apply(callee, arguments) => + val calE = evaluate(callee) + val cal = calE.expValue + val nArgs = arguments.map(evaluate) + val args = nArgs.map(_.expValue) + val retV = cal.getValue.map{ + case FunctionValue(name, prm, ctxArg) => + val callResult = monoer.getFuncRetVal(name, ctxArg.unzip._2 ++ args) + // debug.log(s"call result: $callResult") + callResult + case o: ObjectValue => + val sel = monoer.getFieldVal(o, "apply") + sel.asValue match + case Some(FunctionValue(name, prm, ctx)) => + val callResult = monoer.getFuncRetVal(name, ctx.unzip._2 ++ args) + // debug.log(s"call result: $callResult") + callResult + case _ => BoundedExpr(UnknownValue()) + case _ => BoundedExpr(UnknownValue()) + }.fold(BoundedExpr())((x, y) => { + // debug.log(s"merging $x with $y") + val xy = x ++ y + // debug.log(s"result $xy") + xy + }) + val retExp = Expr.Apply(calE, nArgs) + retExp.expValue = retV + retExp + + case Expr.Select(receiver, field) => + val rec = evaluate(receiver) + val retV = rec.expValue.getValue.map{ + case ObjectValue(_, flds) if flds.contains(field.name) => + flds.get(field.name).get + case obj: ObjectValue => + monoer.getFieldVal(obj, field.name) + case _ => + BoundedExpr(UnknownValue()) + }.fold(BoundedExpr())(_ ++ _) + val retExp = Expr.Select(rec, field) + retExp.expValue = retV + retExp + + case Expr.LetIn(false, name, rhs, body) => + val nRhs = evaluate(rhs) + val nCtx = evalCtx + (name.name -> nRhs.expValue) + val nBody = evaluate(body)(using nCtx) + val retExp = Expr.LetIn(false, name, nRhs, nBody) + retExp.expValue = body.expValue + retExp + + case l@Expr.Literal(value) => + l.expValue = BoundedExpr(LiteralValue(value)) + l + + case Expr.New(apply, arguments) => + val nArgs = arguments.map(evaluate(_)) + val args = nArgs.map(_.expValue) + val retV = BoundedExpr(monoer.createObjValue(apply.name, args)) + val retExp = Expr.New(apply, nArgs) + retExp.expValue = retV + retExp + + case Expr.IfThenElse(condition, consequent, Some(alternate)) => + val nCond = evaluate(condition) + val nCons = evaluate(consequent) + val nAlter = evaluate(alternate) + val retV = nCond.expValue.asValue match { + case Some(x: LiteralValue) if x.asBoolean().isDefined => + if(x.asBoolean().get){ + nCons.expValue + } + else { + nAlter.expValue + } + case _ => + nCons.expValue ++ nAlter.expValue + } + val retExp = Expr.IfThenElse(nCond, nCons, Some(nAlter)) + retExp.expValue = retV + retExp + case Expr.IfThenElse(condition, consequent, None) => + val nCond = evaluate(condition) + val nCons = evaluate(consequent) + val retExp = Expr.IfThenElse(nCond, nCons, None) + retExp.expValue = BoundedExpr(LiteralValue(UnitValue.Undefined)) + retExp + + case self@Expr.Lambda(prm, body) => + throw MonomorphError(s"Unhandled case: ${rawExpr}") + + case Expr.Isolated(isolation) => throw MonomorphError(s"Unhandled case: ${rawExpr}") + + case Expr.Tuple(fields) => + if(fields.length == 1){ + evaluate(fields.head) + } + else + throw MonomorphError(s"Unhandled case: ${rawExpr}") + case Expr.Record(fields) => throw MonomorphError(s"Unhandled case: ${rawExpr}") + case Expr.LetIn(true, name, rhs, body) => throw MonomorphError(s"Unhandled case: ${rawExpr}") + case Expr.Block(items) => + val exps = items.flatMap{ + case e: Expr => Some(evaluate(e)) + case _ => None + } + if(exps.length == 0){ + val retE = Expr.Literal(UnitValue.Undefined) + val retV = BoundedExpr(LiteralValue(UnitValue.Undefined)) + retE.expValue = retV + retE + } + else if(exps.length == 1){ + exps.head + } + else { + val retV = exps.reverse.head.expValue + val retE = Expr.Block(exps) + retE.expValue = retV + retE + } + + case Expr.As(value, toType) => + val retV = evaluate(value) + rawExpr.expValue = retV.expValue + rawExpr + case Expr.Assign(assignee, value) => throw MonomorphError(s"Unhandled case: ${rawExpr}") + case Expr.With(value, fields) => throw MonomorphError(s"Unhandled case: ${rawExpr}") + case Expr.Subscript(receiver, index) => throw MonomorphError(s"Unhandled case: ${rawExpr}") + case Expr.Match(scrutinee, branches) => throw MonomorphError(s"Unhandled case: ${rawExpr}") + } + // }(_.expValue) + + def defunctionalize(rawExpr: Expr): Expr = { + val ret: Expr = rawExpr match { + case _: (Expr.Ref | Expr.Literal) => rawExpr + case Expr.Apply(sel@Expr.Select(receiver, field), args) => + val nRec = defunctionalize(receiver) + val nArgs = args.map(defunctionalize) + val branches = ArrayBuffer[CaseBranch]() + receiver.expValue.getValue.foreach{ + case o@ObjectValue(name, _) => + val selValue = monoer.getFieldVal(o, field.name) + val branchExp = selValue.asValue match{ + // foo.f is a member function + case Some(f: FunctionValue) => + Expr.Apply(Expr.Ref(f.name), Expr.Ref("obj") :: nArgs) + // foo.f is (many candidate) lambda(Object) + case _ if selValue.getValue.forall(_.isInstanceOf[ObjectValue]) => + // foo.f match ... + val scrut = Expr.Select(Expr.Ref("obj"), field) + val brchs = selValue.getValue.toList.map(_.asInstanceOf[ObjectValue]) + .map(o => { + val lambdaMemFunc = monoer.getFieldVal(o, "apply").asValue.get.asInstanceOf[FunctionValue] + val caseVarNm: Expr.Ref = Expr.Ref(s"obj$$${o.name}") + CaseBranch.Instance(Expr.Ref(o.name), caseVarNm, + Expr.Apply(Expr.Ref(lambdaMemFunc.name), caseVarNm :: nArgs)) + }) + Expr.Match(scrut, ArrayBuffer(brchs: _*)) + case _ => throw MonomorphError(s"Unhandled case: ${rawExpr}") + + } + branches.addOne(CaseBranch.Instance(Expr.Ref(name), Expr.Ref("obj"), branchExp)) + case _ => () + } + Expr.Match(nRec, branches) + case Expr.Apply(callee, arguments) => + if(callee.expValue.getValue.find(_.isInstanceOf[ObjectValue]).isDefined) + defunctionalize(Expr.Apply(Expr.Select(callee, Expr.Ref("apply")), arguments)) + else + Expr.Apply(defunctionalize(callee), arguments.map(defunctionalize)) + case Expr.New(typeName, args) => Expr.New(typeName, args.map(defunctionalize)) + case Expr.Tuple(fields) => Expr.Tuple(fields.map(defunctionalize)) + case Expr.LetIn(isRec, name, rhs, body) => Expr.LetIn(isRec, name, defunctionalize(rhs), defunctionalize(body)) + case Expr.IfThenElse(condition, consequent, alternate) => Expr.IfThenElse(defunctionalize(condition), defunctionalize(consequent), alternate.map(defunctionalize)) + case Expr.Block(items) => Expr.Block(items.map{ + case e: Expr => defunctionalize(e) + case other => other + }) + case Expr.Select(receiver, field) => Expr.Select(defunctionalize(receiver), field) + case Expr.As(value, toType) => Expr.As(defunctionalize(value), toType) + case _ => throw MonomorphError(s"Unhandled case: ${rawExpr}") + } + ret.expValue = rawExpr.expValue + ret + } +} diff --git a/compiler/shared/main/scala/mlscript/compiler/printer/ExprPrinter.scala b/compiler/shared/main/scala/mlscript/compiler/printer/ExprPrinter.scala new file mode 100644 index 0000000000..6026a9ea09 --- /dev/null +++ b/compiler/shared/main/scala/mlscript/compiler/printer/ExprPrinter.scala @@ -0,0 +1,73 @@ +package mlscript.compiler.printer + +import mlscript.compiler.{Expr, Isolation, Item, ModuleUnit, Parameter} + +class ExprPrinter: + private val printer = BlockPrinter() + + import printer.{endLine, enter, leave, print} + + private def show(module: ModuleUnit): Unit = module.items.foreach { + case expr: Expr => show(expr) + case item: Item => show(item) + } + + private def show(params: List[Parameter]): String = + params.iterator.map { + case (spec, Expr.Ref(name)) => (if spec then "#" else "") + name + }.mkString("(", ", ", ")") + + private def show(item: Item): Unit = item match + case Item.TypeDecl(Expr.Ref(name), kind, typeParams, params, parents, body) => + val typeParamsStr = if typeParams.isEmpty then "" + else typeParams.iterator.map(_.name).mkString("[", ", ", "]") + val reprParents = if parents.isEmpty then "" + else parents.iterator.map { case (parent, args) => + parent.show(true) + args.iterator.mkString("(", ", ", ")") + }.mkString(": ", ", ", "") + print(s"$kind $name$typeParamsStr${show(params)}$reprParents ") + show(body) + case Item.FuncDecl(Expr.Ref(name), params, body) => + print(s"fun $name${show(params)} =") + enter() + show(body) + leave() + case Item.FuncDefn(Expr.Ref(name), typeParams, polyType) => + val reprTypeParams = if typeParams.isEmpty then "" else + s"${typeParams.mkString("[", ", ", "]")} => " + print(s"fun $name: $reprTypeParams${polyType.show}") + + private def show(isolation: Isolation): Unit = + enter("{", "}") + isolation.items.foreach { + case expr: Expr => show(expr) + case item: Item => show(item) + } + leave() + + private def show(expr: Expr) = + print(expr.toString) + endLine() + + def toLines: List[String] = printer.toLines + + override def toString(): String = printer.toString + +object ExprPrinter: + def print(node: ModuleUnit | Item | Isolation | Expr): String = + val printer = ExprPrinter() + node match + case module: ModuleUnit => printer.show(module) + case item: Item => printer.show(item) + case isolation: Isolation => printer.show(isolation) + case expr: Expr => printer.show(expr) + printer.toString + + def printLines(node: ModuleUnit | Item | Isolation | Expr): List[String] = + val printer = ExprPrinter() + node match + case module: ModuleUnit => printer.show(module) + case item: Item => printer.show(item) + case isolation: Isolation => printer.show(isolation) + case expr: Expr => printer.show(expr) + printer.toLines diff --git a/compiler/shared/main/scala/mlscript/compiler/syntax.scala b/compiler/shared/main/scala/mlscript/compiler/syntax.scala new file mode 100644 index 0000000000..b7e205112e --- /dev/null +++ b/compiler/shared/main/scala/mlscript/compiler/syntax.scala @@ -0,0 +1,271 @@ +package mlscript.compiler + +import mlscript.compiler.debug.{DebugOutput, Printable} +import mlscript.compiler.printer.ExprPrinter +import scala.collection.mutable.ArrayBuffer +import mlscript.{Type, Union, Inter, Function, Record, Tuple, Recursive, AppliedType, + Neg, Rem, Bounds, WithExtension, Constrained, Top, Bot, Literal, + TypeName, TypeVar, PolyType, NamedType} +import scala.collection.immutable.HashMap +import mlscript.compiler.mono.specializer.BoundedExpr +import mlscript.compiler.mono.specializer.Builtin + +trait ASTNode: + var parent: ASTNode = null + def setNodeFields(): Unit + var expValue: BoundedExpr = BoundedExpr() + var freeVars: Set[String] = null + var isStatic: Boolean = false + +enum Expr extends Printable, ASTNode: + case Ref(name: String) + case Lambda(params: List[Parameter], body: Expr) + case Apply(callee: Expr, arguments: List[Expr]) + case Tuple(fields: List[Expr]) + case Record(fields: List[(Ref, Expr)]) + case Select(receiver: Expr, field: Ref) + case LetIn(isRec: Boolean, name: Ref, rhs: Expr, body: Expr) + case Block(items: List[Expr | Item.FuncDecl | Item.FuncDefn]) + case As(value: Expr, toType: Type) + case Assign(assignee: Expr, value: Expr) + case With(value: Expr, fields: Expr.Record) + case Subscript(receiver: Expr, index: Expr) + case Match(scrutinee: Expr, branches: ArrayBuffer[CaseBranch]) + case Literal(value: BigInt | BigDecimal | Boolean | String | UnitValue) + case New(typeName: TypeName, args: List[Expr]) + case IfThenElse(condition: Expr, consequent: Expr, alternate: Option[Expr]) + case Isolated(isolation: Isolation) + + val workaround = setNodeFields() + + def setNodeFields(): Unit = this match + case Expr.Ref(name) => + freeVars = if Builtin.builtinRefs.contains(name) then Set() else Set(name) + case Expr.Lambda(params, body) => + body.parent = this; freeVars = body.freeVars -- params.map(_._2.name) + case Expr.Apply(callee, arguments) => + callee.parent = this; arguments.foreach(x => x.parent = this); freeVars = callee.freeVars ++ arguments.flatMap(_.freeVars); isStatic = arguments.map(_.isStatic).fold(callee.isStatic)(_ && _) + case Expr.Tuple(fields) => + fields.foreach(x => x.parent = this); freeVars = fields.flatMap(_.freeVars).toSet; isStatic = fields.map(_.isStatic).fold(true)(_ && _) + case Expr.Record(fields) => + fields.foreach(x => x._2.parent = this); freeVars = fields.flatMap(_._2.freeVars).toSet -- fields.map(_._1.name); isStatic = fields.map(_._2.isStatic).fold(true)(_ && _) + case Expr.Select(receiver, field) => + receiver.parent = this; field.parent = this; freeVars = receiver.freeVars; isStatic = receiver.isStatic + case Expr.LetIn(isRec, name, rhs, body) => + rhs.parent = this; body.parent = this; freeVars = rhs.freeVars ++ body.freeVars - name.name; isStatic = rhs.isStatic && body.isStatic + case Expr.Block(items) => + val expItems = items.flatMap{ + case x: Expr => Some(x) + case _ => None + } + freeVars = expItems.flatMap(_.freeVars).toSet + expItems.foreach(x => {x.parent = this}) + isStatic = expItems.map(_.isStatic).fold(true)(_ && _) + case Expr.As(value, toType) => + value.parent = this; freeVars = value.freeVars; isStatic = value.isStatic + case Expr.Assign(assignee, value) => + assignee.parent = this; value.parent = this; freeVars = assignee.freeVars ++ value.freeVars; isStatic = true + case Expr.With(value, fields) => + value.parent = this; fields.parent = this; freeVars = value.freeVars ++ fields.freeVars; isStatic = value.isStatic && fields.isStatic + case Expr.Subscript(receiver, index) => + receiver.parent = this; index.parent = this; freeVars = receiver.freeVars ++ index.freeVars; isStatic = receiver.isStatic && index.isStatic + case Expr.Match(scrutinee, branches) => + scrutinee.parent = this + isStatic = scrutinee.isStatic + freeVars = scrutinee.freeVars ++ branches.flatMap{ + case CaseBranch.Instance(className, alias, body) => + isStatic &&= body.isStatic + body.freeVars - alias.name + case CaseBranch.Constant(literal, body) => + isStatic &&= body.isStatic + body.freeVars + case CaseBranch.Wildcard(body) => + isStatic &&= body.isStatic + body.freeVars + } + branches.foreach(x => x.body.parent = this) + case Expr.Literal(value) => + freeVars = Set() + isStatic = true + case Expr.New(typeName, args) => + args.foreach(x => x.parent = this) + isStatic = args.map(_.isStatic).fold(true)(_ && _) + freeVars = args.flatMap(_.freeVars).toSet + typeName.name + case Expr.IfThenElse(condition, consequent, alternate) => + condition.parent = this + consequent.parent = this + alternate.foreach(x => x.parent = this) + freeVars = condition.freeVars ++ consequent.freeVars ++ alternate.map(_.freeVars).getOrElse(Set()) + isStatic = alternate.map(_.isStatic && condition.isStatic && consequent.isStatic).getOrElse(true) + case Expr.Isolated(isolation) => + val exps = isolation.items.flatMap{ + case x: Expr => Some(x) + case _ => None + } + exps.foreach{x => x.parent = this} + freeVars = exps.flatMap(_.freeVars).toSet + isStatic = exps.map(_.isStatic).fold(true)(_ && _) + + def asBoolean(): Boolean = this match + case Literal(value: BigInt) => value != 0 + case Literal(value: BigDecimal) => value != 0 + case Literal(value: Boolean) => value + case Literal(value: String) => !value.isEmpty() + case Literal(_) => false + case _ => false + + def getDebugOutput: DebugOutput = + DebugOutput.Code(ExprPrinter.printLines(this)) + + override def toString(): String = + // val header = if this.parent == null then this.freeVars.mkString("[", ", ", "]~") else "" + val body = this match { + case Ref(name) => name + case Lambda(params, body) => + val head = params.mkString("(", ", ", ")") + s"(fun $head -> $body)" + case Apply(Apply(Ref(op), lhs :: Nil), rhs :: Nil) + if !op.headOption.forall(_.isLetter) => + s"($lhs $op $rhs)" + case Apply(callee, arguments) => + callee.toString + arguments.mkString("(", ", ", ")") + case Tuple(fields) => + val inner = fields.mkString(", ") + "(" + (if fields.length == 1 then inner + ", " else inner) + ")" + case Record(fields) => + "{" + fields.iterator.map { (name, value) => s"$name = $value" } + "}" + case Select(receiver, field) => s"$receiver.$field" + case LetIn(isRec, name, rhs, body) => s"let $name = $rhs in $body" + case Block(items) => items.mkString(";") + case As(value, toType) => s"$value as $toType" + case Assign(assignee, value) => s"$assignee = $value" + case With(value, fields) => s"$value with $fields" + case Subscript(receiver, index) => s"$receiver[$index]" + case Match(scrutinee, branches) => + s"$scrutinee match " + branches.iterator.mkString("{", "; ", "}") + case Literal(value) => "#" + value.toString + case New(callee, args) => + s"new ${callee.name}" + args.mkString(" (", ", ", ") ") + case IfThenElse(condition, consequent, None) => + s"if $condition then $consequent" + case IfThenElse(condition, consequent, Some(alternate)) => + s"if $condition then $consequent else $alternate" + case Isolated(isolation) => s"{\n$isolation\n}" + } + // header + + body +end Expr + +// This corresponds to `mlscript.UnitLit`. +enum UnitValue: + case Null, Undefined + + override def toString(): String = + this match + case Null => "null" + case Undefined => "()" // `()` is shorter than `undefined` + +enum CaseBranch: + val body: Expr + + case Instance(className: Expr.Ref, alias: Expr.Ref, body: Expr) + case Constant(literal: Expr.Literal, body: Expr) + case Wildcard(body: Expr) + + override def toString(): String = + this match + case Instance(Expr.Ref(className), Expr.Ref(alias), body) => + s"case $alias: $className => $body" + case Constant(literal, body) => s"case $literal => $body" + case Wildcard(body) => s"_ => $body" + +enum TypeDeclKind: + case Alias, Class, Trait + + override def toString(): String = this match + case Alias => "alias" + case Class => "class" + case Trait => "trait" + + +/** + * Function parameters: `(specializable, name)`. + */ +type Parameter = (Boolean, Expr.Ref) + +enum Item extends Printable: + val name: Expr.Ref + + /** + * Type declarations: aliases, classes and traits. + */ + case TypeDecl(name: Expr.Ref, kind: TypeDeclKind, typeParams: List[TypeName], + params: List[Parameter], parents: List[(NamedType, List[Expr])], body: Isolation) + /** + * Function declaration (with implementation). + */ + case FuncDecl(name: Expr.Ref, params: List[Parameter], body: Expr) + /** + * Function definition (with definition) + */ + case FuncDefn(name: Expr.Ref, typeParams: List[TypeName], body: PolyType) + + override def toString(): String = this match + case TypeDecl(Expr.Ref(name), kind, typeParams, params, parents, body) => + val typeParamsStr = if typeParams.isEmpty then "" + else typeParams.iterator.map(_.name).mkString("[", ", ", "]") + val parentsStr = if parents.isEmpty then "" + else parents.mkString(" extends ", " with ", " ") + s"$kind $name$typeParamsStr$parentsStr { $body }" + case FuncDecl(Expr.Ref(name), params, body) => + val parameters = params.iterator.map { + case (spec, Expr.Ref(name)) => + (if spec then "#" else "") + name + }.mkString("(", ", ", ")") + s"fun $name$parameters = $body" + case FuncDefn(Expr.Ref(name), Nil, polyType) => + s"fun $name: $polyType" + case FuncDefn(Expr.Ref(name), typeParams, polyType) => + s"fun $name: ${typeParams.mkString("[", ", ", "]")} => $polyType" + + def getDebugOutput: DebugOutput = + DebugOutput.Code(ExprPrinter.printLines(this)) + +object Item: + /** + * A shorthand constructor for classes without type parameters and parents. + */ + def classDecl(name: String, params: List[Parameter], body: Isolation): Item.TypeDecl = + Item.TypeDecl(Expr.Ref(name), TypeDeclKind.Class, Nil, params, Nil, body) + +/** + * An `Isolation` is like a `TypingUnit` but without nested classes. + */ +class Isolation(val items: List[Expr | Item.FuncDecl | Item.FuncDefn]) extends Printable: + private val namedItemMap = HashMap.from(items.iterator.flatMap { + case _: Expr => None: Option[(String, Item.FuncDecl | Item.FuncDefn)] + case item: Item.FuncDecl => Some((item.name.name, item)) + case item: Item.FuncDefn => Some((item.name.name, item)) + }) + + def get(name: String): Option[Item.FuncDecl | Item.FuncDefn] = + namedItemMap.get(name) + + def getDebugOutput: DebugOutput = + DebugOutput.Code(ExprPrinter.printLines(this)) + + override def toString(): String = items.mkString("\n") + +object Isolation: + def empty = Isolation(Nil) + +/** + * A `Module` is like a `TypingUnit`. + * This name conflicts with `java.lang.Module`. + * TODO: Find a better name. + */ +class ModuleUnit(val items: List[Expr | Item]) extends Printable: + def getDebugOutput: DebugOutput = + DebugOutput.Code(ExprPrinter.printLines(this)) + + override def toString(): String = items.mkString("\n") diff --git a/compiler/shared/test/diff/LambLift.mls b/compiler/shared/test/diff/LambLift.mls new file mode 100644 index 0000000000..81fecb52dd --- /dev/null +++ b/compiler/shared/test/diff/LambLift.mls @@ -0,0 +1,83 @@ +:NewDefs + +:AllowRuntimeErrors +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(),)} +//│ let local$2 = (x,) => {('(' new Foo$1([x,]) {} ')').bar} +//│ fun foo$1 = () => {local$2(1,)} +//│ Code(List(foo$1())) +//│ } +//│ fun foo: () -> Int +//│ Int +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +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({new Lambda1$2$1([]) {}},))) +//│ } +//│ fun foo: forall 'a. (1 -> 'a) -> 'a +//│ Int +//│ res +//│ = 2 + +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,)} +//│ let bar$2 = (f, x,) => {f(x,)} +//│ fun foo$1 = (x,) => {bar$2({new Lambda1$3$1([x,]) {}}, x,)} +//│ Code(List(foo$1(1,))) +//│ } +//│ fun foo: Int -> Int +//│ Int +//│ res +//│ = 2 + +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,)} +//│ class Lambda1$3$2([a,]) {fun apply = (z,) => ((this).a).bar(z,)} +//│ fun foo$2 = (f,) => {f(1,)} +//│ fun app$1 = (a,) => {foo$2({new 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/LiftType.mls b/compiler/shared/test/diff/LiftType.mls index 488e54e495..e8a5206b51 100644 --- a/compiler/shared/test/diff/LiftType.mls +++ b/compiler/shared/test/diff/LiftType.mls @@ -1,75 +1,75 @@ -:NewParser +:NewDefs :ParseOnly class CTX{ class A {} fun foo(f: A => A): (A => A) => A = f(new A) } -//│ |#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(), (), TypingUnit(NuTypeDef(class, A, (), Tup(), (), TypingUnit()), NuFunDef(None, foo, [], Lam(Tup(f: Lam(Tup(_: Var(A)), Var(A))), Asc(App(Var(f), Tup(_: New(Some((TypeName(A),)), TypingUnit(List())))), Function(Tuple(List((None,Field(None,Function(Tuple(List((None,Field(None,TypeName(A))))),TypeName(A)))))),TypeName(A)))))))) +//│ |#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}} +//│ //│ Lifted: //│ TypingUnit { -//│ class CTX$1_A$2(par$CTX$1,) {} -//│ class CTX$1() { -//│ fun foo = (f: (CTX$1_A$2,) => CTX$1_A$2,) => f (new CTX$1_A$2(this,) {},) : (CTX$1_A$2 -> CTX$1_A$2) -> CTX$1_A$2 +//│ class CTX$1_A$2([par$CTX$1,]) {} +//│ class CTX$1([]) { +//│ fun foo = (f: (CTX$1_A$2,) => CTX$1_A$2,) => f(new CTX$1_A$2([this,]) {},) : (CTX$1_A$2 -> CTX$1_A$2) -> CTX$1_A$2 //│ } //│ } +//│ 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) + fun foo(any: [A, B]): [B, A] = [any._2, any._1] } -//│ |#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)), (), TypingUnit(NuTypeDef(class, A, (), Tup(), (), TypingUnit(NuFunDef(None, foo, [], Var(x)))), NuTypeDef(class, B, (), Tup(), (Var(A)), TypingUnit(NuFunDef(None, foo, [], Var(y)))), NuFunDef(None, foo, [], Lam(Tup(any: Bra(rcd = false, Tup(_: Var(A), _: Var(B)))), Asc(Bra(rcd = false, Tup(_: Sel(Var(any), _2), _: Sel(Var(any), _1))), Tuple(List((None,Field(None,TypeName(B))), (None,Field(None,TypeName(A))))))))))) +//│ |#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]}} +//│ //│ Lifted: //│ TypingUnit { -//│ class CTX$1_A$2(par$CTX$1,) {fun foo = ((this).par$CTX$1).x} -//│ class CTX$1_B$3(par$CTX$1,): CTX$1_A$2 ((this).par$CTX$1,) {fun foo = ((this).par$CTX$1).y} -//│ class CTX$1(x, y,) { -//│ fun foo = (any: '(' CTX$1_A$2, CTX$1_B$3, ')',) => '(' (any)._2, (any)._1, ')' : (CTX$1_B$3, CTX$1_A$2,) +//│ class CTX$1_A$2([par$CTX$1,]) {fun foo = () => ((this).par$CTX$1).x} +//│ class CTX$1_B$3([par$CTX$1,]) {fun foo = () => ((this).par$CTX$1).y} +//│ class CTX$1([x, y,]) { +//│ fun foo = (any: [CTX$1_A$2, CTX$1_B$3,],) => [(any)._2, (any)._1,] : [CTX$1_B$3, CTX$1_A$2] //│ } //│ } +//│ 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) + fun foo(any: {p1: A, p2: B}): [B, A] = [any.p2, any.p1] } -//│ |#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)), (), TypingUnit(NuTypeDef(class, A, (), Tup(), (), TypingUnit(NuFunDef(None, foo, [], Var(x)))), NuTypeDef(class, B, (), Tup(), (Var(A)), TypingUnit(NuFunDef(None, foo, [], Var(y)))), NuFunDef(None, foo, [], Lam(Tup(any: Bra(rcd = true, Rcd(Var(p1) = Var(A), Var(p2) = Var(B)))), Asc(Bra(rcd = false, Tup(_: Sel(Var(any), p2), _: Sel(Var(any), p1))), Tuple(List((None,Field(None,TypeName(B))), (None,Field(None,TypeName(A))))))))))) +//│ |#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]}} +//│ //│ Lifted: //│ TypingUnit { -//│ class CTX$1_A$2(par$CTX$1,) {fun foo = ((this).par$CTX$1).x} -//│ class CTX$1_B$3(par$CTX$1,): CTX$1_A$2 ((this).par$CTX$1,) {fun foo = ((this).par$CTX$1).y} -//│ class CTX$1(x, y,) { -//│ fun foo = (any: '{' {p1: CTX$1_A$2, p2: CTX$1_B$3} '}',) => '(' (any).p2, (any).p1, ')' : (CTX$1_B$3, CTX$1_A$2,) +//│ class CTX$1_A$2([par$CTX$1,]) {fun foo = () => ((this).par$CTX$1).x} +//│ class CTX$1_B$3([par$CTX$1,]) {fun foo = () => ((this).par$CTX$1).y} +//│ class CTX$1([x, y,]) { +//│ fun foo = (any: '{' {p1: CTX$1_A$2, p2: CTX$1_B$3} '}',) => [(any).p2, (any).p1,] : [CTX$1_B$3, CTX$1_A$2] //│ } //│ } +//│ class CTX(x, y){ class A{ fun foo = x} class B { fun foo = y} - fun foo(any: (A, B)): ((B, A), A) = (any, any._1) + fun foo(any: [A, B]): [[B, A], A] = [any, any._1] } -//│ |#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)), (), TypingUnit(NuTypeDef(class, A, (), Tup(), (), TypingUnit(NuFunDef(None, foo, [], Var(x)))), NuTypeDef(class, B, (TypeName(T)), Tup(), (), TypingUnit(NuFunDef(None, foo, [], Var(y)))), NuFunDef(None, foo, [], Lam(Tup(any: Bra(rcd = false, Tup(_: Var(A), _: TyApp(Var(B), List(TypeName(A)))))), Asc(Bra(rcd = false, 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))))))))))) +//│ |#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]}} +//│ //│ Lifted: //│ TypingUnit { -//│ class CTX$1_A$2(par$CTX$1,) {fun foo = ((this).par$CTX$1).x} -//│ class CTX$1_B$3[T](par$CTX$1,) {fun foo = ((this).par$CTX$1).y} -//│ class CTX$1(x, y,) { -//│ fun foo = (any: '(' CTX$1_A$2, CTX$1_B$3‹CTX$1_A$2›, ')',) => '(' any, (any)._1, ')' : ((CTX$1_B$3[CTX$1_A$2], CTX$1_A$2,), CTX$1_A$2,) +//│ class CTX$1_A$2([par$CTX$1,]) {fun foo = () => ((this).par$CTX$1).x} +//│ class CTX$1_B$3[T]([par$CTX$1,]) {fun foo = () => ((this).par$CTX$1).y} +//│ class CTX$1([x, y,]) { +//│ fun foo = (any: [CTX$1_A$2, CTX$1_B$3‹CTX$1_A$2›,],) => [any, (any)._1,] : [[CTX$1_B$3[CTX$1_A$2], CTX$1_A$2], CTX$1_A$2] //│ } //│ } +//│ class CTX{ fun ctx(x,y) = @@ -80,9 +80,9 @@ class CTX{ (new CTX).bar } //│ |#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(), (), TypingUnit(NuFunDef(None, ctx, [], Lam(Tup(_: Var(x), _: Var(y)), Blk(...)))))) +//│ 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›}}} +//│ //│ 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 d1eb609d99..c0c0f3a681 100644 --- a/compiler/shared/test/diff/Lifter.mls +++ b/compiler/shared/test/diff/Lifter.mls @@ -1,4 +1,4 @@ -:NewParser +:NewDefs :ParseOnly class A(x) { @@ -26,36 +26,36 @@ class A(x) { fun getA = 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)), (), TypingUnit(NuTypeDef(class, B, (), Tup(_: Var(y)), (), TypingUnit(NuFunDef(None, getX, [], Var(x)), NuFunDef(None, getB1, [], App(Var(B1), Tup(_: Var(y)))), NuTypeDef(class, C, (), Tup(_: Var(z)), (), TypingUnit(NuFunDef(None, inc, [], Lam(Tup(), App(App(Var(+), Tup(_: Var(x))), Tup(_: IntLit(1))))), NuFunDef(None, getY, [], Var(y)), NuFunDef(None, getA, [], App(Var(A), Tup(_: Var(z)))), NuFunDef(None, getB, [], Lam(Tup(_: Var(w)), App(Var(B), Tup(_: Var(w))))), NuFunDef(None, getC, [], New(Some((TypeName(C),inc (),)), TypingUnit(List()))), NuFunDef(None, getSelf, [], Var(this)))))), NuTypeDef(class, B1, (), Tup(_: Var(y)), (), TypingUnit(NuFunDef(None, getX, [], Var(x)), NuFunDef(None, getY, [], Var(y)), NuFunDef(None, getB, [], New(Some((TypeName(B),y,)), TypingUnit(List()))), NuFunDef(None, getB1, [], New(Some((TypeName(B1),y,)), TypingUnit(List()))))), NuFunDef(None, getB, [], New(Some((TypeName(B),x,)), TypingUnit(List()))), NuFunDef(None, getB2, [], Lam(Tup(_: Var(y)), App(Var(B1), Tup(_: Var(y))))), NuFunDef(None, getB3, [], Lam(Tup(_: Var(z)), App(Var(getB2), Tup(_: Var(z))))), NuFunDef(None, getA, [], App(Var(A), Tup(_: Var(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,)}} +//│ //│ Lifted: //│ TypingUnit { -//│ class A$1_B$2_C$4(par$A$1_B$2, z,) { -//│ fun inc = () => + ((((this).par$A$1_B$2).par$A$1).x,) (1,) -//│ fun getY = ((this).par$A$1_B$2).y -//│ fun getA = A$1 ((this).z,) -//│ fun getB = (w,) => A$1_B$2 (((this).par$A$1_B$2).par$A$1, w,) -//│ fun getC = new A$1_B$2_C$4((this).par$A$1_B$2, (this).inc (),) {} -//│ fun getSelf = this +//│ class A$1_B$2_C$4([par$A$1_B$2, z, x,]) { +//│ fun inc = () => +((this).x, 1,) +//│ fun getY = () => ((this).par$A$1_B$2).y +//│ fun getA = () => A$1((this).z,) +//│ fun getB = (w,) => A$1_B$2(((this).par$A$1_B$2).par$A$1, w,) +//│ fun getC = () => new A$1_B$2_C$4([(this).par$A$1_B$2, (this).inc(), (this).x,]) {} +//│ fun getSelf = () => this //│ } -//│ class A$1_B$2(par$A$1, y,) { -//│ fun getX = ((this).par$A$1).x -//│ fun getB1 = A$1_B1$3 ((this).par$A$1, (this).y,) +//│ class A$1_B$2([par$A$1, y,]) { +//│ fun getX = () => ((this).par$A$1).x +//│ fun getB1 = () => A$1_B1$3((this).par$A$1, (this).y,) //│ } -//│ class A$1_B1$3(par$A$1, y,) { -//│ fun getX = ((this).par$A$1).x -//│ fun getY = (this).y -//│ fun getB = new A$1_B$2((this).par$A$1, (this).y,) {} -//│ fun getB1 = new A$1_B1$3((this).par$A$1, (this).y,) {} +//│ class A$1_B1$3([par$A$1, y,]) { +//│ fun getX = () => ((this).par$A$1).x +//│ fun getY = () => (this).y +//│ fun getB = () => new A$1_B$2([(this).par$A$1, (this).y,]) {} +//│ fun getB1 = () => new A$1_B1$3([(this).par$A$1, (this).y,]) {} //│ } -//│ class A$1(x,) { -//│ fun getB = new A$1_B$2(this, (this).x,) {} -//│ fun getB2 = (y,) => A$1_B1$3 (this, y,) -//│ fun getB3 = (z,) => (this).getB2 (z,) -//│ fun getA = A$1 ((this).x,) +//│ class A$1([x,]) { +//│ fun getB = () => new A$1_B$2([this, (this).x,]) {} +//│ fun getB2 = (y,) => A$1_B1$3(this, y,) +//│ fun getB3 = (z,) => (this).getB2(z,) +//│ fun getA = () => A$1((this).x,) //│ } //│ } +//│ class A(x) { class B(y) { @@ -65,17 +65,17 @@ 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)), (), TypingUnit(NuTypeDef(class, B, (), Tup(_: Var(y)), (), TypingUnit(NuTypeDef(class, C, (), Tup(_: Var(z)), (), TypingUnit(NuFunDef(None, sum, [], App(App(Var(+), Tup(_: App(App(Var(+), Tup(_: Var(x))), Tup(_: Var(y))))), Tup(_: Var(z))))))))))) +//│ Parsed: {class A(x,) {class B(y,) {class C(z,) {fun sum = +(+(x, y,), z,)}}}} +//│ //│ Lifted: //│ TypingUnit { -//│ class A$1_B$2_C$3(par$A$1_B$2, z,) { -//│ fun sum = + (+ ((((this).par$A$1_B$2).par$A$1).x,) (((this).par$A$1_B$2).y,),) ((this).z,) +//│ class A$1_B$2_C$3([par$A$1_B$2, z, x,]) { +//│ fun sum = () => +(+((this).x, ((this).par$A$1_B$2).y,), (this).z,) //│ } -//│ class A$1_B$2(par$A$1, y,) {} -//│ class A$1(x,) {} +//│ class A$1_B$2([par$A$1, y,]) {} +//│ class A$1([x,]) {} //│ } +//│ class A(x) { @@ -104,20 +104,11 @@ new C{ fun bar = 17 } //│ |#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)), (), TypingUnit(NuTypeDef(class, B, (), Tup(), (), TypingUnit(NuFunDef(None, foo, [], IntLit(1)), NuFunDef(None, bar, [], IntLit(11)))), NuFunDef(None, getB, [], New(Some((TypeName(B),)), TypingUnit(List(fun foo = 2, fun bar = 12)))), NuFunDef(None, bar, [], IntLit(13)))), NuTypeDef(class, C, (), Tup(), (Var(A)), TypingUnit(NuFunDef(None, getB, [], New(Some((TypeName(B),)), TypingUnit(List(fun foo = 3, fun bar = 14)))), NuFunDef(None, bar, [], IntLit(15)))), New(Some((TypeName(C),)), TypingUnit(List(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}} +//│ //│ Lifted: -//│ TypingUnit { -//│ class A$1_B$1$4(par$A$1,): A$1_B$3 ((this).par$A$1,) {fun foo = 2; fun bar = 12} -//│ class A$1_B$3(par$A$1,) {fun foo = 1; fun bar = 11} -//│ class A$1(x,) {fun getB = {new A$1_B$1$4(this,) {}}; fun bar = 13} -//│ class C$2_B$2$5(par$C$2,): A$1_B$3 ((this).par$C$2,) {fun foo = 3; fun bar = 14} -//│ class C$2(): A$1 () {fun getB = {new C$2_B$2$5(this,) {}}; fun bar = 15} -//│ class C$3$6_B$4$7(par$C$3$6,): A$1_B$3 ((this).par$C$3$6,) {fun foo = 4; fun bar = 16} -//│ class C$3$6(): C$2 () {fun getB = {new C$3$6_B$4$7(this,) {}}; fun bar = 17} -//│ Code(List({new C$3$6() {}})) -//│ } +//│ Lifting failed: mlscript.codegen.CodeGenError: Cannot find type B. Class values are not supported in lifter. +//│ @@ -129,81 +120,79 @@ 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, (TypeName(T), TypeName(U), TypeName(V)), Tup(_: Var(x)), (), TypingUnit(NuFunDef(None, foo, [], Lam(Tup(x: Var(Int)), Asc(App(App(Var(+), Tup(_: Var(x))), Tup(_: IntLit(1))), TypeName(T)))), NuTypeDef(class, Inner, (TypeName(W)), Tup(y: Var(Int)), (), TypingUnit(NuFunDef(None, bar, [], Lam(Tup(z: Var(U)), App(Var(foo), Tup(_: Var(y))))), NuFunDef(None, boo, [], Lam(Tup(z: Var(W)), Var(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}}} +//│ //│ Lifted: //│ TypingUnit { -//│ class Parent$1_Inner$2[W,U](par$Parent$1, y: Int,) { -//│ fun bar = (z: U,) => ((this).par$Parent$1).foo ((this).y,) +//│ class Parent$1_Inner$2[W,U]([par$Parent$1, y: Int,]) { +//│ fun bar = (z: U,) => ((this).par$Parent$1).foo((this).y,) //│ fun boo = (z: W,) => z //│ } -//│ class Parent$1[T,U,V](x,) {fun foo = (x: Int,) => + (x,) (1,) : T} +//│ class Parent$1[T,U,V]([x,]) {fun foo = (x: Int,) => +(x, 1,) : T} //│ } +//│ class B {} class C {} class D(y: Int) {} -class A(x: Int): {a1: Int} & B & D(x){ +class A(x: Int): ({a1: Int} & B & D(x)) { fun getA() = new C{ fun foo(x: T) = 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, (TypeName(T)), Tup(), (), TypingUnit()), NuTypeDef(class, C, (), Tup(), (), TypingUnit()), NuTypeDef(class, D, (), Tup(y: Var(Int)), (), TypingUnit()), NuTypeDef(class, A, (TypeName(T), TypeName(U)), Tup(x: Var(Int)), (App(App(Var(&), Tup(_: App(App(Var(&), Tup(_: Bra(rcd = true, Rcd(Var(a1) = Var(Int))))), Tup(_: TyApp(Var(B), List(TypeName(T))))))), Tup(_: App(Var(D), Tup(_: Var(x)))))), TypingUnit(NuFunDef(None, getA, [], Lam(Tup(), New(Some((TypeName(C),)), TypingUnit(List(fun foo = (x: T,) => 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}}} +//│ //│ Lifted: //│ TypingUnit { -//│ class B$1[T]() {} -//│ class C$2() {} -//│ class D$3(y: Int,) {} -//│ class A$4_C$1$5[T](par$A$4,): C$2 () {fun foo = (x: T,) => x} -//│ class A$4[T,U](x: Int,): & (& ('{' {a1: Int} '}',) (B$1 ()‹T›,),) (D$3 ((this).x,),) {fun getA = () => {new A$4_C$1$5(this,) {}}} +//│ class B$1[T]([]) {} +//│ class C$2([]) {} +//│ class D$3([y: Int,]) {} +//│ class A$4[T,U]([x: Int,]) {fun getA = () => new C$2([]) {}} //│ } +//│ // │ 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)))))))) class B {} class C {} class D(y: Int) {} -class A(x: Int): {a1: Int}, B, D(x){ +class A(x: Int) extends {a1: Int}, B, D(x){ fun getA() = new C{ fun foo(x) = 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|)| |#=| |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, (TypeName(T)), Tup(), (), TypingUnit()), NuTypeDef(class, C, (), Tup(), (), TypingUnit()), NuTypeDef(class, D, (), Tup(y: Var(Int)), (), TypingUnit()), NuTypeDef(class, A, (TypeName(T), 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)))), TypingUnit(NuFunDef(None, getA, [], Lam(Tup(), New(Some((TypeName(C),)), TypingUnit(List(fun foo = (x,) => 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}}} +//│ //│ Lifted: //│ TypingUnit { -//│ class B$1[T]() {} -//│ class C$2() {} -//│ class D$3(y: Int,) {} -//│ class A$4_C$1$5(par$A$4,): C$2 () {fun foo = (x,) => x} -//│ class A$4[T,U](x: Int,): '{' {a1: Int} '}', B$1 ()‹T›, D$3 ((this).x,) {fun getA = () => {new A$4_C$1$5(this,) {}}} +//│ class B$1[T]([]) {} +//│ class C$2([]) {} +//│ class D$3([y: Int,]) {} +//│ class A$4[T,U]([x: Int,]): '{' {a1: Int} '}', B$1()‹T›, D$3((this).x,) {fun getA = () => new C$2([]) {}} //│ } +//│ -class Child(x): { age: T } & { name: String} { +class Child(x): ({ age: T } & { name: String}) { class Inner{ fun foo = age } fun bar = age fun boo = new Inner } -//│ |#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, (TypeName(T), TypeName(U)), Tup(_: Var(x)), (App(App(Var(&), Tup(_: Bra(rcd = true, Rcd(Var(age) = Var(T))))), Tup(_: Bra(rcd = true, Rcd(Var(name) = Var(String)))))), TypingUnit(NuTypeDef(class, Inner, (), Tup(), (), TypingUnit(NuFunDef(None, foo, [], Var(age)))), NuFunDef(None, bar, [], Var(age)), NuFunDef(None, boo, [], New(Some((TypeName(Inner),)), TypingUnit(List())))))) +//│ |#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}} +//│ //│ Lifted: //│ TypingUnit { -//│ class Child$1_Inner$2(par$Child$1,) {fun foo = ((this).par$Child$1).age} -//│ class Child$1[T,U](x,): & ('{' {age: T} '}',) ('{' {name: String} '}',) { -//│ fun bar = (this).age -//│ fun boo = new Child$1_Inner$2(this,) {} +//│ class Child$1_Inner$2([par$Child$1, age,]) {fun foo = () => (this).age} +//│ class Child$1[T,U]([x,]) { +//│ fun bar = () => age +//│ fun boo = () => new Child$1_Inner$2([this, age,]) {} //│ } //│ } +//│ class A(x: Int) { @@ -215,15 +204,15 @@ new A(0) { fun getA2 = 2 } //│ |#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)), (), TypingUnit(NuFunDef(None, getA, [], Asc(IntLit(0), TypeName(Int))), NuFunDef(None, getA1, [], IntLit(1)))), New(Some((TypeName(A),0,)), TypingUnit(List(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}} +//│ //│ Lifted: //│ TypingUnit { -//│ class A$1(x: Int,) {fun getA = 0 : Int; fun getA1 = 1} -//│ class A$1$2(x: Int,): A$1 ((this).x,) {fun getA = 3; fun getA2 = 2} -//│ Code(List({new A$1$2(0,) {}})) +//│ class A$1([x: Int,]) {fun getA = () => 0 : Int; fun getA1 = () => 1} +//│ class A$1$2([x: Int,]): A$1((this).x,) {fun getA = () => 3; fun getA2 = () => 2} +//│ Code(List({new A$1$2([0,]) {}})) //│ } +//│ class A(x) { @@ -235,17 +224,17 @@ 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)), (), TypingUnit(NuTypeDef(class, B, (), Tup(_: Var(y)), (), TypingUnit()))), New(Some((TypeName(A),1,)), TypingUnit(List(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,)}}} +//│ //│ Lifted: //│ TypingUnit { -//│ class A$1_B$2(par$A$1, y,) {} -//│ class A$1(x,) {} -//│ class A$1$3_B$2$4(par$A$1$3, y,): A$1_B$2 ((this).par$A$1$3, (this).y,) {fun getB = new A$1_B$2((this).par$A$1$3, 3,) {}} -//│ class A$1$3(x,): A$1 ((this).x,) {fun getB = {new A$1$3_B$2$4(this, 2,) {}}} -//│ Code(List({new A$1$3(1,) {}})) +//│ class A$1_B$2([par$A$1, y,]) {} +//│ class A$1([x,]) {} +//│ class A$1$3_B$2$4([par$A$1$3, y,]): A$1_B$2((this).par$A$1$3, (this).y,) {fun getB = () => new A$1_B$2([(this).par$A$1$3, 3,]) {}} +//│ class A$1$3([x,]): A$1((this).x,) {fun getB = () => {new A$1$3_B$2$4([this, 2,]) {}}} +//│ Code(List({new A$1$3([1,]) {}})) //│ } +//│ @@ -267,21 +256,19 @@ new B{ fun getA = funcB } //│ |#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(), (), TypingUnit(NuFunDef(None, getA, [], IntLit(0)), NuFunDef(None, funcA, [], IntLit(10)))), NuTypeDef(class, B, (), Tup(), (Var(A)), TypingUnit(NuFunDef(None, getA, [], IntLit(1)), NuFunDef(None, funcB, [], IntLit(11)))), New(Some((TypeName(A),)), TypingUnit(List())), New(Some((TypeName(B),)), TypingUnit(List())), NuFunDef(None, f, [], Lam(Tup(_: Var(x)), If(IfThen(App(App(Var(is), Tup(_: Var(x))), Tup(_: Var(A))), IntLit(0), Some(IntLit(1))))), App(Var(f), Tup(_: New(Some((TypeName(A),)), TypingUnit(List(fun getA = 2))))), New(Some((TypeName(B),)), TypingUnit(List(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}} +//│ //│ Lifted: //│ TypingUnit { -//│ class A$1() {fun getA = 0; fun funcA = 10} -//│ class B$2(): A$1 () {fun getA = 1; fun funcB = 11} -//│ class A$1$3(): A$1 () {fun getA = 2} -//│ class B$2$4(): B$2 () {fun getA = (this).funcB} -//│ fun f = (x,) => if (is (x,) (A$1 (),)) then 0 else 1 -//│ Code(List(new A$1() {})) -//│ Code(List(new B$2() {})) -//│ Code(List(f ({new A$1$3() {}},))) -//│ Code(List({new B$2$4() {}})) +//│ class A$1([]) {fun getA = () => 0; fun funcA = () => 10} +//│ class B$2([]) {fun getA = () => 1; fun funcB = () => 11} +//│ fun f$1 = (x,) => if (is(x, A$1(),)) then 0 else 1 +//│ Code(List(new A$1([]) {})) +//│ Code(List(new B$2([]) {})) +//│ Code(List(f$1(new A$1([]) {},))) +//│ Code(List(new B$2([]) {})) //│ } +//│ class A{ @@ -307,18 +294,18 @@ 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(), (), TypingUnit(NuTypeDef(class, B, (), Tup(), (), TypingUnit(NuFunDef(None, funB, [], IntLit(1)), NuFunDef(None, foo, [], IntLit(100)))), NuTypeDef(class, C, (), Tup(), (Var(B)), TypingUnit(NuFunDef(None, funC, [], IntLit(2)), NuFunDef(None, foo, [], IntLit(1000)))), NuTypeDef(class, D, (), Tup(), (), TypingUnit(NuFunDef(None, funD, [], IntLit(3)), NuFunDef(None, foo, [], IntLit(10000)), NuTypeDef(class, E, (), Tup(), (Var(C)), TypingUnit(NuFunDef(None, funE, [], IntLit(4)), NuFunDef(None, foo, [], IntLit(100000)))), NuTypeDef(class, F, (), Tup(), (Var(E)), TypingUnit(NuFunDef(None, funF, [], IntLit(5)), NuFunDef(None, foo, [], IntLit(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}}}} +//│ //│ Lifted: //│ TypingUnit { -//│ class A$1_B$2(par$A$1,) {fun funB = 1; fun foo = 100} -//│ class A$1_C$3(par$A$1,): A$1_B$2 ((this).par$A$1,) {fun funC = 2; fun foo = 1000} -//│ class A$1_D$4_E$5(par$A$1_D$4,): A$1_C$3 (((this).par$A$1_D$4).par$A$1,) {fun funE = 4; fun foo = 100000} -//│ class A$1_D$4_F$6(par$A$1_D$4,): A$1_D$4_E$5 ((this).par$A$1_D$4,) {fun funF = 5; fun foo = 1000000} -//│ class A$1_D$4(par$A$1,) {fun funD = 3; fun foo = 10000} -//│ class A$1() {} +//│ class A$1_B$2([par$A$1,]) {fun funB = () => 1; fun foo = () => 100} +//│ class A$1_C$3([par$A$1,]) {fun funC = () => 2; fun foo = () => 1000} +//│ class A$1_D$4_E$5([par$A$1_D$4,]) {fun funE = () => 4; fun foo = () => 100000} +//│ class A$1_D$4_F$6([par$A$1_D$4,]) {fun funF = () => 5; fun foo = () => 1000000} +//│ class A$1_D$4([par$A$1,]) {fun funD = () => 3; fun foo = () => 10000} +//│ class A$1([]) {} //│ } +//│ class A{ @@ -349,31 +336,30 @@ 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(), (), TypingUnit(NuTypeDef(class, B, (), Tup(), (), TypingUnit(NuFunDef(None, funB, [], IntLit(1)), NuFunDef(None, foo, [], IntLit(100)))), NuTypeDef(class, C, (), Tup(), (Var(B)), TypingUnit(NuFunDef(None, funC, [], IntLit(2)), NuFunDef(None, foo, [], IntLit(1000)), NuFunDef(None, getB, [], New(Some((TypeName(B),)), TypingUnit(List()))))), NuTypeDef(class, D, (), Tup(), (), TypingUnit(NuFunDef(None, funD, [], IntLit(3)), NuFunDef(None, foo, [], IntLit(10000)), NuTypeDef(class, E, (), Tup(), (Var(C)), TypingUnit(NuFunDef(None, funE, [], IntLit(4)), NuFunDef(None, foo, [], IntLit(100000)), NuFunDef(None, getD, [], New(Some((TypeName(D),)), TypingUnit(List()))))), NuTypeDef(class, F, (), Tup(), (Var(E)), TypingUnit(NuFunDef(None, funF, [], IntLit(5)), NuFunDef(None, foo, [], IntLit(1000000)), NuFunDef(None, getE, [], New(Some((TypeName(E),)), TypingUnit(List(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}}}}} +//│ //│ Lifted: //│ TypingUnit { -//│ class A$1_B$2(par$A$1,) {fun funB = 1; fun foo = 100} -//│ class A$1_C$3(par$A$1,): A$1_B$2 ((this).par$A$1,) { -//│ fun funC = 2 -//│ fun foo = 1000 -//│ fun getB = new A$1_B$2((this).par$A$1,) {} +//│ class A$1_B$2([par$A$1,]) {fun funB = () => 1; fun foo = () => 100} +//│ class A$1_C$3([par$A$1,]) { +//│ fun funC = () => 2 +//│ fun foo = () => 1000 +//│ fun getB = () => new A$1_B$2([(this).par$A$1,]) {} //│ } -//│ class A$1_D$4_E$5(par$A$1_D$4,): A$1_C$3 (((this).par$A$1_D$4).par$A$1,) { -//│ fun funE = 4 -//│ fun foo = 100000 -//│ fun getD = new A$1_D$4(((this).par$A$1_D$4).par$A$1,) {} +//│ class A$1_D$4_E$5([par$A$1_D$4,]) { +//│ fun funE = () => 4 +//│ fun foo = () => 100000 +//│ fun getD = () => new A$1_D$4([((this).par$A$1_D$4).par$A$1,]) {} //│ } -//│ class A$1_D$4_F$6_E$1$7(par$A$1_D$4_F$6,): A$1_D$4_E$5 (((this).par$A$1_D$4_F$6).par$A$1_D$4,) {fun foo = 0} -//│ class A$1_D$4_F$6(par$A$1_D$4,): A$1_D$4_E$5 ((this).par$A$1_D$4,) { -//│ fun funF = 5 -//│ fun foo = 1000000 -//│ fun getE = {new A$1_D$4_F$6_E$1$7(this,) {}} +//│ class A$1_D$4_F$6([par$A$1_D$4,]) { +//│ fun funF = () => 5 +//│ fun foo = () => 1000000 +//│ fun getE = () => new A$1_D$4_E$5([(this).par$A$1_D$4,]) {} //│ } -//│ class A$1_D$4(par$A$1,) {fun funD = 3; fun foo = 10000} -//│ class A$1() {} +//│ class A$1_D$4([par$A$1,]) {fun funD = () => 3; fun foo = () => 10000} +//│ class A$1([]) {} //│ } +//│ class A{ @@ -384,15 +370,15 @@ class A{ } 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(), (), TypingUnit(NuTypeDef(class, B, (), Tup(), (), TypingUnit(NuFunDef(None, foo, [], IntLit(1)))), NuFunDef(None, bar, [], New(Some((TypeName(B),)), TypingUnit(List()))))), New(Some((TypeName(A),)), TypingUnit(List()))) +//│ Parsed: {class A {class B {fun foo = 1}; fun bar = new B}; new A} +//│ //│ Lifted: //│ TypingUnit { -//│ class A$1_B$2(par$A$1,) {fun foo = 1} -//│ class A$1() {fun bar = new A$1_B$2(this,) {}} -//│ Code(List(new A$1() {})) +//│ class A$1_B$2([par$A$1,]) {fun foo = () => 1} +//│ class A$1([]) {fun bar = () => new A$1_B$2([this,]) {}} +//│ Code(List(new A$1([]) {})) //│ } +//│ class A(x) { @@ -408,23 +394,14 @@ 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)), (), TypingUnit(NuFunDef(None, foo, [], IntLit(0)), NuFunDef(None, bar, [], Var(x)))), NuFunDef(Some(false), x, [], New(Some((TypeName(A),)), TypingUnit(List(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,)}}} +//│ //│ Lifted: //│ TypingUnit { -//│ class A$1(x,) {fun foo = 0; fun bar = (this).x} -//│ class A$1$2_A$2$3(par$A$1$2, x,): A$1 ((this).x,) { -//│ fun foo = + ((this).bar,) (1,) -//│ fun bar2 = + (((this).par$A$1$2).newFun,) (1,) -//│ } -//│ class A$1$2(x,): A$1 ((this).x,) { -//│ fun foo = 1 -//│ fun newFun = 2 -//│ fun bar = {new A$1$2_A$2$3(this, (this).foo,) {}} -//│ } -//│ let x = {new A$1$2() {}} +//│ class A$1([x,]) {fun foo = () => 0; fun bar = () => (this).x} +//│ let x$1 = () => new A$1([]) {} //│ } +//│ class A {} new A{ @@ -444,29 +421,9 @@ 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(), (), TypingUnit()), New(Some((TypeName(A),)), TypingUnit(List(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}}}}}} +//│ //│ Lifted: -//│ TypingUnit { -//│ class A$1() {} -//│ class A$1$2_A$2$3_A$3$4_A$4$5_A$5$6(par$A$1$2_A$2$3_A$3$4_A$4$5,): A$1 () { -//│ fun foo4 = (((((this).par$A$1$2_A$2$3_A$3$4_A$4$5).par$A$1$2_A$2$3_A$3$4).par$A$1$2_A$2$3).par$A$1$2).foo -//│ fun bar4 = 0 -//│ } -//│ class A$1$2_A$2$3_A$3$4_A$4$5(par$A$1$2_A$2$3_A$3$4,): A$1 () { -//│ fun foo3 = ((((this).par$A$1$2_A$2$3_A$3$4).par$A$1$2_A$2$3).par$A$1$2).foo -//│ fun bar3 = {new A$1$2_A$2$3_A$3$4_A$4$5_A$5$6(this,) {}} -//│ } -//│ class A$1$2_A$2$3_A$3$4(par$A$1$2_A$2$3,): A$1 () { -//│ fun foo2 = (((this).par$A$1$2_A$2$3).par$A$1$2).foo -//│ fun bar2 = {new A$1$2_A$2$3_A$3$4_A$4$5(this,) {}} -//│ } -//│ class A$1$2_A$2$3(par$A$1$2,): A$1 () { -//│ fun foo1 = ((this).par$A$1$2).foo -//│ fun bar1 = {new A$1$2_A$2$3_A$3$4(this,) {}} -//│ } -//│ class A$1$2(): A$1 () {fun foo = 1; fun bar = {new A$1$2_A$2$3(this,) {}}} -//│ Code(List({new A$1$2() {}})) -//│ } +//│ 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 b4e8d2df0c..61ac5eedfc 100644 --- a/compiler/shared/test/diff/LifterBlks.mls +++ b/compiler/shared/test/diff/LifterBlks.mls @@ -1,32 +1,32 @@ -:NewParser +:NewDefs :ParseOnly fun foo = print("ok") print("ko") //│ |#fun| |foo| |#=|→|print|(|"ok"|)|↵|print|(|"ko"|)|←| -//│ Parsed: {fun foo = {print ("ok",); print ("ko",)}} -//│ Parsed: -//│ TypingUnit(NuFunDef(None, foo, [], Blk(...))) +//│ Parsed: {fun foo = {print("ok",); print("ko",)}} +//│ //│ Lifted: -//│ TypingUnit {fun foo = {print ("ok",); print ("ko",)}} +//│ TypingUnit {fun foo$1 = () => {print("ok",); print("ko",)}} +//│ class A{ class B {} - fun foo(x: B) = (x:B) + fun foo(x: B) = (x : B) } -//│ |#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(), (), TypingUnit(NuTypeDef(class, B, (), Tup(), (), TypingUnit()), NuFunDef(None, foo, [], Lam(Tup(x: Var(B)), Bra(rcd = false, Tup(x: Var(B)))))))) +//│ |#class| |A|{|→|#class| |B| |{||}|↵|#fun| |foo|(|x|#:| |B|)| |#=| |(|x| |#:| |B|)|←|↵|}| +//│ Parsed: {class A {class B {}; fun foo = (x: B,) => '(' x : B ')'}} +//│ //│ Lifted: //│ TypingUnit { -//│ class A$1_B$2(par$A$1,) {} -//│ class A$1() {fun foo = (x: A$1_B$2,) => '(' x: A$1_B$2, ')'} +//│ class A$1_B$2([par$A$1,]) {} +//│ class A$1([]) {fun foo = (x: A$1_B$2,) => '(' x : A$1_B$2 ')'} //│ } +//│ fun foo = - fun local(x) = + let local(x) = class Foo { fun bar = x + 1 } @@ -36,30 +36,32 @@ fun foo = fun tmp = 1 print of local of 0 + local of 1 fun tmp = 2 -//│ |#fun| |foo| |#=|→|#fun| |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 = {fun 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, [], Blk(...))) +//│ |#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}} +//│ //│ Lifted: //│ TypingUnit { -//│ class Foo$1(x,) {fun bar = + ((this).x,) (1,)} -//│ fun foo = {fun local = (x,) => {(Foo$1 (x,)).bar}; fun tmp = 1; fun tmp = 2; print (+ (local (0,),) (local (1,),),); print (+ (local (0,),) (local (1,),),); print (local (+ (0,) (local (1,),),),)} +//│ class Foo$1([x,]) {fun bar = () => +((this).x, 1,)} +//│ let local$3 = (x,) => {(Foo$1(x,)).bar} +//│ fun tmp$2 = () => 1 +//│ fun foo$1 = () => {print(+(local$3(0,), local$3(1,),),); print(+('(' local$3(0,) ')', local$3(1,),),); print(local$3(+(0, local$3(1,),),),)} //│ } +//│ class A(y){} let f = x => new A(0){fun bar = x+y} 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)), (), TypingUnit()), NuFunDef(Some(false), f, [], Lam(Tup(_: Var(x)), New(Some((TypeName(A),0,)), TypingUnit(List(fun bar = + (x,) (y,)))))), App(Var(f), Tup(_: IntLit(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,)} +//│ //│ Lifted: //│ TypingUnit { -//│ class A$1(y,) {} -//│ class A$1$2(y, x,): A$1 ((this).y,) {fun bar = + ((this).x,) ((this).y,)} -//│ let f = (x,) => {new A$1$2(0, x,) {}} -//│ Code(List(f (0,))) +//│ class A$1([y,]) {} +//│ class A$2$2([y, x,]): A$1((this).y,) {fun bar = () => +((this).x, (this).y,)} +//│ let f$1 = (x,) => {new A$2$2([0, x,]) {}} +//│ Code(List(f$1(0,))) //│ } +//│ class A(x){ fun w = x @@ -72,22 +74,22 @@ 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)), (), TypingUnit(NuFunDef(None, w, [], Var(x)), NuFunDef(None, foo, [], Lam(Tup(_: Var(y)), Blk(...)))))) +//│ 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,)}}}} +//│ //│ Lifted: //│ TypingUnit { -//│ class A$1_B$2(par$A$1, z, y,) { -//│ fun bar = + (+ (((this).par$A$1).x,) ((this).y,),) ((this).z,) +//│ class A$1_B$2([par$A$1, z, y,]) { +//│ fun bar = () => +(+(((this).par$A$1).x, (this).y,), (this).z,) //│ } -//│ class A$1_B$1$3(par$A$1, z, y,): A$1_B$2 ((this).par$A$1, (this).z, (this).y,) { -//│ fun bar = + (+ (((this).par$A$1).w,) ((this).y,),) ((this).z,) +//│ class A$1_B$1$3([par$A$1, z, y,]): A$1_B$2((this).par$A$1, (this).z, (this).y,) { +//│ fun bar = () => +(+(((this).par$A$1).w, (this).y,), (this).z,) //│ } -//│ class A$1(x,) { -//│ fun w = (this).x -//│ fun foo = (y,) => {{new A$1_B$1$3(this, 0, y,) {}}} +//│ class A$1([x,]) { +//│ fun w = () => (this).x +//│ fun foo = (y,) => {{new A$1_B$1$3([this, 0, y,]) {}}} //│ } //│ } +//│ fun f(x,y,z) = class A{ @@ -98,26 +100,26 @@ fun f(x,y,z) = fun foo = new A fun bar2 = y } - class C: A,B{ + class C extends A, B { fun bar = bar1 + bar2 } -//│ |#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: {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, [], Lam(Tup(_: Var(x), _: Var(y), _: Var(z)), Blk(...)))) +//│ |#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,)}}} +//│ //│ Lifted: //│ TypingUnit { -//│ class A$1(x, y,) { -//│ fun foo = new B$2((this).y, (this).x,) {} -//│ fun bar1 = (this).x +//│ class A$1([x, y,]) { +//│ fun foo = () => new B$2([(this).y, (this).x,]) {} +//│ fun bar1 = () => (this).x //│ } -//│ class B$2(y, x,) { -//│ fun foo = new A$1((this).x, (this).y,) {} -//│ fun bar2 = (this).y +//│ class B$2([y, x,]) { +//│ fun foo = () => new A$1([(this).x, (this).y,]) {} +//│ fun bar2 = () => (this).y //│ } -//│ class C$3(x, y,): A$1 ((this).x, (this).y,), B$2 ((this).y, (this).x,) {fun bar = + ((this).bar1,) ((this).bar2,)} -//│ fun f = (x, y, z,) => {} +//│ class C$3([x, y,]): A$1((this).x, (this).y,), B$2((this).y, (this).x,) {fun bar = () => +((this).bar1, (this).bar2,)} +//│ fun f$1 = (x, y, z,) => {} //│ } +//│ fun f(x,y,z) = class C{ @@ -132,40 +134,54 @@ fun f(x,y,z) = fun boo = (new A).bar1 + B().bar2 + 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, [], Lam(Tup(_: Var(x), _: Var(y), _: Var(z)), Blk(...)))) +//│ 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,)}}} +//│ //│ Lifted: //│ TypingUnit { -//│ class C$1_A$2(par$C$1,) { -//│ fun foo = new C$1_B$3((this).par$C$1,) {} -//│ fun bar1 = ((this).par$C$1).x +//│ class C$1_A$2([par$C$1,]) { +//│ fun foo = () => new C$1_B$3([(this).par$C$1,]) {} +//│ fun bar1 = () => ((this).par$C$1).x //│ } -//│ class C$1_B$3(par$C$1,) { -//│ fun foo = new C$1_A$2((this).par$C$1,) {} -//│ fun bar2 = ((this).par$C$1).y +//│ class C$1_B$3([par$C$1,]) { +//│ fun foo = () => new C$1_A$2([(this).par$C$1,]) {} +//│ fun bar2 = () => ((this).par$C$1).y //│ } -//│ class C$1(x, y, z,) { -//│ fun boo = + (+ (('(' new C$1_A$2(this,) {}, ')').bar1,) ((C$1_B$3 (this,)).bar2,),) ((this).z,) +//│ class C$1([x, y, z,]) { +//│ fun boo = () => +(+(('(' new C$1_A$2([this,]) {} ')').bar1, (C$1_B$3(this,)).bar2,), (this).z,) //│ } -//│ fun f = (x, y, z,) => {} +//│ fun f$1 = (x, y, z,) => {} //│ } +//│ -fun f(x) = - let g(x) = x + 1 +fun f(y) = + let g(x) = x + y + 1 class Foo(x) { fun h = g(x) } - Foo(x).h -//│ |#fun| |f|(|x|)| |#=|→|#let| |g|(|x|)| |#=| |x| |+| |1|↵|#class| |Foo|(|x|)| |{|→|#fun| |h| |#=| |g|(|x|)|←|↵|}|↵|Foo|(|x|)|.h|←| -//│ Parsed: {fun f = (x,) => {let g = (x,) => + (x,) (1,); class Foo(x,) {fun h = g (x,)}; (Foo (x,)).h}} -//│ Parsed: -//│ TypingUnit(NuFunDef(None, f, [], Lam(Tup(_: Var(x)), Blk(...)))) +//│ |#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,)}}} +//│ //│ Lifted: //│ TypingUnit { -//│ class Foo$1(x, g,) {fun h = (this).g ((this).x,)} -//│ fun f = (x,) => {let g = (x,) => + (x,) (1,); (Foo$1 (x, g,)).h} +//│ class Foo$1([x, y,]) {fun h = () => g$2((this).x, y,)} +//│ let g$2 = (x, y,) => +(+(x, y,), 1,) +//│ fun f$1 = (y,) => {} //│ } +//│ + 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))} +//│ fun f(x) = let g(x) = @@ -176,75 +192,78 @@ fun f(x) = } Foo(x, x).bar //│ |#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, [], Lam(Tup(_: Var(x)), Blk(...)))) +//│ 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}} +//│ //│ Lifted: //│ TypingUnit { -//│ class Foo$1(x, y, g,) {fun bar = + ((this).g ((this).x,),) ((this).y,)} -//│ fun f = (x,) => {let g = (x,) => {let h = (x,) => + (x,) (2,); (Foo$1 (h (x,), x, g,)).bar}; (Foo$1 (x, x, g,)).bar} +//│ class Foo$1([x, y,]) {fun bar = () => +(g$2((this).x,), (this).y,)} +//│ let h$3 = (x,) => +(x, 2,) +//│ let g$2 = (x,) => {(Foo$1(h$3(x,), x,)).bar} +//│ fun f$1 = (x,) => {(Foo$1(x, x,)).bar} //│ } +//│ -class Foo(x, y): Bar(y, x), Baz(x + y) -//│ |#class| |Foo|(|x|,| |y|)|#:| |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(App(Var(+), Tup(_: Var(x))), Tup(_: Var(y)))))), TypingUnit())) +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,),) {}} +//│ //│ Lifted: //│ TypingUnit { -//│ class Foo$1(x, y,): Bar ((this).y, (this).x,), Baz (+ ((this).x,) ((this).y,),) {} +//│ class Foo$1([x, y,]): Bar((this).y, (this).x,), Baz(+((this).x, (this).y,),) {} //│ } +//│ fun foo(x: T): string = - class A(y): B, C(y: U) { + class A(y) extends B, C(y: U) { fun bar = this } "rua" -//│ |#fun| |foo|‹|T|,| |U|›|(|x|#:| |T|)|#:| |string| |#=| |→|#class| |A|(|y|)|#:| |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, [TypeName(T), TypeName(U)], Lam(Tup(x: Var(T)), Asc(Blk(...), TypeName(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} +//│ //│ Lifted: //│ TypingUnit { -//│ class A$1[T,U](y,): B‹T›, C (y: U,) {fun bar = this} -//│ fun foo[T, U] = (x: T,) => {"rua"} : string +//│ class A$1[T,U]([y,]): B‹T›, C(y: U,) {fun bar = () => this} +//│ fun foo$1[T, U] = (x: T,) => {"rua"} : string //│ } +//│ class A{ class B{ - fun f: T => B => T = x => y => x + fun f = x => y => x fun g: T => B => T } } -//│ |#class| |A|‹|T|›|{|→|#class| |B|{|→|#fun| |f|#:| |T| |=>| |B| |=>| |T| |#=| |x| |=>| |y| |=>| |x|↵|#fun| |g|#:| |T| |=>| |B| |=>| |T|←|↵|}|←|↵|}| -//│ Parsed: {class A‹T›() {class B() {fun f = (x,) => (y,) => x : T -> B -> T; fun g: T -> B -> T}}} -//│ Parsed: -//│ TypingUnit(NuTypeDef(class, A, (TypeName(T)), Tup(), (), TypingUnit(NuTypeDef(class, B, (), Tup(), (), TypingUnit(NuFunDef(None, f, [], Asc(Lam(Tup(_: Var(x)), Lam(Tup(_: Var(y)), Var(x))), Function(Tuple(List((None,Field(None,TypeName(T))))),Function(Tuple(List((None,Field(None,TypeName(B))))),TypeName(T))))), NuFunDef(None, g, [], PolyType(List(),Function(Tuple(List((None,Field(None,TypeName(T))))),Function(Tuple(List((None,Field(None,TypeName(B))))),TypeName(T)))))))))) +//│ |#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}}} +//│ //│ Lifted: //│ TypingUnit { -//│ class A$1_B$2[T](par$A$1,) { -//│ fun f = (x,) => (y,) => x : T -> A$1_B$2 -> T +//│ class A$1_B$2_Lambda1$1$3([par$A$1_B$2, x,]) {fun apply = (y,) => (this).x} +//│ class A$1_B$2[T]([par$A$1,]) { +//│ fun f = (x,) => {new A$1_B$2_Lambda1$1$3([this, x,]) {}} //│ fun g = T -> A$1_B$2 -> T //│ } -//│ class A$1[T]() {} +//│ class A$1[T]([]) {} //│ } +//│ class Foo{ class RectangleBox: Box & { breadth: T } class StackedRectangleBoxes : RectangleBox & { size: N } class Bar: {any: RectangleBox => StackedRectangleBoxes} } -//│ |#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, (TypeName(T)), Tup(), (), TypingUnit(NuTypeDef(class, RectangleBox, (), Tup(), (App(App(Var(&), Tup(_: TyApp(Var(Box), List(TypeName(T))))), Tup(_: Bra(rcd = true, Rcd(Var(breadth) = Var(T)))))), TypingUnit()), NuTypeDef(class, StackedRectangleBoxes, (TypeName(N)), Tup(), (App(App(Var(&), Tup(_: TyApp(Var(RectangleBox), List(TypeName(T))))), Tup(_: Bra(rcd = true, Rcd(Var(size) = Var(N)))))), TypingUnit()), NuTypeDef(class, Bar, (), Tup(), (Bra(rcd = true, Rcd(Var(any) = Lam(Tup(_: Var(RectangleBox)), Var(StackedRectangleBoxes))))), TypingUnit())))) +//│ |#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} {}}} +//│ //│ Lifted: //│ TypingUnit { -//│ class Foo$1_RectangleBox$2[T](par$Foo$1,): & (Box‹T›,) ('{' {breadth: T} '}',) {} -//│ class Foo$1_StackedRectangleBoxes$3[N,T](par$Foo$1,): & (Foo$1_RectangleBox$2 ((this).par$Foo$1,)‹T›,) ('{' {size: N} '}',) {} -//│ class Foo$1_Bar$4(par$Foo$1,): '{' {any: (Foo$1_RectangleBox$2,) => Foo$1_StackedRectangleBoxes$3} '}' {} -//│ class Foo$1[T]() {} +//│ class Foo$1_RectangleBox$2([par$Foo$1,]) {} +//│ class Foo$1_StackedRectangleBoxes$3[N]([par$Foo$1,]) {} +//│ class Foo$1_Bar$4([par$Foo$1,]) {} +//│ class Foo$1[T]([]) {} //│ } +//│ class Func { fun apply: T => U @@ -256,24 +275,39 @@ fun ctx(a,b) = foo(new Lambda{ fun apply(x) = a+x }, 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, (TypeName(T), TypeName(U)), Tup(), (), TypingUnit(NuFunDef(None, apply, [], PolyType(List(),Function(Tuple(List((None,Field(None,TypeName(T))))),TypeName(U)))))), NuTypeDef(class, Lambda, (TypeName(T), TypeName(U)), Tup(), (TyApp(Var(Func), List(TypeName(T), TypeName(U)))), TypingUnit()), NuFunDef(None, ctx, [], Lam(Tup(_: Var(a), _: Var(b)), Blk(...)))) +//│ |#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,)}} +//│ //│ Lifted: //│ TypingUnit { -//│ class Func$1[T,U]() {fun apply = T -> U} -//│ class Lambda$2[T,U](): Func$1 ()‹T, U› {} -//│ class Lambda$1$3[T,U](a,): Lambda$2 () {fun apply = (x,) => + ((this).a,) (x,)} -//│ fun ctx = (a, b,) => {fun foo = (f: Func$1, x,) => {(f).apply (x,)}; foo ({new Lambda$1$3(a,) {}}, b,)} +//│ class Func$1[T,U]([]) {fun apply = T -> U} +//│ class Lambda$2[T,U]([]) {} +//│ fun foo$2 = (f: Func$1, x,) => {(f).apply(x,)} +//│ fun ctx$1 = (a, b,) => {foo$2(new Lambda$2([]) {}, b,)} //│ } +//│ fun f(T) = new T() f(MyClass) //│ |#fun| |f|(|T|)| |#=| |→|#new| |T|(||)|←|↵|f|(|MyClass|)| -//│ Parsed: {fun f = (T,) => {new T() {}}; f (MyClass,)} -//│ Parsed: -//│ TypingUnit(NuFunDef(None, f, [], Lam(Tup(_: Var(T)), Blk(...))), App(Var(f), Tup(_: Var(MyClass)))) +//│ Parsed: {fun f = (T,) => {(new T)()}; f(MyClass,)} +//│ //│ Lifted: //│ Lifting failed: mlscript.codegen.CodeGenError: Cannot find type T. Class values are not supported in lifter. +//│ + +class A { + fun foo = + fun bar = foo() + bar() +} +//│ |#class| |A| |{|→|#fun| |foo| |#=| |→|#fun| |bar| |#=| |foo|(||)|↵|bar|(||)|←|←|↵|}| +//│ Parsed: {class A {fun foo = {fun bar = foo(); bar()}}} +//│ +//│ Lifted: +//│ TypingUnit { +//│ class A$1([]) {fun foo = () => {bar$1(this,)}} +//│ fun bar$1 = (this,) => (this).foo() +//│ } +//│ diff --git a/compiler/shared/test/diff/mono.mls b/compiler/shared/test/diff/mono.mls new file mode 100644 index 0000000000..a261d75d03 --- /dev/null +++ b/compiler/shared/test/diff/mono.mls @@ -0,0 +1,1177 @@ + +:NewDefs + +:mono +fun f(x: Int) = if x then 42 else 1337 +//│ +//│ Lifted: +//│ TypingUnit { +//│ fun f$1 = (x: Int,) => if (x) then 42 else 1337 +//│ } +//│ Mono: +//│ +//│ Defunc result: +//│ fun f$1(x) = +//│ if x then #42 else #1337 +//│ fun f: (x: Int) -> (1337 | 42) + +:mono +fun foo() = 42 +//│ +//│ Lifted: +//│ TypingUnit {fun foo$1 = () => 42} +//│ Mono: +//│ +//│ Defunc result: +//│ fun foo$1() = +//│ #42 +//│ fun foo: () -> 42 + +: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,) => if (b) then x else 1337 +//│ let a$1 = () => foo$3(42, true,) +//│ let b$2 = () => foo$3(23, false,) +//│ } +//│ Mono: +//│ +//│ Defunc result: +//│ fun b$2() = +//│ foo$3(#23, false) +//│ fun foo$3(x, #b) = +//│ if b then x else #1337 +//│ fun a$1() = +//│ foo$3(#42, true) +//│ fun foo: forall 'a. ('a, Object) -> (1337 | 'a) +//│ let a: 1337 | 42 +//│ let b: 1337 | 23 +//│ a +//│ = 42 +//│ b +//│ = 1337 + +:mono +let x = 42 + 1337 +//│ +//│ Lifted: +//│ TypingUnit {let x$1 = () => +(42, 1337,)} +//│ Mono: +//│ +//│ Defunc result: +//│ fun x$1() = +//│ +(#42, #1337) +//│ let x: Int +//│ x +//│ = 1379 + +//:mono +//:e // FIXME: Mutable Parameters +//class Bar(#x) +//fun foo(#b) = b +//let a = foo(new Bar(1)) +//let b = foo(new Bar(2)) + +//:mono +//:w // FIXME: Mutable Parameters +//class OneInt(#a){ +// fun inc() = a+1 +//} +//(new OneInt(1)).inc() + +//:mono +//:e // FIXME: Mutable Parameters +//class OneInt(#a){ +// fun add(x) = +// new OneInt(a+x.a) +//} +//(new OneInt(1)).add(new OneInt(2)) + +: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: +//│ +//│ Defunc result: +//│ main$$0() +//│ main$$1() +//│ fun main$$0() = +//│ if true then #1 else #0 +//│ fun main$$1() = +//│ if >(+(#1, #1), #1) then -(#1, #1) else *(#1, #1) +//│ Int +//│ res +//│ = 1 +//│ res +//│ = 0 + +:mono +if(b) then 1 else 2 +//│ +//│ Lifted: +//│ TypingUnit {Code(List(if ('(' b ')') then 1 else 2))} +//│ Mono: +//│ +//│ Defunc result: +//│ main$$0() +//│ fun main$$0() = +//│ if b then #1 else #2 +//│ 1 | 2 +//│ res +//│ = 2 + +: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('(' {new Lambda2$1$1([]) {}} ')'({new Lambda1$2$2([]) {}}, true,))) +//│ } +//│ Mono: +//│ +//│ Defunc result: +//│ main$$2() +//│ fun apply$Lambda2$1$1(this, f, g) = +//│ f match {case obj: Lambda1$2$2 => apply$Lambda1$2$2(obj, g)} +//│ fun main$$2() = +//│ new Lambda2$1$1 () match {case obj: Lambda2$1$1 => apply$Lambda2$1$1(obj, new Lambda1$2$2 () , true)} +//│ fun apply$Lambda1$2$2(this, f) = +//│ f +//│ class Lambda2$1$1() { +//│ } +//│ class Lambda1$2$2() { +//│ } +//│ true +//│ 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('(' {new Lambda1$1$1([]) {}} ')'(true,))) +//│ } +//│ Mono: +//│ +//│ Defunc result: +//│ main$$1() +//│ fun apply$Lambda1$1$1(this, b) = +//│ if b then true else false +//│ fun main$$1() = +//│ new Lambda1$1$1 () match {case obj: Lambda1$1$1 => apply$Lambda1$1$1(obj, true)} +//│ class Lambda1$1$1() { +//│ } +//│ Bool +//│ res +//│ = true + +: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: +//│ +//│ Defunc result: +//│ main$$1() +//│ fun f$1(x) = +//│ if >(x, #0) then +(x, #1) else -(x, #1) +//│ fun main$$1() = +//│ +(f$1(#2), #3) +//│ fun f: Int -> Int +//│ Int +//│ res +//│ = 6 + +:mono +fun fac(n) = + if (n > 1) then fac(n - 1) * n else 1 +fac(2) +//│ +//│ Lifted: +//│ TypingUnit { +//│ fun fac$1 = (n,) => {if ('(' >(n, 1,) ')') then *(fac$1(-(n, 1,),), n,) else 1} +//│ Code(List(fac$1(2,))) +//│ } +//│ Mono: +//│ +//│ Defunc result: +//│ main$$1() +//│ fun fac$1(n) = +//│ if >(n, #1) then *(fac$1(-(n, #1)), n) else #1 +//│ fun main$$1() = +//│ fac$1(#2) +//│ fun fac: Int -> Int +//│ Int +//│ res +//│ = 2 + +:mono +class List(val l: List | Nil | undefined, val hasTail: Bool) {} +class Nil(val l: List | Nil | undefined, val hasTail: Bool) {} +fun count(lst) = + if lst.hasTail then + let l = lst.l + 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, Nil,), undefined,), val hasTail: Bool,]) {} +//│ class Nil$2([val l: |(|(List, Nil,), undefined,), val hasTail: Bool,]) {} +//│ let l$2 = (lst,) => (lst).l +//│ fun count$1 = (lst,) => {if ((lst).hasTail) then {if (is(l, undefined,)) then 1 else +(count$1(l,), 1,)} else 0} +//│ Code(List(count$1(new List$1([new List$1([new Nil$2([undefined, false,]) {}, true,]) {}, true,]) {},))) +//│ } +//│ Mono: +//│ +//│ Defunc result: +//│ main$$4() +//│ fun l$2(lst) = +//│ lst.l +//│ fun count$1(lst) = +//│ if lst.hasTail then if is(l, #()) then #1 else +(count$1(l), #1) else #0 +//│ fun main$$4() = +//│ count$1(new List$1 (new List$1 (new Nil$2 (#(), false) , true) , true) ) +//│ class Nil$2(l, hasTail) { +//│ } +//│ class List$1(l, hasTail) { +//│ } +//│ class List(l: List | Nil | (), hasTail: Bool) +//│ class Nil(l: List | Nil | (), hasTail: Bool) +//│ fun count: forall 'a. 'a -> Int +//│ Int +//│ where +//│ 'a <: {hasTail: Object, l: Object & 'a & ~() | ()} +//│ res +//│ = 2 + +//:mono +//class Cons(e, tail){ +// fun gen() = new Cons(e, tail.gen()) +//} +//class Nil(){ +// fun gen() = new Cons(0, this) +//} +//fun generate(x) = +// if x > 0 then new Cons(x, generate(x+1)) else new Nil() +//generate(10).gen() + +:mono +class List(e: Int, tail: List | Nil) { + fun map: (Int -> Int) -> List + fun map(f)= new List(f(e), tail.map(f)) + fun count(): Int + fun count() = 1 + tail.count() +} +class Nil() { + fun map(f) = this + 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, Nil,),]) { +//│ 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({new Lambda1$2$3([]) {}},)).map({new Lambda1$3$4([]) {}},))) +//│ } +//│ Mono: +//│ +//│ Defunc result: +//│ main$$5() +//│ fun map$List$1(this, f) = +//│ new List$1 (f match {case obj: Lambda1$2$3 => apply$Lambda1$2$3(obj, this.e); case obj: Lambda1$3$4 => apply$Lambda1$3$4(obj, this.e)}, this.tail match {case obj: List$1 => map$List$1(obj, f); case obj: Nil$2 => map$Nil$2(obj, f)}) +//│ fun add2$1(x) = +//│ +(x, #2) +//│ fun main$$5() = +//│ new List$1 (#1, new List$1 (#2, new Nil$2 () ) ) match {case obj: List$1 => map$List$1(obj, new Lambda1$2$3 () )} match {case obj: List$1 => map$List$1(obj, new Lambda1$3$4 () )} +//│ 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) +//│ class Lambda1$3$4() { +//│ } +//│ class Nil$2() { +//│ } +//│ class List$1(e, tail) { +//│ } +//│ class Lambda1$2$3() { +//│ } +//│ class List(e: Int, tail: List | Nil) { +//│ fun count: () -> Int +//│ fun map: (Int -> Int) -> List +//│ } +//│ class Nil() { +//│ fun count: () -> 0 +//│ fun map: anything -> Nil +//│ } +//│ fun add2: Int -> Int +//│ List +//│ res +//│ = List {} + +:mono +:AllowRuntimeErrors +class List(e: Int, tail: List | Nil) { + fun count(): Int + fun count() = 1 + tail.count() +} +class Nil() { + fun count() = 0 +} +fun foo(x) = x.count() +fun generate(x) = + if x > 0 then new List(x, generate(x+1)) else new Nil() +foo(new List(1, new List(2, new Nil()))) +foo(generate(1)) +//│ +//│ Lifted: +//│ TypingUnit { +//│ class List$1([e: Int, tail: |(List, Nil,),]) { +//│ 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(new List$1([1, new List$1([2, new Nil$2([]) {},]) {},]) {},))) +//│ Code(List(foo$1(generate$2(1,),))) +//│ } +//│ Mono: +//│ +//│ Defunc result: +//│ main$$4() +//│ main$$5() +//│ fun foo$1(x) = +//│ x match {case obj: Nil$2 => count$Nil$2(obj); case obj: List$1 => count$List$1(obj)} +//│ fun count$Nil$2(this) = +//│ #0 +//│ fun count$List$1(this) = +//│ +(#1, this.tail match {case obj: List$1 => count$List$1(obj); case obj: Nil$2 => count$Nil$2(obj)}) +//│ 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(new List$1 (#1, new List$1 (#2, new Nil$2 () ) ) ) +//│ class Nil$2() { +//│ } +//│ class List$1(e, tail) { +//│ } +//│ class List(e: Int, tail: List | Nil) { +//│ fun count: () -> Int +//│ } +//│ class Nil() { +//│ fun count: () -> 0 +//│ } +//│ fun foo: forall 'a. {count: () -> 'a} -> 'a +//│ fun generate: Int -> (List | Nil) +//│ Int +//│ res +//│ = 2 +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +: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,) => {'(' {new Lambda1$2$1([x,]) {}} ')'({new Lambda1$3$2([]) {}},)} +//│ Code(List(foo$1(2,))) +//│ } +//│ Mono: +//│ +//│ Defunc result: +//│ main$$3() +//│ fun apply$Lambda1$2$1(this, f) = +//│ f match {case obj: Lambda1$3$2 => apply$Lambda1$3$2(obj, this.x)} +//│ fun foo$1(x) = +//│ new Lambda1$2$1 (x) match {case obj: Lambda1$2$1 => apply$Lambda1$2$1(obj, new Lambda1$3$2 () )} +//│ fun main$$3() = +//│ foo$1(#2) +//│ fun apply$Lambda1$3$2(this, z) = +//│ +(z, #1) +//│ class Lambda1$2$1(x) { +//│ } +//│ class Lambda1$3$2() { +//│ } +//│ fun foo: Int -> Int +//│ Int +//│ res +//│ = 3 + +: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,) => {'(' {new Lambda1$2$1([x,]) {}} ')'(+(x, 1,),)} +//│ Code(List(f$1(1,))) +//│ } +//│ Mono: +//│ +//│ Defunc result: +//│ main$$2() +//│ fun apply$Lambda1$2$1(this, y) = +//│ f$1(+(this.x, y)) +//│ fun f$1(x) = +//│ new Lambda1$2$1 (x) match {case obj: Lambda1$2$1 => apply$Lambda1$2$1(obj, +(x, #1))} +//│ fun main$$2() = +//│ f$1(#1) +//│ class Lambda1$2$1(x) { +//│ } +//│ fun f: Int -> nothing +//│ nothing +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + + +: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: +//│ +//│ Defunc result: +//│ main$$1() +//│ main$$2() +//│ fun f$1(x) = +//│ f$1(x) +//│ fun main$$2() = +//│ f$1(#1) +//│ fun main$$1() = +//│ f$1(#0) +//│ fun f: anything -> nothing +//│ nothing +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +:mono +class Cons(e: 'A, tail: Cons | Nil) { + fun count(): Int + fun count() = 1 + tail.count() +} +class Nil() { + fun count() = 0 +} +class Lambda(){ + fun apply(l) = + l.count() +} +class Lambda2(a: Int){ + fun apply(l) = + (new Cons(a, l)).count() +} +fun foo(x) = + x.apply(new Cons(1, new Nil())) + x.apply(new Nil()) +foo(new Lambda()) +foo(new Lambda2(2)) +//│ +//│ Lifted: +//│ TypingUnit { +//│ class Cons$1([e: 'A, tail: |(Cons, Nil,),]) { +//│ 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,) => {('(' new Cons$1([(this).a, l,]) {} ')').count()} +//│ } +//│ fun foo$1 = (x,) => {+((x).apply(new Cons$1([1, new Nil$2([]) {},]) {},), (x).apply(new Nil$2([]) {},),)} +//│ Code(List(foo$1(new Lambda$3([]) {},))) +//│ Code(List(foo$1(new Lambda2$4([2,]) {},))) +//│ } +//│ Mono: +//│ +//│ Defunc result: +//│ main$$5() +//│ main$$6() +//│ fun count$Cons$1(this) = +//│ +(#1, this.tail match {case obj: Cons$1 => count$Cons$1(obj); case obj: Nil$2 => count$Nil$2(obj)}) +//│ fun foo$1(x) = +//│ +(x match {case obj: Lambda2$4 => apply$Lambda2$4(obj, new Cons$1 (#1, new Nil$2 () ) ); case obj: Lambda$3 => apply$Lambda$3(obj, new Cons$1 (#1, new Nil$2 () ) )}, x match {case obj: Lambda2$4 => apply$Lambda2$4(obj, new Nil$2 () ); case obj: Lambda$3 => apply$Lambda$3(obj, new Nil$2 () )}) +//│ fun apply$Lambda$3(this, l) = +//│ l match {case obj: Cons$1 => count$Cons$1(obj); case obj: Nil$2 => count$Nil$2(obj)} +//│ fun count$Nil$2(this) = +//│ #0 +//│ fun apply$Lambda2$4(this, l) = +//│ new Cons$1 (this.a, l) match {case obj: Cons$1 => count$Cons$1(obj)} +//│ fun main$$6() = +//│ foo$1(new Lambda2$4 (#2) ) +//│ fun main$$5() = +//│ foo$1(new Lambda$3 () ) +//│ class Nil$2() { +//│ } +//│ class Lambda2$4(a) { +//│ } +//│ class Cons$1(e, tail) { +//│ } +//│ class Lambda$3() { +//│ } +//│ class Cons(e: nothing, tail: Cons | Nil) { +//│ fun count: () -> Int +//│ } +//│ class Nil() { +//│ fun count: () -> 0 +//│ } +//│ class Lambda() { +//│ fun apply: forall 'a. {count: () -> 'a} -> 'a +//│ } +//│ class Lambda2(a: Int) { +//│ fun apply: (Cons | Nil) -> Int +//│ } +//│ fun foo: {apply: (Cons | Nil) -> Int} -> Int +//│ Int +//│ res +//│ = 1 +//│ res +//│ = 3 + +:mono +class Cons(e: Int, tail: Cons | Nil) { + fun count(): Int + fun count() = 1 + tail.count() +} +class Nil() { + fun count() = 0 +} +fun foo(x) = + x(new Cons(1, new Nil())) + x(new Nil()) +foo(l => l.count()) +foo(l => (new Cons(2, l)).count()) +//│ +//│ Lifted: +//│ TypingUnit { +//│ class Cons$1([e: Int, tail: |(Cons, Nil,),]) { +//│ 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,) => ('(' new Cons$1([2, l,]) {} ')').count() +//│ } +//│ fun foo$1 = (x,) => {+(x(new Cons$1([1, new Nil$2([]) {},]) {},), x(new Nil$2([]) {},),)} +//│ Code(List(foo$1({new Lambda1$2$3([]) {}},))) +//│ Code(List(foo$1({new Lambda1$3$4([]) {}},))) +//│ } +//│ Mono: +//│ +//│ Defunc result: +//│ main$$5() +//│ main$$6() +//│ fun count$Cons$1(this) = +//│ +(#1, this.tail match {case obj: Cons$1 => count$Cons$1(obj); case obj: Nil$2 => count$Nil$2(obj)}) +//│ fun foo$1(x) = +//│ +(x match {case obj: Lambda1$3$4 => apply$Lambda1$3$4(obj, new Cons$1 (#1, new Nil$2 () ) ); case obj: Lambda1$2$3 => apply$Lambda1$2$3(obj, new Cons$1 (#1, new Nil$2 () ) )}, x match {case obj: Lambda1$3$4 => apply$Lambda1$3$4(obj, new Nil$2 () ); case obj: Lambda1$2$3 => apply$Lambda1$2$3(obj, new Nil$2 () )}) +//│ fun count$Nil$2(this) = +//│ #0 +//│ fun main$$6() = +//│ foo$1(new Lambda1$3$4 () ) +//│ fun main$$5() = +//│ foo$1(new Lambda1$2$3 () ) +//│ fun apply$Lambda1$3$4(this, l) = +//│ new Cons$1 (#2, l) match {case obj: Cons$1 => count$Cons$1(obj)} +//│ fun apply$Lambda1$2$3(this, l) = +//│ l match {case obj: Cons$1 => count$Cons$1(obj); case obj: Nil$2 => count$Nil$2(obj)} +//│ class Lambda1$3$4() { +//│ } +//│ class Nil$2() { +//│ } +//│ class Cons$1(e, tail) { +//│ } +//│ class Lambda1$2$3() { +//│ } +//│ class Cons(e: Int, tail: Cons | Nil) { +//│ fun count: () -> Int +//│ } +//│ class Nil() { +//│ fun count: () -> 0 +//│ } +//│ fun foo: ((Cons | Nil) -> Int) -> Int +//│ Int +//│ res +//│ = 1 +//│ res +//│ = 3 + +:mono +class Exp() { + virtual fun derive(x: Int): Exp + virtual fun derive(x: Int) = Exp() + virtual fun isEmpty(): Bool + virtual fun isEmpty() = false +} +class E() extends Exp { + fun derive(x) = + new E + fun isEmpty() = + false +} +class Ep() extends Exp { + fun derive(x) = + new E + fun isEmpty() = + true +} +class Ch(i: Int) extends Exp { + fun derive(x) = + if x == i then new Ep else new E + fun isEmpty() = + false +} +class A(e1: Exp, e2: Exp) extends Exp { + fun derive(x) = + new A(e1.derive(x), e2.derive(x)) + fun isEmpty() = + e1.isEmpty() || e2.isEmpty() +} +class C(e1: Exp, e2: Exp) extends Exp { + fun derive(x) = + if e1.isEmpty() then new A(new C(e1.derive(x), e2), e2.derive(x)) else new C(e1.derive(x), e2) + fun isEmpty() = + e1.isEmpty() && e2.isEmpty() +} +(new C(new Ch(1), new A(new Ch(2), new Ch(3)))).derive(0).isEmpty() +//│ +//│ Lifted: +//│ TypingUnit { +//│ class Exp$1([]) { +//│ fun derive = (x: Int) -> Exp$1 +//│ fun derive = (x: Int,) => Exp$1() +//│ fun isEmpty = () -> Bool +//│ fun isEmpty = () => false +//│ } +//│ class E$2([]): Exp$1() { +//│ fun derive = (x,) => {new E$2([]) {}} +//│ fun isEmpty = () => {false} +//│ } +//│ class Ep$3([]): Exp$1() { +//│ fun derive = (x,) => {new E$2([]) {}} +//│ fun isEmpty = () => {true} +//│ } +//│ class Ch$4([i: Int,]): Exp$1() { +//│ fun derive = (x,) => {if (==(x, (this).i,)) then new Ep$3([]) {} else new E$2([]) {}} +//│ fun isEmpty = () => {false} +//│ } +//│ class A$5([e1: Exp, e2: Exp,]): Exp$1() { +//│ fun derive = (x,) => {new A$5([((this).e1).derive(x,), ((this).e2).derive(x,),]) {}} +//│ fun isEmpty = () => {||(((this).e1).isEmpty(), ((this).e2).isEmpty(),)} +//│ } +//│ class C$6([e1: Exp, e2: Exp,]): Exp$1() { +//│ fun derive = (x,) => {if (((this).e1).isEmpty()) then new A$5([new C$6([((this).e1).derive(x,), (this).e2,]) {}, ((this).e2).derive(x,),]) {} else new C$6([((this).e1).derive(x,), (this).e2,]) {}} +//│ fun isEmpty = () => {&&(((this).e1).isEmpty(), ((this).e2).isEmpty(),)} +//│ } +//│ Code(List((('(' new C$6([new Ch$4([1,]) {}, new A$5([new Ch$4([2,]) {}, new Ch$4([3,]) {},]) {},]) {} ')').derive(0,)).isEmpty())) +//│ } +//│ Mono: +//│ +//│ Defunc result: +//│ main$$6() +//│ fun isEmpty$E$2(this) = +//│ false +//│ fun isEmpty$A$5(this) = +//│ ||(this.e1 match {case obj: Ch$4 => isEmpty$Ch$4(obj)}, this.e2 match {case obj: Ch$4 => isEmpty$Ch$4(obj)}) +//│ fun isEmpty$Ch$4(this) = +//│ false +//│ fun derive$A$5(this, x) = +//│ new A$5 (this.e1 match {case obj: Ch$4 => derive$Ch$4(obj, x)}, this.e2 match {case obj: Ch$4 => derive$Ch$4(obj, x)}) +//│ fun isEmpty$C$6(this) = +//│ &&(this.e1 match {case obj: Ep$3 => isEmpty$Ep$3(obj); case obj: E$2 => isEmpty$E$2(obj)}, this.e2 match {case obj: A$5 => isEmpty$A$5(obj)}) +//│ fun derive$C$6(this, x) = +//│ if this.e1 match {case obj: Ch$4 => isEmpty$Ch$4(obj)} then new A$5 (new C$6 (this.e1 match {case obj: Ch$4 => derive$Ch$4(obj, x)}, this.e2) , this.e2 match {case obj: A$5 => derive$A$5(obj, x)}) else new C$6 (this.e1 match {case obj: Ch$4 => derive$Ch$4(obj, x)}, this.e2) +//│ fun main$$6() = +//│ new C$6 (new Ch$4 (#1) , new A$5 (new Ch$4 (#2) , new Ch$4 (#3) ) ) match {case obj: C$6 => derive$C$6(obj, #0)} match {case obj: C$6 => isEmpty$C$6(obj)} +//│ fun derive$Ch$4(this, x) = +//│ if ==(x, this.i) then new Ep$3 () else new E$2 () +//│ fun isEmpty$Ep$3(this) = +//│ true +//│ class A$5(e1, e2): Exp$1() { +//│ } +//│ class E$2(): Exp$1() { +//│ } +//│ class C$6(e1, e2): Exp$1() { +//│ } +//│ class Ch$4(i): Exp$1() { +//│ } +//│ class Ep$3(): Exp$1() { +//│ } +//│ class Exp$1() { +//│ } +//│ class Exp() { +//│ fun derive: (x: Int) -> Exp +//│ fun isEmpty: () -> Bool +//│ } +//│ class E() extends Exp { +//│ fun derive: anything -> E +//│ fun isEmpty: () -> false +//│ } +//│ class Ep() extends Exp { +//│ fun derive: anything -> E +//│ fun isEmpty: () -> true +//│ } +//│ class Ch(i: Int) extends Exp { +//│ fun derive: Num -> (E | Ep) +//│ fun isEmpty: () -> false +//│ } +//│ class A(e1: Exp, e2: Exp) extends Exp { +//│ fun derive: Int -> A +//│ fun isEmpty: () -> Bool +//│ } +//│ class C(e1: Exp, e2: Exp) extends Exp { +//│ fun derive: Int -> (A | C) +//│ fun isEmpty: () -> Bool +//│ } +//│ Bool +//│ res +//│ = false + + +:mono +val anyUnknown = false +class List(l: List | Nil, hasTail: Bool) {} +class Nil(hasTail: Bool) {} +fun gen() = + if anyUnknown then new List(gen(), true) else new Nil(false) +gen() +//│ +//│ Lifted: +//│ TypingUnit { +//│ class List$1([l: |(List, Nil,), hasTail: Bool,]) {} +//│ class Nil$2([hasTail: Bool,]) {} +//│ let anyUnknown$2 = () => false +//│ fun gen$1 = () => {if (anyUnknown) then new List$1([gen$1(), true,]) {} else new Nil$2([false,]) {}} +//│ Code(List(gen$1())) +//│ } +//│ Mono: +//│ +//│ Defunc result: +//│ main$$4() +//│ fun anyUnknown$2() = +//│ false +//│ fun gen$1() = +//│ if anyUnknown then new List$1 (gen$1(), true) else new Nil$2 (false) +//│ fun main$$4() = +//│ gen$1() +//│ class Nil$2(hasTail) { +//│ } +//│ class List$1(l, hasTail) { +//│ } +//│ val anyUnknown: false +//│ class List(l: List | Nil, hasTail: Bool) +//│ class Nil(hasTail: Bool) +//│ fun gen: () -> (List | Nil) +//│ List | Nil +//│ anyUnknown +//│ = false +//│ res +//│ = Nil {} + + + +:mono +class Foo(x: Int){ + fun bar(y) = x+y + fun boo(z) = bar(z)+x +} +(new 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(('(' new Foo$1([1,]) {} ')').boo(2,))) +//│ } +//│ Mono: +//│ +//│ Defunc result: +//│ main$$1() +//│ fun boo$Foo$1(this, z) = +//│ +(this match {case obj: Foo$1 => bar$Foo$1(obj, z)}, this.x) +//│ fun bar$Foo$1(this, y) = +//│ +(this.x, y) +//│ fun main$$1() = +//│ new Foo$1 (#1) match {case obj: Foo$1 => boo$Foo$1(obj, #2)} +//│ class Foo$1(x) { +//│ } +//│ class Foo(x: Int) { +//│ fun bar: Int -> Int +//│ fun boo: Int -> Int +//│ } +//│ Int +//│ res +//│ = 4 + +:mono +class OneInt(a: Int){ + fun fac: () -> Int + fun fac = () -> + if(a > 0) then (new OneInt(a - 1)).fac() else 1 +} +(new OneInt(10)).fac() +//│ +//│ Lifted: +//│ TypingUnit { +//│ class OneInt$1([a: Int,]) { +//│ fun fac = () -> Int +//│ fun fac = () => {if ('(' >((this).a, 0,) ')') then ('(' new OneInt$1([-((this).a, 1,),]) {} ')').fac() else 1} +//│ } +//│ Code(List(('(' new OneInt$1([10,]) {} ')').fac())) +//│ } +//│ Mono: +//│ +//│ Defunc result: +//│ main$$1() +//│ fun fac$OneInt$1(this) = +//│ if >(this.a, #0) then new OneInt$1 (-(this.a, #1)) match {case obj: OneInt$1 => fac$OneInt$1(obj)} else #1 +//│ fun main$$1() = +//│ new OneInt$1 (#10) match {case obj: OneInt$1 => fac$OneInt$1(obj)} +//│ class OneInt$1(a) { +//│ } +//│ class OneInt(a: Int) { +//│ fun fac: () -> Int +//│ } +//│ Int +//│ res +//│ = 1 + +//:mono +//:e // FIXME: Mutable Parameters +//trait AnyFoo { +//} +//class FooPlus(#a): AnyFoo { +// fun bar(b) = a + b +//} +//class FooMinus(#a): AnyFoo { +// fun bar(b) = a - b +//} +//fun f(x) = x.bar(42) +//f(new FooPlus(1)) +//f(new FooMinus(2)) + +:mono +val any = -20 +fun f(x) = + if x > any then 0 + else g(x - 1) +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,)) then 0 else g$2(-(x, 1,),)} +//│ fun g$2 = (x,) => {if (>(x, any,)) then g$2(-(x, 1,),) else f$1(-(x, 2,),)} +//│ Code(List(g$2(1,))) +//│ } +//│ Mono: +//│ +//│ Defunc result: +//│ main$$3() +//│ fun any$3() = +//│ #-20 +//│ fun f$1(x) = +//│ if >(x, any) then #0 else g$2(-(x, #1)) +//│ fun g$2(x) = +//│ if >(x, any) then g$2(-(x, #1)) else f$1(-(x, #2)) +//│ fun main$$3() = +//│ g$2(#1) +//│ val any: -20 +//│ fun f: Int -> 0 +//│ fun g: Int -> 0 +//│ 0 +//│ any +//│ = -20 +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +:mono +class OneInt(a: Int){ + fun get = () -> a +} +class OneBool(b: Bool){ + fun get = () -> b +} +(if b 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())) +//│ } +//│ Mono: +//│ +//│ Defunc result: +//│ main$$2() +//│ fun get$OneInt$1(this) = +//│ this.a +//│ 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)} +//│ class OneInt$1(a) { +//│ } +//│ class OneBool$2(b) { +//│ } +//│ class OneInt(a: Int) { +//│ fun get: () -> Int +//│ } +//│ class OneBool(b: Bool) { +//│ fun get: () -> Bool +//│ } +//│ Int | false | true +//│ res +//│ = true + +:mono +class Bar(x: Int) { + fun foo(x) = x + fun FooMinus(y: Int) = x + y + fun car = foo(2) +} +class Car { + fun da(b: Bar) = b.foo(2) +} +fun baz(b: Bar) = b.foo(2) +let bar = Bar(42) +baz(bar) +(new 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,))) +//│ Code(List(('(' new Car$2([]) {} ')').da(Bar$1(1337,),))) +//│ Code(List((bar).car)) +//│ } +//│ Mono: +//│ +//│ Defunc result: +//│ main$$4() +//│ main$$5() +//│ main$$6() +//│ fun bar$1() = +//│ Bar$1(#42) +//│ fun da$Car$2(this, b) = +//│ b match {} +//│ fun main$$6() = +//│ bar.car +//│ fun baz$2(b) = +//│ b match {} +//│ fun main$$5() = +//│ new Car$2 () match {case obj: Car$2 => da$Car$2(obj, Bar$1(#1337))} +//│ fun main$$4() = +//│ baz$2(bar) +//│ class Bar$1(x) { +//│ } +//│ class Car$2() { +//│ } +//│ class Bar(x: Int) { +//│ fun FooMinus: (y: Int) -> Int +//│ fun car: 2 +//│ fun foo: forall 'a. 'a -> 'a +//│ } +//│ class Car { +//│ constructor() +//│ fun da: (b: Bar) -> 2 +//│ } +//│ fun baz: (b: Bar) -> 2 +//│ let bar: Bar +//│ 2 +//│ bar +//│ = Bar {} +//│ res +//│ = 2 +//│ res +//│ = 2 +//│ res +//│ = 2 + +:mono +val c = 5 +class Sup(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 +} +(new Sub(10)).foo() +(new Sub2(c)).foo() +//│ +//│ Lifted: +//│ TypingUnit { +//│ class Sup$1([a: Int,]) {fun foo = () => (this).a} +//│ class Sub$2([b: Int,]): Sup$1(+((this).b, (this).b,),) {} +//│ class Sub2$3([c: Int,]): Sub$2(+((this).c, (this).c,),) {fun foo = () => +((this).a, (this).c,)} +//│ let c$1 = () => 5 +//│ Code(List(('(' new Sub$2([10,]) {} ')').foo())) +//│ Code(List(('(' new Sub2$3([c,]) {} ')').foo())) +//│ } +//│ Mono: +//│ +//│ Defunc result: +//│ main$$4() +//│ main$$5() +//│ fun c$1() = +//│ #5 +//│ fun main$$5() = +//│ new Sub2$3 (c) match {case obj: Sub2$3 => foo$Sub2$3(obj)} +//│ fun main$$4() = +//│ new Sub$2 (#10) match {case obj: Sub$2 => foo$Sup$1(obj)} +//│ fun foo$Sup$1(this) = +//│ this.a +//│ fun foo$Sub2$3(this) = +//│ +(this.a, this.c) +//│ class Sub2$3(c): Sub$2(+(this.c, this.c)) { +//│ } +//│ class Sup$1(a) { +//│ } +//│ class Sub$2(b): Sup$1(+(this.b, this.b)) { +//│ } +//│ val c: 5 +//│ class Sup(a: Int) { +//│ fun foo: () -> Int +//│ } +//│ class Sub(b: Int) extends Sup { +//│ fun foo: () -> Int +//│ } +//│ class Sub2(c: Int) extends Sub, Sup { +//│ fun foo: () -> Int +//│ } +//│ Int +//│ c +//│ = 5 +//│ res +//│ = 20 +//│ res +//│ = 47 + +:mono +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() +//│ +//│ Lifted: +//│ TypingUnit { +//│ class Foo$1([f: ->(Int, Int,),]) {fun foo = () => (this).f(1,)} +//│ class F1$2_Lambda1$1$4([par$F1$2,]) {fun apply = (x,) => +(x, 1,)} +//│ class F1$2([]): Foo$1({new F1$2_Lambda1$1$4([this,]) {}},) {} +//│ class F2$3_Lambda1$2$5([par$F2$3,]) {fun apply = (x,) => +(x, 2,)} +//│ class F2$3([]): Foo$1({new F2$3_Lambda1$2$5([this,]) {}},) {} +//│ Code(List(('(' new F1$2([]) {} ')').foo())) +//│ Code(List(('(' new F2$3([]) {} ')').foo())) +//│ } +//│ Mono: +//│ +//│ Defunc result: +//│ main$$5() +//│ main$$6() +//│ fun apply$F2$3_Lambda1$2$5(this, x) = +//│ +(x, #2) +//│ fun foo$Foo$1(this) = +//│ this match {case obj: Foo$1 => obj.f match {case obj$F2$3_Lambda1$2$5: F2$3_Lambda1$2$5 => apply$F2$3_Lambda1$2$5(obj$F2$3_Lambda1$2$5, #1); case obj$F1$2_Lambda1$1$4: F1$2_Lambda1$1$4 => apply$F1$2_Lambda1$1$4(obj$F1$2_Lambda1$1$4, #1)}} +//│ fun main$$6() = +//│ new F2$3 () match {case obj: F2$3 => foo$Foo$1(obj)} +//│ fun main$$5() = +//│ new F1$2 () match {case obj: F1$2 => foo$Foo$1(obj)} +//│ fun apply$F1$2_Lambda1$1$4(this, x) = +//│ +(x, #1) +//│ class F1$2(): Foo$1(new F1$2_Lambda1$1$4 (this) ) { +//│ } +//│ class F2$3_Lambda1$2$5(par$F2$3) { +//│ } +//│ class F2$3(): Foo$1(new F2$3_Lambda1$2$5 (this) ) { +//│ } +//│ class Foo$1(f) { +//│ } +//│ class F1$2_Lambda1$1$4(par$F1$2) { +//│ } +//│ class Foo(f: Int -> Int) { +//│ fun foo: () -> Int +//│ } +//│ class F1() extends Foo { +//│ fun foo: () -> Int +//│ } +//│ class F2() extends Foo { +//│ fun foo: () -> Int +//│ } +//│ Int +//│ res +//│ = 2 +//│ res +//│ = 3 diff --git a/compiler/shared/test/scala/mlscript/compiler/Test.scala b/compiler/shared/test/scala/mlscript/compiler/Test.scala index df325ec59c..7f4c816981 100644 --- a/compiler/shared/test/scala/mlscript/compiler/Test.scala +++ b/compiler/shared/test/scala/mlscript/compiler/Test.scala @@ -1,28 +1,44 @@ -package mlscript -package compiler +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.{DiffTests, ModeType, TypingUnit} +import mlscript.compiler.debug.TreeDebug +import mlscript.compiler.mono.Monomorph +import mlscript.compiler.printer.ExprPrinter +import mlscript.compiler.mono.MonomorphError class DiffTestCompiler extends DiffTests { import DiffTestCompiler.* override def postProcess(mode: ModeType, basePath: List[Str], testName: Str, unit: TypingUnit): List[Str] = val outputBuilder = StringBuilder() - outputBuilder ++= "Parsed:\n" - outputBuilder ++= showStructure(unit) outputBuilder ++= "\nLifted:\n" var rstUnit = unit; try - rstUnit = ClassLifter().liftTypingUnit(unit) + rstUnit = ClassLifter(mode.fullExceptionStack).liftTypingUnit(unit) outputBuilder ++= PrettyPrinter.showTypingUnit(rstUnit) catch case NonFatal(err) => outputBuilder ++= "Lifting failed: " ++ err.toString() - if mode.fullExceptionStack then outputBuilder ++= - "\n" ++ err.getStackTrace().map(_.toString()).mkString("\n") + if mode.fullExceptionStack then + outputBuilder ++= "\n" ++ err.getStackTrace().map(_.toString()).mkString("\n") + if(mode.mono){ + outputBuilder ++= "\nMono:\n" + val treeDebug = new TreeDebug() + try{ + val monomorph = new Monomorph(treeDebug) + val monomorphized = monomorph.defunctionalize(rstUnit) + outputBuilder ++= "\nDefunc result: \n" + outputBuilder ++= ExprPrinter.print(monomorphized) + outputBuilder ++= "\n" + }catch{ + case error: MonomorphError => outputBuilder ++= (error.getMessage() :: error.getStackTrace().map(_.toString()).toList).mkString("\n") + // case error: StackOverflowError => outputBuilder ++= (error.getMessage() :: error.getStackTrace().take(40).map(_.toString()).toList).mkString("\n") + } + // outputBuilder ++= treeDebug.getLines.mkString("\n") + } outputBuilder.toString().linesIterator.toList override protected lazy val files = allFiles.filter { file => diff --git a/doc/mls-codebase-doc.md b/doc/mls-codebase-doc.md new file mode 100644 index 0000000000..50d9bac63e --- /dev/null +++ b/doc/mls-codebase-doc.md @@ -0,0 +1,377 @@ +# Documentation of the MLscript Codebase + +This is the documentation of the MLscript codebase. + +## Overview + +This codebase of the MLscript Programming Language has all the basic components +of a static-typed programming language compiler: lexer, parser, typer, and code generator. +For testing, there is a web demo of MLscript as well as a test suite. +We now give a high-level introduction to each compiler component and its correspondence to +our Scala sources. Note that source file paths are rooted in `/shared/src/main/scala/mlscript`. + +### Lexing + +The lexer accepts source strings and returns tokens to be parsed. +The corresponding files are: + +- `NewLexer.scala` contains the lexer class. +- `Token.scala` contains the token data types. + +### Parsing + +The parser accepts tokens generated by the lexer and +returns an abstract syntax tree of the input program in the surface syntax. +The corresponding files are: + +- `NewParser.scala` contains the parser class. +- `syntax.scala` contains the **surface** syntax data types of the language. + +### Typing + +The typer accepts an abstract syntax tree of a program +and performs type checking. +MLscript's typer supports principal type inference with subtyping. +For more information about the type system, +please refer to [MLstruct](https://dl.acm.org/doi/abs/10.1145/3563304). + +The corresponding files are: +- `Typer.scala` contains the typer class. +- `TypeSimplifier.scala` contains type simplification algorithms to simplify +inferred types. +- `ucs/Desugarer.scala` contains class `ucs.Desugarer` which implements desugaring +methods. +- `TypeDefs.scala` and `NuTypeDefs.scala` contain class `TypeDef` and methods for +declarations like classes, interfaces, and type aliases. +- `ConstraitSolver.scala` contains class `ConstraintSolver` which solves subtyping +constraints. +- `NormalForms.scala` contains class `NormalForms` which provides the infrastructure +to solve tricky subtyping constraints with disjunct normal forms (DNF) on the left +and conjunct normal forms (CNF) on the right. +- `TyperDatatypes.scala` contains class `TyperDatatypes` which includes +data types to support **internal** representation of types with mutable states to support +type inference with subtyping. +- `TyperHelpers.scala` contains class `TyperHelpers` that provides helper methods +for the typer. + +Note that the inheritance relationships between these typer classes do *not* have any actual semantics +- we are following Scala's *Cake Pattern*. Typer classes will be finally composed +into the `Typer` class by inheritance. + +### Code Generation + +The code generator translates MLscript AST into JavaScript AST and generates the corresponding JavaScript code. + +The corresponding files are: + +- `codegen/Codegen.scala` contains definitions of JavaScript AST nodes + and methods for JavaScript code generation. +- `codegen/Scope.scala` contains class `Scope` which manages symbols + and provides hygienic runtime name generation. +- `codegen/Symbol.scala` contains classes `NewClassSymbol`, `MixinSymbol`, + and `ModuleSymbol` which include information on `class`, `mixin` and `module` definitions. +- `JSBackend.scala` contains class `JSBackend` that translates an MLscript AST + into a JavaScript AST. Classes `JSWebBackend` and `JSTestBackend` inherit class `JSBackend` + and generate adapted code for the web demo and the test suite. + +### Web Demo and Testing + + +Testing of MLscript works as follows: + - the MLscript compiler reads the given test file one code block at a time (code blocks are separated by empty lines); + - after reading the code block, it outputs the inferred types as well as any type errors encountered; + - after that, it executes the code block in NodeJS (by shelling out to a `node` process) and outputs the results. + +We have a web demo for users to test our implementation in any modern browser. +It has a textbox for MLscript source code input and it produces typing and running +results live. The implementation can be tried online at https://hkust-taco.github.io/superoop/ +and locally in `/js/src/main/scala/Main.scala`. + +We have a "`diff`-based" test suite for our implementation. +It detects changes to MLscript test sources (using git), +generates typing and running results, and inserts those results +into test sources. The diff-based testing implementation is in +`/shared/src/test/scala/mlscript/DiffTests.scala`. +MLscript test sources are in `/shared/src/test/diff`. + +## Detailed Introduction + +We now introduce the implementation of each compiler component +in more detail. + +### Lexing + +Class `NewLexer` in `NewLexer.scala` is the lexer class. It takes an `origin` object, +which contains the original source string together with the source file name, +the number of the first line, and some helper functions. Lazy value `tokens` generates +a list of tokens with their location in the source code. Lazy value `bracketedTokens` +converts the lexed tokens into *structured tokens*, +which use `BRACKETS` constructs instead of `OPEN_BRACKET`/`CLOSE_BRACKET` and `INDENT`/`DEINDENT`. +Token and structured token data types can be found in `Tokens.scala`. + +### Parsing + +Class `NewParser` in `NewParser.scala` is the parser class. It takes a list +of structured tokens with their location information. Method `typingUnit` +calls method `block` to parse the token list into a list of `Statement` or +`IfBody` (defined in `syntax.scala`), filters out unexpected `then/else` +clauses introduced by `Ifbody`, and returns a `TypingUnit` (a list of `Statement`). + +File `syntax.scala` contains *immutable* surface syntax data types of MLscript, +which are different from the internal representations in the typer for later type inference. +Here we introduce several surface syntax data types: + +- Classes `Decl`, `TypeDef`, `MethodDef` are deprecated. +- Class `TypeDefKind` includes type definition kinds: classes and mixins, etc. +- Class `Term` includes MLscript term data types. Case class `Bind` is no longer used. +Case class `Splc` is for the rest of a parameter list, similar to the rest parameter in JavaScript. +Case classes `Forall` and `Inst` are for first-class polymorphism. +- Class `IfBody` includes if-then-else structure data types. +- Class `CaseBranches` includes case branch data types for MLscript pattern matching. +- Class `TypeLike` includes `Type`, `Signature`, and `NuDecl`. +- Class `Type` includes MLscript type data types. Case class `Rem` is for record member removal. +Case class `WithExtension` is for record type extension. For example, `A with {x : int}` +is equivalent to `A\x & {x : int}`. +- Class `TypeVar` represents the type variable. Its identifier can be an `Int` +generated internally by the compiler or `Str` specified by the user. +- Class `NuTypeDef` is a `NuDecl` for type definitions. +Note that it has optional `superAnnot` +and `thisAnnot` for precisely-typed open recursion. +- Class `NuFunDef` is a `NuDecl` for function and let-bindings. + +### Typing + +The MLscript typer (class `Typer`) works with a typing context (class `Ctx`) which +mainly maintains all global and local bindings of names to their types. +The typer accepts a typing unit from the parser, types the typing unit, and returns a typed typing unit. +The typed typing unit +is sent to the type simplifier and is finally expanded, i.e., converted +back to types in the surface syntax for presentation. +The typer has **internal** representations of types +(defined in `TyperDatatypes.scala`) +with mutable states for type inference with subtyping. + +We first introduce several typer data types defined in `TyperDatatypes.scala`: + +- Class `TypeProvenance` stores the location where a type is introduced. +- Class `LazyTypeInfo` is for type definitions including classes, mixins, modules. +Its type is lazily computed to support *mutual recursive* type +definitions. It has a `complete` method to complete typing lazily typed definitions. +- Class `PolymorphicType` represents a type with universally quantified type variables. +By convention, in the type body, type variables of levels greater than +the polymorphic type's level are polymorphic. +- Class `SimpleType` is a general type form of all types. +It requires a method `level` for level-based polymorphism. +- Class `BaseType` includes base types such as function, array, tuple, and class tag types. +It can later be refined by `RecordType`. +- Class `RecordType` is a record type. It has a list of bindings from record fields to their types. +- Class `SpliceType` is not used for now. +- Class `ProxyType` is a derived type form to store more type provenance information. +- Class `TypeRef` is a reference to named types such as type definitions like classes. +It has a list of type arguments. A type reference with type arguments is expanded to +a class tag type with the class's type parameters refined by the corresponding type arguments as type members. +For example, `Foo[Int]` is expanded to `#Foo & {Foo.A: int..int}`. +- Class `TypeTag` has different kinds of type tags including class tags and abstract tags, etc. +- Class `FieldType` represents a term field type or a type member. +When it represents a term field type, `lb` represents if the type is mutable. +Otherwise, `lb` is the lower bound of the type member. +- Class `TypeVariable` represents a type variable, which has upper and lower bounds +for type inference with subtyping. + +Method `typeTypingUnit` in class `NuTypeDefs` accepts the typing unit to type. It inspects each statement +in the typing unit. If the statement is a type definition, a `DelayedTypeInfo` (which is a subclass of `LazyTypeInfo`) +is produced and stored in the typing context (note the typing context only uses `tyDefs2` to store +type definitions). Otherwise, it desugars the statement and calls `typeTerms` to type +the desugared statements. For a single `Term`, it is passed to `typeTerm` to type. +Method `typeTerm` in class `Typer` types a term. If the term needs type information of a `LazyTypeInfo`, +the typing of that lazily typed definition will be completed. Subtyping constraints are generated during typing +and sent to `ConstraintSolver` to propagate constraints to type variables. +For more about type inference of subtyping, please refer to [MLstruct](https://dl.acm.org/doi/abs/10.1145/3563304). + +Of particular interest, +we introduce how classes and mixins are typed to implement precisely-typed open recursion in more detail. +Method `complete` of `DelayedTypeInfoImpl`, +types type definitions: classes, modules, and mixins and let-/fun-bindings. + +When a class (`Cls` which is a `NuTypeDef`) is typed, class fields are first +added into the typing context, and `this` is associated with a fresh type variable. +The `inherit` helper methods deal with the inheritance clauses of the type definitions. +The inheritance process starts with an empty record type as the initial `super` type. +It inspects each parent, accumulates members of parents, and updates the `super` type on the way. +For each parent, +if it is a mixin, and the typing context has that mixin defined, it completes the type of the mixin +and freshens each type variable of the mixin, as each mixin's type should be constrained +differently at different use-sites. Then, two subtyping constraints are generated: +the current `super` type and the final +object type (`this` type) should be subtypes of the mixin's `super` and `this` type refinements. +Finally, the mixin's members are accumulated to the class, and the current `super` type is +updated using `WithType` because methods in mixins are always *overriding*. +After processing the whole inheritance clause, +we update the current `super` type with the class fields' types as `thisType`, and we constrain that +the resulting `thisType` (i.e. the final object type) should be a subtype of `finalType` +which is a type variable with all `this` type refinements of mixins accumulated. + +Typing of mixins is not that surprising. We associate `this` and `super` +with fresh type variables in the typing context and then type the mixin body. + +### Code Generation + +The code generation consists of three steps. +Firstly, class `JSBackend` translates MLscript data types (i.e. class `NuTypeDef`) +into corresponding symbols. Then class `JSBackend` generates JavaScript AST nodes +based on those symbols. +Finally, we generate JavaScript code from JavaScript AST nodes. + +The first step is implemented in the method `declareNewTypeDefs`. +Here we extract information (including name, parameter list, type, members, parents, and so on) +of classes, mixins, and modules from the given `NuTypeDef` list and generate +a hygienic runtime name for each symbol. + +In the second step, we translate `NewClassSymbol`, `MixinSymbol`, and `ModuleSymbol` +into JavaScript AST nodes by using methods `translateNewClassDeclaration`, `translateMixinDeclaration`, and `translateModuleDeclaration`. +These three methods invoke another method `translateNewTypeDefinition` to translate +classes, mixins, and modules into JavaScript classes. +The method `translateNewClassMember` contains the translation of members. +We call `translateParents` to get the parent class of a type. +Assuming we have code: +```ts +module A extends B, C +``` + +The method `translateParents` processes the inheritance clause in a left-to-right way: + +- First, we process the parent `B`: + - If `B` is a `class` or a `module`, the JS class definition would be `class A extends B`. + - If `B` is a `mixin`, we need a base class for `B`. + Here we choose `Object` in JavaScript and the JS class definition would be `class A extends B(Object)` +- Then we process the parent `C`: + - If `C` is a `mixin`, we can use `B(Object)` as `C`'s base class. + The JS class definition would be `class A extends C(B(Object))`. + - Otherwise, we reject the code because a JavaScript class can have only one parent class. + - If module `A` has more parents on the right of `C`, + we process them similarly as we deal with `C`. + +If there are initialization parameters in the parent list, +we move the arguments into the class constructor and pass them to `super()`. +Note we need to reverse the order of arguments of `mixin`. +For example, assume we have MLscript code below: + +```ts +module A extends MixinA(1), MixinB(2, 3), MixinC(4) +``` + +The parameters in `super()` of `A` would be: +```js +super(4, 2, 3, 1); +``` + +We generate the JavaScript classes inside `typing_unit` objects. +Note we create `...rest` parameters in each constructor of `mixin` +because we have no information about the actual parent mixin until the mixin composition is finished. +For modules, we store the instance of the JavaScript class in the cache. +For classes, if they have primitive parameter lists, +we store the arrow functions in the cache as class constructors that instantiate classes. +Mixins have no constructor because of the uncertainty of the `base` parameter of mixins. + +In the final step, we emit the JavaScript code by using `toSourceCode` methods in each JavaScript AST node class. + +For a class in MLscript: +```ts +class Lit(n: int) +``` + +The generated code would be: +```js +class TypingUnit { + #Lit; + constructor() { + } + get Lit() { + const qualifier = this; + if (this.#Lit === undefined) { + class Lit { + #n; + constructor(n) { + this.#n = n; + } + static + unapply(x) { + return [x.#n]; + } + }; + this.#Lit = ((n) => Object.freeze(new Lit(n))); + this.#Lit.class = Lit; + this.#Lit.unapply = Lit.unapply; + } + return this.#Lit; + } +} +const typing_unit = new TypingUnit; +globalThis.Lit = typing_unit.Lit; +``` + +For a mixin in MLscript: +```ts +mixin EvalBase { + fun eval(e) = + if e is + Lit(n) then n: int +} +``` + +The generated code would be: +```js +class TypingUnit { + constructor() { + } + EvalBase(base) { + const qualifier = this; + return (class EvalBase extends base { + constructor(...rest) { + super(...rest); + } + eval(e) { + return ((() => { + let a; + return (a = e, a instanceof Lit.class ? (([n]) => n)(Lit.unapply(e)) : (() => { + throw new Error("non-exhaustive case expression"); + })()); + })()); + } + }); + } +} +const typing_unit = new TypingUnit; +globalThis.EvalBase = ((base) => typing_unit.EvalBase(base)); +``` + +For a module in MLscript: +```ts +module TestLang extends EvalBase, EvalNeg, EvalNegNeg +``` + +The generated code would be like this: +```js +class TypingUnit { + #TestLang; + constructor() { + } + get TestLang() { + const qualifier = this; + if (this.#TestLang === undefined) { + class TestLang extends EvalNegNeg(EvalNeg(EvalBase(Object))) { + constructor() { + super(); + } + } + this.#TestLang = new TestLang(); + this.#TestLang.class = TestLang; + } + return this.#TestLang; + } +} +const typing_unit = new TypingUnit; +globalThis.TestLang = typing_unit.TestLang; +``` + +For more code generation examples, please check the test source `shared/src/test/diff/codegen/Mixin.mls`. diff --git a/index.css b/index.css index 3c53c67fa0..4e99503067 100644 --- a/index.css +++ b/index.css @@ -20,7 +20,7 @@ h1, p{ #content { width: 100%; padding: 0px; - height: 600px; + height: 750px; display: grid; grid-template-columns: 50% 50%; grid-template-rows: 100%; diff --git a/index.html b/index.html index 100da6b69c..b857718d21 100644 --- a/index.html +++ b/index.html @@ -9,25 +9,20 @@

MLscript online demonstration

-
""" - } - - implicit val raise: Raise = throw _ - implicit var ctx: Ctx = - try processTypeDefs(typeDefs)(Ctx.init, raise) - catch { - case err: ErrorReport => - res ++= formatError("class definitions", err) - Ctx.init - } - implicit val extrCtx: Opt[typer.ExtrCtx] = N - - val curBlockTypeDefs = typeDefs.flatMap(td => ctx.tyDefs.get(td.nme.name)) - typer.computeVariances(curBlockTypeDefs, ctx) - - def getType(ty: typer.SimpleType): Type = { - object SimplifyPipeline extends typer.SimplifyPipeline { - def debugOutput(msg: => Str): Unit = println(msg) - } - val sim = SimplifyPipeline(ty, S(true))(ctx) - val exp = typer.expandType(sim) - exp - } - def formatBinding(nme: Str, ty: SimpleType): Str = { - val exp = getType(ty) - s""" - val - ${nme}: - ${exp.show} -
""" - } def underline(fragment: Str): Str = s"$fragment" @@ -236,7 +46,7 @@ object Main { sb ++= htmlLineBreak () } - val sctx = Message.mkCtx(diag.allMsgs.iterator.map(_._1), "?") + val sctx = Message.mkCtx(diag.allMsgs.iterator.map(_._1), newDefs=true, "?") val headStr = diag match { case ErrorReport(msg, loco, src) => totalTypeErrors += 1 @@ -278,7 +88,7 @@ object Main { if (l =:= endLineNum) endLineCol else curLine.length + 1 val front = curLine.slice(0, c - 1) val middle = underline(curLine.slice(c - 1, lastCol - 1)) - val back = curLine.slice(lastCol - 1, curLine.size) + val back = curLine.slice(lastCol - 1, curLine.length) output(s"$prepre$pre\t$front$middle$back") c = 1 l += 1 @@ -290,83 +100,144 @@ object Main { sb.toString } - var declared: Map[Var, ST] = Map.empty - - def htmlize(str: Str): Str = - str.replace("\n", "
").replace(" ", " ") - - var decls = stmts - while (decls.nonEmpty) { - val d = decls.head - decls = decls.tail - try d match { - case d @ Def(isrec, nme, L(rhs), _) => - val ty_sch = typeLetRhs(isrec, nme.name, rhs)(ctx, raise, Map.empty, true) - println(s"Typed `$nme` as: $ty_sch") - println(s" where: ${ty_sch.showBounds}") - val exp = getType(ty_sch) - declared.get(nme) match { - case S(sign) => - subsume(ty_sch, sign)(ctx, raise, TypeProvenance(d.toLoc, "def definition")) - // Note: keeping the less precise declared type signature here (no ctx update) - case N => - ctx += nme.name -> VarSymbol(ty_sch, nme) - } - res ++= formatBinding(d.nme.name, ty_sch) - results append S(d.nme.name) -> htmlize(getType(ty_sch).show) - case d @ Def(isrec, nme, R(PolyType(tps, rhs)), _) => - declared.get(nme) match { - case S(sign) => - import Message.MessageContext - typer.err(msg"illegal redeclaration of ${nme.name}" -> d.toLoc - :: msg"already defined here:" -> - declared.keysIterator.find(_.name === nme.name).flatMap(_.toLoc) - :: Nil) - case N => () - } - implicit val tp: TP = NoProv // TODO - val ty_sch = ctx.poly { implicit ctx => - typeType(rhs)(ctx, raise, - vars = tps.map(tp => tp.fold(_.name, _ => ??? // FIXME - ) -> freshVar(noProv/*FIXME*/, N)(1)).toMap) - } - ctx += nme.name -> VarSymbol(ty_sch, nme) - declared += nme -> ty_sch - results append S(d.nme.name) -> htmlize(getType(ty_sch).show) - case s: DesugaredStatement => - implicit val vars: Map[Str, SimpleType] = Map.empty - implicit val gl: typer.GenLambdas = true - typer.typeStatement(s, allowPure = true) match { - case R(binds) => - binds.foreach { case (nme, pty) => - ctx += nme -> VarSymbol(pty, Var(nme)) - res ++= formatBinding(nme, pty) - results append S(nme) -> htmlize(getType(pty).show) - } - case L(pty) => - val exp = getType(pty) - if (exp =/= TypeName("unit")) { - val nme = "res" - ctx += nme -> VarSymbol(pty, Var(nme)) - res ++= formatBinding(nme, pty) - results append N -> htmlize(getType(pty).show) - } + val tryRes = try { + import fastparse._ + import fastparse.Parsed.{Success, Failure} + import mlscript.{NewParser, ErrorReport, Origin} + val lines = str.splitSane('\n').toIndexedSeq + val processedBlock = lines.mkString + val fph = new mlscript.FastParseHelpers(str, lines) + val origin = Origin("", 1, fph) + val lexer = new NewLexer(origin, throw _, dbg = false) + val tokens = lexer.bracketedTokens + val parser = new NewParser(origin, tokens, newDefs = true, throw _, dbg = false, N) { + def doPrintDbg(msg: => Str): Unit = if (dbg) println(msg) + } + parser.parseAll(parser.typingUnit) match { + case tu => + val pgrm = Pgrm(tu.entities) + println(s"Parsed: $pgrm") + + val typer = new mlscript.Typer( + dbg = false, + verbose = false, + explainErrors = false, + newDefs = true, + ) + + import typer._ + + implicit val raise: Raise = throw _ + implicit var ctx: Ctx = Ctx.init + implicit val extrCtx: Opt[typer.ExtrCtx] = N + + val vars: Map[Str, typer.SimpleType] = Map.empty + val tpd = typer.typeTypingUnit(tu, N)(ctx.nest, raise, vars) + + object SimplifyPipeline extends typer.SimplifyPipeline { + def debugOutput(msg: => Str): Unit = + // if (mode.dbgSimplif) output(msg) + println(msg) } - } catch { - case err: ErrorReport => - if (stopAtFirstError) decls = Nil - val culprit = d match { - case Def(isrec, nme, rhs, isByname) => "def " + nme - case _: DesugaredStatement => "statement" + val sim = SimplifyPipeline(tpd, S(true))(ctx) + + val exp = typer.expandType(sim)(ctx) + + val expStr = exp.showIn(ShowCtx.mk(exp :: Nil, newDefs = true), 0).stripSuffix("\n") + .replaceAll(" ", "  ") + .replaceAll("\n", "
") + + // TODO format HTML better + val typingStr = """
+ | + | + | + |""".stripMargin + + s""" + | ${s""} + | + |""".stripMargin + + val backend = new JSWebBackend() + val (lines, resNames) = backend(pgrm, true) + val code = lines.mkString("\n") + + // TODO: add a toggle button to show js code + // val jsStr = ("\n\n=====================JavaScript Code=====================\n" + code) + // .stripSuffix("\n") + // .replaceAll(" ", "  ") + // .replaceAll("\n", "
") + + val exe = executeCode(code) match { + case Left(err) => err + case Right(lines) => generateResultTable(resNames.zip(lines)) } - res ++= report(err) - errorOccurred = true + + val resStr = (""" + | + | + |""".stripMargin + exe + "

Typing Results:

${expStr}

Execution Results:

") + + typingStr + resStr } + } catch { + // case err: ErrorReport => + case err: Diagnostic => + report(err) + case err: Throwable => + s""" + + Unexpected error: ${err}${ + err.printStackTrace + // err.getStackTrace().map(s"$htmlLineBreak$htmlWhiteSpace$htmlWhiteSpace at " + _).mkString + "" + }""" } - results.toList -> (if (errorOccurred) S(res.toString) else N) + target.innerHTML = tryRes + } + + // Execute the generated code. + // We extract this function because there is some boilerplate code. + // It returns a tuple of three items: + // 1. results of definitions; + // 2. results of expressions; + // 3. error message (if has). + @SuppressWarnings(Array("org.wartremover.warts.AsInstanceOf")) + private def executeCode(code: Str): Either[Str, Ls[Str]] = { + try { + R(js.eval(code).asInstanceOf[js.Array[Str]].toList) + } catch { + case e: Throwable => + val errorBuilder = new StringBuilder() + errorBuilder ++= "Runtime error occurred:" + errorBuilder ++= htmlLineBreak + e.getMessage + errorBuilder ++= htmlLineBreak + errorBuilder ++= htmlLineBreak + L(errorBuilder.toString) + } + } + + private def generateResultTable(res: Ls[(Str, Str)]): Str = { + val htmlBuilder = new StringBuilder + htmlBuilder ++= """ + | Name + | Value + | + |""".stripMargin + + res.foreach(value => { + htmlBuilder ++= s""" + | ${value._1.replaceAll(" ", "  ").replaceAll("\n", "
")} + | ${s"${value._2.replaceAll(" ", "  ").replaceAll("\n", "
")}"} + | + |""".stripMargin + }) + + htmlBuilder.toString } - private def underline(fragment: Str): Str = - s"$fragment" + private val htmlLineBreak = "
" + private val htmlWhiteSpace = " " } + diff --git a/local_testing.html b/local_testing.html index 82b676a049..4523505c01 100644 --- a/local_testing.html +++ b/local_testing.html @@ -9,67 +9,95 @@

MLscript demonstration

-

-

The code is available on github.

+ +
+ +

Tip: additional definitions from the paper – copy and paste to try them out:

+
+class Mul[E](lhs: E, rhs: E)
+mixin EvalMul {
+  fun eval(override Mul(l, r)) = this.eval(l) * this.eval(r)
+}
+mixin EvalNegNeg {
+  fun eval(override Neg(Neg(d))) = this.eval(d)
+}
+
+module TestLang2 extends EvalAddLit, EvalNeg, EvalMul, EvalNegNeg
+
+let res2 = TestLang2.eval(add2negadd11) +
+
+
+class Some[A](value: A)
+
+mixin Foo { fun foo: Int = 1 }
+mixin Bar { fun foo = Some(super.foo) }
+module ClsFoo extends Foo, Bar, Bar
+
+ClsFoo.foo
+
+
+
+class Some[A](value: A)
+module None
+
+mixin ComparePoint {
+  fun compare(lhs, rhs) = (lhs.x == rhs.x) && (lhs.y == rhs.y)
+}
+mixin CompareColored {
+  fun compare(lhs, rhs) =
+    super.compare(lhs, rhs) && eq(lhs.color)(rhs.color)
+}
+mixin CompareNested {
+  fun compare(lhs, rhs) =
+    super.compare(lhs, rhs) &&
+      if lhs.parent is Some(p)
+        then rhs.parent is Some(q) and this.compare(p, q)
+        else rhs.parent is None
+}
+
+class MyPoint(x: Int, y: Int, color: Str, parent: Some[MyPoint] | None)
+
+module CompareMyPoint extends ComparePoint, CompareColored, CompareNested
+
+let Red = "red"
+let p0 = MyPoint(0, 0, Red, None)
+let p1 = MyPoint(0, 1, Red, None)
+let p2 = MyPoint(0, 1, Red, None)
+let p3 = MyPoint(0, 1, Red, Some(p1))
+let p4 = MyPoint(0, 1, Red, Some(p2))
+let p5 = MyPoint(0, 1, Red, Some(p3))
+
+CompareMyPoint.compare(p0, p1)
+CompareMyPoint.compare(p1, p2)
+CompareMyPoint.compare(p3, p4)
+CompareMyPoint.compare(p3, p5)
+
+ + diff --git a/project/build.properties b/project/build.properties index 22af2628c4..e8a1e246e8 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.7.1 +sbt.version=1.9.7 diff --git a/project/plugins.sbt b/project/plugins.sbt index 66dc10c855..3d01a25463 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,3 +1,3 @@ -addSbtPlugin("org.wartremover" % "sbt-wartremover" % "3.0.6") +addSbtPlugin("org.wartremover" % "sbt-wartremover" % "3.1.5") addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.11.0") addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.2.0") diff --git a/shared/src/main/scala/mlscript/ConstraintSolver.scala b/shared/src/main/scala/mlscript/ConstraintSolver.scala index 79b17048ae..9e223b3e09 100644 --- a/shared/src/main/scala/mlscript/ConstraintSolver.scala +++ b/shared/src/main/scala/mlscript/ConstraintSolver.scala @@ -24,6 +24,166 @@ class ConstraintSolver extends NormalForms { self: Typer => protected var currentConstrainingRun = 0 + + private def noSuchMember(info: DelayedTypeInfo, fld: Var): Diagnostic = + ErrorReport( + msg"${info.decl.kind.str.capitalize} `${info.decl.name}` does not contain member `${fld.name}`" -> fld.toLoc :: Nil, newDefs) + + def lookupMember(clsNme: Str, rfnt: Var => Opt[FieldType], fld: Var) + (implicit ctx: Ctx, raise: Raise) + : Either[Diagnostic, NuMember] + = { + val info = ctx.tyDefs2.getOrElse(clsNme, ???/*TODO*/) + + if (info.isComputing) { + + ??? // TODO support? + + } else info.complete() match { + + case cls: TypedNuCls => + cls.members.get(fld.name) match { + case S(m) => R(m) + case N => L(noSuchMember(info, fld)) + } + + case _ => ??? // TODO + + } + + } + + + /** Note: `mkType` is just for reporting errors. */ + def lookupField(mkType: () => ST, clsNme: Opt[Str], rfnt: Var => Opt[FieldType], + tags: SortedSet[AbstractTag], _fld: Var) + (implicit ctx: Ctx, raise: Raise) + : FieldType + = trace(s"Looking up field ${_fld.name} in $clsNme & ${tags} & {...}") { + + // * Field selections with field names starting with `#` are a typer hack to access private members. + val (fld, allowPrivateAccess) = + if (_fld.name.startsWith("#")) (Var(_fld.name.tail).withLocOf(_fld), true) + else (_fld, false) + + val fromRft = rfnt(fld) + + var foundRec: Opt[Diagnostic] = N + + def getFieldType(info: DelayedTypeInfo): Opt[FieldType] = { + + // * The raw type of this member, with original references to the class' type variables/type parameters + val raw = (if (info.isComputed) N else info.typedFields.get(fld)) match { + + case S(fty) => + if (info.privateParams.contains(fld) && !allowPrivateAccess) + err(msg"Parameter '${fld.name}' cannot tbe accessed as a field" -> fld.toLoc :: Nil) + S(fty) + + case N if info.isComputing => + + if (info.allFields.contains(fld)) // TODO don't report this if the field can be found somewhere else! + foundRec = S(ErrorReport( + msg"Indirectly-recursive member should have type annotation" -> fld.toLoc :: Nil, newDefs)) + + N + + case N => + + def handle(virtualMembers: Map[Str, NuMember]): Opt[FieldType] = + virtualMembers.get(fld.name) match { + case S(d: TypedNuFun) => + S(d.typeSignature.toUpper(provTODO)) + case S(p: NuParam) => + if (!allowPrivateAccess && !p.isPublic) + err(msg"Parameter '${p.nme.name}' cannot tbe accessed as a field" -> fld.toLoc :: + msg"Either make the parameter a `val` or access it through destructuring" -> p.nme.toLoc :: + Nil) + S(p.ty) + case S(m) => + S(err(msg"Access to ${m.kind.str} member not yet supported", fld.toLoc).toUpper(noProv)) + case N => N + } + + info.complete() match { + case cls: TypedNuCls => handle(cls.virtualMembers) + case trt: TypedNuTrt => handle(trt.virtualMembers) + case mxn: TypedNuMxn => handle(mxn.virtualMembers) + case TypedNuDummy(d) => N + case _ => ??? // TODO + } + + } + + println(s"Lookup ${info.decl.name}.${fld.name} : $raw where ${raw.fold("")(_.ub.showBounds)}") + + val freshenedRaw = raw.map { raw => + + implicit val freshened: MutMap[TV, ST] = MutMap.empty + implicit val shadows: Shadows = Shadows.empty + + info.tparams.foreach { case (tn, _tv, vi) => + val targ = rfnt(Var(info.decl.name + "#" + tn.name)) match { + // * TODO to avoid infinite recursion due to ever-expanding type args, + // * we should set the shadows of the targ to be the same as that of the parameter it replaces... + case S(fty) if vi === S(VarianceInfo.co) => fty.ub + case S(fty) if vi === S(VarianceInfo.contra) => fty.lb.getOrElse(BotType) + case S(fty) => + TypeBounds.mk( + fty.lb.getOrElse(BotType), + fty.ub, + ) + case N => + TypeBounds( + // _tv.lowerBounds.foldLeft(BotType: ST)(_ | _), + // _tv.upperBounds.foldLeft(TopType: ST)(_ & _), + _tv.lowerBounds.foldLeft( + Extruded(true, SkolemTag(_tv)(provTODO))(provTODO, Nil): ST + // ^ TODO provide extrusion reason? + )(_ | _), + _tv.upperBounds.foldLeft( + Extruded(false, SkolemTag(_tv)(provTODO))(provTODO, Nil): ST + // ^ TODO provide extrusion reason? + )(_ & _), + )(_tv.prov) + } + freshened += _tv -> targ + } + + raw.freshenAbove(info.level, rigidify = false) + } + + println(s"Fresh[${info.level}] ${info.decl.name}.${fld.name} : $freshenedRaw where ${freshenedRaw.map(_.ub.showBounds)}") + + freshenedRaw + } + + val fromCls = clsNme.flatMap(clsNme => getFieldType(ctx.tyDefs2(clsNme))) + + val fromTrts = tags.toList.collect { + case TraitTag(nme, iht) => + getFieldType(ctx.tyDefs2(nme.name)) + }.flatten + + val fields = fromRft.toList ::: fromCls.toList ::: fromTrts + + println(s" & ${fromRft} (from refinement)") + + fields match { + case x :: xs => + xs.foldRight(x)(_ && _) + case Nil => + foundRec match { + case S(d) => err(d).toUpper(noProv) + case N => + err(msg"Type `${mkType().expPos}` does not contain member `${fld.name}`" -> + fld.toLoc :: Nil).toUpper(noProv) + } + } + + }() + + // * Each type has a shadow which identifies all variables created from copying // * variables that existed at the start of constraining. // * The intent is to make the total number of shadows in a given constraint @@ -261,6 +421,7 @@ class ConstraintSolver extends NormalForms { self: Typer => annoyingImpl(ls, done_ls, rs, done_rs) } + // TODO improve by moving things to the right side *before* branching out in the search! def annoyingImpl(ls: Ls[SimpleType], done_ls: LhsNf, rs: Ls[SimpleType], done_rs: RhsNf) (implicit cctx: ConCtx, prevCctxs: Ls[ConCtx], ctx: Ctx, shadows: Shadows, dbgHelp: Str = "Case") : Unit = @@ -347,7 +508,8 @@ class ConstraintSolver extends NormalForms { self: Typer => case (LhsRefined(_, ts, _, trs), RhsBases(pts, _, _)) if ts.exists(pts.contains) => () case (LhsRefined(bo, ts, r, trs), _) if trs.nonEmpty => - annoying(trs.valuesIterator.map(_.expand).toList, LhsRefined(bo, ts, r, SortedMap.empty), Nil, done_rs) + annoying(trs.valuesIterator.map { tr => tr.expand }.toList, + LhsRefined(bo, ts, r, SortedMap.empty), Nil, done_rs) case (_, RhsBases(pts, bf, trs)) if trs.nonEmpty => annoying(Nil, done_ls, trs.valuesIterator.map(_.expand).toList, RhsBases(pts, bf, SortedMap.empty)) @@ -367,15 +529,64 @@ class ConstraintSolver extends NormalForms { self: Typer => rec(f0, f1, true) case (LhsRefined(S(f: FunctionType), ts, r, trs), RhsBases(pts, _, _)) => annoying(Nil, LhsRefined(N, ts, r, trs), Nil, done_rs) - case (LhsRefined(S(pt: ClassTag), ts, r, trs), RhsBases(pts, bf, trs2)) => - if (pts.contains(pt) || pts.exists(p => pt.parentsST.contains(p.id))) + + // * Note: We could avoid the need for this rule by adding `Eql` to *all* class tag parent sets, + // * but I chose not to for performance reasons (better keep parent sets small). + case (LhsRefined(S(ct: ClassTag), ts, r, trs0), + RhsBases(ots, _, trs)) if EqlTag in ots => + println(s"OK ~ magic Eql ~") + + // * These deal with the implicit Eql type member in primitive types. + // * (Originally I added this to all such types, + // * but it requires not expanding primitive type refs, + // * which causes regressions in simplification + // * because we don't yet simplify unexpanded type refs...) + case (LhsRefined(S(ct @ ClassTag(lit: Lit, _)), ts, r, trs0), + RhsBases(ots, S(R(RhsField(Var("Eql#A"), fldTy))), trs)) => + lit match { + case _: IntLit | _: DecLit => rec(fldTy.lb.getOrElse(TopType), DecType, false) + case _: StrLit => rec(fldTy.lb.getOrElse(TopType), StrType, false) + case _: UnitLit => reportError() + } + + // * This deals with the implicit Eql type member for user-defined classes. + case (LhsRefined(S(ClassTag(Var(nme), _)), ts, r, trs0), + RhsBases(ots, S(R(RhsField(fldNme, fldTy))), trs)) + if ctx.tyDefs2.contains(nme) => if (newDefs && nme =/= "Eql" && fldNme.name === "Eql#A") { + val info = ctx.tyDefs2(nme) + if (info.typedParams.isEmpty && !primitiveTypes.contains(nme)) + // TODO shoudl actually reject all non-data classes... + err(msg"${info.decl.kind.str.capitalize} '${info.decl.name + }' does not support equality comparison because it does not have a parameter list", prov.loco) + info.typedParams + .getOrElse(Nil) // FIXME?... prim type + .foreach { p => + val fty = lookupField(() => done_ls.toType(sort = true), S(nme), r.fields.toMap.get, ts, p._1) + rec(fldTy.lb.getOrElse(die), RecordType(p._1 -> TypeRef(TypeName("Eql"), + fty.ub // FIXME check mutable? + :: Nil + )(provTODO).toUpper(provTODO) :: Nil)(provTODO), false) + } + } else { + val fty = lookupField(() => done_ls.toType(sort = true), S(nme), r.fields.toMap.get, ts, fldNme) + rec(fty.ub, fldTy.ub, false) + recLb(fldTy, fty) + } + case (l @ LhsRefined(S(pt: ClassTag), ts, r, trs), RhsBases(pts, bf, trs2)) => + println(s"class checking $pt $pts") + if (pts.exists(p => (p.id === pt.id) || l.allTags.contains(p.id))) println(s"OK $pt <: ${pts.mkString(" | ")}") // else f.fold(reportError())(f => annoying(Nil, done_ls, Nil, f)) else annoying(Nil, LhsRefined(N, ts, r, trs), Nil, RhsBases(Nil, bf, trs2)) case (lr @ LhsRefined(bo, ts, r, _), rf @ RhsField(n, t2)) => // Reuse the case implemented below: (this shortcut adds a few more annoying calls in stats) annoying(Nil, lr, Nil, RhsBases(Nil, S(R(rf)), SortedMap.empty)) + case (LhsRefined(N, ts, r, _), RhsBases(ots, S(R(RhsField(fldNme, fldTy))), trs)) if newDefs => + val fty = lookupField(() => done_ls.toType(sort = true), N, r.fields.toMap.get, ts, fldNme) + rec(fty.ub, fldTy.ub, false) + recLb(fldTy, fty) case (LhsRefined(bo, ts, r, _), RhsBases(ots, S(R(RhsField(n, t2))), trs)) => + // TODO simplify - merge with above? r.fields.find(_._1 === n) match { case S(nt1) => recLb(t2, nt1._2) @@ -388,7 +599,15 @@ class ConstraintSolver extends NormalForms { self: Typer => case _ => reportError() } } - case (LhsRefined(N, ts, r, _), RhsBases(pts, N | S(L(_: FunctionType | _: ArrayBase)), _)) => + case (LhsRefined(N, ts, r, trs), RhsBases(pts, N, trs2)) => + println(s"Tag checking ${ts} ${pts}") + if (pts.exists(p => ts.iterator.flatMap { + case TraitTag(n, h) => n :: h.toList.map(n => Var(n.name)) + case _ => Nil + }.contains(p.id))) + println(s"OK $ts <: $pts") + else reportError() + case (LhsRefined(N, ts, r, _), RhsBases(pts, S(L(_: FunctionType | _: ArrayBase)), _)) => reportError() case (LhsRefined(S(b: TupleType), ts, r, _), RhsBases(pts, S(L(ty: TupleType)), _)) if b.fields.size === ty.fields.size => @@ -567,6 +786,17 @@ class ConstraintSolver extends NormalForms { self: Typer => case (_: TypeTag, _: TypeTag) if lhs === rhs => () case (NegType(lhs), NegType(rhs)) => rec(rhs, lhs, true) + case (ClassTag(Var(nme), _), rt: RecordType) if newDefs && nme.isCapitalized => + val lti = ctx.tyDefs2(nme) + rt.fields.foreach { + case (fldNme @ Var("Eql#A"), fldTy) => + goToWork(lhs, RecordType(fldNme -> fldTy :: Nil)(noProv)) + case (fldNme, fldTy) => + val fty = lookupField(() => lhs, S(nme), _ => N, SortedSet.empty, fldNme) + rec(fty.ub, fldTy.ub, false) + recLb(fldTy, fty) + } + // * Note: at this point, it could be that a polymorphic type could be distribbed // * out of `r1`, but this would likely not result in something useful, since the // * LHS is a normal non-polymorphic function type... @@ -677,21 +907,51 @@ class ConstraintSolver extends NormalForms { self: Typer => rec(tup.toRecord, rhs, true) // Q: really support this? means we'd put names into tuple reprs at runtime case (err @ ClassTag(ErrTypeId, _), RecordType(fs1)) => fs1.foreach(f => rec(err, f._2.ub, false)) + case (_, RecordType(fs1)) => + goToWork(lhs, rhs) case (RecordType(fs1), err @ ClassTag(ErrTypeId, _)) => fs1.foreach(f => rec(f._2.ub, err, false)) - case (tr1: TypeRef, tr2: TypeRef) if tr1.defn.name =/= "Array" => + case (tr1: TypeRef, tr2: TypeRef) + if tr1.defn.name =/= "Array" && tr2.defn.name =/= "Eql" => if (tr1.defn === tr2.defn) { assert(tr1.targs.sizeCompare(tr2.targs) === 0) - val td = ctx.tyDefs(tr1.defn.name) - val tvv = td.getVariancesOrDefault - td.tparamsargs.unzip._2.lazyZip(tr1.targs).lazyZip(tr2.targs).foreach { (tv, targ1, targ2) => - val v = tvv(tv) - if (!v.isContravariant) rec(targ1, targ2, false) - if (!v.isCovariant) rec(targ2, targ1, false) + ctx.tyDefs.get(tr1.defn.name) match { + case S(td) => + val tvv = td.getVariancesOrDefault + td.tparamsargs.unzip._2.lazyZip(tr1.targs).lazyZip(tr2.targs).foreach { (tv, targ1, targ2) => + val v = tvv(tv) + if (!v.isContravariant) rec(targ1, targ2, false) + if (!v.isCovariant) rec(targ2, targ1, false) + } + case N => + /* + ctx.tyDefs2(tr1.defn.name).complete() match { + case cls: TypedNuCls => + cls.tparams.map(_._2).lazyZip(tr1.targs).lazyZip(tr2.targs).foreach { + (tv, targ1, targ2) => + val v = cls.varianceOf(tv) + if (!v.isContravariant) rec(targ1, targ2, false) + if (!v.isCovariant) rec(targ2, targ1, false) + } + // case _ => ??? + } + */ + ctx.tyDefs2.get(tr1.defn.name) match { + case S(lti) => + lti.tparams.map(_._2).lazyZip(tr1.targs).lazyZip(tr2.targs).foreach { + (tv, targ1, targ2) => + val v = lti.varianceOf(tv) + if (!v.isContravariant) rec(targ1, targ2, false) + if (!v.isCovariant) rec(targ2, targ1, false) + } + case N => + ??? // TODO + } } } else { - (tr1.mkTag, tr2.mkTag) match { + if (tr1.mayHaveTransitiveSelfType) rec(tr1.expand, tr2.expand, true) + else (tr1.mkClsTag, tr2.mkClsTag) match { case (S(tag1), S(tag2)) if !(tag1 <:< tag2) => reportError() case _ => @@ -699,7 +959,13 @@ class ConstraintSolver extends NormalForms { self: Typer => } } case (tr: TypeRef, _) => rec(tr.expand, rhs, true) - case (_, tr: TypeRef) => rec(lhs, tr.expand, true) + case (err @ ClassTag(ErrTypeId, _), tr: TypeRef) => + // rec(tr.copy(targs = tr.targs.map(_ => err))(noProv), tr, true) + // * ^ Nicely propagates more errors to the result, + // * but can incur vast amounts of unnecessary constraining in the context of recursive types! + () + case (_, tr: TypeRef) => + rec(lhs, tr.expand, true) case (ClassTag(ErrTypeId, _), _) => () case (_, ClassTag(ErrTypeId, _)) => () @@ -729,7 +995,8 @@ class ConstraintSolver extends NormalForms { self: Typer => // * Simple case when the parameter type vars don't need to be split case (AliasOf(PolymorphicType(plvl, AliasOf(FunctionType(param, bod)))), _) if distributeForalls - && param.level <= plvl => + && param.level <= plvl + && bod.level > plvl => val newLhs = FunctionType(param, PolymorphicType(plvl, bod))(rhs.prov) println(s"DISTRIB-L ~> $newLhs") rec(newLhs, rhs, true) @@ -783,6 +1050,8 @@ class ConstraintSolver extends NormalForms { self: Typer => rec(ov.approximatePos, rhs, true) case (_: NegType | _: Without, _) | (_, _: NegType | _: Without) => goToWork(lhs, rhs) + case (_: ClassTag | _: TraitTag, _: TraitTag) => + goToWork(lhs, rhs) case _ => reportError() }} }}() @@ -927,7 +1196,7 @@ class ConstraintSolver extends NormalForms { self: Typer => case _ => Nil } ) - return raise(ErrorReport(msgs ::: mk_constraintProvenanceHints)) + return raise(ErrorReport(msgs ::: mk_constraintProvenanceHints, newDefs)) case (_: TV | _: ProxyType, _) => doesntMatch(rhs) case (RecordType(fs0), RecordType(fs1)) => (fs1.map(_._1).toSet -- fs0.map(_._1).toSet) @@ -998,7 +1267,7 @@ class ConstraintSolver extends NormalForms { self: Typer => detailedContext, ).flatten - raise(ErrorReport(msgs)) + raise(ErrorReport(msgs, newDefs)) } rec(lhs, rhs, true)(raise, Nil -> Nil, Nil, outerCtx, shadows) @@ -1144,7 +1413,10 @@ class ConstraintSolver extends NormalForms { self: Typer => err(msg -> loco :: Nil) } def err(msgs: List[Message -> Opt[Loc]])(implicit raise: Raise): SimpleType = { - raise(ErrorReport(msgs)) + err(ErrorReport(msgs, newDefs)) + } + def err(diag: Diagnostic)(implicit raise: Raise): SimpleType = { + raise(diag) errType } def errType: SimpleType = ClassTag(ErrTypeId, Set.empty)(noProv) @@ -1153,7 +1425,7 @@ class ConstraintSolver extends NormalForms { self: Typer => warn(msg -> loco :: Nil) def warn(msgs: List[Message -> Opt[Loc]])(implicit raise: Raise): Unit = - raise(WarningReport(msgs)) + raise(WarningReport(msgs, newDefs)) @@ -1246,8 +1518,9 @@ class ConstraintSolver extends NormalForms { self: Typer => case Some(tv) => tv case None if rigidify && tv.level <= below => // * Rigid type variables (ie, skolems) are encoded as SkolemTag-s - val rv = SkolemTag(freshVar(noProv, N, tv.nameHint.orElse(S("_"))))(tv.prov) - if (tv.lowerBounds.nonEmpty || tv.upperBounds.nonEmpty) { + val rv = SkolemTag(freshVar(noProv, S(tv), tv.nameHint/* .orElse(S("_"))*/))(tv.prov) + println(s"New skolem: $tv ~> $rv") + if (tv.lowerBounds.nonEmpty || tv.upperBounds.nonEmpty) { // TODO just add bounds to skolems! should lead to simpler constraints // The bounds of `tv` may be recursive (refer to `tv` itself), // so here we create a fresh variabe that will be able to tie the presumed recursive knot // (if there is no recursion, it will just be a useless type variable) @@ -1260,16 +1533,19 @@ class ConstraintSolver extends NormalForms { self: Typer => // where rv is the rigidified variables. // Now, since there may be recursive bounds, we do the same // but through the indirection of a type variable tv2: - tv2.lowerBounds ::= tv.lowerBounds.map(freshen).foldLeft(rv: ST)(_ & _) - tv2.upperBounds ::= tv.upperBounds.map(freshen).foldLeft(rv: ST)(_ | _) + tv2.lowerBounds ::= tv.upperBounds.map(freshen).foldLeft(rv: ST)(_ & _) + println(s"$tv2 :> ${tv2.lowerBounds}") + tv2.upperBounds ::= tv.lowerBounds.map(freshen).foldLeft(rv: ST)(_ | _) + println(s"$tv2 <: ${tv2.upperBounds}") tv2 } else { + // NOTE: tv.level may be different from lvl; is that OK? freshened += tv -> rv rv } case None => val v = freshVar(tv.prov, S(tv), tv.nameHint)(if (tv.level > below) tv.level else { - assert(lvl <= below, "this condition should not be true for the result to be correct") + assert(lvl <= below, "this condition should be false for the result to be correct") lvl }) freshened += tv -> v @@ -1311,7 +1587,7 @@ class ConstraintSolver extends NormalForms { self: Typer => case tr @ TypeRef(d, ts) => TypeRef(d, ts.map(freshen(_)))(tr.prov) case pt @ PolymorphicType(polyLvl, bod) if pt.level <= above => pt // is this really useful? case pt @ PolymorphicType(polyLvl, bod) => - if (lvl > polyLvl) freshen(pt.raiseLevelTo(lvl)) + if (lvl > polyLvl) freshen(pt.raiseLevelToImpl(lvl, leaveAlone)) else PolymorphicType(polyLvl, freshenImpl(bod, below = below min polyLvl)) case ct @ ConstrainedType(cs, bod) => val cs2 = cs.map(lu => freshen(lu._1) -> freshen(lu._2)) diff --git a/shared/src/main/scala/mlscript/Diagnostic.scala b/shared/src/main/scala/mlscript/Diagnostic.scala index 1a046f3e06..b552dc7f31 100644 --- a/shared/src/main/scala/mlscript/Diagnostic.scala +++ b/shared/src/main/scala/mlscript/Diagnostic.scala @@ -29,16 +29,16 @@ final case class ErrorReport(mainMsg: Str, allMsgs: Ls[Message -> Opt[Loc]], sou val kind: Kind = Error } object ErrorReport { - def apply(msgs: Ls[Message -> Opt[Loc]], source: Source = Typing): ErrorReport = - ErrorReport(msgs.head._1.show, msgs, source) + def apply(msgs: Ls[Message -> Opt[Loc]], newDefs: Bool, source: Source = Typing): ErrorReport = + ErrorReport(msgs.head._1.show(newDefs), msgs, source) } final case class WarningReport(mainMsg: Str, allMsgs: Ls[Message -> Opt[Loc]], source: Source) extends Diagnostic(mainMsg) { val kind: Kind = Warning } object WarningReport { - def apply(msgs: Ls[Message -> Opt[Loc]], source: Source = Typing): WarningReport = - WarningReport(msgs.head._1.show, msgs, source) + def apply(msgs: Ls[Message -> Opt[Loc]], newDefs: Bool, source: Source = Typing): WarningReport = + WarningReport(msgs.head._1.show(newDefs), msgs, source) } @@ -60,6 +60,10 @@ final case class Loc(spanStart: Int, spanEnd: Int, origin: Origin) { def right: Loc = copy(spanStart = spanEnd) def left: Loc = copy(spanEnd = spanStart) } +object Loc { + def apply(xs: IterableOnce[Located]): Opt[Loc] = + xs.iterator.foldLeft(none[Loc])((acc, l) => acc.fold(l.toLoc)(_ ++ l.toLoc |> some)) +} final case class Origin(fileName: Str, startLineNum: Int, fph: FastParseHelpers) { override def toString = s"$fileName:+$startLineNum" diff --git a/shared/src/main/scala/mlscript/JSBackend.scala b/shared/src/main/scala/mlscript/JSBackend.scala index d01cd2e582..0351fae58f 100644 --- a/shared/src/main/scala/mlscript/JSBackend.scala +++ b/shared/src/main/scala/mlscript/JSBackend.scala @@ -1,14 +1,25 @@ package mlscript import mlscript.utils._, shorthands._, algorithms._ -import mlscript.codegen.Helpers._ import mlscript.codegen._ -import scala.collection.mutable.ListBuffer +import scala.collection.mutable.{ListBuffer, HashMap, HashSet} import mlscript.{JSField, JSLit} import scala.collection.mutable.{Set => MutSet} import scala.util.control.NonFatal +import scala.util.chaining._ -class JSBackend(allowUnresolvedSymbols: Boolean) { +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. */ @@ -19,8 +30,6 @@ class JSBackend(allowUnresolvedSymbols: Boolean) { */ protected val polyfill = Polyfill() - protected val visitedSymbols = MutSet[ValueSymbol]() - /** * This function translates parameter destructions in `def` declarations. * @@ -48,12 +57,12 @@ class JSBackend(allowUnresolvedSymbols: Boolean) { // should returns ("{ x, y }", ["x", "y"]) case Rcd(fields) => JSObjectPattern(fields map { - case (Var(nme), Fld(_, _, Var(als))) => + case (Var(nme), Fld(_, Var(als))) => val runtimeName = scope.declareParameter(als) val fieldName = JSField.emitValidFieldName(nme) if (runtimeName === fieldName) fieldName -> N else fieldName -> S(JSNamePattern(runtimeName)) - case (Var(nme), Fld(_, _, subTrm)) => + case (Var(nme), Fld(_, subTrm)) => JSField.emitValidFieldName(nme) -> S(translatePattern(subTrm)) }) // This branch supports `def f (x: int) = x`. @@ -61,23 +70,40 @@ class JSBackend(allowUnresolvedSymbols: Boolean) { // Replace literals with wildcards. case _: Lit => JSWildcardPattern() case Bra(_, trm) => translatePattern(trm) - case Tup(fields) => JSArrayPattern(fields map { case (_, Fld(_, _, t)) => translatePattern(t) }) + case Tup(fields) => JSArrayPattern(fields map { case (_, Fld(_, t)) => translatePattern(t) }) // Others are not supported yet. case TyApp(base, _) => translatePattern(base) case Inst(bod) => translatePattern(bod) case _: Lam | _: App | _: Sel | _: Let | _: Blk | _: Bind | _: Test | _: With | _: CaseOf | _: Subs | _: Assign - | If(_, _) | New(_, _) | _: Splc | _: Forall | _: Where | _: AdtMatchWith => - throw CodeGenError(s"term ${inspect(t)} is not a valid pattern") + | If(_, _) | New(_, _) | NuNew(_) | _: Splc | _: Forall | _: Where | _: Super | _: Eqn | _: AdtMatchWith | _: Rft => + throw CodeGenError(s"term $t is not a valid pattern") } private def translateParams(t: Term)(implicit scope: Scope): Ls[JSPattern] = t match { - case Tup(params) => params map { case _ -> Fld(_, _, p) => translatePattern(p) } + case Tup(params) => params map { + case N -> Fld(_, p) => translatePattern(p) + case S(nme) -> Fld(_, p) => translatePattern(nme) + } case _ => throw CodeGenError(s"term $t is not a valid parameter list") } + // Set `requireActualCls` to true if we need the actual class rather than the constrcutor function (if the class has) + private def translateNuTypeSymbol(sym: NuTypeSymbol, requireActualCls: Bool)(implicit scope: Scope): JSExpr = { + val trm = sym.qualifier.fold[JSExpr](JSIdent(sym.name))(qualifier => { + scope.resolveQualifier(qualifier).visited = true + JSIdent(qualifier).member(sym.name) + }) + if (requireActualCls && !sym.isPlainJSClass) trm.member("class") else trm + } + protected def translateVar(name: Str, isCallee: Bool)(implicit scope: Scope): JSExpr = - scope.resolveValue(name) match { + translateVarImpl(name, isCallee).fold(throw _, identity) + + /** Try to retrieve a name from the scope, returning a Left value if the name is not found, + * a Right value if it is found, and throwing an exception in case of unrecoverable error. */ + protected def translateVarImpl(name: Str, isCallee: Bool)(implicit scope: Scope): Either[CodeGenError, JSExpr] = + Right(scope.resolveValue(name) match { case S(sym: BuiltinSymbol) => sym.accessed = true if (!polyfill.used(sym.feature)) @@ -91,27 +117,39 @@ class JSBackend(allowUnresolvedSymbols: Boolean) { throw new UnimplementedError(sym) case S(sym: ValueSymbol) => if (sym.isByvalueRec.getOrElse(false) && !sym.isLam) throw CodeGenError(s"unguarded recursive use of by-value binding $name") - visitedSymbols += sym + sym.visited = true val ident = JSIdent(sym.runtimeName) if (sym.isByvalueRec.isEmpty && !sym.isLam) ident() else ident + case S(sym: NuTypeSymbol) => + translateNuTypeSymbol(sym, isCallee) // `isCallee` is true in a `new` expression, which requires the actual class + case S(sym: NewClassMemberSymbol) => + if (sym.isByvalueRec.getOrElse(false) && !sym.isLam) throw CodeGenError(s"unguarded recursive use of by-value binding $name") + sym.qualifier.fold[JSExpr](throw CodeGenError(s"unqualified member symbol $sym"))(qualifier => { + sym.visited = true + scope.resolveQualifier(qualifier).visited = true + val ident = if (sym.isPrivate) JSIdent(s"${qualifier}.#${sym.name}") + else JSIdent(qualifier).member(sym.name) + if (sym.isByvalueRec.isEmpty && !sym.isLam) ident() else ident + }) case S(sym: ClassSymbol) => - if (isCallee) + if (isCallee || !oldDefs) JSNew(JSIdent(sym.runtimeName)) else JSArrowFn(JSNamePattern("x") :: Nil, L(JSNew(JSIdent(sym.runtimeName))(JSIdent("x")))) case S(sym: TraitSymbol) => - JSIdent(sym.lexicalName)("build") + if (oldDefs) JSIdent(sym.lexicalName)("build") + else return Left(CodeGenError(s"trait used in term position")) case N => scope.getType(name) match { case S(sym: TypeAliasSymbol) => - throw CodeGenError(s"type alias ${name} is not a valid expression") - case S(_) => throw new Exception("register mismatch in scope") + return Left(CodeGenError(s"type alias ${name} is not a valid expression")) + case S(_) => lastWords("register mismatch in scope") case N => if (allowUnresolvedSymbols) JSIdent(name) else - throw CodeGenError(s"unresolved symbol ${name}") + return Left(CodeGenError(s"unresolved symbol ${name}")) } - } + }) /** * Handle all possible cases of MLscript function applications. We extract @@ -119,21 +157,30 @@ class JSBackend(allowUnresolvedSymbols: Boolean) { */ protected def translateApp(term: App)(implicit scope: Scope): JSExpr = term match { // Binary expressions - case App(App(Var(op), Tup((N -> Fld(_, _, lhs)) :: Nil)), Tup((N -> Fld(_, _, rhs)) :: Nil)) + case App(App(Var(op), Tup((N -> Fld(_, lhs)) :: Nil)), Tup((N -> Fld(_, rhs)) :: Nil)) if JSBinary.operators contains op => JSBinary(op, translateTerm(lhs), translateTerm(rhs)) + // Binary expressions with new-definitions + case App(Var(op), Tup(N -> Fld(_, lhs) :: N -> Fld(_, rhs) :: Nil)) + if JSBinary.operators.contains(op) && !translateVarImpl(op, isCallee = true).isRight => + JSBinary(op, translateTerm(lhs), translateTerm(rhs)) // If-expressions - case App(App(App(Var("if"), Tup((_, Fld(_, _, tst)) :: Nil)), Tup((_, Fld(_, _, con)) :: Nil)), Tup((_, Fld(_, _, alt)) :: Nil)) => + case App(App(App(Var("if"), Tup((_, Fld(_, tst)) :: Nil)), Tup((_, Fld(_, con)) :: Nil)), Tup((_, Fld(_, alt)) :: Nil)) => JSTenary(translateTerm(tst), translateTerm(con), translateTerm(alt)) case App(App(App(Var("if"), tst), con), alt) => die // Function invocation case App(trm, Tup(args)) => val callee = trm match { - case Var(nme) => translateVar(nme, true) + case Var(nme) if oldDefs => scope.resolveValue(nme) match { + case S(sym: NuTypeSymbol) => + translateNuTypeSymbol(sym, false) // ClassName(params) + case _ => translateVar(nme, true) // Keep this case for the legacy test cases + } case _ => translateTerm(trm) } - callee(args map { case (_, Fld(_, _, arg)) => translateTerm(arg) }: _*) - case _ => throw CodeGenError(s"ill-formed application ${inspect(term)}") + callee(args map { case (_, Fld(_, arg)) => translateTerm(arg) }: _*) + case App(trm, splice) => ??? // TODO represents `trm(...splice)` + case _ => throw CodeGenError(s"ill-formed application $term") } /** @@ -142,13 +189,14 @@ class JSBackend(allowUnresolvedSymbols: Boolean) { protected def translateTerm(term: Term)(implicit scope: Scope): JSExpr = term match { case _ if term.desugaredTerm.isDefined => translateTerm(term.desugaredTerm.getOrElse(die)) case Var(name) => translateVar(name, false) + case Super() => JSIdent("super") case Lam(params, body) => val lamScope = scope.derive("Lam") val patterns = translateParams(params)(lamScope) JSArrowFn(patterns, lamScope.tempVars `with` translateTerm(body)(lamScope)) case t: App => translateApp(t) case Rcd(fields) => - JSRecord(fields map { case (key, Fld(_, _, value)) => + JSRecord(fields map { case (key, Fld(_, value)) => key.name -> translateTerm(value) }) case Sel(receiver, fieldName) => @@ -182,15 +230,28 @@ class JSBackend(allowUnresolvedSymbols: Boolean) { ) case Blk(stmts) => val blkScope = scope.derive("Blk") - val flattened = stmts.iterator.flatMap(_.desugared._2).toList + val flattened = stmts.iterator.flatMap { + case nt: NuTypeDef => nt :: Nil + case nf @ NuFunDef(_, Var(nme), symNme, _, _) => + val symb = symNme.map(_.name) + blkScope.declareStubValue(nme, symb)(true) + nf.desugared._2 + case other => other.desugared._2 + }.toList JSImmEvalFn( N, Nil, R(blkScope.tempVars `with` (flattened.iterator.zipWithIndex.map { case (t: Term, index) if index + 1 == flattened.length => translateTerm(t)(blkScope).`return` 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.isLam + 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. - case (_: Def | _: TypeDef | _: NuFunDef /* | _: NuTypeDef */, _) => + case (_: Def | _: TypeDef | _: NuFunDef | _: DataDefn | _: DatatypeDefn | _: LetS | _: Constructor, _) => throw CodeGenError("unsupported definitions in blocks") }.toList)), Nil @@ -199,8 +260,8 @@ class JSBackend(allowUnresolvedSymbols: Boolean) { case CaseOf(trm, Wildcard(default)) => JSCommaExpr(translateTerm(trm) :: translateTerm(default) :: Nil) // Pattern match with two branches -> tenary operator - case CaseOf(trm, Case(tst, csq, Wildcard(alt))) => - translateCase(translateTerm(trm), tst)(translateTerm(csq), translateTerm(alt)) + case CaseOf(trm, cs @ Case(tst, csq, Wildcard(alt))) => + translateCase(translateTerm(trm), tst)(scope)(translateTerm(csq), translateTerm(alt)) // Pattern match with more branches -> chain of ternary expressions with cache case CaseOf(trm, cases) => val arg = translateTerm(trm) @@ -225,13 +286,19 @@ class JSBackend(allowUnresolvedSymbols: Boolean) { case S(fnName) => fnName case N => polyfill.use("withConstruct", topLevelScope.declareRuntimeSymbol("withConstruct")) }), - translateTerm(trm) :: JSRecord(fields map { case (Var(name), Fld(_, _, value)) => + translateTerm(trm) :: JSRecord(fields map { case (Var(name), Fld(_, value)) => name -> translateTerm(value) }) :: Nil ) + // Only parenthesize binary operators + // Custom operators do not need special handling since they are desugared to plain methods + case Bra(false, trm) => trm match { + case App(Var(op), _) if JSBinary.operators.contains(op) => JSParenthesis(translateTerm(trm)) + case trm => translateTerm(trm) + } case Bra(_, trm) => translateTerm(trm) case Tup(terms) => - JSArray(terms map { case (_, Fld(_, _, term)) => translateTerm(term) }) + JSArray(terms map { case (_, Fld(_, term)) => translateTerm(term) }) case Subs(arr, idx) => JSMember(translateTerm(arr), translateTerm(idx)) case Assign(lhs, value) => @@ -239,35 +306,54 @@ class JSBackend(allowUnresolvedSymbols: Boolean) { 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: $lhs") } case Inst(bod) => translateTerm(bod) case iff: If => - throw CodeGenError(s"if expression has not been desugared") + throw CodeGenError(s"if expression was not desugared") + case NuNew(cls) => + // * The following logic handles the case when `new C(123)` needs to be translated to `new C.class(123)` + cls match { + case Var(className) => + translateVar(className, isCallee = true) match { + case n: JSNew => n + case t => JSNew(t) + } + case _ => throw CodeGenError(s"Unsupported `new` class term: $cls") + } + // * Would not be quite correct: + // JSNew(translateTerm(cls)) case New(N, TypingUnit(Nil)) => JSRecord(Nil) case New(S(TypeName(className) -> Tup(args)), TypingUnit(Nil)) => - val callee = translateVar(className, true) - callee(args.map { case (_, Fld(_, _, arg)) => translateTerm(arg) }: _*) + val callee = translateVar(className, true) match { + case n: JSNew => n + case t => JSNew(t) + } + callee(args.map { case (_, Fld(_, arg)) => translateTerm(arg) }: _*) case New(_, TypingUnit(_)) => throw CodeGenError("custom class body is not supported yet") case Forall(_, bod) => translateTerm(bod) - case _: Bind | _: Test | If(_, _) | TyApp(_, _) | _: Splc | _: Where | _: AdtMatchWith => - throw CodeGenError(s"cannot generate code for term ${inspect(term)}") + case TyApp(base, _) => translateTerm(base) + 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 $term") } private def translateCaseBranch(scrut: JSExpr, branch: CaseBranches)(implicit scope: Scope ): JSExpr = branch match { case Case(pat, body, rest) => - translateCase(scrut, pat)(translateTerm(body), translateCaseBranch(scrut, rest)) - case Wildcard(body) => translateTerm(body) + translateCase(scrut, pat)(scope)(translateTerm(body), translateCaseBranch(scrut, rest)) + case Wildcard(body) => + translateTerm(body) case NoCases => JSImmEvalFn(N, Nil, R(JSInvoke( JSNew(JSIdent("Error")), JSExpr("non-exhaustive case expression") :: Nil ).`throw` :: Nil), Nil) } - private def translateCase(scrut: JSExpr, pat: SimpleTerm) = { + private def translateCase(scrut: JSExpr, pat: SimpleTerm)(implicit scope: Scope) = { JSTenary( pat match { case Var("int") => @@ -279,14 +365,20 @@ class JSBackend(allowUnresolvedSymbols: Boolean) { case Var("string") => // JS is dumb so `instanceof String` won't actually work on "primitive" strings... JSBinary("===", scrut.member("constructor"), JSLit("String")) - case Var(name) => topLevelScope.getType(name) match { - case S(ClassSymbol(_, runtimeName, _, _, _)) => JSInstanceOf(scrut, JSIdent(runtimeName)) - case S(TraitSymbol(_, runtimeName, _, _, _)) => JSIdent(runtimeName)("is")(scrut) - case S(_: TypeAliasSymbol) => throw new CodeGenError(s"cannot match type alias $name") - case N => throw new CodeGenError(s"unknown match case: $name") + case Var(name) => scope.resolveValue(name) match { + case S(sym: NewClassSymbol) => + JSInstanceOf(scrut, translateNuTypeSymbol(sym, true)) // a is case ClassName(params) -> a instanceof ClassName.class + case S(sym: ModuleSymbol) => + JSInstanceOf(scrut, translateNuTypeSymbol(sym, true)) + case _ => topLevelScope.getType(name) match { + case S(ClassSymbol(_, runtimeName, _, _, _)) => JSInstanceOf(scrut, JSIdent(runtimeName)) + case S(TraitSymbol(_, runtimeName, _, _, _)) => JSIdent(runtimeName)("is")(scrut) + case S(_: TypeAliasSymbol) => throw new CodeGenError(s"cannot match type alias $name") + case _ => throw new CodeGenError(s"unknown match case: $name") + } } case lit: Lit => - JSBinary("===", scrut, JSLit(lit.idStr)) + JSBinary("===", scrut, translateTerm(lit)) }, _, _ @@ -311,7 +403,7 @@ class JSBackend(allowUnresolvedSymbols: Boolean) { case Lam(params, body) => val methodScope = scope.derive(s"Method $name") val methodParams = translateParams(params)(methodScope) - methodScope.declareValue("this", Some(false), false) + methodScope.declareValue("this", Some(false), false, N) instance(name) := JSFuncExpr( N, methodParams, @@ -320,7 +412,7 @@ class JSBackend(allowUnresolvedSymbols: Boolean) { // Define getters for pure expressions. case term => val getterScope = scope.derive(s"Getter $name") - getterScope.declareValue("this", Some(false), false) + getterScope.declareValue("this", Some(false), false, N) id("Object")("defineProperty")( instance, JSExpr(name), @@ -424,6 +516,378 @@ class JSBackend(allowUnresolvedSymbols: Boolean) { JSClassDecl(classSymbol.runtimeName, fields, base, members, traits) } + protected def translateQualifierDeclaration(qualifier: ValueSymbol): Ls[JSStmt] = + if (qualifier.visited) + JSConstDecl(qualifier.runtimeName, JSIdent("this")) :: Nil + else Nil + + protected def addNuTypeToGlobalThis(typeDef: NuTypeDef, moduleName: Str) = { + import JSCodeHelpers._ + typeDef match { + case NuTypeDef(Mxn, TypeName(nme), _, _, _, _, _, _, _, _) => + JSAssignExpr(id("globalThis").member(nme), JSArrowFn(param("base") :: Nil, L( + JSInvoke(id(moduleName).member(nme), id("base") :: Nil) + ))).stmt + case NuTypeDef(_, TypeName(nme), _, _, _, _, _, _, _, _) => + JSAssignExpr(id("globalThis").member(nme), id(moduleName).member(nme)).stmt + } + } + + protected def translateLocalNewType(typeDef: NuTypeDef)(implicit scope: Scope): JSConstDecl = { + // TODO: support traitSymbols + val (traitSymbols, classSymbols, mixinSymbols, moduleSymbols) = declareNewTypeDefs(typeDef :: Nil, N) + + val sym = classSymbols match { + case s :: _ => S(s) + case Nil => mixinSymbols match { + case s :: _ => S(s) + case Nil => moduleSymbols match { + case s :: _ => S(s) + case _ => N + } + } + } + sym match { + case S(sym: NewClassSymbol) => + val localScope = scope.derive(s"local ${sym.name}") + val nd = translateNewTypeDefinition(sym, N, false)(localScope) + val ctorMth = localScope.declareValue("ctor", Some(false), false, N).runtimeName + val (constructor, params) = translateNewClassParameters(nd) + val initList = + if (sym.isPlainJSClass) + Ls(JSReturnStmt(S(JSIdent(sym.name)))) + else + Ls( + JSLetDecl.from(Ls(ctorMth)), + JSAssignExpr(JSIdent(ctorMth), JSArrowFn(constructor, L(JSInvoke(JSNew(JSIdent(sym.name)), params)))).stmt, + JSExprStmt(JSAssignExpr(JSIdent(ctorMth).member("class"), JSIdent(sym.name))), + JSReturnStmt(S(JSIdent(ctorMth))) + ) + JSConstDecl(sym.name, JSImmEvalFn( + N, Nil, R(nd :: initList), Nil + )) + case S(sym: MixinSymbol) => + val localScope = scope.derive(s"local ${sym.name}") + val base = localScope.declareValue("base", Some(false), false, N) + val nd = translateNewTypeDefinition(sym, S(base), false)(localScope) + JSConstDecl(sym.name, JSArrowFn( + Ls(JSNamePattern(base.runtimeName)), R(Ls( + JSReturnStmt(S(JSClassExpr(nd))) + )) + )) + case S(sym: ModuleSymbol) => + val localScope = scope.derive(s"local ${sym.name}") + val nd = translateNewTypeDefinition(sym, N, false)(localScope) + val ins = localScope.declareValue("ins", Some(false), false, N).runtimeName + JSConstDecl(sym.name, JSImmEvalFn( + N, Nil, R(Ls( + nd, JSLetDecl.from(Ls(ins)), + JSAssignExpr(JSIdent(ins), JSInvoke(JSNew(JSIdent(sym.name)), Nil)).stmt, + JSExprStmt(JSAssignExpr(JSIdent(ins).member("class"), JSIdent(sym.name))), + JSReturnStmt(S(JSIdent(ins))) + )), Nil + )) + case _ => throw CodeGenError(s"unsupported NuTypeDef in local blocks: $typeDef") + } + } + + protected def translateMixinDeclaration( + mixinSymbol: MixinSymbol, + siblingsMembers: Ls[RuntimeSymbol] + )(implicit getterScope: Scope): JSClassMethod = { + val base = getterScope.declareValue("base", Some(false), false, N) + + val classBody = translateNewTypeDefinition(mixinSymbol, S(base), false)(getterScope) + val qualifierStmt = mixinSymbol.qualifier.fold[JSConstDecl](die)(qualifier => JSConstDecl(qualifier, JSIdent("this"))) + JSClassMethod(mixinSymbol.name, Ls(JSNamePattern(base.runtimeName)), + R((qualifierStmt :: Nil) ::: Ls(JSReturnStmt(S(JSClassExpr(classBody))) + )) + ) + } + + private def translateParents(superFields: Ls[Term], constructorScope: Scope)(implicit scope: Scope): Opt[JSExpr] = { + def translateParent(current: Term, base: JSExpr, mixinOnly: Bool): JSExpr = { + def resolveName(term: Term): Str = term match { + case App(lhs, _) => resolveName(lhs) + case Var(name) => name + case Sel(_, Var(fieldName)) => fieldName + case TyApp(lhs, _) => resolveName(lhs) + case _ => throw CodeGenError("unsupported parents.") + } + + val name = resolveName(current) + + scope.resolveValue(name) match { + case Some(_: TraitSymbol) => base // TODO: + case Some(sym: MixinSymbol) => + JSInvoke(translateNuTypeSymbol(sym, true), Ls(base)) // class D() extends B -> class D extends B.class + case Some(sym: NuTypeSymbol) if !mixinOnly => + translateNuTypeSymbol(sym, true) + case Some(t) => throw CodeGenError(s"unexpected parent symbol $t.") + case N => throw CodeGenError(s"unresolved parent $name.") + } + } + + // for non-first parent classes, they must be mixins or we would get more than one parent classes, + // which is not allowed in JS + superFields match { + case head :: tail => S(tail.foldLeft( + translateParent(head, JSIdent("Object"), false) + )((res, next) => translateParent(next, res, true))) + case Nil => N + } + } + + protected def translateTopModuleDeclaration( + moduleSymbol: ModuleSymbol, + keepTopLevelScope: Bool + )(implicit scope: Scope): JSClassNewDecl = + translateNewTypeDefinition(moduleSymbol, N, keepTopLevelScope) + + protected def translateModuleDeclaration( + moduleSymbol: ModuleSymbol, + siblingsMembers: Ls[RuntimeSymbol] + )(implicit getterScope: Scope): JSClassGetter = { + val decl = translateNewTypeDefinition(moduleSymbol, N, false)(getterScope) + val privateIdent = JSIdent(s"this.#${moduleSymbol.name}") + val qualifierStmt = moduleSymbol.qualifier.fold[JSConstDecl](die)(qualifier => JSConstDecl(qualifier, JSIdent("this"))) + JSClassGetter(moduleSymbol.name, R((qualifierStmt :: Nil) ::: + Ls( + JSIfStmt(JSBinary("===", privateIdent, JSIdent("undefined")), Ls( + decl, + JSExprStmt(JSAssignExpr(privateIdent, + JSNew(JSInvoke(JSIdent(moduleSymbol.name), Nil)))), + JSExprStmt(JSAssignExpr(privateIdent.member("class"), JSIdent(moduleSymbol.name))), + )), + JSReturnStmt(S(privateIdent)) + ))) + } + + protected def translateNewClassParameters(classBody: JSClassNewDecl) = { + val constructor = classBody.ctorParams.map(JSNamePattern(_)) + val params = classBody.ctorParams.map(JSIdent(_)) + (constructor, params) + } + + protected def translateNewClassDeclaration( + classSymbol: NewClassSymbol, + siblingsMembers: Ls[RuntimeSymbol] + )(implicit getterScope: Scope): JSClassGetter = { + val classBody = + translateNewTypeDefinition(classSymbol, N, false)(getterScope) + val (constructor, params) = translateNewClassParameters(classBody) + + val privateIdent = JSIdent(s"this.#${classSymbol.name}") + val qualifierStmt = classSymbol.qualifier.fold[JSConstDecl](die)(qualifier => JSConstDecl(qualifier, JSIdent("this"))) + val initList = + if (classSymbol.isPlainJSClass) + Ls(JSExprStmt(JSAssignExpr(privateIdent, JSIdent(classSymbol.name)))) + else + Ls( + JSExprStmt(JSAssignExpr(privateIdent, + JSArrowFn(constructor, L( + JSInvoke(JSIdent("Object").member("freeze"), Ls(JSInvoke(JSNew(JSIdent(classSymbol.name)), params))) + )))), + JSExprStmt(JSAssignExpr(privateIdent.member("class"), JSIdent(classSymbol.name))), + JSExprStmt(JSAssignExpr(privateIdent.member("unapply"), JSIdent(classSymbol.name).member("unapply"))) + ) + JSClassGetter(classSymbol.name, R(qualifierStmt :: Ls( + JSIfStmt(JSBinary("===", privateIdent, JSIdent("undefined")), + JSExprStmt(JSClassExpr(classBody)) :: initList), + JSReturnStmt(S(privateIdent)) + ))) + } + + protected def translateNewTypeDefinition( + sym: TypeSymbol with NuTypeSymbol, + baseSym: Opt[ValueSymbol], + keepTopLevelScope: Bool + )(implicit scope: Scope): JSClassNewDecl = { + // * nuTypeScope: root scope + // ** inheritanceScope: contains specialized parameters for `super(...)` + // ** bodyScope: contains the part of the class between the `{...}` + // *** constructorScope: contains variables in the ctor statements + // *** memberScopes: contains member methods and variables + val nuTypeScope = scope.derive(sym.toString) + val inheritanceScope = nuTypeScope.derive(s"${sym.name} inheritance") + val bodyScope = nuTypeScope.derive(s"${sym.name} body") + val constructorScope = bodyScope.derive(s"${sym.name} constructor") + + val memberList = ListBuffer[RuntimeSymbol]() // pass to the getter of nested types + val typeList = ListBuffer[Str]() + + // Store the scope for each member and the qualifier's name in the corresponding scope + // Avoid `m._1` or `m._2` in the following code + final case class QualifierPack(memberScope: Scope, qualifier: Str) + + val qualifierName = "qualifier" + val memberScopes = (sym.nested.map(nd => { + val memberScope = bodyScope.derive(s"member ${nd.name}") + val sym = memberScope.declareQualifierSymbol(qualifierName) + nd.name -> QualifierPack(memberScope, sym) + }) ++ sym.methods.map(m => { + val memberScope = bodyScope.derive(s"member ${m.nme.name}") + val sym = memberScope.declareQualifierSymbol(qualifierName) + m.nme.name -> QualifierPack(memberScope, sym) + })).toMap + + // `qualifier` should always be the first value in the getter scope so all qualifiers should have the same name! + val qualifier = memberScopes.values.headOption.fold(S(constructorScope.declareQualifierSymbol(qualifierName)))(mh => { + memberScopes.values.foreach(m => + assert(m.qualifier === mh.qualifier, s"the expected qualifier's runtime name should be ${mh.qualifier}, ${m.qualifier} found") + ) + assert(constructorScope.declareQualifierSymbol(mh.qualifier) === mh.qualifier) + S(mh.qualifier) + }) + + val fields = sym.matchingFields ++ + sym.body.collectTypeNames.flatMap(resolveTraitFields) + + val getters = new ListBuffer[Str]() + + val ctorParams = sym.ctorParams.fold( + fields.map { f => + memberList += NewClassMemberSymbol(f, Some(false), false, !sym.publicCtors.contains(f), qualifier).tap(bodyScope.register) + inheritanceScope.declareValue(f, Some(false), false, N).runtimeName + constructorScope.declareValue(f, Some(false), false, N).runtimeName + } + )(lst => lst.map { p => + if (p._2) { // `constructor(val name)` will also generate a field and a getter + memberList += NewClassMemberSymbol(p._1, Some(false), false, false, qualifier).tap(bodyScope.register) + getters += p._1 + } + constructorScope.declareValue(p._1, Some(false), false, N).runtimeName // Otherwise, it is only available in the constructor + }) + + val initFields = getters.toList.map(name => JSAssignExpr(JSIdent(s"this.#$name"), JSIdent(name)).stmt) + + sym.methods.foreach( + md => memberList += NewClassMemberSymbol(md.nme.name, N, true, false, qualifier).tap(bodyScope.register) + ) + sym.signatures.foreach( + md => memberList += bodyScope.declareStubValue(md.nme.name, N)(true) + ) + sym.ctor.foreach { + case nd @ NuFunDef(rec, Var(nme), _, _, _) => + memberList += NewClassMemberSymbol(nme, rec, false, !nd.genField, qualifier).tap(bodyScope.register) + case _ => () + } + + // TODO: support traitSymbols + val (traitSymbols, classSymbols, mixinSymbols, moduleSymbols) = declareNewTypeDefs(sym.nested, qualifier)(bodyScope) + + if (keepTopLevelScope) // also declare in the top level for diff tests + declareNewTypeDefs(sym.nested, N)(topLevelScope) + classSymbols.foreach(s => {memberList += s; typeList += s.name}) + mixinSymbols.foreach(s => {memberList += s;}) + moduleSymbols.foreach(s => {memberList += s; typeList += s.name}) + val members = sym.methods.map(m => translateNewClassMember(m, fields, qualifier)(memberScopes.getOrElse(m.nme.name, die).memberScope))++ + mixinSymbols.map(s => translateMixinDeclaration(s, memberList.toList)(memberScopes.getOrElse(s.name, die).memberScope)) ++ + moduleSymbols.map(s => translateModuleDeclaration(s, memberList.toList)(memberScopes.getOrElse(s.name, die).memberScope)) ++ + classSymbols.map(s => translateNewClassDeclaration(s, memberList.toList)(memberScopes.getOrElse(s.name, die).memberScope)) + + val base: Opt[JSExpr] = baseSym match { + case Some(base) => S(JSIdent(base.runtimeName)) + case _ => translateParents(sym.superParameters, inheritanceScope) + } + + val traits = sym.body.collectTypeNames.flatMap { + name => scope.getType(name) match { + case S(TraitSymbol(_, runtimeName, _, _, _)) => S(runtimeName) + case S(_: ClassSymbol) => N + case S(_: TypeSymbol) => N + case N => N + } + } + + val (superParameters, rest) = if (baseSym.isDefined) { + val rest = constructorScope.declareValue("rest", Some(false), false, N) + (Ls(JSIdent(s"...${rest.runtimeName}")), S(rest.runtimeName)) + } + else + (sym.superParameters.map { + case App(lhs, Tup(rhs)) => rhs map { + case (_, Fld(_, trm)) => translateTerm(trm)(inheritanceScope) + } + case _ => Nil + }.flatMap(_.reverse).reverse, N) + + val privateMems = new ListBuffer[Str]() + val stmts = sym.ctor.flatMap { + case Eqn(Var(name), rhs) => Ls( + JSAssignExpr(JSIdent(s"this.#$name"), translateTerm(rhs)(constructorScope)).stmt, + JSConstDecl(constructorScope.declareValue(name, S(false), false, N).runtimeName, JSIdent(s"this.#$name")) + ) + case s: Term => JSExprStmt(translateTerm(s)(constructorScope)) :: Nil + case nd @ NuFunDef(_, Var(nme), _, _, Left(rhs)) => + if (nd.genField) { + getters += nme + Ls[JSStmt]( + JSExprStmt(JSAssignExpr(JSIdent(s"this.#$nme"), translateTerm(rhs)(constructorScope))), + JSConstDecl(constructorScope.declareValue(nme, S(false), false, N).runtimeName, JSIdent(s"this.#$nme")) + ) + } + else { + val sym = bodyScope.resolveValue(nme) match { + case Some(sym: NewClassMemberSymbol) => sym + case _ => throw new AssertionError(s"error when handling $nme") + } + if (sym.visited || ctorParams.contains(nme)) { // This field is used in other methods, or it overrides the ctor parameter + privateMems += nme + Ls[JSStmt]( + JSExprStmt(JSAssignExpr(JSIdent(s"this.#$nme"), translateTerm(rhs)(constructorScope))), + JSConstDecl(constructorScope.declareValue(nme, S(false), false, N).runtimeName, JSIdent(s"this.#$nme")) + ) + } + else + JSConstDecl(constructorScope.declareValue(nme, S(false), false, N).runtimeName, + translateTerm(rhs)(constructorScope)) :: Nil + } + case _ => Nil + } + + val tempDecs = constructorScope.tempVars.emit() match { + case S(decs) => decs :: Nil + case _ => Nil + } + + val staticMethods = sym.unapplyMtd match { + // * Note: this code is a bad temporary hack until we have proper `unapply` desugaring + case S(unapplyMtd) => unapplyMtd.rhs match { + case Left(Lam(Tup(_ -> Fld(_, Var(nme)) :: Nil), Let(_, _, _, Tup(fields)))) => + val unapplyScope = nuTypeScope.derive(s"unapply ${sym.name}") + val ins = unapplyScope.declareParameter(nme) + JSClassMethod("unapply", JSNamePattern(ins) :: Nil, L(JSArray(fields.map { + case _ -> Fld(_, trm) => trm match { + case Sel(Var(ins), Var(f)) => JSIdent(s"$ins.$f") + case _ => translateTerm(trm) + } + }))) :: Nil + case _ => throw CodeGenError(s"invalid unapply method in ${sym.name}") + } + case _ => Nil + } + + val qualifierStmt = qualifier.fold[Ls[JSStmt]](Nil)(qualifier => + translateQualifierDeclaration(constructorScope.resolveQualifier(qualifier))) + JSClassNewDecl( + sym.name, + fields, + fields.filter(sym.publicCtors.contains(_)) ++ getters.toList, + privateMems.toList ++ fields, + base, + superParameters, + ctorParams, + rest, + members, + traits, + qualifierStmt ++ tempDecs ++ initFields ++ stmts, + typeList.toList, + sym.ctorParams.isDefined, + staticMethods + ) + } + /** * Translate class methods and getters. */ @@ -449,9 +913,8 @@ class JSBackend(allowUnresolvedSymbols: Boolean) { // Translate class member body. val bodyResult = translateTerm(body)(memberScope).`return` // If `this` is accessed, add `const self = this`. - val bodyStmts = if (visitedSymbols(selfSymbol)) { + val bodyStmts = if (selfSymbol.visited) { val thisDecl = JSConstDecl(selfSymbol.runtimeName, JSIdent("this")) - visitedSymbols -= selfSymbol R(thisDecl :: bodyResult :: Nil) } else { R(bodyResult :: Nil) @@ -463,6 +926,42 @@ class JSBackend(allowUnresolvedSymbols: Boolean) { } } + private def translateNewClassMember( + method: MethodDef[Left[Term, Type]], + props: Ls[Str], // for overriding + qualifier: Opt[Str] + )(implicit memberScope: Scope): JSClassMemberDecl = { + val name = method.nme.name + val preDecs = props.map(p => { + val runtime = memberScope.declareValue(p, Some(false), false, N) + JSConstDecl(runtime.runtimeName, JSIdent(s"this.#$p")) + }) + // Declare parameters. + val (memberParams, body) = method.rhs.value match { + case Lam(params, body) => + val methodParams = translateParams(params)(memberScope) + (S(methodParams), body) + case term => + (N, term) + } + // Translate class member body. + val bodyResult = translateTerm(body)(memberScope).`return` + val tempDecs = memberScope.tempVars.emit() match { + case S(decs) => decs :: Nil + case _ => Nil + } + + val qualifierStmts = qualifier.fold[Ls[JSStmt]](Nil)(qualifier => + translateQualifierDeclaration(memberScope.resolveQualifier(qualifier))) + + val bodyStmts = R(preDecs ++ tempDecs ++ qualifierStmts ++ (bodyResult :: Nil)) + // Returns members depending on what it is. + memberParams match { + case S(memberParams) => JSClassMethod(name, memberParams, bodyStmts) + case N => JSClassGetter(name, bodyStmts) + } + } + /** * Declare symbols for types, traits and classes. * Call this before the code generation. @@ -479,11 +978,111 @@ class JSBackend(allowUnresolvedSymbols: Boolean) { traits += topLevelScope.declareTrait(name, tparams map { _.name }, body, methods) case TypeDef(Cls, TypeName(name), tparams, baseType, _, members, _, _) => classes += topLevelScope.declareClass(name, tparams map { _.name }, baseType, members) - case TypeDef(Nms, _, _, _, _, _, _, _) => throw CodeGenError("Namespaces are not supported yet.") + case TypeDef(Mxn, _, _, _, _, _, _, _) => throw CodeGenError("Mixins are not supported yet.") + case TypeDef(Mod, _, _, _, _, _, _, _) => throw CodeGenError("Namespaces are not supported yet.") } (traits.toList, classes.toList) } + protected def declareNewTypeDefs(typeDefs: Ls[NuTypeDef], qualifier: Opt[Str])(implicit scope: Scope): + (Ls[TraitSymbol], Ls[NewClassSymbol], Ls[MixinSymbol], Ls[ModuleSymbol]) = { + + val traits = new ListBuffer[TraitSymbol]() + val classes = new ListBuffer[NewClassSymbol]() + val mixins = new ListBuffer[MixinSymbol]() + val modules = new ListBuffer[ModuleSymbol]() + + def tt(trm: Term): Type = trm.toType match { + case L(ds) => Top + case R(ty) => ty + } + + def prepare(nme: Str, fs: Ls[Opt[Var] -> Fld], pars: Ls[Term], unit: TypingUnit) = { + val params = fs.map { + case (S(nme), Fld(FldFlags(mut, spec, _), trm)) => + val ty = tt(trm) + nme -> Field(if (mut) S(ty) else N, ty) + case (N, Fld(FldFlags(mut, spec, _), nme: Var)) => nme -> Field(if (mut) S(Bot) else N, Top) + case _ => die + } + val publicCtors = fs.filter{ + case (_, Fld(flags, _)) => flags.genGetter + case _ => false + }.map { + case (S(name), _) => name.name + case (N, Fld(_, nme: Var)) => nme.name + case _ => die + } + + val body = pars.map(tt).foldRight(Record(params): Type)(Inter) + val implemented = new HashSet[Str]() + val members = unit.entities.collect { + case NuFunDef(isLetRec, mnme, _, tys, Left(rhs)) if (isLetRec.isEmpty || isLetRec.getOrElse(false)) => + implemented.add(mnme.name) + MethodDef[Left[Term, Type]](isLetRec.getOrElse(false), TypeName(nme), mnme, tys, Left(rhs)) + } + + val signatures = unit.entities.collect { + case nd @ NuFunDef(isLetRec, mnme, _, tys, Right(rhs)) if nd.genField && !implemented.contains(mnme.name) => + MethodDef[Right[Term, Type]](isLetRec.getOrElse(false), TypeName(nme), mnme, tys, Right(rhs)) + } + + val stmts = unit.entities.filter { + case Asc(Var("this"), _) => false + case Asc(Super(), _) => false + case NuFunDef(S(false), _, _, _, Left(rhs)) => true + case _: Term => true + case _ => false + } + + val nested = unit.entities.collect { + case nd: NuTypeDef => nd + } + + (body, members, signatures, stmts, nested, publicCtors) + } + + typeDefs.foreach { + case td @ NuTypeDef(Mxn, TypeName(mxName), tps, tup, ctor, sig, pars, sup, ths, unit) => { + val (body, members, signatures, stmts, nested, publicCtors) = prepare(mxName, tup.getOrElse(Tup(Nil)).fields, pars, unit) + val sym = MixinSymbol(mxName, tps map { _._2.name }, body, members, signatures, stmts, publicCtors, nested, qualifier).tap(scope.register) + if (!td.isDecl) mixins += sym + } + case td @ NuTypeDef(Mod, TypeName(nme), tps, tup, ctor, sig, pars, sup, ths, unit) => { + val (body, members, signatures, stmts, nested, _) = prepare(nme, tup.getOrElse(Tup(Nil)).fields, pars, unit) + val sym = ModuleSymbol(nme, tps map { _._2.name }, body, members, signatures, stmts, pars, nested, qualifier).tap(scope.register) + if (!td.isDecl) modules += sym + } + case td @ NuTypeDef(Als, TypeName(nme), tps, _, ctor, sig, pars, _, _, _) => { + scope.declareTypeAlias(nme, tps map { _._2.name }, sig.getOrElse(Top)) + } + case td @ NuTypeDef(Cls, TypeName(nme), tps, tup, ctor, sig, pars, sup, ths, unit) => { + val (params, preStmts) = ctor match { + case S(Constructor(Tup(ls), Blk(stmts))) => (S(ls.map { + case (S(Var(nme)), Fld(flags, _)) => (nme, flags.genGetter) + case (N, Fld(flags, Var(nme))) => (nme, flags.genGetter) + case _ => throw CodeGenError(s"Unexpected constructor parameters in $nme.") + }), stmts) + case _ => (N, Nil) + } + val (body, members, signatures, stmts, nested, publicCtors) = prepare(nme, tup.getOrElse(Tup(Nil)).fields, pars, unit) + val sym = + NewClassSymbol(nme, tps map { _._2.name }, params, body, members, td.genUnapply match { + case S(NuFunDef(isLetRec, mnme, _, tys, Left(rhs))) => + S(MethodDef[Left[Term, Type]](isLetRec.getOrElse(false), TypeName(nme), mnme, tys, Left(rhs))) + case _ => N + }, signatures, preStmts ++ stmts, pars, publicCtors, nested, qualifier, td.isPlainJSClass).tap(scope.register) + if (!td.isDecl) classes += sym + } + case td @ NuTypeDef(Trt, TypeName(nme), tps, tup, ctor, sig, pars, sup, ths, unit) => { + val (body, members, _, _, _, _) = prepare(nme, tup.getOrElse(Tup(Nil)).fields, pars, unit) + val sym = scope.declareTrait(nme, tps map { _._2.name }, body, members) + if (!td.isDecl) traits += sym + } + } + (traits.toList, classes.toList, mixins.toList, modules.toList) + } + /** * Recursively collect fields from trait definitions. * Caveat: this might cause stack overflow if cyclic inheritance exists. @@ -511,6 +1110,8 @@ class JSBackend(allowUnresolvedSymbols: Boolean) { case S(sym: TraitSymbol) => N // TODO: inherit from traits case S(sym: TypeAliasSymbol) => throw new CodeGenError(s"cannot inherit from type alias $name" ) + case S(_: NuTypeSymbol) => + throw new CodeGenError(s"NuType symbol $name is not supported when resolving base classes") case N => throw new CodeGenError(s"undeclared type name $name when resolving base classes") } @@ -545,6 +1146,8 @@ class JSBackend(allowUnresolvedSymbols: Boolean) { } class JSWebBackend extends JSBackend(allowUnresolvedSymbols = true) { + def oldDefs = false + // Name of the array that contains execution results val resultsName: Str = topLevelScope declareRuntimeSymbol "results" @@ -552,7 +1155,7 @@ class JSWebBackend extends JSBackend(allowUnresolvedSymbols = true) { polyfill.use("prettyPrint", prettyPrinterName) - def apply(pgrm: Pgrm): Ls[Str] = { + private def generate(pgrm: Pgrm): (Ls[Str], Ls[Str]) = { val (diags, (typeDefs, otherStmts)) = pgrm.desugared val (traitSymbols, classSymbols) = declareTypeDefs(typeDefs) @@ -575,15 +1178,15 @@ class JSWebBackend extends JSBackend(allowUnresolvedSymbols = true) { case Def(recursive, Var(name), L(body), isByname) => val (originalExpr, sym) = if (recursive) { val isByvalueRecIn = if (isByname) None else Some(true) - val sym = topLevelScope.declareValue(name, isByvalueRecIn, body.isInstanceOf[Lam]) + 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, body.isInstanceOf[Lam])) + (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, body.isInstanceOf[Lam])) + (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) :: @@ -598,12 +1201,84 @@ class JSWebBackend extends JSBackend(allowUnresolvedSymbols = true) { ).stmt :: Nil }) val epilogue = resultsIdent.member("map")(JSIdent(prettyPrinterName)).`return` :: Nil - JSImmEvalFn(N, Nil, R(polyfill.emit() ::: stmts ::: epilogue), Nil).toSourceCode.toLines + (JSImmEvalFn(N, Nil, R(polyfill.emit() ::: stmts ::: epilogue), Nil).toSourceCode.toLines, Nil) } + + private def generateNewDef(pgrm: Pgrm): (Ls[Str], Ls[Str]) = { + + val (typeDefs, otherStmts) = pgrm.tops.partitionMap { + case ot: Terms => R(ot) + case fd: NuFunDef => R(fd) + case nd: NuTypeDef => L(nd) + case _ => die + } + + // don't pass `otherStmts` to the top-level module, because we need to execute them one by one later + val topModule = topLevelScope.declareTopModule("TypingUnit", Nil, typeDefs, true) + val moduleIns = topLevelScope.declareValue("typing_unit", Some(false), false, N) + val moduleDecl = translateTopModuleDeclaration(topModule, true)(topLevelScope) + val insDecl = + JSConstDecl(moduleIns.runtimeName, JSNew(JSIdent(topModule.name))) + + val includes = typeDefs.filter(!_.isDecl).map(addNuTypeToGlobalThis(_, moduleIns.runtimeName)) + + val resultsIdent = JSIdent(resultsName) + val resultNames = ListBuffer[Str]() + val stmts: Ls[JSStmt] = + JSConstDecl(resultsName, JSArray(Nil)) :: + (moduleDecl :: insDecl :: includes) + // Generate something like: + // ```js + // const = ; + // .push(); + // ``` + .concat(otherStmts.flatMap { + case NuFunDef(isLetRec, nme @ Var(name), symNme, tys, rhs @ L(body)) => + val recursive = isLetRec.getOrElse(true) + val isByname = isLetRec.isEmpty + 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, body.isLam, N) + val translated = translateTerm(body)(topLevelScope) + topLevelScope.unregisterSymbol(sym) + + val isByvalueRecOut = if (isByname) None else Some(false) + (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, body.isLam, symb)) + }) + val translatedBody = if (sym.isByvalueRec.isEmpty && !sym.isLam) JSArrowFn(Nil, L(originalExpr)) else originalExpr + resultNames += sym.runtimeName + topLevelScope.tempVars `with` JSConstDecl(sym.runtimeName, translatedBody) :: + JSInvoke(resultsIdent("push"), JSIdent(sym.runtimeName) :: Nil).stmt :: Nil + case fd @ NuFunDef(isLetRec, Var(name), _, tys, R(ty)) => + Nil + case _: Def | _: TypeDef | _: Constructor => + throw CodeGenError("Def and TypeDef are not supported in NewDef files.") + case term: Term => + val name = translateTerm(term)(topLevelScope) + resultNames += name.toSourceCode.toString + topLevelScope.tempVars `with` JSInvoke( + resultsIdent("push"), + name :: Nil + ).stmt :: Nil + }) + val epilogue = resultsIdent.member("map")(JSIdent(prettyPrinterName)).`return` :: Nil + (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) } -class JSTestBackend extends JSBackend(allowUnresolvedSymbols = false) { - private val lastResultSymbol = topLevelScope.declareValue("res", Some(false), false) +abstract class JSTestBackend extends JSBackend(allowUnresolvedSymbols = false) { + + private val lastResultSymbol = topLevelScope.declareValue("res", Some(false), false, N) private val resultIdent = JSIdent(lastResultSymbol.runtimeName) private var numRun = 0 @@ -611,12 +1286,20 @@ class JSTestBackend extends JSBackend(allowUnresolvedSymbols = false) { /** * Generate a piece of code for test purpose. It can be invoked repeatedly. */ - def apply(pgrm: Pgrm, allowEscape: Bool): JSTestBackend.Result = - try generate(pgrm)(topLevelScope, allowEscape) catch { - case e: CodeGenError => JSTestBackend.IllFormedCode(e.getMessage()) - case e: UnimplementedError => JSTestBackend.Unimplemented(e.getMessage()) - case e: Throwable => JSTestBackend.UnexpectedCrash(e.getClass().getName, e.getMessage()) - } + def apply(pgrm: Pgrm, allowEscape: Bool, isNewDef: Boolean): JSTestBackend.Result = + if (!isNewDef) + try generate(pgrm)(topLevelScope, allowEscape) catch { + case e: CodeGenError => JSTestBackend.IllFormedCode(e.getMessage()) + case e: UnimplementedError => JSTestBackend.Unimplemented(e.getMessage()) + // case NonFatal(e) => JSTestBackend.UnexpectedCrash(e.getClass().getName, e.getMessage()) + } + else + try generateNewDef(pgrm)(topLevelScope, allowEscape) catch { + case e: CodeGenError => JSTestBackend.IllFormedCode(e.getMessage()) + case e: UnimplementedError => JSTestBackend.Unimplemented(e.getMessage()) + // case NonFatal(e) => JSTestBackend.UnexpectedCrash(e.getClass().getName, e.getMessage()) + } + // generate(pgrm)(topLevelScope, allowEscape) /** * Generate JavaScript code which targets MLscript test from the given block. @@ -645,34 +1328,32 @@ 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) + 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))) + R((translated, scope.declareValue(name, isByvalueRecOut, body.isLam, N))) } catch { case e: UnimplementedError => scope.stubize(sym, e.symbol) L(e.getMessage()) - case e: Throwable => + case NonFatal(e) => scope.unregisterSymbol(sym) val isByvalueRecOut = if (isByname) None else Some(false) - scope.declareValue(name, isByvalueRecOut, bodyIsLam) + scope.declareValue(name, isByvalueRecOut, body.isLam, N) throw e } } else { (try R(translateTerm(body)) catch { case e: UnimplementedError => - scope.declareStubValue(name, e.symbol) + scope.declareStubValue(name, e.symbol, N) L(e.getMessage()) - case e: Throwable => throw e }) map { val isByvalueRec = if (isByname) None else Some(false) - expr => (expr, scope.declareValue(name, isByvalueRec, bodyIsLam)) + expr => (expr, scope.declareValue(name, isByvalueRec, body.isLam, N)) } }) match { case R((originalExpr, sym)) => @@ -692,7 +1373,7 @@ class JSTestBackend extends JSBackend(allowUnresolvedSymbols = false) { case L(reason) => JSTestBackend.AbortedQuery(reason) } case Def(_, Var(name), _, _) => - scope.declareStubValue(name) + scope.declareStubValue(name, N) JSTestBackend.EmptyQuery case term: Term => try { @@ -702,7 +1383,6 @@ class JSTestBackend extends JSBackend(allowUnresolvedSymbols = false) { res } catch { case e: UnimplementedError => JSTestBackend.AbortedQuery(e.getMessage()) - case e: Throwable => throw e } } @@ -716,6 +1396,124 @@ class JSTestBackend extends JSBackend(allowUnresolvedSymbols = false) { JSTestBackend.TestCode(SourceCode.fromStmts(polyfill.emit() ::: prelude).toLines, queries) } + + private def generateNewDef(pgrm: Pgrm)(implicit scope: Scope, allowEscape: Bool): JSTestBackend.TestCode = { + + val (typeDefs, otherStmts) = pgrm.tops.partitionMap { + case _: Constructor => throw CodeGenError("unexpected constructor.") + case ot: Terms => R(ot) + case fd: NuFunDef => R(fd) + case nd: NuTypeDef => L(nd) + case _ => die + } + + otherStmts.foreach { + case fd @ NuFunDef(isLetRec, Var(nme), symNme, _, L(body)) => + val isByname = isLetRec.isEmpty + val isByvalueRecIn = if (isByname) None else Some(true) + val symb = symNme.map(_.name) + scope.declareValue(nme, isByvalueRecIn, body.isLam, symb, true) + case _ => () + } + + // don't pass `otherStmts` to the top-level module, because we need to execute them one by one later + val topModule = topLevelScope.declareTopModule("TypingUnit", Nil, typeDefs, true) + val moduleIns = topLevelScope.declareValue("typing_unit", Some(false), false, N) + val moduleDecl = translateTopModuleDeclaration(topModule, true) + val insDecl = + JSConstDecl(moduleIns.runtimeName, JSNew(JSIdent(topModule.runtimeName))) + + val includes = typeDefs.filter(!_.isDecl).map(addNuTypeToGlobalThis(_, moduleIns.runtimeName)) + + val zeroWidthSpace = JSLit("\"\\u200B\"") + val catchClause = JSCatchClause( + JSIdent("e"), + (zeroWidthSpace + JSIdent("e") + zeroWidthSpace).log() :: Nil + ) + + // TODO Improve: (Lionel) I find this logic very strange! What's going on here? + // Why are we declaring some things above AND below? + // Why does the fact that a binding is recursive affect its declaration in the OUTER scope? + + // Generate statements. + val queries = otherStmts.map { + case NuFunDef(isLetRec, nme @ Var(name), symNme, tys, rhs @ L(body)) => + val recursive = isLetRec.getOrElse(true) + val isByname = isLetRec.isEmpty + 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, 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, 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, body.isLam, symb) + throw e + } + } else { + (try R(translateTerm(body)) catch { // TODO Improve: Why are the bodies translated in the SAME scope?! + case e: UnimplementedError => + scope.declareStubValue(name, e.symbol, symb) + L(e.getMessage()) + }) map { + val isByvalueRec = if (isByname) None else Some(false) + expr => (expr, scope.declareValue(name, isByvalueRec, body.isLam, symb)) + } + }) match { + case R((originalExpr, sym)) => + val expr = + if (sym.isByvalueRec.isEmpty && !sym.isLam) + JSArrowFn(Nil, L(originalExpr)) + else + originalExpr + JSTestBackend.CodeQuery( + scope.tempVars.emit(), + ((JSIdent("globalThis").member(sym.runtimeName) := (expr match { + case t: JSArrowFn => t.toFuncExpr(S(sym.runtimeName)) + case t => t + })) :: Nil), + sym.runtimeName + ) + case L(reason) => JSTestBackend.AbortedQuery(reason) + } + case fd @ NuFunDef(isLetRec, Var(name), symNme, tys, R(ty)) => + val symb = symNme.map(_.name) + scope.declareStubValue(name, symb)(allowEscape || fd.isDecl) + JSTestBackend.EmptyQuery + case term: Term => + try { + val body = translateTerm(term)(scope) + val res = JSTestBackend.CodeQuery(scope.tempVars.emit(), (resultIdent := body) :: Nil) + scope.refreshRes() + res + } catch { + case e: UnimplementedError => JSTestBackend.AbortedQuery(e.getMessage()) + } + case _: Def | _: TypeDef | _: Constructor => + throw CodeGenError("Def and TypeDef are not supported in NewDef files.") + } + + // If this is the first time, insert the declaration of `res`. + var prelude: Ls[JSStmt] = Ls(moduleDecl, insDecl) ::: includes + if (numRun === 0) + prelude = JSLetDecl(lastResultSymbol.runtimeName -> N :: Nil) :: prelude + + // Increase the run number. + numRun = numRun + 1 + + JSTestBackend.TestCode(SourceCode.fromStmts(polyfill.emit() ::: prelude).toLines, queries) + } } object JSTestBackend { @@ -777,7 +1575,7 @@ object JSTestBackend { /** * Code generation crashed. */ - final case class UnexpectedCrash(val name: Str, override val content: Str) extends ErrorMessage(content) + // final case class UnexpectedCrash(val name: Str, override val content: Str) extends ErrorMessage(content) /** * The result is not executed for some reasons. E.g. `:NoJS` flag. diff --git a/shared/src/main/scala/mlscript/MLParser.scala b/shared/src/main/scala/mlscript/MLParser.scala index d958f6cbc6..1ccd068632 100644 --- a/shared/src/main/scala/mlscript/MLParser.scala +++ b/shared/src/main/scala/mlscript/MLParser.scala @@ -23,7 +23,7 @@ class MLParser(origin: Origin, indent: Int = 0, recordLocations: Bool = true) { } def toParam(t: Term): Tup = - Tup((N, Fld(false, false, t)) :: Nil) + Tup((N, Fld(FldFlags.empty, t)) :: Nil) def toParams(t: Term): Tup = t match { case t: Tup => t @@ -41,6 +41,7 @@ class MLParser(origin: Origin, indent: Int = 0, recordLocations: Bool = true) { def number[p: P]: P[Int] = P( CharIn("0-9").repX(1).!.map(_.toInt) ) def ident[p: P]: P[String] = P( (letter | "_") ~~ (letter | digit | "_" | "'").repX ).!.filter(!keywords(_)) + def index[p: P]: P[String] = P( "0" | CharIn("1-9") ~~ digit.repX ).!.map(_.toString) def termOrAssign[p: P]: P[Statement] = P( term ~ ("=" ~ term).? ).map { case (expr, N) => expr @@ -58,6 +59,7 @@ class MLParser(origin: Origin, indent: Int = 0, recordLocations: Bool = true) { | P(kw("undefined")).map(x => UnitLit(true)) | P(kw("null")).map(x => UnitLit(false))) def variable[p: P]: P[Var] = locate(ident.map(Var)) + def fieldName[p: P]: P[Var] = locate( (ident | index).map(Var) ) def parenCell[p: P]: P[Either[Term, (Term, Boolean)]] = (("..." | kw("mut")).!.? ~ term).map { case (Some("..."), t) => Left(t) @@ -67,14 +69,14 @@ class MLParser(origin: Origin, indent: Int = 0, recordLocations: Bool = true) { def parens[p: P]: P[Term] = locate(P( "(" ~/ parenCell.rep(0, ",") ~ ",".!.? ~ ")" ).map { case (Seq(Right(t -> false)), N) => Bra(false, t) - case (Seq(Right(t -> true)), N) => Tup(N -> Fld(true, false, t) :: Nil) // ? single tuple with mutable + case (Seq(Right(t -> true)), N) => Tup(N -> Fld(FldFlags(true, false, false), t) :: Nil) // ? single tuple with mutable case (ts, _) => if (ts.forall(_.isRight)) Tup(ts.iterator.map { - case R(f) => N -> Fld(f._2, false, f._1) + case R(f) => N -> Fld(FldFlags(f._2, false, false), f._1) case _ => die // left unreachable }.toList) else Splc(ts.map { - case R((v, m)) => R(Fld(m, false, v)) + case R((v, m)) => R(Fld(FldFlags(m, false, false), v)) case L(spl) => L(spl) }.toList) }) @@ -83,7 +85,7 @@ class MLParser(origin: Origin, indent: Int = 0, recordLocations: Bool = true) { def subterm[p: P]: P[Term] = P( Index ~~ subtermNoSel ~ ( // Fields: - ("." ~/ (variable | locate(("(" ~/ ident ~ "." ~ ident ~ ")") + ("." ~/ (fieldName | locate(("(" ~/ ident ~ "." ~ ident ~ ")") .map {case (prt, id) => Var(s"${prt}.${id}")}))) .map {(t: Var) => Left(t)} | // Array subscripts: @@ -101,11 +103,11 @@ class MLParser(origin: Origin, indent: Int = 0, recordLocations: Bool = true) { } def record[p: P]: P[Rcd] = locate(P( - "{" ~/ (kw("mut").!.? ~ variable ~ "=" ~ term map L.apply).|(kw("mut").!.? ~ + "{" ~/ (kw("mut").!.? ~ fieldName ~ "=" ~ term map L.apply).|(kw("mut").!.? ~ variable map R.apply).rep(sep = ";" | ",") ~ "}" ).map { fs => Rcd(fs.map{ - case L((mut, v, t)) => v -> Fld(mut.isDefined, false, t) - case R(mut -> id) => id -> Fld(mut.isDefined, false, id) }.toList)}) + case L((mut, v, t)) => v -> Fld(FldFlags(mut.isDefined, false, false), t) + case R(mut -> id) => id -> Fld(FldFlags(mut.isDefined, false, false), id) }.toList)}) def fun[p: P]: P[Term] = locate(P( kw("fun") ~/ term ~ "->" ~ term ).map(nb => Lam(toParams(nb._1), nb._2))) @@ -219,7 +221,8 @@ class MLParser(origin: Origin, indent: Int = 0, recordLocations: Bool = true) { ms.collect { case R(md) => md }, ms.collect{ case L(md) => md }, Nil, N) } case (k @ Als, id, ts) => "=" ~ ty map (bod => TypeDef(k, id, ts, bod, Nil, Nil, Nil, N)) - case (k @ Nms, _, _) => throw new NotImplementedError("Namespaces are not supported yet.") + case (k @ Mod, _, _) => throw new NotImplementedError("Namespaces are not supported yet.") + case (k @ Mxn, _, _) => throw new NotImplementedError("Mixins are not supported yet.") }) def tyParams[p: P]: P[Ls[TypeName]] = ("[" ~ tyName.rep(0, ",") ~ "]").?.map(_.toList.flatten) @@ -252,7 +255,7 @@ class MLParser(origin: Origin, indent: Int = 0, recordLocations: Bool = true) { // Note: field removal types are not supposed to be explicitly used by programmers, // and they won't work in negative positions, // but parsing them is useful in tests (such as shared/src/test/diff/mlscript/Annoying.mls) - def tyNoFun[p: P]: P[Type] = P( (rcd | ctor | parTy) ~ ("\\" ~ variable).rep(0) ) map { + def tyNoFun[p: P]: P[Type] = P( (rcd | ctor | parTy) ~ ("\\" ~ fieldName).rep(0) ) map { case (ty, Nil) => ty case (ty, ids) => Rem(ty, ids.toList) } @@ -269,7 +272,7 @@ class MLParser(origin: Origin, indent: Int = 0, recordLocations: Bool = true) { )) def tyWild[p: P]: P[Bounds] = locate(P("?".! map (_ => Bounds(Bot, Top)))) def rcd[p: P]: P[Record] = - locate(P( "{" ~/ ( kw("mut").!.? ~ variable ~ ":" ~ ty).rep(sep = ";") ~ "}" ) + locate(P( "{" ~/ ( kw("mut").!.? ~ fieldName ~ ":" ~ ty).rep(sep = ";") ~ "}" ) .map(_.toList.map { case (None, v, t) => v -> Field(None, t) case (Some(_), v, t) => v -> Field(Some(t), t) @@ -346,9 +349,9 @@ class MLParser(origin: Origin, indent: Int = 0, recordLocations: Bool = true) { val constructors = bodies.map(cls => adtTyConstructors(cls, alsName, tparams)) val parent = TypeDef(Cls, alsName, tparams, Top, constructors.map { case Def(_, nme, R(body), _) => - val ctorParams = body.body match { - case Function(lhs, _) => lhs - case _: TypeName | _: AppliedType => Top + val ctorParams = body match { + case PolyType(_, Function(lhs, _)) => lhs + case PolyType(_, _: TypeName | _: AppliedType) => Top case _ => die } MethodDef(false, alsName, nme, Nil, R(ctorParams)) diff --git a/shared/src/main/scala/mlscript/Message.scala b/shared/src/main/scala/mlscript/Message.scala index eaea0679a0..8d6a4f46c1 100644 --- a/shared/src/main/scala/mlscript/Message.scala +++ b/shared/src/main/scala/mlscript/Message.scala @@ -4,11 +4,11 @@ import scala.language.implicitConversions import mlscript.utils._, shorthands._ final case class Message(bits: Ls[Message.Bit]) { - def show: Str = { - val ctx = ShowCtx.mk(typeBits) + def show(newDefs: Bool): Str = { + val ctx = ShowCtx.mk(typeBits, newDefs) showIn(ctx) } - def typeBits: Ls[Type] = bits.collect{ case Message.Code(t) => t } + def typeBits: Ls[TypeLike] = bits.collect{ case Message.Code(t) => t } def showIn(ctx: ShowCtx): Str = { bits.map { case Message.Code(ty) => ty.showIn(ctx, 0) @@ -25,15 +25,15 @@ final case class Message(bits: Ls[Message.Bit]) { } object Message { - def mkCtx(msgs: IterableOnce[Message], pre: Str = "'"): ShowCtx = - ShowCtx.mk(msgs.iterator.flatMap(_.typeBits), pre) + def mkCtx(msgs: IterableOnce[Message], newDefs: Bool,pre: Str = "'"): ShowCtx = + ShowCtx.mk(msgs.iterator.flatMap(_.typeBits), newDefs, pre) def join(msgs: Seq[Message]): Message = Message(msgs.iterator.flatMap(_.bits).toList) sealed abstract class Bit final case class Text(str: Str) extends Bit - final case class Code(ty: Type) extends Bit - implicit def fromType(ty: Type): Message = Message(Code(ty)::Nil) + final case class Code(ty: TypeLike) extends Bit + implicit def fromType(ty: TypeLike): Message = Message(Code(ty)::Nil) implicit def fromStr(str: Str): Message = Message(Text(str)::Nil) implicit class MessageContext(private val ctx: StringContext) { diff --git a/shared/src/main/scala/mlscript/NewLexer.scala b/shared/src/main/scala/mlscript/NewLexer.scala index f31370fc7d..6457fb5804 100644 --- a/shared/src/main/scala/mlscript/NewLexer.scala +++ b/shared/src/main/scala/mlscript/NewLexer.scala @@ -17,26 +17,36 @@ class NewLexer(origin: Origin, raise: Diagnostic => Unit, dbg: Bool) { private val isOpChar = Set( '!', '#', '%', '&', '*', '+', '-', '/', ':', '<', '=', '>', '?', '@', '\\', '^', '|', '~' , '.', // ',', - ';' + // ';' ) def isIdentFirstChar(c: Char): 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' + /* // TODO remove (unused) private val isNonStickyKeywordChar = Set( ',', ':', ';', ) + */ private val isSymKeyword = Set( - "->", + // "->", + "=>", "=", ":", ";", + // ",", "#", // ".", // "<", @@ -44,6 +54,7 @@ class NewLexer(origin: Origin, raise: Diagnostic => Unit, dbg: Bool) { ) private val isAlphaOp = Set( + "with", "and", "or", "is", @@ -54,37 +65,169 @@ class NewLexer(origin: Origin, raise: Diagnostic => Unit, dbg: Bool) { def takeWhile(i: Int, cur: Ls[Char] = Nil)(pred: Char => Bool): (Str, Int) = if (i < length && pred(bytes(i))) takeWhile(i + 1, bytes(i) :: cur)(pred) else (cur.reverseIterator.mkString, i) + + final def num(i: Int): (Lit, Int) = { + def test(i: Int, p: Char => Bool): Bool = i < length && p(bytes(i)) + def zero: IntLit = IntLit(BigInt(0)) + /** Take a sequence of digits interleaved with underscores. */ + def takeDigits(i: Int, pred: Char => Bool): (Opt[Str], Int) = { + @tailrec def rec(i: Int, acc: Ls[Char], firstSep: Bool, lastSep: Bool): (Str, Bool, Bool, Int) = + if (i < length) { + val c = bytes(i) + if (pred(c)) rec(i + 1, c :: acc, firstSep, false) + else if (c === '_') rec(i + 1, acc, acc.isEmpty, true) + else (acc.reverseIterator.mkString, firstSep, lastSep, i) + } + else (acc.reverseIterator.mkString, firstSep, lastSep, i) + val (str, firstSep, lastSep, j) = rec(i, Nil, false, false) + if (firstSep) + raise(WarningReport( + msg"Leading separator is not allowed" -> S(loc(i - 1, i)) :: Nil, + newDefs = true, source = Lexing)) + if (lastSep) + raise(WarningReport( + msg"Trailing separator is not allowed" -> S(loc(j - 1, j)) :: Nil, + newDefs = true, source = Lexing)) + (if (str.isEmpty) N else S(str), j) + } + /** Take an integer and coverts to `BigInt`. Also checks if it is empty. */ + def integer(i: Int, radix: Int, desc: Str, pred: Char => Bool): (IntLit, Int) = { + takeDigits(i, pred) match { + case (N, j) => + raise(ErrorReport(msg"Expect at least one $desc digit" -> S(loc(i, i + 2)) :: Nil, + newDefs = true, source = Lexing)) + (zero, j) + case (S(str), j) => (IntLit(BigInt(str, radix)), j) + } + } + def isDecimalStart(ch: Char) = ch === '.' || ch === 'e' || ch === 'E' + /** Take a fraction part with an optional exponent part. Call at periods. */ + def decimal(i: Int, integral: Str): (DecLit, Int) = { + val (fraction, j) = if (test(i, _ === '.')) { + takeDigits(i + 1, isDigit) match { + case (N, j) => + raise(ErrorReport(msg"Expect at least one digit after the decimal point" -> S(loc(i + 1, i + 2)) :: Nil, + newDefs = true, source = Lexing)) + ("", j) + case (S(digits), j) => ("." + digits, j) + } + } else ("", i) + val (exponent, k) = if (test(j, ch => ch === 'e' || ch === 'E')) { + val (sign, k) = if (test(j + 1, ch => ch === '+' || ch === '-')) { + (bytes(j + 1), j + 2) + } else { + ('+', j + 1) + } + takeDigits(k, isDigit) match { + case (N, l) => + raise(ErrorReport(msg"Expect at least one digit after the exponent sign" -> S(loc(l - 1, l)) :: Nil, + newDefs = true, source = Lexing)) + ("", l) + case (S(digits), l) => ("E" + sign + digits, l) + } + } else { + ("", j) + } + (DecLit(BigDecimal(integral + fraction + exponent)), k) + } + if (i < length) { + bytes(i) match { + case '0' if i + 1 < length => bytes(i + 1) match { + case 'x' => integer(i + 2, 16, "hexadecimal", isHexDigit) + case 'o' => integer(i + 2, 8, "octal", isOctDigit) + case 'b' => integer(i + 2, 2, "binary", isBinDigit) + case '.' | 'E' | 'e' => decimal(i + 1, "0") + case _ => integer(i, 10, "decimal", isDigit) + } + case '0' => (zero, i + 1) + case _ => takeDigits(i, isDigit) match { + case (N, j) => + raise(ErrorReport(msg"Expect a numeric literal" -> S(loc(i, i + 1)) :: Nil, + newDefs = true, source = Lexing)) + (zero, i) + case (S(integral), j) => + if (j < length && isDecimalStart(bytes(j))) decimal(j, integral) + else (IntLit(BigInt(integral)), j) + } + } + } else { + raise(ErrorReport(msg"Expect a numeric literal instead of end of input" -> S(loc(i, i + 1)) :: Nil, + newDefs = true, source = Lexing)) + (zero, 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)) + str(i + 1, false, ch :: cur) + } + else { + raise(ErrorReport(msg"Expect an escape character" -> S(loc(i, i + 1)) :: Nil, + newDefs = true, source = Lexing)) + (cur.reverseIterator.mkString, i) + } + else { + if (i < length) + bytes(i) match { + case '\\' => str(i + 1, true, cur) + case '"' | '\n' => (cur.reverseIterator.mkString, i) + case ch => str(i + 1, false, ch :: cur) + } + else + (cur.reverseIterator.mkString, i) + } def loc(start: Int, end: Int): Loc = Loc(start, end, origin) - // @tailrec final + @tailrec final def lex(i: Int, ind: Ls[Int], acc: Ls[TokLoc]): Ls[TokLoc] = if (i >= length) acc.reverse else { val c = bytes(i) def pe(msg: Message): Unit = // raise(ParseError(false, msg -> S(loc(i, i + 1)) :: Nil)) - raise(ErrorReport(msg -> S(loc(i, i + 1)) :: Nil, source = Lexing)) // TODO parse error + raise(ErrorReport(msg -> S(loc(i, i + 1)) :: Nil, newDefs = true, source = Lexing)) // @inline - def go(j: Int, tok: Token) = lex(j, ind, (tok, loc(i, j)) :: acc) + // def go(j: Int, tok: Token) = lex(j, ind, (tok, loc(i, j)) :: acc) + def next(j: Int, tok: Token) = (tok, loc(i, j)) :: acc c match { case ' ' => val (_, j) = takeWhile(i)(_ === ' ') - go(j, SPACE) + // go(j, SPACE) + lex(j, ind, next(j, SPACE)) case ',' => val j = i + 1 - go(j, COMMA) + // go(j, COMMA) + lex(j, ind, next(j, COMMA)) + case ';' => + val j = i + 1 + lex(j, ind, next(j, SEMI)) case '"' => val j = i + 1 - val (chars, k) = takeWhile(j)(c => c =/= '"' && c =/= '\n') + val (chars, k) = str(j, false) val k2 = if (bytes.lift(k) === Some('"')) k + 1 else { pe(msg"unclosed quotation mark") k } - go(k2, LITVAL(StrLit(chars))) + // go(k2, LITVAL(StrLit(chars))) + lex(k2, ind, next(k2, LITVAL(StrLit(chars)))) case '/' if bytes.lift(i + 1).contains('/') => val j = i + 2 val (txt, k) = takeWhile(j)(c => c =/= '\n') - go(k, COMMENT(txt)) + // go(k, COMMENT(txt)) + lex(k, ind, next(k, COMMENT(txt))) case '/' if bytes.lift(i + 1).contains('*') => // multiple-line comment val j = i + 2 var prev1 = '/'; var prev2 = '*' @@ -94,9 +237,12 @@ class NewLexer(origin: Origin, raise: Diagnostic => Unit, dbg: Bool) { prev1 = prev2; prev2 = c res }) - go(k, COMMENT(txt.dropRight(2))) - case BracketKind(Left(k)) => go(i + 1, OPEN_BRACKET(k)) - case BracketKind(Right(k)) => go(i + 1, CLOSE_BRACKET(k)) + // go(k, COMMENT(txt.dropRight(2))) + lex(k, ind, next(k, COMMENT(txt.dropRight(2)))) + // case BracketKind(Left(k)) => go(i + 1, OPEN_BRACKET(k)) + // case BracketKind(Right(k)) => go(i + 1, CLOSE_BRACKET(k)) + case BracketKind(Left(k)) => lex(i + 1, ind, next(i + 1, OPEN_BRACKET(k))) + case BracketKind(Right(k)) => lex(i + 1, ind, next(i + 1, CLOSE_BRACKET(k))) case '\n' => val j = i + 1 val (space, k) = @@ -123,20 +269,38 @@ class NewLexer(origin: Origin, raise: Diagnostic => Unit, dbg: Bool) { } case _ if isIdentFirstChar(c) => val (n, j) = takeWhile(i)(isIdentChar) - go(j, if (keywords.contains(n)) KEYWORD(n) else IDENT(n, isAlphaOp(n))) + // go(j, if (keywords.contains(n)) KEYWORD(n) else IDENT(n, isAlphaOp(n))) + lex(j, ind, next(j, if (keywords.contains(n)) KEYWORD(n) else IDENT(n, isAlphaOp(n)))) case _ if isOpChar(c) => val (n, j) = takeWhile(i)(isOpChar) - if (n === "." && j < length && isIdentFirstChar(bytes(j))) { - val (name, k) = takeWhile(j)(isIdentChar) - go(k, SELECT(name)) + if (n === "." && j < length) { + val nc = bytes(j) + if (isIdentFirstChar(nc)) { + val (name, k) = takeWhile(j)(isIdentChar) + // go(k, SELECT(name)) + lex(k, ind, next(k, SELECT(name))) + } + else if ( + // The first character is '0' and the next character is not a digit + (nc === '0' && !(j + 1 < length && isDigit(bytes(j + 1)))) || + ('0' < nc && nc <= '9') // The first character is a digit other than '0' + ) { + val (name, k) = takeWhile(j)(isDigit) + // go(k, SELECT(name)) + lex(k, ind, next(k, SELECT(name))) + } + else lex(j, ind, next(j, if (isSymKeyword.contains(n)) KEYWORD(n) else IDENT(n, true))) } - else go(j, if (isSymKeyword.contains(n)) KEYWORD(n) else IDENT(n, true)) + // 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) - go(j, LITVAL(IntLit(BigInt(str)))) + val (lit, j) = num(i) + // go(j, LITVAL(IntLit(BigInt(str)))) + lex(j, ind, next(j, LITVAL(lit))) case _ => pe(msg"unexpected character '${escapeChar(c)}'") - go(i + 1, ERROR) + // go(i + 1, ERROR) + lex(i + 1, ind, next(i + 1, ERROR)) } } @@ -174,10 +338,12 @@ class NewLexer(origin: Origin, raise: Diagnostic => Unit, dbg: Bool) { case ((k0, l0), oldAcc) :: stack => if (k0 =/= k1) raise(ErrorReport(msg"Mistmatched closing ${k1.name}" -> S(l1) :: - msg"does not correspond to opening ${k0.name}" -> S(l0) :: Nil, source = Parsing)) + msg"does not correspond to opening ${k0.name}" -> S(l0) :: Nil, newDefs = true, + source = Parsing)) go(rest, true, stack, BRACKETS(k0, acc.reverse)(l0.right ++ l1.left) -> (l0 ++ l1) :: oldAcc) case Nil => - raise(ErrorReport(msg"Unexpected closing ${k1.name}" -> S(l1) :: Nil, source = Parsing)) + raise(ErrorReport(msg"Unexpected closing ${k1.name}" -> S(l1) :: Nil, + newDefs = true, source = Parsing)) go(rest, false, stack, acc) } case (INDENT, loc) :: rest => @@ -198,8 +364,10 @@ class NewLexer(origin: Origin, raise: Diagnostic => Unit, dbg: Bool) { })) => // split `>>` to `>` and `>` so that code like `A>` can be parsed correctly go((CLOSE_BRACKET(Angle) -> loc.left) :: (IDENT(id.drop(1), true) -> loc) :: rest, false, stack, acc) case ((tk @ IDENT(">", true), loc)) :: rest if canStartAngles => - raise(WarningReport(msg"This looks like an angle bracket, but it does not close any angle bracket section" -> S(loc) :: - msg"Add spaces around it if you intended to use `<` as an operator" -> N :: Nil, source = Parsing)) + raise(WarningReport( + msg"This looks like an angle bracket, but it does not close any angle bracket section" -> S(loc) :: + msg"Add spaces around it if you intended to use `<` as an operator" -> N :: Nil, + newDefs = true, source = Parsing)) go(rest, false, stack, tk -> loc :: acc) case (tk: Stroken, loc) :: rest => go(rest, tk match { @@ -215,7 +383,7 @@ class NewLexer(origin: Origin, raise: Diagnostic => Unit, dbg: Bool) { if (k === Angle) msg"Note that `<` without spaces around it is considered as an angle bracket and not as an operator" -> N :: Nil else Nil - ), source = Parsing)) + ), newDefs = true, source = Parsing)) (oldAcc ::: acc).reverse case Nil => acc.reverse } @@ -235,7 +403,10 @@ object NewLexer { "if", "then", "else", + "case", "fun", + "val", + "var", // "is", // "as", "of", @@ -247,20 +418,34 @@ object NewLexer { // "any", // "all", "mut", + "declare", "class", "trait", + "mixin", "interface", + "extends", + "override", + "super", "new", "namespace", + "module", "type", "where", "forall", "exists", + "in", + "out", + "null", + "undefined", + "abstract", + "constructor", + "virtual" ) def printToken(tl: TokLoc): Str = tl match { case (SPACE, _) => " " case (COMMA, _) => "," + case (SEMI, _) => ";" case (NEWLINE, _) => "↵" case (INDENT, _) => "→" case (DEINDENT, _) => "←" diff --git a/shared/src/main/scala/mlscript/NewParser.scala b/shared/src/main/scala/mlscript/NewParser.scala index b145f1393a..ace8141eb8 100644 --- a/shared/src/main/scala/mlscript/NewParser.scala +++ b/shared/src/main/scala/mlscript/NewParser.scala @@ -13,34 +13,108 @@ object NewParser { type ExpectThen >: Bool type FoundErr >: Bool // may be better done as: class FoundErr(var found: Bool) - def expectThen(implicit ptr: ExpectThen): Bool = ptr === true - def foundErr(implicit ptr: FoundErr): Bool = ptr === true + final def expectThen(implicit ptr: ExpectThen): Bool = ptr === true + final def foundErr(implicit ptr: FoundErr): Bool = ptr === true + + type TokLoc = (Stroken, Loc) + + type LTL = Ls[TokLoc] + + private val MinPrec = 0 + private val NoElsePrec = MinPrec + 1 + + private val prec: Map[Char,Int] = + List( + "", // 0 is the virtual precedence of 'else' + "", + "", + "", + "", + "", + // ^ for keywords + // ";", + ",", + "=", + "@", + ":", + "|", + "/ \\", + "^", + "&", + // "= !", + "!", + "< >", + "+ -", + // "* / %", + "* %", + "", // Precedence of application + ".", + ).zipWithIndex.flatMap { + case (cs, i) => cs.filterNot(_ === ' ').map(_ -> (i + 1)) + }.toMap.withDefaultValue(Int.MaxValue) + + private val AppPrec = prec('.') - 1 + + final def opCharPrec(opChar: Char): Int = prec(opChar) + final def opPrec(opStr: Str): (Int, Int) = opStr match { + case "is" => (4, 4) + case "and" => (3, 3) + case "or" => (2, 2) + case "=>" => + // * The lambda operator is special: + // * it should associate very strongly on the left and very loosely on the right + // * so that we can write things like `f() |> x => x is 0` ie `(f()) |> (x => (x is 0))` + val eqPrec = prec('.') // * We pick the tightest precedence + (eqPrec, 1) + // * Note: we used to do this instead which broke the example above on both sides: + // val eqPrec = prec('=') + // (eqPrec, eqPrec - 1) + case _ if opStr.exists(_.isLetter) => + (5, 5) + case _ => + val r = opStr.last + (prec(opStr.head), prec(r) - (if (r === '@' || r === '/' || r === ',' || r === ':') 1 else 0)) + } } import NewParser._ -abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: Diagnostic => Unit, val dbg: Bool, fallbackLoc: Opt[Loc], description: Str = "input") { +abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], newDefs: Bool, raiseFun: Diagnostic => Unit, val dbg: Bool, fallbackLoc: Opt[Loc], description: Str = "input") { outer => - def rec(tokens: Ls[Stroken -> Loc], fallbackLoc: Opt[Loc], description: Str): NewParser = - new NewParser(origin, tokens, raiseFun, dbg, fallbackLoc, description) { + private var freshCnt: Int = 0 + final def freshVar: Var = { + val res = Var("_$" + freshCnt) + freshCnt += 1 + res + } + + object Spaces { + def unapply(xs: Ls[Stroken -> Loc]): S[(() => Unit, Ls[Stroken -> Loc])] = xs match { + case (SPACE, _) :: Spaces(cons, rest) => S((() => {cons(); consume}, rest)) + case _ => S(() => (), xs) + } + } + + final def rec(tokens: Ls[Stroken -> Loc], fallbackLoc: Opt[Loc], description: Str): NewParser = + new NewParser(origin, tokens, newDefs, raiseFun, dbg, fallbackLoc, description) { def doPrintDbg(msg: => Str): Unit = outer.printDbg("> " + msg) } - def raise(mkDiag: => Diagnostic)(implicit fe: FoundErr = false): Unit = + final def raise(mkDiag: => Diagnostic)(implicit fe: FoundErr = false): Unit = if (!foundErr) raiseFun(mkDiag) - def err(msgs: Ls[Message -> Opt[Loc]]): Unit = - raise(ErrorReport(msgs, source = Diagnostic.Parsing)) + final def err(msgs: Ls[Message -> Opt[Loc]]): Unit = + raise(ErrorReport(msgs, newDefs = true, source = Diagnostic.Parsing)) - def mkLoc(l: Int, r: Int): Loc = + final def mkLoc(l: Int, r: Int): Loc = Loc(l, r, origin) protected def doPrintDbg(msg: => Str): Unit protected def printDbg(msg: => Any): Unit = doPrintDbg("│ " * this.indent + msg) protected var indent = 0 - private def wrap[R](args: Any)(mkRes: Unit => R)(implicit l: Line, n: Name): R = { + private def wrap[R](args: => Any)(mkRes: Unit => R)(implicit l: Line, n: Name): R = { printDbg(s"@ ${n.value}${args match { case it: Iterable[_] => it.mkString("(", ",", ")") case _: Product => args @@ -55,7 +129,7 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D res } - def parseAll[R](parser: => R): R = { + final def parseAll[R](parser: => R): R = { val res = parser cur match { case c @ (tk, tkl) :: _ => @@ -66,7 +140,7 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D res } - def concludeWith[R](f: this.type => R): R = { + final def concludeWith[R](f: this.type => R): R = { val res = f(this) cur.dropWhile(tk => (tk._1 === SPACE || tk._1 === NEWLINE) && { consume; true }) match { case c @ (tk, tkl) :: _ => @@ -77,58 +151,6 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D printDbg(s"Concluded with $res") res } - def nil: Unit = () - - type TokLoc = (Stroken, Loc) - - type LTL = Ls[TokLoc] - - private val MinPrec = 0 - private val NoElsePrec = MinPrec + 1 - - private val prec: Map[Char,Int] = - List( - "", // 0 is the virtual precedence of 'else' - "", - "", - "", - "", - "", - // ^ for keywords - ",", - ";", - "=", - "@", - ":", - "|", - "/ \\", - "^", - "&", - // "= !", - "!", - "< >", - "+ -", - // "* / %", - "* %", - ".", - ).zipWithIndex.flatMap { - case (cs, i) => cs.filterNot(_ === ' ').map(_ -> (i + 1)) - }.toMap.withDefaultValue(Int.MaxValue) - - def opCharPrec(opChar: Char): Int = prec(opChar) - def opPrec(opStr: Str): (Int, Int) = opStr match { - case "is" => (4, 4) - case "and" => (3, 3) - case "or" => (2, 2) - case "=>" => - val eqPrec = prec('=') - (eqPrec, eqPrec - 1) - case _ if opStr.exists(_.isLetter) => - (5, 5) - case _ => - val r = opStr.last - (prec(opStr.head), prec(r) - (if (r === '@' || r === '/' || r === ',') 1 else 0)) - } // def pe(msg: Message, l: Loc, rest: (Message, Opt[Loc])*): Unit = // err((msg -> S(l) :: rest.toList)) // TODO parse err @@ -136,6 +158,11 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D private var _cur: Ls[TokLoc] = tokens + private var _modifiersCache: ModifierSet = ModifierSet.empty + def resetCur(newCur: Ls[TokLoc]): Unit = { + _cur = newCur + _modifiersCache = ModifierSet.empty + } private def summarizeCur = NewLexer.printTokens(_cur.take(5)) + (if (_cur.sizeIs > 5) "..." else "") @@ -149,13 +176,13 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D _cur } - def consume(implicit l: Line, n: Name): Unit = { + final def consume(implicit l: Line, n: Name): Unit = { if (dbg) printDbg(s"! ${n.value}\t\tconsumes ${NewLexer.printTokens(_cur.take(1))} [at l.${l.value}]") - _cur = _cur.tailOption.getOrElse(Nil) // FIXME throw error if empty? + resetCur(_cur.tailOption.getOrElse(Nil)) // FIXME throw error if empty? } // TODO simplify logic – this is no longer used much - def skip(tk: Stroken, ignored: Set[Stroken] = Set(SPACE), allowEnd: Bool = false, note: => Ls[Message -> Opt[Loc]] = Nil) + final def skip(tk: Stroken, ignored: Set[Stroken] = Set(SPACE), allowEnd: Bool = false, note: => Ls[Message -> Opt[Loc]] = Nil) (implicit fe: FoundErr): (Bool, Opt[Loc]) = wrap(tk, ignored, allowEnd) { l => require(!ignored(tk)) val skip_res = cur match { @@ -184,7 +211,7 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D private def curLoc = _cur.headOption.map(_._2) /* // * TODO rm - def blockTerm: Blk = { + final def blockTerm: Blk = { val ts = block(false, false) val es = ts.map { case L(t) => @@ -196,7 +223,7 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D } */ - def typingUnit: TypingUnit = { + final def typingUnit: TypingUnit = { val ts = block(false, false) val es = ts.map { case L(t) => @@ -204,54 +231,110 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D errExpr case R(d: NuDecl) => d case R(e: Term) => e + case R(c: Constructor) => c case _ => ??? } TypingUnit(es) } - def typingUnitMaybeIndented(implicit fe: FoundErr): TypingUnit = yeetSpaces match { + final def typingUnitMaybeIndented(implicit fe: FoundErr): TypingUnit = yeetSpaces match { case (br @ BRACKETS(Indent, toks), _) :: _ => consume rec(toks, S(br.innerLoc), br.describe).concludeWith(_.typingUnit) case _ => typingUnit } - def curlyTypingUnit(implicit fe: FoundErr): TypingUnit = yeetSpaces match { + final def curlyTypingUnit(implicit fe: FoundErr): Opt[TypingUnit] = yeetSpaces match { case (br @ BRACKETS(Curly, toks), l1) :: _ => consume - rec(toks, S(br.innerLoc), br.describe).concludeWith(_.typingUnitMaybeIndented).withLoc(S(l1)) + S(rec(toks, S(br.innerLoc), br.describe).concludeWith(_.typingUnitMaybeIndented).withLoc(S(l1))) case _ => - TypingUnit(Nil) + N } - def toParams(t: Term): Tup = t match { - case t: Tup => t - case Bra(false, t: Tup) => t - case _ => Tup((N, Fld(false, false, t)) :: Nil) - } - def toParamsTy(t: Type): Tuple = t match { + final def toParamsTy(t: Type): Tuple = t match { case t: Tuple => t case _ => Tuple((N, Field(None, t)) :: Nil) } - def typ(prec: Int = 0)(implicit fe: FoundErr, l: Line): Type = + final def typ(prec: Int = 0)(implicit fe: FoundErr, l: Line): Type = mkType(expr(prec)) - def block(implicit et: ExpectThen, fe: FoundErr): Ls[IfBody \/ Statement] = + case class ModifierSet(mods: Map[Str, Loc]) { + def handle(mod: Str): (Opt[Loc], ModifierSet) = + mods.get(mod) -> copy(mods = mods - mod) + def done: Unit = mods.foreachEntry { (mod, loc) => + err(msg"Unrecognized modifier `${mod}` in this position" -> S(loc) :: Nil) + } + } + object ModifierSet { + val empty: ModifierSet = ModifierSet(Map.empty) + private def go(acc: ModifierSet): ModifierSet = cur match { + case (KEYWORD(kw), l0) :: c if acc.mods.contains(kw) => + err(msg"Repeated modifier `${kw}`" -> S(l0) :: Nil) + consume + yeetSpaces + go(acc) + case (KEYWORD("declare"), l0) :: c => + consume + yeetSpaces + go(acc.copy(acc.mods + ("declare" -> l0))) + case (KEYWORD("virtual"), l0) :: c => + consume + yeetSpaces + go(acc.copy(acc.mods + ("virtual" -> l0))) + case (KEYWORD("abstract"), l0) :: c => + consume + yeetSpaces + go(acc.copy(acc.mods + ("abstract" -> l0))) + case _ if acc.mods.isEmpty => acc + case (KEYWORD("class" | "infce" | "trait" | "mixin" | "type" | "namespace" | "module" | "fun" | "val"), l0) :: _ => + acc + case (tok, loc) :: _ => + // TODO support indented blocks of modified declarations... + err(msg"Unexpected ${tok.describe} token after modifier${if (acc.mods.sizeIs > 1) "s" else ""}" -> S(loc) :: Nil) + acc + case Nil => + ??? // TODO: + } + def unapply(__ : Ls[TokLoc]): S[ModifierSet -> Ls[TokLoc]] = { + val res = go(_modifiersCache) + _modifiersCache = res + S(res, _cur) + } + } + final def block(implicit et: ExpectThen, fe: FoundErr): Ls[IfBody \/ Statement] = cur match { case Nil => Nil case (NEWLINE, _) :: _ => consume; block case (SPACE, _) :: _ => consume; block + case (KEYWORD("constructor"), l0) :: _ => + consume + val res = yeetSpaces match { + case (br @ BRACKETS(Round, toks), loc) :: _ => + consume + val as = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.argsMaybeIndented()) // TODO + val body = curlyTypingUnit.getOrElse(TypingUnit(Nil)) + Constructor(Tup(as).withLoc(S(loc)), Blk(body.entities).withLocOf(body)) + case _ => + err(msg"Expect parameter list for the constructor" -> S(l0) :: Nil) + Constructor(Tup(Nil), Blk(Nil)) + } + R(res.withLoc(S(l0 ++ res.getLoc))) :: block case c => val t = c match { - case (KEYWORD(k @ ("class" | "trait" | "type" | "namespace")), l0) :: c => + case ModifierSet(mods, (KEYWORD(k @ ("class" | "infce" | "trait" | "mixin" | "type" | "module")), l0) :: c) => consume + val (isDecl, mods2) = mods.handle("declare") + val (isAbs, mods3) = mods2.handle("abstract") + mods3.done val kind = k match { case "class" => Cls case "trait" => Trt + case "mixin" => Mxn case "type" => Als - case "namespace" => Nms + case "module" => Mod case _ => die } val (tn, success) = yeetSpaces match { - case (IDENT(idStr, false), l1) :: _ => + case (IDENT(idStr, _), l1) :: _ => consume (TypeName(idStr).withLoc(S(l1)), true) case c => @@ -263,68 +346,155 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D (TypeName("").withLoc(curLoc.map(_.left)), false) } val tparams = yeetSpaces match { - case (br @ BRACKETS(Angle, toks), loc) :: _ => + case (br @ BRACKETS(Angle | Square, toks), loc) :: _ => consume + /* val ts = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.argsMaybeIndented()).map { case (N, Fld(false, false, v @ Var(nme))) => TypeName(nme).withLocOf(v) - case _ => ??? + case nmeo -> param => + err(msg"unsupported type parameter shape (${param.describe})" -> + param.value.toLoc :: Nil) + TypeName(nmeo.fold("")(_.name)).withLocOf(param.value) } ts + */ + rec(toks, S(br.innerLoc), br.describe).concludeWith(_.maybeIndented((p, _) => p.typeParams)) case _ => Nil } val params = yeetSpaces match { case (br @ BRACKETS(Round, toks), loc) :: _ => consume val as = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.argsMaybeIndented()) // TODO - Tup(as).withLoc(S(loc)) - case _ => Tup(Nil) + S(Tup(as).withLoc(S(loc))) + case _ => N } - def parents(Sep: Stroken): Ls[Term] = yeetSpaces match { - case (Sep, _) :: _ => + def otherParents: Ls[Term] = yeetSpaces match { + case (COMMA, _) :: _ => consume - expr(0) :: parents(COMMA) + expr(prec(',')) :: otherParents // we don't want to parse parent lists as including comma expressions case _ => Nil } - val ps = parents(if (kind === Als) KEYWORD("=") else KEYWORD(":")) - val body = curlyTypingUnit - R(NuTypeDef(kind, tn, tparams, params, ps, body)) + val sigTrm = yeetSpaces match { + case (KEYWORD("="), _) :: _ if kind is Als => + consume + S(expr(0)) + case (KEYWORD(":"), _) :: _ if !(kind is Als) => + consume + S(expr(0)) + case _ => N + } + val ps = yeetSpaces match { + // case (KEYWORD("="), _) :: _ if kind is Als => + // consume + // expr(0) :: otherParents + case (KEYWORD("extends"), _) :: _ => + consume + expr(prec(',')) :: otherParents // we don't want to parse parent lists as including comma expressions + case _ => Nil + } + val (sigTrm2, ps2, fullTu) = curlyTypingUnit.fold { + ps.lastOption match { + case S(Rft(bse, tu)) => (sigTrm, (bse :: ps.reverse.tail).reverse, tu) + case _ => + sigTrm match { + case S(Rft(bse, tu)) => (S(bse), ps, tu) + case _ => + (sigTrm, ps, TypingUnit(Nil)) + } + } + }(tu => (sigTrm, ps, tu)) + val sig = sigTrm2.map(mkType(_)) + val (ctors, bodyStmts) = fullTu.entities.partitionMap { + case c: Constructor => L(c) + case t => R(t) + } + val tu = TypingUnit(bodyStmts).withLocOf(fullTu) + if (ctors.lengthIs > 1) { + err(msg"A class may have at most one explicit constructor" -> S(l0) :: Nil) + N + } + val ctor = ctors.headOption + val res = + NuTypeDef(kind, tn, tparams, params, ctor, sig, ps2, N, N, tu)(isDecl, isAbs) + R(res.withLoc(S(l0 ++ tn.getLoc ++ res.getLoc))) + R(res.withLoc(S(l0 ++ res.getLoc))) - // TODO make `fun` by-name and `let` by-value - case (KEYWORD(kwStr @ ("fun" | "let")), l0) :: c => // TODO support rec? + case ModifierSet(mods, (KEYWORD(kwStr @ ("fun" | "val" | "let")), l0) :: c) => // TODO support rec? consume + val (isDecl, mods2) = mods.handle("declare") + val (isVirtual, mods3) = mods2.handle("virtual") + mods3.done + val genField = kwStr =/= "let" val isLetRec = yeetSpaces match { case (KEYWORD("rec"), l1) :: _ if kwStr === "let" => consume S(true) case c => if (kwStr === "fun") N else S(false) } + val opStr = yeetSpaces match { + case (BRACKETS(Round, ts), brackloc) :: _ => + ts match { + case (IDENT(opStr, true), l1) :: rest => + consume + rest.dropWhile(_._1 === SPACE) match { + case Nil => + case (tok, loc) :: ts => + err((msg"Unexpected ${tok.describe} after symbolic name" -> S(loc) :: Nil)) + } + S(Var(opStr).withLoc(S(l1))) + case (tok, loc) :: _ => + consume + err((msg"Expected a symbolic name, found ${tok.describe} instead" -> S(loc) :: Nil)) + N + case Nil => + consume + err((msg"Expected a symbolic name between brackets, found nothing" -> S(brackloc) :: Nil)) + N + } + case _ => N + } val (v, success) = yeetSpaces match { case (IDENT(idStr, false), l1) :: _ => consume (Var(idStr).withLoc(S(l1)), true) case c => val (tkstr, loc) = c.headOption.fold(("end of input", lastLoc))(_.mapFirst(_.describe).mapSecond(some)) - err(( - // msg"Expected a function name; found ${"[TODO]"} instead" -> N :: Nil)) - msg"Expected a function name; found ${tkstr} instead" -> loc :: Nil)) + err((msg"Expected a function name; found ${tkstr} instead" -> loc :: Nil)) consume // R(errExpr) (Var("").withLoc(curLoc.map(_.left)), false) } foundErr || !success pipe { implicit fe => val tparams = if (kwStr === "let") Ls[TypeName]() else yeetSpaces match { - case (br @ BRACKETS(Angle, toks), loc) :: _ => + case (br @ BRACKETS(Angle | Square, toks), loc) :: _ => consume val ts = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.argsMaybeIndented()).map { - case (N, Fld(false, false, v @ Var(nme))) => + case (N, Fld(FldFlags(false, false, _), v @ Var(nme))) => TypeName(nme).withLocOf(v) case _ => ??? } ts case _ => Nil } - val ps = funParams + val (ps, transformBody) = yeetSpaces match { + case (br @ BRACKETS(Round, Spaces(cons, (KEYWORD("override"), ovLoc) :: rest)), loc) :: rest2 => + resetCur(BRACKETS(Round, rest)(br.innerLoc) -> loc :: rest2) + funParams match { + case ps @ Tup(N -> Fld(FldFlags(false, false, gen), pat) :: Nil) :: Nil => + val fv = freshVar + (Tup(N -> Fld(FldFlags(false, false, gen), fv) :: Nil) :: Nil, S( + (body: Term) => If(IfOpApp(fv, Var("is"), IfThen(pat, body)), S( + App(Sel(Super().withLoc(S(ovLoc)), v), Tup(N -> Fld(FldFlags(false, false, gen), fv) :: Nil)) + )) + )) + case r => + err(msg"Unsupported 'override' parameter list shape" -> S(br.innerLoc) :: Nil) + (r, N) + } + case _ => + (funParams, N) + } val asc = yeetSpaces match { case (KEYWORD(":"), _) :: _ => consume @@ -336,41 +506,69 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D case (KEYWORD("="), _) :: _ => consume val body = expr(0) - val annotatedBody = asc.fold(body)(ty => Asc(body, ty)) - R(NuFunDef(isLetRec, v, tparams, L(ps.foldRight(annotatedBody)((i, acc) => Lam(i, acc))))) + val newBody = transformBody.fold(body)(_(body)) + val annotatedBody = asc.fold(newBody)(ty => Asc(newBody, ty)) + yeetSpaces match { + case (KEYWORD("in"), l1) :: _ if kwStr === "let" => + 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))) + case _ => + R(NuFunDef( + isLetRec, v, opStr, tparams, L(ps.foldRight(annotatedBody)((i, acc) => Lam(i, acc))) + )(isDecl, isVirtual, N, N, genField).withLoc(S(l0 ++ annotatedBody.toLoc))) + } case c => asc match { case S(ty) => - R(NuFunDef(isLetRec, v, tparams, R(PolyType(Nil, ps.foldRight(ty)((p, r) => Function(p.toType match { + if (transformBody.nonEmpty) die // TODO + R(NuFunDef(isLetRec, v, opStr, tparams, R(PolyType(Nil, ps.foldRight(ty)((p, r) => Function(p.toType match { case L(diag) => raise(diag); Top // TODO better case R(tp) => tp - }, r)))))) // TODO rm PolyType after FCP is merged + }, r)))))(isDecl, isVirtual, N, N, genField).withLoc(S(l0 ++ ty.toLoc))) + // TODO rm PolyType after FCP is merged case N => // TODO dedup: val (tkstr, loc) = c.headOption.fold(("end of input", lastLoc))(_.mapFirst(_.describe).mapSecond(some)) err(( msg"Expected ':' or '=' followed by a function body or signature; found ${tkstr} instead" -> loc :: Nil)) consume - R(NuFunDef(isLetRec, v, Nil, L(ps.foldRight(errExpr: Term)((i, acc) => Lam(i, acc))))) + val bod = errExpr + R(NuFunDef( + isLetRec, v, opStr, Nil, L(ps.foldRight(bod: Term)((i, acc) => Lam(i, acc))) + )(isDecl, isVirtual, N, N, genField).withLoc(S(l0 ++ bod.toLoc))) } } } case _ => exprOrIf(0, allowSpace = false) } - cur match { - case (NEWLINE, _) :: _ => consume; t :: block - case _ => t :: Nil + val finalTerm = yeetSpaces match { + case (KEYWORD("="), l0) :: _ => t match { + case R(v: Var) => + consume + R(Eqn(v, expr(0))) + case _ => t + } + case _ => t + } + yeetSpaces match { + case (SEMI, _) :: _ => consume; finalTerm :: block + case (NEWLINE, _) :: _ => consume; finalTerm :: block + case _ => finalTerm :: Nil } } private def yeetSpaces: Ls[TokLoc] = - cur.dropWhile(_._1 === SPACE && { consume; true }) + cur.dropWhile(tkloc => + (tkloc._1 === SPACE + || tkloc._1.isInstanceOf[COMMENT] // TODO properly retrieve and sotre all comments in AST? + ) && { consume; true }) - def funParams(implicit et: ExpectThen, fe: FoundErr, l: Line): Ls[Tup] = wrap(()) { l => + final def funParams(implicit et: ExpectThen, fe: FoundErr, l: Line): Ls[Tup] = wrap(()) { l => yeetSpaces match { case (KEYWORD("=" | ":"), _) :: _ => Nil - case Nil => Nil case (KEYWORD("of"), _) :: _ => consume Tup(args(false) // TODO @@ -385,22 +583,37 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D msg"Expected function parameter list; found ${tk.describe} instead" -> S(l0) :: Nil)) consume Nil + case Nil => Nil } } - def expr(prec: Int, allowSpace: Bool = true)(implicit fe: FoundErr, l: Line): Term = wrap(prec,allowSpace) { l => + private def unexpectedThenElse(loc: Opt[Loc]) = { + err(msg"Expected an expression; found a 'then'/'else' clause instead" -> loc :: Nil) + errExpr + } + + final def expr(prec: Int, allowSpace: Bool = true)(implicit fe: FoundErr, l: Line): Term = wrap(prec,allowSpace) { l => exprOrIf(prec, allowSpace)(et = false, fe = fe, l = implicitly) match { case R(e) => e - case L(e) => - err(msg"Expected an expression; found a 'then'/'else' clause instead" -> e.toLoc :: Nil) - errExpr + case L(e) => unexpectedThenElse(e.toLoc) } } + def exprOrBlockContinuation(implicit et: ExpectThen, fe: FoundErr, l: Line): Term = + yeetSpaces match { + case (NEWLINE, l0) :: _ => + consume + val stmts = block + val es = stmts.map { case L(t) => unexpectedThenElse(t.toLoc); case R(e) => e } + Blk(es) + case _ => expr(0) + } + private def warnDbg(msg: Any, loco: Opt[Loc] = curLoc): Unit = - raise(WarningReport(msg"[${cur.headOption.map(_._1).mkString}] ${""+msg}" -> loco :: Nil)) + raise(WarningReport(msg"[${cur.headOption.map(_._1).mkString}] ${""+msg}" -> loco :: Nil, + newDefs = true)) - def exprOrIf(prec: Int, allowSpace: Bool = true)(implicit et: ExpectThen, fe: FoundErr, l: Line): IfBody \/ Term = wrap(prec, allowSpace) { l => + final def exprOrIf(prec: Int, allowSpace: Bool = true)(implicit et: ExpectThen, fe: FoundErr, l: Line): IfBody \/ Term = wrap(prec, allowSpace) { l => cur match { case (SPACE, l0) :: _ if allowSpace => // Q: do we really need the `allowSpace` flag? consume @@ -416,46 +629,101 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D case (LITVAL(lit), l0) :: _ => consume exprCont(lit.withLoc(S(l0)), prec, allowNewlines = false) + case (KEYWORD(kwStr @ ("undefined" | "null")), l0) :: _ => + consume + exprCont(UnitLit(kwStr === "undefined").withLoc(S(l0)), prec, allowNewlines = false) case (IDENT(nme, false), l0) :: _ => consume exprCont(Var(nme).withLoc(S(l0)), prec, allowNewlines = false) + case (KEYWORD("super"), l0) :: _ => + consume + exprCont(Super().withLoc(S(l0)), prec, allowNewlines = false) + case (IDENT("~", _), l0) :: _ => + consume + val rest = expr(prec, allowSpace = true) + exprCont(App(Var("~").withLoc(S(l0)), rest).withLoc(S(l0 ++ rest.toLoc)), prec, allowNewlines = false) + case (BRACKETS(Round, (IDENT(opStr, true), l1) :: Nil), l0) :: _ => + consume + exprCont(Var(opStr).withLoc(S(l1)), prec, allowNewlines = false) case (br @ BRACKETS(bk @ (Round | Square | Curly), toks), loc) :: _ => consume - val res = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.argsMaybeIndented()) // TODO - val bra = if (bk === Curly) Bra(true, Rcd(res.map { - case S(n) -> fld => n -> fld - case N -> (fld @ Fld(_, _, v: Var)) => v -> fld - case N -> fld => - err(( - msg"Record field should have a name" -> fld.value.toLoc :: Nil)) - Var("") -> fld - })) - else Bra(false, Tup(res)) + val res = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.argsMaybeIndented()) + val bra = (bk, res) match { + case (Curly, _) => + Bra(true, Rcd(res.map { + case S(n) -> fld => n -> fld + case N -> (fld @ Fld(_, v: Var)) => v -> fld + case N -> fld => + err(( + msg"Record field should have a name" -> fld.value.toLoc :: Nil)) + Var("") -> fld + })) + case (Round, (N, Fld(FldFlags(false, false, _), elt)) :: Nil) => + Bra(false, elt) + case (Round, _) => + yeetSpaces match { + case (KEYWORD("=>"), l1) :: _ => + consume + val e = expr(NewParser.opPrec("=>")._2) + Lam(Tup(res), e) + case (IDENT("->", true), l1) :: _ => + consume + val rhs = expr(opPrec("->")._2) + Lam(Tup(res), rhs) + case _ => + res match { + case Nil => + UnitLit(true) + case _ => + res.map { + case N -> Fld(FldFlags.empty, t) => t + case no -> Fld(_, t) => + err((msg"Illegal position for field specification" -> Loc(no.toList :+ t) :: Nil)) + t + }.reduceRight((t, acc) => + App(Var(",").withLoc(Loc(t :: acc :: Nil)), PlainTup(t, acc))) + } + } + case _ => + Tup(res) + } exprCont(bra.withLoc(S(loc)), prec, allowNewlines = false) case (KEYWORD("forall"), l0) :: _ => consume - val as = argsMaybeIndented() - skip(KEYWORD(";")) - val e = expr(0) - R(Forall(as.flatMap { - case N -> Fld(false, false, v: Var) => - TypeVar(R(v.name), N).withLocOf(v) :: Nil - case v -> f => - err(msg"illegal `forall` quantifiee" -> f.value.toLoc :: Nil) - Nil - }, e)) + def getIdents: Ls[TypeVar] = yeetSpaces match { + case (IDENT(nme, false), l0) :: _ => + consume + val res = TypeVar(R(nme), N).withLoc(S(l0)) + yeetSpaces match { + case (COMMA, _) :: _ => + consume + res :: getIdents + case _ => res :: Nil + } + case _ => Nil + } + val idents = getIdents + val rest = cur match { + case (KEYWORD(":"), l0) :: _ => + consume + expr(0) + case _ => + err((msg"Expected `:` after `forall` section" -> curLoc.orElse(lastLoc) :: Nil)) + errExpr + } + R(Forall(idents, rest)) case (KEYWORD("let"), l0) :: _ => consume val bs = bindings(Nil) val body = yeetSpaces match { - case (KEYWORD("in") | KEYWORD(";"), _) :: _ => + case (KEYWORD("in") | SEMI, _) :: _ => consume exprOrIf(0) case (NEWLINE, _) :: _ => consume exprOrIf(0) case _ => - R(UnitLit(true)) + R(UnitLit(true).withLoc(curLoc.map(_.left))) } bs.foldRight(body) { case ((v, r), R(acc)) => R(Let(false, v, r, acc)) @@ -463,26 +731,29 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D } case (KEYWORD("new"), l0) :: c => consume - val body = expr(0) - val head = body match { - case Var(clsNme) => - S(TypeName(clsNme).withLocOf(body) -> Tup(Nil)) - case App(Var(clsNme), Tup(N -> Fld(false, false, UnitLit(true)) :: Nil)) => - S(TypeName(clsNme).withLocOf(body) -> Tup(Nil)) - case App(Var(clsNme), arg) => - S(TypeName(clsNme).withLocOf(body) -> arg) - case UnitLit(true) => - N + val body = expr(NewParser.prec('.')) + exprCont(NuNew(body).withLoc(S(l0 ++ body.toLoc)), prec, allowNewlines = false) + case (KEYWORD("else"), l0) :: _ => + consume + yeetSpaces match { + case (NEWLINE, l0) :: _ => + consume + ??? // TODO case _ => - err(( - msg"Unexpected ${body.describe} after `new` keyword" -> body.toLoc :: Nil)) - N + val e = expr(0) + L(IfElse(e).withLoc(S(l0 ++ e.toLoc))) } - R(New(head, curlyTypingUnit).withLoc(S(head.foldLeft(l0)((l, h) => l ++ h._1.toLoc ++ h._2.toLoc)))) - case (KEYWORD("else"), l0) :: _ => + case (KEYWORD("case"), l0) :: _ => consume - val e = expr(0) - L(IfElse(e).withLoc(S(l0 ++ e.toLoc))) + exprOrIf(0)(et = true, fe = fe, l = implicitly) match { + case L(body) => + R(Lam(PlainTup(Var("case$scrut")), If(IfOpApp(Var("case$scrut"), Var("is"), body), N))) + case R(rhs) => + err((msg"Expected 'then'/'else' clause after 'case'; found ${rhs.describe} instead" -> rhs.toLoc :: + msg"Note: 'case' expression starts here:" -> S(l0) :: Nil)) + R(Lam(PlainTup(Var("case$scrut")), If(IfElse(rhs), N))) + } + case (KEYWORD("if"), l0) :: _ => consume cur match { @@ -492,7 +763,7 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D val els = yeetSpaces match { case (KEYWORD("else"), _) :: _ => consume - S(expr(0)) + S(exprOrBlockContinuation) case (NEWLINE, _) :: (KEYWORD("else"), _) :: _ => consume consume @@ -520,7 +791,7 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D // S(thn, S(nested.concludeWith(_.expr(0)))) S(nested.concludeWith(_.expr(0))) case _ => - nested.concludeWith(_.nil) + nested.concludeWith(_ => ()) // S(thn, N) N } @@ -531,8 +802,8 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D S(e.toLoc.foldRight(l1)(_ ++ _))) case Nil => (msg"${e.describe}", e.toLoc) } - err((msg"Expected 'then'/'else' clause; found $found instead" -> loc :: - msg"Note: 'if' expression started here:" -> S(l0) :: Nil)) + err((msg"Expected 'then'/'else' clause after 'if'; found $found instead" -> loc :: + msg"Note: 'if' expression starts here:" -> S(l0) :: Nil)) R(If(IfThen(e, errExpr), N)) } } @@ -541,9 +812,21 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D case Nil => err(msg"Unexpected end of $description; an expression was expected here" -> lastLoc :: Nil) R(errExpr) - case ((KEYWORD(";") /* | NEWLINE */ /* | BRACKETS(Curly, _) */, _) :: _) => - R(UnitLit(true)) + case ((SEMI /* | NEWLINE */ /* | BRACKETS(Curly, _) */, l0) :: _) => + R(UnitLit(true).withLoc(S(l0))) // R(errExpr) // TODO + case (IDENT("-", true), l0) :: _ /*if opPrec("-")._1 > prec*/ => // Unary minus + consume + val v = Var("-").withLoc(S(l0)) + expr(opPrec("-")._2) match { + case IntLit(i) => // Special case for negative literals + exprCont(IntLit(-i), prec, false) + case rhs: Term => // General case + exprCont( + if (newDefs) App(v, PlainTup(IntLit(BigInt(0)), rhs)) + else App(App(v, PlainTup(IntLit(BigInt(0)))), PlainTup(rhs)) + , prec, false) + } case (tk, l0) :: _ => err(msg"Unexpected ${tk.describe} in expression position" -> S(l0) :: Nil) consume @@ -554,33 +837,67 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D // Tup(Nil).withLoc(lastLoc) // TODO FIXME produce error term instead UnitLit(true).withLoc(lastLoc) // TODO FIXME produce error term instead - def exprCont(acc: Term, prec: Int, allowNewlines: Bool)(implicit et: ExpectThen, fe: FoundErr, l: Line): IfBody \/ Term = wrap(prec, s"`$acc`", allowNewlines) { l => + final def exprCont(acc: Term, prec: Int, allowNewlines: Bool)(implicit et: ExpectThen, fe: FoundErr, l: Line): IfBody \/ Term = wrap(prec, s"`$acc`", allowNewlines) { l => cur match { + case (COMMA, l0) :: _ if prec === 0 => + consume + yeetSpaces match { + case (NEWLINE, _) :: _ => consume + case _ => + } + val rhs = expr(prec) // TODO support exprOrIf for comma operators + R(App(Var(",").withLoc(S(l0)), PlainTup(acc, rhs))) + case (KEYWORD(opStr @ "=>"), l0) :: (NEWLINE, l1) :: _ if opPrec(opStr)._1 > prec => + consume + val rhs = Blk(typingUnit.entities) + R(Lam(PlainTup(acc), rhs)) + case (KEYWORD(opStr @ "=>"), l0) :: _ if opPrec(opStr)._1 > prec => + consume + val rhs = expr(1) + val res = Lam(PlainTup(acc), rhs) + exprCont(res, prec, allowNewlines) + case (IDENT(".", _), l0) :: (br @ BRACKETS(Square, toks), l1) :: _ => + consume + consume + val idx = rec(toks, S(br.innerLoc), br.describe) + .concludeWith(_.expr(0, allowSpace = true)) + val newAcc = Subs(acc, idx).withLoc(S(l0 ++ l1 ++ idx.toLoc)) + exprCont(newAcc, prec, allowNewlines) case (IDENT(opStr, true), l0) :: _ if /* isInfix(opStr) && */ opPrec(opStr)._1 > prec => consume val v = Var(opStr).withLoc(S(l0)) - // printDbg(s">>> $opStr ${opPrec(opStr)}") + yeetSpaces match { + case (NEWLINE, l0) :: _ => consume + case _ => + } exprOrIf(opPrec(opStr)._2) match { case L(rhs) => L(IfOpApp(acc, v, rhs)) case R(rhs) => exprCont(opStr match { - case "=>" => - Lam(toParams(acc), rhs) + case "with" => + rhs match { + case rhs: Rcd => + With(acc, rhs)//.withLocOf(term) + case Bra(true, rhs: Rcd) => + With(acc, rhs)//.withLocOf(term) + case _ => + err(msg"record literal expected here; found ${rhs.describe}" -> rhs.toLoc :: Nil) + acc + } case _ => - App(App(v, toParams(acc)), toParams(rhs)) + if (newDefs) App(v, PlainTup(acc, rhs)) + else App(App(v, PlainTup(acc)), PlainTup(rhs)) }, prec, allowNewlines) } - case (KEYWORD(":"), l0) :: _ => + case (KEYWORD(":"), l0) :: _ if prec <= NewParser.prec(':') => consume R(Asc(acc, typ(0))) - // case (KEYWORD(":"), _) :: _ if prec <= 1 => - // consume - // R(Asc(acc, typ(1))) case (KEYWORD("where"), l0) :: _ if prec <= 1 => consume val tu = typingUnitMaybeIndented - R(Where(acc, tu.entities)) + val res = Where(acc, tu.entities).withLoc(S(l0)) + exprCont(res, prec, allowNewlines = false) case (SPACE, l0) :: _ => consume exprCont(acc, prec, allowNewlines) @@ -599,21 +916,24 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D case (br @ BRACKETS(Indent, (IDENT(opStr, true), l0) :: toks), _) :: _ => consume rec(toks, S(br.innerLoc), br.describe).concludeWith(_.opBlock(acc, opStr, l0)) - case Nil => R(acc) case (KEYWORD("then"), _) :: _ if /* expectThen && */ prec === 0 => // case (KEYWORD("then"), _) :: _ if /* expectThen && */ prec <= 1 => consume - val e = expr(0) - L(IfThen(acc, e)) + L(IfThen(acc, exprOrBlockContinuation)) case (NEWLINE, _) :: (KEYWORD("then"), _) :: _ if /* expectThen && */ prec === 0 => consume consume - val e = expr(0) - L(IfThen(acc, e)) + L(IfThen(acc, exprOrBlockContinuation)) case (NEWLINE, _) :: _ if allowNewlines => consume exprCont(acc, 0, allowNewlines) - case (COMMA | NEWLINE | KEYWORD("then" | "else" | "in" | ";" | "=") + + case (br @ BRACKETS(Curly, toks), loc) :: _ if prec <= AppPrec => + consume + val tu = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.typingUnitMaybeIndented).withLoc(S(loc)) + exprCont(Rft(acc, tu), prec, allowNewlines) + + case (COMMA | SEMI | NEWLINE | KEYWORD("then" | "else" | "in" | "=") | IDENT(_, true) | BRACKETS(Curly, _), _) :: _ => R(acc) case (KEYWORD("of"), _) :: _ if prec <= 1 => @@ -658,42 +978,42 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D case R(res) => exprCont(res, 0, allowNewlines) } - case (br @ BRACKETS(Angle, toks), loc) :: _ => + case (br @ BRACKETS(Angle | Square, toks), loc) :: _ => consume val as = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.argsMaybeIndented()) // val res = TyApp(acc, as.map(_.mapSecond.to)) val res = TyApp(acc, as.map { - case (N, Fld(false, false, trm)) => trm.toType match { + case (N, Fld(FldFlags(false, false, _), trm)) => trm.toType match { case L(d) => raise(d); Top // TODO better case R(ty) => ty } case _ => ??? - }) + }).withLoc(acc.toLoc.fold(some(loc))(_ ++ loc |> some)) exprCont(res, prec, allowNewlines) - case (br @ BRACKETS(Square, toks), loc) :: _ => + /*case (br @ BRACKETS(Square, toks), loc) :: _ => // * Currently unreachable because we match Square brackets as tparams consume val idx = rec(toks, S(br.innerLoc), "subscript").concludeWith(_.expr(0)) val res = Subs(acc, idx.withLoc(S(loc))) + exprCont(res, prec, allowNewlines)*/ + + case (br @ BRACKETS(Round, toks), loc) :: _ if prec <= AppPrec => + consume + val as = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.argsMaybeIndented()) + val res = App(acc, Tup(as).withLoc(S(loc))) + exprCont(res, prec, allowNewlines) + + case (KEYWORD("of"), _) :: _ => + consume + val as = argsMaybeIndented() + // val as = argsOrIf(Nil) // TODO + val res = App(acc, Tup(as)) exprCont(res, prec, allowNewlines) - case (br @ BRACKETS(Round, toks), loc) :: _ => - consume - val as = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.argsMaybeIndented()) - val res = App(acc, Tup(as).withLoc(S(loc))) - exprCont(res, prec, allowNewlines) - - case (KEYWORD("of"), _) :: _ => - consume - val as = argsMaybeIndented() - // val as = argsOrIf(Nil) // TODO - val res = App(acc, Tup(as)) - exprCont(res, prec, allowNewlines) - case c @ (h :: _) if (h._1 match { - case KEYWORD(";" | "of" | "where") | BRACKETS(Round | Square, _) + case KEYWORD(":" | "of" | "where" | "extends") | SEMI | BRACKETS(Round | Square, _) | BRACKETS(Indent, ( - KEYWORD(";" | "of") + KEYWORD("of") | SEMI | BRACKETS(Round | Square, _) | SELECT(_) , _) :: _) @@ -703,20 +1023,20 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D val as = argsMaybeIndented() val res = App(acc, Tup(as)) raise(WarningReport(msg"Paren-less applications should use the 'of' keyword" - -> res.toLoc :: Nil)) + -> res.toLoc :: Nil, newDefs = true)) exprCont(res, prec, allowNewlines) case _ => R(acc) } } - def opBlock(acc: Term, opStr: Str, opLoc: Loc)(implicit et: ExpectThen, fe: FoundErr, l: Line): IfBody \/ Term = wrap(s"`$acc`", opStr) { l => + final def opBlock(acc: Term, opStr: Str, opLoc: Loc)(implicit et: ExpectThen, fe: FoundErr, l: Line): IfBody \/ Term = wrap(s"`$acc`", opStr) { l => val opv = Var(opStr).withLoc(S(opLoc)) val rhs = exprOrIf(0) // val rhs = exprOrIf(1) rhs match { case R(rhs) => - val res = App(App(opv, acc), rhs) + val res = App(opv, PlainTup(acc, rhs)) cur match { case (NEWLINE, _) :: c => // TODO allow let bindings... consume @@ -738,7 +1058,7 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D L(IfOpsApp(acc, opIfBlock(opv -> rhs :: Nil))) } } - 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] = wrap(acc) { l => cur match { case (NEWLINE, _) :: c => // TODO allow let bindings... consume @@ -761,32 +1081,81 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D } } - def tyArgs(implicit fe: FoundErr, et: ExpectThen): Opt[Ls[Type]] = + final def tyArgs(implicit fe: FoundErr, et: ExpectThen): Opt[Ls[Type]] = cur match { case (IDENT("<", true), l0) :: _ => ??? case _ => ??? } - def argsMaybeIndented()(implicit fe: FoundErr, et: ExpectThen): Ls[Opt[Var] -> Fld] = + // TODO support line-broken param lists; share logic with args/argsOrIf + def typeParams(implicit fe: FoundErr, et: ExpectThen): Ls[(Opt[VarianceInfo], TypeName)] = { + val vinfo = yeetSpaces match { + case (KEYWORD("in"), l0) :: (KEYWORD("out"), l1) :: _ => + consume + S(VarianceInfo.in, l0 ++ l1) + case (KEYWORD("in"), l0) :: _ => + consume + S(VarianceInfo.contra, l0) + case (KEYWORD("out"), l0) :: _ => + consume + S(VarianceInfo.co, l0) + case _ => N + } + yeetSpaces match { + case (IDENT(nme, false), l0) :: _ => + consume + val tyNme = TypeName(nme).withLoc(S(l0)) + yeetSpaces match { + case (COMMA, l0) :: _ => + consume + vinfo.map(_._1) -> tyNme :: typeParams + case _ => + vinfo.map(_._1) -> tyNme :: Nil + } + case _ => + vinfo match { + case S((_, loc)) => + err(msg"dangling variance information" -> S(loc) :: Nil) + case N => + } + Nil + } + } + + + final def maybeIndented[R](f: (NewParser, Bool) => R)(implicit fe: FoundErr, et: ExpectThen): R = cur match { case (br @ BRACKETS(Indent, toks), _) :: _ if (toks.headOption match { case S((KEYWORD("then" | "else"), _)) => false case _ => true }) => consume - rec(toks, S(br.innerLoc), br.describe).concludeWith(_.args(true)) - case _ => args(false) + rec(toks, S(br.innerLoc), br.describe).concludeWith(f(_, true)) + case _ => f(this, false) } + final def argsMaybeIndented()(implicit fe: FoundErr, et: ExpectThen): Ls[Opt[Var] -> Fld] = + maybeIndented(_.args(_)) + // final def argsMaybeIndented()(implicit fe: FoundErr, et: ExpectThen): Ls[Opt[Var] -> Fld] = + // cur match { + // case (br @ BRACKETS(Indent, toks), _) :: _ if (toks.headOption match { + // case S((KEYWORD("then" | "else"), _)) => false + // case _ => true + // }) => + // consume + // rec(toks, S(br.innerLoc), br.describe).concludeWith(_.args(true)) + // case _ => args(false) + // } + // TODO support comma-less arg blocks...? - def args(allowNewlines: Bool, prec: Int = NoElsePrec)(implicit fe: FoundErr, et: ExpectThen): Ls[Opt[Var] -> Fld] = + final def args(allowNewlines: Bool, prec: Int = NoElsePrec)(implicit fe: FoundErr, et: ExpectThen): Ls[Opt[Var] -> Fld] = // argsOrIf(Nil).map{case (_, L(x))=> ???; case (n, R(x))=>n->x} // TODO argsOrIf(Nil, Nil, allowNewlines, prec).flatMap{case (n, L(x))=> err(msg"Unexpected 'then'/'else' clause" -> x.toLoc :: Nil) - n->Fld(false, false, errExpr)::Nil + n->Fld(FldFlags.empty, errExpr)::Nil case (n, R(x))=>n->x::Nil} // TODO /* - def argsOrIf2()(implicit fe: FoundErr, et: ExpectThen): IfBlock \/ Ls[Opt[Var] -> Fld] = { + final def argsOrIf2()(implicit fe: FoundErr, et: ExpectThen): IfBlock \/ Ls[Opt[Var] -> Fld] = { // argsOrIf(Nil).partitionMap(identity).mapFirst(ifbods => ???) argsOrIf(Nil) match { case n -> L(ib) => @@ -796,7 +1165,7 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D } } */ - def argsOrIf(acc: Ls[Opt[Var] -> (IfBody \/ Fld)], seqAcc: Ls[Statement], allowNewlines: Bool, prec: Int = NoElsePrec) + 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 => @@ -804,20 +1173,29 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D case Nil => seqAcc match { case res :: seqAcc => - (N -> R(Fld(false, false, Blk((res :: seqAcc).reverse))) :: acc).reverse + (N -> R(Fld(FldFlags.empty, Blk((res :: seqAcc).reverse))) :: acc).reverse case Nil => acc.reverse } case (SPACE, _) :: _ => consume argsOrIf(acc, seqAcc, allowNewlines, prec) - case (NEWLINE | IDENT(_, true), _) :: _ => // TODO: | ... + case (NEWLINE, _) :: _ => // TODO: | ... + assert(seqAcc.isEmpty) + acc.reverse + case (IDENT(nme, true), _) :: _ if nme =/= "-" => // TODO: | ... assert(seqAcc.isEmpty) acc.reverse case _ => // val blck = block + val argVal = yeetSpaces match { + case (KEYWORD("val"), l0) :: _ => + consume + S(l0) + case _ => N + } val argMut = yeetSpaces match { case (KEYWORD("mut"), l0) :: _ => consume @@ -835,15 +1213,19 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D consume consume S(Var(idStr).withLoc(S(l0))) + case (LITVAL(IntLit(i)), l0) :: (KEYWORD(":"), _) :: _ => // TODO: | ... + consume + consume + S(Var(i.toString).withLoc(S(l0))) case _ => N } // val e = expr(NoElsePrec) -> argMut.isDefined - val e = exprOrIf(prec).map(Fld(argMut.isDefined, argSpec.isDefined, _)) + val e = exprOrIf(prec).map(Fld(FldFlags(argMut.isDefined, argSpec.isDefined, argVal.isDefined), _)) def mkSeq = if (seqAcc.isEmpty) argName -> e else e match { case L(_) => ??? - case R(Fld(m, s, res)) => - argName -> R(Fld(m, s, Blk((res :: seqAcc).reverse))) + case R(Fld(flags, res)) => + argName -> R(Fld(flags, Blk((res :: seqAcc).reverse))) } cur match { @@ -863,7 +1245,7 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D } e match { case L(_) => ??? - case R(Fld(false, false, res)) => + case R(Fld(FldFlags(false, false, _), res)) => argsOrIf(acc, res :: seqAcc, allowNewlines) case R(_) => ??? } @@ -874,12 +1256,12 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D } } - def bindings(acc: Ls[Var -> Term])(implicit fe: FoundErr): Ls[Var -> Term] = + final def bindings(acc: Ls[Var -> Term])(implicit fe: FoundErr): Ls[Var -> Term] = cur match { case (SPACE, _) :: _ => consume bindings(acc) - case (NEWLINE | IDENT(_, true) | KEYWORD(";"), _) :: _ => // TODO: | ... + case (NEWLINE | IDENT(_, true) | SEMI, _) :: _ => // TODO: | ... acc.reverse case (IDENT(id, false), l0) :: _ => consume @@ -902,7 +1284,7 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D Nil } - def mkType(trm: Term): Type = trm.toType match { + final def mkType(trm: Term): Type = trm.toType match { case L(d) => raise(d); Top // TODO better case R(ty) => ty } diff --git a/shared/src/main/scala/mlscript/NormalForms.scala b/shared/src/main/scala/mlscript/NormalForms.scala index d96083b8d3..eecf6dd7ed 100644 --- a/shared/src/main/scala/mlscript/NormalForms.scala +++ b/shared/src/main/scala/mlscript/NormalForms.scala @@ -45,7 +45,13 @@ class NormalForms extends TyperDatatypes { self: Typer => } lazy val underlying: SimpleType = mkType(false) def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = - underlying.levelBelow(ub) // TODO avoid forcing `underlying`! + this match { + case LhsRefined(base, ttags, reft, trefs) => + (base.iterator.map(_.levelBelow(ub)) ++ ttags.iterator.map(_.levelBelow(ub)) ++ + reft.fields.iterator.values.map(_.levelBelow(ub)) ++ trefs.iterator.values.map(_.levelBelow(ub)) + ).reduceOption(_ max _).getOrElse(MinLevel) + case LhsTop => MinLevel + } def freshenAbove(lim: Int, rigidify: Bool) (implicit ctx: Ctx, freshened: MutMap[TV, ST]): LhsNf = this match { case LhsRefined(bo, ts, r, trs) => @@ -200,7 +206,7 @@ class NormalForms extends TyperDatatypes { self: Typer => TypeRef(that.defn, newTargs)(that.prov) }) val res = LhsRefined(b, ts, rt, trs2) - that.mkTag.fold(S(res): Opt[LhsNf])(res & (_, pol)) + that.mkClsTag.fold(S(res): Opt[LhsNf])(res & (_, pol)) } def & (that: LhsNf, pol: Bool)(implicit ctx: Ctx, etf: ExpandTupleFields): Opt[LhsNf] = (this, that) match { case (_, LhsTop) => S(this) @@ -210,6 +216,26 @@ class NormalForms extends TyperDatatypes { self: Typer => trs.valuesIterator.foldLeft((bo.fold(some(this & rt))(this & rt & (_, pol))))(_.getOrElse(return N) & (_, pol)) )(_.getOrElse(return N) & (_, pol)) } + def <:< (that: RhsNf)(implicit ctx: Ctx): Bool = (this, that) match { + case (LhsRefined(_, _, reft, trs), RhsField(nme, ft)) => reft <:< RecordType(nme -> ft :: Nil)(noProv) + case (lhs @ LhsRefined(bse, tts, reft, trs), RhsBases(tags, rest, trefs)) => + tags.exists(tag => + bse match { + case S(cls: ClassTag) => tag.id === cls.id || lhs.allTags.contains(tag.id) + case _ => false + } + ) || (rest match { + case S(R(f: RhsField)) => this <:< f + case S(L(rhs @ (_: FunctionType | _: Overload | _: ArrayBase | _: TupleType))) => + bse.exists(_ <:< rhs) + case S(L(wt: Without)) => return underlying <:< that.underlying + case N => false + }) || trefs.exists { case (_, tr) => + underlying <:< tr + } + case (LhsTop, _) => false + case (_, RhsBot) => false + } def <:< (that: LhsNf)(implicit ctx: Ctx): Bool = (this, that) match { case (_, LhsTop) => true case (LhsTop, _) => false @@ -222,6 +248,13 @@ class NormalForms extends TyperDatatypes { self: Typer => } case class LhsRefined(base: Opt[BaseType], ttags: SortedSet[AbstractTag], reft: RecordType, trefs: SortedMap[TypeName, TypeRef]) extends LhsNf { // assert(!trefs.exists(primitiveTypes contains _._1.name)) + lazy val allTags: Set[IdentifiedTerm] = ttags.iterator.foldLeft(base match { + case S(cls: ClassTag) => cls.parentsST + cls.id + case _ => Set.empty[IdentifiedTerm] + }) { + case (acc, tt: TraitTag) => acc ++ tt.parentsST + tt.id + case (acc, _) => acc + } override def toString: Str = s"${base.getOrElse("")}${reft}${ (ttags.iterator ++ trefs.valuesIterator).map("∧"+_).mkString}" } @@ -366,7 +399,28 @@ class NormalForms extends TyperDatatypes { self: Typer => S(RhsBases(p, S(R(RhsField(n1, t1 || that._2))), trs)) case _: RhsField | _: RhsBases => N } - def <:< (that: RhsNf)(implicit ctx: Ctx): Bool = (this.toType() <:< that.toType()) // TODO less inefficient! (uncached calls to toType) + // def <:< (that: RhsNf)(implicit ctx: Ctx): Bool = this.underlying <:< that.underlying + def <:< (that: RhsNf)(implicit ctx: Ctx): Bool = (this, that) match { + case (RhsBases(tags, S(R(fld)), trs), fld2: RhsField) => + tags.isEmpty && trs.valuesIterator.forall(_ <:< fld2.underlying) && fld <:< fld2 + case (RhsBases(tags, _, trs), fld2: RhsField) => false + case (RhsField(nme, fty), RhsBases(tags2, S(R(fld)), trs2)) => this <:< fld + case (RhsField(nme, fty), RhsBases(tags2, S(L(_)) | N, trs2)) => false + case (RhsField(nme, fty), RhsField(nme2, fty2)) => nme === nme2 && fty <:< fty2 + case (RhsBases(tags1, res1, trs1), rhs @ RhsBases(tags2, res2, trs2)) => + tags1.forall(tag1 => + tags2.exists(_.id === tag1.id) // TODO also take parents into account... + ) && trs1.forall { case (_, tr1) => + // tr1 <:< rhs.underlying // * probably not necessary + trs2.exists { case (_, tr2) => tr1 <:< tr2 } + } && ((res1, res2) match { + case (S(L(b)), S(L(b2))) => b <:< b2 + case (N, _) => true + case _ => false + }) + case (RhsBot, _) => true + case (_, RhsBot) => false + } def isBot: Bool = isInstanceOf[RhsBot.type] } case class RhsField(name: Var, ty: FieldType) extends RhsNf { @@ -391,7 +445,7 @@ class NormalForms extends TyperDatatypes { self: Typer => case class Conjunct(lnf: LhsNf, vars: SortedSet[TypeVariable], rnf: RhsNf, nvars: SortedSet[TypeVariable]) extends Ordered[Conjunct] { - def compare(that: Conjunct): Int = this.toString compare that.toString // TODO less inefficient!! + def compare(that: Conjunct): Int = this.mkString compare that.mkString // TODO less inefficient!! def toType(sort: Bool = false): SimpleType = toTypeWith(_.toType(sort), _.toType(sort), sort) def toTypeWith(f: LhsNf => SimpleType, g: RhsNf => SimpleType, sort: Bool = false): SimpleType = @@ -402,9 +456,9 @@ class NormalForms extends TyperDatatypes { self: Typer => (vars.iterator ++ nvars).map(_.levelBelow(ub)).++(Iterator(lnf.levelBelow(ub), rnf.levelBelow(ub))).max def freshenAbove(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): Conjunct = { val (vars2, tags2) = vars.toBuffer[TV].partitionMap( - _.freshenAbove(lim, rigidify) match { case tv: TV => L(tv); case tt: AbstractTag => R(tt) }) + _.freshenAbove(lim, rigidify) match { case tv: TV => L(tv); case tt: AbstractTag => R(tt); case _ => die }) val (nvars2, ntags2) = nvars.toBuffer[TV].partitionMap( - _.freshenAbove(lim, rigidify) match { case tv: TV => L(tv); case tt: AbstractTag => R(tt) }) + _.freshenAbove(lim, rigidify) match { case tv: TV => L(tv); case tt: AbstractTag => R(tt); case _ => die }) Conjunct( tags2.foldLeft(lnf.freshenAbove(lim, rigidify))(_ & _), vars2.toSortedSet, ntags2.foldLeft(rnf.freshenAbove(lim, rigidify))(_ | _), nvars2.toSortedSet) @@ -431,7 +485,7 @@ class NormalForms extends TyperDatatypes { self: Typer => // }(r => s"!! $r") def & (that: Conjunct, pol: Bool)(implicit ctx: Ctx, etf: ExpandTupleFields): Opt[Conjunct] = // trace(s"?? $this & $that ${lnf & that.lnf} ${rnf | that.rnf}") { - if ((lnf.toType() <:< that.rnf.toType())) N // TODO support <:< on any Nf? // TODO less inefficient! (uncached calls to toType) + if (lnf <:< that.rnf) N else S(Conjunct.mk(lnf & (that.lnf, pol) getOrElse (return N), vars | that.vars , rnf | (that.rnf, pol) getOrElse (return N) , nvars | that.nvars, pol)) @@ -503,9 +557,10 @@ class NormalForms extends TyperDatatypes { self: Typer => } case _ => N } - override def toString: Str = + private lazy val mkString: Str = (Iterator(lnf).filter(_ =/= LhsTop) ++ vars ++ (Iterator(rnf).filter(_ =/= RhsBot) ++ nvars).map("~("+_+")")).mkString("∧") + override def toString: Str = mkString } object Conjunct { @@ -665,7 +720,7 @@ class NormalForms extends TyperDatatypes { self: Typer => if (dir) l | r else l & (r, pol) def mkDeep(polymLvl: Level, cons: Constrs, ty: SimpleType, pol: Bool) - (implicit ctx: Ctx, ptr: PreserveTypeRefs = false, etf: ExpandTupleFields = true): DNF = { + (implicit ctx: Ctx, ptr: PreserveTypeRefs = false, etf: ExpandTupleFields = true, expandedTVs: Set[TV] = Set.empty): DNF = { mk(polymLvl, cons, mkDeepST(polymLvl, cons, ty, pol), pol) } def mkDeepST(polymLvl: Level, cons: Constrs, ty: SimpleType, pol: Bool) @@ -688,7 +743,7 @@ class NormalForms extends TyperDatatypes { self: Typer => } // }(r => s"= $r") - def mk(polymLvl: Level, cons: Constrs, ty: SimpleType, pol: Bool)(implicit ctx: Ctx, ptr: PreserveTypeRefs = false, etf: ExpandTupleFields = true): DNF = + def mk(polymLvl: Level, cons: Constrs, ty: SimpleType, pol: Bool)(implicit ctx: Ctx, ptr: PreserveTypeRefs = false, etf: ExpandTupleFields = true, expandedTVs: Set[TV] = Set.empty): DNF = // trace(s"DNF[${printPol(pol)},$ptr,$etf,$polymLvl](${ty})") { (if (pol) ty.pushPosWithout else ty) match { case bt: BaseType => DNF.of(polymLvl, cons, LhsRefined(S(bt), ssEmp, if (expandTupleFields) bt.toRecord else RecordType.empty, smEmp)) @@ -697,13 +752,15 @@ class NormalForms extends TyperDatatypes { self: Typer => case ExtrType(pol) => extr(!pol) case ty @ ComposedType(p, l, r) => merge(p, pol)(mk(polymLvl, cons, l, pol), mk(polymLvl, cons, r, pol)) case NegType(und) => DNF.of(polymLvl, cons, CNF.mk(polymLvl, Nil, und, !pol).ds.map(_.neg)) + case tv @ AssignedVariable(ty) if !preserveTypeRefs && !expandedTVs.contains(tv) => + (expandedTVs + tv) |> { implicit expandedTVs => DNF.mk(polymLvl, cons, ty, pol) } case tv: TypeVariable => DNF.of(polymLvl, cons, Conjunct.of(SortedSet.single(tv)) :: Nil) case ProxyType(underlying) => mk(polymLvl, cons, underlying, pol) case tr @ TypeRef(defn, targs) => // * TODO later: when proper TypeRef-based simplif. is implemented, can remove this special case - if (preserveTypeRefs && !primitiveTypes.contains(defn.name)) { - of(polymLvl, cons, LhsRefined(tr.mkTag, ssEmp, RecordType.empty, SortedMap(defn -> tr))) - } else mk(polymLvl, cons, tr.expand, pol) + if (preserveTypeRefs && !primitiveTypes.contains(defn.name) || !tr.canExpand) { + of(polymLvl, cons, LhsRefined(tr.mkClsTag, ssEmp, RecordType.empty, SortedMap(defn -> tr))) + } else mk(polymLvl, cons, tr.expandOrCrash, pol) case TypeBounds(lb, ub) => mk(polymLvl, cons, if (pol) ub else lb, pol) case PolymorphicType(lvl, bod) => mk(lvl, cons, bod, pol) case ConstrainedType(cs, bod) => mk(polymLvl, cs ::: cons, bod, pol) @@ -731,7 +788,7 @@ class NormalForms extends TyperDatatypes { self: Typer => Disjunct(RhsField(f._1, f._2), ssEmp, LhsTop, ssEmp)).toList) def extr(pol: Bool): CNF = if (pol) CNF(Nil) else of(RhsBot) def merge(pol: Bool)(l: CNF, r: CNF)(implicit ctx: Ctx, etf: ExpandTupleFields): CNF = if (pol) l | (r, pol) else l & r - def mk(polymLvl: Level, cons: Constrs, ty: SimpleType, pol: Bool)(implicit ctx: Ctx, ptr: PreserveTypeRefs, etf: ExpandTupleFields): CNF = + def mk(polymLvl: Level, cons: Constrs, ty: SimpleType, pol: Bool)(implicit ctx: Ctx, ptr: PreserveTypeRefs, etf: ExpandTupleFields, expandedTVs: Set[TV] = Set.empty): CNF = // trace(s"?CNF $ty") { ty match { case bt: BaseType => of(bt) @@ -740,12 +797,14 @@ class NormalForms extends TyperDatatypes { self: Typer => case ExtrType(pol) => extr(!pol) case ty @ ComposedType(p, l, r) => merge(p)(mk(polymLvl, cons, l, pol), mk(polymLvl, cons, r, pol)) case NegType(und) => CNF(DNF.mk(polymLvl, cons, und, !pol).cs.map(_.neg)) + case tv @ AssignedVariable(ty) if !preserveTypeRefs && !expandedTVs.contains(tv) => + (expandedTVs + tv) |> { implicit expandedTVs => CNF.mk(polymLvl, cons, ty, pol) } case tv: TypeVariable => of(SortedSet.single(tv)) case ProxyType(underlying) => mk(polymLvl, cons, underlying, pol) case tr @ TypeRef(defn, targs) => - if (preserveTypeRefs && !primitiveTypes.contains(defn.name)) { + if (preserveTypeRefs && !primitiveTypes.contains(defn.name) || !tr.canExpand) { CNF(Disjunct(RhsBases(Nil, N, SortedMap.single(defn -> tr)), ssEmp, LhsTop, ssEmp) :: Nil) - } else mk(polymLvl, cons, tr.expand, pol) + } else mk(polymLvl, cons, tr.expandOrCrash, pol) case TypeBounds(lb, ub) => mk(polymLvl, cons, if (pol) ub else lb, pol) case PolymorphicType(lvl, bod) => mk(lvl, cons, bod, pol) case ConstrainedType(cs, bod) => mk(lvl, cs ::: cons, bod, pol) diff --git a/shared/src/main/scala/mlscript/NuTypeDefs.scala b/shared/src/main/scala/mlscript/NuTypeDefs.scala index 8e72534c27..08e82484d1 100644 --- a/shared/src/main/scala/mlscript/NuTypeDefs.scala +++ b/shared/src/main/scala/mlscript/NuTypeDefs.scala @@ -12,26 +12,1837 @@ class NuTypeDefs extends ConstraintSolver { self: Typer => import TypeProvenance.{apply => tp} - // * For now these are just unused stubs to be completed and used later + type Params = Ls[Var -> FieldType] + type TyParams = Ls[(TN, TV, Opt[VarianceInfo])] - case class TypedNuTypeDef( - kind: TypeDefKind, - nme: TypeName, - tparamsargs: List[(TypeName, TypeVariable)], - bodyTy: SimpleType, - baseClasses: Set[TypeName], - toLoc: Opt[Loc], - ) - def typeTypingUnit(tu: TypingUnit)(implicit ctx: Ctx, raise: Raise): SimpleType = { - // tu.entities - ??? + sealed abstract class NuDeclInfo + + case class FunInfo() extends NuDeclInfo + case class TypeDefInfo() extends NuDeclInfo + + + sealed trait NuMember { + def name: Str + def kind: DeclKind + def toLoc: Opt[Loc] + def level: Level + def isImplemented: Bool + def isPublic: Bool + def isPrivate: Bool = !isPublic // * We currently don't support `protected` + + def isValueParam: Bool = this match { + case p: NuParam => !p.isType + case _ => false + } + + protected def withLevel[R](k: Ctx => R)(implicit ctx: Ctx): R = k(ctx.copy(lvl = ctx.lvl + 1)) + + def freshenAbove(lim: Int, rigidify: Bool) + (implicit ctx: Ctx, freshened: MutMap[TV, ST]) + : NuMember + + def map(f: ST => ST)(implicit ctx: Ctx): NuMember = + mapPol(N, false)((_, ty) => f(ty)) + + // TODO rm – just use `mapPolMap` + def mapPol(pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType) + (implicit ctx: Ctx): NuMember + + def mapPolMap(pol: PolMap)(f: (PolMap, SimpleType) => SimpleType) + (implicit ctx: Ctx): NuMember + + def showBounds: Str = TypedTypingUnit(this :: Nil, N).showBounds + } + + + case class NuParam(nme: NameRef, ty: FieldType, isPublic: Bool)(val level: Level) + extends NuMember with TypedNuTermDef + { + def name: Str = nme.name + def isType: Bool = nme.isInstanceOf[TypeName] + def kind: DeclKind = + if (isType) Als // FIXME? + else Val + def toLoc: Opt[Loc] = nme.toLoc + def isImplemented: Bool = true + def isVirtual: Bool = false // TODO allow annotating parameters with `virtual` + def typeSignature: ST = ty.ub + + def freshenAbove(lim: Int, rigidify: Bool) + (implicit ctx: Ctx, freshened: MutMap[TV, ST]) + : NuParam = + NuParam(nme, ty.freshenAbove(lim, rigidify), isPublic)(ctx.lvl) + + def mapPol(pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType) + (implicit ctx: Ctx): NuParam = + NuParam(nme, ty.update(t => f(pol.map(!_), t), t => f(pol, t)), isPublic)(level) + def mapPolMap(pol: PolMap)(f: (PolMap, SimpleType) => SimpleType) + (implicit ctx: Ctx): NuParam = + NuParam(nme, ty.update(t => f(pol.contravar, t), t => f(pol, t)), isPublic)(level) } - class TypedTypingUnit(tu: TypingUnit)(implicit ctx: Ctx, raise: Raise) { + + sealed trait TypedNuDecl extends NuMember { + def name: Str + def level: Level + def mapPol(pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType) + (implicit ctx: Ctx): TypedNuDecl + def mapPolMap(pol: PolMap)(f: (PolMap, SimpleType) => SimpleType) + (implicit ctx: Ctx): TypedNuDecl } + // TODO rm? + /** Those declarations that introduce term names in scope. */ + sealed trait TypedNuTermDef extends TypedNuDecl with AnyTypeDef { + def typeSignature: ST + } + + + sealed abstract class TypedNuTypeDef(kind: TypeDefKind) extends TypedNuDecl { + def nme: TypeName + def decl: NuTypeDef + def toLoc: Opt[Loc] = decl.toLoc + def tparams: TyParams + def members: Map[Str, NuMember] + val allFields: Set[Var] = members.valuesIterator.map(_.name |> Var).toSet + val td: NuTypeDef + val prov: TP = TypeProvenance(td.toLoc, td.describe, isType = true) + val level: Level + def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = ??? + } + + + case class TypedNuAls(level: Level, td: NuTypeDef, tparams: TyParams, body: ST) + extends TypedNuTypeDef(Als) + { + def decl: NuTypeDef = td + def kind: DeclKind = td.kind + def name: Str = nme.name + def nme: mlscript.TypeName = td.nme + def members: Map[Str, NuMember] = Map.empty + def isImplemented: Bool = td.sig.isDefined + def isPublic = true // TODO + + def freshenAbove(lim: Int, rigidify: Bool) + (implicit ctx: Ctx, freshened: MutMap[TV,ST]) + : TypedNuAls = { val outer = ctx; withLevel { implicit ctx => + TypedNuAls(outer.lvl, td, + tparams.map(tp => (tp._1, tp._2.freshenAbove(lim, rigidify).assertTV, tp._3)), + body.freshenAbove(lim, rigidify)) + }} + + def mapPol(pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType) + (implicit ctx: Ctx): TypedNuDecl = + TypedNuAls( + level, td, + tparams.map(tp => (tp._1, f(N, tp._2).assertTV, tp._3)), + f(pol, body) + ) + def mapPolMap(pol: PolMap)(f: (PolMap, SimpleType) => SimpleType) + (implicit ctx: Ctx): TypedNuDecl = + TypedNuAls( + level, td, + tparams.map(tp => (tp._1, f(pol.invar, tp._2).assertTV, tp._3)), + f(pol, body) + ) + } + + sealed trait PolyNuDecl extends TypedNuDecl { + def tparams: TyParams + } + + + case class TypedNuTrt( + level: Level, td: NuTypeDef, + tparams: TyParams, + members: Map[Str, NuMember], + thisTy: ST, + sign: ST, + inheritedTags: Set[TypeName], + parentTP: Map[Str, NuMember] + ) extends TypedNuTypeDef(Trt) with PolyNuDecl + { + def decl: NuTypeDef = td + def kind: DeclKind = td.kind + def nme: TypeName = td.nme + def name: Str = nme.name + def isImplemented: Bool = true + def isPublic = true // TODO + + // TODO dedup with the one in TypedNuCls + lazy val virtualMembers: Map[Str, NuMember] = members ++ tparams.map { + case (nme @ TypeName(name), tv, _) => + td.nme.name+"#"+name -> NuParam(nme, FieldType(S(tv), tv)(provTODO), isPublic = true)(level) + } ++ parentTP + + def freshenAbove(lim: Int, rigidify: Bool) + (implicit ctx: Ctx, freshened: MutMap[TV,ST]) + : TypedNuTrt = { val outer = ctx; withLevel { implicit ctx => + TypedNuTrt(outer.lvl, td, + tparams.map(tp => (tp._1, tp._2.freshenAbove(lim, rigidify).assertTV, tp._3)), + members.mapValuesIter(_.freshenAbove(lim, rigidify)).toMap, + thisTy.freshenAbove(lim, rigidify), + sign.freshenAbove(lim, rigidify), + inheritedTags, + parentTP.mapValuesIter(_.freshenAbove(lim, rigidify)).toMap + ) + }} + + def mapPol(pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType) + (implicit ctx: Ctx): TypedNuTrt = + TypedNuTrt(level, td, + tparams.map(tp => (tp._1, f(N, tp._2).assertTV, tp._3)), + members.mapValuesIter(_.mapPol(pol, smart)(f)).toMap, + f(pol.map(!_), thisTy), + f(pol, sign), + inheritedTags, + parentTP.mapValuesIter(_.mapPol(pol, smart)(f)).toMap + ) + def mapPolMap(pol: PolMap)(f: (PolMap, SimpleType) => SimpleType) + (implicit ctx: Ctx): TypedNuTrt = + TypedNuTrt(level, td, + tparams.map(tp => (tp._1, f(pol.invar, tp._2).assertTV, tp._3)), + members.mapValuesIter(_.mapPolMap(pol)(f)).toMap, + f(pol.contravar, thisTy), + f(pol, sign), + inheritedTags, + parentTP.mapValuesIter(_.mapPolMap(pol)(f)).toMap + ) + } + + + case class TypedNuCls( + level: Level, td: NuTypeDef, + tparams: TyParams, + params: Opt[Ls[Var -> FieldType]], + auxCtorParams: Opt[Ls[Var -> ST]], + members: Map[Str, NuMember], + thisTy: ST, + sign: ST, + inheritedTags: Set[TypeName], + parentTP: Map[Str, NuMember] + ) extends TypedNuTypeDef(Cls) with PolyNuDecl + { + def decl: NuTypeDef = td + def kind: DeclKind = td.kind + def nme: TypeName = td.nme + def name: Str = nme.name + def isImplemented: Bool = true + def isPublic = true // TODO + + /** The type of a palin term reference to this type definition. */ + def typeSignature(usesNew: Bool, loco: Opt[Loc])(implicit raise: Raise): ST = + typeSignatureOf(usesNew, loco, td, level, tparams, params, auxCtorParams, sign, inheritedTags) + + /** Includes class-name-coded type parameter fields. */ + lazy val virtualMembers: Map[Str, NuMember] = members ++ tparams.map { + case (nme @ TypeName(name), tv, _) => + td.nme.name+"#"+name -> NuParam(nme, FieldType(S(tv), tv)(provTODO), isPublic = true)(level) + } ++ parentTP + + // TODO + // def checkVariances + + // lazy val explicitVariances: VarianceStore = + // MutMap.from(tparams.iterator.map(tp => tp._2 -> tp._3.getOrElse(VarianceInfo.in))) + + // TODO should we really recompute them on freshened instances, or can we avoid that? + private var _variances: Opt[VarianceStore] = N + def variances(implicit ctx: Ctx): VarianceStore = { + _variances match { + case S(res) => res + case N => trace(s"Computing variances of ${this.name}") { + val store = VarianceStore.empty + val traversed = MutSet.empty[Pol -> TV] + + object Trav extends Traverser2.InvariantFields { + override def apply(pol: PolMap)(ty: ST): Unit = + trace(s"Trav($pol)($ty)") { + ty match { + case tv: TypeVariable => if (traversed.add(pol(tv) -> tv)) { + store(tv) = store.getOrElse(tv, VarianceInfo.bi) && (pol(tv) match { + case S(true) => VarianceInfo.co + case S(false) => VarianceInfo.contra + case N => VarianceInfo.in + }) + super.apply(pol)(ty) + } + case ty @ RecordType(fs) => + // Ignore type param members such as `C#A` in `{C#A: mut A30'..A30'}` + super.apply(pol)(RecordType(fs.filterNot(_._1.name.contains('#')))(ty.prov)) + case _ => super.apply(pol)(ty) + } + }() + } + members.foreachEntry { + case (_, m: NuParam) if m.isType => + case (_, m) => Trav.applyMem(PolMap.pos)(m) + } + + // TODO check consistency with explicitVariances + val res = store ++ tparams.iterator.collect { case (_, tv, S(vi)) => tv -> vi } + + _variances = S(res) + + res + }(r => s"= $r") + } + } + + def varianceOf(tv: TV)(implicit ctx: Ctx): VarianceInfo = + variances.getOrElse(tv, VarianceInfo.in) + + def freshenAbove(lim: Int, rigidify: Bool) + (implicit ctx: Ctx, freshened: MutMap[TV,ST]) + : TypedNuCls = { val outer = ctx; withLevel { implicit ctx => + TypedNuCls(outer.lvl, td, + tparams.map(tp => (tp._1, tp._2.freshenAbove(lim, rigidify).assertTV, tp._3)), + params.map(_.mapValues(_.freshenAbove(lim, rigidify))), + auxCtorParams.map(_.mapValues(_.freshenAbove(lim, rigidify))), + members.mapValuesIter(_.freshenAbove(lim, rigidify)).toMap, + thisTy.freshenAbove(lim, rigidify), + sign.freshenAbove(lim, rigidify), + inheritedTags, + parentTP.mapValuesIter(_.freshenAbove(lim, rigidify)).toMap, + ) + }} + + def mapPol(pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType) + (implicit ctx: Ctx): TypedNuCls = + TypedNuCls(level, td, + tparams.map(tp => (tp._1, f(N, tp._2).assertTV, tp._3)), + params.map(_.mapValues(_.update(t => f(pol.map(!_), t), t => f(pol, t)))), + auxCtorParams.map(_.mapValues(t => f(pol.map(!_), t))), + members.mapValuesIter(_.mapPol(pol, smart)(f)).toMap, + f(pol.map(!_), thisTy), + f(pol, sign), + inheritedTags, + parentTP.mapValuesIter(_.mapPol(pol, smart)(f)).toMap, + ) + def mapPolMap(pol: PolMap)(f: (PolMap, SimpleType) => SimpleType) + (implicit ctx: Ctx): TypedNuCls = + TypedNuCls(level, td, + tparams.map(tp => (tp._1, f(pol.invar, tp._2).assertTV, tp._3)), + params.map(_.mapValues(_.update(t => f(pol.contravar, t), t => f(pol, t)))), + auxCtorParams.map(_.mapValues(t => f(pol.contravar, t))), + members.mapValuesIter(_.mapPolMap(pol)(f)).toMap, + f(pol.contravar, thisTy), + f(pol, sign), + inheritedTags, + parentTP.mapValuesIter(_.mapPolMap(pol)(f)).toMap, + ) + + override def toString: Str = s"TypedNuCls($level, ${td.nme},\n\t$tparams,\n\t$params,\n\tthis: $thisTy, ${ + members.lnIndent()},\n\t: $sign, $inheritedTags, $parentTP)" + } + + + case class TypedNuMxn( + level: Level, td: NuTypeDef, + thisTy: ST, superTy: ST, + tparams: TyParams, + params: Ls[Var -> FieldType], + members: Map[Str, NuMember], + ) extends TypedNuTypeDef(Mxn) with PolyNuDecl + { + def decl: NuTypeDef = td + def kind: DeclKind = td.kind + def nme: TypeName = td.nme + def name: Str = nme.name + def isImplemented: Bool = true + def isPublic = true // TODO + + lazy val virtualMembers: Map[Str, NuMember] = members ++ tparams.map { + case (nme @ TypeName(name), tv, _) => + td.nme.name+"#"+name -> NuParam(nme, FieldType(S(tv), tv)(provTODO), isPublic = false)(level) + } + + def freshenAbove(lim: Int, rigidify: Bool) + (implicit ctx: Ctx, freshened: MutMap[TV,ST]) + : TypedNuMxn = { val outer = ctx; withLevel { implicit ctx => + TypedNuMxn(outer.lvl, td, + thisTy.freshenAbove(lim, rigidify), + superTy.freshenAbove(lim, rigidify), + tparams.map(tp => (tp._1, tp._2.freshenAbove(lim, rigidify).assertTV, tp._3)), + params.mapValues(_.freshenAbove(lim, rigidify)), + members.mapValuesIter(_.freshenAbove(lim, rigidify)).toMap, + ) + }} + + def mapPol(pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType) + (implicit ctx: Ctx): TypedNuMxn = + TypedNuMxn(level, td, f(pol.map(!_), thisTy), f(pol.map(!_), superTy), + tparams.map(tp => (tp._1, f(N, tp._2).assertTV, tp._3)), + params.mapValues(_.update(t => f(pol.map(!_), t), t => f(pol, t))), + members.mapValuesIter(_.mapPol(pol, smart)(f)).toMap) + def mapPolMap(pol: PolMap)(f: (PolMap, SimpleType) => SimpleType) + (implicit ctx: Ctx): TypedNuMxn = + TypedNuMxn(level, td, f(pol.contravar, thisTy), f(pol.contravar, superTy), + tparams.map(tp => (tp._1, f(pol.invar, tp._2).assertTV, tp._3)), + params.mapValues(_.update(t => f(pol.contravar, t), t => f(pol, t))), + members.mapValuesIter(_.mapPolMap(pol)(f)).toMap) + + override def toString: Str = s"TypedNuMxn($level, ${td.nme},\n\tthis: $thisTy,\n\tsuper: $superTy,\n\ttparams: $tparams,\n\tparams: $params,\n\tmembers: ${members.lnIndent()}\n)" + } + + + /** Used when there was an error while tying a definition. */ + case class TypedNuDummy(d: NuDecl) extends TypedNuDecl with TypedNuTermDef { + def level = MinLevel + def kind: DeclKind = Val + def toLoc: Opt[Loc] = N + def name: Str = d.name + def isImplemented: Bool = true + def isPublic = true // TODO + def typeSignature: ST = errType + def freshenAbove(lim: Int, rigidify: Bool) + (implicit ctx: Ctx, freshened: MutMap[TV, ST]) = + this + def mapPol(pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType) + (implicit ctx: Ctx): TypedNuTermDef = + this + def mapPolMap(pol: PolMap)(f: (PolMap, SimpleType) => SimpleType) + (implicit ctx: Ctx): TypedNuTermDef = + this + } + + // Field `thisRef` is defined when the member refers to `this` selecting a field on it + // e.g., val x = this + // Field `refs` contains all `Var`s accessed by the member with their possible `this` qualifiers (`None` if it is an unqualified access) + case class RefMap(thisRef: Opt[Var], refs: Set[(Var, Opt[Var])]) + object RefMap { + lazy val nothing: RefMap = RefMap(N, Set.empty) + } + + /** Note: the type `bodyType` is stored *without* its polymorphic wrapper! (unlike `typeSignature`) */ + case class TypedNuFun(level: Level, fd: NuFunDef, bodyType: ST)(val isImplemented: Bool) + extends TypedNuDecl with TypedNuTermDef { + def kind: DeclKind = Val + def name: Str = fd.nme.name + def symbolicName: Opt[Str] = fd.symbolicNme.map(_.name) + def toLoc: Opt[Loc] = fd.toLoc + def isPublic = true // TODO + lazy val typeSignature: ST = PolymorphicType.mk(level, bodyType) + + def freshenAbove(lim: Int, rigidify: Bool) + (implicit ctx: Ctx, freshened: MutMap[TV, ST]) + : TypedNuFun = { val outer = ctx; withLevel { implicit ctx => this match { + case TypedNuFun(level, fd, ty) => + TypedNuFun(outer.lvl, fd, ty.freshenAbove(lim, rigidify))(isImplemented) + // .tap(res => println(s"Freshen[$level,${ctx.lvl}] $this ~> $res")) + }}} + + def mapPol(pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType) + (implicit ctx: Ctx): TypedNuFun = + TypedNuFun(level, fd, f(pol, bodyType))(isImplemented) + def mapPolMap(pol: PolMap)(f: (PolMap, SimpleType) => SimpleType) + (implicit ctx: Ctx): TypedNuFun = + TypedNuFun(level, fd, f(pol, bodyType))(isImplemented) + + def getFunRefs: RefMap = fd.rhs match { + case L(term) => getRefs(term) + case _ => RefMap.nothing + } + } + + + case class TypedTypingUnit(implementedMembers: Ls[NuMember], result: Opt[ST]) extends OtherTypeLike { + def map(f: ST => ST)(implicit ctx: Ctx): TypedTypingUnit = + TypedTypingUnit(implementedMembers.map(_.map(f)), result.map(f)) + def mapPol(pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType) + (implicit ctx: Ctx): TypedTypingUnit = + TypedTypingUnit(implementedMembers.map(_.mapPol(pol, smart)(f)), result.map(f(pol, _))) + def mapPolMap(pol: PolMap)(f: (PolMap, SimpleType) => SimpleType) + (implicit ctx: Ctx): TypedTypingUnit = + TypedTypingUnit(implementedMembers.map(_.mapPolMap(pol)(f)), result.map(f(pol, _))) + def freshenAbove(lim: Int, rigidify: Bool) + (implicit ctx: Ctx, freshened: MutMap[TV, ST]) + : TypedTypingUnit = + TypedTypingUnit(implementedMembers.map(_.freshenAbove(lim, rigidify)), result.map(_.freshenAbove(lim, rigidify))) + override def toString: Str = s"TypedTypingUnit(${(implementedMembers :+ result).lnIndent()})" + } + + + def typeSignatureOf(usesNew: Bool, loco: Opt[Loc], td: NuTypeDef, level: Level, + tparams: TyParams, params: Opt[Params], acParams: Opt[Ls[Var -> ST]], selfTy: ST, ihtags: Set[TypeName]) + (implicit raise: Raise) + : ST = + if ((td.kind is Mod) && params.isEmpty) + if (tparams.isEmpty) + TypeRef(td.nme, Nil)(provTODO) + else PolymorphicType.mk(level, + TypeRef(td.nme, tparams.map(_._2))(provTODO)) + else if ((td.kind is Cls) || (td.kind is Mod)) { + if (td.kind is Mod) + err(msg"Parameterized modules are not yet supported", loco) + println(s"params: $params $acParams") + if (!usesNew) + if (params.isEmpty) + if (acParams.isEmpty) + return err(msg"Class ${td.nme.name} cannot be instantiated as it exposes no constructor", loco) + else err(msg"Construction of unparameterized class ${td.nme.name} should use the `new` keyword", loco) + else if (acParams.isDefined) + err(msg"Construction of class with auxiliary constructor should use the `new` keyword", loco) + val ps: Params = acParams match { + case S(acParams) => acParams.mapValues(_.toUpper(noProv)) + case N => params.getOrElse(Nil) + } + PolymorphicType.mk(level, + FunctionType( + TupleType(ps.mapKeys(some))(provTODO), + TypeRef(td.nme, tparams.map(_._2))(provTODO) + )(provTODO) + ) + } else errType // FIXME + + + def getRefs(body: Statement): RefMap = { + val refs = mutable.HashSet[(Var, Opt[Var])]() + + def visit(s: Located): Opt[Var] = s match { + case Sel(ths @ Var("this"), v) => + refs.add((v, S(ths))) + N + case v @ Var(name) => + if (name === "this") S(v) + else { + refs.add((v, N)) + N + } + case _: Type => N + case _: Term| _: Statement | _: NuDecl | _: IfBody | _: CaseBranches | _: TypingUnit => + s.children.foldLeft[Opt[Var]](N)((r, c) => r.orElse(visit(c))) + } + + RefMap(visit(body), refs.toSet) + } + + /** Type checks a typing unit, which is a sequence of possibly-mutually-recursive type and function definitions + * interleaved with plain statements. */ + def typeTypingUnit(tu: TypingUnit, outer: Opt[Outer]) + (implicit ctx: Ctx, raise: Raise, vars: Map[Str, SimpleType]): TypedTypingUnit = + trace(s"${ctx.lvl}. Typing $tu") + { + val topLevel: Bool = outer.isEmpty + + // println(s"vars ${vars}") + + tu.entities.foreach { + case fd: NuFunDef if fd.isLetRec.isEmpty && outer.exists(_.kind is Block) => + err(msg"Cannot use `val` or `fun` in local block; use `let` instead.", fd.toLoc) + case _ => + } + + val named = mutable.Map.empty[Str, LazyTypeInfo] + + // * Not sure we should support declaring signature with the `ident: type` syntax + // val (signatures, otherEntities) = tu.entities.partitionMap { + // case Asc(v: Var, ty) => L(v -> ty) + // case s => R(s) + // } + val (decls, statements) = tu.entities.partitionMap { + case decl: NuDecl => L(decl) + case s => R(s) + } + val funSigs = MutMap.empty[Str, NuFunDef] + val implems = decls.filter { + case fd @ NuFunDef(_, nme, snme, tparams, R(rhs)) => + funSigs.updateWith(nme.name) { + case S(s) => + err(s"A type signature for '${nme.name}' was already given", fd.toLoc) + S(s) + case N => S(fd) + } + false // * Explicit signatures will already be typed in DelayedTypeInfo's typedSignatures + case _ => true + } + + val sigInfos = if (topLevel) funSigs.map { case (nme, decl) => + val lti = new DelayedTypeInfo(decl, implicitly) + + // TODO check against duplicated symbolic names in same scope... + decl.symbolicNme.foreach(snme => ctx += snme.name -> lti) + + decl.name -> lti + } else Nil + + val infos = implems.map { + case _decl: NuDecl => + val decl = _decl match { + case fd: NuFunDef => + assert(fd.signature.isEmpty) + funSigs.get(fd.nme.name) match { + case S(sig) => + fd.copy()(fd.declareLoc, fd.virtualLoc, S(sig), outer, fd.genField) + case _ => + fd.copy()(fd.declareLoc, fd.virtualLoc, fd.signature, outer, fd.genField) + } + case td: NuTypeDef => + if (td.nme.name in reservedTypeNames) + err(msg"Type name '${td.nme.name}' is reserved", td.toLoc) + td + } + val lti = new DelayedTypeInfo(decl, implicitly) + decl match { + case td: NuTypeDef => + ctx.tyDefs2 += td.nme.name -> lti + case fd: NuFunDef => + // TODO check against duplicated symbolic names in same scope... + fd.symbolicNme.foreach(snme => ctx += snme.name -> lti) + } + named.updateWith(decl.name) { + case sv @ S(v) => + decl match { + case NuFunDef(S(_), _, _, _, _) => () + case _ => + err(msg"Refininition of '${decl.name}'", decl.toLoc) + } + S(lti) + case N => + S(lti) + } + decl.name -> lti + } + + ctx ++= infos + ctx ++= sigInfos + + val tpdFunSigs = sigInfos.mapValues(_.complete() match { + case res: TypedNuFun if res.fd.isDecl => + TopType + case res: TypedNuFun => + res.typeSignature + case _ => die + }).toMap + + // * Complete typing of block definitions and add results to context + val completedInfos = (infos ++ sigInfos).mapValues(_.complete() match { + case res: TypedNuFun => + tpdFunSigs.get(res.name) match { + case S(expected) => + implicit val prov: TP = + TypeProvenance(res.fd.toLoc, res.fd.describe) + subsume(res.typeSignature, expected) + case _ => + // * Generalize functions as they are typed. + // * Note: eventually we'll want to first reorder their typing topologically so as to maximize polymorphism. + ctx += res.name -> VarSymbol(res.typeSignature, res.fd.nme) + res.symbolicName.foreach(ctx += _ -> VarSymbol(res.typeSignature, res.fd.nme)) + } + CompletedTypeInfo(res) + case res => CompletedTypeInfo(res) + }) + ctx ++= completedInfos + + val returnsLastExpr = outer.map(_.kind) match { + case N | S(Block | Val) => true + case S(_: TypeDefKind) => false + } + + // * Type the block statements + def go(stmts: Ls[Statement]): Opt[ST] = stmts match { + case s :: stmts => + val res_ty = s match { + case decl: NuDecl => N + case t: Term => + implicit val genLambdas: GenLambdas = true + val ty = typeTerm(t) + if (!topLevel && !(stmts.isEmpty && returnsLastExpr)) { + t match { + // * We do not include `_: Var` because references to `fun`s and lazily-initialized + // * definitions may have side effects. + case _: Lit | _: Lam => + warn("Pure expression does nothing in statement position.", t.toLoc) + case _ => + constrain(mkProxy(ty, TypeProvenance(t.toCoveringLoc, "expression in statement position")), UnitType)( + raise = err => raise(WarningReport( // Demote constraint errors from this to warnings + msg"Expression in statement position should have type `()`." -> N :: + msg"Use a comma expression `... , ()` to explicitly discard non-unit values, making your intent clearer." -> N :: + err.allMsgs, newDefs)), + prov = TypeProvenance(t.toLoc, t.describe), ctx) + } + } + S(ty) + case s: DesugaredStatement => + err(msg"Illegal position for this ${s.describe} statement.", s.toLoc)(raise) + N + case _ => die + } + stmts match { + case Nil => res_ty + case stmts => + // TODO check discarded non-unit values + go(stmts) + } + case Nil => N + } + val res_ty = trace("Typing unit statements") { go(statements) } (r => s": $r") + + TypedTypingUnit(completedInfos.map(_._2.member), res_ty) + + }() + + + + + trait DelayedTypeInfoImpl { this: DelayedTypeInfo => + private def outerCtx = ctx + + var isComputing: Bool = false // Replace by a Ctx entry? + var result: Opt[TypedNuDecl] = N + def isComputed: Bool = result.isDefined + + val level: Level = ctx.lvl + + val kind: DeclKind = decl.kind + val name: Str = decl.name + + private implicit val prov: TP = + TypeProvenance(decl.toLoc, decl.describe) + + println(s"${ctx.lvl}. Created lazy type info for $decl") + + type ParentSpec = (Term, Var, LazyTypeInfo, Ls[Type], Ls[Opt[Var] -> Fld]) + lazy val parentSpecs: Ls[ParentSpec] = decl match { + case td: NuTypeDef => + td.parents.flatMap { + case v @ Var(nme) => + S(v, v, Nil, Nil) + case p @ App(v @ Var(nme), Tup(args)) => + S(p, v, Nil, args) + case TyApp(v @ Var(nme), targs) => + S(v, v, targs, Nil) + case p @ App(TyApp(v @ Var(nme), targs), Tup(args)) => + S(p, v, targs, args) + case p => + err(msg"Unsupported parent specification", p.toLoc) // TODO + N + }.flatMap { + case (p, v @ Var(parNme), parTargs, parArgs) => + ctx.get(parNme) match { + case S(lti: LazyTypeInfo) => + S((p, v, lti, parTargs, parArgs)) + case S(_) => + err(msg"Cannot inherit from this", p.toLoc) + N + case N => + err(msg"Could not find definition `${parNme}`", p.toLoc) + N + } + } + case _ => Nil + } + + /** First list of members is for the typed arguments; + * the map of members is for the inherited virtual type argument members. */ + type TypedParentSpec = (TypedNuTypeDef, Ls[NuParam], Map[Str, NuMember], Opt[Loc]) + + private var typingParents = false + lazy val typedParents: Ls[TypedParentSpec] = ctx.nest.nextLevel { implicit ctx => + + ctx ++= paramSymbols + + if (typingParents === true) { + err(msg"Unhandled cyclic parent specification", decl.toLoc) + Nil + } else + try {typingParents = true; parentSpecs}.flatMap { + case (p, v @ Var(parNme), lti, parTargs, parArgs) => + trace(s"${lvl}. Typing parent spec $p") { + val info = lti.complete() + info match { + + case rawMxn: TypedNuMxn => + + // println(s"Raw $rawMxn") + val (fr, ptp) = refreshHelper(rawMxn, v, if (parTargs.isEmpty) N else S(parTargs)) // type args inferred + val mxn = { + implicit val frenshened: MutMap[TV,ST] = fr + implicit val ctx: Ctx = outerCtx + rawMxn.freshenAbove(info.level, rigidify = false) + } + // println(s"Fresh $mxn") + + val argMembs = { + if (parArgs.sizeCompare(mxn.params) =/= 0) + err(msg"mixin $parNme expects ${ + mxn.params.size.toString} parameter(s); got ${parArgs.size.toString}", Loc(v :: parArgs.unzip._2)) + + val paramMems = mxn.params.lazyZip(parArgs).flatMap { + case (nme -> p, _ -> Fld(FldFlags(mut, spec, get), a)) => // TODO factor this with code for classes: + assert(!mut && !spec && !get, "TODO") // TODO check mut, spec, get + implicit val genLambdas: GenLambdas = true + val a_ty = typeTerm(a) + p.lb.foreach(constrain(_, a_ty)) + constrain(a_ty, p.ub) + val isPublic = mxn.members(nme.name).isPublic + val fty = if (p.lb.isDefined) + // * We don't refine the field type when it's mutable as that could lead to muable updates being rejected + FieldType(p.lb, p.ub)(provTODO) + else FieldType(p.lb, a_ty)(provTODO) + Option.when(isPublic)(NuParam(nme, fty, isPublic = isPublic)(lvl)) + } + + paramMems //++ mxn.members.valuesIterator + + } + println(s"Mixin arg members $argMembs") + + S((mxn, argMembs, + Map.empty[Str, NuMember], // TODO add ptp here once we support explicit type args + p.toLoc + )) + + case rawTrt: TypedNuTrt => + if (parArgs.nonEmpty) err(msg"trait arguments are not yet supported", p.toLoc) + + val (fr, ptp) = refreshHelper(rawTrt, v, if (parTargs.isEmpty) N else S(parTargs)) // infer ty args if not provided + val trt = { + implicit val frenshened: MutMap[TV,ST] = fr + implicit val ctx: Ctx = outerCtx + rawTrt.freshenAbove(info.level, rigidify = false) + } + + val paramMems = Nil // * Maybe support trait params? (not sure) + + S((trt, paramMems, ptp ++ trt.parentTP, p.toLoc)) + + case rawCls: TypedNuCls => + + // println(s"Raw $rawCls where ${rawCls.showBounds}") + + val (fr, ptp) = refreshHelper(rawCls, v, if (parTargs.isEmpty) N else S(parTargs)) // infer ty args if not provided + val cls = { + implicit val frenshened: MutMap[TV,ST] = fr + implicit val ctx: Ctx = outerCtx + rawCls.freshenAbove(info.level, rigidify = false) + } + + // println(s"Fresh[${ctx.lvl}] $cls where ${cls.showBounds}") + + def checkArgsNum(effectiveParamSize: Int) = + if (parArgs.sizeCompare(effectiveParamSize) =/= 0) + err(msg"class $parNme expects ${ + effectiveParamSize.toString} parameter(s); got ${parArgs.size.toString + }", Loc(v :: parArgs.unzip._2)) + + val argMembs = { + implicit val genLambdas: GenLambdas = true + cls.auxCtorParams match { + case S(ps) => + checkArgsNum(ps.size) + ps.lazyZip(parArgs).map { + case (nme -> p_ty, _ -> Fld(FldFlags(mut, spec, get), a)) => + assert(!mut && !spec && !get, "TODO") // TODO check mut, spec, get + val a_ty = typeTerm(a) + constrain(a_ty, p_ty) + } + Nil + case N => cls.params match { + case S(ps) => + checkArgsNum(ps.size) + ps.lazyZip(parArgs).flatMap { + case (nme -> p, _ -> Fld(FldFlags(mut, spec, get), a)) => + assert(!mut && !spec && !get, "TODO") // TODO check mut, spec, get + val a_ty = typeTerm(a) + p.lb.foreach(constrain(_, a_ty)) + constrain(a_ty, p.ub) + val isPublic = cls.members(nme.name).isPublic + val fty = if (p.lb.isDefined) + // * We don't refine the field type when it's mutable as that could lead to muable updates being rejected + FieldType(p.lb, p.ub)(provTODO) + else FieldType(p.lb, a_ty)(provTODO) + Option.when(isPublic)(NuParam(nme, fty, isPublic = isPublic)(lvl)) + } + case N => + checkArgsNum(0) + Nil + } + } + } + println(s"Class arg members $argMembs") + + S((cls, argMembs, ptp ++ cls.parentTP, p.toLoc)) + + case als: TypedNuAls => + // TODO dealias first? + err(msg"Cannot inherit from a type alias", p.toLoc) + N + case als: NuParam => + // TODO first-class mixins/classes... + err(msg"Cannot inherit from a parameter", p.toLoc) + N + // case als: NuTypeParam => + // err(msg"Cannot inherit from a type parameter", p.toLoc) + // Nil + case cls: TypedNuFun => + err(msg"Cannot inherit from a function", p.toLoc) + N + + case cls: TypedNuDummy => + N + + } + }() + } finally { typingParents = false } + + } + + + def lookupTags(parents: Ls[ParentSpec], tags: Set[TypeName]): Set[TypeName] = { + parents match { + case Nil => tags + case (p, Var(nm), lti, _, _) :: ps => lti match { + case lti: DelayedTypeInfo => lti.kind match { + case Trt | Cls | Mod => lookupTags(ps, Set.single(TypeName(nm)) union lti.inheritedTags union tags) + case Val | Mxn | Als => lookupTags(ps, tags) + } + case CompletedTypeInfo(trt: TypedNuTrt) => + lookupTags(ps, Set.single(TypeName(nm)) union trt.inheritedTags union tags) + case CompletedTypeInfo(cls: TypedNuCls) => + lookupTags(ps, Set.single(TypeName(nm)) union cls.inheritedTags union tags) + case CompletedTypeInfo(_: NuParam | _: TypedNuFun | _: TypedNuAls | _: TypedNuMxn | _: TypedNuDummy) => + lookupTags(ps, tags) + } + } + } + + private var inheritedTagsStartedComputing = false + lazy val inheritedTags: Set[TypeName] = + if (inheritedTagsStartedComputing) Set.empty // * Deals with malformed inheritances (cycles) + else { + inheritedTagsStartedComputing = true + lookupTags(parentSpecs, Set.empty) + } + + lazy val tparams: TyParams = ctx.nest.nextLevel { implicit ctx => + decl match { + case td: NuTypeDef => + td.tparams.map(tp => + (tp._2, freshVar( + TypeProvenance(tp._2.toLoc, "type parameter", + S(tp._2.name), + isType = true), + N, S(tp._2.name)), tp._1)) + case fd: NuFunDef => + fd.tparams.map { tn => + (tn, freshVar( + TypeProvenance(tn.toLoc, "method type parameter", + originName = S(tn.name), + isType = true), + N, S(tn.name)), N) + } + } + } + lazy val tparamsSkolems: Ls[Str -> SkolemTag] = tparams.map { + case (tp, tv, vi) => (tp.name, SkolemTag(tv)(tv.prov)) + } + + lazy val explicitVariances: VarianceStore = + MutMap.from(tparams.iterator.map(tp => tp._2 -> tp._3.getOrElse(VarianceInfo.in))) + + def varianceOf(tv: TV)(implicit ctx: Ctx): VarianceInfo = + // TODO make use of inferred vce if result is completed + explicitVariances.get(tv).getOrElse(VarianceInfo.in) + + lazy private implicit val vars: Map[Str, SimpleType] = + outerVars ++ tparamsSkolems + + lazy val typedParams: Opt[Ls[Var -> FieldType]] = ctx.nest.nextLevel { implicit ctx => + decl match { + case td: NuTypeDef => + td.params.map(_.fields.map { + case (S(nme), Fld(FldFlags(mut, spec, getter), value)) => + assert(!mut && !spec, "TODO") // TODO + value.toType match { + case R(tpe) => + implicit val newDefsInfo: Map[Str, (TypeDefKind, Int)] = Map.empty // TODO? + val ty = typeType(tpe) + nme -> FieldType(N, ty)(provTODO) + case _ => ??? + } + case (N, Fld(FldFlags(mut, spec, getter), nme: Var)) => + assert(!mut && !spec, "TODO") // TODO + // nme -> FieldType(N, freshVar(ttp(nme), N, S(nme.name)))(provTODO) + nme -> FieldType(N, err(msg"${td.kind.str.capitalize} parameters currently need type annotations", + nme.toLoc))(provTODO) + case _ => ??? + }) + case fd: NuFunDef => N + } + } + + lazy val paramSymbols = typedParams.getOrElse(Nil).map(p => p._1.name -> VarSymbol(p._2.ub, p._1)) + + // TODO also import signatures from base classes and mixins! + lazy val (typedSignatures, funImplems) : (Ls[(NuFunDef, ST)], Ls[NuFunDef]) = decl match { + case td: NuTypeDef => ctx.nest.nextLevel { implicit ctx => + val (signatures, rest) = td.body.entities.partitionMap { + case fd @ NuFunDef(N | S(false), nme, snme, tparams, R(rhs)) => // currently `val`s are encoded with `S(false)` + L((fd, rhs)) + // TODO also pick up signature off implems with typed params/results + case s => R(s) + } + val implems = rest.collect { case fd @ NuFunDef(N | S(false), nme, snme, tparams, L(rhs)) => fd } + + ctx ++= paramSymbols + + signatures.map { case (fd, rhs) => + (fd, ctx.poly { implicit ctx: Ctx => + vars ++ fd.tparams.map { tn => + tn.name -> freshVar(TypeProvenance(tn.toLoc, "method type parameter", + originName = S(tn.name), + isType = true), N, S(tn.name)) + } |> { implicit vars => + + typeType(rhs).withProv( + TypeProvenance(Loc(rhs :: fd.nme :: fd.tparams), s"signature of member `${fd.nme.name}`") + ) + + } + }) + } -> implems + } + case _: NuFunDef => Nil -> Nil + } + lazy val typedSignatureMembers: Ls[Str -> TypedNuFun] = { + val implemented = funImplems.iterator.map(_.nme.name).toSet + typedSignatures.iterator.map { case (fd, ty) => + fd.nme.name -> TypedNuFun(level + 1, fd, ty)(implemented.contains(fd.nme.name)) + }.toList + } + + lazy val inheritedFields: Set[Var] = decl match { + case td: NuTypeDef => + parentSpecs.iterator.flatMap(_._3 match { + case dti: DelayedTypeInfo => dti.allFields + case CompletedTypeInfo(m: TypedNuTypeDef) => m.allFields + case _ => Set.empty}).toSet + case _: NuFunDef => Set.empty + } + lazy val privateParams: Set[Var] = decl match { + case td: NuTypeDef => + // td.params.dlof(_.fields)(Nil).iterator.collect { + // case (S(nme), Fld(flags, _)) if !flags.genGetter => nme + // case (N, Fld(flags, nme: Var)) if !flags.genGetter => nme + // // case (N, Fld(flags, _)) => die + // }.toSet + td.params.dlof(_.fields)(Nil).iterator.flatMap { + case (S(nme), Fld(flags, _)) => Option.when(!flags.genGetter)(nme) + case (N, Fld(flags, nme: Var)) => Option.when(!flags.genGetter)(nme) + case (N, Fld(flags, _)) => die + }.toSet + case _: NuFunDef => Set.empty + } + + lazy val allFields: Set[Var] = decl match { + case td: NuTypeDef => + (td.params.getOrElse(Tup(Nil)).fields.iterator.flatMap(_._1) ++ td.body.entities.iterator.collect { + case fd: NuFunDef => fd.nme + }).toSet ++ inheritedFields ++ tparams.map { + case (nme @ TypeName(name), tv, _) => + Var(td.nme.name+"#"+name).withLocOf(nme) + } + case _: NuFunDef => Set.empty + } + + lazy val typedFields: Map[Var, FieldType] = { + (typedParams.getOrElse(Nil).toMap + // -- privateFields + -- inheritedFields /* parameters can be overridden by inherited fields/methods */ + ) ++ typedSignatures.iterator.map(fd_ty => fd_ty._1.nme -> fd_ty._2.toUpper(noProv)) ++ + typedParents.flatMap(_._3).flatMap { + case (k, p: NuParam) => Var(k) -> p.ty :: Nil + case _ => Nil + } + } + lazy val mutRecTV: TV = freshVar( + TypeProvenance(decl.toLoc, decl.describe, S(decl.name), decl.isInstanceOf[NuTypeDef]), + N, + S(decl.name))(level + 1) + + private lazy val thisTV: TV = + freshVar(provTODO, N, S(decl.name.decapitalize))(lvl + 1) + + def refreshHelper(raw: PolyNuDecl, v: Var, parTargs: Opt[Ls[Type]]) + (implicit ctx: Ctx, raise: Raise, vars: Map[Str, SimpleType]) + : (MutMap[TV, ST], Map[Str, NuParam]) = { + val rawName = v.name + parTargs foreach { pta => + if (raw.tparams.sizeCompare(pta.size) =/= 0) + err(msg"${raw.kind.str} $rawName expects ${ + raw.tparams.size.toString} type parameter(s); got ${pta.size.toString}", Loc(v :: pta)) + } + refreshHelper2(raw: PolyNuDecl, v: Var, parTargs.map(_.map(typeType(_)))) + } + + def complete()(implicit raise: Raise): TypedNuDecl = result.getOrElse { + if (isComputing) { + err(msg"Unhandled cyclic definition", decl.toLoc) + TypedNuDummy(decl) + } + else trace(s"Completing ${decl.showDbg}") { + println(s"Type params ${tparams.mkString(" ")}") + println(s"Params ${typedParams.mkString(" ")}") + + val res = try { + isComputing = true + decl match { + case fd: NuFunDef => + def checkNoTyParams() = + if (fd.tparams.nonEmpty) + err(msg"Type parameters are not yet supported in this position", + fd.tparams.head.toLoc) + val res_ty = fd.rhs match { + case R(PolyType(tps, ty)) => + checkNoTyParams() + val body_ty = ctx.poly { implicit ctx: Ctx => + typeType(ty)(ctx, raise, + vars = vars ++ tps.map { + case L(tn) => tn.name -> freshVar(provTODO, N)(1) + case _ => die + }.toMap) + } + TypedNuFun(ctx.lvl, fd, PolymorphicType(ctx.lvl, body_ty))(isImplemented = false) + case R(_) => die + case L(body) => + fd.isLetRec match { + case S(true) => // * Let rec bindings + checkNoTyParams() + implicit val gl: GenLambdas = true + TypedNuFun(ctx.lvl, fd, typeTerm( + Let(true, fd.nme, body, fd.nme) + ))(isImplemented = true) + case S(false) => // * Let bindings + checkNoTyParams() + implicit val gl: GenLambdas = true + TypedNuFun(ctx.lvl, fd, typeTerm(body))(isImplemented = true) + case N => + // * We don't type functions polymorphically from the point of view of a typing unit + // * to avoid cyclic-looking constraints due to the polymorphic recursion limitation, + // * as these functions are allowed to be mutually-recursive. + // * In the future, we should type each mutual-recursion-component independently + // * and polymorphically wrt to external uses of them. + implicit val gl: GenLambdas = false + val outer_ctx = ctx + val body_ty = ctx.nextLevel { implicit ctx: Ctx => + // * Note: can't use `ctx.poly` instead of `ctx.nextLevel` because all the methods + // * in the current typing unit are quantified together. + assert(fd.tparams.sizeCompare(tparamsSkolems) === 0, (fd.tparams, tparamsSkolems)) + vars ++ tparamsSkolems |> { implicit vars => + // * Only type methods polymorphically if they're at the top level or if + // * they're annotated with a type signature. + // * Otherwise, we get too much extrusion and cycle check failures + // * especially in the context of open recursion and mixins. + if (ctx.lvl === 1 || fd.signature.nonEmpty || fd.outer.exists(_.kind isnt Mxn)) + typeTerm(body) + else outer_ctx |> { implicit ctx: Ctx => + println(s"Not typing polymorphicall (cf. not top level or not annotated)") + typeTerm(body) } + } + }.withProv(TypeProvenance(fd.toLoc, s"definition of method ${fd.nme.name}")) + TypedNuFun(ctx.lvl, fd, body_ty)(isImplemented = true) + } + } + ctx.nextLevel { implicit ctx: Ctx => constrain(res_ty.bodyType, mutRecTV) } + res_ty + + + case td: NuTypeDef => + + /** Check no `this` access in ctor statements or val rhs and reject unqualified accesses to virtual members.. */ + def qualificationCheck(members: Ls[NuMember], stmts: Ls[Statement], base: Ls[NuMember], sigs: Ls[NuMember]): Unit = { + val cache = mutable.HashMap[Str, Opt[Var]]() + val sigMap = sigs.map(m => m.name -> m).toMap + val allMembers = sigMap ++ (base.iterator ++ members).map(m => m.name -> m).toMap + + def isVirtual(nf: TypedNuFun) = + nf.fd.isVirtual || (sigMap.get(nf.name) match { + case S(sig: TypedNuFun) => sig.fd.virtualLoc.nonEmpty // The signature is virtual by itself, so we need to check the virtual keyword + case _ => false + }) + + // Return S(v) when there is an invalid access to the v. + def checkThisInCtor(refs: RefMap, name: Opt[Str], stack: Ls[Str])(expection: Bool): Opt[Var] = { + def run: Opt[Var] = { + refs.thisRef.orElse( + refs.refs.foldLeft[Opt[Var]](N)((res, p) => res.orElse(allMembers.get(p._1.name) match { + case S(nf: TypedNuFun) if name.fold(true)(name => name =/= p._1.name) && !stack.contains(p._1.name) => // Avoid cycle checking + p._2 match { + case q @ S(_) if !expection || isVirtual(nf) => q + case _ => checkThisInCtor(nf.getFunRefs, S(p._1.name), p._1.name :: stack)(false) + } + case _ => N // Refer to outer + })) + ) + } + + name.fold(run)(name => cache.getOrElseUpdate(name, run)) + } + + def checkUnqualifiedVirtual(refs: RefMap, parentLoc: Opt[Loc]) = + refs.refs.foreach(p => if (p._2.isEmpty) allMembers.get(p._1.name) match { // unqualified access + case S(nf: TypedNuFun) if isVirtual(nf) => + err(msg"Unqualified access to virtual member ${p._1.name}" -> parentLoc :: + msg"Declared here:" -> nf.fd.toLoc + :: Nil) + case _ => () + }) + + // If the second error message location is covered by the first one, + // we show the first error message with more precise location + def mergeErrMsg(msg1: Message -> Opt[Loc], msg2: Message -> Opt[Loc]) = + (msg1._2, msg2._2) match { + case (S(loc1), l @ S(loc2)) if loc1 covers loc2 => msg1._1 -> l :: Nil + case _ => msg1 :: msg2 :: Nil + } + + members.foreach { + case tf @ TypedNuFun(_, fd, _) => + val refs = tf.getFunRefs + if (fd.isLetRec.isDefined) checkThisInCtor(refs, S(tf.name), tf.name :: Nil)(true) match { + case S(v) => // not a function && access `this` in the ctor + err(mergeErrMsg(msg"Cannot access `this` while initializing field ${tf.name}" -> fd.toLoc, + msg"The access to `this` is here" -> v.toLoc)) + case N => () + } + checkUnqualifiedVirtual(refs, fd.toLoc) + case _ => () + } + stmts.foreach{ + case Asc(Var("this"), _) => () + case s => + val refs = getRefs(s) + checkThisInCtor(refs, N, Nil)(false) match { + case S(v) => + err(mergeErrMsg(msg"Cannot access `this` during object initialization" -> s.toLoc, + msg"The access to `this` is here" -> v.toLoc)) + case N => () + } + checkUnqualifiedVirtual(refs, s.toLoc) + } + } + + /** Checks everything is implemented and there are no implementation duplicates. */ + def implemCheck(implementedMembers: Ls[NuMember], toImplement: Ls[NuMember]): Unit = { + + val implemsMap = MutMap.empty[Str, NuMember] + implementedMembers.foreach { m => + implemsMap.updateWith(m.name) { + case S(_) => + err(msg"Duplicated `${m.name}` member definition in `${td.name}`", m.toLoc) + N + case N => S(m) + } + } + if (!td.isDecl && td.kind =/= Trt && !td.isAbstract) { + toImplement.foreach { m => + implemsMap.get(m.name) match { + case S(_) => + case N => + err(msg"Member `${m.name}` is declared (or its declaration is inherited) but is not implemented in `${ + td.nme.name}`" -> td.nme.toLoc :: + msg"Declared here:" -> m.toLoc :: + Nil) + } + } + } + + } + + /** Checks overriding members against their parent types. */ + def overrideCheck(newMembers: Ls[NuMember], signatures: Ls[NuMember], clsSigns: Ls[NuMember]): Unit = + ctx.nextLevel { implicit ctx: Ctx => // * Q: why exactly do we need `ctx.nextLevel`? + + val sigMap = MutMap.empty[Str, NuMember] + signatures.foreach { m => + sigMap.updateWith(m.name) { + case S(_) => die + case N => S(m) + } + } + + newMembers.foreach { m => + println(s"Checking overriding for ${m} against ${sigMap.get(m.name)}...") + (m, sigMap.get(m.name)) match { + case (_, N) => + case (m: TypedNuTermDef, S(fun: TypedNuTermDef)) => fun match { + // * If the implementation and the declaration are in the same class, + // * it does not require to be virtual. + case _ if fun.isPrivate => () // * Private members are not actually inherited + case td: TypedNuFun if (!td.fd.isVirtual && !clsSigns.contains(fun)) => + err(msg"${m.kind.str.capitalize} member `${m.name + }` is not virtual and cannot be overridden" -> m.toLoc :: + msg"Originally declared here:" -> fun.toLoc :: + Nil) + case p: NuParam if (!p.isVirtual && !clsSigns.contains(p)) => + err(msg"Inherited parameter named `${m.name + }` is not virtual and cannot be overridden" -> m.toLoc :: + msg"Originally declared here:" -> fun.toLoc :: + Nil) + case _ => + val mSign = m.typeSignature + implicit val prov: TP = mSign.prov + constrain(mSign, fun.typeSignature) + } + case (_, S(that)) => + err(msg"${m.kind.str.capitalize} member `${m.name}` cannot override ${ + that.kind.str} member of the same name declared in parent" -> td.toLoc :: + msg"Originally declared here:" -> that.toLoc :: + Nil) + } + } + + } + + + // intersection of members + def membersInter(l: Ls[NuMember], r: Ls[NuMember]): Ls[NuMember] = { + def merge(ltm: NuMember, rtm: Option[NuMember]) = { + rtm.foreach(rtm => assert(rtm.level === ltm.level, (ltm.level, rtm.level))) + (ltm, rtm) match { + case (a: TypedNuFun, S(b: TypedNuFun)) => + assert(!(a.isImplemented && b.isImplemented)) + val fd = NuFunDef((a.fd.isLetRec, b.fd.isLetRec) match { + case (S(a), S(b)) => S(a || b) + case _ => N // if one is fun, then it will be fun + }, a.fd.nme, N/*no sym name?*/, a.fd.tparams, a.fd.rhs)(a.fd.declareLoc, a.fd.virtualLoc, N, a.fd.outer orElse b.fd.outer, a.fd.genField) + S(TypedNuFun(a.level, fd, a.bodyType & b.bodyType)(a.isImplemented || b.isImplemented)) + case (a: NuParam, S(b: NuParam)) => + if (!a.isPublic) S(b) else if (!b.isPublic) S(a) + else S(NuParam(a.nme, a.ty && b.ty, isPublic = true)(a.level)) + case (a: NuParam, S(b: TypedNuFun)) => + S(TypedNuFun(a.level, b.fd, a.ty.ub & b.bodyType)(a.isImplemented || b.isImplemented)) + case (a: TypedNuFun, S(b: NuParam)) => + S(TypedNuFun(a.level, a.fd, b.ty.ub & a.bodyType)(a.isImplemented || b.isImplemented)) + case (a, N) => S(a) + case (a, S(b)) => + err(msg"Intersection of ${a.kind.str} member and ${b.kind.str} members currently unsupported" -> td.toLoc + :: msg"The ${a.kind.str} member is defined here:" -> a.toLoc + :: msg"The ${b.kind.str} member is defined here:" -> b.toLoc + :: Nil) + N + } + } + l.foldLeft(r.map(d => d.name -> d).toMap) { case (acc, ltm) => + acc.updatedWith(ltm.name)(merge(ltm, _)) + }.values.toList + } + + + if (((td.kind is Mod) || (td.kind is Mxn)) && td.ctor.isDefined) + err(msg"Explicit ${td.kind.str} constructors are not supported", + td.ctor.fold[Opt[Loc]](N)(c => c.toLoc)) + + // * To type signatures correctly, we need to deal with unbound type variables 'X, + // * which should be treated as unknowns (extruded skolems). + // * Allowing such type variables is important for the typing of signatures such as + // * `: Foo | Bar` where `Foo` and `Bar` take type parameters, + // * as these will be (in the future) desugared to `: Foo['A0] | Bar['A1]` + def typeTypeSignature(sign: Type)(implicit ctx: Ctx): ST = { + val outer = ctx + val ty = ctx.nest.nextLevel { implicit ctx => + // * Type the signature in a higher level, so as to contain unbound type vars + val ty = typeType(td.sig.getOrElse(Top)) + // * Make these type vars skolems + implicit val freshened: MutMap[TV, ST] = MutMap.empty + ty.freshenAbove(outer.lvl, rigidify = true) + } + // * Create a lower-levl type variable to extrude the type through it, + // * which will result in making the unbound type vars extruded skolems (i.e., unknowns) + val res = freshVar(provTODO, N, N)(ctx.lvl) + // * Subtle note: it is sufficient and important to add the type as a LB of the TV + // * instead of unifying them because: + // * 1. currently we expand type defs the same no matter of the position polarity + // * so `class S: T` is always expanded as `#S & 'X` where `'X :> T` + // * 2. we don't want to have to check every single subtype of S against T, + // * as this check will already have been performed generally when typing class T, + // * but this would happen if we instead expanded into a type equivalent to #S & T... + constrain(ty, res) + // * Retrieve the extruded lower bound. + // * Note that there should be only one, and in particular it should not be recursive, + // * since the variable is never shared outside this scope. + res.lowerBounds match { + // case lb :: Nil => TypeBounds.mk(TopType, lb) + case lb :: Nil => lb + case _ => die + } + } + + td.kind match { + + case Trt => + td.params match { + case S(ps) => err(msg"trait parameters are not yet supported", ps.toLoc) + case _ => + } + + ctx.nest.nextLevel { implicit ctx => + ctx ++= paramSymbols + ctx ++= typedSignatures.map(nt => nt._1.name -> VarSymbol(nt._2, nt._1.nme)) + ctx += "this" -> VarSymbol(thisTV, Var("this")) + + def inherit(parents: Ls[TypedParentSpec], tags: ST, members: Ls[NuMember], + tparamMembs: Map[Str, NuMember], sig_ty: ST) + : (ST, Ls[NuMember], Map[Str, NuMember], ST) = + parents match { + case (trt: TypedNuTrt, argMembs, tpms, loc) :: ps => + assert(argMembs.isEmpty, argMembs) + inherit(ps, + tags & trt.sign, + membersInter(members, trt.members.values.toList), + tparamMembs ++ tpms, // with type members of parent class + sig_ty & trt.sign, + ) + case (_, _, _, loc) :: ps => + err(msg"A trait can only inherit from other traits", loc) + inherit(ps, tags, members, tparamMembs, sig_ty) + case Nil => (tags, members, tparamMembs, sig_ty) + } + val (tags, trtMembers, tparamMembs, sig_ty) = + inherit(typedParents, trtNameToNomTag(td)(noProv, ctx), Nil, Map.empty, + td.sig.fold(TopType: ST)(typeTypeSignature)) + + td.body.entities.foreach { + case fd @ NuFunDef(_, _, _, _, L(_)) => + err(msg"Method implementations in traits are not yet supported", fd.toLoc) + case _ => + } + + val ttu = typeTypingUnit(td.body, S(td)) + + val allMembers = ( + trtMembers.iterator.map(d => d.name -> d) + ++ ttu.implementedMembers.map(d => d.name -> d) + ++ typedSignatureMembers + ).toMap + + // check trait overriding + overrideCheck(typedSignatureMembers.map(_._2), trtMembers, Nil) + + TypedNuTrt(outerCtx.lvl, td, + tparams, + allMembers, + TopType, // thisType (same as for Cls) + sig_ty, + inheritedTags, + tparamMembs + ) + } + + case Als => + + if (td.params.getOrElse(Tup(Nil)).fields.nonEmpty) + err(msg"Type alias definitions cannot have value parameters" -> td.params.getOrElse(Tup(Nil)).toLoc :: Nil) + if (td.parents.nonEmpty) + err(msg"Type alias definitions cannot extend parents" -> Loc(td.parents) :: Nil) + + val body_ty = td.sig match { + case S(sig) => + ctx.nextLevel { implicit ctx: Ctx => typeType(sig) } + case N => + err(msg"Type alias definition requires a right-hand side", td.toLoc) + } + + TypedNuAls(outerCtx.lvl, td, tparams, body_ty) + + case Cls | Mod => + + ctx.nest.nextLevel { implicit ctx => + + if ((td.kind is Mod) && typedParams.nonEmpty) + // * Can we do better? (Memoization semantics?) + err(msg"${td.kind.str.capitalize} parameters are not supported", + typedParams.fold(td.nme.toLoc)(tp => Loc(tp.iterator.map(_._1)))) + + if (!td.isAbstract && !td.isDecl) td.sig match { + case S(sig) => warn( + msg"Self-type annotations have no effects on non-abstract ${td.kind.str} definitions" -> sig.toLoc + :: msg"Did you mean to use `extends` and inherit from a parent class?" -> N + :: Nil) + case N => + } + + ctx ++= paramSymbols + ctx ++= typedSignatures.map(nt => nt._1.name -> VarSymbol(nt._2, nt._1.nme)) + + val sig_ty = td.sig.fold(TopType: ST)(typeTypeSignature) + + implicit val prov: TP = + TypeProvenance(decl.toLoc, decl.describe) + + val finalType = thisTV + + val tparamMems = tparams.map { case (tp, tv, vi) => // TODO use vi + val fldNme = td.nme.name + "#" + tp.name + val skol = SkolemTag(tv)(tv.prov) + NuParam(TypeName(fldNme).withLocOf(tp), FieldType(S(skol), skol)(tv.prov), isPublic = true)(lvl) + } + val tparamFields = tparamMems.map(p => p.nme.toVar -> p.ty) + assert(!typedParams.exists(_.keys.exists(tparamFields.keys.toSet)), ???) + + case class Pack( + superType: ST, + mxnMembers: Ls[NuMember], + baseClsNme: Opt[Str], + baseClsMembers: Ls[NuMember], + traitMembers: Ls[NuMember], + tparamMembers: Map[Str, NuMember], + selfSig: ST, + ) + + def inherit(parents: Ls[TypedParentSpec], pack: Pack): Pack = parents match { + case (p, argMembs, tpms, loc) :: ps => println(s"=> Inheriting from $p"); p match { + + case mxn: TypedNuMxn => + + assert(finalType.level === lvl) + assert(mxn.superTy.level === lvl) + assert(mxn.thisTy.level === lvl) + + constrain(pack.superType, mxn.superTy) + constrain(finalType, mxn.thisTy) + + assert(tpms.isEmpty) // Mixins do not introduce virtual members for type params + + val newMembs = argMembs ++ mxn.members.valuesIterator.filterNot(_.isValueParam) + val newSuperType = WithType( + pack.superType, + RecordType( + newMembs.collect { + case m: NuParam => m.nme.toVar -> m.ty + case m: TypedNuFun => + // val ty = m.typeSignature + // * ^ Note: this also works and is more precise (some types made more specific), + // * but it causes duplication of recursive type structures + // * in typical SuperOOP mixin compositions, so it's better to be less precise + // * but simpler/more efficient/more concise here. + val ty = m.bodyType + m.fd.nme -> ty.toUpper(provTODO) + } + )(provTODO) + )(provTODO) + + inherit(ps, pack.copy( + superType = newSuperType, + mxnMembers = newMembs ++ pack.mxnMembers + )) + + case trt: TypedNuTrt => + + assert(argMembs.isEmpty, argMembs) + + inherit(ps, pack.copy( + traitMembers = membersInter(pack.traitMembers, trt.members.valuesIterator.filterNot(_.isValueParam).toList), + tparamMembers = pack.tparamMembers ++ tpms, + selfSig = pack.selfSig & trt.sign + )) + + case cls: TypedNuCls => + val parNme = cls.nme.name + + pack.baseClsNme.foreach { cls => + err(msg"Cannot inherit from more than one base class: ${ + cls} and ${parNme}", loc) + } + + val (baseParamMems, otherBaseMems) = + // cls.members.toList.partition(_._2.isValueParam) + cls.members.valuesIterator.toList.partition(_.isValueParam) + + println(s"argMembs $argMembs") + + println(s"selfSig ${cls.sign}") + + inherit(ps, pack.copy( + baseClsNme = S(parNme), + // baseClsMembers = argMembs ++ cls.members.valuesIterator.filterNot(_.isValueParam), + // baseClsMembers = argMembs.filterNot(_.isPrivate) ++ cls.members.valuesIterator.filterNot(_.isValueParam), + // baseClsMembers = cls.members.valuesIterator.filter(_.isValueParam) ++ argMembs ++ cls.members.valuesIterator.filterNot(_.isValueParam), + // baseClsMembers = baseParamMems ::: argMembs ::: otherBaseMems, + baseClsMembers = argMembs ++ cls.members.valuesIterator, + tparamMembers = pack.tparamMembers ++ tpms, + selfSig = pack.selfSig & cls.sign + )) + + case als: TypedNuAls => // Should be rejected in `typedParents` + inherit(ps, pack) + + } + case Nil => + println(s"Done inheriting: $pack") + + val thisType = WithType(pack.superType, + RecordType(typedParams.getOrElse(Nil))(ttp(td.params.getOrElse(Tup(Nil)), isType = true)) + )(provTODO) & + clsNameToNomTag(td)(provTODO, ctx) & + RecordType(tparamFields)(TypeProvenance(Loc(td.tparams.map(_._2)), "type parameters", isType = true)) + + trace(s"${lvl}. Finalizing inheritance with $thisType <: $finalType") { + assert(finalType.level === lvl) + constrain(thisType & sig_ty, finalType) + + }() + + if (!td.isAbstract) trace(s"Checking self signature...") { + constrain(thisType, pack.selfSig) + }() + + // println(s"${lvl}. Finalized inheritance with $superType ~> $thisType") + pack.copy(superType = thisType, selfSig = pack.selfSig & sig_ty) + } + + // * We start from an empty super type. + val baseType = + RecordType(Nil)(TypeProvenance(Loc(td.parents).map(_.left), "Object")) + + val paramMems = typedParams.getOrElse(Nil).map(f => + NuParam(f._1, f._2, isPublic = !privateParams.contains(f._1))(lvl)) + + val Pack(thisType, mxnMembers, _, baseClsMembers, traitMembers, tparamMembers, selfSig) = + inherit(typedParents, Pack(baseType, tparamMems ++ paramMems, N, Nil, Nil, Map.empty, sig_ty)) + + // println(s"Final self-type: ${selfSig}") + + ctx += "this" -> VarSymbol(thisTV, Var("this")) + ctx += "super" -> VarSymbol(thisType, Var("super")) + val ttu = typeTypingUnit(td.body, S(td)) + + // * `baseClsImplemMembers` actually also includes parameter members and their arg-based refinements + val (baseClsImplemMembers, baseClsIfaceMembers) = + baseClsMembers.partition(_.isImplemented) + + println(s"baseClsImplemMembers ${baseClsImplemMembers}") + + val newImplems = ttu.implementedMembers + + val clsSigns = typedSignatureMembers.map(_._2) + + trace(s"Checking `this` accesses...") { + val toCheckImplems = newImplems.filter(_.isImplemented) + qualificationCheck(toCheckImplems, td.body.entities.filter { + case _: NuDecl => false + case _ => true + } ++ td.ctor.fold[Ls[Statement]](Nil)(s => s.body.stmts), baseClsMembers, clsSigns) + }() + + // * Those member implementations we inherit from the base class that are not overridden + val implemsInheritedFromBaseCls = { + val possiblyOverridingNames = (newImplems.iterator ++ mxnMembers).map(_.name).toSet + baseClsImplemMembers.iterator.distinctBy(_.name) + .filterNot(possiblyOverridingNames contains _.name) + .toList + } + // * ... must type check against the trait signatures + trace(s"Checking base class implementations against inherited signatures...") { + overrideCheck(implemsInheritedFromBaseCls, traitMembers, Nil) + }() + + // * The following are type signatures all implementations must satisfy + // * (but we already know the base class implems satisfy the baseClsMembers signatures) + val ifaceMembers = membersInter(baseClsMembers, traitMembers) + + // * We now check current and non-overridden mixin implementations against + // * the signatures from the base class and traits + val toCheck = + (newImplems.iterator ++ mxnMembers).distinctBy(_.name).toList + + trace(s"Checking new implementations against inherited signatures...") { + overrideCheck(toCheck, + (clsSigns.iterator ++ ifaceMembers).distinctBy(_.name).toList, clsSigns) + }() + + val impltdMems = ( + newImplems // local members override mixin members: + ++ mxnMembers // local and mixin members override parent members: + ++ baseClsImplemMembers + ).distinctBy(_.name) + + trace(s"Checking new signatures against inherited signatures...") { + overrideCheck(clsSigns, ifaceMembers, clsSigns) + }() + + implemCheck(impltdMems, + (clsSigns.iterator ++ ifaceMembers.iterator) + .distinctBy(_.name).filterNot(_.isImplemented).toList) + + val allMembers = + (ifaceMembers ++ impltdMems).map(d => d.name -> d).toMap ++ typedSignatureMembers + + println(s"allMembers $allMembers") + + val auxCtorParams = td.ctor match { + case S(ctor @ Constructor(ps, bod)) => outerCtx.nest.nextLevel { implicit ctx => + def getterError(loco: Opt[Loc]) = + err(msg"Cannot use `val` in constructor parameters", loco) + val res = ps.fields.map { + case (S(nme), Fld(FldFlags(mut, spec, getter), value)) => + assert(!mut && !spec, "TODO") // TODO + if (getter) + // TODO we could support this to some extent + getterError(nme.toLoc) + value.toType match { + case R(tpe) => + implicit val newDefsInfo: Map[Str, (TypeDefKind, Int)] = Map.empty // TODO? (similar as one above in file) + val ty = typeType(tpe) + nme -> ty + case _ => ??? + } + case (N, Fld(FldFlags(mut, spec, getter), nme: Var)) => + assert(!mut && !spec, "TODO") // TODO + if (getter) + getterError(nme.toLoc) + nme -> freshVar(ttp(nme), N, S(nme.name)) + case (N, Fld(_, rhs)) => + Var("") -> err(msg"Unsupported constructor parameter shape", rhs.toLoc) + } + res.foreach { case (nme, ty) => ctx += nme.name -> VarSymbol(ty, nme) } + implicit val gl: GenLambdas = false + implicit val prov: TP = + TypeProvenance(ctor.toLoc, "auxiliary class constructor") + val bodStmts = bod match { + case Blk(sts) => sts + case _ => bod :: Nil + } + // * TODO later: for each `typedParams`, first add sthg like `ctx += lhs.name -> UndefinedParam(...)` + val classParamsMap = MutMap.from(typedParams.getOrElse(Nil).mapValues(some)) + bodStmts.foreach { + case Eqn(lhs, rhs) => + classParamsMap.updateWith(lhs) { + case S(S(p)) => + val rhs_ty = typeTerm(rhs) + constrain(rhs_ty, p.ub) + ctx += lhs.name -> VarSymbol(rhs_ty, lhs) + S(N) + case S(N) => + err(msg"Class parameter '${lhs.name}' was already set", lhs.toLoc) + N + case N => + err(msg"Unknown class parameter '${lhs.name}'", lhs.toLoc) + N + } + case stmt: DesugaredStatement => + typeStatement(stmt, allowPure = false) + case _ => die + } + S(res) + } + case N => N + } + + TypedNuCls(outerCtx.lvl, td, + tparams, + typedParams, + auxCtorParams.orElse(Option.when( + typedParams.isEmpty && (td.kind is Cls) && !td.isAbstract)(Nil)), + allMembers, + TopType, + if (td.isAbstract) selfSig else sig_ty, + inheritedTags, + tparamMembers + ).tap(_.variances) // * Force variance computation + } + + case Mxn => + if (td.parents.nonEmpty) + err(msg"mixin definitions cannot yet extend parents" -> Loc(td.parents) :: Nil) + val outer = ctx + ctx.nest.nextLevel { implicit ctx => + ctx ++= paramSymbols + ctx ++= typedSignatures.map(nt => nt._1.name -> VarSymbol(nt._2, nt._1.nme)) + val paramMems = typedParams.map(_.map(f => + f._1.name -> NuParam(f._1, f._2, !privateParams.contains(f._1))(lvl))).getOrElse(Nil).toMap + val thisTV = freshVar(provTODO, N, S("this")) + val superTV = freshVar(provTODO, N, S("super")) + ctx += "this" -> VarSymbol(thisTV, Var("this")) + ctx += "super" -> VarSymbol(superTV, Var("super")) + val ttu = typeTypingUnit(td.body, S(td)) + val impltdMems = ttu.implementedMembers + val signs = typedSignatureMembers.map(_._2) + overrideCheck(impltdMems, signs, signs) + implemCheck(impltdMems, signs) + val mems = paramMems ++ impltdMems.map(m => m.name -> m).toMap ++ typedSignatureMembers + TypedNuMxn(outer.lvl, td, thisTV, superTV, tparams, typedParams.getOrElse(Nil), mems) + } + } + + } + + } finally { isComputing = false } + + result = S(res) + res + + }(r => s"Completed ${r} where ${r.showBounds}") + } + def typeSignature(usesNew: Bool, loco: Opt[Loc])(implicit raise: Raise): ST = + decl match { + case _: NuFunDef => + if (isComputing) { + println(s"Already computing! Using TV: $mutRecTV") + mutRecTV // TODO make sure this is never misused (ie not accessed from difft scope/level) + } else complete() match { + case TypedNuFun(_, fd, ty) => + ty + case _ => die + } + case td: NuTypeDef => + // * We want to avoid forcing completion of types needlessly + // * OTOH we need the type to be completed to use its aux ctor (whose param types are optional) + // * TODO: avoid forcing when the aux ctor has type-annotated params + if (td.ctor.isDefined) complete() match { + case cls: TypedNuCls => + cls.typeSignature(usesNew, loco) + case _: TypedNuDummy => errType + case _ => die + } else typeSignatureOf(usesNew, loco, td, level, tparams, typedParams, N, TopType, inheritedTags) + } + + override def toString: String = + s"${decl.name} ~> ${if (isComputing) "" else result.fold("")(_.toString)}" + + } + + def refreshHelper2(raw: PolyNuDecl, v: Var, parTargs: Opt[Ls[ST]]) + (implicit ctx: Ctx): (MutMap[TV, ST], Map[Str, NuParam]) = { + val freshened: MutMap[TV, ST] = MutMap.empty + val rawName = v.name + + val parTP = raw.tparams.lazyZip(parTargs.getOrElse(raw.tparams.map { + case (_, tv, _) => freshVar(tv.prov, S(tv), tv.nameHint)(tv.level) + })).map { case ((tn, _tv, vi), targ) => + val tv = (targ match { + case tv: TV => + println(s"Passing ${tn.name} :: ${_tv} <=< ${tv}") + tv + case _ => + println(s"Assigning ${tn.name} :: ${_tv} := $targ where ${targ.showBounds}") + val tv = + freshVar(_tv.prov, S(_tv), _tv.nameHint, + lbs = _tv.lowerBounds, + ubs = _tv.upperBounds, + )(targ.level) + println(s"Set ${tv} ~> ${_tv}") + assert(tv.assignedTo.isEmpty) + + // * Note: no checks that the assigned variable satisfies the bounds... + // * When we support bounded types, bounds check will be needed at the type definition site + assert(tv.lowerBounds.isEmpty, tv.lowerBounds) + assert(tv.upperBounds.isEmpty, tv.upperBounds) + tv.assignedTo = S(targ) + + // println(s"Assigned ${tv.assignedTo}") + tv + }) + freshened += _tv -> tv + rawName+"#"+tn.name -> NuParam(tn, FieldType(S(tv), tv)(provTODO), isPublic = true)(ctx.lvl) + } + + freshened -> parTP.toMap + } + } diff --git a/shared/src/main/scala/mlscript/Parser.scala b/shared/src/main/scala/mlscript/Parser.scala index abdb7eee19..3895efd95f 100644 --- a/shared/src/main/scala/mlscript/Parser.scala +++ b/shared/src/main/scala/mlscript/Parser.scala @@ -39,13 +39,18 @@ class Parser(origin: Origin, indent: Int = 0, recordLocations: Bool = true) { def NAME[p: P]: P[Var] = locate(ident.map(Var(_))) - def ident[p: P] = Lexer.identifier | "(" ~ operator.! ~ ")" + def ident[p: P]: P[String] = Lexer.identifier | "(" ~ operator.! ~ ")" def NUMBER[p: P]: P[Lit] = locate( P( Lexer.longinteger | Lexer.integer ).map(IntLit) | P( Lexer.floatnumber ).map(DecLit) ) def STRING[p: P]: P[StrLit] = locate(Lexer.stringliteral.map(StrLit(_))) + + def FIELD[p: P]: P[Var] = locate( + P( Lexer.identifier ).map(Var(_)) | + P( Lexer.decimalinteger ).map(n => Var(n.toString)) + ) def expr[p: P]: P[Term] = P( ite | basicExpr ).opaque("expression") @@ -101,7 +106,7 @@ class Parser(origin: Origin, indent: Int = 0, recordLocations: Bool = true) { Index ~~ (NAME ~ ":" ~ (noCommas | suite) | noCommas.map(Var("") -> _)).rep(1, ",").map(_.toList) ~ ",".!.? ~~ Index ).map { case (_, (Var(""), x) :: Nil, N, _) => x - case (i0, xs, _, i1) => Tup(xs.map { case (n, t) => (n optionIf (_.name.nonEmpty), Fld(false, false, t)) }).withLoc(i0, i1, origin) + case (i0, xs, _, i1) => Tup(xs.map { case (n, t) => (n optionIf (_.name.nonEmpty), Fld(FldFlags.empty, t)) }).withLoc(i0, i1, origin) } def booleans[p: P]: P[Term] = P(binops rep (1, kw("and")) rep (1, kw("or"))) // TODO locs @@ -174,7 +179,7 @@ class Parser(origin: Origin, indent: Int = 0, recordLocations: Bool = true) { case (as, ao) => (as ++ ao.toList).reduceLeft((f, a) => App(f, toParams(a))) } - def atomOrSelect[p: P]: P[Term] = P(atom ~ (Index ~~ "." ~ NAME ~~ Index).rep).map { + def atomOrSelect[p: P]: P[Term] = P(atom ~ (Index ~~ "." ~ FIELD ~~ Index).rep).map { case (lhs, sels) => sels.foldLeft(lhs) { case (acc, (i0,str,i1)) => Sel(lhs, str).withLoc(i0, i1, origin) } diff --git a/shared/src/main/scala/mlscript/Token.scala b/shared/src/main/scala/mlscript/Token.scala index d3af89a7f4..041055cac1 100644 --- a/shared/src/main/scala/mlscript/Token.scala +++ b/shared/src/main/scala/mlscript/Token.scala @@ -8,12 +8,14 @@ sealed abstract class Token { def describe: Str = this match { case SPACE => "space" case COMMA => "comma" + case SEMI => "semicolon" case NEWLINE => "newline" case INDENT => "indentation" case DEINDENT => "deindentation" case ERROR => "error" case LITVAL(value) => "literal" - case KEYWORD(name) => s"'$name' keyword" + case KEYWORD(name) => + if (name.headOption.exists(_.isLetter)) s"'$name' keyword" else s"'$name'" case IDENT(name, symbolic) => if (symbolic) "operator" else "identifier" case SELECT(name) => "selector" case OPEN_BRACKET(k) => s"opening ${k.name}" @@ -30,6 +32,7 @@ sealed trait Stroken extends Token case object SPACE extends Token with Stroken case object COMMA extends Token with Stroken +case object SEMI extends Token with Stroken case object NEWLINE extends Token with Stroken // TODO rm case object INDENT extends Token case object DEINDENT extends Token diff --git a/shared/src/main/scala/mlscript/TypeDefs.scala b/shared/src/main/scala/mlscript/TypeDefs.scala index e761d29916..a33177486b 100644 --- a/shared/src/main/scala/mlscript/TypeDefs.scala +++ b/shared/src/main/scala/mlscript/TypeDefs.scala @@ -8,10 +8,16 @@ import scala.annotation.tailrec import mlscript.utils._, shorthands._ import mlscript.Message._ -class TypeDefs extends NuTypeDefs { self: Typer => +class TypeDefs extends NuTypeDefs { Typer: Typer => import TypeProvenance.{apply => tp} + trait AnyTypeDef { + // val kind: TypeDefKind + // val nme: TypeName + // val tparamsargs: List[(TypeName, TypeVariable)] + } + /** * TypeDef holds information about declarations like classes, interfaces, and type aliases * @@ -40,7 +46,7 @@ class TypeDefs extends NuTypeDefs { self: Typer => adtData: Opt[AdtInfo] = N, ) { def allBaseClasses(ctx: Ctx)(implicit traversed: Set[TypeName]): Set[TypeName] = - baseClasses.map(v => TypeName(v.name.decapitalize)) ++ + baseClasses.map(v => TypeName(v.name)) ++ baseClasses.iterator.filterNot(traversed).flatMap(v => ctx.tyDefs.get(v.name).fold(Set.empty[TypeName])(_.allBaseClasses(ctx)(traversed + v))) val (tparams: List[TypeName], targs: List[TypeVariable]) = tparamsargs.unzip @@ -108,13 +114,26 @@ class TypeDefs extends NuTypeDefs { self: Typer => def tparamField(clsNme: TypeName, tparamNme: TypeName): Var = Var(clsNme.name + "#" + tparamNme.name) + def clsNameToNomTag(td: NuTypeDef)(prov: TypeProvenance, ctx: Ctx): ClassTag = { + require((td.kind is Cls) || (td.kind is Mod), td.kind) + ClassTag(Var(td.nme.name), + if(newDefs && td.nme.name =/= "Object") + Set.single(TN("Object")) + | ctx.tyDefs2.get(td.nme.name).map(_.inheritedTags).getOrElse(Set.empty) + else ctx.allBaseClassesOf(td.nme.name) + )(prov) + } def clsNameToNomTag(td: TypeDef)(prov: TypeProvenance, ctx: Ctx): ClassTag = { - require(td.kind is Cls) - ClassTag(Var(td.nme.name.decapitalize), ctx.allBaseClassesOf(td.nme.name))(prov) + require((td.kind is Cls) || (td.kind is Mod), td.kind) + ClassTag(Var(td.nme.name), ctx.allBaseClassesOf(td.nme.name))(prov) } def trtNameToNomTag(td: TypeDef)(prov: TypeProvenance, ctx: Ctx): TraitTag = { require(td.kind is Trt) - TraitTag(Var(td.nme.name.decapitalize))(prov) + TraitTag(Var(td.nme.name), Set.empty)(prov) + } + def trtNameToNomTag(td: NuTypeDef)(prov: TypeProvenance, ctx: Ctx): TraitTag = { + require(td.kind is Trt) + TraitTag(Var(td.nme.name), ctx.tyDefs2.get(td.nme.name).map(_.inheritedTags).getOrElse(Set.empty))(prov) } def baseClassesOf(tyd: mlscript.TypeDef): Set[TypeName] = @@ -139,7 +158,7 @@ class TypeDefs extends NuTypeDefs { self: Typer => { ty match { case tr @ TypeRef(td, targs) => - fieldsOf(tr.expandWith(paramTags), paramTags) + fieldsOf(tr.expandWith(paramTags, selfTy = false), paramTags) case ComposedType(false, l, r) => mergeMap(fieldsOf(l, paramTags), fieldsOf(r, paramTags))(_ && _) case RecordType(fs) => fs.toMap @@ -166,7 +185,7 @@ class TypeDefs extends NuTypeDefs { self: Typer => err(msg"Type names must start with a capital letter", td0.nme.toLoc) td0.copy(nme = td0.nme.copy(n).withLocOf(td0.nme)).withLocOf(td0) } - if (primitiveTypes.contains(n)) { + if (reservedTypeNames.contains(n)) { err(msg"Type name '$n' is reserved.", td.nme.toLoc) } td.tparams.groupBy(_.name).foreach { case s -> tps if tps.sizeIs > 1 => err( @@ -236,8 +255,8 @@ class TypeDefs extends NuTypeDefs { self: Typer => // }() val rightParents = td.kind match { case Als => checkCycle(td.bodyTy)(Set.single(L(td.nme))) - case Nms => - err(msg"a namespace cannot inherit from others", prov.loco) + case Mod => + err(msg"modules cannot inherit from other types", prov.loco) false case k: ObjDefKind => val parentsClasses = MutSet.empty[TypeRef] @@ -258,12 +277,15 @@ class TypeDefs extends NuTypeDefs { self: Typer => } else checkParents(tr.expand) case Trt => checkParents(tr.expand) - case Nms => - err(msg"cannot inherit from a namespace", prov.loco) + case Mod => + err(msg"cannot inherit from a module", prov.loco) false case Als => err(msg"cannot inherit from a type alias", prov.loco) false + case Mxn => + err(msg"cannot inherit from a mixin", prov.loco) + false } case ComposedType(false, l, r) => checkParents(l) && checkParents(r) case ComposedType(true, l, r) => @@ -351,16 +373,18 @@ class TypeDefs extends NuTypeDefs { self: Typer => PolymorphicType(MinLevel, FunctionType( singleTup(tv), tv & nomTag & RecordType.mk(tparamTags)(noProv) )(originProv(td.nme.toLoc, "trait constructor", td.nme.name))) + case _ => ??? // TODO } ctx += n.name -> VarSymbol(ctor, Var(n.name)) } true } checkParents(td.bodyTy) && checkCycle(td.bodyTy)(Set.single(L(td.nme))) && checkAbstractAddCtors + case _ => ??? // TODO } def checkRegular(ty: SimpleType)(implicit reached: Map[Str, Ls[SimpleType]]): Bool = ty match { case tr @ TypeRef(defn, targs) => reached.get(defn.name) match { - case None => checkRegular(tr.expandWith(false))(reached + (defn.name -> targs)) + case None => checkRegular(tr.expandWith(false, selfTy = false))(reached + (defn.name -> targs)) case Some(tys) => // Note: this check *has* to be relatively syntactic because // the termination of constraint solving relies on recursive type occurrences @@ -368,9 +392,9 @@ class TypeDefs extends NuTypeDefs { self: Typer => // and to have the same has hashCode (see: the use of a cache MutSet) if (defn === td.nme && tys =/= targs) { err(msg"Type definition is not regular: it occurs within itself as ${ - expandType(tr).show + expandType(tr).show(Typer.newDefs) }, but is defined as ${ - expandType(TypeRef(defn, td.targs)(noProv)).show + expandType(TypeRef(defn, td.targs)(noProv)).show(Typer.newDefs) }", td.toLoc)(raise) false } else true @@ -405,7 +429,7 @@ class TypeDefs extends NuTypeDefs { self: Typer => // This is because implicit method calls always default to the parent methods. case S(MethodType(_, _, parents, _)) if { val bcs = ctx.allBaseClassesOf(tn.name) - parents.forall(prt => bcs(TypeName(prt.name.decapitalize))) + parents.forall(prt => bcs(TypeName(prt.name))) } => // If this class is one of the base classes of the parent(s) of the currently registered method, // then we need to register the new method. Only happens when the class definitions are "out-of-order", @@ -418,7 +442,7 @@ class TypeDefs extends NuTypeDefs { self: Typer => // class A // method F: int case S(MethodType(_, _, parents, _)) if { - val v = TypeName(tn.name.decapitalize) + val v = TypeName(tn.name) parents.forall(prt => ctx.allBaseClassesOf(prt.name).contains(v)) } => ctx.addMth(N, mn, mthTy) // If this class is unrelated to the parent(s) of the currently registered method, @@ -512,7 +536,7 @@ class TypeDefs extends NuTypeDefs { self: Typer => case _ => Nil } def go(md: MethodDef[_ <: Term \/ Type]): (Str, MethodType) = { - val thisTag = TraitTag(Var("this"))(noProv) // or Skolem?! + val thisTag = TraitTag(Var("this"), Set.empty)(noProv) // or Skolem?! // val thisTag = SkolemTag(thisCtx.lvl/*TODO correct?*/, Var("this"))(noProv) val thisTy = thisTag & tr thisCtx += "this" -> VarSymbol(thisTy, Var("this")) @@ -546,7 +570,7 @@ class TypeDefs extends NuTypeDefs { self: Typer => } rhs.fold(_ => defined, _ => declared) += nme.name -> nme.toLoc val dummyTargs2 = tparams.map(p => - TraitTag(Var(p.name))(originProv(p.toLoc, "method type parameter", p.name))) // FIXME or Skolem?! + TraitTag(Var(p.name), Set.empty)(originProv(p.toLoc, "method type parameter", p.name))) // FIXME or Skolem?! val targsMap2 = targsMap ++ tparams.iterator.map(_.name).zip(dummyTargs2).toMap val reverseRigid2 = reverseRigid ++ dummyTargs2.map(t => t -> freshVar(t.prov, N, S(t.id.idStr))(thisCtx.lvl + 1)) + @@ -758,33 +782,7 @@ class TypeDefs extends NuTypeDefs { self: Typer => println(s"DONE") } - case class VarianceInfo(isCovariant: Bool, isContravariant: Bool) { - - /** Combine two pieces of variance information together - */ - def &&(that: VarianceInfo): VarianceInfo = - VarianceInfo(isCovariant && that.isCovariant, isContravariant && that.isContravariant) - - /* Flip the current variance if it encounters a contravariant position - */ - def flip: VarianceInfo = VarianceInfo(isContravariant, isCovariant) - - override def toString: Str = show - - def show: Str = this match { - case (VarianceInfo(true, true)) => "±" - case (VarianceInfo(false, true)) => "-" - case (VarianceInfo(true, false)) => "+" - case (VarianceInfo(false, false)) => "=" - } - } - - object VarianceInfo { - val bi: VarianceInfo = VarianceInfo(true, true) - val co: VarianceInfo = VarianceInfo(true, false) - val contra: VarianceInfo = VarianceInfo(false, true) - val in: VarianceInfo = VarianceInfo(false, false) - } - type VarianceStore = MutMap[TypeVariable, VarianceInfo] + object VarianceStore { def empty: VarianceStore = MutMap.empty } + } diff --git a/shared/src/main/scala/mlscript/TypeSimplifier.scala b/shared/src/main/scala/mlscript/TypeSimplifier.scala index 789aa8d509..6f821ceda6 100644 --- a/shared/src/main/scala/mlscript/TypeSimplifier.scala +++ b/shared/src/main/scala/mlscript/TypeSimplifier.scala @@ -16,12 +16,13 @@ trait TypeSimplifier { self: Typer => /** Remove bounds that are not reachable by traversing the type following variances. * Note that doing this on annotated type signatures would need to use polarity None * because a type signature can both be used (positively) and checked against (negatively). */ - def removeIrrelevantBounds(ty: SimpleType, pol: Opt[Bool], inPlace: Bool = false) - (implicit ctx: Ctx): SimpleType = + def removeIrrelevantBounds(ty: TypeLike, pol: Opt[Bool], inPlace: Bool = false) + (implicit ctx: Ctx): TypeLike = { val _ctx = ctx val allVarPols = ty.getVarsPol(PolMap(pol)) + // println("!!"+ty.childrenPol(PolMap(pol))) println(s"allVarPols: ${printPols(allVarPols)}") val renewed = MutMap.empty[TypeVariable, TypeVariable] @@ -31,6 +32,11 @@ trait TypeSimplifier { self: Typer => if (inPlace) tv else freshVar(noProv, S(tv), tv.nameHint)(tv.level) tap { fv => println(s"Renewed $tv ~> $fv") }) + def processLike(ty: TypeLike): TypeLike = ty match { + case ty: ST => process(ty, N) + case OtherTypeLike(tu) => + tu.map(process(_, N)) + } def process(ty: ST, parent: Opt[Bool -> TV], canDistribForall: Opt[Level] = N): ST = // trace(s"process($ty) $canDistribForall") { ty match { @@ -85,24 +91,52 @@ trait TypeSimplifier { self: Typer => ProvType(process(ty, parent, canDistribForall = canDistribForall))(ty.prov) case ProvType(ty) => process(ty, parent, canDistribForall = canDistribForall) - case tr @ TypeRef(defn, targs) if builtinTypes.contains(defn) => process(tr.expand, parent) + case tr @ TypeRef(defn, targs) if builtinTypes.contains(defn) && tr.canExpand => + process(tr.expandOrCrash, parent) case RecordType(fields) => RecordType.mk(fields.flatMap { case (v @ Var(fnme), fty) => - // * We make a pass to transform the LB and UB of variant type parameter fields into their exterma + // * We make a pass to transform the LB and UB of variant type parameter fields into their extrema val prefix = fnme.takeWhile(_ =/= '#') val postfix = fnme.drop(prefix.length + 1) lazy val default = fty.update(process(_ , N), process(_ , N)) - if (postfix.isEmpty) v -> default :: Nil - else { - val td = ctx.tyDefs(prefix) - td.tvarVariances.fold(v -> default :: Nil)(tvv => - tvv(td.tparamsargs.find(_._1.name === postfix).getOrElse(die)._2) match { - case VarianceInfo(true, true) => Nil - case VarianceInfo(co, contra) => - if (co) v -> FieldType(S(BotType), process(fty.ub, N))(fty.prov) :: Nil - else if (contra) v -> FieldType(fty.lb.map(process(_, N)), TopType)(fty.prov) :: Nil - else v -> default :: Nil - }) + if (postfix.isEmpty || prefix.isEmpty) v -> default :: Nil + else ctx.tyDefs.get(prefix) match { + case S(td) => + td.tvarVariances.fold(v -> default :: Nil)(tvv => + tvv(td.tparamsargs.find(_._1.name === postfix).getOrElse(die)._2) match { + case VarianceInfo(true, true) => Nil + case VarianceInfo(co, contra) => + if (co) v -> FieldType(S(BotType), process(fty.ub, N))(fty.prov) :: Nil + else if (contra) v -> FieldType(fty.lb.map(process(_, N)), TopType)(fty.prov) :: Nil + else v -> default :: Nil + }) + case N => + // v -> default :: Nil + ctx.tyDefs2.get(prefix) match { + case S(info) => + info.result match { + case S(cls: TypedNuCls) => + cls.varianceOf(cls.tparams.find(_._1.name === postfix).getOrElse(die)._2) match { + case VarianceInfo(true, true) => Nil + case VarianceInfo(co, contra) => + if (co) v -> FieldType(S(BotType), process(fty.ub, N))(fty.prov) :: Nil + else if (contra) v -> FieldType(fty.lb.map(process(_, N)), TopType)(fty.prov) :: Nil + else v -> default :: Nil + } + case S(trt: TypedNuTrt) => // TODO factor w/ above & generalize + trt.tparams.iterator.find(_._1.name === postfix).flatMap(_._3).getOrElse(VarianceInfo.in) match { + case VarianceInfo(true, true) => Nil + case VarianceInfo(co, contra) => + if (co) v -> FieldType(S(BotType), process(fty.ub, N))(fty.prov) :: Nil + else if (contra) v -> FieldType(fty.lb.map(process(_, N)), TopType)(fty.prov) :: Nil + else v -> default :: Nil + } + case S(_) => ??? // TODO: + case N => + ??? // TODO use info.explicitVariances + } + case N => lastWords(s"'$prefix' not found") + } } })(ty.prov) @@ -126,15 +160,21 @@ trait TypeSimplifier { self: Typer => } // }(r => s"= $r") - process(ty, N) + processLike(ty) } /** Transform the type recursively, putting everything in Disjunctive Normal Forms and reconstructing class types - * from their structural components. */ - def normalizeTypes_!(st: SimpleType, pol: Opt[Bool])(implicit ctx: Ctx): SimpleType = + * from their structural components. + * Note that performing this _in place_ is not fully correct, + * as this will lead us to assume TV bounds while simplifying the TV bounds themselves! + * – see [test:T3] – + * However, I think this only manifests in contrived manually-constructed corner cases like + * unguarded recursive types such as `C['a] | 'a as 'a`. + */ + def normalizeTypes_!(st: TypeLike, pol: Opt[Bool])(implicit ctx: Ctx): TypeLike = { val _ctx = ctx @@ -184,12 +224,16 @@ trait TypeSimplifier { self: Typer => } val traitPrefixes = - tts.iterator.collect{ case TraitTag(Var(tagNme)) => tagNme.capitalize }.toSet + tts.iterator.collect{ case TraitTag(Var(tagNme), _) => tagNme.capitalize }.toSet bo match { - case S(cls @ ClassTag(Var(tagNme), ps)) if !primitiveTypes.contains(tagNme) => - val clsNme = tagNme.capitalize - val clsTyNme = TypeName(tagNme.capitalize) + case S(cls @ ClassTag(Var(tagNme), ps)) + if !primitiveTypes.contains(tagNme) + && ctx.tyDefs.contains(tagNme.capitalize) + && !newDefs + => + val clsNme = tagNme.capitalize // TODO rm capitalize + val clsTyNme = TypeName(clsNme) val td = ctx.tyDefs(clsNme) val rcdMap = rcd.fields.toMap @@ -243,7 +287,7 @@ trait TypeSimplifier { self: Typer => })(noProv) println(s"typeRef ${typeRef}") - val clsFields = fieldsOf(typeRef.expandWith(paramTags = true), paramTags = true) + val clsFields = fieldsOf(typeRef.expandWith(paramTags = true, selfTy = false), paramTags = true) println(s"clsFields ${clsFields.mkString(", ")}") val cleanPrefixes = ps.map(_.name.capitalize) + clsNme ++ traitPrefixes @@ -287,6 +331,77 @@ trait TypeSimplifier { self: Typer => trs3.valuesIterator.foldLeft(withTraits)(_ & _) + case S(cls @ ClassTag(Var(clsNme), ps)) + if !primitiveTypes.contains(clsNme) + && ctx.tyDefs2.contains(clsNme) + && ctx.tyDefs2(clsNme).result.isDefined + => + val clsTyNme = TypeName(clsNme) + val lti = ctx.tyDefs2(clsNme) + val cls = lti.result match { + case S(r: TypedNuCls) => r + case _ => die + } + + val rcdMap = rcd.fields.toMap + + val rcd2 = rcd.copy(rcd.fields.mapValues(_.update(go(_, pol.map(!_)), go(_, pol))))(rcd.prov) + println(s"rcd2 ${rcd2}") + + // val vs = + // // td.getVariancesOrDefault + // // Map.empty[TV, VarianceInfo].withDefaultValue(VarianceInfo.in) + // cls.variances + + // * Reconstruct a TypeRef from its current structural components + val typeRef = TypeRef(cls.td.nme, cls.tparams.zipWithIndex.map { case ((tp, tv, vi), tpidx) => + val fieldTagNme = tparamField(clsTyNme, tp) + val fromTyRef = trs2.get(clsTyNme).map(_.targs(tpidx) |> { ta => FieldType(S(ta), ta)(noProv) }) + fromTyRef.++(rcd2.fields.iterator.filter(_._1 === fieldTagNme).map(_._2)) + .foldLeft((BotType: ST, TopType: ST)) { + case ((acc_lb, acc_ub), FieldType(lb, ub)) => + (acc_lb | lb.getOrElse(BotType), acc_ub & ub) + }.pipe { + case (lb, ub) => + cls.varianceOf(tv) match { + case VarianceInfo(true, true) => TypeBounds.mk(BotType, TopType) + case VarianceInfo(false, false) => TypeBounds.mk(lb, ub) + case VarianceInfo(co, contra) => + if (co) ub else lb + } + } + })(noProv) + println(s"typeRef ${typeRef}") + + val clsFields = fieldsOf(typeRef.expandWith(paramTags = true, selfTy = false), paramTags = true) + println(s"clsFields ${clsFields.mkString(", ")}") + + val cleanPrefixes = ps.map(_.name.capitalize) + clsNme ++ traitPrefixes + + val cleanedRcd = RecordType( + rcd2.fields.filterNot { case (field, fty) => + // * This is a bit messy, but was the only way I was able to achieve maximal simplification: + // * We remove fields that are already inclued by definition of the class by testing for subtyping + // * with BOTH the new normalized type (from `clsFields`) AND the old one too (from `rcdMap`). + // * The reason there's a difference is probably because: + // * - Subtye checking with <:< is an imperfect heuristic and may stop working after normalizing. + // * - Recursive types will be normalized progressively... + // * at this point we may look at some bounds that have not yet been normalized. + clsFields.get(field).exists(cf => cf <:< fty || + rcdMap.get(field).exists(cf <:< _)) + } + )(rcd2.prov) + + val rcd2Fields = rcd2.fields.unzip._1.toSet + + val withTraits = tts.toArray.sorted // TODO also filter out tts that are inherited by the class + .foldLeft(typeRef & cleanedRcd: ST)(_ & _) + + val trs3 = trs2 - cls.nme // TODO also filter out class refs that are inherited by the class + + trs3.valuesIterator.foldLeft(withTraits)(_ & _) + + case _ => lazy val nFields = rcd.fields .filterNot(traitPrefixes contains _._1.name.takeWhile(_ =/= '#')) @@ -296,20 +411,10 @@ trait TypeSimplifier { self: Typer => val arity = fs.size val (componentFields, rcdFields) = rcd.fields .filterNot(traitPrefixes contains _._1.name.takeWhile(_ =/= '#')) - .partitionMap(f => - if (f._1.name.length > 1 && f._1.name.startsWith("_")) { - val namePostfix = f._1.name.tail - if (namePostfix.forall(_.isDigit)) { - val index = namePostfix.toInt - if (index <= arity && index > 0) L(index -> f._2) - else R(f) - } - else R(f) - } else R(f) - ) + .partitionMap(f => f._1.toIndexOption.filter((0 until arity).contains).map(_ -> f._2).toLeft(f)) val componentFieldsMap = componentFields.toMap val tupleComponents = fs.iterator.zipWithIndex.map { case ((nme, ty), i) => - nme -> (ty && componentFieldsMap.getOrElse(i + 1, TopType.toUpper(noProv))).update(go(_, pol.map(!_)), go(_, pol)) + nme -> (ty && componentFieldsMap.getOrElse(i, TopType.toUpper(noProv))).update(go(_, pol.map(!_)), go(_, pol)) }.toList S(TupleType(tupleComponents)(tt.prov)) -> rcdFields.mapValues(_.update(go(_, pol.map(!_)), go(_, pol))) case S(ct: ClassTag) => S(ct) -> nFields @@ -360,13 +465,17 @@ trait TypeSimplifier { self: Typer => } } + def goLike(ty: TL, pol: Opt[Bool], canDistribForall: Opt[Level] = N): TL = trace(s"normLike[${printPol(pol)}] $ty") { ty match { + case ty: ST => go(ty, pol) + case OtherTypeLike(tu) => tu.mapPol(pol, true)((p, t) => go(t, p)) + }}() def go(ty: ST, pol: Opt[Bool], canDistribForall: Opt[Level] = N): ST = trace(s"norm[${printPol(pol)}] $ty") { pol match { case S(p) => helper(DNF.mk(MaxLevel, Nil, ty, p)(ctx, ptr = true, etf = false), pol, canDistribForall) case N => - val dnf1 = DNF.mk(MaxLevel, Nil, ty, false)(ctx, ptr = true, etf = false) - val dnf2 = DNF.mk(MaxLevel, Nil, ty, true)(ctx, ptr = true, etf = false) - TypeBounds.mk(helper(dnf1, S(false), canDistribForall), helper(dnf2, S(true), canDistribForall)) + val dnf1 = DNF.mk(MaxLevel, Nil, ty, false)(ctx, ptr = true, etf = false) + val dnf2 = DNF.mk(MaxLevel, Nil, ty, true)(ctx, ptr = true, etf = false) + TypeBounds.mk(helper(dnf1, S(false), canDistribForall), helper(dnf2, S(true), canDistribForall)) } }(r => s"~> $r") @@ -382,15 +491,18 @@ trait TypeSimplifier { self: Typer => } } - go(st, pol) + goLike(st, pol) } /** Remove polar type variables, unify indistinguishable ones, and inline the bounds of non-recursive ones. */ - def simplifyType(st: SimpleType, removePolarVars: Bool, pol: Opt[Bool], inlineBounds: Bool = true)(implicit ctx: Ctx): SimpleType = { + def simplifyType(st: TypeLike, removePolarVars: Bool, pol: Opt[Bool], inlineBounds: Bool = true)(implicit ctx: Ctx): TypeLike = { + + // * There are two main analyses, which are quite subtle. + // * TODO: add assertion to check that their results are consistent! // * * Analysis 1: count number of TV occurrences at each polarity @@ -406,7 +518,7 @@ trait TypeSimplifier { self: Typer => // * coincides with that of the later `transform` function. // * In particular, the traversal of fields with identical UB/LB is considered invariant. object Analyze1 extends Traverser2.InvariantFields { - override def apply(pol: PolMap)(st: ST): Unit = trace(s"analyze1[${printPol(pol)}] $st") { + override def apply(pol: PolMap)(st: ST): Unit = trace(s"analyze1[${(pol)}] $st") { st match { case tv: TV => pol(tv) match { @@ -441,7 +553,7 @@ trait TypeSimplifier { self: Typer => } }() } - Analyze1(PolMap(pol))(st) + Analyze1.applyLike(PolMap(pol))(st) println(s"[inv] ${occursInvariantly.mkString(", ")}") println(s"[nums] ${occNums.iterator @@ -453,7 +565,7 @@ trait TypeSimplifier { self: Typer => // * * Analysis 2: find the polar co-occurrences of each TV - // * Note that for negatively-quantified type variables, the notion of co-occurrence is reversed! + // * Note: for negatively-quantified vars, the notion of co-occurrence is reversed (wrt unions/inters)... val coOccurrences: MutMap[(Bool, TypeVariable), MutSet[SimpleType]] = LinkedHashMap.empty @@ -516,8 +628,9 @@ trait TypeSimplifier { self: Typer => */ - def analyze2(st: SimpleType, pol: PolMap): Unit = - Analyze2(pol)(st.unwrapProvs) + // FIXME Currently we don't traverse TVs witht he correct PolMap, which introduces misatches with other analyses in tricky cases + def analyze2(st: TL, pol: PolMap): Unit = + Analyze2.applyLike(pol)(st.unwrapProvs) object Analyze2 extends Traverser2 { override def apply(pol: PolMap)(st: ST): Unit = trace(s"analyze2[${(pol)}] $st") { @@ -640,7 +753,7 @@ trait TypeSimplifier { self: Typer => // * Note: a more precise version could be the following, // * but it doesn't seem to change anything in our test suite, so I left if commented for now: // // * Only consider recursive those variables that recursive in their *reachable* bounds: - // occNums.contains(true -> v) && v.isPosRecursive_$ || occNums.contains(false -> v) && v.isNegRecursive_$ + // occNums.contains(true -> v) && v.isPosRecursive_$(false) || occNums.contains(false -> v) && v.isNegRecursive_$(false) )).toSet var recVars = computeRecVars @@ -814,6 +927,10 @@ trait TypeSimplifier { self: Typer => case N => merge(pol, if (pol) tv.lowerBounds else tv.upperBounds) }, polmap.at(tv.level, pol), parents, canDistribForall) + def transformLike(ty: TL, pol: PolMap): TL = ty match { + case ty: ST => transform(ty, pol, semp) + case OtherTypeLike(tu) => tu.mapPolMap(pol)((p,t) => transform(t, p, semp)) + } def transform(st: SimpleType, pol: PolMap, parents: Set[TV], canDistribForall: Opt[Level] = N): SimpleType = trace(s"transform[${printPol(pol)}] $st (${parents.mkString(", ")}) $pol $canDistribForall") { def transformField(f: FieldType): FieldType = f match { @@ -835,7 +952,7 @@ trait TypeSimplifier { self: Typer => case ot @ Overload(as) => ot.mapAltsPol(pol)((p, t) => transform(t, p, parents, canDistribForall)) case SkolemTag(id) => transform(id, pol, parents) - case _: TypeTag | ExtrType(_) => st + case _: ObjectTag | _: Extruded | _: ExtrType => st case tv: TypeVariable if parents(tv) => pol(tv) match { case S(true) => BotType @@ -880,6 +997,8 @@ trait TypeSimplifier { self: Typer => case S(p) if inlineBounds && !occursInvariantly(tv) && !recVars.contains(tv) => // * 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 + // else mergeTransform(false, pol, tv, Set.single(tv), canDistribForall) & res if (p) mergeTransform(true, pol, tv, parents + tv, canDistribForall) | res else mergeTransform(false, pol, tv, parents + tv, canDistribForall) & res case poltv if (!wasDefined) => @@ -941,6 +1060,10 @@ trait TypeSimplifier { self: Typer => pol.base.fold[ST](TypeBounds.mkSafe( transform(lb, PolMap.neg, parents, canDistribForall), transform(ub, PolMap.pos, parents, canDistribForall), + // transform(lb, pol, parents, canDistribForall), + // transform(ub, pol, parents, canDistribForall), + // transform(lb, pol.contravar, parents, canDistribForall), + // transform(ub, pol.covar, parents, canDistribForall), noProv ))(p => if (p) transform(ub, pol, parents) else transform(lb, pol, parents) @@ -965,7 +1088,7 @@ trait TypeSimplifier { self: Typer => } }(r => s"~> $r") - transform(st, PolMap(pol), semp) + transformLike(st, PolMap(pol)) } @@ -978,7 +1101,7 @@ trait TypeSimplifier { self: Typer => * So if no other upper bounds end up in ?a AND ?a is polar * (so that ?a occurrences are indistinguishable from `{x: ?a}`), * we'll eventually want to refactor ?b's recursive upper bound structure into just `?b case (tv, S(pol)) => if (pol) (true, tv.lowerBounds.foldLeft(BotType: ST)(_ | _)) -> tv else (false, tv.upperBounds.foldLeft(TopType: ST)(_ &- _)) -> tv - }.toMap + }.filter { case ((pol, bnd), tv) => bnd.getVarsImpl(includeBounds = false).contains(tv) }.toMap println(s"consed: $consed") @@ -1016,6 +1139,7 @@ trait TypeSimplifier { self: Typer => tv case _ => lazy val mapped = st.mapPol(pol, smart = true)(process(_, _, parent)) + // println(s"mapped $mapped") pol match { case S(p) => // println(s"!1! ${st} ${consed.get(p -> st)}") @@ -1037,14 +1161,19 @@ trait TypeSimplifier { self: Typer => } // }(r => s"~> $r") - process(S(pol), st, N) + def processLike(pol: Opt[Bool], ty: TL): TL = ty match { + case ty: ST => process(pol, ty, N) + case OtherTypeLike(tu) => tu.mapPol(pol, smart = true)(process(_, _, N)) + } + + processLike(S(pol), st) } /** Unify polar recursive type variables that have the same structure. * For example, `?a <: {x: ?a}` and `?b <: {x: ?b}` will be unified if they are bith polar. */ - def factorRecursiveTypes_!(st: SimpleType, approximateRecTypes: Bool, pol: Opt[Bool])(implicit ctx: Ctx): SimpleType = { + def factorRecursiveTypes_!(st: TypeLike, approximateRecTypes: Bool, pol: Opt[Bool])(implicit ctx: Ctx): TypeLike = { val allVarPols = st.getVarsPol(PolMap(pol)) println(s"allVarPols: ${printPols(allVarPols)}") @@ -1084,7 +1213,7 @@ trait TypeSimplifier { self: Typer => (fields1.size === fields2.size || nope) && fields1.map(_._2).lazyZip(fields2.map(_._2)).forall(unifyF) case (FunctionType(lhs1, rhs1), FunctionType(lhs2, rhs2)) => unify(lhs1, lhs2) && unify(rhs1, rhs2) case (Without(base1, names1), Without(base2, names2)) => unify(base1, base2) && (names1 === names2 || nope) - case (TraitTag(id1), TraitTag(id2)) => id1 === id2 || nope + case (TraitTag(id1, _), TraitTag(id2, _)) => id1 === id2 || nope case (SkolemTag(id1), SkolemTag(id2)) => id1 === id2 || nope case (ExtrType(pol1), ExtrType(pol2)) => pol1 === pol2 || nope case (TypeBounds(lb1, ub1), TypeBounds(lb2, ub2)) => @@ -1121,7 +1250,7 @@ trait TypeSimplifier { self: Typer => println(s"[subs] ${varSubst}") - if (varSubst.nonEmpty) subst(st, varSubst.toMap, substInMap = true) else st + if (varSubst.nonEmpty) substLike(st, varSubst.toMap, substInMap = true) else st } @@ -1130,7 +1259,7 @@ trait TypeSimplifier { self: Typer => abstract class SimplifyPipeline { def debugOutput(msg: => Str): Unit - def apply(st: ST, pol: Opt[Bool], removePolarVars: Bool = true)(implicit ctx: Ctx): ST = { + def apply(st: TL, pol: Opt[Bool], removePolarVars: Bool = true)(implicit ctx: Ctx): TypeLike = { var cur = st debugOutput(s"⬤ Initial: ${cur}") @@ -1169,7 +1298,6 @@ trait TypeSimplifier { self: Typer => // * by merging things like function types together... // * So we need another pass of simplification! cur = simplifyType(cur, removePolarVars, pol) - // cur = simplifyType(simplifyType(cur)(ct) debugOutput(s"⬤ Resim: ${cur}") debugOutput(s" where: ${cur.showBounds}") diff --git a/shared/src/main/scala/mlscript/Typer.scala b/shared/src/main/scala/mlscript/Typer.scala index d7fbff1f3e..f45478abea 100644 --- a/shared/src/main/scala/mlscript/Typer.scala +++ b/shared/src/main/scala/mlscript/Typer.scala @@ -2,20 +2,19 @@ package mlscript import scala.collection.mutable import scala.collection.mutable.{Map => MutMap, Set => MutSet} -import scala.collection.immutable.{SortedMap, SortedSet} +import scala.collection.immutable.{SortedSet, SortedMap} +import Set.{empty => semp} import scala.util.chaining._ import scala.annotation.tailrec -import mlscript.utils._ -import shorthands._ +import mlscript.utils._, shorthands._ import mlscript.Message._ -import mlscript.codegen.Helpers /** A class encapsulating type inference state. * It uses its own internal representation of types and type variables, using mutable data structures. * Inferred SimpleType values are then turned into CompactType values for simplification. * 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) +class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val newDefs: Bool) extends ucs.Desugarer with TypeSimplifier { def funkyTuples: Bool = false @@ -36,7 +35,6 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) var recordProvenances: Boolean = true - type Raise = Diagnostic => Unit type Binding = Str -> SimpleType type Bindings = Map[Str, SimpleType] @@ -66,8 +64,8 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) lvl: Int, inPattern: Bool, tyDefs: Map[Str, TypeDef], - inRecursiveDef: Opt[Var], - nuTyDefs: Map[Str, TypedNuTypeDef], + tyDefs2: MutMap[Str, DelayedTypeInfo], + inRecursiveDef: Opt[Var], // TODO rm extrCtx: ExtrCtx, ) { def +=(b: Str -> TypeInfo): Unit = env += b @@ -88,7 +86,7 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) val res = k(newCtx) val ec = newCtx.extrCtx assert(constrainedTypes || newCtx.extrCtx.isEmpty) - trace(s"UNSTASHING... (out)") { + if (ec.nonEmpty) trace(s"UNSTASHING... (out)") { implicit val ctx: Ctx = this ec.foreach { case (tv, bs) => bs.foreach { case (true, b) => constrain(b, tv) @@ -118,7 +116,7 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) println(s"Inferred poly constr: $cty —— where ${cty.showBounds}") val cty_fresh = - // * Samnity check: uncommenting this should change nothing (modulo type simplification randomness) + // * Sanity check: uncommenting this should change nothing (modulo type simplification randomness) // cty.freshenAbove(lvl, false) cty @@ -144,24 +142,40 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) tyDefs.get(name).fold(Set.empty[TypeName])(_.allBaseClasses(this)(Set.empty))) } object Ctx { - def init: Ctx = Ctx( + private val initBase: Ctx = Ctx( parent = N, env = MutMap.from(builtinBindings.iterator.map(nt => nt._1 -> VarSymbol(nt._2, Var(nt._1)))), mthEnv = MutMap.empty, lvl = MinLevel, inPattern = false, tyDefs = Map.from(builtinTypes.map(t => t.nme.name -> t)), + tyDefs2 = MutMap.empty, inRecursiveDef = N, - nuTyDefs = Map.empty, MutMap.empty, ) + def init: Ctx = if (!newDefs) initBase else { + val res = initBase.copy( + tyDefs2 = MutMap.from(nuBuiltinTypes.map { t => + val lti = new DelayedTypeInfo(t, Map.empty)(initBase, e => lastWords(e.theMsg)) + initBase.env += t.nme.name -> lti + t.nme.name -> lti + }), + ) + implicit val raise: Raise = throw _ + res.tyDefs2.valuesIterator.foreach { dti => + val mem = dti.complete() + // * Not strictly necessary, but it makes sense to use the completed member symbols: + res.env += mem.name -> CompletedTypeInfo(mem) + } + res + } val empty: Ctx = init } implicit def lvl(implicit ctx: Ctx): Int = ctx.lvl import TypeProvenance.{apply => tp} - def ttp(trm: Term, desc: Str = ""): TypeProvenance = - TypeProvenance(trm.toLoc, if (desc === "") trm.describe else desc) + def ttp(trm: Term, desc: Str = "", isType: Bool = false): TypeProvenance = + TypeProvenance(trm.toLoc, if (desc === "") trm.describe else desc, isType = isType) def originProv(loco: Opt[Loc], desc: Str, name: Str): TypeProvenance = { tp(loco, desc, S(name), isType = true) // ^ If we did not treat "origin provenances" differently, @@ -176,17 +190,34 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) override def toString: Str = "[NO PROV]" } def noProv: TypeProvenance = NoProv + def provTODO: TypeProvenance = noProv def noTyProv: TypeProvenance = TypeProvenance(N, "type", isType = true) + private def sing[A](x: A): Set[A] = Set.single(x) + val TopType: ExtrType = ExtrType(false)(noTyProv) val BotType: ExtrType = ExtrType(true)(noTyProv) - val UnitType: ClassTag = ClassTag(Var("unit"), Set.empty)(noTyProv) - val BoolType: ClassTag = ClassTag(Var("bool"), Set.empty)(noTyProv) - val TrueType: ClassTag = ClassTag(Var("true"), Set.single(TypeName("bool")))(noTyProv) - val FalseType: ClassTag = ClassTag(Var("false"), Set.single(TypeName("bool")))(noTyProv) - val IntType: ClassTag = ClassTag(Var("int"), Set.single(TypeName("number")))(noTyProv) - val DecType: ClassTag = ClassTag(Var("number"), Set.empty)(noTyProv) - val StrType: ClassTag = ClassTag(Var("string"), Set.empty)(noTyProv) + + val UnitType: ClassTag = if (newDefs) ClassTag(UnitLit(true), semp)(noTyProv) + else ClassTag(Var("unit"), semp)(noTyProv) + + val BoolType: ST = if (newDefs) TR(TN("Bool"), Nil)(noTyProv) + else ClassTag(Var("bool"), semp)(noTyProv) + val ObjCls: ClassTag = ClassTag(Var("Object"), semp)(noTyProv) + val ObjType: ST = if (newDefs) TR(TN("Object"), Nil)(noTyProv) + else TopType + val IntType: ST = if (newDefs) TR(TN("Int"), Nil)(noTyProv) + else ClassTag(Var("int"), sing(TN("number")))(noTyProv) + val DecType: ST = if (newDefs) TR(TN("Num"), Nil)(noTyProv) + else ClassTag(Var("number"), semp)(noTyProv) + val StrType: ST = if (newDefs) TR(TN("Str"), Nil)(noTyProv) + else ClassTag(Var("string"), semp)(noTyProv) + val TrueType: ST = if (newDefs) TR(TN("true"), Nil)(noTyProv) + else ClassTag(Var("true"), sing(TN("bool")))(noTyProv) + val FalseType: ST = if (newDefs) TR(TN("false"), Nil)(noTyProv) + else ClassTag(Var("false"), sing(TN("bool")))(noTyProv) + + val EqlTag: TraitTag = TraitTag(Var("Eql"), Set.empty)(noProv) val ErrTypeId: SimpleTerm = Var("error") @@ -195,24 +226,38 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) List("unit" -> UnitType, "bool" -> BoolType, "int" -> IntType, "number" -> DecType, "string" -> StrType, "anything" -> TopType, "nothing" -> BotType) + private val preludeLoc = Loc(0, 0, Origin("", 0, new FastParseHelpers(""))) + + val nuBuiltinTypes: Ls[NuTypeDef] = Ls( + NuTypeDef(Cls, TN("Object"), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc)), + NuTypeDef(Trt, TN("Eql"), (S(VarianceInfo.contra), TN("A")) :: Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc)), + NuTypeDef(Cls, TN("Num"), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc)), + NuTypeDef(Cls, TN("Int"), Nil, N, N, N, Var("Num") :: Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc)), + NuTypeDef(Cls, TN("Bool"), Nil, N, N, S(Union(TN("true"), TN("false"))), Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc)), + NuTypeDef(Mod, TN("true"), Nil, N, N, N, Var("Bool") :: Nil, N, N, TypingUnit(Nil))(N, N), + NuTypeDef(Mod, TN("false"), Nil, N, N, N, Var("Bool") :: Nil, N, N, TypingUnit(Nil))(N, N), + NuTypeDef(Cls, TN("Str"), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc)), + NuTypeDef(Als, TN("undefined"), Nil, N, N, S(Literal(UnitLit(true))), Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc)), + NuTypeDef(Als, TN("null"), Nil, N, N, S(Literal(UnitLit(false))), Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc)), + ) val builtinTypes: Ls[TypeDef] = - TypeDef(Cls, TypeName("?"), Nil, TopType, Nil, Nil, Set.empty, N, Nil) :: // * Dummy for pretty-printing unknown type locations - TypeDef(Cls, TypeName("int"), Nil, TopType, Nil, Nil, Set.single(TypeName("number")), N, Nil) :: - TypeDef(Cls, TypeName("number"), Nil, TopType, Nil, Nil, Set.empty, N, Nil) :: - TypeDef(Cls, TypeName("bool"), Nil, TopType, Nil, Nil, Set.empty, N, Nil) :: - TypeDef(Cls, TypeName("true"), Nil, TopType, Nil, Nil, Set.single(TypeName("bool")), N, Nil) :: - TypeDef(Cls, TypeName("false"), Nil, TopType, Nil, Nil, Set.single(TypeName("bool")), N, Nil) :: - TypeDef(Cls, TypeName("string"), Nil, TopType, Nil, Nil, Set.empty, N, Nil) :: - TypeDef(Als, TypeName("undefined"), Nil, ClassTag(UnitLit(true), Set.empty)(noProv), Nil, Nil, Set.empty, N, Nil) :: - TypeDef(Als, TypeName("null"), Nil, ClassTag(UnitLit(false), Set.empty)(noProv), Nil, Nil, Set.empty, N, Nil) :: - TypeDef(Als, TypeName("anything"), Nil, TopType, Nil, Nil, Set.empty, N, Nil) :: - TypeDef(Als, TypeName("nothing"), Nil, BotType, Nil, Nil, Set.empty, N, Nil) :: - TypeDef(Cls, TypeName("error"), Nil, TopType, Nil, Nil, Set.empty, N, Nil) :: - TypeDef(Cls, TypeName("unit"), Nil, TopType, Nil, Nil, Set.empty, N, Nil) :: + TypeDef(Cls, TN("?"), Nil, TopType, Nil, Nil, Set.empty, N, Nil) :: // * Dummy for pretty-printing unknown type locations + TypeDef(Cls, TN("int"), Nil, TopType, Nil, Nil, sing(TN("number")), N, Nil) :: + TypeDef(Cls, TN("number"), Nil, TopType, Nil, Nil, semp, N, Nil) :: + TypeDef(Cls, TN("bool"), Nil, TopType, Nil, Nil, semp, N, Nil) :: + TypeDef(Cls, TN("true"), Nil, TopType, Nil, Nil, sing(TN("bool")), N, Nil) :: + TypeDef(Cls, TN("false"), Nil, TopType, Nil, Nil, sing(TN("bool")), N, Nil) :: + TypeDef(Cls, TN("string"), Nil, TopType, Nil, Nil, semp, N, Nil) :: + TypeDef(Als, TN("undefined"), Nil, ClassTag(UnitLit(true), semp)(noProv), Nil, Nil, semp, N, Nil) :: + TypeDef(Als, TN("null"), Nil, ClassTag(UnitLit(false), semp)(noProv), Nil, Nil, semp, N, Nil) :: + TypeDef(Als, TN("anything"), Nil, TopType, Nil, Nil, semp, N, Nil) :: + TypeDef(Als, TN("nothing"), Nil, BotType, Nil, Nil, semp, N, Nil) :: + TypeDef(Cls, TN("error"), Nil, TopType, Nil, Nil, semp, N, Nil) :: + TypeDef(Cls, TN("unit"), Nil, TopType, Nil, Nil, semp, N, Nil) :: { val tv = freshVar(noTyProv, N)(1) - val tyDef = TypeDef(Als, TypeName("Array"), List(TypeName("A") -> tv), - ArrayType(FieldType(None, tv)(noTyProv))(noTyProv), Nil, Nil, Set.empty, N, Nil) + val tyDef = TypeDef(Als, TN("Array"), List(TN("A") -> tv), + ArrayType(FieldType(None, tv)(noTyProv))(noTyProv), Nil, Nil, semp, N, Nil) // * ^ Note that the `noTyProv` here is kind of a problem // * since we currently expand primitive types eagerly in DNFs. // * For instance, see `inn2 v1` in test `Yicong.mls`. @@ -224,33 +269,52 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) } :: { val tv = freshVar(noTyProv, N)(1) - val tyDef = TypeDef(Als, TypeName("MutArray"), List(TypeName("A") -> tv), - ArrayType(FieldType(Some(tv), tv)(noTyProv))(noTyProv), Nil, Nil, Set.empty, N, Nil) + val tyDef = TypeDef(Als, TN("MutArray"), List(TN("A") -> tv), + ArrayType(FieldType(Some(tv), tv)(noTyProv))(noTyProv), Nil, Nil, semp, N, Nil) tyDef.tvarVariances = S(MutMap(tv -> VarianceInfo.in)) tyDef } :: Nil val primitiveTypes: Set[Str] = - builtinTypes.iterator.map(_.nme.name).flatMap(n => n.decapitalize :: n.capitalize :: Nil).toSet + builtinTypes.iterator.map(_.nme.name).flatMap(n => n.decapitalize :: n.capitalize :: Nil).toSet + + "Object" + "Num" + "Str" + val reservedTypeNames: Set[Str] = primitiveTypes + "Eql" def singleTup(ty: ST): ST = if (funkyTuples) ty else TupleType((N, ty.toUpper(ty.prov) ) :: Nil)(noProv) + def pair(ty1: ST, ty2: ST): ST = + TupleType(N -> ty1.toUpper(ty1.prov) :: N -> ty2.toUpper(ty2.prov) :: Nil)(noProv) + private val sharedVar = freshVar(noProv, N)(1) val builtinBindings: Bindings = { val tv = freshVar(noProv, N)(1) import FunctionType.{ apply => fun } - val intBinOpTy = fun(singleTup(IntType), fun(singleTup(IntType), IntType)(noProv))(noProv) - val numberBinOpTy = fun(singleTup(DecType), fun(singleTup(DecType), DecType)(noProv))(noProv) - val numberBinPred = fun(singleTup(DecType), fun(singleTup(DecType), BoolType)(noProv))(noProv) + val (intBinOpTy, numberBinOpTy, numberBinPred, stringBinPred) = if (newDefs) ( + fun(pair(IntType, IntType), IntType)(noProv), + fun(pair(DecType, DecType), DecType)(noProv), + fun(pair(DecType, DecType), BoolType)(noProv), + fun(pair(StrType, StrType), BoolType)(noProv), + ) + else ( + fun(singleTup(IntType), fun(singleTup(IntType), IntType)(noProv))(noProv), + fun(singleTup(DecType), fun(singleTup(DecType), DecType)(noProv))(noProv), + fun(singleTup(DecType), fun(singleTup(DecType), BoolType)(noProv))(noProv), + fun(singleTup(StrType), fun(singleTup(StrType), BoolType)(noProv))(noProv), + ) Map( "true" -> TrueType, "false" -> FalseType, + "True" -> TypeRef(TN("True"), Nil)(noProv), + "False" -> TypeRef(TN("False"), Nil)(noProv), + "NaN" -> DecType, "document" -> BotType, "window" -> BotType, + "typeof" -> fun(singleTup(TopType), StrType)(noProv), "toString" -> fun(singleTup(TopType), StrType)(noProv), "not" -> fun(singleTup(BoolType), BoolType)(noProv), "succ" -> fun(singleTup(IntType), IntType)(noProv), "log" -> PolymorphicType(MinLevel, fun(singleTup(tv), UnitType)(noProv)), "discard" -> PolymorphicType(MinLevel, fun(singleTup(tv), UnitType)(noProv)), "negate" -> fun(singleTup(IntType), IntType)(noProv), + "round" -> fun(singleTup(DecType), IntType)(noProv), "add" -> intBinOpTy, "sub" -> intBinOpTy, "mul" -> intBinOpTy, @@ -260,6 +324,11 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) "le" -> numberBinPred, "gt" -> numberBinPred, "ge" -> numberBinPred, + "slt" -> stringBinPred, + "sle" -> stringBinPred, + "sgt" -> stringBinPred, + "sge" -> stringBinPred, + "length" -> fun(singleTup(StrType), IntType)(noProv), "concat" -> fun(singleTup(StrType), fun(singleTup(StrType), StrType)(noProv))(noProv), "eq" -> { val v = freshVar(noProv, N)(1) @@ -270,6 +339,10 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) PolymorphicType(MinLevel, fun(singleTup(v), fun(singleTup(v), BoolType)(noProv))(noProv)) }, "error" -> BotType, + "," -> { + val v = sharedVar + PolymorphicType(MinLevel, fun(TupleType(N -> TopType.toUpper(provTODO) :: N -> v.toUpper(provTODO) :: Nil)(noProv), v)(noProv)) + }, "+" -> intBinOpTy, "-" -> intBinOpTy, "*" -> intBinOpTy, @@ -280,9 +353,16 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) "<=" -> numberBinPred, ">=" -> numberBinPred, "==" -> numberBinPred, + "===" -> { + val v = sharedVar + val eq = TypeRef(TypeName("Eql"), v :: Nil)(noProv) + PolymorphicType(MinLevel, fun(pair(eq, v), BoolType)(noProv)) + }, "<>" -> numberBinPred, - "&&" -> fun(singleTup(BoolType), fun(singleTup(BoolType), BoolType)(noProv))(noProv), - "||" -> fun(singleTup(BoolType), fun(singleTup(BoolType), BoolType)(noProv))(noProv), + "&&" -> (if (newDefs) fun(pair(BoolType, BoolType), BoolType)(noProv) + else fun(singleTup(BoolType), fun(singleTup(BoolType), BoolType)(noProv))(noProv)), + "||" -> (if (newDefs) fun(pair(BoolType, BoolType), BoolType)(noProv) + else fun(singleTup(BoolType), fun(singleTup(BoolType), BoolType)(noProv))(noProv)), "id" -> { val v = freshVar(noProv, N)(1) PolymorphicType(MinLevel, fun(singleTup(v), v)(noProv)) @@ -295,7 +375,8 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) val v = freshVar(noProv, N)(1) PolymorphicType(0, ArrayType(FieldType(S(v), v)(noProv))(noProv)) }, - ) ++ primTypes ++ primTypes.map(p => "" + p._1.capitalize -> p._2) // TODO settle on naming convention... + ) ++ (if (!newDefs) primTypes ++ primTypes.map(p => p._1.capitalize -> p._2) // TODO settle on naming convention... + else Nil) } @@ -321,19 +402,39 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) (implicit ctx: Ctx, raise: Raise, vars: Map[Str, SimpleType], newDefsInfo: Map[Str, (TypeDefKind, Int)]): (SimpleType, Iterable[TypeVariable]) = // TODO rm _2 result? // trace(s"$lvl. Typing type $ty") { - trace(s"Typing type ${ty.show}") { + trace(s"Typing type ${ty.showDbg}") { println(s"vars=$vars newDefsInfo=$newDefsInfo") val typeType2 = () // val outerCtxLvl = MinLevel + 1 val outerCtxLvl = ctx.lvl + def checkKind(k: DeclKind, nme: Str, loc: Opt[Loc]): Unit = k match { + case Cls | Mod | Als | Trt => () + case _ => err(msg"${k.str} ${nme} cannot be used as a type", loc); () + } def typeNamed(loc: Opt[Loc], name: Str): (() => ST) \/ (TypeDefKind, Int) = newDefsInfo.get(name) .orElse(ctx.tyDefs.get(name).map(td => (td.kind, td.tparamsargs.size))) + .orElse(ctx.get(name).flatMap { + case CompletedTypeInfo(mem: TypedNuTypeDef) => + checkKind(mem.decl.kind, mem.nme.name, loc) + S(mem.td.kind, mem.tparams.size) + case ti: DelayedTypeInfo => + checkKind(ti.decl.kind, ti.decl.name, loc) + ti.decl match { + case NuTypeDef(k @ (Cls | Mod | Als | Trt), _, tps, _, _, _, _, _, _, _) => + S(k, tps.size) + case NuTypeDef(k @ Mxn, nme, tps, _, _, _, _, _, _, _) => + S(k, tps.size) + case fd: NuFunDef => + N + } + case _ => N + }) .toRight(() => err("type identifier not found: " + name, loc)(raise)) val localVars = mutable.Map.empty[TypeVar, TypeVariable] def tyTp(loco: Opt[Loc], desc: Str, originName: Opt[Str] = N) = TypeProvenance(loco, desc, originName, isType = true) - def rec(ty: Type)(implicit ctx: Ctx, recVars: Map[TypeVar, TypeVariable]): SimpleType = trace(s"$lvl. type ${ty.show}") { ty match { + def rec(ty: Type)(implicit ctx: Ctx, recVars: Map[TypeVar, TypeVariable]): SimpleType = trace(s"$lvl. type ${ty.showDbg}") { ty match { case Top => ExtrType(false)(tyTp(ty.toLoc, "top type")) case Bot => ExtrType(true)(tyTp(ty.toLoc, "bottom type")) case Bounds(Bot, Top) => @@ -388,22 +489,37 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) r.fields.map { case (n, f) => n -> FieldType(f.in.map(rec), rec(f.out))( tyTp(App(n, Var("").withLocOf(f)).toCoveringLoc, "extension field")) } )(tyTp(r.toLoc, "extension record")))(tyTp(ty.toLoc, "extension type")) - case Literal(lit) => ClassTag(lit, lit.baseClasses)(tyTp(ty.toLoc, "literal type")) + case Literal(lit) => + ClassTag(lit, if (newDefs) lit.baseClassesNu + else lit.baseClassesOld)(tyTp(ty.toLoc, "literal type")) case TypeName("this") => - ctx.env.getOrElse("this", err(msg"undeclared this" -> ty.toLoc :: Nil)) match { - case AbstractConstructor(_, _) => die - case VarSymbol(t: SimpleType, _) => t + ctx.env.get("this") match { + case S(_: AbstractConstructor | _: LazyTypeInfo) => die + case S(VarSymbol(t: SimpleType, _)) => t + case N => err(msg"undeclared `this`" -> ty.toLoc :: Nil) } - case tn @ TypeTag(name) => rec(TypeName(name.decapitalize)) + case tn @ TypeTag(name) => rec(TypeName(name.decapitalize)) // TODO rm this hack + // case tn @ TypeTag(name) => rec(TypeName(name)) case tn @ TypeName(name) => val tyLoc = ty.toLoc val tpr = tyTp(tyLoc, "type reference") vars.getOrElse(name, { typeNamed(tyLoc, name) match { case R((_, tpnum)) => - if (tpnum =/= 0) { - err(msg"Type $name takes parameters", tyLoc)(raise) - } else TypeRef(tn, Nil)(tpr) + if (tpnum === 0) TypeRef(tn, Nil)(tpr) + else ctx.tyDefs2.get(name) match { + case S(lti) => + lti.decl match { + case NuTypeDef(Cls | Mod, _, _, _, _, _, _, _, _, _) => + clsNameToNomTag(ctx.tyDefs2(name).decl.asInstanceOf[NuTypeDef])(tyTp(tyLoc, "class tag"), ctx) + case NuTypeDef(Trt, _, _, _, _, _, _, _, _, _) => + trtNameToNomTag(ctx.tyDefs2(name).decl.asInstanceOf[NuTypeDef])(tyTp(tyLoc, "class tag"), ctx) + case NuTypeDef(Als, _, _, _, _, _, _, _, _, _) => + TypeRef(tn, List.fill(tpnum)(freshVar(noProv, N, N)))(tpr) + case _ => die // TODO + } + case _ => err(msg"Type $name takes parameters", tyLoc)(raise) + } case L(e) => if (name.isEmpty || !name.head.isLower) e() else (typeNamed(tyLoc, name.capitalize), ctx.tyDefs.get(name.capitalize)) match { @@ -412,19 +528,21 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) case Trt => trtNameToNomTag(td)(tyTp(tyLoc, "trait tag"), ctx) case Als => err( msg"Type alias ${name.capitalize} cannot be used as a type tag", tyLoc)(raise) - case Nms => err( - msg"Namespaces ${name.capitalize} cannot be used as a type tag", tyLoc)(raise) + case Mod => err( + msg"Module ${name.capitalize} cannot be used as a type tag", tyLoc)(raise) + case Mxn => err( + msg"Mixin ${name.capitalize} cannot be used as a type tag", tyLoc)(raise) } case _ => e() } } }) - case tv: TypeVar => vars.getOrElse(tv.identifier.toOption.getOrElse(""), { + case tv: TypeVar => tv.identifier.toOption.flatMap(vars.get).getOrElse { recVars.getOrElse(tv, - localVars.getOrElseUpdate(tv, freshVar(noProv, N, tv.name) + localVars.getOrElseUpdate(tv, freshVar(noProv, N, tv.name.filter(_.exists(_ =/= '\''))) (outerCtxLvl)) // * Type variables not explicily bound are assigned the widest (the outer context's) level ).withProv(tyTp(ty.toLoc, "type variable")) - }) + } case AppliedType(base, targs) => val prov = tyTp(ty.toLoc, "applied type reference") typeNamed(ty.toLoc, base.name) match { @@ -437,6 +555,28 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) TypeRef(base, realTargs)(prov) case L(e) => e() } + case Selection(base, nme) => + implicit val gl: GenLambdas = false + // val base_ty = typeTerm(base) + val base_ty = rec(base) + def go(b_ty: ST, rfnt: Var => Opt[FieldType]): ST = b_ty.unwrapAll match { + case ct: TypeRef => die // TODO actually + case ClassTag(Var(clsNme), _) => + // TODO we should still succeed even if the member is not completed... + lookupMember(clsNme, rfnt, nme.toVar) match { + case R(cls: TypedNuCls) => + if (cls.tparams.nonEmpty) ??? // TODO + clsNameToNomTag(cls.td)(TypeProvenance(ty.toLoc, "type selection", isType = true), ctx) + case R(als: TypedNuAls) => + if (als.tparams.nonEmpty) ??? // TODO + als.body + case R(m) => err(msg"Illegal selection of ${m.kind.str} member in type position", nme.toLoc) + case L(d) => err(d) + } + case _ => + err(msg"Illegal prefix of type selection: ${b_ty.expPos}", base.toLoc) + } + go(base_ty, _ => N) case Recursive(uv, body) => val tv = freshVar(tyTp(ty.toLoc, "local type binding"), N, uv.name) val bod = rec(body)(ctx, recVars + (uv -> tv)) @@ -444,7 +584,10 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) tv case Rem(base, fs) => Without(rec(base), fs.toSortedSet)(tyTp(ty.toLoc, "field removal type")) case Constrained(base, tvbs, where) => - val res = rec(base) + val res = rec(base match { + case ty: Type => ty + case _ => die + }) tvbs.foreach { case (tv, Bounds(lb, ub)) => constrain(rec(lb), tv)(raise, tp(lb.toLoc, "lower bound specifiation"), ctx) constrain(tv, rec(ub))(raise, tp(ub.toLoc, "upper bound specifiation"), ctx) @@ -513,7 +656,7 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) raise = err => raise(WarningReport( // Demote constraint errors from this to warnings msg"Expression in statement position should have type `unit`." -> N :: msg"Use the `discard` function to discard non-unit values, making the intent clearer." -> N :: - err.allMsgs)), + err.allMsgs, newDefs)), prov = TypeProvenance(t.toLoc, t.describe), ctx) } L(ty) @@ -523,8 +666,8 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) } /** Like `typeLetRhs` but removes unnecessary polymorphic type wrappers. */ - def typeLetRhs2(isrec: Boolean, nme: Str, rhs: Term)(implicit ctx: Ctx, raise: Raise): ST = { - val res = typeLetRhs(isrec, nme, rhs)(ctx, raise, Map.empty, genLambdas = true) + def typeLetRhs2(isrec: Boolean, nme: Str, rhs: Term)(implicit ctx: Ctx, raise: Raise, vars: Map[Str, SimpleType]): ST = { + val res = typeLetRhs(isrec, nme, rhs)(ctx, raise, vars, genLambdas = true) def stripPoly(ty: ST): ST = ty match { case pt: PolymorphicType => PolymorphicType.mk(pt.polymLevel, stripPoly(pt.body)) @@ -581,11 +724,11 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) } // TODO also prevent rebinding of "not" - val reservedNames: Set[Str] = Set("|", "&", "~", ",", "neg", "and", "or", "is") + val reservedVarNames: Set[Str] = Set("|", "&", "~", "neg", "and", "or", "is") object ValidVar { def unapply(v: Var)(implicit raise: Raise): S[Str] = S { - if (reservedNames(v.name)) + if (reservedVarNames(v.name)) err(s"Illegal use of reserved operator: " + v.name, v.toLoc)(raise) v.name @@ -710,16 +853,41 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) ) ) case VarSymbol(ty, _) => ty + case lti: LazyTypeInfo => + // TODO deal with classes without parameter lists (ie needing `new`) + def checkNotAbstract(decl: NuDecl) = + if (decl.isAbstract) + err(msg"Class ${decl.name} is abstract and cannot be instantiated", term.toLoc) + lti match { + case ti: CompletedTypeInfo => + ti.member match { + case ti: TypedNuFun => + ti.typeSignature + case p: NuParam => + p.typeSignature + case ti: TypedNuCls => + checkNotAbstract(ti.decl) + ti.typeSignature(false, prov.loco) + case ti: TypedNuDecl => + err(msg"${ti.kind.str} ${ti.name} cannot be used in term position", prov.loco) + } + case ti: DelayedTypeInfo => + checkNotAbstract(ti.decl) + ti.typeSignature(false, prov.loco) + } } mkProxy(ty, prov) // ^ TODO maybe use a description passed in param? // currently we get things like "flows into variable reference" // but we used to get the better "flows into object receiver" or "flows into applied expression"... - case lit: Lit => ClassTag(lit, lit.baseClasses)(prov) - case App(Var("neg" | "~"), trm) => typeTerm(trm).neg(prov) - case App(App(Var("|"), lhs), rhs) => + case lit: Lit => ClassTag(lit, if (newDefs) lit.baseClassesNu else lit.baseClassesOld)(prov) + case Super() => + err(s"Illegal use of `super`", term.toLoc)(raise) + typeTerm(Var("super").withLocOf(term)) + case App(Var("neg" | "~"), trm) if funkyTuples => typeTerm(trm).neg(prov) + case App(App(Var("|"), lhs), rhs) if funkyTuples => typeTerm(lhs) | (typeTerm(rhs), prov) - case App(App(Var("&"), lhs), rhs) => + case App(App(Var("&"), lhs), rhs) if funkyTuples => typeTerm(lhs) & (typeTerm(rhs), prov) case Rcd(fs) => val prov = tp(term.toLoc, "record literal") @@ -728,7 +896,7 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) :: fieldNames.map(tp => msg"Declared at" -> tp.toLoc))(raise) case _ => } - RecordType.mk(fs.map { case (n, Fld(mut, _, t)) => + RecordType.mk(fs.map { case (n, Fld(FldFlags(mut, _, _), t)) => if (n.name.isCapitalized) err(msg"Field identifiers must start with a small letter", term.toLoc)(raise) val tym = typePolymorphicTerm(t) @@ -742,8 +910,19 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) case tup: Tup if funkyTuples => typeTerms(tup :: Nil, false, Nil) case Tup(fs) => - TupleType(fs.map { case (n, Fld(mut, _, t)) => + TupleType(fs.mapConserve { case e @ (n, Fld(flags, t)) => + n match { + case S(v) if ctx.inPattern => + (n, Fld(flags, + Asc(v, t.toTypeRaise).withLoc(v.toLoc.fold(t.toLoc)(_ ++ t.toLoc |> some)))) + case _ => e + } + }.map { case (n, Fld(FldFlags(mut, spec, getter), t)) => + if (getter) + err(msg"Cannot use `val` in this position", Loc(t :: n.toList)) val tym = typePolymorphicTerm(t) + // val tym = if (n.isDefined) typeType(t.toTypeRaise) + // else typePolymorphicTerm(t) val fprov = tp(t.toLoc, (if (mut) "mutable " else "") + "tuple field") if (mut) { val res = freshVar(fprov, N, n.map(_.name)) @@ -751,7 +930,7 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) (n, FieldType(Some(rs), rs)(fprov)) } else (n, tym.toUpper(fprov)) })(fs match { - case Nil | ((N, _) :: Nil) => noProv + case Nil | ((N, _) :: Nil) => noProv // TODO rm? case _ => tp(term.toLoc, "tuple literal") }) case Subs(a, i) => @@ -802,7 +981,7 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) val t_a = ArrayType(freshVar(prov, N).toUpper(prov))(prov) con(t_l, t_a, t_l) }) - case R(Fld(mt, sp, r)) => { + case R(Fld(FldFlags(mt, sp, _), r)) => { val t = typeMonomorphicTerm(r) if (mt) { R(FieldType(Some(t), t)(t.prov)) } else {R(t.toUpper(t.prov))} } @@ -831,14 +1010,98 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) val body_ty = typeTerm(body)(newCtx, raise, vars, generalizeCurriedFunctions || doGenLambdas) FunctionType(param_ty, body_ty)(tp(term.toLoc, "function")) - case App(App(Var("is"), _), _) => + case NuNew(cls) => typeMonomorphicTerm(App(NuNew(cls), Tup(Nil).withLoc(term.toLoc.map(_.right)))) + case app @ App(nw @ NuNew(cls), args) => + cls match { + case _: TyApp => // * TODO improve (hacky) + err(msg"Type arguments in `new` expressions are not yet supported", prov.loco) + case _ => + } + val cls_ty = typeType(cls.toTypeRaise) + def process(clsNme: Str) = { + println(clsNme, ctx.tyDefs2.get(clsNme)) + ctx.tyDefs2.get(clsNme) match { + case N => + err(msg"Type `${clsNme}` cannot be used in `new` expression", term.toLoc) + case S(lti) => + def checkNotAbstract(decl: NuDecl) = + if (decl.isAbstract) + err(msg"Class ${decl.name} is abstract and cannot be instantiated", term.toLoc) + lti match { + case dti: DelayedTypeInfo if !(dti.kind is Cls) => + err(msg"${dti.kind.str.capitalize} ${dti.name} cannot be used in `new` expression", + prov.loco) + case dti: DelayedTypeInfo => + checkNotAbstract(dti.decl) + dti.typeSignature(true, prov.loco) + } + } + } + val new_ty = cls_ty.unwrapProxies match { + case TypeRef(clsNme, targs) => + // FIXME don't disregard `targs` + process(clsNme.name) + case err @ ClassTag(ErrTypeId, _) => err + case ClassTag(Var(clsNme), _) => process(clsNme) + case _ => + // * Debug with: ${cls_ty.getClass.toString} + err(msg"Unexpected type `${cls_ty.expPos}` after `new` keyword" -> cls.toLoc :: Nil) + } + val res = freshVar(prov, N) + 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) + case App(Var("is"), _) => val desug = If(IfThen(term, Var("true")), S(Var("false"))) term.desugaredTerm = S(desug) typeTerm(desug) - case App(App(Var("and"), Tup(_ -> Fld(_, _, lhs) :: Nil)), Tup(_ -> Fld(_, _, rhs) :: Nil)) => + 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) + case App(Var("and"), PlainTup(lhs, rhs)) => + val desug = If(IfThen(lhs, rhs), S(Var("false"))) + term.desugaredTerm = S(desug) + typeTerm(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(_, _))) => + List(fun_ty) + case tt @ FunctionType(_, _) => + List(tt) + case tv: TypeVariable => + tv.lowerBounds.map(getLowerBoundFunctionType(_)).flatten + case ct @ ComposedType(pol, lhs, rhs) => + if (pol === false) { + getLowerBoundFunctionType(lhs) ++ getLowerBoundFunctionType(rhs) + } else + Nil + case _ => + Nil + } + val f_ty = typeTerm(f) + val fun_tys: List[FunctionType] = getLowerBoundFunctionType(f_ty) + + fun_tys match { + case FunctionType(TupleType(fields), _) :: Nil => + val hasUntypedArg = fields.exists(_._1.isEmpty) + if (hasUntypedArg) { + err("Cannot use named arguments as the function type has untyped arguments", a.toLoc) + } else { + val argsList = fields.map(x => x._1 match { + case Some(arg) => arg + case N => die // cannot happen, because already checked with the hasUntypedArg + }) + desugarNamedArgs(term, f, a, argsList, f_ty) + } + case _ :: _ :: _ => + err(msg"More than one function signature found in type `${f_ty.expPos}` for function call with named arguments", f.toLoc) + case Nil | _ :: Nil => + err(msg"Cannot retrieve appropriate function signature from type `${f_ty.expPos}` for applying named arguments", f.toLoc) + } case App(f, a) => val f_ty = typeMonomorphicTerm(f) // * ^ Note: typing the function monomorphically simplifies type inference but @@ -850,7 +1113,7 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) else ctx.poly { implicit ctx => typePolymorphicTerm(a) } a match { case tup @ Tup(as) => - TupleType(as.map { case (n, Fld(mut, spec, a)) => // TODO handle mut? + TupleType(as.map { case (n, Fld(FldFlags(mut, spec, _), a)) => // TODO handle mut? // assert(!mut) val fprov = tp(a.toLoc, "argument") val tym = typeArg(a) @@ -883,14 +1146,14 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) // Returns a function expecting an additional argument of type `Class` before the method arguments def rcdSel(obj: Term, fieldName: Var) = { val o_ty = typeMonomorphicTerm(obj) - val res = freshVar(prov, N, Opt.when(!fieldName.name.startsWith("_"))(fieldName.name)) + val res = freshVar(prov, N, Opt.when(!fieldName.name.startsWith("_") && !fieldName.isIndex)(fieldName.name)) val obj_ty = mkProxy(o_ty, tp(obj.toCoveringLoc, "receiver")) val rcd_ty = RecordType.mk( fieldName -> res.toUpper(tp(fieldName.toLoc, "field selector")) :: Nil)(prov) con(obj_ty, rcd_ty, res) } def mthCallOrSel(obj: Term, fieldName: Var) = - (fieldName.name match { + ( if (newDefs) N else fieldName.name match { case s"$parent.$nme" => ctx.getMth(S(parent), nme) // explicit calls case nme => ctx.getMth(N, nme) // implicit calls }) match { @@ -908,8 +1171,14 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) val res = freshVar(prov, N) con(mth_ty.toPT.instantiate, FunctionType(singleTup(o_ty), res)(prov), res) case N => - if (fieldName.name.isCapitalized) err(msg"Method ${fieldName.name} not found", term.toLoc) - else rcdSel(obj, fieldName) // TODO: no else? + if (!newDefs && fieldName.name.isCapitalized) err(msg"Method ${fieldName.name} not found", term.toLoc) + else { + val realPrefix = obj match { + case Super() => Var("super").withLocOf(obj) + case _ => obj + } + rcdSel(realPrefix, fieldName) + } } obj match { case Var(name) if name.isCapitalized && ctx.tyDefs.isDefinedAt(name) => // explicit retrieval @@ -919,17 +1188,42 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) err(msg"Class ${name} has no method ${fieldName.name}", term.toLoc) mthCallOrSel(obj, fieldName) } + // * The code below is only a temporary solution to type `ClassName.unapply`. + // * It removed when static methods can be typed properly. + case Var(nuCls) => + if (fieldName.name === "unapply") (ctx.get(nuCls) match { + case S(CompletedTypeInfo(cls: TypedNuCls)) => cls.td.genUnapply + case S(ti: DelayedTypeInfo) => ti.decl.genUnapply + case _ => N + }) match { + case S(NuFunDef(_, _, _, _, L(unapplyMtd))) => typePolymorphicTerm(unapplyMtd) + case _ => mthCallOrSel(obj, fieldName) + } + else mthCallOrSel(obj, fieldName) case _ => mthCallOrSel(obj, fieldName) } case Let(isrec, nme, rhs, bod) => - val n_ty = typeLetRhs(isrec, nme.name, rhs) - val newCtx = ctx.nest - newCtx += nme.name -> VarSymbol(n_ty, nme) - typeTerm(bod)(newCtx, raise, vars, genLambdas) + if (newDefs && !isrec) { + // if (isrec) ??? + val rhs_ty = typeTerm(rhs) + val newCtx = ctx.nest + newCtx += nme.name -> VarSymbol(rhs_ty, nme) + typeTerm(bod)(newCtx, raise, vars, genLambdas) + } else { + val n_ty = typeLetRhs(isrec, nme.name, rhs) + val newCtx = ctx.nest + newCtx += nme.name -> VarSymbol(n_ty, nme) + typeTerm(bod)(newCtx, raise, vars, genLambdas) + } // case Blk(s :: stmts) => // val (newCtx, ty) = typeStatement(s) // typeTerm(Blk(stmts))(newCtx, lvl, raise) - case Blk(stmts) => typeTerms(stmts, false, Nil)(ctx.nest, raise, prov, vars, genLambdas) + case b @ Blk(stmts) => + if (newDefs) { + val ttu = typeTypingUnit(TypingUnit(stmts), S(b)) + // TODO check unused defs + ttu.result.getOrElse(UnitType) + } else typeTerms(stmts, false, Nil)(ctx.nest, raise, prov, vars, genLambdas) case Bind(l, r) => val l_ty = typeMonomorphicTerm(l) val newCtx = ctx.nest // so the pattern's context don't merge with the outer context! @@ -948,6 +1242,7 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) (t_ty without rcd.fields.iterator.map(_._1).toSortedSet) & (rcd_ty, prov) case CaseOf(s, cs) => val s_ty = typeMonomorphicTerm(s) + if (newDefs) con(s_ty, ObjType.withProv(prov), TopType) val (tys, cs_ty) = typeArms(s |>? { case v: Var => v case Asc(v: Var, _) => v @@ -956,19 +1251,9 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) case ((a_ty, tv), req) => a_ty & tv | req & a_ty.neg() } con(s_ty, req, cs_ty) - case iff @ If(body, fallback) => - import mlscript.ucs._ - try { - val caseTree = MutCaseOf.build(desugarIf(body, fallback)) - println("The mutable CaseOf tree") - MutCaseOf.show(caseTree).foreach(println(_)) - checkExhaustive(caseTree, N)(summarizePatterns(caseTree), ctx, raise) - val desugared = MutCaseOf.toTerm(caseTree) - println(s"Desugared term: ${desugared.print(false)}") - iff.desugaredTerm = S(desugared) - typeTerm(desugared) - } catch { - case e: DesugaringException => err(e.messages) + case elf: If => + try typeTerm(desugarIf(elf)) catch { + case e: ucs.DesugaringException => err(e.messages) } case AdtMatchWith(cond, arms) => println(s"typed condition term ${cond}") @@ -979,11 +1264,6 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) freshVar(prov.copy(desc = "match expression"), N) } - // // cache type argument variables for adts - // // so as to reuse them for each case expression - // // that has the same adt type - // val alsCache: MutMap[Str, Ls[TV]] = MutMap() - // the assumed shape of an IfBody is a List[IfThen, IfThen, IfElse] with an optional IfElse at the end arms.foreach { case AdtMatchPat(pat, rhs) => val nestCtx = ctx.nest @@ -1039,7 +1319,7 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) con(expected, adt_ty, adt_ty) fs.zipWithIndex.foreach { - case ((_, Fld(_, _, argTerm)), fieldIdx) => + case ((_, Fld(_, argTerm)), fieldIdx) => println(s"Typing $argTerm field $fieldIdx in tup") val fieldType = tupArgs(fieldIdx) println(s"Field $argTerm : $fieldType") @@ -1060,7 +1340,7 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) // find the alias type returned by constructor val (body, tparams, AdtInfo(alsName)) = ctx.tyDefs.get(ctorNme).flatMap { case TypeDef(Cls, _, tparamsargs, BodyRecordType(body), _, _, _, _, _, S(adtInfo)) => S(body, tparamsargs, adtInfo) - case _ => N + case r => N }.getOrElse(lastWords(s"$ctorNme cannot be pattern matched")) // get alias type from cache or initialize a new one with fresh vars @@ -1105,7 +1385,7 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) // () // and others case pat => - lastWords(s"Cannot handle pattern ${pat}") + lastWords(s"Cannot handle pattern ${pat.showDbg}") } handlePat(pat, cond_ty) @@ -1114,10 +1394,10 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) } } ret_ty - case New(S((nmedTy, trm)), TypingUnit(Nil)) => - typeMonomorphicTerm(App(Var(nmedTy.base.name).withLocOf(nmedTy), trm)) - case New(base, args) => ??? - case TyApp(_, _) => ??? // TODO handle + case New(base, args) => err(msg"Currently unsupported `new` syntax", term.toCoveringLoc) + case TyApp(base, _) => + err(msg"Type application syntax is not yet supported", term.toLoc) // TODO handle + typeTerm(base) case Where(bod, sts) => typeTerms(sts :+ bod, false, Nil, allowPure = true) case Forall(vs, bod) => @@ -1145,6 +1425,10 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) if (!founPoly) warn(msg"Inferred type `${bod_ty.expPos}` of this ${ bod_ty.prov.desc} cannot be instantiated", prov.loco) res + case Eqn(lhs, rhs) => + err(msg"Unexpected equation in this position", term.toLoc) + case Rft(bse, tu) => + err(msg"Refinement terms are not yet supported", term.toLoc) } }(r => s"$lvl. : ${r}") @@ -1164,37 +1448,82 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) (fv -> TopType :: Nil) -> typeTerm(b) } case Case(pat, bod, rest) => - val patTy = pat match { + val (tagTy, patTy) : (ST, ST) = pat match { case lit: Lit => - ClassTag(lit, lit.baseClasses)(tp(pat.toLoc, "literal pattern")) - case Var(nme) => + val t = ClassTag(lit, + if (newDefs) lit.baseClassesNu else lit.baseClassesOld)(tp(pat.toLoc, "literal pattern")) + t -> t + case v @ Var(nme) => val tpr = tp(pat.toLoc, "type pattern") ctx.tyDefs.get(nme) match { case None => - err("type identifier not found: " + nme, pat.toLoc)(raise) - val e = ClassTag(ErrTypeId, Set.empty)(tpr) - return ((e -> e) :: Nil) -> e + val bail = () => { + val e = ClassTag(ErrTypeId, Set.empty)(tpr) + return ((e -> e) :: Nil) -> e + } + ctx.get(nme) match { + case S(lti: LazyTypeInfo) => + if ((lti.kind isnt Cls) && (lti.kind isnt Mod) && (lti.kind isnt Trt)) + err(msg"can only match on classes and traits", pat.toLoc)(raise) + + val prov = tp(pat.toLoc, "class pattern") + + lti match { + case dti: DelayedTypeInfo => + val tag = clsNameToNomTag(dti.decl match { case decl: NuTypeDef => decl; case _ => die })(prov, ctx) + val ty = + RecordType.mk(dti.tparams.map { + case (tn, tv, vi) => + val nv = freshVar(tv.prov, S(tv), tv.nameHint) + (Var(nme+"#"+tn.name).withLocOf(tn), + FieldType.mk(vi.getOrElse(VarianceInfo.in), nv, nv)(provTODO)) + })(provTODO) + println(s"Match arm $nme: $tag & $ty") + tag -> ty + case CompletedTypeInfo(cls: TypedNuCls) => + val tag = clsNameToNomTag(cls.td)(prov, ctx) + val ty = + RecordType.mk(cls.tparams.map { + case (tn, tv, vi) => + val nv = freshVar(tv.prov, S(tv), tv.nameHint) + (Var(nme+"#"+tn.name).withLocOf(tn), + FieldType.mk(vi.getOrElse(cls.varianceOf(tv)), nv, nv)(provTODO)) + })(provTODO) + println(s"Match arm $nme: $tag & $ty") + tag -> ty + case CompletedTypeInfo(_) => bail() + } + + case _ => + err("type identifier not found: " + nme, pat.toLoc)(raise) + bail() + } case Some(td) => td.kind match { - case Als => err(msg"can only match on classes and traits", pat.toLoc)(raise) - case Nms => err(msg"can only match on classes and traits", pat.toLoc)(raise) - case Cls => clsNameToNomTag(td)(tp(pat.toLoc, "class pattern"), ctx) - case Trt => trtNameToNomTag(td)(tp(pat.toLoc, "trait pattern"), ctx) + 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 val (req_ty, bod_ty, (tys, rest_ty)) = scrutVar match { case S(v) => - val tv = freshVar(tp(v.toLoc, "refined scrutinee"), N, - // S(v.name), // this one seems a bit excessive - ) - newCtx += v.name -> VarSymbol(tv, v) - val bod_ty = typeTerm(bod)(newCtx, raise, vars, genLambdas) - (patTy -> tv, bod_ty, typeArms(scrutVar, rest)) + if (newDefs) { + newCtx += v.name -> VarSymbol(tagTy & patTy, v) + val bod_ty = typeTerm(bod)(newCtx, raise, vars, genLambdas) + (tagTy -> patTy, bod_ty, typeArms(scrutVar, rest)) + } else { + val tv = freshVar(tp(v.toLoc, "refined scrutinee"), N, + // S(v.name), // this one seems a bit excessive + ) + newCtx += v.name -> VarSymbol(tv, v) + val bod_ty = typeTerm(bod)(newCtx, raise, vars, genLambdas) + (patTy -> tv, bod_ty, typeArms(scrutVar, rest)) + } case N => val bod_ty = typeTerm(bod)(newCtx, raise, vars, genLambdas) - (patTy -> TopType, bod_ty, typeArms(scrutVar, rest)) + (tagTy -> TopType, bod_ty, typeArms(scrutVar, rest)) } (req_ty :: tys) -> (bod_ty | rest_ty) } @@ -1203,10 +1532,10 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) (implicit ctx: Ctx, raise: Raise, prov: TypeProvenance, vars: Map[Str, SimpleType], genLambdas: GenLambdas): SimpleType = term match { case (trm @ Var(nme)) :: sts if rcd => // field punning - typeTerms(Tup(S(trm) -> Fld(false, false, trm) :: Nil) :: sts, rcd, fields) + typeTerms(Tup(S(trm) -> Fld(FldFlags.empty, trm) :: Nil) :: sts, rcd, fields) case Blk(sts0) :: sts1 => typeTerms(sts0 ::: sts1, rcd, fields) case Tup(Nil) :: sts => typeTerms(sts, rcd, fields) - case Tup((no, Fld(tmut, _, trm)) :: ofs) :: sts => + case Tup((no, Fld(FldFlags(tmut, _, _), trm)) :: ofs) :: sts => val ty = { trm match { case Bra(false, t) if ctx.inPattern => // we use syntax `(x: (p))` to type `p` as a pattern and not a type... @@ -1273,18 +1602,87 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) } else TupleType(fields.reverseIterator.mapValues(_.toUpper(noProv)))(prov) } + def getNewVarName(prefix: Str, nonValidVars: Set[Var]): Str = { + // we check all possibe prefix_num combination, till we find one that is not in the nonValidVars + val ints = LazyList.from(1) + prefix + "_" + ints.find(index => { + !nonValidVars.contains(Var(prefix + "_" + index)) + }).getOrElse(die) + } + + def desugarNamedArgs(term: Term, f: Term, a: Tup, argsList: List[Var], f_ty: ST) + (implicit ctx: Ctx, raise: Raise, vars: Map[Str, SimpleType]): SimpleType = { + def rec (as: List[(String -> Fld) -> Boolean], acc: Map[String, Either[Var, Term]]): Term = { + as match { + case ((v, fld), isNamed) :: tail => + if (isNamed) { + fld.value match { + case _: Lit | _: Var => + rec(tail, acc + (v -> R(fld.value))) + case _ => + val newVar = Var(getNewVarName(v, a.freeVars)) + Let(false, newVar, fld.value, rec(tail, acc + (v -> L(newVar)))) + } + } else { + rec(tail, acc + (v -> R(fld.value))) + } + case Nil => + val y: Term = Tup(argsList.map(x => + acc.get(x.name) match { + case Some(Left(v)) => (None, Fld(FldFlags.empty, v)) + case Some(Right(t)) => (None, Fld(FldFlags.empty, t)) + case None => + err(msg"Argument named '${x.name}' is missing from this function call", a.toLoc) + (None, Fld(FldFlags.empty, Var("error"))) + } + )) + App(f, y) + } + } + val hasDefined = a.fields.exists(x => x._1.isDefined) + val hasEmpty = a.fields.exists(x => x._1.isEmpty) + val areArgsMisplaced = a.fields.indexWhere(x => x._1.isDefined) < a.fields.lastIndexWhere(x => x._1.isEmpty) + if (hasDefined && + hasEmpty && + areArgsMisplaced) { + err(msg"Unnamed arguments should appear first when using named arguments", a.toLoc) + } else + a.fields.sizeCompare(argsList) match { + case 0 => + val as = a.fields.zipWithIndex.map{ + case(x, idx) => + x._1 match { + case Some(value) => + ((value.name, x._2), true) + case N => + ((argsList(idx).name, x._2), false) + }} + val asGroupedByVarName = as.groupBy(x => x._1._1) + if (asGroupedByVarName.sizeCompare(argsList) < 0) { + asGroupedByVarName.foreach(x => + x._2 match { + case x1 :: y1 :: xs => err(msg"Argument for parameter '${x._1}' is duplicated", a.toLoc) + case _ => + }) + } + val desugared = rec(as, Map()) + println("Desugared is here => " + desugared) + term.desugaredTerm = S(desugared) + typeTerm(desugared)(ctx = ctx, raise = raise, vars = vars, genLambdas = false) + case _ => + err(msg"Number of arguments doesn't match function signature `${f_ty.expPos}`", a.toLoc) + } + } /** Convert an inferred SimpleType into the immutable Type representation. */ - def expandType(st: SimpleType, stopAtTyVars: Bool = false)(implicit ctx: Ctx): Type = { + def expandType(st: TypeLike, stopAtTyVars: Bool = false)(implicit ctx: Ctx): mlscript.TypeLike = { val expandType = () - import Set.{empty => semp} - var bounds: Ls[TypeVar -> Bounds] = Nil val seenVars = mutable.Set.empty[TV] - def field(ft: FieldType): Field = ft match { + def field(ft: FieldType)(implicit ectx: ExpCtx): Field = ft match { case FieldType(S(l: TV), u: TV) if l === u => val res = go(u) Field(S(res), res) // TODO improve Field @@ -1292,11 +1690,91 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) Field(f.lb.map(go), go(f.ub)) } - def go(st: SimpleType): Type = + class ExpCtx(val tps: Map[TV, TN]) { + def apply(tparams: Ls[(TN, TV, Opt[VarianceInfo])]): ExpCtx = + new ExpCtx(tps ++ tparams.iterator.map{case (tn, tv, vi) => tv -> tn}) + } + + def mkTypingUnit(thisTy: ST, members: Map[Str, NuMember])(implicit ectx: ExpCtx): TypingUnit = { + val sorted = members.toList.sortBy(_._1) + TypingUnit(sorted.collect { + case (_, d: TypedNuFun) => goDecl(d) + case (_, d: TypedNuTypeDef) => goDecl(d) + }) + } + def goDecl(d: NuMember)(implicit ectx: ExpCtx): NuDecl = d match { + case TypedNuAls(level, td, tparams, body) => + ectx(tparams) |> { implicit ectx => + NuTypeDef(td.kind, td.nme, td.tparams, N, N, S(go(body)), Nil, N, N, TypingUnit(Nil))( + td.declareLoc, td.abstractLoc) + } + case TypedNuMxn(level, td, thisTy, superTy, tparams, params, members) => + ectx(tparams) |> { implicit ectx => + NuTypeDef(td.kind, td.nme, td.tparams, + S(Tup(params.map(p => N -> Fld(FldFlags.empty, Asc(p._1, go(p._2.ub)))))), + N,//TODO + N, + Nil, // TODO mixin parents? + Option.when(!(TopType <:< superTy))(go(superTy)), + Option.when(!(TopType <:< thisTy))(go(thisTy)), + mkTypingUnit(thisTy, members) + )(td.declareLoc, td.abstractLoc) + } + case TypedNuCls(level, td, tparams, params, acParams, members, thisTy, sign, ihtags, ptps) => + ectx(tparams) |> { implicit ectx => + NuTypeDef(td.kind, td.nme, td.tparams, + params.map(ps => Tup(ps.map(p => N -> Fld(FldFlags.empty, Asc(p._1, go(p._2.ub)))))), + td.ctor, + Option.when(!(TopType <:< sign))(go(sign)), + ihtags.toList.sorted.map(_.toVar), // TODO provide targs/args + N,//TODO + Option.when(!(TopType <:< thisTy))(go(thisTy)), + { + val tun = mkTypingUnit(thisTy, members) + acParams match { + case S(ps) => TypingUnit(Constructor( + Tup(ps.map(p => N -> Fld(FldFlags.empty, Asc(p._1, go(p._2))))), + Blk(Nil)) :: tun.entities) + case N => tun + } + } + )(td.declareLoc, td.abstractLoc) + } + case TypedNuTrt(level, td, tparams, members, thisTy, sign, ihtags, ptps) => + ectx(tparams) |> { implicit ectx => + NuTypeDef(td.kind, td.nme, td.tparams, + N, + td.ctor, + Option.when(!(TopType <:< sign))(go(sign)), + ihtags.toList.sorted.map(_.toVar), // TODO provide targs/args + N,//TODO + Option.when(!(TopType <:< thisTy))(go(thisTy)), + mkTypingUnit(thisTy, members) + )(td.declareLoc, td.abstractLoc) + } + case tf @ TypedNuFun(level, fd, bodyTy) => + NuFunDef(fd.isLetRec, fd.nme, fd.symbolicNme, Nil, R(go(tf.typeSignature)))(fd.declareLoc, fd.virtualLoc, fd.signature, fd.outer, fd.genField) + case p: NuParam => + ??? // TODO + case TypedNuDummy(d) => + ??? // TODO + } + def goLike(ty: TypeLike)(implicit ectx: ExpCtx): mlscript.TypeLike = ty match { + case ty: SimpleType => + val res = go(ty) + // if (bounds.isEmpty) res + // else Constrained(res, bounds, Nil) + res + case OtherTypeLike(tu) => + val mems = tu.implementedMembers.map(goDecl) + Signature(mems, tu.result.map(go)) + } + + def go(st: SimpleType)(implicit ectx: ExpCtx): Type = // trace(s"expand $st") { st.unwrapProvs match { case tv: TypeVariable if stopAtTyVars => tv.asTypeVar - case tv: TypeVariable => + case tv: TypeVariable => ectx.tps.getOrElse(tv, { val nv = tv.asTypeVar if (!seenVars(tv)) { seenVars += tv @@ -1312,8 +1790,11 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) } } nv + }) case FunctionType(l, r) => Function(go(l), go(r)) - case ComposedType(true, l, r) => Union(go(l), go(r)) + case ct @ ComposedType(true, l, r) => + if (ct >:< (TrueType | FalseType)) TN("Bool") // TODO should rather be done in TypeSimplifier + else Union(go(l), go(r)) case ComposedType(false, l, r) => Inter(go(l), go(r)) case RecordType(fs) => Record(fs.mapValues(field)) case TupleType(fs) => Tuple(fs.mapValues(field)) @@ -1333,7 +1814,6 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) case obj: ObjectTag => obj.id match { case Var(n) => if (primitiveTypes.contains(n) // primitives like `int` are internally maintained as class tags - || n.isCapitalized // rigid type params like A in class Foo[A] || n === "this" // `this` type ) TypeName(n) else TypeTag(n.capitalize) @@ -1356,23 +1836,38 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool) case Without(base, names) => Rem(go(base), names.toList) case Overload(as) => as.map(go).reduce(Inter) case PolymorphicType(lvl, bod) => + val boundsSize = bounds.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 qvars = bod.varsBetween(lvl, MaxLevel).iterator - if (qvars.isEmpty) b else - PolyType(qvars.map(_.asTypeVar pipe (R(_))).toList, b) + val ftvs = b.freeTypeVariables ++ + newBounds.iterator.map(_._1) ++ + newBounds.iterator.flatMap(_._2.freeTypeVariables) + val fvars = qvars.filter(tv => ftvs.contains(tv.asTypeVar)) + if (fvars.isEmpty) b else + PolyType(fvars.map(_.asTypeVar pipe (R(_))).toList, b) case ConstrainedType(cs, bod) => val (ubs, others1) = cs.groupMap(_._1)(_._2).toList.partition(_._2.sizeIs > 1) val lbs = others1.mapValues(_.head).groupMap(_._2)(_._1).toList val bounds = (ubs.mapValues(_.reduce(_ &- _)) ++ lbs.mapValues(_.reduce(_ | _)).map(_.swap)) val procesased = bounds.map { case (lo, hi) => Bounds(go(lo), go(hi)) } Constrained(go(bod), Nil, procesased) + + // case DeclType(lvl, info) => } // }(r => s"~> $r") - val res = go(st) + val res = goLike(st)(new ExpCtx(Map.empty)) if (bounds.isEmpty) res else Constrained(res, bounds, Nil) + + // goLike(st) } diff --git a/shared/src/main/scala/mlscript/TyperDatatypes.scala b/shared/src/main/scala/mlscript/TyperDatatypes.scala index 5a6b9eeabe..5fc80392a6 100644 --- a/shared/src/main/scala/mlscript/TyperDatatypes.scala +++ b/shared/src/main/scala/mlscript/TyperDatatypes.scala @@ -8,14 +8,16 @@ import scala.annotation.tailrec import mlscript.utils._, shorthands._ import mlscript.Message._ -abstract class TyperDatatypes extends TyperHelpers { self: Typer => +abstract class TyperDatatypes extends TyperHelpers { Typer: Typer => def recordTypeVars: Bool = false type TN = TypeName + val TN: TypeName.type = TypeName // The data types used for type inference: + case class TypeProvenance(loco: Opt[Loc], desc: Str, originName: Opt[Str] = N, isType: Bool = false) { val isOrigin: Bool = originName.isDefined def & (that: TypeProvenance): TypeProvenance = this // arbitrary; maybe should do better @@ -23,79 +25,45 @@ abstract class TyperDatatypes extends TyperHelpers { self: Typer => } type TP = TypeProvenance + sealed abstract class TypeInfo - + + /** A type for abstract classes that is used to check and throw * errors if the abstract class is being instantiated */ case class AbstractConstructor(absMths: Set[Var], isTraitWithMethods: Bool) extends TypeInfo + case class VarSymbol(ty: ST, definingVar: Var) extends TypeInfo + + /** Some type information which may not yet be available. */ + sealed abstract class LazyTypeInfo extends TypeInfo { + def complete()(implicit raise: Raise): NuMember + def kind: DeclKind + def name: Str + } + + /** A LazyTypeInfo whose typing has been completed. */ + case class CompletedTypeInfo(member: NuMember) extends LazyTypeInfo { + def complete()(implicit raise: Raise): NuMember = member + def kind: DeclKind = member.kind + val name: Str = member.name + } + + /** Initialized lazy type information, to be computed soon. */ + class DelayedTypeInfo(val decl: NuDecl, val outerVars: Map[Str, SimpleType]) + (implicit val ctx: Ctx, val raise: Raise) extends LazyTypeInfo with DelayedTypeInfoImpl + + /** A type with universally quantified type variables * (by convention, those variables of level greater than `level` are considered quantified). */ - case class PolymorphicType(polymLevel: Level, body: SimpleType) extends SimpleType { // TODO add own prov? + case class PolymorphicType(polymLevel: Level, body: SimpleType) // TODO add own type provenance for consistency + extends SimpleType with PolymorphicTypeImpl { require(polymLevel < MaxLevel, polymLevel) val prov: TypeProvenance = body.prov lazy val level = levelBelow(polymLevel)(MutSet.empty) def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = body.levelBelow(ub min polymLevel) - def instantiate(implicit ctx: Ctx): SimpleType = { - implicit val state: MutMap[TV, ST] = MutMap.empty - println(s"INST [${polymLevel}] $this") - println(s" where ${showBounds}") - val res = body.freshenAbove(polymLevel, rigidify = false) - println(s"TO [${lvl}] ~> $res") - println(s" where ${res.showBounds}") - res - } - def rigidify(implicit ctx: Ctx, raise: Raise): SimpleType = { - implicit val state: MutMap[TV, ST] = MutMap.empty - body.freshenAbove(polymLevel, rigidify = true) - } - def raiseLevelTo(newPolymLevel: Level, leaveAlone: Set[TV] = Set.empty) - (implicit ctx: Ctx): PolymorphicType = { - require(newPolymLevel >= polymLevel) - if (newPolymLevel === polymLevel) return this - implicit val freshened: MutMap[TV, ST] = MutMap.empty - PolymorphicType(newPolymLevel, - self.freshenAbove(polymLevel, body, leaveAlone = leaveAlone)( - ctx.copy(lvl = newPolymLevel + 1), // * Q: is this really fine? cf. stashing/unstashing etc. - freshened) - ) //(prov) - } - /** Tries to split a polymorphic function type - * by distributing the quantification of *some* of its type vars into the function result. */ - def splitFunction(implicit ctx: Ctx, raise: Raise): Opt[ST] = { - def go(ty: ST, traversed: Set[AnyRef], polymLevel: Level): Opt[ST] = ty match { - case ft @ FunctionType(par, bod) => - val couldBeDistribbed = bod.varsBetween(polymLevel, MaxLevel) - println(s"could be distribbed: $couldBeDistribbed") - if (couldBeDistribbed.isEmpty) return N - val cannotBeDistribbed = par.varsBetween(polymLevel, MaxLevel) - println(s"cannot be distribbed: $cannotBeDistribbed") - val canBeDistribbed = couldBeDistribbed -- cannotBeDistribbed - if (canBeDistribbed.isEmpty) return N // TODO - val newInnerLevel = - (polymLevel + 1) max cannotBeDistribbed.maxByOption(_.level).fold(MinLevel)(_.level) - val innerPoly = PolymorphicType(polymLevel, bod) - println(s"inner: ${innerPoly}") - val res = FunctionType(par, innerPoly.raiseLevelTo(newInnerLevel, cannotBeDistribbed))(ft.prov) - println(s"raised: ${res}") - println(s" where: ${res.showBounds}") - if (cannotBeDistribbed.isEmpty) S(res) - else S(PolymorphicType(polymLevel, res)) - case tr: TypeRef if !traversed.contains(tr.defn) => go(tr.expand, traversed + tr.defn, polymLevel) - case proxy: ProxyType => go(proxy.underlying, traversed, polymLevel) - case tv @ AssignedVariable(ty) if !traversed.contains(tv) => - go(ty, traversed + tv, polymLevel) - case tv: TV if tv.level > polymLevel && !traversed.contains(tv) => - // * A quantified variable in positive position can always be replaced by its LBs - go(tv.lowerBounds.foldLeft(BotType: ST)(_ | _), traversed + tv, polymLevel) - case PolymorphicType(plvl, bod) => - go(bod, traversed, polymLevel min plvl) - case _ => N - } - go(body, Set.empty, polymLevel) - } override def toString = s"‹∀ $polymLevel. $body›" } object PolymorphicType { @@ -172,13 +140,26 @@ abstract class TyperDatatypes extends TyperHelpers { self: Typer => body.fold(PolymorphicType(MinLevel, errType))(b => PolymorphicType(level, ProvType(b._2)(prov))) } + sealed abstract class TypeLike extends TypeLikeImpl { + def unwrapProvs: TypeLike + } + type TL = TypeLike + + abstract class OtherTypeLike extends TypeLike { this: TypedTypingUnit => + def self: TypedTypingUnit = this + def unwrapProvs: TypeLike = this + } + object OtherTypeLike { + def unapply(ot: OtherTypeLike): S[TypedTypingUnit] = S(ot.self) + } + /** A general type form (TODO: rename to AnyType). */ - sealed abstract class SimpleType extends SimpleTypeImpl { + sealed abstract class SimpleType extends TypeLike with SimpleTypeImpl { val prov: TypeProvenance def level: Level def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level def freshenAbove(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): SimpleType = - self.freshenAbove(lim, this, rigidify) + Typer.freshenAbove(lim, this, rigidify) constructedTypes += 1 } type ST = SimpleType @@ -203,6 +184,7 @@ abstract class TyperDatatypes extends TyperHelpers { self: Typer => def freshenAboveImpl(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): FunctionType = FunctionType(lhs.freshenAbove(lim, rigidify), rhs.freshenAbove(lim, rigidify))(prov) override def toString = s"(${lhs match { + case TupleType((N, FieldType(N, f: TupleType)) :: Nil) => "[" + f.showInner + "]" case TupleType((N, f) :: Nil) => f.toString case lhs => lhs }} -> $rhs)" @@ -218,7 +200,10 @@ abstract class TyperDatatypes extends TyperHelpers { self: Typer => Overload(alts.map(ft => FunctionType(f(pol.contravar, ft.lhs), f(pol, ft.rhs))(ft.prov)))(prov) def approximatePos: FunctionType = { val (lhss, rhss) = alts.map(ft => ft.lhs -> ft.rhs).unzip - FunctionType(lhss.reduce(_ & _), rhss.reduce(_ | _))(prov) + FunctionType(lhss.reduce(_ | _), rhss.reduce(_ | _))(prov) + // * Note: technically the following is another valid (but probably less useful) + // * approximation of the same function type: + // FunctionType(lhss.reduce(_ & _), rhss.reduce(_ & _))(prov) } lazy val level: Level = levelBelow(MaxLevel)(MutSet.empty) def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = @@ -239,7 +224,7 @@ abstract class TyperDatatypes extends TyperHelpers { self: Typer => lazy val level: Level = levelBelow(MaxLevel)(MutSet.empty) def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = fields.iterator.map(_._2.levelBelow(ub)).maxOption.getOrElse(MinLevel) override def freshenAbove(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): RecordType = - self.mapPol(this, N, false)((_, x) => x.freshenAbove(lim, rigidify)) + Typer.mapPol(this, N, false)((_, x) => x.freshenAbove(lim, rigidify)) def toInter: SimpleType = fields.map(f => RecordType(f :: Nil)(prov)).foldLeft(TopType: ST)(((l, r) => ComposedType(false, l, r)(noProv))) def mergeAllFields(fs: Iterable[Var -> FieldType]): RecordType = { @@ -255,7 +240,7 @@ abstract class TyperDatatypes extends TyperHelpers { self: Typer => RecordType(fields.filterNot(f => shadowing(f._1)) ++ fs)(prov) } def sorted: RecordType = RecordType(fields.sortBy(_._1))(prov) - override def toString = s"{${fields.map(f => s"${f._1}: ${f._2}").mkString(", ")}}" + override def toString = s"{${fields.map(f => s"${f._1.name}: ${f._2}").mkString(", ")}}" } object RecordType { def empty: RecordType = RecordType(Nil)(noProv) @@ -284,14 +269,15 @@ abstract class TyperDatatypes extends TyperHelpers { self: Typer => lazy val toArray: ArrayType = ArrayType(inner)(prov) // upcast to array override lazy val toRecord: RecordType = RecordType( - fields.zipWithIndex.map { case ((_, t), i) => (Var("_"+(i+1)), t) } + fields.zipWithIndex.map { case ((_, t), i) => (Var(i.toString), t) } // Note: In line with TypeScript, tuple field names are pure type system fictions, // with no runtime existence. Therefore, they should not be included in the record type // corresponding to this tuple type. // i.e., no `::: fields.collect { case (S(n), t) => (n, t) }` )(prov) - override def toString = - s"(${fields.map(f => s"${f._1.fold("")(_.name+": ")}${f._2},").mkString(" ")})" + def showInner: Str = + fields.map(f => s"${f._1.fold("")(_.name+": ")}${f._2},").mkString(" ") + override def toString = s"($showInner)" // override def toString = s"(${fields.map(f => s"${f._1.fold("")(_+": ")}${f._2},").mkString(" ")})" } @@ -388,71 +374,12 @@ abstract class TyperDatatypes extends TyperHelpers { self: Typer => } type TR = TypeRef - case class TypeRef(defn: TypeName, targs: Ls[SimpleType])(val prov: TypeProvenance) extends SimpleType { + val TR: TypeRef.type = TypeRef + case class TypeRef(defn: TypeName, targs: Ls[SimpleType])(val prov: TypeProvenance) extends SimpleType with TypeRefImpl { def level: Level = targs.iterator.map(_.level).maxOption.getOrElse(MinLevel) def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = targs.iterator.map(_.levelBelow(ub)).maxOption.getOrElse(MinLevel) override def freshenAbove(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): TypeRef = TypeRef(defn, targs.map(_.freshenAbove(lim, rigidify)))(prov) - def expand(implicit ctx: Ctx): SimpleType = expandWith(paramTags = true) - def expandWith(paramTags: Bool)(implicit ctx: Ctx): SimpleType = { - val td = ctx.tyDefs(defn.name) - require(targs.size === td.tparamsargs.size) - lazy val tparamTags = - if (paramTags) RecordType.mk(td.tparamsargs.map { case (tp, tv) => - val tvv = td.getVariancesOrDefault - tparamField(defn, tp) -> FieldType( - Some(if (tvv(tv).isCovariant) BotType else tv), - if (tvv(tv).isContravariant) TopType else tv)(prov) - })(noProv) - else TopType - subst(td.kind match { - case Als => td.bodyTy - case Nms => throw new NotImplementedError("Namespaces are not supported yet.") - case Cls => clsNameToNomTag(td)(prov, ctx) & td.bodyTy & tparamTags - case Trt => trtNameToNomTag(td)(prov, ctx) & td.bodyTy & tparamTags - }, td.targs.lazyZip(targs).toMap) //.withProv(prov) - } //tap { res => println(s"Expand $this => $res") } - private var tag: Opt[Opt[ClassTag]] = N - def mkTag(implicit ctx: Ctx): Opt[ClassTag] = tag.getOrElse { - val res = ctx.tyDefs.get(defn.name) match { - case S(td: TypeDef) if td.kind is Cls => S(clsNameToNomTag(td)(noProv, ctx)) - case _ => N - } - tag = S(res) - res - } - def mapTargs[R](pol: Opt[Bool])(f: (Opt[Bool], ST) => R)(implicit ctx: Ctx): Ls[R] = { - val td = ctx.tyDefs(defn.name) - td.tvarVariances.fold(targs.map(f(N, _))) { tvv => - assert(td.tparamsargs.sizeCompare(targs) === 0) - (td.tparamsargs lazyZip targs).map { case ((_, tv), ta) => - tvv(tv) match { - case VarianceInfo(true, true) => - f(N, TypeBounds(BotType, TopType)(noProv)) - case VarianceInfo(co, contra) => - f(if (co) pol else if (contra) pol.map(!_) else N, ta) - } - }} - } - // TODO dedup w/ above - def mapTargs[R](pol: PolMap)(f: (PolMap, ST) => R)(implicit ctx: Ctx): Ls[R] = { - val td = ctx.tyDefs.getOrElse(defn.name, - // * This should only happen in the presence of ill-formed type definitions; - // * TODO: Make sure to report this and raise a compiler internal error if the source - // * does not actually have a type error! Otherwise we could silently get wrong results... - return Nil - ) - td.tvarVariances.fold(targs.map(f(pol.invar, _))) { tvv => - assert(td.tparamsargs.sizeCompare(targs) === 0) - (td.tparamsargs lazyZip targs).map { case ((_, tv), ta) => - tvv(tv) match { - case VarianceInfo(true, true) => - f(pol.invar, TypeBounds(BotType, TopType)(noProv)) - case VarianceInfo(co, contra) => - f(if (co) pol else if (contra) pol.contravar else pol.invar, ta) - } - }} - } override def toString = showProvOver(false) { val displayName = if (primitiveTypes.contains(defn.name)) defn.name.capitalize else defn.name @@ -475,7 +402,6 @@ abstract class TyperDatatypes extends TyperHelpers { self: Typer => case class ClassTag(id: SimpleTerm, parents: Set[TypeName])(val prov: TypeProvenance) extends BaseType with TypeTag with ObjectTag { - lazy val parentsST = parents.iterator.map(tn => Var(tn.name)).toSet[IdentifiedTerm] def glb(that: ClassTag): Opt[ClassTag] = if (that.id === this.id) S(this) else if (that.parentsST.contains(this.id)) S(that) @@ -490,19 +416,20 @@ abstract class TyperDatatypes extends TyperHelpers { self: Typer => def level: Level = MinLevel def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = MinLevel def freshenAboveImpl(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): this.type = this - override def toString = showProvOver(false)(id.idStr+s"<${parents.map(_.name).mkString(",")}>") } sealed trait TypeVarOrRigidVar extends SimpleType sealed trait ObjectTag extends TypeTag { val id: SimpleTerm - override def toString = "#" + id.idStr + val parents: Set[TypeName] + lazy val parentsST = parents.iterator.map(tn => Var(tn.name)).toSet[IdentifiedTerm] + override def toString = "#" + showProvOver(false)(id.idStr+s"<${parents.map(_.name).mkString(",")}>") } sealed abstract class AbstractTag extends BaseTypeOrTag with TypeTag with Factorizable - case class TraitTag(id: Var)(val prov: TypeProvenance) extends AbstractTag with ObjectTag { + case class TraitTag(id: Var, parents: Set[TypeName])(val prov: TypeProvenance) extends AbstractTag with ObjectTag { def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = MinLevel def level: Level = MinLevel } @@ -569,6 +496,14 @@ abstract class TyperDatatypes extends TyperHelpers { self: Typer => override def toString = lb.fold(s"$ub")(lb => s"mut ${if (lb === BotType) "" else lb}..$ub") } + object FieldType { + def mk(vi: VarianceInfo, lb: ST, ub: ST)(prov: TP): FieldType = vi match { + case VarianceInfo(true, true) => FieldType(N, TopType)(prov) + case VarianceInfo(true, false) => FieldType(N, ub)(prov) + case VarianceInfo(false, true) => FieldType(S(lb), TopType)(prov) + case VarianceInfo(false, false) => FieldType(S(lb), ub)(prov) + } + } val createdTypeVars: Buffer[TV] = Buffer.empty @@ -592,13 +527,19 @@ abstract class TyperDatatypes extends TyperHelpers { self: Typer => if (recordTypeVars) createdTypeVars += this - var assignedTo: Opt[ST] = N + // var assignedTo: Opt[ST] = N + private var _assignedTo: Opt[ST] = N + def assignedTo: Opt[ST] = _assignedTo + def assignedTo_=(value: Opt[ST]): Unit = { + require(value.forall(_.level <= level)) + _assignedTo = value + } // * Bounds should always be disregarded when `equatedTo` is defined, as they are then irrelevant: - def lowerBounds: List[SimpleType] = { require(assignedTo.isEmpty); _lowerBounds } - def upperBounds: List[SimpleType] = { require(assignedTo.isEmpty); _upperBounds } - def lowerBounds_=(bs: Ls[ST]): Unit = { require(assignedTo.isEmpty); _lowerBounds = bs } - def upperBounds_=(bs: Ls[ST]): Unit = { require(assignedTo.isEmpty); _upperBounds = bs } + def lowerBounds: List[SimpleType] = { require(assignedTo.isEmpty, this); _lowerBounds } + def upperBounds: List[SimpleType] = { require(assignedTo.isEmpty, this); _upperBounds } + def lowerBounds_=(bs: Ls[ST]): Unit = { require(assignedTo.isEmpty, this); _lowerBounds = bs } + def upperBounds_=(bs: Ls[ST]): Unit = { require(assignedTo.isEmpty, this); _upperBounds = bs } private val creationRun = currentConstrainingRun def original: TV = @@ -607,15 +548,6 @@ abstract class TyperDatatypes extends TyperHelpers { self: Typer => private lazy val trueOriginal: Opt[TV] = originalTV.flatMap(_.trueOriginal.orElse(originalTV)) - override def freshenAbove(lim: Int, rigidify: Bool) - (implicit ctx: Ctx, freshened: MutMap[TV, ST]) - : TypeVarOrRigidVar = - super.freshenAbove(lim, rigidify) match { - case tv: TypeVarOrRigidVar => - tv // * Note that type variables can be refreshed as rigid variables (trait tags) - case _ => die - } - def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = if (level <= ub) level else { if (cache(this)) MinLevel else { @@ -639,7 +571,7 @@ abstract class TyperDatatypes extends TyperHelpers { self: Typer => override def toString: String = (trueOriginal match { case S(to) => - assert(to.nameHint === nameHint) + assert(to.nameHint === nameHint, (to.nameHint, nameHint)) to.mkStr + "_" + uid + showLevel(level) case N => showProvOver(false)(mkStr + showLevel(level)) @@ -647,25 +579,35 @@ abstract class TyperDatatypes extends TyperHelpers { self: Typer => private[mlscript] def mkStr = nameHint.getOrElse("α") + uid // * `omitIrrelevantVars` omits top-level as well as quantified variable occurrences - def isRecursive_$(omitIrrelevantVars: Bool)(implicit ctx: Ctx) : Bool = - (lbRecOccs_$(omitIrrelevantVars), ubRecOccs_$(omitIrrelevantVars)) match { - // * Variables occurring strictly negatively in their own lower bound - // * (resp. strictly positively in their own upper bound, ie contravariantly) - // * are NOT recursive, as these occurrences only demonstrate "spurious" cycles - // * which are easily removed. - case (S(N | S(true)), _) | (_, S(N | S(false))) => true + final def isRecursive_$(omitIrrelevantVars: Bool)(implicit ctx: Ctx) : Bool = + isPosRecursive_$(omitIrrelevantVars) || isNegRecursive_$(omitIrrelevantVars) + // * Variables occurring strictly negatively in their own lower bound + // * (resp. strictly positively in their own upper bound, ie contravariantly) + // * are NOT recursive, as these occurrences only demonstrate "spurious" cycles + // * which are easily removed. + final def isPosRecursive_$(omitIrrelevantVars: Bool)(implicit ctx: Ctx) : Bool = lbRecOccs_$(omitIrrelevantVars) match { + case S(N | S(true)) => true + case _ => false + } + final def isNegRecursive_$(omitIrrelevantVars: Bool)(implicit ctx: Ctx) : Bool = ubRecOccs_$(omitIrrelevantVars) match { + case S(N | S(false)) => true case _ => false } /** None: not recursive in this bound; Some(Some(pol)): polarly-recursive; Some(None): nonpolarly-recursive. * Note that if we have something like 'a :> Bot <: 'a -> Top, 'a is not truly recursive - * and its bounds can actually be inlined. */ + * and its bounds can actually be inlined. + * Also note that unfortunately, contrary to whta I previously thought, + * it is not sound to ignore quantified variables during the getVarsPol search. + * indeed, we can be in freaky situations like in `ListBuild.mls` + * where we have `'a :> Ls[('x, forall 'a. 'a)]`! */ private[mlscript] final def lbRecOccs_$(omitIrrelevantVars: Bool)(implicit ctx: Ctx): Opt[Opt[Bool]] = { // println("+", this, assignedTo getOrElse lowerBounds) // assignedTo.getOrElse(TupleType(lowerBounds.map(N -> _.toUpper(noProv)))(noProv)).getVarsPol(PolMap.pos, ignoreTopLevelOccs = true).get(this) val bs = assignedTo.fold(lowerBounds)(_ :: Nil) bs.foldLeft(BotType: ST)(_ | _).getVarsPol(PolMap.pos, ignoreTopLevelOccs = omitIrrelevantVars, - ignoreQuantifiedVars = omitIrrelevantVars, + // ignoreQuantifiedVars = omitIrrelevantVars, + ignoreQuantifiedVars = false, ).get(this) } private[mlscript] final def ubRecOccs_$(omitIrrelevantVars: Bool)(implicit ctx: Ctx): Opt[Opt[Bool]] ={ @@ -674,7 +616,8 @@ abstract class TyperDatatypes extends TyperHelpers { self: Typer => val bs = assignedTo.fold(upperBounds)(_ :: Nil) bs.foldLeft(TopType: ST)(_ & _).getVarsPol(PolMap.posAtNeg, ignoreTopLevelOccs = omitIrrelevantVars, - ignoreQuantifiedVars = omitIrrelevantVars, + // ignoreQuantifiedVars = omitIrrelevantVars, + ignoreQuantifiedVars = false, ).get(this) // .tap(r => println(s"= $r")) } diff --git a/shared/src/main/scala/mlscript/TyperHelpers.scala b/shared/src/main/scala/mlscript/TyperHelpers.scala index 6d36c8f4c9..07b14736a8 100644 --- a/shared/src/main/scala/mlscript/TyperHelpers.scala +++ b/shared/src/main/scala/mlscript/TyperHelpers.scala @@ -1,6 +1,6 @@ package mlscript -import scala.collection.mutable.{Map => MutMap, Set => MutSet, LinkedHashMap, LinkedHashSet} +import scala.collection.mutable.{Map => MutMap, SortedMap => MutSortMap, Set => MutSet, LinkedHashMap, LinkedHashSet} import scala.collection.immutable.{SortedMap, SortedSet} import scala.annotation.tailrec import mlscript.utils._, shorthands._ @@ -82,9 +82,17 @@ abstract class TyperHelpers { Typer: Typer => (implicit ctx: Ctx): PolymorphicType = PolymorphicType(ts.polymLevel, subst(ts.body, map)) + def substLike(ty: TL, map: Map[SimpleType, SimpleType], substInMap: Bool)(implicit ctx: Ctx): TL = ty match { + case ty: ST => subst(ty, map, substInMap) + case OtherTypeLike(ot) => + TypedTypingUnit( + ot.implementedMembers.map(_.map(subst(_, map, substInMap))), + ot.result.map(subst(_, map, substInMap))) + } def subst(st: SimpleType, map: Map[SimpleType, SimpleType], substInMap: Bool = false) (implicit ctx: Ctx): SimpleType = { val cache: MutMap[TypeVariable, SimpleType] = MutMap.empty + implicit val freshened: MutMap[TV, ST] = MutMap.empty val subsLvl: Level = map.valuesIterator.map(_.level).reduceOption(_ max _).getOrElse(MinLevel) def go(st: SimpleType): SimpleType = { // trace(s"subst($st)") { @@ -109,7 +117,7 @@ abstract class TyperHelpers { Typer: Typer => v }) case poly: PolymorphicType if poly.polymLevel < subsLvl => - go(poly.raiseLevelTo(subsLvl)) + go(poly.raiseLevelToImpl(subsLvl, Set.empty)) case _ => st.map(go(_)) } } @@ -259,6 +267,12 @@ abstract class TyperHelpers { Typer: Typer => trait SimpleTypeImpl { self: SimpleType => + // TODO eventually find a way of statically getting rid of those + def assertTV: TV = this match { + case tv: TV => tv + case _ => lastWords(s"$this was not a type variable") + } + def showProvOver(enabled: Bool)(str: Str): Str = if (enabled) str + prov.toString else str @@ -533,11 +547,11 @@ abstract class TyperHelpers { Typer: Typer => case (_, NegType(und)) => (this & und) <:< BotType case (NegType(und), _) => TopType <:< (that | und) case (tr: TypeRef, _) - if (primitiveTypes contains tr.defn.name) => tr.expand <:< that + if (primitiveTypes contains tr.defn.name) && tr.canExpand => tr.expandOrCrash <:< that case (_, tr: TypeRef) - if (primitiveTypes contains tr.defn.name) => this <:< tr.expand - case (tr1: TypeRef, _) => - val td1 = ctx.tyDefs(tr1.defn.name) + if (primitiveTypes contains tr.defn.name) && tr.canExpand => this <:< tr.expandOrCrash + case (tr1: TypeRef, _) => ctx.tyDefs.get(tr1.defn.name) match { + case S(td1) => that match { case tr2: TypeRef if tr2.defn === tr1.defn => val tvv = td1.getVariancesOrDefault @@ -548,6 +562,8 @@ abstract class TyperHelpers { Typer: Typer => case _ => (td1.kind is Cls) && clsNameToNomTag(td1)(noProv, ctx) <:< that } + case N => false // TODO look into ctx.tyDefs2 + } case (_, _: TypeRef) => false // TODO try to expand them (this requires populating the cache because of recursive types) case (_: PolymorphicType, _) | (_, _: PolymorphicType) => false @@ -588,14 +604,7 @@ abstract class TyperHelpers { Typer: Typer => case t @ RecordType(fs) => RecordType(fs.filter(nt => !names(nt._1)))(t.prov) case t @ TupleType(fs) => val relevantNames = names.filter(n => - n.name.startsWith("_") - && { - val t = n.name.tail - t.forall(_.isDigit) && { - val n = t.toInt - 1 <= n && n <= fs.length - } - }) + n.toIndexOption.exists((0 until fs.length).contains)) if (relevantNames.isEmpty) t else { val rcd = t.toRecord @@ -622,7 +631,7 @@ abstract class TyperHelpers { Typer: Typer => case ct: ConstrainedType => ct } def unwrapAll(implicit ctx: Ctx): SimpleType = unwrapProxies match { - case tr: TypeRef => tr.expand.unwrapAll + case tr: TypeRef if tr.canExpand => tr.expandOrCrash.unwrapAll case u => u } def negNormPos(f: SimpleType => SimpleType, p: TypeProvenance) @@ -631,7 +640,7 @@ abstract class TyperHelpers { Typer: Typer => case ComposedType(true, l, r) => l.negNormPos(f, p) & r.negNormPos(f, p) 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.expand.negNormPos(f, 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. @@ -703,8 +712,9 @@ abstract class TyperHelpers { Typer: Typer => 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 _: TypeTag => 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 @@ -713,33 +723,123 @@ abstract class TyperHelpers { Typer: Typer => 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] + val traversed = MutSet.empty[TypeVariable] + def go(ty: ST, lb: Level, ub: Level): Unit = + if (lb < ub && ty.level > lb) { // * Don't traverse types with lower levels + // trace(s"varsBetween($ty, $lb, $ub)") { + ty match { + case tv: TypeVariable => + if (traversed(tv)) () + else { + traversed += tv + if (tv.level > lb && tv.level <= ub) { + // println(s"ADD $tv") + res += tv + } + tv.children(includeBounds = true) // * Note: `children` deals with `assignedTo` + .foreach(go(_, lb, ub)) + } + case pt: PolymorphicType => + go(pt.body, lb, pt.polymLevel min ub) + case ty => + ty.children(includeBounds = true) // * Q: is `includeBounds` useful here? + .foreach(go(_, lb, ub)) + } + // }() + } + go(this, lb, ub) + res.toSet + } + + def expPos(implicit ctx: Ctx): mlscript.TypeLike = exp(S(true), this) + def expNeg(implicit ctx: Ctx): mlscript.TypeLike = exp(S(false), this) + def exp(pol: Opt[Bool], ty: ST)(implicit ctx: Ctx): mlscript.TypeLike = ( + ty + // |> (_.normalize(false)) + // |> (simplifyType(_, pol, removePolarVars = false, inlineBounds = false)) + // |> (shallowCopy) + |> (subst(_, Map.empty)) // * Make a copy of the type and its TV graph – although we won't show the TV bounds, we still care about the bounds as they affect class type reconstruction in normalizeTypes_! + |> (normalizeTypes_!(_, pol)(ctx)) + |> (expandType(_, stopAtTyVars = true)) + ) + + } + + + + trait TypeLikeImpl { self: TypeLike => + def childrenPol(pol: PolMap)(implicit ctx: Ctx): List[PolMap -> SimpleType] = { - def childrenPolField(fld: FieldType): List[PolMap -> SimpleType] = + def childrenPolField(pol: PolMap)(fld: FieldType): List[PolMap -> SimpleType] = fld.lb.map(pol.contravar -> _).toList ::: pol.covar -> fld.ub :: Nil + def childrenPolMem(m: NuMember): List[PolMap -> SimpleType] = m match { + case NuParam(nme, ty, pub) => childrenPolField(PolMap.pos)(ty) // TODO invariant when mutable + case TypedNuFun(level, fd, ty) => pol -> ty :: Nil + case td: TypedNuDecl => TypedTypingUnit(td :: Nil, N).childrenPol(pol: PolMap) // TODO refactor + // case NuTypeParam(nme, ty) => childrenPolField(PolMap.pos)(ty) + } this match { case tv @ AssignedVariable(ty) => pol -> ty :: Nil case tv: TypeVariable => - (if (pol(tv) =/= S(false)) tv.lowerBounds.map(pol.at(tv.level, true) -> _) else Nil) ::: - (if (pol(tv) =/= S(true)) tv.upperBounds.map(pol.at(tv.level, false) -> _) else Nil) + 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) 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 - 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 RecordType(fs) => fs.unzip._2.flatMap(childrenPolField(pol)) + case TupleType(fs) => fs.unzip._2.flatMap(childrenPolField(pol)) + case ArrayType(fld) => childrenPolField(pol)(fld) + case SpliceType(elems) => elems flatMap {case L(l) => pol -> l :: Nil case R(r) => childrenPolField(pol)(r)} case NegType(n) => pol.contravar -> 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 _: TypeTag => Nil case tr: TypeRef => tr.mapTargs(pol)(_ -> _) case Without(b, ns) => pol -> b :: Nil case TypeBounds(lb, ub) => PolMap.neg -> lb :: PolMap.pos -> ub :: Nil case PolymorphicType(_, und) => pol -> und :: Nil case ConstrainedType(cs, bod) => cs.flatMap(vbs => PolMap.pos -> vbs._1 :: PolMap.posAtNeg -> vbs._2 :: Nil) ::: pol -> bod :: Nil + case OtherTypeLike(tu) => + // tu.entities.flatMap(_.childrenPol) ::: tu.result.toList + val ents = tu.implementedMembers.flatMap { + case ta: TypedNuAls => + // Q: PolMap.neu or pol.invar?! + ta.tparams.map(pol.invar -> _._2) ::: pol -> ta.body :: Nil + case tf: TypedNuFun => + PolMap.pos -> tf.bodyType :: Nil + case mxn: TypedNuMxn => + mxn.tparams.iterator.map(pol.invar -> _._2) ++ + mxn.members.valuesIterator.flatMap(childrenPolMem) ++ + S(pol.contravar -> mxn.superTy) ++ + S(pol.contravar -> mxn.thisTy) + case cls: TypedNuCls => + cls.tparams.iterator.map(pol.invar -> _._2) ++ + // cls.params.flatMap(p => childrenPolField(pol.invar)(p._2)) + cls.params.toList.flatMap(_.flatMap(p => childrenPolField(PolMap.pos)(p._2))) ++ + cls.auxCtorParams.toList.flatMap(_.map(PolMap.neg -> _._2)) ++ + cls.members.valuesIterator.flatMap(childrenPolMem) ++ + S(pol.contravar -> cls.thisTy) ++ + S(pol.covar -> cls.sign) ++ + // S(pol.covar -> cls.instanceType) ++ // Not a real child; to remove + cls.parentTP.valuesIterator.flatMap(childrenPolMem) + case trt: TypedNuTrt => + trt.tparams.iterator.map(pol.invar -> _._2) ++ + trt.members.valuesIterator.flatMap(childrenPolMem) ++ + S(pol.contravar -> trt.thisTy) ++ + S(pol.covar -> trt.sign) ++ + trt.parentTP.valuesIterator.flatMap(childrenPolMem) + case prm: NuParam => childrenPolField(pol)(prm.ty) + case TypedNuDummy(d) => N + } + ents ::: tu.result.toList.map(pol -> _) }} /** `ignoreTopLevelOccs` is used to discard immediate occurrences of a variable which @@ -750,8 +850,8 @@ abstract class TyperHelpers { Typer: Typer => val res = MutMap.empty[TV, Pol] val traversed = MutSet.empty[TV -> Bool] // * We ignore variables above `upperLvl` when `ignoreQuantifiedVars` is true. - def go(pol: PolMap, ignoreTLO: Bool, upperLvl: Level)(ty: ST): Unit = { - trace(s"getVarsPol[${printPol(pol.base)}] $ty ${pol} $ignoreTLO") { + def go(pol: PolMap, ignoreTLO: Bool, upperLvl: Level)(ty: TypeLike): Unit = { + // trace(s"getVarsPol[${printPol(pol.base)}] $ty ${pol} $ignoreTLO") { ty match { case tv: TypeVariable => if (ignoreQuantifiedVars && tv.level > upperLvl) return println(s"Quantified! ${tv}") @@ -809,12 +909,39 @@ abstract class TyperHelpers { Typer: Typer => // * just spurious occurs-check failures! ignoreTLO = false, upperLvl)(cp._2)) } - }() + // }() } go(pol, ignoreTopLevelOccs, MaxLevel)(this) res.toSortedMap } + private def childrenMem(m: NuMember): IterableOnce[ST] = m match { + case tf: TypedNuFun => + tf.bodyType :: Nil + case als: TypedNuAls => + als.tparams.iterator.map(_._2) ++ S(als.body) + case mxn: TypedNuMxn => + mxn.tparams.iterator.map(_._2) ++ + mxn.members.valuesIterator.flatMap(childrenMem) ++ + S(mxn.superTy) ++ + S(mxn.thisTy) + case cls: TypedNuCls => + cls.tparams.iterator.map(_._2) ++ + cls.params.toList.flatMap(_.flatMap(p => p._2.lb.toList ::: p._2.ub :: Nil)) ++ + cls.auxCtorParams.toList.flatMap(_.values) ++ + cls.members.valuesIterator.flatMap(childrenMem) ++ + S(cls.thisTy) ++ + S(cls.sign) + case trt: TypedNuTrt => + trt.tparams.iterator.map(_._2) ++ + trt.members.valuesIterator.flatMap(childrenMem) ++ + S(trt.thisTy) ++ + S(trt.sign) ++ + trt.parentTP.valuesIterator.flatMap(childrenMem) + case p: NuParam => + p.ty.lb.toList ::: p.ty.ub :: Nil + case TypedNuDummy(d) => Nil + } 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 @@ -827,59 +954,33 @@ abstract class TyperHelpers { Typer: Typer => case NegType(n) => n :: Nil case ExtrType(_) => Nil case ProxyType(und) => und :: Nil + // case _: TypeTag => Nil + case _: ObjectTag | _: Extruded => Nil case SkolemTag(id) => id :: Nil - case _: TypeTag => Nil case TypeRef(d, ts) => ts case Without(b, ns) => b :: Nil case TypeBounds(lb, ub) => lb :: ub :: Nil case PolymorphicType(_, und) => und :: Nil case ConstrainedType(cs, und) => cs.flatMap(lu => lu._1 :: lu._2 :: Nil) ::: und :: Nil case SpliceType(fs) => fs.flatMap{ case L(l) => l :: Nil case R(r) => r.lb.toList ::: r.ub :: Nil} + case OtherTypeLike(tu) => + val ents = tu.implementedMembers.flatMap(childrenMem) + ents ::: tu.result.toList } - def getVars: SortedSet[TypeVariable] = { + def getVarsImpl(includeBounds: Bool): SortedSet[TypeVariable] = { val res = MutSet.empty[TypeVariable] - @tailrec def rec(queue: List[SimpleType]): Unit = queue match { + @tailrec def rec(queue: List[TypeLike]): Unit = queue match { case (tv: TypeVariable) :: tys => if (res(tv)) rec(tys) - else { res += tv; rec(tv.children(includeBounds = true) ::: tys) } - case ty :: tys => rec(ty.children(includeBounds = true) ::: tys) + else { res += tv; rec(tv.children(includeBounds = includeBounds) ::: tys) } + case ty :: tys => rec(ty.children(includeBounds = includeBounds) ::: tys) case Nil => () } rec(this :: Nil) SortedSet.from(res)(Ordering.by(_.uid)) } - - /** (exclusive, inclusive) */ - def varsBetween(lb: Level, ub: Level): Set[TV] = { - val res = MutSet.empty[TypeVariable] - val traversed = MutSet.empty[TypeVariable] - def go(ty: ST, lb: Level, ub: Level): Unit = - if (lb < ub && ty.level > lb) { // * Don't traverse types with lower levels - // trace(s"varsBetween($ty, $lb, $ub)") { - ty match { - case tv: TypeVariable => - if (traversed(tv)) () - else { - traversed += tv - if (tv.level > lb && tv.level <= ub) { - // println(s"ADD $tv") - res += tv - } - tv.children(includeBounds = true) // * Note: `children` deals with `assignedTo` - .foreach(go(_, lb, ub)) - } - case pt: PolymorphicType => - go(pt.body, lb, pt.polymLevel min ub) - case ty => - ty.children(includeBounds = true) // * Q: is `includeBounds` useful here? - .foreach(go(_, lb, ub)) - } - // }() - } - go(this, lb, ub) - res.toSet - } + def getVars: SortedSet[TypeVariable] = getVarsImpl(includeBounds = true) def showBounds: String = getVars.iterator.filter(tv => tv.assignedTo.nonEmpty || (tv.upperBounds ++ tv.lowerBounds).nonEmpty).map { @@ -889,17 +990,254 @@ abstract class TyperHelpers { Typer: Typer => + (if (tv.upperBounds.isEmpty) "" else " <: " + tv.upperBounds.mkString(" & "))) }.mkString - def expPos(implicit ctx: Ctx): Type = exp(S(true), this) - def expNeg(implicit ctx: Ctx): Type = exp(S(false), this) - def exp(pol: Opt[Bool], ty: ST)(implicit ctx: Ctx): Type = ( - ty - // |> (_.normalize(false)) - // |> (simplifyType(_, pol, removePolarVars = false, inlineBounds = false)) - // |> (shallowCopy) - |> (subst(_, Map.empty)) // * Make a copy of the type and its TV graph – although we won't show the TV bounds, we still care about the bounds as they affect class type reconstruction in normalizeTypes_! - |> (normalizeTypes_!(_, pol)(ctx)) - |> (expandType(_, stopAtTyVars = true)) - ) + } + + + + trait PolymorphicTypeImpl { self: PolymorphicType => + + def instantiate(implicit ctx: Ctx): SimpleType = { + implicit val state: MutMap[TV, ST] = MutMap.empty + println(s"INST [${polymLevel}] $this") + println(s" where ${showBounds}") + val res = body.freshenAbove(polymLevel, rigidify = false) + println(s"TO [${lvl}] ~> $res") + println(s" where ${res.showBounds}") + res + } + def rigidify(implicit ctx: Ctx, raise: Raise): SimpleType = { + implicit val state: MutMap[TV, ST] = MutMap.empty + body.freshenAbove(polymLevel, rigidify = true) + } + def raiseLevelTo(newPolymLevel: Level, leaveAlone: Set[TV] = Set.empty) + (implicit ctx: Ctx): PolymorphicType = { + implicit val freshened: MutMap[TV, ST] = MutMap.empty + raiseLevelToImpl(newPolymLevel, leaveAlone) + } + def raiseLevelToImpl(newPolymLevel: Level, leaveAlone: Set[TV]) + (implicit ctx: Ctx, freshened: MutMap[TV, ST]): PolymorphicType = { + require(newPolymLevel >= polymLevel) + if (newPolymLevel === polymLevel) return this + PolymorphicType(newPolymLevel, + Typer.freshenAbove(polymLevel, body, leaveAlone = leaveAlone)( + ctx.copy(lvl = newPolymLevel + 1), // * Q: is this really fine? cf. stashing/unstashing etc. + freshened) + ) //(prov) + } + /** Tries to split a polymorphic function type + * by distributing the quantification of *some* of its type vars into the function result. */ + def splitFunction(implicit ctx: Ctx, raise: Raise): Opt[ST] = { + def go(ty: ST, traversed: Set[AnyRef], polymLevel: Level): Opt[ST] = ty match { + case ft @ FunctionType(par, bod) => + val couldBeDistribbed = bod.varsBetween(polymLevel, MaxLevel) + println(s"could be distribbed: $couldBeDistribbed") + if (couldBeDistribbed.isEmpty) return N + val cannotBeDistribbed = par.varsBetween(polymLevel, MaxLevel) + println(s"cannot be distribbed: $cannotBeDistribbed") + val canBeDistribbed = couldBeDistribbed -- cannotBeDistribbed + if (canBeDistribbed.isEmpty) return N // TODO + val newInnerLevel = + (polymLevel + 1) max cannotBeDistribbed.maxByOption(_.level).fold(MinLevel)(_.level) + val innerPoly = PolymorphicType(polymLevel, bod) + println(s"inner: ${innerPoly}") + val res = FunctionType(par, innerPoly.raiseLevelTo(newInnerLevel, cannotBeDistribbed))(ft.prov) + println(s"raised: ${res}") + println(s" where: ${res.showBounds}") + if (cannotBeDistribbed.isEmpty) S(res) + else S(PolymorphicType(polymLevel, res)) + case tr: TypeRef if !traversed.contains(tr.defn) => go(tr.expand, traversed + tr.defn, polymLevel) + case proxy: ProxyType => go(proxy.underlying, traversed, polymLevel) + case tv @ AssignedVariable(ty) if !traversed.contains(tv) => + go(ty, traversed + tv, polymLevel) + case tv: TV if tv.level > polymLevel && !traversed.contains(tv) => + // * A quantified variable in positive position can always be replaced by its LBs + go(tv.lowerBounds.foldLeft(BotType: ST)(_ | _), traversed + tv, polymLevel) + case PolymorphicType(plvl, bod) => + go(bod, traversed, polymLevel min plvl) + case _ => N + } + go(body, Set.empty, polymLevel) + } + + } + + + + trait TypeRefImpl { self: TypeRef => + + def canExpand(implicit ctx: Ctx): Bool = + ctx.tyDefs2.get(defn.name).forall(info => + // * Object types do not need to be completed in order to be expanded + info.kind.isInstanceOf[ObjDefKind] + || info.isComputed) + def expand(implicit ctx: Ctx, raise: Raise): SimpleType = { + ctx.tyDefs2.get(defn.name) match { + case S(info) => + if (!info.kind.isInstanceOf[ObjDefKind]) { + info.complete() + if (info.result.isEmpty) // * This can only happen if completion yielded an error + return errType + } + case N => + } + expandWith(paramTags = true, selfTy = true) + } + def expandOrCrash(implicit ctx: Ctx): SimpleType = { + require(canExpand) + expandWith(paramTags = true, selfTy = true) + } + def expandWith(paramTags: Bool, selfTy: Bool)(implicit ctx: Ctx): SimpleType = + ctx.tyDefs2.get(defn.name).map { info => + lazy val mkTparamRcd = RecordType(info.tparams.lazyZip(targs).map { + case ((tn, tv, vi), ta) => + val fldNme = defn.name + "#" + tn.name + // TODO also use computed variance info when available! + Var(fldNme).withLocOf(tn) -> FieldType.mk(vi.getOrElse(VarianceInfo.in), ta, ta)(provTODO) + })(provTODO) + info.result match { + case S(td: TypedNuAls) => + assert(td.tparams.size === targs.size) + subst(td.body, td.tparams.lazyZip(targs).map { + case (tp, ta) => SkolemTag(tp._2)(noProv) -> ta + }.toMap) + case S(td: TypedNuTrt) => + assert(td.tparams.size === targs.size) + // println(s"EXP ${td.sign}") + val (freshenMap, _) = refreshHelper2(td, Var(td.name).withLoc(prov.loco), S(targs)) // infer ty args if not provided + val freshSelf = if (!selfTy) TopType else { + implicit val freshened: MutMap[TV, ST] = freshenMap + implicit val shadows: Shadows = Shadows.empty + td.sign.freshenAbove(td.level, rigidify = false) + } + // println(s"Fresh $freshSelf") + freshSelf & + trtNameToNomTag(td.decl)(provTODO, ctx) & + mkTparamRcd + case S(td: TypedNuCls) => + assert(td.tparams.size === targs.size) + val (freshenMap, _) = refreshHelper2(td, Var(td.name).withLoc(prov.loco), S(targs)) // infer ty args if not provided + val freshSelf = if (!selfTy) TopType else { + implicit val freshened: MutMap[TV, ST] = freshenMap + implicit val shadows: Shadows = Shadows.empty + td.sign.freshenAbove(td.level, rigidify = false) + } + clsNameToNomTag(td.decl)(provTODO, ctx) & + freshSelf & + mkTparamRcd + case _ => // * Case for when the type has not been completed yet + info.decl match { + case td: NuTypeDef if td.kind.isInstanceOf[ClsLikeKind] => + // TODO in the future, add the self signature to DelayedTypeInfo and use it here + assert(td.tparams.size === targs.size) + clsNameToNomTag(td)(provTODO, ctx) & + mkTparamRcd + case td: NuTypeDef if td.kind is Trt => + assert(td.tparams.size === targs.size) + trtNameToNomTag(td)(provTODO, ctx) & + mkTparamRcd + case td: NuTypeDef if td.kind is Als => + // * Definition was not forced yet, which indicates an error (hopefully) + lastWords("cannot expand unforced type alias") + case d => + // * Other kinds of type defs are not allowed to be used as types + // * (an error should have been reported earlier) + errType + } + } + }.getOrElse { + val td = ctx.tyDefs(defn.name) + require(targs.size === td.tparamsargs.size) + lazy val tparamTags = + if (paramTags) RecordType.mk(td.tparamsargs.map { case (tp, tv) => + val tvv = td.getVariancesOrDefault + tparamField(defn, tp) -> FieldType( + Some(if (tvv(tv).isCovariant) BotType else tv), + if (tvv(tv).isContravariant) TopType else tv)(prov) + })(noProv) + else TopType + subst(td.kind match { + case Als => td.bodyTy + case Mod => throw new NotImplementedError("Namespaces are not supported yet.") + case Cls => clsNameToNomTag(td)(prov, ctx) & td.bodyTy & tparamTags + case Trt => trtNameToNomTag(td)(prov, ctx) & td.bodyTy & tparamTags + case Mxn => lastWords("mixins cannot be used as types") + }, td.targs.lazyZip(targs).toMap) //.withProv(prov) + } //tap { res => println(s"Expand $this => $res") } + private var tag: Opt[Opt[ClassTag]] = N + def expansionFallback(implicit ctx: Ctx): Opt[ST] = mkClsTag + /** Note: self types can be inherited from parents if the definition is abstract + * (if it is not abstract, then the class is already known to subtype the inherited self-type) */ + def mayHaveTransitiveSelfType(implicit ctx: Ctx): Bool = ctx.tyDefs2.get(defn.name) match { + case S(lti) => lti.decl match { + case td: NuTypeDef if !td.isAbstract => td.sig.nonEmpty + case _ => true + } + case _ => true + } + def mkClsTag(implicit ctx: Ctx): Opt[ClassTag] = tag.getOrElse { + val res = ctx.tyDefs.get(defn.name) match { + case S(td: TypeDef) if (td.kind is Cls) || (td.kind is Mod) => + S(clsNameToNomTag(td)(noProv, ctx)) + case S(td: TypeDef) if td.kind is Trt => + N + case _ => ctx.tyDefs2.get(defn.name) match { + case S(lti) => lti.decl match { + case td: NuTypeDef if (td.kind is Cls) || (td.kind is Mod) => + S(clsNameToNomTag(td)(noProv, ctx)) + case _ => N + } + case _ => N + } + } + tag = S(res) + res + } + def mapTargs[R](pol: Opt[Bool])(f: (Opt[Bool], ST) => R)(implicit ctx: Ctx): Ls[R] = { + // TODO factor w/ below + val (tvarVariances, tparamsargs) = ctx.tyDefs.get(defn.name) match { + case S(td) => + (td.tvarVariances, td.tparamsargs) + case N => + val td = ctx.tyDefs2(defn.name) + (N, td.tparams.map(tp => (tp._1, tp._2))) + } + tvarVariances.fold(targs.map(f(N, _))) { tvv => + assert(tparamsargs.sizeCompare(targs) === 0) + (tparamsargs lazyZip targs).map { case ((_, tv), ta) => + tvv(tv) match { + case VarianceInfo(true, true) => + f(N, TypeBounds(BotType, TopType)(noProv)) + case VarianceInfo(co, contra) => + f(if (co) pol else if (contra) pol.map(!_) else N, ta) + } + }} + } + // TODO dedup w/ above + def mapTargs[R](pol: PolMap)(f: (PolMap, ST) => R)(implicit ctx: Ctx): Ls[R] = { + val (tvarVariances, tparamsargs) = ctx.tyDefs.get(defn.name) match { + case S(td) => + (td.tvarVariances, td.tparamsargs) + case N => + val td = ctx.tyDefs2.getOrElse(defn.name, + // * This should only happen in the presence of ill-formed type definitions; + // * TODO: Make sure to report this and raise a compiler internal error if the source + // * does not actually have a type error! Otherwise we could silently get wrong results... + return Nil + ) + // TODO use computed varces + (some(td.explicitVariances), td.tparams.map(tp => (tp._1, tp._2))) + } + tvarVariances.fold(targs.map(f(pol.invar, _))) { tvv => + assert(tparamsargs.sizeCompare(targs) === 0) + (tparamsargs lazyZip targs).map { case ((_, tv), ta) => + tvv(tv) match { + case VarianceInfo(true, true) => + f(pol.invar, TypeBounds(BotType, TopType)(noProv)) + case VarianceInfo(co, contra) => + f(if (co) pol else if (contra) pol.contravar else pol.invar, ta) + } + }} + } } @@ -955,11 +1293,47 @@ abstract class TyperHelpers { Typer: Typer => } class Traverser2(implicit ctx: Ctx) { + def applyLike(pol: PolMap)(ty: TypeLike): Unit = ty match { + case ty: ST => apply(pol)(ty) + case OtherTypeLike(tu) => + tu.implementedMembers.foreach(applyMem(pol)) + tu.result.foreach(apply(pol)) + } + def applyMem(pol: PolMap)(m: NuMember): Unit = m match { + case TypedNuAls(level, td, tparams, body) => + tparams.iterator.foreach(tp => apply(pol.invar)(tp._2)) + apply(pol)(body) + case TypedNuCls(level, td, tparams, params, acParams, members, thisTy, sign, _, ptps) => + tparams.iterator.foreach(tp => apply(pol.invar)(tp._2)) + params.foreach(_.foreach(p => applyField(pol)(p._2))) + acParams.foreach(_.foreach(p => apply(pol.contravar)(p._2))) + members.valuesIterator.foreach(applyMem(pol)) + apply(pol.contravar)(thisTy) + apply(pol.contravar)(sign) + ptps.valuesIterator.foreach(applyMem(pol)) + case TypedNuTrt(level, td, tparams, members, thisTy, sign, _, ptps) => + tparams.iterator.foreach(tp => apply(pol.invar)(tp._2)) + members.valuesIterator.foreach(applyMem(pol)) + apply(pol.contravar)(thisTy) + apply(pol.covar)(sign) + ptps.valuesIterator.foreach(applyMem(pol)) + case TypedNuMxn(level, td, thisTy, superTy, tparams, params, members) => + tparams.iterator.foreach(tp => apply(pol.invar)(tp._2)) + params.foreach(p => applyField(pol)(p._2)) + members.valuesIterator.foreach(applyMem(pol)) + apply(pol.contravar)(thisTy) + apply(pol.contravar)(superTy) + case NuParam(nme, ty, pub) => applyField(pol)(ty) + case TypedNuFun(level, fd, ty) => apply(pol)(ty) + case TypedNuDummy(d) => () + // case NuTypeParam(nme, ty) => applyField(pol)(ty) + } def apply(pol: PolMap)(st: ST): Unit = st match { case tv @ AssignedVariable(ty) => apply(pol)(ty) case tv: TypeVariable => - if (pol(tv) =/= S(false)) tv.lowerBounds.foreach(apply(pol.at(tv.level, true))) - if (pol(tv) =/= S(true)) tv.upperBounds.foreach(apply(pol.at(tv.level, false))) + 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))) 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) @@ -970,8 +1344,9 @@ abstract class TyperHelpers { Typer: Typer => case NegType(n) => apply(pol.contravar)(n) case ExtrType(_) => () case ProxyType(und) => apply(pol)(und) + // case _: TypeTag => () + case _: ObjectTag | _: Extruded => () case SkolemTag(id) => apply(pol)(id) - case _: TypeTag => () case tr: TypeRef => tr.mapTargs(pol)(apply(_)(_)); () case Without(b, ns) => apply(pol)(b) case TypeBounds(lb, ub) => pol.traverseRange(lb, ub)(apply(_)(_)) @@ -1025,7 +1400,7 @@ abstract class TyperHelpers { Typer: Typer => object AliasOf { def unapply(ty: ST)(implicit ctx: Ctx): S[ST] = { def go(ty: ST, traversedVars: Set[TV]): S[ST] = ty match { - case tr: TypeRef => go(tr.expand, traversedVars) + case tr: TypeRef if tr.canExpand => go(tr.expandOrCrash, traversedVars) case proxy: ProxyType => go(proxy.underlying, traversedVars) case tv @ AssignedVariable(ty) if !traversedVars.contains(tv) => go(ty, traversedVars + tv) diff --git a/shared/src/main/scala/mlscript/codegen/Codegen.scala b/shared/src/main/scala/mlscript/codegen/Codegen.scala index 36397a7f93..f398407c6d 100644 --- a/shared/src/main/scala/mlscript/codegen/Codegen.scala +++ b/shared/src/main/scala/mlscript/codegen/Codegen.scala @@ -374,7 +374,7 @@ object JSExpr { def arguments(exprs: Ls[JSExpr]): SourceCode = exprs.zipWithIndex .foldLeft(SourceCode.empty) { case (x, (y, i)) => - x ++ y.toSourceCode ++ (if (i === exprs.length - 1) SourceCode.empty + x ++ y.embed(parentPrecedence = JSCommaExpr.outerPrecedence) ++ (if (i === exprs.length - 1) SourceCode.empty else SourceCode(", ")) } .parenthesized @@ -411,7 +411,7 @@ final case class JSArrowFn(params: Ls[JSPattern], body: JSExpr \/ Ls[JSStmt]) ex case pattern => pattern.toSourceCode }) ++ (if (i === params.length - 1) SourceCode.empty else SourceCode(", ")) } - .parenthesized ++ SourceCode(" => ") ++ (body match { + .parenthesized ++ SourceCode.fatArrow ++ (body match { // TODO: Figure out how `=>` competes with other operators. case L(expr: JSRecord) => expr.toSourceCode.parenthesized case L(expr) => expr.embed @@ -444,7 +444,7 @@ final case class JSImmEvalFn( case None => (SourceCode(s"${JSExpr.params(params)} => ") ++ (body match { case Left(expr: JSRecord) => expr.toSourceCode.parenthesized - case Left(expr) => expr.toSourceCode + case Left(expr) => expr.embed(parentPrecedence = 2) case Right(stmts) => stmts.foldLeft(SourceCode.empty) { _ + _.toSourceCode }.block })).parenthesized ++ JSExpr.arguments(arguments) @@ -534,7 +534,8 @@ object JSBinary { "&&" -> 5, // infixl 4 "||" -> 4, - "??" -> 4 + "??" -> 4, + "," -> 1, ) val operators: Set[Str] = opPrecMap.keySet @@ -598,16 +599,18 @@ object JSMember { def apply(`object`: JSExpr, property: JSExpr): JSMember = new JSMember(`object`, property) } -class JSField(`object`: JSExpr, property: JSIdent) extends JSMember(`object`, property) { +class JSField(`object`: JSExpr, val property: JSIdent) extends JSMember(`object`, property) { override def toSourceCode: SourceCode = `object`.toSourceCode.parenthesized( `object`.precedence < precedence || `object`.isInstanceOf[JSRecord] ) ++ SourceCode( - if (JSField.isValidIdentifier(property.name)) { + if (JSField.isValidFieldName(property.name)) { s".${property.name}" - } else { - s"[${JSLit.makeStringLiteral(property.name)}]" - } + } else + property.name.toIntOption match { + case S(index) => s"[$index]" + case N => s"[${JSLit.makeStringLiteral(property.name)}]" + } ) } @@ -618,6 +621,10 @@ object JSField { def isValidIdentifier(s: Str): Bool = identifierPattern.matches(s) && !Symbol.isKeyword(s) + // in this case, a keyword can be used as a field name + // e.g. `something.class` is valid + def isValidFieldName(s: Str): Bool = identifierPattern.matches(s) + def emitValidFieldName(s: Str): Str = if (isValidIdentifier(s)) s else JSLit.makeStringLiteral(s) } @@ -629,13 +636,18 @@ final case class JSArray(items: Ls[JSExpr]) extends JSExpr { SourceCode.array(items map { _.embed(JSCommaExpr.outerPrecedence) }) } -final case class JSRecord(entries: Ls[Str -> JSExpr]) extends JSExpr { +final case class JSRecord(entries: Ls[Str -> JSExpr], methods: Ls[JSStmt] = Nil) extends JSExpr { override def precedence: Int = 22 // Make override def toSourceCode: SourceCode = SourceCode - .record(entries map { case (key, value) => + .record((entries map { case (key, value) => SourceCode(JSField.emitValidFieldName(key) + ": ") ++ value.embed(JSCommaExpr.outerPrecedence) - }) + }) ++ (methods.map((m) => m.toSourceCode))) +} + +final case class JSClassExpr(cls: JSClassNewDecl) extends JSExpr { + implicit def precedence: Int = 22 + def toSourceCode: SourceCode = cls.toSourceCode } abstract class JSStmt extends JSCode @@ -821,10 +833,105 @@ final case class JSClassDecl( private val fieldsSet = collection.immutable.HashSet.from(fields) } +final case class JSClassNewDecl( + name: Str, + fields: Ls[Str], + accessors: Ls[Str], + privateMems: Ls[Str], + `extends`: Opt[JSExpr], + superFields: Ls[JSExpr], + ctorParams: Ls[Str], + rest: Opt[Str], + methods: Ls[JSClassMemberDecl], + implements: Ls[Str], + initStmts: Ls[JSStmt], + nestedTypes: Ls[Str], + ctorOverridden: Bool, + staticMethods: Ls[JSClassMemberDecl] +) extends JSStmt { + def toSourceCode: SourceCode = { + val constructor: SourceCode = { + val buffer = new ListBuffer[Str]() + val params = + ctorParams.iterator.zipWithIndex.foldRight(rest match { + case Some(rest) => s"...$rest" + case _ => "" + })((p, s) => + if (s.isEmpty) s"${p._1}" + else s"${p._1}, $s") + nestedTypes.foreach(t => buffer += s" #$t;") + privateMems.distinct.foreach(f => { + buffer += s" #${f};" + }) + accessors.distinct.foreach(f => { + if (!privateMems.contains(f)) buffer += s" #${f};" + buffer += s" get ${f}() { return this.#${f}; }" + }) + buffer += s" constructor($params) {" + if (`extends`.isDefined) { + val sf = superFields.iterator.zipWithIndex.foldLeft("")((res, p) => + if (p._2 === superFields.length - 1) s"$res${p._1.toSourceCode}" + else s"$res${p._1.toSourceCode}, " + ) + buffer += s" super($sf);" + } + implements.foreach { name => + buffer += s" $name.implement(this);" + } + + // if the default constructor is overridden, we generate the overridden version + // otherwise, generate based on the class fields + if (!ctorOverridden) { + assert(fields.length === ctorParams.length, s"fields and ctorParams have different size in class $name.") + fields.lazyZip(ctorParams).foreach { (field, param) => + buffer += s" this.#$field = $param;" // TODO: invalid name? + } + } + + initStmts.foreach { s => + s.toSourceCode.indented.indented.toString.split("\n").foreach { + line => buffer += line + } + } + buffer += " }" + SourceCode(buffer.toList) + } + val methodsSourceCode = + methods.foldLeft(SourceCode.empty) { case (x, y) => + x + y.toSourceCode.indented + } + val staticMethodsSourceCode = + staticMethods.foldLeft(SourceCode.empty) { case (x, y) => + x + SourceCode("static") + y.toSourceCode.indented + } + val epilogue = SourceCode("}") + `extends` match { + case Some(base) => + SourceCode(s"class $name extends ") ++ base.toSourceCode ++ + SourceCode(" {") + constructor + methodsSourceCode + staticMethodsSourceCode + epilogue + case None => + if (fields.isEmpty && methods.isEmpty && implements.isEmpty && accessors.isEmpty && initStmts.isEmpty && staticMethods.isEmpty) { + SourceCode(s"class $name {}") + } else { + SourceCode( + s"class $name {" :: Nil + ) + constructor + methodsSourceCode + staticMethodsSourceCode + epilogue + } + } + } + + private val fieldsSet = collection.immutable.HashSet.from(fields) +} + final case class JSComment(text: Str) extends JSStmt { def toSourceCode: SourceCode = SourceCode(s"// $text") } +final case class JSParenthesis(exp: JSExpr) extends JSExpr { + implicit def precedence: Int = 0 + def toSourceCode: SourceCode = exp.embed +} + object JSCodeHelpers { def id(name: Str): JSIdent = JSIdent(name) def lit(value: Int): JSLit = JSLit(value.toString()) 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 9a6328b31f..0000000000 --- a/shared/src/main/scala/mlscript/codegen/Helpers.scala +++ /dev/null @@ -1,96 +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"$name: ${inspect(value)}" - case (N, Fld(_, _, value)) => s"_: ${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}, ${body})" - 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 AdtMatchWith(cond, arms) => - s"match ${inspect(cond)} with ${arms.map(patmat => s"${inspect(patmat.pat)} -> ${inspect(patmat.rhs)}").mkString(" | ")}" - } - - 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, targs, L(term)) => - s"NuFunDef(${lt}, ${nme.name}, ${targs.mkString("[", ", ", "]")}, ${inspect(term)})" - case NuFunDef(lt, nme, targs, R(ty)) => - s"NuFunDef(${lt}, ${nme.name}, ${targs.mkString("[", ", ", "]")}, $ty)" - case NuTypeDef(kind, nme, tparams, params, parents, body) => - s"NuTypeDef(${kind.str}, ${nme.name}, ${tparams.mkString("(", ", ", ")")}, ${ - inspect(params)}, ${parents.map(inspect).mkString("(", ", ", ")")}, ${inspect(body)})" - case others => others.toString() - } - .mkString("TypingUnit(", ", ", ")") -} diff --git a/shared/src/main/scala/mlscript/codegen/Polyfill.scala b/shared/src/main/scala/mlscript/codegen/Polyfill.scala index 08e162e550..9a01564ddc 100644 --- a/shared/src/main/scala/mlscript/codegen/Polyfill.scala +++ b/shared/src/main/scala/mlscript/codegen/Polyfill.scala @@ -151,9 +151,12 @@ object Polyfill { ) buffer += BuiltinFunc( "error", fn(_) { - `throw`(JSNew(JSIdent("Error"))(JSExpr("unexpected runtime error"))) + `throw`(JSNew(JSIdent("Error"))(JSExpr("an error was thrown"))) } ) + buffer += BuiltinFunc( + "length", fn(_, param("x")) { `return` { id("x").member("length") } } + ) buffer += BuiltinFunc("concat", makeBinaryFunc("+")) buffer += BuiltinFunc("add", makeBinaryFunc("+")) buffer += BuiltinFunc("sub", makeBinaryFunc("-")) @@ -163,10 +166,19 @@ object Polyfill { buffer += BuiltinFunc("not", makeUnaryFunc("!")) buffer += BuiltinFunc("negate", makeUnaryFunc("-")) buffer += BuiltinFunc("eq", makeBinaryFunc("===")) + buffer += BuiltinFunc("ne", makeBinaryFunc("!==")) + buffer += BuiltinFunc("sgt", makeBinaryFunc(">")) + buffer += BuiltinFunc("slt", makeBinaryFunc("<")) + buffer += BuiltinFunc("sge", makeBinaryFunc(">=")) + buffer += BuiltinFunc("sle", makeBinaryFunc("<=")) + buffer += BuiltinFunc("eq", makeBinaryFunc("===")) buffer += BuiltinFunc("unit", makeUnaryFunc("undefined")) buffer += BuiltinFunc( "log", fn(_, param("x")) { `return` { id("console.info")(id("x")) } } ) + buffer += BuiltinFunc( + "discard", fn(_, param("x"))() + ) buffer.toList } diff --git a/shared/src/main/scala/mlscript/codegen/Scope.scala b/shared/src/main/scala/mlscript/codegen/Scope.scala index cff07ca99d..68c442ca87 100644 --- a/shared/src/main/scala/mlscript/codegen/Scope.scala +++ b/shared/src/main/scala/mlscript/codegen/Scope.scala @@ -4,17 +4,24 @@ import mlscript.utils.shorthands._ import mlscript.{JSStmt, JSExpr, JSLetDecl} import mlscript.Type import scala.reflect.ClassTag -import mlscript.{TypeName, Top, Bot, TypeDef, Als, Trt, Cls, Nms} -import mlscript.MethodDef -import mlscript.Term +import mlscript.{TypeName, Top, Bot, TypeDef, Als, Trt, Cls, Mod, Mxn} +import mlscript.{MethodDef, Var} +import mlscript.{Term, Statement, Record} import mlscript.utils.{AnyOps, lastWords} import mlscript.JSField +import mlscript.{NuTypeDef, NuFunDef} -class Scope(name: Str, enclosing: Opt[Scope]) { +class Scope(val name: Str, enclosing: Opt[Scope]) { private val lexicalTypeSymbols = scala.collection.mutable.HashMap[Str, TypeSymbol]() private val lexicalValueSymbols = scala.collection.mutable.HashMap[Str, RuntimeSymbol]() private val runtimeSymbols = scala.collection.mutable.HashSet[Str]() + // To allow a class method/getter/constructor to access members of an outer class, + // we insert `const qualifier = this;` before the class definition starts. + // To access ALL qualifier variables correctly, we need to make sure + // none of them would be shadowed. + private val qualifierSymbols = scala.collection.mutable.HashMap[Str, ValueSymbol]() + val tempVars: TemporaryVariableEmitter = TemporaryVariableEmitter() /** @@ -27,10 +34,12 @@ class Scope(name: Str, enclosing: Opt[Scope]) { Ls( "true", "false", + "NaN", "id", "emptyArray", "succ", "error", + "length", "concat", "add", "sub", @@ -38,11 +47,19 @@ class Scope(name: Str, enclosing: Opt[Scope]) { "div", "gt", "not", + "ne", + "eq", + "sgt", + "slt", + "sge", + "sle", + "typeof", "toString", "negate", "eq", "unit", "log", + "discard", ) foreach { name => register(BuiltinSymbol(name, name)) } @@ -59,11 +76,20 @@ class Scope(name: Str, enclosing: Opt[Scope]) { i <- (1 to Int.MaxValue).iterator c <- Scope.nameAlphabet.combinations(i) name = c.mkString - if !runtimeSymbols.contains(name) + if !hasRuntimeName(name) } yield { name } + /** + * Check if a runtime name is used recursively. + * + * @param name the name + * @return whether it's available or not + */ + private def hasRuntimeName(name: Str): Bool = + runtimeSymbols.contains(name) || enclosing.exists(_.hasRuntimeName(name)) + /** * Allocate a non-sense runtime name. */ @@ -107,7 +133,7 @@ class Scope(name: Str, enclosing: Opt[Scope]) { /** * Register a lexical symbol in both runtime name set and lexical name set. */ - private def register(symbol: TypeSymbol with RuntimeSymbol): Unit = { + def register(symbol: TypeSymbol with RuntimeSymbol): Unit = { lexicalTypeSymbols.put(symbol.lexicalName, symbol) lexicalValueSymbols.put(symbol.lexicalName, symbol) runtimeSymbols += symbol.runtimeName @@ -117,7 +143,7 @@ class Scope(name: Str, enclosing: Opt[Scope]) { /** * Register a lexical symbol in both runtime name set and lexical name set. */ - private def register(symbol: RuntimeSymbol): Unit = { + def register(symbol: RuntimeSymbol): Unit = { lexicalValueSymbols.put(symbol.lexicalName, symbol) runtimeSymbols += symbol.runtimeName () @@ -171,6 +197,8 @@ class Scope(name: Str, enclosing: Opt[Scope]) { case S(sym: TraitSymbol) => N case S(sym: TypeAliasSymbol) => throw new CodeGenError(s"cannot inherit from type alias $name" ) + case S(_: NuTypeSymbol) => + throw new CodeGenError(s"NuType symbol $name is not supported when resolving base classes") case N => throw new CodeGenError(s"undeclared type name $name when resolving base classes") } @@ -191,6 +219,8 @@ class Scope(name: Str, enclosing: Opt[Scope]) { case S(sym: TraitSymbol) => S(sym) case S(sym: TypeAliasSymbol) => throw new CodeGenError(s"cannot inherit from type alias $name" ) + case S(sym: NuTypeSymbol) => + throw new CodeGenError(s"NuType symbol $name is not supported when resolving implemented traits") case N => throw new CodeGenError(s"undeclared type name $name when resolving implemented traits") } @@ -204,8 +234,10 @@ class Scope(name: Str, enclosing: Opt[Scope]) { declareTrait(name, tparams map { _.name }, body, mthdDefs) case TypeDef(Cls, TypeName(name), tparams, baseType, _, members, _, _) => declareClass(name, tparams map { _.name }, baseType, members) - case TypeDef(Nms, _, _, _, _, _, _, _) => - throw CodeGenError("Namespaces are not supported yet.") + case TypeDef(Mxn, _, _, _, _, _, _, _) => + throw CodeGenError("Mixins are not supported yet.") + case TypeDef(Mod, _, _, _, _, _, _, _) => + throw CodeGenError("Modules are not supported yet.") } def declareClass( @@ -220,6 +252,42 @@ class Scope(name: Str, enclosing: Opt[Scope]) { symbol } + // in DiffTests, we need to rename `TypingUnit` to some other names + // because we would not indicate different names manually + def declareTopModule( + lexicalName: Str, + stmts: Ls[Statement], + nuTypes: Ls[NuTypeDef], + allowRenaming: Bool + ): ModuleSymbol = { + val finalName = + if (allowRenaming) allocateRuntimeName(lexicalName) else lexicalName + val (mths, rest) = stmts.partitionMap { + case NuFunDef(isLetRec, Var(nme), _, tys, Left(rhs)) if (isLetRec.isEmpty || isLetRec.getOrElse(false)) => + Left(MethodDef[Left[Term, Type]](isLetRec.getOrElse(false), TypeName(finalName), Var(nme), tys, Left(rhs))) + case s => Right(s) + } + val (signatures, ctor) = rest.partitionMap { + case NuFunDef(isLetRec, Var(nme), _, tys, Right(rhs)) if (isLetRec.isEmpty || isLetRec.getOrElse(false)) => + Left(MethodDef[Right[Term, Type]](isLetRec.getOrElse(false), TypeName(finalName), Var(nme), tys, Right(rhs))) + case s => Right(s) + } + val symbol = ModuleSymbol(finalName, Nil, Record(Nil), mths, signatures, ctor, Nil, nuTypes, N) + register(symbol) + symbol + } + + // We don't want `qualifier` symbols to be shadowed by each other + // Add all runtime names of `qualifier` symbols from the parent scope + private def pullOuterSymbols(syms: scala.collection.mutable.HashMap[Str, ValueSymbol]) = { + syms.foreach { s => + runtimeSymbols += s._1 + qualifierSymbols += s + } + + this + } + def declareTrait( lexicalName: Str, params: Ls[Str], @@ -245,46 +313,74 @@ class Scope(name: Str, enclosing: Opt[Scope]) { symbol } - def declareValue(lexicalName: Str, isByvalueRec: Option[Boolean], isLam: Boolean): 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, forNewDefsDryRun)) + } symbol } - def declareStubValue(lexicalName: Str)(implicit accessible: Bool): StubValueSymbol = - declareStubValue(lexicalName, N) + def declareQualifierSymbol(lexicalName: Str): Str = { + val symbol = ValueSymbol("this", allocateRuntimeName(lexicalName), S(false), false) + qualifierSymbols += (symbol.runtimeName -> symbol) + register(symbol) + symbol.runtimeName + } + def resolveQualifier(runtimeName: Str): ValueSymbol = + qualifierSymbols.getOrElse(runtimeName, throw CodeGenError(s"qualifier $runtimeName not found")) + + def declareStubValue(lexicalName: Str, symbolicName: Opt[Str])(implicit allowEscape: Bool): StubValueSymbol = + declareStubValue(lexicalName, N, symbolicName) - def declareStubValue(lexicalName: Str, previous: StubValueSymbol)(implicit - accessible: Bool + def declareStubValue(lexicalName: Str, previous: StubValueSymbol, symbolicName: Opt[Str])(implicit + allowEscape: Bool ): StubValueSymbol = - declareStubValue(lexicalName, S(previous)) + declareStubValue(lexicalName, S(previous), symbolicName) - private def declareStubValue(lexicalName: Str, previous: Opt[StubValueSymbol])(implicit - accessible: Bool + private def declareStubValue(lexicalName: Str, previous: Opt[StubValueSymbol], symbolicName: Opt[Str])(implicit + 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 => + register(StubValueSymbol(symbolicName, symbol.runtimeName, false, previous)) + } symbol } def stubize(sym: ValueSymbol, previous: StubValueSymbol)(implicit - accessible: Bool + allowEscape: Bool ): StubValueSymbol = { unregister(sym) - declareStubValue(sym.lexicalName, S(previous)) + declareStubValue(sym.lexicalName, S(previous), N) } def declareRuntimeSymbol(): Str = { @@ -321,7 +417,8 @@ class Scope(name: Str, enclosing: Opt[Scope]) { /** * Shorthands for deriving normal scopes. */ - def derive(name: Str): Scope = new Scope(name, S(this)) + def derive(name: Str): Scope = + (new Scope(name, S(this))).pullOuterSymbols(qualifierSymbols) def refreshRes(): Unit = { diff --git a/shared/src/main/scala/mlscript/codegen/Symbol.scala b/shared/src/main/scala/mlscript/codegen/Symbol.scala index 316461ece5..adc8d1bac9 100644 --- a/shared/src/main/scala/mlscript/codegen/Symbol.scala +++ b/shared/src/main/scala/mlscript/codegen/Symbol.scala @@ -4,8 +4,9 @@ import mlscript.utils.shorthands._ import mlscript.Type import mlscript.JSClassDecl import mlscript.MethodDef -import mlscript.Term +import mlscript.{Term, Statement} import mlscript.TypeName +import mlscript.NuTypeDef sealed trait LexicalSymbol { @@ -19,6 +20,7 @@ sealed trait LexicalSymbol { sealed trait RuntimeSymbol extends LexicalSymbol { def runtimeName: Str + var visited: Bool = false } sealed trait TypeSymbol extends LexicalSymbol { @@ -26,13 +28,52 @@ sealed trait TypeSymbol extends LexicalSymbol { val body: Type } -sealed class ValueSymbol(val lexicalName: Str, val runtimeName: Str, val isByvalueRec: Option[Boolean], val isLam: Boolean) extends RuntimeSymbol { +sealed trait NuTypeSymbol { sym: TypeSymbol => + val name: Str + val methods: Ls[MethodDef[Left[Term, Type]]] // implemented methods + val signatures: Ls[MethodDef[Right[Term, Type]]] // methods signatures + val ctor: Ls[Statement] // statements in the constructor + val nested: Ls[NuTypeDef] // nested class/mixin/module + val qualifier: Opt[Str] // if it is inside another NuTypeSymbol, it indicates the runtime alias of parent's `this` + val superParameters: Ls[Term] // parameters that need to be passed to the `super()` + val isPlainJSClass: Bool // is this a plain class in JS + val ctorParams: Opt[Ls[(Str, Bool)]] // parameters in the constructor + val publicCtors: Ls[Str] // public(i.e., val-) parameters in the ctor + val matchingFields: Ls[Str] = sym.body.collectFields // matchable fields(i.e., fields in `class ClassName(...)`) + val unapplyMtd: Opt[MethodDef[Left[Term, Type]]] // unapply method + + 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, + /** + * Workaround for the first pass traversal with new definition typing. + * "Dry run" here means that we haven't generated the code for the symbol + * yet in the new-definition-typing mode, so the symbol is just defined + * for the sake of code generation of classes/mixins/modules. + * + * This field should be deprecated after the `PreTyper` is done. See [PR + * #197](https://github.com/hkust-taco/mlscript/pull/197) for more details. + */ + 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( @@ -85,6 +126,92 @@ final case class ClassSymbol( override def toString: Str = s"class $lexicalName ($runtimeName)" } +final case class NewClassMemberSymbol( + name: Str, + isByvalueRec: Option[Boolean], + isLam: Boolean, + isPrivate: Boolean, + qualifier: Option[Str] +) extends RuntimeSymbol { + override def toString: Str = s"new class member $name" + + // Class members should have fixed names determined by users + override def lexicalName: Str = name + override def runtimeName: Str = name +} + +final case class NewClassSymbol( + name: Str, + params: Ls[Str], + ctorParams: Opt[Ls[(Str, Bool)]], + body: Type, + methods: Ls[MethodDef[Left[Term, Type]]], + unapplyMtd: Opt[MethodDef[Left[Term, Type]]], + signatures: Ls[MethodDef[Right[Term, Type]]], + ctor: Ls[Statement], + superParameters: Ls[Term], + publicCtors: Ls[Str], + nested: Ls[NuTypeDef], + qualifier: Opt[Str], + isPlainJSClass: Bool +) extends TypeSymbol + with RuntimeSymbol with NuTypeSymbol { + override def toString: Str = s"new class $name" + + // Classes should have fixed names determined by users + override def lexicalName: Str = name + override def runtimeName: Str = name +} + +final case class MixinSymbol( + name: Str, + params: Ls[Str], + body: Type, + methods: Ls[MethodDef[Left[Term, Type]]], + signatures: Ls[MethodDef[Right[Term, Type]]], + ctor: Ls[Statement], + publicCtors: Ls[Str], + nested: Ls[NuTypeDef], + qualifier: Opt[Str] +) extends TypeSymbol + with RuntimeSymbol with NuTypeSymbol { + override def toString: Str = s"mixin $name" + + // Mixins should have fixed names determined by users + override def lexicalName: Str = name + override def runtimeName: Str = name + + // Mixins should pass `...rest` to the `super()` + // But the variable name is not sure when we create the symbol object + override val superParameters: Ls[Term] = Nil + val isPlainJSClass: Bool = true + val ctorParams: Opt[Ls[(Str, Bool)]] = N + val unapplyMtd: Opt[MethodDef[Left[Term, Type]]] = N +} + +final case class ModuleSymbol( + name: Str, + params: Ls[Str], + body: Type, + methods: Ls[MethodDef[Left[Term, Type]]], + signatures: Ls[MethodDef[Right[Term, Type]]], + ctor: Ls[Statement], + superParameters: Ls[Term], + nested: Ls[NuTypeDef], + qualifier: Opt[Str] +) extends TypeSymbol + with RuntimeSymbol with NuTypeSymbol { + override def toString: Str = s"module $name" + + // Modules should have fixed names determined by users + override def lexicalName: Str = name + override def runtimeName: Str = name + val isPlainJSClass: Bool = false + val ctorParams: Opt[Ls[(Str, Bool)]] = N + val publicCtors: Ls[Str] = Nil + val unapplyMtd: Opt[MethodDef[Left[Term, Type]]] = N +} + final case class TraitSymbol( lexicalName: Str, runtimeName: Str, diff --git a/shared/src/main/scala/mlscript/codegen/typescript/TsTypegen.scala b/shared/src/main/scala/mlscript/codegen/typescript/TsTypegen.scala index 5e269fe0ec..51a41cfa35 100644 --- a/shared/src/main/scala/mlscript/codegen/typescript/TsTypegen.scala +++ b/shared/src/main/scala/mlscript/codegen/typescript/TsTypegen.scala @@ -44,7 +44,7 @@ final class TsTypegenCodeBuilder { object TypegenContext { def apply(mlType: Type): TypegenContext = { - val existingTypeVars = ShowCtx.mk(mlType :: Nil, "").vs + val existingTypeVars = ShowCtx.mk(mlType :: Nil, false, "").vs val typegenTypeScope = typeScope.derive("localTypeScope") val typegenTermScope = termScope.derive("localTermScope") val typeVarMapping = existingTypeVars.to(MutSortedMap) @@ -81,6 +81,7 @@ final class TsTypegenCodeBuilder { case (classInfo: ClassSymbol) => addTypeGenClassDef(classInfo, methods) case (aliasInfo: TypeAliasSymbol) => addTypeGenTypeAlias(aliasInfo) case (traitInfo: TraitSymbol) => addTypegenTraitDef(traitInfo, methods) + case _ => ??? // TODO } } @@ -197,13 +198,13 @@ final class TsTypegenCodeBuilder { // add body fields classFieldsAndType - .map {case (fieldVar, fieldTypes) => + .map { case (fieldVar, fieldTypes) => val fieldTypesCode = toTypegenInheritedFields(fieldTypes) (SourceCode(fieldVar.name), fieldTypesCode) } - .foreach({ case (fieldVar, fieldType) => { + .foreachEntry { (fieldVar, fieldType) => { classDeclaration += SourceCode(" ") ++ fieldVar ++ SourceCode.colon ++ fieldType - }}) + }} // constructor needs all fields including super classes val allFieldsAndTypes = (classFieldsAndType ++ @@ -404,11 +405,11 @@ final class TsTypegenCodeBuilder { ) case Record(fields) => // ts can only handle fields that have only out type or the same in out types - if (fields.iterator - .map(field => field._2.in.map(in => in === field._2.out).getOrElse(true)) - .exists(!_)) - throw CodeGenError("Cannot convert mutable record field with different in out types to typescript") - + fields.iterator.foreach { field => + if (field._2.in.exists(in => in =/= field._2.out)) throw CodeGenError( + s"Cannot convert mutable record field with different in out types to typescript (${ + field})") + } SourceCode.recordWithEntries( fields.map(entry => if (entry._2.in.isDefined) @@ -418,11 +419,11 @@ final class TsTypegenCodeBuilder { )) case Tuple(fields) => // ts can only handle fields that have only out type or the same in out types - if (fields.iterator - .map(field => field._2.in.map(in => in === field._2.out).getOrElse(true)) - .exists(!_)) - throw CodeGenError("Cannot convert mutable tuple field with different in out types to typescript") - + fields.iterator.foreach { field => + if (field._2.in.exists(in => in =/= field._2.out)) throw CodeGenError( + s"Cannot convert mutable tuple field with different in out types to typescript (${ + field})") + } // tuple that is a function argument becomes // multi-parameter argument list // ! Note: No equivalent to readonly fields for tuples @@ -573,7 +574,7 @@ final class TsTypegenCodeBuilder { }.getOrElse(SourceCode(tvarName)) case Constrained(base, tvbs, where) => throw CodeGenError(s"Cannot generate type for `where` clause $tvbs $where") - case _: Splice | _: TypeTag | _: PolyType => + 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 c4a3740ff9..3e664b3e65 100644 --- a/shared/src/main/scala/mlscript/helpers.scala +++ b/shared/src/main/scala/mlscript/helpers.scala @@ -2,7 +2,6 @@ package mlscript import scala.util.chaining._ import scala.collection.mutable.{Map => MutMap, SortedMap => SortedMutMap, Set => MutSet, Buffer} -import scala.collection.immutable.SortedSet import math.Ordered.orderingToOrdered @@ -11,25 +10,15 @@ import mlscript.utils._, shorthands._ // Auxiliary definitions for types -abstract class TypeImpl extends Located { self: Type => - - lazy val typeVarsList: List[TypeVar] = this match { - case uv: TypeVar => uv :: Nil - case Recursive(n, b) => n :: b.typeVarsList - case _ => children.flatMap(_.typeVarsList) - } +trait SignatureImpl extends Located { self: Signature => + def children: List[Located] = members +} - /** - * @return - * set of free type variables in type - */ - lazy val freeTypeVariables: Set[TypeVar] = this match { - case Recursive(uv, body) => body.freeTypeVariables - uv - case t: TypeVar => Set.single(t) - case _ => this.children.foldRight(Set.empty[TypeVar])((ty, acc) => ty.freeTypeVariables ++ acc) - } +trait TypeLikeImpl extends Located { self: TypeLike => + + def showDbg2: Str = show(newDefs = true) // TODO more lightweight debug printing routine - def show: Str = showIn(ShowCtx.mk(this :: Nil), 0) + def show(newDefs: Bool): Str = showIn(ShowCtx.mk(this :: Nil, newDefs), 0) private def parensIf(str: Str, cnd: Boolean): Str = if (cnd) "(" + str + ")" else str private def showField(f: Field, ctx: ShowCtx): Str = f match { @@ -39,6 +28,8 @@ abstract class TypeImpl extends Located { self: Type => case Field(S(lb), Top) => s"in ${lb.showIn(ctx, 0)}" case Field(S(lb), ub) => s"in ${lb.showIn(ctx, 0)} out ${ub.showIn(ctx, 0)}" } + private def showFields(fs: Ls[Opt[Var] -> Field], ctx: ShowCtx): Ls[Str] = + fs.map(nt => s"${nt._2.mutStr}${nt._1.fold("")(_.name + ": ")}${showField(nt._2, ctx)}") def showIn(ctx: ShowCtx, outerPrec: Int): Str = this match { // TODO remove obsolete pretty-printing hacks case Top => "anything" @@ -49,10 +40,21 @@ abstract class TypeImpl extends Located { self: Type => case uv: TypeVar => ctx.vs(uv) case Recursive(n, b) => parensIf(s"${b.showIn(ctx, 2)} as ${ctx.vs(n)}", outerPrec > 1) case WithExtension(b, r) => parensIf(s"${b.showIn(ctx, 2)} with ${r.showIn(ctx, 0)}", outerPrec > 1) - case Function(Tuple((N,Field(N,l)) :: Nil), r) => Function(l, r).showIn(ctx, outerPrec) + case Function(Tuple(fs), r) => + val innerStr = fs match { + case Nil => "()" + case N -> Field(N, f) :: Nil if !f.isInstanceOf[Tuple] => f.showIn(ctx, 31) + case _ => + val inner = showFields(fs, ctx) + if (ctx.newDefs) inner.mkString("(", ", ", ")") else inner.mkString("(", ", ", ",)") + } + parensIf(innerStr + " -> " + r.showIn(ctx, 30), outerPrec > 30) + case Function(l, r) if ctx.newDefs => + parensIf("(..." + l.showIn(ctx, 0) + ") -> " + r.showIn(ctx, 30), outerPrec > 30) case Function(l, r) => parensIf(l.showIn(ctx, 31) + " -> " + r.showIn(ctx, 30), outerPrec > 30) case Neg(t) => s"~${t.showIn(ctx, 100)}" - case Record(fs) => fs.map { nt => + case Record(fs) => + val strs = fs.map { nt => val nme = nt._1.name if (nme.isCapitalized) nt._2 match { case Field(N | S(Bot), Top) => s"$nme" @@ -62,11 +64,18 @@ abstract class TypeImpl extends Located { self: Type => case Field(S(lb), ub) => s"$nme :> ${lb.showIn(ctx, 0)} <: ${ub.showIn(ctx, 0)}" } else s"${nt._2.mutStr}${nme}: ${showField(nt._2, ctx)}" - }.mkString("{", ", ", "}") + } + if (strs.foldLeft(0)(_ + _.length) > 80) + strs.mkString("{\n" + ctx.indStr, ",\n" + ctx.indStr, "") + .indentNewLines(ShowCtx.indentation) + "\n" + ctx.indStr + "}" + else strs.mkString("{", ", ", "}") case Splice(fs) => - fs.map{case L(l) => s"...${l.showIn(ctx, 0)}" case R(r) => s"${showField(r, ctx)}"}.mkString("(", ", ", ")") + val inner = fs.map{case L(l) => s"...${l.showIn(ctx, 0)}" case R(r) => s"${showField(r, ctx)}"} + if (ctx.newDefs) inner.mkString("[", ", ", "]") else inner.mkString("(", ", ", ")") case Tuple(fs) => - fs.map(nt => s"${nt._2.mutStr}${nt._1.fold("")(_.name + ": ")}${showField(nt._2, ctx)},").mkString("(", " ", ")") + val inner = showFields(fs, ctx) + if (ctx.newDefs) inner.mkString("[", ", ", "]") + else inner.mkString("(", ", ", if (fs.nonEmpty) ",)" else ")") case Union(TypeName("true"), TypeName("false")) | Union(TypeName("false"), TypeName("true")) => TypeName("bool").showIn(ctx, 0) // case Union(l, r) => parensIf(l.showIn(ctx, 20) + " | " + r.showIn(ctx, 20), outerPrec > 20) @@ -89,35 +98,98 @@ abstract class TypeImpl extends Located { self: Type => case Bounds(lb, Top) => s"in ${lb.showIn(ctx, 0)}" case Bounds(lb, ub) => s"in ${lb.showIn(ctx, 0)} out ${ub.showIn(ctx, 0)}" // - case AppliedType(n, args) => s"${n.name}[${args.map(_.showIn(ctx, 0)).mkString(", ")}]" - case Rem(b, ns) => s"${b.showIn(ctx, 90)}${ns.map("\\"+_).mkString}" + case AppliedType(n, args) => + s"${n.name}${args.map(_.showIn(ctx, 0)).mkString(ctx.<, ", ", ctx.>)}" + case Selection(b, n) => b.showIn(ctx, 100) + "." + n.name + case Rem(b, ns) => s"${b.showIn(ctx, 90)}${ns.map("\\"+_.name).mkString}" case Literal(IntLit(n)) => n.toString case Literal(DecLit(n)) => n.toString case Literal(StrLit(s)) => "\"" + s + "\"" - case Literal(UnitLit(b)) => if (b) "undefined" else "null" + case Literal(UnitLit(b)) => + if (b) if (ctx.newDefs) "()" else "undefined" else "null" case PolyType(Nil, body) => body.showIn(ctx, outerPrec) case PolyType(targs, body) => parensIf( s"${targs.iterator.map(_.fold(_.name, _.showIn(ctx, 0))) - .mkString("forall ", " ", ".")} ${body.showIn(ctx, 1)}", + .mkString("forall ", " ", ".")} ${body.showIn(ctx, 0)}", outerPrec > 1 // or 0? ) - case Constrained(b, bs, ws) => parensIf(s"${b.showIn(ctx, 0)}\n where${bs.map { - case (uv, Bounds(Bot, ub)) => - s"\n ${ctx.vs(uv)} <: ${ub.showIn(ctx, 0)}" - case (uv, Bounds(lb, Top)) => - s"\n ${ctx.vs(uv)} :> ${lb.showIn(ctx, 0)}" - case (uv, Bounds(lb, ub)) if lb === ub => - s"\n ${ctx.vs(uv)} := ${lb.showIn(ctx, 0)}" - case (uv, Bounds(lb, ub)) => - val vstr = ctx.vs(uv) - s"\n ${vstr } :> ${lb.showIn(ctx, 0)}" + - s"\n ${" " * vstr.length} <: ${ub.showIn(ctx, 0)}" - }.mkString}${ws.map{ - case Bounds(lo, hi) => s"\n ${lo.showIn(ctx, 0)} <: ${hi.showIn(ctx, 0)}" // TODO print differently from bs? - }.mkString}", outerPrec > 0) + case Constrained(b, bs, ws) => + val oldCtx = ctx + val bStr = b.showIn(ctx, 0).stripSuffix("\n") + val multiline = bStr.contains('\n') + parensIf({ + val ctx = if (multiline) oldCtx.indent else oldCtx.indent.indent + s"${ + bStr + }\n${oldCtx.indStr}${if (multiline) "" else " "}where${ + bs.map { + case (uv, Bounds(Bot, ub)) => + s"\n${ctx.indStr}${ctx.vs(uv)} <: ${ub.showIn(ctx, 0)}" + case (uv, Bounds(lb, Top)) => + s"\n${ctx.indStr}${ctx.vs(uv)} :> ${lb.showIn(ctx, 0)}" + case (uv, Bounds(lb, ub)) if lb === ub => + s"\n${ctx.indStr}${ctx.vs(uv)} := ${lb.showIn(ctx, 0)}" + case (uv, Bounds(lb, ub)) => + val vstr = ctx.vs(uv) + s"\n${ctx.indStr}${vstr } :> ${lb.showIn(ctx, 0)}" + + s"\n${ctx.indStr}${" " * vstr.length} <: ${ub.showIn(ctx, 0)}" + }.mkString + }${ws.map{ + case Bounds(lo, hi) => s"\n${ctx.indStr}${lo.showIn(ctx, 0)} <: ${hi.showIn(ctx, 0)}" // TODO print differently from bs? + }.mkString}" + }, outerPrec > 0) + case fd @ NuFunDef(isLetRec, nme, snme, targs, rhs) => + s"${isLetRec match { + case S(false) => if (fd.genField) "val" else "let" + case S(true) => if (fd.genField) die else "let rec" + case N => "fun" + }}${snme.fold("")(" (" + _.name + ")") + } ${nme.name}${targs.map(_.showIn(ctx, 0)).mkStringOr(", ", "[", "]")}${rhs match { + case L(trm) => " = ..." + case R(ty) => ": " + ty.showIn(ctx, 0) + }}" + case Signature(decls, res) => + (decls.map(ctx.indStr + _.showIn(ctx, 0) + "\n") ::: (res match { + case S(ty) => ctx.indStr + ty.showIn(ctx, 0) + "\n" :: Nil + case N => Nil + })).mkString + case NuTypeDef(kind @ Als, nme, tparams, params, ctor, sig, parents, sup, ths, body) => + assert(params.isEmpty, params) + assert(ctor.isEmpty, ctor) + assert(parents.isEmpty, parents) + assert(sup.isEmpty, sup) + assert(ths.isEmpty, ths) + assert(body.entities.isEmpty, body) + s"type ${nme.name}${tparams.map(_._2.showIn(ctx, 0)).mkStringOr(", ", "[", "]")} = ${ + sig.getOrElse(die).showIn(ctx, 0)}" + case td @ NuTypeDef(kind, nme, tparams, params, ctor, sig, parents, sup, ths, body) => + val bodyCtx = ctx.indent + s"${td.declareLoc.fold("")(_ => "declare ")}${td.abstractLoc.fold("")(_ => "abstract ")}${kind.str} ${ + nme.name}${tparams.map(_._2.showIn(ctx, 0)).mkStringOr(", ", "[", "]")}${params match { + case S(Tup(fields)) => s"(${fields.map { + case (N, Fld(_, Asc(v: Var, ty))) => v.name + ": " + ty.showIn(ctx, 0) + case (N | S(_), _) => lastWords("ill-formed type definition parameter") + }.mkString(", ")})" + case _ => "" + }}${sig.fold("")(": " + _.showIn(bodyCtx, 0))}${parents match { + case Nil => "" + case ps => " extends " + ps.iterator.map(_.print(false)).mkString(", ") // TODO pp parent terms... + }}${if (body.entities.isEmpty && sup.isEmpty && ths.isEmpty) "" else + " {\n" + sup.fold("")(s"${bodyCtx.indStr}super: " + _.showIn(bodyCtx, 0) + "\n") + + ths.fold("")(s"${bodyCtx.indStr}this: " + _.showIn(bodyCtx, 0) + "\n") + + body.entities.collect { + case Constructor(params, body) => s"${bodyCtx.indStr}constructor(${params.fields.map { + case N -> Fld(FldFlags.empty, Asc(Var(nme), ty)) => + s"${nme}: ${ty.showIn(bodyCtx, 0)}" + case _ => lastWords("ill-formed constructor parameter") + }.mkString(", ")})\n" + }.mkString + + Signature(body.entities.collect { case d: NuDecl => d }, N).showIn(bodyCtx, 0) + + ctx.indStr + "}" + }" } - def children: List[Type] = this match { + def childrenTypes: List[TypeLike] = this match { case _: NullaryType => Nil case Function(l, r) => l :: r :: Nil case Bounds(l, r) => l :: r :: Nil @@ -128,13 +200,45 @@ abstract class TypeImpl extends Located { self: Type => case Inter(l, r) => l :: r :: Nil case Recursive(n, b) => b :: Nil case AppliedType(n, ts) => ts + case Selection(b, nme) => b :: nme :: Nil case Rem(b, _) => b :: Nil 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 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) => + // TODO improve this mess + tparams.map(_._2) ::: params.getOrElse(Tup(Nil)).fields.collect { + case (_, Fld(_, Asc(_, ty))) => ty + } ::: sig.toList ::: sup.toList ::: ths.toList ::: Signature(body.entities.collect { + case d: NuDecl => d + }, N) :: Nil // TODO parents? + } + + lazy val typeVarsList: List[TypeVar] = this match { + case uv: TypeVar => uv :: Nil + case Recursive(n, b) => n :: b.typeVarsList + case _ => childrenTypes.flatMap(_.typeVarsList) + } + + /** + * @return + * set of free type variables in type + */ + lazy val freeTypeVariables: Set[TypeVar] = this match { + case Recursive(uv, body) => body.freeTypeVariables - uv + case t: TypeVar => Set.single(t) + case _ => childrenTypes.foldRight(Set.empty[TypeVar])((ty, acc) => ty.freeTypeVariables ++ acc) } + +} +trait TypeImpl extends Located { self: Type => + + def children: List[Located] = childrenTypes + /** * Collect fields recursively during code generation. * Note that the type checker will reject illegal cases. @@ -145,7 +249,7 @@ abstract class TypeImpl extends Located { self: Type => case _: Union | _: Function | _: Tuple | _: Recursive | _: Neg | _: Rem | _: Bounds | _: WithExtension | Top | Bot | _: Literal | _: TypeVar | _: AppliedType | _: TypeName - | _: Constrained | _ : Splice | _: TypeTag | _: PolyType => + | _: Constrained | _ : Splice | _: TypeTag | _: PolyType | _: Selection => Nil } @@ -159,7 +263,8 @@ abstract class TypeImpl extends Located { self: Type => case Inter(lhs, rhs) => lhs.collectTypeNames ++ rhs.collectTypeNames case _: Union | _: Function | _: Record | _: Tuple | _: Recursive | _: Neg | _: Rem | _: Bounds | _: WithExtension | Top | Bot | _: PolyType - | _: Literal | _: TypeVar | _: Constrained | _ : Splice | _: TypeTag => + | _: Literal | _: TypeVar | _: Constrained | _ : Splice | _: TypeTag + | _: Selection => Nil } @@ -172,21 +277,36 @@ abstract class TypeImpl extends Located { self: Type => case Inter(ty1, ty2) => ty1.collectBodyFieldsAndTypes ++ ty2.collectBodyFieldsAndTypes case _: Union | _: Function | _: Tuple | _: Recursive | _: Neg | _: Rem | _: Bounds | _: WithExtension | Top | Bot | _: PolyType - | _: Literal | _: TypeVar | _: AppliedType | _: TypeName | _: Constrained | _ : Splice | _: TypeTag => + | _: Literal | _: TypeVar | _: AppliedType | _: TypeName | _: Constrained | _ : Splice | _: TypeTag + | _: Selection => Nil } } -final case class ShowCtx(vs: Map[TypeVar, Str], debug: Bool) // TODO make use of `debug` or rm +final case class ShowCtx( + vs: Map[TypeVar, Str], + debug: Bool, // TODO make use of `debug` or rm + indentLevel: Int, + newDefs: Bool, + angletards: Bool = false, + ) +{ + lazy val indStr: Str = ShowCtx.indentation * indentLevel + def lnIndStr: Str = "\n" + indStr + def indent: ShowCtx = copy(indentLevel = indentLevel + 1) + def < : Str = if (angletards) "<" else "[" + def > : Str = if (angletards) ">" else "]" +} object ShowCtx { + def indentation: Str = " " /** * Create a context from a list of types. For named variables and * hinted variables use what is given. For unnamed variables generate * completely new names. If same name exists increment counter suffix * in the name. */ - def mk(tys: IterableOnce[Type], _pre: Str = "'", debug: Bool = false): ShowCtx = { + def mk(tys: IterableOnce[TypeLike], newDefs: Bool, _pre: Str = "'", debug: Bool = false): ShowCtx = { val (otherVars, namedVars) = tys.iterator.toList.flatMap(_.typeVarsList).distinct.partitionMap { tv => tv.identifier match { case L(_) => L(tv.nameHint -> tv); case R(nh) => R(nh -> tv) } } @@ -221,7 +341,7 @@ object ShowCtx { S(('a' + idx % numLetters).toChar.toString + (if (postfix === 0) "" else postfix.toString), idx + 1) }.filterNot(used).map(assignName) - ShowCtx(namedMap ++ unnamedVars.zip(names), debug) + ShowCtx(namedMap ++ unnamedVars.zip(names), debug, indentLevel = 0, newDefs) } } @@ -260,12 +380,14 @@ trait PgrmImpl { self: Pgrm => }.partitionMap { case td: TypeDef => L(td) case ot: Terms => R(ot) - case NuFunDef(isLetRec, nme, tys, rhs) => + case NuFunDef(isLetRec, nme, _, tys, rhs) => R(Def(isLetRec.getOrElse(true), nme, rhs, isLetRec.isEmpty)) + case _: Constructor => die } diags.toList -> res } - override def toString = tops.map("" + _ + ";").mkString(" ") + + def showDbg: Str = tops.iterator.map("" + _.showDbg + ";").mkString(" ") } object OpApp { @@ -280,18 +402,16 @@ object OpApp { trait DeclImpl extends Located { self: Decl => val body: Located def showBody: Str = this match { - case Def(_, _, rhs, isByname) => rhs.fold(_.toString, _.show) - case td: TypeDef => td.body.show + case d: Def => d.rhs.fold(_.showDbg, _.showDbg2) + case td: TypeDef => td.body.showDbg2 } def describe: Str = this match { case _: Def => "definition" case _: TypeDef => "type declaration" } - def show: Str = showHead + (this match { - case TypeDef(Als, _, _, _, _, _, _, _) => " = "; case _ => ": " }) + showBody def showHead: Str = this match { - case Def(true, n, b, isByname) => s"rec def $n" - case Def(false, n, b, isByname) => s"def $n" + case Def(true, n, b, isByname) => s"rec def ${n.showDbg}" + case Def(false, n, b, isByname) => s"def ${n.showDbg}" case TypeDef(k, n, tps, b, _, _, pos, _) => s"${k.str} ${n.name}${if (tps.isEmpty) "" else tps.map(_.name).mkString("[", ", ", "]")}${ if (pos.isEmpty) "" else pos.mkString("(", ", ", ")") @@ -301,41 +421,89 @@ trait DeclImpl extends Located { self: Decl => trait NuDeclImpl extends Located { self: NuDecl => val body: Located + def kind: DeclKind + val declareLoc: Opt[Loc] + val abstractLoc: Opt[Loc] + def isDecl: Bool = declareLoc.nonEmpty + def isAbstract: Bool = abstractLoc.nonEmpty + def declStr: Str = if (isDecl) "declare " else "" + val nameVar: Var = self match { + case td: NuTypeDef => td.nme.toVar + case fd: NuFunDef => fd.nme + } + def name: Str = nameVar.name def showBody: Str = this match { - case NuFunDef(_, _, _, rhs) => rhs.fold(_.toString, _.show) - case td: NuTypeDef => td.body.show + case fd: NuFunDef => fd.rhs.fold(_.print(false), _.showDbg2) + case td: NuTypeDef => td.body.showDbg } def describe: Str = this match { case _: NuFunDef => "definition" case _: NuTypeDef => "type declaration" } - def show: Str = showHead + (this match { - case NuFunDef(_, _, _, L(_)) => " = " - case NuFunDef(_, _, _, R(_)) => ": " - case NuTypeDef(_, _, _, _, _, _) => " " - }) + showBody def showHead: Str = this match { - case NuFunDef(N, n, _, b) => s"fun $n" - case NuFunDef(S(false), n, _, b) => s"let $n" - case NuFunDef(S(true), n, _, b) => s"let rec $n" - case NuTypeDef(k, n, tps, sps, parents, bod) => - s"${k.str} ${n.name}${if (tps.isEmpty) "" else tps.map(_.name).mkString("‹", ", ", "›")}(${ - // sps.mkString("(",",",")") - sps})${if (parents.isEmpty) "" else if (k === Als) " = " else ": "}${parents.mkString(", ")}" + case NuFunDef(N, n, snme, _, b) => s"fun${snme.fold("")(" ("+_.name+")")} ${n.name}" + case NuFunDef(S(false), n, snme, _, b) => s"let${snme.fold("")(" "+_.name+")")} ${n.name}" + case NuFunDef(S(true), n, snme, _, b) => s"let rec${snme.fold("")(" "+_.name+")")} ${n.name}" + case NuTypeDef(k, n, tps, sps, ctor, sig, parents, sup, ths, bod) => + s"${k.str} ${n.name}${if (tps.isEmpty) "" else tps.map(_._2.name).mkString("‹", ", ", "›")}${ + sps.fold("")("(" + _.showElems + ")") + }${sig.fold("")(": " + _.showDbg2)}${ + if (parents.isEmpty) "" else if (k === Als) " = " else ": "}${parents.iterator.map(_.showDbg).mkString(", ")}" + } + lazy val genUnapply: Opt[NuFunDef] = this match { + case td: NuTypeDef if td.kind is Cls => td.params.map { tup => + val ret = Let(false, Var("_"), Asc(Var("x"), TypeName(name)), Tup(tup.fields.map { + case S(p) -> f => N -> Fld(FldFlags.empty, Sel(Var("x"), Var("#" + p.name).withLocOf(p))) + case N -> Fld(flags, p: Var) => N -> Fld(FldFlags.empty, Sel(Var("x"), Var("#" + p.name).withLocOf(p))) + case _ => die + })) + NuFunDef(N, Var("unapply"), N, Nil, L(Lam( + Tup(N -> Fld(FldFlags.empty, Var("x")) :: Nil), + ret)))(N, N, N, N, true) + } + case _ => N } } + trait TypingUnitImpl extends Located { self: TypingUnit => - def show: Str = entities.map { - case t: Term => t.toString - case d: NuDecl => d.show - case _ => die + def showDbg: Str = entities.iterator.map { + case t: Term => t.print(false) + case d: NuDecl => d.showDbg + case c: Constructor => c.showDbg + case e => lastWords(s"Unexpected typing unit entity: $e") }.mkString("{", "; ", "}") + lazy val children: List[Located] = entities } +trait ConstructorImpl { self: Constructor => + // def children: List[Located] = fields.map(_._2) + def describe: Str = "constructor" + // def showDbg: Str = s"constructor(${fields.map(_._1.name).mkString(", ")})" +} + trait TypeNameImpl extends Ordered[TypeName] { self: TypeName => - val base: TypeName = this + def base: TypeName = this + def targs: Ls[Type] = Nil def compare(that: TypeName): Int = this.name compare that.name + lazy val toVar: Var = Var(name).withLocOf(this) +} + +trait FldFlagsImpl extends Located { self: FldFlags => + def children: Ls[Located] = Nil + override def toString(): String = { + val FldFlags(m, s, g) = this + val res = (if (m) "m" else "") + (if (s) "s" else "") + (if (g) "g" else "") + if (res.isEmpty) "_" else res + } +} + +trait FldImpl extends Located { self: Fld => + def children: Ls[Located] = self.value :: Nil + def describe: Str = + (if (self.flags.spec) "specialized " else "") + + (if (self.flags.mut) "mutable " else "") + + self.value.describe } trait TermImpl extends StatementImpl { self: Term => @@ -373,7 +541,7 @@ trait TermImpl extends StatementImpl { self: Term => case Rcd(fields) => "record" case Sel(receiver, fieldName) => "field selection" case Let(isRec, name, rhs, body) => "let binding" - case Tup((N, Fld(_, _, x)) :: Nil) => x.describe + case Tup((N, Fld(_, x)) :: Nil) => x.describe case Tup((S(_), x) :: Nil) => "binding" case Tup(xs) => "tuple" case Bind(l, r) => "'as' binding" @@ -384,84 +552,109 @@ trait TermImpl extends StatementImpl { self: Term => case Assign(lhs, rhs) => "assignment" case Splc(fs) => "splice" case New(h, b) => "object instantiation" + case NuNew(_) => "new instance" + case Rft(_, _) => "refinement" case If(_, _) => "if-else block" case TyApp(_, _) => "type application" case Where(_, _) => s"constraint clause" case Forall(_, _) => s"forall clause" case Inst(bod) => "explicit instantiation" - case AdtMatchWith(cond, arms) => "adt pattern matching" + case Super() => "super" + case Eqn(lhs, rhs) => "assign for ctor" + case AdtMatchWith(cond, arms) => "ADT pattern matching" } } - - override def toString: Str = print(false) + + override def showDbg: Str = print(false) def print(brackets: Bool): Str = { def bra(str: Str): Str = if (brackets) s"($str)" else str this match { - case Bra(true, trm) => s"'{' $trm '}'" - case Bra(false, trm) => s"'(' $trm ')'" - case Blk(stmts) => stmts.mkString("{", "; ", "}") + case Bra(true, trm) => s"'{' ${trm.showDbg} '}'" + case Bra(false, trm) => s"'(' ${trm.showDbg} ')'" + case Blk(stmts) => stmts.iterator.map(_.showDbg).mkString("{", "; ", "}") case IntLit(value) => value.toString case DecLit(value) => value.toString case StrLit(value) => '"'.toString + value + '"' case UnitLit(value) => if (value) "undefined" else "null" - case Var(name) => name - case Asc(trm, ty) => s"$trm : ${ty.show}" |> bra - case Lam(pat, rhs) => s"($pat) => $rhs" |> bra - case App(lhs, rhs) => s"${lhs.print(!lhs.isInstanceOf[App])} ${rhs.print(true)}" |> bra + case v @ Var(name) => name + v.uid.fold("")("::"+_.toString) + case Asc(trm, ty) => s"${trm.showDbg} : ${ty.showDbg2}" |> bra + case Lam(pat: Tup, rhs) => s"(${pat.showElems}) => ${rhs.showDbg}" |> bra + case Lam(pat, rhs) => s"(...${pat.showDbg}) => ${rhs.showDbg}" |> bra + case App(lhs, rhs: Tup) => s"${lhs.print(!lhs.isInstanceOf[App])}(${rhs.showElems})" |> bra + case App(lhs, rhs) => s"${lhs.print(!lhs.isInstanceOf[App])}(...${rhs.print(true)})" |> bra case Rcd(fields) => fields.iterator.map(nv => - (if (nv._2.mut) "mut " else "") + nv._1.name + ": " + nv._2.value).mkString("{", ", ", "}") - case Sel(receiver, fieldName) => "(" + receiver.toString + ")." + fieldName - case Let(isRec, name, rhs, body) => - s"let${if (isRec) " rec" else ""} $name = $rhs in $body" |> bra - case Tup(xs) => - xs.iterator.map { case (n, t) => - (if (t.mut) "mut " else "") + (if (t.spec) "#" else "") + n.fold("")(_.name + ": ") + t.value + "," - // }.mkString("(", " ", ")") - }.mkString(" ") |> bra + (if (nv._2.flags.mut) "mut " else "") + nv._1.name + ": " + nv._2.value.showDbg).mkString("{", ", ", "}") + case Sel(receiver, fieldName) => "(" + receiver.showDbg + ")." + fieldName.showDbg + case Let(isRec, Var(name), rhs, body) => + s"let${if (isRec) " rec" else ""} $name = ${rhs.showDbg} in ${body.showDbg}" |> bra + case tup: Tup => "[" + tup.showElems + "]" case Splc(fields) => fields.map{ case L(l) => s"...$l" - case R(Fld(m, s, r)) => (if (m) "mut " else "") + (if (s) "#" else "") + r + case R(Fld(FldFlags(m, s, g), r)) => ( + (if (m) "mut " else "") + + (if (g) "val " else "") + + (if (s) "#" else "") + + r + ) }.mkString("(", ", ", ")") - case Bind(l, r) => s"$l as $r" |> bra - case Test(l, r) => s"$l is $r" |> bra - case With(t, fs) => s"$t with $fs" |> bra + case Bind(l, r) => s"${l.showDbg} as ${r.showDbg}" |> bra + case Test(l, r) => s"${l.showDbg} is ${r.showDbg}" |> bra + case With(t, fs) => s"${t.showDbg} with ${fs.showDbg}" |> bra case CaseOf(s, c) => - s"case $s of { ${c.print(true)} }" |> bra - case Subs(a, i) => s"($a)[$i]" - case Assign(lhs, rhs) => s" $lhs <- $rhs" |> bra - case New(S((at, ar)), bod) => s"new ${at.show}($ar) ${bod.show}" |> bra - case New(N, bod) => s"new ${bod.show}" |> bra - case If(body, els) => s"if $body" + els.fold("")(" else " + _) |> bra - case TyApp(lhs, targs) => s"$lhs‹${targs.map(_.show).mkString(", ")}›" - case Where(bod, wh) => s"${bod} where {${wh.mkString("; ")}}" - case Forall(ps, bod) => s"forall ${ps.mkString(", ")}. ${bod}" + s"case ${s.showDbg} of { ${c.print(true)} }" |> bra + case Subs(a, i) => s"(${a.showDbg})[${i.showDbg}]" + case Assign(lhs, rhs) => s" ${lhs.showDbg} <- ${rhs.showDbg}" |> bra + case New(S((at, ar)), bod) => s"new ${at.showDbg2}(${ar.showDbg}) ${bod.showDbg}" |> bra + case New(N, bod) => s"new ${bod.showDbg}" |> bra + case NuNew(cls) => s"new ${cls.showDbg}" |> bra + case If(body, els) => s"if ${body.showDbg}" + els.fold("")(" else " + _.showDbg) |> bra + case TyApp(lhs, targs) => s"${lhs.showDbg}‹${targs.iterator.map(_.showDbg2).mkString(", ")}›" + case Where(bod, wh) => s"${bod.showDbg} where {${wh.iterator.map(_.showDbg).mkString("; ")}}" + case Forall(ps, bod) => s"forall ${ps.mkString(", ")}. ${bod.showDbg}" case Inst(bod) => s"${bod.print(true)}!" + case Super() => "super" + case Eqn(lhs, rhs) => s"${lhs.showDbg} = ${rhs.showDbg}" case AdtMatchWith(cond, arms) => - s"match ${cond} with ${arms.map (patmat => s"${patmat.pat} -> ${patmat.rhs}").mkString (" | ") }" + s"match ${cond.showDbg} with ${arms.map (patmat => s"${patmat.pat.showDbg} -> ${patmat.rhs.showDbg}").mkString (" | ") }" + case Rft(bse, tu) => s"${bse.showDbg} ${tu.showDbg}" }} + def toTypeRaise(implicit raise: Raise): Type = toType match { + case L(d) => raise(d); Bot + case R(ty) => ty + } def toType: Diagnostic \/ Type = try R(toType_!.withLocOf(this)) catch { case e: NotAType => import Message._ - L(ErrorReport(msg"not a recognized type: ${e.trm.toString}"->e.trm.toLoc::Nil)) } + L(ErrorReport(msg"Not a recognized type" -> e.trm.toLoc::Nil, newDefs=true, source=Diagnostic.Parsing)) } protected def toType_! : Type = (this match { case Var(name) if name.startsWith("`") => TypeVar(R(name.tail), N) case Var(name) if name.startsWith("'") => TypeVar(R(name), N) case Var(name) => TypeName(name) case lit: Lit => Literal(lit) - case App(App(Var("|"), lhs), rhs) => Union(lhs.toType_!, rhs.toType_!) - case App(App(Var("&"), lhs), rhs) => Inter(lhs.toType_!, rhs.toType_!) + case App(Var("->"), PlainTup(lhs @ (_: Tup | Bra(false, _: Tup)), rhs)) => + // * ^ Note: don't think the plain _: Tup without a Bra can actually occur + Function(lhs.toType_!, rhs.toType_!) + case App(Var("->"), PlainTup(lhs, rhs)) => + Function(Tuple(N -> Field(N, lhs.toType_!) :: Nil), rhs.toType_!) + case App(Var("|"), PlainTup(lhs, rhs)) => + Union(lhs.toType_!, rhs.toType_!) + case App(Var("&"), PlainTup(lhs, rhs)) => + Inter(lhs.toType_!, rhs.toType_!) + case ty @ App(v @ Var("\\"), PlainTup(lhs, rhs)) => + Inter(lhs.toType_!, Neg(rhs.toType_!).withLoc(Loc(v :: rhs :: Nil))).withLoc(ty.toCoveringLoc) + case App(Var("~"), rhs) => Neg(rhs.toType_!) case Lam(lhs, rhs) => Function(lhs.toType_!, rhs.toType_!) - case App(lhs, rhs) => lhs.toType_! match { - case AppliedType(base, targs) => AppliedType(base, targs :+ rhs.toType_!) - case p: TypeName => AppliedType(p, rhs.toType_! :: Nil) - case _ => throw new NotAType(this) - } + case App(lhs, PlainTup(fs @ _*)) => + lhs.toType_! match { + case tn: TypeName => AppliedType(tn, fs.iterator.map(_.toType_!).toList) + case _ => throw new NotAType(this) + } case Tup(fields) => Tuple(fields.map(fld => (fld._1, fld._2 match { - case Fld(m, s, v) => val ty = v.toType_!; Field(Option.when(m)(ty), ty) + case Fld(FldFlags(m, s, _), v) => val ty = v.toType_!; Field(Option.when(m)(ty), ty) }))) case Bra(rcd, trm) => trm match { case _: Rcd => if (rcd) trm.toType_! else throw new NotAType(this) @@ -472,7 +665,7 @@ trait TermImpl extends StatementImpl { self: Term => case _ => throw new NotAType(this) } case Rcd(fields) => Record(fields.map(fld => (fld._1, fld._2 match { - case Fld(m, s, v) => val ty = v.toType_!; Field(Option.when(m)(ty), ty) + case Fld(FldFlags(m, s, _), v) => val ty = v.toType_!; Field(Option.when(m)(ty), ty) }))) case Where(body, where) => Constrained(body.toType_!, Nil, where.map { @@ -482,10 +675,12 @@ trait TermImpl extends StatementImpl { self: Term => case Forall(ps, bod) => PolyType(ps.map(R(_)), bod.toType_!) // - case Sel(receiver, fieldName) => receiver match { - case Var(name) if !name.startsWith("`") => TypeName(s"$name.$fieldName") - case _ => throw new NotAType(this) - } + case Sel(receiver, field) => + Selection(receiver.toType_!, TypeName(field.name).withLocOf(field)) + // case Sel(receiver, fieldName) => receiver match { + // case Var(name) if !name.startsWith("`") => TypeName(s"$name.$fieldName") + // case _ => throw new NotAType(this) + // } // TODO: // case Let(isRec, name, rhs, body) => ??? // case Blk(stmts) => ??? @@ -494,35 +689,68 @@ trait TermImpl extends StatementImpl { self: Term => // case Test(trm, ty) => ??? // case With(trm, fieldNme, fieldVal) => ??? // case CaseOf(trm, cases) => ??? - // case IntLit(value) => ??? - // case DecLit(value) => ??? - // case StrLit(value) => ??? case _ => throw new NotAType(this) }).withLocOf(this) } private class NotAType(val trm: Statement) extends Throwable +object PlainTup { + def apply(fields: Term*): Term = + Tup(fields.iterator.map(t => (N, Fld(FldFlags.empty, t))).toList) + def unapplySeq(trm: Term): Opt[List[Term]] = trm match { + case Tup(fields) if fields.forall(f => + f._1.isEmpty && f._2.flags.mut === false && f._2.flags.spec === false + ) => S(fields.map(_._2.value)) + case _ => N + } +} + trait LitImpl { self: Lit => - def baseClasses: Set[TypeName] = this match { + def baseClassesOld: Set[TypeName] = this match { case _: IntLit => Set.single(TypeName("int")) + TypeName("number") case _: StrLit => Set.single(TypeName("string")) case _: DecLit => Set.single(TypeName("number")) case _: UnitLit => Set.empty } + def baseClassesNu: Set[TypeName] = this match { + case _: IntLit => Set.single(TypeName("Int")) + TypeName("Num") + TypeName("Object") + case _: StrLit => Set.single(TypeName("Str")) + TypeName("Object") + case _: DecLit => Set.single(TypeName("Num")) + TypeName("Object") + case _: UnitLit => Set.single(TypeName("Object")) + } } trait VarImpl { self: Var => + /** Check if the variable name is an integer. */ + def isIndex: Bool = name.headOption match { + case S('0') => name.length === 1 + case S(_) => name.forall(_.isDigit) + case N => false + } + /** Get the integer if it's a valid index. */ + def toIndexOption: Opt[Int] = if (isIndex) name.toIntOption else N def isPatVar: Bool = - name.head.isLetter && name.head.isLower && name =/= "true" && name =/= "false" + (name.head.isLetter && name.head.isLower || name.head === '_' || name.head === '$') && name =/= "true" && name =/= "false" + def toVar: Var = this var uid: Opt[Int] = N } +trait TupImpl { self: Tup => + def showElems: Str = + fields.iterator.map { case (n, t) => ( + (if (t.flags.mut) "mut " else "") + + (if (t.flags.genGetter) "val " else "") + + (if (t.flags.spec) "#" else "") + + n.fold("")(_.name + ": ") + t.value.print(false) + "," + )}.mkString(" ") +} + trait SimpleTermImpl extends Ordered[SimpleTerm] { self: SimpleTerm => def compare(that: SimpleTerm): Int = this.idStr compare that.idStr val idStr: Str = this match { case Var(name) => name - case lit: Lit => lit.toString + case lit: Lit => lit.showDbg } } @@ -566,7 +794,7 @@ trait Located { def withLocOf(that: Located): this.type = withLoc(that.toLoc) def hasLoc: Bool = origin.isDefined lazy val toLoc: Opt[Loc] = getLoc - private def getLoc: Opt[Loc] = { + private[mlscript] def getLoc: Opt[Loc] = { def subLocs = children.iterator.flatMap(_.toLoc.iterator) if (spanStart < 0) spanStart = subLocs.map(_.spanStart).minOption.getOrElse(return N) @@ -592,12 +820,17 @@ trait Located { trait DesugaredStatementImpl extends Located { self: DesugaredStatement => def describe: Str + + def showDbg: Str } trait StatementImpl extends Located { self: Statement => lazy val desugared = doDesugar private def doDesugar: Ls[Diagnostic] -> Ls[DesugaredStatement] = this match { + // case ctor: Constructor => + // import Message._ + // (ErrorReport(msg"constructor must be in a class." -> ctor.toLoc :: Nil, newDefs=true) :: Nil) -> Nil case l @ LetS(isrec, pat, rhs) => val (diags, v, args) = desugDefnPattern(pat, Nil) diags -> (Def(isrec, v, L(args.foldRight(rhs)(Lam(_, _))), false).withLocOf(l) :: Nil) // TODO use v, not v.name @@ -608,64 +841,66 @@ trait StatementImpl extends Located { self: Statement => case v @ Var(nme) => R(TypeName(nme).withLocOf(v)) case t => import Message._ - L(ErrorReport(msg"illegal datatype type parameter shape: ${t.toString}" -> t.toLoc :: Nil)) + L(ErrorReport(msg"illegal datatype type parameter shape: ${t.showDbg}" -> t.toLoc :: Nil, newDefs=false)) } val (diags2, cs) = desugarCases(bod, targs) val dataDefs = cs.collect{case td: TypeDef => td} (diags ::: diags2 ::: diags3) -> (TypeDef(Als, TypeName(v.name).withLocOf(v), targs, dataDefs.map(td => AppliedType(td.nme, td.tparams)).reduceOption(Union).getOrElse(Bot), Nil, Nil, Nil, N ).withLocOf(hd) :: cs) - case NuTypeDef(Nms, nme, tps, tup @ Tup(fs), pars, unit) => - ??? // TODO - case NuTypeDef(k @ Als, nme, tps, tup @ Tup(fs), pars, unit) => - // TODO properly check: - require(fs.isEmpty, fs) - require(pars.size === 1, pars) - require(unit.entities.isEmpty, unit) - val (diags, rhs) = pars.head.toType match { - case L(ds) => (ds :: Nil) -> Top - case R(ty) => Nil -> ty - } - diags -> (TypeDef(k, nme, tps, rhs, Nil, Nil, Nil, N) :: Nil) - case NuTypeDef(k @ (Cls | Trt), nme, tps, tup @ Tup(fs), pars, unit) => - val diags = Buffer.empty[Diagnostic] - def tt(trm: Term): Type = trm.toType match { - case L(ds) => diags += ds; Top - case R(ty) => ty - } - val params = fs.map { - case (S(nme), Fld(mut, spec, trm)) => - val ty = tt(trm) - nme -> Field(if (mut) S(ty) else N, ty) - case (N, Fld(mut, spec, nme: Var)) => nme -> Field(if (mut) S(Bot) else N, Top) - case _ => die - } - val pos = params.unzip._1 - val bod = pars.map(tt).foldRight(Record(params): Type)(Inter) - val termName = Var(nme.name).withLocOf(nme) - val ctor = Def(false, termName, L(Lam(tup, App(termName, Tup(N -> Fld(false, false, Rcd(fs.map { - case (S(nme), fld) => nme -> fld - case (N, fld @ Fld(mut, spec, nme: Var)) => nme -> fld - case _ => die - })) :: Nil)))), true) - diags.toList -> (TypeDef(k, nme, tps, bod, Nil, Nil, pos, N) :: ctor :: Nil) + case NuTypeDef(Mod, nme, tps, tup, ctor, sig, pars, sup, ths, unit) => + ??? // TODO + case NuTypeDef(Mxn, nme, tps, tup, ctor, sig, pars, sup, ths, unit) => + ??? // TODO + case NuTypeDef(k @ Als, nme, tps, tup, ctor, sig, pars, sup, ths, unit) => + // TODO properly check: + require(tup.forall(tup => tup.fields.isEmpty), tup) + require(pars.size === 0, pars) + require(sig.isDefined) + require(ths.isEmpty, ths) + require(unit.entities.isEmpty, unit) + Nil -> (TypeDef(k, nme, tps.map(_._2), sig.getOrElse(die), Nil, Nil, Nil, N) :: Nil) + case NuTypeDef(k @ (Cls | Trt), nme, tps, opt, ctor, sig, pars, sup, ths, unit) => + val tup = opt.getOrElse(Tup(Nil)) + val fs = tup.fields + val diags = Buffer.empty[Diagnostic] + def tt(trm: Term): Type = trm.toType match { + case L(ds) => diags += ds; Top + case R(ty) => ty + } + val params = fs.map { + case (S(nme), Fld(FldFlags(mut, spec, _), trm)) => + val ty = tt(trm) + nme -> Field(if (mut) S(ty) else N, ty) + case (N, Fld(FldFlags(mut, spec, _), nme: Var)) => nme -> Field(if (mut) S(Bot) else N, Top) + case _ => die + } + val pos = params.unzip._1 + val bod = pars.map(tt).foldRight(Record(params): Type)(Inter) + val termName = Var(nme.name).withLocOf(nme) + val ctor = Def(false, termName, L(Lam(tup, App(termName, Tup(N -> Fld(FldFlags.empty, Rcd(fs.map { + case (S(nme), fld) => nme -> Fld(FldFlags(false, false, fld.flags.genGetter), nme) + case (N, fld @ Fld(_, nme: Var)) => nme -> fld + case _ => die + })) :: Nil)))), true) + diags.toList -> (TypeDef(k, nme, tps.map(_._2), bod, Nil, Nil, pos, N) :: ctor :: Nil) case d: DesugaredStatement => Nil -> (d :: Nil) } import Message._ protected def desugDefnPattern(pat: Term, args: Ls[Term]): (Ls[Diagnostic], Var, Ls[Term]) = pat match { case App(l, r) => desugDefnPattern(l, r :: args) case v: Var => (Nil, v, args) - case _ => (ErrorReport(msg"Unsupported pattern shape" -> pat.toLoc :: Nil) :: Nil, Var(""), args) // TODO + case _ => (ErrorReport(msg"Unsupported pattern shape" -> pat.toLoc :: Nil, newDefs=true) :: Nil, Var(""), args) // TODO } protected def desugarCases(bod: Term, baseTargs: Ls[TypeName]): (Ls[Diagnostic], Ls[Decl]) = bod match { case Blk(stmts) => desugarCases(stmts, baseTargs) case Tup(comps) => val stmts = comps.map { - case N -> Fld(_, _, d) => d - case S(n) -> Fld(_, _, d) => ??? + case N -> Fld(_, d) => d + case S(n) -> Fld(_, d) => ??? } desugarCases(stmts, baseTargs) - case _ => (ErrorReport(msg"Unsupported data type case shape" -> bod.toLoc :: Nil) :: Nil, Nil) + case _ => (ErrorReport(msg"Unsupported data type case shape" -> bod.toLoc :: Nil, newDefs=true) :: Nil, Nil) } protected def desugarCases(stmts: Ls[Statement], baseTargs: Ls[TypeName]): (Ls[Diagnostic], Ls[Decl]) = stmts match { case stmt :: stmts => @@ -692,7 +927,7 @@ trait StatementImpl extends Located { self: Statement => case Bra(false, t) => getFields(t) case Bra(true, Tup(fs)) => Record(fs.map { - case (S(n) -> Fld(mut, _, t)) => + case (S(n) -> Fld(FldFlags(mut, _, _), t)) => val ty = t.toType match { case L(d) => allDiags += d; Top case R(t) => t @@ -704,7 +939,7 @@ trait StatementImpl extends Located { self: Statement => case Bra(true, t) => lastWords(s"$t ${t.getClass}") case Tup(fs) => // TODO factor with case Bra(true, Tup(fs)) above Tuple(fs.map { - case (S(n) -> Fld(tmut, _, t)) => + case (S(n) -> Fld(FldFlags(tmut, _, _), t)) => val ty = t.toType match { case L(d) => allDiags += d; Top case R(t) => t @@ -754,28 +989,41 @@ trait StatementImpl extends Located { self: Statement => case Assign(lhs, rhs) => lhs :: rhs :: Nil case Splc(fields) => fields.map{case L(l) => l case R(r) => r.value} case If(body, els) => body :: els.toList - case d @ NuFunDef(_, v, ts, rhs) => v :: ts ::: d.body :: Nil + case d @ NuFunDef(_, v, v2, ts, rhs) => v :: v2.toList ::: ts ::: d.body :: Nil case TyApp(lhs, targs) => lhs :: targs case New(base, bod) => base.toList.flatMap(ab => ab._1 :: ab._2 :: Nil) ::: bod :: Nil - case NuTypeDef(_, _, _, _, _, _) => ??? + case NuNew(cls) => cls :: Nil + case Rft(bs, tu) => bs :: tu :: Nil case Where(bod, wh) => bod :: wh case Forall(ps, bod) => ps ::: bod :: Nil case Inst(bod) => bod :: Nil - case AdtMatchWith(cond, _) => cond :: Nil + case Super() => Nil + case Constructor(params, body) => params :: body :: Nil + case Eqn(lhs, rhs) => lhs :: rhs :: Nil + case NuTypeDef(k, nme, tps, ps, ctor, sig, pars, sup, ths, bod) => + nme :: tps.map(_._2) ::: ps.toList ::: pars ::: ths.toList ::: bod :: Nil + case AdtMatchWith(cond, _) => cond :: Nil // FIXME discards branches... } - - override def toString: Str = this match { - case LetS(isRec, name, rhs) => s"let${if (isRec) " rec" else ""} $name = $rhs" - case DatatypeDefn(head, body) => s"data type $head of $body" - case DataDefn(head) => s"data $head" - case _: Term => super.toString - case d: Decl => d.show - case d: NuDecl => d.show + def showDbg: Str = this match { + case LetS(isRec, name, rhs) => s"let${if (isRec) " rec" else ""} ${name.showDbg} = ${rhs.showDbg}" + case DatatypeDefn(head, body) => s"data type ${head.showDbg} of ${body.showDbg}" + case DataDefn(head) => s"data ${head.showDbg}" + case Constructor(params, body) => s"constructor(${params.showElems}) ${body.showDbg}" + case t: Term => t.print(false) + case d: Decl => d.showHead + (d match { + case n: TypeDef if n.kind is Als => " = " + case _ => ": " + }) + d.showBody + case n: NuDecl => n.showHead + (n match { + case n: NuFunDef => if (n.rhs.isLeft) " = " else ": " + case _: NuTypeDef => " " + }) + n.showBody } } trait BlkImpl { self: Blk => + def kind: Block.type = Block def flatten: Blk = Blk(stmts.flatMap { case b: Blk => b.flatten.stmts @@ -792,11 +1040,6 @@ trait CaseBranchesImpl extends Located { self: CaseBranches => case NoCases => Nil } - lazy val toList: Ls[Case] = this match { - case c: Case => c :: c.rest.toList - case _ => Nil - } - def print(isFirst: Bool): Str = this match { case Case(pat, body, rest) => (if (isFirst) { "" } else { "; " }) + @@ -806,40 +1049,17 @@ trait CaseBranchesImpl extends Located { self: CaseBranches => "_ => " + body.print(false) case NoCases => "" } + } -abstract class MatchCase - -object MatchCase { - final case class ClassPattern(name: Var, fields: Buffer[Var -> Var]) extends MatchCase - final case class TuplePattern(arity: Int, fields: Buffer[Int -> Var]) extends MatchCase - final case class BooleanTest(test: Term) extends MatchCase -} - -////////////////////////////// -// ADT style pattern matching -////////////////////////////// -trait MatchWithImpl { self: AdtMatchWith => - -} trait AdtMatchPatImpl extends Located { self: AdtMatchPat => - - def children: List[Located] = this match { - case AdtMatchPat(p, t) => p :: t :: Nil - } - - override def toString: String = this match { - case AdtMatchPat(pat, trm) => s"($pat) then $trm" - } + def children: List[Located] = pat :: rhs :: Nil + override def toString: String = s"($pat) then $rhs" } trait IfBodyImpl extends Located { self: IfBody => - + def children: List[Located] = this match { - // case Case(pat, body, rest) => pat :: body :: rest :: Nil - // case Wildcard(body) => body :: Nil - // case NoCases => Nil - case _ if false => ??? // TODO case IfBlock(ts) => ts.map(_.fold(identity, identity)) case IfThen(l, r) => l :: r :: Nil case IfElse(t) => t :: Nil @@ -848,20 +1068,13 @@ trait IfBodyImpl extends Located { self: IfBody => case IfOpsApp(t, ops) => t :: ops.flatMap(x => x._1 :: x._2 :: Nil) } - // lazy val toList: Ls[Case] = this match { - // case c: Case => c :: c.rest.toList - // case _ => Nil - // } - - override def toString: String = this match { - // case IfThen(lhs, rhs) => s"${lhs.print(true)} then $rhs" - case IfThen(lhs, rhs) => s"($lhs) then $rhs" - case IfElse(trm) => s"else $trm" - case IfBlock(ts) => s"‹${ts.map(_.fold(identity, identity)).mkString("; ")}›" - case IfOpApp(lhs, op, ib) => s"$lhs $op $ib" - case IfOpsApp(lhs, ops) => s"$lhs ‹${ops.iterator.map{case(v, r) => s"· $v $r"}.mkString("; ")}›" - case IfLet(isRec, v, r, b) => s"${if (isRec) "rec " else ""}let $v = $r in $b" - // case _ => ??? // TODO + def showDbg: String = this match { + case IfThen(lhs, rhs) => s"(${lhs.showDbg}) then ${rhs.showDbg}" + case IfElse(trm) => s"else ${trm.showDbg}" + case IfBlock(ts) => s"‹${ts.iterator.map(_.fold(_.showDbg, _.showDbg)).mkString("; ")}›" + case IfOpApp(lhs, op, ib) => s"${lhs.showDbg} ${op.showDbg} ${ib.showDbg}" + case IfOpsApp(lhs, ops) => s"${lhs.showDbg} ‹${ops.iterator.map{case(v, r) => s"· ${v.showDbg} ${r.showDbg}"}.mkString("; ")}›" + case IfLet(isRec, v, r, b) => s"${if (isRec) "rec " else ""}let ${v.showDbg} = ${r.showDbg} in ${b.showDbg}" } } diff --git a/shared/src/main/scala/mlscript/package.scala b/shared/src/main/scala/mlscript/package.scala index bd4c4023e5..95cff80304 100644 --- a/shared/src/main/scala/mlscript/package.scala +++ b/shared/src/main/scala/mlscript/package.scala @@ -3,6 +3,8 @@ import mlscript.utils.shorthands._ package object mlscript { + type Raise = Diagnostic => Unit + val ExtrusionPrefix: Str = "??" } diff --git a/shared/src/main/scala/mlscript/syntax.scala b/shared/src/main/scala/mlscript/syntax.scala index a208d7f1a8..35ac9f316f 100644 --- a/shared/src/main/scala/mlscript/syntax.scala +++ b/shared/src/main/scala/mlscript/syntax.scala @@ -5,10 +5,10 @@ import mlscript.utils._, shorthands._ // Terms -final case class Pgrm(tops: Ls[Statement]) extends PgrmOrTypingUnit with PgrmImpl +final case class Pgrm(tops: Ls[Statement]) extends PgrmImpl sealed abstract class Decl extends DesugaredStatement with DeclImpl -final case class Def(rec: Bool, nme: Var, rhs: Term \/ PolyType, isByname: Bool) extends Decl with Terms { +final case class Def(rec: Bool, nme: Var, rhs: Term \/ Type, isByname: Bool) extends Decl with Terms { val body: Located = rhs.fold(identity, identity) } @@ -47,23 +47,31 @@ final case class MethodDef[RHS <: Term \/ Type]( val children: Ls[Located] = nme :: body :: Nil } -sealed abstract class TypeDefKind(val str: Str) +sealed trait NameRef extends Located { val name: Str; def toVar: Var } + +sealed abstract class OuterKind(val str: Str) +case object Block extends OuterKind("block") +sealed abstract class DeclKind(str: Str) extends OuterKind(str) +case object Val extends DeclKind("value") +sealed abstract class TypeDefKind(str: Str) extends DeclKind(str) sealed trait ObjDefKind -case object Cls extends TypeDefKind("class") with ObjDefKind +sealed trait ClsLikeKind extends ObjDefKind +case object Cls extends TypeDefKind("class") with ClsLikeKind case object Trt extends TypeDefKind("trait") with ObjDefKind +case object Mxn extends TypeDefKind("mixin") case object Als extends TypeDefKind("type alias") -case object Nms extends TypeDefKind("namespace") +case object Mod extends TypeDefKind("module") with ClsLikeKind sealed abstract class Term extends Terms with TermImpl sealed abstract class Lit extends SimpleTerm with LitImpl -final case class Var(name: Str) extends SimpleTerm with VarImpl +final case class Var(name: Str) extends SimpleTerm with VarImpl with NameRef final case class Lam(lhs: Term, rhs: Term) extends Term final case class App(lhs: Term, rhs: Term) extends Term -final case class Tup(fields: Ls[Opt[Var] -> Fld]) extends Term +final case class Tup(fields: Ls[Opt[Var] -> Fld]) extends Term with TupImpl final case class Rcd(fields: Ls[Var -> Fld]) extends Term final case class Sel(receiver: Term, fieldName: Var) extends Term final case class Let(isRec: Bool, name: Var, rhs: Term, body: Term) extends Term -final case class Blk(stmts: Ls[Statement]) extends Term with BlkImpl +final case class Blk(stmts: Ls[Statement]) extends Term with BlkImpl with Outer final case class Bra(rcd: Bool, trm: Term) extends Term final case class Asc(trm: Term, ty: Type) extends Term final case class Bind(lhs: Term, rhs: Term) extends Term @@ -74,20 +82,20 @@ final case class Subs(arr: Term, idx: Term) extends Ter final case class Assign(lhs: Term, rhs: Term) extends Term final case class Splc(fields: Ls[Either[Term, Fld]]) extends Term final case class New(head: Opt[(NamedType, Term)], body: TypingUnit) extends Term // `new C(...)` or `new C(){...}` or `new{...}` +final case class NuNew(cls: Term) extends Term final case class If(body: IfBody, els: Opt[Term]) extends Term final case class TyApp(lhs: Term, targs: Ls[Type]) extends Term final case class Where(body: Term, where: Ls[Statement]) extends Term final case class Forall(params: Ls[TypeVar], body: Term) extends Term final case class Inst(body: Term) extends Term +final case class Super() extends Term +final case class Eqn(lhs: Var, rhs: Term) extends Term // equations such as x = y, notably used in constructors; TODO: make lhs a Term +final case class Rft(base: Term, decls: TypingUnit) extends Term -final case class AdtMatchWith(cond: Term, arms: Ls[AdtMatchPat]) extends Term { - override def describe: Str = "adt match expression" -} - -final case class AdtMatchPat(pat: Term, rhs: Term) extends AdtMatchPatImpl +final case class AdtMatchWith(cond: Term, arms: Ls[AdtMatchPat]) extends Term +final case class AdtMatchPat(pat: Term, rhs: Term) extends AdtMatchPatImpl sealed abstract class IfBody extends IfBodyImpl -// final case class IfTerm(expr: Term) extends IfBody // rm? final case class IfThen(expr: Term, rhs: Term) extends IfBody final case class IfElse(expr: Term) extends IfBody final case class IfLet(isRec: Bool, name: Var, rhs: Term, body: IfBody) extends IfBody @@ -96,7 +104,10 @@ final case class IfOpsApp(lhs: Term, opsRhss: Ls[Var -> IfBody]) extends IfBody final case class IfBlock(lines: Ls[IfBody \/ Statement]) extends IfBody // final case class IfApp(fun: Term, opsRhss: Ls[Var -> IfBody]) extends IfBody -final case class Fld(mut: Bool, spec: Bool, value: Term) +final case class FldFlags(mut: Bool, spec: Bool, genGetter: Bool) extends FldFlagsImpl // TODO make it a Located and use in diagnostics +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 @@ -113,9 +124,9 @@ trait IdentifiedTerm sealed abstract class SimpleTerm extends Term with IdentifiedTerm with SimpleTermImpl sealed trait Statement extends StatementImpl -final case class LetS(isRec: Bool, pat: Term, rhs: Term) extends Statement -final case class DataDefn(body: Term) extends Statement -final case class DatatypeDefn(head: Term, body: Term) extends Statement +final case class LetS(isRec: Bool, pat: Term, rhs: Term) extends Statement +final case class DataDefn(body: Term) extends Statement +final case class DatatypeDefn(head: Term, body: Term) extends Statement sealed trait DesugaredStatement extends Statement with DesugaredStatementImpl @@ -124,9 +135,11 @@ sealed trait Terms extends DesugaredStatement // Types -sealed abstract class Type extends TypeImpl +sealed abstract class TypeLike extends TypeLikeImpl -sealed trait NamedType extends Type { val base: TypeName } +sealed abstract class Type extends TypeLike with TypeImpl + +sealed trait NamedType extends Type { def base: TypeName; def targs: Ls[Type] } sealed abstract class Composed(val pol: Bool) extends Type with ComposedImpl @@ -137,12 +150,15 @@ final case class Record(fields: Ls[Var -> Field]) extends Type final case class Tuple(fields: Ls[Opt[Var] -> Field]) extends Type final case class Recursive(uv: TypeVar, body: Type) extends Type final case class AppliedType(base: TypeName, targs: List[Type]) extends Type with NamedType +final case class Selection(base: Type, name: TypeName) extends Type final case class Neg(base: Type) extends Type 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: Type, tvBounds: Ls[TypeVar -> Bounds], where: Ls[Bounds]) extends Type +final case class Constrained(base: TypeLike, tvBounds: Ls[TypeVar -> Bounds], where: Ls[Bounds]) extends Type +// final case class FirstClassDefn(defn: NuTypeDef) extends Type // TODO +// final case class Refinement(base: Type, decls: TypingUnit) extends Type // TODO final case class Field(in: Opt[Type], out: Type) extends FieldImpl @@ -155,7 +171,7 @@ case object Bot extends NullaryType final case class Literal(lit: Lit) extends NullaryType /** Reference to an existing type with the given name. */ -final case class TypeName(name: Str) extends NullaryType with NamedType with TypeNameImpl +final case class TypeName(name: Str) extends NullaryType with NamedType with TypeNameImpl with NameRef final case class TypeTag (name: Str) extends NullaryType final case class TypeVar(val identifier: Int \/ Str, nameHint: Opt[Str]) extends NullaryType with TypeVarImpl { @@ -169,27 +185,81 @@ final case class PolyType(targs: Ls[TypeName \/ TypeVar], body: Type) extends Ty // New Definitions AST -final case class TypingUnit(entities: Ls[Statement]) extends PgrmOrTypingUnit with TypingUnitImpl +final case class TypingUnit(entities: Ls[Statement]) extends TypingUnitImpl +// final case class TypingUnit(entities: Ls[Statement]) extends TypeLike with PgrmOrTypingUnit with TypingUnitImpl + +final case class Signature(members: Ls[NuDecl], result: Opt[Type]) extends TypeLike with SignatureImpl + +sealed abstract class NuDecl extends TypeLike with Statement with NuDeclImpl -sealed abstract class NuDecl extends Statement with NuDeclImpl +sealed trait Outer { def kind: OuterKind } final case class NuTypeDef( kind: TypeDefKind, nme: TypeName, - tparams: Ls[TypeName], - params: Tup, // the specialized parameters for that type + tparams: Ls[(Opt[VarianceInfo], TypeName)], + params: Opt[Tup], // the specialized parameters for that type + ctor: Opt[Constructor], + sig: Opt[Type], parents: Ls[Term], + superAnnot: Opt[Type], + thisAnnot: Opt[Type], body: TypingUnit -) extends NuDecl with Statement +)(val declareLoc: Opt[Loc], val abstractLoc: Opt[Loc]) + extends NuDecl with Statement with Outer { + def isPlainJSClass: Bool = params.isEmpty + } final case class NuFunDef( isLetRec: Opt[Bool], // None means it's a `fun`, which is always recursive; Some means it's a `let` nme: Var, - targs: Ls[TypeName], - rhs: Term \/ PolyType, + symbolicNme: Opt[Var], + tparams: Ls[TypeName], + rhs: Term \/ Type, +)( + val declareLoc: Opt[Loc], + val virtualLoc: Opt[Loc], // Some(Loc) means that the function is modified by keyword `virtual` + val signature: Opt[NuFunDef], + val outer: Opt[Outer], + val genField: Bool ) extends NuDecl with DesugaredStatement { val body: Located = rhs.fold(identity, identity) + def kind: DeclKind = Val + val abstractLoc: Opt[Loc] = None + + // If the member has no implementation, it is virtual automatically + def isVirtual: Bool = virtualLoc.nonEmpty || rhs.isRight } +final case class Constructor(params: Tup, body: Blk) extends DesugaredStatement with ConstructorImpl // constructor(...) { ... } + + + +final case class VarianceInfo(isCovariant: Bool, isContravariant: Bool) { + + /** Combine two pieces of variance information together + */ + def &&(that: VarianceInfo): VarianceInfo = + VarianceInfo(isCovariant && that.isCovariant, isContravariant && that.isContravariant) + + /* Flip the current variance if it encounters a contravariant position + */ + def flip: VarianceInfo = VarianceInfo(isContravariant, isCovariant) + + override def toString: Str = show + + def show: Str = this match { + case (VarianceInfo(true, true)) => "±" + case (VarianceInfo(false, true)) => "-" + case (VarianceInfo(true, false)) => "+" + case (VarianceInfo(false, false)) => "=" + } +} + +object VarianceInfo { + val bi: VarianceInfo = VarianceInfo(true, true) + val co: VarianceInfo = VarianceInfo(true, false) + val contra: VarianceInfo = VarianceInfo(false, true) + val in: VarianceInfo = VarianceInfo(false, false) +} -sealed abstract class PgrmOrTypingUnit diff --git a/shared/src/main/scala/mlscript/ucs/Clause.scala b/shared/src/main/scala/mlscript/ucs/Clause.scala index d4c26b6b1d..b91354d984 100644 --- a/shared/src/main/scala/mlscript/ucs/Clause.scala +++ b/shared/src/main/scala/mlscript/ucs/Clause.scala @@ -9,11 +9,11 @@ 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. */ -abstract class Clause { +sealed abstract class Clause { /** * Local interleaved let bindings declared before this condition. */ - var bindings: Ls[(Bool, Var, Term)] = Nil + var bindings: Ls[LetBinding] = Nil /** * Locations of terms that build this `Clause`. @@ -21,52 +21,58 @@ abstract class 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( - scrutinee: Scrutinee, + override val scrutinee: Scrutinee, className: Var, fields: Ls[Str -> Var] - )(override val locations: Ls[Loc]) extends Clause + )(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 - - final case class BooleanTest(test: Term)(override val locations: Ls[Loc]) extends Clause - - def showBindings(bindings: Ls[(Bool, Var, Term)]): Str = - bindings match { - case Nil => "" - case bindings => bindings.map { - case (_, Var(name), _) => name - }.mkString("(", ", ", ")") - } - + )(override val locations: Ls[Loc]) extends Clause { + override def toString: String = s"«$scrutinee is Tuple#$arity»" + bindingsToString + } - def showClauses(clauses: Iterable[Clause]): Str = { - clauses.iterator.map { clause => - (clause match { - case Clause.BooleanTest(test) => s"«$test»" - case Clause.MatchClass(scrutinee, Var(className), fields) => - s"«$scrutinee is $className»" - case Clause.MatchTuple(scrutinee, arity, fields) => - s"«$scrutinee is Tuple#$arity»" - }) + (if (clause.bindings.isEmpty) "" else " with " + showBindings(clause.bindings)) - }.mkString("", " and ", "") + final case class BooleanTest(test: Term)( + override val locations: Ls[Loc] + ) extends Clause { + override def toString: String = s"«$test»" + bindingsToString } - def print(println: (=> Any) => Unit, conjunctions: Iterable[Conjunction -> Term]): Unit = { - println("Flattened conjunctions") - conjunctions.foreach { case Conjunction(clauses, trailingBindings) -> term => - println("+ " + showClauses(clauses) + { - (if (trailingBindings.isEmpty) "" else " ") + - showBindings(trailingBindings) + - s" => $term" - }) - } + /** + * @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/Conjunction.scala b/shared/src/main/scala/mlscript/ucs/Conjunction.scala index ff9c6a2fb3..f0928dccef 100644 --- a/shared/src/main/scala/mlscript/ucs/Conjunction.scala +++ b/shared/src/main/scala/mlscript/ucs/Conjunction.scala @@ -3,11 +3,21 @@ package mlscript.ucs import mlscript._, utils._, shorthands._ import Clause._, helpers._ import scala.collection.mutable.Buffer +import scala.annotation.tailrec /** * A `Conjunction` represents a list of `Clause`s. */ -final case class Conjunction(clauses: Ls[Clause], trailingBindings: Ls[(Bool, Var, Term)]) { +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. * @@ -44,31 +54,55 @@ final case class Conjunction(clauses: Ls[Clause], trailingBindings: Ls[(Bool, Va } } + /** + * 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: (Bool, Var, Term)): Conjunction = + def +(lastBinding: LetBinding): Conjunction = Conjunction(clauses, trailingBindings :+ lastBinding) - def separate(expectedScrutinee: Scrutinee): Opt[(MatchClass, Conjunction)] = { - def rec(past: Ls[Clause], upcoming: Ls[Clause]): Opt[(Ls[Clause], MatchClass, Ls[Clause])] = { + def 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 => N + 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) + 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) + rec(past :+ head, tail, firstAny) } } - rec(Nil, clauses).map { case (past, wanted, remaining) => + rec(Nil, clauses, None).map { case (past, wanted, remaining) => (wanted, Conjunction(past ::: remaining, trailingBindings)) } } @@ -79,7 +113,7 @@ final case class Conjunction(clauses: Ls[Clause], trailingBindings: Ls[(Bool, Va * @param interleavedLets the buffer of let bindings in the current context * @return idential to `conditions` */ - def withBindings(implicit interleavedLets: Buffer[(Bool, Var, Term)]): Conjunction = { + def withBindings(implicit interleavedLets: Buffer[LetBinding]): Conjunction = { clauses match { case Nil => Conjunction(Nil, interleavedLets.toList ::: trailingBindings) case head :: _ => diff --git a/shared/src/main/scala/mlscript/ucs/Desugarer.scala b/shared/src/main/scala/mlscript/ucs/Desugarer.scala index 0a8b7eac5f..6e49e61efc 100644 --- a/shared/src/main/scala/mlscript/ucs/Desugarer.scala +++ b/shared/src/main/scala/mlscript/ucs/Desugarer.scala @@ -1,10 +1,13 @@ package mlscript.ucs -import scala.collection.mutable.{Map => MutMap} +import scala.collection.mutable.{Map => MutMap, HashMap} import scala.collection.mutable.Buffer import mlscript._, utils._, shorthands._ import helpers._ +import Message.MessageContext +import mlscript.ucs.MutCaseOf.MutCase.Constructor +import scala.collection.mutable.ListBuffer /** * This class contains main desugaring methods. @@ -15,6 +18,7 @@ class Desugarer extends TypeDefs { self: Typer => 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]] @@ -35,6 +39,24 @@ class Desugarer extends TypeDefs { self: Typer => 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 + } + /** * * @@ -43,7 +65,7 @@ class Desugarer extends TypeDefs { self: Typer => * @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 a mapping from each field to their var + * @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]) @@ -51,9 +73,10 @@ class Desugarer extends TypeDefs { self: Typer => val subPatterns = Buffer.empty[(Var, Term)] val bindings = params.iterator.zip(positionals).flatMap { // `x is A(_)`: ignore this binding - case (Var("_"), _) => N + case (Var("_"), fieldName) => S(fieldName -> Var("_")) // `x is A(value)`: generate bindings directly - case (name: Var, fieldName) => S(fieldName -> name) + 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) => @@ -65,7 +88,7 @@ class Desugarer extends TypeDefs { self: Typer => subPatterns += ((alias, pattern)) S(fieldName -> alias) }.toList - subPatterns.toList -> bindings + (subPatterns.toList, bindings) } /** @@ -77,9 +100,9 @@ class Desugarer extends TypeDefs { self: Typer => * @return desugared conditions representing the sub-patterns */ private def destructSubPatterns(scrutinee: Scrutinee, subPatterns: Iterable[Var -> Term]) - (implicit ctx: Ctx, raise: Raise, aliasMap: FieldAliasMap): Ls[Clause] = { + (implicit ctx: Ctx, raise: Raise, exhaustivenessMap: MutExhaustivenessMap, aliasMap: FieldAliasMap): Ls[Clause] = { subPatterns.iterator.flatMap[Clause] { case (subScrutinee, subPattern) => - destructPattern(makeScrutinee(subScrutinee, scrutinee.matchRootLoc), subPattern) + destructPattern(makeScrutinee(subScrutinee, scrutinee.matchRootLoc), subPattern, false) }.toList } @@ -95,27 +118,41 @@ class Desugarer extends TypeDefs { self: Typer => * @param matchRootLoc the caller is expect to be in a match environment, * this parameter indicates the location of the match root */ - def makeScrutinee(term: Term, matchRootLoc: Opt[Loc])(implicit ctx: Ctx): Scrutinee = + private def makeScrutinee(term: Term, matchRootLoc: Opt[Loc])(implicit ctx: Ctx): Scrutinee = traceUCS(s"Making a scrutinee for `$term`") { term match { - case _: SimpleTerm => Scrutinee(N, term)(matchRootLoc) + case _: Var => + printlnUCS(s"The scrutinee does not need an alias.") + Scrutinee(N, term)(matchRootLoc) case _ => - val localName = if (localizedScrutineeMap.containsKey(term)) { - localizedScrutineeMap.get(term) - } else { - val v = Var(freshName).desugaredFrom(term) - localizedScrutineeMap.put(term, v) - v - } - Scrutinee(S(localName), term)(matchRootLoc) + 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 @@ -126,9 +163,10 @@ class Desugarer extends TypeDefs { self: Typer => * do not contain interleaved let bindings. */ private def destructPattern - (scrutinee: Scrutinee, pattern: Term) + (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") { @@ -139,6 +177,7 @@ class Desugarer extends TypeDefs { self: Typer => 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, @@ -148,43 +187,79 @@ class Desugarer extends TypeDefs { self: Typer => 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("true") | Var("false") | _: Lit) => - val test = mkBinOp(scrutinee.reference, Var("=="), literal) - val clause = Clause.BooleanTest(test)(scrutinee.term.toLoc.toList ::: literal.toLoc.toList) + case literal: Var if literal.name === "true" || literal.name === "false" => + 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) match { - case N => throw new DesugaringException({ - import Message.MessageContext - msg"Cannot find the constructor `$className` in the context" + 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) - case S(_) => - printlnUCS(s"Build a Clause.MatchClass from $scrutinee where pattern is $classNameVar") - Clause.MatchClass(scrutinee, classNameVar, Nil)(collectLocations(scrutinee.term)) :: Nil } + 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) match { + 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({ - import Message.MessageContext - msg"Cannot find class `$className` in the context" + msg"Cannot find class `$className` in scope" }, classNameVar.toLoc) - case S(td) => - if (args.length === td.positionals.length) { + case S((kind, positionals)) => + if (args.length === positionals.length) { val (subPatterns, bindings) = desugarPositionals( scrutinee, args.iterator.map(_._2.value), - td.positionals + 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") @@ -192,10 +267,9 @@ class Desugarer extends TypeDefs { self: Typer => clause :: destructSubPatterns(scrutinee, subPatterns) } else { throw new DesugaringException({ - import Message.MessageContext - val expected = td.positionals.length + val expected = positionals.length val actual = args.length - msg"${td.kind.str} $className expects ${expected.toString} ${ + msg"${kind.str} $className expects ${expected.toString} ${ "parameter".pluralize(expected) } but found ${args.length.toString} ${ "parameter".pluralize(expected) @@ -208,29 +282,28 @@ class Desugarer extends TypeDefs { self: Typer => case app @ App( App( opVar @ Var(op), - Tup((_ -> Fld(_, _, lhs)) :: Nil) + Tup((_ -> Fld(_, lhs)) :: Nil) ), - Tup((_ -> Fld(_, _, rhs)) :: Nil) + Tup((_ -> Fld(_, rhs)) :: Nil) ) => ctx.tyDefs.get(op) match { case N => throw new DesugaringException({ - import Message.MessageContext msg"Cannot find operator `$op` in the context" }, opVar.toLoc) case S(td) if td.positionals.length === 2 => - val (subPatterns, bindings) = desugarPositionals( + val (subPatterns, fields) = desugarPositionals( scrutinee, lhs :: rhs :: Nil, td.positionals ) - val clause = Clause.MatchClass(scrutinee, opVar, bindings)(collectLocations(scrutinee.term)) + 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({ - import Message.MessageContext val expected = td.positionals.length msg"${td.kind.str} `$op` expects ${expected.toString} ${ "parameter".pluralize(expected) @@ -244,9 +317,9 @@ class Desugarer extends TypeDefs { self: Typer => // x is Cons((x, y), Nil) case tuple: Tup => desugarTuplePattern(tuple) // What else? - case _ => throw new Exception(s"illegal pattern: ${mlscript.codegen.Helpers.inspect(pattern)}") + case _ => throw new DesugaringException(msg"illegal pattern", pattern.toLoc) } - }("[Desugarer.destructPattern] result: " + Clause.showClauses(_)) + }("[Desugarer.destructPattern] Result: " + _.mkString(", ")) /** * Collect `Loc`s from a synthetic term. @@ -255,7 +328,7 @@ class Desugarer extends TypeDefs { self: Typer => * @param fragments the fragment terms * @return all original locations */ - def collectLocations(term: Term)(implicit fragments: Ls[Term]): Ls[Loc] = { + 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 @@ -263,11 +336,72 @@ class Desugarer extends TypeDefs { self: Typer => 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})") - def desugarIf + /** + * 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]") { + 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) - : Ls[Conjunction -> 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 @@ -285,13 +419,18 @@ class Desugarer extends TypeDefs { self: Typer => ts.flatMap { case isApp @ App( App(Var("is"), - Tup(_ -> Fld(_, _, scrutinee) :: Nil)), - Tup(_ -> Fld(_, _, pattern) :: Nil) - ) => + 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)(ctx, raise, scrutineeFieldAliasMap) + 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) @@ -313,30 +452,32 @@ class Desugarer extends TypeDefs { self: Typer => body: IfBody \/ Statement, partialPattern: PartialTerm, collectedConditions: Conjunction, - )(implicit interleavedLets: Buffer[(Bool, Var, Term)]): Unit = + )(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(IfElse(consequent)) => + 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(Var("_"), consequent)) => + 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) - val clauses = destructPattern(scrutinee, partialPattern.addTerm(patternPart).term) + 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: " + Clause.showClauses(conditions.clauses)) + printlnUCS(s"Result: " + conditions.clauses.mkString(", ")) extraTestOpt match { // Case 1. Just a pattern. Easy! case N => @@ -353,19 +494,19 @@ class Desugarer extends TypeDefs { self: Typer => // B(...) then ... // B(...) then ... case L(IfOpApp(patLhs, Var("and"), consequent)) => - val (pattern, optTests) = separatePattern(patLhs) - val patternConditions = destructPattern(scrutinee, pattern) + 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) match { + 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) + val patternConditions = destructPattern(scrutinee, pattern, true) val extraConditions = desugarConditions(splitAnd(extraTests)) val conditions = collectedConditions + Conjunction(patternConditions ::: extraConditions, Nil).withBindings @@ -377,17 +518,17 @@ class Desugarer extends TypeDefs { self: Typer => // 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), collectedConditions) + desugarMatchBranch(scrutinee, L(consequent), partialPattern.addTermOp(patternPart, op, newDefs), collectedConditions) } case L(IfOpsApp(patLhs, opsRhss)) => - separatePattern(patLhs) match { + separatePattern(patLhs, newDefs) match { case (patternPart, N) => - val partialPattern2 = partialPattern.addTerm(patternPart) + 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).term) + val patternConditions = destructPattern(scrutinee, partialPattern.addTerm(patternPart, newDefs).term, true) val testTerms = splitAnd(extraTests) val middleConditions = desugarConditions(testTerms.init) val conditions = @@ -406,21 +547,22 @@ class Desugarer extends TypeDefs { self: Typer => 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 += ((isRec, nameVar, term)) + 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({ - import Message.MessageContext msg"Illegal interleaved statement ${statement.toString}" }, statement.toLoc) } + }(_ => "[desugarMatchBranch]") + def desugarIfBody (body: IfBody, expr: PartialTerm, acc: Conjunction) - (implicit interleavedLets: Buffer[(Bool, Var, Term)]) - : Unit = { + (implicit interleavedLets: Buffer[LetBinding]) + : Unit = traceUCS[Unit]("[desugarIfBody]") { body match { case IfOpsApp(exprPart, opsRhss) => - val exprStart = expr.addTerm(exprPart) + val exprStart = expr.addTerm(exprPart, newDefs) opsRhss.foreach { case opVar -> contBody => desugarIfBody(contBody, exprStart.addOp(opVar), acc) } @@ -428,7 +570,7 @@ class Desugarer extends TypeDefs { self: Typer => branches += (acc -> consequent) // The termination case. case IfThen(term, consequent) => - val totalTerm = expr.addTerm(term) + val totalTerm = expr.addTerm(term, newDefs) // “Atomic” means terms that do not contain `and`. val atomicTerms = splitAnd(totalTerm.term) val fragments = atomicTerms ::: totalTerm.fragments @@ -438,7 +580,7 @@ class Desugarer extends TypeDefs { self: Typer => 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).term + 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 { @@ -451,20 +593,21 @@ class Desugarer extends TypeDefs { self: Typer => case S(alias) => acc case N => acc } - // Create a buffer for interleaved let bindings. - val interleavedLets = Buffer.empty[(Bool, Var, Term)] + // 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)(interleavedLets) + 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).term :: Nil)) + 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), acc) + 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") @@ -474,45 +617,57 @@ class Desugarer extends TypeDefs { self: Typer => case IfBlock(lines) => lines.foreach { case L(subBody) => desugarIfBody(subBody, expr, acc) - case R(NuFunDef(S(isRec), nameVar, _, L(term))) => - interleavedLets += ((isRec, nameVar, term)) + 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[(Bool, Var, Term)] + 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 -> _ } - Clause.print(printlnUCS, branches) + 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} /** - * A map from each scrutinee term to all its cases and the first `MutCase`. + * 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 */ - type ExhaustivenessMap = Map[Str \/ Int, Map[Var, MutCase]] - - def getScurtineeKey(scrutinee: Scrutinee)(implicit ctx: Ctx, raise: Raise): Str \/ Int = { - scrutinee.term match { - // The original scrutinee is an reference. - case v @ Var(name) => - ctx.env.get(name) match { - case S(VarSymbol(_, defVar)) => defVar.uid.fold[Str \/ Int](L(v.name))(R(_)) - case S(_) | N => L(v.name) - } - // Otherwise, the scrutinee has a temporary name. - case _ => - scrutinee.local match { - case N => throw new Error("check your `makeScrutinee`") - case S(localNameVar) => L(localNameVar.name) - } - } - } + 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`. @@ -521,20 +676,20 @@ class Desugarer extends TypeDefs { self: Typer => * @param parentOpt the parent `MutCaseOf` * @param scrutineePatternMap the exhaustiveness map */ - def checkExhaustive + private def checkExhaustive (t: MutCaseOf, parentOpt: Opt[MutCaseOf]) - (implicit scrutineePatternMap: ExhaustivenessMap, ctx: Ctx, raise: Raise) - : Unit = { - printlnUCS(s"Check exhaustiveness of ${t.describe}") - indent += 1 - try t match { + (implicit ctx: Ctx, + raise: Raise, + exhaustivenessMap: ExhaustivenessMap, + subClassMap: SubClassMap) + : Unit = traceUCS(s"[checkExhaustive] ${t.describe}") { + t match { case _: Consequent => () case MissingCase => - import Message.MessageContext 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.toString}", test.toLoc) + 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(_, _, _)) => @@ -542,76 +697,344 @@ class Desugarer extends TypeDefs { self: Typer => case S(Consequent(_)) | S(MissingCase) | N => die // unreachable } case IfThenElse(condition, whenTrue, whenFalse) => - checkExhaustive(whenTrue, S(t)) checkExhaustive(whenFalse, S(t)) + checkExhaustive(whenTrue, S(t)) case Match(scrutinee, branches, default) => - scrutineePatternMap.get(getScurtineeKey(scrutinee)) match { + 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 ${scrutineePatternMap}") + 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.foreach { case (key, mutCase) => printlnUCS(s"- $key => $mutCase")} + 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(classNameVar -> _, _) => classNameVar - }) - printlnUCS(s"Number of missing cases: ${missingCases.size}") + 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({ - import Message.MessageContext val numMissingCases = missingCases.size (msg"The match is not exhaustive." -> scrutinee.matchRootLoc) :: (msg"The scrutinee at this position misses ${numMissingCases.toString} ${ "case".pluralize(numMissingCases) }." -> scrutinee.term.toLoc) :: - missingCases.iterator.zipWithIndex.flatMap { case ((classNameVar, firstMutCase), index) => + 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 `${classNameVar.name}`" -> N) :: - firstMutCase.locations.iterator.zipWithIndex.map { case (loc, index) => - (if (index === 0) msg"It first appears here." else msg"continued at") -> S(loc) + (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 { case MutCase(_, consequent) => - checkExhaustive(consequent, S(t)) + branches.foreach { branch => + checkExhaustive(branch.consequent, S(t)) } - } finally indent -= 1 - } + } + }(_ => 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) + } + } - def summarizePatterns(t: MutCaseOf)(implicit ctx: Ctx, raise: Raise): ExhaustivenessMap = { - val m = MutMap.empty[Str \/ Int, MutMap[Var, MutCase]] - def rec(t: MutCaseOf): Unit = { - printlnUCS(s"Summarize pattern of ${t.describe}") - indent += 1 - try t match { - case Consequent(term) => () - case MissingCase => () - case IfThenElse(_, whenTrue, whenFalse) => - rec(whenTrue) - rec(whenFalse) - case Match(scrutinee, branches, _) => - val key = getScurtineeKey(scrutinee) - branches.foreach { mutCase => - val patternMap = m.getOrElseUpdate( key, MutMap.empty) - if (!patternMap.contains(mutCase.patternFields._1)) { - patternMap += ((mutCase.patternFields._1, mutCase)) + App(Lam(Tup( + 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 + ), 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)) + ) :: Nil) + ) + case _ => mkLetFromFields(scrutinee, fields.filter(_._2.name =/= "_").toList, consequent) + } + Case(className, body, rec2(next)) + case MutCase.Literal(literal, cases) :: next => + printlnUCS(s"• Literal pattern: $literal") + Case(literal, rec(cases), rec2(next)) + 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)) } - rec(mutCase.consequent) } - } finally indent -= 1 + 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) + 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 } - rec(t) - printlnUCS("Exhaustiveness map") - m.foreach { case (scrutinee, patterns) => - printlnUCS(s"- $scrutinee => " + patterns.keys.mkString(", ")) + }(_ => "[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)) } - Map.from(m.iterator.map { case (key, patternMap) => key -> Map.from(patternMap) }) + + 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/LetBinding.scala b/shared/src/main/scala/mlscript/ucs/LetBinding.scala new file mode 100644 index 0000000000..6e08b3ce2d --- /dev/null +++ b/shared/src/main/scala/mlscript/ucs/LetBinding.scala @@ -0,0 +1,46 @@ +package mlscript.ucs + +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/MutCaseOf.scala b/shared/src/main/scala/mlscript/ucs/MutCaseOf.scala index 37d9084999..8472e8b319 100644 --- a/shared/src/main/scala/mlscript/ucs/MutCaseOf.scala +++ b/shared/src/main/scala/mlscript/ucs/MutCaseOf.scala @@ -8,27 +8,9 @@ import scala.collection.mutable.{Map => MutMap, Set => MutSet, Buffer} import helpers._ import mlscript.ucs.MutCaseOf.Consequent - -trait WithBindings { this: MutCaseOf => - private val bindingsSet: MutSet[(Bool, Var, Term)] = MutSet.empty - private val bindings: Buffer[(Bool, Var, Term)] = Buffer.empty - - def addBindings(newBindings: IterableOnce[(Bool, Var, Term)]): Unit = { - newBindings.iterator.foreach { - case binding if bindingsSet.contains(binding) => () - case binding => - bindingsSet += binding - bindings += binding - } - } - - def getBindings: Iterable[(Bool, Var, Term)] = bindings - - def withBindings(newBindings: IterableOnce[(Bool, Var, Term)]): MutCaseOf = { - addBindings(newBindings) - this - } -} +import scala.collection.immutable +import Desugarer.{ExhaustivenessMap, SuperClassMap} +import mlscript.ucs.Clause.MatchAny sealed abstract class MutCaseOf extends WithBindings { def kind: Str = { @@ -41,85 +23,115 @@ sealed abstract class MutCaseOf extends WithBindings { } } + 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): Unit + (implicit raise: Diagnostic => Unit, + getScrutineeKey: Scrutinee => Str \/ Int, + exhaustivenessMap: ExhaustivenessMap, + superClassMap: SuperClassMap): Unit + def mergeDefault - (bindings: Ls[(Bool, Var, Term)], default: Term) - (implicit raise: Diagnostic => Unit): Unit - def toTerm(defs: Set[Var]): Term + (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 toTerm(t: MutCaseOf): Term = { - val term = t.toTerm(Set.from(t.getBindings.iterator.map(_._2))) - mkBindings(t.getBindings.toList, term, Set.empty) - } - def showScrutinee(scrutinee: Scrutinee): Str = - s"«${scrutinee.term}»" + (scrutinee.local match { + 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, leading: String): Unit = { + def rec(t: MutCaseOf, indent: Int): Unit = { val baseIndent = " " * indent - val bindingNames = t.getBindings match { - case Nil => "" - case bindings => bindings.iterator.map(_._2.name).mkString("[", ", ", "] ") - } + 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". - lines += baseIndent + leading + bindingNames + s"if «$condition»" - rec(whenTrue, indent + 1, "") + 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${leading}else" - rec(whenFalse, indent + 1, "") + lines += s"${baseIndent}else" + rec(whenFalse, indent + 1) case Match(scrutinee, branches, default) => - lines += baseIndent + leading + bindingNames + showScrutinee(scrutinee) + " match" - branches.foreach { case MutCase(Var(className) -> fields, consequent) => - lines += s"$baseIndent case $className =>" - fields.foreach { case (field, Var(alias)) => - lines += s"$baseIndent let $alias = .$field" - } - rec(consequent, indent + 2, "") + 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, "") + rec(consequent, indent + 2) } case Consequent(term) => - lines += s"$baseIndent$leading$bindingNames«$term»" + bindingLines.foreach { lines += baseIndent + _ } + lines += s"$baseIndent«${term.showDbg}»" case MissingCase => - lines += s"$baseIndent$leading$bindingNames" + bindingLines.foreach { lines += baseIndent + _ } + lines += s"$baseIndent" } } - rec(t, 0, "") + rec(t, 0) lines.toList } - /** - * MutCase is a _mutable_ representation of a case in `MutCaseOf.Match`. - * - * @param patternFields the alias to the fields - * @param consequent the consequential `MutCaseOf` - */ - final case class MutCase( - val patternFields: Var -> Buffer[Str -> Var], - var consequent: MutCaseOf, - ) { - def matches(expected: Var): Bool = matches(expected.name) - def matches(expected: Str): Bool = patternFields._1.name === expected - def addFields(fields: Iterable[Str -> Var]): Unit = - patternFields._2 ++= fields.iterator.filter(!patternFields._2.contains(_)) + sealed abstract class MutCase { + var consequent: MutCaseOf + + @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 // ====== @@ -142,44 +154,107 @@ object MutCaseOf { locations ++= locOpt this } - def withLocations(locs: Ls[Loc]): MutCase = { + def withLocations(locs: IterableOnce[Loc]): MutCase = { locations ++= locs this } } - import Clause.{MatchClass, MatchTuple, BooleanTest} + 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, whenTrue = ${whenTrue.kind}, whenFalse = ${whenFalse.kind})" + 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 merge(branch: Conjunction -> Term)(implicit raise: Diagnostic => Unit): Unit = + 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 => - this.mergeDefault(trailingBindings, term) - // The CC is an if-then-else. We create a pattern match of true/false. - case Conjunction((head @ BooleanTest(test)) :: tail, trailingBindings) -> term => - // If the test is the same. So, we merge. - if (test === condition) { - whenTrue.addBindings(head.bindings) - whenTrue.merge(Conjunction(tail, trailingBindings) -> term) - } else { - whenFalse match { - case Consequent(_) => - raise(WarningReport(Message.fromStr("duplicated else in the if-then-else") -> N :: Nil)) - case MissingCase => - whenFalse = buildFirst(branch._1, branch._2) - whenFalse.addBindings(head.bindings) - case _ => whenFalse.merge(branch) - } + 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)) + 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) @@ -187,28 +262,21 @@ object MutCaseOf { } } - def mergeDefault(bindings: Ls[(Bool, Var, Term)], default: Term)(implicit raise: Diagnostic => Unit): Unit = { - whenTrue.mergeDefault(bindings, default) - whenFalse match { - case Consequent(term) => - import Message.MessageContext - raise(WarningReport( - msg"Found a duplicated else branch" -> default.toLoc :: - (msg"The first else branch was declared here." -> term.toLoc) :: - Nil)) - case MissingCase => - whenFalse = Consequent(default).withBindings(bindings) - case _: IfThenElse | _: Match => whenFalse.mergeDefault(bindings, default) + 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) + } } } - - def toTerm(defs: Set[Var]): Term = { - val falseBody = mkBindings(whenFalse.getBindings.toList, whenFalse.toTerm(defs ++ whenFalse.getBindings.iterator.map(_._2)), defs) - val trueBody = mkBindings(whenTrue.getBindings.toList, whenTrue.toTerm(defs ++ whenTrue.getBindings.iterator.map(_._2)), defs) - val falseBranch = Wildcard(falseBody) - val trueBranch = Case(Var("true"), trueBody, falseBranch) - CaseOf(condition, trueBranch) - } } final case class Match( scrutinee: Scrutinee, @@ -222,31 +290,86 @@ object MutCaseOf { })" } - def merge(branch: Conjunction -> Term)(implicit raise: Diagnostic => Unit): Unit = { - branch._1.separate(scrutinee) match { + 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(_.matches(tupleClassName)) match { + branches.find(_.covers(tupleClassName)) match { // No such pattern. We should create a new one. - case N => + case N | S(MutCase.Literal(_, _)) => val newBranch = buildFirst(Conjunction(tail, trailingBindings), term) newBranch.addBindings(head.bindings) - branches += MutCase(tupleClassName -> Buffer.from(fields), newBranch) + branches += MutCase.Constructor(tupleClassName -> Buffer.from(fields), newBranch) .withLocations(head.locations) // Found existing pattern. - case S(branch) => + case S(branch: MutCase.Constructor) => branch.consequent.addBindings(head.bindings) branch.addFields(fields) branch.consequent.merge(Conjunction(tail, trailingBindings) -> term) } // A wild card case. We should propagate wildcard to every default positions. - case Conjunction(Nil, trailingBindings) -> term => mergeDefault(trailingBindings, term) + case Conjunction(Nil, trailingBindings) -> term => + 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)) @@ -255,114 +378,213 @@ object MutCaseOf { } } // Found a match condition against the same scrutinee - case S((head @ MatchClass(_, className, fields), remainingConditions)) => - branches.find(_.matches(className)) match { + 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 => - val newBranch = buildFirst(remainingConditions, branch._2) - newBranch.addBindings(head.bindings) - branches += MutCase(className -> Buffer.from(fields), newBranch) + case N | S(MutCase.Constructor(_, _)) => + val newConsequent = buildFirst(remainingConditions, branch._2) + newConsequent.addBindings(head.bindings) + branches += MutCase.Literal(literal, newConsequent) .withLocations(head.locations) - // Found existing pattern. - case S(matchCase) => + case S(matchCase: MutCase.Literal) => // Merge interleaved bindings. matchCase.consequent.addBindings(head.bindings) - matchCase.addFields(fields) 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[(Bool, Var, Term)], default: Term)(implicit raise: Diagnostic => Unit): Unit = { - branches.foreach { - case MutCase(_, consequent) => consequent.mergeDefault(bindings, default) - } - wildcard match { - case N => wildcard = S(Consequent(default).withBindings(bindings)) - case S(consequent) => consequent.mergeDefault(bindings, default) - } - } - - def toTerm(defs: Set[Var]): Term = { - def rec(xs: Ls[MutCase]): CaseBranches = - xs match { - case MutCase(className -> fields, cases) :: next => - // TODO: expand bindings here - val consequent = cases.toTerm(defs ++ fields.iterator.map(_._2)) - Case(className, mkLetFromFields(scrutinee, fields.toList, consequent), rec(next)) - case Nil => - wildcard.fold[CaseBranches](NoCases)(_.toTerm(defs) |> Wildcard) + 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) } - val cases = rec(branches.toList) - val resultTerm = scrutinee.local match { - case N => CaseOf(scrutinee.term, cases) - case S(aliasVar) => Let(false, aliasVar, scrutinee.term, CaseOf(aliasVar, cases)) } - // Collect let bindings from case branches. - val bindings = branches.iterator.flatMap(_.consequent.getBindings).toList - mkBindings(bindings, resultTerm, defs) } } final case class Consequent(term: Term) extends MutCaseOf { - def describe: Str = s"Consequent($term)" - - def merge(branch: Conjunction -> Term)(implicit raise: Diagnostic => Unit): Unit = - raise(WarningReport(Message.fromStr("duplicated branch") -> N :: Nil)) - - def mergeDefault(bindings: Ls[(Bool, Var, Term)], default: Term)(implicit raise: Diagnostic => Unit): Unit = () + 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 toTerm(defs: Set[Var]): Term = term + 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" - def merge(branch: Conjunction -> Term)(implicit raise: Diagnostic => Unit): Unit = - lastWords("`MissingCase` is a placeholder and cannot be merged") + override def duplicate() = MissingCase - def mergeDefault(bindings: Ls[(Bool, Var, Term)], default: Term)(implicit raise: Diagnostic => Unit): Unit = () + override def fill(subTree: MutCaseOf): Unit = () - def toTerm(defs: Set[Var]): Term = { - import Message.MessageContext - throw new DesugaringException(msg"missing a default branch", N) - } + 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 } - private def buildFirst(conjunction: Conjunction, term: Term): MutCaseOf = { + 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) => - val realTail = Conjunction(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 BooleanTest(test) => IfThenElse(test, rec(realTail), MissingCase) + 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(className -> Buffer.from(fields), rec(realTail)) + val branches = Buffer[MutCase]( + MutCase.Constructor(className -> Buffer.from(fields), consequentTree.withBindings(afterHeadBindings)) .withLocations(head.locations) ) - Match(scrutinee, branches, N) + Match(scrutinee, branches, N).withBindings(beforeHeadBindings) case MatchTuple(scrutinee, arity, fields) => - val branches = Buffer( - MutCase(Var(s"Tuple#$arity") -> Buffer.from(fields), rec(realTail)) + val branches = Buffer[MutCase]( + MutCase.Constructor(Var(s"Tuple#$arity") -> Buffer.from(fields), consequentTree.withBindings(afterHeadBindings)) .withLocations(head.locations) ) - Match(scrutinee, branches, N) - }).withBindings(head.bindings) + 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) } - - def build - (cnf: Ls[Conjunction -> Term]) - (implicit raise: Diagnostic => Unit) - : MutCaseOf = { - cnf match { - case Nil => MissingCase - case (conditions -> term) :: next => - val root = MutCaseOf.buildFirst(conditions, term) - next.foreach(root.merge(_)) - root - } - } } diff --git a/shared/src/main/scala/mlscript/ucs/PartialTerm.scala b/shared/src/main/scala/mlscript/ucs/PartialTerm.scala index 3726d7a56a..7ce2d6a001 100644 --- a/shared/src/main/scala/mlscript/ucs/PartialTerm.scala +++ b/shared/src/main/scala/mlscript/ucs/PartialTerm.scala @@ -15,33 +15,33 @@ class PartialTermError(term: PartialTerm, message: Str) extends Error(message) */ sealed abstract class PartialTerm { val fragments: Ls[Term] - def addTerm(term: Term): PartialTerm.Total + def addTerm(term: Term, newDefs: Bool): PartialTerm.Total def addOp(op: Var): PartialTerm.Half - def addTermOp(term: Term, op: Var): PartialTerm.Half = - this.addTerm(term).addOp(op) + def addTermOp(term: Term, op: Var, newDefs: Bool): PartialTerm.Half = + this.addTerm(term, newDefs).addOp(op) } object PartialTerm { final case object Empty extends PartialTerm { val fragments: Ls[Term] = Nil - def addTerm(term: Term): Total = Total(term, 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): Total = + 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): Total = { - val (realRhs, extraExprOpt) = separatePattern(rhs) - val leftmost = mkBinOp(lhs, op, realRhs) + 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), extraExpr :: fragments) + case S(extraExpr) => Total(mkBinOp(leftmost, Var("and"), extraExpr, newDefs), extraExpr :: fragments) } } def addOp(op: Var): Half = diff --git a/shared/src/main/scala/mlscript/ucs/Scrutinee.scala b/shared/src/main/scala/mlscript/ucs/Scrutinee.scala index 48a447ee51..899c5629ac 100644 --- a/shared/src/main/scala/mlscript/ucs/Scrutinee.scala +++ b/shared/src/main/scala/mlscript/ucs/Scrutinee.scala @@ -6,7 +6,7 @@ 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(local: Opt[Var], term: Term)(val matchRootLoc: Opt[Loc]) { +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") @@ -18,11 +18,13 @@ final case class Scrutinee(local: Opt[Var], term: Term)(val matchRootLoc: Opt[Lo * * @return `Some` if the scrutinee is localized, otherwise, `None`. */ - def asBinding: Opt[(Bool, Var, Term)] = local.map((false, _, term)) + 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" + }) + s"${term.showDbg}" } diff --git a/shared/src/main/scala/mlscript/ucs/helpers.scala b/shared/src/main/scala/mlscript/ucs/helpers.scala index 0b20c070fb..0babee09b4 100644 --- a/shared/src/main/scala/mlscript/ucs/helpers.scala +++ b/shared/src/main/scala/mlscript/ucs/helpers.scala @@ -16,7 +16,7 @@ object helpers { * @param t the sole element * @return a tuple term with the only element */ - def mkMonuple(t: Term): Tup = Tup(N -> Fld(false, false, t) :: Nil) + def mkMonuple(t: Term): Tup = Tup(N -> Fld(FldFlags.empty, t) :: Nil) /** * Make a binary operation. @@ -26,8 +26,9 @@ object helpers { * @param rhs the right-hand side term * @return something like `App(App(op, lhs), rhs)` */ - def mkBinOp(lhs: Term, op: Var, rhs: Term): Term = - App(App(op, mkMonuple(lhs)), mkMonuple(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 joined by `and` into a list of terms. @@ -39,9 +40,11 @@ object helpers { t match { case App( App(Var("and"), - Tup((_ -> Fld(_, _, lhs)) :: Nil)), - Tup((_ -> Fld(_, _, rhs)) :: Nil) - ) => + 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 } @@ -64,16 +67,21 @@ object helpers { * @return a tuple, whose the first element is the pattern and the second * element is the extra test */ - def separatePattern(term: Term): (Term, Opt[Term]) = + 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) - ) => - separatePattern(lhs) match { + 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))) + case (pattern, S(lshRhs)) => (pattern, S(mkBinOp(lshRhs, and, rhs, newDefs))) } case _ => (term, N) } @@ -84,34 +92,17 @@ object helpers { * @param bindings a list of bindings, * @param body the final body */ - def mkBindings(bindings: Ls[(Bool, Var, Term)], body: Term, defs: Set[Var]): Term = { - def rec(bindings: Ls[(Bool, Var, Term)], defs: Set[Var]): Term = + 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 (head @ (isRec, nameVar, value)) :: tail => - if (defs.contains(head._2)) { + case LetBinding(_, isRec, nameVar, value) :: tail => + if (defs.contains(nameVar)) { rec(tail, defs) } else { - Let(isRec, nameVar, value, rec(tail, defs + head._2)) + Let(isRec, nameVar, value, rec(tail, defs + nameVar)) } } rec(bindings, defs) } - - /** - * 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 - */ - def mkLetFromFields(scrutinee: Scrutinee, fields: Ls[Str -> Var], body: Term): Term = { - def rec(fields: Ls[Str -> Var]): Term = - fields match { - case Nil => body - case (field -> (aliasVar @ Var(alias))) :: tail => - Let(false, aliasVar, Sel(scrutinee.reference, Var(field)).desugaredFrom(scrutinee.term), rec(tail)) - } - rec(fields) - } } diff --git a/shared/src/main/scala/mlscript/utils/Identity.scala b/shared/src/main/scala/mlscript/utils/Identity.scala index 69e87a6aa3..85dbede27d 100644 --- a/shared/src/main/scala/mlscript/utils/Identity.scala +++ b/shared/src/main/scala/mlscript/utils/Identity.scala @@ -4,7 +4,7 @@ package mlscript.utils class Identity[T <: AnyRef](val value: T) { override def equals(other: Any): Boolean = other match { - case that: Identity[_] => that.value is this.value + case that: Identity[_] => (that.value: Any) is this.value case _ => false } diff --git a/shared/src/main/scala/mlscript/utils/package.scala b/shared/src/main/scala/mlscript/utils/package.scala index 9bf6486056..212f8cac8c 100644 --- a/shared/src/main/scala/mlscript/utils/package.scala +++ b/shared/src/main/scala/mlscript/utils/package.scala @@ -12,12 +12,14 @@ package object utils { "org.wartremover.warts.Equals", "org.wartremover.warts.AsInstanceOf")) implicit final class AnyOps[A](self: A) { - def ===(other: A): Boolean = self == other - def =/=(other: A): Boolean = self != other - def is(other: AnyRef): Boolean = self.asInstanceOf[AnyRef] eq other - def isnt(other: AnyRef): Boolean = !(self.asInstanceOf[AnyRef] eq other) + def ===(other: A): Bool = self == other + def =/=(other: A): Bool = self != other + def is(other: A): Bool = self.asInstanceOf[AnyRef] eq other.asInstanceOf[AnyRef] + def isnt(other: AnyRef): Bool = !(self.asInstanceOf[AnyRef] eq other) /** An alternative to === when in ScalaTest, which shadows our === */ - def =:=(other: A): Boolean = self == other + def =:=(other: A): Bool = self == other + def in(xs: A => Bool): Bool = xs(self) + def in(xs: Seq[_ >: A]): Bool = xs.exists(_ === self) } implicit class StringOps(private val self: String) extends AnyVal { @@ -30,6 +32,10 @@ package object utils { def mapLines(f: String => String): String = splitSane('\n') map f mkString "\n" def indent(pre: String): String = mapLines(pre + _) def indent: String = indent("\t") + def indentNewLines(pre: String = "\t"): String = splitSane('\n').toList match { + case head :: (rest @ _ :: _) => head + "\n" + rest.map(pre + _).mkString("\n") + case _ => self + } def truncate(maxChars: Int, replace: String): String = { val newStr = self.take(maxChars) if (newStr.length < self.length) newStr + replace @@ -48,8 +54,12 @@ package object utils { implicit class IterableOps[A](private val self: IterableOnce[A]) extends AnyVal { def mkStringOr( sep: String = "", start: String = "", end: String = "", els: String = "" - ): String = - if (self.iterator.nonEmpty) self.iterator.mkString(start, sep, end) else els + ): String = { + val ite = self.iterator + if (ite.nonEmpty) ite.mkString(start, sep, end) else els + } + def lnIndent(pre: String = "\t"): Str = + self.iterator.map("\n" + _.toString.indent(pre)).mkString def collectLast[B](f: Paf[A, B]): Opt[B] = self.iterator.collect(f).foldLeft[Opt[B]](N)((_, a) => S(a)) def toSortedSet(implicit ord: Ordering[A]): SortedSet[A] = SortedSet.from(self) @@ -62,6 +72,8 @@ package object utils { def mapValues[C](f: B => C): List[A -> C] = mapValuesIter(f).toList def mapKeysIter[C](f: A => C): Iterator[C -> B] = self.iterator.map(p => f(p._1) -> p._2) def mapValuesIter[C](f: B => C): Iterator[A -> C] = self.iterator.map(p => p._1 -> f(p._2)) + def keys: Iterator[A] = self.iterator.map(_._1) + def values: Iterator[B] = self.iterator.map(_._2) def toSortedMap(implicit ord: Ordering[A]): SortedMap[A, B] = SortedMap.from(self) } @@ -195,6 +207,7 @@ package object utils { def TODO(msg: String): Nothing = throw new NotImplementedError(msg) def die: Nothing = lastWords("Program reached and unexpected state.") def lastWords(msg: String): Nothing = throw new Exception(s"Internal Error: $msg") + def wat(msg: String, wat: Any): Nothing = lastWords(s"$msg ($wat)") /** To make Scala unexhaustivity warnings believed to be spurious go away, * while clearly indicating the intent. */ diff --git a/shared/src/main/scala/mlscript/utils/shorthands.scala b/shared/src/main/scala/mlscript/utils/shorthands.scala index e84ddb954c..613bfe1a47 100644 --- a/shared/src/main/scala/mlscript/utils/shorthands.scala +++ b/shared/src/main/scala/mlscript/utils/shorthands.scala @@ -28,6 +28,8 @@ object shorthands { def some[A]: A => Option[A] = Some(_) def none[A]: Option[A] = None + def nil[A]: List[A] = Nil + type Paf[-A,+B] = PartialFunction[A,B] type Exc = Exception diff --git a/shared/src/test/diff/basics/Blocks.fun b/shared/src/test/diff/basics/Blocks.fun index 081e70fabc..059b5e80de 100644 --- a/shared/src/test/diff/basics/Blocks.fun +++ b/shared/src/test/diff/basics/Blocks.fun @@ -42,9 +42,9 @@ foo / foo / :p discard / foo 1 -//│ Parsed: discard (foo {1}); -//│ Desugared: discard (foo {1}) -//│ AST: App(Var(discard), App(Var(foo), Blk(...))) +//│ Parsed: discard(...(foo(...{1}))); +//│ Desugared: discard(...(foo(...{1}))) +//│ AST: App(Var(discard),App(Var(foo),Blk(List(IntLit(1))))) :e discard foo @@ -79,9 +79,9 @@ foo :p id id id -//│ Parsed: id id {id}; -//│ Desugared: id id {id} -//│ AST: App(App(Var(id), Var(id)), Blk(...)) +//│ Parsed: id(...id)(...{id}); +//│ Desugared: id(...id)(...{id}) +//│ AST: App(App(Var(id),Var(id)),Blk(List(Var(id)))) //│ res: 'a -> 'a :p @@ -89,27 +89,27 @@ id id id id id id 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(...)) +//│ 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(List(App(App(App(Var(id),Var(id)),Var(id)),Blk(List(App(App(App(Var(id),Var(id)),Var(id)),Blk(List(App(App(Var(id),Var(id)),Var(id))))))))))) //│ res: 'a -> 'a :p id id / 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(...)) +//│ Parsed: id(...id)(...{id(...id)(...{id(...id)})}); +//│ Desugared: id(...id)(...{id(...id)(...{id(...id)})}) +//│ AST: App(App(Var(id),Var(id)),Blk(List(App(App(Var(id),Var(id)),Blk(List(App(Var(id),Var(id)))))))) //│ res: 'a -> 'a :p id id 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(...)) +//│ Parsed: id(...id)(...{id(...id)})(...{id(...id)}); +//│ Desugared: id(...id)(...{id(...id)})(...{id(...id)}) +//│ AST: App(App(App(Var(id),Var(id)),Blk(List(App(Var(id),Var(id))))),Blk(List(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 a8279c9e1e..551c3b1c0c 100644 --- a/shared/src/test/diff/basics/Data.fun +++ b/shared/src/test/diff/basics/Data.fun @@ -1,19 +1,19 @@ :p data Test a b -//│ Parsed: 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) +//│ Desugared: def Test: forall a b. (...a) -> (...b) -> Test[a, b] +//│ AST: Def(false,Var(Test),Right(PolyType(List(Left(TypeName(a)), Left(TypeName(b))),Function(TypeName(a),Function(TypeName(b),AppliedType(TypeName(Test),List(TypeName(a), TypeName(b))))))),true) //│ Defined class Test[+a, +b] //│ Test: 'a -> 'b -> Test['a, 'b] :p data Person(name: string, age: int) -//│ Parsed: 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) +//│ Desugared: def Person: (name: string, age: int) -> Person[] +//│ AST: Def(false,Var(Person),Right(PolyType(List(),Function(Tuple(List((Some(Var(name)),Field(None,TypeName(string))), (Some(Var(age)),Field(None,TypeName(int))))),AppliedType(TypeName(Person),List())))),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 32d4430365..a2950ace06 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),Right(PolyType(List(),AppliedType(TypeName(Tru),List()))),true) //│ Desugared: def Fals: Fals[] -//│ AST: Def(false, Fals, PolyType(List(),AppliedType(TypeName(Fals),List())), true) +//│ AST: Def(false,Var(Fals),Right(PolyType(List(),AppliedType(TypeName(Fals),List()))),true) //│ Defined type alias Boolean //│ Defined class Tru //│ Defined class Fals @@ -25,11 +25,11 @@ Boolean :p :e data type Bool2 of True2 & False2 -//│ Parsed: data type Bool2 of {& True2 False2}; +//│ Parsed: 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) +//│ Desugared: def &: forall True2 False2. (...True2) -> (...False2) -> &[True2, False2] +//│ AST: Def(false,Var(&),Right(PolyType(List(Left(TypeName(True2)), Left(TypeName(False2))),Function(TypeName(True2),Function(TypeName(False2),AppliedType(TypeName(&),List(TypeName(True2), TypeName(False2))))))),true) //│ ╔══[ERROR] type identifier not found: True2 //│ ║ l.27: data type Bool2 of True2 & False2 //│ ╙── ^^^^^ @@ -109,40 +109,44 @@ Tru : Boolean // data type List of Nil { T: Nothing }, Cons head tail { T: head | tail.T } :p +:pe :w data type List a of Nil Cons (head: a) (tail: List a) -//│ Parsed: data type List a of {Nil; Cons '(' {head: a,} ')' '(' {tail: List a,} ')'}; +//│ Parsed: data type List(...a) of {Nil; Cons(...'(' {[head: a,]} ')')(...'(' {[tail: List(...a),]} ')')}; +//│ ╔══[PARSE ERROR] Not a recognized type +//│ ║ l.116: Cons (head: a) (tail: List a) +//│ ╙── ^^^^^^ //│ Desugared: type alias List[a] = Nil[a] | Cons[a] //│ Desugared: class Nil[a]: {} -//│ Desugared: class Cons[a]: {head: a, tail: List[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) -//│ Desugared: def Cons: forall a. (head: a,) -> (tail: List[a],) -> 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,AppliedType(TypeName(List),List(TypeName(a))))))),AppliedType(TypeName(Cons),List(TypeName(a)))))), true) +//│ AST: Def(false,Var(Nil),Right(PolyType(List(Left(TypeName(a))),AppliedType(TypeName(Nil),List(TypeName(a))))),true) +//│ Desugared: def Cons: forall a. (head: a) -> (tail: anything) -> Cons[a] +//│ AST: Def(false,Var(Cons),Right(PolyType(List(Left(TypeName(a))),Function(Tuple(List((Some(Var(head)),Field(None,TypeName(a))))),Function(Tuple(List((Some(Var(tail)),Field(None,Top)))),AppliedType(TypeName(Cons),List(TypeName(a))))))),true) //│ Defined type alias List[+a] //│ Defined class Nil[±a] //│ Defined class Cons[+a] //│ ╔══[WARNING] Type definition Nil has bivariant type parameters: -//│ ║ l.114: Nil +//│ ║ l.115: Nil //│ ║ ^^^ //│ ╟── a is irrelevant and may be removed -//│ ║ l.113: data type List a of +//│ ║ l.114: data type List a of //│ ╙── ^ //│ Nil: Nil[?] -//│ Cons: (head: 'a,) -> (tail: List['a],) -> Cons['a] +//│ Cons: (head: 'a,) -> (tail: anything,) -> Cons['a] // TODO interpret as free type variable? :p data type Ls of LsA a -//│ Parsed: data type Ls of {LsA a}; +//│ Parsed: 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) +//│ Desugared: def LsA: forall a. (...a) -> LsA[a] +//│ AST: Def(false,Var(LsA),Right(PolyType(List(Left(TypeName(a))),Function(TypeName(a),AppliedType(TypeName(LsA),List(TypeName(a)))))),true) //│ ╔══[ERROR] type identifier not found: a -//│ ║ l.138: data type Ls of LsA a +//│ ║ l.142: data type Ls of LsA a //│ ╙── ^ //│ Defined type alias Ls //│ Defined class LsA[+a] @@ -151,13 +155,13 @@ data type Ls of LsA a :p :e data type Ls2 of LsA2 `a -//│ Parsed: data type Ls2 of {LsA2 `a}; +//│ Parsed: 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) +//│ Desugared: def LsA2: (...'a) -> LsA2[] +//│ AST: Def(false,Var(LsA2),Right(PolyType(List(),Function(a,AppliedType(TypeName(LsA2),List())))),true) //│ ╔══[ERROR] cannot inherit from a polymorphic type -//│ ║ l.153: data type Ls2 of LsA2 `a +//│ ║ l.157: data type Ls2 of LsA2 `a //│ ╙── ^^^^^^^ //│ ╔══[ERROR] type identifier not found: LsA2 //│ ╙── @@ -172,10 +176,10 @@ Cons 1 Cons 2 Nil Cons 1 (Cons 2 Nil) //│ res: Nil[?] -//│ res: (head: 'a,) -> (tail: List['a],) -> Cons['a] -//│ res: (tail: List['a],) -> Cons[1 | 'a] +//│ res: (head: 'a,) -> (tail: anything,) -> Cons['a] +//│ res: (tail: anything,) -> Cons[1] //│ res: Cons[2] -//│ res: Cons[1 | 2] +//│ res: Cons[1] (Cons 3 Nil).head succ (Cons 3 Nil).head @@ -187,54 +191,38 @@ not (Cons false Nil).head :e not (Cons 42 Nil).head //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.188: not (Cons 42 Nil).head +//│ ║ l.192: not (Cons 42 Nil).head //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `42` is not an instance of type `bool` -//│ ║ l.188: not (Cons 42 Nil).head +//│ ║ l.192: not (Cons 42 Nil).head //│ ║ ^^ //│ ╟── but it flows into field selection with expected type `bool` -//│ ║ l.188: not (Cons 42 Nil).head +//│ ║ l.192: not (Cons 42 Nil).head //│ ╙── ^^^^^ //│ res: bool | error :e (Cons 4).head //│ ╔══[ERROR] Type mismatch in field selection: -//│ ║ l.201: (Cons 4).head +//│ ║ l.205: (Cons 4).head //│ ║ ^^^^^ -//│ ╟── type `(tail: List[?a],) -> Cons[?a]` does not have field 'head' -//│ ║ l.113: data type List a of -//│ ║ ^^^^ -//│ ║ l.114: Nil -//│ ║ ^^^^^ -//│ ║ l.115: Cons (head: a) (tail: List a) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type `(tail: anything,) -> Cons[?a]` does not have field 'head' +//│ ║ l.114: data type List a of +//│ ║ ^ //│ ╟── but it flows into receiver with expected type `{head: ?head}` -//│ ║ l.201: (Cons 4).head +//│ ║ l.205: (Cons 4).head //│ ╙── ^^^^^^^^ //│ res: error -:e +// :e Cons 1 2 -//│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.218: Cons 1 2 -//│ ║ ^^^^^^^^ -//│ ╟── integer literal of type `2` does not match type `Cons[?a] | Nil[?]` -//│ ║ l.218: Cons 1 2 -//│ ║ ^ -//│ ╟── Note: constraint arises from union type: -//│ ║ l.113: data type List a of -//│ ║ ^ -//│ ╟── from tuple type: -//│ ║ l.115: Cons (head: a) (tail: List a) -//│ ╙── ^^^^^^ -//│ res: Cons[1] | error +//│ res: Cons[1] // TODO Allow method/field defintions in the same file (lose the let?): :e let List.head = () // ... //│ ╔══[ERROR] Unsupported pattern shape -//│ ║ l.235: let List.head = () // ... +//│ ║ l.223: let List.head = () // ... //│ ╙── ^^^^^ //│ : () diff --git a/shared/src/test/diff/basics/Either.fun b/shared/src/test/diff/basics/Either.fun index c5b45e0544..a9a49120a7 100644 --- a/shared/src/test/diff/basics/Either.fun +++ b/shared/src/test/diff/basics/Either.fun @@ -4,14 +4,14 @@ data type Either l r of Left l Right r -//│ Parsed: data type Either l r of {Left l; Right r}; +//│ Parsed: data type Either(...l)(...r) of {Left(...l); Right(...r)}; //│ Desugared: type alias Either[l, r] = Left[l, r] | Right[l, r] //│ 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) -//│ 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) +//│ Desugared: def Left: forall l r. (...l) -> Left[l, r] +//│ AST: Def(false,Var(Left),Right(PolyType(List(Left(TypeName(l)), Left(TypeName(r))),Function(TypeName(l),AppliedType(TypeName(Left),List(TypeName(l), TypeName(r)))))),true) +//│ Desugared: def Right: forall l r. (...r) -> Right[l, r] +//│ AST: Def(false,Var(Right),Right(PolyType(List(Left(TypeName(l)), Left(TypeName(r))),Function(TypeName(r),AppliedType(TypeName(Right),List(TypeName(l), TypeName(r)))))),true) //│ Defined type alias Either[+l, +r] //│ Defined class Left[+l, ±r] //│ Defined class Right[±l, +r] @@ -34,10 +34,10 @@ data type Either l r of data type Either2 (l: _) (r: _) of Left2 l Right2 r -//│ ╔══[ERROR] illegal datatype type parameter shape: '(' {l: _,} ')' +//│ ╔══[ERROR] illegal datatype type parameter shape: '(' {[l: _,]} ')' //│ ║ l.34: data type Either2 (l: _) (r: _) of //│ ╙── ^^^^^^ -//│ ╔══[ERROR] illegal datatype type parameter shape: '(' {r: _,} ')' +//│ ╔══[ERROR] illegal datatype type parameter shape: '(' {[r: _,]} ')' //│ ║ l.34: data type Either2 (l: _) (r: _) of //│ ╙── ^^^^^^ //│ ╔══[ERROR] type identifier not found: l diff --git a/shared/src/test/diff/basics/Errors.fun b/shared/src/test/diff/basics/Errors.fun index b7773b8d51..9ce1df7d34 100644 --- a/shared/src/test/diff/basics/Errors.fun +++ b/shared/src/test/diff/basics/Errors.fun @@ -477,7 +477,7 @@ f arg2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+3: f arg1 //│ ║ ^^^^^^ -//│ ╟── record of type `{fld: ?a}` does not have field 'prop' +//│ ╟── record of type `forall ?a. {fld: ?a}` does not have field 'prop' //│ ║ l.+1: let arg1 = {fld: not true} //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{prop: ?prop}` @@ -612,7 +612,7 @@ i arg //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+2: i arg //│ ║ ^^^^^ -//│ ╟── record of type `{prop: ?a}` does not have field 'fld' +//│ ╟── record of type `forall ?a. {prop: ?a}` does not have field 'fld' //│ ║ l.402: let arg = {prop: not true} //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{fld: ?fld}` @@ -643,7 +643,7 @@ test arg2 //│ ╟── from field selection: //│ ║ l.328: x.prop //│ ╙── ^^^^^ -//│ res: 'a -> (int | 'a) | error +//│ res: error | 'a -> (int | 'a) let mkArg = a => {prop: a} h / mkArg 1 diff --git a/shared/src/test/diff/basics/Flow.fun b/shared/src/test/diff/basics/Flow.fun index 6b3cf121b2..8c363a2cb7 100644 --- a/shared/src/test/diff/basics/Flow.fun +++ b/shared/src/test/diff/basics/Flow.fun @@ -2,13 +2,13 @@ :p data L x data R x -//│ Parsed: data L x; data R x; +//│ Parsed: data L(...x); 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) -//│ 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) +//│ Desugared: def L: forall x. (...x) -> L[x] +//│ AST: Def(false,Var(L),Right(PolyType(List(Left(TypeName(x))),Function(TypeName(x),AppliedType(TypeName(L),List(TypeName(x)))))),true) +//│ Desugared: def R: forall x. (...x) -> R[x] +//│ AST: Def(false,Var(R),Right(PolyType(List(Left(TypeName(x))),Function(TypeName(x),AppliedType(TypeName(R),List(TypeName(x)))))),true) //│ Defined class L[+x] //│ Defined class R[+x] //│ L: 'a -> L['a] diff --git a/shared/src/test/diff/basics/Intersections.fun b/shared/src/test/diff/basics/Intersections.fun index 0c2144396c..4bbdaa0f74 100644 --- a/shared/src/test/diff/basics/Intersections.fun +++ b/shared/src/test/diff/basics/Intersections.fun @@ -6,7 +6,7 @@ let foo = _ as (_: (Int => Int) & (Bool => Bool)) :ns let foo = _ as (_: (Int => Int) & (Bool => Bool)) -let foo = (_ as (_: (Int => Int) & (Bool => Bool)))._1 +let foo = (_ as (_: (Int => Int) & (Bool => Bool))).0 //│ foo: forall 'a. (_: 'a,) //│ where //│ 'a <: int -> int & bool -> bool @@ -31,31 +31,7 @@ foo(1) // returns int & bool, equivalent to nothing succ / foo(1) foo(true) not / foo(true) -//│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.30: foo(1) // returns int & bool, equivalent to nothing -//│ ║ ^^^^^^ -//│ ╟── integer literal of type `1` is not an instance of type `bool` -//│ ║ l.30: foo(1) // returns int & bool, equivalent to nothing -//│ ║ ^ -//│ ╟── but it flows into argument with expected type `bool` -//│ ║ l.30: foo(1) // returns int & bool, equivalent to nothing -//│ ║ ^^^ -//│ ╟── Note: constraint arises from reference: -//│ ║ l.26: let foo = (Int => Int) & (Bool => Bool) -//│ ╙── ^^^^ -//│ res: bool | error | int -//│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.31: succ / foo(1) -//│ ║ ^^^^^^ -//│ ╟── integer literal of type `1` is not an instance of type `bool` -//│ ║ l.31: succ / foo(1) -//│ ║ ^ -//│ ╟── but it flows into argument with expected type `bool` -//│ ║ l.31: succ / foo(1) -//│ ║ ^^^ -//│ ╟── Note: constraint arises from reference: -//│ ║ l.26: let foo = (Int => Int) & (Bool => Bool) -//│ ╙── ^^^^ +//│ res: bool | int //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.31: succ / foo(1) //│ ║ ^^^^^^^^^^^^^ @@ -66,31 +42,7 @@ not / foo(true) //│ ║ l.31: succ / foo(1) //│ ╙── ^^^^^^ //│ res: error | int -//│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.32: foo(true) -//│ ║ ^^^^^^^^^ -//│ ╟── reference of type `true` is not an instance of type `int` -//│ ║ l.32: foo(true) -//│ ║ ^^^^ -//│ ╟── but it flows into argument with expected type `int` -//│ ║ l.32: foo(true) -//│ ║ ^^^^^^ -//│ ╟── Note: constraint arises from reference: -//│ ║ l.26: let foo = (Int => Int) & (Bool => Bool) -//│ ╙── ^^^ -//│ res: bool | error | int -//│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.33: not / foo(true) -//│ ║ ^^^^^^^^^ -//│ ╟── reference of type `true` is not an instance of type `int` -//│ ║ l.33: not / foo(true) -//│ ║ ^^^^ -//│ ╟── but it flows into argument with expected type `int` -//│ ║ l.33: not / foo(true) -//│ ║ ^^^^^^ -//│ ╟── Note: constraint arises from reference: -//│ ║ l.26: let foo = (Int => Int) & (Bool => Bool) -//│ ╙── ^^^ +//│ res: bool | int //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.33: not / foo(true) //│ ║ ^^^^^^^^^^^^^^^ @@ -106,76 +58,52 @@ not / foo(true) not / foo(1) foo(1) as Nothing //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.106: not / foo(1) -//│ ║ ^^^^^^ -//│ ╟── integer literal of type `1` is not an instance of type `bool` -//│ ║ l.106: not / foo(1) -//│ ║ ^ -//│ ╟── but it flows into argument with expected type `bool` -//│ ║ l.106: not / foo(1) -//│ ║ ^^^ -//│ ╟── Note: constraint arises from reference: -//│ ║ l.26: let foo = (Int => Int) & (Bool => Bool) -//│ ╙── ^^^^ -//│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.106: not / foo(1) -//│ ║ ^^^^^^^^^^^^ +//│ ║ l.58: not / foo(1) +//│ ║ ^^^^^^^^^^^^ //│ ╟── reference of type `int` is not an instance of type `bool` //│ ║ l.26: let foo = (Int => Int) & (Bool => Bool) //│ ║ ^^^ //│ ╟── but it flows into application with expected type `bool` -//│ ║ l.106: not / foo(1) -//│ ╙── ^^^^^^ +//│ ║ l.58: not / foo(1) +//│ ╙── ^^^^^^ //│ res: bool | error -//│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.107: foo(1) as Nothing -//│ ║ ^^^^^^ -//│ ╟── integer literal of type `1` is not an instance of type `bool` -//│ ║ l.107: foo(1) as Nothing -//│ ║ ^ -//│ ╟── but it flows into argument with expected type `bool` -//│ ║ l.107: foo(1) as Nothing -//│ ║ ^^^ -//│ ╟── Note: constraint arises from reference: -//│ ║ l.26: let foo = (Int => Int) & (Bool => Bool) -//│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in 'as' binding: -//│ ║ l.107: foo(1) as Nothing -//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.59: foo(1) as Nothing +//│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `int` does not match type `nothing` //│ ║ l.26: let foo = (Int => Int) & (Bool => Bool) //│ ║ ^^^ //│ ╟── but it flows into application with expected type `nothing` -//│ ║ l.107: foo(1) as Nothing -//│ ║ ^^^^^^ +//│ ║ l.59: foo(1) as Nothing +//│ ║ ^^^^^^ //│ ╟── Note: constraint arises from reference: -//│ ║ l.107: foo(1) as Nothing -//│ ╙── ^^^^^^^ +//│ ║ l.59: foo(1) as Nothing +//│ ╙── ^^^^^^^ //│ res: nothing :e foo as Nothing //│ ╔══[ERROR] Type mismatch in 'as' binding: -//│ ║ l.157: foo as Nothing -//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.85: foo as Nothing +//│ ║ ^^^^^^^^^^^^^^ //│ ╟── type intersection of type `int -> int & bool -> bool` does not match type `nothing` //│ ║ l.26: let foo = (Int => Int) & (Bool => Bool) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `nothing` -//│ ║ l.157: foo as Nothing -//│ ║ ^^^ +//│ ║ l.85: foo as Nothing +//│ ║ ^^^ //│ ╟── Note: constraint arises from reference: -//│ ║ l.157: foo as Nothing -//│ ╙── ^^^^^^^ +//│ ║ l.85: foo as Nothing +//│ ╙── ^^^^^^^ //│ res: nothing :e let oops = (&) //│ ╔══[ERROR] Illegal use of reserved operator: & -//│ ║ l.173: let oops = (&) +//│ ║ l.101: let oops = (&) //│ ╙── ^^^ //│ ╔══[ERROR] identifier not found: & -//│ ║ l.173: let oops = (&) +//│ ║ l.101: let oops = (&) //│ ╙── ^^^ //│ oops: error diff --git a/shared/src/test/diff/basics/Negations.fun b/shared/src/test/diff/basics/Negations.fun index be1448aa30..ffdbfd39d8 100644 --- a/shared/src/test/diff/basics/Negations.fun +++ b/shared/src/test/diff/basics/Negations.fun @@ -35,7 +35,7 @@ jesus(water: w) //│ ╟── from binding: //│ ║ l.14: let jesus = (water: neg Wine) => Wine //│ ╙── ^^^^^^^^^^^^^^^ -//│ res: error | Wine +//│ res: Wine | error //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.22: jesus(water: w) //│ ║ ^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ jesus(water: w) //│ ╟── Note: constraint arises from application: //│ ║ l.14: let jesus = (water: neg Wine) => Wine //│ ╙── ^^^^^^^^ -//│ res: error | Wine +//│ res: Wine | error (0 | 1) & neg 0 diff --git a/shared/src/test/diff/basics/Operators.fun b/shared/src/test/diff/basics/Operators.fun index d6d82a6036..895e9dd48f 100644 --- a/shared/src/test/diff/basics/Operators.fun +++ b/shared/src/test/diff/basics/Operators.fun @@ -14,9 +14,9 @@ a + b :p a + b -//│ Parsed: + a {b}; -//│ Desugared: + a {b} -//│ AST: App(App(Var(+), Var(a)), Blk(...)) +//│ Parsed: +(...a)(...{b}); +//│ Desugared: +(...a)(...{b}) +//│ AST: App(App(Var(+),Var(a)),Blk(List(Var(b)))) //│ res: int :pe @@ -29,18 +29,18 @@ a + succ a + b + c -//│ Parsed: + (succ a) {+ b {c}}; -//│ Desugared: + (succ a) {+ b {c}} -//│ AST: App(App(Var(+), App(Var(succ), Var(a))), Blk(...)) +//│ Parsed: +(...(succ(...a)))(...{+(...b)(...{c})}); +//│ Desugared: +(...(succ(...a)))(...{+(...b)(...{c})}) +//│ AST: App(App(Var(+),App(Var(succ),Var(a))),Blk(List(App(App(Var(+),Var(b)),Blk(List(Var(c))))))) //│ res: int :p succ / a + b + c -//│ Parsed: succ (+ a {+ b {c}}); -//│ Desugared: succ (+ a {+ b {c}}) -//│ AST: App(Var(succ), App(App(Var(+), Var(a)), Blk(...))) +//│ Parsed: succ(...(+(...a)(...{+(...b)(...{c})}))); +//│ Desugared: succ(...(+(...a)(...{+(...b)(...{c})}))) +//│ AST: App(Var(succ),App(App(Var(+),Var(a)),Blk(List(App(App(Var(+),Var(b)),Blk(List(Var(c)))))))) //│ res: int :p @@ -53,13 +53,13 @@ a + b + c + d -//│ Parsed: + a b; + (+ a b) c; + (+ (+ a b) c) d; -//│ Desugared: + a 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)) -//│ Desugared: + (+ (+ a b) c) d -//│ AST: App(App(Var(+), App(App(Var(+), App(App(Var(+), Var(a)), Var(b))), Var(c))), Var(d)) +//│ Parsed: +(...a)(...b); +(...(+(...a)(...b)))(...c); +(...(+(...(+(...a)(...b)))(...c)))(...d); +//│ Desugared: +(...a)(...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)) +//│ Desugared: +(...(+(...(+(...a)(...b)))(...c)))(...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 @@ -72,9 +72,9 @@ a + 2 + 3 + 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)) +//│ 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)) //│ res: int :pe @@ -110,9 +110,9 @@ succ / succ 1 succ a + b + c -//│ Parsed: + (succ {+ a b}) c; -//│ Desugared: + (succ {+ a b}) c -//│ AST: App(App(Var(+), App(Var(succ), Blk(...))), Var(c)) +//│ Parsed: +(...(succ(...{+(...a)(...b)})))(...c); +//│ Desugared: +(...(succ(...{+(...a)(...b)})))(...c) +//│ AST: App(App(Var(+),App(Var(succ),Blk(List(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/Simplesub1.fun b/shared/src/test/diff/basics/Simplesub1.fun index 1cc511067b..4b50a34f6e 100644 --- a/shared/src/test/diff/basics/Simplesub1.fun +++ b/shared/src/test/diff/basics/Simplesub1.fun @@ -104,7 +104,7 @@ x => succ (not x) //│ ╟── from reference: //│ ║ l.+1: (f => x => not (f x.u)) false //│ ╙── ^ -//│ res: {u: anything} -> bool | error +//│ res: error | {u: anything} -> bool @@ -134,7 +134,7 @@ f => { x: f 42, y: 123 }.y //│ res: (42 -> anything) -> 123 if true then { a: 1, b: true } else { b: false, c: 42 } -//│ res: {b: bool} +//│ res: {b: Bool} :e { a: 123, b: true }.c @@ -220,14 +220,14 @@ x => {l: x x, r: x } //│ ║ l.+1: (f => (x => f (v => (x x) v)) (x => f (v => (x x) v))) (f => x => f) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. -//│ res: 'a | error +//│ res: error | anything -> anything -> anything -> 'a //│ where -//│ 'a :> anything -> 'a +//│ 'a :> forall 'a. anything -> 'a res 1 2 -//│ res: error | 'a +//│ res: error | anything -> 'a //│ where -//│ 'a :> anything -> 'a +//│ 'a :> forall 'a. anything -> 'a let rec trutru = g => trutru (g true) @@ -359,30 +359,26 @@ x => (y => (x (y y))) //│ res: ('a -> 'b) -> ('c -> 'a & 'c) -> 'b (let rec x = (let y = (x x); (z => z)); x) -//│ res: 'x +//│ res: 'a -> 'a //│ where -//│ 'x :> 'a -> 'a -//│ 'a :> 'x +//│ 'a :> 'a -> 'a (let rec x = (y => (let z = (x x); y)); x) -//│ res: 'x +//│ res: 'a -> 'a //│ where -//│ 'x :> 'a -> 'a -//│ 'a :> 'x +//│ 'a :> 'a -> 'a (let rec x = (y => {u: y, v: (x x)}); x) -//│ res: 'x +//│ res: 'a -> 'b //│ where -//│ 'x :> 'a -> 'b +//│ 'a :> 'a -> 'b //│ 'b :> {u: 'a, v: 'b} -//│ 'a :> 'x (let rec x = (y => {u: (x x), v: y}); x) -//│ res: 'x +//│ res: 'a -> 'b //│ where -//│ 'x :> 'a -> 'b +//│ 'a :> 'a -> 'b //│ 'b :> {u: 'b, v: 'a} -//│ 'a :> 'x (let rec x = (y => (let z = (y x); y)); x) //│ res: 'x @@ -394,10 +390,9 @@ x => (y => (x (y y))) //│ res: ('v -> anything & {v: 'v}) -> 0 let rec x = (let y = (x x); (z => z)); (x (y => y.u)) // [test:T1] -//│ x: 'x +//│ x: 'a -> 'a //│ where -//│ 'x :> 'a -> 'a -//│ 'a :> 'x +//│ 'a :> 'a -> 'a //│ res: ({u: 'u} & 'a) -> ('u | 'a) | 'b //│ where //│ 'a :> forall 'u. ({u: 'u} & 'a) -> ('u | 'a) @@ -435,32 +430,32 @@ let rec x = (let y = (x x); (z => z)) // * Z combinator: // :e // Works thanks to inconsistent constrained types... (f => (x => f (v => (x x) v)) (x => f (v => (x x) v))) -//│ res: ((forall 'a 'b. ('a -> 'b -//│ where -//│ forall 'c 'd. ('c -> 'd -//│ where -//│ 'e <: (forall 'f 'g. ('f -> 'g -//│ where -//│ 'c <: 'c -> 'f -> 'g)) -> 'd) <: (forall 'c 'd. ('c -> 'd +//│ res: ((forall 'a 'b. 'a -> 'b //│ where -//│ 'e <: (forall 'f 'g. ('f -> 'g -//│ where -//│ 'c <: 'c -> 'f -> 'g)) -> 'd)) -> 'a -> 'b)) -> 'h & 'e) -> 'h +//│ forall 'c 'd. 'c -> 'd +//│ where +//│ 'e <: (forall 'f 'g. 'f -> 'g +//│ where +//│ 'c <: 'c -> 'f -> 'g) -> 'd <: (forall 'c 'd. 'c -> 'd +//│ where +//│ 'e <: (forall 'f 'g. 'f -> 'g +//│ where +//│ 'c <: 'c -> 'f -> 'g) -> 'd) -> 'a -> 'b) -> 'h & 'e) -> 'h // * Function that takes arbitrarily many arguments: // :e // Works thanks to inconsistent constrained types... (f => (x => f (v => (x x) v)) (x => f (v => (x x) v))) (f => x => f) -//│ res: anything -> (forall 'a 'b. ('b -> 'a -//│ where -//│ forall 'c 'd. ('c -> 'd -//│ where -//│ forall 'e. 'e -> anything -> 'e <: (forall 'f 'g. ('f -> 'g -//│ where -//│ 'c <: 'c -> 'f -> 'g)) -> 'd) <: (forall 'c 'd. ('c -> 'd -//│ where -//│ forall 'e. 'e -> anything -> 'e <: (forall 'f 'g. ('f -> 'g +//│ res: anything -> (forall 'a 'b. 'a -> 'b //│ where -//│ 'c <: 'c -> 'f -> 'g)) -> 'd)) -> 'b -> 'a)) +//│ forall 'c 'd. 'c -> 'd +//│ where +//│ forall 'e. 'e -> anything -> 'e <: (forall 'f 'g. 'f -> 'g +//│ where +//│ 'c <: 'c -> 'f -> 'g) -> 'd <: (forall 'c 'd. 'c -> 'd +//│ where +//│ forall 'e. 'e -> anything -> 'e <: (forall 'f 'g. 'f -> 'g +//│ where +//│ 'c <: 'c -> 'f -> 'g) -> 'd) -> 'a -> 'b) diff --git a/shared/src/test/diff/basics/Simplesub2.fun b/shared/src/test/diff/basics/Simplesub2.fun index 0afe1fea03..9882a1909f 100644 --- a/shared/src/test/diff/basics/Simplesub2.fun +++ b/shared/src/test/diff/basics/Simplesub2.fun @@ -19,7 +19,7 @@ let object2 = { x: 17, y: false } let pick_an_object = b => if b then object1 else object2 -//│ pick_an_object: bool -> {x: 17 | 42, y: forall 'a. 'a -> 'a | false} +//│ pick_an_object: bool -> {x: 17 | 42, y: forall 'a. false | 'a -> 'a} let rec recursive_monster = x => { thing: x, self: recursive_monster x } diff --git a/shared/src/test/diff/basics/Slashes.fun b/shared/src/test/diff/basics/Slashes.fun index 3f3fde492c..d4c3316097 100644 --- a/shared/src/test/diff/basics/Slashes.fun +++ b/shared/src/test/diff/basics/Slashes.fun @@ -21,9 +21,9 @@ x => succ / succ / x + 1 :p 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))))) +//│ 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))))) //│ res: int :e diff --git a/shared/src/test/diff/basics/Tuples.fun b/shared/src/test/diff/basics/Tuples.fun index 8e6623ff7d..4df4ecdb82 100644 --- a/shared/src/test/diff/basics/Tuples.fun +++ b/shared/src/test/diff/basics/Tuples.fun @@ -15,40 +15,26 @@ let t = x: 1, y: 2, z: 3 //│ t: (1, y: 2, 3,) //│ t: (x: 1, y: 2, z: 3,) -(1, true, "hey")._2 -(1, true, "hey")._3 +(1, true, "hey").1 +(1, true, "hey").2 //│ res: true //│ res: "hey" :e -(1, true, "hey")._4 +(1, true, "hey").3 //│ ╔══[ERROR] Type mismatch in field selection: -//│ ║ l.24: (1, true, "hey")._4 -//│ ║ ^^^ -//│ ╟── tuple of type `{_1: 1, _2: true, _3: "hey"}` does not have field '_4' -//│ ║ l.24: (1, true, "hey")._4 +//│ ║ l.24: (1, true, "hey").3 +//│ ║ ^^ +//│ ╟── tuple of type `{0: 1, 1: true, 2: "hey"}` does not have field '3' +//│ ║ l.24: (1, true, "hey").3 //│ ║ ^^^^^^^^^^^^^^ -//│ ╟── but it flows into receiver with expected type `{_4: ?a}` -//│ ║ l.24: (1, true, "hey")._4 +//│ ╟── but it flows into receiver with expected type `{3: ?a}` +//│ ║ l.24: (1, true, "hey").3 //│ ╙── ^^^^^^^^^^^^^^^^ //│ res: error -:p -:e -(1, true, "hey").2 -//│ Parsed: '(' {1, true, "hey",} ')' 0.2; -//│ Desugared: '(' {1, true, "hey",} ')' 0.2 -//│ AST: App(Bra(rcd = false, Blk(...)), DecLit(0.2)) -//│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.38: (1, true, "hey").2 -//│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ╟── tuple of type `(1, true, "hey",)` is not a function -//│ ║ l.38: (1, true, "hey").2 -//│ ║ ^^^^^^^^^^^^^^ -//│ ╟── but it flows into applied expression with expected type `0.2 -> ?a` -//│ ║ l.38: (1, true, "hey").2 -//│ ╙── ^^^^^^^^^^^^^^^^ -//│ res: error +(1, true, "hey").1 +//│ res: true :w let not-tup = ( @@ -56,7 +42,7 @@ let not-tup = ( 2 ) //│ ╔══[WARNING] Pure expression does nothing in statement position. -//│ ║ l.55: 1 +//│ ║ l.41: 1 //│ ╙── ^ //│ not-tup: 2 @@ -66,7 +52,7 @@ let tup = ( 2 ) //│ ╔══[WARNING] Previous field definitions are discarded by this returned expression. -//│ ║ l.66: 2 +//│ ║ l.52: 2 //│ ╙── ^ //│ tup: 2 @@ -76,7 +62,7 @@ let tup = 2, 3 //│ ╔══[WARNING] Previous field definitions are discarded by this returned expression. -//│ ║ l.77: 3 +//│ ║ l.63: 3 //│ ╙── ^ //│ tup: 3 diff --git a/shared/src/test/diff/basics/Unions.fun b/shared/src/test/diff/basics/Unions.fun index 88b43723b5..b693f8afa4 100644 --- a/shared/src/test/diff/basics/Unions.fun +++ b/shared/src/test/diff/basics/Unions.fun @@ -145,7 +145,7 @@ x => foo { v: x } // Notice that in MLscript, `(0, 0) | (1, 1)` is equivalent to `(0 | 1, 0 | 1)` -let bar(r: (0, 0) | (1, 1)) = if r._1 < 1 then r._1 else r._2 +let bar(r: (0, 0) | (1, 1)) = if r.0 < 1 then r.0 else r.1 //│ bar: (r: ('a & (0 | 1), 'a & (0 | 1),),) -> 'a bar(0, 1) @@ -221,25 +221,25 @@ x => bar(bar(x, 1), 0) //│ res: ('a & (0 | 1), 'a & (0 | 1),) -> (0 | 'a) -let baz(r: (0, 0) | _) = if r._1 < 1 then r._1 else r._2 -//│ baz: (r: {_1: number & 'a, _2: 'a},) -> 'a +let baz(r: (0, 0) | _) = if r.0 < 1 then r.0 else r.1 +//│ baz: (r: {0: number & 'a, 1: 'a},) -> 'a :e baz(0) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.228: baz(0) //│ ║ ^^^^^^ -//│ ╟── integer literal of type `0` does not have field '_2' +//│ ╟── integer literal of type `0` does not have field '1' //│ ║ l.228: baz(0) //│ ║ ^ -//│ ╟── but it flows into argument with expected type `{_2: ?a}` +//│ ╟── but it flows into argument with expected type `{1: ?a}` //│ ║ l.228: baz(0) //│ ║ ^^^ //│ ╟── Note: constraint arises from field selection: -//│ ║ l.224: let baz(r: (0, 0) | _) = if r._1 < 1 then r._1 else r._2 -//│ ║ ^^^ +//│ ║ l.224: let baz(r: (0, 0) | _) = if r.0 < 1 then r.0 else r.1 +//│ ║ ^^ //│ ╟── from binding: -//│ ║ l.224: let baz(r: (0, 0) | _) = if r._1 < 1 then r._1 else r._2 +//│ ║ l.224: let baz(r: (0, 0) | _) = if r.0 < 1 then r.0 else r.1 //│ ╙── ^^^^^^^^^^^^^ //│ res: error @@ -260,7 +260,7 @@ x => baz(x, x) //│ res: (number & 'a, 'a,) -> 'a -let baz(r: (0, 0) | (1, _)) = if r._1 < 1 then r._1 else r._2 +let baz(r: (0, 0) | (1, _)) = if r.0 < 1 then r.0 else r.1 //│ baz: (r: ('a & (0 | 1), 'a,),) -> 'a :e @@ -269,17 +269,17 @@ baz(0, 1) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.267: baz(0) //│ ║ ^^^^^^ -//│ ╟── integer literal of type `0` does not have field '_2' +//│ ╟── integer literal of type `0` does not have field '1' //│ ║ l.267: baz(0) //│ ║ ^ -//│ ╟── but it flows into argument with expected type `{_2: ?a}` +//│ ╟── but it flows into argument with expected type `{1: ?a}` //│ ║ l.267: baz(0) //│ ║ ^^^ //│ ╟── Note: constraint arises from field selection: -//│ ║ l.263: let baz(r: (0, 0) | (1, _)) = if r._1 < 1 then r._1 else r._2 -//│ ║ ^^^ +//│ ║ l.263: let baz(r: (0, 0) | (1, _)) = if r.0 < 1 then r.0 else r.1 +//│ ║ ^^ //│ ╟── from binding: -//│ ║ l.263: let baz(r: (0, 0) | (1, _)) = if r._1 < 1 then r._1 else r._2 +//│ ║ l.263: let baz(r: (0, 0) | (1, _)) = if r.0 < 1 then r.0 else r.1 //│ ╙── ^^^^^^^^^^^^^^^^^^ //│ res: error //│ res: 0 | 1 diff --git a/shared/src/test/diff/basics/VerboseErrors.fun b/shared/src/test/diff/basics/VerboseErrors.fun index 0c9ff183fc..e645fe31ae 100644 --- a/shared/src/test/diff/basics/VerboseErrors.fun +++ b/shared/src/test/diff/basics/VerboseErrors.fun @@ -74,5 +74,5 @@ test arg2 //│ ╟── from field selection: //│ ║ l.50: x.prop //│ ╙── ^^^^^ -//│ res: 'a -> (int | 'a) | error +//│ res: error | 'a -> (int | 'a) diff --git a/shared/src/test/diff/codegen/AuxiliaryConstructors.mls b/shared/src/test/diff/codegen/AuxiliaryConstructors.mls new file mode 100644 index 0000000000..36b8a2c8fc --- /dev/null +++ b/shared/src/test/diff/codegen/AuxiliaryConstructors.mls @@ -0,0 +1,579 @@ +:NewParser +:NewDefs + +:js +class A(x: Int) {} +//│ class A(x: Int) +//│ // Prelude +//│ let res; +//│ class TypingUnit { +//│ #A; +//│ constructor() { +//│ } +//│ get A() { +//│ const qualifier = this; +//│ if (this.#A === undefined) { +//│ class A { +//│ #x; +//│ constructor(x) { +//│ this.#x = x; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#x]; +//│ } +//│ }; +//│ this.#A = ((x) => Object.freeze(new A(x))); +//│ this.#A.class = A; +//│ this.#A.unapply = A.unapply; +//│ } +//│ return this.#A; +//│ } +//│ } +//│ const typing_unit = new TypingUnit; +//│ globalThis.A = typing_unit.A; +//│ // End of generated code + +:js +class B {} +//│ class B { +//│ constructor() +//│ } +//│ // Prelude +//│ class TypingUnit1 { +//│ #B; +//│ constructor() { +//│ } +//│ get B() { +//│ const qualifier = this; +//│ if (this.#B === undefined) { +//│ class B {}; +//│ this.#B = B; +//│ } +//│ return this.#B; +//│ } +//│ } +//│ const typing_unit1 = new TypingUnit1; +//│ globalThis.B = typing_unit1.B; +//│ // End of generated code + +new B +//│ B +//│ res +//│ = B {} + +:e +B() +//│ ╔══[ERROR] Construction of unparameterized class B should use the `new` keyword +//│ ║ l.66: B() +//│ ╙── ^ +//│ B +//│ res +//│ Runtime error: +//│ TypeError: Class constructor B cannot be invoked without 'new' + +abstract class C +//│ abstract class C + +:e +new C +//│ ╔══[ERROR] Class C is abstract and cannot be instantiated +//│ ║ l.79: new C +//│ ╙── ^ +//│ C +//│ res +//│ = C {} + +:e +C() +//│ ╔══[ERROR] Class C is abstract and cannot be instantiated +//│ ║ l.88: C() +//│ ╙── ^ +//│ ╔══[ERROR] Class C cannot be instantiated as it exposes no constructor +//│ ║ l.88: C() +//│ ╙── ^ +//│ error +//│ res +//│ Runtime error: +//│ TypeError: Class constructor C cannot be invoked without 'new' + +:js +class C { + constructor(x: Int) { log(x) } +} +//│ class C { +//│ constructor(x: Int) +//│ } +//│ // Prelude +//│ function log(x) { +//│ return console.info(x); +//│ } +//│ class TypingUnit7 { +//│ #C; +//│ constructor() { +//│ } +//│ get C() { +//│ const qualifier = this; +//│ if (this.#C === undefined) { +//│ class C { +//│ constructor(x) { +//│ log(x); +//│ } +//│ }; +//│ this.#C = C; +//│ } +//│ return this.#C; +//│ } +//│ } +//│ const typing_unit7 = new TypingUnit7; +//│ globalThis.C = typing_unit7.C; +//│ // End of generated code + +:e +let c = new C() +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.133: let c = new C() +//│ ║ ^^^^^^^ +//│ ╟── argument list of type `[]` does not match type `[x: Int]` +//│ ║ l.133: let c = new C() +//│ ╙── ^^ +//│ let c: C | error +//│ c +//│ = C {} +//│ // Output +//│ undefined + +let c = new C(1) +//│ let c: C +//│ c +//│ = C {} +//│ // Output +//│ 1 + +:js +class D(val x: Int) { + constructor(y: Int) { + x = y + 1 + } + log(x) +} +//│ class D(x: Int) { +//│ constructor(y: Int) +//│ } +//│ // Prelude +//│ class TypingUnit10 { +//│ #D; +//│ constructor() { +//│ } +//│ get D() { +//│ const qualifier = this; +//│ if (this.#D === undefined) { +//│ class D { +//│ #x; +//│ get x() { return this.#x; } +//│ constructor(y) { +//│ this.#x = y + 1; +//│ const x = this.#x; +//│ log(x); +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#x]; +//│ } +//│ }; +//│ this.#D = ((y) => Object.freeze(new D(y))); +//│ this.#D.class = D; +//│ this.#D.unapply = D.unapply; +//│ } +//│ return this.#D; +//│ } +//│ } +//│ const typing_unit10 = new TypingUnit10; +//│ globalThis.D = typing_unit10.D; +//│ // End of generated code + +let dd = new D(41) +dd.x +//│ let dd: D +//│ Int +//│ dd +//│ = D {} +//│ // Output +//│ 42 +//│ res +//│ = 42 + +let dd = new D(41) +dd.x +//│ let dd: D +//│ Int +//│ dd +//│ = D {} +//│ // Output +//│ 42 +//│ res +//│ = 42 + +:pe +class E { + constructor(x: Int) + constructor(y: Int) +} +//│ ╔══[PARSE ERROR] A class may have at most one explicit constructor +//│ ║ l.218: class E { +//│ ╙── ^^^^^ +//│ class E { +//│ constructor(x: Int) +//│ } + +:e +constructor(x: Int) +//│ ╔══[ERROR] Illegal position for this constructor statement. +//│ ║ l.230: constructor(x: Int) +//│ ╙── ^^^^^^^^^^^^^^^^^^^ +//│ +//│ Code generation encountered an error: +//│ unexpected constructor. + +:js +class F(x: Int) extends C(x + 1) {} +class G extends C(2) {} +class H extends B {} +//│ class F(x: Int) extends C +//│ class G extends C { +//│ constructor() +//│ } +//│ class H extends B { +//│ constructor() +//│ } +//│ // Prelude +//│ class TypingUnit14 { +//│ #F; +//│ #G; +//│ #H; +//│ constructor() { +//│ } +//│ get F() { +//│ const qualifier = this; +//│ if (this.#F === undefined) { +//│ class F extends C { +//│ #x; +//│ constructor(x) { +//│ super(x + 1); +//│ this.#x = x; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#x]; +//│ } +//│ }; +//│ this.#F = ((x) => Object.freeze(new F(x))); +//│ this.#F.class = F; +//│ this.#F.unapply = F.unapply; +//│ } +//│ return this.#F; +//│ } +//│ get G() { +//│ const qualifier = this; +//│ if (this.#G === undefined) { +//│ class G extends C { +//│ constructor() { +//│ super(2); +//│ } +//│ }; +//│ this.#G = G; +//│ } +//│ return this.#G; +//│ } +//│ get H() { +//│ const qualifier = this; +//│ if (this.#H === undefined) { +//│ class H extends B { +//│ constructor() { +//│ super(); +//│ } +//│ }; +//│ this.#H = H; +//│ } +//│ return this.#H; +//│ } +//│ } +//│ const typing_unit14 = new TypingUnit14; +//│ globalThis.F = typing_unit14.F; +//│ globalThis.G = typing_unit14.G; +//│ globalThis.H = typing_unit14.H; +//│ // End of generated code + +:js +fun f(c) = + if c is + F(x) then x + G() then 2 + _ then 0 +//│ fun f: Object -> Int +//│ // Prelude +//│ class TypingUnit15 {} +//│ const typing_unit15 = new TypingUnit15; +//│ // Query 1 +//│ 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; +//│ })()); +//│ }; +//│ // End of generated code + +f(F(12)) +f(new G()) +//│ Int +//│ res +//│ = 12 +//│ // Output +//│ 13 +//│ res +//│ = 2 +//│ // Output +//│ 2 + +:js +module I { + class J { + constructor(x: Int) + } + module K { + class L extends J(0) + } +} +//│ module I { +//│ class J { +//│ constructor(x: Int) +//│ } +//│ module K { +//│ class L extends J { +//│ constructor() +//│ } +//│ } +//│ } +//│ // Prelude +//│ class TypingUnit17 { +//│ #I; +//│ constructor() { +//│ } +//│ get I() { +//│ const qualifier = this; +//│ if (this.#I === undefined) { +//│ class I { +//│ #J; +//│ #K; +//│ constructor() { +//│ } +//│ get K() { +//│ const qualifier1 = this; +//│ if (this.#K === undefined) { +//│ class K { +//│ #L; +//│ constructor() { +//│ } +//│ get L() { +//│ const qualifier2 = this; +//│ if (this.#L === undefined) { +//│ class L extends qualifier1.J { +//│ constructor() { +//│ super(0); +//│ } +//│ }; +//│ this.#L = L; +//│ } +//│ return this.#L; +//│ } +//│ } +//│ this.#K = new K(); +//│ this.#K.class = K; +//│ } +//│ return this.#K; +//│ } +//│ get J() { +//│ const qualifier1 = this; +//│ if (this.#J === undefined) { +//│ class J {}; +//│ this.#J = J; +//│ } +//│ return this.#J; +//│ } +//│ } +//│ this.#I = new I(); +//│ this.#I.class = I; +//│ } +//│ return this.#I; +//│ } +//│ } +//│ const typing_unit17 = new TypingUnit17; +//│ globalThis.I = typing_unit17.I; +//│ // End of generated code + +:js +fun g(x: Int) = + class L(y: Int) { + constructor(z: Int) { + y = z + 1 + } + fun ll = x + y + } + x => new L(x) +//│ fun g: (x: Int) -> Int -> L +//│ // Prelude +//│ class TypingUnit18 {} +//│ const typing_unit18 = new TypingUnit18; +//│ // Query 1 +//│ globalThis.g = function g(x) { +//│ return ((() => { +//│ const L = (() => { +//│ class L { +//│ #y; +//│ constructor(z) { +//│ this.#y = z + 1; +//│ const y = this.#y; +//│ } +//│ get ll() { +//│ const y = this.#y; +//│ return x + y; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#y]; +//│ } +//│ } +//│ let ctor; +//│ ctor = ((z) => new L(z)); +//│ ctor.class = L; +//│ return ctor; +//│ })(); +//│ return (x) => new L.class(x); +//│ })()); +//│ }; +//│ // End of generated code + +:js +let m = g(1) +let n = m(2) +n.ll +//│ let m: Int -> L +//│ let n: L +//│ Int +//│ // Prelude +//│ class TypingUnit19 {} +//│ const typing_unit19 = new TypingUnit19; +//│ // Query 1 +//│ globalThis.m = g(1); +//│ // Query 2 +//│ globalThis.n = m(2); +//│ // Query 3 +//│ res = n.ll; +//│ // End of generated code +//│ m +//│ = [Function (anonymous)] +//│ n +//│ = L {} +//│ res +//│ = 4 + +class M() +//│ class M() + +:js +let mm = new M() +//│ let mm: M +//│ // Prelude +//│ class TypingUnit21 {} +//│ const typing_unit21 = new TypingUnit21; +//│ // Query 1 +//│ globalThis.mm = new M.class(); +//│ // End of generated code +//│ mm +//│ = M {} + +:e // TODO support first-class classes +fun h(z: Int) = + class N { + constructor(x: Int) { + log(x + z) + } + } + N +//│ ╔══[ERROR] Construction of unparameterized class N should use the `new` keyword +//│ ║ l.502: N +//│ ╙── ^ +//│ fun h: (z: Int) -> (x: Int) -> N + +let hh = h(1) +//│ let hh: (x: Int) -> N +//│ hh +//│ = [class N] + +:e +new hh(1) +//│ ╔══[ERROR] type identifier not found: hh +//│ ║ l.514: new hh(1) +//│ ╙── ^^ +//│ error +//│ res +//│ = N {} +//│ // Output +//│ 2 + +:e +module O { + constructor(x: Int) +} +mixin P { + constructor(x: Int) +} +//│ ╔══[ERROR] Explicit module constructors are not supported +//│ ║ l.526: constructor(x: Int) +//│ ╙── ^^^^^^^^^^^^^^^^^^^ +//│ ╔══[ERROR] Explicit mixin constructors are not supported +//│ ║ l.529: constructor(x: Int) +//│ ╙── ^^^^^^^^^^^^^^^^^^^ +//│ module O { +//│ constructor(x: Int) +//│ } +//│ mixin P() + +:w +:e +class QQ(qq: Str) { + constructor(foo: Int) { + lol + qq = foo + } +} +//│ ╔══[ERROR] identifier not found: lol +//│ ║ l.546: lol +//│ ╙── ^^^ +//│ ╔══[WARNING] Pure expression does nothing in statement position. +//│ ║ l.546: lol +//│ ╙── ^^^ +//│ ╔══[ERROR] Type mismatch in auxiliary class constructor: +//│ ║ l.545: constructor(foo: Int) { +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.546: lol +//│ ║ ^^^^^^^ +//│ ║ l.547: qq = foo +//│ ║ ^^^^^^^^^^^^ +//│ ║ l.548: } +//│ ║ ^^^ +//│ ╟── type `Int` is not an instance of type `Str` +//│ ║ l.545: constructor(foo: Int) { +//│ ║ ^^^ +//│ ╟── but it flows into reference with expected type `Str` +//│ ║ l.547: qq = foo +//│ ║ ^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.544: class QQ(qq: Str) { +//│ ╙── ^^^ +//│ class QQ(qq: Str) { +//│ constructor(foo: Int) +//│ } +//│ Code generation encountered an error: +//│ unresolved symbol lol + diff --git a/shared/src/test/diff/codegen/Classes.mls b/shared/src/test/diff/codegen/Classes.mls index 7be4eac29c..38852a109a 100644 --- a/shared/src/test/diff/codegen/Classes.mls +++ b/shared/src/test/diff/codegen/Classes.mls @@ -234,7 +234,7 @@ class Point: { x: int; y: int } //│ Defined Point.Move: Point -> (int, int,) -> Point //│ Defined Point.Sum: Point -> {x: int, y: int} -> Point //│ Defined Point.Shadow: Point -> {x: int, y: int} -> Point -//│ Defined Point.Shadow2: Point -> {x: int, y: int} -> Point +//│ Defined Point.Shadow2: Point -> (({x: int, y: int},),) -> Point //│ Defined Point.Shadow3: Point -> {this: {x: int, y: int}} -> Point //│ // Prelude //│ class Point { diff --git a/shared/src/test/diff/codegen/ConstructorStmt.mls b/shared/src/test/diff/codegen/ConstructorStmt.mls new file mode 100644 index 0000000000..fa0100cef9 --- /dev/null +++ b/shared/src/test/diff/codegen/ConstructorStmt.mls @@ -0,0 +1,418 @@ +:NewDefs + + +log("Hello!") +//│ () +//│ res +//│ = undefined +//│ // Output +//│ Hello! + + +:js +module Test0 { + log("Hello!") +} +//│ module Test0 +//│ // Prelude +//│ class TypingUnit1 { +//│ #Test0; +//│ constructor() { +//│ } +//│ get Test0() { +//│ const qualifier = this; +//│ if (this.#Test0 === undefined) { +//│ class Test0 { +//│ constructor() { +//│ log("Hello!"); +//│ } +//│ } +//│ this.#Test0 = new Test0(); +//│ this.#Test0.class = Test0; +//│ } +//│ return this.#Test0; +//│ } +//│ } +//│ const typing_unit1 = new TypingUnit1; +//│ globalThis.Test0 = typing_unit1.Test0; +//│ // End of generated code + +:js +Test0 +//│ Test0 +//│ // Prelude +//│ class TypingUnit2 {} +//│ const typing_unit2 = new TypingUnit2; +//│ // Query 1 +//│ res = Test0; +//│ // End of generated code +//│ res +//│ = Test0 { class: [class Test0] } +//│ // Output +//│ Hello! + +:js +Test0 +//│ Test0 +//│ // Prelude +//│ class TypingUnit3 {} +//│ const typing_unit3 = new TypingUnit3; +//│ // Query 1 +//│ res = Test0; +//│ // End of generated code +//│ res +//│ = Test0 { class: [class Test0] } + +:js +class A(a: Int) { + log(a) +} +//│ class A(a: Int) +//│ // Prelude +//│ class TypingUnit4 { +//│ #A; +//│ constructor() { +//│ } +//│ get A() { +//│ const qualifier = this; +//│ if (this.#A === undefined) { +//│ class A { +//│ #a; +//│ constructor(a) { +//│ this.#a = a; +//│ log(a); +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#a]; +//│ } +//│ }; +//│ this.#A = ((a) => Object.freeze(new A(a))); +//│ this.#A.class = A; +//│ this.#A.unapply = A.unapply; +//│ } +//│ return this.#A; +//│ } +//│ } +//│ const typing_unit4 = new TypingUnit4; +//│ globalThis.A = typing_unit4.A; +//│ // End of generated code + +:js +let aa = A(42) +//│ let aa: A +//│ // Prelude +//│ class TypingUnit5 {} +//│ const typing_unit5 = new TypingUnit5; +//│ // Query 1 +//│ globalThis.aa = A(42); +//│ // End of generated code +//│ aa +//│ = A {} +//│ // Output +//│ 42 + +:js +aa +//│ A +//│ // Prelude +//│ class TypingUnit6 {} +//│ const typing_unit6 = new TypingUnit6; +//│ // Query 1 +//│ res = aa; +//│ // End of generated code +//│ res +//│ = A {} + +:js +let ab = A(0) +//│ let ab: A +//│ // Prelude +//│ class TypingUnit7 {} +//│ const typing_unit7 = new TypingUnit7; +//│ // Query 1 +//│ globalThis.ab = A(0); +//│ // End of generated code +//│ ab +//│ = A {} +//│ // Output +//│ 0 + +:e +:w +:js +class Foo { + this: { x: Int } +} +//│ ╔══[ERROR] Type `#Foo` does not contain member `x` +//│ ║ l.145: this: { x: Int } +//│ ╙── ^ +//│ ╔══[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 type ascription: +//│ ║ l.145: this: { x: Int } +//│ ║ ^^^^ +//│ ╟── type `{x: Int}` does not match type `()` +//│ ║ l.145: this: { x: Int } +//│ ║ ^^^^^^^^^^ +//│ ╟── but it flows into expression in statement position with expected type `()` +//│ ║ l.145: this: { x: Int } +//│ ╙── ^^^^ +//│ class Foo { +//│ constructor() +//│ } +//│ // Prelude +//│ class TypingUnit8 { +//│ #Foo; +//│ constructor() { +//│ } +//│ get Foo() { +//│ const qualifier = this; +//│ if (this.#Foo === undefined) { +//│ class Foo {}; +//│ this.#Foo = Foo; +//│ } +//│ return this.#Foo; +//│ } +//│ } +//│ const typing_unit8 = new TypingUnit8; +//│ globalThis.Foo = typing_unit8.Foo; +//│ // End of generated code + +:e +:w +:js +class Bar { + super: { x: Int } +} +//│ ╔══[ERROR] Illegal use of `super` +//│ ║ l.186: super: { x: Int } +//│ ╙── ^^^^^ +//│ ╔══[ERROR] Type `#Bar` does not contain member `x` +//│ ║ l.186: super: { x: Int } +//│ ╙── ^ +//│ ╔══[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 type ascription: +//│ ║ l.186: super: { x: Int } +//│ ║ ^^^^^ +//│ ╟── type `{x: Int}` does not match type `()` +//│ ║ l.186: super: { x: Int } +//│ ║ ^^^^^^^^^^ +//│ ╟── but it flows into expression in statement position with expected type `()` +//│ ║ l.186: super: { x: Int } +//│ ╙── ^^^^^ +//│ class Bar { +//│ constructor() +//│ } +//│ // Prelude +//│ class TypingUnit9 { +//│ #Bar; +//│ constructor() { +//│ } +//│ get Bar() { +//│ const qualifier = this; +//│ if (this.#Bar === undefined) { +//│ class Bar {}; +//│ this.#Bar = Bar; +//│ } +//│ return this.#Bar; +//│ } +//│ } +//│ const typing_unit9 = new TypingUnit9; +//│ globalThis.Bar = typing_unit9.Bar; +//│ // End of generated code + +:js +class Baz() { + val x = 123 + log([1, x]) + val y = + log([2, x]) + x + 1 + log([3, y]) +} +//│ class Baz() { +//│ val x: 123 +//│ val y: Int +//│ } +//│ // Prelude +//│ class TypingUnit10 { +//│ #Baz; +//│ constructor() { +//│ } +//│ get Baz() { +//│ const qualifier = this; +//│ if (this.#Baz === undefined) { +//│ class Baz { +//│ #x; +//│ get x() { return this.#x; } +//│ #y; +//│ get y() { return this.#y; } +//│ constructor() { +//│ this.#x = 123; +//│ const x = this.#x; +//│ log([ +//│ 1, +//│ x +//│ ]); +//│ this.#y = (() => { +//│ log([ +//│ 2, +//│ x +//│ ]); +//│ return x + 1; +//│ })(); +//│ const y = this.#y; +//│ log([ +//│ 3, +//│ y +//│ ]); +//│ } +//│ static +//│ unapply(x) { +//│ return []; +//│ } +//│ }; +//│ this.#Baz = (() => Object.freeze(new Baz())); +//│ this.#Baz.class = Baz; +//│ this.#Baz.unapply = Baz.unapply; +//│ } +//│ return this.#Baz; +//│ } +//│ } +//│ const typing_unit10 = new TypingUnit10; +//│ globalThis.Baz = typing_unit10.Baz; +//│ // End of generated code + +let baz = Baz() +log([baz.x, baz.y]) +//│ let baz: Baz +//│ () +//│ baz +//│ = Baz {} +//│ // Output +//│ [ 1, 123 ] +//│ [ 2, 123 ] +//│ [ 3, 124 ] +//│ res +//│ = undefined +//│ // Output +//│ [ 123, 124 ] + +:js +class Q() { + let q = 42 + fun qq = + let f = (x: Int) => {q: x + q}; f(1) +} +//│ class Q() { +//│ let q: 42 +//│ fun qq: {q: Int} +//│ } +//│ // Prelude +//│ class TypingUnit12 { +//│ #Q; +//│ constructor() { +//│ } +//│ get Q() { +//│ const qualifier = this; +//│ if (this.#Q === undefined) { +//│ class Q { +//│ #q; +//│ constructor() { +//│ this.#q = 42; +//│ const q = this.#q; +//│ } +//│ get qq() { +//│ const qualifier1 = this; +//│ return ((() => { +//│ let f = (x) => ({ q: x + qualifier1.#q }); +//│ return f(1); +//│ })()); +//│ } +//│ static +//│ unapply(x) { +//│ return []; +//│ } +//│ }; +//│ this.#Q = (() => Object.freeze(new Q())); +//│ this.#Q.class = Q; +//│ this.#Q.unapply = Q.unapply; +//│ } +//│ return this.#Q; +//│ } +//│ } +//│ const typing_unit12 = new TypingUnit12; +//│ globalThis.Q = typing_unit12.Q; +//│ // End of generated code + +let q = Q() +q.qq.q +//│ let q: Q +//│ Int +//│ q +//│ = Q {} +//│ res +//│ = 43 + +:js +class W() { + let x = 42 + fun add(self: Int) = x + self +} +//│ class W() { +//│ fun add: (self: Int) -> Int +//│ let x: 42 +//│ } +//│ // Prelude +//│ class TypingUnit14 { +//│ #W; +//│ constructor() { +//│ } +//│ get W() { +//│ const qualifier = this; +//│ if (this.#W === undefined) { +//│ class W { +//│ #x; +//│ constructor() { +//│ this.#x = 42; +//│ const x = this.#x; +//│ } +//│ add(self) { +//│ const qualifier1 = this; +//│ return qualifier1.#x + self; +//│ } +//│ static +//│ unapply(x) { +//│ return []; +//│ } +//│ }; +//│ this.#W = (() => Object.freeze(new W())); +//│ this.#W.class = W; +//│ this.#W.unapply = W.unapply; +//│ } +//│ return this.#W; +//│ } +//│ } +//│ const typing_unit14 = new TypingUnit14; +//│ globalThis.W = typing_unit14.W; +//│ // End of generated code + +:js +let www = W() +www.add(42) +//│ let www: W +//│ Int +//│ // Prelude +//│ class TypingUnit15 {} +//│ const typing_unit15 = new TypingUnit15; +//│ // Query 1 +//│ globalThis.www = W(); +//│ // Query 2 +//│ res = www.add(42); +//│ // End of generated code +//│ www +//│ = W {} +//│ res +//│ = 84 diff --git a/shared/src/test/diff/codegen/FieldOverride.mls b/shared/src/test/diff/codegen/FieldOverride.mls new file mode 100644 index 0000000000..0fb2afb424 --- /dev/null +++ b/shared/src/test/diff/codegen/FieldOverride.mls @@ -0,0 +1,144 @@ +:NewParser +:NewDefs + +:js +class C(a: Int) { val a = 1 } +//│ class C(a: Int) { +//│ val a: 1 +//│ } +//│ // Prelude +//│ let res; +//│ class TypingUnit { +//│ #C; +//│ constructor() { +//│ } +//│ get C() { +//│ const qualifier = this; +//│ if (this.#C === undefined) { +//│ class C { +//│ #a; +//│ get a() { return this.#a; } +//│ constructor(a) { +//│ this.#a = a; +//│ this.#a = 1; +//│ const a1 = this.#a; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#a]; +//│ } +//│ }; +//│ this.#C = ((a) => Object.freeze(new C(a))); +//│ this.#C.class = C; +//│ this.#C.unapply = C.unapply; +//│ } +//│ return this.#C; +//│ } +//│ } +//│ const typing_unit = new TypingUnit; +//│ globalThis.C = typing_unit.C; +//│ // End of generated code + +// should return 1 +let a = C(2) +a.a +//│ let a: C +//│ 1 +//│ a +//│ = C {} +//│ res +//│ = 1 + +:js +class C2(a: Int, b: Int) { + val a = b + 1 + val b = a + 1 +} +//│ class C2(a: Int, b: Int) { +//│ val a: Int +//│ val b: Int +//│ } +//│ // Prelude +//│ class TypingUnit2 { +//│ #C2; +//│ constructor() { +//│ } +//│ get C2() { +//│ const qualifier = this; +//│ if (this.#C2 === undefined) { +//│ class C2 { +//│ #a; +//│ #b; +//│ get a() { return this.#a; } +//│ get b() { return this.#b; } +//│ constructor(a, b) { +//│ this.#a = a; +//│ this.#b = b; +//│ this.#a = b + 1; +//│ const a1 = this.#a; +//│ this.#b = a1 + 1; +//│ const b1 = this.#b; +//│ } +//│ static +//│ unapply(x) { +//│ return ([ +//│ x.#a, +//│ x.#b +//│ ]); +//│ } +//│ }; +//│ this.#C2 = ((a, b) => Object.freeze(new C2(a, b))); +//│ this.#C2.class = C2; +//│ this.#C2.unapply = C2.unapply; +//│ } +//│ return this.#C2; +//│ } +//│ } +//│ const typing_unit2 = new TypingUnit2; +//│ globalThis.C2 = typing_unit2.C2; +//│ // End of generated code + +let c2 = C2(1, 2) +c2.a +c2.b +//│ let c2: C2 +//│ Int +//│ c2 +//│ = C2 {} +//│ res +//│ = 3 +//│ res +//│ = 4 + +class C3(a: Int) { + val a = 42 + class C4(a: Int) { + val a = 44 + } +} +//│ class C3(a: Int) { +//│ class C4(a: Int) { +//│ val a: 44 +//│ } +//│ val a: 42 +//│ } + +:e +let c3 = C3(1) +let c4 = c3.C4(2) +c3.a +c4.a +//│ ╔══[ERROR] Access to class member not yet supported +//│ ║ l.128: let c4 = c3.C4(2) +//│ ╙── ^^^ +//│ let c3: C3 +//│ let c4: error +//│ error +//│ c3 +//│ = C3 {} +//│ c4 +//│ = C4 {} +//│ res +//│ = 42 +//│ res +//│ = 44 diff --git a/shared/src/test/diff/codegen/IndirectRecursion.mls b/shared/src/test/diff/codegen/IndirectRecursion.mls index 9b82834cfa..1d80ed2240 100644 --- a/shared/src/test/diff/codegen/IndirectRecursion.mls +++ b/shared/src/test/diff/codegen/IndirectRecursion.mls @@ -155,7 +155,7 @@ def z = //│ ((anything -> nothing) -> anything) -> error //│ <: z: //│ (('a -> 'b) -> ('a -> 'b & 'c)) -> 'c -//│ ╔══[ERROR] Subtyping constraint of the form `?a -> ?b <: (forall ?c ?d ?e ?f ?g. ?g -> ?f) -> ?h` exceeded recursion depth limit (250) +//│ ╔══[ERROR] Subtyping constraint of the form `?a -> ?b <: (forall ?c ?d. ?d -> ?c) -> ?e` exceeded recursion depth limit (250) //│ ║ l.154: (fun f -> (fun x -> f (fun v -> (x x) v)) (fun x -> f (fun v -> (x x) v))) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. diff --git a/shared/src/test/diff/codegen/Inheritance.mls b/shared/src/test/diff/codegen/Inheritance.mls index 0f21f69ef1..63c065a121 100644 --- a/shared/src/test/diff/codegen/Inheritance.mls +++ b/shared/src/test/diff/codegen/Inheritance.mls @@ -1,3 +1,4 @@ + :js trait X: { x: int } class Y: X @@ -211,13 +212,13 @@ def checkP x = case x of { P -> true | _ -> false } -//│ checkS: anything -> bool +//│ checkS: anything -> Bool //│ = [Function: checkS] -//│ checkR: anything -> bool +//│ checkR: anything -> Bool //│ = [Function: checkR] -//│ checkQ: anything -> bool +//│ checkQ: anything -> Bool //│ = [Function: checkQ] -//│ checkP: anything -> bool +//│ checkP: anything -> Bool //│ = [Function: checkP] // Should pass all checks. @@ -256,3 +257,4 @@ bar = Bar{} bar.Foo //│ res: 1 //│ = 1 + diff --git a/shared/src/test/diff/codegen/MemberInitShadowing.mls b/shared/src/test/diff/codegen/MemberInitShadowing.mls new file mode 100644 index 0000000000..96682546ec --- /dev/null +++ b/shared/src/test/diff/codegen/MemberInitShadowing.mls @@ -0,0 +1,110 @@ +:NewDefs + + +// * This is a valid use of let-binding shadowing +:js +class A(x0: Int) { + let x1 = x0 + 1 + let x1 = x1 + 1 + log(x1) +} +//│ class A(x0: Int) { +//│ let x1: Int +//│ } +//│ // Prelude +//│ function log(x) { +//│ return console.info(x); +//│ } +//│ let res; +//│ class TypingUnit { +//│ #A; +//│ constructor() { +//│ } +//│ get A() { +//│ const qualifier = this; +//│ if (this.#A === undefined) { +//│ class A { +//│ #x0; +//│ constructor(x0) { +//│ this.#x0 = x0; +//│ const x1 = x0 + 1; +//│ const x11 = x11 + 1; +//│ log(x11); +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#x0]; +//│ } +//│ }; +//│ this.#A = ((x0) => Object.freeze(new A(x0))); +//│ this.#A.class = A; +//│ this.#A.unapply = A.unapply; +//│ } +//│ return this.#A; +//│ } +//│ } +//│ const typing_unit = new TypingUnit; +//│ globalThis.A = typing_unit.A; +//│ // End of generated code + +// FIXME +A(123) +//│ A +//│ res +//│ Runtime error: +//│ ReferenceError: Cannot access 'x11' before initialization + + +// TODO `val` redefinition should be a type error +:js +class A(x0: Int) { + val x1 = x0 + 1 + val x1 = x1 + 1 + log(x1) +} +//│ class A(x0: Int) { +//│ val x1: Int +//│ } +//│ // Prelude +//│ class TypingUnit2 { +//│ #A; +//│ constructor() { +//│ } +//│ get A() { +//│ const qualifier = this; +//│ if (this.#A === undefined) { +//│ class A { +//│ #x0; +//│ #x1; +//│ get x1() { return this.#x1; } +//│ constructor(x0) { +//│ this.#x0 = x0; +//│ this.#x1 = x0 + 1; +//│ const x1 = this.#x1; +//│ this.#x1 = x1 + 1; +//│ const x11 = this.#x1; +//│ log(x11); +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#x0]; +//│ } +//│ }; +//│ this.#A = ((x0) => Object.freeze(new A(x0))); +//│ this.#A.class = A; +//│ this.#A.unapply = A.unapply; +//│ } +//│ return this.#A; +//│ } +//│ } +//│ const typing_unit2 = new TypingUnit2; +//│ globalThis.A = typing_unit2.A; +//│ // End of generated code + +A(123) +//│ A +//│ res +//│ = A {} +//│ // Output +//│ 125 + diff --git a/shared/src/test/diff/codegen/Mixin.mls b/shared/src/test/diff/codegen/Mixin.mls new file mode 100644 index 0000000000..8e60a28e3f --- /dev/null +++ b/shared/src/test/diff/codegen/Mixin.mls @@ -0,0 +1,439 @@ +:NewParser +:NewDefs + +:js +class Add(lhs: E, rhs: E) +class Lit(n: Int) +//│ class Add[E](lhs: E, rhs: E) +//│ class Lit(n: Int) +//│ // Prelude +//│ let res; +//│ class TypingUnit { +//│ #Add; +//│ #Lit; +//│ constructor() { +//│ } +//│ get Add() { +//│ const qualifier = this; +//│ if (this.#Add === undefined) { +//│ class Add { +//│ #lhs; +//│ #rhs; +//│ constructor(lhs, rhs) { +//│ this.#lhs = lhs; +//│ this.#rhs = rhs; +//│ } +//│ static +//│ unapply(x) { +//│ return ([ +//│ x.#lhs, +//│ x.#rhs +//│ ]); +//│ } +//│ }; +//│ this.#Add = ((lhs, rhs) => Object.freeze(new Add(lhs, rhs))); +//│ this.#Add.class = Add; +//│ this.#Add.unapply = Add.unapply; +//│ } +//│ return this.#Add; +//│ } +//│ get Lit() { +//│ const qualifier = this; +//│ if (this.#Lit === undefined) { +//│ class Lit { +//│ #n; +//│ constructor(n) { +//│ this.#n = n; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#n]; +//│ } +//│ }; +//│ this.#Lit = ((n) => Object.freeze(new Lit(n))); +//│ this.#Lit.class = Lit; +//│ this.#Lit.unapply = Lit.unapply; +//│ } +//│ return this.#Lit; +//│ } +//│ } +//│ const typing_unit = new TypingUnit; +//│ globalThis.Add = typing_unit.Add; +//│ globalThis.Lit = typing_unit.Lit; +//│ // End of generated code + +:js +mixin EvalBase { + fun eval(e) = + if e is + Lit(n) then n: Int + Add(l, r) then this.eval(l) + this.eval(r) +} +//│ mixin EvalBase() { +//│ this: {eval: 'a -> Int} +//│ fun eval: (Add['a] | Lit) -> Int +//│ } +//│ // Prelude +//│ class TypingUnit1 { +//│ constructor() { +//│ } +//│ EvalBase(base) { +//│ const qualifier = this; +//│ return (class EvalBase extends base { +//│ constructor(...rest) { +//│ super(...rest); +//│ } +//│ eval(e) { +//│ 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)) : (() => { +//│ throw new Error("non-exhaustive case expression"); +//│ })()); +//│ })()); +//│ } +//│ }); +//│ } +//│ } +//│ const typing_unit1 = new TypingUnit1; +//│ globalThis.EvalBase = ((base) => typing_unit1.EvalBase(base)); +//│ // End of generated code + +:js +class Neg(expr: A) +//│ class Neg[A](expr: A) +//│ // Prelude +//│ class TypingUnit2 { +//│ #Neg; +//│ constructor() { +//│ } +//│ get Neg() { +//│ const qualifier = this; +//│ if (this.#Neg === undefined) { +//│ class Neg { +//│ #expr; +//│ constructor(expr) { +//│ this.#expr = expr; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#expr]; +//│ } +//│ }; +//│ this.#Neg = ((expr) => Object.freeze(new Neg(expr))); +//│ this.#Neg.class = Neg; +//│ this.#Neg.unapply = Neg.unapply; +//│ } +//│ return this.#Neg; +//│ } +//│ } +//│ const typing_unit2 = new TypingUnit2; +//│ globalThis.Neg = typing_unit2.Neg; +//│ // End of generated code + +:js +mixin EvalNeg { + fun eval(e) = + if e is Neg(d) then 0 - this.eval(d) + else super.eval(e) +} +//│ mixin EvalNeg() { +//│ super: {eval: 'a -> 'b} +//│ this: {eval: 'c -> Int} +//│ fun eval: (Neg['c] | Object & 'a & ~#Neg) -> (Int | 'b) +//│ } +//│ // Prelude +//│ class TypingUnit3 { +//│ constructor() { +//│ } +//│ EvalNeg(base) { +//│ const qualifier = this; +//│ return (class EvalNeg extends base { +//│ constructor(...rest) { +//│ super(...rest); +//│ } +//│ eval(e) { +//│ const qualifier1 = this; +//│ return ((() => { +//│ return e instanceof Neg.class ? (([d]) => 0 - qualifier1.eval(d))(Neg.unapply(e)) : super.eval(e); +//│ })()); +//│ } +//│ }); +//│ } +//│ } +//│ const typing_unit3 = new TypingUnit3; +//│ globalThis.EvalNeg = ((base) => typing_unit3.EvalNeg(base)); +//│ // End of generated code + +:js +mixin EvalNegNeg { + fun eval(e) = + if e is Neg(Neg(d)) then this.eval(d) + else super.eval(e) +} +//│ mixin EvalNegNeg() { +//│ super: {eval: (Neg[nothing] | 'a) -> 'b} +//│ this: {eval: 'c -> 'b} +//│ fun eval: (Neg[Neg['c] | Object & ~#Neg] | Object & 'a & ~#Neg) -> 'b +//│ } +//│ // Prelude +//│ class TypingUnit4 { +//│ constructor() { +//│ } +//│ EvalNegNeg(base) { +//│ const qualifier = this; +//│ return (class EvalNegNeg extends base { +//│ constructor(...rest) { +//│ super(...rest); +//│ } +//│ 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); +//│ })()); +//│ } +//│ }); +//│ } +//│ } +//│ const typing_unit4 = new TypingUnit4; +//│ globalThis.EvalNegNeg = ((base) => typing_unit4.EvalNegNeg(base)); +//│ // End of generated code + +:js +module TestLang extends EvalBase, EvalNeg, EvalNegNeg +//│ module TestLang { +//│ fun eval: (Neg['A] | Object & 'a & ~#Neg) -> Int +//│ } +//│ where +//│ 'A <: 'b & (Neg['b] | Object & ~#Neg) +//│ 'b <: Neg['A] | Object & 'a & ~#Neg +//│ 'a <: Add['b] | Lit | Neg['b] +//│ // Prelude +//│ class TypingUnit5 { +//│ #TestLang; +//│ constructor() { +//│ } +//│ get TestLang() { +//│ const qualifier = this; +//│ if (this.#TestLang === undefined) { +//│ class TestLang extends EvalNegNeg(EvalNeg(EvalBase(Object))) { +//│ constructor() { +//│ super(); +//│ } +//│ } +//│ this.#TestLang = new TestLang(); +//│ this.#TestLang.class = TestLang; +//│ } +//│ return this.#TestLang; +//│ } +//│ } +//│ const typing_unit5 = new TypingUnit5; +//│ globalThis.TestLang = typing_unit5.TestLang; +//│ // End of generated code + +fun mk(n) = if n is + 0 then Lit(0) + 1 then Neg(mk(n)) + _ then Add(mk(n), mk(n)) +TestLang.eval(mk(0)) +//│ fun mk: forall 'a. Object -> (Lit | 'a) +//│ Int +//│ where +//│ 'a :> Neg[Lit | 'a] | Add[Lit | 'a] +//│ res +//│ = 0 + + +class Foo(x: Int) +//│ class Foo(x: Int) + +class Bar(x2: Int, y: Int) extends Foo(x2 + y) +//│ class Bar(x2: Int, y: Int) extends Foo + + +mixin AA(a: Int) { +} +//│ mixin AA(a: Int) + +mixin BB {} +//│ mixin BB() + + +class C(x: Int) extends BB +//│ class C(x: Int) + +class D(x: Int) extends AA(x) +//│ class D(x: Int) + +class E(x: Int) extends BB, AA(x) +//│ class E(x: Int) + +:js +mixin Fooo(val x: Int) { fun f = [x, this.x] } +//│ mixin Fooo(x: Int) { +//│ this: {x: 'x} +//│ fun f: [Int, 'x] +//│ } +//│ // Prelude +//│ class TypingUnit14 { +//│ constructor() { +//│ } +//│ Fooo(base) { +//│ const qualifier = this; +//│ return (class Fooo extends base { +//│ #x; +//│ get x() { return this.#x; } +//│ constructor(x, ...rest) { +//│ super(...rest); +//│ this.#x = x; +//│ } +//│ get f() { +//│ const x = this.#x; +//│ const qualifier1 = this; +//│ return ([ +//│ x, +//│ qualifier1.x +//│ ]); +//│ } +//│ }); +//│ } +//│ } +//│ const typing_unit14 = new TypingUnit14; +//│ globalThis.Fooo = ((base) => typing_unit14.Fooo(base)); +//│ // End of generated code + +:js +mixin Bazz(val y: Int) +//│ mixin Bazz(y: Int) +//│ // Prelude +//│ class TypingUnit15 { +//│ constructor() { +//│ } +//│ Bazz(base) { +//│ const qualifier = this; +//│ return (class Bazz extends base { +//│ #y; +//│ get y() { return this.#y; } +//│ constructor(y, ...rest) { +//│ super(...rest); +//│ this.#y = y; +//│ } +//│ }); +//│ } +//│ } +//│ const typing_unit15 = new TypingUnit15; +//│ globalThis.Bazz = ((base) => typing_unit15.Bazz(base)); +//│ // End of generated code + +:js +module Barr extends Fooo(0), Bazz(1) +//│ module Barr { +//│ fun f: [Int, 0] +//│ } +//│ // Prelude +//│ class TypingUnit16 { +//│ #Barr; +//│ constructor() { +//│ } +//│ get Barr() { +//│ const qualifier = this; +//│ if (this.#Barr === undefined) { +//│ class Barr extends Bazz(Fooo(Object)) { +//│ constructor() { +//│ super(1, 0); +//│ } +//│ } +//│ this.#Barr = new Barr(); +//│ this.#Barr.class = Barr; +//│ } +//│ return this.#Barr; +//│ } +//│ } +//│ const typing_unit16 = new TypingUnit16; +//│ globalThis.Barr = typing_unit16.Barr; +//│ // End of generated code + +Barr.x +//│ 0 +//│ res +//│ = 0 + +Barr.y +//│ 1 +//│ res +//│ = 1 + +:e +:ge +mixin Base { + fun x = y +} +//│ ╔══[ERROR] identifier not found: y +//│ ║ l.372: fun x = y +//│ ╙── ^ +//│ mixin Base() { +//│ fun x: error +//│ } +//│ Code generation encountered an error: +//│ unresolved symbol y + + +:re +module Test extends Base +//│ module Test { +//│ fun x: error +//│ } +//│ Runtime error: +//│ ReferenceError: Base is not defined + + +mixin MA(val a: Int) +mixin MB(val b1: Int, val b2: Int) +mixin MC(val c: Int) +//│ mixin MA(a: Int) +//│ mixin MB(b1: Int, b2: Int) +//│ mixin MC(c: Int) + +:js +module MM extends MA(1), MB(2, 3), MC(4) +//│ module MM +//│ // Prelude +//│ class TypingUnit22 { +//│ #MM; +//│ constructor() { +//│ } +//│ get MM() { +//│ const qualifier = this; +//│ if (this.#MM === undefined) { +//│ class MM extends MC(MB(MA(Object))) { +//│ constructor() { +//│ super(4, 2, 3, 1); +//│ } +//│ } +//│ this.#MM = new MM(); +//│ this.#MM.class = MM; +//│ } +//│ return this.#MM; +//│ } +//│ } +//│ const typing_unit22 = new TypingUnit22; +//│ globalThis.MM = typing_unit22.MM; +//│ // End of generated code + +MM.a +MM.b1 +MM.b2 +MM.c +//│ 4 +//│ res +//│ = 1 +//│ res +//│ = 2 +//│ res +//│ = 3 +//│ res +//│ = 4 + diff --git a/shared/src/test/diff/codegen/MixinCapture.mls b/shared/src/test/diff/codegen/MixinCapture.mls new file mode 100644 index 0000000000..cf721dcf60 --- /dev/null +++ b/shared/src/test/diff/codegen/MixinCapture.mls @@ -0,0 +1,71 @@ +:NewDefs + +:js +class Lit(n: Int) +mixin EvalAddLit { + fun eval(e) = + if e is + Lit(n) then n +} +//│ class Lit(n: Int) +//│ mixin EvalAddLit() { +//│ fun eval: Lit -> Int +//│ } +//│ // Prelude +//│ let res; +//│ class TypingUnit { +//│ #Lit; +//│ constructor() { +//│ } +//│ EvalAddLit(base) { +//│ const qualifier = this; +//│ return (class EvalAddLit extends base { +//│ constructor(...rest) { +//│ super(...rest); +//│ } +//│ eval(e) { +//│ return ((() => { +//│ let a; +//│ return (a = e, a instanceof qualifier.Lit.class ? (([n]) => n)(qualifier.Lit.unapply(e)) : (() => { +//│ throw new Error("non-exhaustive case expression"); +//│ })()); +//│ })()); +//│ } +//│ }); +//│ } +//│ get Lit() { +//│ const qualifier = this; +//│ if (this.#Lit === undefined) { +//│ class Lit { +//│ #n; +//│ constructor(n) { +//│ this.#n = n; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#n]; +//│ } +//│ }; +//│ this.#Lit = ((n) => Object.freeze(new Lit(n))); +//│ this.#Lit.class = Lit; +//│ this.#Lit.unapply = Lit.unapply; +//│ } +//│ return this.#Lit; +//│ } +//│ } +//│ const typing_unit = new TypingUnit; +//│ globalThis.Lit = typing_unit.Lit; +//│ globalThis.EvalAddLit = ((base) => typing_unit.EvalAddLit(base)); +//│ // End of generated code + +module TestLang extends EvalAddLit +//│ module TestLang { +//│ fun eval: Lit -> Int +//│ } + +TestLang.eval(Lit(0)) +//│ Int +//│ res +//│ = 0 + + diff --git a/shared/src/test/diff/codegen/Modules.mls b/shared/src/test/diff/codegen/Modules.mls new file mode 100644 index 0000000000..82d6fa856c --- /dev/null +++ b/shared/src/test/diff/codegen/Modules.mls @@ -0,0 +1,89 @@ +:NewDefs + + +module test { // hello + fun a = 1 +} +//│ module test { +//│ fun a: 1 +//│ } + + +:js +fun y = 1 +module Foo { + fun x = y +} +//│ fun y: 1 +//│ module Foo { +//│ fun x: 1 +//│ } +//│ // Prelude +//│ class TypingUnit1 { +//│ #Foo; +//│ constructor() { +//│ } +//│ get Foo() { +//│ const qualifier = this; +//│ if (this.#Foo === undefined) { +//│ class Foo { +//│ constructor() { +//│ } +//│ get x() { +//│ return y(); +//│ } +//│ } +//│ this.#Foo = new Foo(); +//│ this.#Foo.class = Foo; +//│ } +//│ return this.#Foo; +//│ } +//│ } +//│ const typing_unit1 = new TypingUnit1; +//│ globalThis.Foo = typing_unit1.Foo; +//│ // Query 1 +//│ globalThis.y = function y() { +//│ return 1; +//│ }; +//│ // End of generated code + + +:js +module Foo { + fun x = y +} +fun y = 1 +//│ module Foo { +//│ fun x: 1 +//│ } +//│ fun y: 1 +//│ // Prelude +//│ class TypingUnit2 { +//│ #Foo; +//│ constructor() { +//│ } +//│ get Foo() { +//│ const qualifier = this; +//│ if (this.#Foo === undefined) { +//│ class Foo { +//│ constructor() { +//│ } +//│ get x() { +//│ return y1(); +//│ } +//│ } +//│ this.#Foo = new Foo(); +//│ this.#Foo.class = Foo; +//│ } +//│ return this.#Foo; +//│ } +//│ } +//│ const typing_unit2 = new TypingUnit2; +//│ globalThis.Foo = typing_unit2.Foo; +//│ // Query 1 +//│ globalThis.y1 = function y1() { +//│ return 1; +//│ }; +//│ // End of generated code + + diff --git a/shared/src/test/diff/codegen/Nested.mls b/shared/src/test/diff/codegen/Nested.mls new file mode 100644 index 0000000000..10843c74aa --- /dev/null +++ b/shared/src/test/diff/codegen/Nested.mls @@ -0,0 +1,1502 @@ +:NewParser +:NewDefs + +:js +module A { + val a = 42 + class B(x: Int) { + fun b = x + 1 + } +} +//│ module A { +//│ class B(x: Int) { +//│ fun b: Int +//│ } +//│ val a: 42 +//│ } +//│ // Prelude +//│ let res; +//│ class TypingUnit { +//│ #A; +//│ constructor() { +//│ } +//│ get A() { +//│ const qualifier = this; +//│ if (this.#A === undefined) { +//│ class A { +//│ #B; +//│ #a; +//│ get a() { return this.#a; } +//│ constructor() { +//│ this.#a = 42; +//│ const a = this.#a; +//│ } +//│ get B() { +//│ const qualifier1 = this; +//│ if (this.#B === undefined) { +//│ class B { +//│ #x; +//│ constructor(x) { +//│ this.#x = x; +//│ } +//│ get b() { +//│ const x = this.#x; +//│ return x + 1; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#x]; +//│ } +//│ }; +//│ this.#B = ((x) => Object.freeze(new B(x))); +//│ this.#B.class = B; +//│ this.#B.unapply = B.unapply; +//│ } +//│ return this.#B; +//│ } +//│ } +//│ this.#A = new A(); +//│ this.#A.class = A; +//│ } +//│ return this.#A; +//│ } +//│ } +//│ const typing_unit = new TypingUnit; +//│ globalThis.A = typing_unit.A; +//│ // End of generated code + +:e +:js +let bb = A.B(A.a) +bb.b +//│ ╔══[ERROR] Access to class member not yet supported +//│ ║ l.70: let bb = A.B(A.a) +//│ ╙── ^^ +//│ let bb: error +//│ error +//│ // Prelude +//│ class TypingUnit1 {} +//│ const typing_unit1 = new TypingUnit1; +//│ // Query 1 +//│ globalThis.bb = A.B(A.a); +//│ // Query 2 +//│ res = bb.b; +//│ // End of generated code +//│ bb +//│ = B {} +//│ res +//│ = 43 + + +:e +class B(x: Int) { + val outer = 42 + class C(y: Int) { + val outer1 = outer + outer + } + class D(val outer: Int) +} +let b = B(1) +b.outer +let c = b.C(1) +c.outer1 +let d = b.D(1) +d.outer +//│ ╔══[ERROR] Access to class member not yet supported +//│ ║ l.101: let c = b.C(1) +//│ ╙── ^^ +//│ ╔══[ERROR] Access to class member not yet supported +//│ ║ l.103: let d = b.D(1) +//│ ╙── ^^ +//│ class B(x: Int) { +//│ class C(y: Int) { +//│ val outer1: Int +//│ } +//│ class D(outer: Int) +//│ val outer: 42 +//│ } +//│ let b: B +//│ let c: error +//│ let d: error +//│ error +//│ b +//│ = B {} +//│ res +//│ = 42 +//│ c +//│ = C {} +//│ res +//│ = 84 +//│ d +//│ = D {} +//│ res +//│ = 1 + +:js +mixin C() { + mixin D() { + mixin E() {} + } +} +//│ mixin C() { +//│ mixin D() { +//│ mixin E() +//│ } +//│ } +//│ // Prelude +//│ class TypingUnit3 { +//│ constructor() { +//│ } +//│ C(base) { +//│ const qualifier = this; +//│ return (class C extends base { +//│ constructor(...rest) { +//│ super(...rest); +//│ } +//│ D(base) { +//│ const qualifier1 = this; +//│ return (class D extends base { +//│ constructor(...rest) { +//│ super(...rest); +//│ } +//│ E(base) { +//│ const qualifier2 = this; +//│ return (class E extends base { +//│ constructor(...rest) { +//│ super(...rest); +//│ } +//│ }); +//│ } +//│ }); +//│ } +//│ }); +//│ } +//│ } +//│ const typing_unit3 = new TypingUnit3; +//│ globalThis.C = ((base) => typing_unit3.C(base)); +//│ // End of generated code + +:js +module D { + class E(val x: Int) {} + fun createE(x: Int) = E(x + 1) +} +//│ module D { +//│ class E(x: Int) +//│ fun createE: (x: Int) -> E +//│ } +//│ // Prelude +//│ class TypingUnit4 { +//│ #D; +//│ constructor() { +//│ } +//│ get D() { +//│ const qualifier = this; +//│ if (this.#D === undefined) { +//│ class D { +//│ #E; +//│ constructor() { +//│ } +//│ createE(x) { +//│ const qualifier1 = this; +//│ return qualifier1.E(x + 1); +//│ } +//│ get E() { +//│ const qualifier1 = this; +//│ if (this.#E === undefined) { +//│ class E { +//│ #x; +//│ get x() { return this.#x; } +//│ constructor(x) { +//│ this.#x = x; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#x]; +//│ } +//│ }; +//│ this.#E = ((x) => Object.freeze(new E(x))); +//│ this.#E.class = E; +//│ this.#E.unapply = E.unapply; +//│ } +//│ return this.#E; +//│ } +//│ } +//│ this.#D = new D(); +//│ this.#D.class = D; +//│ } +//│ return this.#D; +//│ } +//│ } +//│ const typing_unit4 = new TypingUnit4; +//│ globalThis.D = typing_unit4.D; +//│ // End of generated code + +:js +let ee = D.createE(42) +ee.x +//│ let ee: E +//│ Int +//│ // Prelude +//│ class TypingUnit5 {} +//│ const typing_unit5 = new TypingUnit5; +//│ // Query 1 +//│ globalThis.ee = D.createE(42); +//│ // Query 2 +//│ res = ee.x; +//│ // End of generated code +//│ ee +//│ = E {} +//│ res +//│ = 43 + +:js +class E(x: Int) { + class F(y: Int) { + fun sum = x + y + class G(z: Int) { + fun sum = x + y + z + } + } +} +//│ class E(x: Int) { +//│ class F(y: Int) { +//│ class G(z: Int) { +//│ fun sum: Int +//│ } +//│ fun sum: Int +//│ } +//│ } +//│ // Prelude +//│ class TypingUnit6 { +//│ #E; +//│ constructor() { +//│ } +//│ get E() { +//│ const qualifier = this; +//│ if (this.#E === undefined) { +//│ class E { +//│ #F; +//│ #x; +//│ constructor(x) { +//│ this.#x = x; +//│ } +//│ get F() { +//│ const qualifier1 = this; +//│ if (this.#F === undefined) { +//│ class F { +//│ #G; +//│ #y; +//│ constructor(y) { +//│ this.#y = y; +//│ } +//│ get sum() { +//│ const y = this.#y; +//│ return qualifier1.#x + y; +//│ } +//│ get G() { +//│ const qualifier2 = this; +//│ if (this.#G === undefined) { +//│ class G { +//│ #z; +//│ constructor(z) { +//│ this.#z = z; +//│ } +//│ get sum() { +//│ const z = this.#z; +//│ return qualifier1.#x + qualifier2.#y + z; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#z]; +//│ } +//│ }; +//│ this.#G = ((z) => Object.freeze(new G(z))); +//│ this.#G.class = G; +//│ this.#G.unapply = G.unapply; +//│ } +//│ return this.#G; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#y]; +//│ } +//│ }; +//│ this.#F = ((y) => Object.freeze(new F(y))); +//│ this.#F.class = F; +//│ this.#F.unapply = F.unapply; +//│ } +//│ return this.#F; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#x]; +//│ } +//│ }; +//│ this.#E = ((x) => Object.freeze(new E(x))); +//│ this.#E.class = E; +//│ this.#E.unapply = E.unapply; +//│ } +//│ return this.#E; +//│ } +//│ } +//│ const typing_unit6 = new TypingUnit6; +//│ globalThis.E = typing_unit6.E; +//│ // End of generated code + +:e +:js +let es = E(1) +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) +//│ ╙── ^^ +//│ let es: E +//│ let fff: error +//│ let gg: error +//│ error +//│ // Prelude +//│ class TypingUnit7 {} +//│ const typing_unit7 = new TypingUnit7; +//│ // Query 1 +//│ globalThis.es = E(1); +//│ // Query 2 +//│ globalThis.fff = es.F(2); +//│ // Query 3 +//│ globalThis.gg = fff.G(3); +//│ // Query 4 +//│ res = gg.sum; +//│ // End of generated code +//│ es +//│ = E {} +//│ fff +//│ = F {} +//│ gg +//│ = G {} +//│ res +//│ = 6 + +:js +class F() { + let x = 42 + class G() { + let x1 = x + 1 + } +} +//│ class F() { +//│ class G() { +//│ let x1: Int +//│ } +//│ let x: 42 +//│ } +//│ // Prelude +//│ class TypingUnit8 { +//│ #F; +//│ constructor() { +//│ } +//│ get F() { +//│ const qualifier = this; +//│ if (this.#F === undefined) { +//│ class F { +//│ #G; +//│ #x; +//│ constructor() { +//│ this.#x = 42; +//│ const x = this.#x; +//│ } +//│ get G() { +//│ const qualifier1 = this; +//│ if (this.#G === undefined) { +//│ class G { +//│ constructor() { +//│ const x1 = qualifier1.#x + 1; +//│ } +//│ static +//│ unapply(x) { +//│ return []; +//│ } +//│ }; +//│ this.#G = (() => Object.freeze(new G())); +//│ this.#G.class = G; +//│ this.#G.unapply = G.unapply; +//│ } +//│ return this.#G; +//│ } +//│ static +//│ unapply(x) { +//│ return []; +//│ } +//│ }; +//│ this.#F = (() => Object.freeze(new F())); +//│ this.#F.class = F; +//│ this.#F.unapply = F.unapply; +//│ } +//│ return this.#F; +//│ } +//│ } +//│ const typing_unit8 = new TypingUnit8; +//│ globalThis.F = typing_unit8.F; +//│ // End of generated code + + +:js +module G { + class I(val x: Int) {} + module H { + fun i1(x: Int) = I(x + 1) + class J(x: Int) { + fun ii(a: Int) = I(x + a) + } + } +} +//│ module G { +//│ module H { +//│ class J(x: Int) { +//│ fun ii: (a: Int) -> I +//│ } +//│ fun i1: (x: Int) -> I +//│ } +//│ class I(x: Int) +//│ } +//│ // Prelude +//│ class TypingUnit9 { +//│ #G; +//│ constructor() { +//│ } +//│ get G() { +//│ const qualifier = this; +//│ if (this.#G === undefined) { +//│ class G { +//│ #I; +//│ #H; +//│ constructor() { +//│ } +//│ get H() { +//│ const qualifier1 = this; +//│ if (this.#H === undefined) { +//│ class H { +//│ #J; +//│ constructor() { +//│ } +//│ i1(x) { +//│ return qualifier1.I(x + 1); +//│ } +//│ get J() { +//│ const qualifier2 = this; +//│ if (this.#J === undefined) { +//│ class J { +//│ #x; +//│ constructor(x) { +//│ this.#x = x; +//│ } +//│ ii(a) { +//│ const x = this.#x; +//│ return qualifier1.I(x + a); +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#x]; +//│ } +//│ }; +//│ this.#J = ((x) => Object.freeze(new J(x))); +//│ this.#J.class = J; +//│ this.#J.unapply = J.unapply; +//│ } +//│ return this.#J; +//│ } +//│ } +//│ this.#H = new H(); +//│ this.#H.class = H; +//│ } +//│ return this.#H; +//│ } +//│ get I() { +//│ const qualifier1 = this; +//│ if (this.#I === undefined) { +//│ class I { +//│ #x; +//│ get x() { return this.#x; } +//│ constructor(x) { +//│ this.#x = x; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#x]; +//│ } +//│ }; +//│ this.#I = ((x) => Object.freeze(new I(x))); +//│ this.#I.class = I; +//│ this.#I.unapply = I.unapply; +//│ } +//│ return this.#I; +//│ } +//│ } +//│ this.#G = new G(); +//│ this.#G.class = G; +//│ } +//│ return this.#G; +//│ } +//│ } +//│ const typing_unit9 = new TypingUnit9; +//│ globalThis.G = typing_unit9.G; +//│ // End of generated code + + +:e +:js +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) +//│ ╙── ^^ +//│ let jj: error +//│ let i: error +//│ error +//│ // Prelude +//│ class TypingUnit10 {} +//│ const typing_unit10 = new TypingUnit10; +//│ // Query 1 +//│ globalThis.jj = G.H.J(42); +//│ // Query 2 +//│ globalThis.i = jj.ii(2); +//│ // Query 3 +//│ res = i.x; +//│ // End of generated code +//│ jj +//│ = J {} +//│ i +//│ = I {} +//│ res +//│ = 44 + +:js +module H { + class I(val x: Int) + class J(x: Int) { + val i = I(x + 1) + } +} +//│ module H { +//│ class I(x: Int) +//│ class J(x: Int) { +//│ val i: I +//│ } +//│ } +//│ // Prelude +//│ class TypingUnit11 { +//│ #H; +//│ constructor() { +//│ } +//│ get H() { +//│ const qualifier = this; +//│ if (this.#H === undefined) { +//│ class H { +//│ #I; +//│ #J; +//│ constructor() { +//│ } +//│ get I() { +//│ const qualifier1 = this; +//│ if (this.#I === undefined) { +//│ class I { +//│ #x; +//│ get x() { return this.#x; } +//│ constructor(x) { +//│ this.#x = x; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#x]; +//│ } +//│ }; +//│ this.#I = ((x) => Object.freeze(new I(x))); +//│ this.#I.class = I; +//│ this.#I.unapply = I.unapply; +//│ } +//│ return this.#I; +//│ } +//│ get J() { +//│ const qualifier1 = this; +//│ if (this.#J === undefined) { +//│ class J { +//│ #x; +//│ #i; +//│ get i() { return this.#i; } +//│ constructor(x) { +//│ this.#x = x; +//│ this.#i = qualifier1.I(x + 1); +//│ const i = this.#i; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#x]; +//│ } +//│ }; +//│ this.#J = ((x) => Object.freeze(new J(x))); +//│ this.#J.class = J; +//│ this.#J.unapply = J.unapply; +//│ } +//│ return this.#J; +//│ } +//│ } +//│ this.#H = new H(); +//│ this.#H.class = H; +//│ } +//│ return this.#H; +//│ } +//│ } +//│ const typing_unit11 = new TypingUnit11; +//│ globalThis.H = typing_unit11.H; +//│ // End of generated code + + +:e +:js +let j = H.J(42) +j.i.x +//│ ╔══[ERROR] Access to class member not yet supported +//│ ║ l.658: let j = H.J(42) +//│ ╙── ^^ +//│ let j: error +//│ error +//│ // Prelude +//│ class TypingUnit12 {} +//│ const typing_unit12 = new TypingUnit12; +//│ // Query 1 +//│ globalThis.j = H.J(42); +//│ // Query 2 +//│ res = j.i.x; +//│ // End of generated code +//│ j +//│ = J {} +//│ res +//│ = 43 + +:js +:e +class I(x: Int) { + let y = x + 1 + class J(x: Int) { + let y = x + 2 + fun incY = y + 1 + } +} +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) +//│ ╙── ^^ +//│ class I(x: Int) { +//│ class J(x: Int) { +//│ fun incY: Int +//│ let y: Int +//│ } +//│ let y: Int +//│ } +//│ let i: I +//│ let ij: error +//│ error +//│ // Prelude +//│ class TypingUnit13 { +//│ #I; +//│ constructor() { +//│ } +//│ get I() { +//│ const qualifier = this; +//│ if (this.#I === undefined) { +//│ class I { +//│ #J; +//│ #x; +//│ constructor(x) { +//│ this.#x = x; +//│ const y = x + 1; +//│ } +//│ get J() { +//│ const qualifier1 = this; +//│ if (this.#J === undefined) { +//│ class J { +//│ #y; +//│ #x; +//│ constructor(x) { +//│ this.#x = x; +//│ this.#y = x + 2; +//│ const y = this.#y; +//│ } +//│ get incY() { +//│ const x = this.#x; +//│ const qualifier2 = this; +//│ return qualifier2.#y + 1; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#x]; +//│ } +//│ }; +//│ this.#J = ((x) => Object.freeze(new J(x))); +//│ this.#J.class = J; +//│ this.#J.unapply = J.unapply; +//│ } +//│ return this.#J; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#x]; +//│ } +//│ }; +//│ this.#I = ((x) => Object.freeze(new I(x))); +//│ this.#I.class = I; +//│ this.#I.unapply = I.unapply; +//│ } +//│ return this.#I; +//│ } +//│ } +//│ const typing_unit13 = new TypingUnit13; +//│ globalThis.I = typing_unit13.I; +//│ // Query 1 +//│ globalThis.i1 = I(1); +//│ // Query 2 +//│ globalThis.ij = i1.J(0); +//│ // Query 3 +//│ res = ij.incY; +//│ // End of generated code +//│ i +//│ = I {} +//│ ij +//│ = J {} +//│ res +//│ = 3 + + +:js +module J { + class K(x: Int) {} + mixin L() {} + class M() extends K(1) {} + class N(x2: Int) extends K(x2 + 2), L +} +//│ module J { +//│ class K(x: Int) +//│ mixin L() +//│ class M() extends K +//│ class N(x2: Int) extends K +//│ } +//│ // Prelude +//│ class TypingUnit14 { +//│ #J; +//│ constructor() { +//│ } +//│ get J() { +//│ const qualifier = this; +//│ if (this.#J === undefined) { +//│ class J { +//│ #K; +//│ #M; +//│ #N; +//│ constructor() { +//│ } +//│ L(base) { +//│ const qualifier1 = this; +//│ return (class L extends base { +//│ constructor(...rest) { +//│ super(...rest); +//│ } +//│ }); +//│ } +//│ get K() { +//│ const qualifier1 = this; +//│ if (this.#K === undefined) { +//│ class K { +//│ #x; +//│ constructor(x) { +//│ this.#x = x; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#x]; +//│ } +//│ }; +//│ this.#K = ((x) => Object.freeze(new K(x))); +//│ this.#K.class = K; +//│ this.#K.unapply = K.unapply; +//│ } +//│ return this.#K; +//│ } +//│ get M() { +//│ const qualifier1 = this; +//│ if (this.#M === undefined) { +//│ class M extends qualifier1.K.class { +//│ constructor() { +//│ super(1); +//│ } +//│ static +//│ unapply(x) { +//│ return []; +//│ } +//│ }; +//│ this.#M = (() => Object.freeze(new M())); +//│ this.#M.class = M; +//│ this.#M.unapply = M.unapply; +//│ } +//│ return this.#M; +//│ } +//│ get N() { +//│ const qualifier1 = this; +//│ if (this.#N === undefined) { +//│ class N extends qualifier1.L(qualifier1.K.class) { +//│ #x2; +//│ constructor(x2) { +//│ super(x2 + 2); +//│ this.#x2 = x2; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#x2]; +//│ } +//│ }; +//│ this.#N = ((x2) => Object.freeze(new N(x2))); +//│ this.#N.class = N; +//│ this.#N.unapply = N.unapply; +//│ } +//│ return this.#N; +//│ } +//│ } +//│ this.#J = new J(); +//│ this.#J.class = J; +//│ } +//│ return this.#J; +//│ } +//│ } +//│ const typing_unit14 = new TypingUnit14; +//│ globalThis.J = typing_unit14.J; +//│ // End of generated code + +:e +:js +let m = J.M() +let n = J.N(2) +//│ ╔══[ERROR] Access to class member not yet supported +//│ ║ l.879: let m = J.M() +//│ ╙── ^^ +//│ ╔══[ERROR] Access to class member not yet supported +//│ ║ l.880: let n = J.N(2) +//│ ╙── ^^ +//│ let m: error +//│ let n: error +//│ // Prelude +//│ class TypingUnit15 {} +//│ const typing_unit15 = new TypingUnit15; +//│ // Query 1 +//│ globalThis.m = J.M(); +//│ // Query 2 +//│ globalThis.n = J.N(2); +//│ // End of generated code +//│ m +//│ = M {} +//│ n +//│ = N {} + + +module K { + let x = 1 + module L { + let x = 42 + class M() { + fun f = x + } + } +} +//│ module K { +//│ module L { +//│ class M() { +//│ fun f: 42 +//│ } +//│ let x: 42 +//│ } +//│ let x: 1 +//│ } + +:e +let m = K.L.M() +m.f +//│ ╔══[ERROR] Access to module member not yet supported +//│ ║ l.923: let m = K.L.M() +//│ ╙── ^^ +//│ let m: error +//│ error +//│ m +//│ = M {} +//│ res +//│ = 42 + +module L { + class M(val x: Int) {} + module N { + module O { + class P(y: Int) extends M(y + 1) {} + } + } +} +//│ module L { +//│ class M(x: Int) +//│ module N { +//│ module O { +//│ class P(y: Int) extends M +//│ } +//│ } +//│ } + +:e +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) +//│ ╙── ^^ +//│ let op: error +//│ error +//│ op +//│ = P {} +//│ res +//│ = 1 + +:js +:e +module M { + module N { + fun op(x) = if x is + O then 0 + P then 1 + _ then 2 + } + class O() + class P() + fun op(x) = if x is + O then 0 + P then 1 + _ then 2 +} +M.N.op(M.P()) +//│ ╔══[ERROR] Access to module member not yet supported +//│ ║ l.981: M.N.op(M.P()) +//│ ╙── ^^ +//│ ╔══[ERROR] Access to class member not yet supported +//│ ║ l.981: M.N.op(M.P()) +//│ ╙── ^^ +//│ module M { +//│ module N { +//│ fun op: Object -> (0 | 1 | 2) +//│ } +//│ class O() +//│ class P() +//│ fun op: Object -> (0 | 1 | 2) +//│ } +//│ error +//│ // Prelude +//│ class TypingUnit20 { +//│ #M; +//│ constructor() { +//│ } +//│ get M() { +//│ const qualifier = this; +//│ if (this.#M === undefined) { +//│ class M { +//│ #O; +//│ #P; +//│ #N; +//│ constructor() { +//│ } +//│ op(x) { +//│ let a; +//│ const qualifier1 = this; +//│ return a = x, a instanceof qualifier1.O.class ? 0 : a instanceof qualifier1.P.class ? 1 : 2; +//│ } +//│ get N() { +//│ const qualifier1 = this; +//│ if (this.#N === undefined) { +//│ class N { +//│ constructor() { +//│ } +//│ op(x) { +//│ let a; +//│ return a = x, a instanceof qualifier1.O.class ? 0 : a instanceof qualifier1.P.class ? 1 : 2; +//│ } +//│ } +//│ this.#N = new N(); +//│ this.#N.class = N; +//│ } +//│ return this.#N; +//│ } +//│ get O() { +//│ const qualifier1 = this; +//│ if (this.#O === undefined) { +//│ class O { +//│ constructor() { +//│ } +//│ static +//│ unapply(x) { +//│ return []; +//│ } +//│ }; +//│ this.#O = (() => Object.freeze(new O())); +//│ this.#O.class = O; +//│ this.#O.unapply = O.unapply; +//│ } +//│ return this.#O; +//│ } +//│ get P() { +//│ const qualifier1 = this; +//│ if (this.#P === undefined) { +//│ class P { +//│ constructor() { +//│ } +//│ static +//│ unapply(x) { +//│ return []; +//│ } +//│ }; +//│ this.#P = (() => Object.freeze(new P())); +//│ this.#P.class = P; +//│ this.#P.unapply = P.unapply; +//│ } +//│ return this.#P; +//│ } +//│ } +//│ this.#M = new M(); +//│ this.#M.class = M; +//│ } +//│ return this.#M; +//│ } +//│ } +//│ const typing_unit20 = new TypingUnit20; +//│ globalThis.M = typing_unit20.M; +//│ // Query 1 +//│ res = M.N.op(M.P()); +//│ // End of generated code +//│ res +//│ = 1 + +:js +module N { + module O { + class P() extends Q + } + class Q() +} +//│ module N { +//│ module O { +//│ class P() extends Q +//│ } +//│ class Q() +//│ } +//│ // Prelude +//│ class TypingUnit21 { +//│ #N; +//│ constructor() { +//│ } +//│ get N() { +//│ const qualifier = this; +//│ if (this.#N === undefined) { +//│ class N { +//│ #Q; +//│ #O; +//│ constructor() { +//│ } +//│ get O() { +//│ const qualifier1 = this; +//│ if (this.#O === undefined) { +//│ class O { +//│ #P; +//│ constructor() { +//│ } +//│ get P() { +//│ const qualifier2 = this; +//│ if (this.#P === undefined) { +//│ class P extends qualifier1.Q.class { +//│ constructor() { +//│ super(); +//│ } +//│ static +//│ unapply(x) { +//│ return []; +//│ } +//│ }; +//│ this.#P = (() => Object.freeze(new P())); +//│ this.#P.class = P; +//│ this.#P.unapply = P.unapply; +//│ } +//│ return this.#P; +//│ } +//│ } +//│ this.#O = new O(); +//│ this.#O.class = O; +//│ } +//│ return this.#O; +//│ } +//│ get Q() { +//│ const qualifier1 = this; +//│ if (this.#Q === undefined) { +//│ class Q { +//│ constructor() { +//│ } +//│ static +//│ unapply(x) { +//│ return []; +//│ } +//│ }; +//│ this.#Q = (() => Object.freeze(new Q())); +//│ this.#Q.class = Q; +//│ this.#Q.unapply = Q.unapply; +//│ } +//│ return this.#Q; +//│ } +//│ } +//│ this.#N = new N(); +//│ this.#N.class = N; +//│ } +//│ return this.#N; +//│ } +//│ } +//│ const typing_unit21 = new TypingUnit21; +//│ globalThis.N = typing_unit21.N; +//│ // End of generated code + +:e +N.O.P() +//│ ╔══[ERROR] Access to module member not yet supported +//│ ║ l.1167: N.O.P() +//│ ╙── ^^ +//│ error +//│ res +//│ = P {} + +:js +:e +class I(x: Int) { + let y = x + 1 + class J(z: Int) { + val a = [x, y, z] + } +} +I(1).J(3).a +//│ ╔══[ERROR] Access to class member not yet supported +//│ ║ l.1183: I(1).J(3).a +//│ ╙── ^^ +//│ class I(x: Int) { +//│ class J(z: Int) { +//│ val a: [Int, Int, Int] +//│ } +//│ let y: Int +//│ } +//│ error +//│ // Prelude +//│ class TypingUnit23 { +//│ #I; +//│ constructor() { +//│ } +//│ get I() { +//│ const qualifier = this; +//│ if (this.#I === undefined) { +//│ class I { +//│ #J; +//│ #y; +//│ #x; +//│ constructor(x) { +//│ this.#x = x; +//│ this.#y = x + 1; +//│ const y = this.#y; +//│ } +//│ get J() { +//│ const qualifier1 = this; +//│ if (this.#J === undefined) { +//│ class J { +//│ #z; +//│ #a; +//│ get a() { return this.#a; } +//│ constructor(z) { +//│ this.#z = z; +//│ this.#a = [ +//│ qualifier1.#x, +//│ qualifier1.#y, +//│ z +//│ ]; +//│ const a = this.#a; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#z]; +//│ } +//│ }; +//│ this.#J = ((z) => Object.freeze(new J(z))); +//│ this.#J.class = J; +//│ this.#J.unapply = J.unapply; +//│ } +//│ return this.#J; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#x]; +//│ } +//│ }; +//│ this.#I = ((x) => Object.freeze(new I(x))); +//│ this.#I.class = I; +//│ this.#I.unapply = I.unapply; +//│ } +//│ return this.#I; +//│ } +//│ } +//│ const typing_unit23 = new TypingUnit23; +//│ globalThis.I = typing_unit23.I; +//│ // Query 1 +//│ res = I(1).J(3).a; +//│ // End of generated code +//│ res +//│ = [ 1, 2, 3 ] + + +:js +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 +//│ fun main: (x: Int) -> Int +//│ // Prelude +//│ class TypingUnit24 {} +//│ const typing_unit24 = new TypingUnit24; +//│ // Query 1 +//│ globalThis.main = function main() { +//│ return ((() => { +//│ let f = (x) => x === 0 ? 1 : g(x - 1); +//│ let g = (x) => f(x); +//│ return f; +//│ })()); +//│ }; +//│ // End of generated code + +:js +fun mian = + class A(x: Int) + mixin B() + module C + A(42) +//│ fun mian: A +//│ // Prelude +//│ class TypingUnit25 {} +//│ const typing_unit25 = new TypingUnit25; +//│ // Query 1 +//│ globalThis.mian = function mian() { +//│ return ((() => { +//│ const A = (() => { +//│ class A { +//│ #x; +//│ constructor(x) { +//│ this.#x = x; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#x]; +//│ } +//│ } +//│ let ctor; +//│ ctor = ((x) => new A(x)); +//│ ctor.class = A; +//│ return ctor; +//│ })(); +//│ const B = (base) => { +//│ return (class B extends base { +//│ constructor(...rest) { +//│ super(...rest); +//│ } +//│ }); +//│ }; +//│ const C = (() => { +//│ class C {} +//│ let ins; +//│ ins = new C(); +//│ ins.class = C; +//│ return ins; +//│ })(); +//│ return A(42); +//│ })()); +//│ }; +//│ // End of generated code + +:js +fun mian = + mixin B() + class A(x: Int) extends B + module C extends B + [A, C] +//│ fun mian: [(x: Int) -> A, C] +//│ // Prelude +//│ class TypingUnit26 {} +//│ const typing_unit26 = new TypingUnit26; +//│ // Query 1 +//│ globalThis.mian1 = function mian1() { +//│ return ((() => { +//│ const B = (base) => { +//│ return (class B extends base { +//│ constructor(...rest) { +//│ super(...rest); +//│ } +//│ }); +//│ }; +//│ const A = (() => { +//│ class A extends B(Object) { +//│ #x; +//│ constructor(x) { +//│ super(); +//│ this.#x = x; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#x]; +//│ } +//│ } +//│ let ctor; +//│ ctor = ((x) => new A(x)); +//│ ctor.class = A; +//│ return ctor; +//│ })(); +//│ const C = (() => { +//│ class C extends B(Object) { +//│ constructor() { +//│ super(); +//│ } +//│ } +//│ let ins; +//│ ins = new C(); +//│ ins.class = C; +//│ return ins; +//│ })(); +//│ return ([ +//│ A, +//│ C +//│ ]); +//│ })()); +//│ }; +//│ // End of generated code + +:js +fun main(arg) = + let x = arg + 1 + let foo(y) = x + y + class C(u: Int) { fun z = [foo(u), bar] } + let bar = x + C(123) +//│ fun main: Int -> C +//│ // Prelude +//│ class TypingUnit27 {} +//│ const typing_unit27 = new TypingUnit27; +//│ // Query 1 +//│ globalThis.main1 = function main1(arg) { +//│ return ((() => { +//│ let x = arg + 1; +//│ let foo = (y) => x + y; +//│ const C = (() => { +//│ class C { +//│ #u; +//│ constructor(u) { +//│ this.#u = u; +//│ } +//│ get z() { +//│ const u = this.#u; +//│ return ([ +//│ foo(u), +//│ bar +//│ ]); +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#u]; +//│ } +//│ } +//│ let ctor; +//│ ctor = ((u) => new C(u)); +//│ ctor.class = C; +//│ return ctor; +//│ })(); +//│ let bar = x; +//│ return C(123); +//│ })()); +//│ }; +//│ // End of generated code + +module Test { + log(0) + module Foo { log(2) } + log(1) + discard(Foo) + log(3) + discard(Foo) +} +//│ module Test { +//│ module Foo +//│ } + +:js +class Outer1(outer: Int) { + log(outer) + class Outer2(x: Int) { + let outer = x + 1 + } +} +//│ class Outer1(outer: Int) { +//│ class Outer2(x: Int) { +//│ let outer: Int +//│ } +//│ } +//│ // Prelude +//│ class TypingUnit29 { +//│ #Outer1; +//│ constructor() { +//│ } +//│ get Outer1() { +//│ const qualifier = this; +//│ if (this.#Outer1 === undefined) { +//│ class Outer1 { +//│ #Outer2; +//│ #outer; +//│ constructor(outer) { +//│ this.#outer = outer; +//│ log(outer); +//│ } +//│ get Outer2() { +//│ const qualifier1 = this; +//│ if (this.#Outer2 === undefined) { +//│ class Outer2 { +//│ #x; +//│ constructor(x) { +//│ this.#x = x; +//│ const outer = x + 1; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#x]; +//│ } +//│ }; +//│ this.#Outer2 = ((x) => Object.freeze(new Outer2(x))); +//│ this.#Outer2.class = Outer2; +//│ this.#Outer2.unapply = Outer2.unapply; +//│ } +//│ return this.#Outer2; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#outer]; +//│ } +//│ }; +//│ this.#Outer1 = ((outer) => Object.freeze(new Outer1(outer))); +//│ this.#Outer1.class = Outer1; +//│ this.#Outer1.unapply = Outer1.unapply; +//│ } +//│ return this.#Outer1; +//│ } +//│ } +//│ const typing_unit29 = new TypingUnit29; +//│ globalThis.Outer1 = typing_unit29.Outer1; +//│ // End of generated code diff --git a/shared/src/test/diff/codegen/New.mls b/shared/src/test/diff/codegen/New.mls new file mode 100644 index 0000000000..9909f20df2 --- /dev/null +++ b/shared/src/test/diff/codegen/New.mls @@ -0,0 +1,109 @@ +:NewDefs + + +class C +//│ class C { +//│ constructor() +//│ } + +:js +new C +//│ C +//│ // Prelude +//│ class TypingUnit1 {} +//│ const typing_unit1 = new TypingUnit1; +//│ // Query 1 +//│ res = new C; +//│ // End of generated code +//│ res +//│ = C {} + +:e +:js +C() +//│ ╔══[ERROR] Construction of unparameterized class C should use the `new` keyword +//│ ║ l.23: C() +//│ ╙── ^ +//│ C +//│ // Prelude +//│ class TypingUnit2 {} +//│ const typing_unit2 = new TypingUnit2; +//│ // Query 1 +//│ res = C(); +//│ // End of generated code +//│ res +//│ Runtime error: +//│ TypeError: Class constructor C cannot be invoked without 'new' + +:js +:e // TODO support first-class classes +let c = C +//│ ╔══[ERROR] Construction of unparameterized class C should use the `new` keyword +//│ ║ l.40: let c = C +//│ ╙── ^ +//│ let c: () -> C +//│ // Prelude +//│ class TypingUnit3 {} +//│ const typing_unit3 = new TypingUnit3; +//│ // Query 1 +//│ globalThis.c = C; +//│ // End of generated code +//│ c +//│ = [class C] + +:re // TODO should eventually be reject in type checking +c() +//│ C +//│ res +//│ Runtime error: +//│ TypeError: Class constructor C cannot be invoked without 'new' + + +class C() +//│ class C() + +:js +new C +//│ C +//│ // Prelude +//│ class TypingUnit6 {} +//│ const typing_unit6 = new TypingUnit6; +//│ // Query 1 +//│ res = new C.class; +//│ // End of generated code +//│ res +//│ = C {} + +:js +C() +//│ C +//│ // Prelude +//│ class TypingUnit7 {} +//│ const typing_unit7 = new TypingUnit7; +//│ // Query 1 +//│ res = C(); +//│ // End of generated code +//│ res +//│ = C {} + +:js +let c = C +//│ let c: () -> C +//│ // Prelude +//│ class TypingUnit8 {} +//│ const typing_unit8 = new TypingUnit8; +//│ // Query 1 +//│ globalThis.c1 = C; +//│ // End of generated code +//│ c +//│ = [Function (anonymous)] { +//│ class: [class C], +//│ unapply: [Function: unapply] +//│ } + +c() +//│ C +//│ res +//│ = C {} + + diff --git a/shared/src/test/diff/codegen/NewClasses.mls b/shared/src/test/diff/codegen/NewClasses.mls new file mode 100644 index 0000000000..86617e4b2a --- /dev/null +++ b/shared/src/test/diff/codegen/NewClasses.mls @@ -0,0 +1,13 @@ +:NewDefs + + +class C[A](n: A) { + fun f = g + fun g = 0 +} +//│ class C[A](n: A) { +//│ fun f: 0 +//│ fun g: 0 +//│ } + + diff --git a/shared/src/test/diff/codegen/NewMatching.mls b/shared/src/test/diff/codegen/NewMatching.mls new file mode 100644 index 0000000000..809dcb337d --- /dev/null +++ b/shared/src/test/diff/codegen/NewMatching.mls @@ -0,0 +1,325 @@ +:NewDefs + +class V0() +class V1(val x: Int) +class V2(x: Int, y: Int) +class Pos(x: Int) +class V22(c1: V2, c2: V2) +class Half(invalid: Int, valid: Int) +class None(no: Int) +//│ class V0() +//│ class V1(x: Int) +//│ class V2(x: Int, y: Int) +//│ class Pos(x: Int) +//│ class V22(c1: V2, c2: V2) +//│ class Half(invalid: Int, valid: Int) +//│ class None(no: Int) + +V2.unapply(V2(114, 514)) +//│ [Int, Int] +//│ res +//│ = [ 114, 514 ] + +:js +fun sum(v) = + if v is + V0() then 0 + V1(a) then a + V2(a, b) then a + b + Pos(x) and x > 0 then x + V22(V2(x1, y1), V2(x2, y2)) then x1 + y1 + x2 + y2 + Half(_, x) then x + None(_) then 0 + _ then -1 +//│ fun sum: Object -> Int +//│ // Prelude +//│ class TypingUnit2 {} +//│ const typing_unit2 = new TypingUnit2; +//│ // Query 1 +//│ 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); +//│ })()); +//│ }; +//│ // End of generated code + +sum(V0()) +sum(V1(42)) +sum(V2(1, 1)) +sum(Pos(1)) +sum(Pos(0)) +sum(V22(V2(1, 2), V2(3, 4))) +sum(Half(-1, 1)) +sum(None(42)) +sum(42) +//│ Int +//│ res +//│ = 0 +//│ res +//│ = 42 +//│ res +//│ = 2 +//│ res +//│ = 1 +//│ res +//│ = -1 +//│ res +//│ = 10 +//│ res +//│ = 1 +//│ res +//│ = 0 +//│ res +//│ = -1 + +class Some[out T](val value: T) +//│ class Some[T](value: T) + +:js +fun get1(s) = + if s is + Some(V1(x)) then x + Some(y) then y +//│ fun get1: forall 'a. Some[Object & 'a] -> (Int | 'a) +//│ // Prelude +//│ class TypingUnit5 {} +//│ const typing_unit5 = new TypingUnit5; +//│ // Query 1 +//│ 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)) : (() => { +//│ throw new Error("non-exhaustive case expression"); +//│ })()); +//│ })()); +//│ }; +//│ // End of generated code + +get1(Some(V1(1))) +get1(Some(V0())) +//│ Int | V0 +//│ res +//│ = 1 +//│ res +//│ = V0 {} + +let s2 = Some(1) +//│ let s2: Some[1] +//│ s2 +//│ = Some {} + +:js +fun foo(s) = + if s is + Some(t) then let b = s2.value in b + t.x + _ then 0 +//│ fun foo: (Object & ~#Some | Some[{x: Int}]) -> Int +//│ // Prelude +//│ class TypingUnit8 {} +//│ const typing_unit8 = new TypingUnit8; +//│ // Query 1 +//│ globalThis.foo = function foo(s) { +//│ return ((() => { +//│ return s instanceof Some.class ? (([t]) => ((b) => b + t.x)(s2.value))(Some.unapply(s)) : 0; +//│ })()); +//│ }; +//│ // End of generated code + +foo(Some(V1(12))) +//│ Int +//│ res +//│ = 13 + +fun bar(s) = + if s is + Some(_) then let b = s2.value in b + 1 + _ then 0 +//│ fun bar: (Object & ~#Some | Some[anything]) -> Int + +bar(Some(V1(12))) +//│ Int +//│ res +//│ = 2 + +:js +class FooBar { + val x = 42 +} +//│ class FooBar { +//│ constructor() +//│ val x: 42 +//│ } +//│ // Prelude +//│ class TypingUnit12 { +//│ #FooBar; +//│ constructor() { +//│ } +//│ get FooBar() { +//│ const qualifier = this; +//│ if (this.#FooBar === undefined) { +//│ class FooBar { +//│ #x; +//│ get x() { return this.#x; } +//│ constructor() { +//│ this.#x = 42; +//│ const x = this.#x; +//│ } +//│ }; +//│ this.#FooBar = FooBar; +//│ } +//│ return this.#FooBar; +//│ } +//│ } +//│ const typing_unit12 = new TypingUnit12; +//│ globalThis.FooBar = typing_unit12.FooBar; +//│ // End of generated code + +:js +fun t(x) = + if x is + FooBar then 1 + _ then 0 +//│ fun t: Object -> (0 | 1) +//│ // Prelude +//│ class TypingUnit13 {} +//│ const typing_unit13 = new TypingUnit13; +//│ // Query 1 +//│ globalThis.t = function t(x) { +//│ return ((() => { +//│ return x instanceof FooBar ? 1 : 0; +//│ })()); +//│ }; +//│ // End of generated code + +t(new FooBar()) +//│ 0 | 1 +//│ res +//│ = 1 + +:e +:ge +fun ft(x) = + if x is + FooBar(x) then x + _ then 0 +//│ ╔══[ERROR] class FooBar expects 0 parameter but found 1 parameter +//│ ║ l.219: FooBar(x) then x +//│ ╙── ^^^^^^^^^ +//│ fun ft: anything -> error +//│ Code generation encountered an error: +//│ if expression was not desugared + +:js +module MM +fun m(x) = + if x is + MM then 1 + _ then 0 +//│ module MM +//│ fun m: Object -> (0 | 1) +//│ // Prelude +//│ class TypingUnit16 { +//│ #MM; +//│ constructor() { +//│ } +//│ get MM() { +//│ const qualifier = this; +//│ if (this.#MM === undefined) { +//│ class MM {} +//│ this.#MM = new MM(); +//│ this.#MM.class = MM; +//│ } +//│ return this.#MM; +//│ } +//│ } +//│ const typing_unit16 = new TypingUnit16; +//│ globalThis.MM = typing_unit16.MM; +//│ // Query 1 +//│ globalThis.m = function m(x) { +//│ return ((() => { +//│ return x instanceof MM.class ? 1 : 0; +//│ })()); +//│ }; +//│ // End of generated code + +:e +class VVC(v: Int, vc: Int) +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 +//│ ╙── ^^^^^^^^^^^^ +//│ class VVC(v: Int, vc: Int) +//│ fun c: anything -> error +//│ Code generation encountered an error: +//│ if expression was not desugared + + + +// * Testing polymorphic `unapply` + +class C[A](f: A -> A) +//│ class C[A](f: A -> A) + +let r = C.unapply +//│ let r: forall '#f. (C[?] & {#f: '#f}) -> ['#f] +//│ r +//│ = [Function: unapply] + +let r2 = r(C(succ)) +//│ let r2: [Int -> Int] +//│ r2 +//│ = [ [Function: succ] ] + +let r3 = (([f]) => f) of r2 +//│ let r3: Int -> Int +//│ r3 +//│ = [Function: succ] + +r3(123) +//│ Int +//│ res +//│ = 124 + +let r4 = r(C(id)) +//│ let r4: ['A -> 'A] +//│ r4 +//│ = [ [Function: id] ] + +// * Notice that the type is not generalized (lack of distributivity?) +let g = (([f]) => f) of r4 +//│ let g: 'A -> 'A +//│ g +//│ = [Function: id] + +g(0) +//│ 0 +//│ res +//│ = 0 + +// * Approximated type +g(true) +//│ 0 | true +//│ res +//│ = true + + + diff --git a/shared/src/test/diff/codegen/NewMutualRef.mls b/shared/src/test/diff/codegen/NewMutualRef.mls new file mode 100644 index 0000000000..49b50da288 --- /dev/null +++ b/shared/src/test/diff/codegen/NewMutualRef.mls @@ -0,0 +1,166 @@ +:NewDefs + +:js +fun foo: Int -> Int +fun foo = x => x + 1 // <- This will be added as a value symbol before translating `Bar`. +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 after `PreTyper` +// is finished, when I replacing the old Scope with the new Scope. +// +// Luyu Cheng on 2023/12/30 +(new Bar()).calc(0) +//│ Int +//│ res +//│ = 1 + +: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 +//│ class TypingUnit2 { +//│ #Bar; +//│ constructor() { +//│ } +//│ get Bar() { +//│ const qualifier = this; +//│ if (this.#Bar === undefined) { +//│ class Bar { +//│ constructor() { +//│ } +//│ calc(x) { +//│ return foo1(x); +//│ } +//│ }; +//│ this.#Bar = Bar; +//│ } +//│ return this.#Bar; +//│ } +//│ } +//│ const typing_unit2 = new TypingUnit2; +//│ globalThis.Bar = typing_unit2.Bar; +//│ // Query 1 is empty +//│ // Query 2 +//│ globalThis.foo1 = function foo1(x) { +//│ return x + 1; +//│ }; +//│ // End of generated code + +foo(0) +new Bar().calc(0) +//│ Int +//│ res +//│ = 1 +//│ res +//│ = 1 + +:js +class Bar { fun calc(x) = foo } +fun foo: Int +fun foo = 123 +//│ class Bar { +//│ constructor() +//│ fun calc: anything -> Int +//│ } +//│ fun foo: 123 +//│ fun foo: Int +//│ // Prelude +//│ class TypingUnit4 { +//│ #Bar; +//│ constructor() { +//│ } +//│ get Bar() { +//│ const qualifier = this; +//│ if (this.#Bar === undefined) { +//│ class Bar { +//│ constructor() { +//│ } +//│ calc(x) { +//│ return foo2(); +//│ } +//│ }; +//│ this.#Bar = Bar; +//│ } +//│ return this.#Bar; +//│ } +//│ } +//│ const typing_unit4 = new TypingUnit4; +//│ globalThis.Bar = typing_unit4.Bar; +//│ // Query 1 is empty +//│ // Query 2 +//│ globalThis.foo2 = function foo2() { +//│ return 123; +//│ }; +//│ // End of generated code + +foo +new Bar().calc(0) +//│ Int +//│ res +//│ = 123 +//│ res +//│ = 123 diff --git a/shared/src/test/diff/codegen/NuClasses.mls b/shared/src/test/diff/codegen/NuClasses.mls new file mode 100644 index 0000000000..846c5be0cc --- /dev/null +++ b/shared/src/test/diff/codegen/NuClasses.mls @@ -0,0 +1,190 @@ +:NewDefs + +:js +class Test(val n: Int) { + fun inc = Test(n + 1) +} +//│ class Test(n: Int) { +//│ fun inc: Test +//│ } +//│ // Prelude +//│ let res; +//│ class TypingUnit { +//│ #Test; +//│ constructor() { +//│ } +//│ get Test() { +//│ const qualifier = this; +//│ if (this.#Test === undefined) { +//│ class Test { +//│ #n; +//│ get n() { return this.#n; } +//│ constructor(n) { +//│ this.#n = n; +//│ } +//│ get inc() { +//│ const n = this.#n; +//│ return qualifier.Test(n + 1); +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#n]; +//│ } +//│ }; +//│ this.#Test = ((n) => Object.freeze(new Test(n))); +//│ this.#Test.class = Test; +//│ this.#Test.unapply = Test.unapply; +//│ } +//│ return this.#Test; +//│ } +//│ } +//│ const typing_unit = new TypingUnit; +//│ globalThis.Test = typing_unit.Test; +//│ // End of generated code + +Test(0).inc.n +//│ Int +//│ res +//│ = 1 + +class Test2(val n: Int) { + fun inc = Test3.inc(n) +} +module Test3 { + fun inc(n) = Test2(n + 1) +} +//│ class Test2(n: Int) { +//│ fun inc: Test2 +//│ } +//│ module Test3 { +//│ fun inc: Int -> Test2 +//│ } + +Test2(0).inc.n +//│ Int +//│ res +//│ = 1 + +class C[out A](n: A) { + fun f = g + fun g = n +} +//│ class C[A](n: A) { +//│ fun f: A +//│ fun g: A +//│ } + +:e +let a = C[Int](42) +a.f +//│ ╔══[ERROR] Type application syntax is not yet supported +//│ ║ l.78: let a = C[Int](42) +//│ ╙── ^^^^^^ +//│ let a: C[42] +//│ 42 +//│ a +//│ = C {} +//│ res +//│ = 42 + + +module Foo { + fun f = C0() + class C0() +} +//│ module Foo { +//│ class C0() +//│ fun f: C0 +//│ } + + +mixin M0(n: Int) { + val m = n // this refers to specifically the `n` we had in parameter, not necessarily this.n + fun bar = m + fun foo = [n, m, bar] // should this be the same as `[this.n, this.m, this.bar]`? +} +//│ mixin M0(n: Int) { +//│ fun bar: Int +//│ fun foo: [Int, Int, Int] +//│ val m: Int +//│ } + +module M1 extends M0(123) { + fun n = "n" + fun m = "m" + fun bar = "bar" +} +//│ module M1 { +//│ fun bar: "bar" +//│ fun foo: [Int, Int, Int] +//│ fun m: "m" +//│ fun n: "n" +//│ } + +[M1.n, M1.m, M1.bar] +//│ ["n", "m", "bar"] +//│ res +//│ = [ 'n', 'm', 'bar' ] + +// FIXME typing/runtime mismatch +M1.foo +//│ [Int, Int, Int] +//│ res +//│ = [ 123, 'm', 'bar' ] + +:e +:js +module M2 { + val m = 100 + fun foo(y) = + fun bar(x) = x + y + this.m + bar(10) +} +//│ ╔══[ERROR] Cannot use `val` or `fun` in local block; use `let` instead. +//│ ║ l.140: fun bar(x) = x + y + this.m +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.140: fun bar(x) = x + y + this.m +//│ ╙── ^^ +//│ module M2 { +//│ fun foo: Int -> Int +//│ val m: 100 +//│ } +//│ // Prelude +//│ class TypingUnit11 { +//│ #M2; +//│ constructor() { +//│ } +//│ get M2() { +//│ const qualifier = this; +//│ if (this.#M2 === undefined) { +//│ class M2 { +//│ #m; +//│ get m() { return this.#m; } +//│ constructor() { +//│ this.#m = 100; +//│ const m = this.#m; +//│ } +//│ foo(y) { +//│ const qualifier1 = this; +//│ return ((() => { +//│ let bar = (x) => x + y + qualifier1.m; +//│ return bar(10); +//│ })()); +//│ } +//│ } +//│ this.#M2 = new M2(); +//│ this.#M2.class = M2; +//│ } +//│ return this.#M2; +//│ } +//│ } +//│ const typing_unit11 = new TypingUnit11; +//│ globalThis.M2 = typing_unit11.M2; +//│ // End of generated code + +M2.foo(1) +//│ Int +//│ res +//│ = 111 + diff --git a/shared/src/test/diff/codegen/NuFuns.mls b/shared/src/test/diff/codegen/NuFuns.mls new file mode 100644 index 0000000000..2c29fcf69f --- /dev/null +++ b/shared/src/test/diff/codegen/NuFuns.mls @@ -0,0 +1,68 @@ +:NewDefs + + +:js +fun foo = + let bar(x) = x + 1 + bar(10) +//│ fun foo: Int +//│ // Prelude +//│ let res; +//│ class TypingUnit {} +//│ const typing_unit = new TypingUnit; +//│ // Query 1 +//│ globalThis.foo = function foo() { +//│ return ((() => { +//│ let bar = (x) => x + 1; +//│ return bar(10); +//│ })()); +//│ }; +//│ // End of generated code + +fun foo = + class C(a: Int) { fun bar(x) = a + x + 1 } + C(100).bar(10) +foo +//│ fun foo: Int +//│ Int +//│ res +//│ = 111 + + +fun main = + log("Hello") +main +//│ fun main: () +//│ () +//│ res +//│ = undefined +//│ // Output +//│ Hello + +fun main = + log("Hello") +//│ fun main: () + +main +//│ () +//│ res +//│ = undefined +//│ // Output +//│ Hello + + +fun main = + mixin B { log(1) } + log(0) + module M extends B + log(2) +main +//│ fun main: () +//│ () +//│ res +//│ = undefined +//│ // Output +//│ 0 +//│ 1 +//│ 2 + diff --git a/shared/src/test/diff/codegen/NuParentheses.mls b/shared/src/test/diff/codegen/NuParentheses.mls new file mode 100644 index 0000000000..356a47a2fd --- /dev/null +++ b/shared/src/test/diff/codegen/NuParentheses.mls @@ -0,0 +1,61 @@ +:NewDefs + + +:js +16 / (2 / 2) +//│ Num +//│ // Prelude +//│ let res; +//│ class TypingUnit {} +//│ const typing_unit = new TypingUnit; +//│ // Query 1 +//│ res = 16 / (2 / 2); +//│ // End of generated code +//│ res +//│ = 16 + +:js +1 - (3 - 5) +//│ Int +//│ // Prelude +//│ class TypingUnit1 {} +//│ const typing_unit1 = new TypingUnit1; +//│ // Query 1 +//│ res = 1 - (3 - 5); +//│ // End of generated code +//│ res +//│ = 3 + + +fun (--) minusminus(a, b) = a - b +//│ fun (--) minusminus: (Int, Int) -> Int + +:js +1 -- (3 -- 5) +//│ Int +//│ // Prelude +//│ class TypingUnit3 {} +//│ const typing_unit3 = new TypingUnit3; +//│ // Query 1 +//│ res = minusminus(1, minusminus(3, 5)); +//│ // End of generated code +//│ res +//│ = 3 + + +fun (-+-) complex(a, b) = a - 2*b +//│ fun (-+-) complex: (Int, Int) -> Int + +:js +1 -+- (3 -+- 5) +//│ Int +//│ // Prelude +//│ class TypingUnit5 {} +//│ const typing_unit5 = new TypingUnit5; +//│ // Query 1 +//│ res = complex(1, complex(3, 5)); +//│ // End of generated code +//│ res +//│ = 15 + + diff --git a/shared/src/test/diff/codegen/NuReplHost.mls b/shared/src/test/diff/codegen/NuReplHost.mls new file mode 100644 index 0000000000..d4805d1c82 --- /dev/null +++ b/shared/src/test/diff/codegen/NuReplHost.mls @@ -0,0 +1,50 @@ +:NewDefs + + +// * This should crash due to `error`, +// * but the crash is somehow swallowed and we get the result of the previous statement instead! +// * The same happens with any other side effect, like `log(...)` +// * Note: this doesn't happen if the last line is in a spearate diff-test block +:showRepl +:re +fun foo(x) = error +let r = foo(1) +//│ fun foo: anything -> nothing +//│ let r: nothing +//│ ┌ Block at NuReplHost.mls:10 +//│ ├─┬ Prelude +//│ │ ├── Code +//│ │ │ function error() { +//│ │ │ throw new Error("an error was thrown"); +//│ │ │ } +//│ │ │ let res; +//│ │ │ class TypingUnit {} +//│ │ │ const typing_unit = new TypingUnit; +//│ │ └── Reply +//│ │ undefined +//│ ├─┬ Query 1/2 +//│ │ ├── Prelude: +//│ │ ├── Code: +//│ │ ├── globalThis.foo = function foo(x) { +//│ │ ├── return error(); +//│ │ ├── }; +//│ │ ├── Intermediate: [Function: foo] +//│ │ └── Reply: [success] [Function: foo] +//│ └─┬ Query 2/2 +//│ ├── Prelude: +//│ ├── Code: +//│ ├── globalThis.r = foo(1); +//│ └── Reply: [runtime error] Error: an error was thrown +//│ r +//│ Runtime error: +//│ Error: an error was thrown + +:re +r +//│ nothing +//│ res +//│ Runtime error: +//│ ReferenceError: r is not defined + + + diff --git a/shared/src/test/diff/codegen/ParameterPattern.mls b/shared/src/test/diff/codegen/ParameterPattern.mls index ef4d42dcaa..91203d20e1 100644 --- a/shared/src/test/diff/codegen/ParameterPattern.mls +++ b/shared/src/test/diff/codegen/ParameterPattern.mls @@ -13,7 +13,7 @@ def volatile ((this, break)) = this + break //│ return this$ + break$; //│ }; //│ // End of generated code -//│ volatile: (int, int,) -> int +//│ volatile: ((int, int,),) -> int //│ = [Function: volatile1] :js diff --git a/shared/src/test/diff/codegen/ReplHost.mls b/shared/src/test/diff/codegen/ReplHost.mls index 00687d025d..3da3fe7697 100644 --- a/shared/src/test/diff/codegen/ReplHost.mls +++ b/shared/src/test/diff/codegen/ReplHost.mls @@ -1,5 +1,5 @@ -:ShowRepl +:showRepl class Box[T]: { inner: T } method Map: (T -> 'a) -> Box['a] method Map f = Box { inner = f this.inner } @@ -37,7 +37,7 @@ box0 = Box { inner = 0 } //│ box0: Box[0] //│ = Box { inner: 0 } -:ShowRepl +:showRepl box1 = Box { inner = 1 } //│ ┌ Block at ReplHost.mls:41 //│ ├── No prelude @@ -50,7 +50,7 @@ box1 = Box { inner = 1 } //│ box1: Box[1] //│ = Box { inner: 1 } -:ShowRepl +:showRepl case box1 of { Box -> 0 } //│ ┌ Block at ReplHost.mls:54 //│ ├── No prelude @@ -66,7 +66,7 @@ case box1 of { Box -> 0 } //│ res: 0 //│ = 0 -:ShowRepl +:showRepl box1.Map (fun x -> add x 1) box1.Map (fun x -> add x 2) box1.Map (fun x -> Box { inner = x }) diff --git a/shared/src/test/diff/codegen/Shadowing.mls b/shared/src/test/diff/codegen/Shadowing.mls new file mode 100644 index 0000000000..7dd871dbb4 --- /dev/null +++ b/shared/src/test/diff/codegen/Shadowing.mls @@ -0,0 +1,23 @@ +:NewDefs + + +:js +x => + x => + x + 1 +//│ anything -> Int -> Int +//│ // Prelude +//│ let res; +//│ class TypingUnit {} +//│ const typing_unit = new TypingUnit; +//│ // Query 1 +//│ res = ((x) => (() => { +//│ return ((x) => (() => { +//│ return x + 1; +//│ })()); +//│ })()); +//│ // End of generated code +//│ res +//│ = [Function: res] + + diff --git a/shared/src/test/diff/codegen/Super.mls b/shared/src/test/diff/codegen/Super.mls new file mode 100644 index 0000000000..18cd9ac2a2 --- /dev/null +++ b/shared/src/test/diff/codegen/Super.mls @@ -0,0 +1,165 @@ +:NewDefs + + + +:js +mixin Foo0 { + val foo0 = 0 +} +//│ mixin Foo0() { +//│ val foo0: 0 +//│ } +//│ // Prelude +//│ let res; +//│ class TypingUnit { +//│ constructor() { +//│ } +//│ Foo0(base) { +//│ const qualifier = this; +//│ return (class Foo0 extends base { +//│ #foo0; +//│ get foo0() { return this.#foo0; } +//│ constructor(...rest) { +//│ super(...rest); +//│ this.#foo0 = 0; +//│ const foo0 = this.#foo0; +//│ } +//│ }); +//│ } +//│ } +//│ const typing_unit = new TypingUnit; +//│ globalThis.Foo0 = ((base) => typing_unit.Foo0(base)); +//│ // End of generated code + +:js +mixin Foo1 { + val foo0 = 1 + val foo1 = super.foo0 +} +//│ mixin Foo1() { +//│ super: {foo0: 'foo0} +//│ val foo0: 1 +//│ val foo1: 'foo0 +//│ } +//│ // Prelude +//│ class TypingUnit1 { +//│ constructor() { +//│ } +//│ Foo1(base) { +//│ const qualifier = this; +//│ return (class Foo1 extends base { +//│ #foo0; +//│ get foo0() { return this.#foo0; } +//│ #foo1; +//│ get foo1() { return this.#foo1; } +//│ constructor(...rest) { +//│ super(...rest); +//│ this.#foo0 = 1; +//│ const foo0 = this.#foo0; +//│ this.#foo1 = super.foo0; +//│ const foo1 = this.#foo1; +//│ } +//│ }); +//│ } +//│ } +//│ const typing_unit1 = new TypingUnit1; +//│ globalThis.Foo1 = ((base) => typing_unit1.Foo1(base)); +//│ // End of generated code + +module Test0 extends Foo0, Foo1 +//│ module Test0 { +//│ val foo0: 1 +//│ val foo1: 0 +//│ } + +[Test0.foo0, Test0.foo1] +//│ [1, 0] +//│ res +//│ = [ 1, 0 ] + + +:js +:e +mixin Foo2 { + fun foo2 = super +} +//│ ╔══[ERROR] Illegal use of `super` +//│ ║ l.84: fun foo2 = super +//│ ╙── ^^^^^ +//│ mixin Foo2() { +//│ super: 'super +//│ fun foo2: 'super +//│ } +//│ // Prelude +//│ class TypingUnit4 { +//│ constructor() { +//│ } +//│ Foo2(base) { +//│ const qualifier = this; +//│ return (class Foo2 extends base { +//│ constructor(...rest) { +//│ super(...rest); +//│ } +//│ get foo2() { +//│ return super; +//│ } +//│ }); +//│ } +//│ } +//│ const typing_unit4 = new TypingUnit4; +//│ globalThis.Foo2 = ((base) => typing_unit4.Foo2(base)); +//│ // End of generated code +//│ Syntax error: +//│ 'super' keyword unexpected here + +:re +:js +module Test0 extends Foo2 +//│ module Test0 { +//│ fun foo2: anything +//│ } +//│ // Prelude +//│ class TypingUnit5 { +//│ #Test0; +//│ constructor() { +//│ } +//│ get Test0() { +//│ const qualifier = this; +//│ if (this.#Test0 === undefined) { +//│ class Test0 extends Foo2(Object) { +//│ constructor() { +//│ super(); +//│ } +//│ } +//│ this.#Test0 = new Test0(); +//│ this.#Test0.class = Test0; +//│ } +//│ return this.#Test0; +//│ } +//│ } +//│ const typing_unit5 = new TypingUnit5; +//│ globalThis.Test0 = typing_unit5.Test0; +//│ // End of generated code +//│ Runtime error: +//│ ReferenceError: Foo2 is not defined + +Test0 +//│ Test0 +//│ res +//│ = Test0 { class: [Function: Test0] } + +Test0.foo2 +//│ anything +//│ res +//│ = undefined + + +class Foo extends Foo0 { + fun foo0(n) = [super.foo0, super.foo0 + n] +} +//│ class Foo { +//│ constructor() +//│ fun foo0: Int -> [0, Int] +//│ } + + diff --git a/shared/src/test/diff/codegen/SymbolicOps.mls b/shared/src/test/diff/codegen/SymbolicOps.mls new file mode 100644 index 0000000000..9dd61cb78a --- /dev/null +++ b/shared/src/test/diff/codegen/SymbolicOps.mls @@ -0,0 +1,343 @@ +:NewDefs + + +fun (>>) compose(f, g) = x => g(f(x)) +//│ fun (>>) compose: forall 'a 'b 'c. ('a -> 'b, 'b -> 'c) -> 'a -> 'c + +:js +let r = succ >> succ +//│ let r: Int -> Int +//│ // Prelude +//│ function succ(x) { +//│ return x + 1; +//│ } +//│ class TypingUnit1 {} +//│ const typing_unit1 = new TypingUnit1; +//│ // Query 1 +//│ globalThis.r = compose(succ, succ); +//│ // End of generated code +//│ r +//│ = [Function (anonymous)] + +r(3) +//│ Int +//│ res +//│ = 5 + +compose(succ, succ)(3) +//│ Int +//│ res +//│ = 5 + +(succ >> succ)(3) +//│ Int +//│ res +//│ = 5 + +(succ >> succ) of 3 +//│ Int +//│ res +//│ = 5 + +// * Note the high left-precedence of `of` +:e +succ >> succ of 3 +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.44: succ >> succ of 3 +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ╟── application of type `Int` is not a function +//│ ║ l.44: succ >> succ of 3 +//│ ║ ^^^^^^^^^ +//│ ╟── Note: constraint arises from application: +//│ ║ l.4: fun (>>) compose(f, g) = x => g(f(x)) +//│ ║ ^^^^^^^ +//│ ╟── from reference: +//│ ║ l.4: fun (>>) compose(f, g) = x => g(f(x)) +//│ ╙── ^ +//│ error | Int -> nothing +//│ res +//│ = [Function (anonymous)] + +:js +let f = (>>) +//│ let f: forall 'a 'b 'c. ('a -> 'b, 'b -> 'c) -> 'a -> 'c +//│ // Prelude +//│ class TypingUnit7 {} +//│ const typing_unit7 = new TypingUnit7; +//│ // Query 1 +//│ globalThis.f = compose; +//│ // End of generated code +//│ f +//│ = [Function: compose] + +f(succ, succ)(3) +//│ Int +//│ res +//│ = 5 + +(>>)(succ, succ)(3) +//│ Int +//│ res +//│ = 5 + +(>>) of succ, succ +//│ Int -> Int +//│ res +//│ = [Function (anonymous)] + +((>>) of succ, succ) of 3 +//│ Int +//│ res +//│ = 5 + +:e // TODO parse this differently? +(>>) of succ, succ + of 3 +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.94: (>>) of succ, succ +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.95: of 3 +//│ ║ ^^^^^^ +//│ ╟── application of type `Int` is not a function +//│ ║ l.94: (>>) of succ, succ +//│ ║ ^^^^ +//│ ║ l.95: of 3 +//│ ║ ^^^^^^ +//│ ╟── Note: constraint arises from application: +//│ ║ l.4: fun (>>) compose(f, g) = x => g(f(x)) +//│ ║ ^^^^^^^ +//│ ╟── from reference: +//│ ║ l.4: fun (>>) compose(f, g) = x => g(f(x)) +//│ ╙── ^ +//│ error | Int -> nothing +//│ res +//│ = [Function (anonymous)] + + +:ge // TODO support this syntax for builtin operators +fun oops = (+) +//│ fun oops: (Int, Int) -> Int +//│ Code generation encountered an error: +//│ unresolved symbol + + + +fun (%) mod = div +//│ fun (%) mod: (Int, Int) -> Int + +// TODO builtin `div` should compute *integer* division +12 % 5 +//│ Int +//│ res +//│ = 2.4 + + + +// TODO support code-gen for non-top-level operator definitions +module Nested { + fun (++) conc(a, b) = concat(a)(b) + log("abc" ++ "def") +} +//│ module Nested { +//│ fun (++) conc: (Str, Str) -> Str +//│ } +//│ Code generation encountered an error: +//│ unresolved symbol ++ + +// TODO +abstract class Nested { + fun (++) conc: (Int, Int) -> Int + log("abc" ++ "def") +} +//│ ╔══[ERROR] identifier not found: ++ +//│ ║ l.149: log("abc" ++ "def") +//│ ╙── ^^ +//│ abstract class Nested { +//│ fun (++) conc: (Int, Int) -> Int +//│ } +//│ Code generation encountered an error: +//│ unresolved symbol ++ + + + +fun (??) oops: (Int, Int) => Int +//│ fun (??) oops: (Int, Int) -> Int + +1 ?? 2 +//│ Int +//│ res +//│ = +//│ ?? is not implemented + +fun (??) oops: (Int, Int) => Int +fun oops(a, b) = a + b +//│ fun oops: (Int, Int) -> Int +//│ fun (??) oops: (Int, Int) -> Int + +// FIXME-later (stub symbols) This is actually implemented... +1 ?? 2 +//│ Int +//│ res +//│ = +//│ ?? is not implemented + +fun oops: (Int, Int) => Int +fun (??) oops(a, b) = a + b +//│ fun (??) oops: (Int, Int) -> Int +//│ fun oops: (Int, Int) -> Int + +1 ?? 2 +//│ Int +//│ res +//│ = 3 + +fun (!?) oops: (Int, Int) => Int +fun (??) oops(a, b) = a + b +//│ fun (??) oops: (Int, Int) -> Int +//│ fun (!?) oops: (Int, Int) -> Int + +1 ?? 2 +//│ Int +//│ res +//│ = 3 + +1 !? 2 +//│ Int +//│ res +//│ = +//│ !? is not implemented + + + +// * Note: some malformed definitions + +:pe +:e +fun (>>)(f, g) = x => g(f(x)) +//│ ╔══[PARSE ERROR] Expected a function name; found parenthesis section instead +//│ ║ l.215: fun (>>)(f, g) = x => g(f(x)) +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] identifier not found: g +//│ ║ l.215: fun (>>)(f, g) = x => g(f(x)) +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.215: fun (>>)(f, g) = x => g(f(x)) +//│ ║ ^^^^ +//│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` +//│ ║ l.215: fun (>>)(f, g) = x => g(f(x)) +//│ ║ ^^^ +//│ ╟── Note: constraint arises from tuple literal: +//│ ║ l.4: fun (>>) compose(f, g) = x => g(f(x)) +//│ ╙── ^^^^^^ +//│ fun (>>) : anything -> error +//│ Code generation encountered an error: +//│ unresolved symbol g + +:pe +fun compose(>>)(f, g) = x => g(f(x)) +//│ ╔══[PARSE ERROR] Unexpected operator here +//│ ║ l.236: fun compose(>>)(f, g) = x => g(f(x)) +//│ ╙── ^^ +//│ fun compose: forall 'a 'b 'c. () -> ('a -> 'b, 'b -> 'c) -> 'a -> 'c + + +:pe +fun () foo(a, b) = a + b +//│ ╔══[PARSE ERROR] Expected a symbolic name between brackets, found nothing +//│ ║ l.244: fun () foo(a, b) = a + b +//│ ╙── ^^ +//│ fun foo: (Int, Int) -> Int + +:pe +fun ( ) foo(a, b) = a + b +//│ ╔══[PARSE ERROR] Expected a symbolic name, found space instead +//│ ║ l.251: fun ( ) foo(a, b) = a + b +//│ ╙── ^^^ +//│ fun foo: (Int, Int) -> Int + +:pe +fun ( +) foo(a, b) = a + b +//│ ╔══[PARSE ERROR] Expected a symbolic name, found newline instead +//│ ║ l.258: fun ( +//│ ║ ^ +//│ ║ l.259: ) foo(a, b) = a + b +//│ ╙── +//│ fun foo: (Int, Int) -> Int + +:pe +fun (1) foo(a, b) = a + b +//│ ╔══[PARSE ERROR] Expected a symbolic name, found literal instead +//│ ║ l.268: fun (1) foo(a, b) = a + b +//│ ╙── ^ +//│ fun foo: (Int, Int) -> Int + +:pe +fun (++ 1) foo(a, b) = a + b +//│ ╔══[PARSE ERROR] Unexpected literal after symbolic name +//│ ║ l.275: fun (++ 1) foo(a, b) = a + b +//│ ╙── ^ +//│ fun (++) foo: (Int, Int) -> Int + +:pe +fun (a ++ 1) foo(a, b) = a + b +//│ ╔══[PARSE ERROR] Expected a symbolic name, found identifier instead +//│ ║ l.282: fun (a ++ 1) foo(a, b) = a + b +//│ ╙── ^ +//│ fun foo: (Int, Int) -> Int +// should be `<<|+_+|>>`, but we got `<<|+` + + +:pe +fun (<<|+_+|>>) robot(a, b) = a + b +//│ ╔══[PARSE ERROR] Unexpected identifier after symbolic name +//│ ║ l.291: fun (<<|+_+|>>) robot(a, b) = a + b +//│ ╙── ^ +//│ fun (<<|+) robot: (Int, Int) -> Int + +fun (<<|+-+|>>) robot(a, b) = a + b +//│ fun (<<|+-+|>>) robot: (Int, Int) -> Int + +2 <<|+-+|>> 2 +//│ Int +//│ res +//│ = 4 + + +:pe +fun (:-D) dd(a, b) = a + b +//│ ╔══[PARSE ERROR] Unexpected identifier after symbolic name +//│ ║ l.307: fun (:-D) dd(a, b) = a + b +//│ ╙── ^ +//│ fun (:-) dd: (Int, Int) -> Int +// should be `:-D`, but we got `:-` + + +val (->) f(x, y) = [x, y] +//│ val (->) f: forall 'a 'b. ('a, 'b) -> ['a, 'b] +//│ f +//│ = [Function: f1] + +12 -> 34 +//│ [12, 34] +//│ res +//│ = [ 12, 34 ] + + +let (->) _ = f +//│ let (->) _: forall 'a 'b. ('a, 'b) -> ['a, 'b] +//│ _ +//│ = [Function: f1] + +:js +12 -> 34 +//│ [12, 34] +//│ // Prelude +//│ class TypingUnit42 {} +//│ const typing_unit42 = new TypingUnit42; +//│ // Query 1 +//│ res = _(12, 34); +//│ // End of generated code +//│ res +//│ = [ 12, 34 ] + + diff --git a/shared/src/test/diff/codegen/Terms.mls b/shared/src/test/diff/codegen/Terms.mls index 221227c531..1df094b5a1 100644 --- a/shared/src/test/diff/codegen/Terms.mls +++ b/shared/src/test/diff/codegen/Terms.mls @@ -413,12 +413,12 @@ t1 = (mut "hello", mut true) //│ = [ 'hello', true ] :js -t1._1 <- "bye" -t1._1 +t1.0 <- "bye" +t1.0 //│ // Query 1 -//│ res = (t1["_1"] = "bye", []); +//│ res = (t1[0] = "bye", []); //│ // Query 2 -//│ res = t1["_1"]; +//│ res = t1[0]; //│ // End of generated code //│ = [] //│ res: "hello" @@ -480,20 +480,20 @@ xpp a1 :js tu = (mut (), mut 2) -tu._1 <- (tu._2 <- 3) -tu._1 -tu._2 +tu.0 <- (tu.1 <- 3) +tu.0 +tu.1 //│ // Query 1 //│ globalThis.tu = [ //│ [], //│ 2 //│ ]; //│ // Query 2 -//│ res = (tu["_1"] = (tu["_2"] = 3, []), []); +//│ res = (tu[0] = (tu[1] = 3, []), []); //│ // Query 3 -//│ res = tu["_1"]; +//│ res = tu[0]; //│ // Query 4 -//│ res = tu["_2"]; +//│ res = tu[1]; //│ // End of generated code //│ tu: (mut 'a, mut 'b,) //│ where diff --git a/shared/src/test/diff/codegen/TraitMethods.mls b/shared/src/test/diff/codegen/TraitMethods.mls index 5c011bc2c0..40db75fe69 100644 --- a/shared/src/test/diff/codegen/TraitMethods.mls +++ b/shared/src/test/diff/codegen/TraitMethods.mls @@ -210,4 +210,3 @@ fun x -> x.Foo //│ = [Function: res] - diff --git a/shared/src/test/diff/codegen/TrickyTerms.mls b/shared/src/test/diff/codegen/TrickyTerms.mls index 4e6da1c519..2b100ed723 100644 --- a/shared/src/test/diff/codegen/TrickyTerms.mls +++ b/shared/src/test/diff/codegen/TrickyTerms.mls @@ -12,7 +12,7 @@ fun ((x, y)) -> x //│ y //│ ]) => x); //│ // End of generated code -//│ res: ('a, anything,) -> 'a +//│ res: (('a, anything,),) -> 'a //│ = [Function: res] :js diff --git a/shared/src/test/diff/codegen/TrickyWiths.mls b/shared/src/test/diff/codegen/TrickyWiths.mls index fa6e3fab4e..28081a27e3 100644 --- a/shared/src/test/diff/codegen/TrickyWiths.mls +++ b/shared/src/test/diff/codegen/TrickyWiths.mls @@ -211,3 +211,18 @@ test r2 //│ = 1 +f = (fun x -> x) with {a = 0} +//│ f: 'a -> 'a & {a: 0} +//│ = Function { a: 0 } + +f.a +//│ res: 0 +//│ = 0 + +:re // `with` is currently broken with functions +f(0) +//│ res: 0 +//│ Runtime error: +//│ TypeError: f2 is not a function + + diff --git a/shared/src/test/diff/codegen/ValLet.mls b/shared/src/test/diff/codegen/ValLet.mls new file mode 100644 index 0000000000..6cdfebc4cb --- /dev/null +++ b/shared/src/test/diff/codegen/ValLet.mls @@ -0,0 +1,300 @@ +:NewDefs + +:js +class A(x0: Int) { + let x1 = x0 + 1 + log([x1]) + let x2 = x1 + 1 + log([x1, x2]) + val x3 = x2 + 1 +} +//│ class A(x0: Int) { +//│ let x1: Int +//│ let x2: Int +//│ val x3: Int +//│ } +//│ // Prelude +//│ function log(x) { +//│ return console.info(x); +//│ } +//│ let res; +//│ class TypingUnit { +//│ #A; +//│ constructor() { +//│ } +//│ get A() { +//│ const qualifier = this; +//│ if (this.#A === undefined) { +//│ class A { +//│ #x0; +//│ #x3; +//│ get x3() { return this.#x3; } +//│ constructor(x0) { +//│ this.#x0 = x0; +//│ const x1 = x0 + 1; +//│ log([x1]); +//│ const x2 = x1 + 1; +//│ log([ +//│ x1, +//│ x2 +//│ ]); +//│ this.#x3 = x2 + 1; +//│ const x3 = this.#x3; +//│ } +//│ static +//│ unapply(x) { +//│ return [x.#x0]; +//│ } +//│ }; +//│ this.#A = ((x0) => Object.freeze(new A(x0))); +//│ this.#A.class = A; +//│ this.#A.unapply = A.unapply; +//│ } +//│ return this.#A; +//│ } +//│ } +//│ const typing_unit = new TypingUnit; +//│ globalThis.A = typing_unit.A; +//│ // End of generated code + +// FIXME: should be rejected +A(0).x1 +//│ Int +//│ res +//│ = undefined +//│ // Output +//│ [ 1 ] +//│ [ 1, 2 ] + +A(1).x3 +//│ Int +//│ res +//│ = 4 +//│ // Output +//│ [ 2 ] +//│ [ 2, 3 ] + +:js +class AA() { + let x = 42 + let no = 0 + fun f(y: Int) = x + y +} +AA().f(0) +//│ class AA() { +//│ fun f: (y: Int) -> Int +//│ let no: 0 +//│ let x: 42 +//│ } +//│ Int +//│ // Prelude +//│ class TypingUnit3 { +//│ #AA; +//│ constructor() { +//│ } +//│ get AA() { +//│ const qualifier = this; +//│ if (this.#AA === undefined) { +//│ class AA { +//│ #x; +//│ constructor() { +//│ this.#x = 42; +//│ const x = this.#x; +//│ const no = 0; +//│ } +//│ f(y) { +//│ const qualifier1 = this; +//│ return qualifier1.#x + y; +//│ } +//│ static +//│ unapply(x) { +//│ return []; +//│ } +//│ }; +//│ this.#AA = (() => Object.freeze(new AA())); +//│ this.#AA.class = AA; +//│ this.#AA.unapply = AA.unapply; +//│ } +//│ return this.#AA; +//│ } +//│ } +//│ const typing_unit3 = new TypingUnit3; +//│ globalThis.AA = typing_unit3.AA; +//│ // Query 1 +//│ res = AA().f(0); +//│ // End of generated code +//│ res +//│ = 42 + +:js +class B(x: Int, val y: Int) +//│ class B(x: Int, y: Int) +//│ // Prelude +//│ class TypingUnit4 { +//│ #B; +//│ constructor() { +//│ } +//│ get B() { +//│ const qualifier = this; +//│ if (this.#B === undefined) { +//│ class B { +//│ #x; +//│ #y; +//│ get y() { return this.#y; } +//│ constructor(x, y) { +//│ this.#x = x; +//│ this.#y = y; +//│ } +//│ static +//│ unapply(x) { +//│ return ([ +//│ x.#x, +//│ x.#y +//│ ]); +//│ } +//│ }; +//│ this.#B = ((x, y) => Object.freeze(new B(x, y))); +//│ this.#B.class = B; +//│ this.#B.unapply = B.unapply; +//│ } +//│ return this.#B; +//│ } +//│ } +//│ const typing_unit4 = new TypingUnit4; +//│ globalThis.B = typing_unit4.B; +//│ // End of generated code + +:e +B(0, 0).x +//│ ╔══[ERROR] Parameter 'x' cannot tbe accessed as a field +//│ ║ l.168: B(0, 0).x +//│ ║ ^^ +//│ ╟── Either make the parameter a `val` or access it through destructuring +//│ ║ l.130: class B(x: Int, val y: Int) +//│ ╙── ^ +//│ Int | error +//│ res +//│ = undefined + +B(0, 0).y +//│ Int +//│ res +//│ = 0 + +:e +:js +class C { + constructor(val x: Int, y: Int) +} +//│ ╔══[ERROR] Cannot use `val` in constructor parameters +//│ ║ l.187: constructor(val x: Int, y: Int) +//│ ╙── ^ +//│ class C { +//│ constructor(x: Int, y: Int) +//│ } +//│ // Prelude +//│ class TypingUnit7 { +//│ #C; +//│ constructor() { +//│ } +//│ get C() { +//│ const qualifier = this; +//│ if (this.#C === undefined) { +//│ class C { +//│ #x; +//│ get x() { return this.#x; } +//│ constructor(x, y) { +//│ this.#x = x; +//│ } +//│ }; +//│ this.#C = C; +//│ } +//│ return this.#C; +//│ } +//│ } +//│ const typing_unit7 = new TypingUnit7; +//│ globalThis.C = typing_unit7.C; +//│ // End of generated code + +// * TODO improve error location +:e +fun f(val x: Int) = x + 1 +//│ ╔══[ERROR] Cannot use `val` in this position +//│ ║ l.221: fun f(val x: Int) = x + 1 +//│ ╙── ^^^^^^ +//│ fun f: (x: Int) -> Int + +:pe +(val x: 1) +//│ ╔══[PARSE ERROR] Illegal position for field specification +//│ ║ l.228: (val x: 1) +//│ ╙── ^^^^ +//│ 1 +//│ res +//│ = 1 + +:pe +:e +(val x: 1) => +//│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here +//│ ║ l.238: (val x: 1) => +//│ ╙── ^ +//│ ╔══[ERROR] Cannot use `val` in this position +//│ ║ l.238: (val x: 1) => +//│ ╙── ^^^^ +//│ (x: 1) -> () +//│ res +//│ = [Function: res] + +:e +(val x: 1) => () +//│ ╔══[ERROR] Cannot use `val` in this position +//│ ║ l.250: (val x: 1) => () +//│ ╙── ^^^^ +//│ (x: 1) -> () +//│ res +//│ = [Function: res] + +class D(x: Int) { + let x = 1 +} +if D(42) is D(x) then x else 0 +//│ class D(x: Int) { +//│ let x: 1 +//│ } +//│ 0 | 1 +//│ res +//│ = 1 + +class E(x: Int) { + val x = 2 +} +if E(42) is E(x) then x else 0 +//│ class E(x: Int) { +//│ val x: 2 +//│ } +//│ 0 | 2 +//│ res +//│ = 2 + +class F(val x: Int) { + let x = 3 +} +F(0).x +//│ class F(x: Int) { +//│ let x: 3 +//│ } +//│ 3 +//│ res +//│ = 3 + +class G(val x: Int) { + val x = 4 +} +G(1).x +//│ class G(x: Int) { +//│ val x: 4 +//│ } +//│ 4 +//│ res +//│ = 4 diff --git a/shared/src/test/diff/contys/AbstractBounds.mls b/shared/src/test/diff/contys/AbstractBounds.mls index 2d19e38ed9..b63a15ce62 100644 --- a/shared/src/test/diff/contys/AbstractBounds.mls +++ b/shared/src/test/diff/contys/AbstractBounds.mls @@ -83,9 +83,9 @@ class Test3[A, B] //│ Defined Test3.Bar3: Test3['A, 'B] -> 'A -> 'B fun x -> fun y -> x.Foo3 y -//│ res: 'a -> (forall 'A 'b. ('A -> 'b +//│ res: 'a -> (forall 'A 'b. 'A -> 'b //│ where -//│ 'a <: Test3['A, 'b])) +//│ 'a <: Test3['A, 'b]) //│ = [Function: res] @@ -95,27 +95,27 @@ class Test4[A, B] method Bar4 i (a: A) = i a : B method Baz4 = this.Bar4 id //│ Defined class Test4[=A, =B] -//│ Defined Test4.Bar4: Test4['A, 'B] -> (forall 'a. 'a -> (forall 'a. ('A -> 'B +//│ Defined Test4.Bar4: Test4['A, 'B] -> (forall 'a. 'a -> (forall 'a. 'A -> 'B //│ where -//│ 'a <: 'A -> 'B))) +//│ 'a <: 'A -> 'B)) //│ Defined Test4.Baz4: Test4['A, 'B] -> ('A -> 'B //│ where //│ forall 'a. 'a -> 'a <: 'A -> 'B) fun x -> fun y -> x.Bar4 y id -//│ res: 'a -> (forall 'A 'B. (('A -> 'B) -> 'B +//│ res: 'a -> (forall 'A 'B. ('A -> 'B) -> 'B //│ where -//│ 'a <: Test4['A, 'B])) -//│ where -//│ 'A :> forall 'b. 'b -> 'b +//│ 'a <: Test4['A, 'B]) +//│ where +//│ 'A :> forall 'b. 'b -> 'b //│ = [Function: res] fun x -> fun y -> x.Baz4 y -//│ res: 'a -> (forall 'A 'B. (('A & 'B) -> 'B -//│ where -//│ 'a <: Test4['A, 'B])) +//│ res: 'a -> (forall 'A 'B. ('A & 'B) -> 'B //│ where -//│ 'A <: 'B +//│ 'a <: Test4['A, 'B]) +//│ where +//│ 'A <: 'B //│ = [Function: res] diff --git a/shared/src/test/diff/contys/ExplicitConstraints.mls b/shared/src/test/diff/contys/ExplicitConstraints.mls index 7c8f04a403..8b3c40ec42 100644 --- a/shared/src/test/diff/contys/ExplicitConstraints.mls +++ b/shared/src/test/diff/contys/ExplicitConstraints.mls @@ -12,7 +12,7 @@ fun f: int => int where int : string //│ ╔══[ERROR] Type mismatch in constraint specifiation: //│ ║ l.11: fun f: int => int where int : string //│ ║ ^^^^^^^^^^^^ -//│ ╟── type `int` is not an instance of `string` +//│ ╟── type `int` is not an instance of type `string` //│ ║ l.11: fun f: int => int where int : string //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: @@ -25,16 +25,16 @@ fun f: int => (int where int : string) //│ ╔══[ERROR] Type mismatch in constraint specifiation: //│ ║ l.24: fun f: int => (int where int : string) //│ ║ ^^^^^^^^^^^^ -//│ ╟── type `int` is not an instance of `string` +//│ ╟── type `int` is not an instance of type `string` //│ ║ l.24: fun f: int => (int where int : string) //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.24: fun f: int => (int where int : string) //│ ╙── ^^^^^^ -//│ f: int -> (int,) +//│ f: int -> int fun f: 'a => ('a where 'a : string) -//│ f: (string & 'a) -> ('a,) +//│ f: (string & 'a) -> 'a :e @@ -42,7 +42,7 @@ fun f: 'a => ('a where 'a : string) where int : 'a //│ ╔══[ERROR] Type mismatch in constraint specifiation: //│ ║ l.41: fun f: 'a => ('a where 'a : string) where int : 'a //│ ║ ^^^^^^^^ -//│ ╟── type `int` is not an instance of `string` +//│ ╟── type `int` is not an instance of type `string` //│ ║ l.41: fun f: 'a => ('a where 'a : string) where int : 'a //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: @@ -51,7 +51,7 @@ fun f: 'a => ('a where 'a : string) where int : 'a //│ ╟── from type variable: //│ ║ l.41: fun f: 'a => ('a where 'a : string) where int : 'a //│ ╙── ^^ -//│ f: (string & 'a) -> ('a | int,) +//│ f: (string & 'a) -> (int | 'a) :e @@ -61,7 +61,7 @@ fun f: 'a => 'a where //│ ╔══[ERROR] Type mismatch in constraint specifiation: //│ ║ l.60: int : 'a //│ ║ ^^^^^^^^ -//│ ╟── type `int` is not an instance of `string` +//│ ╟── type `int` is not an instance of type `string` //│ ║ l.60: int : 'a //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: @@ -70,18 +70,18 @@ fun f: 'a => 'a where //│ ╟── from type variable: //│ ║ l.60: int : 'a //│ ╙── ^^ -//│ f: (string & 'a) -> ('a | int) +//│ f: (string & 'a) -> (int | 'a) :e -fun f: 'a => forall 'b; 'a where +fun f: 'a => forall 'b: 'a where 'a : 'b 'b : string int : 'a //│ ╔══[ERROR] Type mismatch in constraint specifiation: //│ ║ l.80: int : 'a //│ ║ ^^^^^^^^ -//│ ╟── type `int` is not an instance of `string` +//│ ╟── type `int` is not an instance of type `string` //│ ║ l.80: int : 'a //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: @@ -90,24 +90,24 @@ fun f: 'a => forall 'b; 'a where //│ ╟── from type variable: //│ ║ l.80: int : 'a //│ ╙── ^^ -//│ f: (string & 'a) -> ('a | int) +//│ f: (string & 'a) -> (int | 'a) // * Constraint is stashed! -fun f: 'a => forall 'b; 'a where +fun f: 'a => forall 'b: 'a where 'a : 'b => 'b int : 'a -//│ f: 'a -> ('a | int +//│ f: 'a -> (int | 'a //│ where -//│ 'a | int <: 'b -> 'b) +//│ int | 'a <: 'b -> 'b) :ns f -//│ res: forall 'a. 'a -> (forall 'b. ('a -//│ where -//│ 'a <: 'b -> 'b)) +//│ res: forall 'a. 'a -> (forall 'b. 'a //│ where -//│ 'a :> int +//│ 'a <: 'b -> 'b) +//│ where +//│ 'a :> int // * Note the first-class polymorphic type with impossible bound... let r = f(1) @@ -118,9 +118,9 @@ let r = f(1) r //│ res: forall 'c 'a. 'c //│ where -//│ 'c :> forall 'b. ('a -//│ where -//│ 'a <: 'b -> 'b) +//│ 'c :> forall 'b. 'a +//│ where +//│ 'a <: 'b -> 'b //│ 'a :> int :e @@ -150,20 +150,20 @@ r + 1 //│ res: error | int -fun f: 'a => forall 'b; 'b where +fun f: 'a => forall 'b: 'b where 'a : 'b => 'b int : 'a //│ f: 'a -> ('b //│ where -//│ 'a | int <: 'b -> 'b) +//│ int | 'a <: 'b -> 'b) :ns f -//│ res: forall 'a. 'a -> (forall 'b. ('b -//│ where -//│ 'a <: 'b -> 'b)) +//│ res: forall 'a. 'a -> (forall 'b. 'b //│ where -//│ 'a :> int +//│ 'a <: 'b -> 'b) +//│ where +//│ 'a :> int let r = f(1) //│ r: 'b diff --git a/shared/src/test/diff/ecoop23/ComparePointPoly.mls b/shared/src/test/diff/ecoop23/ComparePointPoly.mls new file mode 100644 index 0000000000..c90dfde588 --- /dev/null +++ b/shared/src/test/diff/ecoop23/ComparePointPoly.mls @@ -0,0 +1,90 @@ +:NewDefs + + +class Some[out A](val value: A) +module None +//│ class Some[A](value: A) +//│ module None + +mixin ComparePoint { + fun compare(lhs, rhs) = (lhs.x === rhs.x) && (lhs.y === rhs.y) +} +mixin CompareColored { + fun compare(lhs, rhs) = + super.compare(lhs, rhs) && (lhs.color === rhs.color) +} +mixin CompareNested { + fun compare(lhs, rhs) = + super.compare(lhs, rhs) && + if lhs.parent is Some(p) + then rhs.parent is Some(q) and this.compare(p, q) + else rhs.parent is None +} +//│ mixin ComparePoint() { +//│ fun compare: ({x: Eql['a], y: Eql['b]}, {x: 'a, y: 'b}) -> Bool +//│ } +//│ mixin CompareColored() { +//│ super: {compare: ('c, 'd) -> Bool} +//│ fun compare: ({color: Eql['e]} & 'c, {color: 'e} & 'd) -> Bool +//│ } +//│ mixin CompareNested() { +//│ super: {compare: ('f, 'g) -> Bool} +//│ this: {compare: ('h, 'i) -> Bool} +//│ fun compare: ({parent: Object & ~#Some | Some['h]} & 'f, {parent: Object & ~#Some | Some['i]} & 'g) -> Bool +//│ } + +class MyPoint[out Col](val x: Int, val y: Int, val color: Col, val parent: Some[MyPoint[Col]] | None) +//│ class MyPoint[Col](x: Int, y: Int, color: Col, parent: None | Some[MyPoint[Col]]) + +module CompareMyPoint extends ComparePoint, CompareColored, CompareNested +//│ module CompareMyPoint { +//│ fun compare: ('a, 'b) -> Bool +//│ } +//│ where +//│ 'b <: {color: 'c, parent: Object & ~#Some | Some['b], x: 'd, y: 'e} +//│ 'a <: {color: Eql['c], parent: Object & ~#Some | Some['a], x: Eql['d], y: Eql['e]} + +let Red = 0 +let p0 = MyPoint(0, 0, Red, None) +let p1 = MyPoint(0, 1, Red, None) +let p2 = MyPoint(0, 1, Red, None) +let p3 = MyPoint(0, 1, Red, Some(p1)) +let p4 = MyPoint(0, 1, Red, Some(p2)) +let p5 = MyPoint(0, 1, Red, Some(p3)) +//│ let Red: 0 +//│ let p0: MyPoint[0] +//│ let p1: MyPoint[0] +//│ let p2: MyPoint[0] +//│ let p3: MyPoint[0] +//│ let p4: MyPoint[0] +//│ let p5: MyPoint[0] +//│ Red +//│ = 0 +//│ p0 +//│ = MyPoint {} +//│ p1 +//│ = MyPoint {} +//│ p2 +//│ = MyPoint {} +//│ p3 +//│ = MyPoint {} +//│ p4 +//│ = MyPoint {} +//│ p5 +//│ = MyPoint {} + +CompareMyPoint.compare(p0, p1) +CompareMyPoint.compare(p1, p2) +CompareMyPoint.compare(p3, p4) +CompareMyPoint.compare(p3, p5) +//│ Bool +//│ res +//│ = false +//│ res +//│ = true +//│ res +//│ = true +//│ res +//│ = false + + diff --git a/shared/src/test/diff/ecoop23/ExpressionProblem.mls b/shared/src/test/diff/ecoop23/ExpressionProblem.mls new file mode 100644 index 0000000000..7745355af2 --- /dev/null +++ b/shared/src/test/diff/ecoop23/ExpressionProblem.mls @@ -0,0 +1,222 @@ +:NewDefs + + +// * Motivating paper example, demonstrating the expression problem solution + + +class Add(lhs: E, rhs: E) +class Lit(n: Int) +//│ class Add[E](lhs: E, rhs: E) +//│ class Lit(n: Int) + +fun add11 = Add(Lit(1), Lit(2)) +//│ fun add11: Add[Lit] + + +fun eval(e) = + if e is + Lit(n) then n + Add(l, r) then eval(l) + eval(r) +//│ fun eval: forall 'a. 'a -> Int +//│ where +//│ 'a <: Add['a] | Lit + + +mixin EvalBase { + fun eval(e) = + if e is + Lit(n) then n: Int + Add(l, r) then this.eval(l) + this.eval(r) +} +//│ mixin EvalBase() { +//│ this: {eval: 'a -> Int} +//│ fun eval: (Add['a] | Lit) -> Int +//│ } + + +module TestLang extends EvalBase +//│ module TestLang { +//│ fun eval: 'a -> Int +//│ } +//│ where +//│ 'a <: Add['a] | Lit + +TestLang.eval +//│ 'a -> Int +//│ where +//│ 'a <: Add['a] | Lit +//│ res +//│ = [Function: eval] + +TestLang.eval(add11) +//│ Int +//│ res +//│ = 3 + + +mixin EvalNothing { + fun eval(e) = e : nothing +} +mixin EvalAddLit { + fun eval(e) = + if e is + Lit(n) then n: Int + Add(l, r) then this.eval(l) + this.eval(r) + else super.eval(e) +} +module TestLang extends EvalNothing, EvalAddLit +//│ mixin EvalNothing() { +//│ fun eval: nothing -> nothing +//│ } +//│ mixin EvalAddLit() { +//│ super: {eval: 'a -> 'b} +//│ this: {eval: 'c -> Int} +//│ fun eval: (Add['c] | Lit | Object & 'a & ~#Add & ~#Lit) -> (Int | 'b) +//│ } +//│ module TestLang { +//│ fun eval: 'd -> Int +//│ } +//│ where +//│ 'd <: Add['d] | Lit + +TestLang.eval +//│ 'a -> Int +//│ where +//│ 'a <: Add['a] | Lit +//│ res +//│ = [Function: eval] + +TestLang.eval(add11) +//│ Int +//│ res +//│ = 3 + + +class Neg(expr: A) +//│ class Neg[A](expr: A) + +let add2negadd11 = Add(Lit(2), Neg(add11)) +//│ let add2negadd11: Add[Lit | Neg[Add[Lit]]] +//│ add2negadd11 +//│ = Add {} + + +mixin EvalNeg { + fun eval(e) = + if e is Neg(d) then 0 - this.eval(d) + else super.eval(e) +} +//│ mixin EvalNeg() { +//│ super: {eval: 'a -> 'b} +//│ this: {eval: 'c -> Int} +//│ fun eval: (Neg['c] | Object & 'a & ~#Neg) -> (Int | 'b) +//│ } + + +module TestLang extends EvalBase, EvalNeg +//│ module TestLang { +//│ fun eval: 'a -> Int +//│ } +//│ where +//│ 'a <: Neg['a] | Object & (Add['a] | Lit) & ~#Neg + +TestLang.eval +//│ 'a -> Int +//│ where +//│ 'a <: Neg['a] | Object & (Add['a] | Lit) & ~#Neg +//│ res +//│ = [Function: eval] + + +TestLang.eval(add11) +//│ Int +//│ res +//│ = 3 + +TestLang.eval(Neg(add11)) +//│ Int +//│ res +//│ = -3 + +TestLang.eval(Add(Lit(2), Neg(Lit(1)))) +//│ Int +//│ res +//│ = 1 + +TestLang.eval(Neg(Neg(add11))) +//│ Int +//│ res +//│ = 3 + + +TestLang.eval(add2negadd11) +//│ Int +//│ res +//│ = -1 + +// add11 + +TestLang.eval(Add(Lit(2), Neg(add11))) +//│ Int +//│ res +//│ = -1 + + +mixin EvalNegNeg_0 { + fun eval(e) = + if e is Neg(Neg(d)) then this.eval(d) + else super.eval(e) + // * Note: the above is equivalent to: + // if e is Neg(f) then + // if f is Neg(d) then this.eval(d) + // else super.eval(e) + // else super.eval(e) +} +//│ mixin EvalNegNeg_0() { +//│ super: {eval: (Neg[nothing] | 'a) -> 'b} +//│ this: {eval: 'c -> 'b} +//│ fun eval: (Neg[Neg['c] | Object & ~#Neg] | Object & 'a & ~#Neg) -> 'b +//│ } + +// * Concise alternative, usign syntax sugar: +mixin EvalNegNeg { + fun eval(override Neg(Neg(d))) = this.eval(d) +} +//│ mixin EvalNegNeg() { +//│ super: {eval: (Neg[nothing] | 'a) -> 'b} +//│ this: {eval: 'c -> 'b} +//│ fun eval: (Neg[Neg['c] | Object & ~#Neg] | Object & 'a & ~#Neg) -> 'b +//│ } + +module TestLang extends EvalBase, EvalNeg, EvalNegNeg +//│ module TestLang { +//│ fun eval: (Neg['A] | Object & 'a & ~#Neg) -> Int +//│ } +//│ where +//│ 'A <: 'b & (Neg['b] | Object & ~#Neg) +//│ 'b <: Neg['A] | Object & 'a & ~#Neg +//│ 'a <: Add['b] | Lit | Neg['b] + +fun mk(n) = if n is + 0 then Lit(0) + 1 then Neg(mk(n)) + _ then Add(mk(n), mk(n)) +//│ fun mk: forall 'a. Object -> (Lit | 'a) +//│ where +//│ 'a :> Neg[Lit | 'a] | Add[Lit | 'a] + +TestLang.eval +//│ (Neg['A] | Object & 'a & ~#Neg) -> Int +//│ where +//│ 'A <: 'b & (Neg['b] | Object & ~#Neg) +//│ 'b <: Neg['A] | Object & 'a & ~#Neg +//│ 'a <: Add['b] | Lit | Neg['b] +//│ res +//│ = [Function: eval] + +TestLang.eval(mk(0)) +//│ Int +//│ res +//│ = 0 + + diff --git a/shared/src/test/diff/ecoop23/Intro.mls b/shared/src/test/diff/ecoop23/Intro.mls new file mode 100644 index 0000000000..dda082110c --- /dev/null +++ b/shared/src/test/diff/ecoop23/Intro.mls @@ -0,0 +1,154 @@ +:NewDefs + + +// * Examples from paper intro + + +class Some(value: A) +module None +//│ class Some[A](value: A) +//│ module None + + + +mixin ComparePoint { + fun compare(lhs, rhs) = + (lhs.x === rhs.x) && (lhs.y === rhs.y) +} +//│ mixin ComparePoint() { +//│ fun compare: ({x: Eql['a], y: Eql['b]}, {x: 'a, y: 'b}) -> Bool +//│ } + + +class Color(val str: Str) { + fun equals(that) = str === that.str +} +//│ class Color(str: Str) { +//│ fun equals: {str: anything} -> Bool +//│ } + +let Red = Color("red") +//│ let Red: Color +//│ Red +//│ = Color {} + + +mixin CompareColored { + fun compare(lhs, rhs) = + super.compare(lhs, rhs) && lhs.color.equals(rhs.color) +} +//│ mixin CompareColored() { +//│ super: {compare: ('a, 'b) -> Bool} +//│ fun compare: ({color: {equals: 'color -> Bool}} & 'a, {color: 'color} & 'b) -> Bool +//│ } + + + +// * Explicit version from paper: + +// interface Nested[Base] { parent: Option[Base] } + +// mixin CompareNested[Base, Final] { +// super: { compare: (Base, Base) -> Bool } +// this: { compare: (Final, Final) -> Bool } +// +// fun compare(lhs: Base & Nested[Final], rhs: Base & Nested[Final]): Bool = +// super.compare(lhs, rhs) && +// if lhs.parent is Some(p) +// then rhs.parent is Some(q) and this.compare(p, q) +// else rhs.parent is None +// } + +// * Implicit version: + +mixin CompareNested { + fun compare(lhs, rhs): Bool = + super.compare(lhs, rhs) && + if lhs.parent is Some(p) + then rhs.parent is Some(q) and this.compare(p, q) + else rhs.parent is None +} +//│ mixin CompareNested() { +//│ super: {compare: ('a, 'b) -> Bool} +//│ this: {compare: ('c, 'd) -> Bool} +//│ fun compare: ({parent: Object & ~#Some | Some['c]} & 'a, {parent: Object & ~#Some | Some['d]} & 'b) -> Bool +//│ } + +// * Alternatively: + +// mixin CompareNested { +// fun compare(lhs, rhs): Bool = +// super.compare(lhs, rhs) && +// if lhs.parent is +// Some(p) then rhs.parent is Some(q) and this.compare(p, q) +// None then rhs.parent is None +// } + + + +class MyPoint(val x: Int, val y: Int, val color: Color, val parent: Some[MyPoint] | None) +//│ class MyPoint(x: Int, y: Int, color: Color, parent: None | Some[MyPoint]) + + +module CompareMyPoint extends ComparePoint, CompareColored, CompareNested +//│ module CompareMyPoint { +//│ fun compare: ('a, 'b) -> Bool +//│ } +//│ where +//│ 'b <: {color: 'color, parent: Object & ~#Some | Some['b], x: 'c, y: 'd} +//│ 'a <: { +//│ color: {equals: 'color -> Bool}, +//│ parent: Object & ~#Some | Some['a], +//│ x: Eql['c], +//│ y: Eql['d] +//│ } + + +let p0 = MyPoint(0, 0, Red, None) +let p1 = MyPoint(0, 1, Red, None) +let p2 = MyPoint(0, 1, Red, None) +let p3 = MyPoint(0, 1, Red, Some(p1)) +let p4 = MyPoint(0, 1, Red, Some(p2)) +let p5 = MyPoint(0, 1, Red, Some(p3)) +//│ let p0: MyPoint +//│ let p1: MyPoint +//│ let p2: MyPoint +//│ let p3: MyPoint +//│ let p4: MyPoint +//│ let p5: MyPoint +//│ p0 +//│ = MyPoint {} +//│ p1 +//│ = MyPoint {} +//│ p2 +//│ = MyPoint {} +//│ p3 +//│ = MyPoint {} +//│ p4 +//│ = MyPoint {} +//│ p5 +//│ = MyPoint {} + + +CompareMyPoint.compare(p0, p1) +//│ Bool +//│ res +//│ = false + +CompareMyPoint.compare(p1, p2) +//│ Bool +//│ res +//│ = true + +CompareMyPoint.compare(p3, p4) +//│ Bool +//│ res +//│ = true + +CompareMyPoint.compare(p3, p5) +//│ Bool +//│ res +//│ = false + + + diff --git a/shared/src/test/diff/ecoop23/PolymorphicVariants.mls b/shared/src/test/diff/ecoop23/PolymorphicVariants.mls new file mode 100644 index 0000000000..a92b52ca08 --- /dev/null +++ b/shared/src/test/diff/ecoop23/PolymorphicVariants.mls @@ -0,0 +1,253 @@ +:NewDefs + + +// * Adapted example from Code reuse through polymorphic variants (FOSE 2000) + + +class Cons[out A](head: A, tail: Cons[A] | Nil) +module Nil +//│ class Cons[A](head: A, tail: Cons[A] | Nil) +//│ module Nil + +let l = Cons(1, Nil) +//│ let l: Cons[1] +//│ l +//│ = Cons {} + +class NotFound() +class Success[out A](result: A) +//│ class NotFound() +//│ class Success[A](result: A) + +fun list_assoc(s, l) = + if l is + Cons(h, t) then + if s === h.0 then Success(h.1) + else list_assoc(s, t) + Nil then NotFound() +//│ fun list_assoc: forall 'a 'A. (Eql['a], Cons[{0: 'a, 1: 'A}] | Nil) -> (NotFound | Success['A]) + +// fun list_assoc(s: Str, l: Cons[{ _1: Str, _2: 'b }] | Nil): NotFound | Success['b] + +class Var(s: Str) +//│ class Var(s: Str) + +mixin EvalVar { + fun eval(sub, v) = + if v is Var(s) then + if list_assoc(s, sub) is + NotFound then v + Success(r) then r +} +//│ mixin EvalVar() { +//│ fun eval: (Cons[{0: anything, 1: 'a}] | Nil, Var) -> (Var | 'a) +//│ } + +class Abs[out A](x: Str, t: A) +class App[out A](s: A, t: A) +//│ class Abs[A](x: Str, t: A) +//│ class App[A](s: A, t: A) + +fun gensym(): Str = "fun" +//│ fun gensym: () -> Str + +fun int_to_string(x: Int): Str = "0" +//│ fun int_to_string: (x: Int) -> Str + +mixin EvalLambda { + fun eval(sub, v) = + if v is + App(t1, t2) then + let l1 = this.eval(sub, t1) + let l2 = this.eval(sub, t2) + if t1 is + Abs(x, t) then this.eval(Cons([x, l2], Nil), t) + else + App(l1, l2) + Abs(x, t) then + let s = gensym() + Abs(s, this.eval(Cons([x, Var(s)], sub), t)) + else + super.eval(sub, v) +} +//│ mixin EvalLambda() { +//│ super: {eval: ('a, 'b) -> 'c} +//│ this: { +//│ eval: ('a, 'd) -> 'A & (Cons[[Str, 'A]], 'e) -> 'c & (Cons[[Str, Var] | 'A0], 'f) -> 'A1 +//│ } +//│ fun eval: ('a & (Cons['A0] | Nil), Abs['f] | App['d & (Abs['e] | Object & ~#Abs)] | Object & 'b & ~#Abs & ~#App) -> (Abs['A1] | App['A] | 'c) +//│ } + +module Test1 extends EvalVar, EvalLambda +//│ module Test1 { +//│ fun eval: (Cons[{0: anything, 1: 'a}] | Nil, 'b) -> 'a +//│ } +//│ where +//│ 'b <: Abs['b] | App['b & (Abs['b] | Object & ~#Abs)] | Var +//│ 'a :> Var | App['a] | Abs['a] + +Test1.eval(Nil, Var("a")) +//│ 'a +//│ where +//│ 'a :> App['a] | Abs['a] | Var +//│ res +//│ = Var {} + +Test1.eval(Nil, Abs("b", Var("a"))) +//│ 'a +//│ where +//│ 'a :> App['a] | Abs['a] | Var +//│ res +//│ = Abs {} + +Test1.eval(Cons(["c", Var("d")], Nil), App(Abs("b", Var("b")), Var("c"))) +//│ 'a +//│ where +//│ 'a :> App['a] | Abs['a] | Var +//│ res +//│ = Var {} + +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 +//│ res +//│ = Abs {} + +class Numb(n: Int) +class Add[out A](l: A, r: A) +class Mul[out A](l: A, r: A) +//│ class Numb(n: Int) +//│ class Add[A](l: A, r: A) +//│ class Mul[A](l: A, r: A) + +fun map_expr(f, v) = + if v is + Var then 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) + +mixin EvalExpr { + fun eval(sub, v) = + let eta(e) = this.eval(sub, e) + let vv = map_expr(eta, v) + if vv is + Var then super.eval(sub, vv) + Add(Numb(l), Numb(r)) then Numb(l + r) + Mul(Numb(l), Numb(r)) then Numb(l * r) + else v +} +//│ mixin EvalExpr() { +//│ super: {eval: ('a, Var) -> 'b} +//│ this: {eval: ('a, 'c) -> Object} +//│ fun eval: ('a, 'b & (Add['c] | Mul['c] | Numb | Var)) -> (Numb | 'b) +//│ } + +module Test2 extends EvalVar, EvalExpr +//│ module Test2 { +//│ fun eval: forall 'a. (Cons[{0: anything, 1: Object & 'b}] | Nil, 'a & (Add['c] | Mul['c] | Numb | Var)) -> (Numb | Var | 'b | 'a | 'c) +//│ } +//│ where +//│ 'c <: Add['c] | Mul['c] | Numb | Var + +Test2.eval(Nil, Var("a")) +//│ Numb | Var +//│ res +//│ = Var {} + +Test2.eval(Cons(["c", Abs("d", Var("d"))], Nil), Var("a")) +//│ Abs[Var] | Numb | Var +//│ res +//│ = Var {} + +Test2.eval(Cons(["a", Numb(1)], Nil), Var("a")) +//│ Numb | Var +//│ res +//│ = Numb {} + +// * This expected error shows that Test2 does not handle Abs expression inputs +:e +Test2.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.172: Test2.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── application of type `Abs[?A]` does not match type `Add[?A0] | Mul[?A1] | Numb | Var` +//│ ║ l.172: Test2.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from reference: +//│ ║ l.125: if v is +//│ ║ ^ +//│ ╟── from reference: +//│ ║ l.135: let vv = map_expr(eta, v) +//│ ╙── ^ +//│ Abs[Var] | Numb | Var | error +//│ res +//│ Runtime error: +//│ Error: non-exhaustive case expression + +Test2.eval(Cons(["a", Abs("d", Var("d"))], Nil), Add(Numb(1), Var("a"))) +//│ Abs[Var] | Add[Numb | Var] | Numb | Var +//│ res +//│ = Add {} + +module Test3 extends EvalVar, EvalExpr, EvalLambda +//│ module Test3 { +//│ fun eval: (Cons[{0: anything, 1: 'a}] | Nil, 'b) -> 'c +//│ } +//│ where +//│ 'a :> 'c +//│ <: Object +//│ 'c :> App['c] | Abs['c] | 'a | 'd +//│ 'd <: Add['b] | Mul['b] | Numb | Var +//│ 'b <: Abs['b] | App['b & (Abs['b] | Object & ~#Abs)] | Object & 'd & ~#Abs & ~#App + +Test3.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) +//│ 'a +//│ where +//│ 'a :> App['a] | Abs['a] | Abs[Var] | Numb | Var +//│ res +//│ = Abs {} + +Test3.eval(Cons(["c", Abs("d", Var("d"))], Nil), App(Abs("a", Var("a")), Add(Numb(1), Var("c")))) +//│ 'a +//│ where +//│ 'a :> App['a] | Abs['a] | Abs[Var] | Add[Numb | Var] | Numb | Var +//│ res +//│ = Add {} + +// * Incorrect version, for regression testing – EvalLambda should be mixed in after EvalExpr +module Test3 extends EvalVar, EvalLambda, EvalExpr +//│ module Test3 { +//│ fun eval: (Cons[{0: anything, 1: 'a}] | Nil, 'a & (Add['b] | Mul['b] | Numb | Var)) -> (Numb | 'a | 'b | 'c) +//│ } +//│ where +//│ 'a :> 'c | 'b +//│ <: Object +//│ 'b <: Add['b] | Mul['b] | Numb | Var +//│ 'c :> Abs['a | 'c] | App['a | 'c] | 'a + +// * Because EvalExpr does not dispatch lambdas to super and map_expr only +// * handles exprs +:e +Test3.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.234: Test3.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── application of type `Abs[?A]` does not match type `Add[?A0] | Mul[?A1] | Numb | Var` +//│ ║ l.234: Test3.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from reference: +//│ ║ l.125: if v is +//│ ║ ^ +//│ ╟── from reference: +//│ ║ l.135: let vv = map_expr(eta, v) +//│ ╙── ^ +//│ Abs[Var] | error | 'a +//│ where +//│ 'a :> Abs['a] | App['a] | Numb | Var +//│ res +//│ Runtime error: +//│ Error: non-exhaustive case expression + diff --git a/shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls b/shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls new file mode 100644 index 0000000000..d2b54c6799 --- /dev/null +++ b/shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls @@ -0,0 +1,475 @@ +:NewDefs + + +// * Adapted example from Compositional Embeddings of Domain-Specific Languages (OOPSLA 2022) + + +// ******************* Initial System ******************* + +class Vector(val x: Int, val y: Int) +//│ class Vector(x: Int, y: Int) + +class Circle(radius: Int) +class Outside[out Region](a: Region) +class Union[out Region](a: Region, b: Region) +class Intersect[out Region](a: Region, b: Region) +class Translate[out Region](v: Vector, a: Region) +//│ class Circle(radius: Int) +//│ class Outside[Region](a: Region) +//│ class Union[Region](a: Region, b: Region) +//│ class Intersect[Region](a: Region, b: Region) +//│ class Translate[Region](v: Vector, a: Region) + +type BaseLang[T] = Circle | Intersect[T] | Union[T] | Outside[T] | Translate[T] +//│ type BaseLang[T] = Circle | Intersect[T] | Outside[T] | Translate[T] | Union[T] + +mixin SizeBase { + fun size(r) = + if r is + Circle(_) then 1 + Outside(a) then this.size(a) + 1 + Union(a, b) then this.size(a) + this.size(b) + 1 + Intersect(a, b) then this.size(a) + this.size(b) + 1 + Translate(_, a) then this.size(a) + 1 +} +//│ mixin SizeBase() { +//│ this: {size: 'a -> Int} +//│ fun size: (Circle | Intersect['a] | Outside['a] | Translate['a] | Union['a]) -> Int +//│ } + +// ******************* Linguistic Reuse and Meta-Language Optimizations ******************* + +fun round(n: Num): Int = 0 +//│ fun round: (n: Num) -> Int + +fun go(x, offset) = + if x is 0 then Circle(1) + else + let shared = go(x - 1, round(offset / 2)) + Union(Translate(Vector(0 - offset, 0), shared), Translate(Vector(offset, 0), shared)) +//│ fun go: forall 'a. (0 | Int & ~0, Int) -> (Circle | 'a) +//│ where +//│ 'a :> Union[Translate[Circle | 'a]] + +// * Note that first-class polymorphism manages (correctly) to preserve the universal quantification +let circles = go(2, 1024) +//│ let circles: forall 'a. Circle | 'a +//│ where +//│ 'a :> Union[Translate[Circle | 'a]] +//│ circles +//│ = Union {} + +// ******************* Adding More Language Constructs ******************* + +class Univ() +class Empty() +class Scale[out Region](v: Vector, a: Region) +//│ class Univ() +//│ class Empty() +//│ class Scale[Region](v: Vector, a: Region) + +type ExtLang[T] = Univ | Empty | Scale[T] +//│ type ExtLang[T] = Empty | Scale[T] | Univ + +mixin SizeExt { + fun size(a) = + if a is + Univ then 1 + Empty then 1 + Scale(_, b) then this.size(b) + 1 + else super.size(a) +} +//│ mixin SizeExt() { +//│ super: {size: 'a -> 'b} +//│ this: {size: 'c -> Int} +//│ fun size: (Empty | Object & 'a & ~#Empty & ~#Scale & ~#Univ | Scale['c] | Univ) -> (Int | 'b) +//│ } + +type RegionLang = BaseLang[RegionLang] | ExtLang[RegionLang] +//│ type RegionLang = BaseLang[RegionLang] | ExtLang[RegionLang] + +module TestSize extends SizeBase, SizeExt { + fun size: RegionLang -> Int +} +//│ module TestSize { +//│ fun size: RegionLang -> Int +//│ } + +TestSize.size(Empty()) +//│ Int +//│ res +//│ = 1 + +TestSize.size(circles) +//│ Int +//│ res +//│ = 13 + +TestSize.size(Scale(Vector(1, 1), circles)) +//│ Int +//│ res +//│ = 14 + +// ******************* Adding a New Interpretation ******************* +// a stupid power (Int ** Int) implementation +fun pow(x, a) = + if a is 0 then 1 + else x * pow(x, a - 1) +//│ fun pow: (Int, 0 | Int & ~0) -> Int + +mixin Contains { + fun contains(a, p) = + if a is + Circle(r) then pow(p.x, 2) + pow(p.y, 2) <= pow(r, 2) + Outside(a) then not (this.contains(a, p)) + Union(lhs, rhs) then this.contains(lhs, p) || this.contains(rhs, p) + Intersect(lhs, rhs) then this.contains(lhs, p) && this.contains(rhs, p) + Translate(v, a) then this.contains(a, Vector(p.x - v.x, p.y - v.y)) +} +//│ mixin Contains() { +//│ this: {contains: ('a, 'b) -> Bool & ('c, Vector) -> 'd} +//│ fun contains: (Circle | Intersect['a] | Outside['a] | Translate['c] | Union['a], {x: Int, y: Int} & 'b) -> (Bool | 'd) +//│ } + +type BaseRegionLang = BaseLang[BaseRegionLang] +//│ type BaseRegionLang = BaseLang[BaseRegionLang] + +module TestContains extends Contains { + fun contains: (BaseRegionLang, Vector) -> Bool +} +//│ module TestContains { +//│ fun contains: (BaseRegionLang, Vector) -> Bool +//│ } + +TestContains.contains(Translate(Vector(0, 0), Circle(1)), Vector(0, 0)) +//│ Bool +//│ res +//│ = true + +TestContains.contains(Intersect(Translate(Vector(0, 0), Circle(1)), Circle(1)), Vector(0, 0)) +//│ Bool +//│ res +//│ = true + +TestContains.contains(circles, Vector(0, 0)) +//│ Bool +//│ res +//│ = false + +// ******************* Dependencies, Complex Interpretations, and Domain-Specific Optimizations ******************* + +fun toString(a: Int): Str = "foo" +fun concat(a: Str, b: Str): Str = a +//│ fun toString: (a: Int) -> Str +//│ fun concat: (a: Str, b: Str) -> Str + +mixin Text { + fun text(e) = + if e is + Circle(r) then concat("a circular region of radius ", toString(r)) + Outside(a) then concat("outside a region of size ", toString(this.size(a))) + Union then concat("the union of two regions of size ", toString(this.size(e))) + Intersect then concat("the intersection of two regions of size ", toString(this.size(e))) + 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 +//│ } + +: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))) +//│ ╙── ^^^^^ +//│ module SizeText { +//│ fun text: (Circle | Intersect[anything] | Outside[anything] | Translate[anything] | Union[anything]) -> Str +//│ } + +// * Note: this inferred type got *much worse* after this commit (field access type refinement) +module SizeText extends SizeBase, Text { + fun size: BaseRegionLang -> Int + fun text: BaseRegionLang -> Str +} +//│ module SizeText { +//│ fun size: BaseRegionLang -> Int +//│ fun text: BaseRegionLang -> Str +//│ } + +SizeText.text(circles) +//│ Str +//│ res +//│ = 'the union of two regions of size ' + +SizeText.size(circles) +//│ Int +//│ res +//│ = 13 + +SizeText.text(Intersect(Translate(Vector(0, 0), Circle(1)), Circle(1))) +//│ Str +//│ res +//│ = 'the intersection of two regions of size ' + +SizeText.size(Intersect(Translate(Vector(0, 0), Circle(1)), Circle(1))) +//│ Int +//│ res +//│ = 4 + +mixin IsUniv { + fun isUniv(e) = + if e is + Univ then true + Outside(a) then this.isEmpty(a) + Union(a, b) then this.isUniv(a) || this.isUniv(b) + Intersect(a, b) then this.isUniv(a) && this.isUniv(b) + Translate(_, a) then this.isUniv(a) + Scale(_, a) then this.isUniv(a) + else false +} +//│ mixin IsUniv() { +//│ this: {isEmpty: 'a -> 'b, isUniv: 'c -> Bool & 'd -> 'b} +//│ fun isUniv: (Intersect['c] | Object & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union & ~#Univ | Outside['a] | Scale['d] | Translate['d] | Union['c] | Univ) -> (Bool | 'b) +//│ } + +mixin IsEmpty { + fun isEmpty(e) = + if e is + Univ then true + Outside(a) then this.isUniv(a) + Union(a, b) then this.isEmpty(a) || this.isEmpty(b) + Intersect(a, b) then this.isEmpty(a) && this.isEmpty(b) + Translate(_, a) then this.isEmpty(a) + Scale(_, a) then this.isEmpty(a) + else false +} +//│ mixin IsEmpty() { +//│ this: {isEmpty: 'a -> Bool & 'b -> 'c, isUniv: 'd -> 'c} +//│ fun isEmpty: (Intersect['a] | Object & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union & ~#Univ | Outside['d] | Scale['b] | Translate['b] | Union['a] | Univ) -> (Bool | 'c) +//│ } + +module IsUnivIsEmpty extends IsUniv, IsEmpty { + fun isEmpty: RegionLang -> Bool + fun isUniv: RegionLang -> Bool +} +//│ module IsUnivIsEmpty { +//│ fun isEmpty: RegionLang -> Bool +//│ fun isUniv: RegionLang -> Bool +//│ } + +module IsUnivIsEmpty extends IsEmpty, IsUniv { + fun isEmpty: RegionLang -> Bool + fun isUniv: RegionLang -> Bool +} +//│ module IsUnivIsEmpty { +//│ fun isEmpty: RegionLang -> Bool +//│ fun isUniv: RegionLang -> Bool +//│ } + +IsUnivIsEmpty.isUniv(circles) +//│ Bool +//│ res +//│ = false + +IsUnivIsEmpty.isEmpty(circles) +//│ Bool +//│ res +//│ = false + +:e // Expected since the annotation only allows Lang variants +class Foo() +IsUnivIsEmpty.isEmpty(Scale(Vector(1, 2), Intersect(Foo(), circles))) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.290: IsUnivIsEmpty.isEmpty(Scale(Vector(1, 2), Intersect(Foo(), circles))) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── application of type `Foo` does not match type `BaseLang[RegionLang] | ExtLang[RegionLang]` +//│ ║ l.290: IsUnivIsEmpty.isEmpty(Scale(Vector(1, 2), Intersect(Foo(), circles))) +//│ ║ ^^^^^ +//│ ╟── Note: constraint arises from union type: +//│ ║ l.88: type RegionLang = BaseLang[RegionLang] | ExtLang[RegionLang] +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── from type reference: +//│ ║ l.88: type RegionLang = BaseLang[RegionLang] | ExtLang[RegionLang] +//│ ╙── ^^^^^^^^^^ +//│ class Foo() +//│ error | false | true +//│ res +//│ = false + +mixin Eliminate { + fun eliminate(e) = + if e is + Outside(Outside(a)) then this.eliminate(a) + Outside(a) then Outside(this.eliminate(a)) + Union(a, b) then Union(this.eliminate(a), this.eliminate(b)) + Intersect(a, b) then Intersect(this.eliminate(a), this.eliminate(b)) + Translate(v, a) then Translate(v, this.eliminate(a)) + Scale(v, a) then Scale(v, this.eliminate(a)) + else e +} +//│ 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) +//│ } + +module TestElim extends Eliminate { + fun eliminate: RegionLang -> RegionLang +} +//│ module TestElim { +//│ fun eliminate: RegionLang -> RegionLang +//│ } + +TestElim.eliminate(Outside(Outside(Univ()))) +//│ RegionLang +//│ res +//│ = Univ {} + +TestElim.eliminate(circles) +//│ RegionLang +//│ res +//│ = Union {} + +fun mk(n) = if n is + 1 then Outside(mk(n)) + 2 then Union(mk(n), mk(n)) + 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 +//│ where +//│ 'a :> Outside['a] | Scale['a] | Union['a] | Intersect['a] | Translate['a] + +:re +TestElim.eliminate(mk(100)) +//│ RegionLang +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +// ************************************************************************* + +module Lang extends SizeBase, SizeExt, Contains, Text, IsUniv, IsEmpty, Eliminate { + fun contains: (BaseRegionLang, Vector) -> Bool + fun eliminate: RegionLang -> RegionLang + fun isEmpty: RegionLang -> Bool + fun isUniv: RegionLang -> Bool + fun size: RegionLang -> Int + fun text: BaseRegionLang -> Str +} +//│ module Lang { +//│ fun contains: (BaseRegionLang, Vector) -> Bool +//│ fun eliminate: RegionLang -> RegionLang +//│ fun isEmpty: RegionLang -> Bool +//│ fun isUniv: RegionLang -> Bool +//│ fun size: RegionLang -> Int +//│ fun text: BaseRegionLang -> Str +//│ } + +Lang.size(circles) +//│ Int +//│ res +//│ = 13 + +Lang.contains(circles, Vector(0, 0)) +//│ Bool +//│ res +//│ = false + +Lang.text(circles) +//│ Str +//│ res +//│ = 'the union of two regions of size ' + +Lang.isUniv(circles) +//│ Bool +//│ res +//│ = false + +Lang.isEmpty(circles) +//│ Bool +//│ res +//│ = false + +Lang.size(Lang.eliminate(circles)) +//│ Int +//│ res +//│ = 13 + +:re +Lang.size(mk(100)) +//│ Int +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +:e +:re +Lang.contains(mk(100), Vector(0, 0)) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.418: Lang.contains(mk(100), Vector(0, 0)) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── application of type `Scale[?Region]` does not match type `Circle | Intersect[BaseRegionLang] | Outside[BaseRegionLang] | Translate[BaseRegionLang] | Union[BaseRegionLang]` +//│ ║ l.348: _ then Scale(Vector(0, 0), mk(n)) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from union type: +//│ ║ l.23: type BaseLang[T] = Circle | Intersect[T] | Union[T] | Outside[T] | Translate[T] +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── from type reference: +//│ ║ l.134: type BaseRegionLang = BaseLang[BaseRegionLang] +//│ ╙── ^^^^^^^^^^^^^^ +//│ error | false | true +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +:e +:re +Lang.text(mk(100)) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.438: Lang.text(mk(100)) +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ╟── application of type `Scale[?Region]` does not match type `Circle | Intersect[BaseRegionLang] | Outside[BaseRegionLang] | Translate[BaseRegionLang] | Union[BaseRegionLang]` +//│ ║ l.348: _ then Scale(Vector(0, 0), mk(n)) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from union type: +//│ ║ l.23: type BaseLang[T] = Circle | Intersect[T] | Union[T] | Outside[T] | Translate[T] +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── from type reference: +//│ ║ l.134: type BaseRegionLang = BaseLang[BaseRegionLang] +//│ ╙── ^^^^^^^^^^^^^^ +//│ Str | error +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +:re +Lang.isUniv(mk(100)) +//│ Bool +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +:re +Lang.isEmpty(mk(100)) +//│ Bool +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +:re +Lang.size(Lang.eliminate(mk(100))) +//│ Int +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded diff --git a/shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls b/shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls new file mode 100644 index 0000000000..22b0dec18a --- /dev/null +++ b/shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls @@ -0,0 +1,460 @@ +:NewDefs + + +// * Adapted example from Compositional Embeddings of Domain-Specific Languages (OOPSLA 2022) + + +// ******************* Initial System ******************* + +class Vector(val x: Int, val y: Int) +//│ class Vector(x: Int, y: Int) + +class Circle(radius: Int) +class Outside[out Region](a: Region) +class Union[out Region](a: Region, b: Region) +class Intersect[out Region](a: Region, b: Region) +class Translate[out Region](v: Vector, a: Region) +//│ class Circle(radius: Int) +//│ class Outside[Region](a: Region) +//│ class Union[Region](a: Region, b: Region) +//│ class Intersect[Region](a: Region, b: Region) +//│ class Translate[Region](v: Vector, a: Region) + +mixin SizeBase { + fun size(r) = + if r is + Circle(_) then 1 + Outside(a) then this.size(a) + 1 + Union(a, b) then this.size(a) + this.size(b) + 1 + Intersect(a, b) then this.size(a) + this.size(b) + 1 + Translate(_, a) then this.size(a) + 1 +} +//│ mixin SizeBase() { +//│ this: {size: 'a -> Int} +//│ fun size: (Circle | Intersect['a] | Outside['a] | Translate['a] | Union['a]) -> Int +//│ } + +// ******************* Linguistic Reuse and Meta-Language Optimizations ******************* + +fun round(n: Num): Int = 0 +//│ fun round: (n: Num) -> Int + +fun go(x, offset) = + if x is 0 then Circle(1) + else + let shared = go(x - 1, round(offset / 2)) + Union(Translate(Vector(0 - offset, 0), shared), Translate(Vector(offset, 0), shared)) +//│ fun go: forall 'a. (0 | Int & ~0, Int) -> (Circle | 'a) +//│ where +//│ 'a :> Union[Translate[Circle | 'a]] + +// * Note that first-class polymorphism manages (correctly) to preserve the universal quantification +let circles = go(2, 1024) +//│ let circles: forall 'a. Circle | 'a +//│ where +//│ 'a :> Union[Translate[Circle | 'a]] +//│ circles +//│ = Union {} + +// ******************* Adding More Language Constructs ******************* + +class Univ() +class Empty() +class Scale[out Region](v: Vector, a: Region) +//│ class Univ() +//│ class Empty() +//│ class Scale[Region](v: Vector, a: Region) + +mixin SizeExt { + fun size(a) = + if a is + Univ then 1 + Empty then 1 + Scale(_, b) then this.size(b) + 1 + else super.size(a) +} +//│ mixin SizeExt() { +//│ super: {size: 'a -> 'b} +//│ this: {size: 'c -> Int} +//│ fun size: (Empty | Object & 'a & ~#Empty & ~#Scale & ~#Univ | Scale['c] | Univ) -> (Int | 'b) +//│ } + +module TestSize extends SizeBase, SizeExt +//│ module TestSize { +//│ fun size: 'a -> Int +//│ } +//│ where +//│ 'a <: Empty | Object & (Circle | Intersect['a] | Outside['a] | Translate['a] | Union['a]) & ~#Empty & ~#Scale & ~#Univ | Scale['a] | Univ + +TestSize.size(Empty()) +//│ Int +//│ res +//│ = 1 + +TestSize.size(circles) +//│ Int +//│ res +//│ = 13 + +TestSize.size(Scale(Vector(1, 1), circles)) +//│ Int +//│ res +//│ = 14 + +// ******************* Adding a New Interpretation ******************* +// a stupid power (Int ** Int) implementation +fun pow(x, a) = + if a is 0 then 1 + else x * pow(x, a - 1) +//│ fun pow: (Int, 0 | Int & ~0) -> Int + +mixin Contains { + fun contains(a, p) = + if a is + Circle(r) then pow(p.x, 2) + pow(p.y, 2) <= pow(r, 2) + Outside(a) then not (this.contains(a, p)) + Union(lhs, rhs) then this.contains(lhs, p) || this.contains(rhs, p) + Intersect(lhs, rhs) then this.contains(lhs, p) && this.contains(rhs, p) + Translate(v, a) then this.contains(a, Vector(p.x - v.x, p.y - v.y)) +} +//│ mixin Contains() { +//│ this: {contains: ('a, 'b) -> Bool & ('c, Vector) -> 'd} +//│ fun contains: (Circle | Intersect['a] | Outside['a] | Translate['c] | Union['a], {x: Int, y: Int} & 'b) -> (Bool | 'd) +//│ } + +module TestContains extends Contains +//│ module TestContains { +//│ fun contains: ('a, {x: Int, y: Int}) -> Bool +//│ } +//│ where +//│ 'a <: Circle | Intersect['a] | Outside['a] | Translate['a] | Union['a] + +TestContains.contains(Translate(Vector(0, 0), Circle(1)), Vector(0, 0)) +//│ Bool +//│ res +//│ = true + +TestContains.contains(Intersect(Translate(Vector(0, 0), Circle(1)), Circle(1)), Vector(0, 0)) +//│ Bool +//│ res +//│ = true + +TestContains.contains(circles, Vector(0, 0)) +//│ Bool +//│ res +//│ = false + +// ******************* Dependencies, Complex Interpretations, and Domain-Specific Optimizations ******************* + +fun toString(a: Int): Str = "foo" +fun concat(a: Str, b: Str): Str = a +//│ fun toString: (a: Int) -> Str +//│ fun concat: (a: Str, b: Str) -> Str + +mixin Text { + fun text(e) = + if e is + Circle(r) then concat("a circular region of radius ", toString(r)) + Outside(a) then concat("outside a region of size ", toString(this.size(a))) + Union then concat("the union of two regions of size ", toString(this.size(e))) + Intersect then concat("the intersection of two regions of size ", toString(this.size(e))) + 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 +//│ } + +: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))) +//│ ╙── ^^^^^ +//│ module SizeText { +//│ fun text: (Circle | Intersect[anything] | Outside[anything] | Translate[anything] | Union[anything]) -> Str +//│ } + +// * Note: this inferred type got *much worse* after this commit (field access type refinement) +module SizeText extends SizeBase, Text +//│ module SizeText { +//│ fun size: (Circle | Intersect['a] | Outside['a] | Translate['a] | Union['a]) -> Int +//│ fun text: 'a -> Str +//│ } +//│ where +//│ 'a <: Circle | Intersect['a] | Outside['a] | Translate['a] | Union['a] + +SizeText.text(circles) +//│ Str +//│ res +//│ = 'the union of two regions of size ' + +SizeText.size(circles) +//│ Int +//│ res +//│ = 13 + +SizeText.text(Intersect(Translate(Vector(0, 0), Circle(1)), Circle(1))) +//│ Str +//│ res +//│ = 'the intersection of two regions of size ' + +SizeText.size(Intersect(Translate(Vector(0, 0), Circle(1)), Circle(1))) +//│ Int +//│ res +//│ = 4 + +mixin IsUniv { + fun isUniv(e) = + if e is + Univ then true + Outside(a) then this.isEmpty(a) + Union(a, b) then this.isUniv(a) || this.isUniv(b) + Intersect(a, b) then this.isUniv(a) && this.isUniv(b) + Translate(_, a) then this.isUniv(a) + Scale(_, a) then this.isUniv(a) + else false +} +//│ mixin IsUniv() { +//│ this: {isEmpty: 'a -> 'b, isUniv: 'c -> Bool & 'd -> 'b} +//│ fun isUniv: (Intersect['c] | Object & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union & ~#Univ | Outside['a] | Scale['d] | Translate['d] | Union['c] | Univ) -> (Bool | 'b) +//│ } + +mixin IsEmpty { + fun isEmpty(e) = + if e is + Univ then true + Outside(a) then this.isUniv(a) + Union(a, b) then this.isEmpty(a) || this.isEmpty(b) + Intersect(a, b) then this.isEmpty(a) && this.isEmpty(b) + Translate(_, a) then this.isEmpty(a) + Scale(_, a) then this.isEmpty(a) + else false +} +//│ mixin IsEmpty() { +//│ this: {isEmpty: 'a -> Bool & 'b -> 'c, isUniv: 'd -> 'c} +//│ fun isEmpty: (Intersect['a] | Object & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union & ~#Univ | Outside['d] | Scale['b] | Translate['b] | Union['a] | Univ) -> (Bool | 'c) +//│ } + +module IsUnivIsEmpty extends IsUniv, IsEmpty +//│ module IsUnivIsEmpty { +//│ fun isEmpty: 'a -> Bool +//│ fun isUniv: 'b -> Bool +//│ } +//│ where +//│ 'b <: Intersect['b] | Object & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union & ~#Univ | Outside['b] | Scale['b] | Translate['b] | Union['b] | Univ +//│ 'a <: Intersect['a] | Object & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union & ~#Univ | Outside['a] | Scale['a] | Translate['a] | Union['a] | Univ + +module IsUnivIsEmpty extends IsEmpty, IsUniv +//│ module IsUnivIsEmpty { +//│ fun isEmpty: 'a -> Bool +//│ fun isUniv: 'b -> Bool +//│ } +//│ where +//│ 'b <: Intersect['b] | Object & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union & ~#Univ | Outside['b] | Scale['b] | Translate['b] | Union['b] | Univ +//│ 'a <: Intersect['a] | Object & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union & ~#Univ | Outside['a] | Scale['a] | Translate['a] | Union['a] | Univ + +IsUnivIsEmpty.isUniv(circles) +//│ Bool +//│ res +//│ = false + +IsUnivIsEmpty.isEmpty(circles) +//│ Bool +//│ res +//│ = false + +class Foo() +IsUnivIsEmpty.isEmpty(Scale(Vector(1, 2), Intersect(Foo(), circles))) +//│ class Foo() +//│ Bool +//│ res +//│ = false + +mixin Eliminate { + fun eliminate(e) = + if e is + Outside(Outside(a)) then this.eliminate(a) + Outside(a) then Outside(this.eliminate(a)) + Union(a, b) then Union(this.eliminate(a), this.eliminate(b)) + Intersect(a, b) then Intersect(this.eliminate(a), this.eliminate(b)) + Translate(v, a) then Translate(v, this.eliminate(a)) + Scale(v, a) then Scale(v, this.eliminate(a)) + else e +} +//│ 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) +//│ } + +module TestElim extends Eliminate +//│ module TestElim { +//│ fun eliminate: 'a -> 'b +//│ } +//│ 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] + +TestElim.eliminate(Outside(Outside(Univ()))) +//│ 'a +//│ where +//│ 'a :> Univ | Union[Univ | 'a] | Intersect['a] | Translate[Univ | 'a] | Scale[Univ | 'a] | Outside[Univ | 'a] +//│ res +//│ = Univ {} + +TestElim.eliminate(circles) +//│ 'a +//│ where +//│ 'a :> Circle | Translate[Circle | 'a] | Scale[Circle | 'a] | Outside[Circle | 'a] | Union[Circle | 'a] | Intersect['a] +//│ res +//│ = Union {} + +fun mk(n) = if n is + 1 then Outside(mk(n)) + 2 then Union(mk(n), mk(n)) + 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 +//│ where +//│ 'a :> Outside['a] | Intersect['a] | Translate['a] | Scale['a] | Union['a] + +:re +TestElim.eliminate(mk(100)) +//│ 'a +//│ where +//│ 'a :> Outside['a] | Union['a] | Intersect['a] | Translate['a] | Scale['a] +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +// ************************************************************************* + +module Lang extends SizeBase, SizeExt, Contains, Text, IsUniv, IsEmpty, Eliminate +//│ module Lang { +//│ fun contains: ('a, {x: Int, y: Int}) -> Bool +//│ fun eliminate: 'b -> 'c +//│ fun isEmpty: 'd -> Bool +//│ fun isUniv: 'e -> Bool +//│ fun size: 'f -> Int +//│ fun text: (Circle | Intersect['g] | Outside['g] | Translate['g] | Union['g]) -> Str +//│ } +//│ where +//│ 'g <: Empty | Object & (Circle | Intersect['g] | Outside['g] | Translate['g] | Union['g]) & ~#Empty & ~#Scale & ~#Univ | Scale['g] | Univ +//│ 'f <: Empty | Object & (Circle | Intersect['f] | Outside['f] | Translate['f] | Union['f]) & ~#Empty & ~#Scale & ~#Univ | Scale['f] | Univ +//│ '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] +//│ 'a <: Circle | Intersect['a] | Outside['a] | Translate['a] | Union['a] + +Lang.size(circles) +//│ Int +//│ res +//│ = 13 + +Lang.contains(circles, Vector(0, 0)) +//│ Bool +//│ res +//│ = false + +Lang.text(circles) +//│ Str +//│ res +//│ = 'the union of two regions of size ' + +Lang.isUniv(circles) +//│ Bool +//│ res +//│ = false + +Lang.isEmpty(circles) +//│ Bool +//│ res +//│ = false + +Lang.size(Lang.eliminate(circles)) +//│ Int +//│ res +//│ = 13 + +:re +Lang.size(mk(100)) +//│ Int +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +:e +:re +Lang.contains(mk(100), Vector(0, 0)) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.400: Lang.contains(mk(100), Vector(0, 0)) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── application of type `Scale[?Region]` does not match type `Circle | Intersect[?Region0] | Outside[?Region1] | Translate[?Region2] | Union[?Region3]` +//│ ║ l.327: _ then Scale(Vector(0, 0), mk(n)) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from reference: +//│ ║ l.113: if a is +//│ ║ ^ +//│ ╟── from field selection: +//│ ║ l.13: class Outside[out Region](a: Region) +//│ ║ ^ +//│ ╟── Note: type parameter Region is defined at: +//│ ║ l.13: class Outside[out Region](a: Region) +//│ ╙── ^^^^^^ +//│ error | false | true +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +:e +:re +Lang.text(mk(100)) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.423: Lang.text(mk(100)) +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ╟── application of type `Scale[?Region]` does not match type `Circle | Intersect[?Region0] | Outside[?Region1] | Translate[?Region2] | Union[?Region3]` +//│ ║ l.327: _ then Scale(Vector(0, 0), mk(n)) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── but it flows into application with expected type `Circle | Intersect[?Region4] | Outside[?Region5] | Translate[?Region6] | Union[?Region7]` +//│ ║ l.423: Lang.text(mk(100)) +//│ ║ ^^^^^^^ +//│ ╟── Note: constraint arises from reference: +//│ ║ l.156: if e is +//│ ╙── ^ +//│ Str | error +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +:re +Lang.isUniv(mk(100)) +//│ Bool +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +:re +Lang.isEmpty(mk(100)) +//│ Bool +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +:re +Lang.size(Lang.eliminate(mk(100))) +//│ Int +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded diff --git a/shared/src/test/diff/fcp-lit/CPS_LB.mls b/shared/src/test/diff/fcp-lit/CPS_LB.mls index c2fa5dd59f..c263cbe54b 100644 --- a/shared/src/test/diff/fcp-lit/CPS_LB.mls +++ b/shared/src/test/diff/fcp-lit/CPS_LB.mls @@ -39,7 +39,7 @@ def x () = insert "x" () // (* e ends the sequence. It builds a token that can be inserted in another sequence or that can be printed. *) def e () acu = insert (concat "," acu) -//│ e: () -> List[string] -> () -> List['a] -> (() -> List['a | string] -> 'b) -> 'b +//│ e: () -> List[string] -> () -> List['a] -> (() -> List[string | 'a] -> 'b) -> 'b // (* Prints a token *) @@ -52,36 +52,36 @@ s x x (s x x e) def test8 = (s x x (s x x e) x (s x x x e) e) -//│ test8: () -> List['a] -> (() -> List['a | string] -> 'b) -> 'b +//│ test8: () -> List['a] -> (() -> List[string | 'a] -> 'b) -> 'b print test8 def test14 = (s x x x x x x x x x x x x x x e) -//│ test14: () -> List['a] -> (() -> List['a | string] -> 'b) -> 'b +//│ test14: () -> List['a] -> (() -> List[string | 'a] -> 'b) -> 'b def test16 = (s x x x x x x x x x x x x x x x x e) -//│ test16: () -> List['a] -> (() -> List['a | string] -> 'b) -> 'b +//│ test16: () -> List['a] -> (() -> List[string | 'a] -> 'b) -> 'b // (* This is too much for the type-checker. *) def test18 = (s x x x x x x x x x x x x x x x x x x e) -//│ test18: () -> List['a] -> (() -> List['a | string] -> 'b) -> 'b +//│ test18: () -> List['a] -> (() -> List[string | 'a] -> 'b) -> 'b :RecursiveTypes def test18 = (s x x x x x x x x x x x x x x x x x x e) -//│ test18: () -> List['a] -> (() -> List['a | string] -> 'b) -> 'b +//│ test18: () -> List['a] -> (() -> List[string | 'a] -> 'b) -> 'b :NoRecursiveTypes // (* A function that receives a token *) def f t = (s x x t x x e) -//│ f: (() -> List["x"] -> (forall 'a 'b. () -> List['a] -> (() -> List["x" | 'a] -> 'b) -> 'b) -> (forall 'a 'c. () -> List['a] -> (() -> List["x" | 'a] -> 'c) -> 'c) -> (forall 'a0 'd. () -> List[string] -> () -> List['a0] -> (() -> List['a0 | string] -> 'd) -> 'd) -> 'e) -> 'e +//│ f: (() -> List["x"] -> (forall 'a 'b. () -> List['a] -> (() -> List["x" | 'a] -> 'b) -> 'b) -> (forall 'a 'c. () -> List['a] -> (() -> List["x" | 'a] -> 'c) -> 'c) -> (forall 'a0 'd. () -> List[string] -> () -> List['a0] -> (() -> List[string | 'a0] -> 'd) -> 'd) -> 'e) -> 'e // (* If the token is used twice, we must reveive two arguments *) def g t1 t2 = (s x x t1 x (s x t2 x e) e) -//│ g: (() -> List["x"] -> (forall 'a 'b. () -> List['a] -> (() -> List["x" | 'a] -> 'b) -> 'b) -> 'c -> (forall 'a0 'd. () -> List[string] -> () -> List['a0] -> (() -> List['a0 | string] -> 'd) -> 'd) -> 'e) -> (() -> List["x"] -> (forall 'a 'f. () -> List['a] -> (() -> List["x" | 'a] -> 'f) -> 'f) -> (forall 'a0 'g. () -> List[string] -> () -> List['a0] -> (() -> List['a0 | string] -> 'g) -> 'g) -> 'c) -> 'e +//│ g: (() -> List["x"] -> (forall 'a 'b. () -> List['a] -> (() -> List["x" | 'a] -> 'b) -> 'b) -> 'c -> (forall 'a0 'd. () -> List[string] -> () -> List['a0] -> (() -> List[string | 'a0] -> 'd) -> 'd) -> 'e) -> (() -> List["x"] -> (forall 'a 'f. () -> List['a] -> (() -> List["x" | 'a] -> 'f) -> 'f) -> (forall 'a0 'g. () -> List[string] -> () -> List['a0] -> (() -> List[string | 'a0] -> 'g) -> 'g) -> 'c) -> 'e // (* This does not type. It requires first-class polymorphism. *) def h t = g t t -//│ h: (() -> List["x"] -> ((forall 'a 'b. () -> List['a] -> (() -> List["x" | 'a] -> 'b) -> 'b) -> 'c -> (forall 'a0 'd. () -> List[string] -> () -> List['a0] -> (() -> List['a0 | string] -> 'd) -> 'd) -> 'e & (forall 'f 'a. () -> List['a] -> (() -> List["x" | 'a] -> 'f) -> 'f) -> (forall 'a0 'g. () -> List[string] -> () -> List['a0] -> (() -> List['a0 | string] -> 'g) -> 'g) -> 'c)) -> 'e +//│ h: (() -> List["x"] -> ((forall 'b 'a. () -> List['a] -> (() -> List["x" | 'a] -> 'b) -> 'b) -> 'c -> (forall 'a0 'd. () -> List[string] -> () -> List['a0] -> (() -> List[string | 'a0] -> 'd) -> 'd) -> 'e & (forall 'f 'a. () -> List['a] -> (() -> List["x" | 'a] -> 'f) -> 'f) -> (forall 'a0 'g. () -> List[string] -> () -> List['a0] -> (() -> List[string | 'a0] -> 'g) -> 'g) -> 'c)) -> 'e diff --git a/shared/src/test/diff/fcp-lit/FreezeML.mls b/shared/src/test/diff/fcp-lit/FreezeML.mls index 0a78c7a6f5..a7643f6d02 100644 --- a/shared/src/test/diff/fcp-lit/FreezeML.mls +++ b/shared/src/test/diff/fcp-lit/FreezeML.mls @@ -113,7 +113,7 @@ rec def length l = //│ List[?] -> int //│ <: length: //│ List[?] -> int -//│ = [Function: length] +//│ = [Function: length1] def id: 'a -> 'a def id x = x @@ -520,7 +520,7 @@ choose id (fun (x: ('a -> 'a)) -> auto_ x) // * The skolem used to (unsafely) leak into this type – see also `Skolems.mls`: res -//│ res: (('a | ??a) -> (??a0 & 'a) & 'a0) -> ('a0 | 'b -> 'b | error) +//│ res: (('a | ??a) -> (??a0 & 'a) & 'a0) -> (error | 'a0 | 'b -> 'b) //│ = [Function: id1] :e // skolem extrusion @@ -537,7 +537,7 @@ res id //│ ╟── Note: constraint arises from type variable: //│ ║ l.503: choose id (fun (x: ('a -> 'a)) -> auto_ x) //│ ╙── ^^ -//│ res: 'b -> 'b | error +//│ res: error | 'b -> 'b //│ = [Function: id1] @@ -552,7 +552,7 @@ a (fun x -> { u = x }) //│ ╟── Note: constraint arises from type variable: //│ ║ l.172: def auto_ : (forall 'a. 'a -> 'a) -> 'b -> 'b //│ ╙── ^^ -//│ res: 'b -> ('b | {u: 'b}) | error +//│ res: error | 'b -> ('b | {u: 'b}) //│ = [Function (anonymous)] diff --git a/shared/src/test/diff/fcp-lit/HMF.mls b/shared/src/test/diff/fcp-lit/HMF.mls index 4a7b48287c..cc017dcd58 100644 --- a/shared/src/test/diff/fcp-lit/HMF.mls +++ b/shared/src/test/diff/fcp-lit/HMF.mls @@ -93,7 +93,7 @@ rec def length l = //│ List[?] -> int //│ <: length: //│ List[?] -> int -//│ = [Function: length] +//│ = [Function: length1] def choose: forall 'a. 'a -> 'a -> 'a def choose x y = if true then x else y diff --git a/shared/src/test/diff/fcp-lit/HML.mls b/shared/src/test/diff/fcp-lit/HML.mls index 3d4860b061..ce4e9e11f0 100644 --- a/shared/src/test/diff/fcp-lit/HML.mls +++ b/shared/src/test/diff/fcp-lit/HML.mls @@ -97,7 +97,7 @@ rec def length l = //│ List[?] -> int //│ <: length: //│ List[?] -> int -//│ = [Function: length] +//│ = [Function: length1] def id: forall 'a. 'a -> 'a def id x = x diff --git a/shared/src/test/diff/fcp-lit/Leijen.mls b/shared/src/test/diff/fcp-lit/Leijen.mls index fa1ecbb858..250cd38876 100644 --- a/shared/src/test/diff/fcp-lit/Leijen.mls +++ b/shared/src/test/diff/fcp-lit/Leijen.mls @@ -123,7 +123,7 @@ rec def length l = //│ List[?] -> int //│ <: length: //│ List[?] -> int -//│ = [Function: length] +//│ = [Function: length1] def id: 'a -> 'a def id x = x @@ -228,20 +228,20 @@ def revapp x f = f x def fst: forall 'a 'b. (('a, 'b),) -> 'a def fst ((x, _),) = x -//│ fst: ('a, anything,) -> 'a +//│ fst: (('a, anything,),) -> 'a //│ = -//│ ('a, anything,) -> 'a +//│ (('a, anything,),) -> 'a //│ <: fst: -//│ ('a, anything,) -> 'a +//│ (('a, anything,),) -> 'a //│ = [Function: fst] def snd: forall 'a 'b. (('a, 'b),) -> 'b def snd ((_, x),) = x -//│ snd: (anything, 'b,) -> 'b +//│ snd: ((anything, 'b,),) -> 'b //│ = -//│ (anything, 'a,) -> 'a +//│ ((anything, 'a,),) -> 'a //│ <: snd: -//│ (anything, 'b,) -> 'b +//│ ((anything, 'b,),) -> 'b //│ = [Function: snd] :ng @@ -974,11 +974,7 @@ test2 = test1 //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.955: def test2: List[forall 'a. 'a -> 'a] -//│ ║ ^^ -//│ ╟── adding a type annotation to any of the following terms may help resolve the problem -//│ ╟── • this reference: -//│ ║ l.965: test2 = test1 -//│ ╙── ^^^^^ +//│ ╙── ^^ @@ -999,36 +995,32 @@ map xauto ids :e map xauto (map xauto ids) //│ ╔══[ERROR] Type error in application -//│ ║ l.1000: map xauto (map xauto ids) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.996: map xauto (map xauto ids) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'b` leaks out of its scope //│ ║ l.173: def xauto : forall 'a. (forall 'b. 'b -> 'b) -> 'a -> 'a //│ ║ ^^ //│ ╟── back into type variable `'b` //│ ║ l.173: def xauto : forall 'a. (forall 'b. 'b -> 'b) -> 'a -> 'a -//│ ║ ^^ -//│ ╟── adding a type annotation to any of the following terms may help resolve the problem -//│ ╟── • this application: -//│ ║ l.1000: map xauto (map xauto ids) -//│ ╙── ^^^^^^^^^^^^^ -//│ res: error | List['a -> 'a] +//│ ╙── ^^ +//│ res: List['a -> 'a] | error // * This doesn't work (without distrib.) because we don't currently generalize non-functions: :e (fun x -> xauto x) : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ╔══[ERROR] Type error in type ascription -//│ ║ l.1019: (fun x -> xauto x) : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) +//│ ║ l.1011: (fun x -> xauto x) : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope -//│ ║ l.1019: (fun x -> xauto x) : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) +//│ ║ l.1011: (fun x -> xauto x) : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ║ ^^ //│ ╟── back into type variable `'a` -//│ ║ l.1019: (fun x -> xauto x) : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) +//│ ║ l.1011: (fun x -> xauto x) : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: -//│ ║ l.1019: (fun x -> xauto x) : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) +//│ ║ l.1011: (fun x -> xauto x) : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ╙── ^^^^^^^ //│ res: (forall 'a. 'a -> 'a) -> (forall 'a0. 'a0 -> 'a0) // * But we can trivially fix it by forcing generalization through a polymorphic let binding: @@ -1038,17 +1030,17 @@ map xauto (map xauto ids) :e xauto : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ╔══[ERROR] Type error in type ascription -//│ ║ l.1039: xauto : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) +//│ ║ l.1031: xauto : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ║ ^^^^^ //│ ╟── type variable `'a` leaks out of its scope -//│ ║ l.1039: xauto : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) +//│ ║ l.1031: xauto : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ║ ^^ //│ ╟── back into type variable `'a` -//│ ║ l.1039: xauto : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) +//│ ║ l.1031: xauto : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: -//│ ║ l.1039: xauto : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) +//│ ║ l.1031: xauto : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ╙── ^^^^^ //│ res: (forall 'a. 'a -> 'a) -> (forall 'a0. 'a0 -> 'a0) diff --git a/shared/src/test/diff/fcp-lit/MLF.mls b/shared/src/test/diff/fcp-lit/MLF.mls index 31b814a71a..6cefd4e767 100644 --- a/shared/src/test/diff/fcp-lit/MLF.mls +++ b/shared/src/test/diff/fcp-lit/MLF.mls @@ -114,7 +114,7 @@ let f = choose id in (f auto) (f succ) //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.28: def auto (x: forall 'a. 'a -> 'a) = x x //│ ╙── ^^ -//│ res: int -> int | error +//│ res: error | int -> int // * Reproduced with an unnanotated auto: @@ -135,7 +135,7 @@ res (choose id succ) //│ ╟── but it flows into application with expected type `int` //│ ║ l.128: res (choose id succ) //│ ╙── ^^^^^^^^^^^^^^ -//│ res: int -> int | error | int +//│ res: error | int | int -> int // * Didier Le Botlan suggested this fix by email: @@ -323,15 +323,15 @@ def packed_pair = pack (1, fun x -> (x, x )) def fst((x,y)) = x def snd((x,y)) = y -//│ fst: ('a, anything,) -> 'a -//│ snd: (anything, 'a,) -> 'a +//│ fst: (('a, anything,),) -> 'a +//│ snd: ((anything, 'a,),) -> 'a v = packed_int (fun p -> (snd p) ( fst p )) //│ v: int def a: ' -> ' -//│ a: ' -> ' +//│ a: 'a -> 'a diff --git a/shared/src/test/diff/fcp-lit/PolyML.mls b/shared/src/test/diff/fcp-lit/PolyML.mls index e0ce0e1f65..a49044fd7c 100644 --- a/shared/src/test/diff/fcp-lit/PolyML.mls +++ b/shared/src/test/diff/fcp-lit/PolyML.mls @@ -76,7 +76,7 @@ def mem[a]: a -> List[a] -> bool //│ mem: anything -> List[?] -> bool rec def mem x l = match_list l false (fun head -> fun tail -> if eq head x then true else mem x tail) -//│ anything -> List[?] -> (false | true) +//│ anything -> List[?] -> Bool //│ <: mem: //│ anything -> List[?] -> bool diff --git a/shared/src/test/diff/fcp-lit/QML.mls b/shared/src/test/diff/fcp-lit/QML.mls index 757d071d20..df27d94faf 100644 --- a/shared/src/test/diff/fcp-lit/QML.mls +++ b/shared/src/test/diff/fcp-lit/QML.mls @@ -81,17 +81,17 @@ def fst: forall 'a 'b. (('a, 'b),) -> 'a def fst ((x, _),) = x def snd: forall 'a 'b. (('a, 'b),) -> 'b def snd ((_, x),) = x -//│ fst: ('a, anything,) -> 'a +//│ fst: (('a, anything,),) -> 'a //│ = -//│ ('a, anything,) -> 'a +//│ (('a, anything,),) -> 'a //│ <: fst: -//│ ('a, anything,) -> 'a +//│ (('a, anything,),) -> 'a //│ = [Function: fst] -//│ snd: (anything, 'b,) -> 'b +//│ snd: ((anything, 'b,),) -> 'b //│ = -//│ (anything, 'a,) -> 'a +//│ ((anything, 'a,),) -> 'a //│ <: snd: -//│ (anything, 'b,) -> 'b +//│ ((anything, 'b,),) -> 'b //│ = [Function: snd] // def pack : @@ -105,7 +105,7 @@ def open p t = p t // Altered from // let f = {exists 'a. 'a * ('a -> 'a) * ('a -> int)} (0, (fun x -> x + 1) , (fun x -> x)); -// open {exists 'a. a * ('a -> 'a) * ('a -> int)} g = f in (snd (snd g)) (fst g);; +// open {exists 'a. a * ('a -> 'a) * ('a -> int)} g = f in (snd (snd g)) (fst g); let f = pack ((0, (fun x -> x + 1, fun x -> x),),) in open f (fun x -> (snd (snd x)) (fst x)) //│ res: 0 @@ -202,7 +202,7 @@ def sstep = fun xx -> xx (fun (xinit, xsub) -> then xsub r1 (div i 2) else xsub r2 (div i 2) in fun f -> f (init, sub)) -//│ ((forall 'a 'b 'c 'd 'e. ('d -> 'e, 'a -> int -> 'b,) -> (('d -> ('e, 'e,), ('a, 'a,) -> int -> 'b,) -> 'c) -> 'c) -> 'f) -> 'f +//│ ((forall 'a 'b 'c 'd 'e. ('e -> 'a, 'b -> int -> 'c,) -> (('e -> ('a, 'a,), (('b, 'b,),) -> int -> 'c,) -> 'd) -> 'd) -> 'f) -> 'f //│ <: sstep: //│ ExSmall -> ExSmall //│ ╔══[ERROR] Type error in def definition @@ -415,7 +415,7 @@ def base : ExSig def base f = f ((((fun a -> a, fun r -> fun (i : int) -> r), fun r -> fun (i : int) -> fun a -> a), fun f -> fun b -> fun r -> f r b),) //│ base: ExSig //│ = -//│ ((((forall 'a. 'a -> 'a, forall 'b. 'b -> int -> 'b,), forall 'c. anything -> int -> 'c -> 'c,), forall 'd 'e 'f. ('d -> 'e -> 'f) -> 'e -> 'd -> 'f,) -> 'g) -> 'g +//│ (((((forall 'a. 'a -> 'a, forall 'b. 'b -> int -> 'b,), forall 'c. anything -> int -> 'c -> 'c,), forall 'd 'e 'f. ('d -> 'e -> 'f) -> 'e -> 'd -> 'f,),) -> 'g) -> 'g //│ <: base: //│ ExSig //│ = [Function: base] @@ -440,7 +440,7 @@ def step = fun xx -> xx (fun ((((xinit, xsub), xupdate), xfold),) -> else (fst r, xupdate (snd r) (div i 2) a) in let fold f b r = xfold f (xfold f b (fst r)) (snd r) in fun f -> f ((((init, sub), update), fold),) ) -//│ ((forall 'a 'b 'c 'd 'e 'f 'g 'h 'i 'j 'k 'l 'm 'n 'o 'p. ((('g -> 'k, 'l -> int -> 'a,), 'e -> int -> 'h -> 'd & 'p -> int -> 'h -> 'm,), 'i -> ('f -> 'c -> 'n & 'j -> 'b -> 'f),) -> (((('g -> ('k, 'k,), ('l, 'l,) -> int -> 'a,), forall 'q 'r. ('e & 'q, 'p & 'r,) -> int -> 'h -> ('d | 'q, 'r | 'm,),), 'i -> 'j -> ('b, 'c,) -> 'n,) -> 'o) -> 'o) -> 's) -> 's +//│ ((forall 'a 'b 'c 'd 'e 'f 'g 'h 'i 'j 'k 'l 'm 'n 'o 'p. (((('l -> 'm, 'b -> int -> 'f,), 'i -> int -> 'e -> 'p & 'n -> int -> 'e -> 'j,), 'h -> ('d -> 'o -> 'k & 'c -> 'g -> 'd),),) -> ((((('l -> ('m, 'm,), (('b, 'b,),) -> int -> 'f,), forall 'q 'r. (('i & 'q, 'n & 'r,),) -> int -> 'e -> ('p | 'q, 'r | 'j,),), 'h -> 'c -> (('g, 'o,),) -> 'k,),) -> 'a) -> 'a) -> 's) -> 's //│ <: step: //│ ExSig -> ExSig //│ ╔══[ERROR] Type error in def definition diff --git a/shared/src/test/diff/fcp-lit/Stability.mls b/shared/src/test/diff/fcp-lit/Stability.mls index dad989e542..3859ca53de 100644 --- a/shared/src/test/diff/fcp-lit/Stability.mls +++ b/shared/src/test/diff/fcp-lit/Stability.mls @@ -104,7 +104,7 @@ undef = error //│ <: undef: //│ int -> 'a -> 'a //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown // swizzle :: Int → ∀ a. a → a // swizzle = undef diff --git a/shared/src/test/diff/fcp-lit/variations_PolyML.mls b/shared/src/test/diff/fcp-lit/variations_PolyML.mls index e633b6b4ed..9a971c8160 100644 --- a/shared/src/test/diff/fcp-lit/variations_PolyML.mls +++ b/shared/src/test/diff/fcp-lit/variations_PolyML.mls @@ -28,7 +28,7 @@ rec def mem x l = case l of { Nil -> false | Cons -> if eq l.head x then true else mem x l.tail } -//│ mem: anything -> 'a -> bool +//│ mem: anything -> 'a -> Bool //│ where //│ 'a <: (Cons[?] with {tail: 'a}) | Nil @@ -46,21 +46,21 @@ class Collection[a]: { l: List[a] } method Mem x = mem x this.l method Fold f x = fold_left f x this.l //│ Defined class Collection[+a] -//│ Defined Collection.Mem: Collection[?] -> anything -> bool +//│ Defined Collection.Mem: Collection[?] -> anything -> Bool //│ Defined Collection.Fold: Collection['a] -> ('b -> 'a -> 'b) -> 'b -> 'b def coll_mem c x = c.Mem x -//│ coll_mem: Collection[?] -> anything -> bool +//│ coll_mem: Collection[?] -> anything -> Bool // * Typo in the paper? it was `fun x -> fun y -> ...` def simple_and_double c = let l1 = c.Fold (fun y -> fun x -> Cons x y) Nil in let l2 = c.Fold (fun y -> fun x -> Cons ((x, x),) y) Nil in (l1, l2) -//│ simple_and_double: Collection['a] -> (forall 'tail. 'tail, forall 'tail0. 'tail0,) +//│ simple_and_double: Collection['a] -> (forall 'b. Nil | 'b, forall 'c. Nil | 'c,) //│ where -//│ 'tail0 :> (Cons[('a, 'a,)] with {tail: 'tail0}) | Nil -//│ 'tail :> (Cons['a] with {tail: 'tail}) | Nil +//│ 'c :> Cons[('a, 'a,)] with {tail: forall 'c. Nil | 'c} +//│ 'b :> Cons['a] with {tail: forall 'b. Nil | 'b} @@ -78,7 +78,7 @@ rec def mem x l = case l of //│ 'a <: {head: ?, tail: Cons[?] & 'a} //│ ║ l.72: rec def mem x l = case l of //│ ╙── ^ -//│ mem: anything -> 'a -> bool +//│ mem: anything -> 'a -> Bool //│ where //│ 'a <: (Cons[?] with {tail: 'a}) | Nil diff --git a/shared/src/test/diff/fcp/Aleks.mls b/shared/src/test/diff/fcp/Aleks.mls index 6609961be2..bb200646c9 100644 --- a/shared/src/test/diff/fcp/Aleks.mls +++ b/shared/src/test/diff/fcp/Aleks.mls @@ -52,7 +52,7 @@ r1 id //│ ╟── Note: constraint arises from type variable: //│ ║ l.18: def foo: forall 'x. ('x -> 'x, 'x -> 'x) //│ ╙── ^^ -//│ res: 'a -> 'a | error | ??y +//│ res: error | 'a -> 'a | ??y :e r2 id @@ -72,6 +72,6 @@ r2 id //│ ╟── Note: constraint arises from type variable: //│ ║ l.18: def foo: forall 'x. ('x -> 'x, 'x -> 'x) //│ ╙── ^^ -//│ res: 'a -> 'a | error | ??y +//│ res: error | 'a -> 'a | ??y diff --git a/shared/src/test/diff/fcp/Basics.mls b/shared/src/test/diff/fcp/Basics.mls index 3d60331508..7483007b2f 100644 --- a/shared/src/test/diff/fcp/Basics.mls +++ b/shared/src/test/diff/fcp/Basics.mls @@ -67,7 +67,7 @@ example 1 //│ ╟── from reference: //│ ║ l.52: def example f = (f: forall 'a. 'a -> 'a) //│ ╙── ^ -//│ res: 'a -> 'a | error +//│ res: error | 'a -> 'a //│ = 1 :e @@ -81,7 +81,7 @@ example succ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.52: def example f = (f: forall 'a. 'a -> 'a) //│ ╙── ^^ -//│ res: 'a -> 'a | error +//│ res: error | 'a -> 'a //│ = [Function: succ] example id @@ -124,7 +124,7 @@ ex 1 //│ ╟── Note: constraint arises from reference: //│ ║ l.108: def ex x = example x //│ ╙── ^ -//│ res: (0, nothing,) | error +//│ res: error | (0, nothing,) //│ = [ 0, 1 ] def example f = ((0, f) : forall 'a. (0, 'a -> 'a)) diff --git a/shared/src/test/diff/fcp/Church_CT.mls b/shared/src/test/diff/fcp/Church_CT.mls index 62e0c4cd01..3ab215b05e 100644 --- a/shared/src/test/diff/fcp/Church_CT.mls +++ b/shared/src/test/diff/fcp/Church_CT.mls @@ -37,10 +37,10 @@ def succ: (forall 'N. ('N -> 'N) -> ('N -> 'N)) -> (forall 'M. ('M -> 'M) -> ('M :e // * Since "sound extrusion" def succ n f x = f (n f x) -//│ 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. ('c -> 'e +//│ 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'd -> 'c //│ where -//│ 'b <: 'd -> 'e -//│ 'a <: 'b -> 'c -> 'd))) +//│ 'a <: 'b -> 'd -> 'e +//│ 'b <: 'e -> 'c)) //│ <: succ: //│ (forall 'N. ('N -> 'N) -> 'N -> 'N) -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition @@ -67,10 +67,10 @@ def succ: ChurchInt -> ChurchInt :e // * Since "sound extrusion" def succ n f x = f (n f x) -//│ 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. ('d -> 'c +//│ 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'e -> 'd //│ where -//│ 'b <: 'e -> 'c -//│ 'a <: 'b -> 'd -> 'e))) +//│ 'b <: 'c -> 'd +//│ 'a <: 'b -> 'e -> 'c)) //│ <: succ: //│ ChurchInt -> ChurchInt //│ ╔══[ERROR] Type error in def definition @@ -93,10 +93,10 @@ def succD: forall 'M. ChurchInt -> ('M -> 'M) -> ('M -> 'M) def succD n f x = f (n f x) //│ succD: ChurchInt -> ('M -> 'M) -> 'M -> 'M //│ = -//│ 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. ('c -> 'e +//│ 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'd -> 'c //│ where -//│ 'a <: 'b -> 'c -> 'd -//│ 'b <: 'd -> 'e))) +//│ 'a <: 'b -> 'd -> 'e +//│ 'b <: 'e -> 'c)) //│ <: succD: //│ ChurchInt -> ('M -> 'M) -> 'M -> 'M //│ = [Function: succD] @@ -324,10 +324,10 @@ def z f x = x //│ = [Function: z] def s n f x = f (n f x) -//│ s: 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. ('c -> 'e +//│ s: 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where //│ 'a <: 'b -> 'c -> 'd -//│ 'b <: 'd -> 'e))) +//│ 'b <: 'd -> 'e)) //│ = [Function: s] zero = z @@ -338,10 +338,10 @@ zero = z :e // * Since "sound extrusion" succ = s -//│ 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. ('c -> 'e +//│ 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where //│ 'b <: 'd -> 'e -//│ 'a <: 'b -> 'c -> 'd))) +//│ 'a <: 'b -> 'c -> 'd)) //│ <: succ: //│ ChurchInt -> ChurchInt //│ ╔══[ERROR] Type error in def definition @@ -391,9 +391,9 @@ s: ChurchInt -> ChurchInt //│ ╙── ^ //│ res: ChurchInt -> ChurchInt //│ = [Function: s] -//│ constrain calls : 104 +//│ constrain calls : 103 //│ annoying calls : 0 -//│ subtyping calls : 322 +//│ subtyping calls : 321 @@ -401,33 +401,33 @@ s: ChurchInt -> ChurchInt n1 = s z -//│ n1: 'a -> (forall 'b 'c 'd. ('b -> 'd +//│ n1: 'a -> (forall 'b 'c 'd. 'b -> 'd //│ where //│ 'a <: 'c -> 'd -//│ anything -> (forall 'e. 'e -> 'e) <: 'a -> 'b -> 'c)) +//│ anything -> (forall 'e. 'e -> 'e) <: 'a -> 'b -> 'c) //│ = [Function (anonymous)] n2 = s (s z) -//│ n2: 'a -> (forall 'b 'c 'd. ('b -> 'd +//│ n2: 'a -> (forall 'b 'c 'd. 'b -> 'd //│ where //│ 'a <: 'c -> 'd -//│ forall 'e. 'e -> (forall 'f 'g 'h. ('f -> 'h -//│ where -//│ anything -> (forall 'i. 'i -> 'i) <: 'e -> 'f -> 'g -//│ 'e <: 'g -> 'h)) <: 'a -> 'b -> 'c)) +//│ forall 'e. 'e -> (forall 'f 'g 'h. 'f -> 'h +//│ where +//│ anything -> (forall 'i. 'i -> 'i) <: 'e -> 'f -> 'g +//│ 'e <: 'g -> 'h) <: 'a -> 'b -> 'c) //│ = [Function (anonymous)] n3 = s (s (s z)) -//│ n3: 'a -> (forall 'b 'c 'd. ('b -> 'd -//│ where -//│ forall 'e. 'e -> (forall 'f 'g 'h. ('f -> 'h -//│ where -//│ 'e <: 'g -> 'h -//│ forall 'i. 'i -> (forall 'j 'k 'l. ('j -> 'l -//│ where -//│ anything -> (forall 'm. 'm -> 'm) <: 'i -> 'j -> 'k -//│ 'i <: 'k -> 'l)) <: 'e -> 'f -> 'g)) <: 'a -> 'b -> 'c -//│ 'a <: 'c -> 'd)) +//│ n3: 'a -> (forall 'b 'c 'd. 'c -> 'b +//│ where +//│ forall 'e. 'e -> (forall 'f 'g 'h. 'f -> 'h +//│ where +//│ forall 'i. 'i -> (forall 'j 'k 'l. 'j -> 'l +//│ where +//│ 'i <: 'k -> 'l +//│ anything -> (forall 'm. 'm -> 'm) <: 'i -> 'j -> 'k) <: 'e -> 'f -> 'g +//│ 'e <: 'g -> 'h) <: 'a -> 'c -> 'd +//│ 'a <: 'd -> 'b) //│ = [Function (anonymous)] @@ -518,21 +518,21 @@ res.x.x.x + 1 sz = s zero -//│ sz: 'a -> (forall 'b 'c 'd. ('c -> 'b +//│ sz: 'a -> (forall 'b 'c 'd. 'd -> 'c //│ where -//│ ChurchInt <: 'a -> 'c -> 'd -//│ 'a <: 'd -> 'b)) +//│ ChurchInt <: 'a -> 'd -> 'b +//│ 'a <: 'b -> 'c) //│ = [Function (anonymous)] :ns sz //│ res: forall 'a 'b. 'a //│ where -//│ 'a :> forall 'c. 'c -> (forall 'd 'e 'f 'g. ('g -> 'e -//│ where -//│ 'b <: 'c -> 'f -//│ 'c <: 'd -> 'e)) -//│ 'f <: 'g -> 'd +//│ 'a :> forall 'c. 'c -> (forall 'd 'e 'f 'g. 'd -> 'f +//│ where +//│ 'c <: 'e -> 'f +//│ 'b <: 'c -> 'g) +//│ 'g <: 'd -> 'e //│ 'b :> ChurchInt //│ = [Function (anonymous)] @@ -567,10 +567,10 @@ sz1 = sz 1 sz1 //│ res: forall 'a 'b 'c. 'b //│ where -//│ 'b :> forall 'd 'e 'f 'g. ('e -> 'g -//│ where -//│ 'a <: 'f -> 'g -//│ 'c <: 'a -> 'd) +//│ 'b :> forall 'd 'e 'f 'g. 'e -> 'g +//│ where +//│ 'a <: 'f -> 'g +//│ 'c <: 'a -> 'd //│ 'd <: 'e -> 'f //│ 'c :> ChurchInt //│ 'a :> 1 @@ -631,17 +631,17 @@ rec def to_ch_s n = else s (to_ch_s (n - 1)) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a :> forall 'b. (? & 'b) -> (forall 'c 'd 'e 'f 'b. (('d & 'c) -> ('d | 'f) -//│ where -//│ 'a <: 'b -> 'c -> 'e -//│ 'b <: 'e -> 'f)) +//│ 'a :> forall 'b. (? & 'b) -> (forall 'b 'c 'd 'e 'f. ('c & 'f) -> ('c | 'e) +//│ where +//│ 'b <: 'd -> 'e +//│ 'a <: 'b -> 'f -> 'd) //│ ╙── //│ to_ch_s: int -> 'a //│ where -//│ 'a :> forall 'b. 'b -> (forall 'c 'd 'e 'b 'f. (('e & 'd) -> ('e | 'f) -//│ where -//│ 'a <: 'b -> 'd -> 'c -//│ 'b <: 'c -> 'f)) +//│ 'a :> forall 'b. 'b -> (forall 'c 'b 'd 'e 'f. ('d & 'c) -> ('d | 'e) +//│ where +//│ 'b <: 'f -> 'e +//│ 'a <: 'b -> 'c -> 'f) //│ = [Function: to_ch_s] :e @@ -650,37 +650,37 @@ rec def to_ch n = else s (to_ch (n - 1)) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a :> forall 'b. (? & 'b) -> (forall 'b 'c 'd 'e 'f. (('c & 'f) -> ('c | 'd) -//│ where -//│ 'b <: 'e -> 'd -//│ 'a <: 'b -> 'f -> 'e)) +//│ 'a :> forall 'b. (? & 'b) -> (forall 'c 'd 'e 'b 'f. ('f & 'e) -> ('f | 'c) +//│ where +//│ 'a <: 'b -> 'e -> 'd +//│ 'b <: 'd -> 'c) //│ ╙── //│ to_ch: int -> 'a //│ where -//│ 'a :> forall 'b. 'b -> (forall 'c 'b 'd 'e 'f. (('c & 'd) -> ('c | 'e) -//│ where -//│ 'b <: 'f -> 'e -//│ 'a <: 'b -> 'd -> 'f)) +//│ 'a :> forall 'b. 'b -> (forall 'c 'd 'b 'e 'f. ('c & 'f) -> ('c | 'd) +//│ where +//│ 'a <: 'b -> 'f -> 'e +//│ 'b <: 'e -> 'd) //│ = [Function: to_ch] :e // * Needs distrib (see below) to_church_ty = to_ch //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a :> forall 'b. (? & 'b) -> (forall 'c 'd 'e 'f 'b. (('d & 'c) -> ('d | 'f) -//│ where -//│ 'a <: 'b -> 'c -> 'e -//│ 'b <: 'e -> 'f)) +//│ 'a :> forall 'b. (? & 'b) -> (forall 'b 'c 'd 'e 'f. ('f & 'd) -> ('f | 'c) +//│ where +//│ 'a <: 'b -> 'd -> 'e +//│ 'b <: 'e -> 'c) //│ ╙── //│ int -> 'a //│ where -//│ 'a :> forall 'b. 'b -> (forall 'c 'd 'b 'e 'f. (('d & 'e) -> ('d | 'c) -//│ where -//│ 'b <: 'f -> 'c -//│ 'a <: 'b -> 'e -> 'f)) +//│ 'a :> forall 'b. 'b -> (forall 'c 'd 'e 'b 'f. ('e & 'f) -> ('e | 'd) +//│ where +//│ 'b <: 'c -> 'd +//│ 'a <: 'b -> 'f -> 'c) //│ <: to_church_ty: //│ int -> ChurchInt -//│ ╔══[ERROR] Subtyping constraint of the form `forall ?a ?b ?c ?d ?to_ch. ?to_ch <: int -> ChurchInt` exceeded recursion depth limit (250) +//│ ╔══[ERROR] Subtyping constraint of the form `forall ?to_ch. ?to_ch <: int -> ChurchInt` exceeded recursion depth limit (250) //│ ║ l.667: to_church_ty = to_ch //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. @@ -690,37 +690,37 @@ to_church_ty = to_ch rec def to_ch_simplif n = s (to_ch_simplif n) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a :> forall 'b. 'b -> (forall 'c 'd 'e. ('c -> 'e -//│ where -//│ 'b <: 'd -> 'e -//│ 'a <: 'b -> 'c -> 'd)) +//│ 'a :> forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e +//│ where +//│ 'a <: 'b -> 'c -> 'd +//│ 'b <: 'd -> 'e) //│ ╙── //│ to_ch_simplif: anything -> 'a //│ where -//│ 'a :> forall 'b. 'b -> (forall 'c 'd 'e. ('c -> 'e -//│ where -//│ 'a <: 'b -> 'c -> 'd -//│ 'b <: 'd -> 'e)) +//│ 'a :> forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e +//│ where +//│ 'a <: 'b -> 'c -> 'd +//│ 'b <: 'd -> 'e) //│ = [Function: to_ch_simplif] :e to_church_ty = to_ch_simplif //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a :> forall 'b. 'b -> (forall 'c 'd 'e. ('c -> 'e -//│ where -//│ 'b <: 'd -> 'e -//│ 'a <: 'b -> 'c -> 'd)) +//│ 'a :> forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e +//│ where +//│ 'b <: 'd -> 'e +//│ 'a <: 'b -> 'c -> 'd) //│ ╙── //│ anything -> 'a //│ where -//│ 'a :> forall 'b. 'b -> (forall 'c 'd 'e. ('e -> 'd -//│ where -//│ 'a <: 'b -> 'e -> 'c -//│ 'b <: 'c -> 'd)) +//│ 'a :> forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e +//│ where +//│ 'a <: 'b -> 'c -> 'd +//│ 'b <: 'd -> 'e) //│ <: to_church_ty: //│ int -> ChurchInt -//│ ╔══[ERROR] Subtyping constraint of the form `forall ?a ?b ?c ?d ?to_ch_simplif. ?to_ch_simplif <: int -> ChurchInt` exceeded recursion depth limit (250) +//│ ╔══[ERROR] Subtyping constraint of the form `forall ?to_ch_simplif. ?to_ch_simplif <: int -> ChurchInt` exceeded recursion depth limit (250) //│ ║ l.707: to_church_ty = to_ch_simplif //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. @@ -790,10 +790,10 @@ rec def to_ch_A1 n = //│ ╟── Note: constraint arises from application: //│ ║ l.326: def s n f x = f (n f x) //│ ╙── ^^^^^ -//│ to_ch_A1: int -> (forall 'a. 'a -> (forall 'b 'c 'd 'a 'e. (('c & 'd) -> ('d | 'e) +//│ to_ch_A1: int -> (forall 'a. 'a -> (forall 'b 'a 'c 'd 'e. ('b & 'c) -> ('c | 'd) //│ where -//│ 'a <: 'b -> 'e -//│ ChurchInt <: 'a -> 'c -> 'b))) +//│ ChurchInt <: 'a -> 'b -> 'e +//│ 'a <: 'e -> 'd)) //│ = [Function: to_ch_A1] :precise-rec-typing @@ -803,21 +803,21 @@ rec def to_ch_A1 n = else s (to_ch_A1 (n - 1) : ChurchInt) //│ ╔══[ERROR] Inferred recursive type: 'to_ch_A1 //│ where -//│ 'to_ch_A1 :> forall 'to_ch_A1. (int -> (forall 'a. (? & 'a) -> (forall 'b 'c 'd 'a 'e. (('d & 'c) -> ('c | 'b) -//│ where -//│ ChurchInt <: 'a -> 'd -> 'e -//│ 'a <: 'e -> 'b))) -//│ where -//│ 'to_ch_A1 <: int -> ChurchInt) +//│ 'to_ch_A1 :> forall 'to_ch_A1. int -> (forall 'a. (? & 'a) -> (forall 'b 'c 'a 'd 'e. ('e & 'd) -> ('d | 'c) +//│ where +//│ 'a <: 'b -> 'c +//│ ChurchInt <: 'a -> 'e -> 'b)) +//│ where +//│ 'to_ch_A1 <: int -> ChurchInt //│ ╙── //│ to_ch_A1: 'to_ch_A1 //│ where -//│ 'to_ch_A1 :> forall 'to_ch_A1. (int -> (forall 'a. 'a -> (forall 'b 'c 'd 'a 'e. (('b & 'd) -> ('d | 'e) -//│ where -//│ ChurchInt <: 'a -> 'b -> 'c -//│ 'a <: 'c -> 'e))) -//│ where -//│ 'to_ch_A1 <: int -> ChurchInt) +//│ 'to_ch_A1 :> forall 'to_ch_A1. int -> (forall 'a. 'a -> (forall 'a 'b 'c 'd 'e. ('b & 'c) -> ('c | 'd) +//│ where +//│ 'a <: 'e -> 'd +//│ ChurchInt <: 'a -> 'b -> 'e)) +//│ where +//│ 'to_ch_A1 <: int -> ChurchInt //│ = [Function: to_ch_A11] // * But we can't check the corresponding type @@ -825,21 +825,21 @@ rec def to_ch_A1 n = to_church_ty = to_ch_A1 //│ ╔══[ERROR] Inferred recursive type: 'to_ch_A1 //│ where -//│ 'to_ch_A1 :> forall 'to_ch_A1. (int -> (forall 'a. (? & 'a) -> (forall 'b 'a 'c 'd 'e. (('c & 'b) -> ('b | 'e) -//│ where -//│ ChurchInt <: 'a -> 'c -> 'd -//│ 'a <: 'd -> 'e))) -//│ where -//│ 'to_ch_A1 <: int -> ChurchInt) +//│ 'to_ch_A1 :> forall 'to_ch_A1. int -> (forall 'a. (? & 'a) -> (forall 'b 'c 'a 'd 'e. ('d & 'c) -> ('c | 'b) +//│ where +//│ 'a <: 'e -> 'b +//│ ChurchInt <: 'a -> 'd -> 'e)) +//│ where +//│ 'to_ch_A1 <: int -> ChurchInt //│ ╙── //│ 'to_ch_A1 //│ where -//│ 'to_ch_A1 :> forall 'to_ch_A1. (int -> (forall 'a. 'a -> (forall 'b 'a 'c 'd 'e. (('b & 'c) -> ('c | 'd) -//│ where -//│ ChurchInt <: 'a -> 'b -> 'e -//│ 'a <: 'e -> 'd))) -//│ where -//│ 'to_ch_A1 <: int -> ChurchInt) +//│ 'to_ch_A1 :> forall 'to_ch_A1. int -> (forall 'a. 'a -> (forall 'a 'b 'c 'd 'e. ('e & 'b) -> ('b | 'c) +//│ where +//│ ChurchInt <: 'a -> 'e -> 'd +//│ 'a <: 'd -> 'c)) +//│ where +//│ 'to_ch_A1 <: int -> ChurchInt //│ <: to_church_ty: //│ int -> ChurchInt //│ ╔══[ERROR] Cyclic-looking constraint while typing def definition; a type annotation may be required @@ -931,30 +931,30 @@ rec def to_ch_A2 n = ( ) : ChurchInt //│ ╔══[ERROR] Inferred recursive type: 'to_ch_A2 //│ where -//│ 'to_ch_A2 :> forall 'to_ch_A2. (int -> ChurchInt -//│ where -//│ 'to_ch_A2 <: int -> (??N -> ??N0) -> ??N0 -> ??N) +//│ 'to_ch_A2 :> forall 'to_ch_A2. int -> ChurchInt +//│ where +//│ 'to_ch_A2 <: int -> (??N -> ??N0) -> ??N0 -> ??N //│ ╙── //│ to_ch_A2: 'to_ch_A2 //│ where -//│ 'to_ch_A2 :> forall 'to_ch_A2. (int -> ChurchInt -//│ where -//│ 'to_ch_A2 <: int -> (??N -> ??N0) -> ??N0 -> ??N) +//│ 'to_ch_A2 :> forall 'to_ch_A2. int -> ChurchInt +//│ where +//│ 'to_ch_A2 <: int -> (??N -> ??N0) -> ??N0 -> ??N //│ = [Function: to_ch_A21] :e // * Since the removal of "recursive definition hacks" to_church_ty = to_ch_A2 //│ ╔══[ERROR] Inferred recursive type: 'to_ch_A2 //│ where -//│ 'to_ch_A2 :> forall 'to_ch_A2. (int -> ChurchInt -//│ where -//│ 'to_ch_A2 <: int -> (??N -> ??N0) -> ??N0 -> ??N) +//│ 'to_ch_A2 :> forall 'to_ch_A2. int -> ChurchInt +//│ where +//│ 'to_ch_A2 <: int -> (??N -> ??N0) -> ??N0 -> ??N //│ ╙── //│ 'to_ch_A2 //│ where -//│ 'to_ch_A2 :> forall 'to_ch_A2. (int -> ChurchInt -//│ where -//│ 'to_ch_A2 <: int -> (??N -> ??N0) -> ??N0 -> ??N) +//│ 'to_ch_A2 :> forall 'to_ch_A2. int -> ChurchInt +//│ where +//│ 'to_ch_A2 <: int -> (??N -> ??N0) -> ??N0 -> ??N //│ <: to_church_ty: //│ int -> ChurchInt //│ ╔══[ERROR] Cyclic-looking constraint while typing def definition; a type annotation may be required @@ -968,18 +968,18 @@ to_church_ty = to_ch_A2 def to_church_mix n = if n == 0 then z else s (to_church (n - 1)) -//│ to_church_mix: int -> (forall 'a. 'a -> (forall 'b 'c 'a 'd 'e. (('d & 'c) -> ('c | 'b) +//│ to_church_mix: int -> (forall 'a. 'a -> (forall 'b 'c 'a 'd 'e. ('e & 'd) -> ('d | 'c) //│ where -//│ ChurchInt <: 'a -> 'd -> 'e -//│ 'a <: 'e -> 'b))) +//│ 'a <: 'b -> 'c +//│ ChurchInt <: 'a -> 'e -> 'b)) //│ = [Function: to_church_mix] :e to_church_ty = to_church_mix -//│ int -> (forall 'a. 'a -> (forall 'b 'c 'a 'd 'e. (('c & 'b) -> ('b | 'd) +//│ int -> (forall 'a. 'a -> (forall 'b 'a 'c 'd 'e. ('d & 'e) -> ('e | 'b) //│ where -//│ ChurchInt <: 'a -> 'c -> 'e -//│ 'a <: 'e -> 'd))) +//│ 'a <: 'c -> 'b +//│ ChurchInt <: 'a -> 'd -> 'c)) //│ <: to_church_ty: //│ int -> ChurchInt //│ ╔══[ERROR] Type error in def definition @@ -1011,15 +1011,15 @@ rec def to_chD n = succD (to_chD n) //│ ╔══[ERROR] Inferred recursive type: 'to_chD //│ where -//│ 'to_chD :> forall 'a 'M. ('a -> ('M -> 'M) -> 'M -> 'M -//│ where -//│ 'to_chD <: 'a -> ChurchInt) +//│ 'to_chD :> forall 'a 'M. 'a -> ('M -> 'M) -> 'M -> 'M +//│ where +//│ 'to_chD <: 'a -> ChurchInt //│ ╙── //│ to_chD: 'to_chD //│ where -//│ 'to_chD :> forall 'a 'M. ('a -> ('M -> 'M) -> 'M -> 'M -//│ where -//│ 'to_chD <: 'a -> ChurchInt) +//│ 'to_chD :> forall 'a 'M. 'a -> ('M -> 'M) -> 'M -> 'M +//│ where +//│ 'to_chD <: 'a -> ChurchInt //│ = [Function: to_chD] @@ -1037,8 +1037,8 @@ rec def to_chD n = succ = s //│ 'a -> 'b -> ('c -> 'd //│ where -//│ 'a <: 'b -> 'c -> 'e -//│ 'b <: 'e -> 'd) +//│ 'b <: 'e -> 'd +//│ 'a <: 'b -> 'c -> 'e) //│ <: succ: //│ ChurchInt -> ChurchInt //│ = [Function: s] @@ -1060,23 +1060,23 @@ rec def to_chD n = to_church_ty = to_ch //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a :> forall 'b 'c 'd 'e 'f. (? & 'd) -> (('e & 'c) -> ('e | 'f) -//│ where -//│ 'd <: 'b -> 'f -//│ 'a <: 'd -> 'c -> 'b) +//│ 'a :> forall 'b 'c 'd 'e 'f. (? & 'c) -> (('d & 'b) -> ('d | 'e) +//│ where +//│ 'a <: 'c -> 'b -> 'f +//│ 'c <: 'f -> 'e) //│ ╙── //│ int -> 'a -> ('b -> ('b | 'c) //│ where //│ 'd <: 'a -> 'b -> 'e //│ 'a <: 'e -> 'c) -//│ where -//│ 'd :> forall 'f 'g 'h 'i 'j. 'g -> (('h & 'f) -> ('h | 'i) -//│ where -//│ 'g <: 'j -> 'i -//│ 'd <: 'g -> 'f -> 'j) +//│ where +//│ 'd :> forall 'f 'g 'h 'i 'j. 'f -> (('i & 'j) -> ('i | 'g) +//│ where +//│ 'f <: 'h -> 'g +//│ 'd <: 'f -> 'j -> 'h) //│ <: to_church_ty: //│ int -> ChurchInt -//│ ╔══[ERROR] Subtyping constraint of the form `forall ?to_ch ?a ?b ?c ?d. ?to_ch <: int -> ChurchInt` exceeded recursion depth limit (250) +//│ ╔══[ERROR] Subtyping constraint of the form `forall ?to_ch. ?to_ch <: int -> ChurchInt` exceeded recursion depth limit (250) //│ ║ l.1060: to_church_ty = to_ch //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. @@ -1087,22 +1087,22 @@ to_church_ty = to_ch_simplif //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b 'c 'd 'e. 'b -> ('c -> 'e -//│ where -//│ 'b <: 'd -> 'e -//│ 'a <: 'b -> 'c -> 'd) +//│ where +//│ 'b <: 'd -> 'e +//│ 'a <: 'b -> 'c -> 'd) //│ ╙── //│ anything -> 'a -> ('b -> 'c //│ where //│ 'a <: 'd -> 'c //│ 'e <: 'a -> 'b -> 'd) -//│ where -//│ 'e :> forall 'f 'g 'h 'i. 'i -> ('f -> 'h -//│ where -//│ 'i <: 'g -> 'h -//│ 'e <: 'i -> 'f -> 'g) +//│ where +//│ 'e :> forall 'f 'g 'h 'i. 'f -> ('g -> 'i +//│ where +//│ 'f <: 'h -> 'i +//│ 'e <: 'f -> 'g -> 'h) //│ <: to_church_ty: //│ int -> ChurchInt -//│ ╔══[ERROR] Subtyping constraint of the form `forall ?a ?b ?c ?to_ch_simplif ?d. ?to_ch_simplif <: int -> ChurchInt` exceeded recursion depth limit (250) +//│ ╔══[ERROR] Cyclic-looking constraint while typing def definition; a type annotation may be required //│ ║ l.1086: to_church_ty = to_ch_simplif //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. @@ -1113,20 +1113,20 @@ to_church_ty = to_ch_simplif rec def to_ch_simplif n = s (to_ch_simplif n) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a :> forall 'b 'c 'd 'e. 'b -> ('c -> 'e -//│ where -//│ 'a <: 'b -> 'c -> 'd -//│ 'b <: 'd -> 'e) +//│ 'a :> forall 'b 'c 'd 'e. 'c -> ('d -> 'b +//│ where +//│ 'a <: 'c -> 'd -> 'e +//│ 'c <: 'e -> 'b) //│ ╙── //│ to_ch_simplif: anything -> 'a -> ('b -> 'c //│ where -//│ 'd <: 'a -> 'b -> 'e -//│ 'a <: 'e -> 'c) -//│ where -//│ 'd :> forall 'f 'g 'h 'i. 'f -> ('g -> 'i -//│ where -//│ 'd <: 'f -> 'g -> 'h -//│ 'f <: 'h -> 'i) +//│ 'a <: 'd -> 'c +//│ 'e <: 'a -> 'b -> 'd) +//│ where +//│ 'e :> forall 'f 'g 'h 'i. 'f -> ('g -> 'i +//│ where +//│ 'f <: 'h -> 'i +//│ 'e <: 'f -> 'g -> 'h) //│ = [Function: to_ch_simplif1] :e // * Since the removal of "recursive definition hacks" @@ -1134,22 +1134,22 @@ to_church_ty = to_ch_simplif //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b 'c 'd 'e. 'b -> ('c -> 'e -//│ where -//│ 'a <: 'b -> 'c -> 'd -//│ 'b <: 'd -> 'e) +//│ where +//│ 'a <: 'b -> 'c -> 'd +//│ 'b <: 'd -> 'e) //│ ╙── //│ anything -> 'a -> ('b -> 'c //│ where -//│ 'd <: 'a -> 'b -> 'e -//│ 'a <: 'e -> 'c) -//│ where -//│ 'd :> forall 'f 'g 'h 'i. 'f -> ('g -> 'i -//│ where -//│ 'd <: 'f -> 'g -> 'h -//│ 'f <: 'h -> 'i) +//│ 'a <: 'd -> 'c +//│ 'e <: 'a -> 'b -> 'd) +//│ where +//│ 'e :> forall 'f 'g 'h 'i. 'f -> ('g -> 'i +//│ where +//│ 'f <: 'h -> 'i +//│ 'e <: 'f -> 'g -> 'h) //│ <: to_church_ty: //│ int -> ChurchInt -//│ ╔══[ERROR] Subtyping constraint of the form `forall ?a ?to_ch_simplif ?b ?c ?d. ?to_ch_simplif <: int -> ChurchInt` exceeded recursion depth limit (250) +//│ ╔══[ERROR] Cyclic-looking constraint while typing def definition; a type annotation may be required //│ ║ l.1133: to_church_ty = to_ch_simplif //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. @@ -1160,21 +1160,21 @@ to_church_ty = to_ch_simplif to_church_ty = to_ch_A1 //│ ╔══[ERROR] Inferred recursive type: 'to_ch_A1 //│ where -//│ 'to_ch_A1 :> forall 'to_ch_A1. (int -> (forall 'a 'b 'c 'd 'e. (? & 'c) -> (('e & 'd) -> ('d | 'b) -//│ where -//│ 'c <: 'a -> 'b -//│ ChurchInt <: 'c -> 'e -> 'a)) -//│ where -//│ 'to_ch_A1 <: int -> ChurchInt) +//│ 'to_ch_A1 :> forall 'to_ch_A1. int -> (forall 'a 'b 'c 'd 'e. (? & 'd) -> (('e & 'c) -> ('c | 'b) +//│ where +//│ 'd <: 'a -> 'b +//│ ChurchInt <: 'd -> 'e -> 'a)) +//│ where +//│ 'to_ch_A1 <: int -> ChurchInt //│ ╙── //│ 'to_ch_A1 //│ where -//│ 'to_ch_A1 :> forall 'to_ch_A1. (int -> (forall 'a 'b 'c 'd 'e. 'b -> (('d & 'a) -> ('a | 'e) -//│ where -//│ ChurchInt <: 'b -> 'd -> 'c -//│ 'b <: 'c -> 'e)) -//│ where -//│ 'to_ch_A1 <: int -> ChurchInt) +//│ 'to_ch_A1 :> forall 'to_ch_A1. int -> (forall 'a 'b 'c 'd 'e. 'd -> (('e & 'b) -> ('b | 'c) +//│ where +//│ ChurchInt <: 'd -> 'e -> 'a +//│ 'd <: 'a -> 'c)) +//│ where +//│ 'to_ch_A1 <: int -> ChurchInt //│ <: to_church_ty: //│ int -> ChurchInt //│ ╔══[ERROR] Cyclic-looking constraint while typing def definition; a type annotation may be required @@ -1187,8 +1187,8 @@ to_church_ty = to_ch_A1 to_church_ty = to_church_mix //│ int -> 'a -> ('b -> ('b | 'c) //│ where -//│ 'a <: 'd -> 'c -//│ ChurchInt <: 'a -> 'b -> 'd) +//│ ChurchInt <: 'a -> 'b -> 'd +//│ 'a <: 'd -> 'c) //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_church_mix] diff --git a/shared/src/test/diff/fcp/Church_ST.mls b/shared/src/test/diff/fcp/Church_ST.mls index 51b270f339..a9b6a08651 100644 --- a/shared/src/test/diff/fcp/Church_ST.mls +++ b/shared/src/test/diff/fcp/Church_ST.mls @@ -353,7 +353,7 @@ succ = s //│ = [Function: s] succ = s_A1 -//│ ChurchInt -> (forall 'a 'N 'N0. ('N -> ('N & 'N0) & 'N0 -> 'a) -> ('N & 'N0) -> 'a) +//│ ChurchInt -> (forall 'N 'a 'N0. ('N0 -> ('N0 & 'N) & 'N -> 'a) -> ('N0 & 'N) -> 'a) //│ <: succ: //│ (forall 'N. ('N -> 'N) -> 'N -> 'N) -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ = [Function: s_A1] @@ -393,9 +393,9 @@ s: ChurchInt -> ChurchInt //│ ╙── ^ //│ res: ChurchInt -> ChurchInt //│ = [Function: s] -//│ constrain calls : 110 +//│ constrain calls : 109 //│ annoying calls : 0 -//│ subtyping calls : 383 +//│ subtyping calls : 382 @@ -535,16 +535,16 @@ sz = s zero :ns sz -//│ res: forall 'a 'b 'N 'c 'd. 'b +//│ res: forall 'a 'b 'c 'N 'd. 'b //│ where //│ 'b :> forall 'e 'f 'g. 'e -> (forall 'h 'i. 'h -> 'i) //│ 'i :> 'g -//│ 'h <: 'c -//│ 'c <: 'N +//│ 'h <: 'a +//│ 'a <: 'N //│ 'e <: 'f -> 'g & 'd //│ 'd <: 'N -> 'N -//│ 'N <: 'a -//│ 'f :> 'a +//│ 'N <: 'c +//│ 'f :> 'c //│ = [Function (anonymous)] sz: ChurchInt @@ -589,7 +589,7 @@ sz1 = sz 1 //│ ╟── from reference: //│ ║ l.314: def s n f x = f (n f x) //│ ╙── ^ -//│ sz1: anything -> nothing | error +//│ sz1: error | anything -> nothing //│ = [Function (anonymous)] :re diff --git a/shared/src/test/diff/fcp/ConstrainedTypes2.mls b/shared/src/test/diff/fcp/ConstrainedTypes2.mls index ebcdfa5ac4..acdf742cef 100644 --- a/shared/src/test/diff/fcp/ConstrainedTypes2.mls +++ b/shared/src/test/diff/fcp/ConstrainedTypes2.mls @@ -95,7 +95,7 @@ test 1 //│ ╟── Note: constraint arises from application: //│ ║ l.78: in let _ = extr f //│ ╙── ^^^^^^ -//│ res: anything -> 'a | error +//│ res: error | anything -> 'a //│ where //│ 0 <: 0 -> 'a //│ Runtime error: @@ -108,11 +108,11 @@ def test extr x = in f 0 //│ test: ((forall 'a 'b 'c. 'a -> (anything -> 'c //│ where -//│ 'a <: 0 -> 'b -//│ 'd <: 'b -> 'c)) -> anything) -> 'd -> (anything -> 'e +//│ 'd <: 'b -> 'c +//│ 'a <: 0 -> 'b)) -> anything) -> 'd -> (anything -> 'e //│ where -//│ 'd <: 'f -> 'e -//│ 0 <: 0 -> 'f) +//│ 0 <: 0 -> 'f +//│ 'd <: 'f -> 'e) //│ = [Function: test1] :e @@ -126,10 +126,10 @@ test 0 //│ ╟── Note: constraint arises from application: //│ ║ l.107: in let _ = extr f //│ ╙── ^^^^^^ -//│ res: 'a -> (anything -> 'b +//│ res: error | 'a -> (anything -> 'b //│ where //│ 'a <: 'c -> 'b -//│ 0 <: 0 -> 'c) | error +//│ 0 <: 0 -> 'c) //│ = [Function (anonymous)] def test extr x = @@ -137,8 +137,8 @@ def test extr x = in f 0 //│ test: anything -> 'a -> (anything -> 'b //│ where -//│ 0 <: 0 -> 'c -//│ 'a <: 'c -> 'b) +//│ 'a <: 'c -> 'b +//│ 0 <: 0 -> 'c) //│ = [Function: test2] // Error delayed by inconsistent constrained types diff --git a/shared/src/test/diff/fcp/Demo.mls b/shared/src/test/diff/fcp/Demo.mls index 3c35eb6bb3..6f0c020d3a 100644 --- a/shared/src/test/diff/fcp/Demo.mls +++ b/shared/src/test/diff/fcp/Demo.mls @@ -127,7 +127,7 @@ found = find(ls, 1) match found with | None -> ("???", false) | Some(r) -> (r id "!!!", r id true) -//│ res: ("!!!" | "???", bool,) +//│ res: ("!!!" | "???", Bool,) use fnd = match fnd with @@ -136,7 +136,7 @@ use fnd = match fnd with //│ use: Option[(forall 'a. 'a -> 'a) -> ("!!!" -> 'b & true -> 'c)] -> ("???" | 'b, false | 'c,) use found -//│ res: ("!!!" | "???", bool,) +//│ res: ("!!!" | "???", Bool,) diff --git a/shared/src/test/diff/fcp/DistribWorsening.mls b/shared/src/test/diff/fcp/DistribWorsening.mls index af9cbdaf71..1554f367e4 100644 --- a/shared/src/test/diff/fcp/DistribWorsening.mls +++ b/shared/src/test/diff/fcp/DistribWorsening.mls @@ -66,7 +66,7 @@ test (fun () -> fun n -> id) //│ ╟── • this reference: //│ ║ l.48: a : anything -> BTB //│ ╙── ^ -//│ res: anything -> BTB | error +//│ res: error | anything -> BTB // * Idem @@ -93,7 +93,7 @@ test (fun g -> fun n -> id) //│ ╟── • this application: //│ ║ l.75: a() : nothing -> BTB //│ ╙── ^^^ -//│ res: nothing -> BTB | error +//│ res: error | nothing -> BTB diff --git a/shared/src/test/diff/fcp/FCPTony.mls b/shared/src/test/diff/fcp/FCPTony.mls index bfc91cc4c4..0c50732ff2 100644 --- a/shared/src/test/diff/fcp/FCPTony.mls +++ b/shared/src/test/diff/fcp/FCPTony.mls @@ -44,9 +44,9 @@ def app p q = p q def mul r s = r (app s) id //│ mul: 'a -> ('b -> 'c //│ where -//│ 'a <: (forall 'd 'e. ('d -> 'e -//│ where -//│ 'b <: 'd -> 'e)) -> (forall 'f. 'f -> 'f) -> 'c) +//│ 'a <: (forall 'd 'e. 'd -> 'e +//│ where +//│ 'b <: 'd -> 'e) -> (forall 'f. 'f -> 'f) -> 'c) //│ = [Function: mul] :e @@ -68,9 +68,9 @@ mul id {} //│ TypeError: p is not a function def fact t = mul t {} -//│ fact: ((forall 'a 'b. ('a -> 'b +//│ fact: ((forall 'a 'b. 'a -> 'b //│ where -//│ anything <: 'a -> 'b)) -> (forall 'c. 'c -> 'c) -> 'd) -> 'd +//│ anything <: 'a -> 'b) -> (forall 'c. 'c -> 'c) -> 'd) -> 'd //│ = [Function: fact] :e @@ -91,9 +91,9 @@ fact id def mul r s = r (app s) //│ mul: 'a -> ('b -> 'c //│ where -//│ 'a <: (forall 'd 'e. ('d -> 'e -//│ where -//│ 'b <: 'd -> 'e)) -> 'c) +//│ 'a <: (forall 'd 'e. 'd -> 'e +//│ where +//│ 'b <: 'd -> 'e) -> 'c) //│ = [Function: mul1] mul id id {} @@ -118,20 +118,20 @@ def succ n f x = f (n f x) //│ = //│ 'a -> 'b -> ('c -> 'd //│ where -//│ 'b <: (forall 'e. ('e -//│ where -//│ 'a <: 'b -> 'c -> 'e)) -> 'd) +//│ 'b <: (forall 'e. 'e +//│ where +//│ 'a <: 'b -> 'c -> 'e) -> 'd) //│ <: succ: //│ ChurchInt -> ChurchInt //│ = [Function: succ1] :ns def add n m = n succ m -//│ add: forall 'a. 'a -> (forall 'b 'c 'd. ('b -> 'd +//│ add: forall 'a. 'a -> (forall 'b 'c 'd. 'b -> 'd //│ where -//│ 'a <: (ChurchInt -> ChurchInt) -> 'c)) -//│ where -//│ 'c <: 'b -> 'd +//│ 'a <: (ChurchInt -> ChurchInt) -> 'c) +//│ where +//│ 'c <: 'b -> 'd //│ = [Function: add] add @@ -142,13 +142,13 @@ add :ns def f x y = add x y -//│ f: forall 'a. 'a -> (forall 'b 'c 'd 'e 'f. ('f -> 'd -//│ where -//│ 'a <: (ChurchInt -> ChurchInt) -> 'e)) +//│ f: forall 'a. 'a -> (forall 'b 'c 'd 'e 'f. 'e -> 'f //│ where -//│ 'f <: 'b -//│ 'e <: 'b -> 'c -//│ 'c <: 'd +//│ 'a <: (ChurchInt -> ChurchInt) -> 'b) +//│ where +//│ 'e <: 'c +//│ 'b <: 'c -> 'd +//│ 'd <: 'f //│ = [Function: f] // :ds diff --git a/shared/src/test/diff/fcp/ForallTerms.mls b/shared/src/test/diff/fcp/ForallTerms.mls index 9547d7b116..9c448c408a 100644 --- a/shared/src/test/diff/fcp/ForallTerms.mls +++ b/shared/src/test/diff/fcp/ForallTerms.mls @@ -64,7 +64,7 @@ k2 r.f :ns f = fun x -> fun y -> (x, y) g = fun x -> forall. fun y -> (x, y) -//│ f: forall 'a 'b. 'b -> 'a -> ('b, 'a,) +//│ f: forall 'a 'b. 'a -> 'b -> ('a, 'b,) //│ = [Function: f1] //│ g: forall 'a. 'a -> (forall 'b. 'b -> ('a, 'b,)) //│ = [Function: g] diff --git a/shared/src/test/diff/fcp/FunnyId.mls b/shared/src/test/diff/fcp/FunnyId.mls index 0754f9dee4..9558b97672 100644 --- a/shared/src/test/diff/fcp/FunnyId.mls +++ b/shared/src/test/diff/fcp/FunnyId.mls @@ -35,10 +35,9 @@ rec def id x = let tmp = id id x in x //│ <: ? & 'a //│ ║ l.30: rec def id x = let tmp = id id x in x //│ ╙── ^^^^^ -//│ id: 'id +//│ id: 'a -> 'a //│ where -//│ 'id :> 'a -> 'a -//│ 'a :> 'id +//│ 'a :> 'a -> 'a //│ <: 'a -> anything //│ = [Function: id1] @@ -48,15 +47,15 @@ id 1 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: 'b -> ? -//│ 'b :> 'b -> 'b | 1 +//│ 'b :> 1 | 'b -> 'b //│ <: ? & 'a //│ ║ l.30: rec def id x = let tmp = id id x in x //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.47: id 1 +//│ ║ l.46: id 1 //│ ║ ^^^^ //│ ╟── integer literal of type `1` is not a function -//│ ║ l.47: id 1 +//│ ║ l.46: id 1 //│ ║ ^ //│ ╟── Note: constraint arises from application: //│ ║ l.30: rec def id x = let tmp = id id x in x @@ -64,9 +63,9 @@ id 1 //│ ╟── from reference: //│ ║ l.30: rec def id x = let tmp = id id x in x //│ ╙── ^ -//│ res: 'a -> 'a | 1 | error | 'b +//│ res: 1 | error | 'a -> 'a | 'b //│ where -//│ 'a :> 'a -> 'a | 1 +//│ 'a :> 1 | 'a -> 'a //│ <: 'a -> anything & 'b //│ Runtime error: //│ RangeError: Maximum call stack size exceeded @@ -80,15 +79,14 @@ id_ty = id //│ <: ? & 'a //│ ║ l.30: rec def id x = let tmp = id id x in x //│ ╙── ^^^^^ -//│ 'id +//│ 'a -> 'a //│ where -//│ 'id :> 'a -> 'a -//│ 'a :> 'id +//│ 'a :> 'a -> 'a //│ <: 'a -> anything //│ <: id_ty: //│ 'a -> 'a //│ ╔══[ERROR] Type mismatch in def definition: -//│ ║ l.75: id_ty = id +//│ ║ l.74: id_ty = id //│ ║ ^^^^^^^^^^ //│ ╟── type `'a` is not a function //│ ║ l.4: def id_ty: forall 'a. 'a -> 'a @@ -133,19 +131,17 @@ rec def id x = if true then x else id id x //│ <: 'c //│ 'c :> 'b -> 'c //│ <: 'a -//│ ║ l.128: rec def id x = if true then x else id id x +//│ ║ l.126: rec def id x = if true then x else id id x //│ ╙── ^^^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing binding of lambda expression; a type annotation may be required -//│ ║ l.128: rec def id x = if true then x else id id x +//│ ║ l.126: rec def id x = if true then x else id id x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. -//│ id: 'id +//│ id: 'a -> 'b //│ where -//│ 'id :> 'a -> 'b -//│ 'a :> 'id +//│ 'a :> 'a -> 'b //│ <: 'b -//│ 'b :> 'id -//│ <: 'a -> 'b +//│ 'b := 'a -> 'b //│ = [Function: id3] :e @@ -157,28 +153,26 @@ id_ty = id //│ 'c :> 'a | 'c -> 'b //│ <: 'a //│ 'a <: 'b -//│ ║ l.128: rec def id x = if true then x else id id x +//│ ║ l.126: rec def id x = if true then x else id id x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ -//│ 'id +//│ 'a -> 'b //│ where -//│ 'id :> 'a -> 'b -//│ 'a :> 'id +//│ 'a :> 'a -> 'b //│ <: 'b -//│ 'b :> 'id -//│ <: 'a -> 'b +//│ 'b := 'a -> 'b //│ <: id_ty: //│ 'a -> 'a //│ ╔══[ERROR] Type mismatch in def definition: -//│ ║ l.152: id_ty = id +//│ ║ l.148: id_ty = id //│ ║ ^^^^^^^^^^ //│ ╟── type `'a` is not a function //│ ║ l.4: def id_ty: forall 'a. 'a -> 'a //│ ║ ^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.128: rec def id x = if true then x else id id x +//│ ║ l.126: rec def id x = if true then x else id id x //│ ║ ^^^^^^^ //│ ╟── from reference: -//│ ║ l.128: rec def id x = if true then x else id id x +//│ ║ l.126: rec def id x = if true then x else id id x //│ ║ ^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.4: def id_ty: forall 'a. 'a -> 'a @@ -195,47 +189,35 @@ def choose: 'a -> 'a -> 'a //│ choose: 'a -> 'a -> 'a rec def id1 x = choose x (id1 id1 x) -//│ id1: 'id1 +//│ id1: 'a -> 'b //│ where -//│ 'id1 :> 'a -> 'b -//│ 'a :> 'id1 +//│ 'a :> 'a -> 'b //│ <: 'b -//│ 'b :> 'id1 -//│ <: 'a -> 'b +//│ 'b := 'a -> 'b id1 id -//│ res: 'a -> 'b | 'id +//│ res: ('a & 'b) -> 'a //│ where -//│ 'a :> forall 'id 'c 'd. 'a -> 'b | 'id -//│ <: 'b -//│ 'b :> forall 'id 'c 'd. 'id -//│ <: 'a -> 'b -//│ 'id :> 'c -> 'd -//│ 'c :> 'id -//│ <: 'd -//│ 'd :> 'id -//│ <: 'c -> 'd +//│ 'a :> forall 'c 'd. ('a & 'b & 'd) -> 'a +//│ <: ((forall 'c 'd. 'd -> 'c) | 'b) -> 'a +//│ 'd :> 'd -> 'c +//│ <: 'c +//│ 'c := 'd -> 'c rec def id1 x = if true then x else id1 id1 x -//│ id1: 'id1 +//│ id1: 'a -> 'b //│ where -//│ 'id1 :> 'a -> 'b -//│ 'a :> 'id1 +//│ 'a :> 'a -> 'b //│ <: 'b -//│ 'b :> 'id1 -//│ <: 'a -> 'b +//│ 'b := 'a -> 'b id1 id -//│ res: 'a -> 'b | 'id +//│ res: ('a & 'b) -> 'a //│ where -//│ 'a :> forall 'id 'c 'd. 'a -> 'b | 'id -//│ <: 'b -//│ 'b :> forall 'id 'c 'd. 'id -//│ <: 'a -> 'b -//│ 'id :> 'c -> 'd -//│ 'c :> 'id +//│ 'a :> forall 'c 'd. ('a & 'b & 'c) -> 'a +//│ <: ((forall 'c 'd. 'c -> 'd) | 'b) -> 'a +//│ 'c :> 'c -> 'd //│ <: 'd -//│ 'd :> 'id -//│ <: 'c -> 'd +//│ 'd := 'c -> 'd diff --git a/shared/src/test/diff/fcp/ListBuild.mls b/shared/src/test/diff/fcp/ListBuild.mls index 388f0da856..1dfe4f4548 100644 --- a/shared/src/test/diff/fcp/ListBuild.mls +++ b/shared/src/test/diff/fcp/ListBuild.mls @@ -4,7 +4,7 @@ class Ls[A] method HeadTail: (A, Ls[A]) | undefined //│ Defined class Ls[+A] -//│ Declared Ls.HeadTail: Ls['A] -> (('A, Ls['A],) | undefined) +//│ Declared Ls.HeadTail: Ls['A] -> (undefined | ('A, Ls['A],)) // * Note that a more structural type such as this will raise cycle errors due to `:NoRecursiveTypes` // class Ls[A]: { head: A | undefined; tail: Ls[A] | undefined } @@ -70,15 +70,16 @@ def build0 (g: forall 'b. ('a -> 'a -> 'b) -> 'b) = g (fun x -> fun y -> single( //│ = //│ single is not implemented -:e // * This is recursive because we place the list-typed value inside a new list along with the head +:e // * This is recursive because we place the list-typed value inside a new list along with the head. def build0 (g: forall 'b. ('a -> 'b -> 'b) -> 'b) = g (fun x -> fun y -> single((x, y))) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a :> Ls[(?, 'a,)] -//│ ╙── +//│ 'a :> Ls[(?, forall 'a. 'a,)] +//│ ║ l.74: def build0 (g: forall 'b. ('a -> 'b -> 'b) -> 'b) = g (fun x -> fun y -> single((x, y))) +//│ ╙── ^^^^^^^^^^^^^^ //│ build0: (forall 'b. ('a -> 'b -> 'b) -> 'b) -> 'c //│ where -//│ 'c :> Ls[('a, 'c,)] +//│ 'c :> Ls[('a, forall 'c. 'c,)] //│ = //│ single is not implemented @@ -132,7 +133,7 @@ def build0 (g: forall 'b. ('a -> 'b -> 'b) -> 'b) = g (fun x -> fun xs -> cons ( build0 (fun k -> k 1 error) //│ res: Ls[1] //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown @@ -184,15 +185,15 @@ build: forall 'a. (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> Ls['a] :e build: (('a -> Ls['a] -> Ls['a]) -> Ls['a] -> Ls['a]) -> Ls['a] //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.185: build: (('a -> Ls['a] -> Ls['a]) -> Ls['a] -> Ls['a]) -> Ls['a] +//│ ║ l.186: build: (('a -> Ls['a] -> Ls['a]) -> Ls['a] -> Ls['a]) -> Ls['a] //│ ║ ^^^^^ //│ ╟── type `Ls['a]` does not match type `'b` -//│ ║ l.185: build: (('a -> Ls['a] -> Ls['a]) -> Ls['a] -> Ls['a]) -> Ls['a] +//│ ║ l.186: build: (('a -> Ls['a] -> Ls['a]) -> Ls['a] -> Ls['a]) -> Ls['a] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type variable: -//│ ║ l.142: def build = fun (g: forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> g (fun x -> fun xs -> cons (x, xs)) nil +//│ ║ l.143: def build = fun (g: forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> g (fun x -> fun xs -> cons (x, xs)) nil //│ ╙── ^^ -//│ res: (('a -> Ls['a] -> Ls['a | error]) -> Ls['a | error] -> Ls['a]) -> Ls['a | error] +//│ res: (('a -> Ls['a] -> Ls['a]) -> Ls['a] -> Ls['a]) -> Ls['a] //│ = [Function: build] @@ -264,16 +265,16 @@ b g x = build_ g x //│ <: b: //│ (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> Ls['a] //│ ╔══[ERROR] Type mismatch in def definition: -//│ ║ l.262: b g x = build_ g x +//│ ║ l.263: b g x = build_ g x //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── type `Ls['a]` is not a function //│ ║ l.22: def nil: Ls['a] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.262: b g x = build_ g x +//│ ║ l.263: b g x = build_ g x //│ ║ ^^^^^^^^^^ //│ ╟── from application: -//│ ║ l.203: def build_ = fun g -> g (fun x -> fun xs -> cons (x, xs)) nil +//│ ║ l.204: def build_ = fun g -> g (fun x -> fun xs -> cons (x, xs)) nil //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ = [Function: b3] diff --git a/shared/src/test/diff/fcp/MoreChurch.mls b/shared/src/test/diff/fcp/MoreChurch.mls index e58e8fe375..d7543c4de4 100644 --- a/shared/src/test/diff/fcp/MoreChurch.mls +++ b/shared/src/test/diff/fcp/MoreChurch.mls @@ -90,12 +90,12 @@ def mul_ty: ChurchInt -> ChurchInt -> ChurchInt //│ = def mul n m = n (add m) zero -//│ mul: ((forall 'a 'b 'c 'd 'e. ('c -> 'e -> 'a & 'd) -> (('a -> ('a & 'b) & 'c) -> 'e -> 'b | 'd)) -> (forall 'f. anything -> 'f -> 'f) -> 'g) -> ChurchInt -> 'g +//│ mul: ((forall 'a 'b 'c 'd 'e. ('e -> 'a -> 'd & 'b) -> (('d -> ('d & 'c) & 'e) -> 'a -> 'c | 'b)) -> (forall 'f. anything -> 'f -> 'f) -> 'g) -> ChurchInt -> 'g //│ = [Function: mul] // * Note: cycle check fails when generalizing curried lambdas mul_ty = mul -//│ ((forall 'a 'b 'c 'd 'e. ('a -> 'e -> 'd & 'c) -> (('d -> ('d & 'b) & 'a) -> 'e -> 'b | 'c)) -> (forall 'f. anything -> 'f -> 'f) -> 'g) -> ChurchInt -> 'g +//│ ((forall 'a 'b 'c 'd 'e. ('e -> 'd -> 'b & 'a) -> (('b -> ('b & 'c) & 'e) -> 'd -> 'c | 'a)) -> (forall 'f. anything -> 'f -> 'f) -> 'g) -> ChurchInt -> 'g //│ <: mul_ty: //│ ChurchInt -> ChurchInt -> ChurchInt //│ = [Function: mul] @@ -130,7 +130,7 @@ def pow (n: ChurchInt) (m: ChurchInt) = n (mul m) (succ zero) //│ ╟── Note: constraint arises from reference: //│ ║ l.31: def succ n f x = f (n f x) //│ ╙── ^ -//│ pow: ChurchInt -> ChurchInt -> (((??a | 'a) -> (??a0 & 'b) & 'c -> ('c & 'b)) -> (??a0 & 'a & 'c) -> (??a | 'a | 'b) | error) +//│ pow: ChurchInt -> ChurchInt -> (error | ((??a | 'a) -> (??a0 & 'b) & 'c -> ('c & 'b)) -> (??a0 & 'a & 'c) -> (??a | 'a | 'b)) //│ = [Function: pow] def pow (n: ChurchInt) (m: ChurchInt) = n (mul_ty m) (succ_ty zero_ty) @@ -230,11 +230,11 @@ def pred n = let s p = pair (snd p) (succ (snd p)) in let z = pair zero zero in fst (n s z) -//│ pred: ((forall 'a 'b 'c 'd 'e 'f. ((forall 'g. anything -> 'g -> 'g) -> ('f -> 'd -> 'b & 'a)) -> ('a -> (('b -> 'e & 'f) -> 'd -> 'e) -> 'c) -> 'c) -> (forall 'h. ((forall 'i. anything -> 'i -> 'i) -> (forall 'i. anything -> 'i -> 'i) -> 'h) -> 'h) -> (forall 'j. 'j -> anything -> 'j) -> 'k) -> 'k +//│ pred: ((forall 'a 'b 'c 'd 'e 'f. ((forall 'g. anything -> 'g -> 'g) -> ('e -> 'b -> 'a & 'f)) -> ('f -> (('a -> 'd & 'e) -> 'b -> 'd) -> 'c) -> 'c) -> (forall 'h. ((forall 'i. anything -> 'i -> 'i) -> (forall 'i. anything -> 'i -> 'i) -> 'h) -> 'h) -> (forall 'j. 'j -> anything -> 'j) -> 'k) -> 'k //│ = [Function: pred1] // * Note: cycle check fails when generalizing curried lambdas pred_ty = pred -//│ ((forall 'a 'b 'c 'd 'e 'f. ((forall 'g. anything -> 'g -> 'g) -> ('d -> 'b -> 'a & 'f)) -> ('f -> (('a -> 'e & 'd) -> 'b -> 'e) -> 'c) -> 'c) -> (forall 'h. ((forall 'i. anything -> 'i -> 'i) -> (forall 'i. anything -> 'i -> 'i) -> 'h) -> 'h) -> (forall 'j. 'j -> anything -> 'j) -> 'k) -> 'k +//│ ((forall 'a 'b 'c 'd 'e 'f. ((forall 'g. anything -> 'g -> 'g) -> ('c -> 'a -> 'f & 'e)) -> ('e -> (('f -> 'd & 'c) -> 'a -> 'd) -> 'b) -> 'b) -> (forall 'h. ((forall 'i. anything -> 'i -> 'i) -> (forall 'i. anything -> 'i -> 'i) -> 'h) -> 'h) -> (forall 'j. 'j -> anything -> 'j) -> 'k) -> 'k //│ <: pred_ty: //│ ChurchInt -> ChurchInt //│ = [Function: pred1] @@ -247,7 +247,7 @@ def pred n = //│ pred: ((forall 'a 'b. ((forall 'c. anything -> 'c -> 'c) -> (ChurchInt & 'a)) -> ('a -> ChurchInt -> 'b) -> 'b) -> (forall 'd. ((forall 'e. anything -> 'e -> 'e) -> (forall 'e. anything -> 'e -> 'e) -> 'd) -> 'd) -> (forall 'f. 'f -> anything -> 'f) -> 'g) -> 'g //│ = [Function: pred2] pred_ty = pred -//│ ((forall 'a 'b. ((forall 'c. anything -> 'c -> 'c) -> (ChurchInt & 'b)) -> ('b -> ChurchInt -> 'a) -> 'a) -> (forall 'd. ((forall 'e. anything -> 'e -> 'e) -> (forall 'e. anything -> 'e -> 'e) -> 'd) -> 'd) -> (forall 'f. 'f -> anything -> 'f) -> 'g) -> 'g +//│ ((forall 'a 'b. ((forall 'c. anything -> 'c -> 'c) -> (ChurchInt & 'a)) -> ('a -> ChurchInt -> 'b) -> 'b) -> (forall 'd. ((forall 'e. anything -> 'e -> 'e) -> (forall 'e. anything -> 'e -> 'e) -> 'd) -> 'd) -> (forall 'f. 'f -> anything -> 'f) -> 'g) -> 'g //│ <: pred_ty: //│ ChurchInt -> ChurchInt //│ = [Function: pred2] @@ -289,16 +289,16 @@ def fact (n: ChurchInt) = (iszero n) (fun _ -> succ zero) (fun _ -> mul n {}) //│ ╟── from reference: //│ ║ l.103: def mul (n: ChurchInt) m = n (add m) zero //│ ╙── ^ -//│ fact: ChurchInt -> (('a -> ('a & 'b) & 'c -> 'b) -> ('b & 'a & 'c) -> 'b | error) +//│ fact: ChurchInt -> (error | ('a -> ('a & 'b) & 'c -> 'b) -> ('b & 'a & 'c) -> 'b) //│ = [Function: fact2] // * We can drop all intermediate annotations if the recursive call is annotated: def fact n = (iszero n) (fun _ -> succ zero) (fun _ -> mul n (fact_ty (pred n))) -//│ fact: (ChurchInt & (forall 'a. anything -> anything -> ((forall 'b. 'b -> 'b) -> 'a) -> 'a) -> (forall 'c. ((forall 'b. 'b -> 'b) -> 'c) -> anything -> 'c) -> (forall 'd 'e. anything -> ('d -> 'e) -> 'd -> 'e) -> (forall 'f 'g. anything -> ('f -> ('f & 'g)) -> ('g & 'f) -> 'g) -> 'h & (forall 'i 'j. ((forall 'k. anything -> 'k -> 'k) -> (ChurchInt & 'i)) -> ('i -> ChurchInt -> 'j) -> 'j) -> (forall 'l. ((forall 'm. anything -> 'm -> 'm) -> (forall 'm. anything -> 'm -> 'm) -> 'l) -> 'l) -> (forall 'n. 'n -> anything -> 'n) -> ChurchInt) -> 'h +//│ fact: (ChurchInt & (forall 'a. anything -> anything -> ((forall 'b. 'b -> 'b) -> 'a) -> 'a) -> (forall 'c. ((forall 'b. 'b -> 'b) -> 'c) -> anything -> 'c) -> (forall 'd 'e. anything -> ('d -> 'e) -> 'd -> 'e) -> (forall 'f 'g. anything -> ('f -> ('f & 'g)) -> ('g & 'f) -> 'g) -> 'h & (forall 'i 'j. ((forall 'k. anything -> 'k -> 'k) -> (ChurchInt & 'j)) -> ('j -> ChurchInt -> 'i) -> 'i) -> (forall 'l. ((forall 'm. anything -> 'm -> 'm) -> (forall 'm. anything -> 'm -> 'm) -> 'l) -> 'l) -> (forall 'n. 'n -> anything -> 'n) -> ChurchInt) -> 'h //│ = [Function: fact3] fact_ty = fact -//│ (ChurchInt & (forall 'a. anything -> anything -> ((forall 'b. 'b -> 'b) -> 'a) -> 'a) -> (forall 'c. ((forall 'b. 'b -> 'b) -> 'c) -> anything -> 'c) -> (forall 'd 'e. anything -> ('d -> 'e) -> 'd -> 'e) -> (forall 'f 'g. anything -> ('f -> ('f & 'g)) -> ('g & 'f) -> 'g) -> 'h & (forall 'i 'j. ((forall 'k. anything -> 'k -> 'k) -> (ChurchInt & 'i)) -> ('i -> ChurchInt -> 'j) -> 'j) -> (forall 'l. ((forall 'm. anything -> 'm -> 'm) -> (forall 'm. anything -> 'm -> 'm) -> 'l) -> 'l) -> (forall 'n. 'n -> anything -> 'n) -> ChurchInt) -> 'h +//│ (ChurchInt & (forall 'a. anything -> anything -> ((forall 'b. 'b -> 'b) -> 'a) -> 'a) -> (forall 'c. ((forall 'b. 'b -> 'b) -> 'c) -> anything -> 'c) -> (forall 'd 'e. anything -> ('d -> 'e) -> 'd -> 'e) -> (forall 'f 'g. anything -> ('f -> ('f & 'g)) -> ('g & 'f) -> 'g) -> 'h & (forall 'i 'j. ((forall 'k. anything -> 'k -> 'k) -> (ChurchInt & 'j)) -> ('j -> ChurchInt -> 'i) -> 'i) -> (forall 'l. ((forall 'm. anything -> 'm -> 'm) -> (forall 'm. anything -> 'm -> 'm) -> 'l) -> 'l) -> (forall 'n. 'n -> anything -> 'n) -> ChurchInt) -> 'h //│ <: fact_ty: //│ ChurchInt -> ChurchInt //│ = [Function: fact3] diff --git a/shared/src/test/diff/fcp/NestedDataTypes.mls b/shared/src/test/diff/fcp/NestedDataTypes.mls index eb1ad410cb..65fbaf1062 100644 --- a/shared/src/test/diff/fcp/NestedDataTypes.mls +++ b/shared/src/test/diff/fcp/NestedDataTypes.mls @@ -12,7 +12,7 @@ type Two[A] = (A, A) //│ Defined type alias Two[+A] def mapTwo f ((a, b)) = (f a, f b) -//│ mapTwo: 'a -> (('b, 'c,) -> ('d, 'e,) +//│ mapTwo: 'a -> ((('b, 'c,),) -> ('d, 'e,) //│ where //│ 'a <: 'b -> 'd & 'c -> 'e) //│ = [Function: mapTwo] @@ -220,10 +220,14 @@ rec def map f tree = case tree of { //│ ║ l.67: } //│ ║ ^^^ //│ ╙── Note: use flag `:ex` to see internal error info. -//│ map: ('value -> (Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two['A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two['A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'value0) & 'a -> (Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two['A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'b & 'A) & 'c -> (Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two['A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'd & 'A) & 'e -> (Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two['A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'f & 'A) & 'g -> (Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two['A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'h & 'A)) -> 'i -> 'subTree +//│ map: ('value -> (Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two['A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two['A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'value0) & 'a -> (Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two['A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'b & 'A) & 'c -> (Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two['A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'd & 'A) & 'e -> (Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two['A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'f & 'A) & 'g -> (Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two['A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'h & 'A)) -> 'i -> (Leaf[((nothing, (nothing, nothing,) | 'd,) | 'b, ((nothing, nothing,) | 'h, nothing,) | 'f,) | 'value0] | 'j) //│ where -//│ 'subTree :> Leaf[((nothing, (nothing, nothing,) | 'd,) | 'b, ((nothing, nothing,) | 'h, nothing,) | 'f,) | 'value0] | (Node['A] with {subTree: 'subTree}) -//│ 'i <: Leaf[?] & {value: ((anything, (anything, anything,) & 'c,) & 'a, ((anything, anything,) & 'g, anything,) & 'e,) & 'value} | (Node[?] with {subTree: 'i}) +//│ 'j :> Node['A] with { +//│ subTree: Leaf[((nothing, (nothing, nothing,) | 'd,) | 'b, ((nothing, nothing,) | 'h, nothing,) | 'f,) | 'value0] | 'j +//│ } +//│ 'i <: Leaf[?] & { +//│ value: ((anything, (anything, anything,) & 'c,) & 'a, ((anything, anything,) & 'g, anything,) & 'e,) & 'value +//│ } | (Node[?] with {subTree: 'i}) //│ 'A :> ((nothing, nothing,) | 'h, (nothing, nothing,) | 'd,) //│ <: Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two[Two['A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] & 'A] //│ = [Function: map] @@ -236,36 +240,36 @@ rec def map f tree = case tree of { } //│ ╔══[ERROR] Inferred recursive type: 'map //│ where -//│ 'map :> forall 'a 'subTree 'value 'value0 'subTree0 'A. 'a -> ((Leaf[?] & {value: 'value} | (Node[?] with {subTree: 'subTree0})) -> (Leaf['value0] | (Node['A] with {subTree: 'subTree})) -//│ where -//│ 'map <: (forall 'b 'c 'd 'e. (('b, 'd,) -> ('c, 'e,) -//│ where -//│ 'a <: 'b -> 'c & 'd -> 'e)) -> 'subTree0 -> (PerfectTree[Two['A]] & 'subTree) -//│ 'a <: 'value -> 'value0) +//│ 'map :> forall 'A 'subTree 'value 'value0 'a 'subTree0. 'a -> ((Leaf[?] & {value: 'value} | (Node[?] with {subTree: 'subTree0})) -> (Leaf['value0] | (Node['A] with {subTree: 'subTree})) +//│ where +//│ 'map <: (forall 'b 'c 'd 'e. (('b, 'd,),) -> ('c, 'e,) +//│ where +//│ 'a <: 'b -> 'c & 'd -> 'e) -> 'subTree0 -> (PerfectTree[Two['A]] & 'subTree) +//│ 'a <: 'value -> 'value0) //│ ╙── //│ map: 'map //│ where -//│ 'map :> forall 'value 'a 'value0 'subTree 'A 'subTree0. 'a -> ((Leaf[?] & {value: 'value} | (Node[?] with {subTree: 'subTree})) -> (Leaf['value0] | (Node['A] with {subTree: 'subTree0})) -//│ where -//│ 'map <: (forall 'b 'c 'd 'e. (('b, 'd,) -> ('c, 'e,) -//│ where -//│ 'a <: 'b -> 'c & 'd -> 'e)) -> 'subTree -> (PerfectTree[Two['A]] & 'subTree0) -//│ 'a <: 'value -> 'value0) +//│ 'map :> forall 'subTree 'value 'value0 'subTree0 'a 'A. 'a -> ((Leaf[?] & {value: 'value} | (Node[?] with {subTree: 'subTree0})) -> (Leaf['value0] | (Node['A] with {subTree: 'subTree})) +//│ where +//│ 'map <: (forall 'b 'c 'd 'e. (('b, 'd,),) -> ('c, 'e,) +//│ where +//│ 'a <: 'b -> 'c & 'd -> 'e) -> 'subTree0 -> (PerfectTree[Two['A]] & 'subTree) +//│ 'a <: 'value -> 'value0) //│ = [Function: map1] :e map succ n4 //│ ╔══[ERROR] Inferred recursive type: 'map //│ where -//│ 'map :> forall 'subTree 'a 'value 'subTree0 'value0 'A. 'a -> ((Leaf[?] & {value: 'value} | (Node[?] with {subTree: 'subTree0})) -> (Leaf['value0] | (Node['A] with {subTree: 'subTree})) -//│ where -//│ 'map <: (forall 'b 'c 'd 'e. (('b, 'd,) -> ('c, 'e,) -//│ where -//│ 'a <: 'b -> 'c & 'd -> 'e)) -> 'subTree0 -> (PerfectTree[Two['A]] & 'subTree) -//│ 'a <: 'value -> 'value0) +//│ 'map :> forall 'value 'value0 'subTree 'A 'subTree0. 'a -> ((Leaf[?] & {value: 'value0} | (Node[?] with {subTree: 'subTree})) -> (Leaf['value] | (Node['A] with {subTree: 'subTree0})) +//│ where +//│ 'map <: (forall 'b 'c 'd 'e. (('b, 'd,),) -> ('c, 'e,) +//│ where +//│ 'a <: 'b -> 'c & 'd -> 'e) -> 'subTree -> (PerfectTree[Two['A]] & 'subTree0) +//│ 'a <: 'value0 -> 'value) //│ ╙── -//│ ╔══[ERROR] Subtyping constraint of the form `?a <: (forall ?A ?subTree ?A0 ?subTree0 ?value ?b ?A1. ?b) -> ?c` exceeded recursion depth limit (250) -//│ ║ l.257: map succ n4 +//│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required +//│ ║ l.261: map succ n4 //│ ║ ^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error diff --git a/shared/src/test/diff/fcp/NestedDataTypesGADT.mls b/shared/src/test/diff/fcp/NestedDataTypesGADT.mls index 284092010a..5c5e2f2b55 100644 --- a/shared/src/test/diff/fcp/NestedDataTypesGADT.mls +++ b/shared/src/test/diff/fcp/NestedDataTypesGADT.mls @@ -15,7 +15,7 @@ type Two[A] = (A, A) //│ Defined type alias Two[+A] def mapTwo f ((a, b)) = (f a, f b) -//│ mapTwo: ('a -> 'b & 'c -> 'd) -> ('a, 'c,) -> ('b, 'd,) +//│ mapTwo: ('a -> 'b & 'c -> 'd) -> (('a, 'c,),) -> ('b, 'd,) //│ = [Function: mapTwo] // class Z @@ -63,7 +63,10 @@ d1_ : HTree[Z, int] //│ = [Function: d1_] d2 = DNode { subTree = (d1_, d1_); n = S{} } -//│ d2: DNode['N, 1] with {n: forall 'P. S['P], subTree: (forall 'a. (DLeaf[1] -> 'a) -> 'a, forall 'a. (DLeaf[1] -> 'a) -> 'a,)} +//│ d2: DNode['N, 1] with { +//│ n: forall 'P. S['P], +//│ subTree: (forall 'a. (DLeaf[1] -> 'a) -> 'a, forall 'a. (DLeaf[1] -> 'a) -> 'a,) +//│ } //│ = DNode { n: S {}, subTree: [ [Function: d1_], [Function: d1_] ] } def d1_ty: HTree[Z, int] @@ -91,11 +94,11 @@ d2_ k = k d2 :e // FIXME d2_ : HTree[S[Z], int] //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.92: d2_ : HTree[S[Z], int] +//│ ║ l.95: d2_ : HTree[S[Z], int] //│ ║ ^^^ -//│ ╟── expression of type `S[in Z & 'p out 'p | Z]` is not an instance of `Z` +//│ ╟── expression of type `S[in Z & 'p out Z | 'p]` is not an instance of type `Z` //│ ╟── Note: constraint arises from type reference: -//│ ║ l.69: def d1_ty: HTree[Z, int] +//│ ║ l.72: def d1_ty: HTree[Z, int] //│ ║ ^ //│ ╟── Note: class type parameter N is defined at: //│ ║ l.36: class DNode[N, A]: HTreeBase[S[N], A] & { subTree: Two[HTree[N, A]] } @@ -111,10 +114,10 @@ d2_ : HTree[S[Z], int] //│ = //│ d2 and d1_ty are not implemented //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.109: d2_ : HTree[S[Z], int] +//│ ║ l.112: d2_ : HTree[S[Z], int] //│ ║ ^^^ -//│ ╟── type `HTreeBase[S[Z], ?]` does not match type `DLeaf[int] | DNode[S[in Z & 'p out 'p | Z], int]` -//│ ║ l.108: d2_ k = k (d2:HTreeBase[S[Z], int]) +//│ ╟── type `HTreeBase[S[Z], ?]` does not match type `DLeaf[int] | DNode[S[in Z & 'p out Z | 'p], int]` +//│ ║ l.111: d2_ k = k (d2:HTreeBase[S[Z], int]) //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.37: type HTree[N, A] = forall 'r. (forall 'p. (DLeaf[A] | DNode[S['p], A] & DNode[N, A]) -> 'r) -> 'r diff --git a/shared/src/test/diff/fcp/NoRecursiveTypes.mls b/shared/src/test/diff/fcp/NoRecursiveTypes.mls new file mode 100644 index 0000000000..1775cd71d8 --- /dev/null +++ b/shared/src/test/diff/fcp/NoRecursiveTypes.mls @@ -0,0 +1,15 @@ +:NoRecursiveTypes + + +:e +foo = + let rec f x = f x.a in 0 +//│ ╔══[ERROR] Inferred recursive type: 'a +//│ where +//│ 'a <: {a: 'a} +//│ ║ l.6: let rec f x = f x.a in 0 +//│ ╙── ^^^ +//│ foo: 0 +//│ = 0 + + diff --git a/shared/src/test/diff/fcp/OCamlList.mls b/shared/src/test/diff/fcp/OCamlList.mls index 8f89689bd4..04f1c9b7da 100644 --- a/shared/src/test/diff/fcp/OCamlList.mls +++ b/shared/src/test/diff/fcp/OCamlList.mls @@ -19,8 +19,8 @@ def asr: int -> int -> int def fst: (('a, 'b),) -> 'a def snd: (('a, 'b),) -> 'b -//│ fst: ('a, anything,) -> 'a -//│ snd: (anything, 'b,) -> 'b +//│ fst: (('a, anything,),) -> 'a +//│ snd: ((anything, 'b,),) -> 'b def assert: { fail: anything -> nothing } //│ assert: {fail: anything -> nothing} @@ -714,9 +714,7 @@ def concat_map = let rec r = { | Nil -> r.concat_map f xs | Cons(y, ys) -> r.prepend_concat_map ys f xs } in r.concat_map -//│ ('A -> List[?]) -> List['A] -> 'a -//│ where -//│ 'a :> List[nothing] +//│ ('A -> List[?]) -> List['A] -> (List[nothing] | 'a) //│ <: concat_map: //│ ('a -> List['b]) -> List['a] -> List['b] @@ -1002,7 +1000,7 @@ rec def compare_length_with l n = def is_empty = fun x -> match x with | Nil -> true | Cons(_, _) -> false -//│ List[?] -> bool +//│ List[?] -> Bool //│ <: is_empty: //│ List[?] -> bool diff --git a/shared/src/test/diff/fcp/Overloads.mls b/shared/src/test/diff/fcp/Overloads.mls index 379504f9a1..71a328bb55 100644 --- a/shared/src/test/diff/fcp/Overloads.mls +++ b/shared/src/test/diff/fcp/Overloads.mls @@ -31,12 +31,12 @@ IISS : ZZII //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.30: IISS : ZZII //│ ║ ^^^^ -//│ ╟── type `0` is not an instance of type `string` -//│ ║ l.7: type ZZII = 0 -> 0 & int -> int -//│ ║ ^ -//│ ╟── Note: constraint arises from type reference: +//│ ╟── type `int` does not match type `0` //│ ║ l.12: def IISS: int -> int & string -> string -//│ ╙── ^^^^^^ +//│ ║ ^^^ +//│ ╟── Note: constraint arises from literal type: +//│ ║ l.7: type ZZII = 0 -> 0 & int -> int +//│ ╙── ^ //│ res: ZZII :e @@ -44,12 +44,9 @@ IISS : BBNN //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.43: IISS : BBNN //│ ║ ^^^^ -//│ ╟── type `bool` is not an instance of type `int` +//│ ╟── type `bool` does not match type `int | string` //│ ║ l.6: type BBNN = bool -> bool & number -> number -//│ ║ ^^^^ -//│ ╟── Note: constraint arises from type reference: -//│ ║ l.12: def IISS: int -> int & string -> string -//│ ╙── ^^^ +//│ ╙── ^^^^ //│ res: BBNN @@ -61,53 +58,20 @@ IISS : int -> int IISS : (0 | 1) -> number //│ res: (0 | 1) -> number -:e IISS : 'a -> 'a -//│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.65: IISS : 'a -> 'a -//│ ║ ^^^^ -//│ ╟── type `int` is not an instance of type `string` -//│ ║ l.12: def IISS: int -> int & string -> string -//│ ║ ^^^ -//│ ╟── Note: constraint arises from type reference: -//│ ║ l.12: def IISS: int -> int & string -> string -//│ ║ ^^^^^^ -//│ ╟── from type variable: -//│ ║ l.65: IISS : 'a -> 'a -//│ ╙── ^^ -//│ res: nothing -> (error | int | string) +//│ res: ('a & (int | string)) -> (int | string | 'a) -:e IISS 0 -//│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.81: IISS 0 -//│ ║ ^^^^^^ -//│ ╟── integer literal of type `0` is not an instance of type `string` -//│ ║ l.81: IISS 0 -//│ ║ ^ -//│ ╟── Note: constraint arises from type reference: -//│ ║ l.12: def IISS: int -> int & string -> string -//│ ╙── ^^^^^^ -//│ res: error | int | string +//│ res: int | string (IISS : int -> int) 0 //│ res: int -:e (if true then IISS else BBNN) 0 -//│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.97: (if true then IISS else BBNN) 0 -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── integer literal of type `0` is not an instance of type `string` -//│ ║ l.97: (if true then IISS else BBNN) 0 -//│ ║ ^ -//│ ╟── Note: constraint arises from type reference: -//│ ║ l.12: def IISS: int -> int & string -> string -//│ ╙── ^^^^^^ -//│ res: bool | error | number | string +//│ res: bool | number | string fun x -> (if true then IISS else BBNN) x -//│ res: nothing -> (bool | number | string) +//│ res: int -> (bool | number | string) if true then IISS else BBNN //│ res: bool -> bool & number -> number | int -> int & string -> string @@ -121,14 +85,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.122: (if true then IISS else BBNN) : (0 | 1 | true) -> number -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── type `0` is not an instance of type `string` -//│ ║ l.122: (if true then IISS else BBNN) : (0 | 1 | true) -> number -//│ ║ ^ -//│ ╟── Note: constraint arises from type reference: -//│ ║ l.12: def IISS: int -> int & string -> string -//│ ╙── ^^^^^^ +//│ ║ l.86: (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 +//│ ╙── ^^^^ //│ res: (0 | 1 | true) -> number @@ -147,7 +108,7 @@ not test // :ds def test: ~(int -> int) & ~bool -//│ test: in ~(int -> int) & ~bool out nothing +//│ test: in ~bool & ~(int -> int) out nothing def test: ~(int -> int) & bool //│ test: in bool out nothing diff --git a/shared/src/test/diff/fcp/PaperTable.mls b/shared/src/test/diff/fcp/PaperTable.mls index 24bccbb7ff..f7306471b1 100644 --- a/shared/src/test/diff/fcp/PaperTable.mls +++ b/shared/src/test/diff/fcp/PaperTable.mls @@ -113,7 +113,7 @@ rec def length l = //│ List[?] -> int //│ <: length: //│ List[?] -> int -//│ = [Function: length] +//│ = [Function: length1] def id: 'a -> 'a def id x = x @@ -250,7 +250,7 @@ def fst: forall 'a 'b. Pair['a, 'b] -> 'a def fst((x, y)) = x //│ fst: Pair['a, ?] -> 'a //│ = -//│ ('a, anything,) -> 'a +//│ (('a, anything,),) -> 'a //│ <: fst: //│ Pair['a, ?] -> 'a //│ = [Function: fst] @@ -612,13 +612,11 @@ rec def id1 x = if true then x else id1 id1 x //│ ║ l.601: rec def id1 x = if true then x else id1 id1 x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. -//│ id1: 'id1 +//│ id1: 'a -> 'b //│ where -//│ 'id1 :> 'a -> 'b -//│ 'a :> 'id1 +//│ 'a :> 'a -> 'b //│ <: 'b -//│ 'b :> 'id1 -//│ <: 'a -> 'b +//│ 'b := 'a -> 'b //│ = [Function: id11] :e @@ -666,8 +664,8 @@ id1 id1 //│ 'c := 'b -> 'c //│ ║ l.601: rec def id1 x = if true then x else id1 id1 x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╔══[ERROR] Subtyping constraint of the form `forall ?a ?b ?c ?d ?e ?id1. ?id1 <: (forall ?f ?g ?id10 ?h ?i ?j. ?id10) -> ?k` exceeded recursion depth limit (250) -//│ ║ l.626: id1 id1 +//│ ╔══[ERROR] Subtyping constraint of the form `forall ?id1. ?id1 <: (forall ?id10. ?id10) -> ?a` exceeded recursion depth limit (250) +//│ ║ l.624: id1 id1 //│ ║ ^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error @@ -677,7 +675,7 @@ id1 id1 :e auto auto //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.678: auto auto +//│ ║ l.676: auto auto //│ ║ ^^^^^^^^^ //│ ╟── type `'a` is not a function //│ ║ l.163: def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) @@ -688,7 +686,7 @@ auto auto //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.163: def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ ╙── ^^ -//│ res: 'b -> 'b | error +//│ res: error | 'b -> 'b //│ Runtime error: //│ RangeError: Maximum call stack size exceeded @@ -696,7 +694,7 @@ auto auto :e (fun x -> x x) (fun x -> x x) //│ ╔══[ERROR] Subtyping constraint of the form `?a -> ?b <: (forall ?c ?d. ?c -> ?d) -> ?e` exceeded recursion depth limit (250) -//│ ║ l.697: (fun x -> x x) (fun x -> x x) +//│ ║ l.695: (fun x -> x x) (fun x -> x x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error @@ -1010,19 +1008,17 @@ rec def id1 x = if true then x else id1 id1 x //│ <: 'c //│ 'c :> 'b -> 'c //│ <: 'a -//│ ║ l.1005: rec def id1 x = if true then x else id1 id1 x +//│ ║ l.1003: rec def id1 x = if true then x else id1 id1 x //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Subtyping constraint of the form `?a -> ?b <: ?id1` exceeded recursion depth limit (250) -//│ ║ l.1005: rec def id1 x = if true then x else id1 id1 x +//│ ║ l.1003: rec def id1 x = if true then x else id1 id1 x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. -//│ id1: 'id1 +//│ id1: 'a -> 'b //│ where -//│ 'id1 :> 'a -> 'b -//│ 'a :> 'id1 +//│ 'a :> 'a -> 'b //│ <: 'b -//│ 'b :> 'id1 -//│ <: 'a -> 'b +//│ 'b := 'a -> 'b //│ = [Function: id12] :e @@ -1068,10 +1064,10 @@ id1 id1 //│ 'b :> 'b -> 'c //│ <: 'c //│ 'c := 'b -> 'c -//│ ║ l.1005: rec def id1 x = if true then x else id1 id1 x +//│ ║ l.1003: rec def id1 x = if true then x else id1 id1 x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╔══[ERROR] Subtyping constraint of the form `forall ?a ?id1 ?b ?c ?d ?e. ?id1 <: (forall ?f ?g ?id10 ?h ?i ?j. ?id10) -> ?k` exceeded recursion depth limit (250) -//│ ║ l.1030: id1 id1 +//│ ╔══[ERROR] Subtyping constraint of the form `forall ?id1. ?id1 <: (forall ?id10. ?id10) -> ?a` exceeded recursion depth limit (250) +//│ ║ l.1026: id1 id1 //│ ║ ^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error @@ -1081,7 +1077,7 @@ id1 id1 :e auto auto //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.1082: auto auto +//│ ║ l.1078: auto auto //│ ║ ^^^^^^^^^ //│ ╟── type `'a` is not a function //│ ║ l.163: def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) @@ -1092,7 +1088,7 @@ auto auto //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.163: def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ ╙── ^^ -//│ res: 'b -> 'b | error +//│ res: error | 'b -> 'b //│ Runtime error: //│ RangeError: Maximum call stack size exceeded @@ -1100,7 +1096,7 @@ auto auto :e (fun x -> x x) (fun x -> x x) //│ ╔══[ERROR] Subtyping constraint of the form `?a -> ?b <: (forall ?c ?d. ?c -> ?d) -> ?e` exceeded recursion depth limit (250) -//│ ║ l.1101: (fun x -> x x) (fun x -> x x) +//│ ║ l.1097: (fun x -> x x) (fun x -> x x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error @@ -1414,19 +1410,17 @@ rec def id1 x = if true then x else id1 id1 x //│ <: 'c //│ 'c :> 'b -> 'c //│ <: 'a -//│ ║ l.1409: rec def id1 x = if true then x else id1 id1 x +//│ ║ l.1405: rec def id1 x = if true then x else id1 id1 x //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing binding of lambda expression; a type annotation may be required -//│ ║ l.1409: rec def id1 x = if true then x else id1 id1 x +//│ ║ l.1405: rec def id1 x = if true then x else id1 id1 x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. -//│ id1: 'id1 +//│ id1: 'a -> 'b //│ where -//│ 'id1 :> 'a -> 'b -//│ 'a :> 'id1 +//│ 'a :> 'a -> 'b //│ <: 'b -//│ 'b :> 'id1 -//│ <: 'a -> 'b +//│ 'b := 'a -> 'b //│ = [Function: id13] :e @@ -1449,10 +1443,10 @@ id1 id1 //│ 'b :> 'b -> 'c //│ <: 'c //│ 'c := 'b -> 'c -//│ ║ l.1409: rec def id1 x = if true then x else id1 id1 x +//│ ║ l.1405: rec def id1 x = if true then x else id1 id1 x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required -//│ ║ l.1434: id1 id1 +//│ ║ l.1428: id1 id1 //│ ║ ^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error @@ -1462,7 +1456,7 @@ id1 id1 :e auto auto //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.1463: auto auto +//│ ║ l.1457: auto auto //│ ║ ^^^^^^^^^ //│ ╟── type `'a` is not a function //│ ║ l.163: def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) @@ -1473,7 +1467,7 @@ auto auto //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.163: def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ ╙── ^^ -//│ res: 'b -> 'b | error +//│ res: error | 'b -> 'b //│ Runtime error: //│ RangeError: Maximum call stack size exceeded @@ -1481,7 +1475,7 @@ auto auto :e (fun x -> x x) (fun x -> x x) //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required -//│ ║ l.1482: (fun x -> x x) (fun x -> x x) +//│ ║ l.1476: (fun x -> x x) (fun x -> x x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error @@ -1787,35 +1781,29 @@ to_ch: int -> ChurchInt // G8 rec def id1 x = if true then x else id1 id1 x -//│ id1: 'id1 +//│ id1: 'a -> 'b //│ where -//│ 'id1 :> 'a -> 'b -//│ 'a :> 'id1 +//│ 'a :> 'a -> 'b //│ <: 'b -//│ 'b :> 'id1 -//│ <: 'a -> 'b +//│ 'b := 'a -> 'b //│ = [Function: id14] // G9 id1 id1 -//│ res: 'a -> 'b | 'id1 +//│ res: ('a & 'b) -> 'a //│ where -//│ 'a :> forall 'id1 'c 'd. 'a -> 'b | 'id1 -//│ <: 'b -//│ 'b :> forall 'id1 'c 'd. 'id1 -//│ <: 'a -> 'b -//│ 'id1 :> 'c -> 'd -//│ 'c :> 'id1 +//│ 'a :> forall 'c 'd. ('a & 'b & 'c) -> ('a | 'd) +//│ <: ((forall 'c 'd. 'c -> 'd) | 'b) -> 'a +//│ 'c :> 'c -> 'd //│ <: 'd -//│ 'd :> 'id1 -//│ <: 'c -> 'd +//│ 'd := 'c -> 'd //│ = [Function: id14] // Gn :e auto auto //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.1816: auto auto +//│ ║ l.1804: auto auto //│ ║ ^^^^^^^^^ //│ ╟── type `'a` is not a function //│ ║ l.163: def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) @@ -1826,7 +1814,7 @@ auto auto //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.163: def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ ╙── ^^ -//│ res: 'b -> 'b | error +//│ res: error | 'b -> 'b //│ Runtime error: //│ RangeError: Maximum call stack size exceeded @@ -1834,7 +1822,7 @@ auto auto :e (fun x -> x x) (fun x -> x x) //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required -//│ ║ l.1835: (fun x -> x x) (fun x -> x x) +//│ ║ l.1823: (fun x -> x x) (fun x -> x x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error diff --git a/shared/src/test/diff/fcp/PolyParams.mls b/shared/src/test/diff/fcp/PolyParams.mls index 590dbdf290..7ac50b731d 100644 --- a/shared/src/test/diff/fcp/PolyParams.mls +++ b/shared/src/test/diff/fcp/PolyParams.mls @@ -10,12 +10,12 @@ fooid = foo id //│ fooid: (1, true,) //│ = [ 1, true ] -fooid._1 -fooid._2 +fooid.0 +fooid.1 //│ res: 1 -//│ = undefined +//│ = 1 //│ res: true -//│ = undefined +//│ = true def foo(f: (forall 'A. 'A -> 'A) -> (forall 'B. 'B -> 'B)) = id f id (f id) diff --git a/shared/src/test/diff/fcp/Proofs.mls b/shared/src/test/diff/fcp/Proofs.mls index 2ef1415056..67e92698ee 100644 --- a/shared/src/test/diff/fcp/Proofs.mls +++ b/shared/src/test/diff/fcp/Proofs.mls @@ -375,8 +375,8 @@ EM_to_DNE: EM -> DNE //│ ║ l.23: type DNE = forall 'a. Not[Not['a]] -> 'a //│ ║ ^^ //│ ╟── back into type variable `'a` -//│ ║ l.32: case em of Left -> em.v, Right -> nna em.v -//│ ║ ^^^^ +//│ ║ l.23: type DNE = forall 'a. Not[Not['a]] -> 'a +//│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this field selection: //│ ║ l.32: case em of Left -> em.v, Right -> nna em.v @@ -386,16 +386,13 @@ EM_to_DNE: EM -> DNE //│ ║ ^^^^^^^^^ //│ ╟── • this field selection: //│ ║ l.32: case em of Left -> em.v, Right -> nna em.v -//│ ║ ^^^^ -//│ ╟── Note: constraint arises from type variable: -//│ ║ l.19: type EM = forall 'a. Either['a, Not['a]] -//│ ╙── ^^ +//│ ╙── ^^^^ //│ res: EM -> DNE :e EM_to_DNE: EM -> DNE //│ ╔══[ERROR] Type error in type ascription -//│ ║ l.396: EM_to_DNE: EM -> DNE +//│ ║ l.393: EM_to_DNE: EM -> DNE //│ ║ ^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.23: type DNE = forall 'a. Not[Not['a]] -> 'a @@ -408,7 +405,7 @@ EM_to_DNE: EM -> DNE //│ ║ l.32: case em of Left -> em.v, Right -> nna em.v //│ ║ ^^^^ //│ ╟── • this reference: -//│ ║ l.396: EM_to_DNE: EM -> DNE +//│ ║ l.393: EM_to_DNE: EM -> DNE //│ ║ ^^^^^^^^^ //│ ╟── • this field selection: //│ ║ l.32: case em of Left -> em.v, Right -> nna em.v @@ -423,14 +420,14 @@ EM_to_DNE: EM -> DNE def EM_to_DNE em nna = case em of Left -> em.v, Right -> nna em.v -//│ EM_to_DNE: 'a -> (forall 'v 'v0. (('v0 -> 'v) -> 'v +//│ EM_to_DNE: 'a -> (forall 'v 'v0. ('v0 -> 'v) -> 'v //│ where -//│ 'a <: (Left with {v: 'v}) | (Right with {v: 'v0}))) +//│ 'a <: (Left with {v: 'v}) | (Right with {v: 'v0})) :e // * Still needs distrib! (after "sound extrusion") EM_to_DNE: EM -> DNE //│ ╔══[ERROR] Type error in type ascription -//│ ║ l.431: EM_to_DNE: EM -> DNE +//│ ║ l.428: EM_to_DNE: EM -> DNE //│ ║ ^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.23: type DNE = forall 'a. Not[Not['a]] -> 'a @@ -440,12 +437,12 @@ EM_to_DNE: EM -> DNE //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: -//│ ║ l.424: def EM_to_DNE em nna = +//│ ║ l.421: def EM_to_DNE em nna = //│ ║ ^^^^^ -//│ ║ l.425: case em of Left -> em.v, Right -> nna em.v +//│ ║ l.422: case em of Left -> em.v, Right -> nna em.v //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: -//│ ║ l.431: EM_to_DNE: EM -> DNE +//│ ║ l.428: EM_to_DNE: EM -> DNE //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.19: type EM = forall 'a. Either['a, Not['a]] @@ -454,27 +451,27 @@ EM_to_DNE: EM -> DNE def DNE_to_EM dne = dne (fun not_em -> not_em (Right { v = fun a -> not_em (Left { v = a }) })) -//│ DNE_to_EM: ((forall 'a 'b. ((Right & {v: forall 'v 'c. ('v -> 'c +//│ DNE_to_EM: ((forall 'a 'b. ((Right & {v: forall 'v 'c. 'v -> 'c //│ where -//│ 'a <: (Left with {v: 'v}) -> 'c)}) -> 'b & 'a) -> 'b) -> 'd) -> 'd +//│ 'a <: (Left with {v: 'v}) -> 'c}) -> 'b & 'a) -> 'b) -> 'd) -> 'd :e // * Still needs distrib! (after "sound extrusion") DNE_to_EM: DNE -> EM //│ ╔══[ERROR] Type error in type ascription -//│ ║ l.462: DNE_to_EM: DNE -> EM +//│ ║ l.459: DNE_to_EM: DNE -> EM //│ ║ ^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.19: type EM = forall 'a. Either['a, Not['a]] //│ ║ ^^ //│ ╟── back into type variable `'a0` -//│ ║ l.456: dne (fun not_em -> not_em (Right { v = fun a -> not_em (Left { v = a }) })) +//│ ║ l.453: dne (fun not_em -> not_em (Right { v = fun a -> not_em (Left { v = a }) })) //│ ║ ^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: -//│ ║ l.462: DNE_to_EM: DNE -> EM +//│ ║ l.459: DNE_to_EM: DNE -> EM //│ ║ ^^^^^^^^^ //│ ╟── • this application: -//│ ║ l.456: dne (fun not_em -> not_em (Right { v = fun a -> not_em (Left { v = a }) })) +//│ ║ l.453: dne (fun not_em -> not_em (Right { v = fun a -> not_em (Left { v = a }) })) //│ ╙── ^^^^^^^^^^^^^^ //│ res: DNE -> EM diff --git a/shared/src/test/diff/fcp/QML_exist_Classes.mls b/shared/src/test/diff/fcp/QML_exist_Classes.mls index c63629d4b1..ab5031d83a 100644 --- a/shared/src/test/diff/fcp/QML_exist_Classes.mls +++ b/shared/src/test/diff/fcp/QML_exist_Classes.mls @@ -45,7 +45,12 @@ baseImpl = ArraysImpl { update = fun r -> fun (i : int) -> fun a -> a; fold = fun f -> fun b -> fun r -> f r b } -//│ baseImpl: ArraysImpl['Rep, 'Rep] with {fold: forall 'a 'b 'c. ('a -> 'b -> 'c) -> 'b -> 'a -> 'c, init: forall 'd. 'd -> 'd, sub: forall 'e. 'e -> int -> 'e, update: forall 'f. anything -> int -> 'f -> 'f} +//│ baseImpl: ArraysImpl['Rep, 'Rep] with { +//│ fold: forall 'a 'b 'c. ('a -> 'b -> 'c) -> 'b -> 'a -> 'c, +//│ init: forall 'd. 'd -> 'd, +//│ sub: forall 'e. 'e -> int -> 'e, +//│ update: forall 'f. anything -> int -> 'f -> 'f +//│ } //│ = ArraysImpl { //│ init: [Function: init], //│ sub: [Function: sub], @@ -57,7 +62,12 @@ def base: Arrays['a] def base f = f baseImpl //│ base: Arrays['a] //│ = -//│ ((forall 'Rep. ArraysImpl['Rep, 'Rep] with {fold: forall 'a 'b 'c. ('a -> 'b -> 'c) -> 'b -> 'a -> 'c, init: forall 'd. 'd -> 'd, sub: forall 'e. 'e -> int -> 'e, update: forall 'f. anything -> int -> 'f -> 'f}) -> 'g) -> 'g +//│ ((forall 'Rep. ArraysImpl['Rep, 'Rep] with { +//│ fold: forall 'a 'b 'c. ('a -> 'b -> 'c) -> 'b -> 'a -> 'c, +//│ init: forall 'd. 'd -> 'd, +//│ sub: forall 'e. 'e -> int -> 'e, +//│ update: forall 'f. anything -> int -> 'f -> 'f +//│ }) -> 'g) -> 'g //│ <: base: //│ Arrays['a] //│ = [Function: base] @@ -72,15 +82,20 @@ def simpleStepImpl arrImpl = ArraysImpl { update = fun ((r0, r1)) -> fun i -> fun a -> (arrImpl.Update r0 i a, "updated"); fold = fun f -> fun b -> fun ((r0, r1)) -> arrImpl.Fold f b r0 } -//│ simpleStepImpl: ArraysRep[in 'A & 'A0 & 'A1 out 'A0 | 'A, in 'Rep & 'Rep0 & 'a out 'Rep | 'Rep0] -> (ArraysImpl['A1, 'Rep1] with {fold: forall 'b. ('A0 -> 'b -> 'b) -> 'b -> ('Rep0, anything,) -> 'b, init: 'A -> ('Rep, "initialized",), sub: ('Rep0, anything,) -> int -> 'A0, update: forall 'c. ('Rep0 & 'c, anything,) -> int -> 'A -> ('Rep | 'c, "updated",)}) -//│ where -//│ 'Rep1 :> ('Rep | 'd, "initialized" | "updated",) -//│ <: ('Rep0 & 'a, anything,) -//│ 'a <: 'Rep0 & 'd -//│ 'd :> 'Rep -//│ <: 'Rep0 & 'a -//│ 'A1 :> 'A0 -//│ <: 'A +//│ simpleStepImpl: ArraysRep[in 'A & 'A0 & 'A1 out 'A0 | 'A, in 'Rep & 'Rep0 & 'a out 'Rep | 'Rep0] -> (ArraysImpl['A1, 'Rep1] with { +//│ fold: forall 'b. ('A0 -> 'b -> 'b) -> 'b -> (('Rep0, anything,),) -> 'b, +//│ init: 'A -> ('Rep, "initialized",), +//│ sub: (('Rep0, anything,),) -> int -> 'A0, +//│ update: forall 'c. (('Rep0 & 'c, anything,),) -> int -> 'A -> ('Rep | 'c, "updated",) +//│ }) +//│ where +//│ 'Rep1 :> ('Rep | 'd, "initialized" | "updated",) +//│ <: ('Rep0 & 'a, anything,) +//│ 'a <: 'Rep0 & 'd +//│ 'd :> 'Rep +//│ <: 'Rep0 & 'a +//│ 'A1 :> 'A0 +//│ <: 'A //│ = [Function: simpleStepImpl] def simpleStepImpl_ty: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] @@ -88,15 +103,20 @@ def simpleStepImpl_ty: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] //│ = simpleStepImpl_ty = simpleStepImpl -//│ ArraysRep[in 'A & 'A0 & 'A1 out 'A0 | 'A, in 'Rep & 'Rep0 & 'a out 'Rep | 'Rep0] -> (ArraysImpl['A1, 'Rep1] with {fold: forall 'b. ('A0 -> 'b -> 'b) -> 'b -> ('Rep0, anything,) -> 'b, init: 'A -> ('Rep, "initialized",), sub: ('Rep0, anything,) -> int -> 'A0, update: forall 'c. ('Rep0 & 'c, anything,) -> int -> 'A -> ('Rep | 'c, "updated",)}) -//│ where -//│ 'Rep1 :> ('Rep | 'd, "initialized" | "updated",) -//│ <: ('Rep0 & 'a, anything,) -//│ 'a <: 'Rep0 & 'd -//│ 'd :> 'Rep -//│ <: 'Rep0 & 'a -//│ 'A1 :> 'A0 -//│ <: 'A +//│ ArraysRep[in 'A & 'A0 & 'A1 out 'A0 | 'A, in 'Rep & 'Rep0 & 'a out 'Rep | 'Rep0] -> (ArraysImpl['A1, 'Rep1] with { +//│ fold: forall 'b. ('A0 -> 'b -> 'b) -> 'b -> (('Rep0, anything,),) -> 'b, +//│ init: 'A -> ('Rep, "initialized",), +//│ sub: (('Rep0, anything,),) -> int -> 'A0, +//│ update: forall 'c. (('Rep0 & 'c, anything,),) -> int -> 'A -> ('Rep | 'c, "updated",) +//│ }) +//│ where +//│ 'Rep1 :> ('Rep | 'd, "initialized" | "updated",) +//│ <: ('Rep0 & 'a, anything,) +//│ 'a <: 'Rep0 & 'd +//│ 'd :> 'Rep +//│ <: 'Rep0 & 'a +//│ 'A1 :> 'A0 +//│ <: 'A //│ <: simpleStepImpl_ty: //│ ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string,)] //│ = [Function: simpleStepImpl] @@ -107,17 +127,15 @@ simpleStepImpl_ty = simpleStepImpl :e :stats simpleStepImpl : ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] -//│ ╔══[ERROR] Subtyping constraint of the form `forall ?A ?A0 ?A1 ?A2 ?A3 ?Rep ?a ?b ?c ?Rep0 ?Rep1 ?A4 ?A5 ?d ?Rep2 ?A6 ?sub ?Rep3 ?e ?Rep4 ?A7 ?Rep5 ?f ?update ?g ?h ?Rep6 ?i ?Rep7 ?A8 ?init ?j ?Rep8 ?A9 ?fold ?Rep9 ?Rep10 ?k. ?a -> ?g <: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string,)]` took too many steps and ran out of fuel (10000) -//│ ║ l.109: simpleStepImpl : ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] +//│ ╔══[ERROR] Subtyping constraint of the form `forall ?a ?b. ?a -> ?b <: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string,)]` took too many steps and ran out of fuel (10000) +//│ ║ l.129: simpleStepImpl : ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] //│ ║ ^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string,)] -//│ where -//│ 'a :> error //│ = [Function: simpleStepImpl] -//│ constrain calls : 10249 -//│ annoying calls : 76 -//│ subtyping calls : 71126 +//│ constrain calls : 9926 +//│ annoying calls : 78 +//│ subtyping calls : 70332 // * Note that the above incidentally can be checked using recursive types :RecursiveTypes :stats @@ -125,8 +143,8 @@ simpleStepImpl : ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] //│ res: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string,)] //│ = [Function: simpleStepImpl] //│ constrain calls : 579 -//│ annoying calls : 101 -//│ subtyping calls : 5641 +//│ annoying calls : 104 +//│ subtyping calls : 5591 :NoRecursiveTypes // * Apparently, it's due to excessive extrusion due to the type annot not being generalized! @@ -135,8 +153,8 @@ simpleStepImpl : forall 'a 'r. ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string) //│ res: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string,)] //│ = [Function: simpleStepImpl] //│ constrain calls : 711 -//│ annoying calls : 101 -//│ subtyping calls : 1838 +//│ annoying calls : 104 +//│ subtyping calls : 1792 @@ -148,11 +166,11 @@ def simpleStep: Arrays['a] -> Arrays['a] // * Note: this one fails to type check when the file is typed with `:ConstrainedTypes` def simpleStep arr = arr (fun impl -> fun (k: ArraysRepConsumer['a, 'r]) -> k (simpleStepImpl impl)) -//│ ((forall 'A 'A0 'Rep 'A1 'a. ArraysRep[in 'A & 'A0 & 'A1 out 'A | 'A0, 'Rep] -> ArraysRepConsumer['A, 'a] -> 'a) -> 'b) -> 'b +//│ ((forall 'a 'A 'A0 'A1 'Rep. ArraysRep[in 'A & 'A1 & 'A0 out 'A | 'A1, 'Rep] -> ArraysRepConsumer['A, 'a] -> 'a) -> 'b) -> 'b //│ where -//│ 'A :> 'A0 -//│ <: 'A1 -//│ 'A1 <: 'A +//│ 'A :> 'A1 +//│ <: 'A0 +//│ 'A0 <: 'A //│ <: simpleStep: //│ Arrays['a] -> Arrays['a] //│ = [Function: simpleStep] @@ -187,7 +205,7 @@ sb (fun arr -> arr.Sub (arr.Init true) 1) :e // * Type error is expected – argument order confusion sb (fun arr -> arr.Sub 0 (arr.Init true)) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.188: sb (fun arr -> arr.Sub 0 (arr.Init true)) +//│ ║ l.206: sb (fun arr -> arr.Sub 0 (arr.Init true)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'Rep` is not an instance of type `int` //│ ║ l.37: type Arrays[A] = (forall 'Rep. ArraysRep[A, 'Rep] -> 'r) -> 'r @@ -196,7 +214,7 @@ sb (fun arr -> arr.Sub 0 (arr.Init true)) //│ ║ l.9: method Sub: Rep -> int -> A //│ ║ ^^^ //│ ╟── from application: -//│ ║ l.188: sb (fun arr -> arr.Sub 0 (arr.Init true)) +//│ ║ l.206: sb (fun arr -> arr.Sub 0 (arr.Init true)) //│ ║ ^^^^^^^^^^^^^ //│ ╟── Note: quantified type variable 'Rep is defined at: //│ ║ l.37: type Arrays[A] = (forall 'Rep. ArraysRep[A, 'Rep] -> 'r) -> 'r @@ -215,35 +233,35 @@ sb (fun arr -> arr.Update (arr.Init true) 1 false) :e // * Rightly prevent skolem confusion sb (fun arr1 -> sb (fun arr2 -> arr2.Update (arr1.Init true))) //│ ╔══[ERROR] Type error in application -//│ ║ l.216: sb (fun arr1 -> sb (fun arr2 -> arr2.Update (arr1.Init true))) +//│ ║ l.234: sb (fun arr1 -> sb (fun arr2 -> arr2.Update (arr1.Init true))) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'Rep` leaks out of its scope //│ ║ l.37: type Arrays[A] = (forall 'Rep. ArraysRep[A, 'Rep] -> 'r) -> 'r //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: -//│ ║ l.216: sb (fun arr1 -> sb (fun arr2 -> arr2.Update (arr1.Init true))) +//│ ║ l.234: sb (fun arr1 -> sb (fun arr2 -> arr2.Update (arr1.Init true))) //│ ║ ^^^^^^^^^^^^^^ //│ ╟── • this application: -//│ ║ l.175: sb = simpleStep base +//│ ║ l.193: sb = simpleStep base //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: -//│ ║ l.216: sb (fun arr1 -> sb (fun arr2 -> arr2.Update (arr1.Init true))) +//│ ║ l.234: sb (fun arr1 -> sb (fun arr2 -> arr2.Update (arr1.Init true))) //│ ║ ^^ //│ ╟── • this reference: -//│ ║ l.216: sb (fun arr1 -> sb (fun arr2 -> arr2.Update (arr1.Init true))) +//│ ║ l.234: sb (fun arr1 -> sb (fun arr2 -> arr2.Update (arr1.Init true))) //│ ║ ^^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.216: sb (fun arr1 -> sb (fun arr2 -> arr2.Update (arr1.Init true))) +//│ ║ l.234: sb (fun arr1 -> sb (fun arr2 -> arr2.Update (arr1.Init true))) //│ ╙── ^^^^^^^^^^^^^^ -//│ res: int -> anything -> (??Rep | ??Rep0) | error +//│ res: error | int -> anything -> (??Rep | ??Rep0) //│ = [Function (anonymous)] sb (fun arr -> let r2 = arr.Update (arr.Init true) 1 false in (arr.Sub r2 0, arr.Sub r2 1) ) -//│ res: (bool, bool,) +//│ res: (Bool, Bool,) //│ = [ false, false ] @@ -252,63 +270,73 @@ sb (fun arr -> :e def simpleStep arr = arr (fun impl -> fun k -> k (simpleStepImpl impl)) -//│ ((forall 'A 'a 'c 'Rep 'Rep0 'A0 'A1 'd. ArraysRep[in 'A1 & 'A0 & 'A out 'A0 | 'A1, in 'Rep & 'Rep0 & 'd out 'Rep | 'Rep0] -> ((forall 'Rep1. ArraysImpl['A, 'Rep1] with {fold: forall 'b. ('A0 -> 'b -> 'b) -> 'b -> ('Rep0, anything,) -> 'b, init: 'A1 -> ('Rep, "initialized",), sub: ('Rep0, anything,) -> int -> 'A0, update: forall 'e. ('Rep0 & 'e, anything,) -> int -> 'A1 -> ('Rep | 'e, "updated",)}) -> 'a) -> 'a) -> 'f) -> 'f -//│ where -//│ 'Rep1 :> ('Rep | 'c, "initialized" | "updated",) -//│ <: ('Rep0 & 'd, anything,) -//│ 'd <: 'Rep0 & 'c -//│ 'c :> 'Rep -//│ <: 'Rep0 & 'd -//│ 'A :> 'A0 -//│ <: 'A1 +//│ ((forall 'Rep 'a 'A 'c 'A0 'd 'Rep0 'A1. ArraysRep[in 'A1 & 'A & 'A0 out 'A | 'A1, in 'Rep0 & 'Rep & 'c out 'Rep0 | 'Rep] -> ((forall 'Rep1. ArraysImpl['A0, 'Rep1] with { +//│ fold: forall 'b. ('A -> 'b -> 'b) -> 'b -> (('Rep, anything,),) -> 'b, +//│ init: 'A1 -> ('Rep0, "initialized",), +//│ sub: (('Rep, anything,),) -> int -> 'A, +//│ update: forall 'e. (('Rep & 'e, anything,),) -> int -> 'A1 -> ('Rep0 | 'e, "updated",) +//│ }) -> 'a) -> 'a) -> 'f) -> 'f +//│ where +//│ 'Rep1 :> ('Rep0 | 'd, "initialized" | "updated",) +//│ <: ('Rep & 'c, anything,) +//│ 'c <: 'Rep & 'd +//│ 'd :> 'Rep0 +//│ <: 'Rep & 'c +//│ 'A0 :> 'A +//│ <: 'A1 //│ <: simpleStep: //│ Arrays['a] -> Arrays['a] //│ ╔══[ERROR] Type error in def definition -//│ ║ l.254: def simpleStep arr = arr (fun impl -> fun k -> k (simpleStepImpl impl)) +//│ ║ l.272: def simpleStep arr = arr (fun impl -> fun k -> k (simpleStepImpl impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'Rep` leaks out of its scope //│ ║ l.37: type Arrays[A] = (forall 'Rep. ArraysRep[A, 'Rep] -> 'r) -> 'r //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this applied expression: -//│ ║ l.254: def simpleStep arr = arr (fun impl -> fun k -> k (simpleStepImpl impl)) +//│ ║ l.272: def simpleStep arr = arr (fun impl -> fun k -> k (simpleStepImpl impl)) //│ ║ ^^^ //│ ╟── • this function: -//│ ║ l.254: def simpleStep arr = arr (fun impl -> fun k -> k (simpleStepImpl impl)) +//│ ║ l.272: def simpleStep arr = arr (fun impl -> fun k -> k (simpleStepImpl impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: -//│ ║ l.71: sub = fun ((r0, r1)) -> fun i -> arrImpl.Sub r0 i; +//│ ║ l.81: sub = fun ((r0, r1)) -> fun i -> arrImpl.Sub r0 i; //│ ╙── ^^ //│ = [Function: simpleStep1] :e def simpleStep2 arr k = arr (fun impl -> k (simpleStepImpl impl)) -//│ ((ArraysRep[out 'A, out 'Rep] -> 'a) -> 'c) -> ((forall 'Rep0. ArraysImpl[in 'A & 'A0 out 'A0, 'Rep0] with {fold: forall 'b. (nothing -> 'b -> 'b) -> 'b -> ('Rep, anything,) -> 'b, init: 'A -> (nothing, "initialized",), sub: ('Rep, anything,) -> int -> nothing, update: forall 'd. ('Rep & 'd, anything,) -> int -> 'A -> ('d, "updated",)}) -> 'a) -> 'c -//│ where -//│ 'Rep0 :> ('e | 'f, "initialized" | "updated",) -//│ <: ('Rep & 'g & 'h, anything,) -//│ 'f :> 'e -//│ <: 'Rep & 'g & 'h -//│ 'h <: 'Rep & 'f -//│ 'e <: 'Rep & 'g -//│ 'g <: 'Rep & 'e +//│ ((ArraysRep[out 'A, out 'Rep] -> 'a) -> 'c) -> ((forall 'Rep0. ArraysImpl[in 'A & 'A0 out 'A0, 'Rep0] with { +//│ fold: forall 'b. (nothing -> 'b -> 'b) -> 'b -> (('Rep, anything,),) -> 'b, +//│ init: 'A -> (nothing, "initialized",), +//│ sub: (('Rep, anything,),) -> int -> nothing, +//│ update: forall 'd. (('Rep & 'd, anything,),) -> int -> 'A -> ('d, "updated",) +//│ }) -> 'a) -> 'c +//│ where +//│ 'Rep0 :> ('e | 'f, "initialized" | "updated",) +//│ <: ('Rep & 'g & 'h, anything,) +//│ 'f :> 'e +//│ <: 'Rep & 'g & 'h +//│ 'h <: 'Rep & 'f +//│ 'e <: 'Rep & 'g +//│ 'g <: 'Rep & 'e //│ <: simpleStep2: //│ Arrays['a] -> Arrays['a] //│ ╔══[ERROR] Type error in def definition -//│ ║ l.285: def simpleStep2 arr k = arr (fun impl -> k (simpleStepImpl impl)) +//│ ║ l.308: def simpleStep2 arr k = arr (fun impl -> k (simpleStepImpl impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'Rep` leaks out of its scope //│ ║ l.37: type Arrays[A] = (forall 'Rep. ArraysRep[A, 'Rep] -> 'r) -> 'r //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: -//│ ║ l.73: fold = fun f -> fun b -> fun ((r0, r1)) -> arrImpl.Fold f b r0 +//│ ║ l.83: fold = fun f -> fun b -> fun ((r0, r1)) -> arrImpl.Fold f b r0 //│ ║ ^^^^^^^ //│ ╟── • this reference: -//│ ║ l.285: def simpleStep2 arr k = arr (fun impl -> k (simpleStepImpl impl)) +//│ ║ l.308: def simpleStep2 arr k = arr (fun impl -> k (simpleStepImpl impl)) //│ ║ ^^^^ //│ ╟── • this applied expression: -//│ ║ l.285: def simpleStep2 arr k = arr (fun impl -> k (simpleStepImpl impl)) +//│ ║ l.308: def simpleStep2 arr k = arr (fun impl -> k (simpleStepImpl impl)) //│ ╙── ^^^ //│ = [Function: simpleStep21] @@ -352,18 +380,23 @@ def stepImpl arrImpl = ArraysImpl { else (r0, arrImpl.Update r1 (div i 2) a); fold = fun f -> fun b -> fun ((r0, r1)) -> arrImpl.Fold f (arrImpl.Fold f b r0) r1 } -//│ stepImpl: ArraysRep[in 'A & 'A0 & 'A1 out 'A0 | 'A, in 'Rep & 'Rep0 & 'a & 'c out 'Rep | 'Rep0] -> (ArraysImpl['A1, 'Rep1] with {fold: forall 'b 'b0. ('A0 -> 'b -> 'b & 'A0 -> 'b0 -> ('b & 'b0)) -> ('b & 'b0) -> ('Rep0, 'Rep0,) -> 'b, init: 'A -> ('Rep, 'Rep,), sub: ('Rep0, 'Rep0,) -> int -> 'A0, update: forall 'd 'e. ('Rep0 & 'd, 'Rep0 & 'e,) -> int -> 'A -> ('Rep | 'd, 'Rep | 'e,)}) -//│ where -//│ 'Rep1 :> ('Rep | 'a | 'f, 'Rep | 'c | 'g,) -//│ <: ('Rep0 & 'a, 'Rep0 & 'c,) -//│ 'c <: 'Rep0 & 'g -//│ 'g :> 'Rep -//│ <: 'Rep0 & 'c -//│ 'a <: 'Rep0 & 'f -//│ 'f :> 'Rep -//│ <: 'Rep0 & 'a -//│ 'A1 :> 'A0 -//│ <: 'A +//│ stepImpl: ArraysRep[in 'A & 'A0 & 'A1 out 'A0 | 'A, in 'Rep & 'Rep0 & 'a & 'c out 'Rep | 'Rep0] -> (ArraysImpl['A1, 'Rep1] with { +//│ fold: forall 'b 'b0. ('A0 -> 'b0 -> 'b0 & 'A0 -> 'b -> ('b0 & 'b)) -> ('b0 & 'b) -> (('Rep0, 'Rep0,),) -> 'b0, +//│ init: 'A -> ('Rep, 'Rep,), +//│ sub: (('Rep0, 'Rep0,),) -> int -> 'A0, +//│ update: forall 'd 'e. (('Rep0 & 'd, 'Rep0 & 'e,),) -> int -> 'A -> ('Rep | 'd, 'Rep | 'e,) +//│ }) +//│ where +//│ 'Rep1 :> ('Rep | 'a | 'f, 'Rep | 'c | 'g,) +//│ <: ('Rep0 & 'a, 'Rep0 & 'c,) +//│ 'c <: 'Rep0 & 'g +//│ 'g :> 'Rep +//│ <: 'Rep0 & 'c +//│ 'a <: 'Rep0 & 'f +//│ 'f :> 'Rep +//│ <: 'Rep0 & 'a +//│ 'A1 :> 'A0 +//│ <: 'A //│ = [Function: stepImpl] @@ -372,7 +405,7 @@ def step: Arrays['a] -> Arrays['a] //│ = def step arr = arr (fun impl -> fun (k: ArraysRepConsumer['a, 'r]) -> k (stepImpl impl)) -//│ ((forall 'a 'A 'A0 'A1 'Rep. ArraysRep[in 'A & 'A0 & 'A1 out 'A | 'A0, 'Rep] -> ArraysRepConsumer['A, 'a] -> 'a) -> 'b) -> 'b +//│ ((forall 'Rep 'a 'A 'A0 'A1. ArraysRep[in 'A & 'A0 & 'A1 out 'A | 'A0, 'Rep] -> ArraysRepConsumer['A, 'a] -> 'a) -> 'b) -> 'b //│ where //│ 'A :> 'A0 //│ <: 'A1 @@ -401,7 +434,7 @@ ssb (fun arr -> let r2 = arr.Update (arr.Init true) 1 false in (arr.Sub r2 0, arr.Sub r2 1) ) -//│ res: (bool, bool,) +//│ res: (Bool, Bool,) //│ = [ true, false ] @@ -413,7 +446,7 @@ ssb (fun arr -> let r2 = arr.Update (arr.Init true) 1 false in (arr.Sub r2 0, arr.Sub r2 1) ) -//│ res: (bool, bool,) +//│ res: (Bool, Bool,) //│ = [ false, false ] diff --git a/shared/src/test/diff/fcp/QML_exist_Classes_min.mls b/shared/src/test/diff/fcp/QML_exist_Classes_min.mls index 0dca49474f..fff2ca6edd 100644 --- a/shared/src/test/diff/fcp/QML_exist_Classes_min.mls +++ b/shared/src/test/diff/fcp/QML_exist_Classes_min.mls @@ -22,13 +22,15 @@ type Arrays[A] = (forall 'Rep. ArraysRep[A, 'Rep] -> 'r) -> 'r def simpleStepImpl arrImpl = ArraysImpl { update = fun ((r0, r1)) -> fun i -> fun a -> (arrImpl.Update r0 i a, "updated") } -//│ simpleStepImpl: ArraysRep['A, in 'Rep & 'a out 'Rep | 'Rep0] -> (ArraysImpl['A, 'Rep1] with {update: forall 'b. ('Rep0 & 'b, anything,) -> int -> 'A -> ('Rep | 'b, "updated",)}) -//│ where -//│ 'Rep1 :> ('c, "updated",) -//│ <: ('a, anything,) -//│ 'a <: 'Rep0 & 'c -//│ 'c :> 'Rep -//│ <: 'a +//│ simpleStepImpl: ArraysRep['A, in 'Rep & 'a out 'Rep | 'Rep0] -> (ArraysImpl['A, 'Rep1] with { +//│ update: forall 'b. (('Rep0 & 'b, anything,),) -> int -> 'A -> ('Rep | 'b, "updated",) +//│ }) +//│ where +//│ 'Rep1 :> ('c, "updated",) +//│ <: ('a, anything,) +//│ 'a <: 'Rep0 & 'c +//│ 'c :> 'Rep +//│ <: 'a //│ = [Function: simpleStepImpl] @@ -51,17 +53,17 @@ mkArrays impl k = k impl :e def stepped = arr (fun arrImpl -> fun k -> k (simpleStepImpl arrImpl)) //│ ╔══[ERROR] Type error in application -//│ ║ l.52: def stepped = arr (fun arrImpl -> fun k -> k (simpleStepImpl arrImpl)) +//│ ║ l.54: def stepped = arr (fun arrImpl -> fun k -> k (simpleStepImpl arrImpl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'Rep` leaks out of its scope //│ ║ l.18: type Arrays[A] = (forall 'Rep. ArraysRep[A, 'Rep] -> 'r) -> 'r //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: -//│ ║ l.52: def stepped = arr (fun arrImpl -> fun k -> k (simpleStepImpl arrImpl)) +//│ ║ l.54: def stepped = arr (fun arrImpl -> fun k -> k (simpleStepImpl arrImpl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: -//│ ║ l.52: def stepped = arr (fun arrImpl -> fun k -> k (simpleStepImpl arrImpl)) +//│ ║ l.54: def stepped = arr (fun arrImpl -> fun k -> k (simpleStepImpl arrImpl)) //│ ║ ^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.23: update = fun ((r0, r1)) -> fun i -> fun a -> (arrImpl.Update r0 i a, "updated") diff --git a/shared/src/test/diff/fcp/QML_exist_Records_D.mls b/shared/src/test/diff/fcp/QML_exist_Records_D.mls index d21b3bc64a..0d709ad756 100644 --- a/shared/src/test/diff/fcp/QML_exist_Records_D.mls +++ b/shared/src/test/diff/fcp/QML_exist_Records_D.mls @@ -31,7 +31,12 @@ baseImpl = { update = fun r -> fun i -> fun a -> a; fold = fun f -> fun b -> fun r -> f r b } -//│ baseImpl: {fold: forall 'a 'b 'c. ('a -> 'b -> 'c) -> 'b -> 'a -> 'c, init: forall 'd. 'd -> 'd, sub: forall 'e. 'e -> anything -> 'e, update: forall 'f. anything -> anything -> 'f -> 'f} +//│ baseImpl: { +//│ fold: forall 'a 'b 'c. ('a -> 'b -> 'c) -> 'b -> 'a -> 'c, +//│ init: forall 'd. 'd -> 'd, +//│ sub: forall 'e. 'e -> anything -> 'e, +//│ update: forall 'f. anything -> anything -> 'f -> 'f +//│ } //│ = { //│ init: [Function: init], //│ sub: [Function: sub], @@ -56,7 +61,12 @@ def base: Arrays['a] // * (not within a more polymorphic context), // * so we do not need first-class parametric polymorphism to type check the definition. def base f = f baseImpl -//│ ({fold: forall 'a 'b 'c. ('a -> 'b -> 'c) -> 'b -> 'a -> 'c, init: forall 'd. 'd -> 'd, sub: forall 'e. 'e -> anything -> 'e, update: forall 'f. anything -> anything -> 'f -> 'f} -> 'g) -> 'g +//│ ({ +//│ fold: forall 'a 'b 'c. ('a -> 'b -> 'c) -> 'b -> 'a -> 'c, +//│ init: forall 'd. 'd -> 'd, +//│ sub: forall 'e. 'e -> anything -> 'e, +//│ update: forall 'f. anything -> anything -> 'f -> 'f +//│ } -> 'g) -> 'g //│ <: base: //│ Arrays['a] //│ = [Function: base] @@ -84,11 +94,21 @@ def stepImpl (arrImpl: ArraysImpl['a, 'r]) = { update = fun ((r0, r1)) -> fun i -> fun a -> (arrImpl.update r0 i a, "hey"); fold = fun f -> fun b -> fun ((r0, r1)) -> arrImpl.fold f b r0 } -//│ stepImpl: ArraysImpl['a, 'r] -> {fold: forall 'b. ('a -> 'b -> 'b) -> 'b -> ('r, anything,) -> 'b, init: 'a -> ('r, "hi",), sub: ('r, anything,) -> int -> 'a, update: ('r, anything,) -> int -> 'a -> ('r, "hey",)} +//│ stepImpl: ArraysImpl['a, 'r] -> { +//│ fold: forall 'b. ('a -> 'b -> 'b) -> 'b -> (('r, anything,),) -> 'b, +//│ init: 'a -> ('r, "hi",), +//│ sub: (('r, anything,),) -> int -> 'a, +//│ update: (('r, anything,),) -> int -> 'a -> ('r, "hey",) +//│ } //│ = [Function: stepImpl] stepImpl_ty = stepImpl -//│ ArraysImpl['a, 'r] -> {fold: forall 'b. ('a -> 'b -> 'b) -> 'b -> ('r, anything,) -> 'b, init: 'a -> ('r, "hi",), sub: ('r, anything,) -> int -> 'a, update: ('r, anything,) -> int -> 'a -> ('r, "hey",)} +//│ ArraysImpl['a, 'r] -> { +//│ fold: forall 'b. ('a -> 'b -> 'b) -> 'b -> (('r, anything,),) -> 'b, +//│ init: 'a -> ('r, "hi",), +//│ sub: (('r, anything,),) -> int -> 'a, +//│ update: (('r, anything,),) -> int -> 'a -> ('r, "hey",) +//│ } //│ <: stepImpl_ty: //│ ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string,)] //│ = [Function: stepImpl] @@ -160,7 +180,7 @@ ssb (fun arr -> let r2 = arr.update (arr.init true) 1 false in (arr.sub r2 0, arr.sub r2 1) ) -//│ res: (bool, bool,) +//│ res: (Bool, Bool,) //│ = [ false, false ] @@ -231,7 +251,7 @@ ssb (fun arr -> let r2 = arr.update (arr.init true) 1 false in (arr.sub r2 0, arr.sub r2 1) ) -//│ res: (bool, bool,) +//│ res: (Bool, Bool,) //│ = [ false, false ] @@ -261,20 +281,20 @@ def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (ste //│ <: step2: //│ Arrays['a] -> Arrays['a] //│ ╔══[ERROR] Type error in def definition -//│ ║ l.259: def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (stepImpl_ty impl)) +//│ ║ l.279: def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (stepImpl_ty impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'rep` leaks out of its scope //│ ║ l.17: type ArraysImplConsumer[A, R] = forall 'rep. ArraysImpl[A, 'rep] -> R //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: -//│ ║ l.259: def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (stepImpl_ty impl)) +//│ ║ l.279: def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (stepImpl_ty impl)) //│ ║ ^^^^ //│ ╟── • this reference: -//│ ║ l.259: def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (stepImpl_ty impl)) +//│ ║ l.279: def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (stepImpl_ty impl)) //│ ║ ^^^ //│ ╟── Note: constraint arises from type variable: -//│ ║ l.76: def stepImpl_ty: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] +//│ ║ l.86: def stepImpl_ty: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] //│ ╙── ^^ //│ = [Function: step24] @@ -287,44 +307,49 @@ def step arr = arr (fun impl -> fun k -> k (stepImpl_ty impl)) //│ <: step: //│ Arrays['a] -> Arrays['a] //│ ╔══[ERROR] Type error in def definition -//│ ║ l.285: def step arr = arr (fun impl -> fun k -> k (stepImpl_ty impl)) +//│ ║ l.305: def step arr = arr (fun impl -> fun k -> k (stepImpl_ty impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'rep` leaks out of its scope //│ ║ l.17: type ArraysImplConsumer[A, R] = forall 'rep. ArraysImpl[A, 'rep] -> R //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this applied expression: -//│ ║ l.285: def step arr = arr (fun impl -> fun k -> k (stepImpl_ty impl)) +//│ ║ l.305: def step arr = arr (fun impl -> fun k -> k (stepImpl_ty impl)) //│ ║ ^^^ //│ ╟── • this function: -//│ ║ l.285: def step arr = arr (fun impl -> fun k -> k (stepImpl_ty impl)) +//│ ║ l.305: def step arr = arr (fun impl -> fun k -> k (stepImpl_ty impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type variable: -//│ ║ l.76: def stepImpl_ty: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] +//│ ║ l.86: def stepImpl_ty: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] //│ ╙── ^^ //│ = [Function: step5] // * Still doesn't work if we only annotate `arr`, as `k` still leaks the internal repr :e def step (arr: Arrays['a]) = arr (fun impl -> fun k -> k (stepImpl impl)) -//│ Arrays['a] -> ({fold: forall 'b. ('a -> 'b -> 'b) -> 'b -> (??rep & 'r, anything,) -> 'b, init: 'a -> ('r | ??rep0, "hi",), sub: (??rep & 'r, anything,) -> int -> 'a, update: (??rep & 'r, anything,) -> int -> 'a -> ('r | ??rep0, "hey",)} -> 'c) -> 'c +//│ Arrays['a] -> ({ +//│ fold: forall 'b. ('a -> 'b -> 'b) -> 'b -> ((??rep & 'r, anything,),) -> 'b, +//│ init: 'a -> ('r | ??rep0, "hi",), +//│ sub: ((??rep & 'r, anything,),) -> int -> 'a, +//│ update: ((??rep & 'r, anything,),) -> int -> 'a -> ('r | ??rep0, "hey",) +//│ } -> 'c) -> 'c //│ <: step: //│ Arrays['a] -> Arrays['a] //│ ╔══[ERROR] Type error in def definition -//│ ║ l.309: def step (arr: Arrays['a]) = arr (fun impl -> fun k -> k (stepImpl impl)) +//│ ║ l.329: def step (arr: Arrays['a]) = arr (fun impl -> fun k -> k (stepImpl impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'rep` leaks out of its scope //│ ║ l.17: type ArraysImplConsumer[A, R] = forall 'rep. ArraysImpl[A, 'rep] -> R //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: -//│ ║ l.309: def step (arr: Arrays['a]) = arr (fun impl -> fun k -> k (stepImpl impl)) +//│ ║ l.329: def step (arr: Arrays['a]) = arr (fun impl -> fun k -> k (stepImpl impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: -//│ ║ l.309: def step (arr: Arrays['a]) = arr (fun impl -> fun k -> k (stepImpl impl)) +//│ ║ l.329: def step (arr: Arrays['a]) = arr (fun impl -> fun k -> k (stepImpl impl)) //│ ║ ^^^ //│ ╟── Note: constraint arises from reference: -//│ ║ l.83: sub = fun ((r0, r1)) -> fun i -> arrImpl.sub r0 i; +//│ ║ l.93: sub = fun ((r0, r1)) -> fun i -> arrImpl.sub r0 i; //│ ╙── ^^ //│ = [Function: step6] @@ -338,10 +363,10 @@ def step arr = arr (forall 'a. fun impl -> forall 'r. fun (k: ArraysImplConsumer // * ...unless we use `stepImpl_ty` instead of `stepImpl` def step arr = arr (fun impl -> forall 'r. fun (k: ArraysImplConsumer['a, 'r]) -> k (stepImpl_ty impl)) -//│ ((forall 'r 'a 'a0 'r0 'a1. ArraysImpl[in 'a1 & 'a0 out 'a1 | 'a0, 'r0] -> ArraysImplConsumer['a, 'r] -> 'r) -> 'b) -> 'b +//│ ((forall 'r 'a 'r0 'a0 'a1. ArraysImpl[in 'a0 & 'a1 out 'a0 | 'a1, 'r0] -> ArraysImplConsumer['a, 'r] -> 'r) -> 'b) -> 'b //│ where -//│ 'a :> 'a1 -//│ <: 'a0 +//│ 'a :> 'a0 +//│ <: 'a1 //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step8] @@ -428,11 +453,26 @@ def stepImpl_Ann = forall 'a 'rep. fun arrImpl -> { update = fun ((r0, r1)) -> fun i -> fun a -> (arrImpl.update r0 i a, "hey"); fold = fun f -> fun b -> fun ((r0, r1)) -> (arrImpl.fold: Fold['a, 'rep]) f b r0 } -//│ stepImpl_Ann: {fold: Fold['a, 'rep], init: 'c -> 'd, sub: 'e -> 'f -> 'g, update: 'h -> 'i -> 'j -> 'k} -> {fold: forall 'b. ('a -> 'b -> 'b) -> 'b -> ('rep, anything,) -> 'b, init: 'c -> ('d, "hi",), sub: ('e, anything,) -> 'f -> 'g, update: ('h, anything,) -> 'i -> 'j -> ('k, "hey",)} +//│ stepImpl_Ann: { +//│ fold: Fold['a, 'rep], +//│ init: 'c -> 'd, +//│ sub: 'e -> 'f -> 'g, +//│ update: 'h -> 'i -> 'j -> 'k +//│ } -> { +//│ fold: forall 'b. ('a -> 'b -> 'b) -> 'b -> (('rep, anything,),) -> 'b, +//│ init: 'c -> ('d, "hi",), +//│ sub: (('e, anything,),) -> 'f -> 'g, +//│ update: (('h, anything,),) -> 'i -> 'j -> ('k, "hey",) +//│ } //│ = [Function: stepImpl_Ann] def step arr = arr (fun impl -> fun (k: ArraysImplConsumer['a, 'r]) -> k (stepImpl_Ann impl)) -//│ ((forall 'a 'rep 'b. {fold: Fold['a, 'rep], init: 'a -> 'rep, sub: 'rep -> int -> 'a, update: 'rep -> int -> 'a -> 'rep} -> ArraysImplConsumer['a, 'b] -> 'b) -> 'c) -> 'c +//│ ((forall 'a 'rep 'b. { +//│ fold: Fold['a, 'rep], +//│ init: 'a -> 'rep, +//│ sub: 'rep -> int -> 'a, +//│ update: 'rep -> int -> 'a -> 'rep +//│ } -> ArraysImplConsumer['a, 'b] -> 'b) -> 'c) -> 'c //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step17] diff --git a/shared/src/test/diff/fcp/QML_exist_Records_ND.mls b/shared/src/test/diff/fcp/QML_exist_Records_ND.mls index 5b651c0296..dd130a67e7 100644 --- a/shared/src/test/diff/fcp/QML_exist_Records_ND.mls +++ b/shared/src/test/diff/fcp/QML_exist_Records_ND.mls @@ -33,7 +33,12 @@ baseImpl = { update = fun r -> fun i -> fun a -> a; fold = fun f -> fun b -> fun r -> f r b } -//│ baseImpl: {fold: forall 'a 'b 'c. ('a -> 'b -> 'c) -> 'b -> 'a -> 'c, init: forall 'd. 'd -> 'd, sub: forall 'e. 'e -> anything -> 'e, update: anything -> anything -> (forall 'f. 'f -> 'f)} +//│ baseImpl: { +//│ fold: forall 'a 'b 'c. ('a -> 'b -> 'c) -> 'b -> 'a -> 'c, +//│ init: forall 'd. 'd -> 'd, +//│ sub: forall 'e. 'e -> anything -> 'e, +//│ update: anything -> anything -> (forall 'f. 'f -> 'f) +//│ } //│ = { //│ init: [Function: init], //│ sub: [Function: sub], @@ -58,7 +63,12 @@ def base: Arrays['a] // * (not within a more polymorphic context), // * so we do not need first-class parametric polymorphism to type check the definition. def base f = f baseImpl -//│ ({fold: forall 'a 'b 'c. ('b -> 'c -> 'a) -> 'c -> 'b -> 'a, init: forall 'd. 'd -> 'd, sub: forall 'e. 'e -> anything -> 'e, update: anything -> anything -> (forall 'f. 'f -> 'f)} -> 'g) -> 'g +//│ ({ +//│ fold: forall 'a 'b 'c. ('c -> 'a -> 'b) -> 'a -> 'c -> 'b, +//│ init: forall 'd. 'd -> 'd, +//│ sub: forall 'e. 'e -> anything -> 'e, +//│ update: anything -> anything -> (forall 'f. 'f -> 'f) +//│ } -> 'g) -> 'g //│ <: base: //│ Arrays['a] //│ = [Function: base] @@ -86,11 +96,21 @@ def stepImpl (arrImpl: ArraysImpl['a, 'r]) = { update = fun ((r0, r1)) -> fun i -> fun a -> (arrImpl.update r0 i a, "hey"); fold = fun f -> fun b -> fun ((r0, r1)) -> arrImpl.fold f b r0 } -//│ stepImpl: ArraysImpl['a, 'r] -> {fold: forall 'b 'b0. ('a -> 'b -> ('b & 'b0)) -> (forall 'c. ('b & 'c) -> ('r, anything,) -> ('b0 | 'c)), init: 'a -> ('r, "hi",), sub: ('r, anything,) -> int -> 'a, update: ('r, anything,) -> int -> 'a -> ('r, "hey",)} +//│ stepImpl: ArraysImpl['a, 'r] -> { +//│ fold: forall 'b 'b0. ('a -> 'b -> ('b & 'b0)) -> (forall 'c. ('b & 'c) -> (('r, anything,),) -> ('b0 | 'c)), +//│ init: 'a -> ('r, "hi",), +//│ sub: (('r, anything,),) -> int -> 'a, +//│ update: (('r, anything,),) -> int -> 'a -> ('r, "hey",) +//│ } //│ = [Function: stepImpl] stepImpl_ty = stepImpl -//│ ArraysImpl['a, 'r] -> {fold: forall 'b 'b0. ('a -> 'b -> ('b & 'b0)) -> (forall 'c. ('b & 'c) -> ('r, anything,) -> ('b0 | 'c)), init: 'a -> ('r, "hi",), sub: ('r, anything,) -> int -> 'a, update: ('r, anything,) -> int -> 'a -> ('r, "hey",)} +//│ ArraysImpl['a, 'r] -> { +//│ fold: forall 'b 'b0. ('a -> 'b -> ('b & 'b0)) -> (forall 'c. ('b & 'c) -> (('r, anything,),) -> ('b0 | 'c)), +//│ init: 'a -> ('r, "hi",), +//│ sub: (('r, anything,),) -> int -> 'a, +//│ update: (('r, anything,),) -> int -> 'a -> ('r, "hey",) +//│ } //│ <: stepImpl_ty: //│ ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string,)] //│ = [Function: stepImpl] @@ -115,7 +135,7 @@ def helper impl (k: ArraysImplConsumer['a, 'res]) = k (stepImpl impl) // * FIXME why does this require so much fuel?! :Fuel 50000 def step (arr: Arrays['a]) = arr helper -//│ Arrays['a] -> (forall 'b 'a0. ArraysImplConsumer['a0, 'b] -> 'b) +//│ Arrays['a] -> (forall 'a0 'b. ArraysImplConsumer['a0, 'b] -> 'b) //│ where //│ 'a0 := 'a //│ <: step: @@ -157,17 +177,22 @@ def helper (impl: ArraysImpl['a, 'rep]) (k: ArraysImplConsumer['b, 'res]) = k (s // * FIXME this works with `:Fuel 4000000` but takes ~10s!! // * Why require so much fuel? (notably, more than in the same `helper` but *without* the impl annot) // * -> probably due to 'b being generalized too early +// * Note [2023-10-06]: +// * It seems the fuel might be needed because of TV reconstraining after extrusion, +// * which is currently implemented in a very naive and wasteful way! +// * Indeed, if we set (includeBounds = true) in the `getVars` method, +// * which is used for reconstraining, then this no longer require extra fuel! :e def step (arr: Arrays['a]) = arr helper //│ Arrays['a] -> error //│ <: step: //│ Arrays['a] -> Arrays['a] -//│ ╔══[ERROR] Subtyping constraint of the form `Arrays['a] <: (forall 'a0 'rep. ArraysImpl['a0, 'rep] -> (forall 'b ?a 'res 'a1 ?b ?c. ArraysImplConsumer['b, 'res] -> ?b)) -> ?d` took too many steps and ran out of fuel (10000) -//│ ║ l.161: def step (arr: Arrays['a]) = arr helper +//│ ╔══[ERROR] Subtyping constraint of the form `Arrays['a] <: (forall 'a0 'rep. ArraysImpl['a0, 'rep] -> (forall ?a 'res 'b. ArraysImplConsumer['b, 'res] -> ?a)) -> ?b` took too many steps and ran out of fuel (10000) +//│ ║ l.186: def step (arr: Arrays['a]) = arr helper //│ ║ ^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. -//│ ╔══[ERROR] Subtyping constraint of the form `forall 'a 'a0 'a1 ?a. Arrays['a] -> ?a <: forall 'a2. Arrays['a2] -> Arrays['a2]` took too many steps and ran out of fuel (10000) -//│ ║ l.161: def step (arr: Arrays['a]) = arr helper +//│ ╔══[ERROR] Subtyping constraint of the form `forall 'a ?a. Arrays['a] -> ?a <: forall 'a0. Arrays['a0] -> Arrays['a0]` took too many steps and ran out of fuel (10000) +//│ ║ l.186: def step (arr: Arrays['a]) = arr helper //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ = [Function: step3] @@ -193,62 +218,77 @@ def step (arr: Arrays['a]) = arr helper // * Doesn't work (`'rep` leaks out of its scope in `step`) def helper impl k = k (stepImpl impl) -//│ helper: ArraysImpl[in 'a out 'a | 'a0, in 'r out 'r | 'r0] -> (forall 'a1 'r1 'c. ({fold: forall 'b 'b0. (('a | 'a1) -> 'b -> ('b & 'b0)) -> (forall 'd. ('b & 'd) -> ('r0 & 'r1, anything,) -> ('b0 | 'd)), init: ('a0 & 'a1) -> ('r | 'r1, "hi",), sub: ('r0 & 'r1, anything,) -> int -> ('a | 'a1), update: ('r0 & 'r1, anything,) -> int -> ('a0 & 'a1) -> ('r | 'r1, "hey",)} -> 'c) -> 'c) +//│ helper: ArraysImpl[in 'a out 'a | 'a0, in 'r out 'r | 'r0] -> (forall 'a1 'r1 'c. ({ +//│ fold: forall 'b 'b0. (('a | 'a1) -> 'b -> ('b & 'b0)) -> (forall 'd. ('b & 'd) -> (('r0 & 'r1, anything,),) -> ('b0 | 'd)), +//│ init: ('a0 & 'a1) -> ('r | 'r1, "hi",), +//│ sub: (('r0 & 'r1, anything,),) -> int -> ('a | 'a1), +//│ update: (('r0 & 'r1, anything,),) -> int -> ('a0 & 'a1) -> ('r | 'r1, "hey",) +//│ } -> 'c) -> 'c) //│ = [Function: helper4] // * Idem def helper (impl: ArraysImpl['a, 'rep]) k = k (stepImpl impl) -//│ helper: ArraysImpl['a, 'rep] -> (forall 'c. ({fold: forall 'b 'b0. ('a -> 'b -> ('b & 'b0)) -> (forall 'd. ('b & 'd) -> ('rep, anything,) -> ('b0 | 'd)), init: 'a -> ('rep, "hi",), sub: ('rep, anything,) -> int -> 'a, update: ('rep, anything,) -> int -> 'a -> ('rep, "hey",)} -> 'c) -> 'c) +//│ helper: ArraysImpl['a, 'rep] -> (forall 'c. ({ +//│ fold: forall 'b 'b0. ('a -> 'b -> ('b & 'b0)) -> (forall 'd. ('b & 'd) -> (('rep, anything,),) -> ('b0 | 'd)), +//│ init: 'a -> ('rep, "hi",), +//│ sub: (('rep, anything,),) -> int -> 'a, +//│ update: (('rep, anything,),) -> int -> 'a -> ('rep, "hey",) +//│ } -> 'c) -> 'c) //│ = [Function: helper5] :e def step (arr: Arrays['a]) = arr helper -//│ Arrays['a] -> (forall 'c. ({fold: forall 'b 'b0. (('a0 | 'a) -> 'b -> ('b & 'b0)) -> (forall 'd. ('b & 'd) -> ('rep & 'rep0, anything,) -> ('b0 | 'd)), init: 'a -> ('rep, "hi",), sub: ('rep & 'rep0, anything,) -> int -> ('a0 | 'a), update: ('rep & 'rep0, anything,) -> int -> 'a -> ('rep, "hey",)} -> 'c) -> 'c | error) -//│ where -//│ 'rep :> ??rep -//│ <: 'rep0 -//│ 'rep0 <: ??rep0 & 'rep -//│ 'a <: 'a0 -//│ 'a0 := 'a +//│ Arrays['a] -> (forall 'c. error | ({ +//│ fold: forall 'b 'b0. (('a0 | 'a) -> 'b -> ('b & 'b0)) -> (forall 'd. ('b & 'd) -> (('rep & 'rep0, anything,),) -> ('b0 | 'd)), +//│ init: 'a -> ('rep, "hi",), +//│ sub: (('rep & 'rep0, anything,),) -> int -> ('a0 | 'a), +//│ update: (('rep & 'rep0, anything,),) -> int -> 'a -> ('rep, "hey",) +//│ } -> 'c) -> 'c) +//│ where +//│ 'rep :> ??rep +//│ <: 'rep0 +//│ 'rep0 <: ??rep0 & 'rep +//│ 'a <: 'a0 +//│ 'a0 := 'a //│ <: step: //│ Arrays['a] -> Arrays['a] //│ ╔══[ERROR] Type error in application -//│ ║ l.205: def step (arr: Arrays['a]) = arr helper +//│ ║ l.240: def step (arr: Arrays['a]) = arr helper //│ ║ ^^^^^^^^^^ //│ ╟── type variable `'rep` leaks out of its scope //│ ║ l.19: type ArraysImplConsumer[A, R] = forall 'rep. ArraysImpl[A, 'rep] -> R //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: -//│ ║ l.200: def helper (impl: ArraysImpl['a, 'rep]) k = k (stepImpl impl) +//│ ║ l.230: def helper (impl: ArraysImpl['a, 'rep]) k = k (stepImpl impl) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: -//│ ║ l.205: def step (arr: Arrays['a]) = arr helper +//│ ║ l.240: def step (arr: Arrays['a]) = arr helper //│ ║ ^^^^^^ //│ ╟── • this reference: -//│ ║ l.205: def step (arr: Arrays['a]) = arr helper +//│ ║ l.240: def step (arr: Arrays['a]) = arr helper //│ ║ ^^^ //│ ╟── Note: constraint arises from type variable: -//│ ║ l.83: def stepImpl (arrImpl: ArraysImpl['a, 'r]) = { +//│ ║ l.93: def stepImpl (arrImpl: ArraysImpl['a, 'r]) = { //│ ╙── ^^ //│ ╔══[ERROR] Type error in def definition -//│ ║ l.205: def step (arr: Arrays['a]) = arr helper +//│ ║ l.240: def step (arr: Arrays['a]) = arr helper //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'rep` leaks out of its scope //│ ║ l.19: type ArraysImplConsumer[A, R] = forall 'rep. ArraysImpl[A, 'rep] -> R //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: -//│ ║ l.200: def helper (impl: ArraysImpl['a, 'rep]) k = k (stepImpl impl) +//│ ║ l.230: def helper (impl: ArraysImpl['a, 'rep]) k = k (stepImpl impl) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: -//│ ║ l.205: def step (arr: Arrays['a]) = arr helper +//│ ║ l.240: def step (arr: Arrays['a]) = arr helper //│ ║ ^^^^^^ //│ ╟── • this reference: -//│ ║ l.205: def step (arr: Arrays['a]) = arr helper +//│ ║ l.240: def step (arr: Arrays['a]) = arr helper //│ ║ ^^^ //│ ╟── Note: constraint arises from reference: -//│ ║ l.85: sub = fun ((r0, r1)) -> fun i -> arrImpl.sub r0 i; +//│ ║ l.95: sub = fun ((r0, r1)) -> fun i -> arrImpl.sub r0 i; //│ ╙── ^^ //│ = [Function: step5] @@ -427,7 +467,7 @@ ssb (fun arr -> let r2 = arr.update (arr.init true) 1 false in (arr.sub r2 0, arr.sub r2 1) ) -//│ res: (bool, bool,) +//│ res: (Bool, Bool,) //│ = [ false, false ] @@ -496,20 +536,20 @@ def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (ste //│ <: step2: //│ Arrays['a] -> Arrays['a] //│ ╔══[ERROR] Type error in def definition -//│ ║ l.494: def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (stepImpl_ty impl)) +//│ ║ l.534: def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (stepImpl_ty impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'rep` leaks out of its scope //│ ║ l.19: type ArraysImplConsumer[A, R] = forall 'rep. ArraysImpl[A, 'rep] -> R //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: -//│ ║ l.494: def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (stepImpl_ty impl)) +//│ ║ l.534: def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (stepImpl_ty impl)) //│ ║ ^^^^ //│ ╟── • this reference: -//│ ║ l.494: def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (stepImpl_ty impl)) +//│ ║ l.534: def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (stepImpl_ty impl)) //│ ║ ^^^ //│ ╟── Note: constraint arises from type variable: -//│ ║ l.78: def stepImpl_ty: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] +//│ ║ l.88: def stepImpl_ty: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] //│ ╙── ^^ //│ = [Function: step25] @@ -521,7 +561,7 @@ def step2 = forall 'a. fun arr -> fun (k: ArraysImplConsumer['a, 'rep]) -> arr ( //│ <: step2: //│ Arrays['a] -> Arrays['a] //│ ╔══[ERROR] Type error in def definition -//│ ║ l.517: def step2 = forall 'a. fun arr -> fun (k: ArraysImplConsumer['a, 'rep]) -> arr (fun impl -> k (stepImpl_ty impl)) +//│ ║ l.557: def step2 = forall 'a. fun arr -> fun (k: ArraysImplConsumer['a, 'rep]) -> arr (fun impl -> k (stepImpl_ty impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'r` leaks out of its scope //│ ║ l.22: type Arrays[A] = forall 'r. ArraysImplConsumer[A, 'r] -> 'r @@ -531,7 +571,7 @@ def step2 = forall 'a. fun arr -> fun (k: ArraysImplConsumer['a, 'rep]) -> arr ( //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: -//│ ║ l.517: def step2 = forall 'a. fun arr -> fun (k: ArraysImplConsumer['a, 'rep]) -> arr (fun impl -> k (stepImpl_ty impl)) +//│ ║ l.557: def step2 = forall 'a. fun arr -> fun (k: ArraysImplConsumer['a, 'rep]) -> arr (fun impl -> k (stepImpl_ty impl)) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ = [Function: step26] @@ -543,13 +583,13 @@ def s arr (k: ArraysImplConsumer['a, 'rep]) = arr (fun impl -> k (stepImpl_ty im // * We can see it shouldn't be simplified to nothing: :ns s -//│ res: forall 'a 'b 'a0 'rep 'c. 'b -> (forall 'd 'a1 'rep0. ArraysImplConsumer['a1, 'rep0] -> 'd) +//│ res: forall 'b 'a 'a0 'rep 'c. 'c -> (forall 'd 'a1 'rep0. ArraysImplConsumer['a1, 'rep0] -> 'd) //│ where -//│ 'd :> 'c +//│ 'd :> 'b //│ 'rep0 <: 'rep //│ 'a1 :> 'a0 //│ <: 'a -//│ 'b <: (forall 'e 'a2 'f 'r. 'e -> 'f) -> 'c +//│ 'c <: (forall 'e 'a2 'f 'r. 'e -> 'f) -> 'b //│ 'f :> 'rep //│ 'e <: ArraysImpl['a2, 'r] //│ 'a2 :> 'a @@ -565,7 +605,7 @@ step2 = s //│ <: step2: //│ Arrays['a] -> Arrays['a] //│ ╔══[ERROR] Type error in def definition -//│ ║ l.563: step2 = s +//│ ║ l.603: step2 = s //│ ║ ^^^^^^^^^ //│ ╟── type variable `'r` leaks out of its scope //│ ║ l.22: type Arrays[A] = forall 'r. ArraysImplConsumer[A, 'r] -> 'r @@ -575,10 +615,10 @@ step2 = s //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: -//│ ║ l.563: step2 = s +//│ ║ l.603: step2 = s //│ ║ ^ //│ ╟── • this application: -//│ ║ l.540: def s arr (k: ArraysImplConsumer['a, 'rep]) = arr (fun impl -> k (stepImpl_ty impl)) +//│ ║ l.580: def s arr (k: ArraysImplConsumer['a, 'rep]) = arr (fun impl -> k (stepImpl_ty impl)) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ = [Function: s] @@ -595,7 +635,7 @@ ssb (fun arr -> let r2 = arr.update (arr.init true) 1 false in (arr.sub r2 0, arr.sub r2 1) ) -//│ res: (bool, bool,) +//│ res: (Bool, Bool,) //│ = [ false, false ] diff --git a/shared/src/test/diff/fcp/QML_exist_Records_min_1.mls b/shared/src/test/diff/fcp/QML_exist_Records_min_1.mls index fe837877f2..b39b761b03 100644 --- a/shared/src/test/diff/fcp/QML_exist_Records_min_1.mls +++ b/shared/src/test/diff/fcp/QML_exist_Records_min_1.mls @@ -142,9 +142,11 @@ def step: Arrays2 -> Arrays2 def step arr k = k (arr stepImpl) //│ 'a -> (('b -> 'c) -> 'c //│ where -//│ 'a <: (forall 'd. 'd -> {fold: forall 'e 'f 'g. 'e -> ('f -> 'g -//│ where -//│ 'd <: {fold: 'e -> 'f -> 'g})}) -> 'b) +//│ 'a <: (forall 'd. 'd -> { +//│ fold: forall 'e 'f 'g. 'e -> ('f -> 'g +//│ where +//│ 'd <: {fold: 'e -> 'f -> 'g}) +//│ }) -> 'b) //│ <: step: //│ Arrays2 -> Arrays2 //│ ╔══[ERROR] Type error in def definition diff --git a/shared/src/test/diff/fcp/QML_exist_Records_min_2.mls b/shared/src/test/diff/fcp/QML_exist_Records_min_2.mls index e6ec824874..d2557d44d3 100644 --- a/shared/src/test/diff/fcp/QML_exist_Records_min_2.mls +++ b/shared/src/test/diff/fcp/QML_exist_Records_min_2.mls @@ -27,15 +27,15 @@ def stepImpl_ty: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] :ns def helper impl (k: ArraysImplConsumer['a2, 'res]) = k (stepImpl_ty impl) -//│ helper: forall 'r 'a 'b 'r0 'a0. 'b -> (forall 'a2 'res 'a1 'c. ArraysImplConsumer['a2, 'res] -> 'c) +//│ helper: forall 'a 'a0 'r 'b 'r0. 'b -> (forall 'a2 'res 'a1 'c. ArraysImplConsumer['a2, 'res] -> 'c) //│ where //│ 'res <: 'c //│ 'a2 :> 'a0 //│ <: 'a1 //│ 'a1 :> 'a0 //│ <: 'a2 & 'a -//│ 'b <: ArraysImpl[in 'a0 out 'a, in 'r out 'r0] -//│ 'r0 :> 'r +//│ 'b <: ArraysImpl[in 'a0 out 'a, in 'r0 out 'r] +//│ 'r :> 'r0 //│ 'a0 <: 'a //│ 'a :> 'a0 diff --git a/shared/src/test/diff/fcp/QML_exist_nu.mls b/shared/src/test/diff/fcp/QML_exist_nu.mls new file mode 100644 index 0000000000..39fa4e19e9 --- /dev/null +++ b/shared/src/test/diff/fcp/QML_exist_nu.mls @@ -0,0 +1,339 @@ +// * TODO also a GADT version of this where we use `Arrays[A]: ArraysImpl[A, ?]` + +:NewDefs + +:DontDistributeForalls // * Also works without this + + + +declare module Math { + fun trunc: Num -> Int +} +//│ declare module Math { +//│ fun trunc: Num -> Int +//│ } + +fun div(a, b) = Math.trunc(a/b) +fun mod(a, b) = if a < b then a else mod(a - b, b) +//│ fun div: (Num, Num) -> Int +//│ fun mod: (Int, Int) -> Int + + + +abstract class ArraysImpl[A, Rep] { + fun init: A -> Rep + fun sub: (Rep, Int) -> A + fun update: (Rep, Int, A) -> Rep + fun fold: (Rep, 'b, A -> 'b -> 'b) -> 'b +} +//│ abstract class ArraysImpl[A, Rep] { +//│ fun fold: forall 'b. (Rep, 'b, A -> 'b -> 'b) -> 'b +//│ fun init: A -> Rep +//│ fun sub: (Rep, Int) -> A +//│ fun update: (Rep, Int, A) -> Rep +//│ } + +type ArraysImplConsumer[A, R] = forall 'rep: ArraysImpl[A, 'rep] -> R +//│ type ArraysImplConsumer[A, R] = forall 'rep. ArraysImpl[A, 'rep] -> R + +abstract class Arrays[A] { + fun use: ArraysImplConsumer[A, 'res] -> 'res +} +//│ abstract class Arrays[A] { +//│ fun use: forall 'res. ArraysImplConsumer[A, 'res] -> 'res +//│ } + + +class BaseImpl[A]() extends ArraysImpl[A, A] { + fun init (a) = a + fun sub (r, i) = r + fun update(r, i, a) = a + fun fold (r, b, f) = f(r)(b) +} +//│ class BaseImpl[A]() extends ArraysImpl { +//│ fun fold: forall 'a 'b 'c. ('a, 'b, 'a -> 'b -> 'c) -> 'c +//│ fun init: forall 'd. 'd -> 'd +//│ fun sub: forall 'e. ('e, anything) -> 'e +//│ fun update: forall 'f. (anything, anything, 'f) -> 'f +//│ } + +class StepImpl[A, R](underlying: ArraysImpl[A, R]) extends ArraysImpl[A, [R, R]] { + fun init(a) = [underlying.init(a), underlying.init(a)] + fun sub([r0, r1], i) = + if mod(i, 2) === 0 + then underlying.sub(r0, div(i, 2)) + else underlying.sub(r1, div(i, 2)) + fun update([r0, r1], i, a) = + if mod(i, 2) == 0 + then [underlying.update(r0, div(i, 2), a), r1] + else [r0, underlying.update(r1, div(i, 2), a)] + fun fold([r0, r1], b, f) = + underlying.fold(r0, underlying.fold(r1, b, f), f) +} +//│ class StepImpl[A, R](underlying: ArraysImpl[A, R]) extends ArraysImpl { +//│ fun fold: forall 'b 'b0. ([R, R], 'b & 'b0, A -> ('b -> ('b & 'b0) & 'b0 -> 'b0)) -> 'b0 +//│ fun init: A -> [R, R] +//│ fun sub: ([R, R], Eql[0] & Int) -> A +//│ fun update: ([R, R], Int, A) -> [R, R] +//│ } + + +class Base[A]() extends Arrays[A] { + val impl = BaseImpl() + fun use(k) = k(impl) +} +//│ class Base[A]() extends Arrays { +//│ val impl: BaseImpl[A] +//│ fun use: forall 'a. (BaseImpl[A] -> 'a) -> 'a +//│ } + +class Step[A](from: Arrays[A]) extends Arrays[A] { + + // * Note: expansion of alias is capture-avoiding of polymorphic levels + fun use(k: ArraysImplConsumer[A, 'res]) = from.use of + forall 'rep: + (impl: ArraysImpl[A, 'rep]) => k(StepImpl(impl)) + +} +//│ class Step[A](from: Arrays[A]) extends Arrays { +//│ fun use: forall 'res. (k: ArraysImplConsumer[A, 'res]) -> 'res +//│ } + +// * A version with fewer annotations +class Step[A](from: Arrays[A]) extends Arrays[A] { + + fun use(k: ArraysImplConsumer[A, 'res]) = + from.use of impl => k(StepImpl(impl)) + + // * Spelling out the type synonym: + fun use': ArraysImplConsumer[A, 'res] -> 'res + fun use'(k: forall 'rep: ArraysImpl[A, 'rep] -> 'res) = + from.use of impl => k of StepImpl(impl) + +} +//│ class Step[A](from: Arrays[A]) extends Arrays { +//│ fun use: forall 'res. (k: ArraysImplConsumer[A, 'res]) -> 'res +//│ fun use': forall 'res0. ArraysImplConsumer[A, 'res0] -> 'res0 +//│ } + +// * Note: the annotation on `k` is required, otherwise we leak the locally-polymorphic `impl` +// * (We don't currently do any bidirectional typing.) +:e +class Step'[A](from: Arrays[A]) extends Arrays[A] { + fun use(k) = + from.use of impl => k(StepImpl(impl)) +} +//│ ╔══[ERROR] Type error in definition of method use +//│ ║ l.123: fun use(k) = +//│ ║ ^^^^^^^^ +//│ ║ l.124: from.use of impl => k(StepImpl(impl)) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type variable `'rep` leaks out of its scope +//│ ║ l.36: type ArraysImplConsumer[A, R] = forall 'rep: ArraysImpl[A, 'rep] -> R +//│ ║ ^^^^ +//│ ╟── adding a type annotation to any of the following terms may help resolve the problem +//│ ╟── • this reference: +//│ ║ l.124: from.use of impl => k(StepImpl(impl)) +//│ ║ ^^^^ +//│ ╟── • this signature of member `use`: +//│ ║ l.40: fun use: ArraysImplConsumer[A, 'res] -> 'res +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── • this field selection: +//│ ║ l.124: from.use of impl => k(StepImpl(impl)) +//│ ╙── ^^^^^^^^ +//│ class Step'[A](from: Arrays[A]) extends Arrays { +//│ fun use: forall 'R 'a. (StepImpl[A, in ??rep & 'R out 'R | ??rep0] -> 'a) -> 'a +//│ } +//│ Syntax error: +//│ Unexpected string + + +let ssb = Step(Step(Base())) +//│ let ssb: Step['A] +//│ ssb +//│ = Step {} + +ssb.use of impl => + let r = impl.update(impl.init(true), 1, false) + log(r) + [impl.sub(r, 0), impl.sub(r, 1)] +//│ [Bool, Bool] +//│ res +//│ = [ true, false ] +//│ // Output +//│ [ [ true, true ], [ false, true ] ] + +fun mkMonoArray(n) = + if n === 0 then Base() else Step(mkMonoArray(n - 1)) +//│ fun mkMonoArray: forall 'A. (Eql[0] & Int) -> (Base['A] | Step['A]) + +let snb = mkMonoArray(3) +//│ let snb: Base['A] | Step['A] +//│ snb +//│ = Step {} + +snb.use of impl => + let r = impl.update(impl.init(true), 1, false) + log(r) +//│ () +//│ res +//│ = undefined +//│ // Output +//│ [ +//│ [ [ true, true ], [ true, true ] ], +//│ [ [ false, true ], [ true, true ] ] +//│ ] + +// * Here we are trying to leak the internally-quantified representation, resulting in the `??rep` extrusion +snb.use of impl => impl.init(true) +// :d +//│ true | ??rep +//│ res +//│ = [ +//│ [ [ true, true ], [ true, true ] ], +//│ [ [ true, true ], [ true, true ] ] +//│ ] + + +// * An alternative implementation of Step with the existential opened outside the function. + +class StepAlt[A](from: Arrays[A]) extends Arrays[A] { + val use = from.use of impl => + (k: ArraysImplConsumer[A, 'res]) => k(StepImpl(impl)) +} +//│ class StepAlt[A](from: Arrays[A]) extends Arrays { +//│ val use: forall 'res. (k: ArraysImplConsumer[A, 'res]) -> 'res +//│ } + +// * With the following, we get "type variable `'rep` leaks out of its scope" +:e +class StepAlt'[A](from: Arrays[A]) extends Arrays[A] { + val use = from.use of impl => + k => k(StepImpl(impl)) + // * ^ This is because we leak impl's representation to `k` in the local `k =>` lambda, + // * which flows to the type of `use`, where it's extruded: + // * forall 'r; (StepImpl[A, ??impl] -> 'r) -> 'r + // * Interestingly, once we use first-class existentials to extrude things, + // * this should start working, because we'll get + // * exists impl; forall 'r; (StepImpl[A, impl] -> 'r) -> 'r + // * which is a sutbype of the required + // * (forall 'rep; ArraysImpl[A, 'rep] -> 'res) -> 'res + // * because we can 0-rigidify `impl` and then subtype + // * 0. (StepImpl[A, impl] -> 'r) -> 'r <: (forall 'rep; ArraysImpl[A, 'rep] -> 'res) -> 'res + // * ie, constraining the parameters and 1-instantiating `forall 'rep`: + // * 1. ArraysImpl[A, 'rep] -> 'res <: (StepImpl[A, impl] -> 'r) -> 'r + // * which eventually leads to 'rep := impl and 'r := 'res. +} +//│ ╔══[ERROR] Type error in application +//│ ║ l.211: val use = from.use of impl => +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ║ l.212: k => k(StepImpl(impl)) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type variable `'rep` leaks out of its scope +//│ ║ l.36: type ArraysImplConsumer[A, R] = forall 'rep: ArraysImpl[A, 'rep] -> R +//│ ║ ^^^^ +//│ ╟── adding a type annotation to any of the following terms may help resolve the problem +//│ ╟── • this function: +//│ ║ l.211: val use = from.use of impl => +//│ ║ ^^^^^^^ +//│ ║ l.212: k => k(StepImpl(impl)) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── • this signature of member `use`: +//│ ║ l.40: fun use: ArraysImplConsumer[A, 'res] -> 'res +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── • this field selection: +//│ ║ l.211: val use = from.use of impl => +//│ ╙── ^^^^^^^^ +//│ class StepAlt'[A](from: Arrays[A]) extends Arrays { +//│ val use: forall 'R 'a. error | (StepImpl[A, 'R] -> 'a) -> 'a +//│ } +//│ where +//│ 'R :> ??rep +//│ <: ??rep0 +//│ Syntax error: +//│ Unexpected string + + +// * An alternative implementation of Step which only allocates one StepImpl per instance! + +class StepAlt[A](from: Arrays[A]) extends Arrays[A] { + + // * The explicit `forall 'res` is needed with distributivity turned off + val use = forall 'res: from.use of impl => + val impl2 = StepImpl(impl) + (k: ArraysImplConsumer[A, 'res]) => k(impl2) + + // * Version with full annotations (not necessary): + val use2: ArraysImplConsumer[A, 'res] -> 'res + val use2 = forall 'res: from.use of forall 'rr: (impl : ArraysImpl[A, 'rr]) => + val impl2 = StepImpl(impl) + (k: ArraysImplConsumer[A, 'res]) => k(impl2) + +} +//│ class StepAlt[A](from: Arrays[A]) extends Arrays { +//│ val use: forall 'res. (k: ArraysImplConsumer[A, 'res]) -> 'res +//│ val use2: forall 'res0. ArraysImplConsumer[A, 'res0] -> 'res0 +//│ } + + +// * A variation of the above without explicitly binding 'res, so it has to be distributed out + +:DistributeForalls // * Distributivity is needed here! + +class StepAlt[A](from: Arrays[A]) extends Arrays[A] { + val use = from.use of impl => + val impl2 = StepImpl(impl) + (k: ArraysImplConsumer[A, 'res]) => k(impl2) +} +//│ class StepAlt[A](from: Arrays[A]) extends Arrays { +//│ val use: forall 'res. (k: ArraysImplConsumer[A, 'res]) -> 'res +//│ } + + +// * Works the same: + +let ssb = StepAlt(StepAlt(Base())) +//│ let ssb: StepAlt['A] +//│ ssb +//│ = StepAlt {} + +ssb.use of impl => + let r = impl.update(impl.init(true), 1, false) + log(r) + [impl.sub(r, 0), impl.sub(r, 1)] +//│ [Bool, Bool] +//│ res +//│ = [ true, false ] +//│ // Output +//│ [ [ true, true ], [ false, true ] ] + +fun mkMonoArray(n) = + if n === 0 then Base() else StepAlt(mkMonoArray(n - 1)) +//│ fun mkMonoArray: forall 'A. (Eql[0] & Int) -> (forall 'A0. Base['A0] | StepAlt['A]) + +let snb = mkMonoArray(3) +//│ let snb: forall 'A 'A0. Base['A] | StepAlt['A0] +//│ snb +//│ = StepAlt {} + +snb.use of impl => + let r = impl.update(impl.init(true), 1, false) + log(r) +//│ () +//│ res +//│ = undefined +//│ // Output +//│ [ +//│ [ [ true, true ], [ true, true ] ], +//│ [ [ false, true ], [ true, true ] ] +//│ ] + +snb.use of impl => impl.init(true) +//│ true | ??rep +//│ res +//│ = [ +//│ [ [ true, true ], [ true, true ] ], +//│ [ [ true, true ], [ true, true ] ] +//│ ] + + diff --git a/shared/src/test/diff/fcp/Rank2.mls b/shared/src/test/diff/fcp/Rank2.mls index 47a99c11ed..ccc1c2e21d 100644 --- a/shared/src/test/diff/fcp/Rank2.mls +++ b/shared/src/test/diff/fcp/Rank2.mls @@ -53,7 +53,7 @@ test(fun x -> x + 1) //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.19: test(f: forall 'a. 'a -> 'a) = //│ ╙── ^^ -//│ res: (1, true,) | error +//│ res: error | (1, true,) //│ = [ 2, 2 ] :e @@ -67,7 +67,7 @@ test succ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.19: test(f: forall 'a. 'a -> 'a) = //│ ╙── ^^ -//│ res: (1, true,) | error +//│ res: error | (1, true,) //│ = [ 2, 2 ] @@ -111,7 +111,7 @@ test (fun x -> x + 1) //│ ╟── Note: constraint arises from reference: //│ ║ l.104: test (fun x -> x + 1) //│ ╙── ^ -//│ res: (1, 2,) | error +//│ res: error | (1, 2,) //│ = [ 1, 2 ] //│ // Output //│ 1 diff --git a/shared/src/test/diff/fcp/SelfAppMin.mls b/shared/src/test/diff/fcp/SelfAppMin.mls index f1ef416b82..79ab5315e7 100644 --- a/shared/src/test/diff/fcp/SelfAppMin.mls +++ b/shared/src/test/diff/fcp/SelfAppMin.mls @@ -21,7 +21,7 @@ e = eval eval //│ ║ l.19: e = eval eval //│ ║ ^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. -//│ e: (A & {f: A | L} | L) -> 0 | error +//│ e: error | (A & {f: A | L} | L) -> 0 //│ = [Function (anonymous)] e (A{f = L{}}) @@ -102,7 +102,7 @@ e = eval eval //│ ║ l.100: e = eval eval //│ ║ ^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. -//│ e: (A & {f: A | L} | L) -> (0 | {x: 0 | {x: nothing}}) | error +//│ e: error | (A & {f: A | L} | L) -> (0 | {x: 0 | {x: nothing}}) //│ = [Function (anonymous)] :re diff --git a/shared/src/test/diff/fcp/Skolems.mls b/shared/src/test/diff/fcp/Skolems.mls index e1f4550c73..f99a62e24e 100644 --- a/shared/src/test/diff/fcp/Skolems.mls +++ b/shared/src/test/diff/fcp/Skolems.mls @@ -27,7 +27,7 @@ fun (x: ('a -> 'a)) -> auto_ x //│ ╟── back into type variable `'a` //│ ║ l.5: def auto_ : (forall 'a. 'a -> 'a) -> 'b -> 'b //│ ╙── ^^ -//│ res: (('a | ??a) -> (??a0 & 'a)) -> ('b -> 'b | error) +//│ res: (('a | ??a) -> (??a0 & 'a)) -> (error | 'b -> 'b) //│ = [Function: res] foo(x: ('a -> 'b)) = auto_ x @@ -48,7 +48,7 @@ foo (fun x -> x) //│ ╟── Note: constraint arises from type variable: //│ ║ l.33: foo(x: ('a -> 'b)) = auto_ x //│ ╙── ^^ -//│ res: 'b -> 'b | error +//│ res: error | 'b -> 'b //│ = [Function (anonymous)] :e @@ -62,7 +62,7 @@ foo (fun x -> 0) //│ ╟── Note: constraint arises from type variable: //│ ║ l.33: foo(x: ('a -> 'b)) = auto_ x //│ ╙── ^^ -//│ res: 'b -> 'b | error +//│ res: error | 'b -> 'b //│ = 0 :e @@ -91,7 +91,7 @@ foo (fun x -> foo (fun y -> y) x) //│ ╟── Note: constraint arises from type variable: //│ ║ l.33: foo(x: ('a -> 'b)) = auto_ x //│ ╙── ^^ -//│ res: 'b -> 'b | error +//│ res: error | 'b -> 'b //│ = [Function (anonymous)] // * This would be unsound; `fun y -> x` does not have type `forall 'a. 'a -> 'a` but it is eventually passed to auto_ @@ -109,7 +109,7 @@ foo (fun x -> foo (fun y -> x) x) //│ ╟── Note: constraint arises from reference: //│ ║ l.99: foo (fun x -> foo (fun y -> x) x) //│ ╙── ^ -//│ res: 'b -> 'b | error +//│ res: error | 'b -> 'b //│ Runtime error: //│ RangeError: Maximum call stack size exceeded @@ -141,7 +141,7 @@ f = extrude (fun x -> extrude (fun y -> x) x) //│ ╟── Note: constraint arises from reference: //│ ║ l.131: f = extrude (fun x -> extrude (fun y -> x) x) //│ ╙── ^ -//│ f: 'a -> 'a | error +//│ f: error | 'a -> 'a //│ = [Function (anonymous)] :re @@ -153,13 +153,13 @@ f 42 // Boom! def swapWith: (forall 'x 'y. (('x, 'y),) -> ('y, 'x)) -> ('a, 'b) -> ('b, 'a) -//│ swapWith: (forall 'y 'x. ('x, 'y,) -> ('y, 'x,)) -> ('a, 'b,) -> ('b, 'a,) +//│ swapWith: (forall 'x 'y. (('x, 'y,),) -> ('y, 'x,)) -> ('a, 'b,) -> ('b, 'a,) //│ = def swapWith f (a, b) = f ((a, b)) -//│ (('a, 'b,) -> 'c) -> ('a, 'b,) -> 'c +//│ ((('a, 'b,),) -> 'c) -> ('a, 'b,) -> 'c //│ <: swapWith: -//│ (forall 'x 'y. ('x, 'y,) -> ('y, 'x,)) -> ('a, 'b,) -> ('b, 'a,) +//│ (forall 'x 'y. (('x, 'y,),) -> ('y, 'x,)) -> ('a, 'b,) -> ('b, 'a,) //│ = [Function: swapWith] @@ -175,15 +175,15 @@ fun (x: ('a -> 'a)) -> swapWith x //│ ╟── back into type variable `'y` //│ ║ l.155: def swapWith: (forall 'x 'y. (('x, 'y),) -> ('y, 'x)) -> ('a, 'b) -> ('b, 'a) //│ ╙── ^^ -//│ res: (('a | (??x, ??y,)) -> ((??y0, ??x0,) & 'a)) -> (('a0, 'b,) -> ('b, 'a0,) | error) +//│ res: (('a | (??x, ??y,)) -> ((??y0, ??x0,) & 'a)) -> (error | ('a0, 'b,) -> ('b, 'a0,)) //│ = [Function: res] foo = fun (x: ('a -> 'b)) -> swapWith x -//│ foo: ((??x, ??y,) -> (??y0, ??x0,)) -> ('a, 'b,) -> ('b, 'a,) +//│ foo: (((??x, ??y,),) -> (??y0, ??x0,)) -> ('a, 'b,) -> ('b, 'a,) //│ = [Function: foo1] bar = fun f -> foo f (1, 2) -//│ bar: ((??x, ??y,) -> (??y0, ??x0,)) -> (2, 1,) +//│ bar: (((??x, ??y,),) -> (??y0, ??x0,)) -> (2, 1,) //│ = [Function: bar] :e @@ -197,7 +197,7 @@ bar (fun ((u, v),) -> (v, u)) //│ ╟── back into type variable `'y` //│ ║ l.155: def swapWith: (forall 'x 'y. (('x, 'y),) -> ('y, 'x)) -> ('a, 'b) -> ('b, 'a) //│ ╙── ^^ -//│ res: (2, 1,) | error +//│ res: error | (2, 1,) //│ = [ 2, 1 ] :e @@ -220,7 +220,7 @@ bar (fun ((u1, v1),) -> let tmp = bar (fun ((u2, v2),) -> (v2, u2)) in (v1, u1)) //│ ╟── back into type variable `'y` //│ ║ l.155: def swapWith: (forall 'x 'y. (('x, 'y),) -> ('y, 'x)) -> ('a, 'b) -> ('b, 'a) //│ ╙── ^^ -//│ res: (2, 1,) | error +//│ res: error | (2, 1,) //│ = [ 2, 1 ] :e // * Not sure why this one is rejected (but the `extrude` version above is accepted.) @@ -252,11 +252,11 @@ fun (x: ((('a, 'b),) -> ('b, 'a))) -> swapWith x //│ ╟── back into type variable `'y` //│ ║ l.155: def swapWith: (forall 'x 'y. (('x, 'y),) -> ('y, 'x)) -> ('a, 'b) -> ('b, 'a) //│ ╙── ^^ -//│ res: (('a | ??x, 'b | ??y,) -> (??y0 & 'b, ??x0 & 'a,)) -> (('a0, 'b0,) -> ('b0, 'a0,) | error) +//│ res: ((('a | ??x, 'b | ??y,),) -> (??y0 & 'b, ??x0 & 'a,)) -> (error | ('a0, 'b0,) -> ('b0, 'a0,)) //│ = [Function: res] fun (x: ((('a, 'b),) -> ('c, 'd))) -> swapWith x -//│ res: ((??x, ??y,) -> (??y0, ??x0,)) -> ('a, 'b,) -> ('b, 'a,) +//│ res: (((??x, ??y,),) -> (??y0, ??x0,)) -> ('a, 'b,) -> ('b, 'a,) //│ = [Function: res] diff --git a/shared/src/test/diff/fcp/Skolems2.mls b/shared/src/test/diff/fcp/Skolems2.mls index 97dbe02e2f..fa15cc0c10 100644 --- a/shared/src/test/diff/fcp/Skolems2.mls +++ b/shared/src/test/diff/fcp/Skolems2.mls @@ -53,7 +53,7 @@ extrude(id) //│ ╟── • this reference: //│ ║ l.19: def extrude f = oops((f, 0)) //│ ╙── ^^^^ -//│ res: 'c -> 'c | error +//│ res: error | 'c -> 'c //│ = [Function: id] //│ // Output //│ Hi! @@ -177,7 +177,7 @@ f = extrude (fun x -> extrude (fun y -> x) x) //│ ╟── Note: constraint arises from reference: //│ ║ l.145: f = extrude (fun x -> extrude (fun y -> x) x) //│ ╙── ^ -//│ f: 'c -> 'c | error +//│ f: error | 'c -> 'c //│ = [Function (anonymous)] //│ // Output //│ Hi! @@ -198,7 +198,7 @@ f 42 //│ [Function (anonymous)] // * Note: parser parses this the same as `oops((f, 0)._1)` -def extrude f = oops((f, 0))._1 +def extrude f = oops((f, 0)).0 //│ extrude: (forall 'c. ('c -> 'c, 0,)) -> 'c0 -> 'c0 //│ = [Function: extrude1] @@ -209,8 +209,8 @@ def extrude f = oops((f, 0))._1 def oops (i: forall 'c. ('c -> 'c, 0)) = - let _ = (i._1 id) "hello" - in i._1 + let _ = (i.0 id) "hello" + in i.0 //│ oops: (forall 'c. ('c -> 'c, 0,)) -> 'c0 -> 'c0 //│ = [Function: oops1] @@ -234,7 +234,7 @@ def foo(a, b) = (a, b) : forall 'c. ('c, 'c) foo(error, error) //│ res: (nothing, nothing,) //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown def foo(a, b) = (a, b) : forall 'c. ('c, 'c -> 'c) //│ foo: (??c, ??c0 -> ??c,) -> (forall 'c. ('c, 'c -> 'c,)) @@ -255,7 +255,7 @@ foo(id, id) //│ ╟── back into type variable `'c` //│ ║ l.243: def foo(a, b) = (a, b) : forall 'c. ('c -> 'c, 'c -> 'c) //│ ╙── ^^ -//│ res: ('c -> 'c, 'c -> 'c,) | error +//│ res: error | ('c -> 'c, 'c -> 'c,) //│ = [ [Function: id], [Function: id] ] @@ -277,7 +277,7 @@ foo(id) //│ ╟── Note: constraint arises from application: //│ ║ l.262: def foo(f) = (fun a -> f a) : forall 'c. 'c -> 'c //│ ╙── ^^^ -//│ res: 'c -> 'c | error +//│ res: error | 'c -> 'c //│ = [Function (anonymous)] @@ -296,7 +296,7 @@ foo(id, 0) //│ ╟── back into type variable `'c` //│ ║ l.284: def foo(a, b) = let tmp = (a, b) : forall 'c. ('c -> 'c, 0) in a //│ ╙── ^^ -//│ res: 'a -> 'a | error +//│ res: error | 'a -> 'a //│ = [Function: id] :e @@ -313,7 +313,7 @@ foo(fun x -> foo(fun y -> x, 0) x, 0) //│ ╟── Note: constraint arises from reference: //│ ║ l.303: foo(fun x -> foo(fun y -> x, 0) x, 0) //│ ╙── ^ -//│ res: (??c & 'a) -> 'a | error +//│ res: error | (??c & 'a) -> 'a //│ = [Function (anonymous)] @@ -343,7 +343,7 @@ foo((fun x -> foo((fun y -> x, 0)), 0)) //│ ╟── type variable `'c` leaks out of its scope //│ ║ l.321: def foo((a, b) : forall 'c. ('c -> 'c, 0)) = () //│ ╙── ^^ -//│ res: () | error +//│ res: error | () //│ = [] diff --git a/shared/src/test/diff/fcp/SystemF.mls b/shared/src/test/diff/fcp/SystemF.mls index 72e3f9c073..903f7b261a 100644 --- a/shared/src/test/diff/fcp/SystemF.mls +++ b/shared/src/test/diff/fcp/SystemF.mls @@ -287,15 +287,15 @@ sp_ty = sp //│ = [Function (anonymous)] p01 = pair (()) Z -//│ p01: (() -> (forall 'a. anything -> 'a -> 'a) -> 'b) -> 'b +//│ p01: (((),) -> (forall 'a. anything -> 'a -> 'a) -> 'b) -> 'b //│ = [Function (anonymous)] p = pair (()) -//│ p: 'a -> (() -> 'a -> 'b) -> 'b +//│ p: 'a -> (((),) -> 'a -> 'b) -> 'b //│ = [Function (anonymous)] p01 = p Z -//│ p01: (() -> (forall 'a. anything -> 'a -> 'a) -> 'b) -> 'b +//│ p01: (((),) -> (forall 'a. anything -> 'a -> 'a) -> 'b) -> 'b //│ = [Function (anonymous)] @@ -399,7 +399,7 @@ E q = q id // shallow (not (not true)) -- Shallow encoding. // λid.id(λb t f.b f t)(id(λb t f.b f t)(λx y.x)) sh id = (id not) ((id not) tru) -//│ sh: ((forall 'a 'b 'c. ('a -> 'b -> 'c) -> 'b -> 'a -> 'c) -> ('d -> 'e & (forall 'f. 'f -> anything -> 'f) -> 'd)) -> 'e +//│ sh: ((forall 'a 'b 'c. ('b -> 'c -> 'a) -> 'c -> 'b -> 'a) -> ('d -> 'e & (forall 'f. 'f -> anything -> 'f) -> 'd)) -> 'e //│ = [Function: sh] // E[forall X.X->X->X](shallow (not( not true))) @@ -423,7 +423,7 @@ pu sh_pu //│ ║ l.421: pu sh_pu //│ ║ ^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. -//│ res: anything -> anything -> anything -> nothing | error +//│ res: error | anything -> anything -> anything -> nothing //│ = [Function (anonymous)] // * "Of course, self-application of self-application ((\x.x x)(\x.x x)) remains untypable, because it has no normal form." diff --git a/shared/src/test/diff/fcp/SystemF_2.mls b/shared/src/test/diff/fcp/SystemF_2.mls index d164841893..7246a3e9b2 100644 --- a/shared/src/test/diff/fcp/SystemF_2.mls +++ b/shared/src/test/diff/fcp/SystemF_2.mls @@ -40,10 +40,10 @@ iter2 iter2 K //│ where //│ 'a :> ? -> ? -> 'a //│ ╙── -//│ res: 'a -> 'b +//│ res: 'a -> anything -> 'b //│ where -//│ 'a :> 'b -//│ 'b :> anything -> anything -> 'a +//│ 'a :> anything -> 'b +//│ 'b :> anything -> 'a //│ = [Function (anonymous)] iter2 iter2 iter2 @@ -128,7 +128,7 @@ c2_ c2_ K //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> ? -> ('a | 'b | 'c | 'd) -//│ 'c :> ? -> ('b | 'd) | 'd +//│ 'c :> ? -> ('b | 'c | 'd) //│ 'd :> ? -> ('b | 'c) //│ 'b :> ? -> ('a | 'b) //│ ╙── @@ -154,10 +154,10 @@ c2__ c2__ K //│ where //│ 'a :> ? -> ? -> 'a //│ ╙── -//│ res: 'a -> 'b +//│ res: 'a -> anything -> 'b //│ where -//│ 'a :> 'b -//│ 'b :> anything -> anything -> 'a +//│ 'a :> anything -> 'b +//│ 'b :> anything -> 'a //│ = [Function (anonymous)] @@ -165,36 +165,36 @@ c2__ c2__ K :RecursiveTypes iter2 iter2 K -//│ res: 'a -> 'b +//│ res: 'a -> anything -> 'b //│ where -//│ 'a :> 'b -//│ 'b :> anything -> anything -> 'a +//│ 'a :> anything -> 'b +//│ 'b :> anything -> 'a //│ = [Function (anonymous)] res id -//│ res: 'a +//│ res: anything -> 'a //│ where -//│ 'a :> forall 'b. anything -> anything -> ('b -> 'b | 'a) +//│ 'a :> forall 'b. anything -> 'b -> ('a | 'b) //│ = [Function (anonymous)] r1 = res id id id id id -//│ r1: 'a -> 'a | 'b +//│ r1: 'a -> ('b | 'a) //│ where -//│ 'b :> forall 'c. anything -> 'c -> ('b | 'c) +//│ 'b :> forall 'c. 'c -> (anything -> 'b | 'c) //│ = [Function: id] r1 iter2 iter2 K -//│ res: ('a & 'b & 'c) -> (anything -> anything -> 'b | 'd | 'c | 'e) +//│ res: ('a & 'b & 'c) -> (anything -> (anything -> 'b | 'd) | 'e | 'c) //│ where -//│ 'd :> forall 'f. anything -> 'f -> ('d | 'f) -//│ 'a :> 'e -//│ 'e :> anything -> anything -> 'a +//│ 'e :> forall 'f. anything -> 'f -> ('e | 'f) +//│ 'a :> anything -> 'd +//│ 'd :> anything -> 'a //│ = [Function (anonymous)] r = r1 iter2 iter2 -//│ r: ('a -> 'b & 'b -> 'c & 'd -> 'e & 'e -> ('d & 'f)) -> ('a & 'd) -> ('c | 'f) | 'g +//│ r: ('a -> 'b & 'b -> 'c & 'd -> 'e & 'e -> ('d & 'f)) -> (('a & 'd) -> ('c | 'f) | 'g) //│ where -//│ 'g :> forall 'h. anything -> 'h -> ('g | 'h) +//│ 'g :> forall 'h. 'h -> (anything -> 'g | 'h) //│ = [Function (anonymous)] r iter2 @@ -221,43 +221,43 @@ id x = x iter2 f x = f(f x) //│ id: 'a -> 'a //│ = [Function: id1] -//│ iter2: 'a -> (forall 'b 'c 'd. ('b -> 'd +//│ iter2: 'a -> (forall 'b 'c 'd. 'b -> 'd //│ where -//│ 'a <: 'b -> 'c & 'c -> 'd)) +//│ 'a <: 'b -> 'c & 'c -> 'd) //│ = [Function: iter21] iter2 iter2 //│ res: 'a -> 'b //│ where -//│ forall 'c. 'c -> (forall 'd 'e 'f. ('f -> 'e -//│ where -//│ 'c <: 'f -> 'd & 'd -> 'e)) <: 'a -> 'g & 'g -> 'b +//│ forall 'c. 'c -> (forall 'd 'e 'f. 'e -> 'd +//│ where +//│ 'c <: 'e -> 'f & 'f -> 'd) <: 'a -> 'g & 'g -> 'b //│ = [Function (anonymous)] id iter2 iter2 //│ res: 'a -> 'b //│ where -//│ forall 'c. 'c -> (forall 'd 'e 'f. ('d -> 'f -//│ where -//│ 'c <: 'd -> 'e & 'e -> 'f)) <: 'a -> 'g & 'g -> 'b +//│ forall 'c. 'c -> (forall 'd 'e 'f. 'd -> 'f +//│ where +//│ 'c <: 'd -> 'e & 'e -> 'f) <: 'a -> 'g & 'g -> 'b //│ = [Function (anonymous)] iter2 iter2 K //│ res: 'a -> 'b //│ where -//│ forall 'c 'd 'e. ('c -> 'e -//│ where -//│ forall 'f. 'f -> anything -> 'f <: 'c -> 'd & 'd -> 'e) <: 'a -> 'g & 'g -> 'b +//│ forall 'c 'd 'e. 'c -> 'e +//│ where +//│ forall 'f. 'f -> anything -> 'f <: 'c -> 'd & 'd -> 'e <: 'a -> 'g & 'g -> 'b //│ = [Function (anonymous)] // (λzy. y(zI)(zK))(λx. xx). foo = (fun z -> fun y -> y (z I) (z K)) -//│ foo: 'a -> (forall 'b 'c 'd. (('b -> 'c -> 'd) -> 'd +//│ foo: 'a -> (forall 'b 'c 'd. ('b -> 'c -> 'd) -> 'd //│ where -//│ 'a <: (forall 'e. 'e -> 'e) -> 'b & (forall 'f. 'f -> anything -> 'f) -> 'c)) +//│ 'a <: (forall 'e. 'e -> 'e) -> 'b & (forall 'f. 'f -> anything -> 'f) -> 'c) //│ = [Function: foo1] foo (fun x -> x x) @@ -280,19 +280,19 @@ n0 = n0_ succ_ n s z = s (n s z) def succ: (forall 'X. ('X -> 'X) -> 'X -> 'X) -> (forall 'X. ('X -> 'X) -> 'X -> 'X) -//│ succ_: 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. ('e -> 'd +//│ succ_: 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where -//│ 'b <: 'c -> 'd -//│ 'a <: 'b -> 'e -> 'c))) +//│ 'a <: 'b -> 'c -> 'd +//│ 'b <: 'd -> 'e)) //│ = [Function: succ_1] //│ succ: (forall 'X. ('X -> 'X) -> 'X -> 'X) -> (forall 'X0. ('X0 -> 'X0) -> 'X0 -> 'X0) //│ = :e // * Needs distrib succ = succ_ -//│ 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. ('e -> 'd +//│ 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where -//│ 'b <: 'c -> 'd -//│ 'a <: 'b -> 'e -> 'c))) +//│ 'b <: 'd -> 'e +//│ 'a <: 'b -> 'c -> 'd)) //│ <: succ: //│ (forall 'X. ('X -> 'X) -> 'X -> 'X) -> (forall 'X0. ('X0 -> 'X0) -> 'X0 -> 'X0) //│ ╔══[ERROR] Type error in def definition @@ -332,130 +332,130 @@ c2 c2 K //│ = [Function (anonymous)] c2_ = succ_ (succ_ n0) -//│ c2_: 'a -> (forall 'b 'c 'd. ('b -> 'd -//│ where -//│ 'a <: 'c -> 'd -//│ forall 'e. 'e -> (forall 'f 'g 'h. ('h -> 'g +//│ c2_: 'a -> (forall 'b 'c 'd. 'b -> 'd //│ where -//│ 'e <: 'f -> 'g -//│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'e -> 'h -> 'f)) <: 'a -> 'b -> 'c)) +//│ forall 'e. 'e -> (forall 'f 'g 'h. 'h -> 'g +//│ where +//│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'e -> 'h -> 'f +//│ 'e <: 'f -> 'g) <: 'a -> 'b -> 'c +//│ 'a <: 'c -> 'd) //│ = [Function (anonymous)] c2_ c2_ //│ res: 'a -> 'b //│ where -//│ forall 'c. 'c -> (forall 'd 'e 'f. ('f -> 'e -//│ where -//│ forall 'g. 'g -> (forall 'h 'i 'j. ('j -> 'i -//│ where -//│ 'g <: 'h -> 'i -//│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'g -> 'j -> 'h)) <: 'c -> 'f -> 'd -//│ 'c <: 'd -> 'e)) <: 'k -> 'b -//│ forall 'l. 'l -> (forall 'm 'n 'o. ('o -> 'n -//│ where -//│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'l -> 'o -> 'm -//│ 'l <: 'm -> 'n)) <: (forall 'c. 'c -> (forall 'd 'e 'f. ('f -> 'e -//│ where -//│ forall 'g. 'g -> (forall 'h 'i 'j. ('j -> 'i -//│ where -//│ 'g <: 'h -> 'i -//│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'g -> 'j -> 'h)) <: 'c -> 'f -> 'd -//│ 'c <: 'd -> 'e))) -> 'a -> 'k +//│ forall 'c. 'c -> (forall 'd 'e 'f. 'd -> 'f +//│ where +//│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'c -> 'd -> 'e +//│ 'c <: 'e -> 'f) <: (forall 'g. 'g -> (forall 'h 'i 'j. 'h -> 'j +//│ where +//│ 'g <: 'i -> 'j +//│ forall 'k. 'k -> (forall 'l 'm 'n. 'l -> 'n +//│ where +//│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'k -> 'l -> 'm +//│ 'k <: 'm -> 'n) <: 'g -> 'h -> 'i)) -> 'a -> 'o +//│ forall 'g. 'g -> (forall 'h 'i 'j. 'h -> 'j +//│ where +//│ 'g <: 'i -> 'j +//│ forall 'k. 'k -> (forall 'l 'm 'n. 'l -> 'n +//│ where +//│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'k -> 'l -> 'm +//│ 'k <: 'm -> 'n) <: 'g -> 'h -> 'i) <: 'o -> 'b //│ = [Function (anonymous)] :e c2_ c2_ K //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a :> forall 'b 'c 'd 'e. (('b & 'c) -> (? -> 'c | 'e) -//│ where -//│ 'a <: 'd -> 'e -//│ forall 'f. 'f -> (forall 'g 'h 'i. ('i -> 'h -//│ where -//│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'f -> 'i -> 'g -//│ 'f <: 'g -> 'h)) <: 'a -> 'b -> 'd) +//│ 'a :> forall 'b 'c 'd 'e. ('b & 'e) -> (? -> 'e | 'd) +//│ where +//│ 'a <: 'c -> 'd +//│ forall 'f. 'f -> (forall 'g 'h 'i. 'i -> 'h +//│ where +//│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'f -> 'i -> 'g +//│ 'f <: 'g -> 'h) <: 'a -> 'b -> 'c //│ ╙── //│ res: 'a -> 'b //│ where -//│ forall 'c 'd 'e. ('e -> 'd -//│ where -//│ forall 'f. 'f -> (forall 'g 'h 'i. ('i -> 'h -//│ where -//│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'f -> 'i -> 'g -//│ 'f <: 'g -> 'h)) <: 'j -> 'e -> 'c -//│ 'j <: 'c -> 'd) <: 'k -> 'b -//│ forall 'l. 'l -> (forall 'm 'n 'o. ('o -> 'n -//│ where -//│ 'l <: 'm -> 'n -//│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'l -> 'o -> 'm)) <: (forall 'c 'd 'e. ('e -> 'd -//│ where -//│ forall 'f. 'f -> (forall 'g 'h 'i. ('i -> 'h -//│ where -//│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'f -> 'i -> 'g -//│ 'f <: 'g -> 'h)) <: 'j -> 'e -> 'c -//│ 'j <: 'c -> 'd)) -> 'a -> 'k -//│ where -//│ 'j :> forall 'p 'q 'r 's. (('p & 'q) -> (anything -> 'q | 's) -//│ where -//│ 'j <: 'r -> 's -//│ forall 't. 't -> (forall 'u 'v 'w. ('w -> 'v -//│ where -//│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 't -> 'w -> 'u -//│ 't <: 'u -> 'v)) <: 'j -> 'p -> 'r) +//│ forall 'c. 'c -> (forall 'd 'e 'f. 'd -> 'f +//│ where +//│ 'c <: 'e -> 'f +//│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'c -> 'd -> 'e) <: (forall 'g 'h 'i. 'g -> 'i +//│ where +//│ forall 'j. 'j -> (forall 'k 'l 'm. 'k -> 'm +//│ where +//│ 'j <: 'l -> 'm +//│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'j -> 'k -> 'l) <: 'n -> 'g -> 'h +//│ 'n <: 'h -> 'i) -> 'a -> 'o +//│ forall 'g 'h 'i. 'g -> 'i +//│ where +//│ forall 'j. 'j -> (forall 'k 'l 'm. 'k -> 'm +//│ where +//│ 'j <: 'l -> 'm +//│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'j -> 'k -> 'l) <: 'n -> 'g -> 'h +//│ 'n <: 'h -> 'i <: 'o -> 'b +//│ where +//│ 'n :> forall 'p 'q 'r 's. ('p & 's) -> (anything -> 's | 'r) +//│ where +//│ forall 't. 't -> (forall 'u 'v 'w. 'u -> 'w +//│ where +//│ 't <: 'v -> 'w +//│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 't -> 'u -> 'v) <: 'n -> 'p -> 'q +//│ 'n <: 'q -> 'r //│ = [Function (anonymous)] c2__ = succ_ (succ_ n0_) -//│ c2__: 'a -> (forall 'b 'c 'd. ('b -> 'd -//│ where -//│ 'a <: 'c -> 'd -//│ forall 'e. 'e -> (forall 'f 'g 'h. ('h -> 'g +//│ c2__: 'a -> (forall 'b 'c 'd. 'b -> 'd //│ where -//│ anything -> (forall 'i. 'i -> 'i) <: 'e -> 'h -> 'f -//│ 'e <: 'f -> 'g)) <: 'a -> 'b -> 'c)) +//│ forall 'e. 'e -> (forall 'f 'g 'h. 'h -> 'g +//│ where +//│ anything -> (forall 'i. 'i -> 'i) <: 'e -> 'h -> 'f +//│ 'e <: 'f -> 'g) <: 'a -> 'b -> 'c +//│ 'a <: 'c -> 'd) //│ = [Function (anonymous)] c2__ c2__ //│ res: 'a -> 'b //│ where -//│ forall 'c. 'c -> (forall 'd 'e 'f. ('f -> 'e -//│ where -//│ anything -> (forall 'g. 'g -> 'g) <: 'c -> 'f -> 'd -//│ 'c <: 'd -> 'e)) <: (forall 'h. 'h -> (forall 'i 'j 'k. ('k -> 'j -//│ where -//│ 'h <: 'i -> 'j -//│ forall 'l. 'l -> (forall 'm 'n 'o. ('o -> 'n -//│ where -//│ 'l <: 'm -> 'n -//│ anything -> (forall 'g. 'g -> 'g) <: 'l -> 'o -> 'm)) <: 'h -> 'k -> 'i))) -> 'a -> 'p -//│ forall 'h. 'h -> (forall 'i 'j 'k. ('k -> 'j -//│ where -//│ 'h <: 'i -> 'j -//│ forall 'l. 'l -> (forall 'm 'n 'o. ('o -> 'n -//│ where -//│ 'l <: 'm -> 'n -//│ anything -> (forall 'g. 'g -> 'g) <: 'l -> 'o -> 'm)) <: 'h -> 'k -> 'i)) <: 'p -> 'b +//│ forall 'c. 'c -> (forall 'd 'e 'f. 'e -> 'd +//│ where +//│ 'c <: 'f -> 'd +//│ forall 'g. 'g -> (forall 'h 'i 'j. 'h -> 'j +//│ where +//│ anything -> (forall 'k. 'k -> 'k) <: 'g -> 'h -> 'i +//│ 'g <: 'i -> 'j) <: 'c -> 'e -> 'f) <: 'l -> 'b +//│ forall 'm. 'm -> (forall 'n 'o 'p. 'n -> 'p +//│ where +//│ 'm <: 'o -> 'p +//│ anything -> (forall 'k. 'k -> 'k) <: 'm -> 'n -> 'o) <: (forall 'c. 'c -> (forall 'd 'e 'f. 'e -> 'd +//│ where +//│ 'c <: 'f -> 'd +//│ forall 'g. 'g -> (forall 'h 'i 'j. 'h -> 'j +//│ where +//│ anything -> (forall 'k. 'k -> 'k) <: 'g -> 'h -> 'i +//│ 'g <: 'i -> 'j) <: 'c -> 'e -> 'f)) -> 'a -> 'l //│ = [Function (anonymous)] c2__ c2__ K //│ res: 'a -> 'b //│ where -//│ forall 'c 'd 'e. ('c -> 'e -//│ where -//│ forall 'f. 'f -> anything -> 'f <: 'd -> 'e -//│ forall 'g. 'g -> (forall 'h 'i 'j. ('h -> 'j -//│ where -//│ anything -> (forall 'k. 'k -> 'k) <: 'g -> 'h -> 'i -//│ 'g <: 'i -> 'j)) <: (forall 'f. 'f -> anything -> 'f) -> 'c -> 'd) <: 'l -> 'b -//│ forall 'm. 'm -> (forall 'n 'o 'p. ('p -> 'o -//│ where -//│ 'm <: 'n -> 'o -//│ anything -> (forall 'k. 'k -> 'k) <: 'm -> 'p -> 'n)) <: (forall 'c 'd 'e. ('c -> 'e -//│ where -//│ forall 'f. 'f -> anything -> 'f <: 'd -> 'e -//│ forall 'g. 'g -> (forall 'h 'i 'j. ('h -> 'j -//│ where -//│ anything -> (forall 'k. 'k -> 'k) <: 'g -> 'h -> 'i -//│ 'g <: 'i -> 'j)) <: (forall 'f. 'f -> anything -> 'f) -> 'c -> 'd)) -> 'a -> 'l +//│ forall 'c. 'c -> (forall 'd 'e 'f. 'd -> 'f +//│ where +//│ 'c <: 'e -> 'f +//│ anything -> (forall 'g. 'g -> 'g) <: 'c -> 'd -> 'e) <: (forall 'h 'i 'j. 'h -> 'j +//│ where +//│ forall 'k. 'k -> (forall 'l 'm 'n. 'm -> 'l +//│ where +//│ 'k <: 'n -> 'l +//│ anything -> (forall 'g. 'g -> 'g) <: 'k -> 'm -> 'n) <: (forall 'o. 'o -> anything -> 'o) -> 'h -> 'i +//│ forall 'o. 'o -> anything -> 'o <: 'i -> 'j) -> 'a -> 'p +//│ forall 'h 'i 'j. 'h -> 'j +//│ where +//│ forall 'k. 'k -> (forall 'l 'm 'n. 'm -> 'l +//│ where +//│ 'k <: 'n -> 'l +//│ anything -> (forall 'g. 'g -> 'g) <: 'k -> 'm -> 'n) <: (forall 'o. 'o -> anything -> 'o) -> 'h -> 'i +//│ forall 'o. 'o -> anything -> 'o <: 'i -> 'j <: 'p -> 'b //│ = [Function (anonymous)] diff --git a/shared/src/test/diff/fcp/ToChurchSimplif_CT.mls b/shared/src/test/diff/fcp/ToChurchSimplif_CT.mls index d4d02e7787..5bfa6a504e 100644 --- a/shared/src/test/diff/fcp/ToChurchSimplif_CT.mls +++ b/shared/src/test/diff/fcp/ToChurchSimplif_CT.mls @@ -32,25 +32,25 @@ def z f x = x // def s n f = f (n f) def s n f x = (n f x) // def s n f = n f -//│ s: 'a -> (forall 'b. 'b -> (forall 'c 'd. ('c -> 'd +//│ s: 'a -> (forall 'b. 'b -> (forall 'c 'd. 'c -> 'd //│ where -//│ 'a <: 'b -> 'c -> 'd))) +//│ 'a <: 'b -> 'c -> 'd)) //│ = [Function: s] :ns s -//│ res: forall 'a. 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. ('c -> 'e +//│ res: forall 'a. 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where -//│ 'a <: 'b -> 'd))) -//│ where -//│ 'd <: 'c -> 'e +//│ 'a <: 'b -> 'd)) +//│ where +//│ 'd <: 'c -> 'e //│ = [Function: s] :e // * Since "sound extrusion" succ = s -//│ 'a -> (forall 'b. 'b -> (forall 'c 'd. ('c -> 'd +//│ 'a -> (forall 'b. 'b -> (forall 'c 'd. 'c -> 'd //│ where -//│ 'a <: 'b -> 'c -> 'd))) +//│ 'a <: 'b -> 'c -> 'd)) //│ <: succ: //│ ChurchInt -> ChurchInt //│ ╔══[ERROR] Type error in def definition @@ -73,9 +73,9 @@ succ = s :e // * Since "sound extrusion" succ n f = n f -//│ 'a -> (forall 'b 'c. ('b -> 'c +//│ 'a -> (forall 'b 'c. 'c -> 'b //│ where -//│ 'a <: 'b -> 'c)) +//│ 'a <: 'c -> 'b) //│ <: succ: //│ ChurchInt -> ChurchInt //│ ╔══[ERROR] Type error in def definition @@ -105,9 +105,9 @@ def succ_min : (forall 'N. ('N -> 'N)) -> (forall 'M. ('M -> 'M)) :e // * Since "sound extrusion" succ_min n f = n f -//│ 'a -> (forall 'b 'c. ('b -> 'c +//│ 'a -> (forall 'b 'c. 'b -> 'c //│ where -//│ 'a <: 'b -> 'c)) +//│ 'a <: 'b -> 'c) //│ <: succ_min: //│ (forall 'N. 'N -> 'N) -> (forall 'M. 'M -> 'M) //│ ╔══[ERROR] Type error in def definition @@ -136,15 +136,15 @@ rec def to_ch n = else s (to_ch n) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a :> forall 'b. 'b -> (forall 'c 'd. ('c -> 'd -//│ where -//│ 'a <: 'b -> 'c -> 'd)) | ChurchInt +//│ 'a :> forall 'b. 'b -> (forall 'c 'd. 'c -> 'd +//│ where +//│ 'a <: 'b -> 'c -> 'd) | ChurchInt //│ ╙── //│ to_ch: number -> 'a //│ where -//│ 'a :> forall 'b. 'b -> (forall 'c 'd. ('c -> 'd -//│ where -//│ 'a <: 'b -> 'c -> 'd)) | ChurchInt +//│ 'a :> forall 'b. 'b -> (forall 'c 'd. 'd -> 'c +//│ where +//│ 'a <: 'b -> 'd -> 'c) | ChurchInt //│ = //│ zero is not implemented @@ -152,18 +152,18 @@ rec def to_ch n = to_church = to_ch //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a :> forall 'b. 'b -> (forall 'c 'd. ('c -> 'd -//│ where -//│ 'a <: 'b -> 'c -> 'd)) | ChurchInt +//│ 'a :> forall 'b. 'b -> (forall 'c 'd. 'c -> 'd +//│ where +//│ 'a <: 'b -> 'c -> 'd) | ChurchInt //│ ╙── //│ number -> 'a //│ where -//│ 'a :> forall 'b. 'b -> (forall 'c 'd. ('c -> 'd -//│ where -//│ 'a <: 'b -> 'c -> 'd)) | ChurchInt +//│ 'a :> forall 'b. 'b -> (forall 'c 'd. 'c -> 'd +//│ where +//│ 'a <: 'b -> 'c -> 'd) | ChurchInt //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) -//│ ╔══[ERROR] Subtyping constraint of the form `forall ?a ?b ?to_ch ?c ?d. ?to_ch <: int -> (forall 'M. ('M -> 'M) -> 'M -> 'M)` exceeded recursion depth limit (250) +//│ ╔══[ERROR] Subtyping constraint of the form `forall ?to_ch. ?to_ch <: int -> (forall 'M. ('M -> 'M) -> 'M -> 'M)` exceeded recursion depth limit (250) //│ ║ l.152: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. @@ -177,15 +177,15 @@ rec def to_ch n = else s (to_ch n) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a :> forall 'b. 'b -> (forall 'c 'd. ('c -> 'd -//│ where -//│ 'a <: 'b -> 'c -> 'd)) | ChurchInt +//│ 'a :> forall 'b. 'b -> (forall 'c 'd. 'c -> 'd +//│ where +//│ 'a <: 'b -> 'c -> 'd) | ChurchInt //│ ╙── //│ to_ch: anything -> 'a //│ where -//│ 'a :> forall 'b. 'b -> (forall 'c 'd. ('c -> 'd -//│ where -//│ 'a <: 'b -> 'c -> 'd)) | ChurchInt +//│ 'a :> forall 'b. 'b -> (forall 'c 'd. 'c -> 'd +//│ where +//│ 'a <: 'b -> 'c -> 'd) | ChurchInt //│ = //│ zero is not implemented @@ -193,18 +193,18 @@ rec def to_ch n = to_church = to_ch //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a :> forall 'b. 'b -> (forall 'c 'd. ('c -> 'd -//│ where -//│ 'a <: 'b -> 'c -> 'd)) | ChurchInt +//│ 'a :> forall 'b. 'b -> (forall 'c 'd. 'c -> 'd +//│ where +//│ 'a <: 'b -> 'c -> 'd) | ChurchInt //│ ╙── //│ anything -> 'a //│ where -//│ 'a :> forall 'b. 'b -> (forall 'c 'd. ('c -> 'd -//│ where -//│ 'a <: 'b -> 'c -> 'd)) | ChurchInt +//│ 'a :> forall 'b. 'b -> (forall 'c 'd. 'c -> 'd +//│ where +//│ 'a <: 'b -> 'c -> 'd) | ChurchInt //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) -//│ ╔══[ERROR] Subtyping constraint of the form `forall ?a ?to_ch ?b ?c ?d. ?to_ch <: int -> (forall 'M. ('M -> 'M) -> 'M -> 'M)` exceeded recursion depth limit (250) +//│ ╔══[ERROR] Subtyping constraint of the form `forall ?to_ch. ?to_ch <: int -> (forall 'M. ('M -> 'M) -> 'M -> 'M)` exceeded recursion depth limit (250) //│ ║ l.193: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. @@ -215,17 +215,17 @@ to_church = to_ch rec def to_ch n = if true then zero else s (to_church n) -//│ to_ch: int -> (forall 'a. 'a -> (forall 'b 'c. ('b -> 'c +//│ to_ch: int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where -//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) | ChurchInt) +//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c) | ChurchInt) //│ = //│ zero is not implemented :e to_church = to_ch -//│ int -> (forall 'a. 'a -> (forall 'b 'c. ('b -> 'c +//│ int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where -//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) | ChurchInt) +//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c) | ChurchInt) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition @@ -254,17 +254,17 @@ to_church = to_ch def to_ch n = if true then z else s (to_church n) -//│ to_ch: int -> (forall 'a. 'a -> (forall 'a 'b 'c 'd. (('b & 'd) -> ('d | 'c) +//│ to_ch: int -> (forall 'a. 'a -> (forall 'a 'b 'c 'd. ('b & 'd) -> ('d | 'c) //│ where -//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c))) +//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) //│ = //│ to_church, to_ch and zero are not implemented :e // * Since "sound extrusion" to_church = to_ch -//│ int -> (forall 'a. 'a -> (forall 'b 'c 'd 'a. (('b & 'd) -> ('d | 'c) +//│ int -> (forall 'a. 'a -> (forall 'b 'c 'd 'a. ('b & 'd) -> ('d | 'c) //│ where -//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c))) +//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition @@ -293,17 +293,17 @@ to_church = to_ch def to_ch n = if true then zero else s (to_church n) -//│ to_ch: int -> (forall 'a. 'a -> (forall 'b 'c. ('b -> 'c +//│ to_ch: int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where -//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) | ChurchInt) +//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c) | ChurchInt) //│ = //│ zero is not implemented :e // * Since "sound extrusion" to_church = to_ch -//│ int -> (forall 'a. 'a -> (forall 'b 'c. ('b -> 'c +//│ int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where -//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) | ChurchInt) +//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c) | ChurchInt) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition @@ -331,17 +331,17 @@ to_church = to_ch def to_ch n = s (to_church n) -//│ to_ch: int -> (forall 'a. 'a -> (forall 'b 'c. ('b -> 'c +//│ to_ch: int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where -//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c))) +//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) //│ = //│ to_church, to_ch and zero are not implemented :e // * Since "sound extrusion" to_church = to_ch -//│ int -> (forall 'a. 'a -> (forall 'b 'c. ('b -> 'c +//│ int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where -//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c))) +//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition @@ -369,17 +369,17 @@ to_church = to_ch rec def to_ch n = s (to_church n) -//│ to_ch: int -> (forall 'a. 'a -> (forall 'b 'c. ('b -> 'c +//│ to_ch: int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where -//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c))) +//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) //│ = //│ to_church, to_ch, to_church, to_ch and zero are not implemented :e to_church = to_ch -//│ int -> (forall 'a. 'a -> (forall 'b 'c. ('b -> 'c +//│ int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where -//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c))) +//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition @@ -407,17 +407,17 @@ to_church = to_ch def to_ch (n:int) = s (to_church n) -//│ to_ch: int -> (forall 'a. 'a -> (forall 'b 'c. ('b -> 'c +//│ to_ch: int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where -//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c))) +//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) //│ = //│ to_church, to_ch, to_church, to_ch, to_church, to_ch and zero are not implemented :e // * Since "sound extrusion" to_church = to_ch -//│ int -> (forall 'a. 'a -> (forall 'b 'c. ('b -> 'c +//│ int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where -//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c))) +//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition @@ -448,17 +448,17 @@ to_church = to_ch rec def to_ch n = s (to_church n) -//│ to_ch: int -> (forall 'a. 'a -> (forall 'b 'c. ('b -> 'c +//│ to_ch: int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where -//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c))) +//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) //│ = //│ to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch and zero are not implemented :e to_church = to_ch -//│ int -> (forall 'a. 'a -> (forall 'b 'c. ('b -> 'c +//│ int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where -//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c))) +//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition @@ -486,17 +486,17 @@ to_church = to_ch def to_ch = s (to_church 0) -//│ to_ch: 'a -> (forall 'b 'c. ('b -> 'c +//│ to_ch: 'a -> (forall 'b 'c. 'b -> 'c //│ where -//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) +//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c) //│ = //│ to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch and zero are not implemented :e to_church = to_ch -//│ 'a -> (forall 'b 'c. ('b -> 'c +//│ 'a -> (forall 'b 'c. 'b -> 'c //│ where -//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) +//│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type mismatch in def definition: @@ -556,36 +556,36 @@ rec def to_ch n = //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b 'c 'd 'e. (? & 'b) -> (('e & 'c) -> ('e | 'd) -//│ where -//│ 'a <: 'b -> 'c -> 'd) +//│ where +//│ 'a <: 'b -> 'c -> 'd) //│ ╙── //│ to_ch: int -> 'a -> ('b -> ('b | 'c) //│ where //│ 'd <: 'a -> 'b -> 'c) -//│ where -//│ 'd :> forall 'e 'f 'g 'h. 'e -> (('h & 'f) -> ('h | 'g) -//│ where -//│ 'd <: 'e -> 'f -> 'g) +//│ where +//│ 'd :> forall 'e 'f 'g 'h. 'e -> (('h & 'f) -> ('h | 'g) +//│ where +//│ 'd <: 'e -> 'f -> 'g) //│ = [Function: to_ch7] :e // * Since the removal of "recursive definition hacks" to_church = to_ch //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a :> forall 'b 'c 'd 'e. (? & 'e) -> (('d & 'b) -> ('d | 'c) -//│ where -//│ 'a <: 'e -> 'b -> 'c) +//│ 'a :> forall 'b 'c 'd 'e. (? & 'b) -> (('e & 'c) -> ('e | 'd) +//│ where +//│ 'a <: 'b -> 'c -> 'd) //│ ╙── //│ int -> 'a -> ('b -> ('b | 'c) //│ where //│ 'd <: 'a -> 'b -> 'c) -//│ where -//│ 'd :> forall 'e 'f 'g 'h. 'e -> (('h & 'f) -> ('h | 'g) -//│ where -//│ 'd <: 'e -> 'f -> 'g) +//│ where +//│ 'd :> forall 'e 'f 'g 'h. 'e -> (('h & 'f) -> ('h | 'g) +//│ where +//│ 'd <: 'e -> 'f -> 'g) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) -//│ ╔══[ERROR] Subtyping constraint of the form `forall ?a ?to_ch ?b ?c ?d. ?to_ch <: int -> (forall 'M. ('M -> 'M) -> 'M -> 'M)` exceeded recursion depth limit (250) +//│ ╔══[ERROR] Subtyping constraint of the form `forall ?to_ch. ?to_ch <: int -> (forall 'M. ('M -> 'M) -> 'M -> 'M)` exceeded recursion depth limit (250) //│ ║ l.572: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. @@ -598,36 +598,36 @@ rec def to_ch_weird n = //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b 'c 'd. 'b -> ('c -> 'd -//│ where -//│ 'a <: 'b -> 'c -> 'd) +//│ where +//│ 'a <: 'b -> 'c -> 'd) //│ ╙── //│ to_ch_weird: anything -> 'a -> ('b -> 'c //│ where //│ 'd <: 'a -> 'b -> 'c) -//│ where -//│ 'd :> forall 'e 'f 'g. 'e -> ('f -> 'g -//│ where -//│ 'd <: 'e -> 'f -> 'g) +//│ where +//│ 'd :> forall 'e 'f 'g. 'e -> ('f -> 'g +//│ where +//│ 'd <: 'e -> 'f -> 'g) //│ = [Function: to_ch_weird] :e // * Since the removal of "recursive definition hacks" to_church = to_ch //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a :> forall 'b 'c 'd 'e. (? & 'd) -> (('c & 'e) -> ('c | 'b) -//│ where -//│ 'a <: 'd -> 'e -> 'b) +//│ 'a :> forall 'b 'c 'd 'e. (? & 'e) -> (('d & 'b) -> ('d | 'c) +//│ where +//│ 'a <: 'e -> 'b -> 'c) //│ ╙── //│ int -> 'a -> ('b -> ('b | 'c) //│ where //│ 'd <: 'a -> 'b -> 'c) -//│ where -//│ 'd :> forall 'e 'f 'g 'h. 'e -> (('h & 'f) -> ('h | 'g) -//│ where -//│ 'd <: 'e -> 'f -> 'g) +//│ where +//│ 'd :> forall 'e 'f 'g 'h. 'e -> (('h & 'f) -> ('h | 'g) +//│ where +//│ 'd <: 'e -> 'f -> 'g) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) -//│ ╔══[ERROR] Subtyping constraint of the form `forall ?a ?b ?c ?to_ch ?d. ?to_ch <: int -> (forall 'M. ('M -> 'M) -> 'M -> 'M)` exceeded recursion depth limit (250) +//│ ╔══[ERROR] Subtyping constraint of the form `forall ?to_ch. ?to_ch <: int -> (forall 'M. ('M -> 'M) -> 'M -> 'M)` exceeded recursion depth limit (250) //│ ║ l.614: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. diff --git a/shared/src/test/diff/fcp/ToChurchSimplif_ST.mls b/shared/src/test/diff/fcp/ToChurchSimplif_ST.mls index 619dfa67c4..4ba6c8bd8c 100644 --- a/shared/src/test/diff/fcp/ToChurchSimplif_ST.mls +++ b/shared/src/test/diff/fcp/ToChurchSimplif_ST.mls @@ -37,13 +37,13 @@ def s n f x = (n f x) :ns s -//│ res: forall 'a 'b 'c 'd 'e. 'c -> (forall 'f. 'f -> (forall 'g 'h. 'g -> 'h)) +//│ res: forall 'a 'b 'c 'd 'e. 'a -> (forall 'f. 'f -> (forall 'g 'h. 'g -> 'h)) //│ where //│ 'h :> 'b -//│ 'g <: 'd -//│ 'f <: 'e -//│ 'c <: 'e -> 'a -//│ 'a <: 'd -> 'b +//│ 'g <: 'c +//│ 'f <: 'd +//│ 'a <: 'd -> 'e +//│ 'e <: 'c -> 'b //│ = [Function: s] :e // * Works with CT diff --git a/shared/src/test/diff/fcp/Vec.mls b/shared/src/test/diff/fcp/Vec.mls index 2b969a1430..d829caa12f 100644 --- a/shared/src/test/diff/fcp/Vec.mls +++ b/shared/src/test/diff/fcp/Vec.mls @@ -236,7 +236,7 @@ v1_tty = cons_ty2 1 nil_ty //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.235: v1_tty = cons_ty2 1 nil_ty //│ ║ ^^^^^^^^^^^^^^^^^ -//│ ╟── type `Z` is not an instance of `S['n & 'p]` +//│ ╟── type `Z` is not an instance of type `S` //│ ║ l.39: nil_ty = nil : Vec[int, Z] //│ ║ ^ //│ ╟── Note: constraint arises from applied type reference: @@ -341,7 +341,7 @@ sum_ty = sum //│ 'a <: (((Cons[?, ?]\size with {head: int, tail: 'a}) | Nil) -> int) -> (int & 'b) //│ <: sum_ty: //│ Vec[int, 'n] -> int -//│ ╔══[ERROR] Subtyping constraint of the form `forall ?a ?tail ?b ?c ?sum. ?sum <: forall 'n. Vec[int, 'n] -> int` exceeded recursion depth limit (250) +//│ ╔══[ERROR] Subtyping constraint of the form `forall ?sum. ?sum <: forall 'n. Vec[int, 'n] -> int` exceeded recursion depth limit (250) //│ ║ l.338: sum_ty = sum //│ ║ ^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. @@ -352,7 +352,7 @@ sum nil // * Note: also worked woth top/bot extrusion sum v1_0 -//│ ╔══[ERROR] Subtyping constraint of the form `forall ?a ?b ?sum ?tail ?c. ?sum <: (forall 'a ?P ?N ?head 'n ?A ?d. ?d) -> ?e` exceeded recursion depth limit (250) +//│ ╔══[ERROR] Subtyping constraint of the form `forall ?sum. ?sum <: (forall ?a. ?a) -> ?b` exceeded recursion depth limit (250) //│ ║ l.354: sum v1_0 //│ ║ ^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. @@ -403,7 +403,7 @@ sum v1_ty //│ ╟── type `Cons[int, Z]` is not a function //│ ║ l.187: v1_ty = v1_ : Cons[int, Z] //│ ║ ^^^^^^^^^^^^ -//│ ╟── but it flows into reference with expected type `(forall ?tail ?a ?b ?head ?c ?d. ?c -> ?b) -> ?e` +//│ ╟── but it flows into reference with expected type `(forall ?a ?b. ?a -> ?b) -> ?c` //│ ║ l.+1: sum v1_ty //│ ║ ^^^^^ //│ ╟── Note: constraint arises from application: diff --git a/shared/src/test/diff/fcp/YicongFCP.mls b/shared/src/test/diff/fcp/YicongFCP.mls index e845914520..2f3a12d9e5 100644 --- a/shared/src/test/diff/fcp/YicongFCP.mls +++ b/shared/src/test/diff/fcp/YicongFCP.mls @@ -68,7 +68,7 @@ foo id //│ ╟── Note: constraint arises from application: //│ ║ l.52: forall 'T. fun (x: 'T) -> esc x : 'T //│ ╙── ^^^^^ -//│ res: 'T -> 'T | error +//│ res: error | 'T -> 'T //│ = [Function (anonymous)] foo (fun x -> error) diff --git a/shared/src/test/diff/gadt/Exp1.mls b/shared/src/test/diff/gadt/Exp1.mls new file mode 100644 index 0000000000..489ddea752 --- /dev/null +++ b/shared/src/test/diff/gadt/Exp1.mls @@ -0,0 +1,61 @@ +:NewDefs + + +abstract class Exp[A]: Pair | Lit +class Lit(n: Int) extends Exp[Int] +class Pair[L, R](val lhs: L, val rhs: R) extends Exp[[L, R]] +//│ abstract class Exp[A]: Lit | Pair[anything, anything] +//│ class Lit(n: Int) extends Exp +//│ class Pair[L, R](lhs: L, rhs: R) extends Exp + + +fun f(p: Pair['a, 'b]) = p.lhs +//│ fun f: forall 'a 'b. (p: Pair['a, 'b]) -> 'a + + +fun f(e) = if e is + Pair(l, r) then [l, r] +//│ fun f: forall 'a 'b. Pair['a, 'b] -> ['a, 'b] +// f: (Exp['a] & Pair) -> 0 + + +fun f(e) = if e is + Pair(l, r) then [l, r] + Lit(n) then n +//│ fun f: forall 'a 'b. (Lit | Pair['a, 'b]) -> (Int | ['a, 'b]) + +(e: Exp['X]) => f(e) +//│ forall 'X. (e: Exp['X]) -> (Int | [??L, ??R]) +//│ res +//│ = [Function: res] + + +:e // TODO support +fun f(e) = if e is + Pair['a, 'b](l, r) then [l, r] +//│ ╔══[ERROR] illegal pattern +//│ ║ l.35: Pair['a, 'b](l, r) then [l, r] +//│ ╙── ^^^^^^^^^^^^^^^^^^ +//│ fun f: anything -> error +//│ Code generation encountered an error: +//│ if expression was not desugared + + +:e // TODO support +fun f(e) = if e is + Pair(l: a, r) then + let f(x: a) = x + f(l) +//│ ╔══[ERROR] type identifier not found: a +//│ ║ l.47: let f(x: a) = x +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: l +//│ ║ l.48: f(l) +//│ ╙── ^ +//│ fun f: Pair[anything, anything] -> error +//│ Code generation encountered an error: +//│ unresolved symbol l +// fun f: forall 'lhs 'rhs. Pair['lhs, 'rhs] -> ('lhs, 'rhs,) + + + diff --git a/shared/src/test/diff/gadt/Exp2.mls b/shared/src/test/diff/gadt/Exp2.mls new file mode 100644 index 0000000000..45449541c3 --- /dev/null +++ b/shared/src/test/diff/gadt/Exp2.mls @@ -0,0 +1,110 @@ +:NewDefs + + + +// * Variant: + +abstract class Exp[out A]: Pair | Lit +class Lit(val n: Int) extends Exp[Int] +class Pair[out L, out R](val lhs: Exp[L], val rhs: Exp[R]) extends Exp[[L, R]] +//│ abstract class Exp[A]: Lit | Pair[anything, anything] +//│ class Lit(n: Int) extends Exp +//│ class Pair[L, R](lhs: Exp[L], rhs: Exp[R]) extends Exp + + +fun f(p: Pair['a, 'b]) = p.lhs +//│ fun f: forall 'a. (p: Pair['a, anything]) -> Exp['a] + +fun f(e) = if e is + Pair(l, r) then [l, r] +//│ fun f: forall 'L 'R. Pair['L, 'R] -> [Exp['L], Exp['R]] +// f: (Exp['a] & Pair) -> 0 + +fun f(e) = if e is + Pair(l, r) then [l, r] + Lit(n) then n +//│ fun f: forall 'L 'R. (Lit | Pair['L, 'R]) -> (Int | [Exp['L], Exp['R]]) + +(e: Exp['X]) => f(e) +//│ (e: Exp[anything]) -> (Int | [Exp[??L], Exp[??R]]) +//│ res +//│ = [Function: res] + +fun f(e) = if e is + Pair(l, r) then f(l) + f(r) + Lit(n) then n +//│ fun f: (Lit | Pair[anything, anything]) -> Int + + +// * Invariant: + +abstract class Exp[A]: Pair | Lit +class Lit(val n: Int) extends Exp[Int] +class Pair[L, R](val lhs: Exp[L], val rhs: Exp[R]) extends Exp[[L, R]] +//│ abstract class Exp[A]: Lit | Pair[?, ?] +//│ class Lit(n: Int) extends Exp +//│ class Pair[L, R](lhs: Exp[L], rhs: Exp[R]) extends Exp + + +fun f(p: Pair['a, 'b]) = p.lhs +//│ fun f: forall 'a 'b. (p: Pair['a, 'b]) -> Exp['a] + +fun f(e) = if e is + Pair(l, r) then [l, r] +//│ fun f: forall 'L 'R. Pair['L, 'R] -> [Exp['L], Exp['R]] +// f: (Exp['a] & Pair) -> 0 + +fun f(e) = if e is + Pair(l, r) then [l, r] + Lit(n) then n +//│ fun f: forall 'L 'R. (Lit | Pair['L, 'R]) -> (Int | [Exp['L], Exp['R]]) + +:e +(e: Exp['X]) => f(e) +//│ ╔══[ERROR] Type error in application +//│ ║ l.63: (e: Exp['X]) => f(e) +//│ ║ ^^^^ +//│ ╟── 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]]) +//│ where +//│ 'R :> ??R +//│ <: ??R0 +//│ 'L :> ??L +//│ <: ??L0 +//│ res +//│ = [Function: res] + +:e +fun f(e) = if e is + Pair(l, r) then f(l) + f(r) + Lit(n) then n +//│ ╔══[ERROR] Type error in definition +//│ ║ l.80: fun f(e) = if e is +//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.81: Pair(l, r) then f(l) + f(r) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.82: Lit(n) then n +//│ ║ ^^^^^^^^^^^^^^^ +//│ ╟── 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]] +//│ ╙── ^ +//│ ╔══[ERROR] Type error in definition +//│ ║ l.80: fun f(e) = if e is +//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.81: Pair(l, r) then f(l) + f(r) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.82: Lit(n) then n +//│ ║ ^^^^^^^^^^^^^^^ +//│ ╟── type variable `R` leaks out of its scope +//│ ║ l.43: class Pair[L, R](val lhs: Exp[L], val rhs: Exp[R]) extends Exp[[L, R]] +//│ ╙── ^ +//│ fun f: forall 'L 'R. (Lit | Pair['L, 'R]) -> Int +//│ where +//│ 'R :> ??R +//│ <: ??R0 +//│ 'L :> ??L +//│ <: ??L0 + + diff --git a/shared/src/test/diff/gadt/ThisMatching.mls b/shared/src/test/diff/gadt/ThisMatching.mls new file mode 100644 index 0000000000..cf63b32564 --- /dev/null +++ b/shared/src/test/diff/gadt/ThisMatching.mls @@ -0,0 +1,250 @@ +:NewDefs + + +:e +:re +module Dummy { + log(if this is Dummy then "duh!" else "huh?") +} +//│ ╔══[ERROR] Cannot access `this` during object initialization +//│ ║ l.7: log(if this is Dummy then "duh!" else "huh?") +//│ ╙── ^^^^ +//│ module Dummy +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +module Dummy { + fun introspect = + if this is Dummy then "duh!" else "huh?" +} +//│ module Dummy { +//│ fun introspect: "duh!" | "huh?" +//│ } + +Dummy.introspect +//│ "duh!" | "huh?" +//│ res +//│ = 'duh!' + + +abstract class Funny: Int { fun test = this + 1 } +//│ abstract class Funny: Int { +//│ fun test: Int +//│ } + +:e +class Unfunny { fun test = this + 1 } +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.36: 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 } +//│ ╙── ^^^^ +//│ class Unfunny { +//│ constructor() +//│ fun test: Int | error +//│ } + + +abstract class Exp: (Pair | Lit) { + fun test = if this is + Lit then 0 + Pair then 1 +} +class Lit(n: Int) extends Exp +class Pair(lhs: Exp, rhs: Exp) extends Exp +//│ abstract class Exp: Lit | Pair { +//│ fun test: 0 | 1 +//│ } +//│ class Lit(n: Int) extends Exp { +//│ fun test: 0 | 1 +//│ } +//│ class Pair(lhs: Exp, rhs: Exp) extends Exp { +//│ fun test: 0 | 1 +//│ } + + +abstract class Exp: (() | Lit) { + fun test = if this is + Lit then 0 + () then 1 +} +class Lit(n: Int) extends Exp +//│ abstract class Exp: Lit | () { +//│ fun test: 0 | 1 +//│ } +//│ class Lit(n: Int) extends Exp { +//│ fun test: 0 | 1 +//│ } + +// * TODO fix this by requiring a type annotation on `test` and delaying its body's type checking until all the involed classes are completed +// * Currently we try to complete Exp ->{type checking defn body} complete test ->{type checking pattern} find Wrap's typed fields ->{get Wrap's typed parents} complete Exp +:e +abstract class Exp: (() | Wrap) { + fun test = if this is + Wrap(a) then 0 + () then 1 +} +class Wrap(n: Exp) extends Exp +//│ ╔══[ERROR] Unhandled cyclic definition +//│ ║ l.83: abstract class Exp: (() | Wrap) { +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.84: fun test = if this is +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.85: Wrap(a) then 0 +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.86: () then 1 +//│ ║ ^^^^^^^^^^^^^ +//│ ║ l.87: } +//│ ╙── ^ +//│ abstract class Exp: Wrap | () { +//│ fun test: 0 | 1 +//│ } +//│ class Wrap(n: Exp) extends Exp + +// * TODO (same as above) +:e +abstract class Exp: (Pair | Lit) { + fun test: Int + fun test = if this is + Lit then 0 + Pair(l, r) then 1 +} +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.108: fun test: Int +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.109: fun test = if this is +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.110: Lit then 0 +//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.111: Pair(l, r) then 1 +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.112: } +//│ ╙── ^ +//│ abstract class Exp: Lit | Pair { +//│ fun test: Int +//│ } +//│ class Lit(n: Int) extends Exp { +//│ fun test: Int +//│ } +//│ 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 +//│ ╙── ^^^^^ +//│ error +//│ res +//│ = 1 + + +:e // TODO can we support this? +abstract class Exp: (Pair | Lit) { + fun test = if this is + Lit then 0 + Pair(l, r) then l.test + r.test +} +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.148: fun test = if this is +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.149: Lit then 0 +//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.150: Pair(l, r) then l.test + r.test +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.151: } +//│ ╙── ^ +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.150: Pair(l, r) then l.test + r.test +//│ ╙── ^^^^^ +//│ abstract class Exp: Lit | Pair { +//│ fun test: Int | error +//│ } +//│ class Lit(n: Int) extends Exp { +//│ fun test: Int | error +//│ } +//│ class Pair(lhs: Exp, rhs: Exp) extends Exp + + +:e // TODO +abstract class Exp: (Pair | Lit) { + fun test : Int + fun test = if this is + Lit then 0 + Pair(l, r) then l.test + r.test +} +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.179: fun test : Int +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ║ l.180: fun test = if this is +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.181: Lit then 0 +//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.182: Pair(l, r) then l.test + r.test +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.183: } +//│ ╙── ^ +//│ abstract class Exp: Lit | Pair { +//│ fun test: Int +//│ } +//│ class Lit(n: Int) extends Exp { +//│ fun test: Int +//│ } +//│ class Pair(lhs: Exp, rhs: Exp) extends Exp + + +:e // TODO support – this requires implementing type member lookup without forced completion (we get constraints like Pair <: Pair#L) +abstract class Exp[A]: (Pair | Lit) { + fun test = if this is + Lit then 0 + Pair then 1 +} +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.210: fun test = if this is +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.211: Lit then 0 +//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.212: Pair then 1 +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.213: } +//│ ╙── ^ +//│ ╔══[ERROR] Type error in `case` expression +//│ ║ l.210: fun test = if this is +//│ ║ ^^^^^^^ +//│ ║ l.211: Lit then 0 +//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.212: 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]] +//│ ╙── ^ +//│ abstract class Exp[A]: Lit | Pair[anything, anything] { +//│ fun test: 0 | 1 +//│ } +//│ class Lit(n: Int) extends Exp { +//│ fun test: 0 | 1 +//│ } +//│ class Pair[L, R](lhs: L, rhs: R) extends Exp + +Lit(0).test +//│ 0 | 1 +//│ res +//│ = 0 + + diff --git a/shared/src/test/diff/gen/genTests_v1-0.14-15-x2.fun b/shared/src/test/diff/gen/genTests_v1-0.14-15-x2.fun index 5b88904089..7743c733e1 100644 --- a/shared/src/test/diff/gen/genTests_v1-0.14-15-x2.fun +++ b/shared/src/test/diff/gen/genTests_v1-0.14-15-x2.fun @@ -56,7 +56,7 @@ add //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: (add add) //│ ╙── ^^^ -//│ res: int -> int | error +//│ res: error | int -> int (add 0.u) //│ ╔══[ERROR] Type mismatch in field selection: @@ -101,7 +101,7 @@ add //│ ╟── record of type `{v: 0}` is not an instance of type `int` //│ ║ l.+1: (add {v: 0}) //│ ╙── ^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int (add {v: add}) //│ ╔══[ERROR] Type mismatch in application: @@ -110,7 +110,7 @@ add //│ ╟── record of type `{v: int -> int -> int}` is not an instance of type `int` //│ ║ l.+1: (add {v: add}) //│ ╙── ^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int (add {v: 0.v}) //│ ╔══[ERROR] Type mismatch in field selection: @@ -125,7 +125,7 @@ add //│ ╟── record of type `{v: ?v}` is not an instance of type `int` //│ ║ l.+1: (add {v: 0.v}) //│ ╙── ^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int (add {v: add.v}) //│ ╔══[ERROR] Type mismatch in field selection: @@ -140,7 +140,7 @@ add //│ ╟── record of type `{v: ?v}` is not an instance of type `int` //│ ║ l.+1: (add {v: add.v}) //│ ╙── ^^^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((x => 0) 0) //│ res: 0 @@ -242,7 +242,7 @@ add //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let x = 0; add) add) //│ ╙── ^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = 0; add) (0 0)) //│ ╔══[ERROR] Type mismatch in application: @@ -272,7 +272,7 @@ add //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = 0; add) (x => 0)) //│ ╙── ^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = 0; add) (let x = 0; 0)) //│ res: int -> int @@ -314,7 +314,7 @@ add //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = 0; add) (x => add)) //│ ╙── ^^^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = 0; add) (x => x)) //│ ╔══[ERROR] Type mismatch in application: @@ -326,7 +326,7 @@ add //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = 0; add) (x => x)) //│ ╙── ^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = 0; add) (let x = 0; x)) //│ res: int -> int @@ -341,7 +341,7 @@ add //│ ╟── but it flows into reference with expected type `int` //│ ║ l.+1: ((let x = 0; add) (let x = add; x)) //│ ╙── ^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = 0; add) (let rec x = x; x)) //│ res: int -> int @@ -371,7 +371,7 @@ add //│ ╟── record of type `{u: 0}` is not an instance of type `int` //│ ║ l.+1: ((let x = 0; add) {u: 0}) //│ ╙── ^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = 0; add) {u: add}) //│ ╔══[ERROR] Type mismatch in application: @@ -380,7 +380,7 @@ add //│ ╟── record of type `{u: int -> int -> int}` is not an instance of type `int` //│ ║ l.+1: ((let x = 0; add) {u: add}) //│ ╙── ^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = 0; add) {v: 0}) //│ ╔══[ERROR] Type mismatch in application: @@ -389,7 +389,7 @@ add //│ ╟── record of type `{v: 0}` is not an instance of type `int` //│ ║ l.+1: ((let x = 0; add) {v: 0}) //│ ╙── ^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = 0; add) {v: add}) //│ ╔══[ERROR] Type mismatch in application: @@ -398,7 +398,7 @@ add //│ ╟── record of type `{v: int -> int -> int}` is not an instance of type `int` //│ ║ l.+1: ((let x = 0; add) {v: add}) //│ ╙── ^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = add; add) 0) //│ res: int -> int @@ -410,7 +410,7 @@ add //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let x = add; add) add) //│ ╙── ^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = add; add) (0 0)) //│ ╔══[ERROR] Type mismatch in application: @@ -476,7 +476,7 @@ add //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = add; add) (x => 0)) //│ ╙── ^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = add; add) (let x = 0; 0)) //│ res: int -> int @@ -497,7 +497,7 @@ add //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = add; add) (x => add)) //│ ╙── ^^^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = add; add) (let x = 0; add)) //│ ╔══[ERROR] Type mismatch in application: @@ -509,7 +509,7 @@ add //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = add; add) (let x = 0; add)) //│ ╙── ^^^^^^^^^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = add; add) (let x = add; add)) //│ ╔══[ERROR] Type mismatch in application: @@ -521,7 +521,7 @@ add //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = add; add) (let x = add; add)) //│ ╙── ^^^^^^^^^^^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = add; add) (let rec x = x; add)) //│ ╔══[ERROR] Type mismatch in application: @@ -533,7 +533,7 @@ add //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = add; add) (let rec x = x; add)) //│ ╙── ^^^^^^^^^^^^^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = add; add) (x => x)) //│ ╔══[ERROR] Type mismatch in application: @@ -545,7 +545,7 @@ add //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = add; add) (x => x)) //│ ╙── ^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let rec x = x; add) 0) //│ res: int -> int @@ -557,7 +557,7 @@ add //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let rec x = x; add) add) //│ ╙── ^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let rec x = x; add) 0.u) //│ ╔══[ERROR] Type mismatch in field selection: @@ -590,7 +590,7 @@ add //│ ╟── but it flows into field selection with expected type `int` //│ ║ l.+1: ((let rec x = x; add) {u: add}.u) //│ ╙── ^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let rec x = x; add) {v: 0}) //│ ╔══[ERROR] Type mismatch in application: @@ -599,7 +599,7 @@ add //│ ╟── record of type `{v: 0}` is not an instance of type `int` //│ ║ l.+1: ((let rec x = x; add) {v: 0}) //│ ╙── ^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let rec x = x; add) {v: add}) //│ ╔══[ERROR] Type mismatch in application: @@ -608,7 +608,7 @@ add //│ ╟── record of type `{v: int -> int -> int}` is not an instance of type `int` //│ ║ l.+1: ((let rec x = x; add) {v: add}) //│ ╙── ^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let rec x = x; add) {v: 0.u}) //│ ╔══[ERROR] Type mismatch in field selection: @@ -623,7 +623,7 @@ add //│ ╟── record of type `{v: ?u}` is not an instance of type `int` //│ ║ l.+1: ((let rec x = x; add) {v: 0.u}) //│ ╙── ^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let rec x = x; add) {v: add.u}) //│ ╔══[ERROR] Type mismatch in field selection: @@ -638,7 +638,7 @@ add //│ ╟── record of type `{v: ?u}` is not an instance of type `int` //│ ║ l.+1: ((let rec x = x; add) {v: add.u}) //│ ╙── ^^^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = {v: 0}; add) 0) //│ res: int -> int @@ -650,7 +650,7 @@ add //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let x = {v: 0}; add) add) //│ ╙── ^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = {v: 0}; add) (add 0)) //│ ╔══[ERROR] Type mismatch in application: @@ -662,7 +662,7 @@ add //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (add 0)) //│ ╙── ^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = {v: 0}; add) (add add)) //│ ╔══[ERROR] Type mismatch in application: @@ -680,7 +680,7 @@ add //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (add add)) //│ ╙── ^^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = {v: 0}; add) (x => 0)) //│ ╔══[ERROR] Type mismatch in application: @@ -692,7 +692,7 @@ add //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (x => 0)) //│ ╙── ^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = {v: 0}; add) (let x = 0; 0)) //│ res: int -> int @@ -713,7 +713,7 @@ add //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (x => add)) //│ ╙── ^^^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = {v: 0}; add) (let x = 0; add)) //│ ╔══[ERROR] Type mismatch in application: @@ -725,7 +725,7 @@ add //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (let x = 0; add)) //│ ╙── ^^^^^^^^^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = {v: 0}; add) (let x = add; add)) //│ ╔══[ERROR] Type mismatch in application: @@ -737,7 +737,7 @@ add //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (let x = add; add)) //│ ╙── ^^^^^^^^^^^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = {v: 0}; add) (let rec x = x; add)) //│ ╔══[ERROR] Type mismatch in application: @@ -749,7 +749,7 @@ add //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (let rec x = x; add)) //│ ╙── ^^^^^^^^^^^^^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = {v: 0}; add) (x => x)) //│ ╔══[ERROR] Type mismatch in application: @@ -761,7 +761,7 @@ add //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (x => x)) //│ ╙── ^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = {v: 0}; add) 0.v) //│ ╔══[ERROR] Type mismatch in field selection: @@ -827,7 +827,7 @@ add //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let x = {v: add}; add) add) //│ ╙── ^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = {v: add}; add) {v: 0}) //│ ╔══[ERROR] Type mismatch in application: @@ -836,7 +836,7 @@ add //│ ╟── record of type `{v: 0}` is not an instance of type `int` //│ ║ l.+1: ((let x = {v: add}; add) {v: 0}) //│ ╙── ^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let x = {v: add}; add) {v: add}) //│ ╔══[ERROR] Type mismatch in application: @@ -845,7 +845,7 @@ add //│ ╟── record of type `{v: int -> int -> int}` is not an instance of type `int` //│ ║ l.+1: ((let x = {v: add}; add) {v: add}) //│ ╙── ^^^^^^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((let rec x = {v: x}; add) 0) //│ res: int -> int @@ -857,7 +857,7 @@ add //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let rec x = {v: x}; add) add) //│ ╙── ^^^ -//│ res: int -> int | error +//│ res: error | int -> int ((x => x) 0) //│ res: 0 @@ -2526,7 +2526,7 @@ add //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add; {u: x.v}) //│ ║ ^^ -//│ ╟── reference of type `int -> int -> int` does not have field 'v' +//│ ╟── reference of type `int -> int -> int` does not match type `{v: ?v}` //│ ║ l.+1: (let x = add; {u: x.v}) //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `{v: ?v}` @@ -2579,9 +2579,9 @@ add //│ res: {u: forall 'a. 'a -> 'a} (let rec x = {v: (y => x)}; {u: x.v}) -//│ res: {u: 'v} +//│ res: {u: anything -> 'x} //│ where -//│ 'v :> anything -> {v: 'v} +//│ 'x :> {v: anything -> 'x} (x => 0.v) //│ ╔══[ERROR] Type mismatch in field selection: @@ -2911,7 +2911,7 @@ add //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add; x.v) //│ ║ ^^ -//│ ╟── reference of type `int -> int -> int` does not have field 'v' +//│ ╟── reference of type `int -> int -> int` does not match type `{v: ?v}` //│ ║ l.+1: (let x = add; x.v) //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `{v: ?v}` @@ -2940,9 +2940,9 @@ add //│ res: {v: int -> int -> int} (let rec x = {v: {v: x}}; x.v) -//│ res: 'v +//│ res: {v: 'x} //│ where -//│ 'v :> {v: {v: 'v}} +//│ 'x :> {v: {v: 'x}} 0.u //│ ╔══[ERROR] Type mismatch in field selection: @@ -3203,7 +3203,7 @@ add.v.u //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: {u: {u: 0}, v: (add add)} //│ ╙── ^^^ -//│ res: {u: {u: 0}, v: int -> int | error} +//│ res: {u: {u: 0}, v: error | int -> int} {u: {u: 0}, v: (add {v: 0})} //│ ╔══[ERROR] Type mismatch in application: @@ -3212,7 +3212,7 @@ add.v.u //│ ╟── record of type `{v: 0}` is not an instance of type `int` //│ ║ l.+1: {u: {u: 0}, v: (add {v: 0})} //│ ╙── ^^^^^^ -//│ res: {u: {u: 0}, v: int -> int | error} +//│ res: {u: {u: 0}, v: error | int -> int} {u: {u: 0}, v: (add {v: add})} //│ ╔══[ERROR] Type mismatch in application: @@ -3221,7 +3221,7 @@ add.v.u //│ ╟── record of type `{v: int -> int -> int}` is not an instance of type `int` //│ ║ l.+1: {u: {u: 0}, v: (add {v: add})} //│ ╙── ^^^^^^^^ -//│ res: {u: {u: 0}, v: int -> int | error} +//│ res: {u: {u: 0}, v: error | int -> int} {u: {u: 0}, v: (x => 0)} //│ res: {u: {u: 0}, v: anything -> 0} diff --git a/shared/src/test/diff/mlf-examples/ex_casparticuliers.mls b/shared/src/test/diff/mlf-examples/ex_casparticuliers.mls index 898a391c1b..495ff59ed1 100644 --- a/shared/src/test/diff/mlf-examples/ex_casparticuliers.mls +++ b/shared/src/test/diff/mlf-examples/ex_casparticuliers.mls @@ -87,7 +87,7 @@ succ' {} //│ ╟── from reference: //│ ║ l.48: def succ' n = fun f -> fun x -> f (n f x) //│ ╙── ^ -//│ res: (nothing -> 'a) -> anything -> 'a | error +//│ res: error | (nothing -> 'a) -> anything -> 'a //│ = [Function (anonymous)] :e @@ -116,7 +116,7 @@ succ' {} {} //│ ╟── from reference: //│ ║ l.48: def succ' n = fun f -> fun x -> f (n f x) //│ ╙── ^ -//│ res: anything -> nothing | error +//│ res: error | anything -> nothing //│ = [Function (anonymous)] :e @@ -525,22 +525,22 @@ def succ (n: ChurchInt) = fun f -> fun x -> f (n f x) def succ' n = fun f -> fun x -> f (n f x) //│ succ_ty: ChurchInt -> ChurchInt //│ = -//│ succ: ChurchInt -> (forall 'b. 'b -> (forall 'a 'c. ('a -> 'c +//│ succ: ChurchInt -> (forall 'b. 'b -> (forall 'a 'c. 'a -> 'c //│ where -//│ 'b <: 'a -> 'a & 'a -> 'c))) +//│ 'b <: 'a -> 'a & 'a -> 'c)) //│ = [Function: succ1] -//│ succ': 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. ('c -> 'e +//│ succ': 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where //│ 'a <: 'b -> 'c -> 'd -//│ 'b <: 'd -> 'e))) +//│ 'b <: 'd -> 'e)) //│ = [Function: succ$1] // * Note: without constrained types (and distrib.) we wouldn't get the principal type of succ' succ_ty = succ -//│ ChurchInt -> (forall 'b. 'b -> (forall 'a 'c. ('a -> 'c +//│ ChurchInt -> (forall 'b. 'b -> (forall 'a 'c. 'a -> 'c //│ where -//│ 'b <: 'a -> 'a & 'a -> 'c))) +//│ 'b <: 'a -> 'a & 'a -> 'c)) //│ <: succ_ty: //│ ChurchInt -> ChurchInt //│ = [Function: succ1] @@ -548,10 +548,10 @@ succ_ty = succ // * Still requires distributivity even with CT :e succ_ty = succ' -//│ 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. ('c -> 'e +//│ 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where -//│ 'a <: 'b -> 'c -> 'd -//│ 'b <: 'd -> 'e))) +//│ 'b <: 'd -> 'e +//│ 'a <: 'b -> 'c -> 'd)) //│ <: succ_ty: //│ ChurchInt -> ChurchInt //│ ╔══[ERROR] Type error in def definition @@ -583,18 +583,18 @@ succ_ty = succ' :DontDistributeForalls succ' -//│ res: 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. ('c -> 'e +//│ res: 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where -//│ 'b <: 'd -> 'e -//│ 'a <: 'b -> 'c -> 'd))) +//│ 'a <: 'b -> 'c -> 'd +//│ 'b <: 'd -> 'e)) //│ = [Function: succ$1] // :e // * Error delayed by inconsistent constrained types succ' {} -//│ res: 'a -> (forall 'b 'c 'd. ('b -> 'd +//│ res: 'a -> (forall 'b 'c 'd. 'b -> 'd //│ where -//│ 'a <: 'c -> 'd -//│ anything <: 'a -> 'b -> 'c)) +//│ anything <: 'a -> 'b -> 'c +//│ 'a <: 'c -> 'd) //│ = [Function (anonymous)] // :e // * Error delayed by inconsistent constrained types diff --git a/shared/src/test/diff/mlf-examples/ex_concrete.mls b/shared/src/test/diff/mlf-examples/ex_concrete.mls index fb2be246d2..a90987d1db 100644 --- a/shared/src/test/diff/mlf-examples/ex_concrete.mls +++ b/shared/src/test/diff/mlf-examples/ex_concrete.mls @@ -51,9 +51,9 @@ rec def map f l = case l of { Nil -> Nil | Cons -> Cons (f l.head, map f l.tail) } -//│ map: ('head -> 'head0) -> 'a -> 'tail +//│ map: ('head -> 'head0) -> 'a -> (Nil | 'b) //│ where -//│ 'tail :> (Cons['head0] with {tail: 'tail}) | Nil +//│ 'b :> Cons['head0] with {tail: Nil | 'b} //│ 'a <: (Cons[?] with {head: 'head, tail: 'a}) | Nil //│ = [Function: map] :NoRecursiveTypes diff --git a/shared/src/test/diff/mlf-examples/ex_demo.mls b/shared/src/test/diff/mlf-examples/ex_demo.mls index 9825920460..2bc5aa996b 100644 --- a/shared/src/test/diff/mlf-examples/ex_demo.mls +++ b/shared/src/test/diff/mlf-examples/ex_demo.mls @@ -254,13 +254,11 @@ rec def id1 x = if true then x else id1 id1 x //│ ║ l.243: rec def id1 x = if true then x else id1 id1 x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. -//│ id1: 'id1 +//│ id1: 'a -> 'b //│ where -//│ 'id1 :> 'a -> 'b -//│ 'a :> 'id1 +//│ 'a :> 'a -> 'b //│ <: 'b -//│ 'b :> 'id1 -//│ <: 'a -> 'b +//│ 'b := 'a -> 'b //│ = [Function: id1] @@ -280,13 +278,11 @@ def id1 x = if true then x else idy_ty idy_ty x // * but it does need at least recursive types: :RecursiveTypes rec def id1 x = if true then x else id1 id1 x -//│ id1: 'id1 +//│ id1: 'a -> 'b //│ where -//│ 'id1 :> 'a -> 'b -//│ 'a :> 'id1 +//│ 'a :> 'a -> 'b //│ <: 'b -//│ 'b :> 'id1 -//│ <: 'a -> 'b +//│ 'b := 'a -> 'b //│ = [Function: id11] id1 id //│ res: ('a & 'b) -> 'b | 'c @@ -297,39 +293,35 @@ id1 id //│ <: 'a -> 'b & 'c //│ = [Function: id] id1 id1 -//│ res: 'a -> 'b | 'id1 +//│ res: ('a & 'b) -> 'a //│ where -//│ 'a :> forall 'id1 'c 'd. 'a -> 'b | 'id1 -//│ <: 'b -//│ 'b :> forall 'id1 'c 'd. 'id1 -//│ <: 'a -> 'b -//│ 'id1 :> 'c -> 'd -//│ 'c :> 'id1 +//│ 'a :> forall 'c 'd. ('a & 'b & 'c) -> ('a | 'd) +//│ <: ((forall 'c 'd. 'c -> 'd) | 'b) -> 'a +//│ 'c :> 'c -> 'd //│ <: 'd -//│ 'd :> 'id1 -//│ <: 'c -> 'd +//│ 'd := 'c -> 'd //│ = [Function: id11] // * Note that it can't be applied when typed with :precise-rec-typing AND :DontDistributeForalls :DontDistributeForalls :precise-rec-typing rec def id1_p x = if true then x else id1_p id1_p x -//│ id1_p: 'id1_p +//│ id1_p: 'a -> 'b //│ where -//│ 'id1_p :> forall 'a. ('a & 'b) -> 'a -//│ 'a :> 'c -//│ 'c :> 'id1_p -//│ <: 'b -> 'c -//│ 'b :> 'id1_p -//│ <: 'c +//│ 'a <: 'b & 'c +//│ 'b :> 'd +//│ 'd :> forall 'a 'b. 'a -> 'b +//│ <: 'c -> 'd +//│ 'c :> forall 'a 'b. 'a -> 'b +//│ <: 'd //│ = [Function: id1_p] // * Can't apply it: :e id1_p id //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required -//│ ║ l.327: id1_p id +//│ ║ l.319: id1_p id //│ ║ ^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. -//│ res: ('a & 'b) -> 'a | error | 'c +//│ res: error | ('a & 'b) -> 'a | 'c //│ where //│ 'a :> 'd //│ 'd :> forall 'a 'b. ('a & 'b) -> 'a @@ -344,21 +336,17 @@ id1_p id // * TODO type pp – inline id1? :e id1 -//│ ╔══[ERROR] Inferred recursive type: 'id1 +//│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'id1 :> 'a -> 'b -//│ 'a :> 'id1 +//│ 'a :> 'a -> 'b //│ <: 'b -//│ 'b :> 'id1 -//│ <: 'a -> 'b +//│ 'b := 'a -> 'b //│ ╙── -//│ res: 'id1 +//│ res: 'a -> 'b //│ where -//│ 'id1 :> 'a -> 'b -//│ 'a :> 'id1 +//│ 'a :> 'a -> 'b //│ <: 'b -//│ 'b :> 'id1 -//│ <: 'a -> 'b +//│ 'b := 'a -> 'b //│ = [Function: id11] :e @@ -368,19 +356,19 @@ id1: nothing //│ 'a := 'b -> 'a //│ 'b :> 'b -> 'a //│ <: 'a -//│ ║ l.282: rec def id1 x = if true then x else id1 id1 x +//│ ║ l.280: rec def id1 x = if true then x else id1 id1 x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.365: id1: nothing +//│ ║ l.353: id1: nothing //│ ║ ^^^ //│ ╟── function of type `?a -> ?b` does not match type `nothing` -//│ ║ l.282: rec def id1 x = if true then x else id1 id1 x +//│ ║ l.280: rec def id1 x = if true then x else id1 id1 x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `nothing` -//│ ║ l.365: id1: nothing +//│ ║ l.353: id1: nothing //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.365: id1: nothing +//│ ║ l.353: id1: nothing //│ ╙── ^^^^^^^ //│ res: nothing //│ = [Function: id11] @@ -391,7 +379,7 @@ rec def id1_ x = id1_ id1_ x //│ where //│ 'a <: ('b -> 'c | 'b) -> 'c //│ 'c <: 'a -//│ ║ l.389: rec def id1_ x = id1_ id1_ x +//│ ║ l.377: rec def id1_ x = id1_ id1_ x //│ ╙── ^^^^^^^^^ //│ id1_: anything -> nothing //│ = [Function: id1_] @@ -401,46 +389,46 @@ rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ╔══[ERROR] Inferred recursive type: 'b //│ where //│ 'b <: (forall 'a. 'a -> 'a) -> (??a & 'b) -//│ ║ l.400: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x +//│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Type mismatch in binding of lambda expression: -//│ ║ l.400: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x +//│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'a` is not a function -//│ ║ l.400: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x +//│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ║ ^^ //│ ╟── but it flows into quantified type variable with expected type `'a0 -> 'a0` -//│ ║ l.400: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x +//│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ║ ^^ //│ ╟── Note: constraint arises from function type: -//│ ║ l.400: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x +//│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type error in binding of lambda expression -//│ ║ l.400: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x +//│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope -//│ ║ l.400: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x +//│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ║ ^^^^^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: -//│ ║ l.400: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x +//│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing binding of lambda expression; a type annotation may be required -//│ ║ l.400: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x +//│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ ╔══[ERROR] Type error in binding of lambda expression -//│ ║ l.400: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x +//│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope -//│ ║ l.400: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x +//│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ║ ^^^^^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: -//│ ║ l.400: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x +//│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.400: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x +//│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ╙── ^^^^^^^^^ //│ id1: (forall 'a. 'a -> 'a) -> 'a0 -> 'a0 //│ = [Function: id12] @@ -474,11 +462,11 @@ def id2 x = if true then x else id2 id2 x // let make_ex1 x (f:['a] ('a * ('a -> 'c)) -> 'b) = f x def make_ex1 x (f: forall 'a. (('a, 'a -> 'c),) -> 'b) = f x -//│ make_ex1: ('a, 'a -> 'c,) -> (forall 'a0. ('a0, 'a0 -> 'c,) -> 'b) -> 'b +//│ make_ex1: (('a, 'a -> 'c,),) -> (forall 'a0. (('a0, 'a0 -> 'c,),) -> 'b) -> 'b //│ = [Function: make_ex1] ex1_1 = make_ex1 (("A String", print_string)) -//│ ex1_1: (forall 'a. ('a, 'a -> unit,) -> 'b) -> 'b +//│ ex1_1: (forall 'a. (('a, 'a -> unit,),) -> 'b) -> 'b //│ = [Function (anonymous)] ex1_1 (fun ((x, f)) -> f x) @@ -487,7 +475,7 @@ ex1_1 (fun ((x, f)) -> f x) //│ A String ex1_2 = if true then make_ex1 ((42, print_int)) else ex1_1 -//│ ex1_2: (forall 'a 'a0. ('a0, 'a0 -> unit,) -> 'b & ('a, 'a -> unit,) -> 'b) -> 'b +//│ ex1_2: (forall 'a 'a0. (('a, 'a -> unit,),) -> 'b & (('a0, 'a0 -> unit,),) -> 'b) -> 'b //│ = [Function (anonymous)] ex1_2 (fun ((x, f)) -> f x) @@ -499,7 +487,7 @@ ex1_2 (fun ((x, f)) -> f x) // let make_ex2 x (f:['a] ('a * 'a * ('a -> 'a -> 'c)) -> 'b) = f x // ;; def make_ex2 x (f: forall 'a. (('a, 'a, 'a -> 'a -> 'c),) -> 'b) = f x -//│ make_ex2: ('a, 'a, 'a -> 'a -> 'c,) -> (forall 'a0. ('a0, 'a0, 'a0 -> 'a0 -> 'c,) -> 'b) -> 'b +//│ make_ex2: (('a, 'a, 'a -> 'a -> 'c,),) -> (forall 'a0. (('a0, 'a0, 'a0 -> 'a0 -> 'c,),) -> 'b) -> 'b //│ = [Function: make_ex2] // let ex_list1 = [ make_ex1 ("A String", print_string) ; @@ -509,7 +497,7 @@ def make_ex2 x (f: forall 'a. (('a, 'a, 'a -> 'a -> 'c),) -> 'b) = f x ex_list1 = cons (make_ex1 (("A String", print_string))) (cons (make_ex1 ((8250, print_int))) (cons (make_ex1 ((true, print_bool))) nil)) -//│ ex_list1: List[forall 'b. (forall 'a 'a0 'a1. ('a, 'a -> unit,) -> 'b & ('a0, 'a0 -> unit,) -> 'b & ('a1, 'a1 -> unit,) -> 'b) -> 'b] +//│ ex_list1: List[forall 'b. (forall 'a 'a0 'a1. (('a, 'a -> unit,),) -> 'b & (('a0, 'a0 -> unit,),) -> 'b & (('a1, 'a1 -> unit,),) -> 'b) -> 'b] //│ = Cons { //│ head: [Function (anonymous)], //│ tail: Cons { @@ -525,7 +513,7 @@ ex_list1 = cons (make_ex1 (("A String", print_string))) ex_list2 = cons (make_ex2 (("String", "String", eqstring))) (cons (make_ex2 ((1250, 4890, eqint))) (cons (make_ex2 ((true, false, eqbool))) nil)) -//│ ex_list2: List[forall 'b. (forall 'a 'c 'a0 'a1. ('a, 'a, 'a -> 'a -> (forall 'c. bool | 'c),) -> 'b & ('a0, 'a0, 'a0 -> 'a0 -> (bool | false | 'c),) -> 'b & ('a1, 'a1, 'a1 -> 'a1 -> (forall 'c. bool | 'c),) -> 'b) -> 'b] +//│ ex_list2: List[forall 'b. (forall 'a 'a0 'a1. (('a1, 'a1, 'a1 -> 'a1 -> bool,),) -> 'b & (('a, 'a, 'a -> 'a -> (bool | false),),) -> 'b & (('a0, 'a0, 'a0 -> 'a0 -> bool,),) -> 'b) -> 'b] //│ = Cons { //│ head: [Function (anonymous)], //│ tail: Cons { @@ -536,7 +524,7 @@ ex_list2 = cons (make_ex2 (("String", "String", eqstring))) h = head ex_list1 -//│ h: (forall 'a 'a0 'a1. ('a, 'a -> unit,) -> 'b & ('a0, 'a0 -> unit,) -> 'b & ('a1, 'a1 -> unit,) -> 'b) -> 'b +//│ h: (forall 'a 'a0 'a1. (('a, 'a -> unit,),) -> 'b & (('a0, 'a0 -> unit,),) -> 'b & (('a1, 'a1 -> unit,),) -> 'b) -> 'b //│ = [Function (anonymous)] h (fun ((x, f)) -> f x) @@ -566,7 +554,7 @@ test1 = listiter (fun ex -> ex (fun ((p1, p2)) -> p2 p1)) ex_list1 //│ true test1_ = listiter (fun ex -> ex (fun ((p1, p2)) -> p2 p1)) -//│ test1_: List[(forall 'a 'b. ('a, 'a -> 'b,) -> 'b) -> anything] -> unit +//│ test1_: List[(forall 'a 'b. (('a, 'a -> 'b,),) -> 'b) -> anything] -> unit //│ = [Function (anonymous)] test1_ ex_list1 @@ -577,7 +565,7 @@ test1_ ex_list1 //│ true process ex = ex (fun ((p1, p2)) -> p2 p1) -//│ process: ((forall 'a 'b. ('a, 'a -> 'b,) -> 'b) -> 'c) -> 'c +//│ process: ((forall 'a 'b. (('a, 'a -> 'b,),) -> 'b) -> 'c) -> 'c //│ = [Function: process] process h @@ -619,7 +607,7 @@ test2_ = listiter (fun ex -> ex (fun ((t1, t2, t3)) -> let eqf = t3 in let areequal = eqf arg1 arg2 in print_bool areequal )) -//│ test2_: List[(forall 'a 'b. ('a, 'b, 'a -> 'b -> bool,) -> unit) -> anything] -> unit +//│ test2_: List[(forall 'a 'b. (('a, 'b, 'a -> 'b -> bool,),) -> unit) -> anything] -> unit //│ = [Function (anonymous)] test2_ ex_list2 @@ -943,7 +931,7 @@ def c_mul m (n: Fint) = m (c_add n) c_i0 //│ = [Function: c_mul] def c_mul_ m n = m (c_add_ n) c_i0 -//│ c_mul_: ((forall 'a. ((forall 'b 'c 'd 'e. ('c -> 'd -> 'e) -> ('b -> 'd & 'c) -> 'b -> 'e) -> 'f -> 'a) -> 'a) -> (forall 'g. anything -> 'g -> 'g) -> 'h) -> 'f -> 'h +//│ c_mul_: ((forall 'a. ((forall 'b 'c 'd 'e. ('b -> 'c -> 'd) -> ('e -> 'c & 'b) -> 'e -> 'd) -> 'f -> 'a) -> 'a) -> (forall 'g. anything -> 'g -> 'g) -> 'h) -> 'f -> 'h //│ = [Function: c_mul_] // let c_pow n (m:Int) = m (c_mul n) c_i1 @@ -953,7 +941,7 @@ def c_pow m (n: Fint) = m (c_mul n) c_i1 //│ = [Function: c_pow] def c_pow_ m n = m (c_mul_ n) c_i1 -//│ c_pow_: (('a -> 'b) -> (forall 'c 'd. ('c -> 'd) -> 'c -> 'd) -> 'e) -> ((forall 'f. ((forall 'g 'h 'i 'j. ('g -> 'h -> 'i) -> ('j -> 'h & 'g) -> 'j -> 'i) -> 'a -> 'f) -> 'f) -> (forall 'k. anything -> 'k -> 'k) -> 'b) -> 'e +//│ c_pow_: (('a -> 'b) -> (forall 'c 'd. ('c -> 'd) -> 'c -> 'd) -> 'e) -> ((forall 'f. ((forall 'g 'h 'i 'j. ('j -> 'g -> 'h) -> ('i -> 'g & 'j) -> 'i -> 'h) -> 'a -> 'f) -> 'f) -> (forall 'k. anything -> 'k -> 'k) -> 'b) -> 'e //│ = [Function: c_pow_] @@ -973,7 +961,7 @@ def c_pred_ n = let s = fun p -> c_pair (c_2_2_ p) (c_succ_ (c_2_2_ p)) in let z = c_pair c_i0 c_i0 in c_1_2_ (n s z) -//│ c_pred_: ((forall 'a 'b 'c 'd 'e 'f. ((forall 'g. anything -> 'g -> 'g) -> ('b -> 'f -> 'e & 'a)) -> ('a -> (('c -> 'f & 'b) -> 'c -> 'e) -> 'd) -> 'd) -> (forall 'h. ((forall 'i. anything -> 'i -> 'i) -> (forall 'i. anything -> 'i -> 'i) -> 'h) -> 'h) -> (forall 'j. 'j -> anything -> 'j) -> 'k) -> 'k +//│ c_pred_: ((forall 'a 'b 'c 'd 'e 'f. ((forall 'g. anything -> 'g -> 'g) -> ('a -> 'd -> 'e & 'f)) -> ('f -> (('b -> 'd & 'a) -> 'b -> 'e) -> 'c) -> 'c) -> (forall 'h. ((forall 'i. anything -> 'i -> 'i) -> (forall 'i. anything -> 'i -> 'i) -> 'h) -> 'h) -> (forall 'j. 'j -> anything -> 'j) -> 'k) -> 'k //│ = [Function: c_pred_] @@ -1071,7 +1059,7 @@ def c_fact_A: Fint -> Fint def c_fact_A n = c_if (c_iszero n) (fun u -> c_i1) (fun u -> c_mul n (c_fact_A (c_pred n))) -//│ (Fint & (Fint -> (forall 'a 'b. (('a | 'b) -> 'a) -> 'b -> 'a | Fint)) -> (forall 'c. anything -> 'c -> 'c) -> 'd) -> (('e -> 'f) -> 'e -> 'f | 'd) +//│ (Fint & (Fint -> (forall 'b 'a. (('a | 'b) -> 'a) -> 'b -> 'a | Fint)) -> (forall 'c. anything -> 'c -> 'c) -> 'd) -> (('e -> 'f) -> 'e -> 'f | 'd) //│ <: c_fact_A: //│ Fint -> Fint //│ = @@ -1085,28 +1073,28 @@ rec def c_fact_ n = (fun _ -> c_mul_ n (c_fact_ (c_pred_ n))) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a <: (forall 'b. ((forall 'c. ? -> 'c -> 'c) -> (forall 'c. ? -> 'c -> 'c) -> 'b) -> 'b) -> (forall 'd. 'd -> ? -> 'd) -> ((forall 'e. ? -> ? -> ((forall 'f. 'f -> 'f) -> 'e) -> 'e) -> (forall 'g. ((forall 'f. 'f -> 'f) -> 'g) -> ? -> 'g) -> (forall 'h 'i. ? -> ('i -> 'h) -> 'i -> 'h) -> (? -> 'j) -> 'k & (forall 'l. ((forall 'm 'n 'o 'p. ('m -> 'n -> 'o) -> ('p -> 'n & 'm) -> 'p -> 'o) -> 'k -> 'l) -> 'l) -> (forall 'c. ? -> 'c -> 'c) -> 'j & (forall 'q 'r 's 't 'u 'v. ((forall 'w. ? -> 'w -> 'w) -> ('s -> 't -> 'v & 'q)) -> ('q -> (('r -> 't & 's) -> 'r -> 'v) -> 'u) -> 'u) -> 'a) -//│ ║ l.975: c_1_2_ (n s z) +//│ 'a <: (forall 'b. ((forall 'c. ? -> 'c -> 'c) -> (forall 'c. ? -> 'c -> 'c) -> 'b) -> 'b) -> (forall 'd. 'd -> ? -> 'd) -> ((forall 'e. ? -> ? -> ((forall 'f. 'f -> 'f) -> 'e) -> 'e) -> (forall 'g. ((forall 'f. 'f -> 'f) -> 'g) -> ? -> 'g) -> (forall 'h 'i. ? -> ('h -> 'i) -> 'h -> 'i) -> (? -> 'j) -> 'k & (forall 'l. ((forall 'm 'n 'o 'p. ('m -> 'n -> 'o) -> ('p -> 'n & 'm) -> 'p -> 'o) -> 'k -> 'l) -> 'l) -> (forall 'c. ? -> 'c -> 'c) -> 'j & (forall 'q 'r 's 't 'u 'v. ((forall 'w. ? -> 'w -> 'w) -> ('u -> 'v -> 'r & 't)) -> ('t -> (('s -> 'v & 'u) -> 's -> 'r) -> 'q) -> 'q) -> 'a) +//│ ║ l.963: c_1_2_ (n s z) //│ ╙── ^^^ //│ c_fact_: 'a -> 'b //│ where -//│ 'a <: (forall 'c. anything -> anything -> ((forall 'd. 'd -> 'd) -> 'c) -> 'c) -> (forall 'e. ((forall 'd. 'd -> 'd) -> 'e) -> anything -> 'e) -> (forall 'f 'g. anything -> ('f -> 'g) -> 'f -> 'g) -> (anything -> 'h) -> 'b & (forall 'i. ((forall 'j 'k 'l 'm. ('j -> 'k -> 'l) -> ('m -> 'k & 'j) -> 'm -> 'l) -> 'b -> 'i) -> 'i) -> (forall 'n. anything -> 'n -> 'n) -> 'h & (forall 'o 'p 'q 'r 's 't. ((forall 'u. anything -> 'u -> 'u) -> ('p -> 'r -> 's & 't)) -> ('t -> (('o -> 'r & 'p) -> 'o -> 's) -> 'q) -> 'q) -> (forall 'v. ((forall 'n. anything -> 'n -> 'n) -> (forall 'n. anything -> 'n -> 'n) -> 'v) -> 'v) -> (forall 'w. 'w -> anything -> 'w) -> 'a +//│ 'a <: (forall 'c. anything -> anything -> ((forall 'd. 'd -> 'd) -> 'c) -> 'c) -> (forall 'e. ((forall 'd. 'd -> 'd) -> 'e) -> anything -> 'e) -> (forall 'f 'g. anything -> ('f -> 'g) -> 'f -> 'g) -> (anything -> 'h) -> 'b & (forall 'i. ((forall 'j 'k 'l 'm. ('j -> 'k -> 'l) -> ('m -> 'k & 'j) -> 'm -> 'l) -> 'b -> 'i) -> 'i) -> (forall 'n. anything -> 'n -> 'n) -> 'h & (forall 'o 'p 'q 'r 's 't. ((forall 'u. anything -> 'u -> 'u) -> ('q -> 's -> 'p & 'o)) -> ('o -> (('t -> 's & 'q) -> 't -> 'p) -> 'r) -> 'r) -> (forall 'v. ((forall 'n. anything -> 'n -> 'n) -> (forall 'n. anything -> 'n -> 'n) -> 'v) -> 'v) -> (forall 'w. 'w -> anything -> 'w) -> 'a //│ = [Function: c_fact_] :e // * The type we infer without any annotations can't be checked against the desired signature c_fact_A = c_fact_ //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a <: (forall 'b. ((forall 'c. ? -> 'c -> 'c) -> (forall 'c. ? -> 'c -> 'c) -> 'b) -> 'b) -> (forall 'd. 'd -> ? -> 'd) -> ((forall 'e. ? -> ? -> ((forall 'f. 'f -> 'f) -> 'e) -> 'e) -> (forall 'g. ((forall 'f. 'f -> 'f) -> 'g) -> ? -> 'g) -> (forall 'h 'i. ? -> ('h -> 'i) -> 'h -> 'i) -> (forall 'j. ? -> (? -> 'j -> 'j | 'k)) -> 'l & (forall 'm. ((forall 'n 'o 'p 'q. ('n -> 'o -> 'p) -> ('q -> 'o & 'n) -> 'q -> 'p) -> (forall 'r 's. ('s -> 'r) -> 's -> 'r | 'l) -> 'm) -> 'm) -> (forall 'c. ? -> 'c -> 'c) -> 'k & (forall 't 'u 'v 'w 'x 'y. ((forall 'z. ? -> 'z -> 'z) -> ('v -> 't -> 'y & 'x)) -> ('x -> (('w -> 't & 'v) -> 'w -> 'y) -> 'u) -> 'u) -> 'a) -//│ ║ l.975: c_1_2_ (n s z) +//│ 'a <: (forall 'b. ((forall 'c. ? -> 'c -> 'c) -> (forall 'c. ? -> 'c -> 'c) -> 'b) -> 'b) -> (forall 'd. 'd -> ? -> 'd) -> ((forall 'e. ? -> ? -> ((forall 'f. 'f -> 'f) -> 'e) -> 'e) -> (forall 'g. ((forall 'f. 'f -> 'f) -> 'g) -> ? -> 'g) -> (forall 'h 'i. ? -> ('h -> 'i) -> 'h -> 'i) -> (forall 'j. ? -> (? -> 'j -> 'j | 'k)) -> 'l & (forall 'm. ((forall 'n 'o 'p 'q. ('q -> 'n -> 'o) -> ('p -> 'n & 'q) -> 'p -> 'o) -> (forall 'r 's. ('r -> 's) -> 'r -> 's | 'l) -> 'm) -> 'm) -> (forall 'c. ? -> 'c -> 'c) -> 'k & (forall 't 'u 'v 'w 'x 'y. ((forall 'z. ? -> 'z -> 'z) -> ('t -> 'x -> 'w & 'u)) -> ('u -> (('v -> 'x & 't) -> 'v -> 'w) -> 'y) -> 'y) -> 'a) +//│ ║ l.963: c_1_2_ (n s z) //│ ╙── ^^^ //│ 'a -> 'b //│ where -//│ 'a <: (forall 'c. anything -> anything -> ((forall 'd. 'd -> 'd) -> 'c) -> 'c) -> (forall 'e. ((forall 'd. 'd -> 'd) -> 'e) -> anything -> 'e) -> (forall 'f 'g. anything -> ('f -> 'g) -> 'f -> 'g) -> (anything -> 'h) -> 'b & (forall 'i. ((forall 'j 'k 'l 'm. ('j -> 'k -> 'l) -> ('m -> 'k & 'j) -> 'm -> 'l) -> 'b -> 'i) -> 'i) -> (forall 'n. anything -> 'n -> 'n) -> 'h & (forall 'o 'p 'q 'r 's 't. ((forall 'u. anything -> 'u -> 'u) -> ('r -> 's -> 't & 'o)) -> ('o -> (('p -> 's & 'r) -> 'p -> 't) -> 'q) -> 'q) -> (forall 'v. ((forall 'n. anything -> 'n -> 'n) -> (forall 'n. anything -> 'n -> 'n) -> 'v) -> 'v) -> (forall 'w. 'w -> anything -> 'w) -> 'a +//│ 'a <: (forall 'c. anything -> anything -> ((forall 'd. 'd -> 'd) -> 'c) -> 'c) -> (forall 'e. ((forall 'd. 'd -> 'd) -> 'e) -> anything -> 'e) -> (forall 'f 'g. anything -> ('f -> 'g) -> 'f -> 'g) -> (anything -> 'h) -> 'b & (forall 'i. ((forall 'j 'k 'l 'm. ('j -> 'k -> 'l) -> ('m -> 'k & 'j) -> 'm -> 'l) -> 'b -> 'i) -> 'i) -> (forall 'n. anything -> 'n -> 'n) -> 'h & (forall 'o 'p 'q 'r 's 't. ((forall 'u. anything -> 'u -> 'u) -> ('p -> 's -> 'r & 'q)) -> ('q -> (('o -> 's & 'p) -> 'o -> 'r) -> 't) -> 't) -> (forall 'v. ((forall 'n. anything -> 'n -> 'n) -> (forall 'n. anything -> 'n -> 'n) -> 'v) -> 'v) -> (forall 'w. 'w -> anything -> 'w) -> 'a //│ <: c_fact_A: //│ Fint -> Fint //│ ╔══[ERROR] Cyclic-looking constraint while typing def definition; a type annotation may be required -//│ ║ l.1097: c_fact_A = c_fact_ +//│ ║ l.1085: c_fact_A = c_fact_ //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ = [Function: c_fact_] @@ -1137,11 +1125,11 @@ def print_fact_ n = print_string "\n" //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a <: (forall 'b. ((forall 'c. ? -> 'c -> 'c) -> (forall 'c. ? -> 'c -> 'c) -> 'b) -> 'b) -> (forall 'd. 'd -> ? -> 'd) -> ((forall 'e. ? -> ? -> ((forall 'f. 'f -> 'f) -> 'e) -> 'e) -> (forall 'g. ((forall 'f. 'f -> 'f) -> 'g) -> ? -> 'g) -> (forall 'h 'i. ? -> ('h -> 'i) -> 'h -> 'i) -> (forall 'j. ? -> (? -> 'j -> 'j | 'k)) -> 'l & (forall 'm. ((forall 'n 'o 'p 'q. ('p -> 'q -> 'n) -> ('o -> 'q & 'p) -> 'o -> 'n) -> (forall 'r 's. ('r -> 's) -> 'r -> 's | 'l) -> 'm) -> 'm) -> (forall 'c. ? -> 'c -> 'c) -> 'k & (forall 't 'u 'v 'w 'x 'y. ((forall 'z. ? -> 'z -> 'z) -> ('y -> 'x -> 'v & 'u)) -> ('u -> (('w -> 'x & 'y) -> 'w -> 'v) -> 't) -> 't) -> 'a) -//│ ║ l.975: c_1_2_ (n s z) +//│ 'a <: (forall 'b. ((forall 'c. ? -> 'c -> 'c) -> (forall 'c. ? -> 'c -> 'c) -> 'b) -> 'b) -> (forall 'd. 'd -> ? -> 'd) -> ((forall 'e. ? -> ? -> ((forall 'f. 'f -> 'f) -> 'e) -> 'e) -> (forall 'g. ((forall 'f. 'f -> 'f) -> 'g) -> ? -> 'g) -> (forall 'h 'i. ? -> ('h -> 'i) -> 'h -> 'i) -> (forall 'j. ? -> (? -> 'j -> 'j | 'k)) -> 'l & (forall 'm. ((forall 'n 'o 'p 'q. ('n -> 'o -> 'p) -> ('q -> 'o & 'n) -> 'q -> 'p) -> (forall 'r 's. ('r -> 's) -> 'r -> 's | 'l) -> 'm) -> 'm) -> (forall 'c. ? -> 'c -> 'c) -> 'k & (forall 't 'u 'v 'w 'x 'y. ((forall 'z. ? -> 'z -> 'z) -> ('x -> 'u -> 'y & 'w)) -> ('w -> (('t -> 'u & 'x) -> 't -> 'y) -> 'v) -> 'v) -> 'a) +//│ ║ l.963: c_1_2_ (n s z) //│ ╙── ^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required -//│ ║ l.1136: let _ = c_printint_ (c_fact_ (to_church_ n)) in +//│ ║ l.1124: let _ = c_printint_ (c_fact_ (to_church_ n)) in //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ print_fact_: int -> unit @@ -1172,11 +1160,11 @@ def print_fact2_ n = (c_printint2_ (c_fact_ (to_church_ n))) )) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a <: (forall 'b. ((forall 'c. ? -> 'c -> 'c) -> (forall 'c. ? -> 'c -> 'c) -> 'b) -> 'b) -> (forall 'd. 'd -> ? -> 'd) -> ((forall 'e. ? -> ? -> ((forall 'f. 'f -> 'f) -> 'e) -> 'e) -> (forall 'g. ((forall 'f. 'f -> 'f) -> 'g) -> ? -> 'g) -> (forall 'h 'i. ? -> ('h -> 'i) -> 'h -> 'i) -> (forall 'j. ? -> (? -> 'j -> 'j | 'k)) -> 'l & (forall 'm. ((forall 'n 'o 'p 'q. ('o -> 'p -> 'q) -> ('n -> 'p & 'o) -> 'n -> 'q) -> (forall 'r 's. ('r -> 's) -> 'r -> 's | 'l) -> 'm) -> 'm) -> (forall 'c. ? -> 'c -> 'c) -> 'k & (forall 't 'u 'v 'w 'x 'y. ((forall 'z. ? -> 'z -> 'z) -> ('y -> 't -> 'u & 'w)) -> ('w -> (('x -> 't & 'y) -> 'x -> 'u) -> 'v) -> 'v) -> 'a) -//│ ║ l.975: c_1_2_ (n s z) +//│ 'a <: (forall 'b. ((forall 'c. ? -> 'c -> 'c) -> (forall 'c. ? -> 'c -> 'c) -> 'b) -> 'b) -> (forall 'd. 'd -> ? -> 'd) -> ((forall 'e. ? -> ? -> ((forall 'f. 'f -> 'f) -> 'e) -> 'e) -> (forall 'g. ((forall 'f. 'f -> 'f) -> 'g) -> ? -> 'g) -> (forall 'h 'i. ? -> ('h -> 'i) -> 'h -> 'i) -> (forall 'j. ? -> (? -> 'j -> 'j | 'k)) -> 'l & (forall 'm. ((forall 'n 'o 'p 'q. ('n -> 'o -> 'p) -> ('q -> 'o & 'n) -> 'q -> 'p) -> (forall 'r 's. ('r -> 's) -> 'r -> 's | 'l) -> 'm) -> 'm) -> (forall 'c. ? -> 'c -> 'c) -> 'k & (forall 't 'u 'v 'w 'x 'y. ((forall 'z. ? -> 'z -> 'z) -> ('t -> 'w -> 'y & 'v)) -> ('v -> (('u -> 'w & 't) -> 'u -> 'y) -> 'x) -> 'x) -> 'a) +//│ ║ l.963: c_1_2_ (n s z) //│ ╙── ^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required -//│ ║ l.1172: (c_printint2_ (c_fact_ (to_church_ n))) )) +//│ ║ l.1160: (c_printint2_ (c_fact_ (to_church_ n))) )) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ print_fact2_: int -> string @@ -1222,14 +1210,14 @@ this_should_be_98_ = let c_98_ = c_pred_ c_99_ in c_printint2_ c_98_ //│ ╔══[ERROR] Inferred recursive type: nothing -//│ ║ l.743: type Fint = forall 'a. ('a -> 'a) -> ('a -> 'a) +//│ ║ l.731: type Fint = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ╙── ^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required -//│ ║ l.1219: let c_i10_ = c_mul_ c_i5_ c_i2_ in +//│ ║ l.1207: let c_i10_ = c_mul_ c_i5_ c_i2_ in //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required -//│ ║ l.1220: let c_i9_ = c_pred_ c_i10_ in +//│ ║ l.1208: let c_i9_ = c_pred_ c_i10_ in //│ ║ ^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ this_should_be_98_: string @@ -1252,19 +1240,19 @@ c_i5_ = c_add_ c_i3_ c_i2_ :e c_i10_ = c_mul_ c_i5_ c_i2_ //│ ╔══[ERROR] Inferred recursive type: nothing -//│ ║ l.743: type Fint = forall 'a. ('a -> 'a) -> ('a -> 'a) +//│ ║ l.731: type Fint = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ╙── ^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required -//│ ║ l.1253: c_i10_ = c_mul_ c_i5_ c_i2_ +//│ ║ l.1241: c_i10_ = c_mul_ c_i5_ c_i2_ //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. -//│ c_i10_: ((forall 'a 'b 'c. ('a -> 'b & 'b -> 'c) -> 'a -> 'c | 'd) -> ('e -> 'f -> ('g & 'h & 'i & 'j & 'k & 'l & 'm) & 'n) & (('o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 's) -> 'o -> 'r | 'n) -> ('t -> 'u -> ('h & 'i & 'j & 'k & 'l & 'm) & 'v) & (('w -> ('f & 'o) & 'o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'e & 's) -> 'w -> 'g | 'v) -> ('x -> 'y -> ('i & 'j & 'k & 'l & 'm) & 'z) & (('a1 -> ('u & 'w) & 'w -> ('f & 'o) & 'o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'e & 's & 't) -> 'a1 -> 'h | 'z) -> ('b1 -> 'c1 -> ('j & 'k & 'l & 'm) & 'd1) & (('e1 -> ('y & 'a1) & 'a1 -> ('u & 'w) & 'w -> ('f & 'o) & 'o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'e & 's & 't & 'x) -> 'e1 -> 'i | 'd1) -> ('f1 -> 'g1 -> ('k & 'l & 'm) & 'h1) & (('i1 -> ('c1 & 'e1) & 'e1 -> ('y & 'a1) & 'a1 -> ('u & 'w) & 'w -> ('f & 'o) & 'o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'e & 's & 't & 'x & 'b1) -> 'i1 -> 'j | 'h1) -> ('j1 -> 'k1 -> ('l & 'm) & 'l1) & (('m1 -> ('g1 & 'i1) & 'i1 -> ('c1 & 'e1) & 'e1 -> ('y & 'a1) & 'a1 -> ('u & 'w) & 'w -> ('f & 'o) & 'o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'e & 's & 't & 'x & 'b1 & 'f1) -> 'm1 -> 'k | 'l1) -> ('n1 -> 'o1 -> 'm & 'p1) & (('q1 -> ('k1 & 'm1) & 'm1 -> ('g1 & 'i1) & 'i1 -> ('c1 & 'e1) & 'e1 -> ('y & 'a1) & 'a1 -> ('u & 'w) & 'w -> ('f & 'o) & 'o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'e & 's & 't & 'x & 'b1 & 'f1 & 'j1) -> 'q1 -> 'l | 'p1) -> ('r1 & 's1)) -> ('s -> 'p -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'd) -> (('t1 -> ('o1 & 'q1) & 'q1 -> ('k1 & 'm1) & 'm1 -> ('g1 & 'i1) & 'i1 -> ('c1 & 'e1) & 'e1 -> ('y & 'a1) & 'a1 -> ('u & 'w) & 'w -> ('f & 'o) & 'o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'e & 's & 't & 'x & 'b1 & 'f1 & 'j1 & 'n1) -> 't1 -> 'm | 'r1) | error | 's1 +//│ c_i10_: error | ((forall 'a 'b 'c. ('b -> 'c & 'c -> 'a) -> 'b -> 'a | 'd) -> ('e -> 'f -> ('g & 'h & 'i & 'j & 'k & 'l & 'm) & 'n) & (('o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 's) -> 'o -> 'r | 'n) -> ('t -> 'u -> ('h & 'i & 'j & 'k & 'l & 'm) & 'v) & (('w -> ('f & 'o) & 'o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'e & 's) -> 'w -> 'g | 'v) -> ('x -> 'y -> ('i & 'j & 'k & 'l & 'm) & 'z) & (('a1 -> ('u & 'w) & 'w -> ('f & 'o) & 'o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'e & 's & 't) -> 'a1 -> 'h | 'z) -> ('b1 -> 'c1 -> ('j & 'k & 'l & 'm) & 'd1) & (('e1 -> ('y & 'a1) & 'a1 -> ('u & 'w) & 'w -> ('f & 'o) & 'o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'e & 's & 't & 'x) -> 'e1 -> 'i | 'd1) -> ('f1 -> 'g1 -> ('k & 'l & 'm) & 'h1) & (('i1 -> ('c1 & 'e1) & 'e1 -> ('y & 'a1) & 'a1 -> ('u & 'w) & 'w -> ('f & 'o) & 'o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'e & 's & 't & 'x & 'b1) -> 'i1 -> 'j | 'h1) -> ('j1 -> 'k1 -> ('l & 'm) & 'l1) & (('m1 -> ('g1 & 'i1) & 'i1 -> ('c1 & 'e1) & 'e1 -> ('y & 'a1) & 'a1 -> ('u & 'w) & 'w -> ('f & 'o) & 'o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'e & 's & 't & 'x & 'b1 & 'f1) -> 'm1 -> 'k | 'l1) -> ('n1 -> 'o1 -> 'm & 'p1) & (('q1 -> ('k1 & 'm1) & 'm1 -> ('g1 & 'i1) & 'i1 -> ('c1 & 'e1) & 'e1 -> ('y & 'a1) & 'a1 -> ('u & 'w) & 'w -> ('f & 'o) & 'o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'e & 's & 't & 'x & 'b1 & 'f1 & 'j1) -> 'q1 -> 'l | 'p1) -> ('r1 & 's1)) -> ('s -> 'p -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'd) -> (('t1 -> ('o1 & 'q1) & 'q1 -> ('k1 & 'm1) & 'm1 -> ('g1 & 'i1) & 'i1 -> ('c1 & 'e1) & 'e1 -> ('y & 'a1) & 'a1 -> ('u & 'w) & 'w -> ('f & 'o) & 'o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'e & 's & 't & 'x & 'b1 & 'f1 & 'j1 & 'n1) -> 't1 -> 'm | 'r1) | 's1 //│ where -//│ 's1 <: (forall 'u1 'v1 'w1 'x1. ('u1 -> 'v1 -> 'w1) -> ('x1 -> 'v1 & 'u1) -> 'x1 -> 'w1) -> (forall 'y1 'z1 'a2. ('y1 -> 'z1 & 'z1 -> 'a2) -> 'y1 -> 'a2) -> 's1 +//│ 's1 <: (forall 'u1 'v1 'w1 'x1. ('u1 -> 'v1 -> 'w1) -> ('x1 -> 'v1 & 'u1) -> 'x1 -> 'w1) -> (forall 'y1 'z1 'a2. ('a2 -> 'y1 & 'y1 -> 'z1) -> 'a2 -> 'z1) -> 's1 //│ = [Function (anonymous)] //│ constrain calls : 1274 //│ annoying calls : 0 -//│ subtyping calls : 35217 +//│ subtyping calls : 34858 :stats // * This one works: c_i10_ = c_mul c_i5_ c_i2_ @@ -1272,7 +1260,7 @@ c_i10_ = c_mul c_i5_ c_i2_ //│ = [Function (anonymous)] //│ constrain calls : 902 //│ annoying calls : 0 -//│ subtyping calls : 6616 +//│ subtyping calls : 6626 // * Ouchie (cf stats) :stats @@ -1281,25 +1269,25 @@ c_i9_ = c_pred_ c_i10_ //│ = [Function (anonymous)] //│ constrain calls : 3909 //│ annoying calls : 0 -//│ subtyping calls : 31949 +//│ subtyping calls : 31911 :stats :e c_99_ = c_add_ (c_mul_ c_i9_ c_i10_) c_i9_ //│ ╔══[ERROR] Inferred recursive type: 'b //│ where -//│ 'b <: (forall 'c 'd 'e 'f. ('c -> 'd -> 'e) -> ('f -> 'd & 'c) -> 'f -> 'e) -> ((forall 'a 'g 'a0 'a1 'a2. (('a1 | 'g) -> 'a1 & ('a | 'g) -> 'a & ('a0 | 'g) -> 'a0 & ('a2 | 'g) -> 'a2) -> 'g -> ('a1 | 'a | 'a0 | 'a2) | Fint) -> ? & (forall 'a3 'h 'a4. (('a3 | 'h) -> 'a3 & ('a4 | 'h) -> 'a4) -> 'h -> ('a3 | 'a4) | Fint) -> anything) -//│ ║ l.915: def c_succ_ n = fun f -> fun x -> n f (f x) +//│ 'b <: (forall 'c 'd 'e 'f. ('c -> 'd -> 'e) -> ('f -> 'd & 'c) -> 'f -> 'e) -> ((forall 'a 'a0 'a1 'g 'a2. (('a | 'g) -> 'a & ('a1 | 'g) -> 'a1 & ('a2 | 'g) -> 'a2 & ('a0 | 'g) -> 'a0) -> 'g -> ('a | 'a1 | 'a2 | 'a0) | Fint) -> ? & (forall 'a3 'h 'a4. (('a3 | 'h) -> 'a3 & ('a4 | 'h) -> 'a4) -> 'h -> ('a3 | 'a4) | Fint) -> anything) +//│ ║ l.903: def c_succ_ n = fun f -> fun x -> n f (f x) //│ ╙── ^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required -//│ ║ l.1288: c_99_ = c_add_ (c_mul_ c_i9_ c_i10_) c_i9_ +//│ ║ l.1276: c_99_ = c_add_ (c_mul_ c_i9_ c_i10_) c_i9_ //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. -//│ c_99_: ('a -> 'a & 'b -> 'b & 'c -> 'c & 'd -> 'd) -> ('a & 'b & 'c & 'd) -> error | error +//│ c_99_: error | ('a -> 'a & 'b -> 'b & 'c -> 'c & 'd -> 'd) -> ('a & 'b & 'c & 'd) -> error //│ = [Function (anonymous)] //│ constrain calls : 4612 //│ annoying calls : 0 -//│ subtyping calls : 40656 +//│ subtyping calls : 40645 :stats // * Note: works with `c_mul` AND recursive types... :RecursiveTypes @@ -1308,7 +1296,7 @@ c_99_ = c_add_ (c_mul c_i9_ c_i10_) c_i9_ //│ = [Function (anonymous)] //│ constrain calls : 3728 //│ annoying calls : 0 -//│ subtyping calls : 61435 +//│ subtyping calls : 61344 :NoRecursiveTypes // * Ouchie++ @@ -1319,7 +1307,7 @@ c_98_ = c_pred_ c_99_ //│ = [Function (anonymous)] //│ constrain calls : 18323 //│ annoying calls : 0 -//│ subtyping calls : 178585 +//│ subtyping calls : 178084 :ResetFuel @@ -1388,58 +1376,58 @@ print_fact2_ 6 def c_succ (n: Fint) = fun f -> fun x -> n f (f x) def c_succ_ n = fun f -> fun x -> n f (f x) -//│ c_succ: Fint -> (forall 'b. 'b -> (forall 'a 'c. ('c -> 'a +//│ c_succ: Fint -> (forall 'b. 'b -> (forall 'a 'c. 'c -> 'a //│ where -//│ 'b <: 'a -> 'a & 'c -> 'a))) +//│ 'b <: 'a -> 'a & 'c -> 'a)) //│ = [Function: c_succ1] -//│ c_succ_: 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. ('e -> 'd +//│ c_succ_: 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'e -> 'd //│ where -//│ 'b <: 'e -> 'c -//│ 'a <: 'b -> 'c -> 'd))) +//│ 'a <: 'b -> 'c -> 'd +//│ 'b <: 'e -> 'c)) //│ = [Function: c_succ_1] def c_add_ n m = m c_succ_ n -//│ c_add_: 'a -> (forall 'b. ((forall 'c. 'c -> (forall 'd. 'd -> (forall 'e 'f 'g. ('g -> 'f +//│ c_add_: 'a -> (forall 'b. ((forall 'c. 'c -> (forall 'd. 'd -> (forall 'e 'f 'g. 'g -> 'f //│ where //│ 'd <: 'g -> 'e -//│ 'c <: 'd -> 'e -> 'f)))) -> 'a -> 'b) -> 'b) +//│ 'c <: 'd -> 'e -> 'f))) -> 'a -> 'b) -> 'b) //│ = [Function: c_add_1] // let c_i1 = fun f x -> f x def c_i1 = fun f -> fun x -> f x -//│ c_i1: 'a -> (forall 'b 'c. ('b -> 'c +//│ c_i1: 'a -> (forall 'b 'c. 'b -> 'c //│ where -//│ 'a <: 'b -> 'c)) +//│ 'a <: 'b -> 'c) //│ = [Function: c_i11] // let c_i2 = c_succ c_i1 def c_i2 = c_succ c_i1 def c_i2_ = c_succ_ c_i1 -//│ c_i2: 'b -> (forall 'a 'c. ('c -> 'a +//│ c_i2: 'b -> (forall 'a 'c. 'c -> 'a //│ where -//│ 'b <: 'a -> 'a & 'c -> 'a)) +//│ 'b <: 'a -> 'a & 'c -> 'a) //│ = [Function: c_i22] -//│ c_i2_: 'a -> (forall 'b 'c 'd. ('d -> 'c -//│ where -//│ forall 'e. 'e -> (forall 'f 'g. ('f -> 'g +//│ c_i2_: 'a -> (forall 'b 'c 'd. 'c -> 'b //│ where -//│ 'e <: 'f -> 'g)) <: 'a -> 'b -> 'c -//│ 'a <: 'd -> 'b)) +//│ 'a <: 'c -> 'd +//│ forall 'e. 'e -> (forall 'f 'g. 'f -> 'g +//│ where +//│ 'e <: 'f -> 'g) <: 'a -> 'd -> 'b) //│ = [Function: c_i2_1] // let c_i3 = c_succ c_i2 def c_i3 = c_succ c_i2 def c_i3_ = c_succ_ c_i2 -//│ c_i3: 'b -> (forall 'a 'c. ('c -> 'a +//│ c_i3: 'b -> (forall 'a 'c. 'c -> 'a //│ where -//│ 'b <: 'a -> 'a & 'c -> 'a)) +//│ 'b <: 'a -> 'a & 'c -> 'a) //│ = [Function: c_i31] -//│ c_i3_: 'b -> (forall 'c 'd 'e. ('e -> 'd +//│ c_i3_: 'b -> (forall 'c 'd 'e. 'e -> 'd //│ where //│ 'b <: 'e -> 'c -//│ forall 'f. 'f -> (forall 'g 'a. ('g -> 'a -//│ where -//│ 'f <: 'a -> 'a & 'g -> 'a)) <: 'b -> 'c -> 'd)) +//│ forall 'f. 'f -> (forall 'a 'g. 'g -> 'a +//│ where +//│ 'f <: 'a -> 'a & 'g -> 'a) <: 'b -> 'c -> 'd) //│ = [Function: c_i3_1] :e @@ -1447,42 +1435,42 @@ def c_i3_ = c_succ_ c_i2 c_i5_ = c_add_ c_i3_ c_i2 //│ ╔══[ERROR] Inferred recursive type: 'b //│ where -//│ 'b :> forall 'c. 'c -> (forall 'd 'e 'f 'g. ('f -> 'e -//│ where -//│ 'c <: 'f -> 'd & 'f -> 'g -//│ 'b <: 'c -> 'd -> 'e -//│ forall 'h. 'h -> (forall 'i 'j 'k. ('k -> 'j -//│ where -//│ 'h <: 'k -> 'i -//│ forall 'l. 'l -> (forall 'a 'm. ('m -> 'a -//│ where -//│ 'l <: 'a -> 'a & 'm -> 'a)) <: 'h -> 'i -> 'j)) <: 'c -> 'g -> 'e)) +//│ 'b :> forall 'c. 'c -> (forall 'd 'e 'f 'g. 'g -> 'f +//│ where +//│ 'c <: 'g -> 'e & 'g -> 'd +//│ 'b <: 'c -> 'e -> 'f +//│ forall 'h. 'h -> (forall 'i 'j 'k. 'k -> 'j +//│ where +//│ forall 'l. 'l -> (forall 'a 'm. 'm -> 'a +//│ where +//│ 'l <: 'a -> 'a & 'm -> 'a) <: 'h -> 'i -> 'j +//│ 'h <: 'k -> 'i) <: 'c -> 'd -> 'f) //│ ╙── //│ c_i5_: 'b //│ where -//│ 'b :> forall 'c. 'c -> (forall 'd 'e 'f 'g. ('e -> 'd -//│ where -//│ 'c <: 'e -> 'g & 'e -> 'f -//│ forall 'h. 'h -> (forall 'i 'j 'k. ('k -> 'j -//│ where -//│ 'h <: 'k -> 'i -//│ forall 'l. 'l -> (forall 'a 'm. ('m -> 'a -//│ where -//│ 'l <: 'a -> 'a & 'm -> 'a)) <: 'h -> 'i -> 'j)) <: 'c -> 'f -> 'd -//│ 'b <: 'c -> 'g -> 'd)) +//│ 'b :> forall 'c. 'c -> (forall 'd 'e 'f 'g. 'g -> 'f +//│ where +//│ 'c <: 'g -> 'e & 'g -> 'd +//│ forall 'h. 'h -> (forall 'i 'j 'k. 'i -> 'k +//│ where +//│ 'h <: 'i -> 'j +//│ forall 'l. 'l -> (forall 'a 'm. 'm -> 'a +//│ where +//│ 'l <: 'a -> 'a & 'm -> 'a) <: 'h -> 'j -> 'k) <: 'c -> 'd -> 'f +//│ 'b <: 'c -> 'e -> 'f) //│ = [Function (anonymous)] //│ constrain calls : 140 //│ annoying calls : 0 -//│ subtyping calls : 621 +//│ subtyping calls : 617 :stats :e c_i10_ = c_mul_ c_i5_ c_i2_ //│ ╔══[ERROR] Inferred recursive type: nothing -//│ ║ l.743: type Fint = forall 'a. ('a -> 'a) -> ('a -> 'a) +//│ ║ l.731: type Fint = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ╙── ^^ -//│ ╔══[ERROR] Subtyping constraint of the form `forall ?a ?b ?c ?d ?e ?f. ?b -> ?e -> ?c <: (forall ?g ?h ?i. ?g) -> ?j` exceeded recursion depth limit (250) -//│ ║ l.1480: c_i10_ = c_mul_ c_i5_ c_i2_ +//│ ╔══[ERROR] Subtyping constraint of the form `forall ?a ?b ?c. ?b -> ?a -> ?c <: (forall ?d. ?d) -> ?e` exceeded recursion depth limit (250) +//│ ║ l.1468: c_i10_ = c_mul_ c_i5_ c_i2_ //│ ║ ^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ c_i10_: error diff --git a/shared/src/test/diff/mlf-examples/ex_hashtbl.mls b/shared/src/test/diff/mlf-examples/ex_hashtbl.mls index a15d3d09e2..8269fa68d1 100644 --- a/shared/src/test/diff/mlf-examples/ex_hashtbl.mls +++ b/shared/src/test/diff/mlf-examples/ex_hashtbl.mls @@ -22,8 +22,8 @@ def snd: (('a, 'b),) -> 'b //│ isnil: List[?] -> bool //│ car: List['a] -> 'a //│ cdr: List['a] -> List['a] -//│ fst: ('a, anything,) -> 'a -//│ snd: (anything, 'b,) -> 'b +//│ fst: (('a, anything,),) -> 'a +//│ snd: ((anything, 'b,),) -> 'b // (* Use the value restriction ! *) @@ -150,39 +150,39 @@ table = hashtbl_add table "one" (fun f -> fun x -> f x) table = hashtbl_add table "two" (fun f -> fun x -> f (f x)) //│ table: List[nothing] //│ table: List[("one", forall 'a 'b. ('a -> 'b) -> 'a -> 'b,)] -//│ table: List[("one" | "two", forall 'a 'b 'c. ('c -> 'a & 'a -> 'b & 'c -> 'b) -> 'c -> 'b,)] +//│ table: List[("one" | "two", forall 'a 'b 'c. ('a -> 'b & 'b -> 'c & 'a -> 'c) -> 'a -> 'c,)] // let zog = // printInt (add (nfind table "one") (nfind table "two")) :stats zog = printInt (add (nfind table "one") (nfind table "two")) //│ zog: string -//│ constrain calls : 4393 -//│ annoying calls : 40 -//│ subtyping calls : 38454 +//│ constrain calls : 4385 +//│ annoying calls : 56 +//│ subtyping calls : 38390 // * Some subexpressions typed individually: :stats a = add (nfind table "one") //│ a: ChurchInt -> (('a -> ('a & 'b)) -> 'a -> 'b | ChurchInt) -//│ constrain calls : 2143 -//│ annoying calls : 20 -//│ subtyping calls : 19071 +//│ constrain calls : 2139 +//│ annoying calls : 28 +//│ subtyping calls : 19041 :stats b = (nfind table "two") //│ b: ('a -> 'b & 'b -> 'c & 'a -> 'c & 'a -> 'd & 'd -> 'c & 'a -> 'e & 'e -> 'c & 'a -> 'f & 'f -> 'c & 'a -> 'g & 'g -> 'c) -> 'a -> ('a | 'c) -//│ constrain calls : 1101 -//│ annoying calls : 20 -//│ subtyping calls : 12328 +//│ constrain calls : 1097 +//│ annoying calls : 28 +//│ subtyping calls : 12284 :stats b (fun x -> x) //│ res: 'a -> 'a //│ constrain calls : 893 //│ annoying calls : 0 -//│ subtyping calls : 10777 +//│ subtyping calls : 10761 b (fun x -> x (not x)) //│ res: nothing -> nothing @@ -196,7 +196,7 @@ a b //│ res: ('a -> ('a & 'b)) -> 'a -> 'b | ChurchInt //│ constrain calls : 1012 //│ annoying calls : 0 -//│ subtyping calls : 10478 +//│ subtyping calls : 10480 // * [FCP:patho] !! Note the stats here for this fully annotation-free version @@ -205,23 +205,23 @@ a b :Fuel 20000 zog_ = printInt_ (add_ (nfind table "one") (nfind table "two")) //│ zog_: string -//│ constrain calls : 28224 -//│ annoying calls : 40 -//│ subtyping calls : 137169 +//│ constrain calls : 28216 +//│ annoying calls : 56 +//│ subtyping calls : 137105 :stats a_ = add_ (nfind table "one") //│ a_: ('a -> 'b -> 'c & 'a -> 'b -> 'd & 'a -> 'b -> 'e & 'a -> 'b -> 'f & 'a -> 'b -> 'g & 'a -> 'b -> 'h & 'a -> 'b -> 'i & 'a -> 'b -> 'j & 'a -> 'b -> 'k & 'a -> 'b -> 'l & 'a -> 'b -> 'm & 'a -> 'b -> 'n & 'a -> 'b -> 'o & 'a -> 'b -> 'p & 'a -> 'b -> 'q & 'a -> 'b -> 'r & 's) -> (('c -> 't & 'd -> 't & 'e -> 't & 'f -> 't & 'u -> 't & 'g -> 'u & 'v -> 't & 'h -> 'v & 'w -> 't & 'i -> 'w & 'x -> 't & 'j -> 'x & 'k -> 't & 'l -> 't & 'm -> 't & 'n -> 't & 'y -> 't & 'o -> 'y & 'z -> 't & 'p -> 'z & 'a1 -> 't & 'q -> 'a1 & 'b1 -> 't & 'r -> 'b1 & 'a) -> 'b -> 't | 's) -//│ constrain calls : 2455 -//│ annoying calls : 20 -//│ subtyping calls : 27605 +//│ constrain calls : 2451 +//│ annoying calls : 28 +//│ subtyping calls : 27225 :stats a_ b //│ res: ('a -> 'b & 'b -> 'c & 'a -> 'c & 'a -> 'd & 'd -> 'c & 'a -> 'e & 'e -> 'c & 'a -> 'f & 'f -> 'c & 'a -> 'g & 'g -> 'c & 'h -> 'i & 'j -> 'h & 'k -> ('j & 'l) & 'l -> 'j & 'm -> 'i & 'n -> 'm & 'k -> ('n & 'o) & 'o -> 'n & 'p -> 'i & 'q -> 'p & 'k -> ('q & 'r) & 'r -> 'q & 's -> 'i & 't -> 's & 'k -> ('t & 'u) & 'u -> 't & 'v -> 'i & 'k -> ('v & 'w) & 'w -> 'v & 'x -> 'i & 'k -> ('x & 'y) & 'y -> 'x & 'z -> 'i & 'k -> ('z & 'a1) & 'a1 -> 'z & 'b1 -> 'i & 'k -> ('b1 & 'c1) & 'c1 -> 'b1 & 'd1 -> 'i & 'e1 -> 'd1 & 'k -> ('e1 & 'f1) & 'f1 -> 'e1 & 'g1 -> 'i & 'h1 -> 'g1 & 'k -> ('h1 & 'i1) & 'i1 -> 'h1 & 'j1 -> 'i & 'k1 -> 'j1 & 'k -> ('k1 & 'l1) & 'l1 -> 'k1 & 'm1 -> 'i & 'n1 -> 'm1 & 'k -> ('n1 & 'o1) & 'o1 -> 'n1 & 'p1 -> 'i & 'k -> ('p1 & 'q1) & 'q1 -> 'p1 & 'r1 -> 'i & 'k -> ('r1 & 's1) & 's1 -> 'r1 & 't1 -> 'i & 'k -> ('t1 & 'u1) & 'u1 -> 't1 & 'v1 -> 'i & 'k -> ('v1 & 'w1) & 'w1 -> 'v1) -> ('a & 'j & 'k & 'n & 'q & 't & 'v & 'x & 'z & 'b1 & 'e1 & 'h1 & 'k1 & 'n1 & 'p1 & 'r1 & 't1 & 'v1) -> ('a | 'i | 'c) //│ constrain calls : 12848 //│ annoying calls : 0 -//│ subtyping calls : 279885 +//│ subtyping calls : 278077 :ResetFuel // ========================================================================================== diff --git a/shared/src/test/diff/mlf-examples/ex_predicative.mls b/shared/src/test/diff/mlf-examples/ex_predicative.mls index 3c43e2288c..ca9d05090b 100644 --- a/shared/src/test/diff/mlf-examples/ex_predicative.mls +++ b/shared/src/test/diff/mlf-examples/ex_predicative.mls @@ -203,7 +203,7 @@ def t_ y = (fun h -> h (h (h (fun x -> y)))) (fun f -> fun n -> n (fun v -> k2) //│ <: (forall 'f. ? -> 'f -> 'f) -> (? -> ? -> ? & 'h) //│ 'g :> forall 'i 'b. 'i -> 'i | 'b //│ <: (forall 'f. ? -> 'f -> 'f) -> (? -> ? -> ? & 'h) -//│ 'h <: (forall 'j. ? -> ? -> 'j -> 'j) -> (forall 'k. 'k -> ? -> 'k) -> (forall 'l 'm. ('l -> 'm) -> 'l -> 'm) -> (? -> 'n -> 'g) -> 'a & (forall 'o 'p 'q 'r 's. ((forall 'f. ? -> 'f -> 'f) -> ('r -> 'o -> 'p & 's)) -> ('s -> (forall 't. ('p -> 't & 'r) -> 'o -> 't) -> 'q) -> 'q) -> (forall 'u. ((forall 'f. ? -> 'f -> 'f) -> (forall 'f. ? -> 'f -> 'f) -> 'u) -> 'u) -> (forall 'k. 'k -> ? -> 'k) -> ? & ? -> 'n -> 'g +//│ 'h <: (forall 'j. ? -> ? -> 'j -> 'j) -> (forall 'k. 'k -> ? -> 'k) -> (forall 'l 'm. ('l -> 'm) -> 'l -> 'm) -> (? -> 'n -> 'g) -> 'a & (forall 'o 'p 'q 'r 's. ((forall 'f. ? -> 'f -> 'f) -> ('o -> 'q -> 's & 'r)) -> ('r -> (forall 't. ('s -> 't & 'o) -> 'q -> 't) -> 'p) -> 'p) -> (forall 'u. ((forall 'f. ? -> 'f -> 'f) -> (forall 'f. ? -> 'f -> 'f) -> 'u) -> 'u) -> (forall 'k. 'k -> ? -> 'k) -> ? & ? -> 'n -> 'g //│ 'n :> forall 'b. 'b //│ <: ? & 'g //│ ║ l.197: def t_ y = (fun h -> h (h (h (fun x -> y)))) (fun f -> fun n -> n (fun v -> k2) k app (fun g -> fun x -> n (f (n (fun p -> fun s -> s (p k2) (fun f -> fun x -> f (p k2 f x))) (fun s -> s k2 k2) k) g) x)) two @@ -224,45 +224,45 @@ t_ id succ 0 def t_ y = (fun h -> h (h (h (fun x -> y)))) (fun f -> fun n -> n (fun v -> k2) k app (fun g -> fun x -> n (f (n (fun p -> fun s -> s (p k2) (fun f -> fun x -> f (p k2 f x))) (fun s -> s k2 k2) k) g) x)) two //│ t_: 'a -> 'b -> ('c -> 'd //│ where -//│ forall 'e 'f 'g. ('e -> 'f & 'f -> 'g) -> 'e -> 'g <: (forall 'h. ('h -//│ where -//│ forall 'i. ('i -//│ where -//│ forall 'j 'k 'l. 'l -> ((forall 'm. anything -> anything -> 'm -> 'm) -> (forall 'n. 'n -> anything -> 'n) -> (forall 'o 'p. ('p -> 'o) -> 'p -> 'o) -> (forall 'q 'r 's. 'q -> ('r -> 's -//│ where -//│ 'k <: (forall 't. ('t -//│ where -//│ 'l <: (forall 'u. ('u -//│ where -//│ 'k <: (forall 'v 'w. 'v -> ((forall 'x. ('x -//│ where -//│ 'v <: (forall 'y. anything -> 'y -> 'y) -> 'x)) -> (forall 'z 'a1 'b1. 'z -> ('a1 -> 'b1 -//│ where -//│ 'z <: (forall 'c1. ('c1 -//│ where -//│ 'v <: (forall 'y. anything -> 'y -> 'y) -> 'z -> 'a1 -> 'c1)) -> 'b1)) -> 'w) -> 'w) -> (forall 'd1. ((forall 'y. anything -> 'y -> 'y) -> (forall 'y. anything -> 'y -> 'y) -> 'd1) -> 'd1) -> (forall 'n. 'n -> anything -> 'n) -> 'u)) -> 'q -> 't)) -> 'r -> 's)) -> 'j & 'k) -> 'j <: (forall 'e1. ('e1 -//│ where -//│ forall 'j 'k 'l. 'l -> ((forall 'm. anything -> anything -> 'm -> 'm) -> (forall 'n. 'n -> anything -> 'n) -> (forall 'o 'p. ('p -> 'o) -> 'p -> 'o) -> (forall 'q 'r 's. 'q -> ('r -> 's -//│ where -//│ 'k <: (forall 't. ('t -//│ where -//│ 'l <: (forall 'u. ('u -//│ where -//│ 'k <: (forall 'v 'w. 'v -> ((forall 'x. ('x -//│ where -//│ 'v <: (forall 'y. anything -> 'y -> 'y) -> 'x)) -> (forall 'z 'a1 'b1. 'z -> ('a1 -> 'b1 -//│ where -//│ 'z <: (forall 'c1. ('c1 -//│ where -//│ 'v <: (forall 'y. anything -> 'y -> 'y) -> 'z -> 'a1 -> 'c1)) -> 'b1)) -> 'w) -> 'w) -> (forall 'd1. ((forall 'y. anything -> 'y -> 'y) -> (forall 'y. anything -> 'y -> 'y) -> 'd1) -> 'd1) -> (forall 'n. 'n -> anything -> 'n) -> 'u)) -> 'q -> 't)) -> 'r -> 's)) -> 'j & 'k) -> 'j <: (anything -> 'a) -> 'e1)) -> 'i) <: (forall 'f1. ('f1 -//│ where -//│ forall 'e 'f 'g. ('e -> 'f & 'f -> 'g) -> 'e -> 'g <: (forall 'v 'g1. 'v -> ((forall 'x. ('x -//│ where -//│ 'v <: (forall 'y. anything -> 'y -> 'y) -> 'x)) -> (forall 'z 'a1 'b1. 'z -> ('a1 -> 'b1 -//│ where -//│ 'z <: (forall 'c1. ('c1 -//│ where -//│ 'v <: (forall 'y. anything -> 'y -> 'y) -> 'z -> 'a1 -> 'c1)) -> 'b1)) -> 'g1) -> 'g1) -> (forall 'd1. ((forall 'y. anything -> 'y -> 'y) -> (forall 'y. anything -> 'y -> 'y) -> 'd1) -> 'd1) -> (forall 'n. 'n -> anything -> 'n) -> 'f1)) -> 'b -> 'h)) -> 'c -> 'd) +//│ forall 'e 'f 'g. ('e -> 'f & 'f -> 'g) -> 'e -> 'g <: (forall 'h. 'h +//│ where +//│ forall 'i. 'i +//│ where +//│ forall 'j 'k 'l. 'l -> ((forall 'm. anything -> anything -> 'm -> 'm) -> (forall 'n. 'n -> anything -> 'n) -> (forall 'o 'p. ('o -> 'p) -> 'o -> 'p) -> (forall 'q 'r 's. 'r -> ('s -> 'q +//│ where +//│ 'k <: (forall 't. 't +//│ where +//│ 'l <: (forall 'u. 'u +//│ where +//│ 'k <: (forall 'v 'w. 'v -> ((forall 'x. 'x +//│ where +//│ 'v <: (forall 'y. anything -> 'y -> 'y) -> 'x) -> (forall 'z 'a1 'b1. 'z -> ('a1 -> 'b1 +//│ where +//│ 'z <: (forall 'c1. 'c1 +//│ where +//│ 'v <: (forall 'y. anything -> 'y -> 'y) -> 'z -> 'a1 -> 'c1) -> 'b1)) -> 'w) -> 'w) -> (forall 'd1. ((forall 'y. anything -> 'y -> 'y) -> (forall 'y. anything -> 'y -> 'y) -> 'd1) -> 'd1) -> (forall 'n. 'n -> anything -> 'n) -> 'u) -> 'r -> 't) -> 's -> 'q)) -> 'j & 'k) -> 'j <: (forall 'e1. 'e1 +//│ where +//│ forall 'j 'k 'l. 'l -> ((forall 'm. anything -> anything -> 'm -> 'm) -> (forall 'n. 'n -> anything -> 'n) -> (forall 'o 'p. ('o -> 'p) -> 'o -> 'p) -> (forall 'q 'r 's. 'r -> ('s -> 'q +//│ where +//│ 'k <: (forall 't. 't +//│ where +//│ 'l <: (forall 'u. 'u +//│ where +//│ 'k <: (forall 'v 'w. 'v -> ((forall 'x. 'x +//│ where +//│ 'v <: (forall 'y. anything -> 'y -> 'y) -> 'x) -> (forall 'z 'a1 'b1. 'z -> ('a1 -> 'b1 +//│ where +//│ 'z <: (forall 'c1. 'c1 +//│ where +//│ 'v <: (forall 'y. anything -> 'y -> 'y) -> 'z -> 'a1 -> 'c1) -> 'b1)) -> 'w) -> 'w) -> (forall 'd1. ((forall 'y. anything -> 'y -> 'y) -> (forall 'y. anything -> 'y -> 'y) -> 'd1) -> 'd1) -> (forall 'n. 'n -> anything -> 'n) -> 'u) -> 'r -> 't) -> 's -> 'q)) -> 'j & 'k) -> 'j <: (anything -> 'a) -> 'e1) -> 'i <: (forall 'f1. 'f1 +//│ where +//│ forall 'e 'f 'g. ('e -> 'f & 'f -> 'g) -> 'e -> 'g <: (forall 'v 'g1. 'v -> ((forall 'x. 'x +//│ where +//│ 'v <: (forall 'y. anything -> 'y -> 'y) -> 'x) -> (forall 'z 'a1 'b1. 'z -> ('a1 -> 'b1 +//│ where +//│ 'z <: (forall 'c1. 'c1 +//│ where +//│ 'v <: (forall 'y. anything -> 'y -> 'y) -> 'z -> 'a1 -> 'c1) -> 'b1)) -> 'g1) -> 'g1) -> (forall 'd1. ((forall 'y. anything -> 'y -> 'y) -> (forall 'y. anything -> 'y -> 'y) -> 'd1) -> 'd1) -> (forall 'n. 'n -> anything -> 'n) -> 'f1) -> 'b -> 'h) -> 'c -> 'd) //│ = [Function: t_1] :NoConstrainedTypes @@ -286,15 +286,14 @@ def t_ y = (fun h -> h (h (h (fun x -> y)))) (fun f -> fun n -> n (fun v -> k2) //│ ╔══[ERROR] Inferred recursive type: ? -> (((forall 'a. nothing -> ('a -> 'a | ?) | ?) | 'b) -> ((forall 'c. ? -> 'c -> 'c) -> nothing & 'd) & (forall 'e 'f. ((forall 'c. ? -> 'c -> 'c) -> (forall 'c. ? -> 'c -> 'c) -> 'e & 'f) -> (? -> 'f | 'e) | 'd | ?) -> ((forall 'c. ? -> 'c -> 'c) -> nothing & 'b)) //│ ║ l.285: def t_ y = (fun h -> h (h (h (fun x -> y)))) (fun f -> fun n -> n (fun v -> k2) k app (fun g -> fun x -> n (f (n (fun p -> fun s -> s (p k2) (fun f -> fun x -> f (p k2 f x))) (fun s -> s k2 k2) k) g) x)) three //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ t_: ('a -> ('b -> 'c & 'd & 'e)) -> ('a & 'f) -> ((forall 'g. anything -> 'g -> 'g) -> ((forall 'h 'i 'j 'k 'l 'm. ((forall 'g. anything -> 'g -> 'g) -> ('k -> 'i -> 'h & 'm)) -> ('m -> (forall 'n. ('h -> 'n & 'k) -> 'i -> 'n) -> 'j) -> ('l -> 'l | 'j) | 'o | 'd) -> (forall 'p 'q. ((forall 'g. anything -> 'g -> 'g) -> (forall 'g. anything -> 'g -> 'g) -> 'p & 'q) -> (anything -> 'q | 'p) | 'r | 's | 't | 'c | 'u) -> 'b & 'v) & 't) -> ('c | 'u) -//│ where -//│ 'b :> forall 'w 'x. ((forall 'g. anything -> 'g -> 'g) -> (forall 'g. anything -> 'g -> 'g) -> 'x & 'w & 'x) -> (anything -> 'w | 'x) | 's | 't | 'c | 'u -//│ <: (forall 'g. anything -> 'g -> 'g) -> nothing -> nothing -> anything -//│ 's :> forall 'y 'z. ((forall 'a1 'b1. anything -> 'a1 -> ('b1 -> 'b1 | 'a1) | 'v) -> (forall 'c1. ('b -> 'c1 & 'o) -> ('b & 'r) -> 'c1) -> 'y) -> ('z -> 'z | 'y) | 'c -//│ <: (forall 'g. anything -> 'g -> 'g) -> (nothing -> nothing -> anything & 'd1) -//│ 'c <: (forall 'g. anything -> 'g -> 'g) -> ((forall 'h 'i 'j 'k 'l 'm. ((forall 'g. anything -> 'g -> 'g) -> ('k -> 'i -> 'h & 'm)) -> ('m -> (forall 'n. ('h -> 'n & 'k) -> 'i -> 'n) -> 'j) -> ('l -> 'l | 'j) | 'o | 'd) -> (forall 'e1 'f1. ((forall 'g. anything -> 'g -> 'g) -> (forall 'g. anything -> 'g -> 'g) -> 'e1 & 'f1) -> (anything -> 'f1 | 'e1) | 'r | 's | 't | 'c | 'u) -> 'b & 'v & 'd1) -//│ 'd1 <: (forall 'g1. anything -> anything -> 'g1 -> 'g1) -> (forall 'h1. 'h1 -> anything -> 'h1) -> (forall 'i1 'j1. ('i1 -> 'j1) -> 'i1 -> 'j1) -> ('a -> ('b & 'r & 'k1) -> 'c) -> 'f -> ('s -> ((forall 'g. anything -> 'g -> 'g) -> ((forall 'h 'i 'j 'k 'l 'm. ((forall 'g. anything -> 'g -> 'g) -> ('k -> 'i -> 'h & 'm)) -> ('m -> (forall 'n. ('h -> 'n & 'k) -> 'i -> 'n) -> 'j) -> ('l -> 'l | 'j) | 'o | 'd) -> (forall 'l1 'm1. ((forall 'g. anything -> 'g -> 'g) -> (forall 'g. anything -> 'g -> 'g) -> 'l1 & 'm1) -> (anything -> 'm1 | 'l1) | 'r | 's | 't | 'c | 'u) -> 'b & 'v) & 'u) & (forall 'n1 'o1. ((forall 'g. anything -> 'g -> 'g) -> (forall 'g. anything -> 'g -> 'g) -> 'n1 & 'o1) -> (anything -> 'o1 | 'n1) | 't | 'c | 'u) -> 's) & (forall 'h 'i 'j 'k 'm. ((forall 'g. anything -> 'g -> 'g) -> ('k -> 'i -> 'h & 'm)) -> ('m -> (forall 'n. ('h -> 'n & 'k) -> 'i -> 'n) -> 'j) -> 'j) -> (forall 'p1. ((forall 'g. anything -> 'g -> 'g) -> (forall 'g. anything -> 'g -> 'g) -> 'p1) -> 'p1) -> (forall 'h1. 'h1 -> anything -> 'h1) -> anything & 'd -> 'e -//│ 'e <: (forall 'q1 'r1. ((forall 'g. anything -> 'g -> 'g) -> (forall 'g. anything -> 'g -> 'g) -> 'q1 & 'r1) -> (anything -> 'r1 | 'q1) | 'k1 | 's | 't | 'c | 'u) -> 'c +//│ t_: ('a -> ((forall 'b 'c. ((forall 'd. anything -> 'd -> 'd) -> (forall 'd. anything -> 'd -> 'd) -> 'b & 'c) -> (anything -> 'c | 'b) | 'e | 'f | 'g | 'h | 'i | 'j) -> 'i & 'k)) -> ('a & 'l) -> ((forall 'd. anything -> 'd -> 'd) -> ((forall 'm 'n 'o 'p 'q 'r. ((forall 'd. anything -> 'd -> 'd) -> ('m -> 'r -> 'p & 'q)) -> ('q -> (forall 's. ('p -> 's & 'm) -> 'r -> 's) -> 'n) -> ('o -> 'o | 'n) | 't | 'k) -> (forall 'u 'v. ((forall 'd. anything -> 'd -> 'd) -> (forall 'd. anything -> 'd -> 'd) -> 'u & 'v) -> (anything -> 'v | 'u) | 'w | 'g | 'h | 'i | 'j) -> 'f & 'x) & 'h) -> ('i | 'j) +//│ where +//│ 'f :> forall 'y 'z. ((forall 'd. anything -> 'd -> 'd) -> (forall 'd. anything -> 'd -> 'd) -> 'y & 'z & 'y) -> (anything -> 'z | 'y) | 'g | 'h | 'i | 'j +//│ <: (forall 'd. anything -> 'd -> 'd) -> nothing -> nothing -> anything +//│ 'g :> forall 'a1 'b1. ((forall 'c1 'd1. anything -> 'c1 -> ('d1 -> 'd1 | 'c1) | 'x) -> (forall 'e1. ('f -> 'e1 & 't) -> ('f & 'w) -> 'e1) -> 'a1) -> ('b1 -> 'b1 | 'a1) | 'i +//│ <: (forall 'd. anything -> 'd -> 'd) -> (nothing -> nothing -> anything & 'f1) +//│ 'i <: (forall 'd. anything -> 'd -> 'd) -> ((forall 'm 'n 'o 'p 'q 'r. ((forall 'd. anything -> 'd -> 'd) -> ('m -> 'r -> 'p & 'q)) -> ('q -> (forall 's. ('p -> 's & 'm) -> 'r -> 's) -> 'n) -> ('o -> 'o | 'n) | 't | 'k) -> (forall 'g1 'h1. ((forall 'd. anything -> 'd -> 'd) -> (forall 'd. anything -> 'd -> 'd) -> 'g1 & 'h1) -> (anything -> 'h1 | 'g1) | 'w | 'g | 'h | 'i | 'j) -> 'f & 'x & 'f1) +//│ 'f1 <: (forall 'i1. anything -> anything -> 'i1 -> 'i1) -> (forall 'j1. 'j1 -> anything -> 'j1) -> (forall 'k1 'l1. ('k1 -> 'l1) -> 'k1 -> 'l1) -> ('a -> ('f & 'w & 'e) -> 'i) -> 'l -> ('g -> ((forall 'd. anything -> 'd -> 'd) -> ((forall 'm 'n 'o 'p 'q 'r. ((forall 'd. anything -> 'd -> 'd) -> ('m -> 'r -> 'p & 'q)) -> ('q -> (forall 's. ('p -> 's & 'm) -> 'r -> 's) -> 'n) -> ('o -> 'o | 'n) | 't | 'k) -> (forall 'm1 'n1. ((forall 'd. anything -> 'd -> 'd) -> (forall 'd. anything -> 'd -> 'd) -> 'm1 & 'n1) -> (anything -> 'n1 | 'm1) | 'w | 'g | 'h | 'i | 'j) -> 'f & 'x) & 'j) & (forall 'o1 'p1. ((forall 'd. anything -> 'd -> 'd) -> (forall 'd. anything -> 'd -> 'd) -> 'o1 & 'p1) -> (anything -> 'p1 | 'o1) | 'h | 'i | 'j) -> 'g) & (forall 'm 'n 'p 'q 'r. ((forall 'd. anything -> 'd -> 'd) -> ('m -> 'r -> 'p & 'q)) -> ('q -> (forall 's. ('p -> 's & 'm) -> 'r -> 's) -> 'n) -> 'n) -> (forall 'q1. ((forall 'd. anything -> 'd -> 'd) -> (forall 'd. anything -> 'd -> 'd) -> 'q1) -> 'q1) -> (forall 'j1. 'j1 -> anything -> 'j1) -> anything & 'k -> (forall 'r1 's1. ((forall 'd. anything -> 'd -> 'd) -> (forall 'd. anything -> 'd -> 'd) -> 'r1 & 's1) -> (anything -> 's1 | 'r1) | 'e | 'g | 'h | 'i | 'j) -> 'i //│ = [Function: t_2] :ResetFuel @@ -312,11 +311,14 @@ t id succ 0 :stats :e // * Strange recursive error. The bounds graph is quite large and hard to analyze for debugging... t_ id succ 0 -//│ ╔══[ERROR] Inferred recursive type: nothing +//│ ╔══[ERROR] Inferred recursive type: 'a +//│ where +//│ 'a :> int -> int +//│ <: (forall 'b 'c 'd 'e 'f 'g 'h. ((forall 'i. ? -> 'i -> 'i) -> (forall 'i. ? -> 'i -> 'i) -> 'c & 'h) -> (? -> 'h | 'c) | ? | ((forall 'i. ? -> 'i -> 'i) -> (forall 'i. ? -> 'i -> 'i) -> 'e & 'f & 'b) -> (? -> 'b | 'e | 'f) | nothing -> ('d -> 'd | ?) | 'j | nothing -> ('g -> 'g | ?)) -> nothing //│ ║ l.285: def t_ y = (fun h -> h (h (h (fun x -> y)))) (fun f -> fun n -> n (fun v -> k2) k app (fun g -> fun x -> n (f (n (fun p -> fun s -> s (p k2) (fun f -> fun x -> f (p k2 f x))) (fun s -> s k2 k2) k) g) x)) three -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.314: t_ id succ 0 +//│ ║ l.313: t_ id succ 0 //│ ║ ^^^^^^^^^^ //│ ╟── function of type `?a -> ?b` is not an instance of type `int` //│ ║ l.285: def t_ y = (fun h -> h (h (h (fun x -> y)))) (fun f -> fun n -> n (fun v -> k2) k app (fun g -> fun x -> n (f (n (fun p -> fun s -> s (p k2) (fun f -> fun x -> f (p k2 f x))) (fun s -> s k2 k2) k) g) x)) three @@ -325,7 +327,7 @@ t_ id succ 0 //│ = 6 //│ constrain calls : 352 //│ annoying calls : 0 -//│ subtyping calls : 2703 +//│ subtyping calls : 3162 // let t (z : 0) = let x = (fun (y: ['t > 0] 'a -> 't) -> y z y) in x x;; @@ -357,7 +359,7 @@ def t (z: nothing) = let x = fun (y: forall 't. 'a -> 't) -> y z in x x :e (fun x -> x x) (fun x -> x x) //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required -//│ ║ l.358: (fun x -> x x) (fun x -> x x) +//│ ║ l.360: (fun x -> x x) (fun x -> x x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error @@ -370,22 +372,21 @@ def t (z: nothing) = let x = fun (y: forall 't. 'a -> 't) -> y z in x x :e (fun f -> fun x -> f (f x)) (fun f -> fun x -> f (f x)) (fun v -> fun w -> v) -//│ ╔══[ERROR] Inferred recursive type: 'a +//│ ╔══[ERROR] Inferred recursive type: ? -> ? -> ? +//│ ║ l.374: (fun f -> fun x -> f (f x)) (fun f -> fun x -> f (f x)) (fun v -> fun w -> v) +//│ ╙── ^^^ +//│ res: 'a -> anything -> 'b //│ where -//│ 'a :> anything -> anything -> 'a -//│ ╙── -//│ res: 'a -> 'b -//│ where -//│ 'a :> 'b -//│ 'b :> anything -> anything -> 'a +//│ 'a :> anything -> 'b +//│ 'b :> anything -> 'a //│ = [Function (anonymous)] :RecursiveTypes (fun f -> fun x -> f (f x)) (fun f -> fun x -> f (f x)) (fun v -> fun w -> v) -//│ res: 'a -> 'b +//│ res: 'a -> anything -> 'b //│ where -//│ 'a :> 'b -//│ 'b :> anything -> anything -> 'a +//│ 'a :> anything -> 'b +//│ 'b :> anything -> 'a //│ = [Function (anonymous)] :NoRecursiveTypes @@ -395,22 +396,21 @@ def t (z: nothing) = let x = fun (y: forall 't. 'a -> 't) -> y z in x x :e (fun two -> fun k -> two two k) (fun f -> fun x -> f (f x)) (fun v -> fun w -> v) -//│ ╔══[ERROR] Inferred recursive type: 'a -//│ where -//│ 'a :> anything -> anything -> 'a -//│ ╙── -//│ res: 'a -> 'b +//│ ╔══[ERROR] Inferred recursive type: ? -> ? -> ? +//│ ║ l.398: (fun two -> fun k -> two two k) (fun f -> fun x -> f (f x)) (fun v -> fun w -> v) +//│ ╙── ^^^ +//│ res: 'a -> anything -> 'b //│ where -//│ 'a :> 'b -//│ 'b :> anything -> anything -> 'a +//│ 'a :> anything -> 'b +//│ 'b :> anything -> 'a //│ = [Function (anonymous)] :RecursiveTypes (fun two -> fun k -> two two k) (fun f -> fun x -> f (f x)) (fun v -> fun w -> v) -//│ res: 'a -> 'b +//│ res: 'a -> anything -> 'b //│ where -//│ 'a :> 'b -//│ 'b :> anything -> anything -> 'a +//│ 'a :> anything -> 'b +//│ 'b :> anything -> 'a //│ = [Function (anonymous)] :NoRecursiveTypes @@ -418,26 +418,28 @@ def t (z: nothing) = let x = fun (y: forall 't. 'a -> 't) -> y z in x x // * Rank 5, causes huge blowup. Do not attempt to output skeletons ! // (fun two k -> two two two k)(fun f -x -> f (f x)) (fun v w -> v) +// :d :e (fun two -> fun k -> two two two k) (fun f -> fun x -> f (f x)) (fun v -> fun w -> v) -//│ ╔══[ERROR] Inferred recursive type: 'a +//│ ╔══[ERROR] Inferred recursive type: ? -> 'a //│ where -//│ 'a :> anything -> 'a -//│ ╙── -//│ res: 'a -> 'b +//│ 'a :> ? -> (? | 'a) +//│ ║ l.423: (fun two -> fun k -> two two two k) (fun f -> fun x -> f (f x)) (fun v -> fun w -> v) +//│ ╙── ^^^ +//│ res: 'a -> anything -> 'b //│ where -//│ 'a :> 'b | 'c -//│ 'b :> anything -> 'c -//│ 'c :> anything -> 'a | 'b +//│ 'a :> forall 'c. anything -> 'b | 'c +//│ 'b :> forall 'c. 'c +//│ 'c :> anything -> ('a | 'b) //│ = [Function (anonymous)] :RecursiveTypes (fun two -> fun k -> two two two k) (fun f -> fun x -> f (f x)) (fun v -> fun w -> v) -//│ res: 'a -> 'b +//│ res: 'a -> anything -> 'b //│ where -//│ 'a :> 'b | 'c -//│ 'b :> anything -> 'c -//│ 'c :> anything -> 'a | 'b +//│ 'a :> forall 'c. 'c +//│ 'c :> anything -> ('a | 'b) +//│ 'b :> forall 'c. 'c //│ = [Function (anonymous)] :NoRecursiveTypes @@ -446,45 +448,45 @@ def t (z: nothing) = let x = fun (y: forall 't. 'a -> 't) -> y z in x x (fun two -> fun k -> two two two k) (fun f -> fun x -> f (f x)) (fun v -> fun w -> v) //│ res: 'a -> 'b //│ where -//│ forall 'c. ('c -//│ where -//│ forall 'd 'e 'f. 'd -> ('e -> 'f -//│ where -//│ 'd <: (forall 'g. ('g -//│ where -//│ 'd <: 'e -> 'g)) -> 'f) <: (forall 'h. ('h -//│ where -//│ forall 'i. ('i -//│ where -//│ forall 'd 'j 'k. 'd -> ('j -> 'k -//│ where -//│ 'd <: (forall 'l. ('l -//│ where -//│ 'd <: 'j -> 'l)) -> 'k) <: (forall 'd 'm 'n. 'd -> ('m -> 'n -//│ where -//│ 'd <: (forall 'o. ('o -//│ where -//│ 'd <: 'm -> 'o)) -> 'n)) -> 'i) <: (forall 'p. 'p -> anything -> 'p) -> 'h)) -> 'c) <: (forall 'q. ('q -//│ where -//│ forall 'c. ('c -//│ where -//│ forall 'd 'e 'f. 'd -> ('e -> 'f -//│ where -//│ 'd <: (forall 'g. ('g -//│ where -//│ 'd <: 'e -> 'g)) -> 'f) <: (forall 'h. ('h -//│ where -//│ forall 'i. ('i -//│ where -//│ forall 'd 'j 'k. 'd -> ('j -> 'k -//│ where -//│ 'd <: (forall 'l. ('l -//│ where -//│ 'd <: 'j -> 'l)) -> 'k) <: (forall 'd 'm 'n. 'd -> ('m -> 'n -//│ where -//│ 'd <: (forall 'o. ('o -//│ where -//│ 'd <: 'm -> 'o)) -> 'n)) -> 'i) <: (forall 'p. 'p -> anything -> 'p) -> 'h)) -> 'c) <: 'a -> 'q)) -> 'b +//│ forall 'c. 'c +//│ where +//│ forall 'd 'e 'f. 'e -> ('f -> 'd +//│ where +//│ 'e <: (forall 'g. 'g +//│ where +//│ 'e <: 'f -> 'g) -> 'd) <: (forall 'h. 'h +//│ where +//│ forall 'i. 'i +//│ where +//│ forall 'j 'k 'e. 'e -> ('j -> 'k +//│ where +//│ 'e <: (forall 'l. 'l +//│ where +//│ 'e <: 'j -> 'l) -> 'k) <: (forall 'm 'n 'e. 'e -> ('m -> 'n +//│ where +//│ 'e <: (forall 'o. 'o +//│ where +//│ 'e <: 'm -> 'o) -> 'n)) -> 'i <: (forall 'p. 'p -> anything -> 'p) -> 'h) -> 'c <: (forall 'q. 'q +//│ where +//│ forall 'c. 'c +//│ where +//│ forall 'd 'e 'f. 'e -> ('f -> 'd +//│ where +//│ 'e <: (forall 'g. 'g +//│ where +//│ 'e <: 'f -> 'g) -> 'd) <: (forall 'h. 'h +//│ where +//│ forall 'i. 'i +//│ where +//│ forall 'j 'k 'e. 'e -> ('j -> 'k +//│ where +//│ 'e <: (forall 'l. 'l +//│ where +//│ 'e <: 'j -> 'l) -> 'k) <: (forall 'm 'n 'e. 'e -> ('m -> 'n +//│ where +//│ 'e <: (forall 'o. 'o +//│ where +//│ 'e <: 'm -> 'o) -> 'n)) -> 'i <: (forall 'p. 'p -> anything -> 'p) -> 'h) -> 'c <: 'a -> 'q) -> 'b //│ = [Function (anonymous)] :NoConstrainedTypes @@ -500,15 +502,15 @@ def t (z: nothing) = let x = fun (y: forall 't. 'a -> 't) -> y z in x x (fun h -> (fun x -> h (x x)) (fun x -> h (x x))) (fun f -> fun n -> n (fun v -> fun x -> fun y -> y) k (fun f -> fun x -> f x)(fun g -> fun x -> n (f (n (fun p -> fun s -> s (p (fun x -> fun y -> y)) (fun f -> fun x -> f (p (fun x -> fun y -> y) f x))) (fun s -> s (fun f -> fun x -> x) (fun f -> fun x -> x)) k) g) x)) (fun f -> fun x -> f (f x)) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a <: (('b & 'c & 'd) -> ('e | 'd) | 'f) -> 'c -> 'd & (forall 'g 'h 'i 'j 'k. ((forall 'l. ? -> 'l -> 'l) -> 'k & (forall 'm. ? -> 'm -> 'm) -> 'h -> 'i -> 'j) -> ('k -> (forall 'n. ('j -> 'n & 'h) -> 'i -> 'n) -> 'g) -> 'g) -> (forall 'o. ((forall 'p. ? -> 'p -> 'p) -> (forall 'q. ? -> 'q -> 'q) -> 'o) -> 'o) -> (forall 'r. 'r -> ? -> 'r) -> 'a & (forall 's. ? -> ? -> 's -> 's) -> (forall 'r. 'r -> ? -> 'r) -> (forall 't 'u. ('t -> 'u) -> 't -> 'u) -> (('b -> 'e & 'f & 'v) -> ('c & 'd) -> 'd) -> 'v -> 'f -//│ ║ l.500: (fun h -> (fun x -> h (x x)) (fun x -> h (x x))) (fun f -> fun n -> n (fun v -> fun x -> fun y -> y) k (fun f -> fun x -> f x)(fun g -> fun x -> n (f (n (fun p -> fun s -> s (p (fun x -> fun y -> y)) (fun f -> fun x -> f (p (fun x -> fun y -> y) f x))) (fun s -> s (fun f -> fun x -> x) (fun f -> fun x -> x)) k) g) x)) (fun f -> fun x -> f (f x)) +//│ 'a <: (('b & 'c & 'd) -> ('e | 'd) | 'f) -> 'c -> 'd & (forall 'g 'h 'i 'j 'k. ((forall 'l. ? -> 'l -> 'l) -> 'h & (forall 'm. ? -> 'm -> 'm) -> 'k -> 'g -> 'i) -> ('h -> (forall 'n. ('i -> 'n & 'k) -> 'g -> 'n) -> 'j) -> 'j) -> (forall 'o. ((forall 'p. ? -> 'p -> 'p) -> (forall 'q. ? -> 'q -> 'q) -> 'o) -> 'o) -> (forall 'r. 'r -> ? -> 'r) -> 'a & (forall 's. ? -> ? -> 's -> 's) -> (forall 'r. 'r -> ? -> 'r) -> (forall 't 'u. ('t -> 'u) -> 't -> 'u) -> (('b -> 'e & 'f & 'v) -> ('c & 'd) -> 'd) -> 'v -> 'f +//│ ║ l.502: (fun h -> (fun x -> h (x x)) (fun x -> h (x x))) (fun f -> fun n -> n (fun v -> fun x -> fun y -> y) k (fun f -> fun x -> f x)(fun g -> fun x -> n (f (n (fun p -> fun s -> s (p (fun x -> fun y -> y)) (fun f -> fun x -> f (p (fun x -> fun y -> y) f x))) (fun s -> s (fun f -> fun x -> x) (fun f -> fun x -> x)) k) g) x)) (fun f -> fun x -> f (f x)) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required -//│ ║ l.500: (fun h -> (fun x -> h (x x)) (fun x -> h (x x))) (fun f -> fun n -> n (fun v -> fun x -> fun y -> y) k (fun f -> fun x -> f x)(fun g -> fun x -> n (f (n (fun p -> fun s -> s (p (fun x -> fun y -> y)) (fun f -> fun x -> f (p (fun x -> fun y -> y) f x))) (fun s -> s (fun f -> fun x -> x) (fun f -> fun x -> x)) k) g) x)) (fun f -> fun x -> f (f x)) +//│ ║ l.502: (fun h -> (fun x -> h (x x)) (fun x -> h (x x))) (fun f -> fun n -> n (fun v -> fun x -> fun y -> y) k (fun f -> fun x -> f x)(fun g -> fun x -> n (f (n (fun p -> fun s -> s (p (fun x -> fun y -> y)) (fun f -> fun x -> f (p (fun x -> fun y -> y) f x))) (fun s -> s (fun f -> fun x -> x) (fun f -> fun x -> x)) k) g) x)) (fun f -> fun x -> f (f x)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required -//│ ║ l.500: (fun h -> (fun x -> h (x x)) (fun x -> h (x x))) (fun f -> fun n -> n (fun v -> fun x -> fun y -> y) k (fun f -> fun x -> f x)(fun g -> fun x -> n (f (n (fun p -> fun s -> s (p (fun x -> fun y -> y)) (fun f -> fun x -> f (p (fun x -> fun y -> y) f x))) (fun s -> s (fun f -> fun x -> x) (fun f -> fun x -> x)) k) g) x)) (fun f -> fun x -> f (f x)) +//│ ║ l.502: (fun h -> (fun x -> h (x x)) (fun x -> h (x x))) (fun f -> fun n -> n (fun v -> fun x -> fun y -> y) k (fun f -> fun x -> f x)(fun g -> fun x -> n (f (n (fun p -> fun s -> s (p (fun x -> fun y -> y)) (fun f -> fun x -> f (p (fun x -> fun y -> y) f x))) (fun s -> s (fun f -> fun x -> x) (fun f -> fun x -> x)) k) g) x)) (fun f -> fun x -> f (f x)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error diff --git a/shared/src/test/diff/mlf-examples/ex_propagate.mls b/shared/src/test/diff/mlf-examples/ex_propagate.mls index 945305e1bf..759c7ede61 100644 --- a/shared/src/test/diff/mlf-examples/ex_propagate.mls +++ b/shared/src/test/diff/mlf-examples/ex_propagate.mls @@ -38,7 +38,7 @@ fun x -> ((fun y -> y y) : 'a -> Sid) fun x_ -> (fun y -> y y) //│ res: anything -> ('a -> Sid & 'a) -> Sid //│ = [Function: res] -//│ res: anything -> (forall 'a 'b. ('b -> 'a & 'b) -> 'a) +//│ res: anything -> (forall 'a 'b. ('a -> 'b & 'a) -> 'b) //│ = [Function: res] // let (delta:sid -> sid) = fun x -> x x diff --git a/shared/src/test/diff/mlf-examples/ex_selfapp.mls b/shared/src/test/diff/mlf-examples/ex_selfapp.mls index 18e5f2720b..16d071a205 100644 --- a/shared/src/test/diff/mlf-examples/ex_selfapp.mls +++ b/shared/src/test/diff/mlf-examples/ex_selfapp.mls @@ -73,9 +73,9 @@ rec def foo xs = elim xs ( rec def foo = fun xs -> case xs of Nil -> Nil, Cons -> Cons (xs.head + 1, foo (foo xs.tail)) -//│ foo: 'a -> 'tail +//│ foo: 'a -> (Nil | 'b) //│ where -//│ 'tail :> (Cons[int] with {tail: 'tail}) | Nil +//│ 'b :> Cons[int] with {tail: Nil | 'b} //│ 'a <: (Cons[?] with {head: int, tail: 'a}) | Nil //│ = [Function: foo2] // * An simplified version, easier to type check, just for the record @@ -83,9 +83,9 @@ rec def foo = fun xs -> case xs of { Nil -> Nil | Cons -> Cons (xs.head + 1, foo xs.tail) } -//│ foo: 'a -> 'tail +//│ foo: 'a -> (Nil | 'b) //│ where -//│ 'tail :> (Cons[int] with {tail: 'tail}) | Nil +//│ 'b :> Cons[int] with {tail: Nil | 'b} //│ 'a <: (Cons[?] with {head: int, tail: 'a}) | Nil //│ = [Function: foo3] :NoRecursiveTypes @@ -133,6 +133,11 @@ def build_ = fun g -> g (fun x -> fun xs -> Cons (x, xs)) Nil :e // * Expected: List is a structural equirecursive types and recursive types are disabled build_ : forall 'a. (forall 'b. (('a -> 'b -> 'b) -> 'b -> 'b)) -> List['a] +//│ ╔══[ERROR] Inferred recursive type: 'a +//│ where +//│ 'a :> Cons[?] with {tail: forall 'a. Nil | 'a} +//│ ║ l.130: def build_ = fun g -> g (fun x -> fun xs -> Cons (x, xs)) Nil +//│ ╙── ^^^^^^^^^^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing type ascription; a type annotation may be required //│ ║ l.135: build_ : forall 'a. (forall 'b. (('a -> 'b -> 'b) -> 'b -> 'b)) -> List['a] //│ ║ ^^^^^^ @@ -144,21 +149,22 @@ build_ : forall 'a. (forall 'b. (('a -> 'b -> 'b) -> 'b -> 'b)) -> List['a] def build = fun (g: forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> g (fun x -> fun xs -> Cons (x, xs)) Nil //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a :> Cons[?]\tail & {tail: Nil | 'a} -//│ ╙── +//│ 'a :> Cons[?] with {tail: forall 'a. Nil | 'a} +//│ ║ l.149: def build = fun (g: forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> g (fun x -> fun xs -> Cons (x, xs)) Nil +//│ ╙── ^^^^^^^^^^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required -//│ ║ l.144: def build = fun (g: forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> g (fun x -> fun xs -> Cons (x, xs)) Nil +//│ ║ l.149: def build = fun (g: forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> g (fun x -> fun xs -> Cons (x, xs)) Nil //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. -//│ build: (forall 'b. ('head -> 'b -> 'b) -> 'b -> 'b) -> (error | Nil | 'a) +//│ build: (forall 'b. ('head -> 'b -> 'b) -> 'b -> 'b) -> (Nil | error | 'a) //│ where -//│ 'a :> Cons['head] with {tail: Nil | 'a} +//│ 'a :> Cons['head] with {tail: forall 'a. Nil | 'a} //│ = [Function: build] :RecursiveTypes def build = fun (g: forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> g (fun x -> fun xs -> Cons (x, xs)) Nil //│ build: (forall 'b. ('head -> 'b -> 'b) -> 'b -> 'b) -> (Nil | 'a) //│ where -//│ 'a :> Cons['head] with {tail: Nil | 'a} +//│ 'a :> Cons['head] with {tail: forall 'a. Nil | 'a} //│ = [Function: build1] :NoRecursiveTypes @@ -207,7 +213,7 @@ rec def foldr = fun k -> fun z -> fun xs -> //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: {head: ?, tail: Cons[?] & 'a} -//│ ║ l.204: case xs of +//│ ║ l.210: case xs of //│ ╙── ^^ //│ foldr: ('head -> 'a -> 'a) -> 'a -> 'b -> 'a //│ where @@ -265,7 +271,7 @@ rec def k = fun x -> fun (xs: Ba) -> fun c -> fun n -> c (x + 1) (xs k z c n) //│ <: k: //│ int -> Ba -> Ba //│ ╔══[ERROR] Type error in binding of lambda expression -//│ ║ l.263: rec def k = fun x -> fun (xs: Ba) -> fun c -> fun n -> c (x + 1) (xs k z c n) +//│ ║ l.269: rec def k = fun x -> fun (xs: Ba) -> fun c -> fun n -> c (x + 1) (xs k z c n) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'b` leaks out of its scope //│ ║ l.100: type Ba = forall 'b. (int -> 'b -> 'b) -> 'b -> 'b @@ -277,7 +283,7 @@ rec def k = fun x -> fun (xs: Ba) -> fun c -> fun n -> c (x + 1) (xs k z c n) //│ ║ l.101: type Baa = forall 'a 'b. ('a -> 'b -> 'b) -> 'b -> 'b //│ ╙── ^^ //│ ╔══[ERROR] Type error in def definition -//│ ║ l.263: rec def k = fun x -> fun (xs: Ba) -> fun c -> fun n -> c (x + 1) (xs k z c n) +//│ ║ l.269: rec def k = fun x -> fun (xs: Ba) -> fun c -> fun n -> c (x + 1) (xs k z c n) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'b` leaks out of its scope //│ ║ l.100: type Ba = forall 'b. (int -> 'b -> 'b) -> 'b -> 'b @@ -302,10 +308,10 @@ k = k_ //│ <: k: //│ int -> Ba -> Ba //│ ╔══[ERROR] Type mismatch in def definition: -//│ ║ l.298: k = k_ +//│ ║ l.304: k = k_ //│ ║ ^^^^^^ //│ ╟── function of type `?a -> (forall ?b. ?b -> ?b)` does not match type `'b` -//│ ║ l.223: def z_ = fun c -> fun n -> n +//│ ║ l.229: def z_ = fun c -> fun n -> n //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.100: type Ba = forall 'b. (int -> 'b -> 'b) -> 'b -> 'b @@ -340,10 +346,10 @@ def bfoo xs = build (foldr k z xs) :e def bfoo_ xs = build_ (foldr k_ z_ xs) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.341: def bfoo_ xs = build_ (foldr k_ z_ xs) +//│ ║ l.347: def bfoo_ xs = build_ (foldr k_ z_ xs) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?b -> (forall ?c. ?c -> ?c)` does not match type `Cons[?a] | Nil` -//│ ║ l.223: def z_ = fun c -> fun n -> n +//│ ║ l.229: def z_ = fun c -> fun n -> n //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.33: type List[a] = Nil | Cons[a] @@ -357,9 +363,9 @@ def bfoo_ xs = build_ (foldr k_ z_ xs) //│ = [Function: bfoo_] // * Alt (requires :RecursiveTypes): def bfoo_ xs = build_ (foldr k z_ xs) -//│ bfoo_: 'a -> 'tail +//│ bfoo_: 'a -> (Nil | 'b) //│ where -//│ 'tail :> (Cons[int] with {tail: 'tail}) | Nil +//│ 'b :> Cons[int] with {tail: forall 'b. Nil | 'b} //│ 'a <: (Cons[?] with {head: int, tail: 'a}) | Nil //│ = [Function: bfoo_1] @@ -401,9 +407,9 @@ bfoo lst0 //│ } bfoo_ lst0 -//│ res: 'tail +//│ res: Nil | 'a //│ where -//│ 'tail :> (Cons[int] with {tail: 'tail}) | Nil +//│ 'a :> Cons[int] with {tail: forall 'a. Nil | 'a} //│ = Cons { //│ head: 1, //│ tail: Cons { head: 2, tail: Cons { head: 4, tail: [Cons] } } @@ -430,10 +436,10 @@ def k = fun x -> fun (xs: Baa) -> fun c -> fun n -> c (x + 1) (xs k z c n) //│ <: k: //│ int -> Baa -> Baa //│ ╔══[ERROR] Type mismatch in def definition: -//│ ║ l.428: def k = fun x -> fun (xs: Baa) -> fun c -> fun n -> c (x + 1) (xs k z c n) +//│ ║ l.434: def k = fun x -> fun (xs: Baa) -> fun c -> fun n -> c (x + 1) (xs k z c n) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `int` does not match type `'a` -//│ ║ l.428: def k = fun x -> fun (xs: Baa) -> fun c -> fun n -> c (x + 1) (xs k z c n) +//│ ║ l.434: def k = fun x -> fun (xs: Baa) -> fun c -> fun n -> c (x + 1) (xs k z c n) //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.101: type Baa = forall 'a 'b. ('a -> 'b -> 'b) -> 'b -> 'b @@ -454,10 +460,10 @@ k = k_ // nope //│ <: k: //│ int -> Baa -> Baa //│ ╔══[ERROR] Type mismatch in def definition: -//│ ║ l.452: k = k_ // nope +//│ ║ l.458: k = k_ // nope //│ ║ ^^^^^^ //│ ╟── operator application of type `int` does not match type `'a` -//│ ║ l.446: def k_ = fun x -> fun xs -> fun c -> fun n -> c (x + 1) (xs k z c n) +//│ ║ l.452: def k_ = fun x -> fun xs -> fun c -> fun n -> c (x + 1) (xs k z c n) //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.101: type Baa = forall 'a 'b. ('a -> 'b -> 'b) -> 'b -> 'b @@ -484,16 +490,15 @@ k = k_ // nope //│ 'a <: 'c -> 'd //│ 'c :> 'e -> 'f -> 'd //│ <: 'g -//│ 'e :> 'k_ +//│ 'e :> int -> 'g -> 'e -> 'f -> 'd //│ <: int -> 'a & ? -> 'b -> 'b //│ 'b :> 'e -> 'f -> 'd //│ <: 'g -//│ 'k_ :> int -> 'g -> 'e -> 'f -> 'd +//│ 'g <: (int -> 'g -> 'e -> 'f -> 'd) -> Baa -> 'h //│ 'd :> 'e -> 'f -> 'd //│ <: 'c & 'h -//│ 'g <: 'k_ -> Baa -> 'h //│ 'h <: 'e -> (Baa | 'f) -> 'c -//│ ║ l.470: rec def k_ = fun x -> fun xs -> fun c -> fun n -> c (x + 1) (xs k_ z c n) +//│ ║ l.476: rec def k_ = fun x -> fun xs -> fun c -> fun n -> c (x + 1) (xs k_ z c n) //│ ╙── ^^^^^^^^^ //│ 'k_ //│ where @@ -501,7 +506,7 @@ k = k_ // nope //│ <: k: //│ int -> Baa -> Baa //│ ╔══[ERROR] Cyclic-looking constraint while typing def definition; a type annotation may be required -//│ ║ l.481: k = k_ // nope +//│ ║ l.487: k = k_ // nope //│ ║ ^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ = [Function: k_1] diff --git a/shared/src/test/diff/mlf-examples/ex_shallow.mls b/shared/src/test/diff/mlf-examples/ex_shallow.mls index da62c01949..592f49303f 100644 --- a/shared/src/test/diff/mlf-examples/ex_shallow.mls +++ b/shared/src/test/diff/mlf-examples/ex_shallow.mls @@ -258,7 +258,7 @@ def a0' = a2' a1' //│ ╟── • this reference: //│ ║ l.15: def id x = x //│ ╙── ^ -//│ a0': anything -> ('a -> 'b & 'c -> 'd & 'd -> 'a) -> 'c -> 'b | error +//│ a0': error | anything -> ('a -> 'b & 'c -> 'd & 'd -> 'a) -> 'c -> 'b //│ = [Function: a0$] :e @@ -267,7 +267,7 @@ def a0'_ = a2'_ a1' //│ ║ l.265: def a0'_ = a2'_ a1' //│ ║ ^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. -//│ a0'_: anything -> ('a -> 'b & 'c -> 'd & 'd -> 'a) -> 'c -> 'b | error +//│ a0'_: error | anything -> ('a -> 'b & 'c -> 'd & 'd -> 'a) -> 'c -> 'b //│ = [Function: a0$_] :RecursiveTypes @@ -315,23 +315,23 @@ def a2_ (x: A1) = ) a3 //│ a1: ((forall 'a. 'a -> 'a) -> 'b) -> 'b //│ = [Function: a11] -//│ a3: 'a -> (forall 'b. (anything -> 'b +//│ a3: 'a -> (forall 'b. anything -> 'b //│ where -//│ 'a <: (forall 'c 'd 'e. ('c -> 'd & 'd -> 'e) -> 'c -> 'e) -> 'b)) +//│ 'a <: (forall 'c 'd 'e. ('d -> 'e & 'e -> 'c) -> 'd -> 'c) -> 'b) //│ = [Function: a31] -//│ a2: A1 -> (forall 'a. (anything -> 'a +//│ a2: A1 -> (forall 'a. anything -> 'a //│ where -//│ forall 'b 'c 'd 'e. ('b -> 'c -> 'd) -> ('d -> 'e & 'b) -> 'c -> 'e <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'a)) +//│ forall 'b 'c 'd 'e. ('b -> 'c -> 'd) -> ('d -> 'e & 'b) -> 'c -> 'e <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'a) //│ = [Function: a21] -//│ a2_: A1 -> (forall 'a. (anything -> 'a +//│ a2_: A1 -> (forall 'a. anything -> 'a //│ where -//│ forall 'b 'c 'd 'e. ('b -> 'c -> 'd) -> ('d -> 'e & 'b) -> 'c -> 'e <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'a)) +//│ forall 'b 'c 'd 'e. ('b -> 'c -> 'd) -> ('d -> 'e & 'b) -> 'c -> 'e <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'a) //│ = [Function: a2_1] def a0 = a2 a1 //│ a0: anything -> 'a //│ where -//│ forall 'b 'c 'd 'e. ('e -> 'b -> 'c) -> ('c -> 'd & 'e) -> 'b -> 'd <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'a +//│ forall 'b 'c 'd 'e. ('b -> 'c -> 'd) -> ('d -> 'e & 'b) -> 'c -> 'e <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'a //│ = [Function: a01] def a0_ = a2_ a1 @@ -341,9 +341,9 @@ def a0_ = a2_ a1 //│ = [Function: a0_1] def a1' = fun f -> fun x -> f (f (either id x)) -//│ a1': 'a -> (forall 'b 'c 'd. ('b -> 'd +//│ a1': 'a -> (forall 'b 'c 'd. 'b -> 'd //│ where -//│ 'a <: (forall 'e. 'e -> 'e | 'b) -> 'c & 'c -> 'd)) +//│ 'a <: (forall 'e. 'e -> 'e | 'b) -> 'c & 'c -> 'd) //│ = [Function: a1$1] :e // occurs-check @@ -355,14 +355,14 @@ def a2' (x: A1') = ) a3 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where -//│ 'a :> forall 'b 'a 'c 'd. (nothing -> ('c -> 'd | 'b) -//│ where -//│ 'a | Sid <: (forall 'c 'e 'd. ('c -> 'e & 'e -> 'd) -> 'c -> 'd) -> 'b) +//│ 'a :> forall 'b 'c 'a 'd. nothing -> ('d -> 'b | 'c) +//│ where +//│ 'a | Sid <: (forall 'b 'd 'e. ('d -> 'e & 'e -> 'b) -> 'd -> 'b) -> 'c //│ ║ l.198: type A1' = forall 'a. (('a | Sid) -> 'a) -> 'a -> ('a | Sid) //│ ╙── ^^ -//│ a2': A1' -> (forall 'a. (anything -> 'a +//│ a2': A1' -> (forall 'a. anything -> 'a //│ where -//│ forall 'b 'c 'd 'e. ('b -> 'c -> 'd) -> ('d -> 'e & 'b) -> 'c -> 'e <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'a)) +//│ forall 'b 'c 'd 'e. ('b -> 'c -> 'd) -> ('d -> 'e & 'b) -> 'c -> 'e <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'a) //│ = [Function: a2$1] def a2'_ x = @@ -371,11 +371,11 @@ def a2'_ x = let _ = x y church_two in y church_succ ) a3 -//│ a2'_: ((forall 'a 'b. ('a -> 'b & 'a) -> 'b) -> (forall 'c. 'c -> 'c) -> anything & (forall 'd. 'd -> (forall 'e. (anything -> 'e +//│ a2'_: ((forall 'a 'b. ('a -> 'b & 'a) -> 'b) -> (forall 'c. 'c -> 'c) -> anything & (forall 'd. 'd -> (forall 'e. anything -> 'e //│ where -//│ 'd <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'e))) -> (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> anything) -> (forall 'i. (anything -> 'i +//│ 'd <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'e)) -> (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> anything) -> (forall 'i. anything -> 'i //│ where -//│ forall 'j 'k 'l 'm. ('m -> 'j -> 'k) -> ('k -> 'l & 'm) -> 'j -> 'l <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'i)) +//│ forall 'j 'k 'l 'm. ('j -> 'k -> 'l) -> ('l -> 'm & 'j) -> 'k -> 'm <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'i) //│ = [Function: a2$_1] :e // * [FCP-LIM] see previous version above @@ -415,7 +415,7 @@ def a0' = a2' a1' //│ ╟── • this reference: //│ ║ l.382: def a0' = a2' a1' //│ ╙── ^^^ -//│ a0': anything -> 'a | error +//│ a0': error | anything -> 'a //│ where //│ forall 'b 'c 'd 'e. ('b -> 'c -> 'd) -> ('d -> 'e & 'b) -> 'c -> 'e <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'a //│ = [Function: a0$1] @@ -426,16 +426,16 @@ def a0'_ = a2'_ a1' //│ ║ l.424: def a0'_ = a2'_ a1' //│ ║ ^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. -//│ a0'_: anything -> 'a | error +//│ a0'_: error | anything -> 'a //│ where -//│ forall 'b 'c 'd 'e. ('b -> 'c -> 'd) -> ('d -> 'e & 'b) -> 'c -> 'e <: (forall 'f 'g 'h. ('g -> 'h & 'h -> 'f) -> 'g -> 'f) -> 'a +//│ forall 'b 'c 'd 'e. ('b -> 'c -> 'd) -> ('d -> 'e & 'b) -> 'c -> 'e <: (forall 'f 'g 'h. ('h -> 'f & 'f -> 'g) -> 'h -> 'g) -> 'a //│ = [Function: a0$_2] :RecursiveTypes def a0'_ = a2'_ a1' //│ a0'_: anything -> 'a //│ where -//│ forall 'b 'c 'd 'e. ('d -> 'e -> 'b) -> ('b -> 'c & 'd) -> 'e -> 'c <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'a +//│ forall 'b 'c 'd 'e. ('e -> 'b -> 'c) -> ('c -> 'd & 'e) -> 'b -> 'd <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'a //│ = [Function: a0$_3] :NoRecursiveTypes diff --git a/shared/src/test/diff/mlf-examples/ex_validate.mls b/shared/src/test/diff/mlf-examples/ex_validate.mls index c47814bbb6..85d6c4459c 100644 --- a/shared/src/test/diff/mlf-examples/ex_validate.mls +++ b/shared/src/test/diff/mlf-examples/ex_validate.mls @@ -582,9 +582,9 @@ def main = let _ = print_sep "" x in null //│ main: null -//│ constrain calls : 2473 -//│ annoying calls : 75 -//│ subtyping calls : 13235 +//│ constrain calls : 2450 +//│ annoying calls : 94 +//│ subtyping calls : 13103 @@ -938,13 +938,11 @@ rec def id_ x = if true then x else id_ id_ x //│ ║ l.927: rec def id_ x = if true then x else id_ id_ x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. -//│ id_: 'id_ +//│ id_: 'a -> 'b //│ where -//│ 'id_ :> 'a -> 'b -//│ 'a :> 'id_ +//│ 'a :> 'a -> 'b //│ <: 'b -//│ 'b :> 'id_ -//│ <: 'a -> 'b +//│ 'b := 'a -> 'b //│ = [Function: id_] // let rec (id:sid) x = if true then x else id id x @@ -963,32 +961,30 @@ rec def id x = if true then x else id id x //│ <: 'c //│ 'c :> 'b -> 'c //│ <: 'a -//│ ║ l.958: rec def id x = if true then x else id id x +//│ ║ l.956: rec def id x = if true then x else id id x //│ ╙── ^^^^^ -//│ 'id +//│ 'a -> 'b //│ where -//│ 'id :> 'a -> 'b -//│ 'a :> 'id +//│ 'a :> 'a -> 'b //│ <: 'b -//│ 'b :> 'id -//│ <: 'a -> 'b +//│ 'b := 'a -> 'b //│ <: id: //│ Sid //│ ╔══[ERROR] Cyclic-looking constraint while typing binding of lambda expression; a type annotation may be required -//│ ║ l.958: rec def id x = if true then x else id id x +//│ ║ l.956: rec def id x = if true then x else id id x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ ╔══[ERROR] Type mismatch in def definition: -//│ ║ l.958: rec def id x = if true then x else id id x +//│ ║ l.956: rec def id x = if true then x else id id x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'a` is not a function //│ ║ l.765: type Sid = forall 'a. 'a -> 'a //│ ║ ^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.958: rec def id x = if true then x else id id x +//│ ║ l.956: rec def id x = if true then x else id id x //│ ║ ^^^^^^^ //│ ╟── from reference: -//│ ║ l.958: rec def id x = if true then x else id id x +//│ ║ l.956: rec def id x = if true then x else id id x //│ ║ ^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.765: type Sid = forall 'a. 'a -> 'a diff --git a/shared/src/test/diff/mlf-examples/merge_regression_min.mls b/shared/src/test/diff/mlf-examples/merge_regression_min.mls index 52140e4eb4..4f17ce611b 100644 --- a/shared/src/test/diff/mlf-examples/merge_regression_min.mls +++ b/shared/src/test/diff/mlf-examples/merge_regression_min.mls @@ -6,13 +6,11 @@ def choose: 'a -> 'a -> 'a //│ = rec def id1 x = choose x (id1 id1 x) -//│ id1: 'id1 +//│ id1: 'a -> 'b //│ where -//│ 'id1 :> 'a -> 'b -//│ 'a :> 'id1 +//│ 'a :> 'a -> 'b //│ <: 'b -//│ 'b :> 'id1 -//│ <: 'a -> 'b +//│ 'b := 'a -> 'b //│ = //│ choose is not implemented @@ -40,13 +38,11 @@ id1 id //│ id1 and choose are not implemented rec def id1 x = choose x (id1 id1 x) -//│ id1: 'id1 +//│ id1: 'a -> 'b //│ where -//│ 'id1 :> 'a -> 'b -//│ 'a :> 'id1 +//│ 'a :> 'a -> 'b //│ <: 'b -//│ 'b :> 'id1 -//│ <: 'a -> 'b +//│ 'b := 'a -> 'b //│ = //│ choose is not implemented diff --git a/shared/src/test/diff/mlf-examples/variations_ex_hashtbl.mls b/shared/src/test/diff/mlf-examples/variations_ex_hashtbl.mls index 0348fd6c30..56ddbc3244 100644 --- a/shared/src/test/diff/mlf-examples/variations_ex_hashtbl.mls +++ b/shared/src/test/diff/mlf-examples/variations_ex_hashtbl.mls @@ -72,10 +72,10 @@ table = hashtbl_add table "one" (fun f -> fun x -> f x) rec def find table key = match_list table none (fun h -> fun t -> - if eq h._1 key then some h._2 + if eq h.0 key then some h.1 else find t key ) -//│ find: List[{_1: anything, _2: 'val}] -> anything -> (None | Some['val]) +//│ find: List[{0: anything, 1: 'val}] -> anything -> (None | Some['val]) def nfind table key = let opt = find table key in @@ -83,10 +83,10 @@ def nfind table key = { None -> fun f -> fun x -> x | Some -> opt.val } -//│ nfind: List[{_1: anything, _2: 'val}] -> anything -> (anything -> 'a -> 'a | 'val) +//│ nfind: List[{0: anything, 1: 'val}] -> anything -> (anything -> 'a -> 'a | 'val) -def nfind: List[{_1: anything; _2: 'val}] -> anything -> (anything -> 'a -> 'a | 'val) -//│ nfind: List[{_1: anything, _2: 'val}] -> anything -> ('val | anything -> 'a -> 'a) +def nfind: List[{0: anything; 1: 'val}] -> anything -> (anything -> 'a -> 'a | 'val) +//│ nfind: List[{0: anything, 1: 'val}] -> anything -> ('val | anything -> 'a -> 'a) // * Alternative // def nfind table key = @@ -127,10 +127,10 @@ add (nfind table "one") //│ ╟── Note: constraint arises from reference: //│ ║ l.63: table = hashtbl_add table "one" (fun f -> fun x -> f x) //│ ╙── ^ -//│ res: ChurchInt -> (('a -> ('a & 'b)) -> 'a -> 'b | ChurchInt) | error -//│ constrain calls : 114 -//│ annoying calls : 5 -//│ subtyping calls : 366 +//│ res: error | ChurchInt -> (('a -> ('a & 'b)) -> 'a -> 'b | ChurchInt) +//│ constrain calls : 113 +//│ annoying calls : 7 +//│ subtyping calls : 365 // * On the other hand, in this variation, distributivity saves the day: @@ -150,24 +150,24 @@ add (tmp "one") :stats add (nfind table "one") //│ res: ChurchInt -> (('a -> ('a & 'b)) -> 'a -> 'b | ChurchInt) -//│ constrain calls : 106 -//│ annoying calls : 5 -//│ subtyping calls : 336 +//│ constrain calls : 105 +//│ annoying calls : 7 +//│ subtyping calls : 331 :stats (fun x -> add x) (nfind table "one") //│ res: ChurchInt -> (('a -> ('a & 'b)) -> 'a -> 'b | ChurchInt) -//│ constrain calls : 120 -//│ annoying calls : 5 -//│ subtyping calls : 377 +//│ constrain calls : 119 +//│ annoying calls : 7 +//│ subtyping calls : 372 :stats zog = printInt (add (nfind table "one") (nfind table "two")) //│ zog: string -//│ constrain calls : 320 -//│ annoying calls : 10 -//│ subtyping calls : 985 +//│ constrain calls : 318 +//│ annoying calls : 14 +//│ subtyping calls : 971 :DontGeneralizeArguments // ========================================================================================== @@ -212,7 +212,7 @@ foo 1 2 //│ ╟── from reference: //│ ║ l.63: table = hashtbl_add table "one" (fun f -> fun x -> f x) //│ ╙── ^ -//│ res: 'a -> 'a | error +//│ res: error | 'a -> 'a foo 1 error //│ res: 'a -> 'a diff --git a/shared/src/test/diff/mlscript/Addable.mls b/shared/src/test/diff/mlscript/Addable.mls index 8ca68b7f49..c6deb71626 100644 --- a/shared/src/test/diff/mlscript/Addable.mls +++ b/shared/src/test/diff/mlscript/Addable.mls @@ -4,26 +4,26 @@ class Addable[A] //│ Defined class Addable[=A] //│ Declared Addable.Add: Addable['A] -> 'A -> 'A -class Num: Addable[Num] & { val: int } - method Add that = Num { val = this.val + that.val } -//│ Defined class Num -//│ Defined Num.Add: Num -> {val: int} -> Num +class Numb: Addable[Numb] & { val: int } + method Add that = Numb { val = this.val + that.val } +//│ Defined class Numb +//│ Defined Numb.Add: Numb -> {val: int} -> Numb -class Str: Addable[Str] & { val: string } - method Add that = Str { val = concat this.val that.val } -//│ Defined class Str -//│ Defined Str.Add: Str -> {val: string} -> Str +class Stri: Addable[Stri] & { val: string } + method Add that = Stri { val = concat this.val that.val } +//│ Defined class Stri +//│ Defined Stri.Add: Stri -> {val: string} -> Stri -n = Num { val = 1 } -//│ n: Num & {val: 1} -//│ = Num { val: 1 } +n = Numb { val = 1 } +//│ n: Numb & {val: 1} +//│ = Numb { val: 1 } n.Add n -//│ res: Num -//│ = Num { val: 2 } +//│ res: Numb +//│ = Numb { val: 2 } n.Add -//│ res: Num -> Num +//│ res: Numb -> Numb //│ = [Function: Add] @@ -33,20 +33,20 @@ def addTwo a0 a1 = a0.Add a1 addTwo n n -//│ res: Num -//│ = Num { val: 2 } +//│ res: Numb +//│ = Numb { val: 2 } -s = Str { val = "hey" } -//│ s: Str & {val: "hey"} -//│ = Str { val: 'hey' } +s = Stri { val = "hey" } +//│ s: Stri & {val: "hey"} +//│ = Stri { val: 'hey' } s.Add s -//│ res: Str -//│ = Str { val: 'heyhey' } +//│ res: Stri +//│ = Stri { val: 'heyhey' } addTwo s s -//│ res: Str -//│ = Str { val: 'heyhey' } +//│ res: Stri +//│ = Stri { val: 'heyhey' } @@ -55,8 +55,8 @@ def addSame a = a.Add a //│ = [Function: addSame] addSame n -//│ res: Num -//│ = Num { val: 2 } +//│ res: Numb +//│ = Numb { val: 2 } rec def addNTimes a n = @@ -65,12 +65,12 @@ rec def addNTimes a n = //│ = [Function: addNTimes] addNTimes n 12 -//│ res: Num -//│ = Num { val: 12 } +//│ res: Numb +//│ = Numb { val: 12 } addNTimes s 5 -//│ res: Str -//│ = Str { val: 'heyheyheyheyhey' } +//│ res: Stri +//│ = Stri { val: 'heyheyheyheyhey' } @@ -91,46 +91,44 @@ addNTimes n 12 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+3: addNTimes n 12 //│ ║ ^^^^^^^^^^^ -//│ ╟── integer literal of type `0` is not an instance of type `Num` +//│ ╟── integer literal of type `0` is not an instance of type `Numb` //│ ║ l.+2: if n < 0 then 0 else a.Add (addNTimes a (n - 1)) //│ ║ ^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.7: class Num: Addable[Num] & { val: int } -//│ ╙── ^^^ +//│ ║ l.7: class Numb: Addable[Numb] & { val: int } +//│ ╙── ^^^^ //│ res: error rec def addNTimes a n = if n <= 0 then 0 else a.Add addNTimes a (n - 1) -//│ addNTimes: 'addNTimes +//│ addNTimes: 'a -> int -> 'b //│ where -//│ 'addNTimes :> 'a -> int -> 'b //│ 'a <: Addable['A] -//│ 'A :> 'addNTimes -//│ <: 'a -> int -> 'b +//│ 'A := 'a -> int -> 'b //│ 'b :> 0 addNTimes n 12 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: addNTimes n 12 //│ ║ ^^^^^^^^^^^ -//│ ╟── function of type `?a -> ?b -> ?c` is not an instance of type `Num` +//│ ╟── function of type `?a -> ?b -> ?c` is not an instance of type `Numb` //│ ║ l.102: rec def addNTimes a n = //│ ║ ^^^^^ //│ ║ l.103: if n <= 0 then 0 else a.Add addNTimes a (n - 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.7: class Num: Addable[Num] & { val: int } -//│ ╙── ^^^ +//│ ║ l.7: class Numb: Addable[Numb] & { val: int } +//│ ╙── ^^^^ //│ res: error addSame n n //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: addSame n n //│ ║ ^^^^^^^^^^^ -//│ ╟── application of type `Num & {val: ?val}` is not a function -//│ ║ l.17: n = Num { val = 1 } -//│ ║ ^^^^^^^^^^^^^^^ -//│ ╟── but it flows into reference with expected type `(forall ?a ?val0. ?a) -> ?b` +//│ ╟── application of type `Numb & {val: ?val}` is not a function +//│ ║ l.17: n = Numb { val = 1 } +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ╟── but it flows into reference with expected type `(forall ?a. ?a) -> ?b` //│ ║ l.+1: addSame n n //│ ╙── ^ //│ res: error @@ -139,15 +137,15 @@ addTwo s n //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: addTwo s n //│ ║ ^^^^^^^^^^ -//│ ╟── application of type `Num & {val: ?val}` is not an instance of type `Str` -//│ ║ l.17: n = Num { val = 1 } -//│ ║ ^^^^^^^^^^^^^^^ -//│ ╟── but it flows into reference with expected type `Str` +//│ ╟── application of type `Numb & {val: ?val}` is not an instance of type `Stri` +//│ ║ l.17: n = Numb { val = 1 } +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ╟── but it flows into reference with expected type `Stri` //│ ║ l.+1: addTwo s n //│ ║ ^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.12: class Str: Addable[Str] & { val: string } -//│ ║ ^^^ +//│ ║ l.12: class Stri: Addable[Stri] & { val: string } +//│ ║ ^^^^ //│ ╟── from reference: //│ ║ l.30: def addTwo a0 a1 = a0.Add a1 //│ ╙── ^^ @@ -183,5 +181,5 @@ class Str_bad_0: Addable[Str_bad_0] & { val: string } //│ ║ l.+2: method Add that = Str_bad_0 { val = this.val + that.val } //│ ╙── ^^^^^^^^ //│ Defined class Str_bad_0 -//│ Defined Str_bad_0.Add: Str_bad_0 -> {val: int} -> (error | (Str_bad_0 with {val: error | int})) +//│ Defined Str_bad_0.Add: Str_bad_0 -> {val: int} -> ((Str_bad_0 with {val: error | int}) | error) diff --git a/shared/src/test/diff/mlscript/AdtStyle.mls b/shared/src/test/diff/mlscript/AdtStyle.mls index 030eb7dd96..9291bee0db 100644 --- a/shared/src/test/diff/mlscript/AdtStyle.mls +++ b/shared/src/test/diff/mlscript/AdtStyle.mls @@ -89,7 +89,7 @@ fun x -> | (1, 2, 3) -> 6 | (x, y, 3,) -> x + y | _ -> 0 -//│ res: (int, int, int,) -> int +//│ res: ((int, int, int,),) -> int fun (x, y) -> match (x, y) with @@ -134,7 +134,7 @@ match Cons(0, Nil) with Cons(1, Nil) -> 0 :e match Cons(true, Nil) with Cons(1, Nil) -> 0 -//│ ╔══[ERROR] Type mismatch in adt match expression: +//│ ╔══[ERROR] Type mismatch in ADT pattern matching: //│ ║ l.136: match Cons(true, Nil) with Cons(1, Nil) -> 0 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` diff --git a/shared/src/test/diff/mlscript/AlexJ.mls b/shared/src/test/diff/mlscript/AlexJ.mls new file mode 100644 index 0000000000..b0fa3cb2ad --- /dev/null +++ b/shared/src/test/diff/mlscript/AlexJ.mls @@ -0,0 +1,103 @@ + + +1 +//│ res: 1 +//│ = 1 + +class A: { x: int } +//│ Defined class A + +a = A{ x = 123 } +//│ a: A & {x: 123} +//│ = A { x: 123 } + +a.x +//│ res: 123 +//│ = 123 + + +def foo x = (x, x) +//│ foo: 'a -> ('a, 'a,) +//│ = [Function: foo] + +id +//│ res: 'a -> 'a +//│ = [Function: id] + +r = foo id +//│ r: (forall 'a. 'a -> 'a, forall 'a. 'a -> 'a,) +//│ = [ [Function: id], [Function: id] ] + +def fst(x, _) = x +def snd(_, x) = x +//│ fst: ('a, anything,) -> 'a +//│ = [Function: fst] +//│ snd: (anything, 'a,) -> 'a +//│ = [Function: snd] + +fst(1,2) +//│ res: 1 +//│ = 1 + +:e +fst((1,2)) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.43: fst((1,2)) +//│ ║ ^^^^^^^^^^ +//│ ╟── argument of type `((1, 2,),)` does not match type `(?a, ?b,)` +//│ ║ l.43: fst((1,2)) +//│ ║ ^^^^^^^ +//│ ╟── Note: constraint arises from tuple literal: +//│ ║ l.31: def fst(x, _) = x +//│ ╙── ^^^^^^ +//│ res: error +//│ = [ 1, 2 ] + +def fst((x, _)) = x +def snd((_, x)) = x +//│ fst: (('a, anything,),) -> 'a +//│ = [Function: fst1] +//│ snd: ((anything, 'a,),) -> 'a +//│ = [Function: snd1] + +:e +fst(1,2) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.64: fst(1,2) +//│ ║ ^^^^^^^^ +//│ ╟── argument list of type `(1, 2,)` does not match type `((?a, ?b,),)` +//│ ║ l.64: fst(1,2) +//│ ╙── ^^^^^ +//│ res: error +//│ Runtime error: +//│ TypeError: number 1 is not iterable (cannot read property Symbol(Symbol.iterator)) + +fst((1,2)) +//│ res: 1 +//│ = 1 + +s = fst(r) +//│ s: 'a -> 'a +//│ = [Function: id] + +s "abc" +//│ res: "abc" +//│ = 'abc' + +s 123 +//│ res: 123 +//│ = 123 + +fst r "abc" +//│ res: "abc" +//│ = 'abc' + +fst(r) +//│ res: 'a -> 'a +//│ = [Function: id] + +snd(r) 123 +//│ res: 123 +//│ = 123 + + diff --git a/shared/src/test/diff/mlscript/Arrays.mls b/shared/src/test/diff/mlscript/Arrays.mls index 75e5c547bf..ed765806a8 100644 --- a/shared/src/test/diff/mlscript/Arrays.mls +++ b/shared/src/test/diff/mlscript/Arrays.mls @@ -40,9 +40,9 @@ ty2B = ty2A //│ MyArray['a] //│ <: ty2B: //│ MyArray[MyArray['a]] -//│ constrain calls : 30 +//│ constrain calls : 26 //│ annoying calls : 23 -//│ subtyping calls : 99 +//│ subtyping calls : 63 @@ -78,9 +78,9 @@ ty3B = ty3A //│ 'a //│ where //│ 'a := MyArray[MyArray['a]] -//│ constrain calls : 84 +//│ constrain calls : 78 //│ annoying calls : 23 -//│ subtyping calls : 253 +//│ subtyping calls : 199 :stats ty3A = ty3B @@ -91,9 +91,9 @@ ty3A = ty3B //│ 'a //│ where //│ 'a := MyArray['a] -//│ constrain calls : 84 +//│ constrain calls : 78 //│ annoying calls : 23 -//│ subtyping calls : 253 +//│ subtyping calls : 199 def ty4B: MyArray[MyArray[MyArray['a]]] as 'a //│ ty4B: 'a @@ -135,7 +135,7 @@ ty1A = ty1B //│ ╙── ^^^^^ //│ constrain calls : 5 //│ annoying calls : 5 -//│ subtyping calls : 61 +//│ subtyping calls : 69 :e :stats @@ -154,7 +154,7 @@ ty1B = ty1A //│ ╙── ^^^^^ //│ constrain calls : 5 //│ annoying calls : 5 -//│ subtyping calls : 51 +//│ subtyping calls : 59 :e @@ -172,8 +172,8 @@ ty2A = ty2B //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.35: def ty2B: MyArray[MyArray['a]] //│ ╙── ^^^^^^^^^^^ -//│ constrain calls : 27 -//│ annoying calls : 22 -//│ subtyping calls : 140 +//│ constrain calls : 18 +//│ annoying calls : 29 +//│ subtyping calls : 51 diff --git a/shared/src/test/diff/mlscript/Ascribe.mls b/shared/src/test/diff/mlscript/Ascribe.mls index a48bea35e4..7e8dd8f583 100644 --- a/shared/src/test/diff/mlscript/Ascribe.mls +++ b/shared/src/test/diff/mlscript/Ascribe.mls @@ -12,7 +12,7 @@ //│ ╟── Note: constraint arises from function type: //│ ║ l.7: "hello": 'a -> 'a //│ ╙── ^^^^^^^^ -//│ res: 'a -> ('a | error) +//│ res: 'a -> (error | 'a) //│ = 'hello' { name = "Bob" } : { name: 'a } diff --git a/shared/src/test/diff/mlscript/Baber.mls b/shared/src/test/diff/mlscript/Baber.mls index fe168a6c50..945bb2a4ab 100644 --- a/shared/src/test/diff/mlscript/Baber.mls +++ b/shared/src/test/diff/mlscript/Baber.mls @@ -13,9 +13,9 @@ def f e = case e of :ns f -//│ res: forall 'y 'a 'b 'c 'z 'd. 'c -> ('y | 'z | 'b) +//│ res: forall 'a 'b 'z 'c 'd 'y. 'b -> ('y | 'z | 'c) //│ where -//│ 'c <: #Foo & 'a | (#Foo & 'd | 'b & ~#Foo) & ~#Foo +//│ 'b <: #Foo & 'a | (#Foo & 'd | 'c & ~#Foo) & ~#Foo //│ 'd <: {z: 'z} //│ 'a <: {y: 'y} //│ = [Function: f] diff --git a/shared/src/test/diff/mlscript/BadAlias.mls b/shared/src/test/diff/mlscript/BadAlias.mls index 7874fdf779..ebd2dd8f37 100644 --- a/shared/src/test/diff/mlscript/BadAlias.mls +++ b/shared/src/test/diff/mlscript/BadAlias.mls @@ -142,7 +142,7 @@ type Bot = 'a error: Bot //│ res: Bot //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown type PolyId = 'a -> 'a //│ Defined type alias PolyId diff --git a/shared/src/test/diff/mlscript/BadInherit.mls b/shared/src/test/diff/mlscript/BadInherit.mls index 9b472cb8fa..6e001de84b 100644 --- a/shared/src/test/diff/mlscript/BadInherit.mls +++ b/shared/src/test/diff/mlscript/BadInherit.mls @@ -272,18 +272,33 @@ class Bool //│ Defined class String //│ Defined class Bool +:e "1" : String true : Bool -//│ res: string +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.276: "1" : String +//│ ║ ^^^ +//│ ╟── string literal of type `"1"` is not an instance of type `String` +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.276: "1" : String +//│ ╙── ^^^^^^ +//│ res: String //│ = '1' -//│ res: bool +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.277: true : Bool +//│ ║ ^^^^ +//│ ╟── reference of type `true` is not an instance of type `Bool` +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.277: true : Bool +//│ ╙── ^^^^ +//│ res: Bool //│ = true :e class Weird: {} | {} //│ ╔══[ERROR] cannot inherit from a type union -//│ ║ l.284: class Weird: {} | {} +//│ ║ l.299: class Weird: {} | {} //│ ╙── ^^^^^^^^^^^^^^ @@ -293,7 +308,7 @@ class A type Id[T] = T class B: Id[A] //│ ╔══[ERROR] cannot inherit from a type alias -//│ ║ l.294: class B: Id[A] +//│ ║ l.309: class B: Id[A] //│ ╙── ^^^^^^^^ //│ Defined class A //│ Defined type alias Id[+T] @@ -305,7 +320,7 @@ class B: Id[A] class Class3A class Class3B: Class3A & 'a //│ ╔══[ERROR] cannot inherit from a polymorphic type -//│ ║ l.306: class Class3B: Class3A & 'a +//│ ║ l.321: class Class3B: Class3A & 'a //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ Defined class Class3A diff --git a/shared/src/test/diff/mlscript/BadInherit2.mls b/shared/src/test/diff/mlscript/BadInherit2.mls index d34b702c63..09486a97ba 100644 --- a/shared/src/test/diff/mlscript/BadInherit2.mls +++ b/shared/src/test/diff/mlscript/BadInherit2.mls @@ -16,7 +16,7 @@ class A0: S0[int] & T0[string] //│ ╔══[ERROR] Type mismatch in type definition: //│ ║ l.14: class A0: S0[int] & T0[string] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── type `int` is not an instance of `string` +//│ ╟── type `int` is not an instance of type `string` //│ ║ l.14: class A0: S0[int] & T0[string] //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: @@ -25,7 +25,7 @@ class A0: S0[int] & T0[string] //│ ╔══[ERROR] Type mismatch in type definition: //│ ║ l.14: class A0: S0[int] & T0[string] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── type `string` is not an instance of `int` +//│ ╟── type `string` is not an instance of type `int` //│ ║ l.14: class A0: S0[int] & T0[string] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -41,7 +41,7 @@ class A0: S0[int] & T0[string] //│ ╟── Hint: method Foo0 is abstract //│ ║ l.14: class A0: S0[int] & T0[string] //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ -//│ res: 'A -> ('A | error) +//│ res: 'A -> 'A //│ = undefined :e @@ -50,7 +50,7 @@ class A0_2: S0[int] & T0[string] //│ ╔══[ERROR] Type mismatch in type definition: //│ ║ l.48: class A0_2: S0[int] & T0[string] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── type `int` is not an instance of `string` +//│ ╟── type `int` is not an instance of type `string` //│ ║ l.48: class A0_2: S0[int] & T0[string] //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: @@ -59,7 +59,7 @@ class A0_2: S0[int] & T0[string] //│ ╔══[ERROR] Type mismatch in type definition: //│ ║ l.48: class A0_2: S0[int] & T0[string] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── type `string` is not an instance of `int` +//│ ╟── type `string` is not an instance of type `int` //│ ║ l.48: class A0_2: S0[int] & T0[string] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -104,7 +104,7 @@ class A1: S1 & T1 //│ ╔══[ERROR] Type mismatch in type definition: //│ ║ l.103: class A1: S1 & T1 //│ ║ ^^^^^^^^^^^ -//│ ╟── type `int` is not an instance of `string` +//│ ╟── type `int` is not an instance of type `string` //│ ║ l.93: trait S1: R1[int] //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: @@ -113,7 +113,7 @@ class A1: S1 & T1 //│ ╔══[ERROR] Type mismatch in type definition: //│ ║ l.103: class A1: S1 & T1 //│ ║ ^^^^^^^^^^^ -//│ ╟── type `string` is not an instance of `int` +//│ ╟── type `string` is not an instance of type `int` //│ ║ l.95: trait T1: R1[string] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -169,7 +169,7 @@ class A1_2: S1 & T1 //│ ╔══[ERROR] Type mismatch in type definition: //│ ║ l.167: class A1_2: S1 & T1 //│ ║ ^^^^^^^^^^^^^ -//│ ╟── type `int` is not an instance of `string` +//│ ╟── type `int` is not an instance of type `string` //│ ║ l.93: trait S1: R1[int] //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: @@ -178,7 +178,7 @@ class A1_2: S1 & T1 //│ ╔══[ERROR] Type mismatch in type definition: //│ ║ l.167: class A1_2: S1 & T1 //│ ║ ^^^^^^^^^^^^^ -//│ ╟── type `string` is not an instance of `int` +//│ ╟── type `string` is not an instance of type `int` //│ ║ l.95: trait T1: R1[string] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -188,7 +188,7 @@ class A1_2: S1 & T1 //│ Defined A1_2.Foo1: A1_2 -> nothing //│ // Prelude //│ function error() { -//│ throw new Error("unexpected runtime error"); +//│ throw new Error("an error was thrown"); //│ } //│ class A1_2 { //│ constructor(fields) { @@ -205,7 +205,7 @@ class A1_2: S1 & T1 (A1_2{}).Foo1 //│ res: ('A & (int | string)) -> 'A //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown f = 0 diff --git a/shared/src/test/diff/mlscript/BadInherit2Co.mls b/shared/src/test/diff/mlscript/BadInherit2Co.mls index e9df18f313..bfa7be1f03 100644 --- a/shared/src/test/diff/mlscript/BadInherit2Co.mls +++ b/shared/src/test/diff/mlscript/BadInherit2Co.mls @@ -11,7 +11,7 @@ class A00: S00[int] & T00[string] //│ ╔══[ERROR] Type mismatch in type definition: //│ ║ l.10: class A00: S00[int] & T00[string] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── type `int` is not an instance of `string` +//│ ╟── type `int` is not an instance of type `string` //│ ║ l.10: class A00: S00[int] & T00[string] //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: @@ -20,7 +20,7 @@ class A00: S00[int] & T00[string] //│ ╔══[ERROR] Type mismatch in type definition: //│ ║ l.10: class A00: S00[int] & T00[string] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── type `string` is not an instance of `int` +//│ ╟── type `string` is not an instance of type `int` //│ ║ l.10: class A00: S00[int] & T00[string] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -55,7 +55,7 @@ class A0: S0[int] & T0[string] //│ ╟── Hint: method Foo0 is abstract //│ ║ l.44: class A0: S0[int] & T0[string] //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ -//│ res: error +//│ res: nothing //│ = undefined :e @@ -149,7 +149,7 @@ class A1_2: S1 & T1 //│ Defined A1_2.Foo1: A1_2 -> nothing //│ // Prelude //│ function error() { -//│ throw new Error("unexpected runtime error"); +//│ throw new Error("an error was thrown"); //│ } //│ class A1_2 { //│ constructor(fields) { @@ -166,7 +166,7 @@ class A1_2: S1 & T1 (A1_2{}).Foo1 //│ res: nothing //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown f = 0 diff --git a/shared/src/test/diff/mlscript/BadMethods.mls b/shared/src/test/diff/mlscript/BadMethods.mls index 7ce6102b3f..3a0b768876 100644 --- a/shared/src/test/diff/mlscript/BadMethods.mls +++ b/shared/src/test/diff/mlscript/BadMethods.mls @@ -550,9 +550,9 @@ t = Dup { x = 42 } :stats t : Dup[bool, int] //│ res: Dup[?, int] -//│ constrain calls : 27 -//│ annoying calls : 19 -//│ subtyping calls : 77 +//│ constrain calls : 25 +//│ annoying calls : 21 +//│ subtyping calls : 56 :stats t : Dup[int, bool] @@ -566,23 +566,23 @@ t : Dup[int, bool] //│ ║ l.558: t : Dup[int, bool] //│ ╙── ^^^^ //│ res: Dup[?, bool] -//│ constrain calls : 41 -//│ annoying calls : 22 -//│ subtyping calls : 116 +//│ constrain calls : 30 +//│ annoying calls : 24 +//│ subtyping calls : 77 :stats t.MthDup (fun x -> mul 2 x) //│ res: int -//│ constrain calls : 74 -//│ annoying calls : 19 -//│ subtyping calls : 227 +//│ constrain calls : 72 +//│ annoying calls : 21 +//│ subtyping calls : 206 :stats t.MthDup id //│ res: 42 -//│ constrain calls : 71 -//│ annoying calls : 18 -//│ subtyping calls : 231 +//│ constrain calls : 69 +//│ annoying calls : 20 +//│ subtyping calls : 212 // We don't currently analyze forward method declarations @@ -823,10 +823,10 @@ class Test9B: Test9A[int] //│ ╔══[ERROR] Type mismatch in method declaration: //│ ║ l.815: method Mth9A : Test9A[int] -> int //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── type `Test9A[int] -> int` does not match type `?x` +//│ ╟── type `Test9A[int] -> int` does not match type `int | ?x` //│ ║ l.815: method Mth9A : Test9A[int] -> int //│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ╟── but it flows into method declaration with expected type `?x` +//│ ╟── but it flows into method declaration with expected type `int | ?x` //│ ║ l.815: method Mth9A : Test9A[int] -> int //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from field selection: diff --git a/shared/src/test/diff/mlscript/BadPolym.mls b/shared/src/test/diff/mlscript/BadPolym.mls index 480d847d51..d8695bb76e 100644 --- a/shared/src/test/diff/mlscript/BadPolym.mls +++ b/shared/src/test/diff/mlscript/BadPolym.mls @@ -22,12 +22,12 @@ foo = fooImpl //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.18: foo = fooImpl //│ ║ ^^^^^^^^^^^^^ -//│ ╟── integer literal of type `1` is not an instance of type `string` -//│ ║ l.13: fooImpl f = f 1 -//│ ║ ^ -//│ ╟── Note: constraint arises from type reference: +//│ ╟── type `int` is not a 0-element tuple //│ ║ l.9: def foo: (int -> int & string -> string) -> () -//│ ╙── ^^^^^^ +//│ ║ ^^^ +//│ ╟── Note: constraint arises from tuple type: +//│ ║ l.9: def foo: (int -> int & string -> string) -> () +//│ ╙── ^^ //│ = [Function: fooImpl] foo id @@ -39,35 +39,25 @@ fooImpl id //│ = 1 -:e fooImpl2 (f: int -> int & string -> string) = f 1 -//│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.43: fooImpl2 (f: int -> int & string -> string) = f 1 -//│ ║ ^^^ -//│ ╟── integer literal of type `1` is not an instance of type `string` -//│ ║ l.43: fooImpl2 (f: int -> int & string -> string) = f 1 -//│ ║ ^ -//│ ╟── Note: constraint arises from type reference: -//│ ║ l.43: fooImpl2 (f: int -> int & string -> string) = f 1 -//│ ╙── ^^^^^^ -//│ fooImpl2: (int -> int & string -> string) -> (error | int | string) +//│ fooImpl2: (int -> int & string -> string) -> (int | string) //│ = [Function: fooImpl2] fooImpl2 id -//│ res: error | int | string +//│ res: int | string //│ = 1 :e :re res "oops" //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.62: res "oops" +//│ ║ l.52: res "oops" //│ ║ ^^^^^^^^^^ //│ ╟── type `int` is not a function -//│ ║ l.43: fooImpl2 (f: int -> int & string -> string) = f 1 +//│ ║ l.42: fooImpl2 (f: int -> int & string -> string) = f 1 //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `"oops" -> ?a` -//│ ║ l.62: res "oops" +//│ ║ l.52: res "oops" //│ ╙── ^^^ //│ res: error //│ Runtime error: @@ -96,20 +86,20 @@ bar1 = bar1 error: int -> int & string -> string : (int & string) -> (int | string) //│ res: nothing -> (int | string) //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown // :e // * Did not work before when added subtype checking to constraining... :re error: int -> int & string -> string : int -> int //│ res: int -> int //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown :re error: number -> int & string -> string : int -> number //│ res: int -> number //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown // * The tricky thing in these tests is that we currently lossily merge function types // * in constraint solving, so the best-effort subtyping check may actually accept things @@ -123,7 +113,7 @@ type Als = number -> int error: Als & string -> string : int -> number //│ res: int -> number //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown type Id[A] = A //│ Defined type alias Id[+A] @@ -132,6 +122,6 @@ type Id[A] = A error: Id[Id[Als] & bool -> bool] & string -> string : int -> number //│ res: int -> number //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown diff --git a/shared/src/test/diff/mlscript/BadTraits.mls b/shared/src/test/diff/mlscript/BadTraits.mls index 47c36b7ac7..491024c472 100644 --- a/shared/src/test/diff/mlscript/BadTraits.mls +++ b/shared/src/test/diff/mlscript/BadTraits.mls @@ -72,7 +72,7 @@ T A //│ ╟── Note: class constructor A is defined at: //│ ║ l.1: class A //│ ╙── ^ -//│ res: anything -> A & #T | error +//│ res: error | anything -> A & #T //│ = [Function (anonymous)] :e @@ -99,7 +99,7 @@ T (B {}) t = T error //│ t: nothing //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown :re t: nothing diff --git a/shared/src/test/diff/mlscript/BadTypes.mls b/shared/src/test/diff/mlscript/BadTypes.mls index 4d03245b6a..82e6c43b78 100644 --- a/shared/src/test/diff/mlscript/BadTypes.mls +++ b/shared/src/test/diff/mlscript/BadTypes.mls @@ -137,7 +137,7 @@ type Bad = { x: 'a } error: Bad //│ res: Bad //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown type BadRec = { x: 'a; y: BadRec } //│ Defined type alias BadRec @@ -146,13 +146,13 @@ type BadRec = { x: 'a; y: BadRec } (fun (x: BadRec) -> ()) (error: BadRec) //│ res: () //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown :re error: BadRec //│ res: BadRec //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown def someRec: { y: 'a } as 'a //│ someRec: 'a @@ -165,7 +165,7 @@ someRec: BadRec //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.+1: someRec: BadRec //│ ║ ^^^^^^^ -//│ ╟── type `{y: 'a}` does not have field 'x' +//│ ╟── type `forall 'a. 'a` does not match type `{x: 'a0, y: BadRec}` //│ ║ l.157: def someRec: { y: 'a } as 'a //│ ║ ^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{x: 'a0, y: BadRec}` diff --git a/shared/src/test/diff/mlscript/Basics.mls b/shared/src/test/diff/mlscript/Basics.mls index 2af98bf088..7ecdfea0f7 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(List((None,Fld(_,Var(y)))))),Tup(List((None,Fld(_,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 e632a12518..a9b9b9b18c 100644 --- a/shared/src/test/diff/mlscript/ByNameByValue.mls +++ b/shared/src/test/diff/mlscript/ByNameByValue.mls @@ -14,9 +14,9 @@ def incr x = x.a <- x.a + 1 :p :js 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) +//│ 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),Left(Let(false,Var(n),Rcd(List((Var(a),Fld(m,IntLit(0))))),Lam(Tup(List()),Tup(List((None,Fld(_,App(Var(incr),Tup(List((None,Fld(_,Var(n)))))))), (None,Fld(_,Var(n)))))))),true) //│ // Query 1 //│ globalThis.gensym = function gensym() { //│ return (((n) => () => [ @@ -33,9 +33,9 @@ def gensym = let n = { mut a = 0 } in fun () -> (incr n, n) :js :p 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) +//│ 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),Left(Let(false,Var(n),Rcd(List((Var(a),Fld(m,IntLit(0))))),Lam(Tup(List()),Tup(List((None,Fld(_,App(Var(incr),Tup(List((None,Fld(_,Var(n)))))))), (None,Fld(_,Var(n)))))))),false) //│ // Query 1 //│ globalThis.gensym1 = ((n) => () => [ //│ incr(n), @@ -142,15 +142,15 @@ rec def xs = Cons 0 (Cons 1 xs) //│ return Cons1(0)(Cons1(1)(xs())); //│ }; //│ // End of generated code -//│ xs: 'tail +//│ xs: 'a //│ where -//│ 'tail :> Cons[0 | 1] with {head: 0, tail: Cons[0 | 1] with {head: 1, tail: 'tail}} +//│ 'a :> Cons[0 | 1] with {head: 0, tail: Cons[0 | 1] with {head: 1, tail: forall 'a. 'a}} //│ = [Function: xs] :re xs -//│ res: 'tail +//│ res: 'a //│ where -//│ 'tail :> Cons[0 | 1] with {head: 0, tail: Cons[0 | 1] with {head: 1, tail: 'tail}} +//│ 'a :> Cons[0 | 1] with {head: 0, tail: Cons[0 | 1] with {head: 1, tail: forall 'a. 'a}} //│ Runtime error: //│ RangeError: Maximum call stack size exceeded diff --git a/shared/src/test/diff/mlscript/David.mls b/shared/src/test/diff/mlscript/David.mls index eb8f58cb4c..081e345a75 100644 --- a/shared/src/test/diff/mlscript/David.mls +++ b/shared/src/test/diff/mlscript/David.mls @@ -124,14 +124,14 @@ addOneS "Two" // Attempt 1: def isInt = fun x -> case x of { int -> true | string -> false } -//│ isInt: (int | string) -> bool +//│ isInt: (int | string) -> Bool //│ = [Function: isInt1] isInt 1 isInt "Two" -//│ res: bool +//│ res: Bool //│ = true -//│ res: bool +//│ res: Bool //│ = false def addOne = fun x -> if isInt x then addOneI x else addOneS x diff --git a/shared/src/test/diff/mlscript/David2.mls b/shared/src/test/diff/mlscript/David2.mls index 9b25bc4025..cc4e599152 100644 --- a/shared/src/test/diff/mlscript/David2.mls +++ b/shared/src/test/diff/mlscript/David2.mls @@ -2,15 +2,15 @@ class Integer: { value: int; addOne: Integer -> Integer } //│ Defined class Integer -class Num: Integer & { addOne: Num -> Num } -//│ Defined class Num +class Numb: Integer & { addOne: Numb -> Numb } +//│ Defined class Numb -class Str: { value: string; addOne: Str -> Str } -//│ Defined class Str +class Stri: { value: string; addOne: Stri -> Stri } +//│ Defined class Stri addOne1 x = case x of { | Integer -> x.addOne x - | Num -> x.addOne x + | Numb -> x.addOne x } //│ addOne1: ((Integer\value with {addOne: 'a -> 'b}) & 'a) -> 'b //│ = [Function: addOne1] @@ -18,9 +18,9 @@ addOne1 x = case x of { rec def loopy() = Integer { value = 1; addOne = fun x -> loopy() } -//│ loopy: () -> 'a +//│ loopy: () -> (Integer with {addOne: 'addOne, value: 1}) //│ where -//│ 'a :> Integer with {addOne: anything -> 'a, value: 1} +//│ 'addOne :> anything -> (Integer with {addOne: 'addOne, value: 1}) //│ = [Function: loopy] addOne1 (loopy()) @@ -38,9 +38,9 @@ res : Integer // * so it could be anything; example: funny = loopy() with { value = "oops!" } -//│ funny: 'a\value & {value: "oops!"} +//│ funny: Integer with {addOne: 'addOne, value: "oops!"} //│ where -//│ 'a :> Integer with {addOne: anything -> 'a, value: 1} +//│ 'addOne :> anything -> (Integer with {addOne: 'addOne, value: 1}) //│ = Integer { value: 'oops!', addOne: [Function: addOne] } addOne1 funny @@ -54,7 +54,7 @@ addOne1 funny addOne1 (Integer { value = 1; addOne = fun x -> (error : Integer) } with { value = "oops!" }) //│ res: Integer //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown // * `addOne` is now typed polymorphically, so this line now works! addOne1 ((Integer { value = 1; addOne = id }) with { value = "oops!" }) @@ -70,15 +70,15 @@ addOne1 (Integer { value = 1; addOne = id }) // * Now for properly closing the loop with a constructor for Integer: rec def mkInteger value = Integer { value; addOne = fun n -> mkInteger (n.value + 1) } -//│ mkInteger: int -> 'a +//│ mkInteger: int -> (Integer with {addOne: 'addOne}) //│ where -//│ 'a :> Integer with {addOne: {value: int} -> 'a} +//│ 'addOne :> {value: int} -> (Integer with {addOne: 'addOne}) //│ = [Function: mkInteger] n = mkInteger 42 -//│ n: 'a +//│ n: Integer with {addOne: 'addOne} //│ where -//│ 'a :> Integer with {addOne: {value: int} -> 'a} +//│ 'addOne :> {value: int} -> (Integer with {addOne: 'addOne}) //│ = Integer { value: 42, addOne: [Function: addOne] } n : Integer @@ -102,9 +102,9 @@ def mkInteger2: int -> Integer def mkInteger2 = mkInteger //│ mkInteger2: int -> Integer //│ = -//│ int -> 'a +//│ int -> (Integer with {addOne: 'addOne}) //│ where -//│ 'a :> Integer with {addOne: {value: int} -> 'a} +//│ 'addOne :> {value: int} -> (Integer with {addOne: 'addOne}) //│ <: mkInteger2: //│ int -> Integer //│ = [Function: mkInteger2] @@ -119,9 +119,9 @@ def mkInteger_oops: (int & 'a) -> (Integer & { value: 'a }) :e rec def mkInteger_oops value = Integer { value; addOne = fun n -> mkInteger_oops (n.value + 1) } -//│ int -> 'a +//│ int -> (Integer with {addOne: 'addOne}) //│ where -//│ 'a :> Integer with {addOne: {value: int} -> 'a} +//│ 'addOne :> {value: int} -> (Integer with {addOne: 'addOne}) //│ <: mkInteger_oops: //│ (int & 'a) -> (Integer\addOne with {value: 'a}) //│ ╔══[ERROR] Type mismatch in def definition: @@ -140,7 +140,9 @@ rec def mkInteger_oops value = Integer { value; addOne = fun n -> mkInteger_oops :precise-rec-typing rec def mkInteger_oops value = Integer { value; addOne = fun n -> mkInteger_oops (n.value + 1) } -//│ (int & 'value) -> (Integer with {addOne: {value: int} -> (Integer with {addOne: nothing}), value: 'value}) +//│ (int & 'value) -> (Integer with {addOne: forall 'a. {value: int} -> 'a, value: 'value}) +//│ where +//│ 'a :> Integer with {addOne: forall 'a. {value: int} -> 'a} //│ <: mkInteger_oops: //│ (int & 'a) -> (Integer\addOne with {value: 'a}) //│ = [Function: mkInteger_oops1] @@ -148,13 +150,16 @@ rec def mkInteger_oops value = Integer { value; addOne = fun n -> mkInteger_oops // * We may still want to retain the precise typing of the `value` part: def mkIntegerPrecise value = Integer { value; addOne = addOne1 } -//│ mkIntegerPrecise: (int & 'value) -> (Integer with {addOne: forall 'a 'b. ((Integer\value with {addOne: 'a -> 'b}) & 'a) -> 'b, value: 'value}) +//│ mkIntegerPrecise: (int & 'value) -> (Integer with { +//│ addOne: forall 'a 'b. ((Integer\value with {addOne: 'a -> 'b}) & 'a) -> 'b, +//│ value: 'value +//│ }) //│ = [Function: mkIntegerPrecise] def mkIntegerPrecise value = Integer { value; addOne = fun n -> mkInteger (n.value + 1) } -//│ mkIntegerPrecise: (int & 'value) -> (Integer with {addOne: forall 'a. {value: int} -> 'a, value: 'value}) +//│ mkIntegerPrecise: (int & 'value) -> (Integer with {addOne: forall 'addOne. 'addOne, value: 'value}) //│ where -//│ 'a :> Integer with {addOne: {value: int} -> 'a} +//│ 'addOne :> {value: int} -> (Integer with {addOne: 'addOne}) //│ = [Function: mkIntegerPrecise1] def mkIntegerPrecise value = Integer { value; addOne = fun (n: Integer) -> mkInteger2 (n.value + 1) } @@ -197,45 +202,45 @@ def mkIntegerPrecise3: (int & 'a) -> (Integer & { value: 'a }) // * Note: works with :precise-rec-typing when typing mkInteger :e def mkIntegerPrecise3 = mkInteger -//│ int -> 'a +//│ int -> (Integer with {addOne: 'addOne}) //│ where -//│ 'a :> Integer with {addOne: {value: int} -> 'a} +//│ 'addOne :> {value: int} -> (Integer with {addOne: 'addOne}) //│ <: mkIntegerPrecise3: //│ (int & 'a) -> (Integer\addOne with {value: 'a}) //│ ╔══[ERROR] Type mismatch in def definition: -//│ ║ l.199: def mkIntegerPrecise3 = mkInteger +//│ ║ l.204: def mkIntegerPrecise3 = mkInteger //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `int` does not match type `'a` //│ ║ l.72: rec def mkInteger value = Integer { value; addOne = fun n -> mkInteger (n.value + 1) } //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from type variable: -//│ ║ l.193: def mkIntegerPrecise3: (int & 'a) -> (Integer & { value: 'a }) +//│ ║ l.198: def mkIntegerPrecise3: (int & 'a) -> (Integer & { value: 'a }) //│ ╙── ^^ //│ = [Function: mkIntegerPrecise3] :e -addOne1 (Str { value = ""; addOne = error }) +addOne1 (Stri { value = ""; addOne = error }) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.218: addOne1 (Str { value = ""; addOne = error }) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── application of type `Str & {addOne: ?addOne, value: ?value}` does not match type `Integer & ?a` -//│ ║ l.218: addOne1 (Str { value = ""; addOne = error }) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.223: addOne1 (Stri { value = ""; addOne = error }) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── application of type `Stri & {addOne: ?addOne, value: ?value}` does not match type `Integer & ?a` +//│ ║ l.223: addOne1 (Stri { value = ""; addOne = error }) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.11: addOne1 x = case x of { //│ ╙── ^ //│ res: error //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown addOne2 x = case x of { | Integer -> x.addOne x - | Num -> x.addOne x - | Str -> x.addOne x + | Numb -> x.addOne x + | Stri -> x.addOne x } -//│ addOne2: ((Integer\value with {addOne: 'a -> 'b}) & 'a | (Str\value with {addOne: 'c -> 'b}) & 'c) -> 'b +//│ addOne2: ((Integer\value with {addOne: 'a -> 'b}) & 'a | (Stri\value with {addOne: 'c -> 'b}) & 'c) -> 'b //│ = [Function: addOne2] addOne2 (mkIntegerPrecise 42) @@ -243,35 +248,35 @@ addOne2 (mkIntegerPrecise 42) //│ = Integer { value: 43, addOne: [Function: addOne] } :re -addOne2 (Str { value = ""; addOne = error }) +addOne2 (Stri { value = ""; addOne = error }) //│ res: nothing //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown -def mkStr: string -> Str -rec def mkStr value = Str { value; addOne = fun s -> mkStr (concat s.value "1") } -//│ mkStr: string -> Str +def mkStr: string -> Stri +rec def mkStr value = Stri { value; addOne = fun s -> mkStr (concat s.value "1") } +//│ mkStr: string -> Stri //│ = -//│ string -> 'a +//│ string -> (Stri with {addOne: 'addOne}) //│ where -//│ 'a :> Str with {addOne: {value: string} -> 'a} +//│ 'addOne :> {value: string} -> (Stri with {addOne: 'addOne}) //│ <: mkStr: -//│ string -> Str +//│ string -> Stri //│ = [Function: mkStr] addOne2 (mkStr "hello") -//│ res: Str -//│ = Str { value: 'hello1', addOne: [Function: addOne] } +//│ res: Stri +//│ = Stri { value: 'hello1', addOne: [Function: addOne] } union = if true then mkIntegerPrecise 42 else mkStr "hello" -//│ union: Integer & {value: 42} | Str +//│ union: Integer & {value: 42} | Stri //│ = Integer { value: 42, addOne: [Function: addOne] } union2 = addOne2 union -//│ union2: Integer | Str +//│ union2: Integer | Stri //│ = Integer { value: 43, addOne: [Function: addOne] } addOne2 union2 -//│ res: Integer | Str +//│ res: Integer | Stri //│ = Integer { value: 44, addOne: [Function: addOne] } diff --git a/shared/src/test/diff/mlscript/DavidB.mls b/shared/src/test/diff/mlscript/DavidB.mls index f032c243fd..8f001eb4b7 100644 --- a/shared/src/test/diff/mlscript/DavidB.mls +++ b/shared/src/test/diff/mlscript/DavidB.mls @@ -10,7 +10,7 @@ tuple2 = (4, 5) //│ = [ 4, 5 ] if true then tuple1 else tuple2 -//│ res: Array[1 | 2 | 3 | 4 | 5] & {_1: 1 | 4, _2: 2 | 5} +//│ res: Array[1 | 2 | 3 | 4 | 5] & {0: 1 | 4, 1: 2 | 5} //│ = [ 1, 2, 3 ] arr = tuple1 : Array[int] @@ -98,7 +98,7 @@ list2 = C { h = 3; t = () } //│ = C { h: 3, t: [] } ls = if true then list1 else list2 -//│ ls: C[1 | 2 | 3] with {h: 1 | 3, t: () | (C[2] with {t: ()})} +//│ ls: C[1 | 2 | 3] with {h: 1 | 3, t: (C[2] with {t: ()}) | ()} //│ = C { h: 1, t: C { h: 2, t: [] } } ls : List0[int] @@ -123,7 +123,7 @@ list2 = (4, (5, ())) //│ = [ 4, [ 5, [] ] ] if true then list1 else list2 -//│ res: (1 | 4, (2 | 5, Array[() | 3],),) +//│ res: (1 | 4, (2 | 5, Array[3 | ()],),) //│ = [ 1, [ 2, [ 3, [] ] ] ] @@ -151,31 +151,31 @@ rec def foo xs = case xs of let tmp = foo xs.t.t in if xs.h then Con { h = xs.t.h; t = tmp } else tmp, N -> xs -//│ foo: 'a -> 'T +//│ foo: 'a -> 'b //│ where -//│ 'a <: Con[?, ?] & {h: bool, t: {h: 'h, t: 'a}} | N & 'T -//│ 'T :> Con['h, 'T] +//│ 'a <: Con[?, ?] & {h: bool, t: {h: 'h, t: 'a}} | N & 'b +//│ 'b :> Con['h, 'b] //│ = [Function: foo1] rec def foo xs = case xs of Con -> if xs.h then Con { h = xs.t.h; t = foo xs.t.t } else foo xs.t.t, N -> xs -//│ foo: 'a -> 'T +//│ foo: 'a -> 'b //│ where -//│ 'a <: Con[?, ?] & {h: bool, t: {h: 'h, t: 'a}} | N & 'T -//│ 'T :> Con['h, 'T] +//│ 'a <: Con[?, ?] & {h: bool, t: {h: 'h, t: 'a}} | N & 'b +//│ 'b :> Con['h, 'b] //│ = [Function: foo2] foo (N{}) -//│ res: 'T +//│ res: 'a //│ where -//│ 'T :> Con[nothing, 'T] | N +//│ 'a :> Con[nothing, 'a] | N //│ = N {} foo (Con{ h = true; t = Con{ h = 0; t = N{} } }) -//│ res: 'T +//│ res: 'a //│ where -//│ 'T :> Con[0, 'T] | N +//│ 'a :> Con[0, 'a] | N //│ = Con { h: 0, t: N {} } def inf: Con[true, Con[0, 'I]] as 'I @@ -185,9 +185,9 @@ def inf: Con[true, Con[0, 'I]] as 'I //│ = foo inf -//│ res: 'T +//│ res: 'a //│ where -//│ 'T :> Con[0, 'T] +//│ 'a :> Con[0, 'a] //│ = //│ inf is not implemented @@ -195,12 +195,12 @@ foo inf rec def foo2 xs = case xs of Con -> if xs.h then Con { h = xs.t.h; t = foo xs.t.t } else foo xs.t.t, N -> N{} -//│ foo2: (Con[?, ?] & {h: bool, t: {h: 'h, t: 'a & 'b}} | N) -> ('T | Con['h, forall 'T0. 'T0] | N) +//│ foo2: (Con[?, ?] & {h: bool, t: {h: 'h, t: 'a & 'b}} | N) -> (Con['h, forall 'c. 'c | 'd] | N | 'e) //│ where -//│ 'T0 :> Con['h0, 'T0] | 'c -//│ 'b <: Con[?, ?] & {h: bool, t: {h: 'h1, t: 'b}} | N & 'T -//│ 'T :> Con['h1, 'T] -//│ 'a <: Con[?, ?] & {h: bool, t: {h: 'h0, t: 'a}} | N & 'c +//│ 'c :> Con['h0, 'c | 'd] | 'd +//│ 'b <: Con[?, ?] & {h: bool, t: {h: 'h1, t: 'b}} | N & 'e +//│ 'e :> Con['h1, 'e] +//│ 'a <: Con[?, ?] & {h: bool, t: {h: 'h0, t: 'a}} | N & 'd //│ = [Function: foo21] diff --git a/shared/src/test/diff/mlscript/Didier.mls b/shared/src/test/diff/mlscript/Didier.mls new file mode 100644 index 0000000000..8628cc85d7 --- /dev/null +++ b/shared/src/test/diff/mlscript/Didier.mls @@ -0,0 +1,83 @@ + + +def either x y = if 42 == 6 * 7 then x else y +def fst((a,b)) = a +def snd((a,b)) = b +//│ either: 'a -> 'a -> 'a +//│ = [Function: either] +//│ fst: (('a, anything,),) -> 'a +//│ = [Function: fst] +//│ snd: ((anything, 'a,),) -> 'a +//│ = [Function: snd] + +def K : 'A -> 'B -> 'B +K a b = b +type Sid = 'A -> 'A +def id : Sid +id x = x +def auto : Sid -> Sid +auto f = f f +//│ Defined type alias Sid +//│ K: anything -> 'B -> 'B +//│ = +//│ anything -> 'a -> 'a +//│ <: K: +//│ anything -> 'B -> 'B +//│ = [Function: K] +//│ id: Sid +//│ = +//│ 'a -> 'a +//│ <: id: +//│ Sid +//│ = [Function: id1] +//│ auto: Sid -> Sid +//│ = +//│ ('a -> 'b & 'a) -> 'b +//│ <: auto: +//│ Sid -> Sid +//│ = [Function: auto] + +def trial = + fun x -> + (fun z -> + K (auto (either x (fst z) : Sid)) + (snd z)) + ((id, id)) + 1 +//│ trial: Sid -> 1 +//│ = [Function: trial] + +trial id +//│ res: 1 +//│ = 1 + + +// * In a discussion regarding a declarative system using boxes + +let px = + (fun x -> + let pz = + (fun z -> + (let e = (either x (fst z) : forall 'A. 'A -> 'A) in + // (** [1] forces x and z to have the same tag, + // or to be unboxed with {} marks *) + K (auto e) e, + // (** [2] returning e requires to stay boxed, for + // implicit use of polymorphism below *) + snd z)) + ((id, id)) in + (fst pz + // (** [3] returns the first component unchanged with x=z1-tag, + // still in scope *) + , snd pz 1 + // (** [4] this requires pz to be tagged, with z2-tag, which is + // no longer in scope and can be instantiated *) + )) + id + in + fst px 0 + // (** the z1=x tag is no more in scope so it can be instantiated *) +//│ res: 0 +//│ = 0 + + diff --git a/shared/src/test/diff/mlscript/ExplicitVariance.mls b/shared/src/test/diff/mlscript/ExplicitVariance.mls index 850a7510fd..1d07605fc6 100644 --- a/shared/src/test/diff/mlscript/ExplicitVariance.mls +++ b/shared/src/test/diff/mlscript/ExplicitVariance.mls @@ -33,5 +33,5 @@ class Covar[A] (error : Covar[{ x: int }]) : Covar[{}] //│ res: Covar[?] //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown diff --git a/shared/src/test/diff/mlscript/ExprProb.mls b/shared/src/test/diff/mlscript/ExprProb.mls index 4ed297ac21..4425dbc979 100644 --- a/shared/src/test/diff/mlscript/ExprProb.mls +++ b/shared/src/test/diff/mlscript/ExprProb.mls @@ -50,13 +50,13 @@ rec def eval1_stub e = case e of { | Add -> eval1_stub e.lhs | _ -> 0 } -//│ eval1_stub: forall 'a 'b 'c 'lhs 'd 'eval1_stub 'e. 'eval1_stub +//│ eval1_stub: forall 'a 'b 'c 'd 'e 'lhs 'eval1_stub. 'eval1_stub //│ where -//│ 'eval1_stub := 'd -> (1 | 'e | 0) -//│ 'e :> 1 | 'e | 0 -//│ 'd <: #Lit & 'a | (#Add & 'b | 'c & ~#Add) & ~#Lit -//│ 'b <: {lhs: 'lhs} -//│ 'lhs <: 'd +//│ 'eval1_stub := 'a -> (1 | 'd | 0) +//│ 'd :> 1 | 'd | 0 +//│ 'a <: #Lit & 'c | (#Add & 'e | 'b & ~#Add) & ~#Lit +//│ 'e <: {lhs: 'lhs} +//│ 'lhs <: 'a //│ = [Function: eval1_stub2] eval1_stub @@ -78,35 +78,35 @@ rec def eval1 k e = case e of { //│ = [Function: eval1] //│ constrain calls : 79 //│ annoying calls : 0 -//│ subtyping calls : 313 +//│ subtyping calls : 327 :ns eval1 -//│ res: forall 'a 'b 'c 'd 'e 'f 'val 'rhs 'lhs 'eval1 'g. 'eval1 -//│ where -//│ 'eval1 := 'a -> 'b -> ('val | 'd | 'f) -//│ 'd := int -//│ 'b <: #Lit & 'e | (#Add & 'g | 'c & ~#Add) & ~#Lit -//│ 'g <: {rhs: 'rhs} & {lhs: 'lhs} -//│ 'lhs <: 'b -//│ 'rhs <: 'b -//│ 'e <: {val: 'val} +//│ res: forall 'val 'a 'eval1 'b 'c 'd 'e 'lhs 'rhs 'f 'g. 'eval1 +//│ where +//│ 'eval1 := 'b -> 'd -> ('val | 'c | 'g) +//│ 'c := int +//│ 'd <: #Lit & 'f | (#Add & 'a | 'e & ~#Add) & ~#Lit +//│ 'a <: {rhs: 'rhs} & {lhs: 'lhs} +//│ 'lhs <: 'd +//│ 'rhs <: 'd +//│ 'f <: {val: 'val} //│ 'val <: int -//│ 'a <: 'c -> 'f -//│ 'f <: int +//│ 'b <: 'e -> 'g +//│ 'g <: int //│ = [Function: eval1] :re error: ~Add[?] //│ res: ~Add[nothing] //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown :re error: ('a & ~Lit) -> 'a //│ res: ('a & ~Lit) -> 'a //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown :re error: ('a) -> ('a & Add[?]) @@ -114,19 +114,19 @@ error: ('a) -> ('a & ~Add[?]) error: ('a & ~Add[?]) -> 'a //│ res: 'a -> (Add[?] & 'a) //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown //│ res: 'a -> ('a & ~Add[nothing]) //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown //│ res: ('a & ~Add[?]) -> 'a //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown :re error: ('a & ~add) -> 'a //│ res: ('a & ~#Add) -> 'a //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown :ns def eval1_ty_ugly: ('a -> int) -> (Lit | Add['b] | 'a & ~Lit & ~Add[?] as 'b) -> int @@ -138,7 +138,7 @@ def eval1_ty_ugly: ('a -> int) -> (Lit | Add['b] | 'a & ~Lit & ~Add[?] as 'b) -> eval1_ty_ugly //│ res: ('a -> int) -> 'b -> int //│ where -//│ 'b <: 'a & ~Add[?] & ~Lit | Add['b] | Lit +//│ 'b <: Add['b] | Lit | 'a & ~Add[?] & ~Lit //│ = //│ eval1_ty_ugly is not implemented @@ -150,15 +150,15 @@ def eval1_ty_ugly = eval1 //│ <: eval1_ty_ugly: //│ ('a -> int) -> 'b -> int //│ where -//│ 'b <: 'a & ~Add[?] & ~Lit | Add['b] | Lit +//│ 'b <: Add['b] | Lit | 'a & ~Add[?] & ~Lit //│ = [Function: eval1_ty_ugly] //│ constrain calls : 77 //│ annoying calls : 37 -//│ subtyping calls : 713 +//│ subtyping calls : 640 :ns def eval1_ty: ('a -> int) -> (Lit | Add['b] | 'a & ~lit & ~add as 'b) -> int -//│ eval1_ty: forall 'b 'a. ('a -> int) -> 'b -> int +//│ eval1_ty: forall 'a 'b. ('a -> int) -> 'b -> int //│ where //│ 'b := Lit | Add['b] | 'a & ~#Lit & ~#Add //│ = @@ -166,7 +166,7 @@ def eval1_ty: ('a -> int) -> (Lit | Add['b] | 'a & ~lit & ~add as 'b) -> int eval1_ty //│ res: ('a -> int) -> 'b -> int //│ where -//│ 'b <: 'a & ~#Add & ~#Lit | Add['b] | Lit +//│ 'b <: Add['b] | Lit | 'a & ~#Add & ~#Lit //│ = //│ eval1_ty is not implemented @@ -178,39 +178,39 @@ def eval1_ty = eval1 //│ <: eval1_ty: //│ ('a -> int) -> 'b -> int //│ where -//│ 'b <: 'a & ~#Add & ~#Lit | Add['b] | Lit +//│ 'b <: Add['b] | Lit | 'a & ~#Add & ~#Lit //│ = [Function: eval1_ty] //│ constrain calls : 77 //│ annoying calls : 37 -//│ subtyping calls : 711 +//│ subtyping calls : 636 :stats eval1_ty_ugly = eval1_ty //│ ('a -> int) -> 'b -> int //│ where -//│ 'b <: 'a & ~#Add & ~#Lit | Add['b] | Lit +//│ 'b <: Add['b] | Lit | 'a & ~#Add & ~#Lit //│ <: eval1_ty_ugly: //│ ('a -> int) -> 'b -> int //│ where -//│ 'b <: 'a & ~Add[?] & ~Lit | Add['b] | Lit +//│ 'b <: Add['b] | Lit | 'a & ~Add[?] & ~Lit //│ = [Function: eval1] //│ constrain calls : 36 //│ annoying calls : 33 -//│ subtyping calls : 442 +//│ subtyping calls : 372 :stats eval1_ty = eval1_ty_ugly //│ ('a -> int) -> 'b -> int //│ where -//│ 'b <: 'a & ~Add[?] & ~Lit | Add['b] | Lit +//│ 'b <: Add['b] | Lit | 'a & ~Add[?] & ~Lit //│ <: eval1_ty: //│ ('a -> int) -> 'b -> int //│ where -//│ 'b <: 'a & ~#Add & ~#Lit | Add['b] | Lit +//│ 'b <: Add['b] | Lit | 'a & ~#Add & ~#Lit //│ = [Function: eval1] //│ constrain calls : 208 //│ annoying calls : 529 -//│ subtyping calls : 4578 +//│ subtyping calls : 2710 // Workaround: @@ -236,7 +236,7 @@ def eval1_ty = eval1 //│ = [Function: eval1_ty2] //│ constrain calls : 73 //│ annoying calls : 37 -//│ subtyping calls : 587 +//│ subtyping calls : 519 :stats @@ -251,7 +251,7 @@ rec def pretty1 k e = case e of { //│ = [Function: pretty1] //│ constrain calls : 87 //│ annoying calls : 0 -//│ subtyping calls : 350 +//│ subtyping calls : 363 :stats @@ -269,7 +269,7 @@ rec def prettier1 k ev e = case e of { //│ = [Function: prettier1] //│ constrain calls : 293 //│ annoying calls : 0 -//│ subtyping calls : 1016 +//│ subtyping calls : 1021 :stats rec def prettier11 k ev e = case e of { @@ -281,12 +281,13 @@ rec def prettier11 k ev e = case e of { } //│ prettier11: ('a -> string) -> ('rhs -> number) -> 'b -> string //│ where -//│ 'b <: Add[?] & {lhs: 'c, rhs: 'rhs & 'b} | Lit | 'a & ~#Add & ~#Lit -//│ 'c <: Add[?] & {lhs: 'c, rhs: 'c} | Lit | 'a & ~#Add & ~#Lit +//│ 'b <: Add[?] & {lhs: Add[?] & 'c | Lit | 'a & ~#Add & ~#Lit, rhs: 'rhs & 'b} | Lit | 'a & ~#Add & ~#Lit +//│ 'c <: {lhs: 'd, rhs: 'd} +//│ 'd <: Add[?] & 'c | Lit | 'a & ~#Add & ~#Lit //│ = [Function: prettier11] //│ constrain calls : 191 //│ annoying calls : 0 -//│ subtyping calls : 724 +//│ subtyping calls : 799 // Doesn't make much sense, but generates very ugly type unless aggressively simplified: :stats @@ -297,14 +298,15 @@ rec def prettier12 k ev e = case e of { in if ev e == 0 then tmp else concat tmp (pretty1 k e.rhs) | _ -> k e } -//│ prettier12: ('a -> string & 'b -> 'c) -> ('d -> number) -> (Add[?] & {lhs: 'e, rhs: 'f} & 'd | Lit | 'b & ~#Add & ~#Lit) -> (string | 'c) +//│ prettier12: ('a -> string & 'b -> 'c) -> ('d -> number) -> (Add[?] & {lhs: Add[?] & 'e | Lit | 'a & ~#Add & ~#Lit, rhs: 'f} & 'd | Lit | 'b & ~#Add & ~#Lit) -> (string | 'c) //│ where //│ 'f <: Add[?] & {lhs: 'f, rhs: 'f} | Lit | 'a & ~#Add & ~#Lit -//│ 'e <: Add[?] & {lhs: 'e, rhs: 'e} | Lit | 'a & ~#Add & ~#Lit +//│ 'e <: {lhs: 'g, rhs: 'g} +//│ 'g <: Add[?] & 'e | Lit | 'a & ~#Add & ~#Lit //│ = [Function: prettier12] //│ constrain calls : 166 //│ annoying calls : 0 -//│ subtyping calls : 741 +//│ subtyping calls : 841 :stats @@ -314,7 +316,10 @@ pretty1 done e1 prettier1 done (eval1 done) e1 prettier11 done (eval1 done) e1 prettier12 done (eval1 done) e1 -//│ e1: Add[Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} | Lit & {val: 1}] with {lhs: Lit & {val: 1}, rhs: Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}}} +//│ e1: Add[Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} | Lit & {val: 1}] with { +//│ lhs: Lit & {val: 1}, +//│ rhs: Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} +//│ } //│ = Add { //│ lhs: Lit { val: 1 }, //│ rhs: Add { lhs: Lit { val: 2 }, rhs: Lit { val: 3 } } @@ -331,7 +336,7 @@ prettier12 done (eval1 done) e1 //│ = '123' //│ constrain calls : 1315 //│ annoying calls : 500 -//│ subtyping calls : 13379 +//│ subtyping calls : 12414 e1 = add (lit 1) (add (lit 2) (lit 3)) @@ -340,7 +345,10 @@ pretty1 done e1 prettier1 done (eval1 done) e1 prettier11 done (eval1 done) e1 prettier12 done (eval1 done) e1 -//│ e1: Add[Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} | Lit & {val: 1}] with {lhs: Lit & {val: 1}, rhs: Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}}} +//│ e1: Add[Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} | Lit & {val: 1}] with { +//│ lhs: Lit & {val: 1}, +//│ rhs: Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} +//│ } //│ = Add { //│ lhs: Lit { val: 1 }, //│ rhs: Add { lhs: Lit { val: 2 }, rhs: Lit { val: 3 } } @@ -391,23 +399,26 @@ rec def prettier2 k ev = prettier1 (fun x -> case x of { //│ = [Function: prettier2] //│ constrain calls : 136 //│ annoying calls : 0 -//│ subtyping calls : 599 +//│ subtyping calls : 622 :stats rec def prettier22 k ev = prettier12 (fun x -> case x of { | Nega -> concat "-" (prettier22 k ev x.arg) | _ -> k x }) ev -//│ prettier22: ('a -> string) -> ('b -> number) -> 'arg -> string -//│ where -//│ 'b <: {lhs: 'c, rhs: 'd} -//│ 'd <: Add[?] & {lhs: 'd, rhs: 'd} | Lit | Nega[?] & {arg: 'arg} | 'a & ~#Add & ~#Lit & ~#Nega -//│ 'c <: Add[?] & {lhs: 'c, rhs: 'c} | Lit | Nega[?] & {arg: 'arg} | 'a & ~#Add & ~#Lit & ~#Nega -//│ 'arg <: Add[?] & 'b | Lit | (Nega[?] & {arg: 'arg} | 'a & ~#Nega) & ~#Add & ~#Lit +//│ prettier22: ('a -> string) -> ('b -> number) -> (Add[?] & 'b | Lit | 'c & ~#Add & ~#Lit) -> string +//│ where +//│ 'b <: {lhs: Add[?] & 'd | Lit | 'e & ~#Add & ~#Lit, rhs: 'f} +//│ 'f <: Add[?] & {lhs: 'f, rhs: 'f} | Lit | Nega[?] & {arg: 'arg} | 'a & ~#Add & ~#Lit & ~#Nega +//│ 'd <: {lhs: 'g, rhs: 'g} +//│ 'g <: Add[?] & 'd | Lit | 'e & ~#Add & ~#Lit +//│ 'e <: Nega[?] & {arg: 'arg} | 'a & ~#Nega +//│ 'arg <: Add[?] & 'b | Lit | 'c & ~#Add & ~#Lit +//│ 'c <: Nega[?] & {arg: 'arg} | 'a & ~#Nega //│ = [Function: prettier22] //│ constrain calls : 208 //│ annoying calls : 0 -//│ subtyping calls : 970 +//│ subtyping calls : 1063 @@ -417,10 +428,19 @@ eval2 done e1 //│ = 6 //│ constrain calls : 177 //│ annoying calls : 60 -//│ subtyping calls : 1388 +//│ subtyping calls : 1263 e2 = add (lit 1) (nega e1) -//│ e2: Add[Lit & {val: 1} | Nega[Add[Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} | Lit & {val: 1}] with {lhs: Lit & {val: 1}, rhs: Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}}}]] with {lhs: Lit & {val: 1}, rhs: Nega[Add[Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} | Lit & {val: 1}] with {lhs: Lit & {val: 1}, rhs: Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}}}]} +//│ e2: Add[Lit & {val: 1} | Nega[Add[Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} | Lit & {val: 1}] with { +//│ lhs: Lit & {val: 1}, +//│ rhs: Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} +//│ }]] with { +//│ lhs: Lit & {val: 1}, +//│ rhs: Nega[Add[Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} | Lit & {val: 1}] with { +//│ lhs: Lit & {val: 1}, +//│ rhs: Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} +//│ }] +//│ } //│ = Add { //│ lhs: Lit { val: 1 }, //│ rhs: Nega { arg: Add { lhs: [Lit], rhs: [Add] } } @@ -432,7 +452,7 @@ eval2 done e2 //│ = -5 //│ constrain calls : 275 //│ annoying calls : 103 -//│ subtyping calls : 2134 +//│ subtyping calls : 1927 d2 = nega (add (lit 1) (nega (lit 1))) //│ d2: Nega[Add[Lit & {val: 1} | Nega[Lit & {val: 1}]] with {lhs: Lit & {val: 1}, rhs: Nega[Lit & {val: 1}]}] @@ -444,7 +464,7 @@ eval2 done d2 //│ = 0 //│ constrain calls : 183 //│ annoying calls : 71 -//│ subtyping calls : 1339 +//│ subtyping calls : 1201 prettier2 done @@ -456,13 +476,15 @@ prettier2 done //│ = [Function (anonymous)] prettier22 done -//│ res: ('a -> number) -> 'arg -> string +//│ res: ('a -> number) -> (Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit) -> string //│ where -//│ 'a <: {lhs: 'b, rhs: 'c} -//│ 'c <: Add[?] & {lhs: 'c, rhs: 'c} | Lit | 'd -//│ 'b <: Add[?] & {lhs: 'b, rhs: 'b} | Lit | 'd +//│ 'a <: {lhs: Add[?] & 'c | Lit | 'd & ~#Add & ~#Lit, rhs: 'e} +//│ 'e <: Add[?] & {lhs: 'e, rhs: 'e} | Lit | Nega[?] & {arg: 'arg} +//│ 'c <: {lhs: 'f, rhs: 'f} +//│ 'f <: Add[?] & 'c | Lit | 'd & ~#Add & ~#Lit //│ 'd <: Nega[?] & {arg: 'arg} -//│ 'arg <: Add[?] & 'a | Lit | 'd & ~#Add & ~#Lit +//│ 'arg <: Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit +//│ 'b <: Nega[?] & {arg: 'arg} //│ = [Function (anonymous)] :stats @@ -476,17 +498,20 @@ prettier2 done (eval1 done) //│ = [Function (anonymous)] //│ constrain calls : 101 //│ annoying calls : 0 -//│ subtyping calls : 554 +//│ subtyping calls : 567 prettier22 done (eval1 done) -//│ res: 'arg -> string -//│ where -//│ 'arg <: Add[?] & {lhs: 'a, rhs: 'a} & 'b | Lit | 'c & ~#Add & ~#Lit -//│ 'b <: Add[?] & {lhs: 'b, rhs: 'b} | Lit -//│ 'a <: Add[?] & {lhs: 'a, rhs: 'a} | Lit | 'c -//│ 'c <: Nega[?] & {arg: 'arg} +//│ res: (Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit) -> string +//│ where +//│ 'a <: {lhs: Add[?] & 'c | Lit | 'b & ~#Add & ~#Lit, rhs: 'd} & 'e +//│ 'e <: Add[?] & {lhs: 'e, rhs: 'e} | Lit +//│ 'd <: Add[?] & {lhs: 'd, rhs: 'd} | Lit | Nega[?] & {arg: 'arg} +//│ 'c <: {lhs: 'f, rhs: 'f} +//│ 'f <: Add[?] & 'c | Lit | 'b & ~#Add & ~#Lit +//│ 'b <: Nega[?] & {arg: 'arg} +//│ 'arg <: Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit //│ = [Function (anonymous)] // * TODO could probably merge `c` and `b` here! @@ -504,7 +529,7 @@ prettier2 done (eval2 done) //│ = [Function (anonymous)] //│ constrain calls : 111 //│ annoying calls : 0 -//│ subtyping calls : 758 +//│ subtyping calls : 783 prettier2 done (eval2 done) e2 prettier2 done (eval2 done) d2 @@ -517,13 +542,16 @@ prettier2 done (eval2 done) d2 prettier22 done (eval2 done) prettier22 done (eval2 done) e2 prettier22 done (eval2 done) d2 -//│ res: 'arg -> string -//│ where -//│ 'arg <: Add[?] & {lhs: 'a, rhs: 'a} & 'b | Lit | 'c & ~#Add & ~#Lit -//│ 'b <: Add[?] & {lhs: 'b, rhs: 'b} | Lit | 'd & ~#Add & ~#Lit -//│ 'd <: Nega[?] & {arg: 'b} -//│ 'a <: Add[?] & {lhs: 'a, rhs: 'a} | Lit | 'c -//│ 'c <: Nega[?] & {arg: 'arg} +//│ res: (Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit) -> string +//│ where +//│ 'a <: {lhs: Add[?] & 'c | Lit | 'b & ~#Add & ~#Lit, rhs: 'd} & 'e +//│ 'e <: Add[?] & {lhs: 'e, rhs: 'e} | Lit | 'f & ~#Add & ~#Lit +//│ 'f <: Nega[?] & {arg: 'e} +//│ 'd <: Add[?] & {lhs: 'd, rhs: 'd} | Lit | Nega[?] & {arg: 'arg} +//│ 'c <: {lhs: 'g, rhs: 'g} +//│ 'g <: Add[?] & 'c | Lit | 'b & ~#Add & ~#Lit +//│ 'b <: Nega[?] & {arg: 'arg} +//│ 'arg <: Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit //│ = [Function (anonymous)] //│ res: string //│ = '1-123' @@ -531,7 +559,7 @@ prettier22 done (eval2 done) d2 //│ = '-1' //│ constrain calls : 1178 //│ annoying calls : 390 -//│ subtyping calls : 10943 +//│ subtyping calls : 10299 @@ -548,7 +576,7 @@ eval1 done e2 //│ ║ l.+1: eval1 done e2 //│ ║ ^^^^^^^^^^^^^ //│ ╟── application of type `Nega[?E] & {Nega#E = ?E, arg: ?arg}` does not match type `nothing` -//│ ║ l.362: def nega arg = Nega { arg } +//│ ║ l.370: def nega arg = Nega { arg } //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.4: def done x = case x of {} @@ -603,7 +631,7 @@ 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.362: def nega arg = Nega { arg } +//│ ║ l.370: def nega arg = Nega { arg } //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.4: def done x = case x of {} @@ -616,7 +644,7 @@ prettier2 done (eval1 done) e2 //│ Error: non-exhaustive case expression //│ constrain calls : 710 //│ annoying calls : 238 -//│ subtyping calls : 9186 +//│ subtyping calls : 8743 :e :stats @@ -638,7 +666,7 @@ prettier2 done eval2 //│ ╟── Note: constraint arises from application: //│ ║ l.262: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ -//│ res: 'a -> string | error +//│ res: error | 'a -> string //│ where //│ 'a <: Add[?] & {lhs: nothing -> int & 'a, rhs: nothing -> int & 'a} | Lit | 'b & ~#Add & ~#Lit //│ 'b <: Nega[?] & {arg: 'c} @@ -646,7 +674,7 @@ prettier2 done eval2 //│ = [Function (anonymous)] //│ constrain calls : 71 //│ annoying calls : 0 -//│ subtyping calls : 504 +//│ subtyping calls : 511 :e :stats @@ -675,7 +703,7 @@ prettier2 done eval2 e1 //│ ║ l.18: def lit val = Lit { val } //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.371: | _ -> k x +//│ ║ l.379: | _ -> k x //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.262: else if ev e.rhs == 0 then prettier1 k ev e.lhs @@ -684,7 +712,7 @@ prettier2 done eval2 e1 //│ = '123' //│ constrain calls : 398 //│ annoying calls : 108 -//│ subtyping calls : 4507 +//│ subtyping calls : 4317 :e :stats @@ -713,7 +741,7 @@ prettier2 done eval2 e2 //│ ║ l.18: def lit val = Lit { val } //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.371: | _ -> k x +//│ ║ l.379: | _ -> k x //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.262: else if ev e.rhs == 0 then prettier1 k ev e.lhs @@ -722,7 +750,7 @@ prettier2 done eval2 e2 //│ = '1-123' //│ constrain calls : 457 //│ annoying calls : 131 -//│ subtyping calls : 5047 +//│ subtyping calls : 4803 :e :stats @@ -730,7 +758,7 @@ prettier2 done eval2 d2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval2 d2 //│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ╟── function of type `?a -> (forall ?b ?c. ?c | ?b)` is not an instance of type `number` +//│ ╟── function of type `?a -> (forall ?b ?c. ?b | ?c)` is not an instance of type `number` //│ ║ l.70: rec def eval1 k e = case e of { //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.71: | Lit -> e.val @@ -748,10 +776,10 @@ prettier2 done eval2 d2 //│ ║ l.+1: prettier2 done eval2 d2 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Nega[?E] & {Nega#E = ?E, arg: ?arg}` is not a function -//│ ║ l.362: def nega arg = Nega { arg } +//│ ║ l.370: def nega arg = Nega { arg } //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.371: | _ -> k x +//│ ║ l.379: | _ -> k x //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.262: else if ev e.rhs == 0 then prettier1 k ev e.lhs @@ -760,7 +788,7 @@ prettier2 done eval2 d2 //│ = '-1-1' //│ constrain calls : 323 //│ annoying calls : 95 -//│ subtyping calls : 3021 +//│ subtyping calls : 2836 :e :stats @@ -798,5 +826,5 @@ prettier2 done eval1 e2 //│ = '1-123' //│ constrain calls : 453 //│ annoying calls : 131 -//│ subtyping calls : 4947 +//│ subtyping calls : 4717 diff --git a/shared/src/test/diff/mlscript/ExprProb2.mls b/shared/src/test/diff/mlscript/ExprProb2.mls index 33ae1cac97..f607f6a637 100644 --- a/shared/src/test/diff/mlscript/ExprProb2.mls +++ b/shared/src/test/diff/mlscript/ExprProb2.mls @@ -23,7 +23,7 @@ def eval1 eval1 e = case e of { //│ = [Function: eval1] //│ constrain calls : 36 //│ annoying calls : 0 -//│ subtyping calls : 176 +//│ subtyping calls : 181 :stats :js @@ -45,11 +45,14 @@ def eval1f eval1 e = case e of { //│ = [Function: eval1f] //│ constrain calls : 32 //│ annoying calls : 0 -//│ subtyping calls : 153 +//│ subtyping calls : 161 e1 = add (lit 1) (add (lit 2) (lit 3)) -//│ e1: Add[Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} | Lit & {val: 1}] with {lhs: Lit & {val: 1}, rhs: Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}}} +//│ e1: Add[Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} | Lit & {val: 1}] with { +//│ lhs: Lit & {val: 1}, +//│ rhs: Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} +//│ } //│ = Add { //│ lhs: Lit { val: 1 }, //│ rhs: Add { lhs: Lit { val: 2 }, rhs: Lit { val: 3 } } @@ -72,10 +75,12 @@ eval1_fixed_1 e1 rec def eval1_fixed_2 = eval1f (fun x -> eval1f eval1_fixed_2 x) -//│ eval1_fixed_2: (Add[?] & 'a | (Lit with {val: 'val})) -> (int | 'val) +//│ eval1_fixed_2: (Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit\val & {val: 'val}) -> (int | 'val) //│ where -//│ 'a <: {lhs: Add[?] & {lhs: 'rhs, rhs: 'rhs} | Lit, rhs: Add[?] & {lhs: 'rhs, rhs: 'rhs} | Lit} -//│ 'rhs <: Add[?] & 'a | Lit +//│ 'lhs <: Add[?] & {lhs: 'lhs0, rhs: 'rhs0} | Lit +//│ 'lhs0 <: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit +//│ 'rhs <: Add[?] & {lhs: 'lhs0, rhs: 'rhs0} | Lit +//│ 'rhs0 <: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit //│ = [Function: eval1_fixed_2] eval1_fixed_2 e1 @@ -87,9 +92,21 @@ eval1_fixed_2 e1 def eval1_fixed_3 = let fixed fixed = eval1f (fun x -> eval1f (fixed fixed) x) in fixed fixed! -//│ eval1_fixed_3: (Add[?] & {lhs: Add[?] & {lhs: 'rhs, rhs: 'rhs} | Lit, rhs: Add[?] & {lhs: 'rhs, rhs: 'rhs} | Lit} | (Lit with {val: 'val})) -> (int | 'val) -//│ where -//│ 'rhs <: Add[?] & {lhs: Add[?] & {lhs: 'rhs, rhs: 'rhs} | Lit, rhs: Add[?] & {lhs: 'rhs, rhs: 'rhs} | Lit} | Lit +//│ eval1_fixed_3: (Add[?] & { +//│ lhs: Add[?] & { +//│ lhs: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit, +//│ rhs: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit +//│ } | Lit, +//│ rhs: Add[?] & { +//│ lhs: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit, +//│ rhs: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit +//│ } | Lit +//│ } | Lit\val & {val: 'val}) -> (int | 'val) +//│ where +//│ 'lhs <: Add[?] & {lhs: 'lhs0, rhs: 'rhs0} | Lit +//│ 'lhs0 <: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit +//│ 'rhs <: Add[?] & {lhs: 'lhs0, rhs: 'rhs0} | Lit +//│ 'rhs0 <: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit //│ = [Function: eval1_fixed_3] eval1_fixed_3 e1 @@ -124,7 +141,16 @@ def eval2f eval2 e = case e of { e2 = add (lit 1) (nega e1) -//│ e2: Add[Lit & {val: 1} | Nega[Add[Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} | Lit & {val: 1}] with {lhs: Lit & {val: 1}, rhs: Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}}}]] with {lhs: Lit & {val: 1}, rhs: Nega[Add[Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} | Lit & {val: 1}] with {lhs: Lit & {val: 1}, rhs: Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}}}]} +//│ e2: Add[Lit & {val: 1} | Nega[Add[Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} | Lit & {val: 1}] with { +//│ lhs: Lit & {val: 1}, +//│ rhs: Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} +//│ }]] with { +//│ lhs: Lit & {val: 1}, +//│ rhs: Nega[Add[Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} | Lit & {val: 1}] with { +//│ lhs: Lit & {val: 1}, +//│ rhs: Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} +//│ }] +//│ } //│ = Add { //│ lhs: Lit { val: 1 }, //│ rhs: Nega { arg: Add { lhs: [Lit], rhs: [Add] } } @@ -149,11 +175,9 @@ def fix f = let fixed = fun x -> f (fun v -> (x x) v) in fixed fixed! //│ = [Function: fix] def eval2_fixed_2 = fix eval2f -//│ eval2_fixed_2: (Add[?] & 'a | (Lit with {val: 'val}) | Nega[?] & 'b) -> (int | 'val) +//│ eval2_fixed_2: (Add[?] & {lhs: 'a, rhs: 'a} | (Lit with {val: 'val}) | Nega[?] & {arg: 'a}) -> (int | 'val) //│ where -//│ 'a <: {lhs: 'c, rhs: 'c} -//│ 'c <: Add[?] & 'a | Lit | Nega[?] & 'b -//│ 'b <: {arg: 'c} +//│ 'a <: Add[?] & {lhs: 'a, rhs: 'a} | Lit | Nega[?] & {arg: 'a} //│ = [Function: eval2_fixed_2] :stats @@ -162,7 +186,7 @@ eval2_fixed_2 e1 //│ = 6 //│ constrain calls : 335 //│ annoying calls : 140 -//│ subtyping calls : 2899 +//│ subtyping calls : 2726 :stats eval2_fixed_2 e2 @@ -170,7 +194,7 @@ eval2_fixed_2 e2 //│ = -5 //│ constrain calls : 822 //│ annoying calls : 345 -//│ subtyping calls : 8038 +//│ subtyping calls : 7615 @@ -192,10 +216,12 @@ rec def eval1_fixed = eval1f (eval1f eval1_fixed) //│ return eval1f(eval1f(eval1_fixed())); //│ }; //│ // End of generated code -//│ eval1_fixed: (Add[?] & 'a | Lit\val & {val: 'val}) -> (int | 'val) +//│ eval1_fixed: (Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit\val & {val: 'val}) -> (int | 'val) //│ where -//│ 'a <: {lhs: Add[?] & 'b | Lit, rhs: Add[?] & 'b | Lit} -//│ 'b <: {lhs: Add[?] & 'a | Lit, rhs: Add[?] & 'a | Lit} +//│ 'lhs <: Add[?] & {lhs: 'lhs0, rhs: 'rhs0} | Lit +//│ 'lhs0 <: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit +//│ 'rhs <: Add[?] & {lhs: 'lhs0, rhs: 'rhs0} | Lit +//│ 'rhs0 <: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit //│ = [Function: eval1_fixed] :re @@ -205,10 +231,12 @@ eval1_fixed e1 //│ RangeError: Maximum call stack size exceeded rec def eval1_fixed() = eval1f (eval1f (eval1_fixed())) -//│ eval1_fixed: () -> (Add[?] & 'a | Lit\val & {val: 'val}) -> (int | 'val) +//│ eval1_fixed: () -> (Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit\val & {val: 'val}) -> (int | 'val) //│ where -//│ 'a <: {lhs: Add[?] & 'b | Lit, rhs: Add[?] & 'b | Lit} -//│ 'b <: {lhs: Add[?] & 'a | Lit, rhs: Add[?] & 'a | Lit} +//│ 'lhs <: Add[?] & {lhs: 'lhs0, rhs: 'rhs0} | Lit +//│ 'lhs0 <: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit +//│ 'rhs <: Add[?] & {lhs: 'lhs0, rhs: 'rhs0} | Lit +//│ 'rhs0 <: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit //│ = [Function: eval1_fixed1] :re @@ -240,7 +268,10 @@ def eval1_fixed = eval1f (fun x -> eval1f eval1f x) //│ ╟── Note: constraint arises from application: //│ ║ l.32: | Add -> eval1 e.lhs + eval1 e.rhs //│ ╙── ^^^^^^^^^^^ -//│ eval1_fixed: (Add[?] & {lhs: Add[?] & {lhs: nothing -> int, rhs: nothing -> int} | Lit, rhs: Add[?] & {lhs: nothing -> int, rhs: nothing -> int} | Lit} | (Lit with {val: 'val})) -> (int | 'val) +//│ eval1_fixed: (Add[?] & { +//│ lhs: Add[?] & {lhs: nothing -> int, rhs: nothing -> int} | Lit, +//│ rhs: Add[?] & {lhs: nothing -> int, rhs: nothing -> int} | Lit +//│ } | (Lit with {val: 'val})) -> (int | 'val) rec def eval1_fixed = eval1f (fun x -> eval1_fixed eval1_fixed x) //│ ╔══[ERROR] Type mismatch in binding of application: @@ -314,7 +345,7 @@ def eval2_broken eval2 e = case e of { | Nega -> e.arg | _ -> eval1 eval2 e } -//│ eval2_broken: ('a -> 'lhs -> int & 'a) -> (Add[?] & {lhs: 'lhs, rhs: 'lhs} | (Lit with {val: 'arg}) | Nega[?] & {arg: 'arg}) -> ('arg | int) +//│ eval2_broken: ('a -> 'lhs -> int & 'a) -> (Add[?] & {lhs: 'lhs, rhs: 'lhs} | (Lit with {val: 'arg}) | Nega[?] & {arg: 'arg}) -> (int | 'arg) :e eval2_broken eval2_broken! e2 @@ -328,7 +359,7 @@ eval2_broken eval2_broken! e2 //│ ║ l.20: | Add -> eval1 eval1 e.lhs + eval1 eval1 e.rhs //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── from field selection: -//│ ║ l.314: | Nega -> e.arg +//│ ║ l.345: | Nega -> e.arg //│ ╙── ^^^^^ //│ res: error | int @@ -345,13 +376,13 @@ fix eval2f_oops e2 //│ ║ l.+1: fix eval2f_oops e2 //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b. ?b)` does not match type `Add[?] & ?c | Lit & ?d` -//│ ║ l.147: def fix f = let fixed = fun x -> f (fun v -> (x x) v) in fixed fixed! +//│ ║ l.173: def fix f = let fixed = fun x -> f (fun v -> (x x) v) in fixed fixed! //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.18: def eval1 eval1 e = case e of { //│ ║ ^ //│ ╟── from reference: -//│ ║ l.336: def eval2f_oops eval2 e = case e of { +//│ ║ l.367: def eval2f_oops eval2 e = case e of { //│ ╙── ^ //│ res: error diff --git a/shared/src/test/diff/mlscript/ExprProb_Inv.mls b/shared/src/test/diff/mlscript/ExprProb_Inv.mls index c5882f0de5..40e3cb75a7 100644 --- a/shared/src/test/diff/mlscript/ExprProb_Inv.mls +++ b/shared/src/test/diff/mlscript/ExprProb_Inv.mls @@ -52,13 +52,13 @@ rec def eval1_stub e = case e of { | Add -> eval1_stub e.lhs | _ -> 0 } -//│ eval1_stub: forall 'a 'b 'c 'd 'lhs 'eval1_stub 'e. 'eval1_stub +//│ eval1_stub: forall 'a 'b 'lhs 'eval1_stub 'c 'd 'e. 'eval1_stub //│ where -//│ 'eval1_stub := 'd -> (1 | 'c | 0) -//│ 'c :> 1 | 'c | 0 -//│ 'd <: #Lit & 'b | (#Add & 'e | 'a & ~#Add) & ~#Lit -//│ 'e <: {lhs: 'lhs} -//│ 'lhs <: 'd +//│ 'eval1_stub := 'b -> (1 | 'a | 0) +//│ 'a :> 1 | 'a | 0 +//│ 'b <: #Lit & 'd | (#Add & 'c | 'e & ~#Add) & ~#Lit +//│ 'c <: {lhs: 'lhs} +//│ 'lhs <: 'b //│ = [Function: eval1_stub2] eval1_stub @@ -80,35 +80,35 @@ rec def eval1 k e = case e of { //│ = [Function: eval1] //│ constrain calls : 79 //│ annoying calls : 0 -//│ subtyping calls : 331 +//│ subtyping calls : 343 :ns eval1 -//│ res: forall 'eval1 'a 'b 'c 'val 'd 'e 'f 'lhs 'rhs 'g. 'eval1 -//│ where -//│ 'eval1 := 'b -> 'g -> ('val | 'c | 'e) -//│ 'c := int -//│ 'g <: #Lit & 'f | (#Add & 'a | 'd & ~#Add) & ~#Lit -//│ 'a <: {rhs: 'rhs} & {lhs: 'lhs} -//│ 'lhs <: 'g -//│ 'rhs <: 'g -//│ 'f <: {val: 'val} +//│ res: forall 'val 'eval1 'rhs 'a 'b 'c 'd 'e 'lhs 'f 'g. 'eval1 +//│ where +//│ 'eval1 := 'd -> 'c -> ('val | 'e | 'g) +//│ 'e := int +//│ 'c <: #Lit & 'a | (#Add & 'f | 'b & ~#Add) & ~#Lit +//│ 'f <: {rhs: 'rhs} & {lhs: 'lhs} +//│ 'lhs <: 'c +//│ 'rhs <: 'c +//│ 'a <: {val: 'val} //│ 'val <: int -//│ 'b <: 'd -> 'e -//│ 'e <: int +//│ 'd <: 'b -> 'g +//│ 'g <: int //│ = [Function: eval1] :re error: ~Add[?] //│ res: ~Add[?] //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown :re error: ('a & ~Lit) -> 'a //│ res: ('a & ~Lit) -> 'a //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown :re error: ('a) -> ('a & Add[?]) @@ -116,19 +116,19 @@ error: ('a) -> ('a & ~Add[?]) error: ('a & ~Add[?]) -> 'a //│ res: 'a -> (Add[?] & 'a) //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown //│ res: 'a -> ('a & ~Add[?]) //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown //│ res: ('a & ~Add[?]) -> 'a //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown :re error: ('a & ~add) -> 'a //│ res: ('a & ~#Add) -> 'a //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown :ns def eval1_ty_ugly: ('a -> int) -> (Lit | Add['b] | 'a & ~Lit & ~Add[?] as 'b) -> int @@ -140,7 +140,7 @@ def eval1_ty_ugly: ('a -> int) -> (Lit | Add['b] | 'a & ~Lit & ~Add[?] as 'b) -> eval1_ty_ugly //│ res: ('a -> int) -> 'b -> int //│ where -//│ 'b := 'a & ~Add[?] & ~Lit | Add['b] | Lit +//│ 'b := Add['b] | Lit | 'a & ~Add[?] & ~Lit //│ = //│ eval1_ty_ugly is not implemented @@ -152,11 +152,11 @@ def eval1_ty_ugly = eval1 //│ <: eval1_ty_ugly: //│ ('a -> int) -> 'b -> int //│ where -//│ 'b := 'a & ~Add[?] & ~Lit | Add['b] | Lit +//│ 'b := Add['b] | Lit | 'a & ~Add[?] & ~Lit //│ = [Function: eval1_ty_ugly] //│ constrain calls : 77 //│ annoying calls : 37 -//│ subtyping calls : 749 +//│ subtyping calls : 656 :ns def eval1_ty: ('a -> int) -> (Lit | Add['b] | 'a & ~lit & ~add as 'b) -> int @@ -168,7 +168,7 @@ def eval1_ty: ('a -> int) -> (Lit | Add['b] | 'a & ~lit & ~add as 'b) -> int eval1_ty //│ res: ('a -> int) -> 'b -> int //│ where -//│ 'b := 'a & ~#Add & ~#Lit | Add['b] | Lit +//│ 'b := Add['b] | Lit | 'a & ~#Add & ~#Lit //│ = //│ eval1_ty is not implemented @@ -180,39 +180,39 @@ def eval1_ty = eval1 //│ <: eval1_ty: //│ ('a -> int) -> 'b -> int //│ where -//│ 'b := 'a & ~#Add & ~#Lit | Add['b] | Lit +//│ 'b := Add['b] | Lit | 'a & ~#Add & ~#Lit //│ = [Function: eval1_ty] //│ constrain calls : 77 //│ annoying calls : 37 -//│ subtyping calls : 745 +//│ subtyping calls : 648 :stats eval1_ty_ugly = eval1_ty //│ ('a -> int) -> 'b -> int //│ where -//│ 'b := 'a & ~#Add & ~#Lit | Add['b] | Lit +//│ 'b := Add['b] | Lit | 'a & ~#Add & ~#Lit //│ <: eval1_ty_ugly: //│ ('a -> int) -> 'b -> int //│ where -//│ 'b := 'a & ~Add[?] & ~Lit | Add['b] | Lit +//│ 'b := Add['b] | Lit | 'a & ~Add[?] & ~Lit //│ = [Function: eval1] //│ constrain calls : 150 //│ annoying calls : 1810 -//│ subtyping calls : 4875 +//│ subtyping calls : 2699 :stats eval1_ty = eval1_ty_ugly //│ ('a -> int) -> 'b -> int //│ where -//│ 'b := 'a & ~Add[?] & ~Lit | Add['b] | Lit +//│ 'b := Add['b] | Lit | 'a & ~Add[?] & ~Lit //│ <: eval1_ty: //│ ('a -> int) -> 'b -> int //│ where -//│ 'b := 'a & ~#Add & ~#Lit | Add['b] | Lit +//│ 'b := Add['b] | Lit | 'a & ~#Add & ~#Lit //│ = [Function: eval1] //│ constrain calls : 752 //│ annoying calls : 674 -//│ subtyping calls : 75730 +//│ subtyping calls : 71116 // Workaround: @@ -238,7 +238,7 @@ def eval1_ty = eval1 //│ = [Function: eval1_ty2] //│ constrain calls : 73 //│ annoying calls : 37 -//│ subtyping calls : 605 +//│ subtyping calls : 535 :stats @@ -253,7 +253,7 @@ rec def pretty1 k e = case e of { //│ = [Function: pretty1] //│ constrain calls : 87 //│ annoying calls : 0 -//│ subtyping calls : 368 +//│ subtyping calls : 379 :stats @@ -271,7 +271,7 @@ rec def prettier1 k ev e = case e of { //│ = [Function: prettier1] //│ constrain calls : 293 //│ annoying calls : 0 -//│ subtyping calls : 1040 +//│ subtyping calls : 1043 :stats rec def prettier11 k ev e = case e of { @@ -283,12 +283,13 @@ rec def prettier11 k ev e = case e of { } //│ prettier11: ('a -> string) -> ('rhs -> number) -> 'b -> string //│ where -//│ 'b <: (Add[?] with {lhs: 'c, rhs: 'rhs & 'b}) | Lit | 'a & ~#Add & ~#Lit -//│ 'c <: (Add[?] with {lhs: 'c, rhs: 'c}) | Lit | 'a & ~#Add & ~#Lit +//│ 'b <: (Add[?] with {lhs: Add[?] & 'c | Lit | 'a & ~#Add & ~#Lit, rhs: 'rhs & 'b}) | Lit | 'a & ~#Add & ~#Lit +//│ 'c <: {lhs: 'd, rhs: 'd} +//│ 'd <: Add[?] & 'c | Lit | 'a & ~#Add & ~#Lit //│ = [Function: prettier11] //│ constrain calls : 191 //│ annoying calls : 0 -//│ subtyping calls : 764 +//│ subtyping calls : 820 // Doesn't make much sense, but generates very ugly type unless aggressively simplified: :stats @@ -299,14 +300,15 @@ rec def prettier12 k ev e = case e of { in if ev e == 0 then tmp else concat tmp (pretty1 k e.rhs) | _ -> k e } -//│ prettier12: ('a -> string & 'b -> 'c) -> ('d -> number) -> (Add[?]\lhs\rhs & {lhs: 'e, rhs: 'f} & 'd | Lit | 'b & ~#Add & ~#Lit) -> (string | 'c) +//│ prettier12: ('a -> string & 'b -> 'c) -> ('d -> number) -> ((Add[?] with {lhs: Add[?] & 'e | Lit | 'a & ~#Add & ~#Lit, rhs: 'f}) & 'd | Lit | 'b & ~#Add & ~#Lit) -> (string | 'c) //│ where -//│ 'f <: Add[?]\lhs\rhs & {lhs: 'f, rhs: 'f} | Lit | 'a & ~#Add & ~#Lit -//│ 'e <: Add[?]\lhs\rhs & {lhs: 'e, rhs: 'e} | Lit | 'a & ~#Add & ~#Lit +//│ 'f <: (Add[?] with {lhs: 'f, rhs: 'f}) | Lit | 'a & ~#Add & ~#Lit +//│ 'e <: {lhs: 'g, rhs: 'g} +//│ 'g <: Add[?] & 'e | Lit | 'a & ~#Add & ~#Lit //│ = [Function: prettier12] //│ constrain calls : 166 //│ annoying calls : 0 -//│ subtyping calls : 798 +//│ subtyping calls : 877 :stats @@ -336,7 +338,7 @@ prettier12 done (eval1 done) e1 //│ = '123' //│ constrain calls : 1315 //│ annoying calls : 500 -//│ subtyping calls : 13235 +//│ subtyping calls : 12267 e1 = add (lit 1) (add (lit 2) (lit 3)) @@ -399,23 +401,26 @@ rec def prettier2 k ev = prettier1 (fun x -> case x of { //│ = [Function: prettier2] //│ constrain calls : 136 //│ annoying calls : 0 -//│ subtyping calls : 647 +//│ subtyping calls : 662 :stats rec def prettier22 k ev = prettier12 (fun x -> case x of { | Nega -> concat "-" (prettier22 k ev x.arg) | _ -> k x }) ev -//│ prettier22: ('a -> string) -> ('b -> number) -> 'arg -> string -//│ where -//│ 'b <: {lhs: 'c, rhs: 'd} -//│ 'd <: Add[?]\lhs\rhs & {lhs: 'd, rhs: 'd} | Lit | Nega[?] & {arg: 'arg} | 'a & ~#Add & ~#Lit & ~#Nega -//│ 'c <: Add[?]\lhs\rhs & {lhs: 'c, rhs: 'c} | Lit | Nega[?] & {arg: 'arg} | 'a & ~#Add & ~#Lit & ~#Nega -//│ 'arg <: Add[?] & 'b | Lit | (Nega[?] & {arg: 'arg} | 'a & ~#Nega) & ~#Add & ~#Lit +//│ prettier22: ('a -> string) -> ('b -> number) -> (Add[?] & 'b | Lit | 'c & ~#Add & ~#Lit) -> string +//│ where +//│ 'b <: {lhs: Add[?] & 'd | Lit | 'e & ~#Add & ~#Lit, rhs: 'f} +//│ 'f <: Add[?]\lhs\rhs & {lhs: 'f, rhs: 'f} | Lit | Nega[?] & {arg: 'arg} | 'a & ~#Add & ~#Lit & ~#Nega +//│ 'd <: {lhs: 'g, rhs: 'g} +//│ 'g <: Add[?] & 'd | Lit | 'e & ~#Add & ~#Lit +//│ 'e <: Nega[?] & {arg: 'arg} | 'a & ~#Nega +//│ 'arg <: Add[?] & 'b | Lit | 'c & ~#Add & ~#Lit +//│ 'c <: Nega[?] & {arg: 'arg} | 'a & ~#Nega //│ = [Function: prettier22] //│ constrain calls : 208 //│ annoying calls : 0 -//│ subtyping calls : 1010 +//│ subtyping calls : 1083 @@ -425,14 +430,17 @@ eval2 done e1 //│ = 6 //│ constrain calls : 177 //│ annoying calls : 60 -//│ subtyping calls : 1388 +//│ subtyping calls : 1263 e2 = add (lit 1) (nega e1) -//│ e2: Add['E] with {lhs: Lit & {val: 1}, rhs: Nega[forall 'E0 'E1. Add['E0] with {lhs: Lit & {val: 1}, rhs: Add['E1] with {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}}}]} -//│ where -//│ 'E :> Lit & {val: 1} | Nega[forall 'E0 'E1. Add['E0] with {lhs: Lit & {val: 1}, rhs: Add['E1] with {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}}}] -//│ 'E0 :> (Add['E1] with {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}}) | Lit & {val: 1} -//│ 'E1 :> Lit & {val: 2 | 3} +//│ e2: Add['E] with { +//│ lhs: Lit & {val: 1}, +//│ rhs: Nega[forall 'E0 'E1. Add['E0] with {lhs: Lit & {val: 1}, rhs: Add['E1] with {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}}}] +//│ } +//│ where +//│ 'E :> Lit & {val: 1} | Nega[forall 'E0 'E1. Add['E0] with {lhs: Lit & {val: 1}, rhs: Add['E1] with {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}}}] +//│ 'E0 :> (Add['E1] with {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}}) | Lit & {val: 1} +//│ 'E1 :> Lit & {val: 2 | 3} //│ = Add { //│ lhs: Lit { val: 1 }, //│ rhs: Nega { arg: Add { lhs: [Lit], rhs: [Add] } } @@ -444,7 +452,7 @@ eval2 done e2 //│ = -5 //│ constrain calls : 275 //│ annoying calls : 103 -//│ subtyping calls : 2134 +//│ subtyping calls : 1927 d2 = nega (add (lit 1) (nega (lit 1))) //│ d2: Nega[Add['E] with {lhs: Lit & {val: 1}, rhs: Nega[Lit & {val: 1}]}] @@ -458,7 +466,7 @@ eval2 done d2 //│ = 0 //│ constrain calls : 183 //│ annoying calls : 71 -//│ subtyping calls : 1339 +//│ subtyping calls : 1201 prettier2 done @@ -470,13 +478,15 @@ prettier2 done //│ = [Function (anonymous)] prettier22 done -//│ res: ('a -> number) -> 'arg -> string +//│ res: ('a -> number) -> (Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit) -> string //│ where -//│ 'a <: {lhs: 'b, rhs: 'c} -//│ 'c <: Add[?]\lhs\rhs & {lhs: 'c, rhs: 'c} | Lit | 'd -//│ 'b <: Add[?]\lhs\rhs & {lhs: 'b, rhs: 'b} | Lit | 'd +//│ 'a <: {lhs: Add[?] & 'c | Lit | 'd & ~#Add & ~#Lit, rhs: 'e} +//│ 'e <: Add[?]\lhs\rhs & {lhs: 'e, rhs: 'e} | Lit | Nega[?] & {arg: 'arg} +//│ 'c <: {lhs: 'f, rhs: 'f} +//│ 'f <: Add[?] & 'c | Lit | 'd & ~#Add & ~#Lit //│ 'd <: Nega[?] & {arg: 'arg} -//│ 'arg <: Add[?] & 'a | Lit | 'd & ~#Add & ~#Lit +//│ 'arg <: Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit +//│ 'b <: Nega[?] & {arg: 'arg} //│ = [Function (anonymous)] :stats @@ -490,16 +500,19 @@ prettier2 done (eval1 done) //│ = [Function (anonymous)] //│ constrain calls : 101 //│ annoying calls : 0 -//│ subtyping calls : 656 +//│ subtyping calls : 660 prettier22 done (eval1 done) -//│ res: 'arg -> string -//│ where -//│ 'arg <: Add[?] & {lhs: 'a, rhs: 'a} & 'b | Lit | 'c & ~#Add & ~#Lit -//│ 'b <: (Add[?] with {lhs: 'b, rhs: 'b}) | Lit -//│ 'a <: (Add[?] with {lhs: 'a, rhs: 'a}) | Lit | 'c -//│ 'c <: Nega[?] & {arg: 'arg} +//│ res: (Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit) -> string +//│ where +//│ 'a <: {lhs: Add[?] & 'c | Lit | 'b & ~#Add & ~#Lit, rhs: 'd} & 'e +//│ 'e <: (Add[?] with {lhs: 'e, rhs: 'e}) | Lit +//│ 'd <: (Add[?] with {lhs: 'd, rhs: 'd}) | Lit | Nega[?] & {arg: 'arg} +//│ 'c <: {lhs: 'f, rhs: 'f} +//│ 'f <: Add[?] & 'c | Lit | 'b & ~#Add & ~#Lit +//│ 'b <: Nega[?] & {arg: 'arg} +//│ 'arg <: Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit //│ = [Function (anonymous)] // TODO could probably merge `a` and `b` here! @@ -517,7 +530,7 @@ prettier2 done (eval2 done) //│ = [Function (anonymous)] //│ constrain calls : 111 //│ annoying calls : 0 -//│ subtyping calls : 902 +//│ subtyping calls : 915 prettier2 done (eval2 done) e2 prettier2 done (eval2 done) d2 @@ -530,13 +543,16 @@ prettier2 done (eval2 done) d2 prettier22 done (eval2 done) prettier22 done (eval2 done) e2 prettier22 done (eval2 done) d2 -//│ res: 'arg -> string -//│ where -//│ 'arg <: Add[?] & {lhs: 'a, rhs: 'a} & 'b | Lit | 'c & ~#Add & ~#Lit -//│ 'b <: (Add[?] with {lhs: 'b, rhs: 'b}) | Lit | 'd & ~#Add & ~#Lit -//│ 'd <: Nega[?] & {arg: 'b} -//│ 'a <: (Add[?] with {lhs: 'a, rhs: 'a}) | Lit | 'c -//│ 'c <: Nega[?] & {arg: 'arg} +//│ res: (Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit) -> string +//│ where +//│ 'a <: {lhs: Add[?] & 'c | Lit | 'b & ~#Add & ~#Lit, rhs: 'd} & 'e +//│ 'e <: (Add[?] with {lhs: 'e, rhs: 'e}) | Lit | 'f & ~#Add & ~#Lit +//│ 'f <: Nega[?] & {arg: 'e} +//│ 'd <: (Add[?] with {lhs: 'd, rhs: 'd}) | Lit | Nega[?] & {arg: 'arg} +//│ 'c <: {lhs: 'g, rhs: 'g} +//│ 'g <: Add[?] & 'c | Lit | 'b & ~#Add & ~#Lit +//│ 'b <: Nega[?] & {arg: 'arg} +//│ 'arg <: Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit //│ = [Function (anonymous)] //│ res: string //│ = '1-123' @@ -544,7 +560,7 @@ prettier22 done (eval2 done) d2 //│ = '-1' //│ constrain calls : 1178 //│ annoying calls : 390 -//│ subtyping calls : 11019 +//│ subtyping calls : 10358 @@ -561,7 +577,7 @@ eval1 done e2 //│ ║ 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.372: def nega arg = Nega { arg } //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.4: def done x = case x of {} @@ -616,7 +632,7 @@ 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.372: def nega arg = Nega { arg } //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.4: def done x = case x of {} @@ -629,7 +645,7 @@ prettier2 done (eval1 done) e2 //│ Error: non-exhaustive case expression //│ constrain calls : 710 //│ annoying calls : 238 -//│ subtyping calls : 9192 +//│ subtyping calls : 8749 :e :stats @@ -651,7 +667,7 @@ prettier2 done eval2 //│ ╟── Note: constraint arises from application: //│ ║ l.264: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ -//│ res: 'a -> string | error +//│ res: error | 'a -> string //│ where //│ 'a <: Add[?]\lhs\rhs & {lhs: nothing -> int & 'a, rhs: nothing -> int & 'a} | Lit | 'b & ~#Add & ~#Lit //│ 'b <: Nega[?] & {arg: 'c} @@ -659,7 +675,7 @@ prettier2 done eval2 //│ = [Function (anonymous)] //│ constrain calls : 71 //│ annoying calls : 0 -//│ subtyping calls : 560 +//│ subtyping calls : 559 :e :stats @@ -688,7 +704,7 @@ prettier2 done eval2 e1 //│ ║ l.19: def lit val = Lit { val } //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.379: | _ -> k x +//│ ║ l.381: | _ -> k x //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.264: else if ev e.rhs == 0 then prettier1 k ev e.lhs @@ -697,7 +713,7 @@ prettier2 done eval2 e1 //│ = '123' //│ constrain calls : 398 //│ annoying calls : 108 -//│ subtyping calls : 4519 +//│ subtyping calls : 4329 :e :stats @@ -726,7 +742,7 @@ prettier2 done eval2 e2 //│ ║ l.19: def lit val = Lit { val } //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.379: | _ -> k x +//│ ║ l.381: | _ -> k x //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.264: else if ev e.rhs == 0 then prettier1 k ev e.lhs @@ -735,7 +751,7 @@ prettier2 done eval2 e2 //│ = '1-123' //│ constrain calls : 457 //│ annoying calls : 131 -//│ subtyping calls : 5057 +//│ subtyping calls : 4813 :e :stats @@ -761,10 +777,10 @@ prettier2 done eval2 d2 //│ ║ 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.372: def nega arg = Nega { arg } //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.379: | _ -> k x +//│ ║ l.381: | _ -> k x //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.264: else if ev e.rhs == 0 then prettier1 k ev e.lhs @@ -773,7 +789,7 @@ prettier2 done eval2 d2 //│ = '-1-1' //│ constrain calls : 323 //│ annoying calls : 95 -//│ subtyping calls : 3031 +//│ subtyping calls : 2846 :e :stats @@ -811,6 +827,6 @@ prettier2 done eval1 e2 //│ = '1-123' //│ constrain calls : 453 //│ annoying calls : 131 -//│ subtyping calls : 4953 +//│ subtyping calls : 4723 diff --git a/shared/src/test/diff/mlscript/FunnySubsumptions.mls b/shared/src/test/diff/mlscript/FunnySubsumptions.mls index a5cae7751a..bf786e57a6 100644 --- a/shared/src/test/diff/mlscript/FunnySubsumptions.mls +++ b/shared/src/test/diff/mlscript/FunnySubsumptions.mls @@ -24,7 +24,7 @@ A0 = B0 //│ D & {x: T} | {x: T, y: S} | C & {y: S} | C & D //│ constrain calls : 1 //│ annoying calls : 0 -//│ subtyping calls : 167 +//│ subtyping calls : 197 :stats B0 = A0 @@ -33,7 +33,7 @@ B0 = A0 //│ {x: T, y: S} | D & {x: T} | C & {y: S} //│ constrain calls : 2 //│ annoying calls : 32 -//│ subtyping calls : 282 +//│ subtyping calls : 289 def A1: C & {x : T} | D & {y : U} @@ -48,7 +48,7 @@ A1 = B1 //│ C & {x: T} | D & {y: U} //│ constrain calls : 2 //│ annoying calls : 13 -//│ subtyping calls : 251 +//│ subtyping calls : 230 :stats B1 = A1 @@ -57,6 +57,6 @@ B1 = A1 //│ C & {x: T} | D & {y: U} | C & D //│ constrain calls : 1 //│ annoying calls : 0 -//│ subtyping calls : 159 +//│ subtyping calls : 167 diff --git a/shared/src/test/diff/mlscript/GenericClasses.mls b/shared/src/test/diff/mlscript/GenericClasses.mls index 2c74b499ec..1844d8ee15 100644 --- a/shared/src/test/diff/mlscript/GenericClasses.mls +++ b/shared/src/test/diff/mlscript/GenericClasses.mls @@ -33,9 +33,9 @@ res.value //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.32: res.value //│ ║ ^^^^^^^^^ -//│ ╟── type `None` does not have field 'value' -//│ ║ l.21: type Option[A] = Some[A] | None -//│ ║ ^^^^ +//│ ╟── type `Option[int]` does not have field 'value' +//│ ║ l.25: Some 42 : Option[int] +//│ ║ ^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{value: ?value}` //│ ║ l.32: res.value //│ ╙── ^^^ @@ -122,7 +122,7 @@ Foo2 error: Foo2[int] //│ res: Foo2[int] //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown f = fun x -> case x of { Foo2 -> x } //│ f: (Foo2[?] & 'a) -> 'a @@ -136,7 +136,7 @@ f (Foo2 {}) error: (Foo2[?] & 'a) -> 'a //│ res: (Foo2[?] & 'a) -> 'a //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown f: (Foo2[?] & 'a) -> 'a //│ res: (Foo2[?] & 'a) -> 'a @@ -225,7 +225,7 @@ Foo2_Co error: Foo2_Co[int] //│ res: Foo2_Co[int] //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown f = fun x -> case x of { Foo2_Co -> x } //│ f: (Foo2_Co[?] & 'a) -> 'a @@ -239,7 +239,7 @@ f (Foo2_Co {}) error: (Foo2_Co[?] & 'a) -> 'a //│ res: (Foo2_Co[nothing] & 'a) -> 'a //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown f: (Foo2_Co[?] & 'a) -> 'a //│ res: (Foo2_Co[nothing] & 'a) -> 'a @@ -294,7 +294,7 @@ Foo2_Bi error: Foo2_Bi[int] //│ res: Foo2_Bi[?] //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown f = fun x -> case x of { Foo2_Bi -> x } //│ f: (Foo2_Bi[?] & 'a) -> 'a @@ -308,7 +308,7 @@ f (Foo2_Bi {}) error: (Foo2_Bi[?] & 'a) -> 'a //│ res: (Foo2_Bi[?] & 'a) -> 'a //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown f: (Foo2_Bi[?] & 'a) -> 'a //│ res: (Foo2_Bi[?] & 'a) -> 'a diff --git a/shared/src/test/diff/mlscript/GenericClasses2.mls b/shared/src/test/diff/mlscript/GenericClasses2.mls index 68f345ef6b..5afd338398 100644 --- a/shared/src/test/diff/mlscript/GenericClasses2.mls +++ b/shared/src/test/diff/mlscript/GenericClasses2.mls @@ -95,7 +95,7 @@ error: {A: 1} //│ ╙── ^ //│ res: {A <: 1} //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown b = Bar2{x = 1} //│ b: Bar2 & {x: 1} diff --git a/shared/src/test/diff/mlscript/Group_2021_12_02.mls b/shared/src/test/diff/mlscript/Group_2021_12_02.mls index e1fe1de327..457d8af88e 100644 --- a/shared/src/test/diff/mlscript/Group_2021_12_02.mls +++ b/shared/src/test/diff/mlscript/Group_2021_12_02.mls @@ -13,19 +13,19 @@ Lit def eval0: ('b -> ('c & int)) -> (Lit & {val: int} | Add & {lhs: 'a; rhs: 'a} | ~lit & ~add & 'b as 'a) -> (int | 'c) //│ eval0: ('b -> int) -> 'a -> int //│ where -//│ 'a <: 'b & ~#Add & ~#Lit | Add & {lhs: 'a, rhs: 'a} | Lit & {val: int} +//│ 'a <: Add & {lhs: 'a, rhs: 'a} | Lit & {val: int} | 'b & ~#Add & ~#Lit //│ = def evalUgly: ('b -> ('c & int)) -> (Lit & {val: int} | Add & {lhs: 'a; rhs: 'a} | ~Lit & ~Add & 'b as 'a) -> (int | 'c) //│ evalUgly: ('b -> int) -> 'a -> int //│ where -//│ 'a <: 'b & ~Add & ~Lit | Add & {lhs: 'a, rhs: 'a} | Lit & {val: int} +//│ 'a <: Add & {lhs: 'a, rhs: 'a} | Lit & {val: int} | 'b & ~Add & ~Lit //│ = def evalBad: ('b -> 'c) -> (Lit & {val: int} | Add & {lhs: 'a; rhs: 'a} | ~Lit & ~Add & 'b as 'a) -> (int | 'c) -//│ evalBad: ('b -> 'c) -> 'a -> ('c | int) +//│ evalBad: ('b -> 'c) -> 'a -> (int | 'c) //│ where -//│ 'a <: 'b & ~Add & ~Lit | Add & {lhs: 'a, rhs: 'a} | Lit & {val: int} +//│ 'a <: Add & {lhs: 'a, rhs: 'a} | Lit & {val: int} | 'b & ~Add & ~Lit //│ = type Expr[A] = Lit & {val: int} | Add & {lhs: Expr[A]; rhs: Expr[A]} | ~Lit & ~Add & A @@ -80,7 +80,7 @@ eval0 = evalImpl //│ <: eval0: //│ ('b -> int) -> 'a -> int //│ where -//│ 'a <: 'b & ~#Add & ~#Lit | Add & {lhs: 'a, rhs: 'a} | Lit & {val: int} +//│ 'a <: Add & {lhs: 'a, rhs: 'a} | Lit & {val: int} | 'b & ~#Add & ~#Lit //│ = [Function: evalImpl] evalUgly = evalImpl @@ -90,7 +90,7 @@ evalUgly = evalImpl //│ <: evalUgly: //│ ('b -> int) -> 'a -> int //│ where -//│ 'a <: 'b & ~Add & ~Lit | Add & {lhs: 'a, rhs: 'a} | Lit & {val: int} +//│ 'a <: Add & {lhs: 'a, rhs: 'a} | Lit & {val: int} | 'b & ~Add & ~Lit //│ = [Function: evalImpl] eval1 = evalImpl @@ -105,9 +105,9 @@ eval1 = evalImpl evalBad = eval //│ ('b -> int) -> Expr['b] -> int //│ <: evalBad: -//│ ('b -> 'c) -> 'a -> ('c | int) +//│ ('b -> 'c) -> 'a -> (int | 'c) //│ where -//│ 'a <: 'b & ~Add & ~Lit | Add & {lhs: 'a, rhs: 'a} | Lit & {val: int} +//│ 'a <: Add & {lhs: 'a, rhs: 'a} | Lit & {val: int} | 'b & ~Add & ~Lit //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.105: evalBad = eval //│ ║ ^^^^^^^^^^^^^^ @@ -150,7 +150,7 @@ eval (fun x -> x.hello) {hello=1} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.149: eval (fun x -> x.hello) {hello=1} //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── record literal of type `{hello: 1}` does not match type `'b & ~Add & ~Lit | Add & {lhs: Expr['b], rhs: Expr['b]} | Lit & {val: int}` +//│ ╟── record literal of type `{hello: 1}` does not match type `Add & {lhs: Expr['b], rhs: Expr['b]} | Lit & {val: int} | 'b & ~Add & ~Lit` //│ ║ l.149: eval (fun x -> x.hello) {hello=1} //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from union type: diff --git a/shared/src/test/diff/mlscript/Group_2022_06_09.mls b/shared/src/test/diff/mlscript/Group_2022_06_09.mls index 6b803e4c36..cc0953fd1e 100644 --- a/shared/src/test/diff/mlscript/Group_2022_06_09.mls +++ b/shared/src/test/diff/mlscript/Group_2022_06_09.mls @@ -132,12 +132,13 @@ def evalComposed evalComposed = evalN evalN! (eval evalComposed) // * of the need for an algorithm to tie recursive TV knots and inline the rest; // * once we inline, `b` we should get the expected simplified recursive type. ev2 = evalComposed evalComposed! -//│ ev2: ((Add with {lhs: Neg & 'a | 'b & ~#Neg, rhs: Neg & 'a | 'b & ~#Neg}) | (Lit with {n: 'n}) | (Neg with {e: 'c})) -> (int | 'n) +//│ ev2: (Add\lhs\rhs & {lhs: Neg\e & {e: 'a} | 'b & ~#Neg, rhs: Neg\e & {e: 'a} | 'b & ~#Neg} | Lit\n & {n: 'n} | Neg\e & {e: 'c}) -> (int | 'n) //│ where -//│ 'c <: (Add with {lhs: Neg & 'a | 'b & ~#Neg, rhs: Neg & 'a | 'b & ~#Neg}) | Lit | (Neg with {e: 'c}) -//│ 'a <: {e: 'd} -//│ 'd <: 'b | (Neg with {e: 'd}) -//│ 'b <: Add & {lhs: Neg & 'a | 'b & ~#Neg, rhs: Neg & 'a | 'b & ~#Neg} | Lit +//│ 'c <: Add\lhs\rhs & {lhs: Neg\e & {e: 'a} | 'b & ~#Neg, rhs: Neg\e & {e: 'a} | 'b & ~#Neg} | Lit | Neg\e & {e: 'c} +//│ 'a <: Add\lhs\rhs & {lhs: 'lhs, rhs: 'rhs} | Lit | Neg\e & {e: 'a} +//│ 'lhs <: Neg\e & {e: 'a} | 'b & ~#Neg +//│ 'b <: Add\lhs\rhs & {lhs: 'lhs, rhs: 'rhs} | Lit +//│ 'rhs <: Neg\e & {e: 'a} | 'b & ~#Neg //│ = [Function (anonymous)] ev2 e3 @@ -152,12 +153,13 @@ def ev2_ty: (Add & { lhs: 'a; rhs: 'a } | Lit | Neg & { e: 'a } as 'a) -> int //│ = ev2_ty = ev2 -//│ ((Add with {lhs: Neg & 'a | 'b & ~#Neg, rhs: Neg & 'a | 'b & ~#Neg}) | (Lit with {n: 'n}) | (Neg with {e: 'c})) -> (int | 'n) +//│ (Add\lhs\rhs & {lhs: Neg\e & {e: 'a} | 'b & ~#Neg, rhs: Neg\e & {e: 'a} | 'b & ~#Neg} | Lit\n & {n: 'n} | Neg\e & {e: 'c}) -> (int | 'n) //│ where -//│ 'c <: (Add with {lhs: Neg & 'a | 'b & ~#Neg, rhs: Neg & 'a | 'b & ~#Neg}) | Lit | (Neg with {e: 'c}) -//│ 'a <: {e: 'd} -//│ 'd <: 'b | (Neg with {e: 'd}) -//│ 'b <: Add & {lhs: Neg & 'a | 'b & ~#Neg, rhs: Neg & 'a | 'b & ~#Neg} | Lit +//│ 'c <: Add\lhs\rhs & {lhs: Neg\e & {e: 'a} | 'b & ~#Neg, rhs: Neg\e & {e: 'a} | 'b & ~#Neg} | Lit | Neg\e & {e: 'c} +//│ 'a <: Add\lhs\rhs & {lhs: 'lhs, rhs: 'rhs} | Lit | Neg\e & {e: 'a} +//│ 'lhs <: Neg\e & {e: 'a} | 'b & ~#Neg +//│ 'b <: Add\lhs\rhs & {lhs: 'lhs, rhs: 'rhs} | Lit +//│ 'rhs <: Neg\e & {e: 'a} | 'b & ~#Neg //│ <: ev2_ty: //│ 'a -> int //│ where diff --git a/shared/src/test/diff/mlscript/HeadOption.mls b/shared/src/test/diff/mlscript/HeadOption.mls index 5e25fca683..0326c02209 100644 --- a/shared/src/test/diff/mlscript/HeadOption.mls +++ b/shared/src/test/diff/mlscript/HeadOption.mls @@ -58,57 +58,59 @@ class Cons[A]: List[A] & { head: A; tail: List[A] } :stats l0 = Cons { head = 1; tail = Nil {} } //│ l0: Cons[1] with {tail: Nil[?]} -//│ constrain calls : 25 -//│ annoying calls : 0 -//│ subtyping calls : 87 +//│ constrain calls : 24 +//│ annoying calls : 1 +//│ subtyping calls : 84 :stats Cons.HeadOption l0 //│ res: Some[1] -//│ constrain calls : 49 -//│ annoying calls : 18 -//│ subtyping calls : 151 +//│ constrain calls : 48 +//│ annoying calls : 22 +//│ subtyping calls : 149 :stats l1 = Cons { head = 1; tail = Cons { head = 2; tail = Cons { head = 3; tail = Nil {} } } } //│ l1: Cons[1] with {tail: Cons[2] with {tail: Cons[3] with {tail: Nil[?]}}} -//│ constrain calls : 55 -//│ annoying calls : 0 -//│ subtyping calls : 251 +//│ constrain calls : 54 +//│ annoying calls : 1 +//│ subtyping calls : 246 :stats Cons.HeadOption l1 //│ res: Some[1] //│ constrain calls : 45 -//│ annoying calls : 18 +//│ annoying calls : 21 //│ subtyping calls : 154 :stats l2 = Cons { head = 0; tail = l1 } //│ l2: Cons[0] with {tail: Cons[1] with {tail: Cons[2] with {tail: Cons[3] with {tail: Nil[?]}}}} //│ constrain calls : 20 -//│ annoying calls : 0 -//│ subtyping calls : 173 +//│ annoying calls : 1 +//│ subtyping calls : 169 :stats Cons.HeadOption l2 //│ res: Some[0] //│ constrain calls : 51 -//│ annoying calls : 18 +//│ annoying calls : 22 //│ subtyping calls : 155 :stats l3 = Cons { head = 0-1; tail = l2 } -//│ l3: Cons[int] with {tail: Cons[0] with {tail: Cons[1] with {tail: Cons[2] with {tail: Cons[3] with {tail: Nil[?]}}}}} +//│ l3: Cons[int] with { +//│ tail: Cons[0] with {tail: Cons[1] with {tail: Cons[2] with {tail: Cons[3] with {tail: Nil[?]}}}} +//│ } //│ constrain calls : 34 -//│ annoying calls : 0 -//│ subtyping calls : 258 +//│ annoying calls : 1 +//│ subtyping calls : 253 :stats Cons.HeadOption l3 //│ res: Some[int] //│ constrain calls : 57 -//│ annoying calls : 18 +//│ annoying calls : 22 //│ subtyping calls : 182 @@ -119,52 +121,54 @@ rec def lr1 = Cons { head = 0; tail = lr1 } //│ 'tail :> Cons[0] with {tail: 'tail} //│ constrain calls : 19 //│ annoying calls : 0 -//│ subtyping calls : 97 +//│ subtyping calls : 96 :stats Cons.HeadOption lr1 //│ res: Some[0] -//│ constrain calls : 51 -//│ annoying calls : 18 -//│ subtyping calls : 165 +//│ constrain calls : 48 +//│ annoying calls : 21 +//│ subtyping calls : 162 :stats rec def lr2 = Cons { head = 0; tail = Cons { head = 1; tail = Cons { head = 3; tail = lr2 } } } -//│ lr2: 'tail +//│ lr2: Cons[0] with {tail: 'tail} //│ where -//│ 'tail :> Cons[0] with {tail: Cons[1] with {tail: Cons[3] with {tail: 'tail}}} +//│ 'tail :> Cons[1] with {tail: Cons[3] with {tail: Cons[0] with {tail: 'tail}}} //│ constrain calls : 49 //│ annoying calls : 0 -//│ subtyping calls : 261 +//│ subtyping calls : 280 :stats Cons.HeadOption lr2 //│ res: Some[0] -//│ constrain calls : 50 -//│ annoying calls : 18 -//│ subtyping calls : 159 +//│ constrain calls : 47 +//│ annoying calls : 21 +//│ subtyping calls : 156 :e l1 = Cons { tail = Cons { tail = Cons { tail = Nil {} } } } //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.149: l1 = Cons { tail = Cons { tail = Cons { tail = Nil {} } } } +//│ ║ l.151: l1 = Cons { tail = Cons { tail = Cons { tail = Nil {} } } } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record literal of type `{tail: ?a}` does not have field 'head' -//│ ║ l.149: l1 = Cons { tail = Cons { tail = Cons { tail = Nil {} } } } +//│ ║ l.151: l1 = Cons { tail = Cons { tail = Cons { tail = Nil {} } } } //│ ╙── ^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.149: l1 = Cons { tail = Cons { tail = Cons { tail = Nil {} } } } +//│ ║ l.151: l1 = Cons { tail = Cons { tail = Cons { tail = Nil {} } } } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record literal of type `{tail: ?a}` does not have field 'head' -//│ ║ l.149: l1 = Cons { tail = Cons { tail = Cons { tail = Nil {} } } } +//│ ║ l.151: l1 = Cons { tail = Cons { tail = Cons { tail = Nil {} } } } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.149: l1 = Cons { tail = Cons { tail = Cons { tail = Nil {} } } } +//│ ║ l.151: l1 = Cons { tail = Cons { tail = Cons { tail = Nil {} } } } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record literal of type `{tail: ?a}` does not have field 'head' -//│ ║ l.149: l1 = Cons { tail = Cons { tail = Cons { tail = Nil {} } } } +//│ ║ l.151: l1 = Cons { tail = Cons { tail = Cons { tail = Nil {} } } } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ l1: (Cons[nothing] with {tail: (Cons[nothing] with {tail: (Cons[nothing] with {tail: Nil[?]}) | error}) | error}) | error +//│ l1: (Cons[nothing] with { +//│ tail: (Cons[nothing] with {tail: (Cons[nothing] with {tail: Nil[?]}) | error}) | error +//│ }) | error diff --git a/shared/src/test/diff/mlscript/Huawei2.mls b/shared/src/test/diff/mlscript/Huawei2.mls new file mode 100644 index 0000000000..a8e0499b65 --- /dev/null +++ b/shared/src/test/diff/mlscript/Huawei2.mls @@ -0,0 +1,34 @@ + +class C[A]: { a: A -> A } +//│ Defined class C[=A] + +class D: C[int] & { b: int } +//│ Defined class D + +d = D{a = id; b = if true then 0 else 1} +//│ d: D with {a: forall 'a. 'a -> 'a, b: 0 | 1} +//│ = D { a: [Function: id], b: 0 } + +d.a true +//│ res: true +//│ = true + +d.a 0 +//│ res: 0 +//│ = 0 + +d : #D +//│ res: D +//│ = D { a: [Function: id], b: 0 } + +def foo x = case x of D -> x.b, _ -> 0 +//│ foo: ((D\a with {b: 'b}) | ~D) -> (0 | 'b) +//│ = [Function: foo] + +// Overloading through intersections: not supported +// foo: ((D\a with {b: 'b}) -> 'b) & (~D -> 0) + +foo d +//│ res: 0 | 1 +//│ = 0 + diff --git a/shared/src/test/diff/mlscript/LetPolym.mls b/shared/src/test/diff/mlscript/LetPolym.mls index e42edf627c..9da8acedd3 100644 --- a/shared/src/test/diff/mlscript/LetPolym.mls +++ b/shared/src/test/diff/mlscript/LetPolym.mls @@ -136,18 +136,15 @@ def test f = //│ test: ((1 | 2) -> int) -> (int -> int, int -> int,) //│ = [Function: test11] -:re // TODO f_g = test succ -f_g._1 42 -f_g._2 42 +f_g.0 42 +f_g.1 42 //│ f_g: (int -> int, int -> int,) //│ = [ [Function (anonymous)], [Function (anonymous)] ] //│ res: int -//│ Runtime error: -//│ TypeError: f_g._1 is not a function +//│ = 44 //│ res: int -//│ Runtime error: -//│ TypeError: f_g._2 is not a function +//│ = 45 def test f = @@ -159,12 +156,12 @@ def test f = :e test succ //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.160: test succ +//│ ║ l.157: test succ //│ ║ ^^^^^^^^^ //│ ╟── string literal of type `"ok"` is not an instance of type `int` -//│ ║ l.155: in (local add 1, local concat "ok") +//│ ║ l.152: in (local add 1, local concat "ok") //│ ╙── ^^^^ -//│ res: (int -> int, string -> string,) | error +//│ res: error | (int -> int, string -> string,) //│ = [ [Function (anonymous)], [Function (anonymous)] ] @@ -201,18 +198,18 @@ fun f -> fun x -> :e res add "1" //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.202: res add "1" +//│ ║ l.199: res add "1" //│ ║ ^^^^^^^^^^^ //│ ╟── string literal of type `"1"` is not an instance of type `int` -//│ ║ l.202: res add "1" +//│ ║ l.199: res add "1" //│ ║ ^^^ //│ ╟── Note: constraint arises from reference: -//│ ║ l.196: let local = (fun y -> f y) x +//│ ║ l.193: let local = (fun y -> f y) x //│ ║ ^ //│ ╟── from reference: -//│ ║ l.196: let local = (fun y -> f y) x +//│ ║ l.193: let local = (fun y -> f y) x //│ ╙── ^ -//│ res: () | error +//│ res: error | () //│ = [] fun f -> fun x -> @@ -224,18 +221,18 @@ fun f -> fun x -> :e res add "1" //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.225: res add "1" +//│ ║ l.222: res add "1" //│ ║ ^^^^^^^^^^^ //│ ╟── string literal of type `"1"` is not an instance of type `int` -//│ ║ l.225: res add "1" +//│ ║ l.222: res add "1" //│ ║ ^^^ //│ ╟── Note: constraint arises from application: -//│ ║ l.219: let local = f ((fun y -> y) x) +//│ ║ l.216: let local = f ((fun y -> y) x) //│ ║ ^^^^^^^^^^^^^^ //│ ╟── from reference: -//│ ║ l.219: let local = f ((fun y -> y) x) +//│ ║ l.216: let local = f ((fun y -> y) x) //│ ╙── ^ -//│ res: () | error +//│ res: error | () //│ = [] def id: 'a -> 'a @@ -254,17 +251,17 @@ fun f -> fun x -> let tmp = add x 1 in x )) (fun f -> f true) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.253: (fun k -> k (fun x -> +//│ ║ l.250: (fun k -> k (fun x -> //│ ║ ^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.254: let tmp = add x 1 in x +//│ ║ l.251: let tmp = add x 1 in x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.255: )) (fun f -> f true) +//│ ║ l.252: )) (fun f -> f true) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` -//│ ║ l.255: )) (fun f -> f true) +//│ ║ l.252: )) (fun f -> f true) //│ ║ ^^^^ //│ ╟── Note: constraint arises from reference: -//│ ║ l.254: let tmp = add x 1 in x +//│ ║ l.251: let tmp = add x 1 in x //│ ╙── ^ //│ res: error | true //│ = true @@ -276,21 +273,21 @@ fun f -> fun x -> ) in test ) (fun f -> f true) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.273: (fun k -> +//│ ║ l.270: (fun k -> //│ ║ ^^^^^^^^^ -//│ ║ l.274: let test = k (fun x -> +//│ ║ l.271: let test = k (fun x -> //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.275: let tmp = add x 1 in x +//│ ║ l.272: let tmp = add x 1 in x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.276: ) in test +//│ ║ l.273: ) in test //│ ║ ^^^^^^^^^^^^^ -//│ ║ l.277: ) (fun f -> f true) +//│ ║ l.274: ) (fun f -> f true) //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` -//│ ║ l.277: ) (fun f -> f true) +//│ ║ l.274: ) (fun f -> f true) //│ ║ ^^^^ //│ ╟── Note: constraint arises from reference: -//│ ║ l.275: let tmp = add x 1 in x +//│ ║ l.272: let tmp = add x 1 in x //│ ╙── ^ //│ res: error | true //│ = true @@ -299,9 +296,9 @@ fun f -> fun x -> def fst ((a, b)) = a def snd ((a, b)) = b -//│ fst: ('a, anything,) -> 'a +//│ fst: (('a, anything,),) -> 'a //│ = [Function: fst] -//│ snd: (anything, 'a,) -> 'a +//│ snd: ((anything, 'a,),) -> 'a //│ = [Function: snd] def foo f = @@ -326,7 +323,7 @@ foo (fun x -> (0, x + 1)) def foo (f: 'a -> ('a, 'a)) = let x = f 42 in fst x + snd x -//│ foo: (('a | 42) -> (int & 'a, int & 'a,)) -> int +//│ foo: ((42 | 'a) -> (int & 'a, int & 'a,)) -> int //│ = [Function: foo2] foo (fun x -> (x, x)) @@ -349,16 +346,16 @@ foo (fun x -> (x, x)) :e foo (fun x -> (0, x + 1)) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.350: foo (fun x -> (0, x + 1)) +//│ ║ l.347: foo (fun x -> (0, x + 1)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'a` is not an instance of type `int` -//│ ║ l.339: def foo (f: forall 'a. 'a -> ('a, 'a)) = +//│ ║ l.336: def foo (f: forall 'a. 'a -> ('a, 'a)) = //│ ║ ^^ //│ ╟── Note: constraint arises from reference: -//│ ║ l.350: foo (fun x -> (0, x + 1)) +//│ ║ l.347: foo (fun x -> (0, x + 1)) //│ ║ ^ //│ ╟── Note: quantified type variable 'a is defined at: -//│ ║ l.339: def foo (f: forall 'a. 'a -> ('a, 'a)) = +//│ ║ l.336: def foo (f: forall 'a. 'a -> ('a, 'a)) = //│ ╙── ^^ //│ res: error | int //│ = 43 diff --git a/shared/src/test/diff/mlscript/Luyu.mls b/shared/src/test/diff/mlscript/Luyu.mls index aef1473d4a..7c899cb8c2 100644 --- a/shared/src/test/diff/mlscript/Luyu.mls +++ b/shared/src/test/diff/mlscript/Luyu.mls @@ -55,7 +55,7 @@ mib0.Map (fun _ -> "str") //│ ╟── from application: //│ ║ l.23: method Map f = MappableIntBox { value = f this.value } //│ ╙── ^^^^^^^^^^^^ -//│ res: error | (MappableIntBox with {value: "str"}) +//│ res: (MappableIntBox with {value: "str"}) | error //│ = MappableIntBox { value: 'str' } class BetterIntBox: { value: int } diff --git a/shared/src/test/diff/mlscript/MLList.mls b/shared/src/test/diff/mlscript/MLList.mls index e10ad7ee57..57ce4300c7 100644 --- a/shared/src/test/diff/mlscript/MLList.mls +++ b/shared/src/test/diff/mlscript/MLList.mls @@ -34,7 +34,7 @@ ls = cons({key="I"; val=id}, cons({key="omega"; val=fun x -> x x}, nil)) //│ } res = find(ls, "omega") -//│ res: ('a -> 'b & 'b & 'a) -> 'b | undefined +//│ res: undefined | ('a -> 'b & 'b & 'a) -> 'b //│ = [Function: val] case res of undefined -> "???", _ -> res id "!" @@ -42,6 +42,6 @@ case res of undefined -> "???", _ -> res id "!" //│ = '!' find(ls, "oops") -//│ res: ('a -> 'b & 'c & 'a) -> ('b | 'c) | undefined +//│ res: undefined | ('a -> 'b & 'c & 'a) -> ('b | 'c) //│ = undefined diff --git a/shared/src/test/diff/mlscript/MLTuples.mls b/shared/src/test/diff/mlscript/MLTuples.mls index 33fbe608ef..bc9debd5a3 100644 --- a/shared/src/test/diff/mlscript/MLTuples.mls +++ b/shared/src/test/diff/mlscript/MLTuples.mls @@ -27,7 +27,7 @@ Hey t //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.26: Hey t //│ ║ ^^^^^ -//│ ╟── tuple literal of type `{_1: 1, _2: 2, _3: 3}` does not have field 'x' +//│ ╟── tuple literal of type `{0: 1, 1: 2, 2: 3}` does not have field 'x' //│ ║ l.2: t = (1, 2, 3) //│ ║ ^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{x: int}` @@ -36,7 +36,7 @@ Hey t //│ ╟── Note: constraint arises from record type: //│ ║ l.22: trait Hey: { x: int } //│ ╙── ^^^^^^^^^^ -//│ res: (1, 2, 3,) & #Hey | error +//│ res: error | (1, 2, 3,) & #Hey //│ = [ 1, 2, 3 ] htx = Hey tx @@ -48,7 +48,7 @@ htx.x //│ = 1 f ((a, b, c)) = { a; b; c } -//│ f: ('a, 'b, 'c,) -> {a: 'a, b: 'b, c: 'c} +//│ f: (('a, 'b, 'c,),) -> {a: 'a, b: 'b, c: 'c} //│ = [Function: f] f t @@ -64,7 +64,7 @@ f htx //│ = { a: 1, b: 2, c: 3 } f ((a, b)) = add a b -//│ f: (int, int,) -> int +//│ f: ((int, int,),) -> int //│ = [Function: f1] :e @@ -121,9 +121,9 @@ g arg = case arg of { Hey -> arg.x | _ -> () } g htx g tx -//│ res: () | 1 +//│ res: 1 | () //│ = 1 -//│ res: () | 1 +//│ res: 1 | () //│ = 1 // TODO: pattern match for traits in JavaScript @@ -158,13 +158,13 @@ if bool then (1,) else (2,) :ge if bool then (1,) else (2, 3) -//│ res: Array[1 | 2 | 3] & {_1: 1 | 2} +//│ res: Array[1 | 2 | 3] & {0: 1 | 2} //│ Code generation encountered an error: //│ type alias bool is not a valid expression :ge if bool then (1,) with { a = 1; b = 2 } else (2, 3) with { b = 3; c = 4 } -//│ res: Array[1 | 2 | 3] & {_1: 1 | 2, b: 2 | 3} +//│ res: Array[1 | 2 | 3] & {0: 1 | 2, b: 2 | 3} //│ Code generation encountered an error: //│ type alias bool is not a valid expression @@ -176,12 +176,12 @@ if bool then (1,) else fun x -> x -t._1 -t._2 +t.0 +t.1 //│ res: 1 -//│ = undefined +//│ = 1 //│ res: 2 -//│ = undefined +//│ = 2 t = (1, 2, 3) with {x = 1} @@ -193,32 +193,32 @@ t.x //│ res: 1 //│ = 1 -t._1 -t._2 +t.0 +t.1 //│ res: 1 -//│ = undefined +//│ = 1 //│ res: 2 -//│ = undefined +//│ = 2 -t = (1, 2, 3) with {_1 = "oops"} -//│ t: {_1: "oops", _2: 2, _3: 3} -//│ = [ 1, 2, 3, _1: 'oops' ] +t = (1, 2, 3) with {0 = "oops"} +//│ t: {0: "oops", 1: 2, 2: 3} +//│ = [ 'oops', 2, 3 ] // TODO (https://github.com/hkust-taco/mlscript/issues/69) :e -(t: ("oops",int,int,))._1 +(t: ("oops",int,int,)).0 //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.210: (t: ("oops",int,int,))._1 +//│ ║ l.210: (t: ("oops",int,int,)).0 //│ ║ ^ -//│ ╟── `with` extension of type `{_1: "oops", _2: 2, _3: 3}` is not a 3-element tuple -//│ ║ l.204: t = (1, 2, 3) with {_1 = "oops"} -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── `with` extension of type `{0: "oops", 1: 2, 2: 3}` is not a 3-element tuple +//│ ║ l.204: t = (1, 2, 3) with {0 = "oops"} +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `("oops", int, int,)` -//│ ║ l.210: (t: ("oops",int,int,))._1 +//│ ║ l.210: (t: ("oops",int,int,)).0 //│ ║ ^ //│ ╟── Note: constraint arises from tuple type: -//│ ║ l.210: (t: ("oops",int,int,))._1 +//│ ║ l.210: (t: ("oops",int,int,)).0 //│ ╙── ^^^^^^^^^^^^^^^^^ //│ res: "oops" //│ = 'oops' diff --git a/shared/src/test/diff/mlscript/Match3.mls b/shared/src/test/diff/mlscript/Match3.mls index a3334509d9..edbd388f60 100644 --- a/shared/src/test/diff/mlscript/Match3.mls +++ b/shared/src/test/diff/mlscript/Match3.mls @@ -33,7 +33,7 @@ eval_expr (M {}) //│ ╟── Note: class M is defined at: //│ ║ l.3: class M //│ ╙── ^ -//│ res: error | M +//│ res: M | error //│ = M {} def eval_expr v = diff --git a/shared/src/test/diff/mlscript/MatchBool.mls b/shared/src/test/diff/mlscript/MatchBool.mls index 8f4b16a564..4f566be707 100644 --- a/shared/src/test/diff/mlscript/MatchBool.mls +++ b/shared/src/test/diff/mlscript/MatchBool.mls @@ -3,7 +3,7 @@ def absImpl lt x = case lt of { true -> x | false -> 0 - x } -//│ absImpl: bool -> int -> int +//│ absImpl: Bool -> int -> int //│ = [Function: absImpl] // * TODO support this @@ -29,6 +29,6 @@ def abs x = def neg b = case b of { true -> false | false -> true } -//│ neg: bool -> bool +//│ neg: Bool -> Bool //│ = [Function: neg] diff --git a/shared/src/test/diff/mlscript/Methods.mls b/shared/src/test/diff/mlscript/Methods.mls index c914f6250c..83f8ac8d89 100644 --- a/shared/src/test/diff/mlscript/Methods.mls +++ b/shared/src/test/diff/mlscript/Methods.mls @@ -288,7 +288,7 @@ Test3A.F //│ ╔══[ERROR] identifier not found: Test3A //│ ║ l.284: Test3A.F //│ ╙── ^^^^^^ -//│ res: error +//│ res: nothing //│ = undefined class Test3A: Test1[forall 'a. 'a -> 'a] @@ -306,7 +306,7 @@ Test3B.F //│ ╔══[ERROR] identifier not found: Test3B //│ ║ l.302: Test3B.F //│ ╙── ^^^^^^ -//│ res: error +//│ res: nothing //│ = undefined diff --git a/shared/src/test/diff/mlscript/Methods2.mls b/shared/src/test/diff/mlscript/Methods2.mls index 9f1570f885..dc7a990966 100644 --- a/shared/src/test/diff/mlscript/Methods2.mls +++ b/shared/src/test/diff/mlscript/Methods2.mls @@ -88,8 +88,8 @@ l.Map (fun x -> mul x 2) //│ res: List[int] //│ = Cons { head: 0, tail: Cons { head: 2, tail: Nil {} } } //│ constrain calls : 72 -//│ annoying calls : 5 -//│ subtyping calls : 245 +//│ annoying calls : 6 +//│ subtyping calls : 243 l0 = Cons { head = 0; tail = Nil {} } //│ l0: Cons[0] with {tail: Nil[nothing]} @@ -102,8 +102,8 @@ Cons.HeadOption l0 //│ Runtime error: //│ TypeError: (intermediate value).HeadOption is not a function //│ constrain calls : 55 -//│ annoying calls : 28 -//│ subtyping calls : 194 +//│ annoying calls : 31 +//│ subtyping calls : 185 l1 = Cons { head = 0; tail = Nil {} } @@ -117,8 +117,8 @@ Cons.HeadOption l1 //│ Runtime error: //│ TypeError: (intermediate value).HeadOption is not a function //│ constrain calls : 55 -//│ annoying calls : 28 -//│ subtyping calls : 194 +//│ annoying calls : 31 +//│ subtyping calls : 185 :re :stats @@ -127,16 +127,16 @@ Cons.HeadOption l //│ Runtime error: //│ TypeError: (intermediate value).HeadOption is not a function //│ constrain calls : 70 -//│ annoying calls : 28 -//│ subtyping calls : 258 +//│ annoying calls : 31 +//│ subtyping calls : 253 :stats o = l.(Cons.HeadOption) //│ o: Some[0 | 1] //│ = undefined //│ constrain calls : 68 -//│ annoying calls : 28 -//│ subtyping calls : 245 +//│ annoying calls : 31 +//│ subtyping calls : 240 o = l.(Cons.HeadOption) //│ o: Some[0 | 1] diff --git a/shared/src/test/diff/mlscript/Misc.mls b/shared/src/test/diff/mlscript/Misc.mls index 3df7d5ec7e..ac0c9969f0 100644 --- a/shared/src/test/diff/mlscript/Misc.mls +++ b/shared/src/test/diff/mlscript/Misc.mls @@ -46,26 +46,26 @@ def arg = if true then c else 0 :ns def arg = if true then C{x = 42} else C{x = 1} -//│ arg: forall 'A 'A0 'a 'x 'x0. 'a +//│ arg: forall 'A 'a 'x 'x0 'A0. 'a //│ where -//│ 'a :> #C & {x: 'x0, C#A = 'A} | #C & {x: 'x, C#A = 'A0} -//│ 'x :> 1 -//│ <: 'A0 +//│ 'a :> #C & {x: 'x, C#A = 'A} | #C & {x: 'x0, C#A = 'A0} +//│ 'x0 :> 1 +//│ <: 'A0 //│ 'A0 :> 1 -//│ 'x0 :> 42 -//│ <: 'A +//│ 'x :> 42 +//│ <: 'A //│ 'A :> 42 :ns arg -//│ res: forall 'A 'A0 'a 'x 'x0. 'a +//│ res: forall 'A 'a 'x 'x0 'A0. 'a //│ where -//│ 'a :> #C & {x: 'x0, C#A = 'A} | #C & {x: 'x, C#A = 'A0} -//│ 'x :> 1 -//│ <: 'A0 +//│ 'a :> #C & {x: 'x, C#A = 'A} | #C & {x: 'x0, C#A = 'A0} +//│ 'x0 :> 1 +//│ <: 'A0 //│ 'A0 :> 1 -//│ 'x0 :> 42 -//│ <: 'A +//│ 'x :> 42 +//│ <: 'A //│ 'A :> 42 arg diff --git a/shared/src/test/diff/mlscript/MiscExtrusion.mls b/shared/src/test/diff/mlscript/MiscExtrusion.mls index e6c92e247f..a97950199a 100644 --- a/shared/src/test/diff/mlscript/MiscExtrusion.mls +++ b/shared/src/test/diff/mlscript/MiscExtrusion.mls @@ -65,7 +65,7 @@ alsoPrintSize id //│ ╟── • this application: //│ ║ l.50: mapExpr(fun e -> let tmp = print e in f e) //│ ╙── ^^^ -//│ res: Program -> Program | error +//│ res: error | Program -> Program def alsoPrintSize (f: forall 'a. Expr['a] -> Expr['a]) = mapExpr(fun e -> let tmp = print e in f e) @@ -104,7 +104,7 @@ alsoPrintSizeCo id //│ ╟── • this application: //│ ║ l.89: mapExprCo(fun e -> let tmp = printCo e in f e) //│ ╙── ^^^ -//│ res: Program -> Program | error +//│ res: error | Program -> Program def alsoPrintSizeCo (f: forall 'a. Expr['a] -> Expr['a]) = mapExpr(fun e -> let tmp = print e in f e) diff --git a/shared/src/test/diff/mlscript/MultiArgs.mls b/shared/src/test/diff/mlscript/MultiArgs.mls index 35b8e979b7..9f04e7f150 100644 --- a/shared/src/test/diff/mlscript/MultiArgs.mls +++ b/shared/src/test/diff/mlscript/MultiArgs.mls @@ -65,7 +65,7 @@ bar((1, 2)) def bar((x, y)) = add x y bar((1, 2)) -//│ bar: (int, int,) -> int +//│ bar: ((int, int,),) -> int //│ = [Function: bar4] //│ res: int //│ = 3 @@ -73,11 +73,11 @@ bar((1, 2)) :p 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) -//│ Desugared: f (1, 2,) -//│ AST: App(Var(f), Tup(_: IntLit(1), _: IntLit(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),Left(Lam(Tup(List((None,Fld(_,Var(x))), (None,Fld(_,Var(y))))),App(App(Var(add),Tup(List((None,Fld(_,Var(x)))))),Tup(List((None,Fld(_,Var(y)))))))),false) +//│ Desugared: f(1, 2,) +//│ AST: App(Var(f),Tup(List((None,Fld(_,IntLit(1))), (None,Fld(_,IntLit(2)))))) //│ f: (int, int,) -> int //│ = [Function: f] //│ res: int @@ -117,12 +117,12 @@ f r :p 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) -//│ Desugared: f ('(' 1, 2, ')',) -//│ AST: App(Var(f), Tup(_: Bra(rcd = false, Tup(_: IntLit(1), _: IntLit(2))))) -//│ f: (int, int,) -> int +//│ Parsed: let f = ('(' [x, y,] ')',) => add(x,)(y,); f('(' [1, 2,] ')',); +//│ Desugared: def f: ('(' [x, y,] ')',) => add(x,)(y,) +//│ AST: Def(false,Var(f),Left(Lam(Tup(List((None,Fld(_,Bra(false,Tup(List((None,Fld(_,Var(x))), (None,Fld(_,Var(y)))))))))),App(App(Var(add),Tup(List((None,Fld(_,Var(x)))))),Tup(List((None,Fld(_,Var(y)))))))),false) +//│ Desugared: f('(' [1, 2,] ')',) +//│ AST: App(Var(f),Tup(List((None,Fld(_,Bra(false,Tup(List((None,Fld(_,IntLit(1))), (None,Fld(_,IntLit(2))))))))))) +//│ f: ((int, int,),) -> int //│ = [Function: f1] //│ res: int //│ = 3 diff --git a/shared/src/test/diff/mlscript/Mut.mls b/shared/src/test/diff/mlscript/Mut.mls index 86517480ec..be6748ff56 100644 --- a/shared/src/test/diff/mlscript/Mut.mls +++ b/shared/src/test/diff/mlscript/Mut.mls @@ -97,18 +97,18 @@ immtpl = (1: int,) //│ = [ 1 ] immrcd.x -immtpl._1 +immtpl.0 immtpl[0] //│ res: int //│ = 1 //│ res: int -//│ = undefined +//│ = 1 //│ res: int | undefined //│ = 1 :e immrcd.x <- 0 -immtpl._1 <- 0 +immtpl.0 <- 0 immtpl[0] <- 0 //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.110: immrcd.x <- 0 @@ -121,14 +121,14 @@ immtpl[0] <- 0 //│ ╙── ^ //│ = [] //│ ╔══[ERROR] Type mismatch in assignment: -//│ ║ l.111: immtpl._1 <- 0 -//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.111: immtpl.0 <- 0 +//│ ║ ^^^^^^^^^^^^^ //│ ╟── tuple field of type `int` is not mutable //│ ║ l.93: immtpl = (1: int,) //│ ║ ^ -//│ ╟── but it flows into assigned field with expected type `?a` -//│ ║ l.111: immtpl._1 <- 0 -//│ ╙── ^^ +//│ ╟── but it flows into assigned field with expected type `?0` +//│ ║ l.111: immtpl.0 <- 0 +//│ ╙── ^ //│ = [] //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.112: immtpl[0] <- 0 @@ -209,7 +209,7 @@ ga a1 //│ ╟── from reference: //│ ║ l.185: def ga r = (fun x -> r) (r[1] <- 6) //│ ╙── ^ -//│ res: Array[int] | error +//│ res: error | Array[int] //│ = //│ a1 is not implemented @@ -404,7 +404,7 @@ mt1 : (int, bool) //│ mt1 is not implemented def emt: (mut int) -emt._1 +emt.0 //│ emt: (mut int,) //│ = //│ res: int @@ -412,7 +412,7 @@ emt._1 //│ emt is not implemented k1 = (mut 233, "hello", mut true) -k1._1 <- k1._1 + 1 +k1.0 <- k1.0 + 1 //│ k1: (mut 'a, "hello", mut 'b,) //│ where //│ 'b :> true @@ -421,16 +421,19 @@ k1._1 <- k1._1 + 1 //│ = [] :e -k1._2 <- 233 +k1.1 <- 233 //│ ╔══[ERROR] Type mismatch in assignment: -//│ ║ l.424: k1._2 <- 233 -//│ ║ ^^^^^^^^^^^^ -//│ ╟── tuple field of type `"hello"` is not mutable +//│ ║ l.424: k1.1 <- 233 +//│ ║ ^^^^^^^^^^^ +//│ ╟── tuple literal of type `forall ?a ?b. (mut ?a, "hello", mut ?b,)` does not have field '1' //│ ║ l.414: k1 = (mut 233, "hello", mut true) -//│ ║ ^^^^^^^ -//│ ╟── but it flows into assigned field with expected type `?a` -//│ ║ l.424: k1._2 <- 233 -//│ ╙── ^^ +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── but it flows into reference with expected type `{mut 1: in ?1}` +//│ ║ l.424: k1.1 <- 233 +//│ ║ ^^ +//│ ╟── Note: constraint arises from assigned selection: +//│ ║ l.424: k1.1 <- 233 +//│ ╙── ^^^^ //│ = [] mt1 = (mut 3, mut false) @@ -454,16 +457,16 @@ amf mt3 :e amf mt4 //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.455: amf mt4 +//│ ║ l.458: amf mt4 //│ ║ ^^^^^^^ //│ ╟── type `(mut bool, bool, bool,)` does not match type `MutArray['a]` //│ ║ l.384: def mt4: (mut bool, bool, bool) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `MutArray['a]` -//│ ║ l.455: amf mt4 +//│ ║ l.458: amf mt4 //│ ║ ^^^ //│ ╟── Note: constraint arises from applied type reference: -//│ ║ l.445: def amf : MutArray['a] -> 'a +//│ ║ l.448: def amf : MutArray['a] -> 'a //│ ╙── ^^^^^^^^^^^^ //│ res: bool | error //│ = @@ -471,64 +474,64 @@ amf mt4 :e a1[0] <- 1 -mt1[0] <- mt2._1 +mt1[0] <- mt2.0 mt4[3] <- true //│ ╔══[ERROR] Type mismatch in assignment: -//│ ║ l.473: a1[0] <- 1 +//│ ║ l.476: a1[0] <- 1 //│ ║ ^^^^^^^^^^ //│ ╟── type `Array[int]` does not match type `MutArray[?a]` //│ ║ l.30: def a1: Array[int] //│ ║ ^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `MutArray[?a]` -//│ ║ l.473: a1[0] <- 1 +//│ ║ l.476: a1[0] <- 1 //│ ╙── ^^ //│ = //│ a1 is not implemented //│ ╔══[ERROR] Type mismatch in assignment: -//│ ║ l.474: mt1[0] <- mt2._1 -//│ ║ ^^^^^^^^^^^^^^^^ -//│ ╟── type `int` is not an instance of `bool` +//│ ║ l.477: mt1[0] <- mt2.0 +//│ ║ ^^^^^^^^^^^^^^^ +//│ ╟── type `int` is not an instance of type `bool` //│ ║ l.381: def mt1: (mut int, mut bool) //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.381: def mt1: (mut int, mut bool) //│ ║ ^^^^ //│ ╟── from assigned array element: -//│ ║ l.474: mt1[0] <- mt2._1 +//│ ║ l.477: mt1[0] <- mt2.0 //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in assignment: -//│ ║ l.474: mt1[0] <- mt2._1 -//│ ║ ^^^^^^^^^^^^^^^^ -//│ ╟── type `int` is not an instance of `bool` +//│ ║ l.477: mt1[0] <- mt2.0 +//│ ║ ^^^^^^^^^^^^^^^ +//│ ╟── type `int` is not an instance of type `bool` //│ ║ l.382: def mt2: (int, int) //│ ║ ^^^ //│ ╟── but it flows into field selection with expected type `bool` -//│ ║ l.474: mt1[0] <- mt2._1 -//│ ║ ^^^^^^ +//│ ║ l.477: mt1[0] <- mt2.0 +//│ ║ ^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.381: def mt1: (mut int, mut bool) //│ ║ ^^^^ //│ ╟── from assigned array element: -//│ ║ l.474: mt1[0] <- mt2._1 +//│ ║ l.477: mt1[0] <- mt2.0 //│ ╙── ^^^^^^ //│ = //│ mt2 is not implemented //│ ╔══[ERROR] Type mismatch in assignment: -//│ ║ l.475: mt4[3] <- true +//│ ║ l.478: mt4[3] <- true //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type `(mut bool, bool, bool,)` does not match type `MutArray[?a]` //│ ║ l.384: def mt4: (mut bool, bool, bool) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `MutArray[?a]` -//│ ║ l.475: mt4[3] <- true +//│ ║ l.478: mt4[3] <- true //│ ╙── ^^^ //│ = [] -mt1._1 <- mt2._1 -mt1._1 <- mt1._1 * 2 -mt1._2 <- false +mt1.0 <- mt2.0 +mt1.0 <- mt1.0 * 2 +mt1.1 <- false mt3[0] <- let tmp = mt3[1] in case tmp of { undefined -> 0 | _ -> tmp } -mt3[1] <- mt1._1 +mt3[1] <- mt1.0 //│ = //│ mt2 is not implemented //│ = [] @@ -540,89 +543,89 @@ mt3[1] <- mt1._1 :e :ge -mt1._1 <- mt1._2 -mt1._2 <- 1 -mt1._1 <- (b1.t <- 4) -(mt1._1 <- b1.t) <- 4 +mt1.0 <- mt1.1 +mt1.1 <- 1 +mt1.0 <- (b1.t <- 4) +(mt1.0 <- b1.t) <- 4 b1.x <- 1 + 2 <- 4 //│ ╔══[ERROR] Type mismatch in assignment: -//│ ║ l.543: mt1._1 <- mt1._2 -//│ ║ ^^^^^^^^^^^^^^^^ -//│ ╟── type `bool` is not an instance of `int` +//│ ║ l.546: mt1.0 <- mt1.1 +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── type `bool` is not an instance of type `int` //│ ║ l.381: def mt1: (mut int, mut bool) //│ ║ ^^^^ //│ ╟── but it flows into field selection with expected type `int` -//│ ║ l.543: mt1._1 <- mt1._2 -//│ ║ ^^^^^^ +//│ ║ l.546: mt1.0 <- mt1.1 +//│ ║ ^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.381: def mt1: (mut int, mut bool) //│ ║ ^^^ //│ ╟── from assigned selection: -//│ ║ l.543: mt1._1 <- mt1._2 -//│ ╙── ^^^^^^ +//│ ║ l.546: mt1.0 <- mt1.1 +//│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in assignment: -//│ ║ l.544: mt1._2 <- 1 -//│ ║ ^^^^^^^^^^^ +//│ ║ l.547: mt1.1 <- 1 +//│ ║ ^^^^^^^^^^ //│ ╟── integer literal of type `1` is not an instance of type `bool` -//│ ║ l.544: mt1._2 <- 1 -//│ ║ ^ +//│ ║ l.547: mt1.1 <- 1 +//│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.381: def mt1: (mut int, mut bool) //│ ║ ^^^^ //│ ╟── from assigned selection: -//│ ║ l.544: mt1._2 <- 1 -//│ ╙── ^^^^^^ +//│ ║ l.547: mt1.1 <- 1 +//│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in assignment: -//│ ║ l.545: mt1._1 <- (b1.t <- 4) -//│ ║ ^^^^^^^^^ +//│ ║ l.548: mt1.0 <- (b1.t <- 4) +//│ ║ ^^^^^^^^^ //│ ╟── type `B` does not have field 't' //│ ║ l.265: def b1 : B //│ ║ ^ //│ ╟── but it flows into reference with expected type `{mut t: in ?t}` -//│ ║ l.545: mt1._1 <- (b1.t <- 4) -//│ ║ ^^ +//│ ║ l.548: mt1.0 <- (b1.t <- 4) +//│ ║ ^^ //│ ╟── Note: constraint arises from assigned selection: -//│ ║ l.545: mt1._1 <- (b1.t <- 4) -//│ ╙── ^^^^ +//│ ║ l.548: mt1.0 <- (b1.t <- 4) +//│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in assignment: -//│ ║ l.545: mt1._1 <- (b1.t <- 4) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.548: mt1.0 <- (b1.t <- 4) +//│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── assignment of type `unit` is not an instance of type `int` -//│ ║ l.545: mt1._1 <- (b1.t <- 4) -//│ ║ ^^^^^^^^^ +//│ ║ l.548: mt1.0 <- (b1.t <- 4) +//│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.381: def mt1: (mut int, mut bool) //│ ║ ^^^ //│ ╟── from assigned selection: -//│ ║ l.545: mt1._1 <- (b1.t <- 4) -//│ ╙── ^^^^^^ +//│ ║ l.548: mt1.0 <- (b1.t <- 4) +//│ ╙── ^^^^^ //│ ╔══[ERROR] Illegal assignment -//│ ║ l.546: (mt1._1 <- b1.t) <- 4 -//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.549: (mt1.0 <- b1.t) <- 4 +//│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── cannot assign to assignment -//│ ║ l.546: (mt1._1 <- b1.t) <- 4 -//│ ╙── ^^^^^^^^^^^^^^^^ +//│ ║ l.549: (mt1.0 <- b1.t) <- 4 +//│ ╙── ^^^^^^^^^^^^^^^ //│ res: error //│ ╔══[ERROR] Illegal assignment -//│ ║ l.547: b1.x <- 1 + 2 <- 4 +//│ ║ l.550: b1.x <- 1 + 2 <- 4 //│ ║ ^^^^^^ //│ ╟── cannot assign to integer literal -//│ ║ l.547: 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), _1), Sel(Var(b1), t))) +//│ illegal assignemnt left-hand side: Bra(false,Assign(Sel(Var(mt1),Var(0)),Sel(Var(b1),Var(t)))) -def f : {mut _1 : int} -> int -> unit +def f : {mut 0 : int} -> int -> unit def g : (mut int, bool) -> int -> unit -//│ f: {mut _1: int} -> int -> unit +//│ f: {mut 0: int} -> int -> unit //│ = //│ g: (mut int, bool,) -> int -> unit //│ = -def f a n = a._1 <- n -//│ {mut _1: in 'a} -> 'a -> unit +def f a n = a.0 <- n +//│ {mut 0: in '0} -> '0 -> unit //│ <: f: -//│ {mut _1: int} -> int -> unit +//│ {mut 0: int} -> int -> unit //│ = [Function: f] f mt1 1 @@ -631,22 +634,22 @@ f mt1 1 :e f mt2 //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.632: f mt2 +//│ ║ l.635: f mt2 //│ ║ ^^^^^ //│ ╟── tuple field of type `int` is not mutable //│ ║ l.382: def mt2: (int, int) //│ ╙── ^^^ -//│ res: int -> unit | error +//│ res: error | int -> unit //│ = //│ mt2 is not implemented :e g (1, true) 2 //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.644: g (1, true) 2 +//│ ║ l.647: g (1, true) 2 //│ ║ ^^^^^^^^^^^ //│ ╟── argument of type `1` is not mutable -//│ ║ l.644: g (1, true) 2 +//│ ║ l.647: g (1, true) 2 //│ ╙── ^ //│ res: error | unit //│ = @@ -655,10 +658,10 @@ g (1, true) 2 // TODO forbid `mut` here g (mut 1, true) 2 //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.656: g (mut 1, true) 2 +//│ ║ l.659: g (mut 1, true) 2 //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── argument of type `1` is not mutable -//│ ║ l.656: g (mut 1, true) 2 +//│ ║ l.659: g (mut 1, true) 2 //│ ╙── ^ //│ res: error | unit //│ = @@ -696,20 +699,20 @@ st2 = (mut 4,) //│ (mut int,) //│ = [ 4 ] -st2._1 <- 8 +st2.0 <- 8 //│ = [] :e -st1._1 <- 9 +st1.0 <- 9 //│ ╔══[ERROR] Type mismatch in assignment: -//│ ║ l.703: st1._1 <- 9 -//│ ║ ^^^^^^^^^^^ +//│ ║ l.706: st1.0 <- 9 +//│ ║ ^^^^^^^^^^ //│ ╟── tuple field of type `int` is not mutable -//│ ║ l.679: def st1 : (int, ) +//│ ║ l.682: def st1 : (int, ) //│ ║ ^^^ -//│ ╟── but it flows into assigned field with expected type `?a` -//│ ║ l.703: st1._1 <- 9 -//│ ╙── ^^ +//│ ╟── but it flows into assigned field with expected type `?0` +//│ ║ l.706: st1.0 <- 9 +//│ ╙── ^ //│ = [] def am1 : Array[(mut int)] @@ -721,7 +724,7 @@ def foreach : Array['a] -> ('a -> unit) -> Array['a] //│ = foreach am1 (fun x -> x[0] <- 1) -foreach am1 (fun y -> y._1 <- 2) +foreach am1 (fun y -> y.0 <- 2) //│ res: Array[(mut int,)] //│ = //│ foreach is not implemented @@ -731,41 +734,41 @@ foreach am1 (fun y -> y._1 <- 2) :e (1,2,3)[0] <- true -(1,2,3)._1 <- "hello" +(1,2,3).0 <- "hello" //│ ╔══[ERROR] Type mismatch in assignment: -//│ ║ l.733: (1,2,3)[0] <- true +//│ ║ l.736: (1,2,3)[0] <- true //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── tuple literal of type `(1, 2, 3,)` does not match type `MutArray[?a]` -//│ ║ l.733: (1,2,3)[0] <- true +//│ ║ l.736: (1,2,3)[0] <- true //│ ╙── ^^^^^^^ //│ = [] //│ ╔══[ERROR] Type mismatch in assignment: -//│ ║ l.734: (1,2,3)._1 <- "hello" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.737: (1,2,3).0 <- "hello" +//│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── tuple field of type `1` is not mutable -//│ ║ l.734: (1,2,3)._1 <- "hello" +//│ ║ l.737: (1,2,3).0 <- "hello" //│ ║ ^ -//│ ╟── but it flows into assigned field with expected type `?a` -//│ ║ l.734: (1,2,3)._1 <- "hello" -//│ ╙── ^^ +//│ ╟── but it flows into assigned field with expected type `?0` +//│ ║ l.737: (1,2,3).0 <- "hello" +//│ ╙── ^ //│ = [] :e (0,)["oops"] (mut 0,)["oops"] <- 1 //│ ╔══[ERROR] Type mismatch in array access: -//│ ║ l.754: (0,)["oops"] +//│ ║ l.757: (0,)["oops"] //│ ║ ^^^^^^^^^^^^ //│ ╟── string literal of type `"oops"` is not an instance of type `int` -//│ ║ l.754: (0,)["oops"] +//│ ║ l.757: (0,)["oops"] //│ ╙── ^^^^^^ //│ res: 0 | undefined //│ = undefined //│ ╔══[ERROR] Type mismatch in assignment: -//│ ║ l.755: (mut 0,)["oops"] <- 1 +//│ ║ l.758: (mut 0,)["oops"] <- 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── string literal of type `"oops"` is not an instance of type `int` -//│ ║ l.755: (mut 0,)["oops"] <- 1 +//│ ║ l.758: (mut 0,)["oops"] <- 1 //│ ╙── ^^^^^^ //│ = [] @@ -782,24 +785,24 @@ arr = (mut 0,) arr[oops] arr[oops] <- 1 //│ ╔══[ERROR] Type mismatch in array access: -//│ ║ l.782: arr[oops] +//│ ║ l.785: arr[oops] //│ ║ ^^^^^^^^^ //│ ╟── string literal of type `"oops"` is not an instance of type `int` -//│ ║ l.772: oops = "oops" +//│ ║ l.775: oops = "oops" //│ ║ ^^^^^^ //│ ╟── but it flows into reference with expected type `int` -//│ ║ l.782: arr[oops] +//│ ║ l.785: arr[oops] //│ ╙── ^^^^ //│ res: 0 | undefined //│ = undefined //│ ╔══[ERROR] Type mismatch in assignment: -//│ ║ l.783: arr[oops] <- 1 +//│ ║ l.786: arr[oops] <- 1 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── string literal of type `"oops"` is not an instance of type `int` -//│ ║ l.772: oops = "oops" +//│ ║ l.775: oops = "oops" //│ ║ ^^^^^^ //│ ╟── but it flows into reference with expected type `int` -//│ ║ l.783: arr[oops] <- 1 +//│ ║ l.786: arr[oops] <- 1 //│ ╙── ^^^^ //│ = [] @@ -813,10 +816,10 @@ x = 1 :e x <- 2 //│ ╔══[ERROR] Illegal assignment -//│ ║ l.814: x <- 2 +//│ ║ l.817: x <- 2 //│ ║ ^^^^^^ //│ ╟── cannot assign to reference -//│ ║ l.814: x <- 2 +//│ ║ l.817: x <- 2 //│ ╙── ^ //│ res: error //│ = [] @@ -846,18 +849,18 @@ foo { mut a = 1 } 2 add :e foo { mut a = 1 } 2 3 //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.847: foo { mut a = 1 } 2 3 +//│ ║ l.850: foo { mut a = 1 } 2 3 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `3` is not a function -//│ ║ l.847: foo { mut a = 1 } 2 3 +//│ ║ l.850: foo { mut a = 1 } 2 3 //│ ║ ^ //│ ╟── Note: constraint arises from application: -//│ ║ l.838: def foo x y z = (x.a <- 0, x.a + 1, x.a, x.a <- y, z x.a) +//│ ║ l.841: def foo x y z = (x.a <- 0, x.a + 1, x.a, x.a <- y, z x.a) //│ ║ ^^^^^ //│ ╟── from reference: -//│ ║ l.838: def foo x y z = (x.a <- 0, x.a + 1, x.a, x.a <- y, z x.a) +//│ ║ l.841: def foo x y z = (x.a <- 0, x.a + 1, x.a, x.a <- y, z x.a) //│ ╙── ^ -//│ res: (unit, int, 0 | 1 | 2, unit, nothing,) | error +//│ res: error | (unit, int, 0 | 1 | 2, unit, nothing,) //│ Runtime error: //│ TypeError: z is not a function @@ -865,26 +868,26 @@ foo { mut a = 1 } 2 3 foo { mut a = "oops" } 2 foo { a = 1 } 2 //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.865: foo { mut a = "oops" } 2 +//│ ║ l.868: foo { mut a = "oops" } 2 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── string literal of type `"oops"` is not an instance of type `int` -//│ ║ l.865: foo { mut a = "oops" } 2 +//│ ║ l.868: foo { mut a = "oops" } 2 //│ ║ ^^^^^^ //│ ╟── but it flows into mutable record field with expected type `int` -//│ ║ l.865: foo { mut a = "oops" } 2 +//│ ║ l.868: foo { mut a = "oops" } 2 //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from field selection: -//│ ║ l.838: def foo x y z = (x.a <- 0, x.a + 1, x.a, x.a <- y, z x.a) +//│ ║ l.841: def foo x y z = (x.a <- 0, x.a + 1, x.a, x.a <- y, z x.a) //│ ╙── ^^^ -//│ res: (("oops" | 0 | 2) -> 'a) -> (unit, int, "oops" | 0 | 2, unit, 'a,) | error +//│ res: error | (("oops" | 0 | 2) -> 'a) -> (unit, int, "oops" | 0 | 2, unit, 'a,) //│ = [Function (anonymous)] //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.866: foo { a = 1 } 2 +//│ ║ l.869: foo { a = 1 } 2 //│ ║ ^^^^^^^^^^^^^ //│ ╟── record field of type `1` is not mutable -//│ ║ l.866: foo { a = 1 } 2 +//│ ║ l.869: foo { a = 1 } 2 //│ ╙── ^^^^^ -//│ res: (1 -> 'a) -> (unit, int, 1, unit, 'a,) | error +//│ res: error | (1 -> 'a) -> (unit, int, 1, unit, 'a,) //│ = [Function (anonymous)] diff --git a/shared/src/test/diff/mlscript/Mut2.mls b/shared/src/test/diff/mlscript/Mut2.mls index 527b9bfd75..c4e23d206d 100644 --- a/shared/src/test/diff/mlscript/Mut2.mls +++ b/shared/src/test/diff/mlscript/Mut2.mls @@ -11,13 +11,13 @@ //│ 'a :> 1 | 2 //│ = [ 1, 2 ] -((fun t -> let tmp = t._1 <- 3 in t) ((mut 1, mut 2))): MutArray['a] +((fun t -> let tmp = t.0 <- 3 in t) ((mut 1, mut 2))): MutArray['a] //│ res: MutArray['a] //│ where //│ 'a :> 1 | 2 | 3 -//│ = [ 1, 2, _1: 3 ] +//│ = [ 3, 2 ] -((fun t -> let tmp = t._1 + 1 in t) ((mut 1, mut 2))): MutArray['a] +((fun t -> let tmp = t.0 + 1 in t) ((mut 1, mut 2))): MutArray['a] //│ res: MutArray[in 'a & 'b out 'b] //│ where //│ 'a <: int & 'b @@ -46,19 +46,19 @@ r = if true then t1 else t2 //│ t1 is not implemented :e -r._1 <- 1 +r.0 <- 1 //│ ╔══[ERROR] Type mismatch in assignment: -//│ ║ l.49: r._1 <- 1 -//│ ║ ^^^^^^^^^ +//│ ║ l.49: r.0 <- 1 +//│ ║ ^^^^^^^^ //│ ╟── integer literal of type `1` does not match type `3` -//│ ║ l.49: r._1 <- 1 -//│ ║ ^ +//│ ║ l.49: r.0 <- 1 +//│ ║ ^ //│ ╟── Note: constraint arises from literal type: //│ ║ l.37: def t2: (mut 3, mut 4) //│ ║ ^ //│ ╟── from assigned selection: -//│ ║ l.49: r._1 <- 1 -//│ ╙── ^^^^ +//│ ║ l.49: r.0 <- 1 +//│ ╙── ^^^ //│ = //│ r and t1 are not implemented @@ -75,26 +75,26 @@ r = if true then t1 else t2 //│ = //│ t1 is not implemented -r._1 <- if true then 2 else 3 +r.0 <- if true then 2 else 3 //│ = //│ r and t1 are not implemented :e -r._1 <- if true then 2 else 1 +r.0 <- if true then 2 else 1 //│ ╔══[ERROR] Type mismatch in assignment: -//│ ║ l.83: r._1 <- if true then 2 else 1 -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.83: r.0 <- if true then 2 else 1 +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `1` does not match type `2 | 3 | 4` -//│ ║ l.83: r._1 <- if true then 2 else 1 -//│ ║ ^ +//│ ║ l.83: r.0 <- if true then 2 else 1 +//│ ║ ^ //│ ╟── but it flows into application with expected type `2 | 3 | 4` -//│ ║ l.83: r._1 <- if true then 2 else 1 -//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.83: r.0 <- if true then 2 else 1 +//│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.67: def t2: (mut 2 | 3 | 4) //│ ║ ^^^^^^^^^ //│ ╟── from assigned selection: -//│ ║ l.83: r._1 <- if true then 2 else 1 -//│ ╙── ^^^^ +//│ ║ l.83: r.0 <- if true then 2 else 1 +//│ ╙── ^^^ //│ = //│ r and t1 are not implemented diff --git a/shared/src/test/diff/mlscript/MutArray.mls b/shared/src/test/diff/mlscript/MutArray.mls index a028bc965c..e2775eb50e 100644 --- a/shared/src/test/diff/mlscript/MutArray.mls +++ b/shared/src/test/diff/mlscript/MutArray.mls @@ -29,7 +29,7 @@ set0((1,)) //│ ╟── tuple field of type `1` is not mutable //│ ║ l.25: set0((1,)) //│ ╙── ^ -//│ res: anything -> unit | error +//│ res: error | anything -> unit //│ = [Function (anonymous)] @@ -70,10 +70,8 @@ update0 0 ((mut 1,)) update1 0 ((mut 1,)) //│ = [] -:re // FIXME update2 0 ((mut 1,)) -//│ Runtime error: -//│ TypeError: ((intermediate value) , []) is not a function +//│ = [] @@ -97,16 +95,16 @@ foo = emptyArray :e def bar x = (mut x,) : MutArray[int] & MutArray[string] //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.98: def bar x = (mut x,) : MutArray[int] & MutArray[string] +//│ ║ l.96: def bar x = (mut x,) : MutArray[int] & MutArray[string] //│ ║ ^^^^^^^^ -//│ ╟── type `string` is not an instance of `int` -//│ ║ l.98: def bar x = (mut x,) : MutArray[int] & MutArray[string] +//│ ╟── type `string` is not an instance of type `int` +//│ ║ l.96: def bar x = (mut x,) : MutArray[int] & MutArray[string] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.98: def bar x = (mut x,) : MutArray[int] & MutArray[string] +//│ ║ l.96: def bar x = (mut x,) : MutArray[int] & MutArray[string] //│ ║ ^^^ //│ ╟── from mutable tuple field: -//│ ║ l.98: def bar x = (mut x,) : MutArray[int] & MutArray[string] +//│ ║ l.96: def bar x = (mut x,) : MutArray[int] & MutArray[string] //│ ╙── ^ //│ bar: nothing -> MutArray[in int | string out nothing] //│ = [Function: bar] diff --git a/shared/src/test/diff/mlscript/Neg.mls b/shared/src/test/diff/mlscript/Neg.mls index f0cbc10ea1..e3f02f9719 100644 --- a/shared/src/test/diff/mlscript/Neg.mls +++ b/shared/src/test/diff/mlscript/Neg.mls @@ -136,16 +136,29 @@ def f: (~{x: 1 | 2} & 'a | ~{x: 2 | 3} & 'a) -> 'a f = id //│ 'a -> 'a //│ <: f: -//│ 'a -> 'a +//│ nothing -> nothing //│ = [Function: id] f x = case x of {} //│ nothing -> nothing //│ <: f: -//│ 'a -> 'a +//│ nothing -> nothing //│ = [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 //│ = + +def f: string & 'a & ~int & ~string | string & ~int +//│ f: string +//│ = + + +trait T +//│ Defined trait T + +def a: T & 1 | T & 1 & ~'a | 'a +//│ a: T & 1 +//│ = + diff --git a/shared/src/test/diff/mlscript/NestedClassArgs.mls b/shared/src/test/diff/mlscript/NestedClassArgs.mls index f8083f8fd0..5e7d5250a5 100644 --- a/shared/src/test/diff/mlscript/NestedClassArgs.mls +++ b/shared/src/test/diff/mlscript/NestedClassArgs.mls @@ -23,10 +23,15 @@ def c: C['a] as 'a //│ where //│ 'a := C['a] +// * [test:T3] What happens here is we use `factorize` to simplify the union, +// * which uses the fact that `C['a] <: 'a` to reduce `C['a] | 'a` to `'a`... +// * which is obviously NOT valid when that union we're processing is itself the definition of `'a`! +// * So we will need to be more careful about simplifying types, so as not to assume bounds +// * we are in the process of simplifying. This could be done by NOT simplifying things in place. def c: C['a] | 'a as 'a //│ c: 'a //│ where -//│ 'a := 'a | C['a] +//│ 'a := 'a def c: C[C['a]] as 'a //│ c: 'a @@ -67,16 +72,15 @@ mkC = mkC' //│ 'a -> C2['a] rec def rc = mkC(rc) -//│ rc: 'b +//│ rc: C2['a] //│ where -//│ 'b :> C2['a] -//│ 'a :> 'b +//│ 'a :> C2['a] rec def rc = mkC'(rc) //│ rc: 'a //│ where -//│ 'a :> C2['A] with {a: 'a} -//│ 'A :> 'a +//│ 'a :> C2['A] & {a: forall 'a. 'a} +//│ 'A :> forall 'a. 'a @@ -136,17 +140,17 @@ def c: 'a -> C5['a] //│ c: 'a -> C5['a] rec def c5 a = C5{ a = C2 { a = c5 a } } -//│ c5: anything -> 'a +//│ c5: anything -> (C5['A] with {a: 'a}) //│ where -//│ 'a :> C5['A] with {a: C2['A0] with {a: 'a}} -//│ 'A0 :> 'a | C5['A] +//│ 'a :> C2['A0] with {a: C5['A] with {a: 'a}} +//│ 'A0 :> (C5['A] with {a: 'a}) | C5['A] //│ <: C5['A] c = c5 -//│ anything -> 'a +//│ anything -> (C5['A] with {a: 'a}) //│ where -//│ 'a :> C5['A] with {a: C2['A0] with {a: 'a}} -//│ 'A0 :> 'a | C5['A] +//│ 'a :> C2['A0] with {a: C5['A] with {a: 'a}} +//│ 'A0 :> (C5['A] with {a: 'a}) | C5['A] //│ <: C5['A] //│ <: c: //│ 'a -> C5['a] @@ -163,24 +167,24 @@ def c: 'a -> C6['a] //│ c: 'a -> C6['a] rec def c6 a = C6{ a = c5 (c6 a) } -//│ c6: anything -> (C6['A] with {a: forall 'a 'A0 'A1. 'a}) +//│ c6: anything -> (C6['A] with {a: forall 'A0 'a 'A1. C5['A0] with {a: 'a}}) //│ where -//│ 'a :> C5['A0] with {a: C2['A1] with {a: 'a}} -//│ 'A1 :> 'a | C5['A0] +//│ 'a :> C2['A1] with {a: C5['A0] with {a: 'a}} +//│ 'A1 :> (C5['A0] with {a: 'a}) | C5['A0] //│ <: C5['A0] :stats c = c6 -//│ anything -> (C6['A] with {a: forall 'a 'A0 'A1. 'a}) +//│ anything -> (C6['A] with {a: forall 'A0 'a 'A1. C5['A0] with {a: 'a}}) //│ where -//│ 'a :> C5['A0] with {a: C2['A1] with {a: 'a}} -//│ 'A1 :> 'a | C5['A0] +//│ 'a :> C2['A1] with {a: C5['A0] with {a: 'a}} +//│ 'A1 :> (C5['A0] with {a: 'a}) | C5['A0] //│ <: C5['A0] //│ <: c: //│ 'a -> C6['a] //│ constrain calls : 70 -//│ annoying calls : 30 -//│ subtyping calls : 403 +//│ annoying calls : 34 +//│ subtyping calls : 476 // Reproduction of an issue found while trying out TypeRef ctor typing: @@ -251,25 +255,23 @@ s2 = S{v=S{v=1}}:O[O['_]] L{h=error;t=s1} L{h=error;t=s2} //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.251: L{h=error;t=s1} +//│ ║ l.255: L{h=error;t=s1} //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `1` is not an instance of type `L` -//│ ║ l.241: s1 = S{v=1}:O['_] +//│ ║ l.245: s1 = S{v=1}:O['_] //│ ║ ^ //│ ╟── Note: constraint arises from applied type reference: -//│ ║ l.229: class L[T]: { h: T; t: O[L[T]] } +//│ ║ l.233: class L[T]: { h: T; t: O[L[T]] } //│ ╙── ^^^^ -//│ res: error | (L['T] with {h: nothing, t: forall '_. O['_]}) -//│ where -//│ '_ :> 1 +//│ res: error //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.252: L{h=error;t=s2} +//│ ║ l.256: L{h=error;t=s2} //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── type `S['_]` is not an instance of type `L` -//│ ║ l.228: type O[T] = S[T] | N +//│ ║ l.232: type O[T] = S[T] | N //│ ║ ^^^^ //│ ╟── Note: constraint arises from applied type reference: -//│ ║ l.229: class L[T]: { h: T; t: O[L[T]] } +//│ ║ l.233: class L[T]: { h: T; t: O[L[T]] } //│ ╙── ^^^^ //│ res: error @@ -288,7 +290,7 @@ def append ls elem = L { h = elem; t = S { v = ls } } :ns append -//│ res: forall 'a 'h 'T 'v 't 'T0 'b 'c. 'b -> 'c -> 'a +//│ res: forall 'v 'T 'h 'a 'b 'T0 't 'c. 'b -> 'c -> 'a //│ where //│ 'a :> #L & {h: 'h, t: 't, L#T = 'T} //│ 't :> #S & {v: 'v, S#T = 'T0} @@ -329,16 +331,16 @@ append_ty_2 = append //│ <: append_ty_2: //│ (L['T] & 'v & 'T0) -> ('T & 'h) -> (L['T] with {h: 'h, t: S['T0] with {v: 'v}}) //│ ╔══[ERROR] Type mismatch in def definition: -//│ ║ l.327: append_ty_2 = append +//│ ║ l.329: append_ty_2 = append //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'T0` is not an instance of type `L` -//│ ║ l.320: def append_ty_2: (L['T] & 'v & 'T0) -> ('T & 'h) -> (L['T] & {h: 'h; t: S['T0] & {v: 'v}}) +//│ ║ l.322: def append_ty_2: (L['T] & 'v & 'T0) -> ('T & 'h) -> (L['T] & {h: 'h; t: S['T0] & {v: 'v}}) //│ ║ ^^^ //│ ╟── Note: constraint arises from applied type reference: -//│ ║ l.229: class L[T]: { h: T; t: O[L[T]] } +//│ ║ l.233: class L[T]: { h: T; t: O[L[T]] } //│ ║ ^^^^ //│ ╟── Note: class type parameter T is defined at: -//│ ║ l.225: class S[T]: { v: T } +//│ ║ l.229: class S[T]: { v: T } //│ ╙── ^ append_ty = append_ty_2 @@ -352,13 +354,13 @@ append_ty_2 = append_ty //│ <: append_ty_2: //│ (L['T] & 'v & 'T0) -> ('T & 'h) -> (L['T] with {h: 'h, t: S['T0] with {v: 'v}}) //│ ╔══[ERROR] Type mismatch in def definition: -//│ ║ l.350: append_ty_2 = append_ty +//│ ║ l.352: append_ty_2 = append_ty //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'T0` is not an instance of type `L` -//│ ║ l.320: def append_ty_2: (L['T] & 'v & 'T0) -> ('T & 'h) -> (L['T] & {h: 'h; t: S['T0] & {v: 'v}}) +//│ ║ l.322: def append_ty_2: (L['T] & 'v & 'T0) -> ('T & 'h) -> (L['T] & {h: 'h; t: S['T0] & {v: 'v}}) //│ ║ ^^^ //│ ╟── Note: constraint arises from applied type reference: -//│ ║ l.306: def append_ty: (L['T] & 'v) -> ('T & 'h) -> (L['T] & {h: 'h; t: S[L['T]] & {v: 'v}}) +//│ ║ l.308: def append_ty: (L['T] & 'v) -> ('T & 'h) -> (L['T] & {h: 'h; t: S[L['T]] & {v: 'v}}) //│ ╙── ^^^^^ diff --git a/shared/src/test/diff/mlscript/NestedClassArgs_Co.mls b/shared/src/test/diff/mlscript/NestedClassArgs_Co.mls index d7bf10c750..0afb617bd5 100644 --- a/shared/src/test/diff/mlscript/NestedClassArgs_Co.mls +++ b/shared/src/test/diff/mlscript/NestedClassArgs_Co.mls @@ -68,9 +68,9 @@ rec def rc = mkC(rc) //│ 'a :> C2['a] rec def rc = mkC'(rc) -//│ rc: 'A +//│ rc: 'a //│ where -//│ 'A :> C2['A] +//│ 'a :> C2[forall 'a. 'a] @@ -130,14 +130,14 @@ def c: 'a -> C5['a] //│ c: 'a -> C5['a] rec def c5 a = C5{ a = C2 { a = c5 a } } -//│ c5: anything -> 'A +//│ c5: anything -> (C5[nothing] with {a: 'a}) //│ where -//│ 'A :> C5[nothing] with {a: C2['A]} +//│ 'a :> C2[C5[nothing] with {a: 'a}] c = c5 -//│ anything -> 'A +//│ anything -> (C5[nothing] with {a: 'a}) //│ where -//│ 'A :> C5[nothing] with {a: C2['A]} +//│ 'a :> C2[C5[nothing] with {a: 'a}] //│ <: c: //│ 'a -> C5['a] @@ -157,14 +157,14 @@ def c: 'a -> C6['a] // rec def c a = C6{ a = c5 (c a) } rec def c6 a = C6{ a = c5 (c6 a) } -//│ c6: anything -> (C6[nothing] with {a: forall 'A. 'A}) +//│ c6: anything -> (C6[nothing] with {a: forall 'a. C5[nothing] with {a: 'a}}) //│ where -//│ 'A :> C5[nothing] with {a: C2['A]} +//│ 'a :> C2[C5[nothing] with {a: 'a}] c = c6 -//│ anything -> (C6[nothing] with {a: forall 'A. 'A}) +//│ anything -> (C6[nothing] with {a: forall 'a. C5[nothing] with {a: 'a}}) //│ where -//│ 'A :> C5[nothing] with {a: C2['A]} +//│ 'a :> C2[C5[nothing] with {a: 'a}] //│ <: c: //│ 'a -> C6['a] diff --git a/shared/src/test/diff/mlscript/NestedRecursiveMatch.mls b/shared/src/test/diff/mlscript/NestedRecursiveMatch.mls index 9ab3441bb1..432c25b2da 100644 --- a/shared/src/test/diff/mlscript/NestedRecursiveMatch.mls +++ b/shared/src/test/diff/mlscript/NestedRecursiveMatch.mls @@ -34,9 +34,9 @@ rec def f w = case w of Some -> let m = (tmp0).value in Some (m,) -//│ f: 'right -> (None | Some[in 'a out 'a | 0]) +//│ f: (Leaf | Node & 'b) -> (None | Some[in 'a out 'a | 0]) //│ where -//│ 'right <: Leaf | Node & {left: 'right, right: 'right} +//│ 'b <: {left: Leaf | Node & 'b, right: Leaf | Node & 'b} // * Minimizations: @@ -49,9 +49,9 @@ rec def f w = case w of Some -> let m = (tmp0).value in Some (m,) -//│ f: 'b -> Some[in 'a out 'a | 0] +//│ f: 'left -> Some[in 'a out 'a | 0] //│ where -//│ 'b <: Node & {left: 'b} +//│ 'left <: Node & {left: 'left} rec def f w = case w of Node -> @@ -71,7 +71,7 @@ def f w = None -> Some 0, Some -> let m = tmp0.value in Some m -//│ f: {left: 'b} -> Some[in 'a out 0 | 'a] +//│ f: 'b -> Some[in 'a out 0 | 'a] //│ where -//│ 'b <: Node & {left: 'b} +//│ 'b <: {left: Node & 'b} diff --git a/shared/src/test/diff/mlscript/Ops.mls b/shared/src/test/diff/mlscript/Ops.mls index 4bb2d45835..fb99294cfb 100644 --- a/shared/src/test/diff/mlscript/Ops.mls +++ b/shared/src/test/diff/mlscript/Ops.mls @@ -1,26 +1,26 @@ :p 2 + 2 -//│ Parsed: + (2,) (2,); -//│ Desugared: + (2,) (2,) -//│ AST: App(App(Var(+), Tup(_: IntLit(2))), Tup(_: IntLit(2))) +//│ Parsed: +(2,)(2,); +//│ Desugared: +(2,)(2,) +//│ AST: App(App(Var(+),Tup(List((None,Fld(_,IntLit(2)))))),Tup(List((None,Fld(_,IntLit(2)))))) //│ res: int //│ = 4 :p 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))) +//│ Parsed: +(+(1,)(*(2,)(2,),),)(3,); +//│ Desugared: +(+(1,)(*(2,)(2,),),)(3,) +//│ AST: App(App(Var(+),Tup(List((None,Fld(_,App(App(Var(+),Tup(List((None,Fld(_,IntLit(1)))))),Tup(List((None,Fld(_,App(App(Var(*),Tup(List((None,Fld(_,IntLit(2)))))),Tup(List((None,Fld(_,IntLit(2)))))))))))))))),Tup(List((None,Fld(_,IntLit(3)))))) //│ res: int //│ = 8 :e :p 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))) +//│ Parsed: +(+(1,)(/(2,)(2,),),)(3,); +//│ Desugared: +(+(1,)(/(2,)(2,),),)(3,) +//│ AST: App(App(Var(+),Tup(List((None,Fld(_,App(App(Var(+),Tup(List((None,Fld(_,IntLit(1)))))),Tup(List((None,Fld(_,App(App(Var(/),Tup(List((None,Fld(_,IntLit(2)))))),Tup(List((None,Fld(_,IntLit(2)))))))))))))))),Tup(List((None,Fld(_,IntLit(3)))))) //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.20: 1 + 2 / 2 + 3 //│ ║ ^^^^^^^^^ @@ -34,9 +34,9 @@ :ge :p 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))) +//│ Parsed: ||(|>(1,)(2,),)(3,); +//│ Desugared: ||(|>(1,)(2,),)(3,) +//│ AST: App(App(Var(||),Tup(List((None,Fld(_,App(App(Var(|>),Tup(List((None,Fld(_,IntLit(1)))))),Tup(List((None,Fld(_,IntLit(2))))))))))),Tup(List((None,Fld(_,IntLit(3)))))) //│ ╔══[ERROR] identifier not found: |> //│ ║ l.36: 1 |> 2 || 3 //│ ╙── ^^ @@ -52,9 +52,9 @@ :p 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))) +//│ Parsed: ||(||(true,)(&&(false,)(true,),),)(false,); +//│ Desugared: ||(||(true,)(&&(false,)(true,),),)(false,) +//│ AST: App(App(Var(||),Tup(List((None,Fld(_,App(App(Var(||),Tup(List((None,Fld(_,Var(true)))))),Tup(List((None,Fld(_,App(App(Var(&&),Tup(List((None,Fld(_,Var(false)))))),Tup(List((None,Fld(_,Var(true)))))))))))))))),Tup(List((None,Fld(_,Var(false)))))) //│ res: bool //│ = true diff --git a/shared/src/test/diff/mlscript/Paper.mls b/shared/src/test/diff/mlscript/Paper.mls index b8c6cf60fd..d33d5d27d7 100644 --- a/shared/src/test/diff/mlscript/Paper.mls +++ b/shared/src/test/diff/mlscript/Paper.mls @@ -53,7 +53,7 @@ class SomeAnd[A, P]: Some[A] & { payload: P } let arg = if true then SomeAnd{value = 42; payload = 23} else None{} in mapSome (fun x -> x.value + x.payload) arg -//│ res: int | None +//│ res: None | int //│ = 65 @@ -114,9 +114,9 @@ type List[A] = Cons[A] | None rec def mapList f ls = case ls of Cons -> Cons{value = f ls.value; tail = mapList f ls.tail}, None -> None{} -//│ mapList: ('value -> 'value0) -> 'a -> 'tail +//│ mapList: ('value -> 'value0) -> 'a -> (None | 'b) //│ where -//│ 'tail :> (Cons['value0] with {tail: 'tail}) | None +//│ 'b :> Cons['value0] with {tail: None | 'b} //│ 'a <: (Cons[?] with {tail: 'a, value: 'value}) | None //│ = [Function: mapList] @@ -134,10 +134,10 @@ rec def unzip xs = case xs of None -> { fst = None; snd = None }, Some -> let tmp = unzip xs.tail in { fst = Cons xs.value.fst tmp.fst ; snd = Cons xs.value.snd tmp.snd } -//│ unzip: 'a -> {fst: 'tail, snd: 'tail0} +//│ unzip: 'a -> {fst: None | 'b, snd: None | 'c} //│ where -//│ 'tail0 :> (Cons['value] with {tail: 'tail0}) | None -//│ 'tail :> (Cons['value0] with {tail: 'tail}) | None +//│ 'c :> Cons['value] with {tail: None | 'c} +//│ 'b :> Cons['value0] with {tail: None | 'b} //│ 'a <: None | Some[?] & {tail: 'a, value: {fst: 'value0, snd: 'value}} //│ = [Function: unzip] @@ -156,10 +156,10 @@ def unzip_ty = unzip //│ <: Cons_ty: //│ 'a -> (List['a] & 'b) -> (Cons['a] with {tail: 'b}) //│ = [Function: Cons_ty] -//│ 'a -> {fst: 'tail, snd: 'tail0} +//│ 'a -> {fst: None | 'b, snd: None | 'c} //│ where -//│ 'tail0 :> (Cons['value] with {tail: 'tail0}) | None -//│ 'tail :> (Cons['value0] with {tail: 'tail}) | None +//│ 'c :> Cons['value] with {tail: None | 'c} +//│ 'b :> Cons['value0] with {tail: None | 'b} //│ 'a <: None | Some[?] & {tail: 'a, value: {fst: 'value0, snd: 'value}} //│ <: unzip_ty: //│ List[{fst: 'a, snd: 'b}] -> {fst: List['a], snd: List['b]} diff --git a/shared/src/test/diff/mlscript/PolyVariant.mls b/shared/src/test/diff/mlscript/PolyVariant.mls index feadc37b21..af5df93f64 100644 --- a/shared/src/test/diff/mlscript/PolyVariant.mls +++ b/shared/src/test/diff/mlscript/PolyVariant.mls @@ -104,16 +104,16 @@ rec def map f l = case l of { | Nil -> l | Cons -> Cons (f l.head) (map f l.tail) } -//│ map: ('head -> ('head0 & 'A)) -> 'a -> 'tail +//│ map: ('head -> ('head0 & 'A)) -> 'a -> 'b //│ where -//│ 'a <: (Cons[?] with {head: 'head, tail: 'a}) | Nil & List['A] & 'tail -//│ 'tail :> Cons['A] with {head: 'head0, tail: 'tail} +//│ 'a <: (Cons[?] with {head: 'head, tail: 'a}) | Nil & List['A] & 'b +//│ 'b :> Cons['A] with {head: 'head0, tail: 'b} //│ = [Function: map] map (fun x -> "lol") (Cons 2 (Cons 3 Nil)) -//│ res: 'tail +//│ res: Nil | 'a //│ where -//│ 'tail :> (Cons["lol"] with {tail: 'tail}) | Nil +//│ 'a :> Cons["lol"] with {tail: Nil | 'a} //│ = Cons { head: 'lol', tail: Cons { head: 'lol', tail: Nil {} } } //************** EXAMPLE 3: ROUGH APPROXIMATIONS ************* diff --git a/shared/src/test/diff/mlscript/PolyVariantCodeReuse.mls b/shared/src/test/diff/mlscript/PolyVariantCodeReuse.mls index bf62223b99..851a7317bb 100644 --- a/shared/src/test/diff/mlscript/PolyVariantCodeReuse.mls +++ b/shared/src/test/diff/mlscript/PolyVariantCodeReuse.mls @@ -53,13 +53,13 @@ def eq: string -> string -> bool rec def list_assoc s l = case l of { | Cons -> - if eq l.head._1 s then Success l.head._2 + if eq l.head.0 s then Success l.head.1 else list_assoc s l.tail | Nil -> NotFound } //│ list_assoc: string -> 'a -> (NotFound | (Success with {result: 'result})) //│ where -//│ 'a <: (Cons[?] with {head: {_1: string, _2: 'result}, tail: 'a}) | Nil +//│ 'a <: (Cons[?] with {head: {0: string, 1: 'result}, tail: 'a}) | Nil //│ = //│ eq is not implemented @@ -84,9 +84,9 @@ def eval_var sub v = case v of { | Success -> res.result } } -//│ eval_var: 'a -> (Var & 'result) -> 'result +//│ eval_var: (Cons[?] & 'a | Nil) -> (Var & 'result) -> 'result //│ where -//│ 'a <: (Cons[?] with {head: {_1: string, _2: 'result}, tail: 'a}) | Nil +//│ 'a <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'a | Nil} //│ = //│ list_assoc and eq are not implemented @@ -125,30 +125,35 @@ def eval_lambda eval_rec subst v = case v of { | Abs -> eval_rec (Cons (Tuple l1.name l2) Nil) l1.body | _ -> App { lhs = l1; rhs = l2 } } - | Abs -> let new_name = int_to_string ((gensym ())._2.a) in + | Abs -> let new_name = int_to_string ((gensym ()).1.a) in Abs { name = new_name; body = eval_rec (Cons (Tuple v.name (Var { name = new_name })) subst) v.body } } -//│ eval_lambda: (((Cons[('a, Var | 'body,) | 'A] with {head: ('a, Var | 'body,), tail: Nil | 'tail}) | 'tail) -> 'lhs -> ('body & 'result & ((Abs[?] with {body: 'lhs, name: 'a}) | 'lhs0 & ~#Abs))) -> (List['A] & 'b & 'tail) -> ((Abs[?] with {body: 'lhs, name: 'a}) | App[?] & {lhs: 'lhs, rhs: 'lhs} | Var & 'result) -> (Abs['body] | (App['lhs0 | 'body] with {lhs: 'lhs0, rhs: 'body}) | 'result) +//│ eval_lambda: (((Cons[('a, Var | 'body,) | 'A] with {head: ('a, Var | 'body,), tail: Nil | 'tail}) | 'tail) -> 'lhs -> ('body & 'result & ((Abs[?] with {body: 'lhs, name: 'a}) | 'lhs0 & ~#Abs))) -> (List['A] & (Cons[?] & 'b | Nil) & 'tail) -> ((Abs[?] with {body: 'lhs, name: 'a}) | App[?] & {lhs: 'lhs, rhs: 'lhs} | Var & 'result) -> (Abs['body] | (App['lhs0 | 'body] with {lhs: 'lhs0, rhs: 'body}) | 'result) //│ where -//│ 'b <: (Cons[?] with {head: {_1: string, _2: 'result}, tail: 'b}) | Nil +//│ 'b <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'b | Nil} //│ = //│ eval_var, list_assoc and eq are not implemented rec def eval1 subst = eval_lambda eval1 subst -//│ eval1: (List[?] & 'tail) -> 'b -> 'rhs +//│ eval1: ('tail & (Cons[?] & List[?] & 'b | Nil & List[?])) -> 'c -> 'd //│ where -//│ 'tail <: (Cons[?] with {head: {_1: string, _2: 'result}, tail: 'tail}) | Nil -//│ 'result :> 'rhs | Var -//│ <: 'b & (Abs[?] & 'c | 'lhs & ~#Abs) -//│ 'rhs :> 'result | 'd | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | Abs['rhs] -//│ 'd :> Var -//│ <: 'b & (Abs[?] & 'c | {name: string} & 'lhs & ~#Abs) -//│ 'lhs :> (App['a] with {lhs: 'lhs, rhs: 'rhs}) | Var -//│ <: 'a & 'b -//│ 'a :> (App['a] with {lhs: 'lhs, rhs: 'rhs}) | 'rhs | Var -//│ 'c <: {body: 'b, name: string} -//│ 'b <: Abs[?] & {body: 'b} | App[?] & {lhs: 'b, rhs: 'b} | Var & 'd +//│ 'tail <: Cons[?] & 'b | Nil +//│ 'b <: {head: {0: string, 1: 'result}, tail: 'tail} +//│ 'result :> 'd +//│ <: Abs[?] & 'e & 'f | 'lhs & (Abs[?] & 'f & ~#Abs | App[?] & 'g | Var & 'h) +//│ 'd :> 'result | 'i | App['a]\lhs\rhs & {lhs: 'lhs, rhs: 'rhs} | Abs['d] +//│ 'i :> Var +//│ <: Abs[?] & 'e & 'f | 'lhs & (Abs[?] & 'f & ~#Abs | App[?] & {name: string} & 'g | Var & 'h) +//│ 'lhs :> App['a]\lhs\rhs & {lhs: 'lhs, rhs: 'rhs} | Var +//│ <: 'a & 'c +//│ 'a :> App['a]\lhs\rhs & {lhs: 'lhs, rhs: 'rhs} | 'd +//│ 'rhs :> 'd +//│ 'e <: {body: 'c, name: string} +//│ 'c <: Abs[?] & 'f | App[?] & 'g | Var & 'h +//│ 'h <: Var & 'i +//│ 'g <: {lhs: 'c, rhs: 'c} +//│ 'f <: {body: 'c, name: string} //│ = //│ eval_lambda, eval_var, list_assoc and eq are not implemented @@ -166,20 +171,20 @@ rec def eval1 subst = eval_lambda eval1 subst // ************************* Expr ****************************** -class Num: Expr & { num: int } +class Numb: Expr & { num: int } class Add[a]: Expr & { lhs: a; rhs: a } class Mul[a]: Expr & { lhs: a; rhs: a } -//│ Defined class Num +//│ Defined class Numb //│ Defined class Add[+a] //│ Defined class Mul[+a] def map_expr f v = case v of { | Var -> v - | Num -> v + | Numb -> v | Add -> Add { lhs = f v.lhs; rhs = f v.rhs } | Mul -> Mul { lhs = f v.lhs; rhs = f v.rhs } } -//│ map_expr: ('lhs -> 'lhs0) -> (Add[?] & {lhs: 'lhs, rhs: 'lhs} | Mul[?] & {lhs: 'lhs, rhs: 'lhs} | 'a & (Num | Var)) -> (Add['lhs0] | Mul['lhs0] | 'a) +//│ map_expr: ('lhs -> 'lhs0) -> (Add[?] & {lhs: 'lhs, rhs: 'lhs} | Mul[?] & {lhs: 'lhs, rhs: 'lhs} | 'a & (Numb | Var)) -> (Add['lhs0] | Mul['lhs0] | 'a) //│ = [Function: map_expr] rec def eval_expr eval_rec subst v = @@ -189,8 +194,8 @@ rec def eval_expr eval_rec subst v = | Add -> let vv1 = vv.lhs in let vv2 = vv.rhs in case vv1 of { - | Num -> case vv2 of { - | Num -> Num { num = vv1.num + vv2.num } + | Numb -> case vv2 of { + | Numb -> Numb { num = vv1.num + vv2.num } | _ -> vv } | _ -> vv @@ -198,26 +203,26 @@ rec def eval_expr eval_rec subst v = | Mul -> let vv1 = vv.lhs in let vv2 = vv.rhs in case vv1 of { - | Num -> case vv2 of { - | Num -> Num { num = vv1.num * vv2.num } + | Numb -> case vv2 of { + | Numb -> Numb { num = vv1.num * vv2.num } | _ -> vv } | _ -> vv } - | Num -> vv // _ -> vv + | Numb -> vv // _ -> vv } -//│ eval_expr: ('a -> 'rhs -> 'rhs0) -> ('a & 'b) -> (Add[?] & {lhs: 'rhs, rhs: 'rhs} | Mul[?] & {lhs: 'rhs, rhs: 'rhs} | Num & 'result | Var & 'result) -> (Add['rhs0] | Mul['rhs0] | Num | 'result) +//│ eval_expr: ('a -> 'rhs -> 'rhs0) -> ('a & (Cons[?] & 'b | Nil)) -> (Add[?] & {lhs: 'rhs, rhs: 'rhs} | Mul[?] & {lhs: 'rhs, rhs: 'rhs} | Numb & 'result | Var & 'result) -> (Add['rhs0] | Mul['rhs0] | Numb | 'result) //│ where -//│ 'b <: (Cons[?] with {head: {_1: string, _2: 'result}, tail: 'b}) | Nil +//│ 'b <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'b | Nil} //│ = //│ eval_var, list_assoc and eq are not implemented rec def eval2 subst = eval_expr eval2 subst -//│ eval2: 'a -> 'b -> 'result +//│ eval2: (Cons[?] & 'a | Nil) -> 'b -> (Numb | 'result) //│ where -//│ 'b <: Add[?] & {lhs: 'b, rhs: 'b} | Mul[?] & {lhs: 'b, rhs: 'b} | Num & 'result | Var & 'result -//│ 'a <: (Cons[?] with {head: {_1: string, _2: 'result & (Num | ~#Num)}, tail: 'a}) | Nil -//│ 'result :> Num | Add['result] | Mul['result] +//│ 'b <: Add[?] & {lhs: 'b, rhs: 'b} | Mul[?] & {lhs: 'b, rhs: 'b} | Numb & 'result | Var & 'result +//│ 'a <: {head: {0: string, 1: 'result & (Numb | ~#Numb)}, tail: Cons[?] & 'a | Nil} +//│ 'result :> Mul[Numb | 'result] | Add[Numb | 'result] //│ = //│ eval_expr, eval_var, list_assoc and eq are not implemented @@ -225,26 +230,26 @@ rec def eval2 subst = eval_expr eval2 subst // ------------- OCaml's type ------------- // val map_expr : // ('a -> 'b) -> -// [< `Add of 'a * 'a | `Mult of 'a * 'a | `Num of 'c | `Var of string ] -> -// [> `Add of 'b * 'b | `Mult of 'b * 'b | `Num of 'c | `Var of string ] = +// [< `Add of 'a * 'a | `Mult of 'a * 'a | `Numb of 'c | `Var of string ] -> +// [> `Add of 'b * 'b | `Mult of 'b * 'b | `Numb of 'c | `Var of string ] = // val eval_expr : // ((string * -// ([> `Add of ([> `Num of int ] as 'b) * 'b +// ([> `Add of ([> `Numb of int ] as 'b) * 'b // | `Mult of 'b * 'b -// | `Num of int +// | `Numb of int // | `Var of string ] // as 'a)) // list -> 'c -> 'b) -> // (string * 'a) list -> -// [< `Add of 'c * 'c | `Mult of 'c * 'c | `Num of int | `Var of string ] -> 'a = +// [< `Add of 'c * 'c | `Mult of 'c * 'c | `Numb of int | `Var of string ] -> 'a = // // val eval2 : // (string * -// ([> `Add of 'a * 'a | `Mult of 'a * 'a | `Num of int | `Var of string ] +// ([> `Add of 'a * 'a | `Mult of 'a * 'a | `Numb of int | `Var of string ] // as 'a)) // list -> -// ([< `Add of 'b * 'b | `Mult of 'b * 'b | `Num of int | `Var of string ] as 'b) -> +// ([< `Add of 'b * 'b | `Mult of 'b * 'b | `Numb of int | `Var of string ] as 'b) -> // 'a = @@ -254,30 +259,34 @@ def eval_lexpr eval_rec subst v = case v of { | Lambda -> eval_lambda eval_rec subst v | Expr -> eval_expr eval_rec subst v } -//│ eval_lexpr: ((Cons[('a, Var | 'body,) | 'A]\head\tail & {head: ('a, Var | 'body,), tail: Nil | 'tail} | 'tail) -> 'lhs -> ('result & 'body & (Abs[?]\body\name & {body: 'lhs, name: 'a} | 'lhs0 & ~#Abs))) -> (List['A] & 'b & 'c & 'tail) -> (Abs[?]\body\name & {body: 'lhs, name: 'a} | Add[?] & {lhs: 'lhs, rhs: 'lhs} | App[?] & {lhs: 'lhs, rhs: 'lhs} | Mul[?] & {lhs: 'lhs, rhs: 'lhs} | Num & 'result | Var & 'result) -> (Abs['body] | Add['body] | App['lhs0 | 'body]\lhs\rhs & {lhs: 'lhs0, rhs: 'body} | Mul['body] | Num | 'result) +//│ eval_lexpr: ((Cons[('a, Var | 'body,) | 'A]\head\tail & {head: ('a, Var | 'body,), tail: Nil | 'tail} | 'tail) -> 'lhs -> ('body & 'result & (Abs[?]\body\name & {body: 'lhs, name: 'a} | 'lhs0 & ~#Abs))) -> (List['A] & (Cons[?] & 'b | Nil) & (Cons[?] & 'c | Nil) & 'tail) -> (Abs[?]\body\name & {body: 'lhs, name: 'a} | Add[?] & {lhs: 'lhs, rhs: 'lhs} | App[?] & {lhs: 'lhs, rhs: 'lhs} | Mul[?] & {lhs: 'lhs, rhs: 'lhs} | Numb & 'result | Var & 'result) -> (Abs['body] | Add['body] | App['lhs0 | 'body]\lhs\rhs & {lhs: 'lhs0, rhs: 'body} | Mul['body] | Numb | 'result) //│ where -//│ 'c <: Cons[?]\head\tail & {head: {_1: string, _2: 'result}, tail: 'c} | Nil -//│ 'b <: Cons[?]\head\tail & {head: {_1: string, _2: 'result}, tail: 'b} | Nil +//│ 'c <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'c | Nil} +//│ 'b <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'b | Nil} //│ = //│ eval_lambda, eval_var, list_assoc and eq are not implemented rec def eval3 subst = eval_lexpr eval3 subst -//│ eval3: (List[?] & 'tail & 'tail0) -> 'b -> 'rhs +//│ eval3: ('tail & 'tail0 & (Cons[?] & List[?] & 'b & 'c | Nil & List[?])) -> 'd -> 'e //│ where -//│ 'tail0 <: Cons[?]\head\tail & {head: {_1: string, _2: 'result}, tail: 'tail0} | Nil -//│ 'tail <: Cons[?]\head\tail & {head: {_1: string, _2: 'result}, tail: 'tail} | Nil -//│ 'result :> 'rhs | Var | Num -//│ <: 'b & (Abs[?] & 'c | 'lhs & (Num | ~#Abs & ~#Num)) -//│ 'rhs :> Abs['rhs] | App['a]\lhs\rhs & {lhs: 'lhs, rhs: 'rhs} | 'result | 'd | 'e -//│ 'a :> Add['rhs]\lhs\rhs & {lhs: 'rhs, rhs: 'rhs} | App['a]\lhs\rhs & {lhs: 'lhs, rhs: 'rhs} | Mul['rhs]\lhs\rhs & {lhs: 'rhs, rhs: 'rhs} | 'rhs | Var -//│ 'lhs :> Add['rhs]\lhs\rhs & {lhs: 'rhs, rhs: 'rhs} | App['a]\lhs\rhs & {lhs: 'lhs, rhs: 'rhs} | Mul['rhs]\lhs\rhs & {lhs: 'rhs, rhs: 'rhs} | Num | Var -//│ <: 'b & 'a -//│ 'b <: Abs[?] & {body: 'b} | Add[?] & {lhs: 'b, rhs: 'b} | App[?] & {lhs: 'b, rhs: 'b} | Mul[?] & {lhs: 'b, rhs: 'b} | Num & (Add[?] & 'e | Mul[?] & 'e | Num & 'result | Var & 'd) | Var & 'd -//│ 'd :> Var -//│ <: 'b & (Abs[?] & 'c | 'lhs & (Num & {name: string} | {name: string} & ~#Abs & ~#Num)) -//│ 'e :> Mul['rhs]\lhs\rhs & {lhs: 'rhs, rhs: 'rhs} | Add['rhs]\lhs\rhs & {lhs: 'rhs, rhs: 'rhs} -//│ <: 'b & (Abs[?] & {lhs: anything, rhs: anything} & 'c | 'lhs & (Num & {lhs: anything, rhs: anything} | {lhs: anything, rhs: anything} & ~#Abs & ~#Num)) -//│ 'c <: {body: 'b, name: string} +//│ 'tail0 <: Cons[?] & 'c | Nil +//│ 'c <: {head: {0: string, 1: 'result}, tail: 'tail0} +//│ 'tail <: Cons[?] & 'b | Nil +//│ 'b <: {head: {0: string, 1: 'result}, tail: 'tail} +//│ 'result :> Var | 'e | Numb +//│ <: Abs[?] & 'f & 'g | 'lhs & (Lambda & 'f & ~#Abs | 'h & (Expr & ~#Numb | Numb)) +//│ 'e :> Abs['e] | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | 'result | 'i | 'j | (Add['e] with {lhs: 'e, rhs: 'e}) | (Mul['e] with {lhs: 'e, rhs: 'e}) +//│ 'a :> (Add['e] with {lhs: 'e, rhs: 'e}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['e] with {lhs: 'e, rhs: 'e}) | Var | 'e +//│ 'lhs :> (Add['e] with {lhs: 'e, rhs: 'e}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['e] with {lhs: 'e, rhs: 'e}) | Numb | Var +//│ <: 'd & 'a +//│ 'd <: Expr & 'h | Lambda & 'f +//│ 'h <: Add[?] & {lhs: 'd, rhs: 'd} | Mul[?] & {lhs: 'd, rhs: 'd} | Numb & 'result | Var & 'j +//│ 'j <: Var & (Abs[?] & 'f & 'g | 'lhs & (Lambda & {name: string} & 'f & ~#Abs | 'h & (Expr & {name: string} & ~#Numb | Numb & {name: string}))) +//│ 'f <: Abs[?] & {body: 'd} | App[?] & {lhs: 'd, rhs: 'd} | Var & 'i +//│ 'i :> Var +//│ <: Abs[?] & 'f & 'g | 'lhs & (Lambda & {name: string} & 'f & ~#Abs | 'h & (Expr & {name: string} & ~#Numb | Numb & {name: string})) +//│ 'g <: {body: 'd, name: string} +//│ 'rhs :> 'e //│ = //│ eval_lexpr, eval_lambda, eval_var, list_assoc and eq are not implemented @@ -288,7 +297,7 @@ rec def eval3 subst = eval_lexpr eval3 subst // | `Add of 'a * 'a // | `App of 'a * 'a // | `Mult of 'a * 'a -// | `Num of int +// | `Numb of int // | `Var of string ] // as 'a)) // list -> 'a -> 'a) -> @@ -297,7 +306,7 @@ rec def eval3 subst = eval_lexpr eval3 subst // | `Add of 'a * 'a // | `App of 'a * 'a // | `Mult of 'a * 'a -// | `Num of int +// | `Numb of int // | `Var of string ] -> // 'a = // @@ -307,19 +316,19 @@ rec def eval3 subst = eval_lexpr eval3 subst // | `Add of 'a * 'a // | `App of 'a * 'a // | `Mult of 'a * 'a -// | `Num of int +// | `Numb of int // | `Var of string ] as 'a)) // list -> 'a -> 'a = // ************************** Tests ******************************* eval3 Nil (Var { name = "s" }) -//│ res: 'result +//│ res: 'b //│ where -//│ 'result :> Var | Abs['result] | (App['a] with {lhs: 'lhs, rhs: 'result}) | Num | 'b -//│ 'a :> (App['a] with {lhs: 'lhs, rhs: 'result}) | Num | 'result | Var | 'b -//│ 'lhs :> (App['a] with {lhs: 'lhs, rhs: 'result}) | Num | Var | 'b -//│ 'b :> Add['result] | Mul['result] +//│ 'b :> Abs['b] | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | Var | Numb | (Add['b] with {lhs: 'b, rhs: 'b}) | (Mul['b] with {lhs: 'b, rhs: 'b}) +//│ 'a :> 'lhs | 'b +//│ 'lhs :> (Add['b] with {lhs: 'b, rhs: 'b}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['b] with {lhs: 'b, rhs: 'b}) | Numb | Var +//│ 'rhs :> 'b //│ = //│ eval3, eval_lexpr, eval_lambda, eval_var, list_assoc and eq are not implemented // ------------- OCaml's type ------------- @@ -327,55 +336,55 @@ eval3 Nil (Var { name = "s" }) // | `Add of 'a * 'a // | `App of 'a * 'a // | `Mult of 'a * 'a -// | `Num of int +// | `Numb of int // | `Var of string ] as 'a // = `Var "s" eval3 Nil (Abs { name = "s"; body = Var { name = "s" } }) -//│ res: 'result +//│ res: 'b //│ where -//│ 'result :> Var | Abs['result] | (App['a] with {lhs: 'lhs, rhs: 'result}) | Num | 'b -//│ 'a :> (App['a] with {lhs: 'lhs, rhs: 'result}) | Num | 'result | Var | 'b -//│ 'lhs :> (App['a] with {lhs: 'lhs, rhs: 'result}) | Num | Var | 'b -//│ 'b :> Add['result] | Mul['result] +//│ 'b :> Abs['b] | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | Var | (Mul['b] with {lhs: 'b, rhs: 'b}) | Numb | (Add['b] with {lhs: 'b, rhs: 'b}) +//│ 'a :> 'lhs | 'b +//│ 'lhs :> (Add['b] with {lhs: 'b, rhs: 'b}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['b] with {lhs: 'b, rhs: 'b}) | Numb | Var +//│ 'rhs :> 'b //│ = //│ eval3, eval_lexpr, eval_lambda, eval_var, list_assoc and eq are not implemented -eval2 Nil (Num { num = 1 }) -//│ res: 'rhs +eval2 Nil (Numb { num = 1 }) +//│ res: Numb | 'a //│ where -//│ 'rhs :> Num | Add['rhs] | Mul['rhs] +//│ 'a :> Add[Numb | 'a] | Mul[Numb | 'a] //│ = //│ eval2, eval_expr, eval_var, list_assoc and eq are not implemented -eval3 Nil (Num { num = 1 }) -//│ res: 'result +eval3 Nil (Numb { num = 1 }) +//│ res: 'b //│ where -//│ 'result :> Var | Abs['result] | (App['a] with {lhs: 'lhs, rhs: 'result}) | Num | 'b -//│ 'a :> (App['a] with {lhs: 'lhs, rhs: 'result}) | Num | 'result | Var | 'b -//│ 'lhs :> (App['a] with {lhs: 'lhs, rhs: 'result}) | Num | Var | 'b -//│ 'b :> Add['result] | Mul['result] +//│ 'b :> Abs['b] | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | Var | (Mul['b] with {lhs: 'b, rhs: 'b}) | Numb | (Add['b] with {lhs: 'b, rhs: 'b}) +//│ 'a :> 'lhs | 'b +//│ 'lhs :> (Add['b] with {lhs: 'b, rhs: 'b}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['b] with {lhs: 'b, rhs: 'b}) | Numb | Var +//│ 'rhs :> 'b //│ = //│ eval3, eval_lexpr, eval_lambda, eval_var, list_assoc and eq are not implemented -eval3 Nil (App { lhs = Num {num = 0}; rhs = Num {num = 0}}) -//│ res: 'result +eval3 Nil (App { lhs = Numb {num = 0}; rhs = Numb {num = 0}}) +//│ res: 'b //│ where -//│ 'result :> Var | Abs['result] | (App['a] with {lhs: 'lhs, rhs: 'result}) | Num | 'b -//│ 'a :> (App['a] with {lhs: 'lhs, rhs: 'result}) | Num | 'result | Var | 'b -//│ 'lhs :> (App['a] with {lhs: 'lhs, rhs: 'result}) | Num | Var | 'b -//│ 'b :> Add['result] | Mul['result] +//│ 'b :> Abs['b] | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | Var | Numb | (Add['b] with {lhs: 'b, rhs: 'b}) | (Mul['b] with {lhs: 'b, rhs: 'b}) +//│ 'a :> 'lhs | 'b +//│ 'lhs :> (Add['b] with {lhs: 'b, rhs: 'b}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['b] with {lhs: 'b, rhs: 'b}) | Numb | Var +//│ 'rhs :> 'b //│ = //│ eval3, eval_lexpr, eval_lambda, eval_var, list_assoc and eq are not implemented -eval3 Nil (Abs { name = "s"; body = Add { lhs = Var { name = "s" }; rhs = Num { num = 1 } } }) -//│ res: 'result +eval3 Nil (Abs { name = "s"; body = Add { lhs = Var { name = "s" }; rhs = Numb { num = 1 } } }) +//│ res: 'b //│ where -//│ 'result :> Var | Abs['result] | (App['a] with {lhs: 'lhs, rhs: 'result}) | Num | 'b -//│ 'a :> (App['a] with {lhs: 'lhs, rhs: 'result}) | Num | 'result | Var | 'b -//│ 'lhs :> (App['a] with {lhs: 'lhs, rhs: 'result}) | Num | Var | 'b -//│ 'b :> Add['result] | Mul['result] +//│ 'b :> Abs['b] | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | Var | (Mul['b] with {lhs: 'b, rhs: 'b}) | Numb | (Add['b] with {lhs: 'b, rhs: 'b}) +//│ 'a :> 'lhs | 'b +//│ 'lhs :> (Add['b] with {lhs: 'b, rhs: 'b}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['b] with {lhs: 'b, rhs: 'b}) | Numb | Var +//│ 'rhs :> 'b //│ = //│ eval3, eval_lexpr, eval_lambda, eval_var, list_assoc and eq are not implemented @@ -390,86 +399,135 @@ def eval_lexpr' eval_rec subst v = case v of { | Var -> eval_var subst v | Abs -> eval_lambda eval_rec subst v | App -> eval_lambda eval_rec subst v - | Num -> eval_expr eval_rec subst v + | Numb -> eval_expr eval_rec subst v | Add -> eval_expr eval_rec subst v | Mul -> eval_expr eval_rec subst v } -//│ eval_lexpr': ((Cons[('b, Var | 'body,) | 'A]\head\tail & {head: ('b, Var | 'body,), tail: Nil | 'tail} | 'tail) -> 'body0 -> ('body & 'result & (Abs[?]\body\name & {body: 'body0, name: 'b} | 'lhs & 'a & (Abs[?]\body\name & {body: 'body0, name: 'b} & ~#Abs | 'lhs & 'a & ~#Abs)))) -> (List['A] & 'c & 'd & 'e & 'f & 'g & 'tail & 'h) -> (Abs[?]\body\name & {body: 'body0, name: 'b} | Add[?] & {lhs: 'body0, rhs: 'body0} | App[?] & {lhs: 'body0, rhs: 'body0} | Mul[?] & {lhs: 'body0, rhs: 'body0} | Num & 'result | Var & 'result) -> (Abs['body] | Add['body] | App['a | 'body]\lhs\rhs & {lhs: 'lhs, rhs: 'body} | Mul['body] | Num | 'result) +//│ eval_lexpr': ((Cons[('b, Var | 'body,) | 'A]\head\tail & {head: ('b, Var | 'body,), tail: Nil | 'tail} | 'tail) -> 'body0 -> ('body & 'result & (Abs[?]\body\name & {body: 'body0, name: 'b} | 'lhs & 'a & (Abs[?]\body\name & {body: 'body0, name: 'b} & ~#Abs | 'lhs & 'a & ~#Abs)))) -> (List['A] & (Cons[?] & 'c | Nil) & (Cons[?] & 'd | Nil) & (Cons[?] & 'e | Nil) & (Cons[?] & 'f | Nil) & (Cons[?] & 'g | Nil) & 'tail & (Cons[?] & 'h | Nil)) -> (Abs[?]\body\name & {body: 'body0, name: 'b} | Add[?] & {lhs: 'body0, rhs: 'body0} | App[?] & {lhs: 'body0, rhs: 'body0} | Mul[?] & {lhs: 'body0, rhs: 'body0} | Numb & 'result | Var & 'result) -> (Abs['body] | Add['body] | App['a | 'body]\lhs\rhs & {lhs: 'lhs, rhs: 'body} | Mul['body] | Numb | 'result) //│ where -//│ 'h <: Cons[?]\head\tail & {head: {_1: string, _2: 'result}, tail: 'h} | Nil -//│ 'g <: Cons[?]\head\tail & {head: {_1: string, _2: 'result}, tail: 'g} | Nil -//│ 'f <: Cons[?]\head\tail & {head: {_1: string, _2: 'result}, tail: 'f} | Nil -//│ 'e <: Cons[?]\head\tail & {head: {_1: string, _2: 'result}, tail: 'e} | Nil -//│ 'd <: Cons[?]\head\tail & {head: {_1: string, _2: 'result}, tail: 'd} | Nil -//│ 'c <: Cons[?]\head\tail & {head: {_1: string, _2: 'result}, tail: 'c} | Nil +//│ 'h <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'h | Nil} +//│ 'g <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'g | Nil} +//│ 'f <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'f | Nil} +//│ 'e <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'e | Nil} +//│ 'd <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'd | Nil} +//│ 'c <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'c | Nil} //│ = //│ eval_var, list_assoc and eq are not implemented :e rec def eval4 subst = eval_lexpr' eval4 subst //│ ╔══[ERROR] Subtyping constraint of the form `?a -> ?b <: ?eval4` took too many steps and ran out of fuel (10000) -//│ ║ l.409: rec def eval4 subst = eval_lexpr' eval4 subst +//│ ║ l.418: rec def eval4 subst = eval_lexpr' eval4 subst //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. -//│ eval4: (List[?] & 'tail & 'tail0 & 'tail1 & 'tail2 & 'tail3 & 'tail4) -> 'a -> 'result +//│ eval4: ('tail & 'tail0 & 'tail1 & 'tail2 & 'tail3 & 'tail4 & (Cons[?] & List[?] & 'a & 'b & 'c & 'd & 'e & 'f | Nil & List[?])) -> 'g -> (App[nothing] | 'result | 'h) //│ where -//│ 'tail4 <: Cons[?]\head\tail & {head: {_1: string, _2: 'result0}, tail: 'tail4} | Nil -//│ 'tail3 <: Cons[?]\head\tail & {head: {_1: string, _2: 'result0}, tail: 'tail3} | Nil -//│ 'tail2 <: Cons[?]\head\tail & {head: {_1: string, _2: 'result & (Num | ~#Num)}, tail: 'tail2} | Nil -//│ 'tail1 <: Cons[?]\head\tail & {head: {_1: string, _2: 'result & 'a & (Abs[?] & 'b | 'lhs & (Num | ~#Abs & ~#Num))}, tail: 'tail1} | Nil -//│ 'tail0 <: Cons[?]\head\tail & {head: {_1: string, _2: 'result & 'a & (Abs[?] & 'b | 'lhs & (Num | ~#Abs & ~#Num))}, tail: 'tail0} | Nil -//│ 'tail <: Cons[?]\head\tail & {head: {_1: string, _2: 'result & 'a & (Abs[?] & 'b | 'lhs & (Num | ~#Abs & ~#Num))}, tail: 'tail} | Nil -//│ 'result :> Mul['result] | Add['result] | Abs['result] | App['lhs] & {lhs: 'lhs, rhs: nothing} | 'result0 | 'c | 'd | 'e -//│ 'result0 :> Var | Num -//│ <: 'a & (Num | ~#Num) -//│ 'a <: Abs[?] | Add[?] & {lhs: 'a, rhs: 'a} | App[?] & {rhs: 'a} | Mul[?] & {lhs: 'a, rhs: 'a} | Num & (Add[?] & 'e | Mul[?] & 'e | Num & 'result0 | Var & 'd) | Var & 'c -//│ 'c :> Var -//│ <: 'a & (Abs[?] & 'b | 'lhs & (Num & {name: string} | {name: string} & ~#Abs & ~#Num)) -//│ 'b <: {body: 'a, name: string} -//│ 'd <: 'a & (Num & {name: string} | {name: string} & ~#Num) -//│ 'e :> 'result -//│ <: 'a & (Num & {lhs: anything, rhs: anything} | {lhs: anything, rhs: anything} & ~#Num) +//│ 'tail4 <: Cons[?] & 'f | Nil +//│ 'f <: {head: {0: string, 1: 'result0}, tail: 'tail4} +//│ 'tail3 <: Cons[?] & 'e | Nil +//│ 'e <: {head: {0: string, 1: 'result0}, tail: 'tail3} +//│ 'tail2 <: Cons[?] & 'd | Nil +//│ 'd <: {head: {0: string, 1: 'result & (Numb | ~#Numb)}, tail: 'tail2} +//│ 'tail1 <: Cons[?] & 'c | Nil +//│ 'c <: { +//│ head: { +//│ 0: string, +//│ 1: 'result & (Abs[?] & 'i & 'j | 'lhs & (Abs[?] & 'j & ~#Abs | Add[?] & 'k | App[?] & 'l | Mul[?] & 'm | Var & 'n | 'o & (Numb | Numb & ~#Numb))) +//│ }, +//│ tail: 'tail1 +//│ } +//│ 'tail0 <: Cons[?] & 'b | Nil +//│ 'b <: { +//│ head: { +//│ 0: string, +//│ 1: 'result & (Abs[?] & 'i & 'j | 'lhs & (Abs[?] & 'j & ~#Abs | Add[?] & 'k | App[?] & 'l | Mul[?] & 'm | Var & 'n | 'o & (Numb | Numb & ~#Numb))) +//│ }, +//│ tail: 'tail0 +//│ } +//│ 'tail <: Cons[?] & 'a | Nil +//│ 'a <: { +//│ head: { +//│ 0: string, +//│ 1: 'result & (Abs[?] & 'i & 'j | 'lhs & (Abs[?] & 'j & ~#Abs | Add[?] & 'k | App[?] & 'l | Mul[?] & 'm | Var & 'n | 'o & (Numb | Numb & ~#Numb))) +//│ }, +//│ tail: 'tail +//│ } +//│ 'result :> Abs[Abs[nothing] | App[nothing] | 'result | 'h] | App['lhs] & {lhs: 'lhs, rhs: nothing} | 'h | Numb | Var | Add[Abs[nothing] | App[nothing] | 'result | 'h] | Mul[Abs[nothing] | App[nothing] | 'result | 'h] | 'result0 | 'p +//│ 'h :> Var +//│ <: Abs[?] & 'i & 'j | 'lhs & (Abs[?] & 'j & ~#Abs | Add[?] & {name: string} & 'k | App[?] & {name: string} & 'l | Mul[?] & {name: string} & 'm | Var & 'n | 'o & (Numb & {name: string} | Numb & {name: string} & ~#Numb)) //│ 'lhs :> App['lhs] & {lhs: 'lhs, rhs: nothing} | Var +//│ 'i <: {body: 'g, name: string} +//│ 'g <: Abs[?] & 'j | Add[?] & 'k | App[?] & 'l | Mul[?] & 'm | Numb & 'o | Var & 'n +//│ 'k <: Add[?] & {lhs: 'g, rhs: 'g} | Mul[?] & {lhs: 'g, rhs: 'g} | Numb & 'result | Var & 'p +//│ 'p <: Var & (Abs[?] & 'j | Add[?] & {name: string} & 'k | App[?] & {name: string} & 'l | Mul[?] & {name: string} & 'm | Var & 'n | 'o & (Numb & {name: string} | Numb & {name: string} & ~#Numb)) +//│ 'o <: Add[?] & {lhs: 'g, rhs: 'g} | Mul[?] & {lhs: 'g, rhs: 'g} | Numb & 'result0 | Var & 'p +//│ 'result0 :> Var | Numb +//│ <: Abs[?] & 'j | Add[?] & 'k | App[?] & 'l | Mul[?] & 'm | Var & 'n | 'o & (Numb | Numb & ~#Numb) +//│ 'n <: Var & 'h +//│ 'm <: Add[?] & {lhs: 'g, rhs: 'g} | Mul[?] & {lhs: 'g, rhs: 'g} | Numb & 'result | Var +//│ 'l <: Abs[?] & {body: 'g} | App[?] & {rhs: 'g} | Var & 'h +//│ 'j <: Abs[?] | App[?] | Var & 'h //│ = //│ eval_lexpr', eval_var, list_assoc and eq are not implemented :Fuel 20000 +:stats rec def eval4 subst = eval_lexpr' eval4 subst -//│ eval4: (List[?] & 'tail & 'tail0 & 'tail1 & 'tail2 & 'tail3 & 'tail4) -> 'b -> 'rhs +//│ eval4: ('tail & 'tail0 & 'tail1 & 'tail2 & 'tail3 & 'tail4 & (Cons[?] & List[?] & 'b & 'c & 'd & 'e & 'f & 'g | Nil & List[?])) -> 'h -> 'i //│ where -//│ 'tail4 <: Cons[?]\head\tail & {head: {_1: string, _2: 'result}, tail: 'tail4} | Nil -//│ 'tail3 <: Cons[?]\head\tail & {head: {_1: string, _2: 'result}, tail: 'tail3} | Nil -//│ 'tail2 <: Cons[?]\head\tail & {head: {_1: string, _2: 'result}, tail: 'tail2} | Nil -//│ 'tail1 <: Cons[?]\head\tail & {head: {_1: string, _2: 'result}, tail: 'tail1} | Nil -//│ 'tail0 <: Cons[?]\head\tail & {head: {_1: string, _2: 'result}, tail: 'tail0} | Nil -//│ 'tail <: Cons[?]\head\tail & {head: {_1: string, _2: 'result}, tail: 'tail} | Nil -//│ 'result :> 'rhs | Var | Num -//│ <: 'b & (Abs[?] & 'c | 'lhs & (Abs[?] & 'c & ~#Abs | 'lhs0 & (Num | ~#Abs & ~#Num))) -//│ 'rhs :> Abs['rhs] | App['a]\lhs\rhs & {lhs: 'lhs | 'lhs0, rhs: 'rhs} | 'result | 'd | 'e | Add['rhs]\lhs\rhs & {lhs: 'rhs, rhs: 'rhs} | Mul['rhs]\lhs\rhs & {lhs: 'rhs, rhs: 'rhs} -//│ 'a :> Add['rhs]\lhs\rhs & {lhs: 'rhs, rhs: 'rhs} | App['a]\lhs\rhs & {lhs: 'lhs | 'lhs0, rhs: 'rhs} | Mul['rhs]\lhs\rhs & {lhs: 'rhs, rhs: 'rhs} | 'rhs | Var -//│ 'lhs :> Add['rhs]\lhs\rhs & {lhs: 'rhs, rhs: 'rhs} | App['a]\lhs\rhs & {lhs: 'lhs | 'lhs0, rhs: 'rhs} | Mul['rhs]\lhs\rhs & {lhs: 'rhs, rhs: 'rhs} | Num | Var -//│ <: 'b & 'a -//│ 'lhs0 :> Add['rhs]\lhs\rhs & {lhs: 'rhs, rhs: 'rhs} | App['a]\lhs\rhs & {lhs: 'lhs | 'lhs0, rhs: 'rhs} | Mul['rhs]\lhs\rhs & {lhs: 'rhs, rhs: 'rhs} | Num | Var -//│ <: 'b & 'a -//│ 'b <: Abs[?] & {body: 'b} | Add[?] & {lhs: 'b, rhs: 'b} | App[?] & {lhs: 'b, rhs: 'b} | Mul[?] & {lhs: 'b, rhs: 'b} | Num & (Add[?] & 'e | Mul[?] & 'e | Num & 'result | Var & 'd) | Var & 'd -//│ 'd :> Var -//│ <: 'b & (Abs[?] & 'c | 'lhs & (Abs[?] & 'c & ~#Abs | 'lhs0 & (Num & {name: string} | {name: string} & ~#Abs & ~#Num))) -//│ 'e :> Mul['rhs]\lhs\rhs & {lhs: 'rhs, rhs: 'rhs} | Add['rhs]\lhs\rhs & {lhs: 'rhs, rhs: 'rhs} -//│ <: 'b & (Abs[?] & {lhs: anything, rhs: anything} & 'c | 'lhs & (Abs[?] & {lhs: anything, rhs: anything} & 'c & ~#Abs | 'lhs0 & (Num & {lhs: anything, rhs: anything} | {lhs: anything, rhs: anything} & ~#Abs & ~#Num))) -//│ 'c <: {body: 'b, name: string} +//│ 'tail4 <: Cons[?] & 'd | Nil +//│ 'd <: {head: {0: string, 1: 'result}, tail: 'tail4} +//│ 'tail3 <: Cons[?] & 'c | Nil +//│ 'c <: {head: {0: string, 1: 'result0}, tail: 'tail3} +//│ 'tail2 <: Cons[?] & 'b | Nil +//│ 'b <: {head: {0: string, 1: 'result0}, tail: 'tail2} +//│ 'tail1 <: Cons[?] & 'g | Nil +//│ 'g <: {head: {0: string, 1: 'result0}, tail: 'tail1} +//│ 'tail0 <: Cons[?] & 'f | Nil +//│ 'f <: {head: {0: string, 1: 'result}, tail: 'tail0} +//│ 'tail <: Cons[?] & 'e | Nil +//│ 'e <: {head: {0: string, 1: 'result}, tail: 'tail} +//│ 'result :> Var | 'i | Numb +//│ <: Abs[?] & 'j & 'k & 'l | 'lhs & 'lhs0 & (Add[?] & 'm | App[?] & 'n | Mul[?] & 'o | Var & 'p | 'q & (Numb | Numb & ~#Numb)) +//│ 'i :> 'result0 | 'r | Abs['i] | App['a]\lhs\rhs & {lhs: 'lhs0, rhs: 'rhs} | 'result | 's | Add['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | Mul['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | App['a0]\lhs\rhs & {lhs: 'lhs, rhs: 'rhs0} +//│ 'result0 :> 'i +//│ <: Abs[?] & 'j & 'k & 'l | 'lhs0 & (Abs[?] & 'k & 'l & ~#Abs | 'lhs & (Add[?] & 'm | App[?] & 'n | Mul[?] & 'o | Var & 'p | 'q & (Numb | Numb & ~#Numb))) +//│ 'j <: {body: 'h, name: string} +//│ 'h <: Abs[?] & 'k | Add[?] & 'm | App[?] & 'n | Mul[?] & 'o | Numb & 'q | Var & 'p +//│ 'k <: Abs[?] & {body: 'h} | App[?] & {lhs: 'h, rhs: 'h} | Var & 'r +//│ 'r :> Var +//│ <: Abs[?] & 'j & 'k & 'l | 'lhs0 & (Abs[?] & 'k & 'l & ~#Abs | 'lhs & (Add[?] & {name: string} & 'm | App[?] & {name: string} & 'n | Mul[?] & {name: string} & 'o | Var & 'p | 'q & (Numb & {name: string} | Numb & {name: string} & ~#Numb))) +//│ 'm <: Add[?] & {lhs: 'h, rhs: 'h} | Mul[?] & {lhs: 'h, rhs: 'h} | Numb & 'result | Var & 's +//│ 's <: Var & (Abs[?] & 'j & 'k & 'l | 'lhs & 'lhs0 & (Add[?] & {name: string} & 'm | App[?] & {name: string} & 'n | Mul[?] & {name: string} & 'o | Var & 'p | 'q & (Numb & {name: string} | Numb & {name: string} & ~#Numb))) +//│ 'q <: Add[?] & {lhs: 'h, rhs: 'h} | Mul[?] & {lhs: 'h, rhs: 'h} | Numb & 'result | Var & 's +//│ 'p <: Var & 'r +//│ 'o <: Add[?] & {lhs: 'h, rhs: 'h} | Mul[?] & {lhs: 'h, rhs: 'h} | Numb & 'result | Var & 's +//│ 'n <: Abs[?] & {body: 'h} | App[?] & {lhs: 'h, rhs: 'h} | Var & 'r +//│ 'lhs0 :> Add['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | App['a0 | 'a]\lhs\rhs & {lhs: 'lhs0 | 'lhs, rhs: 'rhs0 | 'rhs} | Mul['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | Numb | Var +//│ <: 'h & 'a +//│ 'a0 :> Add['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | App['a0 | 'a]\lhs\rhs & {lhs: 'lhs0 | 'lhs, rhs: 'rhs0 | 'rhs} | Mul['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | 'i +//│ 'a :> Add['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | App['a0 | 'a]\lhs\rhs & {lhs: 'lhs0 | 'lhs, rhs: 'rhs0 | 'rhs} | Mul['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | 'i +//│ 'lhs :> Add['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | App['a0 | 'a]\lhs\rhs & {lhs: 'lhs0 | 'lhs, rhs: 'rhs0 | 'rhs} | Mul['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | Numb | Var +//│ <: 'h & 'a0 +//│ 'rhs :> 'i +//│ 'rhs0 :> 'i +//│ 'l <: {body: 'h, name: string} //│ = //│ eval_lexpr', eval_var, list_assoc and eq are not implemented +//│ constrain calls : 16185 +//│ annoying calls : 2300 +//│ subtyping calls : 602211 :ResetFuel -eval4 Nil (Abs { name = "s"; body = Add { lhs = Var { name = "s" }; rhs = Num { num = 1 } } }) -//│ res: 'result +eval4 Nil (Abs { name = "s"; body = Add { lhs = Var { name = "s" }; rhs = Numb { num = 1 } } }) +//│ res: 'b //│ where -//│ 'result :> Var | Abs['result] | (App['a] with {lhs: 'lhs, rhs: 'result}) | Num | 'b -//│ 'a :> (App['a] with {lhs: 'lhs, rhs: 'result}) | Num | 'result | Var | 'b -//│ 'lhs :> (App['a] with {lhs: 'lhs, rhs: 'result}) | Num | Var | 'b -//│ 'b :> Add['result] | Mul['result] +//│ 'b :> Var | Abs['b] | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | Numb | (Add['b] with {lhs: 'b, rhs: 'b}) | (Mul['b] with {lhs: 'b, rhs: 'b}) +//│ 'a :> 'lhs | 'b +//│ 'lhs :> (Add['b] with {lhs: 'b, rhs: 'b}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['b] with {lhs: 'b, rhs: 'b}) | Numb | Var +//│ 'rhs :> 'b //│ = //│ eval4, eval_lexpr', eval_var, list_assoc and eq are not implemented diff --git a/shared/src/test/diff/mlscript/ProvFlows.mls b/shared/src/test/diff/mlscript/ProvFlows.mls index f0a262c11c..d5409dee17 100644 --- a/shared/src/test/diff/mlscript/ProvFlows.mls +++ b/shared/src/test/diff/mlscript/ProvFlows.mls @@ -318,7 +318,7 @@ succ foo //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.317: succ foo //│ ║ ^^^^^^^^ -//│ ╟── type `string` is not an instance of `int` +//│ ╟── type `string` is not an instance of type `int` //│ ║ l.313: def foo: int | string //│ ║ ^^^^^^ //│ ╟── but it flows into reference with expected type `int` @@ -331,6 +331,9 @@ succ foo //│ ╟── [info] flowing from type `string` //│ ║ l.313: def foo: int | string //│ ║ ^^^^^^ +//│ ╟── [info] flowing from type `string` +//│ ║ l.313: def foo: int | string +//│ ║ ^^^^^^ //│ ╟── [info] flowing from type `int | string` //│ ║ l.313: def foo: int | string //│ ║ ^^^^^^^^^^^^ @@ -339,6 +342,9 @@ succ foo //│ ║ ^^^ //│ ╟── [info] flowing into type `int` //│ ║ l.3: def succ: int -> int +//│ ║ ^^^ +//│ ╟── [info] flowing into type `int` +//│ ║ l.3: def succ: int -> int //│ ╙── ^^^ //│ res: error | int @@ -367,31 +373,31 @@ ty00 = ty11 //│ <: ty00: //│ (A & 'a | B & 'b) -> ('a, 'b,) //│ ╔══[ERROR] Type mismatch in def definition: -//│ ║ l.365: ty00 = ty11 +//│ ║ l.371: ty00 = ty11 //│ ║ ^^^^^^^^^^^ //│ ╟── type `B & 'b` does not match type `'a` -//│ ║ l.352: def ty00: ('a & A | 'b & B) -> ('a, 'b) +//│ ║ l.358: def ty00: ('a & A | 'b & B) -> ('a, 'b) //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type variable: -//│ ║ l.352: def ty00: ('a & A | 'b & B) -> ('a, 'b) +//│ ║ l.358: def ty00: ('a & A | 'b & B) -> ('a, 'b) //│ ║ ^^ //│ ╟── ========= Additional explanations below ========= //│ ╟── [info] flowing from expression of type `B & 'b` //│ ╟── [info] flowing from type `B & 'b` -//│ ║ l.352: def ty00: ('a & A | 'b & B) -> ('a, 'b) +//│ ║ l.358: def ty00: ('a & A | 'b & B) -> ('a, 'b) //│ ║ ^^^^^^ //│ ╟── [info] flowing from type `B & 'b` -//│ ║ l.352: def ty00: ('a & A | 'b & B) -> ('a, 'b) +//│ ║ l.358: def ty00: ('a & A | 'b & B) -> ('a, 'b) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── [info] flowing from type `B & 'b` -//│ ║ l.355: def ty11: ('a & A | 'a & B) -> ('a, 'a) +//│ ║ l.361: def ty11: ('a & A | 'a & B) -> ('a, 'a) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── [info] flowing from expression of type `'a0` //│ ╟── [info] flowing from type `'a1` -//│ ║ l.355: def ty11: ('a & A | 'a & B) -> ('a, 'a) +//│ ║ l.361: def ty11: ('a & A | 'a & B) -> ('a, 'a) //│ ║ ^^ //│ ╟── [info] flowing into type `'a` -//│ ║ l.352: def ty00: ('a & A | 'b & B) -> ('a, 'b) +//│ ║ l.358: def ty00: ('a & A | 'b & B) -> ('a, 'b) //│ ║ ^^ //│ ╙── [info] flowing into expression of type `'a` diff --git a/shared/src/test/diff/mlscript/Random2.mls b/shared/src/test/diff/mlscript/Random2.mls index b79b604f35..1b5c4ae353 100644 --- a/shared/src/test/diff/mlscript/Random2.mls +++ b/shared/src/test/diff/mlscript/Random2.mls @@ -57,9 +57,7 @@ rec def f a = a a //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.51: rec def f a = a a //│ ║ ^^^^^^^^^^^^^^^^^ -//│ ╟── function of type `?a -> ?b` does not have field 'x' -//│ ║ l.51: rec def f a = a a -//│ ║ ^^^^^^^ +//│ ╟── expression of type `forall ?f. ?f` does not match type `{x: 'a}` //│ ╟── Note: constraint arises from record type: //│ ║ l.8: def f: { x: 'a } as 'a //│ ╙── ^^^^^^^^^ diff --git a/shared/src/test/diff/mlscript/RecDefs.mls b/shared/src/test/diff/mlscript/RecDefs.mls index f19faaa9e8..4722c8bc44 100644 --- a/shared/src/test/diff/mlscript/RecDefs.mls +++ b/shared/src/test/diff/mlscript/RecDefs.mls @@ -12,3 +12,24 @@ f 2 //│ = 2 + +rec def f = if true then fun (x: 'a) -> 32 else let tmp = f f + 1 in error +//│ f: anything -> 32 +//│ = [Function: f1] + + +:e +rec def f = forall 'a. (fun (x: 'a) -> 32) : 'a +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.22: rec def f = forall 'a. (fun (x: 'a) -> 32) : 'a +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ╟── function of type `'a -> 32` does not match type `'a` +//│ ║ l.22: rec def f = forall 'a. (fun (x: 'a) -> 32) : 'a +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from rigid type variable: +//│ ║ l.22: rec def f = forall 'a. (fun (x: 'a) -> 32) : 'a +//│ ╙── ^^ +//│ f: nothing +//│ = [Function: f2] + + diff --git a/shared/src/test/diff/mlscript/RecErrors.mls b/shared/src/test/diff/mlscript/RecErrors.mls index 8e5bd72448..06849f7e7d 100644 --- a/shared/src/test/diff/mlscript/RecErrors.mls +++ b/shared/src/test/diff/mlscript/RecErrors.mls @@ -57,7 +57,7 @@ rec def lefts() = //│ ╙── ^^^^^^^ //│ lefts: () -> 'a //│ where -//│ 'a :> Left with {v: 'a} +//│ 'a :> Left & {v: forall 'a. 'a} diff --git a/shared/src/test/diff/mlscript/RecursiveTypes.mls b/shared/src/test/diff/mlscript/RecursiveTypes.mls index c00d2adda3..6bcef07a2f 100644 --- a/shared/src/test/diff/mlscript/RecursiveTypes.mls +++ b/shared/src/test/diff/mlscript/RecursiveTypes.mls @@ -141,33 +141,27 @@ if true then l else r rec def l (a: int) b = if true then l else b rec def r (a: int) b c = if true then r else if true then b else c -//│ l: 'l +//│ l: int -> 'a -> 'a //│ where -//│ 'l :> int -> 'a -> 'a -//│ 'a :> 'l +//│ 'a :> int -> 'a -> 'a //│ = [Function: l4] -//│ r: 'r +//│ r: int -> 'a -> 'a -> 'a //│ where -//│ 'r :> int -> 'a -> 'a -> 'a -//│ 'a :> 'r +//│ 'a :> int -> 'a -> 'a -> 'a //│ = [Function: r5] if true then l else r -//│ res: 'l | 'r +//│ res: int -> ('a & 'b) -> ('a -> 'a | 'b) //│ where -//│ 'r :> int -> 'a -> 'a -> 'a -//│ 'a :> 'r -//│ 'l :> int -> 'b -> 'b -//│ 'b :> 'l +//│ 'b :> int -> 'b -> 'b +//│ 'a :> int -> 'a -> 'a -> 'a //│ = [Function: l4] if true then l else r -//│ res: 'l | 'r +//│ res: int -> ('a & 'b) -> ('a -> 'a | 'b) //│ where -//│ 'r :> int -> 'a -> 'a -> 'a -//│ 'a :> 'r -//│ 'l :> int -> 'b -> 'b -//│ 'b :> 'l +//│ 'b :> int -> 'b -> 'b +//│ 'a :> int -> 'a -> 'a -> 'a //│ = [Function: l4] @@ -206,7 +200,7 @@ class C[A]: { a: A } :ns rec def foo (c: C['a]) = foo (c.a) -//│ foo: forall 'b 'foo 'a 'a0. 'foo +//│ foo: forall 'foo 'a 'a0 'b. 'foo //│ where //│ 'foo := C['a] -> 'b //│ 'a <: 'a0 @@ -358,16 +352,16 @@ bar2_ty2 = bar_ty2 //│ where //│ 'r :> {x: 'r} //│ ╔══[ERROR] Type mismatch in def definition: -//│ ║ l.352: bar2_ty2 = bar_ty2 +//│ ║ l.346: bar2_ty2 = bar_ty2 //│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ╟── type `C['r]` does not have field 'x' -//│ ║ l.285: def bar_ty2: C['r] as 'r +//│ ╟── type `forall 'r. 'r` does not match type `{x: 'r0}` +//│ ║ l.279: def bar_ty2: C['r] as 'r //│ ║ ^^^^^ -//│ ╟── but it flows into reference with expected type `{x: 'r0}` -//│ ║ l.352: bar2_ty2 = bar_ty2 +//│ ╟── but it flows into reference with expected type `{x: 'r1}` +//│ ║ l.346: bar2_ty2 = bar_ty2 //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from record type: -//│ ║ l.332: def bar2_ty2: { x: 'r } as 'r +//│ ║ l.326: def bar2_ty2: { x: 'r } as 'r //│ ╙── ^^^^^^^^^ :e @@ -380,16 +374,16 @@ bar_ty2 = bar2_ty2 //│ where //│ 'r :> C['r] //│ ╔══[ERROR] Type mismatch in def definition: -//│ ║ l.374: bar_ty2 = bar2_ty2 +//│ ║ l.368: bar_ty2 = bar2_ty2 //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── type `{x: 'r}` is not an instance of type `C` -//│ ║ l.332: def bar2_ty2: { x: 'r } as 'r +//│ ║ l.326: def bar2_ty2: { x: 'r } as 'r //│ ║ ^^^^^^^^^ //│ ╟── but it flows into reference with expected type `C[?]` -//│ ║ l.374: bar_ty2 = bar2_ty2 +//│ ║ l.368: bar_ty2 = bar2_ty2 //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from applied type reference: -//│ ║ l.285: def bar_ty2: C['r] as 'r +//│ ║ l.279: def bar_ty2: C['r] as 'r //│ ╙── ^^^^^ @@ -411,28 +405,27 @@ f 1 :ns rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x -//│ f: forall 'b 'a 'f 'c 'd 'e 'g. 'f -//│ where -//│ 'f := 'b -> 'e -//│ 'b :> 'b\a & {a: 'd} -//│ <: ({a: 'a} | ~{a: 'd} | ~{})\a & ({a: 'a} | ~{a: 'd})\a & (number | ~{a: 'd} | ~{})\a & (number | ~{a: 'd})\a & (int | ~{a: 'd} | ~{})\a & (int | ~{a: 'd})\a & 'c & int & number -//│ 'a <: 'c -//│ 'c :> 'b\a & {a: 'd} -//│ <: 'e -//│ 'e :> 'b\a & {a: 'd} -//│ <: 'g -//│ 'g :> 'b\a & {a: 'd} +//│ f: forall 'b 'a 'c 'd 'e 'g 'f. 'f +//│ where +//│ 'f := 'b -> 'd +//│ 'b :> 'b\a & {a: 'e} +//│ <: ({a: 'a} | ~{a: 'e} | ~{})\a & ({a: 'a} | ~{a: 'e})\a & (number | ~{a: 'e} | ~{})\a & (number | ~{a: 'e})\a & (int | ~{a: 'e} | ~{})\a & (int | ~{a: 'e})\a & 'g & int & number +//│ 'a <: 'g +//│ 'g :> 'b\a & {a: 'e} +//│ <: 'd +//│ 'd :> 'b\a & {a: 'e} +//│ <: 'c +//│ 'c :> 'b\a & {a: 'e} //│ <: {a: 'a} -//│ 'd :> int +//│ 'e :> int f //│ res: 'a -> 'b //│ where //│ 'a :> 'a\a & {a: int} -//│ <: int & (int | ~{a: int})\a & (number | ~{a: int})\a & ('c | ~{a: int})\a & 'b -//│ 'c <: {a: 'b} +//│ <: int & (int | ~{a: int})\a & (number | ~{a: int})\a & ({a: 'b} | ~{a: int})\a & 'b //│ 'b :> 'a\a & {a: int} -//│ <: 'c +//│ <: {a: 'b} // Notice how what is most likely an the error is reported in call sites, // due to the delaying effect of the field removal type... @@ -442,13 +435,13 @@ f :e f 1 //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.443: f 1 +//│ ║ l.436: f 1 //│ ║ ^^^ //│ ╟── operator application of type `int` does not have field 'a' -//│ ║ l.413: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x +//│ ║ l.407: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x //│ ║ ^^^^^ //│ ╟── Note: constraint arises from field selection: -//│ ║ l.413: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x +//│ ║ l.407: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: error | int | 'a\a & {a: int} //│ where @@ -457,13 +450,13 @@ f 1 :e f { a = 1 } //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.458: f { a = 1 } +//│ ║ l.451: f { a = 1 } //│ ║ ^^^^^^^^^^^ //│ ╟── operator application of type `int` does not have field 'a' -//│ ║ l.413: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x +//│ ║ l.407: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x //│ ║ ^^^^^ //│ ╟── Note: constraint arises from field selection: -//│ ║ l.413: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x +//│ ║ l.407: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: error @@ -480,13 +473,13 @@ f ainf //│ where //│ 'ainf :> {a: 'ainf} //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.478: f ainf +//│ ║ l.471: f ainf //│ ║ ^^^^^^ //│ ╟── operator application of type `int` does not have field 'a' -//│ ║ l.413: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x +//│ ║ l.407: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x //│ ║ ^^^^^ //│ ╟── Note: constraint arises from field selection: -//│ ║ l.413: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x +//│ ║ l.407: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: error @@ -497,15 +490,15 @@ f infina //│ where //│ 'infina :> 0 & {a: 'infina} //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.495: f infina +//│ ║ l.488: f infina //│ ║ ^^^^^^^^ //│ ╟── operator application of type `int` does not have field 'a' -//│ ║ l.413: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x +//│ ║ l.407: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x //│ ║ ^^^^^ //│ ╟── Note: constraint arises from field selection: -//│ ║ l.413: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x +//│ ║ l.407: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ res: error | 'infina | int | 'a\a & {a: int} +//│ res: error | int | 'infina | 'a\a & {a: int} //│ where //│ 'a :> 'infina | 'a\a & {a: int} //│ 'infina :> 0 & {a: 'infina} @@ -520,36 +513,36 @@ def f_manual: (({a: 'b & 'a & 'c} as 'a) & 'd) -> ('c | ('d | 'e\a & {a: int} as :e f_manual 1 //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.521: f_manual 1 +//│ ║ l.514: f_manual 1 //│ ║ ^^^^^^^^^^ //│ ╟── integer literal of type `1` does not have field 'a' -//│ ║ l.521: f_manual 1 +//│ ║ l.514: f_manual 1 //│ ║ ^ //│ ╟── Note: constraint arises from record type: -//│ ║ l.514: def f_manual: (({a: 'b & 'a & 'c} as 'a) & 'd) -> ('c | ('d | 'e\a & {a: int} as 'e)) +//│ ║ l.507: def f_manual: (({a: 'b & 'a & 'c} as 'a) & 'd) -> ('c | ('d | 'e\a & {a: int} as 'e)) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── from intersection type: -//│ ║ l.514: def f_manual: (({a: 'b & 'a & 'c} as 'a) & 'd) -> ('c | ('d | 'e\a & {a: int} as 'e)) +//│ ║ l.507: def f_manual: (({a: 'b & 'a & 'c} as 'a) & 'd) -> ('c | ('d | 'e\a & {a: int} as 'e)) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ res: 'e | error +//│ res: error | 'e //│ where -//│ 'e :> 'e\a & {a: int} | 1 +//│ 'e :> 1 | 'e\a & {a: int} :e f_manual { a = 1 } //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.539: f_manual { a = 1 } +//│ ║ l.532: f_manual { a = 1 } //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `1` does not have field 'a' -//│ ║ l.539: f_manual { a = 1 } +//│ ║ l.532: f_manual { a = 1 } //│ ║ ^ //│ ╟── Note: constraint arises from record type: -//│ ║ l.514: def f_manual: (({a: 'b & 'a & 'c} as 'a) & 'd) -> ('c | ('d | 'e\a & {a: int} as 'e)) +//│ ║ l.507: def f_manual: (({a: 'b & 'a & 'c} as 'a) & 'd) -> ('c | ('d | 'e\a & {a: int} as 'e)) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── from intersection type: -//│ ║ l.514: def f_manual: (({a: 'b & 'a & 'c} as 'a) & 'd) -> ('c | ('d | 'e\a & {a: int} as 'e)) +//│ ║ l.507: def f_manual: (({a: 'b & 'a & 'c} as 'a) & 'd) -> ('c | ('d | 'e\a & {a: int} as 'e)) //│ ╙── ^^^^^^^^^^^^ -//│ res: 'e | 1 | error +//│ res: 1 | error | 'e //│ where //│ 'e :> 'e\a & {a: int} | {a: 1} @@ -570,27 +563,27 @@ f_manual ainf // Notice the simplified type is NOT the same as that of `f`... def f_manual_ns: 'a | ('b & (({a: 'd & 'c} as 'c) | ~{a: 'e | int} | ~{})\a & (({a: 'd & 'c} as 'c) | ~{a: 'e | int})\a & (({a: 'f} as 'c) as 'f) & (int | ~{a: 'e | int} | ~{})\a & (int | ~{a: 'e | int})\a & int & int) -> ('g | 'd | ('b | 'h\a & {a: 'e | int} as 'h)) -//│ f_manual_ns: in forall 'b 'd 'f 'c 'c0. ('b & 'f & ((int & 'c)\a & 'c0\a & int | int & ~{a: int})) -> ('b | 'd) out forall 'b 'd 'f 'c 'c0. ((int & 'c & 'c0 | ~{a: int})\a & int & 'b & 'f) -> ('b | 'd) +//│ f_manual_ns: in forall 'd 'b 'c 'c0 'f. ('b & 'f & (int & ~{a: int} | (int & 'c0)\a & 'c\a & int)) -> ('b | 'd) out forall 'd 'b 'c 'c0 'f. ((int & 'c0 & 'c | ~{a: int})\a & int & 'b & 'f) -> ('b | 'd) //│ where -//│ 'c0 <: {a: 'd & 'c0} -//│ 'c <: {a: 'c & 'd} +//│ 'c <: {a: 'd & 'c} +//│ 'c0 <: {a: 'c0 & 'd} //│ 'f <: {a: 'f} //│ 'b :> 'b\a & {a: int} :e f_manual_ns ainf //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.581: f_manual_ns ainf +//│ ║ l.574: f_manual_ns ainf //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── type `int` does not have field 'a' -//│ ║ l.572: def f_manual_ns: 'a | ('b & (({a: 'd & 'c} as 'c) | ~{a: 'e | int} | ~{})\a & (({a: 'd & 'c} as 'c) | ~{a: 'e | int})\a & (({a: 'f} as 'c) as 'f) & (int | ~{a: 'e | int} | ~{})\a & (int | ~{a: 'e | int})\a & int & int) -> ('g | 'd | ('b | 'h\a & {a: 'e | int} as 'h)) +//│ ║ l.565: def f_manual_ns: 'a | ('b & (({a: 'd & 'c} as 'c) | ~{a: 'e | int} | ~{})\a & (({a: 'd & 'c} as 'c) | ~{a: 'e | int})\a & (({a: 'f} as 'c) as 'f) & (int | ~{a: 'e | int} | ~{})\a & (int | ~{a: 'e | int})\a & int & int) -> ('g | 'd | ('b | 'h\a & {a: 'e | int} as 'h)) //│ ║ ^^^ //│ ╟── Note: constraint arises from record type: -//│ ║ l.572: def f_manual_ns: 'a | ('b & (({a: 'd & 'c} as 'c) | ~{a: 'e | int} | ~{})\a & (({a: 'd & 'c} as 'c) | ~{a: 'e | int})\a & (({a: 'f} as 'c) as 'f) & (int | ~{a: 'e | int} | ~{})\a & (int | ~{a: 'e | int})\a & int & int) -> ('g | 'd | ('b | 'h\a & {a: 'e | int} as 'h)) +//│ ║ l.565: def f_manual_ns: 'a | ('b & (({a: 'd & 'c} as 'c) | ~{a: 'e | int} | ~{})\a & (({a: 'd & 'c} as 'c) | ~{a: 'e | int})\a & (({a: 'f} as 'c) as 'f) & (int | ~{a: 'e | int} | ~{})\a & (int | ~{a: 'e | int})\a & int & int) -> ('g | 'd | ('b | 'h\a & {a: 'e | int} as 'h)) //│ ║ ^^^^^^^^^^^^ -//│ ╟── from intersection type: -//│ ║ l.572: def f_manual_ns: 'a | ('b & (({a: 'd & 'c} as 'c) | ~{a: 'e | int} | ~{})\a & (({a: 'd & 'c} as 'c) | ~{a: 'e | int})\a & (({a: 'f} as 'c) as 'f) & (int | ~{a: 'e | int} | ~{})\a & (int | ~{a: 'e | int})\a & int & int) -> ('g | 'd | ('b | 'h\a & {a: 'e | int} as 'h)) -//│ ╙── ^^^^^^^ +//│ ╟── from local type binding: +//│ ║ l.565: def f_manual_ns: 'a | ('b & (({a: 'd & 'c} as 'c) | ~{a: 'e | int} | ~{})\a & (({a: 'd & 'c} as 'c) | ~{a: 'e | int})\a & (({a: 'f} as 'c) as 'f) & (int | ~{a: 'e | int} | ~{})\a & (int | ~{a: 'e | int})\a & int & int) -> ('g | 'd | ('b | 'h\a & {a: 'e | int} as 'h)) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ res: error def f_manual_2: (({a: 'a} as 'a) & 'b) -> ('b | 'c\a & {a: int} as 'c) @@ -630,13 +623,13 @@ r + 1 :e r.a //│ ╔══[ERROR] Type mismatch in field selection: -//│ ║ l.631: r.a +//│ ║ l.624: r.a //│ ║ ^^^ //│ ╟── integer literal of type `1` does not have field 'a' -//│ ║ l.622: r = f 1 +//│ ║ l.615: r = f 1 //│ ║ ^ //│ ╟── but it flows into reference with expected type `{a: ?a}` -//│ ║ l.631: r.a +//│ ║ l.624: r.a //│ ╙── ^ //│ res: error | int diff --git a/shared/src/test/diff/mlscript/RecursiveTypes2.mls b/shared/src/test/diff/mlscript/RecursiveTypes2.mls index 39ee3b2ea0..65a2165b10 100644 --- a/shared/src/test/diff/mlscript/RecursiveTypes2.mls +++ b/shared/src/test/diff/mlscript/RecursiveTypes2.mls @@ -11,7 +11,7 @@ error : T1 : T2 //│ res: T2 //│ constrain calls : 6 //│ annoying calls : 4 -//│ subtyping calls : 29 +//│ subtyping calls : 24 type T3 = { x: T2 & { x: T3 } | T3 & { x: T2 } } //│ Defined type alias T3 @@ -21,14 +21,14 @@ error : T1 : T3 //│ res: T3 //│ constrain calls : 7 //│ annoying calls : 12 -//│ subtyping calls : 457 +//│ subtyping calls : 408 :stats error : T2 : T3 //│ res: T3 //│ constrain calls : 10 //│ annoying calls : 12 -//│ subtyping calls : 423 +//│ subtyping calls : 389 def f: T3 & { x: T1 & 'a } as 'a //│ f: 'a @@ -40,9 +40,9 @@ f.x //│ res: 'a & (T1 & T3 & {x: T2} | T1 & T2 & {x: T3}) //│ where //│ 'a :> T3 & {x: T1 & 'a} -//│ constrain calls : 6 +//│ constrain calls : 4 //│ annoying calls : 4 -//│ subtyping calls : 148 +//│ subtyping calls : 102 g = error : T1 & { x: T2 | 'a } as 'a //│ g: 'a @@ -54,9 +54,9 @@ g.x //│ res: T1 & {x: T1} & 'a | T1 & T2 & {x: T1} //│ where //│ 'a :> T1 & {x: 'a | T2} -//│ constrain calls : 6 +//│ constrain calls : 4 //│ annoying calls : 4 -//│ subtyping calls : 85 +//│ subtyping calls : 71 :stats f = g @@ -67,8 +67,8 @@ f = g //│ 'a //│ where //│ 'a :> T3 & {x: T1 & 'a} -//│ constrain calls : 69 -//│ annoying calls : 95 -//│ subtyping calls : 3476 +//│ constrain calls : 48 +//│ annoying calls : 60 +//│ subtyping calls : 1977 diff --git a/shared/src/test/diff/mlscript/References.mls b/shared/src/test/diff/mlscript/References.mls index 6bb8e8b915..b99c802054 100644 --- a/shared/src/test/diff/mlscript/References.mls +++ b/shared/src/test/diff/mlscript/References.mls @@ -160,7 +160,7 @@ move ri rn //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.159: move ri rn //│ ║ ^^^^^^^^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.87: rn = r : Ref[number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -209,12 +209,12 @@ swap ri rn //│ ╟── Note: constraint arises from type reference: //│ ║ l.86: ri = r : Ref[int] //│ ╙── ^^^ -//│ res: (unit, unit,) | error +//│ res: error | (unit, unit,) //│ = [ [], [] ] //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.202: swap ri rn //│ ║ ^^^^^^^^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.87: rn = r : Ref[number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -226,7 +226,7 @@ swap ri rn //│ ╟── Note: class type parameter A is defined at: //│ ║ l.3: class Ref[A] //│ ╙── ^ -//│ res: (unit, unit,) | error +//│ res: error | (unit, unit,) //│ = [ [], [] ] @@ -269,7 +269,7 @@ res.Swap //│ res: ('A & ((int | string) & 'b | 'd & (int | string))) -> ('b & 'd | 'A) refin: Ref['a..'b] | Ref['c..'d] -//│ res: Ref[in 'd & (~'d | int | string) out 'd] +//│ res: Ref[in 'd & (int | string | ~'d) out 'd] //│ = //│ refin is not implemented @@ -290,7 +290,7 @@ refrefin = RefImpl { mut value = error } //│ <: refrefin: //│ Ref[in Ref[out int | string] out Ref[in int | string out nothing]] //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown :e refrefin = RefImpl { mut value = RefImpl { mut value = error } } @@ -309,7 +309,7 @@ refrefin = RefImpl { mut value = RefImpl { mut value = error } } //│ ║ l.281: def refrefin: Ref[Ref[int] & Ref[string]] //│ ╙── ^^^ //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown def consrefin: (Ref[int] & Ref[string]) -> int @@ -351,7 +351,7 @@ refun.Swap //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.348: refun.Get //│ ║ ^^^^^^^^^ -//│ ╟── type `string` is not an instance of `int` +//│ ╟── type `string` is not an instance of type `int` //│ ║ l.339: def refun: Ref[int] | Ref[string] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -366,7 +366,7 @@ refun.Swap //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.349: fun x -> refun.Set x //│ ║ ^^^^^^^^^ -//│ ╟── type `string` is not an instance of `int` +//│ ╟── type `string` is not an instance of type `int` //│ ║ l.339: def refun: Ref[int] | Ref[string] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -381,7 +381,7 @@ refun.Swap //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.350: refun.Swap //│ ║ ^^^^^^^^^^ -//│ ╟── type `string` is not an instance of `int` +//│ ╟── type `string` is not an instance of type `int` //│ ║ l.339: def refun: Ref[int] | Ref[string] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -390,7 +390,7 @@ refun.Swap //│ ╟── Note: class type parameter A is defined at: //│ ║ l.3: class Ref[A] //│ ╙── ^ -//│ res: nothing -> (int | string) | error +//│ res: error | nothing -> (int | string) //│ = //│ refun is not implemented diff --git a/shared/src/test/diff/mlscript/Repro.mls b/shared/src/test/diff/mlscript/Repro.mls index e69de29bb2..539c23787c 100644 --- a/shared/src/test/diff/mlscript/Repro.mls +++ b/shared/src/test/diff/mlscript/Repro.mls @@ -0,0 +1,3 @@ +:NewDefs + + diff --git a/shared/src/test/diff/mlscript/RigidVariables.mls b/shared/src/test/diff/mlscript/RigidVariables.mls new file mode 100644 index 0000000000..ad93234d27 --- /dev/null +++ b/shared/src/test/diff/mlscript/RigidVariables.mls @@ -0,0 +1,71 @@ + + +:e +def foo[AAA]: AAA -> AAA +def foo(x) = x.a +//│ foo: 'a -> 'a +//│ = +//│ {a: 'a} -> 'a +//│ <: foo: +//│ 'a -> 'a +//│ ╔══[ERROR] Type mismatch in def definition: +//│ ║ l.5: def foo(x) = x.a +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ╟── expression of type `?a` does not have field 'a' +//│ ╟── Note: constraint arises from field selection: +//│ ║ l.5: def foo(x) = x.a +//│ ║ ^^^ +//│ ╟── from reference: +//│ ║ l.5: def foo(x) = x.a +//│ ╙── ^ +//│ = [Function: foo] + +:e +class Test0 + method Foo0[AAA]: AAA -> AAA + method Foo0(x) = x.a +//│ ╔══[ERROR] Type mismatch in method definition: +//│ ║ l.26: method Foo0(x) = x.a +//│ ║ ^^^^^^^^^^^^^ +//│ ╟── type `AAA` does not have field 'a' +//│ ║ l.25: method Foo0[AAA]: AAA -> AAA +//│ ║ ^^^ +//│ ╟── Note: constraint arises from field selection: +//│ ║ l.26: method Foo0(x) = x.a +//│ ║ ^^^ +//│ ╟── from reference: +//│ ║ l.26: method Foo0(x) = x.a +//│ ╙── ^ +//│ Defined class Test0 +//│ Declared Test0.Foo0: Test0 -> 'AAA -> 'AAA +//│ Defined Test0.Foo0: Test0 -> {a: 'a} -> 'a + +class Test1 + method Foo1[AAA]: (AAA & {a: int}) -> int + method Foo1(x) = x.a +//│ Defined class Test1 +//│ Declared Test1.Foo1: Test1 -> {a: int} -> int +//│ Defined Test1.Foo1: Test1 -> {a: 'a} -> 'a + +:e +(Test1{}).Foo1({x=1}) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.51: (Test1{}).Foo1({x=1}) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── record literal of type `{x: 1}` does not have field 'a' +//│ ║ l.51: (Test1{}).Foo1({x=1}) +//│ ║ ^^^^^ +//│ ╟── Note: constraint arises from record type: +//│ ║ l.44: method Foo1[AAA]: (AAA & {a: int}) -> int +//│ ║ ^^^^^^^^ +//│ ╟── from intersection type: +//│ ║ l.44: method Foo1[AAA]: (AAA & {a: int}) -> int +//│ ╙── ^^^^^^^^^^^^^^^^ +//│ res: error | int +//│ = undefined + +(Test1{}).Foo1({a=1}) +//│ res: int +//│ = 1 + + diff --git a/shared/src/test/diff/mlscript/SafeDiv.mls b/shared/src/test/diff/mlscript/SafeDiv.mls index a462b57251..7392c71862 100644 --- a/shared/src/test/diff/mlscript/SafeDiv.mls +++ b/shared/src/test/diff/mlscript/SafeDiv.mls @@ -48,7 +48,7 @@ type Option[A] = Some[A] | None fun x -> case x of { int -> safeDiv 1 x | _ -> None{} } -//│ res: (int & ~0 | ~int) -> (int | None) +//│ res: (int & ~0 | ~int) -> (None | int) //│ = [Function: res] :e // we no longer refine x's type here, as that was rather unexpected diff --git a/shared/src/test/diff/mlscript/SelfNeg.mls b/shared/src/test/diff/mlscript/SelfNeg.mls index 8c9c497769..50e34e43db 100644 --- a/shared/src/test/diff/mlscript/SelfNeg.mls +++ b/shared/src/test/diff/mlscript/SelfNeg.mls @@ -56,11 +56,11 @@ def foo: ('a -> (~'a -> anything) -> anything) -> MutArray['a] :ns r = foo (fun a -> fun f -> f a) -//│ r: forall 'a 'b 'c. 'c +//│ r: forall 'b 'a 'c. 'b //│ where -//│ 'c :> MutArray['a] -//│ 'a <: 'b -//│ 'b <: ~'a +//│ 'b :> MutArray['a] +//│ 'a <: 'c +//│ 'c <: ~'a //│ = //│ foo is not implemented @@ -120,7 +120,7 @@ r //│ = //│ r and bar are not implemented -r._1 : nothing +r.0 : nothing //│ res: nothing //│ = //│ r and bar are not implemented diff --git a/shared/src/test/diff/mlscript/SelfNegs.mls b/shared/src/test/diff/mlscript/SelfNegs.mls index 2313cff065..8446f2bbb4 100644 --- a/shared/src/test/diff/mlscript/SelfNegs.mls +++ b/shared/src/test/diff/mlscript/SelfNegs.mls @@ -6,13 +6,13 @@ def foo(f: (~'a) -> 'a, a: 'a) = //│ = [Function: foo] def foo(fa: ((~'a) -> 'a, 'a)) = - fa._1 fa._2 -//│ foo: (~'a -> 'a, 'a,) -> 'a + fa.0 fa.1 +//│ foo: ((~'a -> 'a, 'a,),) -> 'a //│ = [Function: foo1] :ns foo -//│ res: forall 'a 'b 'c. (~'a -> 'a, 'a,) -> 'c +//│ res: forall 'a 'b 'c. ((~'a -> 'a, 'a,),) -> 'c //│ where //│ 'a <: 'c & 'b //│ 'b <: ~'a diff --git a/shared/src/test/diff/mlscript/Seqs.mls b/shared/src/test/diff/mlscript/Seqs.mls index 8b172e2ed7..1e9912d6d3 100644 --- a/shared/src/test/diff/mlscript/Seqs.mls +++ b/shared/src/test/diff/mlscript/Seqs.mls @@ -33,7 +33,7 @@ Nil{} //│ ╟── record literal of type `anything` does not have field 'size' //│ ║ l.29: Nil{} //│ ╙── ^^ -//│ res: error | Nil & {size: nothing} +//│ res: Nil & {size: nothing} | error //│ = Nil { size: undefined } def Nil = Nil { size = 0 } diff --git a/shared/src/test/diff/mlscript/Sequence.mls b/shared/src/test/diff/mlscript/Sequence.mls new file mode 100644 index 0000000000..42743cc59c --- /dev/null +++ b/shared/src/test/diff/mlscript/Sequence.mls @@ -0,0 +1,65 @@ +:NewDefs + + +:e +let test(x) = log(x); x + 1 +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.5: let test(x) = log(x); x + 1 +//│ ╙── ^ +//│ let test: anything -> () +//│ Int +//│ Code generation encountered an error: +//│ unresolved symbol x + +let test(x) = log(x), x + 1 +//│ let test: Int -> Int +//│ test +//│ = [Function: test1] + +test(1) +//│ Int +//│ res +//│ = 2 +//│ // Output +//│ 1 + +:pe +:e +test(log("here we go"); 123) +//│ ╔══[PARSE ERROR] Unexpected semicolon here +//│ ║ l.28: test(log("here we go"); 123) +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.28: test(log("here we go"); 123) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── application of type `()` is not an instance of type `Int` +//│ ║ l.28: test(log("here we go"); 123) +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from reference: +//│ ║ l.14: let test(x) = log(x), x + 1 +//│ ╙── ^ +//│ Int | error +//│ res +//│ = NaN +//│ // Output +//│ here we go +//│ undefined + +test((log("here we go"), 123)) +//│ Int +//│ res +//│ = 124 +//│ // Output +//│ here we go +//│ 123 + + +let a = 1; let b = a + 1 +//│ let a: 1 +//│ let b: Int +//│ a +//│ = 1 +//│ b +//│ = 2 + + diff --git a/shared/src/test/diff/mlscript/SimpleMethods.mls b/shared/src/test/diff/mlscript/SimpleMethods.mls index 1d881c7bc9..32b2dd048a 100644 --- a/shared/src/test/diff/mlscript/SimpleMethods.mls +++ b/shared/src/test/diff/mlscript/SimpleMethods.mls @@ -6,7 +6,7 @@ class C0 //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.4: method Foo0[A](a: A) = a + 1 //│ ║ ^^^ -//│ ╟── reference of type `A` is not an instance of type `int` +//│ ╟── reference of type `#A` is not an instance of type `int` //│ ║ l.4: method Foo0[A](a: A) = a + 1 //│ ║ ^ //│ ╟── Note: method type parameter A is defined at: diff --git a/shared/src/test/diff/mlscript/Stress.mls b/shared/src/test/diff/mlscript/Stress.mls index d2a6e09f53..3d0c630b5a 100644 --- a/shared/src/test/diff/mlscript/Stress.mls +++ b/shared/src/test/diff/mlscript/Stress.mls @@ -31,7 +31,7 @@ def foo x = case x of { //│ foo: (A[?] & {fA: 'fA} | B[?] & {fB: 'fA} | C[?] & {fC: 'fA} | D[?] & {fD: 'fA} | E[?] & {fE: 'fA} | F[?] & {fF: 'fA} | G[?] & {fG: 'fA} | H[?] & {fH: 'fA}) -> 'fA //│ constrain calls : 26 //│ annoying calls : 0 -//│ subtyping calls : 987 +//│ subtyping calls : 986 // ====== 1 & all ====== // @@ -64,7 +64,7 @@ foo arg //│ res: int //│ constrain calls : 38 //│ annoying calls : 20 -//│ subtyping calls : 311 +//│ subtyping calls : 294 // ====== 3 ====== // @@ -82,7 +82,7 @@ foo arg //│ res: int //│ constrain calls : 51 //│ annoying calls : 30 -//│ subtyping calls : 590 +//│ subtyping calls : 549 // ====== 4 ====== // @@ -101,21 +101,21 @@ foo arg //│ res: int //│ constrain calls : 64 //│ annoying calls : 40 -//│ subtyping calls : 976 +//│ subtyping calls : 888 :stats foo (arg with { x = 1} with { y = 2 }) //│ res: int //│ constrain calls : 39 //│ annoying calls : 37 -//│ subtyping calls : 489 +//│ subtyping calls : 405 :stats foo (arg with { x = 1; y = 2; z = 3 }) //│ res: int //│ constrain calls : 39 //│ annoying calls : 37 -//│ subtyping calls : 453 +//│ subtyping calls : 381 // ====== 5 ====== // @@ -135,7 +135,7 @@ foo arg //│ res: int //│ constrain calls : 77 //│ annoying calls : 50 -//│ subtyping calls : 1483 +//│ subtyping calls : 1319 // ====== 6 ====== // @@ -156,7 +156,7 @@ foo arg //│ res: int //│ constrain calls : 90 //│ annoying calls : 60 -//│ subtyping calls : 2125 +//│ subtyping calls : 1850 // ====== 7 ====== // @@ -178,7 +178,7 @@ foo arg //│ res: int //│ constrain calls : 103 //│ annoying calls : 70 -//│ subtyping calls : 2916 +//│ subtyping calls : 2489 // ====== 8 ====== // @@ -201,6 +201,6 @@ foo arg //│ res: int //│ constrain calls : 116 //│ annoying calls : 80 -//│ subtyping calls : 3870 +//│ subtyping calls : 3244 diff --git a/shared/src/test/diff/mlscript/StressDNF.mls b/shared/src/test/diff/mlscript/StressDNF.mls index c534504c2c..a190878d59 100644 --- a/shared/src/test/diff/mlscript/StressDNF.mls +++ b/shared/src/test/diff/mlscript/StressDNF.mls @@ -21,7 +21,7 @@ ty0 = ty0 //│ (E & 'e & 'f | 'd & (C & 'c | D & 'e) | 'b & (A & 'a | B & 'c)) -> ('a, 'b, 'c, 'd, 'e, 'f,) //│ constrain calls : 1 //│ annoying calls : 0 -//│ subtyping calls : 181 +//│ subtyping calls : 259 def ty1: ('a & A | 'b & B | 'c & C | 'd & D | 'e & E) -> ('a, 'b, 'c, 'd, 'e, 'f) //│ ty1: (A & 'a | B & 'b | C & 'c | D & 'd | E & 'e) -> ('a, 'b, 'c, 'd, 'e, nothing,) @@ -33,7 +33,7 @@ ty0 = ty1 //│ (E & 'e & 'f | 'd & (C & 'c | D & 'e) | 'b & (A & 'a | B & 'c)) -> ('a, 'b, 'c, 'd, 'e, 'f,) //│ constrain calls : 45 //│ annoying calls : 25 -//│ subtyping calls : 1120 +//│ subtyping calls : 1035 :stats :e @@ -52,6 +52,6 @@ ty1 = ty0 //│ ╙── ^^ //│ constrain calls : 70 //│ annoying calls : 51 -//│ subtyping calls : 1255 +//│ subtyping calls : 1048 diff --git a/shared/src/test/diff/mlscript/StressTraits.mls b/shared/src/test/diff/mlscript/StressTraits.mls index d3deeb4b8f..c99e11f3af 100644 --- a/shared/src/test/diff/mlscript/StressTraits.mls +++ b/shared/src/test/diff/mlscript/StressTraits.mls @@ -31,7 +31,7 @@ def foo x = case x of { //│ foo: ({fA: 'fA} & #A | ~#A & ({fB: 'fA} & #B | ~#B & ({fC: 'fA} & #C | ~#C & ({fD: 'fA} & #D | ~#D & ({fE: 'fA} & #E | ~#E & ({fF: 'fA} & #F | ~#F & ({fG: 'fA} & #G | {fH: 'fA} & #H & ~#G))))))) -> 'fA //│ constrain calls : 26 //│ annoying calls : 0 -//│ subtyping calls : 659 +//│ subtyping calls : 623 // ====== 1 & all ====== // @@ -43,7 +43,7 @@ foo arg //│ res: int //│ constrain calls : 20 //│ annoying calls : 9 -//│ subtyping calls : 672 +//│ subtyping calls : 257 :stats :e @@ -53,7 +53,7 @@ foo arg //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.51: foo arg //│ ║ ^^^^^^^ -//│ ╟── expression of type `#B & ({fB: int} & #A | {fB: int} & ~?a)` does not have field 'fA' +//│ ╟── expression of type `#B & ({fB: int} & #A | {fB: int} & ~#B | {fB: int} & ~?a)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.22: | A -> x.fA //│ ║ ^^^^ @@ -63,7 +63,7 @@ foo arg //│ res: error | int //│ constrain calls : 39 //│ annoying calls : 31 -//│ subtyping calls : 1542 +//│ subtyping calls : 741 :stats :e @@ -73,7 +73,7 @@ foo arg //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.71: foo arg //│ ║ ^^^^^^^ -//│ ╟── expression of type `#B & ({fB: int} & #A | {fB: int} & ~?a)` does not have field 'fA' +//│ ╟── expression of type `#B & ({fB: int} & #A | {fB: int} & ~#B | {fB: int} & ~?a)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.22: | A -> x.fA //│ ║ ^^^^ @@ -83,7 +83,7 @@ foo arg //│ res: error | int //│ constrain calls : 71 //│ annoying calls : 90 -//│ subtyping calls : 4318 +//│ subtyping calls : 4236 :stats :e @@ -93,7 +93,7 @@ foo arg //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.91: foo arg //│ ║ ^^^^^^^ -//│ ╟── expression of type `#B & ({fB: int} & #A | {fB: int} & ~?a)` does not have field 'fA' +//│ ╟── expression of type `#B & ({fB: int} & #A | {fB: int} & ~#B | {fB: int} & ~?a)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.22: | A -> x.fA //│ ║ ^^^^ @@ -101,9 +101,9 @@ foo arg //│ ║ l.21: def foo x = case x of { //│ ╙── ^ //│ res: error -//│ constrain calls : 94 -//│ annoying calls : 216 -//│ subtyping calls : 17311 +//│ constrain calls : 90 +//│ annoying calls : 209 +//│ subtyping calls : 25579 // ====== 2 ====== // @@ -121,7 +121,7 @@ foo arg //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.118: foo arg //│ ║ ^^^^^^^ -//│ ╟── expression of type `#B & ({fB: int} & #A | {fB: int} & ~?a)` does not have field 'fA' +//│ ╟── expression of type `#B & ({fB: int} & #A | {fB: int} & ~#B | {fB: int} & ~?a)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.114: | A -> x.fA //│ ║ ^^^^ @@ -131,7 +131,7 @@ foo arg //│ res: error | int //│ constrain calls : 47 //│ annoying calls : 31 -//│ subtyping calls : 525 +//│ subtyping calls : 523 // ====== 3 ====== // @@ -150,7 +150,7 @@ foo arg //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.147: foo arg //│ ║ ^^^^^^^ -//│ ╟── expression of type `#B & ({fB: int} & #A | {fB: int} & ~?a)` does not have field 'fA' +//│ ╟── expression of type `#B & ({fB: int} & #A | {fB: int} & ~#B | {fB: int} & ~?a)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.142: | A -> x.fA //│ ║ ^^^^ @@ -160,7 +160,7 @@ foo arg //│ res: error | int //│ constrain calls : 82 //│ annoying calls : 90 -//│ subtyping calls : 2942 +//│ subtyping calls : 4004 // ====== 4 ====== // @@ -180,7 +180,7 @@ foo arg //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.177: foo arg //│ ║ ^^^^^^^ -//│ ╟── expression of type `#B & ({fB: int} & #A | {fB: int} & ~?a)` does not have field 'fA' +//│ ╟── expression of type `#B & ({fB: int} & #A | {fB: int} & ~#B | {fB: int} & ~?a)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.171: | A -> x.fA //│ ║ ^^^^ @@ -190,7 +190,7 @@ foo arg //│ res: error //│ constrain calls : 102 //│ annoying calls : 131 -//│ subtyping calls : 4011 +//│ subtyping calls : 5312 :stats :e @@ -198,7 +198,7 @@ foo (arg with { x = 1} with { y = 2 }) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.197: foo (arg with { x = 1} with { y = 2 }) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── expression of type `#B & ({fB: int, x: 1, y: 2} & #A | {fB: int, x: 1, y: 2} & ~?a)` does not have field 'fA' +//│ ╟── expression of type `#B & ({fB: int, x: 1, y: 2} & #A | {fB: int, x: 1, y: 2} & ~#B | {fB: int, x: 1, y: 2} & ~?a)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.171: | A -> x.fA //│ ║ ^^^^ @@ -208,7 +208,7 @@ foo (arg with { x = 1} with { y = 2 }) //│ res: error //│ constrain calls : 77 //│ annoying calls : 128 -//│ subtyping calls : 3370 +//│ subtyping calls : 4801 :stats :e @@ -216,7 +216,7 @@ foo (arg with { x = 1; y = 2; z = 3 }) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.215: foo (arg with { x = 1; y = 2; z = 3 }) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── expression of type `#B & ({fB: int, x: 1, y: 2, z: 3} & #A | {fB: int, x: 1, y: 2, z: 3} & ~?a)` does not have field 'fA' +//│ ╟── expression of type `#B & ({fB: int, x: 1, y: 2, z: 3} & #A | {fB: int, x: 1, y: 2, z: 3} & ~#B | {fB: int, x: 1, y: 2, z: 3} & ~?a)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.171: | A -> x.fA //│ ║ ^^^^ @@ -226,7 +226,7 @@ foo (arg with { x = 1; y = 2; z = 3 }) //│ res: error //│ constrain calls : 77 //│ annoying calls : 128 -//│ subtyping calls : 3358 +//│ subtyping calls : 4801 // ====== 5 ====== // @@ -247,7 +247,7 @@ foo arg //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.244: foo arg //│ ║ ^^^^^^^ -//│ ╟── expression of type `#B & ({fB: int} & #A | {fB: int} & ~?a)` does not have field 'fA' +//│ ╟── expression of type `#B & ({fB: int} & #A | {fB: int} & ~#B | {fB: int} & ~?a)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.237: | A -> x.fA //│ ║ ^^^^ @@ -257,7 +257,7 @@ foo arg //│ res: error //│ constrain calls : 106 //│ annoying calls : 131 -//│ subtyping calls : 4478 +//│ subtyping calls : 5565 // ====== 6 ====== // @@ -279,7 +279,7 @@ foo arg //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.276: foo arg //│ ║ ^^^^^^^ -//│ ╟── expression of type `#B & ({fB: int} & #A | {fB: int} & ~?a)` does not have field 'fA' +//│ ╟── expression of type `#B & ({fB: int} & #A | {fB: int} & ~#B | {fB: int} & ~?a)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.268: | A -> x.fA //│ ║ ^^^^ @@ -289,7 +289,7 @@ foo arg //│ res: error //│ constrain calls : 110 //│ annoying calls : 131 -//│ subtyping calls : 5088 +//│ subtyping calls : 5903 // ====== 7 ====== // @@ -312,7 +312,7 @@ foo arg //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.309: foo arg //│ ║ ^^^^^^^ -//│ ╟── expression of type `#B & ({fB: int} & #A | {fB: int} & ~?a)` does not have field 'fA' +//│ ╟── expression of type `#B & ({fB: int} & #A | {fB: int} & ~#B | {fB: int} & ~?a)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.300: | A -> x.fA //│ ║ ^^^^ @@ -322,7 +322,7 @@ foo arg //│ res: error //│ constrain calls : 114 //│ annoying calls : 131 -//│ subtyping calls : 5862 +//│ subtyping calls : 6283 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 @@ -345,7 +345,7 @@ foo_manual arg //│ res: error //│ constrain calls : 24 //│ annoying calls : 67 -//│ subtyping calls : 5262 +//│ subtyping calls : 3627 :stats foo_manual = foo @@ -354,7 +354,7 @@ foo_manual = foo //│ ({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 //│ constrain calls : 98 //│ annoying calls : 183 -//│ subtyping calls : 6065 +//│ subtyping calls : 3588 // ====== 8 ====== // @@ -378,7 +378,7 @@ foo arg //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.375: foo arg //│ ║ ^^^^^^^ -//│ ╟── expression of type `#B & ({fB: int} & #A | {fB: int} & ~?a)` does not have field 'fA' +//│ ╟── expression of type `#B & ({fB: int} & #A | {fB: int} & ~#B | {fB: int} & ~?a)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.365: | A -> x.fA //│ ║ ^^^^ @@ -388,6 +388,6 @@ foo arg //│ res: error //│ constrain calls : 118 //│ annoying calls : 131 -//│ subtyping calls : 6821 +//│ subtyping calls : 6710 diff --git a/shared/src/test/diff/mlscript/StressUgly.mls b/shared/src/test/diff/mlscript/StressUgly.mls index 3093921636..50100359be 100644 --- a/shared/src/test/diff/mlscript/StressUgly.mls +++ b/shared/src/test/diff/mlscript/StressUgly.mls @@ -13,7 +13,7 @@ class Add[E]: { lhs: E; rhs: E } def eval1_ty_ugly: Add['b] | 'a & ~Lit as 'b //│ eval1_ty_ugly: 'b //│ where -//│ 'b := 'a & ~Lit | Add['b] +//│ 'b := Add['b] | 'a & ~Lit //│ = // def eval1_ty: Add['b] @@ -30,7 +30,7 @@ def eval1_ty: Add[int] // ~500 eval1_ty = eval1_ty_ugly //│ 'b //│ where -//│ 'b := 'a & ~Lit | Add['b] +//│ 'b := Add['b] | 'a & ~Lit //│ <: eval1_ty: //│ Add[int] //│ ╔══[ERROR] Type mismatch in def definition: @@ -44,7 +44,7 @@ eval1_ty = eval1_ty_ugly //│ ╙── ^^^ //│ = //│ eval1_ty_ugly is not implemented -//│ constrain calls : 62 -//│ annoying calls : 44 -//│ subtyping calls : 542 +//│ constrain calls : 49 +//│ annoying calls : 42 +//│ subtyping calls : 366 diff --git a/shared/src/test/diff/mlscript/Subsume.mls b/shared/src/test/diff/mlscript/Subsume.mls index 981eb91c36..517dc4eab8 100644 --- a/shared/src/test/diff/mlscript/Subsume.mls +++ b/shared/src/test/diff/mlscript/Subsume.mls @@ -212,7 +212,7 @@ def f a b = if gt a b then a else b //│ = [Function: f3] f 1 -//│ res: (int & 'a) -> ('a | 1) +//│ res: (int & 'a) -> (1 | 'a) //│ = [Function (anonymous)] :e @@ -235,7 +235,7 @@ def f a b = if gt a b then a else b b // mistake! //│ = [Function: f4] f 1 -//│ res: (int & 'a) -> ('a | 1) +//│ res: (int & 'a) -> (1 | 'a) //│ = [Function (anonymous)] :re diff --git a/shared/src/test/diff/mlscript/Test.mls b/shared/src/test/diff/mlscript/Test.mls index 797d50a5f8..beb48975ff 100644 --- a/shared/src/test/diff/mlscript/Test.mls +++ b/shared/src/test/diff/mlscript/Test.mls @@ -16,7 +16,7 @@ def test = fun x -> case x of //│ = [Function: test] test: Person & {age: 'a} | Animal -> 'a | 0 -//│ res: Animal -> 0 | 0 | Person & {age: 0} +//│ res: 0 | Person & {age: 0} | Animal -> 0 //│ = [Function: test] test: (Person & {age: int} | Animal) -> int diff --git a/shared/src/test/diff/mlscript/This.mls b/shared/src/test/diff/mlscript/This.mls index 18753a4bfc..bd2f7ed473 100644 --- a/shared/src/test/diff/mlscript/This.mls +++ b/shared/src/test/diff/mlscript/This.mls @@ -103,7 +103,7 @@ class Base2 class Derived2: Base2 method Foo3 = (this.Foo2,) class DerivedDerived2: Derived2 - method Foo2 = this.Foo3._1 + method Foo2 = this.Foo3.0 //│ Defined class Base2 //│ Declared Base2.Foo2: (Base2 & 'this) -> (Base2 & 'this) //│ Defined class Derived2 @@ -127,7 +127,7 @@ class NewBase //│ Defined NewBase.NewBar: (NewBase & 'this) -> (NewBase & 'this,) class NewDerived: NewBase - method NewQux = this.NewBar._1 + method NewQux = this.NewBar.0 //│ Defined class NewDerived //│ Defined NewDerived.NewQux: (NewDerived & 'this) -> (NewDerived & NewBase & 'this) @@ -155,13 +155,13 @@ NewDerivedDerived.NewQuz :e class NewDerivedDerivedDerived: NewDerivedDerived - method NewQux = this.NewBar._1 + method NewQux = this.NewBar.0 //│ ╔══[ERROR] Overriding method NewDerived.NewQux without explicit declaration is not allowed. -//│ ║ l.158: method NewQux = this.NewBar._1 -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.158: method NewQux = this.NewBar.0 +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: method definition inherited from -//│ ║ l.130: method NewQux = this.NewBar._1 -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.130: method NewQux = this.NewBar.0 +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ //│ Defined class NewDerivedDerivedDerived //│ Defined NewDerivedDerivedDerived.NewQux: (NewDerivedDerivedDerived & 'this) -> (NewDerivedDerivedDerived & NewBase & 'this) diff --git a/shared/src/test/diff/mlscript/Ticks.mls b/shared/src/test/diff/mlscript/Ticks.mls index 8621aa8aae..e39c6cdd0f 100644 --- a/shared/src/test/diff/mlscript/Ticks.mls +++ b/shared/src/test/diff/mlscript/Ticks.mls @@ -267,7 +267,7 @@ trait T : {x': int} //│ Defined trait T //│ Defined T.N': T -> int //│ Defined T.P': T -> int -> int -//│ Defined T.Q: T -> (int, {z': int},) -> int +//│ Defined T.Q: T -> ((int, {z': int},),) -> int //│ // Prelude //│ const T = function () { //│ const tag = Symbol(); diff --git a/shared/src/test/diff/mlscript/Tony.mls b/shared/src/test/diff/mlscript/Tony.mls index 37d2ced2e4..451f47f8f8 100644 --- a/shared/src/test/diff/mlscript/Tony.mls +++ b/shared/src/test/diff/mlscript/Tony.mls @@ -17,11 +17,11 @@ arg = if true then Some{value = 42} with {payload = 23} else None {} // > TODO don't distribute neg inters + handle better at constraint top level :stats flatMap3 (fun x -> add x.value x.payload) arg -//│ res: int | None +//│ res: None | int //│ = 65 //│ constrain calls : 94 //│ annoying calls : 23 -//│ subtyping calls : 493 +//│ subtyping calls : 458 arg = if true then Some{value = 42} else None {} diff --git a/shared/src/test/diff/mlscript/Traits.mls b/shared/src/test/diff/mlscript/Traits.mls index f3b5ece1a9..05d2a3b02f 100644 --- a/shared/src/test/diff/mlscript/Traits.mls +++ b/shared/src/test/diff/mlscript/Traits.mls @@ -36,7 +36,7 @@ D error: A & B //│ res: A & B //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown :e dv = D{} diff --git a/shared/src/test/diff/mlscript/Trans.mls b/shared/src/test/diff/mlscript/Trans.mls index 1169f29c68..22d552d8fa 100644 --- a/shared/src/test/diff/mlscript/Trans.mls +++ b/shared/src/test/diff/mlscript/Trans.mls @@ -34,7 +34,7 @@ a: B //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.33: a: B //│ ║ ^ -//│ ╟── type `A` is not an instance of `B` +//│ ╟── type `A` is not an instance of type `B` //│ ║ l.24: c: A //│ ║ ^ //│ ╟── but it flows into reference with expected type `B` @@ -51,7 +51,7 @@ a: C //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.50: a: C //│ ║ ^ -//│ ╟── type `A` is not an instance of `C` +//│ ╟── type `A` is not an instance of type `C` //│ ║ l.24: c: A //│ ║ ^ //│ ╟── but it flows into reference with expected type `C` diff --git a/shared/src/test/diff/mlscript/TrickyExtrusion.mls b/shared/src/test/diff/mlscript/TrickyExtrusion.mls index 125cc2aa0a..e73b958417 100644 --- a/shared/src/test/diff/mlscript/TrickyExtrusion.mls +++ b/shared/src/test/diff/mlscript/TrickyExtrusion.mls @@ -53,44 +53,44 @@ test f = //│ = [Function: test2] :e // * Due to lack of precision -(test id)._1 + 1 +(test id).0 + 1 //│ ╔══[ERROR] Type mismatch in operator application: -//│ ║ l.56: (test id)._1 + 1 -//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.56: (test id).0 + 1 +//│ ║ ^^^^^^^^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` //│ ║ l.4: True = true //│ ║ ^^^^ //│ ╟── but it flows into field selection with expected type `int` -//│ ║ l.56: (test id)._1 + 1 -//│ ╙── ^^^^^^^^^^^^ +//│ ║ l.56: (test id).0 + 1 +//│ ╙── ^^^^^^^^^^^ //│ res: error | int -//│ = NaN +//│ = 1 :e // * Due to lack of precision -not (test id)._2 +not (test id).1 //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.70: not (test id)._2 -//│ ║ ^^^^^^^^^^^^^^^^ +//│ ║ l.70: not (test id).1 +//│ ║ ^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `0` is not an instance of type `bool` //│ ║ l.51: in (r 0, r True) //│ ║ ^ //│ ╟── but it flows into field selection with expected type `bool` -//│ ║ l.70: not (test id)._2 -//│ ╙── ^^^^^^^^^^^^ +//│ ║ l.70: not (test id).1 +//│ ╙── ^^^^^^^^^^^ //│ res: bool | error -//│ = true +//│ = false :e // * Legit -not (test id)._1 +not (test id).0 //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.84: not (test id)._1 -//│ ║ ^^^^^^^^^^^^^^^^ +//│ ║ l.84: not (test id).0 +//│ ║ ^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `0` is not an instance of type `bool` //│ ║ l.51: in (r 0, r True) //│ ║ ^ //│ ╟── but it flows into field selection with expected type `bool` -//│ ║ l.84: not (test id)._1 -//│ ╙── ^^^^^^^^^^^^ +//│ ║ l.84: not (test id).0 +//│ ╙── ^^^^^^^^^^^ //│ res: bool | error //│ = true @@ -110,13 +110,13 @@ not (test id)._1 test f = let r x = f x in (r 0, r True) -//│ test: forall 'a 'b 'c 'd 'e 'f 'g. 'd -> ('f, 'b,) +//│ test: forall 'a 'b 'c 'd 'e 'f 'g. 'c -> ('e, 'f,) //│ where -//│ 'd <: 'e -> 'g & 'c -> 'a +//│ 'c <: 'g -> 'a & 'b -> 'd +//│ 'd <: 'e +//│ 'b :> 0 //│ 'a <: 'f -//│ 'c :> 0 -//│ 'g <: 'b -//│ 'e :> true +//│ 'g :> true //│ = [Function: test3] // * Q: why does this type *appear* approximated after simplification? @@ -126,25 +126,25 @@ test // * We can tell the type is still precise enough because these work: -(test id)._1 + 1 +(test id).0 + 1 //│ res: int -//│ = NaN +//│ = 1 -not (test id)._2 +not (test id).1 //│ res: bool -//│ = true +//│ = false :e // * Legit -not (test id)._1 +not (test id).0 //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.138: not (test id)._1 -//│ ║ ^^^^^^^^^^^^^^^^ +//│ ║ l.138: not (test id).0 +//│ ║ ^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `0` is not an instance of type `bool` //│ ║ l.112: in (r 0, r True) //│ ║ ^ //│ ╟── but it flows into field selection with expected type `bool` -//│ ║ l.138: not (test id)._1 -//│ ╙── ^^^^^^^^^^^^ +//│ ║ l.138: not (test id).0 +//│ ╙── ^^^^^^^^^^^ //│ res: bool | error //│ = true diff --git a/shared/src/test/diff/mlscript/Trio.mls b/shared/src/test/diff/mlscript/Trio.mls index b0700ea276..5cc3155c84 100644 --- a/shared/src/test/diff/mlscript/Trio.mls +++ b/shared/src/test/diff/mlscript/Trio.mls @@ -25,7 +25,7 @@ foo b //│ = 'test' //│ constrain calls : 24 //│ annoying calls : 10 -//│ subtyping calls : 186 +//│ subtyping calls : 168 def arg: A | B | C @@ -39,7 +39,7 @@ foo arg //│ arg is not implemented //│ constrain calls : 40 //│ annoying calls : 30 -//│ subtyping calls : 414 +//│ subtyping calls : 369 :stats foo (arg with { fC = true }) @@ -48,14 +48,14 @@ foo (arg with { fC = true }) //│ arg is not implemented //│ constrain calls : 33 //│ annoying calls : 28 -//│ subtyping calls : 331 +//│ subtyping calls : 302 def foo x = case x of { | A -> add x.fA x.payload | B -> x.fB | C -> { l = x.fC; r = x.payload } } -//│ foo: (A & {payload: int} | (B with {fB: 'fB}) | (C with {fC: 'fC, payload: 'payload})) -> ('fB | int | {l: 'fC, r: 'payload}) +//│ foo: (A & {payload: int} | (B with {fB: 'fB}) | (C with {fC: 'fC, payload: 'payload})) -> (int | 'fB | {l: 'fC, r: 'payload}) //│ = [Function: foo1] :e @@ -81,7 +81,7 @@ foo arg //│ arg is not implemented //│ constrain calls : 53 //│ annoying calls : 40 -//│ subtyping calls : 513 +//│ subtyping calls : 468 :stats foo (arg with { payload = 1 }) @@ -90,5 +90,5 @@ foo (arg with { payload = 1 }) //│ arg is not implemented //│ constrain calls : 47 //│ annoying calls : 38 -//│ subtyping calls : 411 +//│ subtyping calls : 376 diff --git a/shared/src/test/diff/mlscript/TupleArray.mls b/shared/src/test/diff/mlscript/TupleArray.mls index 800dd8fa5f..fa0b2dc4aa 100644 --- a/shared/src/test/diff/mlscript/TupleArray.mls +++ b/shared/src/test/diff/mlscript/TupleArray.mls @@ -49,43 +49,43 @@ r = {b = "a", c = 1} //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ = { b: 'a', c: 1 } -(1,2,3) with { _4 = "oops" } -//│ res: (1, 2, 3,) & {_4: "oops"} -//│ = [ 1, 2, 3, _4: 'oops' ] +(1,2,3) with { 3 = "oops" } +//│ res: (1, 2, 3,) & {3: "oops"} +//│ = [ 1, 2, 3, 'oops' ] -(1,2,3) with { _1 = "oops" } -//│ res: {_1: "oops", _2: 2, _3: 3} -//│ = [ 1, 2, 3, _1: 'oops' ] +(1,2,3) with { 0 = "oops" } +//│ res: {0: "oops", 1: 2, 2: 3} +//│ = [ 'oops', 2, 3 ] :e -(res: (int,int,int,))._1 +(res: (int,int,int,)).0 //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.61: (res: (int,int,int,))._1 +//│ ║ l.61: (res: (int,int,int,)).0 //│ ║ ^^^ -//│ ╟── `with` extension of type `{_1: "oops", _2: 2, _3: 3}` is not a 3-element tuple -//│ ║ l.56: (1,2,3) with { _1 = "oops" } -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── `with` extension of type `{0: "oops", 1: 2, 2: 3}` is not a 3-element tuple +//│ ║ l.56: (1,2,3) with { 0 = "oops" } +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `(int, int, int,)` -//│ ║ l.61: (res: (int,int,int,))._1 +//│ ║ l.61: (res: (int,int,int,)).0 //│ ║ ^^^ //│ ╟── Note: constraint arises from tuple type: -//│ ║ l.61: (res: (int,int,int,))._1 +//│ ║ l.61: (res: (int,int,int,)).0 //│ ╙── ^^^^^^^^^^^^^^ //│ res: int //│ = 'oops' -def r: int \ _1 -//│ r: int\_1 +def r: int \ 0 +//│ r: int\0 //│ = -def r: (1,2,3) \ _1 -//│ r: in (1, 2, 3,)\_1 out {_2: 2, _3: 3} +def r: (1,2,3) \ 0 +//│ r: in (1, 2, 3,)\0 out {1: 2, 2: 3} //│ = -// (1,2,3).toRecord \ _1 +// (1,2,3).toRecord \ 1 -def r: (1,2,3) \ _12345 -//│ r: in (1, 2, 3,)\_12345 out (1, 2, 3,) +def r: (1,2,3) \ 12345 +//│ r: in (1, 2, 3,)\12345 out (1, 2, 3,) //│ = def arr: Array[int] @@ -107,18 +107,18 @@ fr : Array[int] & {b: int} //│ fr and r are not implemented :e -arr._1 +arr.0 //│ ╔══[ERROR] Type mismatch in field selection: -//│ ║ l.110: arr._1 -//│ ║ ^^^^^^ -//│ ╟── type `Array[int]` does not have field '_1' +//│ ║ l.110: arr.0 +//│ ║ ^^^^^ +//│ ╟── type `Array[int]` does not have field '0' //│ ║ l.91: def arr: Array[int] //│ ║ ^^^^^^^^^^ -//│ ╟── but it flows into reference with expected type `{_1: ?a}` -//│ ║ l.110: arr._1 +//│ ╟── but it flows into reference with expected type `{0: ?a}` +//│ ║ l.110: arr.0 //│ ╙── ^^^ //│ res: error -//│ = undefined +//│ = 1 rr = arr with { x = 1 } //│ rr: Array[int] & {x: 1} @@ -138,13 +138,13 @@ t = (1, 2, 3) with {x = 1} //│ t: (1, 2, 3,) & {x: 1} //│ = [ 1, 2, 3, x: 1 ] -t._1 -t._2 +t.0 +t.1 t.x //│ res: 1 -//│ = undefined +//│ = 1 //│ res: 2 -//│ = undefined +//│ = 2 //│ res: 1 //│ = 1 @@ -211,14 +211,14 @@ def myval: A & { x: anything } // //│ myval: A with {x: string} -def tuuu: (1 | 2, true) & {_1: 2 | 3} +def tuuu: (1 | 2, true) & {0: 2 | 3} //│ tuuu: (2, true,) //│ = // tuuu: ((1 | 2) & (2 | 3), true,) // tuuu: (2, true,) // (S, T, U) -// Array[S | T | U] & { _1: S; _2: T; _3: U } +// Array[S | T | U] & { 1: S; 2: T; 3: U } def f(x: int, y: string) = x //│ f: (int, string,) -> int @@ -282,43 +282,43 @@ res: { x: int } //│ res: (1, 2, (true, false, ("hello", "world", "bye",),),) //│ = [ 1, 2, [ true, false, [ 'hello', 'world', 'bye' ] ] ] -k1 = (6, "hi", false) with {_5=5; _6=true} -k1._1 -k1._3 -//│ k1: (6, "hi", false,) & {_5: 5, _6: true} -//│ = [ 6, 'hi', false, _5: 5, _6: true ] +k1 = (6, "hi", false) with {4=5; 5=true} +k1.0 +k1.2 +//│ k1: (6, "hi", false,) & {4: 5, 5: true} +//│ = [ 6, 'hi', false, <1 empty item>, 5, true ] //│ res: 6 -//│ = undefined +//│ = 6 //│ res: false -//│ = undefined +//│ = false -(1,2,3) with {_2 = "hello"; _a = true; _4 = 4} -//│ res: {_1: 1, _2: "hello", _3: 3, _4: 4, _a: true} -//│ = [ 1, 2, 3, _2: 'hello', _a: true, _4: 4 ] +(1,2,3) with {1 = "hello"; _a = true; 3 = 4} +//│ res: {0: 1, 1: "hello", 2: 3, 3: 4, _a: true} +//│ = [ 1, 'hello', 3, 4, _a: true ] -(1,2,3) with {_1 = true; _0 = 233} -//│ res: {_0: 233, _1: true, _2: 2, _3: 3} -//│ = [ 1, 2, 3, _1: true, _0: 233 ] +(1,2,3) with {1 = true; 0 = 233} +//│ res: {0: 233, 1: true, 2: 3} +//│ = [ 233, true, 3 ] -(1, 2, true) with {_0 = "hello"} -//│ res: (1, 2, true,) & {_0: "hello"} -//│ = [ 1, 2, true, _0: 'hello' ] +(1, 2, true) with {0 = "hello"} +//│ res: {0: "hello", 1: 2, 2: true} +//│ = [ 'hello', 2, true ] ta1 = (5, 6, true, false, "hahaha") -ta2 = ta1 with {x = 123; _7 = "bye"; _1 = 0} -ta1._1 -ta2._2 -ta2._3 +ta2 = ta1 with {x = 123; 7 = "bye"; 0 = 0} +ta1.0 +ta2.1 +ta2.2 //│ ta1: (5, 6, true, false, "hahaha",) //│ = [ 5, 6, true, false, 'hahaha' ] -//│ ta2: {_1: 0, _2: 6, _3: true, _4: false, _5: "hahaha", _7: "bye", x: 123} -//│ = [ 5, 6, true, false, 'hahaha', x: 123, _7: 'bye', _1: 0 ] +//│ ta2: {0: 0, 1: 6, 2: true, 3: false, 4: "hahaha", 7: "bye", x: 123} +//│ = [ 0, 6, true, false, 'hahaha', <2 empty items>, 'bye', x: 123 ] //│ res: 5 -//│ = undefined +//│ = 5 //│ res: 6 -//│ = undefined +//│ = 6 //│ res: true -//│ = undefined +//│ = true def rep5: 'a -> Array['a] def rep5 x = (x,x,x,x,x) @@ -329,69 +329,69 @@ def rep5 x = (x,x,x,x,x) //│ 'a -> Array['a] //│ = [Function: rep5] -rep5 1 with {_1 = 10} -a2 = rep5 2 with {_2 = true; x = "haha"} -a2._2 +rep5 1 with {0 = 10} +a2 = rep5 2 with {1 = true; x = "haha"} +a2.1 a2.x -//│ res: Array[1] & {_1: 10} -//│ = [ 1, 1, 1, 1, 1, _1: 10 ] -//│ a2: Array[2] & {_2: true, x: "haha"} -//│ = [ 2, 2, 2, 2, 2, _2: true, x: 'haha' ] +//│ res: Array[1] & {0: 10} +//│ = [ 10, 1, 1, 1, 1 ] +//│ a2: Array[2] & {1: true, x: "haha"} +//│ = [ 2, true, 2, 2, 2, x: 'haha' ] //│ res: true //│ = true //│ res: "haha" //│ = 'haha' :e -a2._1 +a2.0 //│ ╔══[ERROR] Type mismatch in field selection: -//│ ║ l.346: a2._1 -//│ ║ ^^^^^ -//│ ╟── type `Array['a]` does not match type `{_1: ?a} | ~{_2: true, x: "haha"}` +//│ ║ l.346: a2.0 +//│ ║ ^^^^ +//│ ╟── type `Array['a]` does not match type `{0: ?a} | ~{1: true, x: "haha"}` //│ ║ l.323: def rep5: 'a -> Array['a] //│ ║ ^^^^^^^^^ -//│ ╟── but it flows into reference with expected type `{_1: ?a} | ~{_2: true, x: "haha"}` -//│ ║ l.346: a2._1 +//│ ╟── but it flows into reference with expected type `{0: ?a} | ~{1: true, x: "haha"}` +//│ ║ l.346: a2.0 //│ ╙── ^^ //│ res: error -//│ = undefined +//│ = 2 (1,2,3,true) with {_a = 1; _b1 = false} //│ res: (1, 2, 3, true,) & {_a: 1, _b1: false} //│ = [ 1, 2, 3, true, _a: 1, _b1: false ] -ht1 = (1,2,false) with {_1 = 'a'; _2 = 'hello'; _3 = false} -ht1._1 -//│ ht1: {_1: "a", _2: "hello", _3: false} -//│ = [ 1, 2, false, _1: 'a', _2: 'hello', _3: false ] +ht1 = (1,2,false) with {0 = 'a'; 1 = 'hello'; 2 = false} +ht1.0 +//│ ht1: {0: "a", 1: "hello", 2: false} +//│ = [ 'a', 'hello', false ] //│ res: "a" //│ = 'a' -def hg1 t = (t._1, t._2) +def hg1 t = (t.0, t.1) hg1 ht1 hg1 ((5,5,5)) -(hg1 ht1)._2 -//│ hg1: {_1: 'a, _2: 'b} -> ('a, 'b,) +(hg1 ht1).1 +//│ hg1: {0: 'a, 1: 'b} -> ('a, 'b,) //│ = [Function: hg1] //│ res: ("a", "hello",) //│ = [ 'a', 'hello' ] //│ res: (5, 5,) -//│ = [ undefined, undefined ] +//│ = [ 5, 5 ] //│ res: "hello" -//│ = undefined +//│ = 'hello' def ta1: Array[int] | (int, bool) -def test: (string, 1) & { _1: "hello" } -def test2: (string, 1) & { _1: "hello"; _3: int } +def test: (string, 1) & { 0: "hello" } +def test2: (string, 1) & { 0: "hello"; 2: int } //│ ta1: Array[bool | int] //│ = //│ test: ("hello", 1,) //│ = -//│ test2: ("hello", 1,) & {_3: int} +//│ test2: ("hello", 1,) & {2: int} //│ = -test: { _1: 'a } -//│ res: {_1: "hello"} +test: { 0: 'a } +//│ res: {0: "hello"} //│ = //│ test is not implemented @@ -403,9 +403,9 @@ test: ('a, 1) //│ = //│ test is not implemented -// TODO in principe, we could narrow the refinement to ` & { _1: 1 }` here... -def test3: Array[1] & { _1: int } -//│ test3: Array[1] & {_1: int} +// TODO in principe, we could narrow the refinement to ` & { 1: 1 }` here... +def test3: Array[1] & { 0: int } +//│ test3: Array[1] & {0: int} //│ = def fta1: Array[int | bool] -> int @@ -420,7 +420,7 @@ fta1 r1 //│ res: int //│ = //│ fta1 is not implemented -//│ r1: Array[1 | 2 | 3] & {_1: 1, _2: 2} +//│ r1: Array[1 | 2 | 3] & {0: 1, 1: 2} //│ = [ 1, 2, 3 ] //│ res: int //│ = @@ -429,9 +429,9 @@ fta1 r1 :NoJS def p1: T | Array[bool] | (int, string) | (true, 3) def p2: T | (string, bool) | Array[int] | (2, 4) -def pf t = (t[1], t._1) +def pf t = (t[1], t.0) pf((1,2,3)) //│ p1: Array[bool | int | string] | T //│ p2: Array[bool | int | string] | T -//│ pf: (Array['a & ~undefined] & {_1: 'b}) -> (undefined | 'a, 'b,) +//│ pf: (Array['a & ~undefined] & {0: 'b}) -> (undefined | 'a, 'b,) //│ res: (1 | 2 | 3 | undefined, 1,) diff --git a/shared/src/test/diff/mlscript/TupleArray2.mls b/shared/src/test/diff/mlscript/TupleArray2.mls index 490beced17..7eee0690b0 100644 --- a/shared/src/test/diff/mlscript/TupleArray2.mls +++ b/shared/src/test/diff/mlscript/TupleArray2.mls @@ -75,7 +75,7 @@ trait T error: (1,2) & t | Array[3] //│ res: (1, 2,) & #T | Array[3] //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown def arr: Array[1] @@ -154,7 +154,7 @@ f_2 = f_1 -def test: (string, 1) & { _1: "hello" } +def test: (string, 1) & { 0: "hello" } //│ test: ("hello", 1,) //│ = @@ -170,8 +170,8 @@ test = ("hi", 1) //│ ║ l.162: test = ("hi", 1) //│ ║ ^^^^ //│ ╟── Note: constraint arises from literal type: -//│ ║ l.157: def test: (string, 1) & { _1: "hello" } -//│ ╙── ^^^^^^^ +//│ ║ l.157: def test: (string, 1) & { 0: "hello" } +//│ ╙── ^^^^^^^ //│ = [ 'hi', 1 ] test = ("hello", 1) @@ -184,8 +184,8 @@ test = ("hello", 1) //│ res: string //│ = 'hello' -test: { _1: 'a } -//│ res: {_1: "hello"} +test: { 0: 'a } +//│ res: {0: "hello"} //│ = [ 'hello', 1 ] test: ('a, 1) @@ -200,7 +200,7 @@ class A: (1,2) :re -error: Array[1] & { _1: int } -//│ res: Array[1] & {_1: int} +error: Array[1] & { 0: int } +//│ res: Array[1] & {0: int} //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown diff --git a/shared/src/test/diff/mlscript/TypeClasses.mls b/shared/src/test/diff/mlscript/TypeClasses.mls index 3af6f10cf3..25bbd0c11e 100644 --- a/shared/src/test/diff/mlscript/TypeClasses.mls +++ b/shared/src/test/diff/mlscript/TypeClasses.mls @@ -16,15 +16,15 @@ def IntMonoid = IntMonoid {} //│ IntMonoid: IntMonoid //│ = [Function: IntMonoid1] -class Num: { val: int } -//│ Defined class Num +class Numb: { val: int } +//│ Defined class Numb -class NumMonoid: Monoid[Num] - method Empty = Num { val = 1 } - method Add this that = Num { val = this.val * that.val } +class NumMonoid: Monoid[Numb] + method Empty = Numb { val = 1 } + method Add this that = Numb { val = this.val * that.val } //│ Defined class NumMonoid -//│ Defined NumMonoid.Empty: NumMonoid -> (Num & {val: 1}) -//│ Defined NumMonoid.Add: NumMonoid -> {val: int} -> {val: int} -> Num +//│ Defined NumMonoid.Empty: NumMonoid -> (Numb & {val: 1}) +//│ Defined NumMonoid.Add: NumMonoid -> {val: int} -> {val: int} -> Numb class Complex[A]: { real: A; imaginary: A } method Map f = Complex { real = f this.real; imaginary = f this.imaginary } @@ -128,7 +128,7 @@ class ComplexMonoid_bad_0[A]: Monoid[Complex[A]] & { base: Monoid[A] } //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.+2: method Empty = Complex { real = this.base } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── function of type `?a -> (forall ?b. ?b)` is not a record (expected a record with fields: real, imaginary) +//│ ╟── function of type `forall ?a ?b. ?a -> ?b` is not a record (expected a record with fields: real, imaginary) //│ ║ l.31: def Complex real imaginary = Complex { real; imaginary } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into application with expected type `{imaginary: A, real: A}` @@ -143,7 +143,22 @@ class ComplexMonoid_bad_0[A]: Monoid[Complex[A]] & { base: Monoid[A] } //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.+2: method Empty = Complex { real = this.base } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── function of type `?a -> (forall ?b. ?b)` does not have field 'Complex#A' +//│ ╟── function of type `forall ?a ?b. ?b -> ?a` is not a record (expected a record with fields: real, imaginary) +//│ ║ l.31: def Complex real imaginary = Complex { real; imaginary } +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── but it flows into application with expected type `{imaginary: A, real: A}` +//│ ║ l.+2: method Empty = Complex { real = this.base } +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from record type: +//│ ║ l.29: class Complex[A]: { real: A; imaginary: A } +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── from inherited method declaration: +//│ ║ l.3: method Empty: A +//│ ╙── ^^^^^^^^ +//│ ╔══[ERROR] Type mismatch in method definition: +//│ ║ l.+2: method Empty = Complex { real = this.base } +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── function of type `forall ?a ?b. ?a -> ?b` does not have field 'Complex#A' //│ ║ l.31: def Complex real imaginary = Complex { real; imaginary } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into application with expected type `{Complex#A <: A}` @@ -187,7 +202,22 @@ class ComplexMonoid_bad_1[A]: Monoid[Complex[A]] & { base: Monoid[A] } //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.+2: method Empty = Complex { real = this.base.Empty; imaginary = this.imaginary.Empty } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── function of type `?a -> (forall ?b. ?b)` is not a record (expected a record with fields: real, imaginary) +//│ ╟── function of type `forall ?a ?b. ?a -> ?b` is not a record (expected a record with fields: real, imaginary) +//│ ║ l.31: def Complex real imaginary = Complex { real; imaginary } +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── but it flows into application with expected type `{imaginary: A, real: A}` +//│ ║ l.+2: method Empty = Complex { real = this.base.Empty; imaginary = this.imaginary.Empty } +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from record type: +//│ ║ l.29: class Complex[A]: { real: A; imaginary: A } +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── from inherited method declaration: +//│ ║ l.3: method Empty: A +//│ ╙── ^^^^^^^^ +//│ ╔══[ERROR] Type mismatch in method definition: +//│ ║ l.+2: method Empty = Complex { real = this.base.Empty; imaginary = this.imaginary.Empty } +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── function of type `forall ?a ?b. ?a -> ?b` is not a record (expected a record with fields: real, imaginary) //│ ║ l.31: def Complex real imaginary = Complex { real; imaginary } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into application with expected type `{imaginary: A, real: A}` @@ -202,7 +232,7 @@ class ComplexMonoid_bad_1[A]: Monoid[Complex[A]] & { base: Monoid[A] } //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.+2: method Empty = Complex { real = this.base.Empty; imaginary = this.imaginary.Empty } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── function of type `?a -> (forall ?b. ?b)` does not have field 'Complex#A' +//│ ╟── function of type `forall ?a ?b. ?a -> ?b` does not have field 'Complex#A' //│ ║ l.31: def Complex real imaginary = Complex { real; imaginary } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into application with expected type `{Complex#A <: A}` @@ -215,7 +245,7 @@ class ComplexMonoid_bad_1[A]: Monoid[Complex[A]] & { base: Monoid[A] } //│ ║ l.3: method Empty: A //│ ╙── ^^^^^^^^ //│ Defined class ComplexMonoid_bad_1[=A] -//│ Defined ComplexMonoid_bad_1.Empty: ComplexMonoid_bad_1['A] -> 'A0 -> (Complex['A0 | {imaginary: error, real: 'A}] with {imaginary: 'A0, real: {imaginary: error, real: 'A}}) +//│ Defined ComplexMonoid_bad_1.Empty: ComplexMonoid_bad_1['A] -> 'A0 -> (Complex['A0 | {imaginary: nothing, real: 'A}] with {imaginary: 'A0, real: {imaginary: nothing, real: 'A}}) //│ Defined ComplexMonoid_bad_1.Add: ComplexMonoid_bad_1['A] -> anything -> anything -> nothing @@ -227,23 +257,23 @@ class ComplexMonoid_bad_2[A]: Monoid[Complex[A]] & { base: Monoid[A] } //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+5: Complex (this.base.Add self.real that.real) (this.base.Add self.imaginary that.imaginary) //│ ║ ^^^^^^^^^^^^^ -//│ ╟── type `Monoid[A]` is not an instance of `ComplexMonoid_bad_2[?A]` +//│ ╟── type `Monoid[A]` is not an instance of type `ComplexMonoid_bad_2` //│ ║ l.+1: class ComplexMonoid_bad_2[A]: Monoid[Complex[A]] & { base: Monoid[A] } //│ ║ ^^^^^^^^^ -//│ ╟── but it flows into field selection with expected type `ComplexMonoid_bad_2[?A]` +//│ ╟── but it flows into field selection with expected type `ComplexMonoid_bad_2[?]` //│ ║ l.+5: Complex (this.base.Add self.real that.real) (this.base.Add self.imaginary that.imaginary) //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+5: Complex (this.base.Add self.real that.real) (this.base.Add self.imaginary that.imaginary) //│ ║ ^^^^^^^^^^^^^ -//│ ╟── type `Monoid[A]` is not an instance of `ComplexMonoid_bad_2[?A]` +//│ ╟── type `Monoid[A]` is not an instance of type `ComplexMonoid_bad_2` //│ ║ l.+1: class ComplexMonoid_bad_2[A]: Monoid[Complex[A]] & { base: Monoid[A] } //│ ║ ^^^^^^^^^ -//│ ╟── but it flows into field selection with expected type `ComplexMonoid_bad_2[?A]` +//│ ╟── but it flows into field selection with expected type `ComplexMonoid_bad_2[?]` //│ ║ l.+5: Complex (this.base.Add self.real that.real) (this.base.Add self.imaginary that.imaginary) //│ ╙── ^^^^^^^^^ //│ Defined class ComplexMonoid_bad_2[=A] //│ Defined ComplexMonoid_bad_2.Add: ComplexMonoid_bad_2['A] -> {imaginary: 'A, real: 'A} -> {imaginary: 'A, real: 'A} -> Complex['A] -//│ Defined ComplexMonoid_bad_2.Add2: ComplexMonoid_bad_2['A] -> Complex[{imaginary: 'A0, real: 'A0}] -> Complex[{imaginary: 'A0, real: 'A0}] -> (Complex[Complex['A0] | error] & {imaginary: Complex['A0] | error, real: Complex['A0] | error}) +//│ Defined ComplexMonoid_bad_2.Add2: ComplexMonoid_bad_2['A] -> Complex[?] -> Complex[?] -> Complex[error] diff --git a/shared/src/test/diff/mlscript/TypeDefs.mls b/shared/src/test/diff/mlscript/TypeDefs.mls index 2518005fb7..0aebcdc3cc 100644 --- a/shared/src/test/diff/mlscript/TypeDefs.mls +++ b/shared/src/test/diff/mlscript/TypeDefs.mls @@ -21,7 +21,7 @@ Test1 { x = "oops" } //│ ╟── Note: constraint arises from type reference: //│ ║ l.2: class Test1: { x: int } //│ ╙── ^^^ -//│ res: error | (Test1 with {x: "oops"}) +//│ res: (Test1 with {x: "oops"}) | error //│ = Test1 { x: 'oops' } def Test1 = fun x -> Test1 { x = x } @@ -137,7 +137,7 @@ type Lol = { x: 'a -> 'a } error: Lol -> Lol //│ res: Lol -> Lol //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown { x = id } : Lol //│ res: Lol @@ -186,7 +186,7 @@ id: Runaway2 error: Runaway2 //│ res: Runaway2 //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown :e :re @@ -202,13 +202,13 @@ error: {x: int} -> nothing : Runaway2 //│ ╙── ^^^ //│ res: Runaway2 //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown :re error: {x: 'x -> 'x} -> nothing : Runaway2 //│ res: Runaway2 //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown diff --git a/shared/src/test/diff/mlscript/TypeRanges.mls b/shared/src/test/diff/mlscript/TypeRanges.mls index 3a1099e9be..67519fa45f 100644 --- a/shared/src/test/diff/mlscript/TypeRanges.mls +++ b/shared/src/test/diff/mlscript/TypeRanges.mls @@ -17,7 +17,7 @@ def set3: MutArray[number..int] -> 'b -> () //│ ╔══[ERROR] Type mismatch in type bounds: //│ ║ l.16: def set3: MutArray[number..int] -> 'b -> () //│ ║ ^^^^^^^^^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.16: def set3: MutArray[number..int] -> 'b -> () //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -39,10 +39,10 @@ fun x -> set2 x 0 // :ns fun x -> set0 x 0 fun x -> set1 x 0 -//│ res: MutArray[in 'b | 0 out 'b] -> () +//│ res: MutArray[in 0 | 'b out 'b] -> () //│ = //│ set0 is not implemented -//│ res: MutArray[in 'b | 0 out 'b] -> () +//│ res: MutArray[in 0 | 'b out 'b] -> () //│ = //│ set1 is not implemented @@ -60,7 +60,7 @@ foo = foo //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.56: foo = foo //│ ║ ^^^^^^^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -80,7 +80,7 @@ foo = (mut 0,) //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.74: foo = (mut 0,) //│ ║ ^^^^^^^^^^^^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── but it flows into mutable tuple field with expected type `int` @@ -99,7 +99,7 @@ set0 foo //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.98: set0 foo //│ ║ ^^^^^^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -108,7 +108,7 @@ set0 foo //│ ╟── from type bounds: //│ ║ l.50: def foo: MutArray[int..number] //│ ╙── ^^^^^^^^^^^ -//│ res: int -> () | error +//│ res: error | int -> () //│ = //│ set0 is not implemented @@ -117,7 +117,7 @@ set2 foo //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.116: set2 foo //│ ║ ^^^^^^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -126,7 +126,7 @@ set2 foo //│ ╟── from type bounds: //│ ║ l.11: def set2: MutArray[int..number] -> int -> () //│ ╙── ^^^^^^^^^^^ -//│ res: int -> () | error +//│ res: error | int -> () //│ = //│ set2 is not implemented @@ -135,7 +135,7 @@ foo : MutArray['a] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.134: foo : MutArray['a] //│ ║ ^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -159,7 +159,7 @@ foo[0] <- 1 //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.158: foo[0] <- 1 //│ ║ ^^^^^^^^^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -205,7 +205,7 @@ foo2 = foo : MutArray['a..'b] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.204: foo2 = foo : MutArray['a..'b] //│ ║ ^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -214,7 +214,7 @@ foo2 = foo : MutArray['a..'b] //│ ╟── from type bounds: //│ ║ l.50: def foo: MutArray[int..number] //│ ╙── ^^^^^^^^^^^ -//│ foo2: MutArray[in int & 'b out 'b | number] +//│ foo2: MutArray[in int & 'b out number | 'b] //│ = [ 1 ] foo2[0] @@ -244,7 +244,7 @@ def foo: MutArray[number..int] //│ ╔══[ERROR] Type mismatch in type bounds: //│ ║ l.243: def foo: MutArray[number..int] //│ ║ ^^^^^^^^^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.243: def foo: MutArray[number..int] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -313,7 +313,7 @@ maf ((mut error,)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.312: maf ((mut error,)) //│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.295: def maf: MutArray[int..number] -> MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── but it flows into mutable tuple field with expected type `int` @@ -325,7 +325,7 @@ maf ((mut error,)) //│ ╟── from type bounds: //│ ║ l.295: def maf: MutArray[int..number] -> MutArray[int..number] //│ ╙── ^^^^^^^^^^^ -//│ res: MutArray[in int out number] | error +//│ res: error | MutArray[in int out number] //│ = //│ maf is not implemented @@ -334,7 +334,7 @@ fun x -> maf ((mut x,)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.333: fun x -> maf ((mut x,)) //│ ║ ^^^^^^^^^^^^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.295: def maf: MutArray[int..number] -> MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── but it flows into mutable tuple field with expected type `int` @@ -346,7 +346,7 @@ fun x -> maf ((mut x,)) //│ ╟── from type bounds: //│ ║ l.295: def maf: MutArray[int..number] -> MutArray[int..number] //│ ╙── ^^^^^^^^^^^ -//│ res: int -> (MutArray[in int out number] | error) +//│ res: int -> (error | MutArray[in int out number]) //│ = //│ maf is not implemented @@ -361,7 +361,7 @@ def maf: MutArray[number..int] -> MutArray[int..number] //│ ╔══[ERROR] Type mismatch in type bounds: //│ ║ l.360: def maf: MutArray[number..int] -> MutArray[int..number] //│ ║ ^^^^^^^^^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.360: def maf: MutArray[number..int] -> MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -403,7 +403,7 @@ foo: R['a] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.402: foo: R['a] //│ ║ ^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.388: def foo: R[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -414,7 +414,7 @@ foo: R['a] //│ ╙── ^^^^^^^^^^^ //│ res: R['a] //│ where -//│ 'a :> error | number +//│ 'a :> number //│ <: int //│ = { get: 0, set: [Function: set] } @@ -423,7 +423,7 @@ foo: R['a..'b] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.422: foo: R['a..'b] //│ ║ ^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.388: def foo: R[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -432,12 +432,12 @@ foo: R['a..'b] //│ ╟── from type bounds: //│ ║ l.388: def foo: R[int..number] //│ ╙── ^^^^^^^^^^^ -//│ res: R[in int & 'b out 'b | error | number] +//│ res: R[in int & 'b out number | 'b] //│ = { get: 0, set: [Function: set] } // * FIXME res.get -//│ res: error | number +//│ res: number //│ = 0 type S[A] = { get: A; set: A -> () } @@ -448,7 +448,7 @@ foo: S['a] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.447: foo: S['a] //│ ║ ^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.388: def foo: R[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -459,7 +459,7 @@ foo: S['a] //│ ╙── ^^^^^^^^^^^ //│ res: S['a] //│ where -//│ 'a :> error | number +//│ 'a :> number //│ <: int //│ = { get: 0, set: [Function: set] } @@ -468,7 +468,7 @@ foo: S['a..'b] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.467: foo: S['a..'b] //│ ║ ^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.388: def foo: R[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -477,7 +477,7 @@ foo: S['a..'b] //│ ╟── from type bounds: //│ ║ l.388: def foo: R[int..number] //│ ╙── ^^^^^^^^^^^ -//│ res: S[in int & 'b out 'b | error | number] +//│ res: S[in int & 'b out number | 'b] //│ = { get: 0, set: [Function: set] } foo: { get: number; set: int -> () } @@ -489,7 +489,7 @@ foo: { get: int } //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.488: foo: { get: int } //│ ║ ^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.388: def foo: R[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -503,7 +503,7 @@ foo: { set: number -> () } //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.502: foo: { set: number -> () } //│ ║ ^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.502: foo: { set: number -> () } //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -524,7 +524,7 @@ foo2: S['a] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.523: foo2: S['a] //│ ║ ^^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.388: def foo: R[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -535,7 +535,7 @@ foo2: S['a] //│ ╙── ^^ //│ res: S['a] //│ where -//│ 'a :> error | number +//│ 'a :> number //│ <: int //│ = { get: 0, set: [Function: set] } @@ -544,7 +544,7 @@ foo2: S['a..'b] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.543: foo2: S['a..'b] //│ ║ ^^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.388: def foo: R[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -553,7 +553,7 @@ foo2: S['a..'b] //│ ╟── from type variable: //│ ║ l.518: foo2 = foo: { get: 'a; set: 'b -> () } //│ ╙── ^^ -//│ res: S[in int & 'b out 'b | error | number] +//│ res: S[in int & 'b out number | 'b] //│ = { get: 0, set: [Function: set] } diff --git a/shared/src/test/diff/mlscript/TypeRefs.mls b/shared/src/test/diff/mlscript/TypeRefs.mls index 2ac2fb5d93..84205afe7f 100644 --- a/shared/src/test/diff/mlscript/TypeRefs.mls +++ b/shared/src/test/diff/mlscript/TypeRefs.mls @@ -26,5 +26,5 @@ foo: Baz['res] foo: Bar['res] //│ res: Bar['res] //│ where -//│ 'res <: string -> string | int +//│ 'res <: int | string -> string diff --git a/shared/src/test/diff/mlscript/TypeTags.mls b/shared/src/test/diff/mlscript/TypeTags.mls index 5ac9e7110f..fdbafec742 100644 --- a/shared/src/test/diff/mlscript/TypeTags.mls +++ b/shared/src/test/diff/mlscript/TypeTags.mls @@ -70,3 +70,12 @@ foo = Foo //│ = [Function: foo1] + +:e +1 : #Eql +//│ ╔══[ERROR] type identifier not found: eql +//│ ╙── +//│ res: error +//│ = 1 + + diff --git a/shared/src/test/diff/mlscript/Undef.mls b/shared/src/test/diff/mlscript/Undef.mls index d1fa8a74c2..598b7e6556 100644 --- a/shared/src/test/diff/mlscript/Undef.mls +++ b/shared/src/test/diff/mlscript/Undef.mls @@ -8,13 +8,13 @@ undef = Undef{} def example: int | Undef -//│ example: int | Undef +//│ example: Undef | int //│ = example = if true then undef else 42 //│ 42 | Undef //│ <: example: -//│ int | Undef +//│ Undef | int //│ = Undef {} def qmrk_qmrk lhs rhs = case lhs of { Undef -> rhs | _ -> lhs } diff --git a/shared/src/test/diff/mlscript/Undefined.mls b/shared/src/test/diff/mlscript/Undefined.mls index eb03cf67d9..24c81f3cad 100644 --- a/shared/src/test/diff/mlscript/Undefined.mls +++ b/shared/src/test/diff/mlscript/Undefined.mls @@ -20,7 +20,7 @@ Undefined2 { x = "I am here to make a type mismatch." } //│ ╟── Note: constraint arises from literal type: //│ ║ l.1: class Undefined2: { x: undefined } //│ ╙── ^^^^^^^^^ -//│ res: error | (Undefined2 with {x: "I am here to make a type mismatch."}) +//│ res: (Undefined2 with {x: "I am here to make a type mismatch."}) | error //│ = Undefined2 { x: 'I am here to make a type mismatch.' } :e diff --git a/shared/src/test/diff/mlscript/VarCycles.mls b/shared/src/test/diff/mlscript/VarCycles.mls index 9c3708f5ce..a809567fc0 100644 --- a/shared/src/test/diff/mlscript/VarCycles.mls +++ b/shared/src/test/diff/mlscript/VarCycles.mls @@ -55,7 +55,7 @@ bar foo //│ ║ l.48: bar foo //│ ║ ^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. -//│ res: () | error +//│ res: error | () //│ = //│ bar is not implemented diff --git a/shared/src/test/diff/mlscript/Variant-sub-ad-hoc.mls b/shared/src/test/diff/mlscript/Variant-sub-ad-hoc.mls index 0e6b87c258..ae6d046d7d 100644 --- a/shared/src/test/diff/mlscript/Variant-sub-ad-hoc.mls +++ b/shared/src/test/diff/mlscript/Variant-sub-ad-hoc.mls @@ -53,7 +53,7 @@ rec def evalOpt = fun x -> case x of { //│ = [Function: evalOpt] //│ constrain calls : 630 //│ annoying calls : 156 -//│ subtyping calls : 4296 +//│ subtyping calls : 3972 :stats evalOpt (Plus{} with {lhs = Lit{} with {v=2}; rhs = Lit{} with {v=2}}) @@ -61,5 +61,5 @@ evalOpt (Plus{} with {lhs = Lit{} with {v=2}; rhs = Lit{} with {v=2}}) //│ = Some { v: 4 } //│ constrain calls : 105 //│ annoying calls : 34 -//│ subtyping calls : 961 +//│ subtyping calls : 754 diff --git a/shared/src/test/diff/mlscript/Variant-sub.mls b/shared/src/test/diff/mlscript/Variant-sub.mls index fa4b353b17..a0bd1306a8 100644 --- a/shared/src/test/diff/mlscript/Variant-sub.mls +++ b/shared/src/test/diff/mlscript/Variant-sub.mls @@ -36,7 +36,7 @@ flatMap f "oops" //│ ╟── Note: constraint arises from reference: //│ ║ l.14: case opt of { Some -> f opt.v | None -> opt } //│ ╙── ^^^ -//│ res: error | Some[nothing] +//│ res: Some[nothing] | error //│ Runtime error: //│ Error: non-exhaustive case expression @@ -80,7 +80,7 @@ rec def evalOpt x = case x of { //│ = [Function: evalOpt] //│ constrain calls : 649 //│ annoying calls : 105 -//│ subtyping calls : 5353 +//│ subtyping calls : 5232 :stats evalOpt (Plus{lhs = Lit{v=2}; rhs = Lit{v=3}}) @@ -88,4 +88,4 @@ evalOpt (Plus{lhs = Lit{v=2}; rhs = Lit{v=3}}) //│ = Some { v: 5 } //│ constrain calls : 153 //│ annoying calls : 37 -//│ subtyping calls : 1224 +//│ subtyping calls : 1097 diff --git a/shared/src/test/diff/mlscript/Weird.mls b/shared/src/test/diff/mlscript/Weird.mls index 6b5d71730b..2b658ed4e3 100644 --- a/shared/src/test/diff/mlscript/Weird.mls +++ b/shared/src/test/diff/mlscript/Weird.mls @@ -1,5 +1,6 @@ +// * Weird features to eventually remove + :AllowRuntimeErrors -// Weird features to eventually remove :ge @@ -40,9 +41,9 @@ 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),Right(PolyType(List(),TypeName(C))),true) //│ Desugared: {} -//│ AST: Rcd() +//│ AST: Rcd(List()) //│ n: C //│ = //│ res: anything diff --git a/shared/src/test/diff/mlscript/Wildcards.mls b/shared/src/test/diff/mlscript/Wildcards.mls index c452f68075..cc33abd143 100644 --- a/shared/src/test/diff/mlscript/Wildcards.mls +++ b/shared/src/test/diff/mlscript/Wildcards.mls @@ -210,7 +210,7 @@ f e //│ ╟── Note: constraint arises from type wildcard: //│ ║ l.196: def f: Expr[?] -> Expr[?] //│ ╙── ^ -//│ res: error | Expr[?] +//│ res: Expr[?] | error @@ -265,7 +265,7 @@ foo.Foo1 //│ ╟── type `anything` does not match type `nothing` //│ ║ l.229: def foo: Foo[?] //│ ╙── ^ -//│ res: Foo['A] -> Foo['A] | error +//│ res: error | Foo['A] -> Foo['A] //│ where //│ 'A :> anything //│ <: nothing @@ -281,7 +281,7 @@ foo1 foo //│ ╟── type `anything` does not match type `nothing` //│ ║ l.229: def foo: Foo[?] //│ ╙── ^ -//│ res: Foo['A] -> Foo['A] | error +//│ res: error | Foo['A] -> Foo['A] //│ where //│ 'A :> anything //│ <: nothing @@ -308,7 +308,7 @@ foo2.Id //│ ╟── Note: class type parameter A is defined at: //│ ║ l.218: class Foo[A] //│ ╙── ^ -//│ res: nothing -> anything | error +//│ res: error | nothing -> anything :e foo3 = foo2 : Foo['a] @@ -334,7 +334,7 @@ foo3.Id //│ ╟── Note: class type parameter A is defined at: //│ ║ l.218: class Foo[A] //│ ╙── ^ -//│ res: nothing -> anything | error +//│ res: error | nothing -> anything :e @@ -409,7 +409,7 @@ bar.Bid //│ ╟── type `anything` does not match type `nothing` //│ ║ l.380: def bar: Bar[?] //│ ╙── ^ -//│ res: nothing -> anything | error +//│ res: error | nothing -> anything :e bar: Bar['a..'b] diff --git a/shared/src/test/diff/mlscript/Yicong.mls b/shared/src/test/diff/mlscript/Yicong.mls index 9080a3a452..8d21237181 100644 --- a/shared/src/test/diff/mlscript/Yicong.mls +++ b/shared/src/test/diff/mlscript/Yicong.mls @@ -79,7 +79,7 @@ if true then x1 else x2 def f: ((1,2) | (3,4)) -> anything -//│ f: (1 | 3, 2 | 4,) -> anything +//│ f: ((1 | 3, 2 | 4,),) -> anything //│ = fun (x, y) -> f ((x,y)) @@ -141,17 +141,19 @@ t = if false then res else hhh g = if true then (6,6,6) else res gwx = g with {x=123} gwx.x with {y = gwx} -//│ res: Array["hello" | 1 | 2 | 3 | 4 | false | true] & {_1: 1 | true, _2: 2 | 4, _3: 3 | false} +//│ res: Array["hello" | 1 | 2 | 3 | 4 | false | true] & {0: 1 | true, 1: 2 | 4, 2: 3 | false} //│ = [ 1, 2, 3, 'hello' ] -//│ hhh: Array["bye" | 345 | 3 | 45 | false | true] & {_1: 45 | false, _2: 345 | 3} +//│ hhh: Array["bye" | 345 | 3 | 45 | false | true] & {0: 45 | false, 1: 345 | 3} //│ = [ false, 3 ] -//│ t: Array["bye" | "hello" | 1 | 2 | 345 | 3 | 45 | 4 | false | true] & {_1: 1 | 45 | false | true, _2: 2 | 345 | 3 | 4} +//│ t: Array["bye" | "hello" | 1 | 2 | 345 | 3 | 45 | 4 | false | true] & {0: 1 | 45 | false | true, 1: 2 | 345 | 3 | 4} //│ = [ false, 3 ] -//│ g: Array["hello" | 1 | 2 | 3 | 4 | 6 | false | true] & {_1: 1 | 6 | true, _2: 2 | 4 | 6, _3: 3 | 6 | false} +//│ g: Array["hello" | 1 | 2 | 3 | 4 | 6 | false | true] & {0: 1 | 6 | true, 1: 2 | 4 | 6, 2: 3 | 6 | false} //│ = [ 6, 6, 6 ] -//│ gwx: Array["hello" | 1 | 2 | 3 | 4 | 6 | false | true] & {_1: 1 | 6 | true, _2: 2 | 4 | 6, _3: 3 | 6 | false, x: 123} +//│ gwx: Array["hello" | 1 | 2 | 3 | 4 | 6 | false | true] & {0: 1 | 6 | true, 1: 2 | 4 | 6, 2: 3 | 6 | false, x: 123} //│ = [ 6, 6, 6, x: 123 ] -//│ res: 123 & {y: Array["hello" | 1 | 2 | 3 | 4 | 6 | false | true] & {_1: 1 | 6 | true, _2: 2 | 4 | 6, _3: 3 | 6 | false, x: 123}} +//│ res: 123 & { +//│ y: Array["hello" | 1 | 2 | 3 | 4 | 6 | false | true] & {0: 1 | 6 | true, 1: 2 | 4 | 6, 2: 3 | 6 | false, x: 123} +//│ } //│ = [Number: 123] { y: [ 6, 6, 6, x: 123 ] } def f: (int, bool) -> int @@ -163,14 +165,14 @@ def g: (bool, string, int) -> int //│ = p1 = if true then (1, 2, 2) else (true, false) -//│ p1: Array[1 | 2 | false | true] & {_1: 1 | true, _2: 2 | false} +//│ p1: Array[1 | 2 | false | true] & {0: 1 | true, 1: 2 | false} //│ = [ 1, 2, 2 ] def q: Array[int] q = if true then (1,1) else (1,1,1) //│ q: Array[int] //│ = -//│ Array[1] & {_1: 1, _2: 1} +//│ Array[1] & {0: 1, 1: 1} //│ <: q: //│ Array[int] //│ = [ 1, 1 ] @@ -183,38 +185,38 @@ def h f = (f (1,2,false), f (1,true)) :e h (fun x -> x[0]) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.184: h (fun x -> x[0]) +//│ ║ l.186: h (fun x -> x[0]) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── argument list of type `(1, true,)` does not match type `(?a,)` -//│ ║ l.179: def h f = (f (1,2,false), f (1,true)) +//│ ║ l.181: def h f = (f (1,2,false), f (1,true)) //│ ╙── ^^^^^^^^ -//│ res: (undefined, undefined,) | error +//│ res: error | (undefined, undefined,) //│ = [ undefined, undefined ] :e h (fun (x, y) -> x) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.195: h (fun (x, y) -> x) +//│ ║ l.197: h (fun (x, y) -> x) //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── argument list of type `(1, 2, false,)` does not match type `(?a, ?b,)` -//│ ║ l.179: def h f = (f (1,2,false), f (1,true)) +//│ ║ l.181: def h f = (f (1,2,false), f (1,true)) //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: -//│ ║ l.195: h (fun (x, y) -> x) +//│ ║ l.197: h (fun (x, y) -> x) //│ ╙── ^^^^^^ -//│ res: (nothing, 1,) | error +//│ res: error | (nothing, 1,) //│ = [ 1, 1 ] def h f = (f ((1,2,false)), f ((1,true))) -//│ h: ((1, 2, false,) -> 'a & (1, true,) -> 'b) -> ('a, 'b,) +//│ h: (((1, 2, false,),) -> 'a & ((1, true,),) -> 'b) -> ('a, 'b,) //│ = [Function: h1] h (fun x -> x[0]) -h (fun x -> x._1) +h (fun x -> x.0) //│ res: (1 | 2 | false | undefined, 1 | true | undefined,) //│ = [ 1, 1 ] //│ res: (1, 1,) -//│ = [ undefined, undefined ] +//│ = [ 1, 1 ] q1 = (1,1,1,1) @@ -224,37 +226,37 @@ fx ((a,b,c)) = a + b + c //│ = [ 1, 1, 1, 1 ] //│ q2: (1, 1,) //│ = [ 1, 1 ] -//│ fx: (int, int, int,) -> int +//│ fx: ((int, int, int,),) -> int //│ = [Function: fx] :e fx q1 fx q2 //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.231: fx q1 +//│ ║ l.233: fx q1 //│ ║ ^^^^^ //│ ╟── tuple literal of type `(1, 1, 1, 1,)` does not match type `(?a, ?b, ?c,)` -//│ ║ l.220: q1 = (1,1,1,1) +//│ ║ l.222: q1 = (1,1,1,1) //│ ║ ^^^^^^^^^ //│ ╟── but it flows into reference with expected type `(?d, ?e, ?f,)` -//│ ║ l.231: fx q1 +//│ ║ l.233: fx q1 //│ ║ ^^ //│ ╟── Note: constraint arises from tuple literal: -//│ ║ l.222: fx ((a,b,c)) = a + b + c +//│ ║ l.224: fx ((a,b,c)) = a + b + c //│ ╙── ^^^^^^^ //│ res: error | int //│ = 3 //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.232: fx q2 +//│ ║ l.234: fx q2 //│ ║ ^^^^^ //│ ╟── tuple literal of type `(1, 1,)` does not match type `(?a, ?b, ?c,)` -//│ ║ l.221: q2 = (1,1) +//│ ║ l.223: q2 = (1,1) //│ ║ ^^^^^ //│ ╟── but it flows into reference with expected type `(?d, ?e, ?f,)` -//│ ║ l.232: fx q2 +//│ ║ l.234: fx q2 //│ ║ ^^ //│ ╟── Note: constraint arises from tuple literal: -//│ ║ l.222: fx ((a,b,c)) = a + b + c +//│ ║ l.224: fx ((a,b,c)) = a + b + c //│ ╙── ^^^^^^^ //│ res: error | int //│ = NaN @@ -284,13 +286,13 @@ sum t2 //│ t2: (1, 1, 2, true,) //│ = [ 1, 1, 2, true ] //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.283: sum t2 +//│ ║ l.285: sum t2 //│ ║ ^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` -//│ ║ l.282: t2 = (1,1,2,true) +//│ ║ l.284: t2 = (1,1,2,true) //│ ║ ^^^^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.270: def sum: Array[int] -> int +//│ ║ l.272: def sum: Array[int] -> int //│ ╙── ^^^ //│ res: error | int //│ = @@ -319,44 +321,44 @@ two = Two {fst=(1,2,3); snd=(true,false)} two.fst tk (two.snd) //│ Defined class Two[+A, +B] -//│ two: Two[1 | 2 | 3, bool] & {fst: (1, 2, 3,), snd: (true, false,)} +//│ two: Two[1 | 2 | 3, Bool] & {fst: (1, 2, 3,), snd: (true, false,)} //│ = Two { fst: [ 1, 2, 3 ], snd: [ true, false ] } //│ res: (1, 2, 3,) //│ = [ 1, 2, 3 ] -//│ res: Wrapped[bool] +//│ res: Wrapped[Bool] //│ = //│ tk is not implemented :e def a1: Array[int] a1 = (1,2,true,'hello') -a1._2 +a1.1 //│ a1: Array[int] //│ = //│ (1, 2, true, "hello",) //│ <: a1: //│ Array[int] //│ ╔══[ERROR] Type mismatch in def definition: -//│ ║ l.332: a1 = (1,2,true,'hello') +//│ ║ l.334: a1 = (1,2,true,'hello') //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` -//│ ║ l.332: a1 = (1,2,true,'hello') +//│ ║ l.334: a1 = (1,2,true,'hello') //│ ║ ^^^^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.331: def a1: Array[int] +//│ ║ l.333: def a1: Array[int] //│ ╙── ^^^ //│ = [ 1, 2, true, 'hello' ] //│ ╔══[ERROR] Type mismatch in field selection: -//│ ║ l.333: a1._2 -//│ ║ ^^^^^ -//│ ╟── type `Array[int]` does not have field '_2' -//│ ║ l.331: def a1: Array[int] +//│ ║ l.335: a1.1 +//│ ║ ^^^^ +//│ ╟── type `Array[int]` does not have field '1' +//│ ║ l.333: def a1: Array[int] //│ ║ ^^^^^^^^^^ -//│ ╟── but it flows into reference with expected type `{_2: ?a}` -//│ ║ l.333: a1._2 +//│ ╟── but it flows into reference with expected type `{1: ?a}` +//│ ║ l.335: a1.1 //│ ╙── ^^ //│ res: error -//│ = undefined +//│ = 2 def getx p = p.x def a123: Array[int] @@ -377,7 +379,7 @@ def append: Array['a] -> Array['b] -> Array['a | 'b] append ((1,2,false)) (((), 'hi')) //│ append: Array['a] -> Array['a] -> Array['a] //│ = -//│ res: Array["hi" | () | 1 | 2 | false] +//│ res: Array["hi" | 1 | 2 | false | ()] //│ = //│ append is not implemented @@ -400,15 +402,15 @@ append2 ((mut 1, mut 2, mut false)) ((mut (), mut 'hi')) :e append2 ((mut 1, mut 2, mut false)) ((mut (), 'hi')) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.401: append2 ((mut 1, mut 2, mut false)) ((mut (), 'hi')) +//│ ║ l.403: append2 ((mut 1, mut 2, mut false)) ((mut (), 'hi')) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── tuple literal of type `(mut ?a, "hi",)` does not match type `MutArray['bb]` -//│ ║ l.401: append2 ((mut 1, mut 2, mut false)) ((mut (), 'hi')) +//│ ║ l.403: append2 ((mut 1, mut 2, mut false)) ((mut (), 'hi')) //│ ║ ^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from applied type reference: -//│ ║ l.384: def append2: MutArray['aa] -> MutArray['bb] -> MutArray['aa | 'bb] +//│ ║ l.386: def append2: MutArray['aa] -> MutArray['bb] -> MutArray['aa | 'bb] //│ ╙── ^^^^^^^^^^^^^ -//│ res: MutArray['aa | 'bb] | error +//│ res: error | MutArray['aa | 'bb] //│ where //│ 'bb :> "hi" | () //│ 'aa :> 'a @@ -483,9 +485,9 @@ tg (T2 ((true,false,false))) def fst ((a, b)) = a def snd ((a, b)) = b def get: Array['a] -> int -> 'a -//│ fst: ('a, anything,) -> 'a +//│ fst: (('a, anything,),) -> 'a //│ = [Function: fst] -//│ snd: (anything, 'a,) -> 'a +//│ snd: ((anything, 'a,),) -> 'a //│ = [Function: snd] //│ get: Array['a] -> int -> 'a //│ = @@ -513,31 +515,31 @@ iarr = intersect ((1,2,3,false)) ((1,5,true,'hi',false)) fst iarr snd (T1 ((1,2,3))) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.513: fst iarr +//│ ║ l.515: fst iarr //│ ║ ^^^^^^^^ //│ ╟── type `Array['a & 'b]` is not a 2-element tuple -//│ ║ l.504: def intersect: Array['a] -> Array['b] -> Array['a & 'b] +//│ ║ l.506: def intersect: Array['a] -> Array['b] -> Array['a & 'b] //│ ║ ^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `(?a, ?b,)` -//│ ║ l.513: fst iarr +//│ ║ l.515: fst iarr //│ ║ ^^^^ //│ ╟── Note: constraint arises from tuple literal: -//│ ║ l.483: def fst ((a, b)) = a +//│ ║ l.485: def fst ((a, b)) = a //│ ╙── ^^^^^^ //│ res: error //│ = //│ iarr and intersect are not implemented //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.514: snd (T1 ((1,2,3))) +//│ ║ l.516: snd (T1 ((1,2,3))) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── tuple literal of type `(1, 2, 3,)` does not match type `(?a, ?b,) | ~#T1` -//│ ║ l.514: snd (T1 ((1,2,3))) +//│ ║ l.516: snd (T1 ((1,2,3))) //│ ║ ^^^^^^^ //│ ╟── but it flows into application with expected type `(?a, ?b,) | ~#T1` -//│ ║ l.514: snd (T1 ((1,2,3))) +//│ ║ l.516: snd (T1 ((1,2,3))) //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: -//│ ║ l.484: def snd ((a, b)) = b +//│ ║ l.486: def snd ((a, b)) = b //│ ╙── ^^^^^^ //│ res: error //│ = 2 @@ -587,30 +589,30 @@ inn2 (T2 r1) inn (T1 v3) inn2 v1 //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.587: inn (T1 v3) +//│ ║ l.589: inn (T1 v3) //│ ║ ^^^^^^^^^^^ //│ ╟── type `Array[Array[(int, true,)]]` does not match type `#T2 | ~#T1` -//│ ║ l.551: def v3: Array[Array[(int, true)]] +//│ ║ l.553: def v3: Array[Array[(int, true)]] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `#T2 | ~#T1` -//│ ║ l.587: inn (T1 v3) +//│ ║ l.589: inn (T1 v3) //│ ║ ^^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.547: def inn: (T1 & T2 & Array['a]) -> 'a +//│ ║ l.549: def inn: (T1 & T2 & Array['a]) -> 'a //│ ║ ^^ //│ ╟── from intersection type: -//│ ║ l.547: def inn: (T1 & T2 & Array['a]) -> 'a +//│ ║ l.549: def inn: (T1 & T2 & Array['a]) -> 'a //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ -//│ res: Array[(int, true,)] | error +//│ res: error | Array[(int, true,)] //│ = //│ inn is not implemented //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.588: inn2 v1 +//│ ║ l.590: inn2 v1 //│ ║ ^^^^^^^ //│ ╟── integer literal of type `1` does not match type `Array['a]` -//│ ║ l.545: v1 = T1 (T2 ((1,2,true))) +//│ ║ l.547: v1 = T1 (T2 ((1,2,true))) //│ ╙── ^ -//│ res: Array[nothing] | error +//│ res: error | Array[nothing] //│ = //│ inn2 is not implemented @@ -642,16 +644,16 @@ ra: (Array['a], Array['a]) as 'a ra: ('a, 'a, 'a) as 'a ra: (Array['a], Array['a], Array['a]) as 'a //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.642: ra: ('a, 'a, 'a) as 'a +//│ ║ l.644: ra: ('a, 'a, 'a) as 'a //│ ║ ^^ //│ ╟── type `('a, 'a,)` does not match type `('a0, 'a0, 'a0,)` -//│ ║ l.617: def ra: ('a, 'a) as 'a +//│ ║ l.619: def ra: ('a, 'a) as 'a //│ ║ ^^^^^^^^ //│ ╟── but it flows into reference with expected type `('a1, 'a1, 'a1,)` -//│ ║ l.642: ra: ('a, 'a, 'a) as 'a +//│ ║ l.644: ra: ('a, 'a, 'a) as 'a //│ ║ ^^ //│ ╟── Note: constraint arises from tuple type: -//│ ║ l.642: ra: ('a, 'a, 'a) as 'a +//│ ║ l.644: ra: ('a, 'a, 'a) as 'a //│ ╙── ^^^^^^^^^^^^ //│ res: 'a //│ where @@ -659,16 +661,16 @@ ra: (Array['a], Array['a], Array['a]) as 'a //│ = //│ ra is not implemented //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.643: ra: (Array['a], Array['a], Array['a]) as 'a +//│ ║ l.645: ra: (Array['a], Array['a], Array['a]) as 'a //│ ║ ^^ //│ ╟── type `('a, 'a,)` does not match type `(Array['a0], Array['a0], Array['a0],)` -//│ ║ l.617: def ra: ('a, 'a) as 'a +//│ ║ l.619: def ra: ('a, 'a) as 'a //│ ║ ^^^^^^^^ //│ ╟── but it flows into reference with expected type `(Array['a1], Array['a1], Array['a1],)` -//│ ║ l.643: ra: (Array['a], Array['a], Array['a]) as 'a +//│ ║ l.645: ra: (Array['a], Array['a], Array['a]) as 'a //│ ║ ^^ //│ ╟── Note: constraint arises from tuple type: -//│ ║ l.643: ra: (Array['a], Array['a], Array['a]) as 'a +//│ ║ l.645: ra: (Array['a], Array['a], Array['a]) as 'a //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: 'a //│ where @@ -676,32 +678,32 @@ ra: (Array['a], Array['a], Array['a]) as 'a //│ = //│ ra is not implemented -def tktup t = (t._2, t._3) +def tktup t = (t.1, t.2) tktup ((1,2,3,true)) -//│ tktup: {_2: 'a, _3: 'b} -> ('a, 'b,) +//│ tktup: {1: 'a, 2: 'b} -> ('a, 'b,) //│ = [Function: tktup] //│ res: (2, 3,) -//│ = [ undefined, undefined ] +//│ = [ 2, 3 ] :e tktup a123 //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.687: tktup a123 +//│ ║ l.689: tktup a123 //│ ║ ^^^^^^^^^^ -//│ ╟── type `Array[int]` does not have field '_3' -//│ ║ l.362: def a123: Array[int] +//│ ╟── type `Array[int]` does not have field '2' +//│ ║ l.364: def a123: Array[int] //│ ║ ^^^^^^^^^^ -//│ ╟── but it flows into reference with expected type `{_3: ?a}` -//│ ║ l.687: tktup a123 +//│ ╟── but it flows into reference with expected type `{2: ?a}` +//│ ║ l.689: tktup a123 //│ ║ ^^^^ //│ ╟── Note: constraint arises from field selection: -//│ ║ l.679: def tktup t = (t._2, t._3) -//│ ║ ^^^^ +//│ ║ l.681: def tktup t = (t.1, t.2) +//│ ║ ^^^ //│ ╟── from reference: -//│ ║ l.679: def tktup t = (t._2, t._3) -//│ ╙── ^ -//│ res: (nothing, nothing,) | error -//│ = [ undefined, undefined ] +//│ ║ l.681: def tktup t = (t.1, t.2) +//│ ╙── ^ +//│ res: error | (nothing, nothing,) +//│ = [ 2, 3 ] def definedOr x els = case x of { @@ -721,7 +723,7 @@ def ornull x = definedOr x (fun () -> null) defined ()[0] //│ res: nothing //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown ta1 = (2,3,4,5) @@ -741,7 +743,7 @@ ta3[1-2] //│ = null //│ ta3: ((1, 2, 3,), "hello", false,) //│ = [ [ 1, 2, 3 ], 'hello', false ] -//│ res: "hello" | (1, 2, 3,) | false | undefined +//│ res: "hello" | false | undefined | (1, 2, 3,) //│ = undefined //│ res: 1 | 2 | 3 | undefined //│ = undefined @@ -749,10 +751,10 @@ ta3[1-2] :e ((defined ta2[0])[1+4], ta[1])[0] //│ ╔══[ERROR] Type mismatch in array access: -//│ ║ l.750: ((defined ta2[0])[1+4], ta[1])[0] +//│ ║ l.752: ((defined ta2[0])[1+4], ta[1])[0] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── possibly-undefined array access of type `undefined` does not match type `~undefined` -//│ ║ l.750: ((defined ta2[0])[1+4], ta[1])[0] +//│ ║ l.752: ((defined ta2[0])[1+4], ta[1])[0] //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ res: error | false | int | true | undefined //│ = undefined @@ -766,53 +768,53 @@ ge[2] //│ ge: int -> int //│ = [Function: ge] //│ ╔══[ERROR] Type mismatch in array access: -//│ ║ l.762: ta3[ge 3][ge (1+2)] +//│ ║ l.764: ta3[ge 3][ge (1+2)] //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── string literal of type `"hello"` does not match type `Array[?a]` -//│ ║ l.731: ta3 = ((1,2,3), "hello", false) +//│ ║ l.733: ta3 = ((1,2,3), "hello", false) //│ ║ ^^^^^^^ //│ ╟── but it flows into array access with expected type `Array[?b]` -//│ ║ l.762: ta3[ge 3][ge (1+2)] +//│ ║ l.764: ta3[ge 3][ge (1+2)] //│ ╙── ^^^^^^^^^ //│ res: 1 | 2 | 3 | error | undefined //│ Runtime error: //│ TypeError: Cannot read properties of undefined (reading '4') //│ ╔══[ERROR] Type mismatch in array access: -//│ ║ l.763: true[false] +//│ ║ l.765: true[false] //│ ║ ^^^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` -//│ ║ l.763: true[false] +//│ ║ l.765: true[false] //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in array access: -//│ ║ l.763: true[false] +//│ ║ l.765: true[false] //│ ║ ^^^^^^^^^^^ //│ ╟── reference of type `true` does not match type `Array[?a]` -//│ ║ l.763: true[false] +//│ ║ l.765: true[false] //│ ╙── ^^^^ //│ res: error | undefined //│ = undefined //│ ╔══[ERROR] Type mismatch in array access: -//│ ║ l.764: 4["hello"] +//│ ║ l.766: 4["hello"] //│ ║ ^^^^^^^^^^ //│ ╟── string literal of type `"hello"` is not an instance of type `int` -//│ ║ l.764: 4["hello"] +//│ ║ l.766: 4["hello"] //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Type mismatch in array access: -//│ ║ l.764: 4["hello"] +//│ ║ l.766: 4["hello"] //│ ║ ^^^^^^^^^^ //│ ╟── integer literal of type `4` does not match type `Array[?a]` -//│ ║ l.764: 4["hello"] +//│ ║ l.766: 4["hello"] //│ ╙── ^ //│ res: error | undefined //│ = undefined //│ ╔══[ERROR] Type mismatch in array access: -//│ ║ l.765: ge[2] +//│ ║ l.767: ge[2] //│ ║ ^^^^^ //│ ╟── function of type `?a -> (forall ?b. ?b)` does not match type `Array[?c]` -//│ ║ l.761: def ge x = x + 1 +//│ ║ l.763: def ge x = x + 1 //│ ║ ^^^^^^^^^ //│ ╟── but it flows into reference with expected type `Array[?d]` -//│ ║ l.765: ge[2] +//│ ║ l.767: ge[2] //│ ╙── ^^ //│ res: error | undefined //│ = undefined @@ -839,43 +841,43 @@ mkarr (1,true,"hi")[0] mkarr 3 [ 1] mk1[2 ] //│ ╔══[ERROR] Type mismatch in array access: -//│ ║ l.839: mkarr 3 [ 1] +//│ ║ l.841: mkarr 3 [ 1] //│ ║ ^^^^^^^ //│ ╟── integer literal of type `3` does not match type `Array[?a]` -//│ ║ l.839: mkarr 3 [ 1] +//│ ║ l.841: mkarr 3 [ 1] //│ ╙── ^ //│ res: Array[error | undefined] //│ = [ undefined, undefined, undefined, undefined, undefined ] //│ ╔══[ERROR] Type mismatch in array access: -//│ ║ l.840: mk1[2 ] +//│ ║ l.842: mk1[2 ] //│ ║ ^^^^^^^^ //│ ╟── integer literal of type `6` does not match type `Array[?a]` -//│ ║ l.822: mk1 = defined (mkarr 6)[ 0] +//│ ║ l.824: mk1 = defined (mkarr 6)[ 0] //│ ║ ^ //│ ╟── but it flows into reference with expected type `Array[?b]` -//│ ║ l.840: mk1[2 ] +//│ ║ l.842: mk1[2 ] //│ ╙── ^^^ //│ res: error | undefined //│ = undefined -def s1 a = (defined a[1] + defined a[2], a[(1,2)._2]) -def s2 a = (defined (defined a[1])[0])._2 + (defined a[2])._1 -def s3 a = defined a._1.t[0] + defined (defined a.x[1])[2] -(defined ((1, "hello"),(2, true, false),(3,3,4))[0])._1 +def s1 a = (defined a[1] + defined a[2], a[(1,2).1]) +def s2 a = (defined (defined a[1])[0]).1 + (defined a[2]).0 +def s3 a = defined a.0.t[0] + defined (defined a.x[1])[2] +(defined ((1, "hello"),(2, true, false),(3,3,4))[0]).0 //│ s1: Array[int & 'a] -> (int, undefined | 'a,) //│ = [Function: s1] -//│ s2: Array[Array[{_2: int} & ~undefined] & {_1: int}] -> int +//│ s2: Array[Array[{1: int} & ~undefined] & {0: int}] -> int //│ = [Function: s2] -//│ s3: {_1: {t: Array[int]}, x: Array[Array[int]]} -> int +//│ s3: {0: {t: Array[int]}, x: Array[Array[int]]} -> int //│ = [Function: s3] //│ res: 1 | 2 | 3 -//│ = undefined +//│ = 1 def ara: Array[(int, bool)] def arb: Array[Array[(int, int)]] -(defined ara[1])._1 -(defined (defined arb[0])[1])._2 -def s4 arr = (defined (defined (defined arr.x[0])._3[6])[7])._h.hello +(defined ara[1]).0 +(defined (defined arb[0])[1]).1 +def s4 arr = (defined (defined (defined arr.x[0]).2[6])[7])._h.hello //│ ara: Array[(int, bool,)] //│ = //│ arb: Array[Array[(int, int,)]] @@ -886,13 +888,13 @@ def s4 arr = (defined (defined (defined arr.x[0])._3[6])[7])._h.hello //│ res: int //│ = //│ arb is not implemented -//│ s4: {x: Array[{_3: Array[Array[{_h: {hello: 'hello}} & ~undefined]]} & ~undefined]} -> 'hello +//│ s4: {x: Array[{2: Array[Array[{_h: {hello: 'hello}} & ~undefined]]} & ~undefined]} -> 'hello //│ = [Function: s4] -def at1 xs = (defined xs[0])._1 +def at1 xs = (defined xs[0]).0 def dup a b = a * 2 + b -dup (defined ara[1])._1 (defined (defined arb[10])[8])._2 + 1 -//│ at1: Array[{_1: 'a} & ~undefined] -> 'a +dup (defined ara[1]).0 (defined (defined arb[10])[8]).1 + 1 +//│ at1: Array[{0: 'a} & ~undefined] -> 'a //│ = [Function: at1] //│ dup: int -> int -> int //│ = [Function: dup] @@ -901,16 +903,15 @@ dup (defined ara[1])._1 (defined (defined arb[10])[8])._2 + 1 //│ ara is not implemented :e -(1,2,3)._1[1] +(1,2,3).0[1] //│ ╔══[ERROR] Type mismatch in array access: -//│ ║ l.904: (1,2,3)._1[1] -//│ ║ ^^^^^^^^^^^^^ +//│ ║ l.906: (1,2,3).0[1] +//│ ║ ^^^^^^^^^^^^ //│ ╟── integer literal of type `1` does not match type `Array[?a]` -//│ ║ l.904: (1,2,3)._1[1] +//│ ║ l.906: (1,2,3).0[1] //│ ║ ^ //│ ╟── but it flows into field selection with expected type `Array[?b]` -//│ ║ l.904: (1,2,3)._1[1] -//│ ╙── ^^^^^^^^^^ +//│ ║ l.906: (1,2,3).0[1] +//│ ╙── ^^^^^^^^^ //│ res: error | undefined -//│ Runtime error: -//│ TypeError: Cannot read properties of undefined (reading '1') +//│ = undefined diff --git a/shared/src/test/diff/mlscript/Yuheng.mls b/shared/src/test/diff/mlscript/Yuheng.mls index 798859e4d4..eab616a8b6 100644 --- a/shared/src/test/diff/mlscript/Yuheng.mls +++ b/shared/src/test/diff/mlscript/Yuheng.mls @@ -40,7 +40,7 @@ f a2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.39: f a2 //│ ║ ^^^^ -//│ ╟── type `number` is not an instance of `int` +//│ ╟── type `number` is not an instance of type `int` //│ ║ l.34: a2 = A { x = 0: number } //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: @@ -137,7 +137,7 @@ rec def eval(e) = case e of eval (IntLit { value = 1 }) //│ res: 'a //│ where -//│ 'a :> ('a, 'a,) | 1 +//│ 'a :> 1 | ('a, 'a,) //│ = 1 eval (Pair { @@ -145,7 +145,7 @@ eval (Pair { rhs = IntLit { value = 2 } }) //│ res: 'a //│ where -//│ 'a :> ('a, 'a,) | 1 | 2 +//│ 'a :> 1 | 2 | ('a, 'a,) //│ = [ 1, 2 ] p = Pair { @@ -153,7 +153,10 @@ p = Pair { rhs = Pair { lhs = IntLit { value = 2 }; rhs = IntLit { value = 3 } } } -//│ p: Pair[?, ?] with {lhs: IntLit & {value: 1}, rhs: Pair[?, ?] with {lhs: IntLit & {value: 2}, rhs: IntLit & {value: 3}}} +//│ p: Pair[?, ?] with { +//│ lhs: IntLit & {value: 1}, +//│ rhs: Pair[?, ?] with {lhs: IntLit & {value: 2}, rhs: IntLit & {value: 3}} +//│ } //│ = Pair { //│ lhs: IntLit { value: 1 }, //│ rhs: Pair { lhs: IntLit { value: 2 }, rhs: IntLit { value: 3 } } @@ -162,7 +165,7 @@ p = Pair { ep = eval p //│ ep: 'a //│ where -//│ 'a :> ('a, 'a,) | 1 | 2 | 3 +//│ 'a :> 1 | 2 | 3 | ('a, 'a,) //│ = [ 1, [ 2, 3 ] ] @@ -191,13 +194,13 @@ ep = eval p class Pair2[A, B]: Expr[(A, B)] & { lhs: Expr[A]; rhs: Expr[A] } //│ Defined class Pair2[±A, ±B] //│ ╔══[WARNING] Type definition Pair2 has bivariant type parameters: -//│ ║ l.191: class Pair2[A, B]: Expr[(A, B)] & { lhs: Expr[A]; rhs: Expr[A] } +//│ ║ l.194: class Pair2[A, B]: Expr[(A, B)] & { lhs: Expr[A]; rhs: Expr[A] } //│ ║ ^^^^^ //│ ╟── A is irrelevant and may be removed -//│ ║ l.191: class Pair2[A, B]: Expr[(A, B)] & { lhs: Expr[A]; rhs: Expr[A] } +//│ ║ l.194: class Pair2[A, B]: Expr[(A, B)] & { lhs: Expr[A]; rhs: Expr[A] } //│ ║ ^ //│ ╟── B is irrelevant and may be removed -//│ ║ l.191: class Pair2[A, B]: Expr[(A, B)] & { lhs: Expr[A]; rhs: Expr[A] } +//│ ║ l.194: class Pair2[A, B]: Expr[(A, B)] & { lhs: Expr[A]; rhs: Expr[A] } //│ ╙── ^ @@ -231,7 +234,7 @@ ep[0] //│ ╙── ^^ //│ res: error | undefined | 'a //│ where -//│ 'a :> ('a, 'a,) | 1 | 2 | 3 +//│ 'a :> 1 | 2 | 3 | ('a, 'a,) eval (Pair { @@ -258,7 +261,7 @@ eval (Pair { //│ ╙── ^^^ //│ res: 'a //│ where -//│ 'a :> ('a, 'a,) | 1 | (Pair[?, ?] with {lhs: IntLit & {value: 2}, rhs: IntLit & {value: 3}}) +//│ 'a :> 1 | (Pair[?, ?] with {lhs: IntLit & {value: 2}, rhs: IntLit & {value: 3}}) | ('a, 'a,) p = Pair2 { @@ -266,7 +269,10 @@ p = Pair2 { rhs = Pair2 { lhs = IntLit { value = 2 }; rhs = IntLit { value = 3 } } } -//│ p: Pair2[?, ?] with {lhs: IntLit & {value: 1}, rhs: Pair2[?, ?] with {lhs: IntLit & {value: 2}, rhs: IntLit & {value: 3}}} +//│ p: Pair2[?, ?] with { +//│ lhs: IntLit & {value: 1}, +//│ rhs: Pair2[?, ?] with {lhs: IntLit & {value: 2}, rhs: IntLit & {value: 3}} +//│ } @@ -293,7 +299,7 @@ rec def eval(e) = case e of //│ ║ l.+4: } //│ ║ ^^^ //│ ╟── type `Expr[?]` does not match type `IntLit & ?a | Pair[?, ?] & ?b` -//│ ║ l.273: def eval: Expr['a] -> 'a +//│ ║ l.279: def eval: Expr['a] -> 'a //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.+1: rec def eval(e) = case e of diff --git a/shared/src/test/diff/mlscript/i56.mls b/shared/src/test/diff/mlscript/i56.mls index 5470a1d944..d64fa02c10 100644 --- a/shared/src/test/diff/mlscript/i56.mls +++ b/shared/src/test/diff/mlscript/i56.mls @@ -6,12 +6,12 @@ def test3 x = case x of | true -> true | _ -> false } -//│ test3: anything -> bool +//│ test3: anything -> Bool //│ = [Function: test3] :ns test3 -//│ res: forall 'a 'b 'c 'd. 'a -> (true | false) +//│ res: forall 'a 'b 'c 'd. 'a -> Bool //│ where //│ 'a <: 1 & 'b | (true & 'c | 'd & ~true) & ~1 //│ = [Function: test3] @@ -22,28 +22,28 @@ def test3 x = case x of | true -> true | _ -> false } -//│ test3: (1 & 'a | ~1) -> (false | true | 'a) +//│ test3: (1 & 'a | ~1) -> (Bool | 'a) //│ = [Function: test31] def ty_1: (1 & 'a | true | ~1 & ~true) -> (false | true | 'a) -//│ ty_1: (1 & 'a | true | ~1 & ~true) -> ('a | false | true) +//│ ty_1: (1 & 'a | true | ~1 & ~true) -> (Bool | 'a) //│ = ty_1 = test3 -//│ (1 & 'a | ~1) -> (false | true | 'a) +//│ (1 & 'a | ~1) -> (Bool | 'a) //│ <: ty_1: -//│ (1 & 'a | true | ~1 & ~true) -> ('a | false | true) +//│ (1 & 'a | true | ~1 & ~true) -> (Bool | 'a) //│ = [Function: test31] def ty_2: (1 & 'a | ~1) -> (false | true | 'a) -//│ ty_2: (1 & 'a | ~1) -> ('a | false | true) +//│ ty_2: (1 & 'a | ~1) -> (Bool | 'a) //│ = ty_2 = ty_1 -//│ (1 & 'a | true | ~1 & ~true) -> ('a | false | true) +//│ (1 & 'a | true | ~1 & ~true) -> (Bool | 'a) //│ <: ty_2: -//│ (1 & 'a | ~1) -> ('a | false | true) +//│ (1 & 'a | ~1) -> (Bool | 'a) //│ = [Function: test31] diff --git a/shared/src/test/diff/nu/AbstractClasses.mls b/shared/src/test/diff/nu/AbstractClasses.mls new file mode 100644 index 0000000000..540f361d71 --- /dev/null +++ b/shared/src/test/diff/nu/AbstractClasses.mls @@ -0,0 +1,183 @@ +:NewDefs + + + +abstract class Foo(x: Int) { + fun f(y: Int) = x + y +} +//│ abstract class Foo(x: Int) { +//│ fun f: (y: Int) -> Int +//│ } + +:e +Foo(1) +//│ ╔══[ERROR] Class Foo is abstract and cannot be instantiated +//│ ║ l.13: Foo(1) +//│ ╙── ^^^ +//│ Foo +//│ res +//│ = Foo {} + +:e // TODO allow with `new` keyword +new Foo(1) +//│ ╔══[ERROR] Class Foo is abstract and cannot be instantiated +//│ ║ l.22: new Foo(1) +//│ ╙── ^^^^^^^^^^ +//│ Foo +//│ res +//│ = Foo {} + + +abstract class Foo(x: Int) { + fun f: Int -> Int +} +//│ abstract class Foo(x: Int) { +//│ fun f: Int -> Int +//│ } + +:e +Foo(1) +//│ ╔══[ERROR] Class Foo is abstract and cannot be instantiated +//│ ║ l.39: Foo(1) +//│ ╙── ^^^ +//│ Foo +//│ res +//│ = Foo {} + +:e // TODO support +new Foo(1) { fun f = id } +//│ ╔══[ERROR] Refinement terms are not yet supported +//│ ║ l.48: new Foo(1) { fun f = id } +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ error +//│ Code generation encountered an error: +//│ cannot generate code for term Rft(App(NuNew(Var(Foo)),Tup(List((None,Fld(_,IntLit(1)))))),TypingUnit(List(NuFunDef(None,Var(f),None,List(),Left(Var(id)))))) + + +abstract class Bar extends Foo(1) +//│ abstract class Bar extends Foo { +//│ fun f: Int -> Int +//│ } + +:e +module Baz extends Bar +Baz.f(1) +//│ ╔══[ERROR] Member `f` is declared (or its declaration is inherited) but is not implemented in `Baz` +//│ ║ l.63: module Baz extends Bar +//│ ║ ^^^ +//│ ╟── Declared here: +//│ ║ l.32: fun f: Int -> Int +//│ ╙── ^^^^^^^^^^^^^^^^^ +//│ module Baz extends Bar, Foo { +//│ fun f: Int -> Int +//│ } +//│ Int +//│ res +//│ Runtime error: +//│ TypeError: Baz.f is not a function + +module Baz extends Bar { + fun f(x) = x + 1 +} +Baz.f(1) +//│ module Baz extends Bar, Foo { +//│ fun f: Int -> Int +//│ } +//│ Int +//│ res +//│ = 2 + + + +abstract class C1 { fun x: Int | Str } +//│ abstract class C1 { +//│ fun x: Int | Str +//│ } + +trait T1 { fun x: Int | Bool } +//│ trait T1 { +//│ fun x: Int | false | true +//│ } + +:e +class C2 extends C1, T1 +//│ ╔══[ERROR] Member `x` is declared (or its declaration is inherited) but is not implemented in `C2` +//│ ║ l.103: class C2 extends C1, T1 +//│ ║ ^^ +//│ ╟── Declared here: +//│ ║ l.92: abstract class C1 { fun x: Int | Str } +//│ ╙── ^^^^^^^^^^^^ +//│ class C2 extends C1, T1 { +//│ constructor() +//│ fun x: Int +//│ } + +class C2 extends C1, T1 { fun x = 1 } +//│ class C2 extends C1, T1 { +//│ constructor() +//│ fun x: 1 +//│ } + +abstract class C2 extends C1, T1 +//│ abstract class C2 extends C1, T1 { +//│ fun x: Int +//│ } + +:e +class C3 extends C2 +//│ ╔══[ERROR] Member `x` is declared (or its declaration is inherited) but is not implemented in `C3` +//│ ║ l.127: class C3 extends C2 +//│ ║ ^^ +//│ ╟── Declared here: +//│ ║ l.92: abstract class C1 { fun x: Int | Str } +//│ ╙── ^^^^^^^^^^^^ +//│ class C3 extends C1, C2, T1 { +//│ constructor() +//│ fun x: Int +//│ } + +class C3 extends C2 { fun x = 1 } +//│ class C3 extends C1, C2, T1 { +//│ constructor() +//│ fun x: 1 +//│ } + + + +:e +abstract class C { + fun x : Int + fun foo0 = x + fun foo1 = this.x +} +//│ ╔══[ERROR] Unqualified access to virtual member x +//│ ║ l.150: fun foo0 = x +//│ ║ ^^^^^^^^ +//│ ╟── Declared here: +//│ ║ l.149: fun x : Int +//│ ╙── ^^^^^^^^^^^ +//│ abstract class C { +//│ fun foo0: Int +//│ fun foo1: Int +//│ fun x: Int +//│ } + +:e +abstract class C { + val x : Int + fun foo0 = x + fun foo1 = this.x +} +//│ ╔══[ERROR] Unqualified access to virtual member x +//│ ║ l.168: fun foo0 = x +//│ ║ ^^^^^^^^ +//│ ╟── Declared here: +//│ ║ l.167: val x : Int +//│ ╙── ^^^^^^^^^^^ +//│ abstract class C { +//│ fun foo0: Int +//│ fun foo1: Int +//│ val x: Int +//│ } + + diff --git a/shared/src/test/diff/nu/Andong.mls b/shared/src/test/diff/nu/Andong.mls new file mode 100644 index 0000000000..e2f1e77efc --- /dev/null +++ b/shared/src/test/diff/nu/Andong.mls @@ -0,0 +1,15 @@ +:NewDefs + + +class Union(a: Region, b: Region) +//│ class Union[Region](a: Region, b: Region) + +fun hmm(x) = + if x is Union(x, y) then x +//│ fun hmm: forall 'a. Union['a] -> 'a + +fun hmm(x) = + if x is Union(z, y) then x +//│ fun hmm: forall 'Region. Union['Region] -> Union['Region] + + diff --git a/shared/src/test/diff/nu/ArrayProg.mls b/shared/src/test/diff/nu/ArrayProg.mls new file mode 100644 index 0000000000..857ee4e3ac --- /dev/null +++ b/shared/src/test/diff/nu/ArrayProg.mls @@ -0,0 +1,249 @@ +:NewDefs + + + +fun cast(x) = x +declare fun cast: anything -> nothing +//│ fun cast: forall 'a. 'a -> 'a +//│ fun cast: anything -> nothing + + +fun mapi: (Array['a], ('a, Int) -> 'b) -> Array['b] +fun mapi(xs, f) = cast(xs).map(f) +//│ fun mapi: (anything, anything) -> nothing +//│ fun mapi: forall 'a 'b. (Array['a], ('a, Int) -> 'b) -> Array['b] + +mapi of ["a", "", "bb"], (x, i) => [i, length of x] +//│ Array[[Int, Int]] +//│ res +//│ = [ [ 0, 1 ], [ 1, 0 ], [ 2, 2 ] ] + + +fun map(xs, f) = mapi(xs, (x, i) => f(x)) +//│ fun map: forall 'a 'b. (Array['a], 'a -> 'b) -> Array['b] + +map of ["a", "", "bb"], x => length of x +//│ Array[Int] +//│ res +//│ = [ 1, 0, 2 ] + + +fun zip: (Array['a], Array['b & ~undefined & Object]) -> Array[['a, 'b]] +fun zip(xs, ys) = mapi of xs, (x, i) => + if ys.[i] is + undefined then error + y then [x, y] +//│ fun zip: forall 'c 'd. (Array['c], Array[Object & 'd & ~()]) -> Array[['c, 'd]] +//│ fun zip: forall 'a 'b. (Array['a], Array[Object & 'b & ~()]) -> Array[['a, 'b]] + + +zip +//│ forall 'a 'b. (Array['a], Array[Object & 'b & ~()]) -> Array[['a, 'b]] +//│ res +//│ = [Function: zip] + + + +class Numbr(n: Int) +class Vectr(xs: Array[Numbr | Vectr]) +//│ class Numbr(n: Int) +//│ class Vectr(xs: Array[Numbr | Vectr]) + +class Pair[A, B](a: A, b: B) +//│ class Pair[A, B](a: A, b: B) + + +fun unbox(x) = if x is + Numbr(n) then n + Vectr(xs) then map of xs, unbox +//│ fun unbox: forall 'a. (Numbr | Vectr) -> (Int | 'a) +//│ where +//│ 'a :> Array[Int | 'a] + +fun add(e) = + if e is + Pair(Numbr(n), Numbr(m)) then Numbr(n + m) + Pair(Vectr(xs), Vectr(ys)) then + Vectr of map of zip(xs, ys), ([x, y]) => add of Pair of x, y + Pair(Vectr(xs), Numbr(n)) then + Vectr of map of xs, x => add of Pair of x, Numbr(n) + Pair(Numbr(n), Vectr(xs)) then + Vectr of map of xs, x => add of Pair of Numbr(n), x +//│ fun add: Pair[Numbr | Vectr, Numbr | Vectr] -> (Numbr | Vectr) + + +add(Pair(Numbr(0), Numbr(1))) +//│ Numbr | Vectr +//│ res +//│ = Numbr {} + +add(Pair(Vectr([]), Vectr([]))) +//│ Numbr | Vectr +//│ res +//│ = Vectr {} + +let v = Vectr of [Numbr(10), Numbr(20), Numbr(30)] +//│ let v: Vectr +//│ v +//│ = Vectr {} + +unbox(v) +//│ forall 'a. Int | 'a +//│ where +//│ 'a :> Array[Int | 'a] +//│ res +//│ = [ 10, 20, 30 ] + + +let res = add of Pair of (Vectr of [Numbr(1), Numbr(2)]), (Vectr of [Numbr(3), v]) +//│ let res: Numbr | Vectr +//│ res +//│ = Vectr {} + +unbox(res) +//│ forall 'a. Int | 'a +//│ where +//│ 'a :> Array[Int | 'a] +//│ res +//│ = [ 4, [ 12, 22, 32 ] ] + + +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]) + +add2(Pair(Numbr(0), Numbr(1))) +//│ Numbr | Array[Numbr | Vectr] +//│ res +//│ = Numbr {} + + + +// * Playing with approximated unions/intersections + + +fun t: ([Numbr,Numbr]|[Vectr,Vectr]) -> Int +//│ fun t: ([Numbr | Vectr, Numbr | Vectr]) -> Int + + +fun s: (([Numbr,Numbr] -> Int) & ([Vectr,Vectr] -> Int),) +//│ fun s: (Numbr | Vectr, Numbr | Vectr) -> Int + +// FIXME why does the above parse the same as: + +fun s: ([Numbr,Numbr] -> Int) & ([Vectr,Vectr] -> Int) +//│ fun s: (Numbr | Vectr, Numbr | Vectr) -> Int + + +s(Vectr([]),Vectr([])) +//│ Int +//│ res +//│ = +//│ s is not implemented + + +module A { + fun g: (Int -> Int) & (Str -> Str) + fun g(x) = x +} +// g: (Int | Str) -> (Int & Str) -- under-approx +// g: (Int & Str) -> (Int | Str) -- over-approx +//│ module A { +//│ fun g: Int -> Int & Str -> Str +//│ } + +A.g(0) +//│ Int | Str +//│ res +//│ = 0 + + + + +// === === === ERROR CASES === === === // + + +:ShowRelativeLineNums +:AllowTypeErrors + + +:e +s([Numbr(0),Numbr(0)]) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.+1: s([Numbr(0),Numbr(0)]) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── argument of type `[[?a, ?b]]` does not match type `[Numbr | Vectr, Numbr | Vectr]` +//│ ║ l.+1: s([Numbr(0),Numbr(0)]) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from tuple type: +//│ ║ l.136: fun s: ([Numbr,Numbr] -> Int) & ([Vectr,Vectr] -> Int) +//│ ╙── ^^^^^^^^^^^^^ +//│ Int | error +// g <: 0 -> 'a + +:e +fun add(e) = + if e is + 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. +//│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 +//│ ║ ^^^^^^^^ +//│ ╟── [Missing Case 1/1] `Vectr` +//│ ╟── It first appears here. +//│ ║ l.+4: Pair(Vectr(xs), Vectr(ys)) then 1 +//│ ╙── ^^^^^^^^^ +//│ fun add: anything -> error + +: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. +//│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 +//│ ║ ^^^^^^^^ +//│ ╟── [Missing Case 1/1] `Vectr` +//│ ╟── It first appears here. +//│ ║ l.+4: Pair(Vectr(xs), Vectr(ys)) then 1 +//│ ╙── ^^^^^^^^^ +//│ fun add: anything -> error + +:e +add2(Pair(Vectr(0), Numbr(1))) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.+1: add2(Pair(Vectr(0), Numbr(1))) +//│ ║ ^^^^^^^^ +//│ ╟── integer literal of type `0` does not match type `Array[Numbr | Vectr]` +//│ ║ l.+1: add2(Pair(Vectr(0), Numbr(1))) +//│ ║ ^ +//│ ╟── Note: constraint arises from applied type reference: +//│ ║ l.48: class Vectr(xs: Array[Numbr | Vectr]) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^ +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.+1: add2(Pair(Vectr(0), Numbr(1))) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── application of type `Vectr` is not an instance of type `Numbr` +//│ ║ l.+1: add2(Pair(Vectr(0), Numbr(1))) +//│ ║ ^^^^^^^^ +//│ ╟── Note: constraint arises from class pattern: +//│ ║ l.113: Pair(Numbr(n), Numbr(m)) then Numbr(m + m) +//│ ║ ^^^^^ +//│ ╟── from field selection: +//│ ║ l.52: class Pair[A, B](a: A, b: B) +//│ ║ ^ +//│ ╟── Note: type parameter A is defined at: +//│ ║ l.52: class Pair[A, B](a: A, b: B) +//│ ╙── ^ +//│ Numbr | error | Array[Numbr | Vectr] + + diff --git a/shared/src/test/diff/nu/Ascription.mls b/shared/src/test/diff/nu/Ascription.mls index 558490d024..7bc04929e6 100644 --- a/shared/src/test/diff/nu/Ascription.mls +++ b/shared/src/test/diff/nu/Ascription.mls @@ -1,45 +1,51 @@ -:NewParser +:NewDefs -1: int -//│ res: int -//│ = 1 +1: Int +//│ Int +//│ res +//│ = 1 -1 : int -//│ res: int -//│ = 1 +1 : Int +//│ Int +//│ res +//│ = 1 // TODO? -:e -1 : int : int -//│ ╔══[ERROR] not a recognized type: int : int -//│ ║ l.13: 1 : int : int +:pe +1 : Int : Int +//│ ╔══[PARSE ERROR] Not a recognized type +//│ ║ l.15: 1 : Int : Int //│ ╙── ^^^ -//│ res: anything -//│ = 1 +//│ anything +//│ res +//│ = 1 -// FIXME -:w -:e -fun foo(x: int) = x + 1 -//│ ╔══[WARNING] Variable name 'int' already names a symbol in scope. If you want to refer to that symbol, you can use `scope.int`; if not, give your future readers a break and use another name :^) -//│ ║ l.23: fun foo(x: int) = x + 1 -//│ ╙── ^^^ -//│ ╔══[ERROR] identifier not found: x -//│ ║ l.23: fun foo(x: int) = x + 1 -//│ ╙── ^ -//│ foo: (x: anything,) -> int -//│ Code generation encountered an error: -//│ unresolved symbol x +fun foo(x: Int) = x + 1 +//│ fun foo: (x: Int) -> Int + +fun foo(x : Int) = x + 1 +//│ fun foo: Int -> Int -fun foo(x : int) = x + 1 -//│ foo: int -> int -//│ = [Function: foo1] +foo(123 : Int) : Int +//│ Int +//│ res +//│ = 124 -foo(123 : int) : int -//│ res: int -//│ = 124 +:e +foo(123: Int): Int +//│ ╔══[ERROR] Cannot use named arguments as the function type has untyped arguments +//│ ║ l.35: foo(123: Int): Int +//│ ╙── ^^^^^^^^^^ +//│ Int +//│ Code generation encountered an error: +//│ unresolved symbol Int -foo(123:int):int -//│ res: int -//│ = 124 +:e +foo(123:Int):Int +//│ ╔══[ERROR] Cannot use named arguments as the function type has untyped arguments +//│ ║ l.44: foo(123:Int):Int +//│ ╙── ^^^^^^^^^ +//│ Int +//│ Code generation encountered an error: +//│ unresolved symbol Int diff --git a/shared/src/test/diff/nu/AuxCtors.mls b/shared/src/test/diff/nu/AuxCtors.mls new file mode 100644 index 0000000000..ab2ef6141d --- /dev/null +++ b/shared/src/test/diff/nu/AuxCtors.mls @@ -0,0 +1,223 @@ +:NewDefs + + +class C(val x: Int) { constructor(y: Int) { x = y } } +new C(123).x +//│ class C(x: Int) { +//│ constructor(y: Int) +//│ } +//│ Int +//│ res +//│ = 123 + +class C(val x: Int) { constructor(y) { x = y } } +new C(123).x +//│ class C(x: Int) { +//│ constructor(y: Int) +//│ } +//│ Int +//│ res +//│ = 123 + +class C(val x: Int) { constructor(y: Int) { log(y); x = y; log(x) } } +new C(123).x +//│ class C(x: Int) { +//│ constructor(y: Int) +//│ } +//│ Int +//│ res +//│ = 123 +//│ // Output +//│ 123 +//│ 123 + +:e +class C(val x: Int) { constructor(y: Int) { x = y; log(x); x = y + 1; log(x) } } +new C(123).x +//│ ╔══[ERROR] Class parameter 'x' was already set +//│ ║ l.35: class C(val x: Int) { constructor(y: Int) { x = y; log(x); x = y + 1; log(x) } } +//│ ╙── ^ +//│ class C(x: Int) { +//│ constructor(y: Int) +//│ } +//│ Int +//│ res +//│ = 124 +//│ // Output +//│ 123 +//│ 124 + +:e +class C(val x: Int) { constructor(y: Int) { log(x); x = y } } +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.51: class C(val x: Int) { constructor(y: Int) { log(x); x = y } } +//│ ╙── ^ +//│ class C(x: Int) { +//│ constructor(y: Int) +//│ } +//│ Code generation encountered an error: +//│ unresolved symbol x + +:e +class C(val x: Int) { constructor(y: Str) { x = y } } +//│ ╔══[ERROR] Type mismatch in auxiliary class constructor: +//│ ║ l.62: class C(val x: Int) { constructor(y: Str) { x = y } } +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type `Str` is not an instance of type `Int` +//│ ║ l.62: class C(val x: Int) { constructor(y: Str) { x = y } } +//│ ║ ^^^ +//│ ╟── but it flows into reference with expected type `Int` +//│ ║ l.62: class C(val x: Int) { constructor(y: Str) { x = y } } +//│ ║ ^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.62: class C(val x: Int) { constructor(y: Str) { x = y } } +//│ ╙── ^^^ +//│ class C(x: Int) { +//│ constructor(y: Str) +//│ } + +:e +class C(val x: Int) { constructor(y: Int) { z = y } } +//│ ╔══[ERROR] Unknown class parameter 'z' +//│ ║ l.80: class C(val x: Int) { constructor(y: Int) { z = y } } +//│ ╙── ^ +//│ class C(x: Int) { +//│ constructor(y: Int) +//│ } +//│ Syntax error: +//│ Private field '#z' must be declared in an enclosing class + +:e +class C(val x: Int) { constructor(val y: Int) { x = y } } +//│ ╔══[ERROR] Cannot use `val` in constructor parameters +//│ ║ l.91: class C(val x: Int) { constructor(val y: Int) { x = y } } +//│ ╙── ^ +//│ class C(x: Int) { +//│ constructor(y: Int) +//│ } + +:e +class C(val x: Int) { constructor(val y) { x = y } } +//│ ╔══[ERROR] Cannot use `val` in constructor parameters +//│ ║ l.100: class C(val x: Int) { constructor(val y) { x = y } } +//│ ╙── ^ +//│ class C(x: Int) { +//│ constructor(y: Int) +//│ } + +:e +class C(val x: Int) { constructor(2 + 2) { x = 0 } } +//│ ╔══[ERROR] Unsupported constructor parameter shape +//│ ║ l.109: class C(val x: Int) { constructor(2 + 2) { x = 0 } } +//│ ╙── ^^^^^ +//│ class C(x: Int) { +//│ constructor(: error) +//│ } +//│ Code generation encountered an error: +//│ Unexpected constructor parameters in C. + + +class C(val x: Int, y: Int) { + constructor(z: Int) { x = z; y = z } + log([x, y]) +} +//│ class C(x: Int, y: Int) { +//│ constructor(z: Int) +//│ } + +:e +C(11) +//│ ╔══[ERROR] Construction of class with auxiliary constructor should use the `new` keyword +//│ ║ l.129: C(11) +//│ ╙── ^ +//│ C +//│ res +//│ = C {} +//│ // Output +//│ [ 11, 11 ] + +:e +new C(1, 2) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.140: new C(1, 2) +//│ ║ ^^^^^^^^^^^ +//│ ╟── tuple literal of type `[1, 2]` does not match type `[z: Int]` +//│ ║ l.140: new C(1, 2) +//│ ╙── ^^^^^^ +//│ C | error +//│ res +//│ = C {} +//│ // Output +//│ [ 1, 1 ] + +new C(11) +//│ C +//│ res +//│ = C {} +//│ // Output +//│ [ 11, 11 ] + +:e +new C(1, 2) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.161: new C(1, 2) +//│ ║ ^^^^^^^^^^^ +//│ ╟── tuple literal of type `[1, 2]` does not match type `[z: Int]` +//│ ║ l.161: new C(1, 2) +//│ ╙── ^^^^^^ +//│ C | error +//│ res +//│ = C {} +//│ // Output +//│ [ 1, 1 ] + +:pe +:w +class C { constructor(x: Int); constructor(y: Int) } +//│ ╔══[PARSE ERROR] A class may have at most one explicit constructor +//│ ║ l.176: class C { constructor(x: Int); constructor(y: Int) } +//│ ╙── ^^^^^ +//│ ╔══[WARNING] Pure expression does nothing in statement position. +//│ ║ l.176: class C { constructor(x: Int); constructor(y: Int) } +//│ ╙── ^ +//│ class C { +//│ constructor(x: Int) +//│ } + + +:w // * FIXME +class Foo { constructor(x: Int){}; val y = 2 } +//│ ╔══[WARNING] Pure expression does nothing in statement position. +//│ ║ l.189: class Foo { constructor(x: Int){}; val y = 2 } +//│ ╙── ^ +//│ class Foo { +//│ constructor(x: Int) +//│ val y: 2 +//│ } + +:w +class Foo { constructor(x: Int){}; val y = 2 } +//│ ╔══[WARNING] Pure expression does nothing in statement position. +//│ ║ l.199: class Foo { constructor(x: Int){}; val y = 2 } +//│ ╙── ^ +//│ class Foo { +//│ constructor(x: Int) +//│ val y: 2 +//│ } + +class Foo { constructor(x: Int){} val y = 2 } +//│ class Foo { +//│ constructor(x: Int) +//│ val y: 2 +//│ } + +class Foo { + constructor(x: Int){} + val y = 2 +} +//│ class Foo { +//│ constructor(x: Int) +//│ val y: 2 +//│ } + + diff --git a/shared/src/test/diff/nu/BadAliases.mls b/shared/src/test/diff/nu/BadAliases.mls new file mode 100644 index 0000000000..18d8e11ef7 --- /dev/null +++ b/shared/src/test/diff/nu/BadAliases.mls @@ -0,0 +1,111 @@ +:NewDefs + + +// TODO check cyclicity +// :e +type A = A +//│ type A = A + +// TODO check cyclicity +// :e +type A = A | Int +//│ type A = Int | A + +// TODO check regularity +// :e +type Foo[A] = { x: A, y: Foo[[A, A]] } +//│ type Foo[A] = {x: A, y: Foo[[A, A]]} + + +// TODO support abstract types +:e +type Test +//│ ╔══[ERROR] Type alias definition requires a right-hand side +//│ ║ l.22: type Test +//│ ╙── ^^^^^^^^^ +//│ type Test = error + +:e +type Test(n: Int) = n +//│ ╔══[ERROR] Type alias definitions cannot have value parameters +//│ ║ l.29: type Test(n: Int) = n +//│ ╙── ^^^^^^^^ +//│ ╔══[ERROR] type identifier not found: n +//│ ║ l.29: type Test(n: Int) = n +//│ ╙── ^ +//│ type Test = error + +class Base +//│ class Base { +//│ constructor() +//│ } + +:pe +:e +type Test: Base +//│ ╔══[PARSE ERROR] Expected end of input; found ':' instead +//│ ║ l.45: type Test: Base +//│ ╙── ^ +//│ ╔══[ERROR] Type alias definition requires a right-hand side +//│ ║ l.45: type Test: Base +//│ ╙── ^^^^^^^^^ +//│ type Test = error + +:pe +:e +type Test: Base = Int +//│ ╔══[PARSE ERROR] Expected end of input; found ':' instead +//│ ║ l.56: type Test: Base = Int +//│ ╙── ^ +//│ ╔══[ERROR] Type alias definition requires a right-hand side +//│ ║ l.56: type Test: Base = Int +//│ ╙── ^^^^^^^^^ +//│ type Test = error + +:e +type Test extends Base +//│ ╔══[ERROR] Type alias definitions cannot extend parents +//│ ║ l.66: type Test extends Base +//│ ╙── ^^^^ +//│ ╔══[ERROR] Type alias definition requires a right-hand side +//│ ║ l.66: type Test extends Base +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ +//│ type Test = error + +:pe +:e +type Test extends Base = Int +//│ ╔══[PARSE ERROR] Expected end of input; found '=' instead +//│ ║ l.77: type Test extends Base = Int +//│ ╙── ^ +//│ ╔══[ERROR] Type alias definitions cannot extend parents +//│ ║ l.77: type Test extends Base = Int +//│ ╙── ^^^^ +//│ ╔══[ERROR] Type alias definition requires a right-hand side +//│ ║ l.77: type Test extends Base = Int +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ +//│ type Test = error + +:e +type Test = Int extends Base +//│ ╔══[ERROR] Type alias definitions cannot extend parents +//│ ║ l.90: type Test = Int extends Base +//│ ╙── ^^^^ +//│ type Test = Int + + +:pe +type Poly[mut A] = A +//│ ╔══[PARSE ERROR] Unexpected 'mut' keyword here +//│ ║ l.98: type Poly[mut A] = A +//│ ╙── ^^^ +//│ type Poly = A + +:pe +type Poly[#A] = A +//│ ╔══[PARSE ERROR] Unexpected '#' here +//│ ║ l.105: type Poly[#A] = A +//│ ╙── ^ +//│ type Poly = A + + diff --git a/shared/src/test/diff/nu/BadBlocks.mls b/shared/src/test/diff/nu/BadBlocks.mls new file mode 100644 index 0000000000..b76b35c08c --- /dev/null +++ b/shared/src/test/diff/nu/BadBlocks.mls @@ -0,0 +1,180 @@ +:NewDefs + + +:e +fun test = + fun lol = log("ok") + [lol, lol] +//│ ╔══[ERROR] Cannot use `val` or `fun` in local block; use `let` instead. +//│ ║ l.6: fun lol = log("ok") +//│ ╙── ^^^^^^^^^^^^^^^^^^^ +//│ fun test: [(), ()] + +test +//│ [(), ()] +//│ res +//│ = [ undefined, undefined ] +//│ // Output +//│ ok + +:e +fun test = + fun lol = log("ok") + [] +//│ ╔══[ERROR] Cannot use `val` or `fun` in local block; use `let` instead. +//│ ║ l.22: fun lol = log("ok") +//│ ╙── ^^^^^^^^^^^^^^^^^^^ +//│ fun test: [] + +test +//│ [] +//│ res +//│ = [] +//│ // Output +//│ ok + +fun test = + let a = 0 + a +//│ fun test: 0 + +:e +fun test = + fun a = b + fun b = 1 + a +//│ ╔══[ERROR] Cannot use `val` or `fun` in local block; use `let` instead. +//│ ║ l.43: fun a = b +//│ ╙── ^^^^^^^^^ +//│ ╔══[ERROR] Cannot use `val` or `fun` in local block; use `let` instead. +//│ ║ l.44: fun b = 1 +//│ ╙── ^^^^^^^^^ +//│ fun test: 1 + +// TODO[init-check] reject +fun test = + let a = b + let b = 1 + a +//│ fun test: 1 + +:re +test +//│ 1 +//│ res +//│ Runtime error: +//│ ReferenceError: Cannot access 'b' before initialization + +:js +fun test = + let a() = b + let b = 1 + a() +//│ fun test: 1 +//│ // Prelude +//│ class TypingUnit8 {} +//│ const typing_unit8 = new TypingUnit8; +//│ // Query 1 +//│ globalThis.test5 = function test5() { +//│ return ((() => { +//│ let a = () => b; +//│ let b = 1; +//│ return a(); +//│ })()); +//│ }; +//│ // End of generated code + +test +//│ 1 +//│ res +//│ = 1 + + +// * OK +fun test = + class Foo(x: Int) { fun y = x + 1 } + Foo(1).y +//│ fun test: Int + +// * MAYBE OK +:ge // TODO accept? +fun test = + let r() = Foo(1).y + class Foo(x: Int) { fun y = x + 1 } + r() +//│ fun test: Int +//│ Code generation encountered an error: +//│ unresolved symbol Foo + +// * NOT OK +:ge // :e // TODO[init-check] reject +fun test = + let r = Foo(1).y + class Foo(x: Int) { fun y = x + 1 } + r +//│ fun test: Int +//│ Code generation encountered an error: +//│ unresolved symbol Foo + +:re +test +//│ Int +//│ res +//│ Runtime error: +//│ ReferenceError: test8 is not defined + + +:pe +:ge +fun test = { + fun a = 1 +} +//│ ╔══[PARSE ERROR] Unexpected 'fun' keyword in expression position +//│ ║ l.130: fun a = 1 +//│ ╙── ^^^ +//│ ╔══[PARSE ERROR] Unexpected '=' here +//│ ║ l.130: fun a = 1 +//│ ╙── ^ +//│ fun test: {a: () -> 1} +//│ Code generation encountered an error: +//│ unresolved symbol a + +:pe +:e +fun test = { + val res = a + 1 + fun a = 123 +}.res +//│ ╔══[PARSE ERROR] Unexpected '=' here +//│ ║ l.145: val res = a + 1 +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: res +//│ ║ l.145: val res = a + 1 +//│ ╙── ^^^ +//│ fun test: error + +:pe // TODO support +:e +fun test = (new { + val res = a + 1 + fun a = 123 +}).res +//│ ╔══[PARSE ERROR] Unexpected '=' here +//│ ║ l.159: val res = a + 1 +//│ ╙── ^ +//│ ╔══[ERROR] type identifier not found: res +//│ ║ l.159: val res = a + 1 +//│ ╙── ^^^ +//│ ╔══[ERROR] Unexpected type `{res: error}` after `new` keyword +//│ ║ l.158: fun test = (new { +//│ ║ ^ +//│ ║ l.159: val res = a + 1 +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.160: fun a = 123 +//│ ║ ^^^^^^^^^^^^^ +//│ ║ l.161: }).res +//│ ╙── ^ +//│ fun test: error +//│ Code generation encountered an error: +//│ Unsupported `new` class term: Bra(true,Rcd(List((Var(res),Fld(g,Var(res)))))) + diff --git a/shared/src/test/diff/nu/BadClassInherit.mls b/shared/src/test/diff/nu/BadClassInherit.mls new file mode 100644 index 0000000000..d487fe30cb --- /dev/null +++ b/shared/src/test/diff/nu/BadClassInherit.mls @@ -0,0 +1,232 @@ +:NewDefs + + +class C1(x: Int) +//│ class C1(x: Int) + +:e +class C2(x: Int) extends C1(y) { + val y = x +} +//│ ╔══[ERROR] identifier not found: y +//│ ║ l.8: class C2(x: Int) extends C1(y) { +//│ ╙── ^ +//│ class C2(x: Int) extends C1 { +//│ val y: Int +//│ } +//│ Code generation encountered an error: +//│ unresolved symbol y + +:e +abstract class C2 extends C1(y) { + val y: Int +} +//│ ╔══[ERROR] identifier not found: y +//│ ║ l.21: abstract class C2 extends C1(y) { +//│ ╙── ^ +//│ abstract class C2 extends C1 { +//│ val y: Int +//│ } +//│ Code generation encountered an error: +//│ unresolved symbol y + +:e +abstract class C2 extends C1(this.y) { + val y: Int +} +//│ ╔══[ERROR] identifier not found: this +//│ ║ l.34: abstract class C2 extends C1(this.y) { +//│ ╙── ^^^^ +//│ abstract class C2 extends C1 { +//│ val y: Int +//│ } + + +class C1(x: C1) +//│ class C1(x: C1) + +:e +class C2 extends C1(this) +//│ ╔══[ERROR] identifier not found: this +//│ ║ l.49: class C2 extends C1(this) +//│ ╙── ^^^^ +//│ class C2 extends C1 { +//│ constructor() +//│ } + + +class Foo { virtual fun x: Int = 1 } +//│ class Foo { +//│ constructor() +//│ fun x: Int +//│ } + +:e +class Bar extends Foo { fun x = false } +//│ ╔══[ERROR] Type mismatch in definition of method x: +//│ ║ l.65: class Bar extends Foo { fun x = false } +//│ ║ ^^^^^^^^^ +//│ ╟── reference of type `false` is not an instance of `Int` +//│ ║ l.65: class Bar extends Foo { fun x = false } +//│ ║ ^^^^^ +//│ ╟── but it flows into definition of method x with expected type `Int` +//│ ║ l.65: class Bar extends Foo { fun x = false } +//│ ║ ^^^^^^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.58: class Foo { virtual fun x: Int = 1 } +//│ ║ ^^^ +//│ ╟── from definition of method x: +//│ ║ l.58: class Foo { virtual fun x: Int = 1 } +//│ ╙── ^^^^^^^^^^ +//│ class Bar extends Foo { +//│ constructor() +//│ fun x: false +//│ } + +:e +class Bar extends Foo { + fun x: Bool + fun x = false +} +//│ ╔══[ERROR] Type mismatch in signature of member `x`: +//│ ║ l.88: fun x: Bool +//│ ║ ^^^^^^^ +//│ ╟── type `Bool` is not an instance of type `Int` +//│ ║ l.88: fun x: Bool +//│ ║ ^^^^ +//│ ╟── but it flows into signature of member `x` with expected type `Int` +//│ ║ l.88: fun x: Bool +//│ ║ ^^^^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.58: class Foo { virtual fun x: Int = 1 } +//│ ║ ^^^ +//│ ╟── from definition of method x: +//│ ║ l.58: class Foo { virtual fun x: Int = 1 } +//│ ╙── ^^^^^^^^^^ +//│ ╔══[ERROR] Type mismatch in signature of member `x`: +//│ ║ l.88: fun x: Bool +//│ ║ ^^^^^^^ +//│ ╟── type `Bool` is not an instance of type `Int` +//│ ║ l.88: fun x: Bool +//│ ║ ^^^^ +//│ ╟── but it flows into signature of member `x` with expected type `Int` +//│ ║ l.88: fun x: Bool +//│ ║ ^^^^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.58: class Foo { virtual fun x: Int = 1 } +//│ ║ ^^^ +//│ ╟── from definition of method x: +//│ ║ l.58: class Foo { virtual fun x: Int = 1 } +//│ ╙── ^^^^^^^^^^ +//│ class Bar extends Foo { +//│ constructor() +//│ fun x: Bool +//│ } + +mixin M { fun x = false } +//│ mixin M() { +//│ fun x: false +//│ } + +:e +class Bar extends Foo, M +//│ ╔══[ERROR] Type mismatch in definition of method x: +//│ ║ l.126: mixin M { fun x = false } +//│ ║ ^^^^^^^^^ +//│ ╟── reference of type `false` is not an instance of `Int` +//│ ║ l.126: mixin M { fun x = false } +//│ ║ ^^^^^ +//│ ╟── but it flows into definition of method x with expected type `Int` +//│ ║ l.126: mixin M { fun x = false } +//│ ║ ^^^^^^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.58: class Foo { virtual fun x: Int = 1 } +//│ ║ ^^^ +//│ ╟── from definition of method x: +//│ ║ l.58: class Foo { virtual fun x: Int = 1 } +//│ ╙── ^^^^^^^^^^ +//│ class Bar extends Foo { +//│ constructor() +//│ fun x: false +//│ } + + + +class A { class X { fun f = 1 } } +trait B { class X { fun g = 1 } } +//│ class A { +//│ constructor() +//│ class X { +//│ constructor() +//│ fun f: 1 +//│ } +//│ } +//│ trait B { +//│ class X { +//│ constructor() +//│ fun g: 1 +//│ } +//│ } + +:e +class C extends A, B +//│ ╔══[ERROR] Class member `X` cannot override class member of the same name declared in parent +//│ ║ l.172: class C extends A, B +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ╟── Originally declared here: +//│ ║ l.156: trait B { class X { fun g = 1 } } +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//│ ╔══[ERROR] Intersection of class member and class members currently unsupported +//│ ║ l.172: class C extends A, B +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ╟── The class member is defined here: +//│ ║ l.155: class A { class X { fun f = 1 } } +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── The class member is defined here: +//│ ║ l.156: trait B { class X { fun g = 1 } } +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//│ class C extends A, B { +//│ constructor() +//│ class X { +//│ constructor() +//│ fun f: 1 +//│ } +//│ } + +:e +class C extends A { + class X { fun g = 1 } +} +//│ ╔══[ERROR] Class member `X` cannot override class member of the same name declared in parent +//│ ║ l.197: class C extends A { +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ║ l.198: class X { fun g = 1 } +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.199: } +//│ ║ ^ +//│ ╟── Originally declared here: +//│ ║ l.155: class A { class X { fun f = 1 } } +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//│ class C extends A { +//│ constructor() +//│ class X { +//│ constructor() +//│ fun g: 1 +//│ } +//│ } + + + +:e +class Foo2 extends Foo2 +//│ ╔══[ERROR] Unhandled cyclic definition +//│ ║ l.221: class Foo2 extends Foo2 +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ +//│ class Foo2 extends Foo2 { +//│ constructor() +//│ } +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + + + diff --git a/shared/src/test/diff/nu/BadClassSignatures.mls b/shared/src/test/diff/nu/BadClassSignatures.mls new file mode 100644 index 0000000000..87cb924ed6 --- /dev/null +++ b/shared/src/test/diff/nu/BadClassSignatures.mls @@ -0,0 +1,187 @@ +:NewDefs + + +abstract class Foo(): Foo +//│ abstract class Foo(): Foo + +module Bar extends Foo +//│ module Bar extends Foo + +abstract class Baz(): Baz | Baz +//│ abstract class Baz(): Baz + + +abstract class Foo(): Int +//│ abstract class Foo(): Int + +fun foo(x: Foo) = x : Int +//│ fun foo: (x: Foo) -> Int + +:e +let f = Foo() : Foo +//│ ╔══[ERROR] Class Foo is abstract and cannot be instantiated +//│ ║ l.21: let f = Foo() : Foo +//│ ╙── ^^^ +//│ let f: Foo +//│ f +//│ = Foo {} + +f + 1 +//│ Int +//│ res +//│ = '[object Object]1' + +:e +Foo() + 1 +//│ ╔══[ERROR] Class Foo is abstract and cannot be instantiated +//│ ║ l.35: Foo() + 1 +//│ ╙── ^^^ +//│ Int +//│ res +//│ = '[object Object]1' + +:e +(Foo() : Foo) + 1 +//│ ╔══[ERROR] Class Foo is abstract and cannot be instantiated +//│ ║ l.44: (Foo() : Foo) + 1 +//│ ╙── ^^^ +//│ Int +//│ res +//│ = '[object Object]1' + + +:w +:e +module Foo: Int +//│ ╔══[WARNING] Self-type annotations have no effects on non-abstract module definitions +//│ ║ l.55: module Foo: Int +//│ ║ ^^^ +//│ ╙── Did you mean to use `extends` and inherit from a parent class? +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.55: module Foo: Int +//│ ║ ^^^^^^^^^^ +//│ ╟── expression of type `#Foo` is not an instance of type `Int` +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.55: module Foo: Int +//│ ╙── ^^^ +//│ module Foo: Int + +Foo + 1 +//│ Int +//│ res +//│ = '[object Object]1' + + +:w +class Foo(): {} +//│ ╔══[WARNING] Self-type annotations have no effects on non-abstract class definitions +//│ ║ l.76: class Foo(): {} +//│ ║ ^^ +//│ ╙── Did you mean to use `extends` and inherit from a parent class? +//│ class Foo() + +:w +class Foo(): {} { + fun x = 0 +} +//│ ╔══[WARNING] Self-type annotations have no effects on non-abstract class definitions +//│ ║ l.84: class Foo(): {} { +//│ ║ ^^ +//│ ╙── Did you mean to use `extends` and inherit from a parent class? +//│ class Foo() { +//│ fun x: 0 +//│ } + +:w +:e +class Foo(): { x: Int } { + fun x = 0 +} +//│ ╔══[WARNING] Self-type annotations have no effects on non-abstract class definitions +//│ ║ l.97: class Foo(): { x: Int } { +//│ ║ ^^^^^^^^^^ +//│ ╙── Did you mean to use `extends` and inherit from a parent class? +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.97: class Foo(): { x: Int } { +//│ ╙── ^ +//│ class Foo(): {x: Int} { +//│ fun x: 0 +//│ } + +abstract class Foo(): { x: Int } { + fun x = 0 +} +//│ abstract class Foo(): {x: Int} { +//│ fun x: 0 +//│ } + +:e +module FooM extends Foo +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.111: abstract class Foo(): { x: Int } { +//│ ╙── ^ +//│ module FooM extends Foo { +//│ fun x: 0 +//│ } + +abstract class Foo(): { x: Int } { + fun x: Int + fun x = 0 +} +//│ abstract class Foo(): {x: Int} { +//│ fun x: Int +//│ } + +:e // TODO should work? +module FooM extends Foo +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.127: abstract class Foo(): { x: Int } { +//│ ╙── ^ +//│ module FooM extends Foo { +//│ fun x: Int +//│ } + +:w +:e +class Foo(): { x: 'FigureItOut } { + fun x: Int + fun x = 0 +} +//│ ╔══[WARNING] Self-type annotations have no effects on non-abstract class definitions +//│ ║ l.146: class Foo(): { x: 'FigureItOut } { +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ╙── Did you mean to use `extends` and inherit from a parent class? +//│ ╔══[ERROR] Type error in type declaration +//│ ║ l.146: class Foo(): { x: 'FigureItOut } { +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.147: fun x: Int +//│ ║ ^^^^^^^^^^^^ +//│ ║ l.148: fun x = 0 +//│ ║ ^^^^^^^^^^^ +//│ ║ l.149: } +//│ ║ ^ +//│ ╟── type variable `'FigureItOut` leaks out of its scope +//│ ║ l.147: fun x: Int +//│ ╙── ^^^ +//│ class Foo(): {x: ??FigureItOut} { +//│ fun x: Int +//│ } + +:e +not(Foo().x) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.171: not(Foo().x) +//│ ║ ^^^^^^^^^^^^ +//│ ╟── field selection of type `Int & ??FigureItOut` is not an instance of type `Bool` +//│ ║ l.171: not(Foo().x) +//│ ╙── ^^^^^^^ +//│ error | false | true +//│ res +//│ = true + +(f: Foo) => f.x +//│ (f: Foo) -> (Int & ??FigureItOut) +//│ res +//│ = [Function: res] + + diff --git a/shared/src/test/diff/nu/BadClasses.mls b/shared/src/test/diff/nu/BadClasses.mls new file mode 100644 index 0000000000..4e8a3a555a --- /dev/null +++ b/shared/src/test/diff/nu/BadClasses.mls @@ -0,0 +1,159 @@ +:NewDefs + + + +mixin M0(x: Int) +//│ mixin M0(x: Int) + +:e +class C0 extends M0 +//│ ╔══[ERROR] mixin M0 expects 1 parameter(s); got 0 +//│ ║ l.9: class C0 extends M0 +//│ ╙── ^^ +//│ class C0 { +//│ constructor() +//│ } + +:e +class C0 extends M0(1, 2) +//│ ╔══[ERROR] mixin M0 expects 1 parameter(s); got 2 +//│ ║ l.18: class C0 extends M0(1, 2) +//│ ╙── ^^^^^^^ +//│ class C0 { +//│ constructor() +//│ } + +:e +class C0 extends M0(true) +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.27: class C0 extends M0(true) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── reference of type `true` is not an instance of `Int` +//│ ║ l.27: class C0 extends M0(true) +//│ ║ ^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.5: mixin M0(x: Int) +//│ ╙── ^^^ +//│ class C0 { +//│ constructor() +//│ } + + +module Foo { + fun foo = 0 + fun bar = foo +} +[Foo.foo, Foo.bar] +//│ module Foo { +//│ fun bar: 0 +//│ fun foo: 0 +//│ } +//│ [0, 0] +//│ res +//│ = [ 0, 0 ] + +// * FIXME add initialization checking for non-lazy fields +module Foo { + let foo = 0 + fun bar = foo +} +[Foo.foo, Foo.bar] +//│ module Foo { +//│ fun bar: 0 +//│ let foo: 0 +//│ } +//│ [0, 0] +//│ res +//│ = [ undefined, 0 ] + + +module Bar { + fun hello = 0 + type I = Int +} +//│ module Bar { +//│ type I = Int +//│ fun hello: 0 +//│ } + +:e +hello +//│ ╔══[ERROR] identifier not found: hello +//│ ║ l.80: hello +//│ ╙── ^^^^^ +//│ error +//│ Code generation encountered an error: +//│ unresolved symbol hello + +:e +1 : I +//│ ╔══[ERROR] type identifier not found: I +//│ ║ l.89: 1 : I +//│ ╙── ^ +//│ error +//│ res +//│ = 1 + + +:e +:w +class Foo[A] { 42: A } +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.100: class Foo[A] { 42: A } +//│ ║ ^^ +//│ ╟── integer literal of type `42` does not match type `A` +//│ ╟── Note: constraint arises from type parameter: +//│ ║ l.100: class Foo[A] { 42: A } +//│ ╙── ^ +//│ ╔══[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 type ascription: +//│ ║ l.100: class Foo[A] { 42: A } +//│ ║ ^^ +//│ ╟── expression in statement position of type `A` does not match type `()` +//│ ╟── Note: type parameter A is defined at: +//│ ║ l.100: class Foo[A] { 42: A } +//│ ╙── ^ +//│ class Foo[A] { +//│ constructor() +//│ } + + +:e +class C1 { fun oops = this.x } +//│ ╔══[ERROR] Type `#C1` does not contain member `x` +//│ ║ l.123: class C1 { fun oops = this.x } +//│ ╙── ^^ +//│ class C1 { +//│ constructor() +//│ fun oops: error +//│ } + + +:e +class C { fun x: Int } +//│ ╔══[ERROR] Member `x` is declared (or its declaration is inherited) but is not implemented in `C` +//│ ║ l.134: class C { fun x: Int } +//│ ║ ^ +//│ ╟── Declared here: +//│ ║ l.134: class C { fun x: Int } +//│ ╙── ^^^^^^^^^^ +//│ class C { +//│ constructor() +//│ fun x: Int +//│ } + +:e +class C { val x: Int } +//│ ╔══[ERROR] Member `x` is declared (or its declaration is inherited) but is not implemented in `C` +//│ ║ l.147: class C { val x: Int } +//│ ║ ^ +//│ ╟── Declared here: +//│ ║ l.147: class C { val x: Int } +//│ ╙── ^^^^^^^^^^ +//│ class C { +//│ constructor() +//│ val x: Int +//│ } + + diff --git a/shared/src/test/diff/nu/BadFieldInit.mls b/shared/src/test/diff/nu/BadFieldInit.mls new file mode 100644 index 0000000000..132ea48f4a --- /dev/null +++ b/shared/src/test/diff/nu/BadFieldInit.mls @@ -0,0 +1,132 @@ +:NewDefs + + + +:js +module A { + val x = y + val y = x +} +//│ module A { +//│ val x: nothing +//│ val y: nothing +//│ } +//│ // Prelude +//│ let res; +//│ class TypingUnit { +//│ #A; +//│ constructor() { +//│ } +//│ get A() { +//│ const qualifier = this; +//│ if (this.#A === undefined) { +//│ class A { +//│ #x; +//│ get x() { return this.#x; } +//│ #y; +//│ get y() { return this.#y; } +//│ constructor() { +//│ const qualifier1 = this; +//│ this.#x = qualifier1.y; +//│ const x = this.#x; +//│ this.#y = x; +//│ const y = this.#y; +//│ } +//│ } +//│ this.#A = new A(); +//│ this.#A.class = A; +//│ } +//│ return this.#A; +//│ } +//│ } +//│ const typing_unit = new TypingUnit; +//│ globalThis.A = typing_unit.A; +//│ // End of generated code + +[A.x, A.y] +//│ [nothing, nothing] +//│ res +//│ = [ undefined, undefined ] + + +:js +module A { + val x = y + val y = 1 +} +//│ module A { +//│ val x: 1 +//│ val y: 1 +//│ } +//│ // Prelude +//│ class TypingUnit2 { +//│ #A; +//│ constructor() { +//│ } +//│ get A() { +//│ const qualifier = this; +//│ if (this.#A === undefined) { +//│ class A { +//│ #x; +//│ get x() { return this.#x; } +//│ #y; +//│ get y() { return this.#y; } +//│ constructor() { +//│ const qualifier1 = this; +//│ this.#x = qualifier1.y; +//│ const x = this.#x; +//│ this.#y = 1; +//│ const y = this.#y; +//│ } +//│ } +//│ this.#A = new A(); +//│ this.#A.class = A; +//│ } +//│ return this.#A; +//│ } +//│ } +//│ const typing_unit2 = new TypingUnit2; +//│ globalThis.A = typing_unit2.A; +//│ // End of generated code + +[A.x, A.y] +//│ [1, 1] +//│ res +//│ = [ undefined, 1 ] + + + +:e +class B(x: Int, y: Int) { + constructor() { + x = y + y = x + } +} +//│ ╔══[ERROR] identifier not found: y +//│ ║ l.102: x = y +//│ ╙── ^ +//│ class B(x: Int, y: Int) { +//│ constructor() +//│ } +//│ Code generation encountered an error: +//│ unresolved symbol y + +:e +class B(x: Int, y: Int) { + constructor() { + x = y + y = 1 + } +} +//│ ╔══[ERROR] identifier not found: y +//│ ║ l.118: x = y +//│ ╙── ^ +//│ class B(x: Int, y: Int) { +//│ constructor() +//│ } +//│ Code generation encountered an error: +//│ unresolved symbol y + + + diff --git a/shared/src/test/diff/nu/BadMixins.mls b/shared/src/test/diff/nu/BadMixins.mls new file mode 100644 index 0000000000..2cbe310433 --- /dev/null +++ b/shared/src/test/diff/nu/BadMixins.mls @@ -0,0 +1,24 @@ +:NewDefs + + +:e +mixin M0 +M0 +//│ ╔══[ERROR] mixin M0 cannot be used in term position +//│ ║ l.6: M0 +//│ ╙── ^^ +//│ mixin M0() +//│ error +//│ res +//│ = [Function (anonymous)] + +:e +M0 +//│ ╔══[ERROR] mixin M0 cannot be used in term position +//│ ║ l.16: M0 +//│ ╙── ^^ +//│ error +//│ res +//│ = [Function (anonymous)] + + diff --git a/shared/src/test/diff/nu/BadScopes.mls b/shared/src/test/diff/nu/BadScopes.mls new file mode 100644 index 0000000000..9c66a019a9 --- /dev/null +++ b/shared/src/test/diff/nu/BadScopes.mls @@ -0,0 +1,39 @@ +:NewDefs + + +:e +mixin Foo(x: Int) +x +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.6: x +//│ ╙── ^ +//│ mixin Foo(x: Int) +//│ error +//│ Code generation encountered an error: +//│ unresolved symbol x + + +:e +class Foo(x: Int) +x +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.18: x +//│ ╙── ^ +//│ class Foo(x: Int) +//│ error +//│ Code generation encountered an error: +//│ unresolved symbol x + + +:e +class Bar { x } +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.29: class Bar { x } +//│ ╙── ^ +//│ class Bar { +//│ constructor() +//│ } +//│ Code generation encountered an error: +//│ unresolved symbol x + + diff --git a/shared/src/test/diff/nu/BadSignatures.mls b/shared/src/test/diff/nu/BadSignatures.mls new file mode 100644 index 0000000000..22bc0b8215 --- /dev/null +++ b/shared/src/test/diff/nu/BadSignatures.mls @@ -0,0 +1,102 @@ +:NewDefs + + +:e +trait T { + fun x : Int + fun x = false +} +//│ ╔══[ERROR] Method implementations in traits are not yet supported +//│ ║ l.7: fun x = false +//│ ╙── ^^^^^^^^^^^^^ +//│ trait T { +//│ fun x: Int +//│ } + + +class A { virtual fun x = 1 } +//│ class A { +//│ constructor() +//│ fun x: 1 +//│ } + +:e +class B() extends A { + fun x: Int +} +//│ ╔══[ERROR] Type mismatch in signature of member `x`: +//│ ║ l.25: fun x: Int +//│ ║ ^^^^^^ +//│ ╟── type `Int` does not match type `1` +//│ ║ l.25: fun x: Int +//│ ║ ^^^ +//│ ╟── but it flows into signature of member `x` with expected type `1` +//│ ║ l.25: fun x: Int +//│ ║ ^^^^^^ +//│ ╟── Note: constraint arises from integer literal: +//│ ║ l.17: class A { virtual fun x = 1 } +//│ ║ ^ +//│ ╟── from definition of method x: +//│ ║ l.17: class A { virtual fun x = 1 } +//│ ╙── ^^^^^ +//│ class B() extends A { +//│ fun x: Int +//│ } + +B().x +//│ Int +//│ res +//│ = 1 + +(B() : A).x +//│ 1 +//│ res +//│ = 1 + +class C() extends B { fun x = 0 } +//│ class C() extends A, B { +//│ fun x: 0 +//│ } + +(C() : A).x +//│ 1 +//│ res +//│ = 0 + +:e +class B() extends A { + fun x: Int + fun x = 1 +} +//│ ╔══[ERROR] Type mismatch in signature of member `x`: +//│ ║ l.68: fun x: Int +//│ ║ ^^^^^^ +//│ ╟── type `Int` does not match type `1` +//│ ║ l.68: fun x: Int +//│ ║ ^^^ +//│ ╟── but it flows into signature of member `x` with expected type `1` +//│ ║ l.68: fun x: Int +//│ ║ ^^^^^^ +//│ ╟── Note: constraint arises from integer literal: +//│ ║ l.17: class A { virtual fun x = 1 } +//│ ║ ^ +//│ ╟── from definition of method x: +//│ ║ l.17: class A { virtual fun x = 1 } +//│ ╙── ^^^^^ +//│ class B() extends A { +//│ fun x: Int +//│ } + + +:e +mixin M { fun x : Int } +//│ ╔══[ERROR] Member `x` is declared (or its declaration is inherited) but is not implemented in `M` +//│ ║ l.92: mixin M { fun x : Int } +//│ ║ ^ +//│ ╟── Declared here: +//│ ║ l.92: mixin M { fun x : Int } +//│ ╙── ^^^^^^^^^^^ +//│ mixin M() { +//│ fun x: Int +//│ } + diff --git a/shared/src/test/diff/nu/BadSuper.mls b/shared/src/test/diff/nu/BadSuper.mls new file mode 100644 index 0000000000..cbd0eab923 --- /dev/null +++ b/shared/src/test/diff/nu/BadSuper.mls @@ -0,0 +1,52 @@ +:NewDefs + + +mixin M0 { + fun f = 42 +} +//│ mixin M0() { +//│ fun f: 42 +//│ } + +:e +mixin M1 { + fun g = super +} +//│ ╔══[ERROR] Illegal use of `super` +//│ ║ l.13: fun g = super +//│ ╙── ^^^^^ +//│ mixin M1() { +//│ super: 'super +//│ fun g: 'super +//│ } +//│ Syntax error: +//│ 'super' keyword unexpected here + +:re +module C0 extends M0, M1 +C0.g +//│ module C0 { +//│ fun f: 42 +//│ fun g: {f: 42} +//│ } +//│ {f: 42} +//│ res +//│ Runtime error: +//│ ReferenceError: M1 is not defined + + +:e +class Foo { + fun f = super +} +//│ ╔══[ERROR] Illegal use of `super` +//│ ║ l.40: fun f = super +//│ ╙── ^^^^^ +//│ class Foo { +//│ constructor() +//│ fun f: Foo +//│ } +//│ Syntax error: +//│ 'super' keyword unexpected here + + diff --git a/shared/src/test/diff/nu/BadTraits.mls b/shared/src/test/diff/nu/BadTraits.mls new file mode 100644 index 0000000000..a0b718f8d4 --- /dev/null +++ b/shared/src/test/diff/nu/BadTraits.mls @@ -0,0 +1,16 @@ +:NewDefs + + +trait Foo +//│ trait Foo + +:e +Foo +//│ ╔══[ERROR] trait Foo cannot be used in term position +//│ ║ l.8: Foo +//│ ╙── ^^^ +//│ error +//│ Code generation encountered an error: +//│ trait used in term position + + diff --git a/shared/src/test/diff/nu/BadUCS.mls b/shared/src/test/diff/nu/BadUCS.mls new file mode 100644 index 0000000000..b0eda308c0 --- /dev/null +++ b/shared/src/test/diff/nu/BadUCS.mls @@ -0,0 +1,114 @@ +:NewDefs + + +class Foo +//│ class Foo { +//│ constructor() +//│ } + +fun foo(x) = if x is Foo then 0 +//│ fun foo: Foo -> 0 + + +module Bar { + class Foo0 +} +//│ module Bar { +//│ class Foo0 { +//│ constructor() +//│ } +//│ } + +fun foo(x) = if x is Bar then 0 +//│ fun foo: Bar -> 0 + +:e +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 +//│ ╙── ^^^^ +//│ fun foo: anything -> error +//│ Code generation encountered an error: +//│ if expression was not desugared + + +type F = Foo +//│ type F = Foo + +:e +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 +//│ ╙── ^ +//│ fun foo: anything -> error +//│ Code generation encountered an error: +//│ if expression was not desugared + +:e +fun foo(x) = if x is F() then 0 +//│ ╔══[ERROR] Illegal pattern `F` +//│ ║ l.48: fun foo(x) = if x is F() then 0 +//│ ╙── ^ +//│ fun foo: anything -> error +//│ Code generation encountered an error: +//│ if expression was not desugared + + +mixin M +//│ mixin M() + +:e +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 +//│ ╙── ^ +//│ fun foo: anything -> error +//│ Code generation encountered an error: +//│ if expression was not desugared + +:e +fun foo(x) = if x is M() then 0 +//│ ╔══[ERROR] Illegal pattern `M` +//│ ║ l.70: fun foo(x) = if x is M() then 0 +//│ ╙── ^ +//│ fun foo: anything -> error +//│ Code generation encountered an error: +//│ if expression was not desugared + + +fun foo0(x, y) = if x is y then 0 +//│ fun foo0: (anything, anything) -> 0 + + +fun foo = 0 +//│ fun foo: 0 + +:e +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 +//│ Code generation encountered an error: +//│ if expression was not desugared + +:e +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 + +module Nil +class Cons[out A](head: A, tail: Cons[A] | Nil) +//│ module Nil +//│ class Cons[A](head: A, tail: Cons[A] | Nil) + +fun join(xs) = + if xs is + Nil then "" + Cons(x, Nil) then toString(x) + Cons(x, xs') then concat(toString(x))(concat(", ")(join(xs'))) +//│ fun join: (Cons[anything] | Nil) -> Str diff --git a/shared/src/test/diff/nu/BasicClassInheritance.mls b/shared/src/test/diff/nu/BasicClassInheritance.mls new file mode 100644 index 0000000000..29907d3302 --- /dev/null +++ b/shared/src/test/diff/nu/BasicClassInheritance.mls @@ -0,0 +1,189 @@ +:NewDefs + + +class A +//│ class A { +//│ constructor() +//│ } + +class B(m: Int) extends A +//│ class B(m: Int) extends A + + +class A(n: Int) +//│ class A(n: Int) + +class B(m: Int) extends A(m + 1) +//│ class B(m: Int) extends A + + +class A { + fun a1: Int + fun a1 = 1 + fun a2 = 2 +} +//│ class A { +//│ constructor() +//│ fun a1: Int +//│ fun a2: 2 +//│ } + +class B extends A +//│ class B extends A { +//│ constructor() +//│ fun a1: Int +//│ fun a2: 2 +//│ } + + +// * Interestingly, we can currently inherit from modules... + +module C { fun test = 0 } +//│ module C { +//│ fun test: 0 +//│ } + +class D() extends C +//│ class D() extends C { +//│ fun test: 0 +//│ } + +D().test +//│ 0 +//│ res +//│ = 0 + + +class E(val m: Int) extends A { + constructor(a: Int, b: Int) { + m = a + b + log of concat("Here's m: ")(toString of m) + } +} +//│ class E(m: Int) extends A { +//│ constructor(a: Int, b: Int) +//│ fun a1: Int +//│ fun a2: 2 +//│ } + +:e +E(1).m +//│ ╔══[ERROR] Construction of class with auxiliary constructor should use the `new` keyword +//│ ║ l.70: E(1).m +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.70: E(1).m +//│ ║ ^^^^ +//│ ╟── argument of type `[1]` does not match type `[a: Int, b: Int]` +//│ ║ l.70: E(1).m +//│ ╙── ^^^ +//│ Int | error +//│ res +//│ = NaN +//│ // Output +//│ Here's m: NaN + +(new E(1, 2)).m +//│ Int +//│ res +//│ = 3 +//│ // Output +//│ Here's m: 3 + +new E(1, 2).m +//│ Int +//│ res +//│ = 3 +//│ // Output +//│ Here's m: 3 + +if new E(1, 2) is E(x) then x +//│ Int +//│ res +//│ = 3 +//│ // Output +//│ Here's m: 3 + +:e +module F extends E +//│ ╔══[ERROR] class E expects 2 parameter(s); got 0 +//│ ║ l.108: module F extends E +//│ ╙── ^ +//│ module F extends A, E { +//│ fun a1: Int +//│ fun a2: 2 +//│ } + +:e +module F extends E(123) +//│ ╔══[ERROR] class E expects 2 parameter(s); got 1 +//│ ║ l.118: module F extends E(123) +//│ ╙── ^^^^^ +//│ module F extends A, E { +//│ fun a1: Int +//│ fun a2: 2 +//│ } + +module F extends E(123, 456) +//│ module F extends A, E { +//│ fun a1: Int +//│ fun a2: 2 +//│ } + +// * Note: strangely, we see here the ctor output from the previous definitions of the F module 🤔 +F.m +//│ Int +//│ res +//│ = 579 +//│ // Output +//│ Here's m: NaN +//│ Here's m: NaN +//│ Here's m: 579 + + +class G(x: Int) extends E(x, x + 1) +//│ class G(x: Int) extends A, E { +//│ fun a1: Int +//│ fun a2: 2 +//│ } + +G(123).m +//│ Int +//│ res +//│ = 247 +//│ // Output +//│ Here's m: 247 + + +:e // TODO support +class H extends E { + constructor(a: Int, b: Int) { + super(a, b) + } +} +//│ ╔══[ERROR] class E expects 2 parameter(s); got 0 +//│ ║ l.159: class H extends E { +//│ ╙── ^ +//│ ╔══[ERROR] Illegal use of `super` +//│ ║ l.161: super(a, b) +//│ ╙── ^^^^^ +//│ ╔══[ERROR] identifier not found: super +//│ ║ l.161: super(a, b) +//│ ╙── ^^^^^ +//│ class H extends A, E { +//│ constructor(a: Int, b: Int) +//│ fun a1: Int +//│ fun a2: 2 +//│ } + +:re +new H(111, 222) +//│ H +//│ res +//│ Runtime error: +//│ ReferenceError: Super constructor may only be called once +//│ // Output +//│ Here's m: NaN +//│ Here's m: 333 + + diff --git a/shared/src/test/diff/nu/BasicClasses.mls b/shared/src/test/diff/nu/BasicClasses.mls new file mode 100644 index 0000000000..a24dc50fff --- /dev/null +++ b/shared/src/test/diff/nu/BasicClasses.mls @@ -0,0 +1,237 @@ +:NewDefs + + +class A(val n: Int) +//│ class A(n: Int) + +A +//│ (n: Int) -> A +//│ res +//│ = [Function (anonymous)] { +//│ class: [class A], +//│ unapply: [Function: unapply] +//│ } + +let a = A(42) +//│ let a: A +//│ a +//│ = A {} + +a.n +//│ Int +//│ res +//│ = 42 + + +fun f(x: A) = x.n +//│ fun f: (x: A) -> Int + +fun f(x: A) = if x is A then x.n +//│ fun f: (x: A) -> Int + +fun f(x: A | 'b) = if x is A then x.n else 0 +//│ fun f: (x: Object) -> Int + + +fun f(x) = x.n +//│ fun f: forall 'n. {n: 'n} -> 'n + +f(a) +//│ Int +//│ res +//│ = 42 + +fun f(x) = if x is A then x.n +//│ fun f: A -> Int + +f(a) +//│ Int +//│ res +//│ = 42 + +fun f(x) = if x is A then x.n else 0 +//│ fun f: Object -> Int + +f(a) +//│ Int +//│ res +//│ = 42 + + + +class C { + fun id(x) = x + fun const(x) = id +} +//│ class C { +//│ constructor() +//│ fun const: anything -> (forall 'a. 'a -> 'a) +//│ fun id: forall 'a. 'a -> 'a +//│ } + + +// TODO +class Base0(val n) { + fun me = this + fun my = this.n + fun mine = my + fun oops = this.my +} +//│ ╔══[ERROR] Class parameters currently need type annotations +//│ ║ l.74: class Base0(val n) { +//│ ╙── ^ +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.78: fun oops = this.my +//│ ╙── ^^^ +//│ class Base0(n: error) { +//│ fun me: Base0 & {n: error} +//│ fun mine: error +//│ fun my: error +//│ fun oops: error +//│ } + +// :d +// Base0 + +let b1 = Base0(42) +//│ let b1: Base0 +//│ b1 +//│ = Base0 {} + +let n1 = b1.n +//│ let n1: error +//│ n1 +//│ = 42 + +// TODO +n1 + 1 +//│ Int +//│ res +//│ = 43 + + +let b2 = Base0("hi") +let n2 = b2.n +//│ let b2: Base0 +//│ let n2: error +//│ b2 +//│ = Base0 {} +//│ n2 +//│ = 'hi' + + + +class Base1(val base: Int) { + fun getBase1 = base + fun getBase2 = this.base + fun foo(x) = this.base + x +} +//│ class Base1(base: Int) { +//│ fun foo: Int -> Int +//│ fun getBase1: Int +//│ fun getBase2: Int +//│ } + +class Base1(val base: Int) { + fun getBase1 = base + fun me = this + fun foo(x) = base + x +} +//│ class Base1(base: Int) { +//│ fun foo: Int -> Int +//│ fun getBase1: Int +//│ fun me: Base1 & {base: Int} +//│ } + +Base1 +//│ (base: Int) -> Base1 +//│ res +//│ = [Function (anonymous)] { +//│ class: [class Base1], +//│ unapply: [Function: unapply] +//│ } + +let b = Base1(1) +//│ let b: Base1 +//│ b +//│ = Base1 {} + +b.base +//│ Int +//│ res +//│ = 1 + +b.getBase1 +//│ Int +//│ res +//│ = 1 + +// :d +b.me +//│ Base1 & {base: Int} +//│ res +//│ = Base1 {} + +:e +b.getBaseTypo +//│ ╔══[ERROR] Type `Base1` does not contain member `getBaseTypo` +//│ ║ l.176: b.getBaseTypo +//│ ╙── ^^^^^^^^^^^^ +//│ error +//│ res +//│ = undefined + + +b : Base1 +//│ Base1 +//│ res +//│ = Base1 {} + + +class Rec(val n: Int) { + fun go = Rec(n + 1) +} +//│ class Rec(n: Int) { +//│ fun go: Rec +//│ } + +let r = Rec(0) +r.n +//│ let r: Rec +//│ Int +//│ r +//│ = Rec {} +//│ res +//│ = 0 + +r.go.n +//│ Int +//│ res +//│ = 1 + + + + +// TODO treat `a: Int` as a signature +class Annots(base: 0 | 1) { + a: Int + fun a = base +} +//│ ╔══[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 type ascription: +//│ ║ l.217: a: Int +//│ ║ ^ +//│ ╟── type `Int` does not match type `()` +//│ ║ l.217: a: Int +//│ ║ ^^^ +//│ ╟── but it flows into expression in statement position with expected type `()` +//│ ║ l.217: a: Int +//│ ╙── ^ +//│ class Annots(base: 0 | 1) { +//│ fun a: 0 | 1 +//│ } + + + + diff --git a/shared/src/test/diff/nu/BasicMixins.mls b/shared/src/test/diff/nu/BasicMixins.mls new file mode 100644 index 0000000000..4780ddaf18 --- /dev/null +++ b/shared/src/test/diff/nu/BasicMixins.mls @@ -0,0 +1,329 @@ +:NewDefs + + +mixin Base { + val base = 42 +} +//│ mixin Base() { +//│ val base: 42 +//│ } + +mixin BaseTest { + fun test = super.base +} +//│ mixin BaseTest() { +//│ super: {base: 'base} +//│ fun test: 'base +//│ } + + +module Base0 extends Base, BaseTest +//│ module Base0 { +//│ val base: 42 +//│ fun test: 42 +//│ } + +Base0 +//│ Base0 +//│ res +//│ = Base0 { class: [Function: Base0] } + +Base0.test +//│ 42 +//│ res +//│ = 42 + + +:e +class Base1(val base: Int) extends BaseTest { + fun test2 = [base, this.base] +} +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.38: class Base1(val base: Int) extends BaseTest { +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.39: fun test2 = [base, this.base] +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.40: } +//│ ║ ^ +//│ ╟── Object of type `anything` does not have field 'base' +//│ ║ l.38: class Base1(val base: Int) extends BaseTest { +//│ ║ ^ +//│ ╟── Note: constraint arises from field selection: +//│ ║ l.12: fun test = super.base +//│ ║ ^^^^^^^^^^ +//│ ╟── from reference: +//│ ║ l.12: fun test = super.base +//│ ╙── ^^^^^ +//│ class Base1(base: Int) { +//│ fun test: nothing +//│ fun test2: [Int, Int] +//│ } + + +mixin BaseOf(val base: Int) { + fun original = base +} +//│ mixin BaseOf(base: Int) { +//│ fun original: Int +//│ } + +mixin BaseInc { + fun base = super.base + 1 + fun test2 = [this.original, this.base] +} +//│ mixin BaseInc() { +//│ super: {base: Int} +//│ this: {base: 'base, original: 'original} +//│ fun base: Int +//│ fun test2: ['original, 'base] +//│ } + + +class Base2(x: Int) extends BaseOf(x + 1), BaseTest, BaseInc { + fun base = x + fun overridden = super.base +} +//│ class Base2(x: Int) { +//│ fun base: Int +//│ fun original: Int +//│ fun overridden: Int +//│ fun test: Int +//│ fun test2: [Int, Int] +//│ } + +Base2(11).base +//│ Int +//│ res +//│ = 11 + +Base2(11).overridden +//│ Int +//│ res +//│ = 13 + +Base2(11).test +//│ Int +//│ res +//│ = 12 + +Base2(11).test2 +//│ [Int, Int] +//│ res +//│ = [ 12, 11 ] + + +:e // TODO +class Base2(x) extends BaseOf(x + 1), BaseTest +//│ ╔══[ERROR] Class parameters currently need type annotations +//│ ║ l.116: class Base2(x) extends BaseOf(x + 1), BaseTest +//│ ╙── ^ +//│ class Base2(x: error) { +//│ fun original: Int +//│ fun test: Int +//│ } + +:w +:e +class Base1(x: Int): BaseTest +//│ ╔══[WARNING] Self-type annotations have no effects on non-abstract class definitions +//│ ║ l.127: class Base1(x: Int): BaseTest +//│ ║ ^^^^^^^^ +//│ ╙── Did you mean to use `extends` and inherit from a parent class? +//│ ╔══[ERROR] mixin BaseTest cannot be used as a type +//│ ║ l.127: class Base1(x: Int): BaseTest +//│ ╙── ^^^^^^^^ +//│ class Base1(x: Int): BaseTest + +Base1 +//│ (x: Int) -> Base1 +//│ res +//│ = [Function (anonymous)] { +//│ class: [class Base1], +//│ unapply: [Function: unapply] +//│ } + +:e +1 : BaseTest +//│ ╔══[ERROR] mixin BaseTest cannot be used as a type +//│ ║ l.146: 1 : BaseTest +//│ ╙── ^^^^^^^^ +//│ BaseTest +//│ res +//│ = 1 + +:e +error : BaseTest +//│ ╔══[ERROR] mixin BaseTest cannot be used as a type +//│ ║ l.155: error : BaseTest +//│ ╙── ^^^^^^^^ +//│ BaseTest +//│ res +//│ Runtime error: +//│ Error: an error was thrown + + +// :ns +mixin Foo { + fun test(x) = [super.base + x, x, super.misc] +} +//│ mixin Foo() { +//│ super: {base: Int, misc: 'misc} +//│ fun test: (Int & 'a) -> [Int, 'a, 'misc] +//│ } + +:e +module Base1(base: Int, misc: string) extends Foo +//│ ╔══[ERROR] Module parameters are not supported +//│ ║ l.175: module Base1(base: Int, misc: string) extends Foo +//│ ╙── ^^^^^^^^^^^^^^^ +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.175: module Base1(base: Int, misc: string) extends Foo +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── Object of type `anything` does not have field 'misc' +//│ ║ l.175: module Base1(base: Int, misc: string) extends Foo +//│ ║ ^ +//│ ╟── Note: constraint arises from field selection: +//│ ║ l.167: fun test(x) = [super.base + x, x, super.misc] +//│ ║ ^^^^^^^^^^ +//│ ╟── from reference: +//│ ║ l.167: fun test(x) = [super.base + x, x, super.misc] +//│ ╙── ^^^^^ +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.175: module Base1(base: Int, misc: string) extends Foo +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── Object of type `anything` does not have field 'base' +//│ ║ l.175: module Base1(base: Int, misc: string) extends Foo +//│ ║ ^ +//│ ╟── Note: constraint arises from field selection: +//│ ║ l.167: fun test(x) = [super.base + x, x, super.misc] +//│ ║ ^^^^^^^^^^ +//│ ╟── from reference: +//│ ║ l.167: fun test(x) = [super.base + x, x, super.misc] +//│ ╙── ^^^^^ +//│ module Base1(base: Int, misc: string) { +//│ fun test: forall 'a. (Int & 'a) -> [Int, 'a, nothing] +//│ } + +:e +Base1.test +//│ ╔══[ERROR] Parameterized modules are not yet supported +//│ ║ l.208: Base1.test +//│ ╙── ^^^^^ +//│ ╔══[ERROR] Type mismatch in field selection: +//│ ║ l.208: Base1.test +//│ ║ ^^^^^^^^^^ +//│ ╟── reference of type `(base: Int, misc: string) -> Base1` does not have field 'test' +//│ ║ l.208: Base1.test +//│ ╙── ^^^^^ +//│ error +//│ res +//│ = [Function: test] + + +mixin WrapBase { + // fun wrap(x) = x + // fun wrap(x) = x : Int + fun wrapA(x: Int) = x : Int + fun wrap(x) = x +} +//│ mixin WrapBase() { +//│ fun wrap: 'a -> 'a +//│ fun wrapA: (x: Int) -> Int +//│ } + +// :d +mixin Wrap { + fun wrapA(x) = [super.wrapA(x)] + fun wrap(x) = [super.wrap(x)] +} +//│ mixin Wrap() { +//│ super: {wrap: 'a -> 'b, wrapA: 'c -> 'd} +//│ fun wrap: 'a -> ['b] +//│ fun wrapA: 'c -> ['d] +//│ } + + + +// :d +module WrapBase1 extends WrapBase, Wrap +//│ module WrapBase1 { +//│ fun wrap: 'a -> ['a] +//│ fun wrapA: Int -> [Int] +//│ } + + +WrapBase1 +//│ WrapBase1 +//│ res +//│ = WrapBase1 { class: [Function: WrapBase1] } + +// :d +WrapBase1.wrapA +//│ Int -> [Int] +//│ res +//│ = [Function: wrapA] + +WrapBase1.wrap +//│ 'a -> ['a] +//│ res +//│ = [Function: wrap] + +// :d +// WrapBase1.wrap + + +WrapBase1.wrap(1) +//│ [1] +//│ res +//│ = [ 1 ] + +WrapBase1.wrap("ok") +//│ ["ok"] +//│ res +//│ = [ 'ok' ] + + +WrapBase1.wrapA(1) +//│ [Int] +//│ res +//│ = [ 1 ] + +:e +WrapBase1.wrapA("ok") +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.292: WrapBase1.wrapA("ok") +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── string literal of type `"ok"` is not an instance of type `Int` +//│ ║ l.292: WrapBase1.wrapA("ok") +//│ ║ ^^^^ +//│ ╟── Note: constraint arises from reference: +//│ ║ l.236: fun wrapA(x) = [super.wrapA(x)] +//│ ╙── ^ +//│ error | [Int] +//│ res +//│ = [ 'ok' ] + + + +module WrapBase2 extends WrapBase, Wrap, Wrap, Wrap +//│ module WrapBase2 { +//│ fun wrap: 'a -> [[['a]]] +//│ fun wrapA: Int -> [[[Int]]] +//│ } + +let w = WrapBase2.wrap +//│ let w: 'a -> [[['a]]] +//│ w +//│ = [Function: wrap] + +let wd = w(1) +//│ let wd: [[[1]]] +//│ wd +//│ = [ [ [ 1 ] ] ] + +wd.0.0.0 + 1 +//│ Int +//│ res +//│ = 2 + + diff --git a/shared/src/test/diff/nu/CallByName.mls b/shared/src/test/diff/nu/CallByName.mls new file mode 100644 index 0000000000..6258b19520 --- /dev/null +++ b/shared/src/test/diff/nu/CallByName.mls @@ -0,0 +1,39 @@ +:NewDefs + + +let x = log("ok") +//│ let x: () +//│ x +//│ = undefined +//│ // Output +//│ ok + +x +//│ () +//│ res +//│ = undefined + +x +//│ () +//│ res +//│ = undefined + + +fun x = log("ok") +//│ fun x: () + +x +//│ () +//│ res +//│ = undefined +//│ // Output +//│ ok + +x +//│ () +//│ res +//│ = undefined +//│ // Output +//│ ok + + diff --git a/shared/src/test/diff/nu/CaseExpr.mls b/shared/src/test/diff/nu/CaseExpr.mls new file mode 100644 index 0000000000..c6ebf859ed --- /dev/null +++ b/shared/src/test/diff/nu/CaseExpr.mls @@ -0,0 +1,165 @@ +:NewDefs + + +case 0 then true +//│ 0 -> true +//│ res +//│ = [Function: res] + +:pe // TODO support +case 0 then true, 1 then false +//│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead +//│ ║ l.10: case 0 then true, 1 then false +//│ ╙── ^^^^^^^^^^^^ +//│ 0 -> () +//│ res +//│ = [Function: res] + +case + 0 then true + 1 then false +//│ (0 | 1) -> Bool +//│ res +//│ = [Function: res] + + +fun foo = case + 0 then true + 1 then false +//│ fun foo: (0 | 1) -> Bool + +[foo(0), foo(1)] +//│ [Bool, Bool] +//│ res +//│ = [ true, false ] + + + +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 + + +fun map(f) = case + Some(x) then Some(f(x)) + None then None +//│ fun map: forall 'a 'A. ('a -> 'A) -> (None | Some['a]) -> (None | Some['A]) + +map(succ) of Some of 123 +//│ None | Some[Int] +//│ res +//│ = Some {} + +map(succ) of None +//│ None | Some[Int] +//│ res +//│ = 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] Illegal pattern `as` +//│ ║ l.65: None as n then n +//│ ╙── ^^ +//│ fun map: anything -> anything -> error +//│ Code generation encountered an error: +//│ if expression was not desugared + + +:pe +case 1 +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'case'; found integer literal instead +//│ ║ l.75: case 1 +//│ ║ ^ +//│ ╟── Note: 'case' expression starts here: +//│ ║ l.75: case 1 +//│ ╙── ^^^^ +//│ anything -> 1 +//│ res +//│ = [Function: res] + +:pe +case (1 then true) +//│ ╔══[PARSE ERROR] Unexpected 'then' keyword here +//│ ║ l.87: case (1 then true) +//│ ╙── ^^^^ +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'case'; found integer literal instead +//│ ║ l.87: case (1 then true) +//│ ║ ^^^^^^^^^^^^^ +//│ ╟── Note: 'case' expression starts here: +//│ ║ l.87: case (1 then true) +//│ ╙── ^^^^ +//│ anything -> 1 +//│ res +//│ = [Function: res] + +case else 0 +//│ anything -> 0 +//│ res +//│ = [Function: res] + +:pe +case then 1 else 0 +//│ ╔══[PARSE ERROR] Unexpected 'then' keyword in expression position +//│ ║ l.107: case then 1 else 0 +//│ ╙── ^^^^ +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'case'; found integer literal instead +//│ ║ l.107: case then 1 else 0 +//│ ║ ^ +//│ ╟── Note: 'case' expression starts here: +//│ ║ l.107: case then 1 else 0 +//│ ╙── ^^^^ +//│ ╔══[PARSE ERROR] Expected end of input; found 'else' keyword instead +//│ ║ l.107: case then 1 else 0 +//│ ╙── ^^^^ +//│ anything -> 1 +//│ res +//│ = [Function: res] + + + +// TODO: + +:pe +: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 +//│ ╙── ^^^^^^^^^^^^ +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'case'; found operator application instead +//│ ║ l.130: case x, y then x + y +//│ ║ ^^^^^^^^^^^^^^^ +//│ ╟── Note: 'case' expression starts here: +//│ ║ l.130: case x, y then x + y +//│ ╙── ^^^^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.130: case x, y then x + y +//│ ╙── ^ +//│ anything -> () +//│ Code generation encountered an error: +//│ unresolved symbol x + +:e +case (x, y) then x + y +//│ ╔══[ERROR] Illegal pattern `,` +//│ ║ l.148: 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 + + + diff --git a/shared/src/test/diff/nu/ClassField.mls b/shared/src/test/diff/nu/ClassField.mls new file mode 100644 index 0000000000..a74222dfd1 --- /dev/null +++ b/shared/src/test/diff/nu/ClassField.mls @@ -0,0 +1,83 @@ +:NewDefs + + + +class Foo(x: Int) +//│ class Foo(x: Int) + +Foo +//│ (x: Int) -> Foo +//│ res +//│ = [Function (anonymous)] { +//│ class: [class Foo], +//│ unapply: [Function: unapply] +//│ } + +typeof(Foo) +//│ Str +//│ res +//│ = 'function' + +let f = Foo(123) +//│ let f: Foo +//│ f +//│ = Foo {} + +typeof(f) +//│ Str +//│ res +//│ = 'object' + +:e +let cls = Foo.class +//│ ╔══[ERROR] Type mismatch in field selection: +//│ ║ l.32: let cls = Foo.class +//│ ║ ^^^^^^^^^ +//│ ╟── reference of type `(x: Int) -> Foo` does not have field 'class' +//│ ║ l.32: let cls = Foo.class +//│ ╙── ^^^ +//│ let cls: error +//│ cls +//│ = [class Foo] + +typeof(cls) +//│ Str +//│ res +//│ = 'function' + + + +mixin Base +//│ mixin Base() + +class Derived() extends Base +//│ class Derived() + +// * Strangely, we now get `{ class: [Function: Derived] }` +Derived +//│ () -> Derived +//│ res +//│ = [Function (anonymous)] { +//│ class: [Function: Derived], +//│ unapply: [Function: unapply] +//│ } + +:e +let cls = Derived.class +//│ ╔══[ERROR] Type mismatch in field selection: +//│ ║ l.66: let cls = Derived.class +//│ ║ ^^^^^^^^^^^^^ +//│ ╟── reference of type `() -> Derived` does not have field 'class' +//│ ║ l.66: let cls = Derived.class +//│ ╙── ^^^^^^^ +//│ let cls: error +//│ cls +//│ = [Function: Derived] + +typeof(cls) +//│ Str +//│ res +//│ = 'function' + + + diff --git a/shared/src/test/diff/nu/ClassInstantiation.mls b/shared/src/test/diff/nu/ClassInstantiation.mls new file mode 100644 index 0000000000..31b1a6066d --- /dev/null +++ b/shared/src/test/diff/nu/ClassInstantiation.mls @@ -0,0 +1,61 @@ +// *** Class instantiation tests *** // + +:NewDefs + + +class C +//│ class C { +//│ constructor() +//│ } + +new C +//│ C +//│ res +//│ = C {} + +// * TODO decide: forbid? +new C() +//│ C +//│ res +//│ = C {} + +:e +C() +//│ ╔══[ERROR] Construction of unparameterized class C should use the `new` keyword +//│ ║ l.23: C() +//│ ╙── ^ +//│ C +//│ res +//│ Runtime error: +//│ TypeError: Class constructor C cannot be invoked without 'new' + + +class D(x: Int) +//│ class D(x: Int) + +:js +D(0) +//│ D +//│ // Prelude +//│ class TypingUnit5 {} +//│ const typing_unit5 = new TypingUnit5; +//│ // Query 1 +//│ res = D(0); +//│ // End of generated code +//│ res +//│ = D {} + +// * TODO decide: reject or accept? +:js +new D(0) +//│ D +//│ // Prelude +//│ class TypingUnit6 {} +//│ const typing_unit6 = new TypingUnit6; +//│ // Query 1 +//│ res = new D.class(0); +//│ // End of generated code +//│ res +//│ = D {} + + diff --git a/shared/src/test/diff/nu/ClassSignatures.mls b/shared/src/test/diff/nu/ClassSignatures.mls new file mode 100644 index 0000000000..20c59c9429 --- /dev/null +++ b/shared/src/test/diff/nu/ClassSignatures.mls @@ -0,0 +1,168 @@ +:NewDefs + + +abstract class C0: C1 | C2 +class C1() extends C0 +module C2 extends C0 +//│ abstract class C0: C1 | C2 +//│ class C1() extends C0 +//│ module C2 extends C0 + +fun foo(x: C0) = if x is + C1 then 1 + C2 then 2 +//│ fun foo: (x: C0) -> (1 | 2) + +fun foo(x: C0) = x : C1 | C2 +//│ fun foo: (x: C0) -> (C1 | C2) + +fun foo(x) = if x is + C1 then 1 + C2 then 2 +//│ fun foo: (C1 | C2) -> (1 | 2) + +foo(C1()) + foo(C2) +//│ Int +//│ res +//│ = 3 + + +:e +class C3 extends C0 +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.31: class C3 extends C0 +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ╟── expression of type `#C3` does not match type `C1 | C2` +//│ ╟── Note: constraint arises from union type: +//│ ║ l.4: abstract class C0: C1 | C2 +//│ ╙── ^^^^^^^ +//│ class C3 extends C0 { +//│ constructor() +//│ } + + +trait B +//│ trait B + +:w +:e +class A(): B +//│ ╔══[WARNING] Self-type annotations have no effects on non-abstract class definitions +//│ ║ l.49: class A(): B +//│ ║ ^ +//│ ╙── Did you mean to use `extends` and inherit from a parent class? +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.49: class A(): B +//│ ║ ^^^^^^^^^ +//│ ╟── expression of type `#A` is not an instance of type `B` +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.49: class A(): B +//│ ╙── ^ +//│ class A(): B + +A() : B +//│ B +//│ res +//│ = A {} + +abstract class A(): B +//│ abstract class A(): B + +:e +A() : B +//│ ╔══[ERROR] Class A is abstract and cannot be instantiated +//│ ║ l.72: A() : B +//│ ╙── ^ +//│ B +//│ res +//│ = A {} + +module C extends A, B +//│ module C extends A, B + +C : B +//│ B +//│ res +//│ = C { class: [class C extends A] } + + +:w +:e +class A(): C +class B() extends A +class C() extends B +//│ ╔══[WARNING] Self-type annotations have no effects on non-abstract class definitions +//│ ║ l.91: class A(): C +//│ ║ ^ +//│ ╙── Did you mean to use `extends` and inherit from a parent class? +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.91: class A(): C +//│ ║ ^^^^^^^^^ +//│ ╟── expression of type `#A` is not an instance of type `C` +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.91: class A(): C +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.92: class B() extends A +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ╟── expression of type `#B` is not an instance of type `C` +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.91: class A(): C +//│ ╙── ^ +//│ class A(): C +//│ class B() extends A +//│ class C() extends A, B + + +:e +abstract class A(): C +class B() extends A +class C() extends B +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.119: class B() extends A +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ╟── expression of type `#B` is not an instance of type `C` +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.118: abstract class A(): C +//│ ╙── ^ +//│ abstract class A(): C +//│ class B() extends A +//│ class C() extends A, B + + +abstract class A(): C +abstract class B() extends A +class C() extends B +//│ abstract class A(): C +//│ abstract class B(): C extends A +//│ class C() extends A, B + + +// * Transitively-inherited self-types are checked + +:e +class D() extends B +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.144: class D() extends B +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ╟── expression of type `#D` is not an instance of type `C` +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.133: abstract class A(): C +//│ ╙── ^ +//│ class D() extends A, B + + +// * Transitively-inherited self-types can be leveraged + +fun test(x: B): C = x +//│ fun test: (x: B) -> C + +fun test[A](x: B & A): C = x +//│ fun test: (x: B) -> C + +trait T +fun test(x: B & T): C = x +//│ trait T +//│ fun test: (x: B & T) -> C + + diff --git a/shared/src/test/diff/nu/ClassesInMixins.mls b/shared/src/test/diff/nu/ClassesInMixins.mls new file mode 100644 index 0000000000..eb76284141 --- /dev/null +++ b/shared/src/test/diff/nu/ClassesInMixins.mls @@ -0,0 +1,100 @@ +:NewDefs + + + +mixin Test { + class Foo(val n: Int) + val f = Foo(123) +} +//│ mixin Test() { +//│ class Foo(n: Int) +//│ val f: Foo +//│ } + +module M extends Test +//│ module M { +//│ class Foo(n: Int) +//│ val f: Foo +//│ } + +M.f +//│ Foo +//│ res +//│ = Foo {} + +M.f.n +//│ Int +//│ res +//│ = 123 + +:e +M.Foo +//│ ╔══[ERROR] Access to class member not yet supported +//│ ║ l.31: M.Foo +//│ ╙── ^^^^ +//│ error +//│ res +//│ = [Function (anonymous)] { +//│ class: [class Foo], +//│ unapply: [Function: unapply] +//│ } + +:e // TODO support +fun foo(x) = if x is M.Foo then 1 +//│ ╔══[ERROR] illegal pattern +//│ ║ 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 + +:e +mixin Test2 { let f = Foo(1) } +//│ ╔══[ERROR] identifier not found: Foo +//│ ║ l.52: mixin Test2 { let f = Foo(1) } +//│ ╙── ^^^ +//│ mixin Test2() { +//│ let f: error +//│ } +//│ Code generation encountered an error: +//│ unresolved symbol Foo + +: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 } +//│ ╙── ^^^ +//│ mixin Test3() { +//│ fun f: anything -> error +//│ } +//│ Code generation encountered an error: +//│ if expression was not desugared + + + +:e +mixin Test { + class Lit(n: Int) + class Add(lhs: A, rhs: A) { + // Should be a lazy val only forceable when A has the right shape (constrained types?): + fun cached = size(this) + } + fun size(x) = if x is + Add(l, r) then this.size(l) + this.size(r) +} +//│ ╔══[ERROR] Type error in application +//│ ║ l.80: fun cached = size(this) +//│ ║ ^^^^^^^^^^ +//│ ╟── type variable `A` leaks out of its scope +//│ ║ l.78: class Add(lhs: A, rhs: A) { +//│ ╙── ^ +//│ mixin Test() { +//│ this: {size: (??A | 'a) -> Int} +//│ class Add[A](lhs: A, rhs: A) { +//│ fun cached: Int | error +//│ } +//│ class Lit(n: Int) +//│ fun size: Add[??A0 & 'a] -> Int +//│ } + + diff --git a/shared/src/test/diff/nu/CommaOperator.mls b/shared/src/test/diff/nu/CommaOperator.mls new file mode 100644 index 0000000000..6d7e46db0b --- /dev/null +++ b/shared/src/test/diff/nu/CommaOperator.mls @@ -0,0 +1,424 @@ +:NewDefs + + +1; 2 +//│ 2 +//│ res +//│ = 1 +//│ res +//│ = 2 + +// :dp +:js +1, 2 +//│ 2 +//│ // Prelude +//│ class TypingUnit1 {} +//│ const typing_unit1 = new TypingUnit1; +//│ // Query 1 +//│ res = (1 , 2); +//│ // End of generated code +//│ res +//│ = 2 + +(1, 2) +//│ 2 +//│ res +//│ = 2 + +(1, 2, 3) +//│ 3 +//│ res +//│ = 3 + +1, () +//│ () +//│ res +//│ = undefined + +log(1), 2 +//│ 2 +//│ res +//│ = 2 +//│ // Output +//│ 1 + + +(), +1 +//│ 1 +//│ res +//│ = 1 + +:pe +(), +1, +//│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here +//│ ║ l.55: 1, +//│ ╙── ^ +//│ () +//│ res +//│ = undefined + +(), + 1 +//│ 1 +//│ res +//│ = 1 + +(), + log("ok") + 1 +//│ 1 +//│ res +//│ = 1 +//│ // Output +//│ ok + +:w +(), + 2 + 2 + 1 +//│ ╔══[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 operator application: +//│ ║ l.80: 2 + 2 +//│ ║ ^^^^^ +//│ ╙── operator application of type `Int` does not match type `()` +//│ 1 +//│ res +//│ = 1 + +(), + 2 + 2, () + 1 +//│ 1 +//│ res +//│ = 1 + + + +let a = 1, 2 +//│ let a: 2 +//│ a +//│ = 2 + +let a = 1; 2 +//│ let a: 1 +//│ 2 +//│ a +//│ = 1 +//│ res +//│ = 2 + +:pe // TODO support; make `;` have even less precedence than `,` +(1;2) +//│ ╔══[PARSE ERROR] Unexpected semicolon here +//│ ║ l.115: (1;2) +//│ ╙── ^ +//│ 1 +//│ res +//│ = 1 + +:pe +(1,2;3) +//│ ╔══[PARSE ERROR] Unexpected semicolon here +//│ ║ l.124: (1,2;3) +//│ ╙── ^ +//│ 2 +//│ res +//│ = 2 + +(log(1), 2) +//│ 2 +//│ res +//│ = 2 +//│ // Output +//│ 1 + + + +fun foo(x) = x +//│ fun foo: forall 'a. 'a -> 'a + +:pe +foo(1;2) +//│ ╔══[PARSE ERROR] Unexpected semicolon here +//│ ║ l.145: foo(1;2) +//│ ╙── ^ +//│ 1 +//│ res +//│ = 1 + +foo(let x = 1; x + 1) +//│ Int +//│ res +//│ = 2 + +:js +foo(let x = log(0), 1; log(x), x + 1) +//│ Int +//│ // Prelude +//│ class TypingUnit20 {} +//│ const typing_unit20 = new TypingUnit20; +//│ // Query 1 +//│ res = foo(((x) => (log(x) , x + 1))((log(0) , 1))); +//│ // End of generated code +//│ res +//│ = 2 +//│ // Output +//│ 0 +//│ 1 + + +:js +foo((1, 2)) +//│ 2 +//│ // Prelude +//│ class TypingUnit21 {} +//│ const typing_unit21 = new TypingUnit21; +//│ // Query 1 +//│ res = foo((1 , 2)); +//│ // End of generated code +//│ res +//│ = 2 + +foo(let x = 1; x + 1) +//│ Int +//│ res +//│ = 2 + +foo(let x = 1 in x + 1) +//│ Int +//│ res +//│ = 2 + +foo((log(1), 2)) +//│ 2 +//│ res +//│ = 2 +//│ // Output +//│ 1 + +foo(1), 2 +//│ 2 +//│ res +//│ = 2 + + +:ge // FIXME +let rec x() = x() in x +//│ nothing +//│ Code generation encountered an error: +//│ recursive non-function definition x is not supported + + +: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 +//│ ╙── ^^^ +//│ 1 +//│ res +//│ = 1 + + +let x = 2 in log(x), x + 1 +//│ Int +//│ res +//│ = 3 +//│ // Output +//│ 2 + +let x = 2; log(x), x + 1 +//│ let x: 2 +//│ Int +//│ x +//│ = 2 +//│ res +//│ = 3 +//│ // Output +//│ 2 + + + +fun foo(x, y) = [x, y] +//│ fun foo: forall 'a 'b. ('a, 'b) -> ['a, 'b] + +foo(1,2) +//│ [1, 2] +//│ res +//│ = [ 1, 2 ] + +foo(log(1),2) +//│ [(), 2] +//│ res +//│ = [ undefined, 2 ] +//│ // Output +//│ 1 + +foo( + log(1), + 2 +) +//│ [(), 2] +//│ res +//│ = [ undefined, 2 ] +//│ // Output +//│ 1 + +foo( + log(1), + 2, +) +//│ [(), 2] +//│ res +//│ = [ undefined, 2 ] +//│ // Output +//│ 1 + +:pe +:e +foo(log(1);2) +//│ ╔══[PARSE ERROR] Unexpected semicolon here +//│ ║ l.282: foo(log(1);2) +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.282: foo(log(1);2) +//│ ║ ^^^^^^^^^^^^^ +//│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` +//│ ║ l.282: foo(log(1);2) +//│ ║ ^^^^^^^^^^ +//│ ╟── Note: constraint arises from tuple literal: +//│ ║ l.245: fun foo(x, y) = [x, y] +//│ ╙── ^^^^^^ +//│ error | [nothing, nothing] +//│ res +//│ = [ undefined, undefined ] +//│ // Output +//│ 1 + +:e +foo((log(1),2)) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.302: foo((log(1),2)) +//│ ║ ^^^^^^^^^^^^^^^ +//│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` +//│ ║ l.302: foo((log(1),2)) +//│ ║ ^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from tuple literal: +//│ ║ l.245: fun foo(x, y) = [x, y] +//│ ╙── ^^^^^^ +//│ error | [nothing, nothing] +//│ res +//│ = [ 2, undefined ] +//│ // Output +//│ 1 + +foo((let x = log(0), 1; log(x), x + 1), 2) +//│ [Int, 2] +//│ res +//│ = [ 2, 2 ] +//│ // Output +//│ 0 +//│ 1 + +: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) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` +//│ ║ l.327: foo(let x = log(0), 1; log(x), x + 1) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from tuple literal: +//│ ║ l.245: fun foo(x, y) = [x, y] +//│ ╙── ^^^^^^ +//│ error | [nothing, nothing] +//│ res +//│ = [ 2, undefined ] +//│ // Output +//│ 0 +//│ 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) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` +//│ ║ l.345: 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] +//│ ╙── ^^^^^^ +//│ error | [nothing, nothing] +//│ res +//│ = [ 2, undefined ] +//│ // Output +//│ 0 +//│ 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) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` +//│ ║ l.363: foo(let x = log(0), 1; log(x), 1 + 1) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from tuple literal: +//│ ║ l.245: fun foo(x, y) = [x, y] +//│ ╙── ^^^^^^ +//│ error | [nothing, nothing] +//│ res +//│ = [ 2, undefined ] +//│ // Output +//│ 0 +//│ 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) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── argument of type `[1 | ?a]` does not match type `[?b, ?c]` +//│ ║ l.381: foo(if true then 1 else 2, 3) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from tuple literal: +//│ ║ l.245: fun foo(x, y) = [x, y] +//│ ╙── ^^^^^^ +//│ error | [nothing, nothing] +//│ res +//│ = [ 1, undefined ] + +foo((if true then 1 else 2), 3) +//│ [1 | 2, 3] +//│ res +//│ = [ 1, 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) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── 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) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from tuple literal: +//│ ║ l.245: fun foo(x, y) = [x, y] +//│ ╙── ^^^^^^ +//│ error | [nothing, nothing] +//│ res +//│ = [ 1, undefined ] +//│ // Output +//│ ok + +foo((if true then log("ok"), 1 else log("nok"), 2), 3) +//│ [1 | 2, 3] +//│ res +//│ = [ 1, 3 ] +//│ // Output +//│ ok + + diff --git a/shared/src/test/diff/nu/CtorSubtraction.mls b/shared/src/test/diff/nu/CtorSubtraction.mls new file mode 100644 index 0000000000..9a58ce6842 --- /dev/null +++ b/shared/src/test/diff/nu/CtorSubtraction.mls @@ -0,0 +1,37 @@ +:NewDefs + + +class Cls +//│ class Cls { +//│ constructor() +//│ } + +fun x: ('a & Object) -> ('a \ Cls) +fun x = case + Cls then error + y then y +//│ fun x: forall 'b. (Cls | Object & 'b & ~#Cls) -> 'b +//│ fun x: forall 'a. (Object & 'a) -> ('a & ~Cls) + +x : Int -> Int +//│ Int -> Int +//│ res +//│ = [Function: x] + +x : Cls -> nothing +//│ Cls -> nothing +//│ res +//│ = [Function: x] + + +fun x: (Int | Str | Cls) \ Cls +fun x = 42 +//│ fun x: 42 +//│ fun x: Int & ~Cls | Str & ~Cls + +x : Int | Str +//│ Int | Str +//│ res +//│ = 42 + + diff --git a/shared/src/test/diff/nu/Dates.mls b/shared/src/test/diff/nu/Dates.mls new file mode 100644 index 0000000000..dd5a5fc15c --- /dev/null +++ b/shared/src/test/diff/nu/Dates.mls @@ -0,0 +1,25 @@ +:NewDefs + + +declare class Date { + constructor(date: Num) + fun toString(): Str + fun toLocaleString(locales: Str | Array[Str], options: anything): Str +} +//│ declare class Date { +//│ constructor(date: Num) +//│ fun toLocaleString: (locales: Str | Array[Str], options: anything) -> Str +//│ fun toString: () -> Str +//│ } + +let date1 = new Date(12345678) +//│ let date1: Date +//│ date1 +//│ = 1970-01-01T03:25:45.678Z + +date1.toLocaleString("en-US", { timeZone: "America/New_York" }) +//│ Str +//│ res +//│ = '12/31/1969, 10:25:45 PM' + + diff --git a/shared/src/test/diff/nu/DecLit.mls b/shared/src/test/diff/nu/DecLit.mls new file mode 100644 index 0000000000..1714d7b7d3 --- /dev/null +++ b/shared/src/test/diff/nu/DecLit.mls @@ -0,0 +1,139 @@ +:NewDefs + +// Real Numbers +// ============ + +[0.5, 1.0, 3.14159] +//│ [0.5, 1.0, 3.14159] +//│ res +//│ = [ 0.5, 1, 3.14159 ] + +[1e100, 1E100, 1e+100, 1E+100, 1E-11, 1e-10, 1E-2, 1e-2] +//│ [1E+100, 1E+100, 1E+100, 1E+100, 1E-11, 1E-10, 0.01, 0.01] +//│ res +//│ = [ +//│ 1e+100, 1e+100, +//│ 1e+100, 1e+100, +//│ 1e-11, 1e-10, +//│ 0.01, 0.01 +//│ ] + +[3.14e-10, 3.14E-10, 3.14e+10, 3.14E+10] +//│ [3.14E-10, 3.14E-10, 3.14E+10, 3.14E+10] +//│ res +//│ = [ 3.14e-10, 3.14e-10, 31400000000, 31400000000 ] + +[0.5e-10, 0.5E-10, 0.5e+10, 0.5E+10] +//│ [5E-11, 5E-11, 5E+9, 5E+9] +//│ res +//│ = [ 5e-11, 5e-11, 5000000000, 5000000000 ] + +// Separators in integral, fractional, and exponent parts. +[12_34_56.0, 12_34_56.78_90] +[1_2.3_4e-1_0, 1_2.3_4e+1_0, 1_2.3_4e1_0] +[1_2.3_4E-1_0, 1_2.3_4E+1_0, 1_2.3_4E1_0] +//│ [1.234E-9, 1.234E+11, 1.234E+11] +//│ res +//│ = [ 123456, 123456.789 ] +//│ res +//│ = [ 1.234e-9, 123400000000, 123400000000 ] +//│ res +//│ = [ 1.234e-9, 123400000000, 123400000000 ] + +// Conflict with tuple index selection. +:pe +.1 +//│ ╔══[PARSE ERROR] Unexpected selector in expression position +//│ ║ l.45: .1 +//│ ╙── ^^ +//│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here +//│ ║ l.45: .1 +//│ ╙── ^ +//│ () +//│ res +//│ = undefined + +// Corner cases. +:pe +0.E10 +//│ ╔══[LEXICAL ERROR] Expect at least one digit after the decimal point +//│ ║ l.58: 0.E10 +//│ ╙── ^ +//│ 0E+10 +//│ res +//│ = 0 + +:pe +0.0E +//│ ╔══[LEXICAL ERROR] Expect at least one digit after the exponent sign +//│ ║ l.67: 0.0E +//│ ╙── ^ +//│ 0.0 +//│ res +//│ = 0 + +:pe +0.0E+ +//│ ╔══[LEXICAL ERROR] Expect at least one digit after the exponent sign +//│ ║ l.76: 0.0E+ +//│ ╙── ^ +//│ 0.0 +//│ res +//│ = 0 + +:pe +0E +//│ ╔══[LEXICAL ERROR] Expect at least one digit after the exponent sign +//│ ║ l.85: 0E +//│ ╙── ^ +//│ 0 +//│ res +//│ = 0 + +:pe +0E+ +//│ ╔══[LEXICAL ERROR] Expect at least one digit after the exponent sign +//│ ║ l.94: 0E+ +//│ ╙── ^ +//│ 0 +//│ res +//│ = 0 + +:pe +1234E +//│ ╔══[LEXICAL ERROR] Expect at least one digit after the exponent sign +//│ ║ l.103: 1234E +//│ ╙── ^ +//│ 1234 +//│ res +//│ = 1234 + +:pe +4378. +//│ ╔══[LEXICAL ERROR] Expect at least one digit after the decimal point +//│ ║ l.112: 4378. +//│ ╙── ^ +//│ 4378 +//│ res +//│ = 4378 + +:pe +5. +//│ ╔══[LEXICAL ERROR] Expect at least one digit after the decimal point +//│ ║ l.121: 5. +//│ ╙── ^ +//│ 5 +//│ res +//│ = 5 + +:pe +789.E +//│ ╔══[LEXICAL ERROR] Expect at least one digit after the decimal point +//│ ║ l.130: 789.E +//│ ╙── ^ +//│ ╔══[LEXICAL ERROR] Expect at least one digit after the exponent sign +//│ ║ l.130: 789.E +//│ ╙── ^ +//│ 789 +//│ res +//│ = 789 diff --git a/shared/src/test/diff/nu/Declarations.mls b/shared/src/test/diff/nu/Declarations.mls new file mode 100644 index 0000000000..8f0c1e015b --- /dev/null +++ b/shared/src/test/diff/nu/Declarations.mls @@ -0,0 +1,131 @@ +:NewDefs + + + +declare fun btoa: nothing +declare fun atob: nothing +//│ fun btoa: nothing +//│ fun atob: nothing + +let str = btoa("hello") +atob(str) +//│ let str: nothing +//│ nothing +//│ str +//│ = 'aGVsbG8=' +//│ res +//│ = 'hello' + + +declare module console { + fun error: Str -> unit +} +//│ declare module console { +//│ fun error: Str -> unit +//│ } + +console.error("hello") +//│ unit +//│ res +//│ = undefined +//│ // Output +//│ hello + +:e +console.log("hello") +//│ ╔══[ERROR] Type `console` does not contain member `log` +//│ ║ l.35: console.log("hello") +//│ ╙── ^^^^ +//│ error +//│ res +//│ = undefined + + + +declare module Foo { + fun foo: Int +} +//│ declare module Foo { +//│ fun foo: Int +//│ } + +:re +Foo.foo +//│ Int +//│ res +//│ Runtime error: +//│ ReferenceError: Foo is not defined + + +declare type A = Int +//│ type A = Int + +42 : A +//│ A +//│ res +//│ = 42 + + + +declare class Sanitizer { + fun sanitizeFor(element: Str, input: Str): Str +} +//│ declare class Sanitizer { +//│ constructor() +//│ fun sanitizeFor: (element: Str, input: Str) -> Str +//│ } + +:re +let s = new Sanitizer +//│ let s: Sanitizer +//│ s +//│ Runtime error: +//│ ReferenceError: Sanitizer is not defined + + + +// * TODO allow Buffer2 to be named Buffer... +// :d +declare module Buffer { + abstract class Buffer2 { + val length: Int + } + fun bar: Int + fun from(a: Array[Int]): Buffer2 = from(a) + // fun from1(a: Array[Int]): this.Buffer2 = from1(a) // FIXME + // fun from2(a: Array[Int]): Buffer.Buffer2 = from2(a) // FIXME +} +//│ declare module Buffer { +//│ abstract class Buffer2 { +//│ val length: Int +//│ } +//│ fun bar: Int +//│ fun from: (a: Array[Int]) -> Buffer2 +//│ } + +let b = Buffer.from([0, 1, 2]) +//│ let b: Buffer2 +//│ b +//│ = + +b.length +//│ Int +//│ res +//│ = 3 + + + + +:pe +declare 42 +//│ ╔══[PARSE ERROR] Unexpected literal token after modifier +//│ ║ l.120: declare 42 +//│ ╙── ^^ +//│ ╔══[PARSE ERROR] Unexpected literal token after modifier +//│ ║ l.120: declare 42 +//│ ╙── ^^ +//│ 42 +//│ res +//│ = 42 + + diff --git a/shared/src/test/diff/nu/DiamondInherit.mls b/shared/src/test/diff/nu/DiamondInherit.mls new file mode 100644 index 0000000000..15ca19fffc --- /dev/null +++ b/shared/src/test/diff/nu/DiamondInherit.mls @@ -0,0 +1,182 @@ +:NewDefs + + + + +trait Foo[A] { fun foo: A } +//│ trait Foo[A] { +//│ fun foo: A +//│ } + +module Bar extends Foo[Int | Bool], Foo[Int | Str] { + fun foo = 123 +} +//│ module Bar extends Foo { +//│ fun foo: 123 +//│ } + +Bar.foo +//│ 123 +//│ res +//│ = 123 + +Bar : Foo['X] +//│ Foo['X] +//│ where +//│ 'X := Int | Str +//│ res +//│ = Bar { class: [class Bar extends Object] } + +(Bar : Foo['X]).foo +//│ Int | Str +//│ res +//│ = 123 + + +trait Foo[A] { fun foo: A; fun bar: A -> A } +//│ trait Foo[A] { +//│ fun bar: A -> A +//│ fun foo: A +//│ } + +module Bar extends Foo[Int | Bool], Foo[Int | Str] { + fun foo = 123 + fun bar = id +} +//│ module Bar extends Foo { +//│ fun bar: forall 'a. 'a -> 'a +//│ fun foo: 123 +//│ } + +Bar.bar +//│ forall 'a. 'a -> 'a +//│ res +//│ = [Function: id] + +Bar : Foo['X] +//│ Foo['X] +//│ where +//│ 'X := Int | Str +//│ res +//│ = Bar { class: [class Bar extends Object] } + + +trait T1 extends Foo[Int | Bool] +//│ trait T1 extends Foo { +//│ fun bar: 'A -> 'A +//│ fun foo: 'A +//│ } +//│ where +//│ 'A := Int | false | true + +module Bar extends T1, Foo[Int | Str] { + fun foo = 123 + fun bar = id +} +//│ module Bar extends Foo, T1 { +//│ fun bar: forall 'a. 'a -> 'a +//│ fun foo: 123 +//│ } + +(Bar : Foo['X]).foo +//│ Int | Str +//│ res +//│ = 123 + +(Bar : Foo['X]).bar +//│ ('X & (Int | Str)) -> (Int | Str | 'X) +//│ res +//│ = [Function: id] + +(Bar : T1).foo +//│ Int | false | true +//│ res +//│ = 123 + +let f = (Bar : T1).bar +f(true) +//│ let f: ('A & (Int | false | true)) -> (Int | false | true | 'A) +//│ Int | false | true +//│ f +//│ = [Function: id] +//│ res +//│ = true + +:e +module Bar extends T1, Foo[Int | Str] { + fun foo = 123 + fun bar(x) = x + 1 +} +//│ ╔══[ERROR] Type mismatch in definition of method bar: +//│ ║ l.108: fun bar(x) = x + 1 +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── type `Bool` is not an instance of type `Int` +//│ ║ l.64: trait T1 extends Foo[Int | Bool] +//│ ║ ^^^^ +//│ ╟── Note: constraint arises from reference: +//│ ║ l.108: fun bar(x) = x + 1 +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in definition of method bar: +//│ ║ l.108: fun bar(x) = x + 1 +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── type `Bool` is not an instance of type `Int` +//│ ║ l.64: trait T1 extends Foo[Int | Bool] +//│ ║ ^^^^ +//│ ╟── Note: constraint arises from reference: +//│ ║ l.108: fun bar(x) = x + 1 +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in definition of method bar: +//│ ║ l.108: fun bar(x) = x + 1 +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── type `Str` is not an instance of type `Int` +//│ ║ l.106: module Bar extends T1, Foo[Int | Str] { +//│ ║ ^^^ +//│ ╟── Note: constraint arises from reference: +//│ ║ l.108: fun bar(x) = x + 1 +//│ ╙── ^ +//│ module Bar extends Foo, T1 { +//│ fun bar: Int -> Int +//│ fun foo: 123 +//│ } + + +trait Base[A] { fun foo: A; fun bar: A -> A } +trait Derived1[A] extends Base[A] +trait Derived2 extends Base[[Int | Str, Int | Str]] +//│ trait Base[A] { +//│ fun bar: A -> A +//│ fun foo: A +//│ } +//│ trait Derived1[A] extends Base { +//│ fun bar: 'A -> 'A +//│ fun foo: 'A +//│ } +//│ trait Derived2 extends Base { +//│ fun bar: 'A0 -> 'A0 +//│ fun foo: 'A0 +//│ } +//│ where +//│ 'A0 := [Int | Str, Int | Str] +//│ 'A := A + +class Final extends Derived1[[Int, Int]], Derived2 { + fun foo = [123, 456] + fun bar([x, y]) = [error, error] +} +//│ class Final extends Base, Derived1, Derived2 { +//│ constructor() +//│ fun bar: ([anything, anything]) -> [nothing, nothing] +//│ fun foo: [123, 456] +//│ } + +class Final extends Derived1[[Int, Int]], Derived2 { + fun foo = [123, 456] + fun bar([x, y]) = [y, x] +} +//│ class Final extends Base, Derived1, Derived2 { +//│ constructor() +//│ fun bar: forall 'a 'b. (['a, 'b]) -> ['b, 'a] +//│ fun foo: [123, 456] +//│ } + + diff --git a/shared/src/test/diff/nu/DidierNu.mls b/shared/src/test/diff/nu/DidierNu.mls new file mode 100644 index 0000000000..5f5cf12d78 --- /dev/null +++ b/shared/src/test/diff/nu/DidierNu.mls @@ -0,0 +1,35 @@ +:NewDefs + + +// val k x y = y +// val unify : A. B. A -> A -> B -> B +// = fun x y z -> k (fun z -> k (z x) (k (z y))) z +// val a0 = (fun z -> z) (id : A. A -> A) 1 +// val a1 x = (fun z -> unify x (fst z) (snd z)) (id, (id : A. A -> A)) 1 + + +fun k(x, y) = y +//│ fun k: forall 'a. (anything, 'a) -> 'a + +fun unify : 'A -> 'A -> 'B -> 'B +//│ fun unify: forall 'B. anything -> anything -> 'B -> 'B + +fun unify = x => y => z => k(z => k(z(x), y => k(z(y), y)), z) +//│ fun unify: forall 'a. anything -> anything -> 'a -> 'a + +fun unify = x => y => z => k of z => k(z(x), y => k of z(y), y), z +//│ fun unify: forall 'a. anything -> anything -> 'a -> 'a + + +fun a0 = (z => z) (id : forall 'A: 'A -> 'A) of 1 +//│ fun a0: 1 + +fun fst([a, _]) = a +fun snd([_, b]) = b +//│ fun fst: forall 'a. (['a, anything]) -> 'a +//│ fun snd: forall 'b. ([anything, 'b]) -> 'b + +fun a1(x) = (z => unify(x)(fst of z)(snd of z))([id, (id : forall 'A: 'A -> 'A)]) of 1 +//│ fun a1: anything -> 1 + + diff --git a/shared/src/test/diff/nu/EncodedLists.mls b/shared/src/test/diff/nu/EncodedLists.mls new file mode 100644 index 0000000000..8d12d0a75b --- /dev/null +++ b/shared/src/test/diff/nu/EncodedLists.mls @@ -0,0 +1,29 @@ +:NewDefs +:NoJS + + +class List { + fun match: forall 'res: (ifNil: () => 'res, ifCons: ('res, List[A]) => 'res) => 'res + fun match = error // TODO use self-type... +} +let Nil: List +let Cons: (head: 'a, tail: List<'a>) => List<'a> +//│ class List[A] { +//│ constructor() +//│ fun match: forall 'res. (ifNil: () -> 'res, ifCons: ('res, List[A]) -> 'res) -> 'res +//│ } +//│ let Nil: List[nothing] +//│ let Cons: forall 'a. (head: 'a, tail: List['a]) -> List['a] + +let x: List +//│ let x: List[Int] + +// FIXME +x: List +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.22: x: List +//│ ║ ^ +//│ ╙── expression of type `anything` is not an instance of type `Int` +//│ List[anything] + + diff --git a/shared/src/test/diff/nu/Eql.mls b/shared/src/test/diff/nu/Eql.mls new file mode 100644 index 0000000000..086847d55b --- /dev/null +++ b/shared/src/test/diff/nu/Eql.mls @@ -0,0 +1,237 @@ +:NewDefs + + +let x: Eql[Int] +//│ let x: Eql[Int] +//│ x +//│ = + +x === 1 +//│ Bool +//│ res +//│ = +//│ x is not implemented + +:e +1 === x +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.16: 1 === x +//│ ║ ^^^^^^^ +//│ ╟── type `#Eql` is not an instance of type `Num` +//│ ║ l.4: let x: Eql[Int] +//│ ║ ^^^^^^^^ +//│ ╟── but it flows into reference with expected type `Num` +//│ ║ l.16: 1 === x +//│ ╙── ^ +//│ error | false | true +//│ res +//│ = +//│ x is not implemented + +:e +x === x +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.32: x === x +//│ ║ ^^^^^^^ +//│ ╟── type `#Eql` is not an instance of type `Int` +//│ ║ l.4: let x: Eql[Int] +//│ ║ ^^^^^^^^ +//│ ╟── but it flows into reference with expected type `Int` +//│ ║ l.32: x === x +//│ ╙── ^ +//│ error | false | true +//│ res +//│ = +//│ x is not implemented + + +fun test1(x) = + x === x +//│ fun test1: forall 'a. (Eql['a] & 'a) -> Bool + +fun test2(x, y) = + x === y +//│ fun test2: forall 'a. (Eql['a], 'a) -> Bool + + +1 : Eql['a] +//│ Eql[Num] +//│ res +//│ = 1 + +1 : Eql[Int] +//│ Eql[Int] +//│ res +//│ = 1 + +1 : Eql[1] +//│ Eql[1] +//│ res +//│ = 1 + +test1(1) +//│ Bool +//│ res +//│ = true + +:e +test1(x) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.78: test1(x) +//│ ║ ^^^^^^^^ +//│ ╟── type `#Eql` is not an instance of type `Int` +//│ ║ l.4: let x: Eql[Int] +//│ ║ ^^^^^^^^ +//│ ╟── but it flows into reference with expected type `Int` +//│ ║ l.78: test1(x) +//│ ╙── ^ +//│ error | false | true +//│ res +//│ = +//│ x is not implemented + + +let n: Int = 1 +//│ let n: Int +//│ n +//│ = 1 + +n : Eql['a] +//│ Eql[anything] +//│ res +//│ = 1 + +test1(n) +//│ Bool +//│ res +//│ = true + + +let n: Num +//│ let n: Num +//│ n +//│ = + +test1(n) +//│ Bool +//│ res +//│ = +//│ n is not implemented + +let d = 1/2 +//│ let d: Num +//│ d +//│ = 0.5 + +test1(d) +//│ Bool +//│ res +//│ = true + +test1("hello") +//│ Bool +//│ res +//│ = true + + +test2(0, 1) +//│ Bool +//│ res +//│ = false + +test2(0, d) +//│ Bool +//│ res +//│ = false + +x => test2(0, x) +//│ Num -> Bool +//│ res +//│ = [Function: res] + +x => test2(x, 0) +//│ Eql[0] -> Bool +//│ res +//│ = [Function: res] + +x => test2(d, x) +//│ anything -> Bool +//│ res +//│ = [Function: res] + +x => test2(x, d) +//│ Eql[Num] -> Bool +//│ res +//│ = [Function: res] + + +:e +test2(1, "oops") +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.169: test2(1, "oops") +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ╟── string literal of type `"oops"` is not an instance of type `Num` +//│ ║ l.169: test2(1, "oops") +//│ ║ ^^^^^^ +//│ ╟── Note: constraint arises from reference: +//│ ║ l.53: x === y +//│ ╙── ^ +//│ error | false | true +//│ res +//│ = false + +:e +test2("oops", 1) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.184: test2("oops", 1) +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ╟── integer literal of type `1` is not an instance of type `Str` +//│ ║ l.184: test2("oops", 1) +//│ ║ ^ +//│ ╟── Note: constraint arises from reference: +//│ ║ l.53: x === y +//│ ╙── ^ +//│ error | false | true +//│ res +//│ = false + +:e +test2(1, {}) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.199: test2(1, {}) +//│ ║ ^^^^^^^^^^^^ +//│ ╟── record literal of type `anything` is not an instance of type `Num` +//│ ╟── Note: constraint arises from reference: +//│ ║ l.53: x === y +//│ ╙── ^ +//│ error | false | true +//│ res +//│ = false + +:e +test2({}, 1) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.212: test2({}, 1) +//│ ║ ^^^^^^^^^^^^ +//│ ╟── record literal of type `anything` is not an instance of type `Eql` +//│ ╟── Note: constraint arises from reference: +//│ ║ l.53: x === y +//│ ╙── ^ +//│ error | false | true +//│ res +//│ = false + +:e +test2({}, {}) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.225: test2({}, {}) +//│ ║ ^^^^^^^^^^^^^ +//│ ╟── record literal of type `anything` is not an instance of type `Eql` +//│ ╟── Note: constraint arises from reference: +//│ ║ l.53: x === y +//│ ╙── ^ +//│ error | false | true +//│ res +//│ = false + + diff --git a/shared/src/test/diff/nu/EqlClasses.mls b/shared/src/test/diff/nu/EqlClasses.mls new file mode 100644 index 0000000000..927045a336 --- /dev/null +++ b/shared/src/test/diff/nu/EqlClasses.mls @@ -0,0 +1,220 @@ +:NewDefs + + + +module Mod +//│ module Mod + +:e +Mod === Mod +//│ ╔══[ERROR] Module 'Mod' does not support equality comparison because it does not have a parameter list +//│ ║ l.9: Mod === Mod +//│ ╙── ^^^^^^^^^^^ +//│ error | false | true +//│ res +//│ = true + + +class Cls1() +//│ class Cls1() + +Cls1() === Cls1() +//│ Bool +//│ res +//│ = false + + +class Cls2(x: Int) +//│ class Cls2(x: Int) + +:e // TODO better error – or actually only support data classes +Cls2(0) === Cls2(1) +//│ ╔══[ERROR] Parameter 'x' cannot tbe accessed as a field +//│ ║ l.27: class Cls2(x: Int) +//│ ║ ^ +//│ ╟── Either make the parameter a `val` or access it through destructuring +//│ ║ l.27: class Cls2(x: Int) +//│ ╙── ^ +//│ error | false | true +//│ res +//│ = false + + +class Cls2(val x: Int) +//│ class Cls2(x: Int) + +Cls2(0) === Cls2(1) +//│ Bool +//│ res +//│ = false + + + +class Pair[out A](val fst: A, val snd: A) +// extends (A <: Eql[A]) => Eql[Pair[A]] +//│ class Pair[A](fst: A, snd: A) + +let p = Pair(1, 2) +//│ let p: Pair[1 | 2] +//│ p +//│ = Pair {} + +p === p +//│ Bool +//│ res +//│ = true + + +x => p === x +//│ {fst: Eql[1 | 2], snd: Eql[1 | 2]} -> Bool +//│ res +//│ = [Function: res] + +x => x === p +//│ Eql[Pair[1 | 2]] -> Bool +//│ res +//│ = [Function: res] + +p === { fst: 1, snd: 2 } +//│ Bool +//│ res +//│ = false + +:e +{ fst: 1, snd: 2 } === p +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.84: { fst: 1, snd: 2 } === p +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── record literal of type `{fst: 1, snd: 2}` is not an instance of type `Eql` +//│ ║ l.84: { fst: 1, snd: 2 } === p +//│ ╙── ^^^^^^^^^ +//│ error | false | true +//│ res +//│ = false + +let r = {x: 42, y: y => y} +//│ let r: {x: 42, y: forall 'a. 'a -> 'a} +//│ r +//│ = { x: 42, y: [Function: y] } + +r : {x: Int} +//│ {x: Int} +//│ res +//│ = { x: 42, y: [Function: y] } + + +:e +x => { a: 0 } === x +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.107: x => { a: 0 } === x +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── record literal of type `{a: 0}` is not an instance of type `Eql` +//│ ║ l.107: x => { a: 0 } === x +//│ ╙── ^ +//│ anything -> (error | false | true) +//│ res +//│ Syntax error: +//│ Unexpected token '===' + +x => x === { a: 0 } +//│ Eql[{a: 0}] -> Bool +//│ res +//│ = [Function: res] + + + +let q = Pair(1, "oops") +//│ let q: Pair["oops" | 1] +//│ q +//│ = Pair {} + +:e +q === q +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.132: q === q +//│ ║ ^^^^^^^ +//│ ╟── integer literal of type `1` is not an instance of type `Str` +//│ ║ l.126: let q = Pair(1, "oops") +//│ ╙── ^ +//│ error | false | true +//│ res +//│ = true + + +class Pair2[out A, out B](val fst: A, val snd: B) +//│ class Pair2[A, B](fst: A, snd: B) + +let q = Pair2(1, "oops") +//│ let q: Pair2[1, "oops"] +//│ q +//│ = Pair2 {} + +q === q +//│ Bool +//│ res +//│ = true + + + +class MP[out Col](val color: Col) +//│ class MP[Col](color: Col) + +val mp = MP(1) +//│ val mp: MP[1] +//│ mp +//│ = MP {} + +mp === mp +//│ Bool +//│ res +//│ = true + +fun cmp(lhs, rhs) = lhs.color === rhs.color +//│ fun cmp: forall 'a. ({color: Eql['a]}, {color: 'a}) -> Bool + +cmp(mp, mp) +//│ Bool +//│ res +//│ = true + +module Mix { + fun compare(lhs, rhs) = (lhs.color === rhs.color) +} +module Comp extends Mix +//│ module Mix { +//│ fun compare: forall 'a. ({color: Eql['a]}, {color: 'a}) -> Bool +//│ } +//│ module Comp extends Mix { +//│ fun compare: forall 'b. ({color: Eql['b]}, {color: 'b}) -> Bool +//│ } + +Comp.compare(mp, mp) +//│ Bool +//│ res +//│ = true + + + + +// *** NOTES *** + +// * Intended type for comparing Cons: +// Eql[Cons & { head: Eql['h], tail: Eql['t] } | ~Cons & List] + + +// * Original code +// x: Int +// y: Int +// x == y + +// * Refactored code (we want an error) +// x: Option[Int] +// y: Int +// x == y +// --> +// x.exists(_ == y) + +// * Should not be equatable: +// Int | Option[Int] + + diff --git a/shared/src/test/diff/nu/Eval.mls b/shared/src/test/diff/nu/Eval.mls new file mode 100644 index 0000000000..0f5dd3d2e2 --- /dev/null +++ b/shared/src/test/diff/nu/Eval.mls @@ -0,0 +1,301 @@ +:NewDefs + + + +// * Standard definitions: + + +declare fun String: anything -> Str +//│ fun String: anything -> Str + +fun (++) stringConcat(a, b) = concat(a)(b) +//│ fun (++) stringConcat: (Str, Str) -> Str + +fun (|>) pipe(x, f) = f(x) +fun (<|) pepi(f, x) = f(x) +//│ fun (|>) pipe: forall 'a 'b. ('a, 'a -> 'b) -> 'b +//│ fun (<|) pepi: forall 'c 'd. ('c -> 'd, 'c) -> 'd + + +// * Hack to throw exceptions +:e +declare class throw(arg: anything): nothing +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.22: declare class throw(arg: anything): nothing +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── expression of type `#Throw & {arg: anything}` does not match type `nothing` +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.22: declare class throw(arg: anything): nothing +//│ ╙── ^^^^^^^ +//│ declare class throw(arg: anything): nothing + +fun raise(err) = + throw(err) + error +//│ fun raise: anything -> nothing + +:re +raise(1); 0 +//│ 0 +//│ res +//│ Runtime error: +//│ 1 +//│ res +//│ = 0 + +fun test = raise(1); 0 +//│ fun test: nothing +//│ 0 +//│ res +//│ = 0 + +:re +test +//│ nothing +//│ res +//│ Runtime error: +//│ 1 + +fun test = raise(1), 0 +//│ fun test: 0 + +:re +test +//│ 0 +//│ res +//│ Runtime error: +//│ 1 + +fun test = + raise(1) + error +//│ fun test: nothing + +:re +test +//│ nothing +//│ res +//│ Runtime error: +//│ 1 + + +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 + +abstract class List[out A]: (Cons[A] | Nil) { virtual val length: Int } +class Cons[out A](val head: A, val tail: List[A]) extends List[A] { + val length: Int + val length = tail.length + 1 + fun toString() = "Cons(" ++ String(head) ++ ", " ++ String(tail) ++ ")" +} +module Nil extends List[nothing] { + val length = 0 + fun toString() = "Nil" +} +//│ abstract class List[A]: Cons[A] | Nil { +//│ val length: Int +//│ } +//│ class Cons[A](head: A, tail: List[A]) extends List { +//│ val length: Int +//│ fun toString: () -> Str +//│ } +//│ module Nil extends List { +//│ val length: 0 +//│ fun toString: () -> "Nil" +//│ } + +fun (::) cons(x, xs) = Cons(x, xs) +fun (:::) concatList(xs, ys) = if xs is + Nil then ys + Cons(x, xs) then x :: xs ::: ys +//│ fun (::) cons: forall 'A. ('A, List['A]) -> Cons['A] +//│ fun (:::) concatList: forall 'A0 'a. (Cons['A0] | Nil, List['A0] & 'a) -> (Cons['A0] | 'a) + +module Lists { // TODO use name List when module overloading is supported: + + fun map(f) = case + Nil then Nil + Cons(x, xs) then f(x) :: map(f)(xs) + + fun zip(xs, ys) = if xs is + Nil then Nil + Cons(x, xs) then if ys is + Nil then Nil + Cons(y, ys) then + [x, y] :: zip(xs, ys) + + fun assoc(e) = case + Cons(kv, rest) then + if kv.key === e then Some(kv.value) + else assoc(e)(rest) + Nil then None + +} +//│ 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) +//│ } + +let xs = 1 :: 2 :: 3 :: Nil +//│ let xs: Cons[1 | 2 | 3] +//│ xs +//│ = Cons {} + +String of xs ::: 4 :: 5 :: Nil +//│ Str +//│ res +//│ = 'Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Nil)))))' + + +let ls = {key: "a", value: 0} :: Nil +//│ let ls: Cons[{key: "a", value: 0}] +//│ ls +//│ = Cons {} + +ls |> Lists.assoc("a") +//│ None | Some[0] +//│ res +//│ = Some {} + + + +// * Our little language: + + +:e // * We don't yet support upper bounds on type parameters, so we can't provide the needed `out Sub <: Term` +abstract class Term: Var | App | Lam | Sel | Rcd[Term] | Lit +class Var(val name: Str) extends Term +class App(val lhs: Term, val args: List[Term]) extends Term +class Lam(val params: List[Str], val body: Term) extends Term +class Rcd[out Sub](val fields: List[{key: Str, value: Sub}]) extends Term +class Sel(val prefix: Term, val fieldName: Str) extends Term +abstract class Lit[out A](val value: A): IntLit | StrLit extends Term +class IntLit(v: Int) extends Lit[Int](v) +class StrLit(v: Str) extends Lit[Str](v) +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.175: class Rcd[out Sub](val fields: List[{key: Str, value: Sub}]) extends Term +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type `Sub` is not an instance of type `Term` +//│ ║ l.175: class Rcd[out Sub](val fields: List[{key: Str, value: Sub}]) extends Term +//│ ║ ^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.171: abstract class Term: Var | App | Lam | Sel | Rcd[Term] | Lit +//│ ╙── ^^^^ +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.175: class Rcd[out Sub](val fields: List[{key: Str, value: Sub}]) extends Term +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type `Sub` does not match type `App | Lam | #Lit | Rcd[Term] | Sel | Var` +//│ ║ l.175: class Rcd[out Sub](val fields: List[{key: Str, value: Sub}]) extends Term +//│ ║ ^^^ +//│ ╟── Note: constraint arises from union type: +//│ ║ l.171: abstract class Term: Var | App | Lam | Sel | Rcd[Term] | Lit +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── from type reference: +//│ ║ l.171: abstract class Term: Var | App | Lam | Sel | Rcd[Term] | Lit +//│ ╙── ^^^^ +//│ ╔══[ERROR] Type `Sub` does not contain member `Rcd#Sub` +//│ ║ l.175: class Rcd[out Sub](val fields: List[{key: Str, value: Sub}]) extends Term +//│ ╙── ^^^ +//│ abstract class Term: App | Lam | Lit[anything] | Rcd[Term] | Sel | Var +//│ class Var(name: Str) extends Term +//│ class App(lhs: Term, args: List[Term]) extends Term +//│ class Lam(params: List[Str], body: Term) extends Term +//│ class Rcd[Sub](fields: List[{key: Str, value: Sub}]) extends Term +//│ class Sel(prefix: Term, fieldName: Str) extends Term +//│ abstract class Lit[A](value: A): IntLit | StrLit extends Term +//│ class IntLit(v: Int) extends Lit, Term +//│ class StrLit(v: Str) extends Lit, Term + +// * Workaround/alternative: +type Term = Var | App | Lam | Sel | Rcd[Term] | Lit +class Var(val name: Str) +class App(val lhs: Term, val args: List[Term]) +class Lam(val params: List[Str], val body: Term) +class Rcd[out Sub](val fields: List[{key: Str, value: Sub}]) +class Sel(val prefix: Term, val fieldName: Str) +abstract class Lit[out A](val value: A): IntLit | StrLit +class IntLit(v: Int) extends Lit[Int](v) +class StrLit(v: Str) extends Lit[Str](v) +//│ type Term = App | Lam | Lit[anything] | Rcd[Term] | Sel | Var +//│ class Var(name: Str) +//│ class App(lhs: Term, args: List[Term]) +//│ class Lam(params: List[Str], body: Term) +//│ class Rcd[Sub](fields: List[{key: Str, value: Sub}]) +//│ class Sel(prefix: Term, fieldName: Str) +//│ abstract class Lit[A](value: A): IntLit | StrLit +//│ class IntLit(v: Int) extends Lit +//│ class StrLit(v: Str) extends Lit + +type Value = Lam | Lit | Rcd[Value] +//│ type Value = Lam | Lit[anything] | Rcd[Value] + + +fun err(msg) = + throw(concat("Evaluation error: " ++ msg)) + error +//│ fun err: Str -> nothing + +fun eval(t, env) = if t is + Var(nme) then if env |> Lists.assoc(nme) is Some(v) + then v + else err("variable not found: " ++ nme) + Lit then t + Lam then t + App(f, args) then + let fe = eval(f, env) + if fe is Lam(ps, bod) then + val argse = args |> Lists.map(a => eval(a, env)) + if ps.length === argse.length then () else err("wrong number of arguments") + let envp = Lists.zip(ps, argse) |> Lists.map of ([key, value]) => {key, value} + eval(bod, envp ::: env) + else err(String(fe) ++ " cannot be applied") + Sel(pre, nme) then + let pree = eval(pre, env) + if pree is Rcd(xs) and xs |> Lists.assoc(nme) is Some(v) then v + 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 +//│ 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 + +eval : (Term, List[{key: Str, value: Value}]) -> Value +//│ (Term, List[{key: Str, value: Value}]) -> Value +//│ res +//│ = [Function: eval] + +let rcd = Rcd({key: "a", value: IntLit(0)} :: Nil) +//│ let rcd: Rcd[IntLit] +//│ rcd +//│ = Rcd {} + +eval of rcd, Nil +//│ 'a +//│ where +//│ 'a :> Lam | Lit[Int | ??A] | Rcd[Lam | Lit[Int | ??A] | 'a] +//│ res +//│ = Rcd {} + +eval of Sel(rcd, "a"), Nil +//│ 'a +//│ where +//│ 'a :> Lam | Lit[??A] | Rcd[Lam | Lit[??A] | 'a] +//│ res +//│ = IntLit {} + +eval of App(Lam("x" :: Nil, Sel(Var("x"), "a")), rcd :: Nil), Nil +//│ 'a +//│ where +//│ 'a :> Lam | Lit[??A] | Rcd[Lam | Lit[??A] | 'a] +//│ res +//│ = IntLit {} + + diff --git a/shared/src/test/diff/nu/EvalNegNeg.mls b/shared/src/test/diff/nu/EvalNegNeg.mls new file mode 100644 index 0000000000..4e3fbbaba2 --- /dev/null +++ b/shared/src/test/diff/nu/EvalNegNeg.mls @@ -0,0 +1,77 @@ +:NewDefs + + +class Add(lhs: E, rhs: E) +class Lit(n: Int) +//│ class Add[E](lhs: E, rhs: E) +//│ class Lit(n: Int) + + +mixin EvalBase { + fun eval(e) = + if e is + Lit(n) then n: Int + Add(l, r) then this.eval(l) + this.eval(r) +} +//│ mixin EvalBase() { +//│ this: {eval: 'a -> Int} +//│ fun eval: (Add['a] | Lit) -> Int +//│ } + + +class Neg(expr: A) +//│ class Neg[A](expr: A) + + +mixin EvalNeg { + fun eval(e) = + if e is Neg(d) then 0 - this.eval(d) + else super.eval(e) +} +//│ mixin EvalNeg() { +//│ super: {eval: 'a -> 'b} +//│ this: {eval: 'c -> Int} +//│ fun eval: (Neg['c] | Object & 'a & ~#Neg) -> (Int | 'b) +//│ } + + +mixin EvalNegNeg { + fun eval(e) = + if e is Neg(Neg(d)) then this.eval(d) + else super.eval(e) +} +//│ mixin EvalNegNeg() { +//│ super: {eval: (Neg[nothing] | 'a) -> 'b} +//│ this: {eval: 'c -> 'b} +//│ fun eval: (Neg[Neg['c] | Object & ~#Neg] | Object & 'a & ~#Neg) -> 'b +//│ } + + +module TestLang extends EvalBase, EvalNeg, EvalNegNeg +//│ module TestLang { +//│ fun eval: (Neg['A] | Object & 'a & ~#Neg) -> Int +//│ } +//│ where +//│ 'A <: 'b & (Neg['b] | Object & ~#Neg) +//│ 'b <: Neg['A] | Object & 'a & ~#Neg +//│ 'a <: Add['b] | Lit | Neg['b] + + +fun mk(n) = if n is + 0 then Lit(3) + 1 then Neg(mk(n - 1)) + _ then Add(mk(n - 1), mk(n - 1)) +//│ fun mk: forall 'a. (0 | 1 | Int & ~0 & ~1) -> (Lit | 'a) +//│ where +//│ 'a :> Neg[Lit | 'a] | Add[Lit | 'a] + +TestLang.eval(mk(0)) +//│ Int +//│ res +//│ = 3 + +TestLang.eval(mk(11)) +//│ Int +//│ res +//│ = -3072 + diff --git a/shared/src/test/diff/nu/ExplicitVariance.mls b/shared/src/test/diff/nu/ExplicitVariance.mls new file mode 100644 index 0000000000..2f3980b48a --- /dev/null +++ b/shared/src/test/diff/nu/ExplicitVariance.mls @@ -0,0 +1,150 @@ +:NewDefs + + + +class Foo[out A](x: A) +//│ class Foo[A](x: A) + +fun foo(x: Foo[Int]): Foo[Num] = x +//│ fun foo: (x: Foo[Int]) -> Foo[Num] + +:e +fun foo(x: Foo[Num]): Foo[Int] = x +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.12: fun foo(x: Foo[Num]): Foo[Int] = x +//│ ║ ^ +//│ ╟── type `Num` is not an instance of type `Int` +//│ ║ l.12: fun foo(x: Foo[Num]): Foo[Int] = x +//│ ║ ^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.12: fun foo(x: Foo[Num]): Foo[Int] = x +//│ ╙── ^^^ +//│ fun foo: (x: Foo[Num]) -> Foo[Int] + + +class Foo[in A](x: A -> Int) +//│ class Foo[A](x: A -> Int) + +fun foo(x: Foo[Num]): Foo[Int] = x +//│ fun foo: (x: Foo[Num]) -> Foo[Int] + +:e +fun foo(x: Foo[Int]): Foo[Num] = x +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.32: fun foo(x: Foo[Int]): Foo[Num] = x +//│ ║ ^ +//│ ╟── type `Num` is not an instance of type `Int` +//│ ║ l.32: fun foo(x: Foo[Int]): Foo[Num] = x +//│ ║ ^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.32: fun foo(x: Foo[Int]): Foo[Num] = x +//│ ╙── ^^^ +//│ fun foo: (x: Foo[Int]) -> Foo[Num] + + + +// * Note that not checking variance annotations can actually be made sound in MLscript, +// * but this would be surprising for users, who would find type errors at the definition's use sites. + + +// :e // TODO check variance annotations! +class Oops0[in A](val x: A) +//│ class Oops0[A](x: A) + +// :e // TODO check variance annotations! +class Oops0[out A](val x: A -> Int) +//│ class Oops0[A](x: A -> Int) + +let o = Oops0(id) +//│ let o: Oops0[nothing] +//│ o +//│ = Oops0 {} + +// * What used to happens is `Oops9{ A = nothing..'? }` was inferred for `o` (consistent with `A`'s covariance), +// * so all negative occurrences of `o.A` were viewed as `nothing` from the outside, resulting in `o.x : nothing -> Int` +// * No lomnger the case since I simplified substitution for variant type arguments, to improve inferred type simplification. +o.x +//│ Int -> Int +//│ res +//│ = [Function: id] + +// * Similarly, `Oops0[Int]` here used to expand to the equivalent `Oops0{ A = nothing..Int }`, giving `(o : Oops0[Int]).x : nothing -> Int` +(o : Oops0[Int]).x +//│ Int -> Int +//│ res +//│ = [Function: id] + +// * So code like this no longer reports an error: +// :e +o.x(123) +//│ Int +//│ res +//│ = 123 + +// :e +(o : Oops0[Int]).x(123) +//│ Int +//│ res +//│ = 123 + + +class Oops1[out A](val x: A -> A, val y: A) +//│ class Oops1[A](x: A -> A, y: A) + +let o = Oops1(id, 123) +//│ let o: Oops1[123] +//│ o +//│ = Oops1 {} + +o.x +//│ 'A -> (123 | 'A) +//│ res +//│ = [Function: id] + +// :e +o.x(123) +//│ 123 +//│ res +//│ = 123 + +:re +o.x(error) + 1 +//│ Int +//│ res +//│ Runtime error: +//│ Error: an error was thrown + + +class Oops2[out A](val x: A -> A, val y: A) +//│ class Oops2[A](x: A -> A, y: A) + +let o = Oops2(id, 123) +//│ let o: Oops2[123] +//│ o +//│ = Oops2 {} + +o.x +//│ 'A -> (123 | 'A) +//│ res +//│ = [Function: id] + +// :e +o.x(123) +//│ 123 +//│ res +//│ = 123 + +// :e // * We will be able to make this work later, through `o.x : o.A -> o.A` and `o.y : o.A` +o.x(o.y) +//│ 123 +//│ res +//│ = 123 + +:re +o.x(error) + 1 +//│ Int +//│ res +//│ Runtime error: +//│ Error: an error was thrown + + diff --git a/shared/src/test/diff/nu/ExpressionProblem_repro.mls b/shared/src/test/diff/nu/ExpressionProblem_repro.mls new file mode 100644 index 0000000000..70a8a32ed0 --- /dev/null +++ b/shared/src/test/diff/nu/ExpressionProblem_repro.mls @@ -0,0 +1,166 @@ +:NewDefs +:NoJS + + +class Add0(lhs: E) +//│ class Add0[E](lhs: E) + +fun eval(e) = if e is Add0(l) then eval(l) +//│ fun eval: forall 'a. 'a -> nothing +//│ where +//│ 'a <: Add0['a] + + +class Add(lhs: E, rhs: E) +class Lit(val value: Int) +//│ class Add[E](lhs: E, rhs: E) +//│ class Lit(value: Int) + +let add11 = Add(Lit(1), Lit(2)) +//│ let add11: Add[Lit] + +fun eval(e) = + if e is + Lit(n) then n: Int + Add(l, r) then eval(l) + eval(r) +//│ fun eval: forall 'a. 'a -> Int +//│ where +//│ 'a <: Add['a] | Lit + + +mixin EvalLit { + fun eval(e) = + if e is + Lit(n) then n +} +//│ mixin EvalLit() { +//│ fun eval: Lit -> Int +//│ } + +mixin EvalLit { + fun eval(e: Lit) = e.value +} +//│ mixin EvalLit() { +//│ fun eval: (e: Lit) -> Int +//│ } + + +mixin EvalAdd { + fun eval(e) = + if e is + Add(l, r) then this.eval(l) +} +//│ mixin EvalAdd() { +//│ this: {eval: 'a -> 'b} +//│ fun eval: Add['a] -> 'b +//│ } + +module TestLang extends EvalAdd +//│ module TestLang { +//│ fun eval: 'a -> nothing +//│ } +//│ where +//│ 'a <: Add['a] + +TestLang.eval +//│ 'a -> nothing +//│ where +//│ 'a <: Add['a] + + + +mixin EvalBase { + fun eval(e) = + if e is + Lit(n) then n: Int + Add(l, r) then this.eval(l) + this.eval(r) +} +//│ mixin EvalBase() { +//│ this: {eval: 'a -> Int} +//│ fun eval: (Add['a] | Lit) -> Int +//│ } + + +module TestLang extends EvalBase +//│ module TestLang { +//│ fun eval: 'a -> Int +//│ } +//│ where +//│ 'a <: Add['a] | Lit + +TestLang.eval +//│ 'a -> Int +//│ where +//│ 'a <: Add['a] | Lit + + + +add11 +//│ Add[Lit] + +TestLang.eval(add11) +//│ Int + +add11 +//│ Add[Lit] + +TestLang.eval(add11) +//│ Int + +add11 +//│ Add[Lit] + + + +class Neg(expr: A) +//│ class Neg[A](expr: A) + +let add2negadd11 = Add(Lit(2), Neg(add11)) +//│ let add2negadd11: Add[Lit | Neg[Add[Lit]]] + + +mixin EvalNeg { + fun eval(e) = + if e is Neg(d) then 0 - this.eval(d) + else super.eval(e) +} +//│ mixin EvalNeg() { +//│ super: {eval: 'a -> 'b} +//│ this: {eval: 'c -> Int} +//│ fun eval: (Neg['c] | Object & 'a & ~#Neg) -> (Int | 'b) +//│ } + + +module TestLang extends EvalBase, EvalNeg +//│ module TestLang { +//│ fun eval: 'a -> Int +//│ } +//│ where +//│ 'a <: Neg['a] | Object & (Add['a] | Lit) & ~#Neg + +TestLang.eval +//│ 'a -> Int +//│ where +//│ 'a <: Neg['a] | Object & (Add['a] | Lit) & ~#Neg + + +TestLang.eval(add11) +//│ Int + +TestLang.eval(Neg(add11)) +//│ Int + +TestLang.eval(Add(Lit(2), Neg(Lit(1)))) +//│ Int + +TestLang.eval(Neg(Neg(add11))) +//│ Int + + +TestLang.eval(add2negadd11) +//│ Int + +TestLang.eval(Add(Lit(2), Neg(add11))) +//│ Int + + diff --git a/shared/src/test/diff/nu/ExpressionProblem_small.mls b/shared/src/test/diff/nu/ExpressionProblem_small.mls new file mode 100644 index 0000000000..128c1eee71 --- /dev/null +++ b/shared/src/test/diff/nu/ExpressionProblem_small.mls @@ -0,0 +1,58 @@ +:NewDefs +:NoJS + + +class Neg[out A](expr: A) +class Add[out E](lhs: E, rhs: E) +class Lit(n: Int) +//│ class Neg[A](expr: A) +//│ class Add[E](lhs: E, rhs: E) +//│ class Lit(n: Int) + +let add11 = Add(Lit(1), Lit(2)) +let add2negadd11 = Add(Lit(2), Neg(add11)) +//│ let add11: Add[Lit] +//│ let add2negadd11: Add[Lit | Neg[Add[Lit]]] + +mixin EvalNothing { + fun eval(e: nothing) = e +} +mixin EvalAddLit { + fun eval(e) = + if e is + Lit(n) then n + Add(l, r) then this.eval(l) + this.eval(r) + else super.eval(e) +} +mixin EvalNeg { + fun eval(e) = + if e is Neg(d) then 0 - this.eval(d) + else super.eval(e) +} +//│ mixin EvalNothing() { +//│ fun eval: (e: nothing) -> nothing +//│ } +//│ mixin EvalAddLit() { +//│ super: {eval: 'a -> 'b} +//│ this: {eval: 'c -> Int} +//│ fun eval: (Add['c] | Lit | Object & 'a & ~#Add & ~#Lit) -> (Int | 'b) +//│ } +//│ mixin EvalNeg() { +//│ super: {eval: 'd -> 'e} +//│ this: {eval: 'f -> Int} +//│ fun eval: (Neg['f] | Object & 'd & ~#Neg) -> (Int | 'e) +//│ } + +module TestLang extends EvalNothing, EvalAddLit, EvalNeg +//│ module TestLang { +//│ fun eval: 'a -> Int +//│ } +//│ where +//│ 'a <: Neg['a] | Object & (Add['a] | Lit) & ~#Neg + +TestLang.eval +//│ 'a -> Int +//│ where +//│ 'a <: Neg['a] | Object & (Add['a] | Lit) & ~#Neg + + diff --git a/shared/src/test/diff/nu/Extrusion.mls b/shared/src/test/diff/nu/Extrusion.mls new file mode 100644 index 0000000000..1642e4d890 --- /dev/null +++ b/shared/src/test/diff/nu/Extrusion.mls @@ -0,0 +1,43 @@ +:NewDefs + + +fun f(y) = + let local = forall 'A: (x: 'A) => + discard of y(x) + 1 + x + y +//│ fun f: forall 'a. (??A -> Int & 'a) -> 'a + +:e +f(id) +//│ ╔══[ERROR] Type error in application +//│ ║ l.12: f(id) +//│ ║ ^^^^^ +//│ ╟── type variable `'A` leaks out of its scope +//│ ║ l.5: let local = forall 'A: (x: 'A) => +//│ ║ ^^ +//│ ╟── into application of type `Int` +//│ ║ l.6: discard of y(x) + 1 +//│ ║ ^^^^ +//│ ╟── adding a type annotation to any of the following terms may help resolve the problem +//│ ╟── • this reference: +//│ ║ l.6: discard of y(x) + 1 +//│ ╙── ^ +//│ forall 'a. error | 'a -> 'a +//│ res +//│ = [Function: id] + + +fun f(y) = + let local = forall 'A: (x: 'A) => + discard of (y : forall 'a: 'a -> 'a)(x) + x + y +//│ fun f: forall 'b. (forall 'a. 'a -> 'a & 'b) -> 'b + +f(id) +//│ forall 'a. 'a -> 'a +//│ res +//│ = [Function: id] + + diff --git a/shared/src/test/diff/nu/FieldRefinement.mls b/shared/src/test/diff/nu/FieldRefinement.mls new file mode 100644 index 0000000000..b3c4789409 --- /dev/null +++ b/shared/src/test/diff/nu/FieldRefinement.mls @@ -0,0 +1,36 @@ +:NewDefs +:NoJS + + +class Foo(val x: Int) { + fun bar = x + fun baz: 1 | 2 = 1 +} +//│ class Foo(x: Int) { +//│ fun bar: Int +//│ fun baz: 1 | 2 +//│ } + +let foo: Foo & { x: 0 | 1, bar: 0 | 1, baz: 0 | 1, y: Bool } +//│ let foo: Foo & {y: Bool, bar: 0 | 1, baz: 0 | 1, x: 0 | 1} + +foo.x +//│ 0 | 1 + +foo.bar +//│ 0 | 1 + +foo.baz +//│ 1 + +foo.y +//│ Bool + +:e +foo.z +//│ ╔══[ERROR] Type `Foo & {y: Bool, bar: 0 | 1, baz: 0 | 1, x: 0 | 1}` does not contain member `z` +//│ ║ l.30: foo.z +//│ ╙── ^^ +//│ error + + diff --git a/shared/src/test/diff/nu/FilterMap.mls b/shared/src/test/diff/nu/FilterMap.mls new file mode 100644 index 0000000000..421fe34c4a --- /dev/null +++ b/shared/src/test/diff/nu/FilterMap.mls @@ -0,0 +1,83 @@ +:NewDefs + + +// * From https://arxiv.org/abs/2302.12783 + +// 1 -spec filtermap ( fun (( T ) -> Boolean ()) , [ T ]) -> [ T ] +// 2 ; ( fun (( T ) -> { true , U } | false ) , [ T ]) -> [ U ] +// 3 ; ( fun (( T ) -> { true , U } | Boolean ()) , [ T ]) -> [ T | U ]. +// 4 filtermap ( _F , []) -> []; +// 5 filtermap (F , [ X | XS ]) -> +// 6 case F ( X ) of +// 7 false -> filtermap (F , XS ); +// 8 true -> [ X | filtermap (F , XS )]; +// 9 { true , Y } -> [ Y | filtermap (F , XS )] +// 10 end. + + +module Nil +class Cons[out A](head: A, tail: Cons[A] | Nil) +//│ module Nil +//│ class Cons[A](head: A, tail: Cons[A] | Nil) + + +// FIXME UCS +fun filtermap(f, xs) = if xs is + Nil then Nil + Cons(y, ys) and f(ys) 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) -> nothing, Cons[anything] | Nil) -> (Cons[nothing] | Nil | error) +//│ Code generation encountered an error: +//│ unknown match case: Tuple#2 + + +module Tru +module Fals +//│ module Tru +//│ module Fals + +class Pair[A, B](lhs: A, rhs: B) +//│ class Pair[A, B](lhs: A, rhs: B) + +fun filtermap(f, xs) = if xs is + Nil then Nil + Cons(y, ys) then if f(y) is + Tru then filtermap(f, ys) + Fals then Cons(y, filtermap(f, ys)) + Pair(Tru, z) then Cons(z, filtermap(f, ys)) +//│ fun filtermap: forall 'a 'A. ('a -> (Fals | Pair[Tru, 'A] | Tru), Cons['a & 'A] | Nil) -> (Cons['A] | Nil) + +fun mkString(xs) = + if xs is + Nil then "" + Cons(x, xs') and xs' is + Nil then toString(x) + else concat(toString(x))(concat(", ")(mkString(xs'))) +//│ fun mkString: (Cons[anything] | Nil) -> Str + +let list = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Cons(7, Nil))))))) +mkString of list +//│ let list: Cons[1 | 2 | 3 | 4 | 5 | 6 | 7] +//│ Str +//│ list +//│ = Cons {} +//│ res +//│ = '1, 2, 3, 4, 5, 6, 7' + +mkString of filtermap(x => (if x % 2 == 0 then Tru else Fals), list) +mkString of filtermap(x => (if x % 2 == 0 then Fals else Tru), list) +mkString of filtermap(x => (if + x % 2 == 0 then Fals + x % 3 == 0 then Pair(Tru, x / 3) + else Tru), list) +//│ Str +//│ res +//│ = '1, 3, 5, 7' +//│ res +//│ = '2, 4, 6' +//│ res +//│ = '2, 1, 4, 6' diff --git a/shared/src/test/diff/nu/FlatIfThenElse.mls b/shared/src/test/diff/nu/FlatIfThenElse.mls new file mode 100644 index 0000000000..03a92984ec --- /dev/null +++ b/shared/src/test/diff/nu/FlatIfThenElse.mls @@ -0,0 +1,126 @@ +:NewDefs + + +type Option[out A] = Some[A] | None +class Some[out A](val value: A) +module None +//│ type Option[A] = None | Some[A] +//│ class Some[A](value: A) +//│ module None + + +fun test(x: Option[Int]) = + if x is None then [0, 0] else + log(x.value) + // ... + // do other things on the happy path + // ... + [x.value - 1, x.value + 1] +//│ fun test: (x: Option[Int]) -> [Int, Int] + +test(Some(10)) +//│ [Int, Int] +//│ res +//│ = [ 9, 11 ] +//│ // Output +//│ 10 + + +fun test(x: Option[Int]) = + if x is + None then [0, 0] + Some(value) then + log(value) + [value - 1, value + 1] +//│ fun test: (x: Option[Int]) -> [Int, Int] + +fun test(x: Option[Int]) = + if x is + None then [0, 0] + Some(value) + then + log(value) + [value - 1, value + 1] +//│ fun test: (x: Option[Int]) -> [Int, Int] + +fun test(x: Option[Int]) = + if x is + None then [0, 0] + Some(value) + then + log(value) + [value - 1, value + 1] +//│ fun test: (x: Option[Int]) -> [Int, Int] + +fun test(x: Option[Int]) = + if x is + None then [0, 0] + Some(value) then + log(value) + [value - 1, value + 1] +//│ fun test: (x: Option[Int]) -> [Int, Int] + +:pe // TODO support +fun test(x: Option[Int]) = + if x is + None then [0, 0] + Some(value) then [value - 1, value + 1] +//│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead +//│ ║ l.65: if x is +//│ ║ ^^^^ +//│ ║ l.66: None then [0, 0] +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.67: Some(value) then [value - 1, value + 1] +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ fun test: (x: Option[Int]) -> () + +:pe // TODO support +fun test(x: Option[Int]) = + if x is + None then [0, 0] + Some(value) then + log(value) + [value - 1, value + 1] +//│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead +//│ ║ l.79: if x is +//│ ║ ^^^^ +//│ ║ l.80: None then [0, 0] +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.81: Some(value) then +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.82: log(value) +//│ ║ ^^^^^^^^^^^^ +//│ ║ l.83: [value - 1, value + 1] +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ +//│ fun test: (x: Option[Int]) -> () + +// Q: Support? +:pe +:e +fun test(x: Option[Int]) = + if x is + None then [0, 0] + Some(value) + then + log(value) + [value - 1, value + 1] +//│ ╔══[PARSE ERROR] Unexpected 'then' keyword in expression position +//│ ║ l.104: then +//│ ╙── ^^^^ +//│ ╔══[ERROR] Illegal interleaved statement App(Var(Some),Tup(List((None,Fld(_,Var(value)))))) +//│ ║ l.103: Some(value) +//│ ╙── ^^^^^^^^^^^ +//│ ╔══[ERROR] identifier not found: value +//│ ║ l.105: log(value) +//│ ╙── ^^^^^ +//│ ╔══[ERROR] identifier not found: value +//│ ║ l.106: [value - 1, value + 1] +//│ ╙── ^^^^^ +//│ ╔══[ERROR] identifier not found: value +//│ ║ l.106: [value - 1, value + 1] +//│ ╙── ^^^^^ +//│ fun test: (x: Option[Int]) -> [Int, Int] +//│ Code generation encountered an error: +//│ if expression was not desugared + + diff --git a/shared/src/test/diff/nu/FlatIndentFuns.mls b/shared/src/test/diff/nu/FlatIndentFuns.mls new file mode 100644 index 0000000000..3a58afdd03 --- /dev/null +++ b/shared/src/test/diff/nu/FlatIndentFuns.mls @@ -0,0 +1,64 @@ +:NewDefs + + +x => +y => +x + y +//│ Int -> Int -> Int +//│ res +//│ = [Function: res] + +id of x => +y => +x + y +//│ Int -> Int -> Int +//│ res +//│ = [Function (anonymous)] + +let r = x => +y => +x + y +//│ let r: Int -> Int -> Int +//│ r +//│ = [Function: r] + +r(1)(2) +//│ Int +//│ res +//│ = 3 + +(r of 1) of 2 +//│ Int +//│ res +//│ = 3 + +:e +r of 1 of 2 +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.36: r of 1 of 2 +//│ ║ ^^^^^^ +//│ ╟── integer literal of type `1` is not a function +//│ ║ l.36: r of 1 of 2 +//│ ╙── ^ +//│ Int -> Int +//│ res +//│ Runtime error: +//│ TypeError: 1 is not a function + + +// * Could support this too... +:pe +let r = +x => +y => +x + y +//│ ╔══[PARSE ERROR] Unexpected newline in expression position +//│ ║ l.51: let r = +//│ ║ ^ +//│ ║ l.52: x => +//│ ╙── +//│ let r: Int -> Int -> Int +//│ r +//│ = [Function: r1] + + diff --git a/shared/src/test/diff/nu/FlatMonads.mls b/shared/src/test/diff/nu/FlatMonads.mls new file mode 100644 index 0000000000..2358f88b61 --- /dev/null +++ b/shared/src/test/diff/nu/FlatMonads.mls @@ -0,0 +1,440 @@ +:NewDefs + + +declare fun String: anything -> Str +//│ fun String: anything -> Str + + +abstract class IO[A] { + fun bind(f) = Bind(this, f) + fun run: A +} +class Pure[A](value: A) extends IO[A] { + fun run = value +} +class Bind[A, B](underlying: IO[A], f: A -> IO[B]) extends IO[B] { + fun run = f(underlying.run).run +} +//│ abstract class IO[A] { +//│ fun bind: forall 'B. (A -> IO['B]) -> Bind[A, 'B] +//│ fun run: A +//│ } +//│ class Pure[A](value: A) extends IO { +//│ fun bind: forall 'B0. (A -> IO['B0]) -> Bind[in A & 'A out A, 'B0] +//│ fun run: A +//│ } +//│ class Bind[A, B](underlying: IO[A], f: A -> IO[B]) extends IO { +//│ fun bind: forall 'B1. (B -> IO['B1]) -> Bind[in B & 'A0 out B, 'B1] +//│ fun run: B +//│ } +//│ where +//│ 'A0 := B +//│ 'A := A + +module readInt extends IO[Int] { fun run: Int = 42 } +class printLine(str: Str) extends IO[undefined] { fun run = log(str) } +//│ module readInt extends IO { +//│ fun bind: forall 'B. ('A -> IO['B]) -> Bind[Int & 'A, 'B] +//│ fun run: Int +//│ } +//│ class printLine(str: Str) extends IO { +//│ fun bind: forall 'B0. ('A0 -> IO['B0]) -> Bind[() & 'A0, 'B0] +//│ fun run: () +//│ } +//│ where +//│ 'A0 := () +//│ 'A := Int + + +// * Nested indent: + +val main = + printLine("Hi! Input two numbers: ").bind of _ => + readInt.bind of n => + readInt.bind of m => + val sum = n + m + printLine(concat("The sum is: ")(String of sum)).bind of _ => + Pure(sum) +//│ val main: Bind[(), 'B] +//│ where +//│ 'B :> Int +//│ main +//│ = Bind {} + +main.run +//│ Int +//│ res +//│ = 84 +//│ // Output +//│ Hi! Input two numbers: +//│ The sum is: 84 + + +// * Flat indent: + +val main = + printLine("Hi! Input two numbers: ").bind of _ => + readInt.bind of n => + readInt.bind of m => + val sum = n + m + printLine(concat("The sum is: ")(String of sum)).bind of _ => + Pure(sum) +//│ val main: Bind[(), 'B] +//│ where +//│ 'B :> Int +//│ main +//│ = Bind {} + +main.run +//│ Int +//│ res +//│ = 84 +//│ // Output +//│ Hi! Input two numbers: +//│ The sum is: 84 + + +// * TODO improve this error – missing provenance for '0-element tuple' +:e +printLine("").bind of [] => error +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.99: printLine("").bind of [] => error +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type `()` is not a 0-element tuple +//│ ║ l.35: class printLine(str: Str) extends IO[undefined] { fun run = log(str) } +//│ ╙── ^^^^^^^^^ +//│ Bind[out (), 'B] | error +//│ res +//│ = Bind {} + +// * TODO improve this error (parameter list repr.) +:e +printLine("").bind of () => error +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.112: printLine("").bind of () => error +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type `[?A]` does not match type `[]` +//│ ║ l.15: class Bind[A, B](underlying: IO[A], f: A -> IO[B]) extends IO[B] { +//│ ╙── ^ +//│ Bind[(), 'B] | error +//│ res +//│ = Bind {} + +printLine("").bind of (()) => error +//│ Bind[(), 'B] +//│ res +//│ = Bind {} + + +// * Using a shortand operator for `bind`... What's the use of a `do` notation?! :^) + +fun (#>>) bind[A, B](x: IO[A], f: A -> IO[B]): IO[B] = x.bind(f) +//│ fun (#>>) bind: forall 'A 'B. (x: IO['A], f: 'A -> IO['B]) -> IO['B] + +val main = + printLine("Hi! Input two numbers: ") #>> _ => + readInt #>> n => + readInt #>> m => + val sum = n + m + printLine(concat("The sum is: ")(String of sum)) #>> _ => + Pure(sum) +//│ val main: IO['B] +//│ where +//│ 'B :> Int +//│ main +//│ = Bind {} + +main.run +//│ Int +//│ res +//│ = 84 +//│ // Output +//│ Hi! Input two numbers: +//│ The sum is: 84 + +fun loop = + printLine("Input a positive number: ") #>> _ => + readInt #>> n => + if n < 0 then loop else Pure(n) +//│ fun loop: forall 'B. IO['B] +//│ where +//│ 'B :> Int + +let r = loop.run +//│ let r: Int +//│ r +//│ = 42 +//│ // Output +//│ Input a positive number: + + +// * Using another shortand operator for `map` + +fun (>>) compose[A, B, C](f: A -> B, g: B -> C): A -> C = x => g(f(x)) +fun (#>) map[A, B](x: IO[A], f: A -> B): IO[B] = x.bind(f >> Pure) +//│ fun (>>) compose: forall 'A 'B 'C. (f: 'A -> 'B, g: 'B -> 'C) -> 'A -> 'C +//│ fun (#>) map: forall 'A0 'B0. (x: IO['A0], f: 'A0 -> 'B0) -> IO['B0] + +val main = + printLine("Hi! Input two numbers: ") #>> _ => + readInt #>> n => + readInt #>> m => + val sum = n + m + printLine(concat("The sum is: ")(String of sum)) #> _ => + sum +//│ val main: IO['B] +//│ where +//│ 'B :> Int +//│ main +//│ = Bind {} + + +// * With no type annotations: + +fun (>>) compose(f, g) = x => g(f(x)) +fun (#>>) bind(x, f) = x.bind(f) +fun (#>) map(x, f) = x.bind(f >> Pure) +//│ fun (>>) compose: forall 'a 'b 'c. ('a -> 'b, 'b -> 'c) -> 'a -> 'c +//│ fun (#>>) bind: forall 'd 'e. ({bind: 'd -> 'e}, 'd) -> 'e +//│ fun (#>) map: forall 'f 'g 'A. ({bind: ('g -> Pure['A]) -> 'f}, 'g -> 'A) -> 'f + +val main = + printLine("Hi! Input two numbers: ") #>> _ => + readInt #>> n => + readInt #>> m => + val sum = n + m + printLine(concat("The sum is: ")(String of sum)) #> _ => + sum +//│ val main: Bind[(), 'B] +//│ where +//│ 'B :> Int +//│ main +//│ = Bind {} + +main.run +//│ Int +//│ res +//│ = 84 +//│ // Output +//│ Hi! Input two numbers: +//│ The sum is: 84 + +fun loop = + printLine("Input a positive number: ") #>> _ => + readInt #>> n => + if n < 0 then loop else Pure(n) +//│ fun loop: forall 'B. Bind[(), 'B] +//│ where +//│ 'B :> Int + +let r = loop.run +//│ let r: Int +//│ r +//│ = 42 +//│ // Output +//│ Input a positive number: + + +// * Abstracting over the monad: + +fun main(ctx) = + ctx.printLine("Hi! Input two numbers: ") #>> _ => + ctx.readInt #>> n => + ctx.readInt #>> m => + val sum = n + m + ctx.printLine(concat("The sum is: ")(String of sum)) #>> _ => + ctx.pure(sum) +//│ fun main: forall 'a 'b 'c 'd 'e. { +//│ printLine: "Hi! Input two numbers: " -> {bind: (anything -> 'd) -> 'e} & Str -> {bind: (anything -> 'a) -> 'b}, +//│ pure: Int -> 'a, +//│ readInt: {bind: (Int -> 'c) -> 'd & (Int -> 'b) -> 'c} +//│ } -> 'e + +val defaultCtx = {printLine, readInt, pure: Pure} +//│ val defaultCtx: { +//│ printLine: (str: Str) -> printLine, +//│ pure: forall 'A. (value: 'A) -> Pure['A], +//│ readInt: readInt +//│ } +//│ defaultCtx +//│ = { +//│ printLine: [Function (anonymous)] { +//│ class: [class printLine extends IO], +//│ unapply: [Function: unapply] +//│ }, +//│ readInt: readInt { class: [class readInt extends IO] }, +//│ pure: [Function (anonymous)] { +//│ class: [class Pure extends IO], +//│ unapply: [Function: unapply] +//│ } +//│ } + +main(defaultCtx).run +//│ Int +//│ res +//│ = 84 +//│ // Output +//│ Hi! Input two numbers: +//│ The sum is: 84 + +fun loop(ctx) = + ctx.printLine("Input a positive number: ") #>> _ => + ctx.readInt #>> n => + if n < 0 then loop(ctx) else ctx.pure(n) +//│ fun loop: forall 'a 'b 'c 'd. { +//│ printLine: "Input a positive number: " -> {bind: (anything -> 'a) -> 'b}, +//│ pure: 'c -> 'd, +//│ readInt: {bind: ((Num & 'c) -> ('d | 'b)) -> 'a} +//│ } -> 'b + +let r = loop(defaultCtx) +//│ let r: Bind[(), 'B] +//│ where +//│ 'B :> Int +//│ r +//│ = Bind {} + +let r = loop(defaultCtx).run +//│ let r: Int +//│ r +//│ = 42 +//│ // Output +//│ Input a positive number: + +:e +not(r) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.305: 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) +//│ ╙── ^ +//│ error | false | true +//│ res +//│ = false + + +// * Note: using inferred parent type arguments + +module readInt extends IO { fun run = 42 } +class printLine(str: Str) extends IO { fun run = log(str) } +//│ module readInt extends IO { +//│ fun bind: forall 'B. ('A -> IO['B]) -> Bind['A, 'B] +//│ fun run: 42 +//│ } +//│ class printLine(str: Str) extends IO { +//│ fun bind: forall 'B0. ('A0 -> IO['B0]) -> Bind['A0, 'B0] +//│ fun run: () +//│ } +//│ where +//│ 'A0 :> () +//│ 'A :> 42 + +val main = + printLine("Hi! Input two numbers: ").bind of _ => + readInt.bind of n => + readInt.bind of m => + val sum = n + m + printLine(concat("The sum is: ")(String of sum)).bind of _ => + Pure(sum) +//│ val main: Bind[in 'A out () | 'A, 'B] +//│ where +//│ 'B :> Int +//│ main +//│ = Bind {} + +main +//│ Bind[in 'A out () | 'A, 'B] +//│ where +//│ 'B :> Int +//│ res +//│ = Bind {} + + +:e +let r = printLine("").bind of 0 => Pure(1) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.358: 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) } +//│ ║ ^^^^^^^^ +//│ ╟── Note: constraint arises from integer literal: +//│ ║ l.358: let r = printLine("").bind of 0 => Pure(1) +//│ ╙── ^ +//│ let r: Bind[in 0 & 'A out () | 'A, 'B] | error +//│ where +//│ 'B :> 1 +//│ r +//│ = Bind {} + +:e +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.376: log(x.a) +//│ ║ ^^^^^^^^^ +//│ ║ l.377: Pure(1) +//│ ║ ^^^^^^^ +//│ ╟── application of type `()` does not have field 'a' +//│ ║ l.323: class printLine(str: Str) extends IO { fun run = log(str) } +//│ ║ ^^^^^^^^ +//│ ╟── Note: constraint arises from field selection: +//│ ║ l.376: log(x.a) +//│ ║ ^^^ +//│ ╟── from reference: +//│ ║ l.376: log(x.a) +//│ ╙── ^ +//│ let r: Bind[in {a: anything} & 'A out () | 'A, 'B] | error +//│ where +//│ 'B :> 1 +//│ r +//│ = Bind {} + +:re +r.run +//│ 1 | error +//│ res +//│ Runtime error: +//│ TypeError: Cannot read properties of undefined (reading 'a') + + +// * We can even technically support the following syntax... + +:NoJS // * TODO We'll need to support functions extended with fields + +// * An interface that describes monadic functions with a field `run` +declare trait IO[A]: (forall 'b: (A -> IO['b]) -> IO['b]) { + fun run: A +} +declare fun pure: 'a -> IO['a] +declare fun readInt: IO[Int] +declare fun printLine: Str -> IO[()] +//│ declare trait IO[A]: forall 'b. (A -> IO['b]) -> IO['b] { +//│ fun run: A +//│ } +//│ fun printLine: Str -> IO[()] +//│ fun pure: forall 'a. 'a -> IO['a] +//│ fun readInt: IO[Int] + +val main = + printLine("Hi! Input two numbers: ") of _ => + readInt of n => + readInt of m => + val sum = n + m + printLine(concat("The sum is: ")(String of sum)) of _ => + pure(sum) +//│ val main: IO['b] +//│ where +//│ 'b :> Int + +main.run +//│ Int + + diff --git a/shared/src/test/diff/nu/FlatMonads_repro.mls b/shared/src/test/diff/nu/FlatMonads_repro.mls new file mode 100644 index 0000000000..4ae73f6e72 --- /dev/null +++ b/shared/src/test/diff/nu/FlatMonads_repro.mls @@ -0,0 +1,219 @@ +:NewDefs + + +abstract class IO[A] { + fun bind(f) = Bind(this, f) + fun hey = this + fun run: A +} +class Bind[CC, AA](underlying: IO[CC], f: CC -> IO[AA]) extends IO[AA] { + fun run = f(underlying.run).run +} +class Pure[A](value: A) extends IO[A] { + fun run = value +} +//│ abstract class IO[A] { +//│ fun bind: forall 'AA. (A -> IO['AA]) -> Bind[A, 'AA] +//│ fun hey: IO[A] +//│ fun run: A +//│ } +//│ class Bind[CC, AA](underlying: IO[CC], f: CC -> IO[AA]) extends IO { +//│ fun bind: forall 'AA0. ('A -> IO['AA0]) -> Bind['A, 'AA0] +//│ fun hey: IO['A] +//│ fun run: AA +//│ } +//│ class Pure[A](value: A) extends IO { +//│ fun bind: forall 'AA1. (A -> IO['AA1]) -> Bind[in A & 'A0 out A, 'AA1] +//│ fun hey: IO['A0] +//│ fun run: A +//│ } +//│ where +//│ 'A0 := A +//│ 'A := AA + +module readInt extends IO[Int] { fun run: Int = 42 } +//│ module readInt extends IO { +//│ fun bind: forall 'AA. ('A -> IO['AA]) -> Bind[Int & 'A, 'AA] +//│ fun hey: IO['A] +//│ fun run: Int +//│ } +//│ where +//│ 'A := Int + + +let ri(f) = Bind(Pure(42), f) +// let ri(f) = Bind(Pure(42) : IO[Int], f) +// let ri(f) = Bind(error : IO[Int], f) +//│ let ri: forall 'CC 'AA. ('CC -> IO['AA]) -> Bind['CC, 'AA] +//│ where +//│ 'CC :> 42 +//│ ri +//│ = [Function: ri] + +ri(Pure) +//│ Bind['CC, 'AA] +//│ where +//│ 'CC :> 42 +//│ <: 'AA +//│ 'AA :> 42 +//│ res +//│ = Bind {} + +readInt.bind +//│ forall 'AA. (Int -> IO['AA]) -> Bind[Int, 'AA] +//│ res +//│ = [Function: bind] + +Bind(readInt, Pure) +//│ Bind[Int & 'AA, 'AA] +//│ where +//│ 'AA :> Int +//│ res +//│ = Bind {} + + +// TODO prevent JS method extrusion; force explicit use of eta epxansion + +let b = readInt.bind : (Int -> IO['B]) -> Bind[Int, 'B] +//│ let b: (Int -> IO['B]) -> Bind[Int, 'B] +//│ b +//│ = [Function: bind] + +let b = readInt.bind : ('A -> IO['B]) -> Bind['A, 'B] where 'A : Int +//│ let b: (Int -> IO['B]) -> Bind[Int, 'B] +//│ b +//│ = [Function: bind] + +let b = readInt.bind : ('A -> IO['B]) -> Bind['A, 'B] where Int : 'A +//│ let b: (Int -> IO['B]) -> Bind[Int, 'B] +//│ b +//│ = [Function: bind] + + +let r = b of Pure +//│ let r: Bind[in Int & 'B out Int, 'B] +//│ where +//│ 'B :> Int +//│ r +//│ = Bind {} + +:re // FIXME `undefined` due to JS method extrusion +r.run +//│ Int +//│ res +//│ Runtime error: +//│ TypeError: Cannot read properties of undefined (reading 'run') + + +let r = readInt.bind of Pure +//│ let r: Bind[in Int & 'AA out Int, 'AA] +//│ where +//│ 'AA :> Int +//│ r +//│ = Bind {} + +r.run +//│ Int +//│ res +//│ = 42 + +x => readInt.bind of x +//│ forall 'AA. (Int -> IO['AA]) -> Bind[Int, 'AA] +//│ res +//│ = [Function: res] + +readInt.bind of Pure +//│ Bind[in Int & 'AA out Int, 'AA] +//│ where +//│ 'AA :> Int +//│ res +//│ = Bind {} + +readInt: IO['a] +//│ IO[Int] +//│ res +//│ = readInt { class: [class readInt extends IO] } + +(readInt : IO[Int]).bind +//│ forall 'AA. (Int -> IO['AA]) -> Bind[Int, 'AA] +//│ res +//│ = [Function: bind] + +readInt.run +//│ Int +//│ res +//│ = 42 + +x => Pure(x).run +//│ forall 'run. 'run -> 'run +//│ res +//│ = [Function: res] + + +fun loop0 = readInt.bind of Pure +fun loop1 = readInt.bind of (Pure : Int => IO[Int]) +fun loop2 = readInt.bind of ((x: Int) => Pure(x)) +fun loop3 = readInt.bind of (x => Pure(x) : IO[Int]) +//│ fun loop0: forall 'AA. Bind[in Int & 'AA out Int, 'AA] +//│ fun loop1: Bind[Int, Int] +//│ fun loop2: forall 'AA0. Bind[Int, 'AA0] +//│ fun loop3: Bind[Int, Int] +//│ where +//│ 'AA0 :> Int +//│ 'AA :> Int + +fun (#>>) bindOp(x, f) = x.bind(f) +//│ fun (#>>) bindOp: forall 'a 'b. ({bind: 'a -> 'b}, 'a) -> 'b + +fun loop = + readInt #>> n => + Pure(n) +//│ fun loop: forall 'AA. Bind[in Int & 'AA out Int, 'AA] +//│ where +//│ 'AA :> Int + + + +val x: Bind['A, 'B] where undefined : 'A; 'A : 'B +//│ val x: forall 'A 'B. Bind['A, 'B] +//│ where +//│ 'A :> () +//│ <: 'B +//│ 'B :> () +//│ x +//│ = + +x.run +//│ () +//│ res +//│ = +//│ x is not implemented + +val x: Bind['A, 'B] where 'A : undefined; 'A : 'B +//│ val x: forall 'A 'B. Bind['A, 'B] +//│ where +//│ 'A <: () & 'B +//│ x +//│ = + +x.run +//│ nothing +//│ res +//│ = +//│ x is not implemented + +val x: Bind[Int, Bool] +//│ val x: Bind[Int, Bool] +//│ x +//│ = + +// :d +x.run +//│ Bool +//│ res +//│ = +//│ x is not implemented + + + + diff --git a/shared/src/test/diff/nu/FunPatterns.mls b/shared/src/test/diff/nu/FunPatterns.mls new file mode 100644 index 0000000000..229b978983 --- /dev/null +++ b/shared/src/test/diff/nu/FunPatterns.mls @@ -0,0 +1,46 @@ +:NewDefs +:NoJS + + +fun f(x, y) = x + y +//│ fun f: (Int, Int) -> Int + +// FIXME array pattern...?! +fun f1([x, y]) = x + y +fun f2([x, y],) = x + y +fun f3([[x, y,],],) = x + y +//│ fun f1: ([Int, Int]) -> Int +//│ fun f2: ([Int, Int]) -> Int +//│ fun f3: ([[Int, Int]]) -> Int + +:e +fun f3([(x, y,),],) = x + y +//│ ╔══[ERROR] Unsupported pattern shape: +//│ ║ l.17: fun f3([(x, y,),],) = x + y +//│ ╙── ^^^^^^^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.17: fun f3([(x, y,),],) = x + y +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: y +//│ ║ l.17: fun f3([(x, y,),],) = x + y +//│ ╙── ^ +//│ fun f3: ([error]) -> Int + + +class Pair(lhs: Int, rhs: Int) +//│ class Pair(lhs: Int, rhs: Int) + +:e // * TODO +fun f(Pair(x, y)) = x + y +//│ ╔══[ERROR] Unsupported pattern shape: +//│ ║ l.34: fun f(Pair(x, y)) = x + y +//│ ╙── ^^^^^^^^^^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.34: fun f(Pair(x, y)) = x + y +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: y +//│ ║ l.34: fun f(Pair(x, y)) = x + y +//│ ╙── ^ +//│ fun f: error -> Int + + diff --git a/shared/src/test/diff/nu/FunPoly.mls b/shared/src/test/diff/nu/FunPoly.mls new file mode 100644 index 0000000000..9919df0423 --- /dev/null +++ b/shared/src/test/diff/nu/FunPoly.mls @@ -0,0 +1,68 @@ +:NewDefs + + + +fun id(x) = x +//│ fun id: forall 'a. 'a -> 'a + +[id(1), id(true)] +//│ [1, true] +//│ res +//│ = [ 1, true ] + +not(id(true)) +//│ Bool +//│ res +//│ = false + + + +fun id(x) = x +[id(1), id(true)] +//│ fun id: forall 'a. 'a -> 'a +//│ [1, true] +//│ res +//│ = [ 1, true ] + + + +// * Currently, we type entire typing units monomorphically; +// * later we should try to separate mutually-recursive components and generalize them independently. +fun test = [id(1), id(true)] +fun id(x) = x +//│ fun test: forall 'a 'b. [1 | true | 'a, 1 | true | 'b] +//│ fun id: forall 'a 'b. ('b & 'a) -> (1 | true | 'a) + +[id(1), id(true)] +//│ [1 | true, 1 | true] +//│ res +//│ = [ 1, true ] + +:e +not(id(true)) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.42: not(id(true)) +//│ ║ ^^^^^^^^^^^^^ +//│ ╟── integer literal of type `1` is not an instance of type `Bool` +//│ ║ l.31: fun test = [id(1), id(true)] +//│ ║ ^ +//│ ╟── but it flows into application with expected type `Bool` +//│ ║ l.42: not(id(true)) +//│ ╙── ^^^^^^^^ +//│ error | false | true +//│ res +//│ = false + + + +fun test = [Helper.id(1), Helper.id(true)] +module Helper { + fun id(x) = x +} +//│ fun test: [1, true] +//│ module Helper { +//│ fun id: forall 'a. 'a -> 'a +//│ } + + + diff --git a/shared/src/test/diff/nu/FunSigs.mls b/shared/src/test/diff/nu/FunSigs.mls index c8dba5a11d..ebe09b852a 100644 --- a/shared/src/test/diff/nu/FunSigs.mls +++ b/shared/src/test/diff/nu/FunSigs.mls @@ -1,48 +1,89 @@ -:NewParser +:NewDefs -fun log(msg: string): unit -//│ log: (msg: string,) -> unit -//│ = + +fun log(msg: Str): () +//│ fun log: (msg: Str) -> () let f = log("ok") 123 -//│ f: 123 -//│ = -//│ log is not implemented - -// FIXME -fun log: string -> unit -//│ ╔══[PARSE ERROR] Unexpected '->' keyword in expression position -//│ ║ l.16: fun log: string -> unit -//│ ╙── ^^ -//│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.16: fun log: string -> unit -//│ ╙── ^^^^^^^^^^^^^^ -//│ ╔══[ERROR] Wrong number of type arguments – expected 0, found 1 -//│ ║ l.16: fun log: string -> unit -//│ ╙── ^^^^^^^^^^^^^^ -//│ log: string -//│ = - -fun log: string => unit -//│ log: string -> unit -//│ = +//│ let f: 123 +//│ f +//│ = +//│ log is not implemented + +fun log: Str -> unit +//│ fun log: Str -> unit + +fun log: Str => unit +//│ fun log: Str -> unit log("ok") -//│ = -//│ log is not implemented +//│ unit +//│ res +//│ = +//│ log is not implemented -fun con: string => string => string -//│ con: string -> string -> string -//│ = +fun con: Str => Str => Str fun con = concat -//│ string -> string -> string -//│ <: con: -//│ string -> string -> string -//│ = [Function: con] +//│ fun con: Str -> Str -> Str +//│ fun con: Str -> Str -> Str + +con("aa")("bbb") +//│ Str +//│ res +//│ = 'aabbb' + + +fun con: Str => Str => Str +//│ fun con: Str -> Str -> Str + +fun con = concat +//│ fun con: Str -> Str -> Str + +con("aa")("bbb") +//│ Str +//│ res +//│ = 'aabbb' + + + +fun oops: Str => Str +//│ fun oops: Str -> Str + +fun oops = 0 +//│ fun oops: 0 + +oops +//│ 0 +//│ res +//│ = 0 + + +:e +fun oops: Str => Str +fun oops = 0 +//│ ╔══[ERROR] Type mismatch in definition: +//│ ║ l.68: fun oops = 0 +//│ ║ ^^^^^^^^ +//│ ╟── integer literal of type `0` is not a function +//│ ║ l.68: fun oops = 0 +//│ ║ ^ +//│ ╟── but it flows into definition of method oops with expected type `Str -> Str` +//│ ║ l.68: fun oops = 0 +//│ ║ ^^^^^^^^ +//│ ╟── Note: constraint arises from function type: +//│ ║ l.67: fun oops: Str => Str +//│ ╙── ^^^^^^^^^^ +//│ fun oops: 0 +//│ fun oops: Str -> Str + +oops +//│ Str -> Str +//│ res +//│ = 0 diff --git a/shared/src/test/diff/nu/FunnyIndet.mls b/shared/src/test/diff/nu/FunnyIndet.mls new file mode 100644 index 0000000000..3cd12c5189 --- /dev/null +++ b/shared/src/test/diff/nu/FunnyIndet.mls @@ -0,0 +1,72 @@ +:NewDefs + + +2 + + 2 * + 3 +//│ Int +//│ res +//│ = 8 + +2 - + 3 - + 4 +//│ Int +//│ res +//│ = 3 + +2 + + 2 +//│ Int +//│ res +//│ = 4 + +2 + +2 +//│ Int +//│ res +//│ = 4 + + +:e // TODO support +2 is + 2 +//│ ╔══[ERROR] illegal pattern +//│ ║ l.33: 2 +//│ ╙── ^ +//│ error +//│ Code generation encountered an error: +//│ if expression was not desugared + +if 2 is 2 then true +//│ true +//│ res +//│ = true + +2 is +2 +//│ Bool +//│ res +//│ = true + + +if 2 is +2 then true +//│ true +//│ res +//│ = true + +:pe // TODO support +if 2 is +2 then true +1 then false +//│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause +//│ ║ l.62: 1 then false +//│ ╙── ^^^^^^^^^^^^ +//│ () +//│ res +//│ = true +//│ res +//│ = undefined + + diff --git a/shared/src/test/diff/nu/GADTMono.mls b/shared/src/test/diff/nu/GADTMono.mls new file mode 100644 index 0000000000..0d8323d5fb --- /dev/null +++ b/shared/src/test/diff/nu/GADTMono.mls @@ -0,0 +1,124 @@ +:NewDefs + +trait Expr[A]: LitInt | LitBool | Add | Cond | Pair | Fst | Snd +class LitInt(n: Int) extends Expr[Int] +class LitBool(b: Bool) extends Expr[Bool] +class Add(x: Expr[Int], y: Expr[Int]) extends Expr[Int] +class Cond[T](p: Expr[Bool], t: Expr[T], e: Expr[T]) extends Expr[T] +class Pair[S, T](a: Expr[S], b: Expr[T]) extends Expr[[S, T]] +class Fst[S, T](p: Expr[[S, T]]) extends Expr[S] +class Snd[S, T](p: Expr[[S, T]]) extends Expr[T] +//│ trait Expr[A]: Add | Cond[?] | Fst[?, ?] | LitBool | LitInt | Pair[?, ?] | Snd[?, ?] +//│ class LitInt(n: Int) extends Expr +//│ class LitBool(b: Bool) extends Expr +//│ class Add(x: Expr[Int], y: Expr[Int]) extends Expr +//│ class Cond[T](p: Expr[Bool], t: Expr[T], e: Expr[T]) extends Expr +//│ class Pair[S, T](a: Expr[S], b: Expr[T]) extends Expr +//│ class Fst[S, T](p: Expr[[S, T]]) extends Expr +//│ class Snd[S, T](p: Expr[[S, T]]) extends Expr + +let l1 = LitInt(1) +//│ let l1: LitInt +//│ l1 +//│ = LitInt {} + +// TODO +class Exp[type A] +//│ ╔══[PARSE ERROR] Unexpected 'type' keyword here +//│ ║ l.26: class Exp[type A] +//│ ╙── ^^^^ +//│ class Exp { +//│ constructor() +//│ } + +l1: Expr[Int] +//│ Expr[Int] +//│ res +//│ = LitInt {} + +:e +l1: Expr[Bool] +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.40: l1: Expr[Bool] +//│ ║ ^^ +//│ ╟── type `Int` is not an instance of type `Bool` +//│ ║ l.4: class LitInt(n: Int) extends Expr[Int] +//│ ║ ^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.40: l1: Expr[Bool] +//│ ╙── ^^^^ +//│ Expr[Bool] +//│ res +//│ = LitInt {} + +// FIXME +fun eval[A](e: Expr[A]): A = + if + e is LitInt(n) then n + e is LitBool(b) then b + e is Add(x, y) then eval(x) + eval(y) +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.57: e is LitInt(n) then n +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.58: e is LitBool(b) then b +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.59: e is Add(x, y) then eval(x) + eval(y) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type `#Expr & (Add & {Expr#A = A} | Cond[?] & {Expr#A = A} | Fst[?, ?] & {Expr#A = A} | LitBool & {Expr#A = A} | LitInt & {Expr#A = A} | Pair[?, ?] & {Expr#A = A} | Snd[?, ?] & {Expr#A = A})` does not match type `Add | LitBool | LitInt` +//│ ║ l.55: fun eval[A](e: Expr[A]): A = +//│ ║ ^^^^^^^ +//│ ╟── but it flows into reference with expected type `Add | LitBool | LitInt` +//│ ║ l.57: e is LitInt(n) then n +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.57: e is LitInt(n) then n +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.58: e is LitBool(b) then b +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.59: e is Add(x, y) then eval(x) + eval(y) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type `Int` does not match type `A` +//│ ║ l.4: class LitInt(n: Int) extends Expr[Int] +//│ ║ ^^^ +//│ ╟── but it flows into reference with expected type `A` +//│ ║ l.57: e is LitInt(n) then n +//│ ║ ^ +//│ ╟── Note: constraint arises from method type parameter: +//│ ║ l.55: fun eval[A](e: Expr[A]): A = +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in definition: +//│ ║ l.55: fun eval[A](e: Expr[A]): A = +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.56: if +//│ ║ ^^^^^^^ +//│ ║ l.57: e is LitInt(n) then n +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.58: e is LitBool(b) then b +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.59: e is Add(x, y) then eval(x) + eval(y) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type `Int` does not match type `A` +//│ ║ l.6: class Add(x: Expr[Int], y: Expr[Int]) extends Expr[Int] +//│ ║ ^^^ +//│ ╟── Note: constraint arises from method type parameter: +//│ ║ l.55: fun eval[A](e: Expr[A]): A = +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in definition: +//│ ║ l.55: fun eval[A](e: Expr[A]): A = +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.56: if +//│ ║ ^^^^^^^ +//│ ║ l.57: e is LitInt(n) then n +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.58: e is LitBool(b) then b +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.59: e is Add(x, y) then eval(x) + eval(y) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type `A` is not an instance of type `Int` +//│ ║ l.55: fun eval[A](e: Expr[A]): A = +//│ ║ ^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.6: class Add(x: Expr[Int], y: Expr[Int]) extends Expr[Int] +//│ ╙── ^^^ +//│ fun eval: forall 'A. (e: Expr['A]) -> 'A + diff --git a/shared/src/test/diff/nu/GenericClassInheritance.mls b/shared/src/test/diff/nu/GenericClassInheritance.mls new file mode 100644 index 0000000000..784e81453a --- /dev/null +++ b/shared/src/test/diff/nu/GenericClassInheritance.mls @@ -0,0 +1,198 @@ +:NewDefs + +class Room[A](name: Str) { + virtual fun foo(x: A) = x +} +//│ class Room[A](name: Str) { +//│ fun foo: (x: A) -> A +//│ } + +class BigRoom extends Room[Bool]("big") +//│ class BigRoom extends Room { +//│ constructor() +//│ fun foo: (x: 'A) -> 'A +//│ } +//│ where +//│ 'A := Bool + +// * Note that this essentially infers Room[Bool] +class InferredRoom extends Room("infer") { + fun foo(x) = x && true +} +//│ class InferredRoom extends Room { +//│ constructor() +//│ fun foo: Bool -> Bool +//│ } + +(new InferredRoom) : Room['X] +//│ Room['X] +//│ where +//│ 'X := Bool +//│ res +//│ = InferredRoom {} + +:e +class TooManyRoom extends Room[Int, Str]("too many") +//│ ╔══[ERROR] class Room expects 1 type parameter(s); got 2 +//│ ║ l.35: class TooManyRoom extends Room[Int, Str]("too many") +//│ ╙── ^^^^^^^^^^^^^ +//│ class TooManyRoom extends Room { +//│ constructor() +//│ fun foo: (x: 'A) -> 'A +//│ } +//│ where +//│ 'A := Int + +:e +class WrongRoom extends Room[Bool]("wrong") { + fun foo(x) = x + 1 +} +//│ ╔══[ERROR] Type mismatch in definition of method foo: +//│ ║ l.48: fun foo(x) = x + 1 +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── type `Bool` is not an instance of type `Int` +//│ ║ l.47: class WrongRoom extends Room[Bool]("wrong") { +//│ ║ ^^^^ +//│ ╟── Note: constraint arises from reference: +//│ ║ l.48: fun foo(x) = x + 1 +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in definition of method foo: +//│ ║ l.48: fun foo(x) = x + 1 +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── type `Bool` is not an instance of type `Int` +//│ ║ l.47: class WrongRoom extends Room[Bool]("wrong") { +//│ ║ ^^^^ +//│ ╟── Note: constraint arises from reference: +//│ ║ l.48: fun foo(x) = x + 1 +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in definition of method foo: +//│ ║ l.48: fun foo(x) = x + 1 +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── operator application of type `Int` is not an instance of type `Bool` +//│ ║ l.48: fun foo(x) = x + 1 +//│ ║ ^^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.47: class WrongRoom extends Room[Bool]("wrong") { +//│ ║ ^^^^ +//│ ╟── from reference: +//│ ║ l.4: virtual fun foo(x: A) = x +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in definition of method foo: +//│ ║ l.48: fun foo(x) = x + 1 +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── operator application of type `Int` does not match type `Bool` +//│ ║ l.48: fun foo(x) = x + 1 +//│ ║ ^^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.47: class WrongRoom extends Room[Bool]("wrong") { +//│ ║ ^^^^ +//│ ╟── from reference: +//│ ║ l.4: virtual fun foo(x: A) = x +//│ ╙── ^ +//│ class WrongRoom extends Room { +//│ constructor() +//│ fun foo: Int -> Int +//│ } + + +abstract class C0[A] { val a: A } +//│ abstract class C0[A] { +//│ val a: A +//│ } + +class C1[A] extends C0[A] { val a = a } +//│ class C1[A] extends C0 { +//│ constructor() +//│ val a: nothing +//│ } + +new C1() : C1[Int] +//│ C1[Int] +//│ res +//│ = C1 {} + +(new C1() : C1[Int]) : C0['X] +//│ C0[Int] +//│ res +//│ = C1 {} + +new C1().a +//│ nothing +//│ res +//│ = undefined + + +mixin M1[A] { + fun f1(x: A): A = x + fun f2(x: A): [A, A] = [x, x] +} +//│ mixin M1[A]() { +//│ fun f1: (x: A) -> A +//│ fun f2: (x: A) -> [A, A] +//│ } + +class A1 extends M1 { + fun f1(x: Int) = x +} +//│ class A1 { +//│ constructor() +//│ fun f1: (x: Int) -> Int +//│ fun f2: (x: 'A) -> ['A, 'A] +//│ } + +class A2[S, T] extends M1[[S, T]] +//│ class A2[S, T] { +//│ constructor() +//│ fun f1: (x: [S, T]) -> [S, T] +//│ fun f2: (x: [S, T]) -> [[S, T], [S, T]] +//│ } + +class A3(f1: Bool => Bool) extends M1 +//│ class A3(f1: Bool -> Bool) { +//│ fun f1: (x: 'A) -> 'A +//│ fun f2: (x: 'A) -> ['A, 'A] +//│ } + +mixin M2[A] { + fun m: A = this.a +} +//│ mixin M2[A]() { +//│ this: {a: A} +//│ fun m: A +//│ } + +class B1(val a: Int) extends M2[Int] +//│ class B1(a: Int) { +//│ fun m: Int +//│ } + +class B2[A](val a: Int => A) extends M2 +//│ class B2[A](a: Int -> A) { +//│ fun m: Int -> A +//│ } + +:e +class E1(val a: Int) extends M2[Bool] +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.175: class E1(val a: Int) extends M2[Bool] +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── expression of type `Int` is not an instance of type `Bool` +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.175: class E1(val a: Int) extends M2[Bool] +//│ ║ ^^^^ +//│ ╟── from field selection: +//│ ║ l.157: fun m: A = this.a +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.175: class E1(val a: Int) extends M2[Bool] +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── expression of type `Int` does not match type `Bool` +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.175: class E1(val a: Int) extends M2[Bool] +//│ ║ ^^^^ +//│ ╟── from field selection: +//│ ║ l.157: fun m: A = this.a +//│ ╙── ^^^^^^ +//│ class E1(a: Int) { +//│ fun m: Bool +//│ } diff --git a/shared/src/test/diff/nu/GenericClasses.mls b/shared/src/test/diff/nu/GenericClasses.mls new file mode 100644 index 0000000000..8ce63d9db9 --- /dev/null +++ b/shared/src/test/diff/nu/GenericClasses.mls @@ -0,0 +1,442 @@ +:NewDefs + + +class C +//│ class C[A] { +//│ constructor() +//│ } + +fun f(x) = if x is C then x +//│ fun f: forall 'A. C['A] -> C['A] + +// * TODO parse class tags? +// f(C : #C) + + +class C(val a: A) +//│ class C[A](a: A) + +let c = C(1) +//│ let c: C[1] +//│ c +//│ = C {} + +c.a +//│ 1 +//│ res +//│ = 1 + +fun f(x) = if x is C(a) then a +//│ fun f: forall 'a. C['a] -> 'a + +f(c) +//│ 1 +//│ res +//│ = 1 + + +class C[A](val n: A) { + fun f = this.n + fun g = C(12).n +} +//│ class C[A](n: A) { +//│ fun f: A +//│ fun g: 12 +//│ } + + +class Some(val value: A) { + fun get = value + fun toArray = [value] + fun map(f) = Some(f(value)) + fun map_A(f : A => 'b) = Some(f(value)) +} +//│ class Some[A](value: A) { +//│ fun get: A +//│ fun map: forall 'A. (A -> 'A) -> Some['A] +//│ fun map_A: forall 'A0. (A -> 'A0) -> Some['A0] +//│ fun toArray: [A] +//│ } + + +let s = Some(1) +//│ let s: Some[1] +//│ s +//│ = Some {} + + +s.value +//│ 1 +//│ res +//│ = 1 + +s.get +//│ 1 +//│ res +//│ = 1 + +s.toArray +//│ [1] +//│ res +//│ = [ 1 ] + + +s.map +//│ forall 'A. (1 -> 'A) -> Some['A] +//│ res +//│ = [Function: map] + +s.map(succ) +//│ Some[Int] +//│ res +//│ = Some {} + + +s.map_A +//│ forall 'A. (1 -> 'A) -> Some['A] +//│ res +//│ = [Function: map_A] + +s.map_A(succ) +//│ Some[Int] +//│ res +//│ = Some {} + + + +module None { + fun get = error + fun toArray = [] + fun map(f) = None + fun map_A(f: nothing -> anything) = None +} +//│ module None { +//│ fun get: nothing +//│ fun map: anything -> None +//│ fun map_A: (f: nothing -> anything) -> None +//│ fun toArray: [] +//│ } + + +None.toArray +//│ [] +//│ res +//│ = [] + + +type Option = Some | None +//│ type Option[A] = None | Some[A] + + + +let opt = if true then Some(123) else None +//│ let opt: None | Some[123] +//│ opt +//│ = Some {} + +opt.toArray +//│ Array[123] +//│ res +//│ = [ 123 ] + + +opt.map(succ) +//│ None | Some[Int] +//│ res +//│ = Some {} + +opt.map_A(succ) +//│ None | Some[Int] +//│ res +//│ = Some {} + +opt.map(x => x > 0) +//│ None | Some[Bool] +//│ res +//│ = Some {} + + + +if opt is Some then opt.value else 0 +//│ 0 | 123 +//│ res +//│ = 123 + +if opt is Some(v) then v else 0 +//│ 0 | 123 +//│ res +//│ = 123 + + +fun map(x, f) = if x is + None then None + Some(v) then Some(f(v)) +//│ fun map: forall 'a 'A. (None | Some['a], 'a -> 'A) -> (None | Some['A]) + +let mo = map(opt, succ) +//│ let mo: None | Some[Int] +//│ mo +//│ = Some {} + +mo.toArray +//│ Array[Int] +//│ res +//│ = [ 124 ] + + + +// TODO +class Test(n) { + fun foo = n + 1 + fun bar = n +} +//│ ╔══[ERROR] Class parameters currently need type annotations +//│ ║ l.189: class Test(n) { +//│ ╙── ^ +//│ class Test(n: error) { +//│ fun bar: error +//│ fun foo: Int +//│ } + +Test(1) +//│ Test +//│ res +//│ = Test {} + +// :e +Test(true) +//│ Test +//│ res +//│ = Test {} + + +:e +class Test(n: A) { + fun foo = n + 1 +} +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.215: fun foo = n + 1 +//│ ║ ^^^^^ +//│ ╟── reference of type `A` is not an instance of type `Int` +//│ ║ l.215: fun foo = n + 1 +//│ ║ ^ +//│ ╟── Note: type parameter A is defined at: +//│ ║ l.214: class Test(n: A) { +//│ ╙── ^ +//│ class Test[A](n: A) { +//│ fun foo: Int | error +//│ } + +Test(1) +//│ Test[1] +//│ res +//│ = Test {} + +Test(true) +//│ Test[true] +//│ res +//│ = Test {} + + +class Test(n: A) { + fun foo: A = n + fun foo1(x: A) = x + fun id(x) = x +} +//│ class Test[A](n: A) { +//│ fun foo: A +//│ fun foo1: (x: A) -> A +//│ fun id: forall 'a. 'a -> 'a +//│ } + +Test(1) +//│ Test['A] +//│ where +//│ 'A :> 1 +//│ res +//│ = Test {} + +Test(1).foo +//│ 1 +//│ res +//│ = 1 + +Test("ok").foo +//│ "ok" +//│ res +//│ = 'ok' + +let t = Test(1) +//│ let t: Test['A] +//│ where +//│ 'A :> 1 +//│ t +//│ = Test {} + +t.foo1(true) +//│ 1 | true +//│ res +//│ = true + +t : Test<'a> +//│ Test['a] +//│ where +//│ 'a :> 1 | true +//│ res +//│ = Test {} + +t.id +//│ forall 'a. 'a -> 'a +//│ res +//│ = [Function: id] + +[t.id(1), t.id(true)] +//│ [1, true] +//│ res +//│ = [ 1, true ] + + +:e +class TestBad() { + fun foo1(x: A) = x + fun foo2(x: A) = x + 1 +} +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.302: fun foo2(x: A) = x + 1 +//│ ║ ^^^^^ +//│ ╟── reference of type `A` is not an instance of type `Int` +//│ ║ l.302: fun foo2(x: A) = x + 1 +//│ ║ ^ +//│ ╟── Note: type parameter A is defined at: +//│ ║ l.300: class TestBad() { +//│ ╙── ^ +//│ class TestBad[A]() { +//│ fun foo1: (x: A) -> A +//│ fun foo2: (x: A) -> (Int | error) +//│ } + +TestBad().foo1 +//│ (x: 'A) -> 'A +//│ res +//│ = [Function: foo1] + +TestBad().foo1(1) +//│ 1 +//│ res +//│ = 1 + +x => TestBad().foo1(x) +//│ forall 'a. 'a -> 'a +//│ res +//│ = [Function: res] + +// :d +let t = TestBad() +//│ let t: forall 'A. TestBad['A] +//│ t +//│ = TestBad {} + +t.foo1 +//│ (x: 'A) -> 'A +//│ res +//│ = [Function: foo1] + +[t.foo1(0), t.foo1(true)] +//│ [0, true] +//│ res +//│ = [ 0, true ] + +t.foo1(0) +//│ 0 +//│ res +//│ = 0 + +t +//│ forall 'A. TestBad['A] +//│ res +//│ = TestBad {} + + +fun foo(x: TestBad) = x.foo1 +//│ fun foo: (x: TestBad[Int]) -> (x: Int) -> Int + +foo(t) +//│ (x: Int) -> Int +//│ res +//│ = [Function: foo1] + +foo(t)(1) +//│ Int +//│ res +//│ = 1 + + +TestBad().foo2 +//│ (x: anything) -> (Int | error) +//│ res +//│ = [Function: foo2] + + + +class Weird(val x: C<'a>) +//│ class Weird(x: C['a]) + +let w = Weird(c) +//│ let w: Weird +//│ w +//│ = Weird {} + +w.x +//│ C['a] +//│ res +//│ = C {} + +not(w.x.n) +//│ Bool +//│ res +//│ = true + +:e +not(w.x.a) +//│ ╔══[ERROR] Type `C['a]` does not contain member `a` +//│ ║ l.400: not(w.x.a) +//│ ╙── ^^ +//│ Bool +//│ res +//│ = false + + + +abstract class Cls[A](val x: A) { fun g: A -> Int } +//│ abstract class Cls[A](x: A) { +//│ fun g: A -> Int +//│ } + +module M extends Cls(123) { fun g = id } +//│ module M extends Cls { +//│ fun g: forall 'a. 'a -> 'a +//│ } + +M: Cls['a] +//│ Cls['a] +//│ where +//│ 'a :> 123 +//│ <: Int +//│ res +//│ = M { class: [class M extends Cls] } + + +class Cls[A](val x: A) { fun g: A -> Int; fun g(x) = 42 } +//│ class Cls[A](x: A) { +//│ fun g: A -> Int +//│ } + +Cls(123) +//│ Cls['A] +//│ where +//│ 'A :> 123 +//│ res +//│ = Cls {} + + + diff --git a/shared/src/test/diff/nu/GenericMethods.mls b/shared/src/test/diff/nu/GenericMethods.mls new file mode 100644 index 0000000000..a0d7466a87 --- /dev/null +++ b/shared/src/test/diff/nu/GenericMethods.mls @@ -0,0 +1,130 @@ +:NewDefs + + +fun foo1 = forall 'A: (x: 'A) => x +//│ fun foo1: forall 'A. (x: 'A) -> 'A + +foo1(42) +//│ 42 +//│ res +//│ = 42 + +:e +foo1[Int](42) +//│ ╔══[ERROR] Type application syntax is not yet supported +//│ ║ l.13: foo1[Int](42) +//│ ╙── ^^^^^^^^^ +//│ 42 +//│ res +//│ = 42 + + +fun foo2(x: A) = x +//│ fun foo2: forall 'A. (x: 'A) -> 'A + +foo2(42) +//│ 42 +//│ res +//│ = 42 + +:e +foo2(42) +//│ ╔══[ERROR] Type application syntax is not yet supported +//│ ║ l.31: foo2(42) +//│ ╙── ^^^^^^^^^ +//│ 42 +//│ res +//│ = 42 + + +fun foo3[A](x: A) = x +//│ fun foo3: forall 'A. (x: 'A) -> 'A + +foo3(42) +//│ 42 +//│ res +//│ = 42 + +:e +foo3[Int](42) +//│ ╔══[ERROR] Type application syntax is not yet supported +//│ ║ l.49: foo3[Int](42) +//│ ╙── ^^^^^^^^^ +//│ 42 +//│ res +//│ = 42 + + +fun bar: forall 'A: 'A => 'A +//│ fun bar: forall 'A. 'A -> 'A + +:e +fun bar : A => A +//│ ╔══[ERROR] Type parameters are not yet supported in this position +//│ ║ l.62: fun bar : A => A +//│ ╙── ^ +//│ fun bar: forall 'A. 'A -> 'A + +:pe +:e +:w +fun bar: A => A +//│ ╔══[PARSE ERROR] Unmatched opening angle bracket +//│ ║ l.71: fun bar: A => A +//│ ║ ^ +//│ ╙── Note that `<` without spaces around it is considered as an angle bracket and not as an operator +//│ ╔══[PARSE ERROR] Unexpected 'fun' keyword in expression position +//│ ║ l.71: fun bar: A => A +//│ ╙── ^^^ +//│ ╔══[WARNING] Paren-less applications should use the 'of' keyword +//│ ║ l.71: fun bar: A => A +//│ ╙── ^^^^^^^^^^^^^^ +//│ ╔══[ERROR] identifier not found: >: +//│ ║ l.71: fun bar: A => A +//│ ╙── ^^ +//│ ╔══[ERROR] identifier not found: A +//│ ║ l.71: fun bar: A => A +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: A +//│ ║ l.71: fun bar: A => A +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: A +//│ ║ l.71: fun bar: A => A +//│ ╙── ^ +//│ error +//│ Code generation encountered an error: +//│ unresolved symbol >: + + +:e +module Test { + fun foo: 'A => 'A + fun bar: 'A + fun test(x: A) = x +} +//│ ╔══[ERROR] Member `foo` is declared (or its declaration is inherited) but is not implemented in `Test` +//│ ║ l.100: module Test { +//│ ║ ^^^^ +//│ ╟── Declared here: +//│ ║ l.101: fun foo: 'A => 'A +//│ ╙── ^^^^^^^^^^^^^^^^^ +//│ ╔══[ERROR] Member `bar` is declared (or its declaration is inherited) but is not implemented in `Test` +//│ ║ l.100: module Test { +//│ ║ ^^^^ +//│ ╟── Declared here: +//│ ║ l.102: fun bar: 'A +//│ ╙── ^^^^^^^^^^^ +//│ module Test { +//│ fun bar: nothing +//│ fun foo: forall 'A. 'A -> 'A +//│ fun test: forall 'A0. (x: 'A0) -> 'A0 +//│ } + +class Test(n: A) { + fun test(x: A) = [x, n] +} +//│ class Test[A](n: A) { +//│ fun test: forall 'A. (x: 'A) -> ['A, A] +//│ } + + diff --git a/shared/src/test/diff/nu/GenericMixins.mls b/shared/src/test/diff/nu/GenericMixins.mls new file mode 100644 index 0000000000..f90665c6ab --- /dev/null +++ b/shared/src/test/diff/nu/GenericMixins.mls @@ -0,0 +1,191 @@ +:NewDefs + + + +// TODO support +mixin BaseTest[A] { + fun test(x: A) = x +} +//│ mixin BaseTest[A]() { +//│ fun test: (x: A) -> A +//│ } + +mixin BaseTest[A](x: A) { + fun test = x +} +//│ mixin BaseTest[A](x: A) { +//│ fun test: A +//│ } + + + +mixin Test[A] { + fun foo: A -> A + fun foo = id + fun bar: (A -> A) -> (A -> A) + fun bar = id +} +//│ mixin Test[A]() { +//│ fun bar: (A -> A) -> A -> A +//│ fun foo: A -> A +//│ } + +module C extends Test { + fun baz1 = this.foo(0) + fun baz2 = this.bar(this.foo) +} +//│ module C { +//│ fun bar: ((0 | 'A) -> 'A) -> 'A -> (0 | 'A) +//│ fun baz1: 0 | 'A +//│ fun baz2: 'A -> (0 | 'A) +//│ fun foo: 'A -> (0 | 'A) +//│ } + +C.baz1 +//│ 0 +//│ res +//│ = 0 + +C.foo(1) +//│ 0 | 1 +//│ res +//│ = 1 + +C.foo(false) +//│ 0 | false +//│ res +//│ = false + +module C extends Test[Int] { + fun baz1 = this.foo(0) + fun baz2 = this.bar(this.foo) +} +//│ module C { +//│ fun bar: (Int -> Int) -> Int -> Int +//│ fun baz1: Int +//│ fun baz2: Int -> Int +//│ fun foo: Int -> Int +//│ } + +C.baz1 +//│ Int +//│ res +//│ = 0 + +C.foo(1) +//│ Int +//│ res +//│ = 1 + +:e +C.foo(false) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.81: C.foo(false) +//│ ║ ^^^^^^^^^^^^ +//│ ╟── reference of type `false` is not an instance of `Int` +//│ ║ l.81: C.foo(false) +//│ ║ ^^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.59: module C extends Test[Int] { +//│ ║ ^^^ +//│ ╟── Note: type parameter A is defined at: +//│ ║ l.22: mixin Test[A] { +//│ ╙── ^ +//│ Int | error +//│ res +//│ = false + +class C[A] extends Test[Array[A]] { + fun baz1 = this.foo([]) + fun baz2 = this.bar(this.foo) +} +//│ class C[A] { +//│ constructor() +//│ fun bar: (Array[A] -> Array[A]) -> Array[A] -> Array[A] +//│ fun baz1: Array[A] +//│ fun baz2: Array[A] -> Array[A] +//│ fun foo: Array[A] -> Array[A] +//│ } + + + +mixin Test[A] { + fun foo: A -> A + fun foo = id + fun bar: [A, A] + fun bar = [this.arg, this.arg] + fun baz = foo(this.arg) +} +//│ mixin Test[A]() { +//│ this: {arg: A & 'a} +//│ fun bar: [A, A] +//│ fun baz: 'a +//│ fun foo: A -> A +//│ } + +class C(val arg: Int) extends Test +//│ class C(arg: Int) { +//│ fun bar: [Int | 'A, Int | 'A] +//│ fun baz: Int +//│ fun foo: 'A -> (Int | 'A) +//│ } + +let c = C(1) +[c.foo(false), c.bar] +//│ let c: C +//│ [Int | false, [Int, Int]] +//│ c +//│ = C {} +//│ res +//│ = [ false, [ 1, 1 ] ] + +:e // FIXME +module D extends C(0) { + this.foo(false) +} +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.144: this.foo(false) +//│ ╙── ^^^^ +//│ ╔══[ERROR] Cannot access `this` during object initialization +//│ ║ l.144: this.foo(false) +//│ ╙── ^^^^ +//│ module D extends C { +//│ fun bar: forall 'A. [Int | 'A, Int | 'A] +//│ fun baz: Int +//│ fun foo: forall 'A. 'A -> (Int | 'A) +//│ } + +:e // TODO support or produce better error (arg is not actually recursive) +class C extends Test { // it also fails with Test[Int]... + fun arg = 123 +} +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.117: fun baz = foo(this.arg) +//│ ╙── ^^^^ +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.116: fun bar = [this.arg, this.arg] +//│ ╙── ^^^^ +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.116: fun bar = [this.arg, this.arg] +//│ ╙── ^^^^ +//│ class C { +//│ constructor() +//│ fun arg: 123 +//│ fun bar: [error | 'A, error | 'A] +//│ fun baz: error +//│ fun foo: 'A -> (error | 'A) +//│ } + +class C extends Test { + fun arg: Int + fun arg = 123 +} +//│ class C { +//│ constructor() +//│ fun arg: Int +//│ fun bar: [Int | 'A, Int | 'A] +//│ fun baz: Int +//│ fun foo: 'A -> (Int | 'A) +//│ } + + diff --git a/shared/src/test/diff/nu/GenericModules.mls b/shared/src/test/diff/nu/GenericModules.mls new file mode 100644 index 0000000000..778511bb83 --- /dev/null +++ b/shared/src/test/diff/nu/GenericModules.mls @@ -0,0 +1,181 @@ +:NewDefs + + +// * TODO generic module definitions need to be restricted so they do not include nay state +// * to avoid unsoundness... + + +module Test[A] { + fun foo: A -> A = id +} +//│ module Test[A] { +//│ fun foo: A -> A +//│ } + +Test.foo(1) + 1 +//│ Int +//│ res +//│ = 2 + +not(Test.foo(true)) +//│ Bool +//│ res +//│ = false + +Test.foo +//│ 'A -> 'A +//│ res +//│ = [Function: id] + + +// * FIXME merging generic module types, as we currently do, is unsound: + +let t = Test : Test[Int] & Test[Str] +//│ let t: Test[in Int | Str out nothing] +//│ t +//│ = Test { class: [class Test] } + +t.foo +//│ (Int | Str) -> nothing +//│ res +//│ = [Function: id] + +:re +t.foo(1)(2) +//│ nothing +//│ res +//│ Runtime error: +//│ TypeError: t.foo(...) is not a function + + + +module Test { + fun foo: A => A + fun foo = id + fun bar: A => A = id + fun baz(x: A) = x + fun poly0: 'a -> 'a + fun poly0 = id + fun poly1: forall 'a: 'a -> 'a + fun poly1 = id + fun poly2: 'a -> 'a = id +} +//│ module Test[A] { +//│ fun bar: A -> A +//│ fun baz: (x: A) -> A +//│ fun foo: A -> A +//│ fun poly0: forall 'a. 'a -> 'a +//│ fun poly1: forall 'a0. 'a0 -> 'a0 +//│ fun poly2: forall 'a1. 'a1 -> 'a1 +//│ } + +Test.foo +//│ 'A -> 'A +//│ res +//│ = [Function: id] + +Test.bar +//│ 'A -> 'A +//│ res +//│ = [Function: id] + +Test.baz +//│ (x: 'A) -> 'A +//│ res +//│ = [Function: baz] + +Test.poly0 +//│ forall 'a. 'a -> 'a +//│ res +//│ = [Function: id] + +Test.poly1 +//│ forall 'a. 'a -> 'a +//│ res +//│ = [Function: id] + +Test.poly2 +//│ forall 'a. 'a -> 'a +//│ res +//│ = [Function: id] + +Test.foo(1) +//│ 1 +//│ res +//│ = 1 + +:re +Test.foo(error) + 1 +//│ Int +//│ res +//│ Runtime error: +//│ Error: an error was thrown + +:e +Test .foo +//│ ╔══[ERROR] Type application syntax is not yet supported +//│ ║ l.115: Test .foo +//│ ╙── ^^^^^^^^^ +//│ 'A -> 'A +//│ res +//│ = [Function: id] + +:e +(Test).foo +//│ ╔══[ERROR] Type application syntax is not yet supported +//│ ║ l.124: (Test).foo +//│ ╙── ^^^^^^^^^ +//│ 'A -> 'A +//│ res +//│ = [Function: id] + + +Test +//│ forall 'A. Test['A] +//│ res +//│ = Test { class: [class Test] } + +Test : Test<'a> +//│ Test['a] +//│ res +//│ = Test { class: [class Test] } + + +fun test(x) = if x is Test then x.foo +//│ fun test: forall 'A. Test['A] -> 'A -> 'A + +test(Test) +//│ 'A -> 'A +//│ res +//│ = [Function: id] + + +module Test { + fun foo = id +} +//│ module Test[A] { +//│ fun foo: forall 'a. 'a -> 'a +//│ } + +Test.foo +//│ forall 'a. 'a -> 'a +//│ res +//│ = [Function: id] + + +module Test { + fun foo: A => A + fun foo = id +} +//│ module Test[A] { +//│ fun foo: A -> A +//│ } + +Test.foo +//│ 'A -> 'A +//│ res +//│ = [Function: id] + + + + diff --git a/shared/src/test/diff/nu/HeungTung.mls b/shared/src/test/diff/nu/HeungTung.mls new file mode 100644 index 0000000000..98f962aee7 --- /dev/null +++ b/shared/src/test/diff/nu/HeungTung.mls @@ -0,0 +1,316 @@ +:NewDefs + + + +trait A +trait B +//│ trait A +//│ trait B + +module AA extends A, B +//│ module AA extends A, B + +fun x: A & B +fun x = AA +//│ fun x: AA +//│ fun x: A & B + +x : A +//│ A +//│ res +//│ = AA { class: [class AA extends Object] } + + + +abstract class Foo[A, B] { fun x: A & B } +//│ abstract class Foo[A, B] { +//│ fun x: A & B +//│ } + +module Bar extends Foo[Int, Bool] { fun x = x } +//│ module Bar extends Foo { +//│ fun x: nothing +//│ } + +module Bar extends Foo { fun x = () } +//│ module Bar extends Foo { +//│ fun x: () +//│ } + +Bar : Foo['a, 'b] +//│ Foo['a, 'b] +//│ where +//│ 'b :> () +//│ 'a :> () +//│ res +//│ = Bar { class: [class Bar extends Foo] } + + +// * An overloaded function type +fun f: (Int -> Int) & (Bool -> Bool) +fun f = id +//│ fun f: forall 'a. 'a -> 'a +//│ fun f: Int -> Int & Bool -> Bool + + +// * Widen the results +fun h: (Int -> (Int | Bool)) & (Bool -> (Int | Bool)) +fun h = f +//│ fun h: Int -> Int & Bool -> Bool +//│ fun h: (Int | false | true) -> (Int | false | true) + +// * Merge intersected functions with same domain +fun g: (Int | Bool) -> (Int | Bool) +fun g = h +//│ fun g: (Int | false | true) -> (Int | false | true) +//│ fun g: (Int | false | true) -> (Int | false | true) + +// * In one step +fun g: (Int | Bool) -> (Int | Bool) +fun g = f +//│ fun g: Int -> Int & Bool -> Bool +//│ fun g: (Int | false | true) -> (Int | false | true) + + +// * Can also widen into intersection +fun i: ((Int & Bool) -> Int) & ((Int & Bool) -> Bool) +fun i = f +//│ fun i: Int -> Int & Bool -> Bool +//│ fun i: nothing -> nothing + +// * Merge intersected functions with same codomain +fun j: (Int & Bool) -> (Int & Bool) +fun j = i +//│ fun j: nothing -> nothing +//│ fun j: nothing -> nothing + +:e // * Note: currently it doesn't work when done in a single step +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` +//│ fun j: Int -> Int & Bool -> Bool +//│ fun j: nothing -> nothing + + +// * Or widen even further with both an intersection and a union, into this +fun g: (Int & Bool) -> (Int | Bool) +fun g = f +//│ fun g: Int -> Int & Bool -> Bool +//│ fun g: nothing -> (Int | false | true) + + +// * Note: we currently approximate uses of overloaded function types! +// * With match-type-based constraint solving, we could return Int here + +f(0) +//│ Int | false | true +//│ res +//│ = 0 + +// f(0) : case 0 of { Int => Int; Bool => Bool } == Int + + +x => f(x) +//│ (Int | false | true) -> (Int | false | true) +//│ res +//│ = [Function: res] + +// : forall 'a: 'a -> case 'a of { Int => Int; Bool => Bool } where 'a <: Int | Bool + + +f(if true then 0 else false) +//│ Int | false | true +//│ res +//│ = 0 + +// * With match-type-based constraint solving, we could *also* return Int here + +:e // TODO implement this syntax +: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! +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╔══[ERROR] identifier not found: refined +//│ ║ l.133: f(refined if true then 0 else false) // this one can be precise again! +//│ ╙── ^^^^^^^ +//│ Int | false | true +//│ Code generation encountered an error: +//│ unresolved symbol refined + + + +// * Notes on constraint solving + + +// * Easy: + +// ?a -> ?b <: (Int -> Int) & (Bool -> Bool) +// to: +// ?a -> ?b <: (Int -> Int) AND ?a -> ?b <: (Bool -> Bool) + +// * Hard; but can solve with match types: + +// (Int -> Int) & (Bool -> Bool) <: ?a -> ?b +// to: +// ?a <: Int | Bool AND (case ?a of { Int => Int; Bool => Bool }) <: ?b + +// We can still widen if needed; consider: +// ?a := Int | Bool +// then: +// (case (Int | Bool) of { Int => Int; Bool => Bool }) <: ?b +// to: +// Int <: ?b AND Bool <: ?b + +// An simple match-type constraint example: +// (case ?a of { Int => Int; Bool => Bool }) <: Int +// to: +// ?a <: Int + +// A more complicated match-type constraint example: +// (case ?a of { Int => ?b; Bool => ?c }) <: T +// to: +// ?b <: (case ?a of { Int => T; Bool => Top }) AND ?c <: (case ?a of { Int => Top; Bool => T }) + + + +class List[A] +//│ class List[A] { +//│ constructor() +//│ } + +// * Note: match type `T match { case List[t] => ... t ... }` could be encoded as: + +type M = (forall 't: List['t] => 't) +//│ type M = forall 't. List['t] -> 't + +type T = List[Int] +//│ type T = List[Int] + +:e // TODO application types +type Res = M(T) +//│ ╔══[ERROR] Wrong number of type arguments – expected 0, found 1 +//│ ║ l.194: type Res = M(T) +//│ ╙── ^^^^ +//│ type Res = M + + + +let f = x => [x, x] +//│ let f: forall 'a. 'a -> ['a, 'a] +//│ f +//│ = [Function: f1] + +[f(1), f(true)] +//│ [[1, 1], [true, true]] +//│ res +//│ = [ [ 1, 1 ], [ true, true ] ] + + + +:e // TODO support +fun f: Int -> Int +fun f: Bool -> Bool +fun f = id +//│ ╔══[ERROR] A type signature for 'f' was already given +//│ ║ l.216: fun f: Bool -> Bool +//│ ╙── ^^^^^^^^^^^^^^^^^^^ +//│ fun f: forall 'a. 'a -> 'a +//│ fun f: Int -> Int + +:e // TODO support +f: (Int -> Int) & (Bool -> Bool) +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.225: f: (Int -> Int) & (Bool -> Bool) +//│ ║ ^ +//│ ╟── type `Bool` is not an instance of type `Int` +//│ ║ l.225: f: (Int -> Int) & (Bool -> Bool) +//│ ║ ^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.215: fun f: Int -> Int +//│ ╙── ^^^ +//│ Int -> Int & Bool -> Bool +//│ res +//│ = [Function: id] + +// t: S t: T +// ------------- +// t: S & T + + + +// * Weird MLstruct rule (only sound when we don't have FCP): +// forall 'a: 'a -> 'a <: (Int -> Int) & (Bool -> Bool) == (Int | Bool) -> (Int & Bool) +// ~{ a: Int } <: Str -> Str + +// * Notice: in positive position, this is equivalent to Bottom +fun x: ~{ a: Int } +//│ fun x: nothing + + +class A() +class B() +//│ class A() +//│ class B() + +A() : ~B +//│ ~B +//│ res +//│ = A {} + +// A <: ~B +// <=> +// A <: ~B | Bot +// <=> +// A & B <: Bot + +fun x: A & B +//│ fun x: nothing + + +fun test(x) = if x is + A then 0 + _ then x +//│ fun test: forall 'a. (A | Object & 'a & ~#A) -> (0 | 'a) + +test(B()) +//│ 0 | B +//│ res +//│ = B {} + +test(A()) +//│ 0 +//│ res +//│ = 0 + +// A <: A | Object & 'a & ~A +// A & ~A <: Object & 'a & ~A +// Bot <: Object & 'a & ~A + + +:e // TODO implement this syntax +:w +fun test(x) = refined if x is + A then 0 + B then 1 +//│ ╔══[WARNING] Paren-less applications should use the 'of' keyword +//│ ║ l.296: fun test(x) = refined if x is +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.297: A then 0 +//│ ║ ^^^^^^^^^^ +//│ ║ l.298: B then 1 +//│ ╙── ^^^^^^^^^^ +//│ ╔══[ERROR] identifier not found: refined +//│ ║ l.296: fun test(x) = refined if x is +//│ ╙── ^^^^^^^ +//│ fun test: (A | B) -> error +//│ Code generation encountered an error: +//│ unresolved symbol refined + +// forall 'a: 'a -> (case 'a of A -> 0, B & ~A -> 1) + + + diff --git a/shared/src/test/diff/nu/Huawei1.mls b/shared/src/test/diff/nu/Huawei1.mls new file mode 100644 index 0000000000..7b939fe6a1 --- /dev/null +++ b/shared/src/test/diff/nu/Huawei1.mls @@ -0,0 +1,62 @@ +:NewDefs + + +class C[out A](x: A) { + fun foo = x +} +//│ class C[A](x: A) { +//│ fun foo: A +//│ } + +let c = C(123) +//│ let c: C[123] +//│ c +//│ = C {} + +class B +//│ class B { +//│ constructor() +//│ } + +fun bar(c) = if c is + C(y) then y + B then 0 +//│ fun bar: forall 'a. (B | C['a]) -> (0 | 'a) + +bar(c) +//│ 0 | 123 +//│ res +//│ = 123 + +fun bar(c) = if c is + C(y) then y + 1 + B then 0 + else 1 +//│ fun bar: (C[Int] | Object & ~#C) -> Int + +bar(c) +//│ Int +//│ res +//│ = 124 + +:e +bar(C(true)) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.43: bar(C(true)) +//│ ║ ^^^^^^^^^^^^ +//│ ╟── reference of type `true` is not an instance of `Int` +//│ ║ l.43: bar(C(true)) +//│ ║ ^^^^ +//│ ╟── Note: constraint arises from reference: +//│ ║ l.32: C(y) then y + 1 +//│ ║ ^ +//│ ╟── from field selection: +//│ ║ l.4: class C[out A](x: A) { +//│ ║ ^ +//│ ╟── Note: type parameter A is defined at: +//│ ║ l.4: class C[out A](x: A) { +//│ ╙── ^ +//│ Int | error +//│ res +//│ = 2 + diff --git a/shared/src/test/diff/nu/ImplicitMethodPolym.mls b/shared/src/test/diff/nu/ImplicitMethodPolym.mls new file mode 100644 index 0000000000..600628d3ca --- /dev/null +++ b/shared/src/test/diff/nu/ImplicitMethodPolym.mls @@ -0,0 +1,261 @@ +:NewDefs + + +module M { + fun id1(x) = x +} +//│ module M { +//│ fun id1: forall 'a. 'a -> 'a +//│ } + +M.id1 +//│ forall 'a. 'a -> 'a +//│ res +//│ = [Function: id1] + +M.id1(true) +//│ true +//│ res +//│ = true + +M.id1(0) +//│ 0 +//│ res +//│ = 0 + + +module M { + fun id1(x) = x + let _ = id1(0) +} +//│ module M { +//│ let _: 0 +//│ fun id1: forall 'a. 'a -> 'a +//│ } + +M.id1 +//│ forall 'a. 'a -> 'a +//│ res +//│ = [Function: id1] + +// :d +mixin Mx { + fun id1(x) = x +} +//│ mixin Mx() { +//│ fun id1: 'a -> 'a +//│ } + +// * Note: the order of freshening matters! +// * if TV freshened transitively from traversing the `this` refinement at a lower ctx level, +// * as in the case below, the result is different. +module M extends Mx { + val r = this.id1(0) +} +//│ module M { +//│ fun id1: forall 'a. ('b & 'a) -> (0 | 'a) +//│ val r: 0 | 'b +//│ } + +mixin Mx { + fun id1(x) = this.id2(x) +} +//│ mixin Mx() { +//│ this: {id2: 'a -> 'b} +//│ fun id1: 'a -> 'b +//│ } + +:e +module M extends Mx { + this.id1(0) +} +//│ ╔══[ERROR] Type `#M & {id1: ?a -> ?b}` does not contain member `id2` +//│ ║ l.61: fun id1(x) = this.id2(x) +//│ ╙── ^^^^ +//│ module M { +//│ fun id1: anything -> error +//│ } +//│ Runtime error: +//│ TypeError: qualifier1.id2 is not a function + +:e +module M extends Mx { + fun id2(x) = [x, x] + this.id1(0) +} +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.61: fun id1(x) = this.id2(x) +//│ ╙── ^^^^ +//│ module M { +//│ fun id1: anything -> error +//│ fun id2: forall 'a. 'a -> ['a, 'a] +//│ } + +// * Notice that `id1` is no longer generalized! +module M extends Mx { + fun id2: 'a => ['a, 'a] + fun id2(x) = [x, x] + let _ = this.id1(0) +} +//│ module M { +//│ let _: [0 | 'a, 0 | 'a] +//│ fun id1: 'a -> [0 | 'a, 0 | 'a] +//│ fun id2: forall 'a0. 'a0 -> ['a0, 'a0] +//│ } + + + +:e // FIXME +class C { + virtual fun id1(x) = x + fun f = [this.id1(true), this.id1(0)] + fun id2(x) = x +} +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.111: fun f = [this.id1(true), this.id1(0)] +//│ ╙── ^^^^ +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.111: fun f = [this.id1(true), this.id1(0)] +//│ ╙── ^^^^ +//│ class C { +//│ constructor() +//│ fun f: [error, error] +//│ fun id1: forall 'a. 'a -> 'a +//│ fun id2: forall 'b. 'b -> 'b +//│ } + +// TODO support +// :d +module M extends C { + this.id2(true) +} +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.130: this.id2(true) +//│ ╙── ^^^^ +//│ ╔══[ERROR] Cannot access `this` during object initialization +//│ ║ l.130: this.id2(true) +//│ ╙── ^^^^ +//│ module M extends C { +//│ fun f: [error, error] +//│ fun id1: forall 'a. 'a -> 'a +//│ fun id2: forall 'b. 'b -> 'b +//│ } + +// TODO support +module M extends C { + fun g = (this.id2(true), this.id2(0)) +} +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.146: fun g = (this.id2(true), this.id2(0)) +//│ ╙── ^^^^ +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.146: fun g = (this.id2(true), this.id2(0)) +//│ ╙── ^^^^ +//│ module M extends C { +//│ fun f: [error, error] +//│ fun g: error +//│ fun id1: forall 'a. 'a -> 'a +//│ fun id2: forall 'b. 'b -> 'b +//│ } + +M.id1 +//│ forall 'a. 'a -> 'a +//│ res +//│ = [Function: id1] + +M.id2 +//│ forall 'a. 'a -> 'a +//│ res +//│ = [Function: id2] + +M.f +//│ [error, error] +//│ res +//│ = [ true, 0 ] + +M.g +//│ error +//│ res +//│ = 0 + + +:e +module M extends C { + fun id1 = succ +} +//│ ╔══[ERROR] Type mismatch in definition of method id1: +//│ ║ l.184: fun id1 = succ +//│ ║ ^^^^^^^^^^ +//│ ╙── variable of type `?a` is not an instance of type `Int` +//│ ╔══[ERROR] Type mismatch in definition of method id1: +//│ ║ l.184: fun id1 = succ +//│ ║ ^^^^^^^^^^ +//│ ╟── expression of type `Int` does not match type `?a` +//│ ╟── Note: constraint arises from reference: +//│ ║ l.110: virtual fun id1(x) = x +//│ ╙── ^ +//│ module M extends C { +//│ fun f: [error, error] +//│ fun id1: Int -> Int +//│ fun id2: forall 'a. 'a -> 'a +//│ } + +M.id1 +//│ Int -> Int +//│ res +//│ = [Function: succ] + + +(M : C).id1(false) +//│ false +//│ res +//│ = 1 + + +// FIXME? parsing/semantics of this, currently treated as a named tuple... +(M: C) +//│ ╔══[PARSE ERROR] Illegal position for field specification +//│ ║ l.216: (M: C) +//│ ╙── ^^^^ +//│ ╔══[ERROR] Construction of unparameterized class C should use the `new` keyword +//│ ║ l.216: (M: C) +//│ ╙── ^ +//│ () -> C +//│ res +//│ = [class C] + + + +module None +//│ module None + +// TODO parse +// TODO reject; polymophism should be blocked by mutation from distributing/refreshing +:pe +:e +module M { + mut val m = None + fun oops(x) = m := x +} +//│ ╔══[PARSE ERROR] Unexpected 'mut' keyword in expression position +//│ ║ l.237: mut val m = None +//│ ╙── ^^^ +//│ ╔══[PARSE ERROR] Unexpected 'val' keyword in expression position +//│ ║ l.237: mut val m = None +//│ ╙── ^^^ +//│ ╔══[ERROR] identifier not found: := +//│ ║ l.238: fun oops(x) = m := x +//│ ╙── ^^ +//│ ╔══[ERROR] identifier not found: m +//│ ║ l.238: fun oops(x) = m := x +//│ ╙── ^ +//│ ╔══[ERROR] Unexpected equation in this position +//│ ║ l.237: mut val m = None +//│ ╙── ^^^^^^^^ +//│ module M { +//│ fun oops: anything -> error +//│ } +//│ Code generation encountered an error: +//│ unresolved symbol := + + diff --git a/shared/src/test/diff/nu/InferredInheritanceTypeArgs.mls b/shared/src/test/diff/nu/InferredInheritanceTypeArgs.mls new file mode 100644 index 0000000000..0f52e6be9c --- /dev/null +++ b/shared/src/test/diff/nu/InferredInheritanceTypeArgs.mls @@ -0,0 +1,252 @@ +:NewDefs + + +mixin Test[A] { + fun bar: [A, A] + fun bar = [this.a, this.a] +} +//│ mixin Test[A]() { +//│ this: {a: A} +//│ fun bar: [A, A] +//│ } + +class A(val a: Int) extends Test +//│ class A(a: Int) { +//│ fun bar: [Int, Int] +//│ } + +:e +class A(a: Int) extends Test +//│ ╔══[ERROR] Parameter 'a' cannot tbe accessed as a field +//│ ║ l.6: fun bar = [this.a, this.a] +//│ ╙── ^^ +//│ ╔══[ERROR] Parameter 'a' cannot tbe accessed as a field +//│ ║ l.6: fun bar = [this.a, this.a] +//│ ╙── ^^ +//│ class A(a: Int) { +//│ fun bar: [Int, Int] +//│ } + +mixin Test2[S, T] { + fun x: [S, T] + fun x = [this.s, this.t] + fun fb: S => [S, S] + fun fb(h: S) = [this.s, h] +} +//│ mixin Test2[S, T]() { +//│ this: {s: S, t: T} +//│ fun fb: S -> [S, S] +//│ fun x: [S, T] +//│ } + +class A1[B](val s: Bool, val t: B) extends Test2[Bool, B] +//│ class A1[B](s: Bool, t: B) { +//│ fun fb: ('S & Bool) -> [Bool | 'S, Bool | 'S] +//│ fun x: [Bool | 'S, B] +//│ } + +// * TODO: Investigate type of fb +class A2[A](val s: A, val t: Int) extends Test2 +//│ class A2[A](s: A, t: Int) { +//│ fun fb: 'S -> [A | 'S, A | 'S] +//│ fun x: [A | 'S, Int] +//│ } + +// * TODO: Investigate type of fb +class A3(val s: Int, val t: Bool) extends Test2 +//│ class A3(s: Int, t: Bool) { +//│ fun fb: 'S -> [Int | 'S, Int | 'S] +//│ fun x: [Int | 'S, Bool] +//│ } + +class P(val p: Int) { + virtual fun foo(x) = x + p +} +//│ class P(p: Int) { +//│ fun foo: Int -> Int +//│ } + +:e // TODO improve type checking +class C1(a: Int) extends P(a) { fun bar = this.foo(0) } +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.70: class C1(a: Int) extends P(a) { fun bar = this.foo(0) } +//│ ╙── ^^^^ +//│ class C1(a: Int) extends P { +//│ fun bar: error +//│ fun foo: Int -> Int +//│ } + +:e // TODO improve type checking +class C2(a: Int, b: Int) extends P(a + b) { + fun foo(x) = x * this.p + a * b +} +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.81: fun foo(x) = x * this.p + a * b +//│ ╙── ^^ +//│ class C2(a: Int, b: Int) extends P { +//│ fun foo: Int -> Int +//│ } + +let c2 = C2(1, 2) +//│ let c2: C2 +//│ c2 +//│ = C2 {} + +c2.foo(2) +//│ Int +//│ res +//│ = 8 + +c2.p +//│ Int +//│ res +//│ = 3 + + +class Test[A](val x: A) +//│ class Test[A](x: A) + +class A(a: Int) extends Test(a) +//│ class A(a: Int) extends Test + +let a1 = A(1) +//│ let a1: A +//│ a1 +//│ = A {} + +a1: Test['x] +//│ Test['x] +//│ where +//│ 'x :> Int +//│ res +//│ = A {} + +a1.x +//│ Int +//│ res +//│ = 1 + + + + +trait Foo[A] { fun foo(x: A): A } +//│ trait Foo[A] { +//│ fun foo: (x: A) -> A +//│ } + +// * This is pretty funky but it seems sound for now... +// * Inherited Foo's type arg is left "unspecified", and since it is not constrained, +// * it can be instantiated to any type by downstream callers of the methods! +module B extends Foo { fun foo(x) = x } +//│ module B extends Foo { +//│ fun foo: forall 'a. 'a -> 'a +//│ } + +B : Foo['X] +//│ Foo['X] +//│ res +//│ = B { class: [class B extends Object] } + +B.foo +//│ forall 'a. 'a -> 'a +//│ res +//│ = [Function: foo] + +B.foo(1) +//│ 1 +//│ res +//│ = 1 + +module B extends Foo { fun foo(x) = x + 1 } +//│ module B extends Foo { +//│ fun foo: Int -> Int +//│ } + +B : Foo['X] +//│ Foo[Int] +//│ res +//│ = B { class: [class B extends Object] } + +B.foo +//│ Int -> Int +//│ res +//│ = [Function: foo] + + +// * TODO: when +:pe +trait Foo[type A] { fun foo(x: A): A } +//│ ╔══[PARSE ERROR] Unexpected 'type' keyword here +//│ ║ l.178: trait Foo[type A] { fun foo(x: A): A } +//│ ╙── ^^^^ +//│ trait Foo { +//│ fun foo: (x: A) -> A +//│ } + + +trait Foo[A] { fun a: A; fun foo(x: A): A } +//│ trait Foo[A] { +//│ fun a: A +//│ fun foo: (x: A) -> A +//│ } + +class Bar[out B](a: B) extends Foo { fun foo(x) = x } +//│ class Bar[B](a: B) extends Foo { +//│ fun foo: forall 'a. 'a -> 'a +//│ } + +let b = Bar(123) +//│ let b: Bar[123] +//│ b +//│ = Bar {} + +b : Foo['X] +//│ Foo['X] +//│ where +//│ 'X :> 123 +//│ res +//│ = Bar {} + +b.foo +//│ forall 'a. 'a -> 'a +//│ res +//│ = [Function: foo] + + +// * Note the shadowed type variable `A` in `foo` +trait Foo[A] { fun foo[A](x: A): A } +//│ trait Foo[A] { +//│ fun foo: forall 'A. (x: 'A) -> 'A +//│ } + +class B extends Foo { fun foo(x) = x } +//│ class B extends Foo { +//│ constructor() +//│ fun foo: forall 'a. 'a -> 'a +//│ } + +:e +class B extends Foo { fun foo(x) = x + 1 } +//│ ╔══[ERROR] Type mismatch in definition of method foo: +//│ ║ l.229: class B extends Foo { fun foo(x) = x + 1 } +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── type `A` is not an instance of type `Int` +//│ ║ l.217: trait Foo[A] { fun foo[A](x: A): A } +//│ ║ ^ +//│ ╟── Note: constraint arises from reference: +//│ ║ l.229: class B extends Foo { fun foo(x) = x + 1 } +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in definition of method foo: +//│ ║ l.229: class B extends Foo { fun foo(x) = x + 1 } +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── operator application of type `Int` does not match type `A` +//│ ║ l.229: class B extends Foo { fun foo(x) = x + 1 } +//│ ║ ^^^^^ +//│ ╟── Note: constraint arises from method type parameter: +//│ ║ l.217: trait Foo[A] { fun foo[A](x: A): A } +//│ ╙── ^ +//│ class B extends Foo { +//│ constructor() +//│ fun foo: Int -> Int +//│ } + diff --git a/shared/src/test/diff/nu/InheritanceLevelMismatches.mls b/shared/src/test/diff/nu/InheritanceLevelMismatches.mls new file mode 100644 index 0000000000..f637d0991a --- /dev/null +++ b/shared/src/test/diff/nu/InheritanceLevelMismatches.mls @@ -0,0 +1,41 @@ +:NewDefs + +:NoJS // TODO + + +trait T1 { fun x: 0 | 1 } +//│ trait T1 { +//│ fun x: 0 | 1 +//│ } + +module Foo { + trait T2 { fun x: 1 | 2 } + class C extends T1, T2 { fun x = 1 } +} +//│ module Foo { +//│ class C extends T1, T2 { +//│ constructor() +//│ fun x: 1 +//│ } +//│ trait T2 { +//│ fun x: 1 | 2 +//│ } +//│ } + + +mixin Foo { fun f = this.x } +//│ mixin Foo() { +//│ this: {x: 'x} +//│ fun f: 'x +//│ } + +module Bar { + class C(val x: Int) extends Foo +} +//│ module Bar { +//│ class C(x: Int) { +//│ fun f: Int +//│ } +//│ } + + diff --git a/shared/src/test/diff/nu/IntLit.mls b/shared/src/test/diff/nu/IntLit.mls new file mode 100644 index 0000000000..b140609aaf --- /dev/null +++ b/shared/src/test/diff/nu/IntLit.mls @@ -0,0 +1,139 @@ +:NewDefs + +// Decimal literals. +[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +[-0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10] +//│ [0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10] +//│ res +//│ = [ +//│ 0, 1, 2, 3, 4, +//│ 5, 6, 7, 8, 9, +//│ 10 +//│ ] +//│ res +//│ = [ +//│ 0, -1, -2, -3, -4, +//│ -5, -6, -7, -8, -9, +//│ -10 +//│ ] + +// Hexadecimal literals. +[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A] +[-0x00, -0x01, -0x02, -0x03, -0x04, -0x05, -0x06, -0x07, -0x08, -0x09, -0x0A] +//│ [0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10] +//│ res +//│ = [ +//│ 0, 1, 2, 3, 4, +//│ 5, 6, 7, 8, 9, +//│ 10 +//│ ] +//│ res +//│ = [ +//│ 0, -1, -2, -3, -4, +//│ -5, -6, -7, -8, -9, +//│ -10 +//│ ] + +// Octal literals. +[0o00, 0o01, 0o02, 0o03, 0o04, 0o05, 0o06, 0o07, 0o10, 0o11, 0o12] +[-0o00, -0o01, -0o02, -0o03, -0o04, -0o05, -0o06, -0o07, -0o10, -0o11, -0o12] +//│ [0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10] +//│ res +//│ = [ +//│ 0, 1, 2, 3, 4, +//│ 5, 6, 7, 8, 9, +//│ 10 +//│ ] +//│ res +//│ = [ +//│ 0, -1, -2, -3, -4, +//│ -5, -6, -7, -8, -9, +//│ -10 +//│ ] + +// Binary literals. +[0b0000, 0b0001, 0b0010, 0b0011, 0b0100, 0b0101, 0b0110, 0b0111, 0b1000, 0b1001, 0b1010] +[-0b0, -0b1, -0b10, -0b11, -0b100, -0b101, -0b110, -0b111, -0b1000, -0b1001, -0b1010] +//│ [0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10] +//│ res +//│ = [ +//│ 0, 1, 2, 3, 4, +//│ 5, 6, 7, 8, 9, +//│ 10 +//│ ] +//│ res +//│ = [ +//│ 0, -1, -2, -3, -4, +//│ -5, -6, -7, -8, -9, +//│ -10 +//│ ] + +:pe +0b +//│ ╔══[LEXICAL ERROR] Expect at least one binary digit +//│ ║ l.72: 0b +//│ ╙── ^^ +//│ 0 +//│ res +//│ = 0 + +:pe +0x +//│ ╔══[LEXICAL ERROR] Expect at least one hexadecimal digit +//│ ║ l.81: 0x +//│ ╙── ^^ +//│ 0 +//│ res +//│ = 0 + +:pe +0o +//│ ╔══[LEXICAL ERROR] Expect at least one octal digit +//│ ║ l.90: 0o +//│ ╙── ^^ +//│ 0 +//│ res +//│ = 0 + +// Underscores as separators in numeric literals. +1_000_000 +0xDEAD_CAFE +0b0010_0100_0011_0100 +//│ 9268 +//│ res +//│ = 1000000 +//│ res +//│ = 3735931646 +//│ res +//│ = 9268 + +// Trailing underscores are not allowed. +:w +0b00000000010_ +0xCAFE_____ +//│ ╔══[WARNING] Trailing separator is not allowed +//│ ║ l.112: 0b00000000010_ +//│ ╙── ^ +//│ ╔══[WARNING] Trailing separator is not allowed +//│ ║ l.113: 0xCAFE_____ +//│ ╙── ^ +//│ 51966 +//│ res +//│ = 2 +//│ res +//│ = 51966 + +// Extra leading zeros are discarded. +0123 +023456 +00 +0 +//│ 0 +//│ res +//│ = 123 +//│ res +//│ = 23456 +//│ res +//│ = 0 +//│ res +//│ = 0 diff --git a/shared/src/test/diff/nu/InterfaceGeneric.mls b/shared/src/test/diff/nu/InterfaceGeneric.mls new file mode 100644 index 0000000000..a8be589d52 --- /dev/null +++ b/shared/src/test/diff/nu/InterfaceGeneric.mls @@ -0,0 +1,70 @@ +:NewDefs +:NoJS + +trait Into[T] { + fun Into: T +} +//│ trait Into[T] { +//│ fun Into: T +//│ } + +trait Nat extends Into[Int] +//│ trait Nat extends Into { +//│ fun Into: 'T +//│ } +//│ where +//│ 'T := Int + +trait Product[A, B] extends Into[A] { + let pair: [A, B] +} +//│ trait Product[A, B] extends Into { +//│ fun Into: 'T +//│ let pair: [A, B] +//│ } +//│ where +//│ 'T := A + +class TwoInts(val pair: [Int, Int]) extends Product[Int, Int] { + fun Into = pair.0 + pair.1 +} +//│ class TwoInts(pair: [Int, Int]) extends Into, Product { +//│ fun Into: Int +//│ } + +let i2 = TwoInts([1, 2]) +//│ let i2: TwoInts + +i2: Product[Int, Int] +//│ Product[Int, Int] + +i2: Into[Int] +//│ Into[Int] + +i2.pair +//│ [Int, Int] + +i2.Into +//│ Int + +let p1: Product[Int, Int] +//│ let p1: Product[Int, Int] + +:e +p1: Product[Bool, Int] +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.54: p1: Product[Bool, Int] +//│ ║ ^^ +//│ ╙── expression of type `Int` does not match type `Bool` +//│ Product[Bool, Int] + +p1: Into[Int] +//│ Into[Int] + +:e +p1: Into[Bool] +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.65: p1: Into[Bool] +//│ ║ ^^ +//│ ╙── expression of type `Int` does not match type `Bool` +//│ Into[Bool] diff --git a/shared/src/test/diff/nu/InterfaceMono.mls b/shared/src/test/diff/nu/InterfaceMono.mls new file mode 100644 index 0000000000..c19e5930c4 --- /dev/null +++ b/shared/src/test/diff/nu/InterfaceMono.mls @@ -0,0 +1,466 @@ +:NewDefs + + +trait Showable { + fun toString: Str +} +//│ trait Showable { +//│ fun toString: Str +//│ } + +:e +trait What0 extends woooo +//│ ╔══[ERROR] Could not find definition `woooo` +//│ ║ l.12: trait What0 extends woooo +//│ ╙── ^^^^^ +//│ trait What0 + +class PoInt(x: Int, y: Int) extends Showable { + fun mlen = x + y + fun toString = "I'm a poInt" +} +//│ class PoInt(x: Int, y: Int) extends Showable { +//│ fun mlen: Int +//│ fun toString: "I'm a poInt" +//│ } + +class What1(toString: Str) extends Showable +//│ 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") +//│ ╙── ^^^^^^^^^^^ +//│ trait NoShow extends Showable, What1 + +:e +class ErrC1 extends Showable +class ErrC2 extends Showable { + fun toString = 114 +} +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 +//│ ║ ^^^^^ +//│ ╟── Declared here: +//│ ║ l.5: fun toString: Str +//│ ╙── ^^^^^^^^^^^^^^^^^ +//│ ╔══[ERROR] Type mismatch in definition of method toString: +//│ ║ l.40: fun toString = 114 +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── integer literal of type `114` is not an instance of type `Str` +//│ ║ l.40: fun toString = 114 +//│ ║ ^^^ +//│ ╟── but it flows into definition of method toString with expected type `Str` +//│ ║ l.40: fun toString = 114 +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.5: fun toString: Str +//│ ║ ^^^ +//│ ╟── from signature of member `toString`: +//│ ║ l.5: fun toString: Str +//│ ╙── ^^^^^^^^^^^^^ +//│ ╔══[ERROR] Type mismatch in function type: +//│ ║ 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: +//│ ║ l.5: fun toString: Str +//│ ║ ^^^ +//│ ╟── from signature of member `toString`: +//│ ║ l.5: fun toString: Str +//│ ╙── ^^^^^^^^^^^^^ +//│ class ErrC1 extends Showable { +//│ constructor() +//│ fun toString: Str +//│ } +//│ class ErrC2 extends Showable { +//│ constructor() +//│ fun toString: 114 +//│ } +//│ class ErrC3(toString: Str -> Str) extends Showable + +trait Stadt { + let name: Str +} +//│ trait Stadt { +//│ let name: Str +//│ } + +trait RefinedStadt extends Stadt { + let size: Int + fun foo: Bool -> Int +} +//│ trait RefinedStadt extends Stadt { +//│ fun foo: Bool -> Int +//│ let name: Str +//│ let size: Int +//│ } + +trait SizedStadt extends RefinedStadt { + let size: 1 | 2 | 3 + fun bar: Int -> Int +} +//│ trait SizedStadt extends RefinedStadt, Stadt { +//│ fun bar: Int -> Int +//│ fun foo: Bool -> Int +//│ let name: Str +//│ let size: 1 | 2 | 3 +//│ } + +class Goodstatt(size: 1 | 2) extends RefinedStadt { + let name = "good" + fun bar(x) = x + fun foo(t) = if t && true then this.size else 0 +} +//│ class Goodstatt(size: 1 | 2) extends RefinedStadt, Stadt { +//│ fun bar: forall 'a. 'a -> 'a +//│ fun foo: Bool -> (0 | 1 | 2) +//│ let name: "good" +//│ } + +:e +class Errcity(size: Int) extends SizedStadt { + fun bar = "hahaha" +} +//│ ╔══[ERROR] Type mismatch in definition of method bar: +//│ ║ l.125: fun bar = "hahaha" +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── string literal of type `"hahaha"` is not a function +//│ ║ l.125: fun bar = "hahaha" +//│ ║ ^^^^^^^^ +//│ ╟── but it flows into definition of method bar with expected type `Int -> Int` +//│ ║ l.125: fun bar = "hahaha" +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from function type: +//│ ║ l.103: fun bar: Int -> Int +//│ ║ ^^^^^^^^^^ +//│ ╟── from signature of member `bar`: +//│ ║ l.103: fun bar: Int -> Int +//│ ╙── ^^^^^^^^^^^^^^^ +//│ ╔══[ERROR] Type mismatch in type reference: +//│ ║ l.124: 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 +//│ ║ ^^^^^^^^^ +//│ ╟── from signature of member `size`: +//│ ║ l.102: 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 { +//│ ║ ^^^^^^^ +//│ ╟── Declared here: +//│ ║ l.85: 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 { +//│ ║ ^^^^^^^ +//│ ╟── Declared here: +//│ ║ l.93: fun foo: Bool -> Int +//│ ╙── ^^^^^^^^^^^^^^^^^^^^ +//│ class Errcity(size: Int) extends RefinedStadt, SizedStadt, Stadt { +//│ fun bar: "hahaha" +//│ fun foo: Bool -> Int +//│ let name: Str +//│ } + +module Omg extends Stadt { + fun name = "omg!!!" + fun cool(x) = x + x +} +//│ module Omg extends Stadt { +//│ fun cool: Int -> Int +//│ fun name: "omg!!!" +//│ } + +mixin More { + fun more(x) = x == 1 + fun size = 1 + fun bar(x) = x +} +//│ mixin More() { +//│ fun bar: 'a -> 'a +//│ fun more: Num -> Bool +//│ fun size: 1 +//│ } + +mixin Fooo { + fun foo(x) = 0 +} +//│ mixin Fooo() { +//│ fun foo: anything -> 0 +//│ } + +class Grassberg(name: "grass" | "GRASS") extends More, SizedStadt, Fooo +//│ class Grassberg(name: "GRASS" | "grass") extends RefinedStadt, SizedStadt, Stadt { +//│ fun bar: forall 'a. 'a -> 'a +//│ fun foo: anything -> 0 +//│ fun more: Num -> Bool +//│ fun size: 1 +//│ } + +:e +class Dirtberg extends More, SizedStadt, Fooo { + let name = "dirt" + fun size = 4 // this should not check +} +//│ ╔══[ERROR] Type mismatch in definition of method size: +//│ ║ l.208: 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 +//│ ║ ^ +//│ ╟── but it flows into definition of method size with expected type `1 | 2 | 3` +//│ ║ l.208: fun size = 4 // this should not check +//│ ║ ^^^^^^^^ +//│ ╟── Note: constraint arises from union type: +//│ ║ l.102: let size: 1 | 2 | 3 +//│ ║ ^^^^^^^^^ +//│ ╟── from signature of member `size`: +//│ ║ l.102: let size: 1 | 2 | 3 +//│ ╙── ^^^^^^^^^^^^^^^ +//│ class Dirtberg extends RefinedStadt, SizedStadt, Stadt { +//│ constructor() +//│ fun bar: forall 'a. 'a -> 'a +//│ fun foo: anything -> 0 +//│ fun more: Num -> Bool +//│ let name: "dirt" +//│ fun size: 4 +//│ } + +class Iceburg(name: Str) extends RefinedStadt, More, Fooo +//│ class Iceburg(name: Str) extends RefinedStadt, Stadt { +//│ fun bar: forall 'a. 'a -> 'a +//│ fun foo: anything -> 0 +//│ fun more: Num -> Bool +//│ fun size: 1 +//│ } + +class A { virtual fun x: Int = 1 } +//│ class A { +//│ constructor() +//│ fun x: Int +//│ } + +: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" } +//│ ║ ^^^^^^^ +//│ ╟── string literal of type `"A"` is not an instance of type `Int` +//│ ║ l.249: 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" } +//│ ║ ^^^^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.242: class A { virtual fun x: Int = 1 } +//│ ║ ^^^ +//│ ╟── from definition of method x: +//│ ║ l.242: class A { virtual fun x: Int = 1 } +//│ ╙── ^^^^^^^^^^ +//│ class B extends A { +//│ constructor() +//│ 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 } +//│ ╙── ^^ +//│ class C1[A] { +//│ constructor() +//│ fun a: A +//│ } + +class C2 extends C1[Int] { fun a = 1 } +//│ class C2 extends C1 { +//│ constructor() +//│ fun a: 1 +//│ } + +// trait MyTrait[A] { type MyTrait#A = A; fun a: A = a } +// freshen/subst +// trait MyTrait[Int] { type MyTrait#A = Int; fun a: Int = a } + +trait MyTrait[A] { fun a: A } +//│ trait MyTrait[A] { +//│ fun a: A +//│ } + +// :ns +// :d +// :ds +class C extends MyTrait[Int] { fun a = 1 } +//│ class C extends MyTrait { +//│ constructor() +//│ 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 } +//│ ║ ^^^^^^^^^ +//│ ╟── reference of type `false` is not an instance of `Int` +//│ ║ l.305: 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 } +//│ ║ ^^^^^^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.305: class C extends MyTrait[Int] { fun a = false } +//│ ║ ^^^ +//│ ╟── from signature of member `a`: +//│ ║ l.290: trait MyTrait[A] { fun a: A } +//│ ╙── ^^^^ +//│ class C extends MyTrait { +//│ constructor() +//│ fun a: false +//│ } + + +trait T1 { + let foo : 1 | 2 | 3 + fun bar : Str | Bool +} +trait T2 { + let foo : 2 | 3 | 4 + let bar : Int | Bool +} +//│ trait T1 { +//│ fun bar: Str | false | true +//│ let foo: 1 | 2 | 3 +//│ } +//│ trait T2 { +//│ let bar: Int | false | true +//│ let foo: 2 | 3 | 4 +//│ } + +trait T4 extends T1, T2 { + fun foo: 2 +} +//│ trait T4 extends T1, T2 { +//│ fun bar: Bool +//│ fun foo: 2 +//│ } + +class C1(foo: 2, bar: true) extends T4 +//│ class C1(foo: 2, bar: true) extends T1, T2, T4 + +:e +class C3 extends T4{ + fun foo = 3 + fun bar = false +} +//│ ╔══[ERROR] Type mismatch in definition of method foo: +//│ ║ l.357: fun foo = 3 +//│ ║ ^^^^^^^ +//│ ╟── integer literal of type `3` does not match type `2` +//│ ║ l.357: fun foo = 3 +//│ ║ ^ +//│ ╟── but it flows into definition of method foo with expected type `2` +//│ ║ l.357: fun foo = 3 +//│ ║ ^^^^^^^ +//│ ╟── Note: constraint arises from literal type: +//│ ║ l.345: fun foo: 2 +//│ ║ ^ +//│ ╟── from signature of member `foo`: +//│ ║ l.345: fun foo: 2 +//│ ╙── ^^^^^^ +//│ class C3 extends T1, T2, T4 { +//│ constructor() +//│ fun bar: false +//│ fun foo: 3 +//│ } + +: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 +//│ ║ ^^^ +//│ ╟── type `Int` does not match type `2` +//│ ╟── Note: constraint arises from literal type: +//│ ║ l.345: fun foo: 2 +//│ ║ ^ +//│ ╟── from signature of member `foo`: +//│ ║ l.345: fun foo: 2 +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] Type mismatch in type reference: +//│ ║ 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.333: let bar : Int | Bool +//│ ║ ^^^^^^^^^^ +//│ ╟── from signature of member `bar`: +//│ ║ l.333: let bar : Int | Bool +//│ ╙── ^^^^^^^^^^^^^^^^ +//│ class C2(foo: Int, bar: Str) extends T1, T2, T4 + +:e +trait T5 extends T4 { + let foo: 4 +} +//│ ╔══[ERROR] Type mismatch in signature of member `foo`: +//│ ║ l.407: let foo: 4 +//│ ║ ^^^^^^ +//│ ╟── type `4` does not match type `2` +//│ ║ l.407: let foo: 4 +//│ ║ ^ +//│ ╟── but it flows into signature of member `foo` with expected type `2` +//│ ║ l.407: let foo: 4 +//│ ║ ^^^^^^ +//│ ╟── Note: constraint arises from literal type: +//│ ║ l.345: fun foo: 2 +//│ ║ ^ +//│ ╟── from signature of member `foo`: +//│ ║ l.345: fun foo: 2 +//│ ╙── ^^^^^^ +//│ trait T5 extends T1, T2, T4 { +//│ fun bar: Bool +//│ let foo: 4 +//│ } + +:e +trait T3 extends T1, T2 { + let foo: true +} +//│ ╔══[ERROR] Type mismatch in signature of member `foo`: +//│ ║ l.431: let foo: true +//│ ║ ^^^^^^^^^ +//│ ╟── type `true` does not match type `1 | 2 | 3` +//│ ║ l.431: let foo: true +//│ ║ ^^^^ +//│ ╟── but it flows into signature of member `foo` with expected type `1 | 2 | 3` +//│ ║ l.431: let foo: true +//│ ║ ^^^^^^^^^ +//│ ╟── Note: constraint arises from union type: +//│ ║ l.328: let foo : 1 | 2 | 3 +//│ ║ ^^^^^^^^^ +//│ ╟── from signature of member `foo`: +//│ ║ l.328: let foo : 1 | 2 | 3 +//│ ╙── ^^^^^^^^^^^^^^^ +//│ ╔══[ERROR] Type mismatch in signature of member `foo`: +//│ ║ l.431: let foo: true +//│ ║ ^^^^^^^^^ +//│ ╟── type `true` does not match type `2 | 3 | 4` +//│ ║ l.431: let foo: true +//│ ║ ^^^^ +//│ ╟── but it flows into signature of member `foo` with expected type `2 | 3 | 4` +//│ ║ l.431: let foo: true +//│ ║ ^^^^^^^^^ +//│ ╟── Note: constraint arises from union type: +//│ ║ l.332: let foo : 2 | 3 | 4 +//│ ║ ^^^^^^^^^ +//│ ╟── from signature of member `foo`: +//│ ║ l.332: let foo : 2 | 3 | 4 +//│ ╙── ^^^^^^^^^^^^^^^ +//│ trait T3 extends T1, T2 { +//│ fun bar: Bool +//│ let foo: true +//│ } diff --git a/shared/src/test/diff/nu/Interfaces.mls b/shared/src/test/diff/nu/Interfaces.mls new file mode 100644 index 0000000000..995e0eab56 --- /dev/null +++ b/shared/src/test/diff/nu/Interfaces.mls @@ -0,0 +1,1277 @@ +:NewDefs + + +trait Test { + fun foo: Int + fun bar: Bool -> Bool +} +//│ trait Test { +//│ fun bar: Bool -> Bool +//│ fun foo: Int +//│ } + +fun ts(x: Test) = x.foo +//│ fun ts: (x: Test) -> Int + + +module M extends Test { + fun foo = 0 + fun bar = not +} +//│ module M extends Test { +//│ fun bar: Bool -> Bool +//│ fun foo: 0 +//│ } + +M: Test +//│ Test +//│ res +//│ = M { class: [class M extends Object] } + +ts(M) +//│ Int +//│ res +//│ = 0 + +trait Oth extends Test { + let a : Int + fun cool : Int -> Bool +} +//│ trait Oth extends Test { +//│ let a: Int +//│ fun bar: Bool -> Bool +//│ fun cool: Int -> Bool +//│ fun foo: Int +//│ } + +let oth1: Oth +//│ let oth1: Oth +//│ oth1 +//│ = + +oth1.bar(true) +//│ Bool +//│ res +//│ = +//│ oth1 is not implemented + +oth1: Test +//│ Test +//│ res +//│ = +//│ oth1 is not implemented + +:e +M : Oth +oth1: M +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.65: M : Oth +//│ ║ ^ +//│ ╟── reference of type `M` is not an instance of type `Oth` +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.65: M : Oth +//│ ╙── ^^^ +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.66: oth1: M +//│ ║ ^^^^ +//│ ╟── type `#Oth` is not an instance of type `M` +//│ ║ l.47: let oth1: Oth +//│ ║ ^^^ +//│ ╟── but it flows into reference with expected type `M` +//│ ║ l.66: oth1: M +//│ ║ ^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.66: oth1: M +//│ ╙── ^ +//│ M +//│ res +//│ = M { class: [class M extends Object] } +//│ res +//│ = +//│ oth1 is not implemented + +trait Geo { + let v: 2 | 3 + fun get: Int | Bool + fun ter: Int +} +trait Anemo { + let v: 1 | 2 + fun get: Bool | string + fun ter: Bool +} +//│ trait Geo { +//│ fun get: Int | false | true +//│ fun ter: Int +//│ let v: 2 | 3 +//│ } +//│ trait Anemo { +//│ fun get: false | string | true +//│ fun ter: Bool +//│ let v: 1 | 2 +//│ } + +trait Mixed extends Geo, Anemo +//│ trait Mixed extends Anemo, Geo { +//│ fun get: Bool +//│ fun ter: nothing +//│ let v: 2 +//│ } + + +class C() extends Test { + fun foo: 1 = 1 + fun bar(x) = x +} +//│ class C() extends Test { +//│ fun bar: forall 'a. 'a -> 'a +//│ fun foo: 1 +//│ } + +mixin M { + fun m1 = 3 +} +//│ mixin M() { +//│ fun m1: 3 +//│ } + +class F() extends Oth, M, Mixed { + fun cool(x) = x === 1 + fun foo = 2 + fun bar(x) = x + fun get = true + fun ter = ter + let a = 3 + let v = 2 +} +//│ class F() extends Anemo, Geo, Mixed, Oth, Test { +//│ let a: 3 +//│ fun bar: forall 'a. 'a -> 'a +//│ fun cool: Eql[1] -> Bool +//│ fun foo: 2 +//│ fun get: true +//│ fun m1: 3 +//│ fun ter: nothing +//│ let v: 2 +//│ } + +let fi = F() +//│ let fi: F +//│ fi +//│ = F {} + +fi : Oth & Geo +//│ Geo & Oth +//│ res +//│ = F {} + +fi.get +//│ true +//│ res +//│ = true + +fi: Test & Anemo +//│ Anemo & Test +//│ res +//│ = F {} + +let fog: Oth & Mixed +//│ let fog: Mixed & Oth +//│ fog +//│ = + +fog: Test & Anemo +//│ Anemo & Test +//│ res +//│ = +//│ fog is not implemented + +:e +fog: F +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.190: fog: F +//│ ║ ^^^ +//│ ╟── type `Mixed & Oth` is not an instance of type `F` +//│ ║ l.178: let fog: Oth & Mixed +//│ ║ ^^^^^^^^^^^ +//│ ╟── but it flows into reference with expected type `F` +//│ ║ l.190: fog: F +//│ ║ ^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.190: fog: F +//│ ╙── ^ +//│ F +//│ res +//│ = +//│ fog is not implemented + +let c = C() +//│ let c: C +//│ c +//│ = C {} + +c: Eql +//│ Eql[C] +//│ res +//│ = C {} + +let ct: Test = c +//│ let ct: Test +//│ ct +//│ = C {} + +c.foo +//│ 1 +//│ res +//│ = 1 + +c.bar(true) +//│ true +//│ res +//│ = true + +// :d +c: Test +//│ Test +//│ res +//│ = C {} + +:e +c: Oth +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.240: c: Oth +//│ ║ ^ +//│ ╟── application of type `C` is not an instance of type `Oth` +//│ ║ l.208: let c = C() +//│ ║ ^^^ +//│ ╟── but it flows into reference with expected type `#Oth` +//│ ║ l.240: c: Oth +//│ ║ ^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.240: c: Oth +//│ ╙── ^^^ +//│ Oth +//│ res +//│ = C {} + +// :d +let c1: Test = C() +//│ let c1: Test +//│ c1 +//│ = C {} + +// :d +fun fcc(x: C) = x.foo +//│ fun fcc: (x: C) -> 1 + +fun fc(x: Test) = x +fun ffm(x: F) = x.get +//│ fun fc: (x: Test) -> Test +//│ fun ffm: (x: F) -> true + +:e +fun fee(x: Test) = x: Oth +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.273: fun fee(x: Test) = x: Oth +//│ ║ ^ +//│ ╟── type `#Test` is not an instance of type `Oth` +//│ ║ l.273: fun fee(x: Test) = x: Oth +//│ ║ ^^^^ +//│ ╟── but it flows into reference with expected type `#Oth` +//│ ║ l.273: fun fee(x: Test) = x: Oth +//│ ║ ^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.273: fun fee(x: Test) = x: Oth +//│ ╙── ^^^ +//│ fun fee: (x: Test) -> Oth + +fc(c) +//│ Test +//│ res +//│ = C {} + +fun fts['a](x: 'a & Test) = x.foo +fts(c) +//│ fun fts: (x: Test) -> Int +//│ Int +//│ res +//│ = 1 + +fts(oth1) +//│ Int +//│ res +//│ = +//│ oth1 is not implemented + +fts(c1) +//│ Int +//│ res +//│ = 1 + +trait A1 { fun a1: 1 | 2 | 3 } +trait A2 { fun a1: 2 | 3 | 4 } +//│ trait A1 { +//│ fun a1: 1 | 2 | 3 +//│ } +//│ trait A2 { +//│ fun a1: 2 | 3 | 4 +//│ } + +:e +class Ea1 extends A1, A2 { + fun a1 = 4 +} +//│ ╔══[ERROR] Type mismatch in definition of method a1: +//│ ║ l.322: fun a1 = 4 +//│ ║ ^^^^^^ +//│ ╟── integer literal of type `4` does not match type `1 | 2 | 3` +//│ ║ l.322: fun a1 = 4 +//│ ║ ^ +//│ ╟── but it flows into definition of method a1 with expected type `1 | 2 | 3` +//│ ║ l.322: fun a1 = 4 +//│ ║ ^^^^^^ +//│ ╟── Note: constraint arises from union type: +//│ ║ l.311: trait A1 { fun a1: 1 | 2 | 3 } +//│ ║ ^^^^^^^^^ +//│ ╟── from signature of member `a1`: +//│ ║ l.311: trait A1 { fun a1: 1 | 2 | 3 } +//│ ╙── ^^^^^^^^^^^^^ +//│ class Ea1 extends A1, A2 { +//│ constructor() +//│ fun a1: 4 +//│ } + +trait Ele { + fun ce: Oth -> Test +} +//│ trait Ele { +//│ fun ce: Oth -> Test +//│ } + +class CE extends Ele { + fun ce(x) = x +} +//│ class CE extends Ele { +//│ constructor() +//│ fun ce: forall 'a. 'a -> 'a +//│ } + +:e +class E1 extends Test { + fun foo = 2 +} +//│ ╔══[ERROR] Member `bar` is declared (or its declaration is inherited) but is not implemented in `E1` +//│ ║ l.360: class E1 extends Test { +//│ ║ ^^ +//│ ╟── Declared here: +//│ ║ l.6: fun bar: Bool -> Bool +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//│ class E1 extends Test { +//│ constructor() +//│ fun bar: Bool -> Bool +//│ fun foo: 2 +//│ } + +:e +trait TE1 extends C +trait TE2 extends M, Test +//│ ╔══[ERROR] A trait can only inherit from other traits +//│ ║ l.376: trait TE1 extends C +//│ ╙── ^ +//│ ╔══[ERROR] A trait can only inherit from other traits +//│ ║ l.377: trait TE2 extends M, Test +//│ ╙── ^ +//│ trait TE1 extends C, Test +//│ trait TE2 extends Test { +//│ fun bar: Bool -> Bool +//│ fun foo: Int +//│ } + +:e +class E2 extends Test { + fun foo = true + fun bar(x) = x +} +//│ ╔══[ERROR] Type mismatch in definition of method foo: +//│ ║ l.392: fun foo = true +//│ ║ ^^^^^^^^^^ +//│ ╟── reference of type `true` is not an instance of `Int` +//│ ║ l.392: fun foo = true +//│ ║ ^^^^ +//│ ╟── but it flows into definition of method foo with expected type `Int` +//│ ║ l.392: fun foo = true +//│ ║ ^^^^^^^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.5: fun foo: Int +//│ ║ ^^^ +//│ ╟── from signature of member `foo`: +//│ ║ l.5: fun foo: Int +//│ ╙── ^^^^^^^^ +//│ class E2 extends Test { +//│ constructor() +//│ fun bar: forall 'a. 'a -> 'a +//│ fun foo: true +//│ } + +:e +class D extends Test[Int], Test[Bool] +//│ ╔══[ERROR] trait Test expects 0 type parameter(s); got 1 +//│ ║ l.417: class D extends Test[Int], Test[Bool] +//│ ╙── ^^^^^^^^ +//│ ╔══[ERROR] trait Test expects 0 type parameter(s); got 1 +//│ ║ l.417: class D extends Test[Int], Test[Bool] +//│ ╙── ^^^^^^^^^ +//│ ╔══[ERROR] Member `foo` is declared (or its declaration is inherited) but is not implemented in `D` +//│ ║ l.417: class D extends Test[Int], Test[Bool] +//│ ║ ^ +//│ ╟── Declared here: +//│ ║ l.5: fun foo: Int +//│ ╙── ^^^^^^^^ +//│ ╔══[ERROR] Member `bar` is declared (or its declaration is inherited) but is not implemented in `D` +//│ ║ l.417: class D extends Test[Int], Test[Bool] +//│ ║ ^ +//│ ╟── Declared here: +//│ ║ l.6: fun bar: Bool -> Bool +//│ ╙── ^^^^^^^^^^^^^^^^^ +//│ class D extends Test { +//│ constructor() +//│ fun bar: Bool -> Bool +//│ fun foo: Int +//│ } + + + +trait Base: A | B +class A() extends Base +class B() extends Base +//│ trait Base: A | B +//│ class A() extends Base +//│ class B() extends Base + + +let b: Base = A() +//│ let b: Base +//│ b +//│ = A {} + +b: Base & (A | B) +//│ A & Base | B & Base +//│ res +//│ = A {} + +if b is + A then 0 + B then 1 +//│ 0 | 1 +//│ res +//│ = 0 + + +fun f(x: Base) = if x is + A then 0 + B then 1 +//│ fun f: (x: Base) -> (0 | 1) + +trait Base: Foo | Bar +class Foo[A](val aa: [A, A]) extends Base +class Bar[B](f: B => B) extends Base +//│ trait Base: Bar[?] | Foo[anything] +//│ class Foo[A](aa: [A, A]) extends Base +//│ class Bar[B](f: B -> B) extends Base + +let f: Foo = Foo([1, 2]) +//│ let f: Foo[anything] +//│ f +//│ = Foo {} + +f.aa +//│ [??A, ??A] +//│ res +//│ = [ 1, 2 ] + +let b: Base = f +//│ let b: Base +//│ b +//│ = Foo {} + +if b is Foo(a) then a else 0 +//│ 0 | [??A, ??A] +//│ res +//│ = [ 1, 2 ] + +:e // * Note: an error is raised in this case and not above because B is invariant so it can't be widened +if b is Bar(f) then f else 0 +//│ ╔══[ERROR] Type error in `case` expression +//│ ║ l.503: if b is Bar(f) then f else 0 +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type variable `B` leaks out of its scope +//│ ║ l.477: class Bar[B](f: B => B) extends Base +//│ ╙── ^ +//│ 0 | (??B & 'B) -> ('B | ??B0) +//│ res +//│ = 0 + +:e +if b is + Foo(a) then a + Bar(f) then f +//│ ╔══[ERROR] Type error in `case` expression +//│ ║ l.515: if b is +//│ ║ ^^^^ +//│ ║ l.516: Foo(a) then a +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.517: Bar(f) then f +//│ ║ ^^^^^^^^^^^^^^^ +//│ ╟── type variable `B` leaks out of its scope +//│ ║ l.477: class Bar[B](f: B => B) extends Base +//│ ╙── ^ +//│ anything +//│ res +//│ = [ 1, 2 ] + +:e +let tt1 = Test +//│ ╔══[ERROR] trait Test cannot be used in term position +//│ ║ l.533: let tt1 = Test +//│ ╙── ^^^^ +//│ let tt1: error +//│ 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 + +trait Geo +trait ZL extends Geo +trait GL extends Geo +trait WP extends ZL, GL +trait EM extends WP, Geo +//│ trait Geo +//│ trait ZL extends Geo +//│ trait GL extends Geo +//│ trait WP extends GL, Geo, ZL +//│ trait EM extends GL, Geo, WP, ZL + +let g: Geo +let z: ZL +let w: WP +let e: EM +//│ let e: EM +//│ let g: Geo +//│ let w: WP +//│ let z: ZL +//│ g +//│ = +//│ z +//│ = +//│ w +//│ = +//│ e +//│ = + +fun fot(x: EM): Geo = x +fun fit(x: EM): WP = x +w: Geo +z: Geo +e: WP +w: ZL & GL +e: ZL & Geo +//│ fun fot: (x: EM) -> Geo +//│ fun fit: (x: EM) -> WP +//│ Geo & ZL +//│ res +//│ = +//│ w is not implemented +//│ res +//│ = +//│ z is not implemented +//│ res +//│ = +//│ e is not implemented +//│ res +//│ = +//│ w is not implemented +//│ res +//│ = +//│ e is not implemented + +:e +fun fto(w: WP): EM = w +z: WP +g: ZL +e: ZL & WP +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.605: fun fto(w: WP): EM = w +//│ ║ ^ +//│ ╟── type `#WP` is not an instance of type `EM` +//│ ║ l.605: fun fto(w: WP): EM = w +//│ ║ ^^ +//│ ╟── but it flows into reference with expected type `#EM` +//│ ║ l.605: fun fto(w: WP): EM = w +//│ ║ ^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.605: fun fto(w: WP): EM = w +//│ ╙── ^^ +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.606: z: WP +//│ ║ ^ +//│ ╟── type `#ZL` is not an instance of type `WP` +//│ ║ l.562: let z: ZL +//│ ║ ^^ +//│ ╟── but it flows into reference with expected type `#WP` +//│ ║ l.606: z: WP +//│ ║ ^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.606: z: WP +//│ ╙── ^^ +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.607: g: ZL +//│ ║ ^ +//│ ╟── type `#Geo` is not an instance of type `ZL` +//│ ║ l.561: let g: Geo +//│ ║ ^^^ +//│ ╟── but it flows into reference with expected type `#ZL` +//│ ║ l.607: g: ZL +//│ ║ ^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.607: g: ZL +//│ ╙── ^^ +//│ fun fto: (w: WP) -> EM +//│ WP & ZL +//│ res +//│ = +//│ z is not implemented +//│ res +//│ = +//│ g is not implemented +//│ res +//│ = +//│ e is not implemented + +class Bs(val a: Bool) { + virtual fun foo(x) = x + 1 +} +//│ class Bs(a: Bool) { +//│ fun foo: Int -> Int +//│ } + +class Ih() extends Bs(false) { + fun bar(x) = x + fun foo(x) = 1 +} +//│ class Ih() extends Bs { +//│ fun bar: forall 'a. 'a -> 'a +//│ fun foo: anything -> 1 +//│ } + +let ih1 = Ih() +//│ let ih1: Ih +//│ ih1 +//│ = Ih {} + +ih1.foo(1) +//│ 1 +//│ res +//│ = 1 + +ih1: Bs +//│ Bs +//│ res +//│ = Ih {} + +ih1.a +//│ false +//│ res +//│ = false + +:e +class Eh2 extends Bs(true), Ele { + fun foo(x) = x && false + fun ce(x) = x +} +//│ ╔══[ERROR] Type mismatch in definition of method foo: +//│ ║ l.695: 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 +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in definition of method foo: +//│ ║ l.695: 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 +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in definition of method foo: +//│ ║ l.695: fun foo(x) = x && false +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ╟── operator application of type `Bool` does not match type `Int | ?a` +//│ ║ l.695: fun foo(x) = x && false +//│ ║ ^^^^^^^^^^ +//│ ╟── Note: constraint arises from operator application: +//│ ║ l.658: virtual fun foo(x) = x + 1 +//│ ╙── ^^^^^ +//│ ╔══[ERROR] Type mismatch in definition of method foo: +//│ ║ l.695: fun foo(x) = x && false +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ╟── operator application of type `Bool` does not match type `Int | ?a` +//│ ║ l.695: fun foo(x) = x && false +//│ ║ ^^^^^^^^^^ +//│ ╟── Note: constraint arises from operator application: +//│ ║ l.658: virtual fun foo(x) = x + 1 +//│ ╙── ^^^^^ +//│ class Eh2 extends Bs, Ele { +//│ constructor() +//│ fun ce: forall 'a. 'a -> 'a +//│ fun foo: Bool -> Bool +//│ } + +:e +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) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── integer literal of type `1` is not an instance of type `Bool` +//│ ║ l.737: class Eh extends Bs(1) +//│ ║ ^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.657: class Bs(val a: Bool) { +//│ ╙── ^^^^ +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.737: class Eh extends Bs(1) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── integer literal of type `1` does not match type `Bool` +//│ ║ l.737: class Eh extends Bs(1) +//│ ║ ^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.657: class Bs(val a: Bool) { +//│ ╙── ^^^^ +//│ ╔══[ERROR] class Bs expects 1 parameter(s); got 0 +//│ ║ l.738: class Eh1 extends Bs +//│ ╙── ^^ +//│ ╔══[ERROR] Type mismatch in definition of method foo: +//│ ║ l.658: 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 +//│ ║ ^^^^^^^^^^^ +//│ ╟── but it flows into definition of method foo with expected type `Int` +//│ ║ l.658: virtual fun foo(x) = x + 1 +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.5: fun foo: Int +//│ ║ ^^^ +//│ ╟── from signature of member `foo`: +//│ ║ 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 +//│ ║ ^^^ +//│ ╟── Declared here: +//│ ║ l.6: fun bar: Bool -> Bool +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//│ class Eh extends Bs { +//│ constructor() +//│ fun foo: Int -> Int +//│ } +//│ class Eh1 extends Bs { +//│ constructor() +//│ fun foo: Int -> Int +//│ } +//│ class Eh3 extends Bs, Test { +//│ constructor() +//│ fun bar: Bool -> Bool +//│ fun foo: Int -> Int +//│ } + +class Ca(a: Int) extends Oth { + fun foo = 1 + fun cool(x) = false + fun bar(x) = x +} +//│ class Ca(a: Int) extends Oth, Test { +//│ fun bar: forall 'a. 'a -> 'a +//│ fun cool: anything -> false +//│ fun foo: 1 +//│ } + +class Cx(a2: 1 | 2, val b: Bool) extends Ca(a2) +//│ class Cx(a2: 1 | 2, b: Bool) extends Ca, Oth, Test { +//│ fun bar: forall 'a. 'a -> 'a +//│ fun cool: anything -> false +//│ fun foo: 1 +//│ } + +class Cx(a: 1 | 2, val b: Bool) extends Ca(a) +//│ class Cx(a: 1 | 2, b: Bool) extends Ca, Oth, Test { +//│ fun bar: forall 'a. 'a -> 'a +//│ fun cool: anything -> false +//│ fun foo: 1 +//│ } + +let cx1 = Cx(2, true) +//│ let cx1: Cx +//│ cx1 +//│ = Cx {} + +cx1.bar(cx1.b) +//│ Bool +//│ res +//│ = true + +cx1: Test +//│ Test +//│ res +//│ = Cx {} + +cx1: Ca +//│ Ca +//│ res +//│ = Cx {} + +class Bc1(foo: Int) +class Bc2(bar: Bool) +abstract class Bc3 { + let baz : Int +} +//│ class Bc1(foo: Int) +//│ class Bc2(bar: Bool) +//│ abstract class Bc3 { +//│ let baz: Int +//│ } + +: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) +//│ ╙── ^^^^^^^^^ +//│ class Bc12() extends Bc1, Bc2 +//│ Code generation encountered an error: +//│ unexpected parent symbol new class Bc2. + +class Bc02() extends Bc1(1 : Int) { + val foo = 2 +} +//│ class Bc02() extends Bc1 { +//│ val foo: 2 +//│ } + +Bc02().foo +//│ 2 +//│ res +//│ = 2 + +:e +class Bc31(baz: Bool) extends Bc3 +//│ ╔══[ERROR] Type mismatch in type reference: +//│ ║ l.874: 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 +//│ ║ ^^^ +//│ ╟── from signature of member `baz`: +//│ ║ l.844: let baz : Int +//│ ╙── ^^^^^^^^^ +//│ ╔══[ERROR] Type mismatch in type reference: +//│ ║ l.874: 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 +//│ ║ ^^^ +//│ ╟── from signature of member `baz`: +//│ ║ l.844: let baz : Int +//│ ╙── ^^^^^^^^^ +//│ class Bc31(baz: Bool) extends Bc3 + +class Bc11 extends Bc1(1) { + let foo = true +} +//│ class Bc11 extends Bc1 { +//│ constructor() +//│ let foo: true +//│ } + + +trait Base[A] { fun f: A -> A } +//│ trait Base[A] { +//│ fun f: A -> A +//│ } + +class Der1 extends Base[Int] { fun f(x) = x + 1 } +//│ class Der1 extends Base { +//│ constructor() +//│ fun f: Int -> Int +//│ } + +class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [x, y] } +//│ class Der2[A, B] extends Base { +//│ constructor() +//│ fun f: forall 'a 'b. (['a, 'b]) -> ['a, 'b] +//│ } + +:e +trait BInt extends Base[Int] { + fun f = error +} +//│ ╔══[ERROR] Method implementations in traits are not yet supported +//│ ║ l.925: fun f = error +//│ ╙── ^^^^^^^^^^^^^ +//│ trait BInt extends Base { +//│ fun f: nothing +//│ } + +trait BPar[T] extends Base[[Int, T]] +//│ trait BPar[T] extends Base { +//│ fun f: 'A -> 'A +//│ } +//│ where +//│ 'A := [Int, T] + +let bi: BInt +let bp: BPar[Bool] +//│ let bi: BInt +//│ let bp: BPar[Bool] +//│ bi +//│ = +//│ bp +//│ = + +bp: Base[[Int, Bool]] +//│ Base[[Int, Bool]] +//│ res +//│ = +//│ bp is not implemented + +:e +bp: Base[[Int, Int]] +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.957: bp: Base[[Int, Int]] +//│ ║ ^^ +//│ ╙── expression of type `true` is not an instance of type `Int` +//│ Base[[Int, Int]] +//│ res +//│ = +//│ bp is not implemented + +bi.f(1) +//│ nothing +//│ res +//│ = +//│ bi is not implemented + +bp.f +//│ ([Int, Bool]) -> [Int, Bool] +//│ res +//│ = +//│ bp is not implemented + +fun fb[T](x: Base[[Int, T]], y: T) = x.f([1, y]) +//│ fun fb: forall 'T. (x: Base[[Int, 'T]], y: 'T) -> [Int, 'T] + +fb(bp, false) +//│ [Int, Bool] +//│ res +//│ = +//│ bp is not implemented + +class CP() extends BPar[Int] { + fun f(x) = [x.1, x.0] +} +//│ class CP() extends BPar, Base { +//│ fun f: forall 'a 'b. {0: 'a, 1: 'b} -> ['b, 'a] +//│ } + +let cp1 = CP() +//│ let cp1: CP +//│ cp1 +//│ = CP {} + +fb(cp1, 2) +//│ [Int, Int] +//│ res +//│ = [ 2, 1 ] + +trait BInfer1 extends Base +//│ trait BInfer1 extends Base { +//│ fun f: 'A -> 'A +//│ } + +trait BInfer2 extends Base { + fun f: Int -> Int +} +//│ trait BInfer2 extends Base { +//│ fun f: Int -> Int +//│ } + +: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] +//│ ╙── ^^^^^^^^^^^^^ +//│ ╔══[ERROR] Member `f` is declared (or its declaration is inherited) but is not implemented in `DerBad1` +//│ ║ l.1018: class DerBad1 extends Base[Int, Int] +//│ ║ ^^^^^^^ +//│ ╟── Declared here: +//│ ║ l.906: trait Base[A] { fun f: A -> A } +//│ ╙── ^^^^^^^^^^^^^ +//│ class DerBad1 extends Base { +//│ constructor() +//│ fun f: 'A -> 'A +//│ } +//│ where +//│ 'A := 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] } +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ╟── 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] } +//│ ║ ^ +//│ ╟── Note: constraint arises from type parameter: +//│ ║ l.1036: 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] } +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in definition of method f: +//│ ║ l.1036: 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] } +//│ ║ ^ +//│ ╟── Note: constraint arises from type parameter: +//│ ║ l.1036: 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] } +//│ ╙── ^ +//│ class Der2[A, B] extends Base { +//│ constructor() +//│ fun f: forall 'a 'b. (['b, 'a]) -> ['a, 'b] +//│ } + +trait Ta[T] { + val p: Bool + val g: T +} +class K[A](val k: Ta[A]) +//│ trait Ta[T] { +//│ val g: T +//│ val p: Bool +//│ } +//│ class K[A](k: Ta[A]) + +let ta1: Ta[Int] +//│ let ta1: Ta[Int] +//│ ta1 +//│ = + +let k1 = K(ta1) +//│ let k1: K[Int] +//│ k1 +//│ = +//│ ta1 is not implemented + +k1.k : Ta[Int] +//│ Ta[Int] +//│ res +//│ = +//│ k1 and ta1 are not implemented + +k1.k.g +//│ Int +//│ res +//│ = +//│ k1 and ta1 are not implemented + +k1.k.p +//│ Bool +//│ res +//│ = +//│ k1 and ta1 are not implemented + +:e +trait Tb extends Ta[Int] { + virtual val p = false +} +//│ ╔══[ERROR] Method implementations in traits are not yet supported +//│ ║ l.1108: virtual val p = false +//│ ╙── ^^^^^^^^^^^^^ +//│ trait Tb extends Ta { +//│ val g: 'T +//│ val p: false +//│ } +//│ where +//│ 'T := Int + +class Ctb extends Tb { + let p = false + let g = 2 +} +//│ class Ctb extends Ta, Tb { +//│ constructor() +//│ let g: 2 +//│ let p: false +//│ } + +class G1[A](x: A) +//│ class G1[A](x: A) + +class GI(x2: Int) extends G1[Int](x2) +//│ class GI(x2: Int) extends G1 + +trait Oz { + let age: Int +} +//│ trait Oz { +//│ let age: Int +//│ } + +:e +class Fischl(age: Bool) extends Oz +//│ ╔══[ERROR] Type mismatch in type reference: +//│ ║ l.1144: 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 +//│ ║ ^^^ +//│ ╟── from signature of member `age`: +//│ ║ l.1137: let age: Int +//│ ╙── ^^^^^^^^ +//│ ╔══[ERROR] Type mismatch in type reference: +//│ ║ l.1144: 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 +//│ ║ ^^^ +//│ ╟── from signature of member `age`: +//│ ║ l.1137: let age: Int +//│ ╙── ^^^^^^^^ +//│ class Fischl(age: Bool) extends Oz + +class Klee(age: 1 | 2 | 3) extends Oz +//│ class Klee(age: 1 | 2 | 3) extends Oz + +class Fate { + virtual fun foo(x) = x + 1 +} +//│ class Fate { +//│ constructor() +//│ fun foo: Int -> Int +//│ } + +:e +class Go extends Fate { + fun foo(x) = x && true +} +//│ ╔══[ERROR] Type mismatch in definition of method foo: +//│ ║ l.1180: 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 +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in definition of method foo: +//│ ║ l.1180: 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 +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in definition of method foo: +//│ ║ l.1180: fun foo(x) = x && true +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ╟── operator application of type `Bool` does not match type `Int | ?a` +//│ ║ l.1180: fun foo(x) = x && true +//│ ║ ^^^^^^^^^ +//│ ╟── Note: constraint arises from operator application: +//│ ║ l.1171: virtual fun foo(x) = x + 1 +//│ ╙── ^^^^^ +//│ ╔══[ERROR] Type mismatch in definition of method foo: +//│ ║ l.1180: fun foo(x) = x && true +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ╟── operator application of type `Bool` does not match type `Int | ?a` +//│ ║ l.1180: fun foo(x) = x && true +//│ ║ ^^^^^^^^^ +//│ ╟── Note: constraint arises from operator application: +//│ ║ l.1171: virtual fun foo(x) = x + 1 +//│ ╙── ^^^^^ +//│ class Go extends Fate { +//│ constructor() +//│ fun foo: Bool -> Bool +//│ } + +class Ha { virtual val x: Int = 1 } +//│ class Ha { +//│ constructor() +//│ val x: Int +//│ } + +class Haha(x: 1 | 2) extends Ha +//│ 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 +//│ ║ ^^^^ +//│ ╟── type `Bool` is not an instance of type `Int` +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.1219: class Ha { virtual val x: Int = 1 } +//│ ╙── ^^^ +//│ ╔══[ERROR] Type mismatch in type reference: +//│ ║ l.1229: 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 } +//│ ╙── ^^^ +//│ class Ohhh(x: Bool) extends Ha + +trait TA[A] { let a : A } +//│ trait TA[A] { +//│ let a: A +//│ } + +class G1[A, B](val a: A, val b: B) extends TA[A] +//│ class G1[A, B](a: A, b: B) extends TA + +class G2[T](x: T) extends G1[T, Int](x, 1) +//│ class G2[T](x: T) extends G1, TA + +let g21 = G2(false) +//│ let g21: G2['T] +//│ where +//│ 'T :> false +//│ g21 +//│ = G2 {} + +g21: G1[Bool, Int] +//│ G1[Bool, Int] +//│ res +//│ = G2 {} + +g21.a +//│ Bool +//│ res +//│ = false + +g21: TA[Bool] +//│ TA[Bool] +//│ res +//│ = G2 {} diff --git a/shared/src/test/diff/nu/IntraBlockPolymorphism.mls b/shared/src/test/diff/nu/IntraBlockPolymorphism.mls new file mode 100644 index 0000000000..57c3767c55 --- /dev/null +++ b/shared/src/test/diff/nu/IntraBlockPolymorphism.mls @@ -0,0 +1,60 @@ +:NewDefs + + +// * Note: eventually we should progressively type check every mutually-recursive group +// * in topological order, to maximize polymorphism. +// * But currently we just type check them in srouce code order. +// * So the following inferred types differ: + +fun i(x) = x +let a = i(0) +let b = i(true) +//│ fun i: forall 'a. 'a -> 'a +//│ let a: 0 +//│ let b: true +//│ a +//│ = 0 +//│ b +//│ = true + +:re // FIXME shouldn't be a reference error +let a = i(0) +fun i(x) = x +let b = i(true) +//│ let a: 0 | true | 'a +//│ fun i: forall 'b. ('a & 'b) -> (0 | 'b) +//│ let b: 0 | true +//│ a +//│ Runtime error: +//│ ReferenceError: i1 is not defined +//│ b +//│ = true + +:re // FIXME shouldn't be a reference error +let a = i(0) +let b = i(true) +fun i(x) = x +//│ let a: 0 | true | 'a +//│ let b: 0 | true | 'a +//│ fun i: forall 'b. ('a & 'b) -> (0 | true | 'b) +//│ a +//│ Runtime error: +//│ ReferenceError: i2 is not defined +//│ b +//│ Runtime error: +//│ ReferenceError: i2 is not defined + + +module Test { + fun i(x) = x + let a = i(0) + let b = i(true) +} +//│ module Test { +//│ let a: 0 +//│ let b: true +//│ fun i: forall 'a. 'a -> 'a +//│ } + + + diff --git a/shared/src/test/diff/nu/Jonathan.mls b/shared/src/test/diff/nu/Jonathan.mls new file mode 100644 index 0000000000..b4d0b6ef45 --- /dev/null +++ b/shared/src/test/diff/nu/Jonathan.mls @@ -0,0 +1,100 @@ +:NewDefs +:NoJS + + +// * A monadic effect type, covariant in base type and effect type +class Effectful[out A, out E](value: A) { + fun flatMap[B](f: A => Effectful[B, E]): Effectful[B, E] = + f(value) +} +fun pure[A](a: A): Effectful['a, 'e] = Effectful(a) +//│ class Effectful[A, E](value: A) { +//│ fun flatMap: forall 'B. (f: A -> Effectful['B, E]) -> Effectful['B, E] +//│ } +//│ fun pure: forall 'A. (a: 'A) -> Effectful['A, nothing] + +// * Some effect tags +module IO +module Block +//│ module IO +//│ module Block + +// * Some example functions +fun println(x: anything): Effectful[(), IO] +fun readLine: Effectful[Str, IO | Block] +//│ fun println: (x: anything) -> Effectful[(), IO] +//│ fun readLine: Effectful[Str, Block | IO] + + +// * Define NonBlocking as an effectful computation that does not block (using type-level difference `\`) +type NonBlocking[out A, out E] = Effectful[A, E \ Block] +//│ type NonBlocking[A, E] = Effectful[A, E & ~Block] + +// * Example use of NonBlocking in an annotation; the type arguments are inferred +fun f(x) = x : NonBlocking +//│ fun f: forall 'a 'b. NonBlocking['a, 'b] -> NonBlocking['a, 'b] + + +// * the `listener` callback should be non-blocking +fun onMousePressed(listener) = + let l(e) = listener(e) : NonBlocking + l(0).flatMap of a => l(1).flatMap of b => pure of () +//│ fun onMousePressed: forall 'a. ((0 | 1) -> NonBlocking[anything, 'a]) -> Effectful[(), 'a & ~Block] + + +// * OK: `println` does not block +onMousePressed(event => println("Clicked!")) +//│ Effectful[(), IO & ~Block] + +// * NOT OK: `readLine` blocks +:e +onMousePressed(event => readLine.flatMap(println)) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.51: onMousePressed(event => readLine.flatMap(println)) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type `Block` does not match type `~Block` +//│ ║ l.24: fun readLine: Effectful[Str, IO | Block] +//│ ╙── ^^^^^ +//│ Effectful[(), IO & ~Block] | error + + +class Event +class MouseEvent extends Event +module Register +//│ class Event { +//│ constructor() +//│ } +//│ class MouseEvent extends Event { +//│ constructor() +//│ } +//│ module Register + +fun onMousePressed(listener) = + let l(e: MouseEvent) = listener(e) : Effectful[(), 'e \ Block \ Register] + () +//│ fun onMousePressed: (MouseEvent -> Effectful[(), ~Block & ~Register]) -> () + +// def onMouseClick ( f : Event -> Unit \ { ef - Register }): Unit \ { Register } +fun onMouseClick(f: Event -> Effectful[(), 'e \ Register]): Effectful[(), Register] +//│ fun onMouseClick: (f: Event -> Effectful[(), ~Register]) -> Effectful[(), Register] + +onMouseClick of ev => pure of () +//│ Effectful[(), Register] + +:e +onMouseClick of ev => + onMouseClick(ev => pure of ()).flatMap of _ => + pure of () +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.85: onMouseClick of ev => +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.86: onMouseClick(ev => pure of ()).flatMap of _ => +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.87: pure of () +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── type `Register` does not match type `~Register` +//│ ║ l.78: fun onMouseClick(f: Event -> Effectful[(), 'e \ Register]): Effectful[(), Register] +//│ ╙── ^^^^^^^^ +//│ Effectful[(), Register] | error + + diff --git a/shared/src/test/diff/nu/LamPatterns.mls b/shared/src/test/diff/nu/LamPatterns.mls new file mode 100644 index 0000000000..557784389d --- /dev/null +++ b/shared/src/test/diff/nu/LamPatterns.mls @@ -0,0 +1,62 @@ +:NewDefs + + +class Some(value: Int) +//│ class Some(value: Int) + +:e // TODO +Some(x) => x +//│ ╔══[ERROR] Unsupported pattern shape: +//│ ║ l.8: Some(x) => x +//│ ╙── ^^^^^^^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.8: Some(x) => x +//│ ╙── ^ +//│ error -> error +//│ Code generation encountered an error: +//│ term App(Var(Some),Tup(List((None,Fld(_,Var(x)))))) is not a valid pattern + +:js +// FIXME type +let f = Some => 0 +//│ let f: ((value: Int) -> Some) -> 0 +//│ // Prelude +//│ class TypingUnit2 {} +//│ const typing_unit2 = new TypingUnit2; +//│ // Query 1 +//│ globalThis.f = function f(Some) { +//│ return 0; +//│ }; +//│ // End of generated code +//│ f +//│ = [Function: f] + +// :e // TODO +f(Some) +//│ 0 +//│ res +//│ = 0 + +// :e // TODO +f(_ => error) +//│ 0 +//│ res +//│ = 0 + +:e // TODO +f(Some(0)) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.47: f(Some(0)) +//│ ║ ^^^^^^^^^^ +//│ ╟── application of type `Some` is not a function +//│ ║ l.47: f(Some(0)) +//│ ║ ^^^^^^^ +//│ ╟── Note: constraint arises from reference: +//│ ║ l.21: let f = Some => 0 +//│ ╙── ^^^^ +//│ 0 | error +//│ res +//│ = 0 + + + diff --git a/shared/src/test/diff/nu/LetRec.mls b/shared/src/test/diff/nu/LetRec.mls index a1ca2c44a9..1ff16fa41a 100644 --- a/shared/src/test/diff/nu/LetRec.mls +++ b/shared/src/test/diff/nu/LetRec.mls @@ -1,38 +1,50 @@ -:NewParser +:NewDefs + :js fun f(x) = x > 0 && f(x - 1) +//│ fun f: Int -> Bool //│ // Prelude //│ let res; +//│ class TypingUnit {} +//│ const typing_unit = new TypingUnit; //│ // Query 1 //│ globalThis.f = function f(x) { //│ return x > 0 && f(x - 1); //│ }; //│ // End of generated code -//│ f: int -> bool -//│ = [Function: f] f(12) -//│ res: bool -//│ = false +//│ Bool +//│ res +//│ = false :js let rec f(x) = x > 0 && f(x - 1) +//│ let rec f: Int -> Bool +//│ // Prelude +//│ class TypingUnit2 {} +//│ const typing_unit2 = new TypingUnit2; //│ // Query 1 //│ globalThis.f1 = function f1(x) { //│ return x > 0 && f1(x - 1); //│ }; //│ // End of generated code -//│ f: int -> bool -//│ = [Function: f1] +//│ f +//│ = [Function: f1] f(12) -//│ res: bool -//│ = false +//│ Bool +//│ res +//│ = false :js let rec f() = f() +//│ let rec f: () -> nothing +//│ // Prelude +//│ class TypingUnit4 {} +//│ const typing_unit4 = new TypingUnit4; //│ // Query 1 //│ globalThis.f2 = function f2() { //│ return ((() => { @@ -40,44 +52,127 @@ let rec f() = //│ })()); //│ }; //│ // End of generated code -//│ f: () -> nothing -//│ = [Function: f2] +//│ f +//│ = [Function: f2] :re f() -//│ res: nothing +//│ nothing +//│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded -// TODO (for later) this should be rejected by the type checker +// :e // TODO this should be rejected by the type checker :ge :js let rec f = f -//│ f: nothing +//│ let rec f: nothing //│ Code generation encountered an error: //│ unguarded recursive use of by-value binding f :re :js f +//│ nothing +//│ // Prelude +//│ class TypingUnit7 {} +//│ const typing_unit7 = new TypingUnit7; //│ // Query 1 //│ res = f3; //│ // End of generated code -//│ res: nothing +//│ res //│ Runtime error: //│ ReferenceError: f3 is not defined +// :e // TODO this should be rejected by the type checker +:ge +:js +fun test = + let rec f = + f +//│ fun test: () +//│ Code generation encountered an error: +//│ unguarded recursive use of by-value binding f + +fun test = + let rec f() = f() +//│ fun test: () + +fun test = + let rec lol = () => lol +//│ fun test: () + +fun test = + let rec lol() = lol + lol +//│ fun test: forall 'lol. 'lol +//│ where +//│ 'lol :> () -> 'lol + +fun testWithAsc = + let rec aux: Int -> Int = x => if x <= 0 then 1 else x * aux(x - 1) + aux(10) +testWithAsc +//│ fun testWithAsc: Int +//│ Int +//│ res +//│ = 3628800 + +let rec lol = () => lol +//│ let rec lol: forall 'lol. 'lol +//│ where +//│ 'lol :> () -> 'lol +//│ lol +//│ = [Function: lol] + :p let rec f = 1 //│ |#let| |#rec| |f| |#=| |1| +//│ AST: TypingUnit(List(NuFunDef(Some(true),Var(f),None,List(),Left(IntLit(1))))) //│ Parsed: let rec f = 1; -//│ Desugared: rec def f: 1 -//│ AST: Def(true, f, IntLit(1), false) -//│ f: 1 -//│ = 1 +//│ let rec f: 1 +//│ f +//│ = 1 let rec f = 1 -//│ f: 1 -//│ = 1 +//│ let rec f: 1 +//│ f +//│ = 1 + + +// :e // FIXME +:ge +let foo = foo +//│ let foo: nothing +//│ Code generation encountered an error: +//│ unguarded recursive use of by-value binding foo + + +// FIXME should work +// No recursion: +let foo = 1 +let foo = foo + 1 +//│ let foo: 1 +//│ let foo: Int +//│ foo +//│ = 1 +//│ foo +//│ = 2 + + +// FIXME +let foo = foo.x +//│ let foo: nothing +//│ Code generation encountered an error: +//│ unguarded recursive use of by-value binding foo + +// :e // FIXME +:ge +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 new file mode 100644 index 0000000000..7fab3f5102 --- /dev/null +++ b/shared/src/test/diff/nu/ListConsNil.mls @@ -0,0 +1,48 @@ +:NewDefs + + + +type List[out A] = Cons[A] | Nil +class Cons[out A](head: A, tail: List[A]) { + fun size : Int + fun size = 1 + tail.size +} +module Nil { fun size = 0 } +//│ type List[A] = Cons[A] | Nil +//│ class Cons[A](head: A, tail: List[A]) { +//│ fun size: Int +//│ } +//│ module Nil { +//│ fun size: 0 +//│ } + + + +// TODO should better simplify these types (reduce the List refinement) + +:ng +let test0: Cons[Int] & List[Num] +let test1: Nil & List[Int] +//│ let test0: Cons[Int] & List[Num] +//│ let test1: Nil & List[Int] + + + +fun list_assoc(s, l) = + if l is + Cons(h, t) then + if eq(s)(h._1) then Cons(h._2, Nil) + else list_assoc(s, t) + Nil then Nil +//│ fun list_assoc: forall 'A. (anything, Cons[{_1: anything, _2: 'A}] | Nil) -> (Cons['A] | Nil) + +fun test(x, l) = list_assoc(42, Cons(x, l)) +//│ fun test: forall 'A. ({_1: anything, _2: 'A}, List[{_1: anything, _2: 'A}]) -> (Cons['A] | Nil) + +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]) + + + diff --git a/shared/src/test/diff/nu/LitMatch.mls b/shared/src/test/diff/nu/LitMatch.mls new file mode 100644 index 0000000000..211cec96b1 --- /dev/null +++ b/shared/src/test/diff/nu/LitMatch.mls @@ -0,0 +1,38 @@ +:NewDefs + + +let r = false : Bool +//│ let r: Bool +//│ r +//│ = false + +let b = (if r then true else false) : Bool +//│ let b: Bool +//│ b +//│ = false + +let b = false : Bool +//│ let b: Bool +//│ b +//│ = false + +b : true | false +//│ Bool +//│ res +//│ = false + +if false is false then 0 +//│ 0 +//│ res +//│ = 0 + +fun foo(x) = if x is + false then 0 +//│ fun foo: nothing -> 0 + +fun foo(x) = if x is + false then 0 + true then 1 +//│ fun foo: nothing -> (0 | 1) + + diff --git a/shared/src/test/diff/nu/LocalLets.mls b/shared/src/test/diff/nu/LocalLets.mls index cbccb80778..211987ae06 100644 --- a/shared/src/test/diff/nu/LocalLets.mls +++ b/shared/src/test/diff/nu/LocalLets.mls @@ -1,15 +1,37 @@ -:NewParser +:NewDefs -// FIXME let f = let tmp = "ok" 123 -//│ ╔══[ERROR] Illegal position for this definition statement. -//│ ║ l.6: let tmp = "ok" -//│ ╙── ^^^^^^^^^^ -//│ f: 123 -//│ Code generation encountered an error: -//│ unsupported definitions in blocks +//│ let f: 123 +//│ f +//│ = 123 + + +let x : Int | string +let x = 1 +//│ let x: 1 +//│ let x: Int | string +//│ x +//│ = +//│ x +//│ = 1 + + +class E(x: Int) +//│ class E(x: Int) + +:e // TODO support (currently parsed as a function definition named E) +let E(x) = new E(1) +//│ ╔══[ERROR] value E cannot be used as a type +//│ ║ l.26: let E(x) = new E(1) +//│ ╙── ^ +//│ ╔══[ERROR] type identifier not found: E +//│ ║ l.26: let E(x) = new E(1) +//│ ╙── ^ +//│ let E: anything -> error +//│ E +//│ = [Function: E1] diff --git a/shared/src/test/diff/nu/MIscPoly.mls b/shared/src/test/diff/nu/MIscPoly.mls new file mode 100644 index 0000000000..3d9c94a5af --- /dev/null +++ b/shared/src/test/diff/nu/MIscPoly.mls @@ -0,0 +1,78 @@ +:NewDefs + + + +[(x: 'a) => x] +//│ [forall 'a. (x: 'a) -> 'a] +//│ res +//│ = [ [Function (anonymous)] ] + +[forall 'a: (x: 'a) => x] +//│ [forall 'a. (x: 'a) -> 'a] +//│ res +//│ = [ [Function (anonymous)] ] + + + +abstract class C0[A] { + fun use: forall 'r: [A, 'r -> 'r] +} +//│ abstract class C0[A] { +//│ fun use: forall 'r. [A, 'r -> 'r] +//│ } + +class C1 extends C0[Int] { + fun use = [0, id] +} +//│ class C1 extends C0 { +//│ constructor() +//│ fun use: [0, forall 'a. 'a -> 'a] +//│ } + +class C1[AA](aa: AA) extends C0[AA] { + fun use = [aa, id] +} +//│ class C1[AA](aa: AA) extends C0 { +//│ fun use: [AA, forall 'a. 'a -> 'a] +//│ } + + + +// * FIXME currently we always distribute `forall` types; +// * but this is not sound when distributing into a non-function such as an object type +// * as long as we perform object type intersection merges (which we want to) + +class C[A](){fun f: A -> A = id} +//│ class C[A]() { +//│ fun f: A -> A +//│ } + +let c = C() +//│ let c: forall 'A. C['A] +//│ c +//│ = C {} + +// :e // FIXME +let d = c : C[Int] & C[Str] +//│ let d: C[in Int | Str out nothing] +//│ d +//│ = C {} + +// :e // FIXME +let r = d.f(0) +//│ let r: nothing +//│ r +//│ = 0 + +:re +r() +//│ nothing +//│ res +//│ Runtime error: +//│ TypeError: r is not a function + + + + + + diff --git a/shared/src/test/diff/nu/MemberConfusion.mls b/shared/src/test/diff/nu/MemberConfusion.mls new file mode 100644 index 0000000000..3609783f7f --- /dev/null +++ b/shared/src/test/diff/nu/MemberConfusion.mls @@ -0,0 +1,77 @@ +:NewDefs + + +mixin T { fun a = "hi" } +//│ mixin T() { +//│ fun a: "hi" +//│ } + +class C(a: Int) extends T +//│ class C(a: Int) { +//│ fun a: "hi" +//│ } + +class B { virtual val a = "hi" } +//│ class B { +//│ constructor() +//│ val a: "hi" +//│ } + +:e +class C(a: Int) extends B +//│ ╔══[ERROR] Type mismatch in type reference: +//│ ║ l.21: class C(a: Int) extends B +//│ ║ ^^^ +//│ ╟── type `Int` does not match type `"hi"` +//│ ╟── Note: constraint arises from string literal: +//│ ║ l.14: class B { virtual val a = "hi" } +//│ ╙── ^^^^ +//│ class C(a: Int) extends B + + +mixin M { let b = "hi" } +//│ mixin M() { +//│ let b: "hi" +//│ } + +class B { virtual val a = 1 : Int } +//│ class B { +//│ constructor() +//│ val a: Int +//│ } + +class C(val a: Int, val b: Int) extends B, M +//│ class C(a: Int, b: Int) extends B { +//│ let b: "hi" +//│ } + +let c = C(2, 3) +[c.a, c.b] +//│ let c: C +//│ [Int, "hi"] +//│ c +//│ = C {} +//│ res +//│ = [ 2, 3 ] + +class C(a: Int) { let a = 1 } +//│ class C(a: Int) { +//│ let a: 1 +//│ } + +class C(a: Int) { fun a = 1 } +//│ class C(a: Int) { +//│ fun a: 1 +//│ } + +class C(a: Int) { fun a = a } +//│ class C(a: Int) { +//│ fun a: nothing +//│ } + +class C(a: Int, b: Int) extends B, M { let b = "hi" } +//│ class C(a: Int, b: Int) extends B { +//│ let b: "hi" +//│ } + + diff --git a/shared/src/test/diff/nu/MemberIntersections.mls b/shared/src/test/diff/nu/MemberIntersections.mls new file mode 100644 index 0000000000..1782ac84d6 --- /dev/null +++ b/shared/src/test/diff/nu/MemberIntersections.mls @@ -0,0 +1,122 @@ +:NewDefs + +:NoJS // TODO + + +trait T1 { fun f[A]: A -> A } +trait T2 { fun f[B, C]: (B, C) -> [B, C] } +trait T3 extends T1, T2 +//│ trait T1 { +//│ fun f: forall 'A. 'A -> 'A +//│ } +//│ trait T2 { +//│ fun f: forall 'B 'C. ('B, 'C) -> ['B, 'C] +//│ } +//│ trait T3 extends T1, T2 { +//│ fun f: forall 'A 'B 'C. 'A -> 'A & ('B, 'C) -> ['B, 'C] +//│ } + + +trait S1 { class f } +//│ trait S1 { +//│ class f { +//│ constructor() +//│ } +//│ } + +:e +trait S2 extends T1, S1 +//│ ╔══[ERROR] Intersection of value member and class members currently unsupported +//│ ║ l.28: trait S2 extends T1, S1 +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── The value member is defined here: +//│ ║ l.6: trait T1 { fun f[A]: A -> A } +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ╟── The class member is defined here: +//│ ║ l.20: trait S1 { class f } +//│ ╙── ^^^^^^^ +//│ trait S2 extends S1, T1 + +trait S2 { class f } +//│ trait S2 { +//│ class f { +//│ constructor() +//│ } +//│ } + +:e +trait S3 extends S1, S2 +//│ ╔══[ERROR] Intersection of class member and class members currently unsupported +//│ ║ l.48: trait S3 extends S1, S2 +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── The class member is defined here: +//│ ║ l.20: trait S1 { class f } +//│ ║ ^^^^^^^ +//│ ╟── The class member is defined here: +//│ ║ l.40: trait S2 { class f } +//│ ╙── ^^^^^^^ +//│ trait S3 extends S1, S2 + + +trait S1 { val f: Int -> Int } +//│ trait S1 { +//│ val f: Int -> Int +//│ } + +trait S2 extends T1, S1 +//│ trait S2 extends S1, T1 { +//│ fun f: forall 'A. 'A -> 'A & Int -> Int +//│ } + +trait S3 extends S1, T1 +//│ trait S3 extends S1, T1 { +//│ fun f: forall 'A. Int -> Int & 'A -> 'A +//│ } + + +class C1(val x: Int | Bool) +trait T1 { val x: Int | Str } +//│ class C1(x: Int | false | true) +//│ trait T1 { +//│ val x: Int | Str +//│ } + +class C2() extends C1(0), T1 +//│ class C2() extends C1, T1 + +C2().x : 0 +//│ 0 + +:e +class C2 extends C1(false), T1 +//│ ╔══[ERROR] Type mismatch in reference: +//│ ║ l.91: class C2 extends C1(false), T1 +//│ ║ ^^^^^ +//│ ╟── reference of type `false` does not match type `Int | Str` +//│ ╟── Note: constraint arises from union type: +//│ ║ l.78: trait T1 { val x: Int | Str } +//│ ║ ^^^^^^^^^ +//│ ╟── from signature of member `x`: +//│ ║ l.78: trait T1 { val x: Int | Str } +//│ ╙── ^^^^^^^^^^^^ +//│ class C2 extends C1, T1 { +//│ constructor() +//│ } + +:e +class C2 extends C1("oops"), T1 +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.107: class C2 extends C1("oops"), T1 +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── string literal of type `"oops"` does not match type `Int | false | true` +//│ ║ l.107: class C2 extends C1("oops"), T1 +//│ ║ ^^^^^^ +//│ ╟── Note: constraint arises from union type: +//│ ║ l.77: class C1(val x: Int | Bool) +//│ ╙── ^^^^^^^^^^ +//│ class C2 extends C1, T1 { +//│ constructor() +//│ } + + + diff --git a/shared/src/test/diff/nu/MetaWrap.mls b/shared/src/test/diff/nu/MetaWrap.mls new file mode 100644 index 0000000000..ac0c228866 --- /dev/null +++ b/shared/src/test/diff/nu/MetaWrap.mls @@ -0,0 +1,176 @@ +:NewDefs + + +// * A test to show how to add meta data to underlying values by wrapping them. + + +mixin Base { + fun unwrap(x) = x + fun rewrap(x, f) = f(x) +} +//│ mixin Base() { +//│ fun rewrap: ('a, 'a -> 'b) -> 'b +//│ fun unwrap: 'c -> 'c +//│ } + + +mixin WithUid { + fun unwrap(x) = super.unwrap(x).underlying + fun rewrap(x, f) = super.rewrap(x, y => { underlying: f(y.underlying), uid: y.uid }) + fun getUid(x) = super.unwrap(x).uid + fun setUid(x, uid: Int) = super.rewrap(x, y => { + underlying: y.underlying, + uid + }) +} +//│ mixin WithUid() { +//│ super: { +//│ rewrap: ('a, forall 'uid. {uid: 'uid, underlying: 'underlying} -> {uid: 'uid, underlying: 'b}) -> 'c & ('d, forall 'underlying0. {underlying: 'underlying0} -> {uid: Int, underlying: 'underlying0}) -> 'e, +//│ unwrap: 'f -> {underlying: 'underlying1} & 'g -> {uid: 'uid0} +//│ } +//│ fun getUid: 'g -> 'uid0 +//│ fun rewrap: ('a, 'underlying -> 'b) -> 'c +//│ fun setUid: ('d, uid: Int) -> 'e +//│ fun unwrap: 'f -> 'underlying1 +//│ } + + +class Type(val name: Str) +//│ class Type(name: Str) + +mixin WithType { + fun unwrap(x) = super.unwrap(x).underlying + fun rewrap(x, f) = super.rewrap(x, y => { underlying: f(y.underlying), ty: y.ty }) + fun getType(x) = super.unwrap(x).ty + fun setType(x, ty: Type) = super.rewrap(x, y => { + underlying: y.underlying, + ty + }) +} +//│ mixin WithType() { +//│ super: { +//│ rewrap: ('a, forall 'ty. {ty: 'ty, underlying: 'underlying} -> {ty: 'ty, underlying: 'b}) -> 'c & ('d, forall 'underlying0. {underlying: 'underlying0} -> {ty: Type, underlying: 'underlying0}) -> 'e, +//│ unwrap: 'f -> {underlying: 'underlying1} & 'g -> {ty: 'ty0} +//│ } +//│ fun getType: 'g -> 'ty0 +//│ fun rewrap: ('a, 'underlying -> 'b) -> 'c +//│ fun setType: ('d, ty: Type) -> 'e +//│ fun unwrap: 'f -> 'underlying1 +//│ } + + +module Test0 extends Base, WithUid +//│ module Test0 { +//│ fun getUid: {uid: 'uid, underlying: 'underlying} -> 'uid +//│ fun rewrap: ({uid: 'uid0, underlying: 'underlying0 & 'underlying1}, 'underlying1 -> 'underlying0) -> {uid: Int | 'uid0, underlying: 'underlying0} +//│ fun setUid: ({uid: 'uid0, underlying: 'underlying0 & 'underlying1}, uid: Int) -> {uid: Int | 'uid0, underlying: 'underlying0} +//│ fun unwrap: {uid: 'uid, underlying: 'underlying} -> 'underlying +//│ } + +module Test0 extends Base, WithUid, WithUid +//│ module Test0 { +//│ fun getUid: {uid: anything, underlying: {uid: 'uid, underlying: 'underlying}} -> 'uid +//│ fun rewrap: ({ +//│ uid: 'uid0, +//│ underlying: {uid: 'uid1, underlying: 'underlying0 & 'underlying1} & 'underlying2 +//│ }, 'underlying1 -> 'underlying0) -> { +//│ uid: Int | 'uid0, +//│ underlying: 'underlying2 | {uid: Int | 'uid1, underlying: 'underlying0} +//│ } +//│ fun setUid: ({ +//│ uid: 'uid0, +//│ underlying: {uid: 'uid1, underlying: 'underlying0 & 'underlying1} & 'underlying2 +//│ }, uid: Int) -> { +//│ uid: Int | 'uid0, +//│ underlying: 'underlying2 | {uid: Int | 'uid1, underlying: 'underlying0} +//│ } +//│ fun unwrap: {uid: anything, underlying: {uid: 'uid, underlying: 'underlying}} -> 'underlying +//│ } + + +module Test1 extends Base, WithUid, WithType +//│ module Test1 { +//│ fun getType: {uid: 'uid, underlying: {ty: 'ty, underlying: 'underlying}} -> 'ty +//│ fun getUid: {uid: 'uid, underlying: {ty: 'ty, underlying: 'underlying}} -> 'uid +//│ fun rewrap: ({ +//│ uid: 'uid0, +//│ underlying: {ty: 'ty0, underlying: 'underlying0 & 'underlying1} & 'underlying2 +//│ }, 'underlying1 -> 'underlying0) -> { +//│ uid: Int | 'uid0, +//│ underlying: 'underlying2 | {ty: Type | 'ty0, underlying: 'underlying0} +//│ } +//│ fun setType: ({ +//│ uid: 'uid0, +//│ underlying: {ty: 'ty0, underlying: 'underlying0 & 'underlying1} & 'underlying2 +//│ }, ty: Type) -> { +//│ uid: Int | 'uid0, +//│ underlying: 'underlying2 | {ty: Type | 'ty0, underlying: 'underlying0} +//│ } +//│ fun setUid: ({ +//│ uid: 'uid0, +//│ underlying: {ty: 'ty0, underlying: 'underlying0 & 'underlying1} & 'underlying2 +//│ }, uid: Int) -> { +//│ uid: Int | 'uid0, +//│ underlying: 'underlying2 | {ty: Type | 'ty0, underlying: 'underlying0} +//│ } +//│ fun unwrap: {uid: 'uid, underlying: {ty: 'ty, underlying: 'underlying}} -> 'underlying +//│ } + +let uid = 0 +let ty = Type("A") +let underlying = 42 +let a = { uid, underlying: { ty, underlying } } +//│ let uid: 0 +//│ let ty: Type +//│ let underlying: 42 +//│ let a: {uid: 0, underlying: {ty: Type, underlying: 42}} +//│ uid +//│ = 0 +//│ ty +//│ = Type {} +//│ underlying +//│ = 42 +//│ a +//│ = { uid: 0, underlying: { ty: Type {}, underlying: 42 } } + +Test1.unwrap(a) +//│ 42 +//│ res +//│ = 42 + +Test1.getUid(a) +//│ 0 +//│ res +//│ = 0 + +Test1.setUid(a, 1) +//│ {uid: Int, underlying: {ty: Type, underlying: 42}} +//│ res +//│ = { underlying: { ty: Type {}, underlying: 42 }, uid: 1 } + +Test1.getType(a).name +//│ Str +//│ res +//│ = 'A' + +a.underlying.ty.name +//│ Str +//│ res +//│ = 'A' + +let b = Test1.setType(a, Type("B")) +//│ let b: {uid: Int, underlying: {ty: Type, underlying: 42}} +//│ b +//│ = { underlying: { underlying: 42, ty: Type {} }, uid: 0 } + +Test1.getType(b).name +//│ Str +//│ res +//│ = 'B' + +b.underlying.ty.name +//│ Str +//│ res +//│ = 'B' + + diff --git a/shared/src/test/diff/nu/Metaprog.mls b/shared/src/test/diff/nu/Metaprog.mls new file mode 100644 index 0000000000..3bf8befe70 --- /dev/null +++ b/shared/src/test/diff/nu/Metaprog.mls @@ -0,0 +1,65 @@ +:NewDefs + + +class Code[out A, out Ctx] +//│ class Code[A, Ctx] { +//│ constructor() +//│ } + +class IntLit(value: Int) extends Code[Int, nothing] +//│ class IntLit(value: Int) extends Code + +class Add[out C](lhs: Code[Int, C], rhs: Code[Int, C]) extends Code[Int, C] +//│ class Add[C](lhs: Code[Int, C], rhs: Code[Int, C]) extends Code + +fun bind(x: Code['a, 'c], k: (forall 'cc: Code['a, 'cc] -> Code['b, 'cc])): Code['b, 'c] = k(x) +//│ fun bind: forall 'a 'c 'b. (x: Code['a, 'c], k: forall 'cc. Code['a, 'cc] -> Code['b, 'cc]) -> Code['b, 'c] + + +// * Note: extrusion +fun test(f) = + bind of IntLit(42), n => + f(n) + Add(n, IntLit(1)) +//│ fun test: (Code[Int, ??cc] -> ()) -> Code[Int, nothing] + + +abstract class Test[C] { + // * Represents what happens in "... ${input} ..." when a binding of C is in scope + fun unquote: (input: Code['a, C | 'c]) -> Code[Int, 'c] + fun getVar: Code[Int, C] + fun test0 = this.unquote of IntLit(1) + fun test1 = this.unquote of Add(this.getVar, IntLit(1)) +} +//│ abstract class Test[C] { +//│ fun getVar: Code[Int, C] +//│ fun test0: Code[Int, nothing] +//│ fun test1: Code[Int, nothing] +//│ fun unquote: forall 'c. (input: Code[anything, 'c | C]) -> Code[Int, 'c] +//│ } + + +:NoJS + +fun mkVar(f: forall 'C: Test['C] -> 'a): 'a +//│ fun mkVar: forall 'a. (f: forall 'C. Test['C] -> 'a) -> 'a + +mkVar of t0 => + t0.unquote of Add(t0.getVar, IntLit(1)) +//│ Code[Int, nothing] + +mkVar of t0 => + Add(t0.getVar, IntLit(1)) +//│ Add[??C] + +mkVar of t0 => + mkVar of t1 => + t1.unquote of t0.unquote of Add(t0.getVar, t1.getVar) +//│ Code[Int, nothing] + +mkVar of t0 => + mkVar of t1 => + t0.unquote of t1.unquote of Add(t0.getVar, t1.getVar) +//│ Code[Int, nothing] + + diff --git a/shared/src/test/diff/nu/MethodSignatures.mls b/shared/src/test/diff/nu/MethodSignatures.mls new file mode 100644 index 0000000000..d2689f86aa --- /dev/null +++ b/shared/src/test/diff/nu/MethodSignatures.mls @@ -0,0 +1,177 @@ +:NewDefs + + + +module Oops { + fun a : Int + fun a = 2 +} +//│ module Oops { +//│ fun a: Int +//│ } + +:e +module Oops { + fun a : Int +} +//│ ╔══[ERROR] Member `a` is declared (or its declaration is inherited) but is not implemented in `Oops` +//│ ║ l.14: module Oops { +//│ ║ ^^^^ +//│ ╟── Declared here: +//│ ║ l.15: fun a : Int +//│ ╙── ^^^^^^^^^^^ +//│ module Oops { +//│ fun a: Int +//│ } + +:e +module Oops { + fun a : Int + fun a : string + fun a = a +} +//│ ╔══[ERROR] A type signature for 'a' was already given +//│ ║ l.30: fun a : string +//│ ╙── ^^^^^^^^^^^^^^ +//│ module Oops { +//│ fun a: string +//│ } + +:e +module Oops { + fun a : Int + fun a = false +} +//│ ╔══[ERROR] Type mismatch in definition of method a: +//│ ║ l.43: fun a = false +//│ ║ ^^^^^^^^^ +//│ ╟── reference of type `false` is not an instance of `Int` +//│ ║ l.43: fun a = false +//│ ║ ^^^^^ +//│ ╟── but it flows into definition of method a with expected type `Int` +//│ ║ l.43: fun a = false +//│ ║ ^^^^^^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.42: fun a : Int +//│ ║ ^^^ +//│ ╟── from signature of member `a`: +//│ ║ l.42: fun a : Int +//│ ╙── ^^^^^^^ +//│ module Oops { +//│ fun a: Int +//│ } + +:e +module Oops { + fun a = 1 + fun a = 2 +} +//│ ╔══[ERROR] Refininition of 'a' +//│ ║ l.67: fun a = 2 +//│ ╙── ^^^^^ +//│ module Oops { +//│ fun a: 1 +//│ } + + + +// * Without a type signature, the method's inferred type is not generalized +module A { + fun i(x) = x +} +//│ module A { +//│ fun i: forall 'a. 'a -> 'a +//│ } + +// * With a type signature, it is generalized and checked against the signature +module A { + + fun i: forall 'a: 'a -> 'a + fun i(x) = x + + fun j: 'b -> 'b + fun j(x) = x + +} +//│ module A { +//│ fun i: forall 'a. 'a -> 'a +//│ fun j: forall 'b. 'b -> 'b +//│ } + + +:e +module A { + fun i : 'a + fun i(x) = x +} +//│ ╔══[ERROR] Type mismatch in definition of method i: +//│ ║ l.105: fun i(x) = x +//│ ║ ^^^^^^^^ +//│ ╟── function of type `?a -> ?a` does not match type `'a` +//│ ║ l.105: fun i(x) = x +//│ ║ ^^^^^^^ +//│ ╟── but it flows into definition of method i with expected type `'a` +//│ ║ l.105: fun i(x) = x +//│ ║ ^^^^^^^^ +//│ ╟── Note: constraint arises from type variable: +//│ ║ l.104: fun i : 'a +//│ ║ ^^ +//│ ╟── from signature of member `i`: +//│ ║ l.104: fun i : 'a +//│ ╙── ^^^^^^ +//│ module A { +//│ fun i: nothing +//│ } + + + +// FIXME currently type signatures are typed too early (not in the context where the other defns live) +// We need to move all the typing unit setup to lazy type info prelude +// :d +module M { + class A + fun a: A + fun a = 1 +} +//│ ╔══[ERROR] Type mismatch in definition of method a: +//│ ║ l.134: fun a = 1 +//│ ║ ^^^^^ +//│ ╟── integer literal of type `1` is not an instance of type `A` +//│ ║ l.134: fun a = 1 +//│ ║ ^ +//│ ╟── but it flows into definition of method a with expected type `A` +//│ ║ l.134: fun a = 1 +//│ ║ ^^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.133: fun a: A +//│ ║ ^ +//│ ╟── from signature of member `a`: +//│ ║ l.133: fun a: A +//│ ╙── ^^^^ +//│ module M { +//│ class A { +//│ constructor() +//│ } +//│ fun a: A +//│ } + +// FIXME similar +module M { + class A + fun a: this.A + fun a = 1 +} +//│ ╔══[ERROR] undeclared `this` +//│ ║ l.161: fun a: this.A +//│ ╙── ^^^^ +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing + +// FIXME similar +module M { + class A + fun a: M.A + fun a = 1 +} +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing + + diff --git a/shared/src/test/diff/nu/Misc.mls b/shared/src/test/diff/nu/Misc.mls index 92cf1c2559..afe642af90 100644 --- a/shared/src/test/diff/nu/Misc.mls +++ b/shared/src/test/diff/nu/Misc.mls @@ -1,110 +1,227 @@ -:NewParser +:NewDefs 1 -//│ res: 1 -//│ = 1 +//│ 1 +//│ res +//│ = 1 2 + 2 -//│ res: int -//│ = 4 +//│ Int +//│ res +//│ = 4 let r = { x: 1 } -//│ r: {x: 1} -//│ = { x: 1 } +//│ let r: {x: 1} +//│ r +//│ = { x: 1 } r.x + 1 -//│ res: int -//│ = 2 +//│ Int +//│ res +//│ = 2 x => x + 1 -//│ res: int -> int -//│ = [Function: res] +//│ Int -> Int +//│ res +//│ = [Function: res] { y } => y -//│ res: {y: 'a} -> 'a -//│ = [Function: res] +//│ forall 'a. {y: 'a} -> 'a +//│ res +//│ = [Function: res] fun f({ y }) = y -//│ f: {y: 'a} -> 'a -//│ = [Function: f] +//│ fun f: forall 'a. {y: 'a} -> 'a fun f of { y } = y -//│ f: {y: 'a} -> 'a -//│ = [Function: f1] +//│ fun f: forall 'a. {y: 'a} -> 'a f({y: 1}) -//│ res: 1 -//│ = 1 +//│ 1 +//│ res +//│ = 1 + + +fun f of x, y = x + y +//│ fun f: (Int, Int) -> Int + +f of 1, 2 +//│ Int +//│ res +//│ = 3 + + +fun f of [x, y] = x + y +//│ fun f: ([Int, Int]) -> Int + +f of [1, 2] +//│ Int +//│ res +//│ = 3 let f = (x, y) => x + y -//│ f: (int, int,) -> int -//│ = [Function: f2] +//│ let f: (Int, Int) -> Int +//│ f +//│ = [Function: f4] f(1, 2) -//│ res: int -//│ = 3 +//│ Int +//│ res +//│ = 3 :e f([1, 2]) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.51: f([1, 2]) +//│ ║ l.76: f([1, 2]) //│ ║ ^^^^^^^^^ -//│ ╟── argument of type `((1, 2,),)` does not match type `(?a, ?b,)` -//│ ║ l.51: f([1, 2]) +//│ ╟── argument of type `[[1, 2]]` does not match type `[?a, ?b]` +//│ ║ l.76: f([1, 2]) //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: -//│ ║ l.42: let f = (x, y) => x + y +//│ ║ l.65: let f = (x, y) => x + y //│ ╙── ^^^^ -//│ res: error | int -//│ = '1,2undefined' +//│ Int | error +//│ res +//│ = '1,2undefined' +:e let f = ((x, y)) => x + y -//│ f: (int, int,) -> int -//│ = [Function: f3] +//│ ╔══[ERROR] Unsupported pattern shape: +//│ ║ l.93: let f = ((x, y)) => x + y +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.93: let f = ((x, y)) => x + y +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: y +//│ ║ l.93: let f = ((x, y)) => x + y +//│ ╙── ^ +//│ let f: error -> Int +//│ Code generation encountered an error: +//│ term App(Var(,),Tup(List((None,Fld(_,Var(x))), (None,Fld(_,Var(y)))))) is not a valid pattern :e f(1, 2) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.71: f(1, 2) -//│ ║ ^^^^^^^ -//│ ╟── argument list of type `(1, 2,)` does not match type `((?a, ?b,),)` -//│ ║ l.71: f(1, 2) -//│ ╙── ^^^^^^ -//│ res: error | int +//│ ║ l.108: f(1, 2) +//│ ║ ^^^^^^^ +//│ ╟── argument list of type `[1, 2]` does not match type `[error]` +//│ ║ l.108: f(1, 2) +//│ ╙── ^^^^^^ +//│ Int | error +//│ res //│ Runtime error: -//│ TypeError: number 1 is not iterable (cannot read property Symbol(Symbol.iterator)) - - +//│ ReferenceError: f5 is not defined +:re f((1, 2)) -//│ res: int -//│ = 3 +//│ Int +//│ res +//│ Runtime error: +//│ ReferenceError: f5 is not defined +:re f([1, 2]) -//│ res: int -//│ = 3 +//│ Int +//│ res +//│ Runtime error: +//│ ReferenceError: f5 is not defined -let f = (((x, y))) => x + y -//│ f: (int, int,) -> int -//│ = [Function: f4] +:e +f[1, 2] +//│ ╔══[ERROR] Type application syntax is not yet supported +//│ ║ l.135: f[1, 2] +//│ ╙── ^^^^^^^ +//│ error -> Int +//│ res +//│ Runtime error: +//│ ReferenceError: f5 is not defined -// TODO parse as tuple arg! + +:e +let f = (((x, y))) => x + y +//│ ╔══[ERROR] Unsupported pattern shape: +//│ ║ l.146: let f = (((x, y))) => x + y +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.146: let f = (((x, y))) => x + y +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: y +//│ ║ l.146: let f = (((x, y))) => x + y +//│ ╙── ^ +//│ let f: error -> Int +//│ Code generation encountered an error: +//│ term App(Var(,),Tup(List((None,Fld(_,Var(x))), (None,Fld(_,Var(y)))))) is not a valid pattern + + +// * TODO maybe parse as type lambda? let f = [x, y] => x + y -//│ f: (int, int,) -> int -//│ = [Function: f5] +//│ let f: ([Int, Int]) -> Int +//│ f +//│ = [Function: f5] +:e f(1, 2) -//│ res: int -//│ = 3 +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.168: f(1, 2) +//│ ║ ^^^^^^^ +//│ ╟── argument list of type `[1, 2]` does not match type `[[?a, ?b]]` +//│ ║ l.168: f(1, 2) +//│ ╙── ^^^^^^ +//│ Int | error +//│ res +//│ Runtime error: +//│ TypeError: number 1 is not iterable (cannot read property Symbol(Symbol.iterator)) + +f([1, 2]) +//│ Int +//│ res +//│ = 3 + + +let f = ([x, y]) => x + y +//│ let f: ([Int, Int]) -> Int +//│ f +//│ = [Function: f6] + +f([1, 2]) +//│ Int +//│ res +//│ = 3 + +:e +f(1, 2) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.197: f(1, 2) +//│ ║ ^^^^^^^ +//│ ╟── argument list of type `[1, 2]` does not match type `[[?a, ?b]]` +//│ ║ l.197: f(1, 2) +//│ ╙── ^^^^^^ +//│ Int | error +//│ res +//│ Runtime error: +//│ TypeError: number 1 is not iterable (cannot read property Symbol(Symbol.iterator)) + -// TODO... let f = [[[x, y]]] => x + y -//│ f: (int, int,) -> int -//│ = [Function: f6] +//│ let f: ([[[Int, Int]]]) -> Int +//│ f +//│ = [Function: f7] +:e +f([[1, 2]]) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.216: f([[1, 2]]) +//│ ║ ^^^^^^^^^^^ +//│ ╟── tuple literal of type `[1, 2]` does not match type `[[?a, ?b]]` +//│ ║ l.216: f([[1, 2]]) +//│ ╙── ^^^^^^ +//│ Int | error +//│ res +//│ Runtime error: +//│ TypeError: number 1 is not iterable (cannot read property Symbol(Symbol.iterator)) diff --git a/shared/src/test/diff/nu/MissingImplBug.mls b/shared/src/test/diff/nu/MissingImplBug.mls new file mode 100644 index 0000000000..5be4551c2c --- /dev/null +++ b/shared/src/test/diff/nu/MissingImplBug.mls @@ -0,0 +1,29 @@ +:NewDefs + + +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] + +// * Why do we get below and not above?? + +declare 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} +//│ fun String: nothing +//│ makeString +//│ = [Function: String] +//│ StringInstance +//│ = [Function: String] + + diff --git a/shared/src/test/diff/nu/MissingTypeArg.mls b/shared/src/test/diff/nu/MissingTypeArg.mls new file mode 100644 index 0000000000..1eba54d987 --- /dev/null +++ b/shared/src/test/diff/nu/MissingTypeArg.mls @@ -0,0 +1,94 @@ +// * This is an example program where the error we get is really not ideal + +:NewDefs + + +// * An example recursive definition: + +fun test(pt1, pt2) = pt1.color === pt1.color and + let p1 = pt1.parent + let p2 = pt2.parent + if p1 is undefined then true + else if p2 is undefined then true + else test(p1, p2) +//│ fun test: forall 'a 'b 'c. ('a, 'c) -> Bool +//│ where +//│ 'c <: {parent: Object & 'c & ~() | ()} +//│ 'a <: {color: Eql['b] & 'b, parent: Object & 'a & ~() | ()} + + +// * This works out fine: + +class MyPoint1[Col](val color: Col, val parent: MyPoint1[Col] | undefined) +//│ class MyPoint1[Col](color: Col, parent: MyPoint1[Col] | ()) + +val p = MyPoint1(0, undefined) +//│ val p: MyPoint1['Col] +//│ where +//│ 'Col :> 0 +//│ p +//│ = MyPoint1 {} + +test(p, p) +//│ Bool +//│ res +//│ = true + + +// * BUT... if we forgot to pass the type argument to MyPoint2 (getting a raw/nominal-tag type), +// * the error is not helpful at all: + +class MyPoint2[out Col](val color: Col, val parent: MyPoint2 | undefined) +//│ class MyPoint2[Col](color: Col, parent: MyPoint2[anything] | ()) + +val p = MyPoint2(0, undefined) +//│ val p: MyPoint2[0] +//│ p +//│ = MyPoint2 {} + +:e +test(p, p) +//│ ╔══[ERROR] Type error in application +//│ ║ l.50: test(p, p) +//│ ║ ^^^^^^^^^^ +//│ ╟── type variable `Col` leaks out of its scope +//│ ║ l.41: class MyPoint2[out Col](val color: Col, val parent: MyPoint2 | undefined) +//│ ║ ^^^ +//│ ╟── into field selection of type `#Eql` +//│ ║ l.8: fun test(pt1, pt2) = pt1.color === pt1.color and +//│ ╙── ^^^^^^^^^ +//│ error +//│ res +//│ = true + + + +// TODO[ucs] ideally this should work + +fun test(pt1, pt2) = pt1.color === pt1.color and + let p1 = pt1.parent + let p2 = pt2.parent + if p1 is undefined then p2 is undefined + else test(p1, p2) +//│ fun test: forall 'a 'b 'c. ('a, 'c) -> Bool +//│ where +//│ 'c <: {parent: Object & 'c} +//│ 'a <: {color: Eql['b] & 'b, parent: Object & 'a & ~() | ()} + +:e // TODO support +test(p, p) +//│ ╔══[ERROR] Type error in application +//│ ║ l.79: test(p, p) +//│ ║ ^^^^^^^^^^ +//│ ╟── type variable `Col` leaks out of its scope +//│ ║ l.41: class MyPoint2[out Col](val color: Col, val parent: MyPoint2 | undefined) +//│ ║ ^^^ +//│ ╟── into field selection of type `#Eql` +//│ ║ l.68: fun test(pt1, pt2) = pt1.color === pt1.color and +//│ ╙── ^^^^^^^^^ +//│ error +//│ res +//│ = true + + + diff --git a/shared/src/test/diff/nu/Mixin42.mls b/shared/src/test/diff/nu/Mixin42.mls new file mode 100644 index 0000000000..4bede9b036 --- /dev/null +++ b/shared/src/test/diff/nu/Mixin42.mls @@ -0,0 +1,128 @@ +:NewDefs + + + +mixin M1 { fun test = 21 } +mixin M2 { fun test = super.test * this.factor } +mixin M3 { val factor = 2 } +class C1() extends M1, M2, M3 +C1().test +//│ mixin M1() { +//│ fun test: 21 +//│ } +//│ mixin M2() { +//│ super: {test: Int} +//│ this: {factor: Int} +//│ fun test: Int +//│ } +//│ mixin M3() { +//│ val factor: 2 +//│ } +//│ class C1() { +//│ val factor: 2 +//│ fun test: Int +//│ } +//│ Int +//│ res +//│ = 42 + + + +class C1(val factor: Int) extends M1, M2 +C1(2).test +//│ class C1(factor: Int) { +//│ fun test: Int +//│ } +//│ Int +//│ res +//│ = 42 + + +class C1(val factor: Int) extends M1, M2 +//│ class C1(factor: Int) { +//│ fun test: Int +//│ } + + + +:e +class C1() extends M1, M2 { val factor = 2 } +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.6: mixin M2 { fun test = super.test * this.factor } +//│ ╙── ^^^^^^^ +//│ class C1() { +//│ val factor: 2 +//│ fun test: Int +//│ } + +// * Note that this `val` definition is not yet treated as a type signature/annotation +:e +class C1() extends M1, M2 { val factor: Int = 2 } +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.6: mixin M2 { fun test = super.test * this.factor } +//│ ╙── ^^^^^^^ +//│ class C1() { +//│ val factor: Int +//│ fun test: Int +//│ } + + +abstract class C1 extends M1, M2 { val factor: Int } +module C2 extends C1 { val factor = 2 } +C2.test +//│ abstract class C1 { +//│ val factor: Int +//│ fun test: Int +//│ } +//│ module C2 extends C1 { +//│ val factor: 2 +//│ fun test: Int +//│ } +//│ Int +//│ res +//│ = 42 + + +class C1() extends M1, M2 { val factor: Int; val factor = 2 } +C1().test +//│ class C1() { +//│ val factor: Int +//│ fun test: Int +//│ } +//│ Int +//│ res +//│ = 42 + + +abstract class C0 { val factor = 2 } +//│ abstract class C0 { +//│ val factor: 2 +//│ } + +:e +class C1() extends C0, M1, M2 +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.6: mixin M2 { fun test = super.test * this.factor } +//│ ╙── ^^^^^^^ +//│ class C1() extends C0 { +//│ val factor: 2 +//│ fun test: Int +//│ } + + +abstract class C0 { val factor: Int } +//│ abstract class C0 { +//│ val factor: Int +//│ } + +:e // * TODO support +class C1() extends C0, M1, M2 { val factor = 2 } +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.6: mixin M2 { fun test = super.test * this.factor } +//│ ╙── ^^^^^^^ +//│ class C1() extends C0 { +//│ val factor: 2 +//│ fun test: Int +//│ } + + diff --git a/shared/src/test/diff/nu/MixinParameters.mls b/shared/src/test/diff/nu/MixinParameters.mls new file mode 100644 index 0000000000..b0e1a6320a --- /dev/null +++ b/shared/src/test/diff/nu/MixinParameters.mls @@ -0,0 +1,54 @@ +:NewDefs + + +mixin BaseTest(x: Int) { + fun test = x +} +//│ mixin BaseTest(x: Int) { +//│ fun test: Int +//│ } + +module Test extends BaseTest(42) +//│ module Test { +//│ fun test: Int +//│ } + +Test.test +//│ Int +//│ res +//│ = 42 + +:e +Test.x +//│ ╔══[ERROR] Type `Test` does not contain member `x` +//│ ║ l.22: Test.x +//│ ╙── ^^ +//│ error +//│ res +//│ = undefined + + +mixin BaseTest(val x: Int -> Int) +//│ mixin BaseTest(x: Int -> Int) + +module Test extends BaseTest(id) +//│ module Test + +Test.x(1) +//│ 1 +//│ res +//│ = 1 + + +:e // TODO support +mixin BaseTest(x) { + fun test = x +} +//│ ╔══[ERROR] Mixin parameters currently need type annotations +//│ ║ l.44: mixin BaseTest(x) { +//│ ╙── ^ +//│ mixin BaseTest(x: error) { +//│ fun test: error +//│ } + + diff --git a/shared/src/test/diff/nu/ModuleParameters.mls b/shared/src/test/diff/nu/ModuleParameters.mls new file mode 100644 index 0000000000..f0922ad6a6 --- /dev/null +++ b/shared/src/test/diff/nu/ModuleParameters.mls @@ -0,0 +1,61 @@ +:NewDefs + + +:e +module A(x: Int) { fun y = x } +//│ ╔══[ERROR] Module parameters are not supported +//│ ║ l.5: module A(x: Int) { fun y = x } +//│ ╙── ^ +//│ module A(x: Int) { +//│ fun y: Int +//│ } + +:e +A +//│ ╔══[ERROR] Parameterized modules are not yet supported +//│ ║ l.14: A +//│ ╙── ^ +//│ (x: Int) -> A +//│ res +//│ = A { class: [class A] } + +:e +A(123) +//│ ╔══[ERROR] Parameterized modules are not yet supported +//│ ║ l.23: A(123) +//│ ╙── ^ +//│ A +//│ res +//│ Runtime error: +//│ TypeError: A is not a function + +:e +A.x +//│ ╔══[ERROR] Parameterized modules are not yet supported +//│ ║ l.33: A.x +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in field selection: +//│ ║ l.33: A.x +//│ ║ ^^^ +//│ ╟── reference of type `(x: Int) -> A` does not have field 'x' +//│ ║ l.33: A.x +//│ ╙── ^ +//│ error +//│ res +//│ = undefined + +:e +A.y +//│ ╔══[ERROR] Parameterized modules are not yet supported +//│ ║ l.48: A.y +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in field selection: +//│ ║ l.48: A.y +//│ ║ ^^^ +//│ ╟── reference of type `(x: Int) -> A` does not have field 'y' +//│ ║ l.48: A.y +//│ ╙── ^ +//│ error +//│ res +//│ = undefined + diff --git a/shared/src/test/diff/nu/Mut.mls b/shared/src/test/diff/nu/Mut.mls index 3ad49c8659..c62fdea40c 100644 --- a/shared/src/test/diff/nu/Mut.mls +++ b/shared/src/test/diff/nu/Mut.mls @@ -1,4 +1,4 @@ -:NewParser +:NewDefs :pe @@ -6,117 +6,172 @@ let v1: {mut 1} //│ ╔══[PARSE ERROR] Record field should have a name //│ ║ l.5: let v1: {mut 1} //│ ╙── ^ -//│ v1: {mut : 1} -//│ = +//│ let v1: {mut : 1} +//│ v1 +//│ = -let v1: {mut int} -//│ v1: {mut int: int} -//│ = +:e +let v1: {mut Int} +//│ ╔══[ERROR] Field identifiers must start with a small letter +//│ ║ l.14: let v1: {mut Int} +//│ ╙── ^^^ +//│ let v1: {Int = Int} +//│ v1 +//│ = + +:e +let v1 = {mut Int: 0} +//│ ╔══[ERROR] Field identifiers must start with a small letter +//│ ║ l.23: let v1 = {mut Int: 0} +//│ ╙── ^ +//│ let v1: {Int = 'Int} +//│ where +//│ 'Int :> 0 +//│ v1 +//│ = { Int: 0 } let v1 = {mut int: 0} -//│ {mut int: 'int} +//│ let v1: {mut int: 'int} //│ where //│ 'int :> 0 -//│ <: v1: -//│ {mut int: int} -//│ = { int: 0 } +//│ v1 +//│ = { int: 0 } -let v1: {mut x: int} -//│ v1: {mut x: int} -//│ = +let v1: {mut x: Int} +//│ let v1: {mut x: Int} +//│ v1 +//│ = :pe -:e let v1 = {mut 1} //│ ╔══[PARSE ERROR] Record field should have a name -//│ ║ l.31: let v1 = {mut 1} +//│ ║ l.47: let v1 = {mut 1} //│ ╙── ^ -//│ {mut : '} +//│ let v1: {mut : '} //│ where //│ ' :> 1 -//│ <: v1: -//│ {mut x: int} -//│ ╔══[ERROR] Type mismatch in def definition: -//│ ║ l.31: let v1 = {mut 1} -//│ ║ ^^^^^^^^^^^^ -//│ ╟── record literal of type `{mut : ?}` does not have field 'x' -//│ ║ l.31: let v1 = {mut 1} -//│ ║ ^ -//│ ╟── Note: constraint arises from record type: -//│ ║ l.25: let v1: {mut x: int} -//│ ╙── ^^^^^^^^^^^^ -//│ = { '': 1 } +//│ v1 +//│ = { '': 1 } let v1 = {mut x: 1} -//│ {mut x: 'x} +//│ let v1: {mut x: 'x} //│ where //│ 'x :> 1 -//│ <: v1: -//│ {mut x: int} -//│ = { x: 1 } +//│ v1 +//│ = { x: 1 } // * TODO: support this syntax? :pe v1.x = 1 -//│ ╔══[PARSE ERROR] Expected end of input; found '=' keyword instead -//│ ║ l.61: v1.x = 1 +//│ ╔══[PARSE ERROR] Expected end of input; found '=' instead +//│ ║ l.66: v1.x = 1 //│ ╙── ^ -//│ res: int -//│ = 1 +//│ 1 +//│ res +//│ = 1 // * TODO: support this syntax? :e :ng v1.x <- 1 //│ ╔══[ERROR] identifier not found: <- -//│ ║ l.71: v1.x <- 1 +//│ ║ l.77: v1.x <- 1 //│ ╙── ^^ -//│ res: error +//│ error + + +:pe +let v2: (mut Int) +//│ ╔══[PARSE ERROR] Illegal position for field specification +//│ ║ l.85: let v2: (mut Int) +//│ ╙── ^^^ +//│ let v2: Int +//│ v2 +//│ = +:pe +let v2 = (mut 1) +//│ ╔══[PARSE ERROR] Illegal position for field specification +//│ ║ l.94: let v2 = (mut 1) +//│ ╙── ^ +//│ let v2: 1 +//│ v2 +//│ = 1 -let v2: (mut int) -//│ v2: (mut int,) -//│ = +:pe +let v2: (mut x: Int) +//│ ╔══[PARSE ERROR] Illegal position for field specification +//│ ║ l.103: let v2: (mut x: Int) +//│ ╙── ^^^^^^ +//│ let v2: Int +//│ v2 +//│ = +:pe let v2 = (mut 1) -//│ (mut 'a,) +//│ ╔══[PARSE ERROR] Illegal position for field specification +//│ ║ l.112: let v2 = (mut 1) +//│ ╙── ^ +//│ let v2: 1 +//│ v2 +//│ = 1 + +:pe +let v2 = (mut x: 1) +//│ ╔══[PARSE ERROR] Illegal position for field specification +//│ ║ l.121: let v2 = (mut x: 1) +//│ ╙── ^^^^ +//│ let v2: 1 +//│ v2 +//│ = 1 + +:pe +let v2 = (mut y: 1) +//│ ╔══[PARSE ERROR] Illegal position for field specification +//│ ║ l.130: let v2 = (mut y: 1) +//│ ╙── ^^^^ +//│ let v2: 1 +//│ v2 +//│ = 1 + + +let v2: [mut Int] +//│ let v2: [mut Int] +//│ v2 +//│ = + +let v2 = [mut 1] +//│ let v2: [mut 'a] //│ where //│ 'a :> 1 -//│ <: v2: -//│ (mut int,) -//│ = [ 1 ] +//│ v2 +//│ = [ 1 ] -let v2: (mut x: int) -//│ v2: (mut x: int,) -//│ = +let v2: [mut x: Int] +//│ let v2: [mut x: Int] +//│ v2 +//│ = -let v2 = (mut 1) -//│ (mut 'a,) +let v2 = [mut 1] +//│ let v2: [mut 'a] //│ where //│ 'a :> 1 -//│ <: v2: -//│ (mut x: int,) -//│ = [ 1 ] +//│ v2 +//│ = [ 1 ] -let v2 = (mut x: 1) -//│ (mut x: 'x,) +let v2 = [mut x: 1] +//│ let v2: [mut x: 'x] //│ where //│ 'x :> 1 -//│ <: v2: -//│ (mut x: int,) -//│ = [ 1 ] +//│ v2 +//│ = [ 1 ] -:e -let v2 = (mut y: 1) -//│ (mut y: 'y,) +let v2 = [mut y: 1] +//│ let v2: [mut y: 'y] //│ where //│ 'y :> 1 -//│ <: v2: -//│ (mut x: int,) -//│ ╔══[ERROR] Wrong tuple field name: found 'y' instead of 'x' -//│ ║ l.111: let v2 = (mut y: 1) -//│ ╙── ^ -//│ = [ 1 ] +//│ v2 +//│ = [ 1 ] diff --git a/shared/src/test/diff/nu/MutualRec.mls b/shared/src/test/diff/nu/MutualRec.mls new file mode 100644 index 0000000000..4d4b406614 --- /dev/null +++ b/shared/src/test/diff/nu/MutualRec.mls @@ -0,0 +1,364 @@ +:NewDefs + + + +class Foo() +123 +//│ class Foo() +//│ 123 +//│ res +//│ = 123 + +Foo +//│ () -> Foo +//│ res +//│ = [Function (anonymous)] { +//│ class: [class Foo], +//│ unapply: [Function: unapply] +//│ } + +// TODO +fun fooo(x) = + class C(y, z) + C(0, x) +//│ ╔══[ERROR] Class parameters currently need type annotations +//│ ║ l.22: class C(y, z) +//│ ╙── ^ +//│ ╔══[ERROR] Class parameters currently need type annotations +//│ ║ l.22: class C(y, z) +//│ ╙── ^ +//│ fun fooo: error -> C + + + +fun foo = bar +fun bar = foo +//│ fun foo: nothing +//│ fun bar: nothing + +:re +foo(bar) +//│ nothing +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + + +fun foo = {x: foo} +//│ fun foo: forall 'foo. 'foo +//│ where +//│ 'foo :> {x: 'foo} + + +fun foo = {x: bar} +fun bar = {y: foo} +//│ fun foo: forall 'foo. 'foo +//│ fun bar: forall 'foo. {y: 'foo} +//│ where +//│ 'foo :> {x: {y: 'foo}} + +:re +foo +//│ forall 'foo. 'foo +//│ where +//│ 'foo :> {x: {y: 'foo}} +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +:ns +:re +foo +//│ forall 'foo. {x: {y: 'foo}} +//│ where +//│ 'foo :> {x: {y: 'foo}} +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +:re +foo.x +//│ {y: 'foo} +//│ where +//│ 'foo :> {x: {y: 'foo}} +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +:re +foo.x.y +//│ 'foo +//│ where +//│ 'foo :> {x: {y: 'foo}} +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + + +fun foo(a) = {h: a, t: bar(a)} +fun bar(b) = foo(b) +//│ fun foo: forall 'a 'b. 'a -> 'b +//│ fun bar: forall 'a 'b. 'a -> 'b +//│ where +//│ 'b :> {h: 'a, t: 'b} + +:ns +foo +//│ forall 'a 'b 'c. 'a -> {h: 'a, t: 'c} +//│ where +//│ 'c :> {h: 'a, t: 'c} +//│ 'a <: 'b +//│ 'b <: 'a +//│ res +//│ = [Function: foo3] + + +fun foo(a) = {h1: a, t1: bar(a)} +fun bar(b) = {h2: b, t2: foo(b)} +//│ fun foo: forall 'a 'b 'c. 'c -> {h1: 'c, t1: 'a} +//│ fun bar: forall 'b 'c. 'c -> {h2: 'c, t2: 'b} +//│ where +//│ 'a :> {h2: 'c, t2: 'b} +//│ 'b :> {h1: 'c, t1: 'a} + + + +module Test0_1 { + fun a = Test0_2.b +} +module Test0_2 { + fun b = 123 +} +//│ module Test0_1 { +//│ fun a: 123 +//│ } +//│ module Test0_2 { +//│ fun b: 123 +//│ } + +Test0_1.a +//│ 123 +//│ res +//│ = 123 + +class Test0_1 { + fun a = Test0_2().b +} +class Test0_2() { + fun b = 123 +} +//│ class Test0_1 { +//│ constructor() +//│ fun a: 123 +//│ } +//│ class Test0_2() { +//│ fun b: 123 +//│ } + + +:e +module Test1_1 { + fun a = Test1_2.b +} +module Test1_2 { + fun b = Test1_1.a +} +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.164: fun b = Test1_1.a +//│ ╙── ^^ +//│ module Test1_1 { +//│ fun a: error +//│ } +//│ module Test1_2 { +//│ fun b: error +//│ } + +:re +Test1_1.a +//│ error +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + + +:e +class Test1_1 { + fun a = Test1_2().b +} +class Test1_2 { + fun b = Test1_1().a +} +//│ ╔══[ERROR] Class Test1_2 cannot be instantiated as it exposes no constructor +//│ ║ l.186: fun a = Test1_2().b +//│ ╙── ^^^^^^^ +//│ ╔══[ERROR] Class Test1_1 cannot be instantiated as it exposes no constructor +//│ ║ l.189: fun b = Test1_1().a +//│ ╙── ^^^^^^^ +//│ class Test1_1 { +//│ constructor() +//│ fun a: error +//│ } +//│ class Test1_2 { +//│ constructor() +//│ fun b: error +//│ } + +:e +class Test1_1() { + fun a = Test1_2().b +} +class Test1_2() { + fun b = Test1_1().a +} +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.211: fun b = Test1_1().a +//│ ╙── ^^ +//│ class Test1_1() { +//│ fun a: error +//│ } +//│ class Test1_2() { +//│ fun b: error +//│ } + +:e +class Test1_1 { + fun a = (new Test1_2).b +} +class Test1_2 { + fun b = (new Test1_1).a +} +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.228: fun b = (new Test1_1).a +//│ ╙── ^^ +//│ class Test1_1 { +//│ constructor() +//│ fun a: error +//│ } +//│ class Test1_2 { +//│ constructor() +//│ fun b: error +//│ } + +class Test1_1 { + fun a: Int + fun a = (new Test1_2).b +} +class Test1_2 { + fun b = (new Test1_1).a +} +//│ class Test1_1 { +//│ constructor() +//│ fun a: Int +//│ } +//│ class Test1_2 { +//│ constructor() +//│ fun b: Int +//│ } + + +:e +module Test2_1 { + fun t2 = Test2_2 + fun a = Test2_2.b + fun d = Test2_2.e + fun n = 456 +} +module Test2_2 { + fun b = 123 + fun c = Test2_1.a + fun e = Test2_1.n +} +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.268: fun c = Test2_1.a +//│ ╙── ^^ +//│ module Test2_1 { +//│ fun a: 123 | error +//│ fun d: error +//│ fun n: 456 +//│ fun t2: Test2_2 +//│ } +//│ module Test2_2 { +//│ fun b: 123 +//│ fun c: error +//│ fun e: error +//│ } + +Test2_1.t2.b +//│ 123 +//│ res +//│ = 123 + +Test2_1.a +//│ 123 | error +//│ res +//│ = 123 + +Test2_1.d +//│ error +//│ res +//│ = 456 + +Test2_1.n +//│ 456 +//│ res +//│ = 456 + +module Test2_1 { + fun t2 = Test2_2 + fun a: Int + fun a = Test2_2.b + fun d = Test2_2.e + fun n: Int + fun n = 456 +} +module Test2_2 { + fun b = 123 + fun c = Test2_1.a + fun e = Test2_1.n +} +//│ module Test2_1 { +//│ fun a: Int +//│ fun d: Int +//│ fun n: Int +//│ fun t2: Test2_2 +//│ } +//│ module Test2_2 { +//│ fun b: 123 +//│ fun c: Int +//│ fun e: Int +//│ } + +Test2_1.t2.b +//│ 123 +//│ res +//│ = 123 + +Test2_1.a +//│ Int +//│ res +//│ = 123 + +Test2_1.d +//│ Int +//│ res +//│ = 456 + +Test2_1.n +//│ Int +//│ res +//│ = 456 + + +class Test2(val n: Int) { + fun inc = Test3.inc(this) +} +module Test3 { + fun inc(t: Test2) = Test2(t.n + 1) +} +//│ class Test2(n: Int) { +//│ fun inc: Test2 +//│ } +//│ module Test3 { +//│ fun inc: (t: Test2) -> Test2 +//│ } + diff --git a/shared/src/test/diff/nu/NamedArgs.mls b/shared/src/test/diff/nu/NamedArgs.mls index 54976317b3..8c4d4606f1 100644 --- a/shared/src/test/diff/nu/NamedArgs.mls +++ b/shared/src/test/diff/nu/NamedArgs.mls @@ -1,48 +1,433 @@ -:NewParser +:NewDefs -:w -class Foo(x: int) -//│ Defined class Foo -//│ ╔══[WARNING] Variable name 'int' already names a symbol in scope. If you want to refer to that symbol, you can use `scope.int`; if not, give your future readers a break and use another name :^) -//│ ║ l.5: class Foo(x: int) -//│ ╙── ^^^ -//│ Foo: (x: int & 'x,) -> (Foo with {x: 'x}) -//│ = [Function: Foo1] +fun test(x: 'a) = if x is undefined then 0 else x + 1 +//│ fun test: (x: Int | ()) -> Int -Foo(1) -//│ res: Foo & {x: 1} -//│ = Foo { x: 1 } +test(x: 0) +//│ Int +//│ res +//│ = 1 -Foo(x: 1) -//│ res: Foo & {x: 1} -//│ = Foo { x: 1 } +:e +test(x: 0, 1) +//│ ╔══[ERROR] Unnamed arguments should appear first when using named arguments +//│ ║ l.13: test(x: 0, 1) +//│ ╙── ^^^^^^^^^ +//│ error +//│ res +//│ = 1 + +:e +test(y: 0) +//│ ╔══[ERROR] Argument named 'x' is missing from this function call +//│ ║ l.22: test(y: 0) +//│ ╙── ^^^^^^ +//│ Int +//│ res +//│ Runtime error: +//│ Error: an error was thrown + + +fun test(x: 'a, y: 'b) = [x, y] +//│ fun test: forall 'a 'b. (x: 'a, y: 'b) -> ['a, 'b] + +:e +test(x: 1, 2) +//│ ╔══[ERROR] Unnamed arguments should appear first when using named arguments +//│ ║ l.36: test(x: 1, 2) +//│ ╙── ^^^^^^^^^ +//│ error +//│ res +//│ = [ 1, 2 ] + +:e +test(x: 1, z: 3) +//│ ╔══[ERROR] Argument named 'y' is missing from this function call +//│ ║ l.45: test(x: 1, z: 3) +//│ ╙── ^^^^^^^^^^^^ +//│ [1, nothing] +//│ res +//│ Runtime error: +//│ Error: an error was thrown + +:e +test(y: 0) +//│ ╔══[ERROR] Number of arguments doesn't match function signature `forall 'a 'b. (x: 'a, y: 'b) -> ['a, 'b]` +//│ ║ l.55: test(y: 0) +//│ ╙── ^^^^^^ +//│ error +//│ res +//│ = [ 0, undefined ] + +:e +test(1, x: 0) +//│ ╔══[ERROR] Argument for parameter 'x' is duplicated +//│ ║ l.64: test(1, x: 0) +//│ ╙── ^^^^^^^^^ +//│ ╔══[ERROR] Argument named 'y' is missing from this function call +//│ ║ l.64: test(1, x: 0) +//│ ╙── ^^^^^^^^^ +//│ [0, nothing] +//│ res +//│ Runtime error: +//│ Error: an error was thrown + +// * Notice no let binding is generated for the first argument +:js +test(0, y: 1) +//│ [0, 1] +//│ // Prelude +//│ class TypingUnit9 {} +//│ const typing_unit9 = new TypingUnit9; +//│ // Query 1 +//│ res = test1(0, 1); +//│ // End of generated code +//│ res +//│ = [ 0, 1 ] + +id(test)(0, y: 1) +//│ [0, 1] +//│ res +//│ = [ 0, 1 ] + +id(if true then test else error)(0, y: 1) +//│ [0, 1] +//│ res +//│ = [ 0, 1 ] + +:e +id(if true then test else id)(0, y: 1) +//│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments +//│ ║ l.100: id(if true then test else id)(0, y: 1) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ error +//│ res +//│ = [ 0, 1 ] + +// * No let binding in that value of argument is a var or literal +:js +let tmp = 2 +test(0, y: tmp) +test(0, y: 200) +//│ let tmp: 2 +//│ [0, 200] +//│ // Prelude +//│ class TypingUnit13 {} +//│ const typing_unit13 = new TypingUnit13; +//│ // Query 1 +//│ globalThis.tmp = 2; +//│ // Query 2 +//│ res = test1(0, tmp); +//│ // Query 3 +//│ res = test1(0, 200); +//│ // End of generated code +//│ tmp +//│ = 2 +//│ res +//│ = [ 0, 2 ] +//│ res +//│ = [ 0, 200 ] + +:js +test(0, y: 1 + 2) +//│ [0, Int] +//│ // Prelude +//│ class TypingUnit14 {} +//│ const typing_unit14 = new TypingUnit14; +//│ // Query 1 +//│ res = ((y_1) => test1(0, y_1))(1 + 2); +//│ // End of generated code +//│ res +//│ = [ 0, 3 ] + + + +fun fff(x: Int, y: Int, z: Int) = (x - y) * z +//│ fun fff: (x: Int, y: Int, z: Int) -> Int + +// * Testing renaming :e -Foo(y: 1) -//│ ╔══[ERROR] Wrong tuple field name: found 'y' instead of 'x' -//│ ║ l.22: Foo(y: 1) -//│ ╙── ^^^^^^ -//│ res: error | Foo & {x: 1} -//│ = Foo { x: 1 } +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) +//│ ╙── ^^^ +//│ ╔══[ERROR] identifier not found: z_1 +//│ ║ l.152: fff(y: 2, z: y_1 + 1, x: z_1 - 2) +//│ ╙── ^^^ +//│ Int +//│ Code generation encountered an error: +//│ unresolved symbol z_1 + +:js +let y_1 = 2 +let z_1 = 3 +fff(y: 2, z: y_1 + 1, x: z_1 - 2) +//│ let y_1: 2 +//│ let z_1: 3 +//│ Int +//│ // Prelude +//│ class TypingUnit17 {} +//│ const typing_unit17 = new TypingUnit17; +//│ // Query 1 +//│ globalThis["y_1"] = 2; +//│ // Query 2 +//│ globalThis["z_1"] = 3; +//│ // Query 3 +//│ res = ((z_2) => ((x_1) => fff(x_1, 2, z_2))(z_1 - 2))(y_1 + 1); +//│ // End of generated code +//│ y_1 +//│ = 2 +//│ z_1 +//│ = 3 +//│ res +//│ = -3 + + +class A() { + fun ma(x: Int, y: Int) = x - y + fun mma(x: Int, y: Int) = y - x +} +//│ class A() { +//│ fun ma: (x: Int, y: Int) -> Int +//│ fun mma: (x: Int, y: Int) -> Int +//│ } + +let x = A() +x.ma(y: 2, x: 1) +//│ let x: A +//│ Int +//│ x +//│ = A {} +//│ res +//│ = -1 + +A().ma(x: 1, y: 2) +//│ Int +//│ res +//│ = -1 + +id(x).ma(y: 2, x: 1) +//│ Int +//│ res +//│ = -1 + + +fun print(x: Int) = (y: Int, z: Int) => log([x, y, z]) +let p = print(0) +//│ fun print: (x: Int) -> (y: Int, z: Int) -> () +//│ let p: (y: Int, z: Int) -> () +//│ p +//│ = [Function (anonymous)] + +p(z: 1, y: 2) +//│ () +//│ res +//│ = undefined +//│ // Output +//│ [ 0, 2, 1 ] + +:e +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) +//│ ╙── ^^^^^^^^^^^^ +//│ fun print: anything -> (anything, anything) -> () +//│ let p: (anything, anything) -> () +//│ error +//│ p +//│ = [Function (anonymous)] +//│ res +//│ = undefined +//│ // Output +//│ [ 0, 1, 2 ] + + +class Baz() { + fun f(x: Int, y: Int) = log([x, y]) +} +Baz().f(y: 1, x: 2) +//│ class Baz() { +//│ fun f: (x: Int, y: Int) -> () +//│ } +//│ () +//│ res +//│ = undefined +//│ // Output +//│ [ 2, 1 ] + +let b = Baz() +b.f(y: 1, x: 2) +//│ let b: Baz +//│ () +//│ b +//│ = Baz {} +//│ res +//│ = undefined +//│ // Output +//│ [ 2, 1 ] -// TODO: Here `x` is not currently treated as a field name -class Bar(x) -//│ Defined class Bar -//│ Bar: 'x -> (Bar & {x: 'x}) -//│ = [Function: Bar1] +class A(val x: Int, val y: Int) +//│ class A(x: Int, y: Int) + +let z = A(y: 2, x: 1) +z.x +z.y +//│ let z: A +//│ Int +//│ z +//│ = A {} +//│ res +//│ = 1 +//│ res +//│ = 2 + +:e +(f => f(x: a)) +//│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments +//│ ║ l.290: (f => f(x: a)) +//│ ╙── ^ +//│ anything -> error +//│ Code generation encountered an error: +//│ unresolved symbol a + +:e +(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) +//│ ╙── ^^^^^^^^^^^^^^^ +//│ error +//│ Code generation encountered an error: +//│ unresolved symbol a + +:e +(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) +//│ ╙── ^^^^^^^^^^^^ +//│ error +//│ Code generation encountered an error: +//│ unresolved symbol a + +:e +(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) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ error +//│ Code generation encountered an error: +//│ unresolved symbol a + +(f => f)(if true then (x: Int) => x + 1 else error)(x: 123) +//│ Int +//│ res +//│ = 124 + +: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) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ error +//│ res +//│ = 124 + +: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) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ error +//│ res +//│ = 124 + + +fun foo(f: (x: Int) => Int) = f(x: 123) +//│ fun foo: (f: (x: Int) -> Int) -> Int + +fun foo(f: ((x: Int) => Int) & 'a) = [f(x: 123), f] +//│ fun foo: forall 'a 'b. (f: (x: Int) -> Int & 123 -> 'b & 'a) -> ['b, (x: Int) -> Int & 'a] + +foo((x: Int) => 1) +//│ [1, (x: Int) -> 1] +//│ res +//│ = [ 1, [Function (anonymous)] ] + + +: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) +//│ ╙── ^ +//│ fun foo: (f: anything) -> error + + +// * the result of the if-then-else is a TV with two LBs: the type of x and the function type +: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) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ fun foo: anything -> error + +foo((y: Int) => y) +//│ error +//│ res +//│ = 124 + + +: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) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ fun foo: anything -> error + + +fun foo(x) = if true then (x: Int) => x + 1 else x +//│ fun foo: forall 'a. 'a -> ((x: Int) -> Int | 'a) + +: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) +//│ ╙── ^^^^^^^^^^^^^^^^^^ +//│ error +//│ res +//│ = 124 + +: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) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ +//│ error +//│ res +//│ = 124 + + +fun foo1(x) = [x + 1, x] +//│ fun foo1: forall 'a. (Int & 'a) -> [Int, 'a] + +:e +foo1(x: 123) +//│ ╔══[ERROR] Cannot use named arguments as the function type has untyped arguments +//│ ║ l.417: foo1(x: 123) +//│ ╙── ^^^^^^^^ +//│ error +//│ res +//│ = [ 124, 123 ] -Bar(1) -//│ res: Bar & {x: 1} -//│ = Bar { x: 1 } -Bar(x: 1) -//│ res: Bar & {x: 1} -//│ = Bar { x: 1 } +fun foo1(x: Int & 'a) = [x + 1, x] +//│ fun foo1: forall 'a. (x: Int & 'a) -> [Int, Int & 'a] -// :e -Bar(y: 1) -//│ res: Bar & {x: 1} -//│ = Bar { x: 1 } +foo1(x: 123) +//│ [Int, 123] +//│ res +//│ = [ 124, 123 ] diff --git a/shared/src/test/diff/nu/NestedClasses.mls b/shared/src/test/diff/nu/NestedClasses.mls new file mode 100644 index 0000000000..d712fc8db9 --- /dev/null +++ b/shared/src/test/diff/nu/NestedClasses.mls @@ -0,0 +1,63 @@ +:NewDefs + + +class C0() { + class NC0() +} +//│ class C0() { +//│ class NC0() +//│ } + +let c = C0() +//│ let c: C0 +//│ c +//│ = C0 {} + +:e +c.NC0 +//│ ╔══[ERROR] Access to class member not yet supported +//│ ║ l.17: c.NC0 +//│ ╙── ^^^^ +//│ error +//│ res +//│ = [Function (anonymous)] { +//│ class: [class NC0], +//│ unapply: [Function: unapply] +//│ } + + +module M0 { + class NC0 +} +//│ module M0 { +//│ class NC0 { +//│ constructor() +//│ } +//│ } + +:e +M0.NC0 +//│ ╔══[ERROR] Access to class member not yet supported +//│ ║ l.39: M0.NC0 +//│ ╙── ^^^^ +//│ error +//│ res +//│ = [class NC0] + + +module M1 { + module NM1 +} +//│ module M1 { +//│ module NM1 +//│ } + +:e +M1.NM1 +//│ ╔══[ERROR] Access to module member not yet supported +//│ ║ l.56: M1.NM1 +//│ ╙── ^^^^ +//│ error +//│ res +//│ = NM1 { class: [class NM1] } + diff --git a/shared/src/test/diff/nu/NestedRecords.mls b/shared/src/test/diff/nu/NestedRecords.mls new file mode 100644 index 0000000000..ebe7bbcbd6 --- /dev/null +++ b/shared/src/test/diff/nu/NestedRecords.mls @@ -0,0 +1,45 @@ +:NewDefs + + +let f(x) = [x, x] +//│ let f: forall 'a. 'a -> ['a, 'a] +//│ f +//│ = [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]]]} +//│ a +//│ = { +//│ u: [ [ [Array], [Array] ], [ [Array], [Array] ] ], +//│ v: [ [ [Array], [Array] ], [ [Array], [Array] ] ] +//│ } + +{ a } +//│ { +//│ a: {u: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]], v: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]]} +//│ } +//│ res +//│ = { a: { u: [ [Array], [Array] ], v: [ [Array], [Array] ] } } + +{ a, b: a } +//│ { +//│ a: {u: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]], v: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]]}, +//│ b: {u: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]], v: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]]} +//│ } +//│ res +//│ = { +//│ a: { u: [ [Array], [Array] ], v: [ [Array], [Array] ] }, +//│ b: { u: [ [Array], [Array] ], v: [ [Array], [Array] ] } +//│ } + +{ x: { a, b: a } } +//│ { +//│ x: { +//│ a: {u: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]], v: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]]}, +//│ b: {u: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]], v: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]]} +//│ } +//│ } +//│ res +//│ = { x: { a: { u: [Array], v: [Array] }, b: { u: [Array], v: [Array] } } } + + diff --git a/shared/src/test/diff/nu/New.mls b/shared/src/test/diff/nu/New.mls index bcddd0e045..a145d88fec 100644 --- a/shared/src/test/diff/nu/New.mls +++ b/shared/src/test/diff/nu/New.mls @@ -10,9 +10,7 @@ let f = Foo(1) //│ f: Foo & {x: 1} //│ = Foo { x: 1 } -let f = new Foo(1) -//│ f: Foo & {x: 1} -//│ = Foo { x: 1 } +// let f = new Foo(1) if f is Foo then 1 else 0 //│ res: 0 | 1 @@ -40,11 +38,11 @@ test(f) //│ res: 1 //│ = 1 -class Point(x, y) -//│ Defined class Point -//│ Point: ('x, 'y,) -> (Point & {x: 'x, y: 'y}) -//│ = [Function: Point1] +class PoInt(x, y) +//│ Defined class PoInt +//│ PoInt: ('x, 'y,) -> (PoInt & {x: 'x, y: 'y}) +//│ = [Function: PoInt1] + +// let origin = new PoInt(0, 0) + -let origin = new Point(0, 0) -//│ origin: Point & {x: 0, y: 0} -//│ = Point { x: 0, y: 0 } diff --git a/shared/src/test/diff/nu/NewNew.mls b/shared/src/test/diff/nu/NewNew.mls new file mode 100644 index 0000000000..a2e039d7c3 --- /dev/null +++ b/shared/src/test/diff/nu/NewNew.mls @@ -0,0 +1,309 @@ +:NewDefs + + +class Foo(val x: Int) +//│ class Foo(x: Int) + +let f = Foo(1) +//│ let f: Foo +//│ f +//│ = Foo {} + +f.x +//│ Int +//│ res +//│ = 1 + +let f = new Foo(1) +//│ let f: Foo +//│ f +//│ = Foo {} + +f.x +//│ Int +//│ res +//│ = 1 + +new Foo(1).x +//│ Int +//│ res +//│ = 1 + +if f is Foo then 1 else 0 +//│ 0 | 1 +//│ res +//│ = 1 + +if f is Foo(a) then a else 0 +//│ Int +//│ res +//│ = 1 + +let f = Foo +//│ let f: (x: Int) -> Foo +//│ f +//│ = [Function (anonymous)] { +//│ class: [class Foo], +//│ unapply: [Function: unapply] +//│ } + +f(1) +//│ Foo +//│ res +//│ = Foo {} + + +class Foo { + constructor(x: Int){} + val y = 2 +} +//│ class Foo { +//│ constructor(x: Int) +//│ val y: 2 +//│ } + +new Foo(1).y +//│ 2 +//│ res +//│ = 2 + +:e +let f = Foo(1) +//│ ╔══[ERROR] Construction of unparameterized class Foo should use the `new` keyword +//│ ║ l.71: let f = Foo(1) +//│ ╙── ^^^ +//│ let f: Foo +//│ f +//│ Runtime error: +//│ TypeError: Class constructor Foo cannot be invoked without 'new' + +:e +let f = new Foo +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.81: let f = new Foo +//│ ║ ^^^ +//│ ╟── argument list of type `[]` does not match type `[x: Int]` +//│ ║ l.81: let f = new Foo +//│ ╙── ^ +//│ let f: Foo | error +//│ f +//│ = Foo {} + +:e +f(1) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.93: f(1) +//│ ║ ^^^^ +//│ ╟── application of type `Foo` is not a function +//│ ║ l.81: let f = new Foo +//│ ║ ^^^ +//│ ╟── but it flows into reference with expected type `1 -> ?a` +//│ ║ l.93: f(1) +//│ ╙── ^ +//│ error +//│ res +//│ Runtime error: +//│ TypeError: f4 is not a function + +:e +new Foo("2") +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.109: new Foo("2") +//│ ║ ^^^^^^^^^^^^ +//│ ╟── string literal of type `"2"` is not an instance of type `Int` +//│ ║ l.109: new Foo("2") +//│ ║ ^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.57: constructor(x: Int){} +//│ ╙── ^^^ +//│ Foo | error +//│ res +//│ = Foo {} + +:e +new Foo() +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.124: new Foo() +//│ ║ ^^^^^^^^^ +//│ ╟── argument list of type `[]` does not match type `[x: Int]` +//│ ║ l.124: new Foo() +//│ ╙── ^^ +//│ Foo | error +//│ res +//│ = Foo {} + + + +class PoInt[out A](x: A, y: A) +//│ class PoInt[A](x: A, y: A) + +new PoInt(1, 2) +//│ PoInt[1 | 2] +//│ res +//│ = PoInt {} + +:e +new PoInt +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.146: new PoInt +//│ ║ ^^^^^ +//│ ╟── argument list of type `[]` does not match type `[x: ?A, y: ?A]` +//│ ║ l.146: new PoInt +//│ ╙── ^ +//│ PoInt[nothing] | error +//│ res +//│ = PoInt {} + +:e +new PoInt['_] +//│ ╔══[ERROR] Type arguments in `new` expressions are not yet supported +//│ ║ l.158: new PoInt['_] +//│ ╙── ^^^^^^^^^ +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.158: new PoInt['_] +//│ ║ ^^^^^^^^^ +//│ ╟── argument list of type `[]` does not match type `[x: ?A, y: ?A]` +//│ ║ l.158: new PoInt['_] +//│ ╙── ^ +//│ PoInt[nothing] | error +//│ Code generation encountered an error: +//│ Unsupported `new` class term: TyApp(Var(PoInt),List('_)) + +:e +new PoInt[Str](0, 0) +//│ ╔══[ERROR] Type arguments in `new` expressions are not yet supported +//│ ║ l.173: new PoInt[Str](0, 0) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^ +//│ PoInt[0] +//│ Code generation encountered an error: +//│ Unsupported `new` class term: TyApp(Var(PoInt),List(TypeName(Str))) + +type T = PoInt[Str] +//│ type T = PoInt[Str] + +:e +new T(0, 0) +//│ ╔══[ERROR] Type alias T cannot be used in `new` expression +//│ ║ l.185: new T(0, 0) +//│ ╙── ^^^^^^^^^^^ +//│ error +//│ Code generation encountered an error: +//│ type alias T is not a valid expression + +let origin = new PoInt(0, 0) +//│ let origin: PoInt[0] +//│ origin +//│ = PoInt {} + +: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) +//│ ╙── ^^^^^^^^^^ +//│ let origin: PoInt[0] +//│ origin +//│ = PoInt {} + +: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) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^ +//│ let origin: PoInt[0] +//│ Code generation encountered an error: +//│ Unsupported `new` class term: TyApp(Var(PoInt),List(TypeName(Int))) + + +:e // TODO support +new {} +//│ ╔══[ERROR] Unexpected type `anything` after `new` keyword +//│ ║ l.218: new {} +//│ ╙── ^^ +//│ error +//│ Code generation encountered an error: +//│ Unsupported `new` class term: Bra(true,Rcd(List())) + +:pe +:e +new +//│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here +//│ ║ l.228: new +//│ ╙── ^ +//│ ╔══[ERROR] Unexpected type `()` after `new` keyword +//│ ║ l.228: new +//│ ╙── ^ +//│ error +//│ Code generation encountered an error: +//│ Unsupported `new` class term: UnitLit(true) + +// Support? +:pe +:e +new + x: 0 +//│ ╔══[PARSE ERROR] Not a recognized type +//│ ║ l.243: x: 0 +//│ ╙── ^ +//│ ╔══[ERROR] Unexpected type `nothing` after `new` keyword +//│ ║ l.243: x: 0 +//│ ╙── ^ +//│ error +//│ Code generation encountered an error: +//│ Unsupported `new` class term: Blk(List(Asc(Var(x),Literal(IntLit(0))))) + + + + +fun f(x) = {x} +//│ fun f: forall 'a. 'a -> {x: 'a} + +:e +new f(1) +//│ ╔══[ERROR] type identifier not found: f +//│ ║ l.261: new f(1) +//│ ╙── ^ +//│ error +//│ res +//│ = { x: 1 } + + +module Oops +//│ module Oops + +:e +new Oops +//│ ╔══[ERROR] Module Oops cannot be used in `new` expression +//│ ║ l.274: new Oops +//│ ╙── ^^^^ +//│ error +//│ res +//│ = Oops {} + + +:e +new Oops2 +trait Oops2 +//│ ╔══[ERROR] Trait Oops2 cannot be used in `new` expression +//│ ║ l.284: new Oops2 +//│ ╙── ^^^^^ +//│ trait Oops2 +//│ error +//│ Code generation encountered an error: +//│ trait used in term position + + + +module A { class B } +//│ module A { +//│ class B { +//│ constructor() +//│ } +//│ } + +:ge // TODO support +new A.B +//│ B +//│ Code generation encountered an error: +//│ Unsupported `new` class term: Sel(Var(A),Var(B)) + + diff --git a/shared/src/test/diff/nu/NoThisCtor.mls b/shared/src/test/diff/nu/NoThisCtor.mls new file mode 100644 index 0000000000..4ed1d59bcf --- /dev/null +++ b/shared/src/test/diff/nu/NoThisCtor.mls @@ -0,0 +1,159 @@ +:NewDefs + +class Base(val base: Int) { + fun getBase1 = base + fun getBase2 = this.base + fun foo(x) = this.base + x +} +//│ class Base(base: Int) { +//│ fun foo: Int -> Int +//│ fun getBase1: Int +//│ fun getBase2: Int +//│ } + +class Foo() { + virtual val foo: Int = 42 +} +//│ class Foo() { +//│ val foo: Int +//│ } + +:e +class Foo1() extends Foo() { + val foo: Int + virtual val foo = 1 + log(this.foo) +} +//│ ╔══[ERROR] Cannot access `this` during object initialization +//│ ║ l.25: log(this.foo) +//│ ╙── ^^^^ +//│ class Foo1() extends Foo { +//│ val foo: Int +//│ } + +:e +class Foo2() extends Foo() { + virtual val foo: Int + val foo = 2 + constructor() { + log(this.foo) + } +} +//│ ╔══[ERROR] Cannot access `this` during object initialization +//│ ║ l.39: log(this.foo) +//│ ╙── ^^^^ +//│ ╔══[ERROR] identifier not found: this +//│ ║ l.39: log(this.foo) +//│ ╙── ^^^^ +//│ class Foo2() extends Foo { +//│ constructor() +//│ val foo: Int +//│ } + +:e +class Foo3() extends Foo() { + val foo: Int + virtual val foo = 3 + val s = this.foo +} +//│ ╔══[ERROR] Cannot access `this` while initializing field s +//│ ║ l.57: val s = this.foo +//│ ╙── ^^^^ +//│ class Foo3() extends Foo { +//│ val foo: Int +//│ val s: Int +//│ } + +:e +class Foo4() extends Foo() { + val foo: Int + virtual val foo = 4 + fun bar(x) = this.foo + x // ok + let bb = bar(0) // call `this` indirectly +} +//│ ╔══[ERROR] Cannot access `this` while initializing field bb +//│ ║ l.72: let bb = bar(0) // call `this` indirectly +//│ ║ ^^^^^^^^^^^ +//│ ╟── The access to `this` is here +//│ ║ l.71: fun bar(x) = this.foo + x // ok +//│ ╙── ^^^^ +//│ class Foo4() extends Foo { +//│ fun bar: Int -> Int +//│ let bb: Int +//│ val foo: Int +//│ } + +:e +class Foo5() extends Foo() { + val foo: Int + val x = bar(0) + fun bar(y: Int) = this.foo + y +} +//│ ╔══[ERROR] Cannot access `this` while initializing field x +//│ ║ l.89: val x = bar(0) +//│ ║ ^^^^^^^^^^ +//│ ╟── The access to `this` is here +//│ ║ l.90: fun bar(y: Int) = this.foo + y +//│ ╙── ^^^^ +//│ class Foo5() extends Foo { +//│ fun bar: (y: Int) -> Int +//│ val foo: Int +//│ val x: Int +//│ } + +class Foo6() extends Foo() { + val baz: Int + val baz = 0 + val y = this.baz // baz is final +} +//│ class Foo6() extends Foo { +//│ val baz: Int +//│ val foo: Int +//│ val y: Int +//│ } + +class Bar() { + val d: Int + val d = 1 + fun add(x) = x + this.d +} +//│ class Bar() { +//│ fun add: Int -> Int +//│ val d: Int +//│ } + +:e +class Bar2() extends Bar() { + val two = this.add(1) // add is final, but it refers to `this` +} +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.127: val two = this.add(1) // add is final, but it refers to `this` +//│ ╙── ^^^^ +//│ ╔══[ERROR] Cannot access `this` while initializing field two +//│ ║ l.127: val two = this.add(1) // add is final, but it refers to `this` +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ╟── The access to `this` is here +//│ ║ l.118: fun add(x) = x + this.d +//│ ╙── ^^^^ +//│ class Bar2() extends Bar { +//│ fun add: Int -> Int +//│ val d: Int +//│ val two: error +//│ } + +// it accesses this in an unusual way! +:e +abstract class Foo: (Int -> Int) { + val x = f + fun f = this(0) +} +//│ ╔══[ERROR] Cannot access `this` while initializing field x +//│ ║ l.147: val x = f +//│ ║ ^^^^^ +//│ ╟── The access to `this` is here +//│ ║ l.148: fun f = this(0) +//│ ╙── ^^^^ +//│ abstract class Foo: Int -> Int { +//│ fun f: nothing +//│ val x: nothing +//│ } diff --git a/shared/src/test/diff/nu/NuAlexJ.mls b/shared/src/test/diff/nu/NuAlexJ.mls new file mode 100644 index 0000000000..d660e2fe96 --- /dev/null +++ b/shared/src/test/diff/nu/NuAlexJ.mls @@ -0,0 +1,69 @@ +:NewDefs + + +fun foo(x) = [x, x] +//│ fun foo: forall 'a. 'a -> ['a, 'a] + +id +//│ forall 'a. 'a -> 'a +//│ res +//│ = [Function: id] + +let r = foo(id) +//│ let r: [forall 'a. 'a -> 'a, forall 'a. 'a -> 'a] +//│ r +//│ = [ [Function: id], [Function: id] ] + + +fun fst(x, _) = x +fun snd(_, x) = x +//│ fun fst: forall 'a. ('a, anything) -> 'a +//│ fun snd: forall 'b. (anything, 'b) -> 'b + +fst(1, 2) +//│ 1 +//│ res +//│ = 1 + +:e +fst([1, 2]) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.29: fst([1, 2]) +//│ ║ ^^^^^^^^^^^ +//│ ╟── argument of type `[[1, 2]]` does not match type `[?a, ?b]` +//│ ║ l.29: fst([1, 2]) +//│ ║ ^^^^^^^^ +//│ ╟── Note: constraint arises from tuple literal: +//│ ║ l.18: fun fst(x, _) = x +//│ ╙── ^^^^^^ +//│ error +//│ res +//│ = [ 1, 2 ] + + +fun fst([x, _]) = x +fun snd([_, x]) = x +//│ fun fst: forall 'a. (['a, anything]) -> 'a +//│ fun snd: forall 'b. ([anything, 'b]) -> 'b + +let s = fst(r) +//│ let s: forall 'a. 'a -> 'a +//│ s +//│ = [Function: id] + +s("hello") +//│ "hello" +//│ res +//│ = 'hello' + +s(123) +//│ 123 +//│ res +//│ = 123 + +fst(r)("hello") +//│ "hello" +//│ res +//│ = 'hello' + + diff --git a/shared/src/test/diff/nu/NuForallTerms.mls b/shared/src/test/diff/nu/NuForallTerms.mls new file mode 100644 index 0000000000..00e5321e47 --- /dev/null +++ b/shared/src/test/diff/nu/NuForallTerms.mls @@ -0,0 +1,16 @@ +:NewDefs + + +forall 'a: (x: 'a) => x +//│ forall 'a. (x: 'a) -> 'a +//│ res +//│ = [Function: res] + + +forall 'a: + (x: 'a) => x +//│ forall 'a. (x: 'a) -> 'a +//│ res +//│ = [Function (anonymous)] + + diff --git a/shared/src/test/diff/nu/NuPolymorphicTypeAliases.mls b/shared/src/test/diff/nu/NuPolymorphicTypeAliases.mls new file mode 100644 index 0000000000..999389e9c8 --- /dev/null +++ b/shared/src/test/diff/nu/NuPolymorphicTypeAliases.mls @@ -0,0 +1,93 @@ +:NewDefs + + +type F[A] = forall 'a: (A, 'a) -> [A, 'a] +//│ type F[A] = forall 'a. (A, 'a) -> [A, 'a] + + +fun f[B] = + ((x: B, y) => [x, y]) : F[B] +//│ fun f: forall 'B. F['B] + +fun f = forall 'B: + ((x: 'B, y) => [x, y]) : F['B] +//│ fun f: forall 'B. F['B] + + +module A { + type F[A] = forall 'a: (A, 'a) -> [A, 'a] +} +//│ module A { +//│ type F[A] = forall 'a. (A, 'a) -> [A, 'a] +//│ } + +:pe // TODO +fun f[B] = + ((x: B, y) => [x, y]) : A.F[B] +//│ ╔══[PARSE ERROR] Not a recognized type +//│ ║ l.26: ((x: B, y) => [x, y]) : A.F[B] +//│ ╙── ^^^^^^ +//│ fun f: anything + + +class Test[B] { + fun f(f: F[B]): F[B] = (x, y) => f(x, y) +} +//│ class Test[B] { +//│ constructor() +//│ fun f: (f: F[B]) -> F[B] +//│ } + +class Test[B](f: F[B]) { + fun g: F[B] = (x, y) => f(x, y) +} +//│ class Test[B](f: F[B]) { +//│ fun g: F[B] +//│ } + +class Test[B] { + discard of ((x: B, y) => [x, y]) : F[B] +} +//│ class Test[B] { +//│ constructor() +//│ } + + +type F[A] = (A, 'a) -> [A, 'a] +//│ type F[A] = (A, 'a) -> [A, 'a] + + +fun f[B] = + ((x: B, y) => [x, y]) : F[B] +//│ fun f: forall 'B. F['B] + +fun f = forall 'B: + ((x: 'B, y) => [x, y]) : F['B] +//│ fun f: forall 'B. F['B] + + +class Test[B] { + fun f(f: F[B]): F[B] = (x, y) => f(x, y) +} +//│ class Test[B] { +//│ constructor() +//│ fun f: (f: F[B]) -> F[B] +//│ } + + + +// * Note: NOT polymorphic! +type T = 'a -> 'a +//│ type T = 'a -> 'a + +(id : T)(0) +//│ 0 +//│ res +//│ = 0 + +(id : T)(true) +//│ 0 | true +//│ res +//│ = true + + diff --git a/shared/src/test/diff/nu/NuScratch.mls b/shared/src/test/diff/nu/NuScratch.mls new file mode 100644 index 0000000000..539c23787c --- /dev/null +++ b/shared/src/test/diff/nu/NuScratch.mls @@ -0,0 +1,3 @@ +:NewDefs + + diff --git a/shared/src/test/diff/nu/Numbers.mls b/shared/src/test/diff/nu/Numbers.mls new file mode 100644 index 0000000000..0d15c73020 --- /dev/null +++ b/shared/src/test/diff/nu/Numbers.mls @@ -0,0 +1,64 @@ +:NewDefs + + +let n = 123 +let m = n : Int +let o = m : Num +let p = o : Object +let o = n : Num +let p = n : Object +let p = m : Object +//│ let n: 123 +//│ let m: Int +//│ let o: Num +//│ let p: Object +//│ let o: Num +//│ let p: Object +//│ let p: Object +//│ n +//│ = 123 +//│ m +//│ = 123 +//│ o +//│ = 123 +//│ p +//│ = 123 +//│ o +//│ = 123 +//│ p +//│ = 123 +//│ p +//│ = 123 + + +let x = NaN +//│ let x: Num +//│ x +//│ = NaN + +// TODO polymorphic Num operations +x + 1 +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.40: x + 1 +//│ ║ ^^^^^ +//│ ╟── reference of type `Num` is not an instance of type `Int` +//│ ║ l.34: let x = NaN +//│ ║ ^^^ +//│ ╟── but it flows into reference with expected type `Int` +//│ ║ l.40: x + 1 +//│ ╙── ^ +//│ Int | error +//│ res +//│ = NaN + + +true : Bool +//│ Bool +//│ res +//│ = true + +true : Bool | Str +//│ Str | false | true +//│ res +//│ = true + diff --git a/shared/src/test/diff/nu/Object.mls b/shared/src/test/diff/nu/Object.mls new file mode 100644 index 0000000000..9e93dec27b --- /dev/null +++ b/shared/src/test/diff/nu/Object.mls @@ -0,0 +1,160 @@ +:NewDefs + + + +class A() +class B() +//│ class A() +//│ class B() + +let a: Object = A() +//│ let a: Object +//│ a +//│ = A {} + + +module M +//│ module M + +M: Object +//│ Object +//│ res +//│ = M { class: [class M] } + + + +fun foo(x) = if x is A then true +//│ fun foo: A -> true + +fun foo(x) = if x is A then true else false +//│ fun foo: Object -> Bool + + +:e +fun foo(x: anything) = if x is A then true +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.34: fun foo(x: anything) = if x is A then true +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ╟── type `anything` is not an instance of type `Object` +//│ ║ l.34: fun foo(x: anything) = if x is A then true +//│ ║ ^^^^^^^^ +//│ ╟── but it flows into reference with expected type `Object` +//│ ║ l.34: fun foo(x: anything) = if x is A then true +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.34: fun foo(x: anything) = if x is A then true +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ╟── type `anything` is not an instance of type `A` +//│ ║ l.34: fun foo(x: anything) = if x is A then true +//│ ║ ^^^^^^^^ +//│ ╟── but it flows into reference with expected type `A` +//│ ║ l.34: fun foo(x: anything) = if x is A then true +//│ ║ ^ +//│ ╟── Note: constraint arises from class pattern: +//│ ║ l.34: fun foo(x: anything) = if x is A then true +//│ ╙── ^ +//│ fun foo: (x: anything) -> true + +:e +fun foo(x: anything) = if x is A then true else false +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.59: fun foo(x: anything) = if x is A then true else false +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type `anything` is not an instance of type `Object` +//│ ║ l.59: fun foo(x: anything) = if x is A then true else false +//│ ║ ^^^^^^^^ +//│ ╟── but it flows into reference with expected type `Object` +//│ ║ l.59: fun foo(x: anything) = if x is A then true else false +//│ ╙── ^ +//│ fun foo: (x: anything) -> Bool + + +fun foo(x: Object) = if x is A then true else false +//│ fun foo: (x: Object) -> Bool + + +// TODO make this a rigid type variable! +// :e +fun foo = forall 'a; (x: 'a) => if x is A then true else false +//│ ╔══[PARSE ERROR] Expected `:` after `forall` section +//│ ║ l.78: fun foo = forall 'a; (x: 'a) => if x is A then true else false +//│ ╙── ^ +//│ fun foo: () +//│ (x: Object) -> Bool +//│ res +//│ = [Function: res] + + + +:e +Object +//│ ╔══[ERROR] Class Object is abstract and cannot be instantiated +//│ ║ l.90: Object +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] Class Object cannot be instantiated as it exposes no constructor +//│ ║ l.90: Object +//│ ╙── ^^^^^^ +//│ error +//│ Code generation encountered an error: +//│ unresolved symbol Object + +:e +Object() +//│ ╔══[ERROR] Class Object is abstract and cannot be instantiated +//│ ║ l.102: Object() +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] Class Object cannot be instantiated as it exposes no constructor +//│ ║ l.102: Object() +//│ ╙── ^^^^^^ +//│ error +//│ Code generation encountered an error: +//│ unresolved symbol Object + +:e +new Object +//│ ╔══[ERROR] Class Object is abstract and cannot be instantiated +//│ ║ l.114: new Object +//│ ╙── ^^^^^^ +//│ Object +//│ Code generation encountered an error: +//│ unresolved symbol Object + + + +// TODO +class B() extends Object +//│ class B() extends Object +//│ Code generation encountered an error: +//│ unresolved parent Object. + +class C() extends A +//│ class C() extends A + +let o: Object = C() +//│ let o: Object +//│ o +//│ = C {} + +fun foo(x) = if x is + A then true + B then true + else false +//│ fun foo: Object -> Bool + +foo(0) +//│ Bool +//│ res +//│ = false + +foo(o) +//│ Bool +//│ res +//│ = true + + +(0 : Int) : Object +//│ Object +//│ res +//│ = 0 + + diff --git a/shared/src/test/diff/nu/OpLam.mls b/shared/src/test/diff/nu/OpLam.mls new file mode 100644 index 0000000000..bedc2cd423 --- /dev/null +++ b/shared/src/test/diff/nu/OpLam.mls @@ -0,0 +1,118 @@ +:NewDefs + + +x => x is 42 +//│ Object -> Bool +//│ res +//│ = [Function: res] + + +:pe +fun (|>;) foo(a, b) = [a, b] +//│ ╔══[PARSE ERROR] Unexpected semicolon after symbolic name +//│ ║ l.11: fun (|>;) foo(a, b) = [a, b] +//│ ╙── ^ +//│ fun (|>) foo: forall 'a 'b. ('a, 'b) -> ['a, 'b] + +42 |>; x => x +//│ forall 'a. 'a -> 'a +//│ res +//│ = [ 42, undefined ] +//│ res +//│ = [Function: res] + + +fun (>>) compose(f, g) = x => g(f(x)) +//│ fun (>>) compose: forall 'a 'b 'c. ('c -> 'a, 'a -> 'b) -> 'c -> 'b + +succ >> x => x + 2 +//│ Int -> Int +//│ res +//│ = [Function (anonymous)] + +:e +x => x + 2 >> succ +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.34: x => x + 2 >> succ +//│ ║ ^^^^^^^^^^^^^ +//│ ╟── operator application of type `Int` is not a function +//│ ║ l.34: x => x + 2 >> succ +//│ ║ ^^^^^ +//│ ╟── Note: constraint arises from application: +//│ ║ l.25: fun (>>) compose(f, g) = x => g(f(x)) +//│ ║ ^^^^ +//│ ╟── from reference: +//│ ║ l.25: fun (>>) compose(f, g) = x => g(f(x)) +//│ ╙── ^ +//│ Int -> (error | anything -> Int) +//│ res +//│ = [Function: res] + +(x => x + 2) >> succ +//│ Int -> Int +//│ res +//│ = [Function (anonymous)] + +:e +x => x + 2 + >> succ +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.57: x => x + 2 +//│ ║ ^ +//│ ║ l.58: >> succ +//│ ║ ^^^^^^^^^^ +//│ ╟── integer literal of type `2` is not a function +//│ ║ l.57: x => x + 2 +//│ ║ ^ +//│ ╟── Note: constraint arises from application: +//│ ║ l.25: fun (>>) compose(f, g) = x => g(f(x)) +//│ ║ ^^^^ +//│ ╟── from reference: +//│ ║ l.25: fun (>>) compose(f, g) = x => g(f(x)) +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.57: x => x + 2 +//│ ║ ^^^^^ +//│ ║ l.58: >> succ +//│ ║ ^^^^^^^^^ +//│ ╟── function of type `?a -> ?b` is not an instance of type `Int` +//│ ║ l.25: fun (>>) compose(f, g) = x => g(f(x)) +//│ ║ ^^^^^^^^^^^^ +//│ ╟── but it flows into operator application with expected type `Int` +//│ ║ l.57: x => x + 2 +//│ ║ ^ +//│ ║ l.58: >> succ +//│ ╙── ^^^^^^^^^^ +//│ Int -> (Int | error) +//│ res +//│ = [Function: res] + +(x => x + 2) + >> succ +//│ Int -> Int +//│ res +//│ = [Function (anonymous)] + +:pe +>> succ +//│ ╔══[PARSE ERROR] Unexpected operator in expression position +//│ ║ l.97: >> succ +//│ ╙── ^^ +//│ Int -> Int +//│ res +//│ = [Function: succ] + + +:e +x => x.y => y +//│ ╔══[ERROR] Unsupported pattern shape: +//│ ║ l.107: x => x.y => y +//│ ╙── ^^^ +//│ ╔══[ERROR] identifier not found: y +//│ ║ l.107: x => x.y => y +//│ ╙── ^ +//│ anything -> error -> error +//│ Code generation encountered an error: +//│ term Sel(Var(x),Var(y)) is not a valid pattern + + diff --git a/shared/src/test/diff/nu/OptionFilter.mls b/shared/src/test/diff/nu/OptionFilter.mls new file mode 100644 index 0000000000..530722135f --- /dev/null +++ b/shared/src/test/diff/nu/OptionFilter.mls @@ -0,0 +1,72 @@ +:NewDefs + + +// * Minimization of code that used to cause a problem: + +abstract class Option[T]: (None | ()) { + virtual fun filter: Option[T] +} +module None extends Option[nothing] { + fun filter = None +} +//│ abstract class Option[T]: None | () { +//│ fun filter: Option[T] +//│ } +//│ module None extends Option { +//│ fun filter: None +//│ } + + +// * Original code: + +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 +} +//│ abstract class Option[T]: None | Some[T] { +//│ fun filter: (p: T -> Bool) -> Option[T] +//│ } +//│ class Some[T](value: T) extends Option { +//│ fun filter: (T -> Object) -> (None | Some[T]) +//│ } +//│ module None extends Option { +//│ fun filter: anything -> None +//│ } + + + +abstract class Boxful[T] { + virtual fun clone(): Boxful[T] +} +class MetalBox[T](value: T) extends Boxful[T] { + fun clone(): Boxful[T] = MetalBox(value) +} +//│ abstract class Boxful[T] { +//│ fun clone: () -> Boxful[T] +//│ } +//│ class MetalBox[T](value: T) extends Boxful { +//│ fun clone: () -> Boxful[T] +//│ } + + +fun makeMetalBox(value: 'A): Boxful['A] = MetalBox(value) +abstract class Boxful[T] { + virtual fun clone(): Boxful[T] +} +class MetalBox[T](value: T) extends Boxful[T] { + fun clone(): Boxful[T] = makeMetalBox(value) +} +//│ fun makeMetalBox: forall 'T. (value: 'T) -> Boxful['T] +//│ abstract class Boxful[T] { +//│ fun clone: () -> Boxful[T] +//│ } +//│ class MetalBox[T](value: T) extends Boxful { +//│ fun clone: () -> Boxful[T] +//│ } + + diff --git a/shared/src/test/diff/nu/OverrideShorthand.mls b/shared/src/test/diff/nu/OverrideShorthand.mls new file mode 100644 index 0000000000..2bd823530c --- /dev/null +++ b/shared/src/test/diff/nu/OverrideShorthand.mls @@ -0,0 +1,56 @@ +:NewDefs + + + +class Pair(lhs: Int, rhs: Int) +//│ class Pair(lhs: Int, rhs: Int) + + +:p +:e +fun f(override Pair(x, y)) = x + y +//│ |#fun| |f|(|#override| |Pair|(|x|,| |y|)|)| |#=| |x| |+| |y| +//│ AST: TypingUnit(List(NuFunDef(None,Var(f),None,List(),Left(Lam(Tup(List((None,Fld(_,Var(_$0))))),If(IfOpApp(Var(_$0),Var(is),IfThen(App(Var(Pair),Tup(List((None,Fld(_,Var(x))), (None,Fld(_,Var(y)))))),App(Var(+),Tup(List((None,Fld(_,Var(x))), (None,Fld(_,Var(y)))))))),Some(App(Sel(Super(),Var(f)),Tup(List((None,Fld(_,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 +//│ ╙── ^^^^^^^^ +//│ fun f: Object -> (Int | error) +//│ Syntax error: +//│ 'super' keyword unexpected here + + +mixin Test { + fun f(override Pair(x, y)) = x + y +} +//│ mixin Test() { +//│ super: {f: 'a -> 'b} +//│ fun f: (Object & 'a & ~#Pair | Pair) -> (Int | 'b) +//│ } + + +:pe +:e +fun f(override Pair(x, y), z) = x + y +//│ ╔══[PARSE ERROR] Unsupported 'override' parameter list shape +//│ ║ l.34: fun f(override Pair(x, y), z) = x + y +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ +//│ ╔══[ERROR] Unsupported pattern shape: +//│ ║ l.34: fun f(override Pair(x, y), z) = x + y +//│ ╙── ^^^^^^^^^^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.34: fun f(override Pair(x, y), z) = x + y +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: y +//│ ║ l.34: fun f(override Pair(x, y), z) = x + y +//│ ╙── ^ +//│ fun f: (error, anything) -> Int +//│ Code generation encountered an error: +//│ term App(Var(Pair),Tup(List((None,Fld(_,Var(x))), (None,Fld(_,Var(y)))))) is not a valid pattern + + +// TODO +// :pe +// fun f(override Pair(x, y)): Int + + diff --git a/shared/src/test/diff/nu/ParamImplementing.mls b/shared/src/test/diff/nu/ParamImplementing.mls new file mode 100644 index 0000000000..4b472044fe --- /dev/null +++ b/shared/src/test/diff/nu/ParamImplementing.mls @@ -0,0 +1,68 @@ +:NewDefs + + +trait T { fun x: Int } +mixin M(val x: Bool) +//│ trait T { +//│ fun x: Int +//│ } +//│ mixin M(x: Bool) + +:e +module C extends T, M(false) +C.x +//│ ╔══[ERROR] Type mismatch in reference: +//│ ║ l.12: module C extends T, M(false) +//│ ║ ^^^^^ +//│ ╟── reference of type `false` is not an instance of `Int` +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.4: trait T { fun x: Int } +//│ ║ ^^^ +//│ ╟── from signature of member `x`: +//│ ║ l.4: trait T { fun x: Int } +//│ ╙── ^^^^^^ +//│ module C extends T +//│ false +//│ res +//│ = false + + +trait T { fun x: Int } +mixin M(val x: Num) +//│ trait T { +//│ fun x: Int +//│ } +//│ mixin M(x: Num) + +module C extends T, M(0) +C.x +//│ module C extends T +//│ 0 +//│ res +//│ = 0 + + +trait T { fun x: Int } +mixin M(x: Bool) +//│ trait T { +//│ fun x: Int +//│ } +//│ mixin M(x: Bool) + +:e +module C extends T, M(false) +C.x +//│ ╔══[ERROR] Member `x` is declared (or its declaration is inherited) but is not implemented in `C` +//│ ║ l.53: module C extends T, M(false) +//│ ║ ^ +//│ ╟── Declared here: +//│ ║ l.45: trait T { fun x: Int } +//│ ╙── ^^^^^^^^^^ +//│ module C extends T { +//│ fun x: Int +//│ } +//│ Int +//│ res +//│ = undefined + + diff --git a/shared/src/test/diff/nu/ParamOverride.mls b/shared/src/test/diff/nu/ParamOverride.mls new file mode 100644 index 0000000000..8528a5e2b9 --- /dev/null +++ b/shared/src/test/diff/nu/ParamOverride.mls @@ -0,0 +1,113 @@ +:NewDefs + + +class Base0(n: Num) +//│ class Base0(n: Num) + +// TODO +class Derived0(n: Int) extends Base +//│ ╔══[ERROR] Could not find definition `Base` +//│ ║ l.8: class Derived0(n: Int) extends Base +//│ ╙── ^^^^ +//│ class Derived0(n: Int) +//│ Code generation encountered an error: +//│ unresolved parent Base. + + +mixin Base1(val n: Num) { + fun original = n +} +//│ mixin Base1(n: Num) { +//│ fun original: Num +//│ } + +:e +mixin DerivedBad(n: Int) extends Base +//│ ╔══[ERROR] mixin definitions cannot yet extend parents +//│ ║ l.25: mixin DerivedBad(n: Int) extends Base +//│ ╙── ^^^^ +//│ mixin DerivedBad(n: Int) + +mixin Derived1(val n: Int) { + fun foo = [n, this.n, super.n] +} +//│ mixin Derived1(n: Int) { +//│ super: {n: 'n} +//│ this: {n: 'n0} +//│ fun foo: [Int, 'n0, 'n] +//│ } + + +class Test0() extends Base1(1/2), Derived1(1) +//│ class Test0() { +//│ fun foo: [Int, 1, Num] +//│ fun original: Num +//│ } + +let t = Test0() +//│ let t: Test0 +//│ t +//│ = Test0 {} + +t.n +//│ 1 +//│ res +//│ = 1 + +t.original +//│ Num +//│ res +//│ = 0.5 + +t.foo +//│ [Int, 1, Num] +//│ res +//│ = [ 1, 1, 0.5 ] + + +module Test1 extends Base1(1/2), Derived1(1) { + fun n = this.n +} +//│ module Test1 { +//│ fun foo: [Int, 1, Num] +//│ fun n: 1 +//│ fun original: Num +//│ } + +:re +Test1.n +//│ 1 +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + + +class Test2(val n: Str) extends Base1(1/2), Derived1(1) { + fun bar = [this.foo, n, this.n, this.original] +} +//│ class Test2(n: Str) { +//│ fun bar: [[Int, Str, Num], Str, Str, Num] +//│ fun foo: [Int, Str, Num] +//│ fun original: Num +//│ } + +Test2("test").bar +//│ [[Int, Str, Num], Str, Str, Num] +//│ res +//│ = [ [ 1, 'test', 0.5 ], 'test', 'test', 0.5 ] + + +class Test3(val n: Str) extends Base1(1/2), Derived1(length(n)) { + fun foo = [super.foo, n, this.original] +} +//│ class Test3(n: Str) { +//│ fun foo: [[Int, Str, Num], Str, Num] +//│ fun original: Num +//│ } + +Test3("test").foo +//│ [[Int, Str, Num], Str, Num] +//│ res +//│ = [ [ 4, 'test', 0.5 ], 'test', 0.5 ] + + diff --git a/shared/src/test/diff/nu/ParamOverriding.mls b/shared/src/test/diff/nu/ParamOverriding.mls new file mode 100644 index 0000000000..2851f62dea --- /dev/null +++ b/shared/src/test/diff/nu/ParamOverriding.mls @@ -0,0 +1,33 @@ +:NewDefs + + +mixin Over { + fun p = "hi" +} +//│ mixin Over() { +//│ fun p: "hi" +//│ } + + +class Base1(val p: Int) extends Over { + fun test = [p, this.p] + fun test2 = super.p +} +//│ class Base1(p: Int) { +//│ fun p: "hi" +//│ fun test: [Int, Int] +//│ fun test2: Int +//│ } + + +Base1(123).test +//│ [Int, Int] +//│ res +//│ = [ 123, 123 ] + +Base1(123).test2 +//│ Int +//│ res +//│ = 'hi' + + diff --git a/shared/src/test/diff/nu/ParamPassing.mls b/shared/src/test/diff/nu/ParamPassing.mls new file mode 100644 index 0000000000..0fc6a4af9c --- /dev/null +++ b/shared/src/test/diff/nu/ParamPassing.mls @@ -0,0 +1,245 @@ +:NewDefs + + +class Foo(x: Int) +//│ class Foo(x: Int) + +class Bar(z: Int, y: Int) extends Foo(z + y) +//│ class Bar(z: Int, y: Int) extends Foo + +class Bar(x: Int, y: Int) extends Foo(x + y) +//│ class Bar(x: Int, y: Int) extends Foo + + +class Foo(val x: Int) +//│ class Foo(x: Int) + +module Bar extends Foo(11) +Bar.x +//│ module Bar extends Foo +//│ 11 +//│ res +//│ = 11 + +:e // FIXME +module Bar extends Foo(11) { fun get = this.x } +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.25: module Bar extends Foo(11) { fun get = this.x } +//│ ╙── ^^ +//│ module Bar extends Foo { +//│ fun get: error +//│ } + + +mixin AA(a: Int) { +} +//│ mixin AA(a: Int) + +mixin BB {} +//│ mixin BB() + + +class C(x: Int) extends BB +//│ class C(x: Int) + +class D(x: Int) extends AA(x) +//│ class D(x: Int) + +class E(x: Int) extends BB, AA(x) +//│ class E(x: Int) + + +class Foo(x: Int) +//│ class Foo(x: Int) + +:e +Foo(1).x +//│ ╔══[ERROR] Parameter 'x' cannot tbe accessed as a field +//│ ║ l.56: Foo(1).x +//│ ║ ^^ +//│ ╟── Either make the parameter a `val` or access it through destructuring +//│ ║ l.52: class Foo(x: Int) +//│ ╙── ^ +//│ Int | error +//│ res +//│ = undefined + +:e +Foo(1).#x +//│ ╔══[ERROR] identifier not found: .# +//│ ║ l.68: Foo(1).#x +//│ ╙── ^^ +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.68: Foo(1).#x +//│ ╙── ^ +//│ error +//│ Code generation encountered an error: +//│ unresolved symbol .# + +if Foo(1) is Foo(x) then x +//│ Int +//│ res +//│ = 1 + + +class Foo(val x: Int) +//│ class Foo(x: Int) + +Foo(1).x +//│ Int +//│ res +//│ = 1 + +if Foo(1) is Foo(x) then x +//│ Int +//│ res +//│ = 1 + + +: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) +//│ ║ ^ +//│ ╟── Originally declared here: +//│ ║ 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.110: Bar(11).x +//│ ║ ^^ +//│ ╟── Either make the parameter a `val` or access it through destructuring +//│ ║ l.100: class Bar(x: Int) extends Foo(x) +//│ ╙── ^ +//│ Int | error +//│ res +//│ = 11 + + +: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) +//│ ║ ^ +//│ ╟── Originally declared here: +//│ ║ l.85: class Foo(val x: Int) +//│ ╙── ^ +//│ class Bar(x: Int) extends Foo + +Bar(11).x +//│ Int +//│ res +//│ = 11 + +: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 } +//│ ║ ^^^^^^^^ +//│ ╟── Originally declared here: +//│ ║ l.85: class Foo(val x: Int) +//│ ╙── ^ +//│ class Bar extends Foo { +//│ constructor() +//│ 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 } +//│ ║ ^^^^^ +//│ ╟── Originally declared here: +//│ ║ l.85: class Foo(val x: Int) +//│ ╙── ^ +//│ module Bar extends Foo { +//│ fun x: 2 +//│ } + + +class A(val x: Int) +//│ class A(x: Int) + +module B extends A(42) +//│ module B extends A + +B.x +//│ 42 +//│ res +//│ = 42 + + +class A(x: Int) +//│ class A(x: Int) + +module B extends A(42) +//│ module B extends A + +:e +B.x +//│ ╔══[ERROR] Parameter 'x' cannot tbe accessed as a field +//│ ║ l.182: B.x +//│ ║ ^^ +//│ ╟── Either make the parameter a `val` or access it through destructuring +//│ ║ l.175: class A(x: Int) +//│ ╙── ^ +//│ Int | error +//│ res +//│ = undefined + + + +abstract class Foo[A](val x: A) { fun y = x; fun i: A -> A } +//│ abstract class Foo[A](x: A) { +//│ fun i: A -> A +//│ fun y: A +//│ } + +abstract class Bar extends Foo(0) +//│ abstract class Bar extends Foo { +//│ fun i: 'A -> 'A +//│ fun y: 'A +//│ } +//│ where +//│ 'A :> 0 + +module Baz extends Foo(0) { fun i = id } +[Baz.x, Baz.i] +//│ module Baz extends Foo { +//│ fun i: forall 'a. 'a -> 'a +//│ fun y: 'A +//│ } +//│ [0, forall 'a. 'a -> 'a] +//│ where +//│ 'A :> 0 +//│ res +//│ = [ 0, [Function: id] ] + +:e +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 +//│ ║ ^^^^^^^^ +//│ ╟── Originally declared here: +//│ ║ 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.222: module Bazz extends Foo(0) { +//│ ║ ^^^^ +//│ ╟── Declared here: +//│ ║ l.195: abstract class Foo[A](val x: A) { fun y = x; fun i: A -> A } +//│ ╙── ^^^^^^^^^^^^^ +//│ module Bazz extends Foo { +//│ fun i: 'A -> 'A +//│ val x: 2 +//│ fun y: 'A +//│ } +//│ where +//│ 'A :> 0 + + diff --git a/shared/src/test/diff/nu/Parens.mls b/shared/src/test/diff/nu/Parens.mls index d2fb018fad..16f554ee2f 100644 --- a/shared/src/test/diff/nu/Parens.mls +++ b/shared/src/test/diff/nu/Parens.mls @@ -1,66 +1,85 @@ -:NewParser +:NewDefs () -//│ res: () -//│ = [] +//│ () +//│ res +//│ = undefined :pe (,) //│ ╔══[PARSE ERROR] Unexpected comma in expression position -//│ ║ l.9: (,) -//│ ╙── ^ +//│ ║ l.10: (,) +//│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected end of parenthesis section; an expression was expected here -//│ ║ l.9: (,) -//│ ╙── ^ -//│ res: (undefined,) -//│ = [ undefined ] +//│ ║ l.10: (,) +//│ ╙── ^ +//│ () +//│ res +//│ = undefined -// FIXME shouldn't parse like a 1-tuple... (1) -//│ res: (1,) -//│ = [ 1 ] +//│ 1 +//│ res +//│ = 1 (1,) -//│ res: (1,) -//│ = [ 1 ] +//│ 1 +//│ res +//│ = 1 (1, 2) -//│ res: (1, 2,) -//│ = [ 1, 2 ] +//│ 2 +//│ res +//│ = 2 (1, 2,) -//│ res: (1, 2,) -//│ = [ 1, 2 ] +//│ 2 +//│ res +//│ = 2 let x: () -//│ x: () -//│ = +//│ let x: () +//│ x +//│ = :pe let x: (,) //│ ╔══[PARSE ERROR] Unexpected comma in expression position -//│ ║ l.42: let x: (,) +//│ ║ l.48: let x: (,) //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected end of parenthesis section; an expression was expected here -//│ ║ l.42: let x: (,) +//│ ║ l.48: let x: (,) //│ ╙── ^ -//│ x: (undefined,) -//│ = +//│ let x: () +//│ x +//│ = let x: (1) -//│ x: (1,) -//│ = +//│ let x: 1 +//│ x +//│ = let x: (1,) -//│ x: (1,) -//│ = +//│ let x: 1 +//│ x +//│ = +:e let x: (1, 2) -//│ x: (1, 2,) -//│ = +//│ ╔══[ERROR] type identifier not found: , +//│ ║ l.70: let x: (1, 2) +//│ ╙── ^^^^^^ +//│ let x: error +//│ x +//│ = +:e let x: (1, 2,) -//│ x: (1, 2,) -//│ = +//│ ╔══[ERROR] type identifier not found: , +//│ ║ l.79: let x: (1, 2,) +//│ ╙── ^^^^^^^ +//│ let x: error +//│ x +//│ = diff --git a/shared/src/test/diff/nu/PartialApp.mls b/shared/src/test/diff/nu/PartialApp.mls new file mode 100644 index 0000000000..2311edb37c --- /dev/null +++ b/shared/src/test/diff/nu/PartialApp.mls @@ -0,0 +1,60 @@ +:NewDefs + +// TODO support partial application syntax +:AllowTypeErrors + + +fun foo(x, y) = x + y +//│ fun foo: (Int, Int) -> Int + + +foo(2, _) +//│ ╔══[ERROR] Widlcard in expression position. +//│ ║ l.11: foo(2, _) +//│ ╙── ^ +//│ Int + +// * ie +tmp => foo(2, tmp) +//│ Int -> Int + + +_.foo(1) +//│ ╔══[ERROR] Widlcard in expression position. +//│ ║ l.22: _.foo(1) +//│ ╙── ^ +//│ error + +// * ie +x => x.foo(1) +//│ forall 'a. {foo: 1 -> 'a} -> 'a + + +_ + _ +//│ ╔══[ERROR] Widlcard in expression position. +//│ ║ l.33: _ + _ +//│ ╙── ^ +//│ ╔══[ERROR] Widlcard in expression position. +//│ ║ l.33: _ + _ +//│ ╙── ^ +//│ Int + +// * ie +(x, y) => x + y +//│ (Int, Int) -> Int + + +_2 + _1 +//│ ╔══[ERROR] identifier not found: _2 +//│ ║ l.47: _2 + _1 +//│ ╙── ^^ +//│ ╔══[ERROR] identifier not found: _1 +//│ ║ l.47: _2 + _1 +//│ ╙── ^^ +//│ Int + +// * ie +(x, y) => y + x +//│ (Int, Int) -> Int + + diff --git a/shared/src/test/diff/nu/PolymorphicVariants_Alt.mls b/shared/src/test/diff/nu/PolymorphicVariants_Alt.mls new file mode 100644 index 0000000000..a9355ce6a2 --- /dev/null +++ b/shared/src/test/diff/nu/PolymorphicVariants_Alt.mls @@ -0,0 +1,212 @@ +:NewDefs +:NoJS + + +// * Adapted example from Code reuse through polymorphic variants (FOSE 2000) +// * This time with an ML-style List data type encoding. + + +// TODO improvements/things to investigate: +// - constraining loop with unannotated `list_assoc` ascription +// - still a number of quite ugly types + + +class List { + fun match: forall 'res: (ifNil: () => 'res, ifCons: (A, List[A]) => 'res) => 'res + fun match = error +} +let Nil: () => List<'a> +let Cons: (head: 'a, tail: List<'a>) => List<'a> +//│ class List[A] { +//│ constructor() +//│ fun match: forall 'res. (ifNil: () -> 'res, ifCons: (A, List[A]) -> 'res) -> 'res +//│ } +//│ let Nil: () -> List[nothing] +//│ let Cons: forall 'a. (head: 'a, tail: List['a]) -> List['a] + +module NotFound +class Success(result: A) +//│ module NotFound +//│ class Success[A](result: A) + +fun eq(l: Str, r: Str): Bool +//│ fun eq: (l: Str, r: Str) -> Bool + +// * Annotation currently needed to avoid later ascription loop (due to excessive TV refreshing?) +// fun list_assoc(s, l) = +fun list_assoc(s, l: List<'a>) = + l.match( + ifNil: () => NotFound, + ifCons: (h, t) => + if eq(s, h.0) then Success(h.1) + else list_assoc(s, t) + ) +//│ fun list_assoc: forall 'A. (Str, l: List[{0: Str, 1: 'A}]) -> (NotFound | Success['A]) + +list_assoc : (Str, List<{ 0: Str, 1: 'b }>) => (NotFound | Success['b]) +//│ (Str, List[{0: Str, 1: 'b}]) -> (NotFound | Success['b]) + +fun list_assoc(s: Str, l: List<{ 0: Str, 1: 'b }>): NotFound | Success['b] +//│ fun list_assoc: forall 'b. (s: Str, l: List[{0: Str, 1: 'b}]) -> (NotFound | Success['b]) + +class Var(s: Str) +//│ class Var(s: Str) + +mixin EvalVar { + fun eval(sub, v) = + if v is Var(s) then + if list_assoc(s, sub) is + NotFound then v + Success(r) then r +} +//│ mixin EvalVar() { +//│ fun eval: (List[{0: Str, 1: 'a}], Var) -> (Var | 'a) +//│ } + +class Abs(x: Str, t: A) +class App(s: A, t: A) +//│ class Abs[A](x: Str, t: A) +//│ class App[A](s: A, t: A) + +fun incr(x: {a: Int}): unit +//│ fun incr: (x: {a: Int}) -> unit + +fun gensym(): Str +//│ fun gensym: () -> Str + +fun Int_to_string(x: Int): Str +//│ fun Int_to_string: (x: Int) -> Str + +mixin EvalLambda { + fun eval(sub, v) = + if v is + App(t1, t2) then + let l1 = this.eval(sub, t1) + let l2 = this.eval(sub, t2) + if t1 is + Abs(x, t) then this.eval(Cons([x, l2], Nil()), t) + else + App(l1, l2) + Abs(x, t) then + let s = gensym() + Abs(s, this.eval(Cons([x, Var(s)], sub), t)) + else + super.eval(sub, v) +} +//│ mixin EvalLambda() { +//│ super: {eval: ('b, 'c) -> 'd} +//│ this: { +//│ eval: ('b, 'e) -> 'A & (List[[Str, 'A]], 'f) -> 'd & (List['a | [Str, Var]], 'g) -> 'A0 +//│ } +//│ fun eval: (List['a] & 'b, Abs['g] | App['e & (Abs['f] | Object & ~#Abs)] | Object & 'c & ~#Abs & ~#App) -> (Abs['A0] | App['A] | 'd) +//│ } + +module Test1 extends EvalVar, EvalLambda +//│ module Test1 { +//│ fun eval: (List[{0: Str, 1: 'a}], 'b) -> 'a +//│ } +//│ where +//│ 'b <: Abs['b] | App['b & (Abs['b] | Object & ~#Abs)] | Var +//│ 'a :> App['a] | Abs['a] | Var + +Test1.eval(Nil(), Var("a")) +//│ 'a +//│ where +//│ 'a :> App['a] | Abs['a] | Var + +Test1.eval(Nil(), Abs("b", Var("a"))) +//│ 'a +//│ where +//│ 'a :> App['a] | Abs['a] | Var + +Test1.eval(Cons(["c", Var("d")], Nil()), App(Abs("b", Var("b")), Var("c"))) +//│ 'a +//│ where +//│ 'a :> App['a] | Abs['a] | Var + +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 + +class Numb(n: Int) +class Add(l: A, r: A) +class Mul(l: A, r: A) +//│ class Numb(n: Int) +//│ class Add[A](l: A, r: A) +//│ class Mul[A](l: A, r: A) + +fun map_expr(f, v) = + if v is + Var then 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) + +mixin EvalExpr { + fun eval(sub, v) = + let eta(e) = this.eval(sub, e) + let vv = map_expr(eta, v) + if vv is + Var then super.eval(sub, vv) + Add(Numb(l), Numb(r)) then Numb(l + r) + Mul(Numb(l), Numb(r)) then Numb(l * r) + else v +} +//│ mixin EvalExpr() { +//│ super: {eval: ('a, Var) -> 'b} +//│ this: {eval: ('a, 'c) -> Object} +//│ fun eval: ('a, 'b & (Add['c] | Mul['c] | Numb | Var)) -> (Numb | 'b) +//│ } + +module Test2 extends EvalVar, EvalExpr +//│ module Test2 { +//│ fun eval: forall 'a. (List[{0: Str, 1: Object & 'b}], 'a & (Add['c] | Mul['c] | Numb | Var)) -> (Numb | Var | 'b | 'a | 'c) +//│ } +//│ where +//│ 'c <: Add['c] | Mul['c] | Numb | Var + +Test2.eval(Nil(), Var("a")) +//│ Numb | Var + +Test2.eval(Cons(["c", Abs("d", Var("d"))], Nil()), Var("a")) +//│ Abs[Var] | Numb | Var + +Test2.eval(Cons(["a", Numb(1)], Nil()), Var("a")) +//│ Numb | Var + +Test2.eval(Cons(["a", Abs("d", Var("d"))], Nil()), Add(Numb(1), Var("a"))) +//│ Abs[Var] | Add[Numb | Var] | Numb | Var + +module Test3 extends EvalVar, EvalExpr, EvalLambda +//│ module Test3 { +//│ fun eval: (List[{0: Str, 1: 'a}], 'b) -> 'c +//│ } +//│ where +//│ 'a :> 'c +//│ <: Object +//│ 'c :> App['c] | Abs['c] | 'a | 'd +//│ 'd <: Add['b] | Mul['b] | Numb | Var +//│ 'b <: Abs['b] | App['b & (Abs['b] | Object & ~#Abs)] | Object & 'd & ~#Abs & ~#App + +Test3.eval(Cons(["c", Abs("d", Var("d"))], Nil()), Abs("a", Var("a"))) +//│ 'a +//│ where +//│ 'a :> App['a] | Abs['a] | Abs[Var] | Numb | Var + +Test3.eval(Cons(["c", Abs("d", Var("d"))], Nil()), App(Abs("a", Var("a")), Add(Numb(1), Var("c")))) +//│ 'a +//│ where +//│ 'a :> App['a] | Abs['a] | Abs[Var] | Add[Numb | Var] | Numb | Var + +module Test3 extends EvalVar, EvalLambda, EvalExpr +//│ module Test3 { +//│ fun eval: (List[{0: Str, 1: 'a}], 'a & (Add['b] | Mul['b] | Numb | Var)) -> (Numb | 'a | 'b | 'c) +//│ } +//│ where +//│ 'a :> 'b | 'c +//│ <: Object +//│ 'c :> Abs['a | 'c] | App['a | 'c] | 'a +//│ 'b <: Add['b] | Mul['b] | Numb | Var + diff --git a/shared/src/test/diff/nu/PostHocMixinSignature.mls b/shared/src/test/diff/nu/PostHocMixinSignature.mls new file mode 100644 index 0000000000..5c23e3c3fa --- /dev/null +++ b/shared/src/test/diff/nu/PostHocMixinSignature.mls @@ -0,0 +1,73 @@ +:NewDefs + + +mixin Foo { + fun foo(x) = if x.a then x else foo(x.b) +} +//│ mixin Foo() { +//│ fun foo: 'a -> 'a +//│ } +//│ where +//│ 'a <: {a: Object, b: 'a} + + +module ValA { + val a = false + val b = ValB +} +module ValB { + val a = true + fun b = ValA +} +//│ module ValA { +//│ val a: false +//│ val b: ValB +//│ } +//│ module ValB { +//│ val a: true +//│ fun b: ValA +//│ } + + +module Test extends Foo +//│ module Test { +//│ fun foo: forall 'a. 'a -> 'a +//│ } +//│ where +//│ 'a <: {a: Object, b: 'a} + +[Test.foo(ValA), Test.foo(ValB)] +//│ [ValA | ValB, ValA | ValB] +//│ res +//│ = [ ValB { class: [class ValB] }, ValB { class: [class ValB] } ] + + +type V = {a: Bool, b: V} +//│ type V = {a: Bool, b: V} + +module Test extends Foo { + fun foo: V -> anything +} +//│ module Test { +//│ fun foo: V -> anything +//│ } + +[Test.foo(ValA), Test.foo(ValB)] +//│ [anything, anything] +//│ res +//│ = [ ValB { class: [class ValB] }, ValB { class: [class ValB] } ] + + +module Test extends Foo { + fun foo: V -> V +} +//│ module Test { +//│ fun foo: V -> V +//│ } + +[Test.foo(ValA), Test.foo(ValB)] +//│ [V, V] +//│ res +//│ = [ ValB { class: [class ValB] }, ValB { class: [class ValB] } ] + + diff --git a/shared/src/test/diff/nu/PrivateMemberOverriding.mls b/shared/src/test/diff/nu/PrivateMemberOverriding.mls new file mode 100644 index 0000000000..e7977a41df --- /dev/null +++ b/shared/src/test/diff/nu/PrivateMemberOverriding.mls @@ -0,0 +1,57 @@ +:NewDefs + + +class Foo(x: Int) +//│ class Foo(x: Int) + +class Bar() extends Foo(123) { fun x = true } +//│ class Bar() extends Foo { +//│ fun x: true +//│ } + +Bar().x +//│ true +//│ res +//│ = true + +if Bar() is Foo(a) then a +//│ Int +//│ res +//│ = 123 + + +class Bar(val x: Bool) extends Foo(123) +//│ class Bar(x: Bool) extends Foo + +Bar(true).x +//│ Bool +//│ res +//│ = true + +if Bar(true) is Foo(a) then a +//│ Int +//│ res +//│ = 123 + + +class Bar(x: Bool) extends Foo(123) +//│ class Bar(x: Bool) extends Foo + +:e // * Expected +Bar(true).x +//│ ╔══[ERROR] Parameter 'x' cannot tbe accessed as a field +//│ ║ l.41: Bar(true).x +//│ ║ ^^ +//│ ╟── Either make the parameter a `val` or access it through destructuring +//│ ║ l.37: class Bar(x: Bool) extends Foo(123) +//│ ╙── ^ +//│ error | false | true +//│ res +//│ = undefined + +if Bar(true) is Foo(a) then a +//│ Int +//│ res +//│ = 123 + + diff --git a/shared/src/test/diff/nu/RawUnionTraitSignatures.mls b/shared/src/test/diff/nu/RawUnionTraitSignatures.mls new file mode 100644 index 0000000000..7902b80548 --- /dev/null +++ b/shared/src/test/diff/nu/RawUnionTraitSignatures.mls @@ -0,0 +1,268 @@ +:NewDefs + + + +trait Foo[A] { fun x: A } +//│ trait Foo[A] { +//│ fun x: A +//│ } + + +trait Base1: Foo +//│ trait Base1: #Foo + +(b: Base1) => b.x +//│ (b: Base1) -> ??A +//│ res +//│ = [Function: res] + +(b: Base1) => b : Foo +//│ (b: Base1) -> #Foo +//│ res +//│ = [Function: res] + +:e +(b: Base1) => b : Foo['X] +//│ ╔══[ERROR] Type error in type ascription +//│ ║ l.25: (b: Base1) => b : Foo['X] +//│ ║ ^ +//│ ╟── type variable `A` leaks out of its scope +//│ ║ l.25: (b: Base1) => b : Foo['X] +//│ ║ ^^ +//│ ╟── back into type variable `A` +//│ ║ l.5: trait Foo[A] { fun x: A } +//│ ╙── ^ +//│ forall 'X. (b: Base1) -> Foo['X] +//│ where +//│ 'X :> ??A +//│ <: ??A0 +//│ res +//│ = [Function: res] + +:e +1 : Foo[Int] +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.43: 1 : Foo[Int] +//│ ║ ^ +//│ ╟── integer literal of type `1` is not an instance of type `Foo` +//│ ╟── Note: constraint arises from applied type reference: +//│ ║ l.43: 1 : Foo[Int] +//│ ╙── ^^^^^^^^ +//│ Foo[Int] +//│ res +//│ = 1 + + +trait Base1: Foo { val x: Int } +//│ trait Base1: #Foo { +//│ val x: Int +//│ } + +(b: Base1) => b.x +//│ (b: Base1) -> (Int & ??A) +//│ res +//│ = [Function: res] + + +trait Base1: Foo[1 | 2] { val x: 0 | 1 } +//│ trait Base1: Foo[1 | 2] { +//│ val x: 0 | 1 +//│ } + +(b: Base1) => b.x +//│ (b: Base1) -> 1 +//│ res +//│ = [Function: res] + + +trait Base2: Foo['FigureItOut] +//│ trait Base2: Foo[in ??FigureItOut out ??FigureItOut0] + +(b: Base2) => b.x +//│ (b: Base2) -> ??FigureItOut +//│ res +//│ = [Function: res] + +(b: Base1) => b : Foo +//│ (b: Base1) -> #Foo +//│ res +//│ = [Function: res] + +// :e +(b: Base2) => b : Foo['X] +//│ forall 'X. (b: Base2) -> Foo['X] +//│ where +//│ 'X :> ??FigureItOut +//│ <: ??FigureItOut0 +//│ res +//│ = [Function: res] + + +:e +class Impl extends Base2 +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.102: class Impl extends Base2 +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── expression of type `#Impl` is not an instance of type `Foo` +//│ ╟── Note: constraint arises from applied type reference: +//│ ║ l.78: trait Base2: Foo['FigureItOut] +//│ ╙── ^^^^^^^^^^^^^^^^^ +//│ ╔══[ERROR] Type `#Impl` does not contain member `Foo#A` +//│ ║ l.5: trait Foo[A] { fun x: A } +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.102: class Impl extends Base2 +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── expression of type `#Impl` does not have field 'Foo#A' +//│ ╟── Note: constraint arises from applied type reference: +//│ ║ l.78: trait Base2: Foo['FigureItOut] +//│ ╙── ^^^^^^^^^^^^^^^^^ +//│ class Impl extends Base2 { +//│ constructor() +//│ } + +:e +(x: Impl) => x : Base2 +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.125: (x: Impl) => x : Base2 +//│ ║ ^ +//│ ╟── type `Impl` is not an instance of type `Foo` +//│ ║ l.125: (x: Impl) => x : Base2 +//│ ║ ^^^^ +//│ ╟── but it flows into reference with expected type `#Foo` +//│ ║ l.125: (x: Impl) => x : Base2 +//│ ║ ^ +//│ ╟── Note: constraint arises from applied type reference: +//│ ║ l.78: trait Base2: Foo['FigureItOut] +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ╟── from type reference: +//│ ║ l.125: (x: Impl) => x : Base2 +//│ ╙── ^^^^^ +//│ (x: Impl) -> Base2 +//│ res +//│ = [Function: res] + +:e +class Impl() extends Base2, Foo +//│ ╔══[ERROR] Type error in type declaration +//│ ║ l.146: class Impl() extends Base2, Foo +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type variable `'FigureItOut` leaks out of its scope +//│ ║ l.78: trait Base2: Foo['FigureItOut] +//│ ╙── ^^^^^^^^^^^^ +//│ ╔══[ERROR] Member `x` is declared (or its declaration is inherited) but is not implemented in `Impl` +//│ ║ l.146: class Impl() extends Base2, Foo +//│ ║ ^^^^ +//│ ╟── Declared here: +//│ ║ l.5: trait Foo[A] { fun x: A } +//│ ╙── ^^^^^^^^ +//│ class Impl() extends Base2, Foo { +//│ fun x: 'A +//│ } + +:e +class Impl() extends Base2, Foo { + fun x = 1 +} +//│ ╔══[ERROR] Type error in type declaration +//│ ║ l.164: class Impl() extends Base2, Foo { +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.165: fun x = 1 +//│ ║ ^^^^^^^^^^^ +//│ ║ l.166: } +//│ ║ ^ +//│ ╟── type variable `'FigureItOut` leaks out of its scope +//│ ║ l.78: trait Base2: Foo['FigureItOut] +//│ ╙── ^^^^^^^^^^^^ +//│ class Impl() extends Base2, Foo { +//│ fun x: 1 +//│ } + +Impl().x +//│ 1 +//│ res +//│ = 1 + +:e +Impl() : Base2 +//│ ╔══[ERROR] Type error in type ascription +//│ ║ l.187: Impl() : Base2 +//│ ║ ^^^^^^ +//│ ╟── type variable `'FigureItOut` leaks out of its scope +//│ ║ l.165: fun x = 1 +//│ ╙── ^ +//│ Base2 +//│ res +//│ = Impl {} + +:e +(Impl() : Base2).x +//│ ╔══[ERROR] Type error in type ascription +//│ ║ l.199: (Impl() : Base2).x +//│ ║ ^^^^^^ +//│ ╟── type variable `'FigureItOut` leaks out of its scope +//│ ║ l.165: fun x = 1 +//│ ╙── ^ +//│ ??FigureItOut +//│ res +//│ = 1 + +:e +class Impl2() extends Base2, Foo[Int] { + fun x = 1 +} +//│ ╔══[ERROR] Type error in type declaration +//│ ║ l.211: class Impl2() extends Base2, Foo[Int] { +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.212: fun x = 1 +//│ ║ ^^^^^^^^^^^ +//│ ║ l.213: } +//│ ║ ^ +//│ ╟── type variable `'FigureItOut` leaks out of its scope +//│ ║ l.211: class Impl2() extends Base2, Foo[Int] { +//│ ╙── ^^^ +//│ ╔══[ERROR] Type error in type declaration +//│ ║ l.211: class Impl2() extends Base2, Foo[Int] { +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.212: fun x = 1 +//│ ║ ^^^^^^^^^^^ +//│ ║ l.213: } +//│ ║ ^ +//│ ╟── type variable `'FigureItOut` leaks out of its scope +//│ ║ l.78: trait Base2: Foo['FigureItOut] +//│ ║ ^^^^^^^^^^^^ +//│ ╟── into type `Int` +//│ ║ l.211: class Impl2() extends Base2, Foo[Int] { +//│ ╙── ^^^ +//│ class Impl2() extends Base2, Foo { +//│ fun x: 1 +//│ } + +:e +(Impl2() : Base2).x +//│ ╔══[ERROR] Type error in type ascription +//│ ║ l.242: (Impl2() : Base2).x +//│ ║ ^^^^^^^ +//│ ╟── type variable `'FigureItOut` leaks out of its scope +//│ ║ l.211: class Impl2() extends Base2, Foo[Int] { +//│ ╙── ^^^ +//│ ??FigureItOut +//│ res +//│ = 1 + + + +trait Test1[A] { fun x: A } +trait Test2[A]: Test1[[A, A]] +//│ trait Test1[A] { +//│ fun x: A +//│ } +//│ trait Test2[A]: Test1[[A, A]] + +(t: Test2[Int]) => t.x +//│ (t: Test2[Int]) -> [Int, Int] +//│ res +//│ = [Function: res] + + + diff --git a/shared/src/test/diff/nu/Refinements.mls b/shared/src/test/diff/nu/Refinements.mls new file mode 100644 index 0000000000..53a4e19145 --- /dev/null +++ b/shared/src/test/diff/nu/Refinements.mls @@ -0,0 +1,184 @@ +:NewDefs + + +class D() { fun f = 0 } +//│ class D() { +//│ fun f: 0 +//│ } + +D() : D & Object +//│ D +//│ res +//│ = D {} + +let d = D() : D & { f: 0 } +//│ let d: D & {f: 0} +//│ d +//│ = D {} + +let c = d with { f: 1 } +//│ let c: D\f & {f: 1} +//│ c +//│ = D { f: 1 } + +c : D & { f: 1 } +//│ D & {f: 1} +//│ res +//│ = D { f: 1 } + + +class C[A](val a: A) extends D() +//│ class C[A](a: A) extends D { +//│ fun f: 0 +//│ } + +C(1) +//│ C['A] +//│ where +//│ 'A :> 1 +//│ res +//│ = C {} + +let c: C & {a: 1, f: 0} = C(1) +//│ let c: C[anything] & {a: 1, f: 0} +//│ c +//│ = C {} + +c.f +//│ 0 +//│ res +//│ = 0 + + +let r = {} +//│ let r: anything +//│ r +//│ = {} + +// * Not all records are objects! +// * Objects are class instances. +// * But other values, like functions, can also hold record fields... +// * This is actually important in JS, where some library functions act like modules! +:e +r : Object +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.63: r : Object +//│ ║ ^ +//│ ╟── reference of type `anything` is not an instance of type `Object` +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.63: r : Object +//│ ╙── ^^^^^^ +//│ Object +//│ res +//│ = {} + +r with { x: 1 } +//│ {x: 1} +//│ res +//│ = { x: 1 } + + +:e // TODO support +let r = new {} +//│ ╔══[ERROR] Unexpected type `anything` after `new` keyword +//│ ║ l.82: let r = new {} +//│ ╙── ^^ +//│ let r: error +//│ Code generation encountered an error: +//│ Unsupported `new` class term: Bra(true,Rcd(List())) + +:ge +r : Object +//│ Object +//│ Code generation encountered an error: +//│ unguarded recursive use of by-value binding r + +:ge +r with { x: 1 } +//│ error & {x: 1} +//│ Code generation encountered an error: +//│ unguarded recursive use of by-value binding r + + +let r = { x: 0 } +//│ let r: {x: 0} +//│ r +//│ = { x: 0 } + +:e +r : Object +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.109: r : Object +//│ ║ ^ +//│ ╟── record literal of type `{x: 0}` is not an instance of type `Object` +//│ ║ l.103: let r = { x: 0 } +//│ ║ ^ +//│ ╟── but it flows into reference with expected type `Object` +//│ ║ l.109: r : Object +//│ ║ ^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.109: r : Object +//│ ╙── ^^^^^^ +//│ Object +//│ res +//│ = { x: 0 } + +r with { x: 1 } +//│ {x: 1} +//│ res +//│ = { x: 1 } + + + +:NoJS + + +fun o : {} +//│ fun o: anything + +o : {} +//│ anything + +:e +o : Object +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.143: o : Object +//│ ║ ^ +//│ ╟── type `anything` is not an instance of type `Object` +//│ ║ l.136: fun o : {} +//│ ║ ^^ +//│ ╟── but it flows into reference with expected type `Object` +//│ ║ l.143: o : Object +//│ ║ ^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.143: o : Object +//│ ╙── ^^^^^^ +//│ Object + + +fun o : Object & {} +//│ fun o: Object + +o : {} +//│ anything + +o : Object +//│ Object + + + +// === // + + +:e +let d = D & { f: 0 } +//│ ╔══[ERROR] Illegal use of reserved operator: & +//│ ║ l.174: let d = D & { f: 0 } +//│ ╙── ^ +//│ ╔══[ERROR] identifier not found: & +//│ ║ l.174: let d = D & { f: 0 } +//│ ╙── ^ +//│ let d: error + + + diff --git a/shared/src/test/diff/nu/Res.mls b/shared/src/test/diff/nu/Res.mls new file mode 100644 index 0000000000..0f7f660b69 --- /dev/null +++ b/shared/src/test/diff/nu/Res.mls @@ -0,0 +1,37 @@ +:NewDefs + + +x => x + 2 +//│ Int -> Int +//│ res +//│ = [Function: res] + +:e +res(1) +//│ ╔══[ERROR] identifier not found: res +//│ ║ l.10: res(1) +//│ ╙── ^^^ +//│ error +//│ res +//│ = 3 + + +let res = x => x + 2 +//│ let res: Int -> Int +//│ res +//│ = [Function: res1] + +res(1) +//│ Int +//│ res +//│ = 3 + +// FIXME `res` not accounted in type context +:re +res(1) +//│ Int +//│ res +//│ Runtime error: +//│ TypeError: res is not a function + + diff --git a/shared/src/test/diff/nu/RightAssocOps.mls b/shared/src/test/diff/nu/RightAssocOps.mls new file mode 100644 index 0000000000..b692e7fb06 --- /dev/null +++ b/shared/src/test/diff/nu/RightAssocOps.mls @@ -0,0 +1,75 @@ +:NewDefs + + +// * Operators that end with `:` are right-associative + + +fun (+:) pre(x: Int, xs) = [[x], xs] +fun (:+) post(xs, x: Int) = [xs, [x]] +fun (++) conc(xs, ys) = [xs, ys] +//│ fun (+:) pre: forall 'a. (x: Int, 'a) -> [[Int], 'a] +//│ fun (:+) post: forall 'b. ('b, x: Int) -> ['b, [Int]] +//│ fun (++) conc: forall 'c 'd. ('c, 'd) -> ['c, 'd] + + +1 +: 2 +: 3 +: [] +//│ [[Int], [[Int], [[Int], []]]] +//│ res +//│ = [ [ 1 ], [ [ 2 ], [ [Array], [] ] ] ] + +[] :+ 1 :+ 2 :+ 3 +//│ [[[[], [Int]], [Int]], [Int]] +//│ res +//│ = [ [ [ [], [Array] ], [ 2 ] ], [ 3 ] ] + +[1, 2, 3] ++ [4, 5, 6] +//│ [[1, 2, 3], [4, 5, 6]] +//│ res +//│ = [ [ 1, 2, 3 ], [ 4, 5, 6 ] ] + +:p +1 +: "a" ++ "b" :+ 2 +//│ |1| |+:| |"a"| |++| |"b"| |:+| |2| +//│ AST: TypingUnit(List(App(Var(+:),Tup(List((None,Fld(_,IntLit(1))), (None,Fld(_,App(Var(:+),Tup(List((None,Fld(_,App(Var(++),Tup(List((None,Fld(_,StrLit(a))), (None,Fld(_,StrLit(b)))))))), (None,Fld(_,IntLit(2))))))))))))) +//│ Parsed: +:(1, :+(++("a", "b",), 2,),); +//│ [[Int], [["a", "b"], [Int]]] +//│ res +//│ = [ [ 1 ], [ [ 'a', 'b' ], [ 2 ] ] ] + +:p +1 +: "a" :+ 2 ++ "b" +//│ |1| |+:| |"a"| |:+| |2| |++| |"b"| +//│ AST: TypingUnit(List(App(Var(+:),Tup(List((None,Fld(_,IntLit(1))), (None,Fld(_,App(Var(++),Tup(List((None,Fld(_,App(Var(:+),Tup(List((None,Fld(_,StrLit(a))), (None,Fld(_,IntLit(2)))))))), (None,Fld(_,StrLit(b))))))))))))) +//│ Parsed: +:(1, ++(:+("a", 2,), "b",),); +//│ [[Int], [["a", [Int]], "b"]] +//│ res +//│ = [ [ 1 ], [ [ 'a', [Array] ], 'b' ] ] + +:p +:e +1 +: "a" ++ 2 +: "b" +//│ |1| |+:| |"a"| |++| |2| |+:| |"b"| +//│ AST: TypingUnit(List(App(Var(+:),Tup(List((None,Fld(_,IntLit(1))), (None,Fld(_,App(Var(+:),Tup(List((None,Fld(_,App(Var(++),Tup(List((None,Fld(_,StrLit(a))), (None,Fld(_,IntLit(2)))))))), (None,Fld(_,StrLit(b))))))))))))) +//│ Parsed: +:(1, +:(++("a", 2,), "b",),); +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.50: 1 +: "a" ++ 2 +: "b" +//│ ║ ^^^^^^^^^^^^^^^ +//│ ╟── tuple literal of type `[?a, ?b]` is not an instance of type `Int` +//│ ║ l.9: fun (++) conc(xs, ys) = [xs, ys] +//│ ║ ^^^^^^^^ +//│ ╟── but it flows into operator application with expected type `Int` +//│ ║ l.50: 1 +: "a" ++ 2 +: "b" +//│ ║ ^^^^^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.7: fun (+:) pre(x: Int, xs) = [[x], xs] +//│ ╙── ^^^ +//│ [[Int], error | [[Int], "b"]] +//│ res +//│ = [ [ 1 ], [ [ [Array] ], 'b' ] ] + +1 +: "a" ++ (2 +: "b") +//│ [[Int], ["a", [[Int], "b"]]] +//│ res +//│ = [ [ 1 ], [ 'a', [ [Array], 'b' ] ] ] + + diff --git a/shared/src/test/diff/nu/RigidVariables.mls b/shared/src/test/diff/nu/RigidVariables.mls new file mode 100644 index 0000000000..12f4c4d28b --- /dev/null +++ b/shared/src/test/diff/nu/RigidVariables.mls @@ -0,0 +1,44 @@ +:NewDefs + + +// * Flexible + +fun f(x: 'test) = [x.b, x] +//│ fun f: forall 'b 'test. (x: {b: 'b} & 'test) -> ['b, 'test] + + +// * Rigid + +:e +fun f[A](x: A) = x.b +//│ ╔══[ERROR] Type `A` does not contain member `b` +//│ ║ l.13: fun f[A](x: A) = x.b +//│ ╙── ^^ +//│ fun f: (x: anything) -> error + +fun f[A](x: A & { b: ' }) = x.b +//│ fun f: forall 'b. (x: {b: 'b}) -> 'b + +:e +module Foo { + fun f[A](x: A) = x.b +} +//│ ╔══[ERROR] Type `A` does not contain member `b` +//│ ║ l.24: fun f[A](x: A) = x.b +//│ ╙── ^^ +//│ module Foo { +//│ fun f: (x: anything) -> error +//│ } + +:e +class Foo[A](x: A) { + fun f = x.b +} +//│ ╔══[ERROR] Type `A` does not contain member `b` +//│ ║ l.35: fun f = x.b +//│ ╙── ^^ +//│ class Foo[A](x: A) { +//│ fun f: error +//│ } + + diff --git a/shared/src/test/diff/nu/SelfAppMethods.mls b/shared/src/test/diff/nu/SelfAppMethods.mls new file mode 100644 index 0000000000..3ff6e78883 --- /dev/null +++ b/shared/src/test/diff/nu/SelfAppMethods.mls @@ -0,0 +1,94 @@ +:NewDefs + + +class A { + + fun f = f(f) + + fun g : A + fun g = g(g) // * FIXME not using the signature + +} +//│ class A { +//│ constructor() +//│ fun f: nothing +//│ fun g: A +//│ } + +:e +module A { + fun i(x) = x + fun f = f(f) + fun g(x) = x(x) + fun h = g(g) +} +//│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required +//│ ║ l.23: fun h = g(g) +//│ ║ ^^^^ +//│ ╙── Note: use flag `:ex` to see internal error info. +//│ module A { +//│ fun f: nothing +//│ fun g: forall 'a 'b. ('a -> 'b & 'a) -> 'b +//│ fun h: error +//│ fun i: forall 'c. 'c -> 'c +//│ } + +// * Note this permutation works: currently we generalize check functions in the source code order... +module A { + fun i(x) = x + fun f = f(f) + fun h = g(g) + fun g(x) = x(x) +} +//│ module A { +//│ fun f: nothing +//│ fun g: forall 'a 'b. 'b -> 'a +//│ fun h: forall 'a. 'a +//│ fun i: forall 'c. 'c -> 'c +//│ } +//│ where +//│ 'b := 'b -> 'a + +:ns +A.i +//│ 'i +//│ where +//│ 'i :> forall 'a. 'a -> 'a +//│ res +//│ = [Function: i] + +:re +:ns +A.f +//│ 'f +//│ where +//│ 'f :> forall 'f0 'a. 'a +//│ 'a <: 'f0 +//│ 'f0 <: 'f0 -> 'a +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +:ns +A.g +//│ 'g +//│ where +//│ 'g :> forall 'a 'b 'c 'h. 'a -> 'b +//│ 'a := 'a -> 'b +//│ 'b <: 'c +//│ 'c <: 'h +//│ res +//│ = [Function: g] + +:ns +:re +A.h +//│ 'h +//│ where +//│ 'h :> forall 'a 'h0. 'a +//│ 'a <: 'h0 +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + + diff --git a/shared/src/test/diff/nu/SelfRec.mls b/shared/src/test/diff/nu/SelfRec.mls new file mode 100644 index 0000000000..3a68af47cf --- /dev/null +++ b/shared/src/test/diff/nu/SelfRec.mls @@ -0,0 +1,257 @@ +:NewDefs + + + +class Foo1(val x: Int) { + fun test = Foo1(1).x +} +//│ class Foo1(x: Int) { +//│ fun test: Int +//│ } + +:e +class Foo1X(x: 0 | 1) extends Foo1(x) { + fun test2 = Foo1X(1).x +} +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.14: fun test2 = Foo1X(1).x +//│ ╙── ^^ +//│ ╔══[ERROR] Inherited parameter named `x` is not virtual and cannot be overridden +//│ ║ l.13: class Foo1X(x: 0 | 1) extends Foo1(x) { +//│ ║ ^ +//│ ╟── Originally declared here: +//│ ║ l.5: class Foo1(val x: Int) { +//│ ╙── ^ +//│ class Foo1X(x: 0 | 1) extends Foo1 { +//│ fun test: Int +//│ fun test2: error +//│ } + + +class Foo2[A](val x: A) { + fun test = Foo2(1).x +} +//│ class Foo2[A](x: A) { +//│ fun test: 1 +//│ } + +:e +class Foo2X(x: 0 | 1) extends Foo2(x) { + fun test2 = Foo2X(1).x +} +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.40: fun test2 = Foo2X(1).x +//│ ╙── ^^ +//│ ╔══[ERROR] Inherited parameter named `x` is not virtual and cannot be overridden +//│ ║ l.39: class Foo2X(x: 0 | 1) extends Foo2(x) { +//│ ║ ^ +//│ ╟── Originally declared here: +//│ ║ l.31: class Foo2[A](val x: A) { +//│ ╙── ^ +//│ class Foo2X(x: 0 | 1) extends Foo2 { +//│ fun test: 1 +//│ fun test2: error +//│ } + +:e +Foo2X(1).x +//│ ╔══[ERROR] Parameter 'x' cannot tbe accessed as a field +//│ ║ l.57: Foo2X(1).x +//│ ║ ^^ +//│ ╟── Either make the parameter a `val` or access it through destructuring +//│ ║ l.39: class Foo2X(x: 0 | 1) extends Foo2(x) { +//│ ╙── ^ +//│ 0 | 1 | error +//│ res +//│ = 1 + +:e // TODO improve type checking +class Foo2X(a: 0 | 1) extends Foo2(a) { + fun test2 = Foo2X(1).x +} +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.70: fun test2 = Foo2X(1).x +//│ ╙── ^^ +//│ class Foo2X(a: 0 | 1) extends Foo2 { +//│ fun test: 1 +//│ fun test2: error +//│ } + +Foo2X(1).x +//│ 0 | 1 +//│ res +//│ = 1 + + +class Foo3[A](val x: A) { + fun test = Foo3(1) + fun foo = Foo3 +} +//│ class Foo3[A](x: A) { +//│ fun foo: (x: A) -> Foo3[A] +//│ fun test: forall 'A. Foo3['A] +//│ } +//│ where +//│ 'A :> 1 + +Foo3 +//│ forall 'A. (x: 'A) -> Foo3['A] +//│ res +//│ = [Function (anonymous)] { +//│ class: [class Foo3], +//│ unapply: [Function: unapply] +//│ } + +Foo3(1) +//│ Foo3['A] +//│ where +//│ 'A :> 1 +//│ res +//│ = Foo3 {} + +Foo3(1).x +//│ 1 +//│ res +//│ = 1 + +Foo3(1).foo +//│ forall 'A. (x: 'A) -> Foo3['A] +//│ res +//│ = [Function (anonymous)] { +//│ class: [class Foo3], +//│ unapply: [Function: unapply] +//│ } + +:e +class Foo4 { + fun test = [Foo4.test] +} +//│ ╔══[ERROR] Class Foo4 cannot be instantiated as it exposes no constructor +//│ ║ l.127: fun test = [Foo4.test] +//│ ╙── ^^^^ +//│ class Foo4 { +//│ constructor() +//│ fun test: [error] +//│ } + +:e +class Foo5(x: Int) { + fun test = [Foo5(5).test] +} +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.139: fun test = [Foo5(5).test] +//│ ╙── ^^^^^ +//│ class Foo5(x: Int) { +//│ fun test: [error] +//│ } + +:e +class Foo6[A](x: A) { + fun test1 = [Foo6(x).test1] + fun test2 = [Foo6(123).test2] + fun test3 = [Foo6([x]).test3] +} +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.150: fun test1 = [Foo6(x).test1] +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.151: fun test2 = [Foo6(123).test2] +//│ ╙── ^^^^^^ +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.152: fun test3 = [Foo6([x]).test3] +//│ ╙── ^^^^^^ +//│ class Foo6[A](x: A) { +//│ fun test1: [error] +//│ fun test2: [error] +//│ fun test3: [error] +//│ } + +module N +//│ module N + +:e +class Foo7[A](head: A, tail: Foo7[A] | N) { + fun test1 = if tail is + N then head + _ then tail.test1 +} +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.176: _ then tail.test1 +//│ ╙── ^^^^^^ +//│ class Foo7[A](head: A, tail: Foo7[A] | N) { +//│ fun test1: error | A +//│ } + +class Foo7_A[A](head: A, tail: Foo7_A[A] | N) { + fun test1: A + fun test1 = if tail is + N then head + _ then tail.test1 +} +//│ class Foo7_A[A](head: A, tail: Foo7_A[A] | N) { +//│ fun test1: A +//│ } + +class Foo7_A2[A](head: A, tail: Foo7_A[A] | N) { + fun test1: A = if tail is + N then head + _ then tail.test1 +} +//│ class Foo7_A2[A](head: A, tail: Foo7_A[A] | N) { +//│ fun test1: A +//│ } + +class Foo7_2_A[A](head: A, tail: Foo7_A[Int] | N) { + fun test1: Int | A = if tail is + N then head + _ then tail.test1 +} +//│ class Foo7_2_A[A](head: A, tail: Foo7_A[Int] | N) { +//│ fun test1: Int | A +//│ } + +:e +class Foo8[A](x: A) { + fun test1[B](y: B): A = + let tmp = Foo8(y).test1(x) + x +} +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.216: let tmp = Foo8(y).test1(x) +//│ ╙── ^^^^^^ +//│ class Foo8[A](x: A) { +//│ fun test1: (y: anything) -> A +//│ } + + + + +:e // * FIXME this is caused by the self-annotation... +abstract class List(val length: Int): Cons +class Cons(tail: List) extends List(tail.length + 1) +//│ ╔══[ERROR] Unhandled cyclic parent specification +//│ ║ l.231: class Cons(tail: List) extends List(tail.length + 1) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.231: class Cons(tail: List) extends List(tail.length + 1) +//│ ╙── ^^^^^^^ +//│ abstract class List(length: Int): Cons +//│ class Cons(tail: List) extends List + +// * Note: full (non-minimized) definitions: + +:e // * FIXME +abstract class List[out A](val length: Int): Cons[A] | Nil +class Cons[out A](val head: A, val tail: List[A]) extends List[A](tail.length + 1) +module Nil extends List[nothing](0) +//│ ╔══[ERROR] Unhandled cyclic parent specification +//│ ║ l.245: class Cons[out A](val head: A, val tail: List[A]) extends List[A](tail.length + 1) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.245: class Cons[out A](val head: A, val tail: List[A]) extends List[A](tail.length + 1) +//│ ╙── ^^^^^^^ +//│ abstract class List[A](length: Int): Cons[A] | Nil +//│ class Cons[A](head: A, tail: List[A]) extends List +//│ module Nil extends List + + diff --git a/shared/src/test/diff/nu/SimpleSymbolicOps.mls b/shared/src/test/diff/nu/SimpleSymbolicOps.mls new file mode 100644 index 0000000000..6910d6fbf2 --- /dev/null +++ b/shared/src/test/diff/nu/SimpleSymbolicOps.mls @@ -0,0 +1,20 @@ +:NewDefs + + +fun (-) i(x, y) = x +//│ fun (-) i: forall 'a. ('a, anything) -> 'a + +1 - 1 +//│ 1 +//│ res +//│ = 1 + + +fun (-) i(x, y) = x +[i(1,1), i(2,1), 1 - 1, 2 - 1] +//│ fun (-) i: forall 'a. ('a, anything) -> 'a +//│ [1, 2, 1, 2] +//│ res +//│ = [ 1, 2, 1, 2 ] + + diff --git a/shared/src/test/diff/nu/SimpleTraitImpl.mls b/shared/src/test/diff/nu/SimpleTraitImpl.mls new file mode 100644 index 0000000000..ff7a366dc8 --- /dev/null +++ b/shared/src/test/diff/nu/SimpleTraitImpl.mls @@ -0,0 +1,436 @@ +:NewDefs + +:NoJS // TODO enable JS + + + +trait T1 { fun x: 0 | 1 } +trait T2 { fun x: 1 | 2 } +//│ trait T1 { +//│ fun x: 0 | 1 +//│ } +//│ trait T2 { +//│ fun x: 1 | 2 +//│ } + +abstract class C1 { fun x: 0 | 2 } +//│ abstract class C1 { +//│ fun x: 0 | 2 +//│ } + +:e +module M extends C1, T1 { + fun x = 2 +} +//│ ╔══[ERROR] Type mismatch in definition of method x: +//│ ║ l.23: fun x = 2 +//│ ║ ^^^^^ +//│ ╟── integer literal of type `2` does not match type `0 | 1` +//│ ║ l.23: fun x = 2 +//│ ║ ^ +//│ ╟── but it flows into definition of method x with expected type `0 | 1` +//│ ║ l.23: fun x = 2 +//│ ║ ^^^^^ +//│ ╟── Note: constraint arises from union type: +//│ ║ l.7: trait T1 { fun x: 0 | 1 } +//│ ║ ^^^^^ +//│ ╟── from signature of member `x`: +//│ ║ l.7: trait T1 { fun x: 0 | 1 } +//│ ╙── ^^^^^^^^ +//│ module M extends C1, T1 { +//│ fun x: 2 +//│ } + +abstract class C2 extends C1, T1 +//│ abstract class C2 extends C1, T1 { +//│ fun x: 0 +//│ } + +:e +module M extends C2 { + fun x = 2 +} +//│ ╔══[ERROR] Type mismatch in definition of method x: +//│ ║ l.51: fun x = 2 +//│ ║ ^^^^^ +//│ ╟── integer literal of type `2` does not match type `0 | 1` +//│ ║ l.51: fun x = 2 +//│ ║ ^ +//│ ╟── but it flows into definition of method x with expected type `0 | 1` +//│ ║ l.51: fun x = 2 +//│ ║ ^^^^^ +//│ ╟── Note: constraint arises from union type: +//│ ║ l.7: trait T1 { fun x: 0 | 1 } +//│ ║ ^^^^^ +//│ ╟── from signature of member `x`: +//│ ║ l.7: trait T1 { fun x: 0 | 1 } +//│ ╙── ^^^^^^^^ +//│ module M extends C1, C2, T1 { +//│ fun x: 2 +//│ } + +class C1 { virtual fun x: 0 | 2 = 0 } +//│ class C1 { +//│ constructor() +//│ fun x: 0 | 2 +//│ } + + + +:e +module M extends C1, T1 +//│ ╔══[ERROR] Type mismatch in definition of method x: +//│ ║ l.72: class C1 { virtual fun x: 0 | 2 = 0 } +//│ ║ ^^^^^^^^^^^^ +//│ ╟── type `2` does not match type `0 | 1` +//│ ║ l.72: class C1 { virtual fun x: 0 | 2 = 0 } +//│ ║ ^ +//│ ╟── but it flows into union type with expected type `0 | 1` +//│ ║ l.72: class C1 { virtual fun x: 0 | 2 = 0 } +//│ ║ ^^^^^ +//│ ╟── Note: constraint arises from union type: +//│ ║ l.7: trait T1 { fun x: 0 | 1 } +//│ ║ ^^^^^ +//│ ╟── from signature of member `x`: +//│ ║ l.7: trait T1 { fun x: 0 | 1 } +//│ ╙── ^^^^^^^^ +//│ module M extends C1, T1 { +//│ fun x: 0 | 2 +//│ } + +:e +module M extends T1, C1 +//│ ╔══[ERROR] Type mismatch in definition of method x: +//│ ║ l.72: class C1 { virtual fun x: 0 | 2 = 0 } +//│ ║ ^^^^^^^^^^^^ +//│ ╟── type `2` does not match type `0 | 1` +//│ ║ l.72: class C1 { virtual fun x: 0 | 2 = 0 } +//│ ║ ^ +//│ ╟── but it flows into union type with expected type `0 | 1` +//│ ║ l.72: class C1 { virtual fun x: 0 | 2 = 0 } +//│ ║ ^^^^^ +//│ ╟── Note: constraint arises from union type: +//│ ║ l.7: trait T1 { fun x: 0 | 1 } +//│ ║ ^^^^^ +//│ ╟── from signature of member `x`: +//│ ║ l.7: trait T1 { fun x: 0 | 1 } +//│ ╙── ^^^^^^^^ +//│ module M extends C1, T1 { +//│ fun x: 0 | 2 +//│ } + +:e +module M extends T1, T2, C1 { + fun x = this.x +} +//│ ╔══[ERROR] Indirectly-recursive member should have type annotation +//│ ║ l.124: fun x = this.x +//│ ╙── ^^ +//│ module M extends C1, T1, T2 { +//│ fun x: error +//│ } + +:e +module M extends T1, T2, C1 { + fun x: 0 + fun x = this.x +} +//│ ╔══[ERROR] Type mismatch in signature of member `x`: +//│ ║ l.135: fun x: 0 +//│ ║ ^^^^ +//│ ╟── type `0` does not match type `1 | 2` +//│ ║ l.135: fun x: 0 +//│ ║ ^ +//│ ╟── but it flows into signature of member `x` with expected type `1 | 2` +//│ ║ l.135: fun x: 0 +//│ ║ ^^^^ +//│ ╟── Note: constraint arises from union type: +//│ ║ l.8: trait T2 { fun x: 1 | 2 } +//│ ║ ^^^^^ +//│ ╟── from signature of member `x`: +//│ ║ l.8: trait T2 { fun x: 1 | 2 } +//│ ╙── ^^^^^^^^ +//│ module M extends C1, T1, T2 { +//│ fun x: 0 +//│ } + +class C extends C1, T2 { + virtual fun x: 2 + fun x = this.x +} +//│ class C extends C1, T2 { +//│ constructor() +//│ fun x: 2 +//│ } + +module M extends C { + fun x = 2 +} +M.x +//│ module M extends C, C1, T2 { +//│ fun x: 2 +//│ } +//│ 2 + + +:e +class C2 extends T1 +//│ ╔══[ERROR] Member `x` is declared (or its declaration is inherited) but is not implemented in `C2` +//│ ║ l.177: class C2 extends T1 +//│ ║ ^^ +//│ ╟── Declared here: +//│ ║ l.7: trait T1 { fun x: 0 | 1 } +//│ ╙── ^^^^^^^^^^^^ +//│ class C2 extends T1 { +//│ constructor() +//│ fun x: 0 | 1 +//│ } + +abstract class C2 extends T1 +//│ abstract class C2 extends T1 { +//│ fun x: 0 | 1 +//│ } + +:e +class C3 extends C2 +//│ ╔══[ERROR] Member `x` is declared (or its declaration is inherited) but is not implemented in `C3` +//│ ║ l.195: class C3 extends C2 +//│ ║ ^^ +//│ ╟── Declared here: +//│ ║ l.7: trait T1 { fun x: 0 | 1 } +//│ ╙── ^^^^^^^^^^^^ +//│ class C3 extends C2, T1 { +//│ constructor() +//│ fun x: 0 | 1 +//│ } + +abstract class C3 extends C2 +//│ abstract class C3 extends C2, T1 { +//│ fun x: 0 | 1 +//│ } + +class C2 extends T1 { fun x = 1 } +//│ class C2 extends T1 { +//│ constructor() +//│ fun x: 1 +//│ } + +:e +class C2 extends T1, T2 { fun x = 2 } +//│ ╔══[ERROR] Type mismatch in definition of method x: +//│ ║ l.219: class C2 extends T1, T2 { fun x = 2 } +//│ ║ ^^^^^ +//│ ╟── integer literal of type `2` does not match type `0 | 1` +//│ ║ l.219: class C2 extends T1, T2 { fun x = 2 } +//│ ║ ^ +//│ ╟── but it flows into definition of method x with expected type `0 | 1` +//│ ║ l.219: class C2 extends T1, T2 { fun x = 2 } +//│ ║ ^^^^^ +//│ ╟── Note: constraint arises from union type: +//│ ║ l.7: trait T1 { fun x: 0 | 1 } +//│ ║ ^^^^^ +//│ ╟── from signature of member `x`: +//│ ║ l.7: trait T1 { fun x: 0 | 1 } +//│ ╙── ^^^^^^^^ +//│ class C2 extends T1, T2 { +//│ constructor() +//│ fun x: 2 +//│ } + +class C2 extends T1, T2 { virtual fun x = 1 } +//│ class C2 extends T1, T2 { +//│ constructor() +//│ fun x: 1 +//│ } + +:e +class C3 extends C2 { virtual fun x = 111 } +//│ ╔══[ERROR] Type mismatch in definition of method x: +//│ ║ l.247: class C3 extends C2 { virtual fun x = 111 } +//│ ║ ^^^^^^^ +//│ ╟── integer literal of type `111` does not match type `1` +//│ ║ l.247: class C3 extends C2 { virtual fun x = 111 } +//│ ║ ^^^ +//│ ╟── but it flows into definition of method x with expected type `1` +//│ ║ l.247: class C3 extends C2 { virtual fun x = 111 } +//│ ║ ^^^^^^^ +//│ ╟── Note: constraint arises from integer literal: +//│ ║ l.240: class C2 extends T1, T2 { virtual fun x = 1 } +//│ ║ ^ +//│ ╟── from definition of method x: +//│ ║ l.240: class C2 extends T1, T2 { virtual fun x = 1 } +//│ ╙── ^^^^^ +//│ class C3 extends C2, T1, T2 { +//│ constructor() +//│ fun x: 111 +//│ } + +class C3 extends C2 { fun x = 1 } +//│ class C3 extends C2, T1, T2 { +//│ constructor() +//│ fun x: 1 +//│ } + +class C2 extends C1, T1 { fun x = 0 } +//│ class C2 extends C1, T1 { +//│ constructor() +//│ fun x: 0 +//│ } + +class C2 extends T1, C1 { fun x = 0 } +//│ class C2 extends C1, T1 { +//│ constructor() +//│ fun x: 0 +//│ } + +:e +class C2 extends C1, T1 { fun x = 1 } +//│ ╔══[ERROR] Type mismatch in definition of method x: +//│ ║ l.287: class C2 extends C1, T1 { fun x = 1 } +//│ ║ ^^^^^ +//│ ╟── integer literal of type `1` does not match type `0 | 2` +//│ ║ l.287: class C2 extends C1, T1 { fun x = 1 } +//│ ║ ^ +//│ ╟── but it flows into definition of method x with expected type `0 | 2` +//│ ║ l.287: class C2 extends C1, T1 { fun x = 1 } +//│ ║ ^^^^^ +//│ ╟── Note: constraint arises from union type: +//│ ║ l.72: class C1 { virtual fun x: 0 | 2 = 0 } +//│ ║ ^^^^^ +//│ ╟── from definition of method x: +//│ ║ l.72: class C1 { virtual fun x: 0 | 2 = 0 } +//│ ╙── ^^^^^^^^^^^^ +//│ class C2 extends C1, T1 { +//│ constructor() +//│ fun x: 1 +//│ } + +:e +class C2 extends T1, C1 { fun x = 1 } +//│ ╔══[ERROR] Type mismatch in definition of method x: +//│ ║ l.309: class C2 extends T1, C1 { fun x = 1 } +//│ ║ ^^^^^ +//│ ╟── integer literal of type `1` does not match type `0 | 2` +//│ ║ l.309: class C2 extends T1, C1 { fun x = 1 } +//│ ║ ^ +//│ ╟── but it flows into definition of method x with expected type `0 | 2` +//│ ║ l.309: class C2 extends T1, C1 { fun x = 1 } +//│ ║ ^^^^^ +//│ ╟── Note: constraint arises from union type: +//│ ║ l.72: class C1 { virtual fun x: 0 | 2 = 0 } +//│ ║ ^^^^^ +//│ ╟── from definition of method x: +//│ ║ l.72: class C1 { virtual fun x: 0 | 2 = 0 } +//│ ╙── ^^^^^^^^^^^^ +//│ class C2 extends C1, T1 { +//│ constructor() +//│ fun x: 1 +//│ } + + + +:e +trait T2 { val r = 1(1) } +//│ ╔══[ERROR] Method implementations in traits are not yet supported +//│ ║ l.333: trait T2 { val r = 1(1) } +//│ ╙── ^^^^^^^^^^^^ +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.333: trait T2 { val r = 1(1) } +//│ ║ ^^^^ +//│ ╟── integer literal of type `1` is not a function +//│ ║ l.333: trait T2 { val r = 1(1) } +//│ ╙── ^ +//│ trait T2 { +//│ val r: error +//│ } + +class C2 extends T2 +//│ class C2 extends T2 { +//│ constructor() +//│ val r: error +//│ } + + +:e +trait T3[A] { + val r = C2().x +} +class C2 extends T3[Int] +//│ ╔══[ERROR] Method implementations in traits are not yet supported +//│ ║ l.356: val r = C2().x +//│ ╙── ^^^^^^^^^^^^^^ +//│ ╔══[ERROR] Class C2 cannot be instantiated as it exposes no constructor +//│ ║ l.356: val r = C2().x +//│ ╙── ^^ +//│ trait T3[A] { +//│ val r: error +//│ } +//│ class C2 extends T3 { +//│ constructor() +//│ val r: error +//│ } + +:e // * Note: lack of hygiene... happens only if class shadows previous C2 and is part of the error-throwing block +C2() : T3['X] +//│ ╔══[ERROR] Construction of unparameterized class C2 should use the `new` keyword +//│ ║ l.374: C2() : T3['X] +//│ ╙── ^^ +//│ T3[Int] + +class C3 extends T3[Int] +//│ class C3 extends T3 { +//│ constructor() +//│ val r: error +//│ } + +new C3 : T3['X] +//│ T3[Int] + + + +trait Foo { fun foo: forall 'A: (x: 'A) -> 'A } +//│ trait Foo { +//│ fun foo: forall 'A. (x: 'A) -> 'A +//│ } + +class B extends Foo { fun foo = error } +//│ class B extends Foo { +//│ constructor() +//│ fun foo: nothing +//│ } + +class B extends Foo { fun foo(x) = x } +//│ class B extends Foo { +//│ constructor() +//│ fun foo: forall 'a. 'a -> 'a +//│ } + +:e +class B extends Foo { fun foo(x) = x + 1 } +//│ ╔══[ERROR] Type mismatch in definition of method foo: +//│ ║ l.409: class B extends Foo { fun foo(x) = x + 1 } +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── type `'A` is not an instance of type `Int` +//│ ║ l.391: trait Foo { fun foo: forall 'A: (x: 'A) -> 'A } +//│ ║ ^^ +//│ ╟── Note: constraint arises from reference: +//│ ║ l.409: class B extends Foo { fun foo(x) = x + 1 } +//│ ║ ^ +//│ ╟── Note: quantified type variable 'A is defined at: +//│ ║ l.391: trait Foo { fun foo: forall 'A: (x: 'A) -> 'A } +//│ ╙── ^^ +//│ ╔══[ERROR] Type mismatch in definition of method foo: +//│ ║ l.409: class B extends Foo { fun foo(x) = x + 1 } +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── operator application of type `Int` does not match type `'A` +//│ ║ l.409: class B extends Foo { fun foo(x) = x + 1 } +//│ ║ ^^^^^ +//│ ╟── Note: constraint arises from type variable: +//│ ║ l.391: trait Foo { fun foo: forall 'A: (x: 'A) -> 'A } +//│ ╙── ^^ +//│ class B extends Foo { +//│ constructor() +//│ fun foo: Int -> Int +//│ } + + diff --git a/shared/src/test/diff/nu/StupidJS.mls b/shared/src/test/diff/nu/StupidJS.mls new file mode 100644 index 0000000000..eb6d2b0c91 --- /dev/null +++ b/shared/src/test/diff/nu/StupidJS.mls @@ -0,0 +1,34 @@ +:NewDefs + + +module C { fun f() = this } +//│ module C { +//│ fun f: () -> C +//│ } + +C.f() +//│ C +//│ res +//│ = C { class: [class C] } + + +// * In JS semantics, C.f will return a plain method **without capturing `this`**!! +// * This is very dumb. In Python for example, one gets a closure that captures `this`. +// * We can't easily work around this without incurring either bad performance overhead +// * or JS/TS interoperability woes. +// * Therefore, we'll make sure to reflecy the wonky semantics in the typer +// * (or maybe just make it fail type checking altogether.) + +// :e // TODO prevent unapplied method selections in the type system... +let r = id(C.f)() +//│ let r: C +//│ r +//│ = undefined + +:re +r.f +//│ () -> C +//│ res +//│ Runtime error: +//│ TypeError: Cannot read properties of undefined (reading 'f') + diff --git a/shared/src/test/diff/nu/Subscripts.mls b/shared/src/test/diff/nu/Subscripts.mls new file mode 100644 index 0000000000..eb2812dbb6 --- /dev/null +++ b/shared/src/test/diff/nu/Subscripts.mls @@ -0,0 +1,29 @@ +:NewDefs + + +let xs = [0, 1, 2] +//│ let xs: [0, 1, 2] +//│ xs +//│ = [ 0, 1, 2 ] + +if xs.[0] is + undefined then 0 + x then x +//│ 0 | 1 | 2 +//│ res +//│ = 0 + + +:e +xs.[0] + 1 +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.18: xs.[0] + 1 +//│ ║ ^^^^^^^^ +//│ ╟── possibly-undefined array access of type `()` is not an instance of type `Int` +//│ ║ l.18: xs.[0] + 1 +//│ ╙── ^^^^ +//│ Int | error +//│ res +//│ = 1 + + diff --git a/shared/src/test/diff/nu/TODO_Classes.mls b/shared/src/test/diff/nu/TODO_Classes.mls new file mode 100644 index 0000000000..8df3944017 --- /dev/null +++ b/shared/src/test/diff/nu/TODO_Classes.mls @@ -0,0 +1,226 @@ +:NewDefs + + + +// *** First-class classes *** // + + +class C +//│ class C { +//│ constructor() +//│ } + +class D(x: Int) +//│ class D(x: Int) + +:e +fun foo(c) = new c +//│ ╔══[ERROR] type identifier not found: c +//│ ║ l.17: fun foo(c) = new c +//│ ╙── ^ +//│ fun foo: anything -> error + +:re +foo(() => 123) +//│ error +//│ res +//│ Runtime error: +//│ TypeError: c is not a constructor + +:e +foo(C) +//│ ╔══[ERROR] Construction of unparameterized class C should use the `new` keyword +//│ ║ l.31: foo(C) +//│ ╙── ^ +//│ error +//│ res +//│ = C {} + + +:e +fun bar(c) = new c(123) +//│ ╔══[ERROR] type identifier not found: c +//│ ║ l.41: fun bar(c) = new c(123) +//│ ╙── ^ +//│ fun bar: anything -> error + +:re +bar(D) +//│ error +//│ res +//│ Runtime error: +//│ TypeError: c is not a constructor + +:e // TODO accept when we have first-class classes +bar(D.class) +//│ ╔══[ERROR] Type mismatch in field selection: +//│ ║ l.55: bar(D.class) +//│ ║ ^^^^^^^ +//│ ╟── reference of type `(x: Int) -> D` does not have field 'class' +//│ ║ l.55: bar(D.class) +//│ ╙── ^ +//│ error +//│ res +//│ = D {} + + + +// *** Refinements *** // + + +class C +//│ class C { +//│ constructor() +//│ } + +:e // TODO support +new C { val x = 1 } +//│ ╔══[ERROR] Refinement terms are not yet supported +//│ ║ l.77: new C { val x = 1 } +//│ ╙── ^^^^^^^^^^^^^^^^^^^ +//│ error +//│ Code generation encountered an error: +//│ cannot generate code for term Rft(NuNew(Var(C)),TypingUnit(List(NuFunDef(Some(false),Var(x),None,List(),Left(IntLit(1)))))) + + + +// *** Path-dependent types *** // + + +class Cls[out A] { fun x: A = x } +//│ class Cls[A] { +//│ constructor() +//│ fun x: A +//│ } + +let c = new Cls +//│ let c: Cls[nothing] +//│ c +//│ = Cls {} + +// FIXME +let y: c.A = c.x +//│ ╔══[ERROR] type identifier not found: c +//│ ║ l.102: let y: c.A = c.x +//│ ╙── ^ +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing + + + +// *** GADTs *** // + + +class Cls[A] { fun x: A = x; fun g: A -> Int; fun g = g } +//│ class Cls[A] { +//│ constructor() +//│ fun g: A -> Int +//│ fun x: A +//│ } + + +:e // TODO +fun test(a: Object) = if a is + Cls then a.x + else error +//│ ╔══[ERROR] Type error in `case` expression +//│ ║ l.122: fun test(a: Object) = if a is +//│ ║ ^^^^ +//│ ║ l.123: Cls then a.x +//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.124: else error +//│ ║ ^^^^^^^^^^^^ +//│ ╟── type variable `A` leaks out of its scope +//│ ║ l.113: class Cls[A] { fun x: A = x; fun g: A -> Int; fun g = g } +//│ ╙── ^ +//│ fun test: (a: Object) -> (error | ??A) + +:e // TODO +fun test(a: Object) = if a is + Cls then a.g(a.x) // a.x : a.A ; a.g : a.A -> a.A + else 0 +//│ ╔══[ERROR] Type error in `case` expression +//│ ║ l.138: fun test(a: Object) = if a is +//│ ║ ^^^^ +//│ ║ l.139: Cls then a.g(a.x) // a.x : a.A ; a.g : a.A -> a.A +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.140: else 0 +//│ ║ ^^^^^^^^ +//│ ╟── type variable `A` leaks out of its scope +//│ ║ l.113: class Cls[A] { fun x: A = x; fun g: A -> Int; fun g = g } +//│ ╙── ^ +//│ fun test: (a: Object) -> Int + + +class Cls[out A] { fun x: A = x } +//│ class Cls[A] { +//│ constructor() +//│ fun x: A +//│ } + +fun test(a: Object) = if a is + Cls then a.x + else error +//│ fun test: (a: Object) -> ??A + +fun test(a: Object) = if a is + Cls then a + else error +//│ fun test: (a: Object) -> Cls[??A] + +:re +test(0).x +//│ ??A +//│ res +//│ Runtime error: +//│ Error: an error was thrown + + +// class Expr[T] { +// constructor +// IntLit(n: Int) { T = Int } +// BoolLit(b: Bool) { T = Bool } +// } +// +// fun foo(x) = if x is +// IntLit then x.n as x.T +// BoolLit then x.b +// foo: (x: IntLit | BoolLit) -> x.T | Bool +// +// fun foo(x) = if x is +// IntLit then x.n as x.T +// BoolLit then x.b as x.T +// foo: (x: IntLit | BoolLit) -> x.T +// <: Expr['a] -> 'a +// +// fun foo(x: Expr[T]): T = if x is +// IntLit then x.n // in Scala, compiler sees x.n : Int = x.T <: T +// BoolLit then x.b + +abstract class Expr[A]: (IntLit | BoolLit) {} +class IntLit() extends Expr[Int] +class BoolLit() extends Expr[Bool] +//│ abstract class Expr[A]: BoolLit | IntLit +//│ class IntLit() extends Expr +//│ class BoolLit() extends Expr + +fun test(f: ((IntLit | BoolLit) -> Int)) = + f : Expr[anything] -> Int +//│ fun test: (f: (BoolLit | IntLit) -> Int) -> Expr[anything] -> Int + +:e +class OopsLit() extends Expr[Bool] +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.211: class OopsLit() extends Expr[Bool] +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── expression of type `#OopsLit` does not match type `BoolLit | IntLit` +//│ ╟── Note: constraint arises from union type: +//│ ║ l.199: abstract class Expr[A]: (IntLit | BoolLit) {} +//│ ╙── ^^^^^^^^^^^^^^^^^^ +//│ class OopsLit() extends Expr + +fun test(a) = if a is + IntLit then 0 + OopsLit then 1 +//│ fun test: (IntLit | OopsLit) -> (0 | 1) + + diff --git a/shared/src/test/diff/nu/ThisRefinedClasses.mls b/shared/src/test/diff/nu/ThisRefinedClasses.mls new file mode 100644 index 0000000000..dd4245f923 --- /dev/null +++ b/shared/src/test/diff/nu/ThisRefinedClasses.mls @@ -0,0 +1,91 @@ +:NewDefs + + + +// * I don't think it's a good idea to generate `this` refinements for classes, +// * as this could easily lead to typos and delayed error reporting. +// * Instead, we should require explicit `this` annotations by users when needed. + + +:e +class Foo { fun test = this.x } +//│ ╔══[ERROR] Type `#Foo` does not contain member `x` +//│ ║ l.11: class Foo { fun test = this.x } +//│ ╙── ^^ +//│ class Foo { +//│ constructor() +//│ fun test: error +//│ } + + +:e +class Foo(n: Int) { fun test = this.x } +//│ ╔══[ERROR] Type `#Foo & {n: Int}` does not contain member `x` +//│ ║ l.22: class Foo(n: Int) { fun test = this.x } +//│ ╙── ^^ +//│ class Foo(n: Int) { +//│ fun test: error +//│ } + + +:e +class Foo(n: A) { fun test = this.x } +//│ ╔══[ERROR] Type `#Foo & {Foo#A = A, n: A}` does not contain member `x` +//│ ║ l.32: class Foo(n: A) { fun test = this.x } +//│ ╙── ^^ +//│ class Foo[A](n: A) { +//│ fun test: error +//│ } + + + +// TODO support: (treat `this` annot not like a term ascription) +class Foo { + this: { x: 'a } + // fun test = this.x +} +//│ ╔══[ERROR] Type `#Foo` does not contain member `x` +//│ ║ l.44: this: { x: 'a } +//│ ╙── ^ +//│ ╔══[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 type ascription: +//│ ║ l.44: this: { x: 'a } +//│ ║ ^^^^ +//│ ╟── type `{x: 'a}` does not match type `()` +//│ ║ l.44: this: { x: 'a } +//│ ║ ^^^^^^^^^ +//│ ╟── but it flows into expression in statement position with expected type `()` +//│ ║ l.44: this: { x: 'a } +//│ ╙── ^^^^ +//│ class Foo { +//│ constructor() +//│ } + + +// TODO +// * All on one line: +class Test { this: { x: Int}; fun test = this.x } +//│ ╔══[ERROR] Type `#Test` does not contain member `x` +//│ ║ l.68: class Test { this: { x: Int}; fun test = this.x } +//│ ╙── ^^ +//│ ╔══[ERROR] Type `#Test` does not contain member `x` +//│ ║ l.68: class Test { this: { x: Int}; fun test = this.x } +//│ ╙── ^ +//│ ╔══[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 type ascription: +//│ ║ l.68: class Test { this: { x: Int}; fun test = this.x } +//│ ║ ^^^^ +//│ ╟── type `{x: Int}` does not match type `()` +//│ ║ l.68: class Test { this: { x: Int}; fun test = this.x } +//│ ║ ^^^^^^^^^ +//│ ╟── but it flows into expression in statement position with expected type `()` +//│ ║ l.68: class Test { this: { x: Int}; fun test = this.x } +//│ ╙── ^^^^ +//│ class Test { +//│ constructor() +//│ fun test: error +//│ } + + diff --git a/shared/src/test/diff/nu/TraitParameters.mls b/shared/src/test/diff/nu/TraitParameters.mls new file mode 100644 index 0000000000..4673437023 --- /dev/null +++ b/shared/src/test/diff/nu/TraitParameters.mls @@ -0,0 +1,11 @@ +:NewDefs + + +:e +trait Base1(x: Int) +//│ ╔══[ERROR] trait parameters are not yet supported +//│ ║ l.5: trait Base1(x: Int) +//│ ╙── ^^^^^^^^ +//│ trait Base1 + + diff --git a/shared/src/test/diff/nu/TraitSignatures.mls b/shared/src/test/diff/nu/TraitSignatures.mls new file mode 100644 index 0000000000..49cbe61e91 --- /dev/null +++ b/shared/src/test/diff/nu/TraitSignatures.mls @@ -0,0 +1,183 @@ +:NewDefs + + + +trait C +trait Foo: C +//│ trait C +//│ trait Foo: C + +class D() extends C, Foo +D() : Foo +//│ class D() extends C, Foo +//│ Foo +//│ res +//│ = D {} + +class E extends C +class F() extends E, Foo +F() : Foo +//│ class E extends C { +//│ constructor() +//│ } +//│ class F() extends C, E, Foo +//│ Foo +//│ res +//│ = F {} + +abstract class H extends Foo +class I() extends H, C +I() : Foo +//│ abstract class H: C extends Foo +//│ class I() extends C, Foo, H +//│ Foo +//│ res +//│ = I {} + +:e +class J extends Foo +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.38: class J extends Foo +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ╟── expression of type `#J` is not an instance of type `C` +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.6: trait Foo: C +//│ ╙── ^ +//│ class J extends Foo { +//│ constructor() +//│ } + + +class C1 +trait T1: C1 +trait T2 extends T1 +//│ class C1 { +//│ constructor() +//│ } +//│ trait T1: C1 +//│ trait T2: C1 extends T1 + +:e +class C2 extends T1 +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.61: class C2 extends T1 +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ╟── expression of type `#C2` is not an instance of type `C1` +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.52: trait T1: C1 +//│ ╙── ^^ +//│ class C2 extends T1 { +//│ constructor() +//│ } + +:e +class C2 extends T2 +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.74: class C2 extends T2 +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ╟── expression of type `#C2` is not an instance of type `C1` +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.52: trait T1: C1 +//│ ╙── ^^ +//│ class C2 extends T1, T2 { +//│ constructor() +//│ } + +class C2 extends C1, T2 +//│ class C2 extends C1, T1, T2 { +//│ constructor() +//│ } + + + +// * Interfaces that are "callable" are important to support for TS/JS interop + + +declare trait Foo: ((x: Num) => Num) { + val y: Str +} +//│ declare trait Foo: (x: Num) -> Num { +//│ val y: Str +//│ } + +(f: Foo) => [f.y, f(0)] +//│ (f: Foo) -> [Str, Num] +//│ res +//│ = [Function: res] + + +declare trait FooPoly: forall 'a: (x: 'a) => 'a +//│ declare trait FooPoly: forall 'a. (x: 'a) -> 'a + +(f: FooPoly) => [f(0), f(true)] +//│ (f: FooPoly) -> [0, true] +//│ res +//│ = [Function: res] + + +:e +class Oops(y: Str) extends Foo +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.119: class Oops(y: Str) extends Foo +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── expression of type `#Oops & {y: Str}` is not a function +//│ ╟── Note: constraint arises from function type: +//│ ║ l.96: declare trait Foo: ((x: Num) => Num) { +//│ ╙── ^^^^^^^^^^^^^^^^^ +//│ class Oops(y: Str) extends Foo + +:e +Oops("")(1) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.130: Oops("")(1) +//│ ║ ^^^^^^^^^^^ +//│ ╟── application of type `Oops` is not a function +//│ ║ l.130: Oops("")(1) +//│ ╙── ^^^^^^^^ +//│ error +//│ res +//│ Runtime error: +//│ TypeError: Oops(...) is not a function + +:e +module Oops extends FooPoly +//│ ╔══[ERROR] Type mismatch in type declaration: +//│ ║ l.143: module Oops extends FooPoly +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── expression of type `#Oops` is not a function +//│ ╟── Note: constraint arises from function type: +//│ ║ l.109: declare trait FooPoly: forall 'a: (x: 'a) => 'a +//│ ╙── ^^^^^^^ +//│ module Oops extends FooPoly + +:e +Oops(123) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.154: Oops(123) +//│ ║ ^^^^^^^^^ +//│ ╟── reference of type `Oops` is not a function +//│ ║ l.154: Oops(123) +//│ ╙── ^^^^ +//│ error +//│ res +//│ Runtime error: +//│ TypeError: Oops is not a function + +:e +(Oops : FooPoly)(123) +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.167: (Oops : FooPoly)(123) +//│ ║ ^^^^ +//│ ╟── reference of type `Oops` is not a function +//│ ╟── Note: constraint arises from function type: +//│ ║ l.109: declare trait FooPoly: forall 'a: (x: 'a) => 'a +//│ ║ ^^^^^^^ +//│ ╟── from type reference: +//│ ║ l.167: (Oops : FooPoly)(123) +//│ ╙── ^^^^^^^ +//│ 123 +//│ res +//│ Runtime error: +//│ TypeError: Oops is not a function + + diff --git a/shared/src/test/diff/nu/TrickyGenericInheritance.mls b/shared/src/test/diff/nu/TrickyGenericInheritance.mls new file mode 100644 index 0000000000..e51ae14ac4 --- /dev/null +++ b/shared/src/test/diff/nu/TrickyGenericInheritance.mls @@ -0,0 +1,267 @@ +:NewDefs + + + +trait T1[A] { + fun f: A -> A +} +//│ trait T1[A] { +//│ fun f: A -> A +//│ } + +class C1 extends T1 { + fun f(x: Int) = x +} +//│ class C1 extends T1 { +//│ constructor() +//│ fun f: (x: Int) -> Int +//│ } + +class C1 extends T1['FigureItOut] { + fun f(x: Int) = x +} +//│ class C1 extends T1 { +//│ constructor() +//│ fun f: (x: Int) -> Int +//│ } + +let c1 = new C1 +//│ let c1: C1 +//│ c1 +//│ = C1 {} + +c1.f +//│ (x: Int) -> Int +//│ res +//│ = [Function: f] + +(c1 : T1).f +//│ ??A -> ??A0 +//│ res +//│ = [Function: f] + +(c1 : T1['X]).f +//│ Int -> Int +//│ res +//│ = [Function: f] + +:ns +(c1 : T1).f +//│ 'f +//│ where +//│ 'f :> in ??A out ??A0 -> in ??A out ??A0 +//│ res +//│ = [Function: f] + +:ns +(c1 : T1['X]).f +//│ 'f +//│ where +//│ 'f :> 'X -> 'X +//│ 'X :> Int +//│ <: 'FigureItOut +//│ 'FigureItOut :> Int +//│ <: 'X & Int +//│ res +//│ = [Function: f] + +:e +(c1 : T1['X]).f(false) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.69: (c1 : T1['X]).f(false) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── reference of type `false` is not an instance of `Int` +//│ ║ l.69: (c1 : T1['X]).f(false) +//│ ║ ^^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.21: fun f(x: Int) = x +//│ ║ ^^^ +//│ ╟── from type variable: +//│ ║ l.69: (c1 : T1['X]).f(false) +//│ ╙── ^^ +//│ Int | error | false +//│ res +//│ = false + + + +// * The more tricky case: + +// * TODO better error (cf. "exposes no constructor") +:e +trait T2[A] { + fun f: A -> A + val r = C2().f(false) +} +class C2 extends T2['FigureItOut] { + fun f(x: Int) = x +} +//│ ╔══[ERROR] Method implementations in traits are not yet supported +//│ ║ l.94: val r = C2().f(false) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//│ ╔══[ERROR] Class C2 cannot be instantiated as it exposes no constructor +//│ ║ l.94: val r = C2().f(false) +//│ ╙── ^^ +//│ trait T2[A] { +//│ fun f: A -> A +//│ val r: error +//│ } +//│ class C2 extends T2 { +//│ constructor() +//│ fun f: (x: Int) -> Int +//│ val r: error +//│ } + +:e +trait T3[A] { + fun f: A -> A + val r = (C3() : T3['X]).f(false) +} +class C3 extends T3['FigureItOut] { + fun f(x: Int) = x +} +//│ ╔══[ERROR] Method implementations in traits are not yet supported +//│ ║ l.118: val r = (C3() : T3['X]).f(false) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╔══[ERROR] Class C3 cannot be instantiated as it exposes no constructor +//│ ║ l.118: val r = (C3() : T3['X]).f(false) +//│ ╙── ^^ +//│ trait T3[A] { +//│ fun f: A -> A +//│ val r: false +//│ } +//│ class C3 extends T3 { +//│ constructor() +//│ fun f: (x: Int) -> Int +//│ val r: false +//│ } + +:e // FIXME +C3() : T3['X] +//│ ╔══[ERROR] Construction of unparameterized class C3 should use the `new` keyword +//│ ║ l.140: C3() : T3['X] +//│ ╙── ^^ +//│ T3[Int] +//│ res +//│ Runtime error: +//│ TypeError: Class constructor C3 cannot be invoked without 'new' + + + +// FIXME +abstract class IO[A] { + fun test = Bind(this) : IO[Int] +} +class Bind[A](underlying: IO[A]) extends IO[Int] +//│ ╔══[ERROR] Unhandled cyclic definition +//│ ║ l.152: abstract class IO[A] { +//│ ║ ^^^^^^^^^^^^^ +//│ ║ l.153: fun test = Bind(this) : IO[Int] +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.154: } +//│ ╙── ^ +//│ ╔══[ERROR] Type `Bind[?A]` does not contain member `IO#A` +//│ ║ l.152: abstract class IO[A] { +//│ ╙── ^ +//│ abstract class IO[A] { +//│ fun test: IO[Int] +//│ } +//│ class Bind[A](underlying: IO[A]) extends IO + +// FIXME +abstract class IO[A] { + fun map[B]: (A -> B) -> IO[B] // * Removing this works... + fun map(f) = Map(this, f) + fun run: A +} +class Map[A, B](underlying: IO[A], f: A -> B) extends IO[B] { + fun run: B = error +} +//│ ╔══[ERROR] Unhandled cyclic definition +//│ ║ l.172: abstract class IO[A] { +//│ ║ ^^^^^^^^^^^^^ +//│ ║ l.173: fun map[B]: (A -> B) -> IO[B] // * Removing this works... +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.174: fun map(f) = Map(this, f) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.175: fun run: A +//│ ║ ^^^^^^^^^^^^ +//│ ║ l.176: } +//│ ╙── ^ +//│ ╔══[ERROR] Type `Map[?A, ?B]` does not contain member `IO#A` +//│ ║ l.172: abstract class IO[A] { +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in definition of method map: +//│ ║ l.174: fun map(f) = Map(this, f) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── application of type `Map[?A, ?B]` does not have field 'IO#A' +//│ ║ l.174: fun map(f) = Map(this, f) +//│ ║ ^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from applied type reference: +//│ ║ l.173: fun map[B]: (A -> B) -> IO[B] // * Removing this works... +//│ ╙── ^^^^^ +//│ abstract class IO[A] { +//│ fun map: forall 'B. (A -> 'B) -> IO['B] +//│ fun run: A +//│ } +//│ class Map[A, B](underlying: IO[A], f: A -> B) extends IO { +//│ fun run: B +//│ } + +// FIXME +abstract class IO[A] { + fun bind[B]: (A -> IO[B]) -> IO[B] // * FIXME: causes cycle error + fun bind(f) = Bind(this, f) + fun run: A +} +class Bind[A, B](underlying: IO[A], f: A -> IO[B]) extends IO[B] { + fun run = f(underlying.run).run +} +//│ ╔══[ERROR] Unhandled cyclic definition +//│ ║ l.212: abstract class IO[A] { +//│ ║ ^^^^^^^^^^^^^ +//│ ║ l.213: fun bind[B]: (A -> IO[B]) -> IO[B] // * FIXME: causes cycle error +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.214: fun bind(f) = Bind(this, f) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.215: fun run: A +//│ ║ ^^^^^^^^^^^^ +//│ ║ l.216: } +//│ ╙── ^ +//│ ╔══[ERROR] Type `Bind[?A, ?B]` does not contain member `IO#A` +//│ ║ l.212: abstract class IO[A] { +//│ ╙── ^ +//│ ╔══[ERROR] Type mismatch in definition of method bind: +//│ ║ l.214: fun bind(f) = Bind(this, f) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── application of type `Bind[?A, ?B]` does not have field 'IO#A' +//│ ║ l.214: fun bind(f) = Bind(this, f) +//│ ║ ^^^^^^^^^^^^^ +//│ ╟── Note: constraint arises from applied type reference: +//│ ║ l.213: fun bind[B]: (A -> IO[B]) -> IO[B] // * FIXME: causes cycle error +//│ ╙── ^^^^^ +//│ abstract class IO[A] { +//│ fun bind: forall 'B. (A -> IO['B]) -> IO['B] +//│ fun run: A +//│ } +//│ class Bind[A, B](underlying: IO[A], f: A -> IO[B]) extends IO { +//│ fun run: B +//│ } + + +// TODO support +abstract class IO[A] { + class Bind[B]() extends IO[B] +} +//│ ╔══[ERROR] Unhandled cyclic definition +//│ ║ l.253: abstract class IO[A] { +//│ ║ ^^^^^^^^^^^^^ +//│ ║ l.254: class Bind[B]() extends IO[B] +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.255: } +//│ ╙── ^ +//│ abstract class IO[A] { +//│ class Bind[B]() extends IO +//│ } + + diff --git a/shared/src/test/diff/nu/Tuples.mls b/shared/src/test/diff/nu/Tuples.mls new file mode 100644 index 0000000000..42b5343ac8 --- /dev/null +++ b/shared/src/test/diff/nu/Tuples.mls @@ -0,0 +1,118 @@ +:NewDefs + + +// Tuple literals use square brackets. +let t = [0, 1, 2] +//│ let t: [0, 1, 2] +//│ t +//│ = [ 0, 1, 2 ] + + +// Tuple indices are zero-based. +t.0 +t.1 +t.2 +//│ 2 +//│ res +//│ = 0 +//│ res +//│ = 1 +//│ res +//│ = 2 + + +// Tuple indices cannot be negative. +:e +t.-1 +//│ ╔══[ERROR] identifier not found: .- +//│ ║ l.26: t.-1 +//│ ╙── ^^ +//│ error +//│ Code generation encountered an error: +//│ unresolved symbol .- + + +// Non-zero tuple indices cannot start with a zero. +:e +t.01 +//│ ╔══[ERROR] identifier not found: . +//│ ║ l.37: t.01 +//│ ╙── ^ +//│ error +//│ Code generation encountered an error: +//│ unresolved symbol . + + +// Out of bounds indices cause type errors. +:e +t.3 +//│ ╔══[ERROR] Type mismatch in field selection: +//│ ║ l.48: t.3 +//│ ║ ^^^ +//│ ╟── tuple literal of type `{0: 0, 1: 1, 2: 2}` does not have field '3' +//│ ║ l.5: let t = [0, 1, 2] +//│ ║ ^^^^^^^^^ +//│ ╟── but it flows into reference with expected type `{3: ?a}` +//│ ║ l.48: t.3 +//│ ╙── ^ +//│ error +//│ res +//│ = undefined + + +// Note that record fields can be integers with leading zeros. +{ 000000: "just zero" } +{ 042: "cuarenta y dos" } +//│ {42: "cuarenta y dos"} +//│ res +//│ = { '0': 'just zero' } +//│ res +//│ = { '42': 'cuarenta y dos' } + + +// Negative integer record fields are disallowed, which aligns with JavaScript. +:pe +:e +{ -1: "oh no" } +//│ ╔══[PARSE ERROR] Record field should have a name +//│ ╙── +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ╟── integer literal of type `-1` does not match type `"oh no"` +//│ ╟── Note: constraint arises from literal type: +//│ ║ l.76: { -1: "oh no" } +//│ ╙── ^^^^^^^ +//│ {: "oh no"} +//│ res +//│ = { '': -1 } + + +// Note that leading zeros of integer record fields are ignored. +// And duplicate fields lead to errors. +:e +{ 0: "oh", 00: "no" } +//│ ╔══[ERROR] Multiple declarations of field name 0 in record literal +//│ ║ l.92: { 0: "oh", 00: "no" } +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── Declared at +//│ ║ l.92: { 0: "oh", 00: "no" } +//│ ║ ^ +//│ ╟── Declared at +//│ ║ l.92: { 0: "oh", 00: "no" } +//│ ╙── ^^ +//│ {0: "oh", 0: "no"} +//│ res +//│ = { '0': 'no' } + + +// Fields that are integers or start with an underscore should not be used as name hints. +fun f(x) = x._q +fun g(x) = x._1 +fun h(x) = x.1 +//│ fun f: forall 'a. {_q: 'a} -> 'a +//│ fun g: forall 'b. {_1: 'b} -> 'b +//│ fun h: forall 'c. {1: 'c} -> 'c + + +// Other fields should be used as name hints. +fun p(x) = x.y +//│ fun p: forall 'y. {y: 'y} -> 'y diff --git a/shared/src/test/diff/nu/TypeAliases.mls b/shared/src/test/diff/nu/TypeAliases.mls index 25bbcdb5c6..e1fc895a10 100644 --- a/shared/src/test/diff/nu/TypeAliases.mls +++ b/shared/src/test/diff/nu/TypeAliases.mls @@ -1,68 +1,67 @@ -:NewParser +:NewDefs -type I = int -//│ Defined type alias I + +type I = Int +//│ type I = Int class CI1 -//│ Defined class CI1 -//│ CI1: () -> CI1 -//│ = [Function: CI11] +//│ class CI1 { +//│ constructor() +//│ } -:e -type AI1 = Array[int] -//│ ╔══[ERROR] not a recognized type: (Array)[int] -//│ ║ l.12: type AI1 = Array[int] -//│ ╙── ^^^^^^^^^^ -//│ Defined type alias AI1 +// :e +type AI1 = Array[Int] +//│ type AI1 = Array[Int] -type AI2 = Array -//│ Defined type alias AI2 +type AI2 = Array +//│ type AI2 = Array[Int] -// TODO fail gracefully :e -type AI3(n) = Array[int] -//│ /!!!\ Uncaught error: java.lang.IllegalArgumentException: requirement failed: List((None,Fld(false,false,n))) +type AI3(n) = Array[Int] +//│ ╔══[ERROR] Type alias definitions cannot have value parameters +//│ ║ l.20: type AI3(n) = Array[Int] +//│ ╙── ^^^ +//│ type AI3 = Array[Int] -// TODO fail gracefully -:e +// :e type AI3[A] = Array -//│ ╔══[PARSE ERROR] Expected end of input; found square bracket section instead -//│ ║ l.28: type AI3[A] = Array -//│ ╙── ^^^ -//│ /!!!\ Uncaught error: java.lang.IllegalArgumentException: requirement failed: List() +//│ type AI3[A] = Array[A] type AI4 = Array -//│ Defined type alias AI4[+A] +//│ type AI4[A] = Array[A] let r = 123 -//│ r: 123 -//│ = 123 +//│ let r: 123 +//│ r +//│ = 123 r: I -//│ res: I -//│ = 123 +//│ I +//│ res +//│ = 123 let a = [r, r, r] -//│ a: (123, 123, 123,) -//│ = [ 123, 123, 123 ] +//│ let a: [123, 123, 123] +//│ a +//│ = [ 123, 123, 123 ] a : AI1 -//│ res: AI1 -//│ = [ 123, 123, 123 ] +//│ AI1 +//│ res +//│ = [ 123, 123, 123 ] a : AI2 -//│ res: AI2 -//│ = [ 123, 123, 123 ] +//│ AI2 +//│ res +//│ = [ 123, 123, 123 ] -:e -a : AI3[int] -//│ ╔══[ERROR] not a recognized type: (AI3)[int] -//│ ║ l.58: a : AI3[int] -//│ ╙── ^^^^^^^^ -//│ res: anything -//│ = [ 123, 123, 123 ] - -a : AI4 -//│ res: AI4[int] -//│ = [ 123, 123, 123 ] +a : AI3[Int] +//│ AI3[Int] +//│ res +//│ = [ 123, 123, 123 ] + +a : AI4 +//│ AI4[Int] +//│ res +//│ = [ 123, 123, 123 ] diff --git a/shared/src/test/diff/nu/TypeOps.mls b/shared/src/test/diff/nu/TypeOps.mls new file mode 100644 index 0000000000..6f2363088c --- /dev/null +++ b/shared/src/test/diff/nu/TypeOps.mls @@ -0,0 +1,40 @@ +:NewDefs + + +type *[A, B] = [A, B] +//│ type *[A, B] = [A, B] + + +fun x : Int * Int +fun x = [0, 1] +//│ fun x: [0, 1] +//│ fun x: *[Int, Int] + +fun x : Int * [Int] +fun x = [0, [1]] +//│ fun x: [0, [1]] +//│ fun x: *[Int, [Int]] + +fun x : [Int] * Int +fun x = [[0], 1] +//│ fun x: [[0], 1] +//│ fun x: *[[Int], Int] + + +type Id[A] = A +//│ type Id[A] = A + +:e +fun x: Id[Int, Int] +//│ ╔══[ERROR] Wrong number of type arguments – expected 1, found 2 +//│ ║ l.28: fun x: Id[Int, Int] +//│ ╙── ^^^^^^^^^^^^ +//│ fun x: Id[Int] + +fun x: Id[[Int, Int]] +//│ fun x: Id[[Int, Int]] + +fun x: Id[[[Int, Int]]] +//│ fun x: Id[[[Int, Int]]] + + diff --git a/shared/src/test/diff/nu/TypeSelections.mls b/shared/src/test/diff/nu/TypeSelections.mls new file mode 100644 index 0000000000..909a1eff52 --- /dev/null +++ b/shared/src/test/diff/nu/TypeSelections.mls @@ -0,0 +1,39 @@ +:NewDefs + + +module M { + type T = Int -> Int + class C(n: Int) + fun mkC = C +} +//│ module M { +//│ class C(n: Int) +//│ type T = Int -> Int +//│ fun mkC: (n: Int) -> C +//│ } + +let x: M.T = id +//│ let x: Int -> Int +//│ x +//│ = [Function: id] + +fun foo(x: M.C) = x +//│ fun foo: (x: C) -> C + + +foo(M.mkC(42)) +//│ C +//│ res +//│ = C {} + + +:e +42 : M.mkC +//│ ╔══[ERROR] Illegal selection of value member in type position +//│ ║ l.31: 42 : M.mkC +//│ ╙── ^^^^ +//│ error +//│ res +//│ = 42 + + diff --git a/shared/src/test/diff/nu/TypeVariables.mls b/shared/src/test/diff/nu/TypeVariables.mls new file mode 100644 index 0000000000..327b6ca005 --- /dev/null +++ b/shared/src/test/diff/nu/TypeVariables.mls @@ -0,0 +1,37 @@ +:NewDefs + + +fun x: 'a -> 'a = succ +//│ fun x: Int -> Int + +:e +fun x: forall 'a: 'a -> 'a = succ +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.8: fun x: forall 'a: 'a -> 'a = succ +//│ ║ ^^^^ +//│ ╟── type `'a` is not an instance of type `Int` +//│ ║ l.8: fun x: forall 'a: 'a -> 'a = succ +//│ ║ ^^ +//│ ╟── Note: quantified type variable 'a is defined at: +//│ ║ l.8: fun x: forall 'a: 'a -> 'a = succ +//│ ╙── ^^ +//│ fun x: forall 'a. 'a -> 'a + +fun x: [Int -> Int] = [id : forall 'a: 'a -> 'a] +//│ fun x: [Int -> Int] + +:pe +:e +fun x: [Int -> Int] = [id : forall 'a: 'a -> 'a,] +//│ ╔══[PARSE ERROR] Unexpected end of square bracket section; an expression was expected here +//│ ║ l.25: fun x: [Int -> Int] = [id : forall 'a: 'a -> 'a,] +//│ ╙── ^ +//│ ╔══[ERROR] type identifier not found: , +//│ ║ l.25: fun x: [Int -> Int] = [id : forall 'a: 'a -> 'a,] +//│ ╙── ^^^^^^^^^ +//│ fun x: [Int -> Int] + +fun x: [Int -> Int,] = [id : forall 'a: 'a -> 'a] +//│ fun x: [Int -> Int] + + diff --git a/shared/src/test/diff/nu/TypingUnitTerms.mls b/shared/src/test/diff/nu/TypingUnitTerms.mls new file mode 100644 index 0000000000..a581e7bd3b --- /dev/null +++ b/shared/src/test/diff/nu/TypingUnitTerms.mls @@ -0,0 +1,16 @@ +:NewDefs + + +:e +fun test = + let x = 0(0) + 1 +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.6: let x = 0(0) +//│ ║ ^^^^ +//│ ╟── integer literal of type `0` is not a function +//│ ║ l.6: let x = 0(0) +//│ ╙── ^ +//│ fun test: 1 + + diff --git a/shared/src/test/diff/nu/TypreMembers.mls b/shared/src/test/diff/nu/TypreMembers.mls new file mode 100644 index 0000000000..af6a856b41 --- /dev/null +++ b/shared/src/test/diff/nu/TypreMembers.mls @@ -0,0 +1,30 @@ +:NewDefs + + +class Test { type T = Int } +//│ class Test { +//│ constructor() +//│ type T = Int +//│ } + +1 : Test.T +//│ Int +//│ res +//│ = 1 + + +trait Test { type T = Int } +//│ trait Test { +//│ type T = Int +//│ } + +:e +1 : Test.T +//│ ╔══[ERROR] Illegal prefix of type selection: Test +//│ ║ l.22: 1 : Test.T +//│ ╙── ^^^^ +//│ error +//│ res +//│ = 1 + + diff --git a/shared/src/test/diff/nu/Unapply.mls b/shared/src/test/diff/nu/Unapply.mls new file mode 100644 index 0000000000..9dcc6a93a4 --- /dev/null +++ b/shared/src/test/diff/nu/Unapply.mls @@ -0,0 +1,108 @@ +// *** Explicit unapply tests *** // + +:NewDefs + + +// * Unapply's current compilation strategy: +// function D ... +// D.class = class D { #x; static unapply(self) { return [self.#x] } } +// D.unapply = D.class.unapply + +class D(x: Int) +//│ class D(x: Int) + +D.unapply +//│ forall '#x. (D & {#x: '#x}) -> ['#x] +//│ res +//│ = [Function: unapply] + +D.unapply(D(42)) +//│ [Int] +//│ res +//│ = [ 42 ] + +:e +D.unapply({ x: 42 }) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.25: D.unapply({ x: 42 }) +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ╟── record literal of type `{x: 42}` does not have field '#x' +//│ ║ l.25: D.unapply({ x: 42 }) +//│ ║ ^^ +//│ ╟── Note: constraint arises from field selection: +//│ ║ l.11: class D(x: Int) +//│ ╙── ^ +//│ error | [nothing] +//│ res +//│ Runtime error: +//│ TypeError: Cannot read private member #x from an object whose class did not declare it + +class DT[T](x: T) +DT.unapply +//│ class DT[T](x: T) +//│ forall '#x. (DT[anything] & {#x: '#x}) -> ['#x] +//│ res +//│ = [Function: unapply] + +DT.unapply(DT(42)) +//│ [42] +//│ res +//│ = [ 42 ] + +:e +DT.unapply({ x: 42 }) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.53: DT.unapply({ x: 42 }) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── record literal of type `{x: 42}` does not have field '#x' +//│ ║ l.53: DT.unapply({ x: 42 }) +//│ ║ ^^ +//│ ╟── Note: constraint arises from field selection: +//│ ║ l.40: class DT[T](x: T) +//│ ╙── ^ +//│ error | [nothing] +//│ res +//│ Runtime error: +//│ TypeError: Cannot read private member #x from an object whose class did not declare it + + +// * TODO improve `unapply` logic: currently it picks up shadowing fields/methods + +class Foo(x: Int) { + val x = toString(x) +} +//│ class Foo(x: Int) { +//│ val x: Str +//│ } + +// * Current hack: use `scrut.#x` to access a private field while passing the typer... +Foo.unapply +//│ forall '#x. (Foo & {#x: '#x}) -> ['#x] +//│ res +//│ = [Function: unapply] + +Foo.unapply(Foo(123)) +//│ [Str] +//│ res +//│ = [ '123' ] + +if Foo(123) is Foo(a) then a +//│ Str +//│ res +//│ = '123' + + + +// * Eventually we'll want to support this sort of overloading: +:e +fun D(x: Int) = {x} +module D { fun unapply(a) = [a.x] } +//│ ╔══[ERROR] Refininition of 'D' +//│ ║ l.99: module D { fun unapply(a) = [a.x] } +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ fun D: (x: Int) -> {x: Int} +//│ module D { +//│ fun unapply: forall 'x. {x: 'x} -> ['x] +//│ } + + diff --git a/shared/src/test/diff/nu/UnaryMinus.mls b/shared/src/test/diff/nu/UnaryMinus.mls new file mode 100644 index 0000000000..bb0341d0a3 --- /dev/null +++ b/shared/src/test/diff/nu/UnaryMinus.mls @@ -0,0 +1,93 @@ +:NewDefs + +-1 +//│ -1 +//│ res +//│ = -1 + +- 1 +//│ -1 +//│ res +//│ = -1 + +1+1 +//│ Int +//│ res +//│ = 2 + +1-3 +//│ Int +//│ res +//│ = -2 + +3-1 +//│ Int +//│ res +//│ = 2 + +1 - (3 - 5) +//│ Int +//│ res +//│ = 3 + +3 - 1 +//│ Int +//│ res +//│ = 2 + +1 -1 +//│ Int +//│ res +//│ = 0 + +1-1 +//│ Int +//│ res +//│ = 0 + +1+ -1 +//│ Int +//│ res +//│ = 0 + +1 - (1 - 1) +//│ Int +//│ res +//│ = 1 + +1 - 1 +//│ Int +//│ res +//│ = 0 + +1 - - 1 +//│ Int +//│ res +//│ = 2 + +1 - - - - 1 +//│ Int +//│ res +//│ = 2 + +fun f() = 6 +-f() +//│ fun f: () -> 6 +//│ Int +//│ res +//│ = -6 + +fun inv(x: Int) = -x +inv(15) +inv(inv(15)) +//│ fun inv: (x: Int) -> Int +//│ Int +//│ res +//│ = -15 +//│ res +//│ = 15 + +inv(f()) +//│ Int +//│ res +//│ = -6 diff --git a/shared/src/test/diff/nu/UndefMatching.mls b/shared/src/test/diff/nu/UndefMatching.mls new file mode 100644 index 0000000000..57453f5d99 --- /dev/null +++ b/shared/src/test/diff/nu/UndefMatching.mls @@ -0,0 +1,51 @@ +:NewDefs + + + +fun foo(x: 'a | undefined) = if x is undefined then error else x +//│ fun foo: forall 'a. (x: Object & 'a & ~() | ()) -> 'a + + +class Abstract[A] { + // * Below, 'a in `foo` is basically `A & ~undefined` + + fun bar0(x: (A | undefined) & Object) = + if x is undefined then error else x + + fun bar1(x: (A | undefined) & Object) = + foo(x) + + fun bar2(x: (A | undefined) & Object) = + x : Object & 'a & ~undefined | undefined + + fun baz(a: A & Object) = [bar0(a), bar1(a), bar2(a)] +} +//│ class Abstract[A] { +//│ constructor() +//│ fun bar0: (x: Object & A | ()) -> (Object & A & ~()) +//│ fun bar1: (x: Object & A | ()) -> (Object & A & ~()) +//│ fun bar2: (x: Object & A | ()) -> (Object & A & ~() | ()) +//│ fun baz: (a: Object & A) -> [Object & A & ~(), Object & A & ~(), Object & A & ~() | ()] +//│ } + + +:e +class Abstract[A] { + fun bar(x: A & ~undefined | undefined) = + if x is undefined then error else x +} +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.35: if x is undefined then error else x +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── type `A & ~()` is not an instance of type `Object` +//│ ║ l.34: fun bar(x: A & ~undefined | undefined) = +//│ ║ ^^^^^^^^^^^^^^ +//│ ╟── but it flows into reference with expected type `Object` +//│ ║ l.35: if x is undefined then error else x +//│ ╙── ^ +//│ class Abstract[A] { +//│ constructor() +//│ fun bar: (x: () | A & ~()) -> (A & ~()) +//│ } + + diff --git a/shared/src/test/diff/nu/Uninstantiable.mls b/shared/src/test/diff/nu/Uninstantiable.mls new file mode 100644 index 0000000000..04e5f34318 --- /dev/null +++ b/shared/src/test/diff/nu/Uninstantiable.mls @@ -0,0 +1,44 @@ +:NewDefs + + +:e +Int +//│ ╔══[ERROR] Class Int is abstract and cannot be instantiated +//│ ║ l.5: Int +//│ ╙── ^^^ +//│ ╔══[ERROR] Class Int cannot be instantiated as it exposes no constructor +//│ ║ l.5: Int +//│ ╙── ^^^ +//│ error +//│ Code generation encountered an error: +//│ unresolved symbol Int + +:e +Int() +//│ ╔══[ERROR] Class Int is abstract and cannot be instantiated +//│ ║ l.17: Int() +//│ ╙── ^^^ +//│ ╔══[ERROR] Class Int cannot be instantiated as it exposes no constructor +//│ ║ l.17: Int() +//│ ╙── ^^^ +//│ error +//│ Code generation encountered an error: +//│ unresolved symbol Int + +:e +new Int +//│ ╔══[ERROR] Class Int is abstract and cannot be instantiated +//│ ║ l.29: new Int +//│ ╙── ^^^ +//│ Int +//│ Code generation encountered an error: +//│ unresolved symbol Int + + +// FIXME forbid statically +module A extends Int +//│ module A extends Int, Num +//│ Code generation encountered an error: +//│ unresolved parent Int. + + diff --git a/shared/src/test/diff/nu/Unit.mls b/shared/src/test/diff/nu/Unit.mls new file mode 100644 index 0000000000..5ff8b979f7 --- /dev/null +++ b/shared/src/test/diff/nu/Unit.mls @@ -0,0 +1,344 @@ +:NewDefs + + +() +//│ () +//│ res +//│ = undefined + +fun x: () +fun x = () +//│ fun x: () +//│ fun x: () + +x +//│ () +//│ res +//│ = undefined + + +:e // we used to treat () as an empty array; should in fact be JS's `undefined` +x : Array['a] +//│ ╔══[ERROR] Type mismatch in type ascription: +//│ ║ l.21: x : Array['a] +//│ ║ ^ +//│ ╟── type `()` does not match type `Array['a]` +//│ ║ l.9: fun x: () +//│ ║ ^^ +//│ ╟── but it flows into reference with expected type `Array['a]` +//│ ║ l.21: x : Array['a] +//│ ║ ^ +//│ ╟── Note: constraint arises from applied type reference: +//│ ║ l.21: x : Array['a] +//│ ╙── ^^^^^^^^^ +//│ Array[nothing] +//│ res +//│ = undefined + +x : undefined +//│ () +//│ res +//│ = undefined + +fun x: () +fun x = undefined +//│ fun x: () +//│ fun x: () + + +:e +fun x: () +fun x = 1 +//│ ╔══[ERROR] Type mismatch in definition: +//│ ║ l.51: fun x = 1 +//│ ║ ^^^^^ +//│ ╟── integer literal of type `1` does not match type `()` +//│ ║ l.51: fun x = 1 +//│ ║ ^ +//│ ╟── but it flows into definition of method x with expected type `()` +//│ ║ l.51: fun x = 1 +//│ ║ ^^^^^ +//│ ╟── Note: constraint arises from literal type: +//│ ║ l.50: fun x: () +//│ ╙── ^^ +//│ fun x: 1 +//│ fun x: () + + +(1) +//│ 1 +//│ res +//│ = 1 + +// :pe // TODO? +(1,) +//│ 1 +//│ res +//│ = 1 + +(1,2) +//│ 2 +//│ res +//│ = 2 + +(let x = 1) +//│ () +//│ res +//│ = undefined + +:pe +(let x = 1 in) +//│ ╔══[PARSE ERROR] Unexpected end of parenthesis section; an expression was expected here +//│ ║ l.90: (let x = 1 in) +//│ ╙── ^ +//│ () +//│ res +//│ = undefined + +(log(1)) +//│ () +//│ res +//│ = undefined +//│ // Output +//│ 1 + +:pe +(log(1);) +//│ ╔══[PARSE ERROR] Unexpected semicolon here +//│ ║ l.106: (log(1);) +//│ ╙── ^ +//│ () +//│ res +//│ = undefined +//│ // Output +//│ 1 + +:pe // support? +(log(1); 2) +//│ ╔══[PARSE ERROR] Unexpected semicolon here +//│ ║ l.117: (log(1); 2) +//│ ╙── ^ +//│ () +//│ res +//│ = undefined +//│ // Output +//│ 1 + +:pe // support? +(log(1); ()) +//│ ╔══[PARSE ERROR] Unexpected semicolon here +//│ ║ l.128: (log(1); ()) +//│ ╙── ^ +//│ () +//│ res +//│ = undefined +//│ // Output +//│ 1 + +(((log((()))))) +//│ () +//│ res +//│ = undefined +//│ // Output +//│ undefined + + + +(1, 2) +//│ 2 +//│ res +//│ = 2 + + +x => x +//│ forall 'a. 'a -> 'a +//│ res +//│ = [Function: res] + +(x) => x +//│ forall 'a. 'a -> 'a +//│ res +//│ = [Function: res] + +(x, y) => x + y +//│ (Int, Int) -> Int +//│ res +//│ = [Function: res] + + +(1, 2) => 3 +//│ (1, 2) -> 3 +//│ res +//│ = [Function: res] + + +1 => (2, 3) +//│ 1 -> 3 +//│ res +//│ = [Function: res] + + +fun f(x, y) = x + y +//│ fun f: (Int, Int) -> Int + +f(1, 2) +//│ Int +//│ res +//│ = 3 + +f of 1, 2 +//│ Int +//│ res +//│ = 3 + +:e +f of (1, 2) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.195: f of (1, 2) +//│ ║ ^^^^^^^^^^^ +//│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` +//│ ║ l.195: f of (1, 2) +//│ ║ ^^^^^^ +//│ ╟── Note: constraint arises from tuple literal: +//│ ║ l.181: fun f(x, y) = x + y +//│ ╙── ^^^^^^ +//│ Int | error +//│ res +//│ = NaN + + + +0; 0 +//│ 0 +//│ res +//│ = 0 +//│ res +//│ = 0 + +:w +fun f = + 0; 0 +//│ ╔══[WARNING] Pure expression does nothing in statement position. +//│ ║ l.220: 0; 0 +//│ ╙── ^ +//│ fun f: 0 + +:w +fun f = + succ(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 application: +//│ ║ l.228: succ(0); 0 +//│ ║ ^^^^^^^ +//│ ╙── application of type `Int` does not match type `()` +//│ fun f: 0 + +fun f = + discard(succ(0)); 0 +//│ fun f: 0 + +fun f = + discard of succ(0); 0 +//│ fun f: 0 + +fun f = + let _ = succ(0); 0 +//│ fun f: 0 + +fun f = + succ(0), 0 +//│ fun f: 0 + + +x => x; () +//│ () +//│ res +//│ = [Function: res] +//│ res +//│ = undefined + +x => x; () +//│ () +//│ res +//│ = [Function: res] +//│ res +//│ = undefined + +:w +fun f = + x => x; () +//│ ╔══[WARNING] Pure expression does nothing in statement position. +//│ ║ l.270: x => x; () +//│ ╙── ^^^^^^ +//│ fun f: () + +fun f = + discard of x => x; () +//│ fun f: () + +:w +fun f = + x => x + () +//│ ╔══[WARNING] Pure expression does nothing in statement position. +//│ ║ l.282: x => x +//│ ╙── ^^^^^^ +//│ fun f: () + + +:w +module Test { + 123 +} +//│ ╔══[WARNING] Pure expression does nothing in statement position. +//│ ║ l.292: 123 +//│ ╙── ^^^ +//│ module Test + +:w +module Test { + 123 + 456 +} +//│ ╔══[WARNING] Pure expression does nothing in statement position. +//│ ║ l.301: 123 +//│ ╙── ^^^ +//│ ╔══[WARNING] Pure expression does nothing in statement position. +//│ ║ l.302: 456 +//│ ╙── ^^^ +//│ module Test + +:w +module Test { + x => x +} +//│ ╔══[WARNING] Pure expression does nothing in statement position. +//│ ║ l.314: x => x +//│ ╙── ^^^^^^ +//│ module Test + + + +:e +fun foo = + let tmp = 0 +foo + 1 +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.326: foo + 1 +//│ ║ ^^^^^^^ +//│ ╟── definition of method foo of type `()` is not an instance of type `Int` +//│ ║ l.324: fun foo = +//│ ║ ^^^^^ +//│ ║ l.325: let tmp = 0 +//│ ║ ^^^^^^^^^^^^^ +//│ ╟── but it flows into reference with expected type `Int` +//│ ║ l.326: foo + 1 +//│ ╙── ^^^ +//│ fun foo: () +//│ Int | error +//│ res +//│ = NaN + + + diff --git a/shared/src/test/diff/nu/ValSigs.mls b/shared/src/test/diff/nu/ValSigs.mls new file mode 100644 index 0000000000..01cc07e753 --- /dev/null +++ b/shared/src/test/diff/nu/ValSigs.mls @@ -0,0 +1,54 @@ +:NewDefs + + +val x: Int +//│ val x: Int +//│ x +//│ = + +// * Note that this counts as a completely new `val` since it's in a new block +val x = "hi" +//│ val x: "hi" +//│ x +//│ = 'hi' + + +val x: Int +val x = 1 +//│ val x: 1 +//│ val x: Int +//│ x +//│ = +//│ x +//│ = 1 +val x = 1 +//│ val x: 1 +//│ x +//│ = 1 + +x +//│ 1 +//│ res +//│ = 1 + + +:e +val x: Int +val x = "oops" +//│ ╔══[ERROR] Type mismatch in definition: +//│ ║ l.37: val x = "oops" +//│ ║ ^^^^^^^^^^ +//│ ╟── string literal of type `"oops"` is not an instance of type `Int` +//│ ║ l.37: val x = "oops" +//│ ║ ^^^^^^ +//│ ╟── Note: constraint arises from type reference: +//│ ║ l.36: val x: Int +//│ ╙── ^^^ +//│ val x: "oops" +//│ val x: Int +//│ x +//│ = +//│ x +//│ = 'oops' + + diff --git a/shared/src/test/diff/nu/Vals.mls b/shared/src/test/diff/nu/Vals.mls new file mode 100644 index 0000000000..413b7a8498 --- /dev/null +++ b/shared/src/test/diff/nu/Vals.mls @@ -0,0 +1,57 @@ +:NewDefs + + +val a = 1 +val b = a + 1 +//│ val a: 1 +//│ val b: Int +//│ a +//│ = 1 +//│ b +//│ = 2 + + +// :e // FIXME should not type check +:ge +val c = d + 1 +val d = 1 +//│ val c: Int +//│ val d: 1 +//│ Code generation encountered an error: +//│ unguarded recursive use of by-value binding d + + +// :e // FIXME should not type check +:ge +val a = a +//│ val a: nothing +//│ Code generation encountered an error: +//│ unguarded recursive use of by-value binding a + + +val f(x) = x +//│ val f: forall 'a. 'a -> 'a +//│ f +//│ = [Function: f] + +f(123) +//│ 123 +//│ res +//│ = 123 + + +module M { + let tmp = 2 + val f(x) = x + tmp +} +//│ module M { +//│ val f: Int -> Int +//│ let tmp: 2 +//│ } + +M.f(123) +//│ Int +//│ res +//│ = 125 + + diff --git a/shared/src/test/diff/nu/Virtual.mls b/shared/src/test/diff/nu/Virtual.mls new file mode 100644 index 0000000000..8782ab7cd7 --- /dev/null +++ b/shared/src/test/diff/nu/Virtual.mls @@ -0,0 +1,147 @@ +:NewDefs + +class Foo() { + virtual fun f(x: Int) = x + 1 + val g: Int = 0 +} +//│ class Foo() { +//│ fun f: (x: Int) -> Int +//│ val g: Int +//│ } + +class Bar() extends Foo { + fun f(x: Int) = x + 2 +} +//│ class Bar() extends Foo { +//│ fun f: (x: Int) -> Int +//│ val g: Int +//│ } + +Bar().f(40) +//│ Int +//│ res +//│ = 42 + +:e +class Baz() extends Foo { + val g = 1 +} +//│ ╔══[ERROR] Value member `g` is not virtual and cannot be overridden +//│ ║ l.27: val g = 1 +//│ ║ ^^^^^ +//│ ╟── Originally declared here: +//│ ║ l.5: val g: Int = 0 +//│ ╙── ^^^^^^^^^^ +//│ class Baz() extends Foo { +//│ fun f: (x: Int) -> Int +//│ val g: 1 +//│ } + +mixin X { + fun f(x) = x + 42 +} +mixin XX { + fun f(x) = super.f(x) + 42 +} +class T() extends X, XX +//│ mixin X() { +//│ fun f: Int -> Int +//│ } +//│ mixin XX() { +//│ super: {f: 'a -> Int} +//│ fun f: 'a -> Int +//│ } +//│ class T() { +//│ fun f: Int -> Int +//│ } + +T().f(0 - 42) +//│ Int +//│ res +//│ = 42 + +abstract class M1() { + val foo: Str +} +class M2(s: Str) extends M1 { + val foo = s +} +M2("foo").foo +//│ abstract class M1() { +//│ val foo: Str +//│ } +//│ class M2(s: Str) extends M1 { +//│ val foo: Str +//│ } +//│ Str +//│ res +//│ = 'foo' + +class U() { + fun f: Int -> Int + fun f(x) = x + 1 +} +//│ class U() { +//│ fun f: Int -> Int +//│ } + +:e +class V1() { + virtual val x = 42 + fun foo = x // unqualified access! +} +//│ ╔══[ERROR] Unqualified access to virtual member x +//│ ║ l.91: fun foo = x // unqualified access! +//│ ║ ^^^^^^^ +//│ ╟── Declared here: +//│ ║ l.90: virtual val x = 42 +//│ ╙── ^^^^^^ +//│ class V1() { +//│ fun foo: 42 +//│ val x: 42 +//│ } + +class V2() { + val x: Int + virtual val x = 42 + fun foo: Int = this.x +} +//│ class V2() { +//│ fun foo: Int +//│ val x: Int +//│ } + + +:e +class C() { + virtual fun x : Int + fun x = 1 + fun foo0 = x // should error +} +C().x +//│ ╔══[ERROR] Unqualified access to virtual member x +//│ ║ l.119: fun foo0 = x // should error +//│ ║ ^^^^^^^^ +//│ ╟── Declared here: +//│ ║ l.118: fun x = 1 +//│ ╙── ^^^^^ +//│ class C() { +//│ fun foo0: 1 +//│ fun x: Int +//│ } +//│ Int +//│ res +//│ = 1 + +module D extends C { + fun x = 2 +} +D.x +//│ module D extends C { +//│ fun foo0: 1 +//│ fun x: 2 +//│ } +//│ 2 +//│ res +//│ = 2 + diff --git a/shared/src/test/diff/nu/WeirdDefs.mls b/shared/src/test/diff/nu/WeirdDefs.mls new file mode 100644 index 0000000000..77f7bcc4fc --- /dev/null +++ b/shared/src/test/diff/nu/WeirdDefs.mls @@ -0,0 +1,16 @@ +:NewDefs + + +fun fst[x, _] = error : x -> x +//│ fun fst: forall 'x. 'x -> 'x + +:e +fun fst[x, _] = x +//│ ╔══[ERROR] identifier not found: x +//│ ║ l.8: fun fst[x, _] = x +//│ ╙── ^ +//│ fun fst: error +//│ Code generation encountered an error: +//│ unresolved symbol x + + diff --git a/shared/src/test/diff/nu/WeirdUnions.mls b/shared/src/test/diff/nu/WeirdUnions.mls new file mode 100644 index 0000000000..89326b7576 --- /dev/null +++ b/shared/src/test/diff/nu/WeirdUnions.mls @@ -0,0 +1,113 @@ +:NewDefs + + + +fun f: Str | [Str, Int] +//│ fun f: Str | [Str, Int] + +fun f: [Str] | [Str, Int] +//│ fun f: Array[Int | Str] & {0: Str} + +fun f: (Str | [Str, Int]) +//│ fun f: Str | [Str, Int] + +:e +fun f: Str | (Str, Int) +//│ ╔══[ERROR] type identifier not found: , +//│ ║ l.15: fun f: Str | (Str, Int) +//│ ╙── ^^^^^^^^^^ +//│ fun f: Str | error + + +fun f: Str | ([Str, Int]) +//│ fun f: Str | [Str, Int] + +:e +fun f: Str | ((Str, Int)) +//│ ╔══[ERROR] type identifier not found: , +//│ ║ l.26: fun f: Str | ((Str, Int)) +//│ ╙── ^^^^^^^^^^^^ +//│ fun f: Str | error + + + +// * This type merges the input tuples, resulting in the union seen above: +fun f: (Str => Str) & ((Str, Int) => Str) +//│ fun f: (...Array[Int | Str] & {0: Str}) -> Str + +f("abc", "abc") +//│ Str +//│ res +//│ = +//│ f is not implemented + + +// * Note: the merge doesn't happen when the result type is different... +fun f: (Str => Str) & ((Str, Int) => Int) +//│ fun f: Str -> Str & (Str, Int) -> Int + +// * ...resulting in approximation at call sites (we don't handle overloading) +f("abc", "abc") +//│ Int | Str +//│ res +//│ = +//│ f is not implemented + +f("abcabc") +//│ Int | Str +//│ res +//│ = +//│ f is not implemented + + + +// * Mismatched argument list sizes yields surprising results: +let r = if true then id else (x, y) => [y, x] +//│ let r: (...nothing) -> [nothing, nothing] +//│ r +//│ = [Function: id] + +:e // * Expected: the function is uncallable until we implement syntax `r(...error)` +r(error) +r(error, error) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.71: r(error) +//│ ║ ^^^^^^^^ +//│ ╟── argument of type `[nothing]` does not match type `[?a, ?b]` +//│ ║ l.71: r(error) +//│ ║ ^^^^^^^ +//│ ╟── Note: constraint arises from tuple literal: +//│ ║ l.65: let r = if true then id else (x, y) => [y, x] +//│ ╙── ^^^^ +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.72: r(error, error) +//│ ║ ^^^^^^^^^^^^^^^ +//│ ╟── argument list of type `[nothing, nothing]` does not match type `[?a]` +//│ ║ l.72: r(error, error) +//│ ╙── ^^^^^^^^^^^^^^ +//│ error | [nothing, nothing] +//│ res +//│ Runtime error: +//│ Error: an error was thrown +//│ res +//│ Runtime error: +//│ Error: an error was thrown + +// * Note: the correct version: +let r = if true then id else ([x, y]) => [y, x] +//│ let r: forall 'a 'b 'c. (['a, 'b] & 'c) -> (['b, 'a] | 'c) +//│ r +//│ = [Function: id] + +r of [0, 1] +//│ [0 | 1, 0 | 1] +//│ res +//│ = [ 0, 1 ] + +// Also currently parses the same: +let r = if true then id else [x, y] => [y, x] +//│ let r: forall 'a 'b 'c. (['b, 'c] & 'a) -> (['c, 'b] | 'a) +//│ r +//│ = [Function: id] + + diff --git a/shared/src/test/diff/nu/With.mls b/shared/src/test/diff/nu/With.mls new file mode 100644 index 0000000000..ce040e3d2d --- /dev/null +++ b/shared/src/test/diff/nu/With.mls @@ -0,0 +1,28 @@ +:NewDefs + + +{} with {} +//│ anything +//│ res +//│ = {} + +{x: 1} with {y: 2} +//│ {x: 1, y: 2} +//│ res +//│ = { x: 1, y: 2 } + +{x: 1} with {x: 2} +//│ {x: 2} +//│ res +//│ = { x: 2 } + + +:pe +{x: 1} with 123 +//│ ╔══[PARSE ERROR] record literal expected here; found integer literal +//│ ║ l.21: {x: 1} with 123 +//│ ╙── ^^^ +//│ {x: 1} +//│ res +//│ = { x: 1 } + diff --git a/shared/src/test/diff/nu/i180.mls b/shared/src/test/diff/nu/i180.mls new file mode 100644 index 0000000000..39013ced9f --- /dev/null +++ b/shared/src/test/diff/nu/i180.mls @@ -0,0 +1,79 @@ +:NewDefs + + +fun (++) stringConcat(a, b) = concat(a)(b) +//│ fun (++) stringConcat: (Str, Str) -> Str + +type List[out A] = Cons[A] | Nil +class Cons[out A](head: A, tail: List[A]) { + fun join(sep: Str): Str + fun join(sep) = + if tail is + Nil then toString(head) + Cons(x, Nil) then toString(head) ++ sep ++ toString(x) + Cons(x, xs) then toString(head) ++ sep ++ toString(x) ++ sep ++ xs.join(sep) +} +module Nil { + fun join(sep: Str): Str = "" + fun contains(x): Bool = false +} +//│ type List[A] = Cons[A] | Nil +//│ class Cons[A](head: A, tail: List[A]) { +//│ fun join: (sep: Str) -> Str +//│ } +//│ module Nil { +//│ fun contains: anything -> Bool +//│ fun join: (sep: Str) -> Str +//│ } + + +fun (::) cons[A](x: A, xs: List[A]): List[A] = Cons(x, xs) +//│ fun (::) cons: forall 'A. (x: 'A, xs: List['A]) -> List['A] + +(1 :: Nil).join(", ") +(1 :: (2 :: Nil)).join(", ") +(1 :: (2 :: (3 :: Nil))).join(", ") +(1 :: (2 :: (3 :: (4 :: Nil)))).join(", ") +(1 :: (2 :: (3 :: (4 :: (5 :: Nil))))).join(", ") +//│ Str +//│ res +//│ = '1' +//│ res +//│ = '1, 2' +//│ res +//│ = '1, 2, 3' +//│ res +//│ = '1, 2, 3, 4' +//│ res +//│ = '1, 2, 3, 4, 5' + + +fun (:-) listExclude(xs, x) = + if xs is + Nil then Nil + Cons(x', xs') and + x === x' then xs' :- x + else x' :: (xs' :- x) +//│ fun (:-) listExclude: forall 'A. (Cons['A] | Nil, Eql['A]) -> (Nil | List['A]) + +(Nil :- 0).join(", ") +((0 :: Nil) :- 0).join(", ") +((1 :: Nil) :- 0).join(", ") +((1 :: (2 :: Nil)) :- 0).join(", ") +//│ Str +//│ res +//│ = '' +//│ res +//│ = '' +//│ res +//│ = '1' +//│ res +//│ = '1, 2' + + +("x" :: Nil).join(", ") +//│ Str +//│ res +//│ = 'x' + + diff --git a/shared/src/test/diff/nu/i191.mls b/shared/src/test/diff/nu/i191.mls new file mode 100644 index 0000000000..2601152466 --- /dev/null +++ b/shared/src/test/diff/nu/i191.mls @@ -0,0 +1,169 @@ +:NewDefs + + +class demo() { constructor(param1: Int) } +val invalidConstruction = new demo(1) +//│ class demo() { +//│ constructor(param1: Int) +//│ } +//│ val invalidConstruction: demo +//│ invalidConstruction +//│ = demo {} + +:e +class demo() { constructor(param1: Int) } +val invalidConstruction = demo() +//│ ╔══[ERROR] Construction of class with auxiliary constructor should use the `new` keyword +//│ ║ l.15: val invalidConstruction = demo() +//│ ╙── ^^^^ +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.15: val invalidConstruction = demo() +//│ ║ ^^^^^^ +//│ ╟── argument of type `[]` does not match type `[param1: Int]` +//│ ║ l.15: val invalidConstruction = demo() +//│ ╙── ^^ +//│ class demo() { +//│ constructor(param1: Int) +//│ } +//│ val invalidConstruction: demo | error +//│ invalidConstruction +//│ = demo {} + +:e +val invalidConstruction = demo(1) +//│ ╔══[ERROR] Construction of class with auxiliary constructor should use the `new` keyword +//│ ║ l.33: val invalidConstruction = demo(1) +//│ ╙── ^^^^ +//│ val invalidConstruction: demo +//│ invalidConstruction +//│ = demo {} + +:e +val invalidConstruction = new demo +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.42: val invalidConstruction = new demo +//│ ║ ^^^^ +//│ ╟── argument list of type `[]` does not match type `[param1: Int]` +//│ ║ l.42: val invalidConstruction = new demo +//│ ╙── ^ +//│ val invalidConstruction: demo | error +//│ invalidConstruction +//│ = demo {} + +:e +class demo { constructor(param1: Int) } +val invalidConstruction = demo() +//│ ╔══[ERROR] Construction of unparameterized class demo should use the `new` keyword +//│ ║ l.55: val invalidConstruction = demo() +//│ ╙── ^^^^ +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.55: val invalidConstruction = demo() +//│ ║ ^^^^^^ +//│ ╟── argument of type `[]` does not match type `[param1: Int]` +//│ ║ l.55: val invalidConstruction = demo() +//│ ╙── ^^ +//│ class demo { +//│ constructor(param1: Int) +//│ } +//│ val invalidConstruction: demo | error +//│ invalidConstruction +//│ Runtime error: +//│ TypeError: Class constructor demo cannot be invoked without 'new' + +:e +val invalidConstruction = demo +//│ ╔══[ERROR] Construction of unparameterized class demo should use the `new` keyword +//│ ║ l.74: val invalidConstruction = demo +//│ ╙── ^^^^ +//│ val invalidConstruction: (param1: Int) -> demo +//│ invalidConstruction +//│ = [class demo] + +:e +val invalidConstruction = new demo +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.83: val invalidConstruction = new demo +//│ ║ ^^^^ +//│ ╟── argument list of type `[]` does not match type `[param1: Int]` +//│ ║ l.83: val invalidConstruction = new demo +//│ ╙── ^ +//│ val invalidConstruction: demo | error +//│ invalidConstruction +//│ = demo {} + +val invalidConstruction = new demo(1) +//│ val invalidConstruction: demo +//│ invalidConstruction +//│ = demo {} + +:e +val invalidConstruction = demo() +//│ ╔══[ERROR] Construction of unparameterized class demo should use the `new` keyword +//│ ║ l.100: val invalidConstruction = demo() +//│ ╙── ^^^^ +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.100: val invalidConstruction = demo() +//│ ║ ^^^^^^ +//│ ╟── argument of type `[]` does not match type `[param1: Int]` +//│ ║ l.100: val invalidConstruction = demo() +//│ ╙── ^^ +//│ val invalidConstruction: demo | error +//│ invalidConstruction +//│ Runtime error: +//│ TypeError: Class constructor demo cannot be invoked without 'new' + + +class demo(x: Int) { + constructor(param1: Int, param2: Int) { + log(param1) + log(param2) + x = param1*param2 + } +} +//│ class demo(x: Int) { +//│ constructor(param1: Int, param2: Int) +//│ } + +val validConstruction = new demo(5, 10) +//│ val validConstruction: demo +//│ validConstruction +//│ = demo {} +//│ // Output +//│ 5 +//│ 10 + +:e +val invalidConstruction = demo(5) +//│ ╔══[ERROR] Construction of class with auxiliary constructor should use the `new` keyword +//│ ║ l.136: val invalidConstruction = demo(5) +//│ ╙── ^^^^ +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.136: val invalidConstruction = demo(5) +//│ ║ ^^^^^^^ +//│ ╟── argument of type `[5]` does not match type `[param1: Int, param2: Int]` +//│ ║ l.136: val invalidConstruction = demo(5) +//│ ╙── ^^^ +//│ val invalidConstruction: demo | error +//│ invalidConstruction +//│ = demo {} +//│ // Output +//│ 5 +//│ undefined + +:e +val invalidConstruction = new demo(5) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.154: val invalidConstruction = new demo(5) +//│ ║ ^^^^^^^^^^^ +//│ ╟── argument list of type `[5]` does not match type `[param1: Int, param2: Int]` +//│ ║ l.154: val invalidConstruction = new demo(5) +//│ ╙── ^^^ +//│ val invalidConstruction: demo | error +//│ invalidConstruction +//│ = demo {} +//│ // Output +//│ 5 +//│ undefined + + + diff --git a/shared/src/test/diff/nu/repro0.mls b/shared/src/test/diff/nu/repro0.mls new file mode 100644 index 0000000000..c02b9aa19e --- /dev/null +++ b/shared/src/test/diff/nu/repro0.mls @@ -0,0 +1,22 @@ +:NewDefs +:NoJS + + +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) +//│ 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/nu/repro1.mls b/shared/src/test/diff/nu/repro1.mls new file mode 100644 index 0000000000..09c7cb8328 --- /dev/null +++ b/shared/src/test/diff/nu/repro1.mls @@ -0,0 +1,47 @@ +:NewDefs +:NoJS + + +class Union[out Region](val a: Region) +// class Union[Region](a: Region) +//│ class Union[Region](a: Region) + +fun go(x) = Union(go(x)) +let circles = go(2) +//│ fun go: forall 'Region. anything -> 'Region +//│ let circles: forall 'Region0. 'Region0 +//│ where +//│ 'Region0 :> Union['Region0] +//│ 'Region :> Union['Region] + + +fun contains(a) = + if a is Union then contains(a.a) +//│ fun contains: forall 'a. 'a -> nothing +//│ where +//│ 'a <: Union['a] + +contains(circles) +//│ nothing + + +mixin Contains { + fun contains(a) = + if a is Union then this.contains(a.a) +} +//│ mixin Contains() { +//│ this: {contains: 'a -> 'b} +//│ fun contains: Union['a] -> 'b +//│ } + +module TestContains extends Contains +//│ module TestContains { +//│ fun contains: 'a -> nothing +//│ } +//│ where +//│ 'a <: Union['a] + +TestContains.contains(circles) +//│ nothing + + diff --git a/shared/src/test/diff/nu/repro_EvalNegNeg.mls b/shared/src/test/diff/nu/repro_EvalNegNeg.mls new file mode 100644 index 0000000000..af761f40b9 --- /dev/null +++ b/shared/src/test/diff/nu/repro_EvalNegNeg.mls @@ -0,0 +1,52 @@ +:NewDefs + + +class Add(lhs: E, rhs: E) +class Lit(n: Int) +class Neg(expr: A) +//│ class Add[E](lhs: E, rhs: E) +//│ class Lit(n: Int) +//│ class Neg[A](expr: A) + + +// Note the inferred type because of current UCS limitation +mixin EvalBase { + fun eval(e) = + if e is Neg(Neg(d)) then this.eval(d) + else if e is Neg(d) then 0 - this.eval(d) + else if e is + Lit(n) then n + Add(l, r) then this.eval(l) + this.eval(r) +} +//│ mixin EvalBase() { +//│ this: {eval: 'a -> 'b & 'c -> Int} +//│ fun eval: (Add['c] | Lit | Neg['c & (Neg['a] | Object & ~#Neg)]) -> (Int | 'b) +//│ } + +// module TestLang extends EvalBase, EvalNeg +module TestLang extends EvalBase +//│ module TestLang { +//│ fun eval: 'a -> Int +//│ } +//│ where +//│ 'a <: Add['a] | Lit | Neg['a & (Neg['a] | Object & ~#Neg)] + + +fun mk(n) = if n is + 0 then Lit(0) + 1 then Neg(mk(n)) + _ then Add(mk(n), mk(n)) +//│ fun mk: forall 'a. Object -> (Lit | 'a) +//│ where +//│ 'a :> Neg[Lit | 'a] | Add[Lit | 'a] + +:stats +TestLang.eval(mk(0)) +//│ Int +//│ res +//│ = 0 +//│ constrain calls : 211 +//│ annoying calls : 51 +//│ subtyping calls : 1399 + + diff --git a/shared/src/test/diff/nu/repro_PolymorphicVariants.mls b/shared/src/test/diff/nu/repro_PolymorphicVariants.mls new file mode 100644 index 0000000000..461bada4e8 --- /dev/null +++ b/shared/src/test/diff/nu/repro_PolymorphicVariants.mls @@ -0,0 +1,43 @@ +:NewDefs + +// Test that reprduces subtle type simplification bug + +class Cons[out A](head: A, tail: Cons[A] | Nil) +module Nil +//│ class Cons[A](head: A, tail: Cons[A] | Nil) +//│ module Nil + +class Abs[A](x: string, t: A) +class App[A](s: A, t: A) +//│ class Abs[A](x: string, t: A) +//│ class App[A](s: A, t: A) + +fun eval(sub, v) = + if v is + Abs(x, t) then + eval(Cons([error, error], Nil), error) + eval(Cons(error, sub), error) + error +//│ fun eval: (Cons[anything] | Nil, Abs[anything]) -> nothing + +mixin EvalLambda { + fun eval(sub, v) = + if v is + Abs(x, t) then + this.eval(Cons([error, error], Nil), error) + this.eval(Cons([x, error], sub), error) + error +} +//│ mixin EvalLambda() { +//│ this: {eval: (Cons[[string, nothing] | 'A], nothing) -> ()} +//│ fun eval: (Cons['A] | Nil, Abs[anything]) -> nothing +//│ } + +// * Note: this used to crash because of a current type simplification bug: analyze2 does not traverse TVs witht he correct PolMap +// * The original unreduced version in PolymorphicVariants.mls still crashes... +// :ds +module Test1 extends EvalLambda +//│ module Test1 { +//│ fun eval: (Cons[anything] | Nil, Abs[anything]) -> nothing +//│ } + diff --git a/shared/src/test/diff/parser/Arrays.mls b/shared/src/test/diff/parser/Arrays.mls index 3b297fb2df..24bf9bfe9d 100644 --- a/shared/src/test/diff/parser/Arrays.mls +++ b/shared/src/test/diff/parser/Arrays.mls @@ -3,35 +3,35 @@ [] //│ |[||]| -//│ Parsed: {'(' ')'} +//│ Parsed: {[]} [1] //│ |[|1|]| -//│ Parsed: {'(' 1, ')'} +//│ Parsed: {[1,]} [1,] //│ |[|1|,|]| -//│ Parsed: {'(' 1, ')'} +//│ Parsed: {[1,]} [1, 2, 3] //│ |[|1|,| |2|,| |3|]| -//│ Parsed: {'(' 1, 2, 3, ')'} +//│ Parsed: {[1, 2, 3,]} () //│ |(||)| -//│ Parsed: {'(' ')'} +//│ Parsed: {undefined} (1) //│ |(|1|)| -//│ Parsed: {'(' 1, ')'} +//│ Parsed: {'(' 1 ')'} (1,) //│ |(|1|,|)| -//│ Parsed: {'(' 1, ')'} +//│ Parsed: {'(' 1 ')'} (1, 2, 3) //│ |(|1|,| |2|,| |3|)| -//│ Parsed: {'(' 1, 2, 3, ')'} +//│ Parsed: {,(1, ,(2, 3,),)} 1 @@ -40,88 +40,82 @@ 1, //│ |1|,| -//│ ╔══[PARSE ERROR] Expected end of input; found comma instead +//│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.41: 1, -//│ ╙── ^ -//│ Parsed: {1} +//│ ╙── ^ +//│ Parsed: {,(1, undefined,)} 1, 2, 3 //│ |1|,| |2|,| |3| -//│ ╔══[PARSE ERROR] Expected end of input; found comma instead -//│ ║ l.48: 1, 2, 3 -//│ ╙── ^ -//│ Parsed: {1} +//│ Parsed: {,(1, ,(2, 3,),)} f of 1, 2, 3 //│ |f| |#of| |1|,| |2|,| |3| -//│ Parsed: {f (1, 2, 3,)} +//│ Parsed: {f(1, 2, 3,)} f of 1, 2, 3 //│ |f| |#of|→|1|,| |2|,| |3|←| -//│ Parsed: {f (1, 2, 3,)} +//│ Parsed: {f(1, 2, 3,)} f of 1, 2, 3 //│ |f| |#of|→|1|,|↵|2|,|↵|3|←| -//│ Parsed: {f (1, 2, 3,)} +//│ Parsed: {f(1, 2, 3,)} let arr = [] //│ |#let| |arr| |#=| |[||]| -//│ Parsed: {let arr = '(' ')'} +//│ Parsed: {let arr = []} let arr = [ ] //│ |#let| |arr| |#=| |[|↵|]| -//│ Parsed: {let arr = '(' ')'} +//│ Parsed: {let arr = []} let arr = [ ] //│ |#let| |arr| |#=|↵|[|↵|]| //│ ╔══[PARSE ERROR] Unexpected newline in expression position -//│ ║ l.81: let arr = +//│ ║ l.78: let arr = //│ ║ ^ -//│ ║ l.82: [ +//│ ║ l.79: [ //│ ╙── -//│ Parsed: {let arr = '(' ')'} +//│ Parsed: {let arr = []} let arr = [ 1 ] //│ |#let| |arr| |#=| |[|→|1|←|↵|]| -//│ Parsed: {let arr = '(' 1, ')'} +//│ Parsed: {let arr = [1,]} let arr = [ 1, 2 ] //│ |#let| |arr| |#=| |[|→|1|,| |2|←|↵|]| -//│ Parsed: {let arr = '(' 1, 2, ')'} +//│ Parsed: {let arr = [1, 2,]} let arr = [ 1, 2 ] //│ |#let| |arr| |#=| |[|→|1|,|↵|2|←|↵|]| -//│ Parsed: {let arr = '(' 1, 2, ')'} +//│ Parsed: {let arr = [1, 2,]} -:pe +// :pe f [1, 2, 3] //│ |f| |[|1|,| |2|,| |3|]| -//│ ╔══[PARSE ERROR] Unexpected comma here -//│ ║ l.112: f [1, 2, 3] -//│ ╙── ^ -//│ Parsed: {(f)[1]} +//│ Parsed: {f‹1, 2, 3›} f([1, 2, 3]) //│ |f|(|[|1|,| |2|,| |3|]|)| -//│ Parsed: {f ('(' 1, 2, 3, ')',)} +//│ Parsed: {f([1, 2, 3,],)} f of [1, 2, 3] //│ |f| |#of| |[|1|,| |2|,| |3|]| -//│ Parsed: {f ('(' 1, 2, 3, ')',)} +//│ Parsed: {f([1, 2, 3,],)} diff --git a/shared/src/test/diff/parser/BasicSyntax.mls b/shared/src/test/diff/parser/BasicSyntax.mls index 766c419518..df6b16a603 100644 --- a/shared/src/test/diff/parser/BasicSyntax.mls +++ b/shared/src/test/diff/parser/BasicSyntax.mls @@ -9,50 +9,50 @@ f 1 //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.7: f 1 //│ ╙── ^^^ -//│ Parsed: {f (1,)} +//│ Parsed: {f(1,)} () //│ |(||)| -//│ Parsed: {'(' ')'} +//│ Parsed: {undefined} f() //│ |f|(||)| -//│ Parsed: {f ()} +//│ Parsed: {f()} f(1) //│ |f|(|1|)| -//│ Parsed: {f (1,)} +//│ Parsed: {f(1,)} f (1) //│ |f| |(|1|)| -//│ Parsed: {f (1,)} +//│ Parsed: {f(1,)} f of 1 //│ |f| |#of| |1| -//│ Parsed: {f (1,)} +//│ Parsed: {f(1,)} 1 + 1 //│ |1| |+| |1| -//│ Parsed: {+ (1,) (1,)} +//│ Parsed: {+(1,)(1,)} f 1 + 1 //│ |f| |1| |+| |1| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.38: f 1 + 1 //│ ╙── ^^^^^^^ -//│ Parsed: {f (+ (1,) (1,),)} +//│ Parsed: {f(+(1,)(1,),)} f(1 + 1) //│ |f|(|1| |+| |1|)| -//│ Parsed: {f (+ (1,) (1,),)} +//│ Parsed: {f(+(1,)(1,),)} f of 1 + 1 //│ |f| |#of| |1| |+| |1| -//│ Parsed: {f (+ (1,) (1,),)} +//│ Parsed: {f(+(1,)(1,),)} f(1) + 1 //│ |f|(|1|)| |+| |1| -//│ Parsed: {+ (f (1,),) (1,)} +//│ Parsed: {+(f(1,),)(1,)} 1 2 3 //│ |1| |2| |3| @@ -62,21 +62,21 @@ f(1) + 1 //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.57: 1 2 3 //│ ╙── ^^^^^ -//│ Parsed: {1 (2 (3,),)} +//│ Parsed: {1(2(3,),)} 12 3 //│ |12| |3| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.67: 12 3 //│ ╙── ^^^^^ -//│ Parsed: {12 (3,)} +//│ Parsed: {12(3,)} 3 + 2 4 - 1 //│ |3| |+| |2| |4| |-| |1| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.74: 3 + 2 4 - 1 //│ ╙── ^^^^^^^ -//│ Parsed: {+ (3,) (2 (- (4,) (1,),),)} +//│ Parsed: {+(3,)(2(-(4,)(1,),),)} foo bar baz 1 @@ -102,7 +102,7 @@ foo bar //│ ║ ^^^^^^^ //│ ║ l.83: 2 3 //│ ╙── ^^^^^ -//│ Parsed: {foo (bar ({baz (1,); 2 (3,)},),)} +//│ Parsed: {foo(bar({baz(1,); 2(3,)},),)} foo bar baz 1 @@ -143,14 +143,14 @@ foo bar //│ ║ ^^^^^^^ //│ ║ l.110: 4 v5 //│ ╙── ^^^^^^ -//│ Parsed: {foo (bar ({baz (1 (2 (3,),),); 4 (v5,)},),)} +//│ Parsed: {foo(bar({baz(1(2(3,),),); 4(v5,)},),)} foo of bar of baz of 1 of 2 of 3 4 of v5 //│ |foo| |#of| |bar| |#of|→|baz| |#of| |1| |#of|→|2| |#of| |3|←|↵|4| |#of| |v5|←| -//│ Parsed: {foo (bar ({baz (1 (2 (3,),),); 4 (v5,)},),)} +//│ Parsed: {foo(bar({baz(1(2(3,),),); 4(v5,)},),)} foo 1 @@ -160,52 +160,52 @@ foo //│ ║ ^^^ //│ ║ l.156: 1 //│ ╙── ^^^ -//│ Parsed: {foo (1,)} +//│ Parsed: {foo(1,)} foo of 1 //│ |foo| |#of|→|1|←| -//│ Parsed: {foo (1,)} +//│ Parsed: {foo(1,)} foo of 1 //│ |foo|→|#of| |1|←| -//│ Parsed: {foo (1,)} +//│ Parsed: {foo(1,)} foo of 1, 2, 3 //│ |foo|→|#of| |1|,| |2|,| |3|←| -//│ Parsed: {foo (1, 2, 3,)} +//│ Parsed: {foo(1, 2, 3,)} foo of (1, 2, 3) //│ |foo|→|#of| |(|1|,| |2|,| |3|)|←| -//│ Parsed: {foo ('(' 1, 2, 3, ')',)} +//│ Parsed: {foo(,(1, ,(2, 3,),),)} foo of 1 //│ |foo|→|#of|→|1|←|←| -//│ Parsed: {foo (1,)} +//│ Parsed: {foo(1,)} // TODO foo of 1 of 2 //│ |foo|→|#of| |1|↵|#of| |2|←| -//│ Parsed: {foo (1,) (2,)} +//│ Parsed: {foo(1,)(2,)} foo of f of 2 //│ |foo|→|#of| |f|→|#of| |2|←|←| -//│ Parsed: {foo (f (2,),)} +//│ Parsed: {foo(f(2,),)} foo of 1 of 2 //│ |foo|→|#of| |1|←|→|#of| |2|←| -//│ Parsed: {foo (1,) (2,)} +//│ Parsed: {foo(1,)(2,)} //│ | | @@ -222,7 +222,7 @@ foo (1) //│ |(|1|)| -//│ Parsed: {'(' 1, ')'} +//│ Parsed: {'(' 1 ')'} (1 //│ ╔══[PARSE ERROR] Unmatched opening parenthesis @@ -236,7 +236,7 @@ foo //│ ║ l.234: (1)) //│ ╙── ^ //│ |(|1|)| -//│ Parsed: {'(' 1, ')'} +//│ Parsed: {'(' 1 ')'} ( //│ ╔══[PARSE ERROR] Unmatched opening parenthesis @@ -257,7 +257,7 @@ foo //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.255: 1+ //│ ╙── ^ -//│ Parsed: {+ (1,) (undefined,)} +//│ Parsed: {+(1,)(undefined,)} * //│ |*| @@ -275,15 +275,15 @@ f 1 //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.273: f 1 //│ ╙── ^^^ -//│ Parsed: {f (1,)} +//│ Parsed: {f(1,)} f(1) //│ |f|(|1|)| -//│ Parsed: {f (1,)} +//│ Parsed: {f(1,)} f (1) //│ |f| |(|1|)| -//│ Parsed: {f (1,)} +//│ Parsed: {f(1,)} f 1, 2, 3 @@ -291,180 +291,168 @@ f 1, 2, 3 //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.289: f 1, 2, 3 //│ ╙── ^^^^^^^^^ -//│ Parsed: {f (1, 2, 3,)} +//│ Parsed: {f(1, 2, 3,)} f (1, 2, 3) //│ |f| |(|1|,| |2|,| |3|)| -//│ Parsed: {f (1, 2, 3,)} +//│ Parsed: {f(1, 2, 3,)} f(1, 2, 3) //│ |f|(|1|,| |2|,| |3|)| -//│ Parsed: {f (1, 2, 3,)} +//│ Parsed: {f(1, 2, 3,)} -:pe +// :pe f[] //│ |f|[||]| -//│ ╔══[PARSE ERROR] Unexpected end of subscript; an expression was expected here -//│ ║ l.305: f[] -//│ ╙── ^ -//│ Parsed: {(f)[undefined]} +//│ Parsed: {f‹›} f[1] //│ |f|[|1|]| -//│ Parsed: {(f)[1]} +//│ Parsed: {f‹1›} f[1, 2, 3] //│ |f|[|1|,| |2|,| |3|]| -//│ ╔══[PARSE ERROR] Unexpected comma here -//│ ║ l.316: f[1, 2, 3] -//│ ╙── ^ -//│ Parsed: {(f)[1]} +//│ Parsed: {f‹1, 2, 3›} f{} //│ |f|{||}| -//│ ╔══[PARSE ERROR] Expected end of input; found curly brace section instead -//│ ║ l.323: f{} -//│ ╙── ^^ -//│ Parsed: {f} +//│ Parsed: {f {}} f{1} //│ |f|{|1|}| -//│ ╔══[PARSE ERROR] Expected end of input; found curly brace section instead -//│ ║ l.330: f{1} -//│ ╙── ^^^ -//│ Parsed: {f} +//│ Parsed: {f {1}} f{1, 2, 3} //│ |f|{|1|,| |2|,| |3|}| -//│ ╔══[PARSE ERROR] Expected end of input; found curly brace section instead -//│ ║ l.337: f{1, 2, 3} -//│ ╙── ^^^^^^^^^ -//│ Parsed: {f} +//│ Parsed: {f {,(1, ,(2, 3,),)}} f 1,, 2 //│ |f| |1|,|,| |2| //│ ╔══[PARSE ERROR] Unexpected comma in expression position -//│ ║ l.345: f 1,, 2 +//│ ║ l.330: f 1,, 2 //│ ╙── ^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.345: f 1,, 2 +//│ ║ l.330: f 1,, 2 //│ ╙── ^^^^^^^ -//│ Parsed: {f (1, 2,)} +//│ Parsed: {f(1, 2,)} f of x //│ |f| |#of| |x| -//│ Parsed: {f (x,)} +//│ Parsed: {f(x,)} f g x //│ |f| |g| |x| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.360: f g x +//│ ║ l.345: f g x //│ ╙── ^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.360: f g x +//│ ║ l.345: f g x //│ ╙── ^^^^^ -//│ Parsed: {f (g (x,),)} +//│ Parsed: {f(g(x,),)} f of g of x //│ |f| |#of| |g| |#of| |x| -//│ Parsed: {f (g (x,),)} +//│ Parsed: {f(g(x,),)} f of of g //│ |f| |#of| |#of| |g| //│ ╔══[PARSE ERROR] Unexpected 'of' keyword in expression position -//│ ║ l.374: f of of g +//│ ║ l.359: f of of g //│ ╙── ^^ -//│ Parsed: {f (g,)} +//│ Parsed: {f(g,)} f x: 1 //│ |f| |x|#:| |1| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.382: f x: 1 +//│ ║ l.367: f x: 1 //│ ╙── ^^^^^^ -//│ Parsed: {f (x: 1,)} +//│ Parsed: {f(x: 1,)} f x: 1, //│ |f| |x|#:| |1|,| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.389: f x: 1, +//│ ║ l.374: f x: 1, //│ ╙── ^^^^^^ -//│ Parsed: {f (x: 1,)} +//│ Parsed: {f(x: 1,)} f x : 1 //│ |f| |x| |#:| |1| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.396: f x : 1 +//│ ║ l.381: f x : 1 //│ ╙── ^^^ -//│ Parsed: {f (x : 1,)} +//│ Parsed: {f(x : 1,)} f x: 1, y: 2 //│ |f| |x|#:| |1|,| |y|#:| |2| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.403: f x: 1, y: 2 +//│ ║ l.388: f x: 1, y: 2 //│ ╙── ^^^^^^^^^^^^ -//│ Parsed: {f (x: 1, y: 2,)} +//│ Parsed: {f(x: 1, y: 2,)} f x : 1, y: 2 //│ |f| |x| |#:| |1|,| |y|#:| |2| +//│ ╔══[PARSE ERROR] Not a recognized type +//│ ║ l.395: f x : 1, y: 2 +//│ ╙── ^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.410: f x : 1, y: 2 -//│ ╙── ^^^^^^^^^^^^^ -//│ Parsed: {f (x : 1, y: 2,)} +//│ ║ l.395: f x : 1, y: 2 +//│ ╙── ^^^ +//│ Parsed: {f(x : anything,)} f x: 1, y: 2, z: 3 //│ |f| |x|#:| |1|,| |y|#:| |2|,| |z|#:| |3| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.417: f x: 1, y: 2, z: 3 +//│ ║ l.405: f x: 1, y: 2, z: 3 //│ ╙── ^^^^^^^^^^^^^^^^^^ -//│ Parsed: {f (x: 1, y: 2, z: 3,)} +//│ Parsed: {f(x: 1, y: 2, z: 3,)} f x: 1, y: g 2, z: 3 //│ |f| |x|#:| |1|,| |y|#:| |g| |2|,| |z|#:| |3| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.424: f x: 1, y: g 2, z: 3 +//│ ║ l.412: f x: 1, y: g 2, z: 3 //│ ╙── ^^^^^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.424: f x: 1, y: g 2, z: 3 +//│ ║ l.412: f x: 1, y: g 2, z: 3 //│ ╙── ^^^^^^^^^^^^^^^^^^^^ -//│ Parsed: {f (x: 1, y: g (2, z: 3,),)} +//│ Parsed: {f(x: 1, y: g(2, z: 3,),)} f(x: 1, y: g(2, z: 3)) //│ |f|(|x|#:| |1|,| |y|#:| |g|(|2|,| |z|#:| |3|)|)| -//│ Parsed: {f (x: 1, y: g (2, z: 3,),)} +//│ Parsed: {f(x: 1, y: g(2, z: 3,),)} f(x: 1, y: g(2), z: 3) //│ |f|(|x|#:| |1|,| |y|#:| |g|(|2|)|,| |z|#:| |3|)| -//│ Parsed: {f (x: 1, y: g (2,), z: 3,)} +//│ Parsed: {f(x: 1, y: g(2,), z: 3,)} f x: 1, y: g 2, z: 3 //│ |f| |x|#:| |1|,| |y|#:| |g| |2|,| |z|#:| |3| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.442: f x: 1, y: g 2, z: 3 +//│ ║ l.430: f x: 1, y: g 2, z: 3 //│ ╙── ^^^^^^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.442: f x: 1, y: g 2, z: 3 +//│ ║ l.430: f x: 1, y: g 2, z: 3 //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ -//│ Parsed: {f (x: 1, y: g (2, z: 3,),)} +//│ Parsed: {f(x: 1, y: g(2, z: 3,),)} f of x: 1, y: g of 2, z: 3 //│ |f| |#of| |x|#:| |1|,| |y|#:| |g| |#of| |2|,| |z|#:| |3| -//│ Parsed: {f (x: 1, y: g (2, z: 3,),)} +//│ Parsed: {f(x: 1, y: g(2, z: 3,),)} f x: 1 + 1, y: 2 2, z: 3 + 2 4 - 1 //│ |f| |x|#:| |1| |+| |1|,| |y|#:| |2| |2|,| |z|#:| |3| |+| |2| |4| |-| |1| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.456: f x: 1 + 1, y: 2 2, z: 3 + 2 4 - 1 +//│ ║ l.444: f x: 1 + 1, y: 2 2, z: 3 + 2 4 - 1 //│ ╙── ^^^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.456: f x: 1 + 1, y: 2 2, z: 3 + 2 4 - 1 +//│ ║ l.444: f x: 1 + 1, y: 2 2, z: 3 + 2 4 - 1 //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.456: f x: 1 + 1, y: 2 2, z: 3 + 2 4 - 1 +//│ ║ l.444: f x: 1 + 1, y: 2 2, z: 3 + 2 4 - 1 //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ Parsed: {f (x: + (1,) (1,), y: 2 (2, z: + (3,) (2 (- (4,) (1,),),),),)} +//│ Parsed: {f(x: +(1,)(1,), y: 2(2, z: +(3,)(2(-(4,)(1,),),),),)} x.y @@ -474,22 +462,22 @@ x.y .y //│ |.y| //│ ╔══[PARSE ERROR] Unexpected selector in expression position -//│ ║ l.474: .y +//│ ║ l.462: .y //│ ╙── ^^ //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here -//│ ║ l.474: .y +//│ ║ l.462: .y //│ ╙── ^ //│ Parsed: {undefined} 2 + f of 3 + 3 //│ |2| |+| |f| |#of| |3| |+| |3| -//│ Parsed: {+ (2,) (f (+ (3,) (3,),),)} +//│ Parsed: {+(2,)(f(+(3,)(3,),),)} 2 + 2 of 3 + 3 //│ |2| |+| |2|→|#of| |3| |+| |3|←| -//│ Parsed: {+ (2,) (2,) (+ (3,) (3,),)} +//│ Parsed: {+(2,)(2,)(+(3,)(3,),)} diff --git a/shared/src/test/diff/parser/Binds.mls b/shared/src/test/diff/parser/Binds.mls index 3beb9ad087..711f842512 100644 --- a/shared/src/test/diff/parser/Binds.mls +++ b/shared/src/test/diff/parser/Binds.mls @@ -3,11 +3,11 @@ f(x) is True //│ |f|(|x|)| |is| |True| -//│ Parsed: {is (f (x,),) (True,)} +//│ Parsed: {is(f(x,),)(True,)} // Precedence of 'of'/'is' may be surprising! f of x is True //│ |f| |#of| |x| |is| |True| -//│ Parsed: {f (is (x,) (True,),)} +//│ Parsed: {f(is(x,)(True,),)} diff --git a/shared/src/test/diff/parser/Blocks.mls b/shared/src/test/diff/parser/Blocks.mls index 72be1cae4f..f1cb7d0f4b 100644 --- a/shared/src/test/diff/parser/Blocks.mls +++ b/shared/src/test/diff/parser/Blocks.mls @@ -9,61 +9,55 @@ b a, b //│ |a|,|↵|b| -//│ ╔══[PARSE ERROR] Expected end of input; found comma instead -//│ ║ l.9: a, -//│ ╙── ^ -//│ Parsed: {a} +//│ Parsed: {,(a, b,)} a, b, //│ |a|,|↵|b|,| -//│ ╔══[PARSE ERROR] Expected end of input; found comma instead -//│ ║ l.17: a, -//│ ╙── ^ -//│ Parsed: {a} +//│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here +//│ ║ l.15: b, +//│ ╙── ^ +//│ Parsed: {,(a, ,(b, undefined,),)} a, b, c //│ |a|,|↵|b|,|↵|c| -//│ ╔══[PARSE ERROR] Expected end of input; found comma instead -//│ ║ l.25: a, -//│ ╙── ^ -//│ Parsed: {a} +//│ Parsed: {,(a, ,(b, c,),)} foo a b //│ |foo|→|a|↵|b|←| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.34: foo +//│ ║ l.28: foo //│ ║ ^^^ -//│ ║ l.35: a +//│ ║ l.29: a //│ ║ ^^^ -//│ ║ l.36: b +//│ ║ l.30: b //│ ╙── ^^^ -//│ Parsed: {foo ({a; b},)} +//│ Parsed: {foo({a; b},)} foo( a, b ) //│ |foo|(|→|a|,|↵|b|←|↵|)| -//│ Parsed: {foo (a, b,)} +//│ Parsed: {foo(a, b,)} foo( a, b, ) //│ |foo|(|→|a|,|↵|b|,|←|↵|)| -//│ Parsed: {foo (a, b,)} +//│ Parsed: {foo(a, b,)} foo( a b ) //│ |foo|(|→|a|↵|b|←|↵|)| -//│ Parsed: {foo ({a; b},)} +//│ Parsed: {foo({a; b},)} foo( a @@ -71,11 +65,11 @@ foo( ) //│ |foo|(|→|a|←|→|b|←|↵|)| //│ ╔══[PARSE ERROR] Unexpected indented block here -//│ ║ l.70: b +//│ ║ l.64: b //│ ║ ^^^ -//│ ║ l.71: ) +//│ ║ l.65: ) //│ ╙── ^ -//│ Parsed: {foo (a,)} +//│ Parsed: {foo(a,)} // TODO foo of @@ -84,31 +78,31 @@ foo of b //│ |foo| |#of|→|a|↵|#fun| |f| |#=| |1|↵|b|←| //│ ╔══[PARSE ERROR] Unexpected 'fun' keyword in expression position -//│ ║ l.83: fun f = 1 +//│ ║ l.77: fun f = 1 //│ ╙── ^^^ -//│ ╔══[PARSE ERROR] Unexpected '=' keyword here -//│ ║ l.83: fun f = 1 +//│ ╔══[PARSE ERROR] Unexpected '=' here +//│ ║ l.77: fun f = 1 //│ ╙── ^ -//│ Parsed: {foo ({a; f},)} +//│ Parsed: {foo({a; f},)} foo + a b //│ |foo| |+|→|a|↵|b|←| -//│ Parsed: {+ (foo,) ({a; b},)} +//│ Parsed: {+(foo,)({a; b},)} foo(a, b, c) foo of a, b, c //│ |foo|(|a|,| |b|,| |c|)|↵|foo| |#of| |a|,| |b|,| |c| -//│ Parsed: {foo (a, b, c,); foo (a, b, c,)} +//│ Parsed: {foo(a, b, c,); foo(a, b, c,)} foo of aaaaa, bbbbb, ccccc //│ |foo| |#of|→|aaaaa|,|↵|bbbbb|,|↵|ccccc|←| -//│ Parsed: {foo (aaaaa, bbbbb, ccccc,)} +//│ Parsed: {foo(aaaaa, bbbbb, ccccc,)} foo of a @@ -118,20 +112,20 @@ foo of c //│ |foo| |#of|→|a|↵|boo|→|x|↵|y|←|↵|c|←| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.115: boo +//│ ║ l.109: boo //│ ║ ^^^ -//│ ║ l.116: x +//│ ║ l.110: x //│ ║ ^^^^^ -//│ ║ l.117: y +//│ ║ l.111: y //│ ╙── ^^^^^ -//│ Parsed: {foo ({a; boo ({x; y},); c},)} +//│ Parsed: {foo({a; boo({x; y},); c},)} fun foo = print("ok") print("ko") //│ |#fun| |foo| |#=|→|print|(|"ok"|)|↵|print|(|"ko"|)|←| -//│ Parsed: {fun foo = {print ("ok",); print ("ko",)}} +//│ Parsed: {fun foo = {print("ok",); print("ko",)}} fun foo = @@ -140,14 +134,14 @@ fun foo = print("ko") //│ |#fun| |foo| |#=|→|print|(|"ok"|)|↵|print|(|"ko"|)|↵|←| -//│ Parsed: {fun foo = {print ("ok",); print ("ko",)}} +//│ Parsed: {fun foo = {print("ok",); print("ko",)}} fun foo = fun local(x) = x + 1 print(local(1)) class Foo //│ |#fun| |foo| |#=|→|#fun| |local|(|x|)| |#=| |x| |+| |1|↵|print|(|local|(|1|)|)|↵|#class| |Foo|←| -//│ Parsed: {fun foo = {fun local = (x,) => + (x,) (1,); print (local (1,),); class Foo() {}}} +//│ Parsed: {fun foo = {fun local = (x,) => +(x,)(1,); print(local(1,),); class Foo {}}} fun foo = fun local(x) = @@ -161,6 +155,31 @@ fun foo = print of local of 0 + local of 1 fun tmp = 2 //│ |#fun| |foo| |#=|→|#fun| |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 = {fun 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: {fun foo = {fun 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}} + + + +log(1); log(a) +//│ |log|(|1|)|;| |log|(|a|)| +//│ Parsed: {log(1,); log(a,)} + +constructor(){ + a = 1 + a = 2 +} +//│ |#constructor|(||)|{|→|a| |#=| |1|↵|a| |#=| |2|←|↵|}| +//│ Parsed: {constructor() {a = 1; a = 2}} + +a = 1; log(a) +//│ |a| |#=| |1|;| |log|(|a|)| +//│ Parsed: {a = 1; log(a,)} + +:pe +f(a) = 1 +//│ |f|(|a|)| |#=| |1| +//│ ╔══[PARSE ERROR] Expected end of input; found '=' instead +//│ ║ l.178: f(a) = 1 +//│ ╙── ^ +//│ Parsed: {f(a,)} diff --git a/shared/src/test/diff/parser/Brackets.mls b/shared/src/test/diff/parser/Brackets.mls index 03264709ae..b12bb26104 100644 --- a/shared/src/test/diff/parser/Brackets.mls +++ b/shared/src/test/diff/parser/Brackets.mls @@ -1,11 +1,11 @@ () //│ |(||)| -//│ Parsed: {'(' ')'} +//│ Parsed: {undefined} [] //│ |[||]| -//│ Parsed: {'(' ')'} +//│ Parsed: {[]} {} //│ |{||}| @@ -20,11 +20,11 @@ //│ ║ l.15: (} //│ ╙── ^ //│ |(||)| -//│ Parsed: {'(' ')'} +//│ Parsed: {undefined} (([{}])) //│ |(|(|[|{||}|]|)|)| -//│ Parsed: {'(' '(' '(' '{' {} '}', ')', ')', ')'} +//│ Parsed: {'(' '(' ['{' {} '}',] ')' ')'} :pe (([{})]) @@ -41,10 +41,10 @@ //│ ║ l.30: (([{})]) //│ ╙── ^ //│ |(|(|[|{||}|]|)|)| -//│ Parsed: {'(' '(' '(' '{' {} '}', ')', ')', ')'} +//│ Parsed: {'(' '(' ['{' {} '}',] ')' ')'} fun f = () //│ |#fun| |f| |#=| |(||)| -//│ Parsed: {fun f = '(' ')'} +//│ Parsed: {fun f = undefined} diff --git a/shared/src/test/diff/parser/Classes.mls b/shared/src/test/diff/parser/Classes.mls index 4e64a1e65a..b618b46647 100644 --- a/shared/src/test/diff/parser/Classes.mls +++ b/shared/src/test/diff/parser/Classes.mls @@ -1,19 +1,18 @@ -:AllowParseErrors class Foo //│ |#class| |Foo| -//│ Parsed: {class Foo() {}} +//│ Parsed: {class Foo {}} class Foo {} //│ |#class| |Foo| |{||}| -//│ Parsed: {class Foo() {}} +//│ Parsed: {class Foo {}} class Foo { fun foo: int } //│ |#class| |Foo| |{|→|#fun| |foo|#:| |int|←|↵|}| -//│ Parsed: {class Foo() {fun foo: int}} +//│ Parsed: {class Foo {fun foo: int}} class Foo { class Bar { @@ -21,41 +20,55 @@ class Foo { } } //│ |#class| |Foo| |{|→|#class| |Bar| |{|→|#class| |Baz|←|↵|}|←|↵|}| -//│ Parsed: {class Foo() {class Bar() {class Baz() {}}}} +//│ Parsed: {class Foo {class Bar {class Baz {}}}} class Foo: Bar //│ |#class| |Foo|#:| |Bar| -//│ Parsed: {class Foo(): Bar {}} +//│ Parsed: {class Foo: Bar {}} + +class Foo extends Bar, Baz +//│ |#class| |Foo| |#extends| |Bar|,| |Baz| +//│ Parsed: {class Foo: Bar, Baz {}} class Foo: Bar, Baz //│ |#class| |Foo|#:| |Bar|,| |Baz| -//│ Parsed: {class Foo(): Bar, Baz {}} +//│ Parsed: {class Foo: ,[Bar, Baz] {}} class Foo: Bar { fun f = 0 } //│ |#class| |Foo|#:| |Bar| |{| |#fun| |f| |#=| |0| |}| -//│ Parsed: {class Foo(): Bar {fun f = 0}} +//│ Parsed: {class Foo: Bar {fun f = 0}} + +class Foo extends Bar, Baz { fun f = 0 } +//│ |#class| |Foo| |#extends| |Bar|,| |Baz| |{| |#fun| |f| |#=| |0| |}| +//│ Parsed: {class Foo: Bar, Baz {fun f = 0}} +:pe class Foo: Bar, Baz { fun f = 0 } //│ |#class| |Foo|#:| |Bar|,| |Baz| |{| |#fun| |f| |#=| |0| |}| -//│ Parsed: {class Foo(): Bar, Baz {fun f = 0}} +//│ ╔══[PARSE ERROR] Not a recognized type +//│ ║ l.46: class Foo: Bar, Baz { fun f = 0 } +//│ ╙── ^^^^^^^^^^^^^^^^^ +//│ Parsed: {class Foo: anything {}} // * Pretty confusing... better reject this: +:pe +:w class Foo: Bar { fun f = 0 fun bar = 1 } //│ |#class| |Foo|#:| |Bar| |{| |#fun| |f| |#=| |0|→|#fun| |bar| |#=| |1|←|↵|}| //│ ╔══[PARSE ERROR] Unexpected 'fun' keyword in expression position -//│ ║ l.44: fun bar = 1 +//│ ║ l.57: fun bar = 1 //│ ╙── ^^^ -//│ ╔══[PARSE ERROR] Unexpected '=' keyword here -//│ ║ l.44: fun bar = 1 +//│ ╔══[PARSE ERROR] Unexpected '=' here +//│ ║ l.57: fun bar = 1 //│ ╙── ^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.43: class Foo: Bar { fun f = 0 +//│ ║ l.56: class Foo: Bar { fun f = 0 //│ ║ ^ -//│ ║ l.44: fun bar = 1 +//│ ║ l.57: fun bar = 1 //│ ╙── ^^^^^^^^^ -//│ Parsed: {class Foo(): Bar {fun f = 0 (bar,)}} +//│ Parsed: {class Foo: Bar {fun f = 0(bar,)}} // TODO disallow? (i.e., require unindent before closing brace) // :e @@ -64,14 +77,14 @@ class Foo: Bar { fun bar = 1 } //│ |#class| |Foo|#:| |Bar| |{|→|#fun| |f| |#=| |0|↵|#fun| |bar| |#=| |1|↵|←|}| -//│ Parsed: {class Foo(): Bar {fun f = 0; fun bar = 1}} +//│ Parsed: {class Foo: Bar {fun f = 0; fun bar = 1}} class Foo: Bar { fun f = 0 fun bar = 1 } //│ |#class| |Foo|#:| |Bar| |{|→|#fun| |f| |#=| |0|↵|#fun| |bar| |#=| |1|←|↵|}| -//│ Parsed: {class Foo(): Bar {fun f = 0; fun bar = 1}} +//│ Parsed: {class Foo: Bar {fun f = 0; fun bar = 1}} class Foo: Bar { @@ -81,20 +94,20 @@ class Foo: Bar { } //│ |#class| |Foo|#:| |Bar| |{|→|#fun| |f| |#=| |0|↵|#fun| |bar| |#=| |1|←|↵|}| -//│ Parsed: {class Foo(): Bar {fun f = 0; fun bar = 1}} +//│ Parsed: {class Foo: Bar {fun f = 0; fun bar = 1}} class Foo: Bar { } //│ |#class| |Foo|#:| |Bar| |{|↵|}| -//│ Parsed: {class Foo(): Bar {}} +//│ Parsed: {class Foo: Bar {}} fun foo = class Foo: Bar { } //│ |#fun| |foo| |#=|→|#class| |Foo|#:| |Bar| |{|↵|}|←| -//│ Parsed: {fun foo = {class Foo(): Bar {}}} +//│ Parsed: {fun foo = {class Foo: Bar {}}} class Foo() //│ |#class| |Foo|(||)| @@ -107,16 +120,16 @@ class Foo(x, y, z) class Foo(x, y, z): Bar(z, x) //│ |#class| |Foo|(|x|,| |y|,| |z|)|#:| |Bar|(|z|,| |x|)| -//│ Parsed: {class Foo(x, y, z,): Bar (z, x,) {}} +//│ Parsed: {class Foo(x, y, z,): Bar[z, x] {}} class Foo(x, y, z): Bar(z, x) { fun blah(x) = x + y } //│ |#class| |Foo|(|x|,| |y|,| |z|)|#:| |Bar|(|z|,| |x|)| |{|→|#fun| |blah|(|x|)| |#=| |x| |+| |y|←|↵|}| -//│ Parsed: {class Foo(x, y, z,): Bar (z, x,) {fun blah = (x,) => + (x,) (y,)}} +//│ Parsed: {class Foo(x, y, z,): Bar[z, x] {fun blah = (x,) => +(x,)(y,)}} -class Foo(x, y): Bar(y, x), Baz(x + y) -//│ |#class| |Foo|(|x|,| |y|)|#:| |Bar|(|y|,| |x|)|,| |Baz|(|x| |+| |y|)| -//│ Parsed: {class Foo(x, y,): Bar (y, x,), Baz (+ (x,) (y,),) {}} +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,),) {}} diff --git a/shared/src/test/diff/parser/Comments.mls b/shared/src/test/diff/parser/Comments.mls index 9d52a520bc..1d3949e079 100644 --- a/shared/src/test/diff/parser/Comments.mls +++ b/shared/src/test/diff/parser/Comments.mls @@ -21,5 +21,5 @@ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.17: 1 //│ ╙── ^ -//│ Parsed: {1 (); 2} +//│ Parsed: {1(); 2} diff --git a/shared/src/test/diff/parser/ControversialIfSplits.mls b/shared/src/test/diff/parser/ControversialIfSplits.mls index 79fc46c831..3be9c49736 100644 --- a/shared/src/test/diff/parser/ControversialIfSplits.mls +++ b/shared/src/test/diff/parser/ControversialIfSplits.mls @@ -8,15 +8,15 @@ if f of //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here //│ ║ l.5: 0 then "ok" //│ ╙── ^^^^ -//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause; found application instead +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application instead //│ ║ l.4: if f of //│ ║ ^^^^ //│ ║ l.5: 0 then "ok" //│ ║ ^^^^ -//│ ╟── Note: 'if' expression started here: +//│ ╟── Note: 'if' expression starts here: //│ ║ l.4: if f of //│ ╙── ^^ -//│ Parsed: {if (f (0,)) then undefined} +//│ Parsed: {if (f(0,)) then undefined} if f ( 0 then "ok" @@ -26,7 +26,7 @@ if f ( //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here //│ ║ l.22: 0 then "ok" //│ ╙── ^^^^ -//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause; found application instead +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application instead //│ ║ l.21: if f ( //│ ║ ^^^ //│ ║ l.22: 0 then "ok" @@ -35,10 +35,10 @@ if f ( //│ ║ ^^^^^^^^^^^^^ //│ ║ l.24: ) //│ ║ ^ -//│ ╟── Note: 'if' expression started here: +//│ ╟── Note: 'if' expression starts here: //│ ║ l.21: if f ( //│ ╙── ^^ -//│ Parsed: {if (f (0,)) then undefined} +//│ Parsed: {if (f(0,)) then undefined} if f of 0 then "ok" @@ -48,15 +48,15 @@ if f of //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here //│ ║ l.44: 0 then "ok" //│ ╙── ^^^^ -//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause; found application instead +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application instead //│ ║ l.43: if f of //│ ║ ^^^^ //│ ║ l.44: 0 then "ok" //│ ║ ^^^^ -//│ ╟── Note: 'if' expression started here: +//│ ╟── Note: 'if' expression starts here: //│ ║ l.43: if f of //│ ╙── ^^ -//│ Parsed: {if (f (0,)) then undefined} +//│ Parsed: {if (f(0,)) then undefined} if f of 0 is 0 then "ok" @@ -65,15 +65,15 @@ if f of //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here //│ ║ l.62: 0 is 0 then "ok" //│ ╙── ^^^^ -//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause; found application instead +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application instead //│ ║ l.61: if f of //│ ║ ^^^^ //│ ║ l.62: 0 is 0 then "ok" //│ ║ ^^^^^^^^ -//│ ╟── Note: 'if' expression started here: +//│ ╟── Note: 'if' expression starts here: //│ ║ l.61: if f of //│ ╙── ^^ -//│ Parsed: {if (f (is (0,) (0,),)) then undefined} +//│ Parsed: {if (f(is(0,)(0,),)) then undefined} diff --git a/shared/src/test/diff/parser/FatArrows.mls b/shared/src/test/diff/parser/FatArrows.mls new file mode 100644 index 0000000000..6f0a9c4093 --- /dev/null +++ b/shared/src/test/diff/parser/FatArrows.mls @@ -0,0 +1,57 @@ +:AllowParseErrors + + +fun f: (x: Int) => Int +//│ |#fun| |f|#:| |(|x|#:| |Int|)| |#=>| |Int| +//│ Parsed: {fun f: (x: Int) -> Int} + +fun f: (x: Int => Int, y: Int) => Int +//│ |#fun| |f|#:| |(|x|#:| |Int| |#=>| |Int|,| |y|#:| |Int|)| |#=>| |Int| +//│ Parsed: {fun f: (x: Int -> Int, y: Int) -> Int} + +fun f: (x: Int, y: Int => Int) => Int +//│ |#fun| |f|#:| |(|x|#:| |Int|,| |y|#:| |Int| |#=>| |Int|)| |#=>| |Int| +//│ Parsed: {fun f: (x: Int, y: Int -> Int) -> Int} + +fun f: (x: Int, y: (Int, Int) => Int) => Int +//│ |#fun| |f|#:| |(|x|#:| |Int|,| |y|#:| |(|Int|,| |Int|)| |#=>| |Int|)| |#=>| |Int| +//│ Parsed: {fun f: (x: Int, y: (Int, Int) -> Int) -> Int} + +fun f: (x: Int) +//│ |#fun| |f|#:| |(|x|#:| |Int|)| +//│ ╔══[PARSE ERROR] Illegal position for field specification +//│ ║ l.20: fun f: (x: Int) +//│ ╙── ^^^^^^ +//│ Parsed: {fun f: Int} + +fun f: (x: Int, y: Int) +//│ |#fun| |f|#:| |(|x|#:| |Int|,| |y|#:| |Int|)| +//│ ╔══[PARSE ERROR] Illegal position for field specification +//│ ║ l.27: fun f: (x: Int, y: Int) +//│ ╙── ^^^^^^ +//│ ╔══[PARSE ERROR] Illegal position for field specification +//│ ║ l.27: fun f: (x: Int, y: Int) +//│ ╙── ^^^^^^ +//│ Parsed: {fun f: ,[Int, Int]} + +fun match: forall 'res: (() -> Int, ifCons: Int) => Int +//│ |#fun| |match|#:| |#forall| |'res|#:| |(|(||)| |->| |Int|,| |ifCons|#:| |Int|)| |#=>| |Int| +//│ Parsed: {fun match: forall 'res. (() -> Int, ifCons: Int) -> Int} + +fun match: forall 'res: (() => Int, ifCons: Int) => Int +//│ |#fun| |match|#:| |#forall| |'res|#:| |(|(||)| |#=>| |Int|,| |ifCons|#:| |Int|)| |#=>| |Int| +//│ Parsed: {fun match: forall 'res. (() -> Int, ifCons: Int) -> Int} + +fun match: forall 'res: (() => Int, Int) => Int +//│ |#fun| |match|#:| |#forall| |'res|#:| |(|(||)| |#=>| |Int|,| |Int|)| |#=>| |Int| +//│ Parsed: {fun match: forall 'res. (() -> Int, Int) -> Int} + +f(x => x, a) +//│ |f|(|x| |#=>| |x|,| |a|)| +//│ Parsed: {f((x,) => x, a,)} + +f(x => x, y: a) +//│ |f|(|x| |#=>| |x|,| |y|#:| |a|)| +//│ Parsed: {f((x,) => x, y: a,)} + + diff --git a/shared/src/test/diff/parser/Forall.mls b/shared/src/test/diff/parser/Forall.mls index 013303f52a..c6296bf9ab 100644 --- a/shared/src/test/diff/parser/Forall.mls +++ b/shared/src/test/diff/parser/Forall.mls @@ -1,17 +1,17 @@ -forall 'a; 'a => 'a -//│ |#forall| |'a|#;| |'a| |=>| |'a| +forall 'a: 'a => 'a +//│ |#forall| |'a|#:| |'a| |#=>| |'a| //│ Parsed: {forall 'a. ('a,) => 'a} -forall 'a, 'b; ('a, 'b) => ('b, 'a) -//│ |#forall| |'a|,| |'b|#;| |(|'a|,| |'b|)| |=>| |(|'b|,| |'a|)| -//│ Parsed: {forall 'a, 'b. ('a, 'b,) => '(' 'b, 'a, ')'} +forall 'a, 'b: ['a, 'b] => ['b, 'a] +//│ |#forall| |'a|,| |'b|#:| |[|'a|,| |'b|]| |#=>| |[|'b|,| |'a|]| +//│ Parsed: {forall 'a, 'b. (['a, 'b,],) => ['b, 'a,]} -fun f: forall 'a; 'a => 'a -//│ |#fun| |f|#:| |#forall| |'a|#;| |'a| |=>| |'a| +fun f: forall 'a: 'a => 'a +//│ |#fun| |f|#:| |#forall| |'a|#:| |'a| |#=>| |'a| //│ Parsed: {fun f: forall 'a. 'a -> 'a} -fun f: forall 'a, 'b; ('a, 'b) => ('b, 'a) -//│ |#fun| |f|#:| |#forall| |'a|,| |'b|#;| |(|'a|,| |'b|)| |=>| |(|'b|,| |'a|)| -//│ Parsed: {fun f: forall 'a 'b. ('a, 'b,) -> ('b, 'a,)} +fun f: forall 'a, 'b: ['a, 'b] => ['b, 'a] +//│ |#fun| |f|#:| |#forall| |'a|,| |'b|#:| |[|'a|,| |'b|]| |#=>| |[|'b|,| |'a|]| +//│ Parsed: {fun f: forall 'a 'b. (['a, 'b]) -> ['b, 'a]} diff --git a/shared/src/test/diff/parser/Fun.mls b/shared/src/test/diff/parser/Fun.mls index c7cd54dd19..4f1dae5e95 100644 --- a/shared/src/test/diff/parser/Fun.mls +++ b/shared/src/test/diff/parser/Fun.mls @@ -17,13 +17,13 @@ fun f x = x //│ Parsed: {fun f = x} fun f = x => x -//│ |#fun| |f| |#=| |x| |=>| |x| +//│ |#fun| |f| |#=| |x| |#=>| |x| //│ Parsed: {fun f = (x,) => x} // TODO fun x => x -//│ |#fun| |x| |=>| |x| -//│ ╔══[PARSE ERROR] Expected function parameter list; found operator instead +//│ |#fun| |x| |#=>| |x| +//│ ╔══[PARSE ERROR] Expected function parameter list; found '=>' instead //│ ║ l.24: fun x => x //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected ':' or '=' followed by a function body or signature; found identifier instead @@ -32,12 +32,12 @@ fun x => x //│ Parsed: {fun x = undefined} let f = x => x -//│ |#let| |f| |#=| |x| |=>| |x| +//│ |#let| |f| |#=| |x| |#=>| |x| //│ Parsed: {let f = (x,) => x} // TODO let f = fun x => x -//│ |#let| |f| |#=| |#fun| |x| |=>| |x| +//│ |#let| |f| |#=| |#fun| |x| |#=>| |x| //│ ╔══[PARSE ERROR] Unexpected 'fun' keyword in expression position //│ ║ l.39: let f = fun x => x //│ ╙── ^^^ @@ -46,7 +46,7 @@ let f = fun x => x f(x) + x //│ |f|(|x|)| |+| |x| -//│ Parsed: {+ (f (x,),) (x,)} +//│ Parsed: {+(f(x,),)(x,)} fun f(x, y) = x @@ -82,7 +82,7 @@ fun f x y = x //│ ╔══[PARSE ERROR] Expected ':' or '=' followed by a function body or signature; found identifier instead //│ ║ l.77: fun f x y = x //│ ╙── ^ -//│ ╔══[PARSE ERROR] Expected end of input; found '=' keyword instead +//│ ╔══[PARSE ERROR] Expected end of input; found '=' instead //│ ║ l.77: fun f x y = x //│ ╙── ^ //│ Parsed: {fun f = undefined} @@ -90,40 +90,40 @@ fun f x y = x fun f(x, y)(a, b) = x + y * a / b //│ |#fun| |f|(|x|,| |y|)|(|a|,| |b|)| |#=| |x| |+| |y| |*| |a| |/| |b| -//│ Parsed: {fun f = (x, y,) => (a, b,) => / (+ (x,) (* (y,) (a,),),) (b,)} +//│ Parsed: {fun f = (x, y,) => (a, b,) => /(+(x,)(*(y,)(a,),),)(b,)} fun f(x, y) of a, b = x + y * a / b //│ |#fun| |f|(|x|,| |y|)| |#of| |a|,| |b| |#=| |x| |+| |y| |*| |a| |/| |b| -//│ Parsed: {fun f = (x, y,) => (a, b,) => / (+ (x,) (* (y,) (a,),),) (b,)} +//│ Parsed: {fun f = (x, y,) => (a, b,) => /(+(x,)(*(y,)(a,),),)(b,)} fun f of x, y of a, b = x + y * a / b //│ |#fun| |f| |#of| |x|,| |y| |#of| |a|,| |b| |#=| |x| |+| |y| |*| |a| |/| |b| -//│ Parsed: {fun f = (x, y (a, b,),) => / (+ (x,) (* (y,) (a,),),) (b,)} +//│ Parsed: {fun f = (x, y(a, b,),) => /(+(x,)(*(y,)(a,),),)(b,)} fun f(Some(x)) = x //│ |#fun| |f|(|Some|(|x|)|)| |#=| |x| -//│ Parsed: {fun f = (Some (x,),) => x} +//│ Parsed: {fun f = (Some(x,),) => x} fun f(Some of x) = x //│ |#fun| |f|(|Some| |#of| |x|)| |#=| |x| -//│ Parsed: {fun f = (Some (x,),) => x} +//│ Parsed: {fun f = (Some(x,),) => x} fun f of Some of x = x //│ |#fun| |f| |#of| |Some| |#of| |x| |#=| |x| -//│ Parsed: {fun f = (Some (x,),) => x} +//│ Parsed: {fun f = (Some(x,),) => x} fun f(Some(x), Some(y)) = x //│ |#fun| |f|(|Some|(|x|)|,| |Some|(|y|)|)| |#=| |x| -//│ Parsed: {fun f = (Some (x,), Some (y,),) => x} +//│ Parsed: {fun f = (Some(x,), Some(y,),) => x} fun f(Some of x, Some of y) = x + y //│ |#fun| |f|(|Some| |#of| |x|,| |Some| |#of| |y|)| |#=| |x| |+| |y| -//│ Parsed: {fun f = (Some (x, Some (y,),),) => + (x,) (y,)} +//│ Parsed: {fun f = (Some(x, Some(y,),),) => +(x,)(y,)} fun f of Some of x, Some of y = x + y //│ |#fun| |f| |#of| |Some| |#of| |x|,| |Some| |#of| |y| |#=| |x| |+| |y| -//│ Parsed: {fun f = (Some (x, Some (y,),),) => + (x,) (y,)} +//│ Parsed: {fun f = (Some(x, Some(y,),),) => +(x,)(y,)} fun f(x: Int, y: Int) = x @@ -165,7 +165,7 @@ fun fun = 1 //│ |#fun| |#=| |1| -//│ ╔══[PARSE ERROR] Expected a function name; found '=' keyword instead +//│ ╔══[PARSE ERROR] Expected a function name; found '=' instead //│ ║ l.166: fun = 1 //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected function parameter list; found literal instead @@ -179,16 +179,16 @@ fun = 1 fun add(x: number, y: number): number //│ |#fun| |add|(|x|#:| |number|,| |y|#:| |number|)|#:| |number| -//│ Parsed: {fun add: (x: number, y: number,) -> number} +//│ Parsed: {fun add: (x: number, y: number) -> number} fun apply(x: int, f: int => int): int -//│ |#fun| |apply|(|x|#:| |int|,| |f|#:| |int| |=>| |int|)|#:| |int| -//│ Parsed: {fun apply: (x: int, f: int -> int,) -> int} +//│ |#fun| |apply|(|x|#:| |int|,| |f|#:| |int| |#=>| |int|)|#:| |int| +//│ Parsed: {fun apply: (x: int, f: int -> int) -> int} fun apply2(x: number, f: int => number => number): number -//│ |#fun| |apply2|(|x|#:| |number|,| |f|#:| |int| |=>| |number| |=>| |number|)|#:| |number| -//│ Parsed: {fun apply2: (x: number, f: int -> number -> number,) -> number} +//│ |#fun| |apply2|(|x|#:| |number|,| |f|#:| |int| |#=>| |number| |#=>| |number|)|#:| |number| +//│ Parsed: {fun apply2: (x: number, f: int -> number -> number) -> number} fun apply3(x: number, f: (int => number) => number): number -//│ |#fun| |apply3|(|x|#:| |number|,| |f|#:| |(|int| |=>| |number|)| |=>| |number|)|#:| |number| -//│ Parsed: {fun apply3: (x: number, f: (int -> number) -> number,) -> number} +//│ |#fun| |apply3|(|x|#:| |number|,| |f|#:| |(|int| |#=>| |number|)| |#=>| |number|)|#:| |number| +//│ Parsed: {fun apply3: (x: number, f: (int -> number) -> number) -> number} diff --git a/shared/src/test/diff/parser/IfThenElse.mls b/shared/src/test/diff/parser/IfThenElse.mls index 136efc2454..7255013bc7 100644 --- a/shared/src/test/diff/parser/IfThenElse.mls +++ b/shared/src/test/diff/parser/IfThenElse.mls @@ -7,25 +7,25 @@ if true then 1 else 2 if a == 0 then "false" else 2 //│ |#if| |a| |==| |0|↵|#then| |"false"| |#else| |2| -//│ Parsed: {if (== (a,) (0,)) then "false" else 2} +//│ Parsed: {if (==(a,)(0,)) then "false" else 2} if a == 0 then "false" else 2 //│ |#if| |a| |==| |0|↵|#then| |"false"|↵|#else| |2| -//│ Parsed: {if (== (a,) (0,)) then "false" else 2} +//│ Parsed: {if (==(a,)(0,)) then "false" else 2} if a == 0 then "false" else 2 //│ |#if| |a| |==| |0|→|#then| |"false"|↵|#else| |2|←| -//│ Parsed: {if (== (a,) (0,)) then "false" else 2} +//│ Parsed: {if (==(a,)(0,)) then "false" else 2} if a == 0 then "false" else 2 //│ |#if| |a| |==| |0|↵|#then| |"false"|→|#else| |2|←| -//│ Parsed: {if (== (a,) (0,)) then "false" else 2} +//│ Parsed: {if (==(a,)(0,)) then "false" else 2} :pe if a == 0 @@ -35,19 +35,19 @@ else 2 //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause //│ ║ l.33: else 2 //│ ╙── ^^^^^^ -//│ Parsed: {if (== (a,) (0,)) then "false"; undefined} +//│ Parsed: {if (==(a,)(0,)) then "false"; undefined} if a == 0 then "false" print of "ok!" //│ |#if| |a| |==| |0|→|#then| |"false"|←|↵|print| |#of| |"ok!"| -//│ Parsed: {if (== (a,) (0,)) then "false"; print ("ok!",)} +//│ Parsed: {if (==(a,)(0,)) then "false"; print("ok!",)} if a == 0 then "false" else 2 print of "ok!" //│ |#if| |a| |==| |0| |#then| |"false"|→|#else| |2|←|↵|print| |#of| |"ok!"| -//│ Parsed: {if (== (a,) (0,)) then "false" else 2; print ("ok!",)} +//│ Parsed: {if (==(a,)(0,)) then "false" else 2; print("ok!",)} if true then "true" @@ -59,13 +59,13 @@ if a == 1 then "true" b == 2 then "false" //│ |#if|→|a| |==| |1| |#then| |"true"|↵|b| |==| |2| |#then| |"false"|←| -//│ Parsed: {if ‹(== (a,) (1,)) then "true"; (== (b,) (2,)) then "false"›} +//│ Parsed: {if ‹(==(a,)(1,)) then "true"; (==(b,)(2,)) then "false"›} if a == 0 then "false" a == 1 then "true" //│ |#if|→|a| |==| |0| |#then| |"false"|↵|a| |==| |1| |#then| |"true"|←| -//│ Parsed: {if ‹(== (a,) (0,)) then "false"; (== (a,) (1,)) then "true"›} +//│ Parsed: {if ‹(==(a,)(0,)) then "false"; (==(a,)(1,)) then "true"›} if a == 0 then "false" @@ -149,7 +149,7 @@ if a is Some(x) then "defined" None then "undefined" //│ |#if| |a| |is|→|Some|(|x|)| |#then| |"defined"|↵|None| |#then| |"undefined"|←| -//│ Parsed: {if a is ‹(Some (x,)) then "defined"; (None) then "undefined"›} +//│ Parsed: {if a is ‹(Some(x,)) then "defined"; (None) then "undefined"›} if a is Some(x) and x is @@ -157,45 +157,45 @@ if a is Right(b) then "right-defined" None then "undefined" //│ |#if| |a| |is|→|Some|(|x|)| |and| |x| |is|→|Left|(|a|)| |#then| |"left-defined"|↵|Right|(|b|)| |#then| |"right-defined"|←|↵|None| |#then| |"undefined"|←| -//│ Parsed: {if a is ‹Some (x,) and x is ‹(Left (a,)) then "left-defined"; (Right (b,)) then "right-defined"›; (None) then "undefined"›} +//│ Parsed: {if a is ‹Some(x,) and x is ‹(Left(a,)) then "left-defined"; (Right(b,)) then "right-defined"›; (None) then "undefined"›} if a is Some of x then "defined" None then "undefined" //│ |#if| |a| |is|→|Some| |#of| |x| |#then| |"defined"|↵|None| |#then| |"undefined"|←| -//│ Parsed: {if a is ‹(Some (x,)) then "defined"; (None) then "undefined"›} +//│ Parsed: {if a is ‹(Some(x,)) then "defined"; (None) then "undefined"›} if a is Some(x) then "defined" _ then "unknown" //│ |#if| |a| |is|→|Some|(|x|)| |#then| |"defined"|↵|_| |#then| |"unknown"|←| -//│ Parsed: {if a is ‹(Some (x,)) then "defined"; (_) then "unknown"›} +//│ Parsed: {if a is ‹(Some(x,)) then "defined"; (_) then "unknown"›} if a is Some(x) then "defined" else "unknown" //│ |#if| |a| |is|→|Some|(|x|)| |#then| |"defined"|↵|#else| |"unknown"|←| -//│ Parsed: {if a is ‹(Some (x,)) then "defined"; else "unknown"›} +//│ Parsed: {if a is ‹(Some(x,)) then "defined"; else "unknown"›} if a is Some(x) then "defined" else "unknown" else "unreachable?!" //│ |#if| |a| |is|→|Some|(|x|)| |#then| |"defined"|↵|#else| |"unknown"|↵|#else| |"unreachable?!"|←| -//│ Parsed: {if a is ‹(Some (x,)) then "defined"; else "unknown"; else "unreachable?!"›} +//│ Parsed: {if a is ‹(Some(x,)) then "defined"; else "unknown"; else "unreachable?!"›} a == 1 and b == 2 //│ |a| |==| |1| |and| |b| |==| |2| -//│ Parsed: {and (== (a,) (1,),) (== (b,) (2,),)} +//│ Parsed: {and(==(a,)(1,),)(==(b,)(2,),)} :pe if lol //│ |#if| |lol| -//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause; found reference instead +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found reference instead //│ ║ l.193: if lol //│ ║ ^^^ -//│ ╟── Note: 'if' expression started here: +//│ ╟── Note: 'if' expression starts here: //│ ║ l.193: if lol //│ ╙── ^^ //│ Parsed: {if (lol) then undefined} @@ -232,51 +232,42 @@ if lol then else //│ |#if| |lol|↵|#then|↵|#else| -//│ ╔══[PARSE ERROR] Unexpected newline in expression position -//│ ║ l.232: then -//│ ║ ^ -//│ ║ l.233: else -//│ ╙── //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.233: else //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead //│ ║ l.233: else //│ ╙── ^^^^ -//│ Parsed: {if (lol) then undefined} +//│ Parsed: {if (lol) then {undefined}} :pe if lol else 2 //│ |#if| |lol| |#else| |2| -//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause; found reference followed by 'else' keyword instead -//│ ║ l.249: if lol else 2 +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found reference followed by 'else' keyword instead +//│ ║ l.244: if lol else 2 //│ ║ ^^^^^^^^ -//│ ╟── Note: 'if' expression started here: -//│ ║ l.249: if lol else 2 +//│ ╟── Note: 'if' expression starts here: +//│ ║ l.244: if lol else 2 //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected end of input; found 'else' keyword instead -//│ ║ l.249: if lol else 2 +//│ ║ l.244: if lol else 2 //│ ╙── ^^^^ //│ Parsed: {if (lol) then undefined} -:pe x = if true then false else maybe //│ |x| |#=| |#if| |true| |#then| |false| |#else| |maybe| -//│ ╔══[PARSE ERROR] Expected end of input; found '=' keyword instead -//│ ║ l.264: x = if true then false else maybe -//│ ╙── ^ -//│ Parsed: {x} +//│ Parsed: {x = if (true) then false else maybe} :pe x = if true then y = 1 //│ |x| |#=|→|#if| |true| |#then|←|↵|y| |#=| |1| -//│ ╔══[PARSE ERROR] Expected end of input; found '=' keyword instead -//│ ║ l.272: x = -//│ ╙── ^ -//│ Parsed: {x} +//│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here +//│ ║ l.264: if true then +//│ ╙── ^ +//│ Parsed: {x = {if (true) then undefined}; y = 1} if a == 0 and b == @@ -291,23 +282,23 @@ if a == 2 and b then "" //│ |2| |and| |b| |#then| |""| //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause -//│ ║ l.291: 2 and b then "" +//│ ║ l.282: 2 and b then "" //│ ╙── ^^^^^^^^^^^^^^^ //│ Parsed: {undefined} 2 and b == 0 //│ |2| |and| |b| |==| |0| -//│ Parsed: {and (2,) (== (b,) (0,),)} +//│ Parsed: {and(2,)(==(b,)(0,),)} if x == 2 and b then "" //│ |#if| |x| |==|→|2| |and| |b| |#then| |""|←| -//│ Parsed: {if x == ‹(and (2,) (b,)) then ""›} +//│ Parsed: {if x == ‹(and(2,)(b,)) then ""›} if x == 2 and b == 1 then "" //│ |#if| |x| |==|→|2| |and| |b| |==| |1| |#then| |""|←| -//│ Parsed: {if x == ‹(and (2,) (== (b,) (1,),)) then ""›} +//│ Parsed: {if x == ‹(and(2,)(==(b,)(1,),)) then ""›} if x == 0 then "x" @@ -316,13 +307,13 @@ if 2 and z == 0 then "z = 0" 3 then "y = 3" //│ |#if|→|x| |==| |0| |#then| |"x"|↵|y| |==|→|1| |#then| |"y = 1"|↵|2| |and| |z| |==| |0| |#then| |"z = 0"|↵|3| |#then| |"y = 3"|←|←| -//│ Parsed: {if ‹(== (x,) (0,)) then "x"; y == ‹(1) then "y = 1"; (and (2,) (== (z,) (0,),)) then "z = 0"; (3) then "y = 3"››} +//│ Parsed: {if ‹(==(x,)(0,)) then "x"; y == ‹(1) then "y = 1"; (and(2,)(==(z,)(0,),)) then "z = 0"; (3) then "y = 3"››} :pe else 1 //│ |#else| |1| //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause -//│ ║ l.322: else 1 +//│ ║ l.313: else 1 //│ ╙── ^^^^^^ //│ Parsed: {undefined} @@ -330,7 +321,7 @@ else 1 1 else 2 //│ |1| |#else| |2| //│ ╔══[PARSE ERROR] Expected end of input; found 'else' keyword instead -//│ ║ l.330: 1 else 2 +//│ ║ l.321: 1 else 2 //│ ╙── ^^^^ //│ Parsed: {1} @@ -349,7 +340,7 @@ if a is let y = a + 1 Right(0) then y //│ |#if| |a| |is|→|Left|(|x|)| |#then| |x|↵|#let| |y| |#=| |a| |+| |1|↵|Right|(|0|)| |#then| |y|←| -//│ Parsed: {if a is ‹(Left (x,)) then x; let y = + (a,) (1,); (Right (0,)) then y›} +//│ Parsed: {if a is ‹(Left(x,)) then x; let y = +(a,)(1,); (Right(0,)) then y›} if a is Some(v) and v is @@ -359,7 +350,7 @@ if a is Right(x) then x + y else 0 //│ |#if| |a| |is|→|Some|(|v|)| |and| |v| |is|→|Left|(|x|)| |#then| |x|↵|#let| |y| |#=| |v| |+| |1|↵|Right|(|0|)| |#then| |y|↵|Right|(|x|)| |#then| |x| |+| |y|←|↵|#else| |0|←| -//│ Parsed: {if a is ‹Some (v,) and v is ‹(Left (x,)) then x; let y = + (v,) (1,); (Right (0,)) then y; (Right (x,)) then + (x,) (y,)›; else 0›} +//│ Parsed: {if a is ‹Some(v,) and v is ‹(Left(x,)) then x; let y = +(v,)(1,); (Right(0,)) then y; (Right(x,)) then +(x,)(y,)›; else 0›} if a is Some(x) and x is @@ -368,16 +359,16 @@ if a is Right(b) then "right-defined" None then "undefined" //│ |#if| |a| |is|→|Some|(|x|)| |and| |x| |is|→|Left|(|a|)| |#then| |"left-defined"|↵|#let| |y| |#=| |x| |+| |1|↵|Right|(|b|)| |#then| |"right-defined"|←|↵|None| |#then| |"undefined"|←| -//│ Parsed: {if a is ‹Some (x,) and x is ‹(Left (a,)) then "left-defined"; let y = + (x,) (1,); (Right (b,)) then "right-defined"›; (None) then "undefined"›} +//│ Parsed: {if a is ‹Some(x,) and x is ‹(Left(a,)) then "left-defined"; let y = +(x,)(1,); (Right(b,)) then "right-defined"›; (None) then "undefined"›} :w if a is Left x then x //│ |#if| |a| |is|→|Left| |x| |#then| |x|←| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.375: Left x then x +//│ ║ l.366: Left x then x //│ ╙── ^^^^^^ -//│ Parsed: {if a is ‹(Left (x,)) then x›} +//│ Parsed: {if a is ‹(Left(x,)) then x›} // TODO if a is @@ -386,11 +377,11 @@ if a is then y //│ |#if| |a| |is|→|Left|(|x|)| |#then| |x|↵|#let| |y| |#=| |a| |+| |1|↵|#then| |y|←| //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead -//│ ║ l.385: let y = a + 1 +//│ ║ l.376: let y = a + 1 //│ ║ ^^^^^ -//│ ║ l.386: then y +//│ ║ l.377: then y //│ ╙── ^^^^^^^^ -//│ Parsed: {if a is ‹(Left (x,)) then x; let y = undefined›} +//│ Parsed: {if a is ‹(Left(x,)) then x; let y = undefined›} @@ -400,20 +391,20 @@ if a is :pe if let Some(x) = v then 123 //│ |#if| |#let| |Some|(|x|)| |#=| |v| |#then| |123| -//│ ╔══[PARSE ERROR] Expected '=' keyword; found parenthesis section instead -//│ ║ l.401: if let Some(x) = v then 123 +//│ ╔══[PARSE ERROR] Expected '='; found parenthesis section instead +//│ ║ l.392: if let Some(x) = v then 123 //│ ╙── ^^^ -//│ ╔══[PARSE ERROR] Unexpected '=' keyword in expression position -//│ ║ l.401: if let Some(x) = v then 123 +//│ ╔══[PARSE ERROR] Unexpected '=' in expression position +//│ ║ l.392: if let Some(x) = v then 123 //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead -//│ ║ l.401: if let Some(x) = v then 123 +//│ ║ l.392: if let Some(x) = v then 123 //│ ╙── ^^^^^^^^^^ -//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause; found let binding instead -//│ ║ l.401: if let Some(x) = v then 123 +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found let binding instead +//│ ║ l.392: if let Some(x) = v then 123 //│ ║ ^ -//│ ╟── Note: 'if' expression started here: -//│ ║ l.401: if let Some(x) = v then 123 +//│ ╟── Note: 'if' expression starts here: +//│ ║ l.392: if let Some(x) = v then 123 //│ ╙── ^^ //│ Parsed: {if (let Some = undefined in undefined) then undefined} @@ -421,27 +412,27 @@ if let Some(x) = v then 123 :pe if let Some(x) = v and cond then 123 //│ |#if| |#let| |Some|(|x|)| |#=| |v| |and| |cond| |#then| |123| -//│ ╔══[PARSE ERROR] Expected '=' keyword; found parenthesis section instead -//│ ║ l.422: if let Some(x) = v and cond then 123 +//│ ╔══[PARSE ERROR] Expected '='; found parenthesis section instead +//│ ║ l.413: if let Some(x) = v and cond then 123 //│ ╙── ^^^ -//│ ╔══[PARSE ERROR] Unexpected '=' keyword in expression position -//│ ║ l.422: if let Some(x) = v and cond then 123 +//│ ╔══[PARSE ERROR] Unexpected '=' in expression position +//│ ║ l.413: if let Some(x) = v and cond then 123 //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead -//│ ║ l.422: if let Some(x) = v and cond then 123 +//│ ║ l.413: if let Some(x) = v and cond then 123 //│ ╙── ^^^^^^^^^^^^^^^^^^^ -//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause; found let binding instead -//│ ║ l.422: if let Some(x) = v and cond then 123 +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found let binding instead +//│ ║ l.413: if let Some(x) = v and cond then 123 //│ ║ ^ -//│ ╟── Note: 'if' expression started here: -//│ ║ l.422: if let Some(x) = v and cond then 123 +//│ ╟── Note: 'if' expression starts here: +//│ ║ l.413: if let Some(x) = v and cond then 123 //│ ╙── ^^ //│ Parsed: {if (let Some = undefined in undefined) then undefined} // MLscript: if v is Some(x) and x is Left(y) then 123 //│ |#if| |v| |is| |Some|(|x|)| |and| |x| |is| |Left|(|y|)| |#then| |123| -//│ Parsed: {if (and (is (v,) (Some (x,),),) (is (x,) (Left (y,),),)) then 123} +//│ Parsed: {if (and(is(v,)(Some(x,),),)(is(x,)(Left(y,),),)) then 123} // ML: let Some(x) = v @@ -450,7 +441,7 @@ let Some(x) = v v as Some(x) //│ |v| |as| |Some|(|x|)| -//│ Parsed: {as (v,) (Some (x,),)} +//│ Parsed: {as(v,)(Some(x,),)} @@ -461,7 +452,7 @@ if true + 1 //│ |#if| |true|→|#then| |0|↵|+| |1|←| //│ ╔══[PARSE ERROR] Unexpected operator here -//│ ║ l.461: + 1 +//│ ║ l.452: + 1 //│ ╙── ^ //│ Parsed: {if (true) then 0} @@ -469,7 +460,7 @@ if true then 0 + 1 //│ |#if| |true|→|#then| |0|→|+| |1|←|←| -//│ Parsed: {if (true) then + 0 1} +//│ Parsed: {if (true) then +(0, 1,)} if true then 0 @@ -482,7 +473,7 @@ if true else 0 + 1 //│ |#if| |true|→|#then| |0|↵|#else| |0|→|+| |1|←|←| -//│ Parsed: {if (true) then 0 else + 0 1} +//│ Parsed: {if (true) then 0 else +(0, 1,)} :pe if true @@ -490,7 +481,7 @@ if true + 1 //│ |#if| |true|→|#then| |0|←|→|+| |1|←| //│ ╔══[PARSE ERROR] Expected end of input; found indented block instead -//│ ║ l.490: + 1 +//│ ║ l.481: + 1 //│ ╙── ^^ //│ Parsed: {if (true) then 0} @@ -501,19 +492,19 @@ if true + 1 //│ |#if| |true|→|#then| |0|↵|#else| |0|←|→|+| |1|←| //│ ╔══[PARSE ERROR] Expected end of input; found indented block instead -//│ ║ l.501: + 1 +//│ ║ l.492: + 1 //│ ╙── ^^ //│ Parsed: {if (true) then 0 else 0} if true then 0 + 1 //│ |#if| |true| |#then| |0|→|+| |1|←| -//│ Parsed: {if (true) then + 0 1} +//│ Parsed: {if (true) then +(0, 1,)} if true then 0 else 0 + 1 //│ |#if| |true| |#then| |0| |#else| |0|→|+| |1|←| -//│ Parsed: {if (true) then 0 else + 0 1} +//│ Parsed: {if (true) then 0 else +(0, 1,)} // TODO deal with meaningless whitespace: @@ -535,29 +526,41 @@ if true :pe (if true) //│ |(|#if| |true|)| -//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause; found reference instead -//│ ║ l.536: (if true) +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found reference instead +//│ ║ l.527: (if true) //│ ║ ^^^^ -//│ ╟── Note: 'if' expression started here: -//│ ║ l.536: (if true) +//│ ╟── Note: 'if' expression starts here: +//│ ║ l.527: (if true) //│ ╙── ^^ -//│ Parsed: {'(' if (true) then undefined, ')'} +//│ Parsed: {'(' if (true) then undefined ')'} :pe (if true then) //│ |(|#if| |true| |#then|)| //│ ╔══[PARSE ERROR] Unexpected end of parenthesis section; an expression was expected here -//│ ║ l.547: (if true then) +//│ ║ l.538: (if true then) //│ ╙── ^ -//│ Parsed: {'(' if (true) then undefined, ')'} +//│ Parsed: {'(' if (true) then undefined ')'} -:pe if true then; -//│ |#if| |true| |#then|#;| -//│ ╔══[PARSE ERROR] Expected end of input; found ';' keyword instead -//│ ║ l.555: if true then; -//│ ╙── ^ +//│ |#if| |true| |#then|;| //│ Parsed: {if (true) then undefined} +if true then; +//│ |#if| |true| |#then|;| +//│ Parsed: {if (true) then undefined} + +:pe +if true then; else; +//│ |#if| |true| |#then|;| |#else|;| +//│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause +//│ ║ l.554: if true then; else; +//│ ╙── ^^^^^ +//│ Parsed: {if (true) then undefined; undefined} + +if true then () else; +//│ |#if| |true| |#then| |(||)| |#else|;| +//│ Parsed: {if (true) then undefined else undefined} + diff --git a/shared/src/test/diff/parser/Lambdas.mls b/shared/src/test/diff/parser/Lambdas.mls index b868476532..74771e5cbf 100644 --- a/shared/src/test/diff/parser/Lambdas.mls +++ b/shared/src/test/diff/parser/Lambdas.mls @@ -2,17 +2,17 @@ x => x -//│ |x| |=>| |x| +//│ |x| |#=>| |x| //│ Parsed: {(x,) => x} (x) => x -//│ |(|x|)| |=>| |x| -//│ Parsed: {(x,) => x} +//│ |(|x|)| |#=>| |x| +//│ Parsed: {('(' x ')',) => x} // TODO fun x => x -//│ |#fun| |x| |=>| |x| -//│ ╔══[PARSE ERROR] Expected function parameter list; found operator instead +//│ |#fun| |x| |#=>| |x| +//│ ╔══[PARSE ERROR] Expected function parameter list; found '=>' instead //│ ║ l.13: fun x => x //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected ':' or '=' followed by a function body or signature; found identifier instead @@ -22,7 +22,7 @@ fun x => x // TODO let f = fun x => x -//│ |#let| |f| |#=| |#fun| |x| |=>| |x| +//│ |#let| |f| |#=| |#fun| |x| |#=>| |x| //│ ╔══[PARSE ERROR] Unexpected 'fun' keyword in expression position //│ ║ l.24: let f = fun x => x //│ ╙── ^^^ @@ -38,72 +38,80 @@ fun f x = x (x, y) => x -//│ |(|x|,| |y|)| |=>| |x| +//│ |(|x|,| |y|)| |#=>| |x| //│ Parsed: {(x, y,) => x} => 1 -//│ |=>| |1| -//│ ╔══[PARSE ERROR] Unexpected operator in expression position +//│ |#=>| |1| +//│ ╔══[PARSE ERROR] Unexpected '=>' in expression position //│ ║ l.45: => 1 //│ ╙── ^^ //│ Parsed: {1} x => -//│ |x| |=>| +//│ |x| |#=>| //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.52: x => //│ ╙── ^ //│ Parsed: {(x,) => undefined} (x =>) -//│ |(|x| |=>|)| +//│ |(|x| |#=>|)| //│ ╔══[PARSE ERROR] Unexpected end of parenthesis section; an expression was expected here //│ ║ l.59: (x =>) //│ ╙── ^ -//│ Parsed: {'(' (x,) => undefined, ')'} +//│ Parsed: {'(' (x,) => undefined ')'} a --> b --> c //│ |a| |-->| |b| |-->| |c| -//│ Parsed: {--> (a,) (--> (b,) (c,),)} +//│ Parsed: {-->(a,)(-->(b,)(c,),)} a => b => c -//│ |a| |=>| |b| |=>| |c| +//│ |a| |#=>| |b| |#=>| |c| //│ Parsed: {(a,) => (b,) => c} (a => b) => c -//│ |(|a| |=>| |b|)| |=>| |c| -//│ Parsed: {((a,) => b,) => c} +//│ |(|a| |#=>| |b|)| |#=>| |c| +//│ Parsed: {('(' (a,) => b ')',) => c} a => (b => c) -//│ |a| |=>| |(|b| |=>| |c|)| -//│ Parsed: {(a,) => '(' (b,) => c, ')'} +//│ |a| |#=>| |(|b| |#=>| |c|)| +//│ Parsed: {(a,) => '(' (b,) => c ')'} xs.forall(x => x > 0) -//│ |xs|.forall|(|x| |=>| |x| |>| |0|)| -//│ Parsed: {(xs).forall ((x,) => > (x,) (0,),)} +//│ |xs|.forall|(|x| |#=>| |x| |>| |0|)| +//│ Parsed: {(xs).forall((x,) => >(x,)(0,),)} xs.forall of x => x > 0 -//│ |xs|.forall| |#of| |x| |=>| |x| |>| |0| -//│ Parsed: {(xs).forall ((x,) => > (x,) (0,),)} +//│ |xs|.forall| |#of| |x| |#=>| |x| |>| |0| +//│ Parsed: {(xs).forall((x,) => >(x,)(0,),)} :pe a => b then c -//│ |a| |=>| |b| |#then| |c| +//│ |a| |#=>| |b| |#then| |c| //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause //│ ║ l.91: a => b then c //│ ╙── ^^^^^^^^^^^^^ //│ Parsed: {undefined} if a => b then c -//│ |#if| |a| |=>| |b| |#then| |c| +//│ |#if| |a| |#=>| |b| |#then| |c| //│ Parsed: {if ((a,) => b) then c} if xs.forall(a => b) then c -//│ |#if| |xs|.forall|(|a| |=>| |b|)| |#then| |c| -//│ Parsed: {if ((xs).forall ((a,) => b,)) then c} +//│ |#if| |xs|.forall|(|a| |#=>| |b|)| |#then| |c| +//│ Parsed: {if ((xs).forall((a,) => b,)) then c} if xs.forall of a => b then c -//│ |#if| |xs|.forall| |#of| |a| |=>| |b| |#then| |c| -//│ Parsed: {if ((xs).forall ((a,) => b,)) then c} +//│ |#if| |xs|.forall| |#of| |a| |#=>| |b| |#then| |c| +//│ Parsed: {if ((xs).forall((a,) => b,)) then c} + + +id + of x => x + 1 +//│ |id| |+| |#of| |x| |#=>| |x| |+| |1| +//│ ╔══[PARSE ERROR] Unexpected 'of' keyword in expression position +//│ ║ l.111: id + of x => x + 1 +//│ ╙── ^^ +//│ Parsed: {+(id,)((x,) => +(x,)(1,),)} diff --git a/shared/src/test/diff/parser/Lets.mls b/shared/src/test/diff/parser/Lets.mls index 988c94ed2c..599d93f1cf 100644 --- a/shared/src/test/diff/parser/Lets.mls +++ b/shared/src/test/diff/parser/Lets.mls @@ -7,59 +7,53 @@ let x = 1 let x = 1, y = 2 //│ |#let| |x| |#=| |1|,| |y| |#=| |2| -//│ ╔══[PARSE ERROR] Expected end of input; found comma instead +//│ ╔══[PARSE ERROR] Expected end of input; found '=' instead //│ ║ l.8: let x = 1, y = 2 -//│ ╙── ^ -//│ Parsed: {let x = 1} +//│ ╙── ^ +//│ Parsed: {let x = ,(1, y,)} let x = 1, y = 2 x + y //│ |#let| |x| |#=| |1|,| |y| |#=| |2|↵|x| |+| |y| -//│ ╔══[PARSE ERROR] Expected end of input; found comma instead +//│ ╔══[PARSE ERROR] Expected end of input; found '=' instead //│ ║ l.15: let x = 1, y = 2 -//│ ╙── ^ -//│ Parsed: {let x = 1} +//│ ╙── ^ +//│ Parsed: {let x = ,(1, y,)} let x = 1 in x + 1 //│ |#let| |x| |#=| |1| |#in| |x| |+| |1| -//│ ╔══[PARSE ERROR] Expected end of input; found 'in' keyword instead -//│ ║ l.23: let x = 1 in x + 1 -//│ ╙── ^^ -//│ Parsed: {let x = 1} +//│ Parsed: {let x = 1 in +(x,)(1,)} let x = 1, y = 2 in x + y //│ |#let| |x| |#=| |1|,| |y| |#=| |2| |#in| |x| |+| |y| -//│ ╔══[PARSE ERROR] Expected end of input; found comma instead -//│ ║ l.30: let x = 1, y = 2 in x + y -//│ ╙── ^ -//│ Parsed: {let x = 1} +//│ ╔══[PARSE ERROR] Expected end of input; found '=' instead +//│ ║ l.27: let x = 1, y = 2 in x + y +//│ ╙── ^ +//│ Parsed: {let x = ,(1, y,)} let in 123 //│ |#let| |#in| |123| //│ ╔══[PARSE ERROR] Expected a function name; found 'in' keyword instead -//│ ║ l.37: let in 123 +//│ ║ l.34: let in 123 //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected function parameter list; found literal instead -//│ ║ l.37: let in 123 +//│ ║ l.34: let in 123 //│ ╙── ^^^ //│ ╔══[PARSE ERROR] Expected ':' or '=' followed by a function body or signature; found end of input instead -//│ ║ l.37: let in 123 +//│ ║ l.34: let in 123 //│ ╙── ^ //│ Parsed: {let = undefined} let x = 1; x + 1 -//│ |#let| |x| |#=| |1|#;| |x| |+| |1| -//│ ╔══[PARSE ERROR] Expected end of input; found ';' keyword instead -//│ ║ l.50: let x = 1; x + 1 -//│ ╙── ^ -//│ Parsed: {let x = 1} +//│ |#let| |x| |#=| |1|;| |x| |+| |1| +//│ Parsed: {let x = 1; +(x,)(1,)} let x = 1, y = 2; x + y -//│ |#let| |x| |#=| |1|,| |y| |#=| |2|#;| |x| |+| |y| -//│ ╔══[PARSE ERROR] Expected end of input; found comma instead -//│ ║ l.57: let x = 1, y = 2; x + y -//│ ╙── ^ -//│ Parsed: {let x = 1} +//│ |#let| |x| |#=| |1|,| |y| |#=| |2|;| |x| |+| |y| +//│ ╔══[PARSE ERROR] Expected end of input; found '=' instead +//│ ║ l.51: let x = 1, y = 2; x + y +//│ ╙── ^ +//│ Parsed: {let x = ,(1, y,)} @@ -67,16 +61,16 @@ let x = 1, y = 2; x + y let in person be the default in following meetings //│ |#let| |#in| |person| |be| |the| |default| |#in| |following| |meetings| //│ ╔══[PARSE ERROR] Expected a function name; found 'in' keyword instead -//│ ║ l.67: let in person be the default in following meetings +//│ ║ l.61: let in person be the default in following meetings //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected function parameter list; found identifier instead -//│ ║ l.67: let in person be the default in following meetings +//│ ║ l.61: let in person be the default in following meetings //│ ╙── ^^^^^^ //│ ╔══[PARSE ERROR] Expected ':' or '=' followed by a function body or signature; found identifier instead -//│ ║ l.67: let in person be the default in following meetings +//│ ║ l.61: let in person be the default in following meetings //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected end of input; found identifier instead -//│ ║ l.67: let in person be the default in following meetings +//│ ║ l.61: let in person be the default in following meetings //│ ╙── ^^^ //│ Parsed: {let = undefined} diff --git a/shared/src/test/diff/parser/Misc.mls b/shared/src/test/diff/parser/Misc.mls index eccf0ec21a..b60f4e2d48 100644 --- a/shared/src/test/diff/parser/Misc.mls +++ b/shared/src/test/diff/parser/Misc.mls @@ -3,7 +3,7 @@ fun discard(x) = () //│ |#fun| |discard|(|x|)| |#=| |(||)| -//│ Parsed: {fun discard = (x,) => '(' ')'} +//│ Parsed: {fun discard = (x,) => undefined} // FIXME parses wrong: foo of @@ -21,7 +21,7 @@ foo of //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.12: None then 0 //│ ╙── ^^^^^^^^^^^^^^^^^^ -//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause; found application followed by newline instead +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application followed by newline instead //│ ║ l.10: discard of if f of x is //│ ║ ^^^^^^^^^ //│ ║ l.11: Some(v) then v + 1 @@ -32,28 +32,28 @@ foo of //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.14: Some(v) then v + 1 //│ ║ ^^^^ -//│ ╟── Note: 'if' expression started here: +//│ ╟── Note: 'if' expression starts here: //│ ║ l.10: discard of if f of x is //│ ╙── ^^ -//│ ╔══[PARSE ERROR] Unexpected comma here +//│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here //│ ║ l.15: None then 0, -//│ ╙── ^ +//│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause //│ ║ l.13: if g of y is //│ ║ ^^^^ //│ ║ l.14: Some(v) then v + 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.15: None then 0, -//│ ╙── ^^^^^^^^^^^^^^^^^^ -//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause; found application instead +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application instead //│ ║ l.13: if g of y is //│ ║ ^^^^^^^^^ //│ ║ l.14: Some(v) then v + 1 //│ ║ ^^^^ -//│ ╟── Note: 'if' expression started here: +//│ ╟── Note: 'if' expression starts here: //│ ║ l.13: if g of y is //│ ╙── ^^ -//│ Parsed: {foo ({discard (if (f (undefined,)) then undefined,); if (g (undefined,)) then undefined},)} +//│ Parsed: {foo({discard(if (f(undefined,)) then undefined,); if (g(undefined,)) then undefined},)} foo of if f of x is @@ -67,17 +67,17 @@ foo of //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.60: Some v then v + 1 //│ ╙── ^^^^^^ -//│ ╔══[PARSE ERROR] Unexpected comma here +//│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here //│ ║ l.61: None then 0, -//│ ╙── ^ +//│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause //│ ║ l.59: if f of x is //│ ║ ^^^^ //│ ║ l.60: Some v then v + 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.61: None then 0, -//│ ╙── ^^^^^^^^^^^^^^^^^ -//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause; found application followed by newline instead +//│ ╙── ^^^^^^^^^^^^^^^^^^ +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application followed by newline instead //│ ║ l.59: if f of x is //│ ║ ^^^^^^^^^ //│ ║ l.60: Some v then v + 1 @@ -92,23 +92,23 @@ foo of //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.65: //│ ║ ^^ -//│ ╟── Note: 'if' expression started here: +//│ ╟── Note: 'if' expression starts here: //│ ║ l.59: if f of x is //│ ╙── ^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.63: Some v then v + 1 //│ ╙── ^^^^^^ -//│ ╔══[PARSE ERROR] Unexpected comma here +//│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here //│ ║ l.64: None then 0, -//│ ╙── ^ +//│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause //│ ║ l.62: if g of y is //│ ║ ^^^^ //│ ║ l.63: Some v then v + 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.64: None then 0, -//│ ╙── ^^^^^^^^^^^^^^^^^ -//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause; found application followed by newline instead +//│ ╙── ^^^^^^^^^^^^^^^^^^ +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application followed by newline instead //│ ║ l.62: if g of y is //│ ║ ^^^^^^^^^ //│ ║ l.63: Some v then v + 1 @@ -117,24 +117,24 @@ foo of //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.65: //│ ║ ^^ -//│ ╟── Note: 'if' expression started here: +//│ ╟── Note: 'if' expression starts here: //│ ║ l.62: if g of y is //│ ╙── ^^ -//│ Parsed: {foo ({if (f (undefined,)) then undefined; if (g (undefined,)) then undefined},)} +//│ Parsed: {foo({if (f(undefined,)) then undefined; if (g(undefined,)) then undefined},)} print of Foo(x) is Some //│ |print| |#of| |Foo|(|x|)| |is| |Some| -//│ Parsed: {print (is (Foo (x,),) (Some,),)} +//│ Parsed: {print(is(Foo(x,),)(Some,),)} // FIXME parses wrong: if f of x is Some then 1 else 0 //│ |#if| |f| |#of| |x| |is| |Some| |#then| |1| |#else| |0| -//│ Parsed: {if (f (is (x,) (Some,),)) then 1 else 0} +//│ Parsed: {if (f(is(x,)(Some,),)) then 1 else 0} if f of 0 and g of 1 then "ok" //│ |#if| |f| |#of| |0| |and| |g| |#of| |1| |#then| |"ok"| -//│ Parsed: {if (f (and (0,) (g (1,),),)) then "ok"} +//│ Parsed: {if (f(and(0,)(g(1,),),)) then "ok"} A and B or C and D //│ |A| |and| |B| |or| |C| |and| |D| -//│ Parsed: {or (and (A,) (B,),) (and (C,) (D,),)} +//│ Parsed: {or(and(A,)(B,),)(and(C,)(D,),)} diff --git a/shared/src/test/diff/parser/MultiLineCalls.mls b/shared/src/test/diff/parser/MultiLineCalls.mls index feb1bf2b9f..3a0b774424 100644 --- a/shared/src/test/diff/parser/MultiLineCalls.mls +++ b/shared/src/test/diff/parser/MultiLineCalls.mls @@ -5,34 +5,34 @@ f ( 0 ) //│ |f| |(|→|0|←|↵|)| -//│ Parsed: {f (0,)} +//│ Parsed: {f(0,)} f ( 0, 1 ) //│ |f| |(|→|0|,|↵|1|←|↵|)| -//│ Parsed: {f (0, 1,)} +//│ Parsed: {f(0, 1,)} f ( 0, 1, ) //│ |f| |(|→|0|,|↵|1|,|←|↵|)| -//│ Parsed: {f (0, 1,)} +//│ Parsed: {f(0, 1,)} f (0, 1) //│ |f| |(|0|,| |1|)| -//│ Parsed: {f (0, 1,)} +//│ Parsed: {f(0, 1,)} f (0, 1,) //│ |f| |(|0|,| |1|,|)| -//│ Parsed: {f (0, 1,)} +//│ Parsed: {f(0, 1,)} f (0, 1, ) //│ |f| |(|0|,| |1|,|↵|)| -//│ Parsed: {f (0, 1,)} +//│ Parsed: {f(0, 1,)} f(,) //│ |f|(|,|)| @@ -42,26 +42,26 @@ f(,) //│ ╔══[PARSE ERROR] Unexpected end of parenthesis section; an expression was expected here //│ ║ l.37: f(,) //│ ╙── ^ -//│ Parsed: {f (undefined,)} +//│ Parsed: {f(undefined,)} f ( 0 1 ) //│ |f| |(|→|0|↵|1|←|↵|)| -//│ Parsed: {f ({0; 1},)} +//│ Parsed: {f({0; 1},)} f of 0, 1 //│ |f| |#of|→|0|,|↵|1|←| -//│ Parsed: {f (0, 1,)} +//│ Parsed: {f(0, 1,)} f of 0 1 //│ |f| |#of|→|0|↵|1|←| -//│ Parsed: {f ({0; 1},)} +//│ Parsed: {f({0; 1},)} // :pe f @@ -75,36 +75,36 @@ of 0 f (0) //│ |f|→|(|0|)|←| -//│ Parsed: {f (0,)} +//│ Parsed: {f(0,)} // TODO f of 0 of 1 //│ |f|→|#of| |0|↵|#of| |1|←| -//│ Parsed: {f (0,) (1,)} +//│ Parsed: {f(0,)(1,)} f (0) (1) //│ |f|→|(|0|)|↵|(|1|)|←| -//│ Parsed: {f (0,) (1,)} +//│ Parsed: {f(0,)(1,)} f of 0, 1 //│ |f|→|#of|→|0|,|↵|1|←|←| -//│ Parsed: {f (0, 1,)} +//│ Parsed: {f(0, 1,)} 2 + 2 (0) //│ |2| |+| |2|→|(|0|)|←| -//│ Parsed: {+ (2,) (2,) (0,)} +//│ Parsed: {+(2,)(2,)(0,)} 2 + 2 of 0 //│ |2| |+| |2|→|#of| |0|←| -//│ Parsed: {+ (2,) (2,) (0,)} +//│ Parsed: {+(2,)(2,)(0,)} diff --git a/shared/src/test/diff/parser/MultilineFun.mls b/shared/src/test/diff/parser/MultilineFun.mls index c08618d758..bf9cf21abf 100644 --- a/shared/src/test/diff/parser/MultilineFun.mls +++ b/shared/src/test/diff/parser/MultilineFun.mls @@ -28,18 +28,18 @@ x) = x fun f(x, y) = x + y //│ |#fun| |f|(|x|,|→|y|←|)| |#=| |x| |+| |y| -//│ Parsed: {fun f = (x, {y},) => + (x,) (y,)} +//│ Parsed: {fun f = (x, {y},) => +(x,)(y,)} fun f( x, y) = x + y //│ |#fun| |f|(|→|x|,|↵|y|←|)| |#=| |x| |+| |y| -//│ Parsed: {fun f = (x, y,) => + (x,) (y,)} +//│ Parsed: {fun f = (x, y,) => +(x,)(y,)} fun f( x, y ) = x + y //│ |#fun| |f|(|→|x|,|↵|y|←|↵|)| |#=| |x| |+| |y| -//│ Parsed: {fun f = (x, y,) => + (x,) (y,)} +//│ Parsed: {fun f = (x, y,) => +(x,)(y,)} diff --git a/shared/src/test/diff/parser/NamedArrays.mls b/shared/src/test/diff/parser/NamedArrays.mls index c9c224e3f0..7b8ff3a159 100644 --- a/shared/src/test/diff/parser/NamedArrays.mls +++ b/shared/src/test/diff/parser/NamedArrays.mls @@ -3,33 +3,33 @@ [] //│ |[||]| -//│ Parsed: {'(' ')'} +//│ Parsed: {[]} [x: 1] //│ |[|x|#:| |1|]| -//│ Parsed: {'(' x: 1, ')'} +//│ Parsed: {[x: 1,]} [x : 1] //│ |[|x| |#:| |1|]| -//│ Parsed: {'(' x : 1, ')'} +//│ Parsed: {[x : 1,]} [x: 1,] //│ |[|x|#:| |1|,|]| -//│ Parsed: {'(' x: 1, ')'} +//│ Parsed: {[x: 1,]} [x: 1, y:] //│ |[|x|#:| |1|,| |y|#:|]| //│ ╔══[PARSE ERROR] Unexpected end of square bracket section; an expression was expected here //│ ║ l.20: [x: 1, y:] //│ ╙── ^ -//│ Parsed: {'(' x: 1, y: undefined, ')'} +//│ Parsed: {[x: 1, y: undefined,]} [x:, y: 1] //│ |[|x|#:|,| |y|#:| |1|]| //│ ╔══[PARSE ERROR] Unexpected comma in expression position //│ ║ l.27: [x:, y: 1] //│ ╙── ^ -//│ Parsed: {'(' x: y : 1, ')'} +//│ Parsed: {[x: y : 1,]} [x:, y:] //│ |[|x|#:|,| |y|#:|]| @@ -39,50 +39,74 @@ //│ ╔══[PARSE ERROR] Unexpected end of square bracket section; an expression was expected here //│ ║ l.34: [x:, y:] //│ ╙── ^ -//│ Parsed: {'(' x: y : undefined, ')'} +//│ Parsed: {[x: y : (),]} [x: 1, 2, 3] //│ |[|x|#:| |1|,| |2|,| |3|]| -//│ Parsed: {'(' x: 1, 2, 3, ')'} +//│ Parsed: {[x: 1, 2, 3,]} [1, y: 2, 3] //│ |[|1|,| |y|#:| |2|,| |3|]| -//│ Parsed: {'(' 1, y: 2, 3, ')'} +//│ Parsed: {[1, y: 2, 3,]} [x: 1, y: 2, z: 3] //│ |[|x|#:| |1|,| |y|#:| |2|,| |z|#:| |3|]| -//│ Parsed: {'(' x: 1, y: 2, z: 3, ')'} +//│ Parsed: {[x: 1, y: 2, z: 3,]} () //│ |(||)| -//│ Parsed: {'(' ')'} +//│ Parsed: {undefined} (x: 1) //│ |(|x|#:| |1|)| -//│ Parsed: {'(' x: 1, ')'} +//│ ╔══[PARSE ERROR] Illegal position for field specification +//│ ║ l.60: (x: 1) +//│ ╙── ^^^^ +//│ Parsed: {1} (x:) //│ |(|x|#:|)| //│ ╔══[PARSE ERROR] Unexpected end of parenthesis section; an expression was expected here -//│ ║ l.64: (x:) +//│ ║ l.67: (x:) //│ ╙── ^ -//│ Parsed: {'(' x: undefined, ')'} +//│ ╔══[PARSE ERROR] Illegal position for field specification +//│ ║ l.67: (x:) +//│ ╙── ^^ +//│ Parsed: {undefined} (x: 1,) //│ |(|x|#:| |1|,|)| -//│ Parsed: {'(' x: 1, ')'} +//│ ╔══[PARSE ERROR] Illegal position for field specification +//│ ║ l.77: (x: 1,) +//│ ╙── ^^^^ +//│ Parsed: {1} (x: 1, 2, 3) //│ |(|x|#:| |1|,| |2|,| |3|)| -//│ Parsed: {'(' x: 1, 2, 3, ')'} +//│ ╔══[PARSE ERROR] Illegal position for field specification +//│ ║ l.84: (x: 1, 2, 3) +//│ ╙── ^^^^ +//│ Parsed: {,(1, ,(2, 3,),)} (1, y: 2, 3) //│ |(|1|,| |y|#:| |2|,| |3|)| -//│ Parsed: {'(' 1, y: 2, 3, ')'} +//│ ╔══[PARSE ERROR] Illegal position for field specification +//│ ║ l.91: (1, y: 2, 3) +//│ ╙── ^^^^ +//│ Parsed: {,(1, ,(2, 3,),)} (x: 1, y: 2, z: 3) //│ |(|x|#:| |1|,| |y|#:| |2|,| |z|#:| |3|)| -//│ Parsed: {'(' x: 1, y: 2, z: 3, ')'} +//│ ╔══[PARSE ERROR] Illegal position for field specification +//│ ║ l.98: (x: 1, y: 2, z: 3) +//│ ╙── ^^^^ +//│ ╔══[PARSE ERROR] Illegal position for field specification +//│ ║ l.98: (x: 1, y: 2, z: 3) +//│ ╙── ^^^^ +//│ ╔══[PARSE ERROR] Illegal position for field specification +//│ ║ l.98: (x: 1, y: 2, z: 3) +//│ ╙── ^^^^ +//│ Parsed: {,(1, ,(2, 3,),)} 1 @@ -95,48 +119,45 @@ x: 1 1, //│ |1|,| -//│ ╔══[PARSE ERROR] Expected end of input; found comma instead -//│ ║ l.96: 1, -//│ ╙── ^ -//│ Parsed: {1} +//│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here +//│ ║ l.120: 1, +//│ ╙── ^ +//│ Parsed: {,(1, undefined,)} x: 1, //│ |x|#:| |1|,| -//│ ╔══[PARSE ERROR] Expected end of input; found comma instead -//│ ║ l.103: x: 1, -//│ ╙── ^ -//│ Parsed: {x : 1} +//│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here +//│ ║ l.127: x: 1, +//│ ╙── ^ +//│ Parsed: {x : ,[1, ()]} 1, 2, 3 //│ |1|,| |2|,| |3| -//│ ╔══[PARSE ERROR] Expected end of input; found comma instead -//│ ║ l.110: 1, 2, 3 -//│ ╙── ^ -//│ Parsed: {1} +//│ Parsed: {,(1, ,(2, 3,),)} f of 1, 2, 3 //│ |f| |#of| |1|,| |2|,| |3| -//│ Parsed: {f (1, 2, 3,)} +//│ Parsed: {f(1, 2, 3,)} f of x: 1, y: 2, z: 3 //│ |f| |#of| |x|#:| |1|,| |y|#:| |2|,| |z|#:| |3| -//│ Parsed: {f (x: 1, y: 2, z: 3,)} +//│ Parsed: {f(x: 1, y: 2, z: 3,)} f of x: 1, 2, z: 3 //│ |f| |#of| |x|#:| |1|,| |2|,| |z|#:| |3| -//│ Parsed: {f (x: 1, 2, z: 3,)} +//│ Parsed: {f(x: 1, 2, z: 3,)} f of x: 1, 2, 3 //│ |f| |#of|→|x|#:| |1|,| |2|,| |3|←| -//│ Parsed: {f (x: 1, 2, 3,)} +//│ Parsed: {f(x: 1, 2, 3,)} f of x: 1, y: 2, z: 3 //│ |f| |#of|→|x|#:| |1|,|↵|y|#:| |2|,|↵|z|#:| |3|←| -//│ Parsed: {f (x: 1, y: 2, z: 3,)} +//│ Parsed: {f(x: 1, y: 2, z: 3,)} f of x: 1 @@ -144,12 +165,12 @@ f of z: 3 //│ |f| |#of|→|x|#:| |1|↵|y|#:| |2|↵|z|#:| |3|←| //│ ╔══[PARSE ERROR] Unexpected named argument name here -//│ ║ l.142: x: 1 +//│ ║ l.163: x: 1 //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected named argument name here -//│ ║ l.143: y: 2 +//│ ║ l.164: y: 2 //│ ╙── ^ -//│ Parsed: {f (z: {1; 2; 3},)} +//│ Parsed: {f(z: {1; 2; 3},)} f of x: 1 @@ -157,9 +178,9 @@ f of z: 3 //│ |f| |#of|→|x|#:| |1|↵|2|↵|z|#:| |3|←| //│ ╔══[PARSE ERROR] Unexpected named argument name here -//│ ║ l.155: x: 1 +//│ ║ l.176: x: 1 //│ ╙── ^ -//│ Parsed: {f (z: {1; 2; 3},)} +//│ Parsed: {f(z: {1; 2; 3},)} f of x: 1 @@ -167,12 +188,12 @@ f of 3 //│ |f| |#of|→|x|#:| |1|↵|y|#:| |2|↵|3|←| //│ ╔══[PARSE ERROR] Unexpected named argument name here -//│ ║ l.165: x: 1 +//│ ║ l.186: x: 1 //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected named argument name here -//│ ║ l.166: y: 2 +//│ ║ l.187: y: 2 //│ ╙── ^ -//│ Parsed: {f ({1; 2; 3},)} +//│ Parsed: {f({1; 2; 3},)} diff --git a/shared/src/test/diff/parser/NegativeLits.mls b/shared/src/test/diff/parser/NegativeLits.mls new file mode 100644 index 0000000000..4290b760b4 --- /dev/null +++ b/shared/src/test/diff/parser/NegativeLits.mls @@ -0,0 +1,14 @@ +:NewParser +:ParseOnly + +type MinusOne = -1 +//│ |#type| |MinusOne| |#=| |-|1| +//│ Parsed: {type alias MinusOne: -1 {}} + +fun f(x: MinusOne) = x +//│ |#fun| |f|(|x|#:| |MinusOne|)| |#=| |x| +//│ Parsed: {fun f = (x: MinusOne,) => x} + +f(-1) +//│ |f|(|-|1|)| +//│ Parsed: {f(-1,)} diff --git a/shared/src/test/diff/parser/New.mls b/shared/src/test/diff/parser/New.mls index cd4c5b540b..f10a90908c 100644 --- a/shared/src/test/diff/parser/New.mls +++ b/shared/src/test/diff/parser/New.mls @@ -6,7 +6,7 @@ new //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.4: new //│ ╙── ^ -//│ Parsed: {new {}} +//│ Parsed: {new undefined} :pe (new) @@ -14,82 +14,65 @@ new //│ ╔══[PARSE ERROR] Unexpected end of parenthesis section; an expression was expected here //│ ║ l.12: (new) //│ ╙── ^ -//│ Parsed: {'(' new {}, ')'} +//│ Parsed: {'(' new undefined ')'} -:pe new {} //│ |#new| |{||}| -//│ ╔══[PARSE ERROR] Unexpected record after `new` keyword -//│ ║ l.20: new {} -//│ ╙── ^^ -//│ Parsed: {new {}} +//│ Parsed: {new '{' {} '}'} new A //│ |#new| |A| -//│ Parsed: {new A() {}} +//│ Parsed: {new A} new A() //│ |#new| |A|(||)| -//│ Parsed: {new A() {}} +//│ Parsed: {(new A)()} new A(1, 2, 3) //│ |#new| |A|(|1|,| |2|,| |3|)| -//│ Parsed: {new A(1, 2, 3,) {}} +//│ Parsed: {(new A)(1, 2, 3,)} new A of 1, 2, 3 //│ |#new| |A| |#of| |1|,| |2|,| |3| -//│ Parsed: {new A(1, 2, 3,) {}} +//│ Parsed: {new A(1, 2, 3,)} new A { fun x = 1 } //│ |#new| |A| |{| |#fun| |x| |#=| |1| |}| -//│ Parsed: {new A() {fun x = 1}} +//│ Parsed: {new A {fun x = 1}} new A() { fun x = 1 } //│ |#new| |A|(||)| |{| |#fun| |x| |#=| |1| |}| -//│ Parsed: {new A() {fun x = 1}} +//│ Parsed: {(new A)() {fun x = 1}} new A(1, 2, 3) { fun x = 1 } //│ |#new| |A|(|1|,| |2|,| |3|)| |{| |#fun| |x| |#=| |1| |}| -//│ Parsed: {new A(1, 2, 3,) {fun x = 1}} +//│ Parsed: {(new A)(1, 2, 3,) {fun x = 1}} new A of 1, 2, 3 { fun x = 1 } //│ |#new| |A| |#of| |1|,| |2|,| |3| |{| |#fun| |x| |#=| |1| |}| -//│ Parsed: {new A(1, 2, 3,) {fun x = 1}} +//│ Parsed: {new A(1, 2, 3 {fun x = 1},)} :pe new new //│ |#new| |#new| //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here -//│ ║ l.61: new new +//│ ║ l.57: new new //│ ╙── ^ -//│ ╔══[PARSE ERROR] Unexpected object instantiation after `new` keyword -//│ ║ l.61: new new -//│ ╙── ^^^ -//│ Parsed: {new {}} +//│ Parsed: {new new undefined} -:pe new new {} //│ |#new| |#new| |{||}| -//│ ╔══[PARSE ERROR] Unexpected record after `new` keyword -//│ ║ l.72: new new {} -//│ ╙── ^^ -//│ ╔══[PARSE ERROR] Unexpected object instantiation after `new` keyword -//│ ║ l.72: new new {} -//│ ╙── ^^^ -//│ Parsed: {new {}} +//│ Parsed: {new new '{' {} '}'} :pe new {new} //│ |#new| |{|#new|}| //│ ╔══[PARSE ERROR] Unexpected end of curly brace section; an expression was expected here -//│ ║ l.83: new {new} +//│ ║ l.69: new {new} //│ ╙── ^ //│ ╔══[PARSE ERROR] Record field should have a name -//│ ║ l.83: new {new} +//│ ║ l.69: new {new} //│ ╙── ^^^ -//│ ╔══[PARSE ERROR] Unexpected record after `new` keyword -//│ ║ l.83: new {new} -//│ ╙── ^^^^^ -//│ Parsed: {new {}} +//│ Parsed: {new '{' {: new undefined} '}'} diff --git a/shared/src/test/diff/parser/Ops.mls b/shared/src/test/diff/parser/Ops.mls index db31346ce9..87c0576404 100644 --- a/shared/src/test/diff/parser/Ops.mls +++ b/shared/src/test/diff/parser/Ops.mls @@ -3,7 +3,7 @@ acc + 1 //│ |acc| |+| |1| -//│ Parsed: {+ (acc,) (1,)} +//│ Parsed: {+(acc,)(1,)} acc + 1 @@ -16,12 +16,12 @@ acc acc + 1 //│ |acc|→|+| |1|←| -//│ Parsed: {+ acc 1} +//│ Parsed: {+(acc, 1,)} acc + 1 //│ |acc| |+|→|1|←| -//│ Parsed: {+ (acc,) ({1},)} +//│ Parsed: {+(acc,)({1},)} acc + @@ -32,34 +32,34 @@ acc //│ ║ ^ //│ ║ l.28: 1 //│ ╙── ^^ -//│ Parsed: {+ acc 1} +//│ Parsed: {+(acc, 1,)} acc + 1 //│ |acc|→|+|→|1|←|←| -//│ Parsed: {+ acc {1}} +//│ Parsed: {+(acc, {1},)} acc + 1 * 3 //│ |acc|→|+| |1|↵|*| |3|←| -//│ Parsed: {* (+ acc 1) 3} +//│ Parsed: {*(+(acc, 1,), 3,)} acc + 1 + 2 * 3 //│ |acc|→|+| |1|↵|+| |2|↵|*| |3|←| -//│ Parsed: {* (+ (+ acc 1) 2) 3} +//│ Parsed: {*(+(+(acc, 1,), 2,), 3,)} acc + 1 + 2 * 3 //│ |acc| |+| |1| |+| |2| |*| |3| -//│ Parsed: {+ (+ (acc,) (1,),) (* (2,) (3,),)} +//│ Parsed: {+(+(acc,)(1,),)(*(2,)(3,),)} acc+1+2*3 //│ |acc|+|1|+|2|*|3| -//│ Parsed: {+ (+ (acc,) (1,),) (* (2,) (3,),)} +//│ Parsed: {+(+(acc,)(1,),)(*(2,)(3,),)} acc + foo bar @@ -68,41 +68,41 @@ acc + //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.65: foo bar //│ ╙── ^^^^^^^ -//│ Parsed: {+ (acc,) ({foo (bar,); baz},)} +//│ Parsed: {+(acc,)({foo(bar,); baz},)} a+ b c //│ |a|+|→|b|↵|c|←| -//│ Parsed: {+ (a,) ({b; c},)} +//│ Parsed: {+(a,)({b; c},)} a+ b c //│ |a|+|→|b|←|↵|c| -//│ Parsed: {+ (a,) ({b},); c} +//│ Parsed: {+(a,)({b},); c} a +b c //│ |a|→|+|b|←|↵|c| -//│ Parsed: {+ a b; c} +//│ Parsed: {+(a, b,); c} a + b * 2 //│ |a| |+| |b|→|*| |2|←| -//│ Parsed: {+ (a,) (* b 2,)} +//│ Parsed: {+(a,)(*(b, 2,),)} a(b) * 2 //│ |a|(|b|)|→|*| |2|←| -//│ Parsed: {* (a (b,)) 2} +//│ Parsed: {*(a(b,), 2,)} a of b * 2 //│ |a| |#of| |b|→|*| |2|←| -//│ Parsed: {a (* b 2,)} +//│ Parsed: {a(*(b, 2,),)} a then b * 2 @@ -119,26 +119,26 @@ a + b + 1 * 3 //│ |a| |+| |b|→|*| |2|↵|+| |1|↵|*| |3|←| -//│ Parsed: {+ (a,) (* (+ (* b 2) 1) 3,)} +//│ Parsed: {+(a,)(*(+(*(b, 2,), 1,), 3,),)} a + b * 2 + 1 //│ |a| |+| |b|→|*| |2|→|+| |1|←|←| -//│ Parsed: {+ (a,) (* b (+ 2 1),)} +//│ Parsed: {+(a,)(*(b, +(2, 1,),),)} a + b * 2 - 1 //│ |a| |+| |b|→|*| |2|←|→|-| |1|←| -//│ Parsed: {- (+ (a,) (* b 2,)) 1} +//│ Parsed: {-(+(a,)(*(b, 2,),), 1,)} a + b * 2 + 1 * 3 //│ |a| |+| |b|→|*| |2|→|+| |1|←|↵|*| |3|←| -//│ Parsed: {+ (a,) (* (* b (+ 2 1)) 3,)} +//│ Parsed: {+(a,)(*(*(b, +(2, 1,),), 3,),)} a + b @@ -152,7 +152,7 @@ a + //│ ╔══[PARSE ERROR] Unexpected operator in expression position //│ ║ l.147: * 3 //│ ╙── ^ -//│ Parsed: {+ (a,) ({* b 2; 1; 3},)} +//│ Parsed: {+(a,)({*(b, 2,); 1; 3},)} a + b @@ -163,7 +163,7 @@ a + //│ ╔══[PARSE ERROR] Unexpected operator in expression position //│ ║ l.160: + 1 //│ ╙── ^ -//│ Parsed: {* (+ (a,) ({* b 2; 1},)) 3} +//│ Parsed: {*(+(a,)({*(b, 2,); 1},), 3,)} a + b @@ -175,7 +175,7 @@ a + //│ ╔══[PARSE ERROR] Unexpected operator in expression position //│ ║ l.171: + //│ ╙── ^ -//│ Parsed: {+ (a,) ({* b 2; {* 1 3}},)} +//│ Parsed: {+(a,)({*(b, 2,); {*(1, 3,)}},)} a + b @@ -184,7 +184,7 @@ a + 1 * 3 //│ |a| |+|→|b|→|*| |2|↵|+|→|1|→|*| |3|←|←|←|←| -//│ Parsed: {+ (a,) ({+ (* b 2) {* 1 3}},)} +//│ Parsed: {+(a,)({+(*(b, 2,), {*(1, 3,)},)},)} a + b @@ -195,7 +195,7 @@ a + //│ ╔══[PARSE ERROR] Unexpected operator in expression position //│ ║ l.192: + 1 //│ ╙── ^ -//│ Parsed: {+ (a,) ({* b 2; * 1 3},)} +//│ Parsed: {+(a,)({*(b, 2,); *(1, 3,)},)} a + b @@ -203,6 +203,25 @@ a + + 1 * 3 //│ |a| |+|→|b|→|*| |2|↵|+| |1|→|*| |3|←|←|←| -//│ Parsed: {+ (a,) ({+ (* b 2) (* 1 3)},)} +//│ Parsed: {+(a,)({+(*(b, 2,), *(1, 3,),)},)} +a + + b * + c +//│ |a| |+|→|b| |*|→|c|←|←| +//│ Parsed: {+(a,)({*(b,)({c},)},)} + +a * + b + + c +//│ |a| |*|→|b| |+|→|c|←|←| +//│ Parsed: {*(a,)({+(b,)({c},)},)} + +a * + let x = 1 + b + + c +//│ |a| |*|→|#let| |x| |#=| |1|↵|b| |+|→|c|←|←| +//│ Parsed: {*(a,)({let x = 1; +(b,)({c},)},)} + diff --git a/shared/src/test/diff/parser/Select.mls b/shared/src/test/diff/parser/Select.mls index e370e2a359..58a23c1c4a 100644 --- a/shared/src/test/diff/parser/Select.mls +++ b/shared/src/test/diff/parser/Select.mls @@ -5,11 +5,11 @@ x.a x . a //│ |x| |.| |a| -//│ Parsed: {. (x,) (a,)} +//│ Parsed: {.(x,)(a,)} x. a //│ |x|.| |a| -//│ Parsed: {. (x,) (a,)} +//│ Parsed: {.(x,)(a,)} x .a //│ |x| |.a| @@ -84,21 +84,13 @@ x //│ ╙── ^ //│ Parsed: {undefined} -:pe (.) //│ |(|.|)| -//│ ╔══[PARSE ERROR] Unexpected operator here -//│ ║ l.88: (.) -//│ ╙── ^ -//│ Parsed: {'(' ')'} +//│ Parsed: {.} -:pe a, b //│ |a|,| |b| -//│ ╔══[PARSE ERROR] Expected end of input; found comma instead -//│ ║ l.96: a, b -//│ ╙── ^ -//│ Parsed: {a} +//│ Parsed: {,(a, b,)} a .b .c @@ -107,32 +99,32 @@ a .b .c a + 1 .c //│ |a| |+| |1| |.c| -//│ Parsed: {+ (a,) ((1).c,)} +//│ Parsed: {+(a,)((1).c,)} a + f(1) .c //│ |a| |+| |f|(|1|)| |.c| -//│ Parsed: {+ (a,) ((f (1,)).c,)} +//│ Parsed: {+(a,)((f(1,)).c,)} a + f (1) .c //│ |a| |+| |f| |(|1|)| |.c| -//│ Parsed: {+ (a,) ((f (1,)).c,)} +//│ Parsed: {+(a,)((f(1,)).c,)} a + f (1).c //│ |a| |+| |f| |(|1|)|.c| -//│ Parsed: {+ (a,) ((f (1,)).c,)} +//│ Parsed: {+(a,)((f(1,)).c,)} f of x . y //│ |f| |#of| |x| |.| |y| -//│ Parsed: {f (. (x,) (y,),)} +//│ Parsed: {f(.(x,)(y,),)} f of x .y //│ |f| |#of| |x| |.y| -//│ Parsed: {f ((x).y,)} +//│ Parsed: {f((x).y,)} a + b .c //│ |a| |+| |b|→|.c|←| -//│ Parsed: {(+ (a,) (b,)).c} +//│ Parsed: {(+(a,)(b,)).c} :pe a + @@ -140,49 +132,49 @@ a + .c //│ |a| |+|→|b|↵|.c|←| //│ ╔══[PARSE ERROR] Unexpected selector in expression position -//│ ║ l.140: .c +//│ ║ l.132: .c //│ ╙── ^^ //│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here -//│ ║ l.140: .c +//│ ║ l.132: .c //│ ╙── ^ -//│ Parsed: {+ (a,) ({b; undefined},)} +//│ Parsed: {+(a,)({b; undefined},)} a + b .c //│ |a| |+|→|b|→|.c|←|←| -//│ Parsed: {+ (a,) ({(b).c},)} +//│ Parsed: {+(a,)({(b).c},)} a + b .c * 2 .d - 1 //│ |a| |+| |b|→|.c| |*| |2|→|.d| |-| |1|←|←| -//│ Parsed: {- ((* ((+ (a,) (b,)).c,) (2,)).d,) (1,)} +//│ Parsed: {-((*((+(a,)(b,)).c,)(2,)).d,)(1,)} a + b .c * 2 .d - 1 //│ |a| |+| |b|→|.c| |*| |2|↵|.d| |-| |1|←| -//│ Parsed: {- ((* ((+ (a,) (b,)).c,) (2,)).d,) (1,)} +//│ Parsed: {-((*((+(a,)(b,)).c,)(2,)).d,)(1,)} a .b of c //│ |a|→|.b|↵|#of| |c|←| -//│ Parsed: {(a).b (c,)} +//│ Parsed: {(a).b(c,)} a .b + 1 of c //│ |a|→|.b| |+| |1|↵|#of| |c|←| -//│ Parsed: {+ ((a).b,) (1,) (c,)} +//│ Parsed: {+((a).b,)(1,)(c,)} a .b + 1 of c //│ |a|→|.b| |+| |1|→|#of| |c|←|←| -//│ Parsed: {+ ((a).b,) (1,) (c,)} +//│ Parsed: {+((a).b,)(1,)(c,)} :pe a @@ -191,37 +183,37 @@ a of c //│ |a|→|.b| |+|→|1|↵|#of| |c|←|←| //│ ╔══[PARSE ERROR] Unexpected 'of' keyword in expression position -//│ ║ l.191: of c +//│ ║ l.183: of c //│ ╙── ^^ -//│ Parsed: {+ ((a).b,) ({1; c},)} +//│ Parsed: {+((a).b,)({1; c},)} a .b + 1 of c //│ |a|→|.b| |+|→|1|→|#of| |c|←|←|←| -//│ Parsed: {+ ((a).b,) ({1 (c,)},)} +//│ Parsed: {+((a).b,)({1(c,)},)} a .b of c .d //│ |a|→|.b|↵|#of| |c|↵|.d|←| -//│ Parsed: {((a).b (c,)).d} +//│ Parsed: {((a).b(c,)).d} a .b of c .d //│ |a|→|.b|→|#of| |c|←|↵|.d|←| -//│ Parsed: {((a).b (c,)).d} +//│ Parsed: {((a).b(c,)).d} a .b +1 *2 //│ |a|→|.b|↵|+|1|↵|*|2|←| -//│ Parsed: {* (+ ((a).b,) (1,),) (2,)} +//│ Parsed: {*(+((a).b,)(1,),)(2,)} // TODO we should find a more uniform way of typing indented operator/selector/of blocks a @@ -230,33 +222,33 @@ a .b //│ |a|→|+|1|↵|*|2|↵|.b|←| //│ ╔══[PARSE ERROR] Unexpected selector in operator block -//│ ║ l.230: .b +//│ ║ l.222: .b //│ ╙── ^^ -//│ Parsed: {* (+ a 1) 2} +//│ Parsed: {*(+(a, 1,), 2,)} 1 .+ 2 //│ |1| |.+| |2| -//│ Parsed: {.+ (1,) (2,)} +//│ Parsed: {.+(1,)(2,)} 1 +. 2 //│ |1| |+.| |2| -//│ Parsed: {+. (1,) (2,)} +//│ Parsed: {+.(1,)(2,)} 1 .+! 2 //│ |1| |.+!| |2| -//│ Parsed: {.+! (1,) (2,)} +//│ Parsed: {.+!(1,)(2,)} 0 * 1 .+. 2 * 3 //│ |0| |*| |1| |.+.| |2| |*| |3| -//│ Parsed: {* (* (0,) (.+. (1,) (2,),),) (3,)} +//│ Parsed: {*(*(0,)(.+.(1,)(2,),),)(3,)} :w 1 .+a 2 //│ |1| |.+|a| |2| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.255: 1 .+a 2 +//│ ║ l.247: 1 .+a 2 //│ ╙── ^^^ -//│ Parsed: {.+ (1,) (a (2,),)} +//│ Parsed: {.+(1,)(a(2,),)} diff --git a/shared/src/test/diff/parser/SpecParams.mls b/shared/src/test/diff/parser/SpecParams.mls index 44cb4d192b..d31db8c1a4 100644 --- a/shared/src/test/diff/parser/SpecParams.mls +++ b/shared/src/test/diff/parser/SpecParams.mls @@ -3,15 +3,15 @@ fun Foo(#x, y) = x + y //│ |#fun| |Foo|(|##|x|,| |y|)| |#=| |x| |+| |y| -//│ Parsed: {fun Foo = (#x, y,) => + (x,) (y,)} +//│ Parsed: {fun Foo = (#x, y,) => +(x,)(y,)} fun Foo(x, #y) = x + y //│ |#fun| |Foo|(|x|,| |##|y|)| |#=| |x| |+| |y| -//│ Parsed: {fun Foo = (x, #y,) => + (x,) (y,)} +//│ Parsed: {fun Foo = (x, #y,) => +(x,)(y,)} fun Foo(#x, #y, #z) = if z then x + y else z //│ |#fun| |Foo|(|##|x|,| |##|y|,| |##|z|)| |#=| |#if| |z| |#then| |x| |+| |y| |#else| |z| -//│ Parsed: {fun Foo = (#x, #y, #z,) => if (z) then + (x,) (y,) else z} +//│ Parsed: {fun Foo = (#x, #y, #z,) => if (z) then +(x,)(y,) else z} class Foo(x, #y, z) diff --git a/shared/src/test/diff/parser/Subscripts.mls b/shared/src/test/diff/parser/Subscripts.mls index 3adeb01ad9..a37f7ea274 100644 --- a/shared/src/test/diff/parser/Subscripts.mls +++ b/shared/src/test/diff/parser/Subscripts.mls @@ -1,82 +1,82 @@ a[0] //│ |a|[|0|]| -//│ Parsed: {(a)[0]} +//│ Parsed: {a‹0›} a[0][1] //│ |a|[|0|]|[|1|]| -//│ Parsed: {((a)[0])[1]} +//│ Parsed: {a‹0›‹1›} a.x[0].y[1].z //│ |a|.x|[|0|]|.y|[|1|]|.z| -//│ Parsed: {(((((a).x)[0]).y)[1]).z} +//│ Parsed: {(((a).x‹0›).y‹1›).z} a + b [0] * 2 //│ |a| |+| |b| |[|0|]| |*| |2| -//│ Parsed: {+ (a,) (* ((b)[0],) (2,),)} +//│ Parsed: {+(a,)(*(b‹0›,)(2,),)} foo [1] //│ |foo|→|[|1|]|←| -//│ Parsed: {(foo)[1]} +//│ Parsed: {foo‹1›} Foo(bar) + 1 ["hello"] //│ |Foo|(|bar|)| |+| |1|→|[|"hello"|]|←| -//│ Parsed: {(+ (Foo (bar,),) (1,))["hello"]} +//│ Parsed: {+(Foo(bar,),)(1,)‹"hello"›} Foo(bar) + 1["hello"] //│ |Foo|(|bar|)| |+|→|1|[|"hello"|]|←| -//│ Parsed: {+ (Foo (bar,),) ({(1)["hello"]},)} +//│ Parsed: {+(Foo(bar,),)({1‹"hello"›},)} Foo(bar) + 1 ["hello"] //│ |Foo|(|bar|)| |+|→|1|→|[|"hello"|]|←|←| -//│ Parsed: {+ (Foo (bar,),) ({(1)["hello"]},)} +//│ Parsed: {+(Foo(bar,),)({1‹"hello"›},)} Foo(bar) + 1 [1] [2] //│ |Foo|(|bar|)| |+| |1|→|[|1|]|↵|[|2|]|←| -//│ Parsed: {((+ (Foo (bar,),) (1,))[1])[2]} +//│ Parsed: {+(Foo(bar,),)(1,)‹1›‹2›} Foo(bar) + 1 [1] [2] //│ |Foo|(|bar|)| |+| |1|→|[|1|]|→|[|2|]|←|←| -//│ Parsed: {((+ (Foo (bar,),) (1,))[1])[2]} +//│ Parsed: {+(Foo(bar,),)(1,)‹1›‹2›} a + 111 [22] [333] //│ |a| |+|→|111|→|[|22|]|←|↵|[|333|]|←| -//│ Parsed: {+ (a,) ({(111)[22]; '(' 333, ')'},)} +//│ Parsed: {+(a,)({111‹22›; [333,]},)} Foo(bar) + 1 [1] [2] //│ |Foo|(|bar|)| |+|→|1|→|[|1|]|←|↵|[|2|]|←| -//│ Parsed: {+ (Foo (bar,),) ({(1)[1]; '(' 2, ')'},)} +//│ Parsed: {+(Foo(bar,),)({1‹1›; [2,]},)} a of [333] //│ |a| |#of| |[|333|]| -//│ Parsed: {a ('(' 333, ')',)} +//│ Parsed: {a([333,],)} a of [333] //│ |a| |#of|→|[|333|]|←| -//│ Parsed: {a ('(' 333, ')',)} +//│ Parsed: {a([333,],)} a of 111 [22] [333] //│ |a| |#of|→|111|→|[|22|]|←|↵|[|333|]|←| -//│ Parsed: {a ({(111)[22]; '(' 333, ')'},)} +//│ Parsed: {a({111‹22›; [333,]},)} a( 111 @@ -84,5 +84,5 @@ a( [333] ) //│ |a|(|→|111|→|[|22|]|←|↵|[|333|]|←|↵|)| -//│ Parsed: {a ({(111)[22]; '(' 333, ')'},)} +//│ Parsed: {a({111‹22›; [333,]},)} diff --git a/shared/src/test/diff/parser/TypeParams.mls b/shared/src/test/diff/parser/TypeParams.mls index 5d8c02b609..a6e1ae383c 100644 --- a/shared/src/test/diff/parser/TypeParams.mls +++ b/shared/src/test/diff/parser/TypeParams.mls @@ -2,18 +2,18 @@ class Foo //│ |#class| |Foo|‹|A|›| -//│ Parsed: {class Foo‹A›() {}} +//│ Parsed: {class Foo‹A› {}} class Foo‹A› //│ |#class| |Foo|‹|A|›| -//│ Parsed: {class Foo‹A›() {}} +//│ Parsed: {class Foo‹A› {}} (1) //│ |(|1|)| -//│ Parsed: {'(' 1, ')'} +//│ Parsed: {'(' 1 ')'} foo<1,2,3> @@ -26,15 +26,15 @@ foo<1, 2, 3> foo<1, 2, 3>() //│ |foo|‹|1|,| |2|,| |3|›|(||)| -//│ Parsed: {foo‹1, 2, 3› ()} +//│ Parsed: {foo‹1, 2, 3›()} foo<1, 2, 3>(1, 2, 3) //│ |foo|‹|1|,| |2|,| |3|›|(|1|,| |2|,| |3|)| -//│ Parsed: {foo‹1, 2, 3› (1, 2, 3,)} +//│ Parsed: {foo‹1, 2, 3›(1, 2, 3,)} 1 < 2 > 4 //│ |1| |<| |2| |>| |4| -//│ Parsed: {> (< (1,) (2,),) (4,)} +//│ Parsed: {>(<(1,)(2,),)(4,)} :w 1 < 2> 4 @@ -43,7 +43,7 @@ foo<1, 2, 3>(1, 2, 3) //│ ║ ^ //│ ╙── Add spaces around it if you intended to use `<` as an operator //│ |1| |<| |2|>| |4| -//│ Parsed: {> (< (1,) (2,),) (4,)} +//│ Parsed: {>(<(1,)(2,),)(4,)} :pe :w @@ -56,7 +56,7 @@ foo<1, 2, 3>(1, 2, 3) //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.50: 1< 2 > 4 //│ ╙── ^^^^^^^^ -//│ Parsed: {> (2,) (4 (1,),)} +//│ Parsed: {>(2,)(4(1,),)} :w 1< 2> 4 @@ -64,19 +64,19 @@ foo<1, 2, 3>(1, 2, 3) //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.62: 1< 2> 4 //│ ╙── ^^^^^^^ -//│ Parsed: {1‹2› (4,)} +//│ Parsed: {1‹2›(4,)} 1< 2> (4) //│ |1|‹| |2|›| |(|4|)| -//│ Parsed: {1‹2› (4,)} +//│ Parsed: {1‹2›(4,)} 1<2>(4) //│ |1|‹|2|›|(|4|)| -//│ Parsed: {1‹2› (4,)} +//│ Parsed: {1‹2›(4,)} 1<>2 //│ |1|<>|2| -//│ Parsed: {<> (1,) (2,)} +//│ Parsed: {<>(1,)(2,)} :pe 1< >2 @@ -88,7 +88,7 @@ foo<1, 2, 3>(1, 2, 3) //│ ╔══[PARSE ERROR] Unexpected operator in expression position //│ ║ l.82: 1< >2 //│ ╙── ^ -//│ Parsed: {2 (1,)} +//│ Parsed: {2(1,)} :pe 1 < >2 @@ -96,28 +96,25 @@ foo<1, 2, 3>(1, 2, 3) //│ ╔══[PARSE ERROR] Unexpected operator in expression position //│ ║ l.94: 1 < >2 //│ ╙── ^ -//│ Parsed: {< (1,) (2,)} +//│ Parsed: {<(1,)(2,)} foo(1, 2, 3) & Bar("ok") //│ |foo|‹|S|,| |T|›|(|1|,| |2|,| |3|)| |&| |Bar|‹|U|›|(|"ok"|)| -//│ Parsed: {& (foo‹S, T› (1, 2, 3,),) (Bar‹U› ("ok",),)} +//│ Parsed: {&(foo‹S, T›(1, 2, 3,),)(Bar‹U›("ok",),)} foo(2 + 3) & Bar("ok") //│ |foo|‹|Str|,| |Int|›|(|2| |+| |3|)| |&| |Bar|‹|MyClass|›|(|"ok"|)| -//│ Parsed: {& (foo‹Str, Int› (+ (2,) (3,),),) (Bar‹MyClass› ("ok",),)} +//│ Parsed: {&(foo‹Str, Int›(+(2,)(3,),),)(Bar‹MyClass›("ok",),)} -:pe +// :pe foo[S, T](1, 2, 3) & Bar[U]("ok") //│ |foo|[|S|,| |T|]|(|1|,| |2|,| |3|)| |&| |Bar|[|U|]|(|"ok"|)| -//│ ╔══[PARSE ERROR] Unexpected comma here -//│ ║ l.111: foo[S, T](1, 2, 3) & Bar[U]("ok") -//│ ╙── ^ -//│ Parsed: {& ((foo)[S] (1, 2, 3,),) ((Bar)[U] ("ok",),)} +//│ Parsed: {&(foo‹S, T›(1, 2, 3,),)(Bar‹U›("ok",),)} class Foo {} //│ |#class| |Foo|‹|T|›| |{||}| -//│ Parsed: {class Foo‹T›() {}} +//│ Parsed: {class Foo‹T› {}} fun foo(x: T): string = "rua" //│ |#fun| |foo|‹|T|›|(|x|#:| |T|)|#:| |string| |#=| |"rua"| @@ -125,4 +122,4 @@ fun foo(x: T): string = "rua" foo(42) //│ |foo|‹|int|›|(|42|)| -//│ Parsed: {foo‹int› (42,)} +//│ Parsed: {foo‹int›(42,)} diff --git a/shared/src/test/diff/parser/UserDefinedOps?.mls b/shared/src/test/diff/parser/UserDefinedOpsMaybe.mls similarity index 92% rename from shared/src/test/diff/parser/UserDefinedOps?.mls rename to shared/src/test/diff/parser/UserDefinedOpsMaybe.mls index bceeddd202..b36f6b4818 100644 --- a/shared/src/test/diff/parser/UserDefinedOps?.mls +++ b/shared/src/test/diff/parser/UserDefinedOpsMaybe.mls @@ -9,7 +9,7 @@ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.4: 1 div 2 //│ ╙── ^^^^^^^ -//│ Parsed: {1 (div (2,),)} +//│ Parsed: {1(div(2,),)} 1 mod 2 //│ |1| |mod| |2| @@ -19,7 +19,7 @@ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.14: 1 mod 2 //│ ╙── ^^^^^^^ -//│ Parsed: {1 (mod (2,),)} +//│ Parsed: {1(mod(2,),)} 3 eq 4 //│ |3| |eq| |4| @@ -29,7 +29,7 @@ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.24: 3 eq 4 //│ ╙── ^^^^^^ -//│ Parsed: {3 (eq (4,),)} +//│ Parsed: {3(eq(4,),)} xs map f map g map h //│ |xs| |map| |f| |map| |g| |map| |h| @@ -51,6 +51,6 @@ xs map f map g map h //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.34: xs map f map g map h //│ ╙── ^^^^^^^^^^^^^^^^^^^^ -//│ Parsed: {xs (map (f (map (g (map (h,),),),),),)} +//│ Parsed: {xs(map(f(map(g(map(h,),),),),),)} diff --git a/shared/src/test/diff/parser/WeirdIfs.mls b/shared/src/test/diff/parser/WeirdIfs.mls index b61b6cc3a3..0bfb8c3155 100644 --- a/shared/src/test/diff/parser/WeirdIfs.mls +++ b/shared/src/test/diff/parser/WeirdIfs.mls @@ -17,15 +17,15 @@ if x is //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.12: else e //│ ╙── ^ -//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause; found operator application instead +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead //│ ║ l.11: if x is //│ ║ ^^^^ //│ ║ l.12: else e //│ ║ ^^ -//│ ╟── Note: 'if' expression started here: +//│ ╟── Note: 'if' expression starts here: //│ ║ l.11: if x is //│ ╙── ^^ -//│ Parsed: {if (is (x,) (undefined,)) then undefined} +//│ Parsed: {if (is(x,)(undefined,)) then undefined} :pe if x is @@ -34,15 +34,15 @@ if x is //│ ╔══[PARSE ERROR] Unexpected 'else' keyword here //│ ║ l.32: P else e //│ ╙── ^^^^ -//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause; found operator application instead +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead //│ ║ l.31: if x is //│ ║ ^^^^ //│ ║ l.32: P else e //│ ║ ^^^^ -//│ ╟── Note: 'if' expression started here: +//│ ╟── Note: 'if' expression starts here: //│ ║ l.31: if x is //│ ╙── ^^ -//│ Parsed: {if (is (x,) ({P},)) then undefined} +//│ Parsed: {if (is(x,)({P},)) then undefined} // TODO(Luyu): move to another test file @@ -51,7 +51,7 @@ if x is Some(xv) and y is Right(yv2) then xv + yv2 else els //│ |#if| |x| |is|→|Some|(|xv|)| |and| |y| |is| |Left|(|yv|)| |#then| |xv| |+| |yv|↵|Some|(|xv|)| |and| |y| |is| |Right|(|yv2|)| |#then| |xv| |+| |yv2|↵|#else| |els|←| -//│ Parsed: {if x is ‹(and (Some (xv,),) (is (y,) (Left (yv,),),)) then + (xv,) (yv,); (and (Some (xv,),) (is (y,) (Right (yv2,),),)) then + (xv,) (yv2,); else els›} +//│ Parsed: {if x is ‹(and(Some(xv,),)(is(y,)(Left(yv,),),)) then +(xv,)(yv,); (and(Some(xv,),)(is(y,)(Right(yv2,),),)) then +(xv,)(yv2,); else els›} diff --git a/shared/src/test/diff/parser/Where.mls b/shared/src/test/diff/parser/Where.mls index 2b1c6bc2a3..0bc3cf7667 100644 --- a/shared/src/test/diff/parser/Where.mls +++ b/shared/src/test/diff/parser/Where.mls @@ -1,44 +1,35 @@ 1 + 1 where 1 //│ |1| |+| |1| |#where| |1| -//│ Parsed: {+ (1,) (1,) where {1}} +//│ Parsed: {+(1,)(1,) where {1}} a => a + 1 where foo -//│ |a| |=>| |a| |+| |1| |#where| |foo| -//│ Parsed: {(a,) => + (a,) (1,) where {foo}} +//│ |a| |#=>| |a| |+| |1| |#where| |foo| +//│ Parsed: {(a,) => +(a,)(1,) where {foo}} a + 1 where let a = 1 //│ |a| |+| |1| |#where| |#let| |a| |#=| |1| -//│ Parsed: {+ (a,) (1,) where {let a = 1}} +//│ Parsed: {+(a,)(1,) where {let a = 1}} fun foo: 'a => 'a => 'a where 'a : int -//│ |#fun| |foo|#:| |'a| |=>| |'a| |=>| |'a| |#where| |'a| |#:| |int| -//│ Parsed: {fun foo: 'a -> 'a -> 'a +//│ |#fun| |foo|#:| |'a| |#=>| |'a| |#=>| |'a| |#where| |'a| |#:| |int| +//│ Parsed: {fun foo: 'a -> 'a -> ('a //│ where -//│ 'a <: int} +//│ 'a <: int)} +:pe fun foo: 'a + 'a + 'a where 'a : int //│ |#fun| |foo|#:| |'a| |+| |'a| |+| |'a| |#where| |'a| |#:| |int| -//│ Parsed: {fun foo: +[(+[('a,), ('a,)],), ('a,)] -//│ where -//│ 'a <: int} +//│ ╔══[PARSE ERROR] Not a recognized type +//│ ║ l.21: fun foo: 'a + 'a + 'a where 'a : int +//│ ╙── ^^^^^^^ +//│ Parsed: {fun foo: anything} :pe -:w -:e fun foo: 'a -> 'a -> 'a where 'a : int -//│ |#fun| |foo|#:| |'a| |#->| |'a| |#->| |'a| |#where| |'a| |#:| |int| -//│ ╔══[PARSE ERROR] Unexpected '->' keyword in expression position -//│ ║ l.29: fun foo: 'a -> 'a -> 'a where 'a : int -//│ ╙── ^^ -//│ ╔══[PARSE ERROR] Unexpected '->' keyword in expression position -//│ ║ l.29: fun foo: 'a -> 'a -> 'a where 'a : int -//│ ╙── ^^ -//│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.29: fun foo: 'a -> 'a -> 'a where 'a : int -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╔══[ERROR] not a recognized type: 'a ('a ('a where {'a : int},),) +//│ |#fun| |foo|#:| |'a| |->| |'a| |->| |'a| |#where| |'a| |#:| |int| +//│ ╔══[PARSE ERROR] Not a recognized type //│ ║ l.29: fun foo: 'a -> 'a -> 'a where 'a : int -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╙── ^^^^^^^^^^^^^^ //│ Parsed: {fun foo: anything} diff --git a/shared/src/test/diff/parser/Whitespace.mls b/shared/src/test/diff/parser/Whitespace.mls index 986b2d1476..036725732c 100644 --- a/shared/src/test/diff/parser/Whitespace.mls +++ b/shared/src/test/diff/parser/Whitespace.mls @@ -16,13 +16,13 @@ false(1 , 2 , 3) //│ |false|(|1| |,| |2| |,| |3|)| -//│ Parsed: {false (1, 2, 3,)} +//│ Parsed: {false(1, 2, 3,)} false (1 ,2 ,3) //│ |false| |(|1| |,|2| |,|3|)| -//│ Parsed: {false (1, 2, 3,)} +//│ Parsed: {false(1, 2, 3,)} false( 1 , 2 , 3 ) //│ |false|(| |1| |,| |2| |,| |3| |)| -//│ Parsed: {false (1, 2, 3,)} +//│ Parsed: {false(1, 2, 3,)} diff --git a/shared/src/test/diff/parser/boolops.mls b/shared/src/test/diff/parser/boolops.mls index 6f4fb54d92..dbc1e7d5d6 100644 --- a/shared/src/test/diff/parser/boolops.mls +++ b/shared/src/test/diff/parser/boolops.mls @@ -1,31 +1,31 @@ true and false //│ |true| |and| |false| -//│ Parsed: {and (true,) (false,)} +//│ Parsed: {and(true,)(false,)} a and b or c //│ |a| |and| |b| |or| |c| -//│ Parsed: {or (and (a,) (b,),) (c,)} +//│ Parsed: {or(and(a,)(b,),)(c,)} a or b and c //│ |a| |or| |b| |and| |c| -//│ Parsed: {or (a,) (and (b,) (c,),)} +//│ Parsed: {or(a,)(and(b,)(c,),)} a + 1 or b + 2 and c + 3 //│ |a| |+| |1| |or| |b| |+| |2| |and| |c| |+| |3| -//│ Parsed: {or (+ (a,) (1,),) (and (+ (b,) (2,),) (+ (c,) (3,),),)} +//│ Parsed: {or(+(a,)(1,),)(and(+(b,)(2,),)(+(c,)(3,),),)} any of a, b, c //│ |any| |#of| |a|,| |b|,| |c| -//│ Parsed: {any (a, b, c,)} +//│ Parsed: {any(a, b, c,)} any of a b c //│ |any| |#of|→|a|↵|b|↵|c|←| -//│ Parsed: {any ({a; b; c},)} +//│ Parsed: {any({a; b; c},)} all of x @@ -35,7 +35,7 @@ all of c y //│ |all| |#of|→|x|↵|any| |#of|→|a|↵|b|↵|c|←|↵|y|←| -//│ Parsed: {all ({x; any ({a; b; c},); y},)} +//│ Parsed: {all({x; any({a; b; c},); y},)} diff --git a/shared/src/test/diff/scalac/i13162.mls b/shared/src/test/diff/scalac/i13162.mls new file mode 100644 index 0000000000..90e605fb7b --- /dev/null +++ b/shared/src/test/diff/scalac/i13162.mls @@ -0,0 +1,131 @@ +:NewDefs + + +// https://github.com/lampepfl/dotty/issues/13162 + +fun method = + class Person(val name: Str) + module Person_ { // TODO change when module overloading is supported + val me = Person("Cameron") + } + let m = Person_.me + m +method.name +//│ fun method: Person +//│ Str +//│ res +//│ = 'Cameron' + + +// https://github.com/lampepfl/dotty/issues/13162#issuecomment-887557311 + +// * We don't currently have self bindings + +// def method(): Unit = { +// final case class Person(name: String) +// object Person { self => +// val me = self.apply("Cameron") +// } +// val _ = Person.me +// } +// method() + +:w +:e +fun method: () = + class Person(val name: Str) + module Person_ { self => // * defines a useless lambda! + val me = self.apply("Cameron") + } + let m = Person_.me + m +method +//│ ╔══[WARNING] Pure expression does nothing in statement position. +//│ ║ l.37: module Person_ { self => // * defines a useless lambda! +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.38: val me = self.apply("Cameron") +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╔══[ERROR] Type `Person_` does not contain member `me` +//│ ║ l.40: let m = Person_.me +//│ ╙── ^^^ +//│ fun method: () +//│ () +//│ res +//│ = undefined + + +// https://github.com/lampepfl/dotty/issues/13162#issuecomment-888188804 + +:re +module Person { + fun f: () + fun f = Person2.f +} +module Person2 { + fun f = () + val me = Person.f +} +//│ module Person { +//│ fun f: () +//│ } +//│ module Person2 { +//│ fun f: () +//│ val me: () +//│ } +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + +fun test = Person2.me +//│ fun test: () + + +// * FIXME initialization check? or different codegen? +fun test = + module Person { + fun f: () + fun f = Person2.f + } + module Person2 { + fun f = () + val me = Person.f + } + Person2.me +//│ fun test: () + +:re +test +//│ () +//│ res +//│ Runtime error: +//│ ReferenceError: Cannot access 'Person2' before initialization + +module Test { + module Person { + fun f: () + fun f = Person2.f + } + module Person2 { + fun f = () + val me = Person.f + } + fun test = Person2.me +} +//│ module Test { +//│ module Person { +//│ fun f: () +//│ } +//│ module Person2 { +//│ fun f: () +//│ val me: () +//│ } +//│ fun test: () +//│ } + +:re +Test.test +//│ () +//│ res +//│ Runtime error: +//│ RangeError: Maximum call stack size exceeded + + diff --git a/shared/src/test/diff/tapl/NuSimplyTyped.mls b/shared/src/test/diff/tapl/NuSimplyTyped.mls new file mode 100644 index 0000000000..65c3b0dba2 --- /dev/null +++ b/shared/src/test/diff/tapl/NuSimplyTyped.mls @@ -0,0 +1,167 @@ +:NewParser +:NewDefs + +fun (++) concatOp(a, b) = concat(a)(b) +//│ fun (++) concatOp: (Str, Str) -> Str + +fun par(a) = "(" ++ a ++ ")" +//│ fun par: Str -> Str + +type Option[out A] = Some[A] | None +class Some[out A](value: A) +module None +//│ type Option[A] = None | Some[A] +//│ class Some[A](value: A) +//│ module None + +type Result[A, B] = Ok[A] | Err[B] +class Ok[A](value: A) +class Err[A](message: A) +//│ type Result[A, B] = Err[B] | Ok[A] +//│ class Ok[A](value: A) +//│ class Err[A](message: A) + +type Type = FunctionType | PrimitiveType +class PrimitiveType(name: Str) +class FunctionType(lhs: Type, rhs: Type) +//│ type Type = FunctionType | PrimitiveType +//│ class PrimitiveType(name: Str) +//│ class FunctionType(lhs: Type, rhs: Type) + +// Helpers. +fun _f(lhs, rhs) = FunctionType(lhs, rhs) +fun _t(name) = PrimitiveType(name) +//│ fun _f: (Type, Type) -> FunctionType +//│ fun _t: Str -> PrimitiveType + +type Term = Lit | Var | Abs | App +class Lit(tag: Str, ty: Type) +class Var(name: Str) +class Abs(lhs: Var, lty: Type, rhs: Term) +class App(lhs: Term, rhs: Term) +// class App(lhs: Term, rhs: Term): Term +//│ type Term = Abs | App | Lit | Var +//│ class Lit(tag: Str, ty: Type) +//│ class Var(name: Str) +//│ class Abs(lhs: Var, lty: Type, rhs: Term) +//│ class App(lhs: Term, rhs: Term) + +type TreeMap[A] = Node[A] | Empty +class Node[A](key: Str, value: A, left: TreeMap[A], right: TreeMap[A]) +module Empty +//│ type TreeMap[A] = Empty | Node[A] +//│ class Node[A](key: Str, value: A, left: TreeMap[A], right: TreeMap[A]) +//│ module 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 +//│ fun insert: forall 'A. (Empty | Node['A], Str, 'A) -> Node['A] +//│ fun find: forall 'A0. (Empty | Node['A0], Str) -> (None | Some['A0]) + +fun showType(ty) = + if ty is + FunctionType(PrimitiveType(name), rhs) then name ++ " -> " ++ showType(rhs) + FunctionType(lhs, rhs) then "(" ++ showType(lhs) ++ ") -> " ++ showType(rhs) + PrimitiveType(name) then name +//│ fun showType: (FunctionType | PrimitiveType) -> Str + +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")))) +//│ Str +//│ res +//│ = 'int' +//│ res +//│ = 'int -> bool' +//│ res +//│ = '(int -> bool) -> bool' +//│ res +//│ = '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 +//│ fun typeEqual: (Object, Object) -> Bool + +fun showTerm(t) = + if t is + Lit(tag, _) then tag + Var(name) then name + Abs(lhs, ty, rhs) then "&" ++ showTerm(lhs) ++ ": " ++ showType(ty) ++ " => " ++ showTerm(rhs) + App(Abs(lhs0, ty, lhs1), rhs) then + "((" ++ showTerm(Abs(lhs0, ty, rhs)) ++ ") " ++ showTerm(rhs) ++ ")" + App(lhs, rhs) then par(showTerm(lhs) ++ " " ++ showTerm(rhs)) +//│ fun showTerm: (Abs | App | Lit | Var) -> Str + +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"))) +//│ Str +//│ res +//│ = 'x' +//│ res +//│ = '&x: int => y' +//│ res +//│ = '(x y)' +//│ res +//│ = '((&x: int => z) z)' + +// Removing the return type annotation causes stack overflow. +fun typeTerm(t: Term, ctx: TreeMap[Type]): Result[Type, Str] = + if t is + Lit(_, ty) then Ok(ty) + Var(name) and find(ctx, name) is + Some(ty) then Ok(ty) + None then Err("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) + else Err("expect the argument to be of type `" ++ showType(pTy) ++ "` but found `" ++ showType(aTy) ++ "`") + Err(message) then Err(message) + Ok(PrimitiveType(name)) then Err("cannot apply primitive type `" ++ name ++ "`") + Err(message) then Err(message) +//│ fun typeTerm: (t: Term, ctx: TreeMap[Type]) -> Result[Type, Str] + +fun showTypeTerm(t, ctx) = + if typeTerm(t, ctx) is + Ok(ty) then showTerm(t) ++ " : " ++ showType(ty) + Err(message) then "Type error: " ++ message +//│ fun showTypeTerm: (Term, TreeMap[Type]) -> Str + +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("Str"))) +//│ Str +//│ res +//│ = 'Type error: unbound variable `x`' +//│ res +//│ = '&x: int => x : int -> int' +//│ res +//│ = '(f 0) : int' +//│ res +//│ = 'Type error: expect the argument to be of type `int` but found `float`' +//│ res +//│ = 'Type error: cannot apply primitive type `Str`' diff --git a/shared/src/test/diff/tapl/NuUntyped.mls b/shared/src/test/diff/tapl/NuUntyped.mls new file mode 100644 index 0000000000..a9427adc52 --- /dev/null +++ b/shared/src/test/diff/tapl/NuUntyped.mls @@ -0,0 +1,448 @@ +: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[out A] = Some[A] | None +class Some[out 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 new file mode 100644 index 0000000000..400be3d6e9 --- /dev/null +++ b/shared/src/test/diff/tapl/SimplyTyped.mls @@ -0,0 +1,503 @@ +: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 new file mode 100644 index 0000000000..c8eb62a22f --- /dev/null +++ b/shared/src/test/diff/tapl/Untyped.mls @@ -0,0 +1,644 @@ +: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] diff --git a/shared/src/test/diff/tricky/IrregularSubtypes.mls b/shared/src/test/diff/tricky/IrregularSubtypes.mls index e39dffd0a6..dfb1ad3daa 100644 --- a/shared/src/test/diff/tricky/IrregularSubtypes.mls +++ b/shared/src/test/diff/tricky/IrregularSubtypes.mls @@ -16,7 +16,7 @@ error: Foo[int]: Bar[int] //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: Bar[int] //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown // * Interestingly, this is caught by the cycle checker when rectypes are disabled: @@ -30,6 +30,6 @@ error: Foo[int]: Bar[int] //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: Bar[int] //│ Runtime error: -//│ Error: unexpected runtime error +//│ Error: an error was thrown diff --git a/shared/src/test/diff/tricky/Pottier.fun b/shared/src/test/diff/tricky/Pottier.fun index cc0bcee14f..31db27735a 100644 --- a/shared/src/test/diff/tricky/Pottier.fun +++ b/shared/src/test/diff/tricky/Pottier.fun @@ -26,12 +26,14 @@ let rec f = x => y => add (f x.tail y) (f y.tail x) //│ f: 'a -> 'a -> int //│ where //│ 'a <: {tail: 'a} -//│ f: 'a -> 'a -> int +//│ f: 'a -> 'b -> int //│ where -//│ 'a <: {tail: 'a} -//│ f: 'a -> 'a -> int +//│ 'a <: {tail: 'a} & 'b +//│ 'b <: {tail: 'a} +//│ f: 'a -> 'b -> int //│ where -//│ 'a <: {tail: 'a} +//│ 'a <: {tail: 'a} & 'b +//│ 'b <: {tail: 'a} let f = x => y => if true then { l: x; r: y } else { l: y; r: x } // 2-crown //│ f: 'a -> 'a -> {l: 'a, r: 'a} diff --git a/shared/src/test/diff/typegen/TypegenTerms.mls b/shared/src/test/diff/typegen/TypegenTerms.mls index 9fed47a1e3..e628ca0fc6 100644 --- a/shared/src/test/diff/typegen/TypegenTerms.mls +++ b/shared/src/test/diff/typegen/TypegenTerms.mls @@ -11,7 +11,7 @@ def z = x + y // common values //│ res: {a: "hello", b: "world"} //│ res: int -//│ res: {b: 'b} -> 'b | 1 +//│ res: 1 | {b: 'b} -> 'b //│ x: 1 //│ y: 2 //│ z: int @@ -19,7 +19,7 @@ def z = x + y //│ // start ts //│ export declare const res: {readonly a: "hello", readonly b: "world"} //│ export declare const res: int -//│ export declare const res: ((arg: {readonly b: b}) => b) | 1 +//│ export declare const res: 1 | ((arg: {readonly b: b}) => b) //│ export declare const x: 1 //│ export declare const y: 2 //│ export declare const z: int @@ -66,7 +66,7 @@ rec def l (a: int) = l rec def m (a: int) (b: int) = m def f: ('c -> 'a as 'a) -> 'c -> int // recursion type functions -//│ /!!!\ Uncaught error: mlscript.codegen.CodeGenError: Cannot generate type for `where` clause List((α94,Bounds(Function(Tuple(List((None,Field(None,TypeName(int))))),α94),Top))) List() +//│ /!!!\ Uncaught error: mlscript.codegen.CodeGenError: Cannot generate type for `where` clause List((α95,Bounds(Function(Tuple(List((None,Field(None,TypeName(int))))),α95),Top))) List() :ts :e @@ -152,6 +152,6 @@ def weird: ((int, int) -> 'a) as 'a def weird: ('a -> (int, int)) as 'a def weird: ((int, 'a) as 'a) -> int def weird: ((int, bool) | 'a) -> 'a -//│ /!!!\ Uncaught error: mlscript.codegen.CodeGenError: Cannot generate type for `where` clause List((α217,Bounds(Function(Tuple(List((None,Field(None,TypeName(int))), (None,Field(None,TypeName(int))))),α217),Top))) List() +//│ /!!!\ Uncaught error: mlscript.codegen.CodeGenError: Cannot generate type for `where` clause List((α218,Bounds(Function(Tuple(List((None,Field(None,TypeName(int))), (None,Field(None,TypeName(int))))),α218),Top))) List() diff --git a/shared/src/test/diff/typegen/TypegenTypedefs.mls b/shared/src/test/diff/typegen/TypegenTypedefs.mls index 6b4952c993..5e67baf638 100644 --- a/shared/src/test/diff/typegen/TypegenTypedefs.mls +++ b/shared/src/test/diff/typegen/TypegenTypedefs.mls @@ -29,6 +29,7 @@ class StackedRectangleBoxes[T, N]: RectangleBox[T] & { size: N } //│ // end ts +// FIXME :ts class Lock[T]: { pins: T } method Map: (T -> 'a) -> Lock['a] @@ -71,6 +72,7 @@ let lockA = Lock 20 in let lockB = Lock 30 in (Bank lockA 2000).Better(Bank lock //│ // end ts +// FIXME :ts class None: {} class Some[T]: { value: T } @@ -189,18 +191,18 @@ class Arg[T]: (T, T) class Prog[T] method Run: Arg[T] -> number //│ ╔══[ERROR] cannot inherit from a tuple type -//│ ║ l.188: class Arg[T]: (T, T) +//│ ║ l.190: class Arg[T]: (T, T) //│ ╙── ^^^^^^^^^^^^^^ //│ ╔══[ERROR] type identifier not found: Arg -//│ ║ l.190: method Run: Arg[T] -> number +//│ ║ l.192: method Run: Arg[T] -> number //│ ╙── ^^^^^^ //│ Defined class Prog[±T] //│ Declared Prog.Run: Prog[?] -> error -> number //│ ╔══[WARNING] Type definition Prog has bivariant type parameters: -//│ ║ l.189: class Prog[T] +//│ ║ l.191: class Prog[T] //│ ║ ^^^^ //│ ╟── T is irrelevant and may be removed -//│ ║ l.189: class Prog[T] +//│ ║ l.191: class Prog[T] //│ ╙── ^ //│ // start ts //│ export declare class Prog { diff --git a/shared/src/test/diff/ucs/AppSplits.mls b/shared/src/test/diff/ucs/AppSplits.mls new file mode 100644 index 0000000000..f6f81f1736 --- /dev/null +++ b/shared/src/test/diff/ucs/AppSplits.mls @@ -0,0 +1,76 @@ +:NewDefs + + +fun foo(x) = x > 1 +//│ fun foo: Num -> Bool + +:pe // TODO +:e +if foo of + 0 then "a" + 1 then "b" +//│ ╔══[PARSE ERROR] Unexpected 'then' keyword here +//│ ║ l.10: 0 then "a" +//│ ╙── ^^^^ +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application instead +//│ ║ l.9: if foo of +//│ ║ ^^^^^^ +//│ ║ l.10: 0 then "a" +//│ ║ ^^^^ +//│ ╟── 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 +//│ ║ ^^^^^^ +//│ ║ l.10: 0 then "a" +//│ ╙── ^^^^ +//│ error +//│ Code generation encountered an error: +//│ if expression was not desugared + +:pe // TODO +:e +if foo of 1, + 0 then "a" + 1 then "b" +//│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause +//│ ║ l.35: 0 then "a" +//│ ║ ^^^^^^^^^^ +//│ ║ l.36: 1 then "b" +//│ ╙── ^^^^^^^^^^^^ +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application instead +//│ ║ l.34: if foo of 1, +//│ ║ ^^^^^^^^^ +//│ ║ l.35: 0 then "a" +//│ ║ ^^ +//│ ╟── Note: 'if' expression starts here: +//│ ║ l.34: if foo of 1, +//│ ╙── ^^ +//│ ╔══[ERROR] The case when this is false is not handled: foo(1, undefined,) +//│ ║ l.34: if foo of 1, +//│ ║ ^^^^^^^^^ +//│ ║ l.35: 0 then "a" +//│ ╙── ^^ +//│ error +//│ Code generation encountered an error: +//│ if expression was not desugared + +:pe // TODO +:e +if foo + (0) then "a" + (1) then "b" +//│ ╔══[PARSE ERROR] Unexpected parenthesis section here +//│ ║ l.63: (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 + + diff --git a/shared/src/test/diff/ucs/CrossBranchCapture.mls b/shared/src/test/diff/ucs/CrossBranchCapture.mls new file mode 100644 index 0000000000..364aa0c6ed --- /dev/null +++ b/shared/src/test/diff/ucs/CrossBranchCapture.mls @@ -0,0 +1,60 @@ +:NewDefs + + +class Numb(n: Int) +//│ class Numb(n: Int) + + +// * FIXME should be rejected +fun process(e) = + if e is + Numb(n) and n > 0 then n + Numb(m) then n +//│ fun process: Numb -> Int + +process(Numb(-10)) +//│ Int +//│ res +//│ = -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, anything) -> Int + +process(Numb(0), 10) +//│ Int +//│ res +//│ = 0 + + +class Vec(xs: Array[Numb | Vec]) +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 +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]) + + +// * FIXME should warn, be rejected, or compare both values for equality +fun process(e) = + if e is + Pair(Numb(n), Numb(n)) then n +//│ fun process: Pair[Numb, Numb] -> Int + +process(Pair(Numb(1), Numb(2))) +//│ Int +//│ res +//│ = 2 + + diff --git a/shared/src/test/diff/ucs/DirectLines.mls b/shared/src/test/diff/ucs/DirectLines.mls index a9c25364bf..ccfa61f7d1 100644 --- a/shared/src/test/diff/ucs/DirectLines.mls +++ b/shared/src/test/diff/ucs/DirectLines.mls @@ -22,7 +22,7 @@ class None: Option //│ = [Function: None1] fun isValid(x) = if x then false else true -//│ isValid: anything -> bool +//│ isValid: anything -> Bool //│ = [Function: isValid] fun f(x, allowNone) = @@ -33,7 +33,6 @@ fun f(x, allowNone) = //│ f: (anything, anything,) -> ("bad" | "good" | "okay") //│ = [Function: f1] -:w fun f(x, y, z) = if x == 0 then "x" @@ -45,12 +44,6 @@ fun f(x, y, z) = _ then "bruh" 3 then "y = 3" _ then "bruh" -//│ ╔══[WARNING] Found a duplicated else branch -//│ ║ l.47: _ then "bruh" -//│ ║ ^^^^^^ -//│ ╟── The first else branch was declared here. -//│ ║ l.45: _ then "bruh" -//│ ╙── ^^^^^^ //│ f: (number, number, number,) -> ("bruh" | "x" | "y = 1" | "y = 3" | "z = 0" | "z = 9") //│ = [Function: f2] @@ -63,11 +56,8 @@ fun f(a, b) = 2 then 2 _ then 7 else 3 -//│ ╔══[WARNING] Found a duplicated else branch -//│ ║ l.65: else 3 -//│ ║ ^ -//│ ╟── The first else branch was declared here. -//│ ║ l.64: _ then 7 -//│ ╙── ^ +//│ ╔══[WARNING] Found a redundant else branch +//│ ║ l.58: 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 new file mode 100644 index 0000000000..5b8446a1f0 --- /dev/null +++ b/shared/src/test/diff/ucs/ElseIf.mls @@ -0,0 +1,109 @@ +:NewParser +:NewDefs + + + +fun f(x, y) = if x === + 0 then true + 1 then false + else if y === + 0 then true + 1 then false + else false +//│ fun f: (Eql[0 | 1], Eql[0 | 1]) -> Bool + +fun f(x, y) = if x === + 0 then true + 1 then false + else if y === + 0 then true + _ then false +//│ fun f: (Eql[0 | 1], Eql[0]) -> Bool + +module Tru +module Fals +//│ module Tru +//│ 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 +//│ ║ ^ +//│ ╟── [Missing Case 1/1] `Fals` +//│ ╟── It first appears here. +//│ ║ l.32: Fals and y is Fals then false +//│ ╙── ^^^^ +//│ fun f: (anything, anything) -> error +//│ Code generation encountered an error: +//│ if expression was not desugared + +// The base case. +fun f(x, y) = if x is + Tru and y is Tru then true + Fals and y is Fals then false + Tru and y is Fals then true + Fals and y is Tru then true +//│ fun f: (Fals | Tru, Fals | Tru) -> Bool + +// Replace the `x is Fals` with `_` +fun f(x, y) = if x is + Tru and y is Tru then true + Fals and y is Fals then false + _ and y is + Tru then true + Fals then false +//│ fun f: (Object, Fals | Tru) -> Bool + +f(Tru, Tru) +f(Tru, Fals) +f(Fals, Tru) +f(Fals, Fals) +//│ Bool +//│ res +//│ = true +//│ res +//│ = false +//│ res +//│ = true +//│ res +//│ = false + +// Test with real booleans +fun g(x, y) = if x is + true and y is true then true + false and y is false then false + _ and y is + true then true + false then false +//│ fun g: (Object, nothing) -> Bool + +// Chained UCS terms +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 then true + Fals then false +//│ fun f: (Object, Fals | Tru) -> Bool + +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 +//│ fun f: (Fals | Tru, Fals | Tru) -> Bool + +fun h(x, y, p) = if + x and p(x) then 0 + y is + Tru then 1 + Fals then 2 +//│ fun h: (Object, Fals | Tru, true -> Object) -> (0 | 1 | 2) diff --git a/shared/src/test/diff/ucs/ErrorMessage.mls b/shared/src/test/diff/ucs/ErrorMessage.mls index ae72c51784..aa40b49387 100644 --- a/shared/src/test/diff/ucs/ErrorMessage.mls +++ b/shared/src/test/diff/ucs/ErrorMessage.mls @@ -15,7 +15,7 @@ fun f(p) = //│ ╙── ^^^^^^^^^^^^^^ //│ f: anything -> error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared :e :ge @@ -27,4 +27,4 @@ fun g(xs) = //│ ╙── ^^ //│ g: anything -> error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared diff --git a/shared/src/test/diff/ucs/Exhaustiveness.mls b/shared/src/test/diff/ucs/Exhaustiveness.mls index c56f142300..eb2377c61d 100644 --- a/shared/src/test/diff/ucs/Exhaustiveness.mls +++ b/shared/src/test/diff/ucs/Exhaustiveness.mls @@ -1,15 +1,12 @@ -:NewParser +:NewDefs :NoJS class A() class B() class C() -//│ Defined class A -//│ Defined class B -//│ Defined class C -//│ A: () -> A -//│ B: () -> B -//│ C: () -> C +//│ class A() +//│ class B() +//│ class C() :e fun f(x, y) = @@ -23,11 +20,47 @@ fun f(x, y) = x is A then 4 //│ ╔══[ERROR] The match is not exhaustive. -//│ ║ l.23: x is +//│ ║ l.20: x is //│ ║ ^^^^ //│ ╟── The scrutinee at this position misses 2 cases. -//│ ║ l.23: x is +//│ ║ l.20: x is //│ ║ ^ //│ ╟── [Missing Case 1/2] `B` -//│ ╙── [Missing Case 2/2] `C` -//│ f: (anything, anything,) -> error +//│ ╟── It first appears here. +//│ ║ l.17: B then 1 +//│ ║ ^ +//│ ╟── [Missing Case 2/2] `C` +//│ ╟── It first appears here. +//│ ║ l.18: C then 2 +//│ ╙── ^ +//│ fun f: (anything, anything) -> error + +:e +// These operators are uninterpreted. So, it's impossible to reason the +// exhaustiveness without SMT solvers. +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) + == value then true +} +//│ ╔══[ERROR] The case when this is false is not handled: ==(wanted, value,) +//│ ║ l.46: fun contains(wanted) = if wanted +//│ ║ ^^^^^^ +//│ ║ l.47: <= value then left.find(wanted) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.48: >= value then right.find(wanted) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.49: == value then 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 +//│ } diff --git a/shared/src/test/diff/ucs/Humiliation.mls b/shared/src/test/diff/ucs/Humiliation.mls index 0ffb9e5e1c..878fb05372 100644 --- a/shared/src/test/diff/ucs/Humiliation.mls +++ b/shared/src/test/diff/ucs/Humiliation.mls @@ -11,7 +11,7 @@ if 1 is 1 then 1 else 0 //│ = 1 fun test(x) = if x is 1 then 0 else 1 -//│ test: number -> (0 | 1) +//│ test: anything -> (0 | 1) //│ = [Function: test] // It should report duplicated branches. @@ -19,8 +19,13 @@ 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] duplicated branch -//│ ╙── +//│ ╔══[WARNING] Found a duplicated branch +//│ ╟── This branch +//│ ║ l.21: Foo(a) then a +//│ ║ ^ +//│ ╟── is subsumed by the branch here. +//│ ║ l.20: Foo(a) then a +//│ ╙── ^ //│ testF: (Foo & {x: 'x}) -> 'x //│ = [Function: testF] @@ -47,7 +52,7 @@ fun f(x) = Pair(1, 1) then "ones" Pair(y, 1) then x _ then "nah" -//│ f: (Pair & {fst: number, snd: number} & 'a | ~Pair) -> ("nah" | "ones" | "zeros" | 'a) +//│ f: (Pair & 'a | ~Pair) -> ("nah" | "ones" | "zeros" | 'a) //│ = [Function: f] class Z() @@ -66,38 +71,38 @@ fun foo(x) = if x is Pair(Z(), Z()) then "zeros" Pair(O(), O()) then "ones" //│ ╔══[ERROR] The match is not exhaustive. -//│ ║ l.65: fun foo(x) = if x is +//│ ║ l.70: fun foo(x) = if x is //│ ║ ^^^^ //│ ╟── The scrutinee at this position misses 1 case. -//│ ║ l.66: Pair(Z(), Z()) then "zeros" +//│ ║ l.71: Pair(Z(), Z()) then "zeros" //│ ║ ^^^ //│ ╟── [Missing Case 1/1] `O` //│ ╟── It first appears here. -//│ ║ l.67: Pair(O(), O()) then "ones" +//│ ║ l.72: Pair(O(), O()) then "ones" //│ ╙── ^^^ //│ foo: anything -> error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared // Change `Pair` to a real pair. :e :ge fun foo(x) = if x is - (Z(), Z()) then "zeros" - (O(), O()) then "ones" + [Z(), Z()] then "zeros" + [O(), O()] then "ones" //│ ╔══[ERROR] The match is not exhaustive. -//│ ║ l.85: fun foo(x) = if x is +//│ ║ l.90: fun foo(x) = if x is //│ ║ ^^^^ //│ ╟── The scrutinee at this position misses 1 case. -//│ ║ l.86: (Z(), Z()) then "zeros" +//│ ║ l.91: [Z(), Z()] then "zeros" //│ ║ ^^^ //│ ╟── [Missing Case 1/1] `O` //│ ╟── It first appears here. -//│ ║ l.87: (O(), O()) then "ones" +//│ ║ l.92: [O(), O()] then "ones" //│ ╙── ^^^ //│ foo: anything -> error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared fun foo(x) = if x is Pair(a, b) then if a is @@ -146,11 +151,9 @@ fun foo(x) = if x is //│ foo: (Pair & {fst: O | Z, snd: S & {pred: 'pred} | ~S}) -> ("???" | "zeros" | 'pred) //│ = [Function: foo5] -:re foo(Pair(Z(), Z())) //│ res: "???" | "zeros" -//│ Runtime error: -//│ Error: non-exhaustive case expression +//│ = '???' :e :ge @@ -159,35 +162,26 @@ fun foo(x) = if x is Pair(O(), O()) then "ones" Pair(y, O()) then x //│ ╔══[ERROR] The match is not exhaustive. -//│ ║ l.157: fun foo(x) = if x is +//│ ║ l.160: fun foo(x) = if x is //│ ║ ^^^^ //│ ╟── The scrutinee at this position misses 1 case. -//│ ║ l.158: Pair(Z(), Z()) then "zeros" +//│ ║ l.161: Pair(Z(), Z()) then "zeros" //│ ║ ^^^ //│ ╟── [Missing Case 1/1] `Z` //│ ╟── It first appears here. -//│ ║ l.158: Pair(Z(), Z()) then "zeros" +//│ ║ l.161: Pair(Z(), Z()) then "zeros" //│ ╙── ^^^ //│ foo: anything -> error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared fun foo(x, y) = if x is Z() and y is O() then 0 else 1 //│ foo: (anything, anything,) -> (0 | 1) //│ = [Function: foo7] -:pe -fun foo(x, y) = if x is - Z() and y is O() then 0 else 1 -//│ ╔══[PARSE ERROR] Unexpected 'else' keyword here -//│ ║ l.181: Z() and y is O() then 0 else 1 -//│ ╙── ^^^^ -//│ foo: (Z, O,) -> 0 -//│ = [Function: foo8] - fun foo(x, y) = if x is Z() and y is O() then 0 else 1 //│ foo: (anything, anything,) -> (0 | 1) -//│ = [Function: foo9] +//│ = [Function: foo8] diff --git a/shared/src/test/diff/ucs/Hygiene.mls b/shared/src/test/diff/ucs/Hygiene.mls new file mode 100644 index 0000000000..10424de6ff --- /dev/null +++ b/shared/src/test/diff/ucs/Hygiene.mls @@ -0,0 +1,24 @@ +:NewDefs + +class Some[out T](value: T) +class Left[out T](value: T) +class Right[out T](value: T) +//│ class Some[T](value: T) +//│ class Left[T](value: T) +//│ class Right[T](value: T) + +// FIXME unhygienic, the `x` in the second branch shadows parameter `x` +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 + +foo(Some(Left(1))) +//│ Left[1] +//│ res +//│ = Left {} + +foo(Some(2)) +//│ 2 +//│ res +//│ = 2 diff --git a/shared/src/test/diff/ucs/HygienicBindings.mls b/shared/src/test/diff/ucs/HygienicBindings.mls new file mode 100644 index 0000000000..5e5d577f9c --- /dev/null +++ b/shared/src/test/diff/ucs/HygienicBindings.mls @@ -0,0 +1,164 @@ +:NewDefs + +type Option[out T] = None | Some[T] +module None +class Some[out T](val value: T) +//│ type Option[T] = None | Some[T] +//│ module None +//│ class Some[T](value: T) + +type Either[A, B] = Left[A] | Right[B] +class Left[A](val leftValue: A) +class Right[B](val rightValue: B) +//│ type Either[A, B] = Left[A] | Right[B] +//│ class Left[A](leftValue: A) +//│ class Right[B](rightValue: B) + +type List[out A] = Nil | Cons[A] +module Nil +class Cons[out A](head: A, tail: List[A]) +//│ type List[A] = Cons[A] | Nil +//│ module Nil +//│ class Cons[A](head: A, tail: List[A]) + +fun h0(a) = + if + a is Some(Left(y)) then y + a is Some(Right(z)) then z + 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. +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) + +// 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 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))) +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ╟── application of type `Left[?A]` is not an instance of type `Right` +//│ ║ l.69: h1'(Some(Left(0))) +//│ ║ ^^^^^^^ +//│ ╟── Note: constraint arises from class pattern: +//│ ║ l.52: Right then +//│ ║ ^^^^^ +//│ ╟── from field selection: +//│ ║ l.45: let y = a.value +//│ ║ ^^^^^^^ +//│ ╟── Note: type parameter T is defined at: +//│ ║ l.5: class Some[out T](val value: T) +//│ ╙── ^ +//│ 0 +//│ res +//│ = 0 +//│ res +//│ = 0 +//│ res +//│ = 0 + +// FIXME: Precise scrutinee identification (hard) +fun h2(a) = + if + a is Some(x) and x is x' and x' is Left(y) then y + a is Some(y) and + 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 + +// FIXME: Some results are wrong. +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. (None | Object & 'a & ~#None, 'b, (None | 'a) -> anything, (None | 'a) -> Object) -> ("anyway" | 'b) +//│ "anything" | "anyway" +//│ res +//│ = 'not me' +//│ res +//│ = 'should be me' +//│ res +//│ = 'anyway' + +// FIXME: Some results are wrong. +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! +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" +//│ res +//│ = 'not me' +//│ res +//│ = 'not me' +//│ res +//│ = 'should be me' +//│ res +//│ = 'default' diff --git a/shared/src/test/diff/ucs/InterleavedLet.mls b/shared/src/test/diff/ucs/InterleavedLet.mls index 113e351a77..77b2d6ba51 100644 --- a/shared/src/test/diff/ucs/InterleavedLet.mls +++ b/shared/src/test/diff/ucs/InterleavedLet.mls @@ -39,15 +39,19 @@ fun q(x) = //│ q: Some -> 0 //│ = [Function: q] -// FIXME :w 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] duplicated branch -//│ ╙── +//│ ╔══[WARNING] Found a duplicated branch +//│ ╟── This branch +//│ ║ l.47: 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 +//│ ╙── ^ //│ p: (Some, None | Some,) -> (0 | 1) //│ = [Function: p] @@ -88,9 +92,9 @@ fun q(a) = let y = a + 1 then y //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead -//│ ║ l.88: let y = a + 1 +//│ ║ l.92: let y = a + 1 //│ ║ ^^^^^ -//│ ║ l.89: then y +//│ ║ l.93: then y //│ ╙── ^^^^^^^^^^ //│ q: (Left & {leftValue: 'leftValue}) -> 'leftValue //│ = [Function: q1] @@ -223,13 +227,16 @@ fun showList(xs) = //│ })()); //│ }; //│ // End of generated code -//│ showList: 'tail -> string +//│ showList: (Cons & 'a | Nil) -> string //│ where -//│ 'tail <: Cons & {tail: 'tail} | Nil +//│ '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}}}} +//│ 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] } } @@ -239,7 +246,6 @@ showList(zeroToThree) //│ res: string //│ = '0, 1, 2, 3' -// FIXME: This needs lifting functions. fun mapPartition(f, xs) = if xs is Nil then Pair(Nil(), Nil()) @@ -249,28 +255,99 @@ fun mapPartition(f, xs) = let r = res.snd Left(v) then Pair(Cons(v, l), r) Right(v) then Pair(l, Cons(v, r)) -//│ mapPartition: ('head -> (Left & {leftValue: 'leftValue} | Right & {rightValue: 'rightValue}), 'tail,) -> (Pair & {fst: 'fst, snd: 'tail0}) +//│ 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 -//│ 'tail0 :> Cons & {head: 'rightValue, tail: 'tail0} | Nil -//│ 'fst :> Nil | Cons & {head: 'leftValue, tail: 'fst} -//│ 'tail <: Cons & {head: 'head, tail: 'tail} | Nil +//│ '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] -// FIXME: Something wrong with code generation. -mapPartition(x => if x % 2 == 0 then Left(x) else Right(x), zeroToThree) -//│ res: Pair & {fst: 'fst, snd: 'snd} -//│ where -//│ 'snd :> Cons & {head: 0 | 1 | 2 | 3, tail: 'snd} | Nil -//│ 'fst :> Nil | Cons & {head: 0 | 1 | 2 | 3, tail: 'fst} -//│ Runtime error: -//│ RangeError: Maximum call stack size exceeded +mapPartition(x => (if x % 2 == 0 then Left(x) else Right(x)), zeroToThree) +//│ 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 {} } } +//│ } + +// This should be the desugaring of the above: +fun mapPartition2(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)) +//│ 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) +//│ 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] fun mn(a) = if a is Some(x) and x is - Left(a) then "left-defined" - let y = x + 1 + Left(b) and b is + 0 then "b is 1" + let _ = log(b) + 1 then "b is 2" + 2 then "b is 3" Right(b) then "right-defined" None then "undefined" -//│ mn: (None | Some & {value: nothing}) -> ("left-defined" | "right-defined" | "undefined") +//│ 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' diff --git a/shared/src/test/diff/ucs/JSON.mls b/shared/src/test/diff/ucs/JSON.mls new file mode 100644 index 0000000000..889ff5a296 --- /dev/null +++ b/shared/src/test/diff/ucs/JSON.mls @@ -0,0 +1,319 @@ +: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 new file mode 100644 index 0000000000..2303e4ebbe --- /dev/null +++ b/shared/src/test/diff/ucs/LeadingAnd.mls @@ -0,0 +1,54 @@ +:NewDefs + + + +class Some[T](value: T) +//│ 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(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 + + + +// FIXME (parser) +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 + diff --git a/shared/src/test/diff/ucs/LitUCS.mls b/shared/src/test/diff/ucs/LitUCS.mls new file mode 100644 index 0000000000..6c77687f0f --- /dev/null +++ b/shared/src/test/diff/ucs/LitUCS.mls @@ -0,0 +1,67 @@ +:NewDefs + +module A +//│ module A + +// This one is easy to fix but what about the next one? +// The following example can better reveal the essence of the problem. +fun test(x: 0 | A) = if x is + 0 then 0 + A then A +//│ fun test: (x: 0 | A) -> (0 | A) + +:e +// case === (x,) (0,) of { true => 0; _ => case x of { A => A } } +fun test(x: 0 | A) = + if + x === 0 then 0 + x is A then A +//│ ╔══[ERROR] Module 'A' does not support equality comparison because it does not have a parameter list +//│ ║ l.17: x === 0 then 0 +//│ ╙── ^^^^^^^ +//│ ╔══[ERROR] Type mismatch in `case` expression: +//│ ║ l.18: x is A then A +//│ ║ ^^^^^^^^^^^^^ +//│ ╟── type `0` is not an instance of type `A` +//│ ║ l.15: fun test(x: 0 | A) = +//│ ║ ^ +//│ ╟── but it flows into reference with expected type `A` +//│ ║ l.18: x is A then A +//│ ║ ^ +//│ ╟── Note: constraint arises from class pattern: +//│ ║ l.18: x is A then A +//│ ╙── ^ +//│ fun test: (x: 0 | A) -> (0 | A) + +fun test2(x) = + if + x === 0 then 0 + x is A then A +//│ fun test2: (A & Eql[0]) -> (0 | A) + +:e +test2(0) +//│ ╔══[ERROR] Type mismatch in application: +//│ ║ l.43: test2(0) +//│ ║ ^^^^^^^^ +//│ ╟── integer literal of type `0` is not an instance of type `A` +//│ ║ l.43: test2(0) +//│ ║ ^ +//│ ╟── Note: constraint arises from class pattern: +//│ ║ l.39: x is A then A +//│ ║ ^ +//│ ╟── from reference: +//│ ║ l.39: x is A then A +//│ ╙── ^ +//│ 0 | A | error +//│ res +//│ = 0 + +:e +test2(A) +//│ ╔══[ERROR] Module 'A' does not support equality comparison because it does not have a parameter list +//│ ║ l.61: test2(A) +//│ ╙── ^^^^^^^^ +//│ 0 | A | error +//│ res +//│ = A { class: [class A] } diff --git a/shared/src/test/diff/ucs/MultiwayIf.mls b/shared/src/test/diff/ucs/MultiwayIf.mls new file mode 100644 index 0000000000..6add8f28e5 --- /dev/null +++ b/shared/src/test/diff/ucs/MultiwayIf.mls @@ -0,0 +1,59 @@ +:NewDefs + + +fun f(x) = + if + x > 0 then 0 + x == 0 then 1 + _ then 2 +//│ fun f: Num -> (0 | 1 | 2) + + +fun f(x) = + if + x > 0 and + x % 2 === 0 then true + _ then false + x == 0 then true + _ then false +//│ fun f: Int -> Bool + +f(0) +f(2) +f(3) +f(0 - 1) +f(0 - 2) +//│ Bool +//│ res +//│ = true +//│ res +//│ = true +//│ res +//│ = false +//│ res +//│ = false +//│ res +//│ = false + +fun f(x) = + if + x > 0 and + x % 2 === 0 then true + else false + x == 0 then true + else false +//│ fun f: Int -> Bool + +f(0) +f(2) +f(1) +f(0 - 1) +//│ Bool +//│ res +//│ = true +//│ res +//│ = true +//│ res +//│ = false +//│ res +//│ = false diff --git a/shared/src/test/diff/ucs/NestedBranches.mls b/shared/src/test/diff/ucs/NestedBranches.mls index 332947089f..7aa2059ba1 100644 --- a/shared/src/test/diff/ucs/NestedBranches.mls +++ b/shared/src/test/diff/ucs/NestedBranches.mls @@ -1,88 +1,74 @@ :NewParser +:NewDefs -class Option -class Some(value): Option -class None: Option -class Either -class Left(leftValue): Either -class Right(rightValue): Either -class List -class Nil: List -class Cons(head, tail): List -class Pair(fst, snd) -//│ Defined class Option -//│ Defined class Some -//│ Defined class None -//│ Defined class Either -//│ Defined class Left -//│ Defined class Right -//│ Defined class List -//│ Defined class Nil -//│ Defined class Cons -//│ Defined class Pair -//│ 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] -//│ List: () -> List -//│ = [Function: List1] -//│ Nil: () -> Nil -//│ = [Function: Nil1] -//│ Cons: ('head, 'tail,) -> (Cons & {head: 'head, tail: 'tail}) -//│ = [Function: Cons1] -//│ Pair: ('fst, 'snd,) -> (Pair & {fst: 'fst, snd: 'snd}) -//│ = [Function: Pair1] - +class Some[out A](val value: A) +module None +class Left[out A](val leftValue: A) +class Right[out A](val rightValue: A) +module Nil +class Cons[out A](val head: A, val tail: Cons[A] | Nil) +class Pair[out A, out B](val fst: A, val snd: B) +//│ class Some[A](value: A) +//│ module None +//│ class Left[A](leftValue: A) +//│ class Right[A](rightValue: A) +//│ module Nil +//│ class Cons[A](head: A, tail: Cons[A] | Nil) +//│ class Pair[A, B](fst: A, snd: B) fun optionApply(x, y, f) = if x is Some(xv) and y is Some(yv) then Some(f(xv, yv)) - None() then None() - None() then None() -//│ optionApply: (None | Some & {value: 'value}, None | Some & {value: 'value0}, ('value, 'value0,) -> 'value1,) -> (None | Some & {value: 'value1}) -//│ = [Function: optionApply] + None then None + 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)))) +//│ let zeroToThree: Cons[0 | 1 | 2 | 3] +//│ zeroToThree +//│ = Cons {} +fun f(x) = if x % 2 == 0 then Left(x) else Right(x) +//│ fun f: forall 'A. (Int & 'A) -> (Left['A] | Right['A]) fun mapPartition(f, xs) = if xs is - Nil then Pair(Nil(), Nil()) + 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)) -//│ mapPartition: ('head -> (Left & {leftValue: 'leftValue} | Right & {rightValue: 'rightValue}), 'tail,) -> (Pair & {fst: 'fst, snd: 'tail0}) -//│ where -//│ 'tail0 :> Cons & {head: 'rightValue, tail: 'tail0} | Nil -//│ 'fst :> Nil | Cons & {head: 'leftValue, tail: 'fst} -//│ 'tail <: Cons & {head: 'head, tail: 'tail} | Nil -//│ = [Function: mapPartition] +//│ fun mapPartition: forall 'a 'A 'A0. ('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, Cons[nothing] | Nil] +//│ res +//│ = Pair {} + +mapPartition(f, zeroToThree) +//│ Pair[Cons[0 | 1 | 2 | 3] | Nil, Cons[0 | 1 | 2 | 3] | Nil] +//│ res +//│ = Pair {} + fun mapPartition(f, xs) = if xs is - Nil then Pair(Nil(), Nil()) + 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)) -//│ mapPartition: ('head -> (Left & {leftValue: 'leftValue} | Right & {rightValue: 'rightValue}), 'tail,) -> (Pair & {fst: 'fst, snd: 'tail0}) -//│ where -//│ 'tail0 :> Cons & {head: 'rightValue, tail: 'tail0} | Nil -//│ 'fst :> Nil | Cons & {head: 'leftValue, tail: 'fst} -//│ 'tail <: Cons & {head: 'head, tail: 'tail} | Nil -//│ = [Function: mapPartition1] +//│ fun mapPartition: forall 'a 'A 'A0. ('a -> (Left['A] | Right['A0]), Cons['a] | Nil) -> Pair[Cons['A] | Nil, Cons['A0] | Nil] + +mapPartition(f, zeroToThree) +//│ Pair[Cons[0 | 1 | 2 | 3] | Nil, Cons[0 | 1 | 2 | 3] | Nil] +//│ res +//│ = Pair {} + fun mapPartition(f, xs) = if xs is Nil then - Pair(Nil(), Nil()) + Pair(Nil, Nil) Cons(x, xs) and mapPartition(f, xs) is Pair(l, r) and @@ -91,27 +77,55 @@ fun mapPartition(f, xs) = if xs is Pair(Cons(v, l), r) Right(v) then Pair(l, Cons(v, r)) -//│ mapPartition: ('head -> (Left & {leftValue: 'leftValue} | Right & {rightValue: 'rightValue}), 'tail,) -> (Pair & {fst: 'fst, snd: 'tail0}) -//│ where -//│ 'tail0 :> Cons & {head: 'rightValue, tail: 'tail0} | Nil -//│ 'fst :> Nil | Cons & {head: 'leftValue, tail: 'fst} -//│ 'tail <: Cons & {head: 'head, tail: 'tail} | Nil -//│ = [Function: mapPartition2] +//│ fun mapPartition: forall 'a 'A 'A0. ('a -> (Left['A] | Right['A0]), Cons['a] | Nil) -> Pair[Cons['A] | Nil, Cons['A0] | Nil] +mapPartition(f, zeroToThree) +//│ Pair[Cons[0 | 1 | 2 | 3] | Nil, Cons[0 | 1 | 2 | 3] | Nil] +//│ res +//│ = Pair {} -// TODO make this one work (needs tuple support) +: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] The case when this is false is not handled: is (mapPartition (f, xs,),) (l, r,) -//│ ║ l.105: Cons(x, xs) and mapPartition(f, xs) is (l, r) and f(x) is -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ mapPartition: (anything, anything,) -> error + 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] 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]) //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ unknown match case: Tuple#2 +:re // TODO +mapPartition(f, zeroToThree) +//│ error | [Nil, Nil] +//│ res +//│ Runtime error: +//│ ReferenceError: mapPartition3 is not defined // * Vertical alignment is not allowed! (good) @@ -120,24 +134,45 @@ fun mapPartition(f, xs) = if xs is :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)) + 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.126: Right(v) then (l, Cons(v, r)) +//│ ║ l.140: Right(v) then [l, Cons(v, r)] //│ ╙── ^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword -//│ ║ l.125: and f(x) is Left(v) then (Cons(v, l), r) +//│ ║ l.139: and f(x) is Left(v) then [Cons(v, l), r] //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.126: Right(v) then (l, Cons(v, r)) +//│ ║ l.140: Right(v) then [l, Cons(v, r)] //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] type identifier not found: Tuple#2 //│ ╙── -//│ mapPartition: (anything, 'tail,) -> ((Nil, Nil,) | error) -//│ where -//│ 'tail <: Cons & {tail: 'tail} | Nil +//│ ╔══[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)] +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── 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 - diff --git a/shared/src/test/diff/ucs/NestedOpSplits.mls b/shared/src/test/diff/ucs/NestedOpSplits.mls new file mode 100644 index 0000000000..e4432e6817 --- /dev/null +++ b/shared/src/test/diff/ucs/NestedOpSplits.mls @@ -0,0 +1,26 @@ +:NewDefs + + +// * Note that this always to the left +:e +fun f(x) = + if x == + 1 + + 2 then 0 + _ then 1 +//│ ╔══[ERROR] Type mismatch in operator application: +//│ ║ l.7: if x == +//│ ║ ^^^^ +//│ ║ l.8: 1 + +//│ ║ ^^^^^^^ +//│ ║ l.9: 2 then 0 +//│ ║ ^^^^^^^ +//│ ╟── operator application of type `Bool` is not an instance of type `Int` +//│ ║ l.7: if x == +//│ ║ ^^^^ +//│ ║ l.8: 1 + +//│ ╙── ^^^^^^ +//│ fun f: Num -> (0 | 1) + + + diff --git a/shared/src/test/diff/ucs/NestedPattern.mls b/shared/src/test/diff/ucs/NestedPattern.mls index 4696505990..c292de5570 100644 --- a/shared/src/test/diff/ucs/NestedPattern.mls +++ b/shared/src/test/diff/ucs/NestedPattern.mls @@ -41,8 +41,8 @@ fun crazy(v) = :e fun f(x) = if x is - (0, 0) then "zeros" - (1, 1) then "ones" + [0, 0] then "zeros" + [1, 1] then "ones" _ then "bruh" //│ ╔══[ERROR] type identifier not found: Tuple#2 //│ ╙── @@ -51,9 +51,9 @@ fun f(x) = :e fun f(x) = if x is - (0, 0) then "zeros" - (1, 1) then "ones" - (y, 1) then x + [0, 0] then "zeros" + [1, 1] then "ones" + [y, 1] then x _ then "que?" //│ ╔══[ERROR] type identifier not found: Tuple#2 //│ ╙── @@ -62,9 +62,22 @@ fun f(x) = :e fun f(p) = if p is - Some((x, y)) then x + y + Some([x, y]) then x + y None() then 0 //│ ╔══[ERROR] type identifier not found: Tuple#2 //│ ╙── //│ f: (None | Some) -> (0 | error) +class Union(a, b) +//│ Defined class Union +//│ Union: ('a, 'b,) -> (Union & {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 + +hmm(Union(1, 2)) +//│ res: 1 + diff --git a/shared/src/test/diff/ucs/NuPlainConditionals.mls b/shared/src/test/diff/ucs/NuPlainConditionals.mls new file mode 100644 index 0000000000..328fbf5c57 --- /dev/null +++ b/shared/src/test/diff/ucs/NuPlainConditionals.mls @@ -0,0 +1,103 @@ +:NewDefs + + +class Pair[A](fst: A, snd: A) +//│ class Pair[A](fst: A, snd: A) + + + +Pair(0, 1) is Pair +//│ Bool +//│ res +//│ = true + +Pair(0, 1) is Pair(a, b) +//│ Bool +//│ res +//│ = true + +Pair(0, 1) is Pair(0, _) +//│ Bool +//│ res +//│ = true + +if Pair(0, 1) is Pair(a, b) then true else false +//│ Bool +//│ res +//│ = true + + +fun foo(x) = x is Pair(a, b) +//│ fun foo: (Object & ~#Pair | Pair[anything]) -> Bool + + +Pair(0, 1) is Pair(a, b) and a > b +//│ Bool +//│ res +//│ = false + +if Pair(0, 1) is Pair(a, b) then a > b else false +//│ Bool +//│ res +//│ = false + + +fun foo(x) = x is Pair(a, b) and a > b +//│ fun foo: (Object & ~#Pair | Pair[Num]) -> Bool + +fun foo(x) = if x is Pair(a, b) then a > b else false +//│ fun foo: (Object & ~#Pair | Pair[Num]) -> Bool + + +// TODO proper error +fun foo(x) = x is + Pair + Int +//│ ╔══[ERROR] illegal pattern +//│ ║ l.54: Pair +//│ ║ ^^^^ +//│ ║ l.55: Int +//│ ╙── ^^^^^ +//│ fun foo: anything -> error +//│ Code generation encountered an error: +//│ if expression was not desugared + +// 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 +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.68: Int +//│ ╙── ^^^^^ +//│ fun foo: anything -> error +//│ Code generation encountered an error: +//│ if expression was not desugared + +// 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] Illegal pattern `|` +//│ ║ l.80: fun foo2(x) = x is (Pair(a, b) and a > b) | Int +//│ ╙── ^ +//│ fun foo1: anything -> error +//│ fun foo2: anything -> error +//│ Code generation encountered an error: +//│ if expression was not desugared + + + +class A(arg: Int) +//│ class A(arg: Int) + +// TODO make `is` lower precedence than `=>` +x => (x is A(_)) +//│ Object -> Bool +//│ res +//│ = [Function: res] + + diff --git a/shared/src/test/diff/ucs/Or.mls b/shared/src/test/diff/ucs/Or.mls new file mode 100644 index 0000000000..066a382e2b --- /dev/null +++ b/shared/src/test/diff/ucs/Or.mls @@ -0,0 +1,21 @@ +:NewDefs + + +class Some[T](value: T) +//│ class Some[T](value: T) + + +// TODO support `or` in UCS +fun f(a, b) = if a is + Some(v) + 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 + + diff --git a/shared/src/test/diff/ucs/OverlappedBranches.mls b/shared/src/test/diff/ucs/OverlappedBranches.mls new file mode 100644 index 0000000000..1bac22866b --- /dev/null +++ b/shared/src/test/diff/ucs/OverlappedBranches.mls @@ -0,0 +1,100 @@ +:NewParser + +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] + +// The very basic case. +:w +fun f1(x) = if x is + Base then "b" + Derived1 then "d1" + 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] + +f1(Base()) +f1(Derived1()) +f1(Derived2()) +//│ res: "b" +//│ = 'b' +//│ res: "b" +//│ = 'b' +//│ res: "b" +//│ = 'b' + +// Decision paths: +// + «x is Base» and «p (x,)» => "b and p" +// + «x is Derived1» => "d1" +// + «x is Derived2» => "d2" +// + => "otherwise" +// The case tree: +// «x» match +// case Base => +// if «p (x,)» +// «"b and p"» +// else +// «x» match +// case Derived1 => +// «"d1"» +// case Derived2 => +// «"d2"» +// default +// «"otherwise"» +// default +// «"otherwise"» +fun f2(x, p) = if x is + Base and p(x) then "b and p" + Derived1 then "d1" + Derived2 then "d2" + else "otherwise" +//│ f2: (Base & 'a | ~Base, 'a -> anything,) -> ("b and p" | "d1" | "d2" | "otherwise") +//│ = [Function: f2] + +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' + +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' + +f2(Derived1(), _ => false) // => d1 +f2(Derived2(), _ => false) // => d2 +//│ res: "b and p" | "d1" | "d2" | "otherwise" +//│ = 'd1' +//│ res: "b and p" | "d1" | "d2" | "otherwise" +//│ = 'd2' diff --git a/shared/src/test/diff/ucs/ParseFailures.mls b/shared/src/test/diff/ucs/ParseFailures.mls new file mode 100644 index 0000000000..7843c93586 --- /dev/null +++ b/shared/src/test/diff/ucs/ParseFailures.mls @@ -0,0 +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 + +// 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 + +// FIXME +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 +//│ ╙── ^^^^ +//│ ╔══[ERROR] Illegal pattern `Z` +//│ ║ l.32: Z() and y is O() then 0 else 1 +//│ ╙── ^ +//│ fun foo: (anything, anything) -> error diff --git a/shared/src/test/diff/nu/ParserFailures.mls b/shared/src/test/diff/ucs/ParserFailures.mls similarity index 97% rename from shared/src/test/diff/nu/ParserFailures.mls rename to shared/src/test/diff/ucs/ParserFailures.mls index 61e2ff5a33..fca068c589 100644 --- a/shared/src/test/diff/nu/ParserFailures.mls +++ b/shared/src/test/diff/ucs/ParserFailures.mls @@ -1,4 +1,4 @@ -:NewParser +:NewDefs :NoJS // FIXME: Interleaved let bindings are not implemented in `IfOpsApp`. @@ -15,3 +15,4 @@ fun tt(x) = let y = 0 is B() then "B" //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing + diff --git a/shared/src/test/diff/ucs/PlainConditionals.mls b/shared/src/test/diff/ucs/PlainConditionals.mls index a8bfb4348b..96a98bb53e 100644 --- a/shared/src/test/diff/ucs/PlainConditionals.mls +++ b/shared/src/test/diff/ucs/PlainConditionals.mls @@ -8,17 +8,25 @@ class Pair(fst, snd) +Pair(0, 1) is Pair +//│ res: Bool +//│ = true + Pair(0, 1) is Pair(a, b) -//│ res: bool +//│ res: Bool +//│ = true + +Pair(0, 1) is Pair(0, _) +//│ res: Bool //│ = true if Pair(0, 1) is Pair(a, b) then true else false -//│ res: bool +//│ res: Bool //│ = true fun foo(x) = x is Pair(a, b) -//│ foo: anything -> bool +//│ foo: anything -> Bool //│ = [Function: foo] @@ -40,4 +48,56 @@ fun foo(x) = if x is Pair(a, b) then a > b else false //│ = [Function: foo2] +// TODO proper error +fun foo(x) = x is + Pair + Int +//│ ╔══[ERROR] illegal pattern +//│ ║ l.53: Pair +//│ ║ ^^^^ +//│ ║ l.54: Int +//│ ╙── ^^^^^ +//│ foo: anything -> error +//│ Code generation encountered an error: +//│ if expression was not desugared + +// 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 +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.67: Int +//│ ╙── ^^^^^ +//│ foo: anything -> error +//│ Code generation encountered an error: +//│ if expression was not desugared + +// 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 +//│ ╙── ^ +//│ foo: anything -> error +//│ ╔══[ERROR] Cannot find operator `|` in the context +//│ ║ l.79: fun foo(x) = x is (Pair(a, b) and a > b) | Int +//│ ╙── ^ +//│ foo: anything -> error +//│ Code generation encountered an error: +//│ if expression was not desugared + + + +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] + diff --git a/shared/src/test/diff/ucs/SimpleUCS.mls b/shared/src/test/diff/ucs/SimpleUCS.mls index db955ca7f0..d74a1b06eb 100644 --- a/shared/src/test/diff/ucs/SimpleUCS.mls +++ b/shared/src/test/diff/ucs/SimpleUCS.mls @@ -49,7 +49,7 @@ fun f(x, y) = //│ ╙── ^^^^^^^^^ //│ f: (anything, anything,) -> error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared fun f(x, y) = if x is @@ -130,7 +130,7 @@ fun f(x, y) = //│ ╙── ^^^^^^ //│ f: (anything, anything,) -> error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared :e :ge @@ -153,7 +153,7 @@ fun f(x, y) = //│ ╙── ^^^^^^ //│ f: (anything, anything,) -> error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared fun f(x, y) = if x is @@ -179,7 +179,7 @@ fun f(x, y) = > 0 then "gt" < 0 then "le" == 0 then "eq" -//│ ╔══[ERROR] The case when this is false is not handled: == (y,) (0,) +//│ ╔══[ERROR] The case when this is false is not handled: ==(y,)(0,) //│ ║ l.178: Some(x) and y //│ ║ ^ //│ ║ l.179: > 0 then "gt" @@ -190,10 +190,10 @@ fun f(x, y) = //│ ╙── ^^^^^^^^^^ //│ f: (anything, anything,) -> error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared fun isValid(x) = if x then false else true -//│ isValid: anything -> bool +//│ isValid: anything -> Bool //│ = [Function: isValid] fun f(x, allowNone) = @@ -210,7 +210,7 @@ fun f(x) = Some(x) then "roll" _ and x == 0 then 0 _ then "rock" -//│ f: (None | number | Some) -> ("bruh" | "rock" | "roll" | 0) +//│ f: (None | Some | number) -> ("bruh" | "rock" | "roll" | 0) //│ = [Function: f13] fun f(x, a, b) = @@ -297,7 +297,6 @@ fun g(a, b) = //│ g: (int, None | Some & {value: int},) -> int //│ = [Function: g1] -// TODO: Fix the NaN. g(5, None()) g(5, Some(7)) g(0 - 5, None()) @@ -307,7 +306,7 @@ g(0 - 5, Some(9)) //│ res: int //│ = 35 //│ res: int -//│ = NaN +//│ = 25 //│ res: int //│ = 4 @@ -315,7 +314,7 @@ class Var(name) class ValBase class IntVal(value): ValBase class BoolVal(value): ValBase -class Lit(val) +class Lit(value) //│ Defined class Var //│ Defined class ValBase //│ Defined class IntVal @@ -329,7 +328,7 @@ class Lit(val) //│ = [Function: IntVal1] //│ BoolVal: 'value -> (BoolVal & {value: 'value}) //│ = [Function: BoolVal1] -//│ Lit: 'val -> (Lit & {val: 'val}) +//│ Lit: 'value -> (Lit & {value: 'value}) //│ = [Function: Lit1] fun p(e, context) = @@ -339,7 +338,9 @@ fun p(e, context) = Some(BoolVal(v)) then Right(v) Lit(IntVal(v)) then Left(v) Lit(BoolVal(v)) then Right(v) -//│ p: (Lit & {val: 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}) +//│ 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] class Nil() @@ -355,11 +356,11 @@ fun f(x) = 0 :: Nil() then "oh" //│ ╔══[ERROR] Cannot find operator `::` in the context -//│ ║ l.355: 0 :: +//│ ║ l.356: 0 :: //│ ╙── ^^ //│ f: anything -> error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared fun f(x) = if x == 0 and x is diff --git a/shared/src/test/diff/ucs/SplitAfterOp.mls b/shared/src/test/diff/ucs/SplitAfterOp.mls index 37baeee25a..09e828989c 100644 --- a/shared/src/test/diff/ucs/SplitAfterOp.mls +++ b/shared/src/test/diff/ucs/SplitAfterOp.mls @@ -5,44 +5,46 @@ fun f(x, b) = if x == 0 and b then 0 -//│ ╔══[ERROR] The case when this is false is not handled: b +//│ ╔══[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 has not been desugared +//│ if expression was not desugared :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.17: if x == y + +//│ ╔══[ERROR] The case when this is false is not handled: +(==(x,)(y,),)(7,) +//│ ║ l.19: if x == y + //│ ║ ^^^^^^^^ -//│ ║ l.18: 5 then 0 +//│ ║ l.20: 5 then 0 //│ ║ ^^^^^^^^^^ -//│ ║ l.19: 7 then 0 +//│ ║ l.21: 7 then 0 //│ ╙── ^^^^ //│ res: error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ 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.33: if x == y * +//│ ╔══[ERROR] The case when this is false is not handled: *(==(x,)(y,),)(+(6,)(7,),) +//│ ║ l.35: if x == y * //│ ║ ^^^^^^^^ -//│ ║ l.34: 5 then 0 +//│ ║ l.36: 5 then 0 //│ ║ ^^^^^^^^^^ -//│ ║ l.35: 6 + 7 then 0 +//│ ║ l.37: 6 + 7 then 0 //│ ╙── ^^^^^^^ //│ res: error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared :e :ge @@ -50,29 +52,31 @@ if x == y + 5 then 0 7 then 0 -//│ ╔══[ERROR] The case when this is false is not handled: + (== (x,) (y,),) (7,) -//│ ║ l.49: if x == +//│ ╔══[ERROR] The case when this is false is not handled: +(==(x,)(y,),)(7,) +//│ ║ l.51: if x == //│ ║ ^^^^ -//│ ║ l.50: y + +//│ ║ l.52: y + //│ ║ ^^^^^ -//│ ║ l.51: 5 then 0 +//│ ║ l.53: 5 then 0 //│ ║ ^^^^^^^^^^^^ -//│ ║ l.52: 7 then 0 +//│ ║ l.54: 7 then 0 //│ ╙── ^^^^^ //│ res: error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared :e :ge if x == 1 and b then 0 -//│ ╔══[ERROR] The case when this is false is not handled: b -//│ ║ l.69: 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 has not been desugared +//│ if expression was not desugared :e @@ -81,16 +85,16 @@ fun toEnglish(x) = if x == true then "t" 0 then "z" -//│ ╔══[ERROR] The case when this is false is not handled: == (x,) (0,) -//│ ║ l.81: if x == +//│ ╔══[ERROR] The case when this is false is not handled: ==(x,)(0,) +//│ ║ l.85: if x == //│ ║ ^^^^ -//│ ║ l.82: true then "t" +//│ ║ l.86: true then "t" //│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.83: 0 then "z" +//│ ║ l.87: 0 then "z" //│ ╙── ^^^^^^ //│ toEnglish: anything -> error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared :e :ge @@ -98,16 +102,16 @@ fun toEnglish(x) = if x == 0 then "z" true then "t" -//│ ╔══[ERROR] The case when this is false is not handled: == (x,) (true,) -//│ ║ l.98: if x == -//│ ║ ^^^^ -//│ ║ l.99: 0 then "z" -//│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.100: true then "t" +//│ ╔══[ERROR] The case when this is false is not handled: ==(x,)(true,) +//│ ║ l.102: if x == +//│ ║ ^^^^ +//│ ║ l.103: 0 then "z" +//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.104: true then "t" //│ ╙── ^^^^^^^^ //│ toEnglish: anything -> error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared :e :ge @@ -115,16 +119,16 @@ fun toEnglish(x) = if x == 1 then "o" 0 then "z" -//│ ╔══[ERROR] The case when this is false is not handled: == (x,) (0,) -//│ ║ l.115: if x == +//│ ╔══[ERROR] The case when this is false is not handled: ==(x,)(0,) +//│ ║ l.119: if x == //│ ║ ^^^^ -//│ ║ l.116: 1 then "o" +//│ ║ l.120: 1 then "o" //│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.117: 0 then "z" +//│ ║ l.121: 0 then "z" //│ ╙── ^^^^^^ //│ toEnglish: anything -> error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared fun toEnglish(x) = if x == @@ -140,25 +144,25 @@ fun toEnglish(x) = if x == else 1 //│ ╔══[PARSE ERROR] Unexpected indented block in expression position -//│ ║ l.141: else 1 +//│ ║ l.145: else 1 //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here -//│ ║ l.141: else 1 +//│ ║ l.145: else 1 //│ ╙── ^ -//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause; found operator application instead -//│ ║ l.140: if x == +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead +//│ ║ l.144: if x == //│ ║ ^^^^ -//│ ║ l.141: else 1 +//│ ║ l.145: else 1 //│ ║ ^^^^ -//│ ╟── Note: 'if' expression started here: -//│ ║ l.140: if x == +//│ ╟── Note: 'if' expression starts here: +//│ ║ l.144: if x == //│ ╙── ^^ -//│ ╔══[ERROR] The case when this is false is not handled: == (x,) (undefined,) -//│ ║ l.140: if x == +//│ ╔══[ERROR] The case when this is false is not handled: ==(x,)(undefined,) +//│ ║ l.144: if x == //│ ║ ^^^^ -//│ ║ l.141: else 1 +//│ ║ l.145: else 1 //│ ╙── ^^^^ //│ toEnglish: anything -> error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared diff --git a/shared/src/test/diff/ucs/SplitAnd.mls b/shared/src/test/diff/ucs/SplitAnd.mls index 5f0dd20e6c..3a3c4705d0 100644 --- a/shared/src/test/diff/ucs/SplitAnd.mls +++ b/shared/src/test/diff/ucs/SplitAnd.mls @@ -26,12 +26,12 @@ fun f(x) = B() then "B" x == 0 then "lol" else "bruh" -//│ ╔══[ERROR] The case when this is false is not handled: == (x,) (0,) +//│ ╔══[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 has not been desugared +//│ if expression was not desugared :e :ge @@ -40,9 +40,9 @@ fun f(x, y) = x == 0 and y == 0 then "bruh" else "lol" -//│ ╔══[ERROR] The case when this is false is not handled: == (x,) (0,) +//│ ╔══[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 has not been desugared +//│ if expression was not desugared diff --git a/shared/src/test/diff/ucs/SplitAroundOp.mls b/shared/src/test/diff/ucs/SplitAroundOp.mls index dc2d1be5b0..198590fa4d 100644 --- a/shared/src/test/diff/ucs/SplitAroundOp.mls +++ b/shared/src/test/diff/ucs/SplitAroundOp.mls @@ -1,119 +1,49 @@ :NewParser +:NewDefs -// Why? Can the type of `x` be `number | string`? -:e fun f(x, b) = if x - == + === 0 and b then "n0" 1 and b then "n1" 2 then "n2" - == + === "0" then "s0" "1" then "s1" "2" then "s2" else ":p" -//│ ╔══[ERROR] Type mismatch in operator application: -//│ ║ l.6: if x -//│ ║ ^ -//│ ║ l.7: == -//│ ║ ^^^^^^ -//│ ║ l.8: 0 and b then "n0" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.9: 1 and b then "n1" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.10: 2 then "n2" -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.11: == -//│ ║ ^^^^^^ -//│ ║ l.12: "0" then "s0" -//│ ║ ^^^^^^^^^ -//│ ╟── string literal of type `"0"` is not an instance of type `number` -//│ ║ l.12: "0" then "s0" -//│ ╙── ^^^ -//│ ╔══[ERROR] Type mismatch in operator application: -//│ ║ l.6: if x -//│ ║ ^ -//│ ║ l.7: == -//│ ║ ^^^^^^ -//│ ║ l.8: 0 and b then "n0" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.9: 1 and b then "n1" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.10: 2 then "n2" -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.11: == -//│ ║ ^^^^^^ -//│ ║ l.12: "0" then "s0" -//│ ║ ^^^^^^^^^^^^^^^^^^^ -//│ ║ l.13: "1" then "s1" -//│ ║ ^^^^^^^^^ -//│ ╟── string literal of type `"1"` is not an instance of type `number` -//│ ║ l.13: "1" then "s1" -//│ ╙── ^^^ -//│ ╔══[ERROR] Type mismatch in operator application: -//│ ║ l.6: if x -//│ ║ ^ -//│ ║ l.7: == -//│ ║ ^^^^^^ -//│ ║ l.8: 0 and b then "n0" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.9: 1 and b then "n1" -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.10: 2 then "n2" -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.11: == -//│ ║ ^^^^^^ -//│ ║ l.12: "0" then "s0" -//│ ║ ^^^^^^^^^^^^^^^^^^^ -//│ ║ l.13: "1" then "s1" -//│ ║ ^^^^^^^^^^^^^^^^^^^ -//│ ║ l.14: "2" then "s2" -//│ ║ ^^^^^^^^^ -//│ ╟── string literal of type `"2"` is not an instance of type `number` -//│ ║ l.14: "2" then "s2" -//│ ╙── ^^^ -//│ f: (number, anything,) -> (":p" | "n0" | "n1" | "n2" | "s0" | "s1" | "s2") -//│ = [Function: f] +//│ fun f: (Eql["0" | "1" | "2" | 0 | 1 | 2], Object) -> (":p" | "n0" | "n1" | "n2" | "s0" | "s1" | "s2") fun f(x, y, a, b) = - if x == 0 + if x === 0 and - y == 0 then "x, y" - a == 0 then "x, a" - b == 0 then "x, b" + y === 0 then "x, y" + a === 0 then "x, a" + b === 0 then "x, b" else "nah" -//│ f: (number, number, number, number,) -> ("nah" | "x, a" | "x, b" | "x, y") -//│ = [Function: f1] +//│ fun f: (Eql[0], Eql[0], Eql[0], Eql[0]) -> ("nah" | "x, a" | "x, b" | "x, 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(x) = if x is A() then 0 B() then 1 -//│ f: (A | B) -> (0 | 1) -//│ = [Function: f2] +//│ fun f: (A | B) -> (0 | 1) -// It fails because we interpret == as a constructor. -:e -:ge +:e // FIXME if x is A() - == 0 then 0 + === 0 then 0 > 0 then 1 < 0 then 2 -//│ ╔══[ERROR] Cannot find operator `==` in the context -//│ ║ l.111: == 0 then 0 -//│ ╙── ^^ -//│ res: error +//│ ╔══[ERROR] Illegal pattern `===` +//│ ║ l.41: === 0 then 0 +//│ ╙── ^^^ +//│ error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared diff --git a/shared/src/test/diff/ucs/SplitBeforeOp.mls b/shared/src/test/diff/ucs/SplitBeforeOp.mls index 07b7e340a2..6f07068743 100644 --- a/shared/src/test/diff/ucs/SplitBeforeOp.mls +++ b/shared/src/test/diff/ucs/SplitBeforeOp.mls @@ -1,29 +1,42 @@ -:NewParser +:NewDefs :e :ge if x == 0 then 0 -//│ ╔══[ERROR] The case when this is false is not handled: == (x,) (0,) +//│ ╔══[ERROR] The case when this is false is not handled: ==(x, 0,) //│ ║ l.5: if x //│ ║ ^ //│ ║ l.6: == 0 then 0 //│ ╙── ^^^^^^ -//│ res: error +//│ error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared :e :ge if x is A and y then 0 -//│ ╔══[ERROR] Cannot find the constructor `A` in the context +//│ ╔══[ERROR] Cannot find constructor `A` in scope //│ ║ l.19: is A and //│ ╙── ^ -//│ res: error +//│ error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared + +:e +:ge +if x + is A and + y then 0 +else 1 +//│ ╔══[ERROR] Cannot find constructor `A` in scope +//│ ║ l.31: is A and +//│ ╙── ^ +//│ error +//│ Code generation encountered an error: +//│ if expression was not desugared :e :ge @@ -32,9 +45,9 @@ if x is A() then "A" B() then "B" -//│ ╔══[ERROR] Cannot find class `A` in the context -//│ ║ l.33: A() then "A" +//│ ╔══[ERROR] Illegal pattern `A` +//│ ║ l.46: A() then "A" //│ ╙── ^ -//│ res: error +//│ error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared diff --git a/shared/src/test/diff/ucs/SplitOps.mls b/shared/src/test/diff/ucs/SplitOps.mls index 7db2e834ca..c966e4b0a1 100644 --- a/shared/src/test/diff/ucs/SplitOps.mls +++ b/shared/src/test/diff/ucs/SplitOps.mls @@ -33,7 +33,7 @@ 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,) +//│ ╔══[ERROR] The case when this is false is not handled: <>(x,)(undefined,) //│ ║ l.32: if x //│ ║ ^ //│ ║ l.33: is Left(v) then 0 @@ -44,7 +44,7 @@ fun f(x) = //│ ╙── ^^^^^^^^^^^^^^^^ //│ f: anything -> error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared :e :ge @@ -64,7 +64,7 @@ fun f(x) = //│ ╙── ^^^^^^ //│ f: anything -> error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared class A() class B() @@ -86,7 +86,6 @@ class C() //│ C: () -> C //│ = [Function: C1] -// * FIXME: the missing otherwise is for `a == 0` :p :e :ge @@ -94,15 +93,18 @@ 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|←|←| -//│ 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) -//│ ╔══[ERROR] The case when this is false is not handled: and (is (b,) (B (),),) (is (c,) (C (),),) -//│ ║ l.95: == 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 has not been desugared +//│ if expression was not desugared fun f(x) = if x diff --git a/shared/src/test/diff/ucs/ThenIndent.mls b/shared/src/test/diff/ucs/ThenIndent.mls new file mode 100644 index 0000000000..1ca90defc9 --- /dev/null +++ b/shared/src/test/diff/ucs/ThenIndent.mls @@ -0,0 +1,31 @@ +:NewDefs + + +// FIXME +x => if x == + 0 + then "a" + _ then "b" +//│ ╔══[PARSE ERROR] Unexpected indented block here +//│ ║ l.7: then "a" +//│ ║ ^^^^^^^^^^^^ +//│ ║ l.8: _ then "b" +//│ ╙── ^^ +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead +//│ ║ l.5: x => if x == +//│ ║ ^^^^ +//│ ║ l.6: 0 +//│ ║ ^^^ +//│ ╟── 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 == +//│ ║ ^^^^ +//│ ║ l.6: 0 +//│ ╙── ^^^ +//│ anything -> error +//│ Code generation encountered an error: +//│ if expression was not desugared + + diff --git a/shared/src/test/diff/ucs/Tree.mls b/shared/src/test/diff/ucs/Tree.mls new file mode 100644 index 0000000000..2802614176 --- /dev/null +++ b/shared/src/test/diff/ucs/Tree.mls @@ -0,0 +1,42 @@ +:NewDefs + +type Option[out A] = Some[A] | None +class Some[out A](value: A) +module None +//│ type Option[A] = None | Some[A] +//│ class Some[A](value: A) +//│ module None + +type Tree[out A] = Node[A] | Empty +module Empty +class Node[out A](value: A, left: Tree[A], right: Tree[A]) +//│ type Tree[A] = Empty | Node[A] +//│ module Empty +//│ class Node[A](value: A, left: Tree[A], right: Tree[A]) + +fun find(t, v) = if t is + Node(v', l, r) and + v < v' then find(l, v) + v > v' then find(r, v) + _ then Some(v) + Empty then None +//│ fun find: forall 'A. (Empty | Node[Num], Num & 'A) -> (None | Some['A]) + +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]) + +find(Empty, 0) +find(Node(0, Empty, Empty), 0) +find(Node(1, Empty, Empty), 0) +//│ None | Some[0] +//│ res +//│ = None { class: [class None] } +//│ res +//│ = Some {} +//│ res +//│ = None { class: [class None] } diff --git a/shared/src/test/diff/ucs/TrivialIf.mls b/shared/src/test/diff/ucs/TrivialIf.mls index 1d0f979145..0fa422ee8e 100644 --- a/shared/src/test/diff/ucs/TrivialIf.mls +++ b/shared/src/test/diff/ucs/TrivialIf.mls @@ -43,19 +43,19 @@ map(None(), inc) :e fun f(a, b) = if a and b then 0 -//│ ╔══[ERROR] The case when this is false is not handled: b +//│ ╔══[ERROR] The case when this is false is not handled: a //│ ║ l.45: fun f(a, b) = if a and b then 0 -//│ ╙── ^ +//│ ╙── ^ //│ f: (anything, anything,) -> error :e fun f(x, y) = if x == y + 5 then 0 else if x == y + 7 then 0 -//│ ╔══[ERROR] The case when this is false is not handled: == (x,) (+ (y,) (7,),) +//│ ╔══[ERROR] The case when this is false is not handled: ==(x,)(+(y,)(7,),) //│ ║ l.54: else if x == y + 7 then 0 //│ ╙── ^^^^^^^^^^ -//│ f: (number, int,) -> (0 | error) +//│ f: (anything, anything,) -> error // TODO support fun foo(x) = if x is Some @@ -64,7 +64,7 @@ fun foo(x) = if x is Some //│ ╔══[PARSE ERROR] Unexpected parenthesis section here //│ ║ l.63: (1) then 1 //│ ╙── ^^^ -//│ ╔══[ERROR] The case when this is false is not handled: is (x,) (Some,) (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.62: (0) then 0 @@ -78,16 +78,13 @@ fun foo(x) = if x is Some of //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here //│ ║ l.76: 0 then 0 //│ ╙── ^^^^ -//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause; found operator application instead +//│ ╔══[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 //│ ║ ^^^ -//│ ╟── Note: 'if' expression started here: +//│ ╟── Note: 'if' expression starts here: //│ ║ l.75: fun foo(x) = if x is Some of //│ ╙── ^^ -//│ ╔══[ERROR] The case when this is false is not handled: == (tmp0,) (0,) -//│ ║ l.76: 0 then 0 -//│ ╙── ^ -//│ foo: anything -> error +//│ foo: (Some & {value: 0}) -> undefined diff --git a/shared/src/test/diff/ucs/WeirdIf.mls b/shared/src/test/diff/ucs/WeirdIf.mls index 46e7bd48df..5f4d44439e 100644 --- a/shared/src/test/diff/ucs/WeirdIf.mls +++ b/shared/src/test/diff/ucs/WeirdIf.mls @@ -1,4 +1,4 @@ -:NewParser +:NewDefs // Should report duplicated else branches. :w @@ -6,93 +6,108 @@ if _ then 0 else 0 else 1 -//│ ╔══[WARNING] duplicated branch -//│ ╙── -//│ ╔══[WARNING] duplicated branch -//│ ╙── -//│ res: 0 -//│ = 0 +//│ ╔══[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 if else 0 else 1 -//│ ╔══[WARNING] duplicated branch -//│ ╙── -//│ res: 0 -//│ = 0 +//│ ╔══[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 fun f(x) = if x is else 0 else 1 -//│ ╔══[WARNING] duplicated branch -//│ ╙── -//│ f: anything -> 0 -//│ = [Function: f] +//│ ╔══[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 -//│ f: anything -> 0 -//│ = [Function: f1] +//│ fun f: anything -> 0 :e :ge if true then 0 //│ ╔══[ERROR] The case when this is false is not handled: true -//│ ║ l.36: if true +//│ ║ l.56: if true //│ ╙── ^^^^ -//│ res: error +//│ error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared // This cannot be parsed. But the next one works. :pe :e :ge fun f(x) = - if x == + if x === else "bruh" //│ ╔══[PARSE ERROR] Unexpected indented block in expression position -//│ ║ l.51: else "bruh" +//│ ║ l.71: else "bruh" //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here -//│ ║ l.51: else "bruh" +//│ ║ l.71: else "bruh" //│ ╙── ^ -//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause; found operator application instead -//│ ║ l.50: if x == -//│ ║ ^^^^ -//│ ║ l.51: else "bruh" +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead +//│ ║ l.70: if x === +//│ ║ ^^^^^ +//│ ║ l.71: else "bruh" //│ ║ ^^^^ -//│ ╟── Note: 'if' expression started here: -//│ ║ l.50: if x == +//│ ╟── Note: 'if' expression starts here: +//│ ║ l.70: if x === //│ ╙── ^^ -//│ ╔══[ERROR] The case when this is false is not handled: == (x,) (undefined,) -//│ ║ l.50: if x == -//│ ║ ^^^^ -//│ ║ l.51: else "bruh" +//│ ╔══[ERROR] The case when this is false is not handled: ===(x, undefined,) +//│ ║ l.70: if x === +//│ ║ ^^^^^ +//│ ║ l.71: else "bruh" //│ ╙── ^^^^ -//│ f: anything -> error +//│ fun f: anything -> error //│ Code generation encountered an error: -//│ if expression has not been desugared +//│ if expression was not desugared // But this works. fun f(x) = - if x == + if x === _ then "bruh" -//│ f: anything -> "bruh" -//│ = [Function: f3] +//│ fun f: anything -> "bruh" -:e -:ge -// Hmmmmmm, this one is valid but how to get it work? fun boolToStr(x) = if x is true then "yah" false then "nah" -//│ ╔══[ERROR] The case when this is false is not handled: == (x,) (false,) -//│ ║ l.86: if x is -//│ ║ ^^^^ -//│ ║ l.87: true then "yah" -//│ ║ ^^^^^^^^^^^^^^^^^^^ -//│ ║ l.88: false then "nah" -//│ ╙── ^^^^^^^^^ -//│ boolToStr: anything -> error -//│ Code generation encountered an error: -//│ if expression has not been desugared +//│ fun boolToStr: nothing -> ("nah" | "yah") + +boolToStr of true +boolToStr of false +//│ "nah" | "yah" +//│ res +//│ = 'yah' +//│ res +//│ = 'nah' diff --git a/shared/src/test/diff/ucs/Wildcard.mls b/shared/src/test/diff/ucs/Wildcard.mls new file mode 100644 index 0000000000..51cdbda6f4 --- /dev/null +++ b/shared/src/test/diff/ucs/Wildcard.mls @@ -0,0 +1,261 @@ +:NewParser +:NewDefs + +type Option[T] = None | Some[T] +module None +class Some[T](val value: T) +//│ type Option[T] = None | Some[T] +//│ module None +//│ class Some[T](value: T) + +type Either[A, B] = Left[A] | Right[B] +class Left[A](val leftValue: A) +class Right[B](val rightValue: B) +//│ type Either[A, B] = Left[A] | Right[B] +//│ class Left[A](leftValue: A) +//│ class Right[B](rightValue: B) + +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)) + _ 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 + +w1(Left(None), "a", "b") +w1(Right(None), "a", "b") +w1(Left(Some(0)), "a", "b") +w1(Right(Some(0)), "a", "b") +//│ Str +//│ res +//│ = 'Left of None' +//│ res +//│ = 'Right of None' +//│ res +//│ = 'Left of Some of 0' +//│ res +//│ = 'Right of Some of 0' + +fun w2(x, p) = + if x is + Some then 1 + _ 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) + +w2(Some(0), x => true) +w2(None, x => true) +w2(None, x => false) +w2(0, x => false) +//│ 1 | 2 | 3 | 4 +//│ res +//│ = 1 +//│ res +//│ = 2 +//│ res +//│ = 3 +//│ res +//│ = 4 + +fun w3(x, p) = if x is + _ and p(x) then "r1" + 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 + +// Expect "r1" +w3(0, _ => true) +w3(None, _ => true) +w3(Some(0), _ => true) +//│ Str +//│ res +//│ = 'r1' +//│ res +//│ = 'r1' +//│ res +//│ = 'r1' + +// Expect "r2" +w3(Some(0), _ => false) +//│ Str +//│ res +//│ = 'r2: 0' + +// Expect "r3" +w3(None, _ => false) +//│ Str +//│ res +//│ = 'r3' + +// Expect "r4" +w3(0, _ => false) +//│ Str +//│ res +//│ = 'r4' + +:w +// 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) +w3_1(0, _ => false) +//│ 0 +//│ res +//│ = 0 +//│ res +//│ = 0 + +:w +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) +w3_1_1(0, x => x + 1) +//│ Int +//│ res +//│ = 0 +//│ res +//│ = 1 + +// Decision paths: +// + «a = x» and «p (x,)» => "r1" +// + «x is Some» => concat ("r2: ",) (toString (xv,),) +// + «x is None» => "r3" +fun w4(x, p) = if x is + a and p(x) then "r1" + 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 + + +// Expect "r1" +w4(0, _ => true) +w4(None, _ => true) +w4(Some(0), _ => true) +//│ Str +//│ res +//│ = 'r1' +//│ res +//│ = 'r1' +//│ res +//│ = 'r1' + +// Expect "r2" +w4(Some(0), _ => false) +//│ Str +//│ res +//│ = 'r2: 0' + +// Expect "r3" +w4(None, _ => false) +//│ Str +//│ res +//│ = 'r3' + +// Expect "r4" +w4(0, _ => false) +//│ Str +//│ res +//│ = 'r4' + +class Alpha() +class Beta() +class Gamma() +class Delta() +//│ class Alpha() +//│ class Beta() +//│ class Gamma() +//│ class Delta() + +// This should generate only one case expression instead of a chain of case +// expressions. DO check the desugared term! +fun w5(y) = + if y is + Alpha then "alpha" + _ and y is + Beta then "beta" + _ and y is + Gamma then "gamma" + _ and y is + Delta then "delta" + _ then "unknown" +//│ fun w5: Object -> ("alpha" | "beta" | "delta" | "gamma" | "unknown") + +w5(0) +w5(Alpha()) +w5(Beta()) +w5(Gamma()) +w5(Delta()) +//│ "alpha" | "beta" | "delta" | "gamma" | "unknown" +//│ res +//│ = 'unknown' +//│ res +//│ = 'alpha' +//│ res +//│ = 'beta' +//│ res +//│ = 'gamma' +//│ res +//│ = 'delta' + +fun w6(x, y) = + if x is + _ and y is + Some(z) then z + None then 0 + else x +//│ fun w6: forall 'a. ('a, Object & ~#Some | Some['a]) -> (0 | 'a) + +w6("42", Some(42)) +w6("42", None) +w6("42", "42") +//│ "42" | 0 +//│ res +//│ = 42 +//│ res +//│ = 0 +//│ res +//│ = '42' + +// FIXME +// Should report warnings. +fun w7(x, f) = + if x is + _ and f(x) is + Some(v) then v + 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) + +// 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 +//│ res +//│ = 100 +//│ res +//│ = 100 +//│ res +//│ = 101 +//│ res +//│ = 101 diff --git a/shared/src/test/diff/ucs/zipWith.mls b/shared/src/test/diff/ucs/zipWith.mls new file mode 100644 index 0000000000..a92fef3877 --- /dev/null +++ b/shared/src/test/diff/ucs/zipWith.mls @@ -0,0 +1,203 @@ +:NewDefs + + + +:escape +let nothing: nothing +//│ let nothing: nothing +//│ nothing +//│ = + +module None { + fun value = nothing +} +class Some[out A](val value: A) +//│ module None { +//│ fun value: nothing +//│ } +//│ class Some[A](value: A) + +type List[out A] = Cons[A] | Nil +module Nil { + fun toArray = [] +} +class Cons[out A](val head: A, val tail: List[A]) { + fun toArray: Array[anything] + fun toArray = [head, tail.toArray] +} +//│ type List[A] = Cons[A] | Nil +//│ module Nil { +//│ fun toArray: [] +//│ } +//│ class Cons[A](head: A, tail: List[A]) { +//│ fun toArray: Array[anything] +//│ } + +fun pairup(x, y) = [x, y] +//│ fun pairup: forall 'a 'b. ('a, 'b) -> ['a, 'b] + + + +// FIXME parsing +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 +//│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application followed by newline instead +//│ ║ l.43: if xs is Cons(x, xs) +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.44: and ys is Cons(y, ys) +//│ ║ ^^ +//│ ╟── Note: 'if' expression starts here: +//│ ║ l.43: if xs is Cons(x, xs) +//│ ╙── ^^ +//│ ╔══[PARSE ERROR] Unexpected operator in expression position +//│ ║ l.44: and ys is Cons(y, ys) +//│ ╙── ^^^ +//│ ╔══[PARSE ERROR] Unexpected operator in expression position +//│ ║ l.45: and zipWith_wrong(f, xs, ys) is Some(tail) +//│ ╙── ^^^ +//│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead +//│ ║ l.43: if xs is Cons(x, xs) +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.44: and ys is Cons(y, ys) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.45: and zipWith_wrong(f, xs, ys) is Some(tail) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.46: then Some(Cons(f(x, y), tail)) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.47: else None +//│ ╙── ^^^^^^^^^^^ +//│ fun zipWith_wrong: (anything, anything, anything) -> () + + +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 +//│ 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(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 +//│ fun zipWith_wrong: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (None | Some[Cons['A]]) + +// * Notice the result is wrong (duh) +zipWith_wrong(pairup, Nil, Nil) +//│ None | Some[Cons[[nothing, nothing]]] +//│ res +//│ = None { class: [class None] } + + + +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 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (None | Some[Cons['A] | Nil]) + +zipWith(pairup, Cons(0, Nil), Cons("0", Nil)).value.toArray +//│ Array[anything] +//│ res +//│ = [ [ 0, '0' ], [] ] + + +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 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (None | Some[Cons['A] | Nil]) + +zipWith(pairup, Cons(0, Nil), Cons("0", Nil)).value.toArray +//│ Array[anything] +//│ res +//│ = [ [ 0, '0' ], [] ] + + +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)) + else if xs is Nil and ys is Nil then Some(Nil) + else None +//│ fun zipWith: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (None | Some[Cons['A] | Nil]) + +zipWith(pairup, Cons(0, Nil), Cons("0", Nil)).value.toArray +//│ Array[anything] +//│ res +//│ = [ [ 0, '0' ], [] ] + + +fun zipWith(f, xs, ys) = + if xs is Cons(x, xs) and ys is Cons(y, ys) then + if zipWith(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) + else None + else if xs is Nil and ys is Nil then Some(Nil) + else None +//│ fun zipWith: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (None | Some[Cons['A] | Nil]) + +zipWith(pairup, Cons(0, Nil), Cons("0", Nil)).value.toArray +//│ Array[anything] +//│ res +//│ = [ [ 0, '0' ], [] ] + + +fun zipWith(f, xs, ys) = + if xs is + Cons(x, xs) then + if ys is + Cons(y, ys) then + if zipWith(f, xs, ys) is + Some(tail) then Some(Cons(f(x, y), tail)) + None then None + 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]) + +zipWith(pairup, Nil, Nil).value.toArray +//│ Array[anything] +//│ res +//│ = [] + +:re +zipWith(pairup, Nil, Cons(0, Nil)).value.toArray +//│ Array[anything] +//│ res +//│ Runtime error: +//│ ReferenceError: nothing is not defined + +zipWith(pairup, Cons(0, Nil), Cons("0", Nil)).value.toArray +//│ Array[anything] +//│ res +//│ = [ [ 0, '0' ], [] ] + +let ls = zipWith(pairup, Cons(0, Cons(1, Nil)), Cons("0", Cons("1", Nil))) +ls.value.toArray +//│ let ls: None | Some[Cons[[0 | 1, "0" | "1"]] | Nil] +//│ Array[anything] +//│ ls +//│ = Some {} +//│ res +//│ = [ [ 0, '0' ], [ [ 1, '1' ], [] ] ] + + + +fun zipWith_wrong2(f, xs, ys) = + if xs is Cons(x, xs) and ys is Cons(y, ys) and zipWith_wrong2(f, xs, ys) is Some(tail) then Cons(Some(f(x, y)), tail) + else if xs is Nil and ys is Nil then Some(Nil) + else None +//│ fun zipWith_wrong2: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (Cons[Some['A]] | None | Some[Nil]) + +// * No type error! The definition and use are well-typed... +zipWith_wrong2(pairup, Cons(0, Cons(1, Nil)), Cons("0", Cons("1", Nil))) +//│ Cons[Some[[0 | 1, "0" | "1"]]] | None | Some[Nil] +//│ res +//│ = None { class: [class None] } + + diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index e9e6552201..9906ed260b 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -39,6 +39,7 @@ abstract class ModeType { def expectCodeGenErrors: Bool def showRepl: Bool def allowEscape: Bool + def mono: Bool } class DiffTests @@ -52,6 +53,7 @@ class DiffTests def postProcess(mode: ModeType, basePath: Ls[Str], testName: Str, unit: TypingUnit): Ls[Str] = Nil + @SuppressWarnings(Array("org.wartremover.warts.RedundantIsInstanceOf")) private val inParallel = isInstanceOf[ParallelTestExecution] import DiffTests._ @@ -121,17 +123,21 @@ class DiffTests str.splitSane('\n').foreach(l => out.println(outputMarker + l)) def outputSourceCode(code: SourceCode) = code.lines.foreach{line => out.println(outputMarker + line.toString())} val allStatements = mutable.Buffer.empty[DesugaredStatement] - val typer = new Typer(dbg = false, verbose = false, explainErrors = false) { - override def recordTypeVars = occursCheck - override def funkyTuples = file.ext =:= "fun" - // override def emitDbg(str: String): Unit = if (stdout) System.out.println(str) else output(str) - override def emitDbg(str: String): Unit = output(str) - } - var ctx: typer.Ctx = typer.Ctx.init + var newDefs = false + trait MyTyper extends Typer { var ctx: Ctx } + lazy val typer = + new Typer(dbg = false, verbose = false, explainErrors = false, newDefs = newDefs) with MyTyper { + var ctx: Ctx = Ctx.init + override def recordTypeVars = occursCheck + override def funkyTuples = file.ext =:= "fun" + // override def emitDbg(str: String): Unit = if (stdout) System.out.println(str) else output(str) + override def emitDbg(str: String): Unit = output(str) + createdTypeVars.clear() + } + def ctx = typer.ctx var declared: Map[Str, typer.ST] = Map.empty val failures = mutable.Buffer.empty[Int] val unmergedChanges = mutable.Buffer.empty[Int] - typer.createdTypeVars.clear() case class Mode( expectTypeErrors: Bool = false, @@ -159,13 +165,14 @@ class DiffTests expectCodeGenErrors: Bool = false, showRepl: Bool = false, allowEscape: Bool = false, + mono: Bool = false, // noProvs: Bool = false, ) extends ModeType { def isDebugging: Bool = dbg || dbgSimplif } val defaultMode = Mode() - var parseOnly = basePath.headOption.contains("parser") || basePath.headOption.contains("compiler") + var parseOnly = basePath.headOption.contains("parser") var allowTypeErrors = false var allowParseErrors = false var showRelativeLineNums = false @@ -188,7 +195,9 @@ class DiffTests var newParser = basePath.headOption.contains("parser") || basePath.headOption.contains("compiler") - val backend = new JSTestBackend() + val backend = new JSTestBackend { + def oldDefs = !newDefs + } val host = ReplHost() val codeGenTestHelpers = new CodeGenTestHelpers(file, output) @@ -219,6 +228,7 @@ class DiffTests 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 "NoProvs" => noProvs = true; mode case "GeneralizeCurriedFunctions" => generalizeCurriedFunctions = true; mode @@ -253,8 +263,9 @@ class DiffTests case "dv" => mode.copy(debugVariance = true) case "ge" => mode.copy(expectCodeGenErrors = true) case "re" => mode.copy(expectRuntimeErrors = true) - case "ShowRepl" => mode.copy(showRepl = true) + case "r" | "showRepl" => mode.copy(showRepl = true) case "escape" => mode.copy(allowEscape = true) + case "mono" => {mode.copy(mono = true)} case "exit" => out.println(exitMarker) ls.dropWhile(_ =:= exitMarker).tails.foreach { @@ -321,7 +332,7 @@ class DiffTests // report errors and warnings def report(diags: Ls[mlscript.Diagnostic], output: Str => Unit = output): Unit = { diags.foreach { diag => - val sctx = Message.mkCtx(diag.allMsgs.iterator.map(_._1), "?") + val sctx = Message.mkCtx(diag.allMsgs.iterator.map(_._1), newDefs, "?") val headStr = diag match { case ErrorReport(msg, loco, src) => src match { @@ -383,24 +394,26 @@ class DiffTests if (!mode.fixme) { if (!allowTypeErrors && !mode.expectTypeErrors && diag.isInstanceOf[ErrorReport] && diag.source =:= Diagnostic.Typing) - failures += globalLineNum + { output("TEST CASE FAILURE: There was an unexpected type error"); failures += globalLineNum } if (!allowParseErrors && !mode.expectParseErrors && diag.isInstanceOf[ErrorReport] && (diag.source =:= Diagnostic.Lexing || diag.source =:= Diagnostic.Parsing)) - failures += globalLineNum + { output("TEST CASE FAILURE: There was an unexpected parse error"); failures += globalLineNum } if (!allowTypeErrors && !allowParseErrors && !mode.expectWarnings && diag.isInstanceOf[WarningReport]) - failures += globalLineNum + { output("TEST CASE FAILURE: There was an unexpected warning"); failures += globalLineNum } } () } } - val raise: typer.Raise = d => report(d :: Nil) + val raise: Raise = d => report(d :: Nil) + + val legacyParser = file.ext =:= "fun" // try to parse block of text into mlscript ast val ans = try { - if (newParser || basePath.headOption.contains("compiler")) { + if (newParser) { val origin = Origin(testName, globalStartLineNum, fph) val lexer = new NewLexer(origin, raise, dbg = mode.dbgParsing) @@ -410,13 +423,16 @@ class DiffTests if (mode.showParse || mode.dbgParsing || parseOnly) output(NewLexer.printTokens(tokens)) - val p = new NewParser(origin, tokens, raise, dbg = mode.dbgParsing, N) { + val p = new NewParser(origin, tokens, newDefs, raise, dbg = mode.dbgParsing, N) { def doPrintDbg(msg: => Str): Unit = if (dbg) output(msg) } val res = p.parseAll(p.typingUnit) if (parseOnly) - output("Parsed: " + res.show) + output(s"Parsed: ${res.showDbg}") + + if (mode.showParse) + output(s"AST: $res") postProcess(mode, basePath, testName, res).foreach(output) @@ -427,7 +443,7 @@ class DiffTests } else parse(processedBlockStr, p => - if (file.ext =:= "fun") new Parser(Origin(testName, globalStartLineNum, fph)).pgrm(p) + if (legacyParser) new Parser(Origin(testName, globalStartLineNum, fph)).pgrm(p) else new MLParser(Origin(testName, globalStartLineNum, fph)).pgrm(p), verboseFailures = true ) @@ -437,15 +453,15 @@ class DiffTests val (lineNum, lineStr, col) = fph.getLineColAt(index) val globalLineNum = (allLines.size - lines.size) + lineNum if (!mode.expectParseErrors && !mode.fixme) - failures += globalLineNum + { output("TEST CASE FAILURE: There was an unexpected parse error"); failures += globalLineNum } output("/!\\ Parse error: " + extra.trace().msg + s" at l.$globalLineNum:$col: $lineStr") // successfully parsed block into a valid syntactically valid program case Success(p, index) => - if (mode.expectParseErrors && !newParser) - failures += blockLineNum - if (mode.showParse || mode.dbgParsing) output("Parsed: " + p) + if (mode.expectParseErrors && !newParser && !legacyParser) + { output("TEST CASE FAILURE: There was an unexpected parse success"); failures += blockLineNum } + if (mode.showParse || mode.dbgParsing) output("Parsed: " + p.showDbg) // if (mode.isDebugging) typer.resetState() if (mode.stats) typer.resetStats() typer.dbg = mode.dbg @@ -465,24 +481,14 @@ class DiffTests stdout = mode.stdout typer.preciselyTypeRecursion = mode.preciselyTypeRecursion - val (diags, (typeDefs, stmts)) = p.desugared - report(diags) - - if (mode.showParse) { - typeDefs.foreach(td => output("Desugared: " + td)) - stmts.foreach { s => - output("Desugared: " + s) - output("AST: " + mlscript.codegen.Helpers.inspect(s)) - } - } - val oldCtx = ctx - ctx = - // if (newParser) typer.typeTypingUnit(tu) - // else - typer.processTypeDefs(typeDefs)(ctx, raise) - def getType(ty: typer.SimpleType, pol: Opt[Bool], removePolarVars: Bool = true): Type = { + def getType(ty: typer.SimpleType, pol: Opt[Bool], removePolarVars: Bool = true): Type = + getTypeLike(ty, pol, removePolarVars) match { + case ty: Type => ty + case _ => die + } + def getTypeLike(ty: typer.SimpleType, pol: Opt[Bool], removePolarVars: Bool): TypeLike = { if (mode.isDebugging) output(s"⬤ Typed as: $ty") if (mode.isDebugging) output(s" where: ${ty.showBounds}") typer.dbg = mode.dbgSimplif @@ -508,6 +514,128 @@ 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) + + def showTTU(ttu: typer.TypedTypingUnit, ind: Int): Unit = { + val indStr = " " * ind + ttu.implementedMembers.foreach { + // case p: typer.NuTypeParam => + // output(s"${indStr}${p.name}: ${p.ty}") + case p: typer.NuParam => + output(s"${indStr}${p.name}: ${p.ty}") + case tc: typer.TypedNuAls => + output(s"${indStr}type ${tc.name} = ${tc.body}") + case tt: typer.TypedNuTrt => + output(s"${indStr}trait ${tt.name}") + output(s"${indStr} this: ${tt.thisTy} ${tt.thisTy.showBounds + .indentNewLines(indStr+" |")}") + case tc: typer.TypedNuCls => + output(s"${indStr}class ${tc.name}") + output(s"${indStr} this: ${tc.thisTy} ${tc.thisTy.showBounds + .indentNewLines(indStr+" |")}") + // showTTU(tc.ttu, ind + 1) + case tm: typer.TypedNuMxn => + output(s"${indStr}mixin ${tm.name}") + output(s"${indStr} this: ${tm.thisTy} ${tm.thisTy.showBounds + .indentNewLines(indStr+" |")}") + output(s"${indStr} super: ${tm.superTy} ${tm.superTy.showBounds + .indentNewLines(indStr+" |")}") + // showTTU(tm.ttu, ind + 1) + case tf: typer.TypedNuFun => + output(s"${indStr}${tf.fd.isLetRec match { + case S(false) => "let" + case S(true) => "let rec" + case N => "fun" + }} ${tf.name}: ${tf.typeSignature} where ${tf.typeSignature.showBounds + .indentNewLines(indStr+"|")}") + case typer.TypedNuDummy(d) => + output(s"${indStr} ${d.name}") + } + } + if (mode.dbg || mode.explainErrors) { + output("======== TYPED ========") + showTTU(tpd, 0) + tpd.result.foreach { res_ty => + output("res: " + tpd.result + " where " + res_ty.showBounds) + } + } + + val oldDbg = typer.dbg + val sim = if (mode.noSimplification) tpd else try { + typer.dbg = mode.dbgSimplif + object SimplifyPipeline extends typer.SimplifyPipeline { + def debugOutput(msg: => Str): Unit = + if (mode.dbgSimplif) output(msg) + } + SimplifyPipeline(tpd, pol = S(true))(ctx) + } finally typer.dbg = oldDbg + + val exp = typer.expandType(sim)(ctx) + + val expStr = + exp.showIn(ShowCtx.mk(exp :: Nil, newDefs) + // .copy(newDefs = true) // TODO later + , 0) + + output(expStr.stripSuffix("\n")) + + // // val exp = getType(typer.PolymorphicType(0, res_ty)) + // // output(s"Typed: ${exp}") + // tpd.result.foreach { res_ty => + // val exp = getType(typer.PolymorphicType(0, res_ty)) + // output(s"Typed: ${exp.show}") + // } + // // */ + + /* + import typer._ + + val mod = NuTypeDef(Nms, TypeName("ws"), Nil, Tup(Nil), Nil, TypingUnit(p.tops)) + val info = new LazyTypeInfo(ctx.lvl, mod)(ctx, Map.empty) + // val modTpe = DeclType(ctx.lvl, info) + info.force()(raise) + */ + + // val tpd = info + + + // val exp = typer.expandType(modTpe)(ctx) + // FirstClassDefn() + + + // (Nil, Nil, N) + (Nil, Nil, S(p.tops.collect { + // case LetS(isRec, pat, bod) => ("res", Nil, Nil, false) + case NuFunDef(isLet, nme, snme, tparams, bod) => + (nme.name + " ", nme.name :: Nil, Nil, false, isLet.isEmpty) + case t: Term => ("res ", "res" :: Nil, Nil, false, false) + })) + + } else { + + val (diags, (typeDefs, stmts)) = p.desugared + report(diags) + + if (mode.showParse) { + typeDefs.foreach(td => output("Desugared: " + td.showDbg)) + stmts.foreach { s => + output("Desugared: " + s.showDbg) + output(s"AST: $s") + } + } + + typer.ctx = + // if (newParser) typer.typeTypingUnit(tu) + // else + typer.processTypeDefs(typeDefs)(ctx, raise) + + (typeDefs, stmts, N) + } + // initialize ts typegen code builder and // declare all type definitions for current block val tsTypegenCodeBuilder = new TsTypegenCodeBuilder() @@ -542,7 +670,7 @@ class DiffTests // generate warnings for bivariant type variables val bivariantTypeVars = ttd.tparamsargs.iterator.filter{ case (tname, tvar) => - tvv.get(tvar).contains(typer.VarianceInfo.bi) + tvv.get(tvar).contains(VarianceInfo.bi) }.map(_._1).toList if (!bivariantTypeVars.isEmpty) { varianceWarnings.put(td.nme, bivariantTypeVars) @@ -574,7 +702,7 @@ class DiffTests output(s"${rhs.fold( _ => "Defined", // the method has been defined _ => "Declared" // the method type has just been declared - )} ${tn}.${mn}: ${res.show}") + )} ${tn}.${mn}: ${res.show(newDefs)}") } // start typegen, declare methods if any and complete typegen block @@ -586,7 +714,10 @@ class DiffTests .withFilter { case (mthd, _) => mthd.rhs.isRight || !mthDeclSet.contains(mthd.nme.name) } - .map { case (mthd, mthdTy) => (mthd.nme.name, mthdTy) } + .map { + case (mthd, mthdTy: Type) => (mthd.nme.name, mthdTy) + case _ => die + } tsTypegenCodeBuilder.addTypeDef(td, methods) } @@ -597,7 +728,9 @@ class DiffTests import Message._ val diags = varianceWarnings.iterator.map { case (tname, biVars) => val warnings = biVars.map( tname => msg"${tname.name} is irrelevant and may be removed" -> tname.toLoc) - WarningReport(msg"Type definition ${tname.name} has bivariant type parameters:" -> tname.toLoc :: warnings) + WarningReport( + msg"Type definition ${tname.name} has bivariant type parameters:" -> tname.toLoc :: warnings, + newDefs) }.toList report(diags) } @@ -606,14 +739,14 @@ class DiffTests // all `Def`s and `Term`s are processed here // generate typescript types if generateTsDeclarations flag is // set in the mode - // The tuple type means: (, , , ) - val typerResults: Ls[(Str, Ls[Str], Ls[Str], Bool)] = stmts.map { stmt => + // The tuple type means: (, , , , ) + val typerResults: Ls[(Str, Ls[Str], Ls[Str], Bool, Bool)] = newDefsResults getOrElse stmts.map { stmt => // Because diagnostic lines are after the typing results, // we need to cache the diagnostic blocks and add them to the // `typerResults` buffer after the statement has been processed. val diagnosticLines = mutable.Buffer.empty[Str] // We put diagnosis to the buffer in the following `Typer` routines. - val raiseToBuffer: typer.Raise = d => { + val raiseToBuffer: Raise = d => { report(d :: Nil, diagnosticLines += _) } // Typing results are before diagnostic messages in the subsumption case. @@ -626,7 +759,7 @@ class DiffTests typer.dbg = mode.dbg implicit val prov: typer.TP = typer.NoProv // TODO - implicit val r: typer.Raise = raise + implicit val r: Raise = raise val ty_sch = ctx.poly { ctx => typer.typeType(rhs)(ctx, raise, vars = tps.collect { @@ -638,12 +771,12 @@ class DiffTests declared += nme.name -> ty_sch val exp = getType(ty_sch, N) if (mode.generateTsDeclarations) tsTypegenCodeBuilder.addTypeGenTermDefinition(exp, Some(nme.name)) - S(nme.name -> (s"$nme: ${exp.show}" :: Nil)) + S(nme.name -> (s"${nme.name}: ${exp.show(newDefs)}" :: Nil)) // statement is defined and has a body/definition case d @ Def(isrec, nme, L(rhs), isByname) => typer.dbg = mode.dbg - val ty_sch = typer.typeLetRhs2(isrec, nme.name, rhs)(ctx, raiseToBuffer) + val ty_sch = typer.typeLetRhs2(isrec, nme.name, rhs)(ctx, raiseToBuffer, vars = Map.empty) val exp = getType(ty_sch, S(true)) // statement does not have a declared type for the body // the inferred type must be used and stored for lookup @@ -653,7 +786,7 @@ class DiffTests case N => ctx += nme.name -> typer.VarSymbol(ty_sch, nme) if (mode.generateTsDeclarations) tsTypegenCodeBuilder.addTypeGenTermDefinition(exp, Some(nme.name)) - s"$nme: ${exp.show}" :: Nil + s"${nme.name}: ${exp.show(newDefs)}" :: Nil // statement has a body and a declared type // both are used to compute a subsumption (What is this??) @@ -665,7 +798,7 @@ class DiffTests typer.subsume(ty_sch, sign)(ctx, raiseToBuffer, typer.TypeProvenance(d.toLoc, "def definition")) if (mode.generateTsDeclarations) tsTypegenCodeBuilder.addTypeGenTermDefinition(exp, Some(nme.name)) typeBeforeDiags = true - exp.show :: s" <: $nme:" :: sign_exp.show :: Nil + exp.show(newDefs) :: s" <: ${nme.name}:" :: sign_exp.show(newDefs) :: Nil })) case desug: DesugaredStatement => typer.dbg = mode.dbg @@ -675,7 +808,7 @@ class DiffTests val ptType = getType(pty, S(true)) ctx += nme -> typer.VarSymbol(pty, Var(nme)) if (mode.generateTsDeclarations) tsTypegenCodeBuilder.addTypeGenTermDefinition(ptType, Some(nme)) - nme -> (s"$nme: ${ptType.show}" :: Nil) + nme -> (s"$nme: ${ptType.show(newDefs)}" :: Nil) } // statements for terms that compute to a value @@ -686,16 +819,16 @@ class DiffTests val res = "res" ctx += res -> typer.VarSymbol(pty, Var(res)) if (mode.generateTsDeclarations) tsTypegenCodeBuilder.addTypeGenTermDefinition(exp, None) - res -> (s"res: ${exp.show}" :: Nil) + res -> (s"res: ${exp.show(newDefs)}" :: Nil) } else ( "" -> Nil )) } } typingResults match { - case N => ("", Nil, diagnosticLines.toList, false) + case N => ("", Nil, diagnosticLines.toList, false, false) case S(name -> typingLines) => - (name, typingLines, diagnosticLines.toList, typeBeforeDiags) + (name, typingLines, diagnosticLines.toList, typeBeforeDiags, false) } } @@ -738,8 +871,8 @@ class DiffTests if (mode.dbg) output("REC: " + tv + tv.showBounds) report(ErrorReport( msg"Inferred recursive type: ${ - getType(tv, pol = pol, removePolarVars = false).show - }" -> tv.prov.loco :: Nil) :: Nil) + getType(tv, pol = pol, removePolarVars = false).show(newDefs) + }" -> tv.prov.loco :: Nil, newDefs) :: Nil) } typer.dbg = false @@ -753,22 +886,28 @@ class DiffTests val executionResults: Result \/ Ls[(ReplHost.Reply, Str)] = if (!allowTypeErrors && file.ext =:= "mls" && !mode.noGeneration && !noJavaScript) { import codeGenTestHelpers._ - backend(p, mode.allowEscape) match { + backend(p, mode.allowEscape, newDefs && newParser) match { case testCode @ TestCode(prelude, queries) => { // Display the generated code. if (mode.showGeneratedJS) showGeneratedCode(testCode) // Execute code. if (!mode.noExecution) { val preludeReply = if (prelude.isEmpty) N else S(host.execute(prelude.mkString(" "))) - if (mode.showRepl) showReplPrelude(prelude, preludeReply, blockLineNum) - val replies = queries.map { - case CodeQuery(preludeLines, codeLines, resultName) => - host.query(preludeLines.mkString, codeLines.mkString, resultName) - case AbortedQuery(reason) => (ReplHost.Unexecuted(reason), "") - case EmptyQuery => (ReplHost.Empty, "") + preludeReply match { + case S(ue: ReplHost.Unexecuted) => R((ue, "") :: Nil) + case S(err: ReplHost.Error) => R((err, "") :: Nil) + case _ => { + if (mode.showRepl) showReplPrelude(prelude, preludeReply, blockLineNum) + val replies = queries.map { + case CodeQuery(preludeLines, codeLines, resultName) => + host.query(preludeLines.mkString, codeLines.mkString, resultName) + case AbortedQuery(reason) => (ReplHost.Unexecuted(reason), "") + case EmptyQuery => (ReplHost.Empty, "") + } + if (mode.showRepl) showReplContent(queries, replies) + R(replies) + } } - if (mode.showRepl) showReplContent(queries, replies) - R(replies) } else { L(ResultNotExecuted) } @@ -786,60 +925,76 @@ class DiffTests loglines.foreach(output) } } + + def checkReply( + replyQueue: mutable.Queue[(ReplHost.Reply, Str)], + prefixLength: Int, + hide: Boolean, // Show nothing except errors if `hide` is true. + errorOnly: Boolean + ): Unit = { + val indent = " " * prefixLength + replyQueue.headOption.foreach { case (head, log) => + head match { + case ReplHost.Error(isSyntaxError, content) => + // We don't expect type errors nor FIXME. + if (!mode.expectTypeErrors && !mode.fixme) { + // We don't expect code generation errors and it is. + if (!mode.expectCodeGenErrors && isSyntaxError) + { output("TEST CASE FAILURE: There was an unexpected codegen error"); failures += blockLineNum } + // We don't expect runtime errors and it's a runtime error. + if (!mode.expectRuntimeErrors && !allowRuntimeErrors && !isSyntaxError) + { output("TEST CASE FAILURE: There was an unexpected runtime error"); failures += blockLineNum } + } + if (isSyntaxError) { + // If there is syntax error in the generated code, + // it should be a code generation error. + output("Syntax error:") + totalCodeGenErrors += 1 + } else { // Otherwise, it is a runtime error. + output("Runtime error:") + totalRuntimeErrors += 1 + } + content.linesIterator.foreach { s => output(" " + s) } + case ReplHost.Unexecuted(reason) if (!hide) => + output(indent + "= ") + output(indent + " " + reason) + case ReplHost.Result(result, _) if (!errorOnly && !hide) => + result.linesIterator.zipWithIndex.foreach { case (line, i) => + if (i =:= 0) output(indent + "= " + line) + else output(indent + " " + line) + } + case ReplHost.Empty if (!errorOnly && !hide) => + output(indent + "= ") + case _ => () + } + outputLog(log) + replyQueue.dequeue() + } + } // If code generation fails, show the error message. executionResults match { case R(replies) => val replyQueue = mutable.Queue.from(replies) - typerResults.foreach { case (name, typingLines, diagnosticLines, typeBeforeDiags) => - if (typeBeforeDiags) { - typingLines.foreach(output) - diagnosticLines.foreach(output) - } else { - diagnosticLines.foreach(output) - typingLines.foreach(output) - } - val prefixLength = name.length - replyQueue.headOption.foreach { case (head, log) => - head match { - case ReplHost.Error(isSyntaxError, content) => - // We don't expect type errors nor FIXME. - if (!mode.expectTypeErrors && !mode.fixme) { - // We don't expect code generation errors and it is. - if (!mode.expectCodeGenErrors && isSyntaxError) - failures += blockLineNum - // We don't expect runtime errors and it's a runtime error. - if (!mode.expectRuntimeErrors && !allowRuntimeErrors && !isSyntaxError) - failures += blockLineNum - } - if (isSyntaxError) { - // If there is syntax error in the generated code, - // it should be a code generation error. - output("Syntax error:") - totalCodeGenErrors += 1 - } else { // Otherwise, it is a runtime error. - output("Runtime error:") - totalRuntimeErrors += 1 - } - content.linesIterator.foreach { s => output(" " + s) } - case ReplHost.Unexecuted(reason) => - output(" " * prefixLength + "= ") - output(" " * (prefixLength + 2) + reason) - case ReplHost.Result(result, _) => - result.linesIterator.zipWithIndex.foreach { case (line, i) => - if (i =:= 0) output(" " * prefixLength + "= " + line) - else output(" " * (prefixLength + 2) + line) - } - case ReplHost.Empty => - output(" " * prefixLength + "= ") + if (typerResults.isEmpty) + checkReply(replyQueue, 0, false, true) + else { + typerResults.foreach { case (name, typingLines, diagnosticLines, typeBeforeDiags, hide) => + if (!hide) { + if (typeBeforeDiags) { + typingLines.foreach(output) + diagnosticLines.foreach(output) + } else { + diagnosticLines.foreach(output) + typingLines.foreach(output) + } } - outputLog(log) - replyQueue.dequeue() + checkReply(replyQueue, name.length, hide, false) } } case L(other) => // Print type checking results first. - typerResults.foreach { case (_, typingLines, diagnosticLines, typeBeforeDiags) => + if (!newDefs) typerResults.foreach { case (_, typingLines, diagnosticLines, typeBeforeDiags, _) => if (typeBeforeDiags) { typingLines.foreach(output) diagnosticLines.foreach(output) @@ -850,20 +1005,20 @@ class DiffTests } other match { case _: TestCode => () // Impossible case. - case IllFormedCode(message) => + case e @ IllFormedCode(message) => totalCodeGenErrors += 1 - if (!mode.expectCodeGenErrors && !mode.fixme) + if (!mode.expectCodeGenErrors && !mode.fixme && !mode.expectTypeErrors) failures += blockLineNum output("Code generation encountered an error:") output(s" ${message}") case Unimplemented(message) => output("Unable to execute the code:") output(s" ${message}") - case UnexpectedCrash(name, message) => - if (!mode.fixme) - failures += blockLineNum - output("Code generation crashed:") - output(s" $name: $message") + // case UnexpectedCrash(name, message) => + // if (!mode.fixme) + // failures += blockLineNum + // output("Code generation crashed:") + // output(s" $name: $message") case ResultNotExecuted => () } } @@ -879,15 +1034,15 @@ class DiffTests } if (mode.expectParseErrors && totalParseErrors =:= 0) - failures += blockLineNum + { output("TEST CASE FAILURE: There was an unexpected lack of parse error"); failures += blockLineNum } if (mode.expectTypeErrors && totalTypeErrors =:= 0) - failures += blockLineNum + { output("TEST CASE FAILURE: There was an unexpected lack of type error"); failures += blockLineNum } if (mode.expectWarnings && totalWarnings =:= 0) - failures += blockLineNum + { output("TEST CASE FAILURE: There was an unexpected lack of warning"); failures += blockLineNum } if (mode.expectCodeGenErrors && totalCodeGenErrors =:= 0) - failures += blockLineNum + { output("TEST CASE FAILURE: There was an unexpected lack of codegen error"); failures += blockLineNum } if (mode.expectRuntimeErrors && totalRuntimeErrors =:= 0) - failures += blockLineNum + { output("TEST CASE FAILURE: There was an unexpected lack of runtime error"); failures += blockLineNum } } catch { case oh_noes: ThreadDeath => throw oh_noes case err: Throwable => @@ -918,8 +1073,8 @@ class DiffTests val timeStr = (((endTime - beginTime) / 1000 / 100).toDouble / 10.0).toString val testColor = if (testFailed) Console.RED else Console.GREEN - val resStr = s"${" " * (35 - testStr.size)}${testColor}${ - " " * (6 - timeStr.size)}$timeStr ms${Console.RESET}" + val resStr = s"${" " * (35 - testStr.length)}${testColor}${ + " " * (6 - timeStr.length)}$timeStr ms${Console.RESET}" if (inParallel) println(s"${Console.CYAN}Processed${Console.RESET} $testStr$resStr") else println(resStr) diff --git a/shared/src/test/scala/mlscript/NodeTest.scala b/shared/src/test/scala/mlscript/NodeTest.scala index 1d23160af2..f10b7d895e 100644 --- a/shared/src/test/scala/mlscript/NodeTest.scala +++ b/shared/src/test/scala/mlscript/NodeTest.scala @@ -17,6 +17,7 @@ class NodeTests extends org.scalatest.funsuite.AnyFunSuite { || v.startsWith("v18") || v.startsWith("v19") || v.startsWith("v20") + || v.startsWith("v21") ) } diff --git a/shared/src/test/scala/mlscript/ReplHost.scala b/shared/src/test/scala/mlscript/ReplHost.scala index 0968058db6..2113ac7eb0 100644 --- a/shared/src/test/scala/mlscript/ReplHost.scala +++ b/shared/src/test/scala/mlscript/ReplHost.scala @@ -1,6 +1,6 @@ package mlscript -import mlscript.utils.shorthands._ +import mlscript.utils._, shorthands._ /** * A helper class to manipulate an interactive Node.js process. @@ -31,13 +31,21 @@ final case class ReplHost() { private def collectUntilPrompt(): ReplHost.Reply = { val buffer = new StringBuilder() while (!buffer.endsWith("\n> ")) { - buffer.append(stdout.read().toChar) + val c = stdout.read() + if (c === -1) lastWords(s"ReplHost could not read more from NodeJS stdout.") + buffer.append(c.toChar) } // Remove the trailing `"\n> "` buffer.delete(buffer.length - 3, buffer.length) val reply = buffer.toString() reply.linesIterator.find(_.startsWith(ReplHost.syntaxErrorHead)) match { - case None => ReplHost.Result(reply, None) + case None => reply.linesIterator.find(_.startsWith(ReplHost.uncaughtErrorHead)) match { + case None => ReplHost.Result(reply, None) + case Some(uncaughtErrorLine) => { + val message = uncaughtErrorLine.substring(ReplHost.uncaughtErrorHead.length) + ReplHost.Error(false, message) + } + } case Some(syntaxErrorLine) => val message = syntaxErrorLine.substring(ReplHost.syntaxErrorHead.length) ReplHost.Error(true, message) @@ -141,6 +149,7 @@ object ReplHost { * The syntax error beginning text from Node.js. */ private val syntaxErrorHead = "Uncaught SyntaxError: " + private val uncaughtErrorHead = "Uncaught " /** * The base class of all kinds of REPL replies. diff --git a/ts2mls/js/src/main/scala/ts2mls/JSWriter.scala b/ts2mls/js/src/main/scala/ts2mls/JSWriter.scala index ad84615473..b448bbcbf0 100644 --- a/ts2mls/js/src/main/scala/ts2mls/JSWriter.scala +++ b/ts2mls/js/src/main/scala/ts2mls/JSWriter.scala @@ -18,7 +18,7 @@ class JSWriter(filename: String) { private var fileSize = 0 // how many bytes we've written in the file private var needTruncate = false - writeln(":NewParser\n:ParseOnly") + writeln(":NewDefs\n:ParseOnly") def writeln(str: String) = { val strln = str + "\n" diff --git a/ts2mls/js/src/main/scala/ts2mls/TSNamespace.scala b/ts2mls/js/src/main/scala/ts2mls/TSNamespace.scala index 4bb94bcf5a..bd54aa5685 100644 --- a/ts2mls/js/src/main/scala/ts2mls/TSNamespace.scala +++ b/ts2mls/js/src/main/scala/ts2mls/TSNamespace.scala @@ -38,7 +38,7 @@ class TSNamespace(name: String, parent: Option[TSNamespace]) { def generate(writer: JSWriter, indent: String): Unit = order.toList.foreach((p) => p match { case Left(subName) => { - writer.writeln(s"${indent}namespace $subName {") + writer.writeln(s"${indent}module $subName {") subSpace(subName).generate(writer, indent + " ") writer.writeln(s"$indent}") } diff --git a/ts2mls/js/src/main/scala/ts2mls/types/Converter.scala b/ts2mls/js/src/main/scala/ts2mls/types/Converter.scala index 32760642b2..8dadf566c9 100644 --- a/ts2mls/js/src/main/scala/ts2mls/types/Converter.scala +++ b/ts2mls/js/src/main/scala/ts2mls/types/Converter.scala @@ -39,7 +39,7 @@ object Converter { case TSUnionType(lhs, rhs) => s"(${convert(lhs)}) | (${convert(rhs)})" case TSIntersectionType(lhs, rhs) => s"(${convert(lhs)}) & (${convert(rhs)})" case TSTypeParameter(name, _) => name // constraints should be translated where the type parameters were created rather than be used - case TSTupleType(lst) => s"(${lst.foldLeft("")((p, t) => s"$p${convert(t)}, ")})" + case TSTupleType(lst) => s"[${lst.map(convert).mkString(", ")}]" case TSArrayType(element) => s"MutArray<${convert(element)}>" case TSEnumType => "int" case TSMemberType(base, _) => convert(base) // TODO: support private/protected members diff --git a/ts2mls/js/src/test/diff/Array.d.mls b/ts2mls/js/src/test/diff/Array.d.mls index 5fd86917fc..a6a43ee804 100644 --- a/ts2mls/js/src/test/diff/Array.d.mls +++ b/ts2mls/js/src/test/diff/Array.d.mls @@ -1,4 +1,4 @@ -:NewParser +:NewDefs :ParseOnly fun first(x: MutArray): string fun getZero3(): MutArray @@ -11,7 +11,7 @@ trait I() { fun doCs(c: MutArray): MutArray fun doIs(i: MutArray): MutArray fun inter(x: MutArray<(U) & (T)>): MutArray<(U) & (T)> -fun clean(x: MutArray<(string, number, )>): MutArray<(string, number, )> +fun clean(x: MutArray<[string, number]>): MutArray<[string, number]> fun translate(x: MutArray): MutArray fun uu(x: MutArray<((number) | (false)) | (true)>): MutArray<((number) | (false)) | (true)> class Temp() { @@ -19,5 +19,6 @@ class Temp() { } fun ta(ts: MutArray>): MutArray> fun tat(ts: MutArray>): MutArray> -//│ |#fun| |first|(|x|#:| |MutArray|‹|string|›|)|#:| |string|↵|#fun| |getZero3|(||)|#:| |MutArray|‹|number|›|↵|#fun| |first2|(|fs|#:| |MutArray|‹|(|number|)| |=>| |number|›|)|#:| |(|number|)| |=>| |number|↵|#fun| |doEs|(|e|#:| |MutArray|‹|int|›|)|#:| |MutArray|‹|int|›|↵|#class| |C|(||)| |{||}|↵|#trait| |I|(||)| |{|→|#let| |i|#:| |number|←|↵|}|↵|#fun| |doCs|(|c|#:| |MutArray|‹|C|›|)|#:| |MutArray|‹|C|›|↵|#fun| |doIs|(|i|#:| |MutArray|‹|I|›|)|#:| |MutArray|‹|I|›|↵|#fun| |inter|‹|U|,| |T|›|(|x|#:| |MutArray|‹|(|U|)| |&| |(|T|)|›|)|#:| |MutArray|‹|(|U|)| |&| |(|T|)|›|↵|#fun| |clean|(|x|#:| |MutArray|‹|(|string|,| |number|,| |)|›|)|#:| |MutArray|‹|(|string|,| |number|,| |)|›|↵|#fun| |translate|‹|T|,| |U|›|(|x|#:| |MutArray|‹|T|›|)|#:| |MutArray|‹|U|›|↵|#fun| |uu|(|x|#:| |MutArray|‹|(|(|number|)| ||| |(|false|)|)| ||| |(|true|)|›|)|#:| |MutArray|‹|(|(|number|)| ||| |(|false|)|)| ||| |(|true|)|›|↵|#class| |Temp|‹|T|›|(||)| |{|→|#let| |x|#:| |T|←|↵|}|↵|#fun| |ta|(|ts|#:| |MutArray|‹|Temp|‹|(|false|)| ||| |(|true|)|›|›|)|#:| |MutArray|‹|Temp|‹|(|false|)| ||| |(|true|)|›|›|↵|#fun| |tat|‹|T|›|(|ts|#:| |MutArray|‹|Temp|‹|T|›|›|)|#:| |MutArray|‹|Temp|‹|T|›|›| -//│ Parsed: {fun first: (x: MutArray[string],) -> string; fun getZero3: () -> MutArray[number]; fun first2: (fs: MutArray[number -> number],) -> number -> number; fun doEs: (e: MutArray[int],) -> MutArray[int]; class C() {}; trait I() {let i: number}; fun doCs: (c: MutArray[C],) -> MutArray[C]; fun doIs: (i: MutArray[I],) -> MutArray[I]; fun inter: (x: MutArray[(U,) & (T,)],) -> MutArray[(U,) & (T,)]; fun clean: (x: MutArray[(string, number,)],) -> MutArray[(string, number,)]; fun translate: (x: MutArray[T],) -> MutArray[U]; fun uu: (x: MutArray[((number,) | (false,),) | (true,)],) -> MutArray[((number,) | (false,),) | (true,)]; class Temp‹T›() {let x: T}; fun ta: (ts: MutArray[Temp[(false,) | (true,)]],) -> MutArray[Temp[(false,) | (true,)]]; fun tat: (ts: MutArray[Temp[T]],) -> MutArray[Temp[T]]} +//│ |#fun| |first|(|x|#:| |MutArray|‹|string|›|)|#:| |string|↵|#fun| |getZero3|(||)|#:| |MutArray|‹|number|›|↵|#fun| |first2|(|fs|#:| |MutArray|‹|(|number|)| |#=>| |number|›|)|#:| |(|number|)| |#=>| |number|↵|#fun| |doEs|(|e|#:| |MutArray|‹|int|›|)|#:| |MutArray|‹|int|›|↵|#class| |C|(||)| |{||}|↵|#trait| |I|(||)| |{|→|#let| |i|#:| |number|←|↵|}|↵|#fun| |doCs|(|c|#:| |MutArray|‹|C|›|)|#:| |MutArray|‹|C|›|↵|#fun| |doIs|(|i|#:| |MutArray|‹|I|›|)|#:| |MutArray|‹|I|›|↵|#fun| |inter|‹|U|,| |T|›|(|x|#:| |MutArray|‹|(|U|)| |&| |(|T|)|›|)|#:| |MutArray|‹|(|U|)| |&| |(|T|)|›|↵|#fun| |clean|(|x|#:| |MutArray|‹|[|string|,| |number|]|›|)|#:| |MutArray|‹|[|string|,| |number|]|›|↵|#fun| |translate|‹|T|,| |U|›|(|x|#:| |MutArray|‹|T|›|)|#:| |MutArray|‹|U|›|↵|#fun| |uu|(|x|#:| |MutArray|‹|(|(|number|)| ||| |(|false|)|)| ||| |(|true|)|›|)|#:| |MutArray|‹|(|(|number|)| ||| |(|false|)|)| ||| |(|true|)|›|↵|#class| |Temp|‹|T|›|(||)| |{|→|#let| |x|#:| |T|←|↵|}|↵|#fun| |ta|(|ts|#:| |MutArray|‹|Temp|‹|(|false|)| ||| |(|true|)|›|›|)|#:| |MutArray|‹|Temp|‹|(|false|)| ||| |(|true|)|›|›|↵|#fun| |tat|‹|T|›|(|ts|#:| |MutArray|‹|Temp|‹|T|›|›|)|#:| |MutArray|‹|Temp|‹|T|›|›| +//│ Parsed: {fun first: (x: MutArray[string]) -> string; fun getZero3: () -> MutArray[number]; fun first2: (fs: MutArray[number -> number]) -> number -> number; fun doEs: (e: MutArray[int]) -> MutArray[int]; class C() {}; trait I() {let i: number}; fun doCs: (c: MutArray[C]) -> MutArray[C]; fun doIs: (i: MutArray[I]) -> MutArray[I]; fun inter: (x: MutArray[U & T]) -> MutArray[U & T]; fun clean: (x: MutArray[[string, number]]) -> MutArray[[string, number]]; fun translate: (x: MutArray[T]) -> MutArray[U]; fun uu: (x: MutArray[number | false | true]) -> MutArray[number | false | true]; class Temp‹T›() {let x: T}; fun ta: (ts: MutArray[Temp[bool]]) -> MutArray[Temp[bool]]; fun tat: (ts: MutArray[Temp[T]]) -> MutArray[Temp[T]]} +//│ diff --git a/ts2mls/js/src/test/diff/BasicFunctions.d.mls b/ts2mls/js/src/test/diff/BasicFunctions.d.mls index 2c68fdee09..00356e3ad8 100644 --- a/ts2mls/js/src/test/diff/BasicFunctions.d.mls +++ b/ts2mls/js/src/test/diff/BasicFunctions.d.mls @@ -1,4 +1,4 @@ -:NewParser +:NewDefs :ParseOnly fun hello(): unit fun add(x: number, y: number): number @@ -18,11 +18,12 @@ class Foooooo() { let ooooooo: number } fun inn(f: Foooooo): unit -fun out(): Foooooo +fun out1(): Foooooo trait Barrrrrrrrr() { let rrrrrrr: number } fun inn2(b: Barrrrrrrrr): unit fun out2(): Barrrrrrrrr -//│ |#fun| |hello|(||)|#:| |unit|↵|#fun| |add|(|x|#:| |number|,| |y|#:| |number|)|#:| |number|↵|#fun| |sub|(|x|#:| |number|,| |y|#:| |number|)|#:| |number|↵|#fun| |foo|(||)|#:| |number|↵|#fun| |id|(|x|#:| |anything|)|#:| |anything|↵|#fun| |odd|(|x|#:| |number|)|#:| |(|false|)| ||| |(|true|)|↵|#fun| |isnull|(|x|#:| |anything|)|#:| |(|false|)| ||| |(|true|)|↵|#fun| |bar|(||)|#:| |anything|↵|#fun| |nu|(|n|#:| |null|)|#:| |null|↵|#fun| |un|(|n|#:| |undefined|)|#:| |undefined|↵|#fun| |fail|(||)|#:| |nothing|↵|#fun| |create|(||)|#:| |object|↵|#fun| |pa|(|x|#:| |number|)|#:| |number|↵|#fun| |wtf|(|x|#:| |anything|)|#:| |unit|↵|#class| |Foooooo|(||)| |{|→|#let| |ooooooo|#:| |number|←|↵|}|↵|#fun| |inn|(|f|#:| |Foooooo|)|#:| |unit|↵|#fun| |out|(||)|#:| |Foooooo|↵|#trait| |Barrrrrrrrr|(||)| |{|→|#let| |rrrrrrr|#:| |number|←|↵|}|↵|#fun| |inn2|(|b|#:| |Barrrrrrrrr|)|#:| |unit|↵|#fun| |out2|(||)|#:| |Barrrrrrrrr| -//│ Parsed: {fun hello: () -> unit; fun add: (x: number, y: number,) -> number; fun sub: (x: number, y: number,) -> number; fun foo: () -> number; fun id: (x: anything,) -> anything; fun odd: (x: number,) -> ((false,) | (true,)); fun isnull: (x: anything,) -> ((false,) | (true,)); fun bar: () -> anything; fun nu: (n: null,) -> null; fun un: (n: undefined,) -> undefined; fun fail: () -> nothing; fun create: () -> object; fun pa: (x: number,) -> number; fun wtf: (x: anything,) -> unit; class Foooooo() {let ooooooo: number}; fun inn: (f: Foooooo,) -> unit; fun out: () -> Foooooo; trait Barrrrrrrrr() {let rrrrrrr: number}; fun inn2: (b: Barrrrrrrrr,) -> unit; fun out2: () -> Barrrrrrrrr} +//│ |#fun| |hello|(||)|#:| |unit|↵|#fun| |add|(|x|#:| |number|,| |y|#:| |number|)|#:| |number|↵|#fun| |sub|(|x|#:| |number|,| |y|#:| |number|)|#:| |number|↵|#fun| |foo|(||)|#:| |number|↵|#fun| |id|(|x|#:| |anything|)|#:| |anything|↵|#fun| |odd|(|x|#:| |number|)|#:| |(|false|)| ||| |(|true|)|↵|#fun| |isnull|(|x|#:| |anything|)|#:| |(|false|)| ||| |(|true|)|↵|#fun| |bar|(||)|#:| |anything|↵|#fun| |nu|(|n|#:| |#null|)|#:| |#null|↵|#fun| |un|(|n|#:| |#undefined|)|#:| |#undefined|↵|#fun| |fail|(||)|#:| |nothing|↵|#fun| |create|(||)|#:| |object|↵|#fun| |pa|(|x|#:| |number|)|#:| |number|↵|#fun| |wtf|(|x|#:| |anything|)|#:| |unit|↵|#class| |Foooooo|(||)| |{|→|#let| |ooooooo|#:| |number|←|↵|}|↵|#fun| |inn|(|f|#:| |Foooooo|)|#:| |unit|↵|#fun| |out1|(||)|#:| |Foooooo|↵|#trait| |Barrrrrrrrr|(||)| |{|→|#let| |rrrrrrr|#:| |number|←|↵|}|↵|#fun| |inn2|(|b|#:| |Barrrrrrrrr|)|#:| |unit|↵|#fun| |out2|(||)|#:| |Barrrrrrrrr| +//│ Parsed: {fun hello: () -> unit; fun add: (x: number, y: number) -> number; fun sub: (x: number, y: number) -> number; fun foo: () -> number; fun id: (x: anything) -> anything; fun odd: (x: number) -> bool; fun isnull: (x: anything) -> bool; fun bar: () -> anything; fun nu: (n: null) -> null; fun un: (n: ()) -> (); fun fail: () -> nothing; fun create: () -> object; fun pa: (x: number) -> number; fun wtf: (x: anything) -> unit; class Foooooo() {let ooooooo: number}; fun inn: (f: Foooooo) -> unit; fun out1: () -> Foooooo; trait Barrrrrrrrr() {let rrrrrrr: number}; fun inn2: (b: Barrrrrrrrr) -> unit; fun out2: () -> Barrrrrrrrr} +//│ diff --git a/ts2mls/js/src/test/diff/ClassMember.d.mls b/ts2mls/js/src/test/diff/ClassMember.d.mls index 06a8f40a81..03848dcb26 100644 --- a/ts2mls/js/src/test/diff/ClassMember.d.mls +++ b/ts2mls/js/src/test/diff/ClassMember.d.mls @@ -1,4 +1,4 @@ -:NewParser +:NewDefs :ParseOnly class Student(s: string, age: number) { let name: string @@ -22,4 +22,5 @@ class TTT() { fun ttt2(x: T): T } //│ |#class| |Student|(|s|#:| |string|,| |age|#:| |number|)| |{|→|#let| |name|#:| |string|↵|#fun| |isFriend|(|other|#:| |Student|)|#:| |(|false|)| ||| |(|true|)|↵|#fun| |addScore|(|sub|#:| |string|,| |score|#:| |number|)|#:| |unit|↵|#fun| |getID|(||)|#:| |number|←|↵|}|↵|#class| |Foo|‹|T|›|(||)| |{|→|#fun| |bar|(|x|#:| |T|)|#:| |unit|←|↵|}|↵|#class| |EZ|(||)| |{|→|#fun| |inc|(|x|#:| |number|)|#:| |number|←|↵|}|↵|#class| |Outer|(||)| |{|→|#class| |Inner|(||)| |{|→|#let| |a|#:| |number|←|↵|}|←|↵|}|↵|#class| |TTT|‹|T|›|(||)| |{|→|#fun| |ttt|(|x|#:| |T|)|#:| |T|↵|#fun| |ttt2|(|x|#:| |T|)|#:| |T|←|↵|}| -//│ Parsed: {class Student(s: string, age: number,) {let name: string; fun isFriend: (other: Student,) -> ((false,) | (true,)); fun addScore: (sub: string, score: number,) -> unit; fun getID: () -> number}; class Foo‹T›() {fun bar: (x: T,) -> unit}; class EZ() {fun inc: (x: number,) -> number}; class Outer() {class Inner() {let a: number}}; class TTT‹T›() {fun ttt: (x: T,) -> T; fun ttt2: (x: T,) -> T}} +//│ Parsed: {class Student(s: string, age: number,) {let name: string; fun isFriend: (other: Student) -> bool; fun addScore: (sub: string, score: number) -> unit; fun getID: () -> number}; class Foo‹T›() {fun bar: (x: T) -> unit}; class EZ() {fun inc: (x: number) -> number}; class Outer() {class Inner() {let a: number}}; class TTT‹T›() {fun ttt: (x: T) -> T; fun ttt2: (x: T) -> T}} +//│ diff --git a/ts2mls/js/src/test/diff/Dec.d.mls b/ts2mls/js/src/test/diff/Dec.d.mls index d6f9857c19..6ad560cd71 100644 --- a/ts2mls/js/src/test/diff/Dec.d.mls +++ b/ts2mls/js/src/test/diff/Dec.d.mls @@ -1,4 +1,4 @@ -:NewParser +:NewDefs :ParseOnly fun getName(id: (string) | (number)): string fun render(callback: (unit => unit) | (undefined)): string @@ -8,7 +8,8 @@ trait Get() { class Person(name: string, age: number) { fun getName(id: number): string } -namespace OOO { +module OOO { } -//│ |#fun| |getName|(|id|#:| |(|string|)| ||| |(|number|)|)|#:| |string|↵|#fun| |render|(|callback|#:| |(|unit| |=>| |unit|)| ||| |(|undefined|)|)|#:| |string|↵|#trait| |Get|(||)| |{|→|#fun| |__call|(|id|#:| |string|)|#:| |string|←|↵|}|↵|#class| |Person|(|name|#:| |string|,| |age|#:| |number|)| |{|→|#fun| |getName|(|id|#:| |number|)|#:| |string|←|↵|}|↵|#namespace| |OOO| |{|↵|}| -//│ Parsed: {fun getName: (id: (string,) | (number,),) -> string; fun render: (callback: (unit -> unit,) | (undefined,),) -> string; trait Get() {fun __call: (id: string,) -> string}; class Person(name: string, age: number,) {fun getName: (id: number,) -> string}; namespace OOO() {}} +//│ |#fun| |getName|(|id|#:| |(|string|)| ||| |(|number|)|)|#:| |string|↵|#fun| |render|(|callback|#:| |(|unit| |#=>| |unit|)| ||| |(|#undefined|)|)|#:| |string|↵|#trait| |Get|(||)| |{|→|#fun| |__call|(|id|#:| |string|)|#:| |string|←|↵|}|↵|#class| |Person|(|name|#:| |string|,| |age|#:| |number|)| |{|→|#fun| |getName|(|id|#:| |number|)|#:| |string|←|↵|}|↵|#module| |OOO| |{|↵|}| +//│ Parsed: {fun getName: (id: string | number) -> string; fun render: (callback: unit -> unit | ()) -> string; trait Get() {fun __call: (id: string) -> string}; class Person(name: string, age: number,) {fun getName: (id: number) -> string}; module OOO {}} +//│ diff --git a/ts2mls/js/src/test/diff/Enum.d.mls b/ts2mls/js/src/test/diff/Enum.d.mls index 639bf9aaed..75e68ab29e 100644 --- a/ts2mls/js/src/test/diff/Enum.d.mls +++ b/ts2mls/js/src/test/diff/Enum.d.mls @@ -1,7 +1,8 @@ -:NewParser +:NewDefs :ParseOnly fun pass(c: int): (false) | (true) fun stop(): int fun g(x: int): int //│ |#fun| |pass|(|c|#:| |int|)|#:| |(|false|)| ||| |(|true|)|↵|#fun| |stop|(||)|#:| |int|↵|#fun| |g|(|x|#:| |int|)|#:| |int| -//│ Parsed: {fun pass: (c: int,) -> ((false,) | (true,)); fun stop: () -> int; fun g: (x: int,) -> int} +//│ Parsed: {fun pass: (c: int) -> bool; fun stop: () -> int; fun g: (x: int) -> int} +//│ diff --git a/ts2mls/js/src/test/diff/Heritage.d.mls b/ts2mls/js/src/test/diff/Heritage.d.mls index 34b56a6b61..bc5d879a13 100644 --- a/ts2mls/js/src/test/diff/Heritage.d.mls +++ b/ts2mls/js/src/test/diff/Heritage.d.mls @@ -1,4 +1,4 @@ -:NewParser +:NewDefs :ParseOnly class A() { fun foo(): unit @@ -33,12 +33,13 @@ trait O() { class OR(): O { fun xx(x: R): R } -namespace Five { +module Five { class ROTK() { let wu: string } class Y(): Five.ROTK {} } class Y(): Five.ROTK {} -//│ |#class| |A|(||)| |{|→|#fun| |foo|(||)|#:| |unit|←|↵|}|↵|#class| |B|(||)|#:| |A| |{||}|↵|#class| |C|‹|T|›|(||)| |{|→|#fun| |set|(|x|#:| |T|)|#:| |unit|↵|#fun| |get|(||)|#:| |T|←|↵|}|↵|#class| |D|(||)|#:| |C|‹|number|›| |{||}|↵|#trait| |Wu|(||)| |{|→|#let| |x|#:| |(|false|)| ||| |(|true|)|←|↵|}|↵|#class| |WuWu|(||)|#:| |Wu| |{|→|#let| |y|#:| |(|false|)| ||| |(|true|)|←|↵|}|↵|#trait| |WuWuWu|(||)|#:| |WuWu| |{|→|#let| |z|#:| |(|false|)| ||| |(|true|)|←|↵|}|↵|#trait| |Never|(||)|#:| |WuWuWu| |{|→|#fun| |w|(||)|#:| |nothing|←|↵|}|↵|#class| |VG|‹|T|›|(||)| |{|→|#let| |x|#:| |T|←|↵|}|↵|#class| |Home|‹|T|›|(||)|#:| |VG|‹|string|›| |{|→|#let| |y|#:| |T|←|↵|}|↵|#trait| |O|‹|I|›|(||)| |{|→|#fun| |xx|(|x|#:| |I|)|#:| |I|←|↵|}|↵|#class| |OR|‹|R|›|(||)|#:| |O|‹|R|›| |{|→|#fun| |xx|(|x|#:| |R|)|#:| |R|←|↵|}|↵|#namespace| |Five| |{|→|#class| |ROTK|(||)| |{|→|#let| |wu|#:| |string|←|↵|}|↵|#class| |Y|(||)|#:| |Five|.ROTK| |{||}|←|↵|}|↵|#class| |Y|(||)|#:| |Five|.ROTK| |{||}| -//│ Parsed: {class A() {fun foo: () -> unit}; class B(): A {}; class C‹T›() {fun set: (x: T,) -> unit; fun get: () -> T}; class D(): C‹number› {}; trait Wu() {let x: (false,) | (true,)}; class WuWu(): Wu {let y: (false,) | (true,)}; trait WuWuWu(): WuWu {let z: (false,) | (true,)}; trait Never(): WuWuWu {fun w: () -> nothing}; class VG‹T›() {let x: T}; class Home‹T›(): VG‹string› {let y: T}; trait O‹I›() {fun xx: (x: I,) -> I}; class OR‹R›(): O‹R› {fun xx: (x: R,) -> R}; namespace Five() {class ROTK() {let wu: string}; class Y(): (Five).ROTK {}}; class Y(): (Five).ROTK {}} +//│ |#class| |A|(||)| |{|→|#fun| |foo|(||)|#:| |unit|←|↵|}|↵|#class| |B|(||)|#:| |A| |{||}|↵|#class| |C|‹|T|›|(||)| |{|→|#fun| |set|(|x|#:| |T|)|#:| |unit|↵|#fun| |get|(||)|#:| |T|←|↵|}|↵|#class| |D|(||)|#:| |C|‹|number|›| |{||}|↵|#trait| |Wu|(||)| |{|→|#let| |x|#:| |(|false|)| ||| |(|true|)|←|↵|}|↵|#class| |WuWu|(||)|#:| |Wu| |{|→|#let| |y|#:| |(|false|)| ||| |(|true|)|←|↵|}|↵|#trait| |WuWuWu|(||)|#:| |WuWu| |{|→|#let| |z|#:| |(|false|)| ||| |(|true|)|←|↵|}|↵|#trait| |Never|(||)|#:| |WuWuWu| |{|→|#fun| |w|(||)|#:| |nothing|←|↵|}|↵|#class| |VG|‹|T|›|(||)| |{|→|#let| |x|#:| |T|←|↵|}|↵|#class| |Home|‹|T|›|(||)|#:| |VG|‹|string|›| |{|→|#let| |y|#:| |T|←|↵|}|↵|#trait| |O|‹|I|›|(||)| |{|→|#fun| |xx|(|x|#:| |I|)|#:| |I|←|↵|}|↵|#class| |OR|‹|R|›|(||)|#:| |O|‹|R|›| |{|→|#fun| |xx|(|x|#:| |R|)|#:| |R|←|↵|}|↵|#module| |Five| |{|→|#class| |ROTK|(||)| |{|→|#let| |wu|#:| |string|←|↵|}|↵|#class| |Y|(||)|#:| |Five|.ROTK| |{||}|←|↵|}|↵|#class| |Y|(||)|#:| |Five|.ROTK| |{||}| +//│ Parsed: {class A() {fun foo: () -> unit}; class B(): A {}; class C‹T›() {fun set: (x: T) -> unit; fun get: () -> T}; class D(): C[number] {}; trait Wu() {let x: bool}; class WuWu(): Wu {let y: bool}; trait WuWuWu(): WuWu {let z: bool}; trait Never(): WuWuWu {fun w: () -> nothing}; class VG‹T›() {let x: T}; class Home‹T›(): VG[string] {let y: T}; trait O‹I›() {fun xx: (x: I) -> I}; class OR‹R›(): O[R] {fun xx: (x: R) -> R}; module Five {class ROTK() {let wu: string}; class Y(): Five.ROTK {}}; class Y(): Five.ROTK {}} +//│ diff --git a/ts2mls/js/src/test/diff/HighOrderFunc.d.mls b/ts2mls/js/src/test/diff/HighOrderFunc.d.mls index 3a8f6ede32..e497bae1d4 100644 --- a/ts2mls/js/src/test/diff/HighOrderFunc.d.mls +++ b/ts2mls/js/src/test/diff/HighOrderFunc.d.mls @@ -1,7 +1,8 @@ -:NewParser +:NewDefs :ParseOnly fun h1(inc: (number) => number, num: number): number fun h2(hint: string): unit => string fun h3(f: (number) => number, g: (number) => number): (number) => number -//│ |#fun| |h1|(|inc|#:| |(|number|)| |=>| |number|,| |num|#:| |number|)|#:| |number|↵|#fun| |h2|(|hint|#:| |string|)|#:| |unit| |=>| |string|↵|#fun| |h3|(|f|#:| |(|number|)| |=>| |number|,| |g|#:| |(|number|)| |=>| |number|)|#:| |(|number|)| |=>| |number| -//│ Parsed: {fun h1: (inc: number -> number, num: number,) -> number; fun h2: (hint: string,) -> unit -> string; fun h3: (f: number -> number, g: number -> number,) -> number -> number} +//│ |#fun| |h1|(|inc|#:| |(|number|)| |#=>| |number|,| |num|#:| |number|)|#:| |number|↵|#fun| |h2|(|hint|#:| |string|)|#:| |unit| |#=>| |string|↵|#fun| |h3|(|f|#:| |(|number|)| |#=>| |number|,| |g|#:| |(|number|)| |#=>| |number|)|#:| |(|number|)| |#=>| |number| +//│ Parsed: {fun h1: (inc: number -> number, num: number) -> number; fun h2: (hint: string) -> unit -> string; fun h3: (f: number -> number, g: number -> number) -> number -> number} +//│ diff --git a/ts2mls/js/src/test/diff/InterfaceMember.d.mls b/ts2mls/js/src/test/diff/InterfaceMember.d.mls index 252b3f0e93..30b8dadda6 100644 --- a/ts2mls/js/src/test/diff/InterfaceMember.d.mls +++ b/ts2mls/js/src/test/diff/InterfaceMember.d.mls @@ -1,4 +1,4 @@ -:NewParser +:NewDefs :ParseOnly trait IFoo() { let a: string @@ -36,5 +36,6 @@ trait Next(): Simple {} trait TTT() { fun ttt(x: T): T } -//│ |#trait| |IFoo|(||)| |{|→|#let| |a|#:| |string|↵|#fun| |b|(|x|#:| |number|)|#:| |number|↵|#fun| |c|(||)|#:| |(|false|)| ||| |(|true|)|↵|#fun| |d|(|x|#:| |string|)|#:| |unit|←|↵|}|↵|#trait| |II|‹|T|›|(||)| |{|→|#fun| |test|(|x|#:| |T|)|#:| |number|←|↵|}|↵|#fun| |create|(||)|#:| |{|v|#:| |number|,|}|↵|#fun| |get|(|x|#:| |{|t|#:| |string|,|}|)|#:| |string|↵|#trait| |IEvent|(||)| |{|→|#fun| |callback|(||)|#:| |(|number|)| |=>| |unit|←|↵|}|↵|#trait| |SearchFunc|(||)| |{|→|#fun| |__call|(|source|#:| |string|,| |subString|#:| |string|)|#:| |(|false|)| ||| |(|true|)|←|↵|}|↵|#trait| |StringArray|(||)| |{|→|#fun| |__index|(|index|#:| |number|)|#:| |string|←|↵|}|↵|#trait| |Counter|(||)| |{|→|#fun| |__call|(|start|#:| |number|)|#:| |string|↵|#let| |interval|#:| |number|↵|#fun| |reset|(||)|#:| |unit|←|↵|}|↵|#trait| |Simple|(||)| |{|→|#let| |a|#:| |number|↵|#fun| |b|(|x|#:| |(|false|)| ||| |(|true|)|)|#:| |string|←|↵|}|↵|#trait| |Simple2|‹|T|›|(||)| |{|→|#let| |abc|#:| |T|←|↵|}|↵|#trait| |Next|(||)|#:| |Simple| |{||}|↵|#trait| |TTT|‹|T|›|(||)| |{|→|#fun| |ttt|(|x|#:| |T|)|#:| |T|←|↵|}| -//│ Parsed: {trait IFoo() {let a: string; fun b: (x: number,) -> number; fun c: () -> ((false,) | (true,)); fun d: (x: string,) -> unit}; trait II‹T›() {fun test: (x: T,) -> number}; fun create: () -> {v: number}; fun get: (x: {t: string},) -> string; trait IEvent() {fun callback: () -> number -> unit}; trait SearchFunc() {fun __call: (source: string, subString: string,) -> ((false,) | (true,))}; trait StringArray() {fun __index: (index: number,) -> string}; trait Counter() {fun __call: (start: number,) -> string; let interval: number; fun reset: () -> unit}; trait Simple() {let a: number; fun b: (x: (false,) | (true,),) -> string}; trait Simple2‹T›() {let abc: T}; trait Next(): Simple {}; trait TTT‹T›() {fun ttt: (x: T,) -> T}} +//│ |#trait| |IFoo|(||)| |{|→|#let| |a|#:| |string|↵|#fun| |b|(|x|#:| |number|)|#:| |number|↵|#fun| |c|(||)|#:| |(|false|)| ||| |(|true|)|↵|#fun| |d|(|x|#:| |string|)|#:| |unit|←|↵|}|↵|#trait| |II|‹|T|›|(||)| |{|→|#fun| |test|(|x|#:| |T|)|#:| |number|←|↵|}|↵|#fun| |create|(||)|#:| |{|v|#:| |number|,|}|↵|#fun| |get|(|x|#:| |{|t|#:| |string|,|}|)|#:| |string|↵|#trait| |IEvent|(||)| |{|→|#fun| |callback|(||)|#:| |(|number|)| |#=>| |unit|←|↵|}|↵|#trait| |SearchFunc|(||)| |{|→|#fun| |__call|(|source|#:| |string|,| |subString|#:| |string|)|#:| |(|false|)| ||| |(|true|)|←|↵|}|↵|#trait| |StringArray|(||)| |{|→|#fun| |__index|(|index|#:| |number|)|#:| |string|←|↵|}|↵|#trait| |Counter|(||)| |{|→|#fun| |__call|(|start|#:| |number|)|#:| |string|↵|#let| |interval|#:| |number|↵|#fun| |reset|(||)|#:| |unit|←|↵|}|↵|#trait| |Simple|(||)| |{|→|#let| |a|#:| |number|↵|#fun| |b|(|x|#:| |(|false|)| ||| |(|true|)|)|#:| |string|←|↵|}|↵|#trait| |Simple2|‹|T|›|(||)| |{|→|#let| |abc|#:| |T|←|↵|}|↵|#trait| |Next|(||)|#:| |Simple| |{||}|↵|#trait| |TTT|‹|T|›|(||)| |{|→|#fun| |ttt|(|x|#:| |T|)|#:| |T|←|↵|}| +//│ Parsed: {trait IFoo() {let a: string; fun b: (x: number) -> number; fun c: () -> bool; fun d: (x: string) -> unit}; trait II‹T›() {fun test: (x: T) -> number}; fun create: () -> {v: number}; fun get: (x: {t: string}) -> string; trait IEvent() {fun callback: () -> number -> unit}; trait SearchFunc() {fun __call: (source: string, subString: string) -> bool}; trait StringArray() {fun __index: (index: number) -> string}; trait Counter() {fun __call: (start: number) -> string; let interval: number; fun reset: () -> unit}; trait Simple() {let a: number; fun b: (x: bool) -> string}; trait Simple2‹T›() {let abc: T}; trait Next(): Simple {}; trait TTT‹T›() {fun ttt: (x: T) -> T}} +//│ diff --git a/ts2mls/js/src/test/diff/Intersection.d.mls b/ts2mls/js/src/test/diff/Intersection.d.mls index b9e532b6b1..54213af525 100644 --- a/ts2mls/js/src/test/diff/Intersection.d.mls +++ b/ts2mls/js/src/test/diff/Intersection.d.mls @@ -1,4 +1,4 @@ -:NewParser +:NewDefs :ParseOnly fun extend(first: T, second: U): (T) & (U) fun foo(x: (T) & (U)): unit @@ -13,9 +13,10 @@ fun iii(x: (IA) & (IB)): (IA) & (IB) fun uu(x: ((((U) & (T)) | ((U) & (P))) | ((V) & (T))) | ((V) & (P))): ((((U) & (T)) | ((U) & (P))) | ((V) & (T))) | ((V) & (P)) fun iiii(x: ((U) & (T)) & (V)): ((U) & (T)) & (V) fun arr(a: (MutArray) & (MutArray)): (MutArray) & (MutArray) -fun tt(x: ((U, T, )) & ((V, V, ))): ((U, T, )) & ((V, V, )) +fun tt(x: ([U, T]) & ([V, V])): ([U, T]) & ([V, V]) class A() {} class B() {} fun inter(c: (A) & (B)): (A) & (B) -//│ |#fun| |extend|‹|T|,| |U|›|(|first|#:| |T|,| |second|#:| |U|)|#:| |(|T|)| |&| |(|U|)|↵|#fun| |foo|‹|T|,| |U|›|(|x|#:| |(|T|)| |&| |(|U|)|)|#:| |unit|↵|#fun| |over|(|f|#:| |(|(|number|)| |=>| |string|)| |&| |(|(|object|)| |=>| |string|)|)|#:| |string|↵|#trait| |IA|(||)| |{|→|#let| |x|#:| |number|←|↵|}|↵|#trait| |IB|(||)| |{|→|#let| |y|#:| |number|←|↵|}|↵|#fun| |iii|(|x|#:| |(|IA|)| |&| |(|IB|)|)|#:| |(|IA|)| |&| |(|IB|)|↵|#fun| |uu|‹|U|,| |V|,| |T|,| |P|›|(|x|#:| |(|(|(|(|U|)| |&| |(|T|)|)| ||| |(|(|U|)| |&| |(|P|)|)|)| ||| |(|(|V|)| |&| |(|T|)|)|)| ||| |(|(|V|)| |&| |(|P|)|)|)|#:| |(|(|(|(|U|)| |&| |(|T|)|)| ||| |(|(|U|)| |&| |(|P|)|)|)| ||| |(|(|V|)| |&| |(|T|)|)|)| ||| |(|(|V|)| |&| |(|P|)|)|↵|#fun| |iiii|‹|U|,| |T|,| |V|›|(|x|#:| |(|(|U|)| |&| |(|T|)|)| |&| |(|V|)|)|#:| |(|(|U|)| |&| |(|T|)|)| |&| |(|V|)|↵|#fun| |arr|‹|U|,| |T|›|(|a|#:| |(|MutArray|‹|U|›|)| |&| |(|MutArray|‹|T|›|)|)|#:| |(|MutArray|‹|U|›|)| |&| |(|MutArray|‹|T|›|)|↵|#fun| |tt|‹|U|,| |T|,| |V|›|(|x|#:| |(|(|U|,| |T|,| |)|)| |&| |(|(|V|,| |V|,| |)|)|)|#:| |(|(|U|,| |T|,| |)|)| |&| |(|(|V|,| |V|,| |)|)|↵|#class| |A|(||)| |{||}|↵|#class| |B|(||)| |{||}|↵|#fun| |inter|(|c|#:| |(|A|)| |&| |(|B|)|)|#:| |(|A|)| |&| |(|B|)| -//│ Parsed: {fun extend: (first: T, second: U,) -> ((T,) & (U,)); fun foo: (x: (T,) & (U,),) -> unit; fun over: (f: (number -> string,) & (object -> string,),) -> string; trait IA() {let x: number}; trait IB() {let y: number}; fun iii: (x: (IA,) & (IB,),) -> ((IA,) & (IB,)); fun uu: (x: ((((U,) & (T,),) | ((U,) & (P,),),) | ((V,) & (T,),),) | ((V,) & (P,),),) -> (((((U,) & (T,),) | ((U,) & (P,),),) | ((V,) & (T,),),) | ((V,) & (P,),)); fun iiii: (x: ((U,) & (T,),) & (V,),) -> (((U,) & (T,),) & (V,)); fun arr: (a: (MutArray[U],) & (MutArray[T],),) -> ((MutArray[U],) & (MutArray[T],)); fun tt: (x: ((U, T,),) & ((V, V,),),) -> (((U, T,),) & ((V, V,),)); class A() {}; class B() {}; fun inter: (c: (A,) & (B,),) -> ((A,) & (B,))} +//│ |#fun| |extend|‹|T|,| |U|›|(|first|#:| |T|,| |second|#:| |U|)|#:| |(|T|)| |&| |(|U|)|↵|#fun| |foo|‹|T|,| |U|›|(|x|#:| |(|T|)| |&| |(|U|)|)|#:| |unit|↵|#fun| |over|(|f|#:| |(|(|number|)| |#=>| |string|)| |&| |(|(|object|)| |#=>| |string|)|)|#:| |string|↵|#trait| |IA|(||)| |{|→|#let| |x|#:| |number|←|↵|}|↵|#trait| |IB|(||)| |{|→|#let| |y|#:| |number|←|↵|}|↵|#fun| |iii|(|x|#:| |(|IA|)| |&| |(|IB|)|)|#:| |(|IA|)| |&| |(|IB|)|↵|#fun| |uu|‹|U|,| |V|,| |T|,| |P|›|(|x|#:| |(|(|(|(|U|)| |&| |(|T|)|)| ||| |(|(|U|)| |&| |(|P|)|)|)| ||| |(|(|V|)| |&| |(|T|)|)|)| ||| |(|(|V|)| |&| |(|P|)|)|)|#:| |(|(|(|(|U|)| |&| |(|T|)|)| ||| |(|(|U|)| |&| |(|P|)|)|)| ||| |(|(|V|)| |&| |(|T|)|)|)| ||| |(|(|V|)| |&| |(|P|)|)|↵|#fun| |iiii|‹|U|,| |T|,| |V|›|(|x|#:| |(|(|U|)| |&| |(|T|)|)| |&| |(|V|)|)|#:| |(|(|U|)| |&| |(|T|)|)| |&| |(|V|)|↵|#fun| |arr|‹|U|,| |T|›|(|a|#:| |(|MutArray|‹|U|›|)| |&| |(|MutArray|‹|T|›|)|)|#:| |(|MutArray|‹|U|›|)| |&| |(|MutArray|‹|T|›|)|↵|#fun| |tt|‹|U|,| |T|,| |V|›|(|x|#:| |(|[|U|,| |T|]|)| |&| |(|[|V|,| |V|]|)|)|#:| |(|[|U|,| |T|]|)| |&| |(|[|V|,| |V|]|)|↵|#class| |A|(||)| |{||}|↵|#class| |B|(||)| |{||}|↵|#fun| |inter|(|c|#:| |(|A|)| |&| |(|B|)|)|#:| |(|A|)| |&| |(|B|)| +//│ Parsed: {fun extend: (first: T, second: U) -> (T & U); fun foo: (x: T & U) -> unit; fun over: (f: number -> string & object -> string) -> string; trait IA() {let x: number}; trait IB() {let y: number}; fun iii: (x: IA & IB) -> (IA & IB); fun uu: (x: U & T | U & P | V & T | V & P) -> (U & T | U & P | V & T | V & P); fun iiii: (x: U & T & V) -> (U & T & V); fun arr: (a: MutArray[U] & MutArray[T]) -> (MutArray[U] & MutArray[T]); fun tt: (x: [U, T] & [V, V]) -> ([U, T] & [V, V]); class A() {}; class B() {}; fun inter: (c: A & B) -> (A & B)} +//│ diff --git a/ts2mls/js/src/test/diff/Literal.d.mls b/ts2mls/js/src/test/diff/Literal.d.mls index 1b84fa53f1..1ff16c9da3 100644 --- a/ts2mls/js/src/test/diff/Literal.d.mls +++ b/ts2mls/js/src/test/diff/Literal.d.mls @@ -1,7 +1,8 @@ -:NewParser +:NewDefs :ParseOnly let a: {a: "A",b: "B",} let num: {y: 114,} fun foo(x: {xx: "X",}): {yy: "Y",} //│ |#let| |a|#:| |{|a|#:| |"A"|,|b|#:| |"B"|,|}|↵|#let| |num|#:| |{|y|#:| |114|,|}|↵|#fun| |foo|(|x|#:| |{|xx|#:| |"X"|,|}|)|#:| |{|yy|#:| |"Y"|,|}| -//│ Parsed: {let a: {a: "A", b: "B"}; let num: {y: 114}; fun foo: (x: {xx: "X"},) -> {yy: "Y"}} +//│ Parsed: {let a: {a: "A", b: "B"}; let num: {y: 114}; fun foo: (x: {xx: "X"}) -> {yy: "Y"}} +//│ diff --git a/ts2mls/js/src/test/diff/MultiFiles.d.mls b/ts2mls/js/src/test/diff/MultiFiles.d.mls index e2d44c0bbc..d44b2d9131 100644 --- a/ts2mls/js/src/test/diff/MultiFiles.d.mls +++ b/ts2mls/js/src/test/diff/MultiFiles.d.mls @@ -1,4 +1,4 @@ -:NewParser +:NewDefs :ParseOnly fun multi1(x: number): number fun multi3(): unit @@ -6,7 +6,7 @@ class Foo(): Base {} trait AnotherBase() { let y: string } -namespace N { +module N { fun f(): unit fun g(): unit fun h(): unit @@ -18,5 +18,6 @@ trait Base() { } class AnotherFoo(): AnotherBase {} fun multi5(): unit -//│ |#fun| |multi1|(|x|#:| |number|)|#:| |number|↵|#fun| |multi3|(||)|#:| |unit|↵|#class| |Foo|(||)|#:| |Base| |{||}|↵|#trait| |AnotherBase|(||)| |{|→|#let| |y|#:| |string|←|↵|}|↵|#namespace| |N| |{|→|#fun| |f|(||)|#:| |unit|↵|#fun| |g|(||)|#:| |unit|↵|#fun| |h|(||)|#:| |unit|←|↵|}|↵|#fun| |multi2|(|x|#:| |string|)|#:| |string|↵|#fun| |multi4|(||)|#:| |unit|↵|#trait| |Base|(||)| |{|→|#let| |a|#:| |number|←|↵|}|↵|#class| |AnotherFoo|(||)|#:| |AnotherBase| |{||}|↵|#fun| |multi5|(||)|#:| |unit| -//│ Parsed: {fun multi1: (x: number,) -> number; fun multi3: () -> unit; class Foo(): Base {}; trait AnotherBase() {let y: string}; namespace N() {fun f: () -> unit; fun g: () -> unit; fun h: () -> unit}; fun multi2: (x: string,) -> string; fun multi4: () -> unit; trait Base() {let a: number}; class AnotherFoo(): AnotherBase {}; fun multi5: () -> unit} +//│ |#fun| |multi1|(|x|#:| |number|)|#:| |number|↵|#fun| |multi3|(||)|#:| |unit|↵|#class| |Foo|(||)|#:| |Base| |{||}|↵|#trait| |AnotherBase|(||)| |{|→|#let| |y|#:| |string|←|↵|}|↵|#module| |N| |{|→|#fun| |f|(||)|#:| |unit|↵|#fun| |g|(||)|#:| |unit|↵|#fun| |h|(||)|#:| |unit|←|↵|}|↵|#fun| |multi2|(|x|#:| |string|)|#:| |string|↵|#fun| |multi4|(||)|#:| |unit|↵|#trait| |Base|(||)| |{|→|#let| |a|#:| |number|←|↵|}|↵|#class| |AnotherFoo|(||)|#:| |AnotherBase| |{||}|↵|#fun| |multi5|(||)|#:| |unit| +//│ Parsed: {fun multi1: (x: number) -> number; fun multi3: () -> unit; class Foo(): Base {}; trait AnotherBase() {let y: string}; module N {fun f: () -> unit; fun g: () -> unit; fun h: () -> unit}; fun multi2: (x: string) -> string; fun multi4: () -> unit; trait Base() {let a: number}; class AnotherFoo(): AnotherBase {}; fun multi5: () -> unit} +//│ diff --git a/ts2mls/js/src/test/diff/Namespace.d.mls b/ts2mls/js/src/test/diff/Namespace.d.mls index 04d4d81763..a11aa1f25b 100644 --- a/ts2mls/js/src/test/diff/Namespace.d.mls +++ b/ts2mls/js/src/test/diff/Namespace.d.mls @@ -1,6 +1,6 @@ -:NewParser +:NewDefs :ParseOnly -namespace N1 { +module N1 { fun f(x: anything): number fun ff(y: anything): number class C() { @@ -9,13 +9,13 @@ namespace N1 { trait I() { fun f(): number } - namespace N2 { + module N2 { fun fff(x: (false) | (true)): number fun gg(c: N1.C): N1.C class BBB(): N1.C {} } } -namespace AA { +module AA { fun f(x: anything): string class C() { fun f(): unit @@ -23,10 +23,11 @@ namespace AA { trait I() { fun f(): number } - namespace N2 { + module N2 { } } fun f1(x: N1.C): N1.C fun f2(x: AA.C): AA.C -//│ |#namespace| |N1| |{|→|#fun| |f|(|x|#:| |anything|)|#:| |number|↵|#fun| |ff|(|y|#:| |anything|)|#:| |number|↵|#class| |C|(||)| |{|→|#fun| |f|(||)|#:| |unit|←|↵|}|↵|#trait| |I|(||)| |{|→|#fun| |f|(||)|#:| |number|←|↵|}|↵|#namespace| |N2| |{|→|#fun| |fff|(|x|#:| |(|false|)| ||| |(|true|)|)|#:| |number|↵|#fun| |gg|(|c|#:| |N1|.C|)|#:| |N1|.C|↵|#class| |BBB|(||)|#:| |N1|.C| |{||}|←|↵|}|←|↵|}|↵|#namespace| |AA| |{|→|#fun| |f|(|x|#:| |anything|)|#:| |string|↵|#class| |C|(||)| |{|→|#fun| |f|(||)|#:| |unit|←|↵|}|↵|#trait| |I|(||)| |{|→|#fun| |f|(||)|#:| |number|←|↵|}|↵|#namespace| |N2| |{|↵|}|←|↵|}|↵|#fun| |f1|(|x|#:| |N1|.C|)|#:| |N1|.C|↵|#fun| |f2|(|x|#:| |AA|.C|)|#:| |AA|.C| -//│ Parsed: {namespace N1() {fun f: (x: anything,) -> number; fun ff: (y: anything,) -> number; class C() {fun f: () -> unit}; trait I() {fun f: () -> number}; namespace N2() {fun fff: (x: (false,) | (true,),) -> number; fun gg: (c: N1.C,) -> N1.C; class BBB(): (N1).C {}}}; namespace AA() {fun f: (x: anything,) -> string; class C() {fun f: () -> unit}; trait I() {fun f: () -> number}; namespace N2() {}}; fun f1: (x: N1.C,) -> N1.C; fun f2: (x: AA.C,) -> AA.C} +//│ |#module| |N1| |{|→|#fun| |f|(|x|#:| |anything|)|#:| |number|↵|#fun| |ff|(|y|#:| |anything|)|#:| |number|↵|#class| |C|(||)| |{|→|#fun| |f|(||)|#:| |unit|←|↵|}|↵|#trait| |I|(||)| |{|→|#fun| |f|(||)|#:| |number|←|↵|}|↵|#module| |N2| |{|→|#fun| |fff|(|x|#:| |(|false|)| ||| |(|true|)|)|#:| |number|↵|#fun| |gg|(|c|#:| |N1|.C|)|#:| |N1|.C|↵|#class| |BBB|(||)|#:| |N1|.C| |{||}|←|↵|}|←|↵|}|↵|#module| |AA| |{|→|#fun| |f|(|x|#:| |anything|)|#:| |string|↵|#class| |C|(||)| |{|→|#fun| |f|(||)|#:| |unit|←|↵|}|↵|#trait| |I|(||)| |{|→|#fun| |f|(||)|#:| |number|←|↵|}|↵|#module| |N2| |{|↵|}|←|↵|}|↵|#fun| |f1|(|x|#:| |N1|.C|)|#:| |N1|.C|↵|#fun| |f2|(|x|#:| |AA|.C|)|#:| |AA|.C| +//│ Parsed: {module N1 {fun f: (x: anything) -> number; fun ff: (y: anything) -> number; class C() {fun f: () -> unit}; trait I() {fun f: () -> number}; module N2 {fun fff: (x: bool) -> number; fun gg: (c: N1.C) -> N1.C; class BBB(): N1.C {}}}; module AA {fun f: (x: anything) -> string; class C() {fun f: () -> unit}; trait I() {fun f: () -> number}; module N2 {}}; fun f1: (x: N1.C) -> N1.C; fun f2: (x: AA.C) -> AA.C} +//│ diff --git a/ts2mls/js/src/test/diff/Optional.d.mls b/ts2mls/js/src/test/diff/Optional.d.mls index dbcf0d2a11..10ed5607fa 100644 --- a/ts2mls/js/src/test/diff/Optional.d.mls +++ b/ts2mls/js/src/test/diff/Optional.d.mls @@ -1,4 +1,4 @@ -:NewParser +:NewDefs :ParseOnly fun buildName(firstName: string, lastName: (string) | (undefined)): string fun buildName2(firstName: string, lastName: (string) | (undefined)): string @@ -13,12 +13,13 @@ fun getOrElse(arr: (MutArray) | (undefined)): object class ABC() {} fun testABC(abc: (ABC) | (undefined)): unit fun testSquareConfig(conf: (SquareConfig) | (undefined)): unit -fun err(msg: ((number, string, )) | (undefined)): unit +fun err(msg: ([number, string]) | (undefined)): unit fun toStr(x: (((number) | (false)) | (true)) | (undefined)): string fun boo(x: ((T) & (U)) | (undefined)): unit class B() { let b: T } fun boom(b: (B) | (undefined)): anything -//│ |#fun| |buildName|(|firstName|#:| |string|,| |lastName|#:| |(|string|)| ||| |(|undefined|)|)|#:| |string|↵|#fun| |buildName2|(|firstName|#:| |string|,| |lastName|#:| |(|string|)| ||| |(|undefined|)|)|#:| |string|↵|#fun| |buildName3|(|firstName|#:| |string|,| |lastName|#:| |MutArray|‹|string|›|)|#:| |string|↵|#fun| |buildName4|(|firstName|#:| |string|,| |lastName|#:| |MutArray|‹|anything|›|)|#:| |string|↵|#trait| |SquareConfig|(||)| |{|→|#let| |color|#:| |(|string|)| ||| |(|undefined|)|↵|#let| |width|#:| |(|number|)| ||| |(|undefined|)|←|↵|}|↵|#fun| |did|(|x|#:| |number|,| |f|#:| |(|(|number|)| |=>| |number|)| ||| |(|undefined|)|)|#:| |number|↵|#fun| |getOrElse|(|arr|#:| |(|MutArray|‹|object|›|)| ||| |(|undefined|)|)|#:| |object|↵|#class| |ABC|(||)| |{||}|↵|#fun| |testABC|(|abc|#:| |(|ABC|)| ||| |(|undefined|)|)|#:| |unit|↵|#fun| |testSquareConfig|(|conf|#:| |(|SquareConfig|)| ||| |(|undefined|)|)|#:| |unit|↵|#fun| |err|(|msg|#:| |(|(|number|,| |string|,| |)|)| ||| |(|undefined|)|)|#:| |unit|↵|#fun| |toStr|(|x|#:| |(|(|(|number|)| ||| |(|false|)|)| ||| |(|true|)|)| ||| |(|undefined|)|)|#:| |string|↵|#fun| |boo|‹|T|,| |U|›|(|x|#:| |(|(|T|)| |&| |(|U|)|)| ||| |(|undefined|)|)|#:| |unit|↵|#class| |B|‹|T|›|(||)| |{|→|#let| |b|#:| |T|←|↵|}|↵|#fun| |boom|(|b|#:| |(|B|‹|nothing|›|)| ||| |(|undefined|)|)|#:| |anything| -//│ Parsed: {fun buildName: (firstName: string, lastName: (string,) | (undefined,),) -> string; fun buildName2: (firstName: string, lastName: (string,) | (undefined,),) -> string; fun buildName3: (firstName: string, lastName: MutArray[string],) -> string; fun buildName4: (firstName: string, lastName: MutArray[anything],) -> string; trait SquareConfig() {let color: (string,) | (undefined,); let width: (number,) | (undefined,)}; fun did: (x: number, f: (number -> number,) | (undefined,),) -> number; fun getOrElse: (arr: (MutArray[object],) | (undefined,),) -> object; class ABC() {}; fun testABC: (abc: (ABC,) | (undefined,),) -> unit; fun testSquareConfig: (conf: (SquareConfig,) | (undefined,),) -> unit; fun err: (msg: ((number, string,),) | (undefined,),) -> unit; fun toStr: (x: (((number,) | (false,),) | (true,),) | (undefined,),) -> string; fun boo: (x: ((T,) & (U,),) | (undefined,),) -> unit; class B‹T›() {let b: T}; fun boom: (b: (B[nothing],) | (undefined,),) -> anything} +//│ |#fun| |buildName|(|firstName|#:| |string|,| |lastName|#:| |(|string|)| ||| |(|#undefined|)|)|#:| |string|↵|#fun| |buildName2|(|firstName|#:| |string|,| |lastName|#:| |(|string|)| ||| |(|#undefined|)|)|#:| |string|↵|#fun| |buildName3|(|firstName|#:| |string|,| |lastName|#:| |MutArray|‹|string|›|)|#:| |string|↵|#fun| |buildName4|(|firstName|#:| |string|,| |lastName|#:| |MutArray|‹|anything|›|)|#:| |string|↵|#trait| |SquareConfig|(||)| |{|→|#let| |color|#:| |(|string|)| ||| |(|#undefined|)|↵|#let| |width|#:| |(|number|)| ||| |(|#undefined|)|←|↵|}|↵|#fun| |did|(|x|#:| |number|,| |f|#:| |(|(|number|)| |#=>| |number|)| ||| |(|#undefined|)|)|#:| |number|↵|#fun| |getOrElse|(|arr|#:| |(|MutArray|‹|object|›|)| ||| |(|#undefined|)|)|#:| |object|↵|#class| |ABC|(||)| |{||}|↵|#fun| |testABC|(|abc|#:| |(|ABC|)| ||| |(|#undefined|)|)|#:| |unit|↵|#fun| |testSquareConfig|(|conf|#:| |(|SquareConfig|)| ||| |(|#undefined|)|)|#:| |unit|↵|#fun| |err|(|msg|#:| |(|[|number|,| |string|]|)| ||| |(|#undefined|)|)|#:| |unit|↵|#fun| |toStr|(|x|#:| |(|(|(|number|)| ||| |(|false|)|)| ||| |(|true|)|)| ||| |(|#undefined|)|)|#:| |string|↵|#fun| |boo|‹|T|,| |U|›|(|x|#:| |(|(|T|)| |&| |(|U|)|)| ||| |(|#undefined|)|)|#:| |unit|↵|#class| |B|‹|T|›|(||)| |{|→|#let| |b|#:| |T|←|↵|}|↵|#fun| |boom|(|b|#:| |(|B|‹|nothing|›|)| ||| |(|#undefined|)|)|#:| |anything| +//│ Parsed: {fun buildName: (firstName: string, lastName: string | ()) -> string; fun buildName2: (firstName: string, lastName: string | ()) -> string; fun buildName3: (firstName: string, lastName: MutArray[string]) -> string; fun buildName4: (firstName: string, lastName: MutArray[anything]) -> string; trait SquareConfig() {let color: string | (); let width: number | ()}; fun did: (x: number, f: number -> number | ()) -> number; fun getOrElse: (arr: MutArray[object] | ()) -> object; class ABC() {}; fun testABC: (abc: ABC | ()) -> unit; fun testSquareConfig: (conf: SquareConfig | ()) -> unit; fun err: (msg: [number, string] | ()) -> unit; fun toStr: (x: number | false | true | ()) -> string; fun boo: (x: T & U | ()) -> unit; class B‹T›() {let b: T}; fun boom: (b: B[nothing] | ()) -> anything} +//│ diff --git a/ts2mls/js/src/test/diff/Overload.d.mls b/ts2mls/js/src/test/diff/Overload.d.mls index 0e89921fd3..b8977f71e5 100644 --- a/ts2mls/js/src/test/diff/Overload.d.mls +++ b/ts2mls/js/src/test/diff/Overload.d.mls @@ -1,4 +1,4 @@ -:NewParser +:NewDefs :ParseOnly fun f: ((number) => string) & ((string) => string) class M() { @@ -12,15 +12,16 @@ class N() {} fun id: ((M) => unit) & ((N) => unit) fun tst: (({z: number,}) => {y: string,}) & (({z: (false) | (true),}) => {y: string,}) fun op: ((number) => ((number) | (undefined)) => unit) & ((number) => (((false) | (true)) | (undefined)) => unit) -fun swap: (((number, string, )) => (number, string, )) & (((string, number, )) => (number, string, )) +fun swap: (([number, string]) => [number, string]) & (([string, number]) => [number, string]) fun u: ((((number) | (false)) | (true)) => string) & ((object) => string) fun doSome(x: anything): unit /* warning: the overload of function doSome is not supported yet. */ -namespace XX { +module XX { fun f(x: T, n: anything): string /* warning: the overload of function f is not supported yet. */ } class WWW() { fun F(x: T): anything /* warning: the overload of function F is not supported yet. */ } fun baz(): anything /* warning: the overload of function baz is not supported yet. */ -//│ |#fun| |f|#:| |(|(|number|)| |=>| |string|)| |&| |(|(|string|)| |=>| |string|)|↵|#class| |M|(||)| |{|→|#let| |foo|#:| |(|(|number|)| |=>| |string|)| |&| |(|(|string|)| |=>| |string|)|←|↵|}|↵|#fun| |app|#:| |(|(|(|string|)| |=>| |unit|)| |=>| |(|number|)| |=>| |unit|)| |&| |(|(|(|string|)| |=>| |unit|)| |=>| |(|string|)| |=>| |unit|)|↵|#fun| |create|#:| |(|(|number|)| |=>| |unit| |=>| |(|false|)| ||| |(|true|)|)| |&| |(|(|(|false|)| ||| |(|true|)|)| |=>| |unit| |=>| |(|false|)| ||| |(|true|)|)|↵|#fun| |g0|#:| |(|(|MutArray|‹|string|›|)| |=>| |string|)| |&| |(|(|MutArray|‹|object|›|)| |=>| |string|)|↵|#fun| |db|#:| |(|(|number|)| |=>| |MutArray|‹|number|›|)| |&| |(|(|object|)| |=>| |MutArray|‹|number|›|)|↵|#class| |N|(||)| |{||}|↵|#fun| |id|#:| |(|(|M|)| |=>| |unit|)| |&| |(|(|N|)| |=>| |unit|)|↵|#fun| |tst|#:| |(|(|{|z|#:| |number|,|}|)| |=>| |{|y|#:| |string|,|}|)| |&| |(|(|{|z|#:| |(|false|)| ||| |(|true|)|,|}|)| |=>| |{|y|#:| |string|,|}|)|↵|#fun| |op|#:| |(|(|number|)| |=>| |(|(|number|)| ||| |(|undefined|)|)| |=>| |unit|)| |&| |(|(|number|)| |=>| |(|(|(|false|)| ||| |(|true|)|)| ||| |(|undefined|)|)| |=>| |unit|)|↵|#fun| |swap|#:| |(|(|(|number|,| |string|,| |)|)| |=>| |(|number|,| |string|,| |)|)| |&| |(|(|(|string|,| |number|,| |)|)| |=>| |(|number|,| |string|,| |)|)|↵|#fun| |u|#:| |(|(|(|(|number|)| ||| |(|false|)|)| ||| |(|true|)|)| |=>| |string|)| |&| |(|(|object|)| |=>| |string|)|↵|#fun| |doSome|‹|T|,| |U|›|(|x|#:| |anything|)|#:| |unit| |/* warning: the overload of function doSome is not supported yet. */|↵|#namespace| |XX| |{|→|#fun| |f|‹|T|›|(|x|#:| |T|,| |n|#:| |anything|)|#:| |string| |/* warning: the overload of function f is not supported yet. */|←|↵|}|↵|#class| |WWW|(||)| |{|→|#fun| |F|‹|T|›|(|x|#:| |T|)|#:| |anything| |/* warning: the overload of function F is not supported yet. */|←|↵|}|↵|#fun| |baz|(||)|#:| |anything| |/* warning: the overload of function baz is not supported yet. */| -//│ Parsed: {fun f: (number -> string,) & (string -> string,); class M() {let foo: (number -> string,) & (string -> string,)}; fun app: ((string -> unit) -> number -> unit,) & ((string -> unit) -> string -> unit,); fun create: (number -> unit -> ((false,) | (true,)),) & (((false,) | (true,)) -> unit -> ((false,) | (true,)),); fun g0: (MutArray[string] -> string,) & (MutArray[object] -> string,); fun db: (number -> MutArray[number],) & (object -> MutArray[number],); class N() {}; fun id: (M -> unit,) & (N -> unit,); fun tst: ({z: number} -> {y: string},) & ({z: (false,) | (true,)} -> {y: string},); fun op: (number -> ((number,) | (undefined,)) -> unit,) & (number -> (((false,) | (true,),) | (undefined,)) -> unit,); fun swap: ((number, string,) -> (number, string,),) & ((string, number,) -> (number, string,),); fun u: ((((number,) | (false,),) | (true,)) -> string,) & (object -> string,); fun doSome: (x: anything,) -> unit; namespace XX() {fun f: (x: T, n: anything,) -> string}; class WWW() {fun F: (x: T,) -> anything}; fun baz: () -> anything} +//│ |#fun| |f|#:| |(|(|number|)| |#=>| |string|)| |&| |(|(|string|)| |#=>| |string|)|↵|#class| |M|(||)| |{|→|#let| |foo|#:| |(|(|number|)| |#=>| |string|)| |&| |(|(|string|)| |#=>| |string|)|←|↵|}|↵|#fun| |app|#:| |(|(|(|string|)| |#=>| |unit|)| |#=>| |(|number|)| |#=>| |unit|)| |&| |(|(|(|string|)| |#=>| |unit|)| |#=>| |(|string|)| |#=>| |unit|)|↵|#fun| |create|#:| |(|(|number|)| |#=>| |unit| |#=>| |(|false|)| ||| |(|true|)|)| |&| |(|(|(|false|)| ||| |(|true|)|)| |#=>| |unit| |#=>| |(|false|)| ||| |(|true|)|)|↵|#fun| |g0|#:| |(|(|MutArray|‹|string|›|)| |#=>| |string|)| |&| |(|(|MutArray|‹|object|›|)| |#=>| |string|)|↵|#fun| |db|#:| |(|(|number|)| |#=>| |MutArray|‹|number|›|)| |&| |(|(|object|)| |#=>| |MutArray|‹|number|›|)|↵|#class| |N|(||)| |{||}|↵|#fun| |id|#:| |(|(|M|)| |#=>| |unit|)| |&| |(|(|N|)| |#=>| |unit|)|↵|#fun| |tst|#:| |(|(|{|z|#:| |number|,|}|)| |#=>| |{|y|#:| |string|,|}|)| |&| |(|(|{|z|#:| |(|false|)| ||| |(|true|)|,|}|)| |#=>| |{|y|#:| |string|,|}|)|↵|#fun| |op|#:| |(|(|number|)| |#=>| |(|(|number|)| ||| |(|#undefined|)|)| |#=>| |unit|)| |&| |(|(|number|)| |#=>| |(|(|(|false|)| ||| |(|true|)|)| ||| |(|#undefined|)|)| |#=>| |unit|)|↵|#fun| |swap|#:| |(|(|[|number|,| |string|]|)| |#=>| |[|number|,| |string|]|)| |&| |(|(|[|string|,| |number|]|)| |#=>| |[|number|,| |string|]|)|↵|#fun| |u|#:| |(|(|(|(|number|)| ||| |(|false|)|)| ||| |(|true|)|)| |#=>| |string|)| |&| |(|(|object|)| |#=>| |string|)|↵|#fun| |doSome|‹|T|,| |U|›|(|x|#:| |anything|)|#:| |unit| |/* warning: the overload of function doSome is not supported yet. */|↵|#module| |XX| |{|→|#fun| |f|‹|T|›|(|x|#:| |T|,| |n|#:| |anything|)|#:| |string| |/* warning: the overload of function f is not supported yet. */|←|↵|}|↵|#class| |WWW|(||)| |{|→|#fun| |F|‹|T|›|(|x|#:| |T|)|#:| |anything| |/* warning: the overload of function F is not supported yet. */|←|↵|}|↵|#fun| |baz|(||)|#:| |anything| |/* warning: the overload of function baz is not supported yet. */| +//│ Parsed: {fun f: number -> string & string -> string; class M() {let foo: number -> string & string -> string}; fun app: (string -> unit) -> number -> unit & (string -> unit) -> string -> unit; fun create: number -> unit -> bool & bool -> unit -> bool; fun g0: MutArray[string] -> string & MutArray[object] -> string; fun db: number -> MutArray[number] & object -> MutArray[number]; class N() {}; fun id: M -> unit & N -> unit; fun tst: {z: number} -> {y: string} & {z: bool} -> {y: string}; fun op: number -> (number | ()) -> unit & number -> (false | true | ()) -> unit; fun swap: ([number, string]) -> [number, string] & ([string, number]) -> [number, string]; fun u: (number | false | true) -> string & object -> string; fun doSome: (x: anything) -> unit; module XX {fun f: (x: T, n: anything) -> string}; class WWW() {fun F: (x: T) -> anything}; fun baz: () -> anything} +//│ diff --git a/ts2mls/js/src/test/diff/Tuple.d.mls b/ts2mls/js/src/test/diff/Tuple.d.mls index 551620feb3..672f433a2c 100644 --- a/ts2mls/js/src/test/diff/Tuple.d.mls +++ b/ts2mls/js/src/test/diff/Tuple.d.mls @@ -1,20 +1,21 @@ -:NewParser +:NewDefs :ParseOnly -fun key(x: (string, (false) | (true), )): string -fun value(x: (string, (false) | (true), )): (false) | (true) -fun third(x: (number, number, number, )): number -fun vec2(x: number, y: number): (number, number, ) -fun twoFunctions(ff: ((number) => number, (number) => number, ), x: number): number -fun tupleIt(x: string): (unit => string, ) -fun s(flag: (false) | (true)): ((string) | (number), ((number) | (false)) | (true), ) -fun s2(t: ((false) | (true), (string) | (number), )): (string) | (number) -fun ex(x: T, y: U): (T, U, (T) & (U), ) -fun foo(x: ((T) & (U), )): unit -fun conv(x: {y: number,}): ({y: number,}, {z: string,}, ) +fun key(x: [string, (false) | (true)]): string +fun value(x: [string, (false) | (true)]): (false) | (true) +fun third(x: [number, number, number]): number +fun vec2(x: number, y: number): [number, number] +fun twoFunctions(ff: [(number) => number, (number) => number], x: number): number +fun tupleIt(x: string): [unit => string] +fun s(flag: (false) | (true)): [(string) | (number), ((number) | (false)) | (true)] +fun s2(t: [(false) | (true), (string) | (number)]): (string) | (number) +fun ex(x: T, y: U): [T, U, (T) & (U)] +fun foo(x: [(T) & (U)]): unit +fun conv(x: {y: number,}): [{y: number,}, {z: string,}] class A() { let x: number } class B() {} -fun swap(x: (A, B, )): (B, A, ) -//│ |#fun| |key|(|x|#:| |(|string|,| |(|false|)| ||| |(|true|)|,| |)|)|#:| |string|↵|#fun| |value|(|x|#:| |(|string|,| |(|false|)| ||| |(|true|)|,| |)|)|#:| |(|false|)| ||| |(|true|)|↵|#fun| |third|(|x|#:| |(|number|,| |number|,| |number|,| |)|)|#:| |number|↵|#fun| |vec2|(|x|#:| |number|,| |y|#:| |number|)|#:| |(|number|,| |number|,| |)|↵|#fun| |twoFunctions|(|ff|#:| |(|(|number|)| |=>| |number|,| |(|number|)| |=>| |number|,| |)|,| |x|#:| |number|)|#:| |number|↵|#fun| |tupleIt|(|x|#:| |string|)|#:| |(|unit| |=>| |string|,| |)|↵|#fun| |s|(|flag|#:| |(|false|)| ||| |(|true|)|)|#:| |(|(|string|)| ||| |(|number|)|,| |(|(|number|)| ||| |(|false|)|)| ||| |(|true|)|,| |)|↵|#fun| |s2|(|t|#:| |(|(|false|)| ||| |(|true|)|,| |(|string|)| ||| |(|number|)|,| |)|)|#:| |(|string|)| ||| |(|number|)|↵|#fun| |ex|‹|T|,| |U|›|(|x|#:| |T|,| |y|#:| |U|)|#:| |(|T|,| |U|,| |(|T|)| |&| |(|U|)|,| |)|↵|#fun| |foo|‹|T|,| |U|›|(|x|#:| |(|(|T|)| |&| |(|U|)|,| |)|)|#:| |unit|↵|#fun| |conv|(|x|#:| |{|y|#:| |number|,|}|)|#:| |(|{|y|#:| |number|,|}|,| |{|z|#:| |string|,|}|,| |)|↵|#class| |A|(||)| |{|→|#let| |x|#:| |number|←|↵|}|↵|#class| |B|(||)| |{||}|↵|#fun| |swap|(|x|#:| |(|A|,| |B|,| |)|)|#:| |(|B|,| |A|,| |)| -//│ Parsed: {fun key: (x: (string, (false,) | (true,),),) -> string; fun value: (x: (string, (false,) | (true,),),) -> ((false,) | (true,)); fun third: (x: (number, number, number,),) -> number; fun vec2: (x: number, y: number,) -> (number, number,); fun twoFunctions: (ff: (number -> number, number -> number,), x: number,) -> number; fun tupleIt: (x: string,) -> (unit -> string,); fun s: (flag: (false,) | (true,),) -> ((string,) | (number,), ((number,) | (false,),) | (true,),); fun s2: (t: ((false,) | (true,), (string,) | (number,),),) -> ((string,) | (number,)); fun ex: (x: T, y: U,) -> (T, U, (T,) & (U,),); fun foo: (x: ((T,) & (U,),),) -> unit; fun conv: (x: {y: number},) -> ({y: number}, {z: string},); class A() {let x: number}; class B() {}; fun swap: (x: (A, B,),) -> (B, A,)} +fun swap(x: [A, B]): [B, A] +//│ |#fun| |key|(|x|#:| |[|string|,| |(|false|)| ||| |(|true|)|]|)|#:| |string|↵|#fun| |value|(|x|#:| |[|string|,| |(|false|)| ||| |(|true|)|]|)|#:| |(|false|)| ||| |(|true|)|↵|#fun| |third|(|x|#:| |[|number|,| |number|,| |number|]|)|#:| |number|↵|#fun| |vec2|(|x|#:| |number|,| |y|#:| |number|)|#:| |[|number|,| |number|]|↵|#fun| |twoFunctions|(|ff|#:| |[|(|number|)| |#=>| |number|,| |(|number|)| |#=>| |number|]|,| |x|#:| |number|)|#:| |number|↵|#fun| |tupleIt|(|x|#:| |string|)|#:| |[|unit| |#=>| |string|]|↵|#fun| |s|(|flag|#:| |(|false|)| ||| |(|true|)|)|#:| |[|(|string|)| ||| |(|number|)|,| |(|(|number|)| ||| |(|false|)|)| ||| |(|true|)|]|↵|#fun| |s2|(|t|#:| |[|(|false|)| ||| |(|true|)|,| |(|string|)| ||| |(|number|)|]|)|#:| |(|string|)| ||| |(|number|)|↵|#fun| |ex|‹|T|,| |U|›|(|x|#:| |T|,| |y|#:| |U|)|#:| |[|T|,| |U|,| |(|T|)| |&| |(|U|)|]|↵|#fun| |foo|‹|T|,| |U|›|(|x|#:| |[|(|T|)| |&| |(|U|)|]|)|#:| |unit|↵|#fun| |conv|(|x|#:| |{|y|#:| |number|,|}|)|#:| |[|{|y|#:| |number|,|}|,| |{|z|#:| |string|,|}|]|↵|#class| |A|(||)| |{|→|#let| |x|#:| |number|←|↵|}|↵|#class| |B|(||)| |{||}|↵|#fun| |swap|(|x|#:| |[|A|,| |B|]|)|#:| |[|B|,| |A|]| +//│ Parsed: {fun key: (x: [string, bool]) -> string; fun value: (x: [string, bool]) -> bool; fun third: (x: [number, number, number]) -> number; fun vec2: (x: number, y: number) -> [number, number]; fun twoFunctions: (ff: [number -> number, number -> number], x: number) -> number; fun tupleIt: (x: string) -> [unit -> string]; fun s: (flag: bool) -> [string | number, number | false | true]; fun s2: (t: [bool, string | number]) -> (string | number); fun ex: (x: T, y: U) -> [T, U, T & U]; fun foo: (x: [T & U]) -> unit; fun conv: (x: {y: number}) -> [{y: number}, {z: string}]; class A() {let x: number}; class B() {}; fun swap: (x: [A, B]) -> [B, A]} +//│ diff --git a/ts2mls/js/src/test/diff/Type.d.mls b/ts2mls/js/src/test/diff/Type.d.mls index c1f448a0bc..1839cd27cb 100644 --- a/ts2mls/js/src/test/diff/Type.d.mls +++ b/ts2mls/js/src/test/diff/Type.d.mls @@ -1,4 +1,4 @@ -:NewParser +:NewDefs :ParseOnly trait None() { let _tag: "None" @@ -9,7 +9,7 @@ trait Some() { } type Option = (None) | (Some) type Func = (number) => number -type S2 = (string, string, ) +type S2 = [string, string] trait I1() {} trait I2() {} type I3 = (I1) & (I2) @@ -17,8 +17,8 @@ type StringArray = Array type SomeInterface = {x: number,y: number,} class ABC() {} type DEF = ABC -type TP = (A, B, C, ) -namespace NA { +type TP = [A, B, C] +module NA { fun fb(b: string): unit type B = string } @@ -28,5 +28,6 @@ class NC() { type G = ABC let none: {_tag: "None",} fun some(a: A): (None) | (Some) -//│ |#trait| |None|(||)| |{|→|#let| |_tag|#:| |"None"|←|↵|}|↵|#trait| |Some|‹|A|›|(||)| |{|→|#let| |_tag|#:| |"Some"|↵|#let| |value|#:| |A|←|↵|}|↵|#type| |Option|‹|A|›| |#=| |(|None|)| ||| |(|Some|‹|A|›|)|↵|#type| |Func| |#=| |(|number|)| |=>| |number|↵|#type| |S2| |#=| |(|string|,| |string|,| |)|↵|#trait| |I1|(||)| |{||}|↵|#trait| |I2|(||)| |{||}|↵|#type| |I3| |#=| |(|I1|)| |&| |(|I2|)|↵|#type| |StringArray| |#=| |Array|‹|string|›|↵|#type| |SomeInterface| |#=| |{|x|#:| |number|,|y|#:| |number|,|}|↵|#class| |ABC|(||)| |{||}|↵|#type| |DEF| |#=| |ABC|↵|#type| |TP|‹|A|,| |B|,| |C|›| |#=| |(|A|,| |B|,| |C|,| |)|↵|#namespace| |NA| |{|→|#fun| |fb|(|b|#:| |string|)|#:| |unit|↵|#type| |B| |#=| |string|←|↵|}|↵|#class| |NC|(||)| |{|→|#let| |b|#:| |string|←|↵|}|↵|#type| |G| |#=| |ABC|↵|#let| |none|#:| |{|_tag|#:| |"None"|,|}|↵|#fun| |some|‹|A|›|(|a|#:| |A|)|#:| |(|None|)| ||| |(|Some|‹|A|›|)| -//│ Parsed: {trait None() {let _tag: "None"}; trait Some‹A›() {let _tag: "Some"; let value: A}; type alias Option‹A›() = | (None,) (Some‹A›,) {}; type alias Func() = (number,) => number {}; type alias S2() = '(' string, string, ')' {}; trait I1() {}; trait I2() {}; type alias I3() = & (I1,) (I2,) {}; type alias StringArray() = Array‹string› {}; type alias SomeInterface() = '{' {x: number, y: number} '}' {}; class ABC() {}; type alias DEF() = ABC {}; type alias TP‹A, B, C›() = '(' A, B, C, ')' {}; namespace NA() {fun fb: (b: string,) -> unit; type alias B() = string {}}; class NC() {let b: string}; type alias G() = ABC {}; let none: {_tag: "None"}; fun some: (a: A,) -> ((None,) | (Some[A],))} +//│ |#trait| |None|(||)| |{|→|#let| |_tag|#:| |"None"|←|↵|}|↵|#trait| |Some|‹|A|›|(||)| |{|→|#let| |_tag|#:| |"Some"|↵|#let| |value|#:| |A|←|↵|}|↵|#type| |Option|‹|A|›| |#=| |(|None|)| ||| |(|Some|‹|A|›|)|↵|#type| |Func| |#=| |(|number|)| |#=>| |number|↵|#type| |S2| |#=| |[|string|,| |string|]|↵|#trait| |I1|(||)| |{||}|↵|#trait| |I2|(||)| |{||}|↵|#type| |I3| |#=| |(|I1|)| |&| |(|I2|)|↵|#type| |StringArray| |#=| |Array|‹|string|›|↵|#type| |SomeInterface| |#=| |{|x|#:| |number|,|y|#:| |number|,|}|↵|#class| |ABC|(||)| |{||}|↵|#type| |DEF| |#=| |ABC|↵|#type| |TP|‹|A|,| |B|,| |C|›| |#=| |[|A|,| |B|,| |C|]|↵|#module| |NA| |{|→|#fun| |fb|(|b|#:| |string|)|#:| |unit|↵|#type| |B| |#=| |string|←|↵|}|↵|#class| |NC|(||)| |{|→|#let| |b|#:| |string|←|↵|}|↵|#type| |G| |#=| |ABC|↵|#let| |none|#:| |{|_tag|#:| |"None"|,|}|↵|#fun| |some|‹|A|›|(|a|#:| |A|)|#:| |(|None|)| ||| |(|Some|‹|A|›|)| +//│ Parsed: {trait None() {let _tag: "None"}; trait Some‹A›() {let _tag: "Some"; let value: A}; type alias Option‹A›: None | Some[A] {}; type alias Func: number -> number {}; type alias S2: [string, string] {}; trait I1() {}; trait I2() {}; type alias I3: I1 & I2 {}; type alias StringArray: Array[string] {}; type alias SomeInterface: {x: number, y: number} {}; class ABC() {}; type alias DEF: ABC {}; type alias TP‹A, B, C›: [A, B, C] {}; module NA {fun fb: (b: string) -> unit; type alias B: string {}}; class NC() {let b: string}; type alias G: ABC {}; let none: {_tag: "None"}; fun some: (a: A) -> (None | Some[A])} +//│ diff --git a/ts2mls/js/src/test/diff/TypeParameter.d.mls b/ts2mls/js/src/test/diff/TypeParameter.d.mls index 357be3f678..0f9147888a 100644 --- a/ts2mls/js/src/test/diff/TypeParameter.d.mls +++ b/ts2mls/js/src/test/diff/TypeParameter.d.mls @@ -1,4 +1,4 @@ -:NewParser +:NewDefs :ParseOnly fun inc(x: T): number class CC() { @@ -26,4 +26,5 @@ class FFF() { fun fff(p: FFF, s: string): unit fun getFFF(): FFF //│ |#fun| |inc|‹|T|›|(|x|#:| |T|)|#:| |number|↵|#class| |CC|‹|T|›|(||)| |{|→|#fun| |print|(|s|#:| |T|)|#:| |unit|←|↵|}|↵|#fun| |con|‹|U|,| |T|›|(|t|#:| |T|)|#:| |U|↵|#class| |Printer|‹|T|›|(||)| |{|→|#fun| |print|(|t|#:| |T|)|#:| |unit|←|↵|}|↵|#fun| |setStringPrinter|(|p|#:| |Printer|‹|string|›|)|#:| |unit|↵|#fun| |getStringPrinter|(||)|#:| |Printer|‹|string|›|↵|#fun| |foo|‹|T|›|(|p|#:| |Printer|‹|T|›|,| |x|#:| |T|)|#:| |T|↵|#fun| |foo2|‹|T|›|(|p|#:| |Printer|‹|T|›|,| |x|#:| |T|)|#:| |T|↵|#class| |F|‹|T|›|(||)| |{|→|#let| |x|#:| |T|↵|#fun| |GG|‹|U|›|(|y|#:| |U|)|#:| |T|←|↵|}|↵|#trait| |I|‹|T|›|(||)| |{|→|#let| |x|#:| |T|↵|#fun| |GG|‹|U|›|(|y|#:| |U|)|#:| |T|←|↵|}|↵|#class| |FFF|‹|T|›|(||)| |{|→|#fun| |fff|(|x|#:| |T|)|#:| |unit|←|↵|}|↵|#fun| |fff|(|p|#:| |FFF|‹|string|›|,| |s|#:| |string|)|#:| |unit|↵|#fun| |getFFF|(||)|#:| |FFF|‹|number|›| -//│ Parsed: {fun inc: (x: T,) -> number; class CC‹T›() {fun print: (s: T,) -> unit}; fun con: (t: T,) -> U; class Printer‹T›() {fun print: (t: T,) -> unit}; fun setStringPrinter: (p: Printer[string],) -> unit; fun getStringPrinter: () -> Printer[string]; fun foo: (p: Printer[T], x: T,) -> T; fun foo2: (p: Printer[T], x: T,) -> T; class F‹T›() {let x: T; fun GG: (y: U,) -> T}; trait I‹T›() {let x: T; fun GG: (y: U,) -> T}; class FFF‹T›() {fun fff: (x: T,) -> unit}; fun fff: (p: FFF[string], s: string,) -> unit; fun getFFF: () -> FFF[number]} +//│ Parsed: {fun inc: (x: T) -> number; class CC‹T›() {fun print: (s: T) -> unit}; fun con: (t: T) -> U; class Printer‹T›() {fun print: (t: T) -> unit}; fun setStringPrinter: (p: Printer[string]) -> unit; fun getStringPrinter: () -> Printer[string]; fun foo: (p: Printer[T], x: T) -> T; fun foo2: (p: Printer[T], x: T) -> T; class F‹T›() {let x: T; fun GG: (y: U) -> T}; trait I‹T›() {let x: T; fun GG: (y: U) -> T}; class FFF‹T›() {fun fff: (x: T) -> unit}; fun fff: (p: FFF[string], s: string) -> unit; fun getFFF: () -> FFF[number]} +//│ diff --git a/ts2mls/js/src/test/diff/Union.d.mls b/ts2mls/js/src/test/diff/Union.d.mls index 1124713fa7..8ad24730be 100644 --- a/ts2mls/js/src/test/diff/Union.d.mls +++ b/ts2mls/js/src/test/diff/Union.d.mls @@ -1,11 +1,12 @@ -:NewParser +:NewDefs :ParseOnly fun getString(x: (((string) | (number)) | (false)) | (true)): string fun test(x: (false) | (true)): (string) | (number) fun run(f: ((number) => number) | ((number) => string)): anything fun get(arr: (MutArray) | (MutArray)): unit -fun get2(t: ((string, string, )) | ((number, string, ))): string +fun get2(t: ([string, string]) | ([number, string])): string fun typeVar(x: (T) | (U)): (T) | (U) fun uuuu(x: (((string) | (number)) | (false)) | (true)): (((string) | (number)) | (false)) | (true) -//│ |#fun| |getString|(|x|#:| |(|(|(|string|)| ||| |(|number|)|)| ||| |(|false|)|)| ||| |(|true|)|)|#:| |string|↵|#fun| |test|(|x|#:| |(|false|)| ||| |(|true|)|)|#:| |(|string|)| ||| |(|number|)|↵|#fun| |run|(|f|#:| |(|(|number|)| |=>| |number|)| ||| |(|(|number|)| |=>| |string|)|)|#:| |anything|↵|#fun| |get|(|arr|#:| |(|MutArray|‹|number|›|)| ||| |(|MutArray|‹|string|›|)|)|#:| |unit|↵|#fun| |get2|(|t|#:| |(|(|string|,| |string|,| |)|)| ||| |(|(|number|,| |string|,| |)|)|)|#:| |string|↵|#fun| |typeVar|‹|T|,| |U|›|(|x|#:| |(|T|)| ||| |(|U|)|)|#:| |(|T|)| ||| |(|U|)|↵|#fun| |uuuu|(|x|#:| |(|(|(|string|)| ||| |(|number|)|)| ||| |(|false|)|)| ||| |(|true|)|)|#:| |(|(|(|string|)| ||| |(|number|)|)| ||| |(|false|)|)| ||| |(|true|)| -//│ Parsed: {fun getString: (x: (((string,) | (number,),) | (false,),) | (true,),) -> string; fun test: (x: (false,) | (true,),) -> ((string,) | (number,)); fun run: (f: (number -> number,) | (number -> string,),) -> anything; fun get: (arr: (MutArray[number],) | (MutArray[string],),) -> unit; fun get2: (t: ((string, string,),) | ((number, string,),),) -> string; fun typeVar: (x: (T,) | (U,),) -> ((T,) | (U,)); fun uuuu: (x: (((string,) | (number,),) | (false,),) | (true,),) -> ((((string,) | (number,),) | (false,),) | (true,))} +//│ |#fun| |getString|(|x|#:| |(|(|(|string|)| ||| |(|number|)|)| ||| |(|false|)|)| ||| |(|true|)|)|#:| |string|↵|#fun| |test|(|x|#:| |(|false|)| ||| |(|true|)|)|#:| |(|string|)| ||| |(|number|)|↵|#fun| |run|(|f|#:| |(|(|number|)| |#=>| |number|)| ||| |(|(|number|)| |#=>| |string|)|)|#:| |anything|↵|#fun| |get|(|arr|#:| |(|MutArray|‹|number|›|)| ||| |(|MutArray|‹|string|›|)|)|#:| |unit|↵|#fun| |get2|(|t|#:| |(|[|string|,| |string|]|)| ||| |(|[|number|,| |string|]|)|)|#:| |string|↵|#fun| |typeVar|‹|T|,| |U|›|(|x|#:| |(|T|)| ||| |(|U|)|)|#:| |(|T|)| ||| |(|U|)|↵|#fun| |uuuu|(|x|#:| |(|(|(|string|)| ||| |(|number|)|)| ||| |(|false|)|)| ||| |(|true|)|)|#:| |(|(|(|string|)| ||| |(|number|)|)| ||| |(|false|)|)| ||| |(|true|)| +//│ Parsed: {fun getString: (x: string | number | false | true) -> string; fun test: (x: bool) -> (string | number); fun run: (f: number -> number | number -> string) -> anything; fun get: (arr: MutArray[number] | MutArray[string]) -> unit; fun get2: (t: [string, string] | [number, string]) -> string; fun typeVar: (x: T | U) -> (T | U); fun uuuu: (x: string | number | false | true) -> (string | number | false | true)} +//│ diff --git a/ts2mls/js/src/test/diff/Variables.d.mls b/ts2mls/js/src/test/diff/Variables.d.mls index 178e8f1e15..89f39fa49c 100644 --- a/ts2mls/js/src/test/diff/Variables.d.mls +++ b/ts2mls/js/src/test/diff/Variables.d.mls @@ -1,4 +1,4 @@ -:NewParser +:NewDefs :ParseOnly let URI: string let URI2: string @@ -6,13 +6,14 @@ let foo: number let bar: false class FooBar() {} let fb: FooBar -namespace ABC { +module ABC { class DEF() {} } let d: ABC.DEF -namespace DD { +module DD { let foo: number let bar: number } -//│ |#let| |URI|#:| |string|↵|#let| |URI2|#:| |string|↵|#let| |foo|#:| |number|↵|#let| |bar|#:| |false|↵|#class| |FooBar|(||)| |{||}|↵|#let| |fb|#:| |FooBar|↵|#namespace| |ABC| |{|→|#class| |DEF|(||)| |{||}|←|↵|}|↵|#let| |d|#:| |ABC|.DEF|↵|#namespace| |DD| |{|→|#let| |foo|#:| |number|↵|#let| |bar|#:| |number|←|↵|}| -//│ Parsed: {let URI: string; let URI2: string; let foo: number; let bar: false; class FooBar() {}; let fb: FooBar; namespace ABC() {class DEF() {}}; let d: ABC.DEF; namespace DD() {let foo: number; let bar: number}} +//│ |#let| |URI|#:| |string|↵|#let| |URI2|#:| |string|↵|#let| |foo|#:| |number|↵|#let| |bar|#:| |false|↵|#class| |FooBar|(||)| |{||}|↵|#let| |fb|#:| |FooBar|↵|#module| |ABC| |{|→|#class| |DEF|(||)| |{||}|←|↵|}|↵|#let| |d|#:| |ABC|.DEF|↵|#module| |DD| |{|→|#let| |foo|#:| |number|↵|#let| |bar|#:| |number|←|↵|}| +//│ Parsed: {let URI: string; let URI2: string; let foo: number; let bar: false; class FooBar() {}; let fb: FooBar; module ABC {class DEF() {}}; let d: ABC.DEF; module DD {let foo: number; let bar: number}} +//│ diff --git a/ts2mls/js/src/test/typescript/BasicFunctions.ts b/ts2mls/js/src/test/typescript/BasicFunctions.ts index 27ae2c2562..ef0b915cab 100644 --- a/ts2mls/js/src/test/typescript/BasicFunctions.ts +++ b/ts2mls/js/src/test/typescript/BasicFunctions.ts @@ -60,7 +60,7 @@ function inn(f: Foooooo) { console.log(f.ooooooo) } -function out(): Foooooo { +function out1(): Foooooo { return new Foooooo(); }