< prev index next >

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java

Print this page
@@ -141,11 +141,10 @@
          fileManager = context.get(JavaFileManager.class);
  
          source = Source.instance(context);
          target = Target.instance(context);
          warnOnAnyAccessToMembers = options.isSet("warnOnAccessToMembers");
- 
          Target target = Target.instance(context);
          syntheticNameChar = target.syntheticNameChar();
  
          profile = Profile.instance(context);
          preview = Preview.instance(context);

@@ -506,11 +505,11 @@
  
      public void removeCompiled(ClassSymbol csym) {
          compiled.remove(Pair.of(csym.packge().modle, csym.flatname));
      }
  
- /* *************************************************************************
+     /* *************************************************************************
   * Type Checking
   **************************************************************************/
  
      /**
       * A check context is an object that can be used to perform compatibility

@@ -612,10 +611,15 @@
      Type checkType(final DiagnosticPosition pos, final Type found, final Type req, final CheckContext checkContext) {
          final InferenceContext inferenceContext = checkContext.inferenceContext();
          if (inferenceContext.free(req) || inferenceContext.free(found)) {
              inferenceContext.addFreeTypeListener(List.of(req, found),
                      solvedContext -> checkType(pos, solvedContext.asInstType(found), solvedContext.asInstType(req), checkContext));
+         } else {
+             if (found.hasTag(CLASS)) {
+                 if (inferenceContext != infer.emptyContext)
+                     checkParameterizationByPrimitiveClass(pos, found);
+             }
          }
          if (req.hasTag(ERROR))
              return req;
          if (req.hasTag(NONE))
              return found;

@@ -742,27 +746,89 @@
              return (t.hasTag(TYPEVAR))
                                      ? diags.fragment(Fragments.TypeParameter(t))
                                      : t;
          }
  
+     void checkConstraintsOfValueClass(DiagnosticPosition pos, ClassSymbol c) {
+         for (Type st : types.closure(c.type)) {
+             if (st == null || st.tsym == null || st.tsym.kind == ERR)
+                 continue;
+             if  (st.tsym == syms.objectType.tsym || st.tsym == syms.recordType.tsym || st.isInterface())
+                 continue;
+             if (!st.tsym.isAbstract()) {
+                 if (c != st.tsym) {
+                     log.error(pos, Errors.ConcreteSupertypeForValueClass(c, st));
+                 }
+                 continue;
+             }
+             // dealing with an abstract value or value super class below.
+             Fragment fragment = c.isAbstract() && c.isValueClass() && c == st.tsym ? Fragments.AbstractValueClass(c) : Fragments.SuperclassOfValueClass(c, st);
+             if ((st.tsym.flags() & HASINITBLOCK) != 0) {
+                 log.error(pos, Errors.SuperClassDeclaresInitBlock(fragment));
+             }
+             // No instance fields and no arged constructors both mean inner classes
+             // cannot be super classes for primitive classes.
+             Type encl = st.getEnclosingType();
+             if (encl != null && encl.hasTag(CLASS)) {
+                 log.error(pos, Errors.SuperClassCannotBeInner(fragment));
+             }
+             for (Symbol s : st.tsym.members().getSymbols(NON_RECURSIVE)) {
+                 switch (s.kind) {
+                 case VAR:
+                     if ((s.flags() & STATIC) == 0) {
+                         log.error(pos, Errors.SuperFieldNotAllowed(s, fragment));
+                     }
+                     break;
+                 case MTH:
+                     if ((s.flags() & (SYNCHRONIZED | STATIC)) == SYNCHRONIZED) {
+                         log.error(pos, Errors.SuperMethodCannotBeSynchronized(s, c, st));
+                     } else if (s.isConstructor()) {
+                         MethodSymbol m = (MethodSymbol)s;
+                         if (m.getParameters().size() > 0) {
+                             log.error(pos, Errors.SuperConstructorCannotTakeArguments(m, fragment));
+                         } else if (m.getTypeParameters().size() > 0) {
+                             log.error(pos, Errors.SuperConstructorCannotBeGeneric(m, fragment));
+                         } else if (m.type.getThrownTypes().size() > 0) {
+                             log.error(pos, Errors.SuperConstructorCannotThrow(m, fragment));
+                         } else if (protection(m.flags()) > protection(m.owner.flags())) {
+                             log.error(pos, Errors.SuperConstructorAccessRestricted(m, fragment));
+                         } else if ((m.flags() & EMPTYNOARGCONSTR) == 0) {
+                                 log.error(pos, Errors.SuperNoArgConstructorMustBeEmpty(m, fragment));
+                         }
+                     }
+                     break;
+                 }
+             }
+         }
+     }
+ 
      /** Check that type is a valid qualifier for a constructor reference expression
       */
-     Type checkConstructorRefType(DiagnosticPosition pos, Type t) {
-         t = checkClassOrArrayType(pos, t);
+     Type checkConstructorRefType(JCExpression expr, Type t) {
+         t = checkClassOrArrayType(expr, t);
          if (t.hasTag(CLASS)) {
              if ((t.tsym.flags() & (ABSTRACT | INTERFACE)) != 0) {
-                 log.error(pos, Errors.AbstractCantBeInstantiated(t.tsym));
+                 log.error(expr, Errors.AbstractCantBeInstantiated(t.tsym));
                  t = types.createErrorType(t);
              } else if ((t.tsym.flags() & ENUM) != 0) {
-                 log.error(pos, Errors.EnumCantBeInstantiated);
+                 log.error(expr, Errors.EnumCantBeInstantiated);
                  t = types.createErrorType(t);
              } else {
-                 t = checkClassType(pos, t, true);
+                 // Projection types may not be mentioned in constructor references
+                 if (expr.hasTag(SELECT)) {
+                     JCFieldAccess fieldAccess = (JCFieldAccess) expr;
+                     if (fieldAccess.selected.type.isPrimitiveClass() &&
+                             (fieldAccess.name == names.ref || fieldAccess.name == names.val)) {
+                         log.error(expr, Errors.ProjectionCantBeInstantiated);
+                         t = types.createErrorType(t);
+                     }
+                 }
+                 t = checkClassType(expr, t, true);
              }
          } else if (t.hasTag(ARRAY)) {
              if (!types.isReifiable(((ArrayType)t).elemtype)) {
-                 log.error(pos, Errors.GenericArrayCreation);
+                 log.error(expr, Errors.GenericArrayCreation);
                  t = types.createErrorType(t);
              }
          }
          return t;
      }

@@ -789,29 +855,56 @@
  
      /** Check that type is a reference type, i.e. a class, interface or array type
       *  or a type variable.
       *  @param pos           Position to be used for error reporting.
       *  @param t             The type to be checked.
+      *  @param primitiveClassOK       If false, a primitive class does not qualify
       */
-     Type checkRefType(DiagnosticPosition pos, Type t) {
-         if (t.isReference())
+     Type checkRefType(DiagnosticPosition pos, Type t, boolean primitiveClassOK) {
+         if (t.isReference() && (primitiveClassOK || !t.isPrimitiveClass()))
              return t;
          else
              return typeTagError(pos,
                                  diags.fragment(Fragments.TypeReqRef),
                                  t);
      }
  
+     /** Check that type is an identity type, i.e. not a primitive/value type
+      *  nor its reference projection. When not discernible statically,
+      *  give it the benefit of doubt and defer to runtime.
+      *
+      *  @param pos           Position to be used for error reporting.
+      *  @param t             The type to be checked.
+      */
+     Type checkIdentityType(DiagnosticPosition pos, Type t) {
+ 
+         if (t.isPrimitive() || t.isValueClass() || t.isValueInterface() || t.isReferenceProjection())
+             return typeTagError(pos,
+                     diags.fragment(Fragments.TypeReqIdentity),
+                     t);
+ 
+         return t;
+     }
+ 
+     /** Check that type is a reference type, i.e. a class, interface or array type
+      *  or a type variable.
+      *  @param pos           Position to be used for error reporting.
+      *  @param t             The type to be checked.
+      */
+     Type checkRefType(DiagnosticPosition pos, Type t) {
+         return checkRefType(pos, t, true);
+     }
+ 
      /** Check that each type is a reference type, i.e. a class, interface or array type
       *  or a type variable.
       *  @param trees         Original trees, used for error reporting.
       *  @param types         The types to be checked.
       */
      List<Type> checkRefTypes(List<JCExpression> trees, List<Type> types) {
          List<JCExpression> tl = trees;
          for (List<Type> l = types; l.nonEmpty(); l = l.tail) {
-             l.head = checkRefType(tl.head.pos(), l.head);
+             l.head = checkRefType(tl.head.pos(), l.head, false);
              tl = tl.tail;
          }
          return types;
      }
  

@@ -843,10 +936,59 @@
              return false;
          } else
              return true;
      }
  
+     void checkParameterizationByPrimitiveClass(DiagnosticPosition pos, Type t) {
+         parameterizationByPrimitiveClassChecker.visit(t, pos);
+     }
+ 
+     /** parameterizationByPrimitiveClassChecker: A type visitor that descends down the given type looking for instances of primitive classes
+      *  being used as type arguments and issues error against those usages.
+      */
+     private final Types.SimpleVisitor<Void, DiagnosticPosition> parameterizationByPrimitiveClassChecker =
+             new Types.SimpleVisitor<Void, DiagnosticPosition>() {
+ 
+         @Override
+         public Void visitType(Type t, DiagnosticPosition pos) {
+             return null;
+         }
+ 
+         @Override
+         public Void visitClassType(ClassType t, DiagnosticPosition pos) {
+             for (Type targ : t.allparams()) {
+                 if (targ.isPrimitiveClass()) {
+                     log.error(pos, Errors.GenericParameterizationWithPrimitiveClass(t));
+                 }
+                 visit(targ, pos);
+             }
+             return null;
+         }
+ 
+         @Override
+         public Void visitTypeVar(TypeVar t, DiagnosticPosition pos) {
+              return null;
+         }
+ 
+         @Override
+         public Void visitCapturedType(CapturedType t, DiagnosticPosition pos) {
+             return null;
+         }
+ 
+         @Override
+         public Void visitArrayType(ArrayType t, DiagnosticPosition pos) {
+             return visit(t.elemtype, pos);
+         }
+ 
+         @Override
+         public Void visitWildcardType(WildcardType t, DiagnosticPosition pos) {
+             return visit(t.type, pos);
+         }
+     };
+ 
+ 
+ 
      /** Check that usage of diamond operator is correct (i.e. diamond should not
       * be used with non-generic classes or in anonymous class creation expressions)
       */
      Type checkDiamond(JCNewClass tree, Type t) {
          if (!TreeInfo.isDiamond(tree) ||

@@ -991,11 +1133,48 @@
              log.error(pos, Errors.CantInferLocalVarType(name, Fragments.LocalCantInferVoid));
              return types.createErrorType(t);
          }
  
          //upward project the initializer type
-         return types.upward(t, types.captures(t));
+         Type varType = types.upward(t, types.captures(t));
+         if (varType.hasTag(CLASS)) {
+             checkParameterizationByPrimitiveClass(pos, varType);
+         }
+         return varType;
+     }
+ 
+     public void checkForSuspectClassLiteralComparison(
+             final JCBinary tree,
+             final Type leftType,
+             final Type rightType) {
+ 
+         if (lint.isEnabled(LintCategory.MIGRATION)) {
+             if (isInvocationOfGetClass(tree.lhs) && isClassOfSomeInterface(rightType) ||
+                     isInvocationOfGetClass(tree.rhs) && isClassOfSomeInterface(leftType)) {
+                 log.warning(LintCategory.MIGRATION, tree.pos(), Warnings.GetClassComparedWithInterface);
+             }
+         }
+     }
+     //where
+     private boolean isClassOfSomeInterface(Type someClass) {
+         if (someClass.tsym.flatName() == names.java_lang_Class) {
+             List<Type> arguments = someClass.getTypeArguments();
+             if (arguments.length() == 1) {
+                 return arguments.head.isInterface();
+             }
+         }
+         return false;
+     }
+     //where
+     private boolean isInvocationOfGetClass(JCExpression tree) {
+         tree = TreeInfo.skipParens(tree);
+         if (tree.hasTag(APPLY)) {
+             JCMethodInvocation apply = (JCMethodInvocation)tree;
+             MethodSymbol msym = (MethodSymbol)TreeInfo.symbol(apply.meth);
+             return msym.name == names.getClass && msym.implementedIn(syms.objectType.tsym, types) != null;
+         }
+         return false;
      }
  
      Type checkMethod(final Type mtype,
              final Symbol sym,
              final Env<AttrContext> env,

@@ -1189,12 +1368,16 @@
                  mask = ReceiverParamFlags;
              else if (sym.owner.kind != TYP)
                  mask = LocalVarFlags;
              else if ((sym.owner.flags_field & INTERFACE) != 0)
                  mask = implicit = InterfaceVarFlags;
-             else
+             else {
                  mask = VarFlags;
+                 if (sym.owner.type.isValueClass() && (flags & STATIC) == 0) {
+                     implicit |= FINAL;
+                 }
+             }
              break;
          case MTH:
              if (sym.name == names.init) {
                  if ((sym.owner.flags_field & ENUM) != 0) {
                      // enum constructors cannot be declared public or

@@ -1216,13 +1399,16 @@
                      }
                  } else {
                      mask = implicit = InterfaceMethodFlags;
                  }
              } else if ((sym.owner.flags_field & RECORD) != 0) {
-                 mask = RecordMethodFlags;
+                 mask = ((sym.owner.flags_field & VALUE_CLASS) != 0 && (flags & Flags.STATIC) == 0) ?
+                         RecordMethodFlags & ~SYNCHRONIZED : RecordMethodFlags;
              } else {
-                 mask = MethodFlags;
+                 // value objects do not have an associated monitor/lock
+                 mask = ((sym.owner.flags_field & VALUE_CLASS) != 0 && (flags & Flags.STATIC) == 0) ?
+                         MethodFlags & ~SYNCHRONIZED : MethodFlags;
              }
              if ((flags & STRICTFP) != 0) {
                  warnOnExplicitStrictfp(pos);
              }
              // Imply STRICTFP if owner has STRICTFP set.

@@ -1235,11 +1421,11 @@
                      (sym.isDirectlyOrIndirectlyLocal() && (flags & ANNOTATION) != 0)) {
                  boolean implicitlyStatic = !sym.isAnonymous() &&
                          ((flags & RECORD) != 0 || (flags & ENUM) != 0 || (flags & INTERFACE) != 0);
                  boolean staticOrImplicitlyStatic = (flags & STATIC) != 0 || implicitlyStatic;
                  // local statics are allowed only if records are allowed too
-                 mask = staticOrImplicitlyStatic && allowRecords && (flags & ANNOTATION) == 0 ? StaticLocalFlags : LocalClassFlags;
+                 mask = staticOrImplicitlyStatic && allowRecords && (flags & ANNOTATION) == 0 ? ExtendedStaticLocalClassFlags : ExtendedLocalClassFlags;
                  implicit = implicitlyStatic ? STATIC : implicit;
              } else if (sym.owner.kind == TYP) {
                  // statics in inner classes are allowed only if records are allowed too
                  mask = ((flags & STATIC) != 0) && allowRecords && (flags & ANNOTATION) == 0 ? ExtendedMemberStaticClassFlags : ExtendedMemberClassFlags;
                  if (sym.owner.owner.kind == PCK ||

@@ -1255,12 +1441,12 @@
              }
              // Interfaces are always ABSTRACT
              if ((flags & INTERFACE) != 0) implicit |= ABSTRACT;
  
              if ((flags & ENUM) != 0) {
-                 // enums can't be declared abstract, final, sealed or non-sealed
-                 mask &= ~(ABSTRACT | FINAL | SEALED | NON_SEALED);
+                 // enums can't be declared abstract, final, sealed or non-sealed or primitive/value
+                 mask &= ~(ABSTRACT | FINAL | SEALED | NON_SEALED | PRIMITIVE_CLASS | VALUE_CLASS);
                  implicit |= implicitEnumFinalFlag(tree);
              }
              if ((flags & RECORD) != 0) {
                  // records can't be declared abstract
                  mask &= ~ABSTRACT;

@@ -1269,10 +1455,21 @@
              if ((flags & STRICTFP) != 0) {
                  warnOnExplicitStrictfp(pos);
              }
              // Imply STRICTFP if owner has STRICTFP set.
              implicit |= sym.owner.flags_field & STRICTFP;
+ 
+             // primitive classes are implicitly final value classes.
+             if ((flags & PRIMITIVE_CLASS) != 0)
+                 implicit |= VALUE_CLASS | FINAL;
+ 
+             // concrete value classes are implicitly final
+             if ((flags & (ABSTRACT | INTERFACE | VALUE_CLASS)) == VALUE_CLASS)
+                 implicit |= FINAL;
+ 
+             // TYPs can't be declared synchronized
+             mask &= ~SYNCHRONIZED;
              break;
          default:
              throw new AssertionError();
          }
          long illegal = flags & ExtendedStandardFlags & ~mask;

@@ -1297,21 +1494,25 @@
                                  STATIC | PRIVATE,
                                  DEFAULT)
                   &&
                   checkDisjoint(pos, flags,
                                 ABSTRACT | INTERFACE,
-                                FINAL | NATIVE | SYNCHRONIZED)
+                                FINAL | NATIVE | SYNCHRONIZED | PRIMITIVE_CLASS)
+                  &&
+                  checkDisjoint(pos, flags,
+                         IDENTITY_TYPE,
+                         PRIMITIVE_CLASS | VALUE_CLASS)
                   &&
                   checkDisjoint(pos, flags,
                                 PUBLIC,
                                 PRIVATE | PROTECTED)
                   &&
                   checkDisjoint(pos, flags,
                                 PRIVATE,
                                 PUBLIC | PROTECTED)
                   &&
-                  checkDisjoint(pos, flags,
+                  checkDisjoint(pos, (flags | implicit), // complain against volatile & implcitly final entities too.
                                 FINAL,
                                 VOLATILE)
                   &&
                   (sym.kind == TYP ||
                    checkDisjoint(pos, flags,

@@ -1496,14 +1697,17 @@
              }
          }
  
          public void visitSelectInternal(JCFieldAccess tree) {
              if (tree.type.tsym.isStatic() &&
-                 tree.selected.type.isParameterized()) {
+                 tree.selected.type.isParameterized() &&
+                     (tree.name != names.ref || !tree.type.isReferenceProjection())) {
                  // The enclosing type is not a class, so we are
                  // looking at a static member type.  However, the
                  // qualifying expression is parameterized.
+                 // Tolerate the pseudo-select V.ref: V<T>.ref will be static if V<T> is and
+                 // should not be confused as selecting a static member of a parameterized type.
                  log.error(tree.pos(), Errors.CantSelectStaticClassFromParamType);
              } else {
                  // otherwise validate the rest of the expression
                  tree.selected.accept(this);
              }

@@ -1826,10 +2030,19 @@
                                                            asFlagSet(other.flags() & AccessFlags)));
              m.flags_field |= BAD_OVERRIDE;
              return;
          }
  
+         if (origin.isValueClass() && other.owner == syms.objectType.tsym && m.type.getParameterTypes().size() == 0) {
+             if (m.name == names.finalize) {
+                 log.error(TreeInfo.diagnosticPositionFor(m, tree),
+                         Errors.ValueClassMayNotOverride(m.name));
+                 m.flags_field |= BAD_OVERRIDE;
+                 return;
+             }
+         }
+ 
          Type mt = types.memberType(origin.type, m);
          Type ot = types.memberType(origin.type, other);
          // Error if overriding result type is different
          // (or, in the case of generics mode, not a subtype) of
          // overridden result type. We have to rename any type parameters

@@ -2264,10 +2477,48 @@
              log.error(pos,
                        Errors.DoesNotOverrideAbstract(c, undef1, undef1.location()));
          }
      }
  
+     // A primitive class cannot contain a field of its own type either or indirectly.
+     void checkNonCyclicMembership(JCClassDecl tree) {
+         Assert.check((tree.sym.flags_field & LOCKED) == 0);
+         try {
+             tree.sym.flags_field |= LOCKED;
+             for (List<? extends JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
+                 if (l.head.hasTag(VARDEF)) {
+                     JCVariableDecl field = (JCVariableDecl) l.head;
+                     if (cyclePossible(field.sym)) {
+                         checkNonCyclicMembership((ClassSymbol) field.type.tsym, field.pos());
+                     }
+                 }
+             }
+         } finally {
+             tree.sym.flags_field &= ~LOCKED;
+         }
+ 
+     }
+     // where
+     private void checkNonCyclicMembership(ClassSymbol c, DiagnosticPosition pos) {
+         if ((c.flags_field & LOCKED) != 0) {
+             log.error(pos, Errors.CyclicPrimitiveClassMembership(c));
+             return;
+         }
+         try {
+             c.flags_field |= LOCKED;
+             for (Symbol fld : c.members().getSymbols(s -> s.kind == VAR && cyclePossible((VarSymbol) s), NON_RECURSIVE)) {
+                 checkNonCyclicMembership((ClassSymbol) fld.type.tsym, pos);
+             }
+         } finally {
+             c.flags_field &= ~LOCKED;
+         }
+     }
+         // where
+         private boolean cyclePossible(VarSymbol symbol) {
+             return (symbol.flags() & STATIC) == 0 && symbol.type.isPrimitiveClass();
+         }
+ 
      void checkNonCyclicDecl(JCClassDecl tree) {
          CycleChecker cc = new CycleChecker();
          cc.scan(tree);
          if (!cc.errorFound && !cc.partialCheck) {
              tree.sym.flags_field |= ACYCLIC;

@@ -2512,10 +2763,32 @@
              for (List<Type> m = supertypes; m != l; m = m.tail)
                  if (!checkCompatibleAbstracts(pos, l.head, m.head, c))
                      return;
          }
          checkCompatibleConcretes(pos, c);
+ 
+         boolean cIsValue = (c.tsym.flags() & VALUE_CLASS) != 0;
+         boolean cHasIdentity = (c.tsym.flags() & IDENTITY_TYPE) != 0;
+         Type identitySuper = null, valueSuper = null;
+         for (Type t : types.closure(c)) {
+             if (t != c) {
+                 if ((t.tsym.flags() & IDENTITY_TYPE) != 0)
+                     identitySuper = t;
+                 else if ((t.tsym.flags() & VALUE_CLASS) != 0)
+                     valueSuper = t;
+                 if (cIsValue &&  identitySuper != null) {
+                     log.error(pos, Errors.ValueTypeHasIdentitySuperType(c, identitySuper));
+                     break;
+                 } else if (cHasIdentity &&  valueSuper != null) {
+                     log.error(pos, Errors.IdentityTypeHasValueSuperType(c, valueSuper));
+                     break;
+                 } else if (identitySuper != null && valueSuper != null) {
+                     log.error(pos, Errors.MutuallyIncompatibleSupers(c, identitySuper, valueSuper));
+                     break;
+                 }
+             }
+         }
      }
  
      /** Check that all non-override equivalent methods accessible from 'site'
       *  are mutually compatible (JLS 8.4.8/9.4.1).
       *
< prev index next >