src/share/classes/com/sun/tools/javac/comp/Infer.java

Thu, 04 Oct 2012 13:04:53 +0100

author
mcimadamore
date
Thu, 04 Oct 2012 13:04:53 +0100
changeset 1347
1408af4cd8b0
parent 1338
ad2ca2a4ab5e
child 1348
573ceb23beeb
permissions
-rw-r--r--

7177387: Add target-typing support in method context
Summary: Add support for deferred types and speculative attribution
Reviewed-by: jjg, dlsmith

     1 /*
     2  * Copyright (c) 1999, 2012, 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  */
    26 package com.sun.tools.javac.comp;
    28 import com.sun.tools.javac.code.*;
    29 import com.sun.tools.javac.code.Symbol.*;
    30 import com.sun.tools.javac.code.Type.*;
    31 import com.sun.tools.javac.code.Type.UndetVar.InferenceBound;
    32 import com.sun.tools.javac.comp.DeferredAttr.AttrMode;
    33 import com.sun.tools.javac.comp.Resolve.InapplicableMethodException;
    34 import com.sun.tools.javac.comp.Resolve.VerboseResolutionMode;
    35 import com.sun.tools.javac.tree.JCTree;
    36 import com.sun.tools.javac.tree.JCTree.JCTypeCast;
    37 import com.sun.tools.javac.tree.TreeInfo;
    38 import com.sun.tools.javac.util.*;
    39 import com.sun.tools.javac.util.List;
    40 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
    42 import java.util.HashMap;
    43 import java.util.Map;
    44 import java.util.Set;
    46 import static com.sun.tools.javac.code.TypeTags.*;
    48 /** Helper class for type parameter inference, used by the attribution phase.
    49  *
    50  *  <p><b>This is NOT part of any supported API.
    51  *  If you write code that depends on this, you do so at your own risk.
    52  *  This code and its internal interfaces are subject to change or
    53  *  deletion without notice.</b>
    54  */
    55 public class Infer {
    56     protected static final Context.Key<Infer> inferKey =
    57         new Context.Key<Infer>();
    59     /** A value for prototypes that admit any type, including polymorphic ones. */
    60     public static final Type anyPoly = new Type(NONE, null);
    62     Symtab syms;
    63     Types types;
    64     Check chk;
    65     Resolve rs;
    66     DeferredAttr deferredAttr;
    67     Log log;
    68     JCDiagnostic.Factory diags;
    70     public static Infer instance(Context context) {
    71         Infer instance = context.get(inferKey);
    72         if (instance == null)
    73             instance = new Infer(context);
    74         return instance;
    75     }
    77     protected Infer(Context context) {
    78         context.put(inferKey, this);
    79         syms = Symtab.instance(context);
    80         types = Types.instance(context);
    81         rs = Resolve.instance(context);
    82         deferredAttr = DeferredAttr.instance(context);
    83         log = Log.instance(context);
    84         chk = Check.instance(context);
    85         diags = JCDiagnostic.Factory.instance(context);
    86         inferenceException = new InferenceException(diags);
    87     }
    89    /**
    90     * This exception class is design to store a list of diagnostics corresponding
    91     * to inference errors that can arise during a method applicability check.
    92     */
    93     public static class InferenceException extends InapplicableMethodException {
    94         private static final long serialVersionUID = 0;
    96         List<JCDiagnostic> messages = List.nil();
    98         InferenceException(JCDiagnostic.Factory diags) {
    99             super(diags);
   100         }
   102         @Override
   103         InapplicableMethodException setMessage(JCDiagnostic diag) {
   104             messages = messages.append(diag);
   105             return this;
   106         }
   108         @Override
   109         public JCDiagnostic getDiagnostic() {
   110             return messages.head;
   111         }
   113         void clear() {
   114             messages = List.nil();
   115         }
   116     }
   118     private final InferenceException inferenceException;
   120 /***************************************************************************
   121  * Mini/Maximization of UndetVars
   122  ***************************************************************************/
   124     /** Instantiate undetermined type variable to its minimal upper bound.
   125      *  Throw a NoInstanceException if this not possible.
   126      */
   127    void maximizeInst(UndetVar that, Warner warn) throws InferenceException {
   128         List<Type> hibounds = Type.filter(that.getBounds(InferenceBound.UPPER), boundFilter);
   129         if (that.getBounds(InferenceBound.EQ).isEmpty()) {
   130             if (hibounds.isEmpty())
   131                 that.inst = syms.objectType;
   132             else if (hibounds.tail.isEmpty())
   133                 that.inst = hibounds.head;
   134             else
   135                 that.inst = types.glb(hibounds);
   136         } else {
   137             that.inst = that.getBounds(InferenceBound.EQ).head;
   138         }
   139         if (that.inst == null ||
   140             that.inst.isErroneous())
   141             throw inferenceException
   142                 .setMessage("no.unique.maximal.instance.exists",
   143                             that.qtype, hibounds);
   144     }
   146     private Filter<Type> boundFilter = new Filter<Type>() {
   147         @Override
   148         public boolean accepts(Type t) {
   149             return !t.isErroneous() && t.tag != BOT;
   150         }
   151     };
   153     /** Instantiate undetermined type variable to the lub of all its lower bounds.
   154      *  Throw a NoInstanceException if this not possible.
   155      */
   156     void minimizeInst(UndetVar that, Warner warn) throws InferenceException {
   157         List<Type> lobounds = Type.filter(that.getBounds(InferenceBound.LOWER), boundFilter);
   158         if (that.getBounds(InferenceBound.EQ).isEmpty()) {
   159             if (lobounds.isEmpty()) {
   160                 //do nothing - the inference variable is under-constrained
   161                 return;
   162             } else if (lobounds.tail.isEmpty())
   163                 that.inst = lobounds.head.isPrimitive() ? syms.errType : lobounds.head;
   164             else {
   165                 that.inst = types.lub(lobounds);
   166             }
   167             if (that.inst == null || that.inst.tag == ERROR)
   168                     throw inferenceException
   169                         .setMessage("no.unique.minimal.instance.exists",
   170                                     that.qtype, lobounds);
   171         } else {
   172             that.inst = that.getBounds(InferenceBound.EQ).head;
   173         }
   174     }
   176 /***************************************************************************
   177  * Exported Methods
   178  ***************************************************************************/
   180     /**
   181      * Instantiate uninferred inference variables (JLS 15.12.2.8). First
   182      * if the method return type is non-void, we derive constraints from the
   183      * expected type - then we use declared bound well-formedness to derive additional
   184      * constraints. If no instantiation exists, or if several incomparable
   185      * best instantiations exist throw a NoInstanceException.
   186      */
   187     public void instantiateUninferred(DiagnosticPosition pos,
   188             InferenceContext inferenceContext,
   189             MethodType mtype,
   190             Attr.ResultInfo resultInfo,
   191             Warner warn) throws InferenceException {
   192         Type to = resultInfo.pt;
   193         if (to.tag == NONE || resultInfo.checkContext.inferenceContext().free(resultInfo.pt)) {
   194             to = mtype.getReturnType().tag <= VOID ?
   195                     mtype.getReturnType() : syms.objectType;
   196         }
   197         Type qtype1 = inferenceContext.asFree(mtype.getReturnType(), types);
   198         if (!types.isSubtype(qtype1,
   199                 qtype1.tag == UNDETVAR ? types.boxedTypeOrType(to) : to)) {
   200             throw inferenceException
   201                     .setMessage("infer.no.conforming.instance.exists",
   202                     inferenceContext.restvars(), mtype.getReturnType(), to);
   203         }
   205         while (true) {
   206             boolean stuck = true;
   207             for (Type t : inferenceContext.undetvars) {
   208                 UndetVar uv = (UndetVar)t;
   209                 if (uv.inst == null && (uv.getBounds(InferenceBound.EQ).nonEmpty() ||
   210                         !inferenceContext.free(uv.getBounds(InferenceBound.UPPER)))) {
   211                     maximizeInst((UndetVar)t, warn);
   212                     stuck = false;
   213                 }
   214             }
   215             if (inferenceContext.restvars().isEmpty()) {
   216                 //all variables have been instantiated - exit
   217                 break;
   218             } else if (stuck) {
   219                 //some variables could not be instantiated because of cycles in
   220                 //upper bounds - provide a (possibly recursive) default instantiation
   221                 instantiateAsUninferredVars(inferenceContext);
   222                 break;
   223             } else {
   224                 //some variables have been instantiated - replace newly instantiated
   225                 //variables in remaining upper bounds and continue
   226                 for (Type t : inferenceContext.undetvars) {
   227                     UndetVar uv = (UndetVar)t;
   228                     uv.substBounds(inferenceContext.inferenceVars(), inferenceContext.instTypes(), types);
   229                 }
   230             }
   231         }
   232     }
   234     /**
   235      * Infer cyclic inference variables as described in 15.12.2.8.
   236      */
   237     private void instantiateAsUninferredVars(InferenceContext inferenceContext) {
   238         ListBuffer<Type> todo = ListBuffer.lb();
   239         //step 1 - create fresh tvars
   240         for (Type t : inferenceContext.undetvars) {
   241             UndetVar uv = (UndetVar)t;
   242             if (uv.inst == null) {
   243                 TypeSymbol fresh_tvar = new TypeSymbol(Flags.SYNTHETIC, uv.qtype.tsym.name, null, uv.qtype.tsym.owner);
   244                 fresh_tvar.type = new TypeVar(fresh_tvar, types.makeCompoundType(uv.getBounds(InferenceBound.UPPER)), null);
   245                 todo.append(uv);
   246                 uv.inst = fresh_tvar.type;
   247             }
   248         }
   249         //step 2 - replace fresh tvars in their bounds
   250         List<Type> formals = inferenceContext.inferenceVars();
   251         for (Type t : todo) {
   252             UndetVar uv = (UndetVar)t;
   253             TypeVar ct = (TypeVar)uv.inst;
   254             ct.bound = types.glb(inferenceContext.asInstTypes(types.getBounds(ct), types));
   255             if (ct.bound.isErroneous()) {
   256                 //report inference error if glb fails
   257                 reportBoundError(uv, BoundErrorKind.BAD_UPPER);
   258             }
   259             formals = formals.tail;
   260         }
   261     }
   263     /** Instantiate a generic method type by finding instantiations for all its
   264      * inference variables so that it can be applied to a given argument type list.
   265      */
   266     public Type instantiateMethod(Env<AttrContext> env,
   267                                   List<Type> tvars,
   268                                   MethodType mt,
   269                                   Attr.ResultInfo resultInfo,
   270                                   Symbol msym,
   271                                   List<Type> argtypes,
   272                                   boolean allowBoxing,
   273                                   boolean useVarargs,
   274                                   Resolve.MethodResolutionContext resolveContext,
   275                                   Warner warn) throws InferenceException {
   276         //-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG
   277         final InferenceContext inferenceContext = new InferenceContext(tvars, this);
   278         inferenceException.clear();
   280         try {
   281             rs.checkRawArgumentsAcceptable(env, msym, resolveContext.attrMode(), inferenceContext,
   282                     argtypes, mt.getParameterTypes(), allowBoxing, useVarargs, warn,
   283                     new InferenceCheckHandler(inferenceContext));
   285             // minimize as yet undetermined type variables
   286             for (Type t : inferenceContext.undetvars) {
   287                 minimizeInst((UndetVar)t, warn);
   288             }
   290             checkWithinBounds(inferenceContext, warn);
   292             mt = (MethodType)inferenceContext.asInstType(mt, types);
   294             List<Type> restvars = inferenceContext.restvars();
   296             if (!restvars.isEmpty()) {
   297                 if (resultInfo != null && !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) {
   298                     instantiateUninferred(env.tree.pos(), inferenceContext, mt, resultInfo, warn);
   299                     checkWithinBounds(inferenceContext, warn);
   300                     mt = (MethodType)inferenceContext.asInstType(mt, types);
   301                     if (rs.verboseResolutionMode.contains(VerboseResolutionMode.DEFERRED_INST)) {
   302                         log.note(env.tree.pos, "deferred.method.inst", msym, mt, resultInfo.pt);
   303                     }
   304                 }
   305             }
   307             // return instantiated version of method type
   308             return mt;
   309         } finally {
   310             inferenceContext.notifyChange(types);
   311         }
   312     }
   313     //where
   315         /** inference check handler **/
   316         class InferenceCheckHandler implements Resolve.MethodCheckHandler {
   318             InferenceContext inferenceContext;
   320             public InferenceCheckHandler(InferenceContext inferenceContext) {
   321                 this.inferenceContext = inferenceContext;
   322             }
   324             public InapplicableMethodException arityMismatch() {
   325                 return inferenceException.setMessage("infer.arg.length.mismatch", inferenceContext.inferenceVars());
   326             }
   327             public InapplicableMethodException argumentMismatch(boolean varargs, JCDiagnostic details) {
   328                 String key = varargs ?
   329                         "infer.varargs.argument.mismatch" :
   330                         "infer.no.conforming.assignment.exists";
   331                 return inferenceException.setMessage(key,
   332                         inferenceContext.inferenceVars(), details);
   333             }
   334             public InapplicableMethodException inaccessibleVarargs(Symbol location, Type expected) {
   335                 return inferenceException.setMessage("inaccessible.varargs.type",
   336                         expected, Kinds.kindName(location), location);
   337             }
   338         }
   340     /** check that type parameters are within their bounds.
   341      */
   342     void checkWithinBounds(InferenceContext inferenceContext,
   343                            Warner warn) throws InferenceException {
   344         //step 1 - check compatibility of instantiated type w.r.t. initial bounds
   345         for (Type t : inferenceContext.undetvars) {
   346             UndetVar uv = (UndetVar)t;
   347             uv.substBounds(inferenceContext.inferenceVars(), inferenceContext.instTypes(), types);
   348             checkCompatibleUpperBounds(uv, inferenceContext.inferenceVars());
   349             if (!inferenceContext.restvars().contains(uv.qtype)) {
   350                 Type inst = inferenceContext.asInstType(t, types);
   351                 for (Type u : uv.getBounds(InferenceBound.UPPER)) {
   352                     if (!types.isSubtypeUnchecked(inst, inferenceContext.asFree(u, types), warn)) {
   353                         reportBoundError(uv, BoundErrorKind.UPPER);
   354                     }
   355                 }
   356                 for (Type l : uv.getBounds(InferenceBound.LOWER)) {
   357                     Assert.check(!inferenceContext.free(l));
   358                     if (!types.isSubtypeUnchecked(l, inst, warn)) {
   359                         reportBoundError(uv, BoundErrorKind.LOWER);
   360                     }
   361                 }
   362                 for (Type e : uv.getBounds(InferenceBound.EQ)) {
   363                     Assert.check(!inferenceContext.free(e));
   364                     if (!types.isSameType(inst, e)) {
   365                         reportBoundError(uv, BoundErrorKind.EQ);
   366                     }
   367                 }
   368             }
   369         }
   371         //step 2 - check that eq bounds are consistent w.r.t. eq/lower bounds
   372         for (Type t : inferenceContext.undetvars) {
   373             UndetVar uv = (UndetVar)t;
   374             //check eq bounds consistency
   375             Type eq = null;
   376             for (Type e : uv.getBounds(InferenceBound.EQ)) {
   377                 Assert.check(!inferenceContext.free(e));
   378                 if (eq != null && !types.isSameType(e, eq)) {
   379                     reportBoundError(uv, BoundErrorKind.EQ);
   380                 }
   381                 eq = e;
   382                 for (Type l : uv.getBounds(InferenceBound.LOWER)) {
   383                     Assert.check(!inferenceContext.free(l));
   384                     if (!types.isSubtypeUnchecked(l, e, warn)) {
   385                         reportBoundError(uv, BoundErrorKind.BAD_EQ_LOWER);
   386                     }
   387                 }
   388                 for (Type u : uv.getBounds(InferenceBound.UPPER)) {
   389                     if (inferenceContext.free(u)) continue;
   390                     if (!types.isSubtypeUnchecked(e, u, warn)) {
   391                         reportBoundError(uv, BoundErrorKind.BAD_EQ_UPPER);
   392                     }
   393                 }
   394             }
   395         }
   396     }
   398     void checkCompatibleUpperBounds(UndetVar uv, List<Type> tvars) {
   399         // VGJ: sort of inlined maximizeInst() below.  Adding
   400         // bounds can cause lobounds that are above hibounds.
   401         ListBuffer<Type> hiboundsNoVars = ListBuffer.lb();
   402         for (Type t : Type.filter(uv.getBounds(InferenceBound.UPPER), boundFilter)) {
   403             if (!t.containsAny(tvars)) {
   404                 hiboundsNoVars.append(t);
   405             }
   406         }
   407         List<Type> hibounds = hiboundsNoVars.toList();
   408         Type hb = null;
   409         if (hibounds.isEmpty())
   410             hb = syms.objectType;
   411         else if (hibounds.tail.isEmpty())
   412             hb = hibounds.head;
   413         else
   414             hb = types.glb(hibounds);
   415         if (hb == null || hb.isErroneous())
   416             reportBoundError(uv, BoundErrorKind.BAD_UPPER);
   417     }
   419     enum BoundErrorKind {
   420         BAD_UPPER() {
   421             @Override
   422             InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
   423                 return ex.setMessage("incompatible.upper.bounds", uv.qtype,
   424                         uv.getBounds(InferenceBound.UPPER));
   425             }
   426         },
   427         BAD_EQ_UPPER() {
   428             @Override
   429             InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
   430                 return ex.setMessage("incompatible.eq.upper.bounds", uv.qtype,
   431                         uv.getBounds(InferenceBound.EQ), uv.getBounds(InferenceBound.UPPER));
   432             }
   433         },
   434         BAD_EQ_LOWER() {
   435             @Override
   436             InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
   437                 return ex.setMessage("incompatible.eq.lower.bounds", uv.qtype,
   438                         uv.getBounds(InferenceBound.EQ), uv.getBounds(InferenceBound.LOWER));
   439             }
   440         },
   441         UPPER() {
   442             @Override
   443             InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
   444                 return ex.setMessage("inferred.do.not.conform.to.upper.bounds", uv.inst,
   445                         uv.getBounds(InferenceBound.UPPER));
   446             }
   447         },
   448         LOWER() {
   449             @Override
   450             InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
   451                 return ex.setMessage("inferred.do.not.conform.to.lower.bounds", uv.inst,
   452                         uv.getBounds(InferenceBound.LOWER));
   453             }
   454         },
   455         EQ() {
   456             @Override
   457             InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
   458                 return ex.setMessage("inferred.do.not.conform.to.eq.bounds", uv.inst,
   459                         uv.getBounds(InferenceBound.EQ));
   460             }
   461         };
   463         abstract InapplicableMethodException setMessage(InferenceException ex, UndetVar uv);
   464     }
   465     //where
   466     void reportBoundError(UndetVar uv, BoundErrorKind bk) {
   467         throw bk.setMessage(inferenceException, uv);
   468     }
   470     /**
   471      * Compute a synthetic method type corresponding to the requested polymorphic
   472      * method signature. The target return type is computed from the immediately
   473      * enclosing scope surrounding the polymorphic-signature call.
   474      */
   475     Type instantiatePolymorphicSignatureInstance(Env<AttrContext> env,
   476                                             MethodSymbol spMethod,  // sig. poly. method or null if none
   477                                             Resolve.MethodResolutionContext resolveContext,
   478                                             List<Type> argtypes) {
   479         final Type restype;
   481         //The return type for a polymorphic signature call is computed from
   482         //the enclosing tree E, as follows: if E is a cast, then use the
   483         //target type of the cast expression as a return type; if E is an
   484         //expression statement, the return type is 'void' - otherwise the
   485         //return type is simply 'Object'. A correctness check ensures that
   486         //env.next refers to the lexically enclosing environment in which
   487         //the polymorphic signature call environment is nested.
   489         switch (env.next.tree.getTag()) {
   490             case TYPECAST:
   491                 JCTypeCast castTree = (JCTypeCast)env.next.tree;
   492                 restype = (TreeInfo.skipParens(castTree.expr) == env.tree) ?
   493                     castTree.clazz.type :
   494                     syms.objectType;
   495                 break;
   496             case EXEC:
   497                 JCTree.JCExpressionStatement execTree =
   498                         (JCTree.JCExpressionStatement)env.next.tree;
   499                 restype = (TreeInfo.skipParens(execTree.expr) == env.tree) ?
   500                     syms.voidType :
   501                     syms.objectType;
   502                 break;
   503             default:
   504                 restype = syms.objectType;
   505         }
   507         List<Type> paramtypes = Type.map(argtypes, new ImplicitArgType(spMethod, resolveContext.step));
   508         List<Type> exType = spMethod != null ?
   509             spMethod.getThrownTypes() :
   510             List.of(syms.throwableType); // make it throw all exceptions
   512         MethodType mtype = new MethodType(paramtypes,
   513                                           restype,
   514                                           exType,
   515                                           syms.methodClass);
   516         return mtype;
   517     }
   518     //where
   519         class ImplicitArgType extends DeferredAttr.DeferredTypeMap {
   521             public ImplicitArgType(Symbol msym, Resolve.MethodResolutionPhase phase) {
   522                 deferredAttr.super(AttrMode.SPECULATIVE, msym, phase);
   523             }
   525             public Type apply(Type t) {
   526                 t = types.erasure(super.apply(t));
   527                 if (t.tag == BOT)
   528                     // nulls type as the marker type Null (which has no instances)
   529                     // infer as java.lang.Void for now
   530                     t = types.boxedClass(syms.voidType).type;
   531                 return t;
   532             }
   533         }
   535     /**
   536      * Mapping that turns inference variables into undet vars
   537      * (used by inference context)
   538      */
   539     Mapping fromTypeVarFun = new Mapping("fromTypeVarFun") {
   540         public Type apply(Type t) {
   541             if (t.tag == TYPEVAR) return new UndetVar((TypeVar)t, types);
   542             else return t.map(this);
   543         }
   544     };
   546     /**
   547      * An inference context keeps track of the set of variables that are free
   548      * in the current context. It provides utility methods for opening/closing
   549      * types to their corresponding free/closed forms. It also provide hooks for
   550      * attaching deferred post-inference action (see PendingCheck). Finally,
   551      * it can be used as an entry point for performing upper/lower bound inference
   552      * (see InferenceKind).
   553      */
   554     static class InferenceContext {
   556         /**
   557         * Single-method-interface for defining inference callbacks. Certain actions
   558         * (i.e. subtyping checks) might need to be redone after all inference variables
   559         * have been fixed.
   560         */
   561         interface FreeTypeListener {
   562             void typesInferred(InferenceContext inferenceContext);
   563         }
   565         /** list of inference vars as undet vars */
   566         List<Type> undetvars;
   568         /** list of inference vars in this context */
   569         List<Type> inferencevars;
   571         java.util.Map<FreeTypeListener, List<Type>> freeTypeListeners =
   572                 new java.util.HashMap<FreeTypeListener, List<Type>>();
   574         List<FreeTypeListener> freetypeListeners = List.nil();
   576         public InferenceContext(List<Type> inferencevars, Infer infer) {
   577             this.undetvars = Type.map(inferencevars, infer.fromTypeVarFun);
   578             this.inferencevars = inferencevars;
   579         }
   581         /**
   582          * returns the list of free variables (as type-variables) in this
   583          * inference context
   584          */
   585         List<Type> inferenceVars() {
   586             return inferencevars;
   587         }
   589         /**
   590          * returns the list of uninstantiated variables (as type-variables) in this
   591          * inference context (usually called after instantiate())
   592          */
   593         List<Type> restvars() {
   594             List<Type> undetvars = this.undetvars;
   595             ListBuffer<Type> restvars = ListBuffer.lb();
   596             for (Type t : instTypes()) {
   597                 UndetVar uv = (UndetVar)undetvars.head;
   598                 if (uv.qtype == t) {
   599                     restvars.append(t);
   600                 }
   601                 undetvars = undetvars.tail;
   602             }
   603             return restvars.toList();
   604         }
   606         /**
   607          * is this type free?
   608          */
   609         final boolean free(Type t) {
   610             return t.containsAny(inferencevars);
   611         }
   613         final boolean free(List<Type> ts) {
   614             for (Type t : ts) {
   615                 if (free(t)) return true;
   616             }
   617             return false;
   618         }
   620         /**
   621          * Returns a list of free variables in a given type
   622          */
   623         final List<Type> freeVarsIn(Type t) {
   624             ListBuffer<Type> buf = ListBuffer.lb();
   625             for (Type iv : inferenceVars()) {
   626                 if (t.contains(iv)) {
   627                     buf.add(iv);
   628                 }
   629             }
   630             return buf.toList();
   631         }
   633         final List<Type> freeVarsIn(List<Type> ts) {
   634             ListBuffer<Type> buf = ListBuffer.lb();
   635             for (Type t : ts) {
   636                 buf.appendList(freeVarsIn(t));
   637             }
   638             ListBuffer<Type> buf2 = ListBuffer.lb();
   639             for (Type t : buf) {
   640                 if (!buf2.contains(t)) {
   641                     buf2.add(t);
   642                 }
   643             }
   644             return buf2.toList();
   645         }
   647         /**
   648          * Replace all free variables in a given type with corresponding
   649          * undet vars (used ahead of subtyping/compatibility checks to allow propagation
   650          * of inference constraints).
   651          */
   652         final Type asFree(Type t, Types types) {
   653             return types.subst(t, inferencevars, undetvars);
   654         }
   656         final List<Type> asFree(List<Type> ts, Types types) {
   657             ListBuffer<Type> buf = ListBuffer.lb();
   658             for (Type t : ts) {
   659                 buf.append(asFree(t, types));
   660             }
   661             return buf.toList();
   662         }
   664         List<Type> instTypes() {
   665             ListBuffer<Type> buf = ListBuffer.lb();
   666             for (Type t : undetvars) {
   667                 UndetVar uv = (UndetVar)t;
   668                 buf.append(uv.inst != null ? uv.inst : uv.qtype);
   669             }
   670             return buf.toList();
   671         }
   673         /**
   674          * Replace all free variables in a given type with corresponding
   675          * instantiated types - if one or more free variable has not been
   676          * fully instantiated, it will still be available in the resulting type.
   677          */
   678         Type asInstType(Type t, Types types) {
   679             return types.subst(t, inferencevars, instTypes());
   680         }
   682         List<Type> asInstTypes(List<Type> ts, Types types) {
   683             ListBuffer<Type> buf = ListBuffer.lb();
   684             for (Type t : ts) {
   685                 buf.append(asInstType(t, types));
   686             }
   687             return buf.toList();
   688         }
   690         /**
   691          * Add custom hook for performing post-inference action
   692          */
   693         void addFreeTypeListener(List<Type> types, FreeTypeListener ftl) {
   694             freeTypeListeners.put(ftl, freeVarsIn(types));
   695         }
   697         /**
   698          * Mark the inference context as complete and trigger evaluation
   699          * of all deferred checks.
   700          */
   701         void notifyChange(Types types) {
   702             InferenceException thrownEx = null;
   703             for (Map.Entry<FreeTypeListener, List<Type>> entry :
   704                     new HashMap<FreeTypeListener, List<Type>>(freeTypeListeners).entrySet()) {
   705                 if (!Type.containsAny(entry.getValue(), restvars())) {
   706                     try {
   707                         entry.getKey().typesInferred(this);
   708                         freeTypeListeners.remove(entry.getKey());
   709                     } catch (InferenceException ex) {
   710                         if (thrownEx == null) {
   711                             thrownEx = ex;
   712                         }
   713                     }
   714                 }
   715             }
   716             //inference exception multiplexing - present any inference exception
   717             //thrown when processing listeners as a single one
   718             if (thrownEx != null) {
   719                 throw thrownEx;
   720             }
   721         }
   723         void solveAny(List<Type> varsToSolve, Types types, Infer infer) {
   724             boolean progress = false;
   725             for (Type t : varsToSolve) {
   726                 UndetVar uv = (UndetVar)asFree(t, types);
   727                 if (uv.inst == null) {
   728                     infer.minimizeInst(uv, Warner.noWarnings);
   729                     if (uv.inst != null) {
   730                         progress = true;
   731                     }
   732                 }
   733             }
   734             if (!progress) {
   735                 throw infer.inferenceException.setMessage("cyclic.inference", varsToSolve);
   736             }
   737         }
   738     }
   740     final InferenceContext emptyContext = new InferenceContext(List.<Type>nil(), this);
   741 }

mercurial