Skip to content

Commit bbe420e

Browse files
authored
Do not contify under reset (#872)
This PR fixes #861 by not contifying under reset. It also adds a pretty printer as a drive-by for easier debugging (the one from #843).
1 parent dfe98b3 commit bbe420e

File tree

8 files changed

+253
-8
lines changed

8 files changed

+253
-8
lines changed

effekt/shared/src/main/scala/effekt/core/optimizer/RemoveTailResumptions.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ object RemoveTailResumptions {
3737
case Stmt.Var(ref, init, capture, body) => tailResumptive(k, body) && !freeInExpr(init)
3838
case Stmt.Get(ref, annotatedCapt, tpe, id, body) => tailResumptive(k, body)
3939
case Stmt.Put(ref, annotatedCapt, value, body) => tailResumptive(k, body) && !freeInExpr(value)
40-
case Stmt.Reset(BlockLit(tparams, cparams, vparams, bparams, body)) => tailResumptive(k, body) // is this correct?
40+
case Stmt.Reset(BlockLit(tparams, cparams, vparams, bparams, body)) => false
4141
case Stmt.Shift(prompt, body) => stmt.tpe == Type.TBottom
4242
case Stmt.Resume(k2, body) => k2.id == k // what if k is free in body?
4343
case Stmt.Hole() => true
@@ -55,7 +55,7 @@ object RemoveTailResumptions {
5555
Stmt.Region(removeTailResumption(k, body))
5656
case Stmt.Alloc(id, init, region, body) => Stmt.Alloc(id, init, region, removeTailResumption(k, body))
5757
case Stmt.Var(id, init, capture, body) => Stmt.Var(id, init, capture, removeTailResumption(k, body))
58-
case Stmt.Reset(body) => Stmt.Reset(removeTailResumption(k, body))
58+
case Stmt.Reset(body) => stmt
5959
case Stmt.Resume(k2, body) if k2.id == k => body
6060

6161
case Stmt.Resume(k, body) => stmt

effekt/shared/src/main/scala/effekt/cps/Contify.scala

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,13 @@ object Contify {
8585
given Substitution = Substitution(conts = Map(k -> ContVar(k2)))
8686
Stmt.LetCont(id, ContLam(vparams, ks, substitute(rewrittenBody)), contify(id, rewrittenRest))
8787

88-
case Some(cont: ContLam) if returnsUnique && !isRecursive =>
89-
val k2 = Id("k")
90-
given Substitution = Substitution(conts = Map(k -> ContVar(k2)))
91-
LetCont(k2, cont,
92-
Stmt.LetCont(id, ContLam(vparams, ks, substitute(rewrittenBody)),
93-
contify(id, rewrittenRest)))
88+
// leads to `k_6 is not defined` when disabling optimizations on issue861.effekt
89+
// case Some(cont: ContLam) if returnsUnique && !isRecursive =>
90+
// val k2 = Id("k")
91+
// given Substitution = Substitution(conts = Map(k -> ContVar(k2)))
92+
// LetCont(k2, cont,
93+
// Stmt.LetCont(id, ContLam(vparams, ks, substitute(rewrittenBody)),
94+
// contify(id, rewrittenRest)))
9495
case _ =>
9596
Stmt.LetDef(id, BlockLit(vparams, Nil, ks, k, rewrittenBody), rewrittenRest)
9697
}
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
package effekt
2+
package cps
3+
4+
import core.Id
5+
6+
import kiama.output.ParenPrettyPrinter
7+
import effekt.source.FeatureFlag
8+
9+
object PrettyPrinter extends ParenPrettyPrinter {
10+
11+
import kiama.output.PrettyPrinterTypes.Document
12+
13+
override val defaultIndent = 2
14+
15+
def format(t: ModuleDecl): Document =
16+
pretty(toDoc(t), 4)
17+
18+
def format(defs: List[ToplevelDefinition]): String =
19+
pretty(toDoc(defs), 60).layout
20+
21+
def format(s: Stmt): String =
22+
pretty(toDoc(s), 60).layout
23+
24+
def format(t: Block): String =
25+
pretty(toDoc(t), 60).layout
26+
27+
def format(e: Expr): String =
28+
pretty(toDoc(e), 60).layout
29+
30+
val show: PartialFunction[Any, String] = {
31+
case m: ModuleDecl => format(m).layout
32+
case d: ToplevelDefinition => format(List(d))
33+
case s: Stmt => format(s)
34+
case b: Block => format(b)
35+
case e: Expr => format(e)
36+
case x: Id => x.show
37+
}
38+
39+
val emptyline: Doc = line <> line
40+
41+
def toDoc(m: ModuleDecl): Doc = {
42+
"module" <+> m.path <> emptyline <>
43+
toDoc(m.definitions)
44+
}
45+
46+
def toDoc(definitions: List[ToplevelDefinition]): Doc =
47+
vsep(definitions map toDoc, semi)
48+
49+
def toDoc(d: ToplevelDefinition): Doc = d match {
50+
case ToplevelDefinition.Def(id, block) =>
51+
"def" <+> toDoc(id) <+> "=" <+> toDoc(block)
52+
case ToplevelDefinition.Val(id, ks, k, binding) =>
53+
"let" <+> toDoc(id) <+> "|" <+> toDoc(ks) <> "," <+> toDoc(k) <+> "=" <+> toDoc(binding)
54+
case ToplevelDefinition.Let(id, binding) =>
55+
"let" <+> toDoc(id) <+> "=" <+> toDoc(binding)
56+
}
57+
58+
def toDoc(e: Expr): Doc = e match {
59+
case DirectApp(id, vargs, bargs) =>
60+
toDoc(id) <> argsToDoc(vargs, bargs)
61+
case Pure.ValueVar(id) => toDoc(id)
62+
case Pure.Literal(()) => "()"
63+
case Pure.Literal(s: String) => "\"" + s + "\""
64+
case Pure.Literal(value) => value.toString
65+
case Pure.PureApp(id, vargs) => toDoc(id) <> argsToDoc(vargs, Nil)
66+
case Pure.Make(data, tag, vargs) => "make" <+> toDoc(data.name) <+> toDoc(tag) <> argsToDoc(vargs, Nil)
67+
case Pure.Box(b) => parens("box" <+> toDoc(b))
68+
}
69+
70+
def toDoc(b: Block): Doc = b match {
71+
case Block.BlockVar(id) => toDoc(id)
72+
case b: Block.BlockLit => toDoc(b)
73+
case Block.Unbox(e) => parens("unbox" <+> toDoc(e))
74+
case Block.New(handler) => "new" <+> toDoc(handler)
75+
}
76+
77+
def toDoc(b: Block.BlockLit): Doc = b match {
78+
case Block.BlockLit(vps, bps, ks, k, body) =>
79+
val params = if (vps.isEmpty && bps.isEmpty) emptyDoc else
80+
parens(hsep(vps.map(toDoc), comma)) <+> hsep(bps.map(toDoc))
81+
braces {
82+
space <> params <+> "|" <+> toDoc(ks) <> "," <+> toDoc(k) <+> "=>" <+>
83+
nest(line <> toDoc(body)) <> line
84+
}
85+
}
86+
87+
def toDoc(s: Stmt): Doc = s match {
88+
case Stmt.Jump(k, vargs, ks) =>
89+
"jump" <+> toDoc(k) <> argsToDoc(vargs, Nil) <+> "@" <+> toDoc(ks)
90+
91+
case Stmt.App(callee, vargs, bargs, ks, k) =>
92+
toDoc(callee) <> argsToDoc(vargs, bargs) <+> "@" <+> toDoc(ks) <> "," <+> toDoc(k)
93+
94+
case Stmt.Invoke(callee, method, vargs, bargs, ks, k) =>
95+
toDoc(callee) <> "." <> method.name.toString <> argsToDoc(vargs, bargs) <+> "@" <+> toDoc(ks) <> "," <+> toDoc(k)
96+
97+
case Stmt.If(cond, thn, els) =>
98+
"if" <+> parens(toDoc(cond)) <+> block(toDoc(thn)) <+> "else" <+> block(toDoc(els))
99+
100+
case Stmt.Match(scrutinee, clauses, default) =>
101+
val cs = braces(nest(line <> vsep(clauses map { case (p, b) =>
102+
"case" <+> toDoc(p) <+> toDoc(b)
103+
})) <> line)
104+
val d = default.map { body =>
105+
space <> "else" <+> block(toDoc(body))
106+
}.getOrElse(emptyDoc)
107+
toDoc(scrutinee) <+> "match" <+> cs <> d
108+
109+
case Stmt.LetDef(id, binding, body) =>
110+
"def" <+> toDoc(id) <+> "=" <+> toDoc(binding) <> line <>
111+
toDoc(body)
112+
113+
case Stmt.LetExpr(id, binding, body) =>
114+
"let" <+> toDoc(id) <+> "=" <+> toDoc(binding) <> line <>
115+
toDoc(body)
116+
117+
case Stmt.LetCont(id, binding, body) =>
118+
"let" <+> toDoc(id) <+> "=" <+> toDoc(binding) <> line <>
119+
toDoc(body)
120+
121+
case Stmt.Region(id, ks, body) =>
122+
"region" <+> toDoc(id) <+> "@" <+> toDoc(ks) <+> block(toDoc(body))
123+
124+
case Stmt.Alloc(id, init, region, body) =>
125+
"var" <+> toDoc(id) <+> "in" <+> toDoc(region) <+> "=" <+> toDoc(init) <> ";" <> line <>
126+
toDoc(body)
127+
128+
case Stmt.Dealloc(ref, body) =>
129+
"dealloc" <> parens(toDoc(ref)) <> ";" <> line <>
130+
toDoc(body)
131+
132+
case Stmt.Var(id, init, ks, body) =>
133+
"var" <+> toDoc(id) <+> "=" <+> toDoc(init) <+> "@" <+> toDoc(ks) <> ";" <> line <>
134+
toDoc(body)
135+
136+
case Stmt.Get(ref, id, body) =>
137+
"let" <+> toDoc(id) <+> "=" <+> "!" <> toDoc(ref) <> ";" <> line <>
138+
toDoc(body)
139+
140+
case Stmt.Put(ref, value, body) =>
141+
toDoc(ref) <+> ":=" <+> toDoc(value) <> ";" <> line <>
142+
toDoc(body)
143+
144+
case Stmt.Reset(prog, ks, k) =>
145+
"reset" <+> toDoc(prog) <+> "@" <+> toDoc(ks) <> "," <+> toDoc(k)
146+
147+
case Stmt.Shift(prompt, body, ks, k) =>
148+
"shift" <> parens(toDoc(prompt)) <+> toDoc(body) <+> "@" <+> toDoc(ks) <> "," <+> toDoc(k)
149+
150+
case Stmt.Resume(r, body, ks, k) =>
151+
"resume" <> parens(toDoc(r)) <+> toDoc(body) <+> "@" <+> toDoc(ks) <> "," <+> toDoc(k)
152+
153+
case Stmt.Hole() =>
154+
"<>"
155+
}
156+
157+
def toDoc(clause: Clause): Doc = clause match {
158+
case Clause(vparams, body) =>
159+
parens(hsep(vparams.map(toDoc), comma)) <+> "=>" <+> block(toDoc(body))
160+
}
161+
162+
def toDoc(impl: Implementation): Doc = {
163+
val handlerName = toDoc(impl.interface.name)
164+
val clauses = impl.operations.map { op =>
165+
"def" <+> toDoc(op.name) <> paramsToDoc(op.vparams, op.bparams, op.ks, op.k) <+> "=" <+>
166+
nested(toDoc(op.body))
167+
}
168+
handlerName <+> block(vsep(clauses))
169+
}
170+
171+
def toDoc(k: Cont): Doc = k match {
172+
case Cont.ContVar(id) => toDoc(id)
173+
case Cont.ContLam(results, ks, body) =>
174+
braces {
175+
space <> parens(hsep(results.map(toDoc), comma)) <+> "|" <+> toDoc(ks) <+> "=>" <+>
176+
nest(line <> toDoc(body)) <> line
177+
}
178+
case Cont.Abort => "abort"
179+
}
180+
181+
def toDoc(ks: MetaCont): Doc = toDoc(ks.id)
182+
183+
def toDoc(s: symbols.Symbol): Doc = s.show
184+
185+
def argsToDoc(vargs: List[Pure], bargs: List[Block]): Doc = {
186+
val vargsDoc = if (vargs.isEmpty && !bargs.isEmpty) emptyDoc else parens(vargs.map(toDoc))
187+
val bargsDoc = if (bargs.isEmpty) emptyDoc else hcat(bargs.map { b => braces(toDoc(b)) })
188+
vargsDoc <> bargsDoc
189+
}
190+
191+
def paramsToDoc(vps: List[Id], bps: List[Id], ks: Id, k: Id): Doc = {
192+
val vpsDoc = if (vps.isEmpty && !bps.isEmpty) emptyDoc else parens(vps.map(toDoc))
193+
val bpsDoc = if (bps.isEmpty) emptyDoc else hcat(bps.map(toDoc))
194+
vpsDoc <> bpsDoc <+> "|" <+> toDoc(ks) <> "," <+> toDoc(k)
195+
}
196+
197+
def nested(content: Doc): Doc = group(nest(line <> content))
198+
199+
def parens(docs: List[Doc]): Doc = parens(hsep(docs, comma))
200+
201+
def braces(docs: List[Doc]): Doc = braces(hsep(docs, semi))
202+
203+
def block(content: Doc): Doc = braces(nest(line <> content) <> line)
204+
205+
def block(docs: List[Doc]): Doc = block(vsep(docs, line))
206+
}

effekt/shared/src/main/scala/effekt/util/Debug.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ val show: PartialFunction[Any, String] =
1616
TypePrinter.show orElse
1717
core.PrettyPrinter.show orElse
1818
generator.js.PrettyPrinter.show orElse
19+
cps.PrettyPrinter.show orElse
1920
showGeneric
2021

2122
inline def debug[A](inline value: A): A =

examples/pos/issue842.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
n

examples/pos/issue842.effekt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
type Answer { Yes(); No() }
2+
3+
def println(ans: Answer): Unit = ans match {
4+
case Yes() => println("y")
5+
case No() => println("n")
6+
}
7+
8+
def join(left: Answer, right: Answer): Answer = (left, right) match {
9+
case (Yes(), Yes()) => Yes()
10+
case (No() , _) => No()
11+
case (_ , No() ) => No()
12+
}
13+
14+
def main() = println(join(Yes(), No()))

examples/pos/issue861.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
true true
2+
true false
3+
false true
4+
false false

examples/pos/issue861.effekt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
effect flip(): Bool
2+
effect choice(): Bool
3+
4+
// this is a reproduction for #861
5+
def main() = {
6+
try {
7+
val x = do choice()
8+
val y = do choice()
9+
println(x.show ++ " " ++ y.show)
10+
} with choice {
11+
try {
12+
resume(do flip())
13+
} with flip {
14+
resume(true)
15+
resume(false)
16+
}
17+
}
18+
}

0 commit comments

Comments
 (0)