JDK-6973212 : Hidden type variable incorrectly represented in generic superclass signature
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang:reflect
  • Affected Version: 7
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: linux
  • CPU: x86
  • Submitted: 2010-07-29
  • Updated: 2023-11-05
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_18"
OpenJDK Runtime Environment (IcedTea6 1.8) (fedora-41.b18.fc13-x86_64)
OpenJDK 64-Bit Server VM (build 14.0-b16, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
Linux mattlaptop2.local 2.6.33.6-147.2.4.fc13.x86_64 #1 SMP Fri Jul 23 17:14:44 UTC 2010 x86_64 x86_64 x86_64 GNU/Linux

A DESCRIPTION OF THE PROBLEM :
The "extends" declaration of an inner class can refer implicitly to a type parameter of the enclosing instance that has been hidden by a type parameter of the same name belonging to the inner class.  There is no way to represent this unambiguously in a class file, since generic signatures contain only the names of type variables and do not indicate the declarations they belong to.  See the test case.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and run the test case.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Output "class HiddenTypeVariable$Outer" or a compile error.
ACTUAL -
Output "class HiddenTypeVariable$Outer$InnerSub".

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.TypeVariable;

class HiddenTypeVariable {

  static class Outer<S/*1*/> {
    class Inner {
    }

    class InnerSub<S/*2*/> extends /*Outer<S1>.*/Inner {
      //                                   ^^
      /* In generic signatures in class files, type variables are stored as
       * names only, without the declaring member. When reading a signature, the
       * Java reflect library appears to choose the innermost type variable of
       * the correct name. This leads to accidental capture of the marked
       * reference to S1 by the S2 of InnerSub. */
    }

    class InnerSub2<S/*2*/> extends Outer<S/*2*/>.Inner {
      /* This is also legal, but it looks exactly the same in the class file.
       * Information is irretrievably lost. To fix the problem, one would have
       * to change the class file format. Short of that, all one could do is
       * have the compiler issue an error on classes like InnerSub for which it
       * is unable to produce a class file that will be interpreted correctly.
       * The quick fix is to rename one of the type variables. */
      
      /* Our enclosing instance is an Outer<S1>, but we need to supply an
       * Outer<S2> enclosing instance to the super constructor.  Unfortunately,
       * javac doesn't catch us if we don't; this is a separate bug
       * (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6886402). */
      InnerSub2(Outer<S/*2*/> outer) {
        outer.super();
      }
    }
  }

  public static void main(String[] args) {
    ParameterizedType innerSuper =
      (ParameterizedType) Outer.InnerSub.class.getGenericSuperclass();
    ParameterizedType owner = (ParameterizedType) innerSuper.getOwnerType();
    TypeVariable<?> outerArg =
      (TypeVariable<?>) owner.getActualTypeArguments()[0];
    System.out.println(outerArg.getGenericDeclaration());
    // Expected: class HiddenTypeVariable$Outer
    // Actual:   class HiddenTypeVariable$Outer$InnerSub
  }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Give the type parameters different names!

Comments
Available information from Signature attribute cannot distinguish if a given T; type parameter is from a parent class or this class; the Signature is only able to represent the situation in InnerSub2. This might require Class-File format changes, either to represent a hidden type parameter or to represent implicit generic receiver type.
05-11-2023