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
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Type.java Mon Apr 08 15:57:10 2013 +0100
+++ b/langtools/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<Type> getExplicitComponents() {
+ return allInterfaces ?
+ interfaces_field :
+ getComponents();
+ }
+
@Override
public TypeKind getKind() {
return TypeKind.INTERSECTION;
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Types.java Mon Apr 08 15:57:10 2013 +0100
+++ b/langtools/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<Symbol> {
@@ -629,64 +629,6 @@
}
};
- // <editor-fold defaultstate="collapsed" desc="isMarker">
-
- /**
- * A cache that keeps track of marker interfaces
- */
- class MarkerCache {
-
- private WeakHashMap<TypeSymbol, Entry> _map = new WeakHashMap<TypeSymbol, Entry>();
-
- 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);
- }
- // </editor-fold>
-
// <editor-fold defaultstate="collapsed" desc="isSubtype">
/**
* Is t an unchecked subtype of s?
@@ -2625,15 +2567,15 @@
public List<MethodSymbol> interfaceCandidates(Type site, MethodSymbol ms) {
Filter<Symbol> filter = new MethodFilter(ms, site);
List<MethodSymbol> 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<MethodSymbol> prune(List<MethodSymbol> methods) {
ListBuffer<MethodSymbol> methodsMin = ListBuffer.lb();
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java Mon Apr 08 15:57:10 2013 +0100
+++ b/langtools/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<Type> 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<DiagnosticPosition> targetChecker = new Types.MapVisitor<DiagnosticPosition>() {
+
+ @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<Type> targs = ListBuffer.lb();
+ ListBuffer<Type> 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<Type> argtypes = desc.getParameterTypes();
Pair<Symbol, Resolve.ReferenceLookupHelper> 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<TypeSymbol> 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);
--- a/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties Mon Apr 08 15:57:10 2013 +0100
+++ b/langtools/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=\
--- a/langtools/src/share/classes/com/sun/tools/javac/util/RichDiagnosticFormatter.java Mon Apr 08 15:57:10 2013 +0100
+++ b/langtools/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;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/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) ()-> { };
+}
--- a/langtools/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<?>)()->{};
-}
--- a/langtools/test/tools/javac/lambda/Intersection01.java Mon Apr 08 15:57:10 2013 +0100
+++ b/langtools/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 {
--- a/langtools/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
--- a/langtools/test/tools/javac/lambda/intersection/IntersectionTargetTypeTest.java Mon Apr 08 15:57:10 2013 +0100
+++ b/langtools/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<MethodKind> 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<MethodKind> mergeMethods(List<MethodKind> l1, List<MethodKind> l2) {
+ List<MethodKind> 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<JavaFileObject> {
boolean errorFound;