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