DeferredAttr.java revision 3405:d671601a2f2e
1/*
2 * Copyright (c) 2012, 2015, 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 */
25
26package com.sun.tools.javac.comp;
27
28import com.sun.source.tree.LambdaExpressionTree.BodyKind;
29import com.sun.source.tree.NewClassTree;
30import com.sun.tools.javac.code.*;
31import com.sun.tools.javac.code.Type.TypeMapping;
32import com.sun.tools.javac.comp.ArgumentAttr.LocalCacheContext;
33import com.sun.tools.javac.comp.Resolve.ResolveError;
34import com.sun.tools.javac.resources.CompilerProperties.Fragments;
35import com.sun.tools.javac.tree.*;
36import com.sun.tools.javac.util.*;
37import com.sun.tools.javac.util.DefinedBy.Api;
38import com.sun.tools.javac.util.GraphUtils.DependencyKind;
39import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
40import com.sun.tools.javac.code.Symbol.*;
41import com.sun.tools.javac.comp.Attr.ResultInfo;
42import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase;
43import com.sun.tools.javac.tree.JCTree.*;
44import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType;
45import com.sun.tools.javac.util.Log.DeferredDiagnosticHandler;
46
47import java.util.ArrayList;
48import java.util.Collection;
49import java.util.Collections;
50import java.util.EnumSet;
51import java.util.HashSet;
52import java.util.LinkedHashSet;
53import java.util.Map;
54import java.util.Set;
55import java.util.WeakHashMap;
56import java.util.function.Function;
57
58import static com.sun.tools.javac.code.TypeTag.*;
59import static com.sun.tools.javac.tree.JCTree.Tag.*;
60
61/**
62 * This is an helper class that is used to perform deferred type-analysis.
63 * Each time a poly expression occurs in argument position, javac attributes it
64 * with a temporary 'deferred type' that is checked (possibly multiple times)
65 * against an expected formal type.
66 *
67 *  <p><b>This is NOT part of any supported API.
68 *  If you write code that depends on this, you do so at your own risk.
69 *  This code and its internal interfaces are subject to change or
70 *  deletion without notice.</b>
71 */
72public class DeferredAttr extends JCTree.Visitor {
73    protected static final Context.Key<DeferredAttr> deferredAttrKey = new Context.Key<>();
74
75    final Attr attr;
76    final ArgumentAttr argumentAttr;
77    final Check chk;
78    final JCDiagnostic.Factory diags;
79    final Enter enter;
80    final Infer infer;
81    final Resolve rs;
82    final Log log;
83    final Symtab syms;
84    final TreeMaker make;
85    final TreeCopier<Void> treeCopier;
86    final TypeMapping<Void> deferredCopier;
87    final Types types;
88    final Flow flow;
89    final Names names;
90    final TypeEnvs typeEnvs;
91
92    public static DeferredAttr instance(Context context) {
93        DeferredAttr instance = context.get(deferredAttrKey);
94        if (instance == null)
95            instance = new DeferredAttr(context);
96        return instance;
97    }
98
99    protected DeferredAttr(Context context) {
100        context.put(deferredAttrKey, this);
101        attr = Attr.instance(context);
102        argumentAttr = ArgumentAttr.instance(context);
103        chk = Check.instance(context);
104        diags = JCDiagnostic.Factory.instance(context);
105        enter = Enter.instance(context);
106        infer = Infer.instance(context);
107        rs = Resolve.instance(context);
108        log = Log.instance(context);
109        syms = Symtab.instance(context);
110        make = TreeMaker.instance(context);
111        types = Types.instance(context);
112        flow = Flow.instance(context);
113        names = Names.instance(context);
114        stuckTree = make.Ident(names.empty).setType(Type.stuckType);
115        typeEnvs = TypeEnvs.instance(context);
116        emptyDeferredAttrContext =
117            new DeferredAttrContext(AttrMode.CHECK, null, MethodResolutionPhase.BOX, infer.emptyContext, null, null) {
118                @Override
119                void addDeferredAttrNode(DeferredType dt, ResultInfo ri, DeferredStuckPolicy deferredStuckPolicy) {
120                    Assert.error("Empty deferred context!");
121                }
122                @Override
123                void complete() {
124                    Assert.error("Empty deferred context!");
125                }
126
127                @Override
128                public String toString() {
129                    return "Empty deferred context!";
130                }
131            };
132
133        // For speculative attribution, skip the class definition in <>.
134        treeCopier =
135            new TreeCopier<Void>(make) {
136                @Override @DefinedBy(Api.COMPILER_TREE)
137                public JCTree visitNewClass(NewClassTree node, Void p) {
138                    JCNewClass t = (JCNewClass) node;
139                    if (TreeInfo.isDiamond(t)) {
140                        JCExpression encl = copy(t.encl, p);
141                        List<JCExpression> typeargs = copy(t.typeargs, p);
142                        JCExpression clazz = copy(t.clazz, p);
143                        List<JCExpression> args = copy(t.args, p);
144                        JCClassDecl def = null;
145                        return make.at(t.pos).NewClass(encl, typeargs, clazz, args, def);
146                    } else {
147                        return super.visitNewClass(node, p);
148                    }
149                }
150            };
151        deferredCopier = new TypeMapping<Void> () {
152                @Override
153                public Type visitType(Type t, Void v) {
154                    if (t.hasTag(DEFERRED)) {
155                        DeferredType dt = (DeferredType) t;
156                        return new DeferredType(treeCopier.copy(dt.tree), dt.env);
157                    }
158                    return t;
159                }
160            };
161    }
162
163    /** shared tree for stuck expressions */
164    final JCTree stuckTree;
165
166    /**
167     * This type represents a deferred type. A deferred type starts off with
168     * no information on the underlying expression type. Such info needs to be
169     * discovered through type-checking the deferred type against a target-type.
170     * Every deferred type keeps a pointer to the AST node from which it originated.
171     */
172    public class DeferredType extends Type {
173
174        public JCExpression tree;
175        Env<AttrContext> env;
176        AttrMode mode;
177        boolean pertinentToApplicability = true;
178        SpeculativeCache speculativeCache;
179
180        DeferredType(JCExpression tree, Env<AttrContext> env) {
181            super(null, TypeMetadata.EMPTY);
182            this.tree = tree;
183            this.env = attr.copyEnv(env);
184            this.speculativeCache = new SpeculativeCache();
185        }
186
187        @Override
188        public DeferredType cloneWithMetadata(TypeMetadata md) {
189            throw new AssertionError("Cannot add metadata to a deferred type");
190        }
191
192        @Override
193        public TypeTag getTag() {
194            return DEFERRED;
195        }
196
197        @Override @DefinedBy(Api.LANGUAGE_MODEL)
198        public String toString() {
199            return "DeferredType";
200        }
201
202        /**
203         * A speculative cache is used to keep track of all overload resolution rounds
204         * that triggered speculative attribution on a given deferred type. Each entry
205         * stores a pointer to the speculative tree and the resolution phase in which the entry
206         * has been added.
207         */
208        class SpeculativeCache {
209
210            private Map<Symbol, List<Entry>> cache = new WeakHashMap<>();
211
212            class Entry {
213                JCTree speculativeTree;
214                ResultInfo resultInfo;
215
216                public Entry(JCTree speculativeTree, ResultInfo resultInfo) {
217                    this.speculativeTree = speculativeTree;
218                    this.resultInfo = resultInfo;
219                }
220
221                boolean matches(MethodResolutionPhase phase) {
222                    return resultInfo.checkContext.deferredAttrContext().phase == phase;
223                }
224            }
225
226            /**
227             * Retrieve a speculative cache entry corresponding to given symbol
228             * and resolution phase
229             */
230            Entry get(Symbol msym, MethodResolutionPhase phase) {
231                List<Entry> entries = cache.get(msym);
232                if (entries == null) return null;
233                for (Entry e : entries) {
234                    if (e.matches(phase)) return e;
235                }
236                return null;
237            }
238
239            /**
240             * Stores a speculative cache entry corresponding to given symbol
241             * and resolution phase
242             */
243            void put(JCTree speculativeTree, ResultInfo resultInfo) {
244                Symbol msym = resultInfo.checkContext.deferredAttrContext().msym;
245                List<Entry> entries = cache.get(msym);
246                if (entries == null) {
247                    entries = List.nil();
248                }
249                cache.put(msym, entries.prepend(new Entry(speculativeTree, resultInfo)));
250            }
251        }
252
253        /**
254         * Get the type that has been computed during a speculative attribution round
255         */
256        Type speculativeType(Symbol msym, MethodResolutionPhase phase) {
257            SpeculativeCache.Entry e = speculativeCache.get(msym, phase);
258            return e != null ? e.speculativeTree.type : Type.noType;
259        }
260
261        JCTree speculativeTree(DeferredAttrContext deferredAttrContext) {
262            DeferredType.SpeculativeCache.Entry e = speculativeCache.get(deferredAttrContext.msym, deferredAttrContext.phase);
263            return e != null ? e.speculativeTree : stuckTree;
264        }
265
266        DeferredTypeCompleter completer() {
267            return basicCompleter;
268        }
269
270        /**
271         * Check a deferred type against a potential target-type. Depending on
272         * the current attribution mode, a normal vs. speculative attribution
273         * round is performed on the underlying AST node. There can be only one
274         * speculative round for a given target method symbol; moreover, a normal
275         * attribution round must follow one or more speculative rounds.
276         */
277        Type check(ResultInfo resultInfo) {
278            DeferredStuckPolicy deferredStuckPolicy;
279            if (resultInfo.pt.hasTag(NONE) || resultInfo.pt.isErroneous()) {
280                deferredStuckPolicy = dummyStuckPolicy;
281            } else if (resultInfo.checkContext.deferredAttrContext().mode == AttrMode.SPECULATIVE ||
282                    resultInfo.checkContext.deferredAttrContext().insideOverloadPhase()) {
283                deferredStuckPolicy = new OverloadStuckPolicy(resultInfo, this);
284            } else {
285                deferredStuckPolicy = new CheckStuckPolicy(resultInfo, this);
286            }
287            return check(resultInfo, deferredStuckPolicy, completer());
288        }
289
290        private Type check(ResultInfo resultInfo, DeferredStuckPolicy deferredStuckPolicy,
291                DeferredTypeCompleter deferredTypeCompleter) {
292            DeferredAttrContext deferredAttrContext =
293                    resultInfo.checkContext.deferredAttrContext();
294            Assert.check(deferredAttrContext != emptyDeferredAttrContext);
295            if (deferredStuckPolicy.isStuck()) {
296                pertinentToApplicability = false;
297                deferredAttrContext.addDeferredAttrNode(this, resultInfo, deferredStuckPolicy);
298                return Type.noType;
299            } else {
300                try {
301                    return deferredTypeCompleter.complete(this, resultInfo, deferredAttrContext);
302                } finally {
303                    mode = deferredAttrContext.mode;
304                }
305            }
306        }
307    }
308
309    /**
310     * A completer for deferred types. Defines an entry point for type-checking
311     * a deferred type.
312     */
313    interface DeferredTypeCompleter {
314        /**
315         * Entry point for type-checking a deferred type. Depending on the
316         * circumstances, type-checking could amount to full attribution
317         * or partial structural check (aka potential applicability).
318         */
319        Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext);
320    }
321
322
323    /**
324     * A basic completer for deferred types. This completer type-checks a deferred type
325     * using attribution; depending on the attribution mode, this could be either standard
326     * or speculative attribution.
327     */
328    DeferredTypeCompleter basicCompleter = new DeferredTypeCompleter() {
329        public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
330            switch (deferredAttrContext.mode) {
331                case SPECULATIVE:
332                    //Note: if a symbol is imported twice we might do two identical
333                    //speculative rounds...
334                    Assert.check(dt.mode == null || dt.mode == AttrMode.SPECULATIVE);
335                    JCTree speculativeTree = attribSpeculative(dt.tree, dt.env, resultInfo);
336                    dt.speculativeCache.put(speculativeTree, resultInfo);
337                    return speculativeTree.type;
338                case CHECK:
339                    Assert.check(dt.mode != null);
340                    return attr.attribTree(dt.tree, dt.env, resultInfo);
341            }
342            Assert.error();
343            return null;
344        }
345    };
346
347    /**
348     * Policy for detecting stuck expressions. Different criteria might cause
349     * an expression to be judged as stuck, depending on whether the check
350     * is performed during overload resolution or after most specific.
351     */
352    interface DeferredStuckPolicy {
353        /**
354         * Has the policy detected that a given expression should be considered stuck?
355         */
356        boolean isStuck();
357        /**
358         * Get the set of inference variables a given expression depends upon.
359         */
360        Set<Type> stuckVars();
361        /**
362         * Get the set of inference variables which might get new constraints
363         * if a given expression is being type-checked.
364         */
365        Set<Type> depVars();
366    }
367
368    /**
369     * Basic stuck policy; an expression is never considered to be stuck.
370     */
371    DeferredStuckPolicy dummyStuckPolicy = new DeferredStuckPolicy() {
372        @Override
373        public boolean isStuck() {
374            return false;
375        }
376        @Override
377        public Set<Type> stuckVars() {
378            return Collections.emptySet();
379        }
380        @Override
381        public Set<Type> depVars() {
382            return Collections.emptySet();
383        }
384    };
385
386    /**
387     * The 'mode' in which the deferred type is to be type-checked
388     */
389    public enum AttrMode {
390        /**
391         * A speculative type-checking round is used during overload resolution
392         * mainly to generate constraints on inference variables. Side-effects
393         * arising from type-checking the expression associated with the deferred
394         * type are reversed after the speculative round finishes. This means the
395         * expression tree will be left in a blank state.
396         */
397        SPECULATIVE,
398        /**
399         * This is the plain type-checking mode. Produces side-effects on the underlying AST node
400         */
401        CHECK
402    }
403
404    /**
405     * Performs speculative attribution of a lambda body and returns the speculative lambda tree,
406     * in the absence of a target-type. Since {@link Attr#visitLambda(JCLambda)} cannot type-check
407     * lambda bodies w/o a suitable target-type, this routine 'unrolls' the lambda by turning it
408     * into a regular block, speculatively type-checks the block and then puts back the pieces.
409     */
410    JCLambda attribSpeculativeLambda(JCLambda that, Env<AttrContext> env, ResultInfo resultInfo) {
411        ListBuffer<JCStatement> stats = new ListBuffer<>();
412        stats.addAll(that.params);
413        if (that.getBodyKind() == JCLambda.BodyKind.EXPRESSION) {
414            stats.add(make.Return((JCExpression)that.body));
415        } else {
416            stats.add((JCBlock)that.body);
417        }
418        JCBlock lambdaBlock = make.Block(0, stats.toList());
419        Env<AttrContext> localEnv = attr.lambdaEnv(that, env);
420        try {
421            localEnv.info.returnResult = resultInfo;
422            JCBlock speculativeTree = (JCBlock)attribSpeculative(lambdaBlock, localEnv, resultInfo);
423            List<JCVariableDecl> args = speculativeTree.getStatements().stream()
424                    .filter(s -> s.hasTag(Tag.VARDEF))
425                    .map(t -> (JCVariableDecl)t)
426                    .collect(List.collector());
427            JCTree lambdaBody = speculativeTree.getStatements().last();
428            if (lambdaBody.hasTag(Tag.RETURN)) {
429                lambdaBody = ((JCReturn)lambdaBody).expr;
430            }
431            JCLambda speculativeLambda = make.Lambda(args, lambdaBody);
432            attr.preFlow(speculativeLambda);
433            flow.analyzeLambda(env, speculativeLambda, make, false);
434            return speculativeLambda;
435        } finally {
436            localEnv.info.scope.leave();
437        }
438    }
439
440    /**
441     * Routine that performs speculative type-checking; the input AST node is
442     * cloned (to avoid side-effects cause by Attr) and compiler state is
443     * restored after type-checking. All diagnostics (but critical ones) are
444     * disabled during speculative type-checking.
445     */
446    JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
447        return attribSpeculative(tree, env, resultInfo, treeCopier,
448                (newTree)->new DeferredAttrDiagHandler(log, newTree));
449    }
450
451    <Z> JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo, TreeCopier<Z> deferredCopier,
452                                 Function<JCTree, DeferredDiagnosticHandler> diagHandlerCreator) {
453        final JCTree newTree = deferredCopier.copy(tree);
454        Env<AttrContext> speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared(env.info.scope.owner)));
455        speculativeEnv.info.isSpeculative = true;
456        Log.DeferredDiagnosticHandler deferredDiagnosticHandler = diagHandlerCreator.apply(newTree);
457        try {
458            attr.attribTree(newTree, speculativeEnv, resultInfo);
459            return newTree;
460        } finally {
461            new UnenterScanner(env.toplevel.modle).scan(newTree);
462            log.popDiagnosticHandler(deferredDiagnosticHandler);
463        }
464    }
465    //where
466
467        class UnenterScanner extends TreeScanner {
468            private final ModuleSymbol msym;
469
470            public UnenterScanner(ModuleSymbol msym) {
471                this.msym = msym;
472            }
473
474            @Override
475            public void visitClassDef(JCClassDecl tree) {
476                ClassSymbol csym = tree.sym;
477                //if something went wrong during method applicability check
478                //it is possible that nested expressions inside argument expression
479                //are left unchecked - in such cases there's nothing to clean up.
480                if (csym == null) return;
481                typeEnvs.remove(csym);
482                chk.removeCompiled(csym);
483                chk.clearLocalClassNameIndexes(csym);
484                syms.removeClass(msym, csym.flatname);
485                super.visitClassDef(tree);
486            }
487        }
488
489        static class DeferredAttrDiagHandler extends Log.DeferredDiagnosticHandler {
490
491            static class PosScanner extends TreeScanner {
492                DiagnosticPosition pos;
493                boolean found = false;
494
495                PosScanner(DiagnosticPosition pos) {
496                    this.pos = pos;
497                }
498
499                @Override
500                public void scan(JCTree tree) {
501                    if (tree != null &&
502                            tree.pos() == pos) {
503                        found = true;
504                    }
505                    super.scan(tree);
506                }
507            }
508
509            DeferredAttrDiagHandler(Log log, JCTree newTree) {
510                super(log, new Filter<JCDiagnostic>() {
511                    public boolean accepts(JCDiagnostic d) {
512                        PosScanner posScanner = new PosScanner(d.getDiagnosticPosition());
513                        posScanner.scan(newTree);
514                        return posScanner.found;
515                    }
516                });
517            }
518        }
519
520    /**
521     * A deferred context is created on each method check. A deferred context is
522     * used to keep track of information associated with the method check, such as
523     * the symbol of the method being checked, the overload resolution phase,
524     * the kind of attribution mode to be applied to deferred types and so forth.
525     * As deferred types are processed (by the method check routine) stuck AST nodes
526     * are added (as new deferred attribution nodes) to this context. The complete()
527     * routine makes sure that all pending nodes are properly processed, by
528     * progressively instantiating all inference variables on which one or more
529     * deferred attribution node is stuck.
530     */
531    class DeferredAttrContext {
532
533        /** attribution mode */
534        final AttrMode mode;
535
536        /** symbol of the method being checked */
537        final Symbol msym;
538
539        /** method resolution step */
540        final Resolve.MethodResolutionPhase phase;
541
542        /** inference context */
543        final InferenceContext inferenceContext;
544
545        /** parent deferred context */
546        final DeferredAttrContext parent;
547
548        /** Warner object to report warnings */
549        final Warner warn;
550
551        /** list of deferred attribution nodes to be processed */
552        ArrayList<DeferredAttrNode> deferredAttrNodes = new ArrayList<>();
553
554        DeferredAttrContext(AttrMode mode, Symbol msym, MethodResolutionPhase phase,
555                InferenceContext inferenceContext, DeferredAttrContext parent, Warner warn) {
556            this.mode = mode;
557            this.msym = msym;
558            this.phase = phase;
559            this.parent = parent;
560            this.warn = warn;
561            this.inferenceContext = inferenceContext;
562        }
563
564        /**
565         * Adds a node to the list of deferred attribution nodes - used by Resolve.rawCheckArgumentsApplicable
566         * Nodes added this way act as 'roots' for the out-of-order method checking process.
567         */
568        void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo,
569                DeferredStuckPolicy deferredStuckPolicy) {
570            deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, deferredStuckPolicy));
571        }
572
573        /**
574         * Incrementally process all nodes, by skipping 'stuck' nodes and attributing
575         * 'unstuck' ones. If at any point no progress can be made (no 'unstuck' nodes)
576         * some inference variable might get eagerly instantiated so that all nodes
577         * can be type-checked.
578         */
579        void complete() {
580            while (!deferredAttrNodes.isEmpty()) {
581                boolean progress = false;
582                //scan a defensive copy of the node list - this is because a deferred
583                //attribution round can add new nodes to the list
584                for (DeferredAttrNode deferredAttrNode : List.from(deferredAttrNodes)) {
585                    if (deferredAttrNode.process(this)) {
586                        deferredAttrNodes.remove(deferredAttrNode);
587                        progress = true;
588                    }
589                }
590                if (!progress) {
591                    if (insideOverloadPhase()) {
592                        for (DeferredAttrNode deferredNode: deferredAttrNodes) {
593                            deferredNode.dt.tree.type = Type.noType;
594                        }
595                        return;
596                    }
597                    //remove all variables that have already been instantiated
598                    //from the list of stuck variables
599                    try {
600                        //find stuck expression to unstuck
601                        DeferredAttrNode toUnstuck = pickDeferredNode();
602                        inferenceContext.solveAny(List.from(toUnstuck.deferredStuckPolicy.stuckVars()), warn);
603                        inferenceContext.notifyChange();
604                    } catch (Infer.GraphStrategy.NodeNotFoundException ex) {
605                        //this means that we are in speculative mode and the
606                        //set of contraints are too tight for progess to be made.
607                        //Just leave the remaining expressions as stuck.
608                        break;
609                    }
610                }
611            }
612        }
613
614        public boolean insideOverloadPhase() {
615            DeferredAttrContext dac = this;
616            if (dac == emptyDeferredAttrContext) {
617                return false;
618            }
619            if (dac.mode == AttrMode.SPECULATIVE) {
620                return true;
621            }
622            return dac.parent.insideOverloadPhase();
623        }
624
625        /**
626         * Pick the deferred node to be unstuck. The chosen node is the first strongly connected
627         * component containing exactly one node found in the dependency graph induced by deferred nodes.
628         * If no such component is found, the first deferred node is returned.
629         */
630        DeferredAttrNode pickDeferredNode() {
631            List<StuckNode> nodes = deferredAttrNodes.stream()
632                    .map(StuckNode::new)
633                    .collect(List.collector());
634            //init stuck expression graph; a deferred node A depends on a deferred node B iff
635            //the intersection between A's input variable and B's output variable is non-empty.
636            for (StuckNode sn1 : nodes) {
637                for (Type t : sn1.data.deferredStuckPolicy.stuckVars()) {
638                    for (StuckNode sn2 : nodes) {
639                        if (sn1 != sn2 && sn2.data.deferredStuckPolicy.depVars().contains(t)) {
640                            sn1.deps.add(sn2);
641                        }
642                    }
643                }
644            }
645            //compute tarjan on the stuck graph
646            List<? extends StuckNode> csn = GraphUtils.tarjan(nodes).get(0);
647            return csn.length() == 1 ? csn.get(0).data : deferredAttrNodes.get(0);
648        }
649
650        class StuckNode extends GraphUtils.TarjanNode<DeferredAttrNode, StuckNode> {
651
652            Set<StuckNode> deps = new HashSet<>();
653
654            StuckNode(DeferredAttrNode data) {
655                super(data);
656            }
657
658            @Override
659            public DependencyKind[] getSupportedDependencyKinds() {
660                return new DependencyKind[] { Infer.DependencyKind.STUCK };
661            }
662
663            @Override
664            public Collection<? extends StuckNode> getDependenciesByKind(DependencyKind dk) {
665                if (dk == Infer.DependencyKind.STUCK) {
666                    return deps;
667                } else {
668                    throw new IllegalStateException();
669                }
670            }
671
672            @Override
673            public Iterable<? extends StuckNode> getAllDependencies() {
674                return deps;
675            }
676        }
677    }
678
679    /**
680     * Class representing a deferred attribution node. It keeps track of
681     * a deferred type, along with the expected target type information.
682     */
683    class DeferredAttrNode {
684
685        /** underlying deferred type */
686        DeferredType dt;
687
688        /** underlying target type information */
689        ResultInfo resultInfo;
690
691        /** stuck policy associated with this node */
692        DeferredStuckPolicy deferredStuckPolicy;
693
694        DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, DeferredStuckPolicy deferredStuckPolicy) {
695            this.dt = dt;
696            this.resultInfo = resultInfo;
697            this.deferredStuckPolicy = deferredStuckPolicy;
698        }
699
700        /**
701         * Process a deferred attribution node.
702         * Invariant: a stuck node cannot be processed.
703         */
704        @SuppressWarnings("fallthrough")
705        boolean process(final DeferredAttrContext deferredAttrContext) {
706            switch (deferredAttrContext.mode) {
707                case SPECULATIVE:
708                    if (deferredStuckPolicy.isStuck()) {
709                        dt.check(resultInfo, dummyStuckPolicy, new StructuralStuckChecker());
710                        return true;
711                    } else {
712                        Assert.error("Cannot get here");
713                    }
714                case CHECK:
715                    if (deferredStuckPolicy.isStuck()) {
716                        //stuck expression - see if we can propagate
717                        if (deferredAttrContext.parent != emptyDeferredAttrContext &&
718                                Type.containsAny(deferredAttrContext.parent.inferenceContext.inferencevars,
719                                        List.from(deferredStuckPolicy.stuckVars()))) {
720                            deferredAttrContext.parent.addDeferredAttrNode(dt,
721                                    resultInfo.dup(new Check.NestedCheckContext(resultInfo.checkContext) {
722                                @Override
723                                public InferenceContext inferenceContext() {
724                                    return deferredAttrContext.parent.inferenceContext;
725                                }
726                                @Override
727                                public DeferredAttrContext deferredAttrContext() {
728                                    return deferredAttrContext.parent;
729                                }
730                            }), deferredStuckPolicy);
731                            dt.tree.type = Type.stuckType;
732                            return true;
733                        } else {
734                            return false;
735                        }
736                    } else {
737                        Assert.check(!deferredAttrContext.insideOverloadPhase(),
738                                "attribution shouldn't be happening here");
739                        ResultInfo instResultInfo =
740                                resultInfo.dup(deferredAttrContext.inferenceContext.asInstType(resultInfo.pt));
741                        dt.check(instResultInfo, dummyStuckPolicy, basicCompleter);
742                        return true;
743                    }
744                default:
745                    throw new AssertionError("Bad mode");
746            }
747        }
748
749        /**
750         * Structural checker for stuck expressions
751         */
752        class StructuralStuckChecker extends TreeScanner implements DeferredTypeCompleter {
753
754            ResultInfo resultInfo;
755            InferenceContext inferenceContext;
756            Env<AttrContext> env;
757
758            public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
759                this.resultInfo = resultInfo;
760                this.inferenceContext = deferredAttrContext.inferenceContext;
761                this.env = dt.env;
762                dt.tree.accept(this);
763                dt.speculativeCache.put(stuckTree, resultInfo);
764                return Type.noType;
765            }
766
767            @Override
768            public void visitLambda(JCLambda tree) {
769                Check.CheckContext checkContext = resultInfo.checkContext;
770                Type pt = resultInfo.pt;
771                if (!inferenceContext.inferencevars.contains(pt)) {
772                    //must be a functional descriptor
773                    Type descriptorType = null;
774                    try {
775                        descriptorType = types.findDescriptorType(pt);
776                    } catch (Types.FunctionDescriptorLookupError ex) {
777                        checkContext.report(null, ex.getDiagnostic());
778                    }
779
780                    if (descriptorType.getParameterTypes().length() != tree.params.length()) {
781                        checkContext.report(tree,
782                                diags.fragment("incompatible.arg.types.in.lambda"));
783                    }
784
785                    Type currentReturnType = descriptorType.getReturnType();
786                    boolean returnTypeIsVoid = currentReturnType.hasTag(VOID);
787                    if (tree.getBodyKind() == BodyKind.EXPRESSION) {
788                        boolean isExpressionCompatible = !returnTypeIsVoid ||
789                            TreeInfo.isExpressionStatement((JCExpression)tree.getBody());
790                        if (!isExpressionCompatible) {
791                            resultInfo.checkContext.report(tree.pos(),
792                                diags.fragment("incompatible.ret.type.in.lambda",
793                                    diags.fragment("missing.ret.val", currentReturnType)));
794                        }
795                    } else {
796                        LambdaBodyStructChecker lambdaBodyChecker =
797                                new LambdaBodyStructChecker();
798
799                        tree.body.accept(lambdaBodyChecker);
800                        boolean isVoidCompatible = lambdaBodyChecker.isVoidCompatible;
801
802                        if (returnTypeIsVoid) {
803                            if (!isVoidCompatible) {
804                                resultInfo.checkContext.report(tree.pos(),
805                                    diags.fragment("unexpected.ret.val"));
806                            }
807                        } else {
808                            boolean isValueCompatible = lambdaBodyChecker.isPotentiallyValueCompatible
809                                && !canLambdaBodyCompleteNormally(tree);
810                            if (!isValueCompatible && !isVoidCompatible) {
811                                log.error(tree.body.pos(),
812                                    "lambda.body.neither.value.nor.void.compatible");
813                            }
814
815                            if (!isValueCompatible) {
816                                resultInfo.checkContext.report(tree.pos(),
817                                    diags.fragment("incompatible.ret.type.in.lambda",
818                                        diags.fragment("missing.ret.val", currentReturnType)));
819                            }
820                        }
821                    }
822                }
823            }
824
825            boolean canLambdaBodyCompleteNormally(JCLambda tree) {
826                List<JCVariableDecl> oldParams = tree.params;
827                LocalCacheContext localCacheContext = argumentAttr.withLocalCacheContext();
828                try {
829                    tree.params = tree.params.stream()
830                            .map(vd -> make.VarDef(vd.mods, vd.name, make.Erroneous(), null))
831                            .collect(List.collector());
832                    return attribSpeculativeLambda(tree, env, attr.unknownExprInfo).canCompleteNormally;
833                } finally {
834                    localCacheContext.leave();
835                    tree.params = oldParams;
836                }
837            }
838
839            @Override
840            public void visitNewClass(JCNewClass tree) {
841                //do nothing
842            }
843
844            @Override
845            public void visitApply(JCMethodInvocation tree) {
846                //do nothing
847            }
848
849            @Override
850            public void visitReference(JCMemberReference tree) {
851                Check.CheckContext checkContext = resultInfo.checkContext;
852                Type pt = resultInfo.pt;
853                if (!inferenceContext.inferencevars.contains(pt)) {
854                    try {
855                        types.findDescriptorType(pt);
856                    } catch (Types.FunctionDescriptorLookupError ex) {
857                        checkContext.report(null, ex.getDiagnostic());
858                    }
859                    Env<AttrContext> localEnv = env.dup(tree);
860                    JCExpression exprTree = (JCExpression)attribSpeculative(tree.getQualifierExpression(), localEnv,
861                            attr.memberReferenceQualifierResult(tree));
862                    ListBuffer<Type> argtypes = new ListBuffer<>();
863                    for (Type t : types.findDescriptorType(pt).getParameterTypes()) {
864                        argtypes.append(Type.noType);
865                    }
866                    JCMemberReference mref2 = new TreeCopier<Void>(make).copy(tree);
867                    mref2.expr = exprTree;
868                    Symbol lookupSym =
869                            rs.resolveMemberReference(localEnv, mref2, exprTree.type,
870                                    tree.name, argtypes.toList(), List.nil(), rs.arityMethodCheck,
871                                    inferenceContext, rs.structuralReferenceChooser).fst;
872                    switch (lookupSym.kind) {
873                        case WRONG_MTH:
874                        case WRONG_MTHS:
875                            //note: as argtypes are erroneous types, type-errors must
876                            //have been caused by arity mismatch
877                            checkContext.report(tree, diags.fragment(Fragments.IncompatibleArgTypesInMref));
878                            break;
879                        case ABSENT_MTH:
880                        case STATICERR:
881                            //if no method found, or method found with wrong staticness, report better message
882                            checkContext.report(tree, ((ResolveError)lookupSym).getDiagnostic(DiagnosticType.FRAGMENT,
883                                    tree, exprTree.type.tsym, exprTree.type, tree.name, argtypes.toList(), List.nil()));
884                            break;
885                    }
886                }
887            }
888        }
889
890        /* This visitor looks for return statements, its analysis will determine if
891         * a lambda body is void or value compatible. We must analyze return
892         * statements contained in the lambda body only, thus any return statement
893         * contained in an inner class or inner lambda body, should be ignored.
894         */
895        class LambdaBodyStructChecker extends TreeScanner {
896            boolean isVoidCompatible = true;
897            boolean isPotentiallyValueCompatible = true;
898
899            @Override
900            public void visitClassDef(JCClassDecl tree) {
901                // do nothing
902            }
903
904            @Override
905            public void visitLambda(JCLambda tree) {
906                // do nothing
907            }
908
909            @Override
910            public void visitNewClass(JCNewClass tree) {
911                // do nothing
912            }
913
914            @Override
915            public void visitReturn(JCReturn tree) {
916                if (tree.expr != null) {
917                    isVoidCompatible = false;
918                } else {
919                    isPotentiallyValueCompatible = false;
920                }
921            }
922        }
923    }
924
925    /** an empty deferred attribution context - all methods throw exceptions */
926    final DeferredAttrContext emptyDeferredAttrContext;
927
928    /**
929     * Map a list of types possibly containing one or more deferred types
930     * into a list of ordinary types. Each deferred type D is mapped into a type T,
931     * where T is computed by retrieving the type that has already been
932     * computed for D during a previous deferred attribution round of the given kind.
933     */
934    class DeferredTypeMap extends TypeMapping<Void> {
935        DeferredAttrContext deferredAttrContext;
936
937        protected DeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
938            this.deferredAttrContext = new DeferredAttrContext(mode, msym, phase,
939                    infer.emptyContext, emptyDeferredAttrContext, types.noWarnings);
940        }
941
942        @Override
943        public Type visitType(Type t, Void _unused) {
944            if (!t.hasTag(DEFERRED)) {
945                return super.visitType(t, null);
946            } else {
947                DeferredType dt = (DeferredType)t;
948                return typeOf(dt);
949            }
950        }
951
952        protected Type typeOf(DeferredType dt) {
953            switch (deferredAttrContext.mode) {
954                case CHECK:
955                    return dt.tree.type == null ? Type.noType : dt.tree.type;
956                case SPECULATIVE:
957                    return dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase);
958            }
959            Assert.error();
960            return null;
961        }
962    }
963
964    /**
965     * Specialized recovery deferred mapping.
966     * Each deferred type D is mapped into a type T, where T is computed either by
967     * (i) retrieving the type that has already been computed for D during a previous
968     * attribution round (as before), or (ii) by synthesizing a new type R for D
969     * (the latter step is useful in a recovery scenario).
970     */
971    public class RecoveryDeferredTypeMap extends DeferredTypeMap {
972
973        public RecoveryDeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
974            super(mode, msym, phase != null ? phase : MethodResolutionPhase.BOX);
975        }
976
977        @Override
978        protected Type typeOf(DeferredType dt) {
979            Type owntype = super.typeOf(dt);
980            return owntype == Type.noType ?
981                        recover(dt) : owntype;
982        }
983
984        /**
985         * Synthesize a type for a deferred type that hasn't been previously
986         * reduced to an ordinary type. Functional deferred types and conditionals
987         * are mapped to themselves, in order to have a richer diagnostic
988         * representation. Remaining deferred types are attributed using
989         * a default expected type (j.l.Object).
990         */
991        private Type recover(DeferredType dt) {
992            dt.check(attr.new RecoveryInfo(deferredAttrContext) {
993                @Override
994                protected Type check(DiagnosticPosition pos, Type found) {
995                    return chk.checkNonVoid(pos, super.check(pos, found));
996                }
997            });
998            return super.visit(dt);
999        }
1000    }
1001
1002    /**
1003     * A special tree scanner that would only visit portions of a given tree.
1004     * The set of nodes visited by the scanner can be customized at construction-time.
1005     */
1006    abstract static class FilterScanner extends com.sun.tools.javac.tree.TreeScanner {
1007
1008        final Filter<JCTree> treeFilter;
1009
1010        FilterScanner(final Set<JCTree.Tag> validTags) {
1011            this.treeFilter = new Filter<JCTree>() {
1012                public boolean accepts(JCTree t) {
1013                    return validTags.contains(t.getTag());
1014                }
1015            };
1016        }
1017
1018        @Override
1019        public void scan(JCTree tree) {
1020            if (tree != null) {
1021                if (treeFilter.accepts(tree)) {
1022                    super.scan(tree);
1023                } else {
1024                    skip(tree);
1025                }
1026            }
1027        }
1028
1029        /**
1030         * handler that is executed when a node has been discarded
1031         */
1032        void skip(JCTree tree) {}
1033    }
1034
1035    /**
1036     * A tree scanner suitable for visiting the target-type dependent nodes of
1037     * a given argument expression.
1038     */
1039    static class PolyScanner extends FilterScanner {
1040
1041        PolyScanner() {
1042            super(EnumSet.of(CONDEXPR, PARENS, LAMBDA, REFERENCE));
1043        }
1044    }
1045
1046    /**
1047     * A tree scanner suitable for visiting the target-type dependent nodes nested
1048     * within a lambda expression body.
1049     */
1050    static class LambdaReturnScanner extends FilterScanner {
1051
1052        LambdaReturnScanner() {
1053            super(EnumSet.of(BLOCK, CASE, CATCH, DOLOOP, FOREACHLOOP,
1054                    FORLOOP, IF, RETURN, SYNCHRONIZED, SWITCH, TRY, WHILELOOP));
1055        }
1056    }
1057
1058    /**
1059     * This visitor is used to check that structural expressions conform
1060     * to their target - this step is required as inference could end up
1061     * inferring types that make some of the nested expressions incompatible
1062     * with their corresponding instantiated target
1063     */
1064    class CheckStuckPolicy extends PolyScanner implements DeferredStuckPolicy, Infer.FreeTypeListener {
1065
1066        Type pt;
1067        InferenceContext inferenceContext;
1068        Set<Type> stuckVars = new LinkedHashSet<>();
1069        Set<Type> depVars = new LinkedHashSet<>();
1070
1071        @Override
1072        public boolean isStuck() {
1073            return !stuckVars.isEmpty();
1074        }
1075
1076        @Override
1077        public Set<Type> stuckVars() {
1078            return stuckVars;
1079        }
1080
1081        @Override
1082        public Set<Type> depVars() {
1083            return depVars;
1084        }
1085
1086        public CheckStuckPolicy(ResultInfo resultInfo, DeferredType dt) {
1087            this.pt = resultInfo.pt;
1088            this.inferenceContext = resultInfo.checkContext.inferenceContext();
1089            scan(dt.tree);
1090            if (!stuckVars.isEmpty()) {
1091                resultInfo.checkContext.inferenceContext()
1092                        .addFreeTypeListener(List.from(stuckVars), this);
1093            }
1094        }
1095
1096        @Override
1097        public void typesInferred(InferenceContext inferenceContext) {
1098            stuckVars.clear();
1099        }
1100
1101        @Override
1102        public void visitLambda(JCLambda tree) {
1103            if (inferenceContext.inferenceVars().contains(pt)) {
1104                stuckVars.add(pt);
1105            }
1106            if (!types.isFunctionalInterface(pt)) {
1107                return;
1108            }
1109            Type descType = types.findDescriptorType(pt);
1110            List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
1111            if (tree.paramKind == JCLambda.ParameterKind.IMPLICIT &&
1112                    freeArgVars.nonEmpty()) {
1113                stuckVars.addAll(freeArgVars);
1114                depVars.addAll(inferenceContext.freeVarsIn(descType.getReturnType()));
1115            }
1116            scanLambdaBody(tree, descType.getReturnType());
1117        }
1118
1119        @Override
1120        public void visitReference(JCMemberReference tree) {
1121            scan(tree.expr);
1122            if (inferenceContext.inferenceVars().contains(pt)) {
1123                stuckVars.add(pt);
1124                return;
1125            }
1126            if (!types.isFunctionalInterface(pt)) {
1127                return;
1128            }
1129
1130            Type descType = types.findDescriptorType(pt);
1131            List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
1132            if (freeArgVars.nonEmpty() &&
1133                    tree.overloadKind == JCMemberReference.OverloadKind.OVERLOADED) {
1134                stuckVars.addAll(freeArgVars);
1135                depVars.addAll(inferenceContext.freeVarsIn(descType.getReturnType()));
1136            }
1137        }
1138
1139        void scanLambdaBody(JCLambda lambda, final Type pt) {
1140            if (lambda.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) {
1141                Type prevPt = this.pt;
1142                try {
1143                    this.pt = pt;
1144                    scan(lambda.body);
1145                } finally {
1146                    this.pt = prevPt;
1147                }
1148            } else {
1149                LambdaReturnScanner lambdaScanner = new LambdaReturnScanner() {
1150                    @Override
1151                    public void visitReturn(JCReturn tree) {
1152                        if (tree.expr != null) {
1153                            Type prevPt = CheckStuckPolicy.this.pt;
1154                            try {
1155                                CheckStuckPolicy.this.pt = pt;
1156                                CheckStuckPolicy.this.scan(tree.expr);
1157                            } finally {
1158                                CheckStuckPolicy.this.pt = prevPt;
1159                            }
1160                        }
1161                    }
1162                };
1163                lambdaScanner.scan(lambda.body);
1164            }
1165        }
1166    }
1167
1168    /**
1169     * This visitor is used to check that structural expressions conform
1170     * to their target - this step is required as inference could end up
1171     * inferring types that make some of the nested expressions incompatible
1172     * with their corresponding instantiated target
1173     */
1174    class OverloadStuckPolicy extends CheckStuckPolicy implements DeferredStuckPolicy {
1175
1176        boolean stuck;
1177
1178        @Override
1179        public boolean isStuck() {
1180            return super.isStuck() || stuck;
1181        }
1182
1183        public OverloadStuckPolicy(ResultInfo resultInfo, DeferredType dt) {
1184            super(resultInfo, dt);
1185        }
1186
1187        @Override
1188        public void visitLambda(JCLambda tree) {
1189            super.visitLambda(tree);
1190            if (tree.paramKind == JCLambda.ParameterKind.IMPLICIT) {
1191                stuck = true;
1192            }
1193        }
1194
1195        @Override
1196        public void visitReference(JCMemberReference tree) {
1197            super.visitReference(tree);
1198            if (tree.overloadKind == JCMemberReference.OverloadKind.OVERLOADED) {
1199                stuck = true;
1200            }
1201        }
1202    }
1203}
1204