bazel syntax: rename Environment -> StarlarkThread

The environment features are about to be removed.

This is inevitably a breaking API change (sorry copybara).

PiperOrigin-RevId: 271339546
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BaseFunction.java b/src/main/java/com/google/devtools/build/lib/syntax/BaseFunction.java
index 6866b97..f67419a 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/BaseFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/BaseFunction.java
@@ -179,9 +179,11 @@
   /**
    * Process the caller-provided arguments into an array suitable for the callee (this function).
    */
-  public Object[] processArguments(List<Object> args,
+  public Object[] processArguments(
+      List<Object> args,
       @Nullable Map<String, Object> kwargs,
-      @Nullable Location loc, @Nullable Environment env)
+      @Nullable Location loc,
+      @Nullable StarlarkThread thread)
       throws EvalException {
 
     Object[] arguments = new Object[getArgArraySize()];
@@ -256,7 +258,7 @@
       // If there's a kwParam, it's empty.
       if (hasKwParam) {
         // TODO(bazel-team): create a fresh mutable dict, like Python does
-        arguments[kwParamIndex] = SkylarkDict.of(env);
+        arguments[kwParamIndex] = SkylarkDict.of(thread);
       }
     } else if (hasKwParam && numNamedParams == 0) {
       // Easy case (2b): there are no named parameters, but there is a **kwParam.
@@ -265,10 +267,10 @@
       // Also note that no named parameters means no mandatory parameters that weren't passed,
       // and no missing optional parameters for which to use a default. Thus, no loops.
       // NB: not 2a means kwarg isn't null
-      arguments[kwParamIndex] = SkylarkDict.copyOf(env, kwargs);
+      arguments[kwParamIndex] = SkylarkDict.copyOf(thread, kwargs);
     } else {
       // Hard general case (2c): some keyword arguments may correspond to named parameters
-      SkylarkDict<String, Object> kwArg = hasKwParam ? SkylarkDict.of(env) : SkylarkDict.empty();
+      SkylarkDict<String, Object> kwArg = hasKwParam ? SkylarkDict.of(thread) : SkylarkDict.empty();
 
       // For nicer stabler error messages, start by checking against
       // an argument being provided both as positional argument and as keyword argument.
@@ -303,12 +305,12 @@
             throw new EvalException(loc, String.format(
                 "%s got multiple values for keyword argument '%s'", this, keyword));
           }
-          kwArg.put(keyword, value, loc, env);
+          kwArg.put(keyword, value, loc, thread);
         }
       }
       if (hasKwParam) {
         // TODO(bazel-team): create a fresh mutable dict, like Python does
-        arguments[kwParamIndex] = SkylarkDict.copyOf(env, kwArg);
+        arguments[kwParamIndex] = SkylarkDict.copyOf(thread, kwArg);
       }
 
       // Check that all mandatory parameters were filled in general case 2c.
@@ -381,22 +383,23 @@
    * @param args a list of all positional arguments (as in *starArg)
    * @param kwargs a map for key arguments (as in **kwArgs)
    * @param ast the expression for this function's definition
-   * @param env the Environment in the function is called
+   * @param thread the StarlarkThread in the function is called
    * @return the value resulting from evaluating the function with the given arguments
    * @throws EvalException-s containing source information.
    */
-  public Object call(List<Object> args,
+  public Object call(
+      List<Object> args,
       @Nullable Map<String, Object> kwargs,
       @Nullable FuncallExpression ast,
-      Environment env)
+      StarlarkThread thread)
       throws EvalException, InterruptedException {
     Preconditions.checkState(isConfigured(), "Function %s was not configured", getName());
 
     // ast is null when called from Java (as there's no Skylark call site).
     Location loc = ast == null ? Location.BUILTIN : ast.getLocation();
 
-    Object[] arguments = processArguments(args, kwargs, loc, env);
-    return callWithArgArray(arguments, ast, env, location);
+    Object[] arguments = processArguments(args, kwargs, loc, thread);
+    return callWithArgArray(arguments, ast, thread, location);
   }
 
   /**
@@ -404,11 +407,11 @@
    *
    * @param args an array of argument values sorted as per the signature.
    * @param ast the source code for the function if user-defined
-   * @param env the lexical environment of the function call
+   * @param thread the Starlark thread for the call
    * @throws InterruptedException may be thrown in the function implementations.
    */
   // Don't make it abstract, so that subclasses may be defined that @Override the outer call() only.
-  protected Object call(Object[] args, @Nullable FuncallExpression ast, Environment env)
+  protected Object call(Object[] args, @Nullable FuncallExpression ast, StarlarkThread thread)
       throws EvalException, InterruptedException {
     throw new EvalException(
         (ast == null) ? Location.BUILTIN : ast.getLocation(),
@@ -420,12 +423,12 @@
    * been resolved into positional ones.
    *
    * @param ast the expression for this function's definition
-   * @param env the Environment in the function is called
+   * @param thread the StarlarkThread in the function is called
    * @return the value resulting from evaluating the function with the given arguments
    * @throws EvalException-s containing source information.
    */
   public Object callWithArgArray(
-      Object[] arguments, @Nullable FuncallExpression ast, Environment env, Location loc)
+      Object[] arguments, @Nullable FuncallExpression ast, StarlarkThread thread, Location loc)
       throws EvalException, InterruptedException {
     Preconditions.checkState(isConfigured(), "Function %s was not configured", getName());
     canonicalizeArguments(arguments, loc);
@@ -434,7 +437,7 @@
       if (Callstack.enabled) {
         Callstack.push(this);
       }
-      return call(arguments, ast, env);
+      return call(arguments, ast, thread);
     } finally {
       if (Callstack.enabled) {
         Callstack.pop();
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BuildFileAST.java b/src/main/java/com/google/devtools/build/lib/syntax/BuildFileAST.java
index 737f220..7f7e7b5 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/BuildFileAST.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/BuildFileAST.java
@@ -123,8 +123,9 @@
   }
 
   /** Returns true if there was no error event. */
-  public boolean replayLexerEvents(Environment env, EventHandler eventHandler) {
-    if (env.getSemantics().incompatibleRestrictStringEscapes() && !stringEscapeEvents.isEmpty()) {
+  public boolean replayLexerEvents(StarlarkThread thread, EventHandler eventHandler) {
+    if (thread.getSemantics().incompatibleRestrictStringEscapes()
+        && !stringEscapeEvents.isEmpty()) {
       Event.replayEventsOn(eventHandler, stringEscapeEvents);
       return false;
     }
@@ -132,7 +133,7 @@
   }
 
   /**
-   * Executes this build file in a given Environment.
+   * Executes this build file in a given StarlarkThread.
    *
    * <p>If, for any reason, execution of a statement cannot be completed, exec throws an {@link
    * EvalException}. This exception is caught here and reported through reporter and execution
@@ -147,10 +148,11 @@
    * @return true if no error occurred during execution.
    */
   // TODO(adonovan): move to EvalUtils.
-  public boolean exec(Environment env, EventHandler eventHandler) throws InterruptedException {
+  public boolean exec(StarlarkThread thread, EventHandler eventHandler)
+      throws InterruptedException {
     boolean ok = true;
     for (Statement stmt : statements) {
-      if (!execTopLevelStatement(stmt, env, eventHandler)) {
+      if (!execTopLevelStatement(stmt, thread, eventHandler)) {
         ok = false;
       }
     }
@@ -158,7 +160,7 @@
   }
 
   /**
-   * Executes top-level statement of this build file in a given Environment.
+   * Executes top-level statement of this build file in a given StarlarkThread.
    *
    * <p>If, for any reason, execution of a statement cannot be completed, exec throws an {@link
    * EvalException}. This exception is caught here and reported through reporter. In effect, there
@@ -172,10 +174,11 @@
    *
    * @return true if no error occurred during execution.
    */
-  public boolean execTopLevelStatement(Statement stmt, Environment env, EventHandler eventHandler)
+  public boolean execTopLevelStatement(
+      Statement stmt, StarlarkThread thread, EventHandler eventHandler)
       throws InterruptedException {
     try {
-      Eval.execToplevelStatement(env, stmt);
+      Eval.execToplevelStatement(thread, stmt);
       return true;
     } catch (EvalException e) {
       // Do not report errors caused by a previous parsing error, as it has already been
@@ -283,9 +286,10 @@
   // TODO(adonovan): eliminate. Most callers need validation because they intend to execute the
   // file, and should be made to use higher-level operations in EvalUtils.
   // rest should skip this step. Called from EvaluationTestCase, ParserTest, ASTFileLookupFunction.
-  public BuildFileAST validate(Environment env, boolean isBuildFile, EventHandler eventHandler) {
+  public BuildFileAST validate(
+      StarlarkThread thread, boolean isBuildFile, EventHandler eventHandler) {
     try {
-      ValidationEnvironment.validateFile(this, env, isBuildFile);
+      ValidationEnvironment.validateFile(this, thread, isBuildFile);
       return this;
     } catch (EvalException e) {
       if (!e.isDueToIncompleteAST()) {
@@ -312,7 +316,7 @@
   // (Abolish "statement" and "file+expr" as primary API concepts.)
   // Make callers decide whether they want to execute a file or evaluate an expression.
   @Nullable
-  public Object eval(Environment env) throws EvalException, InterruptedException {
+  public Object eval(StarlarkThread thread) throws EvalException, InterruptedException {
     List<Statement> stmts = statements;
     Expression expr = null;
     int n = statements.size();
@@ -320,22 +324,22 @@
       stmts = statements.subList(0, n - 1);
       expr = ((ExpressionStatement) statements.get(n - 1)).getExpression();
     }
-    Eval.execStatements(env, stmts);
-    return expr == null ? null : Eval.eval(env, expr);
+    Eval.execStatements(thread, stmts);
+    return expr == null ? null : Eval.eval(thread, expr);
   }
 
   /**
    * Parses, resolves and evaluates the input and returns the value of the last statement if it's an
    * Expression or else null. In case of error (either during validation or evaluation), it throws
-   * an EvalException. The return value is as for eval(Environment).
+   * an EvalException. The return value is as for eval(StarlarkThread).
    */
   // Note: uses Starlark (not BUILD) validation semantics.
   // TODO(adonovan): move to EvalUtils; see other eval function.
   @Nullable
-  public static Object eval(ParserInput input, Environment env)
+  public static Object eval(ParserInput input, StarlarkThread thread)
       throws EvalException, InterruptedException {
-    BuildFileAST ast = parseAndValidateSkylark(input, env);
-    return ast.eval(env);
+    BuildFileAST ast = parseAndValidateSkylark(input, thread);
+    return ast.eval(thread);
   }
 
   /**
@@ -343,11 +347,11 @@
    * it throws an EvalException. Uses Starlark (not BUILD) validation semantics.
    */
   // TODO(adonovan): move to EvalUtils; see above.
-  public static BuildFileAST parseAndValidateSkylark(ParserInput input, Environment env)
+  public static BuildFileAST parseAndValidateSkylark(ParserInput input, StarlarkThread thread)
       throws EvalException {
-    BuildFileAST file = parse(input, env.getEventHandler());
-    file.replayLexerEvents(env, env.getEventHandler());
-    ValidationEnvironment.validateFile(file, env, /*isBuildFile=*/ false);
+    BuildFileAST file = parse(input, thread.getEventHandler());
+    file.replayLexerEvents(thread, thread.getEventHandler());
+    ValidationEnvironment.validateFile(file, thread, /*isBuildFile=*/ false);
     return file;
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BuiltinCallable.java b/src/main/java/com/google/devtools/build/lib/syntax/BuiltinCallable.java
index 91389ca..04b0d78 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/BuiltinCallable.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/BuiltinCallable.java
@@ -42,9 +42,9 @@
       List<Object> args,
       @Nullable Map<String, Object> kwargs,
       FuncallExpression ast,
-      Environment env)
+      StarlarkThread thread)
       throws EvalException, InterruptedException {
-    MethodDescriptor methodDescriptor = getMethodDescriptor(env.getSemantics());
+    MethodDescriptor methodDescriptor = getMethodDescriptor(thread.getSemantics());
     Class<?> clazz;
     Object objValue;
 
@@ -62,8 +62,8 @@
         Profiler.instance().profile(ProfilerTask.STARLARK_BUILTIN_FN, methodName)) {
       Object[] javaArguments =
           CallUtils.convertStarlarkArgumentsToJavaMethodArguments(
-              env, ast, methodDescriptor, clazz, args, kwargs);
-      return methodDescriptor.call(objValue, javaArguments, ast.getLocation(), env);
+              thread, ast, methodDescriptor, clazz, args, kwargs);
+      return methodDescriptor.call(objValue, javaArguments, ast.getLocation(), thread);
     }
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BuiltinFunction.java b/src/main/java/com/google/devtools/build/lib/syntax/BuiltinFunction.java
index 8af2052..5887f00 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/BuiltinFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/BuiltinFunction.java
@@ -21,8 +21,8 @@
 import com.google.devtools.build.lib.profiler.SilentCloseable;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkSignature;
-import com.google.devtools.build.lib.syntax.Environment.LexicalFrame;
 import com.google.devtools.build.lib.syntax.SkylarkType.SkylarkFunctionType;
+import com.google.devtools.build.lib.syntax.StarlarkThread.LexicalFrame;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
@@ -133,9 +133,9 @@
 
   @Override
   @Nullable
-  public Object call(Object[] args, @Nullable FuncallExpression ast, Environment env)
+  public Object call(Object[] args, @Nullable FuncallExpression ast, StarlarkThread thread)
       throws EvalException, InterruptedException {
-    Preconditions.checkNotNull(env);
+    Preconditions.checkNotNull(thread);
 
     // ast is null when called from Java (as there's no Skylark call site).
     Location loc = ast == null ? Location.BUILTIN : ast.getLocation();
@@ -154,7 +154,7 @@
             break;
 
           case ENVIRONMENT:
-            args[i] = env;
+            args[i] = thread;
             break;
         }
         i++;
@@ -164,7 +164,8 @@
     // Last but not least, actually make an inner call to the function with the resolved arguments.
     try (SilentCloseable c =
         Profiler.instance().profile(ProfilerTask.STARLARK_BUILTIN_FN, getName())) {
-      env.enterScope(this, SHARED_LEXICAL_FRAME_FOR_BUILTIN_FUNCTION_CALLS, ast, env.getGlobals());
+      thread.enterScope(
+          this, SHARED_LEXICAL_FRAME_FOR_BUILTIN_FUNCTION_CALLS, ast, thread.getGlobals());
       return invokeMethod.invoke(this, args);
     } catch (InvocationTargetException x) {
       Throwable e = x.getCause();
@@ -201,7 +202,7 @@
     } catch (IllegalAccessException e) {
       throw badCallException(loc, e, args);
     } finally {
-      env.exitScope();
+      thread.exitScope();
     }
   }
 
@@ -360,7 +361,7 @@
     }
 
     @Override
-    public Object call(Object[] args, @Nullable FuncallExpression ast, Environment env)
+    public Object call(Object[] args, @Nullable FuncallExpression ast, StarlarkThread thread)
         throws EvalException {
       throw new EvalException(null, "tried to invoke a Factory for function " + this);
     }
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/CallUtils.java b/src/main/java/com/google/devtools/build/lib/syntax/CallUtils.java
index 1e5e6be..459247f 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/CallUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/CallUtils.java
@@ -192,7 +192,7 @@
                 }
               });
 
-  // *args, **kwargs, location, ast, environment, skylark semantics
+  // *args, **kwargs, location, ast, thread, skylark semantics
   private static final int EXTRA_ARGS_COUNT = 6;
 
   /**
@@ -373,7 +373,7 @@
    * Invokes the given structField=true method and returns the result.
    *
    * <p>The given method must <b>not</b> require extra-interpreter parameters, such as {@link
-   * Environment}. This method throws {@link IllegalArgumentException} for violations.
+   * StarlarkThread}. This method throws {@link IllegalArgumentException} for violations.
    *
    * @param methodDescriptor the descriptor of the method to invoke
    * @param fieldName the name of the struct field
@@ -387,7 +387,7 @@
     Preconditions.checkArgument(
         methodDescriptor.isStructField(), "Can only be invoked on structField callables");
     Preconditions.checkArgument(
-        !methodDescriptor.isUseEnvironment()
+        !methodDescriptor.isUseStarlarkThread()
             || !methodDescriptor.isUseStarlarkSemantics()
             || !methodDescriptor.isUseLocation(),
         "Cannot be invoked on structField callables with extra interpreter params");
@@ -403,14 +403,14 @@
    * @param args a list of positional Starlark arguments
    * @param kwargs a map of keyword Starlark arguments; keys are the used keyword, and values are
    *     their corresponding values in the method call
-   * @param environment the current Starlark environment
+   * @param thread the Starlark thread for the call
    * @return the array of arguments which may be passed to {@link MethodDescriptor#call}
    * @throws EvalException if the given set of arguments are invalid for the given method. For
    *     example, if any arguments are of unexpected type, or not all mandatory parameters are
    *     specified by the user
    */
   static Object[] convertStarlarkArgumentsToJavaMethodArguments(
-      Environment environment,
+      StarlarkThread thread,
       FuncallExpression call,
       MethodDescriptor method,
       Class<?> objClass,
@@ -523,18 +523,18 @@
         }
         extraKwargs = extraKwargsBuilder.build();
       } else {
-        throw unexpectedKeywordArgumentException(call, keys, method, objClass, environment);
+        throw unexpectedKeywordArgumentException(call, keys, method, objClass, thread);
       }
     }
 
-    // Then add any skylark-interpreter arguments (for example kwargs or the Environment).
+    // Then add any skylark-interpreter arguments (for example kwargs or the StarlarkThread).
     if (acceptsExtraArgs) {
       builder.add(Tuple.copyOf(extraArgs));
     }
     if (acceptsExtraKwargs) {
-      builder.add(SkylarkDict.copyOf(environment, extraKwargs));
+      builder.add(SkylarkDict.copyOf(thread, extraKwargs));
     }
-    appendExtraInterpreterArgs(builder, method, call, call.getLocation(), environment);
+    appendExtraInterpreterArgs(builder, method, call, call.getLocation(), thread);
 
     return builder.toArray();
   }
@@ -565,7 +565,7 @@
       Set<String> unexpectedKeywords,
       MethodDescriptor method,
       Class<?> objClass,
-      Environment env) {
+      StarlarkThread thread) {
     // Check if any of the unexpected keywords are for parameters which are disabled by the
     // current semantic flags. Throwing an error with information about the misconfigured
     // semantic flag is likely far more helpful.
@@ -573,7 +573,7 @@
       if (param.isDisabledInCurrentSemantics() && unexpectedKeywords.contains(param.getName())) {
         FlagIdentifier flagIdentifier = param.getFlagResponsibleForDisable();
         // If the flag is True, it must be a deprecation flag. Otherwise it's an experimental flag.
-        if (env.getSemantics().flagValue(flagIdentifier)) {
+        if (thread.getSemantics().flagValue(flagIdentifier)) {
           return new EvalException(
               call.getLocation(),
               String.format(
@@ -640,25 +640,28 @@
    * the caller to validate this invariant.
    */
   static List<Object> extraInterpreterArgs(
-      MethodDescriptor method, @Nullable FuncallExpression ast, Location loc, Environment env) {
+      MethodDescriptor method,
+      @Nullable FuncallExpression ast,
+      Location loc,
+      StarlarkThread thread) {
     List<Object> builder = new ArrayList<>();
-    appendExtraInterpreterArgs(builder, method, ast, loc, env);
+    appendExtraInterpreterArgs(builder, method, ast, loc, thread);
     return ImmutableList.copyOf(builder);
   }
 
   /**
    * Same as {@link #extraInterpreterArgs(MethodDescriptor, FuncallExpression, Location,
-   * Environment)} but appends args to a passed {@code builder} to avoid unnecessary allocations of
-   * intermediate instances.
+   * StarlarkThread)} but appends args to a passed {@code builder} to avoid unnecessary allocations
+   * of intermediate instances.
    *
-   * @see #extraInterpreterArgs(MethodDescriptor, FuncallExpression, Location, Environment)
+   * @see #extraInterpreterArgs(MethodDescriptor, FuncallExpression, Location, StarlarkThread)
    */
   private static void appendExtraInterpreterArgs(
       List<Object> builder,
       MethodDescriptor method,
       @Nullable FuncallExpression ast,
       Location loc,
-      Environment env) {
+      StarlarkThread thread) {
     if (method.isUseLocation()) {
       builder.add(loc);
     }
@@ -668,11 +671,11 @@
       }
       builder.add(ast);
     }
-    if (method.isUseEnvironment()) {
-      builder.add(env);
+    if (method.isUseStarlarkThread()) {
+      builder.add(thread);
     }
     if (method.isUseStarlarkSemantics()) {
-      builder.add(env.getSemantics());
+      builder.add(thread.getSemantics());
     }
   }
 
@@ -710,7 +713,7 @@
 
   /** Invoke object.method() and return the result. */
   static Object callMethod(
-      Environment env,
+      StarlarkThread thread,
       FuncallExpression call,
       Object object,
       ArrayList<Object> posargs,
@@ -720,7 +723,7 @@
       throws EvalException, InterruptedException {
     // Case 1: Object is a String. String is an unusual special case.
     if (object instanceof String) {
-      return callStringMethod(env, call, (String) object, methodName, posargs, kwargs);
+      return callStringMethod(thread, call, (String) object, methodName, posargs, kwargs);
     }
 
     // Case 2: Object is a Java object with a matching @SkylarkCallable method.
@@ -728,12 +731,12 @@
     // java method 'bar()', this avoids evaluating 'foo.bar' in isolation (which would require
     // creating a throwaway function-like object).
     MethodDescriptor methodDescriptor =
-        CallUtils.getMethod(env.getSemantics(), object.getClass(), methodName);
+        CallUtils.getMethod(thread.getSemantics(), object.getClass(), methodName);
     if (methodDescriptor != null && !methodDescriptor.isStructField()) {
       Object[] javaArguments =
           convertStarlarkArgumentsToJavaMethodArguments(
-              env, call, methodDescriptor, object.getClass(), posargs, kwargs);
-      return methodDescriptor.call(object, javaArguments, call.getLocation(), env);
+              thread, call, methodDescriptor, object.getClass(), posargs, kwargs);
+      return methodDescriptor.call(object, javaArguments, call.getLocation(), thread);
     }
 
     // Case 3: Object is a function registered with the BuiltinRegistry.
@@ -743,16 +746,16 @@
         Runtime.getBuiltinRegistry().getFunction(object.getClass(), methodName);
     if (legacyRuntimeFunction != null) {
       return callLegacyBuiltinRegistryFunction(
-          call, legacyRuntimeFunction, object, posargs, kwargs, env);
+          call, legacyRuntimeFunction, object, posargs, kwargs, thread);
     }
 
     // Case 4: All other cases. Evaluate "foo.bar" as a dot expression, then try to invoke it
     // as a callable.
-    Object functionObject = EvalUtils.getAttr(env, dotLocation, object, methodName);
+    Object functionObject = EvalUtils.getAttr(thread, dotLocation, object, methodName);
     if (functionObject == null) {
       throw missingMethodException(call, object.getClass(), methodName);
     } else {
-      return call(env, call, functionObject, posargs, kwargs);
+      return call(thread, call, functionObject, posargs, kwargs);
     }
   }
 
@@ -762,16 +765,16 @@
       Object object,
       ArrayList<Object> posargs,
       Map<String, Object> kwargs,
-      Environment env)
+      StarlarkThread thread)
       throws EvalException, InterruptedException {
     if (!isNamespace(object.getClass())) {
       posargs.add(0, object);
     }
-    return legacyRuntimeFunction.call(posargs, kwargs, call, env);
+    return legacyRuntimeFunction.call(posargs, kwargs, call, thread);
   }
 
   private static Object callStringMethod(
-      Environment env,
+      StarlarkThread thread,
       FuncallExpression call,
       String objValue,
       String methodName,
@@ -782,19 +785,19 @@
     // to StringModule, and thus need to include the actual string as a 'self' parameter.
     posargs.add(0, objValue);
 
-    MethodDescriptor method = getMethod(env.getSemantics(), StringModule.class, methodName);
+    MethodDescriptor method = getMethod(thread.getSemantics(), StringModule.class, methodName);
     if (method == null) {
       throw missingMethodException(call, StringModule.class, methodName);
     }
 
     Object[] javaArguments =
         convertStarlarkArgumentsToJavaMethodArguments(
-            env, call, method, StringModule.class, posargs, kwargs);
-    return method.call(StringModule.INSTANCE, javaArguments, call.getLocation(), env);
+            thread, call, method, StringModule.class, posargs, kwargs);
+    return method.call(StringModule.INSTANCE, javaArguments, call.getLocation(), thread);
   }
 
   static Object call(
-      Environment env,
+      StarlarkThread thread,
       FuncallExpression call,
       Object fn,
       ArrayList<Object> posargs,
@@ -803,13 +806,13 @@
 
     if (fn instanceof StarlarkCallable) {
       StarlarkCallable callable = (StarlarkCallable) fn;
-      return callable.call(posargs, ImmutableMap.copyOf(kwargs), call, env);
-    } else if (hasSelfCallMethod(env.getSemantics(), fn.getClass())) {
-      MethodDescriptor descriptor = getSelfCallMethodDescriptor(env.getSemantics(), fn);
+      return callable.call(posargs, ImmutableMap.copyOf(kwargs), call, thread);
+    } else if (hasSelfCallMethod(thread.getSemantics(), fn.getClass())) {
+      MethodDescriptor descriptor = getSelfCallMethodDescriptor(thread.getSemantics(), fn);
       Object[] javaArguments =
           convertStarlarkArgumentsToJavaMethodArguments(
-              env, call, descriptor, fn.getClass(), posargs, kwargs);
-      return descriptor.call(fn, javaArguments, call.getLocation(), env);
+              thread, call, descriptor, fn.getClass(), posargs, kwargs);
+      return descriptor.call(fn, javaArguments, call.getLocation(), thread);
     } else {
       throw new EvalException(
           call.getLocation(), "'" + EvalUtils.getDataTypeName(fn) + "' object is not callable");
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Debugger.java b/src/main/java/com/google/devtools/build/lib/syntax/Debugger.java
index 9997010..39b658b 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Debugger.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Debugger.java
@@ -22,7 +22,7 @@
 public interface Debugger {
 
   /** Notify the debugger that execution is at the point immediately before {@code loc}. */
-  void before(Environment env, Location loc);
+  void before(StarlarkThread thread, Location loc);
 
   /** Notify the debugger that it will no longer receive events from the interpreter. */
   void close();
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Eval.java b/src/main/java/com/google/devtools/build/lib/syntax/Eval.java
index be3a172..63d5dd2 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Eval.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Eval.java
@@ -29,14 +29,14 @@
 
 /** A syntax-tree-walking evaluator. */
 // TODO(adonovan): make this class the sole locus of tree-based evaluation logic.
-// Make all its methods static, and thread Environment (soon: StarlarkThread) explicitly.
-// The only actual state is the return value, which can be saved in the Environment's call frame.
+// Make all its methods static, and thread StarlarkThread (soon: StarlarkThread) explicitly.
+// The only actual state is the return value, which can be saved in the StarlarkThread's call frame.
 // Move remaining Expression.eval logic here, and simplify.
 final class Eval {
 
   private static final AtomicReference<Debugger> debugger = new AtomicReference<>();
 
-  private final Environment env;
+  private final StarlarkThread thread;
   private final Debugger dbg;
   private Object result = Runtime.NONE;
 
@@ -49,37 +49,37 @@
     }
   }
 
-  static Object execStatements(Environment env, List<Statement> statements)
+  static Object execStatements(StarlarkThread thread, List<Statement> statements)
       throws EvalException, InterruptedException {
-    Eval eval = new Eval(env);
+    Eval eval = new Eval(thread);
     eval.execStatementsInternal(statements);
     return eval.result;
   }
 
-  static void execToplevelStatement(Environment env, Statement stmt)
+  static void execToplevelStatement(StarlarkThread thread, Statement stmt)
       throws EvalException, InterruptedException {
     // Ignore the returned BREAK/CONTINUE/RETURN/PASS token:
     // the first three don't exist at top-level, and the last is a no-op.
-    new Eval(env).exec(stmt);
+    new Eval(thread).exec(stmt);
   }
 
-  private Eval(Environment env) {
-    this.env = env;
+  private Eval(StarlarkThread thread) {
+    this.thread = thread;
     this.dbg = debugger.get(); // capture value and use for lifetime of one Eval
   }
 
   private void execAssignment(AssignmentStatement node) throws EvalException, InterruptedException {
-    Object rvalue = eval(env, node.getRHS());
-    assign(node.getLHS(), rvalue, env, node.getLocation());
+    Object rvalue = eval(thread, node.getRHS());
+    assign(node.getLHS(), rvalue, thread, node.getLocation());
   }
 
   private TokenKind execFor(ForStatement node) throws EvalException, InterruptedException {
-    Object o = eval(env, node.getCollection());
-    Iterable<?> col = EvalUtils.toIterable(o, node.getLocation(), env);
+    Object o = eval(thread, node.getCollection());
+    Iterable<?> col = EvalUtils.toIterable(o, node.getLocation(), thread);
     EvalUtils.lock(o, node.getLocation());
     try {
       for (Object it : col) {
-        assign(node.getLHS(), it, env, node.getLocation());
+        assign(node.getLHS(), it, thread, node.getLocation());
 
         switch (execStatementsInternal(node.getBlock())) {
           case PASS:
@@ -109,7 +109,7 @@
     if (defaultExpressions != null) {
       defaultValues = new ArrayList<>(defaultExpressions.size());
       for (Expression expr : defaultExpressions) {
-        defaultValues.add(eval(env, expr));
+        defaultValues.add(eval(thread, expr));
       }
     }
 
@@ -119,18 +119,18 @@
       throw new EvalException(node.getLocation(), "Keyword-only argument is forbidden.");
     }
 
-    env.updateAndExport(
+    thread.updateAndExport(
         node.getIdentifier().getName(),
         new StarlarkFunction(
             node.getIdentifier().getName(),
             node.getIdentifier().getLocation(),
             FunctionSignature.WithValues.create(sig, defaultValues, /*types=*/ null),
             node.getStatements(),
-            env.getGlobals()));
+            thread.getGlobals()));
   }
 
   private TokenKind execIf(IfStatement node) throws EvalException, InterruptedException {
-    boolean cond = EvalUtils.toBoolean(eval(env, node.getCondition()));
+    boolean cond = EvalUtils.toBoolean(eval(thread, node.getCondition()));
     if (cond) {
       return execStatementsInternal(node.getThenBlock());
     } else if (node.getElseBlock() != null) {
@@ -152,8 +152,8 @@
         }
         // The key is the original name that was used to define the symbol
         // in the loaded bzl file.
-        env.importSymbol(node.getImport().getValue(), name, declared.getName());
-      } catch (Environment.LoadFailedException e) {
+        thread.importSymbol(node.getImport().getValue(), name, declared.getName());
+      } catch (StarlarkThread.LoadFailedException e) {
         throw new EvalException(node.getLocation(), e.getMessage());
       }
     }
@@ -162,14 +162,14 @@
   private TokenKind execReturn(ReturnStatement node) throws EvalException, InterruptedException {
     Expression ret = node.getReturnExpression();
     if (ret != null) {
-      this.result = eval(env, ret);
+      this.result = eval(thread, ret);
     }
     return TokenKind.RETURN;
   }
 
   private TokenKind exec(Statement st) throws EvalException, InterruptedException {
     if (dbg != null) {
-      dbg.before(env, st.getLocation());
+      dbg.before(thread, st.getLocation());
     }
 
     try {
@@ -188,7 +188,7 @@
         execAugmentedAssignment((AugmentedAssignmentStatement) st);
         return TokenKind.PASS;
       case EXPRESSION:
-        eval(env, ((ExpressionStatement) st).getExpression());
+        eval(thread, ((ExpressionStatement) st).getExpression());
         return TokenKind.PASS;
       case FLOW:
         return ((FlowStatement) st).getKind();
@@ -224,17 +224,17 @@
    * Updates the environment bindings, and possibly mutates objects, so as to assign the given value
    * to the given expression. The expression must be valid for an {@code LValue}.
    */
-  private static void assign(Expression expr, Object value, Environment env, Location loc)
+  private static void assign(Expression expr, Object value, StarlarkThread thread, Location loc)
       throws EvalException, InterruptedException {
     if (expr instanceof Identifier) {
-      assignIdentifier((Identifier) expr, value, env);
+      assignIdentifier((Identifier) expr, value, thread);
     } else if (expr instanceof IndexExpression) {
-      Object object = eval(env, ((IndexExpression) expr).getObject());
-      Object key = eval(env, ((IndexExpression) expr).getKey());
-      assignItem(object, key, value, env, loc);
+      Object object = eval(thread, ((IndexExpression) expr).getObject());
+      Object key = eval(thread, ((IndexExpression) expr).getKey());
+      assignItem(object, key, value, thread, loc);
     } else if (expr instanceof ListExpression) {
       ListExpression list = (ListExpression) expr;
-      assignList(list, value, env, loc);
+      assignList(list, value, thread, loc);
     } else {
       // Not possible for validated ASTs.
       throw new EvalException(loc, "cannot assign to '" + expr + "'");
@@ -242,9 +242,9 @@
   }
 
   /** Binds a variable to the given value in the environment. */
-  private static void assignIdentifier(Identifier ident, Object value, Environment env)
+  private static void assignIdentifier(Identifier ident, Object value, StarlarkThread thread)
       throws EvalException {
-    env.updateAndExport(ident.getName(), value);
+    thread.updateAndExport(ident.getName(), value);
   }
 
   /**
@@ -256,14 +256,15 @@
    */
   @SuppressWarnings("unchecked")
   private static void assignItem(
-      Object object, Object key, Object value, Environment env, Location loc) throws EvalException {
+      Object object, Object key, Object value, StarlarkThread thread, Location loc)
+      throws EvalException {
     if (object instanceof SkylarkDict) {
       SkylarkDict<Object, Object> dict = (SkylarkDict<Object, Object>) object;
-      dict.put(key, value, loc, env);
+      dict.put(key, value, loc, thread);
     } else if (object instanceof SkylarkList.MutableList) {
       SkylarkList.MutableList<Object> list = (SkylarkList.MutableList<Object>) object;
       int index = EvalUtils.getSequenceIndex(key, list.size(), loc);
-      list.set(index, value, loc, env.mutability());
+      list.set(index, value, loc, thread.mutability());
     } else {
       throw new EvalException(
           loc,
@@ -279,9 +280,10 @@
    * @throws EvalException if the list literal has length 0, or if the value is not an iterable of
    *     matching length
    */
-  private static void assignList(ListExpression list, Object value, Environment env, Location loc)
+  private static void assignList(
+      ListExpression list, Object value, StarlarkThread thread, Location loc)
       throws EvalException, InterruptedException {
-    Collection<?> collection = EvalUtils.toCollection(value, loc, env);
+    Collection<?> collection = EvalUtils.toCollection(value, loc, thread);
     int len = list.getElements().size();
     if (len == 0) {
       throw new EvalException(
@@ -297,7 +299,7 @@
     }
     int i = 0;
     for (Object item : collection) {
-      assign(list.getElements().get(i), item, env, loc);
+      assign(list.getElements().get(i), item, thread, loc);
       i++;
     }
   }
@@ -319,21 +321,21 @@
     Location loc = stmt.getLocation();
 
     if (lhs instanceof Identifier) {
-      Object x = eval(env, lhs);
-      Object y = eval(env, rhs);
-      Object z = inplaceBinaryOp(op, x, y, env, loc);
-      assignIdentifier((Identifier) lhs, z, env);
+      Object x = eval(thread, lhs);
+      Object y = eval(thread, rhs);
+      Object z = inplaceBinaryOp(op, x, y, thread, loc);
+      assignIdentifier((Identifier) lhs, z, thread);
     } else if (lhs instanceof IndexExpression) {
       // object[index] op= y
       // The object and key should be evaluated only once, so we don't use lhs.eval().
       IndexExpression index = (IndexExpression) lhs;
-      Object object = eval(env, index.getObject());
-      Object key = eval(env, index.getKey());
-      Object x = EvalUtils.index(object, key, env, loc);
+      Object object = eval(thread, index.getObject());
+      Object key = eval(thread, index.getKey());
+      Object x = EvalUtils.index(object, key, thread, loc);
       // Evaluate rhs after lhs.
-      Object y = eval(env, rhs);
-      Object z = inplaceBinaryOp(op, x, y, env, loc);
-      assignItem(object, key, z, env, loc);
+      Object y = eval(thread, rhs);
+      Object z = inplaceBinaryOp(op, x, y, thread, loc);
+      assignItem(object, key, z, thread, loc);
     } else if (lhs instanceof ListExpression) {
       throw new EvalException(loc, "cannot perform augmented assignment on a list literal");
     } else {
@@ -343,7 +345,7 @@
   }
 
   private static Object inplaceBinaryOp(
-      TokenKind op, Object x, Object y, Environment env, Location location)
+      TokenKind op, Object x, Object y, StarlarkThread thread, Location location)
       throws EvalException, InterruptedException {
     // list += iterable  behaves like  list.extend(iterable)
     // TODO(b/141263526): following Python, allow list+=iterable (but not list+iterable).
@@ -351,10 +353,10 @@
         && x instanceof SkylarkList.MutableList
         && y instanceof SkylarkList.MutableList) {
       SkylarkList.MutableList<?> list = (SkylarkList.MutableList) x;
-      list.extend(y, location, env);
+      list.extend(y, location, thread);
       return list;
     }
-    return EvalUtils.binaryOp(op, x, y, env, location);
+    return EvalUtils.binaryOp(op, x, y, thread, location);
   }
 
   // ---- expressions ----
@@ -378,17 +380,18 @@
    * @throws EvalException if the expression could not be evaluated.
    * @throws InterruptedException may be thrown in a sub class.
    */
-  static Object eval(Environment env, Expression expr) throws EvalException, InterruptedException {
+  static Object eval(StarlarkThread thread, Expression expr)
+      throws EvalException, InterruptedException {
     // TODO(adonovan): don't push and pop all the time. We should only need the stack of function
     // call frames, and we should recycle them.
-    // TODO(adonovan): put the Environment (Starlark thread) into the Java thread-local store
+    // TODO(adonovan): put the StarlarkThread (Starlark thread) into the Java thread-local store
     // once only, in enterScope, and undo this in exitScope.
     try {
       if (Callstack.enabled) {
         Callstack.push(expr);
       }
       try {
-        return doEval(env, expr);
+        return doEval(thread, expr);
       } catch (EvalException ex) {
         throw maybeTransformException(expr, ex);
       }
@@ -399,45 +402,45 @@
     }
   }
 
-  private static Object doEval(Environment env, Expression expr)
+  private static Object doEval(StarlarkThread thread, Expression expr)
       throws EvalException, InterruptedException {
     switch (expr.kind()) {
       case BINARY_OPERATOR:
         {
           BinaryOperatorExpression binop = (BinaryOperatorExpression) expr;
-          Object x = eval(env, binop.getX());
+          Object x = eval(thread, binop.getX());
           // AND and OR require short-circuit evaluation.
           switch (binop.getOperator()) {
             case AND:
-              return EvalUtils.toBoolean(x) ? Eval.eval(env, binop.getY()) : x;
+              return EvalUtils.toBoolean(x) ? Eval.eval(thread, binop.getY()) : x;
             case OR:
-              return EvalUtils.toBoolean(x) ? x : Eval.eval(env, binop.getY());
+              return EvalUtils.toBoolean(x) ? x : Eval.eval(thread, binop.getY());
             default:
-              Object y = eval(env, binop.getY());
-              return EvalUtils.binaryOp(binop.getOperator(), x, y, env, binop.getLocation());
+              Object y = eval(thread, binop.getY());
+              return EvalUtils.binaryOp(binop.getOperator(), x, y, thread, binop.getLocation());
           }
         }
 
       case COMPREHENSION:
-        return evalComprehension(env, (Comprehension) expr);
+        return evalComprehension(thread, (Comprehension) expr);
 
       case CONDITIONAL:
         {
           ConditionalExpression cond = (ConditionalExpression) expr;
-          Object v = eval(env, cond.getCondition());
-          return eval(env, EvalUtils.toBoolean(v) ? cond.getThenCase() : cond.getElseCase());
+          Object v = eval(thread, cond.getCondition());
+          return eval(thread, EvalUtils.toBoolean(v) ? cond.getThenCase() : cond.getElseCase());
         }
 
       case DICT_EXPR:
         {
           DictExpression dictexpr = (DictExpression) expr;
-          SkylarkDict<Object, Object> dict = SkylarkDict.of(env);
+          SkylarkDict<Object, Object> dict = SkylarkDict.of(thread);
           Location loc = dictexpr.getLocation();
           for (DictExpression.Entry entry : dictexpr.getEntries()) {
-            Object k = eval(env, entry.getKey());
-            Object v = eval(env, entry.getValue());
+            Object k = eval(thread, entry.getKey());
+            Object v = eval(thread, entry.getValue());
             int before = dict.size();
-            dict.put(k, v, loc, env);
+            dict.put(k, v, loc, thread);
             if (dict.size() == before) {
               throw new EvalException(
                   loc, "Duplicated key " + Printer.repr(k) + " when creating dictionary");
@@ -449,10 +452,10 @@
       case DOT:
         {
           DotExpression dot = (DotExpression) expr;
-          Object object = eval(env, dot.getObject());
+          Object object = eval(thread, dot.getObject());
           String name = dot.getField().getName();
-          Object result = EvalUtils.getAttr(env, dot.getLocation(), object, name);
-          return checkResult(object, result, name, dot.getLocation(), env.getSemantics());
+          Object result = EvalUtils.getAttr(thread, dot.getLocation(), object, name);
+          return checkResult(object, result, name, dot.getLocation(), thread.getSemantics());
         }
 
       case FUNCALL:
@@ -466,15 +469,15 @@
           // a closure for x.f if f is a Java method.
           if (call.getFunction() instanceof DotExpression) {
             DotExpression dot = (DotExpression) call.getFunction();
-            Object object = Eval.eval(env, dot.getObject());
-            evalArguments(env, call, posargs, kwargs);
+            Object object = Eval.eval(thread, dot.getObject());
+            evalArguments(thread, call, posargs, kwargs);
             return CallUtils.callMethod(
-                env, call, object, posargs, kwargs, dot.getField().getName(), dot.getLocation());
+                thread, call, object, posargs, kwargs, dot.getField().getName(), dot.getLocation());
           }
 
-          Object fn = Eval.eval(env, call.getFunction());
-          evalArguments(env, call, posargs, kwargs);
-          return CallUtils.call(env, call, fn, posargs, kwargs);
+          Object fn = Eval.eval(thread, call.getFunction());
+          evalArguments(thread, call, posargs, kwargs);
+          return CallUtils.call(thread, call, fn, posargs, kwargs);
         }
 
       case IDENTIFIER:
@@ -483,9 +486,9 @@
           String name = id.getName();
           if (id.getScope() == null) {
             // Legacy behavior, to be removed.
-            Object result = env.lookup(name);
+            Object result = thread.lookup(name);
             if (result == null) {
-              throw createInvalidIdentifierException(id, env.getVariableNames());
+              throw createInvalidIdentifierException(id, thread.getVariableNames());
             }
             return result;
           }
@@ -493,13 +496,13 @@
           Object result;
           switch (id.getScope()) {
             case Local:
-              result = env.localLookup(name);
+              result = thread.localLookup(name);
               break;
             case Module:
-              result = env.moduleLookup(name);
+              result = thread.moduleLookup(name);
               break;
             case Universe:
-              result = env.universeLookup(name);
+              result = thread.universeLookup(name);
               break;
             default:
               throw new IllegalStateException(id.getScope().toString());
@@ -523,9 +526,9 @@
       case INDEX:
         {
           IndexExpression index = (IndexExpression) expr;
-          Object object = eval(env, index.getObject());
-          Object key = eval(env, index.getKey());
-          return EvalUtils.index(object, key, env, index.getLocation());
+          Object object = eval(thread, index.getObject());
+          Object key = eval(thread, index.getKey());
+          return EvalUtils.index(object, key, thread, index.getLocation());
         }
 
       case INTEGER_LITERAL:
@@ -536,26 +539,26 @@
           ListExpression list = (ListExpression) expr;
           ArrayList<Object> result = new ArrayList<>(list.getElements().size());
           for (Expression elem : list.getElements()) {
-            result.add(eval(env, elem));
+            result.add(eval(thread, elem));
           }
           return list.isTuple()
               ? SkylarkList.Tuple.copyOf(result) // TODO(adonovan): opt: avoid copy
-              : SkylarkList.MutableList.wrapUnsafe(env, result);
+              : SkylarkList.MutableList.wrapUnsafe(thread, result);
         }
 
       case SLICE:
         {
           SliceExpression slice = (SliceExpression) expr;
-          Object object = eval(env, slice.getObject());
-          Object start = slice.getStart() == null ? Runtime.NONE : eval(env, slice.getStart());
-          Object end = slice.getEnd() == null ? Runtime.NONE : eval(env, slice.getEnd());
-          Object step = slice.getStep() == null ? Runtime.NONE : eval(env, slice.getStep());
+          Object object = eval(thread, slice.getObject());
+          Object start = slice.getStart() == null ? Runtime.NONE : eval(thread, slice.getStart());
+          Object end = slice.getEnd() == null ? Runtime.NONE : eval(thread, slice.getEnd());
+          Object step = slice.getStep() == null ? Runtime.NONE : eval(thread, slice.getStep());
           Location loc = slice.getLocation();
 
           // TODO(adonovan): move the rest into a public EvalUtils.slice() operator.
 
           if (object instanceof SkylarkList) {
-            return ((SkylarkList<?>) object).getSlice(start, end, step, loc, env.mutability());
+            return ((SkylarkList<?>) object).getSlice(start, end, step, loc, thread.mutability());
           }
 
           if (object instanceof String) {
@@ -589,7 +592,7 @@
       case UNARY_OPERATOR:
         {
           UnaryOperatorExpression unop = (UnaryOperatorExpression) expr;
-          Object x = eval(env, unop.getX());
+          Object x = eval(thread, unop.getX());
           return EvalUtils.unaryOp(unop.getOperator(), x, unop.getLocation());
         }
     }
@@ -630,9 +633,9 @@
         id.getLocation(), "name '" + id.getName() + "' is not defined" + suggestion);
   }
 
-  private static Object evalComprehension(Environment env, Comprehension comp)
+  private static Object evalComprehension(StarlarkThread thread, Comprehension comp)
       throws EvalException, InterruptedException {
-    final SkylarkDict<Object, Object> dict = comp.isDict() ? SkylarkDict.of(env) : null;
+    final SkylarkDict<Object, Object> dict = comp.isDict() ? SkylarkDict.of(thread) : null;
     final ArrayList<Object> list = comp.isDict() ? null : new ArrayList<>();
 
     // The Lambda class serves as a recursive lambda closure.
@@ -646,13 +649,13 @@
           if (clause instanceof Comprehension.For) {
             Comprehension.For forClause = (Comprehension.For) clause;
 
-            Object iterable = eval(env, forClause.getIterable());
+            Object iterable = eval(thread, forClause.getIterable());
             Location loc = comp.getLocation();
-            Iterable<?> listValue = EvalUtils.toIterable(iterable, loc, env);
+            Iterable<?> listValue = EvalUtils.toIterable(iterable, loc, thread);
             EvalUtils.lock(iterable, loc);
             try {
               for (Object elem : listValue) {
-                Eval.assign(forClause.getVars(), elem, env, loc);
+                Eval.assign(forClause.getVars(), elem, thread, loc);
                 execClauses(index + 1);
               }
             } finally {
@@ -661,7 +664,7 @@
 
           } else {
             Comprehension.If ifClause = (Comprehension.If) clause;
-            if (EvalUtils.toBoolean(eval(env, ifClause.getCondition()))) {
+            if (EvalUtils.toBoolean(eval(thread, ifClause.getCondition()))) {
               execClauses(index + 1);
             }
           }
@@ -671,12 +674,12 @@
         // base case: evaluate body and add to result.
         if (dict != null) {
           DictExpression.Entry body = (DictExpression.Entry) comp.getBody();
-          Object k = eval(env, body.getKey());
-          EvalUtils.checkValidDictKey(k, env);
-          Object v = eval(env, body.getValue());
-          dict.put(k, v, comp.getLocation(), env);
+          Object k = eval(thread, body.getKey());
+          EvalUtils.checkValidDictKey(k, thread);
+          Object v = eval(thread, body.getValue());
+          dict.put(k, v, comp.getLocation(), thread);
         } else {
-          list.add(eval(env, ((Expression) comp.getBody())));
+          list.add(eval(thread, ((Expression) comp.getBody())));
         }
       }
     }
@@ -691,12 +694,12 @@
       if (clause instanceof Comprehension.For) {
         for (Identifier ident :
             Identifier.boundIdentifiers(((Comprehension.For) clause).getVars())) {
-          env.removeLocalBinding(ident.getName());
+          thread.removeLocalBinding(ident.getName());
         }
       }
     }
 
-    return comp.isDict() ? dict : SkylarkList.MutableList.copyOf(env, list);
+    return comp.isDict() ? dict : SkylarkList.MutableList.copyOf(thread, list);
   }
 
   /** Returns an exception which should be thrown instead of the original one. */
@@ -775,11 +778,14 @@
    * @param posargs a list to which all positional arguments will be added
    * @param kwargs a mutable map to which all keyword arguments will be added. A mutable map is used
    *     here instead of an immutable map builder to deal with duplicates without memory overhead
-   * @param env the current environment
+   * @param thread the Starlark thread for the call
    */
   @SuppressWarnings("unchecked")
   private static void evalArguments(
-      Environment env, FuncallExpression call, List<Object> posargs, Map<String, Object> kwargs)
+      StarlarkThread thread,
+      FuncallExpression call,
+      List<Object> posargs,
+      Map<String, Object> kwargs)
       throws EvalException, InterruptedException {
 
     // Optimize allocations for the common case where they are no duplicates.
@@ -792,7 +798,7 @@
     // (O(1) for ImmutableList) to avoid the iterator overhead.
     for (int i = 0; i < call.getArguments().size(); i++) {
       Argument.Passed arg = call.getArguments().get(i);
-      Object value = Eval.eval(env, arg.getValue());
+      Object value = Eval.eval(thread, arg.getValue());
       if (arg.isPositional()) {
         posargs.add(value);
       } else if (arg.isStar()) { // expand the starArg
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java b/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java
index 77760dd..8c99b36 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java
@@ -92,8 +92,8 @@
             return Integer.compare((Integer) o1, (Integer) o2);
           }
 
-          o1 = SkylarkType.convertToSkylark(o1, (Environment) null);
-          o2 = SkylarkType.convertToSkylark(o2, (Environment) null);
+          o1 = SkylarkType.convertToSkylark(o1, (StarlarkThread) null);
+          o2 = SkylarkType.convertToSkylark(o2, (StarlarkThread) null);
 
           if (o1 instanceof SkylarkList
               && o2 instanceof SkylarkList
@@ -122,9 +122,9 @@
    * @param o an Object to validate
    * @throws EvalException if o is not a valid key
    */
-  public static void checkValidDictKey(Object o, Environment env) throws EvalException {
+  public static void checkValidDictKey(Object o, StarlarkThread thread) throws EvalException {
     // TODO(bazel-team): check that all recursive elements are both Immutable AND Comparable.
-    if (env != null && env.getSemantics().incompatibleDisallowHashingFrozenMutables()) {
+    if (thread != null && thread.getSemantics().incompatibleDisallowHashingFrozenMutables()) {
       if (isHashable(o)) {
         return;
       }
@@ -342,7 +342,7 @@
     }
   }
 
-  public static Collection<?> toCollection(Object o, Location loc, @Nullable Environment env)
+  public static Collection<?> toCollection(Object o, Location loc, @Nullable StarlarkThread thread)
       throws EvalException {
     if (o instanceof Collection) {
       return (Collection<?>) o;
@@ -366,7 +366,7 @@
         throw new EvalException(loc, e);
       }
     } else if (o instanceof SkylarkNestedSet) {
-      return nestedSetToCollection((SkylarkNestedSet) o, loc, env);
+      return nestedSetToCollection((SkylarkNestedSet) o, loc, thread);
     } else {
       throw new EvalException(loc,
           "type '" + getDataTypeName(o) + "' is not a collection");
@@ -374,8 +374,8 @@
   }
 
   private static Collection<?> nestedSetToCollection(
-      SkylarkNestedSet set, Location loc, @Nullable Environment env) throws EvalException {
-    if (env != null && env.getSemantics().incompatibleDepsetIsNotIterable()) {
+      SkylarkNestedSet set, Location loc, @Nullable StarlarkThread thread) throws EvalException {
+    if (thread != null && thread.getSemantics().incompatibleDepsetIsNotIterable()) {
       throw new EvalException(
           loc,
           "type 'depset' is not iterable. Use the `to_list()` method to get a list. Use "
@@ -395,14 +395,14 @@
     }
   }
 
-  public static Iterable<?> toIterable(Object o, Location loc, @Nullable Environment env)
+  public static Iterable<?> toIterable(Object o, Location loc, @Nullable StarlarkThread thread)
       throws EvalException {
     if (o instanceof SkylarkNestedSet) {
-      return nestedSetToCollection((SkylarkNestedSet) o, loc, env);
+      return nestedSetToCollection((SkylarkNestedSet) o, loc, thread);
     } else if (o instanceof Iterable) {
       return (Iterable<?>) o;
     } else if (o instanceof Map) {
-      return toCollection(o, loc, env);
+      return toCollection(o, loc, thread);
     } else {
       throw new EvalException(loc,
           "type '" + getDataTypeName(o) + "' is not iterable");
@@ -424,12 +424,12 @@
    *     is no longer supported.
    */
   @Deprecated
-  public static Iterable<?> toIterableStrict(Object o, Location loc, @Nullable Environment env)
-      throws EvalException {
+  public static Iterable<?> toIterableStrict(
+      Object o, Location loc, @Nullable StarlarkThread thread) throws EvalException {
     if (o instanceof Iterable) {
       return (Iterable<?>) o;
     } else if (o instanceof SkylarkNestedSet) {
-      return nestedSetToCollection((SkylarkNestedSet) o, loc, env);
+      return nestedSetToCollection((SkylarkNestedSet) o, loc, thread);
     } else {
       throw new EvalException(loc,
           "expected Iterable or depset, but got '" + getDataTypeName(o) + "' (strings and maps "
@@ -582,19 +582,15 @@
   /**
    * Build a SkylarkDict of kwarg arguments from a list, removing null-s or None-s.
    *
-   * @param env the Environment in which this map can be mutated.
-   * @param init a series of key, value pairs (as consecutive arguments)
-   *   as in {@code optionMap(k1, v1, k2, v2, k3, v3)}
-   *   where each key is a String, each value is an arbitrary Objet.
-   * @return a {@code Map<String, Object>} that has all the specified entries,
-   *   where key, value pairs appearing earlier have precedence,
-   *   i.e. {@code k1, v1} may override {@code k3, v3}.
-   *
-   * Ignore any entry where the value is null or None.
-   * Keys cannot be null.
+   * @param thread the StarlarkThread in which this map can be mutated.
+   * @param init a series of key, value pairs (as consecutive arguments) as in {@code optionMap(k1,
+   *     v1, k2, v2, k3, v3)} where each key is a String, each value is an arbitrary Objet.
+   * @return a {@code Map<String, Object>} that has all the specified entries, where key, value
+   *     pairs appearing earlier have precedence, i.e. {@code k1, v1} may override {@code k3, v3}.
+   *     <p>Ignore any entry where the value is null or None. Keys cannot be null.
    */
   @SuppressWarnings("unchecked")
-  public static <K, V> SkylarkDict<K, V> optionMap(Environment env, Object... init) {
+  public static <K, V> SkylarkDict<K, V> optionMap(StarlarkThread thread, Object... init) {
     ImmutableMap.Builder<K, V> b = new ImmutableMap.Builder<>();
     Preconditions.checkState(init.length % 2 == 0);
     for (int i = init.length - 2; i >= 0; i -= 2) {
@@ -604,7 +600,7 @@
         b.put(key, value);
       }
     }
-    return SkylarkDict.copyOf(env, b.build());
+    return SkylarkDict.copyOf(thread, b.build());
   }
 
   /**
@@ -617,23 +613,23 @@
   }
 
   /** Returns the named field or method of the specified object. */
-  static Object getAttr(Environment env, Location loc, Object object, String name)
+  static Object getAttr(StarlarkThread thread, Location loc, Object object, String name)
       throws EvalException, InterruptedException {
     MethodDescriptor method =
         object instanceof Class<?>
-            ? CallUtils.getMethod(env.getSemantics(), (Class<?>) object, name)
-            : CallUtils.getMethod(env.getSemantics(), object.getClass(), name);
+            ? CallUtils.getMethod(thread.getSemantics(), (Class<?>) object, name)
+            : CallUtils.getMethod(thread.getSemantics(), object.getClass(), name);
     if (method != null && method.isStructField()) {
       return method.call(
           object,
-          CallUtils.extraInterpreterArgs(method, /*ast=*/ null, loc, env).toArray(),
+          CallUtils.extraInterpreterArgs(method, /*ast=*/ null, loc, thread).toArray(),
           loc,
-          env);
+          thread);
     }
 
     if (object instanceof SkylarkClassObject) {
       try {
-        return ((SkylarkClassObject) object).getValue(loc, env.getSemantics(), name);
+        return ((SkylarkClassObject) object).getValue(loc, thread.getSemantics(), name);
       } catch (IllegalArgumentException ex) { // TODO(adonovan): why necessary?
         throw new EvalException(loc, ex);
       }
@@ -649,7 +645,7 @@
       // ClassObjects may have fields that are annotated with @SkylarkCallable.
       // Since getValue() does not know about those, we cannot expect that result is a valid object.
       if (result != null) {
-        result = SkylarkType.convertToSkylark(result, env);
+        result = SkylarkType.convertToSkylark(result, thread);
         // If we access NestedSets using ClassObject.getValue() we won't know the generic type,
         // so we have to disable it. This should not happen.
         SkylarkType.checkTypeAllowedInSkylark(result, loc);
@@ -710,15 +706,15 @@
   }
 
   /** Evaluates an eager binary operation, {@code x op y}. (Excludes AND and OR.) */
-  static Object binaryOp(TokenKind op, Object x, Object y, Environment env, Location location)
+  static Object binaryOp(TokenKind op, Object x, Object y, StarlarkThread thread, Location location)
       throws EvalException, InterruptedException {
     try {
       switch (op) {
         case PLUS:
-          return plus(x, y, env, location);
+          return plus(x, y, thread, location);
 
         case PIPE:
-          return pipe(x, y, env, location);
+          return pipe(x, y, thread, location);
 
         case AMPERSAND:
           return and(x, y, location);
@@ -736,7 +732,7 @@
           return minus(x, y, location);
 
         case STAR:
-          return mult(x, y, env, location);
+          return mult(x, y, thread, location);
 
         case SLASH:
           throw new EvalException(
@@ -769,10 +765,10 @@
           return compare(x, y, location) >= 0;
 
         case IN:
-          return in(x, y, env, location);
+          return in(x, y, thread, location);
 
         case NOT_IN:
-          return !in(x, y, env, location);
+          return !in(x, y, thread, location);
 
         default:
           throw new AssertionError("Unsupported binary operator: " + op);
@@ -792,7 +788,8 @@
   }
 
   /** Implements 'x + y'. */
-  static Object plus(Object x, Object y, Environment env, Location location) throws EvalException {
+  static Object plus(Object x, Object y, StarlarkThread thread, Location location)
+      throws EvalException {
     // int + int
     if (x instanceof Integer && y instanceof Integer) {
       return Math.addExact((Integer) x, (Integer) y);
@@ -815,18 +812,18 @@
     }
 
     if (x instanceof MutableList && y instanceof MutableList) {
-      return MutableList.concat((MutableList<?>) x, (MutableList<?>) y, env.mutability());
+      return MutableList.concat((MutableList<?>) x, (MutableList<?>) y, thread.mutability());
     }
 
     if (x instanceof SkylarkDict && y instanceof SkylarkDict) {
-      if (env.getSemantics().incompatibleDisallowDictPlus()) {
+      if (thread.getSemantics().incompatibleDisallowDictPlus()) {
         throw new EvalException(
             location,
             "The `+` operator for dicts is deprecated and no longer supported. Please use the "
                 + "`update` method instead. You can temporarily enable the `+` operator by passing "
                 + "the flag --incompatible_disallow_dict_plus=false");
       }
-      return SkylarkDict.plus((SkylarkDict<?, ?>) x, (SkylarkDict<?, ?>) y, env);
+      return SkylarkDict.plus((SkylarkDict<?, ?>) x, (SkylarkDict<?, ?>) y, thread);
     }
 
     if (x instanceof Concatable && y instanceof Concatable) {
@@ -842,7 +839,7 @@
 
     // TODO(bazel-team): Remove deprecated operator.
     if (x instanceof SkylarkNestedSet) {
-      if (env.getSemantics().incompatibleDepsetUnion()) {
+      if (thread.getSemantics().incompatibleDepsetUnion()) {
         throw new EvalException(
             location,
             "`+` operator on a depset is forbidden. See "
@@ -856,12 +853,12 @@
   }
 
   /** Implements 'x | y'. */
-  private static Object pipe(Object x, Object y, Environment env, Location location)
+  private static Object pipe(Object x, Object y, StarlarkThread thread, Location location)
       throws EvalException {
     if (x instanceof Integer && y instanceof Integer) {
       return ((Integer) x) | ((Integer) y);
     } else if (x instanceof SkylarkNestedSet) {
-      if (env.getSemantics().incompatibleDepsetUnion()) {
+      if (thread.getSemantics().incompatibleDepsetUnion()) {
         throw new EvalException(
             location,
             "`|` operator on a depset is forbidden. See "
@@ -883,7 +880,7 @@
   }
 
   /** Implements 'x * y'. */
-  private static Object mult(Object x, Object y, Environment env, Location location)
+  private static Object mult(Object x, Object y, StarlarkThread thread, Location location)
       throws EvalException {
     Integer number = null;
     Object otherFactor = null;
@@ -904,7 +901,7 @@
         return Strings.repeat((String) otherFactor, Math.max(0, number));
       } else if (otherFactor instanceof SkylarkList && !(otherFactor instanceof RangeList)) {
         // Similar to Python, a factor < 1 leads to an empty string.
-        return ((SkylarkList<?>) otherFactor).repeat(number, env.mutability());
+        return ((SkylarkList<?>) otherFactor).repeat(number, thread.mutability());
       }
     }
     throw unknownBinaryOperator(x, y, TokenKind.STAR, location);
@@ -1006,9 +1003,9 @@
   }
 
   /** Implements 'x in y'. */
-  private static boolean in(Object x, Object y, Environment env, Location location)
+  private static boolean in(Object x, Object y, StarlarkThread thread, Location location)
       throws EvalException {
-    if (env.getSemantics().incompatibleDepsetIsNotIterable() && y instanceof SkylarkNestedSet) {
+    if (thread.getSemantics().incompatibleDepsetIsNotIterable() && y instanceof SkylarkNestedSet) {
       throw new EvalException(
           location,
           "argument of type '"
@@ -1018,7 +1015,7 @@
               + "Use --incompatible_depset_is_not_iterable=false to temporarily disable "
               + "this check.");
     } else if (y instanceof SkylarkQueryable) {
-      return ((SkylarkQueryable) y).containsKey(x, location, env);
+      return ((SkylarkQueryable) y).containsKey(x, location, thread);
     } else if (y instanceof String) {
       if (x instanceof String) {
         return ((String) y).contains((String) x);
@@ -1092,14 +1089,14 @@
    *
    * @throws EvalException if {@code object} is not a sequence or mapping.
    */
-  public static Object index(Object object, Object key, Environment env, Location loc)
+  public static Object index(Object object, Object key, StarlarkThread thread, Location loc)
       throws EvalException, InterruptedException {
     if (object instanceof SkylarkIndexable) {
       Object result = ((SkylarkIndexable) object).getIndex(key, loc);
       // TODO(bazel-team): We shouldn't have this convertToSkylark call here. If it's needed at all,
       // it should go in the implementations of SkylarkIndexable#getIndex that produce non-Skylark
       // values.
-      return SkylarkType.convertToSkylark(result, env);
+      return SkylarkType.convertToSkylark(result, thread);
     } else if (object instanceof String) {
       String string = (String) object;
       int index = getSequenceIndex(key, string.length(), loc);
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/MethodDescriptor.java b/src/main/java/com/google/devtools/build/lib/syntax/MethodDescriptor.java
index 8241b91..de6e0f9 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/MethodDescriptor.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/MethodDescriptor.java
@@ -47,7 +47,7 @@
   private final boolean allowReturnNones;
   private final boolean useLocation;
   private final boolean useAst;
-  private final boolean useEnvironment;
+  private final boolean useStarlarkThread;
   private final boolean useStarlarkSemantics;
 
   private MethodDescriptor(
@@ -64,7 +64,7 @@
       boolean allowReturnNones,
       boolean useLocation,
       boolean useAst,
-      boolean useEnvironment,
+      boolean useStarlarkThread,
       boolean useStarlarkSemantics) {
     this.method = method;
     this.annotation = annotation;
@@ -79,7 +79,7 @@
     this.allowReturnNones = allowReturnNones;
     this.useLocation = useLocation;
     this.useAst = useAst;
-    this.useEnvironment = useEnvironment;
+    this.useStarlarkThread = useStarlarkThread;
     this.useStarlarkSemantics = useStarlarkSemantics;
   }
 
@@ -110,7 +110,7 @@
         annotation.allowReturnNones(),
         annotation.useLocation(),
         annotation.useAst(),
-        annotation.useEnvironment(),
+        annotation.useStarlarkThread(),
         annotation.useStarlarkSemantics());
   }
 
@@ -120,7 +120,7 @@
    * <p>{@code obj} may be {@code null} in case this method is static. Methods with {@code void}
    * return type return {@code None} following Python convention.
    */
-  public Object call(Object obj, Object[] args, Location loc, Environment env)
+  public Object call(Object obj, Object[] args, Location loc, StarlarkThread thread)
       throws EvalException, InterruptedException {
     Preconditions.checkNotNull(obj);
     Object result;
@@ -160,7 +160,7 @@
       }
     }
     // TODO(bazel-team): get rid of this, by having everyone use the Skylark data structures
-    result = SkylarkType.convertToSkylark(result, method, env);
+    result = SkylarkType.convertToSkylark(result, method, thread);
     if (result != null && !EvalUtils.isSkylarkAcceptable(result.getClass())) {
       throw new EvalException(
           loc,
@@ -180,9 +180,9 @@
     return structField;
   }
 
-  /** @see SkylarkCallable#useEnvironment() */
-  public boolean isUseEnvironment() {
-    return useEnvironment;
+  /** @see SkylarkCallable#useStarlarkThread() */
+  public boolean isUseStarlarkThread() {
+    return useStarlarkThread;
   }
 
   /** @see SkylarkCallable#useStarlarkSemantics() */
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java b/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java
index b6575a7..04f6144 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java
@@ -63,10 +63,10 @@
       extraPositionals =
           @Param(name = "args", type = SkylarkList.class, doc = "The elements to be checked."),
       useLocation = true,
-      useEnvironment = true)
-  public Object min(SkylarkList<?> args, Location loc, Environment env) throws EvalException {
+      useStarlarkThread = true)
+  public Object min(SkylarkList<?> args, Location loc, StarlarkThread thread) throws EvalException {
     try {
-      return findExtreme(args, EvalUtils.SKYLARK_COMPARATOR.reverse(), loc, env);
+      return findExtreme(args, EvalUtils.SKYLARK_COMPARATOR.reverse(), loc, thread);
     } catch (ComparisonException e) {
       throw new EvalException(loc, e);
     }
@@ -83,10 +83,10 @@
       extraPositionals =
           @Param(name = "args", type = SkylarkList.class, doc = "The elements to be checked."),
       useLocation = true,
-      useEnvironment = true)
-  public Object max(SkylarkList<?> args, Location loc, Environment env) throws EvalException {
+      useStarlarkThread = true)
+  public Object max(SkylarkList<?> args, Location loc, StarlarkThread thread) throws EvalException {
     try {
-      return findExtreme(args, EvalUtils.SKYLARK_COMPARATOR, loc, env);
+      return findExtreme(args, EvalUtils.SKYLARK_COMPARATOR, loc, thread);
     } catch (ComparisonException e) {
       throw new EvalException(loc, e);
     }
@@ -94,12 +94,13 @@
 
   /** Returns the maximum element from this list, as determined by maxOrdering. */
   private static Object findExtreme(
-      SkylarkList<?> args, Ordering<Object> maxOrdering, Location loc, Environment env)
+      SkylarkList<?> args, Ordering<Object> maxOrdering, Location loc, StarlarkThread thread)
       throws EvalException {
     // Args can either be a list of items to compare, or a singleton list whose element is an
     // iterable of items to compare. In either case, there must be at least one item to compare.
     try {
-      Iterable<?> items = (args.size() == 1) ? EvalUtils.toIterable(args.get(0), loc, env) : args;
+      Iterable<?> items =
+          (args.size() == 1) ? EvalUtils.toIterable(args.get(0), loc, thread) : args;
       return maxOrdering.max(items);
     } catch (NoSuchElementException ex) {
       throw new EvalException(loc, "expected at least one item", ex);
@@ -123,9 +124,9 @@
             legacyNamed = true)
       },
       useLocation = true,
-      useEnvironment = true)
-  public Boolean all(Object collection, Location loc, Environment env) throws EvalException {
-    return !hasElementWithBooleanValue(collection, false, loc, env);
+      useStarlarkThread = true)
+  public Boolean all(Object collection, Location loc, StarlarkThread thread) throws EvalException {
+    return !hasElementWithBooleanValue(collection, false, loc, thread);
   }
 
   @SkylarkCallable(
@@ -145,14 +146,14 @@
             legacyNamed = true)
       },
       useLocation = true,
-      useEnvironment = true)
-  public Boolean any(Object collection, Location loc, Environment env) throws EvalException {
-    return hasElementWithBooleanValue(collection, true, loc, env);
+      useStarlarkThread = true)
+  public Boolean any(Object collection, Location loc, StarlarkThread thread) throws EvalException {
+    return hasElementWithBooleanValue(collection, true, loc, thread);
   }
 
   private static boolean hasElementWithBooleanValue(
-      Object collection, boolean value, Location loc, Environment env) throws EvalException {
-    Iterable<?> iterable = EvalUtils.toIterable(collection, loc, env);
+      Object collection, boolean value, Location loc, StarlarkThread thread) throws EvalException {
+    Iterable<?> iterable = EvalUtils.toIterable(collection, loc, thread);
     for (Object obj : iterable) {
       if (EvalUtils.toBoolean(obj) == value) {
         return true;
@@ -192,12 +193,16 @@
             noneable = true)
       },
       useLocation = true,
-      useEnvironment = true)
+      useStarlarkThread = true)
   public MutableList<?> sorted(
-      Object self, final Object key, Boolean reverse, final Location loc, final Environment env)
+      Object self,
+      final Object key,
+      Boolean reverse,
+      final Location loc,
+      final StarlarkThread thread)
       throws EvalException, InterruptedException {
 
-    ArrayList list = new ArrayList(EvalUtils.toCollection(self, loc, env));
+    ArrayList list = new ArrayList(EvalUtils.toCollection(self, loc, thread));
     if (key == Runtime.NONE) {
       try {
         Collections.sort(list, EvalUtils.SKYLARK_COMPARATOR);
@@ -224,7 +229,7 @@
         }
 
         Object callKeyFunc(Object x) throws EvalException, InterruptedException {
-          return keyfn.call(Collections.singletonList(x), ImmutableMap.of(), ast, env);
+          return keyfn.call(Collections.singletonList(x), ImmutableMap.of(), ast, thread);
         }
       }
 
@@ -249,7 +254,7 @@
     if (reverse) {
       Collections.reverse(list);
     }
-    return MutableList.wrapUnsafe(env, list);
+    return MutableList.wrapUnsafe(thread, list);
   }
 
   @SkylarkCallable(
@@ -266,8 +271,8 @@
             legacyNamed = true),
       },
       useLocation = true,
-      useEnvironment = true)
-  public MutableList<?> reversed(Object sequence, Location loc, Environment env)
+      useStarlarkThread = true)
+  public MutableList<?> reversed(Object sequence, Location loc, StarlarkThread thread)
       throws EvalException {
     // We only allow lists and strings.
     if (sequence instanceof SkylarkDict) {
@@ -276,10 +281,10 @@
       throw new EvalException(loc, "Argument to reversed() must be a sequence, not a depset.");
     }
     ArrayDeque<Object> tmpList = new ArrayDeque<>();
-    for (Object element : EvalUtils.toIterable(sequence, loc, env)) {
+    for (Object element : EvalUtils.toIterable(sequence, loc, thread)) {
       tmpList.addFirst(element);
     }
-    return MutableList.copyOf(env, tmpList);
+    return MutableList.copyOf(thread, tmpList);
   }
 
   @SkylarkCallable(
@@ -298,9 +303,9 @@
             legacyNamed = true)
       },
       useLocation = true,
-      useEnvironment = true)
-  public Tuple<?> tuple(Object x, Location loc, Environment env) throws EvalException {
-    return Tuple.copyOf(EvalUtils.toCollection(x, loc, env));
+      useStarlarkThread = true)
+  public Tuple<?> tuple(Object x, Location loc, StarlarkThread thread) throws EvalException {
+    return Tuple.copyOf(EvalUtils.toCollection(x, loc, thread));
   }
 
   @SkylarkCallable(
@@ -319,9 +324,9 @@
             legacyNamed = true)
       },
       useLocation = true,
-      useEnvironment = true)
-  public MutableList<?> list(Object x, Location loc, Environment env) throws EvalException {
-    return MutableList.copyOf(env, EvalUtils.toCollection(x, loc, env));
+      useStarlarkThread = true)
+  public MutableList<?> list(Object x, Location loc, StarlarkThread thread) throws EvalException {
+    return MutableList.copyOf(thread, EvalUtils.toCollection(x, loc, thread));
   }
 
   @SkylarkCallable(
@@ -335,8 +340,8 @@
             legacyNamed = true)
       },
       useLocation = true,
-      useEnvironment = true)
-  public Integer len(Object x, Location loc, Environment env) throws EvalException {
+      useStarlarkThread = true)
+  public Integer len(Object x, Location loc, StarlarkThread thread) throws EvalException {
     if (x instanceof String) {
       return ((String) x).length();
     } else if (x instanceof Map) {
@@ -344,7 +349,7 @@
     } else if (x instanceof SkylarkList) {
       return ((SkylarkList<?>) x).size();
     } else if (x instanceof SkylarkNestedSet) {
-      if (env.getSemantics().incompatibleDepsetIsNotIterable()) {
+      if (thread.getSemantics().incompatibleDepsetIsNotIterable()) {
         throw new EvalException(
             loc,
             EvalUtils.getDataTypeName(x)
@@ -605,14 +610,15 @@
       },
       extraKeywords = @Param(name = "kwargs", doc = "Dictionary of additional entries."),
       useLocation = true,
-      useEnvironment = true)
+      useStarlarkThread = true)
   public SkylarkDict<?, ?> dict(
-      Object args, SkylarkDict<?, ?> kwargs, Location loc, Environment env) throws EvalException {
+      Object args, SkylarkDict<?, ?> kwargs, Location loc, StarlarkThread thread)
+      throws EvalException {
     SkylarkDict<?, ?> argsDict =
         args instanceof SkylarkDict
             ? (SkylarkDict) args
-            : SkylarkDict.getDictFromArgs("dict", args, loc, env);
-    return SkylarkDict.plus(argsDict, kwargs, env);
+            : SkylarkDict.getDictFromArgs("dict", args, loc, thread);
+    return SkylarkDict.plus(argsDict, kwargs, thread);
   }
 
   @SkylarkCallable(
@@ -632,17 +638,17 @@
             defaultValue = "0",
             named = true)
       },
-      useEnvironment = true,
+      useStarlarkThread = true,
       useLocation = true)
-  public MutableList<?> enumerate(Object input, Integer start, Location loc, Environment env)
+  public MutableList<?> enumerate(Object input, Integer start, Location loc, StarlarkThread thread)
       throws EvalException {
     int count = start;
     ArrayList<SkylarkList<?>> result = new ArrayList<>();
-    for (Object obj : EvalUtils.toCollection(input, loc, env)) {
+    for (Object obj : EvalUtils.toCollection(input, loc, thread)) {
       result.add(Tuple.of(count, obj));
       count++;
     }
-    return MutableList.wrapUnsafe(env, result);
+    return MutableList.wrapUnsafe(thread, result);
   }
 
   @SkylarkCallable(
@@ -704,9 +710,9 @@
             legacyNamed = true)
       },
       useLocation = true,
-      useEnvironment = true)
+      useStarlarkThread = true)
   public SkylarkList<Integer> range(
-      Integer startOrStop, Object stopOrNone, Integer step, Location loc, Environment env)
+      Integer startOrStop, Object stopOrNone, Integer step, Location loc, StarlarkThread thread)
       throws EvalException {
     int start;
     int stop;
@@ -746,13 +752,13 @@
             // TODO(cparsons): This parameter should be positional-only.
             legacyNamed = true)
       },
-      useEnvironment = true)
-  public Boolean hasAttr(Object obj, String name, Environment env) throws EvalException {
+      useStarlarkThread = true)
+  public Boolean hasAttr(Object obj, String name, StarlarkThread thread) throws EvalException {
     if (obj instanceof ClassObject && ((ClassObject) obj).getValue(name) != null) {
       return true;
     }
     // shouldn't this filter things with struct_field = false?
-    return EvalUtils.hasMethod(env.getSemantics(), obj, name);
+    return EvalUtils.hasMethod(thread.getSemantics(), obj, name);
   }
 
   @SkylarkCallable(
@@ -786,15 +792,16 @@
             noneable = true)
       },
       useLocation = true,
-      useEnvironment = true)
-  public Object getAttr(Object obj, String name, Object defaultValue, Location loc, Environment env)
+      useStarlarkThread = true)
+  public Object getAttr(
+      Object obj, String name, Object defaultValue, Location loc, StarlarkThread thread)
       throws EvalException, InterruptedException {
-    Object result = EvalUtils.getAttr(env, loc, obj, name);
+    Object result = EvalUtils.getAttr(thread, loc, obj, name);
     if (result == null) {
       if (defaultValue != Runtime.UNBOUND) {
         return defaultValue;
       }
-      throw EvalUtils.getMissingFieldException(obj, name, loc, env.getSemantics(), "attribute");
+      throw EvalUtils.getMissingFieldException(obj, name, loc, thread.getSemantics(), "attribute");
     }
     return result;
   }
@@ -813,16 +820,17 @@
             noneable = true)
       },
       useLocation = true,
-      useEnvironment = true)
-  public MutableList<?> dir(Object object, Location loc, Environment env) throws EvalException {
+      useStarlarkThread = true)
+  public MutableList<?> dir(Object object, Location loc, StarlarkThread thread)
+      throws EvalException {
     // Order the fields alphabetically.
     Set<String> fields = new TreeSet<>();
     if (object instanceof ClassObject) {
       fields.addAll(((ClassObject) object).getFieldNames());
     }
     fields.addAll(Runtime.getBuiltinRegistry().getFunctionNames(object.getClass()));
-    fields.addAll(CallUtils.getMethodNames(env.getSemantics(), object.getClass()));
-    return MutableList.copyOf(env, fields);
+    fields.addAll(CallUtils.getMethodNames(thread.getSemantics(), object.getClass()));
+    return MutableList.copyOf(thread, fields);
   }
 
   @SkylarkCallable(
@@ -881,18 +889,19 @@
       // NB: as compared to Python3, we're missing optional named-only arguments 'end' and 'file'
       extraPositionals = @Param(name = "args", doc = "The objects to print."),
       useLocation = true,
-      useEnvironment = true)
-  public Runtime.NoneType print(String sep, SkylarkList<?> starargs, Location loc, Environment env)
+      useStarlarkThread = true)
+  public Runtime.NoneType print(
+      String sep, SkylarkList<?> starargs, Location loc, StarlarkThread thread)
       throws EvalException {
     try {
       String msg = starargs.stream().map(Printer::debugPrint).collect(joining(sep));
       // As part of the integration test "skylark_flag_test.sh", if the
       // "--internal_skylark_flag_test_canary" flag is enabled, append an extra marker string to
       // the output.
-      if (env.getSemantics().internalSkylarkFlagTestCanary()) {
+      if (thread.getSemantics().internalSkylarkFlagTestCanary()) {
         msg += "<== skylark flag test ==>";
       }
-      env.handleEvent(Event.debug(loc, msg));
+      thread.handleEvent(Event.debug(loc, msg));
       return Runtime.NONE;
     } catch (NestedSetDepthException exception) {
       throw new EvalException(
@@ -1185,12 +1194,12 @@
               + "zip([1, 2], [3, 4, 5])  # == [(1, 3), (2, 4)]</pre>",
       extraPositionals = @Param(name = "args", doc = "lists to zip."),
       useLocation = true,
-      useEnvironment = true)
-  public MutableList<?> zip(SkylarkList<?> args, Location loc, Environment env)
+      useStarlarkThread = true)
+  public MutableList<?> zip(SkylarkList<?> args, Location loc, StarlarkThread thread)
       throws EvalException {
     Iterator<?>[] iterators = new Iterator<?>[args.size()];
     for (int i = 0; i < args.size(); i++) {
-      iterators[i] = EvalUtils.toIterable(args.get(i), loc, env).iterator();
+      iterators[i] = EvalUtils.toIterable(args.get(i), loc, thread).iterator();
     }
     ArrayList<Tuple<?>> result = new ArrayList<>();
     boolean allHasNext;
@@ -1208,7 +1217,7 @@
         result.add(Tuple.copyOf(elem));
       }
     } while (allHasNext);
-    return MutableList.wrapUnsafe(env, result);
+    return MutableList.wrapUnsafe(thread, result);
   }
 
   /** Skylark int type. */
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Mutability.java b/src/main/java/com/google/devtools/build/lib/syntax/Mutability.java
index d994d1f..76ef1a2 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Mutability.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Mutability.java
@@ -22,29 +22,30 @@
 import java.util.List;
 
 /**
- * An object that manages the capability to mutate Skylark objects and their {@link Environment}s.
- * Collectively, the managed objects are called {@link Freezable}s.
+ * An object that manages the capability to mutate Skylark objects and their {@link
+ * StarlarkThread}s. Collectively, the managed objects are called {@link Freezable}s.
  *
- * <p>Each {@code Environment}, and each of the mutable Skylark values (i.e., {@link
- * StarlarkMutable}s) that are created in that {@code Environment}, holds a pointer to the same
- * {@code Mutability} instance. Once the {@code Environment} is done evaluating, its {@code
+ * <p>Each {@code StarlarkThread}, and each of the mutable Skylark values (i.e., {@link
+ * StarlarkMutable}s) that are created in that {@code StarlarkThread}, holds a pointer to the same
+ * {@code Mutability} instance. Once the {@code StarlarkThread} is done evaluating, its {@code
  * Mutability} is irreversibly closed ("frozen"). At that point, it is no longer possible to change
- * either the bindings in that {@code Environment} or the state of its objects. This protects each
- * {@code Environment} from unintentional and unsafe modification.
+ * either the bindings in that {@code StarlarkThread} or the state of its objects. This protects
+ * each {@code StarlarkThread} from unintentional and unsafe modification.
  *
- * <p>{@code Mutability}s enforce isolation between {@code Environment}s; it is illegal for an
- * evaluation in one {@code Environment} to affect the bindings or values of another. In particular,
- * the {@code Environment} for any Skylark module is frozen before its symbols can be imported for
- * use by another module. Each individual {@code Environment}'s evaluation is single-threaded, so
- * this isolation also translates to thread safety. Any number of threads may simultaneously access
- * frozen data. (The {@code Mutability} itself is also thread-safe if and only if it is frozen.}
+ * <p>{@code Mutability}s enforce isolation between {@code StarlarkThread}s; it is illegal for an
+ * evaluation in one {@code StarlarkThread} to affect the bindings or values of another. In
+ * particular, the {@code StarlarkThread} for any Skylark module is frozen before its symbols can be
+ * imported for use by another module. Each individual {@code StarlarkThread}'s evaluation is
+ * single-threaded, so this isolation also translates to thread safety. Any number of threads may
+ * simultaneously access frozen data. (The {@code Mutability} itself is also thread-safe if and only
+ * if it is frozen.}
  *
  * <p>Although the mutability pointer of a {@code Freezable} contains some debugging information
  * about its context, this should not affect the {@code Freezable}'s semantics. From a behavioral
  * point of view, the only thing that matters is whether the {@code Mutability} is frozen, not what
  * particular {@code Mutability} object is pointed to.
  *
- * <p>A {@code Mutability} also tracks which {@code Freezable} objects in its {@code Environment}
+ * <p>A {@code Mutability} also tracks which {@code Freezable} objects in its {@code StarlarkThread}
  * are temporarily locked from mutation. This is used to prevent modification of iterables during
  * loops. A {@code Freezable} may be locked multiple times (e.g., nested loops over the same
  * iterable). Locking an object does not prohibit mutating its deeply contained values, such as in
@@ -71,18 +72,18 @@
  * try (Mutability mutability = Mutability.create(fmt, ...)) { ... }
  * }</pre>
  *
- * The general pattern is to create a {@code Mutability}, build an {@code Environment}, mutate that
- * {@code Environment} and its objects, and possibly return the result from within the {@code try}
- * block, relying on the try-with-resource construct to ensure that everything gets frozen before
- * the result is used. The only code that should create a {@code Mutability} without using
- * try-with-resource is test code that is not part of the Bazel jar.
+ * The general pattern is to create a {@code Mutability}, build an {@code StarlarkThread}, mutate
+ * that {@code StarlarkThread} and its objects, and possibly return the result from within the
+ * {@code try} block, relying on the try-with-resource construct to ensure that everything gets
+ * frozen before the result is used. The only code that should create a {@code Mutability} without
+ * using try-with-resource is test code that is not part of the Bazel jar.
  *
  * <p>We keep some (unchecked) invariants regarding where {@code Mutability} objects may appear
  * within a compound value.
  *
  * <ol>
  *   <li>A compound value can never contain an unfrozen {@code Mutability} for any {@code
- *       Environment} except the one currently being evaluated.
+ *       StarlarkThread} except the one currently being evaluated.
  *   <li>If a value has the special {@link #IMMUTABLE} {@code Mutability}, all of its contents are
  *       themselves deeply immutable too (i.e. have frozen {@code Mutability}s).
  *   <li>If a value has the special {@link #SHALLOW_IMMUTABLE} {@code Mutability}, its contents may
@@ -94,14 +95,14 @@
  * {@code #SHALLOW_IMMUTABLE} instance. This knowledge is used by {@link
  * StarlarkMutable#isImmutable} to prune traversals of a compound value.
  *
- * <p>There is a special API for freezing individual values rather than whole {@code Environment}s.
- * Because this API makes it easier to violate the above invariants, you should avoid using it if at
- * all possible; at the moment it is only used for serialization. Under this API, you may call
- * {@link Freezable#unsafeShallowFreeze} to reset a value's {@code Mutability} pointer to be {@link
- * #IMMUTABLE}. This operation has no effect on the {@code Mutability} itself. It is up to the
- * caller to preserve or restore the above invariants by ensuring that any deeply contained values
- * are also frozen. For safety and explicitness, this operation is disallowed unless the {@code
- * Mutability}'s {@link #allowsUnsafeShallowFreeze} method returns true.
+ * <p>There is a special API for freezing individual values rather than whole {@code
+ * StarlarkThread}s. Because this API makes it easier to violate the above invariants, you should
+ * avoid using it if at all possible; at the moment it is only used for serialization. Under this
+ * API, you may call {@link Freezable#unsafeShallowFreeze} to reset a value's {@code Mutability}
+ * pointer to be {@link #IMMUTABLE}. This operation has no effect on the {@code Mutability} itself.
+ * It is up to the caller to preserve or restore the above invariants by ensuring that any deeply
+ * contained values are also frozen. For safety and explicitness, this operation is disallowed
+ * unless the {@code Mutability}'s {@link #allowsUnsafeShallowFreeze} method returns true.
  */
 public final class Mutability implements AutoCloseable {
 
@@ -287,9 +288,9 @@
   }
 
   /**
-   * An object that refers to a {@link Mutability} to decide whether to allow mutation. All
-   * {@link Freezable} Skylark objects created within a given {@link Environment} will share the
-   * same {@code Mutability} as that {@code Environment}.
+   * An object that refers to a {@link Mutability} to decide whether to allow mutation. All {@link
+   * Freezable} Skylark objects created within a given {@link StarlarkThread} will share the same
+   * {@code Mutability} as that {@code StarlarkThread}.
    */
   public interface Freezable {
     /**
@@ -355,11 +356,11 @@
       throw new MutabilityException("trying to mutate a frozen object");
     }
 
-    // Consider an {@link Environment} e1, in which is created {@link StarlarkFunction} f1, that
+    // Consider an {@link StarlarkThread} e1, in which is created {@link StarlarkFunction} f1, that
     // closes over some variable v1 bound to list l1. If somehow, via the magic of callbacks, f1 or
-    // l1 is passed as an argument to some function f2 evaluated in {@link Environment} e2 while e1
-    // is still mutable, then e2, being a different {@link Environment}, should not be allowed to
-    // mutate objects from e1. It's a bug, that shouldn't happen in our current code base, so we
+    // l1 is passed as an argument to some function f2 evaluated in {@link StarlarkThread} e2 while
+    // e1 is still mutable, then e2, being a different {@link StarlarkThread}, should not be allowed
+    // to mutate objects from e1. It's a bug, that shouldn't happen in our current code base, so we
     // throw an IllegalArgumentException. If in the future such situations are allowed to happen,
     // then we should throw a MutabilityException instead.
     if (!object.mutability().equals(mutability)) {
@@ -380,7 +381,7 @@
   /**
    * A {@code Mutability} indicating that a value is deeply immutable.
    *
-   * <p>It is not associated with any particular {@link Environment}.
+   * <p>It is not associated with any particular {@link StarlarkThread}.
    */
   public static final Mutability IMMUTABLE = create("IMMUTABLE").freeze();
 
@@ -395,7 +396,7 @@
   // TODO(bazel-team): We might be able to remove this instance, and instead have tuples and other
   // immutable types store the same Mutability as other values in that environment. Then we can
   // simplify the Mutability invariant, and implement deep-immutability checking in constant time
-  // for values whose Environments have been frozen.
+  // for values whose StarlarkThreads have been frozen.
   //
   // This would also affect structs (SkylarkInfo). Maybe they would implement an interface similar
   // to StarlarkMutable, or the relevant methods could be worked into SkylarkValue.
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Runtime.java b/src/main/java/com/google/devtools/build/lib/syntax/Runtime.java
index a8d7181..740fcd8 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Runtime.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Runtime.java
@@ -319,8 +319,8 @@
    * All Skylark builtins.
    *
    * <p>Note that just because a symbol is registered here does not necessarily mean that it is
-   * accessible in a particular {@link Environment}. This registry should include any builtin that
-   * is available in any environment.
+   * accessible in a particular {@link StarlarkThread}. This registry should include any builtin
+   * that is available in any environment.
    *
    * <p>Thread safety: This object is unsynchronized. The register functions are typically called
    * from within static initializer blocks, which should be fine.
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkDict.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkDict.java
index eda856f..844a27c 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkDict.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkDict.java
@@ -77,8 +77,8 @@
     this.mutability = mutability == null ? Mutability.IMMUTABLE : mutability;
   }
 
-  private SkylarkDict(@Nullable Environment env) {
-    this.mutability = env == null ? Mutability.IMMUTABLE : env.mutability();
+  private SkylarkDict(@Nullable StarlarkThread thread) {
+    this.mutability = thread == null ? Mutability.IMMUTABLE : thread.mutability();
   }
 
   @SkylarkCallable(
@@ -97,9 +97,9 @@
             doc = "The default value to use (instead of None) if the key is not found.")
       },
       allowReturnNones = true,
-      useEnvironment = true)
-  public Object get(Object key, Object defaultValue, Environment env) throws EvalException {
-    if (containsKey(key, null, env)) {
+      useStarlarkThread = true)
+  public Object get(Object key, Object defaultValue, StarlarkThread thread) throws EvalException {
+    if (containsKey(key, null, thread)) {
       return this.get(key);
     }
     return defaultValue;
@@ -122,12 +122,12 @@
             doc = "a default value if the key is absent."),
       },
       useLocation = true,
-      useEnvironment = true)
-  public Object pop(Object key, Object defaultValue, Location loc, Environment env)
+      useStarlarkThread = true)
+  public Object pop(Object key, Object defaultValue, Location loc, StarlarkThread thread)
       throws EvalException {
     Object value = get(key);
     if (value != null) {
-      remove(key, loc, env.mutability());
+      remove(key, loc, thread.mutability());
       return value;
     }
     if (defaultValue != Runtime.UNBOUND) {
@@ -145,27 +145,25 @@
               + "If the dictionary is empty, calling <code>popitem()</code> fails. "
               + "It is deterministic which pair is returned.",
       useLocation = true,
-      useEnvironment = true
-  )
-  public Tuple<Object> popitem(Location loc, Environment env)
-      throws EvalException {
+      useStarlarkThread = true)
+  public Tuple<Object> popitem(Location loc, StarlarkThread thread) throws EvalException {
     if (isEmpty()) {
       throw new EvalException(loc, "popitem(): dictionary is empty");
     }
     Object key = keySet().iterator().next();
     Object value = get(key);
-    remove(key, loc, env.mutability());
+    remove(key, loc, thread.mutability());
     return Tuple.of(key, value);
   }
 
   @SkylarkCallable(
-    name = "setdefault",
-    doc =
-        "If <code>key</code> is in the dictionary, return its value. "
-            + "If not, insert key with a value of <code>default</code> "
-            + "and return <code>default</code>. "
-            + "<code>default</code> defaults to <code>None</code>.",
-    parameters = {
+      name = "setdefault",
+      doc =
+          "If <code>key</code> is in the dictionary, return its value. "
+              + "If not, insert key with a value of <code>default</code> "
+              + "and return <code>default</code>. "
+              + "<code>default</code> defaults to <code>None</code>.",
+      parameters = {
         @Param(name = "key", type = Object.class, doc = "The key."),
         @Param(
             name = "default",
@@ -173,23 +171,17 @@
             defaultValue = "None",
             named = true,
             noneable = true,
-            doc = "a default value if the key is absent."
-        ),
-    },
-    useLocation = true,
-    useEnvironment = true
-  )
-  public Object setdefault(
-      K key,
-      V defaultValue,
-      Location loc,
-      Environment env)
+            doc = "a default value if the key is absent."),
+      },
+      useLocation = true,
+      useStarlarkThread = true)
+  public Object setdefault(K key, V defaultValue, Location loc, StarlarkThread thread)
       throws EvalException {
     Object value = get(key);
     if (value != null) {
       return value;
     }
-    put(key, defaultValue, loc, env);
+    put(key, defaultValue, loc, thread);
     return defaultValue;
   }
 
@@ -217,62 +209,62 @@
       },
       extraKeywords = @Param(name = "kwargs", doc = "Dictionary of additional entries."),
       useLocation = true,
-      useEnvironment = true)
+      useStarlarkThread = true)
   public Runtime.NoneType update(
-      Object args, SkylarkDict<?, ?> kwargs, Location loc, Environment env) throws EvalException {
+      Object args, SkylarkDict<?, ?> kwargs, Location loc, StarlarkThread thread)
+      throws EvalException {
     // TODO(adonovan): opt: don't materialize dict; call put directly.
 
     // All these types and casts are lies.
     SkylarkDict<K, V> dict =
         args instanceof SkylarkDict
             ? (SkylarkDict<K, V>) args
-            : getDictFromArgs("update", args, loc, env);
-    dict = SkylarkDict.plus(dict, (SkylarkDict<K, V>) kwargs, env);
-    putAll(dict, loc, env.mutability(), env);
+            : getDictFromArgs("update", args, loc, thread);
+    dict = SkylarkDict.plus(dict, (SkylarkDict<K, V>) kwargs, thread);
+    putAll(dict, loc, thread.mutability(), thread);
     return Runtime.NONE;
   }
 
   @SkylarkCallable(
-    name = "values",
-    doc =
-        "Returns the list of values:"
-            + "<pre class=\"language-python\">"
-            + "{2: \"a\", 4: \"b\", 1: \"c\"}.values() == [\"a\", \"b\", \"c\"]</pre>\n",
-    useEnvironment = true
-  )
-  public MutableList<?> invoke(Environment env) throws EvalException {
-    return MutableList.copyOf(env, values());
+      name = "values",
+      doc =
+          "Returns the list of values:"
+              + "<pre class=\"language-python\">"
+              + "{2: \"a\", 4: \"b\", 1: \"c\"}.values() == [\"a\", \"b\", \"c\"]</pre>\n",
+      useStarlarkThread = true)
+  public MutableList<?> invoke(StarlarkThread thread) throws EvalException {
+    return MutableList.copyOf(thread, values());
   }
 
   @SkylarkCallable(
-    name = "items",
-    doc =
-        "Returns the list of key-value tuples:"
-            + "<pre class=\"language-python\">"
-            + "{2: \"a\", 4: \"b\", 1: \"c\"}.items() == [(2, \"a\"), (4, \"b\"), (1, \"c\")]"
-            + "</pre>\n",
-    useEnvironment = true
-  )
-  public MutableList<?> items(Environment env) throws EvalException {
+      name = "items",
+      doc =
+          "Returns the list of key-value tuples:"
+              + "<pre class=\"language-python\">"
+              + "{2: \"a\", 4: \"b\", 1: \"c\"}.items() == [(2, \"a\"), (4, \"b\"), (1, \"c\")]"
+              + "</pre>\n",
+      useStarlarkThread = true)
+  public MutableList<?> items(StarlarkThread thread) throws EvalException {
     ArrayList<Object> list = Lists.newArrayListWithCapacity(size());
     for (Map.Entry<?, ?> entries : entrySet()) {
       list.add(Tuple.of(entries.getKey(), entries.getValue()));
     }
-    return MutableList.wrapUnsafe(env, list);
+    return MutableList.wrapUnsafe(thread, list);
   }
 
-  @SkylarkCallable(name = "keys",
-    doc = "Returns the list of keys:"
-        + "<pre class=\"language-python\">{2: \"a\", 4: \"b\", 1: \"c\"}.keys() == [2, 4, 1]"
-        + "</pre>\n",
-    useEnvironment = true
-  )
-  public MutableList<?> keys(Environment env) throws EvalException {
+  @SkylarkCallable(
+      name = "keys",
+      doc =
+          "Returns the list of keys:"
+              + "<pre class=\"language-python\">{2: \"a\", 4: \"b\", 1: \"c\"}.keys() == [2, 4, 1]"
+              + "</pre>\n",
+      useStarlarkThread = true)
+  public MutableList<?> keys(StarlarkThread thread) throws EvalException {
     ArrayList<Object> list = Lists.newArrayListWithCapacity(size());
     for (Map.Entry<?, ?> entries : entrySet()) {
       list.add(entries.getKey());
     }
-    return MutableList.wrapUnsafe(env, list);
+    return MutableList.wrapUnsafe(thread, list);
   }
 
   private static final SkylarkDict<?, ?> EMPTY = withMutability(Mutability.IMMUTABLE);
@@ -290,19 +282,19 @@
   }
 
   /** @return a dict mutable in given environment only */
-  public static <K, V> SkylarkDict<K, V> of(@Nullable Environment env) {
-    return new SkylarkDict<>(env);
+  public static <K, V> SkylarkDict<K, V> of(@Nullable StarlarkThread thread) {
+    return new SkylarkDict<>(thread);
   }
 
   /** @return a dict mutable in given environment only, with given initial key and value */
-  public static <K, V> SkylarkDict<K, V> of(@Nullable Environment env, K k, V v) {
-    return SkylarkDict.<K, V>of(env).putUnsafe(k, v);
+  public static <K, V> SkylarkDict<K, V> of(@Nullable StarlarkThread thread, K k, V v) {
+    return SkylarkDict.<K, V>of(thread).putUnsafe(k, v);
   }
 
   /** @return a dict mutable in given environment only, with two given initial key value pairs */
   public static <K, V> SkylarkDict<K, V> of(
-      @Nullable Environment env, K k1, V v1, K k2, V v2) {
-    return SkylarkDict.<K, V>of(env).putUnsafe(k1, v1).putUnsafe(k2, v2);
+      @Nullable StarlarkThread thread, K k1, V v1, K k2, V v2) {
+    return SkylarkDict.<K, V>of(thread).putUnsafe(k1, v1).putUnsafe(k2, v2);
   }
 
   // TODO(bazel-team): Make other methods that take in mutabilities instead of environments, make
@@ -315,8 +307,8 @@
 
   /** @return a dict mutable in given environment only, with contents copied from given map */
   public static <K, V> SkylarkDict<K, V> copyOf(
-      @Nullable Environment env, Map<? extends K, ? extends V> m) {
-    return SkylarkDict.<K, V>of(env).putAllUnsafe(m);
+      @Nullable StarlarkThread thread, Map<? extends K, ? extends V> m) {
+    return SkylarkDict.<K, V>of(thread).putAllUnsafe(m);
   }
 
   /** Puts the given entry into the dict, without calling {@link #checkMutable}. */
@@ -367,12 +359,12 @@
 
   /**
    * Convenience version of {@link #put(K, V, Location, Mutability)} that uses the {@link
-   * Mutability} of an {@link Environment}.
+   * Mutability} of an {@link StarlarkThread}.
    */
   // TODO(bazel-team): Decide whether to eliminate this overload.
-  public void put(K key, V value, Location loc, Environment env) throws EvalException {
+  public void put(K key, V value, Location loc, StarlarkThread thread) throws EvalException {
     checkMutable(loc, mutability);
-    EvalUtils.checkValidDictKey(key, env);
+    EvalUtils.checkValidDictKey(key, thread);
     contents.put(key, value);
   }
 
@@ -385,11 +377,12 @@
    * @throws EvalException if some key is invalid or the dict is frozen
    */
   public <KK extends K, VV extends V> void putAll(
-      Map<KK, VV> map, Location loc, Mutability mutability, Environment env) throws EvalException {
+      Map<KK, VV> map, Location loc, Mutability mutability, StarlarkThread thread)
+      throws EvalException {
     checkMutable(loc, mutability);
     for (Map.Entry<KK, VV> e : map.entrySet()) {
       KK k = e.getKey();
-      EvalUtils.checkValidDictKey(k, env);
+      EvalUtils.checkValidDictKey(k, thread);
       contents.put(k, e.getValue());
     }
   }
@@ -412,12 +405,9 @@
       name = "clear",
       doc = "Remove all items from the dictionary.",
       useLocation = true,
-      useEnvironment = true
-  )
-  public Runtime.NoneType clearDict(
-      Location loc, Environment env)
-      throws EvalException {
-    clear(loc, env.mutability());
+      useStarlarkThread = true)
+  public Runtime.NoneType clearDict(Location loc, StarlarkThread thread) throws EvalException {
+    clear(loc, thread.mutability());
     return Runtime.NONE;
   }
 
@@ -521,9 +511,10 @@
   }
 
   @Override
-  public final boolean containsKey(Object key, Location loc, Environment env) throws EvalException {
-    if (env.getSemantics().incompatibleDisallowDictLookupUnhashableKeys()) {
-      EvalUtils.checkValidDictKey(key, env);
+  public final boolean containsKey(Object key, Location loc, StarlarkThread thread)
+      throws EvalException {
+    if (thread.getSemantics().incompatibleDisallowDictLookupUnhashableKeys()) {
+      EvalUtils.checkValidDictKey(key, thread);
     }
     return this.containsKey(key);
   }
@@ -531,24 +522,25 @@
   public static <K, V> SkylarkDict<K, V> plus(
       SkylarkDict<? extends K, ? extends V> left,
       SkylarkDict<? extends K, ? extends V> right,
-      @Nullable Environment env) {
-    SkylarkDict<K, V> result = SkylarkDict.of(env);
+      @Nullable StarlarkThread thread) {
+    SkylarkDict<K, V> result = SkylarkDict.of(thread);
     result.putAllUnsafe(left);
     result.putAllUnsafe(right);
     return result;
   }
 
   static <K, V> SkylarkDict<K, V> getDictFromArgs(
-      String funcname, Object args, Location loc, @Nullable Environment env) throws EvalException {
+      String funcname, Object args, Location loc, @Nullable StarlarkThread thread)
+      throws EvalException {
     Iterable<?> seq;
     try {
-      seq = EvalUtils.toIterable(args, loc, env);
+      seq = EvalUtils.toIterable(args, loc, thread);
     } catch (EvalException ex) {
       throw new EvalException(
           loc,
           String.format("in %s, got %s, want iterable", funcname, EvalUtils.getDataTypeName(args)));
     }
-    SkylarkDict<K, V> result = SkylarkDict.of(env);
+    SkylarkDict<K, V> result = SkylarkDict.of(thread);
     int pos = 0;
     for (Object item : seq) {
       Iterable<?> seq2;
@@ -572,7 +564,7 @@
                 funcname, pos, pair.size()));
       }
       // These casts are lies
-      result.put((K) pair.get(0), (V) pair.get(1), loc, env);
+      result.put((K) pair.get(0), (V) pair.get(1), loc, thread);
       pos++;
     }
     return result;
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkList.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkList.java
index c0f4f31..5dc7c41 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkList.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkList.java
@@ -197,8 +197,8 @@
    */
   // TODO(bazel-team): Eliminate this function in favor of a new MutableList factory method. With
   // such a method, we may no longer need to take null as a possible value for the Mutability or
-  // Environment. That in turn would allow us to overload MutableList#of to take either a Mutability
-  // or Environment.
+  // StarlarkThread. That in turn would allow us to overload MutableList#of to take either a
+  // Mutability or StarlarkThread.
   public static <E> SkylarkList<E> createImmutable(Iterable<? extends E> contents) {
     return MutableList.copyOf(Mutability.IMMUTABLE, contents);
   }
@@ -242,8 +242,9 @@
      * performance reasons. May be used when the calling code will not modify the supplied list
      * after calling (honor system).
      */
-    static <T> MutableList<T> wrapUnsafe(@Nullable Environment env, ArrayList<T> rawContents) {
-      return wrapUnsafe(env == null ? null : env.mutability(), rawContents);
+    static <T> MutableList<T> wrapUnsafe(
+        @Nullable StarlarkThread thread, ArrayList<T> rawContents) {
+      return wrapUnsafe(thread == null ? null : thread.mutability(), rawContents);
     }
 
     /**
@@ -285,24 +286,22 @@
 
     /**
      * Returns a {@code MutableList} whose items are given by an iterable and which has the {@link
-     * Mutability} belonging to the given {@link Environment}. If {@code env} is null, the list is
-     * immutable.
+     * Mutability} belonging to the given {@link StarlarkThread}. If {@code thread} is null, the
+     * list is immutable.
      */
     public static <T> MutableList<T> copyOf(
-        @Nullable Environment env, Iterable<? extends T> contents) {
-      return MutableList.copyOf(
-          env == null ? null : env.mutability(),
-          contents);
+        @Nullable StarlarkThread thread, Iterable<? extends T> contents) {
+      return MutableList.copyOf(thread == null ? null : thread.mutability(), contents);
     }
 
     /**
      * Returns a {@code MutableList} with the given items and the {@link Mutability} of the given
-     * {@link Environment}. If {@code env} is null, the list is immutable.
+     * {@link StarlarkThread}. If {@code thread} is null, the list is immutable.
      */
-    public static <T> MutableList<T> of(@Nullable Environment env, T... contents) {
+    public static <T> MutableList<T> of(@Nullable StarlarkThread thread, T... contents) {
       // Safe since we're taking a copy of the input.
       return MutableList.wrapUnsafe(
-          env == null ? null : env.mutability(), Lists.newArrayList(contents));
+          thread == null ? null : thread.mutability(), Lists.newArrayList(contents));
     }
 
     @Override
@@ -437,17 +436,14 @@
         doc =
             "Removes the first item from the list whose value is x. "
                 + "It is an error if there is no such item.",
-        parameters = {
-            @Param(name = "x", type = Object.class, doc = "The object to remove.")
-        },
+        parameters = {@Param(name = "x", type = Object.class, doc = "The object to remove.")},
         useLocation = true,
-        useEnvironment = true
-    )
-    public Runtime.NoneType removeObject(Object x, Location loc, Environment env)
+        useStarlarkThread = true)
+    public Runtime.NoneType removeObject(Object x, Location loc, StarlarkThread thread)
         throws EvalException {
       for (int i = 0; i < size(); i++) {
         if (get(i).equals(x)) {
-          remove(i, loc, env.mutability());
+          remove(i, loc, thread.mutability());
           return Runtime.NONE;
         }
       }
@@ -469,21 +465,20 @@
     }
 
     @SkylarkCallable(
-      name = "append",
-      doc = "Adds an item to the end of the list.",
-      parameters = {
-          @Param(name = "item",
-            type = Object.class,
-            doc = "Item to add at the end.",
-            noneable = true)
-      },
-      useLocation = true,
-      useEnvironment = true
-    )
-    public Runtime.NoneType append(
-        E item, Location loc, Environment env)
+        name = "append",
+        doc = "Adds an item to the end of the list.",
+        parameters = {
+          @Param(
+              name = "item",
+              type = Object.class,
+              doc = "Item to add at the end.",
+              noneable = true)
+        },
+        useLocation = true,
+        useStarlarkThread = true)
+    public Runtime.NoneType append(E item, Location loc, StarlarkThread thread)
         throws EvalException {
-      add(item, loc, env.mutability());
+      add(item, loc, thread.mutability());
       return Runtime.NONE;
     }
 
@@ -491,27 +486,25 @@
         name = "clear",
         doc = "Removes all the elements of the list.",
         useLocation = true,
-        useEnvironment = true)
-    public Runtime.NoneType clearMethod(Location loc, Environment env) throws EvalException {
-      checkMutable(loc, env.mutability());
+        useStarlarkThread = true)
+    public Runtime.NoneType clearMethod(Location loc, StarlarkThread thread) throws EvalException {
+      checkMutable(loc, thread.mutability());
       contents.clear();
       return Runtime.NONE;
     }
 
     @SkylarkCallable(
-      name = "insert",
-      doc = "Inserts an item at a given position.",
-      parameters = {
+        name = "insert",
+        doc = "Inserts an item at a given position.",
+        parameters = {
           @Param(name = "index", type = Integer.class, doc = "The index of the given position."),
           @Param(name = "item", type = Object.class, doc = "The item.", noneable = true)
-      },
-      useLocation = true,
-      useEnvironment = true
-    )
-    public Runtime.NoneType insert(
-        Integer index, E item, Location loc, Environment env)
+        },
+        useLocation = true,
+        useStarlarkThread = true)
+    public Runtime.NoneType insert(Integer index, E item, Location loc, StarlarkThread thread)
         throws EvalException {
-      add(EvalUtils.clampRangeEndpoint(index, size()), item, loc, env.mutability());
+      add(EvalUtils.clampRangeEndpoint(index, size()), item, loc, thread.mutability());
       return Runtime.NONE;
     }
 
@@ -522,11 +515,13 @@
           @Param(name = "items", type = Object.class, doc = "Items to add at the end.")
         },
         useLocation = true,
-        useEnvironment = true)
-    public Runtime.NoneType extend(Object items, Location loc, Environment env)
+        useStarlarkThread = true)
+    public Runtime.NoneType extend(Object items, Location loc, StarlarkThread thread)
         throws EvalException {
       addAll(
-          (Collection<? extends E>) EvalUtils.toCollection(items, loc, env), loc, env.mutability());
+          (Collection<? extends E>) EvalUtils.toCollection(items, loc, thread),
+          loc,
+          thread.mutability());
       return Runtime.NONE;
     }
 
@@ -571,36 +566,33 @@
     }
 
     @SkylarkCallable(
-      name = "pop",
-      doc =
-          "Removes the item at the given position in the list, and returns it. "
-              + "If no <code>index</code> is specified, "
-              + "it removes and returns the last item in the list.",
-      parameters = {
+        name = "pop",
+        doc =
+            "Removes the item at the given position in the list, and returns it. "
+                + "If no <code>index</code> is specified, "
+                + "it removes and returns the last item in the list.",
+        parameters = {
           @Param(
               name = "i",
               type = Integer.class,
               noneable = true,
               defaultValue = "None",
-              doc = "The index of the item."
-          )
-      },
-      useLocation = true,
-      useEnvironment = true
-    )
-    public Object pop(Object i, Location loc, Environment env)
-        throws EvalException {
+              doc = "The index of the item.")
+        },
+        useLocation = true,
+        useStarlarkThread = true)
+    public Object pop(Object i, Location loc, StarlarkThread thread) throws EvalException {
       int arg = i == Runtime.NONE ? -1 : (Integer) i;
       int index = EvalUtils.getSequenceIndex(arg, size(), loc);
       Object result = get(index);
-      remove(index, loc, env.mutability());
+      remove(index, loc, thread.mutability());
       return result;
     }
   }
 
   /**
    * A Skylark tuple, i.e. the value represented by {@code (1, 2, 3)}. Tuples are always immutable
-   * (regardless of the {@link Environment} they are created in).
+   * (regardless of the {@link StarlarkThread} they are created in).
    */
   @SkylarkModule(
       name = "tuple",
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkNestedSet.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkNestedSet.java
index 66f0d85..313bd0e 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkNestedSet.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkNestedSet.java
@@ -331,14 +331,13 @@
               + "of the given depset and <code>new_elements</code>. Use the "
               + "<code>transitive</code> constructor argument instead.",
       parameters = {
-          @Param(name = "new_elements", type = Object.class, doc = "The elements to be added.")
+        @Param(name = "new_elements", type = Object.class, doc = "The elements to be added.")
       },
       useLocation = true,
-      useEnvironment = true
-  )
-  public SkylarkNestedSet union(Object newElements, Location loc, Environment env)
+      useStarlarkThread = true)
+  public SkylarkNestedSet union(Object newElements, Location loc, StarlarkThread thread)
       throws EvalException {
-    if (env.getSemantics().incompatibleDepsetUnion()) {
+    if (thread.getSemantics().incompatibleDepsetUnion()) {
       throw new EvalException(
           loc,
           "depset method `.union` has been removed. See "
@@ -361,11 +360,11 @@
               + "</code>-ordered depsets, and for elements of child depsets whose order differs "
               + "from that of the parent depset. The list is a copy; modifying it has no effect "
               + "on the depset and vice versa.",
-      useEnvironment = true,
+      useStarlarkThread = true,
       useLocation = true)
-  public MutableList<Object> toList(Location location, Environment env) throws EvalException {
+  public MutableList<Object> toList(Location location, StarlarkThread thread) throws EvalException {
     try {
-      return MutableList.copyOf(env, this.toCollection());
+      return MutableList.copyOf(thread, this.toCollection());
     } catch (NestedSetDepthException exception) {
       throw new EvalException(
           location,
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkQueryable.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkQueryable.java
index 6d00b7b..79e0482 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkQueryable.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkQueryable.java
@@ -28,7 +28,8 @@
   // Variant used when called directly from a Starlark thread.
   // This is a temporary workaround to enable --incompatible_disallow_dict_lookup_unhashable_keys.
   // TODO(adonovan): remove when that flag is removed.
-  default boolean containsKey(Object key, Location loc, Environment env) throws EvalException {
+  default boolean containsKey(Object key, Location loc, StarlarkThread thread)
+      throws EvalException {
     return this.containsKey(key, loc);
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java
index 45b536c..83723fa 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java
@@ -243,15 +243,15 @@
           return defaultValue;
         }
         try (Mutability mutability = Mutability.create("initialization")) {
-          // Note that this Skylark environment ignores command line flags.
-          Environment env =
-              Environment.builder(mutability)
+          // Note that this Skylark thread ignores command line flags.
+          StarlarkThread thread =
+              StarlarkThread.builder(mutability)
                   .useDefaultSemantics()
-                  .setGlobals(Environment.CONSTANTS_ONLY)
-                  .setEventHandler(Environment.FAIL_FAST_HANDLER)
+                  .setGlobals(StarlarkThread.CONSTANTS_ONLY)
+                  .setEventHandler(StarlarkThread.FAIL_FAST_HANDLER)
                   .build()
                   .update("unbound", Runtime.UNBOUND);
-          defaultValue = BuildFileAST.eval(ParserInput.fromLines(paramDefaultValue), env);
+          defaultValue = BuildFileAST.eval(ParserInput.fromLines(paramDefaultValue), thread);
           defaultValueCache.put(paramDefaultValue, defaultValue);
           return defaultValue;
         }
@@ -269,7 +269,7 @@
   public static ExtraArgKind[] getExtraArgs(SkylarkSignature annotation) {
     final int numExtraArgs =
         Booleans.countTrue(
-            annotation.useLocation(), annotation.useAst(), annotation.useEnvironment());
+            annotation.useLocation(), annotation.useAst(), annotation.useStarlarkThread());
     if (numExtraArgs == 0) {
       return null;
     }
@@ -281,7 +281,7 @@
     if (annotation.useAst()) {
       extraArgs[i++] = ExtraArgKind.SYNTAX_TREE;
     }
-    if (annotation.useEnvironment()) {
+    if (annotation.useStarlarkThread()) {
       extraArgs[i++] = ExtraArgKind.ENVIRONMENT;
     }
     return extraArgs;
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkType.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkType.java
index 8320ef8..373e9fd 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkType.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkType.java
@@ -780,22 +780,18 @@
     throw new IllegalStateException("Cannot infer type from method signature " + method);
   }
 
-  /**
-   * Converts an object retrieved from a Java method to a Skylark-compatible type.
-   */
-  static Object convertToSkylark(Object object, Method method, @Nullable Environment env) {
+  /** Converts an object retrieved from a Java method to a Skylark-compatible type. */
+  static Object convertToSkylark(Object object, Method method, @Nullable StarlarkThread thread) {
     if (object instanceof NestedSet<?>) {
       return SkylarkNestedSet.of(
           SkylarkType.of(getGenericTypeFromMethod(method)), (NestedSet<?>) object);
     }
-    return convertToSkylark(object, env);
+    return convertToSkylark(object, thread);
   }
 
-  /**
-   * Converts an object to a Skylark-compatible type if possible.
-   */
-  public static Object convertToSkylark(Object object, @Nullable Environment env) {
-    return convertToSkylark(object, env == null ? null : env.mutability());
+  /** Converts an object to a Skylark-compatible type if possible. */
+  public static Object convertToSkylark(Object object, @Nullable StarlarkThread thread) {
+    return convertToSkylark(object, thread == null ? null : thread.mutability());
   }
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkUtils.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkUtils.java
index d8e3f8a..d8289cd 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkUtils.java
@@ -27,35 +27,35 @@
     ANALYSIS
   }
 
-  public static void setPhase(Environment env, Phase phase) {
-    env.setThreadLocal(Phase.class, phase);
+  public static void setPhase(StarlarkThread thread, Phase phase) {
+    thread.setThreadLocal(Phase.class, phase);
   }
 
-  private static Phase getPhase(Environment env) {
-    Phase phase = env.getThreadLocal(Phase.class);
+  private static Phase getPhase(StarlarkThread thread) {
+    Phase phase = thread.getThreadLocal(Phase.class);
     return phase == null ? Phase.ANALYSIS : phase;
   }
 
   /**
-   * Checks that the current Environment is in the loading or the workspace phase.
+   * Checks that the current StarlarkThread is in the loading or the workspace phase.
    *
    * @param symbol name of the function being only authorized thus.
    */
-  public static void checkLoadingOrWorkspacePhase(Environment env, String symbol, Location loc)
-      throws EvalException {
-    if (getPhase(env) == Phase.ANALYSIS) {
+  public static void checkLoadingOrWorkspacePhase(
+      StarlarkThread thread, String symbol, Location loc) throws EvalException {
+    if (getPhase(thread) == Phase.ANALYSIS) {
       throw new EvalException(loc, symbol + "() cannot be called during the analysis phase");
     }
   }
 
   /**
-   * Checks that the current Environment is in the loading phase.
+   * Checks that the current StarlarkThread is in the loading phase.
    *
    * @param symbol name of the function being only authorized thus.
    */
-  public static void checkLoadingPhase(Environment env, String symbol, Location loc)
+  public static void checkLoadingPhase(StarlarkThread thread, String symbol, Location loc)
       throws EvalException {
-    if (getPhase(env) != Phase.LOADING) {
+    if (getPhase(thread) != Phase.LOADING) {
       throw new EvalException(loc, symbol + "() can only be called during the loading phase");
     }
   }
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkCallable.java b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkCallable.java
index 772a836..d721f6d 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkCallable.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkCallable.java
@@ -29,23 +29,23 @@
   /**
    * Call this function with the given arguments.
    *
-   * Neither the callee nor the caller may modify the args List or kwargs Map.
+   * <p>Neither the callee nor the caller may modify the args List or kwargs Map.
    *
    * @param args the list of positional arguments
    * @param kwargs the mapping of named arguments
    * @param call the syntax tree of the function call
-   * @param env the Environment in which the function is called
+   * @param thread the StarlarkThread in which the function is called
    * @return the result of the call
    * @throws EvalException if there was an error invoking this function
    */
   // TODO(adonovan):
-  // - rename Environment to StarlarkThread and make it the first parameter.
-  // - eliminate the FuncallExpression parameter (which can be accessed through env).
+  // - rename StarlarkThread to StarlarkThread and make it the first parameter.
+  // - eliminate the FuncallExpression parameter (which can be accessed through thread).
   public Object call(
       List<Object> args,
       @Nullable Map<String, Object> kwargs,
       @Nullable FuncallExpression call,
-      Environment env)
+      StarlarkThread thread)
       throws EvalException, InterruptedException;
 
   // TODO(adonovan): add a getName method that defines how this callable appears in a stack trace.
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkFunction.java b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkFunction.java
index c62a3b2..09d56bc 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkFunction.java
@@ -19,7 +19,7 @@
 import com.google.devtools.build.lib.profiler.ProfilerTask;
 import com.google.devtools.build.lib.profiler.SilentCloseable;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
-import com.google.devtools.build.lib.syntax.Environment.LexicalFrame;
+import com.google.devtools.build.lib.syntax.StarlarkThread.LexicalFrame;
 
 /** A StarlarkFunction is the function value created by a Starlark {@code def} statement. */
 public class StarlarkFunction extends BaseFunction {
@@ -27,14 +27,14 @@
   private final ImmutableList<Statement> statements;
 
   // we close over the globals at the time of definition
-  private final Environment.GlobalFrame definitionGlobals;
+  private final StarlarkThread.GlobalFrame definitionGlobals;
 
   public StarlarkFunction(
       String name,
       Location location,
       FunctionSignature.WithValues<Object, SkylarkType> signature,
       ImmutableList<Statement> statements,
-      Environment.GlobalFrame definitionGlobals) {
+      StarlarkThread.GlobalFrame definitionGlobals) {
     super(name, signature, location);
     this.statements = statements;
     this.definitionGlobals = definitionGlobals;
@@ -44,37 +44,39 @@
     return statements;
   }
 
-  public Environment.GlobalFrame getDefinitionGlobals() {
+  public StarlarkThread.GlobalFrame getDefinitionGlobals() {
     return definitionGlobals;
   }
 
   @Override
-  public Object call(Object[] arguments, FuncallExpression ast, Environment env)
+  public Object call(Object[] arguments, FuncallExpression ast, StarlarkThread thread)
       throws EvalException, InterruptedException {
-    if (env.mutability().isFrozen()) {
+    if (thread.mutability().isFrozen()) {
       throw new EvalException(getLocation(), "Trying to call in frozen environment");
     }
-    if (env.isRecursiveCall(this)) {
-      throw new EvalException(getLocation(),
-          String.format("Recursion was detected when calling '%s' from '%s'",
-              getName(), env.getCurrentFunction().getName()));
+    if (thread.isRecursiveCall(this)) {
+      throw new EvalException(
+          getLocation(),
+          String.format(
+              "Recursion was detected when calling '%s' from '%s'",
+              getName(), thread.getCurrentFunction().getName()));
     }
 
     ImmutableList<String> names = signature.getSignature().getNames();
-    LexicalFrame lexicalFrame = LexicalFrame.create(env.mutability(), /*numArgs=*/ names.size());
+    LexicalFrame lexicalFrame = LexicalFrame.create(thread.mutability(), /*numArgs=*/ names.size());
     try (SilentCloseable c =
         Profiler.instance().profile(ProfilerTask.STARLARK_USER_FN, getName())) {
-      env.enterScope(this, lexicalFrame, ast, definitionGlobals);
+      thread.enterScope(this, lexicalFrame, ast, definitionGlobals);
 
-      // Registering the functions's arguments as variables in the local Environment
+      // Registering the functions's arguments as variables in the local StarlarkThread
       // foreach loop is not used to avoid iterator overhead
       for (int i = 0; i < names.size(); ++i) {
-        env.update(names.get(i), arguments[i]);
+        thread.update(names.get(i), arguments[i]);
       }
 
-      return Eval.execStatements(env, statements);
+      return Eval.execStatements(thread, statements);
     } finally {
-      env.exitScope();
+      thread.exitScope();
     }
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkMutable.java b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkMutable.java
index da8dae4..73e8397 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkMutable.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkMutable.java
@@ -34,14 +34,14 @@
 public abstract class StarlarkMutable implements Freezable, SkylarkValue {
 
   /**
-   * Checks whether this object is currently mutable in the given {@link Environment}, and throws
+   * Checks whether this object is currently mutable in the given {@link StarlarkThread}, and throws
    * an exception if it is not.
    *
    * @deprecated prefer {@link #checkMutable(Location, Mutability)} instead
    */
   @Deprecated
-  protected void checkMutable(Location loc, Environment env) throws EvalException {
-    checkMutable(loc, env.mutability());
+  protected void checkMutable(Location loc, StarlarkThread thread) throws EvalException {
+    checkMutable(loc, thread.mutability());
   }
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Environment.java b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkThread.java
similarity index 87%
rename from src/main/java/com/google/devtools/build/lib/syntax/Environment.java
rename to src/main/java/com/google/devtools/build/lib/syntax/StarlarkThread.java
index fbbbef2..a7732e2 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Environment.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkThread.java
@@ -48,30 +48,30 @@
 import javax.annotation.Nullable;
 
 /**
- * An Environment represents a Starlark thread.
+ * An StarlarkThread represents a Starlark thread.
  *
  * <p>It holds the stack of active Starlark and built-in function calls. In addition, it may hold
  * per-thread application state (see {@link #setThreadLocal}) that passes through Starlark functions
  * but does not directly affect them, such as information about the BUILD file being loaded.
  *
- * <p>Every {@code Environment} has a {@link Mutability} field, and must be used within a function
- * that creates and closes this {@link Mutability} with the try-with-resource pattern. This {@link
- * Mutability} is also used when initializing mutable objects within that {@code Environment}. When
- * the {@code Mutability} is closed at the end of the computation, it freezes the {@code
- * Environment} along with all of those objects. This pattern enforces the discipline that there
- * should be no dangling mutable {@code Environment}, or concurrency between interacting {@code
- * Environment}s. It is a Skylark-level error to attempt to mutate a frozen {@code Environment} or
- * its objects, but it is a Java-level error to attempt to mutate an unfrozen {@code Environment} or
- * its objects from within a different {@code Environment}.
+ * <p>Every {@code StarlarkThread} has a {@link Mutability} field, and must be used within a
+ * function that creates and closes this {@link Mutability} with the try-with-resource pattern. This
+ * {@link Mutability} is also used when initializing mutable objects within that {@code
+ * StarlarkThread}. When the {@code Mutability} is closed at the end of the computation, it freezes
+ * the {@code StarlarkThread} along with all of those objects. This pattern enforces the discipline
+ * that there should be no dangling mutable {@code StarlarkThread}, or concurrency between
+ * interacting {@code StarlarkThread}s. It is a Skylark-level error to attempt to mutate a frozen
+ * {@code StarlarkThread} or its objects, but it is a Java-level error to attempt to mutate an
+ * unfrozen {@code StarlarkThread} or its objects from within a different {@code StarlarkThread}.
  *
- * <p>One creates an Environment using the {@link #builder} function, then populates it with {@link
- * #setup} and sometimes {@link #setupOverride}, before to evaluate code in it with {@link
+ * <p>One creates an StarlarkThread using the {@link #builder} function, then populates it with
+ * {@link #setup} and sometimes {@link #setupOverride}, before to evaluate code in it with {@link
  * BuildFileAST#eval}, or with {@link BuildFileAST#exec} (where the AST was obtained by passing a
- * {@link ValidationEnvironment} constructed from the Environment to {@link BuildFileAST#parse}.
- * When the computation is over, the frozen Environment can still be queried with {@link #lookup}.
+ * {@link ValidationEnvironment} constructed from the StarlarkThread to {@link BuildFileAST#parse}.
+ * When the computation is over, the frozen StarlarkThread can still be queried with {@link
+ * #lookup}.
  */
-// TODO(adonovan): further steps for Environmental remediation:
-// This class should be renamed StarlarkThread, for that is what it is.
+// TODO(adonovan): further steps for StarlarkThread remediation:
 // Its API should expose the following concepts, and no more:
 // 1) "thread local variables": this holds per-thread application
 //    state such as the current Label, or BUILD package, for all the
@@ -94,7 +94,7 @@
 //    Advanced clients such as the debugger, and the generator_name rule attribute, also need:
 //    - the function value (Warning: careless clients can pin closures in memory)
 //    - Object getLocalValue(Identifier parameter).
-//    3) Debugging support (thread name, profiling counters, etc).
+// 3) Debugging support (thread name, profiling counters, etc).
 // And that is all. See go.starlark.net for the model.
 //
 // The Frame interface should be hidden from clients and then eliminated.
@@ -113,7 +113,7 @@
 // Once the API is small and sound, we can start to represent all
 // the lexical frames within a single function using just an array,
 // indexed by a small integer computed during the validation pass.
-public final class Environment implements Freezable {
+public final class StarlarkThread implements Freezable {
 
   /**
    * A mapping of bindings, either mutable or immutable according to an associated {@link
@@ -121,11 +121,11 @@
    * unspecified.
    *
    * <p>Any non-frozen {@link Frame} must have the same {@link Mutability} as the current {@link
-   * Environment}, to avoid interference from other evaluation contexts. For example, a {@link
-   * StarlarkFunction} will close over the global frame of the {@link Environment} in which it was
-   * defined. When the function is called from other {@link Environment}s (possibly simultaneously),
-   * that global frame must already be frozen; a new local {@link Frame} is created to represent the
-   * lexical scope of the function.
+   * StarlarkThread}, to avoid interference from other evaluation contexts. For example, a {@link
+   * StarlarkFunction} will close over the global frame of the {@link StarlarkThread} in which it
+   * was defined. When the function is called from other {@link StarlarkThread}s (possibly
+   * simultaneously), that global frame must already be frozen; a new local {@link Frame} is created
+   * to represent the lexical scope of the function.
    *
    * <p>A {@link Frame} can have an associated "parent" {@link Frame}, which is used in {@link #get}
    * and {@link #getTransitiveBindings()}
@@ -152,17 +152,17 @@
      * <p>If the binding has the same name as one in a transitive parent, the parent binding is
      * shadowed (i.e., the parent is unaffected).
      *
-     * @param env the {@link Environment} attempting the mutation
+     * @param thread the {@link StarlarkThread} attempting the mutation
      * @param varname the name of the variable to be bound
      * @param value the value to bind to the variable
      */
-    void put(Environment env, String varname, Object value) throws MutabilityException;
+    void put(StarlarkThread thread, String varname, Object value) throws MutabilityException;
 
     /**
      * TODO(laurentlb): Remove this method when possible. It should probably not be part of the
      * public interface.
      */
-    void remove(Environment env, String varname) throws MutabilityException;
+    void remove(StarlarkThread thread, String varname) throws MutabilityException;
 
     /**
      * Returns a map containing all bindings of this {@link Frame} and of its transitive parents,
@@ -202,14 +202,15 @@
     }
 
     @Override
-    public void put(Environment env, String varname, Object value) throws MutabilityException {
-      Mutability.checkMutable(this, env.mutability());
+    public void put(StarlarkThread thread, String varname, Object value)
+        throws MutabilityException {
+      Mutability.checkMutable(this, thread.mutability());
       throw new IllegalStateException();
     }
 
     @Override
-    public void remove(Environment env, String varname) throws MutabilityException {
-      Mutability.checkMutable(this, env.mutability());
+    public void remove(StarlarkThread thread, String varname) throws MutabilityException {
+      Mutability.checkMutable(this, thread.mutability());
       throw new IllegalStateException();
     }
 
@@ -251,14 +252,15 @@
     }
 
     @Override
-    public void put(Environment env, String varname, Object value) throws MutabilityException {
-      Mutability.checkMutable(this, env.mutability());
+    public void put(StarlarkThread thread, String varname, Object value)
+        throws MutabilityException {
+      Mutability.checkMutable(this, thread.mutability());
       bindings.put(varname, value);
     }
 
     @Override
-    public void remove(Environment env, String varname) throws MutabilityException {
-      Mutability.checkMutable(this, env.mutability());
+    public void remove(StarlarkThread thread, String varname) throws MutabilityException {
+      Mutability.checkMutable(this, thread.mutability());
       bindings.remove(varname);
     }
 
@@ -530,16 +532,17 @@
     }
 
     @Override
-    public void put(Environment env, String varname, Object value) throws MutabilityException {
+    public void put(StarlarkThread thread, String varname, Object value)
+        throws MutabilityException {
       checkInitialized();
-      Mutability.checkMutable(this, env.mutability());
+      Mutability.checkMutable(this, thread.mutability());
       bindings.put(varname, value);
     }
 
     @Override
-    public void remove(Environment env, String varname) throws MutabilityException {
+    public void remove(StarlarkThread thread, String varname) throws MutabilityException {
       checkInitialized();
-      Mutability.checkMutable(this, env.mutability());
+      Mutability.checkMutable(this, thread.mutability());
       bindings.remove(varname);
     }
 
@@ -553,7 +556,7 @@
     }
   }
 
-  // The mutability of the Environment comes from its initial global frame.
+  // The mutability of the StarlarkThread comes from its initial global frame.
   private final Mutability mutability;
 
   private final Map<Class<?>, Object> threadLocals = new HashMap<>();
@@ -633,18 +636,18 @@
     }
 
     /**
-     * Constructs using the bindings from the global definitions of the given {@link Environment},
-     * and that {@code Environment}'s transitive hash code.
+     * Constructs using the bindings from the global definitions of the given {@link
+     * StarlarkThread}, and that {@code StarlarkThread}'s transitive hash code.
      */
-    public Extension(Environment env) {
+    public Extension(StarlarkThread thread) {
       // Legacy behavior: all symbols from the global Frame are exported (including symbols
       // introduced by load).
       this(
           ImmutableMap.copyOf(
-              env.getSemantics().incompatibleNoTransitiveLoads()
-                  ? env.globalFrame.getExportedBindings()
-                  : env.globalFrame.getBindings()),
-          env.getTransitiveContentHashCode());
+              thread.getSemantics().incompatibleNoTransitiveLoads()
+                  ? thread.globalFrame.getExportedBindings()
+                  : thread.globalFrame.getBindings()),
+          thread.getTransitiveContentHashCode());
     }
 
     private String getTransitiveContentHashCode() {
@@ -777,8 +780,8 @@
   }
 
   /**
-   * Static Frame for lexical variables that are always looked up in the current Environment or for
-   * the definition Environment of the function currently being evaluated.
+   * Static Frame for lexical variables that are always looked up in the current StarlarkThread or
+   * for the definition StarlarkThread of the function currently being evaluated.
    */
   private Frame lexicalFrame;
 
@@ -816,7 +819,8 @@
    * @param function the function whose scope to enter
    * @param lexical the lexical frame to use
    * @param caller the source AST node for the caller
-   * @param globals the global Frame that this function closes over from its definition Environment
+   * @param globals the global Frame that this function closes over from its definition
+   *     StarlarkThread
    */
   void enterScope(
       BaseFunction function,
@@ -839,7 +843,7 @@
   private final String transitiveHashCode;
 
   /**
-   * Is this a global Environment?
+   * Is this a global StarlarkThread?
    *
    * @return true if the current code is being executed at the top-level, as opposed to inside the
    *     body of a function.
@@ -853,7 +857,7 @@
     return mutability;
   }
 
-  /** Returns the global variables for the Environment (not including dynamic bindings). */
+  /** Returns the global variables for the StarlarkThread (not including dynamic bindings). */
   public GlobalFrame getGlobals() {
     return globalFrame;
   }
@@ -905,14 +909,14 @@
   }
 
   /**
-   * Constructs an Environment. This is the main, most basic constructor.
+   * Constructs an StarlarkThread. This is the main, most basic constructor.
    *
-   * @param globalFrame a frame for the global Environment
+   * @param globalFrame a frame for the global StarlarkThread
    * @param eventHandler an EventHandler for warnings, errors, etc
    * @param importedExtensions Extension-s from which to import bindings with load()
    * @param fileContentHashCode a hash for the source file being evaluated, if any
    */
-  private Environment(
+  private StarlarkThread(
       GlobalFrame globalFrame,
       StarlarkSemantics semantics,
       EventHandler eventHandler,
@@ -930,7 +934,7 @@
   }
 
   /**
-   * A Builder class for Environment.
+   * A Builder class for StarlarkThread.
    *
    * <p>The caller must explicitly set the semantics by calling either {@link #setSemantics} or
    * {@link #useDefaultSemantics}.
@@ -982,14 +986,14 @@
       return this;
     }
 
-    /** Declares content hash for the source file for this Environment. */
+    /** Declares content hash for the source file for this StarlarkThread. */
     public Builder setFileContentHashCode(String fileContentHashCode) {
       this.fileContentHashCode = fileContentHashCode;
       return this;
     }
 
-    /** Builds the Environment. */
-    public Environment build() {
+    /** Builds the StarlarkThread. */
+    public StarlarkThread build() {
       Preconditions.checkArgument(!mutability.isFrozen());
       if (semantics == null) {
         throw new IllegalArgumentException("must call either setSemantics or useDefaultSemantics");
@@ -1020,12 +1024,8 @@
       if (importedExtensions == null) {
         importedExtensions = ImmutableMap.of();
       }
-      return new Environment(
-          globalFrame,
-          semantics,
-          eventHandler,
-          importedExtensions,
-          fileContentHashCode);
+      return new StarlarkThread(
+          globalFrame, semantics, eventHandler, importedExtensions, fileContentHashCode);
     }
   }
 
@@ -1043,7 +1043,7 @@
   }
 
   /** Modifies a binding in the current Frame. If it is the module Frame, also export it. */
-  Environment updateAndExport(String varname, Object value) throws EvalException {
+  StarlarkThread updateAndExport(String varname, Object value) throws EvalException {
     update(varname, value);
     if (isGlobal()) {
       globalFrame.exportedBindings.add(varname);
@@ -1052,25 +1052,25 @@
   }
 
   /**
-   * Modifies a binding in the current Frame of this Environment, as would an {@link
+   * Modifies a binding in the current Frame of this StarlarkThread, as would an {@link
    * AssignmentStatement}. Does not try to modify an inherited binding. This will shadow any
    * inherited binding, which may be an error that you want to guard against before calling this
    * function.
    *
    * @param varname the name of the variable to be bound
    * @param value the value to bind to the variable
-   * @return this Environment, in fluid style
+   * @return this StarlarkThread, in fluid style
    */
-  public Environment update(String varname, Object value) throws EvalException {
+  public StarlarkThread update(String varname, Object value) throws EvalException {
     Preconditions.checkNotNull(value, "trying to assign null to '%s'", varname);
     try {
       lexicalFrame.put(this, varname, value);
     } catch (MutabilityException e) {
       // Note that since at this time we don't accept the global keyword, and don't have closures,
-      // end users should never be able to mutate a frozen Environment, and a MutabilityException
+      // end users should never be able to mutate a frozen StarlarkThread, and a MutabilityException
       // is therefore a failed assertion for Bazel. However, it is possible to shadow a binding
-      // imported from a parent Environment by updating the current Environment, which will not
-      // trigger a MutabilityException.
+      // imported from a parent StarlarkThread by updating the current StarlarkThread, which will
+      // not trigger a MutabilityException.
       throw new AssertionError(
           Printer.format("Can't update %s to %r in frozen environment", varname, value), e);
     }
@@ -1078,14 +1078,14 @@
   }
 
   /**
-   * Initializes a binding in this Environment. It is an error if the variable is already bound.
+   * Initializes a binding in this StarlarkThread. It is an error if the variable is already bound.
    * This is not for end-users, and will throw an AssertionError in case of conflict.
    *
    * @param varname the name of the variable to be bound
    * @param value the value to bind to the variable
-   * @return this Environment, in fluid style
+   * @return this StarlarkThread, in fluid style
    */
-  public Environment setup(String varname, Object value) {
+  public StarlarkThread setup(String varname, Object value) {
     if (lookup(varname) != null) {
       throw new AssertionError(String.format("variable '%s' already bound", varname));
     }
@@ -1098,9 +1098,9 @@
    *
    * @param varname the name of the variable to be bound
    * @param value the value to bind to the variable
-   * @return this Environment, in fluid style
+   * @return this StarlarkThread, in fluid style
    */
-  public Environment setupOverride(String varname, Object value) {
+  public StarlarkThread setupOverride(String varname, Object value) {
     try {
       return update(varname, value);
     } catch (EvalException ee) {
@@ -1167,8 +1167,8 @@
   }
 
   /**
-   * Returns a set of all names of variables that are accessible in this {@code Environment}, in a
-   * deterministic order.
+   * Returns a set of all names of variables that are accessible in this {@code StarlarkThread}, in
+   * a deterministic order.
    */
   // TODO(adonovan): eliminate sole external call from docgen.
   public Set<String> getVariableNames() {
@@ -1190,7 +1190,7 @@
     }
   }
 
-  /** Evaluates a Skylark statement in this environment. (Debugger API) */
+  /** Evaluates a Skylark statement in this thread. (Debugger API) */
   // TODO(adonovan): push this up into the debugger once the eval API is finalized.
   public Object debugEval(ParserInput input) throws EvalException, InterruptedException {
     EvalEventHandler handler = new EvalEventHandler();
@@ -1202,7 +1202,7 @@
     return Eval.eval(this, expr);
   }
 
-  /** Executes a Skylark file (sequence of statements) in this environment. (Debugger API) */
+  /** Executes a Skylark file (sequence of statements) in this thread. (Debugger API) */
   // TODO(adonovan): push this up into the debugger once the exec API is finalized.
   public void debugExec(ParserInput input) throws EvalException, InterruptedException {
     EvalEventHandler handler = new EvalEventHandler();
@@ -1278,18 +1278,18 @@
         return null;
       case INTO:
         // pause at the very next statement
-        return env -> true;
+        return thread -> true;
       case OVER:
-        return env -> isAt(env, pausedContinuation) || isOutside(env, pausedContinuation);
+        return thread -> isAt(thread, pausedContinuation) || isOutside(thread, pausedContinuation);
       case OUT:
         // if we're at the outer-most frame, same as NONE
-        return pausedContinuation == null ? null : env -> isOutside(env, pausedContinuation);
+        return pausedContinuation == null ? null : thread -> isOutside(thread, pausedContinuation);
     }
     throw new IllegalArgumentException("Unsupported stepping type: " + stepping);
   }
 
   /** See stepControl (Debugger API) */
-  public interface ReadyToPause extends Predicate<Environment> {}
+  public interface ReadyToPause extends Predicate<StarlarkThread> {}
 
   /**
    * Describes the stepping behavior that should occur when execution of a thread is continued.
@@ -1316,14 +1316,15 @@
     OUT,
   }
 
-  /** Returns true if {@code env} is in a parent frame of {@code pausedContinuation}. */
-  private static boolean isOutside(Environment env, @Nullable Continuation pausedContinuation) {
-    return pausedContinuation != null && env.continuation == pausedContinuation.continuation;
+  /** Returns true if {@code thread} is in a parent frame of {@code pausedContinuation}. */
+  private static boolean isOutside(
+      StarlarkThread thread, @Nullable Continuation pausedContinuation) {
+    return pausedContinuation != null && thread.continuation == pausedContinuation.continuation;
   }
 
-  /** Returns true if {@code env} is at the same frame as {@code pausedContinuation. */
-  private static boolean isAt(Environment env, @Nullable Continuation pausedContinuation) {
-    return env.continuation == pausedContinuation;
+  /** Returns true if {@code thread} is at the same frame as {@code pausedContinuation. */
+  private static boolean isAt(StarlarkThread thread, @Nullable Continuation pausedContinuation) {
+    return thread.continuation == pausedContinuation;
   }
 
   @Override
@@ -1338,7 +1339,7 @@
 
   @Override
   public String toString() {
-    return String.format("<Environment%s>", mutability());
+    return String.format("<StarlarkThread%s>", mutability());
   }
 
   /**
@@ -1406,31 +1407,32 @@
   }
 
   /**
-   * Returns a hash code calculated from the hash code of this Environment and the transitive
-   * closure of other Environments it loads.
+   * Returns a hash code calculated from the hash code of this StarlarkThread and the transitive
+   * closure of other StarlarkThreads it loads.
    */
   public String getTransitiveContentHashCode() {
     return transitiveHashCode;
   }
 
-  /** A read-only {@link Environment.GlobalFrame} with False/True/None constants only. */
+  /** A read-only {@link StarlarkThread.GlobalFrame} with False/True/None constants only. */
   @AutoCodec static final GlobalFrame CONSTANTS_ONLY = createConstantsGlobals();
 
   /**
-   * A read-only {@link Environment.GlobalFrame} with initial globals as defined in MethodLibrary.
+   * A read-only {@link StarlarkThread.GlobalFrame} with initial globals as defined in
+   * MethodLibrary.
    */
   @AutoCodec public static final GlobalFrame DEFAULT_GLOBALS = createDefaultGlobals();
 
   /** To be removed when all call-sites are updated. */
   public static final GlobalFrame SKYLARK = DEFAULT_GLOBALS;
 
-  private static Environment.GlobalFrame createConstantsGlobals() {
+  private static StarlarkThread.GlobalFrame createConstantsGlobals() {
     ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
     Runtime.addConstantsToBuilder(builder);
     return GlobalFrame.createForBuiltins(builder.build());
   }
 
-  private static Environment.GlobalFrame createDefaultGlobals() {
+  private static StarlarkThread.GlobalFrame createDefaultGlobals() {
     ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
     Runtime.addConstantsToBuilder(builder);
     MethodLibrary.addBindingsToBuilder(builder);
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/StringModule.java b/src/main/java/com/google/devtools/build/lib/syntax/StringModule.java
index 3de26fa..ef57182 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/StringModule.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/StringModule.java
@@ -110,11 +110,11 @@
             doc = "The objects to join.")
       },
       useLocation = true,
-      useEnvironment = true)
-  public String join(String self, Object elements, Location loc, Environment env)
+      useStarlarkThread = true)
+  public String join(String self, Object elements, Location loc, StarlarkThread thread)
       throws EvalException {
-    Collection<?> items = EvalUtils.toCollection(elements, loc, env);
-    if (env.getSemantics().incompatibleStringJoinRequiresStrings()) {
+    Collection<?> items = EvalUtils.toCollection(elements, loc, thread);
+    if (thread.getSemantics().incompatibleStringJoinRequiresStrings()) {
       for (Object item : items) {
         if (!(item instanceof String)) {
           throw new EvalException(
@@ -337,10 +337,10 @@
             defaultValue = "None",
             doc = "The maximum number of splits.")
       },
-      useEnvironment = true,
+      useStarlarkThread = true,
       useLocation = true)
   public MutableList<String> split(
-      String self, String sep, Object maxSplitO, Location loc, Environment env)
+      String self, String sep, Object maxSplitO, Location loc, StarlarkThread thread)
       throws EvalException {
     if (sep.isEmpty()) {
       throw new EvalException(loc, "Empty separator");
@@ -360,7 +360,7 @@
       res.add(self.substring(start, end));
       start = end + sep.length();
     }
-    return MutableList.wrapUnsafe(env, res);
+    return MutableList.wrapUnsafe(thread, res);
   }
 
   @SkylarkCallable(
@@ -386,10 +386,10 @@
             defaultValue = "None",
             doc = "The maximum number of splits.")
       },
-      useEnvironment = true,
+      useStarlarkThread = true,
       useLocation = true)
   public MutableList<String> rsplit(
-      String self, String sep, Object maxSplitO, Location loc, Environment env)
+      String self, String sep, Object maxSplitO, Location loc, StarlarkThread thread)
       throws EvalException {
     if (sep.isEmpty()) {
       throw new EvalException(loc, "Empty separator");
@@ -410,7 +410,7 @@
       end = start;
     }
     Collections.reverse(res);
-    return MutableList.wrapUnsafe(env, res);
+    return MutableList.wrapUnsafe(thread, res);
   }
 
   @SkylarkCallable(
@@ -429,9 +429,9 @@
             defaultValue = "unbound",
             doc = "The string to split on.")
       },
-      useEnvironment = true,
+      useStarlarkThread = true,
       useLocation = true)
-  public Tuple<String> partition(String self, Object sep, Location loc, Environment env)
+  public Tuple<String> partition(String self, Object sep, Location loc, StarlarkThread thread)
       throws EvalException {
     if (sep == Runtime.UNBOUND) {
         throw new EvalException(
@@ -464,9 +464,9 @@
             defaultValue = "unbound",
             doc = "The string to split on.")
       },
-      useEnvironment = true,
+      useStarlarkThread = true,
       useLocation = true)
-  public Tuple<String> rpartition(String self, Object sep, Location loc, Environment env)
+  public Tuple<String> rpartition(String self, Object sep, Location loc, StarlarkThread thread)
       throws EvalException {
     if (sep == Runtime.UNBOUND) {
         throw new EvalException(
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/ValidationEnvironment.java b/src/main/java/com/google/devtools/build/lib/syntax/ValidationEnvironment.java
index 47e60f7..d3d90b5 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/ValidationEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/ValidationEnvironment.java
@@ -82,19 +82,19 @@
     }
   }
 
-  private final Environment env;
+  private final StarlarkThread thread;
   private Block block;
   private int loopCount;
   /** In BUILD files, we have a slightly different behavior for legacy reasons. */
   private final boolean isBuildFile;
 
-  /** Create a ValidationEnvironment for a given global Environment (containing builtins). */
-  private ValidationEnvironment(Environment env, boolean isBuildFile) {
-    Preconditions.checkArgument(env.isGlobal());
-    this.env = env;
+  /** Create a ValidationEnvironment for a given global StarlarkThread (containing builtins). */
+  private ValidationEnvironment(StarlarkThread thread, boolean isBuildFile) {
+    Preconditions.checkArgument(thread.isGlobal());
+    this.thread = thread;
     this.isBuildFile = isBuildFile;
     block = new Block(Scope.Universe, null);
-    Set<String> builtinVariables = env.getVariableNames();
+    Set<String> builtinVariables = thread.getVariableNames();
     block.variables.addAll(builtinVariables);
   }
 
@@ -174,11 +174,11 @@
     if (b == null) {
       // The identifier might not exist because it was restricted (hidden) by the current semantics.
       // If this is the case, output a more helpful error message than 'not found'.
-      FlagGuardedValue result = env.getRestrictedBindings().get(node.getName());
+      FlagGuardedValue result = thread.getRestrictedBindings().get(node.getName());
       if (result != null) {
         throw new ValidationException(
             result.getEvalExceptionFromAttemptingAccess(
-                node.getLocation(), env.getSemantics(), node.getName()));
+                node.getLocation(), thread.getSemantics(), node.getName()));
       }
       throw new ValidationException(Eval.createInvalidIdentifierException(node, getAllSymbols()));
     }
@@ -379,7 +379,7 @@
 
   private void validateToplevelStatements(List<Statement> statements) {
     // Check that load() statements are on top.
-    if (!isBuildFile && env.getSemantics().incompatibleBzlDisallowLoadAfterStatement()) {
+    if (!isBuildFile && thread.getSemantics().incompatibleBzlDisallowLoadAfterStatement()) {
       checkLoadAfterStatement(statements);
     }
 
@@ -396,10 +396,10 @@
 
   // Public entry point, throwing variant.
   // TODO(adonovan): combine with variant below.
-  public static void validateFile(BuildFileAST file, Environment env, boolean isBuildFile)
+  public static void validateFile(BuildFileAST file, StarlarkThread thread, boolean isBuildFile)
       throws EvalException {
     try {
-      ValidationEnvironment venv = new ValidationEnvironment(env, isBuildFile);
+      ValidationEnvironment venv = new ValidationEnvironment(thread, isBuildFile);
       venv.validateToplevelStatements(file.getStatements());
       // Check that no closeBlock was forgotten.
       Preconditions.checkState(venv.block.parent == null);
@@ -410,9 +410,9 @@
 
   // Public entry point, error handling variant.
   public static boolean validateFile(
-      BuildFileAST file, Environment env, boolean isBuildFile, EventHandler eventHandler) {
+      BuildFileAST file, StarlarkThread thread, boolean isBuildFile, EventHandler eventHandler) {
     try {
-      validateFile(file, env, isBuildFile);
+      validateFile(file, thread, isBuildFile);
       return true;
     } catch (EvalException e) {
       if (!e.isDueToIncompleteAST()) {