8058112: Invalid BootstrapMethod for constructor/method reference

Tue, 25 Nov 2014 13:37:34 -0800

author
rfield
date
Tue, 25 Nov 2014 13:37:34 -0800
changeset 2614
b5c8adb2206a
parent 2613
272300c8b557
child 2615
4d2222373842

8058112: Invalid BootstrapMethod for constructor/method reference
Summary: Bridge method references with functional interface method parameters of intersection type
Reviewed-by: vromero, dlsmith

src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java file | annotate | diff | comparison | revisions
test/tools/javac/lambda/methodReferenceExecution/MethodReferenceIntersection1.java file | annotate | diff | comparison | revisions
test/tools/javac/lambda/methodReferenceExecution/MethodReferenceIntersection2.java file | annotate | diff | comparison | revisions
test/tools/javac/lambda/methodReferenceExecution/MethodReferenceIntersection3.java file | annotate | diff | comparison | revisions
     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 +}

mercurial