Tue, 26 Feb 2013 09:04:19 +0000
8008436: javac should not issue a warning for overriding equals without hasCode if hashCode has been overriden by a superclass
Reviewed-by: jjg, mcimadamore
1 /*
2 * Copyright (c) 2012, 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.code.Symbol.*;
32 import com.sun.tools.javac.code.Type.*;
33 import com.sun.tools.javac.comp.Attr.ResultInfo;
34 import com.sun.tools.javac.comp.Infer.InferenceContext;
35 import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase;
36 import com.sun.tools.javac.tree.JCTree.*;
38 import javax.tools.JavaFileObject;
40 import java.util.ArrayList;
41 import java.util.EnumSet;
42 import java.util.LinkedHashSet;
43 import java.util.Map;
44 import java.util.Queue;
45 import java.util.Set;
46 import java.util.WeakHashMap;
48 import static com.sun.tools.javac.code.TypeTag.*;
49 import static com.sun.tools.javac.tree.JCTree.Tag.*;
51 /**
52 * This is an helper class that is used to perform deferred type-analysis.
53 * Each time a poly expression occurs in argument position, javac attributes it
54 * with a temporary 'deferred type' that is checked (possibly multiple times)
55 * against an expected formal type.
56 *
57 * <p><b>This is NOT part of any supported API.
58 * If you write code that depends on this, you do so at your own risk.
59 * This code and its internal interfaces are subject to change or
60 * deletion without notice.</b>
61 */
62 public class DeferredAttr extends JCTree.Visitor {
63 protected static final Context.Key<DeferredAttr> deferredAttrKey =
64 new Context.Key<DeferredAttr>();
66 final Attr attr;
67 final Check chk;
68 final JCDiagnostic.Factory diags;
69 final Enter enter;
70 final Infer infer;
71 final Resolve rs;
72 final Log log;
73 final Symtab syms;
74 final TreeMaker make;
75 final Types types;
77 public static DeferredAttr instance(Context context) {
78 DeferredAttr instance = context.get(deferredAttrKey);
79 if (instance == null)
80 instance = new DeferredAttr(context);
81 return instance;
82 }
84 protected DeferredAttr(Context context) {
85 context.put(deferredAttrKey, this);
86 attr = Attr.instance(context);
87 chk = Check.instance(context);
88 diags = JCDiagnostic.Factory.instance(context);
89 enter = Enter.instance(context);
90 infer = Infer.instance(context);
91 rs = Resolve.instance(context);
92 log = Log.instance(context);
93 syms = Symtab.instance(context);
94 make = TreeMaker.instance(context);
95 types = Types.instance(context);
96 Names names = Names.instance(context);
97 stuckTree = make.Ident(names.empty).setType(Type.noType);
98 }
100 /** shared tree for stuck expressions */
101 final JCTree stuckTree;
103 /**
104 * This type represents a deferred type. A deferred type starts off with
105 * no information on the underlying expression type. Such info needs to be
106 * discovered through type-checking the deferred type against a target-type.
107 * Every deferred type keeps a pointer to the AST node from which it originated.
108 */
109 public class DeferredType extends Type {
111 public JCExpression tree;
112 Env<AttrContext> env;
113 AttrMode mode;
114 SpeculativeCache speculativeCache;
116 DeferredType(JCExpression tree, Env<AttrContext> env) {
117 super(DEFERRED, null);
118 this.tree = tree;
119 this.env = env.dup(tree, env.info.dup());
120 this.speculativeCache = new SpeculativeCache();
121 }
123 /**
124 * A speculative cache is used to keep track of all overload resolution rounds
125 * that triggered speculative attribution on a given deferred type. Each entry
126 * stores a pointer to the speculative tree and the resolution phase in which the entry
127 * has been added.
128 */
129 class SpeculativeCache {
131 private Map<Symbol, List<Entry>> cache =
132 new WeakHashMap<Symbol, List<Entry>>();
134 class Entry {
135 JCTree speculativeTree;
136 Resolve.MethodResolutionPhase phase;
138 public Entry(JCTree speculativeTree, MethodResolutionPhase phase) {
139 this.speculativeTree = speculativeTree;
140 this.phase = phase;
141 }
143 boolean matches(Resolve.MethodResolutionPhase phase) {
144 return this.phase == phase;
145 }
146 }
148 /**
149 * Retrieve a speculative cache entry corresponding to given symbol
150 * and resolution phase
151 */
152 Entry get(Symbol msym, MethodResolutionPhase phase) {
153 List<Entry> entries = cache.get(msym);
154 if (entries == null) return null;
155 for (Entry e : entries) {
156 if (e.matches(phase)) return e;
157 }
158 return null;
159 }
161 /**
162 * Stores a speculative cache entry corresponding to given symbol
163 * and resolution phase
164 */
165 void put(Symbol msym, JCTree speculativeTree, MethodResolutionPhase phase) {
166 List<Entry> entries = cache.get(msym);
167 if (entries == null) {
168 entries = List.nil();
169 }
170 cache.put(msym, entries.prepend(new Entry(speculativeTree, phase)));
171 }
172 }
174 /**
175 * Get the type that has been computed during a speculative attribution round
176 */
177 Type speculativeType(Symbol msym, MethodResolutionPhase phase) {
178 SpeculativeCache.Entry e = speculativeCache.get(msym, phase);
179 return e != null ? e.speculativeTree.type : Type.noType;
180 }
182 /**
183 * Check a deferred type against a potential target-type. Depending on
184 * the current attribution mode, a normal vs. speculative attribution
185 * round is performed on the underlying AST node. There can be only one
186 * speculative round for a given target method symbol; moreover, a normal
187 * attribution round must follow one or more speculative rounds.
188 */
189 Type check(ResultInfo resultInfo) {
190 return check(resultInfo, stuckVars(tree, env, resultInfo), basicCompleter);
191 }
193 Type check(ResultInfo resultInfo, List<Type> stuckVars, DeferredTypeCompleter deferredTypeCompleter) {
194 DeferredAttrContext deferredAttrContext =
195 resultInfo.checkContext.deferredAttrContext();
196 Assert.check(deferredAttrContext != emptyDeferredAttrContext);
197 if (stuckVars.nonEmpty()) {
198 deferredAttrContext.addDeferredAttrNode(this, resultInfo, stuckVars);
199 return Type.noType;
200 } else {
201 try {
202 return deferredTypeCompleter.complete(this, resultInfo, deferredAttrContext);
203 } finally {
204 mode = deferredAttrContext.mode;
205 }
206 }
207 }
208 }
210 /**
211 * A completer for deferred types. Defines an entry point for type-checking
212 * a deferred type.
213 */
214 interface DeferredTypeCompleter {
215 /**
216 * Entry point for type-checking a deferred type. Depending on the
217 * circumstances, type-checking could amount to full attribution
218 * or partial structural check (aka potential applicability).
219 */
220 Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext);
221 }
223 /**
224 * A basic completer for deferred types. This completer type-checks a deferred type
225 * using attribution; depending on the attribution mode, this could be either standard
226 * or speculative attribution.
227 */
228 DeferredTypeCompleter basicCompleter = new DeferredTypeCompleter() {
229 public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
230 switch (deferredAttrContext.mode) {
231 case SPECULATIVE:
232 Assert.check(dt.mode == null ||
233 (dt.mode == AttrMode.SPECULATIVE &&
234 dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase).hasTag(NONE)));
235 JCTree speculativeTree = attribSpeculative(dt.tree, dt.env, resultInfo);
236 dt.speculativeCache.put(deferredAttrContext.msym, speculativeTree, deferredAttrContext.phase);
237 return speculativeTree.type;
238 case CHECK:
239 Assert.check(dt.mode != null);
240 return attr.attribTree(dt.tree, dt.env, resultInfo);
241 }
242 Assert.error();
243 return null;
244 }
245 };
247 DeferredTypeCompleter dummyCompleter = new DeferredTypeCompleter() {
248 public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
249 Assert.check(deferredAttrContext.mode == AttrMode.CHECK);
250 return dt.tree.type = Type.noType;
251 }
252 };
254 /**
255 * The 'mode' in which the deferred type is to be type-checked
256 */
257 public enum AttrMode {
258 /**
259 * A speculative type-checking round is used during overload resolution
260 * mainly to generate constraints on inference variables. Side-effects
261 * arising from type-checking the expression associated with the deferred
262 * type are reversed after the speculative round finishes. This means the
263 * expression tree will be left in a blank state.
264 */
265 SPECULATIVE,
266 /**
267 * This is the plain type-checking mode. Produces side-effects on the underlying AST node
268 */
269 CHECK;
270 }
272 /**
273 * Routine that performs speculative type-checking; the input AST node is
274 * cloned (to avoid side-effects cause by Attr) and compiler state is
275 * restored after type-checking. All diagnostics (but critical ones) are
276 * disabled during speculative type-checking.
277 */
278 JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
279 final JCTree newTree = new TreeCopier<Object>(make).copy(tree);
280 Env<AttrContext> speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared()));
281 speculativeEnv.info.scope.owner = env.info.scope.owner;
282 Log.DeferredDiagnosticHandler deferredDiagnosticHandler =
283 new Log.DeferredDiagnosticHandler(log, new Filter<JCDiagnostic>() {
284 public boolean accepts(final JCDiagnostic d) {
285 class PosScanner extends TreeScanner {
286 boolean found = false;
288 @Override
289 public void scan(JCTree tree) {
290 if (tree != null &&
291 tree.pos() == d.getDiagnosticPosition()) {
292 found = true;
293 }
294 super.scan(tree);
295 }
296 };
297 PosScanner posScanner = new PosScanner();
298 posScanner.scan(newTree);
299 return posScanner.found;
300 }
301 });
302 try {
303 attr.attribTree(newTree, speculativeEnv, resultInfo);
304 unenterScanner.scan(newTree);
305 return newTree;
306 } catch (Abort ex) {
307 //if some very bad condition occurred during deferred attribution
308 //we should dump all errors before killing javac
309 deferredDiagnosticHandler.reportDeferredDiagnostics();
310 throw ex;
311 } finally {
312 unenterScanner.scan(newTree);
313 log.popDiagnosticHandler(deferredDiagnosticHandler);
314 }
315 }
316 //where
317 protected TreeScanner unenterScanner = new TreeScanner() {
318 @Override
319 public void visitClassDef(JCClassDecl tree) {
320 ClassSymbol csym = tree.sym;
321 //if something went wrong during method applicability check
322 //it is possible that nested expressions inside argument expression
323 //are left unchecked - in such cases there's nothing to clean up.
324 if (csym == null) return;
325 enter.typeEnvs.remove(csym);
326 chk.compiled.remove(csym.flatname);
327 syms.classes.remove(csym.flatname);
328 super.visitClassDef(tree);
329 }
330 };
332 /**
333 * A deferred context is created on each method check. A deferred context is
334 * used to keep track of information associated with the method check, such as
335 * the symbol of the method being checked, the overload resolution phase,
336 * the kind of attribution mode to be applied to deferred types and so forth.
337 * As deferred types are processed (by the method check routine) stuck AST nodes
338 * are added (as new deferred attribution nodes) to this context. The complete()
339 * routine makes sure that all pending nodes are properly processed, by
340 * progressively instantiating all inference variables on which one or more
341 * deferred attribution node is stuck.
342 */
343 class DeferredAttrContext {
345 /** attribution mode */
346 final AttrMode mode;
348 /** symbol of the method being checked */
349 final Symbol msym;
351 /** method resolution step */
352 final Resolve.MethodResolutionPhase phase;
354 /** inference context */
355 final InferenceContext inferenceContext;
357 /** parent deferred context */
358 final DeferredAttrContext parent;
360 /** Warner object to report warnings */
361 final Warner warn;
363 /** list of deferred attribution nodes to be processed */
364 ArrayList<DeferredAttrNode> deferredAttrNodes = new ArrayList<DeferredAttrNode>();
366 DeferredAttrContext(AttrMode mode, Symbol msym, MethodResolutionPhase phase,
367 InferenceContext inferenceContext, DeferredAttrContext parent, Warner warn) {
368 this.mode = mode;
369 this.msym = msym;
370 this.phase = phase;
371 this.parent = parent;
372 this.warn = warn;
373 this.inferenceContext = inferenceContext;
374 }
376 /**
377 * Adds a node to the list of deferred attribution nodes - used by Resolve.rawCheckArgumentsApplicable
378 * Nodes added this way act as 'roots' for the out-of-order method checking process.
379 */
380 void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
381 deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, stuckVars));
382 }
384 /**
385 * Incrementally process all nodes, by skipping 'stuck' nodes and attributing
386 * 'unstuck' ones. If at any point no progress can be made (no 'unstuck' nodes)
387 * some inference variable might get eagerly instantiated so that all nodes
388 * can be type-checked.
389 */
390 void complete() {
391 while (!deferredAttrNodes.isEmpty()) {
392 Set<Type> stuckVars = new LinkedHashSet<Type>();
393 boolean progress = false;
394 //scan a defensive copy of the node list - this is because a deferred
395 //attribution round can add new nodes to the list
396 for (DeferredAttrNode deferredAttrNode : List.from(deferredAttrNodes)) {
397 if (!deferredAttrNode.process(this)) {
398 stuckVars.addAll(deferredAttrNode.stuckVars);
399 } else {
400 deferredAttrNodes.remove(deferredAttrNode);
401 progress = true;
402 }
403 }
404 if (!progress) {
405 //remove all variables that have already been instantiated
406 //from the list of stuck variables
407 inferenceContext.solveAny(List.from(stuckVars), warn);
408 inferenceContext.notifyChange();
409 }
410 }
411 }
412 }
414 /**
415 * Class representing a deferred attribution node. It keeps track of
416 * a deferred type, along with the expected target type information.
417 */
418 class DeferredAttrNode implements Infer.FreeTypeListener {
420 /** underlying deferred type */
421 DeferredType dt;
423 /** underlying target type information */
424 ResultInfo resultInfo;
426 /** list of uninferred inference variables causing this node to be stuck */
427 List<Type> stuckVars;
429 DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
430 this.dt = dt;
431 this.resultInfo = resultInfo;
432 this.stuckVars = stuckVars;
433 if (!stuckVars.isEmpty()) {
434 resultInfo.checkContext.inferenceContext().addFreeTypeListener(stuckVars, this);
435 }
436 }
438 @Override
439 public void typesInferred(InferenceContext inferenceContext) {
440 stuckVars = List.nil();
441 resultInfo = resultInfo.dup(inferenceContext.asInstType(resultInfo.pt));
442 }
444 /**
445 * Process a deferred attribution node.
446 * Invariant: a stuck node cannot be processed.
447 */
448 @SuppressWarnings("fallthrough")
449 boolean process(DeferredAttrContext deferredAttrContext) {
450 switch (deferredAttrContext.mode) {
451 case SPECULATIVE:
452 dt.check(resultInfo, List.<Type>nil(), new StructuralStuckChecker());
453 return true;
454 case CHECK:
455 if (stuckVars.nonEmpty()) {
456 //stuck expression - see if we can propagate
457 if (deferredAttrContext.parent != emptyDeferredAttrContext &&
458 Type.containsAny(deferredAttrContext.parent.inferenceContext.inferencevars, List.from(stuckVars))) {
459 deferredAttrContext.parent.deferredAttrNodes.add(this);
460 dt.check(resultInfo, List.<Type>nil(), dummyCompleter);
461 return true;
462 } else {
463 return false;
464 }
465 } else {
466 dt.check(resultInfo, stuckVars, basicCompleter);
467 return true;
468 }
469 default:
470 throw new AssertionError("Bad mode");
471 }
472 }
474 /**
475 * Structural checker for stuck expressions
476 */
477 class StructuralStuckChecker extends TreeScanner implements DeferredTypeCompleter {
479 ResultInfo resultInfo;
480 InferenceContext inferenceContext;
481 Env<AttrContext> env;
483 public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
484 this.resultInfo = resultInfo;
485 this.inferenceContext = deferredAttrContext.inferenceContext;
486 this.env = dt.env.dup(dt.tree, dt.env.info.dup());
487 dt.tree.accept(this);
488 dt.speculativeCache.put(deferredAttrContext.msym, stuckTree, deferredAttrContext.phase);
489 return Type.noType;
490 }
492 @Override
493 public void visitLambda(JCLambda tree) {
494 Check.CheckContext checkContext = resultInfo.checkContext;
495 Type pt = resultInfo.pt;
496 if (inferenceContext.inferencevars.contains(pt)) {
497 //ok
498 return;
499 } else {
500 //must be a functional descriptor
501 try {
502 Type desc = types.findDescriptorType(pt);
503 if (desc.getParameterTypes().length() != tree.params.length()) {
504 checkContext.report(tree, diags.fragment("incompatible.arg.types.in.lambda"));
505 }
506 } catch (Types.FunctionDescriptorLookupError ex) {
507 checkContext.report(null, ex.getDiagnostic());
508 }
509 }
510 }
512 @Override
513 public void visitNewClass(JCNewClass tree) {
514 //do nothing
515 }
517 @Override
518 public void visitApply(JCMethodInvocation tree) {
519 //do nothing
520 }
522 @Override
523 public void visitReference(JCMemberReference tree) {
524 Check.CheckContext checkContext = resultInfo.checkContext;
525 Type pt = resultInfo.pt;
526 if (inferenceContext.inferencevars.contains(pt)) {
527 //ok
528 return;
529 } else {
530 try {
531 types.findDescriptorType(pt);
532 } catch (Types.FunctionDescriptorLookupError ex) {
533 checkContext.report(null, ex.getDiagnostic());
534 }
535 JCExpression exprTree = (JCExpression)attribSpeculative(tree.getQualifierExpression(), env,
536 attr.memberReferenceQualifierResult(tree));
537 ListBuffer<Type> argtypes = ListBuffer.lb();
538 for (Type t : types.findDescriptorType(pt).getParameterTypes()) {
539 argtypes.append(syms.errType);
540 }
541 JCMemberReference mref2 = new TreeCopier<Void>(make).copy(tree);
542 mref2.expr = exprTree;
543 Pair<Symbol, ?> lookupRes =
544 rs.resolveMemberReference(tree, env, mref2, exprTree.type, tree.name, argtypes.toList(), null, true);
545 switch (lookupRes.fst.kind) {
546 //note: as argtypes are erroneous types, type-errors must
547 //have been caused by arity mismatch
548 case Kinds.ABSENT_MTH:
549 case Kinds.WRONG_MTH:
550 case Kinds.WRONG_MTHS:
551 case Kinds.STATICERR:
552 case Kinds.MISSING_ENCL:
553 checkContext.report(null, diags.fragment("incompatible.arg.types.in.mref"));
554 }
555 }
556 }
557 }
558 }
560 /** an empty deferred attribution context - all methods throw exceptions */
561 final DeferredAttrContext emptyDeferredAttrContext =
562 new DeferredAttrContext(AttrMode.CHECK, null, MethodResolutionPhase.BOX, null, null, null) {
563 @Override
564 void addDeferredAttrNode(DeferredType dt, ResultInfo ri, List<Type> stuckVars) {
565 Assert.error("Empty deferred context!");
566 }
567 @Override
568 void complete() {
569 Assert.error("Empty deferred context!");
570 }
571 };
573 /**
574 * Map a list of types possibly containing one or more deferred types
575 * into a list of ordinary types. Each deferred type D is mapped into a type T,
576 * where T is computed by retrieving the type that has already been
577 * computed for D during a previous deferred attribution round of the given kind.
578 */
579 class DeferredTypeMap extends Type.Mapping {
581 DeferredAttrContext deferredAttrContext;
583 protected DeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
584 super(String.format("deferredTypeMap[%s]", mode));
585 this.deferredAttrContext = new DeferredAttrContext(mode, msym, phase,
586 infer.emptyContext, emptyDeferredAttrContext, types.noWarnings);
587 }
589 protected boolean validState(DeferredType dt) {
590 return dt.mode != null &&
591 deferredAttrContext.mode.ordinal() <= dt.mode.ordinal();
592 }
594 @Override
595 public Type apply(Type t) {
596 if (!t.hasTag(DEFERRED)) {
597 return t.map(this);
598 } else {
599 DeferredType dt = (DeferredType)t;
600 Assert.check(validState(dt));
601 return typeOf(dt);
602 }
603 }
605 protected Type typeOf(DeferredType dt) {
606 switch (deferredAttrContext.mode) {
607 case CHECK:
608 return dt.tree.type == null ? Type.noType : dt.tree.type;
609 case SPECULATIVE:
610 return dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase);
611 }
612 Assert.error();
613 return null;
614 }
615 }
617 /**
618 * Specialized recovery deferred mapping.
619 * Each deferred type D is mapped into a type T, where T is computed either by
620 * (i) retrieving the type that has already been computed for D during a previous
621 * attribution round (as before), or (ii) by synthesizing a new type R for D
622 * (the latter step is useful in a recovery scenario).
623 */
624 public class RecoveryDeferredTypeMap extends DeferredTypeMap {
626 public RecoveryDeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
627 super(mode, msym, phase != null ? phase : MethodResolutionPhase.BOX);
628 }
630 @Override
631 protected Type typeOf(DeferredType dt) {
632 Type owntype = super.typeOf(dt);
633 return owntype == Type.noType ?
634 recover(dt) : owntype;
635 }
637 @Override
638 protected boolean validState(DeferredType dt) {
639 return true;
640 }
642 /**
643 * Synthesize a type for a deferred type that hasn't been previously
644 * reduced to an ordinary type. Functional deferred types and conditionals
645 * are mapped to themselves, in order to have a richer diagnostic
646 * representation. Remaining deferred types are attributed using
647 * a default expected type (j.l.Object).
648 */
649 private Type recover(DeferredType dt) {
650 dt.check(attr.new RecoveryInfo(deferredAttrContext));
651 return super.apply(dt);
652 }
653 }
655 /**
656 * Retrieves the list of inference variables that need to be inferred before
657 * an AST node can be type-checked
658 */
659 @SuppressWarnings("fallthrough")
660 List<Type> stuckVars(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
661 if (resultInfo.pt.hasTag(NONE) || resultInfo.pt.isErroneous()) {
662 return List.nil();
663 } else {
664 return stuckVarsInternal(tree, resultInfo.pt, resultInfo.checkContext.inferenceContext());
665 }
666 }
667 //where
668 private List<Type> stuckVarsInternal(JCTree tree, Type pt, Infer.InferenceContext inferenceContext) {
669 StuckChecker sc = new StuckChecker(pt, inferenceContext);
670 sc.scan(tree);
671 return List.from(sc.stuckVars);
672 }
674 /**
675 * A special tree scanner that would only visit portions of a given tree.
676 * The set of nodes visited by the scanner can be customized at construction-time.
677 */
678 abstract static class FilterScanner extends TreeScanner {
680 final Filter<JCTree> treeFilter;
682 FilterScanner(final Set<JCTree.Tag> validTags) {
683 this.treeFilter = new Filter<JCTree>() {
684 public boolean accepts(JCTree t) {
685 return validTags.contains(t.getTag());
686 }
687 };
688 }
690 @Override
691 public void scan(JCTree tree) {
692 if (tree != null) {
693 if (treeFilter.accepts(tree)) {
694 super.scan(tree);
695 } else {
696 skip(tree);
697 }
698 }
699 }
701 /**
702 * handler that is executed when a node has been discarded
703 */
704 abstract void skip(JCTree tree);
705 }
707 /**
708 * A tree scanner suitable for visiting the target-type dependent nodes of
709 * a given argument expression.
710 */
711 static class PolyScanner extends FilterScanner {
713 PolyScanner() {
714 super(EnumSet.of(CONDEXPR, PARENS, LAMBDA, REFERENCE));
715 }
717 @Override
718 void skip(JCTree tree) {
719 //do nothing
720 }
721 }
723 /**
724 * A tree scanner suitable for visiting the target-type dependent nodes nested
725 * within a lambda expression body.
726 */
727 static class LambdaReturnScanner extends FilterScanner {
729 LambdaReturnScanner() {
730 super(EnumSet.of(BLOCK, CASE, CATCH, DOLOOP, FOREACHLOOP,
731 FORLOOP, RETURN, SYNCHRONIZED, SWITCH, TRY, WHILELOOP));
732 }
734 @Override
735 void skip(JCTree tree) {
736 //do nothing
737 }
738 }
740 /**
741 * This visitor is used to check that structural expressions conform
742 * to their target - this step is required as inference could end up
743 * inferring types that make some of the nested expressions incompatible
744 * with their corresponding instantiated target
745 */
746 class StuckChecker extends PolyScanner {
748 Type pt;
749 Infer.InferenceContext inferenceContext;
750 Set<Type> stuckVars = new LinkedHashSet<Type>();
752 StuckChecker(Type pt, Infer.InferenceContext inferenceContext) {
753 this.pt = pt;
754 this.inferenceContext = inferenceContext;
755 }
757 @Override
758 public void visitLambda(JCLambda tree) {
759 if (inferenceContext.inferenceVars().contains(pt)) {
760 stuckVars.add(pt);
761 }
762 if (!types.isFunctionalInterface(pt)) {
763 return;
764 }
765 Type descType = types.findDescriptorType(pt);
766 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
767 if (tree.paramKind == JCLambda.ParameterKind.IMPLICIT &&
768 freeArgVars.nonEmpty()) {
769 stuckVars.addAll(freeArgVars);
770 }
771 scanLambdaBody(tree, descType.getReturnType());
772 }
774 @Override
775 public void visitReference(JCMemberReference tree) {
776 scan(tree.expr);
777 if (inferenceContext.inferenceVars().contains(pt)) {
778 stuckVars.add(pt);
779 return;
780 }
781 if (!types.isFunctionalInterface(pt)) {
782 return;
783 }
785 Type descType = types.findDescriptorType(pt);
786 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
787 stuckVars.addAll(freeArgVars);
788 }
790 void scanLambdaBody(JCLambda lambda, final Type pt) {
791 if (lambda.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) {
792 stuckVars.addAll(stuckVarsInternal(lambda.body, pt, inferenceContext));
793 } else {
794 LambdaReturnScanner lambdaScanner = new LambdaReturnScanner() {
795 @Override
796 public void visitReturn(JCReturn tree) {
797 if (tree.expr != null) {
798 stuckVars.addAll(stuckVarsInternal(tree.expr, pt, inferenceContext));
799 }
800 }
801 };
802 lambdaScanner.scan(lambda.body);
803 }
804 }
805 }
806 }