Issue with separate compilation and a generic inline type

Paul Sandoz paul.sandoz at oracle.com
Mon Dec 7 18:22:00 UTC 2020


Hi Srikanth,

Certainly, see here https://bugs.openjdk.java.net/browse/JDK-8257846 <https://bugs.openjdk.java.net/browse/JDK-8257846>

Paul

> On Dec 6, 2020, at 11:37 PM, Srikanth <srikanth.adayapalam at oracle.com> wrote:
> 
> Hi Paul,
> 
> May I request you to raise a JBS ticket ? I will follow up - TIA.
> 
> Srikanth
> 
> On 05/12/20 6:41 am, Paul Sandoz wrote:
>> Hi,
>> 
>> If I have this inline class and a test class
>> 
>> public inline class Range<T> {
>>     private final T lower;
>>     private final T upper;
>> 
>>     private Range(T lower, T upper) {
>>         this.lower = lower;
>>         this.upper = upper;
>>     }
>> 
>>     public static <T> Range<T> of(T lower, T upper) {
>>         return new Range<>(lower, upper);
>>     }
>> }
>> 
>> public class Test {
>>     public static void main(String[] args) {
>>         Range<Integer> r = Range.of(1, 2);
>>     }
>> }
>> 
>> Compiling Range first and then, separately, Test fails:
>> 
>> 
>> $ javac Range.java
>> $ javac Test.java
>> Test.java:3: error: cannot access Range
>>         Range<Integer> r = Range.of(1, 2);
>>         ^
>>   bad class file: ./Range.class
>>     method descriptor invalid for <init>
>>     Please remove or make sure it appears in the correct subdirectory of the classpath.
>> 1 error
>> 
>> This came about as I was writing some inline classes and corresponding tests.
>> 
>> So I started poking around the class file and javac code.
>> 
>> A constructor for an inline class declares that class as the return type of method-related constants/attributes:
>> 
>> #10 = Methodref          #1.#11         // Range."<init>":(Ljava/lang/Object;Ljava/lang/Object;)QRange;
>> #11 = NameAndType        #12:#13        // "<init>":(Ljava/lang/Object;Ljava/lang/Object;)QRange;
>> #12 = Utf8               <init>
>> ...
>> private static Range<T> Range(java.lang.Object, java.lang.Object);
>>   descriptor: (Ljava/lang/Object;Ljava/lang/Object;)QRange;
>> ...
>>   Signature: #22                          // (Ljava/lang/Object;Ljava/lang/Object;)QRange<TT;>;
>> 
>> For reference classes the return type is void (V).
>> 
>> 
>> In javac, ClassReader.readMethod first creates a MethodType instance for “<init>” with an explicit void return type, which gets applied to the MethodSymbol instance. Signature attribute parsing then updates the MethodSymbol’s type with the result of parsing the constructor signature attribute, thus a non-void return type appears, which then fails the method validation check.
>> 
>> 
>> I bodged javac with this patch:
>> 
>> --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java
>> +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java
>> @@ -2254,7 +2254,7 @@ public class ClassReader {
>>          if (name == names.init && ((flags & STATIC) != 0)) {
>>              flags &= ~STATIC;
>>              type = new MethodType(type.getParameterTypes(),
>> -                    syms.voidType,
>> +                    currentOwner.isValue() ? currentOwner.type : syms.voidType,
>>                      type.getThrownTypes(),
>>                      syms.methodClass);
>>          }
>> @@ -2302,7 +2302,7 @@ public class ClassReader {
>>        void validateMethodType(Name name, Type t) {
>>          if ((!t.hasTag(TypeTag.METHOD) && !t.hasTag(TypeTag.FORALL)) ||
>> -            (name == names.init && !t.getReturnType().hasTag(TypeTag.VOID))) {
>> +            (name == names.init && !currentOwner.isValue() && !t.getReturnType().hasTag(TypeTag.VOID))) {
>>              throw badClassFile("method.descriptor.invalid", name);
>>          }
>>      }
>> 
>> … but I don’t really know what the implications are.
>> 
>> Paul.
> 



More information about the valhalla-dev mailing list