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