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.file
 32  *          jdk.compiler/com.sun.tools.javac.util
 33  * @build combo.ComboTestHelper
 34 
 35  * @run main IntersectionTypeCastTest
 36  */
 37 
 38 import com.sun.tools.javac.util.List;
 39 
 40 import combo.ComboInstance;
 41 import combo.ComboParameter;
 42 import combo.ComboTask.Result;
 43 import combo.ComboTestHelper;
 44 
 45 import java.io.IOException;
 46 
 47 public class IntersectionTypeCastTest extends ComboInstance<IntersectionTypeCastTest> {
 48 
 49     interface Type extends ComboParameter {
 50         boolean subtypeOf(Type that);
 51         boolean isClass();
 52         boolean isInterface();
 53     }
 54 
 55     enum InterfaceKind implements Type {
 56         A("A", null),
 57         B("B", null),
 58         C("C", A);
 59 
 60         String typeStr;
 61         InterfaceKind superInterface;
 62 
 63         InterfaceKind(String typeStr, InterfaceKind superInterface) {
 64             this.typeStr = typeStr;
 65             this.superInterface = superInterface;
 66         }
 67 
 68         @Override
 69         public boolean subtypeOf(Type that) {
 70             return this == that || superInterface == that ||
 71                    that == ClassKind.OBJECT;
 72         }
 73 
 74         @Override
 75         public boolean isClass() {
 76             return false;
 77         }
 78 
 79         @Override
 80         public boolean isInterface() {
 81             return true;
 82         }
 83 
 84         @Override
 85         public String expand(String optParameter) {
 86             return typeStr;
 87         }
 88     }
 89 
 90     enum ClassKind implements Type {
 91         OBJECT("Object"),
 92         CA("CA", InterfaceKind.A),
 93         CB("CB", InterfaceKind.B),
 94         CAB("CAB", InterfaceKind.A, InterfaceKind.B),
 95         CC("CC", InterfaceKind.C, InterfaceKind.A),
 96         CCA("CCA", InterfaceKind.C, InterfaceKind.A),
 97         CCB("CCB", InterfaceKind.C, InterfaceKind.A, InterfaceKind.B),
 98         CCAB("CCAB", InterfaceKind.C, InterfaceKind.A, InterfaceKind.B);
 99 
100         String typeStr;
101         List<InterfaceKind> superInterfaces;
102 
103         ClassKind(String typeStr, InterfaceKind... superInterfaces) {
104             this.typeStr = typeStr;
105             this.superInterfaces = List.from(superInterfaces);
106         }
107 
108         @Override
109         public boolean subtypeOf(Type that) {
110             return this == that || superInterfaces.contains(that) ||
111                     that == OBJECT;
112         }
113 
114         @Override
115         public boolean isClass() {
116             return true;
117         }
118 
119         @Override
120         public boolean isInterface() {
121             return false;
122         }
123 
124         @Override
125         public String expand(String optParameter) {
126             return typeStr;
127         }
128     }
129 
130     enum ModifierKind implements ComboParameter {
131         NONE(""),
132         FINAL("final");
133 
134         String modStr;
135 
136         ModifierKind(String modStr) {
137             this.modStr = modStr;
138         }
139 
140         @Override
141         public String expand(String optParameter) {
142             return modStr;
143         }
144     }
145 
146     enum CastKind implements ComboParameter {
147         CLASS("(#{CLAZZ#IDX})", 0),
148         INTERFACE("(#{INTF1#IDX})", 1),
149         INTERSECTION2("(#{CLAZZ#IDX} & #{INTF1#IDX})", 1),
150         INTERSECTION3("(#{CLAZZ#IDX} & #{INTF1#IDX} & #{INTF2#IDX})", 2);
151 
152         String castTemplate;
153         int interfaceBounds;
154 
155         CastKind(String castTemplate, int interfaceBounds) {
156             this.castTemplate = castTemplate;
157             this.interfaceBounds = interfaceBounds;
158         }
159 
160         @Override
161         public String expand(String optParameter) {
162             return castTemplate.replaceAll("#IDX", optParameter);
163         }
164     }
165 
166     static class CastInfo {
167         CastKind kind;
168         Type[] types;
169 
170         CastInfo(CastKind kind, Type... types) {
171             this.kind = kind;
172             this.types = types;
173         }
174 
175         boolean hasDuplicateTypes() {
176             for (int i = 0 ; i < arity() ; i++) {
177                 for (int j = 0 ; j < arity() ; j++) {
178                     if (i != j && types[i] == types[j]) {
179                         return true;
180                     }
181                 }
182             }
183             return false;
184         }
185 
186         boolean compatibleWith(ModifierKind mod, CastInfo that) {
187             for (int i = 0 ; i < arity() ; i++) {
188                 Type t1 = types[i];
189                 for (int j = 0 ; j < that.arity() ; j++) {
190                     Type t2 = that.types[j];
191                     boolean compat =
192                             t1.subtypeOf(t2) ||
193                             t2.subtypeOf(t1) ||
194                             (t1.isInterface() && t2.isInterface()) || //side-cast (1)
195                             (mod == ModifierKind.NONE &&
196                             (t1.isInterface() != t2.isInterface())); //side-cast (2)
197                     if (!compat) return false;
198                 }
199             }
200             return true;
201         }
202 
203         private int arity() {
204             return kind.interfaceBounds + 1;
205         }
206     }
207 
208     public static void main(String... args) throws Exception {
209         new ComboTestHelper<IntersectionTypeCastTest>()
210                 .withFilter(IntersectionTypeCastTest::isRedundantCast)
211                 .withFilter(IntersectionTypeCastTest::arityFilter)
212                 .withArrayDimension("CAST", (x, ck, idx) -> x.castKinds[idx] = ck, 2, CastKind.values())
213                 .withDimension("CLAZZ1", (x, ty) -> x.types1[0] = ty, ClassKind.values())
214                 .withDimension("INTF11", (x, ty) -> x.types1[1] = ty, InterfaceKind.values())
215                 .withDimension("INTF21", (x, ty) -> x.types1[2] = ty, InterfaceKind.values())
216                 .withDimension("CLAZZ2", (x, ty) -> x.types2[0] = ty, ClassKind.values())
217                 .withDimension("INTF12", (x, ty) -> x.types2[1] = ty, InterfaceKind.values())
218                 .withDimension("INTF22", (x, ty) -> x.types2[2] = ty, InterfaceKind.values())
219                 .withDimension("MOD", (x, mod) -> x.mod = mod, ModifierKind.values())
220                 .run(IntersectionTypeCastTest::new);
221     }
222 
223     boolean isRedundantCast() {
224         for (int i = 0 ; i < 2 ; i++) {
225             Type[] types = i == 0 ? types1 : types2;
226             if (castKinds[i] == CastKind.INTERFACE && types[0] != ClassKind.OBJECT) {
227                 return false;
228             }
229         }
230         return true;
231     }
232 
233     boolean arityFilter() {
234         for (int i = 0 ; i < 2 ; i++) {
235             int lastPos = castKinds[i].interfaceBounds + 1;
236             Type[] types = i == 0 ? types1 : types2;
237             for (int j = 1; j < types.length; j++) {
238                 boolean shouldBeSet = j < lastPos;
239                 if (!shouldBeSet && (types[j] != InterfaceKind.A)) {
240                     return false;
241                 }
242             }
243         }
244         return true;
245     }
246 
247     ModifierKind mod;
248     CastKind[] castKinds = new CastKind[2];
249     Type[] types1 = new Type[3];
250     Type[] types2 = new Type[3];
251 
252     @Override
253     public void doWork() throws IOException {
254         newCompilationTask()
255                 .withSourceFromTemplate(bodyTemplate)
256                 .analyze(this::check);
257     }
258 
259     String bodyTemplate = "class Test {\n" +
260                           "   void test() {\n" +
261                           "      Object o = #{CAST[0].1}#{CAST[1].2}null;\n" +
262                           "   } }\n" +
263                           "interface A { }\n" +
264                           "interface B { }\n" +
265                           "interface C extends A { }\n" +
266                           "#{MOD} class CA implements A { }\n" +
267                           "#{MOD} class CB implements B { }\n" +
268                           "#{MOD} class CAB implements A, B { }\n" +
269                           "#{MOD} class CC implements C { }\n" +
270                           "#{MOD} class CCA implements C, A { }\n" +
271                           "#{MOD} class CCB implements C, B { }\n" +
272                           "#{MOD} class CCAB implements C, A, B { }";
273 
274     void check(Result<?> res) {
275         CastInfo cast1 = new CastInfo(castKinds[0], types1);
276         CastInfo cast2 = new CastInfo(castKinds[1], types2);
277         boolean errorExpected = cast1.hasDuplicateTypes() ||
278                 cast2.hasDuplicateTypes();
279 
280         errorExpected |= !cast2.compatibleWith(mod, cast1);
281 
282         boolean errorsFound = res.hasErrors();
283         if (errorExpected != errorsFound) {
284             fail("invalid diagnostics for source:\n" +
285                 res.compilationInfo() +
286                 "\nFound error: " + errorsFound +
287                 "\nExpected error: " + errorExpected);
288         }
289     }
290 }