IntersectionTypeCastTest.java revision 3019:176472b94f2e
1/*
2 * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test
26 * @bug 8002099 8006694 8129962
27 * @summary Add support for intersection types in cast expression
28 *  temporarily workaround combo tests are causing time out in several platforms
29 * @library /tools/javac/lib
30 * @modules jdk.compiler/com.sun.tools.javac.api
31 *          jdk.compiler/com.sun.tools.javac.code
32 *          jdk.compiler/com.sun.tools.javac.comp
33 *          jdk.compiler/com.sun.tools.javac.main
34 *          jdk.compiler/com.sun.tools.javac.tree
35 *          jdk.compiler/com.sun.tools.javac.util
36 * @build combo.ComboTestHelper
37
38 * @run main IntersectionTypeCastTest
39 */
40
41import com.sun.tools.javac.util.List;
42
43import combo.ComboInstance;
44import combo.ComboParameter;
45import combo.ComboTask.Result;
46import combo.ComboTestHelper;
47
48import java.io.IOException;
49
50public class IntersectionTypeCastTest extends ComboInstance<IntersectionTypeCastTest> {
51
52    interface Type extends ComboParameter {
53        boolean subtypeOf(Type that);
54        boolean isClass();
55        boolean isInterface();
56    }
57
58    enum InterfaceKind implements Type {
59        A("A", null),
60        B("B", null),
61        C("C", A);
62
63        String typeStr;
64        InterfaceKind superInterface;
65
66        InterfaceKind(String typeStr, InterfaceKind superInterface) {
67            this.typeStr = typeStr;
68            this.superInterface = superInterface;
69        }
70
71        @Override
72        public boolean subtypeOf(Type that) {
73            return this == that || superInterface == that ||
74                   that == ClassKind.OBJECT;
75        }
76
77        @Override
78        public boolean isClass() {
79            return false;
80        }
81
82        @Override
83        public boolean isInterface() {
84            return true;
85        }
86
87        @Override
88        public String expand(String optParameter) {
89            return typeStr;
90        }
91    }
92
93    enum ClassKind implements Type {
94        OBJECT("Object"),
95        CA("CA", InterfaceKind.A),
96        CB("CB", InterfaceKind.B),
97        CAB("CAB", InterfaceKind.A, InterfaceKind.B),
98        CC("CC", InterfaceKind.C, InterfaceKind.A),
99        CCA("CCA", InterfaceKind.C, InterfaceKind.A),
100        CCB("CCB", InterfaceKind.C, InterfaceKind.A, InterfaceKind.B),
101        CCAB("CCAB", InterfaceKind.C, InterfaceKind.A, InterfaceKind.B);
102
103        String typeStr;
104        List<InterfaceKind> superInterfaces;
105
106        ClassKind(String typeStr, InterfaceKind... superInterfaces) {
107            this.typeStr = typeStr;
108            this.superInterfaces = List.from(superInterfaces);
109        }
110
111        @Override
112        public boolean subtypeOf(Type that) {
113            return this == that || superInterfaces.contains(that) ||
114                    that == OBJECT;
115        }
116
117        @Override
118        public boolean isClass() {
119            return true;
120        }
121
122        @Override
123        public boolean isInterface() {
124            return false;
125        }
126
127        @Override
128        public String expand(String optParameter) {
129            return typeStr;
130        }
131    }
132
133    enum ModifierKind implements ComboParameter {
134        NONE(""),
135        FINAL("final");
136
137        String modStr;
138
139        ModifierKind(String modStr) {
140            this.modStr = modStr;
141        }
142
143        @Override
144        public String expand(String optParameter) {
145            return modStr;
146        }
147    }
148
149    enum CastKind implements ComboParameter {
150        CLASS("(#{CLAZZ#IDX})", 0),
151        INTERFACE("(#{INTF1#IDX})", 1),
152        INTERSECTION2("(#{CLAZZ#IDX} & #{INTF1#IDX})", 1),
153        INTERSECTION3("(#{CLAZZ#IDX} & #{INTF1#IDX} & #{INTF2#IDX})", 2);
154
155        String castTemplate;
156        int interfaceBounds;
157
158        CastKind(String castTemplate, int interfaceBounds) {
159            this.castTemplate = castTemplate;
160            this.interfaceBounds = interfaceBounds;
161        }
162
163        @Override
164        public String expand(String optParameter) {
165            return castTemplate.replaceAll("#IDX", optParameter);
166        }
167    }
168
169    static class CastInfo {
170        CastKind kind;
171        Type[] types;
172
173        CastInfo(CastKind kind, Type... types) {
174            this.kind = kind;
175            this.types = types;
176        }
177
178        boolean hasDuplicateTypes() {
179            for (int i = 0 ; i < arity() ; i++) {
180                for (int j = 0 ; j < arity() ; j++) {
181                    if (i != j && types[i] == types[j]) {
182                        return true;
183                    }
184                }
185            }
186            return false;
187        }
188
189        boolean compatibleWith(ModifierKind mod, CastInfo that) {
190            for (int i = 0 ; i < arity() ; i++) {
191                Type t1 = types[i];
192                for (int j = 0 ; j < that.arity() ; j++) {
193                    Type t2 = that.types[j];
194                    boolean compat =
195                            t1.subtypeOf(t2) ||
196                            t2.subtypeOf(t1) ||
197                            (t1.isInterface() && t2.isInterface()) || //side-cast (1)
198                            (mod == ModifierKind.NONE &&
199                            (t1.isInterface() != t2.isInterface())); //side-cast (2)
200                    if (!compat) return false;
201                }
202            }
203            return true;
204        }
205
206        private int arity() {
207            return kind.interfaceBounds + 1;
208        }
209    }
210
211    public static void main(String... args) throws Exception {
212        new ComboTestHelper<IntersectionTypeCastTest>()
213                .withFilter(IntersectionTypeCastTest::isRedundantCast)
214                .withFilter(IntersectionTypeCastTest::arityFilter)
215                .withArrayDimension("CAST", (x, ck, idx) -> x.castKinds[idx] = ck, 2, CastKind.values())
216                .withDimension("CLAZZ1", (x, ty) -> x.types1[0] = ty, ClassKind.values())
217                .withDimension("INTF11", (x, ty) -> x.types1[1] = ty, InterfaceKind.values())
218                .withDimension("INTF21", (x, ty) -> x.types1[2] = ty, InterfaceKind.values())
219                .withDimension("CLAZZ2", (x, ty) -> x.types2[0] = ty, ClassKind.values())
220                .withDimension("INTF12", (x, ty) -> x.types2[1] = ty, InterfaceKind.values())
221                .withDimension("INTF22", (x, ty) -> x.types2[2] = ty, InterfaceKind.values())
222                .withDimension("MOD", (x, mod) -> x.mod = mod, ModifierKind.values())
223                .run(IntersectionTypeCastTest::new);
224    }
225
226    boolean isRedundantCast() {
227        for (int i = 0 ; i < 2 ; i++) {
228            Type[] types = i == 0 ? types1 : types2;
229            if (castKinds[i] == CastKind.INTERFACE && types[0] != ClassKind.OBJECT) {
230                return false;
231            }
232        }
233        return true;
234    }
235
236    boolean arityFilter() {
237        for (int i = 0 ; i < 2 ; i++) {
238            int lastPos = castKinds[i].interfaceBounds + 1;
239            Type[] types = i == 0 ? types1 : types2;
240            for (int j = 1; j < types.length; j++) {
241                boolean shouldBeSet = j < lastPos;
242                if (!shouldBeSet && (types[j] != InterfaceKind.A)) {
243                    return false;
244                }
245            }
246        }
247        return true;
248    }
249
250    ModifierKind mod;
251    CastKind[] castKinds = new CastKind[2];
252    Type[] types1 = new Type[3];
253    Type[] types2 = new Type[3];
254
255    @Override
256    public void doWork() throws IOException {
257        check(newCompilationTask()
258                .withSourceFromTemplate(bodyTemplate)
259                .analyze());
260    }
261
262    String bodyTemplate = "class Test {\n" +
263                          "   void test() {\n" +
264                          "      Object o = #{CAST[0].1}#{CAST[1].2}null;\n" +
265                          "   } }\n" +
266                          "interface A { }\n" +
267                          "interface B { }\n" +
268                          "interface C extends A { }\n" +
269                          "#{MOD} class CA implements A { }\n" +
270                          "#{MOD} class CB implements B { }\n" +
271                          "#{MOD} class CAB implements A, B { }\n" +
272                          "#{MOD} class CC implements C { }\n" +
273                          "#{MOD} class CCA implements C, A { }\n" +
274                          "#{MOD} class CCB implements C, B { }\n" +
275                          "#{MOD} class CCAB implements C, A, B { }";
276
277    void check(Result<?> res) {
278        CastInfo cast1 = new CastInfo(castKinds[0], types1);
279        CastInfo cast2 = new CastInfo(castKinds[1], types2);
280        boolean errorExpected = cast1.hasDuplicateTypes() ||
281                cast2.hasDuplicateTypes();
282
283        errorExpected |= !cast2.compatibleWith(mod, cast1);
284
285        boolean errorsFound = res.hasErrors();
286        if (errorExpected != errorsFound) {
287            fail("invalid diagnostics for source:\n" +
288                res.compilationInfo() +
289                "\nFound error: " + errorsFound +
290                "\nExpected error: " + errorExpected);
291        }
292    }
293}
294