From 321e342d73ea4d8b36ce5e4785d0f8342797d888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Thu, 15 May 2025 21:01:54 +0200 Subject: [PATCH 01/29] Split DCE from Optimizer phase --- .../effekt/core/DeadCodeElimination.scala | 20 +++++++++++++++++++ .../effekt/core/optimizer/Optimizer.scala | 8 ++++---- .../scala/effekt/generator/llvm/LLVM.scala | 4 +++- 3 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 effekt/shared/src/main/scala/effekt/core/DeadCodeElimination.scala diff --git a/effekt/shared/src/main/scala/effekt/core/DeadCodeElimination.scala b/effekt/shared/src/main/scala/effekt/core/DeadCodeElimination.scala new file mode 100644 index 000000000..8ba335ff9 --- /dev/null +++ b/effekt/shared/src/main/scala/effekt/core/DeadCodeElimination.scala @@ -0,0 +1,20 @@ +package effekt.core + +import effekt.PhaseResult.CoreTransformed +import effekt.context.Context +import effekt.core.optimizer.Deadcode +import effekt.Phase + +object DeadCodeElimination extends Phase[CoreTransformed, CoreTransformed] { + val phaseName: String = "deadcode-elimination" + + def run(input: CoreTransformed)(using Context): Option[CoreTransformed] = + input match { + case CoreTransformed(source, tree, mod, core) => + val term = Context.checkMain(mod) + val dce = Context.timed("deadcode-elimination", source.name) { + Deadcode.remove(term, core) + } + Some(CoreTransformed(source, tree, mod, dce)) + } +} diff --git a/effekt/shared/src/main/scala/effekt/core/optimizer/Optimizer.scala b/effekt/shared/src/main/scala/effekt/core/optimizer/Optimizer.scala index f494eb96c..b6d8dcc70 100644 --- a/effekt/shared/src/main/scala/effekt/core/optimizer/Optimizer.scala +++ b/effekt/shared/src/main/scala/effekt/core/optimizer/Optimizer.scala @@ -23,10 +23,10 @@ object Optimizer extends Phase[CoreTransformed, CoreTransformed] { var tree = core - // (1) first thing we do is simply remove unused definitions (this speeds up all following analysis and rewrites) - tree = Context.timed("deadcode-elimination", source.name) { - Deadcode.remove(mainSymbol, tree) - } + // (1) first thing we do is simply remove unused definitions (this speeds up all following analysis and rewrites) + // tree = Context.timed("deadcode-elimination", source.name) { + // Deadcode.remove(mainSymbol, tree) + // } if !Context.config.optimize() then return tree; diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/LLVM.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/LLVM.scala index 14ecb643b..bfb37c491 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/LLVM.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/LLVM.scala @@ -7,6 +7,8 @@ import effekt.core.optimizer import effekt.machine import kiama.output.PrettyPrinterTypes.{ Document, emptyLinks } import kiama.util.Source +import effekt.core.DeadCodeElimination +import effekt.generator.chez.DeadCodeElimination class LLVM extends Compiler[String] { @@ -52,7 +54,7 @@ class LLVM extends Compiler[String] { // ----------------------------------- object steps { // intermediate steps for VSCode - val afterCore = allToCore(Core) andThen Aggregate andThen optimizer.Optimizer andThen core.PolymorphismBoxing + val afterCore = allToCore(Core) andThen Aggregate andThen core.DeadCodeElimination andThen optimizer.Optimizer andThen core.PolymorphismBoxing val afterMachine = afterCore andThen Machine map { case (mod, main, prog) => prog } val afterLLVM = afterMachine map { case machine.Program(decls, defns, entry) => From 0536cc94828d5fecd74270e5ccd44a78139db244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Thu, 15 May 2025 21:02:54 +0200 Subject: [PATCH 02/29] Add base Mono phase --- .../shared/src/main/scala/effekt/core/Mono.scala | 15 +++++++++++++++ .../main/scala/effekt/generator/llvm/LLVM.scala | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 effekt/shared/src/main/scala/effekt/core/Mono.scala diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala new file mode 100644 index 000000000..39f0b48e5 --- /dev/null +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -0,0 +1,15 @@ +package effekt +package core + +import effekt.context.Context +import effekt.lexer.TokenKind + +object Mono extends Phase[CoreTransformed, CoreTransformed] { + + override val phaseName: String = "mono" + + override def run(input: CoreTransformed)(using Context): Option[CoreTransformed] = { + Some(input) + } + +} \ No newline at end of file diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/LLVM.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/LLVM.scala index bfb37c491..40da90d5b 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/LLVM.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/LLVM.scala @@ -54,7 +54,7 @@ class LLVM extends Compiler[String] { // ----------------------------------- object steps { // intermediate steps for VSCode - val afterCore = allToCore(Core) andThen Aggregate andThen core.DeadCodeElimination andThen optimizer.Optimizer andThen core.PolymorphismBoxing + val afterCore = allToCore(Core) andThen Aggregate andThen core.DeadCodeElimination andThen core.Mono andThen optimizer.Optimizer val afterMachine = afterCore andThen Machine map { case (mod, main, prog) => prog } val afterLLVM = afterMachine map { case machine.Program(decls, defns, entry) => From 4107bfad9d30a03603f44d3a91b74e7139ea0e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Wed, 21 May 2025 12:30:18 +0200 Subject: [PATCH 03/29] Implement find constraints for subset --- .../src/main/scala/effekt/core/Mono.scala | 83 ++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 39f0b48e5..2b199b6a9 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -3,13 +3,94 @@ package core import effekt.context.Context import effekt.lexer.TokenKind +import effekt.context.assertions.asDataType + +// import scala.collection.mutable object Mono extends Phase[CoreTransformed, CoreTransformed] { override val phaseName: String = "mono" override def run(input: CoreTransformed)(using Context): Option[CoreTransformed] = { + input match { + case CoreTransformed(source, tree, mod, core) => { + core match { + case ModuleDecl(path, includes, declarations, externs, definitions, exports) => { + val polys = findConstraints(definitions) + polys.foreach(p => println(p)) + println() + } + } + } + } Some(input) } +} + +// TODO: We probably want some kind of graph data structure instead of the map, for better performance in cycle detection later. +// This works for now +// TODO: Consider using unique IDs instead of Symbol for keys. +// This works, but might give weird output when debugging +// if the same symbol name is used twice +// TODO: After solving the constraints it would be helpful to know +// which functions have which tparams +// so we can generate the required monomorphic functions +type PolyConstraint = Map[symbols.Symbol, Set[symbols.Symbol]] +type PolyConstraintEntry = (symbols.Symbol, Set[symbols.Symbol]) + +def appendConstraint(map: PolyConstraint, sym: symbols.Symbol, tpe: ValueType): PolyConstraintEntry = + val currentFlow = map.getOrElse(sym, Set()) + tpe match { + // Ignore self cycling types A -> A + case ValueType.Data(name, targs) if name != sym => (sym -> currentFlow.incl(name)) + case ValueType.Var(name) if name != sym => (sym -> (currentFlow.incl(name))) + // TODO: What do we do with boxed types? + case o@ValueType.Boxed(tpe, capt) => + println("Hit boxed type: " + o) + (sym -> currentFlow) + case _ => (sym -> currentFlow) // self cycling flow + } -} \ No newline at end of file +def findConstraintRec(value: Val, typeFlow: PolyConstraint): PolyConstraint = + var newTypeFlow = typeFlow + value.binding match { + case App(callee, targ :: targs, vargs, bargs) => + callee match { + case BlockVar(id, annotatedTpe, annotatedCapt) => + annotatedTpe match { + case BlockType.Function(tparam :: tparams, cparams, vparams, bparams, result) => + newTypeFlow += appendConstraint(newTypeFlow, tparam, targ) + case _ => + } + case _ => + } + case _ => + } + value.body match { + case v@Val(_, _, _, _) => findConstraintRec(v, newTypeFlow) + case _ => newTypeFlow + } + +def findConstraints(definitions: List[Toplevel]): PolyConstraint = + var typeFlow: PolyConstraint = Map() + definitions.foreach { + case Toplevel.Def(id, block) => + block match + case BlockLit(tparam :: tparams, cparams, vparams, bparams, body) => + body match { + case v@Val(id, annotatedTpe, binding, body) => + typeFlow ++= findConstraintRec(v, typeFlow) + case Return(expr) => + typeFlow += appendConstraint(typeFlow, tparam, expr.tpe) + case _ => + } + case BlockLit(tparams, cparams, vparams, bparams, body) => + body match { + case v@Val(id, annotatedTpe, binding, body) => + typeFlow ++= findConstraintRec(v, typeFlow) + case _ => + } + case _ => + case _ => + } + typeFlow \ No newline at end of file From cc76e6c91af67b69cdc18b3fdb2c88a782f59d8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Wed, 21 May 2025 13:06:58 +0200 Subject: [PATCH 04/29] Merge forward declare --- libraries/llvm/forward-declare-c.ll | 57 ++++++++++++++--------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/libraries/llvm/forward-declare-c.ll b/libraries/llvm/forward-declare-c.ll index a34fb1b79..1964be77d 100644 --- a/libraries/llvm/forward-declare-c.ll +++ b/libraries/llvm/forward-declare-c.ll @@ -1,43 +1,42 @@ ; forward-declared from primitives.c -declare i64 @c_get_argc() -declare %Pos @c_get_arg(i64) +declare i64 @c_get_argc() mustprogress nofree norecurse nosync nounwind sspstrong willreturn memory(read, argmem: none, inaccessiblemem: none) uwtable +declare %Pos @c_get_arg(i64) nofree nounwind sspstrong uwtable declare void @c_io_println(%Pos) declare %Pos @c_io_readln() -declare void @hole(i8*) -declare void @duplicated_prompt() +declare void @hole(i8*) noreturn nounwind sspstrong uwtable +declare void @duplicated_prompt() noreturn nounwind sspstrong uwtable -declare %Pos @c_ref_fresh(%Pos) -declare %Pos @c_ref_get(%Pos) -declare %Pos @c_ref_set(%Pos, %Pos) +declare %Pos @c_ref_fresh(%Pos) mustprogress nofree nounwind sspstrong willreturn memory(write, argmem: none, inaccessiblemem: readwrite) uwtable +declare %Pos @c_ref_get(%Pos) nounwind sspstrong uwtable +declare %Pos @c_ref_set(%Pos, %Pos) nounwind sspstrong uwtable -declare %Pos @c_array_new(%Int) -declare %Int @c_array_size(%Pos) -declare %Pos @c_array_get(%Pos, %Int) -declare %Pos @c_array_set(%Pos, %Int, %Pos) +declare %Pos @c_array_new(%Int) mustprogress nofree nounwind sspstrong willreturn memory(write, argmem: none, inaccessiblemem: readwrite) uwtable +declare %Int @c_array_size(%Pos) nounwind sspstrong uwtable +declare %Pos @c_array_get(%Pos, %Int) nounwind sspstrong uwtable +declare %Pos @c_array_set(%Pos, %Int, %Pos) nounwind sspstrong uwtable -declare %Pos @c_bytearray_new(%Int) -declare %Int @c_bytearray_size(%Pos) -declare %Byte @c_bytearray_get(%Pos, %Int) -declare %Pos @c_bytearray_set(%Pos, %Int, %Byte) +declare %Pos @c_bytearray_new(%Int) mustprogress nofree nounwind sspstrong willreturn memory(write, argmem: none, inaccessiblemem: readwrite) uwtable +declare %Int @c_bytearray_size(%Pos) nounwind sspstrong uwtable +declare %Byte @c_bytearray_get(%Pos, %Int) nounwind sspstrong uwtable +declare %Pos @c_bytearray_set(%Pos, %Int, %Byte) nounwind sspstrong uwtable -declare ptr @c_bytearray_data(%Pos) -declare %Pos @c_bytearray_construct(i64, ptr) +declare ptr @c_bytearray_data(%Pos) mustprogress nofree norecurse nosync nounwind sspstrong willreturn memory(none) uwtable +declare %Pos @c_bytearray_construct(i64, ptr) mustprogress nofree nounwind sspstrong willreturn uwtable -declare %Pos @c_bytearray_from_nullterminated_string(ptr) -declare ptr @c_bytearray_into_nullterminated_string(%Pos) +declare %Pos @c_bytearray_from_nullterminated_string(ptr) nofree nounwind sspstrong uwtable +declare ptr @c_bytearray_into_nullterminated_string(%Pos) mustprogress nofree nounwind sspstrong willreturn uwtable -declare %Pos @c_bytearray_show_Int(i64) -declare %Pos @c_bytearray_show_Char(i32) -declare %Pos @c_bytearray_show_Byte(i8) -declare %Pos @c_bytearray_show_Double(double) +declare %Pos @c_bytearray_show_Int(i64) nofree nounwind sspstrong uwtable +declare %Pos @c_bytearray_show_Char(i32) nofree nounwind sspstrong memory(readwrite, argmem: none) uwtable +declare %Pos @c_bytearray_show_Byte(i8) nofree nounwind sspstrong uwtable +declare %Pos @c_bytearray_show_Double(double) nofree nounwind sspstrong uwtable -declare %Pos @c_bytearray_concatenate(%Pos, %Pos) -declare %Pos @c_bytearray_equal(%Pos, %Pos) -declare %Int @c_bytearray_compare(%Pos, %Pos) - -declare %Pos @c_bytearray_substring(%Pos, i64, i64) -declare %Int @c_bytearray_character_at(%Pos, i64) +declare %Pos @c_bytearray_concatenate(%Pos, %Pos) nounwind sspstrong uwtable +declare %Pos @c_bytearray_equal(%Pos, %Pos) nounwind sspstrong uwtable +declare %Int @c_bytearray_compare(%Pos, %Pos) nounwind sspstrong uwtable +declare %Pos @c_bytearray_substring(%Pos, i64, i64) nounwind sspstrong uwtable +declare %Int @c_bytearray_character_at(%Pos, i64) nounwind sspstrong uwtable \ No newline at end of file From 72a2f798cc16e9ed5ba24ac2f425a315c0c63052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Wed, 21 May 2025 18:01:58 +0200 Subject: [PATCH 05/29] Detect cycles in PolyConstraints --- .../src/main/scala/effekt/core/Mono.scala | 58 ++++++++++++++----- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 2b199b6a9..85e35ffd7 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -5,8 +5,6 @@ import effekt.context.Context import effekt.lexer.TokenKind import effekt.context.assertions.asDataType -// import scala.collection.mutable - object Mono extends Phase[CoreTransformed, CoreTransformed] { override val phaseName: String = "mono" @@ -27,23 +25,24 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { } } -// TODO: We probably want some kind of graph data structure instead of the map, for better performance in cycle detection later. -// This works for now -// TODO: Consider using unique IDs instead of Symbol for keys. -// This works, but might give weird output when debugging -// if the same symbol name is used twice // TODO: After solving the constraints it would be helpful to know // which functions have which tparams // so we can generate the required monomorphic functions -type PolyConstraint = Map[symbols.Symbol, Set[symbols.Symbol]] -type PolyConstraintEntry = (symbols.Symbol, Set[symbols.Symbol]) -def appendConstraint(map: PolyConstraint, sym: symbols.Symbol, tpe: ValueType): PolyConstraintEntry = +enum PolyType { + case Var(val sym: symbols.Symbol) + case Base(val tpe: symbols.Symbol) +} + +type PolyConstraints = Map[symbols.Symbol, Set[PolyType]] +type PolyConstraintEntry = (symbols.Symbol, Set[PolyType]) + +def appendConstraint(map: PolyConstraints, sym: symbols.Symbol, tpe: ValueType): PolyConstraintEntry = val currentFlow = map.getOrElse(sym, Set()) tpe match { // Ignore self cycling types A -> A - case ValueType.Data(name, targs) if name != sym => (sym -> currentFlow.incl(name)) - case ValueType.Var(name) if name != sym => (sym -> (currentFlow.incl(name))) + case ValueType.Data(name, targs) if name != sym => (sym -> (currentFlow + PolyType.Base(name))) + case ValueType.Var(name) if name != sym => (sym -> (currentFlow + PolyType.Var(name))) // TODO: What do we do with boxed types? case o@ValueType.Boxed(tpe, capt) => println("Hit boxed type: " + o) @@ -51,7 +50,7 @@ def appendConstraint(map: PolyConstraint, sym: symbols.Symbol, tpe: ValueType): case _ => (sym -> currentFlow) // self cycling flow } -def findConstraintRec(value: Val, typeFlow: PolyConstraint): PolyConstraint = +def findConstraintRec(value: Val, typeFlow: PolyConstraints): PolyConstraints = var newTypeFlow = typeFlow value.binding match { case App(callee, targ :: targs, vargs, bargs) => @@ -71,8 +70,8 @@ def findConstraintRec(value: Val, typeFlow: PolyConstraint): PolyConstraint = case _ => newTypeFlow } -def findConstraints(definitions: List[Toplevel]): PolyConstraint = - var typeFlow: PolyConstraint = Map() +def findConstraints(definitions: List[Toplevel]): PolyConstraints = + var typeFlow: PolyConstraints = Map() definitions.foreach { case Toplevel.Def(id, block) => block match @@ -93,4 +92,31 @@ def findConstraints(definitions: List[Toplevel]): PolyConstraint = case _ => case _ => } - typeFlow \ No newline at end of file + typeFlow + +object PolyGraphCalc { + var visited: Set[symbols.Symbol] = Set() + var recStack: Set[symbols.Symbol] = Set() + + def hasCycle(vertex: symbols.Symbol, adjacency: PolyConstraints): Boolean = + if (recStack.contains(vertex)) return true + + if (visited.contains(vertex)) return false + + visited += vertex + recStack += vertex + + adjacency.foreach((v, edges) => if (hasCycle(v, adjacency)) return true) + + recStack -= vertex + false + + def hasCycle(constraints: PolyConstraints): Boolean = + visited = Set() + recStack = Set() + + constraints.keys.foreach(v => if (hasCycle(v, constraints)) return true) + + false +} + From aaed3b0289fd0dd77b0008325d9af9a26e8858c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Wed, 21 May 2025 21:48:53 +0200 Subject: [PATCH 06/29] Fix hasCycle implementation --- .../src/main/scala/effekt/core/Mono.scala | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 85e35ffd7..80ddff9c3 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -17,6 +17,8 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { val polys = findConstraints(definitions) polys.foreach(p => println(p)) println() + println("Has cycle: " + hasCycle(polys)) + println() } } } @@ -30,8 +32,13 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { // so we can generate the required monomorphic functions enum PolyType { - case Var(val sym: symbols.Symbol) case Base(val tpe: symbols.Symbol) + case Var(val sym: symbols.Symbol) + + def toSymbol: symbols.Symbol = this match { + case Base(tpe) => tpe + case Var(sym) => sym + } } type PolyConstraints = Map[symbols.Symbol, Set[PolyType]] @@ -94,29 +101,25 @@ def findConstraints(definitions: List[Toplevel]): PolyConstraints = } typeFlow -object PolyGraphCalc { - var visited: Set[symbols.Symbol] = Set() - var recStack: Set[symbols.Symbol] = Set() +def hasCycle(constraints: PolyConstraints): Boolean = + var visited: Set[symbols.Symbol] = Set() + var recStack: Set[symbols.Symbol] = Set() - def hasCycle(vertex: symbols.Symbol, adjacency: PolyConstraints): Boolean = - if (recStack.contains(vertex)) return true + def hasCycleHelper(vertex: symbols.Symbol): Boolean = + if (recStack.contains(vertex)) return true + if (visited.contains(vertex)) return false - if (visited.contains(vertex)) return false + visited += vertex + recStack += vertex - visited += vertex - recStack += vertex + var cycleFound = false + constraints.getOrElse(vertex, Set()).foreach(v => cycleFound |= hasCycleHelper(v.toSymbol)) - adjacency.foreach((v, edges) => if (hasCycle(v, adjacency)) return true) + recStack -= vertex - recStack -= vertex - false - - def hasCycle(constraints: PolyConstraints): Boolean = - visited = Set() - recStack = Set() - - constraints.keys.foreach(v => if (hasCycle(v, constraints)) return true) - - false -} + cycleFound + + var cycleFound = false + constraints.keys.foreach(v => cycleFound |= !visited.contains(v) && hasCycleHelper(v)) + cycleFound From 6847154c1080777ee57f7d744d8397545b8c737f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Wed, 21 May 2025 22:09:48 +0200 Subject: [PATCH 07/29] Implement solveConstraints --- .../src/main/scala/effekt/core/Mono.scala | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 80ddff9c3..d65cb17f7 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -14,10 +14,17 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { case CoreTransformed(source, tree, mod, core) => { core match { case ModuleDecl(path, includes, declarations, externs, definitions, exports) => { - val polys = findConstraints(definitions) - polys.foreach(p => println(p)) + val constraints = findConstraints(definitions) + println("Constraints") + constraints.foreach(c => println(c)) println() - println("Has cycle: " + hasCycle(polys)) + if (!hasCycle(constraints)) { + val solvedConstraints = solveConstraints(constraints) + println("Solved constraints") + solvedConstraints.foreach(sc => println(sc)) + } else { + println("Cycle detected, skipping solveConstraints") + } println() } } @@ -44,6 +51,24 @@ enum PolyType { type PolyConstraints = Map[symbols.Symbol, Set[PolyType]] type PolyConstraintEntry = (symbols.Symbol, Set[PolyType]) +def solveConstraints(constraints: PolyConstraints): PolyConstraints = + var solved: PolyConstraints = Map() + + def solveConstraint(sym: symbols.Symbol, types: Set[PolyType]): Set[PolyType] = + var polyTypes: Set[PolyType] = Set() + types.foreach(t => { + t match { + case PolyType.Var(symbol) => polyTypes ++= solved.getOrElse(symbol, solveConstraint(symbol, constraints.getOrElse(symbol, Set()))) + case base => polyTypes += base + } + }) + solved += (sym -> polyTypes) + polyTypes + + constraints.foreach(solveConstraint) + + solved + def appendConstraint(map: PolyConstraints, sym: symbols.Symbol, tpe: ValueType): PolyConstraintEntry = val currentFlow = map.getOrElse(sym, Set()) tpe match { From 6ed9127b24169592ec01de3e7e20d33c46e1abb4 Mon Sep 17 00:00:00 2001 From: Mattis Boeckle Date: Mon, 26 May 2025 15:31:16 +0200 Subject: [PATCH 08/29] Revert forward declare changes --- libraries/llvm/forward-declare-c.ll | 57 +++++++++++++++-------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/libraries/llvm/forward-declare-c.ll b/libraries/llvm/forward-declare-c.ll index 1964be77d..a34fb1b79 100644 --- a/libraries/llvm/forward-declare-c.ll +++ b/libraries/llvm/forward-declare-c.ll @@ -1,42 +1,43 @@ ; forward-declared from primitives.c -declare i64 @c_get_argc() mustprogress nofree norecurse nosync nounwind sspstrong willreturn memory(read, argmem: none, inaccessiblemem: none) uwtable -declare %Pos @c_get_arg(i64) nofree nounwind sspstrong uwtable +declare i64 @c_get_argc() +declare %Pos @c_get_arg(i64) declare void @c_io_println(%Pos) declare %Pos @c_io_readln() -declare void @hole(i8*) noreturn nounwind sspstrong uwtable -declare void @duplicated_prompt() noreturn nounwind sspstrong uwtable +declare void @hole(i8*) +declare void @duplicated_prompt() -declare %Pos @c_ref_fresh(%Pos) mustprogress nofree nounwind sspstrong willreturn memory(write, argmem: none, inaccessiblemem: readwrite) uwtable -declare %Pos @c_ref_get(%Pos) nounwind sspstrong uwtable -declare %Pos @c_ref_set(%Pos, %Pos) nounwind sspstrong uwtable +declare %Pos @c_ref_fresh(%Pos) +declare %Pos @c_ref_get(%Pos) +declare %Pos @c_ref_set(%Pos, %Pos) -declare %Pos @c_array_new(%Int) mustprogress nofree nounwind sspstrong willreturn memory(write, argmem: none, inaccessiblemem: readwrite) uwtable -declare %Int @c_array_size(%Pos) nounwind sspstrong uwtable -declare %Pos @c_array_get(%Pos, %Int) nounwind sspstrong uwtable -declare %Pos @c_array_set(%Pos, %Int, %Pos) nounwind sspstrong uwtable +declare %Pos @c_array_new(%Int) +declare %Int @c_array_size(%Pos) +declare %Pos @c_array_get(%Pos, %Int) +declare %Pos @c_array_set(%Pos, %Int, %Pos) -declare %Pos @c_bytearray_new(%Int) mustprogress nofree nounwind sspstrong willreturn memory(write, argmem: none, inaccessiblemem: readwrite) uwtable -declare %Int @c_bytearray_size(%Pos) nounwind sspstrong uwtable -declare %Byte @c_bytearray_get(%Pos, %Int) nounwind sspstrong uwtable -declare %Pos @c_bytearray_set(%Pos, %Int, %Byte) nounwind sspstrong uwtable +declare %Pos @c_bytearray_new(%Int) +declare %Int @c_bytearray_size(%Pos) +declare %Byte @c_bytearray_get(%Pos, %Int) +declare %Pos @c_bytearray_set(%Pos, %Int, %Byte) -declare ptr @c_bytearray_data(%Pos) mustprogress nofree norecurse nosync nounwind sspstrong willreturn memory(none) uwtable -declare %Pos @c_bytearray_construct(i64, ptr) mustprogress nofree nounwind sspstrong willreturn uwtable +declare ptr @c_bytearray_data(%Pos) +declare %Pos @c_bytearray_construct(i64, ptr) -declare %Pos @c_bytearray_from_nullterminated_string(ptr) nofree nounwind sspstrong uwtable -declare ptr @c_bytearray_into_nullterminated_string(%Pos) mustprogress nofree nounwind sspstrong willreturn uwtable +declare %Pos @c_bytearray_from_nullterminated_string(ptr) +declare ptr @c_bytearray_into_nullterminated_string(%Pos) -declare %Pos @c_bytearray_show_Int(i64) nofree nounwind sspstrong uwtable -declare %Pos @c_bytearray_show_Char(i32) nofree nounwind sspstrong memory(readwrite, argmem: none) uwtable -declare %Pos @c_bytearray_show_Byte(i8) nofree nounwind sspstrong uwtable -declare %Pos @c_bytearray_show_Double(double) nofree nounwind sspstrong uwtable +declare %Pos @c_bytearray_show_Int(i64) +declare %Pos @c_bytearray_show_Char(i32) +declare %Pos @c_bytearray_show_Byte(i8) +declare %Pos @c_bytearray_show_Double(double) -declare %Pos @c_bytearray_concatenate(%Pos, %Pos) nounwind sspstrong uwtable -declare %Pos @c_bytearray_equal(%Pos, %Pos) nounwind sspstrong uwtable -declare %Int @c_bytearray_compare(%Pos, %Pos) nounwind sspstrong uwtable +declare %Pos @c_bytearray_concatenate(%Pos, %Pos) +declare %Pos @c_bytearray_equal(%Pos, %Pos) +declare %Int @c_bytearray_compare(%Pos, %Pos) + +declare %Pos @c_bytearray_substring(%Pos, i64, i64) +declare %Int @c_bytearray_character_at(%Pos, i64) -declare %Pos @c_bytearray_substring(%Pos, i64, i64) nounwind sspstrong uwtable -declare %Int @c_bytearray_character_at(%Pos, i64) nounwind sspstrong uwtable \ No newline at end of file From f5496706a97268d0507b5b3d806fa287197c7c33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Thu, 22 May 2025 21:12:46 +0200 Subject: [PATCH 09/29] Replace symbols.Symbol with Id --- .../src/main/scala/effekt/core/Mono.scala | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index d65cb17f7..e673c197f 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -39,29 +39,27 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { // so we can generate the required monomorphic functions enum PolyType { - case Base(val tpe: symbols.Symbol) - case Var(val sym: symbols.Symbol) + case Base(val tpe: Id) + case Var(val sym: Id) - def toSymbol: symbols.Symbol = this match { + def toSymbol: Id = this match { case Base(tpe) => tpe case Var(sym) => sym } } -type PolyConstraints = Map[symbols.Symbol, Set[PolyType]] -type PolyConstraintEntry = (symbols.Symbol, Set[PolyType]) +type PolyConstraints = Map[Id, Set[PolyType]] +type PolyConstraintEntry = (Id, Set[PolyType]) def solveConstraints(constraints: PolyConstraints): PolyConstraints = var solved: PolyConstraints = Map() - def solveConstraint(sym: symbols.Symbol, types: Set[PolyType]): Set[PolyType] = + def solveConstraint(sym: Id, types: Set[PolyType]): Set[PolyType] = var polyTypes: Set[PolyType] = Set() - types.foreach(t => { - t match { - case PolyType.Var(symbol) => polyTypes ++= solved.getOrElse(symbol, solveConstraint(symbol, constraints.getOrElse(symbol, Set()))) - case base => polyTypes += base - } - }) + types.foreach { + case PolyType.Var(symbol) => polyTypes ++= solved.getOrElse(symbol, solveConstraint(symbol, constraints.getOrElse(symbol, Set()))) + case base => polyTypes += base + } solved += (sym -> polyTypes) polyTypes @@ -69,8 +67,9 @@ def solveConstraints(constraints: PolyConstraints): PolyConstraints = solved -def appendConstraint(map: PolyConstraints, sym: symbols.Symbol, tpe: ValueType): PolyConstraintEntry = +def appendConstraint(map: PolyConstraints, sym: Id, tpe: ValueType): PolyConstraintEntry = val currentFlow = map.getOrElse(sym, Set()) + println("Append: " + tpe + ", " + sym) tpe match { // Ignore self cycling types A -> A case ValueType.Data(name, targs) if name != sym => (sym -> (currentFlow + PolyType.Base(name))) @@ -122,15 +121,15 @@ def findConstraints(definitions: List[Toplevel]): PolyConstraints = case _ => } case _ => - case _ => + case _ => } typeFlow def hasCycle(constraints: PolyConstraints): Boolean = - var visited: Set[symbols.Symbol] = Set() - var recStack: Set[symbols.Symbol] = Set() + var visited: Set[Id] = Set() + var recStack: Set[Id] = Set() - def hasCycleHelper(vertex: symbols.Symbol): Boolean = + def hasCycleHelper(vertex: Id): Boolean = if (recStack.contains(vertex)) return true if (visited.contains(vertex)) return false From d12449a9d01acb5bd0e9e97495a4e684fbf0dd50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Mon, 9 Jun 2025 12:14:41 +0200 Subject: [PATCH 10/29] Simplify findConstraints interface --- .../src/main/scala/effekt/core/Mono.scala | 118 +++++++++--------- 1 file changed, 61 insertions(+), 57 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index e673c197f..05d79cc2a 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -14,12 +14,21 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { case CoreTransformed(source, tree, mod, core) => { core match { case ModuleDecl(path, includes, declarations, externs, definitions, exports) => { + // Find constraints in the definitions val constraints = findConstraints(definitions) println("Constraints") constraints.foreach(c => println(c)) println() - if (!hasCycle(constraints)) { - val solvedConstraints = solveConstraints(constraints) + + // Filter out self referencing constraints + val filtered = constraints.map((id, tpes) => (id, tpes.filter(tpe => tpe.toSymbol != id))) + println("Filtered") + filtered.foreach(f => println(f)) + println() + + // Solve if constraints are non-cyclic + if (!hasCycle(filtered)) { + val solvedConstraints = solveConstraints(filtered) println("Solved constraints") solvedConstraints.foreach(sc => println(sc)) } else { @@ -67,63 +76,58 @@ def solveConstraints(constraints: PolyConstraints): PolyConstraints = solved -def appendConstraint(map: PolyConstraints, sym: Id, tpe: ValueType): PolyConstraintEntry = - val currentFlow = map.getOrElse(sym, Set()) - println("Append: " + tpe + ", " + sym) - tpe match { - // Ignore self cycling types A -> A - case ValueType.Data(name, targs) if name != sym => (sym -> (currentFlow + PolyType.Base(name))) - case ValueType.Var(name) if name != sym => (sym -> (currentFlow + PolyType.Var(name))) - // TODO: What do we do with boxed types? - case o@ValueType.Boxed(tpe, capt) => - println("Hit boxed type: " + o) - (sym -> currentFlow) - case _ => (sym -> currentFlow) // self cycling flow - } - -def findConstraintRec(value: Val, typeFlow: PolyConstraints): PolyConstraints = - var newTypeFlow = typeFlow - value.binding match { - case App(callee, targ :: targs, vargs, bargs) => - callee match { - case BlockVar(id, annotatedTpe, annotatedCapt) => - annotatedTpe match { - case BlockType.Function(tparam :: tparams, cparams, vparams, bparams, result) => - newTypeFlow += appendConstraint(newTypeFlow, tparam, targ) - case _ => - } - case _ => - } - case _ => - } - value.body match { - case v@Val(_, _, _, _) => findConstraintRec(v, newTypeFlow) - case _ => newTypeFlow - } +def combineConstraints(a: PolyConstraints, b: PolyConstraints): PolyConstraints = { + a ++ b.map { case (k, v) => k -> (v ++ a.getOrElse(k, Iterable.empty)) } +} def findConstraints(definitions: List[Toplevel]): PolyConstraints = - var typeFlow: PolyConstraints = Map() - definitions.foreach { - case Toplevel.Def(id, block) => - block match - case BlockLit(tparam :: tparams, cparams, vparams, bparams, body) => - body match { - case v@Val(id, annotatedTpe, binding, body) => - typeFlow ++= findConstraintRec(v, typeFlow) - case Return(expr) => - typeFlow += appendConstraint(typeFlow, tparam, expr.tpe) - case _ => - } - case BlockLit(tparams, cparams, vparams, bparams, body) => - body match { - case v@Val(id, annotatedTpe, binding, body) => - typeFlow ++= findConstraintRec(v, typeFlow) - case _ => - } - case _ => - case _ => - } - typeFlow + definitions.map(findConstraints).reduce(combineConstraints) + +def findConstraints(toplevel: Toplevel): PolyConstraints = toplevel match { + case Toplevel.Def(id, block) => findConstraints(block, List.empty) + case Toplevel.Val(id, tpe, binding) => ??? +} + +def findConstraints(block: Block, targs: List[ValueType]): PolyConstraints = block match { + case BlockLit(tparam :: tparams, cparams, vparams, bparams, body) => findConstraints(body, tparam :: tparams) + case BlockLit(List(), cparams, vparams, bparams, body) => findConstraints(body, List.empty) + case BlockVar(id, annotatedTpe, annotatedCapt) => findConstraints(annotatedTpe, targs) + case New(impl) => ??? + case Unbox(pure) => ??? + case _ => Map.empty +} + +def findConstraints(stmt: Stmt, tparams: List[Id]): PolyConstraints = stmt match { + case App(callee, targs, vargs, bargs) => findConstraints(callee, targs) + case Return(expr) if !tparams.isEmpty => Map(tparams.head -> Set(findPolyType(expr.tpe))) + case Return(expr) => Map.empty + case Val(id, annotatedTpe, binding, body) => combineConstraints(findConstraints(binding, tparams), findConstraints(body, tparams)) + // TODO: Let & If case is wrong, but placeholders are required as they are used in print + case Let(id, annotatedTpe, binding, body) => Map.empty + case If(cond, thn, els) => Map.empty + case o => println(o); ??? +} + +def findConstraints(value: Val): PolyConstraints = value match { + // TODO: List.empty might be wrong + case Val(id, annotatedTpe, binding, body) => combineConstraints(findConstraints(binding, List.empty), findConstraints(body, List.empty)) +} + +def findConstraints(blockType: BlockType, targs: List[ValueType]): PolyConstraints = blockType match { + case BlockType.Function(tparams, cparams, vparams, bparams, result) => tparams.zip(targs).map((id, tpe) => (id -> Set(findPolyType(tpe)))).toMap + case BlockType.Interface(name, targs) => ??? +} + +def findPolyType(blockType: BlockType, targs: List[ValueType]): List[PolyType] = blockType match { + case BlockType.Function(tparams, cparams, vparams, bparams, result) => ??? + case BlockType.Interface(name, targs) => ??? +} + +def findPolyType(valueType: ValueType): PolyType = valueType match { + case ValueType.Boxed(tpe, capt) => ??? + case ValueType.Data(name, targs) => PolyType.Base(name) + case ValueType.Var(name) => PolyType.Var(name) +} def hasCycle(constraints: PolyConstraints): Boolean = var visited: Set[Id] = Set() From 0001362c685c2b1914898bc5fd6000c64177714a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Mon, 9 Jun 2025 12:21:10 +0200 Subject: [PATCH 11/29] Add type for solved constraints --- effekt/shared/src/main/scala/effekt/core/Mono.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 05d79cc2a..092d42a41 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -58,16 +58,16 @@ enum PolyType { } type PolyConstraints = Map[Id, Set[PolyType]] -type PolyConstraintEntry = (Id, Set[PolyType]) +type PolyConstraintsSolved = Map[Id, Set[PolyType.Base]] -def solveConstraints(constraints: PolyConstraints): PolyConstraints = - var solved: PolyConstraints = Map() +def solveConstraints(constraints: PolyConstraints): PolyConstraintsSolved = + var solved: PolyConstraintsSolved = Map() - def solveConstraint(sym: Id, types: Set[PolyType]): Set[PolyType] = - var polyTypes: Set[PolyType] = Set() + def solveConstraint(sym: Id, types: Set[PolyType]): Set[PolyType.Base] = + var polyTypes: Set[PolyType.Base] = Set() types.foreach { case PolyType.Var(symbol) => polyTypes ++= solved.getOrElse(symbol, solveConstraint(symbol, constraints.getOrElse(symbol, Set()))) - case base => polyTypes += base + case PolyType.Base(tpe) => polyTypes += PolyType.Base(tpe) } solved += (sym -> polyTypes) polyTypes From 49cfe83f5884027fea10127fa600eacde02b6fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Thu, 12 Jun 2025 06:59:35 +0200 Subject: [PATCH 12/29] Emit monomorphized definitions --- .../src/main/scala/effekt/core/Mono.scala | 86 ++++++++++++++++++- 1 file changed, 83 insertions(+), 3 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 092d42a41..db2b66261 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -31,10 +31,17 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { val solvedConstraints = solveConstraints(filtered) println("Solved constraints") solvedConstraints.foreach(sc => println(sc)) + + println() + val monomorphized = monomorphize(core)(using MonoContext(solvedConstraints)) + println("Mono definitions") + monomorphized.definitions.foreach(println) } else { println("Cycle detected, skipping solveConstraints") } println() + + } } } @@ -43,6 +50,77 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { } } +type PolyConstraints = Map[Id, Set[PolyType]] +type PolyConstraintsSolved = Map[Id, Set[PolyType.Base]] +type PolyConstraintSingle = Map[Id, PolyType.Base] + +class MonoContext(val solvedConstraints: PolyConstraintsSolved) + +var monoCounter = 0 +def freshMonoName(baseId: Id, tpe: PolyType.Base): Id = + monoCounter += 1 + Id(baseId.name.name + tpe.tpe.name.name + monoCounter) + +def freshMonoName(baseId: Id, tpes: List[PolyType.Base]): Id = + monoCounter += 1 + var tpesString = "" + tpes.foreach(tpe => tpesString += tpe.tpe.name.name) + Id(baseId.name.name + tpesString + monoCounter) + +// TODO: The following two are awful and surely doing redundant work. +def generator(xs: List[Set[PolyConstraintSingle]]): List[Set[PolyConstraintSingle]] = xs.foldRight(List(Set.empty)) { (next, combinations) => + (for (a <- next; as <- combinations) yield as + a).toList +} + +def gen(xs: PolyConstraintsSolved) = { + (for ((id, constrs) <- xs) yield (for (c <- constrs) yield Map((id -> c)))).toList +} + +def monomorphize(decl: ModuleDecl)(using ctx: MonoContext): ModuleDecl = decl match + case ModuleDecl(path, includes, declarations, externs, definitions, exports) => + ModuleDecl(path, includes, declarations, externs, definitions flatMap monomorphize, exports) + +def monomorphize(definition: Toplevel)(using ctx: MonoContext): List[Toplevel] = definition match { + case Toplevel.Def(id, block) => monomorphize(block, id).map((newId, newBlock) => Toplevel.Def(newId, newBlock)).toList + case Toplevel.Val(id, tpe, binding) => ??? +} + +def monomorphize(block: Block, baseId: Id)(using ctx: MonoContext): Map[Id, Block] = block match { + case BlockLit(List(), cparams, vparams, bparams, body) => Map(baseId -> block) + case BlockLit(tparams, cparams, vparams, bparams, body) => { + // TODO: There is some redundancy here, but it works for now + val relevantConstraints = tparams.map(tp => Map(tp -> ctx.solvedConstraints.getOrElse(tp, Set.empty)).filter((_, s) => !s.isEmpty)) + val splitConstraints = relevantConstraints.flatMap(gen) + val combinations = generator(splitConstraints) + val flattened = combinations.map(c => c.flatten.toMap) + flattened.map(f => { + val newId = freshMonoName(baseId, f.values.toList) + (newId -> BlockLit(List(), cparams, vparams.map(monomorphize(_, f)), bparams, monomorphize(body, f))) + }).toMap + } + case BlockVar(id, annotatedTpe, annotatedCapt) => ??? + case New(impl) => ??? + case Unbox(pure) => ??? +} + +def monomorphize(stmt: Stmt, replacementTparam: Map[Id, PolyType.Base]): Stmt = stmt match { + case Return(expr) => Return(monomorphize(expr, replacementTparam)) + case _ => ??? +} + +def monomorphize(pure: Pure, replacementTparam: Map[Id, PolyType.Base]): Pure = pure match + case ValueVar(id, annotatedType) => ValueVar(id, monomorphize(annotatedType, replacementTparam)) + case _ => ??? + +def monomorphize(vparam: ValueParam, replacementTparam: Map[Id, PolyType.Base]): ValueParam = vparam match { + case ValueParam(id, tpe) => ValueParam(id, monomorphize(tpe, replacementTparam)) +} + +def monomorphize(valueType: ValueType, replacementTparam: Map[Id, PolyType.Base]): ValueType = valueType match + case ValueType.Var(name) => replacementTparam.get(name).get.toValueType + case ValueType.Data(name, targs) => ??? + case ValueType.Boxed(tpe, capt) => ??? + // TODO: After solving the constraints it would be helpful to know // which functions have which tparams // so we can generate the required monomorphic functions @@ -55,10 +133,12 @@ enum PolyType { case Base(tpe) => tpe case Var(sym) => sym } -} -type PolyConstraints = Map[Id, Set[PolyType]] -type PolyConstraintsSolved = Map[Id, Set[PolyType.Base]] + def toValueType: ValueType = this match { + case Base(tpe) => ValueType.Data(tpe, List.empty) + case Var(sym) => ValueType.Var(sym) + } +} def solveConstraints(constraints: PolyConstraints): PolyConstraintsSolved = var solved: PolyConstraintsSolved = Map() From c064a48d92cc75d3649a5c1192d7482da2b9e428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Mon, 30 Jun 2025 15:18:31 +0200 Subject: [PATCH 13/29] Update findConstraints to fit proposed version --- .../src/main/scala/effekt/core/Mono.scala | 451 +++++++++++------- 1 file changed, 273 insertions(+), 178 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index db2b66261..57b34912e 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -15,30 +15,14 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { core match { case ModuleDecl(path, includes, declarations, externs, definitions, exports) => { // Find constraints in the definitions - val constraints = findConstraints(definitions) + val constraints = findConstraints(definitions)(using new MonoContext) println("Constraints") constraints.foreach(c => println(c)) println() - // Filter out self referencing constraints - val filtered = constraints.map((id, tpes) => (id, tpes.filter(tpe => tpe.toSymbol != id))) - println("Filtered") - filtered.foreach(f => println(f)) - println() - - // Solve if constraints are non-cyclic - if (!hasCycle(filtered)) { - val solvedConstraints = solveConstraints(filtered) - println("Solved constraints") - solvedConstraints.foreach(sc => println(sc)) - - println() - val monomorphized = monomorphize(core)(using MonoContext(solvedConstraints)) - println("Mono definitions") - monomorphized.definitions.foreach(println) - } else { - println("Cycle detected, skipping solveConstraints") - } + // val solved = solveConstraint(constraints) + // println("Solved") + // solved.foreach(println) println() @@ -50,184 +34,295 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { } } -type PolyConstraints = Map[Id, Set[PolyType]] -type PolyConstraintsSolved = Map[Id, Set[PolyType.Base]] -type PolyConstraintSingle = Map[Id, PolyType.Base] +type FunctionId = Id +case class Constraint(lower: Vector[TypeArg], upper: FunctionId) +type Constraints = List[Constraint] -class MonoContext(val solvedConstraints: PolyConstraintsSolved) +// case class SolvedConstraint(lower: Vector[TypeArg.Base], upper: FunctionId | TypeArg.Var) +// type SolvedConstraints = List[SolvedConstraint] -var monoCounter = 0 -def freshMonoName(baseId: Id, tpe: PolyType.Base): Id = - monoCounter += 1 - Id(baseId.name.name + tpe.tpe.name.name + monoCounter) +enum TypeArg { + case Base(val tpe: Id) + case Var(funId: FunctionId, pos: Int) +} -def freshMonoName(baseId: Id, tpes: List[PolyType.Base]): Id = - monoCounter += 1 - var tpesString = "" - tpes.foreach(tpe => tpesString += tpe.tpe.name.name) - Id(baseId.name.name + tpesString + monoCounter) +// Type Id -> Var +type TypeParams = Map[Id, TypeArg.Var] -// TODO: The following two are awful and surely doing redundant work. -def generator(xs: List[Set[PolyConstraintSingle]]): List[Set[PolyConstraintSingle]] = xs.foldRight(List(Set.empty)) { (next, combinations) => - (for (a <- next; as <- combinations) yield as + a).toList -} +class MonoContext { + var typingContext: TypeParams = Map() + var functionId: FunctionId = Id("notAValidId") -def gen(xs: PolyConstraintsSolved) = { - (for ((id, constrs) <- xs) yield (for (c <- constrs) yield Map((id -> c)))).toList + def extendTypingContext(tparam: Id, index: Int) = + typingContext += (tparam -> TypeArg.Var(functionId, index)) } -def monomorphize(decl: ModuleDecl)(using ctx: MonoContext): ModuleDecl = decl match - case ModuleDecl(path, includes, declarations, externs, definitions, exports) => - ModuleDecl(path, includes, declarations, externs, definitions flatMap monomorphize, exports) +def findConstraints(definitions: List[Toplevel])(using MonoContext): Constraints = + definitions flatMap findConstraints -def monomorphize(definition: Toplevel)(using ctx: MonoContext): List[Toplevel] = definition match { - case Toplevel.Def(id, block) => monomorphize(block, id).map((newId, newBlock) => Toplevel.Def(newId, newBlock)).toList +def findConstraints(definition: Toplevel)(using ctx: MonoContext): Constraints = definition match + case Toplevel.Def(id, block) => ctx.functionId = id; findConstraints(block) case Toplevel.Val(id, tpe, binding) => ??? -} -def monomorphize(block: Block, baseId: Id)(using ctx: MonoContext): Map[Id, Block] = block match { - case BlockLit(List(), cparams, vparams, bparams, body) => Map(baseId -> block) - case BlockLit(tparams, cparams, vparams, bparams, body) => { - // TODO: There is some redundancy here, but it works for now - val relevantConstraints = tparams.map(tp => Map(tp -> ctx.solvedConstraints.getOrElse(tp, Set.empty)).filter((_, s) => !s.isEmpty)) - val splitConstraints = relevantConstraints.flatMap(gen) - val combinations = generator(splitConstraints) - val flattened = combinations.map(c => c.flatten.toMap) - flattened.map(f => { - val newId = freshMonoName(baseId, f.values.toList) - (newId -> BlockLit(List(), cparams, vparams.map(monomorphize(_, f)), bparams, monomorphize(body, f))) - }).toMap - } +def findConstraints(block: Block)(using ctx: MonoContext): Constraints = block match case BlockVar(id, annotatedTpe, annotatedCapt) => ??? - case New(impl) => ??? + case BlockLit(tparams, cparams, vparams, bparams, body) => + tparams.zipWithIndex.foreach(ctx.extendTypingContext) + findConstraints(body) case Unbox(pure) => ??? -} - -def monomorphize(stmt: Stmt, replacementTparam: Map[Id, PolyType.Base]): Stmt = stmt match { - case Return(expr) => Return(monomorphize(expr, replacementTparam)) - case _ => ??? -} + case New(impl) => ??? -def monomorphize(pure: Pure, replacementTparam: Map[Id, PolyType.Base]): Pure = pure match - case ValueVar(id, annotatedType) => ValueVar(id, monomorphize(annotatedType, replacementTparam)) - case _ => ??? +def findConstraints(stmt: Stmt)(using ctx: MonoContext): Constraints = stmt match + case Let(id, annotatedTpe, binding, body) => findConstraints(binding) ++ findConstraints(body) + case Return(expr) => findConstraints(expr) + case Val(id, annotatedTpe, binding, body) => findConstraints(binding) ++ findConstraints(body) + case App(callee: BlockVar, targs, vargs, bargs) => List(Constraint(targs.map(findId).toVector, callee.id)) + case If(cond, thn, els) => findConstraints(cond) ++ findConstraints(thn) ++ findConstraints(els) + case o => println(o); ??? -def monomorphize(vparam: ValueParam, replacementTparam: Map[Id, PolyType.Base]): ValueParam = vparam match { - case ValueParam(id, tpe) => ValueParam(id, monomorphize(tpe, replacementTparam)) -} +def findConstraints(expr: Expr)(using ctx: MonoContext): Constraints = expr match + case DirectApp(b, List(), vargs, bargs) => List.empty + case ValueVar(id, annotatedType) => List.empty + case Literal(value, annotatedType) => List.empty + case o => println(o); ??? -def monomorphize(valueType: ValueType, replacementTparam: Map[Id, PolyType.Base]): ValueType = valueType match - case ValueType.Var(name) => replacementTparam.get(name).get.toValueType - case ValueType.Data(name, targs) => ??? +def findId(vt: ValueType)(using ctx: MonoContext): TypeArg = vt match case ValueType.Boxed(tpe, capt) => ??? + case ValueType.Data(name, targs) => TypeArg.Base(name) + case ValueType.Var(name) => ctx.typingContext(name) + + +// Old stuff + +// type PolyConstraints = Map[Id, Set[PolyType]] +// type PolyConstraintsSolved = Map[Id, Set[PolyType.Base]] +// type PolyConstraintSingle = Map[Id, PolyType.Base] + +// class MonoContext(val solvedConstraints: PolyConstraintsSolved, var monoDefs: Map[Id, Map[List[PolyType.Base], (Id, Block)]] = Map.empty) + +// var monoCounter = 0 +// def freshMonoName(baseId: Id, tpe: PolyType.Base): Id = +// monoCounter += 1 +// Id(baseId.name.name + tpe.tpe.name.name + monoCounter) + +// def freshMonoName(baseId: Id, tpes: List[PolyType.Base]): Id = +// monoCounter += 1 +// var tpesString = "" +// tpes.foreach(tpe => tpesString += tpe.tpe.name.name) +// Id(baseId.name.name + tpesString + monoCounter) + +// // TODO: The following two are awful and surely doing redundant work. +// def generator(xs: List[Set[PolyConstraintSingle]]): List[Set[PolyConstraintSingle]] = xs.foldRight(List(Set.empty)) { (next, combinations) => +// (for (a <- next; as <- combinations) yield as + a).toList +// } + +// def gen(xs: PolyConstraintsSolved) = { +// (for ((id, constrs) <- xs) yield (for (c <- constrs) yield Map((id -> c)))).toList +// } + +// def monoVparams(vparams: List[ValueType]): List[PolyType.Base] = vparams map monoVparam + +// def monoVparams(vparams: List[ValueParam]): List[PolyType.Base] = vparams.map(vp => monoVparam(vp.tpe)) + +// def monoVparam(valueType: ValueType): PolyType.Base = valueType match { +// case ValueType.Boxed(tpe, capt) => ??? +// case ValueType.Data(name, targs) => PolyType.Base(name) +// case ValueType.Var(name) => ??? +// } + +// def monomorphize(decl: ModuleDecl)(using ctx: MonoContext): ModuleDecl = decl match +// case ModuleDecl(path, includes, declarations, externs, definitions, exports) => +// ModuleDecl(path, includes, declarations, externs, definitions flatMap monomorphize, exports) + +// def monomorphize(definition: Toplevel)(using ctx: MonoContext): List[Toplevel] = definition match { +// case Toplevel.Def(id, block) => monomorphize(block, id).map((newId, newBlock) => Toplevel.Def(newId, newBlock)).toList +// case Toplevel.Val(id, tpe, binding) => ??? +// } + +// def monomorphize(block: Block, baseId: Id)(using ctx: MonoContext): List[(Id, Block)] = block match { +// case BlockLit(List(), cparams, vparams, bparams, body) => { +// val monoBody = monomorphize(body, Map.empty) +// val monoBlock = BlockLit(List.empty, cparams, vparams, bparams, monoBody) + +// List((baseId, monoBlock)) +// } +// case BlockLit(tparams, cparams, vparams, bparams, body) => { +// // TODO: There is some redundancy here, but it works for now +// val relevantConstraints = tparams.map(tp => Map(tp -> ctx.solvedConstraints.getOrElse(tp, Set.empty)).filter((_, s) => !s.isEmpty)) +// val splitConstraints = relevantConstraints.flatMap(gen) +// val combinations = generator(splitConstraints) +// val flattened = combinations.map(c => c.flatten.toMap) +// val res = flattened.map(f => { +// ctx.monoDefs.getOrElse(baseId, { +// // TODO: Not really happy with this +// val baseTypes = monoVparams(vparams) + +// val newId = freshMonoName(baseId, f.values.toList) +// val newVparams = vparams.map(monomorphize(_, f)) +// val newBlock = BlockLit(List(), cparams, newVparams, bparams, monomorphize(body, f)) +// ctx.monoDefs += (baseId -> Map(baseTypes -> (newId, newBlock))) +// (newId, newBlock) +// }) +// }) +// // res +// ??? +// } +// case BlockVar(id, annotatedTpe, annotatedCapt) => ??? +// case New(impl) => ??? +// case Unbox(pure) => ??? +// } + +// def monomorphize(stmt: Stmt, replacementTparam: Map[Id, PolyType.Base])(using ctx: MonoContext): Stmt = stmt match { +// case Return(expr) => Return(monomorphize(expr, replacementTparam)) +// case Val(id, annotatedTpe, binding, body) => Val(id, annotatedTpe, monomorphize(binding, replacementTparam), monomorphize(body, replacementTparam)) +// case App(callee, List(), vargs, bargs) => App(callee, List(), vargs, bargs) +// case App(callee, targs, vargs, bargs) => { +// // TODO: This does not seem correct, but I need the base id of the BlockVar to monomorphize +// // Not sure if doing everything in one go is possible to do correctly +// callee match { +// case BlockVar(id, annotatedTpe, annotatedCapt) => { +// // TODO: What if the block has not been generated yet? +// // We don't use names of blocks but pass entire blocks +// val baseTypes = targs map monoVparam +// val genCallee = ctx.monoDefs.getOrElse((id, baseTypes), ???) +// val f = App(genCallee._2, List(), vargs, bargs) +// println(f) +// f +// } +// case _ => ??? +// } +// } +// case Let(id, annotatedTpe, binding, body) => Let(id, annotatedTpe, monomorphize(binding, replacementTparam), monomorphize(body, replacementTparam)) +// case If(cond, thn, els) => If(monomorphize(cond, replacementTparam), monomorphize(thn, replacementTparam), monomorphize(els, replacementTparam)) +// case o => println(o); ??? +// } + +// def monomorphize(expr: Expr, replacementTparam: Map[Id, PolyType.Base]): Expr = expr match { +// case DirectApp(b, List(), vargs, bargs) => DirectApp(b, List(), vargs, bargs) +// case o => println(o); ??? +// } + +// def monomorphize(pure: Pure, replacementTparam: Map[Id, PolyType.Base]): Pure = pure match +// case ValueVar(id, annotatedType) => ValueVar(id, monomorphize(annotatedType, replacementTparam)) +// case Literal(value, annotatedType) => pure +// case o => println(o); ??? + +// def monomorphize(vparam: ValueParam, replacementTparam: Map[Id, PolyType.Base]): ValueParam = vparam match { +// case ValueParam(id, tpe) => ValueParam(id, monomorphize(tpe, replacementTparam)) +// } + +// def monomorphize(valueType: ValueType, replacementTparam: Map[Id, PolyType.Base]): ValueType = valueType match +// case ValueType.Var(name) => replacementTparam.get(name).get.toValueType +// case ValueType.Data(name, targs) => valueType +// case ValueType.Boxed(tpe, capt) => ??? // TODO: After solving the constraints it would be helpful to know // which functions have which tparams // so we can generate the required monomorphic functions -enum PolyType { - case Base(val tpe: Id) - case Var(val sym: Id) - - def toSymbol: Id = this match { - case Base(tpe) => tpe - case Var(sym) => sym - } - - def toValueType: ValueType = this match { - case Base(tpe) => ValueType.Data(tpe, List.empty) - case Var(sym) => ValueType.Var(sym) - } -} - -def solveConstraints(constraints: PolyConstraints): PolyConstraintsSolved = - var solved: PolyConstraintsSolved = Map() - - def solveConstraint(sym: Id, types: Set[PolyType]): Set[PolyType.Base] = - var polyTypes: Set[PolyType.Base] = Set() - types.foreach { - case PolyType.Var(symbol) => polyTypes ++= solved.getOrElse(symbol, solveConstraint(symbol, constraints.getOrElse(symbol, Set()))) - case PolyType.Base(tpe) => polyTypes += PolyType.Base(tpe) - } - solved += (sym -> polyTypes) - polyTypes - - constraints.foreach(solveConstraint) - - solved - -def combineConstraints(a: PolyConstraints, b: PolyConstraints): PolyConstraints = { - a ++ b.map { case (k, v) => k -> (v ++ a.getOrElse(k, Iterable.empty)) } -} - -def findConstraints(definitions: List[Toplevel]): PolyConstraints = - definitions.map(findConstraints).reduce(combineConstraints) - -def findConstraints(toplevel: Toplevel): PolyConstraints = toplevel match { - case Toplevel.Def(id, block) => findConstraints(block, List.empty) - case Toplevel.Val(id, tpe, binding) => ??? -} - -def findConstraints(block: Block, targs: List[ValueType]): PolyConstraints = block match { - case BlockLit(tparam :: tparams, cparams, vparams, bparams, body) => findConstraints(body, tparam :: tparams) - case BlockLit(List(), cparams, vparams, bparams, body) => findConstraints(body, List.empty) - case BlockVar(id, annotatedTpe, annotatedCapt) => findConstraints(annotatedTpe, targs) - case New(impl) => ??? - case Unbox(pure) => ??? - case _ => Map.empty -} - -def findConstraints(stmt: Stmt, tparams: List[Id]): PolyConstraints = stmt match { - case App(callee, targs, vargs, bargs) => findConstraints(callee, targs) - case Return(expr) if !tparams.isEmpty => Map(tparams.head -> Set(findPolyType(expr.tpe))) - case Return(expr) => Map.empty - case Val(id, annotatedTpe, binding, body) => combineConstraints(findConstraints(binding, tparams), findConstraints(body, tparams)) - // TODO: Let & If case is wrong, but placeholders are required as they are used in print - case Let(id, annotatedTpe, binding, body) => Map.empty - case If(cond, thn, els) => Map.empty - case o => println(o); ??? -} - -def findConstraints(value: Val): PolyConstraints = value match { - // TODO: List.empty might be wrong - case Val(id, annotatedTpe, binding, body) => combineConstraints(findConstraints(binding, List.empty), findConstraints(body, List.empty)) -} - -def findConstraints(blockType: BlockType, targs: List[ValueType]): PolyConstraints = blockType match { - case BlockType.Function(tparams, cparams, vparams, bparams, result) => tparams.zip(targs).map((id, tpe) => (id -> Set(findPolyType(tpe)))).toMap - case BlockType.Interface(name, targs) => ??? -} - -def findPolyType(blockType: BlockType, targs: List[ValueType]): List[PolyType] = blockType match { - case BlockType.Function(tparams, cparams, vparams, bparams, result) => ??? - case BlockType.Interface(name, targs) => ??? -} - -def findPolyType(valueType: ValueType): PolyType = valueType match { - case ValueType.Boxed(tpe, capt) => ??? - case ValueType.Data(name, targs) => PolyType.Base(name) - case ValueType.Var(name) => PolyType.Var(name) -} - -def hasCycle(constraints: PolyConstraints): Boolean = - var visited: Set[Id] = Set() - var recStack: Set[Id] = Set() - - def hasCycleHelper(vertex: Id): Boolean = - if (recStack.contains(vertex)) return true - if (visited.contains(vertex)) return false - - visited += vertex - recStack += vertex - - var cycleFound = false - constraints.getOrElse(vertex, Set()).foreach(v => cycleFound |= hasCycleHelper(v.toSymbol)) - - recStack -= vertex - - cycleFound +// enum PolyType { +// case Base(val tpe: Id) +// case Var(val sym: Id) + +// def toSymbol: Id = this match { +// case Base(tpe) => tpe +// case Var(sym) => sym +// } + +// def toValueType: ValueType = this match { +// case Base(tpe) => ValueType.Data(tpe, List.empty) +// case Var(sym) => ValueType.Var(sym) +// } +// } + +// def solveConstraints(constraints: PolyConstraints): PolyConstraintsSolved = +// var solved: PolyConstraintsSolved = Map() + +// def solveConstraint(sym: Id, types: Set[PolyType]): Set[PolyType.Base] = +// var polyTypes: Set[PolyType.Base] = Set() +// types.foreach { +// case PolyType.Var(symbol) => polyTypes ++= solved.getOrElse(symbol, solveConstraint(symbol, constraints.getOrElse(symbol, Set()))) +// case PolyType.Base(tpe) => polyTypes += PolyType.Base(tpe) +// } +// solved += (sym -> polyTypes) +// polyTypes + +// constraints.foreach(solveConstraint) + +// solved + +// def combineConstraints(a: PolyConstraints, b: PolyConstraints): PolyConstraints = { +// a ++ b.map { case (k, v) => k -> (v ++ a.getOrElse(k, Iterable.empty)) } +// } + +// def findConstraints(definitions: List[Toplevel]): PolyConstraints = +// definitions.map(findConstraints).reduce(combineConstraints) + +// def findConstraints(toplevel: Toplevel): PolyConstraints = toplevel match { +// case Toplevel.Def(id, block) => findConstraints(block, List.empty) +// case Toplevel.Val(id, tpe, binding) => ??? +// } + +// def findConstraints(block: Block, targs: List[ValueType]): PolyConstraints = block match { +// case BlockLit(tparam :: tparams, cparams, vparams, bparams, body) => findConstraints(body, tparam :: tparams) +// case BlockLit(List(), cparams, vparams, bparams, body) => findConstraints(body, List.empty) +// case BlockVar(id, annotatedTpe, annotatedCapt) => findConstraints(annotatedTpe, targs) +// case New(impl) => ??? +// case Unbox(pure) => ??? +// case _ => Map.empty +// } + +// def findConstraints(stmt: Stmt, tparams: List[Id]): PolyConstraints = stmt match { +// case App(callee, targs, vargs, bargs) => findConstraints(callee, targs) +// case Return(expr) if !tparams.isEmpty => Map(tparams.head -> Set(findPolyType(expr.tpe))) +// case Return(expr) => Map.empty +// case Val(id, annotatedTpe, binding, body) => combineConstraints(findConstraints(binding, tparams), findConstraints(body, tparams)) +// // TODO: Let & If case is wrong, but placeholders are required as they are used in print +// case Let(id, annotatedTpe, binding, body) => Map.empty +// case If(cond, thn, els) => Map.empty +// case o => println(o); ??? +// } + +// def findConstraints(value: Val): PolyConstraints = value match { +// // TODO: List.empty might be wrong +// case Val(id, annotatedTpe, binding, body) => combineConstraints(findConstraints(binding, List.empty), findConstraints(body, List.empty)) +// } + +// def findConstraints(blockType: BlockType, targs: List[ValueType]): PolyConstraints = blockType match { +// case BlockType.Function(tparams, cparams, vparams, bparams, result) => tparams.zip(targs).map((id, tpe) => (id -> Set(findPolyType(tpe)))).toMap +// case BlockType.Interface(name, targs) => ??? +// } + +// def findPolyType(blockType: BlockType, targs: List[ValueType]): List[PolyType] = blockType match { +// case BlockType.Function(tparams, cparams, vparams, bparams, result) => ??? +// case BlockType.Interface(name, targs) => ??? +// } + +// def findPolyType(valueType: ValueType): PolyType = valueType match { +// case ValueType.Boxed(tpe, capt) => ??? +// case ValueType.Data(name, targs) => PolyType.Base(name) +// case ValueType.Var(name) => PolyType.Var(name) +// } + +// def hasCycle(constraints: PolyConstraints): Boolean = +// var visited: Set[Id] = Set() +// var recStack: Set[Id] = Set() + +// def hasCycleHelper(vertex: Id): Boolean = +// if (recStack.contains(vertex)) return true +// if (visited.contains(vertex)) return false + +// visited += vertex +// recStack += vertex + +// var cycleFound = false +// constraints.getOrElse(vertex, Set()).foreach(v => cycleFound |= hasCycleHelper(v.toSymbol)) + +// recStack -= vertex + +// cycleFound - var cycleFound = false - constraints.keys.foreach(v => cycleFound |= !visited.contains(v) && hasCycleHelper(v)) +// var cycleFound = false +// constraints.keys.foreach(v => cycleFound |= !visited.contains(v) && hasCycleHelper(v)) - cycleFound +// cycleFound From 8a1bce0ec05d32a65e2984da6b34067add648cc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Thu, 10 Jul 2025 16:03:14 +0200 Subject: [PATCH 14/29] Handle BlockLit one level higher --- effekt/shared/src/main/scala/effekt/core/Mono.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 57b34912e..624a5f291 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -51,9 +51,8 @@ type TypeParams = Map[Id, TypeArg.Var] class MonoContext { var typingContext: TypeParams = Map() - var functionId: FunctionId = Id("notAValidId") - def extendTypingContext(tparam: Id, index: Int) = + def extendTypingContext(tparam: Id, index: Int, functionId: FunctionId) = typingContext += (tparam -> TypeArg.Var(functionId, index)) } @@ -61,14 +60,15 @@ def findConstraints(definitions: List[Toplevel])(using MonoContext): Constraints definitions flatMap findConstraints def findConstraints(definition: Toplevel)(using ctx: MonoContext): Constraints = definition match - case Toplevel.Def(id, block) => ctx.functionId = id; findConstraints(block) + case Toplevel.Def(id, BlockLit(tparams, cparams, vparams, bparams, body)) => + tparams.zipWithIndex.foreach(ctx.extendTypingContext(_, _, id)) + findConstraints(body) + case Toplevel.Def(id, block) => ??? case Toplevel.Val(id, tpe, binding) => ??? def findConstraints(block: Block)(using ctx: MonoContext): Constraints = block match case BlockVar(id, annotatedTpe, annotatedCapt) => ??? - case BlockLit(tparams, cparams, vparams, bparams, body) => - tparams.zipWithIndex.foreach(ctx.extendTypingContext) - findConstraints(body) + case BlockLit(tparams, cparams, vparams, bparams, body) => ??? case Unbox(pure) => ??? case New(impl) => ??? From 3d0aaac52a2dab3c22d90b98994e3267e661412b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Fri, 11 Jul 2025 15:06:37 +0200 Subject: [PATCH 15/29] Implement solveConstraints --- .../src/main/scala/effekt/core/Mono.scala | 149 +++++------------- 1 file changed, 37 insertions(+), 112 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 624a5f291..193330431 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -20,9 +20,9 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { constraints.foreach(c => println(c)) println() - // val solved = solveConstraint(constraints) - // println("Solved") - // solved.foreach(println) + val solved = solveConstraints(constraints) + println("Solved") + solved.foreach(println) println() @@ -38,8 +38,7 @@ type FunctionId = Id case class Constraint(lower: Vector[TypeArg], upper: FunctionId) type Constraints = List[Constraint] -// case class SolvedConstraint(lower: Vector[TypeArg.Base], upper: FunctionId | TypeArg.Var) -// type SolvedConstraints = List[SolvedConstraint] +type Solution = Map[FunctionId, Set[Vector[TypeArg.Base]]] enum TypeArg { case Base(val tpe: Id) @@ -91,6 +90,39 @@ def findId(vt: ValueType)(using ctx: MonoContext): TypeArg = vt match case ValueType.Data(name, targs) => TypeArg.Base(name) case ValueType.Var(name) => ctx.typingContext(name) +def solveConstraints(constraints: Constraints): Solution = + var solved: Solution = Map() + + val groupedConstraints = constraints.groupBy(c => c.upper) + val vecConstraints = groupedConstraints.map((sym, constraints) => (sym -> constraints.map(c => c.lower))) + + vecConstraints.foreach((sym, tas) => + val sol = solveConstraints(sym).map(bs => bs.toVector) + solved += (sym -> sol) + ) + + def solveConstraints(funId: FunctionId): Set[List[TypeArg.Base]] = + val filteredConstraints = vecConstraints(funId) + var nbs: Set[List[TypeArg.Base]] = Set.empty + filteredConstraints.foreach(b => + var l: List[List[TypeArg.Base]] = List(List.empty) + def listFromIndex(ind: Int) = if (ind >= l.length) List.empty else l(ind) + b.foreach({ + case TypeArg.Base(tpe) => l = productAppend(l, List(TypeArg.Base(tpe))) + case TypeArg.Var(funId, pos) => + val funSolved = solved.getOrElse(funId, solveConstraints(funId)) + val posArgs = funSolved.map(v => v(pos)) + l = posArgs.zipWithIndex.map((base, ind) => listFromIndex(ind) :+ base).toList + println(l) + }) + nbs ++= l + ) + nbs + + solved + +def productAppend[A](ls: List[List[A]], rs: List[A]): List[List[A]] = + rs.flatMap(r => ls.map(l => l :+ r)) // Old stuff @@ -219,110 +251,3 @@ def findId(vt: ValueType)(using ctx: MonoContext): TypeArg = vt match // TODO: After solving the constraints it would be helpful to know // which functions have which tparams // so we can generate the required monomorphic functions - -// enum PolyType { -// case Base(val tpe: Id) -// case Var(val sym: Id) - -// def toSymbol: Id = this match { -// case Base(tpe) => tpe -// case Var(sym) => sym -// } - -// def toValueType: ValueType = this match { -// case Base(tpe) => ValueType.Data(tpe, List.empty) -// case Var(sym) => ValueType.Var(sym) -// } -// } - -// def solveConstraints(constraints: PolyConstraints): PolyConstraintsSolved = -// var solved: PolyConstraintsSolved = Map() - -// def solveConstraint(sym: Id, types: Set[PolyType]): Set[PolyType.Base] = -// var polyTypes: Set[PolyType.Base] = Set() -// types.foreach { -// case PolyType.Var(symbol) => polyTypes ++= solved.getOrElse(symbol, solveConstraint(symbol, constraints.getOrElse(symbol, Set()))) -// case PolyType.Base(tpe) => polyTypes += PolyType.Base(tpe) -// } -// solved += (sym -> polyTypes) -// polyTypes - -// constraints.foreach(solveConstraint) - -// solved - -// def combineConstraints(a: PolyConstraints, b: PolyConstraints): PolyConstraints = { -// a ++ b.map { case (k, v) => k -> (v ++ a.getOrElse(k, Iterable.empty)) } -// } - -// def findConstraints(definitions: List[Toplevel]): PolyConstraints = -// definitions.map(findConstraints).reduce(combineConstraints) - -// def findConstraints(toplevel: Toplevel): PolyConstraints = toplevel match { -// case Toplevel.Def(id, block) => findConstraints(block, List.empty) -// case Toplevel.Val(id, tpe, binding) => ??? -// } - -// def findConstraints(block: Block, targs: List[ValueType]): PolyConstraints = block match { -// case BlockLit(tparam :: tparams, cparams, vparams, bparams, body) => findConstraints(body, tparam :: tparams) -// case BlockLit(List(), cparams, vparams, bparams, body) => findConstraints(body, List.empty) -// case BlockVar(id, annotatedTpe, annotatedCapt) => findConstraints(annotatedTpe, targs) -// case New(impl) => ??? -// case Unbox(pure) => ??? -// case _ => Map.empty -// } - -// def findConstraints(stmt: Stmt, tparams: List[Id]): PolyConstraints = stmt match { -// case App(callee, targs, vargs, bargs) => findConstraints(callee, targs) -// case Return(expr) if !tparams.isEmpty => Map(tparams.head -> Set(findPolyType(expr.tpe))) -// case Return(expr) => Map.empty -// case Val(id, annotatedTpe, binding, body) => combineConstraints(findConstraints(binding, tparams), findConstraints(body, tparams)) -// // TODO: Let & If case is wrong, but placeholders are required as they are used in print -// case Let(id, annotatedTpe, binding, body) => Map.empty -// case If(cond, thn, els) => Map.empty -// case o => println(o); ??? -// } - -// def findConstraints(value: Val): PolyConstraints = value match { -// // TODO: List.empty might be wrong -// case Val(id, annotatedTpe, binding, body) => combineConstraints(findConstraints(binding, List.empty), findConstraints(body, List.empty)) -// } - -// def findConstraints(blockType: BlockType, targs: List[ValueType]): PolyConstraints = blockType match { -// case BlockType.Function(tparams, cparams, vparams, bparams, result) => tparams.zip(targs).map((id, tpe) => (id -> Set(findPolyType(tpe)))).toMap -// case BlockType.Interface(name, targs) => ??? -// } - -// def findPolyType(blockType: BlockType, targs: List[ValueType]): List[PolyType] = blockType match { -// case BlockType.Function(tparams, cparams, vparams, bparams, result) => ??? -// case BlockType.Interface(name, targs) => ??? -// } - -// def findPolyType(valueType: ValueType): PolyType = valueType match { -// case ValueType.Boxed(tpe, capt) => ??? -// case ValueType.Data(name, targs) => PolyType.Base(name) -// case ValueType.Var(name) => PolyType.Var(name) -// } - -// def hasCycle(constraints: PolyConstraints): Boolean = -// var visited: Set[Id] = Set() -// var recStack: Set[Id] = Set() - -// def hasCycleHelper(vertex: Id): Boolean = -// if (recStack.contains(vertex)) return true -// if (visited.contains(vertex)) return false - -// visited += vertex -// recStack += vertex - -// var cycleFound = false -// constraints.getOrElse(vertex, Set()).foreach(v => cycleFound |= hasCycleHelper(v.toSymbol)) - -// recStack -= vertex - -// cycleFound - -// var cycleFound = false -// constraints.keys.foreach(v => cycleFound |= !visited.contains(v) && hasCycleHelper(v)) - -// cycleFound From 02af077583d5c3c4b440d5ca7655b81b9238d2e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Sat, 12 Jul 2025 01:17:53 +0200 Subject: [PATCH 16/29] Monomorphize existing definitions --- .../src/main/scala/effekt/core/Mono.scala | 156 ++++++++++++++---- 1 file changed, 128 insertions(+), 28 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 193330431..39f7df6cb 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -15,17 +15,29 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { core match { case ModuleDecl(path, includes, declarations, externs, definitions, exports) => { // Find constraints in the definitions - val constraints = findConstraints(definitions)(using new MonoContext) - println("Constraints") - constraints.foreach(c => println(c)) - println() - - val solved = solveConstraints(constraints) - println("Solved") - solved.foreach(println) - println() - - + val constraints = findConstraints(definitions)(using new MonoFindContext) + // println("Constraints") + // constraints.foreach(c => println(c)) + // println() + + // Solve collected constraints + val solution = solveConstraints(constraints) + // println("Solved") + // solution.foreach(println) + // println() + + // Monomorphize existing definitions + var monoNames: MonoNames = Map.empty + solution.foreach((funId, targs) => + targs.foreach(vb => + monoNames += ((funId, vb) -> freshMonoName(funId, vb)) + ) + ) + + val monoDefs = monomorphize(definitions)(using MonoContext(solution, monoNames)) + // monoDefs.foreach(defn => println(util.show(defn))) + val newModuleDecl = ModuleDecl(path, includes, declarations, externs, monoDefs, exports) + return Some(CoreTransformed(source, tree, mod, newModuleDecl)) } } } @@ -39,6 +51,7 @@ case class Constraint(lower: Vector[TypeArg], upper: FunctionId) type Constraints = List[Constraint] type Solution = Map[FunctionId, Set[Vector[TypeArg.Base]]] +type MonoNames = Map[(FunctionId, Vector[TypeArg.Base]), FunctionId] enum TypeArg { case Base(val tpe: Id) @@ -48,30 +61,34 @@ enum TypeArg { // Type Id -> Var type TypeParams = Map[Id, TypeArg.Var] -class MonoContext { +class MonoFindContext { var typingContext: TypeParams = Map() def extendTypingContext(tparam: Id, index: Int, functionId: FunctionId) = typingContext += (tparam -> TypeArg.Var(functionId, index)) } -def findConstraints(definitions: List[Toplevel])(using MonoContext): Constraints = +case class MonoContext(solution: Solution, names: MonoNames) { + var replacementTparams: Map[Id, TypeArg.Base] = Map.empty +} + +def findConstraints(definitions: List[Toplevel])(using MonoFindContext): Constraints = definitions flatMap findConstraints -def findConstraints(definition: Toplevel)(using ctx: MonoContext): Constraints = definition match +def findConstraints(definition: Toplevel)(using ctx: MonoFindContext): Constraints = definition match case Toplevel.Def(id, BlockLit(tparams, cparams, vparams, bparams, body)) => tparams.zipWithIndex.foreach(ctx.extendTypingContext(_, _, id)) findConstraints(body) case Toplevel.Def(id, block) => ??? case Toplevel.Val(id, tpe, binding) => ??? -def findConstraints(block: Block)(using ctx: MonoContext): Constraints = block match +def findConstraints(block: Block)(using ctx: MonoFindContext): Constraints = block match case BlockVar(id, annotatedTpe, annotatedCapt) => ??? case BlockLit(tparams, cparams, vparams, bparams, body) => ??? case Unbox(pure) => ??? case New(impl) => ??? -def findConstraints(stmt: Stmt)(using ctx: MonoContext): Constraints = stmt match +def findConstraints(stmt: Stmt)(using ctx: MonoFindContext): Constraints = stmt match case Let(id, annotatedTpe, binding, body) => findConstraints(binding) ++ findConstraints(body) case Return(expr) => findConstraints(expr) case Val(id, annotatedTpe, binding, body) => findConstraints(binding) ++ findConstraints(body) @@ -79,13 +96,13 @@ def findConstraints(stmt: Stmt)(using ctx: MonoContext): Constraints = stmt matc case If(cond, thn, els) => findConstraints(cond) ++ findConstraints(thn) ++ findConstraints(els) case o => println(o); ??? -def findConstraints(expr: Expr)(using ctx: MonoContext): Constraints = expr match +def findConstraints(expr: Expr)(using ctx: MonoFindContext): Constraints = expr match case DirectApp(b, List(), vargs, bargs) => List.empty case ValueVar(id, annotatedType) => List.empty case Literal(value, annotatedType) => List.empty case o => println(o); ??? -def findId(vt: ValueType)(using ctx: MonoContext): TypeArg = vt match +def findId(vt: ValueType)(using ctx: MonoFindContext): TypeArg = vt match case ValueType.Boxed(tpe, capt) => ??? case ValueType.Data(name, targs) => TypeArg.Base(name) case ValueType.Var(name) => ctx.typingContext(name) @@ -113,7 +130,6 @@ def solveConstraints(constraints: Constraints): Solution = val funSolved = solved.getOrElse(funId, solveConstraints(funId)) val posArgs = funSolved.map(v => v(pos)) l = posArgs.zipWithIndex.map((base, ind) => listFromIndex(ind) :+ base).toList - println(l) }) nbs ++= l ) @@ -124,6 +140,99 @@ def solveConstraints(constraints: Constraints): Solution = def productAppend[A](ls: List[List[A]], rs: List[A]): List[List[A]] = rs.flatMap(r => ls.map(l => l :+ r)) +def monomorphize(definitions: List[Toplevel])(using ctx: MonoContext): List[Toplevel] = + var newDefinitions: List[Toplevel] = List.empty + definitions.foreach(definition => newDefinitions ++= monomorphize(definition)) + newDefinitions + +def monomorphize(toplevel: Toplevel)(using ctx: MonoContext): List[Toplevel] = toplevel match + case Toplevel.Def(id, BlockLit(List(), cparams, vparams, bparams, body)) => + List(Toplevel.Def(id, BlockLit(List.empty, cparams, vparams, bparams, monomorphize(body)))) + case Toplevel.Def(id, BlockLit(tparams, cparams, vparams, bparams, body)) => + val monoTypes = ctx.solution(id).toList + monoTypes.map(baseTypes => + val replacementTparams = tparams.zip(baseTypes).toMap + ctx.replacementTparams ++= replacementTparams + Toplevel.Def(ctx.names(id, baseTypes), BlockLit(List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(body))) + ) + case Toplevel.Def(id, block) => ??? + case Toplevel.Val(id, tpe, binding) => ??? + +def monomorphize(block: Block)(using ctx: MonoContext): Block = block match + case b: BlockVar => monomorphize(b) + case o => println(o); ??? + +def monomorphize(blockVar: BlockVar, replacementId: FunctionId)(using ctx: MonoContext): BlockVar = blockVar match + case BlockVar(id, BlockType.Function(List(), cparams, vparams, bparams, result), annotatedCapt) => blockVar + // TODO: What is in annotated captures. Does it need to be handled? + case BlockVar(id, BlockType.Function(tparams, cparams, vparams, bparams, result), annotatedCapt) => + val monoAnnotatedTpe = BlockType.Function(List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(result)) + BlockVar(replacementId, monoAnnotatedTpe, annotatedCapt) + case o => ??? + +def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match + case Return(expr) => Return(monomorphize(expr)) + case Val(id, annotatedTpe, binding, body) => Val(id, monomorphize(annotatedTpe), monomorphize(binding), monomorphize(body)) + case App(callee: BlockVar, targs, vargs, bargs) => + val replacementId = replacementIdFromTargs(callee.id, targs) + App(monomorphize(callee, replacementId), List.empty, vargs map monomorphize, bargs map monomorphize) + case Let(id, annotatedTpe, binding, body) => Let(id, monomorphize(annotatedTpe), monomorphize(binding), monomorphize(body)) + case If(cond, thn, els) => If(monomorphize(cond), monomorphize(thn), monomorphize(els)) + case o => println(o); ??? + +def monomorphize(expr: Expr)(using ctx: MonoContext): Expr = expr match + case DirectApp(b, targs, vargs, bargs) => + val replacementId = replacementIdFromTargs(b.id, targs) + DirectApp(monomorphize(b, replacementId), List.empty, vargs map monomorphize, bargs map monomorphize) + case o => println(o); ??? + +def monomorphize(pure: Pure)(using ctx: MonoContext): Pure = pure match + case ValueVar(id, annotatedType) => ValueVar(id, monomorphize(annotatedType)) + case PureApp(b, targs, vargs) => + val replacementId = replacementIdFromTargs(b.id, targs) + PureApp(monomorphize(b, replacementId), List.empty, vargs map monomorphize) + case Literal(value, annotatedType) => Literal(value, monomorphize(annotatedType)) + case o => println(o); ??? + +def monomorphize(valueParam: ValueParam)(using ctx: MonoContext): ValueParam = valueParam match + case ValueParam(id, tpe) => ValueParam(id, monomorphize(tpe)) + +def monomorphize(blockParam: BlockParam)(using ctx: MonoContext): BlockParam = blockParam match + // TODO: Same question as in block + case BlockParam(id, tpe, capt) => BlockParam(id, monomorphize(tpe), capt) + +def monomorphize(blockType: BlockType)(using ctx: MonoContext): BlockType = blockType match + case BlockType.Function(tparams, cparams, vparams, bparams, result) => + BlockType.Function(List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(result)) + case o => println(o); ??? + +def monomorphize(valueType: ValueType)(using ctx: MonoContext): ValueType = valueType match + case ValueType.Var(name) => ValueType.Var(ctx.replacementTparams(name).tpe) + case ValueType.Data(name, targs) => ValueType.Data(name, targs) + case o => println(o); ??? + +var monoCounter = 0 +def freshMonoName(baseId: Id, tpe: TypeArg.Base): Id = + monoCounter += 1 + Id(baseId.name.name + tpe.tpe.name.name + monoCounter) + +def freshMonoName(baseId: Id, tpes: Vector[TypeArg.Base]): Id = + if (tpes.length == 0) return baseId + + monoCounter += 1 + val tpesString = tpes.map(tpe => tpe.tpe.name.name).mkString + Id(baseId.name.name + tpesString + monoCounter) + +def replacementIdFromTargs(id: FunctionId, targs: List[ValueType])(using ctx: MonoContext): FunctionId = + if (targs.isEmpty) return id + var baseTypes: List[TypeArg.Base] = List.empty + targs.foreach({ + case ValueType.Data(name, targs) => baseTypes :+= TypeArg.Base(name) + case ValueType.Var(name) => baseTypes :+= ctx.replacementTparams(name) + case ValueType.Boxed(tpe, capt) => + }) + ctx.names((id, baseTypes.toVector)) + // Old stuff // type PolyConstraints = Map[Id, Set[PolyType]] @@ -132,16 +241,7 @@ def productAppend[A](ls: List[List[A]], rs: List[A]): List[List[A]] = // class MonoContext(val solvedConstraints: PolyConstraintsSolved, var monoDefs: Map[Id, Map[List[PolyType.Base], (Id, Block)]] = Map.empty) -// var monoCounter = 0 -// def freshMonoName(baseId: Id, tpe: PolyType.Base): Id = -// monoCounter += 1 -// Id(baseId.name.name + tpe.tpe.name.name + monoCounter) -// def freshMonoName(baseId: Id, tpes: List[PolyType.Base]): Id = -// monoCounter += 1 -// var tpesString = "" -// tpes.foreach(tpe => tpesString += tpe.tpe.name.name) -// Id(baseId.name.name + tpesString + monoCounter) // // TODO: The following two are awful and surely doing redundant work. // def generator(xs: List[Set[PolyConstraintSingle]]): List[Set[PolyConstraintSingle]] = xs.foldRight(List(Set.empty)) { (next, combinations) => From acd3d0b0bc49488fe0fbafd1bd7a506bcb619185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Wed, 23 Jul 2025 14:10:31 +0200 Subject: [PATCH 17/29] Handle recursive functions when solving --- effekt/shared/src/main/scala/effekt/core/Mono.scala | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 39f7df6cb..d03acc950 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -113,10 +113,14 @@ def solveConstraints(constraints: Constraints): Solution = val groupedConstraints = constraints.groupBy(c => c.upper) val vecConstraints = groupedConstraints.map((sym, constraints) => (sym -> constraints.map(c => c.lower))) + while (true) { + val previousSolved = solved vecConstraints.foreach((sym, tas) => val sol = solveConstraints(sym).map(bs => bs.toVector) - solved += (sym -> sol) - ) + solved += (sym -> sol) + ) + if (previousSolved == solved) return solved + } def solveConstraints(funId: FunctionId): Set[List[TypeArg.Base]] = val filteredConstraints = vecConstraints(funId) @@ -127,7 +131,7 @@ def solveConstraints(constraints: Constraints): Solution = b.foreach({ case TypeArg.Base(tpe) => l = productAppend(l, List(TypeArg.Base(tpe))) case TypeArg.Var(funId, pos) => - val funSolved = solved.getOrElse(funId, solveConstraints(funId)) + val funSolved = solved.getOrElse(funId, Set.empty) val posArgs = funSolved.map(v => v(pos)) l = posArgs.zipWithIndex.map((base, ind) => listFromIndex(ind) :+ base).toList }) From ddc84dc200a6719ee7ccf3722019cae9dfd14937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Wed, 23 Jul 2025 14:14:00 +0200 Subject: [PATCH 18/29] Test Mono solving --- .../test/scala/effekt/core/MonoTests.scala | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 effekt/jvm/src/test/scala/effekt/core/MonoTests.scala diff --git a/effekt/jvm/src/test/scala/effekt/core/MonoTests.scala b/effekt/jvm/src/test/scala/effekt/core/MonoTests.scala new file mode 100644 index 000000000..8302583a0 --- /dev/null +++ b/effekt/jvm/src/test/scala/effekt/core/MonoTests.scala @@ -0,0 +1,112 @@ +package effekt +package core + + +abstract class AbstractMonoTests extends CorePhaseTests(Mono) { + import TypeArg.* + + implicit def stringBaseT(name: String): Base = Base(Id(name)) + + val BaseTInt: Base = "Int" + val BaseTString: Base = "String" + val BaseTChar: Base = "Char" + val BaseTBool: Base = "Bool" + val BaseTDouble: Base = "Double" + + val fnId: Map[String, FunctionId] = Map( + "a" -> Id("a"), + "b" -> Id("b"), + "c" -> Id("c"), + "d" -> Id("d"), + "e" -> Id("e"), + "f" -> Id("f"), + ) +} + +class MonoTests extends AbstractMonoTests { + + import TypeArg.* + + test("simple polymorphic function") { + val constraints = List( + Constraint(Vector(BaseTInt), fnId("a")), + Constraint(Vector(BaseTString), fnId("a")) + ) + val expectedSolved: Solution = Map( + fnId("a") -> Set(Vector(BaseTInt), Vector(BaseTString)) + ) + + assertEquals(solveConstraints(constraints), expectedSolved) + } + + test("calling other polymorphic function") { + val constraints = List( + Constraint(Vector(Var(fnId("b"), 0)), fnId("a")), + Constraint(Vector(BaseTInt), fnId("a")), + Constraint(Vector(BaseTString), fnId("b")), + ) + val expectedSolved: Solution = Map( + fnId("a") -> Set(Vector(BaseTInt), Vector(BaseTString)), + fnId("b") -> Set(Vector(BaseTString)), + ) + + assertEquals(solveConstraints(constraints), expectedSolved) + } + + test("polymorphic function with multiple type args") { + val constraints = List( + Constraint(Vector(BaseTInt, BaseTString), fnId("a")), + Constraint(Vector(BaseTBool, BaseTChar), fnId("a")), + Constraint(Vector(BaseTBool, BaseTString), fnId("a")), + ) + val expectedSolved: Solution = Map( + fnId("a") -> Set( + Vector(BaseTInt, BaseTString), + Vector(BaseTBool, BaseTChar), + Vector(BaseTBool, BaseTString), + ) + ) + + assertEquals(solveConstraints(constraints), expectedSolved) + } + + test("calling other polymorphic function with type args swapped") { + val constraints = List( + Constraint(Vector(Var(fnId("b"), 1), Var(fnId("b"), 0)), fnId("a")), + Constraint(Vector(BaseTString, BaseTBool), fnId("b")), + ) + val expectedSolved: Solution = Map( + fnId("a") -> Set(Vector(BaseTBool, BaseTString)), + fnId("b") -> Set(Vector(BaseTString, BaseTBool)), + ) + + assertEquals(solveConstraints(constraints), expectedSolved) + } + + test("recursive polymorphic function") { + val constraints = List( + Constraint(Vector(Var(fnId("a"), 0)), fnId("a")), + Constraint(Vector(BaseTInt), fnId("a")), + ) + val expectedSolved: Solution = Map( + fnId("a") -> Set(Vector(BaseTInt)), + ) + + assertEquals(solveConstraints(constraints), expectedSolved) + } + + test("mutually recursive polymorphic functions") { + val constraints = List( + Constraint(Vector(Var(fnId("b"), 0)), fnId("a")), + Constraint(Vector(Var(fnId("a"), 0)), fnId("b")), + Constraint(Vector(BaseTInt), fnId("a")), + Constraint(Vector(BaseTString), fnId("b")), + ) + val expectedSolved: Solution = Map( + fnId("a") -> Set(Vector(BaseTInt), Vector(BaseTString)), + fnId("b") -> Set(Vector(BaseTInt), Vector(BaseTString)), + ) + + assertEquals(solveConstraints(constraints), expectedSolved) + } +} From 3a650808611df0b4b61c564423da24fe7526a434 Mon Sep 17 00:00:00 2001 From: Mattis Boeckle Date: Fri, 25 Jul 2025 13:35:36 +0200 Subject: [PATCH 19/29] Monomorphize Declarations --- .../src/main/scala/effekt/core/Mono.scala | 206 ++++++------------ 1 file changed, 69 insertions(+), 137 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index d03acc950..b8d42423c 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -15,7 +15,9 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { core match { case ModuleDecl(path, includes, declarations, externs, definitions, exports) => { // Find constraints in the definitions - val constraints = findConstraints(definitions)(using new MonoFindContext) + val monoFindContext = MonoFindContext() + val constraints = findConstraints(definitions)(using monoFindContext) + val declConstraints = declarations map (findConstraints(_)(using monoFindContext)) // println("Constraints") // constraints.foreach(c => println(c)) // println() @@ -34,9 +36,12 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { ) ) - val monoDefs = monomorphize(definitions)(using MonoContext(solution, monoNames)) + var monoContext = MonoContext(solution, monoNames) + val monoDecls = declarations flatMap (monomorphize(_)(using monoContext)) + val monoDefs = monomorphize(definitions)(using monoContext) + // monoDecls.foreach(decl => println(util.show(decl))) // monoDefs.foreach(defn => println(util.show(defn))) - val newModuleDecl = ModuleDecl(path, includes, declarations, externs, monoDefs, exports) + val newModuleDecl = ModuleDecl(path, includes, monoDecls, externs, monoDefs, exports) return Some(CoreTransformed(source, tree, mod, newModuleDecl)) } } @@ -82,12 +87,28 @@ def findConstraints(definition: Toplevel)(using ctx: MonoFindContext): Constrain case Toplevel.Def(id, block) => ??? case Toplevel.Val(id, tpe, binding) => ??? +def findConstraints(declaration: Declaration)(using ctx: MonoFindContext): Constraints = declaration match + case Data(id, List(), constructors) => List.empty + case Data(id, tparams, constructors) => + tparams.zipWithIndex.foreach(ctx.extendTypingContext(_, _, id)) + constructors.map((constr => + Constraint(((constr.fields map (_.tpe)) map findId).toVector, constr.id))) + case Interface(id, List(), properties) => List.empty + case Interface(id, tparams, properties) => + tparams.zipWithIndex.foreach(ctx.extendTypingContext(_, _, id)) + List.empty + def findConstraints(block: Block)(using ctx: MonoFindContext): Constraints = block match case BlockVar(id, annotatedTpe, annotatedCapt) => ??? case BlockLit(tparams, cparams, vparams, bparams, body) => ??? case Unbox(pure) => ??? case New(impl) => ??? +def findConstraints(constructor: Constructor)(using ctx: MonoFindContext): Constraints = constructor match + case Constructor(id, List()) => List.empty + case Constructor(id, fields) => + List(Constraint(((fields map (_.tpe)) map findId).toVector, id)) + def findConstraints(stmt: Stmt)(using ctx: MonoFindContext): Constraints = stmt match case Let(id, annotatedTpe, binding, body) => findConstraints(binding) ++ findConstraints(body) case Return(expr) => findConstraints(expr) @@ -100,6 +121,7 @@ def findConstraints(expr: Expr)(using ctx: MonoFindContext): Constraints = expr case DirectApp(b, List(), vargs, bargs) => List.empty case ValueVar(id, annotatedType) => List.empty case Literal(value, annotatedType) => List.empty + case Make(data, tag, targs, vargs) => List(Constraint(data.targs.map(findId).toVector, data.name)) case o => println(o); ??? def findId(vt: ValueType)(using ctx: MonoFindContext): TypeArg = vt match @@ -115,10 +137,10 @@ def solveConstraints(constraints: Constraints): Solution = while (true) { val previousSolved = solved - vecConstraints.foreach((sym, tas) => - val sol = solveConstraints(sym).map(bs => bs.toVector) - solved += (sym -> sol) - ) + vecConstraints.foreach((sym, tas) => + val sol = solveConstraints(sym).map(bs => bs.toVector) + solved += (sym -> sol) + ) if (previousSolved == solved) return solved } @@ -162,10 +184,35 @@ def monomorphize(toplevel: Toplevel)(using ctx: MonoContext): List[Toplevel] = t case Toplevel.Def(id, block) => ??? case Toplevel.Val(id, tpe, binding) => ??? +def monomorphize(decl: Declaration)(using ctx: MonoContext): List[Declaration] = decl match + case Data(id, List(), constructors) => List(decl) + case Data(id, tparams, constructors) => + val monoTypes = ctx.solution.getOrElse(id, Set.empty).toList + monoTypes.map(baseTypes => + val replacementTparams = tparams.zip(baseTypes).toMap + ctx.replacementTparams ++= replacementTparams + val newConstructors = constructors map { + case Constructor(id, List()) => Constructor(id, List.empty) + case Constructor(id, fields) => Constructor(id, fields map monomorphize) + } + Declaration.Data(ctx.names(id, baseTypes), List.empty, newConstructors) + ) + case Interface(id, List(), properties) => List(decl) + case Interface(id, tparams, properties) => + val monoTypes = ctx.solution.getOrElse(id, Set.empty).toList + monoTypes.map(baseTypes => + val replacementTparams = tparams.zip(baseTypes).toMap + ctx.replacementTparams ++= replacementTparams + Declaration.Interface(ctx.names(id, baseTypes), List.empty, properties) + ) + def monomorphize(block: Block)(using ctx: MonoContext): Block = block match case b: BlockVar => monomorphize(b) case o => println(o); ??? +def monomorphize(field: Field)(using ctx: MonoContext): Field = field match + case Field(id, tpe) => Field(id, monomorphize(tpe)) + def monomorphize(blockVar: BlockVar, replacementId: FunctionId)(using ctx: MonoContext): BlockVar = blockVar match case BlockVar(id, BlockType.Function(List(), cparams, vparams, bparams, result), annotatedCapt) => blockVar // TODO: What is in annotated captures. Does it need to be handled? @@ -176,26 +223,30 @@ def monomorphize(blockVar: BlockVar, replacementId: FunctionId)(using ctx: MonoC def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match case Return(expr) => Return(monomorphize(expr)) - case Val(id, annotatedTpe, binding, body) => Val(id, monomorphize(annotatedTpe), monomorphize(binding), monomorphize(body)) + case Val(id, annotatedTpe, binding, body) => + Val(id, monomorphize(annotatedTpe), monomorphize(binding), monomorphize(body)) case App(callee: BlockVar, targs, vargs, bargs) => - val replacementId = replacementIdFromTargs(callee.id, targs) - App(monomorphize(callee, replacementId), List.empty, vargs map monomorphize, bargs map monomorphize) + val replacementData = replacementDataFromTargs(callee.id, targs) + App(monomorphize(callee, replacementData.name), List.empty, vargs map monomorphize, bargs map monomorphize) case Let(id, annotatedTpe, binding, body) => Let(id, monomorphize(annotatedTpe), monomorphize(binding), monomorphize(body)) case If(cond, thn, els) => If(monomorphize(cond), monomorphize(thn), monomorphize(els)) case o => println(o); ??? def monomorphize(expr: Expr)(using ctx: MonoContext): Expr = expr match case DirectApp(b, targs, vargs, bargs) => - val replacementId = replacementIdFromTargs(b.id, targs) - DirectApp(monomorphize(b, replacementId), List.empty, vargs map monomorphize, bargs map monomorphize) + val replacementData = replacementDataFromTargs(b.id, targs) + DirectApp(monomorphize(b, replacementData.name), List.empty, vargs map monomorphize, bargs map monomorphize) case o => println(o); ??? def monomorphize(pure: Pure)(using ctx: MonoContext): Pure = pure match case ValueVar(id, annotatedType) => ValueVar(id, monomorphize(annotatedType)) case PureApp(b, targs, vargs) => - val replacementId = replacementIdFromTargs(b.id, targs) - PureApp(monomorphize(b, replacementId), List.empty, vargs map monomorphize) + val replacementData = replacementDataFromTargs(b.id, targs) + PureApp(monomorphize(b, replacementData.name), List.empty, vargs map monomorphize) case Literal(value, annotatedType) => Literal(value, monomorphize(annotatedType)) + case Make(data, tag, targs, vargs) => + val replacementData = replacementDataFromTargs(data.name, data.targs) + Make(replacementData, tag, List.empty, vargs map monomorphize) case o => println(o); ??? def monomorphize(valueParam: ValueParam)(using ctx: MonoContext): ValueParam = valueParam match @@ -212,7 +263,7 @@ def monomorphize(blockType: BlockType)(using ctx: MonoContext): BlockType = bloc def monomorphize(valueType: ValueType)(using ctx: MonoContext): ValueType = valueType match case ValueType.Var(name) => ValueType.Var(ctx.replacementTparams(name).tpe) - case ValueType.Data(name, targs) => ValueType.Data(name, targs) + case ValueType.Data(name, targs) => replacementDataFromTargs(name, targs) case o => println(o); ??? var monoCounter = 0 @@ -227,131 +278,12 @@ def freshMonoName(baseId: Id, tpes: Vector[TypeArg.Base]): Id = val tpesString = tpes.map(tpe => tpe.tpe.name.name).mkString Id(baseId.name.name + tpesString + monoCounter) -def replacementIdFromTargs(id: FunctionId, targs: List[ValueType])(using ctx: MonoContext): FunctionId = - if (targs.isEmpty) return id +def replacementDataFromTargs(id: FunctionId, targs: List[ValueType])(using ctx: MonoContext): ValueType.Data = + if (targs.isEmpty) return ValueType.Data(id, targs) var baseTypes: List[TypeArg.Base] = List.empty targs.foreach({ case ValueType.Data(name, targs) => baseTypes :+= TypeArg.Base(name) case ValueType.Var(name) => baseTypes :+= ctx.replacementTparams(name) case ValueType.Boxed(tpe, capt) => }) - ctx.names((id, baseTypes.toVector)) - -// Old stuff - -// type PolyConstraints = Map[Id, Set[PolyType]] -// type PolyConstraintsSolved = Map[Id, Set[PolyType.Base]] -// type PolyConstraintSingle = Map[Id, PolyType.Base] - -// class MonoContext(val solvedConstraints: PolyConstraintsSolved, var monoDefs: Map[Id, Map[List[PolyType.Base], (Id, Block)]] = Map.empty) - - - -// // TODO: The following two are awful and surely doing redundant work. -// def generator(xs: List[Set[PolyConstraintSingle]]): List[Set[PolyConstraintSingle]] = xs.foldRight(List(Set.empty)) { (next, combinations) => -// (for (a <- next; as <- combinations) yield as + a).toList -// } - -// def gen(xs: PolyConstraintsSolved) = { -// (for ((id, constrs) <- xs) yield (for (c <- constrs) yield Map((id -> c)))).toList -// } - -// def monoVparams(vparams: List[ValueType]): List[PolyType.Base] = vparams map monoVparam - -// def monoVparams(vparams: List[ValueParam]): List[PolyType.Base] = vparams.map(vp => monoVparam(vp.tpe)) - -// def monoVparam(valueType: ValueType): PolyType.Base = valueType match { -// case ValueType.Boxed(tpe, capt) => ??? -// case ValueType.Data(name, targs) => PolyType.Base(name) -// case ValueType.Var(name) => ??? -// } - -// def monomorphize(decl: ModuleDecl)(using ctx: MonoContext): ModuleDecl = decl match -// case ModuleDecl(path, includes, declarations, externs, definitions, exports) => -// ModuleDecl(path, includes, declarations, externs, definitions flatMap monomorphize, exports) - -// def monomorphize(definition: Toplevel)(using ctx: MonoContext): List[Toplevel] = definition match { -// case Toplevel.Def(id, block) => monomorphize(block, id).map((newId, newBlock) => Toplevel.Def(newId, newBlock)).toList -// case Toplevel.Val(id, tpe, binding) => ??? -// } - -// def monomorphize(block: Block, baseId: Id)(using ctx: MonoContext): List[(Id, Block)] = block match { -// case BlockLit(List(), cparams, vparams, bparams, body) => { -// val monoBody = monomorphize(body, Map.empty) -// val monoBlock = BlockLit(List.empty, cparams, vparams, bparams, monoBody) - -// List((baseId, monoBlock)) -// } -// case BlockLit(tparams, cparams, vparams, bparams, body) => { -// // TODO: There is some redundancy here, but it works for now -// val relevantConstraints = tparams.map(tp => Map(tp -> ctx.solvedConstraints.getOrElse(tp, Set.empty)).filter((_, s) => !s.isEmpty)) -// val splitConstraints = relevantConstraints.flatMap(gen) -// val combinations = generator(splitConstraints) -// val flattened = combinations.map(c => c.flatten.toMap) -// val res = flattened.map(f => { -// ctx.monoDefs.getOrElse(baseId, { -// // TODO: Not really happy with this -// val baseTypes = monoVparams(vparams) - -// val newId = freshMonoName(baseId, f.values.toList) -// val newVparams = vparams.map(monomorphize(_, f)) -// val newBlock = BlockLit(List(), cparams, newVparams, bparams, monomorphize(body, f)) -// ctx.monoDefs += (baseId -> Map(baseTypes -> (newId, newBlock))) -// (newId, newBlock) -// }) -// }) -// // res -// ??? -// } -// case BlockVar(id, annotatedTpe, annotatedCapt) => ??? -// case New(impl) => ??? -// case Unbox(pure) => ??? -// } - -// def monomorphize(stmt: Stmt, replacementTparam: Map[Id, PolyType.Base])(using ctx: MonoContext): Stmt = stmt match { -// case Return(expr) => Return(monomorphize(expr, replacementTparam)) -// case Val(id, annotatedTpe, binding, body) => Val(id, annotatedTpe, monomorphize(binding, replacementTparam), monomorphize(body, replacementTparam)) -// case App(callee, List(), vargs, bargs) => App(callee, List(), vargs, bargs) -// case App(callee, targs, vargs, bargs) => { -// // TODO: This does not seem correct, but I need the base id of the BlockVar to monomorphize -// // Not sure if doing everything in one go is possible to do correctly -// callee match { -// case BlockVar(id, annotatedTpe, annotatedCapt) => { -// // TODO: What if the block has not been generated yet? -// // We don't use names of blocks but pass entire blocks -// val baseTypes = targs map monoVparam -// val genCallee = ctx.monoDefs.getOrElse((id, baseTypes), ???) -// val f = App(genCallee._2, List(), vargs, bargs) -// println(f) -// f -// } -// case _ => ??? -// } -// } -// case Let(id, annotatedTpe, binding, body) => Let(id, annotatedTpe, monomorphize(binding, replacementTparam), monomorphize(body, replacementTparam)) -// case If(cond, thn, els) => If(monomorphize(cond, replacementTparam), monomorphize(thn, replacementTparam), monomorphize(els, replacementTparam)) -// case o => println(o); ??? -// } - -// def monomorphize(expr: Expr, replacementTparam: Map[Id, PolyType.Base]): Expr = expr match { -// case DirectApp(b, List(), vargs, bargs) => DirectApp(b, List(), vargs, bargs) -// case o => println(o); ??? -// } - -// def monomorphize(pure: Pure, replacementTparam: Map[Id, PolyType.Base]): Pure = pure match -// case ValueVar(id, annotatedType) => ValueVar(id, monomorphize(annotatedType, replacementTparam)) -// case Literal(value, annotatedType) => pure -// case o => println(o); ??? - -// def monomorphize(vparam: ValueParam, replacementTparam: Map[Id, PolyType.Base]): ValueParam = vparam match { -// case ValueParam(id, tpe) => ValueParam(id, monomorphize(tpe, replacementTparam)) -// } - -// def monomorphize(valueType: ValueType, replacementTparam: Map[Id, PolyType.Base]): ValueType = valueType match -// case ValueType.Var(name) => replacementTparam.get(name).get.toValueType -// case ValueType.Data(name, targs) => valueType -// case ValueType.Boxed(tpe, capt) => ??? - -// TODO: After solving the constraints it would be helpful to know -// which functions have which tparams -// so we can generate the required monomorphic functions + ValueType.Data(ctx.names((id, baseTypes.toVector)), List.empty) From 094193d5b0c21a279abd10655c9a9fee35335de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Mon, 28 Jul 2025 14:45:25 +0200 Subject: [PATCH 20/29] Monomorphize effects --- .../src/main/scala/effekt/core/Mono.scala | 81 +++++++++++++++++-- 1 file changed, 73 insertions(+), 8 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index b8d42423c..8aa535716 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -39,8 +39,9 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { var monoContext = MonoContext(solution, monoNames) val monoDecls = declarations flatMap (monomorphize(_)(using monoContext)) val monoDefs = monomorphize(definitions)(using monoContext) - // monoDecls.foreach(decl => println(util.show(decl))) - // monoDefs.foreach(defn => println(util.show(defn))) + // println(util.show(monoDecls)) + // println() + // println(util.show(monoDefs)) val newModuleDecl = ModuleDecl(path, includes, monoDecls, externs, monoDefs, exports) return Some(CoreTransformed(source, tree, mod, newModuleDecl)) } @@ -99,10 +100,24 @@ def findConstraints(declaration: Declaration)(using ctx: MonoFindContext): Const List.empty def findConstraints(block: Block)(using ctx: MonoFindContext): Constraints = block match - case BlockVar(id, annotatedTpe, annotatedCapt) => ??? - case BlockLit(tparams, cparams, vparams, bparams, body) => ??? + case BlockVar(id, annotatedTpe, annotatedCapt) => findConstraints(annotatedTpe) + case BlockLit(tparams, cparams, vparams, bparams, body) => findConstraints(body) case Unbox(pure) => ??? - case New(impl) => ??? + case New(impl) => findConstraints(impl) + +def findConstraints(blockType: BlockType)(using ctx: MonoFindContext): Constraints = blockType match + case BlockType.Interface(name, targs) => List(Constraint(targs.map(findId).toVector, name)) + case o => println(o); ??? + +def findConstraints(impl: Implementation)(using ctx: MonoFindContext): Constraints = impl match + case Implementation(interface, operations) => + findConstraints(interface) ++ + (operations flatMap findConstraints) + +def findConstraints(operation: Operation)(using ctx: MonoFindContext): Constraints = operation match + case Operation(name, tparams, cparams, vparams, bparams, body) => + tparams.zipWithIndex.foreach(ctx.extendTypingContext(_, _, name)) + findConstraints(body) def findConstraints(constructor: Constructor)(using ctx: MonoFindContext): Constraints = constructor match case Constructor(id, List()) => List.empty @@ -114,11 +129,22 @@ def findConstraints(stmt: Stmt)(using ctx: MonoFindContext): Constraints = stmt case Return(expr) => findConstraints(expr) case Val(id, annotatedTpe, binding, body) => findConstraints(binding) ++ findConstraints(body) case App(callee: BlockVar, targs, vargs, bargs) => List(Constraint(targs.map(findId).toVector, callee.id)) + case Invoke(callee: BlockVar, method, methodTpe, targs, vargs, bargs) => List(Constraint(targs.map(findId).toVector, callee.id)) + case Reset(body) => findConstraints(body) case If(cond, thn, els) => findConstraints(cond) ++ findConstraints(thn) ++ findConstraints(els) + case Def(id, block, body) => findConstraints(block) ++ findConstraints(body) + case Shift(prompt, body) => findConstraints(prompt) ++ findConstraints(body) + case Match(scrutinee, clauses, default) => clauses.map(_._2).flatMap(findConstraints) ++ findConstraints(default) + case Resume(k, body) => findConstraints(k) ++ findConstraints(body) case o => println(o); ??? +def findConstraints(opt: Option[Stmt])(using ctx: MonoFindContext): Constraints = opt match + case None => List.empty + case Some(stmt) => findConstraints(stmt) + def findConstraints(expr: Expr)(using ctx: MonoFindContext): Constraints = expr match case DirectApp(b, List(), vargs, bargs) => List.empty + case PureApp(b, List(), vargs) => List.empty case ValueVar(id, annotatedType) => List.empty case Literal(value, annotatedType) => List.empty case Make(data, tag, targs, vargs) => List(Constraint(data.targs.map(findId).toVector, data.name)) @@ -207,8 +233,29 @@ def monomorphize(decl: Declaration)(using ctx: MonoContext): List[Declaration] = ) def monomorphize(block: Block)(using ctx: MonoContext): Block = block match + case b: BlockLit => monomorphize(b) case b: BlockVar => monomorphize(b) - case o => println(o); ??? + case New(impl) => New(monomorphize(impl)) + case o => println(o); ??? + +def monomorphize(impl: Implementation)(using ctx: MonoContext): Implementation = impl match + case Implementation(interface, operations) => Implementation(monomorphize(interface), operations.map(monomorphize)) + +def monomorphize(interface: BlockType.Interface)(using ctx: MonoContext): BlockType.Interface = interface match + case BlockType.Interface(name, targs) => + val replacementData = replacementDataFromTargs(name, targs) + BlockType.Interface(replacementData.name, replacementData.targs) + +def monomorphize(operation: Operation)(using ctx: MonoContext): Operation = operation match + case Operation(name, tparams, cparams, vparams, bparams, body) => + Operation(name, List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(body)) + +def monomorphize(block: BlockLit)(using ctx: MonoContext): BlockLit = block match + case BlockLit(tparams, cparams, vparams, bparams, body) => + BlockLit(List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(body)) + +def monomorphize(block: BlockVar)(using ctx: MonoContext): BlockVar = block match + case BlockVar(id, annotatedTpe, annotatedCapt) => BlockVar(id, monomorphize(annotatedTpe), annotatedCapt) def monomorphize(field: Field)(using ctx: MonoContext): Field = field match case Field(id, tpe) => Field(id, monomorphize(tpe)) @@ -219,7 +266,7 @@ def monomorphize(blockVar: BlockVar, replacementId: FunctionId)(using ctx: MonoC case BlockVar(id, BlockType.Function(tparams, cparams, vparams, bparams, result), annotatedCapt) => val monoAnnotatedTpe = BlockType.Function(List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(result)) BlockVar(replacementId, monoAnnotatedTpe, annotatedCapt) - case o => ??? + case o => println(o); ??? def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match case Return(expr) => Return(monomorphize(expr)) @@ -230,8 +277,22 @@ def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match App(monomorphize(callee, replacementData.name), List.empty, vargs map monomorphize, bargs map monomorphize) case Let(id, annotatedTpe, binding, body) => Let(id, monomorphize(annotatedTpe), monomorphize(binding), monomorphize(body)) case If(cond, thn, els) => If(monomorphize(cond), monomorphize(thn), monomorphize(els)) + case Invoke(BlockVar(id, annotatedTpe, annotatedCapt), method, methodTpe, targs, vargs, bargs) => + Invoke(BlockVar(id, monomorphize(annotatedTpe), annotatedCapt), method, methodTpe, List.empty, vargs map monomorphize, bargs map monomorphize) + // TODO: Monomorphizing here throws an error complaining about a missing implementation + // Not sure what is missing, altough it does works like this + case Reset(body) => Reset(body) + case Def(id, block, body) => Def(id, monomorphize(block), monomorphize(body)) + case Shift(prompt, body) => Shift(monomorphize(prompt), monomorphize(body)) + case Match(scrutinee, clauses, default) => + val monoClauses = clauses.map((id, blockLit) => (id, monomorphize(blockLit))) + Match(monomorphize(scrutinee), monoClauses, monomorphize(default)) case o => println(o); ??? +def monomorphize(opt: Option[Stmt])(using ctx: MonoContext): Option[Stmt] = opt match + case None => None + case Some(stmt) => Some(monomorphize(stmt)) + def monomorphize(expr: Expr)(using ctx: MonoContext): Expr = expr match case DirectApp(b, targs, vargs, bargs) => val replacementData = replacementDataFromTargs(b.id, targs) @@ -259,7 +320,7 @@ def monomorphize(blockParam: BlockParam)(using ctx: MonoContext): BlockParam = b def monomorphize(blockType: BlockType)(using ctx: MonoContext): BlockType = blockType match case BlockType.Function(tparams, cparams, vparams, bparams, result) => BlockType.Function(List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(result)) - case o => println(o); ??? + case b: BlockType.Interface => monomorphize(b) def monomorphize(valueType: ValueType)(using ctx: MonoContext): ValueType = valueType match case ValueType.Var(name) => ValueType.Var(ctx.replacementTparams(name).tpe) @@ -280,6 +341,10 @@ def freshMonoName(baseId: Id, tpes: Vector[TypeArg.Base]): Id = def replacementDataFromTargs(id: FunctionId, targs: List[ValueType])(using ctx: MonoContext): ValueType.Data = if (targs.isEmpty) return ValueType.Data(id, targs) + // TODO: Incredibly hacky, resume did not seem to appear when finding constraints + // it does show up while monomorphizing which caused an error + // this seems to work for now + if (id.name.name == "Resume") return ValueType.Data(id, targs) var baseTypes: List[TypeArg.Base] = List.empty targs.foreach({ case ValueType.Data(name, targs) => baseTypes :+= TypeArg.Base(name) From a4439d564db06b55ea25eb652ae6d361331faee5 Mon Sep 17 00:00:00 2001 From: Mattis Boeckle Date: Sat, 9 Aug 2025 13:32:34 +0200 Subject: [PATCH 21/29] wip fix interface implementations --- .../src/main/scala/effekt/core/Mono.scala | 42 +++++++++++-------- .../main/scala/effekt/core/Transformer.scala | 1 + 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 8aa535716..2185d141a 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -16,17 +16,17 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { case ModuleDecl(path, includes, declarations, externs, definitions, exports) => { // Find constraints in the definitions val monoFindContext = MonoFindContext() - val constraints = findConstraints(definitions)(using monoFindContext) - val declConstraints = declarations map (findConstraints(_)(using monoFindContext)) - // println("Constraints") - // constraints.foreach(c => println(c)) - // println() + var constraints = findConstraints(definitions)(using monoFindContext) + constraints = constraints ++ declarations.flatMap(findConstraints(_)(using monoFindContext)) + println("Constraints") + constraints.foreach(c => println(c)) + println() // Solve collected constraints val solution = solveConstraints(constraints) - // println("Solved") - // solution.foreach(println) - // println() + println("Solved") + solution.foreach(println) + println() // Monomorphize existing definitions var monoNames: MonoNames = Map.empty @@ -39,9 +39,9 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { var monoContext = MonoContext(solution, monoNames) val monoDecls = declarations flatMap (monomorphize(_)(using monoContext)) val monoDefs = monomorphize(definitions)(using monoContext) - // println(util.show(monoDecls)) - // println() - // println(util.show(monoDefs)) + monoDecls.foreach(decl => println(util.show(decl))) + println() + monoDefs.foreach(defn => println(util.show(defn))) val newModuleDecl = ModuleDecl(path, includes, monoDecls, externs, monoDefs, exports) return Some(CoreTransformed(source, tree, mod, newModuleDecl)) } @@ -60,7 +60,7 @@ type Solution = Map[FunctionId, Set[Vector[TypeArg.Base]]] type MonoNames = Map[(FunctionId, Vector[TypeArg.Base]), FunctionId] enum TypeArg { - case Base(val tpe: Id) + case Base(val tpe: Id, targs: List[TypeArg]) case Var(funId: FunctionId, pos: Int) } @@ -89,14 +89,19 @@ def findConstraints(definition: Toplevel)(using ctx: MonoFindContext): Constrain case Toplevel.Val(id, tpe, binding) => ??? def findConstraints(declaration: Declaration)(using ctx: MonoFindContext): Constraints = declaration match - case Data(id, List(), constructors) => List.empty + // Maybe[T] { Just[](x: T) } case Data(id, tparams, constructors) => tparams.zipWithIndex.foreach(ctx.extendTypingContext(_, _, id)) - constructors.map((constr => - Constraint(((constr.fields map (_.tpe)) map findId).toVector, constr.id))) + constructors.map{ constr => + val arity = tparams.size // + constr.tparams.size + val constructorArgs = (0 until arity).map(index => + TypeArg.Var(constr.id, index) // Just.0 + ).toVector // < Just.0 > + Constraint(constructorArgs, id) // < Just.0 > <: Maybe + } case Interface(id, List(), properties) => List.empty case Interface(id, tparams, properties) => - tparams.zipWithIndex.foreach(ctx.extendTypingContext(_, _, id)) + // tparams.zipWithIndex.foreach(ctx.extendTypingContext(_, _, id)) List.empty def findConstraints(block: Block)(using ctx: MonoFindContext): Constraints = block match @@ -147,12 +152,13 @@ def findConstraints(expr: Expr)(using ctx: MonoFindContext): Constraints = expr case PureApp(b, List(), vargs) => List.empty case ValueVar(id, annotatedType) => List.empty case Literal(value, annotatedType) => List.empty - case Make(data, tag, targs, vargs) => List(Constraint(data.targs.map(findId).toVector, data.name)) + case Make(data, tag, targs, vargs) => + List(Constraint(data.targs.map(findId).toVector, tag)) // <: Just case o => println(o); ??? def findId(vt: ValueType)(using ctx: MonoFindContext): TypeArg = vt match case ValueType.Boxed(tpe, capt) => ??? - case ValueType.Data(name, targs) => TypeArg.Base(name) + case ValueType.Data(name, targs) => TypeArg.Base(name, targs map findId) case ValueType.Var(name) => ctx.typingContext(name) def solveConstraints(constraints: Constraints): Solution = diff --git a/effekt/shared/src/main/scala/effekt/core/Transformer.scala b/effekt/shared/src/main/scala/effekt/core/Transformer.scala index 6a4d55c02..f116f32ba 100644 --- a/effekt/shared/src/main/scala/effekt/core/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/core/Transformer.scala @@ -143,6 +143,7 @@ object Transformer extends Phase[Typechecked, CoreTransformed] { } }.toList ++ exports.namespaces.values.flatMap(transform) + // Add tparams separately def transform(c: symbols.Constructor)(using Context): core.Constructor = core.Constructor(c, c.tparams, c.fields.map(f => core.Field(f, transform(f.returnType)))) From b46fd4ed3b58ea6d238d3112ae3c2b37431000f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Thu, 28 Aug 2025 09:08:33 +0200 Subject: [PATCH 22/29] Fix merge of Deadcode eliminination --- .../shared/src/main/scala/effekt/core/DeadCodeElimination.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/effekt/shared/src/main/scala/effekt/core/DeadCodeElimination.scala b/effekt/shared/src/main/scala/effekt/core/DeadCodeElimination.scala index 8ba335ff9..466e683c7 100644 --- a/effekt/shared/src/main/scala/effekt/core/DeadCodeElimination.scala +++ b/effekt/shared/src/main/scala/effekt/core/DeadCodeElimination.scala @@ -11,7 +11,7 @@ object DeadCodeElimination extends Phase[CoreTransformed, CoreTransformed] { def run(input: CoreTransformed)(using Context): Option[CoreTransformed] = input match { case CoreTransformed(source, tree, mod, core) => - val term = Context.checkMain(mod) + val term = Context.ensureMainExists(mod) val dce = Context.timed("deadcode-elimination", source.name) { Deadcode.remove(term, core) } From 1c1a0b5a49c459ed4c088eeb3109cae2f229a42e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Thu, 28 Aug 2025 09:46:54 +0200 Subject: [PATCH 23/29] Include tparams for Constructors --- .../src/main/scala/effekt/core/Mono.scala | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 2185d141a..b26b41222 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -125,8 +125,8 @@ def findConstraints(operation: Operation)(using ctx: MonoFindContext): Constrain findConstraints(body) def findConstraints(constructor: Constructor)(using ctx: MonoFindContext): Constraints = constructor match - case Constructor(id, List()) => List.empty - case Constructor(id, fields) => + case Constructor(id, tparams, List()) => List.empty + case Constructor(id, tparams, fields) => List(Constraint(((fields map (_.tpe)) map findId).toVector, id)) def findConstraints(stmt: Stmt)(using ctx: MonoFindContext): Constraints = stmt match @@ -183,7 +183,7 @@ def solveConstraints(constraints: Constraints): Solution = var l: List[List[TypeArg.Base]] = List(List.empty) def listFromIndex(ind: Int) = if (ind >= l.length) List.empty else l(ind) b.foreach({ - case TypeArg.Base(tpe) => l = productAppend(l, List(TypeArg.Base(tpe))) + case TypeArg.Base(tpe, targs) => l = productAppend(l, List(TypeArg.Base(tpe, targs))) case TypeArg.Var(funId, pos) => val funSolved = solved.getOrElse(funId, Set.empty) val posArgs = funSolved.map(v => v(pos)) @@ -224,8 +224,7 @@ def monomorphize(decl: Declaration)(using ctx: MonoContext): List[Declaration] = val replacementTparams = tparams.zip(baseTypes).toMap ctx.replacementTparams ++= replacementTparams val newConstructors = constructors map { - case Constructor(id, List()) => Constructor(id, List.empty) - case Constructor(id, fields) => Constructor(id, fields map monomorphize) + case Constructor(id, tparams, fields) => Constructor(id, tparams, fields map monomorphize) } Declaration.Data(ctx.names(id, baseTypes), List.empty, newConstructors) ) @@ -351,10 +350,11 @@ def replacementDataFromTargs(id: FunctionId, targs: List[ValueType])(using ctx: // it does show up while monomorphizing which caused an error // this seems to work for now if (id.name.name == "Resume") return ValueType.Data(id, targs) - var baseTypes: List[TypeArg.Base] = List.empty - targs.foreach({ - case ValueType.Data(name, targs) => baseTypes :+= TypeArg.Base(name) - case ValueType.Var(name) => baseTypes :+= ctx.replacementTparams(name) - case ValueType.Boxed(tpe, capt) => - }) + + def toTypeArg(vt: ValueType): TypeArg.Base = vt match + case ValueType.Data(name, targs) => TypeArg.Base(name, targs map toTypeArg) + case ValueType.Var(name) => ctx.replacementTparams(name) + case ValueType.Boxed(tpe, capt) => ??? + + val baseTypes: List[TypeArg.Base] = targs map toTypeArg ValueType.Data(ctx.names((id, baseTypes.toVector)), List.empty) From 5893519a8d0dd75228ce168e02f0745d55a01f07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Wed, 3 Sep 2025 17:35:08 +0200 Subject: [PATCH 24/29] Fix MonoTests --- effekt/jvm/src/test/scala/effekt/core/MonoTests.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/effekt/jvm/src/test/scala/effekt/core/MonoTests.scala b/effekt/jvm/src/test/scala/effekt/core/MonoTests.scala index 8302583a0..10383090a 100644 --- a/effekt/jvm/src/test/scala/effekt/core/MonoTests.scala +++ b/effekt/jvm/src/test/scala/effekt/core/MonoTests.scala @@ -5,7 +5,7 @@ package core abstract class AbstractMonoTests extends CorePhaseTests(Mono) { import TypeArg.* - implicit def stringBaseT(name: String): Base = Base(Id(name)) + implicit def stringBaseT(name: String): Base = Base(Id(name), List()) val BaseTInt: Base = "Int" val BaseTString: Base = "String" From 415c0d17f03cef701304aef20f2803319724fad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Wed, 3 Sep 2025 17:36:44 +0200 Subject: [PATCH 25/29] Implement many missing cases Tests go from 151 failed, 138 passed to 124 failed, 165 passed --- .../src/main/scala/effekt/core/Mono.scala | 60 +++++++++++++++---- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index b26b41222..33e65db74 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -18,15 +18,15 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { val monoFindContext = MonoFindContext() var constraints = findConstraints(definitions)(using monoFindContext) constraints = constraints ++ declarations.flatMap(findConstraints(_)(using monoFindContext)) - println("Constraints") - constraints.foreach(c => println(c)) - println() + // println("Constraints") + // constraints.foreach(c => println(c)) + // println() // Solve collected constraints val solution = solveConstraints(constraints) - println("Solved") - solution.foreach(println) - println() + // println("Solved") + // solution.foreach(println) + // println() // Monomorphize existing definitions var monoNames: MonoNames = Map.empty @@ -39,9 +39,9 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { var monoContext = MonoContext(solution, monoNames) val monoDecls = declarations flatMap (monomorphize(_)(using monoContext)) val monoDefs = monomorphize(definitions)(using monoContext) - monoDecls.foreach(decl => println(util.show(decl))) - println() - monoDefs.foreach(defn => println(util.show(defn))) + // monoDecls.foreach(decl => println(util.show(decl))) + // println() + // monoDefs.foreach(defn => println(util.show(defn))) val newModuleDecl = ModuleDecl(path, includes, monoDecls, externs, monoDefs, exports) return Some(CoreTransformed(source, tree, mod, newModuleDecl)) } @@ -133,7 +133,12 @@ def findConstraints(stmt: Stmt)(using ctx: MonoFindContext): Constraints = stmt case Let(id, annotatedTpe, binding, body) => findConstraints(binding) ++ findConstraints(body) case Return(expr) => findConstraints(expr) case Val(id, annotatedTpe, binding, body) => findConstraints(binding) ++ findConstraints(body) + case Var(ref, init, capture, body) => findConstraints(body) case App(callee: BlockVar, targs, vargs, bargs) => List(Constraint(targs.map(findId).toVector, callee.id)) + // TODO: Very specialized, but otherwise passing an id that matches in monomorphize is hard + // although I'm not certain any other case can even happen + // TODO: part 2, also update the implementation in monomorphize if changing this + case App(Unbox(ValueVar(id, annotatedType)), targs, vargs, bargs) => List(Constraint(targs.map(findId).toVector, id)) case Invoke(callee: BlockVar, method, methodTpe, targs, vargs, bargs) => List(Constraint(targs.map(findId).toVector, callee.id)) case Reset(body) => findConstraints(body) case If(cond, thn, els) => findConstraints(cond) ++ findConstraints(thn) ++ findConstraints(els) @@ -141,6 +146,11 @@ def findConstraints(stmt: Stmt)(using ctx: MonoFindContext): Constraints = stmt case Shift(prompt, body) => findConstraints(prompt) ++ findConstraints(body) case Match(scrutinee, clauses, default) => clauses.map(_._2).flatMap(findConstraints) ++ findConstraints(default) case Resume(k, body) => findConstraints(k) ++ findConstraints(body) + case Get(id, annotatedTpe, ref, annotatedCapt, body) => findConstraints(body) + case Put(ref, annotatedCapt, value, body) => findConstraints(value) ++ findConstraints(body) + case Alloc(id, init, region, body) => findConstraints(init) ++ findConstraints(body) + case Region(body) => findConstraints(body) + case Hole(span) => List.empty case o => println(o); ??? def findConstraints(opt: Option[Stmt])(using ctx: MonoFindContext): Constraints = opt match @@ -148,15 +158,20 @@ def findConstraints(opt: Option[Stmt])(using ctx: MonoFindContext): Constraints case Some(stmt) => findConstraints(stmt) def findConstraints(expr: Expr)(using ctx: MonoFindContext): Constraints = expr match - case DirectApp(b, List(), vargs, bargs) => List.empty + // TODO: + // Technically targs should still flow + // Just don't monomorphize + case DirectApp(b, targs, vargs, bargs) => List.empty case PureApp(b, List(), vargs) => List.empty case ValueVar(id, annotatedType) => List.empty case Literal(value, annotatedType) => List.empty case Make(data, tag, targs, vargs) => List(Constraint(data.targs.map(findId).toVector, tag)) // <: Just + case Box(b, annotatedCapture) => List.empty case o => println(o); ??? def findId(vt: ValueType)(using ctx: MonoFindContext): TypeArg = vt match + // TODO: What is the correct TypeArg for Boxed case ValueType.Boxed(tpe, capt) => ??? case ValueType.Data(name, targs) => TypeArg.Base(name, targs map findId) case ValueType.Var(name) => ctx.typingContext(name) @@ -277,9 +292,16 @@ def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match case Return(expr) => Return(monomorphize(expr)) case Val(id, annotatedTpe, binding, body) => Val(id, monomorphize(annotatedTpe), monomorphize(binding), monomorphize(body)) + case Var(ref, init, capture, body) => + Var(ref, monomorphize(init), capture, monomorphize(body)) case App(callee: BlockVar, targs, vargs, bargs) => val replacementData = replacementDataFromTargs(callee.id, targs) App(monomorphize(callee, replacementData.name), List.empty, vargs map monomorphize, bargs map monomorphize) + // TODO: Highly specialized, see todo in findConstraints for info + // change at the same time as findConstraints + case App(Unbox(ValueVar(id, annotatedTpe)), targs, vargs, bargs) => + val replacementData = replacementDataFromTargs(id, targs) + App(Unbox(ValueVar(id, monomorphize(annotatedTpe))), List.empty, vargs map monomorphize, bargs map monomorphize) case Let(id, annotatedTpe, binding, body) => Let(id, monomorphize(annotatedTpe), monomorphize(binding), monomorphize(body)) case If(cond, thn, els) => If(monomorphize(cond), monomorphize(thn), monomorphize(els)) case Invoke(BlockVar(id, annotatedTpe, annotatedCapt), method, methodTpe, targs, vargs, bargs) => @@ -292,6 +314,14 @@ def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match case Match(scrutinee, clauses, default) => val monoClauses = clauses.map((id, blockLit) => (id, monomorphize(blockLit))) Match(monomorphize(scrutinee), monoClauses, monomorphize(default)) + case Get(id, annotatedTpe, ref, annotatedCapt, body) => + Get(id, monomorphize(annotatedTpe), ref, annotatedCapt, monomorphize(body)) + case Put(ref, annotatedCapt, value, body) => + Put(ref, annotatedCapt, monomorphize(value), monomorphize(body)) + case Alloc(id, init, region, body) => + Alloc(id, monomorphize(init), region, monomorphize(body)) + case Region(body) => Region(monomorphize(body)) + case Hole(span) => Hole(span) case o => println(o); ??? def monomorphize(opt: Option[Stmt])(using ctx: MonoContext): Option[Stmt] = opt match @@ -302,6 +332,16 @@ def monomorphize(expr: Expr)(using ctx: MonoContext): Expr = expr match case DirectApp(b, targs, vargs, bargs) => val replacementData = replacementDataFromTargs(b.id, targs) DirectApp(monomorphize(b, replacementData.name), List.empty, vargs map monomorphize, bargs map monomorphize) + case Literal(value, annotatedType) => + Literal(value, monomorphize(annotatedType)) + case PureApp(b, targs, vargs) => + val replacementData = replacementDataFromTargs(b.id, targs) + PureApp(monomorphize(b, replacementData.name), List.empty, vargs map monomorphize) + case Make(data, tag, targs, vargs) => + Make(replacementDataFromTargs(data.name, data.targs), tag, List.empty, vargs map monomorphize) + case Box(b, annotatedCapture) => + // TODO: Does this need other handling? + Box(b, annotatedCapture) case o => println(o); ??? def monomorphize(pure: Pure)(using ctx: MonoContext): Pure = pure match From f37e62ffd1df54e53823d29fc99f7209997ed178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Thu, 4 Sep 2025 10:16:13 +0200 Subject: [PATCH 26/29] Remove unnecessary case --- effekt/shared/src/main/scala/effekt/core/Mono.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 33e65db74..2f5486862 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -99,7 +99,6 @@ def findConstraints(declaration: Declaration)(using ctx: MonoFindContext): Const ).toVector // < Just.0 > Constraint(constructorArgs, id) // < Just.0 > <: Maybe } - case Interface(id, List(), properties) => List.empty case Interface(id, tparams, properties) => // tparams.zipWithIndex.foreach(ctx.extendTypingContext(_, _, id)) List.empty From a369fa9d7fa27f3ef3dc8155c3cf0a88770e1021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Wed, 17 Sep 2025 11:48:29 +0200 Subject: [PATCH 27/29] Consider vargs and bargs --- effekt/shared/src/main/scala/effekt/core/Mono.scala | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 2f5486862..8b7757dc1 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -133,12 +133,15 @@ def findConstraints(stmt: Stmt)(using ctx: MonoFindContext): Constraints = stmt case Return(expr) => findConstraints(expr) case Val(id, annotatedTpe, binding, body) => findConstraints(binding) ++ findConstraints(body) case Var(ref, init, capture, body) => findConstraints(body) - case App(callee: BlockVar, targs, vargs, bargs) => List(Constraint(targs.map(findId).toVector, callee.id)) + case App(callee: BlockVar, targs, vargs, bargs) => + List(Constraint(targs.map(findId).toVector, callee.id)) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) // TODO: Very specialized, but otherwise passing an id that matches in monomorphize is hard // although I'm not certain any other case can even happen // TODO: part 2, also update the implementation in monomorphize if changing this - case App(Unbox(ValueVar(id, annotatedType)), targs, vargs, bargs) => List(Constraint(targs.map(findId).toVector, id)) - case Invoke(callee: BlockVar, method, methodTpe, targs, vargs, bargs) => List(Constraint(targs.map(findId).toVector, callee.id)) + case App(Unbox(ValueVar(id, annotatedType)), targs, vargs, bargs) => + List(Constraint(targs.map(findId).toVector, id)) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) + case Invoke(callee: BlockVar, method, methodTpe, targs, vargs, bargs) => + List(Constraint(targs.map(findId).toVector, callee.id)) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) case Reset(body) => findConstraints(body) case If(cond, thn, els) => findConstraints(cond) ++ findConstraints(thn) ++ findConstraints(els) case Def(id, block, body) => findConstraints(block) ++ findConstraints(body) @@ -161,13 +164,12 @@ def findConstraints(expr: Expr)(using ctx: MonoFindContext): Constraints = expr // Technically targs should still flow // Just don't monomorphize case DirectApp(b, targs, vargs, bargs) => List.empty - case PureApp(b, List(), vargs) => List.empty + case PureApp(b, targs, vargs) => List.empty case ValueVar(id, annotatedType) => List.empty case Literal(value, annotatedType) => List.empty case Make(data, tag, targs, vargs) => List(Constraint(data.targs.map(findId).toVector, tag)) // <: Just case Box(b, annotatedCapture) => List.empty - case o => println(o); ??? def findId(vt: ValueType)(using ctx: MonoFindContext): TypeArg = vt match // TODO: What is the correct TypeArg for Boxed From bdc6c277dca1c06c52e37474a24030de427672e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Wed, 17 Sep 2025 11:49:11 +0200 Subject: [PATCH 28/29] Handle Function in findConstraints --- .../shared/src/main/scala/effekt/core/Mono.scala | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 8b7757dc1..afcf2868c 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -100,18 +100,24 @@ def findConstraints(declaration: Declaration)(using ctx: MonoFindContext): Const Constraint(constructorArgs, id) // < Just.0 > <: Maybe } case Interface(id, tparams, properties) => - // tparams.zipWithIndex.foreach(ctx.extendTypingContext(_, _, id)) + tparams.zipWithIndex.foreach(ctx.extendTypingContext(_, _, id)) List.empty def findConstraints(block: Block)(using ctx: MonoFindContext): Constraints = block match - case BlockVar(id, annotatedTpe, annotatedCapt) => findConstraints(annotatedTpe) + case BlockVar(id, annotatedTpe: BlockType.Interface, annotatedCapt) => findConstraints(annotatedTpe) + case BlockVar(id, annotatedTpe: BlockType.Function, annotatedCapt) => findConstraints(annotatedTpe, id) case BlockLit(tparams, cparams, vparams, bparams, body) => findConstraints(body) case Unbox(pure) => ??? case New(impl) => findConstraints(impl) -def findConstraints(blockType: BlockType)(using ctx: MonoFindContext): Constraints = blockType match - case BlockType.Interface(name, targs) => List(Constraint(targs.map(findId).toVector, name)) - case o => println(o); ??? +def findConstraints(blockType: BlockType.Interface)(using ctx: MonoFindContext): Constraints = blockType match + case BlockType.Interface(name, targs) => + List(Constraint(targs.map(findId).toVector, name)) + +def findConstraints(blockType: BlockType.Function, fnId: Id)(using ctx: MonoFindContext): Constraints = blockType match + case BlockType.Function(tparams, cparams, vparams, bparams, result) => + tparams.zipWithIndex.foreach(ctx.extendTypingContext(_, _, fnId)) + List() def findConstraints(impl: Implementation)(using ctx: MonoFindContext): Constraints = impl match case Implementation(interface, operations) => From 15aacc43a1855cdba3feed17b99c15ff9bc1701e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Wed, 17 Sep 2025 12:07:19 +0200 Subject: [PATCH 29/29] Handle Unbox --- effekt/shared/src/main/scala/effekt/core/Mono.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index afcf2868c..e4684aefd 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -107,7 +107,7 @@ def findConstraints(block: Block)(using ctx: MonoFindContext): Constraints = blo case BlockVar(id, annotatedTpe: BlockType.Interface, annotatedCapt) => findConstraints(annotatedTpe) case BlockVar(id, annotatedTpe: BlockType.Function, annotatedCapt) => findConstraints(annotatedTpe, id) case BlockLit(tparams, cparams, vparams, bparams, body) => findConstraints(body) - case Unbox(pure) => ??? + case Unbox(pure) => findConstraints(pure) case New(impl) => findConstraints(impl) def findConstraints(blockType: BlockType.Interface)(using ctx: MonoFindContext): Constraints = blockType match @@ -263,7 +263,7 @@ def monomorphize(block: Block)(using ctx: MonoContext): Block = block match case b: BlockLit => monomorphize(b) case b: BlockVar => monomorphize(b) case New(impl) => New(monomorphize(impl)) - case o => println(o); ??? + case Unbox(pure) => Unbox(monomorphize(pure)) def monomorphize(impl: Implementation)(using ctx: MonoContext): Implementation = impl match case Implementation(interface, operations) => Implementation(monomorphize(interface), operations.map(monomorphize)) @@ -311,6 +311,8 @@ def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match App(Unbox(ValueVar(id, monomorphize(annotatedTpe))), List.empty, vargs map monomorphize, bargs map monomorphize) case Let(id, annotatedTpe, binding, body) => Let(id, monomorphize(annotatedTpe), monomorphize(binding), monomorphize(body)) case If(cond, thn, els) => If(monomorphize(cond), monomorphize(thn), monomorphize(els)) + case Invoke(Unbox(pure), method, methodTpe, targs, vargs, bargs) => + Invoke(Unbox(monomorphize(pure)), method, methodTpe, List.empty, vargs map monomorphize, bargs map monomorphize) case Invoke(BlockVar(id, annotatedTpe, annotatedCapt), method, methodTpe, targs, vargs, bargs) => Invoke(BlockVar(id, monomorphize(annotatedTpe), annotatedCapt), method, methodTpe, List.empty, vargs map monomorphize, bargs map monomorphize) // TODO: Monomorphizing here throws an error complaining about a missing implementation