Tue, 25 Nov 2014 13:37:34 -0800
8058112: Invalid BootstrapMethod for constructor/method reference
Summary: Bridge method references with functional interface method parameters of intersection type
Reviewed-by: vromero, dlsmith
1.1 --- a/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Tue Nov 25 08:30:52 2014 -0500 1.2 +++ b/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Tue Nov 25 13:37:34 2014 -0800 1.3 @@ -41,6 +41,7 @@ 1.4 import com.sun.tools.javac.code.Symtab; 1.5 import com.sun.tools.javac.code.Type; 1.6 import com.sun.tools.javac.code.Type.MethodType; 1.7 +import com.sun.tools.javac.code.Type.TypeVar; 1.8 import com.sun.tools.javac.code.Types; 1.9 import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzerPreprocessor.*; 1.10 import com.sun.tools.javac.comp.Lower.BasicFreeVarCollector; 1.11 @@ -61,6 +62,7 @@ 1.12 import static com.sun.tools.javac.code.Kinds.*; 1.13 import static com.sun.tools.javac.code.TypeTag.*; 1.14 import static com.sun.tools.javac.tree.JCTree.Tag.*; 1.15 +import javax.lang.model.type.TypeKind; 1.16 1.17 /** 1.18 * This pass desugars lambda expressions into static methods 1.19 @@ -761,49 +763,10 @@ 1.20 int prevPos = make.pos; 1.21 try { 1.22 make.at(tree); 1.23 - Type samDesc = localContext.bridgedRefSig(); 1.24 - List<Type> samPTypes = samDesc.getParameterTypes(); 1.25 - 1.26 - // an extra argument is prepended in the case where the member 1.27 - // reference is an unbound instance method reference (in which 1.28 - // case the receiver expression in passed. 1.29 - VarSymbol rcvr; 1.30 - switch (tree.kind) { 1.31 - case BOUND: 1.32 - rcvr = addParameter("rec$", tree.getQualifierExpression().type, false); 1.33 - receiverExpression = attr.makeNullCheck(tree.getQualifierExpression()); 1.34 - break; 1.35 - case UNBOUND: 1.36 - rcvr = addParameter("rec$", samPTypes.head, false); 1.37 - samPTypes = samPTypes.tail; 1.38 - break; 1.39 - default: 1.40 - rcvr = null; 1.41 - break; 1.42 - } 1.43 - 1.44 - // generate the parameter list for the coverted member reference. 1.45 - // the signature will match the signature of the target sam descriptor 1.46 - 1.47 - List<Type> refPTypes = tree.sym.type.getParameterTypes(); 1.48 - int refSize = refPTypes.size(); 1.49 - int samSize = samPTypes.size(); 1.50 - // Last parameter to copy from referenced method 1.51 - int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize; 1.52 - 1.53 - List<Type> l = refPTypes; 1.54 - // Use parameter types of the referenced method, excluding final var args 1.55 - for (int i = 0; l.nonEmpty() && i < last; ++i) { 1.56 - addParameter("x$" + i, l.head, true); 1.57 - l = l.tail; 1.58 - } 1.59 - // Flatten out the var args 1.60 - for (int i = last; i < samSize; ++i) { 1.61 - addParameter("xva$" + i, tree.varargsElement, true); 1.62 - } 1.63 1.64 //body generation - this can be either a method call or a 1.65 //new instance creation expression, depending on the member reference kind 1.66 + VarSymbol rcvr = addParametersReturnReceiver(); 1.67 JCExpression expr = (tree.getMode() == ReferenceMode.INVOKE) 1.68 ? expressionInvoke(rcvr) 1.69 : expressionNew(); 1.70 @@ -818,6 +781,78 @@ 1.71 } 1.72 } 1.73 1.74 + /** 1.75 + * Generate the parameter list for the converted member reference. 1.76 + * 1.77 + * @return The receiver variable symbol, if any 1.78 + */ 1.79 + VarSymbol addParametersReturnReceiver() { 1.80 + Type samDesc = localContext.bridgedRefSig(); 1.81 + List<Type> samPTypes = samDesc.getParameterTypes(); 1.82 + List<Type> descPTypes = tree.getDescriptorType(types).getParameterTypes(); 1.83 + 1.84 + // Determine the receiver, if any 1.85 + VarSymbol rcvr; 1.86 + switch (tree.kind) { 1.87 + case BOUND: 1.88 + // The receiver is explicit in the method reference 1.89 + rcvr = addParameter("rec$", tree.getQualifierExpression().type, false); 1.90 + receiverExpression = attr.makeNullCheck(tree.getQualifierExpression()); 1.91 + break; 1.92 + case UNBOUND: 1.93 + // The receiver is the first parameter, extract it and 1.94 + // adjust the SAM and unerased type lists accordingly 1.95 + rcvr = addParameter("rec$", samDesc.getParameterTypes().head, false); 1.96 + samPTypes = samPTypes.tail; 1.97 + descPTypes = descPTypes.tail; 1.98 + break; 1.99 + default: 1.100 + rcvr = null; 1.101 + break; 1.102 + } 1.103 + List<Type> implPTypes = tree.sym.type.getParameterTypes(); 1.104 + int implSize = implPTypes.size(); 1.105 + int samSize = samPTypes.size(); 1.106 + // Last parameter to copy from referenced method, exclude final var args 1.107 + int last = localContext.needsVarArgsConversion() ? implSize - 1 : implSize; 1.108 + 1.109 + // Failsafe -- assure match-up 1.110 + boolean checkForIntersection = tree.varargsElement != null || implSize == descPTypes.size(); 1.111 + 1.112 + // Use parameter types of the implementation method unless the unerased 1.113 + // SAM parameter type is an intersection type, in that case use the 1.114 + // erased SAM parameter type so that the supertype relationship 1.115 + // the implementation method parameters is not obscured. 1.116 + // Note: in this loop, the lists implPTypes, samPTypes, and descPTypes 1.117 + // are used as pointers to the current parameter type information 1.118 + // and are thus not usable afterwards. 1.119 + for (int i = 0; implPTypes.nonEmpty() && i < last; ++i) { 1.120 + // By default use the implementation method parmeter type 1.121 + Type parmType = implPTypes.head; 1.122 + // If the unerased parameter type is a type variable whose 1.123 + // bound is an intersection (eg. <T extends A & B>) then 1.124 + // use the SAM parameter type 1.125 + if (checkForIntersection && descPTypes.head.getKind() == TypeKind.TYPEVAR) { 1.126 + TypeVar tv = (TypeVar) descPTypes.head; 1.127 + if (tv.bound.getKind() == TypeKind.INTERSECTION) { 1.128 + parmType = samPTypes.head; 1.129 + } 1.130 + } 1.131 + addParameter("x$" + i, parmType, true); 1.132 + 1.133 + // Advance to the next parameter 1.134 + implPTypes = implPTypes.tail; 1.135 + samPTypes = samPTypes.tail; 1.136 + descPTypes = descPTypes.tail; 1.137 + } 1.138 + // Flatten out the var args 1.139 + for (int i = last; i < samSize; ++i) { 1.140 + addParameter("xva$" + i, tree.varargsElement, true); 1.141 + } 1.142 + 1.143 + return rcvr; 1.144 + } 1.145 + 1.146 JCExpression getReceiverExpression() { 1.147 return receiverExpression; 1.148 } 1.149 @@ -2067,11 +2102,35 @@ 1.150 } 1.151 1.152 /** 1.153 + * Erasure destroys the implementation parameter subtype 1.154 + * relationship for intersection types 1.155 + */ 1.156 + boolean interfaceParameterIsIntersectionType() { 1.157 + List<Type> tl = tree.getDescriptorType(types).getParameterTypes(); 1.158 + if (tree.kind == ReferenceKind.UNBOUND) { 1.159 + tl = tl.tail; 1.160 + } 1.161 + for (; tl.nonEmpty(); tl = tl.tail) { 1.162 + Type pt = tl.head; 1.163 + if (pt.getKind() == TypeKind.TYPEVAR) { 1.164 + TypeVar tv = (TypeVar) pt; 1.165 + if (tv.bound.getKind() == TypeKind.INTERSECTION) { 1.166 + return true; 1.167 + } 1.168 + } 1.169 + } 1.170 + return false; 1.171 + } 1.172 + 1.173 + /** 1.174 * Does this reference need to be converted to a lambda 1.175 * (i.e. var args need to be expanded or "super" is used) 1.176 */ 1.177 final boolean needsConversionToLambda() { 1.178 - return isSuper || needsVarArgsConversion() || isArrayOp() || 1.179 + return interfaceParameterIsIntersectionType() || 1.180 + isSuper || 1.181 + needsVarArgsConversion() || 1.182 + isArrayOp() || 1.183 isPrivateInOtherClass() || 1.184 !receiverAccessible() || 1.185 (tree.getMode() == ReferenceMode.NEW &&
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/test/tools/javac/lambda/methodReferenceExecution/MethodReferenceIntersection1.java Tue Nov 25 13:37:34 2014 -0800 2.3 @@ -0,0 +1,88 @@ 2.4 +/* 2.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 2.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 2.7 + * 2.8 + * This code is free software; you can redistribute it and/or modify it 2.9 + * under the terms of the GNU General Public License version 2 only, as 2.10 + * published by the Free Software Foundation. Oracle designates this 2.11 + * particular file as subject to the "Classpath" exception as provided 2.12 + * by Oracle in the LICENSE file that accompanied this code. 2.13 + * 2.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 2.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 2.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 2.17 + * version 2 for more details (a copy is included in the LICENSE file that 2.18 + * accompanied this code). 2.19 + * 2.20 + * You should have received a copy of the GNU General Public License version 2.21 + * 2 along with this work; if not, write to the Free Software Foundation, 2.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2.23 + * 2.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2.25 + * or visit www.oracle.com if you need additional information or have any 2.26 + * questions. 2.27 + */ 2.28 + 2.29 +/** 2.30 + * @test 2.31 + * @bug 8058112 2.32 + * @summary Invalid BootstrapMethod for constructor/method reference 2.33 + */ 2.34 + 2.35 +import java.util.Arrays; 2.36 +import java.util.Comparator; 2.37 +import java.util.List; 2.38 + 2.39 +import static java.util.stream.Collectors.toList; 2.40 + 2.41 +public class MethodReferenceIntersection1 { 2.42 + 2.43 + public static void main(String[] args) { 2.44 + MethodReferenceIntersection1 main = new MethodReferenceIntersection1(); 2.45 + List<Info_MRI1> list = main.toInfoListError(Arrays.asList(new Base_MRI1())); 2.46 + System.out.printf("result %d\n", list.size()); 2.47 + } 2.48 + 2.49 + public <H extends B_MRI1 & A_MRI1> List<Info_MRI1> toInfoListError(List<H> list) { 2.50 + Comparator<B_MRI1> byNameComparator = 2.51 + (B_MRI1 b1, B_MRI1 b2) -> b1.getB().compareToIgnoreCase(b2.getB()); 2.52 + return list.stream().sorted(byNameComparator).map(Info_MRI1::new).collect(toList()); 2.53 + } 2.54 + 2.55 + public <H extends B_MRI1 & A_MRI1> List<Info_MRI1> toInfoListWorks(List<H> list) { 2.56 + Comparator<B_MRI1> byNameComparator = 2.57 + (B_MRI1 b1, B_MRI1 b2) -> b1.getB().compareToIgnoreCase(b2.getB()); 2.58 + return list.stream().sorted(byNameComparator).map(s -> new Info_MRI1(s)).collect(toList()); 2.59 + } 2.60 +} 2.61 + 2.62 +interface B_MRI1 { 2.63 + public String getB(); 2.64 +} 2.65 + 2.66 +interface A_MRI1 { 2.67 + public long getA(); 2.68 +} 2.69 + 2.70 +class Info_MRI1 { 2.71 + private final long a; 2.72 + private final String b; 2.73 + 2.74 + <H extends A_MRI1 & B_MRI1> Info_MRI1(H h) { 2.75 + a = h.getA(); 2.76 + b = h.getB(); 2.77 + } 2.78 +} 2.79 + 2.80 +class Base_MRI1 implements A_MRI1, B_MRI1 { 2.81 + 2.82 + @Override 2.83 + public long getA() { 2.84 + return 7L; 2.85 + } 2.86 + 2.87 + @Override 2.88 + public String getB() { 2.89 + return "hello"; 2.90 + } 2.91 +}
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/test/tools/javac/lambda/methodReferenceExecution/MethodReferenceIntersection2.java Tue Nov 25 13:37:34 2014 -0800 3.3 @@ -0,0 +1,68 @@ 3.4 +/* 3.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 3.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3.7 + * 3.8 + * This code is free software; you can redistribute it and/or modify it 3.9 + * under the terms of the GNU General Public License version 2 only, as 3.10 + * published by the Free Software Foundation. Oracle designates this 3.11 + * particular file as subject to the "Classpath" exception as provided 3.12 + * by Oracle in the LICENSE file that accompanied this code. 3.13 + * 3.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 3.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 3.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 3.17 + * version 2 for more details (a copy is included in the LICENSE file that 3.18 + * accompanied this code). 3.19 + * 3.20 + * You should have received a copy of the GNU General Public License version 3.21 + * 2 along with this work; if not, write to the Free Software Foundation, 3.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 3.23 + * 3.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 3.25 + * or visit www.oracle.com if you need additional information or have any 3.26 + * questions. 3.27 + */ 3.28 + 3.29 +/** 3.30 + * @test 3.31 + * @bug 8058112 3.32 + * @summary Invalid BootstrapMethod for constructor/method reference 3.33 + */ 3.34 + 3.35 +import java.util.function.Function; 3.36 + 3.37 +public class MethodReferenceIntersection2 { 3.38 + 3.39 + interface B { } 3.40 + 3.41 + interface A { } 3.42 + 3.43 + static class C implements A, B { } 3.44 + 3.45 + static class Info { 3.46 + <H extends A & B> Info(H h) { } 3.47 + 3.48 + static <H extends A & B> Info info(H h) { 3.49 + return new Info(h); 3.50 + } 3.51 + } 3.52 + 3.53 + public static void main(String[] args) { 3.54 + test(); 3.55 + } 3.56 + 3.57 + // Note the switch in order compared to that on Info 3.58 + static <H extends B & A> void test() { 3.59 + Function<H, Info> f1L = _h -> new Info(_h); 3.60 + Function<H, Info> f1 = Info::new; 3.61 + Function<H, Info> f2L = _h -> Info.info(_h); 3.62 + Function<H, Info> f2 = Info::info; 3.63 + H c = (H) new C(); 3.64 + if(f1.apply(c) instanceof Info && 3.65 + f2.apply(c) instanceof Info) { 3.66 + System.out.println("Passes."); 3.67 + } else { 3.68 + throw new AssertionError(); 3.69 + } 3.70 + } 3.71 +}
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/test/tools/javac/lambda/methodReferenceExecution/MethodReferenceIntersection3.java Tue Nov 25 13:37:34 2014 -0800 4.3 @@ -0,0 +1,50 @@ 4.4 +/* 4.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 4.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4.7 + * 4.8 + * This code is free software; you can redistribute it and/or modify it 4.9 + * under the terms of the GNU General Public License version 2 only, as 4.10 + * published by the Free Software Foundation. Oracle designates this 4.11 + * particular file as subject to the "Classpath" exception as provided 4.12 + * by Oracle in the LICENSE file that accompanied this code. 4.13 + * 4.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 4.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 4.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 4.17 + * version 2 for more details (a copy is included in the LICENSE file that 4.18 + * accompanied this code). 4.19 + * 4.20 + * You should have received a copy of the GNU General Public License version 4.21 + * 2 along with this work; if not, write to the Free Software Foundation, 4.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 4.23 + * 4.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 4.25 + * or visit www.oracle.com if you need additional information or have any 4.26 + * questions. 4.27 + */ 4.28 + 4.29 +/** 4.30 + * @test 4.31 + * @bug 8058112 4.32 + * @summary Invalid BootstrapMethod for constructor/method reference 4.33 + */ 4.34 + 4.35 +/** 4.36 + * @author Remi Forax 4.37 + */ 4.38 + 4.39 +public class MethodReferenceIntersection3 { 4.40 + interface A {} 4.41 + 4.42 + interface Foo { 4.43 + <T extends Object & A> void foo(T t); 4.44 + } 4.45 + 4.46 + static <T extends A> void bar(T t) { 4.47 + } 4.48 + 4.49 + public static void main(String[] args) { 4.50 + Foo foo = MethodReferenceIntersection3::bar; 4.51 + foo.foo(new A(){}); 4.52 + } 4.53 +}