Generic constructor thrown type (constructor type variable) is not erased despite of unchecked conversion

Georgiy Rakov georgiy.rakov at oracle.com
Fri Aug 28 15:33:38 UTC 2015


Hello,

let's consider following example.

    class MyExceptionextends Exception {}

    class MyDerivedExceptionextends MyException {}


    class MyType<T> {
         MyType(T t) {}
    }

    public class Test32 {
         public static void test() {
             try {
                 MyDerivedException e1 =new MyDerivedException();

                 Foo foo =new Foo(e1,new MyType(""));
             }catch (MyDerivedException e2) {
             }
         }
    }

    class Foo {
         public <E1 extends MyException> Foo(E1 e, MyType<String> a)throws E1 {
             throw e;
         }
    }

It compiles successfully on JDK9b78. Now let's consider reasoning 
presented below. 1. According to following assertion, 15.12.2 is used in 
order to determine the constructor thrown types:

    ***If C is not an anonymous class,*** let T be the type denoted by C
    followed by any class type arguments in the expression. The process
    specified in §15.12.2, modified to handle constructors, is used to
    select one of the constructors of T and /_*determine its throws
    clause*_/.

2. According to my understanding the types in throws clause are 
determined in the last subsection of 15.12.2, that is in 15.12.2.6, 
which states:

    If the chosen method is generic and the method invocation does not
    provide explicit type arguments, the invocation type is inferred as
    specified in §18.5.2.

3. 18.5.2 contains following assertion:

    If unchecked conversion was necessary for the method to be
    applicable during constraint set reduction in §18.5.1, then the
    parameter types of the invocation type of m are obtained by applying
    a' to the parameter types of m's type, and the return type and
    thrown types of the invocation type of m are given by the erasure of
    the return type and thrown types of m's type. (jls-18.5.2-100-F.1-A)

and 18.2.2. contains following assertions:

    A constraint formula of the form ‹S → T› is reduced as follows:
    (jls-18.2.2-100) Otherwise, if Tis a parameterized type of the form
    G|<|T_1 , ..., T_n |>|, and there exists no type of the form
    G|<|...|>|that is a supertype of S, but the raw type Gis a supertype
    of S, then the constraint reduces to /true/. (jls-18.2.2-100-D) The
    fourth and fifth cases are implicit uses of unchecked conversion
    (§5.1.9). These, along with any use of unchecked conversion in the
    first case, may result in compile-time unchecked warnings, and may
    influence a method's invocation type (§15.12.2.6). (jls-18.2.2-110)

3.1. During reduction constraint [new MyType("") -> MyType<String>] is 
reduced to [MyType -> MyType<String>]. 3.2. [MyType -> MyType<String>] 
is reduced to true according to jls-18.2.2-100-D. 3.3. Thus according to 
jls-18.2.2-110 step 3.2 is considered as applying unchecked conversion. 
3.4. Hence according to jls-18.5.2-100-F.1-Athe constructor exception 
type should be erased that is E1 should be erased to MyException which 
should have caused compilation failure, but compilation succeeds. 3.5. 
So if I understand correctly javac behaves as if jls-18.5.2-100-F.1-A 
specified that substitution a' is applied to m's thrown type first, and 
only after that ctorthrown type is erased.This looks like a spec issue, 
that is spec should have specified something like:  "and the return type 
and thrown types of the invocation type of m are given by the erasure of 
the return type and thrown types of m2 where m2 is the result of 
applying a' to m's type" (I guess the same is true for the return type). 
Could you please tell if you agree that this is really a spec issue. 
Thank you, Georgiy.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20150828/44062d77/attachment.html>
-------------- next part --------------
class MyException extends Exception {}

class MyDerivedException extends MyException {}


class MyType<T> {
    MyType(T t) {}
}

public class Test32 {
    public static void test() {
        try {
            MyDerivedException e1 = new MyDerivedException();

            Foo foo = new Foo(e1, new MyType(""));
        } catch (MyDerivedException e2) {
        }
    }
}

class Foo {
    public <E1 extends MyException> Foo(E1 e, MyType<String> a) throws E1 {
        throw e;
    }
}


More information about the compiler-dev mailing list