Mon, 02 Sep 2013 22:38:36 +0100
8016177: structural most specific and stuckness
Reviewed-by: jjg, vromero
Contributed-by: maurizio.cimadamore@oracle.com
1 /*
2 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
26 package com.sun.tools.javac.comp;
28 import com.sun.source.tree.MemberReferenceTree;
29 import com.sun.tools.javac.code.*;
30 import com.sun.tools.javac.tree.*;
31 import com.sun.tools.javac.util.*;
32 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
33 import com.sun.tools.javac.code.Symbol.*;
34 import com.sun.tools.javac.code.Type.*;
35 import com.sun.tools.javac.comp.Attr.ResultInfo;
36 import com.sun.tools.javac.comp.Infer.InferenceContext;
37 import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase;
38 import com.sun.tools.javac.comp.Resolve.ReferenceLookupHelper;
39 import com.sun.tools.javac.tree.JCTree.*;
42 import java.util.ArrayList;
43 import java.util.Collections;
44 import java.util.EnumSet;
45 import java.util.LinkedHashMap;
46 import java.util.LinkedHashSet;
47 import java.util.Map;
48 import java.util.Set;
49 import java.util.WeakHashMap;
51 import static com.sun.tools.javac.code.TypeTag.*;
52 import static com.sun.tools.javac.tree.JCTree.Tag.*;
54 /**
55 * This is an helper class that is used to perform deferred type-analysis.
56 * Each time a poly expression occurs in argument position, javac attributes it
57 * with a temporary 'deferred type' that is checked (possibly multiple times)
58 * against an expected formal type.
59 *
60 * <p><b>This is NOT part of any supported API.
61 * If you write code that depends on this, you do so at your own risk.
62 * This code and its internal interfaces are subject to change or
63 * deletion without notice.</b>
64 */
65 public class DeferredAttr extends JCTree.Visitor {
66 protected static final Context.Key<DeferredAttr> deferredAttrKey =
67 new Context.Key<DeferredAttr>();
69 final Attr attr;
70 final Check chk;
71 final JCDiagnostic.Factory diags;
72 final Enter enter;
73 final Infer infer;
74 final Resolve rs;
75 final Log log;
76 final Symtab syms;
77 final TreeMaker make;
78 final Types types;
80 public static DeferredAttr instance(Context context) {
81 DeferredAttr instance = context.get(deferredAttrKey);
82 if (instance == null)
83 instance = new DeferredAttr(context);
84 return instance;
85 }
87 protected DeferredAttr(Context context) {
88 context.put(deferredAttrKey, this);
89 attr = Attr.instance(context);
90 chk = Check.instance(context);
91 diags = JCDiagnostic.Factory.instance(context);
92 enter = Enter.instance(context);
93 infer = Infer.instance(context);
94 rs = Resolve.instance(context);
95 log = Log.instance(context);
96 syms = Symtab.instance(context);
97 make = TreeMaker.instance(context);
98 types = Types.instance(context);
99 Names names = Names.instance(context);
100 stuckTree = make.Ident(names.empty).setType(Type.stuckType);
101 emptyDeferredAttrContext =
102 new DeferredAttrContext(AttrMode.CHECK, null, MethodResolutionPhase.BOX, infer.emptyContext, null, null) {
103 @Override
104 void addDeferredAttrNode(DeferredType dt, ResultInfo ri, DeferredStuckPolicy deferredStuckPolicy) {
105 Assert.error("Empty deferred context!");
106 }
107 @Override
108 void complete() {
109 Assert.error("Empty deferred context!");
110 }
111 };
112 }
114 /** shared tree for stuck expressions */
115 final JCTree stuckTree;
117 /**
118 * This type represents a deferred type. A deferred type starts off with
119 * no information on the underlying expression type. Such info needs to be
120 * discovered through type-checking the deferred type against a target-type.
121 * Every deferred type keeps a pointer to the AST node from which it originated.
122 */
123 public class DeferredType extends Type {
125 public JCExpression tree;
126 Env<AttrContext> env;
127 AttrMode mode;
128 SpeculativeCache speculativeCache;
130 DeferredType(JCExpression tree, Env<AttrContext> env) {
131 super(null);
132 this.tree = tree;
133 this.env = attr.copyEnv(env);
134 this.speculativeCache = new SpeculativeCache();
135 }
137 @Override
138 public TypeTag getTag() {
139 return DEFERRED;
140 }
142 /**
143 * A speculative cache is used to keep track of all overload resolution rounds
144 * that triggered speculative attribution on a given deferred type. Each entry
145 * stores a pointer to the speculative tree and the resolution phase in which the entry
146 * has been added.
147 */
148 class SpeculativeCache {
150 private Map<Symbol, List<Entry>> cache =
151 new WeakHashMap<Symbol, List<Entry>>();
153 class Entry {
154 JCTree speculativeTree;
155 ResultInfo resultInfo;
157 public Entry(JCTree speculativeTree, ResultInfo resultInfo) {
158 this.speculativeTree = speculativeTree;
159 this.resultInfo = resultInfo;
160 }
162 boolean matches(MethodResolutionPhase phase) {
163 return resultInfo.checkContext.deferredAttrContext().phase == phase;
164 }
165 }
167 /**
168 * Retrieve a speculative cache entry corresponding to given symbol
169 * and resolution phase
170 */
171 Entry get(Symbol msym, MethodResolutionPhase phase) {
172 List<Entry> entries = cache.get(msym);
173 if (entries == null) return null;
174 for (Entry e : entries) {
175 if (e.matches(phase)) return e;
176 }
177 return null;
178 }
180 /**
181 * Stores a speculative cache entry corresponding to given symbol
182 * and resolution phase
183 */
184 void put(JCTree speculativeTree, ResultInfo resultInfo) {
185 Symbol msym = resultInfo.checkContext.deferredAttrContext().msym;
186 List<Entry> entries = cache.get(msym);
187 if (entries == null) {
188 entries = List.nil();
189 }
190 cache.put(msym, entries.prepend(new Entry(speculativeTree, resultInfo)));
191 }
192 }
194 /**
195 * Get the type that has been computed during a speculative attribution round
196 */
197 Type speculativeType(Symbol msym, MethodResolutionPhase phase) {
198 SpeculativeCache.Entry e = speculativeCache.get(msym, phase);
199 return e != null ? e.speculativeTree.type : Type.noType;
200 }
202 /**
203 * Check a deferred type against a potential target-type. Depending on
204 * the current attribution mode, a normal vs. speculative attribution
205 * round is performed on the underlying AST node. There can be only one
206 * speculative round for a given target method symbol; moreover, a normal
207 * attribution round must follow one or more speculative rounds.
208 */
209 Type check(ResultInfo resultInfo) {
210 DeferredStuckPolicy deferredStuckPolicy;
211 if (resultInfo.pt.hasTag(NONE) || resultInfo.pt.isErroneous()) {
212 deferredStuckPolicy = dummyStuckPolicy;
213 } else if (resultInfo.checkContext.deferredAttrContext().mode == AttrMode.SPECULATIVE) {
214 deferredStuckPolicy = new OverloadStuckPolicy(resultInfo, this);
215 } else {
216 deferredStuckPolicy = new CheckStuckPolicy(resultInfo, this);
217 }
218 return check(resultInfo, deferredStuckPolicy, basicCompleter);
219 }
221 private Type check(ResultInfo resultInfo, DeferredStuckPolicy deferredStuckPolicy,
222 DeferredTypeCompleter deferredTypeCompleter) {
223 DeferredAttrContext deferredAttrContext =
224 resultInfo.checkContext.deferredAttrContext();
225 Assert.check(deferredAttrContext != emptyDeferredAttrContext);
226 if (deferredStuckPolicy.isStuck()) {
227 deferredAttrContext.addDeferredAttrNode(this, resultInfo, deferredStuckPolicy);
228 return Type.noType;
229 } else {
230 try {
231 return deferredTypeCompleter.complete(this, resultInfo, deferredAttrContext);
232 } finally {
233 mode = deferredAttrContext.mode;
234 }
235 }
236 }
237 }
239 /**
240 * A completer for deferred types. Defines an entry point for type-checking
241 * a deferred type.
242 */
243 interface DeferredTypeCompleter {
244 /**
245 * Entry point for type-checking a deferred type. Depending on the
246 * circumstances, type-checking could amount to full attribution
247 * or partial structural check (aka potential applicability).
248 */
249 Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext);
250 }
253 /**
254 * A basic completer for deferred types. This completer type-checks a deferred type
255 * using attribution; depending on the attribution mode, this could be either standard
256 * or speculative attribution.
257 */
258 DeferredTypeCompleter basicCompleter = new DeferredTypeCompleter() {
259 public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
260 switch (deferredAttrContext.mode) {
261 case SPECULATIVE:
262 //Note: if a symbol is imported twice we might do two identical
263 //speculative rounds...
264 Assert.check(dt.mode == null || dt.mode == AttrMode.SPECULATIVE);
265 JCTree speculativeTree = attribSpeculative(dt.tree, dt.env, resultInfo);
266 dt.speculativeCache.put(speculativeTree, resultInfo);
267 return speculativeTree.type;
268 case CHECK:
269 Assert.check(dt.mode != null);
270 return attr.attribTree(dt.tree, dt.env, resultInfo);
271 }
272 Assert.error();
273 return null;
274 }
275 };
277 DeferredTypeCompleter dummyCompleter = new DeferredTypeCompleter() {
278 public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
279 Assert.check(deferredAttrContext.mode == AttrMode.CHECK);
280 return dt.tree.type = Type.stuckType;
281 }
282 };
284 /**
285 * Policy for detecting stuck expressions. Different criteria might cause
286 * an expression to be judged as stuck, depending on whether the check
287 * is performed during overload resolution or after most specific.
288 */
289 interface DeferredStuckPolicy {
290 /**
291 * Has the policy detected that a given expression should be considered stuck?
292 */
293 boolean isStuck();
294 /**
295 * Get the set of inference variables a given expression depends upon.
296 */
297 Set<Type> stuckVars();
298 /**
299 * Get the set of inference variables which might get new constraints
300 * if a given expression is being type-checked.
301 */
302 Set<Type> depVars();
303 }
305 /**
306 * Basic stuck policy; an expression is never considered to be stuck.
307 */
308 DeferredStuckPolicy dummyStuckPolicy = new DeferredStuckPolicy() {
309 @Override
310 public boolean isStuck() {
311 return false;
312 }
313 @Override
314 public Set<Type> stuckVars() {
315 return Collections.emptySet();
316 }
317 @Override
318 public Set<Type> depVars() {
319 return Collections.emptySet();
320 }
321 };
323 /**
324 * The 'mode' in which the deferred type is to be type-checked
325 */
326 public enum AttrMode {
327 /**
328 * A speculative type-checking round is used during overload resolution
329 * mainly to generate constraints on inference variables. Side-effects
330 * arising from type-checking the expression associated with the deferred
331 * type are reversed after the speculative round finishes. This means the
332 * expression tree will be left in a blank state.
333 */
334 SPECULATIVE,
335 /**
336 * This is the plain type-checking mode. Produces side-effects on the underlying AST node
337 */
338 CHECK;
339 }
341 /**
342 * Routine that performs speculative type-checking; the input AST node is
343 * cloned (to avoid side-effects cause by Attr) and compiler state is
344 * restored after type-checking. All diagnostics (but critical ones) are
345 * disabled during speculative type-checking.
346 */
347 JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
348 final JCTree newTree = new TreeCopier<Object>(make).copy(tree);
349 Env<AttrContext> speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared()));
350 speculativeEnv.info.scope.owner = env.info.scope.owner;
351 Log.DeferredDiagnosticHandler deferredDiagnosticHandler =
352 new Log.DeferredDiagnosticHandler(log, new Filter<JCDiagnostic>() {
353 public boolean accepts(final JCDiagnostic d) {
354 class PosScanner extends TreeScanner {
355 boolean found = false;
357 @Override
358 public void scan(JCTree tree) {
359 if (tree != null &&
360 tree.pos() == d.getDiagnosticPosition()) {
361 found = true;
362 }
363 super.scan(tree);
364 }
365 };
366 PosScanner posScanner = new PosScanner();
367 posScanner.scan(newTree);
368 return posScanner.found;
369 }
370 });
371 try {
372 attr.attribTree(newTree, speculativeEnv, resultInfo);
373 unenterScanner.scan(newTree);
374 return newTree;
375 } finally {
376 unenterScanner.scan(newTree);
377 log.popDiagnosticHandler(deferredDiagnosticHandler);
378 }
379 }
380 //where
381 protected TreeScanner unenterScanner = new TreeScanner() {
382 @Override
383 public void visitClassDef(JCClassDecl tree) {
384 ClassSymbol csym = tree.sym;
385 //if something went wrong during method applicability check
386 //it is possible that nested expressions inside argument expression
387 //are left unchecked - in such cases there's nothing to clean up.
388 if (csym == null) return;
389 enter.typeEnvs.remove(csym);
390 chk.compiled.remove(csym.flatname);
391 syms.classes.remove(csym.flatname);
392 super.visitClassDef(tree);
393 }
394 };
396 /**
397 * A deferred context is created on each method check. A deferred context is
398 * used to keep track of information associated with the method check, such as
399 * the symbol of the method being checked, the overload resolution phase,
400 * the kind of attribution mode to be applied to deferred types and so forth.
401 * As deferred types are processed (by the method check routine) stuck AST nodes
402 * are added (as new deferred attribution nodes) to this context. The complete()
403 * routine makes sure that all pending nodes are properly processed, by
404 * progressively instantiating all inference variables on which one or more
405 * deferred attribution node is stuck.
406 */
407 class DeferredAttrContext {
409 /** attribution mode */
410 final AttrMode mode;
412 /** symbol of the method being checked */
413 final Symbol msym;
415 /** method resolution step */
416 final Resolve.MethodResolutionPhase phase;
418 /** inference context */
419 final InferenceContext inferenceContext;
421 /** parent deferred context */
422 final DeferredAttrContext parent;
424 /** Warner object to report warnings */
425 final Warner warn;
427 /** list of deferred attribution nodes to be processed */
428 ArrayList<DeferredAttrNode> deferredAttrNodes = new ArrayList<DeferredAttrNode>();
430 DeferredAttrContext(AttrMode mode, Symbol msym, MethodResolutionPhase phase,
431 InferenceContext inferenceContext, DeferredAttrContext parent, Warner warn) {
432 this.mode = mode;
433 this.msym = msym;
434 this.phase = phase;
435 this.parent = parent;
436 this.warn = warn;
437 this.inferenceContext = inferenceContext;
438 }
440 /**
441 * Adds a node to the list of deferred attribution nodes - used by Resolve.rawCheckArgumentsApplicable
442 * Nodes added this way act as 'roots' for the out-of-order method checking process.
443 */
444 void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo,
445 DeferredStuckPolicy deferredStuckPolicy) {
446 deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, deferredStuckPolicy));
447 }
449 /**
450 * Incrementally process all nodes, by skipping 'stuck' nodes and attributing
451 * 'unstuck' ones. If at any point no progress can be made (no 'unstuck' nodes)
452 * some inference variable might get eagerly instantiated so that all nodes
453 * can be type-checked.
454 */
455 void complete() {
456 while (!deferredAttrNodes.isEmpty()) {
457 Map<Type, Set<Type>> depVarsMap = new LinkedHashMap<Type, Set<Type>>();
458 List<Type> stuckVars = List.nil();
459 boolean progress = false;
460 //scan a defensive copy of the node list - this is because a deferred
461 //attribution round can add new nodes to the list
462 for (DeferredAttrNode deferredAttrNode : List.from(deferredAttrNodes)) {
463 if (!deferredAttrNode.process(this)) {
464 List<Type> restStuckVars =
465 List.from(deferredAttrNode.deferredStuckPolicy.stuckVars())
466 .intersect(inferenceContext.restvars());
467 stuckVars = stuckVars.prependList(restStuckVars);
468 //update dependency map
469 for (Type t : List.from(deferredAttrNode.deferredStuckPolicy.depVars())
470 .intersect(inferenceContext.restvars())) {
471 Set<Type> prevDeps = depVarsMap.get(t);
472 if (prevDeps == null) {
473 prevDeps = new LinkedHashSet<Type>();
474 depVarsMap.put(t, prevDeps);
475 }
476 prevDeps.addAll(restStuckVars);
477 }
478 } else {
479 deferredAttrNodes.remove(deferredAttrNode);
480 progress = true;
481 }
482 }
483 if (!progress) {
484 DeferredAttrContext dac = this;
485 while (dac != emptyDeferredAttrContext) {
486 if (dac.mode == AttrMode.SPECULATIVE) {
487 //unsticking does not take place during overload
488 break;
489 }
490 dac = dac.parent;
491 }
492 //remove all variables that have already been instantiated
493 //from the list of stuck variables
494 try {
495 inferenceContext.solveAny(stuckVars, depVarsMap, warn);
496 inferenceContext.notifyChange();
497 } catch (Infer.GraphStrategy.NodeNotFoundException ex) {
498 //this means that we are in speculative mode and the
499 //set of contraints are too tight for progess to be made.
500 //Just leave the remaining expressions as stuck.
501 break;
502 }
503 }
504 }
505 }
506 }
508 /**
509 * Class representing a deferred attribution node. It keeps track of
510 * a deferred type, along with the expected target type information.
511 */
512 class DeferredAttrNode {
514 /** underlying deferred type */
515 DeferredType dt;
517 /** underlying target type information */
518 ResultInfo resultInfo;
520 /** stuck policy associated with this node */
521 DeferredStuckPolicy deferredStuckPolicy;
523 DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, DeferredStuckPolicy deferredStuckPolicy) {
524 this.dt = dt;
525 this.resultInfo = resultInfo;
526 this.deferredStuckPolicy = deferredStuckPolicy;
527 }
529 /**
530 * Process a deferred attribution node.
531 * Invariant: a stuck node cannot be processed.
532 */
533 @SuppressWarnings("fallthrough")
534 boolean process(final DeferredAttrContext deferredAttrContext) {
535 switch (deferredAttrContext.mode) {
536 case SPECULATIVE:
537 if (deferredStuckPolicy.isStuck()) {
538 dt.check(resultInfo, dummyStuckPolicy, new StructuralStuckChecker());
539 return true;
540 } else {
541 Assert.error("Cannot get here");
542 }
543 case CHECK:
544 if (deferredStuckPolicy.isStuck()) {
545 //stuck expression - see if we can propagate
546 if (deferredAttrContext.parent != emptyDeferredAttrContext &&
547 Type.containsAny(deferredAttrContext.parent.inferenceContext.inferencevars,
548 List.from(deferredStuckPolicy.stuckVars()))) {
549 deferredAttrContext.parent.addDeferredAttrNode(dt,
550 resultInfo.dup(new Check.NestedCheckContext(resultInfo.checkContext) {
551 @Override
552 public InferenceContext inferenceContext() {
553 return deferredAttrContext.parent.inferenceContext;
554 }
555 @Override
556 public DeferredAttrContext deferredAttrContext() {
557 return deferredAttrContext.parent;
558 }
559 }), deferredStuckPolicy);
560 dt.tree.type = Type.stuckType;
561 return true;
562 } else {
563 return false;
564 }
565 } else {
566 ResultInfo instResultInfo =
567 resultInfo.dup(deferredAttrContext.inferenceContext.asInstType(resultInfo.pt));
568 dt.check(instResultInfo, dummyStuckPolicy, basicCompleter);
569 return true;
570 }
571 default:
572 throw new AssertionError("Bad mode");
573 }
574 }
576 /**
577 * Structural checker for stuck expressions
578 */
579 class StructuralStuckChecker extends TreeScanner implements DeferredTypeCompleter {
581 ResultInfo resultInfo;
582 InferenceContext inferenceContext;
583 Env<AttrContext> env;
585 public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
586 this.resultInfo = resultInfo;
587 this.inferenceContext = deferredAttrContext.inferenceContext;
588 this.env = dt.env;
589 dt.tree.accept(this);
590 dt.speculativeCache.put(stuckTree, resultInfo);
591 return Type.noType;
592 }
594 @Override
595 public void visitLambda(JCLambda tree) {
596 Check.CheckContext checkContext = resultInfo.checkContext;
597 Type pt = resultInfo.pt;
598 if (inferenceContext.inferencevars.contains(pt)) {
599 //ok
600 return;
601 } else {
602 //must be a functional descriptor
603 try {
604 Type desc = types.findDescriptorType(pt);
605 if (desc.getParameterTypes().length() != tree.params.length()) {
606 checkContext.report(tree, diags.fragment("incompatible.arg.types.in.lambda"));
607 }
608 } catch (Types.FunctionDescriptorLookupError ex) {
609 checkContext.report(null, ex.getDiagnostic());
610 }
611 }
612 }
614 @Override
615 public void visitNewClass(JCNewClass tree) {
616 //do nothing
617 }
619 @Override
620 public void visitApply(JCMethodInvocation tree) {
621 //do nothing
622 }
624 @Override
625 public void visitReference(JCMemberReference tree) {
626 Check.CheckContext checkContext = resultInfo.checkContext;
627 Type pt = resultInfo.pt;
628 if (inferenceContext.inferencevars.contains(pt)) {
629 //ok
630 return;
631 } else {
632 try {
633 types.findDescriptorType(pt);
634 } catch (Types.FunctionDescriptorLookupError ex) {
635 checkContext.report(null, ex.getDiagnostic());
636 }
637 Env<AttrContext> localEnv = env.dup(tree);
638 JCExpression exprTree = (JCExpression)attribSpeculative(tree.getQualifierExpression(), localEnv,
639 attr.memberReferenceQualifierResult(tree));
640 ListBuffer<Type> argtypes = ListBuffer.lb();
641 for (Type t : types.findDescriptorType(pt).getParameterTypes()) {
642 argtypes.append(Type.noType);
643 }
644 JCMemberReference mref2 = new TreeCopier<Void>(make).copy(tree);
645 mref2.expr = exprTree;
646 Pair<Symbol, ?> lookupRes =
647 rs.resolveMemberReference(tree, localEnv, mref2, exprTree.type,
648 tree.name, argtypes.toList(), null, true, rs.arityMethodCheck, inferenceContext);
649 switch (lookupRes.fst.kind) {
650 //note: as argtypes are erroneous types, type-errors must
651 //have been caused by arity mismatch
652 case Kinds.ABSENT_MTH:
653 case Kinds.WRONG_MTH:
654 case Kinds.WRONG_MTHS:
655 checkContext.report(tree, diags.fragment("incompatible.arg.types.in.mref"));
656 }
657 }
658 }
659 }
660 }
662 /** an empty deferred attribution context - all methods throw exceptions */
663 final DeferredAttrContext emptyDeferredAttrContext;
665 /**
666 * Map a list of types possibly containing one or more deferred types
667 * into a list of ordinary types. Each deferred type D is mapped into a type T,
668 * where T is computed by retrieving the type that has already been
669 * computed for D during a previous deferred attribution round of the given kind.
670 */
671 class DeferredTypeMap extends Type.Mapping {
673 DeferredAttrContext deferredAttrContext;
675 protected DeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
676 super(String.format("deferredTypeMap[%s]", mode));
677 this.deferredAttrContext = new DeferredAttrContext(mode, msym, phase,
678 infer.emptyContext, emptyDeferredAttrContext, types.noWarnings);
679 }
681 @Override
682 public Type apply(Type t) {
683 if (!t.hasTag(DEFERRED)) {
684 return t.map(this);
685 } else {
686 DeferredType dt = (DeferredType)t;
687 return typeOf(dt);
688 }
689 }
691 protected Type typeOf(DeferredType dt) {
692 switch (deferredAttrContext.mode) {
693 case CHECK:
694 return dt.tree.type == null ? Type.noType : dt.tree.type;
695 case SPECULATIVE:
696 return dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase);
697 }
698 Assert.error();
699 return null;
700 }
701 }
703 /**
704 * Specialized recovery deferred mapping.
705 * Each deferred type D is mapped into a type T, where T is computed either by
706 * (i) retrieving the type that has already been computed for D during a previous
707 * attribution round (as before), or (ii) by synthesizing a new type R for D
708 * (the latter step is useful in a recovery scenario).
709 */
710 public class RecoveryDeferredTypeMap extends DeferredTypeMap {
712 public RecoveryDeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
713 super(mode, msym, phase != null ? phase : MethodResolutionPhase.BOX);
714 }
716 @Override
717 protected Type typeOf(DeferredType dt) {
718 Type owntype = super.typeOf(dt);
719 return owntype == Type.noType ?
720 recover(dt) : owntype;
721 }
723 /**
724 * Synthesize a type for a deferred type that hasn't been previously
725 * reduced to an ordinary type. Functional deferred types and conditionals
726 * are mapped to themselves, in order to have a richer diagnostic
727 * representation. Remaining deferred types are attributed using
728 * a default expected type (j.l.Object).
729 */
730 private Type recover(DeferredType dt) {
731 dt.check(attr.new RecoveryInfo(deferredAttrContext) {
732 @Override
733 protected Type check(DiagnosticPosition pos, Type found) {
734 return chk.checkNonVoid(pos, super.check(pos, found));
735 }
736 });
737 return super.apply(dt);
738 }
739 }
741 /**
742 * A special tree scanner that would only visit portions of a given tree.
743 * The set of nodes visited by the scanner can be customized at construction-time.
744 */
745 abstract static class FilterScanner extends TreeScanner {
747 final Filter<JCTree> treeFilter;
749 FilterScanner(final Set<JCTree.Tag> validTags) {
750 this.treeFilter = new Filter<JCTree>() {
751 public boolean accepts(JCTree t) {
752 return validTags.contains(t.getTag());
753 }
754 };
755 }
757 @Override
758 public void scan(JCTree tree) {
759 if (tree != null) {
760 if (treeFilter.accepts(tree)) {
761 super.scan(tree);
762 } else {
763 skip(tree);
764 }
765 }
766 }
768 /**
769 * handler that is executed when a node has been discarded
770 */
771 abstract void skip(JCTree tree);
772 }
774 /**
775 * A tree scanner suitable for visiting the target-type dependent nodes of
776 * a given argument expression.
777 */
778 static class PolyScanner extends FilterScanner {
780 PolyScanner() {
781 super(EnumSet.of(CONDEXPR, PARENS, LAMBDA, REFERENCE));
782 }
784 @Override
785 void skip(JCTree tree) {
786 //do nothing
787 }
788 }
790 /**
791 * A tree scanner suitable for visiting the target-type dependent nodes nested
792 * within a lambda expression body.
793 */
794 static class LambdaReturnScanner extends FilterScanner {
796 LambdaReturnScanner() {
797 super(EnumSet.of(BLOCK, CASE, CATCH, DOLOOP, FOREACHLOOP,
798 FORLOOP, RETURN, SYNCHRONIZED, SWITCH, TRY, WHILELOOP));
799 }
801 @Override
802 void skip(JCTree tree) {
803 //do nothing
804 }
805 }
807 /**
808 * This visitor is used to check that structural expressions conform
809 * to their target - this step is required as inference could end up
810 * inferring types that make some of the nested expressions incompatible
811 * with their corresponding instantiated target
812 */
813 class CheckStuckPolicy extends PolyScanner implements DeferredStuckPolicy, Infer.FreeTypeListener {
815 Type pt;
816 Infer.InferenceContext inferenceContext;
817 Set<Type> stuckVars = new LinkedHashSet<Type>();
818 Set<Type> depVars = new LinkedHashSet<Type>();
820 @Override
821 public boolean isStuck() {
822 return !stuckVars.isEmpty();
823 }
825 @Override
826 public Set<Type> stuckVars() {
827 return stuckVars;
828 }
830 @Override
831 public Set<Type> depVars() {
832 return depVars;
833 }
835 public CheckStuckPolicy(ResultInfo resultInfo, DeferredType dt) {
836 this.pt = resultInfo.pt;
837 this.inferenceContext = resultInfo.checkContext.inferenceContext();
838 scan(dt.tree);
839 if (!stuckVars.isEmpty()) {
840 resultInfo.checkContext.inferenceContext()
841 .addFreeTypeListener(List.from(stuckVars), this);
842 }
843 }
845 @Override
846 public void typesInferred(InferenceContext inferenceContext) {
847 stuckVars.clear();
848 }
850 @Override
851 public void visitLambda(JCLambda tree) {
852 if (inferenceContext.inferenceVars().contains(pt)) {
853 stuckVars.add(pt);
854 }
855 if (!types.isFunctionalInterface(pt)) {
856 return;
857 }
858 Type descType = types.findDescriptorType(pt);
859 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
860 if (tree.paramKind == JCLambda.ParameterKind.IMPLICIT &&
861 freeArgVars.nonEmpty()) {
862 stuckVars.addAll(freeArgVars);
863 depVars.addAll(inferenceContext.freeVarsIn(descType.getReturnType()));
864 }
865 scanLambdaBody(tree, descType.getReturnType());
866 }
868 @Override
869 public void visitReference(JCMemberReference tree) {
870 scan(tree.expr);
871 if (inferenceContext.inferenceVars().contains(pt)) {
872 stuckVars.add(pt);
873 return;
874 }
875 if (!types.isFunctionalInterface(pt)) {
876 return;
877 }
879 Type descType = types.findDescriptorType(pt);
880 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
881 if (freeArgVars.nonEmpty() &&
882 tree.overloadKind == JCMemberReference.OverloadKind.OVERLOADED) {
883 stuckVars.addAll(freeArgVars);
884 depVars.addAll(inferenceContext.freeVarsIn(descType.getReturnType()));
885 }
886 }
888 void scanLambdaBody(JCLambda lambda, final Type pt) {
889 if (lambda.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) {
890 Type prevPt = this.pt;
891 try {
892 this.pt = pt;
893 scan(lambda.body);
894 } finally {
895 this.pt = prevPt;
896 }
897 } else {
898 LambdaReturnScanner lambdaScanner = new LambdaReturnScanner() {
899 @Override
900 public void visitReturn(JCReturn tree) {
901 if (tree.expr != null) {
902 Type prevPt = CheckStuckPolicy.this.pt;
903 try {
904 CheckStuckPolicy.this.pt = pt;
905 CheckStuckPolicy.this.scan(tree.expr);
906 } finally {
907 CheckStuckPolicy.this.pt = prevPt;
908 }
909 }
910 }
911 };
912 lambdaScanner.scan(lambda.body);
913 }
914 }
915 }
917 /**
918 * This visitor is used to check that structural expressions conform
919 * to their target - this step is required as inference could end up
920 * inferring types that make some of the nested expressions incompatible
921 * with their corresponding instantiated target
922 */
923 class OverloadStuckPolicy extends CheckStuckPolicy implements DeferredStuckPolicy {
925 boolean stuck;
927 @Override
928 public boolean isStuck() {
929 return super.isStuck() || stuck;
930 }
932 public OverloadStuckPolicy(ResultInfo resultInfo, DeferredType dt) {
933 super(resultInfo, dt);
934 }
936 @Override
937 public void visitLambda(JCLambda tree) {
938 super.visitLambda(tree);
939 if (tree.paramKind == JCLambda.ParameterKind.IMPLICIT) {
940 stuck = true;
941 }
942 }
944 @Override
945 public void visitReference(JCMemberReference tree) {
946 super.visitReference(tree);
947 if (tree.overloadKind == JCMemberReference.OverloadKind.OVERLOADED) {
948 stuck = true;
949 }
950 }
951 }
953 /**
954 * Does the argument expression {@code expr} need speculative type-checking?
955 */
956 boolean isDeferred(Env<AttrContext> env, JCExpression expr) {
957 DeferredChecker dc = new DeferredChecker(env);
958 dc.scan(expr);
959 return dc.result.isPoly();
960 }
962 /**
963 * The kind of an argument expression. This is used by the analysis that
964 * determines as to whether speculative attribution is necessary.
965 */
966 enum ArgumentExpressionKind {
968 /** kind that denotes poly argument expression */
969 POLY,
970 /** kind that denotes a standalone expression */
971 NO_POLY,
972 /** kind that denotes a primitive/boxed standalone expression */
973 PRIMITIVE;
975 /**
976 * Does this kind denote a poly argument expression
977 */
978 public final boolean isPoly() {
979 return this == POLY;
980 }
982 /**
983 * Does this kind denote a primitive standalone expression
984 */
985 public final boolean isPrimitive() {
986 return this == PRIMITIVE;
987 }
989 /**
990 * Compute the kind of a standalone expression of a given type
991 */
992 static ArgumentExpressionKind standaloneKind(Type type, Types types) {
993 return types.unboxedTypeOrType(type).isPrimitive() ?
994 ArgumentExpressionKind.PRIMITIVE :
995 ArgumentExpressionKind.NO_POLY;
996 }
998 /**
999 * Compute the kind of a method argument expression given its symbol
1000 */
1001 static ArgumentExpressionKind methodKind(Symbol sym, Types types) {
1002 Type restype = sym.type.getReturnType();
1003 if (sym.type.hasTag(FORALL) &&
1004 restype.containsAny(((ForAll)sym.type).tvars)) {
1005 return ArgumentExpressionKind.POLY;
1006 } else {
1007 return ArgumentExpressionKind.standaloneKind(restype, types);
1008 }
1009 }
1010 }
1012 /**
1013 * Tree scanner used for checking as to whether an argument expression
1014 * requires speculative attribution
1015 */
1016 final class DeferredChecker extends FilterScanner {
1018 Env<AttrContext> env;
1019 ArgumentExpressionKind result;
1021 public DeferredChecker(Env<AttrContext> env) {
1022 super(deferredCheckerTags);
1023 this.env = env;
1024 }
1026 @Override
1027 public void visitLambda(JCLambda tree) {
1028 //a lambda is always a poly expression
1029 result = ArgumentExpressionKind.POLY;
1030 }
1032 @Override
1033 public void visitReference(JCMemberReference tree) {
1034 //perform arity-based check
1035 Env<AttrContext> localEnv = env.dup(tree);
1036 JCExpression exprTree = (JCExpression)attribSpeculative(tree.getQualifierExpression(), localEnv,
1037 attr.memberReferenceQualifierResult(tree));
1038 JCMemberReference mref2 = new TreeCopier<Void>(make).copy(tree);
1039 mref2.expr = exprTree;
1040 Pair<Symbol, ReferenceLookupHelper> lookupRes =
1041 rs.resolveMemberReference(tree, localEnv, mref2, exprTree.type,
1042 tree.name, List.<Type>nil(), null, true, rs.nilMethodCheck,
1043 infer.emptyContext);
1044 Symbol res = tree.sym = lookupRes.fst;
1045 if (res.kind >= Kinds.ERRONEOUS ||
1046 res.type.hasTag(FORALL) ||
1047 (res.flags() & Flags.VARARGS) != 0 ||
1048 (TreeInfo.isStaticSelector(exprTree, tree.name.table.names) &&
1049 exprTree.type.isRaw())) {
1050 tree.overloadKind = JCMemberReference.OverloadKind.OVERLOADED;
1051 } else {
1052 tree.overloadKind = JCMemberReference.OverloadKind.UNOVERLOADED;
1053 }
1054 //a method reference is always a poly expression
1055 result = ArgumentExpressionKind.POLY;
1056 }
1058 @Override
1059 public void visitTypeCast(JCTypeCast tree) {
1060 //a cast is always a standalone expression
1061 result = ArgumentExpressionKind.NO_POLY;
1062 }
1064 @Override
1065 public void visitConditional(JCConditional tree) {
1066 scan(tree.truepart);
1067 if (!result.isPrimitive()) {
1068 result = ArgumentExpressionKind.POLY;
1069 return;
1070 }
1071 scan(tree.falsepart);
1072 result = reduce(ArgumentExpressionKind.PRIMITIVE);
1073 }
1075 @Override
1076 public void visitNewClass(JCNewClass tree) {
1077 result = (TreeInfo.isDiamond(tree) || attr.findDiamonds) ?
1078 ArgumentExpressionKind.POLY : ArgumentExpressionKind.NO_POLY;
1079 }
1081 @Override
1082 public void visitApply(JCMethodInvocation tree) {
1083 Name name = TreeInfo.name(tree.meth);
1085 //fast path
1086 if (tree.typeargs.nonEmpty() ||
1087 name == name.table.names._this ||
1088 name == name.table.names._super) {
1089 result = ArgumentExpressionKind.NO_POLY;
1090 return;
1091 }
1093 //slow path
1094 final JCExpression rec = tree.meth.hasTag(SELECT) ?
1095 ((JCFieldAccess)tree.meth).selected :
1096 null;
1098 if (rec != null && !isSimpleReceiver(rec)) {
1099 //give up if receiver is too complex (to cut down analysis time)
1100 result = ArgumentExpressionKind.POLY;
1101 return;
1102 }
1104 Type site = rec != null ?
1105 attribSpeculative(rec, env, attr.unknownTypeExprInfo).type :
1106 env.enclClass.sym.type;
1108 while (site.hasTag(TYPEVAR)) {
1109 site = site.getUpperBound();
1110 }
1112 List<Type> args = rs.dummyArgs(tree.args.length());
1114 Resolve.LookupHelper lh = rs.new LookupHelper(name, site, args, List.<Type>nil(), MethodResolutionPhase.VARARITY) {
1115 @Override
1116 Symbol lookup(Env<AttrContext> env, MethodResolutionPhase phase) {
1117 return rec == null ?
1118 rs.findFun(env, name, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired()) :
1119 rs.findMethod(env, site, name, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired(), false);
1120 }
1121 @Override
1122 Symbol access(Env<AttrContext> env, DiagnosticPosition pos, Symbol location, Symbol sym) {
1123 return sym;
1124 }
1125 };
1127 Symbol sym = rs.lookupMethod(env, tree, site.tsym, rs.arityMethodCheck, lh);
1129 if (sym.kind == Kinds.AMBIGUOUS) {
1130 Resolve.AmbiguityError err = (Resolve.AmbiguityError)sym.baseSymbol();
1131 result = ArgumentExpressionKind.PRIMITIVE;
1132 for (Symbol s : err.ambiguousSyms) {
1133 if (result.isPoly()) break;
1134 if (s.kind == Kinds.MTH) {
1135 result = reduce(ArgumentExpressionKind.methodKind(s, types));
1136 }
1137 }
1138 } else {
1139 result = (sym.kind == Kinds.MTH) ?
1140 ArgumentExpressionKind.methodKind(sym, types) :
1141 ArgumentExpressionKind.NO_POLY;
1142 }
1143 }
1144 //where
1145 private boolean isSimpleReceiver(JCTree rec) {
1146 switch (rec.getTag()) {
1147 case IDENT:
1148 return true;
1149 case SELECT:
1150 return isSimpleReceiver(((JCFieldAccess)rec).selected);
1151 case TYPEAPPLY:
1152 case TYPEARRAY:
1153 return true;
1154 case ANNOTATED_TYPE:
1155 return isSimpleReceiver(((JCAnnotatedType)rec).underlyingType);
1156 default:
1157 return false;
1158 }
1159 }
1160 private ArgumentExpressionKind reduce(ArgumentExpressionKind kind) {
1161 switch (result) {
1162 case PRIMITIVE: return kind;
1163 case NO_POLY: return kind.isPoly() ? kind : result;
1164 case POLY: return result;
1165 default:
1166 Assert.error();
1167 return null;
1168 }
1169 }
1171 @Override
1172 public void visitLiteral(JCLiteral tree) {
1173 Type litType = attr.litType(tree.typetag);
1174 result = ArgumentExpressionKind.standaloneKind(litType, types);
1175 }
1177 @Override
1178 void skip(JCTree tree) {
1179 result = ArgumentExpressionKind.NO_POLY;
1180 }
1181 }
1182 //where
1183 private EnumSet<JCTree.Tag> deferredCheckerTags =
1184 EnumSet.of(LAMBDA, REFERENCE, PARENS, TYPECAST,
1185 CONDEXPR, NEWCLASS, APPLY, LITERAL);
1186 }