Allows subclasses of InMemoryNodeEntry to determine whether a child has actually changed.

PiperOrigin-RevId: 215598031
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 25b867f..d0f0703 100644
--- a/src/main/java/com/google/devtools/build/skyframe/InMemoryNodeEntry.java
+++ b/src/main/java/com/google/devtools/build/skyframe/InMemoryNodeEntry.java
@@ -311,16 +311,21 @@
           isDirty() && getDirtyBuildingState().getDirtyState() == DirtyState.FORCED_REBUILDING;
       // If this is a new value, or it has changed since the last build, set the version to the
       // current graph version.
-      Preconditions.checkState(
-          forcedRebuild || !this.lastChangedVersion.equals(version),
-          "Changed value but with the same version? %s %s %s",
-          this.lastChangedVersion,
-          version,
-          this);
+      if (!forcedRebuild && this.lastChangedVersion.equals(version)) {
+        logError(
+            new IllegalStateException(
+                "Changed value but with the same version? "
+                    + this.lastChangedVersion
+                    + " "
+                    + version
+                    + " "
+                    + this));
+      }
+      // If this is a new value, or it has changed since the last build, set the version to the
+      // current graph version.
       this.lastChangedVersion = version;
       this.value = value;
     }
-
     return setStateFinishedAndReturnReverseDepsToSignal();
   }
 
@@ -449,7 +454,8 @@
     Preconditions.checkState(isEvaluating(), this);
     signaledDeps++;
     if (isDirty()) {
-      dirtyBuildingState.signalDepInternal(!childVersion.atMost(lastEvaluatedVersion), isReady());
+      dirtyBuildingState.signalDepInternal(
+          hasChildChanged(lastEvaluatedVersion, childVersion), isReady());
     }
     return isReady();
   }
@@ -640,6 +646,20 @@
     return isReady(getNumTemporaryDirectDeps());
   }
 
+  /** True if the child has changed since the last evaluated version. */
+  protected boolean hasChildChanged(Version lastEvaluatedVersion, Version childVersion) {
+    // childVersion > lastEvaluatedVersion
+    return !childVersion.atMost(lastEvaluatedVersion);
+  }
+
+  protected int getSignaledDeps() {
+    return signaledDeps;
+  }
+
+  protected void logError(RuntimeException error) {
+    throw error;
+  }
+
   /** Returns whether all known children of this node have signaled that they are done. */
   private boolean isReady(int numDirectDeps) {
     Preconditions.checkState(signaledDeps <= numDirectDeps, "%s %s", numDirectDeps, this);