Skip to content

Commit 736a499

Browse files
authored
Add debug flag and separate type checking and inference (#1394)
In sbt you can now call ``` debug ``` to trigger the debug mode and ``` release ``` to trigger the release mode. `effekt.util.Debug` contains a few debugging helpers that are disabled during release: ```scala inline def debug(inline f: => Unit): Unit = inline if DEBUG then f else () inline def debug[T](inline f: => T)(inline otherwise: => T): T = inline if DEBUG then f else otherwise inline def assert(inline assertion: Boolean): Unit = inline if DEBUG then Predef.assert(assertion) inline def assert(inline assertion: Boolean, inline msg: => String): Unit = inline if DEBUG then Predef.assert(assertion, msg) ```
1 parent b8214fd commit 736a499

18 files changed

Lines changed: 751 additions & 445 deletions

File tree

build.sbt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ lazy val testBackendJS = taskKey[Unit]("Run JavaScript backend tests")
1818
lazy val testBackendChez = taskKey[Unit]("Run Chez Scheme backend tests")
1919
lazy val testBackendLLVM = taskKey[Unit]("Run LLVM backend tests")
2020
lazy val testRemaining = taskKey[Unit]("Run all non-backend tests (internal tests) on effektJVM")
21+
lazy val debugCompiler = settingKey[Boolean]("Enable compiler debugging")
2122

2223
lazy val noPublishSettings = Seq(
2324
publish := {},
@@ -84,7 +85,9 @@ lazy val root = project.in(file("effekt"))
8485
lazy val effekt: CrossProject = crossProject(JSPlatform, JVMPlatform).in(file("effekt"))
8586
.settings(
8687
name := "effekt",
87-
version := effektVersion
88+
version := effektVersion,
89+
debugCompiler := sys.props.get("debug").contains("true"),
90+
Compile / sourceGenerators += versionGenerator.taskValue
8891
)
8992
.settings(commonSettings)
9093
.dependsOn(kiama)
@@ -144,6 +147,9 @@ lazy val effekt: CrossProject = crossProject(JSPlatform, JVMPlatform).in(file("e
144147
// cli flag so sbt doesn't crash when effekt does
145148
addCommandAlias("run", "runMain effekt.Main --no-exit-on-error"),
146149

150+
addCommandAlias("debug", ";set debugCompiler := true ;compile"),
151+
addCommandAlias("release", ";set debugCompiler := false ;compile"),
152+
147153
assembleBinary := {
148154
val jarfile = assembly.value
149155

@@ -264,7 +270,6 @@ lazy val effekt: CrossProject = crossProject(JSPlatform, JVMPlatform).in(file("e
264270

265271

266272
generateDocumentation := TreeDocs.replacer.value,
267-
Compile / sourceGenerators += versionGenerator.taskValue,
268273
Compile / sourceGenerators += TreeDocs.generator.taskValue,
269274

270275
collectBenchmarks := benchmarks.collect.value,
@@ -308,16 +313,17 @@ lazy val mvn = Def.task {
308313
if (platform.value == "windows") "mvn.cmd" else "mvn"
309314
}
310315

311-
312316
lazy val versionGenerator = Def.task {
313317
val sourceDir = (Compile / sourceManaged).value
314318
val sourceFile = sourceDir / "effekt" / "util" / "Version.scala"
319+
val debug = debugCompiler.value
315320

316321
IO.write(sourceFile,
317322
s"""package effekt.util
318323
|
319324
|object Version {
320325
| val effektVersion = \"${effektVersion}\"
326+
| inline val DEBUG = $debug
321327
|}
322328
|""".stripMargin)
323329

effekt/jvm/src/main/scala/effekt/Main.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package effekt
33
import org.rogach.scallop.exceptions.ScallopException
44

55
object Main {
6+
67
/**
78
* Main entry point for the Effekt compiler.
89
*

effekt/jvm/src/test/scala/effekt/core/OptimizerTests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class OptimizerTests extends CoreTests {
3737
def normalize(input: String, expected: String)(using munit.Location) =
3838
assertTransformsTo(input, expected) { tree =>
3939
val anfed = BindSubexpressions.transform(tree)
40-
val normalized = Normalizer.normalize(Set(mainSymbol), anfed, 50, true)
40+
val normalized = Normalizer.normalize(Set(mainSymbol), anfed, 50)
4141
Deadcode.remove(mainSymbol, normalized)
4242
}
4343

effekt/jvm/src/test/scala/effekt/core/SizeTests.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class SizeTests extends CoreTests {
88
}
99

1010
test("Small program"){
11-
assertSized(6) {
11+
assertSized(4) {
1212
"""module main
1313
|
1414
|def foo = { () =>
@@ -19,7 +19,7 @@ class SizeTests extends CoreTests {
1919
}
2020

2121
test("Nested definitions") {
22-
assertSized(15) {
22+
assertSized(14) {
2323
""" module main
2424
|
2525
| def bar = { () => return 1 }

effekt/jvm/src/test/scala/effekt/core/TypeInferenceTests.scala

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package core
33

44
import effekt.core.Type.*
55
import effekt.util.messages.{ DebugMessaging, ErrorReporter }
6-
import scala.collection.immutable.HashMap
6+
import effekt.util.DB
77

88
class TypeInferenceTests extends CoreTests {
99

@@ -87,13 +87,13 @@ class TypeInferenceTests extends CoreTests {
8787
}
8888

8989
test("compatibility") {
90-
Free.mergeValues(HashMap.empty, HashMap.empty)
91-
Free.mergeValues(HashMap(f -> TInt), HashMap.empty)
92-
Free.mergeValues(HashMap(f -> TInt), HashMap(f -> TInt))
93-
Free.mergeValues(HashMap(f -> TInt), HashMap(f -> TInt, g -> TByte))
90+
Free.mergeValues(DB.empty, DB.empty)
91+
Free.mergeValues(DB(f -> TInt), DB.empty)
92+
Free.mergeValues(DB(f -> TInt), DB(f -> TInt))
93+
Free.mergeValues(DB(f -> TInt), DB(f -> TInt, g -> TByte))
9494

9595
intercept[TypeError] {
96-
Free.mergeValues(HashMap(f -> TInt), HashMap(f -> TBoolean, g -> TByte))
96+
Free.mergeValues(DB(f -> TInt), DB(f -> TBoolean, g -> TByte))
9797
}
9898
}
9999

@@ -113,15 +113,12 @@ class TypeInferenceTests extends CoreTests {
113113
assertEquals(ex1.capt, Set.empty)
114114
assertEquals(ex1.free, Free.value(x, TInt).withoutValue(x, TInt))
115115

116-
intercept[TypeError] {
117-
Stmt.Val(x,
118-
Stmt.Return(Expr.Literal(true, TBoolean)),
119-
Stmt.Return(Expr.ValueVar(x, TInt))).free.wellformed()
120-
}
121-
122116
// we can even type check open terms:
123-
assertEquals(typecheck(Stmt.Return(Expr.ValueVar(x, TInt))),
124-
Typing(TInt, Set.empty, Free.value(x, TInt)))
117+
val ex2 = Stmt.Return(Expr.ValueVar(x, TInt))
118+
assertEquals(typecheck(ex2),
119+
Typing(TInt, Set.empty, Constraints.empty))
120+
121+
assertEquals(ex2.free, Free.value(x, TInt))
125122

126123
val add: Block.BlockVar = Block.BlockVar(infixPlus, BlockType.Function(Nil, Nil, List(TInt, TInt), Nil, TInt), Set.empty)
127124

@@ -132,8 +129,7 @@ class TypeInferenceTests extends CoreTests {
132129
val result3 = typecheck(ex3)
133130
assertEquals(result3.tpe, TInt)
134131
assertEquals(result3.capt, Set.empty)
135-
assert(result3.free.freeIds == Set(add.id))
136-
result3.free.wellformed()
132+
assert(ex3.free.freeIds == Set(add.id))
137133

138134
// [A](Option[A], A): A
139135
val orElse: Block.BlockVar = Block.BlockVar(f, BlockType.Function(A :: Nil, Nil, List(OptionT(ValueType.Var(A)), ValueType.Var(A)), Nil, ValueType.Var(A)), Set.empty)
@@ -143,26 +139,21 @@ class TypeInferenceTests extends CoreTests {
143139
// swapped arguments
144140
intercept[TypeError] {
145141
val res = typecheck(Expr.PureApp(orElse, TInt :: Nil, Expr.ValueVar(y, TInt) :: Expr.ValueVar(x, OptionT(TInt)) :: Nil))
146-
res.free.wellformed()
142+
res.check()
147143
}
148144
// too few arguments
149145
intercept[TypeError] {
150146
val res = typecheck(Expr.PureApp(orElse, TInt :: Nil, Expr.ValueVar(x, OptionT(TInt)) :: Nil))
151-
res.free.wellformed()
152-
}
153-
// incompatible free variables in arguments
154-
intercept[TypeError] {
155-
val res = typecheck(Expr.PureApp(orElse, TInt :: Nil, Expr.ValueVar(x, OptionT(TInt)) :: Expr.ValueVar(x, TInt) :: Nil))
156-
res.free.wellformed()
147+
res.check()
157148
}
158149

159150
val ex4: Expr.Make = Make(OptionT(TInt), SomeC, List(), List(Literal(42, TInt)))
160151
assertSameType(ex4.tpe, OptionT(TInt))
161-
assertEquals(ex4.free, Free.defer(ex4))
152+
assertEquals(ex4.typing.constraints, Vector(ex4))
162153

163154
val ex5: Expr.Make = Make(OptionT(TInt), NoneC, List(), List())
164155
assertSameType(ex5.tpe, OptionT(TInt))
165-
assertEquals(ex5.free, Free.defer(ex5))
156+
assertEquals(ex5.typing.constraints, Vector(ex5))
166157
}
167158

168159
val ex6 = Stmt.Region(Block.BlockLit(Nil, r :: Nil, Nil, BlockParam(r, TRegion, Set(r)) :: Nil,
@@ -176,6 +167,6 @@ class TypeInferenceTests extends CoreTests {
176167
assertEquals(Type.equals(got, expected), true)
177168

178169
inline def shouldTypeCheckAs(expected: ValueType, expr: Expr)(using DeclarationContext): Unit =
179-
expr.free.wellformed()
170+
expr.typing.check()
180171
assertSameType(expr.tpe, expected)
181172
}

effekt/jvm/src/test/scala/effekt/core/VMTests.scala

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ class VMTests extends munit.FunSuite {
236236

237237
val are_we_fast_yet: Seq[(File, Option[Summary])] = Seq(
238238
examplesDir / "benchmarks" / "are_we_fast_yet" / "bounce.effekt" -> Some(Summary(
239-
staticDispatches = 20202,
239+
staticDispatches = 15202,
240240
dynamicDispatches = 5000,
241241
patternMatches = 0,
242242
branches = 31628,
@@ -592,7 +592,7 @@ class VMTests extends munit.FunSuite {
592592
)),
593593

594594
examplesDir / "benchmarks" / "effect_handlers_bench" / "tree_explore.effekt" -> Some(Summary(
595-
staticDispatches = 2077,
595+
staticDispatches = 2697,
596596
dynamicDispatches = 0,
597597
patternMatches = 2380,
598598
branches = 3167,
@@ -678,15 +678,15 @@ class VMTests extends munit.FunSuite {
678678
dynamicDispatches = 15,
679679
patternMatches = 232,
680680
branches = 405,
681-
pushedFrames = 196,
682-
poppedFrames = 196,
681+
pushedFrames = 199,
682+
poppedFrames = 199,
683683
allocations = 106,
684684
closures = 23,
685685
variableReads = 164,
686686
variableWrites = 51,
687-
resets = 28,
688-
shifts = 0,
689-
resumes = 0
687+
resets = 29,
688+
shifts = 3,
689+
resumes = 3
690690
)),
691691

692692
examplesDir / "casestudies" / "parser.effekt.md" -> Some(Summary(
@@ -722,14 +722,14 @@ class VMTests extends munit.FunSuite {
722722
)),
723723

724724
examplesDir / "casestudies" / "inference.effekt.md" -> Some(Summary(
725-
staticDispatches = 1482058,
726-
dynamicDispatches = 3224986,
727-
patternMatches = 1497945,
725+
staticDispatches = 1482042,
726+
dynamicDispatches = 3224955,
727+
patternMatches = 1497935,
728728
branches = 304507,
729-
pushedFrames = 2643318,
730-
poppedFrames = 2066053,
731-
allocations = 4654131,
732-
closures = 867079,
729+
pushedFrames = 2643286,
730+
poppedFrames = 2066021,
731+
allocations = 4654126,
732+
closures = 867068,
733733
variableReads = 2955965,
734734
variableWrites = 1480868,
735735
resets = 288867,
@@ -738,13 +738,13 @@ class VMTests extends munit.FunSuite {
738738
)),
739739

740740
examplesDir / "pos" / "raytracer.effekt" -> Some(Summary(
741-
staticDispatches = 85740,
741+
staticDispatches = 71065,
742742
dynamicDispatches = 0,
743-
patternMatches = 763548,
743+
patternMatches = 633105,
744744
branches = 65951,
745-
pushedFrames = 43131,
746-
poppedFrames = 43131,
747-
allocations = 70805,
745+
pushedFrames = 31478,
746+
poppedFrames = 31478,
747+
allocations = 56669,
748748
closures = 0,
749749
variableReads = 77886,
750750
variableWrites = 26904,
@@ -1089,13 +1089,13 @@ class VMTests extends munit.FunSuite {
10891089
resumes = 0
10901090
)),
10911091
examplesDir / "benchmarks" / "nofib" / "constraints.effekt" -> Some(Summary(
1092-
staticDispatches = 894047,
1092+
staticDispatches = 903127,
10931093
dynamicDispatches = 60355,
1094-
patternMatches = 1388933,
1094+
patternMatches = 1409731,
10951095
branches = 432810,
1096-
pushedFrames = 797579,
1097-
poppedFrames = 797579,
1098-
allocations = 1003044,
1096+
pushedFrames = 806659,
1097+
poppedFrames = 806659,
1098+
allocations = 1023842,
10991099
closures = 10,
11001100
variableReads = 0,
11011101
variableWrites = 0,

effekt/shared/src/main/scala/effekt/Compiler.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import effekt.symbols.Module
1010
import effekt.typer.{UnboxInference, Typer, Wellformedness}
1111
import effekt.util.messages.{CompilerPanic, FatalPhaseError}
1212
import effekt.util.paths
13+
import effekt.util.debug
1314
import kiama.output.PrettyPrinterTypes.Document
1415
import kiama.util.Source
1516

@@ -302,7 +303,7 @@ trait Compiler[Executable] {
302303

303304
val aggregated = core.ModuleDecl(main.path, Nil, declarations, externs, definitions, exports)
304305

305-
if Context.config.debug() then aggregated.typecheck()
306+
debug { aggregated.typecheck() }
306307

307308
// TODO in the future check for duplicate exports
308309
CoreTransformed(src, tree, mod, aggregated)

effekt/shared/src/main/scala/effekt/core/PatternMatchingCompiler.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ package core
44
import effekt.core.substitutions.Substitution
55

66
import scala.collection.mutable
7-
7+
import effekt.util.DB
88

99
/**
1010
* Pattern Matching Compiler
@@ -263,7 +263,7 @@ object PatternMatchingCompiler {
263263
*/
264264
def normalize(clause: Clause): Clause = clause match {
265265
case Clause(conditions, label, targs, args) =>
266-
val (normalized, substitution) = normalize(Map.empty, conditions, Map.empty)
266+
val (normalized, substitution) = normalize(Map.empty, conditions, DB.empty)
267267
// TODO also substitute types?
268268
Clause(normalized, label, targs, args.map(v => substitution.getOrElse(v.id, v)))
269269
}
@@ -284,10 +284,10 @@ object PatternMatchingCompiler {
284284
def normalize(
285285
patterns: Map[ValueVar, Pattern],
286286
conditions: List[Condition],
287-
substitution: Map[Id, ValueVar]
288-
): (List[Condition], Map[Id, ValueVar]) = {
287+
substitution: DB[ValueVar]
288+
): (List[Condition], DB[ValueVar]) = {
289289

290-
val subst = Substitution(Map.empty, Map.empty, substitution, Map.empty)
290+
val subst = Substitution(DB.empty, DB.empty, substitution, DB.empty)
291291

292292
def prefix(p: Map[ValueVar, Pattern], cs: List[Condition]): List[Condition] =
293293
if p.isEmpty then cs else Condition.Patterns(p) :: cs
@@ -304,7 +304,7 @@ object PatternMatchingCompiler {
304304
case (sc, p: Pattern.Tag) => sc -> p
305305
case (sc, p: Pattern.Literal) => sc -> p
306306
}
307-
normalize(patterns ++ filtered, rest, substitution ++ additionalSubst)
307+
normalize(patterns ++ filtered, rest, substitution ++ DB.from(additionalSubst))
308308

309309
case Condition.Run(binding) :: rest =>
310310
val substituted = core.substitutions.substitute(binding)(using subst)

0 commit comments

Comments
 (0)