< prev index next >

src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java

Print this page
@@ -47,11 +47,10 @@
  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.*;

@@ -90,10 +89,11 @@
      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;

@@ -118,10 +118,12 @@
          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">
      /**

@@ -266,11 +268,11 @@
                      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;
                      }
                  };

@@ -597,10 +599,21 @@
       */
      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);
          }

@@ -759,14 +772,16 @@
              }
              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() ?

@@ -781,12 +796,22 @@
                                                                                         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.

@@ -951,11 +976,11 @@
          // 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;
              }
          };
  

@@ -1022,11 +1047,23 @@
          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);

@@ -1119,11 +1156,11 @@
                       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:

@@ -1186,10 +1223,11 @@
                  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());
              }

@@ -1197,12 +1235,21 @@
              @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

@@ -1418,13 +1465,22 @@
                              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;

@@ -1580,10 +1636,19 @@
              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))));
                  }

@@ -1669,11 +1734,11 @@
          }
          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;

@@ -1724,11 +1789,11 @@
                  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);

@@ -1743,10 +1808,20 @@
                              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))

@@ -2136,10 +2211,39 @@
       * 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:

@@ -2148,10 +2252,16 @@
           * (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);
      }

@@ -2278,13 +2388,22 @@
       *
       * @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) {

@@ -2433,19 +2552,30 @@
                  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);

@@ -2761,11 +2891,11 @@
              @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

@@ -3882,11 +4012,11 @@
              }
              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.

@@ -4442,11 +4572,11 @@
          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) {

@@ -4853,14 +4983,20 @@
       * 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);
          }

@@ -4868,10 +5004,14 @@
          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();
          }
  
      }

@@ -5106,11 +5246,14 @@
                      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 >