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 /**