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