static name resolution

185
IN4303 2016-2017 Compiler Construction Static Name Resolution static analysis Eelco Visser

Upload: eelco-visser

Post on 17-Jan-2017

348 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Static name resolution

IN4303 2016-2017 Compiler Construction

Static Name Resolution static analysis

Eelco Visser

Page 2: Static name resolution

Static Name Resolution

module A {

def s = 5

}

module B {

import A

def x = 6

def y = 3 + s

def f =

fun x { x + y }

}

Page 3: Static name resolution

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 }

}

Page 4: Static name resolution

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 }

}

Page 5: Static name resolution

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 }

}

Page 6: Static name resolution

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 }

}

Page 7: Static name resolution

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 }

}

Page 8: Static name resolution

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

Page 9: Static name resolution

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

Page 10: Static name resolution

Language = Set of Sentences

fun (x : Int) { x + 1 }

Text is a convenient interface for writing and reading programs

Page 11: Static name resolution

Language = Set of Trees

Fun

AddArgDecl

VarRefId Int

“1”

VarDeclId

“x”

TXINT

“x”

Tree is a convenient interface for transforming programs

Page 12: Static name resolution

Tree Transformation

Tree is a convenient interface for transforming programs

Semantictransform translate

eval analyze refactor

type check

Syntacticcoloring

outline view completion

Page 13: Static name resolution

Language = Sentences and Trees

parse

format

different representations convenient for different purposes

Page 14: Static name resolution

SDF3 defines Trees and Sentences

parse(s) = t where format(t) == s (modulo layout)

Expr.Int = INTExpr.Add = <<Expr> + <Expr>>Expr.Mul = <<Expr> * <Expr>>

trees (structure)

parse (text to tree)

format (tree to text) + =>

Page 15: Static name resolution

Grammar Engineering in Spoofax

Page 16: Static name resolution

Ambiguity

Page 17: Static name resolution

Ambiguity

t1 != t2 /\ format(t1) = format(t2)

Add

VarRef VarRef

“y”“x”

Mul

Int

“3”

Add

VarRef

VarRef

“y”

“x”

Mul

Int

“3”

3 * x + y

Page 18: Static name resolution

Declarative Disambiguation

parse filter

Disambiguation Filters Klint & Visser (1994), Van den Brand, Scheerder, Vinju, Visser (CC 2002)

Page 19: Static name resolution

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

Page 20: Static name resolution

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

Page 21: Static name resolution

Declare Your Names

A Theory of Name ResolutionPierre Néron, Andrew Tolmach, Eelco Visser, Guido Wachsmuth

ESOP 2015

Page 22: Static name resolution

Language = Set of Trees

Fun

AddArgDecl

VarRefId Int

“1”

VarDeclId

“x”

TXINT

“x”

Tree is a convenient interface for transforming programs

Page 23: Static name resolution

Language = Set of Graphs

Fun

AddArgDecl

VarRefId Int

“1”

VarDeclId TXINT

Edges from references to declarations

Page 24: Static name resolution

Fun

AddArgDecl

VarRefId Int

“1”

VarDeclId

“x”

TXINT

“x”

Language = Set of Graphs

Names are placeholders for edges in linear / tree representation

Page 25: Static name resolution

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?

Page 26: Static name resolution

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?

Page 27: Static name resolution

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

Page 28: Static name resolution

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, …

Page 29: Static name resolution

Outline

• Reachability- References, Declarations, Scopes, Resolution Paths - Lexical, Imports, Qualified Names

• Visibility- Ambiguous resolutions - Disambiguation: path specificity, path wellformedness

• Examples- let, def-before-use, inheritance, packages, imports, namespaces

• Formal framework- Generalizing reachability and visibility through labeled scope edges - Resolution algorithm - Alpha equivalence

• Names and Types- Type-dependent name resolution

• Constraint Language

Page 30: Static name resolution

Reachability

(Slides by Pierre Néron)

Page 31: Static name resolution

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

Page 32: Static name resolution

Simple Scopes

def y1 = x2 + 1def x1 = 5

Page 33: Static name resolution

Simple Scopes

def y1 = x2 + 1def x1 = 5

Page 34: Static name resolution

Simple Scopes

def y1 = x2 + 1def x1 = 5

Page 35: Static name resolution

Simple Scopes

S0def y1 = x2 + 1def x1 = 5

Page 36: Static name resolution

Simple Scopes

Scope

S0def y1 = x2 + 1def x1 = 5

S0

S

Page 37: Static name resolution

Simple Scopes

Declaration

Scope

S0def y1 = x2 + 1def x1 = 5

y1

x1S0

Sx

Page 38: Static name resolution

Simple Scopes

Reference

Declaration

Scope

S0def y1 = x2 + 1def x1 = 5

y1

x1S0x2

Sx

x

Page 39: Static name resolution

Simple Scopes

Reference

Declaration

Scope

Reference Step

S0def y1 = x2 + 1def x1 = 5

Sx

Sx R

y1

x1S0x2

Sx

x

Page 40: Static name resolution

Simple Scopes

Reference

Declaration

Scope

Reference Step

S0def y1 = x2 + 1def x1 = 5

Sx

Sx R

R

y1

x1S0x2

Sx

x

Page 41: Static name resolution

Simple Scopes

Reference

Declaration

Scope

Reference Step Declaration Step

S0def y1 = x2 + 1def x1 = 5

Sx

Sx R

xS

xS D

R

y1

x1S0x2

Sx

x

Page 42: Static name resolution

Simple Scopes

Reference

Declaration

Scope

Reference Step Declaration Step

S0def y1 = x2 + 1def x1 = 5

Sx

Sx R

xS

xS D

R D

y1

x1S0x2

Sx

x

Page 43: Static name resolution

Lexical Scoping

def x1 = z2 5 def z1 = fun y1 { x2 + y2 }

Page 44: Static name resolution

Lexical Scoping

S0def x1 = z2 5 def z1 = fun y1 { x2 + y2 }

Page 45: Static name resolution

Lexical Scoping

S1

S0def x1 = z2 5 def z1 = fun y1 { x2 + y2 }

Page 46: Static name resolution

Lexical Scoping

S1

S0def x1 = z2 5 def z1 = fun y1 { x2 + y2 }

z1

x1S0z2

Page 47: Static name resolution

Lexical Scoping

S1

S0def x1 = z2 5 def z1 = fun y1 { x2 + y2 }

S1

y1y2 x2

z1

x1S0z2

Page 48: Static name resolution

Lexical Scoping

S1

S0

Parent

def x1 = z2 5 def z1 = fun y1 { x2 + y2 }

S1

y1y2 x2

z1

x1S0z2

S’S

Page 49: Static name resolution

Lexical Scoping

S1

S0

Parent

def x1 = z2 5 def z1 = fun y1 { x2 + y2 }

S1

y1y2 x2

z1

x1S0z2

S’S

Page 50: Static name resolution

Lexical Scoping

S1

S0

Parent

def x1 = z2 5 def z1 = fun y1 { x2 + y2 }

S1

y1y2 x2

z1

x1S0z2

S’S

Page 51: Static name resolution

Lexical Scoping

S1

S0

Parent

def x1 = z2 5 def z1 = fun y1 { x2 + y2 }

S1

y1y2 x2

z1

x1S0z2

R

S’S

Page 52: Static name resolution

Lexical Scoping

S1

S0

Parent

def x1 = z2 5 def z1 = fun y1 { x2 + y2 }

S1

y1y2 x2

z1

x1S0z2

R D

S’S

Page 53: Static name resolution

Lexical Scoping

S1

S0

Parent

def x1 = z2 5 def z1 = fun y1 { x2 + y2 }

S1

y1y2 x2

z1

x1S0z2

R

S’S

Page 54: Static name resolution

Lexical Scoping

S1

S0

Parent

def x1 = z2 5 def z1 = fun y1 { x2 + y2 }

S’S

S’S P

S1

y1y2 x2

z1

x1S0z2

R

S’S

Parent Step

Page 55: Static name resolution

Lexical Scoping

S1

S0

Parent

def x1 = z2 5 def z1 = fun y1 { x2 + y2 }

S’S

S’S P

S1

y1y2 x2

z1

x1S0z2

R

P

S’S

Parent Step

Page 56: Static name resolution

Lexical Scoping

S1

S0

Parent

def x1 = z2 5 def z1 = fun y1 { x2 + y2 }

S’S

S’S P

S1

y1y2 x2

z1

x1S0z2

R

P

D

S’S

Parent Step

Page 57: Static name resolution

Imports

S0

SB

SAmodule A1 { def z1 = 5}module B1 { import A2 def x1 = 1 + z2}

Page 58: Static name resolution

Imports

S0

SB

SAmodule A1 { def z1 = 5}module B1 { import A2 def x1 = 1 + z2}

A1

SA

z1

B1

SB

z2

S0

A2

x1

Page 59: Static name resolution

Imports

Associated scope

S0

SB

SAmodule A1 { def z1 = 5}module B1 { import A2 def x1 = 1 + z2}

R2 SR

A1

SA

z1

B1

SB

z2

S0

A2

x1

Page 60: Static name resolution

Imports

Associated scope

Import

S0

SB

SAmodule A1 { def z1 = 5}module B1 { import A2 def x1 = 1 + z2}

S R1

R2 SR

A1

SA

z1

B1

SB

z2

S0

A2

x1

Page 61: Static name resolution

Imports

Associated scope

Import

S0

SB

SAmodule A1 { def z1 = 5}module B1 { import A2 def x1 = 1 + z2}

S R1

R2 SR

A1

SA

z1

B1

SB

z2

S0

A2

x1

Page 62: Static name resolution

Imports

Associated scope

Import

S0

SB

SAmodule A1 { def z1 = 5}module B1 { import A2 def x1 = 1 + z2}

S R1

R2 SR

A1

SA

z1

B1

SB

z2

S0

A2

x1

Page 63: Static name resolution

Imports

Associated scope

Import

S0

SB

SAmodule A1 { def z1 = 5}module B1 { import A2 def x1 = 1 + z2}

S R1

R2 SR

A1

SA

z1

B1

SB

z2

S0

A2

x1

R

Page 64: Static name resolution

Imports

Associated scope

Import

S0

SB

SAmodule A1 { def z1 = 5}module B1 { import A2 def x1 = 1 + z2}

S R1

R2 SR

A1

SA

z1

B1

SB

z2

S0

A2

x1

R

S R1 R2 SR

SRS I(R1)

Import Step

Page 65: Static name resolution

Imports

Associated scope

Import

S0

SB

SAmodule A1 { def z1 = 5}module B1 { import A2 def x1 = 1 + z2}

S R1

R2 SR

A1

SA

z1

B1

SB

z2

S0

A2

x1

R

R

S R1 R2 SR

SRS I(R1)

Import Step

Page 66: Static name resolution

Imports

Associated scope

Import

S0

SB

SAmodule A1 { def z1 = 5}module B1 { import A2 def x1 = 1 + z2}

S R1

R2 SR

A1

SA

z1

B1

SB

z2

S0

A2

x1

R

RP

S R1 R2 SR

SRS I(R1)

Import Step

Page 67: Static name resolution

Imports

Associated scope

Import

S0

SB

SAmodule A1 { def z1 = 5}module B1 { import A2 def x1 = 1 + z2}

S R1

R2 SR

A1

SA

z1

B1

SB

z2

S0

A2

x1

R

RP

D

S R1 R2 SR

SRS I(R1)

Import Step

Page 68: Static name resolution

Imports

Associated scope

Import

S0

SB

SAmodule A1 { def z1 = 5}module B1 { import A2 def x1 = 1 + z2}

S R1

R2 SR

A1

SA

z1

B1

SB

z2

S0

A2

x1

I(A2)R

RP

D

S R1 R2 SR

SRS I(R1)

Import Step

Page 69: Static name resolution

Imports

Associated scope

Import

S0

SB

SAmodule A1 { def z1 = 5}module B1 { import A2 def x1 = 1 + z2}

S R1

R2 SR

A1

SA

z1

B1

SB

z2

S0

A2

x1

I(A2)R

R

D

P

D

S R1 R2 SR

SRS I(R1)

Import Step

Page 70: Static name resolution

Qualified Names

module N1 { def s1 = 5}

module M1 { def x1 = 1 + N2.s2

}

S0

Page 71: Static name resolution

Qualified Names

module N1 { def s1 = 5}

module M1 { def x1 = 1 + N2.s2

}

S0

Page 72: Static name resolution

Qualified Names

module N1 { def s1 = 5}

module M1 { def x1 = 1 + N2.s2

}

S0

N1

SN

s2

S0

N2 X1

s1

Page 73: Static name resolution

Qualified Names

module N1 { def s1 = 5}

module M1 { def x1 = 1 + N2.s2

}

S0

N1

SN

s2

S0

N2 X1

s1

Page 74: Static name resolution

Qualified Names

module N1 { def s1 = 5}

module M1 { def x1 = 1 + N2.s2

}

S0

N1

SN

s2

S0

N2

R D

X1

s1

Page 75: Static name resolution

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

Page 76: Static name resolution

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

Page 77: Static name resolution

Visibility(Disambiguation)

(Slides by Pierre Néron)

Page 78: Static name resolution

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

Page 79: Static name resolution

Ambiguous Resolutions

S0 def x1 = 5

def x2 = 3

def z1 = x3 + 1

Page 80: Static name resolution

Ambiguous Resolutions

z1 x2

x1S0x3

S0 def x1 = 5

def x2 = 3

def z1 = x3 + 1

Page 81: Static name resolution

Ambiguous Resolutions

z1 x2

x1S0x3

RS0 def x1 = 5

def x2 = 3

def z1 = x3 + 1

Page 82: Static name resolution

Ambiguous Resolutions

z1 x2

x1S0x3

R DS0 def x1 = 5

def x2 = 3

def z1 = x3 + 1

Page 83: Static name resolution

Ambiguous Resolutions

z1 x2

x1S0x3

R

D

S0 def x1 = 5

def x2 = 3

def z1 = x3 + 1

Page 84: Static name resolution

Ambiguous Resolutions

z1 x2

x1S0x3

R D

D

S0 def x1 = 5

def x2 = 3

def z1 = x3 + 1

Page 85: Static name resolution

Ambiguous Resolutions

match t with | A x | B x => …

z1 x2

x1S0x3

R D

D

S0 def x1 = 5

def x2 = 3

def z1 = x3 + 1

Page 86: Static name resolution

Shadowing

def x3 = z2 5 7 def z1 = fun x1 { fun y1 { x2 + y2 } }

Page 87: Static name resolution

Shadowing

S0

S1S2

def x3 = z2 5 7 def z1 = fun x1 { fun y1 { x2 + y2 } }

Page 88: Static name resolution

Shadowing

S0

S1S2

S1

S2

x1

y1

y2 x2

z1

x3S0z2def x3 = z2 5 7 def z1 = fun x1 { fun y1 { x2 + y2 } }

Page 89: Static name resolution

Shadowing

S0

S1S2

S1

S2

x1

y1

y2 x2

z1

x3S0z2def x3 = z2 5 7 def z1 = fun x1 { fun y1 { x2 + y2 } }

Page 90: Static name resolution

Shadowing

S0

S1S2

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

Page 91: Static name resolution

Shadowing

S0

S1S2

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

Page 92: Static name resolution

Shadowing

S0

S1S2

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

Page 93: Static name resolution

Shadowing

S0

S1S2

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

Page 94: Static name resolution

Shadowing

S0

S1S2

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

Page 95: Static name resolution

Imports shadow Parents

S0def z3 = 2

module A1 { def z1 = 5}module B1 { import A2 def x1 = 1 + z2}

SA

SB

Page 96: Static name resolution

Imports shadow Parents

A1

SA

z1

B1

SB

z2

S0

A2

x1

z3S0def z3 = 2

module A1 { def z1 = 5}module B1 { import A2 def x1 = 1 + z2}

SA

SB

Page 97: Static name resolution

Imports shadow Parents

A1

SA

z1

B1

SB

z2

S0

A2

x1

z3S0def z3 = 2

module A1 { def z1 = 5}module B1 { import A2 def x1 = 1 + z2}

SA

SB

Page 98: Static name resolution

Imports shadow Parents

A1

SA

z1

B1

SB

z2

S0

A2

x1

I(A2)R D

z3S0def z3 = 2

module A1 { def z1 = 5}module B1 { import A2 def x1 = 1 + z2}

SA

SB

Page 99: Static name resolution

Imports shadow Parents

A1

SA

z1

B1

SB

z2

S0

A2

x1

I(A2)R D

P

Dz3

R

S0def z3 = 2

module A1 { def z1 = 5}module B1 { import A2 def x1 = 1 + z2}

SA

SB

Page 100: Static name resolution

Imports shadow Parents

I(_).p’ < P.p

A1

SA

z1

B1

SB

z2

S0

A2

x1

I(A2)R D

P

Dz3

R

S0def z3 = 2

module A1 { def z1 = 5}module B1 { import A2 def x1 = 1 + z2}

SA

SB

Page 101: Static name resolution

Imports shadow Parents

I(_).p’ < P.p ⇒ R.I(A2).D < R.P.D

A1

SA

z1

B1

SB

z2

S0

A2

x1

I(A2)R D

P

Dz3

R

S0def z3 = 2

module A1 { def z1 = 5}module B1 { import A2 def x1 = 1 + z2}

SA

SB

Page 102: Static name resolution

Imports vs. Includes

S0def z3 = 2

module A1 { def z1 = 5}

import A2def x1 = 1 + z2

SA

Page 103: Static name resolution

Imports vs. Includes

S0def z3 = 2

module A1 { def z1 = 5}

import A2def x1 = 1 + z2

SA A1

SA

z1

z2

S0

A2

x1z3

Page 104: Static name resolution

Imports vs. Includes

S0def z3 = 2

module A1 { def z1 = 5}

import A2def x1 = 1 + z2

SA A1

SA

z1

z2

S0

A2

x1

R

z3 D

Page 105: Static name resolution

Imports vs. Includes

S0def z3 = 2

module A1 { def z1 = 5}

import A2def x1 = 1 + z2

SA A1

SA

z1

z2

S0

A2

x1

I(A2)

R

D

z3R

D

Page 106: Static name resolution

Imports vs. Includes

S0def z3 = 2

module A1 { def z1 = 5}

import A2def x1 = 1 + z2

SA A1

SA

z1

z2

S0

A2

x1

I(A2)

R

D

z3R

D

D < I(_).p’

Page 107: Static name resolution

Imports vs. Includes

S0def z3 = 2

module A1 { def z1 = 5}

import A2def x1 = 1 + z2

SA A1

SA

z1

z2

S0

A2

x1

I(A2)

R

D

z3R

D

D < I(_).p’ ⇒ R.D < R.I(A2).D

Page 108: Static name resolution

Imports vs. Includes

A1

SA

z1

z2

S0

A2

x1

I(A2)

R

D

z3R

D

D < I(_).p’ ⇒ R.D < R.I(A2).D

S0def z3 = 2

module A1 { def z1 = 5}

include A2def x1 = 1 + z2

SA

Page 109: Static name resolution

Imports vs. Includes

A1

SA

z1

z2

S0

A2

x1

I(A2)

R

D

z3R

D

D < I(_).p’ ⇒ R.D < R.I(A2).D

S0def z3 = 2

module A1 { def z1 = 5}

include A2def x1 = 1 + z2

SA

X

Page 110: Static name resolution

Imports vs. Includes

A1

SA

z1

z2

S0

A2

x1

I(A2)

R

D

z3R

D

D < I(_).p’

S0def z3 = 2

module A1 { def z1 = 5}

include A2def x1 = 1 + z2

SA

X

Page 111: Static name resolution

Import Parents

def s1 = 5module N1 {

}

def x1 = 1 + N2.s2

S0

SN

Page 112: Static name resolution

Import Parents

def s1 = 5module N1 {

}

def x1 = 1 + N2.s2

S0

SN

Page 113: Static name resolution

Import Parents

def s1 = 5module N1 {

}

def x1 = 1 + N2.s2

S0

SN

N1

SN

s2

S0

N2 X1

s1

Page 114: Static name resolution

Import Parents

def s1 = 5module N1 {

}

def x1 = 1 + N2.s2

S0

SN

N1

SN

s2

S0

N2 X1

s1

R

I(N2)

D

P

Page 115: Static name resolution

Import Parents

def s1 = 5module N1 {

}

def x1 = 1 + N2.s2

S0

SN

N1

SN

s2

S0

N2 X1

s1

R

I(N2)

D

P

Page 116: Static name resolution

Import Parents

def s1 = 5module N1 {

}

def x1 = 1 + N2.s2

S0

SN

Well formed path: R.P*.I(_)*.D

N1

SN

s2

S0

N2 X1

s1

R

I(N2)

D

P

Page 117: Static name resolution

Transitive vs. Non-Transitive

module A1 { def z1 = 5}module B1 { import A2}module C1 { import B2 def x1 = 1 + z2}

SA

SB

SC

Page 118: Static name resolution

Transitive vs. Non-Transitive

module A1 { def z1 = 5}module B1 { import A2}module C1 { import B2 def x1 = 1 + z2}

SA

SB

SC

Page 119: Static name resolution

Transitive vs. Non-Transitive

??

module A1 { def z1 = 5}module B1 { import A2}module C1 { import B2 def x1 = 1 + z2}

SA

SB

SC

Page 120: Static name resolution

Transitive vs. Non-Transitive

A1

SA

z1

B1

SB

S0

A2

C1

SCz2

x1 B2

??

module A1 { def z1 = 5}module B1 { import A2}module C1 { import B2 def x1 = 1 + z2}

SA

SB

SC

Page 121: Static name resolution

Transitive vs. Non-Transitive

A1

SA

z1

B1

SB

S0

A2

I(A2)

D

C1

SCz2I(B2)

R

x1 B2

??

module A1 { def z1 = 5}module B1 { import A2}module C1 { import B2 def x1 = 1 + z2}

SA

SB

SC

Page 122: Static name resolution

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

SCz2I(B2)

R

x1 B2

??

module A1 { def z1 = 5}module B1 { import A2}module C1 { import B2 def x1 = 1 + z2}

SA

SB

SC

Page 123: Static name resolution

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

Page 124: Static name resolution

Examples

(From TUD-SERG-2015-001. Adapted for this screen)

Page 125: Static name resolution

Let Bindings

1

2

b2a1 c3

a4

b6

c12a10 b11

c5

b9

a7

c8

def a1 = 0def b2 = 1def c3 = 2

letpar a4 = c5 b6 = a7 c8 = b9in a10+b11+c12

1

b2a1 c3

a4

b6

c12a10 b11

c5

b9

a7

c8

2

def a1 = 0def b2 = 1def c3 = 2

letrec a4 = c5 b6 = a7 c8 = b9in a10+b11+c12

1

b2a1 c3

a4

b6

c12a10 b11

c5

b9

a7

c8

2

4

3

def a1 = 0def b2 = 1def c3 = 2

let a4 = c5 b6 = a7 c8 = b9in a10+b11+c12

Page 126: Static name resolution

Definition before Use / Use before Definition

class C1 { int a2 = b3; int b4; void m5 (int a6) {

int c7 = a8 + b9; int b10 = b11 + c12; } int c12;}

0 C1

1

2

a2

b4

c12

b3

m5

a6

3

4

c7

b10b9

a8

b11 c12

Page 127: Static name resolution

Inheritance

class C1 { int f2 = 42;}class D3 extends C4 { int g5 = f6; }class E7 extends D8 { int f9 = g10; int h11 = f12; }

32

1

C4

C1

4D3

E7

D8

f2 g5 f6

f9

g10f12 h11

Page 128: Static name resolution

Java Packages

package p3;

class D4 {}

package p1;

class C2 {}

1 p3p1

2

p1 p3

3

D4C2

Page 129: Static name resolution

Java Import

package p1;

imports r2.*;imports q3.E4;

public class C5 {}

class D6 {}4

p1

D6C5

3

2r2

1

p1

E4

q3

Page 130: Static name resolution

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

Page 131: Static name resolution

More Examples?

Here the audience proposes binding patterns that they think are not covered by the scope graph framework

(and they may be right?)

Page 132: Static name resolution

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?

Page 133: Static name resolution

Constraint Language

Page 134: Static name resolution

Constraint Generation

module constraint-gen

imports signatures/-

rules

[[ Constr(t1, ..., tn) ^ (s1, …, sn) ]] := C1, ..., Cn.

one rule per abstract syntax constructor

Page 135: Static name resolution

Name Binding Constraints

false // always failstrue // 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 sNS{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 messageC | warning $[does not look right] // custom warning

Page 136: Static name resolution

Tiger Name Binding

Page 137: Static name resolution

Let Bindings

let var x : int := 5 var f : int := 1in 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

Page 138: Static name resolution

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 + zend

Page 139: Static name resolution

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

Page 140: Static name resolution

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 fooend

Page 141: Static name resolution

Tiger in NaBL2

Page 142: Static name resolution

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 < Prules

init ^ (s) := new s, // generate the root scope Type{"int"} <- s. // declare primitive type int

Page 143: Static name resolution

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') ]].

Page 144: Static name resolution

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 := 1in for y := 1 to x do ( f := f * y )end

Page 145: Static name resolution

For Binds Loop Index Variable

let var x : int := 5 var f : int := 1in 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

Page 146: Static name resolution

Function Declarations and Referencesrules // 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

Page 147: Static name resolution

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 + zend

Page 148: Static name resolution

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

Page 149: Static name resolution

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 fooend

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].

Page 150: Static name resolution

Full Definition Online

https://github.com/MetaBorgCube/metaborg-tiger

https://github.com/MetaBorgCube/metaborg-tiger/blob/master/org.metaborg.lang.tiger/trans/static-semantics/static-semantics.nabl2

Page 151: Static name resolution

Formal Framework(with labeled scope edges)

Page 152: Static name resolution

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

Page 153: Static name resolution

Resolution Calculus with Edge Labels

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 : TD := � | xD

i

R := x

Ri

S := & | nT := ⌧ | 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 simplyapply a multi-sorted substitution �, mapping type variables ⌧ toground types, declaration variables � to ground declarations andscope variables & to ground scopes. Thus, our overall definition ofsatisfaction for a program p is:

�(CG

p

), |= �(CResp

) ^ �(CTyp

) (⇧)

where �(E) denotes the application of the substitution � to all thevariables appearing in E that are in the domain of �. When theproposition ⇧ holds we say that and � resolve p.

Resolution and Typing Constraints The |= relation is given bythe inductive rules in Fig. 8, where = is the syntactic equality onterms and `G x

Ri

7�! x

Dj

is the resolution relation for graph G.The interpretation of a name collection JNKG is the multiset definedas follows: JD(S)KG = ⇡(DG(S)), JR(S)KG = ⇡(RG(S)), andJV(S)KG = ⇡({xD

i

| 9p, `G p : S 7�! x

Di

}) where ⇡(A) isthe multiset produced by projecting the identifiers from a set A ofreferences or declarations. Given a multiset M , 1

M

(x) denotes themultiplicity of x in M .

3.3 Resolution Calculus

The resolution calculus defines the resolution of a reference to adeclaration in a scope graph as a most specific, well-formed pathfrom reference to declaration through a sequence of edges. A pathp is a list of steps representing the atomic scope transitions in thegraph. There are three kinds of steps:• A (direct) edge step E(l, S2) is a direct transition from the

current scope to the scope S2. This step records the label ofthe scope transition that is used.

• A nominal edge step N(l, yR, S) requires the resolution of ref-

erence y

R to a declaration with associated scope S to allow atransition between the current scope and scope S.

• A complete path always ends with a declaration step D(xD) thatstores the declaration the path is leading to.

A path p is a valid resolution in the graph from reference x

Ri

to declaration x

Di

such that `G p : x

Ri

7�! x

Di

according tothe calculus rules in Fig. 9. These rules all implicitly apply toa fixed graph G, which we omit to avoid clutter. The calculusdefines the resolution relation in terms of edges in the scope graph,reachable declarations, and visible declarations. Here I is the set ofseen imports, a technical device needed to avoid “out of thin air”anomalies in resolution of nominal imports. We often drop I froma resolution when it is empty. The S component that appears inthe transitive closure rules is the set of seen scopes that is used toprevent cycles in the resolution path of a given reference.

G, |= True

(C-TRUE)

G, |= C1 G, |= C2

G, |= C1 ^ C2(C-AND)

(d) = T

G, |= d : T(C-TYPEOF)

`G p : xRi

7! x

Dj

G, |= x

Ri

7! x

Dj

(C-RESOLVE)

d GS

G, |= d S

(C-SCOPEOF)

8x,1JNKG (x) 1

G, |= !N(C-UNIQUE)

JN1KG ✓ JN2KGG, |= N1 ⇢⇠ N2

(C-SUBNAME)

t1 = t2

G, |= t1 ⌘ t2(C-EQ)

Figure 8. Interpretation of resolution and typing constraints

Resolution paths

s := D(xDi

) | E(l, S) | N(l, xRi

, S)

p := [] | s | p · p (inductively generated)[] · p = p · [] = p

(p1 · p2) · p3 = p1 · (p2 · p3)Well-formed paths

WF(p) , labels(p) 2 EVisibility ordering on paths

label(s1) < label(s2)s1 · p1 < s2 · p2

p1 < p2

s · p1 < s · p2Edges in scope graph

S1l

S2

I ` E(l, S2) : S1 �! S2(E)

S1l

y

Ri

y

Ri

/2 I I ` p : yRi

7�! y

Dj

y

Dj

S2

I ` N(l, yRi

, 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⇣ S

0 WF(p) S

0x

Di

I ` p · D(xDi

) : S⇢ x

Di

(R)

Visible declarations

I ` p : S⇢ x

Di

8j, p0(I ` p

0 : S⇢ x

Dj

) ¬(p0 < p))

I ` p : S 7�! x

Di

(V )

Reference resolution

x

Ri

S {xRi

} [ I ` p : S 7�! x

Dj

I ` p : xRi

7�! x

Dj

(X)

Figure 9. Resolution calculus from [14] extended for arbitraryedge labels and parameterized with well-formedness predicate WFand visibility ordering <. Here label projects the label from a stepand labels projects the sequence of labels from a path.

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 : TD := � | xD

i

R := x

Ri

S := & | nT := ⌧ | 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 simplyapply a multi-sorted substitution �, mapping type variables ⌧ toground types, declaration variables � to ground declarations andscope variables & to ground scopes. Thus, our overall definition ofsatisfaction for a program p is:

�(CG

p

), |= �(CResp

) ^ �(CTyp

) (⇧)

where �(E) denotes the application of the substitution � to all thevariables appearing in E that are in the domain of �. When theproposition ⇧ holds we say that and � resolve p.

Resolution and Typing Constraints The |= relation is given bythe inductive rules in Fig. 8, where = is the syntactic equality onterms and `G x

Ri

7�! x

Dj

is the resolution relation for graph G.The interpretation of a name collection JNKG is the multiset definedas follows: JD(S)KG = ⇡(DG(S)), JR(S)KG = ⇡(RG(S)), andJV(S)KG = ⇡({xD

i

| 9p, `G p : S 7�! x

Di

}) where ⇡(A) isthe multiset produced by projecting the identifiers from a set A ofreferences or declarations. Given a multiset M , 1

M

(x) denotes themultiplicity of x in M .

3.3 Resolution Calculus

The resolution calculus defines the resolution of a reference to adeclaration in a scope graph as a most specific, well-formed pathfrom reference to declaration through a sequence of edges. A pathp is a list of steps representing the atomic scope transitions in thegraph. There are three kinds of steps:• A (direct) edge step E(l, S2) is a direct transition from the

current scope to the scope S2. This step records the label ofthe scope transition that is used.

• A nominal edge step N(l, yR, S) requires the resolution of ref-

erence y

R to a declaration with associated scope S to allow atransition between the current scope and scope S.

• A complete path always ends with a declaration step D(xD) thatstores the declaration the path is leading to.

A path p is a valid resolution in the graph from reference x

Ri

to declaration x

Di

such that `G p : x

Ri

7�! x

Di

according tothe calculus rules in Fig. 9. These rules all implicitly apply toa fixed graph G, which we omit to avoid clutter. The calculusdefines the resolution relation in terms of edges in the scope graph,reachable declarations, and visible declarations. Here I is the set ofseen imports, a technical device needed to avoid “out of thin air”anomalies in resolution of nominal imports. We often drop I froma resolution when it is empty. The S component that appears inthe transitive closure rules is the set of seen scopes that is used toprevent cycles in the resolution path of a given reference.

G, |= True

(C-TRUE)

G, |= C1 G, |= C2

G, |= C1 ^ C2(C-AND)

(d) = T

G, |= d : T(C-TYPEOF)

`G p : xRi

7! x

Dj

G, |= x

Ri

7! x

Dj

(C-RESOLVE)

d GS

G, |= d S

(C-SCOPEOF)

8x,1JNKG (x) 1

G, |= !N(C-UNIQUE)

JN1KG ✓ JN2KGG, |= N1 ⇢⇠ N2

(C-SUBNAME)

t1 = t2

G, |= t1 ⌘ t2(C-EQ)

Figure 8. Interpretation of resolution and typing constraints

Resolution paths

s := D(xDi

) | E(l, S) | N(l, xRi

, S)

p := [] | s | p · p (inductively generated)[] · p = p · [] = p

(p1 · p2) · p3 = p1 · (p2 · p3)Well-formed paths

WF(p) , labels(p) 2 EVisibility ordering on paths

label(s1) < label(s2)s1 · p1 < s2 · p2

p1 < p2

s · p1 < s · p2Edges in scope graph

S1l

S2

I ` E(l, S2) : S1 �! S2(E)

S1l

y

Ri

y

Ri

/2 I I ` p : yRi

7�! y

Dj

y

Dj

S2

I ` N(l, yRi

, 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⇣ S

0 WF(p) S

0x

Di

I ` p · D(xDi

) : S⇢ x

Di

(R)

Visible declarations

I ` p : S⇢ x

Di

8j, p0(I ` p

0 : S⇢ x

Dj

) ¬(p0 < p))

I ` p : S 7�! x

Di

(V )

Reference resolution

x

Ri

S {xRi

} [ I ` p : S 7�! x

Dj

I ` p : xRi

7�! x

Dj

(X)

Figure 9. Resolution calculus from [14] extended for arbitraryedge labels and parameterized with well-formedness predicate WFand visibility ordering <. Here label projects the label from a stepand labels projects the sequence of labels from a path.

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 : TD := � | xD

i

R := x

Ri

S := & | nT := ⌧ | 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 simplyapply a multi-sorted substitution �, mapping type variables ⌧ toground types, declaration variables � to ground declarations andscope variables & to ground scopes. Thus, our overall definition ofsatisfaction for a program p is:

�(CG

p

), |= �(CResp

) ^ �(CTyp

) (⇧)

where �(E) denotes the application of the substitution � to all thevariables appearing in E that are in the domain of �. When theproposition ⇧ holds we say that and � resolve p.

Resolution and Typing Constraints The |= relation is given bythe inductive rules in Fig. 8, where = is the syntactic equality onterms and `G x

Ri

7�! x

Dj

is the resolution relation for graph G.The interpretation of a name collection JNKG is the multiset definedas follows: JD(S)KG = ⇡(DG(S)), JR(S)KG = ⇡(RG(S)), andJV(S)KG = ⇡({xD

i

| 9p, `G p : S 7�! x

Di

}) where ⇡(A) isthe multiset produced by projecting the identifiers from a set A ofreferences or declarations. Given a multiset M , 1

M

(x) denotes themultiplicity of x in M .

3.3 Resolution Calculus

The resolution calculus defines the resolution of a reference to adeclaration in a scope graph as a most specific, well-formed pathfrom reference to declaration through a sequence of edges. A pathp is a list of steps representing the atomic scope transitions in thegraph. There are three kinds of steps:• A (direct) edge step E(l, S2) is a direct transition from the

current scope to the scope S2. This step records the label ofthe scope transition that is used.

• A nominal edge step N(l, yR, S) requires the resolution of ref-

erence y

R to a declaration with associated scope S to allow atransition between the current scope and scope S.

• A complete path always ends with a declaration step D(xD) thatstores the declaration the path is leading to.

A path p is a valid resolution in the graph from reference x

Ri

to declaration x

Di

such that `G p : x

Ri

7�! x

Di

according tothe calculus rules in Fig. 9. These rules all implicitly apply toa fixed graph G, which we omit to avoid clutter. The calculusdefines the resolution relation in terms of edges in the scope graph,reachable declarations, and visible declarations. Here I is the set ofseen imports, a technical device needed to avoid “out of thin air”anomalies in resolution of nominal imports. We often drop I froma resolution when it is empty. The S component that appears inthe transitive closure rules is the set of seen scopes that is used toprevent cycles in the resolution path of a given reference.

G, |= True

(C-TRUE)

G, |= C1 G, |= C2

G, |= C1 ^ C2(C-AND)

(d) = T

G, |= d : T(C-TYPEOF)

`G p : xRi

7! x

Dj

G, |= x

Ri

7! x

Dj

(C-RESOLVE)

d GS

G, |= d S

(C-SCOPEOF)

8x,1JNKG (x) 1

G, |= !N(C-UNIQUE)

JN1KG ✓ JN2KGG, |= N1 ⇢⇠ N2

(C-SUBNAME)

t1 = t2

G, |= t1 ⌘ t2(C-EQ)

Figure 8. Interpretation of resolution and typing constraints

Resolution paths

s := D(xDi

) | E(l, S) | N(l, xRi

, S)

p := [] | s | p · p (inductively generated)[] · p = p · [] = p

(p1 · p2) · p3 = p1 · (p2 · p3)Well-formed paths

WF(p) , labels(p) 2 EVisibility ordering on paths

label(s1) < label(s2)s1 · p1 < s2 · p2

p1 < p2

s · p1 < s · p2Edges in scope graph

S1l

S2

I ` E(l, S2) : S1 �! S2(E)

S1l

y

Ri

y

Ri

/2 I I ` p : yRi

7�! y

Dj

y

Dj

S2

I ` N(l, yRi

, 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⇣ S

0 WF(p) S

0x

Di

I ` p · D(xDi

) : S⇢ x

Di

(R)

Visible declarations

I ` p : S⇢ x

Di

8j, p0(I ` p

0 : S⇢ x

Dj

) ¬(p0 < p))

I ` p : S 7�! x

Di

(V )

Reference resolution

x

Ri

S {xRi

} [ I ` p : S 7�! x

Dj

I ` p : xRi

7�! x

Dj

(X)

Figure 9. Resolution calculus from [14] extended for arbitraryedge labels and parameterized with well-formedness predicate WFand visibility ordering <. Here label projects the label from a stepand labels projects the sequence of labels from a path.

Page 154: Static name resolution

Visibility Policies

Lexical scopeL := {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 IncludesL := {P, Inc} E := P

⇤ · Inc

⇤D < P, Inc < P

Transitive includes and imports, and non-transitive importsL := {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

In order to model the name binding features and resolution poli-cies from different programming languages, the scope graph andresolution calculus are parameterized with a set of labels L, a reg-ular expression E that defines the scope reachability policy, and anorder < on the L (extended with the built-in D label) that definesthe scope visibility policy. Fig. 9 defines generic predicates derivedfrom these parameters and used in the calculus. The regular expres-sion E entails a well-formedness predicate WF on paths obtainedby projecting the sequence of labels from the path and testing it formembership in the language of E . The ordering relation on labelsentails an ordering relation on paths using the lexicographic orderon the projected label sequences.

Fig. 10 presents several example instantiations for these param-eters, encoding different policies. The first policy defines lexicalscope in which scopes are transitively linked through parent edges(P) and local declarations shadow declarations in parents. The nextpolicy extends lexical scope with non-transitive imports (I). Thewell-formedness predicate allows an optional import at the end ofa lexical scope chain, ruling out access to the parents of an im-ported scope. Further, the policy states that imported declarationsshadow declarations in the lexical context. The transitive importspolicy extends this by allowing paths with a chain of imports (TI).The transitive includes policy is a variation in which local decla-rations do not shadow included (Inc) declarations. The final policycombines three import policies, not providing rules to disambiguatebetween paths through different kinds of import edges. Thus, a ref-erence that can be resolved through an import and an include edgeis ambiguous and can be flagged as an error.

4. Resolution Algorithm

In this section, we describe an algorithm for solving constraints inthe sense of Section 3.2, i.e. finding � and that satisfy (⇧). Ouralgorithm works only for a restricted class of generated constraints:all constraints in CG

p

must be ground, except that scope variables &can appear as targets in direct edge constraints (e.g. S l

&). Thisrestriction is met by the constraints generated by the LMR collec-tion algorithm in Section 2. Broader classes of constraints might beuseful for other languages; we defer exploration of algorithms thatcould handle these to future work.

4.1 Variables in Scope Graph Constraints

The basic approach of the algorithm is to interpret the scope graphconstraints as a scope graph G and then use it to resolve resolu-tion and typing constraints using a conventional unification-based

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 = ?EnvL[{D}

re

[I, S](S)

EnvLre

[I, S](S) :=[

l2Max(L)

⇣Env{l

02L|l0<l}re

[I, S](S)� Envlre

[I, S](S)⌘

EnvD

re

[I, S](S) :=((T, ;) if [] /2 re

(T, D(S))

Envlre

[I, S](S) :=

8><

>:

(P, ;) if SIl

contains a variable or ISl[I](S) = US

S

02⇣

ISl[I](S)[S

Il

⌘Env(l�1re)[I, {S} [ S](S0)

ISl[I](S) :=(U if 9yR 2 (S

Bl

\I) s.t. R[I](yR) = U

{S0 | yR2 (SBl

\I) ^ y

D2 R[I](yR) ^ y

DS

0}

Figure 11. Name Resolution Algorithm

algorithm. However, since scope graph constraints can contain vari-ables, we cannot fully define the scope graph before starting con-straint resolution, because we do not fully know �. Thus, our algo-rithm builds � (and ) incrementally. The key idea is that we cansolve some resolution and typing constraints even when � is notyet fully defined, in such a way that the solution remains valid as itbecomes more defined.

4.2 Name Resolution Algorithm

In order to solve resolution constraints (e.g. xR 7! �) or to com-pute the set of visible elements from a scope (V(S)) we need analgorithm that computes the name resolution relation (xR

i

7�! x

Dj

)specified by the calculus presented in Section 3.3. We introducedsuch an algorithm in our prior work [14], but it was specific to aparticular set of labels, visibility order, and well-formedness predi-cate. In this section, we present a generic version of the algorithmthat is parameterized by L, E and < as described in Section 3.4.

Incomplete Scope Graphs A further new requirement on thealgorithm is that it can operate on an incomplete scope graph,specified by a set of constraints that may still contains variablesas the targets of direct edges. The non-strictly positive premiseof the (V) rule of the resolution calculus makes the derivation ofa resolution relation from a graph non-monotonic with respect toadditions to the graph. For example, suppose that in some graphG a reference x

R in a scope S resolves to declaration x

Di

in theparent scope S

0. In a bigger graph G0 that also has a declarationx

Di

0 in S itself, xR will resolve to x

Di

0 , and the old resolution to x

Di

will be shadowed. Thus we cannot simply restrict resolution to thecomplete part of the graph, and expect the results to remain validas the graph becomes more completely known. Instead, we modifythe original algorithm to signal when a result is preliminary.

The Algorithm Fig. 11 defines a resolution algorithm that workson such incomplete scope graphs.The function for resolving a sin-gle reference, R[I](xR), returns either a set of declarations or U(unknown) if the reference cannot be resolved in the current graph.Similarly, the environment functions Env _

re

[I, S](S) return a pairconsisting of:

• a result flag, T (total) if all declarations visible from S canbe computed or P (partial) if there are still possible additionalresolutions (some scope variables are accessible)

Page 155: Static name resolution

Seen Imports

13

A

D2 :SA2 2 D(S

A1)

A

R4 2 I(S

root

)

A

R4 2 R(S

root

) A

D1 :SA1 2 D(S

root

)

A

R4 7�! A

D1 :SA1

S

root

�! S

A1 (⇤)S

root

⇢ A

D2 :SA2

A

R4 2 R(S

root

) S

root

7�! A

D2 :SA2

A

R4 7�! A

D2 :SA2

Fig. 10. Derivation for A

R4 7�! A

D2 :SA2 in a calculus without import tracking.

The complete definition of well-formed paths and specificity order on paths isgiven in Fig. 2. In Section 2.5 we discuss how alternative visibility policies canbe defined by just changing the well-formedness predicate and specificity order.

module A1 {

module A2 {

def a3 = ...

}

}

import A4

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

Seen imports. Consider the example in Fig. 11. Is declarationa3 reachable in the scope of reference a6? This reduces to thequestion whether the import of A4 can resolve to moduleA2. Surprisingly, it can, in the calculus as discussed so far,as shown by the derivation in Fig. 10 (which takes a fewshortcuts). The conclusion of the derivation is that A

R4 7�!

A

D2 :SA2 . This conclusion is obtained by using the import at A4

to conclude at step (*) that Sroot

�! S

A1 , i.e. that the bodyof module A1 is reachable! In other words, the import of A4is used in its own resolution. Intuitively, this is nonsensical.

To rule out this kind of behavior we extend the calculusto keep track of the set of seen imports I using judgementsof the form I ` p : xR

i

7�! x

Dj

. We need to extend all rules topass the set I, but only the rules for resolution and importare truly affected:

x

Ri

2 R(S) {xRi

} [ I ` p : S 7�! x

Dj

I ` p : xRi

7�! x

Dj

(X)

y

Ri

2 I(S1) \ I I ` p : yRi

7�! y

Dj

:S2

I ` I(yRi

, y

Dj

:S2) : S1 �! S2

(I)

With this final ingredient, we reach the full calculus inFig. 3. It is not hard to see that the resolution relation iswell-founded. The only recursive invocation (via the I rule)uses a strictly larger set I of seen imports (via the X rule); since the set R(G)is finite, I cannot grow indefinitely.

Anomalies. Although the calculus produces the desired resolutions for a widevariety of real language constructs, its behavior can be surprising on corner cases.Even with the “seen imports” mechanism, it is still possible for a single derivation

?

13

A

D2 :SA2 2 D(S

A1)

A

R4 2 I(S

root

)

A

R4 2 R(S

root

) A

D1 :SA1 2 D(S

root

)

A

R4 7�! A

D1 :SA1

S

root

�! S

A1 (⇤)S

root

⇢ A

D2 :SA2

A

R4 2 R(S

root

) S

root

7�! A

D2 :SA2

A

R4 7�! A

D2 :SA2

Fig. 10. Derivation for A

R4 7�! A

D2 :SA2 in a calculus without import tracking.

The complete definition of well-formed paths and specificity order on paths isgiven in Fig. 2. In Section 2.5 we discuss how alternative visibility policies canbe defined by just changing the well-formedness predicate and specificity order.

module A1 {

module A2 {

def a3 = ...

}

}

import A4

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

Seen imports. Consider the example in Fig. 11. Is declarationa3 reachable in the scope of reference a6? This reduces to thequestion whether the import of A4 can resolve to moduleA2. Surprisingly, it can, in the calculus as discussed so far,as shown by the derivation in Fig. 10 (which takes a fewshortcuts). The conclusion of the derivation is that A

R4 7�!

A

D2 :SA2 . This conclusion is obtained by using the import at A4

to conclude at step (*) that Sroot

�! S

A1 , i.e. that the bodyof module A1 is reachable! In other words, the import of A4is used in its own resolution. Intuitively, this is nonsensical.

To rule out this kind of behavior we extend the calculusto keep track of the set of seen imports I using judgementsof the form I ` p : xR

i

7�! x

Dj

. We need to extend all rules topass the set I, but only the rules for resolution and importare truly affected:

x

Ri

2 R(S) {xRi

} [ I ` p : S 7�! x

Dj

I ` p : xRi

7�! x

Dj

(X)

y

Ri

2 I(S1) \ I I ` p : yRi

7�! y

Dj

:S2

I ` I(yRi

, y

Dj

:S2) : S1 �! S2

(I)

With this final ingredient, we reach the full calculus inFig. 3. It is not hard to see that the resolution relation iswell-founded. The only recursive invocation (via the I rule)uses a strictly larger set I of seen imports (via the X rule); since the set R(G)is finite, I cannot grow indefinitely.

Anomalies. Although the calculus produces the desired resolutions for a widevariety of real language constructs, its behavior can be surprising on corner cases.Even with the “seen imports” mechanism, it is still possible for a single derivation

13

A

D2 :SA2 2 D(S

A1)

A

R4 2 I(S

root

)

A

R4 2 R(S

root

) A

D1 :SA1 2 D(S

root

)

A

R4 7�! A

D1 :SA1

S

root

�! S

A1 (⇤)S

root

⇢ A

D2 :SA2

A

R4 2 R(S

root

) S

root

7�! A

D2 :SA2

A

R4 7�! A

D2 :SA2

Fig. 10. Derivation for A

R4 7�! A

D2 :SA2 in a calculus without import tracking.

The complete definition of well-formed paths and specificity order on paths isgiven in Fig. 2. In Section 2.5 we discuss how alternative visibility policies canbe defined by just changing the well-formedness predicate and specificity order.

module A1 {

module A2 {

def a3 = ...

}

}

import A4

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

Seen imports. Consider the example in Fig. 11. Is declarationa3 reachable in the scope of reference a6? This reduces to thequestion whether the import of A4 can resolve to moduleA2. Surprisingly, it can, in the calculus as discussed so far,as shown by the derivation in Fig. 10 (which takes a fewshortcuts). The conclusion of the derivation is that A

R4 7�!

A

D2 :SA2 . This conclusion is obtained by using the import at A4

to conclude at step (*) that Sroot

�! S

A1 , i.e. that the bodyof module A1 is reachable! In other words, the import of A4is used in its own resolution. Intuitively, this is nonsensical.

To rule out this kind of behavior we extend the calculusto keep track of the set of seen imports I using judgementsof the form I ` p : xR

i

7�! x

Dj

. We need to extend all rules topass the set I, but only the rules for resolution and importare truly affected:

x

Ri

2 R(S) {xRi

} [ I ` p : S 7�! x

Dj

I ` p : xRi

7�! x

Dj

(X)

y

Ri

2 I(S1) \ I I ` p : yRi

7�! y

Dj

:S2

I ` I(yRi

, y

Dj

:S2) : S1 �! S2

(I)

With this final ingredient, we reach the full calculus inFig. 3. It is not hard to see that the resolution relation iswell-founded. The only recursive invocation (via the I rule)uses a strictly larger set I of seen imports (via the X rule); since the set R(G)is finite, I cannot grow indefinitely.

Anomalies. Although the calculus produces the desired resolutions for a widevariety of real language constructs, its behavior can be surprising on corner cases.Even with the “seen imports” mechanism, it is still possible for a single derivation

Page 156: Static name resolution

13

A

D2 :SA2 2 D(S

A1)

A

R4 2 I(S

root

)

A

R4 2 R(S

root

) A

D1 :SA1 2 D(S

root

)

A

R4 7�! A

D1 :SA1

S

root

�! S

A1 (⇤)S

root

⇢ A

D2 :SA2

A

R4 2 R(S

root

) S

root

7�! A

D2 :SA2

A

R4 7�! A

D2 :SA2

Fig. 10. Derivation for A

R4 7�! A

D2 :SA2 in a calculus without import tracking.

The complete definition of well-formed paths and specificity order on paths isgiven in Fig. 2. In Section 2.5 we discuss how alternative visibility policies canbe defined by just changing the well-formedness predicate and specificity order.

module A1 {

module A2 {

def a3 = ...

}

}

import A4

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

Seen imports. Consider the example in Fig. 11. Is declarationa3 reachable in the scope of reference a6? This reduces to thequestion whether the import of A4 can resolve to moduleA2. Surprisingly, it can, in the calculus as discussed so far,as shown by the derivation in Fig. 10 (which takes a fewshortcuts). The conclusion of the derivation is that A

R4 7�!

A

D2 :SA2 . This conclusion is obtained by using the import at A4

to conclude at step (*) that Sroot

�! S

A1 , i.e. that the bodyof module A1 is reachable! In other words, the import of A4is used in its own resolution. Intuitively, this is nonsensical.

To rule out this kind of behavior we extend the calculusto keep track of the set of seen imports I using judgementsof the form I ` p : xR

i

7�! x

Dj

. We need to extend all rules topass the set I, but only the rules for resolution and importare truly affected:

x

Ri

2 R(S) {xRi

} [ I ` p : S 7�! x

Dj

I ` p : xRi

7�! x

Dj

(X)

y

Ri

2 I(S1) \ I I ` p : yRi

7�! y

Dj

:S2

I ` I(yRi

, y

Dj

:S2) : S1 �! S2

(I)

With this final ingredient, we reach the full calculus inFig. 3. It is not hard to see that the resolution relation iswell-founded. The only recursive invocation (via the I rule)uses a strictly larger set I of seen imports (via the X rule); since the set R(G)is finite, I cannot grow indefinitely.

Anomalies. Although the calculus produces the desired resolutions for a widevariety of real language constructs, its behavior can be surprising on corner cases.Even with the “seen imports” mechanism, it is still possible for a single derivation

Anomaly

13

A

D2 :SA2 2 D(S

A1)

A

R4 2 I(S

root

)

A

R4 2 R(S

root

) A

D1 :SA1 2 D(S

root

)

A

R4 7�! A

D1 :SA1

S

root

�! S

A1 (⇤)S

root

⇢ A

D2 :SA2

A

R4 2 R(S

root

) S

root

7�! A

D2 :SA2

A

R4 7�! A

D2 :SA2

Fig. 10. Derivation for A

R4 7�! A

D2 :SA2 in a calculus without import tracking.

The complete definition of well-formed paths and specificity order on paths isgiven in Fig. 2. In Section 2.5 we discuss how alternative visibility policies canbe defined by just changing the well-formedness predicate and specificity order.

module A1 {

module A2 {

def a3 = ...

}

}

import A4

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

Seen imports. Consider the example in Fig. 11. Is declarationa3 reachable in the scope of reference a6? This reduces to thequestion whether the import of A4 can resolve to moduleA2. Surprisingly, it can, in the calculus as discussed so far,as shown by the derivation in Fig. 10 (which takes a fewshortcuts). The conclusion of the derivation is that A

R4 7�!

A

D2 :SA2 . This conclusion is obtained by using the import at A4

to conclude at step (*) that Sroot

�! S

A1 , i.e. that the bodyof module A1 is reachable! In other words, the import of A4is used in its own resolution. Intuitively, this is nonsensical.

To rule out this kind of behavior we extend the calculusto keep track of the set of seen imports I using judgementsof the form I ` p : xR

i

7�! x

Dj

. We need to extend all rules topass the set I, but only the rules for resolution and importare truly affected:

x

Ri

2 R(S) {xRi

} [ I ` p : S 7�! x

Dj

I ` p : xRi

7�! x

Dj

(X)

y

Ri

2 I(S1) \ I I ` p : yRi

7�! y

Dj

:S2

I ` I(yRi

, y

Dj

:S2) : S1 �! S2

(I)

With this final ingredient, we reach the full calculus inFig. 3. It is not hard to see that the resolution relation iswell-founded. The only recursive invocation (via the I rule)uses a strictly larger set I of seen imports (via the X rule); since the set R(G)is finite, I cannot grow indefinitely.

Anomalies. Although the calculus produces the desired resolutions for a widevariety of real language constructs, its behavior can be surprising on corner cases.Even with the “seen imports” mechanism, it is still possible for a single derivation

21

3

1

3

2

Page 157: Static name resolution

Resolution Algorithm

Lexical scopeL := {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 IncludesL := {P, Inc} E := P

⇤ · Inc

⇤D < P, Inc < P

Transitive includes and imports, and non-transitive importsL := {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

In order to model the name binding features and resolution poli-cies from different programming languages, the scope graph andresolution calculus are parameterized with a set of labels L, a reg-ular expression E that defines the scope reachability policy, and anorder < on the L (extended with the built-in D label) that definesthe scope visibility policy. Fig. 9 defines generic predicates derivedfrom these parameters and used in the calculus. The regular expres-sion E entails a well-formedness predicate WF on paths obtainedby projecting the sequence of labels from the path and testing it formembership in the language of E . The ordering relation on labelsentails an ordering relation on paths using the lexicographic orderon the projected label sequences.

Fig. 10 presents several example instantiations for these param-eters, encoding different policies. The first policy defines lexicalscope in which scopes are transitively linked through parent edges(P) and local declarations shadow declarations in parents. The nextpolicy extends lexical scope with non-transitive imports (I). Thewell-formedness predicate allows an optional import at the end ofa lexical scope chain, ruling out access to the parents of an im-ported scope. Further, the policy states that imported declarationsshadow declarations in the lexical context. The transitive importspolicy extends this by allowing paths with a chain of imports (TI).The transitive includes policy is a variation in which local decla-rations do not shadow included (Inc) declarations. The final policycombines three import policies, not providing rules to disambiguatebetween paths through different kinds of import edges. Thus, a ref-erence that can be resolved through an import and an include edgeis ambiguous and can be flagged as an error.

4. Resolution Algorithm

In this section, we describe an algorithm for solving constraints inthe sense of Section 3.2, i.e. finding � and that satisfy (⇧). Ouralgorithm works only for a restricted class of generated constraints:all constraints in CG

p

must be ground, except that scope variables &can appear as targets in direct edge constraints (e.g. S l

&). Thisrestriction is met by the constraints generated by the LMR collec-tion algorithm in Section 2. Broader classes of constraints might beuseful for other languages; we defer exploration of algorithms thatcould handle these to future work.

4.1 Variables in Scope Graph Constraints

The basic approach of the algorithm is to interpret the scope graphconstraints as a scope graph G and then use it to resolve resolu-tion and typing constraints using a conventional unification-based

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 = ?EnvL[{D}

re

[I, S](S)

EnvLre

[I, S](S) :=[

l2Max(L)

⇣Env{l

02L|l0<l}re

[I, S](S)� Envlre

[I, S](S)⌘

EnvD

re

[I, S](S) :=((T, ;) if [] /2 re

(T, D(S))

Envlre

[I, S](S) :=

8><

>:

(P, ;) if SIl

contains a variable or ISl[I](S) = US

S

02⇣

ISl[I](S)[S

Il

⌘Env(l�1re)[I, {S} [ S](S0)

ISl[I](S) :=(U if 9yR 2 (S

Bl

\I) s.t. R[I](yR) = U

{S0 | yR2 (SBl

\I) ^ y

D2 R[I](yR) ^ y

DS

0}

Figure 11. Name Resolution Algorithm

algorithm. However, since scope graph constraints can contain vari-ables, we cannot fully define the scope graph before starting con-straint resolution, because we do not fully know �. Thus, our algo-rithm builds � (and ) incrementally. The key idea is that we cansolve some resolution and typing constraints even when � is notyet fully defined, in such a way that the solution remains valid as itbecomes more defined.

4.2 Name Resolution Algorithm

In order to solve resolution constraints (e.g. xR 7! �) or to com-pute the set of visible elements from a scope (V(S)) we need analgorithm that computes the name resolution relation (xR

i

7�! x

Dj

)specified by the calculus presented in Section 3.3. We introducedsuch an algorithm in our prior work [14], but it was specific to aparticular set of labels, visibility order, and well-formedness predi-cate. In this section, we present a generic version of the algorithmthat is parameterized by L, E and < as described in Section 3.4.

Incomplete Scope Graphs A further new requirement on thealgorithm is that it can operate on an incomplete scope graph,specified by a set of constraints that may still contains variablesas the targets of direct edges. The non-strictly positive premiseof the (V) rule of the resolution calculus makes the derivation ofa resolution relation from a graph non-monotonic with respect toadditions to the graph. For example, suppose that in some graphG a reference x

R in a scope S resolves to declaration x

Di

in theparent scope S

0. In a bigger graph G0 that also has a declarationx

Di

0 in S itself, xR will resolve to x

Di

0 , and the old resolution to x

Di

will be shadowed. Thus we cannot simply restrict resolution to thecomplete part of the graph, and expect the results to remain validas the graph becomes more completely known. Instead, we modifythe original algorithm to signal when a result is preliminary.

The Algorithm Fig. 11 defines a resolution algorithm that workson such incomplete scope graphs.The function for resolving a sin-gle reference, R[I](xR), returns either a set of declarations or U(unknown) if the reference cannot be resolved in the current graph.Similarly, the environment functions Env _

re

[I, S](S) return a pairconsisting of:

• a result flag, T (total) if all declarations visible from S canbe computed or P (partial) if there are still possible additionalresolutions (some scope variables are accessible)

Page 158: Static name resolution

Alpha Equivalence

Page 159: Static name resolution

Language-independent 𝜶-equivalence

Program similarity

24

5.1 ↵-Equivalence

We now define ↵-equivalence using scope graphs. Except for the leaves represent-ing identifiers, two ↵-equivalent programs must have the same abstract syntaxtree. We write P ' P’ (pronounced “P and P’ are similar”) when the ASTs of Pand P’ are equal up to identifiers. To compare two programs we first comparetheir AST structures; if these are equal then we compare how identifiers behavein these programs. Since two potentially ↵-equivalent programs are similar, theidentifiers occur at the same positions. In order to compare the identifiers’ behav-ior, we define equivalence classes of positions of identifiers in a program: positionsin the same equivalence class are declarations of or reference to the same entity.The abstract position x̄ identifies the equivalence class corresponding to the freevariable x.

Given a program P, we write P for the set of positions corresponding toreferences and declarations and PX for P extended with the artificial positions(e.g. x̄). We define the P⇠ equivalence relation between elements of PX as thereflexive symmetric and transitive closure of the resolution relation.

Definition 7 (Position equivalence).

` p : r i

x

7�! d i

0

x

iP⇠ i0

i0P⇠ i

iP⇠ i0

iP⇠ i0 i0

P⇠ i00

iP⇠ i00 i

P⇠ i

In this equivalence relation, the class containing the abstract free variable dec-laration can not contain any other declaration. So the references in a particularclass are either all free or all bound.

Lemma 6 (Free variable class). The equivalence class of a free variable doesnot contain any other declaration, i.e. 8 d i

x

s.t. iP⇠ 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 d i

0

x

, p ` r i

x

7�! d i

0

x

=) i0 = x̄ ^ p = >and then proceed by induction on the equivalence relation.

The equivalence classes defined by this relation contain references to or declara-tions of the same entity. Given this relation, we can state that two program are↵-equivalent if the identifiers at identical positions refer to the same entity, thatis belong to the same equivalence class:

Definition 8 (↵-equivalence). Two programs P1 and P2 are ↵-equivalent (de-noted P1

↵⇡ P2) when they are similar and have the same ⇠-equivalence classes:

P1↵⇡ P2 , P1 ' P2 ^ 8 e e0, e

P1⇠ e0 , eP2⇠ e0

Remark 1.↵⇡ is an equivalence relation since ' and , are equivalence relations.

if have same AST ignoring identifier names

Page 160: Static name resolution

Language-independent 𝜶-equivalence

Position equivalence

24

5.1 ↵-Equivalence

We now define ↵-equivalence using scope graphs. Except for the leaves represent-ing identifiers, two ↵-equivalent programs must have the same abstract syntaxtree. We write P ' P’ (pronounced “P and P’ are similar”) when the ASTs of Pand P’ are equal up to identifiers. To compare two programs we first comparetheir AST structures; if these are equal then we compare how identifiers behavein these programs. Since two potentially ↵-equivalent programs are similar, theidentifiers occur at the same positions. In order to compare the identifiers’ behav-ior, we define equivalence classes of positions of identifiers in a program: positionsin the same equivalence class are declarations of or reference to the same entity.The abstract position x̄ identifies the equivalence class corresponding to the freevariable x.

Given a program P, we write P for the set of positions corresponding toreferences and declarations and PX for P extended with the artificial positions(e.g. x̄). We define the P⇠ equivalence relation between elements of PX as thereflexive symmetric and transitive closure of the resolution relation.

Definition 7 (Position equivalence).

` p : r i

x

7�! d i

0

x

iP⇠ i0

i0P⇠ i

iP⇠ i0

iP⇠ i0 i0

P⇠ i00

iP⇠ i00 i

P⇠ i

In this equivalence relation, the class containing the abstract free variable dec-laration can not contain any other declaration. So the references in a particularclass are either all free or all bound.

Lemma 6 (Free variable class). The equivalence class of a free variable doesnot contain any other declaration, i.e. 8 d i

x

s.t. iP⇠ 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 d i

0

x

, p ` r i

x

7�! d i

0

x

=) i0 = x̄ ^ p = >and then proceed by induction on the equivalence relation.

The equivalence classes defined by this relation contain references to or declara-tions of the same entity. Given this relation, we can state that two program are↵-equivalent if the identifiers at identical positions refer to the same entity, thatis belong to the same equivalence class:

Definition 8 (↵-equivalence). Two programs P1 and P2 are ↵-equivalent (de-noted P1

↵⇡ P2) when they are similar and have the same ⇠-equivalence classes:

P1↵⇡ P2 , P1 ' P2 ^ 8 e e0, e

P1⇠ e0 , eP2⇠ e0

Remark 1.↵⇡ is an equivalence relation since ' and , are equivalence relations.

xi xi'

Program similarity

24

5.1 ↵-Equivalence

We now define ↵-equivalence using scope graphs. Except for the leaves represent-ing identifiers, two ↵-equivalent programs must have the same abstract syntaxtree. We write P ' P’ (pronounced “P and P’ are similar”) when the ASTs of Pand P’ are equal up to identifiers. To compare two programs we first comparetheir AST structures; if these are equal then we compare how identifiers behavein these programs. Since two potentially ↵-equivalent programs are similar, theidentifiers occur at the same positions. In order to compare the identifiers’ behav-ior, we define equivalence classes of positions of identifiers in a program: positionsin the same equivalence class are declarations of or reference to the same entity.The abstract position x̄ identifies the equivalence class corresponding to the freevariable x.

Given a program P, we write P for the set of positions corresponding toreferences and declarations and PX for P extended with the artificial positions(e.g. x̄). We define the P⇠ equivalence relation between elements of PX as thereflexive symmetric and transitive closure of the resolution relation.

Definition 7 (Position equivalence).

` p : r i

x

7�! d i

0

x

iP⇠ i0

i0P⇠ i

iP⇠ i0

iP⇠ i0 i0

P⇠ i00

iP⇠ i00 i

P⇠ i

In this equivalence relation, the class containing the abstract free variable dec-laration can not contain any other declaration. So the references in a particularclass are either all free or all bound.

Lemma 6 (Free variable class). The equivalence class of a free variable doesnot contain any other declaration, i.e. 8 d i

x

s.t. iP⇠ 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 d i

0

x

, p ` r i

x

7�! d i

0

x

=) i0 = x̄ ^ p = >and then proceed by induction on the equivalence relation.

The equivalence classes defined by this relation contain references to or declara-tions of the same entity. Given this relation, we can state that two program are↵-equivalent if the identifiers at identical positions refer to the same entity, thatis belong to the same equivalence class:

Definition 8 (↵-equivalence). Two programs P1 and P2 are ↵-equivalent (de-noted P1

↵⇡ P2) when they are similar and have the same ⇠-equivalence classes:

P1↵⇡ P2 , P1 ' P2 ^ 8 e e0, e

P1⇠ e0 , eP2⇠ e0

Remark 1.↵⇡ is an equivalence relation since ' and , are equivalence relations.

if have same AST ignoring identifier names

Page 161: Static name resolution

Language-independent 𝜶-equivalence

Position equivalence

24

5.1 ↵-Equivalence

We now define ↵-equivalence using scope graphs. Except for the leaves represent-ing identifiers, two ↵-equivalent programs must have the same abstract syntaxtree. We write P ' P’ (pronounced “P and P’ are similar”) when the ASTs of Pand P’ are equal up to identifiers. To compare two programs we first comparetheir AST structures; if these are equal then we compare how identifiers behavein these programs. Since two potentially ↵-equivalent programs are similar, theidentifiers occur at the same positions. In order to compare the identifiers’ behav-ior, we define equivalence classes of positions of identifiers in a program: positionsin the same equivalence class are declarations of or reference to the same entity.The abstract position x̄ identifies the equivalence class corresponding to the freevariable x.

Given a program P, we write P for the set of positions corresponding toreferences and declarations and PX for P extended with the artificial positions(e.g. x̄). We define the P⇠ equivalence relation between elements of PX as thereflexive symmetric and transitive closure of the resolution relation.

Definition 7 (Position equivalence).

` p : r i

x

7�! d i

0

x

iP⇠ i0

i0P⇠ i

iP⇠ i0

iP⇠ i0 i0

P⇠ i00

iP⇠ i00 i

P⇠ i

In this equivalence relation, the class containing the abstract free variable dec-laration can not contain any other declaration. So the references in a particularclass are either all free or all bound.

Lemma 6 (Free variable class). The equivalence class of a free variable doesnot contain any other declaration, i.e. 8 d i

x

s.t. iP⇠ 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 d i

0

x

, p ` r i

x

7�! d i

0

x

=) i0 = x̄ ^ p = >and then proceed by induction on the equivalence relation.

The equivalence classes defined by this relation contain references to or declara-tions of the same entity. Given this relation, we can state that two program are↵-equivalent if the identifiers at identical positions refer to the same entity, thatis belong to the same equivalence class:

Definition 8 (↵-equivalence). Two programs P1 and P2 are ↵-equivalent (de-noted P1

↵⇡ P2) when they are similar and have the same ⇠-equivalence classes:

P1↵⇡ P2 , P1 ' P2 ^ 8 e e0, e

P1⇠ e0 , eP2⇠ e0

Remark 1.↵⇡ is an equivalence relation since ' and , are equivalence relations.

xi xi'

Program similarity

24

5.1 ↵-Equivalence

We now define ↵-equivalence using scope graphs. Except for the leaves represent-ing identifiers, two ↵-equivalent programs must have the same abstract syntaxtree. We write P ' P’ (pronounced “P and P’ are similar”) when the ASTs of Pand P’ are equal up to identifiers. To compare two programs we first comparetheir AST structures; if these are equal then we compare how identifiers behavein these programs. Since two potentially ↵-equivalent programs are similar, theidentifiers occur at the same positions. In order to compare the identifiers’ behav-ior, we define equivalence classes of positions of identifiers in a program: positionsin the same equivalence class are declarations of or reference to the same entity.The abstract position x̄ identifies the equivalence class corresponding to the freevariable x.

Given a program P, we write P for the set of positions corresponding toreferences and declarations and PX for P extended with the artificial positions(e.g. x̄). We define the P⇠ equivalence relation between elements of PX as thereflexive symmetric and transitive closure of the resolution relation.

Definition 7 (Position equivalence).

` p : r i

x

7�! d i

0

x

iP⇠ i0

i0P⇠ i

iP⇠ i0

iP⇠ i0 i0

P⇠ i00

iP⇠ i00 i

P⇠ i

In this equivalence relation, the class containing the abstract free variable dec-laration can not contain any other declaration. So the references in a particularclass are either all free or all bound.

Lemma 6 (Free variable class). The equivalence class of a free variable doesnot contain any other declaration, i.e. 8 d i

x

s.t. iP⇠ 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 d i

0

x

, p ` r i

x

7�! d i

0

x

=) i0 = x̄ ^ p = >and then proceed by induction on the equivalence relation.

The equivalence classes defined by this relation contain references to or declara-tions of the same entity. Given this relation, we can state that two program are↵-equivalent if the identifiers at identical positions refer to the same entity, thatis belong to the same equivalence class:

Definition 8 (↵-equivalence). Two programs P1 and P2 are ↵-equivalent (de-noted P1

↵⇡ P2) when they are similar and have the same ⇠-equivalence classes:

P1↵⇡ P2 , P1 ' P2 ^ 8 e e0, e

P1⇠ e0 , eP2⇠ e0

Remark 1.↵⇡ is an equivalence relation since ' and , are equivalence relations.

if have same AST ignoring identifier names

24

5.1 ↵-Equivalence

We now define ↵-equivalence using scope graphs. Except for the leaves represent-ing identifiers, two ↵-equivalent programs must have the same abstract syntaxtree. We write P ' P’ (pronounced “P and P’ are similar”) when the ASTs of Pand P’ are equal up to identifiers. To compare two programs we first comparetheir AST structures; if these are equal then we compare how identifiers behavein these programs. Since two potentially ↵-equivalent programs are similar, theidentifiers occur at the same positions. In order to compare the identifiers’ behav-ior, we define equivalence classes of positions of identifiers in a program: positionsin the same equivalence class are declarations of or reference to the same entity.The abstract position x̄ identifies the equivalence class corresponding to the freevariable x.

Given a program P, we write P for the set of positions corresponding toreferences and declarations and PX for P extended with the artificial positions(e.g. x̄). We define the P⇠ equivalence relation between elements of PX as thereflexive symmetric and transitive closure of the resolution relation.

Definition 7 (Position equivalence).

` p : r i

x

7�! d i

0

x

iP⇠ i0

i0P⇠ i

iP⇠ i0

iP⇠ i0 i0

P⇠ i00

iP⇠ i00 i

P⇠ i

In this equivalence relation, the class containing the abstract free variable dec-laration can not contain any other declaration. So the references in a particularclass are either all free or all bound.

Lemma 6 (Free variable class). The equivalence class of a free variable doesnot contain any other declaration, i.e. 8 d i

x

s.t. iP⇠ 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 d i

0

x

, p ` r i

x

7�! d i

0

x

=) i0 = x̄ ^ p = >and then proceed by induction on the equivalence relation.

The equivalence classes defined by this relation contain references to or declara-tions of the same entity. Given this relation, we can state that two program are↵-equivalent if the identifiers at identical positions refer to the same entity, thatis belong to the same equivalence class:

Definition 8 (↵-equivalence). Two programs P1 and P2 are ↵-equivalent (de-noted P1

↵⇡ P2) when they are similar and have the same ⇠-equivalence classes:

P1↵⇡ P2 , P1 ' P2 ^ 8 e e0, e

P1⇠ e0 , eP2⇠ e0

Remark 1.↵⇡ is an equivalence relation since ' and , are equivalence relations.(with some further details about free variables)

Alpha equivalence

Page 162: Static name resolution

Preserving ambiguity25

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 declaration

5.2 The

↵⇡ Relation

Free variables. The P⇠ equivalence classes corresponding to free variables x alsocontains the artificial position x̄. Since the equivalence classes of two equivalentprograms P1 and P2 have to be exactly the same, every element equivalent tox̄ (i.e. a free reference) in P1 is also equivalent to x̄ in P2. Therefore the freereferences of ↵-equivalent programs have to be identical.

Duplicate declarations. The definition allows us to also capture ↵-equivalenceof programs with duplicate declarations. For example, in the program P1 inFigure 20, x9 has a duplicate declaration (it can resolve to x2 through A1 or tox4 through B3) whereas x13 simply resolves to x2 and x17 to x4 . Thus positions2, 4, 9, 13, and 17 are in the same equivalence class. (Similarly, positions 1,6,and 11 form an equivalence class refering to module A and positions 3,7, and15 form an equivalence class refering to module B , while positions 8, 12, and 16each form singleton equivalence classes.) The program P2 is ↵-equivalent to P1:all members of each equivalence class have been consistently renamed. Howeverthe program P3, where only the first declaration of x and its direct referencesare renamed into z , is not ↵-equivalent to P1, z9 now only resolves to z2 thusthis program does not contain the initial ambiguity of P1.

5.3 Renaming

Renaming is the substitution of a bound variable by a new variable throughoutthe program. It has several practical applications such as rename refactoring inan IDE, transformation to program with unique identifiers, or as an intermediatetransformation when implementing capture-avoiding substitution.

P1

24

5.1 ↵-Equivalence

We now define ↵-equivalence using scope graphs. Except for the leaves represent-ing identifiers, two ↵-equivalent programs must have the same abstract syntaxtree. We write P ' P’ (pronounced “P and P’ are similar”) when the ASTs of Pand P’ are equal up to identifiers. To compare two programs we first comparetheir AST structures; if these are equal then we compare how identifiers behavein these programs. Since two potentially ↵-equivalent programs are similar, theidentifiers occur at the same positions. In order to compare the identifiers’ behav-ior, we define equivalence classes of positions of identifiers in a program: positionsin the same equivalence class are declarations of or reference to the same entity.The abstract position x̄ identifies the equivalence class corresponding to the freevariable x.

Given a program P, we write P for the set of positions corresponding toreferences and declarations and PX for P extended with the artificial positions(e.g. x̄). We define the P⇠ equivalence relation between elements of PX as thereflexive symmetric and transitive closure of the resolution relation.

Definition 7 (Position equivalence).

` p : r i

x

7�! d i

0

x

iP⇠ i0

i0P⇠ i

iP⇠ i0

iP⇠ i0 i0

P⇠ i00

iP⇠ i00 i

P⇠ i

In this equivalence relation, the class containing the abstract free variable dec-laration can not contain any other declaration. So the references in a particularclass are either all free or all bound.

Lemma 6 (Free variable class). The equivalence class of a free variable doesnot contain any other declaration, i.e. 8 d i

x

s.t. iP⇠ 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 d i

0

x

, p ` r i

x

7�! d i

0

x

=) i0 = x̄ ^ p = >and then proceed by induction on the equivalence relation.

The equivalence classes defined by this relation contain references to or declara-tions of the same entity. Given this relation, we can state that two program are↵-equivalent if the identifiers at identical positions refer to the same entity, thatis belong to the same equivalence class:

Definition 8 (↵-equivalence). Two programs P1 and P2 are ↵-equivalent (de-noted P1

↵⇡ P2) when they are similar and have the same ⇠-equivalence classes:

P1↵⇡ P2 , P1 ' P2 ^ 8 e e0, e

P1⇠ e0 , eP2⇠ e0

Remark 1.↵⇡ is an equivalence relation since ' and , are equivalence relations.

P2 P2

24

5.1 ↵-Equivalence

We now define ↵-equivalence using scope graphs. Except for the leaves represent-ing identifiers, two ↵-equivalent programs must have the same abstract syntaxtree. We write P ' P’ (pronounced “P and P’ are similar”) when the ASTs of Pand P’ are equal up to identifiers. To compare two programs we first comparetheir AST structures; if these are equal then we compare how identifiers behavein these programs. Since two potentially ↵-equivalent programs are similar, theidentifiers occur at the same positions. In order to compare the identifiers’ behav-ior, we define equivalence classes of positions of identifiers in a program: positionsin the same equivalence class are declarations of or reference to the same entity.The abstract position x̄ identifies the equivalence class corresponding to the freevariable x.

Given a program P, we write P for the set of positions corresponding toreferences and declarations and PX for P extended with the artificial positions(e.g. x̄). We define the P⇠ equivalence relation between elements of PX as thereflexive symmetric and transitive closure of the resolution relation.

Definition 7 (Position equivalence).

` p : r i

x

7�! d i

0

x

iP⇠ i0

i0P⇠ i

iP⇠ i0

iP⇠ i0 i0

P⇠ i00

iP⇠ i00 i

P⇠ i

In this equivalence relation, the class containing the abstract free variable dec-laration can not contain any other declaration. So the references in a particularclass are either all free or all bound.

Lemma 6 (Free variable class). The equivalence class of a free variable doesnot contain any other declaration, i.e. 8 d i

x

s.t. iP⇠ 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 d i

0

x

, p ` r i

x

7�! d i

0

x

=) i0 = x̄ ^ p = >and then proceed by induction on the equivalence relation.

The equivalence classes defined by this relation contain references to or declara-tions of the same entity. Given this relation, we can state that two program are↵-equivalent if the identifiers at identical positions refer to the same entity, thatis belong to the same equivalence class:

Definition 8 (↵-equivalence). Two programs P1 and P2 are ↵-equivalent (de-noted P1

↵⇡ P2) when they are similar and have the same ⇠-equivalence classes:

P1↵⇡ P2 , P1 ' P2 ^ 8 e e0, e

P1⇠ e0 , eP2⇠ e0

Remark 1.↵⇡ is an equivalence relation since ' and , are equivalence relations.

P3

Page 163: Static name resolution

Names and Types

Page 164: Static name resolution

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 }

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

Page 165: Static name resolution

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 }

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

Page 166: Static name resolution

Types from Declarationdef x : int = 6

def f = fun (y : int) { x + y }

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

Page 167: Static name resolution

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

Page 168: Static name resolution

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

Page 169: Static name resolution

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

Page 170: Static name resolution

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

Page 171: Static name resolution

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

Page 172: Static name resolution

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

Page 173: Static name resolution

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

Page 174: Static name resolution

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

Page 175: Static name resolution

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

Page 176: Static name resolution

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

Page 177: Static name resolution

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

Page 178: Static name resolution

More on types next week

Page 179: Static name resolution

Closing

Page 180: Static name resolution

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

Page 181: Static name resolution

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

Page 182: Static name resolution

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

Page 183: Static name resolution

Name Resolution

copyrights

86

Page 185: Static name resolution

copyrights

Name Resolution

Pictures

Slide 1: Reference by Ian Charleton, some rights reserved

88