in src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java [649:747]
public static boolean checkCompatibleAssignmentTypes(final ClassNode left, final ClassNode right, final Expression rightExpression, final boolean allowConstructorCoercion) {
if (isNullConstant(rightExpression)) { // TODO: left is @NonNull
return (!isPrimitiveType(left) || isPrimitiveBoolean(left)); // GROOVY-6577
}
if (left.isArray()) {
ClassNode leftItemType = left.getComponentType();
if (right.isArray()) {
ClassNode rightItemType = right.getComponentType();
return (isPrimitiveType(leftItemType) && !isPrimitiveBoolean(leftItemType))
? isPrimitiveType(rightItemType) // GROOVY-11371: primitive array only
: checkCompatibleAssignmentTypes(leftItemType, rightItemType, rightExpression, false);
}
if (rightExpression instanceof ListExpression) {
return true; // addPrecisionErrors checks values
}
if (GeneralUtils.isOrImplements(right, Collection_TYPE)) {
var elementType = GenericsUtils.parameterizeType(right, Collection_TYPE).getGenericsTypes()[0];
return isObjectType(leftItemType) // Object[] can accept any collection element type(s)
|| (elementType.getLowerBound() == null && isCovariant(extractType(elementType), leftItemType));
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GROOVY-8984: "? super T" is only compatible with an Object[] target
}
if (GeneralUtils.isOrImplements(right, BaseStream_TYPE)) {
var elementType = GenericsUtils.parameterizeType(right, BaseStream_TYPE).getGenericsTypes()[0];
return isObjectType(leftItemType) // Object[] can accept any stream API element type(s)
|| (elementType.getLowerBound() == null && isCovariant(extractType(elementType), getWrapper(leftItemType)));
}
}
ClassNode leftRedirect = left.redirect();
ClassNode rightRedirect = right.redirect();
if (leftRedirect == rightRedirect) return true;
if (leftRedirect == VOID_TYPE) return rightRedirect == void_WRAPPER_TYPE;
if (leftRedirect == void_WRAPPER_TYPE) return rightRedirect == VOID_TYPE;
if (leftRedirect == char_TYPE || leftRedirect == Character_TYPE) {
// GROOVY-10079, GROOVY-10095, GROOVY-10359: assign Character, char or int to char or Character
if (rightRedirect == Character_TYPE || rightRedirect == char_TYPE || rightRedirect == int_TYPE) {
return true;
}
if (rightRedirect == STRING_TYPE) {
return rightExpression instanceof ConstantExpression && rightExpression.getText().length() == 1;
}
} else if (isLongCategory(getUnwrapper(leftRedirect))) {
// byte, int, long or short can be assigned any base number type
if (isNumberType(rightRedirect)) {
return true;
}
} else if (isFloatingCategory(getUnwrapper(leftRedirect))) {
// float or double can be assigned any base number type or BigDecimal
if (isNumberType(rightRedirect) || isBigDecimalType(rightRedirect)) {
return true;
}
} else if (left.isGenericsPlaceHolder()) { // must precede non-final types
return right.getUnresolvedName().charAt(0) != '#' // RHS not adaptable
? left.asGenericsType().isCompatibleWith(right) // GROOVY-7307, GROOVY-9952, GROOVY-11026
: implementsInterfaceOrSubclassOf(leftRedirect, rightRedirect); // GROOVY-10067, GROOVY-10342
} else if (isBigDecimalType(leftRedirect) || Number_TYPE.equals(leftRedirect)) {
// BigDecimal or Number can be assigned any derivative of java.lang.Number
if (isNumberType(rightRedirect) || rightRedirect.isDerivedFrom(Number_TYPE)) {
return true;
}
} else if (isBigIntegerType(leftRedirect)) {
// BigInteger can be assigned byte, char, int, long, short or BigInteger
if (isLongCategory(getUnwrapper(rightRedirect)) || rightRedirect.isDerivedFrom(BigInteger_TYPE)) {
return true;
}
} else if (leftRedirect.isDerivedFrom(Enum_Type)) {
// Enum types can be assigned String or GString (triggers `valueOf` call)
if (rightRedirect == STRING_TYPE || isGStringOrGStringStringLUB(rightRedirect)) {
return true;
}
} else if (isWildcardLeftHandSide(leftRedirect)) {
// Object, String, [Bb]oolean or Class can be assigned any non-null value
return true;
}
// if right is array, map or collection we try invoking the constructor
if (allowConstructorCoercion && isGroovyConstructorCompatible(rightExpression)) {
// TODO: in case of the array we could maybe make a partial check
if (rightRedirect.isArray() && !leftRedirect.isArray()) {
return false;
}
return true;
}
if (implementsInterfaceOrSubclassOf(getWrapper(right), left)) {
return true;
}
if (right.isDerivedFrom(CLOSURE_TYPE) && isSAMType(left)) {
return true;
}
// GROOVY-7316, GROOVY-10256: "Type x = m()" given "def <T> T m()"; T adapts to target
return right.isGenericsPlaceHolder() && right.asGenericsType().isCompatibleWith(left);
}