8191802: Upward projection result is A<? extends Number> instead of A<? super Integer>
authormcimadamore
Wed, 29 Nov 2017 17:31:23 +0000
changeset 47959 5dd899009525
parent 47958 d34958cb3163
child 47982 44371ff1ee9a
8191802: Upward projection result is A<? extends Number> instead of A<? super Integer> Summary: Code in Types.TypeProjection doesn't match the latest spec text Reviewed-by: vromero
src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
test/langtools/jdk/jshell/TypeNameTest.java
test/langtools/tools/javac/lvti/T8191893.java
test/langtools/tools/javac/lvti/T8191959.java
test/langtools/tools/javac/lvti/TestBadArray.java
test/langtools/tools/javac/lvti/harness/UpperBounds.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java	Wed Nov 29 09:25:25 2017 -0800
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java	Wed Nov 29 17:31:23 2017 +0000
@@ -237,7 +237,7 @@
      * {@code upwards(List<#CAP1>, [#CAP2]) = List<#CAP1> }
      * {@code downwards(List<#CAP1>, [#CAP1]) = not defined }
      */
-    class TypeProjection extends StructuralTypeMapping<ProjectionKind> {
+    class TypeProjection extends TypeMapping<ProjectionKind> {
 
         List<Type> vars;
         Set<Type> seen = new HashSet<>();
@@ -257,13 +257,21 @@
                 Type outer = t.getEnclosingType();
                 Type outer1 = visit(outer, pkind);
                 List<Type> typarams = t.getTypeArguments();
-                List<Type> typarams1 = typarams.map(ta -> mapTypeArgument(ta, pkind));
-                if (typarams1.stream().anyMatch(ta -> ta.hasTag(BOT))) {
-                    //not defined
-                    return syms.botType;
+                List<Type> formals = t.tsym.type.getTypeArguments();
+                ListBuffer<Type> typarams1 = new ListBuffer<>();
+                boolean changed = false;
+                for (Type actual : typarams) {
+                    Type t2 = mapTypeArgument(t, formals.head.getUpperBound(), actual, pkind);
+                    if (t2.hasTag(BOT)) {
+                        //not defined
+                        return syms.botType;
+                    }
+                    typarams1.add(t2);
+                    changed |= actual != t2;
+                    formals = formals.tail;
                 }
-                if (outer1 == outer && typarams1 == typarams) return t;
-                else return new ClassType(outer1, typarams1, t.tsym, t.getMetadata()) {
+                if (outer1 == outer && !changed) return t;
+                else return new ClassType(outer1, typarams1.toList(), t.tsym, t.getMetadata()) {
                     @Override
                     protected boolean needsStripping() {
                         return true;
@@ -272,21 +280,23 @@
             }
         }
 
-        protected Type makeWildcard(Type upper, Type lower) {
-            BoundKind bk;
-            Type bound;
-            if (upper.hasTag(BOT)) {
-                upper = syms.objectType;
-            }
-            boolean isUpperObject = isSameType(upper, syms.objectType);
-            if (!lower.hasTag(BOT) && isUpperObject) {
-                bound = lower;
-                bk = SUPER;
+        @Override
+        public Type visitArrayType(ArrayType t, ProjectionKind s) {
+            Type elemtype = t.elemtype;
+            Type elemtype1 = visit(elemtype, s);
+            if (elemtype1 == elemtype) {
+                return t;
+            } else if (elemtype1.hasTag(BOT)) {
+                //undefined
+                return syms.botType;
             } else {
-                bound = upper;
-                bk = isUpperObject ? UNBOUND : EXTENDS;
-            }
-            return new WildcardType(bound, bk, syms.boundClass);
+                return new ArrayType(elemtype1, t.tsym, t.metadata) {
+                    @Override
+                    protected boolean needsStripping() {
+                        return true;
+                    }
+                };
+            }
         }
 
         @Override
@@ -322,33 +332,79 @@
             }
         }
 
-        @Override
-        public Type visitWildcardType(WildcardType wt, ProjectionKind pkind) {
-            switch (pkind) {
-                case UPWARDS:
-                    return wt.isExtendsBound() ?
-                            wt.type.map(this, pkind) :
-                            syms.objectType;
-                case DOWNWARDS:
-                    return wt.isSuperBound() ?
-                            wt.type.map(this, pkind) :
-                            syms.botType;
-                default:
-                    Assert.error();
-                    return null;
-            }
+        private Type mapTypeArgument(Type site, Type declaredBound, Type t, ProjectionKind pkind) {
+            return t.containsAny(vars) ?
+                    t.map(new TypeArgumentProjection(site, declaredBound), pkind) :
+                    t;
         }
 
-        private Type mapTypeArgument(Type t, ProjectionKind pkind) {
-            if (!t.containsAny(vars)) {
-                return t;
-            } else if (!t.hasTag(WILDCARD) && pkind == ProjectionKind.DOWNWARDS) {
-                //not defined
-                return syms.botType;
-            } else {
-                Type upper = t.map(this, pkind);
-                Type lower = t.map(this, pkind.complement());
-                return makeWildcard(upper, lower);
+        class TypeArgumentProjection extends TypeMapping<ProjectionKind> {
+
+            Type site;
+            Type declaredBound;
+
+            TypeArgumentProjection(Type site, Type declaredBound) {
+                this.site = site;
+                this.declaredBound = declaredBound;
+            }
+
+            @Override
+            public Type visitType(Type t, ProjectionKind pkind) {
+                //type argument is some type containing restricted vars
+                if (pkind == ProjectionKind.DOWNWARDS) {
+                    //not defined
+                    return syms.botType;
+                }
+                Type upper = t.map(TypeProjection.this, ProjectionKind.UPWARDS);
+                Type lower = t.map(TypeProjection.this, ProjectionKind.DOWNWARDS);
+                List<Type> formals = site.tsym.type.getTypeArguments();
+                BoundKind bk;
+                Type bound;
+                if (!isSameType(upper, syms.objectType) &&
+                        (declaredBound.containsAny(formals) ||
+                         !isSubtype(declaredBound, upper))) {
+                    bound = upper;
+                    bk = EXTENDS;
+                } else if (!lower.hasTag(BOT)) {
+                    bound = lower;
+                    bk = SUPER;
+                } else {
+                    bound = syms.objectType;
+                    bk = UNBOUND;
+                }
+                return makeWildcard(bound, bk);
+            }
+
+            @Override
+            public Type visitWildcardType(WildcardType wt, ProjectionKind pkind) {
+                //type argument is some wildcard whose bound contains restricted vars
+                Type bound = syms.botType;
+                BoundKind bk = wt.kind;
+                switch (wt.kind) {
+                    case EXTENDS:
+                        bound = wt.type.map(TypeProjection.this, pkind);
+                        if (bound.hasTag(BOT)) {
+                            return syms.botType;
+                        }
+                        break;
+                    case SUPER:
+                        bound = wt.type.map(TypeProjection.this, pkind.complement());
+                        if (bound.hasTag(BOT)) {
+                            bound = syms.objectType;
+                            bk = UNBOUND;
+                        }
+                        break;
+                }
+                return makeWildcard(bound, bk);
+            }
+
+            private Type makeWildcard(Type bound, BoundKind bk) {
+                return new WildcardType(bound, bk, syms.boundClass) {
+                    @Override
+                    protected boolean needsStripping() {
+                        return true;
+                    }
+                };
             }
         }
     }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java	Wed Nov 29 09:25:25 2017 -0800
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java	Wed Nov 29 17:31:23 2017 +0000
@@ -946,8 +946,6 @@
         }
 
     Type checkLocalVarType(DiagnosticPosition pos, Type t, Name name) {
-        //upward project the initializer type
-        t = types.upward(t, types.captures(t));
         //check that resulting type is not the null type
         if (t.hasTag(BOT)) {
             log.error(pos, Errors.CantInferLocalVarType(name, Fragments.LocalCantInferNull));
@@ -956,7 +954,9 @@
             log.error(pos, Errors.CantInferLocalVarType(name, Fragments.LocalCantInferVoid));
             return types.createErrorType(t);
         }
-        return t;
+
+        //upward project the initializer type
+        return types.upward(t, types.captures(t));
     }
 
     Type checkMethod(final Type mtype,
--- a/test/langtools/jdk/jshell/TypeNameTest.java	Wed Nov 29 09:25:25 2017 -0800
+++ b/test/langtools/jdk/jshell/TypeNameTest.java	Wed Nov 29 17:31:23 2017 +0000
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8144903 8171981
+ * @bug 8144903 8171981 8191802
  * @summary Tests for determining the type from the expression
  * @build KullaTesting TestingInputStream
  * @run testng TypeNameTest
@@ -55,7 +55,7 @@
 
         assertEval("class D<T extends CharSequence> { D<? super T> getS() { return null; } }");
         assertEval("D<?> d = new D<String>();");
-        assertType("d.getS()", "D<? extends CharSequence>");
+        assertType("d.getS()", "D<?>");
         assertType("null", "Object");
         assertType("Class.forName( \"java.util.ArrayList\" )", "Class<?>");
         assertType("new ArrayList<Boolean>() {}", "ArrayList<Boolean>");
@@ -68,7 +68,7 @@
         assertEval("interface J extends A, I {}");
         assertEval("interface K extends A, I {}");
         assertEval("class P<T extends A & I> {}");
-        assertType("(P<?>) null", "P<? extends Object>");
+        assertType("(P<?>) null", "P<?>");
     }
 
     public void testConditionals() {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/lvti/T8191893.java	Wed Nov 29 17:31:23 2017 +0000
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8191802 8191893
+ * @summary Upward projection result is A<? extends Number> instead of A<? super Integer>
+ * @compile T8191893.java
+ */
+
+class T8191893 {
+
+    static class A<E> { }
+
+    <T> A<? super T> m(T t) {
+        return null;
+    }
+
+    <U> A<? super A<? super U>> m2(A<? super U> u) {
+        return null;
+    }
+
+    void test() {
+        var varValue = m2(m(10));
+        A<? super A<Object>> expectedTypeValue = varValue;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/lvti/T8191959.java	Wed Nov 29 17:31:23 2017 +0000
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8191802 8191959
+ * @summary Upward projection result is A<? extends Number> instead of A<? super Integer>
+ * @compile T8191959.java
+ */
+
+public class T8191959 {
+    static class A<E> { }
+
+    <T> A<? super T> m(T t) {
+        return null;
+    }
+
+    <U> A<? super A<? extends U>> m2(A<? super U> u) {
+        return null;
+    }
+
+    void test() {
+        var varValue = m2(m(10));
+        A<? super A<? extends Integer>> expectedTypeValue = varValue;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/lvti/TestBadArray.java	Wed Nov 29 17:31:23 2017 +0000
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8191802
+ * @summary Upward projection result is A<? extends Number> instead of A<? super Integer>
+ * @compile TestBadArray.java
+ */
+
+import java.util.List;
+
+class TestArr {
+    <Z> List<? super Z[]> m(List<Z> z) { return null; }
+    void test(List<? extends Number> l) {
+        var v = m(l);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/lvti/harness/UpperBounds.java	Wed Nov 29 17:31:23 2017 +0000
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8191802
+ * @summary Upward projection result is A<? extends Number> instead of A<? super Integer>
+ * @modules jdk.compiler/com.sun.source.tree
+ *          jdk.compiler/com.sun.source.util
+ *          jdk.compiler/com.sun.tools.javac.api
+ *          jdk.compiler/com.sun.tools.javac.code
+ *          jdk.compiler/com.sun.tools.javac.util
+ * @build LocalVariableInferenceTester
+ * @run main LocalVariableInferenceTester UpperBounds.java
+ */
+class UpperBounds {
+
+    static class A<T extends Number> { }
+
+    static class C<T extends Comparable<T>> { }
+
+    void test1(A<? super Integer> s) {
+        //U is not Object, Bi is not fbound and U is not more specific than Bi, use "? super L"
+        @InferredType("UpperBounds.A<? super java.lang.Integer>")
+        var x = s;
+    }
+
+    void test2(C<? super Integer> s) {
+        //U is not Object, Bi is fbound, use "? extends L"
+        @InferredType("UpperBounds.C<? extends java.lang.Comparable<?>>")
+        var x = s;
+    }
+
+    void test3(A<? extends Object> s) {
+        //U is not Object, Bi is not fbound and U is not more specific than Bi, L is undefined, use "?"
+        @InferredType("UpperBounds.A<?>")
+        var x = s;
+    }
+
+    void test4(A<? extends Integer> s) {
+        //U is not Object, Bi is not fbound and U is more specific than Bi, use "? extends U"
+        @InferredType("UpperBounds.A<? extends java.lang.Integer>")
+        var x = s;
+    }
+
+    void test5(A<? extends Object> s) {
+        //U is not Object, Bi is not fbound and U is not more specific than Bi, L is undefined, use "?"
+        @InferredType("UpperBounds.A<?>")
+        var x = s;
+    }
+}