Aspects-on-aspects implementation.

--
MOS_MIGRATED_REVID=139189444
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/AspectValueTest.java b/src/test/java/com/google/devtools/build/lib/analysis/AspectValueTest.java
index bcdb951..b5e3bcd 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/AspectValueTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/AspectValueTest.java
@@ -21,8 +21,12 @@
 import com.google.devtools.build.lib.analysis.util.TestAspects.ExtraAttributeAspect;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.packages.AspectParameters;
+import com.google.devtools.build.lib.packages.NativeAspectClass;
+import com.google.devtools.build.lib.skyframe.ActionLookupValue;
 import com.google.devtools.build.lib.skyframe.AspectValue;
 
+import com.google.devtools.build.lib.skyframe.AspectValue.AspectKey;
+import com.google.devtools.build.skyframe.SkyKey;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -62,145 +66,177 @@
 
     new EqualsTester()
         .addEqualityGroup(
-            AspectValue.key(l1, c1, c1, a1, i1),
-            AspectValue.key(l1, c1, c1, a1, i1b),
-            AspectValue.key(l1, c1, c1, a1b, i1),
-            AspectValue.key(l1, c1, c1, a1b, i1b),
-            AspectValue.key(l1b, c1, c1, a1, i1),
-            AspectValue.key(l1b, c1, c1, a1, i1b),
-            AspectValue.key(l1b, c1, c1, a1b, i1),
-            AspectValue.key(l1b, c1, c1, a1b, i1b))
+            createKey(l1, c1, a1, i1, c1),
+            createKey(l1, c1, a1, i1b, c1),
+            createKey(l1, c1, a1b, i1, c1),
+            createKey(l1, c1, a1b, i1b, c1),
+            createKey(l1b, c1, a1, i1, c1),
+            createKey(l1b, c1, a1, i1b, c1),
+            createKey(l1b, c1, a1b, i1, c1),
+            createKey(l1b, c1, a1b, i1b, c1))
         .addEqualityGroup(
-            AspectValue.key(l1, c1, c1, a1, i2),
-            AspectValue.key(l1, c1, c1, a1b, i2),
-            AspectValue.key(l1b, c1, c1, a1, i2),
-            AspectValue.key(l1b, c1, c1, a1b, i2))
+            createKey(l1, c1, a1, i2, c1),
+            createKey(l1, c1, a1b, i2, c1),
+            createKey(l1b, c1, a1, i2, c1),
+            createKey(l1b, c1, a1b, i2, c1))
         .addEqualityGroup(
-            AspectValue.key(l1, c1, c1, a2, i1),
-            AspectValue.key(l1, c1, c1, a2, i1b),
-            AspectValue.key(l1b, c1, c1, a2, i1),
-            AspectValue.key(l1b, c1, c1, a2, i1b))
+            createKey(l1, c1, a2, i1, c1),
+            createKey(l1, c1, a2, i1b, c1),
+            createKey(l1b, c1, a2, i1, c1),
+            createKey(l1b, c1, a2, i1b, c1))
         .addEqualityGroup(
-            AspectValue.key(l1, c1, c1, a2, i2),
-            AspectValue.key(l1b, c1, c1, a2, i2))
+            createKey(l1, c1, a2, i2, c1),
+            createKey(l1b, c1, a2, i2, c1))
         .addEqualityGroup(
-            AspectValue.key(l1, c1, c2, a1, i1),
-            AspectValue.key(l1, c1, c2, a1, i1b),
-            AspectValue.key(l1, c1, c2, a1b, i1),
-            AspectValue.key(l1, c1, c2, a1b, i1b),
-            AspectValue.key(l1b, c1, c2, a1, i1),
-            AspectValue.key(l1b, c1, c2, a1, i1b),
-            AspectValue.key(l1b, c1, c2, a1b, i1),
-            AspectValue.key(l1b, c1, c2, a1b, i1b))
+            createKey(l1, c2, a1, i1, c1),
+            createKey(l1, c2, a1, i1b, c1),
+            createKey(l1, c2, a1b, i1, c1),
+            createKey(l1, c2, a1b, i1b, c1),
+            createKey(l1b, c2, a1, i1, c1),
+            createKey(l1b, c2, a1, i1b, c1),
+            createKey(l1b, c2, a1b, i1, c1),
+            createKey(l1b, c2, a1b, i1b, c1))
         .addEqualityGroup(
-            AspectValue.key(l1, c1, c2, a1, i2),
-            AspectValue.key(l1, c1, c2, a1b, i2),
-            AspectValue.key(l1b, c1, c2, a1, i2),
-            AspectValue.key(l1b, c1, c2, a1b, i2))
+            createKey(l1, c2, a1, i2, c1),
+            createKey(l1, c2, a1b, i2, c1),
+            createKey(l1b, c2, a1, i2, c1),
+            createKey(l1b, c2, a1b, i2, c1))
         .addEqualityGroup(
-            AspectValue.key(l1, c1, c2, a2, i1),
-            AspectValue.key(l1, c1, c2, a2, i1b),
-            AspectValue.key(l1b, c1, c2, a2, i1),
-            AspectValue.key(l1b, c1, c2, a2, i1b))
+            createKey(l1, c2, a2, i1, c1),
+            createKey(l1, c2, a2, i1b, c1),
+            createKey(l1b, c2, a2, i1, c1),
+            createKey(l1b, c2, a2, i1b, c1))
         .addEqualityGroup(
-            AspectValue.key(l1, c1, c2, a2, i2),
-            AspectValue.key(l1b, c1, c2, a2, i2))
+            createKey(l1, c2, a2, i2, c1),
+            createKey(l1b, c2, a2, i2, c1))
         .addEqualityGroup(
-            AspectValue.key(l1, c2, c1, a1, i1),
-            AspectValue.key(l1, c2, c1, a1, i1b),
-            AspectValue.key(l1, c2, c1, a1b, i1),
-            AspectValue.key(l1, c2, c1, a1b, i1b),
-            AspectValue.key(l1b, c2, c1, a1, i1),
-            AspectValue.key(l1b, c2, c1, a1, i1b),
-            AspectValue.key(l1b, c2, c1, a1b, i1),
-            AspectValue.key(l1b, c2, c1, a1b, i1b))
+            createKey(l1, c1, a1, i1, c2),
+            createKey(l1, c1, a1, i1b, c2),
+            createKey(l1, c1, a1b, i1, c2),
+            createKey(l1, c1, a1b, i1b, c2),
+            createKey(l1b, c1, a1, i1, c2),
+            createKey(l1b, c1, a1, i1b, c2),
+            createKey(l1b, c1, a1b, i1, c2),
+            createKey(l1b, c1, a1b, i1b, c2))
         .addEqualityGroup(
-            AspectValue.key(l1, c2, c1, a1, i2),
-            AspectValue.key(l1, c2, c1, a1b, i2),
-            AspectValue.key(l1b, c2, c1, a1, i2),
-            AspectValue.key(l1b, c2, c1, a1b, i2))
+            createKey(l1, c1, a1, i2, c2),
+            createKey(l1, c1, a1b, i2, c2),
+            createKey(l1b, c1, a1, i2, c2),
+            createKey(l1b, c1, a1b, i2, c2))
         .addEqualityGroup(
-            AspectValue.key(l1, c2, c1, a2, i1),
-            AspectValue.key(l1, c2, c1, a2, i1b),
-            AspectValue.key(l1b, c2, c1, a2, i1),
-            AspectValue.key(l1b, c2, c1, a2, i1b))
+            createKey(l1, c1, a2, i1, c2),
+            createKey(l1, c1, a2, i1b, c2),
+            createKey(l1b, c1, a2, i1, c2),
+            createKey(l1b, c1, a2, i1b, c2))
         .addEqualityGroup(
-            AspectValue.key(l1, c2, c1, a2, i2),
-            AspectValue.key(l1b, c2, c1, a2, i2))
+            createKey(l1, c1, a2, i2, c2),
+            createKey(l1b, c1, a2, i2, c2))
         .addEqualityGroup(
-            AspectValue.key(l1, c2, c2, a1, i1),
-            AspectValue.key(l1, c2, c2, a1, i1b),
-            AspectValue.key(l1, c2, c2, a1b, i1),
-            AspectValue.key(l1, c2, c2, a1b, i1b),
-            AspectValue.key(l1b, c2, c2, a1, i1),
-            AspectValue.key(l1b, c2, c2, a1, i1b),
-            AspectValue.key(l1b, c2, c2, a1b, i1),
-            AspectValue.key(l1b, c2, c2, a1b, i1b))
+            createKey(l1, c2, a1, i1, c2),
+            createKey(l1, c2, a1, i1b, c2),
+            createKey(l1, c2, a1b, i1, c2),
+            createKey(l1, c2, a1b, i1b, c2),
+            createKey(l1b, c2, a1, i1, c2),
+            createKey(l1b, c2, a1, i1b, c2),
+            createKey(l1b, c2, a1b, i1, c2),
+            createKey(l1b, c2, a1b, i1b, c2))
         .addEqualityGroup(
-            AspectValue.key(l1, c2, c2, a1, i2),
-            AspectValue.key(l1, c2, c2, a1b, i2),
-            AspectValue.key(l1b, c2, c2, a1, i2),
-            AspectValue.key(l1b, c2, c2, a1b, i2))
+            createKey(l1, c2, a1, i2, c2),
+            createKey(l1, c2, a1b, i2, c2),
+            createKey(l1b, c2, a1, i2, c2),
+            createKey(l1b, c2, a1b, i2, c2))
         .addEqualityGroup(
-            AspectValue.key(l1, c2, c2, a2, i1),
-            AspectValue.key(l1, c2, c2, a2, i1b),
-            AspectValue.key(l1b, c2, c2, a2, i1),
-            AspectValue.key(l1b, c2, c2, a2, i1b))
+            createKey(l1, c2, a2, i1, c2),
+            createKey(l1, c2, a2, i1b, c2),
+            createKey(l1b, c2, a2, i1, c2),
+            createKey(l1b, c2, a2, i1b, c2))
         .addEqualityGroup(
-            AspectValue.key(l1, c2, c2, a2, i2),
-            AspectValue.key(l1b, c2, c2, a2, i2))
+            createKey(l1, c2, a2, i2, c2),
+            createKey(l1b, c2, a2, i2, c2))
         .addEqualityGroup(
-            AspectValue.key(l2, c1, c1, a1, i1),
-            AspectValue.key(l2, c1, c1, a1, i1b),
-            AspectValue.key(l2, c1, c1, a1b, i1),
-            AspectValue.key(l2, c1, c1, a1b, i1b))
+            createKey(l2, c1, a1, i1, c1),
+            createKey(l2, c1, a1, i1b, c1),
+            createKey(l2, c1, a1b, i1, c1),
+            createKey(l2, c1, a1b, i1b, c1))
         .addEqualityGroup(
-            AspectValue.key(l2, c1, c1, a1, i2),
-            AspectValue.key(l2, c1, c1, a1b, i2))
+            createKey(l2, c1, a1, i2, c1),
+            createKey(l2, c1, a1b, i2, c1))
         .addEqualityGroup(
-            AspectValue.key(l2, c1, c1, a2, i1),
-            AspectValue.key(l2, c1, c1, a2, i1b))
+            createKey(l2, c1, a2, i1, c1),
+            createKey(l2, c1, a2, i1b, c1))
         .addEqualityGroup(
-            AspectValue.key(l2, c1, c1, a2, i2))
+            createKey(l2, c1, a2, i2, c1))
         .addEqualityGroup(
-            AspectValue.key(l2, c1, c2, a1, i1),
-            AspectValue.key(l2, c1, c2, a1, i1b),
-            AspectValue.key(l2, c1, c2, a1b, i1),
-            AspectValue.key(l2, c1, c2, a1b, i1b))
+            createKey(l2, c2, a1, i1, c1),
+            createKey(l2, c2, a1, i1b, c1),
+            createKey(l2, c2, a1b, i1, c1),
+            createKey(l2, c2, a1b, i1b, c1))
         .addEqualityGroup(
-            AspectValue.key(l2, c1, c2, a1, i2),
-            AspectValue.key(l2, c1, c2, a1b, i2))
+            createKey(l2, c2, a1, i2, c1),
+            createKey(l2, c2, a1b, i2, c1))
         .addEqualityGroup(
-            AspectValue.key(l2, c1, c2, a2, i1),
-            AspectValue.key(l2, c1, c2, a2, i1b))
+            createKey(l2, c2, a2, i1, c1),
+            createKey(l2, c2, a2, i1b, c1))
         .addEqualityGroup(
-            AspectValue.key(l2, c1, c2, a2, i2))
+            createKey(l2, c2, a2, i2, c1))
         .addEqualityGroup(
-            AspectValue.key(l2, c2, c1, a1, i1),
-            AspectValue.key(l2, c2, c1, a1, i1b),
-            AspectValue.key(l2, c2, c1, a1b, i1),
-            AspectValue.key(l2, c2, c1, a1b, i1b))
+            createKey(l2, c1, a1, i1, c2),
+            createKey(l2, c1, a1, i1b, c2),
+            createKey(l2, c1, a1b, i1, c2),
+            createKey(l2, c1, a1b, i1b, c2))
         .addEqualityGroup(
-            AspectValue.key(l2, c2, c1, a1, i2),
-            AspectValue.key(l2, c2, c1, a1b, i2))
+            createKey(l2, c1, a1, i2, c2),
+            createKey(l2, c1, a1b, i2, c2))
         .addEqualityGroup(
-            AspectValue.key(l2, c2, c1, a2, i1),
-            AspectValue.key(l2, c2, c1, a2, i1b))
+            createKey(l2, c1, a2, i1, c2),
+            createKey(l2, c1, a2, i1b, c2))
         .addEqualityGroup(
-            AspectValue.key(l2, c2, c1, a2, i2))
+            createKey(l2, c1, a2, i2, c2))
         .addEqualityGroup(
-            AspectValue.key(l2, c2, c2, a1, i1),
-            AspectValue.key(l2, c2, c2, a1, i1b),
-            AspectValue.key(l2, c2, c2, a1b, i1),
-            AspectValue.key(l2, c2, c2, a1b, i1b))
+            createKey(l2, c2, a1, i1, c2),
+            createKey(l2, c2, a1, i1b, c2),
+            createKey(l2, c2, a1b, i1, c2),
+            createKey(l2, c2, a1b, i1b, c2))
         .addEqualityGroup(
-            AspectValue.key(l2, c2, c2, a1, i2),
-            AspectValue.key(l2, c2, c2, a1b, i2))
+            createKey(l2, c2, a1, i2, c2),
+            createKey(l2, c2, a1b, i2, c2))
         .addEqualityGroup(
-            AspectValue.key(l2, c2, c2, a2, i1),
-            AspectValue.key(l2, c2, c2, a2, i1b))
+            createKey(l2, c2, a2, i1, c2),
+            createKey(l2, c2, a2, i1b, c2))
         .addEqualityGroup(
-            AspectValue.key(l2, c2, c2, a2, i2))
+            createKey(l2, c2, a2, i2, c2))
+        .addEqualityGroup(
+            createDerivedKey(l1, c1, a1, i1, c1, a2, i2, c2),
+            createDerivedKey(l1, c1, a1, i1b, c1, a2, i2, c2)
+        )
+        .addEqualityGroup(
+            createDerivedKey(l1, c1, a2, i1, c1, a1, i2, c2),
+            createDerivedKey(l1, c1, a2, i1b, c1, a1, i2, c2)
+        )
         .testEquals();
   }
+
+  private static SkyKey createKey(
+      Label label, BuildConfiguration baseConfiguration, NativeAspectClass aspectClass,
+      AspectParameters parameters, BuildConfiguration aspectConfiguration) {
+    return ActionLookupValue.key(AspectValue.createAspectKey(
+                label, baseConfiguration, new AspectDescriptor(aspectClass, parameters),
+        aspectConfiguration
+    ));
+  }
+
+  private static SkyKey createDerivedKey(
+      Label label, BuildConfiguration baseConfiguration,
+      NativeAspectClass aspectClass1, AspectParameters parameters1,
+      BuildConfiguration aspectConfiguration1,
+      NativeAspectClass aspectClass2, AspectParameters parameters2,
+      BuildConfiguration aspectConfiguration2) {
+    AspectKey baseKey = AspectValue.createAspectKey(label, baseConfiguration,
+        new AspectDescriptor(aspectClass1, parameters1), aspectConfiguration1);
+    return ActionLookupValue.key(AspectValue.createAspectKey(
+        baseKey, new AspectDescriptor(aspectClass2, parameters2),
+        aspectConfiguration2
+    ));
+  }
+
 }
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
index 4a4b03d..f5b15a8 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
@@ -48,6 +48,7 @@
 import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
 import com.google.devtools.build.lib.analysis.AnalysisEnvironment;
 import com.google.devtools.build.lib.analysis.AnalysisUtils;
+import com.google.devtools.build.lib.analysis.AspectDescriptor;
 import com.google.devtools.build.lib.analysis.BlazeDirectories;
 import com.google.devtools.build.lib.analysis.BuildView;
 import com.google.devtools.build.lib.analysis.BuildView.AnalysisResult;
@@ -119,6 +120,7 @@
 import com.google.devtools.build.lib.rules.extra.ExtraAction;
 import com.google.devtools.build.lib.rules.test.BaselineCoverageAction;
 import com.google.devtools.build.lib.rules.test.InstrumentedFilesProvider;
+import com.google.devtools.build.lib.skyframe.ActionLookupValue;
 import com.google.devtools.build.lib.skyframe.AspectValue;
 import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
 import com.google.devtools.build.lib.skyframe.DiffAwareness;
@@ -1063,12 +1065,10 @@
         packageRelativePath,
         owner.getConfiguration().getBinDirectory(RepositoryName.MAIN),
         (AspectValue.AspectKey)
-            AspectValue.key(
-                    owner.getLabel(),
-                    owner.getConfiguration(),
-                    owner.getConfiguration(),
-                    creatingAspectFactory,
-                    parameters)
+            ActionLookupValue.key(AspectValue.createAspectKey(
+                owner.getLabel(), owner.getConfiguration(),
+                new AspectDescriptor(creatingAspectFactory, parameters), owner.getConfiguration()
+            ))
                 .argument());
   }
 
@@ -1141,12 +1141,10 @@
         owner.getConfiguration().getGenfilesDirectory(
             owner.getTarget().getLabel().getPackageIdentifier().getRepository()),
         (AspectValue.AspectKey)
-            AspectValue.key(
-                    owner.getLabel(),
-                    owner.getConfiguration(),
-                    owner.getConfiguration(),
-                    creatingAspectFactory,
-                    params)
+            ActionLookupValue.key(AspectValue.createAspectKey(
+                owner.getLabel(), owner.getConfiguration(),
+                new AspectDescriptor(creatingAspectFactory, params), owner.getConfiguration()
+            ))
                 .argument());
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ConfigurationsForTargetsTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ConfigurationsForTargetsTest.java
index 4e258a9..e6bfebc 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ConfigurationsForTargetsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ConfigurationsForTargetsTest.java
@@ -20,6 +20,7 @@
 import com.google.common.base.VerifyException;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.ListMultimap;
@@ -34,6 +35,7 @@
 import com.google.devtools.build.lib.analysis.util.AnalysisTestCase;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.packages.Aspect;
 import com.google.devtools.build.lib.packages.Attribute;
 import com.google.devtools.build.lib.packages.Package;
 import com.google.devtools.build.lib.packages.RuleClassProvider;
@@ -118,7 +120,7 @@
                 env,
                 new SkyframeDependencyResolver(env),
                 (TargetAndConfiguration) skyKey.argument(),
-                null,
+                ImmutableList.<Aspect>of(),
                 ImmutableMap.<Label, ConfigMatchingProvider>of(),
                 stateProvider.lateBoundRuleClassProvider(),
                 stateProvider.lateBoundHostConfig(),
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkAspectsTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkAspectsTest.java
index cebf63d..3e1a552 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkAspectsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkAspectsTest.java
@@ -37,6 +37,7 @@
 import com.google.devtools.build.lib.rules.cpp.CppConfiguration;
 import com.google.devtools.build.lib.rules.java.Jvm;
 import com.google.devtools.build.lib.skyframe.AspectValue;
+import com.google.devtools.build.lib.syntax.SkylarkList;
 import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
 import java.util.Arrays;
@@ -1361,19 +1362,177 @@
     Object names = skylarkProviders.getValue("target_labels");
     assertThat(names).isInstanceOf(SkylarkNestedSet.class);
     assertThat(
-            transform(
-                (SkylarkNestedSet) names,
-                new Function<Object, String>() {
-                  @Nullable
-                  @Override
-                  public String apply(Object o) {
-                    assertThat(o).isInstanceOf(Label.class);
-                    return ((Label) o).getName();
-                  }
-                }))
+        transform(
+              (SkylarkNestedSet) names,
+              new Function<Object, String>() {
+                @Nullable
+                @Override
+                public String apply(Object o) {
+                  assertThat(o).isInstanceOf(Label.class);
+                  return ((Label) o).getName();
+                }
+              }))
         .containsExactly("foo", "bar", "tool");
   }
 
+  /**
+   * Simple straightforward linear aspects-on-aspects.
+   */
+  @Test
+  public void aspectOnAspectLinear() throws Exception {
+    scratch.file(
+        "test/aspect.bzl",
+        "a1p = provider()",
+        "def _a1_impl(target,ctx):",
+        "  return struct(a1p = a1p(text = 'random'))",
+        "a1 = aspect(_a1_impl, attr_aspects = ['dep'])",
+        "a2p = provider()",
+        "def _a2_impl(target,ctx):",
+        "  value = []",
+        "  if hasattr(ctx.rule.attr.dep, 'a2p'):",
+        "     value += ctx.rule.attr.dep.a2p.value",
+        "  if hasattr(target, 'a1p'):",
+        "     value.append(str(target.label) + '=yes')",
+        "  else:",
+        "     value.append(str(target.label) + '=no')",
+        "  return struct(a2p = a2p(value = value))",
+        "a2 = aspect(_a2_impl, attr_aspects = ['dep'])",
+        "def _r1_impl(ctx):",
+        "  pass",
+        "def _r2_impl(ctx):",
+        "  return struct(result = ctx.attr.dep.a2p.value)",
+        "r1 = rule(_r1_impl, attrs = { 'dep' : attr.label(aspects = [a1])})",
+        "r2 = rule(_r2_impl, attrs = { 'dep' : attr.label(aspects = [a2])})"
+    );
+    scratch.file(
+        "test/BUILD",
+        "load(':aspect.bzl', 'r1', 'r2')",
+        "r1(name = 'r0')",
+        "r1(name = 'r1', dep = ':r0')",
+        "r2(name = 'r2', dep = ':r1')"
+    );
+    AnalysisResult analysisResult = update("//test:r2");
+    ConfiguredTarget target = Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
+    SkylarkList result = (SkylarkList) target.get("result");
+
+    // "yes" means that aspect a2 sees a1's providers.
+    assertThat(result).containsExactly("//test:r0=yes", "//test:r1=no");
+  }
+
+  /**
+   * Diamond case.
+   * rule r1 depends or r0 with aspect a1.
+   * rule r2 depends or r0 with aspect a2.
+   * rule rcollect depends on r1, r2 with aspect a3.
+   *
+   * Aspect a3 should be applied twice to target r0: once in [a1, a3] sequence
+   * and once in [a2, a3] sequence.
+   */
+  @Test
+  public void aspectOnAspectDiamond() throws Exception {
+    scratch.file(
+        "test/aspect.bzl",
+        "def _a1_impl(target,ctx):",
+        "  return struct(a1p = 'text from a1')",
+        "a1 = aspect(_a1_impl, attr_aspects = ['deps'])",
+        "",
+        "def _a2_impl(target,ctx):",
+        "  return struct(a2p = 'text from a2')",
+        "a2 = aspect(_a2_impl, attr_aspects = ['deps'])",
+        "",
+        "def _a3_impl(target,ctx):",
+        "  value = []",
+        "  f = ctx.new_file('a3.out')",
+        "  ctx.file_action(f, 'text')",
+        "  for dep in ctx.rule.attr.deps:",
+        "     if hasattr(dep, 'a3p'):",
+        "         value += dep.a3p",
+        "  s = str(target.label) + '='",
+        "  if hasattr(target, 'a1p'):",
+        "     s += 'a1p'",
+        "  if hasattr(target, 'a2p'):",
+        "     s += 'a2p'",
+        "  value.append(s)",
+        "  return struct(a3p = value)",
+        "a3 = aspect(_a3_impl, attr_aspects = ['deps'])",
+        "def _r1_impl(ctx):",
+        "  pass",
+        "def _rcollect_impl(ctx):",
+        "  value = []",
+        "  for dep in ctx.attr.deps:",
+        "     if hasattr(dep, 'a3p'):",
+        "         value += dep.a3p",
+        "  return struct(result = value)",
+        "r1 = rule(_r1_impl, attrs = { 'deps' : attr.label_list(aspects = [a1])})",
+        "r2 = rule(_r1_impl, attrs = { 'deps' : attr.label_list(aspects = [a2])})",
+        "rcollect = rule(_rcollect_impl, attrs = { 'deps' : attr.label_list(aspects = [a3])})"
+    );
+    scratch.file(
+        "test/BUILD",
+        "load(':aspect.bzl', 'r1', 'r2', 'rcollect')",
+        "r1(name = 'r0')",
+        "r1(name = 'r1', deps = [':r0'])",
+        "r2(name = 'r2', deps = [':r0'])",
+        "rcollect(name = 'rcollect', deps = [':r1', ':r2'])"
+    );
+    AnalysisResult analysisResult = update("//test:rcollect");
+    ConfiguredTarget target = Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
+    SkylarkList result = (SkylarkList) target.get("result");
+    assertThat(result).containsExactly(
+        "//test:r0=a1p", "//test:r1=", "//test:r0=a2p", "//test:r2=");
+  }
+
+  /**
+   * Linear with duplicates.
+   * r2_1 depends on r0 with aspect a2.
+   * r1 depends on r2_1 with aspect a1.
+   * r2 depends on r1 with aspect a2.
+   *
+   * There should be just one instance of aspect a2 on r0, and is should *not* see a1.
+   */
+  @Test
+  public void aspectOnAspectLinearDuplicates() throws Exception {
+    scratch.file(
+        "test/aspect.bzl",
+        "a1p = provider()",
+        "def _a1_impl(target,ctx):",
+        "  return struct(a1p = 'a1p')",
+        "a1 = aspect(_a1_impl, attr_aspects = ['dep'])",
+        "a2p = provider()",
+        "def _a2_impl(target,ctx):",
+        "  value = []",
+        "  if hasattr(ctx.rule.attr.dep, 'a2p'):",
+        "     value += ctx.rule.attr.dep.a2p.value",
+        "  if hasattr(target, 'a1p'):",
+        "     value.append(str(target.label) + '=yes')",
+        "  else:",
+        "     value.append(str(target.label) + '=no')",
+        "  return struct(a2p = a2p(value = value))",
+        "a2 = aspect(_a2_impl, attr_aspects = ['dep'])",
+        "def _r1_impl(ctx):",
+        "  pass",
+        "def _r2_impl(ctx):",
+        "  return struct(result = ctx.attr.dep.a2p.value)",
+        "r1 = rule(_r1_impl, attrs = { 'dep' : attr.label(aspects = [a1])})",
+        "r2 = rule(_r2_impl, attrs = { 'dep' : attr.label(aspects = [a2])})"
+    );
+    scratch.file(
+        "test/BUILD",
+        "load(':aspect.bzl', 'r1', 'r2')",
+        "r1(name = 'r0')",
+        "r2(name = 'r2_1', dep = ':r0')",
+        "r1(name = 'r1', dep = ':r2_1')",
+        "r2(name = 'r2', dep = ':r1')"
+    );
+    AnalysisResult analysisResult = update("//test:r2");
+    ConfiguredTarget target = Iterables.getOnlyElement(analysisResult.getTargetsToBuild());
+    SkylarkList result = (SkylarkList) target.get("result");
+    // "yes" means that aspect a2 sees a1's providers.
+    assertThat(result).containsExactly("//test:r0=no", "//test:r1=no", "//test:r2_1=yes");
+  }
+
+
+
   @RunWith(JUnit4.class)
   public static final class WithKeepGoing extends SkylarkAspectsTest {
     @Override