Let's say I have some code in my target language like:
fun someFunction(arg1: Int, arg2: String) {
var a = foo()
if a < 10 {
var b = a+1
var a = a+b+2
...
}
The second a
shadows the first one.
In my compiler, I created a Scope
class to track names and their definitions. The scopes roughly correspond to the nodes in the abstract syntax tree (AST). Each scope has a map of name → info, and a pointer to its enclosing scope.
But my problem is that this data structure doesn't work well when I get shadowing like this. For example, in what Scope
do you evaluate a+b+2
? If it's in the function body scope, then it misses the b
, if it's in the if's
scope, then it gets the uninitialized a
, not the outer shadowed one, which I think is what it should use.
Perhaps I have not modeled this correctly. Perhaps it's more accurate to say that each var
declaration creates a new nested Scope
, used by the code that comes after it in a given {...}
block. So the scope tree has more nodes, and they do not so exactly correspond to AST nodes or curly brace blocks.
If I implement this, then many of my Scope
objects would be nothing but a single variable, and looking up a name would be a linear walk up the linked list (stack) of scopes. Maybe that's fine, but I'm wondering if there are some standard data structures used in compilers that are better than this linked list stack approach.
I think I would make Scope
an abstract class and so that some scopes with many names could use an internal hash table. Scopes for a class
could be like this, while scopes created by local var
declarations could have just the one name, and a different implementation of a virtual lookup(name)
method.
var a = a+b+2
core to the language design here? Most block-structured languages have scoping rules that preclude this sort of construction, but yours doesn’t have to; it seems like yourvar
is more like thelet … in …
construct typical in functional languages. The standard data structures are aligned with standard designs and you can borrow from them when that’s what you have, but we should check what it is that’s needed first. $\endgroup${...}
block. Eg, you saylet x = 1;
and on the next linelet x = x+1
and that's a secondx
that will be 2. Other languages I've seen allow you shadow variables, but you need to at least be in a nested block. So, yes, I guess myvar
is like alet ... in ...
but without the rightward drift that happens in some languages I've used. $\endgroup$let a = a + b + 2;
because thelet
binding shadows the outera
for the entire block, and you can't reference a variable before its initialization is complete. $\endgroup$