# HG changeset patch # User mcimadamore # Date 1365433169 -3600 # Node ID c635a966ce84c523171d9f786052cc13a520063f # Parent 94a202228ec2e8475ef33b7f98c28a2f29afb90e 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 diff -r 94a202228ec2 -r c635a966ce84 src/share/classes/com/sun/tools/javac/code/Type.java --- a/src/share/classes/com/sun/tools/javac/code/Type.java Mon Apr 08 15:57:10 2013 +0100 +++ b/src/share/classes/com/sun/tools/javac/code/Type.java Mon Apr 08 15:59:29 2013 +0100 @@ -908,6 +908,12 @@ return interfaces_field.prepend(supertype_field); } + public List getExplicitComponents() { + return allInterfaces ? + interfaces_field : + getComponents(); + } + @Override public TypeKind getKind() { return TypeKind.INTERSECTION; diff -r 94a202228ec2 -r c635a966ce84 src/share/classes/com/sun/tools/javac/code/Types.java --- a/src/share/classes/com/sun/tools/javac/code/Types.java Mon Apr 08 15:57:10 2013 +0100 +++ b/src/share/classes/com/sun/tools/javac/code/Types.java Mon Apr 08 15:59:29 2013 +0100 @@ -610,7 +610,7 @@ /** * Scope filter used to skip methods that should be ignored (such as methods - * overridden by j.l.Object) during function interface conversion/marker interface checks + * overridden by j.l.Object) during function interface conversion interface check */ class DescriptorFilter implements Filter { @@ -629,64 +629,6 @@ } }; - // - - /** - * A cache that keeps track of marker interfaces - */ - class MarkerCache { - - private WeakHashMap _map = new WeakHashMap(); - - class Entry { - final boolean isMarkerIntf; - final int prevMark; - - public Entry(boolean isMarkerIntf, - int prevMark) { - this.isMarkerIntf = isMarkerIntf; - this.prevMark = prevMark; - } - - boolean matches(int mark) { - return this.prevMark == mark; - } - } - - boolean get(TypeSymbol origin) throws FunctionDescriptorLookupError { - Entry e = _map.get(origin); - CompoundScope members = membersClosure(origin.type, false); - if (e == null || - !e.matches(members.getMark())) { - boolean isMarkerIntf = isMarkerInterfaceInternal(origin, members); - _map.put(origin, new Entry(isMarkerIntf, members.getMark())); - return isMarkerIntf; - } - else { - return e.isMarkerIntf; - } - } - - /** - * Is given symbol a marker interface - */ - public boolean isMarkerInterfaceInternal(TypeSymbol origin, CompoundScope membersCache) throws FunctionDescriptorLookupError { - return !origin.isInterface() ? - false : - !membersCache.getElements(new DescriptorFilter(origin)).iterator().hasNext(); - } - } - - private MarkerCache markerCache = new MarkerCache(); - - /** - * Is given type a marker interface? - */ - public boolean isMarkerInterface(Type site) { - return markerCache.get(site.tsym); - } - // - // /** * Is t an unchecked subtype of s? @@ -2625,15 +2567,15 @@ public List interfaceCandidates(Type site, MethodSymbol ms) { Filter filter = new MethodFilter(ms, site); List candidates = List.nil(); - for (Symbol s : membersClosure(site, false).getElements(filter)) { - if (!site.tsym.isInterface() && !s.owner.isInterface()) { - return List.of((MethodSymbol)s); - } else if (!candidates.contains(s)) { - candidates = candidates.prepend((MethodSymbol)s); + for (Symbol s : membersClosure(site, false).getElements(filter)) { + if (!site.tsym.isInterface() && !s.owner.isInterface()) { + return List.of((MethodSymbol)s); + } else if (!candidates.contains(s)) { + candidates = candidates.prepend((MethodSymbol)s); + } } + return prune(candidates); } - return prune(candidates); - } public List prune(List methods) { ListBuffer methodsMin = ListBuffer.lb(); diff -r 94a202228ec2 -r c635a966ce84 src/share/classes/com/sun/tools/javac/comp/Attr.java --- a/src/share/classes/com/sun/tools/javac/comp/Attr.java Mon Apr 08 15:57:10 2013 +0100 +++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java Mon Apr 08 15:59:29 2013 +0100 @@ -2273,7 +2273,7 @@ Type lambdaType; if (pt() != Type.recoveryType) { - target = checkIntersectionTarget(that, target, resultInfo.checkContext); + target = targetChecker.visit(target, that); lambdaType = types.findDescriptorType(target); chk.checkFunctionalInterface(that, target); } else { @@ -2281,7 +2281,7 @@ lambdaType = fallbackDescriptorType(that); } - setFunctionalInfo(that, pt(), lambdaType, resultInfo.checkContext.inferenceContext()); + setFunctionalInfo(that, pt(), lambdaType, target, resultInfo.checkContext.inferenceContext()); if (lambdaType.hasTag(FORALL)) { //lambda expression target desc cannot be a generic method @@ -2396,26 +2396,55 @@ } } } - - private Type checkIntersectionTarget(DiagnosticPosition pos, Type pt, CheckContext checkContext) { - if (pt != Type.recoveryType && pt.isCompound()) { - IntersectionClassType ict = (IntersectionClassType)pt; - List bounds = ict.allInterfaces ? - ict.getComponents().tail : - ict.getComponents(); - types.findDescriptorType(bounds.head); //propagate exception outwards! - for (Type bound : bounds.tail) { - if (!types.isMarkerInterface(bound)) { - checkContext.report(pos, diags.fragment("secondary.bound.must.be.marker.intf", bound)); + //where + Types.MapVisitor targetChecker = new Types.MapVisitor() { + + @Override + public Type visitClassType(ClassType t, DiagnosticPosition pos) { + return t.isCompound() ? + visitIntersectionClassType((IntersectionClassType)t, pos) : t; + } + + public Type visitIntersectionClassType(IntersectionClassType ict, DiagnosticPosition pos) { + Symbol desc = types.findDescriptorSymbol(makeNotionalInterface(ict)); + Type target = null; + for (Type bound : ict.getExplicitComponents()) { + TypeSymbol boundSym = bound.tsym; + if (types.isFunctionalInterface(boundSym) && + types.findDescriptorSymbol(boundSym) == desc) { + target = bound; + } else if (!boundSym.isInterface() || (boundSym.flags() & ANNOTATION) != 0) { + //bound must be an interface + reportIntersectionError(pos, "not.an.intf.component", boundSym); + } } + return target != null ? + target : + ict.getExplicitComponents().head; //error recovery } - //for now (translation doesn't support intersection types) - return bounds.head; - } else { - return pt; - } - } - //where + + private TypeSymbol makeNotionalInterface(IntersectionClassType ict) { + ListBuffer targs = ListBuffer.lb(); + ListBuffer supertypes = ListBuffer.lb(); + for (Type i : ict.interfaces_field) { + if (i.isParameterized()) { + targs.appendList(i.tsym.type.allparams()); + } + supertypes.append(i.tsym.type); + } + IntersectionClassType notionalIntf = + (IntersectionClassType)types.makeCompoundType(supertypes.toList()); + notionalIntf.allparams_field = targs.toList(); + notionalIntf.tsym.flags_field |= INTERFACE; + return notionalIntf.tsym; + } + + private void reportIntersectionError(DiagnosticPosition pos, String key, Object... args) { + resultInfo.checkContext.report(pos, diags.fragment("bad.intersection.target.for.functional.expr", + diags.fragment(key, args))); + } + }; + private Type fallbackDescriptorType(JCExpression tree) { switch (tree.getTag()) { case LAMBDA: @@ -2586,7 +2615,7 @@ Type target; Type desc; if (pt() != Type.recoveryType) { - target = checkIntersectionTarget(that, pt(), resultInfo.checkContext); + target = targetChecker.visit(pt(), that); desc = types.findDescriptorType(target); chk.checkFunctionalInterface(that, target); } else { @@ -2594,7 +2623,7 @@ desc = fallbackDescriptorType(that); } - setFunctionalInfo(that, pt(), desc, resultInfo.checkContext.inferenceContext()); + setFunctionalInfo(that, pt(), desc, target, resultInfo.checkContext.inferenceContext()); List argtypes = desc.getParameterTypes(); Pair refResult = @@ -2789,19 +2818,24 @@ * might contain inference variables, we might need to register an hook in the * current inference context. */ - private void setFunctionalInfo(final JCFunctionalExpression fExpr, final Type pt, final Type descriptorType, InferenceContext inferenceContext) { + private void setFunctionalInfo(final JCFunctionalExpression fExpr, final Type pt, + final Type descriptorType, final Type primaryTarget, InferenceContext inferenceContext) { if (inferenceContext.free(descriptorType)) { inferenceContext.addFreeTypeListener(List.of(pt, descriptorType), new FreeTypeListener() { public void typesInferred(InferenceContext inferenceContext) { - setFunctionalInfo(fExpr, pt, inferenceContext.asInstType(descriptorType), inferenceContext); + setFunctionalInfo(fExpr, pt, inferenceContext.asInstType(descriptorType), + inferenceContext.asInstType(primaryTarget), inferenceContext); } }); } else { ListBuffer targets = ListBuffer.lb(); if (pt.hasTag(CLASS)) { if (pt.isCompound()) { + targets.append(primaryTarget.tsym); //this goes first for (Type t : ((IntersectionClassType)pt()).interfaces_field) { - targets.append(t.tsym); + if (t != primaryTarget) { + targets.append(t.tsym); + } } } else { targets.append(pt.tsym); diff -r 94a202228ec2 -r c635a966ce84 src/share/classes/com/sun/tools/javac/resources/compiler.properties --- a/src/share/classes/com/sun/tools/javac/resources/compiler.properties Mon Apr 08 15:57:10 2013 +0100 +++ b/src/share/classes/com/sun/tools/javac/resources/compiler.properties Mon Apr 08 15:59:29 2013 +0100 @@ -216,9 +216,14 @@ compiler.misc.no.suitable.functional.intf.inst=\ cannot infer functional interface descriptor for {0} +# 0: message segment +compiler.misc.bad.intersection.target.for.functional.expr=\ + bad intersection type target for lambda or method reference\n\ + {0} + # 0: type -compiler.misc.secondary.bound.must.be.marker.intf=\ - secondary bound {0} must be a marker interface +compiler.misc.not.an.intf.component=\ + component type {0} is not an interface # 0: symbol kind, 1: message segment compiler.err.invalid.mref=\ diff -r 94a202228ec2 -r c635a966ce84 src/share/classes/com/sun/tools/javac/util/RichDiagnosticFormatter.java --- a/src/share/classes/com/sun/tools/javac/util/RichDiagnosticFormatter.java Mon Apr 08 15:57:10 2013 +0100 +++ b/src/share/classes/com/sun/tools/javac/util/RichDiagnosticFormatter.java Mon Apr 08 15:59:29 2013 +0100 @@ -395,6 +395,9 @@ @Override public String visitClassSymbol(ClassSymbol s, Locale locale) { + if (s.type.isCompound()) { + return visit(s.type, locale); + } String name = nameSimplifier.simplify(s); if (name.length() == 0 || !getConfiguration().isEnabled(RichFormatterFeature.SIMPLE_NAMES)) { @@ -583,7 +586,11 @@ @Override public Void visitClassSymbol(ClassSymbol s, Void ignored) { - nameSimplifier.addUsage(s); + if (s.type.isCompound()) { + typePreprocessor.visit(s.type); + } else { + nameSimplifier.addUsage(s); + } return null; } diff -r 94a202228ec2 -r c635a966ce84 test/tools/javac/diags/examples/NotAnInterfaceComponent.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/diags/examples/NotAnInterfaceComponent.java Mon Apr 08 15:59:29 2013 +0100 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// key: compiler.err.prob.found.req +// key: compiler.misc.bad.intersection.target.for.functional.expr +// key: compiler.misc.not.an.intf.component + +class NotAnInterfaceComponent { + Object o = (Object & Runnable) ()-> { }; +} diff -r 94a202228ec2 -r c635a966ce84 test/tools/javac/diags/examples/SecondaryBoundMustBeMarkerIntf.java --- a/test/tools/javac/diags/examples/SecondaryBoundMustBeMarkerIntf.java Mon Apr 08 15:57:10 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -// key: compiler.err.prob.found.req -// key: compiler.misc.secondary.bound.must.be.marker.intf - -class SecondaryBoundMustBeMarkerInterface { - Runnable r = (Runnable & Comparable)()->{}; -} diff -r 94a202228ec2 -r c635a966ce84 test/tools/javac/lambda/Intersection01.java --- a/test/tools/javac/lambda/Intersection01.java Mon Apr 08 15:57:10 2013 +0100 +++ b/test/tools/javac/lambda/Intersection01.java Mon Apr 08 15:59:29 2013 +0100 @@ -25,7 +25,7 @@ * @test * @bug 8002099 * @summary Add support for intersection types in cast expression - * @compile/fail/ref=Intersection01.out -XDrawDiagnostics Intersection01.java + * @compile Intersection01.java */ class Intersection01 { diff -r 94a202228ec2 -r c635a966ce84 test/tools/javac/lambda/Intersection01.out --- a/test/tools/javac/lambda/Intersection01.out Mon Apr 08 15:57:10 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -Intersection01.java:36:45: compiler.err.prob.found.req: (compiler.misc.not.a.functional.intf.1: java.io.Serializable, (compiler.misc.no.abstracts: kindname.interface, java.io.Serializable)) -Intersection01.java:38:45: compiler.err.prob.found.req: (compiler.misc.not.a.functional.intf.1: java.io.Serializable, (compiler.misc.no.abstracts: kindname.interface, java.io.Serializable)) -2 errors diff -r 94a202228ec2 -r c635a966ce84 test/tools/javac/lambda/intersection/IntersectionTargetTypeTest.java --- a/test/tools/javac/lambda/intersection/IntersectionTargetTypeTest.java Mon Apr 08 15:57:10 2013 +0100 +++ b/test/tools/javac/lambda/intersection/IntersectionTargetTypeTest.java Mon Apr 08 15:59:29 2013 +0100 @@ -28,10 +28,11 @@ */ import com.sun.source.util.JavacTask; -import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import java.net.URI; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import javax.tools.Diagnostic; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; @@ -45,37 +46,45 @@ enum BoundKind { INTF, - CLASS, - SAM, - ZAM; + CLASS; } enum MethodKind { - NONE, - ABSTRACT, - DEFAULT; + NONE(false), + ABSTRACT_M(true), + DEFAULT_M(false), + ABSTRACT_G(true), + DEFAULT_G(false); + + boolean isAbstract; + + MethodKind(boolean isAbstract) { + this.isAbstract = isAbstract; + } } enum TypeKind { - A("interface A { }\n", "A", BoundKind.ZAM), - B("interface B { default void m() { } }\n", "B", BoundKind.ZAM), - C("interface C { void m(); }\n", "C", BoundKind.SAM), - D("interface D extends B { }\n", "D", BoundKind.ZAM), - E("interface E extends C { }\n", "E", BoundKind.SAM), - F("interface F extends C { void g(); }\n", "F", BoundKind.INTF), - G("interface G extends B { void g(); }\n", "G", BoundKind.SAM), - H("interface H extends A { void g(); }\n", "H", BoundKind.SAM), + A("interface A { }\n", "A", BoundKind.INTF, MethodKind.NONE), + B("interface B { default void m() { } }\n", "B", BoundKind.INTF, MethodKind.DEFAULT_M), + C("interface C { void m(); }\n", "C", BoundKind.INTF, MethodKind.ABSTRACT_M), + D("interface D extends B { }\n", "D", BoundKind.INTF, MethodKind.DEFAULT_M), + E("interface E extends C { }\n", "E", BoundKind.INTF, MethodKind.ABSTRACT_M), + F("interface F extends C { void g(); }\n", "F", BoundKind.INTF, MethodKind.ABSTRACT_G, MethodKind.ABSTRACT_M), + G("interface G extends B { void g(); }\n", "G", BoundKind.INTF, MethodKind.ABSTRACT_G, MethodKind.DEFAULT_M), + H("interface H extends A { void g(); }\n", "H", BoundKind.INTF, MethodKind.ABSTRACT_G), OBJECT("", "Object", BoundKind.CLASS), STRING("", "String", BoundKind.CLASS); String declStr; String typeStr; BoundKind boundKind; + MethodKind[] methodKinds; - private TypeKind(String declStr, String typeStr, BoundKind boundKind) { + private TypeKind(String declStr, String typeStr, BoundKind boundKind, MethodKind... methodKinds) { this.declStr = declStr; this.typeStr = typeStr; this.boundKind = boundKind; + this.methodKinds = methodKinds; } boolean compatibleSupertype(TypeKind tk) { @@ -263,14 +272,22 @@ boolean errorExpected = !cInfo.wellFormed(); if (ek.isFunctional) { - //first bound must be a SAM - errorExpected |= cInfo.types[0].boundKind != BoundKind.SAM; - if (cInfo.types.length > 1) { - //additional bounds must be ZAMs - for (int i = 1; i < cInfo.types.length; i++) { - errorExpected |= cInfo.types[i].boundKind != BoundKind.ZAM; + List mks = new ArrayList<>(); + for (TypeKind tk : cInfo.types) { + if (tk.boundKind == BoundKind.CLASS) { + errorExpected = true; + break; + } else { + mks = mergeMethods(mks, Arrays.asList(tk.methodKinds)); } } + int abstractCount = 0; + for (MethodKind mk : mks) { + if (mk.isAbstract) { + abstractCount++; + } + } + errorExpected |= abstractCount != 1; } if (errorExpected != diagChecker.errorFound) { @@ -281,6 +298,32 @@ } } + List mergeMethods(List l1, List l2) { + List mergedMethods = new ArrayList<>(l1); + for (MethodKind mk2 : l2) { + boolean add = !mergedMethods.contains(mk2); + switch (mk2) { + case ABSTRACT_G: + add = add && !mergedMethods.contains(MethodKind.DEFAULT_G); + break; + case ABSTRACT_M: + add = add && !mergedMethods.contains(MethodKind.DEFAULT_M); + break; + case DEFAULT_G: + mergedMethods.remove(MethodKind.ABSTRACT_G); + case DEFAULT_M: + mergedMethods.remove(MethodKind.ABSTRACT_M); + case NONE: + add = false; + break; + } + if (add) { + mergedMethods.add(mk2); + } + } + return mergedMethods; + } + static class DiagnosticChecker implements javax.tools.DiagnosticListener { boolean errorFound;