aboutsummaryrefslogtreecommitdiff
path: root/resolve
diff options
context:
space:
mode:
authoralandonovan <adonovan@google.com>2018-08-22 17:44:47 -0400
committerGitHub <noreply@github.com>2018-08-22 17:44:47 -0400
commit4956ce9eeded4014824fb376f36e655ee53809b0 (patch)
treef07fc7b1c6d81c33340b42d49ca524a70f689503 /resolve
parent5b0e788dfd99180b0a1af28ca8c5c2ed08d87f09 (diff)
downloadstarlark-go-4956ce9eeded4014824fb376f36e655ee53809b0.tar.gz
resolve: a non-binding use of a global may precede its binding (#123)
According to the agreed Skylark spec, and unlike the Python semantics currently implemented, this program should yield a dynamic error: print(len) # dynamic error: uninitialized global len = 1 print(len) We now defer the resolution of forward-uses at top level until the end of the module, just as we do for locals. Fixes #116
Diffstat (limited to 'resolve')
-rw-r--r--resolve/resolve.go16
-rw-r--r--resolve/testdata/resolve.sky13
2 files changed, 15 insertions, 14 deletions
diff --git a/resolve/resolve.go b/resolve/resolve.go
index 0843bad..4c1e8f9 100644
--- a/resolve/resolve.go
+++ b/resolve/resolve.go
@@ -30,8 +30,11 @@ package resolve
//
// Python-style resolution requires multiple passes because a name is
// determined to be local to a function only if the function contains a
-// "binding" use of it, and this use may lexically follow a non-binding
-// use. In the first pass, we inspect each function, recording in
+// "binding" use of it; similarly, a name is determined be global (as
+// opposed to predeclared) if the module contains a binding use at the
+// top level. For both locals and globals, a non-binding use may
+// lexically precede the binding to which it is resolved.
+// In the first pass, we inspect each function, recording in
// 'uses' each identifier and the environment block in which it occurs.
// If a use of a name is binding, such as a function parameter or
// assignment, we add the name to the block's bindings mapping and add a
@@ -66,7 +69,8 @@ package resolve
//
// Skylark enforces that all global names are assigned at most once on
// all control flow paths by forbidding if/else statements and loops at
-// top level.
+// top level. A global may be used before it is defined, leading to a
+// dynamic error.
//
// TODO(adonovan): opt: reuse local slots once locals go out of scope.
@@ -332,12 +336,6 @@ func (r *resolver) bind(id *syntax.Ident, allowRebind bool) bool {
}
func (r *resolver) use(id *syntax.Ident) {
- // Reference outside any local (comprehension/function) block?
- if r.env.isModule() {
- r.useGlobal(id)
- return
- }
-
b := r.container()
b.uses = append(b.uses, use{id, r.env})
}
diff --git a/resolve/testdata/resolve.sky b/resolve/testdata/resolve.sky
index 6ddcf20..bbad8e0 100644
--- a/resolve/testdata/resolve.sky
+++ b/resolve/testdata/resolve.sky
@@ -9,8 +9,8 @@ x = 1
_ = x
---
-# premature use of global
-_ = x ### "undefined: x"
+# premature use of global is not a static error; see issue 116.
+_ = x
x = 1
---
@@ -37,6 +37,12 @@ U = 1 # ok
U = 1 ### "cannot reassign global U declared at .*/resolve.sky"
---
+# A global declaration shadows all references to a predeclared; see issue 116.
+
+a = U # ok: U is a reference to the global defined on the next line.
+U = 1
+
+---
# reference to predeclared name
M()
@@ -79,7 +85,6 @@ def f():
i()
i = lambda: 0
-
def g():
f()
@@ -115,8 +120,6 @@ e = 1
---
# This program should resolve successfully but fail dynamically.
-# However, the Java implementation currently reports the dynamic
-# error at the x=2 statement.
x = 1
def f():