Introduce TreeFileArtifact, which represents files under TreeArtifacts.
Remove ArtifactFile, which is rendered obsolete by TreeFileArtifact.

--
MOS_MIGRATED_REVID=119789154
diff --git a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
index 4e8989f..f28683b 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
@@ -70,7 +70,7 @@
  * In the usual case, an Artifact represents a single file. However, an Artifact may
  * also represent the following:
  * <ul>
- * <li>A TreeArtifact, which is a directory containing a tree of unknown {@link ArtifactFile}s.
+ * <li>A TreeArtifact, which is a directory containing a tree of unknown {@link Artifact}s.
  * In the future, Actions will be able to examine these files as inputs and declare them as outputs
  * at execution time, but this is not yet implemented. This is used for Actions where
  * the inputs and/or outputs might not be discoverable except during Action execution.
@@ -87,8 +87,8 @@
  * by new rule implementations.
  * </ul>
  * <p/>
- * This class implements {@link ArtifactFile}, and is modeled as an Artifact "containing" itself
- * as an ArtifactFile.
+ * This class implements {@link Artifact}, and is modeled as an Artifact "containing" itself
+ * as an Artifact.
  * <p/>
  * <p>This class is "theoretically" final; it should not be subclassed except by
  * {@link SpecialArtifact}.
@@ -98,7 +98,7 @@
     doc = "This type represents a file used by the build system. It can be "
         + "either a source file or a derived file produced by a rule.")
 public class Artifact
-    implements FileType.HasFilename, ArtifactFile, SkylarkValue , Comparable<Object> {
+    implements FileType.HasFilename, ActionInput, SkylarkValue, Comparable<Object> {
 
   /**
    * Compares artifact according to their exec paths. Sorts null values first.
@@ -136,7 +136,7 @@
      * <p>{@code artifact.isMiddlemanArtifact() || artifact.isTreeArtifact()} must be true.
      * Only aggregating middlemen and tree artifacts are expanded.
      */
-    void expand(Artifact artifact, Collection<? super ArtifactFile> output);
+    void expand(Artifact artifact, Collection<? super Artifact> output);
   }
 
   public static final ImmutableList<Artifact> NO_ARTIFACTS = ImmutableList.of();
@@ -239,20 +239,20 @@
         root.getExecPath().getRelative(rootRelativePath), ArtifactOwner.NULL_OWNER);
   }
 
-  @Override
   public final Path getPath() {
     return path;
   }
 
+  public boolean hasParent() {
+    return getParent() != null;
+  }
+
   /**
-   * Returns the Artifact containing this ArtifactFile. Since normal Artifacts correspond
-   * to only one ArtifactFile -- itself -- for normal Artifacts, this method returns {@code this}.
-   * For special artifacts, throws {@link UnsupportedOperationException}.
-   * See also {@link ArtifactFile#getParent()}.
+   * Returns the parent Artifact containing this Artifact. Artifacts without parents shall
+   * return null.
    */
-  @Override
-  public Artifact getParent() throws UnsupportedOperationException {
-    return this;
+  @Nullable public Artifact getParent() {
+    return null;
   }
 
   /**
@@ -307,7 +307,6 @@
    * package-path entries (for source Artifacts), or one of the bin, genfiles or includes dirs
    * (for derived Artifacts). It will always be an ancestor of getPath().
    */
-  @Override
   @SkylarkCallable(name = "root", structField = true,
       doc = "The root beneath which this file resides."
   )
@@ -315,18 +314,16 @@
     return root;
   }
 
-  @Override
   public final PathFragment getExecPath() {
     return execPath;
   }
 
   /**
-   * Returns the path of this ArtifactFile relative to this containing Artifact. Since
-   * ordinary Artifacts correspond to only one ArtifactFile -- itself -- for ordinary Artifacts,
+   * Returns the path of this Artifact relative to this containing Artifact. Since
+   * ordinary Artifacts correspond to only one Artifact -- itself -- for ordinary Artifacts,
    * this just returns the empty path. For special Artifacts, throws
-   * {@link UnsupportedOperationException}. See also {@link ArtifactFile#getParentRelativePath()}.
+   * {@link UnsupportedOperationException}. See also {@link Artifact#getParentRelativePath()}.
    */
-  @Override
   public PathFragment getParentRelativePath() {
     return PathFragment.EMPTY_FRAGMENT;
   }
@@ -350,7 +347,7 @@
   }
 
   /**
-   * Returns true iff this is a TreeArtifact representing a directory tree containing ArtifactFiles.
+   * Returns true iff this is a TreeArtifact representing a directory tree containing Artifacts.
    */
   public boolean isTreeArtifact() {
     return false;
@@ -418,13 +415,77 @@
     }
 
     @Override
+    public boolean hasParent() {
+      return false;
+    }
+
+    @Override
+    @Nullable
     public Artifact getParent() {
-      throw new UnsupportedOperationException();
+      return null;
+    }
+
+    @Override
+    @Nullable
+    public PathFragment getParentRelativePath() {
+      return null;
+    }
+  }
+
+  /**
+   * A special kind of artifact that represents a concrete file created at execution time under
+   * its associated TreeArtifact.
+   *
+   * <p> TreeFileArtifacts should be only created during execution time inside some special actions
+   * to support action inputs and outputs that are unpredictable at analysis time.
+   * TreeFileArtifacts should not be created directly by any rules at analysis time.
+   *
+   * <p>We subclass {@link Artifact} instead of storing the extra fields directly inside in order
+   * to save memory. The proportion of TreeFileArtifacts is very small, and by not having to keep
+   * around the extra fields for the rest we save some memory.
+   */
+  @Immutable
+  public static final class TreeFileArtifact extends Artifact {
+    private final Artifact parentTreeArtifact;
+    private final PathFragment parentRelativePath;
+
+    /**
+     * Constructs a TreeFileArtifact with the given parent-relative path under the given parent
+     * TreeArtifact. The {@link ArtifactOwner} of the TreeFileArtifact is the {@link ArtifactOwner}
+     * of the parent TreeArtifact.
+     */
+    TreeFileArtifact(Artifact parent, PathFragment parentRelativePath) {
+      this(parent, parentRelativePath, parent.getArtifactOwner());
+    }
+
+    /**
+     * Constructs a TreeFileArtifact with the given parent-relative path under the given parent
+     * TreeArtifact, owned by the given {@code artifactOwner}.
+     */
+    TreeFileArtifact(Artifact parent, PathFragment parentRelativePath,
+        ArtifactOwner artifactOwner) {
+      super(
+          parent.getPath().getRelative(parentRelativePath),
+          parent.getRoot(),
+          parent.getExecPath().getRelative(parentRelativePath),
+          artifactOwner);
+      Preconditions.checkState(
+          parent.isTreeArtifact(),
+          "The parent of TreeFileArtifact (parent-relative path: %s) is not a TreeArtifact: %s",
+          parentRelativePath,
+          parent);
+      this.parentTreeArtifact = parent;
+      this.parentRelativePath = parentRelativePath;
+    }
+
+    @Override
+    public Artifact getParent() {
+      return parentTreeArtifact;
     }
 
     @Override
     public PathFragment getParentRelativePath() {
-      throw new UnsupportedOperationException();
+      return parentRelativePath;
     }
   }
 
@@ -432,7 +493,6 @@
    * Returns the relative path to this artifact relative to its root.  (Useful
    * when deriving output filenames from input files, etc.)
    */
-  @Override
   public final PathFragment getRootRelativePath() {
     return rootRelativePath;
   }
@@ -478,7 +538,6 @@
     return getRootRelativePath().getPathString();
   }
 
-  @Override
   public final String prettyPrint() {
     // toDetailString would probably be more useful to users, but lots of tests rely on the
     // current values.
@@ -550,26 +609,26 @@
   /**
    * Formatter for execPath PathFragment output.
    */
-  private static final Function<ArtifactFile, PathFragment> EXEC_PATH_FORMATTER =
-      new Function<ArtifactFile, PathFragment>() {
+  private static final Function<Artifact, PathFragment> EXEC_PATH_FORMATTER =
+      new Function<Artifact, PathFragment>() {
         @Override
-        public PathFragment apply(ArtifactFile input) {
+        public PathFragment apply(Artifact input) {
           return input.getExecPath();
         }
       };
 
-  public static final Function<ArtifactFile, String> ROOT_RELATIVE_PATH_STRING =
-      new Function<ArtifactFile, String>() {
+  public static final Function<Artifact, String> ROOT_RELATIVE_PATH_STRING =
+      new Function<Artifact, String>() {
         @Override
-        public String apply(ArtifactFile artifact) {
+        public String apply(Artifact artifact) {
           return artifact.getRootRelativePath().getPathString();
         }
       };
 
-  public static final Function<ArtifactFile, String> ABSOLUTE_PATH_STRING =
-      new Function<ArtifactFile, String>() {
+  public static final Function<Artifact, String> ABSOLUTE_PATH_STRING =
+      new Function<Artifact, String>() {
         @Override
-        public String apply(ArtifactFile artifact) {
+        public String apply(Artifact artifact) {
           return artifact.getPath().getPathString();
         }
       };
@@ -654,8 +713,8 @@
    * {@link MiddlemanType#AGGREGATING_MIDDLEMAN} middleman actions expanded once.
    */
   public static void addExpandedArtifacts(Iterable<Artifact> artifacts,
-      Collection<? super ArtifactFile> output, ArtifactExpander artifactExpander) {
-    addExpandedArtifacts(artifacts, output, Functions.<ArtifactFile>identity(), artifactExpander);
+      Collection<? super Artifact> output, ArtifactExpander artifactExpander) {
+    addExpandedArtifacts(artifacts, output, Functions.<Artifact>identity(), artifactExpander);
   }
 
   /**
@@ -690,7 +749,7 @@
    */
   private static <E> void addExpandedArtifacts(Iterable<? extends Artifact> artifacts,
                                                Collection<? super E> output,
-                                               Function<? super ArtifactFile, E> outputFormatter,
+                                               Function<? super Artifact, E> outputFormatter,
                                                ArtifactExpander artifactExpander) {
     for (Artifact artifact : artifacts) {
       if (artifact.isMiddlemanArtifact() || artifact.isTreeArtifact()) {
@@ -703,12 +762,12 @@
 
   private static <E> void expandArtifact(Artifact middleman,
       Collection<? super E> output,
-      Function<? super ArtifactFile, E> outputFormatter,
+      Function<? super Artifact, E> outputFormatter,
       ArtifactExpander artifactExpander) {
     Preconditions.checkArgument(middleman.isMiddlemanArtifact() || middleman.isTreeArtifact());
-    List<ArtifactFile> artifacts = new ArrayList<>();
+    List<Artifact> artifacts = new ArrayList<>();
     artifactExpander.expand(middleman, artifacts);
-    for (ArtifactFile artifact : artifacts) {
+    for (Artifact artifact : artifacts) {
       output.add(outputFormatter.apply(artifact));
     }
   }
@@ -785,7 +844,7 @@
   /**
    * Converts artifacts into their exec paths. Returns an immutable list.
    */
-  public static List<PathFragment> asPathFragments(Iterable<? extends ArtifactFile> artifacts) {
+  public static List<PathFragment> asPathFragments(Iterable<? extends Artifact> artifacts) {
     return ImmutableList.copyOf(Iterables.transform(artifacts, EXEC_PATH_FORMATTER));
   }