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