[ 
https://issues.apache.org/jira/browse/GROOVY-10763?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Christopher Smith updated GROOVY-10763:
---------------------------------------
    Description: 
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.

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


> Generic types are cleared on read when a ClassNode is assigned as a superClass
> ------------------------------------------------------------------------------
>
>                 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.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to