changeset 2991:be15fb529cd3

Fix: missing synthetic cast for specialized generic calls Fix: wrong bytecode mapping generated for certain specialized calls
author mcimadamore
date Mon, 25 May 2015 12:55:54 +0100
parents 4f7c56237d8c
children c5b28bea0a2b
files src/jdk.compiler/share/classes/com/sun/tools/javac/comp/SpecializeTypes.java test/tools/javac/valhalla/typespec/Inference07.java
diffstat 2 files changed, 83 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/SpecializeTypes.java	Tue May 12 19:19:05 2015 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/SpecializeTypes.java	Mon May 25 12:55:54 2015 +0100
@@ -217,9 +217,34 @@
                 ForAll uninstantiatedMethodType = !receiverType.hasTag(TypeTag.NONE) ?
                         (ForAll)types.memberType(receiverType, msym) :
                         (ForAll)msym.type;
+
+                //Note: we want to generate an indy type that is as close as possible to the unspecialized
+                //descriptor, except for the specialized args. If we simply used the inferred type,
+                //the resulting descriptor would end up being too specific, so we play a trick and
+                //we instantiate all non-specialized type-arguments to the first inferred supertype matching
+                //the erasure of the corresponding type-argument. This means that the resulting signature
+                //will match the one in the non-specialized case and that the resulting bytecode mapping
+                //will always carry enough info for the specializer to dynamically reconstruct types.
+                //
+                //Example:
+                //class Foo<T> implements Comparable<T> {
+                //<X extends Comparable<T>, any Z> void m(X x, Z z) { ... }
+                //void test() { m(this, 1); //inferred X = Foo<T>, Z = int
+                //}
+                //
+                //In the above case, the inferred signature for the 'm' call is (LFoo<TT;>;I)V, while
+                //the signature we generate here (and the one which ends up in the bytecode mapping)
+                //is: (LComparable<TT;>;I)V.
+                //We do this as we'd like spcialized callsites to have same properties as non-specialized
+                //ones (as much as possible) - so we'd like the same set of synthetic casts to be generated
+                //in both cases (i..e minimize the number of adaptations performed by indy). An obvious
+                //alternative would be to let indy to do all the work.
+
                 List<Type> formals = uninstantiatedMethodType.tvars;
                 List<Type> instTypes = Tuple2.zip(formals, typeargs).stream()
-                        .map(p -> needsIndy.test(p.elem1) ? p.elem1 : types.erasure(p.elem0))
+                        .map(p -> needsIndy.test(p.elem1) ?
+                                p.elem1 :
+                                types.asSuper(p.elem1, types.erasure(p.elem0).tsym))
                         .collect(List.collector());
 
                 Type indyType = types.subst(uninstantiatedMethodType.asMethodType(), formals, instTypes);
@@ -236,7 +261,10 @@
                 JCMethodInvocation methInv = makeIndyCall(tree, msym.owner, syms.genericMethodSpecialzer,
                         names.metafactory, staticArgs, (MethodType)indyType, args, msym.name);
                 methInv.varargsElement = varargsElement;
-                return methInv.setType(methInv.type);
+
+                //set type to be inferred type (instead of indy return type). This will cause
+                //usual checkcast to be generated during TransTypes (where required).
+                return methInv.setType(tree.type);
             }
         }
         return tree;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/valhalla/typespec/Inference07.java	Mon May 25 12:55:54 2015 +0100
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015, 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
+ * @summary check that proper synthetic cast are added to specialized generic calls
+ */
+public class Inference07 {
+    interface Sink<any T> { }
+
+    static class ForEachOp<any T> implements Sink<T> {
+
+        <any P_IN,S extends Sink<T>> S wrapAndCopyInto(S sink, P_IN p) {
+            return (S)this;
+        }
+
+        public Void get() {
+            return null;
+        }
+
+        void test() {
+            wrapAndCopyInto(this, new Integer(1)).get();
+            wrapAndCopyInto(this, 1).get();
+        }
+    }
+
+    public static void main(String[] args) {
+        new ForEachOp<String>().test();
+        new ForEachOp<int>().test();
+    }
+}