JDK-8026527: Unchecked conversion is allowed by method type argument bound checking

B. Blaser bsrbnd at gmail.com
Mon Sep 25 15:15:26 UTC 2017


Note that conforming to the current JLS:

 Object parseEnumValue(Class<? extends Enum<?>> enumType, String constName)

would also fail per §18.2.3:

* from complementary pairs of bounds < CAP#1 of ? extends Enum<?> <: Enum<T> >
* reduced to < ? <= T >
* reduced to false

But:

 <E extends Enum<E>> Object parseEnumValue(Class<E> enumType, String constName)

would succeed, which make sense, at my mind.

So, being compliant with the current JLS would probably and hopefully
do more good than harm in this specific case...

But, as the language isn't limited to this sole example, let's wait
for the experts' conclusion about the specifications!

Cheers,
Bernard

On 25 September 2017 at 10:24, Maurizio Cimadamore
<maurizio.cimadamore at oracle.com> wrote:
> Hi Bernard,
> we are aware of the issue and the patch - the hard thing here is to
> establish as to whether the fix is doing more harm than good. As described
> in the comment you quote, a fix for this has been attempted, but then
> aborted in the light of these kinds of incompatibilities.
>
> There is a number of type-system issues being worked on at the moment (at
> the spec level) [1] - I think it's best to defer any such point fixes after
> we have a clear picture of where the spec will land.
>
> [1] -
> https://bugs.openjdk.java.net/browse/JDK-8078095?jql=labels%20%3D%20jls-types
>
> Cheers
> Maurizio
>
>
>
> On 23/09/17 17:15, B. Blaser wrote:
>>
>> Hi,
>>
>> Following [1], I tried to investigate a bit more issue JDK-8026527.
>>
>> A JBS comment mention the following example, not far from [1]:
>>
>>    class Test {
>>        @SuppressWarnings({"unchecked"})
>>        Object parseEnumValue(Class<? extends Enum> enumType, String
>> constName) {
>>            return Enum.valueOf(enumType, constName);
>>        }
>>    }
>>
>> With:
>>
>>    public static <T extends Enum<T>> T valueOf(Class<T> enumType, String
>> name);
>>
>> As it isn't clearly established if the latter should compile or not, I
>> tried to follow JLS 9 §18.5.1 to see what's happening on the bound set
>> of T:
>>
>>  From "Enum.valueOf()"    : T <: Enum<T>
>>     (§18.5.1 + §18.1.3)
>>  From formal parameters & actual arguments
>> * Constraint formula     < enumType -> Class<T> >
>>     (§18.5.1)
>> * Reduced to             < Class<CAP#1 of ? extends Enum> -> Class<T>
>>>
>>>    (§18.2.1 + §5.1.10)
>>
>> * Reduced to             < Class<CAP#1> <: Class<T> >
>>     (§18.2.2)
>> * Reduced to             < CAP#1 <= T >
>>     (§18.2.3)
>> * Reduced to             < CAP#1 = T >
>>     (§18.2.3)
>> * Reduced to bound       : CAP#1 = T
>>     (§18.2.4)
>>
>>  From incorporation of complementary pairs of bounds
>> * Constraint formula     < CAP#1 <: Enum<T> >
>>     (§18.3.1)
>> * Reduced to             : false
>>     (§18.2.3)
>>
>> Which is similar to [1].
>>
>> So, I think this example should fail to compile as the others (the two
>> from JDK-8026527 and the enhanced enums one [1]).
>>
>> My conclusion is that the fix, like here under, could perhaps be as
>> straightforward as replacing "isSubtypeUnchecked()" by "isSubtype()"
>> in "Resolve.rawInstantiate()" [2] per §15.12.2.2-3 and in
>> "Infer.IncorporationBinaryOpKind.IS_SUBTYPE()" [3] per §18.2.3.
>>
>> What do you think?
>>
>> Thanks,
>> Bernard
>>
>> [1]
>> http://mail.openjdk.java.net/pipermail/amber-dev/2017-September/002075.html
>> [2]
>> http://hg.openjdk.java.net/jdk10/master/file/4fe50ead4783/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java#l582
>> [3]
>> http://hg.openjdk.java.net/jdk10/master/file/4fe50ead4783/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java#l1189
>>
>>
>> diff --git
>> a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java
>> b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java
>> --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java
>> +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java
>> @@ -1186,7 +1186,7 @@
>>           IS_SUBTYPE() {
>>               @Override
>>               boolean apply(Type op1, Type op2, Warner warn, Types types)
>> {
>> -                return types.isSubtypeUnchecked(op1, op2, warn);
>> +                return types.isSubtype(op1, op2);
>>               }
>>           },
>>           IS_SAME_TYPE() {
>> diff --git
>> a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java
>> b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java
>> --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java
>> +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java
>> @@ -579,7 +579,7 @@
>>                   List<Type> bounds =
>> types.subst(types.getBounds((TypeVar)formals.head),
>>                                                   pmt.tvars,
>> typeargtypes);
>>                   for (; bounds.nonEmpty(); bounds = bounds.tail) {
>> -                    if (!types.isSubtypeUnchecked(actuals.head,
>> bounds.head, warn))
>> +                    if (!types.isSubtype(actuals.head, bounds.head))
>>                           throw
>>
>> inapplicableMethodException.setMessage("explicit.param.do.not.conform.to.bounds",actuals.head,
>> bounds);
>>                   }
>>                   formals = formals.tail;
>
>


More information about the compiler-dev mailing list