Tue, 08 Jan 2013 10:17:29 +0100
8005184: Restructure DeferredAttr to allow pluggable deferred type completers
Summary: Add hooks to generalize deferred type completion via custom helper objects
Reviewed-by: jjg
src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java | file | annotate | diff | comparison | revisions |
1.1 --- a/src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java Tue Jan 08 10:16:26 2013 +0100 1.2 +++ b/src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java Tue Jan 08 10:17:29 2013 +0100 1.3 @@ -38,6 +38,7 @@ 1.4 import javax.tools.JavaFileObject; 1.5 1.6 import java.util.ArrayList; 1.7 +import java.util.EnumSet; 1.8 import java.util.LinkedHashSet; 1.9 import java.util.Map; 1.10 import java.util.Queue; 1.11 @@ -177,29 +178,19 @@ 1.12 * attribution round must follow one or more speculative rounds. 1.13 */ 1.14 Type check(ResultInfo resultInfo) { 1.15 + return check(resultInfo, stuckVars(tree, env, resultInfo), basicCompleter); 1.16 + } 1.17 + 1.18 + Type check(ResultInfo resultInfo, List<Type> stuckVars, DeferredTypeCompleter deferredTypeCompleter) { 1.19 DeferredAttrContext deferredAttrContext = 1.20 resultInfo.checkContext.deferredAttrContext(); 1.21 Assert.check(deferredAttrContext != emptyDeferredAttrContext); 1.22 - List<Type> stuckVars = stuckVars(tree, env, resultInfo); 1.23 if (stuckVars.nonEmpty()) { 1.24 deferredAttrContext.addDeferredAttrNode(this, resultInfo, stuckVars); 1.25 return Type.noType; 1.26 } else { 1.27 try { 1.28 - switch (deferredAttrContext.mode) { 1.29 - case SPECULATIVE: 1.30 - Assert.check(mode == null || 1.31 - (mode == AttrMode.SPECULATIVE && 1.32 - speculativeType(deferredAttrContext.msym, deferredAttrContext.phase).hasTag(NONE))); 1.33 - JCTree speculativeTree = attribSpeculative(tree, env, resultInfo); 1.34 - speculativeCache.put(deferredAttrContext.msym, speculativeTree, deferredAttrContext.phase); 1.35 - return speculativeTree.type; 1.36 - case CHECK: 1.37 - Assert.check(mode == AttrMode.SPECULATIVE); 1.38 - return attr.attribTree(tree, env, resultInfo); 1.39 - } 1.40 - Assert.error(); 1.41 - return null; 1.42 + return deferredTypeCompleter.complete(this, resultInfo, deferredAttrContext); 1.43 } finally { 1.44 mode = deferredAttrContext.mode; 1.45 } 1.46 @@ -208,6 +199,43 @@ 1.47 } 1.48 1.49 /** 1.50 + * A completer for deferred types. Defines an entry point for type-checking 1.51 + * a deferred type. 1.52 + */ 1.53 + interface DeferredTypeCompleter { 1.54 + /** 1.55 + * Entry point for type-checking a deferred type. Depending on the 1.56 + * circumstances, type-checking could amount to full attribution 1.57 + * or partial structural check (aka potential applicability). 1.58 + */ 1.59 + Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext); 1.60 + } 1.61 + 1.62 + /** 1.63 + * A basic completer for deferred types. This completer type-checks a deferred type 1.64 + * using attribution; depending on the attribution mode, this could be either standard 1.65 + * or speculative attribution. 1.66 + */ 1.67 + DeferredTypeCompleter basicCompleter = new DeferredTypeCompleter() { 1.68 + public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) { 1.69 + switch (deferredAttrContext.mode) { 1.70 + case SPECULATIVE: 1.71 + Assert.check(dt.mode == null || 1.72 + (dt.mode == AttrMode.SPECULATIVE && 1.73 + dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase).hasTag(NONE))); 1.74 + JCTree speculativeTree = attribSpeculative(dt.tree, dt.env, resultInfo); 1.75 + dt.speculativeCache.put(deferredAttrContext.msym, speculativeTree, deferredAttrContext.phase); 1.76 + return speculativeTree.type; 1.77 + case CHECK: 1.78 + Assert.check(dt.mode == AttrMode.SPECULATIVE); 1.79 + return attr.attribTree(dt.tree, dt.env, resultInfo); 1.80 + } 1.81 + Assert.error(); 1.82 + return null; 1.83 + } 1.84 + }; 1.85 + 1.86 + /** 1.87 * The 'mode' in which the deferred type is to be type-checked 1.88 */ 1.89 public enum AttrMode { 1.90 @@ -498,10 +526,80 @@ 1.91 if (resultInfo.pt.hasTag(NONE) || resultInfo.pt.isErroneous()) { 1.92 return List.nil(); 1.93 } else { 1.94 - StuckChecker sc = new StuckChecker(resultInfo, env); 1.95 + return stuckVarsInternal(tree, resultInfo.pt, resultInfo.checkContext.inferenceContext()); 1.96 + } 1.97 + } 1.98 + //where 1.99 + private List<Type> stuckVarsInternal(JCTree tree, Type pt, Infer.InferenceContext inferenceContext) { 1.100 + StuckChecker sc = new StuckChecker(pt, inferenceContext); 1.101 sc.scan(tree); 1.102 return List.from(sc.stuckVars); 1.103 } 1.104 + 1.105 + /** 1.106 + * A special tree scanner that would only visit portions of a given tree. 1.107 + * The set of nodes visited by the scanner can be customized at construction-time. 1.108 + */ 1.109 + abstract static class FilterScanner extends TreeScanner { 1.110 + 1.111 + final Filter<JCTree> treeFilter; 1.112 + 1.113 + FilterScanner(final Set<JCTree.Tag> validTags) { 1.114 + this.treeFilter = new Filter<JCTree>() { 1.115 + public boolean accepts(JCTree t) { 1.116 + return validTags.contains(t.getTag()); 1.117 + } 1.118 + }; 1.119 + } 1.120 + 1.121 + @Override 1.122 + public void scan(JCTree tree) { 1.123 + if (tree != null) { 1.124 + if (treeFilter.accepts(tree)) { 1.125 + super.scan(tree); 1.126 + } else { 1.127 + skip(tree); 1.128 + } 1.129 + } 1.130 + } 1.131 + 1.132 + /** 1.133 + * handler that is executed when a node has been discarded 1.134 + */ 1.135 + abstract void skip(JCTree tree); 1.136 + } 1.137 + 1.138 + /** 1.139 + * A tree scanner suitable for visiting the target-type dependent nodes of 1.140 + * a given argument expression. 1.141 + */ 1.142 + static class PolyScanner extends FilterScanner { 1.143 + 1.144 + PolyScanner() { 1.145 + super(EnumSet.of(CONDEXPR, PARENS, LAMBDA, REFERENCE)); 1.146 + } 1.147 + 1.148 + @Override 1.149 + void skip(JCTree tree) { 1.150 + //do nothing 1.151 + } 1.152 + } 1.153 + 1.154 + /** 1.155 + * A tree scanner suitable for visiting the target-type dependent nodes nested 1.156 + * within a lambda expression body. 1.157 + */ 1.158 + static class LambdaReturnScanner extends FilterScanner { 1.159 + 1.160 + LambdaReturnScanner() { 1.161 + super(EnumSet.of(BLOCK, CASE, CATCH, DOLOOP, FOREACHLOOP, 1.162 + FORLOOP, RETURN, SYNCHRONIZED, SWITCH, TRY, WHILELOOP)); 1.163 + } 1.164 + 1.165 + @Override 1.166 + void skip(JCTree tree) { 1.167 + //do nothing 1.168 + } 1.169 } 1.170 1.171 /** 1.172 @@ -510,83 +608,32 @@ 1.173 * inferring types that make some of the nested expressions incompatible 1.174 * with their corresponding instantiated target 1.175 */ 1.176 - class StuckChecker extends TreeScanner { 1.177 + class StuckChecker extends PolyScanner { 1.178 1.179 Type pt; 1.180 - Filter<JCTree> treeFilter; 1.181 Infer.InferenceContext inferenceContext; 1.182 Set<Type> stuckVars = new LinkedHashSet<Type>(); 1.183 - Env<AttrContext> env; 1.184 1.185 - final Filter<JCTree> argsFilter = new Filter<JCTree>() { 1.186 - public boolean accepts(JCTree t) { 1.187 - switch (t.getTag()) { 1.188 - case CONDEXPR: 1.189 - case LAMBDA: 1.190 - case PARENS: 1.191 - case REFERENCE: 1.192 - return true; 1.193 - default: 1.194 - return false; 1.195 - } 1.196 - } 1.197 - }; 1.198 - 1.199 - final Filter<JCTree> lambdaBodyFilter = new Filter<JCTree>() { 1.200 - public boolean accepts(JCTree t) { 1.201 - switch (t.getTag()) { 1.202 - case BLOCK: case CASE: case CATCH: case DOLOOP: 1.203 - case FOREACHLOOP: case FORLOOP: case RETURN: 1.204 - case SYNCHRONIZED: case SWITCH: case TRY: case WHILELOOP: 1.205 - return true; 1.206 - default: 1.207 - return false; 1.208 - } 1.209 - } 1.210 - }; 1.211 - 1.212 - StuckChecker(ResultInfo resultInfo, Env<AttrContext> env) { 1.213 - this.pt = resultInfo.pt; 1.214 - this.inferenceContext = resultInfo.checkContext.inferenceContext(); 1.215 - this.treeFilter = argsFilter; 1.216 - this.env = env; 1.217 - } 1.218 - 1.219 - @Override 1.220 - public void scan(JCTree tree) { 1.221 - if (tree != null && treeFilter.accepts(tree)) { 1.222 - super.scan(tree); 1.223 - } 1.224 + StuckChecker(Type pt, Infer.InferenceContext inferenceContext) { 1.225 + this.pt = pt; 1.226 + this.inferenceContext = inferenceContext; 1.227 } 1.228 1.229 @Override 1.230 public void visitLambda(JCLambda tree) { 1.231 - Type prevPt = pt; 1.232 - Filter<JCTree> prevFilter = treeFilter; 1.233 - try { 1.234 - if (inferenceContext.inferenceVars().contains(pt)) { 1.235 - stuckVars.add(pt); 1.236 - } 1.237 - if (!types.isFunctionalInterface(pt.tsym)) { 1.238 - return; 1.239 - } 1.240 - Type descType = types.findDescriptorType(pt); 1.241 - List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes()); 1.242 - if (!TreeInfo.isExplicitLambda(tree) && 1.243 - freeArgVars.nonEmpty()) { 1.244 - stuckVars.addAll(freeArgVars); 1.245 - } 1.246 - pt = descType.getReturnType(); 1.247 - if (tree.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) { 1.248 - scan(tree.getBody()); 1.249 - } else { 1.250 - treeFilter = lambdaBodyFilter; 1.251 - super.visitLambda(tree); 1.252 - } 1.253 - } finally { 1.254 - pt = prevPt; 1.255 - treeFilter = prevFilter; 1.256 + if (inferenceContext.inferenceVars().contains(pt)) { 1.257 + stuckVars.add(pt); 1.258 } 1.259 + if (!types.isFunctionalInterface(pt.tsym)) { 1.260 + return; 1.261 + } 1.262 + Type descType = types.findDescriptorType(pt); 1.263 + List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes()); 1.264 + if (!TreeInfo.isExplicitLambda(tree) && 1.265 + freeArgVars.nonEmpty()) { 1.266 + stuckVars.addAll(freeArgVars); 1.267 + } 1.268 + scanLambdaBody(tree, descType.getReturnType()); 1.269 } 1.270 1.271 @Override 1.272 @@ -605,16 +652,19 @@ 1.273 stuckVars.addAll(freeArgVars); 1.274 } 1.275 1.276 - @Override 1.277 - public void visitReturn(JCReturn tree) { 1.278 - Filter<JCTree> prevFilter = treeFilter; 1.279 - try { 1.280 - treeFilter = argsFilter; 1.281 - if (tree.expr != null) { 1.282 - scan(tree.expr); 1.283 - } 1.284 - } finally { 1.285 - treeFilter = prevFilter; 1.286 + void scanLambdaBody(JCLambda lambda, final Type pt) { 1.287 + if (lambda.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) { 1.288 + stuckVars.addAll(stuckVarsInternal(lambda.body, pt, inferenceContext)); 1.289 + } else { 1.290 + LambdaReturnScanner lambdaScanner = new LambdaReturnScanner() { 1.291 + @Override 1.292 + public void visitReturn(JCReturn tree) { 1.293 + if (tree.expr != null) { 1.294 + stuckVars.addAll(stuckVarsInternal(tree.expr, pt, inferenceContext)); 1.295 + } 1.296 + } 1.297 + }; 1.298 + lambdaScanner.scan(lambda.body); 1.299 } 1.300 } 1.301 }