You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/09/05 13:04:36 UTC
[groovy] branch GROOVY_3_0_X updated: GROOVY-10749: STC: closure/lambda/reference parameter(s) as type witness
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_3_0_X by this push:
new 5515c2b9fd GROOVY-10749: STC: closure/lambda/reference parameter(s) as type witness
5515c2b9fd is described below
commit 5515c2b9fd3bd53573f032e637e3dd3c282c94d2
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Sep 4 11:39:21 2022 -0500
GROOVY-10749: STC: closure/lambda/reference parameter(s) as type witness
3_0_X backport
---
.../java/org/codehaus/groovy/ast/Parameter.java | 2 +-
.../transform/stc/StaticTypeCheckingVisitor.java | 53 +++-------------
.../groovy/transform/stc/GenericsSTCTest.groovy | 73 ++++++++++++++++++++++
3 files changed, 84 insertions(+), 44 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/ast/Parameter.java b/src/main/java/org/codehaus/groovy/ast/Parameter.java
index ce49781ab7..bf69a34ecf 100644
--- a/src/main/java/org/codehaus/groovy/ast/Parameter.java
+++ b/src/main/java/org/codehaus/groovy/ast/Parameter.java
@@ -52,7 +52,7 @@ public class Parameter extends AnnotatedNode implements Variable {
}
public String toString() {
- return super.toString() + "[name:" + name + ((type == null) ? "" : " type: " + type.getName()) + ", hasDefaultValue: " + this.hasInitialExpression() + "]";
+ return super.toString() + "[name: " + name + (type == null ? "" : ", type: " + type.toString(false)) + ", hasDefaultValue: " + this.hasInitialExpression() + "]";
}
public String getName() {
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 198b113f4b..11eb97ea20 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -278,7 +278,6 @@ import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isPowe
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isShiftOperation;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isTraitSelf;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isUsingGenericsOrIsArrayUsingGenerics;
-import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isUsingUncheckedGenerics;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isVargs;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isWildcardLeftHandSide;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.lastArgMatchesVarg;
@@ -5518,14 +5517,13 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
* @param samType the type into which the closure is coerced into
* @return SAM type augmented using information from the argument expression
*/
- private static ClassNode convertClosureTypeToSAMType(final Expression expression, final ClassNode closureType, final MethodNode sam, final ClassNode samType, final Map<GenericsTypeName, GenericsType> placeholders) {
+ private static ClassNode convertClosureTypeToSAMType(Expression expression, final ClassNode closureType, final MethodNode sam, final ClassNode samType, final Map<GenericsTypeName, GenericsType> placeholders) {
// use the generics information from Closure to further specify the type
- if (closureType.isUsingGenerics()) {
+ if (isClosureWithType(closureType)) {
ClassNode closureReturnType = closureType.getGenericsTypes()[0].getType();
Parameter[] parameters = sam.getParameters();
- if (parameters.length > 0 && expression instanceof MethodPointerExpression
- && isUsingUncheckedGenerics(closureReturnType)) { // needs resolve
+ if (parameters.length > 0 && expression instanceof MethodPointerExpression) {
MethodPointerExpression mp = (MethodPointerExpression) expression;
MethodNode mn = chooseMethod(mp, () ->
applyGenericsContext(placeholders, extractTypesFromParameters(parameters))
@@ -5541,6 +5539,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
closureReturnType = applyGenericsContext(connections, closureReturnType);
// apply known generics connections to the placeholders of the return type
closureReturnType = applyGenericsContext(placeholders, closureReturnType);
+
+ expression = new ClosureExpression(Arrays.stream(pTypes).map(t -> new Parameter(t,"")).toArray(Parameter[]::new), null);
}
}
@@ -5549,44 +5549,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
// repeat the same for each parameter given in the ClosureExpression
if (parameters.length > 0 && expression instanceof ClosureExpression) {
- List<ClassNode[]> genericsToConnect = new ArrayList<>();
- Parameter[] closureParams = ((ClosureExpression) expression).getParameters();
- ClassNode[] closureParamTypes = expression.getNodeMetaData(CLOSURE_ARGUMENTS);
- if (closureParamTypes == null) closureParamTypes = extractTypesFromParameters(closureParams);
-
- for (int i = 0, n = parameters.length; i < n; i += 1) {
- Parameter parameter = parameters[i];
- if (parameter.getOriginType().isUsingGenerics() && closureParamTypes.length > i) {
- genericsToConnect.add(new ClassNode[]{closureParamTypes[i], parameter.getOriginType()});
- }
- }
- for (ClassNode[] classNodes : genericsToConnect) {
- ClassNode found = classNodes[0];
- ClassNode expected = classNodes[1];
- if (!isAssignableTo(found, expected)) {
- // probably facing a type mismatch
- continue;
- }
- ClassNode generifiedType = GenericsUtils.parameterizeType(found, expected);
- while (expected.isArray()) {
- expected = expected.getComponentType();
- generifiedType = generifiedType.getComponentType();
- }
- if (expected.isGenericsPlaceHolder()) {
- placeholders.put(new GenericsTypeName(expected.getGenericsTypes()[0].getName()), new GenericsType(generifiedType));
- } else {
- GenericsType[] expectedGenericsTypes = expected.getGenericsTypes();
- GenericsType[] foundGenericsTypes = generifiedType.getGenericsTypes();
-
- for (int i = 0, n = expectedGenericsTypes.length; i < n; i += 1) {
- GenericsType type = expectedGenericsTypes[i];
- if (type.isPlaceholder()) {
- String name = type.getName();
- placeholders.put(new GenericsTypeName(name), foundGenericsTypes[i]);
- }
- }
- }
- }
+ ClassNode[] paramTypes = applyGenericsContext(placeholders, extractTypesFromParameters(parameters));
+ int i = 0;
+ // GROOVY-10054, GROOVY-10699, GROOVY-10749, et al.
+ for (Parameter p : getParametersSafe((ClosureExpression) expression))
+ if (!p.isDynamicTyped()) extractGenericsConnections(placeholders, p.getType(), paramTypes[i++]);
}
}
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index f00967f7f5..1d6e8ab3e5 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -719,6 +719,79 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
+ @NotYetImplemented // GROOVY-10646
+ void testReturnTypeInferenceWithMethodGenerics28() {
+ String types = '''
+ class Model {
+ }
+ interface Output<T> {
+ T getT()
+ }
+ abstract class WhereDSL<Type> {
+ abstract Type where()
+ }
+ abstract class Input<T> extends WhereDSL<ReferencesOuterClassTP> {
+ class ReferencesOuterClassTP implements Output<T> {
+ @Override T getT() { return null }
+ }
+ }
+ '''
+ assertScript types + '''
+ void m(Input<Model> input) {
+ def output = input.where()
+ @ASTTest(phase=INSTRUCTION_SELECTION, value={
+ assert node.getNodeMetaData(INFERRED_TYPE).toString(false) == 'Model'
+ })
+ def result = output.getT()
+ }
+ '''
+ assertScript types + '''
+ @FunctionalInterface
+ interface Xform extends java.util.function.Function<Input<Model>, Output<Model>> {
+ }
+
+ void select(Xform xform) {
+ }
+
+ select { input ->
+ def result = input.where()
+ return result // Cannot return value of type Input$ReferencesOuterClassTP for closure expecting Output<Model>
+ }
+ '''
+ }
+
+ // GROOVY-10749
+ void testReturnTypeInferenceWithMethodGenerics29() {
+ String named = 'class Named { String name }'
+
+ for (expr in ['Named.&getName', 'Named::getName', '{Named named -> named.getName()}', '(Named named) -> named.getName()']) {
+ assertScript named + """
+ @ASTTest(phase=INSTRUCTION_SELECTION, value={
+ def type = node.getNodeMetaData(INFERRED_TYPE)
+ assert type.toString(false) == 'java.util.stream.Collector <Named, ?, java.util.Map>'
+ })
+ def collector = java.util.stream.Collectors.groupingBy($expr)
+ """
+ }
+
+ // explicit type args
+ assertScript named + '''
+ @ASTTest(phase=INSTRUCTION_SELECTION, value={
+ def type = node.getNodeMetaData(INFERRED_TYPE)
+ assert type.toString(false) == 'java.util.stream.Collector <Named, ?, java.util.Map>'
+ })
+ def c1 = java.util.stream.Collectors.<Named,String>groupingBy(named -> named.getName())
+ // ^^^^^^^^^^^^^^
+
+ @ASTTest(phase=INSTRUCTION_SELECTION, value={
+ def type = node.getNodeMetaData(INFERRED_TYPE)
+ assert type.toString(false) == 'java.util.stream.Collector <Named, ?, java.util.Map>'
+ })
+ def c2 = java.util.stream.Collectors.<Named,String,Named>toMap(named -> named.getName(), named -> named)
+ // ^^^^^^^^^^^^^^^^^^^^
+ '''
+ }
+
void testDiamondInferrenceFromConstructor1() {
assertScript '''
class Foo<U> {