From 4956ce9eeded4014824fb376f36e655ee53809b0 Mon Sep 17 00:00:00 2001 From: alandonovan Date: Wed, 22 Aug 2018 17:44:47 -0400 Subject: 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 --- resolve/resolve.go | 16 +++++++--------- resolve/testdata/resolve.sky | 13 ++++++++----- 2 files changed, 15 insertions(+), 14 deletions(-) (limited to 'resolve') 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 --- @@ -36,6 +36,12 @@ M = 2 ### "cannot reassign global M declared at .*/resolve.sky" 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(): -- cgit v1.2.3