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