< prev index next >

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

Print this page
*** 57,10 ***
--- 57,11 ---
  import com.sun.tools.javac.comp.ExhaustivenessComputer.RecordPattern;
  import com.sun.tools.javac.resources.CompilerProperties.Fragments;
  import static com.sun.tools.javac.tree.JCTree.Tag.*;
  import com.sun.tools.javac.util.JCDiagnostic.Fragment;
  import java.util.Arrays;
+ import java.util.function.Predicate;
  
  /** This pass implements dataflow analysis for Java programs though
   *  different AST visitor steps. Liveness analysis (see AliveAnalyzer) checks that
   *  every statement is reachable. Exception analysis (see FlowAnalyzer) ensures that
   *  every checked exception that is thrown is declared or caught.  Definite assignment analysis

*** 467,31 ***
                  brk.target = swtch;
                  scan(brk);
              }
          }
  
!         // Do something with all static or non-static field initializers and initialization blocks.
!         protected void forEachInitializer(JCClassDecl classDef, boolean isStatic, Consumer<? super JCTree> handler) {
              if (classDef == initScanClass)          // avoid infinite loops
                  return;
              JCClassDecl initScanClassPrev = initScanClass;
              initScanClass = classDef;
              try {
                  for (List<JCTree> defs = classDef.defs; defs.nonEmpty(); defs = defs.tail) {
                      JCTree def = defs.head;
  
!                     // Don't recurse into nested classes
-                     if (def.hasTag(CLASSDEF))
                          continue;
  
!                     /* we need to check for flags in the symbol too as there could be cases for which implicit flags are
-                      * represented in the symbol but not in the tree modifiers as they were not originally in the source
-                      * code
-                      */
-                     boolean isDefStatic = ((TreeInfo.flags(def) | (TreeInfo.symbolFor(def) == null ? 0 : TreeInfo.symbolFor(def).flags_field)) & STATIC) != 0;
-                     if (!def.hasTag(METHODDEF) && (isDefStatic == isStatic))
-                         handler.accept(def);
                  }
              } finally {
                  initScanClass = initScanClassPrev;
              }
          }
--- 468,48 ---
                  brk.target = swtch;
                  scan(brk);
              }
          }
  
!         /** a predicate that selects all static initializers */
!         static final Predicate<JCTree> STATIC_INITS = tree ->
+             switch (tree) {
+                 case JCVariableDecl vdecl -> vdecl.sym.isStatic();
+                 case JCBlock b -> b.isStatic();
+                 default -> false;
+             };
+ 
+         /** a predicate that selects early instance initializers */
+         static final Predicate<JCTree> EARLY_INSTANCE_INITS = tree ->
+                 tree instanceof JCVariableDecl vdecl && vdecl.sym.isStrictInstance();
+ 
+         /** a predicate that selects late instance initializers */
+         static final Predicate<JCTree> LATE_INSTANCE_INITS = tree ->
+                 switch (tree) {
+                     case JCVariableDecl vdecl -> !vdecl.sym.isStatic() && !vdecl.sym.isStrict();
+                     case JCBlock b -> !b.isStatic();
+                     default -> false;
+                 };
+ 
+         /** a predicate that all instance initializers */
+         static final Predicate<JCTree> INSTANCE_INITS = EARLY_INSTANCE_INITS.or(LATE_INSTANCE_INITS);
+ 
+         protected void forEachInitializer(JCClassDecl classDef, Predicate<? super JCTree> initFilter,
+                                           Consumer<? super JCTree> handler) {
              if (classDef == initScanClass)          // avoid infinite loops
                  return;
              JCClassDecl initScanClassPrev = initScanClass;
              initScanClass = classDef;
              try {
                  for (List<JCTree> defs = classDef.defs; defs.nonEmpty(); defs = defs.tail) {
                      JCTree def = defs.head;
  
!                     if (!initFilter.test(def)) {
                          continue;
+                     }
  
!                     handler.accept(def);
                  }
              } finally {
                  initScanClass = initScanClassPrev;
              }
          }

*** 563,17 ***
                          scan(l.head);
                      }
                  }
  
                  // process all the static initializers
!                 forEachInitializer(tree, true, def -> {
                      scanDef(def);
                      clearPendingExits(false);
                  });
  
                  // process all the instance initializers
!                 forEachInitializer(tree, false, def -> {
                      scanDef(def);
                      clearPendingExits(false);
                  });
  
                  // process all the methods
--- 581,17 ---
                          scan(l.head);
                      }
                  }
  
                  // process all the static initializers
!                 forEachInitializer(tree, STATIC_INITS, def -> {
                      scanDef(def);
                      clearPendingExits(false);
                  });
  
                  // process all the instance initializers
!                 forEachInitializer(tree, INSTANCE_INITS, def -> {
                      scanDef(def);
                      clearPendingExits(false);
                  });
  
                  // process all the methods

*** 1052,11 ***
                          scan(l.head);
                      }
                  }
  
                  // process all the static initializers
!                 forEachInitializer(tree, true, def -> {
                      scan(def);
                      errorUncaught();
                  });
  
                  // in an anonymous class, add the set of thrown exceptions to
--- 1070,11 ---
                          scan(l.head);
                      }
                  }
  
                  // process all the static initializers
!                 forEachInitializer(tree, STATIC_INITS, def -> {
                      scan(def);
                      errorUncaught();
                  });
  
                  // in an anonymous class, add the set of thrown exceptions to

*** 1396,11 ***
              for (List<Type> l = tree.meth.type.getThrownTypes(); l.nonEmpty(); l = l.tail)
                  markThrown(tree, l.head);
  
              // After super(), scan initializers to uncover any exceptions they throw
              if (TreeInfo.name(tree.meth) == names._super) {
!                 forEachInitializer(classDef, false, def -> {
                      scan(def);
                      errorUncaught();
                  });
              }
          }
--- 1414,11 ---
              for (List<Type> l = tree.meth.type.getThrownTypes(); l.nonEmpty(); l = l.tail)
                  markThrown(tree, l.head);
  
              // After super(), scan initializers to uncover any exceptions they throw
              if (TreeInfo.name(tree.meth) == names._super) {
!                 forEachInitializer(classDef, INSTANCE_INITS, def -> {
                      scan(def);
                      errorUncaught();
                  });
              }
          }

*** 1744,10 ***
--- 1762,11 ---
              uninitsWhenTrue = new Bits(true);
              uninitsWhenFalse = new Bits(true);
          }
  
          private boolean isConstructor;
+         private boolean isCompactOrGeneratedRecordConstructor;
  
          @Override
          protected void markDead() {
              inits.inclRange(returnadr, nextadr);
              uninits.inclRange(returnadr, nextadr);

*** 1760,17 ***
           */
          protected boolean trackable(VarSymbol sym) {
              return
                  sym.pos >= startPos &&
                  ((sym.owner.kind == MTH || sym.owner.kind == VAR ||
!                 isFinalUninitializedField(sym)));
          }
  
!         boolean isFinalUninitializedField(VarSymbol sym) {
              return sym.owner.kind == TYP &&
!                    ((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL &&
!                    classDef.sym.isEnclosedBy((ClassSymbol)sym.owner));
          }
  
          /** Initialize new trackable variable by setting its address field
           *  to the next available sequence number and entering it under that
           *  index into the vars array.
--- 1779,18 ---
           */
          protected boolean trackable(VarSymbol sym) {
              return
                  sym.pos >= startPos &&
                  ((sym.owner.kind == MTH || sym.owner.kind == VAR ||
!                 isBlankFinalOrStrictField(sym)));
          }
  
!         boolean isBlankFinalOrStrictField(VarSymbol sym) {
              return sym.owner.kind == TYP &&
!                    (sym.flags() & (HASINIT | PARAMETER)) == 0 &&
!                    (sym.isFinal() || sym.isStrict()) &&
+                    classDef.sym.isEnclosedBy((ClassSymbol)sym.owner);
          }
  
          /** Initialize new trackable variable by setting its address field
           *  to the next available sequence number and entering it under that
           *  index into the vars array.

*** 1837,10 ***
--- 1857,14 ---
          /** If tree is either a simple name or of the form this.name or
           *  C.this.name, and tree represents a trackable variable,
           *  record an initialization of the variable.
           */
          void letInit(JCTree tree) {
+             letInit(tree, (JCAssign) null);
+         }
+ 
+         void letInit(JCTree tree, JCAssign assign) {
              tree = TreeInfo.skipParens(tree);
              if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) {
                  Symbol sym = TreeInfo.symbol(tree);
                  if (sym.kind == VAR) {
                      letInit(tree.pos(), (VarSymbol)sym);

*** 1857,11 ***
          void checkInit(DiagnosticPosition pos, VarSymbol sym, Error errkey) {
              if ((sym.adr >= firstadr || sym.owner.kind != TYP) &&
                  trackable(sym) &&
                  !inits.isMember(sym.adr) &&
                  (sym.flags_field & CLASH) == 0) {
!                     log.error(pos, errkey);
                  inits.incl(sym.adr);
              }
          }
  
          /** Utility method to reset several Bits instances.
--- 1881,11 ---
          void checkInit(DiagnosticPosition pos, VarSymbol sym, Error errkey) {
              if ((sym.adr >= firstadr || sym.owner.kind != TYP) &&
                  trackable(sym) &&
                  !inits.isMember(sym.adr) &&
                  (sym.flags_field & CLASH) == 0) {
!                 log.error(pos, errkey);
                  inits.incl(sym.adr);
              }
          }
  
          /** Utility method to reset several Bits instances.

*** 1979,11 ***
                          }
                      }
                  }
  
                  // process all the static initializers
!                 forEachInitializer(tree, true, def -> {
                      scan(def);
                      clearPendingExits(false);
                  });
  
                  // verify all static final fields got initialized
--- 2003,11 ---
                          }
                      }
                  }
  
                  // process all the static initializers
!                 forEachInitializer(tree, STATIC_INITS, def -> {
                      scan(def);
                      clearPendingExits(false);
                  });
  
                  // verify all static final fields got initialized

*** 2046,12 ***
--- 2070,15 ---
              int firstadrPrev = firstadr;
              int returnadrPrev = returnadr;
  
              Assert.check(pendingExits.isEmpty());
              boolean isConstructorPrev = isConstructor;
+             boolean isCompactOrGeneratedRecordConstructorPrev = isCompactOrGeneratedRecordConstructor;
              try {
                  isConstructor = TreeInfo.isConstructor(tree);
+                 isCompactOrGeneratedRecordConstructor = isConstructor && ((tree.sym.flags() & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0 ||
+                          (tree.sym.flags() & (GENERATEDCONSTR | RECORD)) == (GENERATEDCONSTR | RECORD));
  
                  // We only track field initialization inside constructors
                  if (!isConstructor) {
                      firstadr = nextadr;
                  }

*** 2064,16 ***
                      /*  If we are executing the code from Gen, then there can be
                       *  synthetic or mandated variables, ignore them.
                       */
                      initParam(def);
                  }
                  // else we are in an instance initializer block;
                  // leave caught unchanged.
                  scan(tree.body);
  
-                 boolean isCompactOrGeneratedRecordConstructor = (tree.sym.flags() & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0 ||
-                         (tree.sym.flags() & (GENERATEDCONSTR | RECORD)) == (GENERATEDCONSTR | RECORD);
                  if (isConstructor) {
                      boolean isSynthesized = (tree.sym.flags() &
                                               GENERATEDCONSTR) != 0;
                      for (int i = firstadr; i < nextadr; i++) {
                          JCVariableDecl vardecl = vardecls[i];
--- 2091,23 ---
                      /*  If we are executing the code from Gen, then there can be
                       *  synthetic or mandated variables, ignore them.
                       */
                      initParam(def);
                  }
+                 if (isConstructor &&
+                         !TreeInfo.hasConstructorCall(tree, names._this)) {
+                     // Strict fields initializers are executed before the first statement
+                     // in the constructor body, unless the constructor is an alternate constructor
+                     forEachInitializer(classDef, EARLY_INSTANCE_INITS, def -> {
+                         scan(def);
+                         clearPendingExits(false);
+                     });
+                 }
                  // else we are in an instance initializer block;
                  // leave caught unchanged.
                  scan(tree.body);
  
                  if (isConstructor) {
                      boolean isSynthesized = (tree.sym.flags() &
                                               GENERATEDCONSTR) != 0;
                      for (int i = firstadr; i < nextadr; i++) {
                          JCVariableDecl vardecl = vardecls[i];

*** 2113,10 ***
--- 2147,11 ---
                  uninits.assign(uninitsPrev);
                  nextadr = nextadrPrev;
                  firstadr = firstadrPrev;
                  returnadr = returnadrPrev;
                  isConstructor = isConstructorPrev;
+                 isCompactOrGeneratedRecordConstructor = isCompactOrGeneratedRecordConstructorPrev;
              }
          }
  
          private void clearPendingExits(boolean inMethod) {
              List<PendingExit> exits = pendingExits.toList();

*** 2581,27 ***
              if (isConstructor) {
  
                  // If super(): at this point all initialization blocks will execute
                  Name name = TreeInfo.name(tree.meth);
                  if (name == names._super) {
!                     forEachInitializer(classDef, false, def -> {
!                         scan(def);
!                         clearPendingExits(false);
!                     });
                  }
  
                  // If this(): at this point all final uninitialized fields will get initialized
                  else if (name == names._this) {
                      for (int address = firstadr; address < nextadr; address++) {
                          VarSymbol sym = vardecls[address].sym;
!                         if (isFinalUninitializedField(sym) && !sym.isStatic())
                              letInit(tree.pos(), sym);
                      }
                  }
              }
          }
  
          public void visitNewClass(JCNewClass tree) {
              scanExpr(tree.encl);
              scanExprs(tree.args);
              scan(tree.def);
          }
--- 2616,42 ---
              if (isConstructor) {
  
                  // If super(): at this point all initialization blocks will execute
                  Name name = TreeInfo.name(tree.meth);
                  if (name == names._super) {
!                     if (!isCompactOrGeneratedRecordConstructor) {
!                         // all strict fields must be initialized at this point
!                         checkStrictFieldsInitializedBeforeSuper(tree);
!                     }
+                     forEachInitializer(classDef, LATE_INSTANCE_INITS,
+                             def -> {
+                                 scan(def);
+                                 clearPendingExits(false);
+                             });
                  }
  
                  // If this(): at this point all final uninitialized fields will get initialized
                  else if (name == names._this) {
                      for (int address = firstadr; address < nextadr; address++) {
                          VarSymbol sym = vardecls[address].sym;
!                         if (isBlankFinalOrStrictField(sym) && !sym.isStatic())
                              letInit(tree.pos(), sym);
                      }
                  }
              }
          }
  
+         void checkStrictFieldsInitializedBeforeSuper(JCMethodInvocation tree) {
+             for (int i = firstadr; i < nextadr; i++) {
+                 JCVariableDecl vardecl = vardecls[i];
+                 VarSymbol var = vardecl.sym;
+                 if (var.owner == classDef.sym && var.isStrictInstance()) {
+                     checkInit(TreeInfo.diagEndPos(tree), var, Errors.StrictFieldNotHaveBeenInitializedBeforeSuper(var));
+                 }
+             }
+         }
+ 
          public void visitNewClass(JCNewClass tree) {
              scanExpr(tree.encl);
              scanExprs(tree.args);
              scan(tree.def);
          }

*** 2665,11 ***
  
          public void visitAssign(JCAssign tree) {
              if (!TreeInfo.isIdentOrThisDotIdent(tree.lhs))
                  scanExpr(tree.lhs);
              scanExpr(tree.rhs);
!             letInit(tree.lhs);
          }
  
          // check fields accessed through this.<field> are definitely
          // assigned before reading their value
          public void visitSelect(JCFieldAccess tree) {
--- 2715,11 ---
  
          public void visitAssign(JCAssign tree) {
              if (!TreeInfo.isIdentOrThisDotIdent(tree.lhs))
                  scanExpr(tree.lhs);
              scanExpr(tree.rhs);
!             letInit(tree.lhs, tree);
          }
  
          // check fields accessed through this.<field> are definitely
          // assigned before reading their value
          public void visitSelect(JCFieldAccess tree) {
< prev index next >