Skip to content

Commit a938ee4

Browse files
committed
Add test to simulate arena allocation of contexts in dotc
1 parent e1449b6 commit a938ee4

File tree

1 file changed

+170
-0
lines changed

1 file changed

+170
-0
lines changed
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
2+
// A mini typechecker to experiment with arena allocated contexts
3+
import compiletime.uninitialized
4+
import annotation.{experimental, tailrec, constructorOnly}
5+
import collection.mutable
6+
7+
case class Symbol(name: String) extends caps.Pure:
8+
private var myInfo: Type = uninitialized
9+
def infoOrCompleter: Type = myInfo
10+
def info: Type =
11+
infoOrCompleter match
12+
case completer: LazyType =>
13+
completer.complete()
14+
info
15+
case tp =>
16+
tp
17+
def info_=(tp: Type) = myInfo = tp
18+
def exists: Boolean = true
19+
def orElse(alt: => Symbol): Symbol = this
20+
21+
object NoSymbol extends Symbol(""):
22+
override def infoOrCompleter = NoType
23+
override def exists: Boolean = false
24+
override def orElse(alt: => Symbol): Symbol = alt
25+
26+
abstract class Type extends caps.Pure:
27+
def exists = true
28+
def show: String
29+
class IntType()(using @constructorOnly c: Context) extends Type:
30+
def show = "Int"
31+
class StringType()(using @constructorOnly c: Context) extends Type:
32+
def show = "String"
33+
object NoType extends Type:
34+
override def exists = false
35+
def show = "<none>"
36+
37+
abstract class LazyType(using DetachedContext) extends Type:
38+
def complete(): Unit = doComplete()
39+
def doComplete()(using Context): Unit
40+
def show = "?"
41+
42+
enum Tree:
43+
case Let(name: String, rhs: Tree, in: Tree)
44+
case Ref(name: String)
45+
case Add(x: Tree, y: Tree)
46+
case Length(x: Tree)
47+
case Lit(value: Any)
48+
49+
class Scope:
50+
private val elems = mutable.Map[String, Symbol]()
51+
def enter(sym: Symbol)(using Context): Unit =
52+
if elems.contains(sym.name) then
53+
report.error(s"duplicate definition: ${sym.name}")
54+
elems(sym.name) = sym
55+
def lookup(name: String): Symbol =
56+
elems.getOrElse(name, NoSymbol)
57+
58+
object EmptyScope extends Scope
59+
60+
object report:
61+
def error(msg: -> String)(using Context) =
62+
ctx.errorCount += 1
63+
println(s"ERROR: $msg")
64+
65+
abstract class Ctx:
66+
def outer: Context
67+
def owner: Symbol
68+
def scope: Scope
69+
var errorCount: Int = 0
70+
def detached: DetachedContext
71+
72+
type Context = {*} Ctx
73+
74+
abstract class DetachedContext extends Ctx:
75+
def outer: DetachedContext
76+
77+
class FreshCtx(val level: Int) extends DetachedContext:
78+
var outer: FreshCtx = uninitialized
79+
var owner: Symbol = uninitialized
80+
var scope: Scope = uninitialized
81+
def initFrom(other: Context): this.type =
82+
outer = other.asInstanceOf[FreshCtx]
83+
owner = other.owner
84+
scope = other.scope
85+
this
86+
def detached: DetachedContext =
87+
var c = this
88+
while c.level >= 0 && (ctxStack(c.level) eq c) do
89+
ctxStack(c.level) = null
90+
c = c.outer
91+
this
92+
93+
object NoContext extends FreshCtx(-1):
94+
owner = NoSymbol
95+
scope = EmptyScope
96+
97+
type FreshContext = {*} FreshCtx
98+
99+
def ctx(using c: Context): {c} Ctx = c
100+
101+
var ctxStack: Array[FreshContext] = new Array(10)
102+
var curLevel = 0
103+
104+
def inFreshContext[T](op: FreshContext ?-> T)(using Context): T =
105+
if curLevel == ctxStack.length then
106+
val prev = ctxStack
107+
ctxStack = new Array[FreshContext](curLevel * 2)
108+
Array.copy(prev, 0, ctxStack, 0, prev.length)
109+
if ctxStack(curLevel) == null then
110+
ctxStack(curLevel) = FreshCtx(curLevel)
111+
val result = ctxStack(curLevel).initFrom(ctx)
112+
curLevel += 1
113+
try op(using result)
114+
finally curLevel -= 1
115+
116+
def withOwner[T](owner: Symbol)(op: Context ?-> T)(using Context): T =
117+
val prev = ctx
118+
inFreshContext { c ?=>
119+
c.owner = owner
120+
op
121+
}
122+
123+
def withScope[T](scope: Scope)(op: Context ?-> T)(using Context): T =
124+
val prev = ctx
125+
inFreshContext { c ?=>
126+
c.scope = scope
127+
op
128+
}
129+
130+
def typed(tree: Tree, expected: Type = NoType)(using Context): Type =
131+
val tp = typedUnadapted(tree, expected)
132+
if expected.exists && tp != expected then
133+
report.error(
134+
s"""Type error
135+
|found : $tp
136+
|expected: $expected
137+
|for : $tree""".stripMargin)
138+
tp
139+
140+
import Tree.*
141+
def typedUnadapted(tree: Tree, expected: Type = NoType)(using ctx: Context): Type = tree match
142+
case Let(name, rhs, in) =>
143+
val sym = Symbol(name)
144+
val dctx = ctx.detached
145+
sym.info = new LazyType(using dctx):
146+
override def doComplete()(using Context) =
147+
sym.info = withOwner(sym)(withScope(new Scope())(typed(rhs)))
148+
ctx.scope.enter(sym)
149+
try typed(in, expected)
150+
finally sym.info
151+
case Ref(name: String) =>
152+
def findIn(c: Context): Symbol =
153+
val sym = c.scope.lookup(name)
154+
if sym.exists || (c eq NoContext) then sym
155+
else findIn(c.outer)
156+
findIn(ctx).info
157+
case Add(x: Tree, y: Tree) =>
158+
typed(x, IntType())
159+
typed(y, IntType())
160+
IntType()
161+
case Length(x: Tree) =>
162+
typed(x, StringType())
163+
IntType()
164+
case Lit(value: Any) =>
165+
value match
166+
case value: Int => IntType()
167+
case value: String => StringType()
168+
case _ =>
169+
report.error(s"Int or String literzal expected by $value found")
170+
NoType

0 commit comments

Comments
 (0)