changeset 1694:f6f1fd261f57

8002099: Add support for intersection types in cast expression Summary: Add parser and type-checking support for intersection types in cast expressions Reviewed-by: jjg
author mcimadamore
date Fri, 30 Nov 2012 15:14:48 +0000
parents 9b26c96f5138
children 98e14fc9ee11
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/Source.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/resources/compiler.properties 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/IntersectionTypeParserTest.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 test/tools/javac/diags/examples/IntersectionTypesInCastNotSupported.java test/tools/javac/diags/examples/SecondaryBoundMustBeMarkerIntf.java test/tools/javac/lambda/Intersection01.java test/tools/javac/lambda/Intersection01.out test/tools/javac/lambda/LambdaParserTest.java test/tools/javac/lambda/intersection/IntersectionTargetTypeTest.java
diffstat 38 files changed, 1873 insertions(+), 229 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	Fri Nov 30 15:14:48 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	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/com/sun/source/tree/Tree.java	Fri Nov 30 15:14:48 2012 +0000
@@ -247,6 +247,11 @@
         UNION_TYPE(UnionTypeTree.class),
 
         /**
+         * Used for instances of {@link IntersectionTypeTree}.
+         */
+        INTERSECTION_TYPE(IntersectionTypeTree.class),
+
+        /**
          * Used for instances of {@link TypeCastTree}.
          */
         TYPE_CAST(TypeCastTree.class),
--- a/src/share/classes/com/sun/source/tree/TreeVisitor.java	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/com/sun/source/tree/TreeVisitor.java	Fri Nov 30 15:14:48 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	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/com/sun/source/util/SimpleTreeVisitor.java	Fri Nov 30 15:14:48 2012 +0000
@@ -240,6 +240,10 @@
         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	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/com/sun/source/util/TreeScanner.java	Fri Nov 30 15:14:48 2012 +0000
@@ -371,6 +371,10 @@
         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);
         return r;
--- a/src/share/classes/com/sun/tools/javac/code/Source.java	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/code/Source.java	Fri Nov 30 15:14:48 2012 +0000
@@ -215,6 +215,9 @@
     public boolean allowRepeatedAnnotations() {
         return compareTo(JDK1_8) >= 0;
     }
+    public boolean allowIntersectionTypesInCast() {
+        return compareTo(JDK1_8) >= 0;
+    }
     public static SourceVersion toSourceVersion(Source source) {
         switch(source) {
         case JDK1_2:
--- a/src/share/classes/com/sun/tools/javac/code/Type.java	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/code/Type.java	Fri Nov 30 15:14:48 2012 +0000
@@ -839,6 +839,49 @@
         }
     }
 
+    // a clone of a ClassType that knows about the bounds of an intersection type.
+    public static class IntersectionClassType extends ClassType implements IntersectionType {
+
+        public boolean allInterfaces;
+
+        public enum IntersectionKind {
+            EXPLICIT,
+            IMPLICT;
+        }
+
+        public IntersectionKind intersectionKind;
+
+        public IntersectionClassType(List<Type> bounds, ClassSymbol csym, boolean allInterfaces) {
+            super(Type.noType, List.<Type>nil(), csym);
+            this.allInterfaces = allInterfaces;
+            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 intersectionKind == IntersectionKind.EXPLICIT ?
+                v.visitIntersection(this, p) :
+                v.visitDeclared(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	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/code/Types.java	Fri Nov 30 15:14:48 2012 +0000
@@ -389,28 +389,6 @@
         }
 
         /**
-         * Scope filter used to skip methods that should be ignored during
-         * function interface conversion (such as methods overridden by
-         * j.l.Object)
-         */
-        class DescriptorFilter implements Filter<Symbol> {
-
-            TypeSymbol origin;
-
-            DescriptorFilter(TypeSymbol origin) {
-                this.origin = origin;
-            }
-
-            @Override
-            public boolean accepts(Symbol sym) {
-                return sym.kind == Kinds.MTH &&
-                        (sym.flags() & (ABSTRACT | DEFAULT)) == ABSTRACT &&
-                        !overridesObjectMethod(origin, sym) &&
-                        (interfaceCandidates(origin.type, (MethodSymbol)sym).head.flags() & DEFAULT) == 0;
-            }
-        };
-
-        /**
          * Compute the function descriptor associated with a given functional interface
          */
         public FunctionDescriptor findDescriptorInternal(TypeSymbol origin, CompoundScope membersCache) throws FunctionDescriptorLookupError {
@@ -577,6 +555,85 @@
     }
     // </editor-fold>
 
+   /**
+    * Scope filter used to skip methods that should be ignored (such as methods
+    * overridden by j.l.Object) during function interface conversion/marker interface checks
+    */
+    class DescriptorFilter implements Filter<Symbol> {
+
+       TypeSymbol origin;
+
+       DescriptorFilter(TypeSymbol origin) {
+           this.origin = origin;
+       }
+
+       @Override
+       public boolean accepts(Symbol sym) {
+           return sym.kind == Kinds.MTH &&
+                   (sym.flags() & (ABSTRACT | DEFAULT)) == ABSTRACT &&
+                   !overridesObjectMethod(origin, sym) &&
+                   (interfaceCandidates(origin.type, (MethodSymbol)sym).head.flags() & DEFAULT) == 0;
+       }
+    };
+
+    // <editor-fold defaultstate="collapsed" desc="isMarker">
+
+    /**
+     * A cache that keeps track of marker interfaces
+     */
+    class MarkerCache {
+
+        private WeakHashMap<TypeSymbol, Entry> _map = new WeakHashMap<TypeSymbol, Entry>();
+
+        class Entry {
+            final boolean isMarkerIntf;
+            final int prevMark;
+
+            public Entry(boolean isMarkerIntf,
+                    int prevMark) {
+                this.isMarkerIntf = isMarkerIntf;
+                this.prevMark = prevMark;
+            }
+
+            boolean matches(int mark) {
+                return  this.prevMark == mark;
+            }
+        }
+
+        boolean get(TypeSymbol origin) throws FunctionDescriptorLookupError {
+            Entry e = _map.get(origin);
+            CompoundScope members = membersClosure(origin.type, false);
+            if (e == null ||
+                    !e.matches(members.getMark())) {
+                boolean isMarkerIntf = isMarkerInterfaceInternal(origin, members);
+                _map.put(origin, new Entry(isMarkerIntf, members.getMark()));
+                return isMarkerIntf;
+            }
+            else {
+                return e.isMarkerIntf;
+            }
+        }
+
+        /**
+         * Is given symbol a marker interface
+         */
+        public boolean isMarkerInterfaceInternal(TypeSymbol origin, CompoundScope membersCache) throws FunctionDescriptorLookupError {
+            return !origin.isInterface() ?
+                    false :
+                    !membersCache.getElements(new DescriptorFilter(origin)).iterator().hasNext();
+        }
+    }
+
+    private MarkerCache markerCache = new MarkerCache();
+
+    /**
+     * Is given type a marker interface?
+     */
+    public boolean isMarkerInterface(Type site) {
+        return markerCache.get(site.tsym);
+    }
+    // </editor-fold>
+
     // <editor-fold defaultstate="collapsed" desc="isSubtype">
     /**
      * Is t an unchecked subtype of s?
@@ -1955,45 +2012,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, allInterfaces);
+        bc.erasure_field = (bounds.head.tag == TYPEVAR) ?
+                syms.objectType : // error condition, recover
+                erasure(firstExplicitBound);
+        bc.members_field = new Scope(bc);
+        return bc.type;
     }
 
     /**
@@ -2183,12 +2223,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());
     }
 
     /**
@@ -2200,10 +2236,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>
@@ -2213,7 +2249,7 @@
      * Return list of bounds of the given type variable.
      */
     public List<Type> getBounds(TypeVar t) {
-                if (t.bound.hasTag(NONE))
+        if (t.bound.hasTag(NONE))
             return List.nil();
         else if (t.bound.isErroneous() || !t.bound.isCompound())
             return List.of(t.bound);
@@ -3312,8 +3348,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	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java	Fri Nov 30 15:14:48 2012 +0000
@@ -716,21 +716,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);
-                }
-            }
         }
     }
 
@@ -892,7 +879,12 @@
             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 ???
@@ -903,12 +895,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 (isDefaultMethod && types.overridesObjectMethod(m.enclClass(), m)) {
                 log.error(tree, "default.overrides.object.member", m.name, Kinds.kindName(m.location()), m.location());
             }
@@ -2196,7 +2182,7 @@
             Type target;
             Type lambdaType;
             if (pt() != Type.recoveryType) {
-                target = infer.instantiateFunctionalInterface(that, pt(), explicitParamTypes, resultInfo.checkContext);
+                target = infer.instantiateFunctionalInterface(that, checkIntersectionTarget(that, resultInfo), explicitParamTypes, resultInfo.checkContext);
                 lambdaType = types.findDescriptorType(target);
                 chk.checkFunctionalInterface(that, target);
             } else {
@@ -2294,6 +2280,26 @@
             }
         }
     }
+
+    private Type checkIntersectionTarget(DiagnosticPosition pos, ResultInfo resultInfo) {
+        Type pt = resultInfo.pt;
+        if (pt != Type.recoveryType && pt.isCompound()) {
+            IntersectionClassType ict = (IntersectionClassType)pt;
+            List<Type> bounds = ict.allInterfaces ?
+                    ict.getComponents().tail :
+                    ict.getComponents();
+            types.findDescriptorType(bounds.head); //propagate exception outwards!
+            for (Type bound : bounds.tail) {
+                if (!types.isMarkerInterface(bound)) {
+                    resultInfo.checkContext.report(pos, diags.fragment("secondary.bound.must.be.marker.intf", bound));
+                }
+            }
+            //for now (translation doesn't support intersection types)
+            return bounds.head;
+        } else {
+            return pt;
+        }
+    }
     //where
         private Type fallbackDescriptorType(JCExpression tree) {
             switch (tree.getTag()) {
@@ -2466,7 +2472,7 @@
             Type target;
             Type desc;
             if (pt() != Type.recoveryType) {
-                target = infer.instantiateFunctionalInterface(that, pt(), null, resultInfo.checkContext);
+                target = infer.instantiateFunctionalInterface(that, checkIntersectionTarget(that, resultInfo), null, resultInfo.checkContext);
                 desc = types.findDescriptorType(target);
                 chk.checkFunctionalInterface(that, target);
             } else {
@@ -3575,63 +3581,79 @@
         tree.type = result = t;
     }
 
-    public void visitTypeParameter(JCTypeParameter tree) {
-        TypeVar a = (TypeVar)tree.type;
+    public void visitTypeIntersection(JCTypeIntersection tree) {
+        attribTypes(tree.bounds, env);
+        tree.type = result = checkIntersection(tree, tree.bounds);
+    }
+
+     public void visitTypeParameter(JCTypeParameter tree) {
+        TypeVar typeVar = (TypeVar)tree.type;
+        if (!typeVar.bound.isErroneous()) {
+            //fixup type-parameter bound computed in 'attribTypeVariables'
+            typeVar.bound = checkIntersection(tree, tree.bounds);
+        }
+    }
+
+    Type checkIntersection(JCTree tree, 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.hasTag(TYPEVAR)) {
+            else if (bounds.head.type.hasTag(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.hasTag(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.hasTag(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));
+            if (tree.hasTag(TYPEINTERSECTION)) {
+                ((IntersectionClassType)owntype).intersectionKind =
+                        IntersectionClassType.IntersectionKind.EXPLICIT;
+            }
             // ... 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(tree).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;
@@ -3640,10 +3662,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)
@@ -3797,7 +3820,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	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/comp/TransTypes.java	Fri Nov 30 15:14:48 2012 +0000
@@ -551,6 +551,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;
         }
@@ -786,6 +787,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;
     }
@@ -803,6 +805,12 @@
         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	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Fri Nov 30 15:14:48 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	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/model/JavacTypes.java	Fri Nov 30 15:14:48 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	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Fri Nov 30 15:14:48 2012 +0000
@@ -124,6 +124,9 @@
         this.allowLambda = source.allowLambda();
         this.allowMethodReferences = source.allowMethodReferences();
         this.allowDefaultMethods = source.allowDefaultMethods();
+        this.allowIntersectionTypesInCast =
+                source.allowIntersectionTypesInCast() &&
+                fac.options.isSet("allowIntersectionTypes");
         this.keepDocComments = keepDocComments;
         docComments = newDocCommentTable(keepDocComments, fac);
         this.keepLineMap = keepLineMap;
@@ -197,6 +200,10 @@
      */
     boolean allowDefaultMethods;
 
+    /** Switch: should we allow intersection types in cast?
+     */
+    boolean allowIntersectionTypesInCast;
+
     /** Switch: should we keep docComments?
      */
     boolean keepDocComments;
@@ -239,22 +246,38 @@
     }
 
     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 S.token(1).kind == tk1 &&
-                S.token(2).kind == tk2;
+        return peekToken(0, tk1, 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 S.token(1).kind == tk1 &&
-                S.token(2).kind == tk2 &&
-                S.token(3).kind == tk3;
+        return peekToken(0, tk1, tk2, 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) {
-        for (int lookahead = 0 ; lookahead < kinds.length ; lookahead++) {
+        return peekToken(0, kinds);
+    }
+
+    protected boolean peekToken(int lookahead, TokenKind... kinds) {
+        for (; lookahead < kinds.length ; lookahead++) {
             if (S.token(lookahead + 1).kind != kinds[lookahead]) {
                 return false;
             }
@@ -966,102 +989,40 @@
             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)
+                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) {
+                           checkIntersectionTypesInCast();
+                           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;
+                    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) {
@@ -1346,6 +1307,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>();
         params.append(firstParam);
@@ -3386,6 +3479,12 @@
             allowDefaultMethods = true;
         }
     }
+    void checkIntersectionTypesInCast() {
+        if (!allowIntersectionTypesInCast) {
+            log.error(token.pos, "intersection.types.in.cast.not.supported.in.source", source.name);
+            allowIntersectionTypesInCast = true;
+        }
+    }
 
     /*
      * a functional source tree and end position mappings
--- a/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Fri Nov 30 15:14:48 2012 +0000
@@ -207,6 +207,10 @@
 compiler.misc.no.suitable.functional.intf.inst=\
     cannot infer functional interface descriptor for {0}
 
+# 0: type
+compiler.misc.secondary.bound.must.be.marker.intf=\
+    secondary bound {0} must be a marker interface
+
 # 0: symbol kind, 1: message segment
 compiler.err.invalid.mref=\
     invalid {0} reference; {1}
@@ -2203,6 +2207,11 @@
     default methods are not supported in -source {0}\n\
     (use -source 8 or higher to enable default methods)
 
+# 0: string
+compiler.err.intersection.types.in.cast.not.supported.in.source=\
+    intersection types in cast are not supported in -source {0}\n\
+    (use -source 8 or higher to enable default methods)
+
 ########################################
 # Diagnostics for verbose resolution
 # used by Resolve (debug only)
--- a/src/share/classes/com/sun/tools/javac/tree/JCTree.java	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/tree/JCTree.java	Fri Nov 30 15:14:48 2012 +0000
@@ -254,6 +254,10 @@
          */
         TYPEUNION,
 
+        /** Intersection types, of type TypeIntersection
+         */
+        TYPEINTERSECTION,
+
         /** Formal type parameters, of type TypeParameter.
          */
         TYPEPARAMETER,
@@ -2062,6 +2066,34 @@
     }
 
     /**
+     * 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.
      */
     public static class JCTypeParameter extends JCTree implements TypeParameterTree {
@@ -2383,6 +2415,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	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/tree/Pretty.java	Fri Nov 30 15:14:48 2012 +0000
@@ -1249,6 +1249,14 @@
         }
     }
 
+    public void visitTypeIntersection(JCTypeIntersection tree) {
+        try {
+            printExprs(tree.bounds, " & ");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
     public void visitTypeParameter(JCTypeParameter tree) {
         try {
             print(tree.name);
--- a/src/share/classes/com/sun/tools/javac/tree/TreeCopier.java	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/tree/TreeCopier.java	Fri Nov 30 15:14:48 2012 +0000
@@ -358,6 +358,12 @@
         return M.at(t.pos).TypeUnion(components);
     }
 
+    public JCTree visitIntersectionType(IntersectionTypeTree node, P p) {
+        JCTypeIntersection t = (JCTypeIntersection) node;
+        List<JCExpression> bounds = copy(t.bounds, p);
+        return M.at(t.pos).TypeIntersection(bounds);
+    }
+
     public JCTree visitArrayType(ArrayTypeTree node, P p) {
         JCArrayTypeTree t = (JCArrayTypeTree) node;
         JCExpression elemtype = copy(t.elemtype, p);
--- a/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java	Fri Nov 30 15:14:48 2012 +0000
@@ -456,6 +456,12 @@
         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);
         tree.pos = pos;
--- a/src/share/classes/com/sun/tools/javac/tree/TreeScanner.java	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/tree/TreeScanner.java	Fri Nov 30 15:14:48 2012 +0000
@@ -286,6 +286,10 @@
         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	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/tree/TreeTranslator.java	Fri Nov 30 15:14:48 2012 +0000
@@ -379,6 +379,11 @@
         result = tree;
     }
 
+    public void visitTypeIntersection(JCTypeIntersection tree) {
+        tree.bounds = translate(tree.bounds);
+        result = tree;
+    }
+
     public void visitTypeParameter(JCTypeParameter tree) {
         tree.bounds = translate(tree.bounds);
         result = tree;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/javax/lang/model/type/IntersectionType.java	Fri Nov 30 15:14:48 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 an intersection 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.8
+ */
+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	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/javax/lang/model/type/TypeKind.java	Fri Nov 30 15:14:48 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	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/javax/lang/model/type/TypeVisitor.java	Fri Nov 30 15:14:48 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	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/javax/lang/model/util/AbstractTypeVisitor6.java	Fri Nov 30 15:14:48 2012 +0000
@@ -111,6 +111,20 @@
     }
 
     /**
+     * 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}
      *
      * <p> The default implementation of this method in {@code
--- a/src/share/classes/javax/lang/model/util/AbstractTypeVisitor8.java	Fri Nov 30 15:14:36 2012 +0000
+++ b/src/share/classes/javax/lang/model/util/AbstractTypeVisitor8.java	Fri Nov 30 15:14:48 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	Fri Nov 30 15:14:48 2012 +0000
@@ -0,0 +1,330 @@
+/*
+ * 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 8002099
+ * @summary Add support for intersection types in cast expression
+ */
+
+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,
+                Arrays.asList("-XDallowIntersectionTypes"), 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/IntersectionTypeParserTest.java	Fri Nov 30 15:14:48 2012 +0000
@@ -0,0 +1,191 @@
+/*
+ * 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 8002099
+ * @summary Add support for intersection types in cast expression
+ */
+
+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.StandardJavaFileManager;
+import javax.tools.ToolProvider;
+
+public class IntersectionTypeParserTest {
+
+    static int checkCount = 0;
+
+    enum TypeKind {
+        SIMPLE("A"),
+        GENERIC("A<X>"),
+        WILDCARD("A<? super X, ? extends Y>");
+
+        String typeStr;
+
+        TypeKind(String typeStr) {
+            this.typeStr = typeStr;
+        }
+    }
+
+    enum ArrayKind {
+        NONE(""),
+        SINGLE("[]"),
+        DOUBLE("[][]");
+
+        String arrStr;
+
+        ArrayKind(String arrStr) {
+            this.arrStr = arrStr;
+        }
+    }
+
+    static class Type {
+        TypeKind tk;
+        ArrayKind ak;
+
+        Type(TypeKind tk, ArrayKind ak) {
+            this.tk = tk;
+            this.ak = ak;
+        }
+
+        String asString() {
+            return tk.typeStr + ak.arrStr;
+        }
+    }
+
+    enum CastKind {
+        ONE("(#T0)", 1),
+        TWO("(#T0 & T1)", 2),
+        THREE("(#T0 & #T1 & #T2)", 3);
+
+        String castTemplate;
+        int nBounds;
+
+        CastKind(String castTemplate, int nBounds) {
+            this.castTemplate = castTemplate;
+            this.nBounds = nBounds;
+        }
+
+        String asString(Type... types) {
+            String res = castTemplate;
+            for (int i = 0; i < nBounds ; i++) {
+                res = res.replaceAll(String.format("#T%d", i), types[i].asString());
+            }
+            return res;
+        }
+    }
+
+    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 (CastKind ck : CastKind.values()) {
+            for (TypeKind t1 : TypeKind.values()) {
+                for (ArrayKind ak1 : ArrayKind.values()) {
+                    Type typ1 = new Type(t1, ak1);
+                    if (ck.nBounds == 1) {
+                        new IntersectionTypeParserTest(ck, typ1).run(comp, fm);
+                        continue;
+                    }
+                    for (TypeKind t2 : TypeKind.values()) {
+                        for (ArrayKind ak2 : ArrayKind.values()) {
+                            Type typ2 = new Type(t2, ak2);
+                            if (ck.nBounds == 2) {
+                                new IntersectionTypeParserTest(ck, typ1, typ2).run(comp, fm);
+                                continue;
+                            }
+                            for (TypeKind t3 : TypeKind.values()) {
+                                for (ArrayKind ak3 : ArrayKind.values()) {
+                                    Type typ3 = new Type(t3, ak3);
+                                    new IntersectionTypeParserTest(ck, typ1, typ2, typ3).run(comp, fm);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        System.out.println("Total check executed: " + checkCount);
+    }
+
+    CastKind ck;
+    Type[] types;
+    JavaSource source;
+    DiagnosticChecker diagChecker;
+
+    IntersectionTypeParserTest(CastKind ck, Type... types) {
+        this.ck = ck;
+        this.types = types;
+        this.source = new JavaSource();
+        this.diagChecker = new DiagnosticChecker();
+    }
+
+    class JavaSource extends SimpleJavaFileObject {
+
+        String bodyTemplate = "class Test {\n" +
+                              "   void test() {\n" +
+                              "      Object o = #Cnull;\n" +
+                              "   } }";
+
+        String source = "";
+
+        public JavaSource() {
+            super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
+            source += bodyTemplate.replaceAll("#C", ck.asString(types));
+        }
+
+        @Override
+        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+            return source;
+        }
+    }
+
+    void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception {
+        checkCount++;
+        JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker,
+                Arrays.asList("-XDallowIntersectionTypes"), null, Arrays.asList(source));
+        ct.parse();
+        if (diagChecker.errorFound) {
+            throw new Error("Unexpected parser error for source:\n" +
+                source.getCharContent(true));
+        }
+    }
+
+    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	Fri Nov 30 15:14:48 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	Fri Nov 30 15:14:48 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	Fri Nov 30 15:14:48 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	Fri Nov 30 15:14:48 2012 +0000
@@ -0,0 +1,52 @@
+/*
+ * 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 8002099
+ * @summary Add support for intersection types in cast expression
+ * @library ../../../lib
+ * @build JavacTestingAbstractProcessor ModelChecker
+ * @compile -XDallowIntersectionTypes -processor ModelChecker Model01.java
+ */
+
+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	Fri Nov 30 15:14:48 2012 +0000
@@ -0,0 +1,153 @@
+/*
+ * 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 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.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()) {
+                assertTrue(false, "an intersection type has no declared members");
+            }
+
+            for (Element e2 : elements.getAllMembers((TypeElement)types.asElement(t))) {
+                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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/diags/examples/IntersectionTypesInCastNotSupported.java	Fri Nov 30 15:14:48 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.
+ */
+
+// key: compiler.err.intersection.types.in.cast.not.supported.in.source
+// options: -source 7 -Xlint:-options
+
+interface IntersectionTypesInCastNotSupported {
+    Object o = (A & B)null;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/diags/examples/SecondaryBoundMustBeMarkerIntf.java	Fri Nov 30 15:14:48 2012 +0000
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+// key: compiler.err.prob.found.req
+// key: compiler.misc.secondary.bound.must.be.marker.intf
+// options: -XDallowIntersectionTypes
+
+class SecondaryBoundMustBeMarkerInterface {
+    Runnable r = (Runnable & Comparable<?>)()->{};
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/lambda/Intersection01.java	Fri Nov 30 15:14:48 2012 +0000
@@ -0,0 +1,42 @@
+/*
+ * 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 8002099
+ * @summary Add support for intersection types in cast expression
+ * @compile/fail/ref=Intersection01.out -XDallowIntersectionTypes -XDrawDiagnostics Intersection01.java
+ */
+class Intersection01 {
+
+    interface SAM {
+        void m();
+    }
+
+    Object o1 = (java.io.Serializable & SAM)()->{};
+    Object o2 = (SAM & java.io.Serializable)()->{};
+    Object o3 = (java.io.Serializable & SAM)Intersection01::m;
+    Object o4 = (SAM & java.io.Serializable)Intersection01::m;
+
+    static void m() { }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/lambda/Intersection01.out	Fri Nov 30 15:14:48 2012 +0000
@@ -0,0 +1,3 @@
+Intersection01.java:36:45: compiler.err.prob.found.req: (compiler.misc.not.a.functional.intf.1: (compiler.misc.no.abstracts: kindname.interface, java.io.Serializable))
+Intersection01.java:38:45: compiler.err.prob.found.req: (compiler.misc.not.a.functional.intf.1: (compiler.misc.no.abstracts: kindname.interface, java.io.Serializable))
+2 errors
--- a/test/tools/javac/lambda/LambdaParserTest.java	Fri Nov 30 15:14:36 2012 +0000
+++ b/test/tools/javac/lambda/LambdaParserTest.java	Fri Nov 30 15:14:48 2012 +0000
@@ -90,9 +90,14 @@
     enum LambdaParameterKind {
         IMPLICIT(""),
         EXPLIICT_SIMPLE("A"),
+        EXPLIICT_SIMPLE_ARR1("A[]"),
+        EXPLIICT_SIMPLE_ARR2("A[][]"),
         EXPLICIT_VARARGS("A..."),
         EXPLICIT_GENERIC1("A<X>"),
-        EXPLICIT_GENERIC3("A<? extends X, ? super Y>");
+        EXPLICIT_GENERIC2("A<? extends X, ? super Y>"),
+        EXPLICIT_GENERIC2_VARARGS("A<? extends X, ? super Y>..."),
+        EXPLICIT_GENERIC2_ARR1("A<? extends X, ? super Y>[]"),
+        EXPLICIT_GENERIC2_ARR2("A<? extends X, ? super Y>[][]");
 
         String parameterType;
 
@@ -103,6 +108,11 @@
         boolean explicit() {
             return this != IMPLICIT;
         }
+
+        boolean isVarargs() {
+            return this == EXPLICIT_VARARGS ||
+                    this == EXPLICIT_GENERIC2_VARARGS;
+        }
     }
 
     enum ModifierKind {
@@ -253,7 +263,7 @@
 
         if (lk.arity() == 2 &&
                 (pk1.explicit() != pk2.explicit() ||
-                pk1 == LambdaParameterKind.EXPLICIT_VARARGS)) {
+                pk1.isVarargs())) {
             errorExpected = true;
         }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/lambda/intersection/IntersectionTargetTypeTest.java	Fri Nov 30 15:14:48 2012 +0000
@@ -0,0 +1,294 @@
+/*
+ * 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 8002099
+ * @summary Add support for intersection types in cast expression
+ */
+
+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 IntersectionTargetTypeTest {
+
+    static int checkCount = 0;
+
+    enum BoundKind {
+        INTF,
+        CLASS,
+        SAM,
+        ZAM;
+    }
+
+    enum MethodKind {
+        NONE,
+        ABSTRACT,
+        DEFAULT;
+    }
+
+    enum TypeKind {
+        A("interface A { }\n", "A", BoundKind.ZAM),
+        B("interface B { default void m() { } }\n", "B", BoundKind.ZAM),
+        C("interface C { void m(); }\n", "C", BoundKind.SAM),
+        D("interface D extends B { }\n", "D", BoundKind.ZAM),
+        E("interface E extends C { }\n", "E", BoundKind.SAM),
+        F("interface F extends C { void g(); }\n", "F", BoundKind.INTF),
+        G("interface G extends B { void g(); }\n", "G", BoundKind.SAM),
+        H("interface H extends A { void g(); }\n", "H", BoundKind.SAM),
+        OBJECT("", "Object", BoundKind.CLASS),
+        STRING("", "String", BoundKind.CLASS);
+
+        String declStr;
+        String typeStr;
+        BoundKind boundKind;
+
+        private TypeKind(String declStr, String typeStr, BoundKind boundKind) {
+            this.declStr = declStr;
+            this.typeStr = typeStr;
+            this.boundKind = boundKind;
+        }
+
+        boolean compatibleSupertype(TypeKind tk) {
+            if (tk == this) return true;
+            switch (tk) {
+                case B:
+                    return this != C && this != E && this != F;
+                case C:
+                    return this != B && this != C && this != D && this != G;
+                case D: return compatibleSupertype(B);
+                case E:
+                case F: return compatibleSupertype(C);
+                case G: return compatibleSupertype(B);
+                case H: return compatibleSupertype(A);
+                default:
+                    return true;
+            }
+        }
+    }
+
+    enum CastKind {
+        ONE_ARY("(#B0)", 1),
+        TWO_ARY("(#B0 & #B1)", 2),
+        THREE_ARY("(#B0 & #B1 & #B2)", 3);
+
+        String castTemplate;
+        int nbounds;
+
+        CastKind(String castTemplate, int nbounds) {
+            this.castTemplate = castTemplate;
+            this.nbounds = nbounds;
+        }
+    }
+
+    enum ExpressionKind {
+        LAMBDA("()->{}", true),
+        MREF("this::m", true),
+        //COND_LAMBDA("(true ? ()->{} : ()->{})", true), re-enable if spec allows this
+        //COND_MREF("(true ? this::m : this::m)", true),
+        STANDALONE("null", false);
+
+        String exprString;
+        boolean isFunctional;
+
+        private ExpressionKind(String exprString, boolean isFunctional) {
+            this.exprString = exprString;
+            this.isFunctional = isFunctional;
+        }
+    }
+
+    static class CastInfo {
+        CastKind kind;
+        TypeKind[] types;
+
+        CastInfo(CastKind kind, TypeKind... types) {
+            this.kind = kind;
+            this.types = types;
+        }
+
+        String getCast() {
+            String temp = kind.castTemplate;
+            for (int i = 0; i < kind.nbounds ; i++) {
+                temp = temp.replace(String.format("#B%d", i), types[i].typeStr);
+            }
+            return temp;
+        }
+
+        boolean wellFormed() {
+            //check for duplicate types
+            for (int i = 0 ; i < types.length ; i++) {
+                for (int j = 0 ; j < types.length ; j++) {
+                    if (i != j && types[i] == types[j]) {
+                        return false;
+                    }
+                }
+            }
+            //check that classes only appear as first bound
+            boolean classOk = true;
+            for (int i = 0 ; i < types.length ; i++) {
+                if (types[i].boundKind == BoundKind.CLASS &&
+                        !classOk) {
+                    return false;
+                }
+                classOk = false;
+            }
+            //check that supertypes are mutually compatible
+            for (int i = 0 ; i < types.length ; i++) {
+                for (int j = 0 ; j < types.length ; j++) {
+                    if (!types[i].compatibleSupertype(types[j]) && i != j) {
+                        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 (CastInfo cInfo : allCastInfo()) {
+            for (ExpressionKind ek : ExpressionKind.values()) {
+                new IntersectionTargetTypeTest(cInfo, ek).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 (TypeKind b1 : TypeKind.values()) {
+                if (kind.nbounds == 1) {
+                    buf.append(new CastInfo(kind, b1));
+                    continue;
+                } else {
+                    for (TypeKind b2 : TypeKind.values()) {
+                        if (kind.nbounds == 2) {
+                            buf.append(new CastInfo(kind, b1, b2));
+                            continue;
+                        } else {
+                            for (TypeKind b3 : TypeKind.values()) {
+                                buf.append(new CastInfo(kind, b1, b2, b3));
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return buf.toList();
+    }
+
+    CastInfo cInfo;
+    ExpressionKind ek;
+    JavaSource source;
+    DiagnosticChecker diagChecker;
+
+    IntersectionTargetTypeTest(CastInfo cInfo, ExpressionKind ek) {
+        this.cInfo = cInfo;
+        this.ek = ek;
+        this.source = new JavaSource();
+        this.diagChecker = new DiagnosticChecker();
+    }
+
+    class JavaSource extends SimpleJavaFileObject {
+
+        String bodyTemplate = "class Test {\n" +
+                              "   void m() { }\n" +
+                              "   void test() {\n" +
+                              "      Object o = #C#E;\n" +
+                              "   } }";
+
+        String source = "";
+
+        public JavaSource() {
+            super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
+            for (TypeKind tk : TypeKind.values()) {
+                source += tk.declStr;
+            }
+            source += bodyTemplate.replaceAll("#C", cInfo.getCast()).replaceAll("#E", ek.exprString);
+        }
+
+        @Override
+        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+            return source;
+        }
+    }
+
+    void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception {
+        JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker,
+                Arrays.asList("-XDallowIntersectionTypes"), 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 = !cInfo.wellFormed();
+
+        if (ek.isFunctional) {
+            //first bound must be a SAM
+            errorExpected |= cInfo.types[0].boundKind != BoundKind.SAM;
+            if (cInfo.types.length > 1) {
+                //additional bounds must be ZAMs
+                for (int i = 1; i < cInfo.types.length; i++) {
+                    errorExpected |= cInfo.types[i].boundKind != BoundKind.ZAM;
+                }
+            }
+        }
+
+        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;
+            }
+        }
+    }
+}