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