1 /* 2 * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 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.Map; 31 import java.util.Map.Entry; 32 import java.util.HashMap; 33 import java.util.HashSet; 34 import java.util.Set; 35 import java.util.stream.Collectors; 36 import java.util.stream.StreamSupport; 37 38 import com.sun.source.tree.LambdaExpressionTree.BodyKind; 39 import com.sun.tools.javac.code.*; 40 import com.sun.tools.javac.code.Scope.WriteableScope; 41 import com.sun.tools.javac.code.Source.Feature; 42 import com.sun.tools.javac.resources.CompilerProperties.Errors; 43 import com.sun.tools.javac.resources.CompilerProperties.Warnings; 44 import com.sun.tools.javac.tree.*; 45 import com.sun.tools.javac.util.*; 46 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 47 import com.sun.tools.javac.util.JCDiagnostic.Error; 48 import com.sun.tools.javac.util.JCDiagnostic.Warning; 49 50 import com.sun.tools.javac.code.Symbol.*; 51 import com.sun.tools.javac.tree.JCTree.*; 52 53 import static com.sun.tools.javac.code.Flags.*; 54 import static com.sun.tools.javac.code.Flags.BLOCK; 55 import static com.sun.tools.javac.code.Kinds.Kind.*; 56 import com.sun.tools.javac.code.Type.TypeVar; 57 import static com.sun.tools.javac.code.TypeTag.BOOLEAN; 58 import static com.sun.tools.javac.code.TypeTag.NONE; 59 import static com.sun.tools.javac.code.TypeTag.VOID; 60 import com.sun.tools.javac.code.Types.UniqueType; 61 import com.sun.tools.javac.resources.CompilerProperties.Fragments; 62 import static com.sun.tools.javac.tree.JCTree.Tag.*; 63 import com.sun.tools.javac.util.JCDiagnostic.Fragment; 64 65 /** This pass implements dataflow analysis for Java programs though 66 * different AST visitor steps. Liveness analysis (see AliveAnalyzer) checks that 67 * every statement is reachable. Exception analysis (see FlowAnalyzer) ensures that 68 * every checked exception that is thrown is declared or caught. Definite assignment analysis 69 * (see AssignAnalyzer) ensures that each variable is assigned when used. Definite 70 * unassignment analysis (see AssignAnalyzer) in ensures that no final variable 71 * is assigned more than once. Finally, local variable capture analysis (see CaptureAnalyzer) 72 * determines that local variables accessed within the scope of an inner class/lambda 73 * are either final or effectively-final. 74 * 75 * <p>The JLS has a number of problems in the 76 * specification of these flow analysis problems. This implementation 77 * attempts to address those issues. 78 * 79 * <p>First, there is no accommodation for a finally clause that cannot 80 * complete normally. For liveness analysis, an intervening finally 81 * clause can cause a break, continue, or return not to reach its 82 * target. For exception analysis, an intervening finally clause can 83 * cause any exception to be "caught". For DA/DU analysis, the finally 84 * clause can prevent a transfer of control from propagating DA/DU 85 * state to the target. In addition, code in the finally clause can 86 * affect the DA/DU status of variables. 87 * 88 * <p>For try statements, we introduce the idea of a variable being 89 * definitely unassigned "everywhere" in a block. A variable V is 90 * "unassigned everywhere" in a block iff it is unassigned at the 91 * beginning of the block and there is no reachable assignment to V 92 * in the block. An assignment V=e is reachable iff V is not DA 93 * after e. Then we can say that V is DU at the beginning of the 94 * catch block iff V is DU everywhere in the try block. Similarly, V 95 * is DU at the beginning of the finally block iff V is DU everywhere 96 * in the try block and in every catch block. Specifically, the 97 * following bullet is added to 16.2.2 98 * <pre> 99 * V is <em>unassigned everywhere</em> in a block if it is 100 * unassigned before the block and there is no reachable 101 * assignment to V within the block. 102 * </pre> 103 * <p>In 16.2.15, the third bullet (and all of its sub-bullets) for all 104 * try blocks is changed to 105 * <pre> 106 * V is definitely unassigned before a catch block iff V is 107 * definitely unassigned everywhere in the try block. 108 * </pre> 109 * <p>The last bullet (and all of its sub-bullets) for try blocks that 110 * have a finally block is changed to 111 * <pre> 112 * V is definitely unassigned before the finally block iff 113 * V is definitely unassigned everywhere in the try block 114 * and everywhere in each catch block of the try statement. 115 * </pre> 116 * <p>In addition, 117 * <pre> 118 * V is definitely assigned at the end of a constructor iff 119 * V is definitely assigned after the block that is the body 120 * of the constructor and V is definitely assigned at every 121 * return that can return from the constructor. 122 * </pre> 123 * <p>In addition, each continue statement with the loop as its target 124 * is treated as a jump to the end of the loop body, and "intervening" 125 * finally clauses are treated as follows: V is DA "due to the 126 * continue" iff V is DA before the continue statement or V is DA at 127 * the end of any intervening finally block. V is DU "due to the 128 * continue" iff any intervening finally cannot complete normally or V 129 * is DU at the end of every intervening finally block. This "due to 130 * the continue" concept is then used in the spec for the loops. 131 * 132 * <p>Similarly, break statements must consider intervening finally 133 * blocks. For liveness analysis, a break statement for which any 134 * intervening finally cannot complete normally is not considered to 135 * cause the target statement to be able to complete normally. Then 136 * we say V is DA "due to the break" iff V is DA before the break or 137 * V is DA at the end of any intervening finally block. V is DU "due 138 * to the break" iff any intervening finally cannot complete normally 139 * or V is DU at the break and at the end of every intervening 140 * finally block. (I suspect this latter condition can be 141 * simplified.) This "due to the break" is then used in the spec for 142 * all statements that can be "broken". 143 * 144 * <p>The return statement is treated similarly. V is DA "due to a 145 * return statement" iff V is DA before the return statement or V is 146 * DA at the end of any intervening finally block. Note that we 147 * don't have to worry about the return expression because this 148 * concept is only used for constructors. 149 * 150 * <p>There is no spec in the JLS for when a variable is definitely 151 * assigned at the end of a constructor, which is needed for final 152 * fields (8.3.1.2). We implement the rule that V is DA at the end 153 * of the constructor iff it is DA and the end of the body of the 154 * constructor and V is DA "due to" every return of the constructor. 155 * 156 * <p>Intervening finally blocks similarly affect exception analysis. An 157 * intervening finally that cannot complete normally allows us to ignore 158 * an otherwise uncaught exception. 159 * 160 * <p>To implement the semantics of intervening finally clauses, all 161 * nonlocal transfers (break, continue, return, throw, method call that 162 * can throw a checked exception, and a constructor invocation that can 163 * thrown a checked exception) are recorded in a queue, and removed 164 * from the queue when we complete processing the target of the 165 * nonlocal transfer. This allows us to modify the queue in accordance 166 * with the above rules when we encounter a finally clause. The only 167 * exception to this [no pun intended] is that checked exceptions that 168 * are known to be caught or declared to be caught in the enclosing 169 * method are not recorded in the queue, but instead are recorded in a 170 * global variable "{@code Set<Type> thrown}" that records the type of all 171 * exceptions that can be thrown. 172 * 173 * <p>Other minor issues the treatment of members of other classes 174 * (always considered DA except that within an anonymous class 175 * constructor, where DA status from the enclosing scope is 176 * preserved), treatment of the case expression (V is DA before the 177 * case expression iff V is DA after the switch expression), 178 * treatment of variables declared in a switch block (the implied 179 * DA/DU status after the switch expression is DU and not DA for 180 * variables defined in a switch block), the treatment of boolean ?: 181 * expressions (The JLS rules only handle b and c non-boolean; the 182 * new rule is that if b and c are boolean valued, then V is 183 * (un)assigned after a?b:c when true/false iff V is (un)assigned 184 * after b when true/false and V is (un)assigned after c when 185 * true/false). 186 * 187 * <p>There is the remaining question of what syntactic forms constitute a 188 * reference to a variable. It is conventional to allow this.x on the 189 * left-hand-side to initialize a final instance field named x, yet 190 * this.x isn't considered a "use" when appearing on a right-hand-side 191 * in most implementations. Should parentheses affect what is 192 * considered a variable reference? The simplest rule would be to 193 * allow unqualified forms only, parentheses optional, and phase out 194 * support for assigning to a final field via this.x. 195 * 196 * <p><b>This is NOT part of any supported API. 197 * If you write code that depends on this, you do so at your own risk. 198 * This code and its internal interfaces are subject to change or 199 * deletion without notice.</b> 200 */ 201 public class Flow { 202 protected static final Context.Key<Flow> flowKey = new Context.Key<>(); 203 204 private final Names names; 205 private final Log log; 206 private final Symtab syms; 207 private final Types types; 208 private final Check chk; 209 private TreeMaker make; 210 private final Resolve rs; 211 private final JCDiagnostic.Factory diags; 212 private Env<AttrContext> attrEnv; 213 private Lint lint; 214 215 public static Flow instance(Context context) { 216 Flow instance = context.get(flowKey); 217 if (instance == null) 218 instance = new Flow(context); 219 return instance; 220 } 221 222 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { 223 new AliveAnalyzer().analyzeTree(env, make); 224 new AssignAnalyzer().analyzeTree(env, make); 225 new FlowAnalyzer().analyzeTree(env, make); 226 new CaptureAnalyzer().analyzeTree(env, make); 227 } 228 229 public void analyzeLambda(Env<AttrContext> env, JCLambda that, TreeMaker make, boolean speculative) { 230 Log.DiagnosticHandler diagHandler = null; 231 //we need to disable diagnostics temporarily; the problem is that if 232 //a lambda expression contains e.g. an unreachable statement, an error 233 //message will be reported and will cause compilation to skip the flow analysis 234 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis 235 //related errors, which will allow for more errors to be detected 236 if (!speculative) { 237 diagHandler = new Log.DiscardDiagnosticHandler(log); 238 } 239 try { 240 new LambdaAliveAnalyzer().analyzeTree(env, that, make); 241 } finally { 242 if (!speculative) { 243 log.popDiagnosticHandler(diagHandler); 244 } 245 } 246 } 247 248 public List<Type> analyzeLambdaThrownTypes(final Env<AttrContext> env, 249 JCLambda that, TreeMaker make) { 250 //we need to disable diagnostics temporarily; the problem is that if 251 //a lambda expression contains e.g. an unreachable statement, an error 252 //message will be reported and will cause compilation to skip the flow analysis 253 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis 254 //related errors, which will allow for more errors to be detected 255 Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log); 256 try { 257 new LambdaAssignAnalyzer(env).analyzeTree(env, that, make); 258 LambdaFlowAnalyzer flowAnalyzer = new LambdaFlowAnalyzer(); 259 flowAnalyzer.analyzeTree(env, that, make); 260 return flowAnalyzer.inferredThrownTypes; 261 } finally { 262 log.popDiagnosticHandler(diagHandler); 263 } 264 } 265 266 public boolean aliveAfter(Env<AttrContext> env, JCTree that, TreeMaker make) { 267 //we need to disable diagnostics temporarily; the problem is that if 268 //"that" contains e.g. an unreachable statement, an error 269 //message will be reported and will cause compilation to skip the flow analysis 270 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis 271 //related errors, which will allow for more errors to be detected 272 Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log); 273 try { 274 SnippetAliveAnalyzer analyzer = new SnippetAliveAnalyzer(); 275 276 analyzer.analyzeTree(env, that, make); 277 return analyzer.isAlive(); 278 } finally { 279 log.popDiagnosticHandler(diagHandler); 280 } 281 } 282 283 public boolean breaksOutOf(Env<AttrContext> env, JCTree loop, JCTree body, TreeMaker make) { 284 //we need to disable diagnostics temporarily; the problem is that if 285 //"that" contains e.g. an unreachable statement, an error 286 //message will be reported and will cause compilation to skip the flow analysis 287 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis 288 //related errors, which will allow for more errors to be detected 289 Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log); 290 try { 291 SnippetBreakAnalyzer analyzer = new SnippetBreakAnalyzer(); 292 293 analyzer.analyzeTree(env, body, make); 294 return analyzer.breaksOut(); 295 } finally { 296 log.popDiagnosticHandler(diagHandler); 297 } 298 } 299 300 /** 301 * Definite assignment scan mode 302 */ 303 enum FlowKind { 304 /** 305 * This is the normal DA/DU analysis mode 306 */ 307 NORMAL("var.might.already.be.assigned", false), 308 /** 309 * This is the speculative DA/DU analysis mode used to speculatively 310 * derive assertions within loop bodies 311 */ 312 SPECULATIVE_LOOP("var.might.be.assigned.in.loop", true); 313 314 final String errKey; 315 final boolean isFinal; 316 317 FlowKind(String errKey, boolean isFinal) { 318 this.errKey = errKey; 319 this.isFinal = isFinal; 320 } 321 322 boolean isFinal() { 323 return isFinal; 324 } 325 } 326 327 protected Flow(Context context) { 328 context.put(flowKey, this); 329 names = Names.instance(context); 330 log = Log.instance(context); 331 syms = Symtab.instance(context); 332 types = Types.instance(context); 333 chk = Check.instance(context); 334 lint = Lint.instance(context); 335 rs = Resolve.instance(context); 336 diags = JCDiagnostic.Factory.instance(context); 337 Source source = Source.instance(context); 338 } 339 340 /** 341 * Base visitor class for all visitors implementing dataflow analysis logic. 342 * This class define the shared logic for handling jumps (break/continue statements). 343 */ 344 abstract static class BaseAnalyzer extends TreeScanner { 345 346 enum JumpKind { 347 BREAK(JCTree.Tag.BREAK) { 348 @Override 349 JCTree getTarget(JCTree tree) { 350 return ((JCBreak)tree).target; 351 } 352 }, 353 CONTINUE(JCTree.Tag.CONTINUE) { 354 @Override 355 JCTree getTarget(JCTree tree) { 356 return ((JCContinue)tree).target; 357 } 358 }, 359 YIELD(JCTree.Tag.YIELD) { 360 @Override 361 JCTree getTarget(JCTree tree) { 362 return ((JCYield)tree).target; 363 } 364 }; 365 366 final JCTree.Tag treeTag; 367 368 private JumpKind(Tag treeTag) { 369 this.treeTag = treeTag; 370 } 371 372 abstract JCTree getTarget(JCTree tree); 373 } 374 375 /** The currently pending exits that go from current inner blocks 376 * to an enclosing block, in source order. 377 */ 378 ListBuffer<PendingExit> pendingExits; 379 380 /** A pending exit. These are the statements return, break, and 381 * continue. In addition, exception-throwing expressions or 382 * statements are put here when not known to be caught. This 383 * will typically result in an error unless it is within a 384 * try-finally whose finally block cannot complete normally. 385 */ 386 static class PendingExit { 387 JCTree tree; 388 389 PendingExit(JCTree tree) { 390 this.tree = tree; 391 } 392 393 void resolveJump() { 394 //do nothing 395 } 396 } 397 398 abstract void markDead(); 399 400 /** Record an outward transfer of control. */ 401 void recordExit(PendingExit pe) { 402 pendingExits.append(pe); 403 markDead(); 404 } 405 406 /** Resolve all jumps of this statement. */ 407 private Liveness resolveJump(JCTree tree, 408 ListBuffer<PendingExit> oldPendingExits, 409 JumpKind jk) { 410 boolean resolved = false; 411 List<PendingExit> exits = pendingExits.toList(); 412 pendingExits = oldPendingExits; 413 for (; exits.nonEmpty(); exits = exits.tail) { 414 PendingExit exit = exits.head; 415 if (exit.tree.hasTag(jk.treeTag) && 416 jk.getTarget(exit.tree) == tree) { 417 exit.resolveJump(); 418 resolved = true; 419 } else { 420 pendingExits.append(exit); 421 } 422 } 423 return Liveness.from(resolved); 424 } 425 426 /** Resolve all continues of this statement. */ 427 Liveness resolveContinues(JCTree tree) { 428 return resolveJump(tree, new ListBuffer<PendingExit>(), JumpKind.CONTINUE); 429 } 430 431 /** Resolve all breaks of this statement. */ 432 Liveness resolveBreaks(JCTree tree, ListBuffer<PendingExit> oldPendingExits) { 433 return resolveJump(tree, oldPendingExits, JumpKind.BREAK); 434 } 435 436 /** Resolve all yields of this statement. */ 437 Liveness resolveYields(JCTree tree, ListBuffer<PendingExit> oldPendingExits) { 438 return resolveJump(tree, oldPendingExits, JumpKind.YIELD); 439 } 440 441 @Override 442 public void scan(JCTree tree) { 443 if (tree != null && ( 444 tree.type == null || 445 tree.type != Type.stuckType)) { 446 super.scan(tree); 447 } 448 } 449 450 public void visitPackageDef(JCPackageDecl tree) { 451 // Do nothing for PackageDecl 452 } 453 454 protected void scanSyntheticBreak(TreeMaker make, JCTree swtch) { 455 if (swtch.hasTag(SWITCH_EXPRESSION)) { 456 JCYield brk = make.at(Position.NOPOS).Yield(null); 457 brk.target = swtch; 458 scan(brk); 459 } else { 460 JCBreak brk = make.at(Position.NOPOS).Break(null); 461 brk.target = swtch; 462 scan(brk); 463 } 464 } 465 } 466 467 /** 468 * This pass implements the first step of the dataflow analysis, namely 469 * the liveness analysis check. This checks that every statement is reachable. 470 * The output of this analysis pass are used by other analyzers. This analyzer 471 * sets the 'finallyCanCompleteNormally' field in the JCTry class. 472 */ 473 class AliveAnalyzer extends BaseAnalyzer { 474 475 /** A flag that indicates whether the last statement could 476 * complete normally. 477 */ 478 private Liveness alive; 479 480 @Override 481 void markDead() { 482 alive = Liveness.DEAD; 483 } 484 485 /************************************************************************* 486 * Visitor methods for statements and definitions 487 *************************************************************************/ 488 489 /** Analyze a definition. 490 */ 491 void scanDef(JCTree tree) { 492 scanStat(tree); 493 if (tree != null && tree.hasTag(JCTree.Tag.BLOCK) && alive == Liveness.DEAD) { 494 log.error(tree.pos(), 495 Errors.InitializerMustBeAbleToCompleteNormally); 496 } 497 } 498 499 /** Analyze a statement. Check that statement is reachable. 500 */ 501 void scanStat(JCTree tree) { 502 if (alive == Liveness.DEAD && tree != null) { 503 log.error(tree.pos(), Errors.UnreachableStmt); 504 if (!tree.hasTag(SKIP)) alive = Liveness.RECOVERY; 505 } 506 scan(tree); 507 } 508 509 /** Analyze list of statements. 510 */ 511 void scanStats(List<? extends JCStatement> trees) { 512 if (trees != null) 513 for (List<? extends JCStatement> l = trees; l.nonEmpty(); l = l.tail) 514 scanStat(l.head); 515 } 516 517 /* ------------ Visitor methods for various sorts of trees -------------*/ 518 519 public void visitClassDef(JCClassDecl tree) { 520 if (tree.sym == null) return; 521 Liveness alivePrev = alive; 522 ListBuffer<PendingExit> pendingExitsPrev = pendingExits; 523 Lint lintPrev = lint; 524 525 pendingExits = new ListBuffer<>(); 526 lint = lint.augment(tree.sym); 527 528 try { 529 // process all the static initializers 530 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 531 if (!l.head.hasTag(METHODDEF) && 532 (TreeInfo.flags(l.head) & STATIC) != 0) { 533 scanDef(l.head); 534 clearPendingExits(false); 535 } 536 } 537 538 // process all the instance initializers 539 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 540 if (!l.head.hasTag(METHODDEF) && 541 (TreeInfo.flags(l.head) & STATIC) == 0) { 542 scanDef(l.head); 543 clearPendingExits(false); 544 } 545 } 546 547 // process all the methods 548 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 549 if (l.head.hasTag(METHODDEF)) { 550 scan(l.head); 551 } 552 } 553 } finally { 554 pendingExits = pendingExitsPrev; 555 alive = alivePrev; 556 lint = lintPrev; 557 } 558 } 559 560 public void visitMethodDef(JCMethodDecl tree) { 561 if (tree.body == null) return; 562 Lint lintPrev = lint; 563 564 lint = lint.augment(tree.sym); 565 566 Assert.check(pendingExits.isEmpty()); 567 568 try { 569 alive = Liveness.ALIVE; 570 scanStat(tree.body); 571 tree.completesNormally = alive != Liveness.DEAD; 572 573 if (alive == Liveness.ALIVE && !tree.sym.type.getReturnType().hasTag(VOID)) 574 log.error(TreeInfo.diagEndPos(tree.body), Errors.MissingRetStmt); 575 576 clearPendingExits(true); 577 } finally { 578 lint = lintPrev; 579 } 580 } 581 582 private void clearPendingExits(boolean inMethod) { 583 List<PendingExit> exits = pendingExits.toList(); 584 pendingExits = new ListBuffer<>(); 585 while (exits.nonEmpty()) { 586 PendingExit exit = exits.head; 587 exits = exits.tail; 588 Assert.check((inMethod && exit.tree.hasTag(RETURN)) || 589 log.hasErrorOn(exit.tree.pos())); 590 } 591 } 592 593 public void visitVarDef(JCVariableDecl tree) { 594 if (tree.init != null) { 595 Lint lintPrev = lint; 596 lint = lint.augment(tree.sym); 597 try{ 598 scan(tree.init); 599 } finally { 600 lint = lintPrev; 601 } 602 } 603 } 604 605 public void visitBlock(JCBlock tree) { 606 scanStats(tree.stats); 607 } 608 609 public void visitDoLoop(JCDoWhileLoop tree) { 610 ListBuffer<PendingExit> prevPendingExits = pendingExits; 611 pendingExits = new ListBuffer<>(); 612 scanStat(tree.body); 613 alive = alive.or(resolveContinues(tree)); 614 scan(tree.cond); 615 alive = alive.and(!tree.cond.type.isTrue()); 616 alive = alive.or(resolveBreaks(tree, prevPendingExits)); 617 } 618 619 public void visitWhileLoop(JCWhileLoop tree) { 620 ListBuffer<PendingExit> prevPendingExits = pendingExits; 621 pendingExits = new ListBuffer<>(); 622 scan(tree.cond); 623 alive = Liveness.from(!tree.cond.type.isFalse()); 624 scanStat(tree.body); 625 alive = alive.or(resolveContinues(tree)); 626 alive = resolveBreaks(tree, prevPendingExits).or( 627 !tree.cond.type.isTrue()); 628 } 629 630 public void visitForLoop(JCForLoop tree) { 631 ListBuffer<PendingExit> prevPendingExits = pendingExits; 632 scanStats(tree.init); 633 pendingExits = new ListBuffer<>(); 634 if (tree.cond != null) { 635 scan(tree.cond); 636 alive = Liveness.from(!tree.cond.type.isFalse()); 637 } else { 638 alive = Liveness.ALIVE; 639 } 640 scanStat(tree.body); 641 alive = alive.or(resolveContinues(tree)); 642 scan(tree.step); 643 alive = resolveBreaks(tree, prevPendingExits).or( 644 tree.cond != null && !tree.cond.type.isTrue()); 645 } 646 647 public void visitForeachLoop(JCEnhancedForLoop tree) { 648 if(tree.varOrRecordPattern instanceof JCVariableDecl jcVariableDecl) { 649 visitVarDef(jcVariableDecl); 650 } else if (tree.varOrRecordPattern instanceof JCRecordPattern jcRecordPattern) { 651 visitRecordPattern(jcRecordPattern); 652 653 Set<Symbol> coveredSymbols = 654 coveredSymbols(jcRecordPattern.pos(), List.of(jcRecordPattern)); 655 656 boolean isExhaustive = 657 isExhaustive(jcRecordPattern.pos(), tree.elementType, coveredSymbols); 658 659 if (!isExhaustive) { 660 log.error(tree, Errors.ForeachNotExhaustiveOnType(jcRecordPattern.type, tree.elementType)); 661 } 662 } 663 ListBuffer<PendingExit> prevPendingExits = pendingExits; 664 scan(tree.expr); 665 pendingExits = new ListBuffer<>(); 666 scanStat(tree.body); 667 alive = alive.or(resolveContinues(tree)); 668 resolveBreaks(tree, prevPendingExits); 669 alive = Liveness.ALIVE; 670 } 671 672 public void visitLabelled(JCLabeledStatement tree) { 673 ListBuffer<PendingExit> prevPendingExits = pendingExits; 674 pendingExits = new ListBuffer<>(); 675 scanStat(tree.body); 676 alive = alive.or(resolveBreaks(tree, prevPendingExits)); 677 } 678 679 public void visitSwitch(JCSwitch tree) { 680 ListBuffer<PendingExit> prevPendingExits = pendingExits; 681 pendingExits = new ListBuffer<>(); 682 scan(tree.selector); 683 boolean exhaustiveSwitch = TreeInfo.expectedExhaustive(tree); 684 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { 685 alive = Liveness.ALIVE; 686 JCCase c = l.head; 687 for (JCCaseLabel pat : c.labels) { 688 scan(pat); 689 } 690 scanStats(c.stats); 691 if (alive != Liveness.DEAD && c.caseKind == JCCase.RULE) { 692 scanSyntheticBreak(make, tree); 693 alive = Liveness.DEAD; 694 } 695 // Warn about fall-through if lint switch fallthrough enabled. 696 if (alive == Liveness.ALIVE && 697 lint.isEnabled(Lint.LintCategory.FALLTHROUGH) && 698 c.stats.nonEmpty() && l.tail.nonEmpty()) 699 log.warning(Lint.LintCategory.FALLTHROUGH, 700 l.tail.head.pos(), 701 Warnings.PossibleFallThroughIntoCase); 702 } 703 tree.isExhaustive = tree.hasUnconditionalPattern || 704 TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases); 705 if (exhaustiveSwitch) { 706 Set<Symbol> coveredSymbols = coveredSymbolsForCases(tree.pos(), tree.cases); 707 tree.isExhaustive |= isExhaustive(tree.selector.pos(), tree.selector.type, coveredSymbols); 708 if (!tree.isExhaustive) { 709 log.error(tree, Errors.NotExhaustiveStatement); 710 } 711 } 712 if (!tree.hasUnconditionalPattern && !exhaustiveSwitch) { 713 alive = Liveness.ALIVE; 714 } 715 alive = alive.or(resolveBreaks(tree, prevPendingExits)); 716 } 717 718 @Override 719 public void visitSwitchExpression(JCSwitchExpression tree) { 720 ListBuffer<PendingExit> prevPendingExits = pendingExits; 721 pendingExits = new ListBuffer<>(); 722 scan(tree.selector); 723 Liveness prevAlive = alive; 724 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { 725 alive = Liveness.ALIVE; 726 JCCase c = l.head; 727 for (JCCaseLabel pat : c.labels) { 728 scan(pat); 729 } 730 scanStats(c.stats); 731 if (alive == Liveness.ALIVE) { 732 if (c.caseKind == JCCase.RULE) { 733 log.error(TreeInfo.diagEndPos(c.body), 734 Errors.RuleCompletesNormally); 735 } else if (l.tail.isEmpty()) { 736 log.error(TreeInfo.diagEndPos(tree), 737 Errors.SwitchExpressionCompletesNormally); 738 } 739 } 740 } 741 Set<Symbol> coveredSymbols = coveredSymbolsForCases(tree.pos(), tree.cases); 742 tree.isExhaustive = tree.hasUnconditionalPattern || 743 TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases) || 744 isExhaustive(tree.selector.pos(), tree.selector.type, coveredSymbols); 745 if (!tree.isExhaustive) { 746 log.error(tree, Errors.NotExhaustive); 747 } 748 alive = prevAlive; 749 alive = alive.or(resolveYields(tree, prevPendingExits)); 750 } 751 752 private Set<Symbol> coveredSymbolsForCases(DiagnosticPosition pos, 753 List<JCCase> cases) { 754 HashSet<JCTree> labelValues = cases.stream() 755 .flatMap(c -> c.labels.stream()) 756 .filter(TreeInfo::unguardedCaseLabel) 757 .filter(l -> !l.hasTag(DEFAULTCASELABEL)) 758 .map(l -> l.hasTag(CONSTANTCASELABEL) ? ((JCConstantCaseLabel) l).expr 759 : ((JCPatternCaseLabel) l).pat) 760 .collect(Collectors.toCollection(HashSet::new)); 761 return coveredSymbols(pos, labelValues); 762 } 763 764 private Set<Symbol> coveredSymbols(DiagnosticPosition pos, 765 Iterable<? extends JCTree> labels) { 766 Set<Symbol> coveredSymbols = new HashSet<>(); 767 Map<UniqueType, List<JCRecordPattern>> deconstructionPatternsByType = new HashMap<>(); 768 769 for (JCTree labelValue : labels) { 770 switch (labelValue.getTag()) { 771 case BINDINGPATTERN, PARENTHESIZEDPATTERN -> { 772 Type primaryPatternType = TreeInfo.primaryPatternType((JCPattern) labelValue); 773 if (!primaryPatternType.hasTag(NONE)) { 774 coveredSymbols.add(primaryPatternType.tsym); 775 } 776 } 777 case RECORDPATTERN -> { 778 JCRecordPattern dpat = (JCRecordPattern) labelValue; 779 UniqueType type = new UniqueType(dpat.type, types); 780 List<JCRecordPattern> augmentedPatterns = 781 deconstructionPatternsByType.getOrDefault(type, List.nil()) 782 .prepend(dpat); 783 784 deconstructionPatternsByType.put(type, augmentedPatterns); 785 } 786 787 default -> { 788 Assert.check(labelValue instanceof JCExpression, labelValue.getTag().name()); 789 JCExpression expr = (JCExpression) labelValue; 790 if (expr.hasTag(IDENT) && ((JCIdent) expr).sym.isEnum()) 791 coveredSymbols.add(((JCIdent) expr).sym); 792 } 793 } 794 } 795 for (Entry<UniqueType, List<JCRecordPattern>> e : deconstructionPatternsByType.entrySet()) { 796 if (e.getValue().stream().anyMatch(r -> r.nested.size() != r.record.getRecordComponents().size())) { 797 coveredSymbols.add(syms.errSymbol); 798 } else if (coversDeconstructionFromComponent(pos, e.getKey().type, e.getValue(), 0)) { 799 coveredSymbols.add(e.getKey().type.tsym); 800 } 801 } 802 return coveredSymbols; 803 } 804 805 private boolean coversDeconstructionFromComponent(DiagnosticPosition pos, 806 Type recordType, 807 List<JCRecordPattern> deconstructionPatterns, 808 int component) { 809 //Given a set of record patterns for the same record, and a starting component, 810 //this method checks, whether the nested patterns for the components are exhaustive, 811 //i.e. represent all possible combinations. 812 //This is done by categorizing the patterns based on the type covered by the given 813 //starting component. 814 //For each such category, it is then checked if the nested patterns starting at the next 815 //component are exhaustive, by recursivelly invoking this method. If these nested patterns 816 //are exhaustive, the given covered type is accepted. 817 //All such covered types are then checked whether they cover the declared type of 818 //the starting component's declaration. If yes, the given set of patterns starting at 819 //the given component cover the given record exhaustivelly, and true is returned. 820 List<? extends RecordComponent> components = 821 deconstructionPatterns.head.record.getRecordComponents(); 822 823 if (components.size() == component) { 824 //no components remain to be checked: 825 return true; 826 } 827 828 //for the first tested component, gather symbols covered by the nested patterns: 829 Type instantiatedComponentType = types.memberType(recordType, components.get(component)); 830 List<JCPattern> nestedComponentPatterns = deconstructionPatterns.map(d -> d.nested.get(component)); 831 Set<Symbol> coveredSymbolsForComponent = coveredSymbols(pos, 832 nestedComponentPatterns); 833 834 //for each of the symbols covered by the starting component, find all deconstruction patterns 835 //that have the given type, or its supertype, as a type of the starting nested pattern: 836 Map<Symbol, List<JCRecordPattern>> coveredSymbol2Patterns = new HashMap<>(); 837 838 for (JCRecordPattern deconstructionPattern : deconstructionPatterns) { 839 JCPattern nestedPattern = deconstructionPattern.nested.get(component); 840 Symbol componentPatternType; 841 switch (nestedPattern.getTag()) { 842 case BINDINGPATTERN, PARENTHESIZEDPATTERN -> { 843 Type primaryPatternType = 844 TreeInfo.primaryPatternType(nestedPattern); 845 componentPatternType = primaryPatternType.tsym; 846 } 847 case RECORDPATTERN -> { 848 componentPatternType = ((JCRecordPattern) nestedPattern).record; 849 } 850 default -> { 851 throw Assert.error("Unexpected tree kind: " + nestedPattern.getTag()); 852 } 853 } 854 for (Symbol currentType : coveredSymbolsForComponent) { 855 if (types.isSubtype(types.erasure(currentType.type), 856 types.erasure(componentPatternType.type))) { 857 coveredSymbol2Patterns.put(currentType, 858 coveredSymbol2Patterns.getOrDefault(currentType, 859 List.nil()) 860 .prepend(deconstructionPattern)); 861 } 862 } 863 } 864 865 //Check the components following the starting component, for each of the covered symbol, 866 //if they are exhaustive. If yes, the given covered symbol should be part of the following 867 //exhaustiveness check: 868 Set<Symbol> covered = new HashSet<>(); 869 870 for (Entry<Symbol, List<JCRecordPattern>> e : coveredSymbol2Patterns.entrySet()) { 871 if (coversDeconstructionFromComponent(pos, recordType, e.getValue(), component + 1)) { 872 covered.add(e.getKey()); 873 } 874 } 875 876 //verify whether the filtered symbols cover the given record's declared type: 877 return isExhaustive(pos, instantiatedComponentType, covered); 878 } 879 880 private void transitiveCovers(DiagnosticPosition pos, Type seltype, Set<Symbol> covered) { 881 List<Symbol> todo = List.from(covered); 882 while (todo.nonEmpty()) { 883 Symbol sym = todo.head; 884 todo = todo.tail; 885 switch (sym.kind) { 886 case VAR -> { 887 Iterable<Symbol> constants = sym.owner 888 .members() 889 .getSymbols(s -> s.isEnum() && 890 s.kind == VAR); 891 boolean hasAll = StreamSupport.stream(constants.spliterator(), false) 892 .allMatch(covered::contains); 893 894 if (hasAll && covered.add(sym.owner)) { 895 todo = todo.prepend(sym.owner); 896 } 897 } 898 899 case TYP -> { 900 for (Type sup : types.directSupertypes(sym.type)) { 901 if (sup.tsym.kind == TYP) { 902 if (isTransitivelyCovered(pos, seltype, sup.tsym, covered) && 903 covered.add(sup.tsym)) { 904 todo = todo.prepend(sup.tsym); 905 } 906 } 907 } 908 } 909 } 910 } 911 } 912 913 private boolean isTransitivelyCovered(DiagnosticPosition pos, Type seltype, 914 Symbol sealed, Set<Symbol> covered) { 915 try { 916 if (covered.stream().anyMatch(c -> sealed.isSubClass(c, types))) 917 return true; 918 if (sealed.kind == TYP && sealed.isAbstract() && sealed.isSealed()) { 919 return ((ClassSymbol) sealed).permitted 920 .stream() 921 .filter(s -> { 922 return types.isCastable(seltype, s.type/*, types.noWarnings*/); 923 }) 924 .allMatch(s -> isTransitivelyCovered(pos, seltype, s, covered)); 925 } 926 return false; 927 } catch (CompletionFailure cf) { 928 chk.completionError(pos, cf); 929 return true; 930 } 931 } 932 933 private boolean isExhaustive(DiagnosticPosition pos, Type seltype, Set<Symbol> covered) { 934 transitiveCovers(pos, seltype, covered); 935 return switch (seltype.getTag()) { 936 case CLASS -> { 937 if (seltype.isCompound()) { 938 if (seltype.isIntersection()) { 939 yield ((Type.IntersectionClassType) seltype).getComponents() 940 .stream() 941 .anyMatch(t -> isExhaustive(pos, t, covered)); 942 } 943 yield false; 944 } 945 yield covered.stream() 946 .filter(coveredSym -> coveredSym.kind == TYP) 947 .anyMatch(coveredSym -> types.isSubtype(types.erasure(seltype), 948 types.erasure(coveredSym.type))); 949 } 950 case TYPEVAR -> isExhaustive(pos, ((TypeVar) seltype).getUpperBound(), covered); 951 default -> { 952 yield covered.contains(types.erasure(seltype).tsym); 953 } 954 }; 955 } 956 957 public void visitTry(JCTry tree) { 958 ListBuffer<PendingExit> prevPendingExits = pendingExits; 959 pendingExits = new ListBuffer<>(); 960 for (JCTree resource : tree.resources) { 961 if (resource instanceof JCVariableDecl variableDecl) { 962 visitVarDef(variableDecl); 963 } else if (resource instanceof JCExpression expression) { 964 scan(expression); 965 } else { 966 throw new AssertionError(tree); // parser error 967 } 968 } 969 970 scanStat(tree.body); 971 Liveness aliveEnd = alive; 972 973 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { 974 alive = Liveness.ALIVE; 975 JCVariableDecl param = l.head.param; 976 scan(param); 977 scanStat(l.head.body); 978 aliveEnd = aliveEnd.or(alive); 979 } 980 if (tree.finalizer != null) { 981 ListBuffer<PendingExit> exits = pendingExits; 982 pendingExits = prevPendingExits; 983 alive = Liveness.ALIVE; 984 scanStat(tree.finalizer); 985 tree.finallyCanCompleteNormally = alive != Liveness.DEAD; 986 if (alive == Liveness.DEAD) { 987 if (lint.isEnabled(Lint.LintCategory.FINALLY)) { 988 log.warning(Lint.LintCategory.FINALLY, 989 TreeInfo.diagEndPos(tree.finalizer), 990 Warnings.FinallyCannotComplete); 991 } 992 } else { 993 while (exits.nonEmpty()) { 994 pendingExits.append(exits.next()); 995 } 996 alive = aliveEnd; 997 } 998 } else { 999 alive = aliveEnd; 1000 ListBuffer<PendingExit> exits = pendingExits; 1001 pendingExits = prevPendingExits; 1002 while (exits.nonEmpty()) pendingExits.append(exits.next()); 1003 } 1004 } 1005 1006 @Override 1007 public void visitIf(JCIf tree) { 1008 scan(tree.cond); 1009 scanStat(tree.thenpart); 1010 if (tree.elsepart != null) { 1011 Liveness aliveAfterThen = alive; 1012 alive = Liveness.ALIVE; 1013 scanStat(tree.elsepart); 1014 alive = alive.or(aliveAfterThen); 1015 } else { 1016 alive = Liveness.ALIVE; 1017 } 1018 } 1019 1020 public void visitBreak(JCBreak tree) { 1021 recordExit(new PendingExit(tree)); 1022 } 1023 1024 @Override 1025 public void visitYield(JCYield tree) { 1026 scan(tree.value); 1027 recordExit(new PendingExit(tree)); 1028 } 1029 1030 public void visitContinue(JCContinue tree) { 1031 recordExit(new PendingExit(tree)); 1032 } 1033 1034 public void visitReturn(JCReturn tree) { 1035 scan(tree.expr); 1036 recordExit(new PendingExit(tree)); 1037 } 1038 1039 public void visitThrow(JCThrow tree) { 1040 scan(tree.expr); 1041 markDead(); 1042 } 1043 1044 public void visitApply(JCMethodInvocation tree) { 1045 scan(tree.meth); 1046 scan(tree.args); 1047 } 1048 1049 public void visitNewClass(JCNewClass tree) { 1050 scan(tree.encl); 1051 scan(tree.args); 1052 if (tree.def != null) { 1053 scan(tree.def); 1054 } 1055 } 1056 1057 @Override 1058 public void visitLambda(JCLambda tree) { 1059 if (tree.type != null && 1060 tree.type.isErroneous()) { 1061 return; 1062 } 1063 1064 ListBuffer<PendingExit> prevPending = pendingExits; 1065 Liveness prevAlive = alive; 1066 try { 1067 pendingExits = new ListBuffer<>(); 1068 alive = Liveness.ALIVE; 1069 scanStat(tree.body); 1070 tree.canCompleteNormally = alive != Liveness.DEAD; 1071 } 1072 finally { 1073 pendingExits = prevPending; 1074 alive = prevAlive; 1075 } 1076 } 1077 1078 public void visitModuleDef(JCModuleDecl tree) { 1079 // Do nothing for modules 1080 } 1081 1082 /************************************************************************** 1083 * main method 1084 *************************************************************************/ 1085 1086 /** Perform definite assignment/unassignment analysis on a tree. 1087 */ 1088 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { 1089 analyzeTree(env, env.tree, make); 1090 } 1091 public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) { 1092 try { 1093 attrEnv = env; 1094 Flow.this.make = make; 1095 pendingExits = new ListBuffer<>(); 1096 alive = Liveness.ALIVE; 1097 scan(tree); 1098 } finally { 1099 pendingExits = null; 1100 Flow.this.make = null; 1101 } 1102 } 1103 } 1104 1105 /** 1106 * This pass implements the second step of the dataflow analysis, namely 1107 * the exception analysis. This is to ensure that every checked exception that is 1108 * thrown is declared or caught. The analyzer uses some info that has been set by 1109 * the liveliness analyzer. 1110 */ 1111 class FlowAnalyzer extends BaseAnalyzer { 1112 1113 /** A flag that indicates whether the last statement could 1114 * complete normally. 1115 */ 1116 HashMap<Symbol, List<Type>> preciseRethrowTypes; 1117 1118 /** The current class being defined. 1119 */ 1120 JCClassDecl classDef; 1121 1122 /** The list of possibly thrown declarable exceptions. 1123 */ 1124 List<Type> thrown; 1125 1126 /** The list of exceptions that are either caught or declared to be 1127 * thrown. 1128 */ 1129 List<Type> caught; 1130 1131 class ThrownPendingExit extends BaseAnalyzer.PendingExit { 1132 1133 Type thrown; 1134 1135 ThrownPendingExit(JCTree tree, Type thrown) { 1136 super(tree); 1137 this.thrown = thrown; 1138 } 1139 } 1140 1141 @Override 1142 void markDead() { 1143 //do nothing 1144 } 1145 1146 /*-------------------- Exceptions ----------------------*/ 1147 1148 /** Complain that pending exceptions are not caught. 1149 */ 1150 void errorUncaught() { 1151 for (PendingExit exit = pendingExits.next(); 1152 exit != null; 1153 exit = pendingExits.next()) { 1154 if (exit instanceof ThrownPendingExit thrownExit) { 1155 if (classDef != null && 1156 classDef.pos == exit.tree.pos) { 1157 log.error(exit.tree.pos(), 1158 Errors.UnreportedExceptionDefaultConstructor(thrownExit.thrown)); 1159 } else if (exit.tree.hasTag(VARDEF) && 1160 ((JCVariableDecl)exit.tree).sym.isResourceVariable()) { 1161 log.error(exit.tree.pos(), 1162 Errors.UnreportedExceptionImplicitClose(thrownExit.thrown, 1163 ((JCVariableDecl)exit.tree).sym.name)); 1164 } else { 1165 log.error(exit.tree.pos(), 1166 Errors.UnreportedExceptionNeedToCatchOrThrow(thrownExit.thrown)); 1167 } 1168 } else { 1169 Assert.check(log.hasErrorOn(exit.tree.pos())); 1170 } 1171 } 1172 } 1173 1174 /** Record that exception is potentially thrown and check that it 1175 * is caught. 1176 */ 1177 void markThrown(JCTree tree, Type exc) { 1178 if (!chk.isUnchecked(tree.pos(), exc)) { 1179 if (!chk.isHandled(exc, caught)) { 1180 pendingExits.append(new ThrownPendingExit(tree, exc)); 1181 } 1182 thrown = chk.incl(exc, thrown); 1183 } 1184 } 1185 1186 /************************************************************************* 1187 * Visitor methods for statements and definitions 1188 *************************************************************************/ 1189 1190 /* ------------ Visitor methods for various sorts of trees -------------*/ 1191 1192 public void visitClassDef(JCClassDecl tree) { 1193 if (tree.sym == null) return; 1194 1195 JCClassDecl classDefPrev = classDef; 1196 List<Type> thrownPrev = thrown; 1197 List<Type> caughtPrev = caught; 1198 ListBuffer<PendingExit> pendingExitsPrev = pendingExits; 1199 Lint lintPrev = lint; 1200 boolean anonymousClass = tree.name == names.empty; 1201 pendingExits = new ListBuffer<>(); 1202 if (!anonymousClass) { 1203 caught = List.nil(); 1204 } 1205 classDef = tree; 1206 thrown = List.nil(); 1207 lint = lint.augment(tree.sym); 1208 1209 try { 1210 // process all the static initializers 1211 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 1212 if (!l.head.hasTag(METHODDEF) && 1213 (TreeInfo.flags(l.head) & STATIC) != 0) { 1214 scan(l.head); 1215 errorUncaught(); 1216 } 1217 } 1218 1219 // add intersection of all thrown clauses of initial constructors 1220 // to set of caught exceptions, unless class is anonymous. 1221 if (!anonymousClass) { 1222 boolean firstConstructor = true; 1223 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 1224 if (TreeInfo.isInitialConstructor(l.head)) { 1225 List<Type> mthrown = 1226 ((JCMethodDecl) l.head).sym.type.getThrownTypes(); 1227 if (firstConstructor) { 1228 caught = mthrown; 1229 firstConstructor = false; 1230 } else { 1231 caught = chk.intersect(mthrown, caught); 1232 } 1233 } 1234 } 1235 } 1236 1237 // process all the instance initializers 1238 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 1239 if (!l.head.hasTag(METHODDEF) && 1240 (TreeInfo.flags(l.head) & STATIC) == 0) { 1241 scan(l.head); 1242 errorUncaught(); 1243 } 1244 } 1245 1246 // in an anonymous class, add the set of thrown exceptions to 1247 // the throws clause of the synthetic constructor and propagate 1248 // outwards. 1249 // Changing the throws clause on the fly is okay here because 1250 // the anonymous constructor can't be invoked anywhere else, 1251 // and its type hasn't been cached. 1252 if (anonymousClass) { 1253 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 1254 if (TreeInfo.isConstructor(l.head)) { 1255 JCMethodDecl mdef = (JCMethodDecl)l.head; 1256 scan(mdef); 1257 mdef.thrown = make.Types(thrown); 1258 mdef.sym.type = types.createMethodTypeWithThrown(mdef.sym.type, thrown); 1259 } 1260 } 1261 thrownPrev = chk.union(thrown, thrownPrev); 1262 } 1263 1264 // process all the methods 1265 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 1266 if (anonymousClass && TreeInfo.isConstructor(l.head)) 1267 continue; // there can never be an uncaught exception. 1268 if (l.head.hasTag(METHODDEF)) { 1269 scan(l.head); 1270 errorUncaught(); 1271 } 1272 } 1273 1274 thrown = thrownPrev; 1275 } finally { 1276 pendingExits = pendingExitsPrev; 1277 caught = caughtPrev; 1278 classDef = classDefPrev; 1279 lint = lintPrev; 1280 } 1281 } 1282 1283 public void visitMethodDef(JCMethodDecl tree) { 1284 if (tree.body == null) return; 1285 1286 List<Type> caughtPrev = caught; 1287 List<Type> mthrown = tree.sym.type.getThrownTypes(); 1288 Lint lintPrev = lint; 1289 1290 lint = lint.augment(tree.sym); 1291 1292 Assert.check(pendingExits.isEmpty()); 1293 1294 try { 1295 for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { 1296 JCVariableDecl def = l.head; 1297 scan(def); 1298 } 1299 if (TreeInfo.isInitialConstructor(tree)) 1300 caught = chk.union(caught, mthrown); 1301 else if ((tree.sym.flags() & (BLOCK | STATIC)) != BLOCK) 1302 caught = mthrown; 1303 // else we are in an instance initializer block; 1304 // leave caught unchanged. 1305 1306 scan(tree.body); 1307 1308 List<PendingExit> exits = pendingExits.toList(); 1309 pendingExits = new ListBuffer<>(); 1310 while (exits.nonEmpty()) { 1311 PendingExit exit = exits.head; 1312 exits = exits.tail; 1313 if (!(exit instanceof ThrownPendingExit)) { 1314 Assert.check(exit.tree.hasTag(RETURN) || 1315 log.hasErrorOn(exit.tree.pos())); 1316 } else { 1317 // uncaught throws will be reported later 1318 pendingExits.append(exit); 1319 } 1320 } 1321 } finally { 1322 caught = caughtPrev; 1323 lint = lintPrev; 1324 } 1325 } 1326 1327 public void visitVarDef(JCVariableDecl tree) { 1328 if (tree.init != null) { 1329 Lint lintPrev = lint; 1330 lint = lint.augment(tree.sym); 1331 try{ 1332 scan(tree.init); 1333 } finally { 1334 lint = lintPrev; 1335 } 1336 } 1337 } 1338 1339 public void visitBlock(JCBlock tree) { 1340 scan(tree.stats); 1341 } 1342 1343 public void visitDoLoop(JCDoWhileLoop tree) { 1344 ListBuffer<PendingExit> prevPendingExits = pendingExits; 1345 pendingExits = new ListBuffer<>(); 1346 scan(tree.body); 1347 resolveContinues(tree); 1348 scan(tree.cond); 1349 resolveBreaks(tree, prevPendingExits); 1350 } 1351 1352 public void visitWhileLoop(JCWhileLoop tree) { 1353 ListBuffer<PendingExit> prevPendingExits = pendingExits; 1354 pendingExits = new ListBuffer<>(); 1355 scan(tree.cond); 1356 scan(tree.body); 1357 resolveContinues(tree); 1358 resolveBreaks(tree, prevPendingExits); 1359 } 1360 1361 public void visitForLoop(JCForLoop tree) { 1362 ListBuffer<PendingExit> prevPendingExits = pendingExits; 1363 scan(tree.init); 1364 pendingExits = new ListBuffer<>(); 1365 if (tree.cond != null) { 1366 scan(tree.cond); 1367 } 1368 scan(tree.body); 1369 resolveContinues(tree); 1370 scan(tree.step); 1371 resolveBreaks(tree, prevPendingExits); 1372 } 1373 1374 public void visitForeachLoop(JCEnhancedForLoop tree) { 1375 if(tree.varOrRecordPattern instanceof JCVariableDecl jcVariableDecl) { 1376 visitVarDef(jcVariableDecl); 1377 } else if (tree.varOrRecordPattern instanceof JCRecordPattern jcRecordPattern) { 1378 visitRecordPattern(jcRecordPattern); 1379 } 1380 ListBuffer<PendingExit> prevPendingExits = pendingExits; 1381 scan(tree.expr); 1382 pendingExits = new ListBuffer<>(); 1383 scan(tree.body); 1384 resolveContinues(tree); 1385 resolveBreaks(tree, prevPendingExits); 1386 } 1387 1388 public void visitLabelled(JCLabeledStatement tree) { 1389 ListBuffer<PendingExit> prevPendingExits = pendingExits; 1390 pendingExits = new ListBuffer<>(); 1391 scan(tree.body); 1392 resolveBreaks(tree, prevPendingExits); 1393 } 1394 1395 public void visitSwitch(JCSwitch tree) { 1396 handleSwitch(tree, tree.selector, tree.cases); 1397 } 1398 1399 @Override 1400 public void visitSwitchExpression(JCSwitchExpression tree) { 1401 handleSwitch(tree, tree.selector, tree.cases); 1402 } 1403 1404 private void handleSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) { 1405 ListBuffer<PendingExit> prevPendingExits = pendingExits; 1406 pendingExits = new ListBuffer<>(); 1407 scan(selector); 1408 for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) { 1409 JCCase c = l.head; 1410 scan(c.labels); 1411 scan(c.stats); 1412 } 1413 if (tree.hasTag(SWITCH_EXPRESSION)) { 1414 resolveYields(tree, prevPendingExits); 1415 } else { 1416 resolveBreaks(tree, prevPendingExits); 1417 } 1418 } 1419 1420 public void visitTry(JCTry tree) { 1421 List<Type> caughtPrev = caught; 1422 List<Type> thrownPrev = thrown; 1423 thrown = List.nil(); 1424 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { 1425 List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ? 1426 ((JCTypeUnion)l.head.param.vartype).alternatives : 1427 List.of(l.head.param.vartype); 1428 for (JCExpression ct : subClauses) { 1429 caught = chk.incl(ct.type, caught); 1430 } 1431 } 1432 1433 ListBuffer<PendingExit> prevPendingExits = pendingExits; 1434 pendingExits = new ListBuffer<>(); 1435 for (JCTree resource : tree.resources) { 1436 if (resource instanceof JCVariableDecl variableDecl) { 1437 visitVarDef(variableDecl); 1438 } else if (resource instanceof JCExpression expression) { 1439 scan(expression); 1440 } else { 1441 throw new AssertionError(tree); // parser error 1442 } 1443 } 1444 for (JCTree resource : tree.resources) { 1445 List<Type> closeableSupertypes = resource.type.isCompound() ? 1446 types.interfaces(resource.type).prepend(types.supertype(resource.type)) : 1447 List.of(resource.type); 1448 for (Type sup : closeableSupertypes) { 1449 if (types.asSuper(sup, syms.autoCloseableType.tsym) != null) { 1450 Symbol closeMethod = rs.resolveQualifiedMethod(tree, 1451 attrEnv, 1452 types.skipTypeVars(sup, false), 1453 names.close, 1454 List.nil(), 1455 List.nil()); 1456 Type mt = types.memberType(resource.type, closeMethod); 1457 if (closeMethod.kind == MTH) { 1458 for (Type t : mt.getThrownTypes()) { 1459 markThrown(resource, t); 1460 } 1461 } 1462 } 1463 } 1464 } 1465 scan(tree.body); 1466 List<Type> thrownInTry = chk.union(thrown, List.of(syms.runtimeExceptionType, syms.errorType)); 1467 thrown = thrownPrev; 1468 caught = caughtPrev; 1469 1470 List<Type> caughtInTry = List.nil(); 1471 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { 1472 JCVariableDecl param = l.head.param; 1473 List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ? 1474 ((JCTypeUnion)l.head.param.vartype).alternatives : 1475 List.of(l.head.param.vartype); 1476 List<Type> ctypes = List.nil(); 1477 List<Type> rethrownTypes = chk.diff(thrownInTry, caughtInTry); 1478 for (JCExpression ct : subClauses) { 1479 Type exc = ct.type; 1480 if (exc != syms.unknownType) { 1481 ctypes = ctypes.append(exc); 1482 if (types.isSameType(exc, syms.objectType)) 1483 continue; 1484 var pos = subClauses.size() > 1 ? ct.pos() : l.head.pos(); 1485 checkCaughtType(pos, exc, thrownInTry, caughtInTry); 1486 caughtInTry = chk.incl(exc, caughtInTry); 1487 } 1488 } 1489 scan(param); 1490 preciseRethrowTypes.put(param.sym, chk.intersect(ctypes, rethrownTypes)); 1491 scan(l.head.body); 1492 preciseRethrowTypes.remove(param.sym); 1493 } 1494 if (tree.finalizer != null) { 1495 List<Type> savedThrown = thrown; 1496 thrown = List.nil(); 1497 ListBuffer<PendingExit> exits = pendingExits; 1498 pendingExits = prevPendingExits; 1499 scan(tree.finalizer); 1500 if (!tree.finallyCanCompleteNormally) { 1501 // discard exits and exceptions from try and finally 1502 thrown = chk.union(thrown, thrownPrev); 1503 } else { 1504 thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry)); 1505 thrown = chk.union(thrown, savedThrown); 1506 // FIX: this doesn't preserve source order of exits in catch 1507 // versus finally! 1508 while (exits.nonEmpty()) { 1509 pendingExits.append(exits.next()); 1510 } 1511 } 1512 } else { 1513 thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry)); 1514 ListBuffer<PendingExit> exits = pendingExits; 1515 pendingExits = prevPendingExits; 1516 while (exits.nonEmpty()) pendingExits.append(exits.next()); 1517 } 1518 } 1519 1520 @Override 1521 public void visitIf(JCIf tree) { 1522 scan(tree.cond); 1523 scan(tree.thenpart); 1524 if (tree.elsepart != null) { 1525 scan(tree.elsepart); 1526 } 1527 } 1528 1529 void checkCaughtType(DiagnosticPosition pos, Type exc, List<Type> thrownInTry, List<Type> caughtInTry) { 1530 if (chk.subset(exc, caughtInTry)) { 1531 log.error(pos, Errors.ExceptAlreadyCaught(exc)); 1532 } else if (!chk.isUnchecked(pos, exc) && 1533 !isExceptionOrThrowable(exc) && 1534 !chk.intersects(exc, thrownInTry)) { 1535 log.error(pos, Errors.ExceptNeverThrownInTry(exc)); 1536 } else { 1537 List<Type> catchableThrownTypes = chk.intersect(List.of(exc), thrownInTry); 1538 // 'catchableThrownTypes' cannot possibly be empty - if 'exc' was an 1539 // unchecked exception, the result list would not be empty, as the augmented 1540 // thrown set includes { RuntimeException, Error }; if 'exc' was a checked 1541 // exception, that would have been covered in the branch above 1542 if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty() && 1543 !isExceptionOrThrowable(exc)) { 1544 Warning key = catchableThrownTypes.length() == 1 ? 1545 Warnings.UnreachableCatch(catchableThrownTypes) : 1546 Warnings.UnreachableCatch1(catchableThrownTypes); 1547 log.warning(pos, key); 1548 } 1549 } 1550 } 1551 //where 1552 private boolean isExceptionOrThrowable(Type exc) { 1553 return exc.tsym == syms.throwableType.tsym || 1554 exc.tsym == syms.exceptionType.tsym; 1555 } 1556 1557 public void visitBreak(JCBreak tree) { 1558 recordExit(new PendingExit(tree)); 1559 } 1560 1561 public void visitYield(JCYield tree) { 1562 scan(tree.value); 1563 recordExit(new PendingExit(tree)); 1564 } 1565 1566 public void visitContinue(JCContinue tree) { 1567 recordExit(new PendingExit(tree)); 1568 } 1569 1570 public void visitReturn(JCReturn tree) { 1571 scan(tree.expr); 1572 recordExit(new PendingExit(tree)); 1573 } 1574 1575 public void visitThrow(JCThrow tree) { 1576 scan(tree.expr); 1577 Symbol sym = TreeInfo.symbol(tree.expr); 1578 if (sym != null && 1579 sym.kind == VAR && 1580 (sym.flags() & (FINAL | EFFECTIVELY_FINAL)) != 0 && 1581 preciseRethrowTypes.get(sym) != null) { 1582 for (Type t : preciseRethrowTypes.get(sym)) { 1583 markThrown(tree, t); 1584 } 1585 } 1586 else { 1587 markThrown(tree, tree.expr.type); 1588 } 1589 markDead(); 1590 } 1591 1592 public void visitApply(JCMethodInvocation tree) { 1593 scan(tree.meth); 1594 scan(tree.args); 1595 for (List<Type> l = tree.meth.type.getThrownTypes(); l.nonEmpty(); l = l.tail) 1596 markThrown(tree, l.head); 1597 } 1598 1599 public void visitNewClass(JCNewClass tree) { 1600 scan(tree.encl); 1601 scan(tree.args); 1602 // scan(tree.def); 1603 for (List<Type> l = tree.constructorType.getThrownTypes(); 1604 l.nonEmpty(); 1605 l = l.tail) { 1606 markThrown(tree, l.head); 1607 } 1608 List<Type> caughtPrev = caught; 1609 try { 1610 // If the new class expression defines an anonymous class, 1611 // analysis of the anonymous constructor may encounter thrown 1612 // types which are unsubstituted type variables. 1613 // However, since the constructor's actual thrown types have 1614 // already been marked as thrown, it is safe to simply include 1615 // each of the constructor's formal thrown types in the set of 1616 // 'caught/declared to be thrown' types, for the duration of 1617 // the class def analysis. 1618 if (tree.def != null) 1619 for (List<Type> l = tree.constructor.type.getThrownTypes(); 1620 l.nonEmpty(); 1621 l = l.tail) { 1622 caught = chk.incl(l.head, caught); 1623 } 1624 scan(tree.def); 1625 } 1626 finally { 1627 caught = caughtPrev; 1628 } 1629 } 1630 1631 @Override 1632 public void visitLambda(JCLambda tree) { 1633 if (tree.type != null && 1634 tree.type.isErroneous()) { 1635 return; 1636 } 1637 List<Type> prevCaught = caught; 1638 List<Type> prevThrown = thrown; 1639 ListBuffer<PendingExit> prevPending = pendingExits; 1640 try { 1641 pendingExits = new ListBuffer<>(); 1642 caught = tree.getDescriptorType(types).getThrownTypes(); 1643 thrown = List.nil(); 1644 scan(tree.body); 1645 List<PendingExit> exits = pendingExits.toList(); 1646 pendingExits = new ListBuffer<>(); 1647 while (exits.nonEmpty()) { 1648 PendingExit exit = exits.head; 1649 exits = exits.tail; 1650 if (!(exit instanceof ThrownPendingExit)) { 1651 Assert.check(exit.tree.hasTag(RETURN) || 1652 log.hasErrorOn(exit.tree.pos())); 1653 } else { 1654 // uncaught throws will be reported later 1655 pendingExits.append(exit); 1656 } 1657 } 1658 1659 errorUncaught(); 1660 } finally { 1661 pendingExits = prevPending; 1662 caught = prevCaught; 1663 thrown = prevThrown; 1664 } 1665 } 1666 1667 public void visitModuleDef(JCModuleDecl tree) { 1668 // Do nothing for modules 1669 } 1670 1671 /************************************************************************** 1672 * main method 1673 *************************************************************************/ 1674 1675 /** Perform definite assignment/unassignment analysis on a tree. 1676 */ 1677 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { 1678 analyzeTree(env, env.tree, make); 1679 } 1680 public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) { 1681 try { 1682 attrEnv = env; 1683 Flow.this.make = make; 1684 pendingExits = new ListBuffer<>(); 1685 preciseRethrowTypes = new HashMap<>(); 1686 this.thrown = this.caught = null; 1687 this.classDef = null; 1688 scan(tree); 1689 } finally { 1690 pendingExits = null; 1691 Flow.this.make = null; 1692 this.thrown = this.caught = null; 1693 this.classDef = null; 1694 } 1695 } 1696 } 1697 1698 /** 1699 * Specialized pass that performs reachability analysis on a lambda 1700 */ 1701 class LambdaAliveAnalyzer extends AliveAnalyzer { 1702 1703 boolean inLambda; 1704 1705 @Override 1706 public void visitReturn(JCReturn tree) { 1707 //ignore lambda return expression (which might not even be attributed) 1708 recordExit(new PendingExit(tree)); 1709 } 1710 1711 @Override 1712 public void visitLambda(JCLambda tree) { 1713 if (inLambda || tree.getBodyKind() == BodyKind.EXPRESSION) { 1714 return; 1715 } 1716 inLambda = true; 1717 try { 1718 super.visitLambda(tree); 1719 } finally { 1720 inLambda = false; 1721 } 1722 } 1723 1724 @Override 1725 public void visitClassDef(JCClassDecl tree) { 1726 //skip 1727 } 1728 } 1729 1730 /** 1731 * Determine if alive after the given tree. 1732 */ 1733 class SnippetAliveAnalyzer extends AliveAnalyzer { 1734 @Override 1735 public void visitClassDef(JCClassDecl tree) { 1736 //skip 1737 } 1738 @Override 1739 public void visitLambda(JCLambda tree) { 1740 //skip 1741 } 1742 public boolean isAlive() { 1743 return super.alive != Liveness.DEAD; 1744 } 1745 } 1746 1747 class SnippetBreakAnalyzer extends AliveAnalyzer { 1748 private final Set<JCTree> seenTrees = new HashSet<>(); 1749 private boolean breaksOut; 1750 1751 public SnippetBreakAnalyzer() { 1752 } 1753 1754 @Override 1755 public void visitLabelled(JCTree.JCLabeledStatement tree) { 1756 seenTrees.add(tree); 1757 super.visitLabelled(tree); 1758 } 1759 1760 @Override 1761 public void visitWhileLoop(JCTree.JCWhileLoop tree) { 1762 seenTrees.add(tree); 1763 super.visitWhileLoop(tree); 1764 } 1765 1766 @Override 1767 public void visitForLoop(JCTree.JCForLoop tree) { 1768 seenTrees.add(tree); 1769 super.visitForLoop(tree); 1770 } 1771 1772 @Override 1773 public void visitForeachLoop(JCTree.JCEnhancedForLoop tree) { 1774 seenTrees.add(tree); 1775 super.visitForeachLoop(tree); 1776 } 1777 1778 @Override 1779 public void visitDoLoop(JCTree.JCDoWhileLoop tree) { 1780 seenTrees.add(tree); 1781 super.visitDoLoop(tree); 1782 } 1783 1784 @Override 1785 public void visitBreak(JCBreak tree) { 1786 breaksOut |= (super.alive == Liveness.ALIVE && 1787 !seenTrees.contains(tree.target)); 1788 super.visitBreak(tree); 1789 } 1790 1791 public boolean breaksOut() { 1792 return breaksOut; 1793 } 1794 } 1795 1796 /** 1797 * Specialized pass that performs DA/DU on a lambda 1798 */ 1799 class LambdaAssignAnalyzer extends AssignAnalyzer { 1800 WriteableScope enclosedSymbols; 1801 boolean inLambda; 1802 1803 LambdaAssignAnalyzer(Env<AttrContext> env) { 1804 enclosedSymbols = WriteableScope.create(env.enclClass.sym); 1805 } 1806 1807 @Override 1808 public void visitLambda(JCLambda tree) { 1809 if (inLambda) { 1810 return; 1811 } 1812 inLambda = true; 1813 try { 1814 super.visitLambda(tree); 1815 } finally { 1816 inLambda = false; 1817 } 1818 } 1819 1820 @Override 1821 public void visitVarDef(JCVariableDecl tree) { 1822 enclosedSymbols.enter(tree.sym); 1823 super.visitVarDef(tree); 1824 } 1825 @Override 1826 protected boolean trackable(VarSymbol sym) { 1827 return enclosedSymbols.includes(sym) && 1828 sym.owner.kind == MTH; 1829 } 1830 1831 @Override 1832 public void visitClassDef(JCClassDecl tree) { 1833 //skip 1834 } 1835 } 1836 1837 /** 1838 * Specialized pass that performs inference of thrown types for lambdas. 1839 */ 1840 class LambdaFlowAnalyzer extends FlowAnalyzer { 1841 List<Type> inferredThrownTypes; 1842 boolean inLambda; 1843 @Override 1844 public void visitLambda(JCLambda tree) { 1845 if ((tree.type != null && 1846 tree.type.isErroneous()) || inLambda) { 1847 return; 1848 } 1849 List<Type> prevCaught = caught; 1850 List<Type> prevThrown = thrown; 1851 ListBuffer<PendingExit> prevPending = pendingExits; 1852 inLambda = true; 1853 try { 1854 pendingExits = new ListBuffer<>(); 1855 caught = List.of(syms.throwableType); 1856 thrown = List.nil(); 1857 scan(tree.body); 1858 inferredThrownTypes = thrown; 1859 } finally { 1860 pendingExits = prevPending; 1861 caught = prevCaught; 1862 thrown = prevThrown; 1863 inLambda = false; 1864 } 1865 } 1866 @Override 1867 public void visitClassDef(JCClassDecl tree) { 1868 //skip 1869 } 1870 } 1871 1872 /** 1873 * This pass implements (i) definite assignment analysis, which ensures that 1874 * each variable is assigned when used and (ii) definite unassignment analysis, 1875 * which ensures that no final variable is assigned more than once. This visitor 1876 * depends on the results of the liveliness analyzer. This pass is also used to mark 1877 * effectively-final local variables/parameters. 1878 */ 1879 1880 public class AssignAnalyzer extends BaseAnalyzer { 1881 1882 /** The set of definitely assigned variables. 1883 */ 1884 final Bits inits; 1885 1886 /** The set of definitely unassigned variables. 1887 */ 1888 final Bits uninits; 1889 1890 /** The set of variables that are definitely unassigned everywhere 1891 * in current try block. This variable is maintained lazily; it is 1892 * updated only when something gets removed from uninits, 1893 * typically by being assigned in reachable code. To obtain the 1894 * correct set of variables which are definitely unassigned 1895 * anywhere in current try block, intersect uninitsTry and 1896 * uninits. 1897 */ 1898 final Bits uninitsTry; 1899 1900 /** When analyzing a condition, inits and uninits are null. 1901 * Instead we have: 1902 */ 1903 final Bits initsWhenTrue; 1904 final Bits initsWhenFalse; 1905 final Bits uninitsWhenTrue; 1906 final Bits uninitsWhenFalse; 1907 1908 /** A mapping from addresses to variable symbols. 1909 */ 1910 protected JCVariableDecl[] vardecls; 1911 1912 /** The current class being defined. 1913 */ 1914 JCClassDecl classDef; 1915 1916 /** The first variable sequence number in this class definition. 1917 */ 1918 int firstadr; 1919 1920 /** The next available variable sequence number. 1921 */ 1922 protected int nextadr; 1923 1924 /** The first variable sequence number in a block that can return. 1925 */ 1926 protected int returnadr; 1927 1928 /** The list of unreferenced automatic resources. 1929 */ 1930 WriteableScope unrefdResources; 1931 1932 /** Modified when processing a loop body the second time for DU analysis. */ 1933 FlowKind flowKind = FlowKind.NORMAL; 1934 1935 /** The starting position of the analyzed tree */ 1936 int startPos; 1937 1938 public class AssignPendingExit extends BaseAnalyzer.PendingExit { 1939 1940 final Bits inits; 1941 final Bits uninits; 1942 final Bits exit_inits = new Bits(true); 1943 final Bits exit_uninits = new Bits(true); 1944 1945 public AssignPendingExit(JCTree tree, final Bits inits, final Bits uninits) { 1946 super(tree); 1947 this.inits = inits; 1948 this.uninits = uninits; 1949 this.exit_inits.assign(inits); 1950 this.exit_uninits.assign(uninits); 1951 } 1952 1953 @Override 1954 public void resolveJump() { 1955 inits.andSet(exit_inits); 1956 uninits.andSet(exit_uninits); 1957 } 1958 } 1959 1960 public AssignAnalyzer() { 1961 this.inits = new Bits(); 1962 uninits = new Bits(); 1963 uninitsTry = new Bits(); 1964 initsWhenTrue = new Bits(true); 1965 initsWhenFalse = new Bits(true); 1966 uninitsWhenTrue = new Bits(true); 1967 uninitsWhenFalse = new Bits(true); 1968 } 1969 1970 private boolean isInitialConstructor = false; 1971 1972 @Override 1973 protected void markDead() { 1974 if (!isInitialConstructor) { 1975 inits.inclRange(returnadr, nextadr); 1976 } else { 1977 for (int address = returnadr; address < nextadr; address++) { 1978 if (!(isFinalUninitializedStaticField(vardecls[address].sym))) { 1979 inits.incl(address); 1980 } 1981 } 1982 } 1983 uninits.inclRange(returnadr, nextadr); 1984 } 1985 1986 /*-------------- Processing variables ----------------------*/ 1987 1988 /** Do we need to track init/uninit state of this symbol? 1989 * I.e. is symbol either a local or a blank final variable? 1990 */ 1991 protected boolean trackable(VarSymbol sym) { 1992 return 1993 sym.pos >= startPos && 1994 ((sym.owner.kind == MTH || sym.owner.kind == VAR || 1995 isFinalUninitializedField(sym))); 1996 } 1997 1998 boolean isFinalUninitializedField(VarSymbol sym) { 1999 return sym.owner.kind == TYP && 2000 ((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL && 2001 classDef.sym.isEnclosedBy((ClassSymbol)sym.owner)); 2002 } 2003 2004 boolean isFinalUninitializedStaticField(VarSymbol sym) { 2005 return isFinalUninitializedField(sym) && sym.isStatic(); 2006 } 2007 2008 /** Initialize new trackable variable by setting its address field 2009 * to the next available sequence number and entering it under that 2010 * index into the vars array. 2011 */ 2012 void newVar(JCVariableDecl varDecl) { 2013 VarSymbol sym = varDecl.sym; 2014 vardecls = ArrayUtils.ensureCapacity(vardecls, nextadr); 2015 if ((sym.flags() & FINAL) == 0) { 2016 sym.flags_field |= EFFECTIVELY_FINAL; 2017 } 2018 sym.adr = nextadr; 2019 vardecls[nextadr] = varDecl; 2020 inits.excl(nextadr); 2021 uninits.incl(nextadr); 2022 nextadr++; 2023 } 2024 2025 /** Record an initialization of a trackable variable. 2026 */ 2027 void letInit(DiagnosticPosition pos, VarSymbol sym) { 2028 if (sym.adr >= firstadr && trackable(sym)) { 2029 if ((sym.flags() & EFFECTIVELY_FINAL) != 0) { 2030 if (inits.isMember(sym.adr) || !uninits.isMember(sym.adr)) { 2031 //assignment targeting an effectively final variable makes the 2032 //variable lose its status of effectively final if the variable 2033 //is definitely assigned or _not_ definitively unassigned 2034 sym.flags_field &= ~EFFECTIVELY_FINAL; 2035 } 2036 uninit(sym); 2037 } 2038 else if ((sym.flags() & FINAL) != 0) { 2039 if ((sym.flags() & PARAMETER) != 0) { 2040 if ((sym.flags() & UNION) != 0) { //multi-catch parameter 2041 log.error(pos, Errors.MulticatchParameterMayNotBeAssigned(sym)); 2042 } 2043 else { 2044 log.error(pos, 2045 Errors.FinalParameterMayNotBeAssigned(sym)); 2046 } 2047 } else if (!uninits.isMember(sym.adr)) { 2048 log.error(pos, diags.errorKey(flowKind.errKey, sym)); 2049 } else { 2050 uninit(sym); 2051 } 2052 } 2053 inits.incl(sym.adr); 2054 } else if ((sym.flags() & FINAL) != 0) { 2055 log.error(pos, Errors.VarMightAlreadyBeAssigned(sym)); 2056 } 2057 } 2058 //where 2059 void uninit(VarSymbol sym) { 2060 if (!inits.isMember(sym.adr)) { 2061 // reachable assignment 2062 uninits.excl(sym.adr); 2063 uninitsTry.excl(sym.adr); 2064 } else { 2065 //log.rawWarning(pos, "unreachable assignment");//DEBUG 2066 uninits.excl(sym.adr); 2067 } 2068 } 2069 2070 /** If tree is either a simple name or of the form this.name or 2071 * C.this.name, and tree represents a trackable variable, 2072 * record an initialization of the variable. 2073 */ 2074 void letInit(JCTree tree) { 2075 tree = TreeInfo.skipParens(tree); 2076 if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) { 2077 Symbol sym = TreeInfo.symbol(tree); 2078 if (sym.kind == VAR) { 2079 letInit(tree.pos(), (VarSymbol)sym); 2080 } 2081 } 2082 } 2083 2084 /** Check that trackable variable is initialized. 2085 */ 2086 void checkInit(DiagnosticPosition pos, VarSymbol sym) { 2087 checkInit(pos, sym, Errors.VarMightNotHaveBeenInitialized(sym)); 2088 } 2089 2090 void checkInit(DiagnosticPosition pos, VarSymbol sym, Error errkey) { 2091 if ((sym.adr >= firstadr || sym.owner.kind != TYP) && 2092 trackable(sym) && 2093 !inits.isMember(sym.adr) && 2094 (sym.flags_field & CLASH) == 0) { 2095 log.error(pos, errkey); 2096 inits.incl(sym.adr); 2097 } 2098 } 2099 2100 /** Utility method to reset several Bits instances. 2101 */ 2102 private void resetBits(Bits... bits) { 2103 for (Bits b : bits) { 2104 b.reset(); 2105 } 2106 } 2107 2108 /** Split (duplicate) inits/uninits into WhenTrue/WhenFalse sets 2109 */ 2110 void split(boolean setToNull) { 2111 initsWhenFalse.assign(inits); 2112 uninitsWhenFalse.assign(uninits); 2113 initsWhenTrue.assign(inits); 2114 uninitsWhenTrue.assign(uninits); 2115 if (setToNull) { 2116 resetBits(inits, uninits); 2117 } 2118 } 2119 2120 /** Merge (intersect) inits/uninits from WhenTrue/WhenFalse sets. 2121 */ 2122 protected void merge() { 2123 inits.assign(initsWhenFalse.andSet(initsWhenTrue)); 2124 uninits.assign(uninitsWhenFalse.andSet(uninitsWhenTrue)); 2125 } 2126 2127 /* ************************************************************************ 2128 * Visitor methods for statements and definitions 2129 *************************************************************************/ 2130 2131 /** Analyze an expression. Make sure to set (un)inits rather than 2132 * (un)initsWhenTrue(WhenFalse) on exit. 2133 */ 2134 void scanExpr(JCTree tree) { 2135 if (tree != null) { 2136 scan(tree); 2137 if (inits.isReset()) { 2138 merge(); 2139 } 2140 } 2141 } 2142 2143 /** Analyze a list of expressions. 2144 */ 2145 void scanExprs(List<? extends JCExpression> trees) { 2146 if (trees != null) 2147 for (List<? extends JCExpression> l = trees; l.nonEmpty(); l = l.tail) 2148 scanExpr(l.head); 2149 } 2150 2151 void scanPattern(JCTree tree) { 2152 scan(tree); 2153 if (inits.isReset()) { 2154 inits.assign(initsWhenTrue); 2155 uninits.assign(uninitsWhenTrue); 2156 } 2157 } 2158 2159 /** Analyze a condition. Make sure to set (un)initsWhenTrue(WhenFalse) 2160 * rather than (un)inits on exit. 2161 */ 2162 void scanCond(JCTree tree) { 2163 if (tree.type.isFalse()) { 2164 if (inits.isReset()) merge(); 2165 initsWhenTrue.assign(inits); 2166 initsWhenTrue.inclRange(firstadr, nextadr); 2167 uninitsWhenTrue.assign(uninits); 2168 uninitsWhenTrue.inclRange(firstadr, nextadr); 2169 initsWhenFalse.assign(inits); 2170 uninitsWhenFalse.assign(uninits); 2171 } else if (tree.type.isTrue()) { 2172 if (inits.isReset()) merge(); 2173 initsWhenFalse.assign(inits); 2174 initsWhenFalse.inclRange(firstadr, nextadr); 2175 uninitsWhenFalse.assign(uninits); 2176 uninitsWhenFalse.inclRange(firstadr, nextadr); 2177 initsWhenTrue.assign(inits); 2178 uninitsWhenTrue.assign(uninits); 2179 } else { 2180 scan(tree); 2181 if (!inits.isReset()) 2182 split(tree.type != syms.unknownType); 2183 } 2184 if (tree.type != syms.unknownType) { 2185 resetBits(inits, uninits); 2186 } 2187 } 2188 2189 /* ------------ Visitor methods for various sorts of trees -------------*/ 2190 2191 public void visitClassDef(JCClassDecl tree) { 2192 if (tree.sym == null) { 2193 return; 2194 } 2195 2196 Lint lintPrev = lint; 2197 lint = lint.augment(tree.sym); 2198 try { 2199 JCClassDecl classDefPrev = classDef; 2200 int firstadrPrev = firstadr; 2201 int nextadrPrev = nextadr; 2202 ListBuffer<PendingExit> pendingExitsPrev = pendingExits; 2203 2204 pendingExits = new ListBuffer<>(); 2205 if (tree.name != names.empty) { 2206 firstadr = nextadr; 2207 } 2208 classDef = tree; 2209 try { 2210 // define all the static fields 2211 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 2212 if (l.head.hasTag(VARDEF)) { 2213 JCVariableDecl def = (JCVariableDecl)l.head; 2214 if ((def.mods.flags & STATIC) != 0) { 2215 VarSymbol sym = def.sym; 2216 if (trackable(sym)) { 2217 newVar(def); 2218 } 2219 } 2220 } 2221 } 2222 2223 // process all the static initializers 2224 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 2225 if (!l.head.hasTag(METHODDEF) && 2226 (TreeInfo.flags(l.head) & STATIC) != 0) { 2227 scan(l.head); 2228 clearPendingExits(false); 2229 } 2230 } 2231 2232 // define all the instance fields 2233 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 2234 if (l.head.hasTag(VARDEF)) { 2235 JCVariableDecl def = (JCVariableDecl)l.head; 2236 if ((def.mods.flags & STATIC) == 0) { 2237 VarSymbol sym = def.sym; 2238 if (trackable(sym)) { 2239 newVar(def); 2240 } 2241 } 2242 } 2243 } 2244 2245 // process all the instance initializers 2246 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 2247 if (!l.head.hasTag(METHODDEF) && 2248 (TreeInfo.flags(l.head) & STATIC) == 0) { 2249 scan(l.head); 2250 clearPendingExits(false); 2251 } 2252 } 2253 2254 // process all the methods 2255 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 2256 if (l.head.hasTag(METHODDEF)) { 2257 scan(l.head); 2258 } 2259 } 2260 } finally { 2261 pendingExits = pendingExitsPrev; 2262 nextadr = nextadrPrev; 2263 firstadr = firstadrPrev; 2264 classDef = classDefPrev; 2265 } 2266 } finally { 2267 lint = lintPrev; 2268 } 2269 } 2270 2271 public void visitMethodDef(JCMethodDecl tree) { 2272 if (tree.body == null) { 2273 return; 2274 } 2275 2276 /* MemberEnter can generate synthetic methods ignore them 2277 */ 2278 if ((tree.sym.flags() & SYNTHETIC) != 0) { 2279 return; 2280 } 2281 2282 Lint lintPrev = lint; 2283 lint = lint.augment(tree.sym); 2284 try { 2285 final Bits initsPrev = new Bits(inits); 2286 final Bits uninitsPrev = new Bits(uninits); 2287 int nextadrPrev = nextadr; 2288 int firstadrPrev = firstadr; 2289 int returnadrPrev = returnadr; 2290 2291 Assert.check(pendingExits.isEmpty()); 2292 boolean lastInitialConstructor = isInitialConstructor; 2293 try { 2294 isInitialConstructor = TreeInfo.isInitialConstructor(tree); 2295 2296 if (!isInitialConstructor) { 2297 firstadr = nextadr; 2298 } 2299 for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { 2300 JCVariableDecl def = l.head; 2301 scan(def); 2302 Assert.check((def.sym.flags() & PARAMETER) != 0, "Method parameter without PARAMETER flag"); 2303 /* If we are executing the code from Gen, then there can be 2304 * synthetic or mandated variables, ignore them. 2305 */ 2306 initParam(def); 2307 } 2308 // else we are in an instance initializer block; 2309 // leave caught unchanged. 2310 scan(tree.body); 2311 2312 boolean isCompactOrGeneratedRecordConstructor = (tree.sym.flags() & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0 || 2313 (tree.sym.flags() & (GENERATEDCONSTR | RECORD)) == (GENERATEDCONSTR | RECORD); 2314 if (isInitialConstructor) { 2315 boolean isSynthesized = (tree.sym.flags() & 2316 GENERATEDCONSTR) != 0; 2317 for (int i = firstadr; i < nextadr; i++) { 2318 JCVariableDecl vardecl = vardecls[i]; 2319 VarSymbol var = vardecl.sym; 2320 if (var.owner == classDef.sym) { 2321 // choose the diagnostic position based on whether 2322 // the ctor is default(synthesized) or not 2323 if (isSynthesized && !isCompactOrGeneratedRecordConstructor) { 2324 checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), 2325 var, Errors.VarNotInitializedInDefaultConstructor(var)); 2326 } else if (isCompactOrGeneratedRecordConstructor) { 2327 boolean isInstanceRecordField = var.enclClass().isRecord() && 2328 (var.flags_field & (Flags.PRIVATE | Flags.FINAL | Flags.GENERATED_MEMBER | Flags.RECORD)) != 0 && 2329 !var.isStatic() && 2330 var.owner.kind == TYP; 2331 if (isInstanceRecordField) { 2332 boolean notInitialized = !inits.isMember(var.adr); 2333 if (notInitialized && uninits.isMember(var.adr) && tree.completesNormally) { 2334 /* this way we indicate Lower that it should generate an initialization for this field 2335 * in the compact constructor 2336 */ 2337 var.flags_field |= UNINITIALIZED_FIELD; 2338 } else { 2339 checkInit(TreeInfo.diagEndPos(tree.body), var); 2340 } 2341 } else { 2342 checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), var); 2343 } 2344 } else { 2345 checkInit(TreeInfo.diagEndPos(tree.body), var); 2346 } 2347 } 2348 } 2349 } 2350 clearPendingExits(true); 2351 } finally { 2352 inits.assign(initsPrev); 2353 uninits.assign(uninitsPrev); 2354 nextadr = nextadrPrev; 2355 firstadr = firstadrPrev; 2356 returnadr = returnadrPrev; 2357 isInitialConstructor = lastInitialConstructor; 2358 } 2359 } finally { 2360 lint = lintPrev; 2361 } 2362 } 2363 2364 private void clearPendingExits(boolean inMethod) { 2365 List<PendingExit> exits = pendingExits.toList(); 2366 pendingExits = new ListBuffer<>(); 2367 while (exits.nonEmpty()) { 2368 PendingExit exit = exits.head; 2369 exits = exits.tail; 2370 Assert.check((inMethod && exit.tree.hasTag(RETURN)) || 2371 log.hasErrorOn(exit.tree.pos()), 2372 exit.tree); 2373 if (inMethod && isInitialConstructor) { 2374 Assert.check(exit instanceof AssignPendingExit); 2375 inits.assign(((AssignPendingExit) exit).exit_inits); 2376 for (int i = firstadr; i < nextadr; i++) { 2377 checkInit(exit.tree.pos(), vardecls[i].sym); 2378 } 2379 } 2380 } 2381 } 2382 protected void initParam(JCVariableDecl def) { 2383 inits.incl(def.sym.adr); 2384 uninits.excl(def.sym.adr); 2385 } 2386 2387 public void visitVarDef(JCVariableDecl tree) { 2388 Lint lintPrev = lint; 2389 lint = lint.augment(tree.sym); 2390 try{ 2391 boolean track = trackable(tree.sym); 2392 if (track && (tree.sym.owner.kind == MTH || tree.sym.owner.kind == VAR)) { 2393 newVar(tree); 2394 } 2395 if (tree.init != null) { 2396 scanExpr(tree.init); 2397 if (track) { 2398 letInit(tree.pos(), tree.sym); 2399 } 2400 } 2401 } finally { 2402 lint = lintPrev; 2403 } 2404 } 2405 2406 public void visitBlock(JCBlock tree) { 2407 int nextadrPrev = nextadr; 2408 scan(tree.stats); 2409 nextadr = nextadrPrev; 2410 } 2411 2412 public void visitDoLoop(JCDoWhileLoop tree) { 2413 ListBuffer<PendingExit> prevPendingExits = pendingExits; 2414 FlowKind prevFlowKind = flowKind; 2415 flowKind = FlowKind.NORMAL; 2416 final Bits initsSkip = new Bits(true); 2417 final Bits uninitsSkip = new Bits(true); 2418 pendingExits = new ListBuffer<>(); 2419 int prevErrors = log.nerrors; 2420 do { 2421 final Bits uninitsEntry = new Bits(uninits); 2422 uninitsEntry.excludeFrom(nextadr); 2423 scan(tree.body); 2424 resolveContinues(tree); 2425 scanCond(tree.cond); 2426 if (!flowKind.isFinal()) { 2427 initsSkip.assign(initsWhenFalse); 2428 uninitsSkip.assign(uninitsWhenFalse); 2429 } 2430 if (log.nerrors != prevErrors || 2431 flowKind.isFinal() || 2432 new Bits(uninitsEntry).diffSet(uninitsWhenTrue).nextBit(firstadr)==-1) 2433 break; 2434 inits.assign(initsWhenTrue); 2435 uninits.assign(uninitsEntry.andSet(uninitsWhenTrue)); 2436 flowKind = FlowKind.SPECULATIVE_LOOP; 2437 } while (true); 2438 flowKind = prevFlowKind; 2439 inits.assign(initsSkip); 2440 uninits.assign(uninitsSkip); 2441 resolveBreaks(tree, prevPendingExits); 2442 } 2443 2444 public void visitWhileLoop(JCWhileLoop tree) { 2445 ListBuffer<PendingExit> prevPendingExits = pendingExits; 2446 FlowKind prevFlowKind = flowKind; 2447 flowKind = FlowKind.NORMAL; 2448 final Bits initsSkip = new Bits(true); 2449 final Bits uninitsSkip = new Bits(true); 2450 pendingExits = new ListBuffer<>(); 2451 int prevErrors = log.nerrors; 2452 final Bits uninitsEntry = new Bits(uninits); 2453 uninitsEntry.excludeFrom(nextadr); 2454 do { 2455 scanCond(tree.cond); 2456 if (!flowKind.isFinal()) { 2457 initsSkip.assign(initsWhenFalse) ; 2458 uninitsSkip.assign(uninitsWhenFalse); 2459 } 2460 inits.assign(initsWhenTrue); 2461 uninits.assign(uninitsWhenTrue); 2462 scan(tree.body); 2463 resolveContinues(tree); 2464 if (log.nerrors != prevErrors || 2465 flowKind.isFinal() || 2466 new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1) { 2467 break; 2468 } 2469 uninits.assign(uninitsEntry.andSet(uninits)); 2470 flowKind = FlowKind.SPECULATIVE_LOOP; 2471 } while (true); 2472 flowKind = prevFlowKind; 2473 //a variable is DA/DU after the while statement, if it's DA/DU assuming the 2474 //branch is not taken AND if it's DA/DU before any break statement 2475 inits.assign(initsSkip); 2476 uninits.assign(uninitsSkip); 2477 resolveBreaks(tree, prevPendingExits); 2478 } 2479 2480 public void visitForLoop(JCForLoop tree) { 2481 ListBuffer<PendingExit> prevPendingExits = pendingExits; 2482 FlowKind prevFlowKind = flowKind; 2483 flowKind = FlowKind.NORMAL; 2484 int nextadrPrev = nextadr; 2485 scan(tree.init); 2486 final Bits initsSkip = new Bits(true); 2487 final Bits uninitsSkip = new Bits(true); 2488 pendingExits = new ListBuffer<>(); 2489 int prevErrors = log.nerrors; 2490 do { 2491 final Bits uninitsEntry = new Bits(uninits); 2492 uninitsEntry.excludeFrom(nextadr); 2493 if (tree.cond != null) { 2494 scanCond(tree.cond); 2495 if (!flowKind.isFinal()) { 2496 initsSkip.assign(initsWhenFalse); 2497 uninitsSkip.assign(uninitsWhenFalse); 2498 } 2499 inits.assign(initsWhenTrue); 2500 uninits.assign(uninitsWhenTrue); 2501 } else if (!flowKind.isFinal()) { 2502 initsSkip.assign(inits); 2503 initsSkip.inclRange(firstadr, nextadr); 2504 uninitsSkip.assign(uninits); 2505 uninitsSkip.inclRange(firstadr, nextadr); 2506 } 2507 scan(tree.body); 2508 resolveContinues(tree); 2509 scan(tree.step); 2510 if (log.nerrors != prevErrors || 2511 flowKind.isFinal() || 2512 new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1) 2513 break; 2514 uninits.assign(uninitsEntry.andSet(uninits)); 2515 flowKind = FlowKind.SPECULATIVE_LOOP; 2516 } while (true); 2517 flowKind = prevFlowKind; 2518 //a variable is DA/DU after a for loop, if it's DA/DU assuming the 2519 //branch is not taken AND if it's DA/DU before any break statement 2520 inits.assign(initsSkip); 2521 uninits.assign(uninitsSkip); 2522 resolveBreaks(tree, prevPendingExits); 2523 nextadr = nextadrPrev; 2524 } 2525 2526 public void visitForeachLoop(JCEnhancedForLoop tree) { 2527 ListBuffer<PendingExit> prevPendingExits = pendingExits; 2528 FlowKind prevFlowKind = flowKind; 2529 flowKind = FlowKind.NORMAL; 2530 int nextadrPrev = nextadr; 2531 scan(tree.expr); 2532 final Bits initsStart = new Bits(inits); 2533 final Bits uninitsStart = new Bits(uninits); 2534 2535 if(tree.varOrRecordPattern instanceof JCVariableDecl jcVariableDecl) { 2536 visitVarDef(jcVariableDecl); 2537 letInit(tree.pos(), jcVariableDecl.sym); 2538 } else if (tree.varOrRecordPattern instanceof JCRecordPattern jcRecordPattern) { 2539 visitRecordPattern(jcRecordPattern); 2540 } 2541 2542 pendingExits = new ListBuffer<>(); 2543 int prevErrors = log.nerrors; 2544 do { 2545 final Bits uninitsEntry = new Bits(uninits); 2546 uninitsEntry.excludeFrom(nextadr); 2547 scan(tree.body); 2548 resolveContinues(tree); 2549 if (log.nerrors != prevErrors || 2550 flowKind.isFinal() || 2551 new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1) 2552 break; 2553 uninits.assign(uninitsEntry.andSet(uninits)); 2554 flowKind = FlowKind.SPECULATIVE_LOOP; 2555 } while (true); 2556 flowKind = prevFlowKind; 2557 inits.assign(initsStart); 2558 uninits.assign(uninitsStart.andSet(uninits)); 2559 resolveBreaks(tree, prevPendingExits); 2560 nextadr = nextadrPrev; 2561 } 2562 2563 public void visitLabelled(JCLabeledStatement tree) { 2564 ListBuffer<PendingExit> prevPendingExits = pendingExits; 2565 pendingExits = new ListBuffer<>(); 2566 scan(tree.body); 2567 resolveBreaks(tree, prevPendingExits); 2568 } 2569 2570 public void visitSwitch(JCSwitch tree) { 2571 handleSwitch(tree, tree.selector, tree.cases, tree.isExhaustive); 2572 } 2573 2574 public void visitSwitchExpression(JCSwitchExpression tree) { 2575 handleSwitch(tree, tree.selector, tree.cases, tree.isExhaustive); 2576 } 2577 2578 private void handleSwitch(JCTree tree, JCExpression selector, 2579 List<JCCase> cases, boolean isExhaustive) { 2580 ListBuffer<PendingExit> prevPendingExits = pendingExits; 2581 pendingExits = new ListBuffer<>(); 2582 int nextadrPrev = nextadr; 2583 scanExpr(selector); 2584 final Bits initsSwitch = new Bits(inits); 2585 final Bits uninitsSwitch = new Bits(uninits); 2586 for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) { 2587 inits.assign(initsSwitch); 2588 uninits.assign(uninits.andSet(uninitsSwitch)); 2589 JCCase c = l.head; 2590 for (JCCaseLabel pat : c.labels) { 2591 scanPattern(pat); 2592 } 2593 if (l.head.stats.isEmpty() && 2594 l.tail.nonEmpty() && 2595 l.tail.head.labels.size() == 1 && 2596 TreeInfo.isNullCaseLabel(l.tail.head.labels.head)) { 2597 //handling: 2598 //case Integer i: 2599 //case null: 2600 //joining these two cases together - processing Integer i pattern, 2601 //but statements from case null: 2602 l = l.tail; 2603 c = l.head; 2604 } 2605 scan(c.stats); 2606 if (c.completesNormally && c.caseKind == JCCase.RULE) { 2607 scanSyntheticBreak(make, tree); 2608 } 2609 addVars(c.stats, initsSwitch, uninitsSwitch); 2610 // Warn about fall-through if lint switch fallthrough enabled. 2611 } 2612 if (!isExhaustive) { 2613 if (tree.hasTag(SWITCH_EXPRESSION)) { 2614 markDead(); 2615 } else if (tree.hasTag(SWITCH) && !TreeInfo.expectedExhaustive((JCSwitch) tree)) { 2616 inits.assign(initsSwitch); 2617 uninits.assign(uninits.andSet(uninitsSwitch)); 2618 } 2619 } 2620 if (tree.hasTag(SWITCH_EXPRESSION)) { 2621 resolveYields(tree, prevPendingExits); 2622 } else { 2623 resolveBreaks(tree, prevPendingExits); 2624 } 2625 nextadr = nextadrPrev; 2626 } 2627 // where 2628 /** Add any variables defined in stats to inits and uninits. */ 2629 private void addVars(List<JCStatement> stats, final Bits inits, 2630 final Bits uninits) { 2631 for (;stats.nonEmpty(); stats = stats.tail) { 2632 JCTree stat = stats.head; 2633 if (stat.hasTag(VARDEF)) { 2634 int adr = ((JCVariableDecl) stat).sym.adr; 2635 inits.excl(adr); 2636 uninits.incl(adr); 2637 } 2638 } 2639 } 2640 2641 public void visitTry(JCTry tree) { 2642 ListBuffer<JCVariableDecl> resourceVarDecls = new ListBuffer<>(); 2643 final Bits uninitsTryPrev = new Bits(uninitsTry); 2644 ListBuffer<PendingExit> prevPendingExits = pendingExits; 2645 pendingExits = new ListBuffer<>(); 2646 final Bits initsTry = new Bits(inits); 2647 uninitsTry.assign(uninits); 2648 for (JCTree resource : tree.resources) { 2649 if (resource instanceof JCVariableDecl variableDecl) { 2650 visitVarDef(variableDecl); 2651 unrefdResources.enter(variableDecl.sym); 2652 resourceVarDecls.append(variableDecl); 2653 } else if (resource instanceof JCExpression expression) { 2654 scanExpr(expression); 2655 } else { 2656 throw new AssertionError(tree); // parser error 2657 } 2658 } 2659 scan(tree.body); 2660 uninitsTry.andSet(uninits); 2661 final Bits initsEnd = new Bits(inits); 2662 final Bits uninitsEnd = new Bits(uninits); 2663 int nextadrCatch = nextadr; 2664 2665 if (!resourceVarDecls.isEmpty() && 2666 lint.isEnabled(Lint.LintCategory.TRY)) { 2667 for (JCVariableDecl resVar : resourceVarDecls) { 2668 if (unrefdResources.includes(resVar.sym)) { 2669 log.warning(Lint.LintCategory.TRY, resVar.pos(), 2670 Warnings.TryResourceNotReferenced(resVar.sym)); 2671 unrefdResources.remove(resVar.sym); 2672 } 2673 } 2674 } 2675 2676 /* The analysis of each catch should be independent. 2677 * Each one should have the same initial values of inits and 2678 * uninits. 2679 */ 2680 final Bits initsCatchPrev = new Bits(initsTry); 2681 final Bits uninitsCatchPrev = new Bits(uninitsTry); 2682 2683 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { 2684 JCVariableDecl param = l.head.param; 2685 inits.assign(initsCatchPrev); 2686 uninits.assign(uninitsCatchPrev); 2687 scan(param); 2688 /* If this is a TWR and we are executing the code from Gen, 2689 * then there can be synthetic variables, ignore them. 2690 */ 2691 initParam(param); 2692 scan(l.head.body); 2693 initsEnd.andSet(inits); 2694 uninitsEnd.andSet(uninits); 2695 nextadr = nextadrCatch; 2696 } 2697 if (tree.finalizer != null) { 2698 inits.assign(initsTry); 2699 uninits.assign(uninitsTry); 2700 ListBuffer<PendingExit> exits = pendingExits; 2701 pendingExits = prevPendingExits; 2702 scan(tree.finalizer); 2703 if (!tree.finallyCanCompleteNormally) { 2704 // discard exits and exceptions from try and finally 2705 } else { 2706 uninits.andSet(uninitsEnd); 2707 // FIX: this doesn't preserve source order of exits in catch 2708 // versus finally! 2709 while (exits.nonEmpty()) { 2710 PendingExit exit = exits.next(); 2711 if (exit instanceof AssignPendingExit assignPendingExit) { 2712 assignPendingExit.exit_inits.orSet(inits); 2713 assignPendingExit.exit_uninits.andSet(uninits); 2714 } 2715 pendingExits.append(exit); 2716 } 2717 inits.orSet(initsEnd); 2718 } 2719 } else { 2720 inits.assign(initsEnd); 2721 uninits.assign(uninitsEnd); 2722 ListBuffer<PendingExit> exits = pendingExits; 2723 pendingExits = prevPendingExits; 2724 while (exits.nonEmpty()) pendingExits.append(exits.next()); 2725 } 2726 uninitsTry.andSet(uninitsTryPrev).andSet(uninits); 2727 } 2728 2729 public void visitConditional(JCConditional tree) { 2730 scanCond(tree.cond); 2731 final Bits initsBeforeElse = new Bits(initsWhenFalse); 2732 final Bits uninitsBeforeElse = new Bits(uninitsWhenFalse); 2733 inits.assign(initsWhenTrue); 2734 uninits.assign(uninitsWhenTrue); 2735 if (tree.truepart.type.hasTag(BOOLEAN) && 2736 tree.falsepart.type.hasTag(BOOLEAN)) { 2737 // if b and c are boolean valued, then 2738 // v is (un)assigned after a?b:c when true iff 2739 // v is (un)assigned after b when true and 2740 // v is (un)assigned after c when true 2741 scanCond(tree.truepart); 2742 final Bits initsAfterThenWhenTrue = new Bits(initsWhenTrue); 2743 final Bits initsAfterThenWhenFalse = new Bits(initsWhenFalse); 2744 final Bits uninitsAfterThenWhenTrue = new Bits(uninitsWhenTrue); 2745 final Bits uninitsAfterThenWhenFalse = new Bits(uninitsWhenFalse); 2746 inits.assign(initsBeforeElse); 2747 uninits.assign(uninitsBeforeElse); 2748 scanCond(tree.falsepart); 2749 initsWhenTrue.andSet(initsAfterThenWhenTrue); 2750 initsWhenFalse.andSet(initsAfterThenWhenFalse); 2751 uninitsWhenTrue.andSet(uninitsAfterThenWhenTrue); 2752 uninitsWhenFalse.andSet(uninitsAfterThenWhenFalse); 2753 } else { 2754 scanExpr(tree.truepart); 2755 final Bits initsAfterThen = new Bits(inits); 2756 final Bits uninitsAfterThen = new Bits(uninits); 2757 inits.assign(initsBeforeElse); 2758 uninits.assign(uninitsBeforeElse); 2759 scanExpr(tree.falsepart); 2760 inits.andSet(initsAfterThen); 2761 uninits.andSet(uninitsAfterThen); 2762 } 2763 } 2764 2765 public void visitIf(JCIf tree) { 2766 scanCond(tree.cond); 2767 final Bits initsBeforeElse = new Bits(initsWhenFalse); 2768 final Bits uninitsBeforeElse = new Bits(uninitsWhenFalse); 2769 inits.assign(initsWhenTrue); 2770 uninits.assign(uninitsWhenTrue); 2771 scan(tree.thenpart); 2772 if (tree.elsepart != null) { 2773 final Bits initsAfterThen = new Bits(inits); 2774 final Bits uninitsAfterThen = new Bits(uninits); 2775 inits.assign(initsBeforeElse); 2776 uninits.assign(uninitsBeforeElse); 2777 scan(tree.elsepart); 2778 inits.andSet(initsAfterThen); 2779 uninits.andSet(uninitsAfterThen); 2780 } else { 2781 inits.andSet(initsBeforeElse); 2782 uninits.andSet(uninitsBeforeElse); 2783 } 2784 } 2785 2786 @Override 2787 public void visitBreak(JCBreak tree) { 2788 recordExit(new AssignPendingExit(tree, inits, uninits)); 2789 } 2790 2791 @Override 2792 public void visitYield(JCYield tree) { 2793 JCSwitchExpression expr = (JCSwitchExpression) tree.target; 2794 if (expr != null && expr.type.hasTag(BOOLEAN)) { 2795 scanCond(tree.value); 2796 Bits initsAfterBreakWhenTrue = new Bits(initsWhenTrue); 2797 Bits initsAfterBreakWhenFalse = new Bits(initsWhenFalse); 2798 Bits uninitsAfterBreakWhenTrue = new Bits(uninitsWhenTrue); 2799 Bits uninitsAfterBreakWhenFalse = new Bits(uninitsWhenFalse); 2800 PendingExit exit = new PendingExit(tree) { 2801 @Override 2802 void resolveJump() { 2803 if (!inits.isReset()) { 2804 split(true); 2805 } 2806 initsWhenTrue.andSet(initsAfterBreakWhenTrue); 2807 initsWhenFalse.andSet(initsAfterBreakWhenFalse); 2808 uninitsWhenTrue.andSet(uninitsAfterBreakWhenTrue); 2809 uninitsWhenFalse.andSet(uninitsAfterBreakWhenFalse); 2810 } 2811 }; 2812 merge(); 2813 recordExit(exit); 2814 return ; 2815 } else { 2816 scanExpr(tree.value); 2817 recordExit(new AssignPendingExit(tree, inits, uninits)); 2818 } 2819 } 2820 2821 @Override 2822 public void visitContinue(JCContinue tree) { 2823 recordExit(new AssignPendingExit(tree, inits, uninits)); 2824 } 2825 2826 @Override 2827 public void visitReturn(JCReturn tree) { 2828 scanExpr(tree.expr); 2829 recordExit(new AssignPendingExit(tree, inits, uninits)); 2830 } 2831 2832 public void visitThrow(JCThrow tree) { 2833 scanExpr(tree.expr); 2834 markDead(); 2835 } 2836 2837 public void visitApply(JCMethodInvocation tree) { 2838 scanExpr(tree.meth); 2839 scanExprs(tree.args); 2840 } 2841 2842 public void visitNewClass(JCNewClass tree) { 2843 scanExpr(tree.encl); 2844 scanExprs(tree.args); 2845 scan(tree.def); 2846 } 2847 2848 @Override 2849 public void visitLambda(JCLambda tree) { 2850 final Bits prevUninits = new Bits(uninits); 2851 final Bits prevInits = new Bits(inits); 2852 int returnadrPrev = returnadr; 2853 int nextadrPrev = nextadr; 2854 ListBuffer<PendingExit> prevPending = pendingExits; 2855 try { 2856 returnadr = nextadr; 2857 pendingExits = new ListBuffer<>(); 2858 for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { 2859 JCVariableDecl def = l.head; 2860 scan(def); 2861 inits.incl(def.sym.adr); 2862 uninits.excl(def.sym.adr); 2863 } 2864 if (tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION) { 2865 scanExpr(tree.body); 2866 } else { 2867 scan(tree.body); 2868 } 2869 } 2870 finally { 2871 returnadr = returnadrPrev; 2872 uninits.assign(prevUninits); 2873 inits.assign(prevInits); 2874 pendingExits = prevPending; 2875 nextadr = nextadrPrev; 2876 } 2877 } 2878 2879 public void visitNewArray(JCNewArray tree) { 2880 scanExprs(tree.dims); 2881 scanExprs(tree.elems); 2882 } 2883 2884 public void visitAssert(JCAssert tree) { 2885 final Bits initsExit = new Bits(inits); 2886 final Bits uninitsExit = new Bits(uninits); 2887 scanCond(tree.cond); 2888 uninitsExit.andSet(uninitsWhenTrue); 2889 if (tree.detail != null) { 2890 inits.assign(initsWhenFalse); 2891 uninits.assign(uninitsWhenFalse); 2892 scanExpr(tree.detail); 2893 } 2894 inits.assign(initsExit); 2895 uninits.assign(uninitsExit); 2896 } 2897 2898 public void visitAssign(JCAssign tree) { 2899 if (!TreeInfo.isIdentOrThisDotIdent(tree.lhs)) 2900 scanExpr(tree.lhs); 2901 scanExpr(tree.rhs); 2902 letInit(tree.lhs); 2903 } 2904 2905 // check fields accessed through this.<field> are definitely 2906 // assigned before reading their value 2907 public void visitSelect(JCFieldAccess tree) { 2908 super.visitSelect(tree); 2909 if (TreeInfo.isThisQualifier(tree.selected) && 2910 tree.sym.kind == VAR) { 2911 checkInit(tree.pos(), (VarSymbol)tree.sym); 2912 } 2913 } 2914 2915 public void visitAssignop(JCAssignOp tree) { 2916 scanExpr(tree.lhs); 2917 scanExpr(tree.rhs); 2918 letInit(tree.lhs); 2919 } 2920 2921 public void visitUnary(JCUnary tree) { 2922 switch (tree.getTag()) { 2923 case NOT: 2924 scanCond(tree.arg); 2925 final Bits t = new Bits(initsWhenFalse); 2926 initsWhenFalse.assign(initsWhenTrue); 2927 initsWhenTrue.assign(t); 2928 t.assign(uninitsWhenFalse); 2929 uninitsWhenFalse.assign(uninitsWhenTrue); 2930 uninitsWhenTrue.assign(t); 2931 break; 2932 case PREINC: case POSTINC: 2933 case PREDEC: case POSTDEC: 2934 scanExpr(tree.arg); 2935 letInit(tree.arg); 2936 break; 2937 default: 2938 scanExpr(tree.arg); 2939 } 2940 } 2941 2942 public void visitBinary(JCBinary tree) { 2943 switch (tree.getTag()) { 2944 case AND: 2945 scanCond(tree.lhs); 2946 final Bits initsWhenFalseLeft = new Bits(initsWhenFalse); 2947 final Bits uninitsWhenFalseLeft = new Bits(uninitsWhenFalse); 2948 inits.assign(initsWhenTrue); 2949 uninits.assign(uninitsWhenTrue); 2950 scanCond(tree.rhs); 2951 initsWhenFalse.andSet(initsWhenFalseLeft); 2952 uninitsWhenFalse.andSet(uninitsWhenFalseLeft); 2953 break; 2954 case OR: 2955 scanCond(tree.lhs); 2956 final Bits initsWhenTrueLeft = new Bits(initsWhenTrue); 2957 final Bits uninitsWhenTrueLeft = new Bits(uninitsWhenTrue); 2958 inits.assign(initsWhenFalse); 2959 uninits.assign(uninitsWhenFalse); 2960 scanCond(tree.rhs); 2961 initsWhenTrue.andSet(initsWhenTrueLeft); 2962 uninitsWhenTrue.andSet(uninitsWhenTrueLeft); 2963 break; 2964 default: 2965 scanExpr(tree.lhs); 2966 scanExpr(tree.rhs); 2967 } 2968 } 2969 2970 public void visitIdent(JCIdent tree) { 2971 if (tree.sym.kind == VAR) { 2972 checkInit(tree.pos(), (VarSymbol)tree.sym); 2973 referenced(tree.sym); 2974 } 2975 } 2976 2977 @Override 2978 public void visitTypeTest(JCInstanceOf tree) { 2979 scanExpr(tree.expr); 2980 scan(tree.pattern); 2981 } 2982 2983 @Override 2984 public void visitBindingPattern(JCBindingPattern tree) { 2985 scan(tree.var); 2986 initParam(tree.var); 2987 } 2988 2989 @Override 2990 public void visitPatternCaseLabel(JCPatternCaseLabel tree) { 2991 scan(tree.pat); 2992 scan(tree.guard); 2993 } 2994 2995 void referenced(Symbol sym) { 2996 unrefdResources.remove(sym); 2997 } 2998 2999 public void visitAnnotatedType(JCAnnotatedType tree) { 3000 // annotations don't get scanned 3001 tree.underlyingType.accept(this); 3002 } 3003 3004 public void visitModuleDef(JCModuleDecl tree) { 3005 // Do nothing for modules 3006 } 3007 3008 /************************************************************************** 3009 * main method 3010 *************************************************************************/ 3011 3012 /** Perform definite assignment/unassignment analysis on a tree. 3013 */ 3014 public void analyzeTree(Env<?> env, TreeMaker make) { 3015 analyzeTree(env, env.tree, make); 3016 } 3017 3018 public void analyzeTree(Env<?> env, JCTree tree, TreeMaker make) { 3019 try { 3020 startPos = tree.pos().getStartPosition(); 3021 3022 if (vardecls == null) 3023 vardecls = new JCVariableDecl[32]; 3024 else 3025 for (int i=0; i<vardecls.length; i++) 3026 vardecls[i] = null; 3027 firstadr = 0; 3028 nextadr = 0; 3029 Flow.this.make = make; 3030 pendingExits = new ListBuffer<>(); 3031 this.classDef = null; 3032 unrefdResources = WriteableScope.create(env.enclClass.sym); 3033 scan(tree); 3034 } finally { 3035 // note that recursive invocations of this method fail hard 3036 startPos = -1; 3037 resetBits(inits, uninits, uninitsTry, initsWhenTrue, 3038 initsWhenFalse, uninitsWhenTrue, uninitsWhenFalse); 3039 if (vardecls != null) { 3040 for (int i=0; i<vardecls.length; i++) 3041 vardecls[i] = null; 3042 } 3043 firstadr = 0; 3044 nextadr = 0; 3045 Flow.this.make = null; 3046 pendingExits = null; 3047 this.classDef = null; 3048 unrefdResources = null; 3049 } 3050 } 3051 } 3052 3053 /** 3054 * This pass implements the last step of the dataflow analysis, namely 3055 * the effectively-final analysis check. This checks that every local variable 3056 * reference from a lambda body/local inner class is either final or effectively final. 3057 * Additional this also checks that every variable that is used as an operand to 3058 * try-with-resources is final or effectively final. 3059 * As effectively final variables are marked as such during DA/DU, this pass must run after 3060 * AssignAnalyzer. 3061 */ 3062 class CaptureAnalyzer extends BaseAnalyzer { 3063 3064 JCTree currentTree; //local class or lambda 3065 3066 @Override 3067 void markDead() { 3068 //do nothing 3069 } 3070 3071 @SuppressWarnings("fallthrough") 3072 void checkEffectivelyFinal(DiagnosticPosition pos, VarSymbol sym) { 3073 if (currentTree != null && 3074 sym.owner.kind == MTH && 3075 sym.pos < currentTree.getStartPosition()) { 3076 switch (currentTree.getTag()) { 3077 case CLASSDEF: 3078 case PATTERNCASELABEL: 3079 case LAMBDA: 3080 if ((sym.flags() & (EFFECTIVELY_FINAL | FINAL)) == 0) { 3081 reportEffectivelyFinalError(pos, sym); 3082 } 3083 } 3084 } 3085 } 3086 3087 @SuppressWarnings("fallthrough") 3088 void letInit(JCTree tree) { 3089 tree = TreeInfo.skipParens(tree); 3090 if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) { 3091 Symbol sym = TreeInfo.symbol(tree); 3092 if (currentTree != null && 3093 sym.kind == VAR && 3094 sym.owner.kind == MTH && 3095 ((VarSymbol)sym).pos < currentTree.getStartPosition()) { 3096 switch (currentTree.getTag()) { 3097 case CLASSDEF: 3098 case CASE: 3099 case LAMBDA: 3100 reportEffectivelyFinalError(tree, sym); 3101 } 3102 } 3103 } 3104 } 3105 3106 void reportEffectivelyFinalError(DiagnosticPosition pos, Symbol sym) { 3107 Fragment subKey = switch (currentTree.getTag()) { 3108 case LAMBDA -> Fragments.Lambda; 3109 case PATTERNCASELABEL -> Fragments.Guard; 3110 case CLASSDEF -> Fragments.InnerCls; 3111 default -> throw new AssertionError("Unexpected tree kind: " + currentTree.getTag()); 3112 }; 3113 log.error(pos, Errors.CantRefNonEffectivelyFinalVar(sym, diags.fragment(subKey))); 3114 } 3115 3116 /************************************************************************* 3117 * Visitor methods for statements and definitions 3118 *************************************************************************/ 3119 3120 /* ------------ Visitor methods for various sorts of trees -------------*/ 3121 3122 public void visitClassDef(JCClassDecl tree) { 3123 JCTree prevTree = currentTree; 3124 try { 3125 currentTree = tree.sym.isDirectlyOrIndirectlyLocal() ? tree : null; 3126 super.visitClassDef(tree); 3127 } finally { 3128 currentTree = prevTree; 3129 } 3130 } 3131 3132 @Override 3133 public void visitLambda(JCLambda tree) { 3134 JCTree prevTree = currentTree; 3135 try { 3136 currentTree = tree; 3137 super.visitLambda(tree); 3138 } finally { 3139 currentTree = prevTree; 3140 } 3141 } 3142 3143 @Override 3144 public void visitBindingPattern(JCBindingPattern tree) { 3145 scan(tree.var); 3146 } 3147 3148 @Override 3149 public void visitParenthesizedPattern(JCParenthesizedPattern tree) { 3150 scan(tree.pattern); 3151 } 3152 3153 @Override 3154 public void visitPatternCaseLabel(JCPatternCaseLabel tree) { 3155 scan(tree.pat); 3156 JCTree prevTree = currentTree; 3157 try { 3158 currentTree = tree; 3159 scan(tree.guard); 3160 } finally { 3161 currentTree = prevTree; 3162 } 3163 } 3164 3165 @Override 3166 public void visitRecordPattern(JCRecordPattern tree) { 3167 scan(tree.deconstructor); 3168 scan(tree.nested); 3169 } 3170 3171 @Override 3172 public void visitIdent(JCIdent tree) { 3173 if (tree.sym.kind == VAR) { 3174 checkEffectivelyFinal(tree, (VarSymbol)tree.sym); 3175 } 3176 } 3177 3178 public void visitAssign(JCAssign tree) { 3179 JCTree lhs = TreeInfo.skipParens(tree.lhs); 3180 if (!(lhs instanceof JCIdent)) { 3181 scan(lhs); 3182 } 3183 scan(tree.rhs); 3184 letInit(lhs); 3185 } 3186 3187 public void visitAssignop(JCAssignOp tree) { 3188 scan(tree.lhs); 3189 scan(tree.rhs); 3190 letInit(tree.lhs); 3191 } 3192 3193 public void visitUnary(JCUnary tree) { 3194 switch (tree.getTag()) { 3195 case PREINC: case POSTINC: 3196 case PREDEC: case POSTDEC: 3197 scan(tree.arg); 3198 letInit(tree.arg); 3199 break; 3200 default: 3201 scan(tree.arg); 3202 } 3203 } 3204 3205 public void visitTry(JCTry tree) { 3206 for (JCTree resource : tree.resources) { 3207 if (!resource.hasTag(VARDEF)) { 3208 Symbol var = TreeInfo.symbol(resource); 3209 if (var != null && (var.flags() & (FINAL | EFFECTIVELY_FINAL)) == 0) { 3210 log.error(resource.pos(), Errors.TryWithResourcesExprEffectivelyFinalVar(var)); 3211 } 3212 } 3213 } 3214 super.visitTry(tree); 3215 } 3216 3217 @Override 3218 public void visitYield(JCYield tree) { 3219 scan(tree.value); 3220 } 3221 3222 public void visitModuleDef(JCModuleDecl tree) { 3223 // Do nothing for modules 3224 } 3225 3226 /************************************************************************** 3227 * main method 3228 *************************************************************************/ 3229 3230 /** Perform definite assignment/unassignment analysis on a tree. 3231 */ 3232 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { 3233 analyzeTree(env, env.tree, make); 3234 } 3235 public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) { 3236 try { 3237 attrEnv = env; 3238 Flow.this.make = make; 3239 pendingExits = new ListBuffer<>(); 3240 scan(tree); 3241 } finally { 3242 pendingExits = null; 3243 Flow.this.make = null; 3244 } 3245 } 3246 } 3247 3248 enum Liveness { 3249 ALIVE { 3250 @Override 3251 public Liveness or(Liveness other) { 3252 return this; 3253 } 3254 @Override 3255 public Liveness and(Liveness other) { 3256 return other; 3257 } 3258 }, 3259 DEAD { 3260 @Override 3261 public Liveness or(Liveness other) { 3262 return other; 3263 } 3264 @Override 3265 public Liveness and(Liveness other) { 3266 return this; 3267 } 3268 }, 3269 RECOVERY { 3270 @Override 3271 public Liveness or(Liveness other) { 3272 if (other == ALIVE) { 3273 return ALIVE; 3274 } else { 3275 return this; 3276 } 3277 } 3278 @Override 3279 public Liveness and(Liveness other) { 3280 if (other == DEAD) { 3281 return DEAD; 3282 } else { 3283 return this; 3284 } 3285 } 3286 }; 3287 3288 public abstract Liveness or(Liveness other); 3289 public abstract Liveness and(Liveness other); 3290 public Liveness or(boolean value) { 3291 return or(from(value)); 3292 } 3293 public Liveness and(boolean value) { 3294 return and(from(value)); 3295 } 3296 public static Liveness from(boolean value) { 3297 return value ? ALIVE : DEAD; 3298 } 3299 } 3300 3301 }