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