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