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