< prev index next > src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
Print this page
import com.sun.tools.javac.code.TypeMetadata.Annotations;
import com.sun.tools.javac.comp.AttrContext;
import com.sun.tools.javac.comp.Check;
import com.sun.tools.javac.comp.Enter;
import com.sun.tools.javac.comp.Env;
- import com.sun.tools.javac.comp.LambdaToMethod;
import com.sun.tools.javac.jvm.ClassFile;
import com.sun.tools.javac.util.*;
import static com.sun.tools.javac.code.BoundKind.*;
import static com.sun.tools.javac.code.Flags.*;
protected static final Context.Key<Types> typesKey = new Context.Key<>();
final Symtab syms;
final JavacMessages messages;
final Names names;
+ final boolean allowPrimitiveClasses;
final Check chk;
final Enter enter;
JCDiagnostic.Factory diags;
List<Warner> warnStack = List.nil();
final Name capturedName;
enter = Enter.instance(context);
capturedName = names.fromString("<captured wildcard>");
messages = JavacMessages.instance(context);
diags = JCDiagnostic.Factory.instance(context);
noWarnings = new Warner(null);
+ Options options = Options.instance(context);
+ allowPrimitiveClasses = Feature.PRIMITIVE_CLASSES.allowedInSource(source) && options.isSet("enablePrimitiveClasses");
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="bounds">
/**
typarams1.add(t2);
changed |= actual != t2;
formals = formals.tail;
}
if (outer1 == outer && !changed) return t;
- else return new ClassType(outer1, typarams1.toList(), t.tsym, t.getMetadata()) {
+ else return new ClassType(outer1, typarams1.toList(), t.tsym, t.getMetadata(), t.getFlavor()) {
@Override
protected boolean needsStripping() {
return true;
}
};
*/
public boolean isConvertible(Type t, Type s, Warner warn) {
if (t.hasTag(ERROR)) {
return true;
}
+
+ if (allowPrimitiveClasses) {
+ boolean tValue = t.isPrimitiveClass();
+ boolean sValue = s.isPrimitiveClass();
+ if (tValue != sValue) {
+ return tValue ?
+ isSubtype(t.referenceProjection(), s) :
+ !t.hasTag(BOT) && isSubtype(t, s.referenceProjection());
+ }
+ }
+
boolean tPrimitive = t.isPrimitive();
boolean sPrimitive = s.isPrimitive();
if (tPrimitive == sPrimitive) {
return isSubtypeUnchecked(t, s, warn);
}
}
if (abstracts.isEmpty()) {
//t must define a suitable non-generic method
throw failure("not.a.functional.intf.1", origin,
diags.fragment(Fragments.NoAbstracts(Kinds.kindName(origin), origin)));
- } else if (abstracts.size() == 1) {
- return new FunctionDescriptor(abstracts.first());
+ }
+ FunctionDescriptor descRes;
+ if (abstracts.size() == 1) {
+ descRes = new FunctionDescriptor(abstracts.first());
} else { // size > 1
- FunctionDescriptor descRes = mergeDescriptors(origin, abstracts.toList());
+ descRes = mergeDescriptors(origin, abstracts.toList());
if (descRes == null) {
//we can get here if the functional interface is ill-formed
ListBuffer<JCDiagnostic> descriptors = new ListBuffer<>();
for (Symbol desc : abstracts) {
String key = desc.type.getThrownTypes().nonEmpty() ?
origin));
JCDiagnostic.MultilineDiagnostic incompatibleDescriptors =
new JCDiagnostic.MultilineDiagnostic(msg, descriptors.toList());
throw failure(incompatibleDescriptors);
}
- return descRes;
}
+ // an interface must be neither an identity interface nor a value interface to be functional.
+ List<Type> allInterfaces = closure(origin.type);
+ for (Type iface : allInterfaces) {
+ if (iface.isValueInterface()) {
+ throw failure("not.a.functional.intf.1", origin, diags.fragment(Fragments.ValueInterfaceNonfunctional));
+ }
+ if (iface.isIdentityInterface()) {
+ throw failure("not.a.functional.intf.1", origin, diags.fragment(Fragments.IdentityInterfaceNonfunctional));
+ }
+ }
+ return descRes;
}
/**
* Compute a synthetic type for the target descriptor given a list
* of override-equivalent methods in the functional interface type.
// Use anonymous class instead of lambda expression intentionally,
// because the variable `names` has modifier: final.
private Predicate<Symbol> bridgeFilter = new Predicate<Symbol>() {
public boolean test(Symbol t) {
return t.kind == MTH &&
- t.name != names.init &&
+ !names.isInitOrVNew(t.name) &&
t.name != names.clinit &&
(t.flags() & SYNTHETIC) == 0;
}
};
private boolean isSubtypeUncheckedInternal(Type t, Type s, boolean capture, Warner warn) {
if (t.hasTag(ARRAY) && s.hasTag(ARRAY)) {
if (((ArrayType)t).elemtype.isPrimitive()) {
return isSameType(elemtype(t), elemtype(s));
} else {
- return isSubtypeUncheckedInternal(elemtype(t), elemtype(s), false, warn);
+ // if T.ref <: S, then T[] <: S[]
+ Type es = elemtype(s);
+ Type et = elemtype(t);
+ if (allowPrimitiveClasses) {
+ if (et.isPrimitiveClass()) {
+ et = et.referenceProjection();
+ if (es.isPrimitiveClass())
+ es = es.referenceProjection(); // V <: V, surely
+ }
+ }
+ if (!isSubtypeUncheckedInternal(et, es, false, warn))
+ return false;
+ return true;
}
} else if (isSubtype(t, s, capture)) {
return true;
} else if (t.hasTag(TYPEVAR)) {
return isSubtypeUncheckedInternal(t.getUpperBound(), s, false, warn);
return t.hasTag(s.getTag());
case TYPEVAR:
return isSubtypeNoCapture(t.getUpperBound(), s);
case BOT:
return
- s.hasTag(BOT) || s.hasTag(CLASS) ||
+ s.hasTag(BOT) || (s.hasTag(CLASS) && (!allowPrimitiveClasses || !s.isPrimitiveClass())) ||
s.hasTag(ARRAY) || s.hasTag(TYPEVAR);
case WILDCARD: //we shouldn't be here - avoids crash (see 7034495)
case NONE:
return false;
default:
Type sup = asSuper(t, s.tsym);
if (sup == null) return false;
// If t is an intersection, sup might not be a class type
if (!sup.hasTag(CLASS)) return isSubtypeNoCapture(sup, s);
return sup.tsym == s.tsym
+ && (t.tsym != s.tsym || t.isReferenceProjection() == s.isReferenceProjection())
// Check type variable containment
&& (!s.isParameterized() || containsTypeRecursive(s, sup))
&& isSubtypeNoCapture(sup.getEnclosingType(),
s.getEnclosingType());
}
@Override
public Boolean visitArrayType(ArrayType t, Type s) {
if (s.hasTag(ARRAY)) {
if (t.elemtype.isPrimitive())
return isSameType(t.elemtype, elemtype(s));
- else
- return isSubtypeNoCapture(t.elemtype, elemtype(s));
+ else {
+ // if T.ref <: S, then T[] <: S[]
+ Type es = elemtype(s);
+ Type et = elemtype(t);
+ if (allowPrimitiveClasses && et.isPrimitiveClass()) {
+ et = et.referenceProjection();
+ if (es.isPrimitiveClass())
+ es = es.referenceProjection(); // V <: V, surely
+ }
+ return isSubtypeNoCapture(et, es);
+ }
}
if (s.hasTag(CLASS)) {
Name sname = s.tsym.getQualifiedName();
return sname == names.java_lang_Object
return false;
}
return tMap.isEmpty();
}
return t.tsym == s.tsym
- && visit(t.getEnclosingType(), s.getEnclosingType())
+ && t.isReferenceProjection() == s.isReferenceProjection()
+ && visit(getEnclosingType(t), getEnclosingType(s))
&& containsTypeEquivalent(t.getTypeArguments(), s.getTypeArguments());
}
+ // where
+ private Type getEnclosingType(Type t) {
+ Type et = t.getEnclosingType();
+ if (et.isReferenceProjection()) {
+ et = et.valueProjection();
+ }
+ return et;
+ }
@Override
public Boolean visitArrayType(ArrayType t, Type s) {
if (t == s)
return true;
public Boolean visitWildcardType(WildcardType t, Type s) {
if (s.isPartial())
return containedBy(s, t);
else {
// debugContainsType(t, s);
+
+ // ----------------------------------- Unspecified behavior ----------------
+
+ /* If a primitive class V implements an interface I, then does "? extends I" contain V?
+ It seems widening must be applied here to answer yes to compile some common code
+ patterns.
+ */
+
+ // ---------------------------------------------------------------------------
return isSameWildcard(t, s)
|| isCaptureOf(s, t)
|| ((t.isExtendsBound() || isSubtypeNoCapture(wildLowerBound(t), wildLowerBound(s))) &&
(t.isSuperBound() || isSubtypeNoCapture(wildUpperBound(s), wildUpperBound(t))));
}
}
return result;
}
// where
private boolean areDisjoint(ClassSymbol ts, ClassSymbol ss) {
- if (isSubtype(erasure(ts.type), erasure(ss.type))) {
+ if (isSubtype(erasure(ts.type.referenceProjectionOrSelf()), erasure(ss.type))) {
return false;
}
// if both are classes or both are interfaces, shortcut
if (ts.isInterface() == ss.isInterface() && isSubtype(erasure(ss.type), erasure(ts.type))) {
return false;
return isCastable(wildUpperBound(t), s, warnStack.head);
}
@Override
public Boolean visitClassType(ClassType t, Type s) {
- if (s.hasTag(ERROR) || s.hasTag(BOT))
+ if (s.hasTag(ERROR) || (s.hasTag(BOT) && (!allowPrimitiveClasses || !t.isPrimitiveClass())))
return true;
if (s.hasTag(TYPEVAR)) {
if (isCastable(t, s.getUpperBound(), noWarnings)) {
warnStack.head.warn(LintCategory.UNCHECKED);
visitCompoundType((ClassType)s, t, true) :
visitCompoundType(t, s, false);
}
if (s.hasTag(CLASS) || s.hasTag(ARRAY)) {
+ if (allowPrimitiveClasses) {
+ if (t.isPrimitiveClass()) {
+ // (s) Value ? == (s) Value.ref
+ t = t.referenceProjection();
+ }
+ if (s.isPrimitiveClass()) {
+ // (Value) t ? == (Value.ref) t
+ s = s.referenceProjection();
+ }
+ }
boolean upcast;
if ((upcast = isSubtype(erasure(t), erasure(s)))
|| isSubtype(erasure(s), erasure(t))) {
if (!upcast && s.hasTag(ARRAY)) {
if (!isReifiable(s))
* Caveat Emptor: Since javac represents the class of all arrays with a singleton
* symbol Symtab.arrayClass, which by being a singleton cannot hold any discriminant,
* this method could yield surprising answers when invoked on arrays. For example when
* invoked with t being byte [] and sym being t.sym itself, asSuper would answer null.
*
+ * Further caveats in Valhalla: There are two "hazards" we need to watch out for when using
+ * this method.
+ *
+ * 1. Since Foo.ref and Foo.val share the same symbol, that of Foo.class, a call to
+ * asSuper(Foo.ref.type, Foo.val.type.tsym) would return non-null. This MAY NOT BE correct
+ * depending on the call site. Foo.val is NOT a super type of Foo.ref either in the language
+ * model or in the VM's world view. An example of such an hazardous call used to exist in
+ * Gen.visitTypeCast. When we emit code for (Foo) Foo.ref.instance a check for whether we
+ * really need the cast cannot/shouldn't be gated on
+ *
+ * asSuper(tree.expr.type, tree.clazz.type.tsym) == null)
+ *
+ * but use !types.isSubtype(tree.expr.type, tree.clazz.type) which operates in terms of
+ * types. When we operate in terms of symbols, there is a loss of type information leading
+ * to a hazard. Whether a call to asSuper should be transformed into a isSubtype call is
+ * tricky. isSubtype returns just a boolean while asSuper returns richer information which
+ * may be required at the call site. Also where the concerned symbol corresponds to a
+ * generic class, an asSuper call cannot be conveniently rewritten as an isSubtype call
+ * (see that asSuper(ArrayList<String>.type, List<T>.tsym) != null while
+ * isSubType(ArrayList<String>.type, List<T>.type) is false;) So care needs to be exercised.
+ *
+ * 2. Given a primitive class Foo, a call to asSuper(Foo.type, SuperclassOfFoo.tsym) and/or
+ * a call to asSuper(Foo.type, SuperinterfaceOfFoo.tsym) would answer null. In many places
+ * that is NOT what we want. An example of such a hazardous call used to occur in
+ * Attr.visitForeachLoop when checking to make sure the for loop's control variable of a type
+ * that implements Iterable: viz: types.asSuper(exprType, syms.iterableType.tsym);
+ * These hazardous calls should be rewritten as
+ * types.asSuper(exprType.referenceProjectionOrSelf(), syms.iterableType.tsym); instead.
+ *
* @param t a type
* @param sym a symbol
*/
public Type asSuper(Type t, Symbol sym) {
/* Some examples:
* (c.s.s.d.AttributeTree.ValueKind, Enum) => Enum<c.s.s.d.AttributeTree.ValueKind>
* (c.s.s.t.ExpressionTree, c.s.s.t.Tree) => c.s.s.t.Tree
* (j.u.List<capture#160 of ? extends c.s.s.d.DocTree>, Iterable) =>
* Iterable<capture#160 of ? extends c.s.s.d.DocTree>
*/
+
+ if (allowPrimitiveClasses && t.isPrimitiveClass()) {
+ // No man may be an island, but the bell tolls for a value.
+ return t.tsym == sym ? t : null;
+ }
+
if (sym.type == syms.objectType) { //optimization
return syms.objectType;
}
return asSuper.visit(t, sym);
}
*
* @param t a type
* @param sym a symbol
*/
public Type memberType(Type t, Symbol sym) {
- return (sym.flags() & STATIC) != 0
- ? sym.type
- : memberType.visit(t, sym);
+
+ if ((sym.flags() & STATIC) != 0)
+ return sym.type;
+
+ /* If any primitive class types are involved, switch over to the reference universe,
+ where the hierarchy is navigable. V and V.ref have identical membership
+ with no bridging needs.
+ */
+ if (allowPrimitiveClasses && t.isPrimitiveClass())
+ t = t.referenceProjection();
+
+ return memberType.visit(t, sym);
}
// where
private SimpleVisitor<Type,Symbol> memberType = new SimpleVisitor<Type,Symbol>() {
public Type visitType(Type t, Symbol sym) {
return combineMetadata(erased, t);
}
@Override
public Type visitClassType(ClassType t, Boolean recurse) {
- Type erased = t.tsym.erasure(Types.this);
- if (recurse) {
- erased = new ErasedClassType(erased.getEnclosingType(),erased.tsym,
- t.dropMetadata(Annotations.class).getMetadata());
- return erased;
- } else {
- return combineMetadata(erased, t);
+ // erasure(projection(primitive)) = projection(erasure(primitive))
+ Type erased = eraseClassType(t, recurse);
+ if (erased.hasTag(CLASS) && t.flavor != erased.getFlavor()) {
+ erased = new ClassType(erased.getEnclosingType(),
+ List.nil(), erased.tsym,
+ erased.getMetadata(), t.flavor);
+ }
+ return erased;
+ }
+ // where
+ private Type eraseClassType(ClassType t, Boolean recurse) {
+ Type erased = t.tsym.erasure(Types.this);
+ if (recurse) {
+ erased = new ErasedClassType(erased.getEnclosingType(), erased.tsym,
+ t.dropMetadata(Annotations.class).getMetadata());
+ return erased;
+ } else {
+ return combineMetadata(erased, t);
+ }
}
- }
@Override
public Type visitTypeVar(TypeVar t, Boolean recurse) {
Type erased = erasure(t.getUpperBound(), recurse);
return combineMetadata(erased, t);
@Override
public Type visitClassType(ClassType t, Void ignored) {
Type outer1 = classBound(t.getEnclosingType());
if (outer1 != t.getEnclosingType())
return new ClassType(outer1, t.getTypeArguments(), t.tsym,
- t.getMetadata());
+ t.getMetadata(), t.getFlavor());
else
return t;
}
@Override
}
Assert.check(act1.isEmpty() && act2.isEmpty() && typarams.isEmpty());
// There is no spec detailing how type annotations are to
// be inherited. So set it to noAnnotations for now
return new ClassType(class1.getEnclosingType(), merged.toList(),
- class1.tsym);
+ class1.tsym, List.nil(), class1.getFlavor());
}
/**
* Return the minimum type of a closure, a compound type if no
* unique minimum exists.
if (!currentA.isEmpty() || !currentT.isEmpty() || !currentS.isEmpty())
return erasure(t); // some "rare" type involved
if (captured)
return new ClassType(cls.getEnclosingType(), S, cls.tsym,
- cls.getMetadata());
+ cls.getMetadata(), cls.getFlavor());
else
return t;
}
// where
public List<Type> freshTypeVariables(List<Type> types) {
* A wrapper for a type that allows use in sets.
*/
public static class UniqueType {
public final Type type;
final Types types;
+ private boolean encodeTypeSig;
- public UniqueType(Type type, Types types) {
+ public UniqueType(Type type, Types types, boolean encodeTypeSig) {
this.type = type;
this.types = types;
+ this.encodeTypeSig = encodeTypeSig;
+ }
+
+ public UniqueType(Type type, Types types) {
+ this(type, types, true);
}
public int hashCode() {
return types.hashCode(type);
}
public boolean equals(Object obj) {
return (obj instanceof UniqueType uniqueType) &&
types.isSameType(type, uniqueType.type);
}
+ public boolean encodeTypeSig() {
+ return encodeTypeSig;
+ }
+
public String toString() {
return type.toString();
}
}
break;
case CLASS:
if (type.isCompound()) {
reportIllegalSignature(type);
}
- append('L');
+ if (types.allowPrimitiveClasses && type.isPrimitiveClass())
+ append('Q');
+ else
+ append('L');
assembleClassSig(type);
append(';');
break;
case ARRAY:
ArrayType at = (ArrayType) type;
< prev index next >