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