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