7175538: Integrate efectively final check with DA/DU analysis

Thu, 02 Aug 2012 18:23:21 +0100

author
mcimadamore
date
Thu, 02 Aug 2012 18:23:21 +0100
changeset 1297
e5cf1569d3a4
parent 1296
cddc2c894cc6
child 1298
2d75e7c952b8

7175538: Integrate efectively final check with DA/DU analysis
Summary: Allow generalized effectively-final analysis for all local variables
Reviewed-by: jjg, dlsmith

src/share/classes/com/sun/tools/javac/code/Source.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/comp/Attr.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/comp/Flow.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/parser/JavacParser.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/resources/compiler.properties file | annotate | diff | comparison | revisions
test/tools/javac/diags/examples/CantRefNonEffectivelyFinalVar.java file | annotate | diff | comparison | revisions
test/tools/javac/lambda/EffectivelyFinalTest.java file | annotate | diff | comparison | revisions
test/tools/javac/lambda/EffectivelyFinalTest01.out file | annotate | diff | comparison | revisions
test/tools/javac/lambda/EffectivelyFinalTest02.out file | annotate | diff | comparison | revisions
     1.1 --- a/src/share/classes/com/sun/tools/javac/code/Source.java	Thu Aug 02 18:22:41 2012 +0100
     1.2 +++ b/src/share/classes/com/sun/tools/javac/code/Source.java	Thu Aug 02 18:23:21 2012 +0100
     1.3 @@ -200,6 +200,9 @@
     1.4      public boolean allowMethodReferences() {
     1.5          return compareTo(JDK1_8) >= 0;
     1.6      }
     1.7 +    public boolean allowEffectivelyFinalInInnerClasses() {
     1.8 +        return compareTo(JDK1_8) >= 0;
     1.9 +    }
    1.10      public static SourceVersion toSourceVersion(Source source) {
    1.11          switch(source) {
    1.12          case JDK1_2:
     2.1 --- a/src/share/classes/com/sun/tools/javac/comp/Attr.java	Thu Aug 02 18:22:41 2012 +0100
     2.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java	Thu Aug 02 18:23:21 2012 +0100
     2.3 @@ -228,7 +228,7 @@
     2.4       *  @param env    The current environment.
     2.5       */
     2.6      boolean isAssignableAsBlankFinal(VarSymbol v, Env<AttrContext> env) {
     2.7 -        Symbol owner = env.info.scope.owner;
     2.8 +        Symbol owner = owner(env);
     2.9             // owner refers to the innermost variable, method or
    2.10             // initializer block declaration at this point.
    2.11          return
    2.12 @@ -243,6 +243,41 @@
    2.13               ((v.flags() & STATIC) != 0) == Resolve.isStatic(env));
    2.14      }
    2.15  
    2.16 +    /**
    2.17 +     * Return the innermost enclosing owner symbol in a given attribution context
    2.18 +     */
    2.19 +    Symbol owner(Env<AttrContext> env) {
    2.20 +        while (true) {
    2.21 +            switch (env.tree.getTag()) {
    2.22 +                case VARDEF:
    2.23 +                    //a field can be owner
    2.24 +                    VarSymbol vsym = ((JCVariableDecl)env.tree).sym;
    2.25 +                    if (vsym.owner.kind == TYP) {
    2.26 +                        return vsym;
    2.27 +                    }
    2.28 +                    break;
    2.29 +                case METHODDEF:
    2.30 +                    //method def is always an owner
    2.31 +                    return ((JCMethodDecl)env.tree).sym;
    2.32 +                case CLASSDEF:
    2.33 +                    //class def is always an owner
    2.34 +                    return ((JCClassDecl)env.tree).sym;
    2.35 +                case BLOCK:
    2.36 +                    //static/instance init blocks are owner
    2.37 +                    Symbol blockSym = env.info.scope.owner;
    2.38 +                    if ((blockSym.flags() & BLOCK) != 0) {
    2.39 +                        return blockSym;
    2.40 +                    }
    2.41 +                    break;
    2.42 +                case TOPLEVEL:
    2.43 +                    //toplevel is always an owner (for pkge decls)
    2.44 +                    return env.info.scope.owner;
    2.45 +            }
    2.46 +            Assert.checkNonNull(env.next);
    2.47 +            env = env.next;
    2.48 +        }
    2.49 +    }
    2.50 +
    2.51      /** Check that variable can be assigned to.
    2.52       *  @param pos    The current source code position.
    2.53       *  @param v      The assigned varaible
    2.54 @@ -883,7 +918,6 @@
    2.55                  memberEnter.memberEnter(tree, env);
    2.56                  annotate.flush();
    2.57              }
    2.58 -            tree.sym.flags_field |= EFFECTIVELY_FINAL;
    2.59          }
    2.60  
    2.61          VarSymbol v = tree.sym;
    2.62 @@ -2187,16 +2221,6 @@
    2.63              // illegal forward reference.
    2.64              checkInit(tree, env, v, false);
    2.65  
    2.66 -            // If symbol is a local variable accessed from an embedded
    2.67 -            // inner class check that it is final.
    2.68 -            if (v.owner.kind == MTH &&
    2.69 -                v.owner != env.info.scope.owner &&
    2.70 -                (v.flags_field & FINAL) == 0) {
    2.71 -                log.error(tree.pos(),
    2.72 -                          "local.var.accessed.from.icls.needs.final",
    2.73 -                          v);
    2.74 -            }
    2.75 -
    2.76              // If we are expecting a variable (as opposed to a value), check
    2.77              // that the variable is assignable in the current environment.
    2.78              if (pkind() == VAR)
    2.79 @@ -2590,7 +2614,7 @@
    2.80              // and are subject to definite assignment checking.
    2.81              if ((env.info.enclVar == v || v.pos > tree.pos) &&
    2.82                  v.owner.kind == TYP &&
    2.83 -                canOwnInitializer(env.info.scope.owner) &&
    2.84 +                canOwnInitializer(owner(env)) &&
    2.85                  v.owner == env.info.scope.owner.enclClass() &&
    2.86                  ((v.flags() & STATIC) != 0) == Resolve.isStatic(env) &&
    2.87                  (!env.tree.hasTag(ASSIGN) ||
     3.1 --- a/src/share/classes/com/sun/tools/javac/comp/Flow.java	Thu Aug 02 18:22:41 2012 +0100
     3.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Flow.java	Thu Aug 02 18:23:21 2012 +0100
     3.3 @@ -43,13 +43,15 @@
     3.4  import static com.sun.tools.javac.code.TypeTags.*;
     3.5  import static com.sun.tools.javac.tree.JCTree.Tag.*;
     3.6  
     3.7 -/** This pass implements dataflow analysis for Java programs.
     3.8 - *  Liveness analysis checks that every statement is reachable.
     3.9 - *  Exception analysis ensures that every checked exception that is
    3.10 - *  thrown is declared or caught.  Definite assignment analysis
    3.11 - *  ensures that each variable is assigned when used.  Definite
    3.12 - *  unassignment analysis ensures that no final variable is assigned
    3.13 - *  more than once.
    3.14 +/** This pass implements dataflow analysis for Java programs though
    3.15 + *  different AST visitor steps. Liveness analysis (see AliveAlanyzer) checks that
    3.16 + *  every statement is reachable. Exception analysis (see FlowAnalyzer) ensures that
    3.17 + *  every checked exception that is thrown is declared or caught.  Definite assignment analysis
    3.18 + *  (see AssignAnalyzer) ensures that each variable is assigned when used.  Definite
    3.19 + *  unassignment analysis (see AssignAnalyzer) in ensures that no final variable
    3.20 + *  is assigned more than once. Finally, local variable capture analysis (see CaptureAnalyzer)
    3.21 + *  determines that local variables accessed within the scope of an inner class are
    3.22 + *  either final or effectively-final.
    3.23   *
    3.24   *  <p>The JLS has a number of problems in the
    3.25   *  specification of these flow analysis problems. This implementation
    3.26 @@ -188,10 +190,12 @@
    3.27      private final Check chk;
    3.28      private       TreeMaker make;
    3.29      private final Resolve rs;
    3.30 +    private final JCDiagnostic.Factory diags;
    3.31      private Env<AttrContext> attrEnv;
    3.32      private       Lint lint;
    3.33      private final boolean allowImprovedRethrowAnalysis;
    3.34      private final boolean allowImprovedCatchAnalysis;
    3.35 +    private final boolean allowEffectivelyFinalInInnerClasses;
    3.36  
    3.37      public static Flow instance(Context context) {
    3.38          Flow instance = context.get(flowKey);
    3.39 @@ -201,8 +205,37 @@
    3.40      }
    3.41  
    3.42      public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
    3.43 +        new AliveAnalyzer().analyzeTree(env, make);
    3.44 +        new AssignAnalyzer().analyzeTree(env, make);
    3.45          new FlowAnalyzer().analyzeTree(env, make);
    3.46 -        new AssignAnalyzer().analyzeTree(env, make);
    3.47 +        new CaptureAnalyzer().analyzeTree(env, make);
    3.48 +    }
    3.49 +
    3.50 +    /**
    3.51 +     * Definite assignment scan mode
    3.52 +     */
    3.53 +    enum FlowKind {
    3.54 +        /**
    3.55 +         * This is the normal DA/DU analysis mode
    3.56 +         */
    3.57 +        NORMAL("var.might.already.be.assigned", false),
    3.58 +        /**
    3.59 +         * This is the speculative DA/DU analysis mode used to speculatively
    3.60 +         * derive assertions within loop bodies
    3.61 +         */
    3.62 +        SPECULATIVE_LOOP("var.might.be.assigned.in.loop", true);
    3.63 +
    3.64 +        String errKey;
    3.65 +        boolean isFinal;
    3.66 +
    3.67 +        FlowKind(String errKey, boolean isFinal) {
    3.68 +            this.errKey = errKey;
    3.69 +            this.isFinal = isFinal;
    3.70 +        }
    3.71 +
    3.72 +        boolean isFinal() {
    3.73 +            return isFinal;
    3.74 +        }
    3.75      }
    3.76  
    3.77      protected Flow(Context context) {
    3.78 @@ -214,9 +247,13 @@
    3.79          chk = Check.instance(context);
    3.80          lint = Lint.instance(context);
    3.81          rs = Resolve.instance(context);
    3.82 +        diags = JCDiagnostic.Factory.instance(context);
    3.83          Source source = Source.instance(context);
    3.84          allowImprovedRethrowAnalysis = source.allowImprovedRethrowAnalysis();
    3.85          allowImprovedCatchAnalysis = source.allowImprovedCatchAnalysis();
    3.86 +        Options options = Options.instance(context);
    3.87 +        allowEffectivelyFinalInInnerClasses = source.allowEffectivelyFinalInInnerClasses() &&
    3.88 +                options.isSet("allowEffectivelyFinalInInnerClasses"); //pre-lambda guard
    3.89      }
    3.90  
    3.91      /**
    3.92 @@ -259,14 +296,16 @@
    3.93           *  will typically result in an error unless it is within a
    3.94           *  try-finally whose finally block cannot complete normally.
    3.95           */
    3.96 -        abstract static class PendingExit {
    3.97 +        static class PendingExit {
    3.98              JCTree tree;
    3.99  
   3.100              PendingExit(JCTree tree) {
   3.101                  this.tree = tree;
   3.102              }
   3.103  
   3.104 -            abstract void resolveJump();
   3.105 +            void resolveJump() {
   3.106 +                //do nothing
   3.107 +            }
   3.108          }
   3.109  
   3.110          abstract void markDead();
   3.111 @@ -309,18 +348,352 @@
   3.112      }
   3.113  
   3.114      /**
   3.115 -     * This pass implements the first two steps of the dataflow analysis:
   3.116 -     * (i) liveness analysis checks that every statement is reachable and (ii)
   3.117 -     *  exception analysis to ensure that every checked exception that is
   3.118 -     *  thrown is declared or caught.
   3.119 +     * This pass implements the first step of the dataflow analysis, namely
   3.120 +     * the liveness analysis check. This checks that every statement is reachable.
   3.121 +     * The output of this analysis pass are used by other analyzers. This analyzer
   3.122 +     * sets the 'finallyCanCompleteNormally' field in the JCTry class.
   3.123 +     */
   3.124 +    class AliveAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> {
   3.125 +
   3.126 +        /** A flag that indicates whether the last statement could
   3.127 +         *  complete normally.
   3.128 +         */
   3.129 +        private boolean alive;
   3.130 +
   3.131 +        @Override
   3.132 +        void markDead() {
   3.133 +            alive = false;
   3.134 +        }
   3.135 +
   3.136 +    /*************************************************************************
   3.137 +     * Visitor methods for statements and definitions
   3.138 +     *************************************************************************/
   3.139 +
   3.140 +        /** Analyze a definition.
   3.141 +         */
   3.142 +        void scanDef(JCTree tree) {
   3.143 +            scanStat(tree);
   3.144 +            if (tree != null && tree.hasTag(JCTree.Tag.BLOCK) && !alive) {
   3.145 +                log.error(tree.pos(),
   3.146 +                          "initializer.must.be.able.to.complete.normally");
   3.147 +            }
   3.148 +        }
   3.149 +
   3.150 +        /** Analyze a statement. Check that statement is reachable.
   3.151 +         */
   3.152 +        void scanStat(JCTree tree) {
   3.153 +            if (!alive && tree != null) {
   3.154 +                log.error(tree.pos(), "unreachable.stmt");
   3.155 +                if (!tree.hasTag(SKIP)) alive = true;
   3.156 +            }
   3.157 +            scan(tree);
   3.158 +        }
   3.159 +
   3.160 +        /** Analyze list of statements.
   3.161 +         */
   3.162 +        void scanStats(List<? extends JCStatement> trees) {
   3.163 +            if (trees != null)
   3.164 +                for (List<? extends JCStatement> l = trees; l.nonEmpty(); l = l.tail)
   3.165 +                    scanStat(l.head);
   3.166 +        }
   3.167 +
   3.168 +        /* ------------ Visitor methods for various sorts of trees -------------*/
   3.169 +
   3.170 +        public void visitClassDef(JCClassDecl tree) {
   3.171 +            if (tree.sym == null) return;
   3.172 +            boolean alivePrev = alive;
   3.173 +            ListBuffer<PendingExit> pendingExitsPrev = pendingExits;
   3.174 +            Lint lintPrev = lint;
   3.175 +
   3.176 +            pendingExits = new ListBuffer<PendingExit>();
   3.177 +            lint = lint.augment(tree.sym.attributes_field);
   3.178 +
   3.179 +            try {
   3.180 +                // process all the static initializers
   3.181 +                for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
   3.182 +                    if (!l.head.hasTag(METHODDEF) &&
   3.183 +                        (TreeInfo.flags(l.head) & STATIC) != 0) {
   3.184 +                        scanDef(l.head);
   3.185 +                    }
   3.186 +                }
   3.187 +
   3.188 +                // process all the instance initializers
   3.189 +                for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
   3.190 +                    if (!l.head.hasTag(METHODDEF) &&
   3.191 +                        (TreeInfo.flags(l.head) & STATIC) == 0) {
   3.192 +                        scanDef(l.head);
   3.193 +                    }
   3.194 +                }
   3.195 +
   3.196 +                // process all the methods
   3.197 +                for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
   3.198 +                    if (l.head.hasTag(METHODDEF)) {
   3.199 +                        scan(l.head);
   3.200 +                    }
   3.201 +                }
   3.202 +            } finally {
   3.203 +                pendingExits = pendingExitsPrev;
   3.204 +                alive = alivePrev;
   3.205 +                lint = lintPrev;
   3.206 +            }
   3.207 +        }
   3.208 +
   3.209 +        public void visitMethodDef(JCMethodDecl tree) {
   3.210 +            if (tree.body == null) return;
   3.211 +            Lint lintPrev = lint;
   3.212 +
   3.213 +            lint = lint.augment(tree.sym.attributes_field);
   3.214 +
   3.215 +            Assert.check(pendingExits.isEmpty());
   3.216 +
   3.217 +            try {
   3.218 +                alive = true;
   3.219 +                scanStat(tree.body);
   3.220 +
   3.221 +                if (alive && tree.sym.type.getReturnType().tag != VOID)
   3.222 +                    log.error(TreeInfo.diagEndPos(tree.body), "missing.ret.stmt");
   3.223 +
   3.224 +                List<PendingExit> exits = pendingExits.toList();
   3.225 +                pendingExits = new ListBuffer<PendingExit>();
   3.226 +                while (exits.nonEmpty()) {
   3.227 +                    PendingExit exit = exits.head;
   3.228 +                    exits = exits.tail;
   3.229 +                    Assert.check(exit.tree.hasTag(RETURN));
   3.230 +                }
   3.231 +            } finally {
   3.232 +                lint = lintPrev;
   3.233 +            }
   3.234 +        }
   3.235 +
   3.236 +        public void visitVarDef(JCVariableDecl tree) {
   3.237 +            if (tree.init != null) {
   3.238 +                Lint lintPrev = lint;
   3.239 +                lint = lint.augment(tree.sym.attributes_field);
   3.240 +                try{
   3.241 +                    scan(tree.init);
   3.242 +                } finally {
   3.243 +                    lint = lintPrev;
   3.244 +                }
   3.245 +            }
   3.246 +        }
   3.247 +
   3.248 +        public void visitBlock(JCBlock tree) {
   3.249 +            scanStats(tree.stats);
   3.250 +        }
   3.251 +
   3.252 +        public void visitDoLoop(JCDoWhileLoop tree) {
   3.253 +            ListBuffer<PendingExit> prevPendingExits = pendingExits;
   3.254 +            pendingExits = new ListBuffer<PendingExit>();
   3.255 +            scanStat(tree.body);
   3.256 +            alive |= resolveContinues(tree);
   3.257 +            scan(tree.cond);
   3.258 +            alive = alive && !tree.cond.type.isTrue();
   3.259 +            alive |= resolveBreaks(tree, prevPendingExits);
   3.260 +        }
   3.261 +
   3.262 +        public void visitWhileLoop(JCWhileLoop tree) {
   3.263 +            ListBuffer<PendingExit> prevPendingExits = pendingExits;
   3.264 +            pendingExits = new ListBuffer<PendingExit>();
   3.265 +            scan(tree.cond);
   3.266 +            alive = !tree.cond.type.isFalse();
   3.267 +            scanStat(tree.body);
   3.268 +            alive |= resolveContinues(tree);
   3.269 +            alive = resolveBreaks(tree, prevPendingExits) ||
   3.270 +                !tree.cond.type.isTrue();
   3.271 +        }
   3.272 +
   3.273 +        public void visitForLoop(JCForLoop tree) {
   3.274 +            ListBuffer<PendingExit> prevPendingExits = pendingExits;
   3.275 +            scanStats(tree.init);
   3.276 +            pendingExits = new ListBuffer<PendingExit>();
   3.277 +            if (tree.cond != null) {
   3.278 +                scan(tree.cond);
   3.279 +                alive = !tree.cond.type.isFalse();
   3.280 +            } else {
   3.281 +                alive = true;
   3.282 +            }
   3.283 +            scanStat(tree.body);
   3.284 +            alive |= resolveContinues(tree);
   3.285 +            scan(tree.step);
   3.286 +            alive = resolveBreaks(tree, prevPendingExits) ||
   3.287 +                tree.cond != null && !tree.cond.type.isTrue();
   3.288 +        }
   3.289 +
   3.290 +        public void visitForeachLoop(JCEnhancedForLoop tree) {
   3.291 +            visitVarDef(tree.var);
   3.292 +            ListBuffer<PendingExit> prevPendingExits = pendingExits;
   3.293 +            scan(tree.expr);
   3.294 +            pendingExits = new ListBuffer<PendingExit>();
   3.295 +            scanStat(tree.body);
   3.296 +            alive |= resolveContinues(tree);
   3.297 +            resolveBreaks(tree, prevPendingExits);
   3.298 +            alive = true;
   3.299 +        }
   3.300 +
   3.301 +        public void visitLabelled(JCLabeledStatement tree) {
   3.302 +            ListBuffer<PendingExit> prevPendingExits = pendingExits;
   3.303 +            pendingExits = new ListBuffer<PendingExit>();
   3.304 +            scanStat(tree.body);
   3.305 +            alive |= resolveBreaks(tree, prevPendingExits);
   3.306 +        }
   3.307 +
   3.308 +        public void visitSwitch(JCSwitch tree) {
   3.309 +            ListBuffer<PendingExit> prevPendingExits = pendingExits;
   3.310 +            pendingExits = new ListBuffer<PendingExit>();
   3.311 +            scan(tree.selector);
   3.312 +            boolean hasDefault = false;
   3.313 +            for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
   3.314 +                alive = true;
   3.315 +                JCCase c = l.head;
   3.316 +                if (c.pat == null)
   3.317 +                    hasDefault = true;
   3.318 +                else
   3.319 +                    scan(c.pat);
   3.320 +                scanStats(c.stats);
   3.321 +                // Warn about fall-through if lint switch fallthrough enabled.
   3.322 +                if (alive &&
   3.323 +                    lint.isEnabled(Lint.LintCategory.FALLTHROUGH) &&
   3.324 +                    c.stats.nonEmpty() && l.tail.nonEmpty())
   3.325 +                    log.warning(Lint.LintCategory.FALLTHROUGH,
   3.326 +                                l.tail.head.pos(),
   3.327 +                                "possible.fall-through.into.case");
   3.328 +            }
   3.329 +            if (!hasDefault) {
   3.330 +                alive = true;
   3.331 +            }
   3.332 +            alive |= resolveBreaks(tree, prevPendingExits);
   3.333 +        }
   3.334 +
   3.335 +        public void visitTry(JCTry tree) {
   3.336 +            ListBuffer<PendingExit> prevPendingExits = pendingExits;
   3.337 +            pendingExits = new ListBuffer<PendingExit>();
   3.338 +            for (JCTree resource : tree.resources) {
   3.339 +                if (resource instanceof JCVariableDecl) {
   3.340 +                    JCVariableDecl vdecl = (JCVariableDecl) resource;
   3.341 +                    visitVarDef(vdecl);
   3.342 +                } else if (resource instanceof JCExpression) {
   3.343 +                    scan((JCExpression) resource);
   3.344 +                } else {
   3.345 +                    throw new AssertionError(tree);  // parser error
   3.346 +                }
   3.347 +            }
   3.348 +
   3.349 +            scanStat(tree.body);
   3.350 +            boolean aliveEnd = alive;
   3.351 +
   3.352 +            for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
   3.353 +                alive = true;
   3.354 +                JCVariableDecl param = l.head.param;
   3.355 +                scan(param);
   3.356 +                scanStat(l.head.body);
   3.357 +                aliveEnd |= alive;
   3.358 +            }
   3.359 +            if (tree.finalizer != null) {
   3.360 +                ListBuffer<PendingExit> exits = pendingExits;
   3.361 +                pendingExits = prevPendingExits;
   3.362 +                alive = true;
   3.363 +                scanStat(tree.finalizer);
   3.364 +                tree.finallyCanCompleteNormally = alive;
   3.365 +                if (!alive) {
   3.366 +                    if (lint.isEnabled(Lint.LintCategory.FINALLY)) {
   3.367 +                        log.warning(Lint.LintCategory.FINALLY,
   3.368 +                                TreeInfo.diagEndPos(tree.finalizer),
   3.369 +                                "finally.cannot.complete");
   3.370 +                    }
   3.371 +                } else {
   3.372 +                    while (exits.nonEmpty()) {
   3.373 +                        pendingExits.append(exits.next());
   3.374 +                    }
   3.375 +                    alive = aliveEnd;
   3.376 +                }
   3.377 +            } else {
   3.378 +                alive = aliveEnd;
   3.379 +                ListBuffer<PendingExit> exits = pendingExits;
   3.380 +                pendingExits = prevPendingExits;
   3.381 +                while (exits.nonEmpty()) pendingExits.append(exits.next());
   3.382 +            }
   3.383 +        }
   3.384 +
   3.385 +        @Override
   3.386 +        public void visitIf(JCIf tree) {
   3.387 +            scan(tree.cond);
   3.388 +            scanStat(tree.thenpart);
   3.389 +            if (tree.elsepart != null) {
   3.390 +                boolean aliveAfterThen = alive;
   3.391 +                alive = true;
   3.392 +                scanStat(tree.elsepart);
   3.393 +                alive = alive | aliveAfterThen;
   3.394 +            } else {
   3.395 +                alive = true;
   3.396 +            }
   3.397 +        }
   3.398 +
   3.399 +        public void visitBreak(JCBreak tree) {
   3.400 +            recordExit(tree, new PendingExit(tree));
   3.401 +        }
   3.402 +
   3.403 +        public void visitContinue(JCContinue tree) {
   3.404 +            recordExit(tree, new PendingExit(tree));
   3.405 +        }
   3.406 +
   3.407 +        public void visitReturn(JCReturn tree) {
   3.408 +            scan(tree.expr);
   3.409 +            recordExit(tree, new PendingExit(tree));
   3.410 +        }
   3.411 +
   3.412 +        public void visitThrow(JCThrow tree) {
   3.413 +            scan(tree.expr);
   3.414 +            markDead();
   3.415 +        }
   3.416 +
   3.417 +        public void visitApply(JCMethodInvocation tree) {
   3.418 +            scan(tree.meth);
   3.419 +            scan(tree.args);
   3.420 +        }
   3.421 +
   3.422 +        public void visitNewClass(JCNewClass tree) {
   3.423 +            scan(tree.encl);
   3.424 +            scan(tree.args);
   3.425 +            if (tree.def != null) {
   3.426 +                scan(tree.def);
   3.427 +            }
   3.428 +        }
   3.429 +
   3.430 +        public void visitTopLevel(JCCompilationUnit tree) {
   3.431 +            // Do nothing for TopLevel since each class is visited individually
   3.432 +        }
   3.433 +
   3.434 +    /**************************************************************************
   3.435 +     * main method
   3.436 +     *************************************************************************/
   3.437 +
   3.438 +        /** Perform definite assignment/unassignment analysis on a tree.
   3.439 +         */
   3.440 +        public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
   3.441 +            try {
   3.442 +                attrEnv = env;
   3.443 +                Flow.this.make = make;
   3.444 +                pendingExits = new ListBuffer<PendingExit>();
   3.445 +                alive = true;
   3.446 +                scan(env.tree);
   3.447 +            } finally {
   3.448 +                pendingExits = null;
   3.449 +                Flow.this.make = null;
   3.450 +            }
   3.451 +        }
   3.452 +    }
   3.453 +
   3.454 +    /**
   3.455 +     * This pass implements the second step of the dataflow analysis, namely
   3.456 +     * the exception analysis. This is to ensure that every checked exception that is
   3.457 +     * thrown is declared or caught. The analyzer uses some info that has been set by
   3.458 +     * the liveliness analyzer.
   3.459       */
   3.460      class FlowAnalyzer extends BaseAnalyzer<FlowAnalyzer.FlowPendingExit> {
   3.461  
   3.462          /** A flag that indicates whether the last statement could
   3.463           *  complete normally.
   3.464           */
   3.465 -        private boolean alive;
   3.466 -
   3.467          HashMap<Symbol, List<Type>> preciseRethrowTypes;
   3.468  
   3.469          /** The current class being defined.
   3.470 @@ -344,13 +717,11 @@
   3.471                  super(tree);
   3.472                  this.thrown = thrown;
   3.473              }
   3.474 -
   3.475 -            void resolveJump() { /*do nothing*/ }
   3.476          }
   3.477  
   3.478          @Override
   3.479          void markDead() {
   3.480 -            alive = false;
   3.481 +            //do nothing
   3.482          }
   3.483  
   3.484          /*-------------------- Exceptions ----------------------*/
   3.485 @@ -395,34 +766,6 @@
   3.486       * Visitor methods for statements and definitions
   3.487       *************************************************************************/
   3.488  
   3.489 -        /** Analyze a definition.
   3.490 -         */
   3.491 -        void scanDef(JCTree tree) {
   3.492 -            scanStat(tree);
   3.493 -            if (tree != null && tree.hasTag(JCTree.Tag.BLOCK) && !alive) {
   3.494 -                log.error(tree.pos(),
   3.495 -                          "initializer.must.be.able.to.complete.normally");
   3.496 -            }
   3.497 -        }
   3.498 -
   3.499 -        /** Analyze a statement. Check that statement is reachable.
   3.500 -         */
   3.501 -        void scanStat(JCTree tree) {
   3.502 -            if (!alive && tree != null) {
   3.503 -                log.error(tree.pos(), "unreachable.stmt");
   3.504 -                if (!tree.hasTag(SKIP)) alive = true;
   3.505 -            }
   3.506 -            scan(tree);
   3.507 -        }
   3.508 -
   3.509 -        /** Analyze list of statements.
   3.510 -         */
   3.511 -        void scanStats(List<? extends JCStatement> trees) {
   3.512 -            if (trees != null)
   3.513 -                for (List<? extends JCStatement> l = trees; l.nonEmpty(); l = l.tail)
   3.514 -                    scanStat(l.head);
   3.515 -        }
   3.516 -
   3.517          /* ------------ Visitor methods for various sorts of trees -------------*/
   3.518  
   3.519          public void visitClassDef(JCClassDecl tree) {
   3.520 @@ -431,7 +774,6 @@
   3.521              JCClassDecl classDefPrev = classDef;
   3.522              List<Type> thrownPrev = thrown;
   3.523              List<Type> caughtPrev = caught;
   3.524 -            boolean alivePrev = alive;
   3.525              ListBuffer<FlowPendingExit> pendingExitsPrev = pendingExits;
   3.526              Lint lintPrev = lint;
   3.527  
   3.528 @@ -448,7 +790,7 @@
   3.529                  for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
   3.530                      if (!l.head.hasTag(METHODDEF) &&
   3.531                          (TreeInfo.flags(l.head) & STATIC) != 0) {
   3.532 -                        scanDef(l.head);
   3.533 +                        scan(l.head);
   3.534                          errorUncaught();
   3.535                      }
   3.536                  }
   3.537 @@ -475,7 +817,7 @@
   3.538                  for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
   3.539                      if (!l.head.hasTag(METHODDEF) &&
   3.540                          (TreeInfo.flags(l.head) & STATIC) == 0) {
   3.541 -                        scanDef(l.head);
   3.542 +                        scan(l.head);
   3.543                          errorUncaught();
   3.544                      }
   3.545                  }
   3.546 @@ -508,7 +850,6 @@
   3.547                  thrown = thrownPrev;
   3.548              } finally {
   3.549                  pendingExits = pendingExitsPrev;
   3.550 -                alive = alivePrev;
   3.551                  caught = caughtPrev;
   3.552                  classDef = classDefPrev;
   3.553                  lint = lintPrev;
   3.554 @@ -538,11 +879,7 @@
   3.555                  // else we are in an instance initializer block;
   3.556                  // leave caught unchanged.
   3.557  
   3.558 -                alive = true;
   3.559 -                scanStat(tree.body);
   3.560 -
   3.561 -                if (alive && tree.sym.type.getReturnType().tag != VOID)
   3.562 -                    log.error(TreeInfo.diagEndPos(tree.body), "missing.ret.stmt");
   3.563 +                scan(tree.body);
   3.564  
   3.565                  List<FlowPendingExit> exits = pendingExits.toList();
   3.566                  pendingExits = new ListBuffer<FlowPendingExit>();
   3.567 @@ -575,45 +912,38 @@
   3.568          }
   3.569  
   3.570          public void visitBlock(JCBlock tree) {
   3.571 -            scanStats(tree.stats);
   3.572 +            scan(tree.stats);
   3.573          }
   3.574  
   3.575          public void visitDoLoop(JCDoWhileLoop tree) {
   3.576              ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
   3.577              pendingExits = new ListBuffer<FlowPendingExit>();
   3.578 -            scanStat(tree.body);
   3.579 -            alive |= resolveContinues(tree);
   3.580 +            scan(tree.body);
   3.581 +            resolveContinues(tree);
   3.582              scan(tree.cond);
   3.583 -            alive = alive && !tree.cond.type.isTrue();
   3.584 -            alive |= resolveBreaks(tree, prevPendingExits);
   3.585 +            resolveBreaks(tree, prevPendingExits);
   3.586          }
   3.587  
   3.588          public void visitWhileLoop(JCWhileLoop tree) {
   3.589              ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
   3.590              pendingExits = new ListBuffer<FlowPendingExit>();
   3.591              scan(tree.cond);
   3.592 -            alive = !tree.cond.type.isFalse();
   3.593 -            scanStat(tree.body);
   3.594 -            alive |= resolveContinues(tree);
   3.595 -            alive = resolveBreaks(tree, prevPendingExits) ||
   3.596 -                !tree.cond.type.isTrue();
   3.597 +            scan(tree.body);
   3.598 +            resolveContinues(tree);
   3.599 +            resolveBreaks(tree, prevPendingExits);
   3.600          }
   3.601  
   3.602          public void visitForLoop(JCForLoop tree) {
   3.603              ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
   3.604 -            scanStats(tree.init);
   3.605 +            scan(tree.init);
   3.606              pendingExits = new ListBuffer<FlowPendingExit>();
   3.607              if (tree.cond != null) {
   3.608                  scan(tree.cond);
   3.609 -                alive = !tree.cond.type.isFalse();
   3.610 -            } else {
   3.611 -                alive = true;
   3.612              }
   3.613 -            scanStat(tree.body);
   3.614 -            alive |= resolveContinues(tree);
   3.615 +            scan(tree.body);
   3.616 +            resolveContinues(tree);
   3.617              scan(tree.step);
   3.618 -            alive = resolveBreaks(tree, prevPendingExits) ||
   3.619 -                tree.cond != null && !tree.cond.type.isTrue();
   3.620 +            resolveBreaks(tree, prevPendingExits);
   3.621          }
   3.622  
   3.623          public void visitForeachLoop(JCEnhancedForLoop tree) {
   3.624 @@ -621,44 +951,30 @@
   3.625              ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
   3.626              scan(tree.expr);
   3.627              pendingExits = new ListBuffer<FlowPendingExit>();
   3.628 -            scanStat(tree.body);
   3.629 -            alive |= resolveContinues(tree);
   3.630 +            scan(tree.body);
   3.631 +            resolveContinues(tree);
   3.632              resolveBreaks(tree, prevPendingExits);
   3.633 -            alive = true;
   3.634          }
   3.635  
   3.636          public void visitLabelled(JCLabeledStatement tree) {
   3.637              ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
   3.638              pendingExits = new ListBuffer<FlowPendingExit>();
   3.639 -            scanStat(tree.body);
   3.640 -            alive |= resolveBreaks(tree, prevPendingExits);
   3.641 +            scan(tree.body);
   3.642 +            resolveBreaks(tree, prevPendingExits);
   3.643          }
   3.644  
   3.645          public void visitSwitch(JCSwitch tree) {
   3.646              ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
   3.647              pendingExits = new ListBuffer<FlowPendingExit>();
   3.648              scan(tree.selector);
   3.649 -            boolean hasDefault = false;
   3.650              for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
   3.651 -                alive = true;
   3.652                  JCCase c = l.head;
   3.653 -                if (c.pat == null)
   3.654 -                    hasDefault = true;
   3.655 -                else
   3.656 +                if (c.pat != null) {
   3.657                      scan(c.pat);
   3.658 -                scanStats(c.stats);
   3.659 -                // Warn about fall-through if lint switch fallthrough enabled.
   3.660 -                if (alive &&
   3.661 -                    lint.isEnabled(Lint.LintCategory.FALLTHROUGH) &&
   3.662 -                    c.stats.nonEmpty() && l.tail.nonEmpty())
   3.663 -                    log.warning(Lint.LintCategory.FALLTHROUGH,
   3.664 -                                l.tail.head.pos(),
   3.665 -                                "possible.fall-through.into.case");
   3.666 +                }
   3.667 +                scan(c.stats);
   3.668              }
   3.669 -            if (!hasDefault) {
   3.670 -                alive = true;
   3.671 -            }
   3.672 -            alive |= resolveBreaks(tree, prevPendingExits);
   3.673 +            resolveBreaks(tree, prevPendingExits);
   3.674          }
   3.675  
   3.676          public void visitTry(JCTry tree) {
   3.677 @@ -706,17 +1022,15 @@
   3.678                      }
   3.679                  }
   3.680              }
   3.681 -            scanStat(tree.body);
   3.682 +            scan(tree.body);
   3.683              List<Type> thrownInTry = allowImprovedCatchAnalysis ?
   3.684                  chk.union(thrown, List.of(syms.runtimeExceptionType, syms.errorType)) :
   3.685                  thrown;
   3.686              thrown = thrownPrev;
   3.687              caught = caughtPrev;
   3.688 -            boolean aliveEnd = alive;
   3.689  
   3.690              List<Type> caughtInTry = List.nil();
   3.691              for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
   3.692 -                alive = true;
   3.693                  JCVariableDecl param = l.head.param;
   3.694                  List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ?
   3.695                          ((JCTypeUnion)l.head.param.vartype).alternatives :
   3.696 @@ -735,26 +1049,18 @@
   3.697                  }
   3.698                  scan(param);
   3.699                  preciseRethrowTypes.put(param.sym, chk.intersect(ctypes, rethrownTypes));
   3.700 -                scanStat(l.head.body);
   3.701 +                scan(l.head.body);
   3.702                  preciseRethrowTypes.remove(param.sym);
   3.703 -                aliveEnd |= alive;
   3.704              }
   3.705              if (tree.finalizer != null) {
   3.706                  List<Type> savedThrown = thrown;
   3.707                  thrown = List.nil();
   3.708                  ListBuffer<FlowPendingExit> exits = pendingExits;
   3.709                  pendingExits = prevPendingExits;
   3.710 -                alive = true;
   3.711 -                scanStat(tree.finalizer);
   3.712 -                tree.finallyCanCompleteNormally = alive;
   3.713 -                if (!alive) {
   3.714 +                scan(tree.finalizer);
   3.715 +                if (!tree.finallyCanCompleteNormally) {
   3.716                      // discard exits and exceptions from try and finally
   3.717                      thrown = chk.union(thrown, thrownPrev);
   3.718 -                    if (lint.isEnabled(Lint.LintCategory.FINALLY)) {
   3.719 -                        log.warning(Lint.LintCategory.FINALLY,
   3.720 -                                TreeInfo.diagEndPos(tree.finalizer),
   3.721 -                                "finally.cannot.complete");
   3.722 -                    }
   3.723                  } else {
   3.724                      thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry));
   3.725                      thrown = chk.union(thrown, savedThrown);
   3.726 @@ -763,11 +1069,9 @@
   3.727                      while (exits.nonEmpty()) {
   3.728                          pendingExits.append(exits.next());
   3.729                      }
   3.730 -                    alive = aliveEnd;
   3.731                  }
   3.732              } else {
   3.733                  thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry));
   3.734 -                alive = aliveEnd;
   3.735                  ListBuffer<FlowPendingExit> exits = pendingExits;
   3.736                  pendingExits = prevPendingExits;
   3.737                  while (exits.nonEmpty()) pendingExits.append(exits.next());
   3.738 @@ -777,14 +1081,9 @@
   3.739          @Override
   3.740          public void visitIf(JCIf tree) {
   3.741              scan(tree.cond);
   3.742 -            scanStat(tree.thenpart);
   3.743 +            scan(tree.thenpart);
   3.744              if (tree.elsepart != null) {
   3.745 -                boolean aliveAfterThen = alive;
   3.746 -                alive = true;
   3.747 -                scanStat(tree.elsepart);
   3.748 -                alive = alive | aliveAfterThen;
   3.749 -            } else {
   3.750 -                alive = true;
   3.751 +                scan(tree.elsepart);
   3.752              }
   3.753          }
   3.754  
   3.755 @@ -826,7 +1125,6 @@
   3.756  
   3.757          public void visitReturn(JCReturn tree) {
   3.758              scan(tree.expr);
   3.759 -            // if not initial constructor, should markDead instead of recordExit
   3.760              recordExit(tree, new FlowPendingExit(tree, null));
   3.761          }
   3.762  
   3.763 @@ -898,13 +1196,14 @@
   3.764          /** Perform definite assignment/unassignment analysis on a tree.
   3.765           */
   3.766          public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
   3.767 +            analyzeTree(env, env.tree, make);
   3.768 +        }
   3.769 +        public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) {
   3.770              try {
   3.771                  attrEnv = env;
   3.772 -                JCTree tree = env.tree;
   3.773                  Flow.this.make = make;
   3.774                  pendingExits = new ListBuffer<FlowPendingExit>();
   3.775                  preciseRethrowTypes = new HashMap<Symbol, List<Type>>();
   3.776 -                alive = true;
   3.777                  this.thrown = this.caught = null;
   3.778                  this.classDef = null;
   3.779                  scan(tree);
   3.780 @@ -921,7 +1220,8 @@
   3.781       * This pass implements (i) definite assignment analysis, which ensures that
   3.782       * each variable is assigned when used and (ii) definite unassignment analysis,
   3.783       * which ensures that no final variable is assigned more than once. This visitor
   3.784 -     * depends on the results of the liveliness analyzer.
   3.785 +     * depends on the results of the liveliness analyzer. This pass is also used to mark
   3.786 +     * effectively-final local variables/parameters.
   3.787       */
   3.788      class AssignAnalyzer extends BaseAnalyzer<AssignAnalyzer.AssignPendingExit> {
   3.789  
   3.790 @@ -972,7 +1272,10 @@
   3.791          Scope unrefdResources;
   3.792  
   3.793          /** Set when processing a loop body the second time for DU analysis. */
   3.794 -        boolean loopPassTwo = false;
   3.795 +        FlowKind flowKind = FlowKind.NORMAL;
   3.796 +
   3.797 +        /** The starting position of the analysed tree */
   3.798 +        int startPos;
   3.799  
   3.800          class AssignPendingExit extends BaseAnalyzer.PendingExit {
   3.801  
   3.802 @@ -1004,9 +1307,10 @@
   3.803           */
   3.804          boolean trackable(VarSymbol sym) {
   3.805              return
   3.806 -                (sym.owner.kind == MTH ||
   3.807 +                sym.pos >= startPos &&
   3.808 +                ((sym.owner.kind == MTH ||
   3.809                   ((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL &&
   3.810 -                  classDef.sym.isEnclosedBy((ClassSymbol)sym.owner)));
   3.811 +                  classDef.sym.isEnclosedBy((ClassSymbol)sym.owner))));
   3.812          }
   3.813  
   3.814          /** Initialize new trackable variable by setting its address field
   3.815 @@ -1019,6 +1323,9 @@
   3.816                  System.arraycopy(vars, 0, newvars, 0, nextadr);
   3.817                  vars = newvars;
   3.818              }
   3.819 +            if ((sym.flags() & FINAL) == 0) {
   3.820 +                sym.flags_field |= EFFECTIVELY_FINAL;
   3.821 +            }
   3.822              sym.adr = nextadr;
   3.823              vars[nextadr] = sym;
   3.824              inits.excl(nextadr);
   3.825 @@ -1030,7 +1337,17 @@
   3.826           */
   3.827          void letInit(DiagnosticPosition pos, VarSymbol sym) {
   3.828              if (sym.adr >= firstadr && trackable(sym)) {
   3.829 -                if ((sym.flags() & FINAL) != 0) {
   3.830 +                if ((sym.flags() & EFFECTIVELY_FINAL) != 0) {
   3.831 +                    if (!uninits.isMember(sym.adr)) {
   3.832 +                        //assignment targeting an effectively final variable
   3.833 +                        //makes the variable lose its status of effectively final
   3.834 +                        //if the variable is _not_ definitively unassigned
   3.835 +                        sym.flags_field &= ~EFFECTIVELY_FINAL;
   3.836 +                    } else {
   3.837 +                        uninit(sym);
   3.838 +                    }
   3.839 +                }
   3.840 +                else if ((sym.flags() & FINAL) != 0) {
   3.841                      if ((sym.flags() & PARAMETER) != 0) {
   3.842                          if ((sym.flags() & UNION) != 0) { //multi-catch parameter
   3.843                              log.error(pos, "multicatch.parameter.may.not.be.assigned",
   3.844 @@ -1041,18 +1358,9 @@
   3.845                                    sym);
   3.846                          }
   3.847                      } else if (!uninits.isMember(sym.adr)) {
   3.848 -                        log.error(pos,
   3.849 -                                  loopPassTwo
   3.850 -                                  ? "var.might.be.assigned.in.loop"
   3.851 -                                  : "var.might.already.be.assigned",
   3.852 -                                  sym);
   3.853 -                    } else if (!inits.isMember(sym.adr)) {
   3.854 -                        // reachable assignment
   3.855 -                        uninits.excl(sym.adr);
   3.856 -                        uninitsTry.excl(sym.adr);
   3.857 +                        log.error(pos, flowKind.errKey, sym);
   3.858                      } else {
   3.859 -                        //log.rawWarning(pos, "unreachable assignment");//DEBUG
   3.860 -                        uninits.excl(sym.adr);
   3.861 +                        uninit(sym);
   3.862                      }
   3.863                  }
   3.864                  inits.incl(sym.adr);
   3.865 @@ -1060,6 +1368,17 @@
   3.866                  log.error(pos, "var.might.already.be.assigned", sym);
   3.867              }
   3.868          }
   3.869 +        //where
   3.870 +            void uninit(VarSymbol sym) {
   3.871 +                if (!inits.isMember(sym.adr)) {
   3.872 +                    // reachable assignment
   3.873 +                    uninits.excl(sym.adr);
   3.874 +                    uninitsTry.excl(sym.adr);
   3.875 +                } else {
   3.876 +                    //log.rawWarning(pos, "unreachable assignment");//DEBUG
   3.877 +                    uninits.excl(sym.adr);
   3.878 +                }
   3.879 +            }
   3.880  
   3.881          /** If tree is either a simple name or of the form this.name or
   3.882           *  C.this.name, and tree represents a trackable variable,
   3.883 @@ -1308,66 +1627,79 @@
   3.884  
   3.885          public void visitDoLoop(JCDoWhileLoop tree) {
   3.886              ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
   3.887 -            boolean prevLoopPassTwo = loopPassTwo;
   3.888 +            FlowKind prevFlowKind = flowKind;
   3.889 +            flowKind = FlowKind.NORMAL;
   3.890 +            Bits initsSkip = null;
   3.891 +            Bits uninitsSkip = null;
   3.892              pendingExits = new ListBuffer<AssignPendingExit>();
   3.893              int prevErrors = log.nerrors;
   3.894              do {
   3.895                  Bits uninitsEntry = uninits.dup();
   3.896                  uninitsEntry.excludeFrom(nextadr);
   3.897 -            scan(tree.body);
   3.898 -            resolveContinues(tree);
   3.899 +                scan(tree.body);
   3.900 +                resolveContinues(tree);
   3.901                  scanCond(tree.cond);
   3.902 +                if (!flowKind.isFinal()) {
   3.903 +                    initsSkip = initsWhenFalse;
   3.904 +                    uninitsSkip = uninitsWhenFalse;
   3.905 +                }
   3.906                  if (log.nerrors !=  prevErrors ||
   3.907 -                    loopPassTwo ||
   3.908 +                    flowKind.isFinal() ||
   3.909                      uninitsEntry.dup().diffSet(uninitsWhenTrue).nextBit(firstadr)==-1)
   3.910                      break;
   3.911                  inits = initsWhenTrue;
   3.912                  uninits = uninitsEntry.andSet(uninitsWhenTrue);
   3.913 -                loopPassTwo = true;
   3.914 +                flowKind = FlowKind.SPECULATIVE_LOOP;
   3.915              } while (true);
   3.916 -            loopPassTwo = prevLoopPassTwo;
   3.917 -            inits = initsWhenFalse;
   3.918 -            uninits = uninitsWhenFalse;
   3.919 +            flowKind = prevFlowKind;
   3.920 +            inits = initsSkip;
   3.921 +            uninits = uninitsSkip;
   3.922              resolveBreaks(tree, prevPendingExits);
   3.923          }
   3.924  
   3.925          public void visitWhileLoop(JCWhileLoop tree) {
   3.926              ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
   3.927 -            boolean prevLoopPassTwo = loopPassTwo;
   3.928 -            Bits initsCond;
   3.929 -            Bits uninitsCond;
   3.930 +            FlowKind prevFlowKind = flowKind;
   3.931 +            flowKind = FlowKind.NORMAL;
   3.932 +            Bits initsSkip = null;
   3.933 +            Bits uninitsSkip = null;
   3.934              pendingExits = new ListBuffer<AssignPendingExit>();
   3.935              int prevErrors = log.nerrors;
   3.936 +            Bits uninitsEntry = uninits.dup();
   3.937 +            uninitsEntry.excludeFrom(nextadr);
   3.938              do {
   3.939 -                Bits uninitsEntry = uninits.dup();
   3.940 -                uninitsEntry.excludeFrom(nextadr);
   3.941                  scanCond(tree.cond);
   3.942 -                initsCond = initsWhenFalse;
   3.943 -                uninitsCond = uninitsWhenFalse;
   3.944 +                if (!flowKind.isFinal()) {
   3.945 +                    initsSkip = initsWhenFalse;
   3.946 +                    uninitsSkip = uninitsWhenFalse;
   3.947 +                }
   3.948                  inits = initsWhenTrue;
   3.949                  uninits = uninitsWhenTrue;
   3.950                  scan(tree.body);
   3.951                  resolveContinues(tree);
   3.952                  if (log.nerrors != prevErrors ||
   3.953 -                    loopPassTwo ||
   3.954 +                    flowKind.isFinal() ||
   3.955                      uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1)
   3.956                      break;
   3.957                  uninits = uninitsEntry.andSet(uninits);
   3.958 -                loopPassTwo = true;
   3.959 +                flowKind = FlowKind.SPECULATIVE_LOOP;
   3.960              } while (true);
   3.961 -            loopPassTwo = prevLoopPassTwo;
   3.962 -            inits = initsCond;
   3.963 -            uninits = uninitsCond;
   3.964 +            flowKind = prevFlowKind;
   3.965 +            //a variable is DA/DU after the while statement, if it's DA/DU assuming the
   3.966 +            //branch is not taken AND if it's DA/DU before any break statement
   3.967 +            inits = initsSkip;
   3.968 +            uninits = uninitsSkip;
   3.969              resolveBreaks(tree, prevPendingExits);
   3.970          }
   3.971  
   3.972          public void visitForLoop(JCForLoop tree) {
   3.973              ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
   3.974 -            boolean prevLoopPassTwo = loopPassTwo;
   3.975 +            FlowKind prevFlowKind = flowKind;
   3.976 +            flowKind = FlowKind.NORMAL;
   3.977              int nextadrPrev = nextadr;
   3.978              scan(tree.init);
   3.979 -            Bits initsCond;
   3.980 -            Bits uninitsCond;
   3.981 +            Bits initsSkip = null;
   3.982 +            Bits uninitsSkip = null;
   3.983              pendingExits = new ListBuffer<AssignPendingExit>();
   3.984              int prevErrors = log.nerrors;
   3.985              do {
   3.986 @@ -1375,29 +1707,33 @@
   3.987                  uninitsEntry.excludeFrom(nextadr);
   3.988                  if (tree.cond != null) {
   3.989                      scanCond(tree.cond);
   3.990 -                    initsCond = initsWhenFalse;
   3.991 -                    uninitsCond = uninitsWhenFalse;
   3.992 +                    if (!flowKind.isFinal()) {
   3.993 +                        initsSkip = initsWhenFalse;
   3.994 +                        uninitsSkip = uninitsWhenFalse;
   3.995 +                    }
   3.996                      inits = initsWhenTrue;
   3.997                      uninits = uninitsWhenTrue;
   3.998 -                } else {
   3.999 -                    initsCond = inits.dup();
  3.1000 -                    initsCond.inclRange(firstadr, nextadr);
  3.1001 -                    uninitsCond = uninits.dup();
  3.1002 -                    uninitsCond.inclRange(firstadr, nextadr);
  3.1003 +                } else if (!flowKind.isFinal()) {
  3.1004 +                    initsSkip = inits.dup();
  3.1005 +                    initsSkip.inclRange(firstadr, nextadr);
  3.1006 +                    uninitsSkip = uninits.dup();
  3.1007 +                    uninitsSkip.inclRange(firstadr, nextadr);
  3.1008                  }
  3.1009                  scan(tree.body);
  3.1010                  resolveContinues(tree);
  3.1011                  scan(tree.step);
  3.1012                  if (log.nerrors != prevErrors ||
  3.1013 -                    loopPassTwo ||
  3.1014 +                    flowKind.isFinal() ||
  3.1015                      uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1)
  3.1016                      break;
  3.1017                  uninits = uninitsEntry.andSet(uninits);
  3.1018 -                loopPassTwo = true;
  3.1019 +                flowKind = FlowKind.SPECULATIVE_LOOP;
  3.1020              } while (true);
  3.1021 -            loopPassTwo = prevLoopPassTwo;
  3.1022 -            inits = initsCond;
  3.1023 -            uninits = uninitsCond;
  3.1024 +            flowKind = prevFlowKind;
  3.1025 +            //a variable is DA/DU after a for loop, if it's DA/DU assuming the
  3.1026 +            //branch is not taken AND if it's DA/DU before any break statement
  3.1027 +            inits = initsSkip;
  3.1028 +            uninits = uninitsSkip;
  3.1029              resolveBreaks(tree, prevPendingExits);
  3.1030              nextadr = nextadrPrev;
  3.1031          }
  3.1032 @@ -1406,7 +1742,8 @@
  3.1033              visitVarDef(tree.var);
  3.1034  
  3.1035              ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
  3.1036 -            boolean prevLoopPassTwo = loopPassTwo;
  3.1037 +            FlowKind prevFlowKind = flowKind;
  3.1038 +            flowKind = FlowKind.NORMAL;
  3.1039              int nextadrPrev = nextadr;
  3.1040              scan(tree.expr);
  3.1041              Bits initsStart = inits.dup();
  3.1042 @@ -1421,13 +1758,13 @@
  3.1043                  scan(tree.body);
  3.1044                  resolveContinues(tree);
  3.1045                  if (log.nerrors != prevErrors ||
  3.1046 -                    loopPassTwo ||
  3.1047 +                    flowKind.isFinal() ||
  3.1048                      uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1)
  3.1049                      break;
  3.1050                  uninits = uninitsEntry.andSet(uninits);
  3.1051 -                loopPassTwo = true;
  3.1052 +                flowKind = FlowKind.SPECULATIVE_LOOP;
  3.1053              } while (true);
  3.1054 -            loopPassTwo = prevLoopPassTwo;
  3.1055 +            flowKind = prevFlowKind;
  3.1056              inits = initsStart;
  3.1057              uninits = uninitsStart.andSet(uninits);
  3.1058              resolveBreaks(tree, prevPendingExits);
  3.1059 @@ -1628,7 +1965,6 @@
  3.1060  
  3.1061          public void visitReturn(JCReturn tree) {
  3.1062              scanExpr(tree.expr);
  3.1063 -            // if not initial constructor, should markDead instead of recordExit
  3.1064              recordExit(tree, new AssignPendingExit(tree, inits, uninits));
  3.1065          }
  3.1066  
  3.1067 @@ -1669,7 +2005,9 @@
  3.1068  
  3.1069          public void visitAssign(JCAssign tree) {
  3.1070              JCTree lhs = TreeInfo.skipParens(tree.lhs);
  3.1071 -            if (!(lhs instanceof JCIdent)) scanExpr(lhs);
  3.1072 +            if (!(lhs instanceof JCIdent)) {
  3.1073 +                scanExpr(lhs);
  3.1074 +            }
  3.1075              scanExpr(tree.rhs);
  3.1076              letInit(lhs);
  3.1077          }
  3.1078 @@ -1751,10 +2089,14 @@
  3.1079          /** Perform definite assignment/unassignment analysis on a tree.
  3.1080           */
  3.1081          public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
  3.1082 +            analyzeTree(env, env.tree, make);
  3.1083 +        }
  3.1084 +
  3.1085 +        public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) {
  3.1086              try {
  3.1087                  attrEnv = env;
  3.1088 -                JCTree tree = env.tree;
  3.1089                  Flow.this.make = make;
  3.1090 +                startPos = tree.pos().getStartPosition();
  3.1091                  inits = new Bits();
  3.1092                  uninits = new Bits();
  3.1093                  uninitsTry = new Bits();
  3.1094 @@ -1773,6 +2115,7 @@
  3.1095                  scan(tree);
  3.1096              } finally {
  3.1097                  // note that recursive invocations of this method fail hard
  3.1098 +                startPos = -1;
  3.1099                  inits = uninits = uninitsTry = null;
  3.1100                  initsWhenTrue = initsWhenFalse =
  3.1101                      uninitsWhenTrue = uninitsWhenFalse = null;
  3.1102 @@ -1787,4 +2130,162 @@
  3.1103              }
  3.1104          }
  3.1105      }
  3.1106 +
  3.1107 +    /**
  3.1108 +     * This pass implements the last step of the dataflow analysis, namely
  3.1109 +     * the effectively-final analysis check. This checks that every local variable
  3.1110 +     * reference from a lambda body/local inner class is either final or effectively final.
  3.1111 +     * As effectively final variables are marked as such during DA/DU, this pass must run after
  3.1112 +     * AssignAnalyzer.
  3.1113 +     */
  3.1114 +    class CaptureAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> {
  3.1115 +
  3.1116 +        JCTree currentTree; //local class or lambda
  3.1117 +
  3.1118 +        @Override
  3.1119 +        void markDead() {
  3.1120 +            //do nothing
  3.1121 +        }
  3.1122 +
  3.1123 +        @SuppressWarnings("fallthrough")
  3.1124 +        void checkEffectivelyFinal(DiagnosticPosition pos, VarSymbol sym) {
  3.1125 +            if (currentTree != null &&
  3.1126 +                    sym.owner.kind == MTH &&
  3.1127 +                    sym.pos < currentTree.getStartPosition()) {
  3.1128 +                switch (currentTree.getTag()) {
  3.1129 +                    case CLASSDEF:
  3.1130 +                        if (!allowEffectivelyFinalInInnerClasses) {
  3.1131 +                            if ((sym.flags() & FINAL) == 0) {
  3.1132 +                                reportInnerClsNeedsFinalError(pos, sym);
  3.1133 +                            }
  3.1134 +                            break;
  3.1135 +                        }
  3.1136 +                    case LAMBDA:
  3.1137 +                        if ((sym.flags() & (EFFECTIVELY_FINAL | FINAL)) == 0) {
  3.1138 +                           reportEffectivelyFinalError(pos, sym);
  3.1139 +                        }
  3.1140 +                }
  3.1141 +            }
  3.1142 +        }
  3.1143 +
  3.1144 +        @SuppressWarnings("fallthrough")
  3.1145 +        void letInit(JCTree tree) {
  3.1146 +            tree = TreeInfo.skipParens(tree);
  3.1147 +            if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) {
  3.1148 +                Symbol sym = TreeInfo.symbol(tree);
  3.1149 +                if (currentTree != null &&
  3.1150 +                        sym.kind == VAR &&
  3.1151 +                        sym.owner.kind == MTH &&
  3.1152 +                        ((VarSymbol)sym).pos < currentTree.getStartPosition()) {
  3.1153 +                    switch (currentTree.getTag()) {
  3.1154 +                        case CLASSDEF:
  3.1155 +                            if (!allowEffectivelyFinalInInnerClasses) {
  3.1156 +                                reportInnerClsNeedsFinalError(tree, sym);
  3.1157 +                                break;
  3.1158 +                            }
  3.1159 +                        case LAMBDA:
  3.1160 +                            reportEffectivelyFinalError(tree, sym);
  3.1161 +                    }
  3.1162 +                }
  3.1163 +            }
  3.1164 +        }
  3.1165 +
  3.1166 +        void reportEffectivelyFinalError(DiagnosticPosition pos, Symbol sym) {
  3.1167 +            String subKey = currentTree.hasTag(LAMBDA) ?
  3.1168 +                  "lambda"  : "inner.cls";
  3.1169 +            log.error(pos, "cant.ref.non.effectively.final.var", sym, diags.fragment(subKey));
  3.1170 +        }
  3.1171 +
  3.1172 +        void reportInnerClsNeedsFinalError(DiagnosticPosition pos, Symbol sym) {
  3.1173 +            log.error(pos,
  3.1174 +                    "local.var.accessed.from.icls.needs.final",
  3.1175 +                    sym);
  3.1176 +        }
  3.1177 +
  3.1178 +    /*************************************************************************
  3.1179 +     * Visitor methods for statements and definitions
  3.1180 +     *************************************************************************/
  3.1181 +
  3.1182 +        /* ------------ Visitor methods for various sorts of trees -------------*/
  3.1183 +
  3.1184 +        public void visitClassDef(JCClassDecl tree) {
  3.1185 +            JCTree prevTree = currentTree;
  3.1186 +            try {
  3.1187 +                currentTree = tree.sym.isLocal() ? tree : null;
  3.1188 +                super.visitClassDef(tree);
  3.1189 +            } finally {
  3.1190 +                currentTree = prevTree;
  3.1191 +            }
  3.1192 +        }
  3.1193 +
  3.1194 +        @Override
  3.1195 +        public void visitLambda(JCLambda tree) {
  3.1196 +            JCTree prevTree = currentTree;
  3.1197 +            try {
  3.1198 +                currentTree = tree;
  3.1199 +                super.visitLambda(tree);
  3.1200 +            } finally {
  3.1201 +                currentTree = prevTree;
  3.1202 +            }
  3.1203 +        }
  3.1204 +
  3.1205 +        @Override
  3.1206 +        public void visitIdent(JCIdent tree) {
  3.1207 +            if (tree.sym.kind == VAR) {
  3.1208 +                checkEffectivelyFinal(tree, (VarSymbol)tree.sym);
  3.1209 +            }
  3.1210 +        }
  3.1211 +
  3.1212 +        public void visitAssign(JCAssign tree) {
  3.1213 +            JCTree lhs = TreeInfo.skipParens(tree.lhs);
  3.1214 +            if (!(lhs instanceof JCIdent)) {
  3.1215 +                scan(lhs);
  3.1216 +            }
  3.1217 +            scan(tree.rhs);
  3.1218 +            letInit(lhs);
  3.1219 +        }
  3.1220 +
  3.1221 +        public void visitAssignop(JCAssignOp tree) {
  3.1222 +            scan(tree.lhs);
  3.1223 +            scan(tree.rhs);
  3.1224 +            letInit(tree.lhs);
  3.1225 +        }
  3.1226 +
  3.1227 +        public void visitUnary(JCUnary tree) {
  3.1228 +            switch (tree.getTag()) {
  3.1229 +                case PREINC: case POSTINC:
  3.1230 +                case PREDEC: case POSTDEC:
  3.1231 +                    scan(tree.arg);
  3.1232 +                    letInit(tree.arg);
  3.1233 +                    break;
  3.1234 +                default:
  3.1235 +                    scan(tree.arg);
  3.1236 +            }
  3.1237 +        }
  3.1238 +
  3.1239 +        public void visitTopLevel(JCCompilationUnit tree) {
  3.1240 +            // Do nothing for TopLevel since each class is visited individually
  3.1241 +        }
  3.1242 +
  3.1243 +    /**************************************************************************
  3.1244 +     * main method
  3.1245 +     *************************************************************************/
  3.1246 +
  3.1247 +        /** Perform definite assignment/unassignment analysis on a tree.
  3.1248 +         */
  3.1249 +        public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
  3.1250 +            analyzeTree(env, env.tree, make);
  3.1251 +        }
  3.1252 +        public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) {
  3.1253 +            try {
  3.1254 +                attrEnv = env;
  3.1255 +                Flow.this.make = make;
  3.1256 +                pendingExits = new ListBuffer<PendingExit>();
  3.1257 +                scan(tree);
  3.1258 +            } finally {
  3.1259 +                pendingExits = null;
  3.1260 +                Flow.this.make = null;
  3.1261 +            }
  3.1262 +        }
  3.1263 +    }
  3.1264  }
     4.1 --- a/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Thu Aug 02 18:22:41 2012 +0100
     4.2 +++ b/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Thu Aug 02 18:23:21 2012 +0100
     4.3 @@ -113,9 +113,9 @@
     4.4          this.allowMulticatch = source.allowMulticatch();
     4.5          this.allowStringFolding = fac.options.getBoolean("allowStringFolding", true);
     4.6          this.allowLambda = source.allowLambda() &&
     4.7 -                fac.options.isSet("allowLambda");
     4.8 +                fac.options.isSet("allowLambda"); //pre-lambda guard
     4.9          this.allowMethodReferences = source.allowMethodReferences() &&
    4.10 -                fac.options.isSet("allowMethodReferences");
    4.11 +                fac.options.isSet("allowMethodReferences"); //pre-lambda guard
    4.12          this.keepDocComments = keepDocComments;
    4.13          docComments = newDocCommentTable(keepDocComments);
    4.14          this.keepLineMap = keepLineMap;
     5.1 --- a/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Thu Aug 02 18:22:41 2012 +0100
     5.2 +++ b/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Thu Aug 02 18:23:21 2012 +0100
     5.3 @@ -168,6 +168,14 @@
     5.4  compiler.err.cant.assign.val.to.final.var=\
     5.5      cannot assign a value to final variable {0}
     5.6  
     5.7 +# 0: symbol, 1: message segment
     5.8 +compiler.err.cant.ref.non.effectively.final.var=\
     5.9 +    local variables referenced from {1} must be final or effectively final
    5.10 +
    5.11 +
    5.12 +compiler.misc.inner.cls=\
    5.13 +    an inner class
    5.14 +
    5.15  # 0: type
    5.16  compiler.err.cant.deref=\
    5.17      {0} cannot be dereferenced
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/test/tools/javac/diags/examples/CantRefNonEffectivelyFinalVar.java	Thu Aug 02 18:23:21 2012 +0100
     6.3 @@ -0,0 +1,34 @@
     6.4 +/*
     6.5 + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
     6.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     6.7 + *
     6.8 + * This code is free software; you can redistribute it and/or modify it
     6.9 + * under the terms of the GNU General Public License version 2 only, as
    6.10 + * published by the Free Software Foundation.
    6.11 + *
    6.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
    6.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    6.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    6.15 + * version 2 for more details (a copy is included in the LICENSE file that
    6.16 + * accompanied this code).
    6.17 + *
    6.18 + * You should have received a copy of the GNU General Public License version
    6.19 + * 2 along with this work; if not, write to the Free Software Foundation,
    6.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    6.21 + *
    6.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    6.23 + * or visit www.oracle.com if you need additional information or have any
    6.24 + * questions.
    6.25 + */
    6.26 +
    6.27 +// key: compiler.err.cant.ref.non.effectively.final.var
    6.28 +// key: compiler.misc.inner.cls
    6.29 +// options: -XDallowEffectivelyFinalInInnerClasses
    6.30 +
    6.31 +class CantRefNonEffectivelyFinalVar {
    6.32 +    void test() {
    6.33 +        int i = 0;
    6.34 +        new Object() { int j = i; };
    6.35 +        i = 2;
    6.36 +    }
    6.37 +}
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/test/tools/javac/lambda/EffectivelyFinalTest.java	Thu Aug 02 18:23:21 2012 +0100
     7.3 @@ -0,0 +1,76 @@
     7.4 +/*
     7.5 + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
     7.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     7.7 + *
     7.8 + * This code is free software; you can redistribute it and/or modify it
     7.9 + * under the terms of the GNU General Public License version 2 only, as
    7.10 + * published by the Free Software Foundation.
    7.11 + *
    7.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
    7.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    7.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    7.15 + * version 2 for more details (a copy is included in the LICENSE file that
    7.16 + * accompanied this code).
    7.17 + *
    7.18 + * You should have received a copy of the GNU General Public License version
    7.19 + * 2 along with this work; if not, write to the Free Software Foundation,
    7.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    7.21 + *
    7.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    7.23 + * or visit www.oracle.com if you need additional information or have any
    7.24 + * questions.
    7.25 + */
    7.26 +
    7.27 +/*
    7.28 + * @test
    7.29 + * @summary Integrate efectively final check with DA/DU analysis
    7.30 + * @compile/fail/ref=EffectivelyFinalTest01.out -XDallowEffectivelyFinalInInnerClasses -XDrawDiagnostics EffectivelyFinalTest.java
    7.31 + * @compile/fail/ref=EffectivelyFinalTest02.out -source 7 -Xlint:-options -XDrawDiagnostics EffectivelyFinalTest.java
    7.32 + */
    7.33 +class EffectivelyFinalTest {
    7.34 +
    7.35 +    void m1(int x) {
    7.36 +        int y = 1;
    7.37 +        new Object() { { System.out.println(x+y); } }; //ok - both x and y are EF
    7.38 +    }
    7.39 +
    7.40 +    void m2(int x) {
    7.41 +        int y;
    7.42 +        y = 1;
    7.43 +        new Object() { { System.out.println(x+y); } }; //ok - both x and y are EF
    7.44 +    }
    7.45 +
    7.46 +    void m3(int x, boolean cond) {
    7.47 +        int y;
    7.48 +        if (cond) y = 1;
    7.49 +        new Object() { { System.out.println(x+y); } }; //error - y not DA
    7.50 +    }
    7.51 +
    7.52 +    void m4(int x, boolean cond) {
    7.53 +        int y;
    7.54 +        if (cond) y = 1;
    7.55 +        else y = 2;
    7.56 +        new Object() { { System.out.println(x+y); } }; //ok - both x and y are EF
    7.57 +    }
    7.58 +
    7.59 +    void m5(int x, boolean cond) {
    7.60 +        int y;
    7.61 +        if (cond) y = 1;
    7.62 +        y = 2;
    7.63 +        new Object() { { System.out.println(x+y); } }; //error - y not EF
    7.64 +    }
    7.65 +
    7.66 +    void m6(int x) {
    7.67 +        new Object() { { System.out.println(x+1); } }; //error - x not EF
    7.68 +        x++;
    7.69 +    }
    7.70 +
    7.71 +    void m7(int x) {
    7.72 +        new Object() { { System.out.println(x=1); } }; //error - x not EF
    7.73 +    }
    7.74 +
    7.75 +    void m8() {
    7.76 +        int y;
    7.77 +        new Object() { { System.out.println(y=1); } }; //error - y not EF
    7.78 +    }
    7.79 +}
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/test/tools/javac/lambda/EffectivelyFinalTest01.out	Thu Aug 02 18:23:21 2012 +0100
     8.3 @@ -0,0 +1,6 @@
     8.4 +EffectivelyFinalTest.java:46:47: compiler.err.var.might.not.have.been.initialized: y
     8.5 +EffectivelyFinalTest.java:60:47: compiler.err.cant.ref.non.effectively.final.var: y, (compiler.misc.inner.cls)
     8.6 +EffectivelyFinalTest.java:64:45: compiler.err.cant.ref.non.effectively.final.var: x, (compiler.misc.inner.cls)
     8.7 +EffectivelyFinalTest.java:69:45: compiler.err.cant.ref.non.effectively.final.var: x, (compiler.misc.inner.cls)
     8.8 +EffectivelyFinalTest.java:74:45: compiler.err.cant.ref.non.effectively.final.var: y, (compiler.misc.inner.cls)
     8.9 +5 errors
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/test/tools/javac/lambda/EffectivelyFinalTest02.out	Thu Aug 02 18:23:21 2012 +0100
     9.3 @@ -0,0 +1,14 @@
     9.4 +EffectivelyFinalTest.java:46:47: compiler.err.var.might.not.have.been.initialized: y
     9.5 +EffectivelyFinalTest.java:34:45: compiler.err.local.var.accessed.from.icls.needs.final: x
     9.6 +EffectivelyFinalTest.java:34:47: compiler.err.local.var.accessed.from.icls.needs.final: y
     9.7 +EffectivelyFinalTest.java:40:45: compiler.err.local.var.accessed.from.icls.needs.final: x
     9.8 +EffectivelyFinalTest.java:40:47: compiler.err.local.var.accessed.from.icls.needs.final: y
     9.9 +EffectivelyFinalTest.java:46:45: compiler.err.local.var.accessed.from.icls.needs.final: x
    9.10 +EffectivelyFinalTest.java:53:45: compiler.err.local.var.accessed.from.icls.needs.final: x
    9.11 +EffectivelyFinalTest.java:53:47: compiler.err.local.var.accessed.from.icls.needs.final: y
    9.12 +EffectivelyFinalTest.java:60:45: compiler.err.local.var.accessed.from.icls.needs.final: x
    9.13 +EffectivelyFinalTest.java:60:47: compiler.err.local.var.accessed.from.icls.needs.final: y
    9.14 +EffectivelyFinalTest.java:64:45: compiler.err.local.var.accessed.from.icls.needs.final: x
    9.15 +EffectivelyFinalTest.java:69:45: compiler.err.local.var.accessed.from.icls.needs.final: x
    9.16 +EffectivelyFinalTest.java:74:45: compiler.err.local.var.accessed.from.icls.needs.final: y
    9.17 +13 errors

mercurial