8058112: Invalid BootstrapMethod for constructor/method reference

Bridge method references with functional interface method parameters of intersection type

Reviewed-by: vromero, dlsmith
This commit is contained in:
Robert Field 2014-11-24 14:52:46 -08:00
parent 26298f1124
commit 634c33938c
4 changed files with 306 additions and 41 deletions

View File

@ -40,6 +40,7 @@ import com.sun.tools.javac.code.Symbol.VarSymbol;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Type.MethodType;
import com.sun.tools.javac.code.Type.TypeVar;
import com.sun.tools.javac.code.Types;
import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzerPreprocessor.*;
import com.sun.tools.javac.comp.Lower.BasicFreeVarCollector;
@ -60,6 +61,7 @@ import static com.sun.tools.javac.code.Flags.*;
import static com.sun.tools.javac.code.Kinds.Kind.*;
import static com.sun.tools.javac.code.TypeTag.*;
import static com.sun.tools.javac.tree.JCTree.Tag.*;
import javax.lang.model.type.TypeKind;
/**
* This pass desugars lambda expressions into static methods
@ -759,49 +761,10 @@ public class LambdaToMethod extends TreeTranslator {
int prevPos = make.pos;
try {
make.at(tree);
Type samDesc = localContext.bridgedRefSig();
List<Type> samPTypes = samDesc.getParameterTypes();
// an extra argument is prepended in the case where the member
// reference is an unbound instance method reference (in which
// case the receiver expression in passed.
VarSymbol rcvr;
switch (tree.kind) {
case BOUND:
rcvr = addParameter("rec$", tree.getQualifierExpression().type, false);
receiverExpression = attr.makeNullCheck(tree.getQualifierExpression());
break;
case UNBOUND:
rcvr = addParameter("rec$", samPTypes.head, false);
samPTypes = samPTypes.tail;
break;
default:
rcvr = null;
break;
}
// generate the parameter list for the coverted member reference.
// the signature will match the signature of the target sam descriptor
List<Type> refPTypes = tree.sym.type.getParameterTypes();
int refSize = refPTypes.size();
int samSize = samPTypes.size();
// Last parameter to copy from referenced method
int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize;
List<Type> l = refPTypes;
// Use parameter types of the referenced method, excluding final var args
for (int i = 0; l.nonEmpty() && i < last; ++i) {
addParameter("x$" + i, l.head, true);
l = l.tail;
}
// Flatten out the var args
for (int i = last; i < samSize; ++i) {
addParameter("xva$" + i, tree.varargsElement, true);
}
//body generation - this can be either a method call or a
//new instance creation expression, depending on the member reference kind
VarSymbol rcvr = addParametersReturnReceiver();
JCExpression expr = (tree.getMode() == ReferenceMode.INVOKE)
? expressionInvoke(rcvr)
: expressionNew();
@ -816,6 +779,78 @@ public class LambdaToMethod extends TreeTranslator {
}
}
/**
* Generate the parameter list for the converted member reference.
*
* @return The receiver variable symbol, if any
*/
VarSymbol addParametersReturnReceiver() {
Type samDesc = localContext.bridgedRefSig();
List<Type> samPTypes = samDesc.getParameterTypes();
List<Type> descPTypes = tree.getDescriptorType(types).getParameterTypes();
// Determine the receiver, if any
VarSymbol rcvr;
switch (tree.kind) {
case BOUND:
// The receiver is explicit in the method reference
rcvr = addParameter("rec$", tree.getQualifierExpression().type, false);
receiverExpression = attr.makeNullCheck(tree.getQualifierExpression());
break;
case UNBOUND:
// The receiver is the first parameter, extract it and
// adjust the SAM and unerased type lists accordingly
rcvr = addParameter("rec$", samDesc.getParameterTypes().head, false);
samPTypes = samPTypes.tail;
descPTypes = descPTypes.tail;
break;
default:
rcvr = null;
break;
}
List<Type> implPTypes = tree.sym.type.getParameterTypes();
int implSize = implPTypes.size();
int samSize = samPTypes.size();
// Last parameter to copy from referenced method, exclude final var args
int last = localContext.needsVarArgsConversion() ? implSize - 1 : implSize;
// Failsafe -- assure match-up
boolean checkForIntersection = tree.varargsElement != null || implSize == descPTypes.size();
// Use parameter types of the implementation method unless the unerased
// SAM parameter type is an intersection type, in that case use the
// erased SAM parameter type so that the supertype relationship
// the implementation method parameters is not obscured.
// Note: in this loop, the lists implPTypes, samPTypes, and descPTypes
// are used as pointers to the current parameter type information
// and are thus not usable afterwards.
for (int i = 0; implPTypes.nonEmpty() && i < last; ++i) {
// By default use the implementation method parmeter type
Type parmType = implPTypes.head;
// If the unerased parameter type is a type variable whose
// bound is an intersection (eg. <T extends A & B>) then
// use the SAM parameter type
if (checkForIntersection && descPTypes.head.getKind() == TypeKind.TYPEVAR) {
TypeVar tv = (TypeVar) descPTypes.head;
if (tv.bound.getKind() == TypeKind.INTERSECTION) {
parmType = samPTypes.head;
}
}
addParameter("x$" + i, parmType, true);
// Advance to the next parameter
implPTypes = implPTypes.tail;
samPTypes = samPTypes.tail;
descPTypes = descPTypes.tail;
}
// Flatten out the var args
for (int i = last; i < samSize; ++i) {
addParameter("xva$" + i, tree.varargsElement, true);
}
return rcvr;
}
JCExpression getReceiverExpression() {
return receiverExpression;
}
@ -2063,12 +2098,36 @@ public class LambdaToMethod extends TreeTranslator {
types.isSignaturePolymorphic((MethodSymbol)tree.sym);
}
/**
* Erasure destroys the implementation parameter subtype
* relationship for intersection types
*/
boolean interfaceParameterIsIntersectionType() {
List<Type> tl = tree.getDescriptorType(types).getParameterTypes();
if (tree.kind == ReferenceKind.UNBOUND) {
tl = tl.tail;
}
for (; tl.nonEmpty(); tl = tl.tail) {
Type pt = tl.head;
if (pt.getKind() == TypeKind.TYPEVAR) {
TypeVar tv = (TypeVar) pt;
if (tv.bound.getKind() == TypeKind.INTERSECTION) {
return true;
}
}
}
return false;
}
/**
* Does this reference need to be converted to a lambda
* (i.e. var args need to be expanded or "super" is used)
*/
final boolean needsConversionToLambda() {
return isSuper || needsVarArgsConversion() || isArrayOp() ||
return interfaceParameterIsIntersectionType() ||
isSuper ||
needsVarArgsConversion() ||
isArrayOp() ||
isPrivateInOtherClass() ||
!receiverAccessible() ||
(tree.getMode() == ReferenceMode.NEW &&

View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2014, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 8058112
* @summary Invalid BootstrapMethod for constructor/method reference
*/
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import static java.util.stream.Collectors.toList;
public class MethodReferenceIntersection1 {
public static void main(String[] args) {
MethodReferenceIntersection1 main = new MethodReferenceIntersection1();
List<Info_MRI1> list = main.toInfoListError(Arrays.asList(new Base_MRI1()));
System.out.printf("result %d\n", list.size());
}
public <H extends B_MRI1 & A_MRI1> List<Info_MRI1> toInfoListError(List<H> list) {
Comparator<B_MRI1> byNameComparator =
(B_MRI1 b1, B_MRI1 b2) -> b1.getB().compareToIgnoreCase(b2.getB());
return list.stream().sorted(byNameComparator).map(Info_MRI1::new).collect(toList());
}
public <H extends B_MRI1 & A_MRI1> List<Info_MRI1> toInfoListWorks(List<H> list) {
Comparator<B_MRI1> byNameComparator =
(B_MRI1 b1, B_MRI1 b2) -> b1.getB().compareToIgnoreCase(b2.getB());
return list.stream().sorted(byNameComparator).map(s -> new Info_MRI1(s)).collect(toList());
}
}
interface B_MRI1 {
public String getB();
}
interface A_MRI1 {
public long getA();
}
class Info_MRI1 {
private final long a;
private final String b;
<H extends A_MRI1 & B_MRI1> Info_MRI1(H h) {
a = h.getA();
b = h.getB();
}
}
class Base_MRI1 implements A_MRI1, B_MRI1 {
@Override
public long getA() {
return 7L;
}
@Override
public String getB() {
return "hello";
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2014, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 8058112
* @summary Invalid BootstrapMethod for constructor/method reference
*/
import java.util.function.Function;
public class MethodReferenceIntersection2 {
interface B { }
interface A { }
static class C implements A, B { }
static class Info {
<H extends A & B> Info(H h) { }
static <H extends A & B> Info info(H h) {
return new Info(h);
}
}
public static void main(String[] args) {
test();
}
// Note the switch in order compared to that on Info
static <H extends B & A> void test() {
Function<H, Info> f1L = _h -> new Info(_h);
Function<H, Info> f1 = Info::new;
Function<H, Info> f2L = _h -> Info.info(_h);
Function<H, Info> f2 = Info::info;
H c = (H) new C();
if(f1.apply(c) instanceof Info &&
f2.apply(c) instanceof Info) {
System.out.println("Passes.");
} else {
throw new AssertionError();
}
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2014, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 8058112
* @summary Invalid BootstrapMethod for constructor/method reference
*/
/**
* @author Remi Forax
*/
public class MethodReferenceIntersection3 {
interface A {}
interface Foo {
<T extends Object & A> void foo(T t);
}
static <T extends A> void bar(T t) {
}
public static void main(String[] args) {
Foo foo = MethodReferenceIntersection3::bar;
foo.foo(new A(){});
}
}