[ 
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)

Reply via email to