Convert ToolchainCollection to use AutoValue.

Also Break the subclass relationship between ExecGroupCollection and ToolchainCollection.

This adds toString, hashCode, and equals implementations for both classes.

PiperOrigin-RevId: 314338648
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BUILD b/src/main/java/com/google/devtools/build/lib/analysis/BUILD
index de3bbfe..026d10d 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/analysis/BUILD
@@ -1024,6 +1024,7 @@
     deps = [
         ":toolchain_context",
         "//src/main/java/com/google/devtools/build/lib/cmdline",
+        "//third_party:auto_value",
         "//third_party:guava",
     ],
 )
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ExecGroupCollection.java b/src/main/java/com/google/devtools/build/lib/analysis/ExecGroupCollection.java
index a7f8545..db8af35 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ExecGroupCollection.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ExecGroupCollection.java
@@ -13,6 +13,9 @@
 // limitations under the License.
 package com.google.devtools.build.lib.analysis;
 
+import static com.google.devtools.build.lib.analysis.ToolchainCollection.DEFAULT_EXEC_GROUP_NAME;
+
+import com.google.auto.value.AutoValue;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.skylarkbuildapi.platform.ExecGroupCollectionApi;
@@ -29,16 +32,20 @@
  * A {@link StarlarkIndexable} collection of resolved toolchain contexts that can be exposed to
  * starlark.
  */
-public class ExecGroupCollection extends ToolchainCollection<ResolvedToolchainContext>
-    implements ExecGroupCollectionApi {
+@AutoValue
+public abstract class ExecGroupCollection implements ExecGroupCollectionApi {
 
-  public ExecGroupCollection(ToolchainCollection<ResolvedToolchainContext> toolchainCollection) {
-    super(toolchainCollection);
+  /** Returns a new {@link ExecGroupCollection} backed by the given {@code toolchainCollection}. */
+  public static ExecGroupCollection create(
+      ToolchainCollection<ResolvedToolchainContext> toolchainCollection) {
+    return new AutoValue_ExecGroupCollection(toolchainCollection);
   }
 
+  protected abstract ToolchainCollection<ResolvedToolchainContext> toolchainCollection();
+
   @VisibleForTesting
   public ImmutableMap<String, ResolvedToolchainContext> getToolchainCollectionForTesting() {
-    return getContextMap();
+    return toolchainCollection().getContextMap();
   }
 
   public static boolean isValidGroupName(String execGroupName) {
@@ -48,7 +55,8 @@
   @Override
   public boolean containsKey(StarlarkSemantics semantics, Object key) throws EvalException {
     String group = castGroupName(key);
-    return !DEFAULT_EXEC_GROUP_NAME.equals(group) && getExecGroups().contains(group);
+    return !DEFAULT_EXEC_GROUP_NAME.equals(group)
+        && toolchainCollection().getExecGroups().contains(group);
   }
 
   /**
@@ -62,11 +70,11 @@
     if (!containsKey(semantics, key)) {
       throw Starlark.errorf(
           "In %s, unrecognized exec group '%s' requested. Available exec groups: [%s]",
-          getDefaultToolchainContext().targetDescription(),
+          toolchainCollection().getDefaultToolchainContext().targetDescription(),
           execGroup,
           String.join(", ", getScrubbedExecGroups()));
     }
-    return new ExecGroupContext(getToolchainContext(execGroup));
+    return new ExecGroupContext(toolchainCollection().getToolchainContext(execGroup));
   }
 
   private static String castGroupName(Object key) throws EvalException {
@@ -87,7 +95,7 @@
   }
 
   private List<String> getScrubbedExecGroups() {
-    return getExecGroups().stream()
+    return toolchainCollection().getExecGroups().stream()
         .filter(group -> !DEFAULT_EXEC_GROUP_NAME.equals(group))
         .sorted()
         .collect(Collectors.toList());
@@ -105,7 +113,7 @@
     }
 
     @Override
-    public ResolvedToolchainContext toolchains() throws EvalException {
+    public ResolvedToolchainContext toolchains() {
       return resolvedToolchainContext;
     }
 
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
index 9d96691..79e1f75 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
@@ -1856,7 +1856,7 @@
           this.toolchainContexts == null,
           "toolchainContexts has already been set for this Builder");
       this.toolchainContexts =
-          new ToolchainCollection.Builder<ResolvedToolchainContext>()
+          ToolchainCollection.<ResolvedToolchainContext>builder()
               .addDefaultContext(toolchainContext)
               .build();
       return this;
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ToolchainCollection.java b/src/main/java/com/google/devtools/build/lib/analysis/ToolchainCollection.java
index 206a8d6..4991889 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ToolchainCollection.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ToolchainCollection.java
@@ -15,6 +15,7 @@
 
 import static com.google.common.collect.ImmutableSet.toImmutableSet;
 
+import com.google.auto.value.AutoValue;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;
@@ -29,30 +30,56 @@
  * @param <T> any class that extends ToolchainContext. This generic allows ToolchainCollection to be
  *     used, e.g., both before and after toolchain resolution.
  */
-public class ToolchainCollection<T extends ToolchainContext> {
+@AutoValue
+public abstract class ToolchainCollection<T extends ToolchainContext> {
 
   // This is intentionally a string that would fail {@code Identifier.isValid} so that
   // users can't create a group with the same name.
   @VisibleForTesting public static final String DEFAULT_EXEC_GROUP_NAME = "default-exec-group";
 
   /** A map of execution group names to toolchain contexts. */
-  private final ImmutableMap<String, T> toolchainContexts;
+  public abstract ImmutableMap<String, T> getContextMap();
 
-  private ToolchainCollection(Map<String, T> contexts) {
-    Preconditions.checkArgument(contexts.containsKey(DEFAULT_EXEC_GROUP_NAME));
-    toolchainContexts = ImmutableMap.copyOf(contexts);
+  T getDefaultToolchainContext() {
+    return getContextMap().get(DEFAULT_EXEC_GROUP_NAME);
   }
 
-  ToolchainCollection(ToolchainCollection<T> toCopy) {
-    toolchainContexts = ImmutableMap.copyOf(toCopy.getContextMap());
+  boolean hasToolchainContext(String execGroup) {
+    return getContextMap().containsKey(execGroup);
+  }
+
+  public T getToolchainContext(String execGroup) {
+    return getContextMap().get(execGroup);
+  }
+
+  public ImmutableSet<Label> getResolvedToolchains() {
+    return getContextMap().values().stream()
+        .flatMap(c -> c.resolvedToolchainLabels().stream())
+        .collect(toImmutableSet());
+  }
+
+  ImmutableSet<String> getExecGroups() {
+    return getContextMap().keySet();
+  }
+
+  @SuppressWarnings("unchecked")
+  public ToolchainCollection<ToolchainContext> asToolchainContexts() {
+    return (ToolchainCollection<ToolchainContext>) this;
+  }
+
+  /** Returns a new builder for {@link ToolchainCollection} instances. */
+  public static <T extends ToolchainContext> Builder<T> builder() {
+    return new Builder<T>();
   }
 
   /** Builder for ToolchainCollection. */
-  public static class Builder<T extends ToolchainContext> {
+  public static final class Builder<T extends ToolchainContext> {
+    // This is not immutable so that we can check for duplicate keys easily.
     private final Map<String, T> toolchainContexts = new HashMap<>();
 
     public ToolchainCollection<T> build() {
-      return new ToolchainCollection<>(toolchainContexts);
+      Preconditions.checkArgument(toolchainContexts.containsKey(DEFAULT_EXEC_GROUP_NAME));
+      return new AutoValue_ToolchainCollection<T>(ImmutableMap.copyOf(toolchainContexts));
     }
 
     public void addContext(String execGroup, T context) {
@@ -68,34 +95,4 @@
       return this;
     }
   }
-
-  T getDefaultToolchainContext() {
-    return toolchainContexts.get(DEFAULT_EXEC_GROUP_NAME);
-  }
-
-  boolean hasToolchainContext(String execGroup) {
-    return toolchainContexts.containsKey(execGroup);
-  }
-
-  public T getToolchainContext(String execGroup) {
-    return toolchainContexts.get(execGroup);
-  }
-
-  public ImmutableSet<Label> getResolvedToolchains() {
-    return toolchainContexts.values().stream()
-        .flatMap(c -> c.resolvedToolchainLabels().stream())
-        .collect(toImmutableSet());
-  }
-
-  ImmutableSet<String> getExecGroups() {
-    return toolchainContexts.keySet();
-  }
-
-  public ToolchainCollection<ToolchainContext> asToolchainContexts() {
-    return new ToolchainCollection<>(ImmutableMap.copyOf(toolchainContexts));
-  }
-
-  public ImmutableMap<String, T> getContextMap() {
-    return toolchainContexts;
-  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/StarlarkRuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/StarlarkRuleContext.java
index 25eae40..5910713 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/StarlarkRuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/StarlarkRuleContext.java
@@ -709,7 +709,8 @@
 
   @Override
   public ExecGroupCollection execGroups() {
-    return new ExecGroupCollection(ruleContext.getToolchainContexts());
+    // Create a thin wrapper around the toolchain collection, to expose the Starlark API.
+    return ExecGroupCollection.create(ruleContext.getToolchainContexts());
   }
 
   @Override
diff --git a/src/main/java/com/google/devtools/build/lib/query2/cquery/ConfiguredTargetAccessor.java b/src/main/java/com/google/devtools/build/lib/query2/cquery/ConfiguredTargetAccessor.java
index e8a91d3..c8fe5d0 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/cquery/ConfiguredTargetAccessor.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/cquery/ConfiguredTargetAccessor.java
@@ -223,7 +223,7 @@
     ImmutableMap<String, ExecGroup> execGroups = rule.getRuleClassObject().getExecGroups();
 
     ToolchainCollection.Builder<UnloadedToolchainContext> toolchainContexts =
-        new ToolchainCollection.Builder<>();
+        ToolchainCollection.builder();
     try {
       for (Map.Entry<String, ExecGroup> group : execGroups.entrySet()) {
         ExecGroup execGroup = group.getValue();
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
index 5601f39..627d206 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
@@ -421,7 +421,7 @@
                 configConditions,
                 unloadedToolchainContext == null
                     ? null
-                    : new ToolchainCollection.Builder<>()
+                    : ToolchainCollection.builder()
                         .addDefaultContext(unloadedToolchainContext)
                         .build(),
                 ruleClassProvider,
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
index ef001ad..685c86f 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
@@ -325,7 +325,7 @@
       if (unloadedToolchainContexts != null) {
         String targetDescription = target.toString();
         ToolchainCollection.Builder<ResolvedToolchainContext> contextsBuilder =
-            new ToolchainCollection.Builder<>();
+            ToolchainCollection.builder();
         for (Map.Entry<String, UnloadedToolchainContext> unloadedContext :
             unloadedToolchainContexts.getContextMap().entrySet()) {
           contextsBuilder.addContext(
@@ -514,7 +514,7 @@
     boolean valuesMissing = env.valuesMissing();
 
     ToolchainCollection.Builder<UnloadedToolchainContext> toolchainContexts =
-        valuesMissing ? null : new ToolchainCollection.Builder<>();
+        valuesMissing ? null : ToolchainCollection.builder();
     for (Map.Entry<String, ToolchainContextKey> unloadedToolchainContextKey :
         toolchainContextKeys.entrySet()) {
       UnloadedToolchainContext unloadedToolchainContext =