< prev index next >

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

Print this page
*** 182,10 ***
--- 182,11 ---
          deferredLintHandler = DeferredLintHandler.instance(context);
  
          allowModules = Feature.MODULES.allowedInSource(source);
          allowRecords = Feature.RECORDS.allowedInSource(source);
          allowSealed = Feature.SEALED_CLASSES.allowedInSource(source);
+         allowValueClasses = Feature.VALUE_CLASSES.allowedInSource(source);
      }
  
      /** Character for synthetic names
       */
      char syntheticNameChar;

*** 225,10 ***
--- 226,14 ---
  
      /** Are sealed classes allowed
       */
      private final boolean allowSealed;
  
+     /** Are value classes allowed
+      */
+     private final boolean allowValueClasses;
+ 
  /* *************************************************************************
   * Errors and Warnings
   **************************************************************************/
  
      Lint setLint(Lint newLint) {

*** 357,19 ***
          }
          log.error(pos, Errors.TypeFoundReq(found, required));
          return types.createErrorType(found instanceof Type type ? type : syms.errType);
      }
  
-     /** Report an error that symbol cannot be referenced before super
-      *  has been called.
-      *  @param pos        Position to be used for error reporting.
-      *  @param sym        The referenced symbol.
-      */
-     void earlyRefError(DiagnosticPosition pos, Symbol sym) {
-         log.error(pos, Errors.CantRefBeforeCtorCalled(sym));
-     }
- 
      /** Report duplicate declaration error.
       */
      void duplicateError(DiagnosticPosition pos, Symbol sym) {
          if (!sym.type.isErroneous()) {
              Symbol location = sym.location();
--- 362,10 ---

*** 766,10 ***
--- 762,35 ---
              return (t.hasTag(TYPEVAR))
                                      ? diags.fragment(Fragments.TypeParameter(t))
                                      : t;
          }
  
+     void checkConstraintsOfValueClass(JCClassDecl tree, ClassSymbol c) {
+         DiagnosticPosition pos = tree.pos();
+         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.
+             for (Symbol s : st.tsym.members().getSymbols(NON_RECURSIVE)) {
+                 if (s.kind == MTH) {
+                     if ((s.flags() & (SYNCHRONIZED | STATIC)) == SYNCHRONIZED) {
+                         log.error(pos, Errors.SuperClassMethodCannotBeSynchronized(s, c, st));
+                     }
+                     break;
+                 }
+             }
+         }
+     }
+ 
      /** Check that type is a valid qualifier for a constructor reference expression
       */
      Type checkConstructorRefType(DiagnosticPosition pos, Type t) {
          t = checkClassOrArrayType(pos, t);
          if (t.hasTag(CLASS)) {

*** 823,10 ***
--- 844,32 ---
              return typeTagError(pos,
                                  diags.fragment(Fragments.TypeReqRef),
                                  t);
      }
  
+     /** Check that type is an identity type, i.e. not a value type.
+      *  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.
+      */
+     void checkIdentityType(DiagnosticPosition pos, Type t) {
+         if (t.hasTag(TYPEVAR)) {
+             t = types.skipTypeVars(t, false);
+         }
+         if (t.isIntersection()) {
+             IntersectionClassType ict = (IntersectionClassType)t;
+             for (Type component : ict.getExplicitComponents()) {
+                 checkIdentityType(pos, component);
+             }
+             return;
+         }
+         if (t.isPrimitive() || (t.isValueClass() && !t.tsym.isAbstract()))
+             typeTagError(pos, diags.fragment(Fragments.TypeReqIdentity), t);
+     }
+ 
      /** 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.
       */

*** 1213,12 ***
                  mask = ReceiverParamFlags;
              else if (sym.owner.kind != TYP)
                  mask = LocalVarFlags;
              else if ((sym.owner.flags_field & INTERFACE) != 0)
                  mask = implicit = InterfaceVarFlags;
!             else
!                 mask = VarFlags;
              break;
          case MTH:
              if (sym.name == names.init) {
                  if ((sym.owner.flags_field & ENUM) != 0) {
                      // enum constructors cannot be declared public or
--- 1256,17 ---
                  mask = ReceiverParamFlags;
              else if (sym.owner.kind != TYP)
                  mask = LocalVarFlags;
              else if ((sym.owner.flags_field & INTERFACE) != 0)
                  mask = implicit = InterfaceVarFlags;
!             else {
!                 boolean isInstanceFieldOfValueClass = sym.owner.type.isValueClass() && (flags & STATIC) == 0;
+                 mask = !isInstanceFieldOfValueClass ? VarFlags : ExtendedVarFlags;
+                 if (isInstanceFieldOfValueClass) {
+                     implicit |= FINAL | STRICT;
+                 }
+             }
              break;
          case MTH:
              if (sym.name == names.init) {
                  if ((sym.owner.flags_field & ENUM) != 0) {
                      // enum constructors cannot be declared public or

*** 1240,13 ***
                      }
                  } else {
                      mask = implicit = InterfaceMethodFlags;
                  }
              } else if ((sym.owner.flags_field & RECORD) != 0) {
!                 mask = RecordMethodFlags;
              } else {
!                 mask = MethodFlags;
              }
              if ((flags & STRICTFP) != 0) {
                  warnOnExplicitStrictfp(pos);
              }
              // Imply STRICTFP if owner has STRICTFP set.
--- 1288,16 ---
                      }
                  } else {
                      mask = implicit = InterfaceMethodFlags;
                  }
              } else if ((sym.owner.flags_field & RECORD) != 0) {
!                 mask = ((sym.owner.flags_field & VALUE_CLASS) != 0 && (flags & Flags.STATIC) == 0) ?
+                         RecordMethodFlags & ~SYNCHRONIZED : RecordMethodFlags;
              } else {
!                 // 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.

*** 1259,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;
                  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 ||
--- 1310,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 ? 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 ||

*** 1278,13 ***
                  mask = ExtendedClassFlags;
              }
              // 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);
                  implicit |= implicitEnumFinalFlag(tree);
              }
              if ((flags & RECORD) != 0) {
                  // records can't be declared abstract
                  mask &= ~ABSTRACT;
--- 1329,17 ---
                  mask = ExtendedClassFlags;
              }
              // Interfaces are always ABSTRACT
              if ((flags & INTERFACE) != 0) implicit |= ABSTRACT;
  
+             if ((flags & (INTERFACE | VALUE_CLASS)) == 0) {
+                 implicit |= IDENTITY_TYPE;
+             }
+ 
              if ((flags & ENUM) != 0) {
!                 // enums can't be declared abstract, final, sealed or non-sealed or value
!                 mask &= ~(ABSTRACT | FINAL | SEALED | NON_SEALED | VALUE_CLASS);
                  implicit |= implicitEnumFinalFlag(tree);
              }
              if ((flags & RECORD) != 0) {
                  // records can't be declared abstract
                  mask &= ~ABSTRACT;

*** 1293,10 ***
--- 1348,18 ---
              if ((flags & STRICTFP) != 0) {
                  warnOnExplicitStrictfp(pos);
              }
              // Imply STRICTFP if owner has STRICTFP set.
              implicit |= sym.owner.flags_field & STRICTFP;
+ 
+             // 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;

*** 1323,19 ***
                   &&
                   checkDisjoint(pos, flags,
                                 ABSTRACT | INTERFACE,
                                 FINAL | NATIVE | SYNCHRONIZED)
                   &&
                   checkDisjoint(pos, flags,
                                 PUBLIC,
                                 PRIVATE | PROTECTED)
                   &&
                   checkDisjoint(pos, flags,
                                 PRIVATE,
                                 PUBLIC | PROTECTED)
                   &&
!                  checkDisjoint(pos, flags,
                                 FINAL,
                                 VOLATILE)
                   &&
                   (sym.kind == TYP ||
                    checkDisjoint(pos, flags,
--- 1386,23 ---
                   &&
                   checkDisjoint(pos, flags,
                                 ABSTRACT | INTERFACE,
                                 FINAL | NATIVE | SYNCHRONIZED)
                   &&
+                  checkDisjoint(pos, flags,
+                         INTERFACE,
+                         VALUE_CLASS)
+                  &&
                   checkDisjoint(pos, flags,
                                 PUBLIC,
                                 PRIVATE | PROTECTED)
                   &&
                   checkDisjoint(pos, flags,
                                 PRIVATE,
                                 PUBLIC | PROTECTED)
                   &&
!                  checkDisjoint(pos, (flags | implicit), // complain against volatile & implcitly final entities too.
                                 FINAL,
                                 VOLATILE)
                   &&
                   (sym.kind == TYP ||
                    checkDisjoint(pos, flags,

*** 1347,11 ***
                   && checkDisjoint(pos, flags,
                                  SEALED,
                             FINAL | NON_SEALED)
                   && checkDisjoint(pos, flags,
                                  SEALED,
!                                 ANNOTATION)) {
              // skip
          }
          return flags & (mask | ~ExtendedStandardFlags) | implicit;
      }
  
--- 1414,20 ---
                   && checkDisjoint(pos, flags,
                                  SEALED,
                             FINAL | NON_SEALED)
                   && checkDisjoint(pos, flags,
                                  SEALED,
!                                 ANNOTATION)
+                 && checkDisjoint(pos, flags,
+                                 VALUE_CLASS,
+                                 ANNOTATION)
+                 && checkDisjoint(pos, flags,
+                                 VALUE_CLASS,
+                                 NON_SEALED)
+                 && checkDisjoint(pos, flags,
+                                 VALUE_CLASS,
+                                 INTERFACE) ) {
              // skip
          }
          return flags & (mask | ~ExtendedStandardFlags) | implicit;
      }
  

*** 2592,10 ***
--- 2668,25 ---
              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;
+         Type identitySuper = null, valueSuper = null;
+         for (Type t : types.closure(c)) {
+             if (t != c) {
+                 if ((t.tsym.flags() & IDENTITY_TYPE) != 0 && (t.tsym.flags() & VALUE_BASED) == 0)
+                     identitySuper = t;
+                 else if ((t.tsym.flags() & VALUE_CLASS) != 0)
+                     valueSuper = t;
+                 if (cIsValue && identitySuper != null && identitySuper.tsym != syms.objectType.tsym) { // Object is special
+                     log.error(pos, Errors.ValueTypeHasIdentitySuperType(c, identitySuper));
+                     break;
+                 }
+             }
+         }
      }
  
      /** Check that all non-override equivalent methods accessible from 'site'
       *  are mutually compatible (JLS 8.4.8/9.4.1).
       *

*** 3932,14 ***
          // use LinkedHashMap so we generate errors deterministically
          Map<Symbol,Symbol> callMap = new LinkedHashMap<>();
  
          // enter each constructor this-call into the map
          for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
!             JCMethodInvocation app = TreeInfo.firstConstructorCall(l.head);
!             if (app == null) continue;
!             JCMethodDecl meth = (JCMethodDecl) l.head;
!             if (TreeInfo.name(app.meth) == names._this) {
                  callMap.put(meth.sym, TreeInfo.symbol(app.meth));
              } else {
                  meth.sym.flags_field |= ACYCLIC;
              }
          }
--- 4023,15 ---
          // use LinkedHashMap so we generate errors deterministically
          Map<Symbol,Symbol> callMap = new LinkedHashMap<>();
  
          // enter each constructor this-call into the map
          for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
!             if (!TreeInfo.isConstructor(l.head))
!                 continue;
!             JCMethodDecl meth = (JCMethodDecl)l.head;
!             JCMethodInvocation app = TreeInfo.findConstructorCall(meth);
+             if (app != null && TreeInfo.name(app.meth) == names._this) {
                  callMap.put(meth.sym, TreeInfo.symbol(app.meth));
              } else {
                  meth.sym.flags_field |= ACYCLIC;
              }
          }

*** 3968,10 ***
--- 4060,135 ---
              }
              ctor.flags_field |= ACYCLIC;
          }
      }
  
+ /* *************************************************************************
+  * Verify the proper placement of super()/this() calls.
+  *
+  *    - super()/this() may only appear in constructors
+  *    - There must be at most one super()/this() call per constructor
+  *    - The super()/this() call, if any, must be a top-level statement in the
+  *      constructor, i.e., not nested inside any other statement or block
+  *    - There must be no return statements prior to the super()/this() call
+  **************************************************************************/
+ 
+     void checkSuperInitCalls(JCClassDecl tree) {
+         new SuperThisChecker().check(tree);
+     }
+ 
+     private class SuperThisChecker extends TreeScanner {
+ 
+         // Match this scan stack: 1=JCMethodDecl, 2=JCExpressionStatement, 3=JCMethodInvocation
+         private static final int MATCH_SCAN_DEPTH = 3;
+ 
+         private boolean constructor;        // is this method a constructor?
+         private boolean firstStatement;     // at the first statement in method?
+         private JCReturn earlyReturn;       // first return prior to the super()/init(), if any
+         private Name initCall;              // whichever of "super" or "init" we've seen already
+         private int scanDepth;              // current scan recursion depth in method body
+ 
+         public void check(JCClassDecl classDef) {
+             scan(classDef.defs);
+         }
+ 
+         @Override
+         public void visitMethodDef(JCMethodDecl tree) {
+             Assert.check(!constructor);
+             Assert.check(earlyReturn == null);
+             Assert.check(initCall == null);
+             Assert.check(scanDepth == 1);
+ 
+             // Initialize state for this method
+             constructor = TreeInfo.isConstructor(tree);
+             try {
+ 
+                 // Scan method body
+                 if (tree.body != null) {
+                     firstStatement = true;
+                     for (List<JCStatement> l = tree.body.stats; l.nonEmpty(); l = l.tail) {
+                         scan(l.head);
+                         firstStatement = false;
+                     }
+                 }
+ 
+                 // Verify no 'return' seen prior to an explicit super()/this() call
+                 if (constructor && earlyReturn != null && initCall != null)
+                     log.error(earlyReturn.pos(), Errors.ReturnBeforeSuperclassInitialized);
+             } finally {
+                 firstStatement = false;
+                 constructor = false;
+                 earlyReturn = null;
+                 initCall = null;
+             }
+         }
+ 
+         @Override
+         public void scan(JCTree tree) {
+             scanDepth++;
+             try {
+                 super.scan(tree);
+             } finally {
+                 scanDepth--;
+             }
+         }
+ 
+         @Override
+         public void visitApply(JCMethodInvocation apply) {
+             do {
+ 
+                 // Is this a super() or this() call?
+                 Name methodName = TreeInfo.name(apply.meth);
+                 if (methodName != names._super && methodName != names._this)
+                     break;
+ 
+                 // super()/this() calls must only appear in a constructor
+                 if (!constructor) {
+                     log.error(apply.pos(), Errors.CallMustOnlyAppearInCtor);
+                     break;
+                 }
+ 
+                 // super()/this() calls must be a top level statement
+                 if (scanDepth != MATCH_SCAN_DEPTH) {
+                     log.error(apply.pos(), Errors.CtorCallsNotAllowedHere);
+                     break;
+                 }
+ 
+                 // super()/this() calls must not appear more than once
+                 if (initCall != null) {
+                     log.error(apply.pos(), Errors.RedundantSuperclassInit);
+                     break;
+                 }
+ 
+                 // valhalla is using this feature so commenting this code for now so that the
+                 // build doesn't depend on preview code
+                 // If super()/this() isn't first, require "statements before super()" feature
+                 /*if (!firstStatement) {
+                     preview.checkSourceLevel(apply.pos(), Feature.SUPER_INIT);
+                 }*/
+ 
+                 // We found a legitimate super()/this() call; remember it
+                 initCall = methodName;
+             } while (false);
+ 
+             // Proceed
+             super.visitApply(apply);
+         }
+ 
+         @Override
+         public void visitReturn(JCReturn tree) {
+             if (constructor && initCall == null && earlyReturn == null)
+                 earlyReturn = tree;             // we have seen a return but not (yet) a super()/this()
+             super.visitReturn(tree);
+         }
+ 
+         @Override
+         public void visitClassDef(JCClassDecl tree) {
+             // don't descend any further
+         }
+     }
+ 
  /* *************************************************************************
   * Miscellaneous
   **************************************************************************/
  
      /**
< prev index next >