Wed, 13 Feb 2013 17:04:21 +0000
8006345: Report Synthesized Parameters in java.lang.reflect.Parameter API
8006896: ClassReader doesn't see MethodParameters attr for method of anon inner class
8007098: Output Synthesized Parameters to MethodParameters Attributes
Summary: Correctly report synthesized and mandated parameters
Reviewed-by: mcimadamore, jjg
Contributed-by: eric.mccorkle@oracle.com
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 Log log;
72 final Symtab syms;
73 final TreeMaker make;
74 final Types types;
76 public static DeferredAttr instance(Context context) {
77 DeferredAttr instance = context.get(deferredAttrKey);
78 if (instance == null)
79 instance = new DeferredAttr(context);
80 return instance;
81 }
83 protected DeferredAttr(Context context) {
84 context.put(deferredAttrKey, this);
85 attr = Attr.instance(context);
86 chk = Check.instance(context);
87 diags = JCDiagnostic.Factory.instance(context);
88 enter = Enter.instance(context);
89 infer = Infer.instance(context);
90 log = Log.instance(context);
91 syms = Symtab.instance(context);
92 make = TreeMaker.instance(context);
93 types = Types.instance(context);
94 Names names = Names.instance(context);
95 stuckTree = make.Ident(names.empty).setType(Type.noType);
96 }
98 /** shared tree for stuck expressions */
99 final JCTree stuckTree;
101 /**
102 * This type represents a deferred type. A deferred type starts off with
103 * no information on the underlying expression type. Such info needs to be
104 * discovered through type-checking the deferred type against a target-type.
105 * Every deferred type keeps a pointer to the AST node from which it originated.
106 */
107 public class DeferredType extends Type {
109 public JCExpression tree;
110 Env<AttrContext> env;
111 AttrMode mode;
112 SpeculativeCache speculativeCache;
114 DeferredType(JCExpression tree, Env<AttrContext> env) {
115 super(DEFERRED, null);
116 this.tree = tree;
117 this.env = env.dup(tree, env.info.dup());
118 this.speculativeCache = new SpeculativeCache();
119 }
121 /**
122 * A speculative cache is used to keep track of all overload resolution rounds
123 * that triggered speculative attribution on a given deferred type. Each entry
124 * stores a pointer to the speculative tree and the resolution phase in which the entry
125 * has been added.
126 */
127 class SpeculativeCache {
129 private Map<Symbol, List<Entry>> cache =
130 new WeakHashMap<Symbol, List<Entry>>();
132 class Entry {
133 JCTree speculativeTree;
134 Resolve.MethodResolutionPhase phase;
136 public Entry(JCTree speculativeTree, MethodResolutionPhase phase) {
137 this.speculativeTree = speculativeTree;
138 this.phase = phase;
139 }
141 boolean matches(Resolve.MethodResolutionPhase phase) {
142 return this.phase == phase;
143 }
144 }
146 /**
147 * Retrieve a speculative cache entry corresponding to given symbol
148 * and resolution phase
149 */
150 Entry get(Symbol msym, MethodResolutionPhase phase) {
151 List<Entry> entries = cache.get(msym);
152 if (entries == null) return null;
153 for (Entry e : entries) {
154 if (e.matches(phase)) return e;
155 }
156 return null;
157 }
159 /**
160 * Stores a speculative cache entry corresponding to given symbol
161 * and resolution phase
162 */
163 void put(Symbol msym, JCTree speculativeTree, MethodResolutionPhase phase) {
164 List<Entry> entries = cache.get(msym);
165 if (entries == null) {
166 entries = List.nil();
167 }
168 cache.put(msym, entries.prepend(new Entry(speculativeTree, phase)));
169 }
170 }
172 /**
173 * Get the type that has been computed during a speculative attribution round
174 */
175 Type speculativeType(Symbol msym, MethodResolutionPhase phase) {
176 SpeculativeCache.Entry e = speculativeCache.get(msym, phase);
177 return e != null ? e.speculativeTree.type : Type.noType;
178 }
180 /**
181 * Check a deferred type against a potential target-type. Depending on
182 * the current attribution mode, a normal vs. speculative attribution
183 * round is performed on the underlying AST node. There can be only one
184 * speculative round for a given target method symbol; moreover, a normal
185 * attribution round must follow one or more speculative rounds.
186 */
187 Type check(ResultInfo resultInfo) {
188 return check(resultInfo, stuckVars(tree, env, resultInfo), basicCompleter);
189 }
191 Type check(ResultInfo resultInfo, List<Type> stuckVars, DeferredTypeCompleter deferredTypeCompleter) {
192 DeferredAttrContext deferredAttrContext =
193 resultInfo.checkContext.deferredAttrContext();
194 Assert.check(deferredAttrContext != emptyDeferredAttrContext);
195 if (stuckVars.nonEmpty()) {
196 deferredAttrContext.addDeferredAttrNode(this, resultInfo, stuckVars);
197 return Type.noType;
198 } else {
199 try {
200 return deferredTypeCompleter.complete(this, resultInfo, deferredAttrContext);
201 } finally {
202 mode = deferredAttrContext.mode;
203 }
204 }
205 }
206 }
208 /**
209 * A completer for deferred types. Defines an entry point for type-checking
210 * a deferred type.
211 */
212 interface DeferredTypeCompleter {
213 /**
214 * Entry point for type-checking a deferred type. Depending on the
215 * circumstances, type-checking could amount to full attribution
216 * or partial structural check (aka potential applicability).
217 */
218 Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext);
219 }
221 /**
222 * A basic completer for deferred types. This completer type-checks a deferred type
223 * using attribution; depending on the attribution mode, this could be either standard
224 * or speculative attribution.
225 */
226 DeferredTypeCompleter basicCompleter = new DeferredTypeCompleter() {
227 public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
228 switch (deferredAttrContext.mode) {
229 case SPECULATIVE:
230 Assert.check(dt.mode == null ||
231 (dt.mode == AttrMode.SPECULATIVE &&
232 dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase).hasTag(NONE)));
233 JCTree speculativeTree = attribSpeculative(dt.tree, dt.env, resultInfo);
234 dt.speculativeCache.put(deferredAttrContext.msym, speculativeTree, deferredAttrContext.phase);
235 return speculativeTree.type;
236 case CHECK:
237 Assert.check(dt.mode != null);
238 return attr.attribTree(dt.tree, dt.env, resultInfo);
239 }
240 Assert.error();
241 return null;
242 }
243 };
245 DeferredTypeCompleter dummyCompleter = new DeferredTypeCompleter() {
246 public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
247 Assert.check(deferredAttrContext.mode == AttrMode.CHECK);
248 return dt.tree.type = Type.noType;
249 }
250 };
252 /**
253 * The 'mode' in which the deferred type is to be type-checked
254 */
255 public enum AttrMode {
256 /**
257 * A speculative type-checking round is used during overload resolution
258 * mainly to generate constraints on inference variables. Side-effects
259 * arising from type-checking the expression associated with the deferred
260 * type are reversed after the speculative round finishes. This means the
261 * expression tree will be left in a blank state.
262 */
263 SPECULATIVE,
264 /**
265 * This is the plain type-checking mode. Produces side-effects on the underlying AST node
266 */
267 CHECK;
268 }
270 /**
271 * Routine that performs speculative type-checking; the input AST node is
272 * cloned (to avoid side-effects cause by Attr) and compiler state is
273 * restored after type-checking. All diagnostics (but critical ones) are
274 * disabled during speculative type-checking.
275 */
276 JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
277 JCTree newTree = new TreeCopier<Object>(make).copy(tree);
278 Env<AttrContext> speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared()));
279 speculativeEnv.info.scope.owner = env.info.scope.owner;
280 final JavaFileObject currentSource = log.currentSourceFile();
281 Log.DeferredDiagnosticHandler deferredDiagnosticHandler =
282 new Log.DeferredDiagnosticHandler(log, new Filter<JCDiagnostic>() {
283 public boolean accepts(JCDiagnostic t) {
284 return t.getDiagnosticSource().getFile().equals(currentSource);
285 }
286 });
287 try {
288 attr.attribTree(newTree, speculativeEnv, resultInfo);
289 unenterScanner.scan(newTree);
290 return newTree;
291 } catch (Abort ex) {
292 //if some very bad condition occurred during deferred attribution
293 //we should dump all errors before killing javac
294 deferredDiagnosticHandler.reportDeferredDiagnostics();
295 throw ex;
296 } finally {
297 unenterScanner.scan(newTree);
298 log.popDiagnosticHandler(deferredDiagnosticHandler);
299 }
300 }
301 //where
302 protected TreeScanner unenterScanner = new TreeScanner() {
303 @Override
304 public void visitClassDef(JCClassDecl tree) {
305 ClassSymbol csym = tree.sym;
306 //if something went wrong during method applicability check
307 //it is possible that nested expressions inside argument expression
308 //are left unchecked - in such cases there's nothing to clean up.
309 if (csym == null) return;
310 enter.typeEnvs.remove(csym);
311 chk.compiled.remove(csym.flatname);
312 syms.classes.remove(csym.flatname);
313 super.visitClassDef(tree);
314 }
315 };
317 /**
318 * A deferred context is created on each method check. A deferred context is
319 * used to keep track of information associated with the method check, such as
320 * the symbol of the method being checked, the overload resolution phase,
321 * the kind of attribution mode to be applied to deferred types and so forth.
322 * As deferred types are processed (by the method check routine) stuck AST nodes
323 * are added (as new deferred attribution nodes) to this context. The complete()
324 * routine makes sure that all pending nodes are properly processed, by
325 * progressively instantiating all inference variables on which one or more
326 * deferred attribution node is stuck.
327 */
328 class DeferredAttrContext {
330 /** attribution mode */
331 final AttrMode mode;
333 /** symbol of the method being checked */
334 final Symbol msym;
336 /** method resolution step */
337 final Resolve.MethodResolutionPhase phase;
339 /** inference context */
340 final InferenceContext inferenceContext;
342 /** parent deferred context */
343 final DeferredAttrContext parent;
345 /** Warner object to report warnings */
346 final Warner warn;
348 /** list of deferred attribution nodes to be processed */
349 ArrayList<DeferredAttrNode> deferredAttrNodes = new ArrayList<DeferredAttrNode>();
351 DeferredAttrContext(AttrMode mode, Symbol msym, MethodResolutionPhase phase,
352 InferenceContext inferenceContext, DeferredAttrContext parent, Warner warn) {
353 this.mode = mode;
354 this.msym = msym;
355 this.phase = phase;
356 this.parent = parent;
357 this.warn = warn;
358 this.inferenceContext = inferenceContext;
359 }
361 /**
362 * Adds a node to the list of deferred attribution nodes - used by Resolve.rawCheckArgumentsApplicable
363 * Nodes added this way act as 'roots' for the out-of-order method checking process.
364 */
365 void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
366 deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, stuckVars));
367 }
369 /**
370 * Incrementally process all nodes, by skipping 'stuck' nodes and attributing
371 * 'unstuck' ones. If at any point no progress can be made (no 'unstuck' nodes)
372 * some inference variable might get eagerly instantiated so that all nodes
373 * can be type-checked.
374 */
375 void complete() {
376 while (!deferredAttrNodes.isEmpty()) {
377 Set<Type> stuckVars = new LinkedHashSet<Type>();
378 boolean progress = false;
379 //scan a defensive copy of the node list - this is because a deferred
380 //attribution round can add new nodes to the list
381 for (DeferredAttrNode deferredAttrNode : List.from(deferredAttrNodes)) {
382 if (!deferredAttrNode.process(this)) {
383 stuckVars.addAll(deferredAttrNode.stuckVars);
384 } else {
385 deferredAttrNodes.remove(deferredAttrNode);
386 progress = true;
387 }
388 }
389 if (!progress) {
390 //remove all variables that have already been instantiated
391 //from the list of stuck variables
392 inferenceContext.solveAny(List.from(stuckVars), warn);
393 inferenceContext.notifyChange();
394 }
395 }
396 }
397 }
399 /**
400 * Class representing a deferred attribution node. It keeps track of
401 * a deferred type, along with the expected target type information.
402 */
403 class DeferredAttrNode implements Infer.FreeTypeListener {
405 /** underlying deferred type */
406 DeferredType dt;
408 /** underlying target type information */
409 ResultInfo resultInfo;
411 /** list of uninferred inference variables causing this node to be stuck */
412 List<Type> stuckVars;
414 DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
415 this.dt = dt;
416 this.resultInfo = resultInfo;
417 this.stuckVars = stuckVars;
418 if (!stuckVars.isEmpty()) {
419 resultInfo.checkContext.inferenceContext().addFreeTypeListener(stuckVars, this);
420 }
421 }
423 @Override
424 public void typesInferred(InferenceContext inferenceContext) {
425 stuckVars = List.nil();
426 resultInfo = resultInfo.dup(inferenceContext.asInstType(resultInfo.pt));
427 }
429 /**
430 * Process a deferred attribution node.
431 * Invariant: a stuck node cannot be processed.
432 */
433 @SuppressWarnings("fallthrough")
434 boolean process(DeferredAttrContext deferredAttrContext) {
435 switch (deferredAttrContext.mode) {
436 case SPECULATIVE:
437 dt.check(resultInfo, List.<Type>nil(), new StructuralStuckChecker());
438 return true;
439 case CHECK:
440 if (stuckVars.nonEmpty()) {
441 //stuck expression - see if we can propagate
442 if (deferredAttrContext.parent != emptyDeferredAttrContext &&
443 Type.containsAny(deferredAttrContext.parent.inferenceContext.inferencevars, List.from(stuckVars))) {
444 deferredAttrContext.parent.deferredAttrNodes.add(this);
445 dt.check(resultInfo, List.<Type>nil(), dummyCompleter);
446 return true;
447 } else {
448 return false;
449 }
450 } else {
451 dt.check(resultInfo, stuckVars, basicCompleter);
452 return true;
453 }
454 default:
455 throw new AssertionError("Bad mode");
456 }
457 }
459 /**
460 * Structural checker for stuck expressions
461 */
462 class StructuralStuckChecker extends TreeScanner implements DeferredTypeCompleter {
464 ResultInfo resultInfo;
465 InferenceContext inferenceContext;
467 public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
468 this.resultInfo = resultInfo;
469 this.inferenceContext = deferredAttrContext.inferenceContext;
470 dt.tree.accept(this);
471 dt.speculativeCache.put(deferredAttrContext.msym, stuckTree, deferredAttrContext.phase);
472 return Type.noType;
473 }
475 @Override
476 public void visitLambda(JCLambda tree) {
477 Check.CheckContext checkContext = resultInfo.checkContext;
478 Type pt = resultInfo.pt;
479 if (inferenceContext.inferencevars.contains(pt)) {
480 //ok
481 return;
482 } else {
483 //must be a functional descriptor
484 try {
485 Type desc = types.findDescriptorType(pt);
486 if (desc.getParameterTypes().length() != tree.params.length()) {
487 checkContext.report(tree, diags.fragment("incompatible.arg.types.in.lambda"));
488 }
489 } catch (Types.FunctionDescriptorLookupError ex) {
490 checkContext.report(null, ex.getDiagnostic());
491 }
492 }
493 }
495 @Override
496 public void visitNewClass(JCNewClass tree) {
497 //do nothing
498 }
500 @Override
501 public void visitApply(JCMethodInvocation tree) {
502 //do nothing
503 }
505 @Override
506 public void visitReference(JCMemberReference tree) {
507 Check.CheckContext checkContext = resultInfo.checkContext;
508 Type pt = resultInfo.pt;
509 if (inferenceContext.inferencevars.contains(pt)) {
510 //ok
511 return;
512 } else {
513 try {
514 //TODO: we should speculative determine if there's a match
515 //based on arity - if yes, method is applicable.
516 types.findDescriptorType(pt);
517 } catch (Types.FunctionDescriptorLookupError ex) {
518 checkContext.report(null, ex.getDiagnostic());
519 }
520 }
521 }
522 }
523 }
525 /** an empty deferred attribution context - all methods throw exceptions */
526 final DeferredAttrContext emptyDeferredAttrContext =
527 new DeferredAttrContext(AttrMode.CHECK, null, MethodResolutionPhase.BOX, null, null, null) {
528 @Override
529 void addDeferredAttrNode(DeferredType dt, ResultInfo ri, List<Type> stuckVars) {
530 Assert.error("Empty deferred context!");
531 }
532 @Override
533 void complete() {
534 Assert.error("Empty deferred context!");
535 }
536 };
538 /**
539 * Map a list of types possibly containing one or more deferred types
540 * into a list of ordinary types. Each deferred type D is mapped into a type T,
541 * where T is computed by retrieving the type that has already been
542 * computed for D during a previous deferred attribution round of the given kind.
543 */
544 class DeferredTypeMap extends Type.Mapping {
546 DeferredAttrContext deferredAttrContext;
548 protected DeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
549 super(String.format("deferredTypeMap[%s]", mode));
550 this.deferredAttrContext = new DeferredAttrContext(mode, msym, phase,
551 infer.emptyContext, emptyDeferredAttrContext, types.noWarnings);
552 }
554 protected boolean validState(DeferredType dt) {
555 return dt.mode != null &&
556 deferredAttrContext.mode.ordinal() <= dt.mode.ordinal();
557 }
559 @Override
560 public Type apply(Type t) {
561 if (!t.hasTag(DEFERRED)) {
562 return t.map(this);
563 } else {
564 DeferredType dt = (DeferredType)t;
565 Assert.check(validState(dt));
566 return typeOf(dt);
567 }
568 }
570 protected Type typeOf(DeferredType dt) {
571 switch (deferredAttrContext.mode) {
572 case CHECK:
573 return dt.tree.type == null ? Type.noType : dt.tree.type;
574 case SPECULATIVE:
575 return dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase);
576 }
577 Assert.error();
578 return null;
579 }
580 }
582 /**
583 * Specialized recovery deferred mapping.
584 * Each deferred type D is mapped into a type T, where T is computed either by
585 * (i) retrieving the type that has already been computed for D during a previous
586 * attribution round (as before), or (ii) by synthesizing a new type R for D
587 * (the latter step is useful in a recovery scenario).
588 */
589 public class RecoveryDeferredTypeMap extends DeferredTypeMap {
591 public RecoveryDeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
592 super(mode, msym, phase != null ? phase : MethodResolutionPhase.BOX);
593 }
595 @Override
596 protected Type typeOf(DeferredType dt) {
597 Type owntype = super.typeOf(dt);
598 return owntype == Type.noType ?
599 recover(dt) : owntype;
600 }
602 @Override
603 protected boolean validState(DeferredType dt) {
604 return true;
605 }
607 /**
608 * Synthesize a type for a deferred type that hasn't been previously
609 * reduced to an ordinary type. Functional deferred types and conditionals
610 * are mapped to themselves, in order to have a richer diagnostic
611 * representation. Remaining deferred types are attributed using
612 * a default expected type (j.l.Object).
613 */
614 private Type recover(DeferredType dt) {
615 dt.check(attr.new RecoveryInfo(deferredAttrContext));
616 return super.apply(dt);
617 }
618 }
620 /**
621 * Retrieves the list of inference variables that need to be inferred before
622 * an AST node can be type-checked
623 */
624 @SuppressWarnings("fallthrough")
625 List<Type> stuckVars(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
626 if (resultInfo.pt.hasTag(NONE) || resultInfo.pt.isErroneous()) {
627 return List.nil();
628 } else {
629 return stuckVarsInternal(tree, resultInfo.pt, resultInfo.checkContext.inferenceContext());
630 }
631 }
632 //where
633 private List<Type> stuckVarsInternal(JCTree tree, Type pt, Infer.InferenceContext inferenceContext) {
634 StuckChecker sc = new StuckChecker(pt, inferenceContext);
635 sc.scan(tree);
636 return List.from(sc.stuckVars);
637 }
639 /**
640 * A special tree scanner that would only visit portions of a given tree.
641 * The set of nodes visited by the scanner can be customized at construction-time.
642 */
643 abstract static class FilterScanner extends TreeScanner {
645 final Filter<JCTree> treeFilter;
647 FilterScanner(final Set<JCTree.Tag> validTags) {
648 this.treeFilter = new Filter<JCTree>() {
649 public boolean accepts(JCTree t) {
650 return validTags.contains(t.getTag());
651 }
652 };
653 }
655 @Override
656 public void scan(JCTree tree) {
657 if (tree != null) {
658 if (treeFilter.accepts(tree)) {
659 super.scan(tree);
660 } else {
661 skip(tree);
662 }
663 }
664 }
666 /**
667 * handler that is executed when a node has been discarded
668 */
669 abstract void skip(JCTree tree);
670 }
672 /**
673 * A tree scanner suitable for visiting the target-type dependent nodes of
674 * a given argument expression.
675 */
676 static class PolyScanner extends FilterScanner {
678 PolyScanner() {
679 super(EnumSet.of(CONDEXPR, PARENS, LAMBDA, REFERENCE));
680 }
682 @Override
683 void skip(JCTree tree) {
684 //do nothing
685 }
686 }
688 /**
689 * A tree scanner suitable for visiting the target-type dependent nodes nested
690 * within a lambda expression body.
691 */
692 static class LambdaReturnScanner extends FilterScanner {
694 LambdaReturnScanner() {
695 super(EnumSet.of(BLOCK, CASE, CATCH, DOLOOP, FOREACHLOOP,
696 FORLOOP, RETURN, SYNCHRONIZED, SWITCH, TRY, WHILELOOP));
697 }
699 @Override
700 void skip(JCTree tree) {
701 //do nothing
702 }
703 }
705 /**
706 * This visitor is used to check that structural expressions conform
707 * to their target - this step is required as inference could end up
708 * inferring types that make some of the nested expressions incompatible
709 * with their corresponding instantiated target
710 */
711 class StuckChecker extends PolyScanner {
713 Type pt;
714 Infer.InferenceContext inferenceContext;
715 Set<Type> stuckVars = new LinkedHashSet<Type>();
717 StuckChecker(Type pt, Infer.InferenceContext inferenceContext) {
718 this.pt = pt;
719 this.inferenceContext = inferenceContext;
720 }
722 @Override
723 public void visitLambda(JCLambda tree) {
724 if (inferenceContext.inferenceVars().contains(pt)) {
725 stuckVars.add(pt);
726 }
727 if (!types.isFunctionalInterface(pt)) {
728 return;
729 }
730 Type descType = types.findDescriptorType(pt);
731 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
732 if (tree.paramKind == JCLambda.ParameterKind.IMPLICIT &&
733 freeArgVars.nonEmpty()) {
734 stuckVars.addAll(freeArgVars);
735 }
736 scanLambdaBody(tree, descType.getReturnType());
737 }
739 @Override
740 public void visitReference(JCMemberReference tree) {
741 scan(tree.expr);
742 if (inferenceContext.inferenceVars().contains(pt)) {
743 stuckVars.add(pt);
744 return;
745 }
746 if (!types.isFunctionalInterface(pt)) {
747 return;
748 }
750 Type descType = types.findDescriptorType(pt);
751 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
752 stuckVars.addAll(freeArgVars);
753 }
755 void scanLambdaBody(JCLambda lambda, final Type pt) {
756 if (lambda.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) {
757 stuckVars.addAll(stuckVarsInternal(lambda.body, pt, inferenceContext));
758 } else {
759 LambdaReturnScanner lambdaScanner = new LambdaReturnScanner() {
760 @Override
761 public void visitReturn(JCReturn tree) {
762 if (tree.expr != null) {
763 stuckVars.addAll(stuckVarsInternal(tree.expr, pt, inferenceContext));
764 }
765 }
766 };
767 lambdaScanner.scan(lambda.body);
768 }
769 }
770 }
771 }