Mon, 21 Jan 2013 20:14:39 +0000
8006566: Remove transient lambda-related guards from JavacParser
Summary: Remove transitional internal flag for allowing intersection types in cast
Reviewed-by: jjg
1 /*
2 * Copyright (c) 2012, 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 */
24 /*
25 * @test
26 * @bug 8002099
27 * @summary Add support for intersection types in cast expression
28 */
30 import com.sun.source.util.JavacTask;
31 import com.sun.tools.javac.util.List;
32 import com.sun.tools.javac.util.ListBuffer;
33 import java.net.URI;
34 import java.util.Arrays;
35 import javax.tools.Diagnostic;
36 import javax.tools.JavaCompiler;
37 import javax.tools.JavaFileObject;
38 import javax.tools.SimpleJavaFileObject;
39 import javax.tools.StandardJavaFileManager;
40 import javax.tools.ToolProvider;
42 public class IntersectionTargetTypeTest {
44 static int checkCount = 0;
46 enum BoundKind {
47 INTF,
48 CLASS,
49 SAM,
50 ZAM;
51 }
53 enum MethodKind {
54 NONE,
55 ABSTRACT,
56 DEFAULT;
57 }
59 enum TypeKind {
60 A("interface A { }\n", "A", BoundKind.ZAM),
61 B("interface B { default void m() { } }\n", "B", BoundKind.ZAM),
62 C("interface C { void m(); }\n", "C", BoundKind.SAM),
63 D("interface D extends B { }\n", "D", BoundKind.ZAM),
64 E("interface E extends C { }\n", "E", BoundKind.SAM),
65 F("interface F extends C { void g(); }\n", "F", BoundKind.INTF),
66 G("interface G extends B { void g(); }\n", "G", BoundKind.SAM),
67 H("interface H extends A { void g(); }\n", "H", BoundKind.SAM),
68 OBJECT("", "Object", BoundKind.CLASS),
69 STRING("", "String", BoundKind.CLASS);
71 String declStr;
72 String typeStr;
73 BoundKind boundKind;
75 private TypeKind(String declStr, String typeStr, BoundKind boundKind) {
76 this.declStr = declStr;
77 this.typeStr = typeStr;
78 this.boundKind = boundKind;
79 }
81 boolean compatibleSupertype(TypeKind tk) {
82 if (tk == this) return true;
83 switch (tk) {
84 case B:
85 return this != C && this != E && this != F;
86 case C:
87 return this != B && this != C && this != D && this != G;
88 case D: return compatibleSupertype(B);
89 case E:
90 case F: return compatibleSupertype(C);
91 case G: return compatibleSupertype(B);
92 case H: return compatibleSupertype(A);
93 default:
94 return true;
95 }
96 }
97 }
99 enum CastKind {
100 ONE_ARY("(#B0)", 1),
101 TWO_ARY("(#B0 & #B1)", 2),
102 THREE_ARY("(#B0 & #B1 & #B2)", 3);
104 String castTemplate;
105 int nbounds;
107 CastKind(String castTemplate, int nbounds) {
108 this.castTemplate = castTemplate;
109 this.nbounds = nbounds;
110 }
111 }
113 enum ExpressionKind {
114 LAMBDA("()->{}", true),
115 MREF("this::m", true),
116 //COND_LAMBDA("(true ? ()->{} : ()->{})", true), re-enable if spec allows this
117 //COND_MREF("(true ? this::m : this::m)", true),
118 STANDALONE("null", false);
120 String exprString;
121 boolean isFunctional;
123 private ExpressionKind(String exprString, boolean isFunctional) {
124 this.exprString = exprString;
125 this.isFunctional = isFunctional;
126 }
127 }
129 static class CastInfo {
130 CastKind kind;
131 TypeKind[] types;
133 CastInfo(CastKind kind, TypeKind... types) {
134 this.kind = kind;
135 this.types = types;
136 }
138 String getCast() {
139 String temp = kind.castTemplate;
140 for (int i = 0; i < kind.nbounds ; i++) {
141 temp = temp.replace(String.format("#B%d", i), types[i].typeStr);
142 }
143 return temp;
144 }
146 boolean wellFormed() {
147 //check for duplicate types
148 for (int i = 0 ; i < types.length ; i++) {
149 for (int j = 0 ; j < types.length ; j++) {
150 if (i != j && types[i] == types[j]) {
151 return false;
152 }
153 }
154 }
155 //check that classes only appear as first bound
156 boolean classOk = true;
157 for (int i = 0 ; i < types.length ; i++) {
158 if (types[i].boundKind == BoundKind.CLASS &&
159 !classOk) {
160 return false;
161 }
162 classOk = false;
163 }
164 //check that supertypes are mutually compatible
165 for (int i = 0 ; i < types.length ; i++) {
166 for (int j = 0 ; j < types.length ; j++) {
167 if (!types[i].compatibleSupertype(types[j]) && i != j) {
168 return false;
169 }
170 }
171 }
172 return true;
173 }
174 }
176 public static void main(String... args) throws Exception {
177 //create default shared JavaCompiler - reused across multiple compilations
178 JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
179 StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
181 for (CastInfo cInfo : allCastInfo()) {
182 for (ExpressionKind ek : ExpressionKind.values()) {
183 new IntersectionTargetTypeTest(cInfo, ek).run(comp, fm);
184 }
185 }
186 System.out.println("Total check executed: " + checkCount);
187 }
189 static List<CastInfo> allCastInfo() {
190 ListBuffer<CastInfo> buf = ListBuffer.lb();
191 for (CastKind kind : CastKind.values()) {
192 for (TypeKind b1 : TypeKind.values()) {
193 if (kind.nbounds == 1) {
194 buf.append(new CastInfo(kind, b1));
195 continue;
196 } else {
197 for (TypeKind b2 : TypeKind.values()) {
198 if (kind.nbounds == 2) {
199 buf.append(new CastInfo(kind, b1, b2));
200 continue;
201 } else {
202 for (TypeKind b3 : TypeKind.values()) {
203 buf.append(new CastInfo(kind, b1, b2, b3));
204 }
205 }
206 }
207 }
208 }
209 }
210 return buf.toList();
211 }
213 CastInfo cInfo;
214 ExpressionKind ek;
215 JavaSource source;
216 DiagnosticChecker diagChecker;
218 IntersectionTargetTypeTest(CastInfo cInfo, ExpressionKind ek) {
219 this.cInfo = cInfo;
220 this.ek = ek;
221 this.source = new JavaSource();
222 this.diagChecker = new DiagnosticChecker();
223 }
225 class JavaSource extends SimpleJavaFileObject {
227 String bodyTemplate = "class Test {\n" +
228 " void m() { }\n" +
229 " void test() {\n" +
230 " Object o = #C#E;\n" +
231 " } }";
233 String source = "";
235 public JavaSource() {
236 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
237 for (TypeKind tk : TypeKind.values()) {
238 source += tk.declStr;
239 }
240 source += bodyTemplate.replaceAll("#C", cInfo.getCast()).replaceAll("#E", ek.exprString);
241 }
243 @Override
244 public CharSequence getCharContent(boolean ignoreEncodingErrors) {
245 return source;
246 }
247 }
249 void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception {
250 JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker,
251 null, null, Arrays.asList(source));
252 try {
253 ct.analyze();
254 } catch (Throwable ex) {
255 throw new AssertionError("Error thrown when compiling the following code:\n" + source.getCharContent(true));
256 }
257 check();
258 }
260 void check() {
261 checkCount++;
263 boolean errorExpected = !cInfo.wellFormed();
265 if (ek.isFunctional) {
266 //first bound must be a SAM
267 errorExpected |= cInfo.types[0].boundKind != BoundKind.SAM;
268 if (cInfo.types.length > 1) {
269 //additional bounds must be ZAMs
270 for (int i = 1; i < cInfo.types.length; i++) {
271 errorExpected |= cInfo.types[i].boundKind != BoundKind.ZAM;
272 }
273 }
274 }
276 if (errorExpected != diagChecker.errorFound) {
277 throw new Error("invalid diagnostics for source:\n" +
278 source.getCharContent(true) +
279 "\nFound error: " + diagChecker.errorFound +
280 "\nExpected error: " + errorExpected);
281 }
282 }
284 static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
286 boolean errorFound;
288 public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
289 if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
290 errorFound = true;
291 }
292 }
293 }
294 }