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:
parent
26298f1124
commit
634c33938c
@ -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 &&
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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(){});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user