Permit rewinding with incrementality.

When rewinding prerequisites are not met, fail the build instead of ignoring the flag. Respecting the value of `--rewind_lost_inputs` instead of silently ignoring it allows for some cleanups to be made in followups.

PiperOrigin-RevId: 568724875
Change-Id: If7a1e8beec2b6753268258379521960c6d2d262c
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutor.java
index 308a97a..add7334 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutor.java
@@ -59,6 +59,8 @@
 import com.google.devtools.build.lib.profiler.Profiler;
 import com.google.devtools.build.lib.profiler.ProfilerTask;
 import com.google.devtools.build.lib.repository.ExternalPackageHelper;
+import com.google.devtools.build.lib.server.FailureDetails.ActionRewinding;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
 import com.google.devtools.build.lib.skyframe.AspectKeyCreator.AspectKey;
 import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.ExternalFileAction;
 import com.google.devtools.build.lib.skyframe.PackageFunction.ActionOnIOExceptionReadingBuildFile;
@@ -67,6 +69,7 @@
 import com.google.devtools.build.lib.skyframe.actiongraph.v2.AqueryConsumingOutputHandler;
 import com.google.devtools.build.lib.skyframe.rewinding.RewindableGraphInconsistencyReceiver;
 import com.google.devtools.build.lib.util.AbruptExitException;
+import com.google.devtools.build.lib.util.DetailedExitCode;
 import com.google.devtools.build.lib.util.ResourceUsage;
 import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
 import com.google.devtools.build.lib.vfs.BatchStat;
@@ -244,8 +247,10 @@
       QuiescingExecutors executors,
       OptionsProvider options)
       throws InterruptedException, AbruptExitException {
+    // TODO(b/228090759): Set the correct inconsistency receiver when evaluatorNeedsReset is false.
+    boolean rewindingEnabled = rewindingEnabled(options);
     if (evaluatorNeedsReset) {
-      if (rewindingPermitted(options)) {
+      if (rewindingEnabled) {
         // Currently incompatible with Skymeld i.e. this code path won't be run in Skymeld mode. We
         // may need to combine these GraphInconsistencyReceiver implementations in the future.
         var rewindableReceiver = new RewindableGraphInconsistencyReceiver();
@@ -295,15 +300,22 @@
     return workspaceInfo;
   }
 
-  private boolean rewindingPermitted(OptionsProvider options) {
-    // Rewinding is only supported with no incremental state and no action cache.
-    if (trackIncrementalState) {
+  private static boolean rewindingEnabled(OptionsProvider options) throws AbruptExitException {
+    var buildRequestOptions = options.getOptions(BuildRequestOptions.class);
+    if (buildRequestOptions == null || !buildRequestOptions.rewindLostInputs) {
       return false;
     }
-    BuildRequestOptions buildRequestOptions = options.getOptions(BuildRequestOptions.class);
-    return buildRequestOptions != null
-        && !buildRequestOptions.useActionCache
-        && buildRequestOptions.rewindLostInputs;
+    if (buildRequestOptions.useActionCache) {
+      throw new AbruptExitException(
+          DetailedExitCode.of(
+              FailureDetail.newBuilder()
+                  .setMessage("--rewind_lost_inputs requires --nouse_action_cache")
+                  .setActionRewinding(
+                      ActionRewinding.newBuilder()
+                          .setCode(ActionRewinding.Code.REWIND_LOST_INPUTS_PREREQ_UNMET))
+                  .build()));
+    }
+    return true;
   }
 
   /**
diff --git a/src/main/protobuf/failure_details.proto b/src/main/protobuf/failure_details.proto
index fb15475..3cc189c 100644
--- a/src/main/protobuf/failure_details.proto
+++ b/src/main/protobuf/failure_details.proto
@@ -1033,6 +1033,7 @@
     ACTION_REWINDING_UNKNOWN = 0 [(metadata) = { exit_code: 37 }];
     LOST_INPUT_TOO_MANY_TIMES = 1 [(metadata) = { exit_code: 1 }];
     LOST_INPUT_IS_SOURCE = 2 [(metadata) = { exit_code: 1 }];
+    REWIND_LOST_INPUTS_PREREQ_UNMET = 3 [(metadata) = { exit_code: 2 }];
   }
 
   Code code = 1;
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutorTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutorTest.java
index 4326b00..1d8a38c 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutorTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutorTest.java
@@ -105,6 +105,7 @@
 import com.google.devtools.build.lib.query2.common.QueryTransitivePackagePreloader;
 import com.google.devtools.build.lib.runtime.KeepGoingOption;
 import com.google.devtools.build.lib.runtime.QuiescingExecutorsImpl;
+import com.google.devtools.build.lib.server.FailureDetails.ActionRewinding;
 import com.google.devtools.build.lib.server.FailureDetails.Crash;
 import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
 import com.google.devtools.build.lib.server.FailureDetails.Spawn;
@@ -2662,24 +2663,11 @@
   }
 
   @Test
-  public void usesCorrectGraphInconsistencyReceiver(
-      @TestParameter boolean trackIncrementalState,
-      @TestParameter boolean useActionCache,
-      @TestParameter boolean rewindLostInputs)
+  public void rewindingPrerequisites(
+      @TestParameter boolean trackIncrementalState, @TestParameter boolean useActionCache)
       throws Exception {
-    extraSkyFunctions.put(
-        SkyFunctionName.FOR_TESTING,
-        (key, env) -> {
-          if (!trackIncrementalState && !useActionCache && rewindLostInputs) {
-            assertThat(env.resetPermitted()).isTrue();
-          } else {
-            assertThat(env.resetPermitted()).isFalse();
-          }
-          return new SkyValue() {};
-        });
     initializeSkyframeExecutor();
-    options.parse(
-        "--use_action_cache=" + useActionCache, "--rewind_lost_inputs=" + rewindLostInputs);
+    options.parse("--rewind_lost_inputs", "--use_action_cache=" + useActionCache);
 
     skyframeExecutor.setActive(false);
     skyframeExecutor.decideKeepIncrementalState(
@@ -2690,10 +2678,14 @@
         /* discardAnalysisCache= */ false,
         reporter);
     skyframeExecutor.setActive(true);
-    syncSkyframeExecutor();
 
-    EvaluationResult<?> result = evaluate(ImmutableList.of(GraphTester.skyKey("key")));
-    assertThat(result.hasError()).isFalse();
+    if (useActionCache) {
+      AbruptExitException e = assertThrows(AbruptExitException.class, this::syncSkyframeExecutor);
+      assertThat(e.getDetailedExitCode().getFailureDetail().getActionRewinding().getCode())
+          .isEqualTo(ActionRewinding.Code.REWIND_LOST_INPUTS_PREREQ_UNMET);
+    } else {
+      syncSkyframeExecutor(); // Permitted.
+    }
   }
 
   private void syncSkyframeExecutor() throws InterruptedException, AbruptExitException {