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