8148354: Errors targeting functional interface intersection types

Reviewed-by: mcimadamore, dlsmith
This commit is contained in:
Vicente Romero 2018-05-18 09:22:31 -07:00
parent 53ec88908c
commit ec167413f9
9 changed files with 178 additions and 78 deletions

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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() { }
}
}

View File

@ -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();
}
}
}

View File

@ -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))

View File

@ -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) ()-> { };