Fix theoretical bug pointed out by jhorvitz@, that GroupedList assumes its elements are either raw or Lists, but #appendGroup(Collection) might add a Set or other Collection, which we wouldn't handle correctly.

The original motivation was to stress that the deps in a group were unordered with respect to each other, but that's not a good enough reason.

PiperOrigin-RevId: 233121648
diff --git a/src/main/java/com/google/devtools/build/lib/util/GroupedList.java b/src/main/java/com/google/devtools/build/lib/util/GroupedList.java
index 0054a3f..614bea8 100644
--- a/src/main/java/com/google/devtools/build/lib/util/GroupedList.java
+++ b/src/main/java/com/google/devtools/build/lib/util/GroupedList.java
@@ -22,7 +22,6 @@
 import com.google.devtools.build.lib.collect.compacthashset.CompactHashSet;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadHostile;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
@@ -36,10 +35,10 @@
  * <p>Despite the "list" name, it is an error for the same element to appear multiple times in the
  * list. Users are responsible for not trying to add the same element to a GroupedList twice.
  *
- * <p>Groups are implemented as lists to minimize memory use. However, {@link #equals} is defined
- * to treat groups as unordered.
+ * <p>Groups are implemented as lists to minimize memory use. However, {@link #equals} is defined to
+ * treat groups as unordered.
  */
-public class GroupedList<T> implements Iterable<Collection<T>> {
+public class GroupedList<T> implements Iterable<List<T>> {
   // Total number of items in the list. At least elements.size(), but might be larger if there are
   // any nested lists.
   private int size = 0;
@@ -110,11 +109,9 @@
 
   // Use with caution as there are no checks in place for the integrity of the resulting object
   // (no de-duping).
-  public void appendGroup(Collection<? extends T> group) {
+  public void appendGroup(List<? extends T> group) {
     // Do a check to make sure we don't have lists here. Note that if group is empty,
     // Iterables.getFirst will return null, and null is not instanceof List.
-    Preconditions.checkState(!(Iterables.getFirst(group, null) instanceof List),
-        "Cannot make grouped list of lists: %s", group);
     switch (group.size()) {
       case 0:
         return;
@@ -125,6 +122,8 @@
         elements.add(group);
         break;
     }
+    Preconditions.checkState(
+        !(group.get(0) instanceof List), "Cannot make grouped list of lists: %s", group);
     size += group.size();
   }
 
@@ -393,7 +392,7 @@
    * iterator is needed here because, to optimize memory, we store single-element lists as elements
    * internally, and so they must be wrapped before they're returned.
    */
-  private class GroupedIterator implements Iterator<Collection<T>> {
+  private class GroupedIterator implements Iterator<List<T>> {
     private final Iterator<Object> iter = elements.iterator();
 
     @Override
@@ -403,7 +402,7 @@
 
     @SuppressWarnings("unchecked") // Cast of Object to List<T> or T.
     @Override
-    public Collection<T> next() {
+    public List<T> next() {
       Object obj = iter.next();
       if (obj instanceof List) {
         return (List<T>) obj;
@@ -413,7 +412,7 @@
   }
 
   @Override
-  public Iterator<Collection<T>> iterator() {
+  public Iterator<List<T>> iterator() {
     return new GroupedIterator();
   }
 
@@ -474,7 +473,7 @@
      * goes in a group of its own.
      */
     public void add(E elt) {
-      elements.add(Preconditions.checkNotNull(elt, "%s %s", elt, this));
+      elements.add(Preconditions.checkNotNull(elt, "Null add of elt: %s", this));
       if (currentGroup == null) {
         groupedList.add(elt);
       } else {
@@ -502,16 +501,6 @@
       currentGroup = new ArrayList<>();
     }
 
-    /**
-     * Starts a group with an initial capacity. All elements added until {@link #endGroup} will be
-     * in the same group. Each call of startGroup must be paired with a following {@link #endGroup}
-     * call. Any duplicate elements added to this group will be silently deduplicated.
-     */
-    public void startGroup(int expectedGroupSize) {
-      Preconditions.checkState(currentGroup == null, this);
-      currentGroup = new ArrayList<>(expectedGroupSize);
-    }
-
     /** Ends a group started with {@link #startGroup}. */
     public void endGroup() {
       Preconditions.checkNotNull(currentGroup);
diff --git a/src/main/java/com/google/devtools/build/skyframe/AbstractParallelEvaluator.java b/src/main/java/com/google/devtools/build/skyframe/AbstractParallelEvaluator.java
index 85aa3e8..84e5ff7 100644
--- a/src/main/java/com/google/devtools/build/skyframe/AbstractParallelEvaluator.java
+++ b/src/main/java/com/google/devtools/build/skyframe/AbstractParallelEvaluator.java
@@ -44,6 +44,7 @@
 import java.time.Duration;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
@@ -219,7 +220,7 @@
         // its reverse dep on this node removed. Failing to do either one of these would result in
         // a graph inconsistency, where the child had a reverse dep on this node, but this node
         // had no kind of dependency on the child.
-        Collection<SkyKey> directDepsToCheck = state.getNextDirtyDirectDeps();
+        List<SkyKey> directDepsToCheck = state.getNextDirtyDirectDeps();
 
         if (invalidatedByErrorTransience(directDepsToCheck, state)) {
           // If this dep is the ErrorTransienceValue and the ErrorTransienceValue has been
diff --git a/src/main/java/com/google/devtools/build/skyframe/DelegatingNodeEntry.java b/src/main/java/com/google/devtools/build/skyframe/DelegatingNodeEntry.java
index c9d202e..823c127 100644
--- a/src/main/java/com/google/devtools/build/skyframe/DelegatingNodeEntry.java
+++ b/src/main/java/com/google/devtools/build/skyframe/DelegatingNodeEntry.java
@@ -16,7 +16,7 @@
 import com.google.devtools.build.lib.util.GroupedList;
 import com.google.devtools.build.lib.util.GroupedList.GroupedListHelper;
 import java.math.BigInteger;
-import java.util.Collection;
+import java.util.List;
 import java.util.Set;
 import javax.annotation.Nullable;
 
@@ -99,7 +99,7 @@
   }
 
   @Override
-  public Collection<SkyKey> getNextDirtyDirectDeps() throws InterruptedException {
+  public List<SkyKey> getNextDirtyDirectDeps() throws InterruptedException {
     return getDelegate().getNextDirtyDirectDeps();
   }
 
@@ -213,7 +213,7 @@
   }
 
   @Override
-  public void addTemporaryDirectDepsGroupToDirtyEntry(Collection<SkyKey> group) {
+  public void addTemporaryDirectDepsGroupToDirtyEntry(List<SkyKey> group) {
     getDelegate().addTemporaryDirectDepsGroupToDirtyEntry(group);
   }
 }
diff --git a/src/main/java/com/google/devtools/build/skyframe/DirtyBuildingState.java b/src/main/java/com/google/devtools/build/skyframe/DirtyBuildingState.java
index 7d16bb2..b110ab0 100644
--- a/src/main/java/com/google/devtools/build/skyframe/DirtyBuildingState.java
+++ b/src/main/java/com/google/devtools/build/skyframe/DirtyBuildingState.java
@@ -19,7 +19,7 @@
 import com.google.devtools.build.lib.util.GroupedList;
 import com.google.devtools.build.skyframe.NodeEntry.DirtyState;
 import com.google.devtools.build.skyframe.ThinNodeEntry.DirtyType;
-import java.util.Collection;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -194,7 +194,7 @@
    *
    * <p>See {@link NodeEntry#getNextDirtyDirectDeps}.
    */
-  final Collection<SkyKey> getNextDirtyDirectDeps() throws InterruptedException {
+  final List<SkyKey> getNextDirtyDirectDeps() throws InterruptedException {
     Preconditions.checkState(dirtyState == DirtyState.CHECK_DEPENDENCIES, this);
     Preconditions.checkState(dirtyDirectDepIndex < getNumOfGroupsInLastBuildDirectDeps(), this);
     return getLastBuildDirectDeps().get(dirtyDirectDepIndex++);
diff --git a/src/main/java/com/google/devtools/build/skyframe/InMemoryNodeEntry.java b/src/main/java/com/google/devtools/build/skyframe/InMemoryNodeEntry.java
index e350a3d..f72d069 100644
--- a/src/main/java/com/google/devtools/build/skyframe/InMemoryNodeEntry.java
+++ b/src/main/java/com/google/devtools/build/skyframe/InMemoryNodeEntry.java
@@ -23,7 +23,6 @@
 import com.google.devtools.build.skyframe.KeyToConsolidate.OpToStoreBare;
 import java.math.BigInteger;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 import javax.annotation.Nullable;
@@ -589,7 +588,7 @@
 
   /** @see DirtyBuildingState#getNextDirtyDirectDeps() */
   @Override
-  public synchronized Collection<SkyKey> getNextDirtyDirectDeps() throws InterruptedException {
+  public synchronized List<SkyKey> getNextDirtyDirectDeps() throws InterruptedException {
     Preconditions.checkState(isReady(), this);
     Preconditions.checkState(isEvaluating(), "Not evaluating during getNextDirty? %s", this);
     return getDirtyBuildingState().getNextDirtyDirectDeps();
@@ -708,7 +707,7 @@
   }
 
   @Override
-  public synchronized void addTemporaryDirectDepsGroupToDirtyEntry(Collection<SkyKey> group) {
+  public synchronized void addTemporaryDirectDepsGroupToDirtyEntry(List<SkyKey> group) {
     Preconditions.checkState(!isDone(), "add group temp shouldn't be done: %s %s", group, this);
     getTemporaryDirectDeps().appendGroup(group);
   }
diff --git a/src/main/java/com/google/devtools/build/skyframe/NodeEntry.java b/src/main/java/com/google/devtools/build/skyframe/NodeEntry.java
index 78fb6ca..61bbc24 100644
--- a/src/main/java/com/google/devtools/build/skyframe/NodeEntry.java
+++ b/src/main/java/com/google/devtools/build/skyframe/NodeEntry.java
@@ -18,6 +18,7 @@
 import com.google.devtools.build.lib.util.GroupedList.GroupedListHelper;
 import java.math.BigInteger;
 import java.util.Collection;
+import java.util.List;
 import java.util.Set;
 import javax.annotation.Nullable;
 
@@ -299,7 +300,7 @@
    * @see DirtyBuildingState#getNextDirtyDirectDeps()
    */
   @ThreadSafe
-  Collection<SkyKey> getNextDirtyDirectDeps() throws InterruptedException;
+  List<SkyKey> getNextDirtyDirectDeps() throws InterruptedException;
 
   /**
    * Returns all deps of a node that has not yet finished evaluating. In other words, if a node has
@@ -425,7 +426,7 @@
    * checking.
    */
   @ThreadSafe
-  void addTemporaryDirectDepsGroupToDirtyEntry(Collection<SkyKey> group);
+  void addTemporaryDirectDepsGroupToDirtyEntry(List<SkyKey> group);
 
   /**
    * Returns true if the node is ready to be evaluated, i.e., it has been signaled exactly as many