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 
41 import com.sun.tools.javac.util.List;
42 
43 import combo.ComboInstance;
44 import combo.ComboParameter;
45 import combo.ComboTask.Result;
46 import combo.ComboTestHelper;
47 
48 import java.io.IOException;
49 
50 public class IntersectionTypeCastTest extends ComboInstance<IntersectionTypeCastTest> {
51 
52     interface Type extends ComboParameter {
subtypeOf(Type that)53         boolean subtypeOf(Type that);
isClass()54         boolean isClass();
isInterface()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 
InterfaceKind(String typeStr, InterfaceKind superInterface)66         InterfaceKind(String typeStr, InterfaceKind superInterface) {
67             this.typeStr = typeStr;
68             this.superInterface = superInterface;
69         }
70 
71         @Override
subtypeOf(Type that)72         public boolean subtypeOf(Type that) {
73             return this == that || superInterface == that ||
74                    that == ClassKind.OBJECT;
75         }
76 
77         @Override
isClass()78         public boolean isClass() {
79             return false;
80         }
81 
82         @Override
isInterface()83         public boolean isInterface() {
84             return true;
85         }
86 
87         @Override
expand(String optParameter)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 
ClassKind(String typeStr, InterfaceKind... superInterfaces)106         ClassKind(String typeStr, InterfaceKind... superInterfaces) {
107             this.typeStr = typeStr;
108             this.superInterfaces = List.from(superInterfaces);
109         }
110 
111         @Override
subtypeOf(Type that)112         public boolean subtypeOf(Type that) {
113             return this == that || superInterfaces.contains(that) ||
114                     that == OBJECT;
115         }
116 
117         @Override
isClass()118         public boolean isClass() {
119             return true;
120         }
121 
122         @Override
isInterface()123         public boolean isInterface() {
124             return false;
125         }
126 
127         @Override
expand(String optParameter)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 
ModifierKind(String modStr)139         ModifierKind(String modStr) {
140             this.modStr = modStr;
141         }
142 
143         @Override
expand(String optParameter)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 
CastKind(String castTemplate, int interfaceBounds)158         CastKind(String castTemplate, int interfaceBounds) {
159             this.castTemplate = castTemplate;
160             this.interfaceBounds = interfaceBounds;
161         }
162 
163         @Override
expand(String optParameter)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 
CastInfo(CastKind kind, Type... types)173         CastInfo(CastKind kind, Type... types) {
174             this.kind = kind;
175             this.types = types;
176         }
177 
hasDuplicateTypes()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 
compatibleWith(ModifierKind mod, CastInfo that)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 
arity()206         private int arity() {
207             return kind.interfaceBounds + 1;
208         }
209     }
210 
main(String... args)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 
isRedundantCast()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 
arityFilter()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         newCompilationTask()
258                 .withSourceFromTemplate(bodyTemplate)
259                 .analyze(this::check);
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