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