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