changeset 1586:99ce7dc58473

Add support for intersection types in cast expressions *) Add parser support for intersection types *) Refactor parser to better handle ambiguities between parens/lambda/cast *) Refactor Attr/Types to share logic between type-variable and intersection type well-formedness checks *) Add support for SAM conversion when target is intersection type *) Add 269 support for intersection types *) Add com.sun.source.tree API support for intersection types Todo: *) Translation of lambda/method-references targeting intersection types is disabled for now
author mcimadamore
date Wed, 31 Oct 2012 22:32:32 +0000
parents 1655737bb7ad
children 7ffcc63f2b3d
files src/share/classes/com/sun/source/tree/IntersectionTypeTree.java src/share/classes/com/sun/source/tree/Tree.java src/share/classes/com/sun/source/tree/TreeVisitor.java src/share/classes/com/sun/source/util/SimpleTreeVisitor.java src/share/classes/com/sun/source/util/TreeScanner.java src/share/classes/com/sun/tools/javac/code/Symbol.java src/share/classes/com/sun/tools/javac/code/Type.java 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/comp/TransTypes.java src/share/classes/com/sun/tools/javac/jvm/ClassReader.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/tree/JCTree.java src/share/classes/com/sun/tools/javac/tree/Pretty.java src/share/classes/com/sun/tools/javac/tree/TreeCopier.java src/share/classes/com/sun/tools/javac/tree/TreeMaker.java src/share/classes/com/sun/tools/javac/tree/TreeScanner.java src/share/classes/com/sun/tools/javac/tree/TreeTranslator.java src/share/classes/javax/lang/model/type/IntersectionType.java src/share/classes/javax/lang/model/type/TypeKind.java src/share/classes/javax/lang/model/type/TypeVisitor.java src/share/classes/javax/lang/model/util/AbstractTypeVisitor6.java src/share/classes/javax/lang/model/util/AbstractTypeVisitor8.java test/tools/javac/cast/intersection/IntersectionTypeCastTest.java test/tools/javac/cast/intersection/model/Check.java test/tools/javac/cast/intersection/model/IntersectionTypeInfo.java test/tools/javac/cast/intersection/model/Member.java test/tools/javac/cast/intersection/model/Model01.java test/tools/javac/cast/intersection/model/ModelChecker.java
diffstat 30 files changed, 1148 insertions(+), 216 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/source/tree/IntersectionTypeTree.java	Wed Oct 31 22:32:32 2012 +0000
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+package com.sun.source.tree;
+
+import java.util.List;
+
+/**
+ * A tree node for an intersection type in a cast expression.
+ *
+ * @author Maurizio Cimadamore
+ *
+ * @since 1.8
+ */
+public interface IntersectionTypeTree extends Tree {
+    List<? extends Tree> getBounds();
+}
--- a/src/share/classes/com/sun/source/tree/Tree.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/com/sun/source/tree/Tree.java	Wed Oct 31 22:32:32 2012 +0000
@@ -245,6 +245,11 @@
          * Used for instances of {@link UnionTypeTree}.
          */
         UNION_TYPE(UnionTypeTree.class),
+        
+        /**
+         * Used for instances of {@link IntersectionTypeTree}.
+         */
+        INTERSECTION_TYPE(IntersectionTypeTree.class),
 
         /**
          * Used for instances of {@link TypeCastTree}.
--- a/src/share/classes/com/sun/source/tree/TreeVisitor.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/com/sun/source/tree/TreeVisitor.java	Wed Oct 31 22:32:32 2012 +0000
@@ -98,6 +98,7 @@
     R visitTry(TryTree node, P p);
     R visitParameterizedType(ParameterizedTypeTree node, P p);
     R visitUnionType(UnionTypeTree node, P p);
+    R visitIntersectionType(IntersectionTypeTree node, P p);
     R visitArrayType(ArrayTypeTree node, P p);
     R visitTypeCast(TypeCastTree node, P p);
     R visitPrimitiveType(PrimitiveTypeTree node, P p);
--- a/src/share/classes/com/sun/source/util/SimpleTreeVisitor.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/com/sun/source/util/SimpleTreeVisitor.java	Wed Oct 31 22:32:32 2012 +0000
@@ -239,6 +239,10 @@
     public R visitUnionType(UnionTypeTree node, P p) {
         return defaultAction(node, p);
     }
+    
+    public R visitIntersectionType(IntersectionTypeTree node, P p) {
+        return defaultAction(node, p);
+    }
 
     public R visitTypeParameter(TypeParameterTree node, P p) {
         return defaultAction(node, p);
--- a/src/share/classes/com/sun/source/util/TreeScanner.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/com/sun/source/util/TreeScanner.java	Wed Oct 31 22:32:32 2012 +0000
@@ -370,6 +370,10 @@
     public R visitUnionType(UnionTypeTree node, P p) {
         return scan(node.getTypeAlternatives(), p);
     }
+    
+    public R visitIntersectionType(IntersectionTypeTree node, P p) {
+        return scan(node.getBounds(), p);
+    }
 
     public R visitTypeParameter(TypeParameterTree node, P p) {
         R r = scan(node.getBounds(), p);
--- a/src/share/classes/com/sun/tools/javac/code/Symbol.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/code/Symbol.java	Wed Oct 31 22:32:32 2012 +0000
@@ -575,12 +575,18 @@
             List<Symbol> list = List.nil();
             if (kind == TYP && type.tag == TYPEVAR) {
                 return list;
+            } else if ((flags() & COMPOUND) != 0) {
+                for (Type t : ((IntersectionClassType)type).getComponents()) {
+                    list = list.appendList(List.from(t.tsym.getEnclosedElements()));
+                }
+                return list;
+            } else {
+                for (Scope.Entry e = members().elems; e != null; e = e.sibling) {
+                    if (e.sym != null && (e.sym.flags() & SYNTHETIC) == 0 && e.sym.owner == this)
+                        list = list.prepend(e.sym);
+                }
+                return list;
             }
-            for (Scope.Entry e = members().elems; e != null; e = e.sibling) {
-                if (e.sym != null && (e.sym.flags() & SYNTHETIC) == 0 && e.sym.owner == this)
-                    list = list.prepend(e.sym);
-            }
-            return list;
         }
 
         // For type params.
--- a/src/share/classes/com/sun/tools/javac/code/Type.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/code/Type.java	Wed Oct 31 22:32:32 2012 +0000
@@ -782,6 +782,37 @@
             return v.visitUnion(this, p);
         }
     }
+    
+    // a clone of a ClassType that knows about the bounds of an intersection type.
+    public static class IntersectionClassType extends ClassType implements IntersectionType {
+
+        public IntersectionClassType(List<Type> bounds, ClassSymbol csym) {
+            super(Type.noType, List.<Type>nil(), csym);            
+            Assert.check((csym.flags() & COMPOUND) != 0);
+            supertype_field = bounds.head;
+            interfaces_field = bounds.tail;
+            Assert.check(supertype_field.tsym.completer != null ||
+                    !supertype_field.isInterface(), supertype_field);
+        }
+
+        public java.util.List<? extends TypeMirror> getBounds() {
+            return Collections.unmodifiableList(getComponents());
+        }
+        
+        public List<Type> getComponents() {
+            return interfaces_field.prepend(supertype_field);
+        }
+
+        @Override
+        public TypeKind getKind() {
+            return TypeKind.INTERSECTION;
+        }
+
+        @Override
+        public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+            return v.visitIntersection(this, p);
+        }
+    }
 
     public static class ArrayType extends Type
             implements javax.lang.model.type.ArrayType {
--- a/src/share/classes/com/sun/tools/javac/code/Types.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/code/Types.java	Wed Oct 31 22:32:32 2012 +0000
@@ -412,9 +412,14 @@
          * Compute the function descriptor associated with a given functional interface
          */
         public FunctionDescriptor findDescriptorInternal(TypeSymbol origin, CompoundScope membersCache) throws FunctionDescriptorLookupError {
-            if (!origin.isInterface()) {
-                //t must be an interface
-                throw failure("not.a.functional.intf");
+            List<Type> targets = !origin.type.isCompound() ?
+                    List.of(origin.type) :
+                    List.filter(((IntersectionClassType)origin.type).getComponents(), syms.objectType);
+            for (Type t : targets) {
+                if (!t.tsym.isInterface()) {
+                    //t must be an interface
+                    throw failure("not.a.functional.intf");
+                }
             }
 
             final ListBuffer<Symbol> abstracts = ListBuffer.lb();
@@ -1967,45 +1972,28 @@
      * @param supertype         is objectType if all bounds are interfaces,
      *                          null otherwise.
      */
-    public Type makeCompoundType(List<Type> bounds,
-                                 Type supertype) {
+    public Type makeCompoundType(List<Type> bounds) {
+        return makeCompoundType(bounds, bounds.head.tsym.isInterface());
+    }
+    public Type makeCompoundType(List<Type> bounds, boolean allInterfaces) {
+        Assert.check(bounds.nonEmpty());
+        Type firstExplicitBound = bounds.head;
+        if (allInterfaces) {
+            bounds = bounds.prepend(syms.objectType);
+        }
         ClassSymbol bc =
             new ClassSymbol(ABSTRACT|PUBLIC|SYNTHETIC|COMPOUND|ACYCLIC,
                             Type.moreInfo
                                 ? names.fromString(bounds.toString())
                                 : names.empty,
+                            null,
                             syms.noSymbol);
-        if (bounds.head.tag == TYPEVAR)
-            // error condition, recover
-                bc.erasure_field = syms.objectType;
-            else
-                bc.erasure_field = erasure(bounds.head);
-            bc.members_field = new Scope(bc);
-        ClassType bt = (ClassType)bc.type;
-        bt.allparams_field = List.nil();
-        if (supertype != null) {
-            bt.supertype_field = supertype;
-            bt.interfaces_field = bounds;
-        } else {
-            bt.supertype_field = bounds.head;
-            bt.interfaces_field = bounds.tail;
-        }
-        Assert.check(bt.supertype_field.tsym.completer != null
-                || !bt.supertype_field.isInterface(),
-            bt.supertype_field);
-        return bt;
-    }
-
-    /**
-     * Same as {@link #makeCompoundType(List,Type)}, except that the
-     * second parameter is computed directly. Note that this might
-     * cause a symbol completion.  Hence, this version of
-     * makeCompoundType may not be called during a classfile read.
-     */
-    public Type makeCompoundType(List<Type> bounds) {
-        Type supertype = (bounds.head.tsym.flags() & INTERFACE) != 0 ?
-            supertype(bounds.head) : null;
-        return makeCompoundType(bounds, supertype);
+        bc.type = new IntersectionClassType(bounds, bc);
+        bc.erasure_field = (bounds.head.tag == TYPEVAR) ?
+                syms.objectType : // error condition, recover
+                erasure(firstExplicitBound);
+        bc.members_field = new Scope(bc);        
+        return bc.type;
     }
 
     /**
@@ -2188,12 +2176,8 @@
      * @param supertype         is objectType if all bounds are interfaces,
      *                          null otherwise.
      */
-    public void setBounds(TypeVar t, List<Type> bounds, Type supertype) {
-        if (bounds.tail.isEmpty())
-            t.bound = bounds.head;
-        else
-            t.bound = makeCompoundType(bounds, supertype);
-        t.rank_field = -1;
+    public void setBounds(TypeVar t, List<Type> bounds) {
+        setBounds(t, bounds, bounds.head.tsym.isInterface());
     }
 
     /**
@@ -2205,10 +2189,10 @@
      * Note that this check might cause a symbol completion. Hence, this version of
      * setBounds may not be called during a classfile read.
      */
-    public void setBounds(TypeVar t, List<Type> bounds) {
-        Type supertype = (bounds.head.tsym.flags() & INTERFACE) != 0 ?
-            syms.objectType : null;
-        setBounds(t, bounds, supertype);
+    public void setBounds(TypeVar t, List<Type> bounds, boolean allInterfaces) {
+        t.bound = bounds.tail.isEmpty() ?
+                bounds.head :
+                makeCompoundType(bounds, allInterfaces);
         t.rank_field = -1;
     }
     // </editor-fold>
@@ -3322,8 +3306,7 @@
                     if (arraySuperType == null) {
                         // JLS 10.8: all arrays implement Cloneable and Serializable.
                         arraySuperType = makeCompoundType(List.of(syms.serializableType,
-                                                                  syms.cloneableType),
-                                                          syms.objectType);
+                                                                  syms.cloneableType), true);
                     }
                 }
             }
--- a/src/share/classes/com/sun/tools/javac/comp/Attr.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java	Wed Oct 31 22:32:32 2012 +0000
@@ -723,21 +723,8 @@
             }
             a.tsym.flags_field &= ~UNATTRIBUTED;
         }
-        for (JCTypeParameter tvar : typarams)
+        for (JCTypeParameter tvar : typarams) {
             chk.checkNonCyclic(tvar.pos(), (TypeVar)tvar.type);
-        attribStats(typarams, env);
-    }
-
-    void attribBounds(List<JCTypeParameter> typarams) {
-        for (JCTypeParameter typaram : typarams) {
-            Type bound = typaram.type.getUpperBound();
-            if (bound != null && bound.tsym instanceof ClassSymbol) {
-                ClassSymbol c = (ClassSymbol)bound.tsym;
-                if ((c.flags_field & COMPOUND) != 0) {
-                    Assert.check((c.flags_field & UNATTRIBUTED) != 0, c);
-                    attribClass(typaram.pos(), c);
-                }
-            }
         }
     }
 
@@ -898,8 +885,13 @@
         try {
             deferredLintHandler.flush(tree.pos());
             chk.checkDeprecatedAnnotation(tree.pos(), m);
-
-            attribBounds(tree.typarams);
+            
+            // Create a new environment with local scope
+            // for attributing the method.
+            Env<AttrContext> localEnv = memberEnter.methodEnv(tree, env);
+            localEnv.info.lint = lint;
+
+            attribStats(tree.typarams, localEnv);
 
             // If we override any other methods, check that we do so properly.
             // JLS ???
@@ -910,12 +902,6 @@
             }
             chk.checkOverride(tree, m);
 
-            // Create a new environment with local scope
-            // for attributing the method.
-            Env<AttrContext> localEnv = memberEnter.methodEnv(tree, env);
-
-            localEnv.info.lint = lint;
-
             if (isDefender) {
                 //when attributing defender method body we need a synthetic 'this' variable
                 //whose type is the type of the enclosing interface
@@ -3465,64 +3451,76 @@
         }
         tree.type = result = t;
     }
+    
+    public void visitTypeIntersection(JCTypeIntersection tree) {
+        attribTypes(tree.bounds, env);
+        tree.type = result = checkIntersection(tree, tree.bounds);
+    }
 
     public void visitTypeParameter(JCTypeParameter tree) {
-        TypeVar a = (TypeVar)tree.type;
+        TypeVar typeVar = (TypeVar)tree.type;
+        if (!typeVar.bound.isErroneous()) {
+            //fixup type-parameter bound computed in 'attribTypeVariables'
+            typeVar.bound = checkIntersection(tree, tree.bounds);
+        }
+    }
+    
+    Type checkIntersection(DiagnosticPosition pos, List<JCExpression> bounds) {
         Set<Type> boundSet = new HashSet<Type>();
-        if (a.bound.isErroneous())
-            return;
-        List<Type> bs = types.getBounds(a);
-        if (tree.bounds.nonEmpty()) {
+        if (bounds.nonEmpty()) {
             // accept class or interface or typevar as first bound.
-            Type b = checkBase(bs.head, tree.bounds.head, env, false, false, false);
-            boundSet.add(types.erasure(b));
-            if (b.isErroneous()) {
-                a.bound = b;
+            bounds.head.type = checkBase(bounds.head.type, bounds.head, env, false, false, false);
+            boundSet.add(types.erasure(bounds.head.type));
+            if (bounds.head.type.isErroneous()) {
+                return bounds.head.type;
             }
-            else if (b.tag == TYPEVAR) {
+            else if (bounds.head.type.tag == TYPEVAR) {
                 // if first bound was a typevar, do not accept further bounds.
-                if (tree.bounds.tail.nonEmpty()) {
-                    log.error(tree.bounds.tail.head.pos(),
+                if (bounds.tail.nonEmpty()) {
+                    log.error(bounds.tail.head.pos(),
                               "type.var.may.not.be.followed.by.other.bounds");
-                    tree.bounds = List.of(tree.bounds.head);
-                    a.bound = bs.head;
+                    return bounds.head.type;
                 }
             } else {
                 // if first bound was a class or interface, accept only interfaces
                 // as further bounds.
-                for (JCExpression bound : tree.bounds.tail) {
-                    bs = bs.tail;
-                    Type i = checkBase(bs.head, bound, env, false, true, false);
-                    if (i.isErroneous())
-                        a.bound = i;
-                    else if (i.tag == CLASS)
-                        chk.checkNotRepeated(bound.pos(), types.erasure(i), boundSet);
+                for (JCExpression bound : bounds.tail) {
+                    bound.type = checkBase(bound.type, bound, env, false, true, false);
+                    if (bound.type.isErroneous()) {
+                        bounds = List.of(bound);
+                    }
+                    else if (bound.type.tag == CLASS) {
+                        chk.checkNotRepeated(bound.pos(), types.erasure(bound.type), boundSet);
+                    }
                 }
             }
         }
-        bs = types.getBounds(a);
-
-        // in case of multiple bounds ...
-        if (bs.length() > 1) {
+        
+        if (bounds.length() == 0) {
+            return syms.objectType;
+        } else if (bounds.length() == 1) {
+            return bounds.head.type;
+        } else {
+            Type owntype = types.makeCompoundType(TreeInfo.types(bounds));
             // ... the variable's bound is a class type flagged COMPOUND
             // (see comment for TypeVar.bound).
             // In this case, generate a class tree that represents the
             // bound class, ...
             JCExpression extending;
             List<JCExpression> implementing;
-            if ((bs.head.tsym.flags() & INTERFACE) == 0) {
-                extending = tree.bounds.head;
-                implementing = tree.bounds.tail;
+            if (!bounds.head.type.isInterface()) {
+                extending = bounds.head;
+                implementing = bounds.tail;
             } else {
                 extending = null;
-                implementing = tree.bounds;
+                implementing = bounds;
             }
-            JCClassDecl cd = make.at(tree.pos).ClassDef(
+            JCClassDecl cd = make.at(pos).ClassDef(
                 make.Modifiers(PUBLIC | ABSTRACT),
-                tree.name, List.<JCTypeParameter>nil(),
+                names.empty, List.<JCTypeParameter>nil(),
                 extending, implementing, List.<JCTree>nil());
 
-            ClassSymbol c = (ClassSymbol)a.getUpperBound().tsym;
+            ClassSymbol c = (ClassSymbol)owntype.tsym;
             Assert.check((c.flags() & COMPOUND) != 0);
             cd.sym = c;
             c.sourcefile = env.toplevel.sourcefile;
@@ -3531,10 +3529,11 @@
             c.flags_field |= UNATTRIBUTED;
             Env<AttrContext> cenv = enter.classEnv(cd, env);
             enter.typeEnvs.put(c, cenv);
+            attribClass(c);
+            return owntype;
         }
     }
 
-
     public void visitWildcard(JCWildcard tree) {
         //- System.err.println("visitWildcard("+tree+");");//DEBUG
         Type type = (tree.kind.kind == BoundKind.UNBOUND)
@@ -3688,7 +3687,7 @@
         chk.validateAnnotations(tree.mods.annotations, c);
 
         // Validate type parameters, supertype and interfaces.
-        attribBounds(tree.typarams);
+        attribStats(tree.typarams, env);
         if (!c.isAnonymous()) {
             //already checked if anonymous
             chk.validate(tree.typarams, env);
--- a/src/share/classes/com/sun/tools/javac/comp/TransTypes.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/TransTypes.java	Wed Oct 31 22:32:32 2012 +0000
@@ -568,6 +568,7 @@
             tree.body = translate(tree.body, null);
             //save non-erased target
             tree.targetType = tree.type;
+            Assert.check(!tree.targetType.isCompound(), "Intersection-type targets not supported yet!");
             tree.type = erasure(tree.type);
             result = tree;
         }
@@ -803,6 +804,7 @@
         tree.expr = translate(tree.expr, null);
         //save non-erased target
         tree.targetType = tree.type;
+        Assert.check(!tree.targetType.isCompound(), "Intersection-type targets not supported yet!");
         tree.type = erasure(tree.type);
         result = tree;
     }
@@ -819,6 +821,12 @@
         JCTree clazz = translate(tree.clazz, null);
         result = clazz;
     }
+    
+    public void visitTypeIntersection(JCTypeIntersection tree) {
+        tree.bounds = translate(tree.bounds, null);
+        tree.type = erasure(tree.type);
+        result = tree;
+    }
 
 /**************************************************************************
  * utility methods
--- a/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Wed Oct 31 22:32:32 2012 +0000
@@ -846,17 +846,17 @@
             tvar = (TypeVar)findTypeVar(name);
         }
         List<Type> bounds = List.nil();
-        Type st = null;
+        boolean allInterfaces = false;
         if (signature[sigp] == ':' && signature[sigp+1] == ':') {
             sigp++;
-            st = syms.objectType;
+            allInterfaces = true;
         }
         while (signature[sigp] == ':') {
             sigp++;
             bounds = bounds.prepend(sigToType());
         }
         if (!sigEnterPhase) {
-            types.setBounds(tvar, bounds.reverse(), st);
+            types.setBounds(tvar, bounds.reverse(), allInterfaces);
         }
         return tvar;
     }
--- a/src/share/classes/com/sun/tools/javac/model/JavacTypes.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/model/JavacTypes.java	Wed Oct 31 22:32:32 2012 +0000
@@ -74,6 +74,7 @@
     public Element asElement(TypeMirror t) {
         switch (t.getKind()) {
             case DECLARED:
+            case INTERSECTION:
             case ERROR:
             case TYPEVAR:
                 Type type = cast(Type.class, t);
--- a/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Wed Oct 31 22:32:32 2012 +0000
@@ -224,24 +224,40 @@
         S.nextToken();
         token = S.token();
     }
-
+    
     protected boolean peekToken(TokenKind tk) {
-        return S.token(1).kind == tk;
+        return peekToken(0, tk);
+    }
+
+    protected boolean peekToken(int lookahead, TokenKind tk) {
+        return S.token(lookahead + 1).kind == tk;
+    }
+    
+    protected boolean peekToken(TokenKind tk1, TokenKind tk2) {
+        return peekToken(0, tk1, tk2);
     }
 
-    protected boolean peekToken(TokenKind tk1, TokenKind tk2) {
-        return S.token(1).kind == tk1 &&
-                S.token(2).kind == tk2;
+    protected boolean peekToken(int lookahead, TokenKind tk1, TokenKind tk2) {
+        return S.token(lookahead + 1).kind == tk1 &&
+                S.token(lookahead + 2).kind == tk2;
+    }
+    
+    protected boolean peekToken(TokenKind tk1, TokenKind tk2, TokenKind tk3) {
+        return peekToken(0, tk1, tk2, tk3);
     }
 
-    protected boolean peekToken(TokenKind tk1, TokenKind tk2, TokenKind tk3) {
-        return S.token(1).kind == tk1 &&
-                S.token(2).kind == tk2 &&
-                S.token(3).kind == tk3;
+    protected boolean peekToken(int lookahead, TokenKind tk1, TokenKind tk2, TokenKind tk3) {
+        return S.token(lookahead + 1).kind == tk1 &&
+                S.token(lookahead + 2).kind == tk2 &&
+                S.token(lookahead + 3).kind == tk3;
+    }
+    
+    protected boolean peekToken(TokenKind... kinds) {
+        return peekToken(0, kinds);
     }
 
-    protected boolean peekToken(TokenKind... kinds) {
-        for (int lookahead = 0 ; lookahead < kinds.length ; lookahead++) {
+    protected boolean peekToken(int lookahead, TokenKind... kinds) {
+        for (; lookahead < kinds.length ; lookahead++) {
             if (S.token(lookahead + 1).kind != kinds[lookahead]) {
                 return false;
             }
@@ -928,7 +944,7 @@
         int pos = token.pos;
         JCExpression t;
         List<JCExpression> typeArgs = typeArgumentsOpt(EXPR);
-        switch (token.kind) {
+        outer: switch (token.kind) {
         case QUES:
             if ((mode & TYPE) != 0 && (mode & (TYPEARG|NOPARAMS)) == TYPEARG) {
                 mode = TYPE;
@@ -952,103 +968,40 @@
             } else return illegal();
             break;
         case LPAREN:
-            if (typeArgs == null && (mode & EXPR) != 0) {
-                if (peekToken(MONKEYS_AT) ||
-                        peekToken(FINAL) ||
-                        peekToken(RPAREN) ||
-                        peekToken(IDENTIFIER, COMMA) ||
-                        peekToken(IDENTIFIER, RPAREN, ARROW)) {
-                    //implicit n-ary lambda
-                    t = lambdaExpressionOrStatement(true, peekToken(MONKEYS_AT) || peekToken(FINAL), pos);
-                    break;
-                } else {
-                    nextToken();
-                    mode = EXPR | TYPE | NOPARAMS;
-                    t = term3();
-                    if ((mode & TYPE) != 0 && token.kind == LT) {
-                        // Could be a cast to a parameterized type
-                        JCTree.Tag op = JCTree.Tag.LT;
-                        int pos1 = token.pos;
-                        nextToken();
-                        mode &= (EXPR | TYPE);
-                        mode |= TYPEARG;
-                        JCExpression t1 = term3();
-                        if ((mode & TYPE) != 0 &&
-                            (token.kind == COMMA || token.kind == GT)) {
-                            mode = TYPE;
-                            ListBuffer<JCExpression> args = new ListBuffer<JCExpression>();
-                            args.append(t1);
-                            while (token.kind == COMMA) {
-                                nextToken();
-                                args.append(typeArgument());
-                            }
-                            accept(GT);
-                            t = toP(F.at(pos1).TypeApply(t, args.toList()));
-                            checkGenerics();
-                            mode = EXPR | TYPE; //could be a lambda or a method ref or a cast to a type
-                            t = term3Rest(t, typeArgs);
-                            if (token.kind == IDENTIFIER || token.kind == ELLIPSIS) {
-                                //explicit lambda (w/ generic type)
-                                mode = EXPR;
-                                JCModifiers mods = F.at(token.pos).Modifiers(Flags.PARAMETER);
-                                if (token.kind == ELLIPSIS) {
-                                    mods.flags = Flags.VARARGS;
-                                    t = to(F.at(token.pos).TypeArray(t));
-                                    nextToken();
-                                }
-                                t = lambdaExpressionOrStatement(variableDeclaratorId(mods, t), pos);
-                                break;
-                            }
-                        } else if ((mode & EXPR) != 0) {
-                            mode = EXPR;
-                            JCExpression e = term2Rest(t1, TreeInfo.shiftPrec);
-                            t = F.at(pos1).Binary(op, t, e);
-                            t = termRest(term1Rest(term2Rest(t, TreeInfo.orPrec)));
-                        } else {
-                            accept(GT);
-                        }
-                    } else if ((mode & TYPE) != 0 &&
-                            (token.kind == IDENTIFIER || token.kind == ELLIPSIS)) {
-                        //explicit lambda (w/ non-generic type)
+            if (typeArgs == null && (mode & EXPR) != 0) {                
+                ParensResult pres = analyzeParens();
+                switch (pres) {
+                    case CAST:
+                       accept(LPAREN);
+                       mode = TYPE;
+                       int pos1 = pos;
+                       List<JCExpression> targets = List.of(t = term3());
+                       while (token.kind == AMP) {
+                           accept(AMP);
+                           targets = targets.prepend(term3());
+                       }
+                       if (targets.length() > 1) {
+                           t = toP(F.at(pos1).TypeIntersection(targets.reverse()));
+                       }
+                       accept(RPAREN);
+                       mode = EXPR;
+                       JCExpression t1 = term3();
+                       return F.at(pos).TypeCast(t, t1);
+                    case IMPLICIT_LAMBDA:                        
+                    case EXPLICIT_LAMBDA:
+                        t = lambdaExpressionOrStatement(true, pres == ParensResult.EXPLICIT_LAMBDA, pos);
+                        break outer;
+                    default: //PARENS
+                        accept(LPAREN);
                         mode = EXPR;
-                        JCModifiers mods = F.at(token.pos).Modifiers(Flags.PARAMETER);
-                        if (token.kind == ELLIPSIS) {
-                            mods.flags = Flags.VARARGS;
-                            t = to(F.at(token.pos).TypeArray(t));
-                            nextToken();
-                        }
-                        t = lambdaExpressionOrStatement(variableDeclaratorId(mods, t), pos);
+                        t = termRest(term1Rest(term2Rest(term3(), TreeInfo.orPrec)));
+                        accept(RPAREN);
+                        t = toP(F.at(pos).Parens(t));
                         break;
-                    } else {
-                        t = termRest(term1Rest(term2Rest(t, TreeInfo.orPrec)));
-                    }
-                }
-
-                accept(RPAREN);
-                lastmode = mode;
-                mode = EXPR;
-                if ((lastmode & EXPR) == 0) {
-                    JCExpression t1 = term3();
-                    return F.at(pos).TypeCast(t, t1);
-                } else if ((lastmode & TYPE) != 0) {
-                    switch (token.kind) {
-                    /*case PLUSPLUS: case SUBSUB: */
-                    case BANG: case TILDE:
-                    case LPAREN: case THIS: case SUPER:
-                    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 BYTE: case SHORT: case CHAR: case INT:
-                    case LONG: case FLOAT: case DOUBLE: case BOOLEAN: case VOID:
-                        JCExpression t1 = term3();
-                        return F.at(pos).TypeCast(t, t1);
-                    }
                 }
             } else {
                 return illegal();
             }
-            t = toP(F.at(pos).Parens(t));
             break;
         case THIS:
             if ((mode & EXPR) != 0) {
@@ -1332,6 +1285,138 @@
             }
         }
     }
+    
+    /**
+     * If we see an identifier followed by a '&lt;' it could be an unbound
+     * method reference or a binary expression. To disambiguate, look for a
+     * matching '&gt;' and see if the subsequent terminal is either '.' or '#'.
+     */
+    @SuppressWarnings("fallthrough")
+    ParensResult analyzeParens() {
+        int depth = 0;
+        boolean type = false;
+        for (int lookahead = 0 ; ; lookahead++) {
+            TokenKind tk = S.token(lookahead).kind;
+            switch (tk) {
+                case EXTENDS: case SUPER: case COMMA:
+                    type = true;
+                case QUES: case DOT: case AMP:
+                    //skip
+                    break;
+                case BYTE: case SHORT: case INT: case LONG: case FLOAT:
+                case DOUBLE: case BOOLEAN: case CHAR:
+                    if (peekToken(lookahead, RPAREN)) {
+                        //Type, ')' -> cast
+                        return ParensResult.CAST;
+                    } else if (peekToken(lookahead, IDENTIFIER)) {
+                        //Type, 'Identifier -> explicit lambda
+                        return ParensResult.EXPLICIT_LAMBDA;
+                    }
+                    break;
+                case LPAREN:
+                    if (lookahead != 0) {
+                        // '(' in a non-starting position -> parens
+                        return ParensResult.PARENS;
+                    } else if (peekToken(lookahead, RPAREN)) {
+                        // '(', ')' -> explicit lambda
+                        return ParensResult.EXPLICIT_LAMBDA;
+                    }
+                    break;
+                case RPAREN:
+                    // if we have seen something that looks like a type,
+                    // then it's a cast expression
+                    if (type) return ParensResult.CAST;
+                    // otherwise, disambiguate cast vs. parenthesized expression
+                    // based on subsequent token.
+                    switch (S.token(lookahead + 1).kind) {
+                        /*case PLUSPLUS: case SUBSUB: */
+                        case BANG: case TILDE:
+                        case LPAREN: case THIS: case SUPER:
+                        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 BYTE: case SHORT: case CHAR: case INT:
+                        case LONG: case FLOAT: case DOUBLE: case BOOLEAN: case VOID:
+                            return ParensResult.CAST;
+                        default:
+                            return ParensResult.PARENS;
+                    }
+                case IDENTIFIER:
+                    if (peekToken(lookahead, IDENTIFIER)) {
+                        // Identifier, Identifier -> explicit lambda
+                        return ParensResult.EXPLICIT_LAMBDA;                        
+                    } else if (peekToken(lookahead, RPAREN, ARROW)) {
+                        // Identifier, ')' '->' -> implicit lambda
+                        return ParensResult.IMPLICIT_LAMBDA;
+                    }
+                    break;
+                case FINAL:
+                case ELLIPSIS:
+                case MONKEYS_AT:
+                    //those can only appear in explicit lambdas
+                    return ParensResult.EXPLICIT_LAMBDA;
+                case LBRACKET:
+                    if (peekToken(lookahead, RBRACKET, IDENTIFIER)) {
+                        // '[', ']', Identifier -> explicit lambda
+                        return ParensResult.EXPLICIT_LAMBDA;
+                    } else if (peekToken(lookahead, RBRACKET, RPAREN) ||
+                            peekToken(lookahead, RBRACKET, AMP)) {
+                        // '[', ']', ')' -> cast
+                        // '[', ']', '&' -> cast (intersection type)
+                        return ParensResult.CAST;
+                    } else if (peekToken(lookahead, RBRACKET)) {
+                        //consume the ']' and skip
+                        type = true;
+                        lookahead++;
+                        break;
+                    } else {
+                        return ParensResult.PARENS;
+                    }
+                case LT:
+                    depth++; break;
+                case GTGTGT:
+                    depth--;
+                case GTGT:
+                    depth--;
+                case GT:
+                    depth--;
+                    if (depth == 0) {
+                        if (peekToken(lookahead, RPAREN) ||
+                                peekToken(lookahead, AMP)) {
+                            // '>', ')' -> cast
+                            // '>', '&' -> cast
+                            return ParensResult.CAST;
+                        } else if (peekToken(lookahead, IDENTIFIER, COMMA) ||
+                                peekToken(lookahead, IDENTIFIER, RPAREN, ARROW) ||
+                                peekToken(lookahead, ELLIPSIS)) {
+                            // '>', Identifier, ',' -> explicit lambda
+                            // '>', Identifier, ')', '->' -> explicit lambda
+                            // '>', '...' -> explicit lambda
+                            return ParensResult.EXPLICIT_LAMBDA;
+                        }
+                        //it looks a type, but could still be (i) a cast to generic type,
+                        //(ii) an unbound method reference or (iii) an explicit lambda
+                        type = true;
+                        break;
+                    } else if (depth < 0) {
+                        //unbalanced '<', '>' - not a generic type
+                        return ParensResult.PARENS;
+                    }
+                    break;
+                default:
+                    //this includes EOF
+                    return ParensResult.PARENS;
+            }
+        }
+    }
+    
+    enum ParensResult {
+        CAST,
+        EXPLICIT_LAMBDA,
+        IMPLICIT_LAMBDA,
+        PARENS;
+    }
 
     JCExpression lambdaExpressionOrStatement(JCVariableDecl firstParam, int pos) {
         ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>();
--- a/src/share/classes/com/sun/tools/javac/tree/JCTree.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/tree/JCTree.java	Wed Oct 31 22:32:32 2012 +0000
@@ -253,6 +253,10 @@
         /** Union types, of type TypeUnion
          */
         TYPEUNION,
+        
+        /** Intersection types, of type TypeIntersection
+         */
+        TYPEINTERSECTION,
 
         /** Formal type parameters, of type TypeParameter.
          */
@@ -2117,6 +2121,34 @@
             return TYPEUNION;
         }
     }
+    
+    /**
+     * An intersection type, T1 & T2 & ... Tn (used in cast expressions)
+     */
+    public static class JCTypeIntersection extends JCExpression implements IntersectionTypeTree {
+
+        public List<JCExpression> bounds;
+
+        protected JCTypeIntersection(List<JCExpression> bounds) {
+            this.bounds = bounds;
+        }
+        @Override
+        public void accept(Visitor v) { v.visitTypeIntersection(this); }
+
+        public Kind getKind() { return Kind.INTERSECTION_TYPE; }
+
+        public List<JCExpression> getBounds() {
+            return bounds;
+        }
+        @Override
+        public <R,D> R accept(TreeVisitor<R,D> v, D d) {
+            return v.visitIntersectionType(this, d);
+        }
+        @Override
+        public Tag getTag() {
+            return TYPEINTERSECTION;
+        }
+    }
 
     /**
      * A formal class parameter.
@@ -2440,6 +2472,7 @@
         public void visitTypeArray(JCArrayTypeTree that)     { visitTree(that); }
         public void visitTypeApply(JCTypeApply that)         { visitTree(that); }
         public void visitTypeUnion(JCTypeUnion that)         { visitTree(that); }
+        public void visitTypeIntersection(JCTypeIntersection that)  { visitTree(that); }
         public void visitTypeParameter(JCTypeParameter that) { visitTree(that); }
         public void visitWildcard(JCWildcard that)           { visitTree(that); }
         public void visitTypeBoundKind(TypeBoundKind that)   { visitTree(that); }
--- a/src/share/classes/com/sun/tools/javac/tree/Pretty.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/tree/Pretty.java	Wed Oct 31 22:32:32 2012 +0000
@@ -1250,6 +1250,14 @@
             throw new UncheckedIOException(e);
         }
     }
+    
+    public void visitTypeIntersection(JCTypeIntersection tree) {
+        try {
+            printExprs(tree.bounds, " & ");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
 
     public void visitTypeParameter(JCTypeParameter tree) {
         try {
--- a/src/share/classes/com/sun/tools/javac/tree/TreeCopier.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/tree/TreeCopier.java	Wed Oct 31 22:32:32 2012 +0000
@@ -357,6 +357,12 @@
         List<JCExpression> components = copy(t.alternatives, p);
         return M.at(t.pos).TypeUnion(components);
     }
+    
+    public JCTree visitIntersectionType(IntersectionTypeTree node, P p) {
+        JCTypeIntersection t = (JCTypeIntersection) node;
+        List<JCExpression> components = copy(t.bounds, p);
+        return M.at(t.pos).TypeIntersection(components);
+    }
 
     public JCTree visitArrayType(ArrayTypeTree node, P p) {
         JCArrayTypeTree t = (JCArrayTypeTree) node;
--- a/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java	Wed Oct 31 22:32:32 2012 +0000
@@ -455,6 +455,12 @@
         tree.pos = pos;
         return tree;
     }
+    
+    public JCTypeIntersection TypeIntersection(List<JCExpression> components) {
+        JCTypeIntersection tree = new JCTypeIntersection(components);
+        tree.pos = pos;
+        return tree;
+    }
 
     public JCTypeParameter TypeParameter(Name name, List<JCExpression> bounds) {
         JCTypeParameter tree = new JCTypeParameter(name, bounds);
--- a/src/share/classes/com/sun/tools/javac/tree/TreeScanner.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/tree/TreeScanner.java	Wed Oct 31 22:32:32 2012 +0000
@@ -285,6 +285,10 @@
     public void visitTypeUnion(JCTypeUnion tree) {
         scan(tree.alternatives);
     }
+    
+    public void visitTypeIntersection(JCTypeIntersection tree) {
+        scan(tree.bounds);
+    }
 
     public void visitTypeParameter(JCTypeParameter tree) {
         scan(tree.bounds);
--- a/src/share/classes/com/sun/tools/javac/tree/TreeTranslator.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/com/sun/tools/javac/tree/TreeTranslator.java	Wed Oct 31 22:32:32 2012 +0000
@@ -378,6 +378,11 @@
         tree.alternatives = translate(tree.alternatives);
         result = tree;
     }
+    
+    public void visitTypeIntersection(JCTypeIntersection tree) {
+        tree.bounds = translate(tree.bounds);
+        result = tree;
+    }
 
     public void visitTypeParameter(JCTypeParameter tree) {
         tree.bounds = translate(tree.bounds);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/javax/lang/model/type/IntersectionType.java	Wed Oct 31 22:32:32 2012 +0000
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+package javax.lang.model.type;
+
+import java.util.List;
+
+/**
+ * Represents a union type.
+ *
+ * As of the {@link javax.lang.model.SourceVersion#RELEASE_8
+ * RELEASE_8} source version, intersection types can appear as the target type
+ * of a cast expression.
+ *
+ * @since 1.7
+ */
+public interface IntersectionType extends TypeMirror {
+
+    /**
+     * Return the bounds comprising this intersection type.
+     *
+     * @return the bounds of this intersection types.
+     */
+    List<? extends TypeMirror> getBounds();
+}
--- a/src/share/classes/javax/lang/model/type/TypeKind.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/javax/lang/model/type/TypeKind.java	Wed Oct 31 22:32:32 2012 +0000
@@ -144,7 +144,14 @@
       *
       * @since 1.7
       */
-    UNION;
+    UNION,
+    
+    /**
+      * An intersection type.
+      *
+      * @since 1.8
+      */
+    INTERSECTION;
 
     /**
      * Returns {@code true} if this kind corresponds to a primitive
--- a/src/share/classes/javax/lang/model/type/TypeVisitor.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/javax/lang/model/type/TypeVisitor.java	Wed Oct 31 22:32:32 2012 +0000
@@ -172,4 +172,14 @@
      * @since 1.7
      */
     R visitUnion(UnionType t, P p);
+    
+    /**
+     * Visits an intersection type.
+     *
+     * @param t the type to visit
+     * @param p a visitor-specified parameter
+     * @return  a visitor-specified result
+     * @since 1.8
+     */
+    R visitIntersection(IntersectionType t, P p);
 }
--- a/src/share/classes/javax/lang/model/util/AbstractTypeVisitor6.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/javax/lang/model/util/AbstractTypeVisitor6.java	Wed Oct 31 22:32:32 2012 +0000
@@ -109,6 +109,20 @@
     public R visitUnion(UnionType t, P p) {
         return visitUnknown(t, p);
     }
+    
+    /**
+     * Visits an {@code IntersectionType} element by calling {@code
+     * visitUnknown}.
+
+     * @param t  {@inheritDoc}
+     * @param p  {@inheritDoc}
+     * @return the result of {@code visitUnknown}
+     *
+     * @since 1.8
+     */
+    public R visitIntersection(IntersectionType t, P p) {
+        return visitUnknown(t, p);
+    }
 
     /**
      * {@inheritDoc}
--- a/src/share/classes/javax/lang/model/util/AbstractTypeVisitor8.java	Thu Oct 25 23:25:08 2012 +0100
+++ b/src/share/classes/javax/lang/model/util/AbstractTypeVisitor8.java	Wed Oct 31 22:32:32 2012 +0000
@@ -66,4 +66,13 @@
     protected AbstractTypeVisitor8() {
         super();
     }
+    
+    /**
+     * Visits an {@code IntersectionType} in a manner defined by a subclass.
+     *
+     * @param t  {@inheritDoc}
+     * @param p  {@inheritDoc}
+     * @return the result of the visit as defined by a subclass
+     */
+    public abstract R visitIntersection(IntersectionType t, P p);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/cast/intersection/IntersectionTypeCastTest.java	Wed Oct 31 22:32:32 2012 +0000
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 2012, 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 intersection type cast test
+ */
+
+import com.sun.source.util.JavacTask;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+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.StandardJavaFileManager;
+import javax.tools.ToolProvider;
+
+public class IntersectionTypeCastTest {
+
+    static int checkCount = 0;
+    
+    interface Type {
+        boolean subtypeOf(Type that);
+        String asString();
+        boolean isClass();
+        boolean isInterface();
+    }
+
+    enum InterfaceKind implements Type {
+        A("interface A { }\n", "A", null),
+        B("interface B { }\n", "B", null),
+        C("interface C extends A { }\n", "C", A);
+
+        String declStr;
+        String typeStr;
+        InterfaceKind superInterface;
+
+        InterfaceKind(String declStr, String typeStr, InterfaceKind superInterface) {
+            this.declStr = declStr;
+            this.typeStr = typeStr;
+            this.superInterface = superInterface;
+        }
+
+        @Override
+        public boolean subtypeOf(Type that) {
+            return this == that || superInterface == that || that == ClassKind.OBJECT;
+        }
+
+        @Override
+        public String asString() {
+            return typeStr;
+        }
+
+        @Override
+        public boolean isClass() {
+            return false;
+        }
+
+        @Override
+        public boolean isInterface() {
+            return true;
+        }
+    }
+    
+    enum ClassKind implements Type {
+        OBJECT(null, "Object"),
+        CA("#M class CA implements A { }\n", "CA", InterfaceKind.A),
+        CB("#M class CB implements B { }\n", "CB", InterfaceKind.B),
+        CAB("#M class CAB implements A, B { }\n", "CAB", InterfaceKind.A, InterfaceKind.B),
+        CC("#M class CC implements C { }\n", "CC", InterfaceKind.C, InterfaceKind.A),
+        CCA("#M class CCA implements C, A { }\n", "CCA", InterfaceKind.C, InterfaceKind.A),
+        CCB("#M class CCB implements C, B { }\n", "CCB", InterfaceKind.C, InterfaceKind.A, InterfaceKind.B),
+        CCAB("#M class CCAB implements C, A, B { }\n", "CCAB", InterfaceKind.C, InterfaceKind.A, InterfaceKind.B);
+        
+        String declTemplate;
+        String typeStr;
+        List<InterfaceKind> superInterfaces;
+
+        ClassKind(String declTemplate, String typeStr, InterfaceKind... superInterfaces) {
+            this.declTemplate = declTemplate;
+            this.typeStr = typeStr;
+            this.superInterfaces = List.from(superInterfaces);
+        }
+        
+        String getDecl(ModifierKind mod) {
+            return declTemplate != null ?
+                    declTemplate.replaceAll("#M", mod.modStr) :
+                    "";
+        }
+
+        @Override
+        public boolean subtypeOf(Type that) {
+            return this == that || superInterfaces.contains(that) || that == OBJECT;
+        }
+        
+        @Override
+        public String asString() {
+            return typeStr;
+        }
+        
+        @Override
+        public boolean isClass() {
+            return true;
+        }
+
+        @Override
+        public boolean isInterface() {
+            return false;
+        }
+    }
+    
+    enum ModifierKind {
+        NONE(""),
+        FINAL("final");
+        
+        String modStr;
+
+        ModifierKind(String modStr) {
+            this.modStr = modStr;
+        }
+    }
+    
+    enum CastKind {
+        CLASS("(#C)", 0),
+        INTERFACE("(#I0)", 1),
+        INTERSECTION2("(#C & #I0)", 1),
+        INTERSECTION3("(#C & #I0 & #I1)", 2);
+        //INTERSECTION4("(#C & #I0 & #I1 & #I2)", 3);
+        
+        String castTemplate;
+        int interfaceBounds;
+
+        CastKind(String castTemplate, int interfaceBounds) {
+            this.castTemplate = castTemplate;
+            this.interfaceBounds = interfaceBounds;
+        }
+    }
+    
+    static class CastInfo {
+        CastKind kind;
+        Type[] types;
+
+        CastInfo(CastKind kind, Type... types) {
+            this.kind = kind;
+            this.types = types;
+        }
+        
+        String getCast() {
+            String temp = kind.castTemplate.replaceAll("#C", types[0].asString());
+            for (int i = 0; i < kind.interfaceBounds ; i++) {
+                temp = temp.replace(String.format("#I%d", i), types[i + 1].asString());
+            }
+            return temp;
+        }
+        
+        boolean hasDuplicateTypes() {
+            for (int i = 0 ; i < types.length ; i++) {
+                for (int j = 0 ; j < types.length ; j++) {
+                    if (i != j && types[i] == types[j]) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+        
+        boolean compatibleWith(ModifierKind mod, CastInfo that) {
+            for (Type t1 : types) {
+                for (Type t2 : that.types) {
+                    boolean compat =
+                            t1.subtypeOf(t2) ||
+                            t2.subtypeOf(t1) ||
+                            (t1.isInterface() && t2.isInterface()) || //side-cast (1)
+                            (mod == ModifierKind.NONE && (t1.isInterface() != t2.isInterface())); //side-cast (2)
+                    if (!compat) return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    public static void main(String... args) throws Exception {
+        //create default shared JavaCompiler - reused across multiple compilations
+        JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
+        StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
+
+        for (ModifierKind mod : ModifierKind.values()) {
+            for (CastInfo cast1 : allCastInfo()) {
+                for (CastInfo cast2 : allCastInfo()) {
+                    new IntersectionTypeCastTest(mod, cast1, cast2).run(comp, fm);
+                }
+            }
+        }
+        System.out.println("Total check executed: " + checkCount);
+    }
+    
+    static List<CastInfo> allCastInfo() {
+        ListBuffer<CastInfo> buf = ListBuffer.lb();
+        for (CastKind kind : CastKind.values()) {
+            for (ClassKind clazz : ClassKind.values()) {
+                if (kind == CastKind.INTERFACE && clazz != ClassKind.OBJECT) {
+                    continue;
+                } else if (kind.interfaceBounds == 0) {
+                    buf.append(new CastInfo(kind, clazz));
+                    continue;
+                } else {
+                    for (InterfaceKind intf1 : InterfaceKind.values()) {
+                        if (kind.interfaceBounds == 1) {
+                            buf.append(new CastInfo(kind, clazz, intf1));
+                            continue;
+                        } else {
+                            for (InterfaceKind intf2 : InterfaceKind.values()) {
+                                if (kind.interfaceBounds == 2) {
+                                    buf.append(new CastInfo(kind, clazz, intf1, intf2));
+                                    continue;
+                                } else {
+                                    for (InterfaceKind intf3 : InterfaceKind.values()) {
+                                        buf.append(new CastInfo(kind, clazz, intf1, intf2, intf3));
+                                        continue;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return buf.toList();
+    }
+    
+    ModifierKind mod;
+    CastInfo cast1, cast2;
+    JavaSource source;
+    DiagnosticChecker diagChecker;
+
+    IntersectionTypeCastTest(ModifierKind mod, CastInfo cast1, CastInfo cast2) {
+        this.mod = mod;
+        this.cast1 = cast1;
+        this.cast2 = cast2;
+        this.source = new JavaSource();
+        this.diagChecker = new DiagnosticChecker();
+    }
+
+    class JavaSource extends SimpleJavaFileObject {
+
+        String bodyTemplate = "class Test {\n" +
+                              "   void test() {\n" +
+                              "      Object o = #C1#C2null;\n" +
+                              "   } }";
+
+        String source = "";
+
+        public JavaSource() {
+            super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
+            for (ClassKind ck : ClassKind.values()) {                
+                source += ck.getDecl(mod);
+            }
+            for (InterfaceKind ik : InterfaceKind.values()) {
+                source += ik.declStr;
+            }
+            source += bodyTemplate.replaceAll("#C1", cast1.getCast()).replaceAll("#C2", cast2.getCast());
+        }
+
+        @Override
+        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+            return source;
+        }
+    }
+
+    void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception {
+        JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker,
+                null, null, Arrays.asList(source));
+        try {
+            ct.analyze();
+        } catch (Throwable ex) {
+            throw new AssertionError("Error thrown when compiling the following code:\n" + source.getCharContent(true));
+        }
+        check();
+    }
+
+    void check() {
+        checkCount++;
+
+        boolean errorExpected = cast1.hasDuplicateTypes() || cast2.hasDuplicateTypes();
+        
+        errorExpected |= !cast2.compatibleWith(mod, cast1);
+
+        if (errorExpected != diagChecker.errorFound) {
+            throw new Error("invalid diagnostics for source:\n" +
+                source.getCharContent(true) +
+                "\nFound error: " + diagChecker.errorFound +
+                "\nExpected error: " + errorExpected);
+        }
+    }
+
+    static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
+
+        boolean errorFound;
+
+        public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+            if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
+                errorFound = true;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/cast/intersection/model/Check.java	Wed Oct 31 22:32:32 2012 +0000
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+/**
+ * Annotation used by ModelChecker to mark the class whose model is to be checked
+ */
+@interface Check {}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/cast/intersection/model/IntersectionTypeInfo.java	Wed Oct 31 22:32:32 2012 +0000
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+/**
+ * Used by ModelChecker to validate the modeling information of a union type.
+ */
+@interface IntersectionTypeInfo {
+    String[] value();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/cast/intersection/model/Member.java	Wed Oct 31 22:32:32 2012 +0000
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+import javax.lang.model.element.ElementKind;
+
+/**
+ * Annotation used by ModelChecker to mark a member that is to be checked
+ */
+@interface Member {
+   ElementKind value();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/cast/intersection/model/Model01.java	Wed Oct 31 22:32:32 2012 +0000
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+import javax.lang.model.element.ElementKind;
+
+@Check
+class Test {
+
+    interface A {
+        @Member(ElementKind.METHOD)
+        public void m1();
+    }
+    
+    interface B {
+        @Member(ElementKind.METHOD)
+        public void m2();
+    }
+
+    void test(){
+        @IntersectionTypeInfo({"java.lang.Object", "Test.A", "Test.B"})
+        Object o = (A & B)null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/cast/intersection/model/ModelChecker.java	Wed Oct 31 22:32:32 2012 +0000
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2012, 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
+ * @bug 6993963 7025809
+ * @summary Project Coin: Use precise exception analysis for effectively final catch parameters
+ * @library ../../../lib
+ * @build JavacTestingAbstractProcessor ModelChecker
+ * @compile -processor ModelChecker Model01.java
+ */
+
+import com.sun.source.tree.CatchTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.TypeCastTree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.TreePathScanner;
+import com.sun.source.util.Trees;
+import com.sun.source.util.TreePath;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+
+import java.util.Set;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.IntersectionType;
+import javax.lang.model.type.UnknownTypeException;
+import javax.lang.model.util.SimpleTypeVisitor6;
+import javax.lang.model.util.SimpleTypeVisitor7;
+
+@SupportedAnnotationTypes("Check")
+public class ModelChecker extends JavacTestingAbstractProcessor {
+
+    @Override
+    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+        if (roundEnv.processingOver())
+            return true;
+
+        Trees trees = Trees.instance(processingEnv);
+
+        TypeElement testAnno = elements.getTypeElement("Check");
+        for (Element elem: roundEnv.getElementsAnnotatedWith(testAnno)) {
+            TreePath p = trees.getPath(elem);
+            new IntersectionCastTester(trees).scan(p, null);
+        }
+        return true;
+    }
+
+    class IntersectionCastTester extends TreePathScanner<Void, Void> {
+        Trees trees;
+
+        public IntersectionCastTester(Trees trees) {
+            super();
+            this.trees = trees;
+        }
+
+        @Override
+        public Void visitVariable(VariableTree node, Void p) {
+            
+            TreePath varPath = new TreePath(getCurrentPath(), node);
+            Element v = trees.getElement(varPath);
+            
+            IntersectionTypeInfo it = v.getAnnotation(IntersectionTypeInfo.class);
+            assertTrue(it != null, "IntersectionType annotation must be present");
+
+            ExpressionTree varInit = node.getInitializer();
+            assertTrue(varInit != null && varInit.getKind() == Tree.Kind.TYPE_CAST,
+                    "variable must have be initialized to an expression containing an intersection type cast");
+            
+            TypeMirror t = ((JCExpression)((TypeCastTree)varInit).getType()).type;
+            
+            validateIntersectionTypeInfo(t, it);
+            
+            for (Element e2 : types.asElement(t).getEnclosedElements()) {
+                Member m = e2.getAnnotation(Member.class);
+                if (m != null) {
+                    assertTrue(e2.getKind() == m.value(), "Expected " + m.value() + " - found " + e2.getKind());
+                }
+            }
+            assertTrue(assertionCount == 10, "Expected 10 assertions - found " + assertionCount);
+            return super.visitVariable(node, p);
+        }
+    }
+
+    private void validateIntersectionTypeInfo(TypeMirror expectedIntersectionType, IntersectionTypeInfo it) {
+        
+        assertTrue(expectedIntersectionType.getKind() == TypeKind.INTERSECTION, "INTERSECTION kind expected");
+
+        try {
+            new SimpleTypeVisitor6<Void, Void>(){}.visit(expectedIntersectionType);
+            throw new RuntimeException("Expected UnknownTypeException not thrown.");
+        } catch (UnknownTypeException ute) {
+            ; // Expected
+        }
+        
+        try {
+            new SimpleTypeVisitor7<Void, Void>(){}.visit(expectedIntersectionType);
+            throw new RuntimeException("Expected UnknownTypeException not thrown.");
+        } catch (UnknownTypeException ute) {
+            ; // Expected
+        }
+
+        IntersectionType intersectionType = new SimpleTypeVisitor<IntersectionType, Void>(){
+            @Override
+            protected IntersectionType defaultAction(TypeMirror e, Void p) {return null;}
+
+            @Override
+            public IntersectionType visitIntersection(IntersectionType t, Void p) {return t;}
+        }.visit(expectedIntersectionType);
+        assertTrue(intersectionType != null, "Must get a non-null intersection type.");
+
+        assertTrue(it.value().length == intersectionType.getBounds().size(), "Cardinalities do not match");
+
+        String[] typeNames = it.value();
+        for(int i = 0; i < typeNames.length; i++) {
+            TypeMirror typeFromAnnotation = nameToType(typeNames[i]);
+            assertTrue(types.isSameType(typeFromAnnotation, intersectionType.getBounds().get(i)),
+                       "Types were not equal.");
+        }
+    }
+
+    private TypeMirror nameToType(String name) {
+        return elements.getTypeElement(name).asType();
+    }
+
+    private static void assertTrue(boolean cond, String msg) {
+        assertionCount++;
+        if (!cond)
+            throw new AssertionError(msg);
+    }
+
+    static int assertionCount = 0;
+}