diff --git a/src/main/java/com/google/devtools/build/lib/packages/SelectorList.java b/src/main/java/com/google/devtools/build/lib/packages/SelectorList.java
index 4a2547dbaf3e6a..c9775b8fb5535d 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/SelectorList.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/SelectorList.java
@@ -32,7 +32,8 @@
import net.starlark.java.syntax.TokenKind;
/**
- * An attribute value consisting of a concatenation of native types and selects, e.g:
+ * An attribute value consisting of a concatenation (via the {@code +} operator for lists or the
+ * {@code |} operator for dicts) of native types and selects, e.g:
*
*
* rule(
@@ -113,7 +114,11 @@ static SelectorList concat(Object x, Object y) throws EvalException {
@Override
public SelectorList binaryOp(TokenKind op, Object that, boolean thisLeft) throws EvalException {
- if (op == TokenKind.PLUS) {
+ if (getNativeType(that).equals(Dict.class)) {
+ if (op == TokenKind.PIPE) {
+ return thisLeft ? concat(this, that) : concat(that, this);
+ }
+ } else if (op == TokenKind.PLUS) {
return thisLeft ? concat(this, that) : concat(that, this);
}
return null;
@@ -141,7 +146,7 @@ static SelectorList of(Iterable> values) throws EvalException {
}
if (!canConcatenate(getNativeType(firstValue), getNativeType(value))) {
throw Starlark.errorf(
- "'+' operator applied to incompatible types (%s, %s)",
+ "Cannot combine incompatible types (%s, %s)",
getTypeName(firstValue), getTypeName(value));
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/SelectorValue.java b/src/main/java/com/google/devtools/build/lib/packages/SelectorValue.java
index 414905f4dd0e21..afc78596350fdf 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/SelectorValue.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/SelectorValue.java
@@ -83,10 +83,7 @@ public String toString() {
@Override
public SelectorList binaryOp(TokenKind op, Object that, boolean thisLeft) throws EvalException {
- if (op == TokenKind.PLUS) {
- return thisLeft ? SelectorList.concat(this, that) : SelectorList.concat(that, this);
- }
- return null;
+ return SelectorList.of(this).binaryOp(op, that, thisLeft);
}
@Override
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Type.java b/src/main/java/com/google/devtools/build/lib/packages/Type.java
index f1f4d9cd5e962e..ee9490ddb041f7 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Type.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Type.java
@@ -34,6 +34,7 @@
import java.util.Set;
import java.util.logging.Level;
import javax.annotation.Nullable;
+import net.starlark.java.eval.Dict;
import net.starlark.java.eval.EvalException;
import net.starlark.java.eval.Printer;
import net.starlark.java.eval.Sequence;
@@ -208,9 +209,11 @@ public LabelClass getLabelClass() {
/**
* Implementation of concatenation for this type, as if by {@code elements[0] + ... +
- * elements[n-1]}). Returns null to indicate concatenation isn't supported. This method exists to
- * support deferred additions {@code select + T} for catenable types T such as string, int, and
- * list.
+ * elements[n-1]}) for scalars or lists, or {@code elements[0] | ... | elements[n-1]} for dicts.
+ * Returns null to indicate concatenation isn't supported.
+ *
+ * This method exists to support deferred additions {@code select + T} for catenable types T
+ * such as string, int, list, and deferred unions {@code select | T} for map types T.
*/
public T concat(Iterable elements) {
return null;
@@ -568,6 +571,15 @@ public Map convert(Object x, Object what, Object context)
return ImmutableMap.copyOf(result);
}
+ @Override
+ public Map concat(Iterable