175 * round is performed on the underlying AST node. There can be only one |
176 * round is performed on the underlying AST node. There can be only one |
176 * speculative round for a given target method symbol; moreover, a normal |
177 * speculative round for a given target method symbol; moreover, a normal |
177 * attribution round must follow one or more speculative rounds. |
178 * attribution round must follow one or more speculative rounds. |
178 */ |
179 */ |
179 Type check(ResultInfo resultInfo) { |
180 Type check(ResultInfo resultInfo) { |
|
181 return check(resultInfo, stuckVars(tree, env, resultInfo), basicCompleter); |
|
182 } |
|
183 |
|
184 Type check(ResultInfo resultInfo, List<Type> stuckVars, DeferredTypeCompleter deferredTypeCompleter) { |
180 DeferredAttrContext deferredAttrContext = |
185 DeferredAttrContext deferredAttrContext = |
181 resultInfo.checkContext.deferredAttrContext(); |
186 resultInfo.checkContext.deferredAttrContext(); |
182 Assert.check(deferredAttrContext != emptyDeferredAttrContext); |
187 Assert.check(deferredAttrContext != emptyDeferredAttrContext); |
183 List<Type> stuckVars = stuckVars(tree, env, resultInfo); |
|
184 if (stuckVars.nonEmpty()) { |
188 if (stuckVars.nonEmpty()) { |
185 deferredAttrContext.addDeferredAttrNode(this, resultInfo, stuckVars); |
189 deferredAttrContext.addDeferredAttrNode(this, resultInfo, stuckVars); |
186 return Type.noType; |
190 return Type.noType; |
187 } else { |
191 } else { |
188 try { |
192 try { |
189 switch (deferredAttrContext.mode) { |
193 return deferredTypeCompleter.complete(this, resultInfo, deferredAttrContext); |
190 case SPECULATIVE: |
|
191 Assert.check(mode == null || |
|
192 (mode == AttrMode.SPECULATIVE && |
|
193 speculativeType(deferredAttrContext.msym, deferredAttrContext.phase).hasTag(NONE))); |
|
194 JCTree speculativeTree = attribSpeculative(tree, env, resultInfo); |
|
195 speculativeCache.put(deferredAttrContext.msym, speculativeTree, deferredAttrContext.phase); |
|
196 return speculativeTree.type; |
|
197 case CHECK: |
|
198 Assert.check(mode == AttrMode.SPECULATIVE); |
|
199 return attr.attribTree(tree, env, resultInfo); |
|
200 } |
|
201 Assert.error(); |
|
202 return null; |
|
203 } finally { |
194 } finally { |
204 mode = deferredAttrContext.mode; |
195 mode = deferredAttrContext.mode; |
205 } |
196 } |
206 } |
197 } |
207 } |
198 } |
208 } |
199 } |
|
200 |
|
201 /** |
|
202 * A completer for deferred types. Defines an entry point for type-checking |
|
203 * a deferred type. |
|
204 */ |
|
205 interface DeferredTypeCompleter { |
|
206 /** |
|
207 * Entry point for type-checking a deferred type. Depending on the |
|
208 * circumstances, type-checking could amount to full attribution |
|
209 * or partial structural check (aka potential applicability). |
|
210 */ |
|
211 Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext); |
|
212 } |
|
213 |
|
214 /** |
|
215 * A basic completer for deferred types. This completer type-checks a deferred type |
|
216 * using attribution; depending on the attribution mode, this could be either standard |
|
217 * or speculative attribution. |
|
218 */ |
|
219 DeferredTypeCompleter basicCompleter = new DeferredTypeCompleter() { |
|
220 public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) { |
|
221 switch (deferredAttrContext.mode) { |
|
222 case SPECULATIVE: |
|
223 Assert.check(dt.mode == null || |
|
224 (dt.mode == AttrMode.SPECULATIVE && |
|
225 dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase).hasTag(NONE))); |
|
226 JCTree speculativeTree = attribSpeculative(dt.tree, dt.env, resultInfo); |
|
227 dt.speculativeCache.put(deferredAttrContext.msym, speculativeTree, deferredAttrContext.phase); |
|
228 return speculativeTree.type; |
|
229 case CHECK: |
|
230 Assert.check(dt.mode == AttrMode.SPECULATIVE); |
|
231 return attr.attribTree(dt.tree, dt.env, resultInfo); |
|
232 } |
|
233 Assert.error(); |
|
234 return null; |
|
235 } |
|
236 }; |
209 |
237 |
210 /** |
238 /** |
211 * The 'mode' in which the deferred type is to be type-checked |
239 * The 'mode' in which the deferred type is to be type-checked |
212 */ |
240 */ |
213 public enum AttrMode { |
241 public enum AttrMode { |
496 @SuppressWarnings("fallthrough") |
524 @SuppressWarnings("fallthrough") |
497 List<Type> stuckVars(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) { |
525 List<Type> stuckVars(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) { |
498 if (resultInfo.pt.hasTag(NONE) || resultInfo.pt.isErroneous()) { |
526 if (resultInfo.pt.hasTag(NONE) || resultInfo.pt.isErroneous()) { |
499 return List.nil(); |
527 return List.nil(); |
500 } else { |
528 } else { |
501 StuckChecker sc = new StuckChecker(resultInfo, env); |
529 return stuckVarsInternal(tree, resultInfo.pt, resultInfo.checkContext.inferenceContext()); |
|
530 } |
|
531 } |
|
532 //where |
|
533 private List<Type> stuckVarsInternal(JCTree tree, Type pt, Infer.InferenceContext inferenceContext) { |
|
534 StuckChecker sc = new StuckChecker(pt, inferenceContext); |
502 sc.scan(tree); |
535 sc.scan(tree); |
503 return List.from(sc.stuckVars); |
536 return List.from(sc.stuckVars); |
|
537 } |
|
538 |
|
539 /** |
|
540 * A special tree scanner that would only visit portions of a given tree. |
|
541 * The set of nodes visited by the scanner can be customized at construction-time. |
|
542 */ |
|
543 abstract static class FilterScanner extends TreeScanner { |
|
544 |
|
545 final Filter<JCTree> treeFilter; |
|
546 |
|
547 FilterScanner(final Set<JCTree.Tag> validTags) { |
|
548 this.treeFilter = new Filter<JCTree>() { |
|
549 public boolean accepts(JCTree t) { |
|
550 return validTags.contains(t.getTag()); |
|
551 } |
|
552 }; |
|
553 } |
|
554 |
|
555 @Override |
|
556 public void scan(JCTree tree) { |
|
557 if (tree != null) { |
|
558 if (treeFilter.accepts(tree)) { |
|
559 super.scan(tree); |
|
560 } else { |
|
561 skip(tree); |
|
562 } |
|
563 } |
|
564 } |
|
565 |
|
566 /** |
|
567 * handler that is executed when a node has been discarded |
|
568 */ |
|
569 abstract void skip(JCTree tree); |
|
570 } |
|
571 |
|
572 /** |
|
573 * A tree scanner suitable for visiting the target-type dependent nodes of |
|
574 * a given argument expression. |
|
575 */ |
|
576 static class PolyScanner extends FilterScanner { |
|
577 |
|
578 PolyScanner() { |
|
579 super(EnumSet.of(CONDEXPR, PARENS, LAMBDA, REFERENCE)); |
|
580 } |
|
581 |
|
582 @Override |
|
583 void skip(JCTree tree) { |
|
584 //do nothing |
|
585 } |
|
586 } |
|
587 |
|
588 /** |
|
589 * A tree scanner suitable for visiting the target-type dependent nodes nested |
|
590 * within a lambda expression body. |
|
591 */ |
|
592 static class LambdaReturnScanner extends FilterScanner { |
|
593 |
|
594 LambdaReturnScanner() { |
|
595 super(EnumSet.of(BLOCK, CASE, CATCH, DOLOOP, FOREACHLOOP, |
|
596 FORLOOP, RETURN, SYNCHRONIZED, SWITCH, TRY, WHILELOOP)); |
|
597 } |
|
598 |
|
599 @Override |
|
600 void skip(JCTree tree) { |
|
601 //do nothing |
504 } |
602 } |
505 } |
603 } |
506 |
604 |
507 /** |
605 /** |
508 * This visitor is used to check that structural expressions conform |
606 * This visitor is used to check that structural expressions conform |
509 * to their target - this step is required as inference could end up |
607 * to their target - this step is required as inference could end up |
510 * inferring types that make some of the nested expressions incompatible |
608 * inferring types that make some of the nested expressions incompatible |
511 * with their corresponding instantiated target |
609 * with their corresponding instantiated target |
512 */ |
610 */ |
513 class StuckChecker extends TreeScanner { |
611 class StuckChecker extends PolyScanner { |
514 |
612 |
515 Type pt; |
613 Type pt; |
516 Filter<JCTree> treeFilter; |
|
517 Infer.InferenceContext inferenceContext; |
614 Infer.InferenceContext inferenceContext; |
518 Set<Type> stuckVars = new LinkedHashSet<Type>(); |
615 Set<Type> stuckVars = new LinkedHashSet<Type>(); |
519 Env<AttrContext> env; |
616 |
520 |
617 StuckChecker(Type pt, Infer.InferenceContext inferenceContext) { |
521 final Filter<JCTree> argsFilter = new Filter<JCTree>() { |
618 this.pt = pt; |
522 public boolean accepts(JCTree t) { |
619 this.inferenceContext = inferenceContext; |
523 switch (t.getTag()) { |
|
524 case CONDEXPR: |
|
525 case LAMBDA: |
|
526 case PARENS: |
|
527 case REFERENCE: |
|
528 return true; |
|
529 default: |
|
530 return false; |
|
531 } |
|
532 } |
|
533 }; |
|
534 |
|
535 final Filter<JCTree> lambdaBodyFilter = new Filter<JCTree>() { |
|
536 public boolean accepts(JCTree t) { |
|
537 switch (t.getTag()) { |
|
538 case BLOCK: case CASE: case CATCH: case DOLOOP: |
|
539 case FOREACHLOOP: case FORLOOP: case RETURN: |
|
540 case SYNCHRONIZED: case SWITCH: case TRY: case WHILELOOP: |
|
541 return true; |
|
542 default: |
|
543 return false; |
|
544 } |
|
545 } |
|
546 }; |
|
547 |
|
548 StuckChecker(ResultInfo resultInfo, Env<AttrContext> env) { |
|
549 this.pt = resultInfo.pt; |
|
550 this.inferenceContext = resultInfo.checkContext.inferenceContext(); |
|
551 this.treeFilter = argsFilter; |
|
552 this.env = env; |
|
553 } |
|
554 |
|
555 @Override |
|
556 public void scan(JCTree tree) { |
|
557 if (tree != null && treeFilter.accepts(tree)) { |
|
558 super.scan(tree); |
|
559 } |
|
560 } |
620 } |
561 |
621 |
562 @Override |
622 @Override |
563 public void visitLambda(JCLambda tree) { |
623 public void visitLambda(JCLambda tree) { |
564 Type prevPt = pt; |
624 if (inferenceContext.inferenceVars().contains(pt)) { |
565 Filter<JCTree> prevFilter = treeFilter; |
625 stuckVars.add(pt); |
566 try { |
626 } |
567 if (inferenceContext.inferenceVars().contains(pt)) { |
627 if (!types.isFunctionalInterface(pt.tsym)) { |
568 stuckVars.add(pt); |
628 return; |
569 } |
629 } |
570 if (!types.isFunctionalInterface(pt.tsym)) { |
630 Type descType = types.findDescriptorType(pt); |
571 return; |
631 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes()); |
572 } |
632 if (!TreeInfo.isExplicitLambda(tree) && |
573 Type descType = types.findDescriptorType(pt); |
633 freeArgVars.nonEmpty()) { |
574 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes()); |
634 stuckVars.addAll(freeArgVars); |
575 if (!TreeInfo.isExplicitLambda(tree) && |
635 } |
576 freeArgVars.nonEmpty()) { |
636 scanLambdaBody(tree, descType.getReturnType()); |
577 stuckVars.addAll(freeArgVars); |
|
578 } |
|
579 pt = descType.getReturnType(); |
|
580 if (tree.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) { |
|
581 scan(tree.getBody()); |
|
582 } else { |
|
583 treeFilter = lambdaBodyFilter; |
|
584 super.visitLambda(tree); |
|
585 } |
|
586 } finally { |
|
587 pt = prevPt; |
|
588 treeFilter = prevFilter; |
|
589 } |
|
590 } |
637 } |
591 |
638 |
592 @Override |
639 @Override |
593 public void visitReference(JCMemberReference tree) { |
640 public void visitReference(JCMemberReference tree) { |
594 scan(tree.expr); |
641 scan(tree.expr); |