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