IntersectionTypeCastTest.java revision 1481:954541f13717
12726Sjlahoda/*
22311Sjlahoda * Copyright (c) 2012, 2013, 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
27 * @summary Add support for intersection types in cast expression
28 * @library ../../lib
29 * @build JavacTestingAbstractThreadedTest
30 * @run main/timeout=360 IntersectionTypeCastTest
31 */
32
33import java.net.URI;
34import java.util.Arrays;
35import javax.tools.Diagnostic;
36import javax.tools.JavaCompiler;
37import javax.tools.JavaFileObject;
38import javax.tools.SimpleJavaFileObject;
39import javax.tools.ToolProvider;
40
41import com.sun.source.util.JavacTask;
42import com.sun.tools.javac.util.List;
43import com.sun.tools.javac.util.ListBuffer;
44
45public class IntersectionTypeCastTest
46    extends JavacTestingAbstractThreadedTest
47    implements Runnable {
48
49    interface Type {
50        boolean subtypeOf(Type that);
51        String asString();
52        boolean isClass();
53        boolean isInterface();
54    }
55
56    enum InterfaceKind implements Type {
57        A("interface A { }\n", "A", null),
58        B("interface B { }\n", "B", null),
59        C("interface C extends A { }\n", "C", A);
60
61        String declStr;
62        String typeStr;
63        InterfaceKind superInterface;
64
65        InterfaceKind(String declStr, String typeStr,
66                InterfaceKind superInterface) {
67            this.declStr = declStr;
68            this.typeStr = typeStr;
69            this.superInterface = superInterface;
70        }
71
72        @Override
73        public boolean subtypeOf(Type that) {
74            return this == that || superInterface == that ||
75                   that == ClassKind.OBJECT;
76        }
77
78        @Override
79        public String asString() {
80            return typeStr;
81        }
82
83        @Override
84        public boolean isClass() {
85            return false;
86        }
87
88        @Override
89        public boolean isInterface() {
90            return true;
91        }
92    }
93
94    enum ClassKind implements Type {
95        OBJECT(null, "Object"),
96        CA("#M class CA implements A { }\n", "CA",
97           InterfaceKind.A),
98        CB("#M class CB implements B { }\n", "CB",
99           InterfaceKind.B),
100        CAB("#M class CAB implements A, B { }\n", "CAB",
101            InterfaceKind.A, InterfaceKind.B),
102        CC("#M class CC implements C { }\n", "CC",
103           InterfaceKind.C, InterfaceKind.A),
104        CCA("#M class CCA implements C, A { }\n", "CCA",
105            InterfaceKind.C, InterfaceKind.A),
106        CCB("#M class CCB implements C, B { }\n", "CCB",
107            InterfaceKind.C, InterfaceKind.A, InterfaceKind.B),
108        CCAB("#M class CCAB implements C, A, B { }\n", "CCAB",
109             InterfaceKind.C, InterfaceKind.A, InterfaceKind.B);
110
111        String declTemplate;
112        String typeStr;
113        List<InterfaceKind> superInterfaces;
114
115        ClassKind(String declTemplate, String typeStr,
116                InterfaceKind... superInterfaces) {
117            this.declTemplate = declTemplate;
118            this.typeStr = typeStr;
119            this.superInterfaces = List.from(superInterfaces);
120        }
121
122        String getDecl(ModifierKind mod) {
123            return declTemplate != null ?
124                    declTemplate.replaceAll("#M", mod.modStr) :
125                    "";
126        }
127
128        @Override
129        public boolean subtypeOf(Type that) {
130            return this == that || superInterfaces.contains(that) ||
131                    that == OBJECT;
132        }
133
134        @Override
135        public String asString() {
136            return typeStr;
137        }
138
139        @Override
140        public boolean isClass() {
141            return true;
142        }
143
144        @Override
145        public boolean isInterface() {
146            return false;
147        }
148    }
149
150    enum ModifierKind {
151        NONE(""),
152        FINAL("final");
153
154        String modStr;
155
156        ModifierKind(String modStr) {
157            this.modStr = modStr;
158        }
159    }
160
161    enum CastKind {
162        CLASS("(#C)", 0),
163        INTERFACE("(#I0)", 1),
164        INTERSECTION2("(#C & #I0)", 1),
165        INTERSECTION3("(#C & #I0 & #I1)", 2);
166        //INTERSECTION4("(#C & #I0 & #I1 & #I2)", 3);
167
168        String castTemplate;
169        int interfaceBounds;
170
171        CastKind(String castTemplate, int interfaceBounds) {
172            this.castTemplate = castTemplate;
173            this.interfaceBounds = interfaceBounds;
174        }
175    }
176
177    static class CastInfo {
178        CastKind kind;
179        Type[] types;
180
181        CastInfo(CastKind kind, Type... types) {
182            this.kind = kind;
183            this.types = types;
184        }
185
186        String getCast() {
187            String temp = kind.castTemplate.replaceAll("#C",
188                    types[0].asString());
189            for (int i = 0; i < kind.interfaceBounds ; i++) {
190                temp = temp.replace(String.format("#I%d", i),
191                                    types[i + 1].asString());
192            }
193            return temp;
194        }
195
196        boolean hasDuplicateTypes() {
197            for (int i = 0 ; i < types.length ; i++) {
198                for (int j = 0 ; j < types.length ; j++) {
199                    if (i != j && types[i] == types[j]) {
200                        return true;
201                    }
202                }
203            }
204            return false;
205        }
206
207        boolean compatibleWith(ModifierKind mod, CastInfo that) {
208            for (Type t1 : types) {
209                for (Type t2 : that.types) {
210                    boolean compat =
211                            t1.subtypeOf(t2) ||
212                            t2.subtypeOf(t1) ||
213                            (t1.isInterface() && t2.isInterface()) || //side-cast (1)
214                            (mod == ModifierKind.NONE &&
215                            (t1.isInterface() != t2.isInterface())); //side-cast (2)
216                    if (!compat) return false;
217                }
218            }
219            return true;
220        }
221    }
222
223    public static void main(String... args) throws Exception {
224        for (ModifierKind mod : ModifierKind.values()) {
225            for (CastInfo cast1 : allCastInfo()) {
226                for (CastInfo cast2 : allCastInfo()) {
227                    pool.execute(
228                        new IntersectionTypeCastTest(mod, cast1, cast2));
229                }
230            }
231        }
232        checkAfterExec();
233    }
234
235    static List<CastInfo> allCastInfo() {
236        ListBuffer<CastInfo> buf = ListBuffer.lb();
237        for (CastKind kind : CastKind.values()) {
238            for (ClassKind clazz : ClassKind.values()) {
239                if (kind == CastKind.INTERFACE && clazz != ClassKind.OBJECT) {
240                    continue;
241                } else if (kind.interfaceBounds == 0) {
242                    buf.append(new CastInfo(kind, clazz));
243                    continue;
244                } else {
245                    for (InterfaceKind intf1 : InterfaceKind.values()) {
246                        if (kind.interfaceBounds == 1) {
247                            buf.append(new CastInfo(kind, clazz, intf1));
248                            continue;
249                        } else {
250                            for (InterfaceKind intf2 : InterfaceKind.values()) {
251                                if (kind.interfaceBounds == 2) {
252                                    buf.append(
253                                            new CastInfo(kind, clazz, intf1, intf2));
254                                    continue;
255                                } else {
256                                    for (InterfaceKind intf3 : InterfaceKind.values()) {
257                                        buf.append(
258                                                new CastInfo(kind, clazz, intf1,
259                                                             intf2, intf3));
260                                        continue;
261                                    }
262                                }
263                            }
264                        }
265                    }
266                }
267            }
268        }
269        return buf.toList();
270    }
271
272    ModifierKind mod;
273    CastInfo cast1, cast2;
274    JavaSource source;
275    DiagnosticChecker diagChecker;
276
277    IntersectionTypeCastTest(ModifierKind mod, CastInfo cast1, CastInfo cast2) {
278        this.mod = mod;
279        this.cast1 = cast1;
280        this.cast2 = cast2;
281        this.source = new JavaSource();
282        this.diagChecker = new DiagnosticChecker();
283    }
284
285    @Override
286    public void run() {
287        final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
288
289        JavacTask ct = (JavacTask)tool.getTask(null, fm.get(), diagChecker,
290                Arrays.asList("-XDallowIntersectionTypes"),
291                null, Arrays.asList(source));
292        try {
293            ct.analyze();
294        } catch (Throwable ex) {
295            throw new AssertionError("Error thrown when compiling the following code:\n" +
296                    source.getCharContent(true));
297        }
298        check();
299    }
300
301    class JavaSource extends SimpleJavaFileObject {
302
303        String bodyTemplate = "class Test {\n" +
304                              "   void test() {\n" +
305                              "      Object o = #C1#C2null;\n" +
306                              "   } }";
307
308        String source = "";
309
310        public JavaSource() {
311            super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
312            for (ClassKind ck : ClassKind.values()) {
313                source += ck.getDecl(mod);
314            }
315            for (InterfaceKind ik : InterfaceKind.values()) {
316                source += ik.declStr;
317            }
318            source += bodyTemplate.replaceAll("#C1", cast1.getCast()).
319                    replaceAll("#C2", cast2.getCast());
320        }
321
322        @Override
323        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
324            return source;
325        }
326    }
327
328    void check() {
329        checkCount.incrementAndGet();
330
331        boolean errorExpected = cast1.hasDuplicateTypes() ||
332                cast2.hasDuplicateTypes();
333
334        errorExpected |= !cast2.compatibleWith(mod, cast1);
335
336        if (errorExpected != diagChecker.errorFound) {
337            throw new Error("invalid diagnostics for source:\n" +
338                source.getCharContent(true) +
339                "\nFound error: " + diagChecker.errorFound +
340                "\nExpected error: " + errorExpected);
341        }
342    }
343
344    static class DiagnosticChecker
345        implements javax.tools.DiagnosticListener<JavaFileObject> {
346
347        boolean errorFound;
348
349        public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
350            if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
351                errorFound = true;
352            }
353        }
354    }
355
356}
357