3. Static Name Resolution
module module
def import def def defA
A
B
6 y funf
x +
x y
x +
3 s
5s
module A {
def s = 5
}
module B {
import A
def x = 6
def y = 3 + s
def f =
fun x { x + y }
}
4. Static Name Resolution
module module
def import def def defA
A
B
6 y funf
x +
x y
x +
3 s
5s
module A {
def s = 5
}
module B {
import A
def x = 6
def y = 3 + s
def f =
fun x { x + y }
}
5. Static Name Resolution
module module
def import def def defA
A
B
6 y funf
x +
x y
x +
3 s
5s
module A {
def s = 5
}
module B {
import A
def x = 6
def y = 3 + s
def f =
fun x { x + y }
}
6. Static Name Resolution
module module
def import def def defA
A
B
6 y funf
x +
x y
x +
3 s
5s
module A {
def s = 5
}
module B {
import A
def x = 6
def y = 3 + s
def f =
fun x { x + y }
}
7. Static Name Resolution
module module
def import def def defA
A
B
6 y funf
x +
x y
x +
3 s
5s
module A {
def s = 5
}
module B {
import A
def x = 6
def y = 3 + s
def f =
fun x { x + y }
}
8. Based On …
• Declarative Name Binding and Scope Rules
- NaBL name binding language
- Gabriël D. P. Konat, Lennart C. L. Kats, Guido Wachsmuth, Eelco Visser
- SLE 2012
• A Language Designer's Workbench
- A one-stop-shop for implementation and verification of language designs
- Eelco Visser, Guido Wachsmuth, Andrew P. Tolmach, Pierre Neron, Vlad A. Vergu,
Augusto Passalaqua, Gabriël D. P. Konat
- Onward 2014
• A Theory of Name Resolution
- Pierre Néron, Andrew Tolmach, Eelco Visser, Guido Wachsmuth
- ESOP 2015
• A Constraint Language for Static Semantic Analysis based on Scope
Graphs
- Hendrik van Antwerpen, Pierre Neron, Andrew P. Tolmach, Eelco Visser, Guido
Wachsmuth
- PEPM 2016
9. Detour:
Declare Your Syntax
(an analogy for formalizing declarative name binding)
Pure and declarative syntax definition: paradise lost and regained
Lennart C. L. Kats, Eelco Visser, Guido Wachsmuth
Onward 2010
10. Language = Set of Sentences
fun (x : Int) { x + 1 }
Text is a convenient interface for writing and reading programs
11. Language = Set of Trees
Fun
AddArgDecl
VarRefId Int
“1”
VarDeclId
“x”
TXINT
“x”
Tree is a convenient interface for transforming programs
12. Tree Transformation
Tree is a convenient interface for transforming programs
Semantic
transform
translate
eval
analyze
refactor
type check
Syntactic
coloring
outline view
completion
13. Language = Sentences and Trees
parse
format
different representations convenient for different purposes
14. SDF3 defines Trees and Sentences
parse(s) = t where format(t) == s (modulo layout)
Expr.Int = INT
Expr.Add = <<Expr> + <Expr>>
Expr.Mul = <<Expr> * <Expr>>
trees
(structure)
parse
(text to tree)
format
(tree to text)
+ =>
19. Priority and Associativity
context-free syntax
Expr.Int = INT
Expr.Add = <<Expr> + <Expr>> {left}
Expr.Mul = <<Expr> * <Expr>> {left}
context-free priorities
Expr.Mul > Expr.Add
Recent improvement: safe disambiguation of operator precedence
Afroozeh et al. (SLE 2013, Onward 2015)
Add
VarRef VarRef
“y”“x”
Mul
Int
“3”
Add
VarRef
VarRef
“y”
“x”
Mul
Int
“3”
3 * x + y
20. Declarative Syntax Definition
• Representation: (Abstract Syntax) Trees
- Standardized representation for structure of programs
- Basis for syntactic and semantic operations
• Formalism: Syntax Definition
- Productions + Constructors + Templates + Disambiguation
- Language-specific rules: structure of each language construct
• Language-Independent Interpretation
- Well-formedness of abstract syntax trees
‣ provides declarative correctness criterion for parsing
- Parsing algorithm
‣ No need to understand parsing algorithm
‣ Debugging in terms of representation
- Formatting based on layout hints in grammar
21. Declare Your Names
A Theory of Name Resolution
Pierre Néron, Andrew Tolmach, Eelco Visser, Guido Wachsmuth
ESOP 2015
22. Language = Set of Trees
Fun
AddArgDecl
VarRefId Int
“1”
VarDeclId
“x”
TXINT
“x”
Tree is a convenient interface for transforming programs
23. Language = Set of Graphs
Fun
AddArgDecl
VarRefId Int
“1”
VarDeclId TXINT
Edges from references to declarations
25. Name Resolution is Pervasive
• Used in many different language artifacts
- compiler
- interpreter
- semantics
- IDE
- refactoring
• Binding rules encoded in many different and ad-hoc ways
- symbol tables
- environments
- substitutions
• No standard approach to formalization
- there is no BNF for name binding
• No reuse of binding rules between artifacts
- how do we know substitution respects binding rules?
26. NaBL Name Binding Language
binding rules // variables
Param(t, x) :
defines Variable x of type t
Let(bs, e) :
scopes Variable
Bind(t, x, e) :
defines Variable x of type t
Var(x) :
refers to Variable x
Declarative Name Binding and Scope Rules
Gabriël D. P. Konat, Lennart C. L. Kats, Guido Wachsmuth, Eelco Visser
SLE 2012
Declarative specification
Abstracts from implementation
Incremental name resolution
But:
How to explain it to Coq?
What is the semantics of NaBL?
27. NaBL Name Binding Language
Declarative Name Binding and Scope Rules
Gabriël D. P. Konat, Lennart C. L. Kats, Guido Wachsmuth, Eelco Visser
SLE 2012
Especially:
What is the semantics of imports?
binding rules // classes
Class(c, _, _, _) :
defines Class c of type ClassT(c)
scopes Field, Method, Variable
Extends(c) :
imports Field, Method from Class c
ClassT(c) :
refers to Class c
New(c) :
refers to Class c
28. A Theory of Name Resolution
• Representation: Scope Graphs
- Standardized representation for lexical scoping structure of programs
- Path in scope graph relates reference to declaration
- Basis for syntactic and semantic operations
- Supports ambiguous / erroneous programs
• Formalism: Name Binding Specification
- References + Declarations + Scopes + Reachability + Visibility
- Language-specific rules: mapping constructs to scope graph
• Language-Independent Interpretation
- Resolution calculus
‣ Correctness of path with respect to scope graph
‣ Separation of reachability and visibility (disambiguation)
- Name resolution algorithm
‣ sound wrt calculus
- Transformation
‣ Alpha equivalence, substitution, refactoring, …
31. A Calculus for Name Resolution
S R1 R2 SR
SRS
I(R1)
S’S
S’S P
Sx
Sx R
xS
xS D
Path in scope graph connects reference to declaration
Scopes, References, Declarations, Parents, Imports
75. Qualified Names
module N1 {
def s1 = 5
}
module M1 {
def x1 = 1 + N2.s2
}
S0
N1
SN
s2
S0
N2
R
D
R
I(N2) D
X1
s1
76. A Calculus for Name Resolution
S R1 R2 SR
SRS
I(R1)
S’S
S’S P
Sx
Sx R
xS
xS D
Reachability of declarations from references
through scope graph edges
How about ambiguities?
References with multiple paths
78. A Calculus for Name Resolution
S R1 R2 SR
SRS
I(R1)
S’S
S’S P
Sx
Sx R
xS
xS D
I(_).p’ < P.p
D < I(_).p’
D < P.p
s.p < s.p’
p < p’
Reachability
VisibilityWell formed path: R.P*.I(_)*.D
90. Shadowing
S0
S1
S2
def x3 = z2 5 7
def z1 =
fun x1 {
fun y1 {
x2 + y2
}
}
S1
S2
x1
y1
y2 x2
z1
x3S0z2
R
P
P
D
91. Shadowing
S0
S1
S2
def x3 = z2 5 7
def z1 =
fun x1 {
fun y1 {
x2 + y2
}
}
S1
S2
x1
y1
y2 x2
z1
x3S0z2
D
P
R
R
P
P
D
92. Shadowing
S0
S1
S2
D < P.p
def x3 = z2 5 7
def z1 =
fun x1 {
fun y1 {
x2 + y2
}
}
S1
S2
x1
y1
y2 x2
z1
x3S0z2
D
P
R
R
P
P
D
93. Shadowing
S0
S1
S2
D < P.p
s.p < s.p’
p < p’
def x3 = z2 5 7
def z1 =
fun x1 {
fun y1 {
x2 + y2
}
}
S1
S2
x1
y1
y2 x2
z1
x3S0z2
D
P
R
R
P
P
D
94. Shadowing
S0
S1
S2
D < P.p
s.p < s.p’
p < p’
def x3 = z2 5 7
def z1 =
fun x1 {
fun y1 {
x2 + y2
}
}
S1
S2
x1
y1
y2 x2
z1
x3S0z2
D
P
R
R
P
P
D
R.P.D < R.P.P.D
103. Imports vs. Includes
S0def z3 = 2
module A1 {
def z1 = 5
}
import A2
def x1 = 1 + z2
SA A1
SA
z1
z2
S0
A2
x1z3
104. Imports vs. Includes
S0def z3 = 2
module A1 {
def z1 = 5
}
import A2
def x1 = 1 + z2
SA A1
SA
z1
z2
S0
A2
x1
R
z3
D
105. Imports vs. Includes
S0def z3 = 2
module A1 {
def z1 = 5
}
import A2
def x1 = 1 + z2
SA A1
SA
z1
z2
S0
A2
x1
I(A2
)
R
D
z3
R
D
106. Imports vs. Includes
S0def z3 = 2
module A1 {
def z1 = 5
}
import A2
def x1 = 1 + z2
SA A1
SA
z1
z2
S0
A2
x1
I(A2
)
R
D
z3
R
D
D < I(_).p’
107. Imports vs. Includes
S0def z3 = 2
module A1 {
def z1 = 5
}
import A2
def x1 = 1 + z2
SA A1
SA
z1
z2
S0
A2
x1
I(A2
)
R
D
z3
R
D
D < I(_).p’
R.D < R.I(A2).D
122. Transitive vs. Non-Transitive
With transitive imports, a well formed path is R.P*.I(_)*.D
With non-transitive imports, a well formed path is R.P*.I(_)?
.D
A1
SA
z1
B1
SB
S0
A2
I(A2
)
D
C1
SCz2
I(B2
)
R
x1 B2
??
module A1 {
def z1 = 5
}
module B1 {
import A2
}
module C1 {
import B2
def x1 = 1 + z2
}
SA
SB
SC
123. A Calculus for Name Resolution
S R1 R2 SR
SRS
I(R1)
S’S
S’S P
Sx
Sx R
xS
xS D
I(_).p’ < P.p
D < I(_).p’
D < P.p
s.p < s.p’
p < p’
Reachability
VisibilityWell formed path: R.P*.I(_)*.D
130. C# Namespaces and Partial Classes
namespace N1 {
using M2;
partial class C3 {
int f4;
}
}
namespace N5 {
partial class C6 {
int m7() {
return f8;
}
}
}
1
3 6
4 7
8
C3 C6
N1 N5
f4 m7
N1 N5
C3 C6
f8
2 M2 5
131. More Examples?
Here the audience proposes binding patterns that they
think are not covered by the scope graph framework
(and they may be right?)
132. Blocks in Java
class Foo {
void foo() {
int x = 1;
{
int y = 2;
}
x = y;
}
}
What is the scope graph for this program?
Is the y declaration visible to the y reference?
135. Name Binding Constraints
false // always fails
true // always succeeds
C1, C2 // conjunction of constraints
[[ e ^ (s) ]] // generate constraints for sub-term
new s // generate a new scope
NS{x} <- s // declaration of name x in namespace NS in scope s
NS{x} -> s // reference of name x in namespace NS in scope s
s -> s' // unlabeled scope edge from s to s'
s -L-> s' // scope edge from s to s' labeled L
NS{x} |-> d // resolve reference x in namespace NS to declaration d
C | error $[something is wrong] // custom error message
C | warning $[does not look right] // custom warning
137. Let Bindings
let
var x : int := 5
var f : int := 1
in
for y := 1 to x do (
f := f * y
)
end
let function fact(n : int) : int =
if n < 1 then 1 else (n * fact(n - 1))
in fact(10)
end
138. Let Bindings are Sequential
let
var x : int := 0 + z // z not in scope
var y : int := x + 1
var z : int := x + y + 1
in
x + y + z
end
139. Adjacent Function Declarations are Mutually Recursive
let
function odd(x : int) : int =
if x > 0 then even(x - 1) else false
function even(x : int) : int =
if x > 0 then odd(x - 1) else true
in
even(34)
end
let
function odd(x : int) : int =
if x > 0 then even(x - 1) else false
var x : int
function even(x : int) : int =
if x > 0 then odd(x - 1) else true
in
even(34)
end
140. Namespaces
let
type foo = int
function foo(x : foo) : foo = 3
var foo : foo := foo(4)
in foo(56) + foo // both refer to the variable foo
end
142. Set Up
module static-semantics
imports signatures/-
imports nabl-lib
signature
name resolution
labels
D, P, I
well-formedness
P* . I*
order
D < P,
D < I,
I < P
rules
init ^ (s) :=
new s, // generate the root scope
Type{"int"} <- s. // declare primitive type int
143. Library
module nabl-lib
rules // auxiliary
[[ None() ^ (s) ]] := true.
[[ Some(e) ^ (s) ]] := [[ e ^ (s) ]].
Map[[ [] ^ (s) ]] := true.
Map[[ [ x | xs ] ^ (s) ]] :=
[[ x ^ (s) ]], Map[[ xs ^ (s) ]].
Map2[[ [] ^ (s, s') ]] := true.
Map2[[ [ x | xs ] ^ (s, s') ]] :=
[[ x ^ (s, s') ]], Map2[[ xs ^ (s, s') ]].
144. Variable Declarations and References
rules // variable declarations
[[ VarDec(x, t, e) ^ (s, s_outer) ]] :=
Var{x} <- s,
[[ t ^ (s_outer) ]],
[[ e ^ (s_outer) ]].
[[ VarDecNoInit(x, t) ^ (s, s_outer) ]] :=
Var{x} <- s,
[[ t ^ (s_outer) ]].
rules // variable references
[[ Var(x) ^ (s) ]] :=
Var{x} -> s, // declare x as variable reference
Var{x} |-> d. // check that x resolves to a declaration
let
var x : int := 5
var f : int := 1
in
for y := 1 to x do (
f := f * y
)
end
145. For Binds Loop Index Variable
let
var x : int := 5
var f : int := 1
in
for y := 1 to x do (
f := f * y
)
end
rules // binding statements
[[ For(Var(x), e1, e2, e3) ^ (s) ]] :=
new s',
s' -P-> s,
Var{x} <- s',
[[ e1 ^ (s) ]], // x not bound in loop bounds
[[ e2 ^ (s) ]],
[[ e3 ^ (s') ]]. // x bound in body
146. Function Declarations and References
rules // function declarations
[[ FunDecs(fdecs) ^ (s, s_outer) ]] :=
Map2[[ fdecs ^ (s, s_outer) ]].
[[ FunDec(f, args, t, e) ^ (s, s_outer) ]] :=
Var{f} <- s, // declare f
new s', // declare a new scope for body of function
s' -P-> s, // make lexical environment accessible in body
Map2[[ args ^ (s', s_outer) ]],
[[ t ^ (s_outer) ]],
[[ e ^ (s') ]].
[[ FArg(x, t) ^ (s, s_outer) ]] :=
Var{x} <- s, // declare argument x as a variable declaration
[[ t ^ (s_outer) ]].
rules // function calls
[[ Call(Var(f), exps) ^ (s) ]] :=
Var{f} -> s, // declare f as a function reference
Var{f} |-> d | error $[Function [f] not declared],
// check that f resolves to a declaration
Map[[ exps ^ (s) ]].
let function fact(n : int) : int =
if n < 1 then 1 else (n * fact(n - 1))
in fact(10)
end
147. Let Bindings are Sequential
rules // declarations
[[ Let(blocks, exps) ^ (s) ]] :=
new s', // declare a new scope for the names introduced by the let
s' -P-> s, // declare s as its parent scope
new s_body, // scope for body of the let
Decs[[ blocks ^ (s', s_body) ]],
Map[[ exps ^ (s_body) ]].
Decs[[ [] ^ (s, s_body) ]] :=
s_body -P-> s.
Decs[[ [block | blocks] ^ (s_outer, s_body) ]] :=
new s', s' -P-> s_outer,
[[ block ^ (s', s_outer) ]],
Decs[[ blocks ^ (s', s_body) ]].
let
var x : int := 0 + z // z not in scope
var y : int := x + 1
var z : int := x + y + 1
in
x + y + z
end
148. Adjacent Function Declarations are Mutually Recursive
let
function odd(x : int) : int =
if x > 0 then even(x - 1) else false
function even(x : int) : int =
if x > 0 then odd(x - 1) else true
in
even(34)
end
let
function odd(x : int) : int =
if x > 0 then even(x - 1) else false
var x : int
function even(x : int) : int =
if x > 0 then odd(x - 1) else true
in
even(34)
end
149. Namespaces
let
type foo = int
function foo(x : foo) : foo = 3
var foo : foo := foo(4)
in foo(56) + foo // both refer to the variable foo
end
rules // type declarations
[[ TypeDecs(tdecs) ^ (s, s_outer) ]] :=
Map2[[ tdecs ^ (s, s_outer) ]].
[[ TypeDec(x, t) ^ (s, s_outer) ]] :=
Type{x} <- s,
[[ t ^ (s_outer) ]].
rules // types
[[ Tid(x) ^ (s) ]] :=
Type{x} -> s,
Type{x} |-> d | error $[Type [x] not declared].
152. Issues with the Reachability Calculus
S R1 R2 SR
SRS
I(R1)
S’S
S’S P
Sx
Sx R
xS
xS D
I(_).p’ < P.p
D < I(_).p’
D < P.p
s.p < s.p’
p < p’
Well formed path: R.P*.I(_)*.D
Disambiguating import paths
Fixed visibility policy
Cyclic Import Paths
Multi-import interpretation
153. Resolution Calculus with Edge Labels
s the resolution relation for graph G.
collection JNKG is the multiset defined
DG(S)), JR(S)KG = ⇡(RG(S)), and
`G p : S 7 ! xD
i }) where ⇡(A) is
ojecting the identifiers from a set A of
iven a multiset M, 1M (x) denotes the
nes the resolution of a reference to a
as a most specific, well-formed path
n through a sequence of edges. A path
ng the atomic scope transitions in the
of steps:
l, S2) is a direct transition from the
pe S2. This step records the label of
s used.
, yR
, S) requires the resolution of ref-
n with associated scope S to allow a
rrent scope and scope S.
nds with a declaration step D(xD
) that
path is leading to.
ion in the graph from reference xR
i
`G p : xR
i 7 ! xD
i according to
. These rules all implicitly apply to
omit to avoid clutter. The calculus
n in terms of edges in the scope graph,
isible declarations. Here I is the set of
vice needed to avoid “out of thin air”
Well-formed paths
WF(p) , labels(p) 2 E
Visibility ordering on paths
label(s1) < label(s2)
s1 · p1 < s2 · p2
p1 < p2
s · p1 < s · p2
Edges in scope graph
S1
l
S2
I ` E(l, S2) : S1 ! S2
(E)
S1
l
yR
i yR
i /2 I I ` p : yR
i 7 ! yD
j yD
j S2
I ` N(l, yR
i , S2) : S1 ! S2
(N)
Transitive closure
I, S ` [] : A ⇣ A
(I)
B /2 S I ` s : A ! B I, {B} [ S ` p : B ⇣ C
I, S ` s · p : A ⇣ C
(T)
Reachable declarations
I, {S} ` p : S ⇣ S0
WF(p) S0
xD
i
I ` p · D(xD
i ) : S ⇢ xD
i
(R)
Visible declarations
I ` p : S ⇢ xD
i
8j, p0
(I ` p0
: S ⇢ xD
j ) ¬(p0
< p))
I ` p : S 7 ! xD
i
(V )
Reference resolution
xR
i S {xR
i } [ I ` p : S 7 ! xD
j
I ` p : xR
i 7 ! xD
j
(X)
G, |= !N
JN1KG ✓ JN2KG
G, |= N1
⇢
⇠ N2
(C-SUBNAME)
t1 = t2
G, |= t1 ⌘ t2
(C-EQ)
Figure 8. Interpretation of resolution and typing constraints
Resolution paths
s := D(xD
i ) | E(l, S) | N(l, xR
i , S)
p := [] | s | p · p (inductively generated)
[] · p = p · [] = p
(p1 · p2) · p3 = p1 · (p2 · p3)
Well-formed paths
WF(p) , labels(p) 2 E
Visibility ordering on paths
label(s1) < label(s2)
s1 · p1 < s2 · p2
p1 < p2
s · p1 < s · p2
Edges in scope graph
S1
l
S2
I ` E(l, S2) : S1 ! S2
(E)
S1
l
yR
i yR
i /2 I I ` p : yR
i 7 ! yD
j yD
j S2
I ` N(l, yR
i , S2) : S1 ! S2
(N)
Transitive closure
I, S ` [] : A ⇣ A
(I)
B /2 S I ` s : A ! B I, {B} [ S ` p : B ⇣ C
I, S ` s · p : A ⇣ C
(T)
Reachable declarations
I, {S} ` p : S ⇣ S0
WF(p) S0
xD
i
I ` p · D(xD
i ) : S ⇢ xD
i
(R)
Visible declarations
I ` p : S ⇢ xD
i
8j, p0
(I ` p0
: S ⇢ xD
j ) ¬(p0
< p))
I ` p : S 7 ! xD
i
(V )
Reference resolution
xR
S {xR
} [ I ` p : S 7 ! xD
C := CG
| CTy | CRes | C ^ C | True
CG
:= R S | S D | S l
S | D S | S l
R
CRes := R 7! D | D S | !N | N ⇢
⇠ N
CTy := T ⌘ T | D : T
D := | xD
i
R := xR
i
S := & | n
T := ⌧ | c(T, ..., T) with c 2 CT
N := D(S) | R(S) | V(S)
Figure 7. Syntax of constraints
scope graph resolution calculus (described in Section 3.3). Finally,
we apply |= with G set to CG
.
To lift this approach to constraints with variables, we simply
apply a multi-sorted substitution , mapping type variables ⌧ to
ground types, declaration variables to ground declarations and
scope variables & to ground scopes. Thus, our overall definition of
satisfaction for a program p is:
(CG
), |= (CRes
) ^ (CTy
) (⇧)
154. Visibility Policies
Lexical scope
L := {P} E := P⇤
D < P
Non-transitive imports
L := {P, I} E := P⇤
· I?
D < P, D < I, I < P
Transitive imports
L := {P, TI} E := P⇤
· TI⇤
D < P, D < TI, TI < P
Transitive Includes
L := {P, Inc} E := P⇤
· Inc⇤
D < P, Inc < P
Transitive includes and imports, and non-transitive imports
L := {P, Inc, TI, I} E := P⇤
· (Inc | TI)⇤
· I?
D < P, D < TI, TI < P, Inc < P, D < I, I < P,
Figure 10. Example reachability and visibility policies by instan-
tiation of path well-formedness and visibility.
3.4 Parameterization
R
Envre [
EnvL
re [
EnvD
re [
Envl
re [
I
155. Seen Imports
hout import tracking.
ficity order on paths is
e visibility policies can
e and specificity order.
module A1 {
module A2 {
def a3 = ...
}
}
import A4
def b5 = a6
Fig. 11. Self im-
port
tion
the
dule
far,
few
!
t A4
ody
?
13
AD
2 :SA2 2 D(SA1 )
AR
4 2 I(Sroot)
AR
4 2 R(Sroot) AD
1 :SA1 2 D(Sroot)
AR
4 7 ! AD
1 :SA1
Sroot ! SA1 (⇤)
Sroot ⇢ AD
2 :SA2
AR
4 2 R(Sroot) Sroot 7 ! AD
2 :SA2
AR
4 7 ! AD
2 :SA2
Fig. 10. Derivation for AR
4 7 ! AD
2 :SA2 in a calculus without import tracking.
The complete definition of well-formed paths and specificity order on paths is
given in Fig. 2. In Section 2.5 we discuss how alternative visibility policies can
be defined by just changing the well-formedness predicate and specificity order.
module A1 {
module A2 {
def a3 = ...
}
}
import A4
def b5 = a6
Seen imports. Consider the example in Fig. 11. Is declaration
a3 reachable in the scope of reference a6? This reduces to the
question whether the import of A4 can resolve to module
A2. Surprisingly, it can, in the calculus as discussed so far,
as shown by the derivation in Fig. 10 (which takes a few
shortcuts). The conclusion of the derivation is that AR
4 7 !
AD
:S . This conclusion is obtained by using the import at A
as shown by the derivation in Fig. 10 (which takes
shortcuts). The conclusion of the derivation is that AR
4
AD
2 :SA2 . This conclusion is obtained by using the import
to conclude at step (*) that Sroot ! SA1
, i.e. that the
of module A1 is reachable! In other words, the import
is used in its own resolution. Intuitively, this is nonsen
To rule out this kind of behavior we extend the cal
to keep track of the set of seen imports I using judgem
of the form I ` p : xR
i 7 ! xD
j . We need to extend all ru
pass the set I, but only the rules for resolution and im
are truly affected:
xR
i 2 R(S) {xR
i } [ I ` p : S 7 ! xD
j
I ` p : xR
i 7 ! xD
j
yR
i 2 I(S1) I I ` p : yR
i 7 ! yD
j :S2
I ` I(yR
i , yD
j :S2) : S1 ! S2
With this final ingredient, we reach the full calcul
156. 4
def b5 = a6
Fig. 11. Self im-
port
module A1 {
module B2 {
def x3 = 1
}
}
module B4 {
module A5 {
def y6 = 2
}
}
module C7 {
import A8
import B9
def z10 = x11
+ y12
}
Fig. 12. Anoma-
lous resolution
. The conclusion of the derivation is that AR
4 7 !
This conclusion is obtained by using the import at A4
de at step (*) that Sroot ! SA1
, i.e. that the body
A1 is reachable! In other words, the import of A4
its own resolution. Intuitively, this is nonsensical.
e out this kind of behavior we extend the calculus
ack of the set of seen imports I using judgements
m I ` p : xR
i 7 ! xD
j . We need to extend all rules to
et I, but only the rules for resolution and import
affected:
xR
i 2 R(S) {xR
i } [ I ` p : S 7 ! xD
j
I ` p : xR
i 7 ! xD
j
(X)
yR
i 2 I(S1) I I ` p : yR
i 7 ! yD
j :S2
I ` I(yR
i , yD
j :S2) : S1 ! S2
(I)
this final ingredient, we reach the full calculus in
is not hard to see that the resolution relation is
ded. The only recursive invocation (via the I rule)
ictly larger set I of seen imports (via the X rule); since the set R(G)
Anomaly
4
def b5 = a6
Fig. 11. Self im-
port
module A1 {
module B2 {
def x3 = 1
}
}
module B4 {
module A5 {
def y6 = 2
}
}
module C7 {
import A8
import B9
def z10 = x11
+ y12
}
Fig. 12. Anoma-
lous resolution
R
4 7 !
at A4
body
of A4
sical.
culus
ments
les to
mport
(X)
(I)
us in
on is
rule)
rule); since the set R(G)
2
1
3
1
3
2
157. Resolution Algorithm
< P
P,
nstan-
R[I](xR
) := let (r, s) = EnvE [{xR
} [ I, ;](Sc(xR
))} in
(
U if r = P and {xD
|xD
2 s} = ;
{xD
|xD
2 s}
Envre [I, S](S) :=
(
(T, ;) if S 2 S or re = ?
Env
L[{D}
re [I, S](S)
EnvL
re [I, S](S) :=
[
l2Max(L)
⇣
Env
{l0
2L|l0
<l}
re [I, S](S) Envl
re [I, S](S)
⌘
EnvD
re [I, S](S) :=
(
(T, ;) if [] /2 re
(T, D(S))
Envl
re [I, S](S) :=
8
><
>:
(P, ;) if S
I
l contains a variable or ISl[I](S) = U
S
S02
⇣
ISl[I](S)[S
I
l
⌘
Env(l 1re)[I, {S} [ S](S0)
ISl
[I](S) :=
(
U if 9yR
2 (S
B
l I) s.t. R[I](yR
) = U
{S0 | yR
2 (S
B
l I) ^ yD
2 R[I](yR
) ^ yD
S0}
159. Language-independent 𝜶-equivalence
Program similarity
Equivalence
define ↵-equivalence using scope graphs. Except for the leaves rep
ifiers, two ↵-equivalent programs must have the same abstract
write P ' P’ (pronounced “P and P’ are similar”) when the AS
re equal up to identifiers. To compare two programs we first c
T structures; if these are equal then we compare how identifiers
programs. Since two potentially ↵-equivalent programs are simi
s occur at the same positions. In order to compare the identifiers’
efine equivalence classes of positions of identifiers in a program: p
me equivalence class are declarations of or reference to the same
ract position ¯x identifies the equivalence class corresponding to
x.
n a program P, we write P for the set of positions correspon
s and declarations and PX for P extended with the artificial p
We define the
P
⇠ equivalence relation between elements of PX
if have same AST ignoring identifier names
160. Language-independent 𝜶-equivalence
Position equivalence
e same equivalence class are declarations of or reference to the same enti
abstract position ¯x identifies the equivalence class corresponding to the fr
ble x.
Given a program P, we write P for the set of positions corresponding
ences and declarations and PX for P extended with the artificial positio
¯x). We define the
P
⇠ equivalence relation between elements of PX as t
xive symmetric and transitive closure of the resolution relation.
nition 7 (Position equivalence).
` p : r i
x 7 ! di0
x
i
P
⇠ i0
i0 P
⇠ i
i
P
⇠ i0
i
P
⇠ i0
i0 P
⇠ i00
i
P
⇠ i00
i
P
⇠ i
his equivalence relation, the class containing the abstract free variable d
ion can not contain any other declaration. So the references in a particu
are either all free or all bound.
mma 6 (Free variable class). The equivalence class of a free variable do
contain any other declaration, i.e. 8 di
x s.t. i
P
⇠ ¯x =) i = ¯x
xi xi'
Program similarity
Equivalence
define ↵-equivalence using scope graphs. Except for the leaves rep
ifiers, two ↵-equivalent programs must have the same abstract
write P ' P’ (pronounced “P and P’ are similar”) when the AS
re equal up to identifiers. To compare two programs we first c
T structures; if these are equal then we compare how identifiers
programs. Since two potentially ↵-equivalent programs are simi
s occur at the same positions. In order to compare the identifiers’
efine equivalence classes of positions of identifiers in a program: p
me equivalence class are declarations of or reference to the same
ract position ¯x identifies the equivalence class corresponding to
x.
n a program P, we write P for the set of positions correspon
s and declarations and PX for P extended with the artificial p
We define the
P
⇠ equivalence relation between elements of PX
if have same AST ignoring identifier names
161. Language-independent 𝜶-equivalence
Position equivalence
e same equivalence class are declarations of or reference to the same enti
abstract position ¯x identifies the equivalence class corresponding to the fr
ble x.
Given a program P, we write P for the set of positions corresponding
ences and declarations and PX for P extended with the artificial positio
¯x). We define the
P
⇠ equivalence relation between elements of PX as t
xive symmetric and transitive closure of the resolution relation.
nition 7 (Position equivalence).
` p : r i
x 7 ! di0
x
i
P
⇠ i0
i0 P
⇠ i
i
P
⇠ i0
i
P
⇠ i0
i0 P
⇠ i00
i
P
⇠ i00
i
P
⇠ i
his equivalence relation, the class containing the abstract free variable d
ion can not contain any other declaration. So the references in a particu
are either all free or all bound.
mma 6 (Free variable class). The equivalence class of a free variable do
contain any other declaration, i.e. 8 di
x s.t. i
P
⇠ ¯x =) i = ¯x
xi xi'
Program similarity
Equivalence
define ↵-equivalence using scope graphs. Except for the leaves rep
ifiers, two ↵-equivalent programs must have the same abstract
write P ' P’ (pronounced “P and P’ are similar”) when the AS
re equal up to identifiers. To compare two programs we first c
T structures; if these are equal then we compare how identifiers
programs. Since two potentially ↵-equivalent programs are simi
s occur at the same positions. In order to compare the identifiers’
efine equivalence classes of positions of identifiers in a program: p
me equivalence class are declarations of or reference to the same
ract position ¯x identifies the equivalence class corresponding to
x.
n a program P, we write P for the set of positions correspon
s and declarations and PX for P extended with the artificial p
We define the
P
⇠ equivalence relation between elements of PX
if have same AST ignoring identifier names
ed proof is in appendix A.5, we first prove:
r i
x 7 ! d ¯x
x ) =) 8 p di0
x , p ` r i
x 7 ! di0
x =) i0
= ¯x ^ p =
eed by induction on the equivalence relation.
ce classes defined by this relation contain references to
me entity. Given this relation, we can state that two p
f the identifiers at identical positions refer to the same e
he same equivalence class:
(↵-equivalence). Two programs P1 and P2 are ↵-equi
2) when they are similar and have the same ⇠-equivalen
P1
↵
⇡ P2 , P1 ' P2 ^ 8 e e0
, e
P1
⇠ e0
, e
P2
⇠ e0
is an equivalence relation since ' and , are equivalenc(with some further details about free variables)
Alpha equivalence
162. Preserving ambiguity
25
module A1 {
def x2 := 1
}
module B3 {
def x4 := 2
}
module C5 {
import A6 B7 ;
def y8 := x9
}
module D10 {
import A11 ;
def y12 := x13
}
module E14 {
import B15 ;
def y16 := x17
}
P1
module AA1 {
def z2 := 1
}
module BB3 {
def z4 := 2
}
module C5 {
import AA6 BB7 ;
def s8 := z9
}
module D10 {
import AA11 ;
def u12 := z13
}
module E14 {
import BB15 ;
def v16 := z17
}
P2
module A1 {
def z2 := 1
}
module B3 {
def x4 := 2
}
module C5 {
import A6 B7 ;
def y8 := z9
}
module D10 {
import A11 ;
def y12 := z13
}
module E14 {
import B15 ;
def y16 := x17
}
P3
Fig. 23. ↵-equivalence and duplicate declarationP1
Lemma 6 (Free variable class). The equivalence class of a fr
ot contain any other declaration, i.e. 8 di
x s.t. i
P
⇠ ¯x =) i = ¯x
Proof. Detailed proof is in appendix A.5, we first prove:
8 r i
x, (` > : r i
x 7 ! d ¯x
x ) =) 8 p di0
x , p ` r i
x 7 ! di0
x =) i0
= ¯x
nd then proceed by induction on the equivalence relation.
The equivalence classes defined by this relation contain reference
ions of the same entity. Given this relation, we can state that t
↵-equivalent if the identifiers at identical positions refer to the s
s belong to the same equivalence class:
Definition 8 (↵-equivalence). Two programs P1 and P2 are ↵
oted P1
↵
⇡ P2) when they are similar and have the same ⇠-equi
P1
↵
⇡ P2 , P1 ' P2 ^ 8 e e0
, e
P1
⇠ e0
, e
P2
⇠ e0P2 P2
Lemma 6 (Free variable class). The e
not contain any other declaration, i.e. 8 d
Proof. Detailed proof is in appendix A.5,
8 r i
x, (` > : r i
x 7 ! d ¯x
x ) =) 8 p di0
x , p `
and then proceed by induction on the equ
The equivalence classes defined by this rel
tions of the same entity. Given this relatio
↵-equivalent if the identifiers at identical
is belong to the same equivalence class:
Definition 8 (↵-equivalence). Two pro
noted P1
↵
⇡ P2) when they are similar and
P1
↵
⇡ P2 , P1 ' P2 ^ 8P3
164. Types from Declaration
def x : int = 6
def x : int = 6
def f = fun (y : int) { x + y }
Static type-checking (or inference) is one obvious client for name resolution
In many cases, we can perform resolution before doing type analysis
165. Types from Declaration
def x : int = 6
def f = fun (y : int) { x + y }
def f = fun (y : int) { x + y }
def x : int = 6
def f = fun (y : int) { x + y }
Static type-checking (or inference) is one obvious client for name resolution
In many cases, we can perform resolution before doing type analysis
166. Types from Declaration
def x : int = 6
def f = fun (y : int) { x + y }
def x : int = 6
def f = fun (y : int) { x + y }
Static type-checking (or inference) is one obvious client for name resolution
In many cases, we can perform resolution before doing type analysis
167. Type-Dependent Name Resolution
But sometimes we need types before we can do name resolution
record A1 { x1 : int }
record B1 { a1 : A2 ; x2 : bool}
def z1 : B2 = ...
def y1 = z2.x3
def y2 = z3.a2.x4
168. Type-Dependent Name Resolution
But sometimes we need types before we can do name resolution
record A1 { x1 : int }
record B1 { a1 : A2 ; x2 : bool}
def z1 : B2 = ...
def y1 = z2.x3
def y2 = z3.a2.x4
169. Type-Dependent Name Resolution
But sometimes we need types before we can do name resolution
record A1 { x1 : int }
record B1 { a1 : A2 ; x2 : bool}
def z1 : B2 = ...
def y1 = z2.x3
def y2 = z3.a2.x4
170. Type-Dependent Name Resolution
But sometimes we need types before we can do name resolution
record A1 { x1 : int }
record B1 { a1 : A2 ; x2 : bool}
def z1 : B2 = ...
def y1 = z2.x3
def y2 = z3.a2.x4
171. Type-Dependent Name Resolution
But sometimes we need types before we can do name resolution
record A1 { x1 : int }
record B1 { a1 : A2 ; x2 : bool}
def z1 : B2 = ...
def y1 = z2.x3
def y2 = z3.a2.x4
172. Type-Dependent Name Resolution
But sometimes we need types before we can do name resolution
record A1 { x1 : int }
record B1 { a1 : A2 ; x2 : bool}
def z1 : B2 = ...
def y1 = z2.x3
def y2 = z3.a2.x4
173. Type-Dependent Name Resolution
But sometimes we need types before we can do name resolution
record A1 { x1 : int }
record B1 { a1 : A2 ; x2 : bool}
def z1 : B2 = ...
def y1 = z2.x3
def y2 = z3.a2.x4
174. Type-Dependent Name Resolution
But sometimes we need types before we can do name resolution
record A1 { x1 : int }
record B1 { a1 : A2 ; x2 : bool}
def z1 : B2 = ...
def y1 = z2.x3
def y2 = z3.a2.x4
175. Type-Dependent Name Resolution
But sometimes we need types before we can do name resolution
record A1 { x1 : int }
record B1 { a1 : A2 ; x2 : bool}
def z1 : B2 = ...
def y1 = z2.x3
def y2 = z3.a2.x4
176. Type-Dependent Name Resolution
But sometimes we need types before we can do name resolution
record A1 { x1 : int }
record B1 { a1 : A2 ; x2 : bool}
def z1 : B2 = ...
def y1 = z2.x3
def y2 = z3.a2.x4
177. Type-Dependent Name Resolution
But sometimes we need types before we can do name resolution
record A1 { x1 : int }
record B1 { a1 : A2 ; x2 : bool}
def z1 : B2 = ...
def y1 = z2.x3
def y2 = z3.a2.x4
Our approach: interleave partial name resolution with type resolution
(also using constraints)
See PEPM 2016 paper / talk
180. Summary: A Theory of Name Resolution
• Representation: Scope Graphs
- Standardized representation for lexical scoping structure of programs
- Path in scope graph relates reference to declaration
- Basis for syntactic and semantic operations
• Formalism: Name Binding Constraints
- References + Declarations + Scopes + Reachability + Visibility
- Language-specific rules map AST to constraints
• Language-Independent Interpretation
- Resolution calculus: correctness of path with respect to scope graph
- Name resolution algorithm
- Alpha equivalence
- Mapping from graph to tree (to text)
- Refactorings
- And many other applications
181. Validation
• We have modeled a large set of example binding patterns
- definition before use
- different let binding flavors
- recursive modules
- imports and includes
- qualified names
- class inheritance
- partial classes
• Next goal: fully model some real languages
- Java
- ML
182. Future Work
• Scope graph semantics for binding specification languages
- starting with NaBL
- or rather: a redesign of NaBL based on scope graphs
• Resolution-sensitive program transformations
- renaming, refactoring, substitution, …
• Dynamic analogs to static scope graphs
- how does scope graph relate to memory at run-time?
• Supporting mechanized language meta-theory
- relating static and dynamic bindings