changeset 583:dda155f6d75d

Added accessibility check to lambda conversion. This requires a non-trivial change in compiler internals: each type-conversion becomes now a ternary relation between two types (source, target) and a 'site' type (the type in which the conversion is performed).
author mcimadamore
date Fri, 25 Jun 2010 13:00:45 +0100
parents 1cbf9ca0c589
children e8877bcc2fab
files src/share/classes/com/sun/tools/apt/mirror/util/TypesImpl.java src/share/classes/com/sun/tools/javac/code/Types.java src/share/classes/com/sun/tools/javac/comp/Annotate.java src/share/classes/com/sun/tools/javac/comp/Attr.java src/share/classes/com/sun/tools/javac/comp/Check.java src/share/classes/com/sun/tools/javac/comp/Infer.java src/share/classes/com/sun/tools/javac/comp/Lower.java src/share/classes/com/sun/tools/javac/comp/Resolve.java src/share/classes/com/sun/tools/javac/comp/TransTypes.java src/share/classes/com/sun/tools/javac/model/JavacTypes.java src/share/classes/com/sun/tools/javac/parser/JavacParser.java src/share/classes/com/sun/tools/javac/resources/compiler.properties src/share/classes/javax/lang/model/util/Types.java test/tools/javac/lambda/LambdaConversionTest.java
diffstat 14 files changed, 671 insertions(+), 184 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/apt/mirror/util/TypesImpl.java	Tue Jun 22 17:19:34 2010 +0100
+++ b/src/share/classes/com/sun/tools/apt/mirror/util/TypesImpl.java	Fri Jun 25 13:00:45 2010 +0100
@@ -79,7 +79,7 @@
      * {@inheritDoc}
      */
     public boolean isAssignable(TypeMirror t1, TypeMirror t2) {
-        return env.jctypes.isAssignable(((TypeMirrorImpl) t1).type,
+        return env.jctypes.isAssignableNoCheck(((TypeMirrorImpl) t1).type,
                                         ((TypeMirrorImpl) t2).type);
     }
 
--- a/src/share/classes/com/sun/tools/javac/code/Types.java	Tue Jun 22 17:19:34 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/code/Types.java	Fri Jun 25 13:00:45 2010 +0100
@@ -33,6 +33,8 @@
 
 import com.sun.tools.javac.jvm.ClassReader;
 import com.sun.tools.javac.comp.Check;
+import com.sun.tools.javac.comp.Env;
+import com.sun.tools.javac.comp.AttrContext;
 
 import static com.sun.tools.javac.code.Type.*;
 import static com.sun.tools.javac.code.TypeTags.*;
@@ -263,90 +265,213 @@
 
     // <editor-fold defaultstate="collapsed" desc="isConvertible">
     /**
-     * Is t a subtype of or convertiable via boxing/unboxing
+     * Is t a subtype of or convertiable via boxing/unboxing/lambda
      * convertions to s?
      */
-    public boolean isConvertible(Type t, Type s, Warner warn) {
+    public boolean isConvertible(Env<AttrContext> env, Type t, Type s) {
+        return isConvertible(env, t, s, Warner.noWarnings);
+    }
+
+    public boolean isConvertible(Env<AttrContext> env, Type t, Type s, Warner warn) {
+        return isConvertible(t, s, warn).check(env.enclClass.type);
+    }
+
+    private boolean isConvertibleNoCheck(Type t, Type s, Warner warn) {
+        return isConvertible(t, s, warn).isSuccess();
+    }
+    //where
+    private ConversionResult isConvertible(Type t, final Type s, Warner warn) {
         if (isFunctionType(t) &&
-                (findSAM(s) != null ||
-                isFunctionType(s))) {
+                (isFunctionType(s) || s.tsym.kind == Kinds.TYP)) {
+            if (s == syms.objectType)
+                return ConversionResult.SUCCESS;
+            final SAMResult samRes = findSAM(s);
+            if (!isFunctionType(s) && samRes.isErroneous()) {
+                return new ConversionResult(samRes.errKey,
+                        samRes.getTargetName(),
+                        Kinds.kindName(s.tsym),
+                        s.tsym);
+            }
             Type mtype = isFunctionType(s) ?
                 s.asMethodType(this) :
-                findSAM(s);
-            boolean isReturnOk = t.getReturnType() == syms.voidType ?
-                //the order is inmportant here because of type-inference
-                //(reference types first)
+                samRes.getTargetType();
+            //the order is inmportant here because of type-inference
+            //(reference types first)
+            final boolean isReturnOk = t.getReturnType() == syms.voidType ?
                 (isSameType(mtype.getReturnType(), boxedClass(syms.voidType).type) ||
-                isSameType(mtype.getReturnType(), syms.voidType)) :
-                isConvertible(t.getReturnType(), mtype.getReturnType(), warn);
+                    isSameType(mtype.getReturnType(), syms.voidType)) :
+                isConvertibleNoCheck(t.getReturnType(), mtype.getReturnType(), warn);
+            
             boolean argsOk = t.getParameterTypes().size() == mtype.getParameterTypes().size() &&
                 containsType(mtype.getParameterTypes(), t.getParameterTypes());
                     
             boolean thrownOk = chk.unhandled(t.getThrownTypes(), mtype.getThrownTypes()).isEmpty();
-            return isReturnOk && argsOk && thrownOk;
+            return new ConversionResult(isReturnOk && argsOk && thrownOk) {
+                @Override
+                public boolean check(Type site) {
+                    if (!isSuccess()) {
+                        //target method is not compatible
+                        setDiagnostic("incompatible.target.in.lambda.conv");
+                        return false;
+                    }
+                    else if (s.isInterface() ||
+                            isFunctionType(s))
+                        return true;
+                    else {
+                        //SAM type is an abstract class - check that both
+                        //the target method and the SAM constructor are accessible
+                        //in the given context
+                        Symbol noArgConstr = findNoArgConstructor((ClassSymbol)s.tsym);
+                        ListBuffer<Symbol> buf = lb();
+                        findSAM(s, buf);
+                        boolean isTargetAccessible = false;
+                        for (Symbol target : buf) {
+                            if (isAccessible(target, site)) {
+                                isTargetAccessible = true;
+                                break;
+                            }
+                        }
+                        if (!isAccessible(noArgConstr, site)) {
+                            setDiagnostic("target.for.lambda.conv.must.have.default.constr");
+                            return false;
+                        }
+                        if (!isTargetAccessible) {
+                            setDiagnostic("target.not.accessible");
+                            return false;
+                        }
+                        return true;
+                    }
+                }
+                private void setDiagnostic(String key) {
+                    setKey(key).setArgs(samRes.getTargetName(), Kinds.kindName(s.tsym), s.tsym);
+                }
+                private boolean isAccessible(Symbol sym, Type site) {
+                    return (sym.flags() & (PUBLIC | PROTECTED)) != 0 ||
+                            (sym.packge() == site.tsym.packge() &&
+                            (sym.flags() & PRIVATE) == 0);
+                }
+            };
         }
         else {
             boolean tPrimitive = t.isPrimitive();
             boolean sPrimitive = s.isPrimitive();
             if (tPrimitive == sPrimitive)
-                return isSubtypeUnchecked(t, s, warn);
-            if (!allowBoxing) return false;
-            return tPrimitive
+                return new ConversionResult(isSubtypeUnchecked(t, s, warn));
+            if (!allowBoxing) return ConversionResult.FAILURE;
+            return new ConversionResult(tPrimitive
                 ? isSubtype(boxedClass(t).type, s)
-                : isSubtype(unboxedType(t), s);
+                : isSubtype(unboxedType(t), s));
         }
     }
 
-    public Type findSAM(Type t) {
-        if (t.tsym.kind == Kinds.TYP &&
-                (t.tsym.flags() & ABSTRACT) != 0 &&
-                (t.isInterface() || hasDefaultConstructor((ClassSymbol)t.tsym))) {
+    /**
+     * This class is used to keep track of the result of a given conversion.
+     * In order to retrieve a boolean value (success/failure) clients must
+     * supply a type to the 'check' method. This is required as some kinds
+     * of conversion (see lambda conversion) also perform an accessibility
+     * check - as such their outcome depends on the current attribution environment.
+     * This class also stores detailed information about why a type conversion
+     * has failed; this info can be leveraged when a diagnostic message is
+     * generated by javac.
+     */
+    public static class ConversionResult {
+
+        private boolean success;
+        private String detailsKey;
+        private Object[] detailsArgs;
+
+        ConversionResult(boolean success) {
+            this(success, null);
+        }
+
+        ConversionResult(String key, Object... args) {
+            this(false, key, args);
+        }
+
+        ConversionResult(boolean success, String key, Object... args) {
+            this.success = success;
+            this.detailsKey = key;
+            this.detailsArgs = args;
+        }
+
+        public static final ConversionResult SUCCESS = new ConversionResult(true);
+        public static final ConversionResult FAILURE = new ConversionResult(false);
+
+        public boolean check(Type site) { return success; }
+
+        public JCDiagnostic getDiagnostic(JCDiagnostic.Factory diags) {
+            return detailsKey != null ?
+                diags.fragment(detailsKey, detailsArgs) :
+                null;
+        }
+
+        protected ConversionResult setKey(String key) {
+            this.detailsKey = key;
+            return this;
+        }
+
+        protected ConversionResult setArgs(Object... args) {
+            this.detailsArgs = args;
+            return this;
+        }
+
+        protected boolean isSuccess() {
+            return success;
+        }
+    }
+
+    // <editor-fold defaultstate="collapsed" desc="findSam">
+
+    /**
+     * Search for a target method that is suitable for lambda conversion.
+     *
+     * @param t the type in which the target method is to be searched
+     * @return a SAMResult instance
+     */
+    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");
+        } 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");
+        } else {
             ListBuffer<Symbol> abstracts = ListBuffer.lb();
-            findSAM(t, abstracts);
+            int count = findSAM(t, abstracts);
             if (abstracts.size() == 0) {
-                return null;
-            } else if (abstracts.size() == 1) {
-                Symbol sym = abstracts.first();
-                return sym.type.tag == FORALL ?
-                    null :
-                    memberType(t, sym);
+                //t must define a suitable non-generic method
+                return new SAMResult("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");
             } else {
-                Type resType = Type.noType;
-                List<Type> thrownTypes = List.nil();
-                List<Type> argtypes =
-                        memberType(t, abstracts.first()).getParameterTypes();
-                for (Symbol msym : abstracts.toList()) {
-                    if (msym.type.tag == FORALL) return null;
-                    Type mtype = memberType(t, msym);
-                    resType = mtype.getReturnType() == syms.voidType ?
-                        syms.voidType :
-                        lub(resType, mtype.getReturnType());
-                    thrownTypes = chk.union(mtype.getThrownTypes(), thrownTypes);
-                }
-                return new MethodType(argtypes,
-                        resType,
-                        thrownTypes,
-                        syms.methodClass);
+                return new SAMResult(t, abstracts.toList());
             }
         }
-        return null;
-    }
-
-    public void findSAM(Type t, ListBuffer<Symbol> buf) {
-        if (t == Type.noType) return;
+    }    
+    //where
+    private int findSAM(Type t, ListBuffer<Symbol> buf) {
+        int count = 0;
+        if (t == Type.noType) return count;
         for (Scope.Entry e = t.tsym.members().elems ; e != null ; e = e.sibling) {
             if (e.sym != null && 
                     e.sym.kind == Kinds.MTH &&
                     (e.sym.flags() & ABSTRACT) != 0 &&
-                    !overridesObjectMethod(e.sym, t.tsym) &&
-                    (buf.isEmpty() || overrideEquivalent(e.sym.type, buf.first().type))) {
-                buf.append(e.sym);
+                    !overridesObjectMethod(e.sym, t.tsym)) {
+                if (buf.isEmpty() ||
+                        (e.sym.name == buf.first().name &&
+                        overrideEquivalent(e.sym.type, buf.first().type))) {
+                    buf.append(e.sym);
+                }
+                count++;
             }
         }
-        findSAM(supertype(t), buf);
+        count += findSAM(supertype(t), buf);
         for (Type i : interfaces(t)) {
-            findSAM(i, buf);
+            count += findSAM(i, buf);
         }
+        return count;
     }
     //where
     private boolean overridesObjectMethod(Symbol msym, TypeSymbol tsym) {
@@ -358,22 +483,141 @@
         return false;
     }
     
-    public boolean hasDefaultConstructor(ClassSymbol csym) {
+    public Symbol findNoArgConstructor(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()) {
-                return true;
+                return e.sym;
             }
         }
-        return false;
+        return null;
     }
 
     /**
-     * Is t a subtype of or convertiable via boxing/unboxing
-     * convertions to s?
+     * This class is used to store the result of a SAM lookup. Usually a lookup
+     * ends up in finding a list of suitable, override-equivalent method symbols.
+     * SAMResult provides functionalities in order to instantiate the type of the
+     * target method of a SAM conversion (if such unique type exists), as well
+     * as to retrieve the accessibility flags and the name associated with such
+     * method. If no such method exists, SAMResult stores detailed info about
+     * what went wrong so that this info can be encapsulated in a richer compiler
+     * diagnostic.
      */
-    public boolean isConvertible(Type t, Type s) {
-        return isConvertible(t, s, Warner.noWarnings);
+    public class SAMResult {
+        Type samType;
+        Type targetType;
+        List<Symbol> methodSyms;
+        String errKey;
+
+        SAMResult(String errKey) {
+           this(null, List.<Symbol>nil(), errKey);
+           targetType = syms.errType;
+        }
+
+        SAMResult(Type samType, List<Symbol> methodSyms) {
+            this(samType, methodSyms, null);
+            targetType = getTargetType();
+        }
+
+        private SAMResult(Type samType, List<Symbol> methodSyms, String errKey) {
+            this.samType = samType;
+            this.methodSyms = methodSyms;
+            this.errKey = errKey;
+        }
+
+        /**
+         * Get the name of the target method
+         *
+         * @return the name of the target method
+         */
+        public Name getTargetName() {
+            return methodSyms.nonEmpty() ?
+                methodSyms.head.name :
+                null;
+        }
+
+        /**
+         * Get the flags associated with the target method. Accessibility flags
+         * are computed as the 'union' of the accessibility flags associated with
+         * the methods found during a SAM lookup. For instance, if the most-public
+         * modifier found is 'protected' the target method will also have
+         * 'protected' visibility (even in the presence of other override-equivalent
+         * package-private methods).
+         *
+         * @return flags associated with the target method
+         */
+        public long getTargetFlags() {
+            long flags = 0;
+            for (Symbol s : methodSyms) {
+                flags |= s.flags();
+            }
+            if ((flags & PUBLIC) != 0) {
+                flags = flags & (~PROTECTED | ~PRIVATE);
+            }
+            else if ((flags & PROTECTED) != 0) {
+                flags = flags & ~PRIVATE;
+            }
+            return flags;
+        }
+
+        /**
+         * Compute the type of the target method seen as a member of the
+         * specified SAM type. If no method has been found during the SAM
+         * lookup, no type can be instantiated. If more than one method has been
+         * found, the resulting type is computed as per lambda spec (return type
+         * is lub() of all return types).
+         *
+         * @return target method type
+         */
+        public Type getTargetType() {
+            if (targetType != null) {
+                return targetType;
+            } else {
+                if (methodSyms.size() == 1) {
+                    if (methodSyms.head.type.tag == FORALL) {
+                        errKey = "invalid.generic.target.for.lambda.conv";
+                        return syms.errType;
+                    } else {
+                        return memberType(samType, methodSyms.head);
+                    }
+                }
+                else {
+                    // size > 1
+                    Type resType = Type.noType;
+                    List<Type> thrownTypes = List.nil();
+                    List<Type> argtypes =
+                            memberType(samType, methodSyms.head).getParameterTypes();
+                    for (Symbol msym : methodSyms) {
+                        if (msym.type.tag == FORALL) {
+                            errKey = "invalid.generic.target.for.lambda.conv";
+                            return syms.errType;
+                        }
+                        Type mtype = memberType(samType, msym);
+                        resType = mtype.getReturnType() == syms.voidType ?
+                            syms.voidType :
+                            lub(resType, mtype.getReturnType());
+                        if (resType.isErroneous()) {
+                            errKey = "incompatible.targets.in.lambda.conv";
+                            return syms.errType;
+                        }
+                        thrownTypes = chk.union(mtype.getThrownTypes(), thrownTypes);
+                    }
+                    return new MethodType(argtypes,
+                            resType,
+                            thrownTypes,
+                            syms.methodClass);
+                }
+            }
+        }
+
+        /**
+         * is the SAM class suitable for lambda conversion?
+         *
+         * @return true if an error occurred during the SAM lookup.
+         */
+        public boolean isErroneous() {
+            return targetType.isErroneous();
+        }
     }
     // </editor-fold>
 
@@ -1052,22 +1296,31 @@
     // </editor-fold>
 
     // <editor-fold defaultstate="collapsed" desc="isCastable">
-    public boolean isCastable(Type t, Type s) {
-        return isCastable(t, s, Warner.noWarnings);
-    }
 
     /**
      * Is t is castable to s?<br>
      * s is assumed to be an erased type.<br>
      * (not defined for Method and ForAll types).
      */
-    public boolean isCastable(Type t, Type s, Warner warn) {
+    public boolean isCastable(Env<AttrContext> env, Type t, Type s) {
+        return isCastable(env, t, s, Warner.noWarnings);
+    }
+
+    public boolean isCastable(Env<AttrContext> env, Type t, Type s, Warner warn) {
+        if (isConvertible(env, t, s, warn))
+            return true;
+        else
+            return isCastableReference(t, s, warn);
+    }
+
+    public boolean isCastableReference(Type t, Type s) {
+        return isCastableReference(t, s, Warner.noWarnings);
+    }
+    
+    public boolean isCastableReference(Type t, Type s, Warner warn) {
         if (t == s)
             return true;
 
-        if (t.isPrimitive() != s.isPrimitive())
-            return allowBoxing && isConvertible(t, s, warn);
-
         if (warn != warnStack.head) {
             try {
                 warnStack = warnStack.prepend(warn);
@@ -1103,7 +1356,7 @@
 
             @Override
             public Boolean visitWildcardType(WildcardType t, Type s) {
-                return isCastable(upperBound(t), s, warnStack.head);
+                return isCastableReference(upperBound(t), s, warnStack.head);
             }
 
             @Override
@@ -1112,7 +1365,7 @@
                     return true;
 
                 if (s.tag == TYPEVAR) {
-                    if (isCastable(s.getUpperBound(), t, Warner.noWarnings)) {
+                    if (isCastableReference(s.getUpperBound(), t, Warner.noWarnings)) {
                         warnStack.head.warnUnchecked();
                         return true;
                     } else {
@@ -1225,7 +1478,7 @@
                 case BOT:
                     return true;
                 case TYPEVAR:
-                    if (isCastable(s, t, Warner.noWarnings)) {
+                    if (isCastableReference(s, t, Warner.noWarnings)) {
                         warnStack.head.warnUnchecked();
                         return true;
                     } else {
@@ -1253,21 +1506,21 @@
                 case TYPEVAR:
                     if (isSubtype(t, s)) {
                         return true;
-                    } else if (isCastable(t.bound, s, Warner.noWarnings)) {
+                    } else if (isCastableReference(t.bound, s, Warner.noWarnings)) {
                         warnStack.head.warnUnchecked();
                         return true;
                     } else {
                         return false;
                     }
                 default:
-                    return isCastable(t.bound, s, warnStack.head);
+                    return isCastableReference(t.bound, s, warnStack.head);
                 }
             }
 
             @Override
             public Boolean visitDisjunctiveType(DisjunctiveType t, Type s) {
                 for (Type t2 : t.types) {
-                    if (isCastable(t2, s)) {
+                    if (isCastableReference(t2, s)) {
                         return true;
                     }
                 }
@@ -1319,7 +1572,7 @@
                 TypePair pair = new TypePair(t, s);
                 if (cache.add(pair)) {
                     try {
-                        return Types.this.isCastable(t, s);
+                        return Types.this.isCastableReference(t, s);
                     } finally {
                         cache.remove(pair);
                     }
@@ -1404,7 +1657,7 @@
             TypeVar tv = (TypeVar) t;
             if (s.tag == TYPEVAR)
                 s = s.getUpperBound();
-            return !isCastable(tv.bound,
+            return !isCastableReference(tv.bound,
                                s,
                                Warner.noWarnings);
         }
@@ -1685,8 +1938,23 @@
     // </editor-fold>
 
     // <editor-fold defaultstate="collapsed" desc="isAssignable">
-    public boolean isAssignable(Type t, Type s) {
-        return isAssignable(t, s, Warner.noWarnings);
+    public boolean isAssignable(Env<AttrContext> env, Type t, Type s) {
+        return isAssignable(env, t, s, Warner.noWarnings);
+    }
+
+    public boolean isAssignable(Env<AttrContext> env, Type t, Type s, Warner warn) {        
+        return isAssignable(t, s, warn).check(env.enclClass.type);
+    }
+
+    //the following methods should be private, but they need to be accessed from
+    //deprecated methods in JavacTypes (for backward compatibility)
+
+    public boolean isAssignableNoCheck(Type t, Type s) {
+        return isAssignableNoCheck(t, s, Warner.noWarnings);
+    }
+
+    public boolean isAssignableNoCheck(Type t, Type s, Warner warn) {
+        return isAssignable(t, s, warn).isSuccess();
     }
 
     /**
@@ -1695,26 +1963,26 @@
      * types.<br>
      * (not defined for Method and ForAll types)
      */
-    public boolean isAssignable(Type t, Type s, Warner warn) {
+    public ConversionResult isAssignable(Type t, Type s, Warner warn) {
         if (t.tag == ERROR)
-            return true;
+            return ConversionResult.SUCCESS;
         if (t.tag <= INT && t.constValue() != null) {
             int value = ((Number)t.constValue()).intValue();
             switch (s.tag) {
             case BYTE:
                 if (Byte.MIN_VALUE <= value && value <= Byte.MAX_VALUE)
-                    return true;
+                    return ConversionResult.SUCCESS;
                 break;
             case CHAR:
                 if (Character.MIN_VALUE <= value && value <= Character.MAX_VALUE)
-                    return true;
+                    return ConversionResult.SUCCESS;
                 break;
             case SHORT:
                 if (Short.MIN_VALUE <= value && value <= Short.MAX_VALUE)
-                    return true;
+                    return ConversionResult.SUCCESS;
                 break;
             case INT:
-                return true;
+                return ConversionResult.SUCCESS;
             case CLASS:
                 switch (unboxedType(s).tag) {
                 case BYTE:
@@ -3082,7 +3350,7 @@
             source.allowCovariantReturns() &&
             !t.isPrimitive() &&
             !s.isPrimitive() &&
-            isAssignable(t, s, warner);
+            isAssignableNoCheck(t, s, warner);
     }
     // </editor-fold>
 
--- a/src/share/classes/com/sun/tools/javac/comp/Annotate.java	Tue Jun 22 17:19:34 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Annotate.java	Fri Jun 25 13:00:45 2010 +0100
@@ -139,7 +139,7 @@
         // need to do it again.
         Type at = (a.annotationType.type != null ? a.annotationType.type
                   : attr.attribType(a.annotationType, env));
-        a.type = chk.checkType(a.annotationType.pos(), at, expected);
+        a.type = chk.checkType(a.annotationType.pos(), env, at, expected);
         if (a.type.isErroneous())
             return new Attribute.Compound(a.type, List.<Pair<MethodSymbol,Attribute>>nil());
         if ((a.type.tsym.flags() & Flags.ANNOTATION) == 0) {
--- a/src/share/classes/com/sun/tools/javac/comp/Attr.java	Tue Jun 22 17:19:34 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java	Fri Jun 25 13:00:45 2010 +0100
@@ -200,7 +200,7 @@
     Type check(JCTree tree, Type owntype, int ownkind, int pkind, Type pt) {
         if (owntype.tag != ERROR && pt.tag != METHOD && pt.tag != FORALL) {
             if ((ownkind & ~pkind) == 0) {
-                owntype = chk.checkType(tree.pos(), owntype, pt);
+                owntype = chk.checkType(tree.pos(), env, owntype, pt);
             } else {
                 log.error(tree.pos(), "unexpected.type",
                           kindNames(pkind),
@@ -707,7 +707,7 @@
                 log.error(tree.thrown.head.pos(),
                           "throws.not.allowed.in.intf.annotation");
             for (List<JCExpression> l = tree.thrown; l.nonEmpty(); l = l.tail)
-                chk.checkType(l.head.pos(), l.head.type, syms.throwableType);
+                chk.checkType(l.head.pos(), localEnv, l.head.type, syms.throwableType);
 
             if (tree.body == null) {
                 // Empty bodies are only allowed for
@@ -764,7 +764,7 @@
                         log.error(tree.body.stats.head, "bad.defender.method.body");
                     } else {
                         JCTree defaultImpl = ((JCExpressionStatement)tree.body.stats.head).expr;
-                        chk.checkType(defaultImpl, defaultImpl.type, m.type.getReturnType());
+                        chk.checkType(defaultImpl, localEnv, defaultImpl.type, m.type.getReturnType());
                         m.defaultImpl = TreeInfo.symbol(((JCMethodInvocation)defaultImpl).meth);
                     }
                 }
@@ -898,7 +898,7 @@
                     : types.upperBound(iterableParams.head);
             }
         }
-        chk.checkType(tree.expr.pos(), elemtype, tree.var.sym.type);
+        chk.checkType(tree.expr.pos(), loopEnv, elemtype, tree.var.sym.type);
         loopEnv.tree = tree; // before, we were not in loop!
         attribStat(tree.body, loopEnv);
         loopEnv.info.scope.leave();
@@ -924,6 +924,7 @@
 
     public void visitSwitch(JCSwitch tree) {
         Type seltype = attribExpr(tree.selector, env);
+        Type site = env.info.scope.owner.type;
 
         Env<AttrContext> switchEnv =
             env.dup(tree, env.info.dup(env.info.scope.dup()));
@@ -940,7 +941,7 @@
             }
         }
         if (!enumSwitch && !stringSwitch)
-            seltype = chk.checkType(tree.selector.pos(), seltype, syms.intType);
+            seltype = chk.checkType(tree.selector.pos(), switchEnv, seltype, syms.intType);
 
         // Attribute all cases and
         // check that there are no duplicate case labels or default clauses.
@@ -1023,6 +1024,7 @@
 
     public void visitTry(JCTry tree) {
         // Attribute body
+        Type site = env.info.scope.owner.type;
         attribStat(tree.body, env.dup(tree, env.info.dup()));
 
         // Attribute catch clauses
@@ -1041,7 +1043,7 @@
             if (c.param.type.tsym.kind == Kinds.VAR) {
                 c.param.sym.setData(ElementKind.EXCEPTION_PARAMETER);
             }
-            chk.checkType(c.param.vartype.pos(),
+            chk.checkType(c.param.vartype.pos(), catchEnv,
                           chk.checkClassOrDisjunctiveType(c.param.vartype.pos(), ctype),
                           syms.throwableType);
             attribStat(c.body, catchEnv);
@@ -1117,10 +1119,10 @@
                 // short, or char), and the other is an integer constant
                 // that fits into the subrange, return the subrange type.
                 if (thenUnboxed.tag < INT && elseUnboxed.tag == INT &&
-                    types.isAssignable(elseUnboxed, thenUnboxed))
+                    types.isAssignable(env, elseUnboxed, thenUnboxed))
                     return thenUnboxed.baseType();
                 if (elseUnboxed.tag < INT && thenUnboxed.tag == INT &&
-                    types.isAssignable(thenUnboxed, elseUnboxed))
+                    types.isAssignable(env, thenUnboxed, elseUnboxed))
                     return elseUnboxed.baseType();
 
                 for (int i = BYTE; i < VOID; i++) {
@@ -1747,7 +1749,7 @@
             //type-variables, infer them using the expected type and declared
             //bounds (JLS 15.12.2.8).
             try {
-                clazztype = infer.instantiateExpr((ForAll) clazztype,
+                clazztype = infer.instantiateExpr(env, (ForAll) clazztype,
                         pt.tag == NONE ? syms.objectType : pt,
                         Warner.noWarnings);
             } catch (Infer.InferenceException ex) {
@@ -1995,6 +1997,7 @@
 
     public void visitAssignop(JCAssignOp tree) {
         // Attribute arguments.
+        Type site = env.info.scope.owner.type;
         Type owntype = attribTree(tree.lhs, env, VAR, Type.noType);
         Type operand = attribExpr(tree.rhs, env);
         // Find operator.
@@ -2009,7 +2012,7 @@
                               owntype,
                               operand);
             chk.checkDivZero(tree.rhs.pos(), operator, operand);
-            chk.checkCastable(tree.rhs.pos(),
+            chk.checkCastable(tree.rhs.pos(), env,
                               operator.type.getReturnType(),
                               owntype);
         }
@@ -2094,7 +2097,7 @@
             // Check that argument types of a reference ==, != are
             // castable to each other, (JLS???).
             if ((opc == ByteCodes.if_acmpeq || opc == ByteCodes.if_acmpne)) {
-                if (!types.isCastable(left, right, new Warner(tree.pos()))) {
+                if (!types.isCastable(env, left, right, new Warner(tree.pos()))) {
                     log.error(tree.pos(), "incomparable.types", left, right);
                 }
             }
@@ -2108,7 +2111,7 @@
         Type clazztype = attribType(tree.clazz, env);
         chk.validate(tree.clazz, env);
         Type exprtype = attribExpr(tree.expr, env, Infer.anyPoly);
-        Type owntype = chk.checkCastable(tree.expr.pos(), exprtype, clazztype);
+        Type owntype = chk.checkCastable(tree.expr.pos(), env, exprtype, clazztype);
         if (exprtype.constValue() != null)
             owntype = cfolder.coerce(exprtype, owntype);
         result = check(tree, capture(owntype), VAL, pkind, pt);
@@ -2120,7 +2123,7 @@
         Type clazztype = chk.checkReifiableReferenceType(
             tree.clazz.pos(), attribType(tree.clazz, env));
         chk.validate(tree.clazz, env);
-        chk.checkCastable(tree.expr.pos(), exprtype, clazztype);
+        chk.checkCastable(tree.expr.pos(), env, exprtype, clazztype);
         result = check(tree, syms.booleanType, VAL, pkind, pt);
     }
 
@@ -2766,7 +2769,7 @@
             while (formals.head != last) {
                 JCTree arg = args.head;
                 Warner warn = chk.convertWarner(arg.pos(), arg.type, formals.head);
-                assertConvertible(arg, arg.type, formals.head, warn);
+                assertConvertible(env, arg, arg.type, formals.head, warn);
                 warned |= warn.warned;
                 args = args.tail;
                 formals = formals.tail;
@@ -2776,7 +2779,7 @@
                 while (args.tail != null) {
                     JCTree arg = args.head;
                     Warner warn = chk.convertWarner(arg.pos(), arg.type, varArg);
-                    assertConvertible(arg, arg.type, varArg, warn);
+                    assertConvertible(env, arg, arg.type, varArg, warn);
                     warned |= warn.warned;
                     args = args.tail;
                 }
@@ -2826,8 +2829,8 @@
         return owntype;
     }
 
-    private void assertConvertible(JCTree tree, Type actual, Type formal, Warner warn) {
-        if (types.isConvertible(actual, formal, warn))
+    private void assertConvertible(Env<AttrContext> env, JCTree tree, Type actual, Type formal, Warner warn) {
+        if (types.isConvertible(env, actual, formal, warn))
             return;
 
         if (formal.isCompound()
--- a/src/share/classes/com/sun/tools/javac/comp/Check.java	Tue Jun 22 17:19:34 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Check.java	Fri Jun 25 13:00:45 2010 +0100
@@ -273,46 +273,6 @@
         }
     }
 
-    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
  *************************************************************************/
@@ -412,14 +372,15 @@
      *  @param found      The type that was found.
      *  @param req        The type that was required.
      */
-    Type checkType(DiagnosticPosition pos, Type found, Type req) {
+    Type checkType(DiagnosticPosition pos, Env<AttrContext> env, Type found, Type req) {
         if (req.tag == ERROR)
             return req;
         if (found.tag == FORALL)
-            return instantiatePoly(pos, (ForAll)found, req, convertWarner(pos, found, req));
+            return instantiatePoly(pos, env, (ForAll)found, req, convertWarner(pos, found, req));
         if (req.tag == NONE)
             return found;
-        if (types.isAssignable(found, req, convertWarner(pos, found, req)))
+        Types.ConversionResult res = types.isAssignable(found, req, convertWarner(pos, found, req));
+        if (res.check(env.enclClass.type))
             return found;
         if (found.tag <= DOUBLE && req.tag <= DOUBLE)
             return typeError(pos, diags.fragment("possible.loss.of.precision"), found, req);
@@ -431,24 +392,27 @@
             log.error(pos, "assignment.to.extends-bound", req);
             return types.createErrorType(found);
         }
-        return incompatibleTypesError(pos, found, req);
+        JCDiagnostic subDiag = res != null && res.getDiagnostic(diags) != null ?
+            diags.fragment("incompatible.types.1", res.getDiagnostic(diags)) :
+            diags.fragment("incompatible.types");
+        return typeError(pos, subDiag, found, req);
     }
 
     /** Instantiate polymorphic type to some prototype, unless
      *  prototype is `anyPoly' in which case polymorphic type
      *  is returned unchanged.
      */
-    Type instantiatePoly(DiagnosticPosition pos, ForAll t, Type pt, Warner warn) throws Infer.NoInstanceException {
+    Type instantiatePoly(DiagnosticPosition pos, Env<AttrContext> env, ForAll t, Type pt, Warner warn) throws Infer.NoInstanceException {
         if (pt == Infer.anyPoly && complexInference) {
             return t;
         } else if (pt == Infer.anyPoly || pt.tag == NONE) {
             Type newpt = t.qtype.tag <= VOID ? t.qtype : syms.objectType;
-            return instantiatePoly(pos, t, newpt, warn);
+            return instantiatePoly(pos, env, t, newpt, warn);
         } else if (pt.tag == ERROR) {
             return pt;
         } else {
             try {
-                return infer.instantiateExpr(t, pt, warn);
+                return infer.instantiateExpr(env, t, pt, warn);
             } catch (Infer.NoInstanceException ex) {
                 if (ex.isAmbiguous) {
                     JCDiagnostic d = ex.getDiagnostic();
@@ -476,11 +440,11 @@
      *  @param found      The type that is being cast.
      *  @param req        The target type of the cast.
      */
-    Type checkCastable(DiagnosticPosition pos, Type found, Type req) {
+    Type checkCastable(DiagnosticPosition pos, Env<AttrContext> env, Type found, Type req) {
         if (found.tag == FORALL) {
-            instantiatePoly(pos, (ForAll) found, req, castWarner(pos, found, req));
+            instantiatePoly(pos, env, (ForAll) found, req, castWarner(pos, found, req));
             return req;
-        } else if (types.isCastable(found, req, castWarner(pos, found, req))) {
+        } else if (types.isCastable(env, found, req, castWarner(pos, found, req))) {
             return req;
         } else {
             return typeError(pos,
@@ -539,7 +503,7 @@
         } else {
             switch (bk) {
                 case UNBOUND: return true;
-                case EXTENDS: return types.isCastable(f, s, Warner.noWarnings);
+                case EXTENDS: return types.isCastableReference(f, s, Warner.noWarnings);
                 case SUPER: return !types.notSoftSubtype(s, f);
                 default: return false;
             }
--- a/src/share/classes/com/sun/tools/javac/comp/Infer.java	Tue Jun 22 17:19:34 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Infer.java	Fri Jun 25 13:00:45 2010 +0100
@@ -250,7 +250,7 @@
      *  If no instantiation exists, or if several incomparable
      *  best instantiations exist throw a NoInstanceException.
      */
-    public Type instantiateExpr(ForAll that,
+    public Type instantiateExpr(Env<AttrContext> env, ForAll that,
                                 Type to,
                                 Warner warn) throws InferenceException {
         List<Type> undetvars = Type.map(that.tvars, fromTypeVarFun);
@@ -283,7 +283,7 @@
         List<Type> targs = Type.map(undetvars, getInstFun);
         targs = types.subst(targs, that.tvars, targs);
         checkWithinBounds(that.tvars, targs, warn);
-        return chk.checkType(warn.pos(), that.inst(targs, types), to);
+        return chk.checkType(warn.pos(), env, that.inst(targs, types), to);
     }
 
     /** Instantiate method type `mt' by finding instantiations of
@@ -312,10 +312,10 @@
             Type actual = actuals.head.baseType();
             Type actualNoCapture = actualsNoCapture.head.baseType();
             if (actual.tag == FORALL)
-                actual = instantiateArg((ForAll)actual, formal, tvars, warn);
+                actual = instantiateArg(env, (ForAll)actual, formal, tvars, warn);
             Type undetFormal = types.subst(formal, tvars, undetvars);
             boolean works = allowBoxing
-                ? types.isConvertible(actual, undetFormal, warn)
+                ? types.isConvertible(env, actual, undetFormal, warn)
                 : types.isSubtypeUnchecked(actual, undetFormal, warn);
             if (!works) {
                 throw unambiguousNoInstanceException
@@ -341,8 +341,8 @@
                 Type actual = actuals.head.baseType();
                 Type actualNoCapture = actualsNoCapture.head.baseType();
                 if (actual.tag == FORALL)
-                    actual = instantiateArg((ForAll)actual, elemType, tvars, warn);
-                boolean works = types.isConvertible(actual, elemUndet, warn);
+                    actual = instantiateArg(env, (ForAll)actual, elemType, tvars, warn);
+                boolean works = types.isConvertible(env, actual, elemUndet, warn);
                 if (!works) {
                     throw unambiguousNoInstanceException
                         .setMessage("no.conforming.assignment.exists",
@@ -411,7 +411,7 @@
                 @Override
                 public Type inst(List<Type> inferred, Types types) throws NoInstanceException {
                     List<Type> formals = types.subst(mt2.argtypes, tvars, inferred);
-                    if (!rs.argumentsAcceptable(capturedArgs, formals,
+                    if (!rs.argumentsAcceptable(env, capturedArgs, formals,
                            allowBoxing, useVarargs, warn)) {
                       // inferred method is not applicable
                       throw invalidInstanceException.setMessage("inferred.do.not.conform.to.params", formals, argtypes);
@@ -426,7 +426,7 @@
             }};
             return mt2;
         }
-        else if (!rs.argumentsAcceptable(capturedArgs, mt.getParameterTypes(), allowBoxing, useVarargs, warn)) {
+        else if (!rs.argumentsAcceptable(env, capturedArgs, mt.getParameterTypes(), allowBoxing, useVarargs, warn)) {
             // inferred method is not applicable
             throw invalidInstanceException.setMessage("inferred.do.not.conform.to.params", mt.getParameterTypes(), argtypes);
         }
@@ -442,18 +442,18 @@
          *  every occurrence of a type variable in `tvars' is replaced
          *  by an unknown type.
          */
-        private Type instantiateArg(ForAll that,
+        private Type instantiateArg(Env<AttrContext> env, ForAll that,
                                     Type to,
                                     List<Type> tvars,
                                     Warner warn) throws InferenceException {
             List<Type> targs;
             try {
-                return instantiateExpr(that, to, warn);
+                return instantiateExpr(env, that, to, warn);
             } catch (NoInstanceException ex) {
                 Type to1 = to;
                 for (List<Type> l = tvars; l.nonEmpty(); l = l.tail)
                     to1 = types.subst(to1, List.of(l.head), List.of(syms.unknownType));
-                return instantiateExpr(that, to1, warn);
+                return instantiateExpr(env, that, to1, warn);
             }
         }
 
--- a/src/share/classes/com/sun/tools/javac/comp/Lower.java	Tue Jun 22 17:19:34 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Lower.java	Fri Jun 25 13:00:45 2010 +0100
@@ -1775,9 +1775,7 @@
             public void visitIdent(JCIdent tree) {
                 if (tree.name == names._super) {
                     //set super constructor symbol and type
-                    tree.sym = lookupConstructor(pos,
-                            types.supertype(owner.type),
-                            defaultConstrSym.type.getParameterTypes());
+                    tree.sym = types.findNoArgConstructor((ClassSymbol)types.supertype(owner.type).tsym);
                     tree.type = tree.sym.type;
                 }
             }
@@ -2872,7 +2870,7 @@
     @SuppressWarnings("unchecked") // XXX unchecked
     <T extends JCTree> T unlambdaIfNeeded(T tree, Type type) {
         if (types.isSameType(tree.type, syms.methodHandleType) &&
-                types.findSAM(type) != null) {
+                !types.findSAM(type).isErroneous()) {
             return (T)(type.isInterface() ?
                 lambdaToInterface((JCExpression)tree, type) :
                 lambdaToClass((JCExpression)tree, type));
@@ -2896,9 +2894,8 @@
         boolean isStatic = outerThisStack.isEmpty();
 
         ListBuffer<Symbol> samMethods = ListBuffer.lb();
-        types.findSAM(type, samMethods);
-        Symbol targetMethodSym = samMethods.first();
-        Type targetMethodType = types.findSAM(type);
+        Types.SAMResult samRes = types.findSAM(type);        
+        Type targetMethodType = samRes.getTargetType();
 
         //assign translated method handle to final variable $mh
         //   MethodHandle $mh = <trans-closure>
@@ -2937,8 +2934,8 @@
         //      [return] $mh.invoke(a1, a2, ... an);
         //   }
 
-        MethodSymbol samMethSym = new MethodSymbol(targetMethodSym.flags() & ~ABSTRACT,
-                targetMethodSym.name,
+        MethodSymbol samMethSym = new MethodSymbol(samRes.getTargetFlags() & ~ABSTRACT,
+                samRes.getTargetName(),
                 null,
                 samClassSym);
         samMethSym.type = new MethodType(targetMethodType.getParameterTypes(),
--- a/src/share/classes/com/sun/tools/javac/comp/Resolve.java	Tue Jun 22 17:19:34 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Resolve.java	Fri Jun 25 13:00:45 2010 +0100
@@ -353,7 +353,7 @@
                                     useVarargs,
                                     warn);
         return
-            argumentsAcceptable(argtypes, mt.getParameterTypes(),
+            argumentsAcceptable(env, argtypes, mt.getParameterTypes(),
                                 allowBoxing, useVarargs, warn)
             ? mt
             : null;
@@ -379,7 +379,7 @@
 
     /** Check if a parameter list accepts a list of args.
      */
-    boolean argumentsAcceptable(List<Type> argtypes,
+    boolean argumentsAcceptable(Env<AttrContext> env, List<Type> argtypes,
                                 List<Type> formals,
                                 boolean allowBoxing,
                                 boolean useVarargs,
@@ -387,7 +387,7 @@
         Type varargsFormal = useVarargs ? formals.last() : null;
         while (argtypes.nonEmpty() && formals.head != varargsFormal) {
             boolean works = allowBoxing
-                ? types.isConvertible(argtypes.head, formals.head, warn)
+                ? types.isConvertible(env, argtypes.head, formals.head, warn)
                 : types.isSubtypeUnchecked(argtypes.head, formals.head, warn);
             if (!works) return false;
             argtypes = argtypes.tail;
@@ -398,7 +398,7 @@
             return argtypes.isEmpty();
         Type elt = types.elemtype(varargsFormal);
         while (argtypes.nonEmpty()) {
-            if (!types.isConvertible(argtypes.head, elt, warn))
+            if (!types.isConvertible(env, argtypes.head, elt, warn))
                 return false;
             argtypes = argtypes.tail;
         }
@@ -959,11 +959,11 @@
             m = new MethodSymbol(flags, name, mtype, c);
             implicit.enter(m);
         }
-        boolean argsOk = argumentsAcceptable(argtypes, types.memberType(site, m).getParameterTypes(),
+        boolean argsOk = argumentsAcceptable(env, argtypes, types.memberType(site, m).getParameterTypes(),
                                    true, false, Warner.noWarnings);
         if (types.isFunctionType(site) && !argsOk)
             return wrongMethod.setWrongSym(m);
-        if (!argumentsAcceptable(argtypes, types.memberType(site, m).getParameterTypes(),
+        if (!argumentsAcceptable(env, argtypes, types.memberType(site, m).getParameterTypes(),
                                    true, false, Warner.noWarnings) ||
             instantiate(env, site, m, argtypes, typeargtypes, true, false, Warner.noWarnings) == null) {
             throw new AssertionError();
--- a/src/share/classes/com/sun/tools/javac/comp/TransTypes.java	Tue Jun 22 17:19:34 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/TransTypes.java	Fri Jun 25 13:00:45 2010 +0100
@@ -129,7 +129,7 @@
         }
         Type btarget = target.baseType();
         if (tree.type.isPrimitive() == target.isPrimitive()) {
-            return types.isAssignable(tree.type, btarget, Warner.noWarnings)
+            return types.isAssignableNoCheck(tree.type, btarget, Warner.noWarnings)
                 ? tree
                 : cast(tree, btarget);
         }
--- a/src/share/classes/com/sun/tools/javac/model/JavacTypes.java	Tue Jun 22 17:19:34 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/model/JavacTypes.java	Fri Jun 25 13:00:45 2010 +0100
@@ -97,7 +97,14 @@
     public boolean isAssignable(TypeMirror t1, TypeMirror t2) {
         validateTypeNotIn(t1, EXEC_OR_PKG);
         validateTypeNotIn(t2, EXEC_OR_PKG);
-        return types.isAssignable((Type) t1, (Type) t2);
+        return types.isAssignableNoCheck((Type) t1, (Type) t2);
+    }
+
+    public boolean isAssignable(TypeMirror site, TypeMirror t1, TypeMirror t2) {
+        validateTypeNotIn(site, EXEC_OR_PKG);
+        validateTypeNotIn(t1, EXEC_OR_PKG);
+        validateTypeNotIn(t2, EXEC_OR_PKG);
+        return types.isAssignable((Type) t1, (Type) t2, Warner.noWarnings).check((Type)site);
     }
 
     public boolean contains(TypeMirror t1, TypeMirror t2) {
--- a/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Tue Jun 22 17:19:34 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Fri Jun 25 13:00:45 2010 +0100
@@ -1038,7 +1038,7 @@
                     case INTLITERAL: case LONGLITERAL: case FLOATLITERAL:
                     case DOUBLELITERAL: case CHARLITERAL: case STRINGLITERAL:
                     case TRUE: case FALSE: case NULL:
-                    case NEW: case IDENTIFIER: case ASSERT: case ENUM:
+                        case NEW: case IDENTIFIER: case HASH: case ASSERT: case ENUM:
                     case BYTE: case SHORT: case CHAR: case INT:
                     case LONG: case FLOAT: case DOUBLE: case BOOLEAN: case VOID:
                         JCExpression t1 = term3();
--- a/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Tue Jun 22 17:19:34 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Fri Jun 25 13:00:45 2010 +0100
@@ -104,7 +104,11 @@
 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
+    the target type of a lambda conversion must define an accessible no-arg constructor
+compiler.misc.target.not.accessible=\
+    target method of lambda conversion {0} in {1} {2} is not accessible
+compiler.misc.multiple.targets.for.lambda.conv=\
+    the target type of a lambda conversion has multiple non-overriding abstract methods
 compiler.err.cant.assign.val.to.final.var=\
     cannot assign a value to final variable {0}
 compiler.err.cant.deref=\
--- a/src/share/classes/javax/lang/model/util/Types.java	Tue Jun 22 17:19:34 2010 +0100
+++ b/src/share/classes/javax/lang/model/util/Types.java	Fri Jun 25 13:00:45 2010 +0100
@@ -86,7 +86,9 @@
     boolean isSubtype(TypeMirror t1, TypeMirror t2);
 
     /**
-     * Tests whether one type is assignable to another.
+     * Tests whether one type is assignable to another. This method has been
+     * deprecated; the new assignment conversion rules are expressed in terms
+     * of an additional argument (required in order to perform accessibility checks).
      *
      * @param t1  the first type
      * @param t2  the second type
@@ -95,9 +97,24 @@
      * @throws IllegalArgumentException if given an executable or package type
      * @jls3 5.2 Assignment Conversion
      */
+    @Deprecated
     boolean isAssignable(TypeMirror t1, TypeMirror t2);
 
     /**
+     * Tests whether one type is assignable to another in the context of
+     * the type 'site'
+     *
+     * @param site the context used to perform accessibility check
+     * @param t1  the first type
+     * @param t2  the second type
+     * @return {@code true} if and only if the first type is assignable
+     *          to the second
+     * @throws IllegalArgumentException if given an executable or package type
+     * @jls3 5.2 Assignment Conversion
+     */
+    boolean isAssignable(TypeMirror site, TypeMirror t1, TypeMirror t2);
+
+    /**
      * Tests whether one type argument <i>contains</i> another.
      *
      * @param t1  the first type
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/lambda/LambdaConversionTest.java	Fri Jun 25 13:00:45 2010 +0100
@@ -0,0 +1,227 @@
+/*
+ * 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 perform several automated checks in lambda conversion
+ * @author  Maurizio Cimadamore
+ * @run main LambdaConversionTest
+ */
+
+import com.sun.source.util.JavacTask;
+import java.net.URI;
+import java.util.Arrays;
+import javax.tools.Diagnostic;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.ToolProvider;
+
+public class LambdaConversionTest {
+
+    enum PackageKind {
+        NO_PKG(""),
+        PKG_A("a");
+
+        String pkg;
+
+        PackageKind(String pkg) {
+            this.pkg = pkg;
+        }
+
+        String getPkgDecl() {
+            return this == NO_PKG ?
+                "" :
+                "package " + pkg + ";";
+        }
+
+        String getImportStat() {
+            return this == NO_PKG ?
+                "" :
+                "import " + pkg + ".*;";
+        }
+    }
+
+    enum SamKind {
+        CLASS("#P \n public class Sam { \n #C \n #METH; \n }"),
+        ABSTACT_CLASS("#P \n public abstract class Sam { \n #C; \n #METH; \n }"),
+        INTERFACE("#P \n public interface Sam { \n #METH; \n }");
+
+        String sam_str;
+
+        SamKind(String sam_str) {
+            this.sam_str = sam_str;
+        }
+
+        String getSam(String constr, String meth, PackageKind pk) {
+            String res = sam_str.replaceAll("#C", constr);
+            res = res.replaceAll("#P", pk.getPkgDecl());
+            res = res.replaceAll("#METH", meth);
+            return res;
+        }
+    }
+
+    enum ConstructorKind {
+        NO_ARG("#M Sam() {}"),
+        ONE_ARG("#M Sam(int i) {}");
+
+        String method_str;
+
+        ConstructorKind(String method_str) {
+            this.method_str = method_str;
+        }
+
+        String getConstructor(Modifier mod) {
+            return method_str.replaceAll("#M", mod.modifier_str);
+        }
+    }
+
+    enum Modifier {
+        PUBLIC("public"),
+        PROTECTED("protected"),
+        PACKAGE(""),        
+        PRIVATE("private");
+
+        String modifier_str;
+
+        Modifier(String modifier_str) {
+            this.modifier_str = modifier_str;
+        }
+
+        boolean stricterThan(Modifier that) {
+            return this.ordinal() > that.ordinal();
+        }
+    }
+
+    enum MethodKind {
+        NONE(""),
+        NON_GENERIC("#A #M void m(String s) #B"),
+        GENERIC("#A #M <X> void m(String s) #B");
+
+        String methodName;
+
+        MethodKind(String methodName) {
+            this.methodName = methodName;
+        }
+
+        String getMethod(SamKind samKind, Modifier mod) {
+            mod = samKind == samKind.INTERFACE ?
+                Modifier.PUBLIC :
+                mod;
+            String res = methodName.replaceAll("#M", mod.modifier_str);
+            res = res.replaceAll("#B", samKind == SamKind.CLASS ? "return null;" : "");
+            res = res.replaceAll("#A", samKind == SamKind.ABSTACT_CLASS ? "abstract" : "");
+            return res;
+        }
+    }
+
+    static String formClient(PackageKind samPkg) {
+        String clientTemplate = "class Client { \n" +
+                " void test() { Sam s = #(String s){}; } \n }";
+        return  samPkg.getImportStat() + "\n" +
+                clientTemplate;
+    }
+
+    public static void main(String[] args) throws Exception {
+        for (PackageKind samPkg : PackageKind.values()) {
+            for (SamKind samKind : SamKind.values()) {                
+                for (MethodKind m1 : MethodKind.values()) {
+                    for (Modifier m1Mod : Modifier.values()) {
+                        for (ConstructorKind consKind : ConstructorKind.values()) {
+                            for (Modifier consMod : Modifier.values()) {
+                                test(samPkg, samKind, m1, m1Mod, consKind, consMod);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    static class JavaSource extends SimpleJavaFileObject {
+
+        String source;
+
+        public JavaSource(String name, String body) {
+            super(URI.create("myfo:/" + name), JavaFileObject.Kind.SOURCE);
+            source = body;
+        }
+
+        @Override
+        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+          return source;
+        }
+        @Override
+        public String toString() {
+            return source;
+        }
+    }
+
+    static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
+
+        boolean errorFound = false;
+
+        public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+            if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
+                errorFound = true;
+            }
+        }
+    }
+
+    static void test(PackageKind samPkg, SamKind samKind, MethodKind m, Modifier mmod,
+            ConstructorKind cons, Modifier consMod) throws Exception {
+        final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
+        JavaSource samSource = new JavaSource("Sam.java",
+                samKind.getSam(cons.getConstructor(consMod),
+                m.getMethod(samKind, mmod),
+                samPkg));
+        JavaSource clientSource = new JavaSource("Client.java", formClient(samPkg));
+        DiagnosticChecker dc = new DiagnosticChecker();
+        JavacTask ct = (JavacTask)tool.getTask(null, null, dc,
+        null, null, Arrays.asList(samSource, clientSource));
+        ct.analyze();
+        if (dc.errorFound == check(samPkg, samKind, m, mmod, cons, consMod)) {
+            throw new AssertionError(samSource + "\n\n" + clientSource);
+        }
+    }
+
+    static boolean check(PackageKind samPkg, SamKind samKind, MethodKind meth,
+            Modifier methMod, ConstructorKind consKind, Modifier consMod) {
+        if (samKind == SamKind.CLASS)
+            return false; //sam type must be abstract
+        if (samKind == SamKind.ABSTACT_CLASS &&
+                consKind != ConstructorKind.NO_ARG)
+            return false; //sam type must have no arg constructor
+        if (meth != MethodKind.NON_GENERIC)
+            return false;        
+        if (samKind == SamKind.ABSTACT_CLASS) {
+            //check that both constructor and target method are indeed accessible
+            boolean targetOk = Modifier.PACKAGE.stricterThan(methMod) ||
+                    (methMod == Modifier.PACKAGE && samPkg == PackageKind.NO_PKG);
+            boolean constructorOk = Modifier.PACKAGE.stricterThan(consMod) ||
+                    (consMod == Modifier.PACKAGE && samPkg == PackageKind.NO_PKG);
+            return targetOk && constructorOk;
+        }
+        return true;
+    }
+}