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