< prev index next >

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java

Print this page




 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 construcrors.
 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


 206     private final boolean allowEffectivelyFinalInInnerClasses;
 207 
 208     public static Flow instance(Context context) {
 209         Flow instance = context.get(flowKey);
 210         if (instance == null)
 211             instance = new Flow(context);
 212         return instance;
 213     }
 214 
 215     public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
 216         new AliveAnalyzer().analyzeTree(env, make);
 217         new AssignAnalyzer().analyzeTree(env, make);
 218         new FlowAnalyzer().analyzeTree(env, make);
 219         new CaptureAnalyzer().analyzeTree(env, make);
 220     }
 221 
 222     public void analyzeLambda(Env<AttrContext> env, JCLambda that, TreeMaker make, boolean speculative) {
 223         Log.DiagnosticHandler diagHandler = null;
 224         //we need to disable diagnostics temporarily; the problem is that if
 225         //a lambda expression contains e.g. an unreachable statement, an error
 226         //message will be reported and will cause compilation to skip the flow analyis
 227         //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
 228         //related errors, which will allow for more errors to be detected
 229         if (!speculative) {
 230             diagHandler = new Log.DiscardDiagnosticHandler(log);
 231         }
 232         try {
 233             new LambdaAliveAnalyzer().analyzeTree(env, that, make);
 234         } finally {
 235             if (!speculative) {
 236                 log.popDiagnosticHandler(diagHandler);
 237             }
 238         }
 239     }
 240 
 241     public List<Type> analyzeLambdaThrownTypes(final Env<AttrContext> env,
 242             JCLambda that, TreeMaker make) {
 243         //we need to disable diagnostics temporarily; the problem is that if
 244         //a lambda expression contains e.g. an unreachable statement, an error
 245         //message will be reported and will cause compilation to skip the flow analyis
 246         //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
 247         //related errors, which will allow for more errors to be detected
 248         Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
 249         try {
 250             new LambdaAssignAnalyzer(env).analyzeTree(env, that, make);
 251             LambdaFlowAnalyzer flowAnalyzer = new LambdaFlowAnalyzer();
 252             flowAnalyzer.analyzeTree(env, that, make);
 253             return flowAnalyzer.inferredThrownTypes;
 254         } finally {
 255             log.popDiagnosticHandler(diagHandler);
 256         }
 257     }
 258 
 259     public boolean aliveAfter(Env<AttrContext> env, JCTree that, TreeMaker make) {
 260         //we need to disable diagnostics temporarily; the problem is that if
 261         //"that" contains e.g. an unreachable statement, an error
 262         //message will be reported and will cause compilation to skip the flow analyis
 263         //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
 264         //related errors, which will allow for more errors to be detected
 265         Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
 266         try {
 267             SnippetAliveAnalyzer analyzer = new SnippetAliveAnalyzer();
 268 
 269             analyzer.analyzeTree(env, that, make);
 270             return analyzer.isAlive();
 271         } finally {
 272             log.popDiagnosticHandler(diagHandler);
 273         }
 274     }
 275 
 276     public boolean breaksOutOf(Env<AttrContext> env, JCTree loop, JCTree body, TreeMaker make) {
 277         //we need to disable diagnostics temporarily; the problem is that if
 278         //"that" contains e.g. an unreachable statement, an error
 279         //message will be reported and will cause compilation to skip the flow analyis
 280         //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
 281         //related errors, which will allow for more errors to be detected
 282         Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
 283         try {
 284             SnippetBreakAnalyzer analyzer = new SnippetBreakAnalyzer();
 285 
 286             analyzer.analyzeTree(env, body, make);
 287             return analyzer.breaksOut();
 288         } finally {
 289             log.popDiagnosticHandler(diagHandler);
 290         }
 291     }
 292 
 293     /**
 294      * Definite assignment scan mode
 295      */
 296     enum FlowKind {
 297         /**
 298          * This is the normal DA/DU analysis mode
 299          */


1293         }
1294 
1295         @Override
1296         public void visitIf(JCIf tree) {
1297             scan(tree.cond);
1298             scan(tree.thenpart);
1299             if (tree.elsepart != null) {
1300                 scan(tree.elsepart);
1301             }
1302         }
1303 
1304         void checkCaughtType(DiagnosticPosition pos, Type exc, List<Type> thrownInTry, List<Type> caughtInTry) {
1305             if (chk.subset(exc, caughtInTry)) {
1306                 log.error(pos, Errors.ExceptAlreadyCaught(exc));
1307             } else if (!chk.isUnchecked(pos, exc) &&
1308                     !isExceptionOrThrowable(exc) &&
1309                     !chk.intersects(exc, thrownInTry)) {
1310                 log.error(pos, Errors.ExceptNeverThrownInTry(exc));
1311             } else {
1312                 List<Type> catchableThrownTypes = chk.intersect(List.of(exc), thrownInTry);
1313                 // 'catchableThrownTypes' cannnot possibly be empty - if 'exc' was an
1314                 // unchecked exception, the result list would not be empty, as the augmented
1315                 // thrown set includes { RuntimeException, Error }; if 'exc' was a checked
1316                 // exception, that would have been covered in the branch above
1317                 if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty() &&
1318                         !isExceptionOrThrowable(exc)) {
1319                     Warning key = catchableThrownTypes.length() == 1 ?
1320                             Warnings.UnreachableCatch(catchableThrownTypes) :
1321                             Warnings.UnreachableCatch1(catchableThrownTypes);
1322                     log.warning(pos, key);
1323                 }
1324             }
1325         }
1326         //where
1327             private boolean isExceptionOrThrowable(Type exc) {
1328                 return exc.tsym == syms.throwableType.tsym ||
1329                     exc.tsym == syms.exceptionType.tsym;
1330             }
1331 
1332         public void visitBreak(JCBreak tree) {
1333             recordExit(new PendingExit(tree));




 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


 206     private final boolean allowEffectivelyFinalInInnerClasses;
 207 
 208     public static Flow instance(Context context) {
 209         Flow instance = context.get(flowKey);
 210         if (instance == null)
 211             instance = new Flow(context);
 212         return instance;
 213     }
 214 
 215     public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
 216         new AliveAnalyzer().analyzeTree(env, make);
 217         new AssignAnalyzer().analyzeTree(env, make);
 218         new FlowAnalyzer().analyzeTree(env, make);
 219         new CaptureAnalyzer().analyzeTree(env, make);
 220     }
 221 
 222     public void analyzeLambda(Env<AttrContext> env, JCLambda that, TreeMaker make, boolean speculative) {
 223         Log.DiagnosticHandler diagHandler = null;
 224         //we need to disable diagnostics temporarily; the problem is that if
 225         //a lambda expression contains e.g. an unreachable statement, an error
 226         //message will be reported and will cause compilation to skip the flow analysis
 227         //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
 228         //related errors, which will allow for more errors to be detected
 229         if (!speculative) {
 230             diagHandler = new Log.DiscardDiagnosticHandler(log);
 231         }
 232         try {
 233             new LambdaAliveAnalyzer().analyzeTree(env, that, make);
 234         } finally {
 235             if (!speculative) {
 236                 log.popDiagnosticHandler(diagHandler);
 237             }
 238         }
 239     }
 240 
 241     public List<Type> analyzeLambdaThrownTypes(final Env<AttrContext> env,
 242             JCLambda that, TreeMaker make) {
 243         //we need to disable diagnostics temporarily; the problem is that if
 244         //a lambda expression contains e.g. an unreachable statement, an error
 245         //message will be reported and will cause compilation to skip the flow analysis
 246         //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
 247         //related errors, which will allow for more errors to be detected
 248         Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
 249         try {
 250             new LambdaAssignAnalyzer(env).analyzeTree(env, that, make);
 251             LambdaFlowAnalyzer flowAnalyzer = new LambdaFlowAnalyzer();
 252             flowAnalyzer.analyzeTree(env, that, make);
 253             return flowAnalyzer.inferredThrownTypes;
 254         } finally {
 255             log.popDiagnosticHandler(diagHandler);
 256         }
 257     }
 258 
 259     public boolean aliveAfter(Env<AttrContext> env, JCTree that, TreeMaker make) {
 260         //we need to disable diagnostics temporarily; the problem is that if
 261         //"that" contains e.g. an unreachable statement, an error
 262         //message will be reported and will cause compilation to skip the flow analysis
 263         //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
 264         //related errors, which will allow for more errors to be detected
 265         Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
 266         try {
 267             SnippetAliveAnalyzer analyzer = new SnippetAliveAnalyzer();
 268 
 269             analyzer.analyzeTree(env, that, make);
 270             return analyzer.isAlive();
 271         } finally {
 272             log.popDiagnosticHandler(diagHandler);
 273         }
 274     }
 275 
 276     public boolean breaksOutOf(Env<AttrContext> env, JCTree loop, JCTree body, TreeMaker make) {
 277         //we need to disable diagnostics temporarily; the problem is that if
 278         //"that" contains e.g. an unreachable statement, an error
 279         //message will be reported and will cause compilation to skip the flow analysis
 280         //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
 281         //related errors, which will allow for more errors to be detected
 282         Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
 283         try {
 284             SnippetBreakAnalyzer analyzer = new SnippetBreakAnalyzer();
 285 
 286             analyzer.analyzeTree(env, body, make);
 287             return analyzer.breaksOut();
 288         } finally {
 289             log.popDiagnosticHandler(diagHandler);
 290         }
 291     }
 292 
 293     /**
 294      * Definite assignment scan mode
 295      */
 296     enum FlowKind {
 297         /**
 298          * This is the normal DA/DU analysis mode
 299          */


1293         }
1294 
1295         @Override
1296         public void visitIf(JCIf tree) {
1297             scan(tree.cond);
1298             scan(tree.thenpart);
1299             if (tree.elsepart != null) {
1300                 scan(tree.elsepart);
1301             }
1302         }
1303 
1304         void checkCaughtType(DiagnosticPosition pos, Type exc, List<Type> thrownInTry, List<Type> caughtInTry) {
1305             if (chk.subset(exc, caughtInTry)) {
1306                 log.error(pos, Errors.ExceptAlreadyCaught(exc));
1307             } else if (!chk.isUnchecked(pos, exc) &&
1308                     !isExceptionOrThrowable(exc) &&
1309                     !chk.intersects(exc, thrownInTry)) {
1310                 log.error(pos, Errors.ExceptNeverThrownInTry(exc));
1311             } else {
1312                 List<Type> catchableThrownTypes = chk.intersect(List.of(exc), thrownInTry);
1313                 // 'catchableThrownTypes' cannot possibly be empty - if 'exc' was an
1314                 // unchecked exception, the result list would not be empty, as the augmented
1315                 // thrown set includes { RuntimeException, Error }; if 'exc' was a checked
1316                 // exception, that would have been covered in the branch above
1317                 if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty() &&
1318                         !isExceptionOrThrowable(exc)) {
1319                     Warning key = catchableThrownTypes.length() == 1 ?
1320                             Warnings.UnreachableCatch(catchableThrownTypes) :
1321                             Warnings.UnreachableCatch1(catchableThrownTypes);
1322                     log.warning(pos, key);
1323                 }
1324             }
1325         }
1326         //where
1327             private boolean isExceptionOrThrowable(Type exc) {
1328                 return exc.tsym == syms.throwableType.tsym ||
1329                     exc.tsym == syms.exceptionType.tsym;
1330             }
1331 
1332         public void visitBreak(JCBreak tree) {
1333             recordExit(new PendingExit(tree));


< prev index next >