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