aboutsummaryrefslogtreecommitdiff
path: root/resolve/resolve.go
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/resolve.go
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/resolve.go')
-rw-r--r--resolve/resolve.go16
1 files changed, 7 insertions, 9 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})
}