7030606: Project-coin: multi-catch types should be pairwise disjoint
authormcimadamore
Tue, 29 Mar 2011 16:40:31 +0100
changeset 9074 76b505d19026
parent 9073 a29adf86aa0c
child 9075 cba34854a40e
7030606: Project-coin: multi-catch types should be pairwise disjoint Summary: Bring javac in sync with latest Project Coin EDR Reviewed-by: jjg
langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties
langtools/test/tools/javac/diags/examples/MulticatchTypesMustBeDisjoint.java
langtools/test/tools/javac/multicatch/7030606/DisjunctiveTypeWellFormednessTest.java
langtools/test/tools/javac/multicatch/7030606/T7030606.java
langtools/test/tools/javac/multicatch/7030606/T7030606.out
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java	Tue Mar 29 16:40:07 2011 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java	Tue Mar 29 16:40:31 2011 +0100
@@ -2901,7 +2901,23 @@
             ctype = chk.checkType(typeTree.pos(),
                           chk.checkClassType(typeTree.pos(), ctype),
                           syms.throwableType);
-            multicatchTypes.append(ctype);
+            if (!ctype.isErroneous()) {
+                //check that alternatives of a disjunctive type are pairwise
+                //unrelated w.r.t. subtyping
+                if (chk.intersects(ctype,  multicatchTypes.toList())) {
+                    for (Type t : multicatchTypes) {
+                        boolean sub = types.isSubtype(ctype, t);
+                        boolean sup = types.isSubtype(t, ctype);
+                        if (sub || sup) {
+                            //assume 'a' <: 'b'
+                            Type a = sub ? ctype : t;
+                            Type b = sub ? t : ctype;
+                            log.error(typeTree.pos(), "multicatch.types.must.be.disjoint", a, b);
+                        }
+                    }
+                }
+                multicatchTypes.append(ctype);
+            }
         }
         tree.type = result = check(tree, types.lub(multicatchTypes.toList()), TYP, pkind, pt);
     }
--- a/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Tue Mar 29 16:40:07 2011 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Tue Mar 29 16:40:31 2011 +0100
@@ -302,6 +302,11 @@
 compiler.err.multicatch.parameter.may.not.be.assigned=\
     multi-catch parameter {0} may not be assigned
 
+# 0: type, 1: type
+compiler.err.multicatch.types.must.be.disjoint=\
+    Alternatives in a multi-catch statement cannot be related by subclassing\n\
+    Alternative {0} is a subclass of alternative {1}
+
 compiler.err.finally.without.try=\
     ''finally'' without ''try''
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/diags/examples/MulticatchTypesMustBeDisjoint.java	Tue Mar 29 16:40:31 2011 +0100
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2011, 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.
+ *
+ * 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.
+ */
+
+// key: compiler.err.multicatch.types.must.be.disjoint
+
+class MulticatchTypesMustBeDisjoint {
+    class E1 extends Exception { }
+    class E2 extends E1 { }
+
+    void e1() throws E1 { }
+    void e2() throws E2 { }
+
+    void m() {
+        try {
+            e1();
+            e2();
+        } catch (E1 | E2 e) { }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/multicatch/7030606/DisjunctiveTypeWellFormednessTest.java	Tue Mar 29 16:40:31 2011 +0100
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2011, 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.
+ *
+ * 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 7030606
+ * @summary Project-coin: multi-catch types should be pairwise disjoint
+ */
+
+import com.sun.source.util.JavacTask;
+import java.net.URI;
+import java.util.Arrays;
+import javax.tools.Diagnostic;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.ToolProvider;
+
+public class DisjunctiveTypeWellFormednessTest {
+
+    enum Alternative {
+        EXCEPTION("Exception"),
+        RUNTIME_EXCEPTION("RuntimeException"),
+        IO_EXCEPTION("java.io.IOException"),
+        FILE_NOT_FOUND_EXCEPTION("java.io.FileNotFoundException"),
+        ILLEGAL_ARGUMENT_EXCEPTION("IllegalArgumentException");
+
+        String exceptionStr;
+
+        private Alternative(String exceptionStr) {
+            this.exceptionStr = exceptionStr;
+        }
+
+        static String makeDisjunctiveType(Alternative... alternatives) {
+            StringBuilder buf = new StringBuilder();
+            String sep = "";
+            for (Alternative alternative : alternatives) {
+                buf.append(sep);
+                buf.append(alternative.exceptionStr);
+                sep = "|";
+            }
+            return buf.toString();
+        }
+
+        boolean disjoint(Alternative that) {
+            return disjoint[this.ordinal()][that.ordinal()];
+        }
+
+        static boolean[][] disjoint = {
+            //                              Exception    RuntimeException    IOException    FileNotFoundException    IllegalArgumentException
+            /*Exception*/                {  false,       false,              false,         false,                   false },
+            /*RuntimeException*/         {  false,       false,              true,          true,                    false },
+            /*IOException*/              {  false,       true,               false,         false,                   true },
+            /*FileNotFoundException*/    {  false,       true,               false,         false,                   true },
+            /*IllegalArgumentException*/ {  false,       false,              true,          true,                    false }
+        };
+    }
+
+    enum Arity {
+        ONE(1),
+        TWO(2),
+        THREE(3),
+        FOUR(4),
+        FIVE(5);
+
+        int n;
+
+        private Arity(int n) {
+            this.n = n;
+        }
+    }
+
+    public static void main(String... args) throws Exception {
+
+        //create default shared JavaCompiler - reused across multiple compilations
+        JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
+        StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
+
+        for (Arity arity : Arity.values()) {
+            for (Alternative a1 : Alternative.values()) {
+                if (arity == Arity.ONE) {
+                    new DisjunctiveTypeWellFormednessTest(a1).run(comp, fm);
+                    continue;
+                }
+                for (Alternative a2 : Alternative.values()) {
+                    if (arity == Arity.TWO) {
+                        new DisjunctiveTypeWellFormednessTest(a1, a2).run(comp, fm);
+                        continue;
+                    }
+                    for (Alternative a3 : Alternative.values()) {
+                        if (arity == Arity.THREE) {
+                            new DisjunctiveTypeWellFormednessTest(a1, a2, a3).run(comp, fm);
+                            continue;
+                        }
+                        for (Alternative a4 : Alternative.values()) {
+                            if (arity == Arity.FOUR) {
+                                new DisjunctiveTypeWellFormednessTest(a1, a2, a3, a4).run(comp, fm);
+                                continue;
+                            }
+                            for (Alternative a5 : Alternative.values()) {
+                                new DisjunctiveTypeWellFormednessTest(a1, a2, a3, a4, a5).run(comp, fm);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    Alternative[] alternatives;
+    JavaSource source;
+    DiagnosticChecker diagChecker;
+
+    DisjunctiveTypeWellFormednessTest(Alternative... alternatives) {
+        this.alternatives = alternatives;
+        this.source = new JavaSource();
+        this.diagChecker = new DiagnosticChecker();
+    }
+
+    class JavaSource extends SimpleJavaFileObject {
+
+        String template = "class Test {\n" +
+                              "void test() {\n" +
+                                 "try {} catch (#T e) {}\n" +
+                              "}\n" +
+                          "}\n";
+
+        String source;
+
+        public JavaSource() {
+            super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
+            source = template.replace("#T", Alternative.makeDisjunctiveType(alternatives));
+        }
+
+        @Override
+        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+            return source;
+        }
+    }
+
+    void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception {
+        JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker,
+                null, null, Arrays.asList(source));
+        ct.analyze();
+        check();
+    }
+
+    void check() {
+
+        int non_disjoint = 0;
+        int i = 0;
+        for (Alternative a1 : alternatives) {
+            int j = 0;
+            for (Alternative a2 : alternatives) {
+                if (i == j) continue;
+                if (!a1.disjoint(a2)) {
+                    non_disjoint++;
+                    break;
+                }
+                j++;
+            }
+            i++;
+        }
+
+        if (non_disjoint != diagChecker.errorsFound) {
+            throw new Error("invalid diagnostics for source:\n" +
+                source.getCharContent(true) +
+                "\nFound errors: " + diagChecker.errorsFound +
+                "\nExpected errors: " + non_disjoint);
+        }
+    }
+
+    static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
+
+        int errorsFound;
+
+        public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+            if (diagnostic.getKind() == Diagnostic.Kind.ERROR &&
+                    diagnostic.getCode().startsWith("compiler.err.multicatch.types.must.be.disjoint")) {
+                errorsFound++;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/multicatch/7030606/T7030606.java	Tue Mar 29 16:40:31 2011 +0100
@@ -0,0 +1,57 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 7030606
+ *
+ * @summary Project-coin: multi-catch types should be pairwise disjoint
+ * @compile/fail/ref=T7030606.out -XDrawDiagnostics T7030606.java
+ */
+
+class T7030606 {
+    class E1 extends Exception { }
+    class E2 extends E1 { }
+
+    void e1() throws E1 { }
+    void e2() throws E2 { }
+
+    void m1() {
+        try {
+            e1();
+            e2();
+        } catch (NonExistentType | E2 | E1 e) { }
+    }
+
+    void m2() {
+        try {
+            e1();
+            e2();
+        } catch (NonExistentType | E1 | E2 e) { }
+    }
+
+    void m3() {
+        try {
+            e1();
+            e2();
+        } catch (E2 | NonExistentType | E1 e) { }
+    }
+
+    void m4() {
+        try {
+            e1();
+            e2();
+        } catch (E1 | NonExistentType | E2 e) { }
+    }
+
+    void m5() {
+        try {
+            e1();
+            e2();
+        } catch (E2 | E1 | NonExistentType e) { }
+    }
+
+    void m6() {
+        try {
+            e1();
+            e2();
+        } catch (E1 | E2 | NonExistentType  e) { }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/multicatch/7030606/T7030606.out	Tue Mar 29 16:40:31 2011 +0100
@@ -0,0 +1,13 @@
+T7030606.java:20:18: compiler.err.cant.resolve.location: kindname.class, NonExistentType, , , (compiler.misc.location: kindname.class, T7030606, null)
+T7030606.java:20:41: compiler.err.multicatch.types.must.be.disjoint: T7030606.E2, T7030606.E1
+T7030606.java:27:18: compiler.err.cant.resolve.location: kindname.class, NonExistentType, , , (compiler.misc.location: kindname.class, T7030606, null)
+T7030606.java:27:41: compiler.err.multicatch.types.must.be.disjoint: T7030606.E2, T7030606.E1
+T7030606.java:34:23: compiler.err.cant.resolve.location: kindname.class, NonExistentType, , , (compiler.misc.location: kindname.class, T7030606, null)
+T7030606.java:34:41: compiler.err.multicatch.types.must.be.disjoint: T7030606.E2, T7030606.E1
+T7030606.java:41:23: compiler.err.cant.resolve.location: kindname.class, NonExistentType, , , (compiler.misc.location: kindname.class, T7030606, null)
+T7030606.java:41:41: compiler.err.multicatch.types.must.be.disjoint: T7030606.E2, T7030606.E1
+T7030606.java:48:23: compiler.err.multicatch.types.must.be.disjoint: T7030606.E2, T7030606.E1
+T7030606.java:48:28: compiler.err.cant.resolve.location: kindname.class, NonExistentType, , , (compiler.misc.location: kindname.class, T7030606, null)
+T7030606.java:55:23: compiler.err.multicatch.types.must.be.disjoint: T7030606.E2, T7030606.E1
+T7030606.java:55:28: compiler.err.cant.resolve.location: kindname.class, NonExistentType, , , (compiler.misc.location: kindname.class, T7030606, null)
+12 errors