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