Infer.java revision 3528:5538ba41cb97
1212795Sdim/*
2212795Sdim * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
3212795Sdim * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4212795Sdim *
5212795Sdim * This code is free software; you can redistribute it and/or modify it
6212795Sdim * under the terms of the GNU General Public License version 2 only, as
7212795Sdim * published by the Free Software Foundation.  Oracle designates this
8212795Sdim * particular file as subject to the "Classpath" exception as provided
9212795Sdim * by Oracle in the LICENSE file that accompanied this code.
10212795Sdim *
11212795Sdim * This code is distributed in the hope that it will be useful, but WITHOUT
12212795Sdim * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13212795Sdim * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14212795Sdim * version 2 for more details (a copy is included in the LICENSE file that
15249423Sdim * accompanied this code).
16218893Sdim *
17234982Sdim * You should have received a copy of the GNU General Public License version
18212795Sdim * 2 along with this work; if not, write to the Free Software Foundation,
19212795Sdim * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20249423Sdim *
21212795Sdim * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22212795Sdim * or visit www.oracle.com if you need additional information or have any
23249423Sdim * questions.
24212795Sdim */
25249423Sdim
26249423Sdimpackage com.sun.tools.javac.comp;
27249423Sdim
28239462Sdimimport com.sun.tools.javac.code.Type.UndetVar.UndetVarListener;
29212795Sdimimport com.sun.tools.javac.tree.JCTree;
30212795Sdimimport com.sun.tools.javac.tree.JCTree.JCTypeCast;
31212795Sdimimport com.sun.tools.javac.tree.TreeInfo;
32212795Sdimimport com.sun.tools.javac.util.*;
33212795Sdimimport com.sun.tools.javac.util.GraphUtils.DottableNode;
34212795Sdimimport com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
35212795Sdimimport com.sun.tools.javac.util.List;
36212795Sdimimport com.sun.tools.javac.code.*;
37212795Sdimimport com.sun.tools.javac.code.Type.*;
38212795Sdimimport com.sun.tools.javac.code.Type.UndetVar.InferenceBound;
39234353Sdimimport com.sun.tools.javac.code.Symbol.*;
40212795Sdimimport com.sun.tools.javac.comp.DeferredAttr.AttrMode;
41234353Sdimimport com.sun.tools.javac.comp.DeferredAttr.DeferredAttrContext;
42218893Sdimimport com.sun.tools.javac.comp.Infer.GraphSolver.InferenceGraph;
43218893Sdimimport com.sun.tools.javac.comp.Infer.GraphSolver.InferenceGraph.Node;
44212795Sdimimport com.sun.tools.javac.comp.Resolve.InapplicableMethodException;
45212795Sdimimport com.sun.tools.javac.comp.Resolve.VerboseResolutionMode;
46221345Sdim
47243830Sdimimport java.io.IOException;
48243830Sdimimport java.io.Writer;
49212795Sdimimport java.nio.file.Files;
50226633Sdimimport java.nio.file.Path;
51218893Sdimimport java.nio.file.Paths;
52218893Sdimimport java.util.ArrayList;
53218893Sdimimport java.util.Collection;
54226633Sdimimport java.util.Collections;
55218893Sdimimport java.util.EnumSet;
56218893Sdimimport java.util.HashMap;
57218893Sdimimport java.util.HashSet;
58226633Sdimimport java.util.Map;
59218893Sdimimport java.util.Optional;
60218893Sdimimport java.util.Properties;
61218893Sdimimport java.util.Set;
62226633Sdimimport java.util.function.BiFunction;
63226633Sdimimport java.util.function.BiPredicate;
64226633Sdim
65226633Sdimimport com.sun.tools.javac.main.Option;
66226633Sdim
67226633Sdimimport static com.sun.tools.javac.code.TypeTag.*;
68226633Sdim
69226633Sdim/** Helper class for type parameter inference, used by the attribution phase.
70226633Sdim *
71226633Sdim *  <p><b>This is NOT part of any supported API.
72226633Sdim *  If you write code that depends on this, you do so at your own risk.
73226633Sdim *  This code and its internal interfaces are subject to change or
74226633Sdim *  deletion without notice.</b>
75226633Sdim */
76218893Sdimpublic class Infer {
77218893Sdim    protected static final Context.Key<Infer> inferKey = new Context.Key<>();
78218893Sdim
79218893Sdim    Resolve rs;
80226633Sdim    Check chk;
81218893Sdim    Symtab syms;
82218893Sdim    Types types;
83218893Sdim    JCDiagnostic.Factory diags;
84218893Sdim    Log log;
85226633Sdim
86218893Sdim    /** should the graph solver be used? */
87218893Sdim    boolean allowGraphInference;
88218893Sdim
89218893Sdim    /**
90212795Sdim     * folder in which the inference dependency graphs should be written.
91234353Sdim     */
92234353Sdim    private final String dependenciesFolder;
93234353Sdim
94234353Sdim    /**
95234353Sdim     * List of graphs awaiting to be dumped to a file.
96234353Sdim     */
97234353Sdim    private List<String> pendingGraphs;
98234353Sdim
99234353Sdim    public static Infer instance(Context context) {
100234353Sdim        Infer instance = context.get(inferKey);
101234353Sdim        if (instance == null)
102234353Sdim            instance = new Infer(context);
103218893Sdim        return instance;
104218893Sdim    }
105218893Sdim
106234353Sdim    protected Infer(Context context) {
107234353Sdim        context.put(inferKey, this);
108234353Sdim
109234353Sdim        rs = Resolve.instance(context);
110234353Sdim        chk = Check.instance(context);
111234353Sdim        syms = Symtab.instance(context);
112234353Sdim        types = Types.instance(context);
113234353Sdim        diags = JCDiagnostic.Factory.instance(context);
114234353Sdim        log = Log.instance(context);
115234353Sdim        inferenceException = new InferenceException(diags);
116249423Sdim        Options options = Options.instance(context);
117234353Sdim        allowGraphInference = Source.instance(context).allowGraphInference()
118243830Sdim                && options.isUnset("useLegacyInference");
119234353Sdim        dependenciesFolder = options.get("debug.dumpInferenceGraphsTo");
120234353Sdim        pendingGraphs = List.nil();
121249423Sdim
122249423Sdim        emptyContext = new InferenceContext(this, List.<Type>nil());
123249423Sdim    }
124234353Sdim
125234353Sdim    /** A value for prototypes that admit any type, including polymorphic ones. */
126249423Sdim    public static final Type anyPoly = new JCNoType();
127249423Sdim
128234353Sdim   /**
129234353Sdim    * This exception class is design to store a list of diagnostics corresponding
130234353Sdim    * to inference errors that can arise during a method applicability check.
131234353Sdim    */
132234353Sdim    public static class InferenceException extends InapplicableMethodException {
133249423Sdim        private static final long serialVersionUID = 0;
134249423Sdim
135234353Sdim        List<JCDiagnostic> messages = List.nil();
136234353Sdim
137234353Sdim        InferenceException(JCDiagnostic.Factory diags) {
138234353Sdim            super(diags);
139234353Sdim        }
140234353Sdim
141234353Sdim        @Override
142234353Sdim        InapplicableMethodException setMessage() {
143234353Sdim            //no message to set
144234353Sdim            return this;
145234353Sdim        }
146234353Sdim
147249423Sdim        @Override
148234353Sdim        InapplicableMethodException setMessage(JCDiagnostic diag) {
149234353Sdim            messages = messages.append(diag);
150234353Sdim            return this;
151234353Sdim        }
152234353Sdim
153234353Sdim        @Override
154234353Sdim        public JCDiagnostic getDiagnostic() {
155234353Sdim            return messages.head;
156234353Sdim        }
157234353Sdim
158234353Sdim        void clear() {
159243830Sdim            messages = List.nil();
160234353Sdim        }
161234353Sdim    }
162234353Sdim
163234353Sdim    protected final InferenceException inferenceException;
164234353Sdim
165234353Sdim    // <editor-fold defaultstate="collapsed" desc="Inference routines">
166234353Sdim    /**
167234353Sdim     * Main inference entry point - instantiate a generic method type
168234353Sdim     * using given argument types and (possibly) an expected target-type.
169234353Sdim     */
170234353Sdim    Type instantiateMethod( Env<AttrContext> env,
171234353Sdim                            List<Type> tvars,
172234353Sdim                            MethodType mt,
173234353Sdim                            Attr.ResultInfo resultInfo,
174234353Sdim                            MethodSymbol msym,
175234353Sdim                            List<Type> argtypes,
176234353Sdim                            boolean allowBoxing,
177234353Sdim                            boolean useVarargs,
178234353Sdim                            Resolve.MethodResolutionContext resolveContext,
179234353Sdim                            Warner warn) throws InferenceException {
180234353Sdim        //-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG
181234353Sdim        final InferenceContext inferenceContext = new InferenceContext(this, tvars);  //B0
182234353Sdim        inferenceException.clear();
183234353Sdim        try {
184234353Sdim            DeferredAttr.DeferredAttrContext deferredAttrContext =
185234353Sdim                        resolveContext.deferredAttrContext(msym, inferenceContext, resultInfo, warn);
186234353Sdim
187234353Sdim            resolveContext.methodCheck.argumentsAcceptable(env, deferredAttrContext,   //B2
188234353Sdim                    argtypes, mt.getParameterTypes(), warn);
189212795Sdim
190234353Sdim            if (allowGraphInference && resultInfo != null && resultInfo.pt == anyPoly) {
191243830Sdim                doIncorporation(inferenceContext, warn);
192234353Sdim                //we are inside method attribution - just return a partially inferred type
193218893Sdim                return new PartiallyInferredMethodType(mt, inferenceContext, env, warn);
194243830Sdim            } else if (allowGraphInference &&
195234353Sdim                    resultInfo != null &&
196243830Sdim                    !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) {
197212795Sdim                //inject return constraints earlier
198218893Sdim                doIncorporation(inferenceContext, warn); //propagation
199234353Sdim
200218893Sdim                boolean shouldPropagate = shouldPropagate(mt.getReturnType(), resultInfo, inferenceContext);
201243830Sdim
202243830Sdim                InferenceContext minContext = shouldPropagate ?
203243830Sdim                        inferenceContext.min(roots(mt, deferredAttrContext), true, warn) :
204212795Sdim                        inferenceContext;
205212795Sdim
206234353Sdim                Type newRestype = generateReturnConstraints(env.tree, resultInfo,  //B3
207221345Sdim                        mt, minContext);
208218893Sdim                mt = (MethodType)types.createMethodTypeWithReturn(mt, newRestype);
209226633Sdim
210226633Sdim                //propagate outwards if needed
211226633Sdim                if (shouldPropagate) {
212226633Sdim                    //propagate inference context outwards and exit
213226633Sdim                    minContext.dupTo(resultInfo.checkContext.inferenceContext());
214212795Sdim                    deferredAttrContext.complete();
215212795Sdim                    return mt;
216212795Sdim                }
217218893Sdim            }
218212795Sdim
219212795Sdim            deferredAttrContext.complete();
220212795Sdim
221212795Sdim            // minimize as yet undetermined type variables
222234353Sdim            if (allowGraphInference) {
223212795Sdim                inferenceContext.solve(warn);
224221345Sdim            } else {
225212795Sdim                inferenceContext.solveLegacy(true, warn, LegacyInferenceSteps.EQ_LOWER.steps); //minimizeInst
226263508Sdim            }
227212795Sdim
228263508Sdim            mt = (MethodType)inferenceContext.asInstType(mt);
229263508Sdim
230263508Sdim            if (!allowGraphInference &&
231263508Sdim                    inferenceContext.restvars().nonEmpty() &&
232263508Sdim                    resultInfo != null &&
233263508Sdim                    !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) {
234212795Sdim                generateReturnConstraints(env.tree, resultInfo, mt, inferenceContext);
235263508Sdim                inferenceContext.solveLegacy(false, warn, LegacyInferenceSteps.EQ_UPPER.steps); //maximizeInst
236263508Sdim                mt = (MethodType)inferenceContext.asInstType(mt);
237263508Sdim            }
238212795Sdim
239212795Sdim            if (resultInfo != null && rs.verboseResolutionMode.contains(VerboseResolutionMode.DEFERRED_INST)) {
240226633Sdim                log.note(env.tree.pos, "deferred.method.inst", msym, mt, resultInfo.pt);
241226633Sdim            }
242263508Sdim
243263508Sdim            // return instantiated version of method type
244263508Sdim            return mt;
245263508Sdim        } finally {
246263508Sdim            if (resultInfo != null || !allowGraphInference) {
247263508Sdim                inferenceContext.notifyChange();
248263508Sdim            } else {
249212795Sdim                inferenceContext.notifyChange(inferenceContext.boundedVars());
250212795Sdim            }
251212795Sdim            if (resultInfo == null) {
252212795Sdim                /* if the is no result info then we can clear the capture types
253212795Sdim                 * cache without affecting any result info check
254212795Sdim                 */
255212795Sdim                inferenceContext.captureTypeCache.clear();
256212795Sdim            }
257212795Sdim            dumpGraphsIfNeeded(env.tree, msym, resolveContext);
258212795Sdim        }
259212795Sdim    }
260251662Sdim    //where
261218893Sdim        private boolean shouldPropagate(Type restype, Attr.ResultInfo target, InferenceContext inferenceContext) {
262263508Sdim            return target.checkContext.inferenceContext() != emptyContext && //enclosing context is a generic method
263263508Sdim                        inferenceContext.free(restype) && //return type contains inference vars
264212795Sdim                        (!inferenceContext.inferencevars.contains(restype) || //no eager instantiation is required (as per 18.5.2)
265212795Sdim                                !needsEagerInstantiation((UndetVar)inferenceContext.asUndetVar(restype), target.pt, inferenceContext));
266212795Sdim        }
267212795Sdim
268234353Sdim        private List<Type> roots(MethodType mt, DeferredAttrContext deferredAttrContext) {
269212795Sdim            ListBuffer<Type> roots = new ListBuffer<>();
270263508Sdim            roots.add(mt.getReturnType());
271212795Sdim            if (deferredAttrContext != null && deferredAttrContext.mode == AttrMode.CHECK) {
272212795Sdim                roots.addAll(mt.getThrownTypes());
273223017Sdim                for (DeferredAttr.DeferredAttrNode n : deferredAttrContext.deferredAttrNodes) {
274212795Sdim                    roots.addAll(n.deferredStuckPolicy.stuckVars());
275212795Sdim                    roots.addAll(n.deferredStuckPolicy.depVars());
276212795Sdim                }
277212795Sdim            }
278234353Sdim            return roots.toList();
279212795Sdim        }
280212795Sdim
281212795Sdim    /**
282212795Sdim     * A partially infered method/constructor type; such a type can be checked multiple times
283212795Sdim     * against different targets.
284251662Sdim     */
285249423Sdim    public class PartiallyInferredMethodType extends MethodType {
286212795Sdim        public PartiallyInferredMethodType(MethodType mtype, InferenceContext inferenceContext, Env<AttrContext> env, Warner warn) {
287212795Sdim            super(mtype.getParameterTypes(), mtype.getReturnType(), mtype.getThrownTypes(), mtype.tsym);
288234353Sdim            this.inferenceContext = inferenceContext;
289234353Sdim            this.env = env;
290234353Sdim            this.warn = warn;
291212795Sdim        }
292234353Sdim
293234353Sdim        /** The inference context. */
294263508Sdim        final InferenceContext inferenceContext;
295263508Sdim
296263508Sdim        /** The attribution environment. */
297263508Sdim        Env<AttrContext> env;
298263508Sdim
299263508Sdim        /** The warner. */
300263508Sdim        final Warner warn;
301263508Sdim
302212795Sdim        @Override
303212795Sdim        public boolean isPartial() {
304212795Sdim            return true;
305212795Sdim        }
306212795Sdim
307212795Sdim        /**
308212795Sdim         * Checks this type against a target; this means generating return type constraints, solve
309212795Sdim         * and then roll back the results (to avoid poolluting the context).
310212795Sdim         */
311212795Sdim        Type check(Attr.ResultInfo resultInfo) {
312212795Sdim            Warner noWarnings = new Warner(null);
313212795Sdim            inferenceException.clear();
314212795Sdim            List<Type> saved_undet = null;
315212795Sdim            try {
316249423Sdim                /** we need to save the inference context before generating target type constraints.
317212795Sdim                 *  This constraints may pollute the inference context and make it useless in case we
318212795Sdim                 *  need to use it several times: with several targets.
319212795Sdim                 */
320212795Sdim                saved_undet = inferenceContext.save();
321226633Sdim                if (allowGraphInference && !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) {
322212795Sdim                    boolean shouldPropagate = shouldPropagate(getReturnType(), resultInfo, inferenceContext);
323212795Sdim
324212795Sdim                    InferenceContext minContext = shouldPropagate ?
325212795Sdim                            inferenceContext.min(roots(asMethodType(), null), false, warn) :
326212795Sdim                            inferenceContext;
327223017Sdim
328223017Sdim                    MethodType other = (MethodType)minContext.update(asMethodType());
329223017Sdim                    Type newRestype = generateReturnConstraints(env.tree, resultInfo,  //B3
330223017Sdim                            other, minContext);
331223017Sdim
332223017Sdim                    if (shouldPropagate) {
333223017Sdim                        //propagate inference context outwards and exit
334223017Sdim                        minContext.dupTo(resultInfo.checkContext.inferenceContext(),
335223017Sdim                                resultInfo.checkContext.deferredAttrContext().insideOverloadPhase());
336223017Sdim                        return newRestype;
337223017Sdim                    }
338223017Sdim                }
339212795Sdim                inferenceContext.solve(noWarnings);
340212795Sdim                return inferenceContext.asInstType(this).getReturnType();
341218893Sdim            } catch (InferenceException ex) {
342234353Sdim                resultInfo.checkContext.report(null, ex.getDiagnostic());
343234353Sdim                Assert.error(); //cannot get here (the above should throw)
344234353Sdim                return null;
345212795Sdim            } finally {
346212795Sdim                if (saved_undet != null) {
347243830Sdim                    inferenceContext.rollback(saved_undet);
348243830Sdim                }
349243830Sdim            }
350243830Sdim        }
351243830Sdim    }
352243830Sdim
353243830Sdim    private void dumpGraphsIfNeeded(DiagnosticPosition pos, Symbol msym, Resolve.MethodResolutionContext rsContext) {
354243830Sdim        int round = 0;
355212795Sdim        try {
356212795Sdim            for (String graph : pendingGraphs.reverse()) {
357212795Sdim                Assert.checkNonNull(dependenciesFolder);
358212795Sdim                Name name = msym.name == msym.name.table.names.init ?
359221345Sdim                        msym.owner.name : msym.name;
360221345Sdim                String filename = String.format("%s@%s[mode=%s,step=%s]_%d.dot",
361221345Sdim                        name,
362221345Sdim                        pos.getStartPosition(),
363221345Sdim                        rsContext.attrMode(),
364249423Sdim                        rsContext.step,
365249423Sdim                        round);
366249423Sdim                Path dotFile = Paths.get(dependenciesFolder, filename);
367249423Sdim                try (Writer w = Files.newBufferedWriter(dotFile)) {
368249423Sdim                    w.append(graph);
369226633Sdim                }
370221345Sdim                round++;
371234353Sdim            }
372234353Sdim        } catch (IOException ex) {
373263508Sdim            Assert.error("Error occurred when dumping inference graph: " + ex.getMessage());
374234353Sdim        } finally {
375234353Sdim            pendingGraphs = List.nil();
376263508Sdim        }
377263508Sdim    }
378221345Sdim
379234353Sdim    /**
380212795Sdim     * Generate constraints from the generic method's return type. If the method
381218893Sdim     * call occurs in a context where a type T is expected, use the expected
382212795Sdim     * type to derive more constraints on the generic method inference variables.
383218893Sdim     */
384234353Sdim    Type generateReturnConstraints(JCTree tree, Attr.ResultInfo resultInfo,
385234353Sdim            MethodType mt, InferenceContext inferenceContext) {
386234353Sdim        InferenceContext rsInfoInfContext = resultInfo.checkContext.inferenceContext();
387212795Sdim        Type from = mt.getReturnType();
388212795Sdim        if (mt.getReturnType().containsAny(inferenceContext.inferencevars) &&
389263508Sdim                rsInfoInfContext != emptyContext) {
390221345Sdim            from = types.capture(from);
391234353Sdim            //add synthetic captured ivars
392212795Sdim            for (Type t : from.getTypeArguments()) {
393226633Sdim                if (t.hasTag(TYPEVAR) && ((TypeVar)t).isCaptured()) {
394234353Sdim                    inferenceContext.addVar((TypeVar)t);
395234353Sdim                }
396234353Sdim            }
397234353Sdim        }
398234353Sdim        Type qtype = inferenceContext.asUndetVar(from);
399234353Sdim        Type to = resultInfo.pt;
400234353Sdim
401234353Sdim        if (qtype.hasTag(VOID)) {
402234353Sdim            to = syms.voidType;
403234353Sdim        } else if (to.hasTag(NONE)) {
404234353Sdim            to = from.isPrimitive() ? from : syms.objectType;
405234353Sdim        } else if (qtype.hasTag(UNDETVAR)) {
406234353Sdim            if (resultInfo.pt.isReference()) {
407234353Sdim                if (needsEagerInstantiation((UndetVar)qtype, to, inferenceContext)) {
408234353Sdim                    to = generateReferenceToTargetConstraint(tree, (UndetVar)qtype, to, resultInfo, inferenceContext);
409234353Sdim                }
410234353Sdim            } else {
411234353Sdim                if (to.isPrimitive()) {
412234353Sdim                    to = generateReturnConstraintsPrimitive(tree, (UndetVar)qtype, to,
413234353Sdim                        resultInfo, inferenceContext);
414234353Sdim                }
415234353Sdim            }
416234353Sdim        } else if (rsInfoInfContext.free(resultInfo.pt)) {
417212795Sdim            //propagation - cache captured vars
418212795Sdim            qtype = inferenceContext.asUndetVar(rsInfoInfContext.cachedCapture(tree, from, false));
419212795Sdim        }
420226633Sdim        Assert.check(allowGraphInference || !rsInfoInfContext.free(to),
421212795Sdim                "legacy inference engine cannot handle constraints on both sides of a subtyping assertion");
422212795Sdim        //we need to skip capture?
423212795Sdim        Warner retWarn = new Warner();
424212795Sdim        if (!resultInfo.checkContext.compatible(qtype, rsInfoInfContext.asUndetVar(to), retWarn) ||
425226633Sdim                //unchecked conversion is not allowed in source 7 mode
426212795Sdim                (!allowGraphInference && retWarn.hasLint(Lint.LintCategory.UNCHECKED))) {
427212795Sdim            throw inferenceException
428212795Sdim                    .setMessage("infer.no.conforming.instance.exists",
429212795Sdim                    inferenceContext.restvars(), mt.getReturnType(), to);
430221345Sdim        }
431212795Sdim        return from;
432226633Sdim    }
433212795Sdim
434212795Sdim    private Type generateReturnConstraintsPrimitive(JCTree tree, UndetVar from,
435234353Sdim            Type to, Attr.ResultInfo resultInfo, InferenceContext inferenceContext) {
436234353Sdim        if (!allowGraphInference) {
437212795Sdim            //if legacy, just return boxed type
438263508Sdim            return types.boxedClass(to).type;
439263508Sdim        }
440263508Sdim        //if graph inference we need to skip conflicting boxed bounds...
441263508Sdim        for (Type t : from.getBounds(InferenceBound.EQ, InferenceBound.UPPER,
442263508Sdim                InferenceBound.LOWER)) {
443263508Sdim            Type boundAsPrimitive = types.unboxedType(t);
444234353Sdim            if (boundAsPrimitive == null || boundAsPrimitive.hasTag(NONE)) {
445212795Sdim                continue;
446212795Sdim            }
447234353Sdim            return generateReferenceToTargetConstraint(tree, from, to,
448234353Sdim                    resultInfo, inferenceContext);
449234353Sdim        }
450234353Sdim        return types.boxedClass(to).type;
451221345Sdim    }
452234353Sdim
453221345Sdim    private boolean needsEagerInstantiation(UndetVar from, Type to, InferenceContext inferenceContext) {
454221345Sdim        Type captureOfTo = types.capture(to);
455263508Sdim        /* T is a reference type, but is not a wildcard-parameterized type, and either
456234353Sdim         */
457212795Sdim        if (captureOfTo == to) { //not a wildcard parameterized type
458234353Sdim            /* i) B2 contains a bound of one of the forms alpha = S or S <: alpha,
459212795Sdim             *      where S is a wildcard-parameterized type, or
460212795Sdim             */
461226633Sdim            for (Type t : from.getBounds(InferenceBound.EQ, InferenceBound.LOWER)) {
462212795Sdim                Type captureOfBound = types.capture(t);
463226633Sdim                if (captureOfBound != t) {
464263508Sdim                    return true;
465218893Sdim                }
466234353Sdim            }
467218893Sdim
468226633Sdim            /* ii) B2 contains two bounds of the forms S1 <: alpha and S2 <: alpha,
469218893Sdim             * where S1 and S2 have supertypes that are two different
470263508Sdim             * parameterizations of the same generic class or interface.
471218893Sdim             */
472263508Sdim            for (Type aLowerBound : from.getBounds(InferenceBound.LOWER)) {
473234353Sdim                for (Type anotherLowerBound : from.getBounds(InferenceBound.LOWER)) {
474263508Sdim                    if (aLowerBound != anotherLowerBound &&
475263508Sdim                            !inferenceContext.free(aLowerBound) &&
476212795Sdim                            !inferenceContext.free(anotherLowerBound) &&
477212795Sdim                            commonSuperWithDiffParameterization(aLowerBound, anotherLowerBound)) {
478212795Sdim                        return true;
479212795Sdim                    }
480218893Sdim                }
481218893Sdim            }
482218893Sdim        }
483226633Sdim
484226633Sdim        /* T is a parameterization of a generic class or interface, G,
485212795Sdim         * and B2 contains a bound of one of the forms alpha = S or S <: alpha,
486212795Sdim         * where there exists no type of the form G<...> that is a
487218893Sdim         * supertype of S, but the raw type G is a supertype of S
488218893Sdim         */
489218893Sdim        if (to.isParameterized()) {
490234353Sdim            for (Type t : from.getBounds(InferenceBound.EQ, InferenceBound.LOWER)) {
491263508Sdim                Type sup = types.asSuper(t, to.tsym);
492263508Sdim                if (sup != null && sup.isRaw()) {
493263508Sdim                    return true;
494263508Sdim                }
495263508Sdim            }
496263508Sdim        }
497263508Sdim        return false;
498263508Sdim    }
499263508Sdim
500263508Sdim    private boolean commonSuperWithDiffParameterization(Type t, Type s) {
501263508Sdim        for (Pair<Type, Type> supers : getParameterizedSupers(t, s)) {
502263508Sdim            if (!types.isSameType(supers.fst, supers.snd)) return true;
503263508Sdim        }
504234353Sdim        return false;
505234353Sdim    }
506234353Sdim
507234353Sdim    private Type generateReferenceToTargetConstraint(JCTree tree, UndetVar from,
508234353Sdim            Type to, Attr.ResultInfo resultInfo,
509234353Sdim            InferenceContext inferenceContext) {
510212795Sdim        inferenceContext.solve(List.of(from.qtype), new Warner());
511212795Sdim        inferenceContext.notifyChange();
512263508Sdim        Type capturedType = resultInfo.checkContext.inferenceContext()
513263508Sdim                .cachedCapture(tree, from.getInst(), false);
514263508Sdim        if (types.isConvertible(capturedType,
515212795Sdim                resultInfo.checkContext.inferenceContext().asUndetVar(to))) {
516212795Sdim            //effectively skip additional return-type constraint generation (compatibility)
517212795Sdim            return syms.objectType;
518249423Sdim        }
519263508Sdim        return to;
520212795Sdim    }
521212795Sdim
522212795Sdim    /**
523212795Sdim      * Infer cyclic inference variables as described in 15.12.2.8.
524226633Sdim      */
525212795Sdim    void instantiateAsUninferredVars(List<Type> vars, InferenceContext inferenceContext) {
526212795Sdim        ListBuffer<Type> todo = new ListBuffer<>();
527212795Sdim        //step 1 - create fresh tvars
528212795Sdim        for (Type t : vars) {
529212795Sdim            UndetVar uv = (UndetVar)inferenceContext.asUndetVar(t);
530218893Sdim            List<Type> upperBounds = uv.getBounds(InferenceBound.UPPER);
531212795Sdim            if (Type.containsAny(upperBounds, vars)) {
532263508Sdim                TypeSymbol fresh_tvar = new TypeVariableSymbol(Flags.SYNTHETIC, uv.qtype.tsym.name, null, uv.qtype.tsym.owner);
533212795Sdim                fresh_tvar.type = new TypeVar(fresh_tvar, types.makeIntersectionType(uv.getBounds(InferenceBound.UPPER)), null);
534212795Sdim                todo.append(uv);
535212795Sdim                uv.setInst(fresh_tvar.type);
536212795Sdim            } else if (upperBounds.nonEmpty()) {
537221345Sdim                uv.setInst(types.glb(upperBounds));
538218893Sdim            } else {
539218893Sdim                uv.setInst(syms.objectType);
540226633Sdim            }
541218893Sdim        }
542218893Sdim        //step 2 - replace fresh tvars in their bounds
543223017Sdim        List<Type> formals = vars;
544212795Sdim        for (Type t : todo) {
545212795Sdim            UndetVar uv = (UndetVar)t;
546212795Sdim            TypeVar ct = (TypeVar)uv.getInst();
547234353Sdim            ct.bound = types.glb(inferenceContext.asInstTypes(types.getBounds(ct)));
548212795Sdim            if (ct.bound.isErroneous()) {
549212795Sdim                //report inference error if glb fails
550218893Sdim                reportBoundError(uv, InferenceBound.UPPER);
551212795Sdim            }
552234353Sdim            formals = formals.tail;
553234353Sdim        }
554234353Sdim    }
555249423Sdim
556234353Sdim    /**
557234353Sdim     * Compute a synthetic method type corresponding to the requested polymorphic
558234353Sdim     * method signature. The target return type is computed from the immediately
559234353Sdim     * enclosing scope surrounding the polymorphic-signature call.
560234353Sdim     */
561234353Sdim    Type instantiatePolymorphicSignatureInstance(Env<AttrContext> env,
562234353Sdim                                            MethodSymbol spMethod,  // sig. poly. method or null if none
563234353Sdim                                            Resolve.MethodResolutionContext resolveContext,
564234353Sdim                                            List<Type> argtypes) {
565234353Sdim        final Type restype;
566234353Sdim
567234353Sdim        if (spMethod == null || types.isSameType(spMethod.getReturnType(), syms.objectType, true)) {
568234353Sdim            // The return type of the polymorphic signature is polymorphic,
569249423Sdim            // and is computed from the enclosing tree E, as follows:
570263508Sdim            // if E is a cast, then use the target type of the cast expression
571263508Sdim            // as a return type; if E is an expression statement, the return
572234353Sdim            // type is 'void'; otherwise
573234353Sdim            // the return type is simply 'Object'. A correctness check ensures
574212795Sdim            // that env.next refers to the lexically enclosing environment in
575212795Sdim            // which the polymorphic signature call environment is nested.
576234353Sdim
577212795Sdim            switch (env.next.tree.getTag()) {
578212795Sdim                case TYPECAST:
579226633Sdim                    JCTypeCast castTree = (JCTypeCast)env.next.tree;
580226633Sdim                    restype = (TreeInfo.skipParens(castTree.expr) == env.tree) ?
581212795Sdim                              castTree.clazz.type :
582212795Sdim                              syms.objectType;
583226633Sdim                    break;
584212795Sdim                case EXEC:
585218893Sdim                    JCTree.JCExpressionStatement execTree =
586226633Sdim                            (JCTree.JCExpressionStatement)env.next.tree;
587212795Sdim                    restype = (TreeInfo.skipParens(execTree.expr) == env.tree) ?
588212795Sdim                              syms.voidType :
589212795Sdim                              syms.objectType;
590212795Sdim                    break;
591226633Sdim                default:
592226633Sdim                    restype = syms.objectType;
593212795Sdim            }
594212795Sdim        } else {
595212795Sdim            // The return type of the polymorphic signature is fixed
596226633Sdim            // (not polymorphic)
597218893Sdim            restype = spMethod.getReturnType();
598212795Sdim        }
599212795Sdim
600226633Sdim        List<Type> paramtypes = argtypes.map(new ImplicitArgType(spMethod, resolveContext.step));
601212795Sdim        List<Type> exType = spMethod != null ?
602226633Sdim            spMethod.getThrownTypes() :
603226633Sdim            List.of(syms.throwableType); // make it throw all exceptions
604212795Sdim
605212795Sdim        MethodType mtype = new MethodType(paramtypes,
606212795Sdim                                          restype,
607212795Sdim                                          exType,
608218893Sdim                                          syms.methodClass);
609212795Sdim        return mtype;
610218893Sdim    }
611218893Sdim    //where
612212795Sdim        class ImplicitArgType extends DeferredAttr.DeferredTypeMap {
613212795Sdim
614218893Sdim            public ImplicitArgType(Symbol msym, Resolve.MethodResolutionPhase phase) {
615212795Sdim                (rs.deferredAttr).super(AttrMode.SPECULATIVE, msym, phase);
616226633Sdim            }
617218893Sdim
618218893Sdim            @Override
619226633Sdim            public Type visitClassType(ClassType t, Void aVoid) {
620218893Sdim                return types.erasure(t);
621226633Sdim            }
622218893Sdim
623218893Sdim            @Override
624218893Sdim            public Type visitType(Type t, Void _unused) {
625226633Sdim                if (t.hasTag(DEFERRED)) {
626226633Sdim                    return visit(super.visitType(t, null));
627218893Sdim                } else if (t.hasTag(BOT))
628218893Sdim                    // nulls type as the marker type Null (which has no instances)
629218893Sdim                    // infer as java.lang.Void for now
630218893Sdim                    t = types.boxedClass(syms.voidType).type;
631218893Sdim                return t;
632218893Sdim            }
633226633Sdim        }
634218893Sdim
635218893Sdim    TypeMapping<Void> fromTypeVarFun = new TypeMapping<Void>() {
636218893Sdim        @Override
637218893Sdim        public Type visitTypeVar(TypeVar tv, Void aVoid) {
638218893Sdim            return new UndetVar(tv, incorporationEngine(), types);
639218893Sdim        }
640218893Sdim
641218893Sdim        @Override
642218893Sdim        public Type visitCapturedType(CapturedType t, Void aVoid) {
643263508Sdim            return new CapturedUndetVar(t, incorporationEngine(), types);
644263508Sdim        }
645243830Sdim    };
646263508Sdim
647263508Sdim    /**
648263508Sdim      * This method is used to infer a suitable target SAM in case the original
649263508Sdim      * SAM type contains one or more wildcards. An inference process is applied
650263508Sdim      * so that wildcard bounds, as well as explicit lambda/method ref parameters
651263508Sdim      * (where applicable) are used to constraint the solution.
652263508Sdim      */
653218893Sdim    public Type instantiateFunctionalInterface(DiagnosticPosition pos, Type funcInterface,
654212795Sdim            List<Type> paramTypes, Check.CheckContext checkContext) {
655212795Sdim        if (types.capture(funcInterface) == funcInterface) {
656212795Sdim            //if capture doesn't change the type then return the target unchanged
657212795Sdim            //(this means the target contains no wildcards!)
658212795Sdim            return funcInterface;
659212795Sdim        } else {
660212795Sdim            Type formalInterface = funcInterface.tsym.type;
661226633Sdim            InferenceContext funcInterfaceContext =
662212795Sdim                    new InferenceContext(this, funcInterface.tsym.type.getTypeArguments());
663212795Sdim
664212795Sdim            Assert.check(paramTypes != null);
665212795Sdim            //get constraints from explicit params (this is done by
666212795Sdim            //checking that explicit param types are equal to the ones
667218893Sdim            //in the functional interface descriptors)
668218893Sdim            List<Type> descParameterTypes = types.findDescriptorType(formalInterface).getParameterTypes();
669218893Sdim            if (descParameterTypes.size() != paramTypes.size()) {
670212795Sdim                checkContext.report(pos, diags.fragment("incompatible.arg.types.in.lambda"));
671226633Sdim                return types.createErrorType(funcInterface);
672212795Sdim            }
673212795Sdim            for (Type p : descParameterTypes) {
674212795Sdim                if (!types.isSameType(funcInterfaceContext.asUndetVar(p), paramTypes.head)) {
675212795Sdim                    checkContext.report(pos, diags.fragment("no.suitable.functional.intf.inst", funcInterface));
676212795Sdim                    return types.createErrorType(funcInterface);
677212795Sdim                }
678212795Sdim                paramTypes = paramTypes.tail;
679226633Sdim            }
680212795Sdim
681212795Sdim            List<Type> actualTypeargs = funcInterface.getTypeArguments();
682226633Sdim            for (Type t : funcInterfaceContext.undetvars) {
683226633Sdim                UndetVar uv = (UndetVar)t;
684212795Sdim                Optional<Type> inst = uv.getBounds(InferenceBound.EQ).stream()
685212795Sdim                        .filter(b -> !b.containsAny(formalInterface.getTypeArguments())).findFirst();
686212795Sdim                uv.setInst(inst.orElse(actualTypeargs.head));
687212795Sdim                actualTypeargs = actualTypeargs.tail;
688212795Sdim            }
689243830Sdim
690243830Sdim            Type owntype = funcInterfaceContext.asInstType(formalInterface);
691243830Sdim            if (!chk.checkValidGenericType(owntype)) {
692243830Sdim                //if the inferred functional interface type is not well-formed,
693226633Sdim                //or if it's not a subtype of the original target, issue an error
694226633Sdim                checkContext.report(pos, diags.fragment("no.suitable.functional.intf.inst", funcInterface));
695212795Sdim            }
696212795Sdim            //propagate constraints as per JLS 18.2.1
697212795Sdim            checkContext.compatible(owntype, funcInterface, types.noWarnings);
698243830Sdim            return owntype;
699212795Sdim        }
700239462Sdim    }
701249423Sdim    // </editor-fold>
702226633Sdim
703226633Sdim    // <editor-fold defaultstate="collapsed" desc="Incorporation">
704226633Sdim
705226633Sdim    /**
706226633Sdim     * This class is the root of all incorporation actions.
707226633Sdim     */
708226633Sdim    public abstract class IncorporationAction {
709212795Sdim        UndetVar uv;
710212795Sdim        Type t;
711223017Sdim
712226633Sdim        IncorporationAction(UndetVar uv, Type t) {
713218893Sdim            this.uv = uv;
714239462Sdim            this.t = t;
715212795Sdim        }
716226633Sdim
717212795Sdim        public abstract IncorporationAction dup(UndetVar that);
718212795Sdim
719226633Sdim        /**
720226633Sdim         * Incorporation action entry-point. Subclasses should define the logic associated with
721226633Sdim         * this incorporation action.
722226633Sdim         */
723226633Sdim        abstract void apply(InferenceContext ic, Warner warn);
724226633Sdim
725226633Sdim        /**
726226633Sdim         * Helper function: perform subtyping through incorporation cache.
727226633Sdim         */
728226633Sdim        boolean isSubtype(Type s, Type t, Warner warn) {
729212795Sdim            return doIncorporationOp(IncorporationBinaryOpKind.IS_SUBTYPE, s, t, warn);
730212795Sdim        }
731212795Sdim
732212795Sdim        /**
733226633Sdim         * Helper function: perform type-equivalence through incorporation cache.
734226633Sdim         */
735212795Sdim        boolean isSameType(Type s, Type t) {
736212795Sdim            return doIncorporationOp(IncorporationBinaryOpKind.IS_SAME_TYPE, s, t, null);
737212795Sdim        }
738234353Sdim
739212795Sdim        @Override
740234353Sdim        public String toString() {
741234353Sdim            return String.format("%s[undet=%s,t=%s]", getClass().getSimpleName(), uv.qtype, t);
742212795Sdim        }
743234353Sdim    }
744234353Sdim
745234353Sdim    /**
746234353Sdim     * Bound-check incorporation action. A newly added bound is checked against existing bounds,
747234353Sdim     * to verify its compatibility; each bound is checked using either subtyping or type equivalence.
748234353Sdim     */
749234353Sdim    class CheckBounds extends IncorporationAction {
750234353Sdim
751234353Sdim        InferenceBound from;
752234353Sdim        BiFunction<InferenceContext, Type, Type> typeFunc;
753234353Sdim        BiPredicate<InferenceContext, Type> optFilter;
754234353Sdim
755234353Sdim        CheckBounds(UndetVar uv, Type t, InferenceBound from) {
756234353Sdim            this(uv, t, InferenceContext::asUndetVar, null, from);
757234353Sdim        }
758234353Sdim
759234353Sdim        CheckBounds(UndetVar uv, Type t, BiFunction<InferenceContext, Type, Type> typeFunc,
760234353Sdim                    BiPredicate<InferenceContext, Type> typeFilter, InferenceBound from) {
761234353Sdim            super(uv, t);
762234353Sdim            this.from = from;
763234353Sdim            this.typeFunc = typeFunc;
764234353Sdim            this.optFilter = typeFilter;
765234353Sdim        }
766234353Sdim
767234353Sdim        @Override
768234353Sdim        public IncorporationAction dup(UndetVar that) {
769234353Sdim            return new CheckBounds(that, t, typeFunc, optFilter, from);
770234353Sdim        }
771212795Sdim
772234353Sdim        @Override
773234353Sdim        void apply(InferenceContext inferenceContext, Warner warn) {
774234353Sdim            t = typeFunc.apply(inferenceContext, t);
775234353Sdim            if (optFilter != null && optFilter.test(inferenceContext, t)) return;
776234353Sdim            for (InferenceBound to : boundsToCheck()) {
777234353Sdim                for (Type b : uv.getBounds(to)) {
778234353Sdim                    b = typeFunc.apply(inferenceContext, b);
779234353Sdim                    if (optFilter != null && optFilter.test(inferenceContext, b)) continue;
780212795Sdim                    boolean success = checkBound(t, b, from, to, warn);
781234353Sdim                    if (!success) {
782234353Sdim                        report(from, to);
783234353Sdim                    }
784234353Sdim                }
785234353Sdim            }
786234353Sdim        }
787234353Sdim
788234353Sdim        /**
789234353Sdim         * The list of bound kinds to be checked.
790234353Sdim         */
791234353Sdim        EnumSet<InferenceBound> boundsToCheck() {
792212795Sdim            return (from == InferenceBound.EQ) ?
793212795Sdim                            EnumSet.allOf(InferenceBound.class) :
794212795Sdim                            EnumSet.complementOf(EnumSet.of(from));
795212795Sdim        }
796212795Sdim
797212795Sdim        /**
798212795Sdim         * Is source type 's' compatible with target type 't' given source and target bound kinds?
799212795Sdim         */
800212795Sdim        boolean checkBound(Type s, Type t, InferenceBound ib_s, InferenceBound ib_t, Warner warn) {
801263508Sdim            if (ib_s.lessThan(ib_t)) {
802263508Sdim                return isSubtype(s, t, warn);
803212795Sdim            } else if (ib_t.lessThan(ib_s)) {
804212795Sdim                return isSubtype(t, s, warn);
805212795Sdim            } else {
806234353Sdim                return isSameType(s, t);
807212795Sdim            }
808234353Sdim        }
809234353Sdim
810234353Sdim        /**
811234353Sdim         * Report a bound check error.
812234353Sdim         */
813234353Sdim        void report(InferenceBound from, InferenceBound to) {
814234353Sdim            //this is a workaround to preserve compatibility with existing messages
815234353Sdim            if (from == to) {
816234353Sdim                reportBoundError(uv, from);
817234353Sdim            } else if (from == InferenceBound.LOWER || to == InferenceBound.EQ) {
818234353Sdim                reportBoundError(uv, to, from);
819234353Sdim            } else {
820234353Sdim                reportBoundError(uv, from, to);
821234353Sdim            }
822234353Sdim        }
823234353Sdim
824234353Sdim        @Override
825234353Sdim        public String toString() {
826234353Sdim            return String.format("%s[undet=%s,t=%s,bound=%s]", getClass().getSimpleName(), uv.qtype, t, from);
827234353Sdim        }
828234353Sdim    }
829234353Sdim
830234353Sdim    /**
831234353Sdim     * Custom check executed by the legacy incorporation engine. Newly added bounds are checked
832234353Sdim     * against existing eq bounds.
833234353Sdim     */
834234353Sdim    class EqCheckLegacy extends CheckBounds {
835212795Sdim        EqCheckLegacy(UndetVar uv, Type t, InferenceBound from) {
836212795Sdim            super(uv, t, InferenceContext::asInstType, InferenceContext::free, from);
837212795Sdim        }
838212795Sdim
839212795Sdim        @Override
840212795Sdim        public IncorporationAction dup(UndetVar that) {
841212795Sdim            return new EqCheckLegacy(that, t, from);
842212795Sdim        }
843234353Sdim
844234353Sdim        @Override
845234353Sdim        EnumSet<InferenceBound> boundsToCheck() {
846234353Sdim            return (from == InferenceBound.EQ) ?
847234353Sdim                            EnumSet.allOf(InferenceBound.class) :
848234353Sdim                            EnumSet.of(InferenceBound.EQ);
849234353Sdim        }
850234353Sdim    }
851234353Sdim
852226633Sdim    /**
853212795Sdim     * Check that the inferred type conforms to all bounds.
854226633Sdim     */
855212795Sdim    class CheckInst extends CheckBounds {
856212795Sdim
857226633Sdim        EnumSet<InferenceBound> to;
858226633Sdim
859212795Sdim        CheckInst(UndetVar uv, InferenceBound ib, InferenceBound... rest) {
860212795Sdim            this(uv, EnumSet.of(ib, rest));
861218893Sdim        }
862212795Sdim
863226633Sdim        CheckInst(UndetVar uv, EnumSet<InferenceBound> to) {
864212795Sdim            super(uv, uv.getInst(), InferenceBound.EQ);
865212795Sdim            this.to = to;
866212795Sdim        }
867212795Sdim
868226633Sdim        @Override
869212795Sdim        public IncorporationAction dup(UndetVar that) {
870212795Sdim            return new CheckInst(that, to);
871212795Sdim        }
872212795Sdim
873218893Sdim        @Override
874234353Sdim        EnumSet<InferenceBound> boundsToCheck() {
875218893Sdim            return to;
876212795Sdim        }
877212795Sdim
878212795Sdim        @Override
879212795Sdim        void report(InferenceBound from, InferenceBound to) {
880212795Sdim            reportInstError(uv, to);
881212795Sdim        }
882212795Sdim    }
883212795Sdim
884226633Sdim    /**
885226633Sdim     * Replace undetvars in bounds and check that the inferred type conforms to all bounds.
886226633Sdim     */
887226633Sdim    class SubstBounds extends CheckInst {
888226633Sdim        SubstBounds(UndetVar uv) {
889212795Sdim            super(uv, InferenceBound.LOWER, InferenceBound.EQ, InferenceBound.UPPER);
890212795Sdim        }
891212795Sdim
892212795Sdim        @Override
893226633Sdim        public IncorporationAction dup(UndetVar that) {
894212795Sdim            return new SubstBounds(that);
895212795Sdim        }
896212795Sdim
897212795Sdim        @Override
898226633Sdim        void apply(InferenceContext inferenceContext, Warner warn) {
899234353Sdim            for (Type undet : inferenceContext.undetvars) {
900212795Sdim                //we could filter out variables not mentioning uv2...
901212795Sdim                UndetVar uv2 = (UndetVar)undet;
902212795Sdim                uv2.substBounds(List.of(uv.qtype), List.of(uv.getInst()), types);
903212795Sdim                checkCompatibleUpperBounds(uv2, inferenceContext);
904226633Sdim            }
905251662Sdim            super.apply(inferenceContext, warn);
906234353Sdim        }
907234353Sdim
908243830Sdim        /**
909243830Sdim         * Make sure that the upper bounds we got so far lead to a solvable inference
910212795Sdim         * variable by making sure that a glb exists.
911218893Sdim         */
912212795Sdim        void checkCompatibleUpperBounds(UndetVar uv, InferenceContext inferenceContext) {
913212795Sdim            List<Type> hibounds =
914212795Sdim                    Type.filter(uv.getBounds(InferenceBound.UPPER), new BoundFilter(inferenceContext));
915212795Sdim            final Type hb;
916212795Sdim            if (hibounds.isEmpty())
917218893Sdim                hb = syms.objectType;
918226633Sdim            else if (hibounds.tail.isEmpty())
919226633Sdim                hb = hibounds.head;
920218893Sdim            else
921218893Sdim                hb = types.glb(hibounds);
922218893Sdim            if (hb == null || hb.isErroneous())
923212795Sdim                reportBoundError(uv, InferenceBound.UPPER);
924212795Sdim        }
925212795Sdim    }
926212795Sdim
927239462Sdim    /**
928239462Sdim     * Perform pairwise comparison between common generic supertypes of two upper bounds.
929239462Sdim     */
930239462Sdim    class CheckUpperBounds extends IncorporationAction {
931239462Sdim
932212795Sdim        public CheckUpperBounds(UndetVar uv, Type t) {
933226633Sdim            super(uv, t);
934226633Sdim        }
935212795Sdim
936263508Sdim        @Override
937212795Sdim        public IncorporationAction dup(UndetVar that) {
938212795Sdim            return new CheckUpperBounds(that, t);
939251662Sdim        }
940251662Sdim
941251662Sdim        @Override
942251662Sdim        void apply(InferenceContext inferenceContext, Warner warn) {
943251662Sdim            List<Type> boundList = uv.getBounds(InferenceBound.UPPER).stream()
944251662Sdim                    .collect(types.closureCollector(true, types::isSameType));
945218893Sdim            for (Type b2 : boundList) {
946218893Sdim                if (t == b2) continue;
947218893Sdim                    /* This wildcard check is temporary workaround. This code may need to be
948218893Sdim                     * revisited once spec bug JDK-7034922 is fixed.
949218893Sdim                     */
950226633Sdim                if (t != b2 && !t.hasTag(WILDCARD) && !b2.hasTag(WILDCARD)) {
951218893Sdim                    for (Pair<Type, Type> commonSupers : getParameterizedSupers(t, b2)) {
952218893Sdim                        List<Type> allParamsSuperBound1 = commonSupers.fst.allparams();
953226633Sdim                        List<Type> allParamsSuperBound2 = commonSupers.snd.allparams();
954218893Sdim                        while (allParamsSuperBound1.nonEmpty() && allParamsSuperBound2.nonEmpty()) {
955218893Sdim                            //traverse the list of all params comparing them
956263508Sdim                            if (!allParamsSuperBound1.head.hasTag(WILDCARD) &&
957234353Sdim                                    !allParamsSuperBound2.head.hasTag(WILDCARD)) {
958212795Sdim                                if (!isSameType(inferenceContext.asUndetVar(allParamsSuperBound1.head),
959249423Sdim                                        inferenceContext.asUndetVar(allParamsSuperBound2.head))) {
960221345Sdim                                    reportBoundError(uv, InferenceBound.UPPER);
961251662Sdim                                }
962234353Sdim                            }
963221345Sdim                            allParamsSuperBound1 = allParamsSuperBound1.tail;
964221345Sdim                            allParamsSuperBound2 = allParamsSuperBound2.tail;
965221345Sdim                        }
966224145Sdim                        Assert.check(allParamsSuperBound1.isEmpty() && allParamsSuperBound2.isEmpty());
967243830Sdim                    }
968263508Sdim                }
969263508Sdim            }
970263508Sdim        }
971263508Sdim    }
972263508Sdim
973263508Sdim    /**
974278788Sdim     * Perform propagation of bounds. Given a constraint of the kind {@code alpha <: T}, three
975263508Sdim     * kind of propagation occur:
976263508Sdim     *
977263508Sdim     * <li>T is copied into all matching bounds (i.e. lower/eq bounds) B of alpha such that B=beta (forward propagation)</li>
978234353Sdim     * <li>if T=beta, matching bounds (i.e. upper bounds) of beta are copied into alpha (backwards propagation)</li>
979263508Sdim     * <li>if T=beta, sets a symmetric bound on beta (i.e. beta :> alpha) (symmetric propagation) </li>
980234353Sdim     */
981234353Sdim    class PropagateBounds extends IncorporationAction {
982234353Sdim
983218893Sdim        InferenceBound ib;
984234353Sdim
985234353Sdim        public PropagateBounds(UndetVar uv, Type t, InferenceBound ib) {
986234353Sdim            super(uv, t);
987234353Sdim            this.ib = ib;
988234353Sdim        }
989234353Sdim
990212795Sdim        @Override
991263508Sdim        public IncorporationAction dup(UndetVar that) {
992263508Sdim            return new PropagateBounds(that, t, ib);
993263508Sdim        }
994263508Sdim
995263508Sdim        void apply(InferenceContext inferenceContext, Warner warner) {
996263508Sdim            Type undetT = inferenceContext.asUndetVar(t);
997263508Sdim            if (undetT.hasTag(UNDETVAR) && !((UndetVar)undetT).isCaptured()) {
998263508Sdim                UndetVar uv2 = (UndetVar)undetT;
999263508Sdim                //symmetric propagation
1000263508Sdim                uv2.addBound(ib.complement(), uv, types);
1001226633Sdim                //backwards propagation
1002212795Sdim                for (InferenceBound ib2 : backwards()) {
1003218893Sdim                    for (Type b : uv2.getBounds(ib2)) {
1004226633Sdim                        uv.addBound(ib2, b, types);
1005263508Sdim                    }
1006212795Sdim                }
1007263508Sdim            }
1008263508Sdim            //forward propagation
1009263508Sdim            for (InferenceBound ib2 : forward()) {
1010212795Sdim                for (Type l : uv.getBounds(ib2)) {
1011212795Sdim                    Type undet = inferenceContext.asUndetVar(l);
1012212795Sdim                    if (undet.hasTag(TypeTag.UNDETVAR) && !((UndetVar)undet).isCaptured()) {
1013212795Sdim                        UndetVar uv2 = (UndetVar)undet;
1014212795Sdim                        uv2.addBound(ib, inferenceContext.asInstType(t), types);
1015212795Sdim                    }
1016212795Sdim                }
1017212795Sdim            }
1018221345Sdim        }
1019221345Sdim
1020221345Sdim        EnumSet<InferenceBound> forward() {
1021221345Sdim            return (ib == InferenceBound.EQ) ?
1022221345Sdim                    EnumSet.of(InferenceBound.EQ) : EnumSet.complementOf(EnumSet.of(ib));
1023221345Sdim        }
1024221345Sdim
1025221345Sdim        EnumSet<InferenceBound> backwards() {
1026221345Sdim            return (ib == InferenceBound.EQ) ?
1027221345Sdim                    EnumSet.allOf(InferenceBound.class) : EnumSet.of(ib);
1028221345Sdim        }
1029221345Sdim
1030221345Sdim        @Override
1031212795Sdim        public String toString() {
1032218893Sdim            return String.format("%s[undet=%s,t=%s,bound=%s]", getClass().getSimpleName(), uv.qtype, t, ib);
1033263508Sdim        }
1034263508Sdim    }
1035263508Sdim
1036212795Sdim    /**
1037212795Sdim     * This class models an incorporation engine. The engine is responsible for listening to
1038212795Sdim     * changes in inference variables and register incorporation actions accordingly.
1039212795Sdim     */
1040218893Sdim    abstract class AbstractIncorporationEngine implements UndetVarListener {
1041221345Sdim
1042212795Sdim        @Override
1043212795Sdim        public void varInstantiated(UndetVar uv) {
1044212795Sdim            uv.incorporationActions.addFirst(new SubstBounds(uv));
1045212795Sdim        }
1046218893Sdim
1047218893Sdim        @Override
1048212795Sdim        public void varBoundChanged(UndetVar uv, InferenceBound ib, Type bound, boolean update) {
1049226633Sdim            if (uv.isCaptured()) return;
1050212795Sdim            uv.incorporationActions.addAll(getIncorporationActions(uv, ib, bound, update));
1051212795Sdim        }
1052226633Sdim
1053226633Sdim        abstract List<IncorporationAction> getIncorporationActions(UndetVar uv, InferenceBound ib, Type t, boolean update);
1054218893Sdim    }
1055234982Sdim
1056234982Sdim    /**
1057234982Sdim     * A legacy incorporation engine. Used for source <= 7.
1058234982Sdim     */
1059218893Sdim    AbstractIncorporationEngine legacyEngine = new AbstractIncorporationEngine() {
1060218893Sdim
1061226633Sdim        List<IncorporationAction> getIncorporationActions(UndetVar uv, InferenceBound ib, Type t, boolean update) {
1062218893Sdim            ListBuffer<IncorporationAction> actions = new ListBuffer<>();
1063218893Sdim            Type inst = uv.getInst();
1064226633Sdim            if (inst != null) {
1065218893Sdim                actions.add(new CheckInst(uv, ib));
1066218893Sdim            }
1067218893Sdim            actions.add(new EqCheckLegacy(uv, t, ib));
1068218893Sdim            return actions.toList();
1069218893Sdim        }
1070218893Sdim    };
1071218893Sdim
1072226633Sdim    /**
1073218893Sdim     * The standard incorporation engine. Used for source >= 8.
1074212795Sdim     */
1075212795Sdim    AbstractIncorporationEngine graphEngine = new AbstractIncorporationEngine() {
1076251662Sdim
1077251662Sdim        @Override
1078251662Sdim        List<IncorporationAction> getIncorporationActions(UndetVar uv, InferenceBound ib, Type t, boolean update) {
1079251662Sdim            ListBuffer<IncorporationAction> actions = new ListBuffer<>();
1080251662Sdim            Type inst = uv.getInst();
1081251662Sdim            if (inst != null) {
1082251662Sdim                actions.add(new CheckInst(uv, ib));
1083212795Sdim            }
1084212795Sdim            actions.add(new CheckBounds(uv, t, ib));
1085212795Sdim
1086221345Sdim            if (update) {
1087221345Sdim                return actions.toList();
1088212795Sdim            }
1089212795Sdim
1090218893Sdim            if (ib == InferenceBound.UPPER) {
1091218893Sdim                actions.add(new CheckUpperBounds(uv, t));
1092221345Sdim            }
1093218893Sdim
1094218893Sdim            actions.add(new PropagateBounds(uv, t, ib));
1095218893Sdim
1096212795Sdim            return actions.toList();
1097234353Sdim        }
1098212795Sdim    };
1099234353Sdim
1100221345Sdim    /**
1101221345Sdim     * Get the incorporation engine to be used in this compilation.
1102263508Sdim     */
1103234353Sdim    AbstractIncorporationEngine incorporationEngine() {
1104212795Sdim        return allowGraphInference ? graphEngine : legacyEngine;
1105234353Sdim    }
1106234353Sdim
1107234353Sdim    /** max number of incorporation rounds. */
1108234353Sdim    static final int MAX_INCORPORATION_STEPS = 10000;
1109234353Sdim
1110234353Sdim    /**
1111234353Sdim     * Check bounds and perform incorporation.
1112234353Sdim     */
1113234353Sdim    void doIncorporation(InferenceContext inferenceContext, Warner warn) throws InferenceException {
1114234353Sdim        try {
1115263508Sdim            boolean progress = true;
1116234353Sdim            int round = 0;
1117212795Sdim            while (progress && round < MAX_INCORPORATION_STEPS) {
1118212795Sdim                progress = false;
1119212795Sdim                for (Type t : inferenceContext.undetvars) {
1120212795Sdim                    UndetVar uv = (UndetVar)t;
1121218893Sdim                    if (!uv.incorporationActions.isEmpty()) {
1122218893Sdim                        progress = true;
1123219077Sdim                        uv.incorporationActions.removeFirst().apply(inferenceContext, warn);
1124226633Sdim                    }
1125212795Sdim                }
1126212795Sdim                round++;
1127212795Sdim            }
1128212795Sdim        } finally {
1129263508Sdim            incorporationCache.clear();
1130219077Sdim        }
1131218893Sdim    }
1132234353Sdim
1133263508Sdim    /* If for two types t and s there is a least upper bound that contains
1134226633Sdim     * parameterized types G1, G2 ... Gn, then there exists supertypes of 't' of the form
1135226633Sdim     * G1<T1, ..., Tn>, G2<T1, ..., Tn>, ... Gn<T1, ..., Tn> and supertypes of 's' of the form
1136212795Sdim     * G1<S1, ..., Sn>, G2<S1, ..., Sn>, ... Gn<S1, ..., Sn> which will be returned by this method.
1137212795Sdim     * If no such common supertypes exists then an empty list is returned.
1138212795Sdim     *
1139263508Sdim     * As an example for the following input:
1140212795Sdim     *
1141226633Sdim     * t = java.util.ArrayList<java.lang.String>
1142226633Sdim     * s = java.util.List<T>
1143226633Sdim     *
1144212795Sdim     * we get this ouput (singleton list):
1145226633Sdim     *
1146263508Sdim     * [Pair[java.util.List<java.lang.String>,java.util.List<T>]]
1147212795Sdim     */
1148212795Sdim    private List<Pair<Type, Type>> getParameterizedSupers(Type t, Type s) {
1149212795Sdim        Type lubResult = types.lub(t, s);
1150212795Sdim        if (lubResult == syms.errType || lubResult == syms.botType) {
1151218893Sdim            return List.nil();
1152218893Sdim        }
1153219077Sdim        List<Type> supertypesToCheck = lubResult.isIntersection() ?
1154226633Sdim                ((IntersectionClassType)lubResult).getComponents() :
1155226633Sdim                List.of(lubResult);
1156212795Sdim        ListBuffer<Pair<Type, Type>> commonSupertypes = new ListBuffer<>();
1157212795Sdim        for (Type sup : supertypesToCheck) {
1158212795Sdim            if (sup.isParameterized()) {
1159212795Sdim                Type asSuperOfT = asSuper(t, sup);
1160218893Sdim                Type asSuperOfS = asSuper(s, sup);
1161219077Sdim                commonSupertypes.add(new Pair<>(asSuperOfT, asSuperOfS));
1162218893Sdim            }
1163212795Sdim        }
1164212795Sdim        return commonSupertypes.toList();
1165212795Sdim    }
1166212795Sdim    //where
1167212795Sdim        private Type asSuper(Type t, Type sup) {
1168218893Sdim            return (sup.hasTag(ARRAY)) ?
1169219077Sdim                    new ArrayType(asSuper(types.elemtype(t), types.elemtype(sup)), syms.arrayClass) :
1170212795Sdim                    types.asSuper(t, sup.tsym);
1171212795Sdim        }
1172218893Sdim
1173218893Sdim    boolean doIncorporationOp(IncorporationBinaryOpKind opKind, Type op1, Type op2, Warner warn) {
1174218893Sdim            IncorporationBinaryOp newOp = new IncorporationBinaryOp(opKind, op1, op2);
1175234353Sdim            Boolean res = incorporationCache.get(newOp);
1176218893Sdim            if (res == null) {
1177249423Sdim                incorporationCache.put(newOp, res = newOp.apply(warn));
1178218893Sdim            }
1179218893Sdim            return res;
1180218893Sdim        }
1181218893Sdim
1182218893Sdim    /**
1183221345Sdim     * Three kinds of basic operation are supported as part of an incorporation step:
1184221345Sdim     * (i) subtype check, (ii) same type check and (iii) bound addition (either
1185221345Sdim     * upper/lower/eq bound).
1186221345Sdim     */
1187221345Sdim    enum IncorporationBinaryOpKind {
1188223017Sdim        IS_SUBTYPE() {
1189234353Sdim            @Override
1190239462Sdim            boolean apply(Type op1, Type op2, Warner warn, Types types) {
1191249423Sdim                return types.isSubtypeUnchecked(op1, op2, warn);
1192249423Sdim            }
1193249423Sdim        },
1194249423Sdim        IS_SAME_TYPE() {
1195249423Sdim            @Override
1196249423Sdim            boolean apply(Type op1, Type op2, Warner warn, Types types) {
1197249423Sdim                return types.isSameType(op1, op2);
1198249423Sdim            }
1199249423Sdim        };
1200226633Sdim
1201234353Sdim        abstract boolean apply(Type op1, Type op2, Warner warn, Types types);
1202234353Sdim    }
1203221345Sdim
1204218893Sdim    /**
1205223017Sdim     * This class encapsulates a basic incorporation operation; incorporation
1206249423Sdim     * operations takes two type operands and a kind. Each operation performed
1207249423Sdim     * during an incorporation round is stored in a cache, so that operations
1208249423Sdim     * are not executed unnecessarily (which would potentially lead to adding
1209249423Sdim     * same bounds over and over).
1210249423Sdim     */
1211218893Sdim    class IncorporationBinaryOp {
1212218893Sdim
1213218893Sdim        IncorporationBinaryOpKind opKind;
1214226633Sdim        Type op1;
1215218893Sdim        Type op2;
1216218893Sdim
1217226633Sdim        IncorporationBinaryOp(IncorporationBinaryOpKind opKind, Type op1, Type op2) {
1218218893Sdim            this.opKind = opKind;
1219226633Sdim            this.op1 = op1;
1220226633Sdim            this.op2 = op2;
1221218893Sdim        }
1222263508Sdim
1223263508Sdim        @Override
1224234353Sdim        public boolean equals(Object o) {
1225234353Sdim            if (!(o instanceof IncorporationBinaryOp)) {
1226234353Sdim                return false;
1227234353Sdim            } else {
1228234353Sdim                IncorporationBinaryOp that = (IncorporationBinaryOp)o;
1229263508Sdim                return opKind == that.opKind &&
1230263508Sdim                        types.isSameType(op1, that.op1, true) &&
1231234353Sdim                        types.isSameType(op2, that.op2, true);
1232234353Sdim            }
1233234353Sdim        }
1234234353Sdim
1235234353Sdim        @Override
1236234353Sdim        public int hashCode() {
1237234353Sdim            int result = opKind.hashCode();
1238243830Sdim            result *= 127;
1239234353Sdim            result += types.hashCode(op1);
1240234353Sdim            result *= 127;
1241234353Sdim            result += types.hashCode(op2);
1242234353Sdim            return result;
1243263508Sdim        }
1244263508Sdim
1245263508Sdim        boolean apply(Warner warn) {
1246263508Sdim            return opKind.apply(op1, op2, warn, types);
1247263508Sdim        }
1248263508Sdim    }
1249263508Sdim
1250263508Sdim    /** an incorporation cache keeps track of all executed incorporation-related operations */
1251263508Sdim    Map<IncorporationBinaryOp, Boolean> incorporationCache = new HashMap<>();
1252263508Sdim
1253263508Sdim    protected static class BoundFilter implements Filter<Type> {
1254218893Sdim
1255212795Sdim        InferenceContext inferenceContext;
1256218893Sdim
1257212795Sdim        public BoundFilter(InferenceContext inferenceContext) {
1258263508Sdim            this.inferenceContext = inferenceContext;
1259263508Sdim        }
1260263508Sdim
1261212795Sdim        @Override
1262226633Sdim        public boolean accepts(Type t) {
1263263508Sdim            return !t.isErroneous() && !inferenceContext.free(t) &&
1264263508Sdim                    !t.hasTag(BOT);
1265234353Sdim        }
1266234353Sdim    }
1267234353Sdim
1268234353Sdim    /**
1269243830Sdim     * Incorporation error: mismatch between inferred type and given bound.
1270263508Sdim     */
1271263508Sdim    void reportInstError(UndetVar uv, InferenceBound ib) {
1272234353Sdim        reportInferenceError(
1273234353Sdim                String.format("inferred.do.not.conform.to.%s.bounds", StringUtils.toLowerCase(ib.name())),
1274263508Sdim                uv.getInst(),
1275263508Sdim                uv.getBounds(ib));
1276263508Sdim    }
1277234353Sdim
1278234353Sdim    /**
1279263508Sdim     * Incorporation error: mismatch between two (or more) bounds of same kind.
1280263508Sdim     */
1281263508Sdim    void reportBoundError(UndetVar uv, InferenceBound ib) {
1282263508Sdim        reportInferenceError(
1283263508Sdim                String.format("incompatible.%s.bounds", StringUtils.toLowerCase(ib.name())),
1284263508Sdim                uv.qtype,
1285263508Sdim                uv.getBounds(ib));
1286263508Sdim    }
1287263508Sdim
1288263508Sdim    /**
1289263508Sdim     * Incorporation error: mismatch between two (or more) bounds of different kinds.
1290263508Sdim     */
1291263508Sdim    void reportBoundError(UndetVar uv, InferenceBound ib1, InferenceBound ib2) {
1292263508Sdim        reportInferenceError(
1293263508Sdim                String.format("incompatible.%s.%s.bounds",
1294263508Sdim                        StringUtils.toLowerCase(ib1.name()),
1295263508Sdim                        StringUtils.toLowerCase(ib2.name())),
1296263508Sdim                uv.qtype,
1297263508Sdim                uv.getBounds(ib1),
1298263508Sdim                uv.getBounds(ib2));
1299234353Sdim    }
1300234353Sdim
1301263508Sdim    /**
1302234353Sdim     * Helper method: reports an inference error.
1303212795Sdim     */
1304212795Sdim    void reportInferenceError(String key, Object... args) {
1305212795Sdim        throw inferenceException.setMessage(key, args);
1306212795Sdim    }
1307212795Sdim    // </editor-fold>
1308212795Sdim
1309212795Sdim    // <editor-fold defaultstate="collapsed" desc="Inference engine">
1310212795Sdim    /**
1311226633Sdim     * Graph inference strategy - act as an input to the inference solver; a strategy is
1312212795Sdim     * composed of two ingredients: (i) find a node to solve in the inference graph,
1313212795Sdim     * and (ii) tell th engine when we are done fixing inference variables
1314226633Sdim     */
1315212795Sdim    interface GraphStrategy {
1316218893Sdim
1317218893Sdim        /**
1318218893Sdim         * A NodeNotFoundException is thrown whenever an inference strategy fails
1319218893Sdim         * to pick the next node to solve in the inference graph.
1320212795Sdim         */
1321212795Sdim        public static class NodeNotFoundException extends RuntimeException {
1322212795Sdim            private static final long serialVersionUID = 0;
1323218893Sdim
1324263508Sdim            InferenceGraph graph;
1325218893Sdim
1326263508Sdim            public NodeNotFoundException(InferenceGraph graph) {
1327263508Sdim                this.graph = graph;
1328263508Sdim            }
1329263508Sdim        }
1330218893Sdim        /**
1331263508Sdim         * Pick the next node (leaf) to solve in the graph
1332263508Sdim         */
1333212795Sdim        Node pickNode(InferenceGraph g) throws NodeNotFoundException;
1334212795Sdim        /**
1335212795Sdim         * Is this the last step?
1336212795Sdim         */
1337212795Sdim        boolean done();
1338212795Sdim    }
1339212795Sdim
1340212795Sdim    /**
1341226633Sdim     * Simple solver strategy class that locates all leaves inside a graph
1342226633Sdim     * and picks the first leaf as the next node to solve
1343212795Sdim     */
1344212795Sdim    abstract class LeafSolver implements GraphStrategy {
1345212795Sdim        public Node pickNode(InferenceGraph g) {
1346212795Sdim            if (g.nodes.isEmpty()) {
1347212795Sdim                //should not happen
1348212795Sdim                throw new NodeNotFoundException(g);
1349212795Sdim            }
1350218893Sdim            return g.nodes.get(0);
1351218893Sdim        }
1352212795Sdim    }
1353212795Sdim
1354212795Sdim    /**
1355212795Sdim     * This solver uses an heuristic to pick the best leaf - the heuristic
1356212795Sdim     * tries to select the node that has maximal probability to contain one
1357226633Sdim     * or more inference variables in a given list
1358212795Sdim     */
1359212795Sdim    abstract class BestLeafSolver extends LeafSolver {
1360212795Sdim
1361212795Sdim        /** list of ivars of which at least one must be solved */
1362212795Sdim        List<Type> varsToSolve;
1363212795Sdim
1364212795Sdim        BestLeafSolver(List<Type> varsToSolve) {
1365234353Sdim            this.varsToSolve = varsToSolve;
1366234353Sdim        }
1367234353Sdim
1368234353Sdim        /**
1369234353Sdim         * Computes a path that goes from a given node to the leafs in the graph.
1370234353Sdim         * Typically this will start from a node containing a variable in
1371234353Sdim         * {@code varsToSolve}. For any given path, the cost is computed as the total
1372243830Sdim         * number of type-variables that should be eagerly instantiated across that path.
1373234353Sdim         */
1374234353Sdim        Pair<List<Node>, Integer> computeTreeToLeafs(Node n) {
1375212795Sdim            Pair<List<Node>, Integer> cachedPath = treeCache.get(n);
1376212795Sdim            if (cachedPath == null) {
1377218893Sdim                //cache miss
1378212795Sdim                if (n.isLeaf()) {
1379212795Sdim                    //if leaf, stop
1380212795Sdim                    cachedPath = new Pair<>(List.of(n), n.data.length());
1381212795Sdim                } else {
1382249423Sdim                    //if non-leaf, proceed recursively
1383249423Sdim                    Pair<List<Node>, Integer> path = new Pair<>(List.of(n), n.data.length());
1384249423Sdim                    for (Node n2 : n.getAllDependencies()) {
1385218893Sdim                        if (n2 == n) continue;
1386249423Sdim                        Pair<List<Node>, Integer> subpath = computeTreeToLeafs(n2);
1387249423Sdim                        path = new Pair<>(path.fst.prependList(subpath.fst),
1388263508Sdim                                          path.snd + subpath.snd);
1389218893Sdim                    }
1390218893Sdim                    cachedPath = path;
1391212795Sdim                }
1392212795Sdim                //save results in cache
1393212795Sdim                treeCache.put(n, cachedPath);
1394212795Sdim            }
1395212795Sdim            return cachedPath;
1396212795Sdim        }
1397212795Sdim
1398212795Sdim        /** cache used to avoid redundant computation of tree costs */
1399218893Sdim        final Map<Node, Pair<List<Node>, Integer>> treeCache = new HashMap<>();
1400212795Sdim
1401226633Sdim        /** constant value used to mark non-existent paths */
1402212795Sdim        final Pair<List<Node>, Integer> noPath = new Pair<>(null, Integer.MAX_VALUE);
1403218893Sdim
1404218893Sdim        /**
1405212795Sdim         * Pick the leaf that minimize cost
1406212795Sdim         */
1407212795Sdim        @Override
1408212795Sdim        public Node pickNode(final InferenceGraph g) {
1409212795Sdim            treeCache.clear(); //graph changes at every step - cache must be cleared
1410226633Sdim            Pair<List<Node>, Integer> bestPath = noPath;
1411212795Sdim            for (Node n : g.nodes) {
1412218893Sdim                if (!Collections.disjoint(n.data, varsToSolve)) {
1413212795Sdim                    Pair<List<Node>, Integer> path = computeTreeToLeafs(n);
1414263508Sdim                    //discard all paths containing at least a node in the
1415263508Sdim                    //closure computed above
1416263508Sdim                    if (path.snd < bestPath.snd) {
1417212795Sdim                        bestPath = path;
1418212795Sdim                    }
1419234353Sdim                }
1420234353Sdim            }
1421234353Sdim            if (bestPath == noPath) {
1422212795Sdim                //no path leads there
1423234353Sdim                throw new NodeNotFoundException(g);
1424234353Sdim            }
1425234353Sdim            return bestPath.fst.head;
1426234353Sdim        }
1427234353Sdim    }
1428234353Sdim
1429234353Sdim    /**
1430234353Sdim     * The inference process can be thought of as a sequence of steps. Each step
1431234353Sdim     * instantiates an inference variable using a subset of the inference variable
1432234353Sdim     * bounds, if certain condition are met. Decisions such as the sequence in which
1433234353Sdim     * steps are applied, or which steps are to be applied are left to the inference engine.
1434234353Sdim     */
1435212795Sdim    enum InferenceStep {
1436226633Sdim
1437212795Sdim        /**
1438212795Sdim         * Instantiate an inference variables using one of its (ground) equality
1439234353Sdim         * constraints
1440212795Sdim         */
1441212795Sdim        EQ(InferenceBound.EQ) {
1442212795Sdim            @Override
1443212795Sdim            Type solve(UndetVar uv, InferenceContext inferenceContext) {
1444243830Sdim                return filterBounds(uv, inferenceContext).head;
1445218893Sdim            }
1446218893Sdim        },
1447243830Sdim        /**
1448243830Sdim         * Instantiate an inference variables using its (ground) lower bounds. Such
1449243830Sdim         * bounds are merged together using lub().
1450263508Sdim         */
1451263508Sdim        LOWER(InferenceBound.LOWER) {
1452263508Sdim            @Override
1453263508Sdim            Type solve(UndetVar uv, InferenceContext inferenceContext) {
1454263508Sdim                Infer infer = inferenceContext.infer;
1455234353Sdim                List<Type> lobounds = filterBounds(uv, inferenceContext);
1456212795Sdim                //note: lobounds should have at least one element
1457212795Sdim                Type owntype = lobounds.tail.tail == null  ? lobounds.head : infer.types.lub(lobounds);
1458212795Sdim                if (owntype.isPrimitive() || owntype.hasTag(ERROR)) {
1459234353Sdim                    throw infer.inferenceException
1460212795Sdim                        .setMessage("no.unique.minimal.instance.exists",
1461234353Sdim                                    uv.qtype, lobounds);
1462218893Sdim                } else {
1463218893Sdim                    return owntype;
1464226633Sdim                }
1465218893Sdim            }
1466218893Sdim        },
1467218893Sdim        /**
1468218893Sdim         * Infer uninstantiated/unbound inference variables occurring in 'throws'
1469218893Sdim         * clause as RuntimeException
1470226633Sdim         */
1471226633Sdim        THROWS(InferenceBound.UPPER) {
1472212795Sdim            @Override
1473218893Sdim            public boolean accepts(UndetVar t, InferenceContext inferenceContext) {
1474218893Sdim                if ((t.qtype.tsym.flags() & Flags.THROWS) == 0) {
1475218893Sdim                    //not a throws undet var
1476226633Sdim                    return false;
1477226633Sdim                }
1478212795Sdim                Infer infer = inferenceContext.infer;
1479243830Sdim                for (Type db : t.getBounds(InferenceBound.UPPER)) {
1480218893Sdim                    if (t.isInterface()) continue;
1481218893Sdim                    if (infer.types.asSuper(infer.syms.runtimeExceptionType, db.tsym) == null) {
1482218893Sdim                        //upper bound is not a supertype of RuntimeException - give up
1483234353Sdim                        return false;
1484218893Sdim                    }
1485226633Sdim                }
1486218893Sdim
1487218893Sdim                return true;
1488218893Sdim            }
1489218893Sdim
1490243830Sdim            @Override
1491212795Sdim            Type solve(UndetVar uv, InferenceContext inferenceContext) {
1492212795Sdim                return inferenceContext.infer.syms.runtimeExceptionType;
1493212795Sdim            }
1494263508Sdim        },
1495263508Sdim        /**
1496263508Sdim         * Instantiate an inference variables using its (ground) upper bounds. Such
1497263508Sdim         * bounds are merged together using glb().
1498263508Sdim         */
1499263508Sdim        UPPER(InferenceBound.UPPER) {
1500263508Sdim            @Override
1501263508Sdim            Type solve(UndetVar uv, InferenceContext inferenceContext) {
1502263508Sdim                Infer infer = inferenceContext.infer;
1503263508Sdim                List<Type> hibounds = filterBounds(uv, inferenceContext);
1504263508Sdim                //note: hibounds should have at least one element
1505263508Sdim                Type owntype = hibounds.tail.tail == null  ? hibounds.head : infer.types.glb(hibounds);
1506263508Sdim                if (owntype.isPrimitive() || owntype.hasTag(ERROR)) {
1507263508Sdim                    throw infer.inferenceException
1508263508Sdim                        .setMessage("no.unique.maximal.instance.exists",
1509263508Sdim                                    uv.qtype, hibounds);
1510263508Sdim                } else {
1511263508Sdim                    return owntype;
1512263508Sdim                }
1513263508Sdim            }
1514263508Sdim        },
1515263508Sdim        /**
1516263508Sdim         * Like the former; the only difference is that this step can only be applied
1517263508Sdim         * if all upper bounds are ground.
1518263508Sdim         */
1519263508Sdim        UPPER_LEGACY(InferenceBound.UPPER) {
1520263508Sdim            @Override
1521263508Sdim            public boolean accepts(UndetVar t, InferenceContext inferenceContext) {
1522263508Sdim                return !inferenceContext.free(t.getBounds(ib)) && !t.isCaptured();
1523263508Sdim            }
1524263508Sdim
1525263508Sdim            @Override
1526263508Sdim            Type solve(UndetVar uv, InferenceContext inferenceContext) {
1527263508Sdim                return UPPER.solve(uv, inferenceContext);
1528263508Sdim            }
1529263508Sdim        },
1530263508Sdim        /**
1531263508Sdim         * Like the former; the only difference is that this step can only be applied
1532263508Sdim         * if all upper/lower bounds are ground.
1533263508Sdim         */
1534263508Sdim        CAPTURED(InferenceBound.UPPER) {
1535218893Sdim            @Override
1536226633Sdim            public boolean accepts(UndetVar t, InferenceContext inferenceContext) {
1537226633Sdim                return t.isCaptured() &&
1538212795Sdim                        !inferenceContext.free(t.getBounds(InferenceBound.UPPER, InferenceBound.LOWER));
1539218893Sdim            }
1540212795Sdim
1541226633Sdim            @Override
1542218893Sdim            Type solve(UndetVar uv, InferenceContext inferenceContext) {
1543218893Sdim                Infer infer = inferenceContext.infer;
1544218893Sdim                Type upper = UPPER.filterBounds(uv, inferenceContext).nonEmpty() ?
1545218893Sdim                        UPPER.solve(uv, inferenceContext) :
1546218893Sdim                        infer.syms.objectType;
1547218893Sdim                Type lower = LOWER.filterBounds(uv, inferenceContext).nonEmpty() ?
1548218893Sdim                        LOWER.solve(uv, inferenceContext) :
1549218893Sdim                        infer.syms.botType;
1550218893Sdim                CapturedType prevCaptured = (CapturedType)uv.qtype;
1551218893Sdim                return new CapturedType(prevCaptured.tsym.name, prevCaptured.tsym.owner,
1552218893Sdim                                        upper, lower, prevCaptured.wildcard);
1553212795Sdim            }
1554212795Sdim        };
1555212795Sdim
1556226633Sdim        final InferenceBound ib;
1557218893Sdim
1558218893Sdim        InferenceStep(InferenceBound ib) {
1559218893Sdim            this.ib = ib;
1560218893Sdim        }
1561218893Sdim
1562243830Sdim        /**
1563243830Sdim         * Find an instantiated type for a given inference variable within
1564243830Sdim         * a given inference context
1565226633Sdim         */
1566243830Sdim        abstract Type solve(UndetVar uv, InferenceContext inferenceContext);
1567263508Sdim
1568263508Sdim        /**
1569263508Sdim         * Can the inference variable be instantiated using this step?
1570263508Sdim         */
1571263508Sdim        public boolean accepts(UndetVar t, InferenceContext inferenceContext) {
1572263508Sdim            return filterBounds(t, inferenceContext).nonEmpty() && !t.isCaptured();
1573243830Sdim        }
1574263508Sdim
1575263508Sdim        /**
1576243830Sdim         * Return the subset of ground bounds in a given bound set (i.e. eq/lower/upper)
1577263508Sdim         */
1578263508Sdim        List<Type> filterBounds(UndetVar uv, InferenceContext inferenceContext) {
1579263508Sdim            return Type.filter(uv.getBounds(ib), new BoundFilter(inferenceContext));
1580263508Sdim        }
1581263508Sdim    }
1582263508Sdim
1583263508Sdim    /**
1584263508Sdim     * This enumeration defines the sequence of steps to be applied when the
1585263508Sdim     * solver works in legacy mode. The steps in this enumeration reflect
1586263508Sdim     * the behavior of old inference routine (see JLS SE 7 15.12.2.7/15.12.2.8).
1587263508Sdim     */
1588263508Sdim    enum LegacyInferenceSteps {
1589263508Sdim
1590263508Sdim        EQ_LOWER(EnumSet.of(InferenceStep.EQ, InferenceStep.LOWER)),
1591263508Sdim        EQ_UPPER(EnumSet.of(InferenceStep.EQ, InferenceStep.UPPER_LEGACY));
1592263508Sdim
1593263508Sdim        final EnumSet<InferenceStep> steps;
1594263508Sdim
1595263508Sdim        LegacyInferenceSteps(EnumSet<InferenceStep> steps) {
1596212795Sdim            this.steps = steps;
1597212795Sdim        }
1598263508Sdim    }
1599263508Sdim
1600263508Sdim    /**
1601263508Sdim     * This enumeration defines the sequence of steps to be applied when the
1602263508Sdim     * graph solver is used. This order is defined so as to maximize compatibility
1603263508Sdim     * w.r.t. old inference routine (see JLS SE 7 15.12.2.7/15.12.2.8).
1604263508Sdim     */
1605263508Sdim    enum GraphInferenceSteps {
1606263508Sdim
1607263508Sdim        EQ(EnumSet.of(InferenceStep.EQ)),
1608263508Sdim        EQ_LOWER(EnumSet.of(InferenceStep.EQ, InferenceStep.LOWER)),
1609263508Sdim        EQ_LOWER_THROWS_UPPER_CAPTURED(EnumSet.of(InferenceStep.EQ, InferenceStep.LOWER, InferenceStep.UPPER, InferenceStep.THROWS, InferenceStep.CAPTURED));
1610212795Sdim
1611212795Sdim        final EnumSet<InferenceStep> steps;
1612212795Sdim
1613212795Sdim        GraphInferenceSteps(EnumSet<InferenceStep> steps) {
1614263508Sdim            this.steps = steps;
1615212795Sdim        }
1616218893Sdim    }
1617263508Sdim
1618218893Sdim    /**
1619212795Sdim     * There are two kinds of dependencies between inference variables. The basic
1620263508Sdim     * kind of dependency (or bound dependency) arises when a variable mention
1621218893Sdim     * another variable in one of its bounds. There's also a more subtle kind
1622226633Sdim     * of dependency that arises when a variable 'might' lead to better constraints
1623218893Sdim     * on another variable (this is typically the case with variables holding up
1624212795Sdim     * stuck expressions).
1625212795Sdim     */
1626212795Sdim    enum DependencyKind implements GraphUtils.DependencyKind {
1627226633Sdim
1628226633Sdim        /** bound dependency */
1629226633Sdim        BOUND("dotted"),
1630226633Sdim        /** stuck dependency */
1631226633Sdim        STUCK("dashed");
1632226633Sdim
1633212795Sdim        final String dotSyle;
1634234353Sdim
1635212795Sdim        private DependencyKind(String dotSyle) {
1636234353Sdim            this.dotSyle = dotSyle;
1637212795Sdim        }
1638212795Sdim    }
1639263508Sdim
1640263508Sdim    /**
1641263508Sdim     * This is the graph inference solver - the solver organizes all inference variables in
1642263508Sdim     * a given inference context by bound dependencies - in the general case, such dependencies
1643263508Sdim     * would lead to a cyclic directed graph (hence the name); the dependency info is used to build
1644263508Sdim     * an acyclic graph, where all cyclic variables are bundled together. An inference
1645263508Sdim     * step corresponds to solving a node in the acyclic graph - this is done by
1646263508Sdim     * relying on a given strategy (see GraphStrategy).
1647263508Sdim     */
1648263508Sdim    class GraphSolver {
1649263508Sdim
1650212795Sdim        InferenceContext inferenceContext;
1651212795Sdim        Warner warn;
1652212795Sdim
1653263508Sdim        GraphSolver(InferenceContext inferenceContext, Warner warn) {
1654263508Sdim            this.inferenceContext = inferenceContext;
1655263508Sdim            this.warn = warn;
1656263508Sdim        }
1657263508Sdim
1658263508Sdim        /**
1659263508Sdim         * Solve variables in a given inference context. The amount of variables
1660263508Sdim         * to be solved, and the way in which the underlying acyclic graph is explored
1661263508Sdim         * depends on the selected solver strategy.
1662263508Sdim         */
1663263508Sdim        void solve(GraphStrategy sstrategy) {
1664263508Sdim            doIncorporation(inferenceContext, warn); //initial propagation of bounds
1665263508Sdim            InferenceGraph inferenceGraph = new InferenceGraph();
1666263508Sdim            while (!sstrategy.done()) {
1667263508Sdim                if (dependenciesFolder != null) {
1668263508Sdim                    //add this graph to the pending queue
1669263508Sdim                    pendingGraphs = pendingGraphs.prepend(inferenceGraph.toDot());
1670263508Sdim                }
1671263508Sdim                InferenceGraph.Node nodeToSolve = sstrategy.pickNode(inferenceGraph);
1672263508Sdim                List<Type> varsToSolve = List.from(nodeToSolve.data);
1673263508Sdim                List<Type> saved_undet = inferenceContext.save();
1674263508Sdim                try {
1675263508Sdim                    //repeat until all variables are solved
1676263508Sdim                    outer: while (Type.containsAny(inferenceContext.restvars(), varsToSolve)) {
1677263508Sdim                        //for each inference phase
1678263508Sdim                        for (GraphInferenceSteps step : GraphInferenceSteps.values()) {
1679263508Sdim                            if (inferenceContext.solveBasic(varsToSolve, step.steps).nonEmpty()) {
1680263508Sdim                                doIncorporation(inferenceContext, warn);
1681263508Sdim                                continue outer;
1682263508Sdim                            }
1683263508Sdim                        }
1684263508Sdim                        //no progress
1685263508Sdim                        throw inferenceException.setMessage();
1686263508Sdim                    }
1687263508Sdim                }
1688263508Sdim                catch (InferenceException ex) {
1689263508Sdim                    //did we fail because of interdependent ivars?
1690263508Sdim                    inferenceContext.rollback(saved_undet);
1691263508Sdim                    instantiateAsUninferredVars(varsToSolve, inferenceContext);
1692263508Sdim                    doIncorporation(inferenceContext, warn);
1693263508Sdim                }
1694263508Sdim                inferenceGraph.deleteNode(nodeToSolve);
1695263508Sdim            }
1696263508Sdim        }
1697263508Sdim
1698263508Sdim        /**
1699263508Sdim         * The dependencies between the inference variables that need to be solved
1700263508Sdim         * form a (possibly cyclic) graph. This class reduces the original dependency graph
1701263508Sdim         * to an acyclic version, where cyclic nodes are folded into a single 'super node'.
1702263508Sdim         */
1703263508Sdim        class InferenceGraph {
1704263508Sdim
1705263508Sdim            /**
1706263508Sdim             * This class represents a node in the graph. Each node corresponds
1707263508Sdim             * to an inference variable and has edges (dependencies) on other
1708263508Sdim             * nodes. The node defines an entry point that can be used to receive
1709263508Sdim             * updates on the structure of the graph this node belongs to (used to
1710263508Sdim             * keep dependencies in sync).
1711263508Sdim             */
1712263508Sdim            class Node extends GraphUtils.TarjanNode<ListBuffer<Type>, Node> implements DottableNode<ListBuffer<Type>, Node> {
1713263508Sdim
1714263508Sdim                /** node dependencies */
1715263508Sdim                Set<Node> deps;
1716263508Sdim
1717263508Sdim                Node(Type ivar) {
1718263508Sdim                    super(ListBuffer.of(ivar));
1719263508Sdim                    this.deps = new HashSet<>();
1720263508Sdim                }
1721263508Sdim
1722263508Sdim                @Override
1723263508Sdim                public GraphUtils.DependencyKind[] getSupportedDependencyKinds() {
1724263508Sdim                    return new GraphUtils.DependencyKind[] { DependencyKind.BOUND };
1725263508Sdim                }
1726263508Sdim
1727263508Sdim                public Iterable<? extends Node> getAllDependencies() {
1728263508Sdim                    return deps;
1729263508Sdim                }
1730263508Sdim
1731263508Sdim                @Override
1732263508Sdim                public Collection<? extends Node> getDependenciesByKind(GraphUtils.DependencyKind dk) {
1733263508Sdim                    if (dk == DependencyKind.BOUND) {
1734263508Sdim                        return deps;
1735263508Sdim                    } else {
1736212795Sdim                        throw new IllegalStateException();
1737212795Sdim                    }
1738212795Sdim                }
1739212795Sdim
1740212795Sdim                /**
1741212795Sdim                 * Adds dependency with given kind.
1742218893Sdim                 */
1743212795Sdim                protected void addDependency(Node depToAdd) {
1744212795Sdim                    deps.add(depToAdd);
1745212795Sdim                }
1746212795Sdim
1747218893Sdim                /**
1748212795Sdim                 * Add multiple dependencies of same given kind.
1749212795Sdim                 */
1750212795Sdim                protected void addDependencies(Set<Node> depsToAdd) {
1751218893Sdim                    for (Node n : depsToAdd) {
1752218893Sdim                        addDependency(n);
1753218893Sdim                    }
1754226633Sdim                }
1755218893Sdim
1756218893Sdim                /**
1757218893Sdim                 * Remove a dependency, regardless of its kind.
1758218893Sdim                 */
1759218893Sdim                protected boolean removeDependency(Node n) {
1760218893Sdim                    return deps.remove(n);
1761218893Sdim                }
1762218893Sdim
1763218893Sdim                /**
1764218893Sdim                 * Compute closure of a give node, by recursively walking
1765218893Sdim                 * through all its dependencies (of given kinds)
1766212795Sdim                 */
1767212795Sdim                protected Set<Node> closure() {
1768212795Sdim                    boolean progress = true;
1769212795Sdim                    Set<Node> closure = new HashSet<>();
1770212795Sdim                    closure.add(this);
1771212795Sdim                    while (progress) {
1772212795Sdim                        progress = false;
1773243830Sdim                        for (Node n1 : new HashSet<>(closure)) {
1774243830Sdim                            progress = closure.addAll(n1.deps);
1775243830Sdim                        }
1776243830Sdim                    }
1777243830Sdim                    return closure;
1778243830Sdim                }
1779243830Sdim
1780243830Sdim                /**
1781243830Sdim                 * Is this node a leaf? This means either the node has no dependencies,
1782243830Sdim                 * or it just has self-dependencies.
1783243830Sdim                 */
1784243830Sdim                protected boolean isLeaf() {
1785212795Sdim                    //no deps, or only one self dep
1786212795Sdim                    if (deps.isEmpty()) return true;
1787223017Sdim                    for (Node n : deps) {
1788223017Sdim                        if (n != this) {
1789223017Sdim                            return false;
1790223017Sdim                        }
1791212795Sdim                    }
1792212795Sdim                    return true;
1793239462Sdim                }
1794239462Sdim
1795218893Sdim                /**
1796221345Sdim                 * Merge this node with another node, acquiring its dependencies.
1797212795Sdim                 * This routine is used to merge all cyclic node together and
1798212795Sdim                 * form an acyclic graph.
1799249423Sdim                 */
1800249423Sdim                protected void mergeWith(List<? extends Node> nodes) {
1801249423Sdim                    for (Node n : nodes) {
1802249423Sdim                        Assert.check(n.data.length() == 1, "Attempt to merge a compound node!");
1803212795Sdim                        data.appendList(n.data);
1804212795Sdim                        addDependencies(n.deps);
1805212795Sdim                    }
1806212795Sdim                    //update deps
1807212795Sdim                    Set<Node> deps2 = new HashSet<>();
1808212795Sdim                    for (Node d : deps) {
1809212795Sdim                        if (data.contains(d.data.first())) {
1810212795Sdim                            deps2.add(this);
1811234353Sdim                        } else {
1812234353Sdim                            deps2.add(d);
1813234353Sdim                        }
1814234353Sdim                    }
1815234353Sdim                    deps = deps2;
1816234353Sdim                }
1817234353Sdim
1818234353Sdim                /**
1819234353Sdim                 * Notify all nodes that something has changed in the graph
1820234353Sdim                 * topology.
1821234353Sdim                 */
1822218893Sdim                private void graphChanged(Node from, Node to) {
1823218893Sdim                    if (removeDependency(from)) {
1824218893Sdim                        if (to != null) {
1825218893Sdim                            addDependency(to);
1826239462Sdim                        }
1827234353Sdim                    }
1828234353Sdim                }
1829234353Sdim
1830234353Sdim                @Override
1831234353Sdim                public Properties nodeAttributes() {
1832234353Sdim                    Properties p = new Properties();
1833234353Sdim                    p.put("label", "\"" + toString() + "\"");
1834249423Sdim                    return p;
1835249423Sdim                }
1836234353Sdim
1837212795Sdim                @Override
1838234353Sdim                public Properties dependencyAttributes(Node sink, GraphUtils.DependencyKind dk) {
1839234353Sdim                    Properties p = new Properties();
1840234353Sdim                    p.put("style", ((DependencyKind)dk).dotSyle);
1841263508Sdim                    StringBuilder buf = new StringBuilder();
1842234353Sdim                    String sep = "";
1843234353Sdim                    for (Type from : data) {
1844234353Sdim                        UndetVar uv = (UndetVar)inferenceContext.asUndetVar(from);
1845212795Sdim                        for (Type bound : uv.getBounds(InferenceBound.values())) {
1846263508Sdim                            if (bound.containsAny(List.from(sink.data))) {
1847263508Sdim                                buf.append(sep);
1848263508Sdim                                buf.append(bound);
1849263508Sdim                                sep = ",";
1850263508Sdim                            }
1851263508Sdim                        }
1852263508Sdim                    }
1853263508Sdim                    p.put("label", "\"" + buf.toString() + "\"");
1854263508Sdim                    return p;
1855263508Sdim                }
1856263508Sdim            }
1857263508Sdim
1858263508Sdim            /** the nodes in the inference graph */
1859263508Sdim            ArrayList<Node> nodes;
1860263508Sdim
1861263508Sdim            InferenceGraph() {
1862263508Sdim                initNodes();
1863263508Sdim            }
1864263508Sdim
1865263508Sdim            /**
1866263508Sdim             * Basic lookup helper for retrieving a graph node given an inference
1867263508Sdim             * variable type.
1868263508Sdim             */
1869263508Sdim            public Node findNode(Type t) {
1870234353Sdim                for (Node n : nodes) {
1871263508Sdim                    if (n.data.contains(t)) {
1872263508Sdim                        return n;
1873263508Sdim                    }
1874263508Sdim                }
1875263508Sdim                return null;
1876263508Sdim            }
1877263508Sdim
1878263508Sdim            /**
1879263508Sdim             * Delete a node from the graph. This update the underlying structure
1880263508Sdim             * of the graph (including dependencies) via listeners updates.
1881263508Sdim             */
1882263508Sdim            public void deleteNode(Node n) {
1883263508Sdim                Assert.check(nodes.contains(n));
1884263508Sdim                nodes.remove(n);
1885263508Sdim                notifyUpdate(n, null);
1886263508Sdim            }
1887263508Sdim
1888263508Sdim            /**
1889263508Sdim             * Notify all nodes of a change in the graph. If the target node is
1890263508Sdim             * {@code null} the source node is assumed to be removed.
1891263508Sdim             */
1892263508Sdim            void notifyUpdate(Node from, Node to) {
1893263508Sdim                for (Node n : nodes) {
1894263508Sdim                    n.graphChanged(from, to);
1895263508Sdim                }
1896263508Sdim            }
1897263508Sdim
1898263508Sdim            /**
1899263508Sdim             * Create the graph nodes. First a simple node is created for every inference
1900263508Sdim             * variables to be solved. Then Tarjan is used to found all connected components
1901212795Sdim             * in the graph. For each component containing more than one node, a super node is
1902212795Sdim             * created, effectively replacing the original cyclic nodes.
1903212795Sdim             */
1904263508Sdim            void initNodes() {
1905263508Sdim                //add nodes
1906263508Sdim                nodes = new ArrayList<>();
1907263508Sdim                for (Type t : inferenceContext.restvars()) {
1908263508Sdim                    nodes.add(new Node(t));
1909263508Sdim                }
1910263508Sdim                //add dependencies
1911263508Sdim                for (Node n_i : nodes) {
1912263508Sdim                    Type i = n_i.data.first();
1913263508Sdim                    for (Node n_j : nodes) {
1914263508Sdim                        Type j = n_j.data.first();
1915263508Sdim                        UndetVar uv_i = (UndetVar)inferenceContext.asUndetVar(i);
1916263508Sdim                        if (Type.containsAny(uv_i.getBounds(InferenceBound.values()), List.of(j))) {
1917263508Sdim                            //update i's bound dependencies
1918263508Sdim                            n_i.addDependency(n_j);
1919263508Sdim                        }
1920263508Sdim                    }
1921263508Sdim                }
1922263508Sdim                //merge cyclic nodes
1923263508Sdim                ArrayList<Node> acyclicNodes = new ArrayList<>();
1924263508Sdim                for (List<? extends Node> conSubGraph : GraphUtils.tarjan(nodes)) {
1925263508Sdim                    if (conSubGraph.length() > 1) {
1926249423Sdim                        Node root = conSubGraph.head;
1927249423Sdim                        root.mergeWith(conSubGraph.tail);
1928249423Sdim                        for (Node n : conSubGraph) {
1929263508Sdim                            notifyUpdate(n, root);
1930249423Sdim                        }
1931249423Sdim                    }
1932263508Sdim                    acyclicNodes.add(conSubGraph.head);
1933249423Sdim                }
1934249423Sdim                nodes = acyclicNodes;
1935249423Sdim            }
1936249423Sdim
1937212795Sdim            /**
1938212795Sdim             * Debugging: dot representation of this graph
1939212795Sdim             */
1940212795Sdim            String toDot() {
1941212795Sdim                StringBuilder buf = new StringBuilder();
1942234353Sdim                for (Type t : inferenceContext.undetvars) {
1943218893Sdim                    UndetVar uv = (UndetVar)t;
1944218893Sdim                    buf.append(String.format("var %s - upper bounds = %s, lower bounds = %s, eq bounds = %s\\n",
1945212795Sdim                            uv.qtype, uv.getBounds(InferenceBound.UPPER), uv.getBounds(InferenceBound.LOWER),
1946212795Sdim                            uv.getBounds(InferenceBound.EQ)));
1947226633Sdim                }
1948212795Sdim                return GraphUtils.toDot(nodes, "inferenceGraph" + hashCode(), buf.toString());
1949212795Sdim            }
1950212795Sdim        }
1951212795Sdim    }
1952212795Sdim    // </editor-fold>
1953212795Sdim
1954212795Sdim    // <editor-fold defaultstate="collapsed" desc="Inference context">
1955212795Sdim    /**
1956212795Sdim     * Functional interface for defining inference callbacks. Certain actions
1957212795Sdim     * (i.e. subtyping checks) might need to be redone after all inference variables
1958212795Sdim     * have been fixed.
1959212795Sdim     */
1960212795Sdim    interface FreeTypeListener {
1961212795Sdim        void typesInferred(InferenceContext inferenceContext);
1962212795Sdim    }
1963212795Sdim
1964212795Sdim    final InferenceContext emptyContext;
1965212795Sdim    // </editor-fold>
1966212795Sdim}
1967212795Sdim