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