You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@groovy.apache.org by "ASF GitHub Bot (Jira)" <ji...@apache.org> on 2026/10/08 16:42:00 UTC
[jira] [Commented] (GROOVY-12434) Implementing a precompiled trait fails with "Duplicate annotation" when a trait method has a TYPE_USE annotation on its return type
[ https://issues.apache.org/jira/browse/GROOVY-12434?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18125152#comment-18125152 ]
ASF GitHub Bot commented on GROOVY-12434:
-----------------------------------------
paulk-asert opened a new pull request, #2961:
URL: https://github.com/apache/groovy/pull/2961
…annotation" when a trait method has a TYPE_USE annotation on its return type
> Implementing a precompiled trait fails with "Duplicate annotation" when a trait method has a TYPE_USE annotation on its return type
> -----------------------------------------------------------------------------------------------------------------------------------
>
> Key: GROOVY-12434
> URL: https://issues.apache.org/jira/browse/GROOVY-12434
> Project: Groovy
> Issue Type: Bug
> Components: Compiler
> Affects Versions: 6.0.0
> Reporter: Mattias Reichel
> Assignee: Paul King
> Priority: Major
>
> When a trait method's return type carries a {{TYPE_USE}} annotation, such as JSpecify's {{@Nullable}}, the trait is written with that annotation twice. A class in a later compilation that implements the trait then fails to compile on Groovy 6.0.0 with {{java.lang.annotation.AnnotationFormatError: Duplicate annotation}}.
> This compiles on Groovy 5.1.3 and fails on 6.0.0. It does not happen when the trait and the implementing class are in the same compilation, or when the annotation is on a parameter instead of the return type. It fails whether the trait was compiled by 5.1.3 or by 6.0.0, so traits in existing jars built with Groovy 5 are affected too.
> h3. Steps to reproduce
> {code:title=trait/MaybeNull.java}
> import java.lang.annotation.ElementType;
> import java.lang.annotation.Retention;
> import java.lang.annotation.RetentionPolicy;
> import java.lang.annotation.Target;
> @Retention(RetentionPolicy.RUNTIME)
> @Target(ElementType.TYPE_USE)
> public @interface MaybeNull { }
> {code}
> {code:title=trait/Named.groovy}
> trait Named {
> @MaybeNull String getName() { null }
> }
> {code}
> {code:title=src/Person.groovy}
> class Person implements Named { }
> {code}
> {noformat}
> mkdir lib out
> javac -d lib trait/MaybeNull.java
> java -cp groovy-6.0.0.jar:lib org.codehaus.groovy.tools.FileSystemCompiler -cp lib -d lib trait/Named.groovy
> java -cp groovy-6.0.0.jar org.codehaus.groovy.tools.FileSystemCompiler -cp lib -d out src/Person.groovy
> {noformat}
> h3. Expected
> Compilation succeeds, as on Groovy 5.1.3.
> h3. Actual
> {noformat}
> >>> a serious error occurred: Duplicate annotation for class: interface MaybeNull: @MaybeNull()
> >>> stacktrace:
> java.lang.annotation.AnnotationFormatError: Duplicate annotation for class: interface MaybeNull: @MaybeNull()
> at java.base/sun.reflect.annotation.TypeAnnotationParser.mapTypeAnnotations(TypeAnnotationParser.java:382)
> at java.base/sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl.<init>(AnnotatedTypeFactory.java:138)
> ...
> at org.codehaus.groovy.vmplugin.v8.Java8.configureClassNode(Java8.java:392)
> at org.codehaus.groovy.ast.ClassNode.lazyClassInit(ClassNode.java:192)
> at org.codehaus.groovy.ast.ClassNode.getMethods(ClassNode.java:1090)
> at org.codehaus.groovy.ast.ClassNode.getMethods(ClassNode.java:1089)
> at org.codehaus.groovy.transform.trait.TraitComposer.applyTrait(TraitComposer.java:134)
> at org.codehaus.groovy.transform.trait.TraitComposer.doExtendTraits(TraitComposer.java:110)
> ...
> {noformat}
> h3. Notes
> {{javap -v}} shows the return type annotation twice on the trait's interface method and on its helper method. The class files are the same whether the trait is compiled by 5.1.3 or by 6.0.0:
> {noformat}
> public abstract java.lang.String getName();
> descriptor: ()Ljava/lang/String;
> flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
> RuntimeVisibleAnnotations:
> 0: #9()
> org.codehaus.groovy.transform.trait.Traits$Implemented
> RuntimeVisibleTypeAnnotations:
> 0: #10(): METHOD_RETURN
> MaybeNull
> 1: #10(): METHOD_RETURN
> MaybeNull
> {noformat}
> {{Named$Trait$Helper.getName(Named)}} has the same two {{METHOD_RETURN}} entries. The same method on a plain class or interface is written with a single entry.
> Groovy 5.1.3 accepts the duplicate because it does not read the annotated return type when it initializes the trait's {{ClassNode}}. Groovy 6.0.0 reads it through reflection in {{Java8.configureClassNode}}, where the JDK rejects the duplicate. Groovy 5.1.3 can still implement a trait compiled by 6.0.0.
> GROOVY-12413 fixed similar duplicated {{TYPE_USE}} annotations on record components and tuple-constructor parameters. The trait transform seems to copy the return type annotations as well as the method annotations.
> Found while moving Apache Grails to Groovy 6: a JSpecify {{@Nullable}} on a return type in the Grails unit testing traits ({{ControllerUnitTest}}, {{UrlMappingsUnitTest}}, {{InterceptorUnitTest}}) makes every test class that implements them fail to compile. The workaround is to leave {{TYPE_USE}} annotations off trait method return types.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)