resolve: a non-binding use of a global may precede its binding

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

Change-Id: I802bded5d50c45702a4da08c40144574cdf4296a
diff --git a/doc/spec.md b/doc/spec.md
index 051e07c..b7a611e 100644
--- a/doc/spec.md
+++ b/doc/spec.md
@@ -1140,6 +1140,7 @@
 If name is bound anywhere within a block, all uses of the name within
 the block are treated as references to that binding, even uses that
 appear before the binding.
+This is true even in the module block, unlike Python.
 The binding of `y` on the last line of the example below makes `y`
 local to the function `hello`, so the use of `y` in the print
 statement also refers to the local `y`, even though it appears
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 @@
 //
 // 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 @@
 //
 // 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) 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
 
 ---
-# 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 ### "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 @@
      i()
      i = lambda: 0
 
-
 def g():
   f()
 
@@ -115,8 +120,6 @@
 
 ---
 # 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():
diff --git a/testdata/assign.sky b/testdata/assign.sky
index ca6b3c5..fd87073 100644
--- a/testdata/assign.sky
+++ b/testdata/assign.sky
@@ -191,22 +191,26 @@
 z += 3 ### "global variable z referenced before assignment"
 
 ---
-# It's ok to define a global that shadows a built-in.
-
 load("assert.sky", "assert")
 
-assert.eq(type(list), "builtin_function_or_method")
+# It's ok to define a global that shadows a built-in...
 list = []
 assert.eq(type(list), "list")
 
-# set and float are dialect-specific,
-# but we shouldn't notice any difference.
+# ...but then all uses refer to the global,
+# even if they occur before the binding use.
+# See github.com/google/skylark/issues/116.
+assert.fails(lambda: tuple, "global variable tuple referenced before assignment")
+tuple = ()
 
-assert.eq(type(float), "builtin_function_or_method")
+---
+# Same as above, but set and float are dialect-specific;
+# we shouldn't notice any difference.
+load("assert.sky", "assert")
+
 float = 1.0
 assert.eq(type(float), "float")
 
-assert.eq(type(set), "builtin_function_or_method")
 set = [1, 2, 3]
 assert.eq(type(set), "list")