JDK-8146862 : Example of wrong type annotation indexing of type varaible bounds on recursive type
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang:reflect
  • Affected Version: 8u66,9
  • Priority: P4
  • Status: Closed
  • Resolution: Cannot Reproduce
  • OS: generic
  • CPU: generic
  • Submitted: 2016-01-11
  • Updated: 2021-04-13
  • Resolved: 2021-04-13
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbd_minorResolved
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :


A DESCRIPTION OF THE PROBLEM :
Type annotations on type variable bounds are not always resolved correctly by the Java core reflection API if the bound is put on a parameterized type that is not an interface type or on another type variable.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
true
true
true
true
true
ACTUAL -
false
true
true
true
false

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List;

public class Foo<T,
        S extends @Sample T,
        U extends @Sample Runnable,
        V extends @Sample String,
        W extends @Sample List<?>,
        X extends @Sample ArrayList<?>> {

    public static void main(String[] args) {
        System.out.println(Foo.class.getTypeParameters()[1].getAnnotatedBounds()[0].isAnnotationPresent(Sample.class));
        System.out.println(Foo.class.getTypeParameters()[2].getAnnotatedBounds()[0].isAnnotationPresent(Sample.class));
        System.out.println(Foo.class.getTypeParameters()[3].getAnnotatedBounds()[0].isAnnotationPresent(Sample.class));
        System.out.println(Foo.class.getTypeParameters()[4].getAnnotatedBounds()[0].isAnnotationPresent(Sample.class));
        System.out.println(Foo.class.getTypeParameters()[5].getAnnotatedBounds()[0].isAnnotationPresent(Sample.class));
    }
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@interface Sample { }
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
None, it is however simple to see why this is happening. javac writes bounds that are "non-primary", i.e. not an interface type at index 1 instead of 0 even if there is no class type bound. The reflection API does not correctly account for this.

The type references in the class file can be interpreted as follows using ASM:

        System.out.println(new TypeReference(285278208).getTypeParameterBoundIndex());
        System.out.println(new TypeReference(285344000).getTypeParameterBoundIndex());
        System.out.println(new TypeReference(285409280).getTypeParameterBoundIndex());
        System.out.println(new TypeReference(285475072).getTypeParameterBoundIndex());
        System.out.println(new TypeReference(285540352).getTypeParameterBoundIndex());

printing

0
1
0
1
0

This does not make much sense in the first place and this is neither specified in this way in the JVMS.


Comments
Not reproducible in JDK 15; possible dup of JDK-8202469.
13-04-2021

Attached test case run on following versions: JDK 8u66 - Fail JDK 8u72 - Fail JDK 9ea - Fail
12-01-2016