changeset 659:db1d811040a5

Updated rules for lambda conversion - now Object methods are ignored only if target SAM type is an interface.
author mcimadamore
date Thu, 02 Sep 2010 18:30:13 +0100
parents 1805864c6a0f
children c572fa185d05
files src/share/classes/com/sun/tools/javac/code/Types.java src/share/classes/com/sun/tools/javac/comp/Attr.java src/share/classes/com/sun/tools/javac/resources/compiler.properties test/tools/javac/diags/examples.not-yet.txt test/tools/javac/lambda/BadTargetType.out test/tools/javac/lambda/LambdaConv09.java test/tools/javac/lambda/LambdaConv09.out
diffstat 7 files changed, 115 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/javac/code/Types.java	Thu Aug 26 12:14:11 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/code/Types.java	Thu Sep 02 18:30:13 2010 +0100
@@ -341,7 +341,7 @@
                         Type site = env.enclClass.type;
                         Symbol noArgConstr = findNoArgConstructor((ClassSymbol)s.tsym);
                         ListBuffer<Symbol> buf = lb();
-                        findSAM(s, buf);
+                        findSAM(s, buf, s.isInterface());
                         boolean isTargetAccessible = false;
                         for (Symbol target : buf) {
                             if (isAccessible(target, site)) {
@@ -464,27 +464,27 @@
     public SAMResult findSAM(Type t) {
         if (t.tsym.kind != Kinds.TYP || (t.tsym.flags() & ABSTRACT) == 0) {
             //t must be an abstract class or an interface
-            return new SAMResult("target.for.lambda.conv.must.be.abstract");
+            return new SAMResult(t, "target.for.lambda.conv.must.be.abstract");
         } else if (!t.isInterface() &&
                     findNoArgConstructor((ClassSymbol)t.tsym) == null) {
             //if t is an abstract class, then it should have a default constructor
-            return new SAMResult("target.for.lambda.conv.must.have.default.constr");
+            return new SAMResult(t, "target.for.lambda.conv.must.have.default.constr");
         } else {
             ListBuffer<Symbol> abstracts = ListBuffer.lb();
-            int count = findSAM(t, abstracts);
+            int count = findSAM(t, abstracts, t.isInterface());
             if (abstracts.size() == 0) {
                 //t must define a suitable non-generic method
-                return new SAMResult("no.target.method.for.lambda.conv");
+                return new SAMResult(t, "no.target.method.for.lambda.conv");
             } else if (abstracts.size() != count) {
                 //the target method(s) should be the only abstract members of t
-                return new SAMResult("multiple.targets.for.lambda.conv");
+                return new SAMResult(t, "multiple.targets.for.lambda.conv");
             } else {
                 return new SAMResult(t, abstracts.toList());
             }
         }
     }    
     //where
-    private int findSAM(Type t, ListBuffer<Symbol> buf) {
+    private int findSAM(Type t, ListBuffer<Symbol> buf, boolean isInterface) {
         int count = 0;
         if (t == Type.noType) return count;
         for (Scope.Entry e = t.tsym.members().elems ; e != null ; e = e.sibling) {
@@ -492,7 +492,7 @@
                     e.sym.kind == Kinds.MTH &&
                     (e.sym.flags() & ABSTRACT) != 0 &&
                     (e.sym.flags() & DEFENDER) == 0 &&
-                    !overridesObjectMethod(e.sym, t.tsym)) {
+                    (!isInterface || !overridesObjectMethod(e.sym, t.tsym))) {
                 if (buf.isEmpty() ||
                         (e.sym.name == buf.first().name &&
                         overrideEquivalent(e.sym.type, buf.first().type))) {
@@ -501,9 +501,9 @@
                 count++;
             }
         }
-        count += findSAM(supertype(t), buf);
+        count += findSAM(supertype(t), buf, isInterface);
         for (Type i : interfaces(t)) {
-            count += findSAM(i, buf);
+            count += findSAM(i, buf, isInterface);
         }
         return count;
     }
@@ -543,8 +543,8 @@
         List<Symbol> methodSyms;
         String errKey;
 
-        public SAMResult(String errKey) {
-           this(null, List.<Symbol>nil(), errKey);
+        public SAMResult(Type samType, String errKey) {
+           this(samType, List.<Symbol>nil(), errKey);
            targetType = syms.errType;
         }
 
@@ -649,6 +649,13 @@
             }
         }
 
+        public JCDiagnostic getDiagnostic(JCDiagnostic.Factory diags) {
+            return diags.fragment(errKey,
+                        getTargetName(),
+                        Kinds.kindName(samType.tsym),
+                        samType.tsym);
+        }
+
         /**
          * is the SAM class suitable for lambda conversion?
          *
--- a/src/share/classes/com/sun/tools/javac/comp/Attr.java	Thu Aug 26 12:14:11 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java	Thu Sep 02 18:30:13 2010 +0100
@@ -2295,7 +2295,14 @@
                 (!types.isFunctionType(samOrFunctionType) &&
                 types.findSAM(superType).isErroneous())) {
             //if the target type is neither a SAM type nor a function type, reports an error
-            log.error(that.pos(), "invalid.target.type.for.lambda.conv", samOrFunctionType);
+            Types.SAMResult res = types.findSAM(superType);
+            String key = "invalid.target.type.for.lambda.conv";
+            JCDiagnostic explanation = null;
+            if (res.isErroneous()) {
+                key = "invalid.target.type.for.lambda.conv.1";
+                explanation = res.getDiagnostic(diags);
+            }
+            log.error(that.pos(), key, samOrFunctionType, explanation);
             return that.type = syms.errType;
         }
 
--- a/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Thu Aug 26 12:14:11 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Thu Sep 02 18:30:13 2010 +0100
@@ -97,6 +97,9 @@
     found: {1}
 compiler.err.invalid.target.type.for.lambda.conv=\
     invalid target type {0} for lambda conversion
+compiler.err.invalid.target.type.for.lambda.conv.1=\
+    invalid target type {0} for lambda conversion\n\
+    reason: {1}
 compiler.misc.no.target.method.for.lambda.conv=\
     no target method for lambda conversion found in {1} {2}
 compiler.misc.incompatible.target.in.lambda.conv=\
--- a/test/tools/javac/diags/examples.not-yet.txt	Thu Aug 26 12:14:11 2010 +0100
+++ b/test/tools/javac/diags/examples.not-yet.txt	Thu Sep 02 18:30:13 2010 +0100
@@ -123,6 +123,7 @@
 compiler.err.func.types.not.supported.in.source                                  #LAMBDA
 compiler.err.incompatibles.ret.types.in.lambda                                   #LAMBDA
 compiler.err.invalid.target.type.for.lambda.conv                                 #LAMBDA
+compiler.err.invalid.target.type.for.lambda.conv.1                               #LAMBDA
 compiler.err.lambda.call.non.func.type                                           #LAMBDA
 compiler.err.lambda.not.supported.in.source                                      #LAMBDA
 compiler.err.method.references.not.supported.in.source                           #LAMBDA
--- a/test/tools/javac/lambda/BadTargetType.out	Thu Aug 26 12:14:11 2010 +0100
+++ b/test/tools/javac/lambda/BadTargetType.out	Thu Sep 02 18:30:13 2010 +0100
@@ -1,5 +1,5 @@
-BadTargetType.java:37:24: compiler.err.invalid.target.type.for.lambda.conv: java.lang.Object
-BadTargetType.java:38:17: compiler.err.invalid.target.type.for.lambda.conv: java.lang.Object
+BadTargetType.java:37:24: compiler.err.invalid.target.type.for.lambda.conv.1: java.lang.Object, (compiler.misc.target.for.lambda.conv.must.be.abstract: null, kindname.class, java.lang.Object)
+BadTargetType.java:38:17: compiler.err.invalid.target.type.for.lambda.conv.1: java.lang.Object, (compiler.misc.target.for.lambda.conv.must.be.abstract: null, kindname.class, java.lang.Object)
 BadTargetType.java:41:9: compiler.err.cant.apply.symbol: kindname.method, m1, java.lang.Object, #void(int), kindname.class, BadTargetType, null
 BadTargetType.java:42:9: compiler.err.cant.apply.symbol: kindname.method, m2, java.lang.Object, #void(int), kindname.class, BadTargetType, null
 4 errors
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/lambda/LambdaConv09.java	Thu Sep 02 18:30:13 2010 +0100
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2010, 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.
+ *
+ * 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 SAM conversion handles Object members correctly
+ * @author  Alex Buckley
+ * @author  Maurizio Cimadamore
+ * @compile/fail/ref=LambdaConv09.out -XDrawDiagnostics LambdaConv09.java
+ */
+
+class LambdaConv09 {
+
+    // Not a SAM type; not enough abstract methods
+    static abstract class Foo1 {}
+
+    // Not a SAM type; no discernable abstract method
+    interface Foo2 {}
+
+    // SAM type; Foo has one abstract method
+    static abstract class Foo3 { public abstract boolean equals(Object object); }
+
+
+    // Not a SAM type; too many abstract methods
+    static abstract class Bar3 extends Foo3 { public abstract String toString(); }
+
+
+    // Not a SAM type; too many abstract methods
+    static abstract class Quux {
+        public abstract boolean equals(Object object);
+        public abstract String toString();
+    }
+
+    // Not a SAM type; too many abstract methods
+    interface Foo4 { boolean equals(Object obj); }
+
+    // SAM type; Bar has one abstract non-Object method
+    interface Bar4<T> extends Foo4 { int compare(T o1, T o2); }
+
+    // SAM type; Bar has one abstract non-Object method
+    interface Comparator<T> {
+        boolean equals(Object obj);
+        int compare(T o1, T o2);
+    }
+
+    void test() {
+        Foo1 f1 = #{};
+        Foo2 f2 = #{};
+        Foo3 f3 = #(x) { true };
+        Bar3 b3 = #(x) { true };
+        Quux q = #(x) { true };
+        Foo4 f4 = #(x) { true };
+        Bar4<Integer> b4 = #(x, y) { 1 };
+        Comparator<Integer> c = #(x, y) { 1 };
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/lambda/LambdaConv09.out	Thu Sep 02 18:30:13 2010 +0100
@@ -0,0 +1,6 @@
+LambdaConv09.java:67:19: compiler.err.invalid.target.type.for.lambda.conv.1: LambdaConv09.Foo1, (compiler.misc.no.target.method.for.lambda.conv: null, kindname.class, LambdaConv09.Foo1)
+LambdaConv09.java:68:19: compiler.err.invalid.target.type.for.lambda.conv.1: LambdaConv09.Foo2, (compiler.misc.no.target.method.for.lambda.conv: null, kindname.interface, LambdaConv09.Foo2)
+LambdaConv09.java:70:19: compiler.err.invalid.target.type.for.lambda.conv.1: LambdaConv09.Bar3, (compiler.misc.multiple.targets.for.lambda.conv: null, kindname.class, LambdaConv09.Bar3)
+LambdaConv09.java:71:18: compiler.err.invalid.target.type.for.lambda.conv.1: LambdaConv09.Quux, (compiler.misc.multiple.targets.for.lambda.conv: null, kindname.class, LambdaConv09.Quux)
+LambdaConv09.java:72:19: compiler.err.invalid.target.type.for.lambda.conv.1: LambdaConv09.Foo4, (compiler.misc.no.target.method.for.lambda.conv: null, kindname.interface, LambdaConv09.Foo4)
+5 errors