Introduce --incompatible_static_name_resolution

This implements a part of the name resolution document: https://github.com/bazelbuild/proposals/blob/master/docs/2018-06-18-name-resolution.md

When the flag is set:
 - Builtins may be shadowed (e.g. `len = 2`) on top-level
 - Symbols may be used before their definition point, e.g.
      def fct(): return x
      x = 2

In a followup change, we'll need to update the dynamic Environment too (to have truly lexical binding).

https://github.com/bazelbuild/bazel/issues/5637

RELNOTES: None.
PiperOrigin-RevId: 210412609
diff --git a/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java b/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java
index eff7ba3..90588f9 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java
@@ -144,6 +144,7 @@
         "--incompatible_range_type=" + rand.nextBoolean(),
         "--incompatible_remove_native_git_repository=" + rand.nextBoolean(),
         "--incompatible_remove_native_http_archive=" + rand.nextBoolean(),
+        "--incompatible_static_name_resolution=" + rand.nextBoolean(),
         "--incompatible_string_is_not_iterable=" + rand.nextBoolean(),
         "--internal_skylark_flag_test_canary=" + rand.nextBoolean());
   }
@@ -179,6 +180,7 @@
         .incompatibleRangeType(rand.nextBoolean())
         .incompatibleRemoveNativeGitRepository(rand.nextBoolean())
         .incompatibleRemoveNativeHttpArchive(rand.nextBoolean())
+        .incompatibleStaticNameResolution(rand.nextBoolean())
         .incompatibleStringIsNotIterable(rand.nextBoolean())
         .internalSkylarkFlagTestCanary(rand.nextBoolean())
         .build();
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/EnvironmentTest.java b/src/test/java/com/google/devtools/build/lib/syntax/EnvironmentTest.java
index 73f66fb..f11cfe7 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/EnvironmentTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/EnvironmentTest.java
@@ -245,19 +245,17 @@
   }
 
   @Test
-  public void testReadOnly() throws Exception {
-    Environment env = newSkylarkEnvironment()
-        .setup("special_var", 42)
-        .update("global_var", 666);
+  public void testBuiltinsCanBeShadowed() throws Exception {
+    Environment env =
+        newEnvironmentWithSkylarkOptions("--incompatible_static_name_resolution=true")
+            .setup("special_var", 42);
+    BuildFileAST.eval(env, "special_var = 41");
+    assertThat(env.lookup("special_var")).isEqualTo(41);
+  }
 
-    // We don't even get a runtime exception trying to modify these,
-    // because we get compile-time exceptions even before we reach runtime!
-    try {
-      BuildFileAST.eval(env, "special_var = 41");
-      throw new AssertionError("failed to fail");
-    } catch (EvalException e) {
-      assertThat(e).hasMessageThat().contains("Variable special_var is read only");
-    }
+  @Test
+  public void testVariableIsReferencedBeforeAssignment() throws Exception {
+    Environment env = newSkylarkEnvironment().update("global_var", 666);
 
     try {
       BuildFileAST.eval(env, "def foo(x): x += global_var; global_var = 36; return x", "foo(1)");
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/ValidationTest.java b/src/test/java/com/google/devtools/build/lib/syntax/ValidationTest.java
index 3a003b1..403bfa3 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/ValidationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/ValidationTest.java
@@ -93,6 +93,18 @@
 
   @Test
   public void testDefinitionByItself() throws Exception {
+    env = newEnvironmentWithSkylarkOptions("--incompatible_static_name_resolution=true");
+    // Variables are assumed to be statically visible in the block (even if they might not be
+    // initialized).
+    parse("a = a");
+    parse("a += a");
+    parse("[[] for a in a]");
+    parse("def f():", "  for a in a: pass");
+  }
+
+  @Test
+  public void testDefinitionByItselfLegacy() throws Exception {
+    env = newEnvironmentWithSkylarkOptions("--incompatible_static_name_resolution=false");
     checkError("name 'a' is not defined", "a = a");
     checkError("name 'a' is not defined", "a += a");
     checkError("name 'a' is not defined", "[[] for a in a]");
@@ -105,11 +117,18 @@
   }
 
   @Test
-  public void testBuiltinSymbolsAreReadOnly() throws Exception {
+  public void testBuiltinSymbolsAreReadOnlyLegacy() throws Exception {
+    env = newEnvironmentWithSkylarkOptions("--incompatible_static_name_resolution=false");
     checkError("Variable repr is read only", "repr = 1");
   }
 
   @Test
+  public void testBuiltinsCanBeShadowed() throws Exception {
+    env = newEnvironmentWithSkylarkOptions("--incompatible_static_name_resolution=true");
+    parse("repr = 1");
+  }
+
+  @Test
   public void testSkylarkGlobalVariablesAreReadonly() throws Exception {
     checkError("Variable a is read only", "a = 1", "a = 2");
   }
@@ -130,6 +149,23 @@
   }
 
   @Test
+  public void testGlobalDefinedBelow() throws Exception {
+    env = newEnvironmentWithSkylarkOptions("--incompatible_static_name_resolution=true");
+    parse("def bar(): return x", "x = 5\n");
+  }
+
+  @Test
+  public void testLocalVariableDefinedBelow() throws Exception {
+    env = newEnvironmentWithSkylarkOptions("--incompatible_static_name_resolution=true");
+    parse(
+        "def bar():",
+        "    for i in range(5):",
+        "        if i > 2: return x",
+        "        x = i" // x is visible in the entire function block
+        );
+  }
+
+  @Test
   public void testFunctionDoesNotExist() {
     checkError("name 'foo' is not defined", "def bar(): a = foo() + 'a'");
   }