Fri, 05 Jul 2013 11:02:17 +0100
8019480: Javac crashes when method is called on a type-variable receiver from lambda expression
Summary: Logic for shortcircuiting speculative attribution doesn't handle type-variable receivers
Reviewed-by: jjg, vromero
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.tools.javac.code.*;
29 import com.sun.tools.javac.tree.*;
30 import com.sun.tools.javac.util.*;
31 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
32 import com.sun.tools.javac.code.Symbol.*;
33 import com.sun.tools.javac.code.Type.*;
34 import com.sun.tools.javac.comp.Attr.ResultInfo;
35 import com.sun.tools.javac.comp.Infer.InferenceContext;
36 import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase;
37 import com.sun.tools.javac.tree.JCTree.*;
39 import javax.tools.JavaFileObject;
41 import java.util.ArrayList;
42 import java.util.EnumSet;
43 import java.util.LinkedHashSet;
44 import java.util.Map;
45 import java.util.Queue;
46 import java.util.Set;
47 import java.util.WeakHashMap;
49 import static com.sun.tools.javac.code.TypeTag.*;
50 import static com.sun.tools.javac.tree.JCTree.Tag.*;
52 /**
53 * This is an helper class that is used to perform deferred type-analysis.
54 * Each time a poly expression occurs in argument position, javac attributes it
55 * with a temporary 'deferred type' that is checked (possibly multiple times)
56 * against an expected formal type.
57 *
58 * <p><b>This is NOT part of any supported API.
59 * If you write code that depends on this, you do so at your own risk.
60 * This code and its internal interfaces are subject to change or
61 * deletion without notice.</b>
62 */
63 public class DeferredAttr extends JCTree.Visitor {
64 protected static final Context.Key<DeferredAttr> deferredAttrKey =
65 new Context.Key<DeferredAttr>();
67 final Attr attr;
68 final Check chk;
69 final JCDiagnostic.Factory diags;
70 final Enter enter;
71 final Infer infer;
72 final Resolve rs;
73 final Log log;
74 final Symtab syms;
75 final TreeMaker make;
76 final Types types;
78 public static DeferredAttr instance(Context context) {
79 DeferredAttr instance = context.get(deferredAttrKey);
80 if (instance == null)
81 instance = new DeferredAttr(context);
82 return instance;
83 }
85 protected DeferredAttr(Context context) {
86 context.put(deferredAttrKey, this);
87 attr = Attr.instance(context);
88 chk = Check.instance(context);
89 diags = JCDiagnostic.Factory.instance(context);
90 enter = Enter.instance(context);
91 infer = Infer.instance(context);
92 rs = Resolve.instance(context);
93 log = Log.instance(context);
94 syms = Symtab.instance(context);
95 make = TreeMaker.instance(context);
96 types = Types.instance(context);
97 Names names = Names.instance(context);
98 stuckTree = make.Ident(names.empty).setType(Type.noType);
99 }
101 /** shared tree for stuck expressions */
102 final JCTree stuckTree;
104 /**
105 * This type represents a deferred type. A deferred type starts off with
106 * no information on the underlying expression type. Such info needs to be
107 * discovered through type-checking the deferred type against a target-type.
108 * Every deferred type keeps a pointer to the AST node from which it originated.
109 */
110 public class DeferredType extends Type {
112 public JCExpression tree;
113 Env<AttrContext> env;
114 AttrMode mode;
115 SpeculativeCache speculativeCache;
117 DeferredType(JCExpression tree, Env<AttrContext> env) {
118 super(null);
119 this.tree = tree;
120 this.env = env.dup(tree, env.info.dup());
121 this.speculativeCache = new SpeculativeCache();
122 }
124 @Override
125 public TypeTag getTag() {
126 return DEFERRED;
127 }
129 /**
130 * A speculative cache is used to keep track of all overload resolution rounds
131 * that triggered speculative attribution on a given deferred type. Each entry
132 * stores a pointer to the speculative tree and the resolution phase in which the entry
133 * has been added.
134 */
135 class SpeculativeCache {
137 private Map<Symbol, List<Entry>> cache =
138 new WeakHashMap<Symbol, List<Entry>>();
140 class Entry {
141 JCTree speculativeTree;
142 Resolve.MethodResolutionPhase phase;
144 public Entry(JCTree speculativeTree, MethodResolutionPhase phase) {
145 this.speculativeTree = speculativeTree;
146 this.phase = phase;
147 }
149 boolean matches(Resolve.MethodResolutionPhase phase) {
150 return this.phase == phase;
151 }
152 }
154 /**
155 * Retrieve a speculative cache entry corresponding to given symbol
156 * and resolution phase
157 */
158 Entry get(Symbol msym, MethodResolutionPhase phase) {
159 List<Entry> entries = cache.get(msym);
160 if (entries == null) return null;
161 for (Entry e : entries) {
162 if (e.matches(phase)) return e;
163 }
164 return null;
165 }
167 /**
168 * Stores a speculative cache entry corresponding to given symbol
169 * and resolution phase
170 */
171 void put(Symbol msym, JCTree speculativeTree, MethodResolutionPhase phase) {
172 List<Entry> entries = cache.get(msym);
173 if (entries == null) {
174 entries = List.nil();
175 }
176 cache.put(msym, entries.prepend(new Entry(speculativeTree, phase)));
177 }
178 }
180 /**
181 * Get the type that has been computed during a speculative attribution round
182 */
183 Type speculativeType(Symbol msym, MethodResolutionPhase phase) {
184 SpeculativeCache.Entry e = speculativeCache.get(msym, phase);
185 return e != null ? e.speculativeTree.type : Type.noType;
186 }
188 /**
189 * Check a deferred type against a potential target-type. Depending on
190 * the current attribution mode, a normal vs. speculative attribution
191 * round is performed on the underlying AST node. There can be only one
192 * speculative round for a given target method symbol; moreover, a normal
193 * attribution round must follow one or more speculative rounds.
194 */
195 Type check(ResultInfo resultInfo) {
196 return check(resultInfo, stuckVars(tree, env, resultInfo), basicCompleter);
197 }
199 Type check(ResultInfo resultInfo, List<Type> stuckVars, DeferredTypeCompleter deferredTypeCompleter) {
200 DeferredAttrContext deferredAttrContext =
201 resultInfo.checkContext.deferredAttrContext();
202 Assert.check(deferredAttrContext != emptyDeferredAttrContext);
203 if (stuckVars.nonEmpty()) {
204 deferredAttrContext.addDeferredAttrNode(this, resultInfo, stuckVars);
205 return Type.noType;
206 } else {
207 try {
208 return deferredTypeCompleter.complete(this, resultInfo, deferredAttrContext);
209 } finally {
210 mode = deferredAttrContext.mode;
211 }
212 }
213 }
214 }
216 /**
217 * A completer for deferred types. Defines an entry point for type-checking
218 * a deferred type.
219 */
220 interface DeferredTypeCompleter {
221 /**
222 * Entry point for type-checking a deferred type. Depending on the
223 * circumstances, type-checking could amount to full attribution
224 * or partial structural check (aka potential applicability).
225 */
226 Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext);
227 }
229 /**
230 * A basic completer for deferred types. This completer type-checks a deferred type
231 * using attribution; depending on the attribution mode, this could be either standard
232 * or speculative attribution.
233 */
234 DeferredTypeCompleter basicCompleter = new DeferredTypeCompleter() {
235 public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
236 switch (deferredAttrContext.mode) {
237 case SPECULATIVE:
238 //Note: if a symbol is imported twice we might do two identical
239 //speculative rounds...
240 Assert.check(dt.mode == null || dt.mode == AttrMode.SPECULATIVE);
241 JCTree speculativeTree = attribSpeculative(dt.tree, dt.env, resultInfo);
242 dt.speculativeCache.put(deferredAttrContext.msym, speculativeTree, deferredAttrContext.phase);
243 return speculativeTree.type;
244 case CHECK:
245 Assert.check(dt.mode != null);
246 return attr.attribTree(dt.tree, dt.env, resultInfo);
247 }
248 Assert.error();
249 return null;
250 }
251 };
253 DeferredTypeCompleter dummyCompleter = new DeferredTypeCompleter() {
254 public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
255 Assert.check(deferredAttrContext.mode == AttrMode.CHECK);
256 return dt.tree.type = Type.noType;
257 }
258 };
260 /**
261 * The 'mode' in which the deferred type is to be type-checked
262 */
263 public enum AttrMode {
264 /**
265 * A speculative type-checking round is used during overload resolution
266 * mainly to generate constraints on inference variables. Side-effects
267 * arising from type-checking the expression associated with the deferred
268 * type are reversed after the speculative round finishes. This means the
269 * expression tree will be left in a blank state.
270 */
271 SPECULATIVE,
272 /**
273 * This is the plain type-checking mode. Produces side-effects on the underlying AST node
274 */
275 CHECK;
276 }
278 /**
279 * Routine that performs speculative type-checking; the input AST node is
280 * cloned (to avoid side-effects cause by Attr) and compiler state is
281 * restored after type-checking. All diagnostics (but critical ones) are
282 * disabled during speculative type-checking.
283 */
284 JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
285 final JCTree newTree = new TreeCopier<Object>(make).copy(tree);
286 Env<AttrContext> speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared()));
287 speculativeEnv.info.scope.owner = env.info.scope.owner;
288 Log.DeferredDiagnosticHandler deferredDiagnosticHandler =
289 new Log.DeferredDiagnosticHandler(log, new Filter<JCDiagnostic>() {
290 public boolean accepts(final JCDiagnostic d) {
291 class PosScanner extends TreeScanner {
292 boolean found = false;
294 @Override
295 public void scan(JCTree tree) {
296 if (tree != null &&
297 tree.pos() == d.getDiagnosticPosition()) {
298 found = true;
299 }
300 super.scan(tree);
301 }
302 };
303 PosScanner posScanner = new PosScanner();
304 posScanner.scan(newTree);
305 return posScanner.found;
306 }
307 });
308 try {
309 attr.attribTree(newTree, speculativeEnv, resultInfo);
310 unenterScanner.scan(newTree);
311 return newTree;
312 } finally {
313 unenterScanner.scan(newTree);
314 log.popDiagnosticHandler(deferredDiagnosticHandler);
315 }
316 }
317 //where
318 protected TreeScanner unenterScanner = new TreeScanner() {
319 @Override
320 public void visitClassDef(JCClassDecl tree) {
321 ClassSymbol csym = tree.sym;
322 //if something went wrong during method applicability check
323 //it is possible that nested expressions inside argument expression
324 //are left unchecked - in such cases there's nothing to clean up.
325 if (csym == null) return;
326 enter.typeEnvs.remove(csym);
327 chk.compiled.remove(csym.flatname);
328 syms.classes.remove(csym.flatname);
329 super.visitClassDef(tree);
330 }
331 };
333 /**
334 * A deferred context is created on each method check. A deferred context is
335 * used to keep track of information associated with the method check, such as
336 * the symbol of the method being checked, the overload resolution phase,
337 * the kind of attribution mode to be applied to deferred types and so forth.
338 * As deferred types are processed (by the method check routine) stuck AST nodes
339 * are added (as new deferred attribution nodes) to this context. The complete()
340 * routine makes sure that all pending nodes are properly processed, by
341 * progressively instantiating all inference variables on which one or more
342 * deferred attribution node is stuck.
343 */
344 class DeferredAttrContext {
346 /** attribution mode */
347 final AttrMode mode;
349 /** symbol of the method being checked */
350 final Symbol msym;
352 /** method resolution step */
353 final Resolve.MethodResolutionPhase phase;
355 /** inference context */
356 final InferenceContext inferenceContext;
358 /** parent deferred context */
359 final DeferredAttrContext parent;
361 /** Warner object to report warnings */
362 final Warner warn;
364 /** list of deferred attribution nodes to be processed */
365 ArrayList<DeferredAttrNode> deferredAttrNodes = new ArrayList<DeferredAttrNode>();
367 DeferredAttrContext(AttrMode mode, Symbol msym, MethodResolutionPhase phase,
368 InferenceContext inferenceContext, DeferredAttrContext parent, Warner warn) {
369 this.mode = mode;
370 this.msym = msym;
371 this.phase = phase;
372 this.parent = parent;
373 this.warn = warn;
374 this.inferenceContext = inferenceContext;
375 }
377 /**
378 * Adds a node to the list of deferred attribution nodes - used by Resolve.rawCheckArgumentsApplicable
379 * Nodes added this way act as 'roots' for the out-of-order method checking process.
380 */
381 void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
382 deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, stuckVars));
383 }
385 /**
386 * Incrementally process all nodes, by skipping 'stuck' nodes and attributing
387 * 'unstuck' ones. If at any point no progress can be made (no 'unstuck' nodes)
388 * some inference variable might get eagerly instantiated so that all nodes
389 * can be type-checked.
390 */
391 void complete() {
392 while (!deferredAttrNodes.isEmpty()) {
393 Set<Type> stuckVars = new LinkedHashSet<Type>();
394 boolean progress = false;
395 //scan a defensive copy of the node list - this is because a deferred
396 //attribution round can add new nodes to the list
397 for (DeferredAttrNode deferredAttrNode : List.from(deferredAttrNodes)) {
398 if (!deferredAttrNode.process(this)) {
399 stuckVars.addAll(deferredAttrNode.stuckVars);
400 } else {
401 deferredAttrNodes.remove(deferredAttrNode);
402 progress = true;
403 }
404 }
405 if (!progress) {
406 //remove all variables that have already been instantiated
407 //from the list of stuck variables
408 inferenceContext.solveAny(List.from(stuckVars), warn);
409 inferenceContext.notifyChange();
410 }
411 }
412 }
413 }
415 /**
416 * Class representing a deferred attribution node. It keeps track of
417 * a deferred type, along with the expected target type information.
418 */
419 class DeferredAttrNode implements Infer.FreeTypeListener {
421 /** underlying deferred type */
422 DeferredType dt;
424 /** underlying target type information */
425 ResultInfo resultInfo;
427 /** list of uninferred inference variables causing this node to be stuck */
428 List<Type> stuckVars;
430 DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
431 this.dt = dt;
432 this.resultInfo = resultInfo;
433 this.stuckVars = stuckVars;
434 if (!stuckVars.isEmpty()) {
435 resultInfo.checkContext.inferenceContext().addFreeTypeListener(stuckVars, this);
436 }
437 }
439 @Override
440 public void typesInferred(InferenceContext inferenceContext) {
441 stuckVars = List.nil();
442 resultInfo = resultInfo.dup(inferenceContext.asInstType(resultInfo.pt));
443 }
445 /**
446 * Process a deferred attribution node.
447 * Invariant: a stuck node cannot be processed.
448 */
449 @SuppressWarnings("fallthrough")
450 boolean process(DeferredAttrContext deferredAttrContext) {
451 switch (deferredAttrContext.mode) {
452 case SPECULATIVE:
453 dt.check(resultInfo, List.<Type>nil(), new StructuralStuckChecker());
454 return true;
455 case CHECK:
456 if (stuckVars.nonEmpty()) {
457 //stuck expression - see if we can propagate
458 if (deferredAttrContext.parent != emptyDeferredAttrContext &&
459 Type.containsAny(deferredAttrContext.parent.inferenceContext.inferencevars, List.from(stuckVars))) {
460 deferredAttrContext.parent.deferredAttrNodes.add(this);
461 dt.check(resultInfo, List.<Type>nil(), dummyCompleter);
462 return true;
463 } else {
464 return false;
465 }
466 } else {
467 dt.check(resultInfo, stuckVars, basicCompleter);
468 return true;
469 }
470 default:
471 throw new AssertionError("Bad mode");
472 }
473 }
475 /**
476 * Structural checker for stuck expressions
477 */
478 class StructuralStuckChecker extends TreeScanner implements DeferredTypeCompleter {
480 ResultInfo resultInfo;
481 InferenceContext inferenceContext;
482 Env<AttrContext> env;
484 public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
485 this.resultInfo = resultInfo;
486 this.inferenceContext = deferredAttrContext.inferenceContext;
487 this.env = dt.env.dup(dt.tree, dt.env.info.dup());
488 dt.tree.accept(this);
489 dt.speculativeCache.put(deferredAttrContext.msym, stuckTree, deferredAttrContext.phase);
490 return Type.noType;
491 }
493 @Override
494 public void visitLambda(JCLambda tree) {
495 Check.CheckContext checkContext = resultInfo.checkContext;
496 Type pt = resultInfo.pt;
497 if (inferenceContext.inferencevars.contains(pt)) {
498 //ok
499 return;
500 } else {
501 //must be a functional descriptor
502 try {
503 Type desc = types.findDescriptorType(pt);
504 if (desc.getParameterTypes().length() != tree.params.length()) {
505 checkContext.report(tree, diags.fragment("incompatible.arg.types.in.lambda"));
506 }
507 } catch (Types.FunctionDescriptorLookupError ex) {
508 checkContext.report(null, ex.getDiagnostic());
509 }
510 }
511 }
513 @Override
514 public void visitNewClass(JCNewClass tree) {
515 //do nothing
516 }
518 @Override
519 public void visitApply(JCMethodInvocation tree) {
520 //do nothing
521 }
523 @Override
524 public void visitReference(JCMemberReference tree) {
525 Check.CheckContext checkContext = resultInfo.checkContext;
526 Type pt = resultInfo.pt;
527 if (inferenceContext.inferencevars.contains(pt)) {
528 //ok
529 return;
530 } else {
531 try {
532 types.findDescriptorType(pt);
533 } catch (Types.FunctionDescriptorLookupError ex) {
534 checkContext.report(null, ex.getDiagnostic());
535 }
536 JCExpression exprTree = (JCExpression)attribSpeculative(tree.getQualifierExpression(), env,
537 attr.memberReferenceQualifierResult(tree));
538 ListBuffer<Type> argtypes = ListBuffer.lb();
539 for (Type t : types.findDescriptorType(pt).getParameterTypes()) {
540 argtypes.append(Type.noType);
541 }
542 JCMemberReference mref2 = new TreeCopier<Void>(make).copy(tree);
543 mref2.expr = exprTree;
544 Pair<Symbol, ?> lookupRes =
545 rs.resolveMemberReference(tree, env, mref2, exprTree.type,
546 tree.name, argtypes.toList(), null, true, rs.arityMethodCheck);
547 switch (lookupRes.fst.kind) {
548 //note: as argtypes are erroneous types, type-errors must
549 //have been caused by arity mismatch
550 case Kinds.ABSENT_MTH:
551 case Kinds.WRONG_MTH:
552 case Kinds.WRONG_MTHS:
553 case Kinds.STATICERR:
554 case Kinds.MISSING_ENCL:
555 checkContext.report(null, diags.fragment("incompatible.arg.types.in.mref"));
556 }
557 }
558 }
559 }
560 }
562 /** an empty deferred attribution context - all methods throw exceptions */
563 final DeferredAttrContext emptyDeferredAttrContext =
564 new DeferredAttrContext(AttrMode.CHECK, null, MethodResolutionPhase.BOX, null, null, null) {
565 @Override
566 void addDeferredAttrNode(DeferredType dt, ResultInfo ri, List<Type> stuckVars) {
567 Assert.error("Empty deferred context!");
568 }
569 @Override
570 void complete() {
571 Assert.error("Empty deferred context!");
572 }
573 };
575 /**
576 * Map a list of types possibly containing one or more deferred types
577 * into a list of ordinary types. Each deferred type D is mapped into a type T,
578 * where T is computed by retrieving the type that has already been
579 * computed for D during a previous deferred attribution round of the given kind.
580 */
581 class DeferredTypeMap extends Type.Mapping {
583 DeferredAttrContext deferredAttrContext;
585 protected DeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
586 super(String.format("deferredTypeMap[%s]", mode));
587 this.deferredAttrContext = new DeferredAttrContext(mode, msym, phase,
588 infer.emptyContext, emptyDeferredAttrContext, types.noWarnings);
589 }
591 protected boolean validState(DeferredType dt) {
592 return dt.mode != null &&
593 deferredAttrContext.mode.ordinal() <= dt.mode.ordinal();
594 }
596 @Override
597 public Type apply(Type t) {
598 if (!t.hasTag(DEFERRED)) {
599 return t.map(this);
600 } else {
601 DeferredType dt = (DeferredType)t;
602 Assert.check(validState(dt));
603 return typeOf(dt);
604 }
605 }
607 protected Type typeOf(DeferredType dt) {
608 switch (deferredAttrContext.mode) {
609 case CHECK:
610 return dt.tree.type == null ? Type.noType : dt.tree.type;
611 case SPECULATIVE:
612 return dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase);
613 }
614 Assert.error();
615 return null;
616 }
617 }
619 /**
620 * Specialized recovery deferred mapping.
621 * Each deferred type D is mapped into a type T, where T is computed either by
622 * (i) retrieving the type that has already been computed for D during a previous
623 * attribution round (as before), or (ii) by synthesizing a new type R for D
624 * (the latter step is useful in a recovery scenario).
625 */
626 public class RecoveryDeferredTypeMap extends DeferredTypeMap {
628 public RecoveryDeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
629 super(mode, msym, phase != null ? phase : MethodResolutionPhase.BOX);
630 }
632 @Override
633 protected Type typeOf(DeferredType dt) {
634 Type owntype = super.typeOf(dt);
635 return owntype == Type.noType ?
636 recover(dt) : owntype;
637 }
639 @Override
640 protected boolean validState(DeferredType dt) {
641 return true;
642 }
644 /**
645 * Synthesize a type for a deferred type that hasn't been previously
646 * reduced to an ordinary type. Functional deferred types and conditionals
647 * are mapped to themselves, in order to have a richer diagnostic
648 * representation. Remaining deferred types are attributed using
649 * a default expected type (j.l.Object).
650 */
651 private Type recover(DeferredType dt) {
652 dt.check(attr.new RecoveryInfo(deferredAttrContext));
653 return super.apply(dt);
654 }
655 }
657 /**
658 * Retrieves the list of inference variables that need to be inferred before
659 * an AST node can be type-checked
660 */
661 @SuppressWarnings("fallthrough")
662 List<Type> stuckVars(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
663 if (resultInfo.pt.hasTag(NONE) || resultInfo.pt.isErroneous()) {
664 return List.nil();
665 } else {
666 return stuckVarsInternal(tree, resultInfo.pt, resultInfo.checkContext.inferenceContext());
667 }
668 }
669 //where
670 private List<Type> stuckVarsInternal(JCTree tree, Type pt, Infer.InferenceContext inferenceContext) {
671 StuckChecker sc = new StuckChecker(pt, inferenceContext);
672 sc.scan(tree);
673 return List.from(sc.stuckVars);
674 }
676 /**
677 * A special tree scanner that would only visit portions of a given tree.
678 * The set of nodes visited by the scanner can be customized at construction-time.
679 */
680 abstract static class FilterScanner extends TreeScanner {
682 final Filter<JCTree> treeFilter;
684 FilterScanner(final Set<JCTree.Tag> validTags) {
685 this.treeFilter = new Filter<JCTree>() {
686 public boolean accepts(JCTree t) {
687 return validTags.contains(t.getTag());
688 }
689 };
690 }
692 @Override
693 public void scan(JCTree tree) {
694 if (tree != null) {
695 if (treeFilter.accepts(tree)) {
696 super.scan(tree);
697 } else {
698 skip(tree);
699 }
700 }
701 }
703 /**
704 * handler that is executed when a node has been discarded
705 */
706 abstract void skip(JCTree tree);
707 }
709 /**
710 * A tree scanner suitable for visiting the target-type dependent nodes of
711 * a given argument expression.
712 */
713 static class PolyScanner extends FilterScanner {
715 PolyScanner() {
716 super(EnumSet.of(CONDEXPR, PARENS, LAMBDA, REFERENCE));
717 }
719 @Override
720 void skip(JCTree tree) {
721 //do nothing
722 }
723 }
725 /**
726 * A tree scanner suitable for visiting the target-type dependent nodes nested
727 * within a lambda expression body.
728 */
729 static class LambdaReturnScanner extends FilterScanner {
731 LambdaReturnScanner() {
732 super(EnumSet.of(BLOCK, CASE, CATCH, DOLOOP, FOREACHLOOP,
733 FORLOOP, RETURN, SYNCHRONIZED, SWITCH, TRY, WHILELOOP));
734 }
736 @Override
737 void skip(JCTree tree) {
738 //do nothing
739 }
740 }
742 /**
743 * This visitor is used to check that structural expressions conform
744 * to their target - this step is required as inference could end up
745 * inferring types that make some of the nested expressions incompatible
746 * with their corresponding instantiated target
747 */
748 class StuckChecker extends PolyScanner {
750 Type pt;
751 Infer.InferenceContext inferenceContext;
752 Set<Type> stuckVars = new LinkedHashSet<Type>();
754 StuckChecker(Type pt, Infer.InferenceContext inferenceContext) {
755 this.pt = pt;
756 this.inferenceContext = inferenceContext;
757 }
759 @Override
760 public void visitLambda(JCLambda tree) {
761 if (inferenceContext.inferenceVars().contains(pt)) {
762 stuckVars.add(pt);
763 }
764 if (!types.isFunctionalInterface(pt)) {
765 return;
766 }
767 Type descType = types.findDescriptorType(pt);
768 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
769 if (tree.paramKind == JCLambda.ParameterKind.IMPLICIT &&
770 freeArgVars.nonEmpty()) {
771 stuckVars.addAll(freeArgVars);
772 }
773 scanLambdaBody(tree, descType.getReturnType());
774 }
776 @Override
777 public void visitReference(JCMemberReference tree) {
778 scan(tree.expr);
779 if (inferenceContext.inferenceVars().contains(pt)) {
780 stuckVars.add(pt);
781 return;
782 }
783 if (!types.isFunctionalInterface(pt)) {
784 return;
785 }
787 Type descType = types.findDescriptorType(pt);
788 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
789 stuckVars.addAll(freeArgVars);
790 }
792 void scanLambdaBody(JCLambda lambda, final Type pt) {
793 if (lambda.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) {
794 stuckVars.addAll(stuckVarsInternal(lambda.body, pt, inferenceContext));
795 } else {
796 LambdaReturnScanner lambdaScanner = new LambdaReturnScanner() {
797 @Override
798 public void visitReturn(JCReturn tree) {
799 if (tree.expr != null) {
800 stuckVars.addAll(stuckVarsInternal(tree.expr, pt, inferenceContext));
801 }
802 }
803 };
804 lambdaScanner.scan(lambda.body);
805 }
806 }
807 }
809 /**
810 * Does the argument expression {@code expr} need speculative type-checking?
811 */
812 boolean isDeferred(Env<AttrContext> env, JCExpression expr) {
813 DeferredChecker dc = new DeferredChecker(env);
814 dc.scan(expr);
815 return dc.result.isPoly();
816 }
818 /**
819 * The kind of an argument expression. This is used by the analysis that
820 * determines as to whether speculative attribution is necessary.
821 */
822 enum ArgumentExpressionKind {
824 /** kind that denotes poly argument expression */
825 POLY,
826 /** kind that denotes a standalone expression */
827 NO_POLY,
828 /** kind that denotes a primitive/boxed standalone expression */
829 PRIMITIVE;
831 /**
832 * Does this kind denote a poly argument expression
833 */
834 public final boolean isPoly() {
835 return this == POLY;
836 }
838 /**
839 * Does this kind denote a primitive standalone expression
840 */
841 public final boolean isPrimitive() {
842 return this == PRIMITIVE;
843 }
845 /**
846 * Compute the kind of a standalone expression of a given type
847 */
848 static ArgumentExpressionKind standaloneKind(Type type, Types types) {
849 return types.unboxedTypeOrType(type).isPrimitive() ?
850 ArgumentExpressionKind.PRIMITIVE :
851 ArgumentExpressionKind.NO_POLY;
852 }
854 /**
855 * Compute the kind of a method argument expression given its symbol
856 */
857 static ArgumentExpressionKind methodKind(Symbol sym, Types types) {
858 Type restype = sym.type.getReturnType();
859 if (sym.type.hasTag(FORALL) &&
860 restype.containsAny(((ForAll)sym.type).tvars)) {
861 return ArgumentExpressionKind.POLY;
862 } else {
863 return ArgumentExpressionKind.standaloneKind(restype, types);
864 }
865 }
866 }
868 /**
869 * Tree scanner used for checking as to whether an argument expression
870 * requires speculative attribution
871 */
872 final class DeferredChecker extends FilterScanner {
874 Env<AttrContext> env;
875 ArgumentExpressionKind result;
877 public DeferredChecker(Env<AttrContext> env) {
878 super(deferredCheckerTags);
879 this.env = env;
880 }
882 @Override
883 public void visitLambda(JCLambda tree) {
884 //a lambda is always a poly expression
885 result = ArgumentExpressionKind.POLY;
886 }
888 @Override
889 public void visitReference(JCMemberReference tree) {
890 //a method reference is always a poly expression
891 result = ArgumentExpressionKind.POLY;
892 }
894 @Override
895 public void visitTypeCast(JCTypeCast tree) {
896 //a cast is always a standalone expression
897 result = ArgumentExpressionKind.NO_POLY;
898 }
900 @Override
901 public void visitConditional(JCConditional tree) {
902 scan(tree.truepart);
903 if (!result.isPrimitive()) {
904 result = ArgumentExpressionKind.POLY;
905 return;
906 }
907 scan(tree.falsepart);
908 result = reduce(ArgumentExpressionKind.PRIMITIVE);
909 }
911 @Override
912 public void visitNewClass(JCNewClass tree) {
913 result = (TreeInfo.isDiamond(tree) || attr.findDiamonds) ?
914 ArgumentExpressionKind.POLY : ArgumentExpressionKind.NO_POLY;
915 }
917 @Override
918 public void visitApply(JCMethodInvocation tree) {
919 Name name = TreeInfo.name(tree.meth);
921 //fast path
922 if (tree.typeargs.nonEmpty() ||
923 name == name.table.names._this ||
924 name == name.table.names._super) {
925 result = ArgumentExpressionKind.NO_POLY;
926 return;
927 }
929 //slow path
930 final JCExpression rec = tree.meth.hasTag(SELECT) ?
931 ((JCFieldAccess)tree.meth).selected :
932 null;
934 if (rec != null && !isSimpleReceiver(rec)) {
935 //give up if receiver is too complex (to cut down analysis time)
936 result = ArgumentExpressionKind.POLY;
937 return;
938 }
940 Type site = rec != null ?
941 attribSpeculative(rec, env, attr.unknownTypeExprInfo).type :
942 env.enclClass.sym.type;
944 while (site.hasTag(TYPEVAR)) {
945 site = site.getUpperBound();
946 }
948 ListBuffer<Type> args = ListBuffer.lb();
949 for (int i = 0; i < tree.args.length(); i ++) {
950 args.append(Type.noType);
951 }
953 Resolve.LookupHelper lh = rs.new LookupHelper(name, site, args.toList(), List.<Type>nil(), MethodResolutionPhase.VARARITY) {
954 @Override
955 Symbol lookup(Env<AttrContext> env, MethodResolutionPhase phase) {
956 return rec == null ?
957 rs.findFun(env, name, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired()) :
958 rs.findMethod(env, site, name, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired(), false);
959 }
960 @Override
961 Symbol access(Env<AttrContext> env, DiagnosticPosition pos, Symbol location, Symbol sym) {
962 return sym;
963 }
964 };
966 Symbol sym = rs.lookupMethod(env, tree, site.tsym, rs.arityMethodCheck, lh);
968 if (sym.kind == Kinds.AMBIGUOUS) {
969 Resolve.AmbiguityError err = (Resolve.AmbiguityError)sym.baseSymbol();
970 result = ArgumentExpressionKind.PRIMITIVE;
971 for (Symbol s : err.ambiguousSyms) {
972 if (result.isPoly()) break;
973 if (s.kind == Kinds.MTH) {
974 result = reduce(ArgumentExpressionKind.methodKind(s, types));
975 }
976 }
977 } else {
978 result = (sym.kind == Kinds.MTH) ?
979 ArgumentExpressionKind.methodKind(sym, types) :
980 ArgumentExpressionKind.NO_POLY;
981 }
982 }
983 //where
984 private boolean isSimpleReceiver(JCTree rec) {
985 switch (rec.getTag()) {
986 case IDENT:
987 return true;
988 case SELECT:
989 return isSimpleReceiver(((JCFieldAccess)rec).selected);
990 case TYPEAPPLY:
991 case TYPEARRAY:
992 return true;
993 case ANNOTATED_TYPE:
994 return isSimpleReceiver(((JCAnnotatedType)rec).underlyingType);
995 default:
996 return false;
997 }
998 }
999 private ArgumentExpressionKind reduce(ArgumentExpressionKind kind) {
1000 switch (result) {
1001 case PRIMITIVE: return kind;
1002 case NO_POLY: return kind.isPoly() ? kind : result;
1003 case POLY: return result;
1004 default:
1005 Assert.error();
1006 return null;
1007 }
1008 }
1010 @Override
1011 public void visitLiteral(JCLiteral tree) {
1012 Type litType = attr.litType(tree.typetag);
1013 result = ArgumentExpressionKind.standaloneKind(litType, types);
1014 }
1016 @Override
1017 void skip(JCTree tree) {
1018 result = ArgumentExpressionKind.NO_POLY;
1019 }
1020 }
1021 //where
1022 private EnumSet<JCTree.Tag> deferredCheckerTags =
1023 EnumSet.of(LAMBDA, REFERENCE, PARENS, TYPECAST,
1024 CONDEXPR, NEWCLASS, APPLY, LITERAL);
1025 }