< prev index next >

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

Print this page
rev 51104 : imported patch switch


  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;
  51 import static com.sun.tools.javac.code.TypeTag.VOID;


 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.CaseTree.CaseKind;
  35 import com.sun.source.tree.LambdaExpressionTree.BodyKind;
  36 import com.sun.tools.javac.code.*;
  37 import com.sun.tools.javac.code.Scope.WriteableScope;
  38 import com.sun.tools.javac.code.Source.Feature;
  39 import com.sun.tools.javac.resources.CompilerProperties.Errors;
  40 import com.sun.tools.javac.resources.CompilerProperties.Warnings;
  41 import com.sun.tools.javac.tree.*;
  42 import com.sun.tools.javac.util.*;
  43 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
  44 import com.sun.tools.javac.util.JCDiagnostic.Error;
  45 import com.sun.tools.javac.util.JCDiagnostic.Warning;
  46 
  47 import com.sun.tools.javac.code.Symbol.*;
  48 import com.sun.tools.javac.tree.JCTree.*;
  49 
  50 import static com.sun.tools.javac.code.Flags.*;
  51 import static com.sun.tools.javac.code.Flags.BLOCK;
  52 import static com.sun.tools.javac.code.Kinds.Kind.*;
  53 import static com.sun.tools.javac.code.TypeTag.BOOLEAN;
  54 import static com.sun.tools.javac.code.TypeTag.VOID;


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


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


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


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


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


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


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


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


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


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


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