RELNOTES: Fix select condition intersections.
PiperOrigin-RevId: 153531483
diff --git a/src/main/java/com/google/devtools/build/docgen/templates/be/functions.vm b/src/main/java/com/google/devtools/build/docgen/templates/be/functions.vm
index d702b0e..11a2396 100644
--- a/src/main/java/com/google/devtools/build/docgen/templates/be/functions.vm
+++ b/src/main/java/com/google/devtools/build/docgen/templates/be/functions.vm
@@ -642,12 +642,12 @@
<ul>
<li>Exactly one condition is selected on any invocation.
</li>
- <li>If two conditions match and one is a specialization of the other
- (i.e. it matches on the same flags as the other plus additional ones),
- the specialization takes precedence.
+ <li>If multiple conditions match and one is a specialization of the others
+ (i.e. it matches on the same flags as any of the others plus additional
+ ones), the specialization takes precedence.
</li>
- <li>If two conditions match and neither is a specialization of the other,
- Bazel fails with an error.
+ <li>If multiple conditions match and one is not a specialization of all the
+ others, Bazel fails with an error.
</li>
<li>The special pseudo-label <code>//conditions:default</code> is
considered to match if no other condition matches. If this condition
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredAttributeMapper.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredAttributeMapper.java
index fcde7ab..2c69504 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredAttributeMapper.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredAttributeMapper.java
@@ -18,6 +18,7 @@
import com.google.common.base.Predicates;
import com.google.common.base.Verify;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.packages.AbstractAttributeMapper;
@@ -30,8 +31,9 @@
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.util.Preconditions;
-
import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -140,7 +142,7 @@
private <T> ConfigKeyAndValue<T> resolveSelector(String attributeName, Selector<T> selector)
throws EvalException {
- ConfigMatchingProvider matchingCondition = null;
+ Map<ConfigMatchingProvider, ConfigKeyAndValue<T>> matchingConditions = new LinkedHashMap<>();
Set<Label> conditionLabels = new LinkedHashSet<>();
ConfigKeyAndValue<T> matchingResult = null;
@@ -160,25 +162,44 @@
conditionLabels.add(curCondition.label());
if (curCondition.matches()) {
- if (matchingCondition == null || curCondition.refines(matchingCondition)) {
- // A match is valid if either this is the *only* condition that matches or this is a
- // more "precise" specification of another matching condition (in which case we choose
- // the most precise one).
- matchingCondition = curCondition;
- matchingResult = new ConfigKeyAndValue<>(selectorKey, entry.getValue());
- } else if (matchingCondition.refines(curCondition)) {
- // The originally matching conditions is more precise, so keep that one.
- } else {
- throw new EvalException(rule.getAttributeLocation(attributeName),
- "Both " + matchingCondition.label() + " and " + curCondition.label()
- + " match configurable attribute \"" + attributeName + "\" in " + getLabel()
- + ". Multiple matches are not allowed unless one is a specialization of the other");
+ // We keep track of all matches which are more precise than any we have found so far.
+ // Therefore, we remove any previous matches which are strictly less precise than this
+ // one, and only add this one if none of the previous matches are more precise.
+ // It is an error if we do not end up with only one most-precise match.
+ boolean suppressed = false;
+ Iterator<ConfigMatchingProvider> it = matchingConditions.keySet().iterator();
+ while (it.hasNext()) {
+ ConfigMatchingProvider existingMatch = it.next();
+ if (curCondition.refines(existingMatch)) {
+ it.remove();
+ } else if (existingMatch.refines(curCondition)) {
+ suppressed = true;
+ break;
+ }
+ }
+ if (!suppressed) {
+ matchingConditions.put(
+ curCondition, new ConfigKeyAndValue<>(selectorKey, entry.getValue()));
}
}
}
+ if (matchingConditions.size() > 1) {
+ throw new EvalException(
+ rule.getAttributeLocation(attributeName),
+ "Illegal ambiguous match on configurable attribute \""
+ + attributeName
+ + "\" in "
+ + getLabel()
+ + ":\n"
+ + Joiner.on("\n").join(matchingConditions.keySet())
+ + "\nMultiple matches are not allowed unless one is unambiguously more specialized.");
+ } else if (matchingConditions.size() == 1) {
+ matchingResult = Iterables.getOnlyElement(matchingConditions.values());
+ }
+
// If nothing matched, choose the default condition.
- if (matchingCondition == null) {
+ if (matchingResult == null) {
if (!selector.hasDefault()) {
String noMatchMessage =
"Configurable attribute \"" + attributeName + "\" doesn't match this configuration";
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/ConfigMatchingProvider.java b/src/main/java/com/google/devtools/build/lib/analysis/config/ConfigMatchingProvider.java
index 2d7ccd2..586c462 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/ConfigMatchingProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/ConfigMatchingProvider.java
@@ -97,4 +97,9 @@
return true;
}
+
+ /** Format this provider as its label. */
+ public String toString() {
+ return label.toString();
+ }
}