[ https://issues.apache.org/jira/browse/GROOVY-10763?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17607380#comment-17607380 ]
Eric Milles commented on GROOVY-10763: -------------------------------------- ClassNode#isUsingGenerics is used as a quick check to prevent doing expensive AST visitation looking for generics. If you are updating a primary class node, that is one you intend to create a class file from, you should setUsingGenerics(true) if you add super class, interface, field, method, etc. that makes uses of generics. > Generic types not written to bytecode after ClassNode#setSuperClass > ------------------------------------------------------------------- > > Key: GROOVY-10763 > URL: https://issues.apache.org/jira/browse/GROOVY-10763 > Project: Groovy > Issue Type: Bug > Components: Compiler > Affects Versions: 4.0.5 > Reporter: Christopher Smith > Priority: Major > > I have an annotation that, if an annotated class has no {{{}extends{}}}, > should make it extend {{{}Resource<A>{}}}, where {{A}} is determined by > Logic. I attempted to create the {{ClassNode}} representing the intended base > class and assign it as the derived class's {{{}superClass{}}}: > {code:groovy} > println resource.superClass // java.lang.Object > def sc = makeClassSafeWithGenerics(TYPE_RESOURCE, attr) > println sc // Resource<com.example.Derived$Attributes> -> Resource<A> > println sc.genericsTypes // [com.example.Derived$Attributes] > cn.superClass = sc > def sc2 = cn.superClass > println sc2 // Resource<A> > println cn.@superClass // Resource<com.example.Derived$Attributes> -> > Resource<A> > println sc2 == sc // true, but compares only the erasure > println sc2 === sc // false > {code} > The compiler then proceeds to write out a {{.class}} file extending the raw > {{Resource}} type. The compiler writes the correct {{Resource<Attributes>}} > if it is typed out inline in the input file. > I've confirmed that the {{ClassNode TYPE_RESOURCE}} is not a redirect or a > genericsPlaceholder, so the {{superClass}} assignment appears to be a direct > field assignment (once {{redirect()}} returns {{{}this{}}}), and > {{getUnresolvedSuperClass}} appears to read the field directly > ({{{}lazyInitDone{}}} is already true), but using {{.@superClass}} returns > the generic copy and {{.superClass}} (which is used by the rest of the > compilation process) the raw copy. > > {*}Update{*}: {{cn.@superClass.redirectNode}} is true, and {{getSuperClass}} > unwraps redirects ({{{}ClassNode:1040{}}} in 4.0.5). This appears to be a > logic error precisely because of this discard-generics-on-read effect. > {*}Update 2{*}: I see the same "superclass is a redirect, and > {{getSuperClass}} returns a raw type" on a class where the generics are > specified in Groovy source code, but this class has the correct generics > written into the bytecode where the ASTT-modified class does not. I cannot > find any way to distinguish between the {{{}ClassNode{}}}s for the > superclasses in these cases, but the output differs. (In case it's relevant > at all, constructing a node for an interface type that has generic parameters > and adding it to a class writes the type parameters into the bytecode; it's > only the superclass that gets erased.) > {*}Update 3{*}: In the case of a resource subclass that has type parameters > defined in Groovy source, the field {{usesGenerics}} is set to true, > presumably via the check at {{{}ClassNode:345{}}}. I have tried manually > setting {{{}cn.usingGenerics = true{}}}, but that's triggering a (spurious?) > "transform used a generics containing... directly" error downstream. It seems > like a bug that the constructor sets {{usesGenerics}} if the superclass uses > generics but that {{setSuperClass}} does not. > {*}Update 4{*}: Adding the explicit {{cn.usingGenerics = true}} fixes the > missing type parameters in the bytecode, at the cost of having to make > spurious calls to {{.plainNodeReference}} later. It appears that the compiler > is checking {{usingGenerics}} on class nodes when writing the {{extends}} > part of the bytecode but not {{{}implements{}}}. There seem to be two logical > bugs here: If the superclass has generic types, it shouldn't matter what the > flag is on the subclass (write the type parameters as for interfaces), and > creating a {{ClassNode}} that extends a generic superclass but does not > itself have type parameters (e.g., {{{}StringMap extends HashMap<String, > String>{}}}) should not trigger the "you must copy this node to refer to it" > check. -- This message was sent by Atlassian Jira (v8.20.10#820010)