Repository: incubator-groovy Updated Branches: refs/heads/master de31d0456 -> 4c4ead4ac
GROOVY-7363: Frequent compilation error on cascading generic types (closes #158) Reliable type resolution in the static type checker with covariant getters Prefer a non-bridge getter method Preferring a non-bridge getter over a bridge one ensures that the type resolution of expressions in the static type checker always uses the covariant return type of getters (due to generics, for example) and doesn't lose accuracy, resulting in spurious type checking errors. It's worth noting that the check is actually whether the method is synthetic or not, because MethodNode doesn't keep the bridge flag, but that should be good enough. Project: http://git-wip-us.apache.org/repos/asf/incubator-groovy/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-groovy/commit/4c4ead4a Tree: http://git-wip-us.apache.org/repos/asf/incubator-groovy/tree/4c4ead4a Diff: http://git-wip-us.apache.org/repos/asf/incubator-groovy/diff/4c4ead4a Branch: refs/heads/master Commit: 4c4ead4acd52dea0fe669d34e8eb93ce1ea763af Parents: de31d04 Author: Frank Pavageau <fpavag...@ekino.com> Authored: Mon Aug 3 15:04:37 2015 +0200 Committer: pascalschumacher <pascalschumac...@gmx.net> Committed: Tue Nov 3 19:02:42 2015 +0100 ---------------------------------------------------------------------- src/main/org/codehaus/groovy/ast/ClassNode.java | 10 +++- .../classgen/asm/sc/bugs/Groovy7363Bug.groovy | 28 ++++++++++++ .../asm/sc/bugs/support/Groovy7363Support.java | 48 ++++++++++++++++++++ 3 files changed, 85 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/4c4ead4a/src/main/org/codehaus/groovy/ast/ClassNode.java ---------------------------------------------------------------------- diff --git a/src/main/org/codehaus/groovy/ast/ClassNode.java b/src/main/org/codehaus/groovy/ast/ClassNode.java index 8485157..bf045d8 100644 --- a/src/main/org/codehaus/groovy/ast/ClassNode.java +++ b/src/main/org/codehaus/groovy/ast/ClassNode.java @@ -1096,13 +1096,21 @@ public class ClassNode extends AnnotatedNode implements Opcodes { } public MethodNode getGetterMethod(String getterName) { + MethodNode getterMethod = null; for (MethodNode method : getDeclaredMethods(getterName)) { if (getterName.equals(method.getName()) && ClassHelper.VOID_TYPE!=method.getReturnType() && method.getParameters().length == 0) { - return method; + // GROOVY-7363: There can be multiple matches for a getter returning a generic parameter type, due to + // the generation of a bridge method. The real getter is really the non-bridge, non-synthetic one as it + // has the most specific and exact return type of the two. Picking the bridge method results in loss of + // type information, as it down-casts the return type to the lower bound of the generic parameter. + if (getterMethod == null || getterMethod.isSynthetic()) { + getterMethod = method; + } } } + if (getterMethod != null) return getterMethod; ClassNode parent = getSuperClass(); if (parent!=null) return parent.getGetterMethod(getterName); return null; http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/4c4ead4a/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7363Bug.groovy ---------------------------------------------------------------------- diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7363Bug.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7363Bug.groovy new file mode 100644 index 0000000..b1f23c1 --- /dev/null +++ b/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7363Bug.groovy @@ -0,0 +1,28 @@ +/* + * Copyright 2003-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.codehaus.groovy.classgen.asm.sc.bugs + +import groovy.transform.stc.StaticTypeCheckingTestCase +import org.codehaus.groovy.classgen.asm.sc.StaticCompilationTestSupport + +class Groovy7363Bug extends StaticTypeCheckingTestCase implements StaticCompilationTestSupport { + void testCascadingGenericTypes() { + assertScript """import org.codehaus.groovy.classgen.asm.sc.bugs.support.Groovy7363Support + Groovy7363Support.ABC a = new Groovy7363Support.ABC() + assert ('' + a.b.object.value) == '42' + """ + } +} http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/4c4ead4a/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/support/Groovy7363Support.java ---------------------------------------------------------------------- diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/support/Groovy7363Support.java b/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/support/Groovy7363Support.java new file mode 100644 index 0000000..44e4d6a --- /dev/null +++ b/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/support/Groovy7363Support.java @@ -0,0 +1,48 @@ +/* + * Copyright 2003-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.codehaus.groovy.classgen.asm.sc.bugs.support; + +public class Groovy7363Support { + public interface A<T, U extends B<T>> { + U getB(); + } + + public static class ABC implements A<C, BC> { + public void setB(BC b) {} + + @Override + public BC getB() { + return new BC(); + } + } + + public interface B<T> { + T getObject(); + } + + public static class BC implements B<C> { + @Override + public C getObject() { + return new C(); + } + } + + public static class C { + public long getValue() { + return 42; + } + } +}