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