mcimadamore@1436: /* mcimadamore@1436: * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. mcimadamore@1436: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. mcimadamore@1436: * mcimadamore@1436: * This code is free software; you can redistribute it and/or modify it mcimadamore@1436: * under the terms of the GNU General Public License version 2 only, as mcimadamore@1436: * published by the Free Software Foundation. mcimadamore@1436: * mcimadamore@1436: * This code is distributed in the hope that it will be useful, but WITHOUT mcimadamore@1436: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or mcimadamore@1436: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License mcimadamore@1436: * version 2 for more details (a copy is included in the LICENSE file that mcimadamore@1436: * accompanied this code). mcimadamore@1436: * mcimadamore@1436: * You should have received a copy of the GNU General Public License version mcimadamore@1436: * 2 along with this work; if not, write to the Free Software Foundation, mcimadamore@1436: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. mcimadamore@1436: * mcimadamore@1436: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA mcimadamore@1436: * or visit www.oracle.com if you need additional information or have any mcimadamore@1436: * questions. mcimadamore@1436: */ mcimadamore@1436: mcimadamore@1436: /* mcimadamore@1436: * @test mcimadamore@1436: * @bug 8002099 mcimadamore@1436: * @summary Add support for intersection types in cast expression mcimadamore@1436: */ mcimadamore@1436: mcimadamore@1436: import com.sun.source.util.JavacTask; mcimadamore@1436: import com.sun.tools.javac.util.ListBuffer; mcimadamore@1436: import java.net.URI; mcimadamore@1678: import java.util.ArrayList; mcimadamore@1436: import java.util.Arrays; mcimadamore@1678: import java.util.List; mcimadamore@1436: import javax.tools.Diagnostic; mcimadamore@1436: import javax.tools.JavaCompiler; mcimadamore@1436: import javax.tools.JavaFileObject; mcimadamore@1436: import javax.tools.SimpleJavaFileObject; mcimadamore@1436: import javax.tools.StandardJavaFileManager; mcimadamore@1436: import javax.tools.ToolProvider; mcimadamore@1436: mcimadamore@1436: public class IntersectionTargetTypeTest { mcimadamore@1436: mcimadamore@1436: static int checkCount = 0; mcimadamore@1436: mcimadamore@1436: enum BoundKind { mcimadamore@1436: INTF, mcimadamore@1678: CLASS; mcimadamore@1436: } mcimadamore@1436: mcimadamore@1436: enum MethodKind { mcimadamore@1678: NONE(false), mcimadamore@1678: ABSTRACT_M(true), mcimadamore@1678: DEFAULT_M(false), mcimadamore@1678: ABSTRACT_G(true), mcimadamore@1678: DEFAULT_G(false); mcimadamore@1678: mcimadamore@1678: boolean isAbstract; mcimadamore@1678: mcimadamore@1678: MethodKind(boolean isAbstract) { mcimadamore@1678: this.isAbstract = isAbstract; mcimadamore@1678: } mcimadamore@1436: } mcimadamore@1436: mcimadamore@1436: enum TypeKind { mcimadamore@1678: A("interface A { }\n", "A", BoundKind.INTF, MethodKind.NONE), mcimadamore@1678: B("interface B { default void m() { } }\n", "B", BoundKind.INTF, MethodKind.DEFAULT_M), mcimadamore@1678: C("interface C { void m(); }\n", "C", BoundKind.INTF, MethodKind.ABSTRACT_M), mcimadamore@1678: D("interface D extends B { }\n", "D", BoundKind.INTF, MethodKind.DEFAULT_M), mcimadamore@1678: E("interface E extends C { }\n", "E", BoundKind.INTF, MethodKind.ABSTRACT_M), mcimadamore@1678: F("interface F extends C { void g(); }\n", "F", BoundKind.INTF, MethodKind.ABSTRACT_G, MethodKind.ABSTRACT_M), mcimadamore@1678: G("interface G extends B { void g(); }\n", "G", BoundKind.INTF, MethodKind.ABSTRACT_G, MethodKind.DEFAULT_M), mcimadamore@1678: H("interface H extends A { void g(); }\n", "H", BoundKind.INTF, MethodKind.ABSTRACT_G), mcimadamore@1436: OBJECT("", "Object", BoundKind.CLASS), mcimadamore@1436: STRING("", "String", BoundKind.CLASS); mcimadamore@1436: mcimadamore@1436: String declStr; mcimadamore@1436: String typeStr; mcimadamore@1436: BoundKind boundKind; mcimadamore@1678: MethodKind[] methodKinds; mcimadamore@1436: mcimadamore@1678: private TypeKind(String declStr, String typeStr, BoundKind boundKind, MethodKind... methodKinds) { mcimadamore@1436: this.declStr = declStr; mcimadamore@1436: this.typeStr = typeStr; mcimadamore@1436: this.boundKind = boundKind; mcimadamore@1678: this.methodKinds = methodKinds; mcimadamore@1436: } mcimadamore@1436: mcimadamore@1436: boolean compatibleSupertype(TypeKind tk) { mcimadamore@1436: if (tk == this) return true; mcimadamore@1436: switch (tk) { mcimadamore@1436: case B: mcimadamore@1436: return this != C && this != E && this != F; mcimadamore@1436: case C: mcimadamore@1436: return this != B && this != C && this != D && this != G; mcimadamore@1436: case D: return compatibleSupertype(B); mcimadamore@1436: case E: mcimadamore@1436: case F: return compatibleSupertype(C); mcimadamore@1436: case G: return compatibleSupertype(B); mcimadamore@1436: case H: return compatibleSupertype(A); mcimadamore@1436: default: mcimadamore@1436: return true; mcimadamore@1436: } mcimadamore@1436: } mcimadamore@1436: } mcimadamore@1436: mcimadamore@1436: enum CastKind { mcimadamore@1436: ONE_ARY("(#B0)", 1), mcimadamore@1436: TWO_ARY("(#B0 & #B1)", 2), mcimadamore@1436: THREE_ARY("(#B0 & #B1 & #B2)", 3); mcimadamore@1436: mcimadamore@1436: String castTemplate; mcimadamore@1436: int nbounds; mcimadamore@1436: mcimadamore@1436: CastKind(String castTemplate, int nbounds) { mcimadamore@1436: this.castTemplate = castTemplate; mcimadamore@1436: this.nbounds = nbounds; mcimadamore@1436: } mcimadamore@1436: } mcimadamore@1436: mcimadamore@1436: enum ExpressionKind { mcimadamore@1436: LAMBDA("()->{}", true), mcimadamore@1436: MREF("this::m", true), mcimadamore@1436: //COND_LAMBDA("(true ? ()->{} : ()->{})", true), re-enable if spec allows this mcimadamore@1436: //COND_MREF("(true ? this::m : this::m)", true), mcimadamore@1436: STANDALONE("null", false); mcimadamore@1436: mcimadamore@1436: String exprString; mcimadamore@1436: boolean isFunctional; mcimadamore@1436: mcimadamore@1436: private ExpressionKind(String exprString, boolean isFunctional) { mcimadamore@1436: this.exprString = exprString; mcimadamore@1436: this.isFunctional = isFunctional; mcimadamore@1436: } mcimadamore@1436: } mcimadamore@1436: mcimadamore@1436: static class CastInfo { mcimadamore@1436: CastKind kind; mcimadamore@1436: TypeKind[] types; mcimadamore@1436: mcimadamore@1436: CastInfo(CastKind kind, TypeKind... types) { mcimadamore@1436: this.kind = kind; mcimadamore@1436: this.types = types; mcimadamore@1436: } mcimadamore@1436: mcimadamore@1436: String getCast() { mcimadamore@1436: String temp = kind.castTemplate; mcimadamore@1436: for (int i = 0; i < kind.nbounds ; i++) { mcimadamore@1436: temp = temp.replace(String.format("#B%d", i), types[i].typeStr); mcimadamore@1436: } mcimadamore@1436: return temp; mcimadamore@1436: } mcimadamore@1436: mcimadamore@1436: boolean wellFormed() { mcimadamore@1436: //check for duplicate types mcimadamore@1436: for (int i = 0 ; i < types.length ; i++) { mcimadamore@1436: for (int j = 0 ; j < types.length ; j++) { mcimadamore@1436: if (i != j && types[i] == types[j]) { mcimadamore@1436: return false; mcimadamore@1436: } mcimadamore@1436: } mcimadamore@1436: } mcimadamore@1436: //check that classes only appear as first bound mcimadamore@1436: boolean classOk = true; mcimadamore@1436: for (int i = 0 ; i < types.length ; i++) { mcimadamore@1436: if (types[i].boundKind == BoundKind.CLASS && mcimadamore@1436: !classOk) { mcimadamore@1436: return false; mcimadamore@1436: } mcimadamore@1436: classOk = false; mcimadamore@1436: } mcimadamore@1436: //check that supertypes are mutually compatible mcimadamore@1436: for (int i = 0 ; i < types.length ; i++) { mcimadamore@1436: for (int j = 0 ; j < types.length ; j++) { mcimadamore@1436: if (!types[i].compatibleSupertype(types[j]) && i != j) { mcimadamore@1436: return false; mcimadamore@1436: } mcimadamore@1436: } mcimadamore@1436: } mcimadamore@1436: return true; mcimadamore@1436: } mcimadamore@1436: } mcimadamore@1436: mcimadamore@1436: public static void main(String... args) throws Exception { mcimadamore@1436: //create default shared JavaCompiler - reused across multiple compilations mcimadamore@1436: JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); mcimadamore@1436: StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null); mcimadamore@1436: mcimadamore@1436: for (CastInfo cInfo : allCastInfo()) { mcimadamore@1436: for (ExpressionKind ek : ExpressionKind.values()) { mcimadamore@1436: new IntersectionTargetTypeTest(cInfo, ek).run(comp, fm); mcimadamore@1436: } mcimadamore@1436: } mcimadamore@1436: System.out.println("Total check executed: " + checkCount); mcimadamore@1436: } mcimadamore@1436: mcimadamore@1436: static List allCastInfo() { mcimadamore@1436: ListBuffer buf = ListBuffer.lb(); mcimadamore@1436: for (CastKind kind : CastKind.values()) { mcimadamore@1436: for (TypeKind b1 : TypeKind.values()) { mcimadamore@1436: if (kind.nbounds == 1) { mcimadamore@1436: buf.append(new CastInfo(kind, b1)); mcimadamore@1436: continue; mcimadamore@1436: } else { mcimadamore@1436: for (TypeKind b2 : TypeKind.values()) { mcimadamore@1436: if (kind.nbounds == 2) { mcimadamore@1436: buf.append(new CastInfo(kind, b1, b2)); mcimadamore@1436: continue; mcimadamore@1436: } else { mcimadamore@1436: for (TypeKind b3 : TypeKind.values()) { mcimadamore@1436: buf.append(new CastInfo(kind, b1, b2, b3)); mcimadamore@1436: } mcimadamore@1436: } mcimadamore@1436: } mcimadamore@1436: } mcimadamore@1436: } mcimadamore@1436: } mcimadamore@1436: return buf.toList(); mcimadamore@1436: } mcimadamore@1436: mcimadamore@1436: CastInfo cInfo; mcimadamore@1436: ExpressionKind ek; mcimadamore@1436: JavaSource source; mcimadamore@1436: DiagnosticChecker diagChecker; mcimadamore@1436: mcimadamore@1436: IntersectionTargetTypeTest(CastInfo cInfo, ExpressionKind ek) { mcimadamore@1436: this.cInfo = cInfo; mcimadamore@1436: this.ek = ek; mcimadamore@1436: this.source = new JavaSource(); mcimadamore@1436: this.diagChecker = new DiagnosticChecker(); mcimadamore@1436: } mcimadamore@1436: mcimadamore@1436: class JavaSource extends SimpleJavaFileObject { mcimadamore@1436: mcimadamore@1436: String bodyTemplate = "class Test {\n" + mcimadamore@1436: " void m() { }\n" + mcimadamore@1436: " void test() {\n" + mcimadamore@1436: " Object o = #C#E;\n" + mcimadamore@1436: " } }"; mcimadamore@1436: mcimadamore@1436: String source = ""; mcimadamore@1436: mcimadamore@1436: public JavaSource() { mcimadamore@1436: super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); mcimadamore@1436: for (TypeKind tk : TypeKind.values()) { mcimadamore@1436: source += tk.declStr; mcimadamore@1436: } mcimadamore@1436: source += bodyTemplate.replaceAll("#C", cInfo.getCast()).replaceAll("#E", ek.exprString); mcimadamore@1436: } mcimadamore@1436: mcimadamore@1436: @Override mcimadamore@1436: public CharSequence getCharContent(boolean ignoreEncodingErrors) { mcimadamore@1436: return source; mcimadamore@1436: } mcimadamore@1436: } mcimadamore@1436: mcimadamore@1436: void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception { mcimadamore@1436: JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker, mcimadamore@1511: null, null, Arrays.asList(source)); mcimadamore@1436: try { mcimadamore@1436: ct.analyze(); mcimadamore@1436: } catch (Throwable ex) { mcimadamore@1436: throw new AssertionError("Error thrown when compiling the following code:\n" + source.getCharContent(true)); mcimadamore@1436: } mcimadamore@1436: check(); mcimadamore@1436: } mcimadamore@1436: mcimadamore@1436: void check() { mcimadamore@1436: checkCount++; mcimadamore@1436: mcimadamore@1436: boolean errorExpected = !cInfo.wellFormed(); mcimadamore@1436: mcimadamore@1436: if (ek.isFunctional) { mcimadamore@1678: List mks = new ArrayList<>(); mcimadamore@1678: for (TypeKind tk : cInfo.types) { mcimadamore@1678: if (tk.boundKind == BoundKind.CLASS) { mcimadamore@1678: errorExpected = true; mcimadamore@1678: break; mcimadamore@1678: } else { mcimadamore@1678: mks = mergeMethods(mks, Arrays.asList(tk.methodKinds)); mcimadamore@1436: } mcimadamore@1436: } mcimadamore@1678: int abstractCount = 0; mcimadamore@1678: for (MethodKind mk : mks) { mcimadamore@1678: if (mk.isAbstract) { mcimadamore@1678: abstractCount++; mcimadamore@1678: } mcimadamore@1678: } mcimadamore@1678: errorExpected |= abstractCount != 1; mcimadamore@1436: } mcimadamore@1436: mcimadamore@1436: if (errorExpected != diagChecker.errorFound) { mcimadamore@1436: throw new Error("invalid diagnostics for source:\n" + mcimadamore@1436: source.getCharContent(true) + mcimadamore@1436: "\nFound error: " + diagChecker.errorFound + mcimadamore@1436: "\nExpected error: " + errorExpected); mcimadamore@1436: } mcimadamore@1436: } mcimadamore@1436: mcimadamore@1678: List mergeMethods(List l1, List l2) { mcimadamore@1678: List mergedMethods = new ArrayList<>(l1); mcimadamore@1678: for (MethodKind mk2 : l2) { mcimadamore@1678: boolean add = !mergedMethods.contains(mk2); mcimadamore@1678: switch (mk2) { mcimadamore@1678: case ABSTRACT_G: mcimadamore@1678: add = add && !mergedMethods.contains(MethodKind.DEFAULT_G); mcimadamore@1678: break; mcimadamore@1678: case ABSTRACT_M: mcimadamore@1678: add = add && !mergedMethods.contains(MethodKind.DEFAULT_M); mcimadamore@1678: break; mcimadamore@1678: case DEFAULT_G: mcimadamore@1678: mergedMethods.remove(MethodKind.ABSTRACT_G); mcimadamore@1678: case DEFAULT_M: mcimadamore@1678: mergedMethods.remove(MethodKind.ABSTRACT_M); mcimadamore@1678: case NONE: mcimadamore@1678: add = false; mcimadamore@1678: break; mcimadamore@1678: } mcimadamore@1678: if (add) { mcimadamore@1678: mergedMethods.add(mk2); mcimadamore@1678: } mcimadamore@1678: } mcimadamore@1678: return mergedMethods; mcimadamore@1678: } mcimadamore@1678: mcimadamore@1436: static class DiagnosticChecker implements javax.tools.DiagnosticListener { mcimadamore@1436: mcimadamore@1436: boolean errorFound; mcimadamore@1436: mcimadamore@1436: public void report(Diagnostic diagnostic) { mcimadamore@1436: if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { mcimadamore@1436: errorFound = true; mcimadamore@1436: } mcimadamore@1436: } mcimadamore@1436: } mcimadamore@1436: }