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:
parent
c76c08e82a
commit
d7884e5ae2
@ -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();
|
||||
}
|
@ -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}.
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
*************************************************************************/
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 '<' 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>();
|
||||
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
|
||||
|
@ -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)
|
||||
|
@ -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); }
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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}
|
||||
*
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {}
|
@ -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();
|
||||
}
|
@ -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();
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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<?>)()->{};
|
||||
}
|
42
langtools/test/tools/javac/lambda/Intersection01.java
Normal file
42
langtools/test/tools/javac/lambda/Intersection01.java
Normal 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() { }
|
||||
}
|
3
langtools/test/tools/javac/lambda/Intersection01.out
Normal file
3
langtools/test/tools/javac/lambda/Intersection01.out
Normal 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
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user