src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java

changeset 2370
acd64168cf8b
parent 2193
d4cbb671de1c
child 2382
14979dd5e034
     1.1 --- a/src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java	Fri Apr 18 23:58:05 2014 +0100
     1.2 +++ b/src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java	Tue Apr 22 17:55:22 2014 +0100
     1.3 @@ -1,5 +1,5 @@
     1.4  /*
     1.5 - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
     1.6 + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
     1.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     1.8   *
     1.9   * This code is free software; you can redistribute it and/or modify it
    1.10 @@ -25,7 +25,7 @@
    1.11  
    1.12  package com.sun.tools.javac.comp;
    1.13  
    1.14 -import com.sun.source.tree.MemberReferenceTree;
    1.15 +import com.sun.source.tree.LambdaExpressionTree.BodyKind;
    1.16  import com.sun.tools.javac.code.*;
    1.17  import com.sun.tools.javac.tree.*;
    1.18  import com.sun.tools.javac.util.*;
    1.19 @@ -35,10 +35,8 @@
    1.20  import com.sun.tools.javac.comp.Attr.ResultInfo;
    1.21  import com.sun.tools.javac.comp.Infer.InferenceContext;
    1.22  import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase;
    1.23 -import com.sun.tools.javac.comp.Resolve.ReferenceLookupHelper;
    1.24  import com.sun.tools.javac.tree.JCTree.*;
    1.25  
    1.26 -
    1.27  import java.util.ArrayList;
    1.28  import java.util.Collections;
    1.29  import java.util.EnumSet;
    1.30 @@ -48,6 +46,7 @@
    1.31  import java.util.Set;
    1.32  import java.util.WeakHashMap;
    1.33  
    1.34 +import static com.sun.tools.javac.code.Kinds.VAL;
    1.35  import static com.sun.tools.javac.code.TypeTag.*;
    1.36  import static com.sun.tools.javac.tree.JCTree.Tag.*;
    1.37  
    1.38 @@ -76,6 +75,8 @@
    1.39      final Symtab syms;
    1.40      final TreeMaker make;
    1.41      final Types types;
    1.42 +    final Flow flow;
    1.43 +    final Names names;
    1.44  
    1.45      public static DeferredAttr instance(Context context) {
    1.46          DeferredAttr instance = context.get(deferredAttrKey);
    1.47 @@ -96,7 +97,8 @@
    1.48          syms = Symtab.instance(context);
    1.49          make = TreeMaker.instance(context);
    1.50          types = Types.instance(context);
    1.51 -        Names names = Names.instance(context);
    1.52 +        flow = Flow.instance(context);
    1.53 +        names = Names.instance(context);
    1.54          stuckTree = make.Ident(names.empty).setType(Type.stuckType);
    1.55          emptyDeferredAttrContext =
    1.56              new DeferredAttrContext(AttrMode.CHECK, null, MethodResolutionPhase.BOX, infer.emptyContext, null, null) {
    1.57 @@ -139,6 +141,11 @@
    1.58              return DEFERRED;
    1.59          }
    1.60  
    1.61 +        @Override
    1.62 +        public String toString() {
    1.63 +            return "DeferredType";
    1.64 +        }
    1.65 +
    1.66          /**
    1.67           * A speculative cache is used to keep track of all overload resolution rounds
    1.68           * that triggered speculative attribution on a given deferred type. Each entry
    1.69 @@ -378,7 +385,9 @@
    1.70          }
    1.71      }
    1.72      //where
    1.73 -        protected TreeScanner unenterScanner = new TreeScanner() {
    1.74 +        protected UnenterScanner unenterScanner = new UnenterScanner();
    1.75 +
    1.76 +        class UnenterScanner extends TreeScanner {
    1.77              @Override
    1.78              public void visitClassDef(JCClassDecl tree) {
    1.79                  ClassSymbol csym = tree.sym;
    1.80 @@ -391,7 +400,7 @@
    1.81                  syms.classes.remove(csym.flatname);
    1.82                  super.visitClassDef(tree);
    1.83              }
    1.84 -        };
    1.85 +        }
    1.86  
    1.87      /**
    1.88       * A deferred context is created on each method check. A deferred context is
    1.89 @@ -595,19 +604,111 @@
    1.90              public void visitLambda(JCLambda tree) {
    1.91                  Check.CheckContext checkContext = resultInfo.checkContext;
    1.92                  Type pt = resultInfo.pt;
    1.93 -                if (inferenceContext.inferencevars.contains(pt)) {
    1.94 -                    //ok
    1.95 -                    return;
    1.96 -                } else {
    1.97 +                if (!inferenceContext.inferencevars.contains(pt)) {
    1.98                      //must be a functional descriptor
    1.99 +                    Type descriptorType = null;
   1.100                      try {
   1.101 -                        Type desc = types.findDescriptorType(pt);
   1.102 -                        if (desc.getParameterTypes().length() != tree.params.length()) {
   1.103 -                            checkContext.report(tree, diags.fragment("incompatible.arg.types.in.lambda"));
   1.104 -                        }
   1.105 +                        descriptorType = types.findDescriptorType(pt);
   1.106                      } catch (Types.FunctionDescriptorLookupError ex) {
   1.107                          checkContext.report(null, ex.getDiagnostic());
   1.108                      }
   1.109 +
   1.110 +                    if (descriptorType.getParameterTypes().length() != tree.params.length()) {
   1.111 +                        checkContext.report(tree,
   1.112 +                                diags.fragment("incompatible.arg.types.in.lambda"));
   1.113 +                    }
   1.114 +
   1.115 +                    Type currentReturnType = descriptorType.getReturnType();
   1.116 +                    boolean returnTypeIsVoid = currentReturnType.hasTag(VOID);
   1.117 +                    if (tree.getBodyKind() == BodyKind.EXPRESSION) {
   1.118 +                        boolean isExpressionCompatible = !returnTypeIsVoid ||
   1.119 +                            TreeInfo.isExpressionStatement((JCExpression)tree.getBody());
   1.120 +                        if (!isExpressionCompatible) {
   1.121 +                            resultInfo.checkContext.report(tree.pos(),
   1.122 +                                diags.fragment("incompatible.ret.type.in.lambda",
   1.123 +                                    diags.fragment("missing.ret.val", currentReturnType)));
   1.124 +                        }
   1.125 +                    } else {
   1.126 +                        LambdaBodyStructChecker lambdaBodyChecker =
   1.127 +                                new LambdaBodyStructChecker();
   1.128 +
   1.129 +                        tree.body.accept(lambdaBodyChecker);
   1.130 +                        boolean isVoidCompatible = lambdaBodyChecker.isVoidCompatible;
   1.131 +
   1.132 +                        if (returnTypeIsVoid) {
   1.133 +                            if (!isVoidCompatible) {
   1.134 +                                resultInfo.checkContext.report(tree.pos(),
   1.135 +                                    diags.fragment("unexpected.ret.val"));
   1.136 +                            }
   1.137 +                        } else {
   1.138 +                            boolean isValueCompatible = lambdaBodyChecker.isPotentiallyValueCompatible
   1.139 +                                && !canLambdaBodyCompleteNormally(tree);
   1.140 +                            if (!isValueCompatible && !isVoidCompatible) {
   1.141 +                                log.error(tree.body.pos(),
   1.142 +                                    "lambda.body.neither.value.nor.void.compatible");
   1.143 +                            }
   1.144 +
   1.145 +                            if (!isValueCompatible) {
   1.146 +                                resultInfo.checkContext.report(tree.pos(),
   1.147 +                                    diags.fragment("incompatible.ret.type.in.lambda",
   1.148 +                                        diags.fragment("missing.ret.val", currentReturnType)));
   1.149 +                            }
   1.150 +                        }
   1.151 +                    }
   1.152 +                }
   1.153 +            }
   1.154 +
   1.155 +            boolean canLambdaBodyCompleteNormally(JCLambda tree) {
   1.156 +                JCLambda newTree = new TreeCopier<>(make).copy(tree);
   1.157 +                /* attr.lambdaEnv will create a meaningful env for the
   1.158 +                 * lambda expression. This is specially useful when the
   1.159 +                 * lambda is used as the init of a field. But we need to
   1.160 +                 * remove any added symbol.
   1.161 +                 */
   1.162 +                Env<AttrContext> localEnv = attr.lambdaEnv(newTree, env);
   1.163 +                try {
   1.164 +                    List<JCVariableDecl> tmpParams = newTree.params;
   1.165 +                    while (tmpParams.nonEmpty()) {
   1.166 +                        tmpParams.head.vartype = make.at(tmpParams.head).Type(syms.errType);
   1.167 +                        tmpParams = tmpParams.tail;
   1.168 +                    }
   1.169 +
   1.170 +                    attr.attribStats(newTree.params, localEnv);
   1.171 +
   1.172 +                    /* set pt to Type.noType to avoid generating any bound
   1.173 +                     * which may happen if lambda's return type is an
   1.174 +                     * inference variable
   1.175 +                     */
   1.176 +                    Attr.ResultInfo bodyResultInfo = attr.new ResultInfo(VAL, Type.noType);
   1.177 +                    localEnv.info.returnResult = bodyResultInfo;
   1.178 +
   1.179 +                    // discard any log output
   1.180 +                    Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
   1.181 +                    try {
   1.182 +                        JCBlock body = (JCBlock)newTree.body;
   1.183 +                        /* we need to attribute the lambda body before
   1.184 +                         * doing the aliveness analysis. This is because
   1.185 +                         * constant folding occurs during attribution
   1.186 +                         * and the reachability of some statements depends
   1.187 +                         * on constant values, for example:
   1.188 +                         *
   1.189 +                         *     while (true) {...}
   1.190 +                         */
   1.191 +                        attr.attribStats(body.stats, localEnv);
   1.192 +
   1.193 +                        attr.preFlow(newTree);
   1.194 +                        /* make an aliveness / reachability analysis of the lambda
   1.195 +                         * to determine if it can complete normally
   1.196 +                         */
   1.197 +                        flow.analyzeLambda(localEnv, newTree, make, true);
   1.198 +                    } finally {
   1.199 +                        log.popDiagnosticHandler(diagHandler);
   1.200 +                    }
   1.201 +                    return newTree.canCompleteNormally;
   1.202 +                } finally {
   1.203 +                    JCBlock body = (JCBlock)newTree.body;
   1.204 +                    unenterScanner.scan(body.stats);
   1.205 +                    localEnv.info.scope.leave();
   1.206                  }
   1.207              }
   1.208  
   1.209 @@ -625,10 +726,7 @@
   1.210              public void visitReference(JCMemberReference tree) {
   1.211                  Check.CheckContext checkContext = resultInfo.checkContext;
   1.212                  Type pt = resultInfo.pt;
   1.213 -                if (inferenceContext.inferencevars.contains(pt)) {
   1.214 -                    //ok
   1.215 -                    return;
   1.216 -                } else {
   1.217 +                if (!inferenceContext.inferencevars.contains(pt)) {
   1.218                      try {
   1.219                          types.findDescriptorType(pt);
   1.220                      } catch (Types.FunctionDescriptorLookupError ex) {
   1.221 @@ -658,6 +756,40 @@
   1.222                  }
   1.223              }
   1.224          }
   1.225 +
   1.226 +        /* This visitor looks for return statements, its analysis will determine if
   1.227 +         * a lambda body is void or value compatible. We must analyze return
   1.228 +         * statements contained in the lambda body only, thus any return statement
   1.229 +         * contained in an inner class or inner lambda body, should be ignored.
   1.230 +         */
   1.231 +        class LambdaBodyStructChecker extends TreeScanner {
   1.232 +            boolean isVoidCompatible = true;
   1.233 +            boolean isPotentiallyValueCompatible = true;
   1.234 +
   1.235 +            @Override
   1.236 +            public void visitClassDef(JCClassDecl tree) {
   1.237 +                // do nothing
   1.238 +            }
   1.239 +
   1.240 +            @Override
   1.241 +            public void visitLambda(JCLambda tree) {
   1.242 +                // do nothing
   1.243 +            }
   1.244 +
   1.245 +            @Override
   1.246 +            public void visitNewClass(JCNewClass tree) {
   1.247 +                // do nothing
   1.248 +            }
   1.249 +
   1.250 +            @Override
   1.251 +            public void visitReturn(JCReturn tree) {
   1.252 +                if (tree.expr != null) {
   1.253 +                    isVoidCompatible = false;
   1.254 +                } else {
   1.255 +                    isPotentiallyValueCompatible = false;
   1.256 +                }
   1.257 +            }
   1.258 +        }
   1.259      }
   1.260  
   1.261      /** an empty deferred attribution context - all methods throw exceptions */
   1.262 @@ -769,7 +901,7 @@
   1.263          /**
   1.264           * handler that is executed when a node has been discarded
   1.265           */
   1.266 -        abstract void skip(JCTree tree);
   1.267 +        void skip(JCTree tree) {}
   1.268      }
   1.269  
   1.270      /**
   1.271 @@ -781,11 +913,6 @@
   1.272          PolyScanner() {
   1.273              super(EnumSet.of(CONDEXPR, PARENS, LAMBDA, REFERENCE));
   1.274          }
   1.275 -
   1.276 -        @Override
   1.277 -        void skip(JCTree tree) {
   1.278 -            //do nothing
   1.279 -        }
   1.280      }
   1.281  
   1.282      /**
   1.283 @@ -798,11 +925,6 @@
   1.284              super(EnumSet.of(BLOCK, CASE, CATCH, DOLOOP, FOREACHLOOP,
   1.285                      FORLOOP, RETURN, SYNCHRONIZED, SWITCH, TRY, WHILELOOP));
   1.286          }
   1.287 -
   1.288 -        @Override
   1.289 -        void skip(JCTree tree) {
   1.290 -            //do nothing
   1.291 -        }
   1.292      }
   1.293  
   1.294      /**

mercurial