8002099: Add support for intersection types in cast expression

Add parser and type-checking support for intersection types in cast expressions

Reviewed-by: jjg
This commit is contained in:
Maurizio Cimadamore 2012-11-30 15:14:48 +00:00
parent c76c08e82a
commit d7884e5ae2
38 changed files with 1873 additions and 229 deletions

View File

@ -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();
}

View File

@ -246,6 +246,11 @@ public interface Tree {
*/
UNION_TYPE(UnionTypeTree.class),
/**
* Used for instances of {@link IntersectionTypeTree}.
*/
INTERSECTION_TYPE(IntersectionTypeTree.class),
/**
* Used for instances of {@link TypeCastTree}.
*/

View File

@ -98,6 +98,7 @@ public interface TreeVisitor<R,P> {
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);

View File

@ -240,6 +240,10 @@ public class SimpleTreeVisitor <R,P> implements TreeVisitor<R,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);
}

View File

@ -371,6 +371,10 @@ public class TreeScanner<R,P> implements TreeVisitor<R,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);
return r;

View File

@ -215,6 +215,9 @@ public enum Source {
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:

View File

@ -839,6 +839,49 @@ public class Type implements PrimitiveType {
}
}
// a clone of a ClassType that knows about the bounds of an intersection type.
public static class IntersectionClassType extends ClassType implements IntersectionType {
public boolean allInterfaces;
public enum IntersectionKind {
EXPLICIT,
IMPLICT;
}
public IntersectionKind intersectionKind;
public IntersectionClassType(List<Type> bounds, ClassSymbol csym, boolean allInterfaces) {
super(Type.noType, List.<Type>nil(), csym);
this.allInterfaces = allInterfaces;
Assert.check((csym.flags() & COMPOUND) != 0);
supertype_field = bounds.head;
interfaces_field = bounds.tail;
Assert.check(supertype_field.tsym.completer != null ||
!supertype_field.isInterface(), supertype_field);
}
public java.util.List<? extends TypeMirror> getBounds() {
return Collections.unmodifiableList(getComponents());
}
public List<Type> getComponents() {
return interfaces_field.prepend(supertype_field);
}
@Override
public TypeKind getKind() {
return TypeKind.INTERSECTION;
}
@Override
public <R, P> R accept(TypeVisitor<R, P> v, P p) {
return intersectionKind == IntersectionKind.EXPLICIT ?
v.visitIntersection(this, p) :
v.visitDeclared(this, p);
}
}
public static class ArrayType extends Type
implements javax.lang.model.type.ArrayType {

View File

@ -388,28 +388,6 @@ public class Types {
}
}
/**
* Scope filter used to skip methods that should be ignored during
* function interface conversion (such as methods overridden by
* j.l.Object)
*/
class DescriptorFilter implements Filter<Symbol> {
TypeSymbol origin;
DescriptorFilter(TypeSymbol origin) {
this.origin = origin;
}
@Override
public boolean accepts(Symbol sym) {
return sym.kind == Kinds.MTH &&
(sym.flags() & (ABSTRACT | DEFAULT)) == ABSTRACT &&
!overridesObjectMethod(origin, sym) &&
(interfaceCandidates(origin.type, (MethodSymbol)sym).head.flags() & DEFAULT) == 0;
}
};
/**
* Compute the function descriptor associated with a given functional interface
*/
@ -577,6 +555,85 @@ public class Types {
}
// </editor-fold>
/**
* Scope filter used to skip methods that should be ignored (such as methods
* overridden by j.l.Object) during function interface conversion/marker interface checks
*/
class DescriptorFilter implements Filter<Symbol> {
TypeSymbol origin;
DescriptorFilter(TypeSymbol origin) {
this.origin = origin;
}
@Override
public boolean accepts(Symbol sym) {
return sym.kind == Kinds.MTH &&
(sym.flags() & (ABSTRACT | DEFAULT)) == ABSTRACT &&
!overridesObjectMethod(origin, sym) &&
(interfaceCandidates(origin.type, (MethodSymbol)sym).head.flags() & DEFAULT) == 0;
}
};
// <editor-fold defaultstate="collapsed" desc="isMarker">
/**
* A cache that keeps track of marker interfaces
*/
class MarkerCache {
private WeakHashMap<TypeSymbol, Entry> _map = new WeakHashMap<TypeSymbol, Entry>();
class Entry {
final boolean isMarkerIntf;
final int prevMark;
public Entry(boolean isMarkerIntf,
int prevMark) {
this.isMarkerIntf = isMarkerIntf;
this.prevMark = prevMark;
}
boolean matches(int mark) {
return this.prevMark == mark;
}
}
boolean get(TypeSymbol origin) throws FunctionDescriptorLookupError {
Entry e = _map.get(origin);
CompoundScope members = membersClosure(origin.type, false);
if (e == null ||
!e.matches(members.getMark())) {
boolean isMarkerIntf = isMarkerInterfaceInternal(origin, members);
_map.put(origin, new Entry(isMarkerIntf, members.getMark()));
return isMarkerIntf;
}
else {
return e.isMarkerIntf;
}
}
/**
* Is given symbol a marker interface
*/
public boolean isMarkerInterfaceInternal(TypeSymbol origin, CompoundScope membersCache) throws FunctionDescriptorLookupError {
return !origin.isInterface() ?
false :
!membersCache.getElements(new DescriptorFilter(origin)).iterator().hasNext();
}
}
private MarkerCache markerCache = new MarkerCache();
/**
* Is given type a marker interface?
*/
public boolean isMarkerInterface(Type site) {
return markerCache.get(site.tsym);
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="isSubtype">
/**
* Is t an unchecked subtype of s?
@ -1955,45 +2012,28 @@ public class Types {
* @param supertype is objectType if all bounds are interfaces,
* null otherwise.
*/
public Type makeCompoundType(List<Type> bounds,
Type supertype) {
public Type makeCompoundType(List<Type> bounds) {
return makeCompoundType(bounds, bounds.head.tsym.isInterface());
}
public Type makeCompoundType(List<Type> bounds, boolean allInterfaces) {
Assert.check(bounds.nonEmpty());
Type firstExplicitBound = bounds.head;
if (allInterfaces) {
bounds = bounds.prepend(syms.objectType);
}
ClassSymbol bc =
new ClassSymbol(ABSTRACT|PUBLIC|SYNTHETIC|COMPOUND|ACYCLIC,
Type.moreInfo
? names.fromString(bounds.toString())
: names.empty,
null,
syms.noSymbol);
if (bounds.head.tag == TYPEVAR)
// error condition, recover
bc.erasure_field = syms.objectType;
else
bc.erasure_field = erasure(bounds.head);
bc.members_field = new Scope(bc);
ClassType bt = (ClassType)bc.type;
bt.allparams_field = List.nil();
if (supertype != null) {
bt.supertype_field = supertype;
bt.interfaces_field = bounds;
} else {
bt.supertype_field = bounds.head;
bt.interfaces_field = bounds.tail;
}
Assert.check(bt.supertype_field.tsym.completer != null
|| !bt.supertype_field.isInterface(),
bt.supertype_field);
return bt;
}
/**
* Same as {@link #makeCompoundType(List,Type)}, except that the
* second parameter is computed directly. Note that this might
* cause a symbol completion. Hence, this version of
* makeCompoundType may not be called during a classfile read.
*/
public Type makeCompoundType(List<Type> bounds) {
Type supertype = (bounds.head.tsym.flags() & INTERFACE) != 0 ?
supertype(bounds.head) : null;
return makeCompoundType(bounds, supertype);
bc.type = new IntersectionClassType(bounds, bc, allInterfaces);
bc.erasure_field = (bounds.head.tag == TYPEVAR) ?
syms.objectType : // error condition, recover
erasure(firstExplicitBound);
bc.members_field = new Scope(bc);
return bc.type;
}
/**
@ -2183,12 +2223,8 @@ public class Types {
* @param supertype is objectType if all bounds are interfaces,
* null otherwise.
*/
public void setBounds(TypeVar t, List<Type> bounds, Type supertype) {
if (bounds.tail.isEmpty())
t.bound = bounds.head;
else
t.bound = makeCompoundType(bounds, supertype);
t.rank_field = -1;
public void setBounds(TypeVar t, List<Type> bounds) {
setBounds(t, bounds, bounds.head.tsym.isInterface());
}
/**
@ -2200,10 +2236,10 @@ public class Types {
* Note that this check might cause a symbol completion. Hence, this version of
* setBounds may not be called during a classfile read.
*/
public void setBounds(TypeVar t, List<Type> bounds) {
Type supertype = (bounds.head.tsym.flags() & INTERFACE) != 0 ?
syms.objectType : null;
setBounds(t, bounds, supertype);
public void setBounds(TypeVar t, List<Type> bounds, boolean allInterfaces) {
t.bound = bounds.tail.isEmpty() ?
bounds.head :
makeCompoundType(bounds, allInterfaces);
t.rank_field = -1;
}
// </editor-fold>
@ -2213,7 +2249,7 @@ public class Types {
* Return list of bounds of the given type variable.
*/
public List<Type> getBounds(TypeVar t) {
if (t.bound.hasTag(NONE))
if (t.bound.hasTag(NONE))
return List.nil();
else if (t.bound.isErroneous() || !t.bound.isCompound())
return List.of(t.bound);
@ -3312,8 +3348,7 @@ public class Types {
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);
}
}
}

View File

@ -716,21 +716,8 @@ public class Attr extends JCTree.Visitor {
}
a.tsym.flags_field &= ~UNATTRIBUTED;
}
for (JCTypeParameter tvar : typarams)
for (JCTypeParameter tvar : typarams) {
chk.checkNonCyclic(tvar.pos(), (TypeVar)tvar.type);
attribStats(typarams, env);
}
void attribBounds(List<JCTypeParameter> typarams) {
for (JCTypeParameter typaram : typarams) {
Type bound = typaram.type.getUpperBound();
if (bound != null && bound.tsym instanceof ClassSymbol) {
ClassSymbol c = (ClassSymbol)bound.tsym;
if ((c.flags_field & COMPOUND) != 0) {
Assert.check((c.flags_field & UNATTRIBUTED) != 0, c);
attribClass(typaram.pos(), c);
}
}
}
}
@ -892,7 +879,12 @@ public class Attr extends JCTree.Visitor {
deferredLintHandler.flush(tree.pos());
chk.checkDeprecatedAnnotation(tree.pos(), m);
attribBounds(tree.typarams);
// Create a new environment with local scope
// for attributing the method.
Env<AttrContext> localEnv = memberEnter.methodEnv(tree, env);
localEnv.info.lint = lint;
attribStats(tree.typarams, localEnv);
// If we override any other methods, check that we do so properly.
// JLS ???
@ -903,12 +895,6 @@ public class Attr extends JCTree.Visitor {
}
chk.checkOverride(tree, m);
// Create a new environment with local scope
// for attributing the method.
Env<AttrContext> localEnv = memberEnter.methodEnv(tree, env);
localEnv.info.lint = lint;
if (isDefaultMethod && types.overridesObjectMethod(m.enclClass(), m)) {
log.error(tree, "default.overrides.object.member", m.name, Kinds.kindName(m.location()), m.location());
}
@ -2196,7 +2182,7 @@ public class Attr extends JCTree.Visitor {
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 @@ public class Attr extends JCTree.Visitor {
}
}
}
private Type checkIntersectionTarget(DiagnosticPosition pos, ResultInfo resultInfo) {
Type pt = resultInfo.pt;
if (pt != Type.recoveryType && pt.isCompound()) {
IntersectionClassType ict = (IntersectionClassType)pt;
List<Type> bounds = ict.allInterfaces ?
ict.getComponents().tail :
ict.getComponents();
types.findDescriptorType(bounds.head); //propagate exception outwards!
for (Type bound : bounds.tail) {
if (!types.isMarkerInterface(bound)) {
resultInfo.checkContext.report(pos, diags.fragment("secondary.bound.must.be.marker.intf", bound));
}
}
//for now (translation doesn't support intersection types)
return bounds.head;
} else {
return pt;
}
}
//where
private Type fallbackDescriptorType(JCExpression tree) {
switch (tree.getTag()) {
@ -2466,7 +2472,7 @@ public class Attr extends JCTree.Visitor {
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 @@ public class Attr extends JCTree.Visitor {
tree.type = result = t;
}
public void visitTypeParameter(JCTypeParameter tree) {
TypeVar a = (TypeVar)tree.type;
public void visitTypeIntersection(JCTypeIntersection tree) {
attribTypes(tree.bounds, env);
tree.type = result = checkIntersection(tree, tree.bounds);
}
public void visitTypeParameter(JCTypeParameter tree) {
TypeVar typeVar = (TypeVar)tree.type;
if (!typeVar.bound.isErroneous()) {
//fixup type-parameter bound computed in 'attribTypeVariables'
typeVar.bound = checkIntersection(tree, tree.bounds);
}
}
Type checkIntersection(JCTree tree, List<JCExpression> bounds) {
Set<Type> boundSet = new HashSet<Type>();
if (a.bound.isErroneous())
return;
List<Type> bs = types.getBounds(a);
if (tree.bounds.nonEmpty()) {
if (bounds.nonEmpty()) {
// accept class or interface or typevar as first bound.
Type b = checkBase(bs.head, tree.bounds.head, env, false, false, false);
boundSet.add(types.erasure(b));
if (b.isErroneous()) {
a.bound = b;
bounds.head.type = checkBase(bounds.head.type, bounds.head, env, false, false, false);
boundSet.add(types.erasure(bounds.head.type));
if (bounds.head.type.isErroneous()) {
return bounds.head.type;
}
else if (b.hasTag(TYPEVAR)) {
else if (bounds.head.type.hasTag(TYPEVAR)) {
// if first bound was a typevar, do not accept further bounds.
if (tree.bounds.tail.nonEmpty()) {
log.error(tree.bounds.tail.head.pos(),
if (bounds.tail.nonEmpty()) {
log.error(bounds.tail.head.pos(),
"type.var.may.not.be.followed.by.other.bounds");
tree.bounds = List.of(tree.bounds.head);
a.bound = bs.head;
return bounds.head.type;
}
} else {
// if first bound was a class or interface, accept only interfaces
// as further bounds.
for (JCExpression bound : tree.bounds.tail) {
bs = bs.tail;
Type i = checkBase(bs.head, bound, env, false, true, false);
if (i.isErroneous())
a.bound = i;
else if (i.hasTag(CLASS))
chk.checkNotRepeated(bound.pos(), types.erasure(i), boundSet);
for (JCExpression bound : bounds.tail) {
bound.type = checkBase(bound.type, bound, env, false, true, false);
if (bound.type.isErroneous()) {
bounds = List.of(bound);
}
else if (bound.type.hasTag(CLASS)) {
chk.checkNotRepeated(bound.pos(), types.erasure(bound.type), boundSet);
}
}
}
}
bs = types.getBounds(a);
// in case of multiple bounds ...
if (bs.length() > 1) {
if (bounds.length() == 0) {
return syms.objectType;
} else if (bounds.length() == 1) {
return bounds.head.type;
} else {
Type owntype = types.makeCompoundType(TreeInfo.types(bounds));
if (tree.hasTag(TYPEINTERSECTION)) {
((IntersectionClassType)owntype).intersectionKind =
IntersectionClassType.IntersectionKind.EXPLICIT;
}
// ... the variable's bound is a class type flagged COMPOUND
// (see comment for TypeVar.bound).
// In this case, generate a class tree that represents the
// bound class, ...
JCExpression extending;
List<JCExpression> implementing;
if ((bs.head.tsym.flags() & INTERFACE) == 0) {
extending = tree.bounds.head;
implementing = tree.bounds.tail;
if (!bounds.head.type.isInterface()) {
extending = bounds.head;
implementing = bounds.tail;
} else {
extending = null;
implementing = tree.bounds;
implementing = bounds;
}
JCClassDecl cd = make.at(tree.pos).ClassDef(
JCClassDecl cd = make.at(tree).ClassDef(
make.Modifiers(PUBLIC | ABSTRACT),
tree.name, List.<JCTypeParameter>nil(),
names.empty, List.<JCTypeParameter>nil(),
extending, implementing, List.<JCTree>nil());
ClassSymbol c = (ClassSymbol)a.getUpperBound().tsym;
ClassSymbol c = (ClassSymbol)owntype.tsym;
Assert.check((c.flags() & COMPOUND) != 0);
cd.sym = c;
c.sourcefile = env.toplevel.sourcefile;
@ -3640,10 +3662,11 @@ public class Attr extends JCTree.Visitor {
c.flags_field |= UNATTRIBUTED;
Env<AttrContext> cenv = enter.classEnv(cd, env);
enter.typeEnvs.put(c, cenv);
attribClass(c);
return owntype;
}
}
public void visitWildcard(JCWildcard tree) {
//- System.err.println("visitWildcard("+tree+");");//DEBUG
Type type = (tree.kind.kind == BoundKind.UNBOUND)
@ -3797,7 +3820,7 @@ public class Attr extends JCTree.Visitor {
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);

View File

@ -551,6 +551,7 @@ public class TransTypes extends TreeTranslator {
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 @@ public class TransTypes extends TreeTranslator {
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 @@ public class TransTypes extends TreeTranslator {
result = clazz;
}
public void visitTypeIntersection(JCTypeIntersection tree) {
tree.bounds = translate(tree.bounds, null);
tree.type = erasure(tree.type);
result = tree;
}
/**************************************************************************
* utility methods
*************************************************************************/

View File

@ -846,17 +846,17 @@ public class ClassReader implements Completer {
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;
}

View File

@ -74,6 +74,7 @@ public class JavacTypes implements javax.lang.model.util.Types {
public Element asElement(TypeMirror t) {
switch (t.getKind()) {
case DECLARED:
case INTERSECTION:
case ERROR:
case TYPEVAR:
Type type = cast(Type.class, t);

View File

@ -124,6 +124,9 @@ public class JavacParser implements Parser {
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 @@ public class JavacParser implements Parser {
*/
boolean allowDefaultMethods;
/** Switch: should we allow intersection types in cast?
*/
boolean allowIntersectionTypesInCast;
/** Switch: should we keep docComments?
*/
boolean keepDocComments;
@ -239,22 +246,38 @@ public class JavacParser implements Parser {
}
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 @@ public class JavacParser implements Parser {
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)
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);
ParensResult pres = analyzeParens();
switch (pres) {
case CAST:
accept(LPAREN);
mode = TYPE;
int pos1 = pos;
List<JCExpression> targets = List.of(t = term3());
while (token.kind == AMP) {
checkIntersectionTypesInCast();
accept(AMP);
targets = targets.prepend(term3());
}
if (targets.length() > 1) {
t = toP(F.at(pos1).TypeIntersection(targets.reverse()));
}
accept(RPAREN);
mode = EXPR;
JCExpression t1 = term3();
return F.at(pos).TypeCast(t, t1);
case IMPLICIT_LAMBDA:
case EXPLICIT_LAMBDA:
t = lambdaExpressionOrStatement(true, pres == ParensResult.EXPLICIT_LAMBDA, pos);
break;
default: //PARENS
accept(LPAREN);
mode = EXPR;
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 @@ public class JavacParser implements Parser {
}
}
/**
* If we see an identifier followed by a '&lt;' it could be an unbound
* method reference or a binary expression. To disambiguate, look for a
* matching '&gt;' and see if the subsequent terminal is either '.' or '#'.
*/
@SuppressWarnings("fallthrough")
ParensResult analyzeParens() {
int depth = 0;
boolean type = false;
for (int lookahead = 0 ; ; lookahead++) {
TokenKind tk = S.token(lookahead).kind;
switch (tk) {
case EXTENDS: case SUPER: case COMMA:
type = true;
case QUES: case DOT: case AMP:
//skip
break;
case BYTE: case SHORT: case INT: case LONG: case FLOAT:
case DOUBLE: case BOOLEAN: case CHAR:
if (peekToken(lookahead, RPAREN)) {
//Type, ')' -> cast
return ParensResult.CAST;
} else if (peekToken(lookahead, IDENTIFIER)) {
//Type, 'Identifier -> explicit lambda
return ParensResult.EXPLICIT_LAMBDA;
}
break;
case LPAREN:
if (lookahead != 0) {
// '(' in a non-starting position -> parens
return ParensResult.PARENS;
} else if (peekToken(lookahead, RPAREN)) {
// '(', ')' -> explicit lambda
return ParensResult.EXPLICIT_LAMBDA;
}
break;
case RPAREN:
// if we have seen something that looks like a type,
// then it's a cast expression
if (type) return ParensResult.CAST;
// otherwise, disambiguate cast vs. parenthesized expression
// based on subsequent token.
switch (S.token(lookahead + 1).kind) {
/*case PLUSPLUS: case SUBSUB: */
case BANG: case TILDE:
case LPAREN: case THIS: case SUPER:
case INTLITERAL: case LONGLITERAL: case FLOATLITERAL:
case DOUBLELITERAL: case CHARLITERAL: case STRINGLITERAL:
case TRUE: case FALSE: case NULL:
case NEW: case IDENTIFIER: case ASSERT: case ENUM:
case BYTE: case SHORT: case CHAR: case INT:
case LONG: case FLOAT: case DOUBLE: case BOOLEAN: case VOID:
return ParensResult.CAST;
default:
return ParensResult.PARENS;
}
case IDENTIFIER:
if (peekToken(lookahead, IDENTIFIER)) {
// Identifier, Identifier -> explicit lambda
return ParensResult.EXPLICIT_LAMBDA;
} else if (peekToken(lookahead, RPAREN, ARROW)) {
// Identifier, ')' '->' -> implicit lambda
return ParensResult.IMPLICIT_LAMBDA;
}
break;
case FINAL:
case ELLIPSIS:
case MONKEYS_AT:
//those can only appear in explicit lambdas
return ParensResult.EXPLICIT_LAMBDA;
case LBRACKET:
if (peekToken(lookahead, RBRACKET, IDENTIFIER)) {
// '[', ']', Identifier -> explicit lambda
return ParensResult.EXPLICIT_LAMBDA;
} else if (peekToken(lookahead, RBRACKET, RPAREN) ||
peekToken(lookahead, RBRACKET, AMP)) {
// '[', ']', ')' -> cast
// '[', ']', '&' -> cast (intersection type)
return ParensResult.CAST;
} else if (peekToken(lookahead, RBRACKET)) {
//consume the ']' and skip
type = true;
lookahead++;
break;
} else {
return ParensResult.PARENS;
}
case LT:
depth++; break;
case GTGTGT:
depth--;
case GTGT:
depth--;
case GT:
depth--;
if (depth == 0) {
if (peekToken(lookahead, RPAREN) ||
peekToken(lookahead, AMP)) {
// '>', ')' -> cast
// '>', '&' -> cast
return ParensResult.CAST;
} else if (peekToken(lookahead, IDENTIFIER, COMMA) ||
peekToken(lookahead, IDENTIFIER, RPAREN, ARROW) ||
peekToken(lookahead, ELLIPSIS)) {
// '>', Identifier, ',' -> explicit lambda
// '>', Identifier, ')', '->' -> explicit lambda
// '>', '...' -> explicit lambda
return ParensResult.EXPLICIT_LAMBDA;
}
//it looks a type, but could still be (i) a cast to generic type,
//(ii) an unbound method reference or (iii) an explicit lambda
type = true;
break;
} else if (depth < 0) {
//unbalanced '<', '>' - not a generic type
return ParensResult.PARENS;
}
break;
default:
//this includes EOF
return ParensResult.PARENS;
}
}
}
enum ParensResult {
CAST,
EXPLICIT_LAMBDA,
IMPLICIT_LAMBDA,
PARENS;
}
JCExpression lambdaExpressionOrStatement(JCVariableDecl firstParam, int pos) {
ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>();
params.append(firstParam);
@ -3386,6 +3479,12 @@ public class JavacParser implements Parser {
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

View File

@ -207,6 +207,10 @@ compiler.misc.descriptor.throws=\
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 @@ compiler.err.default.methods.not.supported.in.source=\
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)

View File

@ -254,6 +254,10 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
*/
TYPEUNION,
/** Intersection types, of type TypeIntersection
*/
TYPEINTERSECTION,
/** Formal type parameters, of type TypeParameter.
*/
TYPEPARAMETER,
@ -2061,6 +2065,34 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
}
}
/**
* 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.
*/
@ -2383,6 +2415,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
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); }

View File

@ -1249,6 +1249,14 @@ public class Pretty extends JCTree.Visitor {
}
}
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);

View File

@ -358,6 +358,12 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
return M.at(t.pos).TypeUnion(components);
}
public JCTree visitIntersectionType(IntersectionTypeTree node, P p) {
JCTypeIntersection t = (JCTypeIntersection) node;
List<JCExpression> bounds = copy(t.bounds, p);
return M.at(t.pos).TypeIntersection(bounds);
}
public JCTree visitArrayType(ArrayTypeTree node, P p) {
JCArrayTypeTree t = (JCArrayTypeTree) node;
JCExpression elemtype = copy(t.elemtype, p);

View File

@ -456,6 +456,12 @@ public class TreeMaker implements JCTree.Factory {
return tree;
}
public JCTypeIntersection TypeIntersection(List<JCExpression> components) {
JCTypeIntersection tree = new JCTypeIntersection(components);
tree.pos = pos;
return tree;
}
public JCTypeParameter TypeParameter(Name name, List<JCExpression> bounds) {
JCTypeParameter tree = new JCTypeParameter(name, bounds);
tree.pos = pos;

View File

@ -286,6 +286,10 @@ public class TreeScanner extends Visitor {
scan(tree.alternatives);
}
public void visitTypeIntersection(JCTypeIntersection tree) {
scan(tree.bounds);
}
public void visitTypeParameter(JCTypeParameter tree) {
scan(tree.bounds);
}

View File

@ -379,6 +379,11 @@ public class TreeTranslator extends JCTree.Visitor {
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;

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.lang.model.type;
import java.util.List;
/**
* Represents an intersection type.
*
* As of the {@link javax.lang.model.SourceVersion#RELEASE_8
* RELEASE_8} source version, intersection types can appear as the target type
* of a cast expression.
*
* @since 1.8
*/
public interface IntersectionType extends TypeMirror {
/**
* Return the bounds comprising this intersection type.
*
* @return the bounds of this intersection types.
*/
List<? extends TypeMirror> getBounds();
}

View File

@ -144,7 +144,14 @@ public enum TypeKind {
*
* @since 1.7
*/
UNION;
UNION,
/**
* An intersection type.
*
* @since 1.8
*/
INTERSECTION;
/**
* Returns {@code true} if this kind corresponds to a primitive

View File

@ -172,4 +172,14 @@ public interface TypeVisitor<R, P> {
* @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);
}

View File

@ -110,6 +110,20 @@ public abstract class AbstractTypeVisitor6<R, P> implements TypeVisitor<R, 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}
*

View File

@ -66,4 +66,13 @@ public abstract class AbstractTypeVisitor8<R, P> extends AbstractTypeVisitor7<R,
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);
}

View File

@ -0,0 +1,330 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8002099
* @summary Add support for intersection types in cast expression
*/
import com.sun.source.util.JavacTask;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import java.net.URI;
import java.util.Arrays;
import javax.tools.Diagnostic;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class IntersectionTypeCastTest {
static int checkCount = 0;
interface Type {
boolean subtypeOf(Type that);
String asString();
boolean isClass();
boolean isInterface();
}
enum InterfaceKind implements Type {
A("interface A { }\n", "A", null),
B("interface B { }\n", "B", null),
C("interface C extends A { }\n", "C", A);
String declStr;
String typeStr;
InterfaceKind superInterface;
InterfaceKind(String declStr, String typeStr, InterfaceKind superInterface) {
this.declStr = declStr;
this.typeStr = typeStr;
this.superInterface = superInterface;
}
@Override
public boolean subtypeOf(Type that) {
return this == that || superInterface == that || that == ClassKind.OBJECT;
}
@Override
public String asString() {
return typeStr;
}
@Override
public boolean isClass() {
return false;
}
@Override
public boolean isInterface() {
return true;
}
}
enum ClassKind implements Type {
OBJECT(null, "Object"),
CA("#M class CA implements A { }\n", "CA", InterfaceKind.A),
CB("#M class CB implements B { }\n", "CB", InterfaceKind.B),
CAB("#M class CAB implements A, B { }\n", "CAB", InterfaceKind.A, InterfaceKind.B),
CC("#M class CC implements C { }\n", "CC", InterfaceKind.C, InterfaceKind.A),
CCA("#M class CCA implements C, A { }\n", "CCA", InterfaceKind.C, InterfaceKind.A),
CCB("#M class CCB implements C, B { }\n", "CCB", InterfaceKind.C, InterfaceKind.A, InterfaceKind.B),
CCAB("#M class CCAB implements C, A, B { }\n", "CCAB", InterfaceKind.C, InterfaceKind.A, InterfaceKind.B);
String declTemplate;
String typeStr;
List<InterfaceKind> superInterfaces;
ClassKind(String declTemplate, String typeStr, InterfaceKind... superInterfaces) {
this.declTemplate = declTemplate;
this.typeStr = typeStr;
this.superInterfaces = List.from(superInterfaces);
}
String getDecl(ModifierKind mod) {
return declTemplate != null ?
declTemplate.replaceAll("#M", mod.modStr) :
"";
}
@Override
public boolean subtypeOf(Type that) {
return this == that || superInterfaces.contains(that) || that == OBJECT;
}
@Override
public String asString() {
return typeStr;
}
@Override
public boolean isClass() {
return true;
}
@Override
public boolean isInterface() {
return false;
}
}
enum ModifierKind {
NONE(""),
FINAL("final");
String modStr;
ModifierKind(String modStr) {
this.modStr = modStr;
}
}
enum CastKind {
CLASS("(#C)", 0),
INTERFACE("(#I0)", 1),
INTERSECTION2("(#C & #I0)", 1),
INTERSECTION3("(#C & #I0 & #I1)", 2);
//INTERSECTION4("(#C & #I0 & #I1 & #I2)", 3);
String castTemplate;
int interfaceBounds;
CastKind(String castTemplate, int interfaceBounds) {
this.castTemplate = castTemplate;
this.interfaceBounds = interfaceBounds;
}
}
static class CastInfo {
CastKind kind;
Type[] types;
CastInfo(CastKind kind, Type... types) {
this.kind = kind;
this.types = types;
}
String getCast() {
String temp = kind.castTemplate.replaceAll("#C", types[0].asString());
for (int i = 0; i < kind.interfaceBounds ; i++) {
temp = temp.replace(String.format("#I%d", i), types[i + 1].asString());
}
return temp;
}
boolean hasDuplicateTypes() {
for (int i = 0 ; i < types.length ; i++) {
for (int j = 0 ; j < types.length ; j++) {
if (i != j && types[i] == types[j]) {
return true;
}
}
}
return false;
}
boolean compatibleWith(ModifierKind mod, CastInfo that) {
for (Type t1 : types) {
for (Type t2 : that.types) {
boolean compat =
t1.subtypeOf(t2) ||
t2.subtypeOf(t1) ||
(t1.isInterface() && t2.isInterface()) || //side-cast (1)
(mod == ModifierKind.NONE && (t1.isInterface() != t2.isInterface())); //side-cast (2)
if (!compat) return false;
}
}
return true;
}
}
public static void main(String... args) throws Exception {
//create default shared JavaCompiler - reused across multiple compilations
JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
for (ModifierKind mod : ModifierKind.values()) {
for (CastInfo cast1 : allCastInfo()) {
for (CastInfo cast2 : allCastInfo()) {
new IntersectionTypeCastTest(mod, cast1, cast2).run(comp, fm);
}
}
}
System.out.println("Total check executed: " + checkCount);
}
static List<CastInfo> allCastInfo() {
ListBuffer<CastInfo> buf = ListBuffer.lb();
for (CastKind kind : CastKind.values()) {
for (ClassKind clazz : ClassKind.values()) {
if (kind == CastKind.INTERFACE && clazz != ClassKind.OBJECT) {
continue;
} else if (kind.interfaceBounds == 0) {
buf.append(new CastInfo(kind, clazz));
continue;
} else {
for (InterfaceKind intf1 : InterfaceKind.values()) {
if (kind.interfaceBounds == 1) {
buf.append(new CastInfo(kind, clazz, intf1));
continue;
} else {
for (InterfaceKind intf2 : InterfaceKind.values()) {
if (kind.interfaceBounds == 2) {
buf.append(new CastInfo(kind, clazz, intf1, intf2));
continue;
} else {
for (InterfaceKind intf3 : InterfaceKind.values()) {
buf.append(new CastInfo(kind, clazz, intf1, intf2, intf3));
continue;
}
}
}
}
}
}
}
}
return buf.toList();
}
ModifierKind mod;
CastInfo cast1, cast2;
JavaSource source;
DiagnosticChecker diagChecker;
IntersectionTypeCastTest(ModifierKind mod, CastInfo cast1, CastInfo cast2) {
this.mod = mod;
this.cast1 = cast1;
this.cast2 = cast2;
this.source = new JavaSource();
this.diagChecker = new DiagnosticChecker();
}
class JavaSource extends SimpleJavaFileObject {
String bodyTemplate = "class Test {\n" +
" void test() {\n" +
" Object o = #C1#C2null;\n" +
" } }";
String source = "";
public JavaSource() {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
for (ClassKind ck : ClassKind.values()) {
source += ck.getDecl(mod);
}
for (InterfaceKind ik : InterfaceKind.values()) {
source += ik.declStr;
}
source += bodyTemplate.replaceAll("#C1", cast1.getCast()).replaceAll("#C2", cast2.getCast());
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}
}
void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception {
JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker,
Arrays.asList("-XDallowIntersectionTypes"), null, Arrays.asList(source));
try {
ct.analyze();
} catch (Throwable ex) {
throw new AssertionError("Error thrown when compiling the following code:\n" + source.getCharContent(true));
}
check();
}
void check() {
checkCount++;
boolean errorExpected = cast1.hasDuplicateTypes() || cast2.hasDuplicateTypes();
errorExpected |= !cast2.compatibleWith(mod, cast1);
if (errorExpected != diagChecker.errorFound) {
throw new Error("invalid diagnostics for source:\n" +
source.getCharContent(true) +
"\nFound error: " + diagChecker.errorFound +
"\nExpected error: " + errorExpected);
}
}
static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
boolean errorFound;
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
errorFound = true;
}
}
}
}

View File

@ -0,0 +1,191 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8002099
* @summary Add support for intersection types in cast expression
*/
import com.sun.source.util.JavacTask;
import java.net.URI;
import java.util.Arrays;
import javax.tools.Diagnostic;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class IntersectionTypeParserTest {
static int checkCount = 0;
enum TypeKind {
SIMPLE("A"),
GENERIC("A<X>"),
WILDCARD("A<? super X, ? extends Y>");
String typeStr;
TypeKind(String typeStr) {
this.typeStr = typeStr;
}
}
enum ArrayKind {
NONE(""),
SINGLE("[]"),
DOUBLE("[][]");
String arrStr;
ArrayKind(String arrStr) {
this.arrStr = arrStr;
}
}
static class Type {
TypeKind tk;
ArrayKind ak;
Type(TypeKind tk, ArrayKind ak) {
this.tk = tk;
this.ak = ak;
}
String asString() {
return tk.typeStr + ak.arrStr;
}
}
enum CastKind {
ONE("(#T0)", 1),
TWO("(#T0 & T1)", 2),
THREE("(#T0 & #T1 & #T2)", 3);
String castTemplate;
int nBounds;
CastKind(String castTemplate, int nBounds) {
this.castTemplate = castTemplate;
this.nBounds = nBounds;
}
String asString(Type... types) {
String res = castTemplate;
for (int i = 0; i < nBounds ; i++) {
res = res.replaceAll(String.format("#T%d", i), types[i].asString());
}
return res;
}
}
public static void main(String... args) throws Exception {
//create default shared JavaCompiler - reused across multiple compilations
JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
for (CastKind ck : CastKind.values()) {
for (TypeKind t1 : TypeKind.values()) {
for (ArrayKind ak1 : ArrayKind.values()) {
Type typ1 = new Type(t1, ak1);
if (ck.nBounds == 1) {
new IntersectionTypeParserTest(ck, typ1).run(comp, fm);
continue;
}
for (TypeKind t2 : TypeKind.values()) {
for (ArrayKind ak2 : ArrayKind.values()) {
Type typ2 = new Type(t2, ak2);
if (ck.nBounds == 2) {
new IntersectionTypeParserTest(ck, typ1, typ2).run(comp, fm);
continue;
}
for (TypeKind t3 : TypeKind.values()) {
for (ArrayKind ak3 : ArrayKind.values()) {
Type typ3 = new Type(t3, ak3);
new IntersectionTypeParserTest(ck, typ1, typ2, typ3).run(comp, fm);
}
}
}
}
}
}
}
System.out.println("Total check executed: " + checkCount);
}
CastKind ck;
Type[] types;
JavaSource source;
DiagnosticChecker diagChecker;
IntersectionTypeParserTest(CastKind ck, Type... types) {
this.ck = ck;
this.types = types;
this.source = new JavaSource();
this.diagChecker = new DiagnosticChecker();
}
class JavaSource extends SimpleJavaFileObject {
String bodyTemplate = "class Test {\n" +
" void test() {\n" +
" Object o = #Cnull;\n" +
" } }";
String source = "";
public JavaSource() {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
source += bodyTemplate.replaceAll("#C", ck.asString(types));
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}
}
void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception {
checkCount++;
JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker,
Arrays.asList("-XDallowIntersectionTypes"), null, Arrays.asList(source));
ct.parse();
if (diagChecker.errorFound) {
throw new Error("Unexpected parser error for source:\n" +
source.getCharContent(true));
}
}
static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
boolean errorFound;
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
errorFound = true;
}
}
}
}

View File

@ -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 {}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -0,0 +1,153 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.TypeCastTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePathScanner;
import com.sun.source.util.Trees;
import com.sun.source.util.TreePath;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import java.util.Set;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.IntersectionType;
import javax.lang.model.type.UnknownTypeException;
import javax.lang.model.util.SimpleTypeVisitor6;
import javax.lang.model.util.SimpleTypeVisitor7;
@SupportedAnnotationTypes("Check")
public class ModelChecker extends JavacTestingAbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (roundEnv.processingOver())
return true;
Trees trees = Trees.instance(processingEnv);
TypeElement testAnno = elements.getTypeElement("Check");
for (Element elem: roundEnv.getElementsAnnotatedWith(testAnno)) {
TreePath p = trees.getPath(elem);
new IntersectionCastTester(trees).scan(p, null);
}
return true;
}
class IntersectionCastTester extends TreePathScanner<Void, Void> {
Trees trees;
public IntersectionCastTester(Trees trees) {
super();
this.trees = trees;
}
@Override
public Void visitVariable(VariableTree node, Void p) {
TreePath varPath = new TreePath(getCurrentPath(), node);
Element v = trees.getElement(varPath);
IntersectionTypeInfo it = v.getAnnotation(IntersectionTypeInfo.class);
assertTrue(it != null, "IntersectionType annotation must be present");
ExpressionTree varInit = node.getInitializer();
assertTrue(varInit != null && varInit.getKind() == Tree.Kind.TYPE_CAST,
"variable must have be initialized to an expression containing an intersection type cast");
TypeMirror t = ((JCExpression)((TypeCastTree)varInit).getType()).type;
validateIntersectionTypeInfo(t, it);
for (Element e2 : types.asElement(t).getEnclosedElements()) {
assertTrue(false, "an intersection type has no declared members");
}
for (Element e2 : elements.getAllMembers((TypeElement)types.asElement(t))) {
Member m = e2.getAnnotation(Member.class);
if (m != null) {
assertTrue(e2.getKind() == m.value(), "Expected " + m.value() + " - found " + e2.getKind());
}
}
assertTrue(assertionCount == 10, "Expected 10 assertions - found " + assertionCount);
return super.visitVariable(node, p);
}
}
private void validateIntersectionTypeInfo(TypeMirror expectedIntersectionType, IntersectionTypeInfo it) {
assertTrue(expectedIntersectionType.getKind() == TypeKind.INTERSECTION, "INTERSECTION kind expected");
try {
new SimpleTypeVisitor6<Void, Void>(){}.visit(expectedIntersectionType);
throw new RuntimeException("Expected UnknownTypeException not thrown.");
} catch (UnknownTypeException ute) {
; // Expected
}
try {
new SimpleTypeVisitor7<Void, Void>(){}.visit(expectedIntersectionType);
throw new RuntimeException("Expected UnknownTypeException not thrown.");
} catch (UnknownTypeException ute) {
; // Expected
}
IntersectionType intersectionType = new SimpleTypeVisitor<IntersectionType, Void>(){
@Override
protected IntersectionType defaultAction(TypeMirror e, Void p) {return null;}
@Override
public IntersectionType visitIntersection(IntersectionType t, Void p) {return t;}
}.visit(expectedIntersectionType);
assertTrue(intersectionType != null, "Must get a non-null intersection type.");
assertTrue(it.value().length == intersectionType.getBounds().size(), "Cardinalities do not match");
String[] typeNames = it.value();
for(int i = 0; i < typeNames.length; i++) {
TypeMirror typeFromAnnotation = nameToType(typeNames[i]);
assertTrue(types.isSameType(typeFromAnnotation, intersectionType.getBounds().get(i)),
"Types were not equal.");
}
}
private TypeMirror nameToType(String name) {
return elements.getTypeElement(name).asType();
}
private static void assertTrue(boolean cond, String msg) {
assertionCount++;
if (!cond)
throw new AssertionError(msg);
}
static int assertionCount = 0;
}

View File

@ -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;
}

View File

@ -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<?>)()->{};
}

View File

@ -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() { }
}

View File

@ -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

View File

@ -90,9 +90,14 @@ public class LambdaParserTest {
enum LambdaParameterKind {
IMPLICIT(""),
EXPLIICT_SIMPLE("A"),
EXPLIICT_SIMPLE_ARR1("A[]"),
EXPLIICT_SIMPLE_ARR2("A[][]"),
EXPLICIT_VARARGS("A..."),
EXPLICIT_GENERIC1("A<X>"),
EXPLICIT_GENERIC3("A<? extends X, ? super Y>");
EXPLICIT_GENERIC2("A<? extends X, ? super Y>"),
EXPLICIT_GENERIC2_VARARGS("A<? extends X, ? super Y>..."),
EXPLICIT_GENERIC2_ARR1("A<? extends X, ? super Y>[]"),
EXPLICIT_GENERIC2_ARR2("A<? extends X, ? super Y>[][]");
String parameterType;
@ -103,6 +108,11 @@ public class LambdaParserTest {
boolean explicit() {
return this != IMPLICIT;
}
boolean isVarargs() {
return this == EXPLICIT_VARARGS ||
this == EXPLICIT_GENERIC2_VARARGS;
}
}
enum ModifierKind {
@ -253,7 +263,7 @@ public class LambdaParserTest {
if (lk.arity() == 2 &&
(pk1.explicit() != pk2.explicit() ||
pk1 == LambdaParameterKind.EXPLICIT_VARARGS)) {
pk1.isVarargs())) {
errorExpected = true;
}

View File

@ -0,0 +1,294 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8002099
* @summary Add support for intersection types in cast expression
*/
import com.sun.source.util.JavacTask;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import java.net.URI;
import java.util.Arrays;
import javax.tools.Diagnostic;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class IntersectionTargetTypeTest {
static int checkCount = 0;
enum BoundKind {
INTF,
CLASS,
SAM,
ZAM;
}
enum MethodKind {
NONE,
ABSTRACT,
DEFAULT;
}
enum TypeKind {
A("interface A { }\n", "A", BoundKind.ZAM),
B("interface B { default void m() { } }\n", "B", BoundKind.ZAM),
C("interface C { void m(); }\n", "C", BoundKind.SAM),
D("interface D extends B { }\n", "D", BoundKind.ZAM),
E("interface E extends C { }\n", "E", BoundKind.SAM),
F("interface F extends C { void g(); }\n", "F", BoundKind.INTF),
G("interface G extends B { void g(); }\n", "G", BoundKind.SAM),
H("interface H extends A { void g(); }\n", "H", BoundKind.SAM),
OBJECT("", "Object", BoundKind.CLASS),
STRING("", "String", BoundKind.CLASS);
String declStr;
String typeStr;
BoundKind boundKind;
private TypeKind(String declStr, String typeStr, BoundKind boundKind) {
this.declStr = declStr;
this.typeStr = typeStr;
this.boundKind = boundKind;
}
boolean compatibleSupertype(TypeKind tk) {
if (tk == this) return true;
switch (tk) {
case B:
return this != C && this != E && this != F;
case C:
return this != B && this != C && this != D && this != G;
case D: return compatibleSupertype(B);
case E:
case F: return compatibleSupertype(C);
case G: return compatibleSupertype(B);
case H: return compatibleSupertype(A);
default:
return true;
}
}
}
enum CastKind {
ONE_ARY("(#B0)", 1),
TWO_ARY("(#B0 & #B1)", 2),
THREE_ARY("(#B0 & #B1 & #B2)", 3);
String castTemplate;
int nbounds;
CastKind(String castTemplate, int nbounds) {
this.castTemplate = castTemplate;
this.nbounds = nbounds;
}
}
enum ExpressionKind {
LAMBDA("()->{}", true),
MREF("this::m", true),
//COND_LAMBDA("(true ? ()->{} : ()->{})", true), re-enable if spec allows this
//COND_MREF("(true ? this::m : this::m)", true),
STANDALONE("null", false);
String exprString;
boolean isFunctional;
private ExpressionKind(String exprString, boolean isFunctional) {
this.exprString = exprString;
this.isFunctional = isFunctional;
}
}
static class CastInfo {
CastKind kind;
TypeKind[] types;
CastInfo(CastKind kind, TypeKind... types) {
this.kind = kind;
this.types = types;
}
String getCast() {
String temp = kind.castTemplate;
for (int i = 0; i < kind.nbounds ; i++) {
temp = temp.replace(String.format("#B%d", i), types[i].typeStr);
}
return temp;
}
boolean wellFormed() {
//check for duplicate types
for (int i = 0 ; i < types.length ; i++) {
for (int j = 0 ; j < types.length ; j++) {
if (i != j && types[i] == types[j]) {
return false;
}
}
}
//check that classes only appear as first bound
boolean classOk = true;
for (int i = 0 ; i < types.length ; i++) {
if (types[i].boundKind == BoundKind.CLASS &&
!classOk) {
return false;
}
classOk = false;
}
//check that supertypes are mutually compatible
for (int i = 0 ; i < types.length ; i++) {
for (int j = 0 ; j < types.length ; j++) {
if (!types[i].compatibleSupertype(types[j]) && i != j) {
return false;
}
}
}
return true;
}
}
public static void main(String... args) throws Exception {
//create default shared JavaCompiler - reused across multiple compilations
JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
for (CastInfo cInfo : allCastInfo()) {
for (ExpressionKind ek : ExpressionKind.values()) {
new IntersectionTargetTypeTest(cInfo, ek).run(comp, fm);
}
}
System.out.println("Total check executed: " + checkCount);
}
static List<CastInfo> allCastInfo() {
ListBuffer<CastInfo> buf = ListBuffer.lb();
for (CastKind kind : CastKind.values()) {
for (TypeKind b1 : TypeKind.values()) {
if (kind.nbounds == 1) {
buf.append(new CastInfo(kind, b1));
continue;
} else {
for (TypeKind b2 : TypeKind.values()) {
if (kind.nbounds == 2) {
buf.append(new CastInfo(kind, b1, b2));
continue;
} else {
for (TypeKind b3 : TypeKind.values()) {
buf.append(new CastInfo(kind, b1, b2, b3));
}
}
}
}
}
}
return buf.toList();
}
CastInfo cInfo;
ExpressionKind ek;
JavaSource source;
DiagnosticChecker diagChecker;
IntersectionTargetTypeTest(CastInfo cInfo, ExpressionKind ek) {
this.cInfo = cInfo;
this.ek = ek;
this.source = new JavaSource();
this.diagChecker = new DiagnosticChecker();
}
class JavaSource extends SimpleJavaFileObject {
String bodyTemplate = "class Test {\n" +
" void m() { }\n" +
" void test() {\n" +
" Object o = #C#E;\n" +
" } }";
String source = "";
public JavaSource() {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
for (TypeKind tk : TypeKind.values()) {
source += tk.declStr;
}
source += bodyTemplate.replaceAll("#C", cInfo.getCast()).replaceAll("#E", ek.exprString);
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}
}
void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception {
JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker,
Arrays.asList("-XDallowIntersectionTypes"), null, Arrays.asList(source));
try {
ct.analyze();
} catch (Throwable ex) {
throw new AssertionError("Error thrown when compiling the following code:\n" + source.getCharContent(true));
}
check();
}
void check() {
checkCount++;
boolean errorExpected = !cInfo.wellFormed();
if (ek.isFunctional) {
//first bound must be a SAM
errorExpected |= cInfo.types[0].boundKind != BoundKind.SAM;
if (cInfo.types.length > 1) {
//additional bounds must be ZAMs
for (int i = 1; i < cInfo.types.length; i++) {
errorExpected |= cInfo.types[i].boundKind != BoundKind.ZAM;
}
}
}
if (errorExpected != diagChecker.errorFound) {
throw new Error("invalid diagnostics for source:\n" +
source.getCharContent(true) +
"\nFound error: " + diagChecker.errorFound +
"\nExpected error: " + errorExpected);
}
}
static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
boolean errorFound;
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
errorFound = true;
}
}
}
}