Infer.java revision 3728:a94763e89674
1/*
2 * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.tools.javac.comp;
27
28import com.sun.tools.javac.code.Type.UndetVar.UndetVarListener;
29import com.sun.tools.javac.tree.JCTree;
30import com.sun.tools.javac.tree.JCTree.JCTypeCast;
31import com.sun.tools.javac.tree.TreeInfo;
32import com.sun.tools.javac.util.*;
33import com.sun.tools.javac.util.GraphUtils.DottableNode;
34import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
35import com.sun.tools.javac.util.List;
36import com.sun.tools.javac.code.*;
37import com.sun.tools.javac.code.Type.*;
38import com.sun.tools.javac.code.Type.UndetVar.InferenceBound;
39import com.sun.tools.javac.code.Symbol.*;
40import com.sun.tools.javac.comp.DeferredAttr.AttrMode;
41import com.sun.tools.javac.comp.DeferredAttr.DeferredAttrContext;
42import com.sun.tools.javac.comp.Infer.GraphSolver.InferenceGraph;
43import com.sun.tools.javac.comp.Infer.GraphSolver.InferenceGraph.Node;
44import com.sun.tools.javac.comp.Resolve.InapplicableMethodException;
45import com.sun.tools.javac.comp.Resolve.VerboseResolutionMode;
46
47import java.io.IOException;
48import java.io.Writer;
49import java.nio.file.Files;
50import java.nio.file.Path;
51import java.nio.file.Paths;
52import java.util.ArrayList;
53import java.util.Collection;
54import java.util.Collections;
55import java.util.EnumSet;
56import java.util.HashMap;
57import java.util.HashSet;
58import java.util.Map;
59import java.util.Optional;
60import java.util.Properties;
61import java.util.Set;
62import java.util.function.BiFunction;
63import java.util.function.BiPredicate;
64import java.util.stream.Collectors;
65
66import com.sun.tools.javac.main.Option;
67
68import static com.sun.tools.javac.code.TypeTag.*;
69
70/** Helper class for type parameter inference, used by the attribution phase.
71 *
72 *  <p><b>This is NOT part of any supported API.
73 *  If you write code that depends on this, you do so at your own risk.
74 *  This code and its internal interfaces are subject to change or
75 *  deletion without notice.</b>
76 */
77public class Infer {
78    protected static final Context.Key<Infer> inferKey = new Context.Key<>();
79
80    Resolve rs;
81    Check chk;
82    Symtab syms;
83    Types types;
84    JCDiagnostic.Factory diags;
85    Log log;
86
87    /** should the graph solver be used? */
88    boolean allowGraphInference;
89
90    /**
91     * folder in which the inference dependency graphs should be written.
92     */
93    private final String dependenciesFolder;
94
95    /**
96     * List of graphs awaiting to be dumped to a file.
97     */
98    private List<String> pendingGraphs;
99
100    public static Infer instance(Context context) {
101        Infer instance = context.get(inferKey);
102        if (instance == null)
103            instance = new Infer(context);
104        return instance;
105    }
106
107    protected Infer(Context context) {
108        context.put(inferKey, this);
109
110        rs = Resolve.instance(context);
111        chk = Check.instance(context);
112        syms = Symtab.instance(context);
113        types = Types.instance(context);
114        diags = JCDiagnostic.Factory.instance(context);
115        log = Log.instance(context);
116        inferenceException = new InferenceException(diags);
117        Options options = Options.instance(context);
118        allowGraphInference = Source.instance(context).allowGraphInference()
119                && options.isUnset("useLegacyInference");
120        dependenciesFolder = options.get("debug.dumpInferenceGraphsTo");
121        pendingGraphs = List.nil();
122
123        emptyContext = new InferenceContext(this, List.<Type>nil());
124    }
125
126    /** A value for prototypes that admit any type, including polymorphic ones. */
127    public static final Type anyPoly = new JCNoType();
128
129   /**
130    * This exception class is design to store a list of diagnostics corresponding
131    * to inference errors that can arise during a method applicability check.
132    */
133    public static class InferenceException extends InapplicableMethodException {
134        private static final long serialVersionUID = 0;
135
136        List<JCDiagnostic> messages = List.nil();
137
138        InferenceException(JCDiagnostic.Factory diags) {
139            super(diags);
140        }
141
142        @Override
143        InapplicableMethodException setMessage() {
144            //no message to set
145            return this;
146        }
147
148        @Override
149        InapplicableMethodException setMessage(JCDiagnostic diag) {
150            messages = messages.append(diag);
151            return this;
152        }
153
154        @Override
155        public JCDiagnostic getDiagnostic() {
156            return messages.head;
157        }
158
159        void clear() {
160            messages = List.nil();
161        }
162    }
163
164    protected final InferenceException inferenceException;
165
166    // <editor-fold defaultstate="collapsed" desc="Inference routines">
167    /**
168     * Main inference entry point - instantiate a generic method type
169     * using given argument types and (possibly) an expected target-type.
170     */
171    Type instantiateMethod( Env<AttrContext> env,
172                            List<Type> tvars,
173                            MethodType mt,
174                            Attr.ResultInfo resultInfo,
175                            MethodSymbol msym,
176                            List<Type> argtypes,
177                            boolean allowBoxing,
178                            boolean useVarargs,
179                            Resolve.MethodResolutionContext resolveContext,
180                            Warner warn) throws InferenceException {
181        //-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG
182        final InferenceContext inferenceContext = new InferenceContext(this, tvars);  //B0
183        inferenceException.clear();
184        try {
185            DeferredAttr.DeferredAttrContext deferredAttrContext =
186                        resolveContext.deferredAttrContext(msym, inferenceContext, resultInfo, warn);
187
188            resolveContext.methodCheck.argumentsAcceptable(env, deferredAttrContext,   //B2
189                    argtypes, mt.getParameterTypes(), warn);
190
191            if (allowGraphInference && resultInfo != null && resultInfo.pt == anyPoly) {
192                doIncorporation(inferenceContext, warn);
193                //we are inside method attribution - just return a partially inferred type
194                return new PartiallyInferredMethodType(mt, inferenceContext, env, warn);
195            } else if (allowGraphInference &&
196                    resultInfo != null &&
197                    !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) {
198                //inject return constraints earlier
199                doIncorporation(inferenceContext, warn); //propagation
200
201                boolean shouldPropagate = shouldPropagate(mt.getReturnType(), resultInfo, inferenceContext);
202
203                InferenceContext minContext = shouldPropagate ?
204                        inferenceContext.min(roots(mt, deferredAttrContext), true, warn) :
205                        inferenceContext;
206
207                Type newRestype = generateReturnConstraints(env.tree, resultInfo,  //B3
208                        mt, minContext);
209                mt = (MethodType)types.createMethodTypeWithReturn(mt, newRestype);
210
211                //propagate outwards if needed
212                if (shouldPropagate) {
213                    //propagate inference context outwards and exit
214                    minContext.dupTo(resultInfo.checkContext.inferenceContext());
215                    deferredAttrContext.complete();
216                    return mt;
217                }
218            }
219
220            deferredAttrContext.complete();
221
222            // minimize as yet undetermined type variables
223            if (allowGraphInference) {
224                inferenceContext.solve(warn);
225            } else {
226                inferenceContext.solveLegacy(true, warn, LegacyInferenceSteps.EQ_LOWER.steps); //minimizeInst
227            }
228
229            mt = (MethodType)inferenceContext.asInstType(mt);
230
231            if (!allowGraphInference &&
232                    inferenceContext.restvars().nonEmpty() &&
233                    resultInfo != null &&
234                    !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) {
235                generateReturnConstraints(env.tree, resultInfo, mt, inferenceContext);
236                inferenceContext.solveLegacy(false, warn, LegacyInferenceSteps.EQ_UPPER.steps); //maximizeInst
237                mt = (MethodType)inferenceContext.asInstType(mt);
238            }
239
240            if (resultInfo != null && rs.verboseResolutionMode.contains(VerboseResolutionMode.DEFERRED_INST)) {
241                log.note(env.tree.pos, "deferred.method.inst", msym, mt, resultInfo.pt);
242            }
243
244            // return instantiated version of method type
245            return mt;
246        } finally {
247            if (resultInfo != null || !allowGraphInference) {
248                inferenceContext.notifyChange();
249            } else {
250                inferenceContext.notifyChange(inferenceContext.boundedVars());
251            }
252            if (resultInfo == null) {
253                /* if the is no result info then we can clear the capture types
254                 * cache without affecting any result info check
255                 */
256                inferenceContext.captureTypeCache.clear();
257            }
258            dumpGraphsIfNeeded(env.tree, msym, resolveContext);
259        }
260    }
261    //where
262        private boolean shouldPropagate(Type restype, Attr.ResultInfo target, InferenceContext inferenceContext) {
263            return target.checkContext.inferenceContext() != emptyContext && //enclosing context is a generic method
264                        inferenceContext.free(restype) && //return type contains inference vars
265                        (!inferenceContext.inferencevars.contains(restype) || //no eager instantiation is required (as per 18.5.2)
266                                !needsEagerInstantiation((UndetVar)inferenceContext.asUndetVar(restype), target.pt, inferenceContext));
267        }
268
269        private List<Type> roots(MethodType mt, DeferredAttrContext deferredAttrContext) {
270            ListBuffer<Type> roots = new ListBuffer<>();
271            roots.add(mt.getReturnType());
272            if (deferredAttrContext != null && deferredAttrContext.mode == AttrMode.CHECK) {
273                roots.addAll(mt.getThrownTypes());
274                for (DeferredAttr.DeferredAttrNode n : deferredAttrContext.deferredAttrNodes) {
275                    roots.addAll(n.deferredStuckPolicy.stuckVars());
276                    roots.addAll(n.deferredStuckPolicy.depVars());
277                }
278            }
279            return roots.toList();
280        }
281
282    /**
283     * A partially infered method/constructor type; such a type can be checked multiple times
284     * against different targets.
285     */
286    public class PartiallyInferredMethodType extends MethodType {
287        public PartiallyInferredMethodType(MethodType mtype, InferenceContext inferenceContext, Env<AttrContext> env, Warner warn) {
288            super(mtype.getParameterTypes(), mtype.getReturnType(), mtype.getThrownTypes(), mtype.tsym);
289            this.inferenceContext = inferenceContext;
290            this.env = env;
291            this.warn = warn;
292        }
293
294        /** The inference context. */
295        final InferenceContext inferenceContext;
296
297        /** The attribution environment. */
298        Env<AttrContext> env;
299
300        /** The warner. */
301        final Warner warn;
302
303        @Override
304        public boolean isPartial() {
305            return true;
306        }
307
308        /**
309         * Checks this type against a target; this means generating return type constraints, solve
310         * and then roll back the results (to avoid poolluting the context).
311         */
312        Type check(Attr.ResultInfo resultInfo) {
313            Warner noWarnings = new Warner(null);
314            inferenceException.clear();
315            List<Type> saved_undet = null;
316            try {
317                /** we need to save the inference context before generating target type constraints.
318                 *  This constraints may pollute the inference context and make it useless in case we
319                 *  need to use it several times: with several targets.
320                 */
321                saved_undet = inferenceContext.save();
322                if (allowGraphInference && !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) {
323                    boolean shouldPropagate = shouldPropagate(getReturnType(), resultInfo, inferenceContext);
324
325                    InferenceContext minContext = shouldPropagate ?
326                            inferenceContext.min(roots(asMethodType(), null), false, warn) :
327                            inferenceContext;
328
329                    MethodType other = (MethodType)minContext.update(asMethodType());
330                    Type newRestype = generateReturnConstraints(env.tree, resultInfo,  //B3
331                            other, minContext);
332
333                    if (shouldPropagate) {
334                        //propagate inference context outwards and exit
335                        minContext.dupTo(resultInfo.checkContext.inferenceContext(),
336                                resultInfo.checkContext.deferredAttrContext().insideOverloadPhase());
337                        return newRestype;
338                    }
339                }
340                inferenceContext.solve(noWarnings);
341                return inferenceContext.asInstType(this).getReturnType();
342            } catch (InferenceException ex) {
343                resultInfo.checkContext.report(null, ex.getDiagnostic());
344                Assert.error(); //cannot get here (the above should throw)
345                return null;
346            } finally {
347                if (saved_undet != null) {
348                    inferenceContext.rollback(saved_undet);
349                }
350            }
351        }
352    }
353
354    private void dumpGraphsIfNeeded(DiagnosticPosition pos, Symbol msym, Resolve.MethodResolutionContext rsContext) {
355        int round = 0;
356        try {
357            for (String graph : pendingGraphs.reverse()) {
358                Assert.checkNonNull(dependenciesFolder);
359                Name name = msym.name == msym.name.table.names.init ?
360                        msym.owner.name : msym.name;
361                String filename = String.format("%s@%s[mode=%s,step=%s]_%d.dot",
362                        name,
363                        pos.getStartPosition(),
364                        rsContext.attrMode(),
365                        rsContext.step,
366                        round);
367                Path dotFile = Paths.get(dependenciesFolder, filename);
368                try (Writer w = Files.newBufferedWriter(dotFile)) {
369                    w.append(graph);
370                }
371                round++;
372            }
373        } catch (IOException ex) {
374            Assert.error("Error occurred when dumping inference graph: " + ex.getMessage());
375        } finally {
376            pendingGraphs = List.nil();
377        }
378    }
379
380    /**
381     * Generate constraints from the generic method's return type. If the method
382     * call occurs in a context where a type T is expected, use the expected
383     * type to derive more constraints on the generic method inference variables.
384     */
385    Type generateReturnConstraints(JCTree tree, Attr.ResultInfo resultInfo,
386            MethodType mt, InferenceContext inferenceContext) {
387        InferenceContext rsInfoInfContext = resultInfo.checkContext.inferenceContext();
388        Type from = mt.getReturnType();
389        if (mt.getReturnType().containsAny(inferenceContext.inferencevars) &&
390                rsInfoInfContext != emptyContext) {
391            from = types.capture(from);
392            //add synthetic captured ivars
393            for (Type t : from.getTypeArguments()) {
394                if (t.hasTag(TYPEVAR) && ((TypeVar)t).isCaptured()) {
395                    inferenceContext.addVar((TypeVar)t);
396                }
397            }
398        }
399        Type qtype = inferenceContext.asUndetVar(from);
400        Type to = resultInfo.pt;
401
402        if (qtype.hasTag(VOID)) {
403            to = syms.voidType;
404        } else if (to.hasTag(NONE)) {
405            to = from.isPrimitive() ? from : syms.objectType;
406        } else if (qtype.hasTag(UNDETVAR)) {
407            if (needsEagerInstantiation((UndetVar)qtype, to, inferenceContext) &&
408                    (allowGraphInference || !to.isPrimitive())) {
409                to = generateReferenceToTargetConstraint(tree, (UndetVar)qtype, to, resultInfo, inferenceContext);
410            } else if (to.isPrimitive()) {
411                to = types.boxedClass(to).type;
412            }
413        } else if (rsInfoInfContext.free(resultInfo.pt)) {
414            //propagation - cache captured vars
415            qtype = inferenceContext.asUndetVar(rsInfoInfContext.cachedCapture(tree, from, false));
416        }
417        Assert.check(allowGraphInference || !rsInfoInfContext.free(to),
418                "legacy inference engine cannot handle constraints on both sides of a subtyping assertion");
419        //we need to skip capture?
420        Warner retWarn = new Warner();
421        if (!resultInfo.checkContext.compatible(qtype, rsInfoInfContext.asUndetVar(to), retWarn) ||
422                //unchecked conversion is not allowed in source 7 mode
423                (!allowGraphInference && retWarn.hasLint(Lint.LintCategory.UNCHECKED))) {
424            throw inferenceException
425                    .setMessage("infer.no.conforming.instance.exists",
426                    inferenceContext.restvars(), mt.getReturnType(), to);
427        }
428        return from;
429    }
430
431    private boolean needsEagerInstantiation(UndetVar from, Type to, InferenceContext inferenceContext) {
432        if (to.isPrimitive()) {
433            /* T is a primitive type, and one of the primitive wrapper classes is an instantiation,
434             * upper bound, or lower bound for alpha in B2.
435             */
436            for (Type t : from.getBounds(InferenceBound.values())) {
437                Type boundAsPrimitive = types.unboxedType(t);
438                if (boundAsPrimitive == null || boundAsPrimitive.hasTag(NONE)) {
439                    continue;
440                }
441                return true;
442            }
443            return false;
444        }
445
446        Type captureOfTo = types.capture(to);
447        /* T is a reference type, but is not a wildcard-parameterized type, and either
448         */
449        if (captureOfTo == to) { //not a wildcard parameterized type
450            /* i) B2 contains a bound of one of the forms alpha = S or S <: alpha,
451             *      where S is a wildcard-parameterized type, or
452             */
453            for (Type t : from.getBounds(InferenceBound.EQ, InferenceBound.LOWER)) {
454                Type captureOfBound = types.capture(t);
455                if (captureOfBound != t) {
456                    return true;
457                }
458            }
459
460            /* ii) B2 contains two bounds of the forms S1 <: alpha and S2 <: alpha,
461             * where S1 and S2 have supertypes that are two different
462             * parameterizations of the same generic class or interface.
463             */
464            for (Type aLowerBound : from.getBounds(InferenceBound.LOWER)) {
465                for (Type anotherLowerBound : from.getBounds(InferenceBound.LOWER)) {
466                    if (aLowerBound != anotherLowerBound &&
467                            !inferenceContext.free(aLowerBound) &&
468                            !inferenceContext.free(anotherLowerBound) &&
469                            commonSuperWithDiffParameterization(aLowerBound, anotherLowerBound)) {
470                        return true;
471                    }
472                }
473            }
474        }
475
476        /* T is a parameterization of a generic class or interface, G,
477         * and B2 contains a bound of one of the forms alpha = S or S <: alpha,
478         * where there exists no type of the form G<...> that is a
479         * supertype of S, but the raw type G is a supertype of S
480         */
481        if (to.isParameterized()) {
482            for (Type t : from.getBounds(InferenceBound.EQ, InferenceBound.LOWER)) {
483                Type sup = types.asSuper(t, to.tsym);
484                if (sup != null && sup.isRaw()) {
485                    return true;
486                }
487            }
488        }
489        return false;
490    }
491
492    private boolean commonSuperWithDiffParameterization(Type t, Type s) {
493        for (Pair<Type, Type> supers : getParameterizedSupers(t, s)) {
494            if (!types.isSameType(supers.fst, supers.snd)) return true;
495        }
496        return false;
497    }
498
499    private Type generateReferenceToTargetConstraint(JCTree tree, UndetVar from,
500            Type to, Attr.ResultInfo resultInfo,
501            InferenceContext inferenceContext) {
502        inferenceContext.solve(List.of(from.qtype), new Warner());
503        inferenceContext.notifyChange();
504        Type capturedType = resultInfo.checkContext.inferenceContext()
505                .cachedCapture(tree, from.getInst(), false);
506        if (types.isConvertible(capturedType,
507                resultInfo.checkContext.inferenceContext().asUndetVar(to))) {
508            //effectively skip additional return-type constraint generation (compatibility)
509            return syms.objectType;
510        }
511        return to;
512    }
513
514    /**
515      * Infer cyclic inference variables as described in 15.12.2.8.
516      */
517    void instantiateAsUninferredVars(List<Type> vars, InferenceContext inferenceContext) {
518        ListBuffer<Type> todo = new ListBuffer<>();
519        //step 1 - create fresh tvars
520        for (Type t : vars) {
521            UndetVar uv = (UndetVar)inferenceContext.asUndetVar(t);
522            List<Type> upperBounds = uv.getBounds(InferenceBound.UPPER);
523            if (Type.containsAny(upperBounds, vars)) {
524                TypeSymbol fresh_tvar = new TypeVariableSymbol(Flags.SYNTHETIC, uv.qtype.tsym.name, null, uv.qtype.tsym.owner);
525                fresh_tvar.type = new TypeVar(fresh_tvar, types.makeIntersectionType(uv.getBounds(InferenceBound.UPPER)), null);
526                todo.append(uv);
527                uv.setInst(fresh_tvar.type);
528            } else if (upperBounds.nonEmpty()) {
529                uv.setInst(types.glb(upperBounds));
530            } else {
531                uv.setInst(syms.objectType);
532            }
533        }
534        //step 2 - replace fresh tvars in their bounds
535        List<Type> formals = vars;
536        for (Type t : todo) {
537            UndetVar uv = (UndetVar)t;
538            TypeVar ct = (TypeVar)uv.getInst();
539            ct.bound = types.glb(inferenceContext.asInstTypes(types.getBounds(ct)));
540            if (ct.bound.isErroneous()) {
541                //report inference error if glb fails
542                reportBoundError(uv, InferenceBound.UPPER);
543            }
544            formals = formals.tail;
545        }
546    }
547
548    /**
549     * Compute a synthetic method type corresponding to the requested polymorphic
550     * method signature. The target return type is computed from the immediately
551     * enclosing scope surrounding the polymorphic-signature call.
552     */
553    Type instantiatePolymorphicSignatureInstance(Env<AttrContext> env,
554                                            MethodSymbol spMethod,  // sig. poly. method or null if none
555                                            Resolve.MethodResolutionContext resolveContext,
556                                            List<Type> argtypes) {
557        final Type restype;
558
559        if (spMethod == null || types.isSameType(spMethod.getReturnType(), syms.objectType, true)) {
560            // The return type of the polymorphic signature is polymorphic,
561            // and is computed from the enclosing tree E, as follows:
562            // if E is a cast, then use the target type of the cast expression
563            // as a return type; if E is an expression statement, the return
564            // type is 'void'; otherwise
565            // the return type is simply 'Object'. A correctness check ensures
566            // that env.next refers to the lexically enclosing environment in
567            // which the polymorphic signature call environment is nested.
568
569            switch (env.next.tree.getTag()) {
570                case TYPECAST:
571                    JCTypeCast castTree = (JCTypeCast)env.next.tree;
572                    restype = (TreeInfo.skipParens(castTree.expr) == env.tree) ?
573                              castTree.clazz.type :
574                              syms.objectType;
575                    break;
576                case EXEC:
577                    JCTree.JCExpressionStatement execTree =
578                            (JCTree.JCExpressionStatement)env.next.tree;
579                    restype = (TreeInfo.skipParens(execTree.expr) == env.tree) ?
580                              syms.voidType :
581                              syms.objectType;
582                    break;
583                default:
584                    restype = syms.objectType;
585            }
586        } else {
587            // The return type of the polymorphic signature is fixed
588            // (not polymorphic)
589            restype = spMethod.getReturnType();
590        }
591
592        List<Type> paramtypes = argtypes.map(new ImplicitArgType(spMethod, resolveContext.step));
593        List<Type> exType = spMethod != null ?
594            spMethod.getThrownTypes() :
595            List.of(syms.throwableType); // make it throw all exceptions
596
597        MethodType mtype = new MethodType(paramtypes,
598                                          restype,
599                                          exType,
600                                          syms.methodClass);
601        return mtype;
602    }
603    //where
604        class ImplicitArgType extends DeferredAttr.DeferredTypeMap {
605
606            public ImplicitArgType(Symbol msym, Resolve.MethodResolutionPhase phase) {
607                (rs.deferredAttr).super(AttrMode.SPECULATIVE, msym, phase);
608            }
609
610            @Override
611            public Type visitClassType(ClassType t, Void aVoid) {
612                return types.erasure(t);
613            }
614
615            @Override
616            public Type visitType(Type t, Void _unused) {
617                if (t.hasTag(DEFERRED)) {
618                    return visit(super.visitType(t, null));
619                } else if (t.hasTag(BOT))
620                    // nulls type as the marker type Null (which has no instances)
621                    // infer as java.lang.Void for now
622                    t = types.boxedClass(syms.voidType).type;
623                return t;
624            }
625        }
626
627    TypeMapping<Void> fromTypeVarFun = new TypeMapping<Void>() {
628        @Override
629        public Type visitTypeVar(TypeVar tv, Void aVoid) {
630            return new UndetVar(tv, incorporationEngine(), types);
631        }
632
633        @Override
634        public Type visitCapturedType(CapturedType t, Void aVoid) {
635            return new CapturedUndetVar(t, incorporationEngine(), types);
636        }
637    };
638
639    /**
640      * This method is used to infer a suitable target SAM in case the original
641      * SAM type contains one or more wildcards. An inference process is applied
642      * so that wildcard bounds, as well as explicit lambda/method ref parameters
643      * (where applicable) are used to constraint the solution.
644      */
645    public Type instantiateFunctionalInterface(DiagnosticPosition pos, Type funcInterface,
646            List<Type> paramTypes, Check.CheckContext checkContext) {
647        if (types.capture(funcInterface) == funcInterface) {
648            //if capture doesn't change the type then return the target unchanged
649            //(this means the target contains no wildcards!)
650            return funcInterface;
651        } else {
652            Type formalInterface = funcInterface.tsym.type;
653            InferenceContext funcInterfaceContext =
654                    new InferenceContext(this, funcInterface.tsym.type.getTypeArguments());
655
656            Assert.check(paramTypes != null);
657            //get constraints from explicit params (this is done by
658            //checking that explicit param types are equal to the ones
659            //in the functional interface descriptors)
660            List<Type> descParameterTypes = types.findDescriptorType(formalInterface).getParameterTypes();
661            if (descParameterTypes.size() != paramTypes.size()) {
662                checkContext.report(pos, diags.fragment("incompatible.arg.types.in.lambda"));
663                return types.createErrorType(funcInterface);
664            }
665            for (Type p : descParameterTypes) {
666                if (!types.isSameType(funcInterfaceContext.asUndetVar(p), paramTypes.head)) {
667                    checkContext.report(pos, diags.fragment("no.suitable.functional.intf.inst", funcInterface));
668                    return types.createErrorType(funcInterface);
669                }
670                paramTypes = paramTypes.tail;
671            }
672
673            List<Type> actualTypeargs = funcInterface.getTypeArguments();
674            for (Type t : funcInterfaceContext.undetvars) {
675                UndetVar uv = (UndetVar)t;
676                Optional<Type> inst = uv.getBounds(InferenceBound.EQ).stream()
677                        .filter(b -> !b.containsAny(formalInterface.getTypeArguments())).findFirst();
678                uv.setInst(inst.orElse(actualTypeargs.head));
679                actualTypeargs = actualTypeargs.tail;
680            }
681
682            Type owntype = funcInterfaceContext.asInstType(formalInterface);
683            if (!chk.checkValidGenericType(owntype)) {
684                //if the inferred functional interface type is not well-formed,
685                //or if it's not a subtype of the original target, issue an error
686                checkContext.report(pos, diags.fragment("no.suitable.functional.intf.inst", funcInterface));
687            }
688            //propagate constraints as per JLS 18.2.1
689            checkContext.compatible(owntype, funcInterface, types.noWarnings);
690            return owntype;
691        }
692    }
693    // </editor-fold>
694
695    // <editor-fold defaultstate="collapsed" desc="Incorporation">
696
697    /**
698     * This class is the root of all incorporation actions.
699     */
700    public abstract class IncorporationAction {
701        UndetVar uv;
702        Type t;
703
704        IncorporationAction(UndetVar uv, Type t) {
705            this.uv = uv;
706            this.t = t;
707        }
708
709        public abstract IncorporationAction dup(UndetVar that);
710
711        /**
712         * Incorporation action entry-point. Subclasses should define the logic associated with
713         * this incorporation action.
714         */
715        abstract void apply(InferenceContext ic, Warner warn);
716
717        /**
718         * Helper function: perform subtyping through incorporation cache.
719         */
720        boolean isSubtype(Type s, Type t, Warner warn) {
721            return doIncorporationOp(IncorporationBinaryOpKind.IS_SUBTYPE, s, t, warn);
722        }
723
724        /**
725         * Helper function: perform type-equivalence through incorporation cache.
726         */
727        boolean isSameType(Type s, Type t) {
728            return doIncorporationOp(IncorporationBinaryOpKind.IS_SAME_TYPE, s, t, null);
729        }
730
731        @Override
732        public String toString() {
733            return String.format("%s[undet=%s,t=%s]", getClass().getSimpleName(), uv.qtype, t);
734        }
735    }
736
737    /**
738     * Bound-check incorporation action. A newly added bound is checked against existing bounds,
739     * to verify its compatibility; each bound is checked using either subtyping or type equivalence.
740     */
741    class CheckBounds extends IncorporationAction {
742
743        InferenceBound from;
744        BiFunction<InferenceContext, Type, Type> typeFunc;
745        BiPredicate<InferenceContext, Type> optFilter;
746
747        CheckBounds(UndetVar uv, Type t, InferenceBound from) {
748            this(uv, t, InferenceContext::asUndetVar, null, from);
749        }
750
751        CheckBounds(UndetVar uv, Type t, BiFunction<InferenceContext, Type, Type> typeFunc,
752                    BiPredicate<InferenceContext, Type> typeFilter, InferenceBound from) {
753            super(uv, t);
754            this.from = from;
755            this.typeFunc = typeFunc;
756            this.optFilter = typeFilter;
757        }
758
759        @Override
760        public IncorporationAction dup(UndetVar that) {
761            return new CheckBounds(that, t, typeFunc, optFilter, from);
762        }
763
764        @Override
765        void apply(InferenceContext inferenceContext, Warner warn) {
766            t = typeFunc.apply(inferenceContext, t);
767            if (optFilter != null && optFilter.test(inferenceContext, t)) return;
768            for (InferenceBound to : boundsToCheck()) {
769                for (Type b : uv.getBounds(to)) {
770                    b = typeFunc.apply(inferenceContext, b);
771                    if (optFilter != null && optFilter.test(inferenceContext, b)) continue;
772                    boolean success = checkBound(t, b, from, to, warn);
773                    if (!success) {
774                        report(from, to);
775                    }
776                }
777            }
778        }
779
780        /**
781         * The list of bound kinds to be checked.
782         */
783        EnumSet<InferenceBound> boundsToCheck() {
784            return (from == InferenceBound.EQ) ?
785                            EnumSet.allOf(InferenceBound.class) :
786                            EnumSet.complementOf(EnumSet.of(from));
787        }
788
789        /**
790         * Is source type 's' compatible with target type 't' given source and target bound kinds?
791         */
792        boolean checkBound(Type s, Type t, InferenceBound ib_s, InferenceBound ib_t, Warner warn) {
793            if (ib_s.lessThan(ib_t)) {
794                return isSubtype(s, t, warn);
795            } else if (ib_t.lessThan(ib_s)) {
796                return isSubtype(t, s, warn);
797            } else {
798                return isSameType(s, t);
799            }
800        }
801
802        /**
803         * Report a bound check error.
804         */
805        void report(InferenceBound from, InferenceBound to) {
806            //this is a workaround to preserve compatibility with existing messages
807            if (from == to) {
808                reportBoundError(uv, from);
809            } else if (from == InferenceBound.LOWER || to == InferenceBound.EQ) {
810                reportBoundError(uv, to, from);
811            } else {
812                reportBoundError(uv, from, to);
813            }
814        }
815
816        @Override
817        public String toString() {
818            return String.format("%s[undet=%s,t=%s,bound=%s]", getClass().getSimpleName(), uv.qtype, t, from);
819        }
820    }
821
822    /**
823     * Custom check executed by the legacy incorporation engine. Newly added bounds are checked
824     * against existing eq bounds.
825     */
826    class EqCheckLegacy extends CheckBounds {
827        EqCheckLegacy(UndetVar uv, Type t, InferenceBound from) {
828            super(uv, t, InferenceContext::asInstType, InferenceContext::free, from);
829        }
830
831        @Override
832        public IncorporationAction dup(UndetVar that) {
833            return new EqCheckLegacy(that, t, from);
834        }
835
836        @Override
837        EnumSet<InferenceBound> boundsToCheck() {
838            return (from == InferenceBound.EQ) ?
839                            EnumSet.allOf(InferenceBound.class) :
840                            EnumSet.of(InferenceBound.EQ);
841        }
842    }
843
844    /**
845     * Check that the inferred type conforms to all bounds.
846     */
847    class CheckInst extends CheckBounds {
848
849        EnumSet<InferenceBound> to;
850
851        CheckInst(UndetVar uv, InferenceBound ib, InferenceBound... rest) {
852            this(uv, EnumSet.of(ib, rest));
853        }
854
855        CheckInst(UndetVar uv, EnumSet<InferenceBound> to) {
856            super(uv, uv.getInst(), InferenceBound.EQ);
857            this.to = to;
858        }
859
860        @Override
861        public IncorporationAction dup(UndetVar that) {
862            return new CheckInst(that, to);
863        }
864
865        @Override
866        EnumSet<InferenceBound> boundsToCheck() {
867            return to;
868        }
869
870        @Override
871        void report(InferenceBound from, InferenceBound to) {
872            reportInstError(uv, to);
873        }
874    }
875
876    /**
877     * Replace undetvars in bounds and check that the inferred type conforms to all bounds.
878     */
879    class SubstBounds extends CheckInst {
880        SubstBounds(UndetVar uv) {
881            super(uv, InferenceBound.LOWER, InferenceBound.EQ, InferenceBound.UPPER);
882        }
883
884        @Override
885        public IncorporationAction dup(UndetVar that) {
886            return new SubstBounds(that);
887        }
888
889        @Override
890        void apply(InferenceContext inferenceContext, Warner warn) {
891            for (Type undet : inferenceContext.undetvars) {
892                //we could filter out variables not mentioning uv2...
893                UndetVar uv2 = (UndetVar)undet;
894                uv2.substBounds(List.of(uv.qtype), List.of(uv.getInst()), types);
895                checkCompatibleUpperBounds(uv2, inferenceContext);
896            }
897            super.apply(inferenceContext, warn);
898        }
899
900        /**
901         * Make sure that the upper bounds we got so far lead to a solvable inference
902         * variable by making sure that a glb exists.
903         */
904        void checkCompatibleUpperBounds(UndetVar uv, InferenceContext inferenceContext) {
905            List<Type> hibounds =
906                    Type.filter(uv.getBounds(InferenceBound.UPPER), new BoundFilter(inferenceContext));
907            final Type hb;
908            if (hibounds.isEmpty())
909                hb = syms.objectType;
910            else if (hibounds.tail.isEmpty())
911                hb = hibounds.head;
912            else
913                hb = types.glb(hibounds);
914            if (hb == null || hb.isErroneous())
915                reportBoundError(uv, InferenceBound.UPPER);
916        }
917    }
918
919    /**
920     * Perform pairwise comparison between common generic supertypes of two upper bounds.
921     */
922    class CheckUpperBounds extends IncorporationAction {
923
924        public CheckUpperBounds(UndetVar uv, Type t) {
925            super(uv, t);
926        }
927
928        @Override
929        public IncorporationAction dup(UndetVar that) {
930            return new CheckUpperBounds(that, t);
931        }
932
933        @Override
934        void apply(InferenceContext inferenceContext, Warner warn) {
935            List<Type> boundList = uv.getBounds(InferenceBound.UPPER).stream()
936                    .collect(types.closureCollector(true, types::isSameType));
937            for (Type b2 : boundList) {
938                if (t == b2) continue;
939                    /* This wildcard check is temporary workaround. This code may need to be
940                     * revisited once spec bug JDK-7034922 is fixed.
941                     */
942                if (t != b2 && !t.hasTag(WILDCARD) && !b2.hasTag(WILDCARD)) {
943                    for (Pair<Type, Type> commonSupers : getParameterizedSupers(t, b2)) {
944                        List<Type> allParamsSuperBound1 = commonSupers.fst.allparams();
945                        List<Type> allParamsSuperBound2 = commonSupers.snd.allparams();
946                        while (allParamsSuperBound1.nonEmpty() && allParamsSuperBound2.nonEmpty()) {
947                            //traverse the list of all params comparing them
948                            if (!allParamsSuperBound1.head.hasTag(WILDCARD) &&
949                                    !allParamsSuperBound2.head.hasTag(WILDCARD)) {
950                                if (!isSameType(inferenceContext.asUndetVar(allParamsSuperBound1.head),
951                                        inferenceContext.asUndetVar(allParamsSuperBound2.head))) {
952                                    reportBoundError(uv, InferenceBound.UPPER);
953                                }
954                            }
955                            allParamsSuperBound1 = allParamsSuperBound1.tail;
956                            allParamsSuperBound2 = allParamsSuperBound2.tail;
957                        }
958                        Assert.check(allParamsSuperBound1.isEmpty() && allParamsSuperBound2.isEmpty());
959                    }
960                }
961            }
962        }
963    }
964
965    /**
966     * Perform propagation of bounds. Given a constraint of the kind {@code alpha <: T}, three
967     * kind of propagation occur:
968     *
969     * <li>T is copied into all matching bounds (i.e. lower/eq bounds) B of alpha such that B=beta (forward propagation)</li>
970     * <li>if T=beta, matching bounds (i.e. upper bounds) of beta are copied into alpha (backwards propagation)</li>
971     * <li>if T=beta, sets a symmetric bound on beta (i.e. beta :> alpha) (symmetric propagation) </li>
972     */
973    class PropagateBounds extends IncorporationAction {
974
975        InferenceBound ib;
976
977        public PropagateBounds(UndetVar uv, Type t, InferenceBound ib) {
978            super(uv, t);
979            this.ib = ib;
980        }
981
982        @Override
983        public IncorporationAction dup(UndetVar that) {
984            return new PropagateBounds(that, t, ib);
985        }
986
987        void apply(InferenceContext inferenceContext, Warner warner) {
988            Type undetT = inferenceContext.asUndetVar(t);
989            if (undetT.hasTag(UNDETVAR) && !((UndetVar)undetT).isCaptured()) {
990                UndetVar uv2 = (UndetVar)undetT;
991                //symmetric propagation
992                uv2.addBound(ib.complement(), uv, types);
993                //backwards propagation
994                for (InferenceBound ib2 : backwards()) {
995                    for (Type b : uv2.getBounds(ib2)) {
996                        uv.addBound(ib2, b, types);
997                    }
998                }
999            }
1000            //forward propagation
1001            for (InferenceBound ib2 : forward()) {
1002                for (Type l : uv.getBounds(ib2)) {
1003                    Type undet = inferenceContext.asUndetVar(l);
1004                    if (undet.hasTag(TypeTag.UNDETVAR) && !((UndetVar)undet).isCaptured()) {
1005                        UndetVar uv2 = (UndetVar)undet;
1006                        uv2.addBound(ib, inferenceContext.asInstType(t), types);
1007                    }
1008                }
1009            }
1010        }
1011
1012        EnumSet<InferenceBound> forward() {
1013            return (ib == InferenceBound.EQ) ?
1014                    EnumSet.of(InferenceBound.EQ) : EnumSet.complementOf(EnumSet.of(ib));
1015        }
1016
1017        EnumSet<InferenceBound> backwards() {
1018            return (ib == InferenceBound.EQ) ?
1019                    EnumSet.allOf(InferenceBound.class) : EnumSet.of(ib);
1020        }
1021
1022        @Override
1023        public String toString() {
1024            return String.format("%s[undet=%s,t=%s,bound=%s]", getClass().getSimpleName(), uv.qtype, t, ib);
1025        }
1026    }
1027
1028    /**
1029     * This class models an incorporation engine. The engine is responsible for listening to
1030     * changes in inference variables and register incorporation actions accordingly.
1031     */
1032    abstract class AbstractIncorporationEngine implements UndetVarListener {
1033
1034        @Override
1035        public void varInstantiated(UndetVar uv) {
1036            uv.incorporationActions.addFirst(new SubstBounds(uv));
1037        }
1038
1039        @Override
1040        public void varBoundChanged(UndetVar uv, InferenceBound ib, Type bound, boolean update) {
1041            if (uv.isCaptured()) return;
1042            uv.incorporationActions.addAll(getIncorporationActions(uv, ib, bound, update));
1043        }
1044
1045        abstract List<IncorporationAction> getIncorporationActions(UndetVar uv, InferenceBound ib, Type t, boolean update);
1046    }
1047
1048    /**
1049     * A legacy incorporation engine. Used for source <= 7.
1050     */
1051    AbstractIncorporationEngine legacyEngine = new AbstractIncorporationEngine() {
1052
1053        List<IncorporationAction> getIncorporationActions(UndetVar uv, InferenceBound ib, Type t, boolean update) {
1054            ListBuffer<IncorporationAction> actions = new ListBuffer<>();
1055            Type inst = uv.getInst();
1056            if (inst != null) {
1057                actions.add(new CheckInst(uv, ib));
1058            }
1059            actions.add(new EqCheckLegacy(uv, t, ib));
1060            return actions.toList();
1061        }
1062    };
1063
1064    /**
1065     * The standard incorporation engine. Used for source >= 8.
1066     */
1067    AbstractIncorporationEngine graphEngine = new AbstractIncorporationEngine() {
1068
1069        @Override
1070        List<IncorporationAction> getIncorporationActions(UndetVar uv, InferenceBound ib, Type t, boolean update) {
1071            ListBuffer<IncorporationAction> actions = new ListBuffer<>();
1072            Type inst = uv.getInst();
1073            if (inst != null) {
1074                actions.add(new CheckInst(uv, ib));
1075            }
1076            actions.add(new CheckBounds(uv, t, ib));
1077
1078            if (update) {
1079                return actions.toList();
1080            }
1081
1082            if (ib == InferenceBound.UPPER) {
1083                actions.add(new CheckUpperBounds(uv, t));
1084            }
1085
1086            actions.add(new PropagateBounds(uv, t, ib));
1087
1088            return actions.toList();
1089        }
1090    };
1091
1092    /**
1093     * Get the incorporation engine to be used in this compilation.
1094     */
1095    AbstractIncorporationEngine incorporationEngine() {
1096        return allowGraphInference ? graphEngine : legacyEngine;
1097    }
1098
1099    /** max number of incorporation rounds. */
1100    static final int MAX_INCORPORATION_STEPS = 10000;
1101
1102    /**
1103     * Check bounds and perform incorporation.
1104     */
1105    void doIncorporation(InferenceContext inferenceContext, Warner warn) throws InferenceException {
1106        try {
1107            boolean progress = true;
1108            int round = 0;
1109            while (progress && round < MAX_INCORPORATION_STEPS) {
1110                progress = false;
1111                for (Type t : inferenceContext.undetvars) {
1112                    UndetVar uv = (UndetVar)t;
1113                    if (!uv.incorporationActions.isEmpty()) {
1114                        progress = true;
1115                        uv.incorporationActions.removeFirst().apply(inferenceContext, warn);
1116                    }
1117                }
1118                round++;
1119            }
1120        } finally {
1121            incorporationCache.clear();
1122        }
1123    }
1124
1125    /* If for two types t and s there is a least upper bound that contains
1126     * parameterized types G1, G2 ... Gn, then there exists supertypes of 't' of the form
1127     * G1<T1, ..., Tn>, G2<T1, ..., Tn>, ... Gn<T1, ..., Tn> and supertypes of 's' of the form
1128     * G1<S1, ..., Sn>, G2<S1, ..., Sn>, ... Gn<S1, ..., Sn> which will be returned by this method.
1129     * If no such common supertypes exists then an empty list is returned.
1130     *
1131     * As an example for the following input:
1132     *
1133     * t = java.util.ArrayList<java.lang.String>
1134     * s = java.util.List<T>
1135     *
1136     * we get this ouput (singleton list):
1137     *
1138     * [Pair[java.util.List<java.lang.String>,java.util.List<T>]]
1139     */
1140    private List<Pair<Type, Type>> getParameterizedSupers(Type t, Type s) {
1141        Type lubResult = types.lub(t, s);
1142        if (lubResult == syms.errType || lubResult == syms.botType) {
1143            return List.nil();
1144        }
1145        List<Type> supertypesToCheck = lubResult.isIntersection() ?
1146                ((IntersectionClassType)lubResult).getComponents() :
1147                List.of(lubResult);
1148        ListBuffer<Pair<Type, Type>> commonSupertypes = new ListBuffer<>();
1149        for (Type sup : supertypesToCheck) {
1150            if (sup.isParameterized()) {
1151                Type asSuperOfT = asSuper(t, sup);
1152                Type asSuperOfS = asSuper(s, sup);
1153                commonSupertypes.add(new Pair<>(asSuperOfT, asSuperOfS));
1154            }
1155        }
1156        return commonSupertypes.toList();
1157    }
1158    //where
1159        private Type asSuper(Type t, Type sup) {
1160            return (sup.hasTag(ARRAY)) ?
1161                    new ArrayType(asSuper(types.elemtype(t), types.elemtype(sup)), syms.arrayClass) :
1162                    types.asSuper(t, sup.tsym);
1163        }
1164
1165    boolean doIncorporationOp(IncorporationBinaryOpKind opKind, Type op1, Type op2, Warner warn) {
1166            IncorporationBinaryOp newOp = new IncorporationBinaryOp(opKind, op1, op2);
1167            Boolean res = incorporationCache.get(newOp);
1168            if (res == null) {
1169                incorporationCache.put(newOp, res = newOp.apply(warn));
1170            }
1171            return res;
1172        }
1173
1174    /**
1175     * Three kinds of basic operation are supported as part of an incorporation step:
1176     * (i) subtype check, (ii) same type check and (iii) bound addition (either
1177     * upper/lower/eq bound).
1178     */
1179    enum IncorporationBinaryOpKind {
1180        IS_SUBTYPE() {
1181            @Override
1182            boolean apply(Type op1, Type op2, Warner warn, Types types) {
1183                return types.isSubtypeUnchecked(op1, op2, warn);
1184            }
1185        },
1186        IS_SAME_TYPE() {
1187            @Override
1188            boolean apply(Type op1, Type op2, Warner warn, Types types) {
1189                return types.isSameType(op1, op2);
1190            }
1191        };
1192
1193        abstract boolean apply(Type op1, Type op2, Warner warn, Types types);
1194    }
1195
1196    /**
1197     * This class encapsulates a basic incorporation operation; incorporation
1198     * operations takes two type operands and a kind. Each operation performed
1199     * during an incorporation round is stored in a cache, so that operations
1200     * are not executed unnecessarily (which would potentially lead to adding
1201     * same bounds over and over).
1202     */
1203    class IncorporationBinaryOp {
1204
1205        IncorporationBinaryOpKind opKind;
1206        Type op1;
1207        Type op2;
1208
1209        IncorporationBinaryOp(IncorporationBinaryOpKind opKind, Type op1, Type op2) {
1210            this.opKind = opKind;
1211            this.op1 = op1;
1212            this.op2 = op2;
1213        }
1214
1215        @Override
1216        public boolean equals(Object o) {
1217            if (!(o instanceof IncorporationBinaryOp)) {
1218                return false;
1219            } else {
1220                IncorporationBinaryOp that = (IncorporationBinaryOp)o;
1221                return opKind == that.opKind &&
1222                        types.isSameType(op1, that.op1, true) &&
1223                        types.isSameType(op2, that.op2, true);
1224            }
1225        }
1226
1227        @Override
1228        public int hashCode() {
1229            int result = opKind.hashCode();
1230            result *= 127;
1231            result += types.hashCode(op1);
1232            result *= 127;
1233            result += types.hashCode(op2);
1234            return result;
1235        }
1236
1237        boolean apply(Warner warn) {
1238            return opKind.apply(op1, op2, warn, types);
1239        }
1240    }
1241
1242    /** an incorporation cache keeps track of all executed incorporation-related operations */
1243    Map<IncorporationBinaryOp, Boolean> incorporationCache = new HashMap<>();
1244
1245    protected static class BoundFilter implements Filter<Type> {
1246
1247        InferenceContext inferenceContext;
1248
1249        public BoundFilter(InferenceContext inferenceContext) {
1250            this.inferenceContext = inferenceContext;
1251        }
1252
1253        @Override
1254        public boolean accepts(Type t) {
1255            return !t.isErroneous() && !inferenceContext.free(t) &&
1256                    !t.hasTag(BOT);
1257        }
1258    }
1259
1260    /**
1261     * Incorporation error: mismatch between inferred type and given bound.
1262     */
1263    void reportInstError(UndetVar uv, InferenceBound ib) {
1264        reportInferenceError(
1265                String.format("inferred.do.not.conform.to.%s.bounds", StringUtils.toLowerCase(ib.name())),
1266                uv.getInst(),
1267                uv.getBounds(ib));
1268    }
1269
1270    /**
1271     * Incorporation error: mismatch between two (or more) bounds of same kind.
1272     */
1273    void reportBoundError(UndetVar uv, InferenceBound ib) {
1274        reportInferenceError(
1275                String.format("incompatible.%s.bounds", StringUtils.toLowerCase(ib.name())),
1276                uv.qtype,
1277                uv.getBounds(ib));
1278    }
1279
1280    /**
1281     * Incorporation error: mismatch between two (or more) bounds of different kinds.
1282     */
1283    void reportBoundError(UndetVar uv, InferenceBound ib1, InferenceBound ib2) {
1284        reportInferenceError(
1285                String.format("incompatible.%s.%s.bounds",
1286                        StringUtils.toLowerCase(ib1.name()),
1287                        StringUtils.toLowerCase(ib2.name())),
1288                uv.qtype,
1289                uv.getBounds(ib1),
1290                uv.getBounds(ib2));
1291    }
1292
1293    /**
1294     * Helper method: reports an inference error.
1295     */
1296    void reportInferenceError(String key, Object... args) {
1297        throw inferenceException.setMessage(key, args);
1298    }
1299    // </editor-fold>
1300
1301    // <editor-fold defaultstate="collapsed" desc="Inference engine">
1302    /**
1303     * Graph inference strategy - act as an input to the inference solver; a strategy is
1304     * composed of two ingredients: (i) find a node to solve in the inference graph,
1305     * and (ii) tell th engine when we are done fixing inference variables
1306     */
1307    interface GraphStrategy {
1308
1309        /**
1310         * A NodeNotFoundException is thrown whenever an inference strategy fails
1311         * to pick the next node to solve in the inference graph.
1312         */
1313        public static class NodeNotFoundException extends RuntimeException {
1314            private static final long serialVersionUID = 0;
1315
1316            InferenceGraph graph;
1317
1318            public NodeNotFoundException(InferenceGraph graph) {
1319                this.graph = graph;
1320            }
1321        }
1322        /**
1323         * Pick the next node (leaf) to solve in the graph
1324         */
1325        Node pickNode(InferenceGraph g) throws NodeNotFoundException;
1326        /**
1327         * Is this the last step?
1328         */
1329        boolean done();
1330    }
1331
1332    /**
1333     * Simple solver strategy class that locates all leaves inside a graph
1334     * and picks the first leaf as the next node to solve
1335     */
1336    abstract class LeafSolver implements GraphStrategy {
1337        public Node pickNode(InferenceGraph g) {
1338            if (g.nodes.isEmpty()) {
1339                //should not happen
1340                throw new NodeNotFoundException(g);
1341            }
1342            return g.nodes.get(0);
1343        }
1344    }
1345
1346    /**
1347     * This solver uses an heuristic to pick the best leaf - the heuristic
1348     * tries to select the node that has maximal probability to contain one
1349     * or more inference variables in a given list
1350     */
1351    abstract class BestLeafSolver extends LeafSolver {
1352
1353        /** list of ivars of which at least one must be solved */
1354        List<Type> varsToSolve;
1355
1356        BestLeafSolver(List<Type> varsToSolve) {
1357            this.varsToSolve = varsToSolve;
1358        }
1359
1360        /**
1361         * Computes a path that goes from a given node to the leafs in the graph.
1362         * Typically this will start from a node containing a variable in
1363         * {@code varsToSolve}. For any given path, the cost is computed as the total
1364         * number of type-variables that should be eagerly instantiated across that path.
1365         */
1366        Pair<List<Node>, Integer> computeTreeToLeafs(Node n) {
1367            Pair<List<Node>, Integer> cachedPath = treeCache.get(n);
1368            if (cachedPath == null) {
1369                //cache miss
1370                if (n.isLeaf()) {
1371                    //if leaf, stop
1372                    cachedPath = new Pair<>(List.of(n), n.data.length());
1373                } else {
1374                    //if non-leaf, proceed recursively
1375                    Pair<List<Node>, Integer> path = new Pair<>(List.of(n), n.data.length());
1376                    for (Node n2 : n.getAllDependencies()) {
1377                        if (n2 == n) continue;
1378                        Pair<List<Node>, Integer> subpath = computeTreeToLeafs(n2);
1379                        path = new Pair<>(path.fst.prependList(subpath.fst),
1380                                          path.snd + subpath.snd);
1381                    }
1382                    cachedPath = path;
1383                }
1384                //save results in cache
1385                treeCache.put(n, cachedPath);
1386            }
1387            return cachedPath;
1388        }
1389
1390        /** cache used to avoid redundant computation of tree costs */
1391        final Map<Node, Pair<List<Node>, Integer>> treeCache = new HashMap<>();
1392
1393        /** constant value used to mark non-existent paths */
1394        final Pair<List<Node>, Integer> noPath = new Pair<>(null, Integer.MAX_VALUE);
1395
1396        /**
1397         * Pick the leaf that minimize cost
1398         */
1399        @Override
1400        public Node pickNode(final InferenceGraph g) {
1401            treeCache.clear(); //graph changes at every step - cache must be cleared
1402            Pair<List<Node>, Integer> bestPath = noPath;
1403            for (Node n : g.nodes) {
1404                if (!Collections.disjoint(n.data, varsToSolve)) {
1405                    Pair<List<Node>, Integer> path = computeTreeToLeafs(n);
1406                    //discard all paths containing at least a node in the
1407                    //closure computed above
1408                    if (path.snd < bestPath.snd) {
1409                        bestPath = path;
1410                    }
1411                }
1412            }
1413            if (bestPath == noPath) {
1414                //no path leads there
1415                throw new NodeNotFoundException(g);
1416            }
1417            return bestPath.fst.head;
1418        }
1419    }
1420
1421    /**
1422     * The inference process can be thought of as a sequence of steps. Each step
1423     * instantiates an inference variable using a subset of the inference variable
1424     * bounds, if certain condition are met. Decisions such as the sequence in which
1425     * steps are applied, or which steps are to be applied are left to the inference engine.
1426     */
1427    enum InferenceStep {
1428
1429        /**
1430         * Instantiate an inference variables using one of its (ground) equality
1431         * constraints
1432         */
1433        EQ(InferenceBound.EQ) {
1434            @Override
1435            Type solve(UndetVar uv, InferenceContext inferenceContext) {
1436                return filterBounds(uv, inferenceContext).head;
1437            }
1438        },
1439        /**
1440         * Instantiate an inference variables using its (ground) lower bounds. Such
1441         * bounds are merged together using lub().
1442         */
1443        LOWER(InferenceBound.LOWER) {
1444            @Override
1445            Type solve(UndetVar uv, InferenceContext inferenceContext) {
1446                Infer infer = inferenceContext.infer;
1447                List<Type> lobounds = filterBounds(uv, inferenceContext);
1448                //note: lobounds should have at least one element
1449                Type owntype = lobounds.tail.tail == null  ? lobounds.head : infer.types.lub(lobounds);
1450                if (owntype.isPrimitive() || owntype.hasTag(ERROR)) {
1451                    throw infer.inferenceException
1452                        .setMessage("no.unique.minimal.instance.exists",
1453                                    uv.qtype, lobounds);
1454                } else {
1455                    return owntype;
1456                }
1457            }
1458        },
1459        /**
1460         * Infer uninstantiated/unbound inference variables occurring in 'throws'
1461         * clause as RuntimeException
1462         */
1463        THROWS(InferenceBound.UPPER) {
1464            @Override
1465            public boolean accepts(UndetVar t, InferenceContext inferenceContext) {
1466                if ((t.qtype.tsym.flags() & Flags.THROWS) == 0) {
1467                    //not a throws undet var
1468                    return false;
1469                }
1470                Types types = inferenceContext.types;
1471                Symtab syms = inferenceContext.infer.syms;
1472                return t.getBounds(InferenceBound.UPPER).stream()
1473                        .filter(b -> !inferenceContext.free(b))
1474                        .allMatch(u -> types.isSubtype(syms.runtimeExceptionType, u));
1475            }
1476
1477            @Override
1478            Type solve(UndetVar uv, InferenceContext inferenceContext) {
1479                return inferenceContext.infer.syms.runtimeExceptionType;
1480            }
1481        },
1482        /**
1483         * Instantiate an inference variables using its (ground) upper bounds. Such
1484         * bounds are merged together using glb().
1485         */
1486        UPPER(InferenceBound.UPPER) {
1487            @Override
1488            Type solve(UndetVar uv, InferenceContext inferenceContext) {
1489                Infer infer = inferenceContext.infer;
1490                List<Type> hibounds = filterBounds(uv, inferenceContext);
1491                //note: hibounds should have at least one element
1492                Type owntype = hibounds.tail.tail == null  ? hibounds.head : infer.types.glb(hibounds);
1493                if (owntype.isPrimitive() || owntype.hasTag(ERROR)) {
1494                    throw infer.inferenceException
1495                        .setMessage("no.unique.maximal.instance.exists",
1496                                    uv.qtype, hibounds);
1497                } else {
1498                    return owntype;
1499                }
1500            }
1501        },
1502        /**
1503         * Like the former; the only difference is that this step can only be applied
1504         * if all upper bounds are ground.
1505         */
1506        UPPER_LEGACY(InferenceBound.UPPER) {
1507            @Override
1508            public boolean accepts(UndetVar t, InferenceContext inferenceContext) {
1509                return !inferenceContext.free(t.getBounds(ib)) && !t.isCaptured();
1510            }
1511
1512            @Override
1513            Type solve(UndetVar uv, InferenceContext inferenceContext) {
1514                return UPPER.solve(uv, inferenceContext);
1515            }
1516        },
1517        /**
1518         * Like the former; the only difference is that this step can only be applied
1519         * if all upper/lower bounds are ground.
1520         */
1521        CAPTURED(InferenceBound.UPPER) {
1522            @Override
1523            public boolean accepts(UndetVar t, InferenceContext inferenceContext) {
1524                return t.isCaptured() &&
1525                        !inferenceContext.free(t.getBounds(InferenceBound.UPPER, InferenceBound.LOWER));
1526            }
1527
1528            @Override
1529            Type solve(UndetVar uv, InferenceContext inferenceContext) {
1530                Infer infer = inferenceContext.infer;
1531                Type upper = UPPER.filterBounds(uv, inferenceContext).nonEmpty() ?
1532                        UPPER.solve(uv, inferenceContext) :
1533                        infer.syms.objectType;
1534                Type lower = LOWER.filterBounds(uv, inferenceContext).nonEmpty() ?
1535                        LOWER.solve(uv, inferenceContext) :
1536                        infer.syms.botType;
1537                CapturedType prevCaptured = (CapturedType)uv.qtype;
1538                return new CapturedType(prevCaptured.tsym.name, prevCaptured.tsym.owner,
1539                                        upper, lower, prevCaptured.wildcard);
1540            }
1541        };
1542
1543        final InferenceBound ib;
1544
1545        InferenceStep(InferenceBound ib) {
1546            this.ib = ib;
1547        }
1548
1549        /**
1550         * Find an instantiated type for a given inference variable within
1551         * a given inference context
1552         */
1553        abstract Type solve(UndetVar uv, InferenceContext inferenceContext);
1554
1555        /**
1556         * Can the inference variable be instantiated using this step?
1557         */
1558        public boolean accepts(UndetVar t, InferenceContext inferenceContext) {
1559            return filterBounds(t, inferenceContext).nonEmpty() && !t.isCaptured();
1560        }
1561
1562        /**
1563         * Return the subset of ground bounds in a given bound set (i.e. eq/lower/upper)
1564         */
1565        List<Type> filterBounds(UndetVar uv, InferenceContext inferenceContext) {
1566            return Type.filter(uv.getBounds(ib), new BoundFilter(inferenceContext));
1567        }
1568    }
1569
1570    /**
1571     * This enumeration defines the sequence of steps to be applied when the
1572     * solver works in legacy mode. The steps in this enumeration reflect
1573     * the behavior of old inference routine (see JLS SE 7 15.12.2.7/15.12.2.8).
1574     */
1575    enum LegacyInferenceSteps {
1576
1577        EQ_LOWER(EnumSet.of(InferenceStep.EQ, InferenceStep.LOWER)),
1578        EQ_UPPER(EnumSet.of(InferenceStep.EQ, InferenceStep.UPPER_LEGACY));
1579
1580        final EnumSet<InferenceStep> steps;
1581
1582        LegacyInferenceSteps(EnumSet<InferenceStep> steps) {
1583            this.steps = steps;
1584        }
1585    }
1586
1587    /**
1588     * This enumeration defines the sequence of steps to be applied when the
1589     * graph solver is used. This order is defined so as to maximize compatibility
1590     * w.r.t. old inference routine (see JLS SE 7 15.12.2.7/15.12.2.8).
1591     */
1592    enum GraphInferenceSteps {
1593
1594        EQ(EnumSet.of(InferenceStep.EQ)),
1595        EQ_LOWER(EnumSet.of(InferenceStep.EQ, InferenceStep.LOWER)),
1596        EQ_LOWER_THROWS_UPPER_CAPTURED(EnumSet.of(InferenceStep.EQ, InferenceStep.LOWER, InferenceStep.UPPER, InferenceStep.THROWS, InferenceStep.CAPTURED));
1597
1598        final EnumSet<InferenceStep> steps;
1599
1600        GraphInferenceSteps(EnumSet<InferenceStep> steps) {
1601            this.steps = steps;
1602        }
1603    }
1604
1605    /**
1606     * There are two kinds of dependencies between inference variables. The basic
1607     * kind of dependency (or bound dependency) arises when a variable mention
1608     * another variable in one of its bounds. There's also a more subtle kind
1609     * of dependency that arises when a variable 'might' lead to better constraints
1610     * on another variable (this is typically the case with variables holding up
1611     * stuck expressions).
1612     */
1613    enum DependencyKind implements GraphUtils.DependencyKind {
1614
1615        /** bound dependency */
1616        BOUND("dotted"),
1617        /** stuck dependency */
1618        STUCK("dashed");
1619
1620        final String dotSyle;
1621
1622        private DependencyKind(String dotSyle) {
1623            this.dotSyle = dotSyle;
1624        }
1625    }
1626
1627    /**
1628     * This is the graph inference solver - the solver organizes all inference variables in
1629     * a given inference context by bound dependencies - in the general case, such dependencies
1630     * would lead to a cyclic directed graph (hence the name); the dependency info is used to build
1631     * an acyclic graph, where all cyclic variables are bundled together. An inference
1632     * step corresponds to solving a node in the acyclic graph - this is done by
1633     * relying on a given strategy (see GraphStrategy).
1634     */
1635    class GraphSolver {
1636
1637        InferenceContext inferenceContext;
1638        Warner warn;
1639
1640        GraphSolver(InferenceContext inferenceContext, Warner warn) {
1641            this.inferenceContext = inferenceContext;
1642            this.warn = warn;
1643        }
1644
1645        /**
1646         * Solve variables in a given inference context. The amount of variables
1647         * to be solved, and the way in which the underlying acyclic graph is explored
1648         * depends on the selected solver strategy.
1649         */
1650        void solve(GraphStrategy sstrategy) {
1651            doIncorporation(inferenceContext, warn); //initial propagation of bounds
1652            InferenceGraph inferenceGraph = new InferenceGraph();
1653            while (!sstrategy.done()) {
1654                if (dependenciesFolder != null) {
1655                    //add this graph to the pending queue
1656                    pendingGraphs = pendingGraphs.prepend(inferenceGraph.toDot());
1657                }
1658                InferenceGraph.Node nodeToSolve = sstrategy.pickNode(inferenceGraph);
1659                List<Type> varsToSolve = List.from(nodeToSolve.data);
1660                List<Type> saved_undet = inferenceContext.save();
1661                try {
1662                    //repeat until all variables are solved
1663                    outer: while (Type.containsAny(inferenceContext.restvars(), varsToSolve)) {
1664                        //for each inference phase
1665                        for (GraphInferenceSteps step : GraphInferenceSteps.values()) {
1666                            if (inferenceContext.solveBasic(varsToSolve, step.steps).nonEmpty()) {
1667                                doIncorporation(inferenceContext, warn);
1668                                continue outer;
1669                            }
1670                        }
1671                        //no progress
1672                        throw inferenceException.setMessage();
1673                    }
1674                }
1675                catch (InferenceException ex) {
1676                    //did we fail because of interdependent ivars?
1677                    inferenceContext.rollback(saved_undet);
1678                    instantiateAsUninferredVars(varsToSolve, inferenceContext);
1679                    doIncorporation(inferenceContext, warn);
1680                }
1681                inferenceGraph.deleteNode(nodeToSolve);
1682            }
1683        }
1684
1685        /**
1686         * The dependencies between the inference variables that need to be solved
1687         * form a (possibly cyclic) graph. This class reduces the original dependency graph
1688         * to an acyclic version, where cyclic nodes are folded into a single 'super node'.
1689         */
1690        class InferenceGraph {
1691
1692            /**
1693             * This class represents a node in the graph. Each node corresponds
1694             * to an inference variable and has edges (dependencies) on other
1695             * nodes. The node defines an entry point that can be used to receive
1696             * updates on the structure of the graph this node belongs to (used to
1697             * keep dependencies in sync).
1698             */
1699            class Node extends GraphUtils.TarjanNode<ListBuffer<Type>, Node> implements DottableNode<ListBuffer<Type>, Node> {
1700
1701                /** node dependencies */
1702                Set<Node> deps;
1703
1704                Node(Type ivar) {
1705                    super(ListBuffer.of(ivar));
1706                    this.deps = new HashSet<>();
1707                }
1708
1709                @Override
1710                public GraphUtils.DependencyKind[] getSupportedDependencyKinds() {
1711                    return new GraphUtils.DependencyKind[] { DependencyKind.BOUND };
1712                }
1713
1714                public Iterable<? extends Node> getAllDependencies() {
1715                    return deps;
1716                }
1717
1718                @Override
1719                public Collection<? extends Node> getDependenciesByKind(GraphUtils.DependencyKind dk) {
1720                    if (dk == DependencyKind.BOUND) {
1721                        return deps;
1722                    } else {
1723                        throw new IllegalStateException();
1724                    }
1725                }
1726
1727                /**
1728                 * Adds dependency with given kind.
1729                 */
1730                protected void addDependency(Node depToAdd) {
1731                    deps.add(depToAdd);
1732                }
1733
1734                /**
1735                 * Add multiple dependencies of same given kind.
1736                 */
1737                protected void addDependencies(Set<Node> depsToAdd) {
1738                    for (Node n : depsToAdd) {
1739                        addDependency(n);
1740                    }
1741                }
1742
1743                /**
1744                 * Remove a dependency, regardless of its kind.
1745                 */
1746                protected boolean removeDependency(Node n) {
1747                    return deps.remove(n);
1748                }
1749
1750                /**
1751                 * Compute closure of a give node, by recursively walking
1752                 * through all its dependencies (of given kinds)
1753                 */
1754                protected Set<Node> closure() {
1755                    boolean progress = true;
1756                    Set<Node> closure = new HashSet<>();
1757                    closure.add(this);
1758                    while (progress) {
1759                        progress = false;
1760                        for (Node n1 : new HashSet<>(closure)) {
1761                            progress = closure.addAll(n1.deps);
1762                        }
1763                    }
1764                    return closure;
1765                }
1766
1767                /**
1768                 * Is this node a leaf? This means either the node has no dependencies,
1769                 * or it just has self-dependencies.
1770                 */
1771                protected boolean isLeaf() {
1772                    //no deps, or only one self dep
1773                    if (deps.isEmpty()) return true;
1774                    for (Node n : deps) {
1775                        if (n != this) {
1776                            return false;
1777                        }
1778                    }
1779                    return true;
1780                }
1781
1782                /**
1783                 * Merge this node with another node, acquiring its dependencies.
1784                 * This routine is used to merge all cyclic node together and
1785                 * form an acyclic graph.
1786                 */
1787                protected void mergeWith(List<? extends Node> nodes) {
1788                    for (Node n : nodes) {
1789                        Assert.check(n.data.length() == 1, "Attempt to merge a compound node!");
1790                        data.appendList(n.data);
1791                        addDependencies(n.deps);
1792                    }
1793                    //update deps
1794                    Set<Node> deps2 = new HashSet<>();
1795                    for (Node d : deps) {
1796                        if (data.contains(d.data.first())) {
1797                            deps2.add(this);
1798                        } else {
1799                            deps2.add(d);
1800                        }
1801                    }
1802                    deps = deps2;
1803                }
1804
1805                /**
1806                 * Notify all nodes that something has changed in the graph
1807                 * topology.
1808                 */
1809                private void graphChanged(Node from, Node to) {
1810                    if (removeDependency(from)) {
1811                        if (to != null) {
1812                            addDependency(to);
1813                        }
1814                    }
1815                }
1816
1817                @Override
1818                public Properties nodeAttributes() {
1819                    Properties p = new Properties();
1820                    p.put("label", "\"" + toString() + "\"");
1821                    return p;
1822                }
1823
1824                @Override
1825                public Properties dependencyAttributes(Node sink, GraphUtils.DependencyKind dk) {
1826                    Properties p = new Properties();
1827                    p.put("style", ((DependencyKind)dk).dotSyle);
1828                    StringBuilder buf = new StringBuilder();
1829                    String sep = "";
1830                    for (Type from : data) {
1831                        UndetVar uv = (UndetVar)inferenceContext.asUndetVar(from);
1832                        for (Type bound : uv.getBounds(InferenceBound.values())) {
1833                            if (bound.containsAny(List.from(sink.data))) {
1834                                buf.append(sep);
1835                                buf.append(bound);
1836                                sep = ",";
1837                            }
1838                        }
1839                    }
1840                    p.put("label", "\"" + buf.toString() + "\"");
1841                    return p;
1842                }
1843            }
1844
1845            /** the nodes in the inference graph */
1846            ArrayList<Node> nodes;
1847
1848            InferenceGraph() {
1849                initNodes();
1850            }
1851
1852            /**
1853             * Basic lookup helper for retrieving a graph node given an inference
1854             * variable type.
1855             */
1856            public Node findNode(Type t) {
1857                for (Node n : nodes) {
1858                    if (n.data.contains(t)) {
1859                        return n;
1860                    }
1861                }
1862                return null;
1863            }
1864
1865            /**
1866             * Delete a node from the graph. This update the underlying structure
1867             * of the graph (including dependencies) via listeners updates.
1868             */
1869            public void deleteNode(Node n) {
1870                Assert.check(nodes.contains(n));
1871                nodes.remove(n);
1872                notifyUpdate(n, null);
1873            }
1874
1875            /**
1876             * Notify all nodes of a change in the graph. If the target node is
1877             * {@code null} the source node is assumed to be removed.
1878             */
1879            void notifyUpdate(Node from, Node to) {
1880                for (Node n : nodes) {
1881                    n.graphChanged(from, to);
1882                }
1883            }
1884
1885            /**
1886             * Create the graph nodes. First a simple node is created for every inference
1887             * variables to be solved. Then Tarjan is used to found all connected components
1888             * in the graph. For each component containing more than one node, a super node is
1889             * created, effectively replacing the original cyclic nodes.
1890             */
1891            void initNodes() {
1892                //add nodes
1893                nodes = new ArrayList<>();
1894                for (Type t : inferenceContext.restvars()) {
1895                    nodes.add(new Node(t));
1896                }
1897                //add dependencies
1898                for (Node n_i : nodes) {
1899                    Type i = n_i.data.first();
1900                    for (Node n_j : nodes) {
1901                        Type j = n_j.data.first();
1902                        UndetVar uv_i = (UndetVar)inferenceContext.asUndetVar(i);
1903                        if (Type.containsAny(uv_i.getBounds(InferenceBound.values()), List.of(j))) {
1904                            //update i's bound dependencies
1905                            n_i.addDependency(n_j);
1906                        }
1907                    }
1908                }
1909                //merge cyclic nodes
1910                ArrayList<Node> acyclicNodes = new ArrayList<>();
1911                for (List<? extends Node> conSubGraph : GraphUtils.tarjan(nodes)) {
1912                    if (conSubGraph.length() > 1) {
1913                        Node root = conSubGraph.head;
1914                        root.mergeWith(conSubGraph.tail);
1915                        for (Node n : conSubGraph) {
1916                            notifyUpdate(n, root);
1917                        }
1918                    }
1919                    acyclicNodes.add(conSubGraph.head);
1920                }
1921                nodes = acyclicNodes;
1922            }
1923
1924            /**
1925             * Debugging: dot representation of this graph
1926             */
1927            String toDot() {
1928                StringBuilder buf = new StringBuilder();
1929                for (Type t : inferenceContext.undetvars) {
1930                    UndetVar uv = (UndetVar)t;
1931                    buf.append(String.format("var %s - upper bounds = %s, lower bounds = %s, eq bounds = %s\\n",
1932                            uv.qtype, uv.getBounds(InferenceBound.UPPER), uv.getBounds(InferenceBound.LOWER),
1933                            uv.getBounds(InferenceBound.EQ)));
1934                }
1935                return GraphUtils.toDot(nodes, "inferenceGraph" + hashCode(), buf.toString());
1936            }
1937        }
1938    }
1939    // </editor-fold>
1940
1941    // <editor-fold defaultstate="collapsed" desc="Inference context">
1942    /**
1943     * Functional interface for defining inference callbacks. Certain actions
1944     * (i.e. subtyping checks) might need to be redone after all inference variables
1945     * have been fixed.
1946     */
1947    interface FreeTypeListener {
1948        void typesInferred(InferenceContext inferenceContext);
1949    }
1950
1951    final InferenceContext emptyContext;
1952    // </editor-fold>
1953}
1954