mirror of
https://github.com/openjdk/jdk.git
synced 2026-04-28 15:51:02 +00:00
8148354: Errors targeting functional interface intersection types
Reviewed-by: mcimadamore, dlsmith
This commit is contained in:
parent
53ec88908c
commit
ec167413f9
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2018, 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
|
||||
@ -97,7 +97,6 @@ public class Types {
|
||||
JCDiagnostic.Factory diags;
|
||||
List<Warner> warnStack = List.nil();
|
||||
final Name capturedName;
|
||||
private final FunctionDescriptorLookupError functionDescriptorLookupError;
|
||||
|
||||
public final Warner noWarnings;
|
||||
|
||||
@ -122,7 +121,6 @@ public class Types {
|
||||
capturedName = names.fromString("<captured wildcard>");
|
||||
messages = JavacMessages.instance(context);
|
||||
diags = JCDiagnostic.Factory.instance(context);
|
||||
functionDescriptorLookupError = new FunctionDescriptorLookupError();
|
||||
noWarnings = new Warner(null);
|
||||
}
|
||||
// </editor-fold>
|
||||
@ -796,7 +794,7 @@ public class Types {
|
||||
}
|
||||
|
||||
FunctionDescriptorLookupError failure(JCDiagnostic diag) {
|
||||
return functionDescriptorLookupError.setMessage(diag);
|
||||
return new FunctionDescriptorLookupError().setMessage(diag);
|
||||
}
|
||||
}
|
||||
|
||||
@ -887,12 +885,12 @@ public class Types {
|
||||
* main purposes: (i) checking well-formedness of a functional interface;
|
||||
* (ii) perform functional interface bridge calculation.
|
||||
*/
|
||||
public ClassSymbol makeFunctionalInterfaceClass(Env<AttrContext> env, Name name, List<Type> targets, long cflags) {
|
||||
if (targets.isEmpty()) {
|
||||
public ClassSymbol makeFunctionalInterfaceClass(Env<AttrContext> env, Name name, Type target, long cflags) {
|
||||
if (target == null || target == syms.unknownType) {
|
||||
return null;
|
||||
}
|
||||
Symbol descSym = findDescriptorSymbol(targets.head.tsym);
|
||||
Type descType = findDescriptorType(targets.head);
|
||||
Symbol descSym = findDescriptorSymbol(target.tsym);
|
||||
Type descType = findDescriptorType(target);
|
||||
ClassSymbol csym = new ClassSymbol(cflags, name, env.enclClass.sym.outermostClass());
|
||||
csym.completer = Completer.NULL_COMPLETER;
|
||||
csym.members_field = WriteableScope.create(csym);
|
||||
@ -900,7 +898,9 @@ public class Types {
|
||||
csym.members_field.enter(instDescSym);
|
||||
Type.ClassType ctype = new Type.ClassType(Type.noType, List.nil(), csym);
|
||||
ctype.supertype_field = syms.objectType;
|
||||
ctype.interfaces_field = targets;
|
||||
ctype.interfaces_field = target.isIntersection() ?
|
||||
directSupertypes(target) :
|
||||
List.of(target);
|
||||
csym.type = ctype;
|
||||
csym.sourcefile = ((ClassSymbol)csym.owner).sourcefile;
|
||||
return csym;
|
||||
|
||||
@ -2656,12 +2656,29 @@ public class Attr extends JCTree.Visitor {
|
||||
* the target will be updated to SAM
|
||||
*/
|
||||
currentTarget = targetChecker.visit(currentTarget, that);
|
||||
if (explicitParamTypes != null) {
|
||||
currentTarget = infer.instantiateFunctionalInterface(that,
|
||||
currentTarget, explicitParamTypes, resultInfo.checkContext);
|
||||
if (!currentTarget.isIntersection()) {
|
||||
if (explicitParamTypes != null) {
|
||||
currentTarget = infer.instantiateFunctionalInterface(that,
|
||||
currentTarget, explicitParamTypes, resultInfo.checkContext);
|
||||
}
|
||||
currentTarget = types.removeWildcards(currentTarget);
|
||||
lambdaType = types.findDescriptorType(currentTarget);
|
||||
} else {
|
||||
IntersectionClassType ict = (IntersectionClassType)currentTarget;
|
||||
ListBuffer<Type> components = new ListBuffer<>();
|
||||
for (Type bound : ict.getExplicitComponents()) {
|
||||
if (explicitParamTypes != null) {
|
||||
bound = infer.instantiateFunctionalInterface(that,
|
||||
bound, explicitParamTypes, resultInfo.checkContext);
|
||||
}
|
||||
bound = types.removeWildcards(bound);
|
||||
components.add(bound);
|
||||
}
|
||||
currentTarget = types.makeIntersectionType(components.toList());
|
||||
currentTarget.tsym.flags_field |= INTERFACE;
|
||||
lambdaType = types.findDescriptorType(currentTarget);
|
||||
}
|
||||
currentTarget = types.removeWildcards(currentTarget);
|
||||
lambdaType = types.findDescriptorType(currentTarget);
|
||||
|
||||
} else {
|
||||
currentTarget = Type.recoveryType;
|
||||
lambdaType = fallbackDescriptorType(that);
|
||||
@ -2701,27 +2718,11 @@ public class Attr extends JCTree.Visitor {
|
||||
}
|
||||
|
||||
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 (bound.tsym == syms.objectType.tsym) {
|
||||
continue;
|
||||
}
|
||||
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
|
||||
types.findDescriptorSymbol(makeNotionalInterface(ict, pos));
|
||||
return ict;
|
||||
}
|
||||
|
||||
private TypeSymbol makeNotionalInterface(IntersectionClassType ict) {
|
||||
private TypeSymbol makeNotionalInterface(IntersectionClassType ict, DiagnosticPosition pos) {
|
||||
ListBuffer<Type> targs = new ListBuffer<>();
|
||||
ListBuffer<Type> supertypes = new ListBuffer<>();
|
||||
for (Type i : ict.interfaces_field) {
|
||||
@ -2735,11 +2736,6 @@ public class Attr extends JCTree.Visitor {
|
||||
notionalIntf.tsym.flags_field |= INTERFACE;
|
||||
return notionalIntf.tsym;
|
||||
}
|
||||
|
||||
private void reportIntersectionError(DiagnosticPosition pos, String key, Object... args) {
|
||||
resultInfo.checkContext.report(pos,
|
||||
diags.fragment(Fragments.BadIntersectionTargetForFunctionalExpr(diags.fragment(key, args))));
|
||||
}
|
||||
};
|
||||
|
||||
private Type fallbackDescriptorType(JCExpression tree) {
|
||||
@ -3283,20 +3279,9 @@ public class Attr extends JCTree.Visitor {
|
||||
inferenceContext -> setFunctionalInfo(env, fExpr, pt, inferenceContext.asInstType(descriptorType),
|
||||
inferenceContext.asInstType(primaryTarget), checkContext));
|
||||
} else {
|
||||
ListBuffer<Type> targets = new ListBuffer<>();
|
||||
if (pt.hasTag(CLASS)) {
|
||||
if (pt.isCompound()) {
|
||||
targets.append(types.removeWildcards(primaryTarget)); //this goes first
|
||||
for (Type t : ((IntersectionClassType)pt()).interfaces_field) {
|
||||
if (t != primaryTarget) {
|
||||
targets.append(types.removeWildcards(t));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
targets.append(types.removeWildcards(primaryTarget));
|
||||
}
|
||||
fExpr.target = primaryTarget;
|
||||
}
|
||||
fExpr.targets = targets.toList();
|
||||
if (checkContext.deferredAttrContext().mode == DeferredAttr.AttrMode.CHECK &&
|
||||
pt != Type.recoveryType) {
|
||||
//check that functional interface class is well-formed
|
||||
@ -3306,7 +3291,7 @@ public class Attr extends JCTree.Visitor {
|
||||
* above.
|
||||
*/
|
||||
ClassSymbol csym = types.makeFunctionalInterfaceClass(env,
|
||||
names.empty, List.of(fExpr.targets.head), ABSTRACT);
|
||||
names.empty, fExpr.target, ABSTRACT);
|
||||
if (csym != null) {
|
||||
chk.checkImplementations(env.tree, csym, csym);
|
||||
try {
|
||||
@ -3317,7 +3302,7 @@ public class Attr extends JCTree.Visitor {
|
||||
types.findDescriptorType(csym.type);
|
||||
} catch (FunctionDescriptorLookupError err) {
|
||||
resultInfo.checkContext.report(fExpr,
|
||||
diags.fragment(Fragments.NoSuitableFunctionalIntfInst(fExpr.targets.head)));
|
||||
diags.fragment(Fragments.NoSuitableFunctionalIntfInst(fExpr.target)));
|
||||
}
|
||||
}
|
||||
} catch (Types.FunctionDescriptorLookupError ex) {
|
||||
@ -5185,8 +5170,8 @@ public class Attr extends JCTree.Visitor {
|
||||
@Override
|
||||
public void visitLambda(JCLambda that) {
|
||||
super.visitLambda(that);
|
||||
if (that.targets == null) {
|
||||
that.targets = List.nil();
|
||||
if (that.target == null) {
|
||||
that.target = syms.unknownType;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5197,8 +5182,8 @@ public class Attr extends JCTree.Visitor {
|
||||
that.sym = new MethodSymbol(0, names.empty, dummyMethodType(),
|
||||
syms.noSymbol);
|
||||
}
|
||||
if (that.targets == null) {
|
||||
that.targets = List.nil();
|
||||
if (that.target == null) {
|
||||
that.target = syms.unknownType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,6 +72,8 @@ import static com.sun.tools.javac.jvm.Pool.DynamicMethod;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
|
||||
import com.sun.tools.javac.code.Type.IntersectionClassType;
|
||||
import com.sun.tools.javac.code.Types.FunctionDescriptorLookupError;
|
||||
import com.sun.tools.javac.main.Option;
|
||||
|
||||
/**
|
||||
@ -929,7 +931,7 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
: expressionNew();
|
||||
|
||||
JCLambda slam = make.Lambda(params.toList(), expr);
|
||||
slam.targets = tree.targets;
|
||||
slam.target = tree.target;
|
||||
slam.type = tree.type;
|
||||
slam.pos = tree.pos;
|
||||
return slam;
|
||||
@ -1111,7 +1113,7 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
int refKind, Symbol refSym, List<JCExpression> indy_args) {
|
||||
JCFunctionalExpression tree = context.tree;
|
||||
//determine the static bsm args
|
||||
MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym);
|
||||
MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.target.tsym);
|
||||
List<Object> staticArgs = List.of(
|
||||
typeToMethodType(samSym.type),
|
||||
new Pool.MethodHandle(refKind, refSym, types),
|
||||
@ -1134,8 +1136,13 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
|
||||
if (context.needsAltMetafactory()) {
|
||||
ListBuffer<Object> markers = new ListBuffer<>();
|
||||
for (Type t : tree.targets.tail) {
|
||||
if (t.tsym != syms.serializableType.tsym) {
|
||||
List<Type> targets = tree.target.isIntersection() ?
|
||||
types.directSupertypes(tree.target) :
|
||||
List.nil();
|
||||
for (Type t : targets) {
|
||||
t = types.erasure(t);
|
||||
if (t.tsym != syms.serializableType.tsym &&
|
||||
t.tsym != syms.objectType.tsym) {
|
||||
markers.append(t.tsym);
|
||||
}
|
||||
}
|
||||
@ -1903,13 +1910,13 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
this.depth = frameStack.size() - 1;
|
||||
this.prev = context();
|
||||
ClassSymbol csym =
|
||||
types.makeFunctionalInterfaceClass(attrEnv, names.empty, tree.targets, ABSTRACT | INTERFACE);
|
||||
types.makeFunctionalInterfaceClass(attrEnv, names.empty, tree.target, ABSTRACT | INTERFACE);
|
||||
this.bridges = types.functionalInterfaceBridges(csym);
|
||||
}
|
||||
|
||||
/** does this functional expression need to be created using alternate metafactory? */
|
||||
boolean needsAltMetafactory() {
|
||||
return tree.targets.length() > 1 ||
|
||||
return tree.target.isIntersection() ||
|
||||
isSerializable() ||
|
||||
bridges.length() > 1;
|
||||
}
|
||||
@ -1919,12 +1926,7 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
if (forceSerializable) {
|
||||
return true;
|
||||
}
|
||||
for (Type target : tree.targets) {
|
||||
if (types.asSuper(target, syms.serializableType.tsym) != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return types.asSuper(tree.target, syms.serializableType.tsym) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2416,7 +2418,7 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
}
|
||||
|
||||
Type bridgedRefSig() {
|
||||
return types.erasure(types.findDescriptorSymbol(tree.targets.head.tsym).type);
|
||||
return types.erasure(types.findDescriptorSymbol(tree.target.tsym).type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2018, 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
|
||||
@ -31,6 +31,8 @@ import com.sun.tools.javac.code.*;
|
||||
import com.sun.tools.javac.code.Attribute.TypeCompound;
|
||||
import com.sun.tools.javac.code.Source.Feature;
|
||||
import com.sun.tools.javac.code.Symbol.*;
|
||||
import com.sun.tools.javac.code.Type.IntersectionClassType;
|
||||
import com.sun.tools.javac.code.Types.FunctionDescriptorLookupError;
|
||||
import com.sun.tools.javac.resources.CompilerProperties.Errors;
|
||||
import com.sun.tools.javac.tree.*;
|
||||
import com.sun.tools.javac.tree.JCTree.*;
|
||||
@ -593,7 +595,11 @@ public class TransTypes extends TreeTranslator {
|
||||
currentMethod = null;
|
||||
tree.params = translate(tree.params);
|
||||
tree.body = translate(tree.body, tree.body.type==null? null : erasure(tree.body.type));
|
||||
tree.type = erasure(tree.type);
|
||||
if (!tree.type.isIntersection()) {
|
||||
tree.type = erasure(tree.type);
|
||||
} else {
|
||||
tree.type = types.erasure(types.findDescriptorSymbol(tree.type.tsym).owner.type);
|
||||
}
|
||||
result = tree;
|
||||
}
|
||||
finally {
|
||||
@ -876,8 +882,11 @@ public class TransTypes extends TreeTranslator {
|
||||
} else {
|
||||
tree.expr = translate(tree.expr, receiverTarget);
|
||||
}
|
||||
|
||||
tree.type = erasure(tree.type);
|
||||
if (!tree.type.isIntersection()) {
|
||||
tree.type = erasure(tree.type);
|
||||
} else {
|
||||
tree.type = types.erasure(types.findDescriptorSymbol(tree.type.tsym).owner.type);
|
||||
}
|
||||
if (tree.varargsElement != null)
|
||||
tree.varargsElement = erasure(tree.varargsElement);
|
||||
result = tree;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2018, 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
|
||||
@ -729,10 +729,10 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
||||
}
|
||||
|
||||
/** list of target types inferred for this functional expression. */
|
||||
public List<Type> targets;
|
||||
public Type target;
|
||||
|
||||
public Type getDescriptorType(Types types) {
|
||||
return targets.nonEmpty() ? types.findDescriptorType(targets.head) : types.createErrorType(null);
|
||||
return target != null ? types.findDescriptorType(target) : types.createErrorType(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* bug 8148354
|
||||
* @summary Errors targeting functional interface intersection types
|
||||
* @compile IntersectionFunctionalButComponentsNotTest.java
|
||||
*/
|
||||
class IntersectionFunctionalButComponentsNotTest {
|
||||
// nor A or B are functional interfaces but the intersection is
|
||||
<T extends Object & A & B> void consume(T arg) { }
|
||||
|
||||
void foo() {
|
||||
consume(System::gc);
|
||||
}
|
||||
|
||||
interface C {
|
||||
void c();
|
||||
}
|
||||
|
||||
interface A extends C {
|
||||
void a();
|
||||
}
|
||||
|
||||
interface B extends C {
|
||||
default void c() { }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* bug 8148354
|
||||
* @summary Errors targeting functional interface intersection types
|
||||
* @compile IntersectionTypeBugTest.java
|
||||
*/
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
class IntersectionTypeBugTest {
|
||||
<T extends Object & Serializable & Consumer<String>> void consume(final T cons, final String s) {}
|
||||
|
||||
void process(final String s) {}
|
||||
|
||||
public void foo() {
|
||||
consume(this::process, "Hello World");
|
||||
}
|
||||
|
||||
// another case
|
||||
static class AnotherTest<T> {
|
||||
void foo() {
|
||||
Object r = (Object & Serializable & R<T>) () -> {};
|
||||
}
|
||||
|
||||
interface R<I> {
|
||||
void foo();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -129,6 +129,8 @@ compiler.note.multiple.elements # needs user code
|
||||
compiler.err.preview.feature.disabled.classfile # preview feature support: needs compilation against classfile
|
||||
compiler.warn.preview.feature.use.classfile # preview feature support: needs compilation against classfile
|
||||
compiler.note.preview.plural.additional # preview feature support: diag test causes intermittent failures (see JDK-8201498)
|
||||
compiler.misc.bad.intersection.target.for.functional.expr # currently not generated, should be removed?
|
||||
compiler.misc.not.an.intf.component
|
||||
|
||||
# The following module-related messages will have to stay on the not-yet list for various reasons:
|
||||
compiler.warn.locn.unknown.file.on.module.path # Never issued ATM (short circuited with an if (false))
|
||||
|
||||
@ -21,9 +21,9 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
// key: compiler.misc.not.a.functional.intf.1
|
||||
// key: compiler.err.prob.found.req
|
||||
// key: compiler.misc.bad.intersection.target.for.functional.expr
|
||||
// key: compiler.misc.not.an.intf.component
|
||||
// key: compiler.misc.incompatible.abstracts
|
||||
|
||||
class NotAnInterfaceComponent {
|
||||
Object o = (String & Runnable) ()-> { };
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user