From d973785113acc668a2dc9bf35aa8344767f307bf 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/57] 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 6a0ca976a9bdcaf7ac134da8ff9c9c93e4af2eb3 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/57] 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 611d4e68f0f7531f28470ce8bece67ed7d9aa842 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/57] 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 1842bad534bc810f9ea71249b7ea195194dba45d 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/57] 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 d38e8ebd19b5ee419cacefbf1f14519702265c0a 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/57] 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 823c10a8e0e1b223f4855cb18e53223867d64ac3 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/57] 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 d6f977d09e894df29de9d0431ab79d6176fc1d96 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/57] 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 c6e78d0b4f08a0c65c8a18b9b9695b1a23e50536 Mon Sep 17 00:00:00 2001 From: Mattis Boeckle Date: Mon, 26 May 2025 15:31:16 +0200 Subject: [PATCH 08/57] 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 1f4f5a9572d3262bc7edf3618dd4730b9da258ee 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/57] 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 02ab1916fd06ef6ab1128a236a657f96f432f8e5 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/57] 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 d602b1df7b8e2f3c77ac688bc9b858c82b96f902 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/57] 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 e1cd1bbef4eac12b942a1f9a2107270a51625fe2 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/57] 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 58c4655fdbe7e34fc7ce4320afa89f2bad4297ba 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/57] 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 6315ab448a20aea1c068ab179c4ca113c669c99c 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/57] 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 1a31de8e31ee1641ffda56aed4561bf22c2f160b 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/57] 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 0b3b638aefd8dc1c18f8b8cb5770570ceabb671b 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/57] 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 8bc3ba8b504c87c8522cb66a1ebdef9edf1f96c9 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/57] 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 c13d5fc7fab4e8171aacfc516152bee0986f4e33 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/57] 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 0a82d3c3fd5a7ef621eff683bccf5472fedda21f Mon Sep 17 00:00:00 2001 From: Mattis Boeckle Date: Fri, 25 Jul 2025 13:35:36 +0200 Subject: [PATCH 19/57] 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 5319d1a24e7ffc60b39f183a6bbd84f0103e99f8 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/57] 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 be8e8e01ecba126a5e3d339f94d6f6fde09faa12 Mon Sep 17 00:00:00 2001 From: Mattis Boeckle Date: Sat, 9 Aug 2025 13:32:34 +0200 Subject: [PATCH 21/57] 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 311685e26..3cd050a26 100644 --- a/effekt/shared/src/main/scala/effekt/core/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/core/Transformer.scala @@ -139,6 +139,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 6f31c5abed4265c13047388bcbdbbbfd03cf8b24 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/57] 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 406a80687cfdda548fb124ecdf61c5bb320df584 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/57] 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 fd91a1d1f5cf3fe437644b675e2beba910698f30 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/57] 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 9ead25f5ccd6d06fe6146cfdcb4007e117970024 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/57] 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 e9fdbc01fadfeac6a153214d4fd65b075ca54c08 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/57] 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 7ad654af66df37a0b434c5aceb1659e1f8f278fc 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/57] 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 a89e4936343f97aac3683093f4a0fd8c0ce6a5d9 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/57] 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 5260caecb07ff27a141e52c051348fb5d0efa00a 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/57] 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 From 4d26970e72cec383ae748b89d912585b2d8f6028 Mon Sep 17 00:00:00 2001 From: Mattis Boeckle Date: Wed, 24 Sep 2025 12:54:21 +0200 Subject: [PATCH 30/57] Handle Box and Boxed --- .../src/main/scala/effekt/core/Mono.scala | 58 +++++++++++++------ 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index e4684aefd..950a319a7 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -43,6 +43,9 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { // println() // monoDefs.foreach(defn => println(util.show(defn))) val newModuleDecl = ModuleDecl(path, includes, monoDecls, externs, monoDefs, exports) + + // println(util.show(newModuleDecl)) + return Some(CoreTransformed(source, tree, mod, newModuleDecl)) } } @@ -56,12 +59,14 @@ type FunctionId = Id 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] +type Ground = TypeArg.Base | TypeArg.Boxed +type Solution = Map[FunctionId, Set[Vector[Ground]]] +type MonoNames = Map[(FunctionId, Vector[Ground]), FunctionId] enum TypeArg { case Base(val tpe: Id, targs: List[TypeArg]) case Var(funId: FunctionId, pos: Int) + case Boxed(tpe: BlockType, capt: Captures) } // Type Id -> Var @@ -75,7 +80,7 @@ class MonoFindContext { } case class MonoContext(solution: Solution, names: MonoNames) { - var replacementTparams: Map[Id, TypeArg.Base] = Map.empty + var replacementTparams: Map[Id, Ground] = Map.empty } def findConstraints(definitions: List[Toplevel])(using MonoFindContext): Constraints = @@ -85,7 +90,7 @@ def findConstraints(definition: Toplevel)(using ctx: MonoFindContext): Constrain 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.Def(id, block) => println(definition); ??? case Toplevel.Val(id, tpe, binding) => ??? def findConstraints(declaration: Declaration)(using ctx: MonoFindContext): Constraints = declaration match @@ -178,8 +183,8 @@ def findConstraints(expr: Expr)(using ctx: MonoFindContext): Constraints = expr case Box(b, annotatedCapture) => List.empty def findId(vt: ValueType)(using ctx: MonoFindContext): TypeArg = vt match - // TODO: What is the correct TypeArg for Boxed - case ValueType.Boxed(tpe, capt) => ??? + // TODO: Perhaps recurse into tpe + case ValueType.Boxed(tpe, capt) => TypeArg.Boxed(tpe, capt) case ValueType.Data(name, targs) => TypeArg.Base(name, targs map findId) case ValueType.Var(name) => ctx.typingContext(name) @@ -198,11 +203,11 @@ def solveConstraints(constraints: Constraints): Solution = if (previousSolved == solved) return solved } - def solveConstraints(funId: FunctionId): Set[List[TypeArg.Base]] = + def solveConstraints(funId: FunctionId): Set[List[Ground]] = val filteredConstraints = vecConstraints(funId) - var nbs: Set[List[TypeArg.Base]] = Set.empty + var nbs: Set[List[Ground]] = Set.empty filteredConstraints.foreach(b => - var l: List[List[TypeArg.Base]] = List(List.empty) + var l: List[List[Ground]] = List(List.empty) def listFromIndex(ind: Int) = if (ind >= l.length) List.empty else l(ind) b.foreach({ case TypeArg.Base(tpe, targs) => l = productAppend(l, List(TypeArg.Base(tpe, targs))) @@ -210,6 +215,7 @@ def solveConstraints(constraints: Constraints): Solution = val funSolved = solved.getOrElse(funId, Set.empty) val posArgs = funSolved.map(v => v(pos)) l = posArgs.zipWithIndex.map((base, ind) => listFromIndex(ind) :+ base).toList + case TypeArg.Boxed(tpe, capt) => l = productAppend(l, List(TypeArg.Boxed(tpe, capt))) }) nbs ++= l ) @@ -279,6 +285,7 @@ def monomorphize(operation: Operation)(using ctx: MonoContext): Operation = oper def monomorphize(block: BlockLit)(using ctx: MonoContext): BlockLit = block match case BlockLit(tparams, cparams, vparams, bparams, body) => + // TODO: Add the tparams to context here BlockLit(List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(body)) def monomorphize(block: BlockVar)(using ctx: MonoContext): BlockVar = block match @@ -362,7 +369,7 @@ def monomorphize(pure: Pure)(using ctx: MonoContext): Pure = pure match 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); ??? + case Box(b, annotatedCapture) => Box(monomorphize(b), annotatedCapture) def monomorphize(valueParam: ValueParam)(using ctx: MonoContext): ValueParam = valueParam match case ValueParam(id, tpe) => ValueParam(id, monomorphize(tpe)) @@ -377,20 +384,33 @@ def monomorphize(blockType: BlockType)(using ctx: MonoContext): BlockType = bloc 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) + case ValueType.Var(name) => ctx.replacementTparams(name) match { + case TypeArg.Base(tpe, targs) => ValueType.Var(tpe) + case TypeArg.Boxed(tpe, capt) => ValueType.Boxed(tpe, capt) + } case ValueType.Data(name, targs) => replacementDataFromTargs(name, targs) - case o => println(o); ??? + case ValueType.Boxed(tpe, capt) => ValueType.Boxed(monomorphize(tpe), capt) var monoCounter = 0 -def freshMonoName(baseId: Id, tpe: TypeArg.Base): Id = +def freshMonoName(baseId: Id, tpe: Ground): Id = monoCounter += 1 - Id(baseId.name.name + tpe.tpe.name.name + monoCounter) + tpe match { + case TypeArg.Base(tpe, targs) => + Id(baseId.name.name + tpe.name.name + monoCounter) + case TypeArg.Boxed(tpe, capt) => + // TODO: Fix naming + Id(baseId.name.name + "BOXED" + monoCounter) + } -def freshMonoName(baseId: Id, tpes: Vector[TypeArg.Base]): Id = +def freshMonoName(baseId: Id, tpes: Vector[Ground]): Id = if (tpes.length == 0) return baseId monoCounter += 1 - val tpesString = tpes.map(tpe => tpe.tpe.name.name).mkString + val tpesString = tpes.map({ + case TypeArg.Base(tpe, targs) => tpe.name.name + // TODO: Fix naming + case TypeArg.Boxed(tpe, capt) => "BOXED" + }).mkString Id(baseId.name.name + tpesString + monoCounter) def replacementDataFromTargs(id: FunctionId, targs: List[ValueType])(using ctx: MonoContext): ValueType.Data = @@ -400,10 +420,10 @@ def replacementDataFromTargs(id: FunctionId, targs: List[ValueType])(using ctx: // this seems to work for now if (id.name.name == "Resume") return ValueType.Data(id, targs) - def toTypeArg(vt: ValueType): TypeArg.Base = vt match + def toTypeArg(vt: ValueType): Ground = 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) => ??? + case ValueType.Boxed(tpe, capt) => TypeArg.Boxed(tpe, capt) - val baseTypes: List[TypeArg.Base] = targs map toTypeArg + val baseTypes: List[Ground] = targs map toTypeArg ValueType.Data(ctx.names((id, baseTypes.toVector)), List.empty) From 3f99ffc252c30b7cb500707e276072a48aa9063f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Wed, 24 Sep 2025 15:17:41 +0200 Subject: [PATCH 31/57] Fix an issue with solving we weren't using productAppend in certain cases with vars which was causing problems --- .../src/test/scala/effekt/core/MonoTests.scala | 17 +++++++++++++++++ .../src/main/scala/effekt/core/Mono.scala | 14 ++++++++------ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/effekt/jvm/src/test/scala/effekt/core/MonoTests.scala b/effekt/jvm/src/test/scala/effekt/core/MonoTests.scala index 10383090a..a485f72c4 100644 --- a/effekt/jvm/src/test/scala/effekt/core/MonoTests.scala +++ b/effekt/jvm/src/test/scala/effekt/core/MonoTests.scala @@ -109,4 +109,21 @@ class MonoTests extends AbstractMonoTests { assertEquals(solveConstraints(constraints), expectedSolved) } + + test("correct product of vars") { + val constraints = List( + Constraint(Vector(BaseTInt), fnId("a")), + Constraint(Vector(BaseTString), fnId("a")), + Constraint(Vector(BaseTBool), fnId("b")), + Constraint(Vector(Var(fnId("a"), 0), Var(fnId("b"), 0)), fnId("c")), + ) + + val expectedSolved: Solution = Map( + fnId("a") -> Set(Vector(BaseTInt), Vector(BaseTString)), + fnId("b") -> Set(Vector(BaseTBool)), + fnId("c") -> Set(Vector(BaseTInt, BaseTBool), Vector(BaseTString, BaseTBool)), + ) + + assertEquals(solveConstraints(constraints), expectedSolved) + } } diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 950a319a7..90c03ab72 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -210,12 +210,14 @@ def solveConstraints(constraints: Constraints): Solution = var l: List[List[Ground]] = List(List.empty) def listFromIndex(ind: Int) = if (ind >= l.length) List.empty else l(ind) b.foreach({ - 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)) - l = posArgs.zipWithIndex.map((base, ind) => listFromIndex(ind) :+ base).toList - case TypeArg.Boxed(tpe, capt) => l = productAppend(l, List(TypeArg.Boxed(tpe, capt))) + case TypeArg.Base(tpe, targs) => + l = productAppend(l, List(TypeArg.Base(tpe, targs))) + case TypeArg.Var(fnId, pos) => + val funSolved = solved.getOrElse(fnId, Set.empty) + val posArgs = funSolved.map(v => v(pos)).toList + l = productAppend(l, posArgs) + case TypeArg.Boxed(tpe, capt) => + l = productAppend(l, List(TypeArg.Boxed(tpe, capt))) }) nbs ++= l ) From 1865c9921de946a560baa3c6cf63d3d8f45f585d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Wed, 24 Sep 2025 15:18:09 +0200 Subject: [PATCH 32/57] Monomorphize nested definitions --- .../src/main/scala/effekt/core/Mono.scala | 18 +++++++++++++++++- 1 file changed, 17 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 90c03ab72..1bf385419 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -155,7 +155,11 @@ def findConstraints(stmt: Stmt)(using ctx: MonoFindContext): Constraints = stmt 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) + case Def(id, BlockLit(tparams, cparams, vparams, bparams, bbody), body) => + tparams.zipWithIndex.foreach(ctx.extendTypingContext(_, _, id)) + findConstraints(bbody) ++ findConstraints(body) + 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) @@ -327,6 +331,18 @@ def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match // 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, BlockLit(tparams, cparams, vparams, bparams, bbody), body) => + val monoTypes = ctx.solution(id).toList + // Monomorphizing inner functions may yield multiple definitions + // which then need to be nested + def nestDefs(defnTypes: List[Vector[Ground]]): Stmt = defnTypes match { + case head :: next => + val replacementTparams = tparams.zip(head).toMap + ctx.replacementTparams ++= replacementTparams + Stmt.Def(ctx.names(id, head), BlockLit(List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(bbody)), nestDefs(next)) + case Nil => monomorphize(body) + } + nestDefs(monoTypes) 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) => From b041e5640d3df81f419818d25bdacb49222b2d38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Wed, 24 Sep 2025 15:19:44 +0200 Subject: [PATCH 33/57] Fill Toplevel holes --- effekt/shared/src/main/scala/effekt/core/Mono.scala | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 1bf385419..8806e0504 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -90,8 +90,10 @@ def findConstraints(definition: Toplevel)(using ctx: MonoFindContext): Constrain case Toplevel.Def(id, BlockLit(tparams, cparams, vparams, bparams, body)) => tparams.zipWithIndex.foreach(ctx.extendTypingContext(_, _, id)) findConstraints(body) - case Toplevel.Def(id, block) => println(definition); ??? - case Toplevel.Val(id, tpe, binding) => ??? + case Toplevel.Def(id, block) => + findConstraints(block) + case Toplevel.Val(id, tpe, binding) => + findConstraints(binding) def findConstraints(declaration: Declaration)(using ctx: MonoFindContext): Constraints = declaration match // Maybe[T] { Just[](x: T) } @@ -247,8 +249,10 @@ def monomorphize(toplevel: Toplevel)(using ctx: MonoContext): List[Toplevel] = t 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) => ??? + case Toplevel.Def(id, block) => + List(Toplevel.Def(id, monomorphize(block))) + case Toplevel.Val(id, tpe, binding) => + List(Toplevel.Val(id, monomorphize(tpe), monomorphize(binding))) def monomorphize(decl: Declaration)(using ctx: MonoContext): List[Declaration] = decl match case Data(id, List(), constructors) => List(decl) From c188e8044e9765a390211a70b36935c97a255f49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Wed, 24 Sep 2025 15:32:39 +0200 Subject: [PATCH 34/57] Fill missing holes --- .../src/main/scala/effekt/core/Mono.scala | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 8806e0504..961d58d1e 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -153,8 +153,15 @@ def findConstraints(stmt: Stmt)(using ctx: MonoFindContext): Constraints = stmt // 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)) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) + case App(callee, targs, vargs, bargs) => + findConstraints(callee) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) + // TODO: Maybe need to do something with methodTpe case Invoke(callee: BlockVar, method, methodTpe, targs, vargs, bargs) => List(Constraint(targs.map(findId).toVector, callee.id)) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) + case Invoke(Unbox(ValueVar(id, annotatedType)), method, methodTpe, targs, vargs, bargs) => + List(Constraint(targs.map(findId).toVector, id)) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) + case Invoke(callee, method, methodTpe, targs, vargs, bargs) => + findConstraints(callee) ++ 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, BlockLit(tparams, cparams, vparams, bparams, bbody), body) => @@ -170,7 +177,6 @@ def findConstraints(stmt: Stmt)(using ctx: MonoFindContext): Constraints = stmt 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 case None => List.empty @@ -310,7 +316,8 @@ 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 => println(o); ??? + case BlockVar(id, annotatedTpe: BlockType.Interface, annotatedCapt) => + BlockVar(id, monomorphize(annotatedTpe), annotatedCapt) def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match case Return(expr) => Return(monomorphize(expr)) @@ -326,12 +333,18 @@ def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match 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 App(callee, targs, vargs, bargs) => + App(monomorphize(callee), 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) + case Invoke(callee, method, methodTpe, targs, vargs, bargs) => + Invoke(monomorphize(callee), method, methodTpe, List.empty, vargs map monomorphize, bargs map monomorphize) + case Resume(k, body) => + Resume(monomorphize(k), monomorphize(body)) // 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) @@ -360,7 +373,6 @@ def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match 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 case None => None @@ -380,7 +392,8 @@ def monomorphize(expr: Expr)(using ctx: MonoContext): Expr = expr match case Box(b, annotatedCapture) => // TODO: Does this need other handling? Box(b, annotatedCapture) - case o => println(o); ??? + case ValueVar(id, annotatedType) => + ValueVar(id, monomorphize(annotatedType)) def monomorphize(pure: Pure)(using ctx: MonoContext): Pure = pure match case ValueVar(id, annotatedType) => ValueVar(id, monomorphize(annotatedType)) From c9a53b97d400fc1b6c9cadc52b3fe0deeef123e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Tue, 30 Sep 2025 15:34:10 +0200 Subject: [PATCH 35/57] Make method invocations flow into the method not the type --- effekt/shared/src/main/scala/effekt/core/Mono.scala | 4 ++-- 1 file changed, 2 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 961d58d1e..f65b2a180 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -157,9 +157,9 @@ def findConstraints(stmt: Stmt)(using ctx: MonoFindContext): Constraints = stmt findConstraints(callee) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) // TODO: Maybe need to do something with methodTpe case Invoke(callee: BlockVar, method, methodTpe, targs, vargs, bargs) => - List(Constraint(targs.map(findId).toVector, callee.id)) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) + List(Constraint(targs.map(findId).toVector, method)) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) case Invoke(Unbox(ValueVar(id, annotatedType)), method, methodTpe, targs, vargs, bargs) => - List(Constraint(targs.map(findId).toVector, id)) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) + List(Constraint(targs.map(findId).toVector, method)) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) case Invoke(callee, method, methodTpe, targs, vargs, bargs) => findConstraints(callee) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) case Reset(body) => findConstraints(body) From a74d368d46ff2ab1727961410bcf7939bca033cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Tue, 30 Sep 2025 15:35:33 +0200 Subject: [PATCH 36/57] Fix match clauses & method invocation --- .../src/main/scala/effekt/core/Mono.scala | 66 +++++++++++++------ 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index f65b2a180..d2f4b1d79 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -183,16 +183,17 @@ def findConstraints(opt: Option[Stmt])(using ctx: MonoFindContext): Constraints case Some(stmt) => findConstraints(stmt) def findConstraints(expr: Expr)(using ctx: MonoFindContext): Constraints = expr match - // TODO: - // Technically targs should still flow - // Just don't monomorphize - case DirectApp(b, targs, vargs, bargs) => List.empty - case PureApp(b, targs, vargs) => List.empty + case DirectApp(b, targs, vargs, bargs) => + List(Constraint(targs.map(findId).toVector, b.id)) + case PureApp(b, targs, vargs) => + List(Constraint(targs.map(findId).toVector, b.id)) case ValueVar(id, annotatedType) => List.empty case Literal(value, annotatedType) => List.empty case Make(data, tag, targs, vargs) => + List(Constraint(targs.map(findId).toVector, tag)) ++ List(Constraint(data.targs.map(findId).toVector, tag)) // <: Just - case Box(b, annotatedCapture) => List.empty + case Box(b, annotatedCapture) => + findConstraints(b) def findId(vt: ValueType)(using ctx: MonoFindContext): TypeArg = vt match // TODO: Perhaps recurse into tpe @@ -203,8 +204,9 @@ def findId(vt: ValueType)(using ctx: MonoFindContext): TypeArg = vt match 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))) + val filteredConstraints = constraints.filterNot(c => c.lower.isEmpty) + val groupedConstraints = filteredConstraints.groupBy(c => c.upper) + val vecConstraints = groupedConstraints.map((sym, constraints) => (sym -> filteredConstraints.map(c => c.lower))) while (true) { val previousSolved = solved @@ -288,20 +290,25 @@ def monomorphize(block: Block)(using ctx: MonoContext): Block = block match 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)) + case Implementation(interface, operations) => Implementation(monomorphize(interface), operations.flatMap(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 +def monomorphize(operation: Operation)(using ctx: MonoContext): List[Operation] = operation match case Operation(name, tparams, cparams, vparams, bparams, body) => - Operation(name, List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(body)) + val monoTypes = ctx.solution.getOrElse(name, Set.empty).toList + monoTypes.map(baseTypes => + val replacementTparams = tparams.zip(baseTypes).toMap + ctx.replacementTparams ++= replacementTparams + Operation(ctx.names(name, baseTypes), 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) => - // TODO: Add the tparams to context here BlockLit(List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(body)) def monomorphize(block: BlockVar)(using ctx: MonoContext): BlockVar = block match @@ -339,8 +346,13 @@ def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match 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) + case Invoke(BlockVar(id, annotatedTpe, annotatedCapt), method, BlockType.Function(tparams, cparams, vparams, bparams, result), targs, vargs, bargs) => + val replacementData = replacementDataFromTargs(method, targs) + val replacementTparams = tparams.zip(targs map toTypeArg).toMap + ctx.replacementTparams ++= replacementTparams + + val monoAnnotatedTpe = BlockType.Function(List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(result)) + Invoke(BlockVar(id, monomorphize(annotatedTpe), annotatedCapt), replacementData.name, monoAnnotatedTpe, List.empty, vargs map monomorphize, bargs map monomorphize) case Invoke(callee, method, methodTpe, targs, vargs, bargs) => Invoke(monomorphize(callee), method, methodTpe, List.empty, vargs map monomorphize, bargs map monomorphize) case Resume(k, body) => @@ -348,6 +360,8 @@ def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match // 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, BlockLit(List(), cparams, vparams, bparams, bbody), body) => + Stmt.Def(id, BlockLit(List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(bbody)), monomorphize(body)) case Def(id, BlockLit(tparams, cparams, vparams, bparams, bbody), body) => val monoTypes = ctx.solution(id).toList // Monomorphizing inner functions may yield multiple definitions @@ -363,7 +377,7 @@ def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match 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))) + val monoClauses = clauses flatMap monomorphize Match(monomorphize(scrutinee), monoClauses, monomorphize(default)) case Get(id, annotatedTpe, ref, annotatedCapt, body) => Get(id, monomorphize(annotatedTpe), ref, annotatedCapt, monomorphize(body)) @@ -374,6 +388,18 @@ def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match case Region(body) => Region(monomorphize(body)) case Hole(span) => Hole(span) +def monomorphize(clause: (Id, BlockLit))(using ctx: MonoContext): List[(Id, BlockLit)] = clause match + case (id, BlockLit(List(), cparams, vparams, bparams, body)) => + List((id, BlockLit(List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(body)))) + case (id, BlockLit(tparams, cparams, vparams, bparams, body)) => + val newClauseNameMap = ctx.names.view.filterKeys((tid, groundTypes) => tid == id) + newClauseNameMap.map((clauseKey, monoId) => + val replacementTparams = tparams.zip(clauseKey._2).toMap + ctx.replacementTparams ++= replacementTparams + val monoBlockLit: Block.BlockLit = BlockLit(List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(body)) + (monoId, monoBlockLit) + ).toList + def monomorphize(opt: Option[Stmt])(using ctx: MonoContext): Option[Stmt] = opt match case None => None case Some(stmt) => Some(monomorphize(stmt)) @@ -455,10 +481,10 @@ def replacementDataFromTargs(id: FunctionId, targs: List[ValueType])(using ctx: // this seems to work for now if (id.name.name == "Resume") return ValueType.Data(id, targs) - def toTypeArg(vt: ValueType): Ground = 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) => TypeArg.Boxed(tpe, capt) - val baseTypes: List[Ground] = targs map toTypeArg ValueType.Data(ctx.names((id, baseTypes.toVector)), List.empty) + +def toTypeArg(vt: ValueType)(using ctx: MonoContext): Ground = 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) => TypeArg.Boxed(tpe, capt) From 1339e67a702305044ce161960041978409c4dbec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Tue, 30 Sep 2025 16:18:49 +0200 Subject: [PATCH 37/57] Rename DirectApp to ImpureApp --- .../src/main/scala/effekt/core/Mono.scala | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index d2f4b1d79..66d61656e 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -146,6 +146,8 @@ 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 ImpureApp(id, callee, targs, vargs, bargs, body) => + List(Constraint(targs.map(findId).toVector, callee.id)) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) ++ findConstraints(body) 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 @@ -183,8 +185,6 @@ 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, targs, vargs, bargs) => - List(Constraint(targs.map(findId).toVector, b.id)) case PureApp(b, targs, vargs) => List(Constraint(targs.map(findId).toVector, b.id)) case ValueVar(id, annotatedType) => List.empty @@ -332,6 +332,9 @@ def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match Val(id, monomorphize(annotatedTpe), monomorphize(binding), monomorphize(body)) case Var(ref, init, capture, body) => Var(ref, monomorphize(init), capture, monomorphize(body)) + case ImpureApp(id, callee, targs, vargs, bargs, body) => + val replacementData = replacementDataFromTargs(callee.id, targs) + ImpureApp(id, monomorphize(callee, replacementData.name), List.empty, vargs map monomorphize, bargs map monomorphize, 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) @@ -421,16 +424,6 @@ def monomorphize(expr: Expr)(using ctx: MonoContext): Expr = expr match case ValueVar(id, annotatedType) => ValueVar(id, monomorphize(annotatedType)) -def monomorphize(pure: Pure)(using ctx: MonoContext): Pure = pure match - case ValueVar(id, annotatedType) => ValueVar(id, monomorphize(annotatedType)) - case PureApp(b, targs, vargs) => - 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 Box(b, annotatedCapture) => Box(monomorphize(b), annotatedCapture) def monomorphize(valueParam: ValueParam)(using ctx: MonoContext): ValueParam = valueParam match case ValueParam(id, tpe) => ValueParam(id, monomorphize(tpe)) From d59779277650745e5015ef69329f6cb3775dac59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Tue, 30 Sep 2025 16:19:32 +0200 Subject: [PATCH 38/57] Fix constructor mono --- .../src/main/scala/effekt/core/Mono.scala | 26 +++++++++++-------- 1 file changed, 15 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 66d61656e..a6fc7fee9 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -263,16 +263,14 @@ def monomorphize(toplevel: Toplevel)(using ctx: MonoContext): List[Toplevel] = t List(Toplevel.Val(id, monomorphize(tpe), monomorphize(binding))) def monomorphize(decl: Declaration)(using ctx: MonoContext): List[Declaration] = decl match - case Data(id, List(), constructors) => List(decl) + case Data(id, List(), constructors) => + List(Declaration.Data(id, List.empty, constructors.flatMap(monomorphize(_, 0)))) 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, tparams, fields) => Constructor(id, tparams, fields map monomorphize) - } - Declaration.Data(ctx.names(id, baseTypes), List.empty, newConstructors) + Declaration.Data(ctx.names(id, baseTypes), List.empty, constructors.flatMap(monomorphize(_, tparams.size))) ) case Interface(id, List(), properties) => List(decl) case Interface(id, tparams, properties) => @@ -283,6 +281,15 @@ def monomorphize(decl: Declaration)(using ctx: MonoContext): List[Declaration] = Declaration.Interface(ctx.names(id, baseTypes), List.empty, properties) ) +def monomorphize(constructor: Constructor, tparamCount: Int)(using ctx: MonoContext): List[Constructor] = constructor match + case Constructor(id, tparams, fields) => + val monoTypes = ctx.solution.getOrElse(id, Set.empty).toList + monoTypes.map(baseTypes => + val replacementTparams = tparams.drop(tparamCount).zip(baseTypes).toMap + ctx.replacementTparams ++= replacementTparams + Constructor(ctx.names(id, baseTypes), List.empty, fields map monomorphize) + ) + def monomorphize(block: Block)(using ctx: MonoContext): Block = block match case b: BlockLit => monomorphize(b) case b: BlockVar => monomorphize(b) @@ -408,19 +415,16 @@ def monomorphize(opt: Option[Stmt])(using ctx: MonoContext): Option[Stmt] = opt 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) - 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) + val replacementTag = ctx.names.getOrElse((tag, (targs map toTypeArg).toVector), tag) + Make(replacementDataFromTargs(data.name, data.targs), replacementTag, List.empty, vargs map monomorphize) case Box(b, annotatedCapture) => - // TODO: Does this need other handling? - Box(b, annotatedCapture) + Box(monomorphize(b), annotatedCapture) case ValueVar(id, annotatedType) => ValueVar(id, monomorphize(annotatedType)) From 4a250ef9a3941f406e34d363f2d6b3f192ccd01e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Tue, 30 Sep 2025 23:35:24 +0200 Subject: [PATCH 39/57] Remove unnecessary hack --- effekt/shared/src/main/scala/effekt/core/Mono.scala | 4 ---- 1 file changed, 4 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index a6fc7fee9..48cf1b9cf 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -473,10 +473,6 @@ def freshMonoName(baseId: Id, tpes: Vector[Ground]): 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) val baseTypes: List[Ground] = targs map toTypeArg ValueType.Data(ctx.names((id, baseTypes.toVector)), List.empty) From 5da0369c4b0771464ff26649eb67a87e4556c501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Tue, 30 Sep 2025 23:35:47 +0200 Subject: [PATCH 40/57] Fix typo in solving --- effekt/shared/src/main/scala/effekt/core/Mono.scala | 2 +- 1 file changed, 1 insertion(+), 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 48cf1b9cf..c570cb28c 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -206,7 +206,7 @@ def solveConstraints(constraints: Constraints): Solution = val filteredConstraints = constraints.filterNot(c => c.lower.isEmpty) val groupedConstraints = filteredConstraints.groupBy(c => c.upper) - val vecConstraints = groupedConstraints.map((sym, constraints) => (sym -> filteredConstraints.map(c => c.lower))) + val vecConstraints = groupedConstraints.map((sym, constraints) => (sym -> constraints.map(c => c.lower))) while (true) { val previousSolved = solved From 197c5f65c01653ee5be50928caee26cb39d83f45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Tue, 30 Sep 2025 23:36:44 +0200 Subject: [PATCH 41/57] Monomorphize existentials in special circumstances --- .../src/main/scala/effekt/core/Mono.scala | 18 +++++++++++++++++- 1 file changed, 17 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 c570cb28c..ceedd2ef5 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -172,7 +172,23 @@ def findConstraints(stmt: Stmt)(using ctx: MonoFindContext): Constraints = stmt 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 Match(scrutinee, clauses, default) => + // TODO: This is probably not technically correct, but allows for fun stuff like + // type Foo[A] { + // Bar[B](x: B) + // } + // to be monomorphized (in special cases (?)) + var additionalConstraint: Constraints = List() + clauses.foreach((id, bl) => { bl match { + case BlockLit(tparams, cparams, vparams, bparams, App(callee: BlockVar, targs, vargs, bargs)) => + if (targs.size == tparams.size) { + additionalConstraint +:= Constraint(tparams.zipWithIndex.map((_, paramIndex) => TypeArg.Var(id, paramIndex)).toVector, callee.id) + } + case _ => () + } + }) + + additionalConstraint ++ 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) From dd9a832c0240bc0116190968300bd6687d7263ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Tue, 30 Sep 2025 23:37:22 +0200 Subject: [PATCH 42/57] Add a temporary folder for mono examples --- mono/mono-test.sh | 15 +++++++++++++++ mono/mono1.effekt | 9 +++++++++ mono/mono10.effekt | 14 ++++++++++++++ mono/mono11.effekt | 30 ++++++++++++++++++++++++++++++ mono/mono12.effekt | 31 +++++++++++++++++++++++++++++++ mono/mono13.effekt | 36 ++++++++++++++++++++++++++++++++++++ mono/mono2.effekt | 6 ++++++ mono/mono3.effekt | 7 +++++++ mono/mono4.effekt | 13 +++++++++++++ mono/mono5.effekt | 20 ++++++++++++++++++++ mono/mono6.effekt | 10 ++++++++++ mono/mono7.effekt | 6 ++++++ mono/mono8.effekt | 16 ++++++++++++++++ mono/mono9.effekt | 11 +++++++++++ 14 files changed, 224 insertions(+) create mode 100755 mono/mono-test.sh create mode 100644 mono/mono1.effekt create mode 100644 mono/mono10.effekt create mode 100644 mono/mono11.effekt create mode 100644 mono/mono12.effekt create mode 100644 mono/mono13.effekt create mode 100644 mono/mono2.effekt create mode 100644 mono/mono3.effekt create mode 100644 mono/mono4.effekt create mode 100644 mono/mono5.effekt create mode 100644 mono/mono6.effekt create mode 100644 mono/mono7.effekt create mode 100644 mono/mono8.effekt create mode 100644 mono/mono9.effekt diff --git a/mono/mono-test.sh b/mono/mono-test.sh new file mode 100755 index 000000000..8987f2fe4 --- /dev/null +++ b/mono/mono-test.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env zsh +sbt "project effektJVM" \ + "run mono/mono1.effekt" \ + "run mono/mono2.effekt" \ + "run mono/mono3.effekt" \ + "run mono/mono4.effekt" \ + "run mono/mono5.effekt" \ + "run mono/mono6.effekt" \ + "run mono/mono7.effekt" \ + "run mono/mono8.effekt" \ + "run mono/mono9.effekt" \ + "run mono/mono10.effekt" \ + "run mono/mono11.effekt" \ + "run mono/mono12.effekt" \ + "run mono/mono13.effekt" \ \ No newline at end of file diff --git a/mono/mono1.effekt b/mono/mono1.effekt new file mode 100644 index 000000000..4fc3f17d5 --- /dev/null +++ b/mono/mono1.effekt @@ -0,0 +1,9 @@ +def a[A](in: A) = { in } +def b[B](in: B): B = { a(in) } +def c[C](in: C): C = { b(in) } +def d[D](in: D): D = { c(in) } + +def main() = { + println(a(1)) + println(d("32")) +} \ No newline at end of file diff --git a/mono/mono10.effekt b/mono/mono10.effekt new file mode 100644 index 000000000..a96094609 --- /dev/null +++ b/mono/mono10.effekt @@ -0,0 +1,14 @@ +def f[A](x: A) = { + def g[B](y: B): (A,B) = + (x,y) + def h[B](y: B): (A,B) = + (x,y) + g("String") + h(123) +} + +def main(): Unit = { + println(f[String]("abc").first) + println(f(false).second) + () +} diff --git a/mono/mono11.effekt b/mono/mono11.effekt new file mode 100644 index 000000000..00f35e9bc --- /dev/null +++ b/mono/mono11.effekt @@ -0,0 +1,30 @@ +interface Foo { + def bar[A](x: A): A +} + +def main(): Unit = { + def foo = new Foo { + // Have to write [B], can annotate x + def bar[B](x) = x + } + + println(foo.bar(123)) + println(foo.bar("abc")) + () +} + +interface FooMono { + def barInt(x: Int): Int + def barString(x: String): String +} + +def mainMono(): Unit = { + def foo = new FooMono { + def barInt(x) = x + def barString(x) = x + } + + println(foo.barInt(123)) + println(foo.barString("abc")) + () +} diff --git a/mono/mono12.effekt b/mono/mono12.effekt new file mode 100644 index 000000000..d81b4a855 --- /dev/null +++ b/mono/mono12.effekt @@ -0,0 +1,31 @@ +type Foo { + Bar[A](x: A) +} + +def main(): Unit = { + val foo1 = Bar(123) + val foo2 = Bar("abc") + + foo1 match { + // Must write [B] + case Bar(x) => println("true") + } + () +} + +type FooMono { + BarInt(x: Int) + BarString(x: String) +} + +def mainMono() = { + val foo1 = BarInt(123) + val foo2 = BarString("abc") + + foo1 match { + case BarInt(x) => println("true") + case BarString(x) => println("true") + } +} + + diff --git a/mono/mono13.effekt b/mono/mono13.effekt new file mode 100644 index 000000000..d7446185f --- /dev/null +++ b/mono/mono13.effekt @@ -0,0 +1,36 @@ +type Foo[A] { + Bar[B](x: B) +} + +def main(): Unit = { + val foo1 = Bar[Int, String]("abc") + val foo2 = Bar[String,Int](123) + + foo1 match { + // Must write [B] + case Bar(x) => println("true") + } + + foo2 match { + case Bar(x) => println("other") + } + + () +} + +type FooMono { + BarInt(x: Int) + BarString(x: String) +} + +def mainMono(): Unit = { + val foo1 = BarInt(123) + val foo2 = BarString("abc") + + foo1 match { + case BarInt(x) => println("true") + case BarString(x) => println("true") + } +} + + diff --git a/mono/mono2.effekt b/mono/mono2.effekt new file mode 100644 index 000000000..1c2352f14 --- /dev/null +++ b/mono/mono2.effekt @@ -0,0 +1,6 @@ +def f[C, D](in: C, in2: D) = in +def g[A, B](in: A, in2: B) = { f[B, A](in2, in) } +def main() = { + println(g(3, "3")) + println(g('c', false)) +} \ No newline at end of file diff --git a/mono/mono3.effekt b/mono/mono3.effekt new file mode 100644 index 000000000..d7170b2ab --- /dev/null +++ b/mono/mono3.effekt @@ -0,0 +1,7 @@ +def a[A](in: A) = { in } +def b[B](in: B) = { a(in) } + +def main() = { + println(b(1)) + println(b("1")) +} \ No newline at end of file diff --git a/mono/mono4.effekt b/mono/mono4.effekt new file mode 100644 index 000000000..990baa740 --- /dev/null +++ b/mono/mono4.effekt @@ -0,0 +1,13 @@ +type Maybe[A] { + Nothing() + Just[B](x: A) +} + +def f[T](a: T): Maybe[T] = Just[T, Bool](a) + +def main() = { + val a = Just[Int, Char](5) + val b = Nothing[Char]() + f(5) + () +} \ No newline at end of file diff --git a/mono/mono5.effekt b/mono/mono5.effekt new file mode 100644 index 000000000..72c665340 --- /dev/null +++ b/mono/mono5.effekt @@ -0,0 +1,20 @@ +effect yield[A](x: A): Unit + +def f() = { + do yield[Int](123) + do yield[String]("test") +} + +def main() = { + try { + f() + } with yield[Int] { + x => println(x) + resume(()) + resume(()) + } with yield[String] { + x => println(x) + resume(()) + } + () +} \ No newline at end of file diff --git a/mono/mono6.effekt b/mono/mono6.effekt new file mode 100644 index 000000000..82d9f913e --- /dev/null +++ b/mono/mono6.effekt @@ -0,0 +1,10 @@ +type Maybe { + Nothing() + Just[A](x: A) +} + +def main() = { + val a = Just[Int](5) + val b = Nothing() + () +} \ No newline at end of file diff --git a/mono/mono7.effekt b/mono/mono7.effekt new file mode 100644 index 000000000..1cf4497ea --- /dev/null +++ b/mono/mono7.effekt @@ -0,0 +1,6 @@ +def foo[E](on: on[E]): Unit = println("hello") + +def main(): Unit = { + foo[Int](on[Int]) + () +} \ No newline at end of file diff --git a/mono/mono8.effekt b/mono/mono8.effekt new file mode 100644 index 000000000..aaca3ddf3 --- /dev/null +++ b/mono/mono8.effekt @@ -0,0 +1,16 @@ +def foo[E](x: E): E = x + +def main(): Unit = { + foo[() => Int at {io}](box { () => println("asd"); 123 } ) + foo[Int](42) + () +} + +def fooInt(x: Int): Int = x +def fooBoxEmptyIntatIo(x: () => Int at {io}): () => Int at {io} = x + +def monoMain(): Unit = { + fooBoxEmptyIntatIo(box { () => println("asd"); 123 }) + fooInt(42) + () +} \ No newline at end of file diff --git a/mono/mono9.effekt b/mono/mono9.effekt new file mode 100644 index 000000000..d543fcbd6 --- /dev/null +++ b/mono/mono9.effekt @@ -0,0 +1,11 @@ +def f[A](x: A) = { + def g[B](y: B): (A,B) = + (x,y) + g(123) +} + +def main(): Unit = { + println(f[String]("abc").first) + println(f(false).second) + () +} \ No newline at end of file From 441225e878b13ca4694c4159102e8164ed2746b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Sun, 19 Oct 2025 09:13:03 +0200 Subject: [PATCH 43/57] Various fixes Mostly WIP for Match, polymorphic externs, ... --- .../src/main/scala/effekt/core/Mono.scala | 109 +++++++++++++++--- 1 file changed, 93 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 ceedd2ef5..95180c47e 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -36,7 +36,15 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { ) ) - var monoContext = MonoContext(solution, monoNames) + // Collect polymorphic extern definitions + var polyExternDefs: List[Id] = List.empty + externs.foreach { + case Extern.Include(featureFlag, contents) => () + case Extern.Def(id, List(), cparams, vparams, bparams, ret, annotatedCapture, body) => () + case Extern.Def(id, tparams, cparams, vparams, bparams, ret, annotatedCapture, body) => polyExternDefs :+= id + } + + var monoContext = MonoContext(solution, monoNames, polyExternDefs) val monoDecls = declarations flatMap (monomorphize(_)(using monoContext)) val monoDefs = monomorphize(definitions)(using monoContext) // monoDecls.foreach(decl => println(util.show(decl))) @@ -79,8 +87,9 @@ class MonoFindContext { typingContext += (tparam -> TypeArg.Var(functionId, index)) } -case class MonoContext(solution: Solution, names: MonoNames) { +case class MonoContext(solution: Solution, names: MonoNames, polyExternDefs: List[Id]) { var replacementTparams: Map[Id, Ground] = Map.empty + def isPolyExtern(id: Id) = polyExternDefs.contains(id) } def findConstraints(definitions: List[Toplevel])(using MonoFindContext): Constraints = @@ -298,13 +307,22 @@ def monomorphize(decl: Declaration)(using ctx: MonoContext): List[Declaration] = ) def monomorphize(constructor: Constructor, tparamCount: Int)(using ctx: MonoContext): List[Constructor] = constructor match + case Constructor(id, List(), fields) => + List(Constructor(id, List.empty, fields map monomorphize)) case Constructor(id, tparams, fields) => val monoTypes = ctx.solution.getOrElse(id, Set.empty).toList + if (monoTypes.isEmpty) { + // FIXME?: Assuming here tparams is empty, or at least tparams.drop(tparamCount) is + // This case can happen if there is no flow into a specific constructor, + // in which case we should still emit the Constructor, but without the tparams + List(Constructor(id, List.empty, fields map monomorphize)) + } else { monoTypes.map(baseTypes => val replacementTparams = tparams.drop(tparamCount).zip(baseTypes).toMap ctx.replacementTparams ++= replacementTparams Constructor(ctx.names(id, baseTypes), List.empty, fields map monomorphize) ) + } def monomorphize(block: Block)(using ctx: MonoContext): Block = block match case b: BlockLit => monomorphize(b) @@ -321,6 +339,8 @@ def monomorphize(interface: BlockType.Interface)(using ctx: MonoContext): BlockT BlockType.Interface(replacementData.name, replacementData.targs) def monomorphize(operation: Operation)(using ctx: MonoContext): List[Operation] = operation match + case Operation(name, List(), cparams, vparams, bparams, body) => + List(Operation(name, List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(body))) case Operation(name, tparams, cparams, vparams, bparams, body) => val monoTypes = ctx.solution.getOrElse(name, Set.empty).toList monoTypes.map(baseTypes => @@ -332,7 +352,8 @@ def monomorphize(operation: Operation)(using ctx: MonoContext): List[Operation] 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)) + // FIXME: Is passing tparams directly here without any change correct? + BlockLit(tparams, 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) @@ -342,7 +363,7 @@ def monomorphize(field: Field)(using ctx: MonoContext): Field = field match 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) if ctx.isPolyExtern(id) => blockVar 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) @@ -356,8 +377,7 @@ def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match case Var(ref, init, capture, body) => Var(ref, monomorphize(init), capture, monomorphize(body)) case ImpureApp(id, callee, targs, vargs, bargs, body) => - val replacementData = replacementDataFromTargs(callee.id, targs) - ImpureApp(id, monomorphize(callee, replacementData.name), List.empty, vargs map monomorphize, bargs map monomorphize, monomorphize(body)) + ImpureApp(id, callee, targs, vargs map monomorphize, bargs map monomorphize, 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) @@ -385,7 +405,8 @@ def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match Resume(monomorphize(k), monomorphize(body)) // 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 Reset(body) => + Reset(body) case Def(id, BlockLit(List(), cparams, vparams, bparams, bbody), body) => Stmt.Def(id, BlockLit(List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(bbody)), monomorphize(body)) case Def(id, BlockLit(tparams, cparams, vparams, bparams, bbody), body) => @@ -403,7 +424,17 @@ def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match 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 flatMap monomorphize + val monoScrutinee = monomorphize(scrutinee) + + // FIXME: Not correct in all cases. Have to figure out where this is needed + // We need the type of the scrutinee, to give each clause the correct monomorphized name based on said type + val monoScrutineeType = exprType(scrutinee) match { + case ValueType.Data(name, targs) => + targs map monomorphize + case _ => sys error "scrutinee type was not data" + } + + val monoClauses = clauses.flatMap(monomorphize(_, monoScrutineeType.toVector)) Match(monomorphize(scrutinee), monoClauses, monomorphize(default)) case Get(id, annotatedTpe, ref, annotatedCapt, body) => Get(id, monomorphize(annotatedTpe), ref, annotatedCapt, monomorphize(body)) @@ -414,9 +445,34 @@ def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match case Region(body) => Region(monomorphize(body)) case Hole(span) => Hole(span) -def monomorphize(clause: (Id, BlockLit))(using ctx: MonoContext): List[(Id, BlockLit)] = clause match +def exprType(expr: Expr): ValueType = expr match { + case Box(b, annotatedCapture) => ValueType.Boxed(b.tpe, annotatedCapture) + case Literal(value, annotatedType) => annotatedType + case Make(data, tag, targs, vargs) => data + case PureApp(b, targs, vargs) => b.annotatedTpe match { + case effekt.core.BlockType.Function(tparams, cparams, vparams, bparams, result) => result + case effekt.core.BlockType.Interface(name, targs) => ValueType.Data(name, targs) + } + case ValueVar(id, annotatedType) => annotatedType +} + +def monomorphize(clause: (Id, BlockLit), scrutineeTypes: Vector[ValueType])(using ctx: MonoContext): List[(Id, BlockLit)] = clause match case (id, BlockLit(List(), cparams, vparams, bparams, body)) => + val monoName = if (scrutineeTypes.isEmpty) { + Some(id) + } else { + ctx.names.get((id, scrutineeTypes map toTypeArg)) + } + + monoName match { + case Some(name) => List((name, BlockLit(List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(body)))) + case None => + // WARN: There is no mono name for some clause in the match + // This will happen for example in List[T] ( Cons(head: T, rest: List[T]), Nil() ) + // if one of the constructors is never initialized and therefore there is no Constraint flowing into it + // in that case we can just reuse the original name, as it is never initialized List((id, BlockLit(List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(body)))) + } case (id, BlockLit(tparams, cparams, vparams, bparams, body)) => val newClauseNameMap = ctx.names.view.filterKeys((tid, groundTypes) => tid == id) newClauseNameMap.map((clauseKey, monoId) => @@ -433,11 +489,17 @@ def monomorphize(opt: Option[Stmt])(using ctx: MonoContext): Option[Stmt] = opt def monomorphize(expr: Expr)(using ctx: MonoContext): Expr = expr match case Literal(value, annotatedType) => Literal(value, monomorphize(annotatedType)) + // FIXME: Check these two cases below. Is the first one the only one we need? case PureApp(b, targs, vargs) => - val replacementData = replacementDataFromTargs(b.id, targs) - PureApp(monomorphize(b, replacementData.name), List.empty, vargs map monomorphize) + PureApp(b, targs, vargs) + // case PureApp(b, targs, vargs) => + // val replacementData = replacementDataFromTargs(b.id, targs) + // val replaceBVar = monomorphize(b, replacementData.name) + // println(s"data: ${replacementData}, bvar: ${replaceBVar}, targs: ${targs}") + // PureApp(replaceBVar, List.empty, vargs map monomorphize) case Make(data, tag, targs, vargs) => - val replacementTag = ctx.names.getOrElse((tag, (targs map toTypeArg).toVector), tag) + // TODO: When are targs relevant and not data.targs + val replacementTag = ctx.names.getOrElse((tag, (data.targs map toTypeArg).toVector), tag) Make(replacementDataFromTargs(data.name, data.targs), replacementTag, List.empty, vargs map monomorphize) case Box(b, annotatedCapture) => Box(monomorphize(b), annotatedCapture) @@ -449,17 +511,19 @@ def monomorphize(valueParam: ValueParam)(using ctx: MonoContext): ValueParam = v 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 b: BlockType.Interface => monomorphize(b) + // FIXME: Is this correct? + case BlockType.Interface(name, targs) => BlockType.Interface(name, targs map monomorphize) def monomorphize(valueType: ValueType)(using ctx: MonoContext): ValueType = valueType match case ValueType.Var(name) => ctx.replacementTparams(name) match { - case TypeArg.Base(tpe, targs) => ValueType.Var(tpe) + case TypeArg.Base(tpe, targs) => + // FIXME: Why was this ever ValueType.Var? + ValueType.Data(tpe, targs map toValueType) case TypeArg.Boxed(tpe, capt) => ValueType.Boxed(tpe, capt) } case ValueType.Data(name, targs) => replacementDataFromTargs(name, targs) @@ -491,9 +555,22 @@ def replacementDataFromTargs(id: FunctionId, targs: List[ValueType])(using ctx: if (targs.isEmpty) return ValueType.Data(id, targs) val baseTypes: List[Ground] = targs map toTypeArg - ValueType.Data(ctx.names((id, baseTypes.toVector)), List.empty) + + val nameOpt = ctx.names.get((id, baseTypes.toVector)) + nameOpt match { + // If we have a monomorphized name stored, then this Type can be replaced with that + case Some(name) => ValueType.Data(name, List.empty) + // If there is a polymorphic type with no name stored, then it HAS to be an extern type + // We can't really do anything about those and have to leave them + case None => ValueType.Data(id, targs) + } def toTypeArg(vt: ValueType)(using ctx: MonoContext): Ground = 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) => TypeArg.Boxed(tpe, capt) + +def toValueType(ta: TypeArg)(using ctx: MonoContext): ValueType = ta match + case TypeArg.Base(tpe, targs) => ValueType.Data(tpe, targs map toValueType) + case TypeArg.Var(funId, pos) => ??? + case TypeArg.Boxed(tpe, capt) => ValueType.Boxed(tpe, capt) From 6df99ca5cccef9967cac523c78218159bc565b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Thu, 30 Oct 2025 09:43:44 +0100 Subject: [PATCH 44/57] Fix bug in recursive solving --- .../src/main/scala/effekt/core/Mono.scala | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 95180c47e..214556799 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -248,16 +248,17 @@ def solveConstraints(constraints: Constraints): Solution = filteredConstraints.foreach(b => var l: List[List[Ground]] = List(List.empty) def listFromIndex(ind: Int) = if (ind >= l.length) List.empty else l(ind) - b.foreach({ - case TypeArg.Base(tpe, targs) => - l = productAppend(l, List(TypeArg.Base(tpe, targs))) - case TypeArg.Var(fnId, pos) => - val funSolved = solved.getOrElse(fnId, Set.empty) + + def solveTypeArg(typeArg: TypeArg): List[Ground] = typeArg match { + case TypeArg.Base(tpe, targs) => List(TypeArg.Base(tpe, targs flatMap solveTypeArg)) + case TypeArg.Boxed(tpe, capt) => List(TypeArg.Boxed(tpe, capt)) + case TypeArg.Var(funId, pos) => + val funSolved = solved.getOrElse(funId, Set.empty) val posArgs = funSolved.map(v => v(pos)).toList - l = productAppend(l, posArgs) - case TypeArg.Boxed(tpe, capt) => - l = productAppend(l, List(TypeArg.Boxed(tpe, capt))) - }) + posArgs + } + + b.foreach(typeArg => l = productAppend(l, solveTypeArg(typeArg))) nbs ++= l ) nbs From 300661d1955bec66e5348b67ad2d4769cbd181fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Thu, 30 Oct 2025 09:44:22 +0100 Subject: [PATCH 45/57] Correctly monomorphize TypeArgs --- .../src/main/scala/effekt/core/Mono.scala | 20 +++++++++---------- 1 file changed, 10 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 214556799..1654ac11a 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -521,15 +521,19 @@ def monomorphize(blockType: BlockType)(using ctx: MonoContext): BlockType = bloc case BlockType.Interface(name, targs) => BlockType.Interface(name, targs map monomorphize) def monomorphize(valueType: ValueType)(using ctx: MonoContext): ValueType = valueType match - case ValueType.Var(name) => ctx.replacementTparams(name) match { - case TypeArg.Base(tpe, targs) => - // FIXME: Why was this ever ValueType.Var? - ValueType.Data(tpe, targs map toValueType) - case TypeArg.Boxed(tpe, capt) => ValueType.Boxed(tpe, capt) - } + case ValueType.Var(name) => monomorphize(ctx.replacementTparams(name)) case ValueType.Data(name, targs) => replacementDataFromTargs(name, targs) case ValueType.Boxed(tpe, capt) => ValueType.Boxed(monomorphize(tpe), capt) +def monomorphize(typeArg: TypeArg)(using ctx: MonoContext): ValueType = typeArg match { + case TypeArg.Base(tpe, targs) => ValueType.Data(tpe, targs map monomorphize) + case TypeArg.Boxed(tpe, capt) => ValueType.Boxed(tpe, capt) + case TypeArg.Var(funId, pos) => + // FIXME: Do we want to reflect this unreachability in the Data structure used for monomorphizing? + // we would need another version of TypeArg that disallows targs in Base to be anything other than Ground + throw new RuntimeException(s"All the vars should have been removed in the solving stage, still got '${typeArg}'") +} + var monoCounter = 0 def freshMonoName(baseId: Id, tpe: Ground): Id = monoCounter += 1 @@ -571,7 +575,3 @@ def toTypeArg(vt: ValueType)(using ctx: MonoContext): Ground = vt match case ValueType.Var(name) => ctx.replacementTparams(name) case ValueType.Boxed(tpe, capt) => TypeArg.Boxed(tpe, capt) -def toValueType(ta: TypeArg)(using ctx: MonoContext): ValueType = ta match - case TypeArg.Base(tpe, targs) => ValueType.Data(tpe, targs map toValueType) - case TypeArg.Var(funId, pos) => ??? - case TypeArg.Boxed(tpe, capt) => ValueType.Boxed(tpe, capt) From 336694537264079c20f7d706e7743fad44230435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Wed, 5 Nov 2025 10:12:54 +0100 Subject: [PATCH 46/57] Fix indentation --- effekt/shared/src/main/scala/effekt/core/Mono.scala | 2 +- 1 file changed, 1 insertion(+), 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 1654ac11a..3c286df24 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -472,7 +472,7 @@ def monomorphize(clause: (Id, BlockLit), scrutineeTypes: Vector[ValueType])(usin // This will happen for example in List[T] ( Cons(head: T, rest: List[T]), Nil() ) // if one of the constructors is never initialized and therefore there is no Constraint flowing into it // in that case we can just reuse the original name, as it is never initialized - List((id, BlockLit(List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(body)))) + List((id, BlockLit(List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(body)))) } case (id, BlockLit(tparams, cparams, vparams, bparams, body)) => val newClauseNameMap = ctx.names.view.filterKeys((tid, groundTypes) => tid == id) From ab80da1b24986aa3b6bd72a4641fa20d021b1bc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Wed, 5 Nov 2025 21:36:00 +0100 Subject: [PATCH 47/57] Rewrite findId as findConstraints --- .../src/main/scala/effekt/core/Mono.scala | 88 +++++++++++++------ 1 file changed, 59 insertions(+), 29 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 3c286df24..6680d8728 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -128,7 +128,8 @@ def findConstraints(block: Block)(using ctx: MonoFindContext): Constraints = blo def findConstraints(blockType: BlockType.Interface)(using ctx: MonoFindContext): Constraints = blockType match case BlockType.Interface(name, targs) => - List(Constraint(targs.map(findId).toVector, name)) + val (newTargs, constraints) = findConstraints(targs) + List(Constraint(newTargs.toVector, name)) ++ constraints def findConstraints(blockType: BlockType.Function, fnId: Id)(using ctx: MonoFindContext): Constraints = blockType match case BlockType.Function(tparams, cparams, vparams, bparams, result) => @@ -148,7 +149,8 @@ def findConstraints(operation: Operation)(using ctx: MonoFindContext): Constrain def findConstraints(constructor: Constructor)(using ctx: MonoFindContext): Constraints = constructor match case Constructor(id, tparams, List()) => List.empty case Constructor(id, tparams, fields) => - List(Constraint(((fields map (_.tpe)) map findId).toVector, id)) + val (newTargs, constraints) = findConstraints(fields map (_.tpe)) + List(Constraint(newTargs.toVector, id)) ++ constraints def findConstraints(stmt: Stmt)(using ctx: MonoFindContext): Constraints = stmt match case Let(id, annotatedTpe, binding, body) => findConstraints(binding) ++ findConstraints(body) @@ -156,21 +158,26 @@ def findConstraints(stmt: Stmt)(using ctx: MonoFindContext): Constraints = stmt case Val(id, annotatedTpe, binding, body) => findConstraints(binding) ++ findConstraints(body) case Var(ref, init, capture, body) => findConstraints(body) case ImpureApp(id, callee, targs, vargs, bargs, body) => - List(Constraint(targs.map(findId).toVector, callee.id)) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) ++ findConstraints(body) + val (newTargs, constraints) = findConstraints(targs) + List(Constraint(newTargs.toVector, callee.id)) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) ++ findConstraints(body) ++ constraints case App(callee: BlockVar, targs, vargs, bargs) => - List(Constraint(targs.map(findId).toVector, callee.id)) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) + val (newTargs, constraints) = findConstraints(targs) + List(Constraint(newTargs.toVector, callee.id)) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) ++ constraints // 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)) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) + val (newTargs, constraints) = findConstraints(targs) + List(Constraint(newTargs.toVector, id)) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) ++ constraints case App(callee, targs, vargs, bargs) => findConstraints(callee) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) // TODO: Maybe need to do something with methodTpe case Invoke(callee: BlockVar, method, methodTpe, targs, vargs, bargs) => - List(Constraint(targs.map(findId).toVector, method)) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) + val (newTargs, constraints) = findConstraints(targs) + List(Constraint(newTargs.toVector, method)) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) ++ constraints case Invoke(Unbox(ValueVar(id, annotatedType)), method, methodTpe, targs, vargs, bargs) => - List(Constraint(targs.map(findId).toVector, method)) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) + val (newTargs, constraints) = findConstraints(targs) + List(Constraint(newTargs.toVector, method)) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) ++ constraints case Invoke(callee, method, methodTpe, targs, vargs, bargs) => findConstraints(callee) ++ vargs.flatMap(findConstraints) ++ bargs.flatMap(findConstraints) case Reset(body) => findConstraints(body) @@ -211,20 +218,42 @@ def findConstraints(opt: Option[Stmt])(using ctx: MonoFindContext): Constraints def findConstraints(expr: Expr)(using ctx: MonoFindContext): Constraints = expr match case PureApp(b, targs, vargs) => - List(Constraint(targs.map(findId).toVector, b.id)) + val (newTargs, constraints) = findConstraints(targs) + Constraint(newTargs.toVector, b.id) :: constraints case ValueVar(id, annotatedType) => List.empty case Literal(value, annotatedType) => List.empty case Make(data, tag, targs, vargs) => - List(Constraint(targs.map(findId).toVector, tag)) ++ - List(Constraint(data.targs.map(findId).toVector, tag)) // <: Just + // TODO: Is this the correct order? + val combinedTargs = data.targs ++ targs + val (newTargs, constraints) = findConstraints(combinedTargs) + List(Constraint(newTargs.toVector, tag)) ++ // <: Just + constraints case Box(b, annotatedCapture) => findConstraints(b) -def findId(vt: ValueType)(using ctx: MonoFindContext): TypeArg = vt match - // TODO: Perhaps recurse into tpe - case ValueType.Boxed(tpe, capt) => TypeArg.Boxed(tpe, capt) - case ValueType.Data(name, targs) => TypeArg.Base(name, targs map findId) - case ValueType.Var(name) => ctx.typingContext(name) +def findConstraints(vts: List[ValueType])(using ctx: MonoFindContext): (List[TypeArg], Constraints) = { + val vtFindConstraints = vts map findConstraints + val targs = vtFindConstraints.map(_._1) + val constraints = vtFindConstraints.flatMap(_._2) + (targs, constraints) +} + +def findConstraints(vt: ValueType)(using ctx: MonoFindContext): (TypeArg, Constraints) = vt match { + case ValueType.Boxed(tpe, capt) => { + // TODO: Perhaps recurse into tpe + (TypeArg.Boxed(tpe, capt), List.empty) + } + case ValueType.Data(name, targs) => { + val (newTargs, constraints) = findConstraints(targs) + val additionalConstraints = if (!newTargs.isEmpty) { + List(Constraint(newTargs.toVector, name)) + } else { + List.empty + } + (TypeArg.Base(name, newTargs), constraints ++ additionalConstraints) + } + case ValueType.Var(name) => (ctx.typingContext(name), List.empty) +} def solveConstraints(constraints: Constraints): Solution = var solved: Solution = Map() @@ -236,7 +265,7 @@ def solveConstraints(constraints: Constraints): Solution = while (true) { val previousSolved = solved vecConstraints.foreach((sym, tas) => - val sol = solveConstraints(sym).map(bs => bs.toVector) + val sol = solveConstraints(sym).map(bs => bs.toVector) solved += (sym -> sol) ) if (previousSolved == solved) return solved @@ -250,7 +279,9 @@ def solveConstraints(constraints: Constraints): Solution = def listFromIndex(ind: Int) = if (ind >= l.length) List.empty else l(ind) def solveTypeArg(typeArg: TypeArg): List[Ground] = typeArg match { - case TypeArg.Base(tpe, targs) => List(TypeArg.Base(tpe, targs flatMap solveTypeArg)) + case TypeArg.Base(tpe, targs) => + val solvedTargs = targs flatMap solveTypeArg + List(TypeArg.Base(tpe, solvedTargs)) case TypeArg.Boxed(tpe, capt) => List(TypeArg.Boxed(tpe, capt)) case TypeArg.Var(funId, pos) => val funSolved = solved.getOrElse(funId, Set.empty) @@ -275,7 +306,7 @@ def monomorphize(definitions: List[Toplevel])(using ctx: MonoContext): List[Topl 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)))) + List(Toplevel.Def(id, BlockLit(List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(body)))) case Toplevel.Def(id, BlockLit(tparams, cparams, vparams, bparams, body)) => val monoTypes = ctx.solution(id).toList monoTypes.map(baseTypes => @@ -490,18 +521,14 @@ def monomorphize(opt: Option[Stmt])(using ctx: MonoContext): Option[Stmt] = opt def monomorphize(expr: Expr)(using ctx: MonoContext): Expr = expr match case Literal(value, annotatedType) => Literal(value, monomorphize(annotatedType)) - // FIXME: Check these two cases below. Is the first one the only one we need? case PureApp(b, targs, vargs) => PureApp(b, targs, vargs) - // case PureApp(b, targs, vargs) => - // val replacementData = replacementDataFromTargs(b.id, targs) - // val replaceBVar = monomorphize(b, replacementData.name) - // println(s"data: ${replacementData}, bvar: ${replaceBVar}, targs: ${targs}") - // PureApp(replaceBVar, List.empty, vargs map monomorphize) case Make(data, tag, targs, vargs) => - // TODO: When are targs relevant and not data.targs - val replacementTag = ctx.names.getOrElse((tag, (data.targs map toTypeArg).toVector), tag) - Make(replacementDataFromTargs(data.name, data.targs), replacementTag, List.empty, vargs map monomorphize) + // TODO: Is this the correct order to combine the two type args or the other way around + val combinedTargs = data.targs ++ targs + val typeArgs = combinedTargs map toTypeArg + val replacementTag = ctx.names.getOrElse((tag, typeArgs.toVector), tag) + Make(replacementDataFromTargs(data.name, combinedTargs), replacementTag, List.empty, vargs map monomorphize) case Box(b, annotatedCapture) => Box(monomorphize(b), annotatedCapture) case ValueVar(id, annotatedType) => @@ -565,9 +592,12 @@ def replacementDataFromTargs(id: FunctionId, targs: List[ValueType])(using ctx: nameOpt match { // If we have a monomorphized name stored, then this Type can be replaced with that case Some(name) => ValueType.Data(name, List.empty) - // If there is a polymorphic type with no name stored, then it HAS to be an extern type + // If there is a polymorphic type with no name stored, then it SHOULD be an extern type // We can't really do anything about those and have to leave them - case None => ValueType.Data(id, targs) + // FIXME: Currently these might also be types that are not extern but something like the `Exists` type in `exists.effekt` + case None => + // println(s"expecting extern type for '${id}', '${baseTypes.toVector}'") + ValueType.Data(id, targs) } def toTypeArg(vt: ValueType)(using ctx: MonoContext): Ground = vt match From 22815640e3c1ffe86762e7c85a22a34c84834da7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Thu, 6 Nov 2025 10:17:21 +0100 Subject: [PATCH 48/57] Add some debugging functionality --- .../src/main/scala/effekt/core/Mono.scala | 24 +++++++++++++++++-- 1 file changed, 22 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 6680d8728..779f2d754 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -54,6 +54,12 @@ object Mono extends Phase[CoreTransformed, CoreTransformed] { // println(util.show(newModuleDecl)) + // for examples/pos/probabilistic.effekt + // This + // Vector(Base(Weighted,List(Base(Bool,List()), Base(Var,List())))) + // Should be + // Vector(Base(Weighted,List(Base(Bool,List())), Base(Weighted,List(Base(Bool,List()))))) + return Some(CoreTransformed(source, tree, mod, newModuleDecl)) } } @@ -90,6 +96,9 @@ class MonoFindContext { case class MonoContext(solution: Solution, names: MonoNames, polyExternDefs: List[Id]) { var replacementTparams: Map[Id, Ground] = Map.empty def isPolyExtern(id: Id) = polyExternDefs.contains(id) + + def allNames(id: Id) = + names.filter((key, _) => key._1 == id) } def findConstraints(definitions: List[Toplevel])(using MonoFindContext): Constraints = @@ -260,7 +269,7 @@ def solveConstraints(constraints: Constraints): Solution = val filteredConstraints = constraints.filterNot(c => c.lower.isEmpty) val groupedConstraints = filteredConstraints.groupBy(c => c.upper) - val vecConstraints = groupedConstraints.map((sym, constraints) => (sym -> constraints.map(c => c.lower))) + val vecConstraints = groupedConstraints.map((sym, constraints) => (sym -> constraints.map(c => c.lower).toSet)) while (true) { val previousSolved = solved @@ -527,7 +536,18 @@ def monomorphize(expr: Expr)(using ctx: MonoContext): Expr = expr match // TODO: Is this the correct order to combine the two type args or the other way around val combinedTargs = data.targs ++ targs val typeArgs = combinedTargs map toTypeArg - val replacementTag = ctx.names.getOrElse((tag, typeArgs.toVector), tag) + val replacementTag = ctx.names.get((tag, typeArgs.toVector)) match { + case Some(value) => value + case None => { + // TODO: This is for debugging, can ideally be removed later because it doesn't happen :^) + // (for non-empty typeArgs) + if (typeArgs.length > 0) { + println(s"Could not find name for ${tag} with types ${typeArgs.toVector}") + println(s"all names: ${ctx.allNames(tag)}") + } + tag + } + } Make(replacementDataFromTargs(data.name, combinedTargs), replacementTag, List.empty, vargs map monomorphize) case Box(b, annotatedCapture) => Box(monomorphize(b), annotatedCapture) From 797fdbdcb2416792a8db732939a62675a3be7c3b Mon Sep 17 00:00:00 2001 From: Mattis Boeckle Date: Thu, 6 Nov 2025 13:30:48 +0100 Subject: [PATCH 49/57] Add solving test case --- .../jvm/src/test/scala/effekt/core/MonoTests.scala | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/effekt/jvm/src/test/scala/effekt/core/MonoTests.scala b/effekt/jvm/src/test/scala/effekt/core/MonoTests.scala index a485f72c4..75831f6b2 100644 --- a/effekt/jvm/src/test/scala/effekt/core/MonoTests.scala +++ b/effekt/jvm/src/test/scala/effekt/core/MonoTests.scala @@ -126,4 +126,18 @@ class MonoTests extends AbstractMonoTests { assertEquals(solveConstraints(constraints), expectedSolved) } + + test("nested constraints") { + val constraints = List( + Constraint(Vector(Base(fnId("Weighted"), List(Var(fnId("b"), 0)))), fnId("a")), + Constraint(Vector(BaseTInt), fnId("b")) + ) + + val expectedSolved: Solution = Map( + fnId("b") -> Set(Vector(BaseTInt)), + fnId("a") -> Set(Vector(Base(fnId("Weighted"), List(BaseTInt)))) + ) + + assertEquals(solveConstraints(constraints), expectedSolved) + } } From 56c6feea7b9fd5008f8c151d680ea88c7ac9a928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Thu, 6 Nov 2025 13:55:19 +0100 Subject: [PATCH 50/57] Fix productAppend for our purpose --- .../test/scala/effekt/core/MonoTests.scala | 36 +++++++++++++++++++ .../src/main/scala/effekt/core/Mono.scala | 3 +- 2 files changed, 38 insertions(+), 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 75831f6b2..2e8e7e508 100644 --- a/effekt/jvm/src/test/scala/effekt/core/MonoTests.scala +++ b/effekt/jvm/src/test/scala/effekt/core/MonoTests.scala @@ -23,6 +23,42 @@ abstract class AbstractMonoTests extends CorePhaseTests(Mono) { ) } +class MonoProductAppendTests extends AbstractMonoTests { + + test("product append start empty append empty") { + val start = List(List.empty) + val append1 = List.empty + + val res = productAppend(start, append1) + val expected = List(List.empty) + + assertEquals(res, expected) + } + + test("product append start with empty list") { + val start = List(List.empty) + val append1 = List(1, 2) + val append2 = List(3, 4) + + var res = productAppend(start, append1) + res = productAppend(res, append2) + + val expected = List(List(1, 3), List(1, 4), List(2, 3), List(2, 4)) + assertEquals(res, expected) + } + + test("product append unequal number of added values") { + val start = List(List(1)) + val append1 = List(2,3) + val append2 = List(4) + + var res = productAppend(start, append1) + res = productAppend(res, append2) + val expected = List(List(1,2,4), List(1,3,4)) + } + +} + class MonoTests extends AbstractMonoTests { import TypeArg.* diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 779f2d754..051114e05 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -306,7 +306,8 @@ def solveConstraints(constraints: Constraints): Solution = solved def productAppend[A](ls: List[List[A]], rs: List[A]): List[List[A]] = - rs.flatMap(r => ls.map(l => l :+ r)) + if (rs.isEmpty) return ls + for { l <- ls; r <- rs } yield l :+ r def monomorphize(definitions: List[Toplevel])(using ctx: MonoContext): List[Toplevel] = var newDefinitions: List[Toplevel] = List.empty From b99052a4c782502a5663a14035636eb43a74bba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Thu, 6 Nov 2025 14:31:38 +0100 Subject: [PATCH 51/57] Rewrite MonoTest infra --- .../test/scala/effekt/core/MonoTests.scala | 88 +++++++++---------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/effekt/jvm/src/test/scala/effekt/core/MonoTests.scala b/effekt/jvm/src/test/scala/effekt/core/MonoTests.scala index 2e8e7e508..52210d61e 100644 --- a/effekt/jvm/src/test/scala/effekt/core/MonoTests.scala +++ b/effekt/jvm/src/test/scala/effekt/core/MonoTests.scala @@ -13,14 +13,14 @@ abstract class AbstractMonoTests extends CorePhaseTests(Mono) { 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"), - ) + var fnId: Map[String, FunctionId] = Map() + + def id(name: String): FunctionId = + fnId.getOrElse(name, { + val storedId = Id(name) + fnId += (name -> storedId) + storedId + }) } class MonoProductAppendTests extends AbstractMonoTests { @@ -65,11 +65,11 @@ class MonoTests extends AbstractMonoTests { test("simple polymorphic function") { val constraints = List( - Constraint(Vector(BaseTInt), fnId("a")), - Constraint(Vector(BaseTString), fnId("a")) + Constraint(Vector(BaseTInt), id("a")), + Constraint(Vector(BaseTString), id("a")) ) val expectedSolved: Solution = Map( - fnId("a") -> Set(Vector(BaseTInt), Vector(BaseTString)) + id("a") -> Set(Vector(BaseTInt), Vector(BaseTString)) ) assertEquals(solveConstraints(constraints), expectedSolved) @@ -77,13 +77,13 @@ class MonoTests extends AbstractMonoTests { 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")), + Constraint(Vector(Var(id("b"), 0)), id("a")), + Constraint(Vector(BaseTInt), id("a")), + Constraint(Vector(BaseTString), id("b")), ) val expectedSolved: Solution = Map( - fnId("a") -> Set(Vector(BaseTInt), Vector(BaseTString)), - fnId("b") -> Set(Vector(BaseTString)), + id("a") -> Set(Vector(BaseTInt), Vector(BaseTString)), + id("b") -> Set(Vector(BaseTString)), ) assertEquals(solveConstraints(constraints), expectedSolved) @@ -91,12 +91,12 @@ class MonoTests extends AbstractMonoTests { 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")), + Constraint(Vector(BaseTInt, BaseTString), id("a")), + Constraint(Vector(BaseTBool, BaseTChar), id("a")), + Constraint(Vector(BaseTBool, BaseTString), id("a")), ) val expectedSolved: Solution = Map( - fnId("a") -> Set( + id("a") -> Set( Vector(BaseTInt, BaseTString), Vector(BaseTBool, BaseTChar), Vector(BaseTBool, BaseTString), @@ -108,12 +108,12 @@ class MonoTests extends AbstractMonoTests { 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")), + Constraint(Vector(Var(id("b"), 1), Var(id("b"), 0)), id("a")), + Constraint(Vector(BaseTString, BaseTBool), id("b")), ) val expectedSolved: Solution = Map( - fnId("a") -> Set(Vector(BaseTBool, BaseTString)), - fnId("b") -> Set(Vector(BaseTString, BaseTBool)), + id("a") -> Set(Vector(BaseTBool, BaseTString)), + id("b") -> Set(Vector(BaseTString, BaseTBool)), ) assertEquals(solveConstraints(constraints), expectedSolved) @@ -121,11 +121,11 @@ class MonoTests extends AbstractMonoTests { test("recursive polymorphic function") { val constraints = List( - Constraint(Vector(Var(fnId("a"), 0)), fnId("a")), - Constraint(Vector(BaseTInt), fnId("a")), + Constraint(Vector(Var(id("a"), 0)), id("a")), + Constraint(Vector(BaseTInt), id("a")), ) val expectedSolved: Solution = Map( - fnId("a") -> Set(Vector(BaseTInt)), + id("a") -> Set(Vector(BaseTInt)), ) assertEquals(solveConstraints(constraints), expectedSolved) @@ -133,14 +133,14 @@ class MonoTests extends AbstractMonoTests { 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")), + Constraint(Vector(Var(id("b"), 0)), id("a")), + Constraint(Vector(Var(id("a"), 0)), id("b")), + Constraint(Vector(BaseTInt), id("a")), + Constraint(Vector(BaseTString), id("b")), ) val expectedSolved: Solution = Map( - fnId("a") -> Set(Vector(BaseTInt), Vector(BaseTString)), - fnId("b") -> Set(Vector(BaseTInt), Vector(BaseTString)), + id("a") -> Set(Vector(BaseTInt), Vector(BaseTString)), + id("b") -> Set(Vector(BaseTInt), Vector(BaseTString)), ) assertEquals(solveConstraints(constraints), expectedSolved) @@ -148,16 +148,16 @@ class MonoTests extends AbstractMonoTests { test("correct product of vars") { val constraints = List( - Constraint(Vector(BaseTInt), fnId("a")), - Constraint(Vector(BaseTString), fnId("a")), - Constraint(Vector(BaseTBool), fnId("b")), - Constraint(Vector(Var(fnId("a"), 0), Var(fnId("b"), 0)), fnId("c")), + Constraint(Vector(BaseTInt), id("a")), + Constraint(Vector(BaseTString), id("a")), + Constraint(Vector(BaseTBool), id("b")), + Constraint(Vector(Var(id("a"), 0), Var(id("b"), 0)), id("c")), ) val expectedSolved: Solution = Map( - fnId("a") -> Set(Vector(BaseTInt), Vector(BaseTString)), - fnId("b") -> Set(Vector(BaseTBool)), - fnId("c") -> Set(Vector(BaseTInt, BaseTBool), Vector(BaseTString, BaseTBool)), + id("a") -> Set(Vector(BaseTInt), Vector(BaseTString)), + id("b") -> Set(Vector(BaseTBool)), + id("c") -> Set(Vector(BaseTInt, BaseTBool), Vector(BaseTString, BaseTBool)), ) assertEquals(solveConstraints(constraints), expectedSolved) @@ -165,13 +165,13 @@ class MonoTests extends AbstractMonoTests { test("nested constraints") { val constraints = List( - Constraint(Vector(Base(fnId("Weighted"), List(Var(fnId("b"), 0)))), fnId("a")), - Constraint(Vector(BaseTInt), fnId("b")) + Constraint(Vector(Base(id("Weighted"), List(Var(id("b"), 0)))), id("a")), + Constraint(Vector(BaseTInt), id("b")) ) val expectedSolved: Solution = Map( - fnId("b") -> Set(Vector(BaseTInt)), - fnId("a") -> Set(Vector(Base(fnId("Weighted"), List(BaseTInt)))) + id("b") -> Set(Vector(BaseTInt)), + id("a") -> Set(Vector(Base(id("Weighted"), List(BaseTInt)))) ) assertEquals(solveConstraints(constraints), expectedSolved) From 8d14e83a3aeaaa8e314e3218b92a743e990fcdf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Thu, 6 Nov 2025 14:32:39 +0100 Subject: [PATCH 52/57] Use cross product for solving typeArgs --- 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 051114e05..961849a47 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -274,7 +274,7 @@ def solveConstraints(constraints: Constraints): Solution = while (true) { val previousSolved = solved vecConstraints.foreach((sym, tas) => - val sol = solveConstraints(sym).map(bs => bs.toVector) + val sol = solveConstraints(sym).map(bs => bs.toVector).filter(v => !v.isEmpty) solved += (sym -> sol) ) if (previousSolved == solved) return solved @@ -289,8 +289,12 @@ def solveConstraints(constraints: Constraints): Solution = def solveTypeArg(typeArg: TypeArg): List[Ground] = typeArg match { case TypeArg.Base(tpe, targs) => - val solvedTargs = targs flatMap solveTypeArg - List(TypeArg.Base(tpe, solvedTargs)) + val solvedTargs = targs map solveTypeArg + var crossTargs: List[List[TypeArg]] = List(List.empty) + solvedTargs.foreach(targ => { + crossTargs = productAppend(crossTargs, targ) + }) + crossTargs.map(tas => TypeArg.Base(tpe, tas)) case TypeArg.Boxed(tpe, capt) => List(TypeArg.Boxed(tpe, capt)) case TypeArg.Var(funId, pos) => val funSolved = solved.getOrElse(funId, Set.empty) From 7b70c64c5f141a3b5288e8735f8ab06c9fefcb8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Fri, 7 Nov 2025 09:34:36 +0100 Subject: [PATCH 53/57] Handle Boxed types --- effekt/shared/src/main/scala/effekt/core/Mono.scala | 9 +++++++-- 1 file changed, 7 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 961849a47..aa25611a5 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -248,10 +248,15 @@ def findConstraints(vts: List[ValueType])(using ctx: MonoFindContext): (List[Typ } def findConstraints(vt: ValueType)(using ctx: MonoFindContext): (TypeArg, Constraints) = vt match { - case ValueType.Boxed(tpe, capt) => { + case ValueType.Boxed(tpe@BlockType.Function(tparams, cparams, vparams, bparams, result), capt) => { // TODO: Perhaps recurse into tpe + // TODO: What do I do with a function type here? It does not have a name which does not work for my current findConstraints (TypeArg.Boxed(tpe, capt), List.empty) } + case ValueType.Boxed(tpe@BlockType.Interface(name, targs), capt) => { + val constraints = findConstraints(tpe) + (TypeArg.Boxed(tpe, capt), constraints) + } case ValueType.Data(name, targs) => { val (newTargs, constraints) = findConstraints(targs) val additionalConstraints = if (!newTargs.isEmpty) { @@ -579,7 +584,7 @@ def monomorphize(valueType: ValueType)(using ctx: MonoContext): ValueType = valu def monomorphize(typeArg: TypeArg)(using ctx: MonoContext): ValueType = typeArg match { case TypeArg.Base(tpe, targs) => ValueType.Data(tpe, targs map monomorphize) - case TypeArg.Boxed(tpe, capt) => ValueType.Boxed(tpe, capt) + case TypeArg.Boxed(tpe, capt) => ValueType.Boxed(monomorphize(tpe), capt) case TypeArg.Var(funId, pos) => // FIXME: Do we want to reflect this unreachability in the Data structure used for monomorphizing? // we would need another version of TypeArg that disallows targs in Base to be anything other than Ground From 97cfb49a90f3c4362c6286be98a17166f80e1811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Fri, 7 Nov 2025 09:35:15 +0100 Subject: [PATCH 54/57] Monomorphize reset --- effekt/shared/src/main/scala/effekt/core/Mono.scala | 10 +++++----- 1 file changed, 5 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 aa25611a5..519733719 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -457,7 +457,7 @@ def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match // 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) + Reset(monomorphize(body)) case Def(id, BlockLit(List(), cparams, vparams, bparams, bbody), body) => Stmt.Def(id, BlockLit(List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(bbody)), monomorphize(body)) case Def(id, BlockLit(tparams, cparams, vparams, bparams, bbody), body) => @@ -551,10 +551,10 @@ def monomorphize(expr: Expr)(using ctx: MonoContext): Expr = expr match case None => { // TODO: This is for debugging, can ideally be removed later because it doesn't happen :^) // (for non-empty typeArgs) - if (typeArgs.length > 0) { - println(s"Could not find name for ${tag} with types ${typeArgs.toVector}") - println(s"all names: ${ctx.allNames(tag)}") - } + // if (typeArgs.length > 0) { + // println(s"Could not find name for ${tag} with types ${typeArgs.toVector}") + // println(s"all names: ${ctx.allNames(tag)}") + // } tag } } From 4c5714bd46ec4391aabaa951eb407ab6595b97e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Fri, 7 Nov 2025 09:35:44 +0100 Subject: [PATCH 55/57] Handle Interface & Property --- .../src/main/scala/effekt/core/Mono.scala | 34 +++++++++++++++++-- 1 file changed, 31 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 519733719..85688060d 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -126,7 +126,12 @@ def findConstraints(declaration: Declaration)(using ctx: MonoFindContext): Const } case Interface(id, tparams, properties) => tparams.zipWithIndex.foreach(ctx.extendTypingContext(_, _, id)) - List.empty + properties flatMap findConstraints + +def findConstraints(property: Property)(using ctx: MonoFindContext): Constraints = property match { + case Property(id, tpe@BlockType.Function(tparams, cparams, vparams, bparams, result)) => findConstraints(tpe, id) + case Property(id, tpe@BlockType.Interface(name, targs)) => findConstraints(tpe) +} def findConstraints(block: Block)(using ctx: MonoFindContext): Constraints = block match case BlockVar(id, annotatedTpe: BlockType.Interface, annotatedCapt) => findConstraints(annotatedTpe) @@ -348,15 +353,38 @@ def monomorphize(decl: Declaration)(using ctx: MonoContext): List[Declaration] = ctx.replacementTparams ++= replacementTparams Declaration.Data(ctx.names(id, baseTypes), List.empty, constructors.flatMap(monomorphize(_, tparams.size))) ) - case Interface(id, List(), properties) => List(decl) + case Interface(id, List(), properties) => { + val monoProp = properties flatMap monomorphize + List(Declaration.Interface(id, List.empty, monoProp)) + } 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) + val monoProp = properties flatMap monomorphize + Declaration.Interface(ctx.names(id, baseTypes), List.empty, monoProp) ) +def monomorphize(property: Property)(using ctx: MonoContext): List[Property] = property match { + case Property(id, tpe@BlockType.Function(tparams, cparams, vparams, bparams, result)) => { + val monoTypes = ctx.solution.getOrElse(id, Set.empty).toList + if (monoTypes.isEmpty) { + // FIXME?: Assuming here tparams is empty, or at least tparams.drop(tparamCount) is + // This case can happen if there is no flow into a specific property, + // in which case we should still emit the Property, but without the tparams + List(Property(id, monomorphize(tpe))) + } else { + monoTypes.map(baseTypes => + val replacementTparams = tparams.zip(baseTypes).toMap + ctx.replacementTparams ++= replacementTparams + Property(ctx.names((id, baseTypes)), monomorphize(tpe)) + ) + } + } + case Property(id, tpe) => List(Property(id, monomorphize(tpe))) +} + def monomorphize(constructor: Constructor, tparamCount: Int)(using ctx: MonoContext): List[Constructor] = constructor match case Constructor(id, List(), fields) => List(Constructor(id, List.empty, fields map monomorphize)) From f5dfa40f7dc9e9fa529b1979d9d64349295d8cfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Fri, 7 Nov 2025 09:36:24 +0100 Subject: [PATCH 56/57] Exclude crashing normalizer tests --- effekt/jvm/src/test/scala/effekt/LLVMTests.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/effekt/jvm/src/test/scala/effekt/LLVMTests.scala b/effekt/jvm/src/test/scala/effekt/LLVMTests.scala index 85d9a418a..18a22d918 100644 --- a/effekt/jvm/src/test/scala/effekt/LLVMTests.scala +++ b/effekt/jvm/src/test/scala/effekt/LLVMTests.scala @@ -54,6 +54,10 @@ class LLVMTests extends EffektTests { // Generic comparison examplesDir / "pos" / "issue733.effekt", + + // Normalizer crash include again when fixed + examplesDir / "pos" / "raytracer.effekt", + examplesDir / "benchmarks" / "nofib" / "constraints.effekt", ) override lazy val withoutOptimizations: Set[File] = Set( From 39c6ed2ac69c1fc670b1ede6e63c21202ba8f375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattis=20B=C3=B6ckle?= Date: Fri, 7 Nov 2025 20:14:06 +0100 Subject: [PATCH 57/57] Fix some problems with interfaces --- .../src/main/scala/effekt/core/Mono.scala | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/core/Mono.scala b/effekt/shared/src/main/scala/effekt/core/Mono.scala index 85688060d..9bd855a15 100644 --- a/effekt/shared/src/main/scala/effekt/core/Mono.scala +++ b/effekt/shared/src/main/scala/effekt/core/Mono.scala @@ -410,7 +410,8 @@ def monomorphize(block: Block)(using ctx: MonoContext): Block = block match case Unbox(pure) => Unbox(monomorphize(pure)) def monomorphize(impl: Implementation)(using ctx: MonoContext): Implementation = impl match - case Implementation(interface, operations) => Implementation(monomorphize(interface), operations.flatMap(monomorphize)) + case Implementation(interface, operations) => + Implementation(monomorphize(interface), operations.flatMap(monomorphize)) def monomorphize(interface: BlockType.Interface)(using ctx: MonoContext): BlockType.Interface = interface match case BlockType.Interface(name, targs) => @@ -419,7 +420,29 @@ def monomorphize(interface: BlockType.Interface)(using ctx: MonoContext): BlockT def monomorphize(operation: Operation)(using ctx: MonoContext): List[Operation] = operation match case Operation(name, List(), cparams, vparams, bparams, body) => - List(Operation(name, List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(body))) + val optMonoTypes = ctx.solution.get(name) + // TODO: Not for here but in general. Why is the type omission not just syntactic sugar and we re-add the tparams. + // What we have to do here seems much more complicated and is probably not the only location where we need to do something like this + // We need to differentiate cases here, because tparams may be omitted + // if they were omitted we can see if they still need to be there if we have a solution for these types + optMonoTypes match { + case None => { + List(Operation(name, List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(body))) + } + case Some(monoTypes) => { + // TODO: I'm assuming that all value params have a Var type if we get into this case. Is that correct? + val tparams = vparams.map(vp => vp.tpe match { + case ValueType.Boxed(tpe, capt) => ??? + case ValueType.Data(name, targs) => ??? + case ValueType.Var(name) => name + }) + monoTypes.toList.map(baseTypes => + val replacementTparams = tparams.zip(baseTypes).toMap + ctx.replacementTparams ++= replacementTparams + Operation(ctx.names(name, baseTypes), List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(body)) + ) + } + } case Operation(name, tparams, cparams, vparams, bparams, body) => val monoTypes = ctx.solution.getOrElse(name, Set.empty).toList monoTypes.map(baseTypes => @@ -440,10 +463,13 @@ def monomorphize(block: BlockVar)(using ctx: MonoContext): BlockVar = block matc 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 +// FIXME: Not a big fan of this function needing so many extra parameters +def monomorphize(blockVar: BlockVar, replacementId: FunctionId, targs: List[ValueType])(using ctx: MonoContext): BlockVar = blockVar match case BlockVar(id, BlockType.Function(List(), cparams, vparams, bparams, result), annotatedCapt) => blockVar case BlockVar(id, BlockType.Function(tparams, cparams, vparams, bparams, result), annotatedCapt) if ctx.isPolyExtern(id) => blockVar case BlockVar(id, BlockType.Function(tparams, cparams, vparams, bparams, result), annotatedCapt) => + val replacementTparams = tparams.zip(targs map toTypeArg).toMap + ctx.replacementTparams ++= replacementTparams val monoAnnotatedTpe = BlockType.Function(List.empty, cparams, vparams map monomorphize, bparams map monomorphize, monomorphize(result)) BlockVar(replacementId, monoAnnotatedTpe, annotatedCapt) case BlockVar(id, annotatedTpe: BlockType.Interface, annotatedCapt) => @@ -459,7 +485,7 @@ def monomorphize(stmt: Stmt)(using ctx: MonoContext): Stmt = stmt match ImpureApp(id, callee, targs, vargs map monomorphize, bargs map monomorphize, 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) + App(monomorphize(callee, replacementData.name, targs), 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) =>