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