Skip to content

Commit d2b54b7

Browse files
Fix handling of top-level and builtin names
1 parent ab26c44 commit d2b54b7

File tree

4 files changed

+192
-128
lines changed

4 files changed

+192
-128
lines changed

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

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ class TestRenamer(names: Names = Names(Map.empty)) extends core.Tree.Rewrite {
1717

1818
// list of scopes that map bound symbols to their renamed variants.
1919
private var scopes: List[Map[Id, Id]] = List.empty
20+
// Top-level items in the current module. Collected to check for free variables.
21+
private var toplevelScope: Map[Id, Id] = Map.empty
2022

2123
// Here we track ALL renamings
2224
var renamed: Map[Id, Id] = Map.empty
@@ -39,27 +41,25 @@ class TestRenamer(names: Names = Names(Map.empty)) extends core.Tree.Rewrite {
3941
/** Alias for withBindings(List(id)){...} */
4042
def withBinding[R](id: Id)(f: => R): R = withBindings(List(id))(f)
4143

42-
// free variables cannot be left untouched, because top-level items may be mutually recursive.
43-
// This means that a bound occurrence may precede its binding.
44+
// Top-level items may be mutually recursive. This means that a bound occurrence may precede its binding.
45+
// We use a separate pass to collect all top-level ids, so that we can distinguish them from free variables.
4446
override def id: PartialFunction[core.Id, core.Id] = {
45-
case id => {
47+
case id =>
4648
if (isBuiltin(id)) {
49+
// builtin, do not rename
4750
id
51+
} else if (toplevelScope.contains(id)) {
52+
// id references a top-level item
53+
toplevelScope(id)
4854
} else {
4955
scopes.collectFirst {
56+
// locally bound variable
5057
case bnds if bnds.contains(id) => bnds(id)
5158
}.getOrElse {
52-
scopes match {
53-
case Nil => id
54-
case _ =>
55-
val freshId = freshIdFor(id)
56-
val updatedLast = scopes.last + (id -> freshId)
57-
scopes = scopes.init :+ updatedLast
58-
freshId
59-
}
59+
// free variable, do not rename
60+
id
6061
}
6162
}
62-
}
6363
}
6464

6565
override def stmt: PartialFunction[Stmt, Stmt] = {
@@ -229,13 +229,31 @@ class TestRenamer(names: Names = Names(Map.empty)) extends core.Tree.Rewrite {
229229

230230
def apply(m: core.ModuleDecl): core.ModuleDecl =
231231
suffix = 0
232+
scopes = List.empty
232233
m match {
233234
case core.ModuleDecl(path, includes, declarations, externs, definitions, exports) =>
235+
// Collect toplevel ids, so that we can distinguish a top-level definition bound later
236+
// from a free variable when deciding on whether to freshen an id or not.
237+
toplevelScope = collectToplevelIds(m).map(id => id -> freshIdFor(id)).toMap
234238
core.ModuleDecl(path, includes, declarations map rewrite, externs map rewrite, definitions map rewrite, exports map rewrite)
235239
}
236240

237241
def apply(s: Stmt): Stmt = {
238242
suffix = 0
243+
toplevelScope = Map.empty
244+
scopes = List.empty
239245
rewrite(s)
240246
}
247+
248+
def collectToplevelIds(m: core.ModuleDecl): Iterable[Id] =
249+
m match {
250+
case core.ModuleDecl (path, includes, declarations, externs, definitions, exports) =>
251+
declarations.flatMap {
252+
case Declaration.Data(id, tparams, constructors) => constructors.map(_.id) :+ id
253+
case Interface(id, tparams, properties) => properties.map(_.id) :+ id
254+
} ++ definitions.map(_.id) ++ externs.flatMap {
255+
case Extern.Def(id, _, _, _, _, _, _, _) => Some(id)
256+
case Extern.Include(_, _) => None
257+
}
258+
}
241259
}

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

Lines changed: 130 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -14,166 +14,229 @@ class TestRenamerTests extends CoreTests {
1414
val pExpected = parse(renamed, "expected", names)
1515
val renamer = new TestRenamer(names)
1616
val obtained = renamer(pInput)
17-
shouldBeEqual(obtained, pExpected, clue)
17+
val prettyInput = effekt.core.PrettyPrinter.format(pInput).layout
18+
assertEquals(prettyInput, renamed)
19+
assertEquals(effekt.util.PrettyPrinter.format(pInput).layout, effekt.util.PrettyPrinter.format(pExpected).layout)
20+
assertAlphaEquivalent(obtained, pExpected, clue)
1821
}
1922

2023
test("No bound local variables"){
21-
val code =
24+
val input =
2225
"""module main
2326
|
24-
|def foo = { () =>
25-
| return (bar: (Int) => Int @ {})(baz:Int)
27+
|def foo$1 = { () =>
28+
| return (bar$2: (Int) => Int @ {})(baz$3:Int)
2629
|}
2730
|""".stripMargin
28-
assertRenamedTo(code, code)
31+
32+
val expected =
33+
"""module main
34+
|
35+
|
36+
|
37+
|
38+
|
39+
|
40+
|
41+
|def foo$1() = {
42+
| return (bar$2: (Int) => Int @ {})(baz$3: Int)
43+
|}""".stripMargin
44+
assertRenamedTo(input, expected)
2945
}
3046

3147
test("val binding"){
3248
val input =
3349
"""module main
3450
|
35-
|def foo = { () =>
36-
| val x = (foo:(Int)=>Int@{})(4) ;
37-
| return x:Int
51+
|def foo$1 = { () =>
52+
| val x$2 = (foo$1:(Int)=>Int@{})(4) ;
53+
| return x$2:Int
3854
|}
3955
|""".stripMargin
4056
val expected =
4157
"""module main
4258
|
43-
|def foo = { () =>
44-
| val $1 = (foo:(Int)=>Int@{})(4);
45-
| return $1:Int
46-
|}
47-
|""".stripMargin
59+
|
60+
|
61+
|
62+
|
63+
|
64+
|
65+
|def foo$1() = {
66+
| val x$2: Int = {
67+
| foo$1: (Int) => Int @ {}(4)
68+
| };
69+
| return x$2: Int
70+
|}""".stripMargin
4871
assertRenamedTo(input, expected)
4972
}
5073

5174
test("var binding"){
5275
val input =
5376
"""module main
5477
|
55-
|def foo = { () =>
56-
| var x @ global = (foo:(Int)=>Int@{})(4) ;
57-
| return x:Int
78+
|def foo$1 = { () =>
79+
| var x$2 @ global = (foo$1:(Int)=>Int@{})(4) ;
80+
| return x$2:Int
5881
|}
5982
|""".stripMargin
6083
val expected =
6184
"""module main
6285
|
63-
|def foo = { () =>
64-
| var $1 @ global = (foo:(Int)=>Int@{})(4);
65-
| return $1:Int
66-
|}
67-
|""".stripMargin
86+
|
87+
|
88+
|
89+
|
90+
|
91+
|
92+
|def foo$1() = {
93+
| var x$2 @ global = (foo$1: (Int) => Int @ {})(4);
94+
| return x$2: Int
95+
|}""".stripMargin
6896
assertRenamedTo(input, expected)
6997
}
7098

7199
test("function (value) parameters"){
72100
val input =
73101
"""module main
74102
|
75-
|def foo = { (x:Int) =>
76-
| return x:Int
103+
|def foo$1 = { (x$2:Int) =>
104+
| return x$2:Int
77105
|}
78106
|""".stripMargin
79107
val expected =
80108
"""module main
81109
|
82-
|def foo = { ($1:Int) =>
83-
| return $1:Int
84-
|}
85-
|""".stripMargin
110+
|
111+
|
112+
|
113+
|
114+
|
115+
|
116+
|def foo$1(x$2: Int) = {
117+
| return x$2: Int
118+
|}""".stripMargin
86119
assertRenamedTo(input, expected)
87120
}
88121

89122
test("match clauses"){
90123
val input =
91124
"""module main
92125
|
93-
|type Data { X(a:Int, b:Int) }
94-
|def foo = { () =>
126+
|type Data$1 { X$2(a$3:Int, b$3:Int) }
127+
|def foo$4 = { () =>
95128
| 12 match {
96-
| X : {(aa:Int, bb:Int) => return aa:Int }
129+
| X$2 : {(aa$5:Int, bb$6:Int) => return aa$5:Int }
97130
| }
98131
|}
99132
|""".stripMargin
100133
val expected =
101134
"""module main
102135
|
103-
|type Data { X(a:Int, b:Int) }
104-
|def foo = { () =>
136+
|
137+
|
138+
|type Data$1 {
139+
| X$2(a$3: Int, b$3: Int)
140+
|}
141+
|
142+
|
143+
|
144+
|def foo$4() = {
105145
| 12 match {
106-
| X : {($1:Int, $2:Int) => return $1:Int }
146+
| X$2 : { (aa$5: Int, bb$6: Int) =>
147+
| return aa$5: Int
148+
| }
107149
| }
108-
|}
109-
|""".stripMargin
150+
|}""".stripMargin
110151
assertRenamedTo(input, expected)
111152
}
112153

113154
test("type parameters"){
114155
val input =
115156
"""module main
116157
|
117-
|def foo = { ['A](a: A) =>
118-
| return a:Identity[A]
158+
|def foo$1 = { ['A$2](a$3: A$2) =>
159+
| return a$3: Identity$4[A$2]
119160
|}
120161
|""".stripMargin
121162
val expected =
122163
"""module main
123164
|
124-
|def foo = { ['$1]($2: $1) =>
125-
| return $2:Identity[$1]
126-
|}
127-
|""".stripMargin
165+
|
166+
|
167+
|
168+
|
169+
|
170+
|
171+
|def foo$1['A$2](a$3: A$2) = {
172+
| return a$3: Identity$4[A$2]
173+
|}""".stripMargin
128174
assertRenamedTo(input, expected)
129175
}
130176

131177
test("pseudo recursive"){
132178
val input =
133179
""" module main
134180
|
135-
| def bar = { () => return 1 }
136-
| def main = { () =>
137-
| def foo = { () => (bar : () => Unit @ {})() }
138-
| def bar = { () => return 2 }
139-
| (foo : () => Unit @ {})()
181+
| def bar$1 = { () => return 1 }
182+
| def main$2 = { () =>
183+
| def foo$3 = { () => (bar$1 : () => Unit @ {})() }
184+
| def bar$4 = { () => return 2 }
185+
| (foo$3 : () => Unit @ {})()
140186
| }
141187
|""".stripMargin
142188

143189
val expected =
144-
""" module main
190+
"""module main
145191
|
146-
| def bar = { () => return 1 }
147-
| def main = { () =>
148-
| def $1 = { () => (bar : () => Unit @ {})() }
149-
| def $2 = { () => return 2 }
150-
| ($1 : () => Unit @ {})()
151-
| }
152-
|""".stripMargin
192+
|
193+
|
194+
|
195+
|
196+
|
197+
|
198+
|def bar$1() = {
199+
| return 1
200+
|}
201+
|def main$2() = {
202+
| def foo$3() = {
203+
| bar$1: () => Unit @ {}()
204+
| }
205+
| def bar$4() = {
206+
| return 2
207+
| }
208+
| foo$3: () => Unit @ {}()
209+
|}""".stripMargin
153210

154211
assertRenamedTo(input, expected)
155212
}
213+
156214
test("shadowing let bindings"){
157-
val input =
215+
val code =
158216
""" module main
159217
|
160-
| def main = { () =>
161-
| let x = 1
162-
| let x = 2
163-
| return x:Int
218+
| def main$1 = { () =>
219+
| let x$2 = 1
220+
| let x$3 = 2
221+
| return x$3:Int
164222
| }
165223
|""".stripMargin
166224

167225
val expected =
168-
""" module main
226+
"""module main
169227
|
170-
| def main = { () =>
171-
| let $1 = 1
172-
| let $2 = 2
173-
| return $2:Int
174-
| }
175-
|""".stripMargin
228+
|
229+
|
230+
|
231+
|
232+
|
233+
|
234+
|def main$1() = {
235+
| let x$2 = 1
236+
| let x$3 = 2
237+
| return x$3: Int
238+
|}""".stripMargin
176239

177-
assertRenamedTo(input, expected)
240+
assertRenamedTo(code, expected)
178241
}
179242
}

0 commit comments

Comments
 (0)