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