changeset 582:1cbf9ca0c589

Improved support for diagnostic during lambda conversion. Now the compiler emits more specific info about why a function type cannot be assigned to a SAM type (does not cover method resolution diagnostics). Examples: TestX.java:16: incompatible types; target method pippo in class I is not suitable for lambda conversion I f = #(Integer i) { return new Integer(i); }; ^ required: I found: #Integer(Integer) 1 error Test.java:16: incompatible types; target method pippo in class I is not suitable for lambda conversion I f = #(Integer i) { return new Integer(i); }; ^ required: I found: #Integer(Integer) Test.java:16: incompatible types; the target type of a lambda conversion must be an abstract class/interface I f = #(Integer i) { return new Integer(i); }; ^ required: I found: #Integer(Integer) Test.java:16: incompatible types; no target method for lambda conversion found in class I I f = #(Integer i) { return new Integer(i); }; ^ required: I found: #Integer(Integer) Test.java:16: incompatible types; the target type of a lambda conversion must define a default constructor I f = #(Integer i) { return new Integer(i); }; ^ required: I found: #Integer(Integer)
author mcimadamore
date Tue, 22 Jun 2010 17:19:34 +0100
parents 02b8de982628
children dda155f6d75d
files src/share/classes/com/sun/tools/javac/code/Types.java src/share/classes/com/sun/tools/javac/comp/Check.java src/share/classes/com/sun/tools/javac/resources/compiler.properties test/tools/javac/lambda/BadConv01.out test/tools/javac/lambda/BadConv02.out
diffstat 5 files changed, 57 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/javac/code/Types.java	Mon Jun 21 13:43:37 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/code/Types.java	Tue Jun 22 17:19:34 2010 +0100
@@ -357,8 +357,8 @@
         }
         return false;
     }
-    //where
-    private boolean hasDefaultConstructor(ClassSymbol csym) {
+    
+    public boolean hasDefaultConstructor(ClassSymbol csym) {
         for (Scope.Entry e = csym.members().lookup(names.init) ; e.scope != null ; e = e.next()) {
             if (e.sym.kind == Kinds.MTH &&
                     e.sym.type.getParameterTypes().isEmpty()) {
--- a/src/share/classes/com/sun/tools/javac/comp/Check.java	Mon Jun 21 13:43:37 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Check.java	Tue Jun 22 17:19:34 2010 +0100
@@ -273,6 +273,46 @@
         }
     }
 
+    Type incompatibleTypesError(DiagnosticPosition pos, Type found, Type req) {
+        JCDiagnostic details = null;
+        if (types.isFunctionType(found) &&
+                !types.isFunctionType(req) &&
+                req.tag == CLASS) {
+            //generate better SAM conversion diagnostic
+            ListBuffer<Symbol> samMethods = ListBuffer.lb();
+            types.findSAM(req, samMethods);
+            Name methName = samMethods.nonEmpty() ?
+                samMethods.first().name :
+                null;
+            final String subkey;
+            if ((req.tsym.flags() & ABSTRACT) == 0) {
+                //req must be an abstract class or an interface
+                subkey = "target.for.lambda.conv.must.be.abstract";
+            } else if (!req.isInterface() &&
+                    !types.hasDefaultConstructor((ClassSymbol)req.tsym)) {
+                //if req is an abstract class, then it should have a default constructor
+                subkey = "target.for.lambda.conv.must.have.default.constr";
+            } else {
+                //req must define a suitable non-generic method
+                switch (samMethods.size()) {
+                    case 0: subkey = "no.target.method.for.lambda.conv"; break;
+                    case 1: {
+                        subkey = samMethods.first().type.tag == FORALL ?
+                            "invalid.generic.target.for.lambda.conv" :
+                            "incompatible.target.in.lambda.conv";
+                        break;
+                    }
+                    default: subkey = "incompatible.targets.in.lambda.conv"; break;
+                }
+            }
+            details = diags.fragment(subkey, methName, kindName(req.tsym), req.tsym);
+        }
+        JCDiagnostic subDiag = details != null ?
+            diags.fragment("incompatible.types.1", details) :
+            diags.fragment("incompatible.types");
+        return typeError(pos, subDiag, found, req);
+    }
+
 /* ************************************************************************
  * duplicate declaration checking
  *************************************************************************/
@@ -391,7 +431,7 @@
             log.error(pos, "assignment.to.extends-bound", req);
             return types.createErrorType(found);
         }
-        return typeError(pos, diags.fragment("incompatible.types"), found, req);
+        return incompatibleTypesError(pos, found, req);
     }
 
     /** Instantiate polymorphic type to some prototype, unless
--- a/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Mon Jun 21 13:43:37 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Tue Jun 22 17:19:34 2010 +0100
@@ -93,6 +93,18 @@
     lambda expression cannot be applied to given types\n\
     required: {0}\n\
     found: {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=\
+    target method {0} in {1} {2} is not suitable for lambda conversion
+compiler.misc.incompatible.targets.in.lambda.conv=\
+    no suitable target method for lambda conversion found in {1} {2}
+compiler.misc.invalid.generic.target.for.lambda.conv=\
+    invalid target for lambda conversion: method {0} in {1} {2} is generic
+compiler.misc.target.for.lambda.conv.must.be.abstract=\
+    the target type of a lambda conversion must be an abstract class/interface
+compiler.misc.target.for.lambda.conv.must.have.default.constr=\
+    the target type of a lambda conversion must define a default constructor
 compiler.err.cant.assign.val.to.final.var=\
     cannot assign a value to final variable {0}
 compiler.err.cant.deref=\
--- a/test/tools/javac/lambda/BadConv01.out	Mon Jun 21 13:43:37 2010 +0100
+++ b/test/tools/javac/lambda/BadConv01.out	Tue Jun 22 17:19:34 2010 +0100
@@ -1,2 +1,2 @@
-BadConv01.java:35:13: compiler.err.prob.found.req: (compiler.misc.incompatible.types), #int(T), BadConv01.Bar
+BadConv01.java:35:13: compiler.err.prob.found.req: (compiler.misc.incompatible.types.1: (compiler.misc.invalid.generic.target.for.lambda.conv: m, kindname.interface, BadConv01.Bar)), #int(T), BadConv01.Bar
 1 error
--- a/test/tools/javac/lambda/BadConv02.out	Mon Jun 21 13:43:37 2010 +0100
+++ b/test/tools/javac/lambda/BadConv02.out	Tue Jun 22 17:19:34 2010 +0100
@@ -1,3 +1,3 @@
 BadConv02.java:44:15: compiler.err.cant.apply.symbol: kindname.constructor, FooImpl, int, compiler.misc.no.args, kindname.class, BadConv02.FooImpl, null
-BadConv02.java:45:15: compiler.err.prob.found.req: (compiler.misc.incompatible.types), #int(), BadConv02.Foo
+BadConv02.java:45:15: compiler.err.prob.found.req: (compiler.misc.incompatible.types.1: (compiler.misc.target.for.lambda.conv.must.have.default.constr: null, kindname.class, BadConv02.Foo)), #int(), BadConv02.Foo
 2 errors