From 8e31679b4b9c5339f1798f1905135e618f5d4245 Mon Sep 17 00:00:00 2001 From: denhie Date: Tue, 20 Jun 2023 14:42:56 +0200 Subject: [PATCH] make compilable --- .../src/main/scala/effekt/EffektConfig.scala | 8 - effekt/jvm/src/main/scala/effekt/Repl.scala | 2 +- effekt/jvm/src/main/scala/effekt/Server.scala | 5 +- .../shared/src/main/scala/effekt/Namer.scala | 6 +- .../shared/src/main/scala/effekt/Typer.scala | 138 ++++++++++++------ .../scala/effekt/context/Annotations.scala | 10 ++ .../src/main/scala/effekt/core/Parser.scala | 2 +- .../main/scala/effekt/core/Transformer.scala | 37 ++++- .../src/main/scala/effekt/core/Tree.scala | 2 +- .../src/main/scala/effekt/core/Type.scala | 2 +- .../src/main/scala/effekt/lifted/Tree.scala | 2 +- .../scala/effekt/source/ExplicitRegions.scala | 2 +- .../scala/effekt/symbols/DeclPrinter.scala | 2 +- .../scala/effekt/symbols/TypePrinter.scala | 2 +- .../main/scala/effekt/symbols/builtins.scala | 4 +- .../src/main/scala/effekt/symbols/kinds.scala | 2 + .../main/scala/effekt/symbols/symbols.scala | 14 +- .../src/main/scala/effekt/symbols/types.scala | 2 +- .../effekt/typer/BoxUnboxInference.scala | 2 +- .../scala/effekt/typer/ConcreteEffects.scala | 2 +- .../scala/effekt/typer/Substitution.scala | 2 +- .../scala/effekt/typer/TypeComparer.scala | 9 +- .../main/scala/effekt/typer/Unification.scala | 16 +- 23 files changed, 178 insertions(+), 95 deletions(-) diff --git a/effekt/jvm/src/main/scala/effekt/EffektConfig.scala b/effekt/jvm/src/main/scala/effekt/EffektConfig.scala index 3d299ec0c..9e842f335 100644 --- a/effekt/jvm/src/main/scala/effekt/EffektConfig.scala +++ b/effekt/jvm/src/main/scala/effekt/EffektConfig.scala @@ -53,14 +53,6 @@ class EffektConfig(args: Seq[String]) extends REPLConfig(args) { noshort = true ) - val no_optimize: ScallopOption[String] = choice( - choices = List("none", "all", "inlining", "sat", "beta", "dead"), - name = "no_optimize", - descr = "Optimizations that should not be used", - default = Some("none"), - noshort = true - ) - val llvmVersion: ScallopOption[String] = opt[String]( "llvm-version", descr = "the llvm version that should be used to compile the generated programs (only necessary if backend is llvm, defaults to 12)", diff --git a/effekt/jvm/src/main/scala/effekt/Repl.scala b/effekt/jvm/src/main/scala/effekt/Repl.scala index a0a42bfd3..f2a43495b 100644 --- a/effekt/jvm/src/main/scala/effekt/Repl.scala +++ b/effekt/jvm/src/main/scala/effekt/Repl.scala @@ -308,7 +308,7 @@ class Repl(driver: Driver) extends REPL[Tree, EffektConfig, EffektError] { */ def make(expr: Term): ModuleDecl = { - val body = Return(expr) + val body = Return(List(expr)) ModuleDecl("interactive", imports, definitions :+ FunDef(IdDef("main"), Nil, Nil, Nil, None, diff --git a/effekt/jvm/src/main/scala/effekt/Server.scala b/effekt/jvm/src/main/scala/effekt/Server.scala index 045b98248..d8894fb0d 100644 --- a/effekt/jvm/src/main/scala/effekt/Server.scala +++ b/effekt/jvm/src/main/scala/effekt/Server.scala @@ -208,7 +208,10 @@ trait LSPServer extends kiama.util.Server[Tree, EffektConfig, EffektError] with result <- fun.symbol.annotatedResult effects <- fun.symbol.annotatedEffects } yield (result, effects) - if ann.map { needsUpdate(_, (tpe, eff)) }.getOrElse(true) + if ann.map { + case (List(y), eff1) => needsUpdate((y, eff1), (tpe, eff)) + case _ => ??? // TODO MRV + }.getOrElse(true) res <- CodeAction("Update return type with inferred effects", fun.ret, s": $tpe / $eff") } yield res diff --git a/effekt/shared/src/main/scala/effekt/Namer.scala b/effekt/shared/src/main/scala/effekt/Namer.scala index b8f31d9b0..d22dec55a 100644 --- a/effekt/shared/src/main/scala/effekt/Namer.scala +++ b/effekt/shared/src/main/scala/effekt/Namer.scala @@ -606,7 +606,7 @@ object Namer extends Phase[Parsed, NameResolved] { val res = resolve(ret) - FunctionType(tps, cps, vps, bps, res, effs) + FunctionType(tps, cps, vps, bps, List(res), effs) // TODO MRV remove List(res) } } @@ -640,8 +640,8 @@ object Namer extends Phase[Parsed, NameResolved] { def resolve(tpe: source.Effects)(using Context): Effects = Effects(tpe.effs.flatMap(resolveWithAliases).toSeq: _*) // TODO this otherwise is calling the wrong apply - def resolve(e: source.Effectful)(using Context): (ValueType, Effects) = - (resolve(e.tpe), resolve(e.eff)) + def resolve(e: source.Effectful)(using Context): (List[ValueType], Effects) = + (e.tpe map resolve, resolve(e.eff)) def resolve(capt: source.CaptureSet)(using Context): CaptureSet = { val captResolved = CaptureSet(capt.captures.map { Context.resolveCapture }.toSet) diff --git a/effekt/shared/src/main/scala/effekt/Typer.scala b/effekt/shared/src/main/scala/effekt/Typer.scala index f5c0c375c..9ac2b1b46 100644 --- a/effekt/shared/src/main/scala/effekt/Typer.scala +++ b/effekt/shared/src/main/scala/effekt/Typer.scala @@ -97,31 +97,31 @@ object Typer extends Phase[NameResolved, Typechecked] { // - def checkExpr(expr: Term, expected: Option[ValueType])(using Context, Captures): Result[ValueType] = // TODO + def checkExpr(expr: Term, expected: Option[List[ValueType]])(using Context, Captures): Result[List[ValueType]] = // TODO checkAgainst(expr, expected) { - case source.Literal(_, tpe) => Result(tpe, Pure) + case source.Literal(_, tpe) => Result(List(tpe), Pure) case source.If(cond, thn, els) => - val Result(cndTpe, cndEffs) = cond checkAgainst TBoolean + val Result(cndTpe, cndEffs) = cond checkAgainst List(TBoolean) val Result(thnTpe, thnEffs) = checkStmt(thn, expected) val Result(elsTpe, elsEffs) = checkStmt(els, expected) - Result(Context.join(thnTpe, elsTpe), cndEffs ++ thnEffs ++ elsEffs) + Result(Context.join(thnTpe, elsTpe), cndEffs ++ thnEffs ++ elsEffs) // TODO Elemente einzeln vergleichen, untersch. Länge case source.While(cond, body) => - val Result(_, condEffs) = cond checkAgainst TBoolean - val Result(_, bodyEffs) = body checkAgainst TUnit - Result(TUnit, condEffs ++ bodyEffs) + val Result(_, condEffs) = cond checkAgainst List(TBoolean) + val Result(_, bodyEffs) = body checkAgainst List(TUnit) + Result(List(TUnit), condEffs ++ bodyEffs) case source.Var(id) => id.symbol match { case x: VarBinder => Context.lookup(x) match { case (btpe, capt) => val vtpe = TState.extractType(btpe) usingCapture(capt) - Result(vtpe, Pure) + Result(List(vtpe), Pure) } case b: BlockSymbol => Context.abort("Expected an expression, but got a block.") - case x: ValueSymbol => Result(Context.lookup(x), Pure) + case x: ValueSymbol => Result(List(Context.lookup(x)), Pure) } case e @ source.Assign(id, expr) => e.definition match { @@ -131,13 +131,13 @@ object Typer extends Phase[NameResolved, Typechecked] { usingCapture(capt) TState.extractType(btpe) } - val Result(_, eff) = expr checkAgainst stTpe - Result(TUnit, eff) + val Result(_, eff) = expr checkAgainst List(stTpe) + Result(List(TUnit), eff) } case l @ source.Box(annotatedCapture, block) => - val expectedTpe = expected.collect { case BoxedType(tpe, cap) => tpe } + val expectedTpe = expected.collect { case List(BoxedType(tpe, cap)) => tpe } val inferredCap: Captures = annotatedCapture.map { _.resolve }.getOrElse { Context.freshCaptVar(CaptUnificationVar.InferredBox(l)) } @@ -145,8 +145,8 @@ object Typer extends Phase[NameResolved, Typechecked] { given Captures = inferredCap val Result(inferredTpe, inferredEff) = checkExprAsBlock(block, expectedTpe) val tpe = Context.unification(BoxedType(inferredTpe, inferredCap)) - expected.map(Context.unification.apply) foreach { matchExpected(tpe, _) } - Result(tpe, inferredEff) + expected.map(Context.unification.apply) foreach { matchExpected(List(tpe), _) } + Result(List(tpe), inferredEff) case source.Unbox(_) => Context.abort("Expected an expression, but got an unbox (which is a block).") @@ -185,6 +185,7 @@ object Typer extends Phase[NameResolved, Typechecked] { val Result(t, eff) = checkCallTo(c, "function", tpe, targs map { _.resolve }, vargs, bargs, expected) Result(t, eff ++ funEffs) + // precondition: PreTyper translates all uniform-function calls to `Call`. // so the method calls here are actually methods calls on blocks as receivers. case c @ source.MethodCall(receiver, id, targs, vargs, bargs) => @@ -232,8 +233,15 @@ object Typer extends Phase[NameResolved, Typechecked] { handlers foreach Context.withFocus { h => given Captures = continuationCaptHandled - val Result(_, usedEffects) = checkImplementation(h.impl, Some((ret, continuationCapt))) - handlerEffs = handlerEffs ++ usedEffects + + ret match { + case List(ret) => { + val Result(_, usedEffects) = checkImplementation(h.impl, Some((ret, continuationCapt))) + handlerEffs = handlerEffs ++ usedEffects + } + case _ => ??? // TODO MRV + } + } // (4) Wellformedness checks @@ -287,7 +295,10 @@ object Typer extends Phase[NameResolved, Typechecked] { val tpes = clauses.map { case source.MatchClause(p, body) => // (3) infer types for all clauses - Context.bind(checkPattern(tpe, p)) + tpe match { + case List(tpe) => Context.bind(checkPattern(tpe, p)) + case _ => ??? // TODO MRV + } val Result(clTpe, clEff) = Context in { checkStmt(body, expected) } resEff = resEff ++ clEff clTpe @@ -296,11 +307,11 @@ object Typer extends Phase[NameResolved, Typechecked] { // Clauses could in general be empty if there are no constructors // In that case the scrutinee couldn't have been constructed and // we can unify with everything. - Result(Context.join(tpes: _*), resEff) + Result(Context.join(tpes: _*), resEff) // TODO: wie oben case source.Hole(stmt) => val Result(tpe, effs) = checkStmt(stmt, None) - Result(expected.getOrElse(TBottom), Pure) + Result(expected.getOrElse(List(TBottom)), Pure) case tree : source.New => Context.abort("Expected an expression, but got an object implementation (which is a block).") case tree : source.BlockLiteral => Context.abort("Expected an expression, but got a block literal.") @@ -404,15 +415,15 @@ object Typer extends Phase[NameResolved, Typechecked] { // resume { e } val resumeType = FunctionType(Nil, cparams, Nil, Nil, tpe, otherEffs) val resumeCapt = CaptureParam(Name.local("resumeBlock")) - FunctionType(Nil, List(resumeCapt), Nil, List(resumeType), ret, Effects.Pure) + FunctionType(Nil, List(resumeCapt), Nil, List(resumeType), List(ret), Effects.Pure) } else { // resume(v) - FunctionType(Nil, Nil, List(tpe), Nil, ret, Effects.Pure) + FunctionType(Nil, Nil, tpe, Nil, List(ret), Effects.Pure) } Context.bind(Context.symbolOf(resume).asBlockSymbol, resumeType, continuationCapt) - body checkAgainst ret + body checkAgainst List(ret) } handlerEffects = handlerEffects ++ effs @@ -435,13 +446,13 @@ object Typer extends Phase[NameResolved, Typechecked] { val expectedTpe = expected map { tpe => val captVar = Context.freshCaptVar(CaptUnificationVar.InferredUnbox(u)) - BoxedType(tpe, captVar) + List(BoxedType(tpe, captVar)) } - val Result(vtpe, eff1) = checkExpr(expr, expectedTpe) + val Result(vtpe, eff1) = checkExpr(expr, expectedTpe) // TODO MRV ??? // TODO here we also need unification variables for block types! // C.unify(tpe, BoxedType()) Context.unification(vtpe) match { - case BoxedType(btpe, capt2) => + case List(BoxedType(btpe, capt2)) => usingCapture(capt2) Result(btpe, eff1) case _ => @@ -499,7 +510,10 @@ object Typer extends Phase[NameResolved, Typechecked] { val (rigids, crigids, FunctionType(_, _, vps, _, ret, _)) = Context.instantiate(sym.toType, Nil, Nil) // (5) given a scrutinee of `List[Int]`, we learn `?t1 -> Int` - matchPattern(sc, ret, p) + ret match { + case List(ret) => matchPattern(sc, ret, p) + case _ => ??? // TODO MRV + } // (6) check for existential type variables // at the moment we do not allow existential type parameters on constructors, so this is not necessary. @@ -526,7 +540,7 @@ object Typer extends Phase[NameResolved, Typechecked] { // - def checkStmt(stmt: Stmt, expected: Option[ValueType])(using Context, Captures): Result[ValueType] = //TODO: List[ValueType] + def checkStmt(stmt: Stmt, expected: Option[List[ValueType]])(using Context, Captures): Result[List[ValueType]] = //TODO: List[ValueType] checkAgainst(stmt, expected) { case source.DefStmt(b, rest) => val Result(t, effBinding) = Context in { precheckDef(b); synthDef(b) } @@ -539,7 +553,21 @@ object Typer extends Phase[NameResolved, Typechecked] { val Result(r, eff2) = checkStmt(rest, expected) Result(r, eff1 ++ eff2) - case source.Return(e) => checkExpr(e, expected) + // TODO MRV: Verbieten? (return (1, return(2,3)) + case source.Return(e) => expected match { + case None => { + val check = e map { checkExpr(_, None) } + val tpes = check flatMap { _.tpe } + val effs = check map { _.effects } reduce { _ ++ _ } + Result(tpes, effs) + } + case Some(tpe) => { + val checked = (e zip tpe) map { case (e, tpe) => checkExpr(e, Some(List(tpe))) } + val tpes = checked flatMap { _.tpe } + val effs = checked map { _.effects } reduce { _ ++ _ } + Result(tpes, effs) + } + } case source.BlockStmt(stmts) => Context in { checkStmt(stmts, expected) } } @@ -688,13 +716,13 @@ object Typer extends Phase[NameResolved, Typechecked] { case d @ source.ValDef(id, annot, binding) => val Result(t, effBinding) = d.symbol.tpe match { case Some(t) => - val Result(_, eff) = binding checkAgainst t + val Result(_, eff) = binding checkAgainst List(t) // use annotated, not inferred type - Result(t, eff) + Result(List(t), eff) case None => checkStmt(binding, None) } - Context.bind(d.symbol, t) + Context.bind(d.symbol, t.head) // TODO MRV: ??? Result((), effBinding) @@ -712,10 +740,13 @@ object Typer extends Phase[NameResolved, Typechecked] { } getOrElse { CaptureSet(Context.region) } val Result(tpeBind, effBind) = d.symbol.tpe match { - case Some(t) => binding checkAgainst t + case Some(t) => binding checkAgainst List(t) case None => checkStmt(binding, None) } - val stTpe = TState(tpeBind) + val stTpe = tpeBind match { + case List(tpeBind) => TState(tpeBind) + case _ => ??? // TODO MRV + } // to allocate into the region, it needs to be live... usingCapture(stCapt) @@ -810,7 +841,7 @@ object Typer extends Phase[NameResolved, Typechecked] { // (6) Substitute both types and captures into expected return type val subst = typeSubst ++ captSubst - val expectedReturn = subst substitute tpe1 + val expectedReturn = tpe1 map { subst substitute _ } // TODO MRV // (7) Check function body val selfRegion = Context.getSelfRegion(arg) @@ -864,6 +895,7 @@ object Typer extends Phase[NameResolved, Typechecked] { val funType = FunctionType(tps, cps, vps, bps, tpe, effs.toEffects) + // Like with functions, bound parameters and capabilities are not closed over usingCaptureWithout(inferredCapture) { (bparams.map(_.symbol) ++ capabilities).map(_.capture) ++ List(selfRegion) @@ -902,8 +934,8 @@ object Typer extends Phase[NameResolved, Typechecked] { targs: List[ValueType], vargs: List[source.Term], bargs: List[source.Term], - expected: Option[ValueType] - )(using Context, Captures): Result[ValueType] = { + expected: Option[List[ValueType]] + )(using Context, Captures): Result[List[ValueType]] = { val sym = id.symbol val methods = sym match { @@ -940,8 +972,8 @@ object Typer extends Phase[NameResolved, Typechecked] { targs: List[ValueType], vargs: List[source.Term], bargs: List[source.Term], - expected: Option[ValueType] - )(using Context, Captures): Result[ValueType] = { + expected: Option[List[ValueType]] + )(using Context, Captures): Result[List[ValueType]] = { val scopes = id.symbol match { // an overloaded call target @@ -985,9 +1017,9 @@ object Typer extends Phase[NameResolved, Typechecked] { private def resolveOverload( id: source.IdRef, - successes: List[List[(BlockSymbol, Result[ValueType], TyperState)]], + successes: List[List[(BlockSymbol, Result[List[ValueType]], TyperState)]], failures: List[(BlockSymbol, EffektMessages)] - )(using Context): Result[ValueType] = { + )(using Context): Result[List[ValueType]] = { successes foreachAborting { // continue in outer scope @@ -1030,8 +1062,8 @@ object Typer extends Phase[NameResolved, Typechecked] { targs: List[ValueType], vargs: List[source.Term], bargs: List[source.Term], - expected: Option[ValueType] - )(using Context, Captures): Result[ValueType] = { + expected: Option[List[ValueType]] + )(using Context, Captures): Result[List[ValueType]] = { if (targs.nonEmpty && targs.size != funTpe.tparams.size) Context.abort(s"Wrong number of type arguments ${targs.size}") @@ -1052,7 +1084,7 @@ object Typer extends Phase[NameResolved, Typechecked] { var effs: ConcreteEffects = Pure (vps zip vargs) foreach { case (tpe, expr) => - val Result(t, eff) = checkExpr(expr, Some(tpe)) + val Result(t, eff) = checkExpr(expr, Some(List(tpe))) effs = effs ++ eff } @@ -1165,28 +1197,36 @@ object Typer extends Phase[NameResolved, Typechecked] { def matchPattern(scrutinee: ValueType, patternTpe: ValueType, pattern: source.MatchPattern)(using Context): Unit = Context.requireSubtype(scrutinee, patternTpe, ErrorContext.PatternMatch(pattern)) + // TODO MRV def matchExpected(got: ValueType, expected: ValueType)(using Context): Unit = Context.requireSubtype(got, expected, ErrorContext.Expected(Context.unification(got), Context.unification(expected), Context.focus)) + def matchExpected(got: List[ValueType], expected: List[ValueType])(using Context): Unit = { + if (got.length != expected.length) Context.error("Expected " + expected.length + " arguments, but got " + got.length) + + got zip expected foreach { (g, e) => Context.requireSubtype(g, e, + ErrorContext.Expected(Context.unification(g), Context.unification(e), Context.focus)) } + } + def matchExpected(got: BlockType, expected: BlockType)(using Context): Unit = Context.requireSubtype(got, expected, ErrorContext.Expected(Context.unification(got), Context.unification(expected), Context.focus)) extension (expr: Term) { - def checkAgainst(tpe: ValueType)(using Context, Captures): Result[ValueType] = + def checkAgainst(tpe: List[ValueType])(using Context, Captures): Result[List[ValueType]] = checkExpr(expr, Some(tpe)) } extension (stmt: Stmt) { - def checkAgainst(tpe: ValueType)(using Context, Captures): Result[ValueType] = + def checkAgainst(tpe: List[ValueType])(using Context, Captures): Result[List[ValueType]] = checkStmt(stmt, Some(tpe)) } /** * Combinators that also store the computed type for a tree in the TypesDB */ - def checkAgainst[T <: Tree](t: T, expected: Option[ValueType])(f: T => Result[ValueType])(using Context, Captures): Result[ValueType] = + def checkAgainst[T <: Tree](t: T, expected: Option[List[ValueType]])(f: T => Result[List[ValueType]])(using Context, Captures): Result[List[ValueType]] = Context.at(t) { val Result(got, effs) = f(t) wellformed(got) @@ -1216,13 +1256,14 @@ object Typer extends Phase[NameResolved, Typechecked] { // invariant: only works if ret is defined! def toType: FunctionType = annotatedType.get - def toType(ret: ValueType, effects: Effects, capabilityParams: List[Capture]): FunctionType = + def toType(ret: List[ValueType], effects: Effects, capabilityParams: List[Capture]): FunctionType = val bcapt = fun.bparams.map { p => p.capture } val tps = fun.tparams val vps = fun.vparams.map { p => p.tpe.get } val bps = fun.bparams.map { p => p.tpe } FunctionType(tps, bcapt ++ capabilityParams, vps, bps, ret, effects) + def annotatedType: Option[FunctionType] = for { ret <- fun.annotatedResult; @@ -1459,6 +1500,9 @@ trait TyperOps extends ContextOps { self: Context => private[typer] def annotateInferredType(t: Tree, e: ValueType) = annotations.update(Annotations.InferredValueType, t, e) + private[typer] def annotateInferredType(t: Tree, e: List[ValueType]) = + annotations.update(Annotations.InferredValueTypeList, t, e) + private[typer] def annotateInferredType(t: Tree, e: BlockType) = annotations.update(Annotations.InferredBlockType, t, e) diff --git a/effekt/shared/src/main/scala/effekt/context/Annotations.scala b/effekt/shared/src/main/scala/effekt/context/Annotations.scala index 88880c891..4d4743af4 100644 --- a/effekt/shared/src/main/scala/effekt/context/Annotations.scala +++ b/effekt/shared/src/main/scala/effekt/context/Annotations.scala @@ -115,6 +115,16 @@ object Annotations { "the inferred type of" ) + /** + * The type as inferred by typer at a given position in the tree + * + * Important for return values + */ + val InferredValueTypeList = Annotation[source.Tree, List[symbols.ValueType]]( + "InferredValueTypeList", + "the inferred list of types of" + ) + /** * The type as inferred by typer at a given position in the tree */ diff --git a/effekt/shared/src/main/scala/effekt/core/Parser.scala b/effekt/shared/src/main/scala/effekt/core/Parser.scala index 963969c00..d265c5fb6 100644 --- a/effekt/shared/src/main/scala/effekt/core/Parser.scala +++ b/effekt/shared/src/main/scala/effekt/core/Parser.scala @@ -109,7 +109,7 @@ class CoreParsers(positions: Positions, names: Names) extends EffektLexers(posit // ---------- lazy val stmt: P[Stmt] = ( `{` ~/> many(definition) ~ stmt <~ `}` ^^ Stmt.Scope.apply // curly braces induce scopes! - | `return` ~> someSep(pure, `,`) ^^ Stmt.Return.apply + | `return` ~> pure ^^ Stmt.Return.apply | `val` ~> id ~ (`=` ~> stmt) ~ (`;` ~> stmt) ^^ Stmt.Val.apply | block ~ maybeTypeArgs ~ valueArgs ~ blockArgs ^^ Stmt.App.apply | (`if` ~> `(` ~/> pure <~ `)`) ~ stmt ~ (`else` ~> stmt) ^^ Stmt.If.apply diff --git a/effekt/shared/src/main/scala/effekt/core/Transformer.scala b/effekt/shared/src/main/scala/effekt/core/Transformer.scala index 4b82f0c90..2e3fcaa7d 100644 --- a/effekt/shared/src/main/scala/effekt/core/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/core/Transformer.scala @@ -89,15 +89,25 @@ object Transformer extends Phase[Typechecked, CoreTransformed] { val cparams = capabilities.map { tpe => symbols.CaptureParam(tpe.name) } // here we reconstruct the block type - val btpe = core.BlockType.Function(tparams, cparams, vparams, bparams, transform(resultType)) - core.Property(op, btpe) + resultType match { + case List(resultType) => { + val btpe = core.BlockType.Function(tparams, cparams, vparams, bparams, transform(resultType)) + core.Property(op, btpe) + } + case _ => ??? //TODO MRV + } + })) case f @ source.ExternDef(pure, id, _, vps, bps, _, body) => val sym@ExternFunction(name, tps, _, _, ret, effects, capt, _) = f.symbol assert(effects.isEmpty) val cps = bps.map(b => b.symbol.capture) - List(Extern.Def(sym, tps, cps, vps map transform, bps map transform, transform(ret), transform(capt), body)) + ret match { + case List(ret) => List(Extern.Def(sym, tps, cps, vps map transform, bps map transform, transform(ret), transform(capt), body)) + case _ => ??? // TODO MRV + } + case e @ source.ExternInclude(path, contents, _) => List(Extern.Include(contents.get)) @@ -122,8 +132,12 @@ object Transformer extends Phase[Typechecked, CoreTransformed] { Val(Wildcard(), insertBindings { Return(transformAsPure(e)) }, transform(rest)) // return e - case source.Return(e) => - insertBindings { Return(transformAsPure(e)) } + case source.Return(e) => e match { + case List(e) => insertBindings { + Return(transformAsPure(e)) + } + case _ => ??? // TODO MRV + } // simply drop superfluous {}s case source.BlockStmt(b) => @@ -242,7 +256,10 @@ object Transformer extends Phase[Typechecked, CoreTransformed] { val result = TmpValue() BlockLit(tparams, bparams.map(_.id), vparams, bparams, core.Let(result, DirectApp(BlockVar(f), targs, vargs, bargs), - Stmt.Return(Pure.ValueVar(result, transform(restpe))))) + restpe.map(transform) match { + case List(restpe) => Stmt.Return(Pure.ValueVar(result, restpe)) + case _ => ??? // TODO MRV + })) } sym match { @@ -468,7 +485,7 @@ object Transformer extends Phase[Typechecked, CoreTransformed] { // in the future namer needs to annotate the function with the capture parameters it introduced. val cparams = effects.canonical.map { tpe => symbols.CaptureParam(tpe.name) } - FunctionType(remainingTypeParams, cparams, vparams.map(t => substitution.substitute(t.tpe.get)), bparams, substitution.substitute(resultType), substitution.substitute(effects)) + FunctionType(remainingTypeParams, cparams, vparams.map(t => substitution.substitute(t.tpe.get)), bparams, resultType.map(substitution.substitute), substitution.substitute(effects)) } case InterfaceType(i: ExternInterface, targs) => @@ -535,7 +552,11 @@ object Transformer extends Phase[Typechecked, CoreTransformed] { | Captures: ${cparams} |""".stripMargin) - core.BlockType.Function(tparams, cparams, vparams.map(transform), allBlockParams, transform(result)) + result.map(transform) match { + case List(res) => core.BlockType.Function(tparams, cparams, vparams.map(transform), allBlockParams, res) + case _ => ??? // TODO MRV + } + case BlockType.InterfaceType(tc, args) => core.BlockType.Interface(tc, args.map(transform)) } diff --git a/effekt/shared/src/main/scala/effekt/core/Tree.scala b/effekt/shared/src/main/scala/effekt/core/Tree.scala index e05593ddf..05aece954 100644 --- a/effekt/shared/src/main/scala/effekt/core/Tree.scala +++ b/effekt/shared/src/main/scala/effekt/core/Tree.scala @@ -237,7 +237,7 @@ enum Stmt extends Tree { case Scope(definitions: List[Definition], body: Stmt) // Fine-grain CBV - case Return(expr: List[Pure]) + case Return(expr: Pure) case Val(id: Id, binding: Stmt, body: Stmt) // TODO case App(callee: Block, targs: List[ValueType], vargs: List[Pure], bargs: List[Block]) diff --git a/effekt/shared/src/main/scala/effekt/core/Type.scala b/effekt/shared/src/main/scala/effekt/core/Type.scala index e289489c7..188ac36de 100644 --- a/effekt/shared/src/main/scala/effekt/core/Type.scala +++ b/effekt/shared/src/main/scala/effekt/core/Type.scala @@ -157,7 +157,7 @@ object Type { def inferType(stmt: Stmt): ValueType = stmt match { case Stmt.Scope(definitions, body) => body.tpe - case Stmt.Return(expr) => expr.foldLeft(TBottom) { case (acc, e) => merge(acc, e.tpe, covariant = true) } // TODO: flatmap + case Stmt.Return(expr) => expr.tpe case Stmt.Val(id, binding, body) => body.tpe case Stmt.App(callee, targs, vargs, bargs) => instantiate(callee.functionType, targs, bargs.map(_.capt)).result diff --git a/effekt/shared/src/main/scala/effekt/lifted/Tree.scala b/effekt/shared/src/main/scala/effekt/lifted/Tree.scala index 71b2a2e04..a3de14cbd 100644 --- a/effekt/shared/src/main/scala/effekt/lifted/Tree.scala +++ b/effekt/shared/src/main/scala/effekt/lifted/Tree.scala @@ -221,7 +221,7 @@ def freeVariables(stmt: Stmt): FreeVariables = stmt match { case Val(id, binding, body) => freeVariables(binding) ++ freeVariables(body) -- FreeVariables(ValueParam(id, binding.tpe)) case App(b, targs, args) => freeVariables(b) ++ args.map(freeVariables).combineFV case If(cond, thn, els) => freeVariables(cond) ++ freeVariables(thn) ++ freeVariables(els) - case Return(e) => e.foldLeft(Set.empty[Symbol]) { case (acc, e) => acc ++ freeVariables(e) } // : flatmap + case Return(e) => freeVariables(e) case Match(scrutinee, clauses, default) => freeVariables(scrutinee) ++ clauses.map { case (pattern, lit) => freeVariables(lit) }.combineFV ++ default.toSet.map(s => freeVariables(s)).combineFV case Hole() => FreeVariables.empty case State(id, init, region, ev, body) => diff --git a/effekt/shared/src/main/scala/effekt/source/ExplicitRegions.scala b/effekt/shared/src/main/scala/effekt/source/ExplicitRegions.scala index ffb22d101..2b669a5b8 100644 --- a/effekt/shared/src/main/scala/effekt/source/ExplicitRegions.scala +++ b/effekt/shared/src/main/scala/effekt/source/ExplicitRegions.scala @@ -43,7 +43,7 @@ object ExplicitRegions extends Phase[Typechecked, Typechecked], Rewrite { // Also copy the other annotations from transformed body to the synthesized region // captures of the overall region should not include self val region = Region(regionId, transformedBody) - val result = Return(region) + val result = Return(List(region)) Context.copyAnnotations(transformedBody, region) Context.copyAnnotations(transformedBody, result) diff --git a/effekt/shared/src/main/scala/effekt/symbols/DeclPrinter.scala b/effekt/shared/src/main/scala/effekt/symbols/DeclPrinter.scala index 3dcedc2cf..4bbd3053c 100644 --- a/effekt/shared/src/main/scala/effekt/symbols/DeclPrinter.scala +++ b/effekt/shared/src/main/scala/effekt/symbols/DeclPrinter.scala @@ -74,7 +74,7 @@ object DeclPrinter extends ParenPrettyPrinter { pp"def ${ d.name }: ${ tpe }" } - def format(kw: String, f: Callable, result: Option[ValueType], effects: Option[Effects]): Doc = { + def format(kw: String, f: Callable, result: Option[List[ValueType]], effects: Option[Effects]): Doc = { val tps = if (f.tparams.isEmpty) "" else s"[${f.tparams.mkString(", ")}]" val valueParams = f.vparams.map { p => pp"${p.name}: ${p.tpe.get}" }.mkString(", ") diff --git a/effekt/shared/src/main/scala/effekt/symbols/TypePrinter.scala b/effekt/shared/src/main/scala/effekt/symbols/TypePrinter.scala index 9e2ffe3c2..671cf87ff 100644 --- a/effekt/shared/src/main/scala/effekt/symbols/TypePrinter.scala +++ b/effekt/shared/src/main/scala/effekt/symbols/TypePrinter.scala @@ -49,7 +49,7 @@ object TypePrinter extends ParenPrettyPrinter { val bps = if (bparams.isEmpty) emptyDoc else hcat(bparams.map(toDoc).map(braces)) vps <> bps } - val ret = toDoc(result) + val ret = hsep(result.map(toDoc), comma) // TODO MRV: ??? val eff = if (effects.isEmpty) emptyDoc else space <> "/" <+> toDoc(effects) tps <> ps <+> "=>" <+> ret <> eff diff --git a/effekt/shared/src/main/scala/effekt/symbols/builtins.scala b/effekt/shared/src/main/scala/effekt/symbols/builtins.scala index 9a3b8e35d..ac59179a0 100644 --- a/effekt/shared/src/main/scala/effekt/symbols/builtins.scala +++ b/effekt/shared/src/main/scala/effekt/symbols/builtins.scala @@ -49,8 +49,8 @@ object builtins { object TState { val S: TypeParam = TypeParam(Name.local("S")) val interface: Interface = Interface(Name.local("Ref"), List(S), Nil) - val get = Operation(name("get"), List(S), Nil, ValueTypeRef(S), Effects.Pure, interface) - val put = Operation(name("put"), List(S), List(ValueParam(Name.local("s"), Some(ValueTypeRef(S)))), TUnit, Effects.Pure, interface) + val get = Operation(name("get"), List(S), Nil, List(ValueTypeRef(S)), Effects.Pure, interface) + val put = Operation(name("put"), List(S), List(ValueParam(Name.local("s"), Some(ValueTypeRef(S)))), List(TUnit), Effects.Pure, interface) interface.operations = List(get, put) def apply(stateType: ValueType) = InterfaceType(interface, List(stateType)) diff --git a/effekt/shared/src/main/scala/effekt/symbols/kinds.scala b/effekt/shared/src/main/scala/effekt/symbols/kinds.scala index 9ab49ba28..5f2a8f9b5 100644 --- a/effekt/shared/src/main/scala/effekt/symbols/kinds.scala +++ b/effekt/shared/src/main/scala/effekt/symbols/kinds.scala @@ -23,6 +23,8 @@ package object kinds { case _ => C.abort(s"Expected a value type but got ${tpe}") } + def wellformed(tpe: List[ValueType])(using C: Context): Unit = tpe foreach { wellformed } + def wellformed(tpe: BlockType)(using Context): Unit = tpe match { case b: FunctionType => wellformed(b) case c: InterfaceType => wellformed(c) diff --git a/effekt/shared/src/main/scala/effekt/symbols/symbols.scala b/effekt/shared/src/main/scala/effekt/symbols/symbols.scala index 73adb2e24..0a70e2918 100644 --- a/effekt/shared/src/main/scala/effekt/symbols/symbols.scala +++ b/effekt/shared/src/main/scala/effekt/symbols/symbols.scala @@ -104,7 +104,7 @@ trait Callable extends BlockSymbol { def tparams: List[TypeParam] def vparams: List[ValueParam] def bparams: List[BlockParam] - def annotatedResult: Option[ValueType] + def annotatedResult: Option[List[ValueType]] def annotatedEffects: Option[Effects] } @@ -113,7 +113,7 @@ case class UserFunction( tparams: List[TypeParam], vparams: List[ValueParam], bparams: List[BlockParam], - annotatedResult: Option[ValueType], + annotatedResult: Option[List[ValueType]], annotatedEffects: Option[Effects], decl: FunDef ) extends Callable @@ -226,7 +226,7 @@ case class Constructor(name: Name, tparams: List[TypeParam], var fields: List[Fi val bparams: List[BlockParam] = Nil val returnType: ValueType = ValueTypeApp(tpe, tparams map ValueTypeRef.apply) - def annotatedResult: Option[ValueType] = Some(returnType) + def annotatedResult: Option[List[ValueType]] = Some(List(returnType)) def annotatedEffects: Option[Effects] = Some(Effects.Pure) } @@ -237,7 +237,7 @@ case class Field(name: Name, param: ValueParam, constructor: Constructor) extend val bparams = List.empty[BlockParam] val returnType = param.tpe.get - def annotatedResult = Some(returnType) + def annotatedResult = Some(List(returnType)) def annotatedEffects = Some(Effects.Pure) } @@ -251,10 +251,10 @@ enum BlockTypeConstructor extends BlockTypeSymbol { export BlockTypeConstructor.* -case class Operation(name: Name, tparams: List[TypeParam], vparams: List[ValueParam], resultType: ValueType, effects: Effects, interface: BlockTypeConstructor.Interface) extends Callable { +case class Operation(name: Name, tparams: List[TypeParam], vparams: List[ValueParam], resultType: List[ValueType], effects: Effects, interface: BlockTypeConstructor.Interface) extends Callable { val bparams = List.empty[BlockParam] - def annotatedResult: Option[ValueType] = Some(resultType) + def annotatedResult: Option[List[ValueType]] = Some(resultType) def annotatedEffects: Option[Effects] = Some(Effects(effects.toList)) def appliedInterface: InterfaceType = InterfaceType(interface, interface.tparams map ValueTypeRef.apply) } @@ -350,7 +350,7 @@ case class ExternFunction( tparams: List[TypeParam], vparams: List[ValueParam], bparams: List[BlockParam], - result: ValueType, + result: List[ValueType], effects: Effects, capture: CaptureSet, body: String = "" diff --git a/effekt/shared/src/main/scala/effekt/symbols/types.scala b/effekt/shared/src/main/scala/effekt/symbols/types.scala index f629055ce..0bcb65d65 100644 --- a/effekt/shared/src/main/scala/effekt/symbols/types.scala +++ b/effekt/shared/src/main/scala/effekt/symbols/types.scala @@ -56,7 +56,7 @@ enum BlockType extends Type { cparams: List[Capture], vparams: List[ValueType], bparams: List[BlockType], - result: ValueType, + result: List[ValueType], effects: Effects ) diff --git a/effekt/shared/src/main/scala/effekt/typer/BoxUnboxInference.scala b/effekt/shared/src/main/scala/effekt/typer/BoxUnboxInference.scala index e5c3567ef..c9343901a 100644 --- a/effekt/shared/src/main/scala/effekt/typer/BoxUnboxInference.scala +++ b/effekt/shared/src/main/scala/effekt/typer/BoxUnboxInference.scala @@ -173,7 +173,7 @@ object BoxUnboxInference extends Phase[NameResolved, NameResolved] { ExprStmt(rewriteAsExpr(e), rewrite(rest)) case Return(e) => - Return(rewriteAsExpr(e)) + Return(e.map(rewriteAsExpr)) case BlockStmt(b) => BlockStmt(rewrite(b)) diff --git a/effekt/shared/src/main/scala/effekt/typer/ConcreteEffects.scala b/effekt/shared/src/main/scala/effekt/typer/ConcreteEffects.scala index 6b2f4b931..d13e5eff0 100644 --- a/effekt/shared/src/main/scala/effekt/typer/ConcreteEffects.scala +++ b/effekt/shared/src/main/scala/effekt/typer/ConcreteEffects.scala @@ -94,7 +94,7 @@ private def isConcreteValueType(tpe: TypeVar): Boolean = tpe match { private def isConcreteBlockType(tpe: BlockType): Boolean = tpe match { case FunctionType(tparams, cparams, vparams, bparams, result, effects) => - vparams.forall(isConcreteValueType) && bparams.forall(isConcreteBlockType) && isConcreteValueType(result) && isConcreteEffects(effects) + vparams.forall(isConcreteValueType) && bparams.forall(isConcreteBlockType) && result.forall(isConcreteValueType) && isConcreteEffects(effects) case InterfaceType(tpe, args) => args.forall(isConcreteValueType) } private def isConcreteCaptureSet(capt: Captures): Boolean = capt.isInstanceOf[CaptureSet] diff --git a/effekt/shared/src/main/scala/effekt/typer/Substitution.scala b/effekt/shared/src/main/scala/effekt/typer/Substitution.scala index 610e935d5..1c3117491 100644 --- a/effekt/shared/src/main/scala/effekt/typer/Substitution.scala +++ b/effekt/shared/src/main/scala/effekt/typer/Substitution.scala @@ -85,7 +85,7 @@ case class Substitutions( cps, vps map substWithout.substitute, bps map substWithout.substitute, - substWithout.substitute(ret), + ret map substWithout.substitute, substWithout.substitute(eff)) } } diff --git a/effekt/shared/src/main/scala/effekt/typer/TypeComparer.scala b/effekt/shared/src/main/scala/effekt/typer/TypeComparer.scala index 3362f9c66..af99385d8 100644 --- a/effekt/shared/src/main/scala/effekt/typer/TypeComparer.scala +++ b/effekt/shared/src/main/scala/effekt/typer/TypeComparer.scala @@ -104,13 +104,14 @@ trait TypeUnifier { val subst = Substitutions(tparams2 zip targs1, cparams2 zip cparams1.map(c => CaptureSet(c))) val substVParams2 = vparams2 map subst.substitute val substBParams2 = bparams2 map subst.substitute - val substRet2 = subst.substitute(ret2) + val substRet2 = ret2 map subst.substitute val substEffs2 = subst.substitute(eff2) (vparams1 zip substVParams2) foreach { case (t1, t2) => unifyValueTypes(t1, t2, ErrorContext.FunctionArgument(f1, f2, ctx)) } (bparams1 zip substBParams2) foreach { case (t1, t2) => unifyBlockTypes(t1, t2, ErrorContext.FunctionArgument(f1, f2, ctx)) } - unifyValueTypes(ret1, substRet2, ErrorContext.FunctionReturn(ctx)) + // TODO MRV compare lengths + (ret1 zip substRet2) map { case (r1, r2) => unifyValueTypes(r1, r2, ErrorContext.FunctionReturn(ctx)) } // We compare effects to be equal, since we do not have subtyping on effects // TODO verify that a different ordering doesn't interact badly with capture polymorphism @@ -210,12 +211,12 @@ trait TypeMerger extends TypeUnifier { val subst = Substitutions(tparams2 zip targs1, cparams2 zip cparams1.map(c => CaptureSet(c))) val substVParams2 = vparams2 map subst.substitute val substBParams2 = bparams2 map subst.substitute - val substRet2 = subst.substitute(ret2) + val substRet2 = ret2 map subst.substitute val substEffs2 = subst.substitute(eff2) val mergedVps = (vparams1 zip substVParams2) map { case (t1, t2) => mergeValueTypes(t1, t2, ErrorContext.FunctionArgument(f1, f2, ctx)) } val mergedBps = (bparams1 zip substBParams2) map { case (t1, t2) => mergeBlockTypes(t1, t2, ErrorContext.FunctionArgument(f1, f2, ctx)) } - val mergedRet = mergeValueTypes(ret1, substRet2, ErrorContext.FunctionReturn(ctx)) + val mergedRet = (ret1 zip substRet2) map { case (r1, sr2) => mergeValueTypes(r1, sr2, ErrorContext.FunctionReturn(ctx)) } // We compare effects to be equal, since we do not have subtyping on effects unifyEffects(eff1, substEffs2, ErrorContext.FunctionEffects(ctx)) diff --git a/effekt/shared/src/main/scala/effekt/typer/Unification.scala b/effekt/shared/src/main/scala/effekt/typer/Unification.scala index 11cdc39d3..7d24918e2 100644 --- a/effekt/shared/src/main/scala/effekt/typer/Unification.scala +++ b/effekt/shared/src/main/scala/effekt/typer/Unification.scala @@ -84,6 +84,9 @@ class Unification(using C: ErrorReporter) extends TypeUnifier, TypeMerger, TypeI def apply(tpe: ValueType): ValueType = substitution.substitute(tpe) + def apply(tpes: List[ValueType]): List[ValueType] = + tpes map substitution.substitute + // Lifecycle management // -------------------- def backup(): UnificationState = UnificationState(scope, constraints.clone()) @@ -147,11 +150,18 @@ class Unification(using C: ErrorReporter) extends TypeUnifier, TypeMerger, TypeI /** * Computes the join of all types, only called to merge the different arms of if and match */ - def join(tpes: ValueType*): ValueType = + def joinOld(tpes: ValueType*): ValueType = tpes.foldLeft[ValueType](TBottom) { (t1, t2) => mergeValueTypes(t1, t2, ErrorContext.MergeTypes(apply(t1), apply(t2))) } + def join(tpes: List[ValueType]*): List[ValueType] = + if (!tpes.forall(tpe => tpe.length == tpes.head.length)) abort("Cannot join lists of different length") // TODO MRV: correct error context + + tpes.toList.transpose map { _.foldLeft[ValueType](TBottom) { (t1, t2) => + mergeValueTypes(t1, t2, ErrorContext.MergeTypes(apply(t1), apply(t2))) + } } + def requireSubregionWithout(lower: Captures, upper: Captures, filter: List[Capture])(using C: Context): Unit = requireSubregionWithout(lower, upper, filter.toSet, ErrorContext.CaptureFlow(lower, upper, C.focus)) @@ -214,7 +224,7 @@ class Unification(using C: ErrorReporter) extends TypeUnifier, TypeMerger, TypeI val substitutedVparams = vparams map instantiate val substitutedBparams = bparams map instantiate - val substitutedReturn = instantiate(ret) + val substitutedReturn = ret map instantiate val substitutedEffects = instantiate(eff) @@ -355,7 +365,7 @@ trait TypeInstantiator { self: Unification => cps, vps map instantiate, bps map instantiate, - instantiate(ret), + ret map instantiate, instantiate(eff)) } } \ No newline at end of file