Details
-
Bug
-
Status: Closed
-
Major
-
Resolution: Fixed
-
None
-
None
-
Patch
Description
ResolveVisitor improperly marks generics types as being resolved when sub-generics types haven't been resolved, leading to classes being generated that lack fully qualified generics typing and then subsequent errors while compiling against those generated class files. Here's an example:
package a; public interface Item {}
package b import a.* class ItemListList { static List<List<Item>> ITEMS }
If you compile this with the joint compiler and inspect ItemListList with javap, the type signature will be:
public static java.util.List<java.util.List<Item>> ITEMS
Note that Item type is not fully qualified (it should refer to a.Item), and if that class file is accessed during a later compilation (e.g., for an incremental compilation or a groovy server page), an exception with a stack trace similar to the following will be thrown:
java.lang.TypeNotPresentException: Type Item not present at sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:117) at sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:125) at sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49) at sun.reflect.generics.visitor.Reifier.reifyTypeArguments(Reifier.java:68) at sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:138) at sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49) at sun.reflect.generics.visitor.Reifier.reifyTypeArguments(Reifier.java:68) at sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:138) at sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49) at sun.reflect.generics.repository.FieldRepository.getGenericType(FieldRepository.java:85) at java.lang.reflect.Field.getGenericType(Field.java:237) at org.codehaus.groovy.vmplugin.v5.Java5.configureClassNode(Java5.java:357) at org.codehaus.groovy.ast.ClassNode.lazyClassInit(ClassNode.java:261) ...
The problem is that ResolveVisitor marks a GenericsType as resolved iff the type (i.e., ClassNode) has been resolved, but ignores any failure to resolve any generic types associated with the generic type itself. To use the example above, when resolving List<List<Item>> it first attempts to resolve the generic type List<Item>. It successfully resolves List to java.util.List, but isn't yet able to resolve Item to a.Item, as a star import is used and the java-compiler stage of joint compilation has yet to run, so a.Item is unavailable for classloader-based resolution in resolveToOuter to occur. Nonetheless, it incorrectly marks List<Item> as resolved, as it only checks that the primary type was resolved. A later attempt at resolution occurs after the java compilation, at which time an attempt to resolve Item would work since a/Item.class now exists, but no re-attempt is made at this time to resolve the GenericType as it had already been marked as being resolved. The solution is simple: only mark a GenericType as resolved if both its type and any genericsTypes it has are resolved.
Pull request is at https://github.com/groovy/groovy-core/pull/408
It's possible that this bug was responsible for GROOVY-3157.