< prev index next >

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

Print this page
rev 51258 : imported patch switch.diff


  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 //todo: one might eliminate uninits.andSets when monotonic
  27 
  28 package com.sun.tools.javac.comp;
  29 
  30 import java.util.HashMap;


  31 
  32 import com.sun.source.tree.LambdaExpressionTree.BodyKind;
  33 import com.sun.tools.javac.code.*;
  34 import com.sun.tools.javac.code.Scope.WriteableScope;
  35 import com.sun.tools.javac.code.Source.Feature;
  36 import com.sun.tools.javac.resources.CompilerProperties.Errors;
  37 import com.sun.tools.javac.resources.CompilerProperties.Warnings;
  38 import com.sun.tools.javac.tree.*;
  39 import com.sun.tools.javac.util.*;
  40 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
  41 import com.sun.tools.javac.util.JCDiagnostic.Error;
  42 import com.sun.tools.javac.util.JCDiagnostic.Warning;
  43 
  44 import com.sun.tools.javac.code.Symbol.*;
  45 import com.sun.tools.javac.tree.JCTree.*;
  46 
  47 import static com.sun.tools.javac.code.Flags.*;
  48 import static com.sun.tools.javac.code.Flags.BLOCK;
  49 import static com.sun.tools.javac.code.Kinds.Kind.*;
  50 import static com.sun.tools.javac.code.TypeTag.BOOLEAN;


 194     private final Log log;
 195     private final Symtab syms;
 196     private final Types types;
 197     private final Check chk;
 198     private       TreeMaker make;
 199     private final Resolve rs;
 200     private final JCDiagnostic.Factory diags;
 201     private Env<AttrContext> attrEnv;
 202     private       Lint lint;
 203     private final boolean allowEffectivelyFinalInInnerClasses;
 204 
 205     public static Flow instance(Context context) {
 206         Flow instance = context.get(flowKey);
 207         if (instance == null)
 208             instance = new Flow(context);
 209         return instance;
 210     }
 211 
 212     public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
 213         new AliveAnalyzer().analyzeTree(env, make);
 214         new AssignAnalyzer().analyzeTree(env);
 215         new FlowAnalyzer().analyzeTree(env, make);
 216         new CaptureAnalyzer().analyzeTree(env, make);
 217     }
 218 
 219     public void analyzeLambda(Env<AttrContext> env, JCLambda that, TreeMaker make, boolean speculative) {
 220         Log.DiagnosticHandler diagHandler = null;
 221         //we need to disable diagnostics temporarily; the problem is that if
 222         //a lambda expression contains e.g. an unreachable statement, an error
 223         //message will be reported and will cause compilation to skip the flow analyis
 224         //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
 225         //related errors, which will allow for more errors to be detected
 226         if (!speculative) {
 227             diagHandler = new Log.DiscardDiagnosticHandler(log);
 228         }
 229         try {
 230             new LambdaAliveAnalyzer().analyzeTree(env, that, make);
 231         } finally {
 232             if (!speculative) {
 233                 log.popDiagnosticHandler(diagHandler);
 234             }
 235         }
 236     }
 237 
 238     public List<Type> analyzeLambdaThrownTypes(final Env<AttrContext> env,
 239             JCLambda that, TreeMaker make) {
 240         //we need to disable diagnostics temporarily; the problem is that if
 241         //a lambda expression contains e.g. an unreachable statement, an error
 242         //message will be reported and will cause compilation to skip the flow analyis
 243         //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
 244         //related errors, which will allow for more errors to be detected
 245         Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
 246         try {
 247             new LambdaAssignAnalyzer(env).analyzeTree(env, that);
 248             LambdaFlowAnalyzer flowAnalyzer = new LambdaFlowAnalyzer();
 249             flowAnalyzer.analyzeTree(env, that, make);
 250             return flowAnalyzer.inferredThrownTypes;
 251         } finally {
 252             log.popDiagnosticHandler(diagHandler);
 253         }
 254     }
 255 
 256     /**
 257      * Definite assignment scan mode
 258      */
 259     enum FlowKind {
 260         /**
 261          * This is the normal DA/DU analysis mode
 262          */
 263         NORMAL("var.might.already.be.assigned", false),
 264         /**
 265          * This is the speculative DA/DU analysis mode used to speculatively
 266          * derive assertions within loop bodies
 267          */


 379             return resolveJump(tree, new ListBuffer<P>(), JumpKind.CONTINUE);
 380         }
 381 
 382         /** Resolve all breaks of this statement. */
 383         boolean resolveBreaks(JCTree tree, ListBuffer<P> oldPendingExits) {
 384             return resolveJump(tree, oldPendingExits, JumpKind.BREAK);
 385         }
 386 
 387         @Override
 388         public void scan(JCTree tree) {
 389             if (tree != null && (
 390                     tree.type == null ||
 391                     tree.type != Type.stuckType)) {
 392                 super.scan(tree);
 393             }
 394         }
 395 
 396         public void visitPackageDef(JCPackageDecl tree) {
 397             // Do nothing for PackageDecl
 398         }






 399     }
 400 
 401     /**
 402      * This pass implements the first step of the dataflow analysis, namely
 403      * the liveness analysis check. This checks that every statement is reachable.
 404      * The output of this analysis pass are used by other analyzers. This analyzer
 405      * sets the 'finallyCanCompleteNormally' field in the JCTry class.
 406      */
 407     class AliveAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> {
 408 
 409         /** A flag that indicates whether the last statement could
 410          *  complete normally.
 411          */
 412         private boolean alive;
 413 
 414         @Override
 415         void markDead() {
 416             alive = false;
 417         }
 418 


 579             alive |= resolveContinues(tree);
 580             resolveBreaks(tree, prevPendingExits);
 581             alive = true;
 582         }
 583 
 584         public void visitLabelled(JCLabeledStatement tree) {
 585             ListBuffer<PendingExit> prevPendingExits = pendingExits;
 586             pendingExits = new ListBuffer<>();
 587             scanStat(tree.body);
 588             alive |= resolveBreaks(tree, prevPendingExits);
 589         }
 590 
 591         public void visitSwitch(JCSwitch tree) {
 592             ListBuffer<PendingExit> prevPendingExits = pendingExits;
 593             pendingExits = new ListBuffer<>();
 594             scan(tree.selector);
 595             boolean hasDefault = false;
 596             for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
 597                 alive = true;
 598                 JCCase c = l.head;
 599                 if (c.pat == null)
 600                     hasDefault = true;
 601                 else
 602                     scan(c.pat);



 603                 scanStats(c.stats);





 604                 // Warn about fall-through if lint switch fallthrough enabled.
 605                 if (alive &&
 606                     lint.isEnabled(Lint.LintCategory.FALLTHROUGH) &&
 607                     c.stats.nonEmpty() && l.tail.nonEmpty())
 608                     log.warning(Lint.LintCategory.FALLTHROUGH,
 609                                 l.tail.head.pos(),
 610                                 Warnings.PossibleFallThroughIntoCase);
 611             }
 612             if (!hasDefault) {
 613                 alive = true;
 614             }
 615             alive |= resolveBreaks(tree, prevPendingExits);
 616         }
 617 








































 618         public void visitTry(JCTry tree) {
 619             ListBuffer<PendingExit> prevPendingExits = pendingExits;
 620             pendingExits = new ListBuffer<>();
 621             for (JCTree resource : tree.resources) {
 622                 if (resource instanceof JCVariableDecl) {
 623                     JCVariableDecl vdecl = (JCVariableDecl) resource;
 624                     visitVarDef(vdecl);
 625                 } else if (resource instanceof JCExpression) {
 626                     scan((JCExpression) resource);
 627                 } else {
 628                     throw new AssertionError(tree);  // parser error
 629                 }
 630             }
 631 
 632             scanStat(tree.body);
 633             boolean aliveEnd = alive;
 634 
 635             for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
 636                 alive = true;
 637                 JCVariableDecl param = l.head.param;


 663                 pendingExits = prevPendingExits;
 664                 while (exits.nonEmpty()) pendingExits.append(exits.next());
 665             }
 666         }
 667 
 668         @Override
 669         public void visitIf(JCIf tree) {
 670             scan(tree.cond);
 671             scanStat(tree.thenpart);
 672             if (tree.elsepart != null) {
 673                 boolean aliveAfterThen = alive;
 674                 alive = true;
 675                 scanStat(tree.elsepart);
 676                 alive = alive | aliveAfterThen;
 677             } else {
 678                 alive = true;
 679             }
 680         }
 681 
 682         public void visitBreak(JCBreak tree) {


 683             recordExit(new PendingExit(tree));
 684         }
 685 
 686         public void visitContinue(JCContinue tree) {
 687             recordExit(new PendingExit(tree));
 688         }
 689 
 690         public void visitReturn(JCReturn tree) {
 691             scan(tree.expr);
 692             recordExit(new PendingExit(tree));
 693         }
 694 
 695         public void visitThrow(JCThrow tree) {
 696             scan(tree.expr);
 697             markDead();
 698         }
 699 
 700         public void visitApply(JCMethodInvocation tree) {
 701             scan(tree.meth);
 702             scan(tree.args);


1023         }
1024 
1025         public void visitForeachLoop(JCEnhancedForLoop tree) {
1026             visitVarDef(tree.var);
1027             ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
1028             scan(tree.expr);
1029             pendingExits = new ListBuffer<>();
1030             scan(tree.body);
1031             resolveContinues(tree);
1032             resolveBreaks(tree, prevPendingExits);
1033         }
1034 
1035         public void visitLabelled(JCLabeledStatement tree) {
1036             ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
1037             pendingExits = new ListBuffer<>();
1038             scan(tree.body);
1039             resolveBreaks(tree, prevPendingExits);
1040         }
1041 
1042         public void visitSwitch(JCSwitch tree) {









1043             ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
1044             pendingExits = new ListBuffer<>();
1045             scan(tree.selector);
1046             for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
1047                 JCCase c = l.head;
1048                 if (c.pat != null) {
1049                     scan(c.pat);
1050                 }
1051                 scan(c.stats);
1052             }
1053             resolveBreaks(tree, prevPendingExits);
1054         }
1055 
1056         public void visitTry(JCTry tree) {
1057             List<Type> caughtPrev = caught;
1058             List<Type> thrownPrev = thrown;
1059             thrown = List.nil();
1060             for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
1061                 List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ?
1062                         ((JCTypeUnion)l.head.param.vartype).alternatives :
1063                         List.of(l.head.param.vartype);
1064                 for (JCExpression ct : subClauses) {
1065                     caught = chk.incl(ct.type, caught);
1066                 }
1067             }
1068 
1069             ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
1070             pendingExits = new ListBuffer<>();


1174                 // 'catchableThrownTypes' cannnot possibly be empty - if 'exc' was an
1175                 // unchecked exception, the result list would not be empty, as the augmented
1176                 // thrown set includes { RuntimeException, Error }; if 'exc' was a checked
1177                 // exception, that would have been covered in the branch above
1178                 if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty() &&
1179                         !isExceptionOrThrowable(exc)) {
1180                     Warning key = catchableThrownTypes.length() == 1 ?
1181                             Warnings.UnreachableCatch(catchableThrownTypes) :
1182                             Warnings.UnreachableCatch1(catchableThrownTypes);
1183                     log.warning(pos, key);
1184                 }
1185             }
1186         }
1187         //where
1188             private boolean isExceptionOrThrowable(Type exc) {
1189                 return exc.tsym == syms.throwableType.tsym ||
1190                     exc.tsym == syms.exceptionType.tsym;
1191             }
1192 
1193         public void visitBreak(JCBreak tree) {


1194             recordExit(new FlowPendingExit(tree, null));
1195         }
1196 
1197         public void visitContinue(JCContinue tree) {
1198             recordExit(new FlowPendingExit(tree, null));
1199         }
1200 
1201         public void visitReturn(JCReturn tree) {
1202             scan(tree.expr);
1203             recordExit(new FlowPendingExit(tree, null));
1204         }
1205 
1206         public void visitThrow(JCThrow tree) {
1207             scan(tree.expr);
1208             Symbol sym = TreeInfo.symbol(tree.expr);
1209             if (sym != null &&
1210                 sym.kind == VAR &&
1211                 (sym.flags() & (FINAL | EFFECTIVELY_FINAL)) != 0 &&
1212                 preciseRethrowTypes.get(sym) != null) {
1213                 for (Type t : preciseRethrowTypes.get(sym)) {


2088                     new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1)
2089                     break;
2090                 uninits.assign(uninitsEntry.andSet(uninits));
2091                 flowKind = FlowKind.SPECULATIVE_LOOP;
2092             } while (true);
2093             flowKind = prevFlowKind;
2094             inits.assign(initsStart);
2095             uninits.assign(uninitsStart.andSet(uninits));
2096             resolveBreaks(tree, prevPendingExits);
2097             nextadr = nextadrPrev;
2098         }
2099 
2100         public void visitLabelled(JCLabeledStatement tree) {
2101             ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
2102             pendingExits = new ListBuffer<>();
2103             scan(tree.body);
2104             resolveBreaks(tree, prevPendingExits);
2105         }
2106 
2107         public void visitSwitch(JCSwitch tree) {








2108             ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
2109             pendingExits = new ListBuffer<>();
2110             int nextadrPrev = nextadr;
2111             scanExpr(tree.selector);
2112             final Bits initsSwitch = new Bits(inits);
2113             final Bits uninitsSwitch = new Bits(uninits);
2114             boolean hasDefault = false;
2115             for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
2116                 inits.assign(initsSwitch);
2117                 uninits.assign(uninits.andSet(uninitsSwitch));
2118                 JCCase c = l.head;
2119                 if (c.pat == null) {
2120                     hasDefault = true;
2121                 } else {
2122                     scanExpr(c.pat);


2123                 }
2124                 if (hasDefault) {
2125                     inits.assign(initsSwitch);
2126                     uninits.assign(uninits.andSet(uninitsSwitch));
2127                 }
2128                 scan(c.stats);



2129                 addVars(c.stats, initsSwitch, uninitsSwitch);
2130                 if (!hasDefault) {
2131                     inits.assign(initsSwitch);
2132                     uninits.assign(uninits.andSet(uninitsSwitch));
2133                 }
2134                 // Warn about fall-through if lint switch fallthrough enabled.
2135             }
2136             if (!hasDefault) {
2137                 inits.andSet(initsSwitch);
2138             }
2139             resolveBreaks(tree, prevPendingExits);
2140             nextadr = nextadrPrev;
2141         }
2142         // where
2143             /** Add any variables defined in stats to inits and uninits. */
2144             private void addVars(List<JCStatement> stats, final Bits inits,
2145                                         final Bits uninits) {
2146                 for (;stats.nonEmpty(); stats = stats.tail) {
2147                     JCTree stat = stats.head;
2148                     if (stat.hasTag(VARDEF)) {


2284             final Bits uninitsBeforeElse = new Bits(uninitsWhenFalse);
2285             inits.assign(initsWhenTrue);
2286             uninits.assign(uninitsWhenTrue);
2287             scan(tree.thenpart);
2288             if (tree.elsepart != null) {
2289                 final Bits initsAfterThen = new Bits(inits);
2290                 final Bits uninitsAfterThen = new Bits(uninits);
2291                 inits.assign(initsBeforeElse);
2292                 uninits.assign(uninitsBeforeElse);
2293                 scan(tree.elsepart);
2294                 inits.andSet(initsAfterThen);
2295                 uninits.andSet(uninitsAfterThen);
2296             } else {
2297                 inits.andSet(initsBeforeElse);
2298                 uninits.andSet(uninitsBeforeElse);
2299             }
2300         }
2301 
2302         @Override
2303         public void visitBreak(JCBreak tree) {


2304             recordExit(new AssignPendingExit(tree, inits, uninits));
2305         }
2306 
2307         @Override
2308         public void visitContinue(JCContinue tree) {
2309             recordExit(new AssignPendingExit(tree, inits, uninits));
2310         }
2311 
2312         @Override
2313         public void visitReturn(JCReturn tree) {
2314             scanExpr(tree.expr);
2315             recordExit(new AssignPendingExit(tree, inits, uninits));
2316         }
2317 
2318         public void visitThrow(JCThrow tree) {
2319             scanExpr(tree.expr);
2320             markDead();
2321         }
2322 
2323         public void visitApply(JCMethodInvocation tree) {


2462 
2463         void referenced(Symbol sym) {
2464             unrefdResources.remove(sym);
2465         }
2466 
2467         public void visitAnnotatedType(JCAnnotatedType tree) {
2468             // annotations don't get scanned
2469             tree.underlyingType.accept(this);
2470         }
2471 
2472         public void visitModuleDef(JCModuleDecl tree) {
2473             // Do nothing for modules
2474         }
2475 
2476     /**************************************************************************
2477      * main method
2478      *************************************************************************/
2479 
2480         /** Perform definite assignment/unassignment analysis on a tree.
2481          */
2482         public void analyzeTree(Env<?> env) {
2483             analyzeTree(env, env.tree);
2484          }
2485 
2486         public void analyzeTree(Env<?> env, JCTree tree) {
2487             try {
2488                 startPos = tree.pos().getStartPosition();
2489 
2490                 if (vardecls == null)
2491                     vardecls = new JCVariableDecl[32];
2492                 else
2493                     for (int i=0; i<vardecls.length; i++)
2494                         vardecls[i] = null;
2495                 firstadr = 0;
2496                 nextadr = 0;

2497                 pendingExits = new ListBuffer<>();
2498                 this.classDef = null;
2499                 unrefdResources = WriteableScope.create(env.enclClass.sym);
2500                 scan(tree);
2501             } finally {
2502                 // note that recursive invocations of this method fail hard
2503                 startPos = -1;
2504                 resetBits(inits, uninits, uninitsTry, initsWhenTrue,
2505                         initsWhenFalse, uninitsWhenTrue, uninitsWhenFalse);
2506                 if (vardecls != null) {
2507                     for (int i=0; i<vardecls.length; i++)
2508                         vardecls[i] = null;
2509                 }
2510                 firstadr = 0;
2511                 nextadr = 0;

2512                 pendingExits = null;
2513                 this.classDef = null;
2514                 unrefdResources = null;
2515             }
2516         }
2517     }
2518 
2519     /**
2520      * This pass implements the last step of the dataflow analysis, namely
2521      * the effectively-final analysis check. This checks that every local variable
2522      * reference from a lambda body/local inner class is either final or effectively final.
2523      * Additional this also checks that every variable that is used as an operand to
2524      * try-with-resources is final or effectively final.
2525      * As effectively final variables are marked as such during DA/DU, this pass must run after
2526      * AssignAnalyzer.
2527      */
2528     class CaptureAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> {
2529 
2530         JCTree currentTree; //local class or lambda
2531 


2642                 case PREINC: case POSTINC:
2643                 case PREDEC: case POSTDEC:
2644                     scan(tree.arg);
2645                     letInit(tree.arg);
2646                     break;
2647                 default:
2648                     scan(tree.arg);
2649             }
2650         }
2651 
2652         public void visitTry(JCTry tree) {
2653             for (JCTree resource : tree.resources) {
2654                 if (!resource.hasTag(VARDEF)) {
2655                     Symbol var = TreeInfo.symbol(resource);
2656                     if (var != null && (var.flags() & (FINAL | EFFECTIVELY_FINAL)) == 0) {
2657                         log.error(resource.pos(), Errors.TryWithResourcesExprEffectivelyFinalVar(var));
2658                     }
2659                 }
2660             }
2661             super.visitTry(tree);






2662         }
2663 
2664         public void visitModuleDef(JCModuleDecl tree) {
2665             // Do nothing for modules
2666         }
2667 
2668     /**************************************************************************
2669      * main method
2670      *************************************************************************/
2671 
2672         /** Perform definite assignment/unassignment analysis on a tree.
2673          */
2674         public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
2675             analyzeTree(env, env.tree, make);
2676         }
2677         public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) {
2678             try {
2679                 attrEnv = env;
2680                 Flow.this.make = make;
2681                 pendingExits = new ListBuffer<>();


  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 //todo: one might eliminate uninits.andSets when monotonic
  27 
  28 package com.sun.tools.javac.comp;
  29 
  30 import java.util.HashMap;
  31 import java.util.HashSet;
  32 import java.util.Set;
  33 
  34 import com.sun.source.tree.LambdaExpressionTree.BodyKind;
  35 import com.sun.tools.javac.code.*;
  36 import com.sun.tools.javac.code.Scope.WriteableScope;
  37 import com.sun.tools.javac.code.Source.Feature;
  38 import com.sun.tools.javac.resources.CompilerProperties.Errors;
  39 import com.sun.tools.javac.resources.CompilerProperties.Warnings;
  40 import com.sun.tools.javac.tree.*;
  41 import com.sun.tools.javac.util.*;
  42 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
  43 import com.sun.tools.javac.util.JCDiagnostic.Error;
  44 import com.sun.tools.javac.util.JCDiagnostic.Warning;
  45 
  46 import com.sun.tools.javac.code.Symbol.*;
  47 import com.sun.tools.javac.tree.JCTree.*;
  48 
  49 import static com.sun.tools.javac.code.Flags.*;
  50 import static com.sun.tools.javac.code.Flags.BLOCK;
  51 import static com.sun.tools.javac.code.Kinds.Kind.*;
  52 import static com.sun.tools.javac.code.TypeTag.BOOLEAN;


 196     private final Log log;
 197     private final Symtab syms;
 198     private final Types types;
 199     private final Check chk;
 200     private       TreeMaker make;
 201     private final Resolve rs;
 202     private final JCDiagnostic.Factory diags;
 203     private Env<AttrContext> attrEnv;
 204     private       Lint lint;
 205     private final boolean allowEffectivelyFinalInInnerClasses;
 206 
 207     public static Flow instance(Context context) {
 208         Flow instance = context.get(flowKey);
 209         if (instance == null)
 210             instance = new Flow(context);
 211         return instance;
 212     }
 213 
 214     public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
 215         new AliveAnalyzer().analyzeTree(env, make);
 216         new AssignAnalyzer().analyzeTree(env, make);
 217         new FlowAnalyzer().analyzeTree(env, make);
 218         new CaptureAnalyzer().analyzeTree(env, make);
 219     }
 220 
 221     public void analyzeLambda(Env<AttrContext> env, JCLambda that, TreeMaker make, boolean speculative) {
 222         Log.DiagnosticHandler diagHandler = null;
 223         //we need to disable diagnostics temporarily; the problem is that if
 224         //a lambda expression contains e.g. an unreachable statement, an error
 225         //message will be reported and will cause compilation to skip the flow analyis
 226         //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
 227         //related errors, which will allow for more errors to be detected
 228         if (!speculative) {
 229             diagHandler = new Log.DiscardDiagnosticHandler(log);
 230         }
 231         try {
 232             new LambdaAliveAnalyzer().analyzeTree(env, that, make);
 233         } finally {
 234             if (!speculative) {
 235                 log.popDiagnosticHandler(diagHandler);
 236             }
 237         }
 238     }
 239 
 240     public List<Type> analyzeLambdaThrownTypes(final Env<AttrContext> env,
 241             JCLambda that, TreeMaker make) {
 242         //we need to disable diagnostics temporarily; the problem is that if
 243         //a lambda expression contains e.g. an unreachable statement, an error
 244         //message will be reported and will cause compilation to skip the flow analyis
 245         //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
 246         //related errors, which will allow for more errors to be detected
 247         Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
 248         try {
 249             new LambdaAssignAnalyzer(env).analyzeTree(env, that, make);
 250             LambdaFlowAnalyzer flowAnalyzer = new LambdaFlowAnalyzer();
 251             flowAnalyzer.analyzeTree(env, that, make);
 252             return flowAnalyzer.inferredThrownTypes;
 253         } finally {
 254             log.popDiagnosticHandler(diagHandler);
 255         }
 256     }
 257 
 258     /**
 259      * Definite assignment scan mode
 260      */
 261     enum FlowKind {
 262         /**
 263          * This is the normal DA/DU analysis mode
 264          */
 265         NORMAL("var.might.already.be.assigned", false),
 266         /**
 267          * This is the speculative DA/DU analysis mode used to speculatively
 268          * derive assertions within loop bodies
 269          */


 381             return resolveJump(tree, new ListBuffer<P>(), JumpKind.CONTINUE);
 382         }
 383 
 384         /** Resolve all breaks of this statement. */
 385         boolean resolveBreaks(JCTree tree, ListBuffer<P> oldPendingExits) {
 386             return resolveJump(tree, oldPendingExits, JumpKind.BREAK);
 387         }
 388 
 389         @Override
 390         public void scan(JCTree tree) {
 391             if (tree != null && (
 392                     tree.type == null ||
 393                     tree.type != Type.stuckType)) {
 394                 super.scan(tree);
 395             }
 396         }
 397 
 398         public void visitPackageDef(JCPackageDecl tree) {
 399             // Do nothing for PackageDecl
 400         }
 401 
 402         protected void scanSyntheticBreak(TreeMaker make, JCTree swtch) {
 403             JCBreak brk = make.at(Position.NOPOS).Break(null);
 404             brk.target = swtch;
 405             scan(brk);
 406         }
 407     }
 408 
 409     /**
 410      * This pass implements the first step of the dataflow analysis, namely
 411      * the liveness analysis check. This checks that every statement is reachable.
 412      * The output of this analysis pass are used by other analyzers. This analyzer
 413      * sets the 'finallyCanCompleteNormally' field in the JCTry class.
 414      */
 415     class AliveAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> {
 416 
 417         /** A flag that indicates whether the last statement could
 418          *  complete normally.
 419          */
 420         private boolean alive;
 421 
 422         @Override
 423         void markDead() {
 424             alive = false;
 425         }
 426 


 587             alive |= resolveContinues(tree);
 588             resolveBreaks(tree, prevPendingExits);
 589             alive = true;
 590         }
 591 
 592         public void visitLabelled(JCLabeledStatement tree) {
 593             ListBuffer<PendingExit> prevPendingExits = pendingExits;
 594             pendingExits = new ListBuffer<>();
 595             scanStat(tree.body);
 596             alive |= resolveBreaks(tree, prevPendingExits);
 597         }
 598 
 599         public void visitSwitch(JCSwitch tree) {
 600             ListBuffer<PendingExit> prevPendingExits = pendingExits;
 601             pendingExits = new ListBuffer<>();
 602             scan(tree.selector);
 603             boolean hasDefault = false;
 604             for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
 605                 alive = true;
 606                 JCCase c = l.head;
 607                 if (c.pats.isEmpty())
 608                     hasDefault = true;
 609                 else {
 610                     for (JCExpression pat : c.pats) {
 611                         scan(pat);
 612                     }
 613                 }
 614                 scanStats(c.stats);
 615                 c.completesNormally = alive;
 616                 if (alive && c.caseKind == JCCase.RULE) {
 617                     scanSyntheticBreak(make, tree);
 618                     alive = false;
 619                 }
 620                 // Warn about fall-through if lint switch fallthrough enabled.
 621                 if (alive &&
 622                     lint.isEnabled(Lint.LintCategory.FALLTHROUGH) &&
 623                     c.stats.nonEmpty() && l.tail.nonEmpty())
 624                     log.warning(Lint.LintCategory.FALLTHROUGH,
 625                                 l.tail.head.pos(),
 626                                 Warnings.PossibleFallThroughIntoCase);
 627             }
 628             if (!hasDefault) {
 629                 alive = true;
 630             }
 631             alive |= resolveBreaks(tree, prevPendingExits);
 632         }
 633 
 634         @Override
 635         public void visitSwitchExpression(JCSwitchExpression tree) {
 636             ListBuffer<PendingExit> prevPendingExits = pendingExits;
 637             pendingExits = new ListBuffer<>();
 638             scan(tree.selector);
 639             Set<Object> constants = null;
 640             if ((tree.selector.type.tsym.flags() & ENUM) != 0) {
 641                 constants = new HashSet<>();
 642                 for (Symbol s : tree.selector.type.tsym.members().getSymbols(s -> (s.flags() & ENUM) != 0)) {
 643                     constants.add(s.name);
 644                 }
 645             }
 646             boolean hasDefault = false;
 647             boolean prevAlive = alive;
 648             for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
 649                 alive = true;
 650                 JCCase c = l.head;
 651                 if (c.pats.isEmpty())
 652                     hasDefault = true;
 653                 else {
 654                     for (JCExpression pat : c.pats) {
 655                         scan(pat);
 656                         if (constants != null) {
 657                             if (pat.hasTag(IDENT))
 658                                 constants.remove(((JCIdent) pat).name);
 659                             if (pat.type != null)
 660                                 constants.remove(pat.type.constValue());
 661                         }
 662                     }
 663                 }
 664                 scanStats(c.stats);
 665                 c.completesNormally = alive;
 666             }
 667             if ((constants == null || !constants.isEmpty()) && !hasDefault) {
 668                 log.error(tree, Errors.NotExhaustive);
 669             }
 670             alive = prevAlive;
 671             alive |= resolveBreaks(tree, prevPendingExits);
 672         }
 673 
 674         public void visitTry(JCTry tree) {
 675             ListBuffer<PendingExit> prevPendingExits = pendingExits;
 676             pendingExits = new ListBuffer<>();
 677             for (JCTree resource : tree.resources) {
 678                 if (resource instanceof JCVariableDecl) {
 679                     JCVariableDecl vdecl = (JCVariableDecl) resource;
 680                     visitVarDef(vdecl);
 681                 } else if (resource instanceof JCExpression) {
 682                     scan((JCExpression) resource);
 683                 } else {
 684                     throw new AssertionError(tree);  // parser error
 685                 }
 686             }
 687 
 688             scanStat(tree.body);
 689             boolean aliveEnd = alive;
 690 
 691             for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
 692                 alive = true;
 693                 JCVariableDecl param = l.head.param;


 719                 pendingExits = prevPendingExits;
 720                 while (exits.nonEmpty()) pendingExits.append(exits.next());
 721             }
 722         }
 723 
 724         @Override
 725         public void visitIf(JCIf tree) {
 726             scan(tree.cond);
 727             scanStat(tree.thenpart);
 728             if (tree.elsepart != null) {
 729                 boolean aliveAfterThen = alive;
 730                 alive = true;
 731                 scanStat(tree.elsepart);
 732                 alive = alive | aliveAfterThen;
 733             } else {
 734                 alive = true;
 735             }
 736         }
 737 
 738         public void visitBreak(JCBreak tree) {
 739             if (tree.isValueBreak())
 740                 scan(tree.value);
 741             recordExit(new PendingExit(tree));
 742         }
 743 
 744         public void visitContinue(JCContinue tree) {
 745             recordExit(new PendingExit(tree));
 746         }
 747 
 748         public void visitReturn(JCReturn tree) {
 749             scan(tree.expr);
 750             recordExit(new PendingExit(tree));
 751         }
 752 
 753         public void visitThrow(JCThrow tree) {
 754             scan(tree.expr);
 755             markDead();
 756         }
 757 
 758         public void visitApply(JCMethodInvocation tree) {
 759             scan(tree.meth);
 760             scan(tree.args);


1081         }
1082 
1083         public void visitForeachLoop(JCEnhancedForLoop tree) {
1084             visitVarDef(tree.var);
1085             ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
1086             scan(tree.expr);
1087             pendingExits = new ListBuffer<>();
1088             scan(tree.body);
1089             resolveContinues(tree);
1090             resolveBreaks(tree, prevPendingExits);
1091         }
1092 
1093         public void visitLabelled(JCLabeledStatement tree) {
1094             ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
1095             pendingExits = new ListBuffer<>();
1096             scan(tree.body);
1097             resolveBreaks(tree, prevPendingExits);
1098         }
1099 
1100         public void visitSwitch(JCSwitch tree) {
1101             handleSwitch(tree, tree.selector, tree.cases);
1102         }
1103 
1104         @Override
1105         public void visitSwitchExpression(JCSwitchExpression tree) {
1106             handleSwitch(tree, tree.selector, tree.cases);
1107         }
1108 
1109         private void handleSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) {
1110             ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
1111             pendingExits = new ListBuffer<>();
1112             scan(selector);
1113             for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
1114                 JCCase c = l.head;
1115                 scan(c.pats);


1116                 scan(c.stats);
1117             }
1118             resolveBreaks(tree, prevPendingExits);
1119         }
1120 
1121         public void visitTry(JCTry tree) {
1122             List<Type> caughtPrev = caught;
1123             List<Type> thrownPrev = thrown;
1124             thrown = List.nil();
1125             for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
1126                 List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ?
1127                         ((JCTypeUnion)l.head.param.vartype).alternatives :
1128                         List.of(l.head.param.vartype);
1129                 for (JCExpression ct : subClauses) {
1130                     caught = chk.incl(ct.type, caught);
1131                 }
1132             }
1133 
1134             ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
1135             pendingExits = new ListBuffer<>();


1239                 // 'catchableThrownTypes' cannnot possibly be empty - if 'exc' was an
1240                 // unchecked exception, the result list would not be empty, as the augmented
1241                 // thrown set includes { RuntimeException, Error }; if 'exc' was a checked
1242                 // exception, that would have been covered in the branch above
1243                 if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty() &&
1244                         !isExceptionOrThrowable(exc)) {
1245                     Warning key = catchableThrownTypes.length() == 1 ?
1246                             Warnings.UnreachableCatch(catchableThrownTypes) :
1247                             Warnings.UnreachableCatch1(catchableThrownTypes);
1248                     log.warning(pos, key);
1249                 }
1250             }
1251         }
1252         //where
1253             private boolean isExceptionOrThrowable(Type exc) {
1254                 return exc.tsym == syms.throwableType.tsym ||
1255                     exc.tsym == syms.exceptionType.tsym;
1256             }
1257 
1258         public void visitBreak(JCBreak tree) {
1259             if (tree.isValueBreak())
1260                 scan(tree.value);
1261             recordExit(new FlowPendingExit(tree, null));
1262         }
1263 
1264         public void visitContinue(JCContinue tree) {
1265             recordExit(new FlowPendingExit(tree, null));
1266         }
1267 
1268         public void visitReturn(JCReturn tree) {
1269             scan(tree.expr);
1270             recordExit(new FlowPendingExit(tree, null));
1271         }
1272 
1273         public void visitThrow(JCThrow tree) {
1274             scan(tree.expr);
1275             Symbol sym = TreeInfo.symbol(tree.expr);
1276             if (sym != null &&
1277                 sym.kind == VAR &&
1278                 (sym.flags() & (FINAL | EFFECTIVELY_FINAL)) != 0 &&
1279                 preciseRethrowTypes.get(sym) != null) {
1280                 for (Type t : preciseRethrowTypes.get(sym)) {


2155                     new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1)
2156                     break;
2157                 uninits.assign(uninitsEntry.andSet(uninits));
2158                 flowKind = FlowKind.SPECULATIVE_LOOP;
2159             } while (true);
2160             flowKind = prevFlowKind;
2161             inits.assign(initsStart);
2162             uninits.assign(uninitsStart.andSet(uninits));
2163             resolveBreaks(tree, prevPendingExits);
2164             nextadr = nextadrPrev;
2165         }
2166 
2167         public void visitLabelled(JCLabeledStatement tree) {
2168             ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
2169             pendingExits = new ListBuffer<>();
2170             scan(tree.body);
2171             resolveBreaks(tree, prevPendingExits);
2172         }
2173 
2174         public void visitSwitch(JCSwitch tree) {
2175             handleSwitch(tree, tree.selector, tree.cases);
2176         }
2177 
2178         public void visitSwitchExpression(JCSwitchExpression tree) {
2179             handleSwitch(tree, tree.selector, tree.cases);
2180         }
2181 
2182         private void handleSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) {
2183             ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
2184             pendingExits = new ListBuffer<>();
2185             int nextadrPrev = nextadr;
2186             scanExpr(selector);
2187             final Bits initsSwitch = new Bits(inits);
2188             final Bits uninitsSwitch = new Bits(uninits);
2189             boolean hasDefault = false;
2190             for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
2191                 inits.assign(initsSwitch);
2192                 uninits.assign(uninits.andSet(uninitsSwitch));
2193                 JCCase c = l.head;
2194                 if (c.pats.isEmpty()) {
2195                     hasDefault = true;
2196                 } else {
2197                     for (JCExpression pat : c.pats) {
2198                         scanExpr(pat);
2199                     }
2200                 }
2201                 if (hasDefault) {
2202                     inits.assign(initsSwitch);
2203                     uninits.assign(uninits.andSet(uninitsSwitch));
2204                 }
2205                 scan(c.stats);
2206                 if (c.completesNormally && c.caseKind == JCCase.RULE) {
2207                     scanSyntheticBreak(make, tree);
2208                 }
2209                 addVars(c.stats, initsSwitch, uninitsSwitch);
2210                 if (!hasDefault) {
2211                     inits.assign(initsSwitch);
2212                     uninits.assign(uninits.andSet(uninitsSwitch));
2213                 }
2214                 // Warn about fall-through if lint switch fallthrough enabled.
2215             }
2216             if (!hasDefault) {
2217                 inits.andSet(initsSwitch);
2218             }
2219             resolveBreaks(tree, prevPendingExits);
2220             nextadr = nextadrPrev;
2221         }
2222         // where
2223             /** Add any variables defined in stats to inits and uninits. */
2224             private void addVars(List<JCStatement> stats, final Bits inits,
2225                                         final Bits uninits) {
2226                 for (;stats.nonEmpty(); stats = stats.tail) {
2227                     JCTree stat = stats.head;
2228                     if (stat.hasTag(VARDEF)) {


2364             final Bits uninitsBeforeElse = new Bits(uninitsWhenFalse);
2365             inits.assign(initsWhenTrue);
2366             uninits.assign(uninitsWhenTrue);
2367             scan(tree.thenpart);
2368             if (tree.elsepart != null) {
2369                 final Bits initsAfterThen = new Bits(inits);
2370                 final Bits uninitsAfterThen = new Bits(uninits);
2371                 inits.assign(initsBeforeElse);
2372                 uninits.assign(uninitsBeforeElse);
2373                 scan(tree.elsepart);
2374                 inits.andSet(initsAfterThen);
2375                 uninits.andSet(uninitsAfterThen);
2376             } else {
2377                 inits.andSet(initsBeforeElse);
2378                 uninits.andSet(uninitsBeforeElse);
2379             }
2380         }
2381 
2382         @Override
2383         public void visitBreak(JCBreak tree) {
2384             if (tree.isValueBreak())
2385                 scan(tree.value);
2386             recordExit(new AssignPendingExit(tree, inits, uninits));
2387         }
2388 
2389         @Override
2390         public void visitContinue(JCContinue tree) {
2391             recordExit(new AssignPendingExit(tree, inits, uninits));
2392         }
2393 
2394         @Override
2395         public void visitReturn(JCReturn tree) {
2396             scanExpr(tree.expr);
2397             recordExit(new AssignPendingExit(tree, inits, uninits));
2398         }
2399 
2400         public void visitThrow(JCThrow tree) {
2401             scanExpr(tree.expr);
2402             markDead();
2403         }
2404 
2405         public void visitApply(JCMethodInvocation tree) {


2544 
2545         void referenced(Symbol sym) {
2546             unrefdResources.remove(sym);
2547         }
2548 
2549         public void visitAnnotatedType(JCAnnotatedType tree) {
2550             // annotations don't get scanned
2551             tree.underlyingType.accept(this);
2552         }
2553 
2554         public void visitModuleDef(JCModuleDecl tree) {
2555             // Do nothing for modules
2556         }
2557 
2558     /**************************************************************************
2559      * main method
2560      *************************************************************************/
2561 
2562         /** Perform definite assignment/unassignment analysis on a tree.
2563          */
2564         public void analyzeTree(Env<?> env, TreeMaker make) {
2565             analyzeTree(env, env.tree, make);
2566          }
2567 
2568         public void analyzeTree(Env<?> env, JCTree tree, TreeMaker make) {
2569             try {
2570                 startPos = tree.pos().getStartPosition();
2571 
2572                 if (vardecls == null)
2573                     vardecls = new JCVariableDecl[32];
2574                 else
2575                     for (int i=0; i<vardecls.length; i++)
2576                         vardecls[i] = null;
2577                 firstadr = 0;
2578                 nextadr = 0;
2579                 Flow.this.make = make;
2580                 pendingExits = new ListBuffer<>();
2581                 this.classDef = null;
2582                 unrefdResources = WriteableScope.create(env.enclClass.sym);
2583                 scan(tree);
2584             } finally {
2585                 // note that recursive invocations of this method fail hard
2586                 startPos = -1;
2587                 resetBits(inits, uninits, uninitsTry, initsWhenTrue,
2588                         initsWhenFalse, uninitsWhenTrue, uninitsWhenFalse);
2589                 if (vardecls != null) {
2590                     for (int i=0; i<vardecls.length; i++)
2591                         vardecls[i] = null;
2592                 }
2593                 firstadr = 0;
2594                 nextadr = 0;
2595                 Flow.this.make = null;
2596                 pendingExits = null;
2597                 this.classDef = null;
2598                 unrefdResources = null;
2599             }
2600         }
2601     }
2602 
2603     /**
2604      * This pass implements the last step of the dataflow analysis, namely
2605      * the effectively-final analysis check. This checks that every local variable
2606      * reference from a lambda body/local inner class is either final or effectively final.
2607      * Additional this also checks that every variable that is used as an operand to
2608      * try-with-resources is final or effectively final.
2609      * As effectively final variables are marked as such during DA/DU, this pass must run after
2610      * AssignAnalyzer.
2611      */
2612     class CaptureAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> {
2613 
2614         JCTree currentTree; //local class or lambda
2615 


2726                 case PREINC: case POSTINC:
2727                 case PREDEC: case POSTDEC:
2728                     scan(tree.arg);
2729                     letInit(tree.arg);
2730                     break;
2731                 default:
2732                     scan(tree.arg);
2733             }
2734         }
2735 
2736         public void visitTry(JCTry tree) {
2737             for (JCTree resource : tree.resources) {
2738                 if (!resource.hasTag(VARDEF)) {
2739                     Symbol var = TreeInfo.symbol(resource);
2740                     if (var != null && (var.flags() & (FINAL | EFFECTIVELY_FINAL)) == 0) {
2741                         log.error(resource.pos(), Errors.TryWithResourcesExprEffectivelyFinalVar(var));
2742                     }
2743                 }
2744             }
2745             super.visitTry(tree);
2746         }
2747 
2748         @Override
2749         public void visitBreak(JCBreak tree) {
2750             if (tree.isValueBreak())
2751                 scan(tree.value);
2752         }
2753 
2754         public void visitModuleDef(JCModuleDecl tree) {
2755             // Do nothing for modules
2756         }
2757 
2758     /**************************************************************************
2759      * main method
2760      *************************************************************************/
2761 
2762         /** Perform definite assignment/unassignment analysis on a tree.
2763          */
2764         public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
2765             analyzeTree(env, env.tree, make);
2766         }
2767         public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) {
2768             try {
2769                 attrEnv = env;
2770                 Flow.this.make = make;
2771                 pendingExits = new ListBuffer<>();
< prev index next >