Change allowedRuleClasses/mandatoryProviders semantics to "either-or" instead of "and".

Also allow native rules to require declared providers on their
dependencies.

--
MOS_MIGRATED_REVID=135454750
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/AbstractConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/AbstractConfiguredTarget.java
index 718c67b..107e64b 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/AbstractConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/AbstractConfiguredTarget.java
@@ -25,6 +25,7 @@
 import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.packages.PackageSpecification;
 import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
+import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier;
 import com.google.devtools.build.lib.packages.Target;
 import com.google.devtools.build.lib.syntax.ClassObject;
 import com.google.devtools.build.lib.syntax.EvalException;
@@ -77,6 +78,15 @@
     return configuration;
   }
 
+  @Nullable
+  @Override
+  public Object get(SkylarkProviderIdentifier id) {
+    if (id.isLegacy()) {
+      return get(id.getLegacyId());
+    }
+    return get(id.getKey());
+  }
+
   @Override
   public Label getLabel() {
     return getTarget().getLabel();
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/EnvironmentGroupConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/EnvironmentGroupConfiguredTarget.java
index 3465a1d..4a345f1 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/EnvironmentGroupConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/EnvironmentGroupConfiguredTarget.java
@@ -15,7 +15,10 @@
 package com.google.devtools.build.lib.analysis;
 
 import com.google.devtools.build.lib.packages.EnvironmentGroup;
+import com.google.devtools.build.lib.packages.SkylarkClassObject;
+import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor.Key;
 import com.google.devtools.build.lib.util.Preconditions;
+import javax.annotation.Nullable;
 
 /**
  * Dummy ConfiguredTarget for environment groups. Contains no functionality, since
@@ -37,4 +40,11 @@
     // No providers.
     return null;
   }
+
+  @Nullable
+  @Override
+  public SkylarkClassObject get(Key providerKey) {
+    // No providers.
+    return null;
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/FileConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/FileConfiguredTarget.java
index f55298f..3009d1d 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/FileConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/FileConfiguredTarget.java
@@ -19,9 +19,12 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.packages.FileTarget;
+import com.google.devtools.build.lib.packages.SkylarkClassObject;
+import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor.Key;
 import com.google.devtools.build.lib.rules.fileset.FilesetProvider;
 import com.google.devtools.build.lib.rules.test.InstrumentedFilesProvider;
 import com.google.devtools.build.lib.util.FileType;
+import javax.annotation.Nullable;
 
 /**
  * A ConfiguredTarget for a source FileTarget.  (Generated files use a
@@ -79,4 +82,10 @@
   public Object get(String providerKey) {
     return null;
   }
+
+  @Nullable
+  @Override
+  public SkylarkClassObject get(Key providerKey) {
+    return null;
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/MergedConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/MergedConfiguredTarget.java
index 4c03666..9a5317f 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/MergedConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/MergedConfiguredTarget.java
@@ -14,9 +14,12 @@
 package com.google.devtools.build.lib.analysis;
 
 import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.packages.SkylarkClassObject;
+import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor.Key;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import javax.annotation.Nullable;
 
 /**
  * A single dependency with its configured target and aspects merged together.
@@ -40,6 +43,12 @@
     return getProvider(SkylarkProviders.class).getValue(providerKey);
   }
 
+  @Nullable
+  @Override
+  public SkylarkClassObject get(Key providerKey) {
+    return getProvider(SkylarkProviders.class).getDeclaredProvider(providerKey);
+  }
+
   @Override
   public <P extends TransitiveInfoProvider> P getProvider(Class<P> providerClass) {
     AnalysisUtils.checkProvider(providerClass);
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/PackageGroupConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/PackageGroupConfiguredTarget.java
index 549932d..c8b26b9 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/PackageGroupConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/PackageGroupConfiguredTarget.java
@@ -20,7 +20,10 @@
 import com.google.devtools.build.lib.events.Event;
 import com.google.devtools.build.lib.packages.PackageGroup;
 import com.google.devtools.build.lib.packages.PackageSpecification;
+import com.google.devtools.build.lib.packages.SkylarkClassObject;
+import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor.Key;
 import com.google.devtools.build.lib.util.Preconditions;
+import javax.annotation.Nullable;
 
 /**
  * Dummy ConfiguredTarget for package groups. Contains no functionality, since
@@ -69,4 +72,11 @@
     // No providers.
     return null;
   }
+
+  @Nullable
+  @Override
+  public SkylarkClassObject get(Key providerKey) {
+    // No providers.
+    return null;
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTarget.java
index 2879236..094b10f 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTarget.java
@@ -21,6 +21,8 @@
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.packages.OutputFile;
 import com.google.devtools.build.lib.packages.Rule;
+import com.google.devtools.build.lib.packages.SkylarkClassObject;
+import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import com.google.devtools.build.lib.util.Preconditions;
 import javax.annotation.Nullable;
 
@@ -108,6 +110,15 @@
     return getProvider(SkylarkProviders.class).getValue(providerKey);
   }
 
+  /**
+   * Returns a declared provider provided by this target. Only meant to use from Skylark.
+   */
+  @Override
+  public SkylarkClassObject get(SkylarkClassObjectConstructor.Key providerKey) {
+    return getProvider(SkylarkProviders.class).getDeclaredProvider(providerKey);
+  }
+
+
   @Override
   public final Rule getTarget() {
     return (Rule) super.getTarget();
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
index 49f1c92..5e6d506 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
@@ -70,6 +70,7 @@
 import com.google.devtools.build.lib.packages.RuleClass;
 import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
 import com.google.devtools.build.lib.packages.RuleErrorConsumer;
+import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier;
 import com.google.devtools.build.lib.packages.Target;
 import com.google.devtools.build.lib.packages.TargetUtils;
 import com.google.devtools.build.lib.rules.AliasProvider;
@@ -88,6 +89,7 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -1788,16 +1790,17 @@
     }
 
     private String getMissingMandatoryProviders(ConfiguredTarget prerequisite, Attribute attribute){
-      List<ImmutableSet<String>> mandatoryProvidersList = attribute.getMandatoryProvidersList();
+      ImmutableList<ImmutableSet<SkylarkProviderIdentifier>> mandatoryProvidersList
+          = attribute.getMandatoryProvidersList();
       if (mandatoryProvidersList.isEmpty()) {
         return null;
       }
       List<List<String>> missingProvidersList = new ArrayList<>();
-      for (ImmutableSet<String> providers : mandatoryProvidersList) {
+      for (ImmutableSet<SkylarkProviderIdentifier> providers : mandatoryProvidersList) {
         List<String> missing = new ArrayList<>();
-        for (String provider : providers) {
+        for (SkylarkProviderIdentifier provider : providers) {
           if (prerequisite.get(provider) == null) {
-            missing.add(provider);
+            missing.add(provider.toString());
           }
         }
         if (missing.isEmpty()) {
@@ -1864,30 +1867,36 @@
     private void validateRuleDependency(ConfiguredTarget prerequisite, Attribute attribute) {
       Target prerequisiteTarget = prerequisite.getTarget();
       RuleClass ruleClass = ((Rule) prerequisiteTarget).getRuleClassObject();
-      HashSet<String> unfulfilledRequirements = new HashSet<>();
+      String notAllowedRuleClassesMessage = null;
 
       if (attribute.getAllowedRuleClassesPredicate() != Predicates.<RuleClass>alwaysTrue()) {
         if (attribute.getAllowedRuleClassesPredicate().apply(ruleClass)) {
+          // prerequisite has an allowed rule class => accept.
           return;
         }
-        unfulfilledRequirements.add(badPrerequisiteMessage(prerequisiteTarget.getTargetKind(),
-            prerequisite, "expected " + attribute.getAllowedRuleClassesPredicate(), false));
+        // remember that the rule class that was not allowed;
+        // but maybe prerequisite provides required providers? do not reject yet.
+        notAllowedRuleClassesMessage =
+            badPrerequisiteMessage(
+                prerequisiteTarget.getTargetKind(),
+                prerequisite,
+                "expected " + attribute.getAllowedRuleClassesPredicate(),
+                false);
       }
 
-      if (attribute.getAllowedRuleClassesWarningPredicate()
-          != Predicates.<RuleClass>alwaysTrue()) {
+      if (attribute.getAllowedRuleClassesWarningPredicate() != Predicates.<RuleClass>alwaysTrue()) {
         Predicate<RuleClass> warningPredicate = attribute.getAllowedRuleClassesWarningPredicate();
         if (warningPredicate.apply(ruleClass)) {
           reportBadPrerequisite(attribute, prerequisiteTarget.getTargetKind(), prerequisite,
               "expected " + attribute.getAllowedRuleClassesPredicate(), true);
+          // prerequisite has a rule class allowed with a warning => accept, emitting a warning.
           return;
         }
       }
 
-      // This condition is required; getMissingMandatory[Native]Providers() would be vacuously true
-      // if no providers were mandatory (thus, none are missing), which would cause an early return
-      // below without emitting the error message about the not-allowed rule class if that
-      // requirement was unfulfilled.
+      // If we got there, we have no allowed rule class.
+      // If mandatory providers are specified, try them.
+      Set<String> unfulfilledRequirements = new LinkedHashSet<>();
       if (!attribute.getMandatoryNativeProvidersList().isEmpty()
           || !attribute.getMandatoryProvidersList().isEmpty()) {
         boolean hadAllMandatoryProviders = true;
@@ -1909,10 +1918,16 @@
         }
 
         if (hadAllMandatoryProviders) {
+          // all mandatory providers are present => accept.
           return;
         }
       }
 
+      // not allowed rule class and some mandatory providers missing => reject.
+      if (notAllowedRuleClassesMessage != null) {
+        unfulfilledRequirements.add(notAllowedRuleClassesMessage);
+      }
+
       if (!unfulfilledRequirements.isEmpty()) {
         attributeError(
             attribute.getName(), StringUtil.joinEnglishList(unfulfilledRequirements, "and"));
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoCollection.java b/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoCollection.java
index d1a9ece..7fd5d61 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoCollection.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoCollection.java
@@ -16,6 +16,9 @@
 
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
 import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.packages.SkylarkClassObject;
+import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
+import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
 import com.google.devtools.build.lib.syntax.SkylarkIndexable;
@@ -78,4 +81,20 @@
    * The transitive information has to have been added using the Skylark framework.
    */
   @Nullable Object get(String providerKey);
+
+  /**
+   * Returns the declared provider requested, or null, if the information is not found.
+   * The transitive information has to have been added using the Skylark framework.
+   */
+  @Nullable SkylarkClassObject get(SkylarkClassObjectConstructor.Key providerKey);
+
+  /**
+   * Returns the provider defined in Skylark, or null, if the information is not found.
+   * The transitive information has to have been added using the Skylark framework.
+   *
+   * This method dispatches to either {@link #get(SkylarkClassObjectConstructor.Key)} or
+   * {@link #get(String)} depending on whether {@link SkylarkProviderIdentifier} is for
+   * legacy or for declared provider.
+   */
+  @Nullable Object get(SkylarkProviderIdentifier id);
 }