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

B. Blaser bsrbnd at gmail.com
Sat Sep 23 16:15:46 UTC 2017


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