test/tools/javac/lambda/intersection/IntersectionTargetTypeTest.java

Mon, 08 Apr 2013 15:59:29 +0100

author
mcimadamore
date
Mon, 08 Apr 2013 15:59:29 +0100
changeset 1678
c635a966ce84
parent 1511
c7c41a044e7c
child 2047
5f915a0c9615
permissions
-rw-r--r--

8010822: Intersection type cast for functional expressions does not follow spec EDR
Summary: Remove support for marker interfaces; redefine intersection type casts to be order-independent
Reviewed-by: jjg

mcimadamore@1436 1 /*
mcimadamore@1436 2 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
mcimadamore@1436 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
mcimadamore@1436 4 *
mcimadamore@1436 5 * This code is free software; you can redistribute it and/or modify it
mcimadamore@1436 6 * under the terms of the GNU General Public License version 2 only, as
mcimadamore@1436 7 * published by the Free Software Foundation.
mcimadamore@1436 8 *
mcimadamore@1436 9 * This code is distributed in the hope that it will be useful, but WITHOUT
mcimadamore@1436 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
mcimadamore@1436 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
mcimadamore@1436 12 * version 2 for more details (a copy is included in the LICENSE file that
mcimadamore@1436 13 * accompanied this code).
mcimadamore@1436 14 *
mcimadamore@1436 15 * You should have received a copy of the GNU General Public License version
mcimadamore@1436 16 * 2 along with this work; if not, write to the Free Software Foundation,
mcimadamore@1436 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
mcimadamore@1436 18 *
mcimadamore@1436 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
mcimadamore@1436 20 * or visit www.oracle.com if you need additional information or have any
mcimadamore@1436 21 * questions.
mcimadamore@1436 22 */
mcimadamore@1436 23
mcimadamore@1436 24 /*
mcimadamore@1436 25 * @test
mcimadamore@1436 26 * @bug 8002099
mcimadamore@1436 27 * @summary Add support for intersection types in cast expression
mcimadamore@1436 28 */
mcimadamore@1436 29
mcimadamore@1436 30 import com.sun.source.util.JavacTask;
mcimadamore@1436 31 import com.sun.tools.javac.util.ListBuffer;
mcimadamore@1436 32 import java.net.URI;
mcimadamore@1678 33 import java.util.ArrayList;
mcimadamore@1436 34 import java.util.Arrays;
mcimadamore@1678 35 import java.util.List;
mcimadamore@1436 36 import javax.tools.Diagnostic;
mcimadamore@1436 37 import javax.tools.JavaCompiler;
mcimadamore@1436 38 import javax.tools.JavaFileObject;
mcimadamore@1436 39 import javax.tools.SimpleJavaFileObject;
mcimadamore@1436 40 import javax.tools.StandardJavaFileManager;
mcimadamore@1436 41 import javax.tools.ToolProvider;
mcimadamore@1436 42
mcimadamore@1436 43 public class IntersectionTargetTypeTest {
mcimadamore@1436 44
mcimadamore@1436 45 static int checkCount = 0;
mcimadamore@1436 46
mcimadamore@1436 47 enum BoundKind {
mcimadamore@1436 48 INTF,
mcimadamore@1678 49 CLASS;
mcimadamore@1436 50 }
mcimadamore@1436 51
mcimadamore@1436 52 enum MethodKind {
mcimadamore@1678 53 NONE(false),
mcimadamore@1678 54 ABSTRACT_M(true),
mcimadamore@1678 55 DEFAULT_M(false),
mcimadamore@1678 56 ABSTRACT_G(true),
mcimadamore@1678 57 DEFAULT_G(false);
mcimadamore@1678 58
mcimadamore@1678 59 boolean isAbstract;
mcimadamore@1678 60
mcimadamore@1678 61 MethodKind(boolean isAbstract) {
mcimadamore@1678 62 this.isAbstract = isAbstract;
mcimadamore@1678 63 }
mcimadamore@1436 64 }
mcimadamore@1436 65
mcimadamore@1436 66 enum TypeKind {
mcimadamore@1678 67 A("interface A { }\n", "A", BoundKind.INTF, MethodKind.NONE),
mcimadamore@1678 68 B("interface B { default void m() { } }\n", "B", BoundKind.INTF, MethodKind.DEFAULT_M),
mcimadamore@1678 69 C("interface C { void m(); }\n", "C", BoundKind.INTF, MethodKind.ABSTRACT_M),
mcimadamore@1678 70 D("interface D extends B { }\n", "D", BoundKind.INTF, MethodKind.DEFAULT_M),
mcimadamore@1678 71 E("interface E extends C { }\n", "E", BoundKind.INTF, MethodKind.ABSTRACT_M),
mcimadamore@1678 72 F("interface F extends C { void g(); }\n", "F", BoundKind.INTF, MethodKind.ABSTRACT_G, MethodKind.ABSTRACT_M),
mcimadamore@1678 73 G("interface G extends B { void g(); }\n", "G", BoundKind.INTF, MethodKind.ABSTRACT_G, MethodKind.DEFAULT_M),
mcimadamore@1678 74 H("interface H extends A { void g(); }\n", "H", BoundKind.INTF, MethodKind.ABSTRACT_G),
mcimadamore@1436 75 OBJECT("", "Object", BoundKind.CLASS),
mcimadamore@1436 76 STRING("", "String", BoundKind.CLASS);
mcimadamore@1436 77
mcimadamore@1436 78 String declStr;
mcimadamore@1436 79 String typeStr;
mcimadamore@1436 80 BoundKind boundKind;
mcimadamore@1678 81 MethodKind[] methodKinds;
mcimadamore@1436 82
mcimadamore@1678 83 private TypeKind(String declStr, String typeStr, BoundKind boundKind, MethodKind... methodKinds) {
mcimadamore@1436 84 this.declStr = declStr;
mcimadamore@1436 85 this.typeStr = typeStr;
mcimadamore@1436 86 this.boundKind = boundKind;
mcimadamore@1678 87 this.methodKinds = methodKinds;
mcimadamore@1436 88 }
mcimadamore@1436 89
mcimadamore@1436 90 boolean compatibleSupertype(TypeKind tk) {
mcimadamore@1436 91 if (tk == this) return true;
mcimadamore@1436 92 switch (tk) {
mcimadamore@1436 93 case B:
mcimadamore@1436 94 return this != C && this != E && this != F;
mcimadamore@1436 95 case C:
mcimadamore@1436 96 return this != B && this != C && this != D && this != G;
mcimadamore@1436 97 case D: return compatibleSupertype(B);
mcimadamore@1436 98 case E:
mcimadamore@1436 99 case F: return compatibleSupertype(C);
mcimadamore@1436 100 case G: return compatibleSupertype(B);
mcimadamore@1436 101 case H: return compatibleSupertype(A);
mcimadamore@1436 102 default:
mcimadamore@1436 103 return true;
mcimadamore@1436 104 }
mcimadamore@1436 105 }
mcimadamore@1436 106 }
mcimadamore@1436 107
mcimadamore@1436 108 enum CastKind {
mcimadamore@1436 109 ONE_ARY("(#B0)", 1),
mcimadamore@1436 110 TWO_ARY("(#B0 & #B1)", 2),
mcimadamore@1436 111 THREE_ARY("(#B0 & #B1 & #B2)", 3);
mcimadamore@1436 112
mcimadamore@1436 113 String castTemplate;
mcimadamore@1436 114 int nbounds;
mcimadamore@1436 115
mcimadamore@1436 116 CastKind(String castTemplate, int nbounds) {
mcimadamore@1436 117 this.castTemplate = castTemplate;
mcimadamore@1436 118 this.nbounds = nbounds;
mcimadamore@1436 119 }
mcimadamore@1436 120 }
mcimadamore@1436 121
mcimadamore@1436 122 enum ExpressionKind {
mcimadamore@1436 123 LAMBDA("()->{}", true),
mcimadamore@1436 124 MREF("this::m", true),
mcimadamore@1436 125 //COND_LAMBDA("(true ? ()->{} : ()->{})", true), re-enable if spec allows this
mcimadamore@1436 126 //COND_MREF("(true ? this::m : this::m)", true),
mcimadamore@1436 127 STANDALONE("null", false);
mcimadamore@1436 128
mcimadamore@1436 129 String exprString;
mcimadamore@1436 130 boolean isFunctional;
mcimadamore@1436 131
mcimadamore@1436 132 private ExpressionKind(String exprString, boolean isFunctional) {
mcimadamore@1436 133 this.exprString = exprString;
mcimadamore@1436 134 this.isFunctional = isFunctional;
mcimadamore@1436 135 }
mcimadamore@1436 136 }
mcimadamore@1436 137
mcimadamore@1436 138 static class CastInfo {
mcimadamore@1436 139 CastKind kind;
mcimadamore@1436 140 TypeKind[] types;
mcimadamore@1436 141
mcimadamore@1436 142 CastInfo(CastKind kind, TypeKind... types) {
mcimadamore@1436 143 this.kind = kind;
mcimadamore@1436 144 this.types = types;
mcimadamore@1436 145 }
mcimadamore@1436 146
mcimadamore@1436 147 String getCast() {
mcimadamore@1436 148 String temp = kind.castTemplate;
mcimadamore@1436 149 for (int i = 0; i < kind.nbounds ; i++) {
mcimadamore@1436 150 temp = temp.replace(String.format("#B%d", i), types[i].typeStr);
mcimadamore@1436 151 }
mcimadamore@1436 152 return temp;
mcimadamore@1436 153 }
mcimadamore@1436 154
mcimadamore@1436 155 boolean wellFormed() {
mcimadamore@1436 156 //check for duplicate types
mcimadamore@1436 157 for (int i = 0 ; i < types.length ; i++) {
mcimadamore@1436 158 for (int j = 0 ; j < types.length ; j++) {
mcimadamore@1436 159 if (i != j && types[i] == types[j]) {
mcimadamore@1436 160 return false;
mcimadamore@1436 161 }
mcimadamore@1436 162 }
mcimadamore@1436 163 }
mcimadamore@1436 164 //check that classes only appear as first bound
mcimadamore@1436 165 boolean classOk = true;
mcimadamore@1436 166 for (int i = 0 ; i < types.length ; i++) {
mcimadamore@1436 167 if (types[i].boundKind == BoundKind.CLASS &&
mcimadamore@1436 168 !classOk) {
mcimadamore@1436 169 return false;
mcimadamore@1436 170 }
mcimadamore@1436 171 classOk = false;
mcimadamore@1436 172 }
mcimadamore@1436 173 //check that supertypes are mutually compatible
mcimadamore@1436 174 for (int i = 0 ; i < types.length ; i++) {
mcimadamore@1436 175 for (int j = 0 ; j < types.length ; j++) {
mcimadamore@1436 176 if (!types[i].compatibleSupertype(types[j]) && i != j) {
mcimadamore@1436 177 return false;
mcimadamore@1436 178 }
mcimadamore@1436 179 }
mcimadamore@1436 180 }
mcimadamore@1436 181 return true;
mcimadamore@1436 182 }
mcimadamore@1436 183 }
mcimadamore@1436 184
mcimadamore@1436 185 public static void main(String... args) throws Exception {
mcimadamore@1436 186 //create default shared JavaCompiler - reused across multiple compilations
mcimadamore@1436 187 JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
mcimadamore@1436 188 StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
mcimadamore@1436 189
mcimadamore@1436 190 for (CastInfo cInfo : allCastInfo()) {
mcimadamore@1436 191 for (ExpressionKind ek : ExpressionKind.values()) {
mcimadamore@1436 192 new IntersectionTargetTypeTest(cInfo, ek).run(comp, fm);
mcimadamore@1436 193 }
mcimadamore@1436 194 }
mcimadamore@1436 195 System.out.println("Total check executed: " + checkCount);
mcimadamore@1436 196 }
mcimadamore@1436 197
mcimadamore@1436 198 static List<CastInfo> allCastInfo() {
mcimadamore@1436 199 ListBuffer<CastInfo> buf = ListBuffer.lb();
mcimadamore@1436 200 for (CastKind kind : CastKind.values()) {
mcimadamore@1436 201 for (TypeKind b1 : TypeKind.values()) {
mcimadamore@1436 202 if (kind.nbounds == 1) {
mcimadamore@1436 203 buf.append(new CastInfo(kind, b1));
mcimadamore@1436 204 continue;
mcimadamore@1436 205 } else {
mcimadamore@1436 206 for (TypeKind b2 : TypeKind.values()) {
mcimadamore@1436 207 if (kind.nbounds == 2) {
mcimadamore@1436 208 buf.append(new CastInfo(kind, b1, b2));
mcimadamore@1436 209 continue;
mcimadamore@1436 210 } else {
mcimadamore@1436 211 for (TypeKind b3 : TypeKind.values()) {
mcimadamore@1436 212 buf.append(new CastInfo(kind, b1, b2, b3));
mcimadamore@1436 213 }
mcimadamore@1436 214 }
mcimadamore@1436 215 }
mcimadamore@1436 216 }
mcimadamore@1436 217 }
mcimadamore@1436 218 }
mcimadamore@1436 219 return buf.toList();
mcimadamore@1436 220 }
mcimadamore@1436 221
mcimadamore@1436 222 CastInfo cInfo;
mcimadamore@1436 223 ExpressionKind ek;
mcimadamore@1436 224 JavaSource source;
mcimadamore@1436 225 DiagnosticChecker diagChecker;
mcimadamore@1436 226
mcimadamore@1436 227 IntersectionTargetTypeTest(CastInfo cInfo, ExpressionKind ek) {
mcimadamore@1436 228 this.cInfo = cInfo;
mcimadamore@1436 229 this.ek = ek;
mcimadamore@1436 230 this.source = new JavaSource();
mcimadamore@1436 231 this.diagChecker = new DiagnosticChecker();
mcimadamore@1436 232 }
mcimadamore@1436 233
mcimadamore@1436 234 class JavaSource extends SimpleJavaFileObject {
mcimadamore@1436 235
mcimadamore@1436 236 String bodyTemplate = "class Test {\n" +
mcimadamore@1436 237 " void m() { }\n" +
mcimadamore@1436 238 " void test() {\n" +
mcimadamore@1436 239 " Object o = #C#E;\n" +
mcimadamore@1436 240 " } }";
mcimadamore@1436 241
mcimadamore@1436 242 String source = "";
mcimadamore@1436 243
mcimadamore@1436 244 public JavaSource() {
mcimadamore@1436 245 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
mcimadamore@1436 246 for (TypeKind tk : TypeKind.values()) {
mcimadamore@1436 247 source += tk.declStr;
mcimadamore@1436 248 }
mcimadamore@1436 249 source += bodyTemplate.replaceAll("#C", cInfo.getCast()).replaceAll("#E", ek.exprString);
mcimadamore@1436 250 }
mcimadamore@1436 251
mcimadamore@1436 252 @Override
mcimadamore@1436 253 public CharSequence getCharContent(boolean ignoreEncodingErrors) {
mcimadamore@1436 254 return source;
mcimadamore@1436 255 }
mcimadamore@1436 256 }
mcimadamore@1436 257
mcimadamore@1436 258 void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception {
mcimadamore@1436 259 JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker,
mcimadamore@1511 260 null, null, Arrays.asList(source));
mcimadamore@1436 261 try {
mcimadamore@1436 262 ct.analyze();
mcimadamore@1436 263 } catch (Throwable ex) {
mcimadamore@1436 264 throw new AssertionError("Error thrown when compiling the following code:\n" + source.getCharContent(true));
mcimadamore@1436 265 }
mcimadamore@1436 266 check();
mcimadamore@1436 267 }
mcimadamore@1436 268
mcimadamore@1436 269 void check() {
mcimadamore@1436 270 checkCount++;
mcimadamore@1436 271
mcimadamore@1436 272 boolean errorExpected = !cInfo.wellFormed();
mcimadamore@1436 273
mcimadamore@1436 274 if (ek.isFunctional) {
mcimadamore@1678 275 List<MethodKind> mks = new ArrayList<>();
mcimadamore@1678 276 for (TypeKind tk : cInfo.types) {
mcimadamore@1678 277 if (tk.boundKind == BoundKind.CLASS) {
mcimadamore@1678 278 errorExpected = true;
mcimadamore@1678 279 break;
mcimadamore@1678 280 } else {
mcimadamore@1678 281 mks = mergeMethods(mks, Arrays.asList(tk.methodKinds));
mcimadamore@1436 282 }
mcimadamore@1436 283 }
mcimadamore@1678 284 int abstractCount = 0;
mcimadamore@1678 285 for (MethodKind mk : mks) {
mcimadamore@1678 286 if (mk.isAbstract) {
mcimadamore@1678 287 abstractCount++;
mcimadamore@1678 288 }
mcimadamore@1678 289 }
mcimadamore@1678 290 errorExpected |= abstractCount != 1;
mcimadamore@1436 291 }
mcimadamore@1436 292
mcimadamore@1436 293 if (errorExpected != diagChecker.errorFound) {
mcimadamore@1436 294 throw new Error("invalid diagnostics for source:\n" +
mcimadamore@1436 295 source.getCharContent(true) +
mcimadamore@1436 296 "\nFound error: " + diagChecker.errorFound +
mcimadamore@1436 297 "\nExpected error: " + errorExpected);
mcimadamore@1436 298 }
mcimadamore@1436 299 }
mcimadamore@1436 300
mcimadamore@1678 301 List<MethodKind> mergeMethods(List<MethodKind> l1, List<MethodKind> l2) {
mcimadamore@1678 302 List<MethodKind> mergedMethods = new ArrayList<>(l1);
mcimadamore@1678 303 for (MethodKind mk2 : l2) {
mcimadamore@1678 304 boolean add = !mergedMethods.contains(mk2);
mcimadamore@1678 305 switch (mk2) {
mcimadamore@1678 306 case ABSTRACT_G:
mcimadamore@1678 307 add = add && !mergedMethods.contains(MethodKind.DEFAULT_G);
mcimadamore@1678 308 break;
mcimadamore@1678 309 case ABSTRACT_M:
mcimadamore@1678 310 add = add && !mergedMethods.contains(MethodKind.DEFAULT_M);
mcimadamore@1678 311 break;
mcimadamore@1678 312 case DEFAULT_G:
mcimadamore@1678 313 mergedMethods.remove(MethodKind.ABSTRACT_G);
mcimadamore@1678 314 case DEFAULT_M:
mcimadamore@1678 315 mergedMethods.remove(MethodKind.ABSTRACT_M);
mcimadamore@1678 316 case NONE:
mcimadamore@1678 317 add = false;
mcimadamore@1678 318 break;
mcimadamore@1678 319 }
mcimadamore@1678 320 if (add) {
mcimadamore@1678 321 mergedMethods.add(mk2);
mcimadamore@1678 322 }
mcimadamore@1678 323 }
mcimadamore@1678 324 return mergedMethods;
mcimadamore@1678 325 }
mcimadamore@1678 326
mcimadamore@1436 327 static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
mcimadamore@1436 328
mcimadamore@1436 329 boolean errorFound;
mcimadamore@1436 330
mcimadamore@1436 331 public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
mcimadamore@1436 332 if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
mcimadamore@1436 333 errorFound = true;
mcimadamore@1436 334 }
mcimadamore@1436 335 }
mcimadamore@1436 336 }
mcimadamore@1436 337 }

mercurial