[ 
https://issues.apache.org/jira/browse/GROOVY-10763?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17607380#comment-17607380
 ] 

Eric Milles edited comment on GROOVY-10763 at 9/20/22 7:59 PM:
---------------------------------------------------------------

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.

getSuperClass() and getUnresolvedSuperClass() are for accessing the declared 
superclass and the resolved superclass respectively.  For example "class C 
extends Optional<String>" will return "Optional<String>" for 
unresolvedSuperClass and "Optional<T>" (the way Optional is declared) for 
superClass.


was (Author: emilles):
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.

getSuperClass() and getUnresolvedSuperClass() are for accessing the declared 
superclass and the resolved superclass respectively.  For example "class C 
extends Optional<String>" will return "Optional<String>" for 
unresolvedSuperClass and "Optional<T>" for superClass.

> 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