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 2021/04/04 17:51:00 UTC
[groovy] 01/01: GROOVY-9971: STC: support "Closure c = { ->
return "$v" }"
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY-9971
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 05c160b327dfa7f2489c44e85532121acc930f0e
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Apr 4 12:50:38 2021 -0500
GROOVY-9971: STC: support "Closure<String> c = { -> return "$v" }"
- convert GString to String at the point of return
---
.../groovy/classgen/asm/ClosureWriter.java | 31 ++++++++++++----------
.../transform/stc/StaticTypeCheckingVisitor.java | 25 +++++++++++++----
.../groovy/transform/stc/ClosuresSTCTest.groovy | 15 +++++++++++
.../groovy/transform/stc/GenericsSTCTest.groovy | 24 +----------------
4 files changed, 53 insertions(+), 42 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
index 13e1370..607d6e3 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
@@ -38,6 +38,7 @@ import org.codehaus.groovy.ast.expr.FieldExpression;
import org.codehaus.groovy.ast.expr.TupleExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.tools.GenericsUtils;
import org.codehaus.groovy.classgen.AsmClassGenerator;
import org.codehaus.groovy.classgen.Verifier;
import org.objectweb.asm.MethodVisitor;
@@ -57,6 +58,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_RETURN_TYPE;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
@@ -188,7 +190,6 @@ public class ClosureWriter {
ClassNode classNode = controller.getClassNode();
ClassNode outerClass = controller.getOutermostClass();
String name = genClosureClassName();
- boolean staticMethodOrInStaticClass = controller.isStaticMethod() || classNode.isStaticClass();
Parameter[] parameters = expression.getParameters();
if (parameters == null) {
@@ -204,20 +205,22 @@ public class ClosureWriter {
Parameter[] localVariableParams = getClosureSharedVariables(expression);
removeInitialValues(localVariableParams);
+ // GROOVY-9971: closure return type is mapped to Groovy cast by classgen
+ ClassNode returnType = expression.getNodeMetaData(INFERRED_RETURN_TYPE);
+ if (returnType == null) returnType = ClassHelper.OBJECT_TYPE; // not STC or unknown path
+ else if (returnType.isPrimaryClassNode()) returnType = returnType.getPlainNodeReference();
+ else if (ClassHelper.isPrimitiveType(returnType)) returnType = ClassHelper.getWrapper(returnType);
+ else if (GenericsUtils.hasUnresolvedGenerics(returnType)) returnType = GenericsUtils.nonGeneric(returnType);
+
InnerClassNode answer = new InnerClassNode(classNode, name, modifiers, ClassHelper.CLOSURE_TYPE.getPlainNodeReference());
answer.setEnclosingMethod(controller.getMethodNode());
+ answer.setScriptBody(controller.isInScriptBody());
+ answer.setSourcePosition(expression);
+ answer.setStaticClass(controller.isStaticMethod() || classNode.isStaticClass());
answer.setSynthetic(true);
answer.setUsingGenerics(outerClass.isUsingGenerics());
- answer.setSourcePosition(expression);
- if (staticMethodOrInStaticClass) {
- answer.setStaticClass(true);
- }
- if (controller.isInScriptBody()) {
- answer.setScriptBody(true);
- }
- MethodNode method =
- answer.addMethod("doCall", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, parameters, ClassNode.EMPTY_ARRAY, expression.getCode());
+ MethodNode method = answer.addMethod("doCall", ACC_PUBLIC, returnType, parameters, ClassNode.EMPTY_ARRAY, expression.getCode());
method.setSourcePosition(expression);
VariableScope varScope = expression.getVariableScope();
@@ -229,14 +232,14 @@ public class ClosureWriter {
}
if (parameters.length > 1
|| (parameters.length == 1
- && parameters[0].getType() != null
- && parameters[0].getType() != ClassHelper.OBJECT_TYPE
- && !ClassHelper.OBJECT_TYPE.equals(parameters[0].getType().getComponentType()))) {
+ && parameters[0].getType() != null
+ && !ClassHelper.OBJECT_TYPE.equals(parameters[0].getType())
+ && !ClassHelper.OBJECT_TYPE.equals(parameters[0].getType().getComponentType()))) {
// let's add a typesafe call method
MethodNode call = new MethodNode(
"call",
ACC_PUBLIC,
- ClassHelper.OBJECT_TYPE,
+ returnType,
parameters,
ClassNode.EMPTY_ARRAY,
returnS(callThisX("doCall", args(parameters))));
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 b9fa745..bacb1fd 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -266,6 +266,7 @@ import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isBitO
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isBoolIntrinsicOp;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isClassClassNodeWrappingConcreteType;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isCompareToBoolean;
+import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isGStringOrGStringStringLUB;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isOperationInGroup;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isParameterizedWithGStringOrGStringString;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isParameterizedWithString;
@@ -2216,7 +2217,14 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
protected void addClosureReturnType(final ClassNode returnType) {
- typeCheckingContext.getEnclosingClosure().addReturnType(returnType);
+ TypeCheckingContext.EnclosingClosure enclosingClosure = typeCheckingContext.getEnclosingClosure();
+ if (isGStringOrGStringStringLUB(returnType)
+ && STRING_TYPE.equals(getInferredReturnType(enclosingClosure.getClosureExpression()))) {
+ // GROOVY-9971: convert GString to String at the point of return
+ enclosingClosure.addReturnType(STRING_TYPE);
+ } else {
+ enclosingClosure.addReturnType(returnType);
+ }
}
@Override
@@ -2745,14 +2753,21 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
if (visitClosures && expression instanceof ClosureExpression
|| !visitClosures && !(expression instanceof ClosureExpression)) {
if (i < params.length && visitClosures) {
- Parameter param = params[i];
- checkClosureWithDelegatesTo(receiver, selectedMethod, newArgs, params, expression, param);
+ Parameter target = params[i];
+ ClassNode targetType = target.getType();
+ ClosureExpression source = (ClosureExpression) expression;
+ checkClosureWithDelegatesTo(receiver, selectedMethod, newArgs, params, source, target);
if (selectedMethod instanceof ExtensionMethodNode) {
if (i > 0) {
- inferClosureParameterTypes(receiver, arguments, (ClosureExpression) expression, param, selectedMethod);
+ inferClosureParameterTypes(receiver, arguments, source, target, selectedMethod);
}
} else {
- inferClosureParameterTypes(receiver, newArgs, (ClosureExpression) expression, param, selectedMethod);
+ inferClosureParameterTypes(receiver, newArgs, source, target, selectedMethod);
+ }
+ if (isFunctionalInterface(targetType)) {
+ storeInferredReturnType(source, GenericsUtils.parameterizeSAM(targetType).getV2());
+ } else if (isClosureWithType(targetType)) {
+ storeInferredReturnType(source, getCombinedBoundType(targetType.getGenericsTypes()[0]));
}
}
expression.visit(this);
diff --git a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
index 287465b..b707e77 100644
--- a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
@@ -117,6 +117,21 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase {
'''
}
+ // GROOVY-9971
+ void testClosureReturnTypeInference6() {
+ assertScript '''
+ def m(Closure<String> c) {
+ c.call()
+ }
+ final x = 123
+ Closure<String> c = { -> "x=$x" }
+ String type = c.call().class.name
+ assert type == 'java.lang.String'
+ type = (m { -> "x=$x" }).class.name
+ assert type == 'java.lang.String' // not GStringImpl
+ '''
+ }
+
// GROOVY-5145
void testCollect() {
assertScript '''
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 638d14c..1a7751b 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -1186,7 +1186,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
// GROOVY-5559: related behaviour
- void testGStringVsString1() {
+ void testGStringVsString() {
assertScript '''
int i = 1
@ASTTest(phase=INSTRUCTION_SELECTION, value={
@@ -1197,28 +1197,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- // GROOVY-9971
- void testGStringVsString2() {
- shouldFailWithMessages '''
- class C {
- String apply(Closure<String> code) {
- code.call()
- }
-
- void test() {
- int i = 1
- assert apply { "i=$i" } == 'i=1'
- assert apply { "i=$i".toString() } == 'i=1'
- assert apply { false ? 'abc' : "i=$i" } == 'i=1'
- assert apply { true ? 'abc' : "i=$i".toString() } == 'abc'
- }
- }
- new C().test()
- ''',
- 'Cannot find matching method C#apply(groovy.lang.Closure <groovy.lang.GString>)',
- 'Cannot find matching method C#apply(groovy.lang.Closure <java.io.Serializable>)'
- }
-
// GROOVY-5594
void testMapEntryUsingPropertyNotation() {
assertScript '''