diff options
author | alandonovan <adonovan@google.com> | 2018-08-22 17:44:47 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-08-22 17:44:47 -0400 |
commit | 4956ce9eeded4014824fb376f36e655ee53809b0 (patch) | |
tree | f07fc7b1c6d81c33340b42d49ca524a70f689503 /resolve/resolve.go | |
parent | 5b0e788dfd99180b0a1af28ca8c5c2ed08d87f09 (diff) | |
download | starlark-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.go | 16 |
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}) } |