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