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

Tue, 25 Sep 2012 11:56:46 +0100

author
mcimadamore
date
Tue, 25 Sep 2012 11:56:46 +0100
changeset 1338
ad2ca2a4ab5e
parent 1337
2eca84194807
child 1347
1408af4cd8b0
permissions
-rw-r--r--

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

mercurial