When falling back from a reduced classpath Java compilation, delete outputs and
generated digests before executing the fallback.

RELNOTES: None.
PiperOrigin-RevId: 235001623
diff --git a/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataHandler.java b/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataHandler.java
index fc61397..c48d5bc 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataHandler.java
@@ -82,4 +82,10 @@
    * <p>Must be called at most once on any specific instance.
    */
   void discardOutputMetadata();
+
+  /**
+   * Discards output artifact metadata and removes them from other data structures. Use this if an
+   * action can make multiple attempts that are expected to create the same set of output files.
+   */
+  void resetOutputs(Iterable<Artifact> outputs);
 }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java
index db44244..cdfc4a7 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java
@@ -315,7 +315,11 @@
             }
           }
 
-          // Fall back to running with the full classpath.
+          // Fall back to running with the full classpath. This requires first deleting potential
+          // artifacts generated by the reduced action and clearing the metadata caches.
+          deleteOutputs(
+              actionExecutionContext.getFileSystem(), actionExecutionContext.getExecRoot());
+          actionExecutionContext.getMetadataHandler().resetOutputs(getOutputs());
           List<SpawnResult> fallbackResults =
               spawnActionContext.exec(
                   getReducedSpawn(actionExecutionContext, reducedClasspath, /* fallback=*/ true),
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
index a2c2c06..67931c7 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
@@ -510,6 +510,16 @@
     store.clear();
   }
 
+  @Override
+  public void resetOutputs(Iterable<Artifact> outputs) {
+    Preconditions.checkState(
+        executionMode.get(), "resetOutputs() should only be called from within a running action.");
+    for (Artifact output : outputs) {
+      omittedOutputs.remove(output);
+      store.remove(output);
+    }
+  }
+
   OutputStore getOutputStore() {
     return store;
   }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/OutputStore.java b/src/main/java/com/google/devtools/build/lib/skyframe/OutputStore.java
index 7829819..60a0ef2 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/OutputStore.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/OutputStore.java
@@ -184,4 +184,13 @@
     treeArtifactContents.clear();
     injectedFiles.clear();
   }
+
+  /** Clears data about a specific Artifact from this store. */
+  final void remove(Artifact artifact) {
+    artifactData.remove(artifact);
+    treeArtifactData.remove(artifact);
+    additionalOutputData.remove(artifact);
+    treeArtifactContents.remove(artifact);
+    injectedFiles.remove(artifact);
+  }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/actions/util/ActionsTestUtil.java b/src/test/java/com/google/devtools/build/lib/actions/util/ActionsTestUtil.java
index fdfff3b..4f46062 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/util/ActionsTestUtil.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/util/ActionsTestUtil.java
@@ -754,5 +754,10 @@
     public void discardOutputMetadata() {
       throw new UnsupportedOperationException();
     }
+
+    @Override
+    public void resetOutputs(Iterable<Artifact> outputs) {
+      throw new UnsupportedOperationException();
+    }
   }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandlerTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandlerTest.java
index 89ac11c..eab3acf 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandlerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandlerTest.java
@@ -248,4 +248,32 @@
     } catch (IllegalStateException expected) {
     }
   }
+
+  @Test
+  public void resettingOutputs() throws Exception {
+    scratch.file("/output/bin/foo/bar", "not empty");
+    PathFragment path = PathFragment.create("foo/bar");
+    Artifact artifact = new Artifact(path, outputRoot);
+    ActionInputMap map = new ActionInputMap(1);
+    ActionMetadataHandler handler =
+        new ActionMetadataHandler(
+            map,
+            /* missingArtifactsAllowed= */ true,
+            /* outputs= */ ImmutableList.of(artifact),
+            /* tsgm= */ null,
+            ArtifactPathResolver.IDENTITY,
+            new MinimalOutputStore());
+    handler.discardOutputMetadata();
+
+    // The handler doesn't have any info. It'll stat the file and discover that it's 10 bytes long.
+    assertThat(handler.getMetadata(artifact).getSize()).isEqualTo(10);
+
+    // Inject a remote file of size 42.
+    handler.injectRemoteFile(artifact, new byte[] {1, 2, 3}, 42, 0);
+    assertThat(handler.getMetadata(artifact).getSize()).isEqualTo(42);
+
+    // Reset this output, which will make the handler stat the file again.
+    handler.resetOutputs(ImmutableList.of(artifact));
+    assertThat(handler.getMetadata(artifact).getSize()).isEqualTo(10);
+  }
 }