Skip to content

Commit 61287d6

Browse files
committed
GROOVY-10271, GROOVY-10272: STC: process closure in ternary expression
3_0_X backport
1 parent e090327 commit 61287d6

File tree

2 files changed

+95
-41
lines changed

2 files changed

+95
-41
lines changed

src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

+49-40
Original file line numberDiff line numberDiff line change
@@ -768,11 +768,7 @@ public void visitBinaryExpression(final BinaryExpression expression) {
768768
} else {
769769
lType = getOriginalDeclarationType(leftExpression);
770770

771-
if (isFunctionalInterface(lType)) {
772-
processFunctionalInterfaceAssignment(lType, rightExpression);
773-
} else if (isClosureWithType(lType) && rightExpression instanceof ClosureExpression) {
774-
storeInferredReturnType(rightExpression, getCombinedBoundType(lType.getGenericsTypes()[0]));
775-
}
771+
applyTargetType(lType, rightExpression);
776772
}
777773
rightExpression.visit(this);
778774
}
@@ -908,21 +904,28 @@ private void validateResourceInARM(final BinaryExpression expression, final Clas
908904
}
909905
}
910906

911-
private void processFunctionalInterfaceAssignment(final ClassNode lhsType, final Expression rhsExpression) {
912-
if (rhsExpression instanceof ClosureExpression) {
913-
inferParameterAndReturnTypesOfClosureOnRHS(lhsType, (ClosureExpression) rhsExpression);
914-
} else if (rhsExpression instanceof MapExpression) { // GROOVY-7141
915-
List<MapEntryExpression> spec = ((MapExpression) rhsExpression).getMapEntryExpressions();
916-
if (spec.size() == 1 && spec.get(0).getValueExpression() instanceof ClosureExpression
917-
&& findSAM(lhsType).getName().equals(spec.get(0).getKeyExpression().getText())) {
918-
inferParameterAndReturnTypesOfClosureOnRHS(lhsType, (ClosureExpression) spec.get(0).getValueExpression());
907+
private void applyTargetType(final ClassNode target, final Expression source) {
908+
if (isClosureWithType(target)) {
909+
if (source instanceof ClosureExpression) {
910+
GenericsType returnType = target.getGenericsTypes()[0];
911+
storeInferredReturnType(source, getCombinedBoundType(returnType));
919912
}
920-
} else if (rhsExpression instanceof MethodReferenceExpression) {
921-
LambdaExpression lambdaExpression = constructLambdaExpressionForMethodReference(lhsType, (MethodReferenceExpression) rhsExpression);
913+
} else if (isFunctionalInterface(target)) {
914+
if (source instanceof ClosureExpression) {
915+
inferParameterAndReturnTypesOfClosureOnRHS(target, (ClosureExpression) source);
916+
} else if (source instanceof MapExpression) { // GROOVY-7141
917+
List<MapEntryExpression> spec = ((MapExpression) source).getMapEntryExpressions();
918+
if (spec.size() == 1 && spec.get(0).getValueExpression() instanceof ClosureExpression
919+
&& findSAM(target).getName().equals(spec.get(0).getKeyExpression().getText())) {
920+
inferParameterAndReturnTypesOfClosureOnRHS(target, (ClosureExpression) spec.get(0).getValueExpression());
921+
}
922+
} else if (source instanceof MethodReferenceExpression) {
923+
LambdaExpression lambda = constructLambdaExpressionForMethodReference(target, (MethodReferenceExpression) source);
922924

923-
inferParameterAndReturnTypesOfClosureOnRHS(lhsType, lambdaExpression);
924-
rhsExpression.putNodeMetaData(CONSTRUCTED_LAMBDA_EXPRESSION, lambdaExpression);
925-
rhsExpression.putNodeMetaData(CLOSURE_ARGUMENTS, Arrays.stream(lambdaExpression.getParameters()).map(Parameter::getType).toArray(ClassNode[]::new));
925+
inferParameterAndReturnTypesOfClosureOnRHS(target, lambda);
926+
source.putNodeMetaData(CONSTRUCTED_LAMBDA_EXPRESSION, lambda);
927+
source.putNodeMetaData(CLOSURE_ARGUMENTS, extractTypesFromParameters(lambda.getParameters()));
928+
}
926929
}
927930
}
928931

@@ -1952,16 +1955,12 @@ public void visitField(final FieldNode node) {
19521955
}
19531956
}
19541957

1955-
private void visitInitialExpression(final Expression value, final Expression target, final ASTNode position) {
1958+
private void visitInitialExpression(final Expression value, final Expression target, final ASTNode origin) {
19561959
if (value != null) {
19571960
ClassNode lType = target.getType();
1958-
if (isFunctionalInterface(lType)) { // GROOVY-9977
1959-
processFunctionalInterfaceAssignment(lType, value);
1960-
} else if (isClosureWithType(lType) && value instanceof ClosureExpression) {
1961-
storeInferredReturnType(value, getCombinedBoundType(lType.getGenericsTypes()[0]));
1962-
}
1961+
applyTargetType(lType, value); // GROOVY-9977
19631962

1964-
typeCheckingContext.pushEnclosingBinaryExpression(assignX(target, value, position));
1963+
typeCheckingContext.pushEnclosingBinaryExpression(assignX(target, value, origin));
19651964

19661965
value.visit(this);
19671966
ClassNode rType = getType(value);
@@ -4215,18 +4214,14 @@ public void visitArrayExpression(final ArrayExpression expression) {
42154214

42164215
@Override
42174216
public void visitCastExpression(final CastExpression expression) {
4218-
ClassNode type = expression.getType();
4219-
Expression target = expression.getExpression();
4220-
if (isFunctionalInterface(type)) { // GROOVY-9997
4221-
processFunctionalInterfaceAssignment(type, target);
4222-
} else if (isClosureWithType(type) && target instanceof ClosureExpression) {
4223-
storeInferredReturnType(target, getCombinedBoundType(type.getGenericsTypes()[0]));
4224-
}
4217+
ClassNode target = expression.getType();
4218+
Expression source = expression.getExpression();
4219+
applyTargetType(target, source); // GROOVY-9997
42254220

4226-
target.visit(this);
4221+
source.visit(this);
42274222

4228-
if (!expression.isCoerce() && !checkCast(type, target) && !isDelegateOrOwnerInClosure(target)) {
4229-
addStaticTypeError("Inconvertible types: cannot cast " + prettyPrintType(getType(target)) + " to " + prettyPrintType(type), expression);
4223+
if (!expression.isCoerce() && !checkCast(target, source) && !isDelegateOrOwnerInClosure(source)) {
4224+
addStaticTypeError("Inconvertible types: cannot cast " + prettyPrintType(getType(source)) + " to " + prettyPrintType(target), expression);
42304225
}
42314226
}
42324227

@@ -4273,12 +4268,10 @@ public void visitTernaryExpression(final TernaryExpression expression) {
42734268
}
42744269
Expression trueExpression = expression.getTrueExpression();
42754270
ClassNode typeOfTrue = findCurrentInstanceOfClass(trueExpression, null);
4276-
trueExpression.visit(this);
4277-
if (typeOfTrue == null) typeOfTrue = getType(trueExpression);
4271+
typeOfTrue = Optional.ofNullable(typeOfTrue).orElse(visitValueExpression(trueExpression));
42784272
typeCheckingContext.popTemporaryTypeInfo(); // instanceof doesn't apply to false branch
42794273
Expression falseExpression = expression.getFalseExpression();
4280-
falseExpression.visit(this);
4281-
ClassNode typeOfFalse = getType(falseExpression);
4274+
ClassNode typeOfFalse = visitValueExpression(falseExpression);
42824275

42834276
ClassNode resultType;
42844277
if (isNullConstant(trueExpression) && isNullConstant(falseExpression)) { // GROOVY-5523
@@ -4298,6 +4291,18 @@ && isOrImplements(typeOfFalse, typeOfTrue))) { // List/Collection/Iterable : []
42984291
popAssignmentTracking(oldTracker);
42994292
}
43004293

4294+
/**
4295+
* @param expr true or false branch of ternary expression
4296+
* @return the inferred type of {@code expr}
4297+
*/
4298+
private ClassNode visitValueExpression(final Expression expr) {
4299+
if (expr instanceof ClosureExpression) {
4300+
applyTargetType(checkForTargetType(expr, null), expr);
4301+
}
4302+
expr.visit(this);
4303+
return getType(expr);
4304+
}
4305+
43014306
/**
43024307
* @param expr true or false branch of ternary expression
43034308
* @param type the inferred type of {@code expr}
@@ -4322,6 +4327,10 @@ && isTypeSource(expr, enclosingMethod)) {
43224327
targetType = enclosingMethod.getReturnType();
43234328
}
43244329

4330+
if (expr instanceof ClosureExpression) { // GROOVY-10271, GROOVY-10272
4331+
return isSAMType(targetType) ? targetType : type;
4332+
}
4333+
43254334
if (targetType == null)
43264335
targetType = type.getPlainNodeReference();
43274336
if (type == UNKNOWN_PARAMETER_TYPE) return targetType;
@@ -4351,7 +4360,7 @@ && missesGenericsTypes(resultType)
43514360
// GROOVY-6126, GROOVY-6558, GROOVY-6564, et al.
43524361
if (!targetType.isGenericsPlaceHolder()) return targetType;
43534362
} else {
4354-
// GROOVY-5640, GROOVY-9033, GROOVY-10220, GROOVY-10235, GROOVY-10688, et al.
4363+
// GROOVY-5640, GROOVY-9033, GROOVY-10220, GROOVY-10235, GROOVY-10688, GROOVY-11192, et al.
43554364
Map<GenericsTypeName, GenericsType> gt = new HashMap<>();
43564365
ClassNode sc = resultType;
43574366
for (;;) {

src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy

+46-1
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,53 @@ class TernaryOperatorSTCTest extends StaticTypeCheckingTestCase {
180180
'''
181181
}
182182

183+
// GROOVY-10688
184+
void testTypeParameterTypeParameter3() {
185+
assertScript '''
186+
class A<T,U> {
187+
}
188+
<T> void test(
189+
A<Double, ? extends T> x) {
190+
A<Double, ? extends T> y = x
191+
A<Double, ? extends T> z = true ? y : x
192+
}
193+
test(null)
194+
'''
195+
}
196+
197+
// GROOVY-10271
198+
void testFunctionalInterfaceTarget1() {
199+
for (flag in ['true', 'false']) {
200+
assertScript """import java.util.function.Supplier
201+
202+
Supplier<Integer> x = { -> 1 }
203+
Supplier<Integer> y = $flag ? x : { -> 2 }
204+
205+
assert y.get() == ($flag ? 1 : 2)
206+
"""
207+
}
208+
}
209+
210+
// GROOVY-10272
211+
void testFunctionalInterfaceTarget2() {
212+
assertScript '''
213+
import java.util.function.Function
214+
215+
Function<Integer, Long> x
216+
if (true) {
217+
x = { a -> a.longValue() }
218+
} else {
219+
x = { Integer b -> (Long)b }
220+
}
221+
assert x.apply(42) == 42L
222+
223+
Function<Integer, Long> y = (true ? { a -> a.longValue() } : { Integer b -> (Long)b })
224+
assert y.apply(42) == 42L
225+
'''
226+
}
227+
183228
// GROOVY-10701
184-
void testFunctionalInterfaceTarget() {
229+
void testFunctionalInterfaceTarget3() {
185230
for (type in ['Function<T,T>', 'UnaryOperator<T>']) {
186231
assertScript """import java.util.function.*
187232

0 commit comments

Comments
 (0)