changeset 4047:0025bb118860

8176534: Missing check against target-type during applicability inference Summary: PartiallyInferredMethodType should check against target if unchecked conversion occurred Reviewed-by: vromero
author mcimadamore
date Wed, 15 Mar 2017 11:42:42 +0000
parents adef848660f9
children 147a9390f8e2
files src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java test/tools/javac/generics/inference/8176534/T8176534.java test/tools/javac/generics/inference/8176534/T8176534.out test/tools/javac/generics/inference/8176534/TestUncheckedCalls.java
diffstat 4 files changed, 306 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java	Tue Mar 14 10:51:19 2017 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java	Wed Mar 15 11:42:42 2017 +0000
@@ -190,28 +190,29 @@
                 doIncorporation(inferenceContext, warn);
                 //we are inside method attribution - just return a partially inferred type
                 return new PartiallyInferredMethodType(mt, inferenceContext, env, warn);
-            } else if (allowGraphInference &&
-                    resultInfo != null &&
-                    !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) {
+            } else if (allowGraphInference && resultInfo != null) {
+
                 //inject return constraints earlier
                 doIncorporation(inferenceContext, warn); //propagation
 
-                boolean shouldPropagate = shouldPropagate(mt.getReturnType(), resultInfo, inferenceContext);
+                if (!warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) {
+                    boolean shouldPropagate = shouldPropagate(mt.getReturnType(), resultInfo, inferenceContext);
 
-                InferenceContext minContext = shouldPropagate ?
-                        inferenceContext.min(roots(mt, deferredAttrContext), true, warn) :
-                        inferenceContext;
+                    InferenceContext minContext = shouldPropagate ?
+                            inferenceContext.min(roots(mt, deferredAttrContext), true, warn) :
+                            inferenceContext;
 
-                Type newRestype = generateReturnConstraints(env.tree, resultInfo,  //B3
-                        mt, minContext);
-                mt = (MethodType)types.createMethodTypeWithReturn(mt, newRestype);
+                    Type newRestype = generateReturnConstraints(env.tree, resultInfo,  //B3
+                            mt, minContext);
+                    mt = (MethodType)types.createMethodTypeWithReturn(mt, newRestype);
 
-                //propagate outwards if needed
-                if (shouldPropagate) {
-                    //propagate inference context outwards and exit
-                    minContext.dupTo(resultInfo.checkContext.inferenceContext());
-                    deferredAttrContext.complete();
-                    return mt;
+                    //propagate outwards if needed
+                    if (shouldPropagate) {
+                        //propagate inference context outwards and exit
+                        minContext.dupTo(resultInfo.checkContext.inferenceContext());
+                        deferredAttrContext.complete();
+                        return mt;
+                    }
                 }
             }
 
@@ -318,7 +319,7 @@
                  */
                 saved_undet = inferenceContext.save();
                 boolean unchecked = warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED);
-                if (allowGraphInference && !unchecked) {
+                if (!unchecked) {
                     boolean shouldPropagate = shouldPropagate(getReturnType(), resultInfo, inferenceContext);
 
                     InferenceContext minContext = shouldPropagate ?
@@ -338,9 +339,13 @@
                 }
                 inferenceContext.solve(noWarnings);
                 Type ret = inferenceContext.asInstType(this).getReturnType();
-                //inline logic from Attr.checkMethod - if unchecked conversion was required, erase
-                //return type _after_ resolution
-                return unchecked ? types.erasure(ret) : ret;
+                if (unchecked) {
+                    //inline logic from Attr.checkMethod - if unchecked conversion was required, erase
+                    //return type _after_ resolution, and check against target
+                    ret = types.erasure(ret);
+                    resultInfo.check(env.tree, ret);
+                }
+                return ret;
             } catch (InferenceException ex) {
                 resultInfo.checkContext.report(null, ex.getDiagnostic());
                 Assert.error(); //cannot get here (the above should throw)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/generics/inference/8176534/T8176534.java	Wed Mar 15 11:42:42 2017 +0000
@@ -0,0 +1,18 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8176534
+ * @summary Missing check against target-type during applicability inference
+ * @compile/fail/ref=T8176534.out -Werror -Xlint:unchecked -XDrawDiagnostics T8176534.java
+ */
+
+import java.util.*;
+
+abstract class T8176534 {
+    List<String> f(Enumeration e) {
+        return newArrayList(forEnumeration(e));
+    }
+
+    abstract <T> Iterator<T> forEnumeration(Enumeration<T> e);
+    abstract <E> ArrayList<E> newArrayList(Iterator<? extends E> xs);
+    abstract <E> ArrayList<E> newArrayList(Iterable<? extends E> xs);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/generics/inference/8176534/T8176534.out	Wed Mar 15 11:42:42 2017 +0000
@@ -0,0 +1,8 @@
+T8176534.java:12:43: compiler.warn.unchecked.meth.invocation.applied: kindname.method, forEnumeration, java.util.Enumeration<T>, java.util.Enumeration, kindname.class, T8176534
+T8176534.java:12:44: compiler.warn.prob.found.req: (compiler.misc.unchecked.assign), java.util.Enumeration, java.util.Enumeration<T>
+T8176534.java:12:28: compiler.warn.unchecked.meth.invocation.applied: kindname.method, newArrayList, java.util.Iterator<? extends E>, java.util.Iterator, kindname.class, T8176534
+T8176534.java:12:43: compiler.warn.prob.found.req: (compiler.misc.unchecked.assign), java.util.Iterator, java.util.Iterator<? extends E>
+T8176534.java:12:28: compiler.warn.prob.found.req: (compiler.misc.unchecked.assign), java.util.ArrayList, java.util.List<java.lang.String>
+- compiler.err.warnings.and.werror
+1 error
+5 warnings
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/generics/inference/8176534/TestUncheckedCalls.java	Wed Mar 15 11:42:42 2017 +0000
@@ -0,0 +1,255 @@
+/*
+ * 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.
+ */
+
+import combo.ComboInstance;
+import combo.ComboParameter;
+import combo.ComboTask.Result;
+import combo.ComboTestHelper;
+
+import javax.lang.model.element.Element;
+import java.util.stream.Stream;
+
+/*
+ * @test
+ * @bug 8176534
+ * @summary Missing check against target-type during applicability inference
+ * @library /tools/javac/lib
+ * @modules jdk.compiler/com.sun.tools.javac.api
+ *          jdk.compiler/com.sun.tools.javac.code
+ *          jdk.compiler/com.sun.tools.javac.comp
+ *          jdk.compiler/com.sun.tools.javac.main
+ *          jdk.compiler/com.sun.tools.javac.tree
+ *          jdk.compiler/com.sun.tools.javac.util
+ * @build combo.ComboTestHelper
+ *
+ * @run main TestUncheckedCalls
+ */
+public class TestUncheckedCalls extends ComboInstance<TestUncheckedCalls> {
+    enum InputExpressionKind implements ComboParameter {
+        A("(A)null"),
+        A_STRING("(A<String>)null"),
+        B("(B)null"),
+        B_STRING("(B<String>)null");
+
+        String inputExpr;
+
+        InputExpressionKind(String inputExpr) {
+            this.inputExpr = inputExpr;
+        }
+
+
+        @Override
+        public String expand(String optParameter) {
+            return inputExpr;
+        }
+    }
+
+    enum TypeKind implements ComboParameter {
+        Z("Z"),
+        C_T("#C<T>"),
+        C_STRING("#C<String>"),
+        C("#C");
+
+        String typeTemplate;
+
+        TypeKind(String typeTemplate) {
+            this.typeTemplate = typeTemplate;
+        }
+
+        boolean hasTypeVars() {
+            return this == Z || this == C_T;
+        }
+
+        @Override
+        public String expand(String className) {
+            return typeTemplate.replaceAll("#C", className);
+        }
+    }
+
+    enum TypeVarsKind implements ComboParameter {
+        NONE("", "Object"),
+        Z_T("<Z extends #C<T>, T>", "Z");
+
+        String typeVarsTemplate;
+        String paramString;
+
+        TypeVarsKind(String typeVarsTemplate, String paramString) {
+            this.typeVarsTemplate = typeVarsTemplate;
+            this.paramString = paramString;
+        }
+
+
+        @Override
+        public String expand(String className) {
+            if (className.equals("Z")) {
+                return paramString;
+            } else {
+                return typeVarsTemplate.replaceAll("#C", className);
+            }
+        }
+    }
+
+    enum CallKind implements ComboParameter {
+        M("M(#{IN}, #{IN})"),
+        M_G("M(G(#{IN}, #{IN}), #{IN})"),
+        M_G_G("M(G(#{IN}, #{IN}), G(#{IN}, #{IN}))");
+
+        String callExpr;
+
+        CallKind(String callExpr) {
+            this.callExpr = callExpr;
+        }
+
+
+        @Override
+        public String expand(String optParameter) {
+            return callExpr;
+        }
+    }
+
+    enum DeclKind implements ComboParameter {
+        NONE(""),
+        ONE("#{TVARS[#M_IDX].I1} #{RET[#M_IDX].A} #M(#{ARG[#M_IDX].A} x1, #{TVARS[#M_IDX].Z} x2) { return null; }"),
+        TWO("#{TVARS[#M_IDX].I1} #{RET[#M_IDX].A} #M(#{ARG[#M_IDX].A} x1, #{TVARS[#M_IDX].Z} x2) { return null; }\n" +
+        "    #{TVARS[#M_IDX].I2} #{RET[#M_IDX].B} #M(#{ARG[#M_IDX].B} x1, #{TVARS[#M_IDX].Z} x2) { return null; }");
+
+        String declTemplate;
+
+        DeclKind(String declTemplate) {
+            this.declTemplate = declTemplate;
+        }
+
+        @Override
+        public String expand(String methName) {
+            return declTemplate.replaceAll("#M_IDX", methName.equals("M") ? "0" : "1")
+                    .replaceAll("#M", methName);
+
+        }
+    }
+
+    static final String sourceTemplate =
+            "class Test {\n" +
+            "   interface I1<X> { }\n" +
+            "   interface I2<X> { }\n" +
+            "   static class A<X> implements I1<X> { }\n" +
+            "   static class B<X> implements I2<X> { }\n" +
+            "   #{DECL[0].M}\n" +
+            "   #{DECL[1].G}\n" +
+            "   void test() {\n" +
+            "       #{CALL};\n" +
+            "   }\n" +
+            "}\n";
+
+    public static void main(String... args) throws Exception {
+        new ComboTestHelper<TestUncheckedCalls>()
+                .withFilter(TestUncheckedCalls::arityFilter)
+                .withFilter(TestUncheckedCalls::declFilter)
+                .withFilter(TestUncheckedCalls::tvarFilter)
+                .withFilter(TestUncheckedCalls::inputExprFilter)
+                .withDimension("IN", (x, expr) -> x.inputExpressionKind = expr, InputExpressionKind.values())
+                .withDimension("CALL", (x, expr) -> x.callKind = expr, CallKind.values())
+                .withArrayDimension("DECL", (x, decl, idx) -> x.decls[idx] = x.new Decl(decl, idx), 2, DeclKind.values())
+                .withArrayDimension("TVARS", (x, tvars, idx) -> x.typeVarsKinds[idx] = tvars, 2, TypeVarsKind.values())
+                .withArrayDimension("RET", (x, ret, idx) -> x.returnKinds[idx] = ret, 2, TypeKind.values())
+                .withArrayDimension("ARG", (x, arg, idx) -> x.argumentKinds[idx] = arg, 2, TypeKind.values())
+                .run(TestUncheckedCalls::new);
+    }
+
+    class Decl {
+        private DeclKind declKind;
+        private int index;
+
+        Decl(DeclKind declKind, int index) {
+            this.declKind = declKind;
+            this.index = index;
+        }
+
+        boolean hasKind(DeclKind declKind) {
+            return this.declKind == declKind;
+        }
+
+        boolean isGeneric() {
+            return typeVarsKind() == TypeVarsKind.Z_T;
+        }
+
+        TypeKind returnKind() {
+            return returnKinds[index];
+        }
+
+        TypeKind argumentsKind() {
+            return argumentKinds[index];
+        }
+
+        TypeVarsKind typeVarsKind() {
+            return typeVarsKinds[index];
+        }
+    }
+
+    CallKind callKind;
+    InputExpressionKind inputExpressionKind;
+    TypeKind[] returnKinds = new TypeKind[2];
+    TypeKind[] argumentKinds = new TypeKind[2];
+    TypeVarsKind[] typeVarsKinds = new TypeVarsKind[2];
+    Decl[] decls = new Decl[2];
+
+    boolean arityFilter() {
+        return (callKind == CallKind.M || !decls[1].hasKind(DeclKind.NONE)) &&
+                !decls[0].hasKind(DeclKind.NONE);
+    }
+
+    boolean declFilter() {
+        return Stream.of(decls)
+                .filter(d -> d.hasKind(DeclKind.NONE))
+                .flatMap(d -> Stream.of(d.returnKind(), d.argumentsKind(), d.typeVarsKind()))
+                .noneMatch(tk -> tk.ordinal() != 0);
+    }
+
+    boolean tvarFilter() {
+        return Stream.of(decls)
+                .filter(d -> !d.hasKind(DeclKind.NONE))
+                .filter(d -> !d.isGeneric())
+                .flatMap(d -> Stream.of(d.returnKind(), d.argumentsKind()))
+                .noneMatch(TypeKind::hasTypeVars);
+    }
+
+    boolean inputExprFilter() {
+        return (inputExpressionKind != InputExpressionKind.B && inputExpressionKind != InputExpressionKind.B_STRING) ||
+                Stream.of(decls).allMatch(d -> d.declKind == DeclKind.TWO);
+    }
+
+    @Override
+    public void doWork() throws Throwable {
+        check(newCompilationTask()
+                .withSourceFromTemplate(sourceTemplate)
+                .analyze());
+    }
+
+    void check(Result<Iterable<? extends Element>> result) {
+        if (result.hasErrors()) {
+            fail("compiler error:\n" +
+                    result.compilationInfo());
+        }
+    }
+}