Skip to content

Commit cf4c88a

Browse files
committed
GROOVY-6232, GROOVY-9956
1 parent 9d28ea6 commit cf4c88a

File tree

7 files changed

+200
-0
lines changed

7 files changed

+200
-0
lines changed

base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/TypeCheckedTests.java

+91
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,26 @@ public void testTypeChecked10() {
234234
runConformTest(sources, "");
235235
}
236236

237+
@Test
238+
public void testTypeChecked6232() {
239+
//@formatter:off
240+
String[] sources = {
241+
"Main.groovy",
242+
"class C<T> {\n" +
243+
" C(T x, T y) {\n" +
244+
" }\n" +
245+
"}\n" +
246+
"@groovy.transform.TypeChecked\n" +
247+
"void test() {\n" +
248+
" C<Object> c = new C<>('a', new Object())\n" +
249+
"}\n" +
250+
"test()\n",
251+
};
252+
//@formatter:on
253+
254+
runConformTest(sources, "");
255+
}
256+
237257
@Test
238258
public void testTypeChecked6786() {
239259
//@formatter:off
@@ -976,4 +996,75 @@ public void testTypeChecked9953() {
976996

977997
runConformTest(sources, "");
978998
}
999+
1000+
@Test
1001+
public void testTypeChecked9956() {
1002+
//@formatter:off
1003+
String[] sources = {
1004+
"Main.groovy",
1005+
"@groovy.transform.TupleConstructor\n" +
1006+
"class C<Y> {\n" +
1007+
" Y p\n" +
1008+
"}\n" +
1009+
"interface I { }\n" +
1010+
"class D implements I { }\n" +
1011+
"@groovy.transform.TypeChecked\n" +
1012+
"void test() {\n" +
1013+
" C<I> ci = new C<>(new D())\n" +
1014+
"}\n" +
1015+
"test()\n",
1016+
};
1017+
//@formatter:on
1018+
1019+
runConformTest(sources, "");
1020+
}
1021+
1022+
@Test
1023+
public void testTypeChecked9956a() {
1024+
//@formatter:off
1025+
String[] sources = {
1026+
"Main.groovy",
1027+
"abstract class A<X> {\n" +
1028+
"}\n" +
1029+
"@groovy.transform.TupleConstructor\n" +
1030+
"class C<Y> extends A<Y> {\n" +
1031+
" Y p\n" +
1032+
"}\n" +
1033+
"interface I { }\n" +
1034+
"class D implements I { }\n" +
1035+
"@groovy.transform.TypeChecked\n" +
1036+
"void test() {\n" +
1037+
" A<I> ai = new C<>(new D())\n" +
1038+
"}\n" +
1039+
"test()\n",
1040+
};
1041+
//@formatter:on
1042+
1043+
runConformTest(sources, "");
1044+
}
1045+
1046+
@Test
1047+
public void testTypeChecked9956b() {
1048+
//@formatter:off
1049+
String[] sources = {
1050+
"Main.groovy",
1051+
"abstract class A<X> {\n" +
1052+
"}\n" +
1053+
"class C<Y> extends A<Y> {\n" +
1054+
"}\n" +
1055+
"@groovy.transform.TypeChecked\n" +
1056+
"void test() {\n" +
1057+
" A<String> ax = new C<Number>()\n" +
1058+
"}\n",
1059+
};
1060+
//@formatter:on
1061+
1062+
runNegativeTest(sources,
1063+
"----------\n" +
1064+
"1. ERROR in Main.groovy (at line 7)\n" +
1065+
"\tA<String> ax = new C<Number>()\n" +
1066+
"\t ^^^^^^^^^^^^^^^\n" +
1067+
"Groovy:[Static type checking] - Incompatible generic argument types. Cannot assign C <Number> to: A <String>\n" +
1068+
"----------\n");
1069+
}
9791070
}

base/org.codehaus.groovy25/src/org/codehaus/groovy/ast/GenericsType.java

+22
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,14 @@ private boolean compareGenericsWithBound(final ClassNode classNode, final ClassN
377377
// class node are not parameterized. This means that we must create a
378378
// new class node with the parameterized types that the current class node
379379
// has defined.
380+
/* GRECLIPSE edit
380381
ClassNode node = GenericsUtils.parameterizeType(classNode, anInterface);
382+
*/
383+
ClassNode node = anInterface;
384+
if (node.getGenericsTypes() != null) {
385+
node = GenericsUtils.parameterizeType(classNode, node);
386+
}
387+
// GRECLIPSE end
381388
return compareGenericsWithBound(node, bound);
382389
}
383390
}
@@ -395,7 +402,20 @@ private boolean compareGenericsWithBound(final ClassNode classNode, final ClassN
395402
if (success) return true;
396403
}
397404
}
405+
/* GRECLIPSE edit
398406
return compareGenericsWithBound(getParameterizedSuperClass(classNode), bound);
407+
*/
408+
if (classNode.equals(ClassHelper.OBJECT_TYPE)) {
409+
return false;
410+
}
411+
ClassNode superClass = classNode.getUnresolvedSuperClass();
412+
if (superClass == null) {
413+
superClass = ClassHelper.OBJECT_TYPE;
414+
} else if (superClass.getGenericsTypes() != null) {
415+
superClass = GenericsUtils.parameterizeType(classNode, superClass);
416+
}
417+
return compareGenericsWithBound(superClass, bound);
418+
// GRECLIPSE end
399419
}
400420
GenericsType[] cnTypes = classNode.getGenericsTypes();
401421
if (cnTypes==null && classNode.isRedirectNode()) cnTypes=classNode.redirect().getGenericsTypes();
@@ -539,6 +559,7 @@ private boolean compareGenericsWithBound(final ClassNode classNode, final ClassN
539559
* @param classNode the class for which we want to return the parameterized superclass
540560
* @return the parameterized superclass
541561
*/
562+
/* GRECLIPSE Edit
542563
private static ClassNode getParameterizedSuperClass(ClassNode classNode) {
543564
if (ClassHelper.OBJECT_TYPE.equals(classNode)) return null;
544565
ClassNode superClass = classNode.getUnresolvedSuperClass();
@@ -564,6 +585,7 @@ private static ClassNode getParameterizedSuperClass(ClassNode classNode) {
564585
}
565586
return superClass;
566587
}
588+
*/
567589

568590
/**
569591
* Represents GenericsType name

base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

+14
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,20 @@ protected void inferDiamondType(final ConstructorCallExpression cce, final Class
11401140
ClassNode type = GenericsUtils.parameterizeType(cceType, cceType);
11411141
type = inferReturnTypeGenerics(type, constructor, argumentListExpression);
11421142
if (type.isUsingGenerics()) {
1143+
// GROOVY-6232, GROOVY-9956: if cce not assignment compatible, process target as additional type witness
1144+
if (checkCompatibleAssignmentTypes(lType, type, cce) && !GenericsUtils.buildWildcardType(lType).isCompatibleWith(type)) {
1145+
// allow covariance of each type parameter, but maintain semantics for nested generics
1146+
1147+
ClassNode pType = GenericsUtils.parameterizeType(lType, type);
1148+
GenericsType[] lhs = pType.getGenericsTypes(), rhs = type.getGenericsTypes();
1149+
if (lhs == null || rhs == null || lhs.length != rhs.length) throw new GroovyBugError(
1150+
"Parameterization failed: " + prettyPrintType(pType) + " ~ " + prettyPrintType(type));
1151+
1152+
if (java.util.stream.IntStream.range(0, lhs.length).allMatch(i ->
1153+
GenericsUtils.buildWildcardType(getCombinedBoundType(lhs[i])).isCompatibleWith(getCombinedBoundType(rhs[i])))) {
1154+
type = pType; // lType proved to be a viable type witness
1155+
}
1156+
}
11431157
inferredType = type;
11441158
}
11451159
}

base/org.codehaus.groovy30/src/org/codehaus/groovy/ast/GenericsType.java

+22
Original file line numberDiff line numberDiff line change
@@ -351,8 +351,15 @@ private static boolean compareGenericsWithBound(final ClassNode classNode, final
351351
// class node are not parameterized. This means that we must create a
352352
// new class node with the parameterized types that the current class node
353353
// has defined.
354+
/* GRECLIPSE edit
354355
ClassNode node = GenericsUtils.parameterizeType(classNode, face);
355356
return compareGenericsWithBound(node, bound);
357+
*/
358+
if (face.getGenericsTypes() != null) {
359+
face = GenericsUtils.parameterizeType(classNode, face);
360+
}
361+
return compareGenericsWithBound(face, bound);
362+
// GRECLIPSE end
356363
}
357364
}
358365
}
@@ -368,7 +375,20 @@ private static boolean compareGenericsWithBound(final ClassNode classNode, final
368375
if (success) return true;
369376
}
370377
}
378+
/* GRECLIPSE edit
371379
return compareGenericsWithBound(getParameterizedSuperClass(classNode), bound);
380+
*/
381+
if (classNode.equals(ClassHelper.OBJECT_TYPE)) {
382+
return false;
383+
}
384+
ClassNode superClass = classNode.getUnresolvedSuperClass();
385+
if (superClass == null) {
386+
superClass = ClassHelper.OBJECT_TYPE;
387+
} else if (superClass.getGenericsTypes() != null) {
388+
superClass = GenericsUtils.parameterizeType(classNode, superClass);
389+
}
390+
return compareGenericsWithBound(superClass, bound);
391+
// GRECLIPSE end
372392
}
373393

374394
GenericsType[] cnTypes = classNode.getGenericsTypes();
@@ -494,6 +514,7 @@ private static boolean compareGenericsWithBound(final ClassNode classNode, final
494514
* @param classNode the class for which we want to return the parameterized superclass
495515
* @return the parameterized superclass
496516
*/
517+
/* GRECLIPSE edit
497518
private static ClassNode getParameterizedSuperClass(final ClassNode classNode) {
498519
if (ClassHelper.OBJECT_TYPE.equals(classNode)) return null;
499520
ClassNode superClass = classNode.getUnresolvedSuperClass();
@@ -523,6 +544,7 @@ private static ClassNode getParameterizedSuperClass(final ClassNode classNode) {
523544
}
524545
return superClass;
525546
}
547+
*/
526548

527549
/**
528550
* Represents GenericsType name

base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

+14
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,20 @@ protected void inferDiamondType(final ConstructorCallExpression cce, final Class
10741074
ClassNode type = GenericsUtils.parameterizeType(cceType, cceType);
10751075
type = inferReturnTypeGenerics(type, constructor, argumentList);
10761076
if (type.isUsingGenerics()) {
1077+
// GROOVY-6232, GROOVY-9956: if cce not assignment compatible, process target as additional type witness
1078+
if (checkCompatibleAssignmentTypes(lType, type, cce) && !GenericsUtils.buildWildcardType(lType).isCompatibleWith(type)) {
1079+
// allow covariance of each type parameter, but maintain semantics for nested generics
1080+
1081+
ClassNode pType = GenericsUtils.parameterizeType(lType, type);
1082+
GenericsType[] lhs = pType.getGenericsTypes(), rhs = type.getGenericsTypes();
1083+
if (lhs == null || rhs == null || lhs.length != rhs.length) throw new GroovyBugError(
1084+
"Parameterization failed: " + prettyPrintType(pType) + " ~ " + prettyPrintType(type));
1085+
1086+
if (java.util.stream.IntStream.range(0, lhs.length).allMatch(i ->
1087+
GenericsUtils.buildWildcardType(getCombinedBoundType(lhs[i])).isCompatibleWith(getCombinedBoundType(rhs[i])))) {
1088+
type = pType; // lType proved to be a viable type witness
1089+
}
1090+
}
10771091
inferredType = type;
10781092
}
10791093
}

base/org.codehaus.groovy40/src/org/codehaus/groovy/ast/GenericsType.java

+23
Original file line numberDiff line numberDiff line change
@@ -352,8 +352,15 @@ private static boolean compareGenericsWithBound(final ClassNode classNode, final
352352
// class node are not parameterized. This means that we must create a
353353
// new class node with the parameterized types that the current class node
354354
// has defined.
355+
/* GRECLIPSE edit
355356
ClassNode node = GenericsUtils.parameterizeType(classNode, face);
356357
return compareGenericsWithBound(node, bound);
358+
*/
359+
if (face.getGenericsTypes() != null) {
360+
face = GenericsUtils.parameterizeType(classNode, face);
361+
}
362+
return compareGenericsWithBound(face, bound);
363+
// GRECLIPSE end
357364
}
358365
}
359366
}
@@ -369,7 +376,21 @@ private static boolean compareGenericsWithBound(final ClassNode classNode, final
369376
if (success) return true;
370377
}
371378
}
379+
/* GRECLIPSE edit
372380
return compareGenericsWithBound(getParameterizedSuperClass(classNode), bound);
381+
*/
382+
if (classNode.equals(ClassHelper.OBJECT_TYPE)) {
383+
return false;
384+
}
385+
ClassNode superClass = classNode.getUnresolvedSuperClass();
386+
if (superClass == null) {
387+
superClass = ClassHelper.OBJECT_TYPE;
388+
} else if (superClass.getGenericsTypes() != null) {
389+
superClass = GenericsUtils.parameterizeType(classNode, superClass);
390+
}
391+
return compareGenericsWithBound(superClass, bound);
392+
// GRECLIPSE end
393+
373394
}
374395

375396
GenericsType[] cnTypes = classNode.getGenericsTypes();
@@ -495,6 +516,7 @@ private static boolean compareGenericsWithBound(final ClassNode classNode, final
495516
* @param classNode the class for which we want to return the parameterized superclass
496517
* @return the parameterized superclass
497518
*/
519+
/* GRECLIPSE edit
498520
private static ClassNode getParameterizedSuperClass(final ClassNode classNode) {
499521
if (ClassHelper.OBJECT_TYPE.equals(classNode)) return null;
500522
ClassNode superClass = classNode.getUnresolvedSuperClass();
@@ -524,6 +546,7 @@ private static ClassNode getParameterizedSuperClass(final ClassNode classNode) {
524546
}
525547
return superClass;
526548
}
549+
*/
527550

528551
/**
529552
* Represents GenericsType name

base/org.codehaus.groovy40/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

+14
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,20 @@ protected void inferDiamondType(final ConstructorCallExpression cce, final Class
10721072
ClassNode type = GenericsUtils.parameterizeType(cceType, cceType);
10731073
type = inferReturnTypeGenerics(type, constructor, argumentList);
10741074
if (type.isUsingGenerics()) {
1075+
// GROOVY-6232, GROOVY-9956: if cce not assignment compatible, process target as additional type witness
1076+
if (checkCompatibleAssignmentTypes(lType, type, cce) && !GenericsUtils.buildWildcardType(lType).isCompatibleWith(type)) {
1077+
// allow covariance of each type parameter, but maintain semantics for nested generics
1078+
1079+
ClassNode pType = GenericsUtils.parameterizeType(lType, type);
1080+
GenericsType[] lhs = pType.getGenericsTypes(), rhs = type.getGenericsTypes();
1081+
if (lhs == null || rhs == null || lhs.length != rhs.length) throw new GroovyBugError(
1082+
"Parameterization failed: " + prettyPrintType(pType) + " ~ " + prettyPrintType(type));
1083+
1084+
if (java.util.stream.IntStream.range(0, lhs.length).allMatch(i ->
1085+
GenericsUtils.buildWildcardType(getCombinedBoundType(lhs[i])).isCompatibleWith(getCombinedBoundType(rhs[i])))) {
1086+
type = pType; // lType proved to be a viable type witness
1087+
}
1088+
}
10751089
inferredType = type;
10761090
}
10771091
}

0 commit comments

Comments
 (0)