1 /* 2 * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 //todo: one might eliminate uninits.andSets when monotonic 27 28 package com.sun.tools.javac.comp; 29 30 import java.util.Map; 31 import java.util.Map.Entry; 32 import java.util.HashMap; 33 import java.util.HashSet; 34 import java.util.Set; 35 import java.util.stream.Collectors; 36 import java.util.stream.StreamSupport; 37 38 import com.sun.source.tree.LambdaExpressionTree.BodyKind; 39 import com.sun.tools.javac.code.*; 40 import com.sun.tools.javac.code.Scope.WriteableScope; 41 import com.sun.tools.javac.code.Source.Feature; 42 import com.sun.tools.javac.resources.CompilerProperties.Errors; 43 import com.sun.tools.javac.resources.CompilerProperties.Warnings; 44 import com.sun.tools.javac.tree.*; 45 import com.sun.tools.javac.util.*; 46 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 47 import com.sun.tools.javac.util.JCDiagnostic.Error; 48 import com.sun.tools.javac.util.JCDiagnostic.Warning; 49 50 import com.sun.tools.javac.code.Symbol.*; 51 import com.sun.tools.javac.tree.JCTree.*; 52 53 import static com.sun.tools.javac.code.Flags.*; 54 import static com.sun.tools.javac.code.Flags.BLOCK; 55 import static com.sun.tools.javac.code.Kinds.Kind.*; 56 import com.sun.tools.javac.code.Type.TypeVar; 57 import static com.sun.tools.javac.code.TypeTag.BOOLEAN; 58 import static com.sun.tools.javac.code.TypeTag.NONE; 59 import static com.sun.tools.javac.code.TypeTag.VOID; 60 import static com.sun.tools.javac.comp.Flow.ThisExposability.ALLOWED; 61 import static com.sun.tools.javac.comp.Flow.ThisExposability.BANNED; 62 import com.sun.tools.javac.code.Types.UniqueType; 63 import com.sun.tools.javac.resources.CompilerProperties.Fragments; 64 import static com.sun.tools.javac.tree.JCTree.Tag.*; 65 import com.sun.tools.javac.util.JCDiagnostic.Fragment; 66 67 /** This pass implements dataflow analysis for Java programs though 68 * different AST visitor steps. Liveness analysis (see AliveAnalyzer) checks that 69 * every statement is reachable. Exception analysis (see FlowAnalyzer) ensures that 70 * every checked exception that is thrown is declared or caught. Definite assignment analysis 71 * (see AssignAnalyzer) ensures that each variable is assigned when used. Definite 72 * unassignment analysis (see AssignAnalyzer) in ensures that no final variable 73 * is assigned more than once. Finally, local variable capture analysis (see CaptureAnalyzer) 74 * determines that local variables accessed within the scope of an inner class/lambda 75 * are either final or effectively-final. 76 * 77 * <p>The JLS has a number of problems in the 78 * specification of these flow analysis problems. This implementation 79 * attempts to address those issues. 80 * 81 * <p>First, there is no accommodation for a finally clause that cannot 82 * complete normally. For liveness analysis, an intervening finally 83 * clause can cause a break, continue, or return not to reach its 84 * target. For exception analysis, an intervening finally clause can 85 * cause any exception to be "caught". For DA/DU analysis, the finally 86 * clause can prevent a transfer of control from propagating DA/DU 87 * state to the target. In addition, code in the finally clause can 88 * affect the DA/DU status of variables. 89 * 90 * <p>For try statements, we introduce the idea of a variable being 91 * definitely unassigned "everywhere" in a block. A variable V is 92 * "unassigned everywhere" in a block iff it is unassigned at the 93 * beginning of the block and there is no reachable assignment to V 94 * in the block. An assignment V=e is reachable iff V is not DA 95 * after e. Then we can say that V is DU at the beginning of the 96 * catch block iff V is DU everywhere in the try block. Similarly, V 97 * is DU at the beginning of the finally block iff V is DU everywhere 98 * in the try block and in every catch block. Specifically, the 99 * following bullet is added to 16.2.2 100 * <pre> 101 * V is <em>unassigned everywhere</em> in a block if it is 102 * unassigned before the block and there is no reachable 103 * assignment to V within the block. 104 * </pre> 105 * <p>In 16.2.15, the third bullet (and all of its sub-bullets) for all 106 * try blocks is changed to 107 * <pre> 108 * V is definitely unassigned before a catch block iff V is 109 * definitely unassigned everywhere in the try block. 110 * </pre> 111 * <p>The last bullet (and all of its sub-bullets) for try blocks that 112 * have a finally block is changed to 113 * <pre> 114 * V is definitely unassigned before the finally block iff 115 * V is definitely unassigned everywhere in the try block 116 * and everywhere in each catch block of the try statement. 117 * </pre> 118 * <p>In addition, 119 * <pre> 120 * V is definitely assigned at the end of a constructor iff 121 * V is definitely assigned after the block that is the body 122 * of the constructor and V is definitely assigned at every 123 * return that can return from the constructor. 124 * </pre> 125 * <p>In addition, each continue statement with the loop as its target 126 * is treated as a jump to the end of the loop body, and "intervening" 127 * finally clauses are treated as follows: V is DA "due to the 128 * continue" iff V is DA before the continue statement or V is DA at 129 * the end of any intervening finally block. V is DU "due to the 130 * continue" iff any intervening finally cannot complete normally or V 131 * is DU at the end of every intervening finally block. This "due to 132 * the continue" concept is then used in the spec for the loops. 133 * 134 * <p>Similarly, break statements must consider intervening finally 135 * blocks. For liveness analysis, a break statement for which any 136 * intervening finally cannot complete normally is not considered to 137 * cause the target statement to be able to complete normally. Then 138 * we say V is DA "due to the break" iff V is DA before the break or 139 * V is DA at the end of any intervening finally block. V is DU "due 140 * to the break" iff any intervening finally cannot complete normally 141 * or V is DU at the break and at the end of every intervening 142 * finally block. (I suspect this latter condition can be 143 * simplified.) This "due to the break" is then used in the spec for 144 * all statements that can be "broken". 145 * 146 * <p>The return statement is treated similarly. V is DA "due to a 147 * return statement" iff V is DA before the return statement or V is 148 * DA at the end of any intervening finally block. Note that we 149 * don't have to worry about the return expression because this 150 * concept is only used for constructors. 151 * 152 * <p>There is no spec in the JLS for when a variable is definitely 153 * assigned at the end of a constructor, which is needed for final 154 * fields (8.3.1.2). We implement the rule that V is DA at the end 155 * of the constructor iff it is DA and the end of the body of the 156 * constructor and V is DA "due to" every return of the constructor. 157 * 158 * <p>Intervening finally blocks similarly affect exception analysis. An 159 * intervening finally that cannot complete normally allows us to ignore 160 * an otherwise uncaught exception. 161 * 162 * <p>To implement the semantics of intervening finally clauses, all 163 * nonlocal transfers (break, continue, return, throw, method call that 164 * can throw a checked exception, and a constructor invocation that can 165 * thrown a checked exception) are recorded in a queue, and removed 166 * from the queue when we complete processing the target of the 167 * nonlocal transfer. This allows us to modify the queue in accordance 168 * with the above rules when we encounter a finally clause. The only 169 * exception to this [no pun intended] is that checked exceptions that 170 * are known to be caught or declared to be caught in the enclosing 171 * method are not recorded in the queue, but instead are recorded in a 172 * global variable "{@code Set<Type> thrown}" that records the type of all 173 * exceptions that can be thrown. 174 * 175 * <p>Other minor issues the treatment of members of other classes 176 * (always considered DA except that within an anonymous class 177 * constructor, where DA status from the enclosing scope is 178 * preserved), treatment of the case expression (V is DA before the 179 * case expression iff V is DA after the switch expression), 180 * treatment of variables declared in a switch block (the implied 181 * DA/DU status after the switch expression is DU and not DA for 182 * variables defined in a switch block), the treatment of boolean ?: 183 * expressions (The JLS rules only handle b and c non-boolean; the 184 * new rule is that if b and c are boolean valued, then V is 185 * (un)assigned after a?b:c when true/false iff V is (un)assigned 186 * after b when true/false and V is (un)assigned after c when 187 * true/false). 188 * 189 * <p>There is the remaining question of what syntactic forms constitute a 190 * reference to a variable. It is conventional to allow this.x on the 191 * left-hand-side to initialize a final instance field named x, yet 192 * this.x isn't considered a "use" when appearing on a right-hand-side 193 * in most implementations. Should parentheses affect what is 194 * considered a variable reference? The simplest rule would be to 195 * allow unqualified forms only, parentheses optional, and phase out 196 * support for assigning to a final field via this.x. 197 * 198 * <p><b>This is NOT part of any supported API. 199 * If you write code that depends on this, you do so at your own risk. 200 * This code and its internal interfaces are subject to change or 201 * deletion without notice.</b> 202 */ 203 public class Flow { 204 protected static final Context.Key<Flow> flowKey = new Context.Key<>(); 205 206 private final Names names; 207 private final Log log; 208 private final Symtab syms; 209 private final Types types; 210 private final Check chk; 211 private TreeMaker make; 212 private final Resolve rs; 213 private final JCDiagnostic.Factory diags; 214 private Env<AttrContext> attrEnv; 215 private Lint lint; 216 217 public static Flow instance(Context context) { 218 Flow instance = context.get(flowKey); 219 if (instance == null) 220 instance = new Flow(context); 221 return instance; 222 } 223 224 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { 225 new AliveAnalyzer().analyzeTree(env, make); 226 new AssignAnalyzer().analyzeTree(env, make); 227 new FlowAnalyzer().analyzeTree(env, make); 228 new CaptureAnalyzer().analyzeTree(env, make); 229 } 230 231 public void analyzeLambda(Env<AttrContext> env, JCLambda that, TreeMaker make, boolean speculative) { 232 Log.DiagnosticHandler diagHandler = null; 233 //we need to disable diagnostics temporarily; the problem is that if 234 //a lambda expression contains e.g. an unreachable statement, an error 235 //message will be reported and will cause compilation to skip the flow analysis 236 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis 237 //related errors, which will allow for more errors to be detected 238 if (!speculative) { 239 diagHandler = new Log.DiscardDiagnosticHandler(log); 240 } 241 try { 242 new LambdaAliveAnalyzer().analyzeTree(env, that, make); 243 } finally { 244 if (!speculative) { 245 log.popDiagnosticHandler(diagHandler); 246 } 247 } 248 } 249 250 public List<Type> analyzeLambdaThrownTypes(final Env<AttrContext> env, 251 JCLambda that, TreeMaker make) { 252 //we need to disable diagnostics temporarily; the problem is that if 253 //a lambda expression contains e.g. an unreachable statement, an error 254 //message will be reported and will cause compilation to skip the flow analysis 255 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis 256 //related errors, which will allow for more errors to be detected 257 Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log); 258 try { 259 new LambdaAssignAnalyzer(env).analyzeTree(env, that, make); 260 LambdaFlowAnalyzer flowAnalyzer = new LambdaFlowAnalyzer(); 261 flowAnalyzer.analyzeTree(env, that, make); 262 return flowAnalyzer.inferredThrownTypes; 263 } finally { 264 log.popDiagnosticHandler(diagHandler); 265 } 266 } 267 268 public boolean aliveAfter(Env<AttrContext> env, JCTree that, TreeMaker make) { 269 //we need to disable diagnostics temporarily; the problem is that if 270 //"that" contains e.g. an unreachable statement, an error 271 //message will be reported and will cause compilation to skip the flow analysis 272 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis 273 //related errors, which will allow for more errors to be detected 274 Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log); 275 try { 276 SnippetAliveAnalyzer analyzer = new SnippetAliveAnalyzer(); 277 278 analyzer.analyzeTree(env, that, make); 279 return analyzer.isAlive(); 280 } finally { 281 log.popDiagnosticHandler(diagHandler); 282 } 283 } 284 285 public boolean breaksOutOf(Env<AttrContext> env, JCTree loop, JCTree body, TreeMaker make) { 286 //we need to disable diagnostics temporarily; the problem is that if 287 //"that" contains e.g. an unreachable statement, an error 288 //message will be reported and will cause compilation to skip the flow analysis 289 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis 290 //related errors, which will allow for more errors to be detected 291 Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log); 292 try { 293 SnippetBreakAnalyzer analyzer = new SnippetBreakAnalyzer(); 294 295 analyzer.analyzeTree(env, body, make); 296 return analyzer.breaksOut(); 297 } finally { 298 log.popDiagnosticHandler(diagHandler); 299 } 300 } 301 302 /** 303 * Definite assignment scan mode 304 */ 305 enum FlowKind { 306 /** 307 * This is the normal DA/DU analysis mode 308 */ 309 NORMAL("var.might.already.be.assigned", false), 310 /** 311 * This is the speculative DA/DU analysis mode used to speculatively 312 * derive assertions within loop bodies 313 */ 314 SPECULATIVE_LOOP("var.might.be.assigned.in.loop", true); 315 316 final String errKey; 317 final boolean isFinal; 318 319 FlowKind(String errKey, boolean isFinal) { 320 this.errKey = errKey; 321 this.isFinal = isFinal; 322 } 323 324 boolean isFinal() { 325 return isFinal; 326 } 327 } 328 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 thrown 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.referenceProjectionOrSelf(), 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 /** Enum to model whether constructors allowed to "leak" this reference before 1875 all instance fields are DA. 1876 */ 1877 enum ThisExposability { 1878 ALLOWED, // identity Object classes - NOP 1879 BANNED, // primitive/value classes - Error 1880 } 1881 1882 /** 1883 * This pass implements (i) definite assignment analysis, which ensures that 1884 * each variable is assigned when used and (ii) definite unassignment analysis, 1885 * which ensures that no final variable is assigned more than once. This visitor 1886 * depends on the results of the liveliness analyzer. This pass is also used to mark 1887 * effectively-final local variables/parameters. 1888 */ 1889 1890 public class AssignAnalyzer extends BaseAnalyzer { 1891 1892 /** The set of definitely assigned variables. 1893 */ 1894 final Bits inits; 1895 1896 /** The set of definitely unassigned variables. 1897 */ 1898 final Bits uninits; 1899 1900 /** The set of variables that are definitely unassigned everywhere 1901 * in current try block. This variable is maintained lazily; it is 1902 * updated only when something gets removed from uninits, 1903 * typically by being assigned in reachable code. To obtain the 1904 * correct set of variables which are definitely unassigned 1905 * anywhere in current try block, intersect uninitsTry and 1906 * uninits. 1907 */ 1908 final Bits uninitsTry; 1909 1910 /** When analyzing a condition, inits and uninits are null. 1911 * Instead we have: 1912 */ 1913 final Bits initsWhenTrue; 1914 final Bits initsWhenFalse; 1915 final Bits uninitsWhenTrue; 1916 final Bits uninitsWhenFalse; 1917 1918 /** A mapping from addresses to variable symbols. 1919 */ 1920 protected JCVariableDecl[] vardecls; 1921 1922 /** The current class being defined. 1923 */ 1924 JCClassDecl classDef; 1925 1926 /** The first variable sequence number in this class definition. 1927 */ 1928 int firstadr; 1929 1930 /** The next available variable sequence number. 1931 */ 1932 protected int nextadr; 1933 1934 /** The first variable sequence number in a block that can return. 1935 */ 1936 protected int returnadr; 1937 1938 /** The list of unreferenced automatic resources. 1939 */ 1940 WriteableScope unrefdResources; 1941 1942 /** Modified when processing a loop body the second time for DU analysis. */ 1943 FlowKind flowKind = FlowKind.NORMAL; 1944 1945 /** The starting position of the analyzed tree */ 1946 int startPos; 1947 1948 public class AssignPendingExit extends BaseAnalyzer.PendingExit { 1949 1950 final Bits inits; 1951 final Bits uninits; 1952 final Bits exit_inits = new Bits(true); 1953 final Bits exit_uninits = new Bits(true); 1954 1955 public AssignPendingExit(JCTree tree, final Bits inits, final Bits uninits) { 1956 super(tree); 1957 this.inits = inits; 1958 this.uninits = uninits; 1959 this.exit_inits.assign(inits); 1960 this.exit_uninits.assign(uninits); 1961 } 1962 1963 @Override 1964 public void resolveJump() { 1965 inits.andSet(exit_inits); 1966 uninits.andSet(exit_uninits); 1967 } 1968 } 1969 1970 // Are constructors allowed to leak this reference ? 1971 ThisExposability thisExposability = ALLOWED; 1972 1973 public AssignAnalyzer() { 1974 this.inits = new Bits(); 1975 uninits = new Bits(); 1976 uninitsTry = new Bits(); 1977 initsWhenTrue = new Bits(true); 1978 initsWhenFalse = new Bits(true); 1979 uninitsWhenTrue = new Bits(true); 1980 uninitsWhenFalse = new Bits(true); 1981 } 1982 1983 private boolean isInitialConstructor = false; 1984 1985 @Override 1986 protected void markDead() { 1987 if (!isInitialConstructor) { 1988 inits.inclRange(returnadr, nextadr); 1989 } else { 1990 for (int address = returnadr; address < nextadr; address++) { 1991 if (!(isFinalUninitializedStaticField(vardecls[address].sym))) { 1992 inits.incl(address); 1993 } 1994 } 1995 } 1996 uninits.inclRange(returnadr, nextadr); 1997 } 1998 1999 /*-------------- Processing variables ----------------------*/ 2000 2001 /** Do we need to track init/uninit state of this symbol? 2002 * I.e. is symbol either a local or a blank final variable? 2003 */ 2004 protected boolean trackable(VarSymbol sym) { 2005 return 2006 sym.pos >= startPos && 2007 ((sym.owner.kind == MTH || sym.owner.kind == VAR || 2008 isFinalUninitializedField(sym))); 2009 } 2010 2011 boolean isFinalUninitializedField(VarSymbol sym) { 2012 return sym.owner.kind == TYP && 2013 ((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL && 2014 classDef.sym.isEnclosedBy((ClassSymbol)sym.owner)); 2015 } 2016 2017 boolean isFinalUninitializedStaticField(VarSymbol sym) { 2018 return isFinalUninitializedField(sym) && sym.isStatic(); 2019 } 2020 2021 /** Initialize new trackable variable by setting its address field 2022 * to the next available sequence number and entering it under that 2023 * index into the vars array. 2024 */ 2025 void newVar(JCVariableDecl varDecl) { 2026 VarSymbol sym = varDecl.sym; 2027 vardecls = ArrayUtils.ensureCapacity(vardecls, nextadr); 2028 if ((sym.flags() & FINAL) == 0) { 2029 sym.flags_field |= EFFECTIVELY_FINAL; 2030 } 2031 sym.adr = nextadr; 2032 vardecls[nextadr] = varDecl; 2033 inits.excl(nextadr); 2034 uninits.incl(nextadr); 2035 nextadr++; 2036 } 2037 2038 /** Record an initialization of a trackable variable. 2039 */ 2040 void letInit(DiagnosticPosition pos, VarSymbol sym) { 2041 if (sym.adr >= firstadr && trackable(sym)) { 2042 if ((sym.flags() & EFFECTIVELY_FINAL) != 0) { 2043 if (inits.isMember(sym.adr) || !uninits.isMember(sym.adr)) { 2044 //assignment targeting an effectively final variable makes the 2045 //variable lose its status of effectively final if the variable 2046 //is definitely assigned or _not_ definitively unassigned 2047 sym.flags_field &= ~EFFECTIVELY_FINAL; 2048 } 2049 uninit(sym); 2050 } 2051 else if ((sym.flags() & FINAL) != 0) { 2052 if ((sym.flags() & PARAMETER) != 0) { 2053 if ((sym.flags() & UNION) != 0) { //multi-catch parameter 2054 log.error(pos, Errors.MulticatchParameterMayNotBeAssigned(sym)); 2055 } 2056 else { 2057 log.error(pos, 2058 Errors.FinalParameterMayNotBeAssigned(sym)); 2059 } 2060 } else if (!uninits.isMember(sym.adr)) { 2061 log.error(pos, diags.errorKey(flowKind.errKey, sym)); 2062 } else { 2063 uninit(sym); 2064 } 2065 } 2066 inits.incl(sym.adr); 2067 } else if ((sym.flags() & FINAL) != 0) { 2068 log.error(pos, Errors.VarMightAlreadyBeAssigned(sym)); 2069 } 2070 } 2071 //where 2072 void uninit(VarSymbol sym) { 2073 if (!inits.isMember(sym.adr)) { 2074 // reachable assignment 2075 uninits.excl(sym.adr); 2076 uninitsTry.excl(sym.adr); 2077 } else { 2078 //log.rawWarning(pos, "unreachable assignment");//DEBUG 2079 uninits.excl(sym.adr); 2080 } 2081 } 2082 2083 /** If tree is either a simple name or of the form this.name or 2084 * C.this.name, and tree represents a trackable variable, 2085 * record an initialization of the variable. 2086 */ 2087 void letInit(JCTree tree) { 2088 tree = TreeInfo.skipParens(tree); 2089 if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) { 2090 Symbol sym = TreeInfo.symbol(tree); 2091 if (sym.kind == VAR) { 2092 letInit(tree.pos(), (VarSymbol)sym); 2093 } 2094 } 2095 } 2096 2097 void checkEmbryonicThisExposure(JCTree node) { 2098 if (this.thisExposability == ALLOWED || classDef == null) 2099 return; 2100 2101 // Note: for non-initial constructors, firstadr is post all instance fields. 2102 for (int i = firstadr; i < nextadr; i++) { 2103 VarSymbol sym = vardecls[i].sym; 2104 if (sym.owner != classDef.sym) 2105 continue; 2106 if ((sym.flags() & (FINAL | HASINIT | STATIC | PARAMETER)) != FINAL) 2107 continue; 2108 if (sym.pos < startPos || sym.adr < firstadr) 2109 continue; 2110 if (!inits.isMember(sym.adr)) { 2111 if (this.thisExposability == BANNED) { 2112 log.error(node, Errors.ThisExposedPrematurely); 2113 } 2114 return; // don't flog a dead horse. 2115 } 2116 } 2117 } 2118 2119 /** Check that trackable variable is initialized. 2120 */ 2121 void checkInit(DiagnosticPosition pos, VarSymbol sym) { 2122 checkInit(pos, sym, Errors.VarMightNotHaveBeenInitialized(sym)); 2123 } 2124 2125 void checkInit(DiagnosticPosition pos, VarSymbol sym, Error errkey) { 2126 if ((sym.adr >= firstadr || sym.owner.kind != TYP) && 2127 trackable(sym) && 2128 !inits.isMember(sym.adr) && 2129 (sym.flags_field & CLASH) == 0) { 2130 log.error(pos, errkey); 2131 inits.incl(sym.adr); 2132 } 2133 } 2134 2135 /** Utility method to reset several Bits instances. 2136 */ 2137 private void resetBits(Bits... bits) { 2138 for (Bits b : bits) { 2139 b.reset(); 2140 } 2141 } 2142 2143 /** Split (duplicate) inits/uninits into WhenTrue/WhenFalse sets 2144 */ 2145 void split(boolean setToNull) { 2146 initsWhenFalse.assign(inits); 2147 uninitsWhenFalse.assign(uninits); 2148 initsWhenTrue.assign(inits); 2149 uninitsWhenTrue.assign(uninits); 2150 if (setToNull) { 2151 resetBits(inits, uninits); 2152 } 2153 } 2154 2155 /** Merge (intersect) inits/uninits from WhenTrue/WhenFalse sets. 2156 */ 2157 protected void merge() { 2158 inits.assign(initsWhenFalse.andSet(initsWhenTrue)); 2159 uninits.assign(uninitsWhenFalse.andSet(uninitsWhenTrue)); 2160 } 2161 2162 /* ************************************************************************ 2163 * Visitor methods for statements and definitions 2164 *************************************************************************/ 2165 2166 /** Analyze an expression. Make sure to set (un)inits rather than 2167 * (un)initsWhenTrue(WhenFalse) on exit. 2168 */ 2169 void scanExpr(JCTree tree) { 2170 if (tree != null) { 2171 scan(tree); 2172 if (inits.isReset()) { 2173 merge(); 2174 } 2175 } 2176 } 2177 2178 /** Analyze a list of expressions. 2179 */ 2180 void scanExprs(List<? extends JCExpression> trees) { 2181 if (trees != null) 2182 for (List<? extends JCExpression> l = trees; l.nonEmpty(); l = l.tail) 2183 scanExpr(l.head); 2184 } 2185 2186 void scanPattern(JCTree tree) { 2187 scan(tree); 2188 if (inits.isReset()) { 2189 inits.assign(initsWhenTrue); 2190 uninits.assign(uninitsWhenTrue); 2191 } 2192 } 2193 2194 /** Analyze a condition. Make sure to set (un)initsWhenTrue(WhenFalse) 2195 * rather than (un)inits on exit. 2196 */ 2197 void scanCond(JCTree tree) { 2198 if (tree.type.isFalse()) { 2199 if (inits.isReset()) merge(); 2200 initsWhenTrue.assign(inits); 2201 initsWhenTrue.inclRange(firstadr, nextadr); 2202 uninitsWhenTrue.assign(uninits); 2203 uninitsWhenTrue.inclRange(firstadr, nextadr); 2204 initsWhenFalse.assign(inits); 2205 uninitsWhenFalse.assign(uninits); 2206 } else if (tree.type.isTrue()) { 2207 if (inits.isReset()) merge(); 2208 initsWhenFalse.assign(inits); 2209 initsWhenFalse.inclRange(firstadr, nextadr); 2210 uninitsWhenFalse.assign(uninits); 2211 uninitsWhenFalse.inclRange(firstadr, nextadr); 2212 initsWhenTrue.assign(inits); 2213 uninitsWhenTrue.assign(uninits); 2214 } else { 2215 scan(tree); 2216 if (!inits.isReset()) 2217 split(tree.type != syms.unknownType); 2218 } 2219 if (tree.type != syms.unknownType) { 2220 resetBits(inits, uninits); 2221 } 2222 } 2223 2224 /* ------------ Visitor methods for various sorts of trees -------------*/ 2225 2226 public void visitClassDef(JCClassDecl tree) { 2227 if (tree.sym == null) { 2228 return; 2229 } 2230 2231 Lint lintPrev = lint; 2232 lint = lint.augment(tree.sym); 2233 try { 2234 JCClassDecl classDefPrev = classDef; 2235 int firstadrPrev = firstadr; 2236 int nextadrPrev = nextadr; 2237 ListBuffer<PendingExit> pendingExitsPrev = pendingExits; 2238 2239 pendingExits = new ListBuffer<>(); 2240 if (tree.name != names.empty) { 2241 firstadr = nextadr; 2242 } 2243 classDef = tree; 2244 try { 2245 // define all the static fields 2246 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 2247 if (l.head.hasTag(VARDEF)) { 2248 JCVariableDecl def = (JCVariableDecl)l.head; 2249 if ((def.mods.flags & STATIC) != 0) { 2250 VarSymbol sym = def.sym; 2251 if (trackable(sym)) { 2252 newVar(def); 2253 } 2254 } 2255 } 2256 } 2257 2258 // process all the static initializers 2259 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 2260 if (!l.head.hasTag(METHODDEF) && 2261 (TreeInfo.flags(l.head) & STATIC) != 0) { 2262 scan(l.head); 2263 clearPendingExits(false); 2264 } 2265 } 2266 2267 // define all the instance fields 2268 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 2269 if (l.head.hasTag(VARDEF)) { 2270 JCVariableDecl def = (JCVariableDecl)l.head; 2271 if ((def.mods.flags & STATIC) == 0) { 2272 VarSymbol sym = def.sym; 2273 if (trackable(sym)) { 2274 newVar(def); 2275 } 2276 } 2277 } 2278 } 2279 2280 // process all the instance initializers 2281 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 2282 if (!l.head.hasTag(METHODDEF) && 2283 (TreeInfo.flags(l.head) & STATIC) == 0) { 2284 scan(l.head); 2285 clearPendingExits(false); 2286 } 2287 } 2288 2289 // process all the methods 2290 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 2291 if (l.head.hasTag(METHODDEF)) { 2292 scan(l.head); 2293 } 2294 } 2295 } finally { 2296 pendingExits = pendingExitsPrev; 2297 nextadr = nextadrPrev; 2298 firstadr = firstadrPrev; 2299 classDef = classDefPrev; 2300 } 2301 } finally { 2302 lint = lintPrev; 2303 } 2304 } 2305 2306 public void visitMethodDef(JCMethodDecl tree) { 2307 if (tree.body == null) { 2308 return; 2309 } 2310 2311 /* MemberEnter can generate synthetic methods ignore them 2312 */ 2313 if ((tree.sym.flags() & SYNTHETIC) != 0) { 2314 return; 2315 } 2316 2317 Lint lintPrev = lint; 2318 lint = lint.augment(tree.sym); 2319 ThisExposability priorThisExposability = this.thisExposability; 2320 try { 2321 final Bits initsPrev = new Bits(inits); 2322 final Bits uninitsPrev = new Bits(uninits); 2323 int nextadrPrev = nextadr; 2324 int firstadrPrev = firstadr; 2325 int returnadrPrev = returnadr; 2326 2327 Assert.check(pendingExits.isEmpty()); 2328 boolean lastInitialConstructor = isInitialConstructor; 2329 try { 2330 isInitialConstructor = TreeInfo.isInitialConstructor(tree); 2331 2332 if (!isInitialConstructor) { 2333 firstadr = nextadr; 2334 this.thisExposability = ALLOWED; 2335 } else { 2336 if (tree.sym.owner.type.isValueClass()) 2337 this.thisExposability = BANNED; 2338 else 2339 this.thisExposability = ALLOWED; 2340 } 2341 for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { 2342 JCVariableDecl def = l.head; 2343 scan(def); 2344 Assert.check((def.sym.flags() & PARAMETER) != 0, "Method parameter without PARAMETER flag"); 2345 /* If we are executing the code from Gen, then there can be 2346 * synthetic or mandated variables, ignore them. 2347 */ 2348 initParam(def); 2349 } 2350 // else we are in an instance initializer block; 2351 // leave caught unchanged. 2352 scan(tree.body); 2353 2354 boolean isCompactOrGeneratedRecordConstructor = (tree.sym.flags() & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0 || 2355 (tree.sym.flags() & (GENERATEDCONSTR | RECORD)) == (GENERATEDCONSTR | RECORD); 2356 if (isInitialConstructor) { 2357 boolean isSynthesized = (tree.sym.flags() & 2358 GENERATEDCONSTR) != 0; 2359 for (int i = firstadr; i < nextadr; i++) { 2360 JCVariableDecl vardecl = vardecls[i]; 2361 VarSymbol var = vardecl.sym; 2362 if (var.owner == classDef.sym) { 2363 // choose the diagnostic position based on whether 2364 // the ctor is default(synthesized) or not 2365 if (isSynthesized && !isCompactOrGeneratedRecordConstructor) { 2366 checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), 2367 var, Errors.VarNotInitializedInDefaultConstructor(var)); 2368 } else if (isCompactOrGeneratedRecordConstructor) { 2369 boolean isInstanceRecordField = var.enclClass().isRecord() && 2370 (var.flags_field & (Flags.PRIVATE | Flags.FINAL | Flags.GENERATED_MEMBER | Flags.RECORD)) != 0 && 2371 !var.isStatic() && 2372 var.owner.kind == TYP; 2373 if (isInstanceRecordField) { 2374 boolean notInitialized = !inits.isMember(var.adr); 2375 if (notInitialized && uninits.isMember(var.adr) && tree.completesNormally) { 2376 /* this way we indicate Lower that it should generate an initialization for this field 2377 * in the compact constructor 2378 */ 2379 var.flags_field |= UNINITIALIZED_FIELD; 2380 } else { 2381 checkInit(TreeInfo.diagEndPos(tree.body), var); 2382 } 2383 } else { 2384 checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), var); 2385 } 2386 } else { 2387 checkInit(TreeInfo.diagEndPos(tree.body), var); 2388 } 2389 } 2390 } 2391 } 2392 clearPendingExits(true); 2393 } finally { 2394 inits.assign(initsPrev); 2395 uninits.assign(uninitsPrev); 2396 nextadr = nextadrPrev; 2397 firstadr = firstadrPrev; 2398 returnadr = returnadrPrev; 2399 isInitialConstructor = lastInitialConstructor; 2400 } 2401 } finally { 2402 lint = lintPrev; 2403 this.thisExposability = priorThisExposability; 2404 } 2405 } 2406 2407 private void clearPendingExits(boolean inMethod) { 2408 List<PendingExit> exits = pendingExits.toList(); 2409 pendingExits = new ListBuffer<>(); 2410 while (exits.nonEmpty()) { 2411 PendingExit exit = exits.head; 2412 exits = exits.tail; 2413 Assert.check((inMethod && exit.tree.hasTag(RETURN)) || 2414 log.hasErrorOn(exit.tree.pos()), 2415 exit.tree); 2416 if (inMethod && isInitialConstructor) { 2417 Assert.check(exit instanceof AssignPendingExit); 2418 inits.assign(((AssignPendingExit) exit).exit_inits); 2419 for (int i = firstadr; i < nextadr; i++) { 2420 checkInit(exit.tree.pos(), vardecls[i].sym); 2421 } 2422 } 2423 } 2424 } 2425 protected void initParam(JCVariableDecl def) { 2426 inits.incl(def.sym.adr); 2427 uninits.excl(def.sym.adr); 2428 } 2429 2430 public void visitVarDef(JCVariableDecl tree) { 2431 Lint lintPrev = lint; 2432 lint = lint.augment(tree.sym); 2433 try{ 2434 boolean track = trackable(tree.sym); 2435 if (track && (tree.sym.owner.kind == MTH || tree.sym.owner.kind == VAR)) { 2436 newVar(tree); 2437 } 2438 if (tree.init != null) { 2439 scanExpr(tree.init); 2440 if (track) { 2441 letInit(tree.pos(), tree.sym); 2442 } 2443 } 2444 } finally { 2445 lint = lintPrev; 2446 } 2447 } 2448 2449 public void visitBlock(JCBlock tree) { 2450 int nextadrPrev = nextadr; 2451 scan(tree.stats); 2452 nextadr = nextadrPrev; 2453 } 2454 2455 public void visitDoLoop(JCDoWhileLoop 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 do { 2464 final Bits uninitsEntry = new Bits(uninits); 2465 uninitsEntry.excludeFrom(nextadr); 2466 scan(tree.body); 2467 resolveContinues(tree); 2468 scanCond(tree.cond); 2469 if (!flowKind.isFinal()) { 2470 initsSkip.assign(initsWhenFalse); 2471 uninitsSkip.assign(uninitsWhenFalse); 2472 } 2473 if (log.nerrors != prevErrors || 2474 flowKind.isFinal() || 2475 new Bits(uninitsEntry).diffSet(uninitsWhenTrue).nextBit(firstadr)==-1) 2476 break; 2477 inits.assign(initsWhenTrue); 2478 uninits.assign(uninitsEntry.andSet(uninitsWhenTrue)); 2479 flowKind = FlowKind.SPECULATIVE_LOOP; 2480 } while (true); 2481 flowKind = prevFlowKind; 2482 inits.assign(initsSkip); 2483 uninits.assign(uninitsSkip); 2484 resolveBreaks(tree, prevPendingExits); 2485 } 2486 2487 public void visitWhileLoop(JCWhileLoop tree) { 2488 ListBuffer<PendingExit> prevPendingExits = pendingExits; 2489 FlowKind prevFlowKind = flowKind; 2490 flowKind = FlowKind.NORMAL; 2491 final Bits initsSkip = new Bits(true); 2492 final Bits uninitsSkip = new Bits(true); 2493 pendingExits = new ListBuffer<>(); 2494 int prevErrors = log.nerrors; 2495 final Bits uninitsEntry = new Bits(uninits); 2496 uninitsEntry.excludeFrom(nextadr); 2497 do { 2498 scanCond(tree.cond); 2499 if (!flowKind.isFinal()) { 2500 initsSkip.assign(initsWhenFalse) ; 2501 uninitsSkip.assign(uninitsWhenFalse); 2502 } 2503 inits.assign(initsWhenTrue); 2504 uninits.assign(uninitsWhenTrue); 2505 scan(tree.body); 2506 resolveContinues(tree); 2507 if (log.nerrors != prevErrors || 2508 flowKind.isFinal() || 2509 new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1) { 2510 break; 2511 } 2512 uninits.assign(uninitsEntry.andSet(uninits)); 2513 flowKind = FlowKind.SPECULATIVE_LOOP; 2514 } while (true); 2515 flowKind = prevFlowKind; 2516 //a variable is DA/DU after the while statement, if it's DA/DU assuming the 2517 //branch is not taken AND if it's DA/DU before any break statement 2518 inits.assign(initsSkip); 2519 uninits.assign(uninitsSkip); 2520 resolveBreaks(tree, prevPendingExits); 2521 } 2522 2523 public void visitForLoop(JCForLoop tree) { 2524 ListBuffer<PendingExit> prevPendingExits = pendingExits; 2525 FlowKind prevFlowKind = flowKind; 2526 flowKind = FlowKind.NORMAL; 2527 int nextadrPrev = nextadr; 2528 scan(tree.init); 2529 final Bits initsSkip = new Bits(true); 2530 final Bits uninitsSkip = new Bits(true); 2531 pendingExits = new ListBuffer<>(); 2532 int prevErrors = log.nerrors; 2533 do { 2534 final Bits uninitsEntry = new Bits(uninits); 2535 uninitsEntry.excludeFrom(nextadr); 2536 if (tree.cond != null) { 2537 scanCond(tree.cond); 2538 if (!flowKind.isFinal()) { 2539 initsSkip.assign(initsWhenFalse); 2540 uninitsSkip.assign(uninitsWhenFalse); 2541 } 2542 inits.assign(initsWhenTrue); 2543 uninits.assign(uninitsWhenTrue); 2544 } else if (!flowKind.isFinal()) { 2545 initsSkip.assign(inits); 2546 initsSkip.inclRange(firstadr, nextadr); 2547 uninitsSkip.assign(uninits); 2548 uninitsSkip.inclRange(firstadr, nextadr); 2549 } 2550 scan(tree.body); 2551 resolveContinues(tree); 2552 scan(tree.step); 2553 if (log.nerrors != prevErrors || 2554 flowKind.isFinal() || 2555 new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1) 2556 break; 2557 uninits.assign(uninitsEntry.andSet(uninits)); 2558 flowKind = FlowKind.SPECULATIVE_LOOP; 2559 } while (true); 2560 flowKind = prevFlowKind; 2561 //a variable is DA/DU after a for loop, if it's DA/DU assuming the 2562 //branch is not taken AND if it's DA/DU before any break statement 2563 inits.assign(initsSkip); 2564 uninits.assign(uninitsSkip); 2565 resolveBreaks(tree, prevPendingExits); 2566 nextadr = nextadrPrev; 2567 } 2568 2569 public void visitForeachLoop(JCEnhancedForLoop tree) { 2570 ListBuffer<PendingExit> prevPendingExits = pendingExits; 2571 FlowKind prevFlowKind = flowKind; 2572 flowKind = FlowKind.NORMAL; 2573 int nextadrPrev = nextadr; 2574 scan(tree.expr); 2575 final Bits initsStart = new Bits(inits); 2576 final Bits uninitsStart = new Bits(uninits); 2577 2578 if(tree.varOrRecordPattern instanceof JCVariableDecl jcVariableDecl) { 2579 visitVarDef(jcVariableDecl); 2580 letInit(tree.pos(), jcVariableDecl.sym); 2581 } else if (tree.varOrRecordPattern instanceof JCRecordPattern jcRecordPattern) { 2582 visitRecordPattern(jcRecordPattern); 2583 } 2584 2585 pendingExits = new ListBuffer<>(); 2586 int prevErrors = log.nerrors; 2587 do { 2588 final Bits uninitsEntry = new Bits(uninits); 2589 uninitsEntry.excludeFrom(nextadr); 2590 scan(tree.body); 2591 resolveContinues(tree); 2592 if (log.nerrors != prevErrors || 2593 flowKind.isFinal() || 2594 new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1) 2595 break; 2596 uninits.assign(uninitsEntry.andSet(uninits)); 2597 flowKind = FlowKind.SPECULATIVE_LOOP; 2598 } while (true); 2599 flowKind = prevFlowKind; 2600 inits.assign(initsStart); 2601 uninits.assign(uninitsStart.andSet(uninits)); 2602 resolveBreaks(tree, prevPendingExits); 2603 nextadr = nextadrPrev; 2604 } 2605 2606 public void visitLabelled(JCLabeledStatement tree) { 2607 ListBuffer<PendingExit> prevPendingExits = pendingExits; 2608 pendingExits = new ListBuffer<>(); 2609 scan(tree.body); 2610 resolveBreaks(tree, prevPendingExits); 2611 } 2612 2613 public void visitSwitch(JCSwitch tree) { 2614 handleSwitch(tree, tree.selector, tree.cases, tree.isExhaustive); 2615 } 2616 2617 public void visitSwitchExpression(JCSwitchExpression tree) { 2618 handleSwitch(tree, tree.selector, tree.cases, tree.isExhaustive); 2619 } 2620 2621 private void handleSwitch(JCTree tree, JCExpression selector, 2622 List<JCCase> cases, boolean isExhaustive) { 2623 ListBuffer<PendingExit> prevPendingExits = pendingExits; 2624 pendingExits = new ListBuffer<>(); 2625 int nextadrPrev = nextadr; 2626 scanExpr(selector); 2627 final Bits initsSwitch = new Bits(inits); 2628 final Bits uninitsSwitch = new Bits(uninits); 2629 for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) { 2630 inits.assign(initsSwitch); 2631 uninits.assign(uninits.andSet(uninitsSwitch)); 2632 JCCase c = l.head; 2633 for (JCCaseLabel pat : c.labels) { 2634 scanPattern(pat); 2635 } 2636 if (l.head.stats.isEmpty() && 2637 l.tail.nonEmpty() && 2638 l.tail.head.labels.size() == 1 && 2639 TreeInfo.isNullCaseLabel(l.tail.head.labels.head)) { 2640 //handling: 2641 //case Integer i: 2642 //case null: 2643 //joining these two cases together - processing Integer i pattern, 2644 //but statements from case null: 2645 l = l.tail; 2646 c = l.head; 2647 } 2648 scan(c.stats); 2649 if (c.completesNormally && c.caseKind == JCCase.RULE) { 2650 scanSyntheticBreak(make, tree); 2651 } 2652 addVars(c.stats, initsSwitch, uninitsSwitch); 2653 // Warn about fall-through if lint switch fallthrough enabled. 2654 } 2655 if (!isExhaustive) { 2656 if (tree.hasTag(SWITCH_EXPRESSION)) { 2657 markDead(); 2658 } else if (tree.hasTag(SWITCH) && !TreeInfo.expectedExhaustive((JCSwitch) tree)) { 2659 inits.assign(initsSwitch); 2660 uninits.assign(uninits.andSet(uninitsSwitch)); 2661 } 2662 } 2663 if (tree.hasTag(SWITCH_EXPRESSION)) { 2664 resolveYields(tree, prevPendingExits); 2665 } else { 2666 resolveBreaks(tree, prevPendingExits); 2667 } 2668 nextadr = nextadrPrev; 2669 } 2670 // where 2671 /** Add any variables defined in stats to inits and uninits. */ 2672 private void addVars(List<JCStatement> stats, final Bits inits, 2673 final Bits uninits) { 2674 for (;stats.nonEmpty(); stats = stats.tail) { 2675 JCTree stat = stats.head; 2676 if (stat.hasTag(VARDEF)) { 2677 int adr = ((JCVariableDecl) stat).sym.adr; 2678 inits.excl(adr); 2679 uninits.incl(adr); 2680 } 2681 } 2682 } 2683 2684 public void visitTry(JCTry tree) { 2685 ListBuffer<JCVariableDecl> resourceVarDecls = new ListBuffer<>(); 2686 final Bits uninitsTryPrev = new Bits(uninitsTry); 2687 ListBuffer<PendingExit> prevPendingExits = pendingExits; 2688 pendingExits = new ListBuffer<>(); 2689 final Bits initsTry = new Bits(inits); 2690 uninitsTry.assign(uninits); 2691 for (JCTree resource : tree.resources) { 2692 if (resource instanceof JCVariableDecl variableDecl) { 2693 visitVarDef(variableDecl); 2694 unrefdResources.enter(variableDecl.sym); 2695 resourceVarDecls.append(variableDecl); 2696 } else if (resource instanceof JCExpression expression) { 2697 scanExpr(expression); 2698 } else { 2699 throw new AssertionError(tree); // parser error 2700 } 2701 } 2702 scan(tree.body); 2703 uninitsTry.andSet(uninits); 2704 final Bits initsEnd = new Bits(inits); 2705 final Bits uninitsEnd = new Bits(uninits); 2706 int nextadrCatch = nextadr; 2707 2708 if (!resourceVarDecls.isEmpty() && 2709 lint.isEnabled(Lint.LintCategory.TRY)) { 2710 for (JCVariableDecl resVar : resourceVarDecls) { 2711 if (unrefdResources.includes(resVar.sym)) { 2712 log.warning(Lint.LintCategory.TRY, resVar.pos(), 2713 Warnings.TryResourceNotReferenced(resVar.sym)); 2714 unrefdResources.remove(resVar.sym); 2715 } 2716 } 2717 } 2718 2719 /* The analysis of each catch should be independent. 2720 * Each one should have the same initial values of inits and 2721 * uninits. 2722 */ 2723 final Bits initsCatchPrev = new Bits(initsTry); 2724 final Bits uninitsCatchPrev = new Bits(uninitsTry); 2725 2726 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { 2727 JCVariableDecl param = l.head.param; 2728 inits.assign(initsCatchPrev); 2729 uninits.assign(uninitsCatchPrev); 2730 scan(param); 2731 /* If this is a TWR and we are executing the code from Gen, 2732 * then there can be synthetic variables, ignore them. 2733 */ 2734 initParam(param); 2735 scan(l.head.body); 2736 initsEnd.andSet(inits); 2737 uninitsEnd.andSet(uninits); 2738 nextadr = nextadrCatch; 2739 } 2740 if (tree.finalizer != null) { 2741 inits.assign(initsTry); 2742 uninits.assign(uninitsTry); 2743 ListBuffer<PendingExit> exits = pendingExits; 2744 pendingExits = prevPendingExits; 2745 scan(tree.finalizer); 2746 if (!tree.finallyCanCompleteNormally) { 2747 // discard exits and exceptions from try and finally 2748 } else { 2749 uninits.andSet(uninitsEnd); 2750 // FIX: this doesn't preserve source order of exits in catch 2751 // versus finally! 2752 while (exits.nonEmpty()) { 2753 PendingExit exit = exits.next(); 2754 if (exit instanceof AssignPendingExit assignPendingExit) { 2755 assignPendingExit.exit_inits.orSet(inits); 2756 assignPendingExit.exit_uninits.andSet(uninits); 2757 } 2758 pendingExits.append(exit); 2759 } 2760 inits.orSet(initsEnd); 2761 } 2762 } else { 2763 inits.assign(initsEnd); 2764 uninits.assign(uninitsEnd); 2765 ListBuffer<PendingExit> exits = pendingExits; 2766 pendingExits = prevPendingExits; 2767 while (exits.nonEmpty()) pendingExits.append(exits.next()); 2768 } 2769 uninitsTry.andSet(uninitsTryPrev).andSet(uninits); 2770 } 2771 2772 public void visitConditional(JCConditional tree) { 2773 scanCond(tree.cond); 2774 final Bits initsBeforeElse = new Bits(initsWhenFalse); 2775 final Bits uninitsBeforeElse = new Bits(uninitsWhenFalse); 2776 inits.assign(initsWhenTrue); 2777 uninits.assign(uninitsWhenTrue); 2778 if (tree.truepart.type.hasTag(BOOLEAN) && 2779 tree.falsepart.type.hasTag(BOOLEAN)) { 2780 // if b and c are boolean valued, then 2781 // v is (un)assigned after a?b:c when true iff 2782 // v is (un)assigned after b when true and 2783 // v is (un)assigned after c when true 2784 scanCond(tree.truepart); 2785 final Bits initsAfterThenWhenTrue = new Bits(initsWhenTrue); 2786 final Bits initsAfterThenWhenFalse = new Bits(initsWhenFalse); 2787 final Bits uninitsAfterThenWhenTrue = new Bits(uninitsWhenTrue); 2788 final Bits uninitsAfterThenWhenFalse = new Bits(uninitsWhenFalse); 2789 inits.assign(initsBeforeElse); 2790 uninits.assign(uninitsBeforeElse); 2791 scanCond(tree.falsepart); 2792 initsWhenTrue.andSet(initsAfterThenWhenTrue); 2793 initsWhenFalse.andSet(initsAfterThenWhenFalse); 2794 uninitsWhenTrue.andSet(uninitsAfterThenWhenTrue); 2795 uninitsWhenFalse.andSet(uninitsAfterThenWhenFalse); 2796 } else { 2797 scanExpr(tree.truepart); 2798 final Bits initsAfterThen = new Bits(inits); 2799 final Bits uninitsAfterThen = new Bits(uninits); 2800 inits.assign(initsBeforeElse); 2801 uninits.assign(uninitsBeforeElse); 2802 scanExpr(tree.falsepart); 2803 inits.andSet(initsAfterThen); 2804 uninits.andSet(uninitsAfterThen); 2805 } 2806 } 2807 2808 public void visitIf(JCIf tree) { 2809 scanCond(tree.cond); 2810 final Bits initsBeforeElse = new Bits(initsWhenFalse); 2811 final Bits uninitsBeforeElse = new Bits(uninitsWhenFalse); 2812 inits.assign(initsWhenTrue); 2813 uninits.assign(uninitsWhenTrue); 2814 scan(tree.thenpart); 2815 if (tree.elsepart != null) { 2816 final Bits initsAfterThen = new Bits(inits); 2817 final Bits uninitsAfterThen = new Bits(uninits); 2818 inits.assign(initsBeforeElse); 2819 uninits.assign(uninitsBeforeElse); 2820 scan(tree.elsepart); 2821 inits.andSet(initsAfterThen); 2822 uninits.andSet(uninitsAfterThen); 2823 } else { 2824 inits.andSet(initsBeforeElse); 2825 uninits.andSet(uninitsBeforeElse); 2826 } 2827 } 2828 2829 @Override 2830 public void visitBreak(JCBreak tree) { 2831 recordExit(new AssignPendingExit(tree, inits, uninits)); 2832 } 2833 2834 @Override 2835 public void visitYield(JCYield tree) { 2836 JCSwitchExpression expr = (JCSwitchExpression) tree.target; 2837 if (expr != null && expr.type.hasTag(BOOLEAN)) { 2838 scanCond(tree.value); 2839 Bits initsAfterBreakWhenTrue = new Bits(initsWhenTrue); 2840 Bits initsAfterBreakWhenFalse = new Bits(initsWhenFalse); 2841 Bits uninitsAfterBreakWhenTrue = new Bits(uninitsWhenTrue); 2842 Bits uninitsAfterBreakWhenFalse = new Bits(uninitsWhenFalse); 2843 PendingExit exit = new PendingExit(tree) { 2844 @Override 2845 void resolveJump() { 2846 if (!inits.isReset()) { 2847 split(true); 2848 } 2849 initsWhenTrue.andSet(initsAfterBreakWhenTrue); 2850 initsWhenFalse.andSet(initsAfterBreakWhenFalse); 2851 uninitsWhenTrue.andSet(uninitsAfterBreakWhenTrue); 2852 uninitsWhenFalse.andSet(uninitsAfterBreakWhenFalse); 2853 } 2854 }; 2855 merge(); 2856 recordExit(exit); 2857 return ; 2858 } else { 2859 scanExpr(tree.value); 2860 recordExit(new AssignPendingExit(tree, inits, uninits)); 2861 } 2862 } 2863 2864 @Override 2865 public void visitContinue(JCContinue tree) { 2866 recordExit(new AssignPendingExit(tree, inits, uninits)); 2867 } 2868 2869 @Override 2870 public void visitReturn(JCReturn tree) { 2871 scanExpr(tree.expr); 2872 recordExit(new AssignPendingExit(tree, inits, uninits)); 2873 } 2874 2875 public void visitThrow(JCThrow tree) { 2876 scanExpr(tree.expr); 2877 markDead(); 2878 } 2879 2880 public void visitApply(JCMethodInvocation tree) { 2881 scanExpr(tree.meth); 2882 scanExprs(tree.args); 2883 if (tree.meth.hasTag(IDENT)) { 2884 JCIdent ident = (JCIdent) tree.meth; 2885 if (ident.name != names._super && !ident.sym.isStatic()) 2886 checkEmbryonicThisExposure(tree); 2887 } 2888 } 2889 2890 public void visitNewClass(JCNewClass tree) { 2891 scanExpr(tree.encl); 2892 scanExprs(tree.args); 2893 scan(tree.def); 2894 if (classDef != null && tree.encl == null && tree.clazz.hasTag(IDENT)) { 2895 JCIdent clazz = (JCIdent) tree.clazz; 2896 if (!clazz.sym.isStatic() && clazz.type.getEnclosingType().tsym == classDef.sym) { 2897 checkEmbryonicThisExposure(tree); 2898 } 2899 } 2900 } 2901 2902 @Override 2903 public void visitLambda(JCLambda tree) { 2904 final Bits prevUninits = new Bits(uninits); 2905 final Bits prevInits = new Bits(inits); 2906 int returnadrPrev = returnadr; 2907 int nextadrPrev = nextadr; 2908 ListBuffer<PendingExit> prevPending = pendingExits; 2909 try { 2910 returnadr = nextadr; 2911 pendingExits = new ListBuffer<>(); 2912 for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { 2913 JCVariableDecl def = l.head; 2914 scan(def); 2915 inits.incl(def.sym.adr); 2916 uninits.excl(def.sym.adr); 2917 } 2918 if (tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION) { 2919 scanExpr(tree.body); 2920 } else { 2921 scan(tree.body); 2922 } 2923 } 2924 finally { 2925 returnadr = returnadrPrev; 2926 uninits.assign(prevUninits); 2927 inits.assign(prevInits); 2928 pendingExits = prevPending; 2929 nextadr = nextadrPrev; 2930 } 2931 } 2932 2933 public void visitNewArray(JCNewArray tree) { 2934 scanExprs(tree.dims); 2935 scanExprs(tree.elems); 2936 } 2937 2938 public void visitAssert(JCAssert tree) { 2939 final Bits initsExit = new Bits(inits); 2940 final Bits uninitsExit = new Bits(uninits); 2941 scanCond(tree.cond); 2942 uninitsExit.andSet(uninitsWhenTrue); 2943 if (tree.detail != null) { 2944 inits.assign(initsWhenFalse); 2945 uninits.assign(uninitsWhenFalse); 2946 scanExpr(tree.detail); 2947 } 2948 inits.assign(initsExit); 2949 uninits.assign(uninitsExit); 2950 } 2951 2952 public void visitAssign(JCAssign tree) { 2953 if (!TreeInfo.isIdentOrThisDotIdent(tree.lhs)) 2954 scanExpr(tree.lhs); 2955 scanExpr(tree.rhs); 2956 letInit(tree.lhs); 2957 } 2958 2959 // check fields accessed through this.<field> are definitely 2960 // assigned before reading their value 2961 public void visitSelect(JCFieldAccess tree) { 2962 ThisExposability priorThisExposability = this.thisExposability; 2963 try { 2964 if (tree.name == names._this && classDef != null && tree.sym.owner == classDef.sym) { 2965 checkEmbryonicThisExposure(tree); 2966 } else if (tree.sym.kind == VAR || tree.sym.isStatic()) { 2967 this.thisExposability = ALLOWED; 2968 } 2969 super.visitSelect(tree); 2970 if (TreeInfo.isThisQualifier(tree.selected) && 2971 tree.sym.kind == VAR) { 2972 checkInit(tree.pos(), (VarSymbol)tree.sym); 2973 } 2974 } finally { 2975 this.thisExposability = priorThisExposability; 2976 } 2977 } 2978 2979 public void visitAssignop(JCAssignOp tree) { 2980 scanExpr(tree.lhs); 2981 scanExpr(tree.rhs); 2982 letInit(tree.lhs); 2983 } 2984 2985 public void visitUnary(JCUnary tree) { 2986 switch (tree.getTag()) { 2987 case NOT: 2988 scanCond(tree.arg); 2989 final Bits t = new Bits(initsWhenFalse); 2990 initsWhenFalse.assign(initsWhenTrue); 2991 initsWhenTrue.assign(t); 2992 t.assign(uninitsWhenFalse); 2993 uninitsWhenFalse.assign(uninitsWhenTrue); 2994 uninitsWhenTrue.assign(t); 2995 break; 2996 case PREINC: case POSTINC: 2997 case PREDEC: case POSTDEC: 2998 scanExpr(tree.arg); 2999 letInit(tree.arg); 3000 break; 3001 default: 3002 scanExpr(tree.arg); 3003 } 3004 } 3005 3006 public void visitBinary(JCBinary tree) { 3007 switch (tree.getTag()) { 3008 case AND: 3009 scanCond(tree.lhs); 3010 final Bits initsWhenFalseLeft = new Bits(initsWhenFalse); 3011 final Bits uninitsWhenFalseLeft = new Bits(uninitsWhenFalse); 3012 inits.assign(initsWhenTrue); 3013 uninits.assign(uninitsWhenTrue); 3014 scanCond(tree.rhs); 3015 initsWhenFalse.andSet(initsWhenFalseLeft); 3016 uninitsWhenFalse.andSet(uninitsWhenFalseLeft); 3017 break; 3018 case OR: 3019 scanCond(tree.lhs); 3020 final Bits initsWhenTrueLeft = new Bits(initsWhenTrue); 3021 final Bits uninitsWhenTrueLeft = new Bits(uninitsWhenTrue); 3022 inits.assign(initsWhenFalse); 3023 uninits.assign(uninitsWhenFalse); 3024 scanCond(tree.rhs); 3025 initsWhenTrue.andSet(initsWhenTrueLeft); 3026 uninitsWhenTrue.andSet(uninitsWhenTrueLeft); 3027 break; 3028 default: 3029 scanExpr(tree.lhs); 3030 scanExpr(tree.rhs); 3031 } 3032 } 3033 3034 public void visitIdent(JCIdent tree) { 3035 if (tree.sym.kind == VAR) { 3036 checkInit(tree.pos(), (VarSymbol)tree.sym); 3037 referenced(tree.sym); 3038 } 3039 if (tree.name == names._this) { 3040 checkEmbryonicThisExposure(tree); 3041 } 3042 } 3043 3044 @Override 3045 public void visitTypeTest(JCInstanceOf tree) { 3046 scanExpr(tree.expr); 3047 scan(tree.pattern); 3048 } 3049 3050 @Override 3051 public void visitBindingPattern(JCBindingPattern tree) { 3052 scan(tree.var); 3053 initParam(tree.var); 3054 } 3055 3056 @Override 3057 public void visitPatternCaseLabel(JCPatternCaseLabel tree) { 3058 scan(tree.pat); 3059 scan(tree.guard); 3060 } 3061 3062 void referenced(Symbol sym) { 3063 unrefdResources.remove(sym); 3064 } 3065 3066 public void visitAnnotatedType(JCAnnotatedType tree) { 3067 // annotations don't get scanned 3068 tree.underlyingType.accept(this); 3069 } 3070 3071 public void visitModuleDef(JCModuleDecl tree) { 3072 // Do nothing for modules 3073 } 3074 3075 /************************************************************************** 3076 * main method 3077 *************************************************************************/ 3078 3079 /** Perform definite assignment/unassignment analysis on a tree. 3080 */ 3081 public void analyzeTree(Env<?> env, TreeMaker make) { 3082 analyzeTree(env, env.tree, make); 3083 } 3084 3085 public void analyzeTree(Env<?> env, JCTree tree, TreeMaker make) { 3086 try { 3087 startPos = tree.pos().getStartPosition(); 3088 3089 if (vardecls == null) 3090 vardecls = new JCVariableDecl[32]; 3091 else 3092 for (int i=0; i<vardecls.length; i++) 3093 vardecls[i] = null; 3094 firstadr = 0; 3095 nextadr = 0; 3096 Flow.this.make = make; 3097 pendingExits = new ListBuffer<>(); 3098 this.classDef = null; 3099 unrefdResources = WriteableScope.create(env.enclClass.sym); 3100 scan(tree); 3101 } finally { 3102 // note that recursive invocations of this method fail hard 3103 startPos = -1; 3104 resetBits(inits, uninits, uninitsTry, initsWhenTrue, 3105 initsWhenFalse, uninitsWhenTrue, uninitsWhenFalse); 3106 if (vardecls != null) { 3107 for (int i=0; i<vardecls.length; i++) 3108 vardecls[i] = null; 3109 } 3110 firstadr = 0; 3111 nextadr = 0; 3112 Flow.this.make = null; 3113 pendingExits = null; 3114 this.classDef = null; 3115 unrefdResources = null; 3116 } 3117 } 3118 } 3119 3120 /** 3121 * This pass implements the last step of the dataflow analysis, namely 3122 * the effectively-final analysis check. This checks that every local variable 3123 * reference from a lambda body/local inner class is either final or effectively final. 3124 * Additional this also checks that every variable that is used as an operand to 3125 * try-with-resources is final or effectively final. 3126 * As effectively final variables are marked as such during DA/DU, this pass must run after 3127 * AssignAnalyzer. 3128 */ 3129 class CaptureAnalyzer extends BaseAnalyzer { 3130 3131 JCTree currentTree; //local class or lambda 3132 3133 @Override 3134 void markDead() { 3135 //do nothing 3136 } 3137 3138 @SuppressWarnings("fallthrough") 3139 void checkEffectivelyFinal(DiagnosticPosition pos, VarSymbol sym) { 3140 if (currentTree != null && 3141 sym.owner.kind == MTH && 3142 sym.pos < currentTree.getStartPosition()) { 3143 switch (currentTree.getTag()) { 3144 case CLASSDEF: 3145 case PATTERNCASELABEL: 3146 case LAMBDA: 3147 if ((sym.flags() & (EFFECTIVELY_FINAL | FINAL)) == 0) { 3148 reportEffectivelyFinalError(pos, sym); 3149 } 3150 } 3151 } 3152 } 3153 3154 @SuppressWarnings("fallthrough") 3155 void letInit(JCTree tree) { 3156 tree = TreeInfo.skipParens(tree); 3157 if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) { 3158 Symbol sym = TreeInfo.symbol(tree); 3159 if (currentTree != null && 3160 sym.kind == VAR && 3161 sym.owner.kind == MTH && 3162 ((VarSymbol)sym).pos < currentTree.getStartPosition()) { 3163 switch (currentTree.getTag()) { 3164 case CLASSDEF: 3165 case CASE: 3166 case LAMBDA: 3167 reportEffectivelyFinalError(tree, sym); 3168 } 3169 } 3170 } 3171 } 3172 3173 void reportEffectivelyFinalError(DiagnosticPosition pos, Symbol sym) { 3174 Fragment subKey = switch (currentTree.getTag()) { 3175 case LAMBDA -> Fragments.Lambda; 3176 case PATTERNCASELABEL -> Fragments.Guard; 3177 case CLASSDEF -> Fragments.InnerCls; 3178 default -> throw new AssertionError("Unexpected tree kind: " + currentTree.getTag()); 3179 }; 3180 log.error(pos, Errors.CantRefNonEffectivelyFinalVar(sym, diags.fragment(subKey))); 3181 } 3182 3183 /************************************************************************* 3184 * Visitor methods for statements and definitions 3185 *************************************************************************/ 3186 3187 /* ------------ Visitor methods for various sorts of trees -------------*/ 3188 3189 public void visitClassDef(JCClassDecl tree) { 3190 JCTree prevTree = currentTree; 3191 try { 3192 currentTree = tree.sym.isDirectlyOrIndirectlyLocal() ? tree : null; 3193 super.visitClassDef(tree); 3194 } finally { 3195 currentTree = prevTree; 3196 } 3197 } 3198 3199 @Override 3200 public void visitLambda(JCLambda tree) { 3201 JCTree prevTree = currentTree; 3202 try { 3203 currentTree = tree; 3204 super.visitLambda(tree); 3205 } finally { 3206 currentTree = prevTree; 3207 } 3208 } 3209 3210 @Override 3211 public void visitBindingPattern(JCBindingPattern tree) { 3212 scan(tree.var); 3213 } 3214 3215 @Override 3216 public void visitParenthesizedPattern(JCParenthesizedPattern tree) { 3217 scan(tree.pattern); 3218 } 3219 3220 @Override 3221 public void visitPatternCaseLabel(JCPatternCaseLabel tree) { 3222 scan(tree.pat); 3223 JCTree prevTree = currentTree; 3224 try { 3225 currentTree = tree; 3226 scan(tree.guard); 3227 } finally { 3228 currentTree = prevTree; 3229 } 3230 } 3231 3232 @Override 3233 public void visitRecordPattern(JCRecordPattern tree) { 3234 scan(tree.deconstructor); 3235 scan(tree.nested); 3236 } 3237 3238 @Override 3239 public void visitIdent(JCIdent tree) { 3240 if (tree.sym.kind == VAR) { 3241 checkEffectivelyFinal(tree, (VarSymbol)tree.sym); 3242 } 3243 } 3244 3245 public void visitAssign(JCAssign tree) { 3246 JCTree lhs = TreeInfo.skipParens(tree.lhs); 3247 if (!(lhs instanceof JCIdent)) { 3248 scan(lhs); 3249 } 3250 scan(tree.rhs); 3251 letInit(lhs); 3252 } 3253 3254 public void visitAssignop(JCAssignOp tree) { 3255 scan(tree.lhs); 3256 scan(tree.rhs); 3257 letInit(tree.lhs); 3258 } 3259 3260 public void visitUnary(JCUnary tree) { 3261 switch (tree.getTag()) { 3262 case PREINC: case POSTINC: 3263 case PREDEC: case POSTDEC: 3264 scan(tree.arg); 3265 letInit(tree.arg); 3266 break; 3267 default: 3268 scan(tree.arg); 3269 } 3270 } 3271 3272 public void visitTry(JCTry tree) { 3273 for (JCTree resource : tree.resources) { 3274 if (!resource.hasTag(VARDEF)) { 3275 Symbol var = TreeInfo.symbol(resource); 3276 if (var != null && (var.flags() & (FINAL | EFFECTIVELY_FINAL)) == 0) { 3277 log.error(resource.pos(), Errors.TryWithResourcesExprEffectivelyFinalVar(var)); 3278 } 3279 } 3280 } 3281 super.visitTry(tree); 3282 } 3283 3284 @Override 3285 public void visitYield(JCYield tree) { 3286 scan(tree.value); 3287 } 3288 3289 public void visitModuleDef(JCModuleDecl tree) { 3290 // Do nothing for modules 3291 } 3292 3293 /************************************************************************** 3294 * main method 3295 *************************************************************************/ 3296 3297 /** Perform definite assignment/unassignment analysis on a tree. 3298 */ 3299 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { 3300 analyzeTree(env, env.tree, make); 3301 } 3302 public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) { 3303 try { 3304 attrEnv = env; 3305 Flow.this.make = make; 3306 pendingExits = new ListBuffer<>(); 3307 scan(tree); 3308 } finally { 3309 pendingExits = null; 3310 Flow.this.make = null; 3311 } 3312 } 3313 } 3314 3315 enum Liveness { 3316 ALIVE { 3317 @Override 3318 public Liveness or(Liveness other) { 3319 return this; 3320 } 3321 @Override 3322 public Liveness and(Liveness other) { 3323 return other; 3324 } 3325 }, 3326 DEAD { 3327 @Override 3328 public Liveness or(Liveness other) { 3329 return other; 3330 } 3331 @Override 3332 public Liveness and(Liveness other) { 3333 return this; 3334 } 3335 }, 3336 RECOVERY { 3337 @Override 3338 public Liveness or(Liveness other) { 3339 if (other == ALIVE) { 3340 return ALIVE; 3341 } else { 3342 return this; 3343 } 3344 } 3345 @Override 3346 public Liveness and(Liveness other) { 3347 if (other == DEAD) { 3348 return DEAD; 3349 } else { 3350 return this; 3351 } 3352 } 3353 }; 3354 3355 public abstract Liveness or(Liveness other); 3356 public abstract Liveness and(Liveness other); 3357 public Liveness or(boolean value) { 3358 return or(from(value)); 3359 } 3360 public Liveness and(boolean value) { 3361 return and(from(value)); 3362 } 3363 public static Liveness from(boolean value) { 3364 return value ? ALIVE : DEAD; 3365 } 3366 } 3367 3368 }