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