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