Aspects propagating other aspects

This CL adds a new parameter to the `aspect()` API called `requires` guarded by `experimental_required_aspects` flag. Through this parameter we can specify a list of aspects needed by the aspect being defined to be propagated before it to the targets it operates on.

Aspects can only require Starlark defined aspects.

PiperOrigin-RevId: 377473191
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Attribute.java b/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
index d7f4a0b..29149fe 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
@@ -78,12 +78,40 @@
   /** Wraps the information necessary to construct an Aspect. */
   @VisibleForSerialization
   abstract static class RuleAspect<C extends AspectClass> {
+    private static final ImmutableList<String> ALL_ATTR_ASPECTS = ImmutableList.of("*");
+
     protected final C aspectClass;
     protected final Function<Rule, AspectParameters> parametersExtractor;
 
+    protected String baseAspectName;
+    protected ImmutableList.Builder<ImmutableSet<StarlarkProviderIdentifier>>
+        inheritedRequiredProviders;
+    protected ImmutableList.Builder<String> inheritedAttributeAspects;
+    protected boolean inheritedAllProviders = false;
+    protected boolean inheritedAllAttributes = false;
+
     private RuleAspect(C aspectClass, Function<Rule, AspectParameters> parametersExtractor) {
       this.aspectClass = aspectClass;
       this.parametersExtractor = parametersExtractor;
+      this.inheritedRequiredProviders = ImmutableList.builder();
+      this.inheritedAttributeAspects = ImmutableList.builder();
+    }
+
+    private RuleAspect(
+        C aspectClass,
+        Function<Rule, AspectParameters> parametersExtractor,
+        String baseAspectName,
+        ImmutableList<ImmutableSet<StarlarkProviderIdentifier>> inheritedRequiredProviders,
+        ImmutableList<String> inheritedAttributeAspects) {
+      this.aspectClass = aspectClass;
+      this.parametersExtractor = parametersExtractor;
+      this.baseAspectName = baseAspectName;
+      this.inheritedRequiredProviders = ImmutableList.builder();
+      this.inheritedAttributeAspects = ImmutableList.builder();
+      if (baseAspectName != null) {
+        updateInheritedRequiredProviders(inheritedRequiredProviders);
+        updateInheritedAttributeAspects(inheritedAttributeAspects);
+      }
     }
 
     public String getName() {
@@ -99,6 +127,70 @@
     public C getAspectClass() {
       return aspectClass;
     }
+
+    protected void updateInheritedRequiredProviders(
+        ImmutableList<ImmutableSet<StarlarkProviderIdentifier>> requiredProviders) {
+      if (!inheritedAllProviders && !requiredProviders.isEmpty()) {
+        inheritedRequiredProviders.addAll(requiredProviders);
+      } else {
+        inheritedAllProviders = true;
+      }
+    }
+
+    protected void updateInheritedAttributeAspects(ImmutableList<String> attributeAspects) {
+      if (!inheritedAllAttributes && !ALL_ATTR_ASPECTS.equals(attributeAspects)) {
+        inheritedAttributeAspects.addAll(attributeAspects);
+      } else {
+        inheritedAllAttributes = true;
+      }
+    }
+
+    protected RequiredProviders buildInheritedRequiredProviders() {
+      if (baseAspectName == null) {
+        return RequiredProviders.acceptNoneBuilder().build();
+      } else if (inheritedAllProviders) {
+        return RequiredProviders.acceptAnyBuilder().build();
+      } else {
+        ImmutableList<ImmutableSet<StarlarkProviderIdentifier>> inheritedRequiredProvidersList =
+            inheritedRequiredProviders.build();
+        RequiredProviders.Builder inheritedRequiredProvidersBuilder =
+            RequiredProviders.acceptAnyBuilder();
+        for (ImmutableSet<StarlarkProviderIdentifier> providerSet :
+            inheritedRequiredProvidersList) {
+          if (!providerSet.isEmpty()) {
+            inheritedRequiredProvidersBuilder.addStarlarkSet(providerSet);
+          }
+        }
+        return inheritedRequiredProvidersBuilder.build();
+      }
+    }
+
+    @Nullable
+    protected ImmutableSet<String> buildInheritedAttributeAspects() {
+      if (baseAspectName == null) {
+        return ImmutableSet.of();
+      } else if (inheritedAllAttributes) {
+        return null;
+      } else {
+        return ImmutableSet.<String>copyOf(inheritedAttributeAspects.build());
+      }
+    }
+
+    @VisibleForSerialization
+    public ImmutableList<ImmutableSet<StarlarkProviderIdentifier>>
+        getInheritedRequiredProvidersList() {
+      return inheritedRequiredProviders.build();
+    }
+
+    @VisibleForSerialization
+    public ImmutableList<String> getInheritedAttributeAspectsList() {
+      return inheritedAttributeAspects.build();
+    }
+
+    @VisibleForSerialization
+    public String getBaseAspectName() {
+      return baseAspectName;
+    }
   }
 
   private static class NativeRuleAspect extends RuleAspect<NativeAspectClass> {
@@ -107,10 +199,30 @@
       super(aspectClass, parametersExtractor);
     }
 
+    NativeRuleAspect(
+        NativeAspectClass aspectClass,
+        Function<Rule, AspectParameters> parametersExtractor,
+        String baseAspectName,
+        ImmutableList<ImmutableSet<StarlarkProviderIdentifier>> inheritedRequiredProvidersList,
+        ImmutableList<String> inheritedAttributeAspectsList) {
+      super(
+          aspectClass,
+          parametersExtractor,
+          baseAspectName,
+          inheritedRequiredProvidersList,
+          inheritedAttributeAspectsList);
+    }
+
     @Override
     public Aspect getAspect(Rule rule) {
       AspectParameters params = parametersExtractor.apply(rule);
-      return params == null ? null : Aspect.forNative(aspectClass, params);
+      return params == null
+          ? null
+          : Aspect.forNative(
+              aspectClass,
+              params,
+              buildInheritedRequiredProviders(),
+              buildInheritedAttributeAspects());
     }
   }
 
@@ -120,8 +232,17 @@
     private final StarlarkDefinedAspect aspect;
 
     @VisibleForSerialization
-    StarlarkRuleAspect(StarlarkDefinedAspect aspect) {
-      super(aspect.getAspectClass(), aspect.getDefaultParametersExtractor());
+    StarlarkRuleAspect(
+        StarlarkDefinedAspect aspect,
+        String baseAspectName,
+        ImmutableList<ImmutableSet<StarlarkProviderIdentifier>> inheritedRequiredProvidersList,
+        ImmutableList<String> inheritedAttributeAspectsList) {
+      super(
+          aspect.getAspectClass(),
+          aspect.getDefaultParametersExtractor(),
+          baseAspectName,
+          inheritedRequiredProvidersList,
+          inheritedAttributeAspectsList);
       this.aspect = aspect;
     }
 
@@ -133,7 +254,12 @@
     @Override
     public Aspect getAspect(Rule rule) {
       AspectParameters parameters = parametersExtractor.apply(rule);
-      return Aspect.forStarlark(aspectClass, aspect.getDefinition(parameters), parameters);
+      return Aspect.forStarlark(
+          aspectClass,
+          aspect.getDefinition(parameters),
+          parameters,
+          buildInheritedRequiredProviders(),
+          buildInheritedAttributeAspects());
     }
   }
 
@@ -993,6 +1119,9 @@
       return this;
     }
 
+    @AutoCodec @AutoCodec.VisibleForSerialization
+    static final Function<Rule, AspectParameters> EMPTY_FUNCTION = input -> AspectParameters.EMPTY;
+
     /**
      * Asserts that a particular parameterized aspect probably needs to be computed for all direct
      * dependencies through this attribute.
@@ -1019,14 +1148,51 @@
       return this.aspect(aspect, EMPTY_FUNCTION);
     }
 
-    @AutoCodec @AutoCodec.VisibleForSerialization
-    static final Function<Rule, AspectParameters> EMPTY_FUNCTION = input -> AspectParameters.EMPTY;
+    public Builder<TYPE> aspect(
+        StarlarkDefinedAspect starlarkAspect,
+        String baseAspectName,
+        ImmutableList<ImmutableSet<StarlarkProviderIdentifier>> inheritedRequiredProviders,
+        ImmutableList<String> inheritedAttributeAspects)
+        throws EvalException {
+      boolean needsToAdd =
+          checkAndUpdateExistingAspects(
+              starlarkAspect.getName(),
+              baseAspectName,
+              inheritedRequiredProviders,
+              inheritedAttributeAspects);
+      if (needsToAdd) {
+        StarlarkRuleAspect starlarkRuleAspect =
+            new StarlarkRuleAspect(
+                starlarkAspect,
+                baseAspectName,
+                inheritedRequiredProviders,
+                inheritedAttributeAspects);
+        this.aspects.put(starlarkAspect.getName(), starlarkRuleAspect);
+      }
+      return this;
+    }
 
-    public Builder<TYPE> aspect(StarlarkDefinedAspect starlarkAspect) throws EvalException {
-      StarlarkRuleAspect starlarkRuleAspect = new StarlarkRuleAspect(starlarkAspect);
-      RuleAspect<?> oldAspect = this.aspects.put(starlarkAspect.getName(), starlarkRuleAspect);
-      if (oldAspect != null) {
-        throw Starlark.errorf("aspect %s added more than once", starlarkAspect.getName());
+    public Builder<TYPE> aspect(
+        NativeAspectClass nativeAspect,
+        String baseAspectName,
+        ImmutableList<ImmutableSet<StarlarkProviderIdentifier>> inheritedRequiredProviders,
+        ImmutableList<String> inheritedAttributeAspects)
+        throws EvalException {
+      boolean needsToAdd =
+          checkAndUpdateExistingAspects(
+              nativeAspect.getName(),
+              baseAspectName,
+              inheritedRequiredProviders,
+              inheritedAttributeAspects);
+      if (needsToAdd) {
+        NativeRuleAspect nativeRuleAspect =
+            new NativeRuleAspect(
+                nativeAspect,
+                EMPTY_FUNCTION,
+                baseAspectName,
+                inheritedRequiredProviders,
+                inheritedAttributeAspects);
+        this.aspects.put(nativeAspect.getName(), nativeRuleAspect);
       }
       return this;
     }
@@ -1043,6 +1209,40 @@
       return this;
     }
 
+    private boolean checkAndUpdateExistingAspects(
+        String aspectName,
+        String baseAspectName,
+        ImmutableList<ImmutableSet<StarlarkProviderIdentifier>> inheritedRequiredProviders,
+        ImmutableList<String> inheritedAttributeAspects)
+        throws EvalException {
+
+      RuleAspect<?> oldAspect = this.aspects.get(aspectName);
+
+      if (oldAspect != null) {
+        // If the aspect to be added is required by another aspect, i.e. {@code baseAspectName} is
+        // not null, then we need to update its inherited required providers and propgation
+        // attributes.
+        if (baseAspectName != null) {
+          oldAspect.baseAspectName = baseAspectName;
+          oldAspect.updateInheritedRequiredProviders(inheritedRequiredProviders);
+          oldAspect.updateInheritedAttributeAspects(inheritedAttributeAspects);
+          return false; // no need to add the new aspect
+        } else {
+          // If the aspect to be added is not required by another aspect, then we
+          // should throw an error
+          String oldAspectBaseAspectName = oldAspect.baseAspectName;
+          if (oldAspectBaseAspectName != null) {
+            throw Starlark.errorf(
+                "aspect %s was added before as a required aspect of aspect %s",
+                oldAspect.getName(), oldAspectBaseAspectName);
+          }
+          throw Starlark.errorf("aspect %s added more than once", oldAspect.getName());
+        }
+      }
+
+      return true; // we need to add the new aspect
+    }
+
     /** Sets the predicate-like edge validity checker. */
     public Builder<TYPE> validityPredicate(ValidityPredicate validityPredicate) {
       propertyFlags.add(PropertyFlag.STRICT_LABEL_CHECKING);