# HG changeset patch # User mcimadamore # Date 1354288488 0 # Node ID f6f1fd261f57269e236d145e3f772f30ad0131d7 # Parent 9b26c96f5138f4c5771eec174499485d16cafc23 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 diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/com/sun/source/tree/IntersectionTypeTree.java --- /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 getBounds(); +} diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/com/sun/source/tree/Tree.java --- 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), diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/com/sun/source/tree/TreeVisitor.java --- 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); diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/com/sun/source/util/SimpleTreeVisitor.java --- 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); } diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/com/sun/source/util/TreeScanner.java --- 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; diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/com/sun/tools/javac/code/Source.java --- 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: diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/com/sun/tools/javac/code/Type.java --- 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 bounds, ClassSymbol csym, boolean allInterfaces) { + super(Type.noType, List.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 getBounds() { + return Collections.unmodifiableList(getComponents()); + } + + public List getComponents() { + return interfaces_field.prepend(supertype_field); + } + + @Override + public TypeKind getKind() { + return TypeKind.INTERSECTION; + } + + @Override + public R accept(TypeVisitor 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 { diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/com/sun/tools/javac/code/Types.java --- 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 { - - 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 @@ } // + /** + * 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 { + + 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; + } + }; + + // + + /** + * A cache that keeps track of marker interfaces + */ + class MarkerCache { + + private WeakHashMap _map = new WeakHashMap(); + + 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); + } + // + // /** * 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 bounds, - Type supertype) { + public Type makeCompoundType(List bounds) { + return makeCompoundType(bounds, bounds.head.tsym.isInterface()); + } + public Type makeCompoundType(List 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 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 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 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 bounds) { - Type supertype = (bounds.head.tsym.flags() & INTERFACE) != 0 ? - syms.objectType : null; - setBounds(t, bounds, supertype); + public void setBounds(TypeVar t, List bounds, boolean allInterfaces) { + t.bound = bounds.tail.isEmpty() ? + bounds.head : + makeCompoundType(bounds, allInterfaces); t.rank_field = -1; } // @@ -2213,7 +2249,7 @@ * Return list of bounds of the given type variable. */ public List 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); } } } diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/com/sun/tools/javac/comp/Attr.java --- 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 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 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 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 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 bounds) { Set boundSet = new HashSet(); - if (a.bound.isErroneous()) - return; - List 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 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.nil(), + names.empty, List.nil(), extending, implementing, List.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 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); diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/com/sun/tools/javac/comp/TransTypes.java --- 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 *************************************************************************/ diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/com/sun/tools/javac/jvm/ClassReader.java --- 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 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; } diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/com/sun/tools/javac/model/JavacTypes.java --- 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); diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/com/sun/tools/javac/parser/JavacParser.java --- 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 args = new ListBuffer(); - 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 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 '<' it could be an unbound + * method reference or a binary expression. To disambiguate, look for a + * matching '>' 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 params = new ListBuffer(); 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 diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/com/sun/tools/javac/resources/compiler.properties --- 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) diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/com/sun/tools/javac/tree/JCTree.java --- 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 bounds; + + protected JCTypeIntersection(List bounds) { + this.bounds = bounds; + } + @Override + public void accept(Visitor v) { v.visitTypeIntersection(this); } + + public Kind getKind() { return Kind.INTERSECTION_TYPE; } + + public List getBounds() { + return bounds; + } + @Override + public R accept(TreeVisitor 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); } diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/com/sun/tools/javac/tree/Pretty.java --- 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); diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/com/sun/tools/javac/tree/TreeCopier.java --- 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 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); diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/com/sun/tools/javac/tree/TreeMaker.java --- 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 components) { + JCTypeIntersection tree = new JCTypeIntersection(components); + tree.pos = pos; + return tree; + } + public JCTypeParameter TypeParameter(Name name, List bounds) { JCTypeParameter tree = new JCTypeParameter(name, bounds); tree.pos = pos; diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/com/sun/tools/javac/tree/TreeScanner.java --- 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); } diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/com/sun/tools/javac/tree/TreeTranslator.java --- 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; diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/javax/lang/model/type/IntersectionType.java --- /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 getBounds(); +} diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/javax/lang/model/type/TypeKind.java --- 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 diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/javax/lang/model/type/TypeVisitor.java --- 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); } diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/javax/lang/model/util/AbstractTypeVisitor6.java --- 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} * *

The default implementation of this method in {@code diff -r 9b26c96f5138 -r f6f1fd261f57 src/share/classes/javax/lang/model/util/AbstractTypeVisitor8.java --- 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); } diff -r 9b26c96f5138 -r f6f1fd261f57 test/tools/javac/cast/intersection/IntersectionTypeCastTest.java --- /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 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 allCastInfo() { + ListBuffer 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 { + + boolean errorFound; + + public void report(Diagnostic diagnostic) { + if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { + errorFound = true; + } + } + } +} diff -r 9b26c96f5138 -r f6f1fd261f57 test/tools/javac/cast/intersection/IntersectionTypeParserTest.java --- /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"), + WILDCARD("A"); + + 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 { + + boolean errorFound; + + public void report(Diagnostic diagnostic) { + if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { + errorFound = true; + } + } + } +} diff -r 9b26c96f5138 -r f6f1fd261f57 test/tools/javac/cast/intersection/model/Check.java --- /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 {} diff -r 9b26c96f5138 -r f6f1fd261f57 test/tools/javac/cast/intersection/model/IntersectionTypeInfo.java --- /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(); +} diff -r 9b26c96f5138 -r f6f1fd261f57 test/tools/javac/cast/intersection/model/Member.java --- /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(); +} diff -r 9b26c96f5138 -r f6f1fd261f57 test/tools/javac/cast/intersection/model/Model01.java --- /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; + } +} diff -r 9b26c96f5138 -r f6f1fd261f57 test/tools/javac/cast/intersection/model/ModelChecker.java --- /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 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 { + 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(){}.visit(expectedIntersectionType); + throw new RuntimeException("Expected UnknownTypeException not thrown."); + } catch (UnknownTypeException ute) { + ; // Expected + } + + try { + new SimpleTypeVisitor7(){}.visit(expectedIntersectionType); + throw new RuntimeException("Expected UnknownTypeException not thrown."); + } catch (UnknownTypeException ute) { + ; // Expected + } + + IntersectionType intersectionType = new SimpleTypeVisitor(){ + @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; +} diff -r 9b26c96f5138 -r f6f1fd261f57 test/tools/javac/diags/examples/IntersectionTypesInCastNotSupported.java --- /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; +} diff -r 9b26c96f5138 -r f6f1fd261f57 test/tools/javac/diags/examples/SecondaryBoundMustBeMarkerIntf.java --- /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)()->{}; +} diff -r 9b26c96f5138 -r f6f1fd261f57 test/tools/javac/lambda/Intersection01.java --- /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() { } +} diff -r 9b26c96f5138 -r f6f1fd261f57 test/tools/javac/lambda/Intersection01.out --- /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 diff -r 9b26c96f5138 -r f6f1fd261f57 test/tools/javac/lambda/LambdaParserTest.java --- 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"), - EXPLICIT_GENERIC3("A"); + EXPLICIT_GENERIC2("A"), + EXPLICIT_GENERIC2_VARARGS("A..."), + EXPLICIT_GENERIC2_ARR1("A[]"), + EXPLICIT_GENERIC2_ARR2("A[][]"); 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; } diff -r 9b26c96f5138 -r f6f1fd261f57 test/tools/javac/lambda/intersection/IntersectionTargetTypeTest.java --- /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 allCastInfo() { + ListBuffer 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 { + + boolean errorFound; + + public void report(Diagnostic diagnostic) { + if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { + errorFound = true; + } + } + } +}