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

Fri, 29 Aug 2008 11:10:12 -0700

author
jjg
date
Fri, 29 Aug 2008 11:10:12 -0700
changeset 104
5e89c4ca637c
parent 89
b6d5f53b3b29
child 110
91eea580fbe9
permissions
-rw-r--r--

6597471: unused imports in javax.tools.JavaCompiler
6597531: unused imports and unused private const. in com.sun.tools.javac.Server.java
Reviewed-by: mcimadamore
Contributed-by: davide.angelocola@gmail.com

     1 /*
     2  * Copyright 1999-2008 Sun Microsystems, Inc.  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.  Sun designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
    23  * have any questions.
    24  */
    26 package com.sun.tools.javac.comp;
    28 import com.sun.tools.javac.util.*;
    29 import com.sun.tools.javac.util.List;
    30 import com.sun.tools.javac.code.*;
    31 import com.sun.tools.javac.code.Type.*;
    32 import com.sun.tools.javac.util.JCDiagnostic;
    34 import static com.sun.tools.javac.code.TypeTags.*;
    36 /** Helper class for type parameter inference, used by the attribution phase.
    37  *
    38  *  <p><b>This is NOT part of any API supported by Sun Microsystems.  If
    39  *  you write code that depends on this, you do so at your own risk.
    40  *  This code and its internal interfaces are subject to change or
    41  *  deletion without notice.</b>
    42  */
    43 public class Infer {
    44     protected static final Context.Key<Infer> inferKey =
    45         new Context.Key<Infer>();
    47     /** A value for prototypes that admit any type, including polymorphic ones. */
    48     public static final Type anyPoly = new Type(NONE, null);
    50     Symtab syms;
    51     Types types;
    52     JCDiagnostic.Factory diags;
    54     public static Infer instance(Context context) {
    55         Infer instance = context.get(inferKey);
    56         if (instance == null)
    57             instance = new Infer(context);
    58         return instance;
    59     }
    61     protected Infer(Context context) {
    62         context.put(inferKey, this);
    63         syms = Symtab.instance(context);
    64         types = Types.instance(context);
    65         diags = JCDiagnostic.Factory.instance(context);
    66         ambiguousNoInstanceException =
    67             new NoInstanceException(true, diags);
    68         unambiguousNoInstanceException =
    69             new NoInstanceException(false, diags);
    70     }
    72     public static class NoInstanceException extends RuntimeException {
    73         private static final long serialVersionUID = 0;
    75         boolean isAmbiguous; // exist several incomparable best instances?
    77         JCDiagnostic diagnostic;
    78         JCDiagnostic.Factory diags;
    80         NoInstanceException(boolean isAmbiguous, JCDiagnostic.Factory diags) {
    81             this.diagnostic = null;
    82             this.isAmbiguous = isAmbiguous;
    83             this.diags = diags;
    84         }
    85         NoInstanceException setMessage(String key) {
    86             this.diagnostic = diags.fragment(key);
    87             return this;
    88         }
    89         NoInstanceException setMessage(String key, Object arg1) {
    90             this.diagnostic = diags.fragment(key, arg1);
    91             return this;
    92         }
    93         NoInstanceException setMessage(String key, Object arg1, Object arg2) {
    94             this.diagnostic = diags.fragment(key, arg1, arg2);
    95             return this;
    96         }
    97         NoInstanceException setMessage(String key, Object arg1, Object arg2, Object arg3) {
    98             this.diagnostic = diags.fragment(key, arg1, arg2, arg3);
    99             return this;
   100         }
   101         public JCDiagnostic getDiagnostic() {
   102             return diagnostic;
   103         }
   104     }
   105     private final NoInstanceException ambiguousNoInstanceException;
   106     private final NoInstanceException unambiguousNoInstanceException;
   108 /***************************************************************************
   109  * Auxiliary type values and classes
   110  ***************************************************************************/
   112     /** A mapping that turns type variables into undetermined type variables.
   113      */
   114     Mapping fromTypeVarFun = new Mapping("fromTypeVarFun") {
   115             public Type apply(Type t) {
   116                 if (t.tag == TYPEVAR) return new UndetVar(t);
   117                 else return t.map(this);
   118             }
   119         };
   121     /** A mapping that returns its type argument with every UndetVar replaced
   122      *  by its `inst' field. Throws a NoInstanceException
   123      *  if this not possible because an `inst' field is null.
   124      */
   125     Mapping getInstFun = new Mapping("getInstFun") {
   126             public Type apply(Type t) {
   127                 switch (t.tag) {
   128                 case UNKNOWN:
   129                     throw ambiguousNoInstanceException
   130                         .setMessage("undetermined.type");
   131                 case UNDETVAR:
   132                     UndetVar that = (UndetVar) t;
   133                     if (that.inst == null)
   134                         throw ambiguousNoInstanceException
   135                             .setMessage("type.variable.has.undetermined.type",
   136                                         that.qtype);
   137                     return apply(that.inst);
   138                 default:
   139                     return t.map(this);
   140                 }
   141             }
   142         };
   144 /***************************************************************************
   145  * Mini/Maximization of UndetVars
   146  ***************************************************************************/
   148     /** Instantiate undetermined type variable to its minimal upper bound.
   149      *  Throw a NoInstanceException if this not possible.
   150      */
   151     void maximizeInst(UndetVar that, Warner warn) throws NoInstanceException {
   152         if (that.inst == null) {
   153             if (that.hibounds.isEmpty())
   154                 that.inst = syms.objectType;
   155             else if (that.hibounds.tail.isEmpty())
   156                 that.inst = that.hibounds.head;
   157             else {
   158                 for (List<Type> bs = that.hibounds;
   159                      bs.nonEmpty() && that.inst == null;
   160                      bs = bs.tail) {
   161                     // System.out.println("hibounds = " + that.hibounds);//DEBUG
   162                     if (isSubClass(bs.head, that.hibounds))
   163                         that.inst = types.fromUnknownFun.apply(bs.head);
   164                 }
   165                 if (that.inst == null) {
   166                     int classCount = 0, interfaceCount = 0;
   167                     for (Type t : that.hibounds) {
   168                         if (t.tag == CLASS) {
   169                             if (t.isInterface())
   170                                 interfaceCount++;
   171                             else
   172                                 classCount++;
   173                         }
   174                     }
   175                     if ((that.hibounds.size() == classCount + interfaceCount) && classCount == 1)
   176                         that.inst = types.makeCompoundType(that.hibounds);
   177                 }
   178                 if (that.inst == null || !types.isSubtypeUnchecked(that.inst, that.hibounds, warn))
   179                     throw ambiguousNoInstanceException
   180                         .setMessage("no.unique.maximal.instance.exists",
   181                                     that.qtype, that.hibounds);
   182             }
   183         }
   184     }
   185     //where
   186         private boolean isSubClass(Type t, final List<Type> ts) {
   187             t = t.baseType();
   188             if (t.tag == TYPEVAR) {
   189                 List<Type> bounds = types.getBounds((TypeVar)t);
   190                 for (Type s : ts) {
   191                     if (!types.isSameType(t, s.baseType())) {
   192                         for (Type bound : bounds) {
   193                             if (!isSubClass(bound, List.of(s.baseType())))
   194                                 return false;
   195                         }
   196                     }
   197                 }
   198             } else {
   199                 for (Type s : ts) {
   200                     if (!t.tsym.isSubClass(s.baseType().tsym, types))
   201                         return false;
   202                 }
   203             }
   204             return true;
   205         }
   207     /** Instaniate undetermined type variable to the lub of all its lower bounds.
   208      *  Throw a NoInstanceException if this not possible.
   209      */
   210     void minimizeInst(UndetVar that, Warner warn) throws NoInstanceException {
   211         if (that.inst == null) {
   212             if (that.lobounds.isEmpty())
   213                 that.inst = syms.botType;
   214             else if (that.lobounds.tail.isEmpty())
   215                 that.inst = that.lobounds.head.isPrimitive() ? syms.errType : that.lobounds.head;
   216             else {
   217                 that.inst = types.lub(that.lobounds);
   218             }
   219             if (that.inst == null || that.inst == syms.errType)
   220                     throw ambiguousNoInstanceException
   221                         .setMessage("no.unique.minimal.instance.exists",
   222                                     that.qtype, that.lobounds);
   223             // VGJ: sort of inlined maximizeInst() below.  Adding
   224             // bounds can cause lobounds that are above hibounds.
   225             if (that.hibounds.isEmpty())
   226                 return;
   227             Type hb = null;
   228             if (that.hibounds.tail.isEmpty())
   229                 hb = that.hibounds.head;
   230             else for (List<Type> bs = that.hibounds;
   231                       bs.nonEmpty() && hb == null;
   232                       bs = bs.tail) {
   233                 if (isSubClass(bs.head, that.hibounds))
   234                     hb = types.fromUnknownFun.apply(bs.head);
   235             }
   236             if (hb == null ||
   237                 !types.isSubtypeUnchecked(hb, that.hibounds, warn) ||
   238                 !types.isSubtypeUnchecked(that.inst, hb, warn))
   239                 throw ambiguousNoInstanceException;
   240         }
   241     }
   243 /***************************************************************************
   244  * Exported Methods
   245  ***************************************************************************/
   247     /** Try to instantiate expression type `that' to given type `to'.
   248      *  If a maximal instantiation exists which makes this type
   249      *  a subtype of type `to', return the instantiated type.
   250      *  If no instantiation exists, or if several incomparable
   251      *  best instantiations exist throw a NoInstanceException.
   252      */
   253     public Type instantiateExpr(ForAll that,
   254                                 Type to,
   255                                 Warner warn) throws NoInstanceException {
   256         List<Type> undetvars = Type.map(that.tvars, fromTypeVarFun);
   257         for (List<Type> l = undetvars; l.nonEmpty(); l = l.tail) {
   258             UndetVar v = (UndetVar) l.head;
   259             ListBuffer<Type> hibounds = new ListBuffer<Type>();
   260             for (List<Type> l1 = types.getBounds((TypeVar) v.qtype); l1.nonEmpty(); l1 = l1.tail) {
   261                 if (!l1.head.containsSome(that.tvars)) {
   262                     hibounds.append(l1.head);
   263                 }
   264             }
   265             v.hibounds = hibounds.toList();
   266         }
   267         Type qtype1 = types.subst(that.qtype, that.tvars, undetvars);
   268         if (!types.isSubtype(qtype1, to)) {
   269             throw unambiguousNoInstanceException
   270                 .setMessage("no.conforming.instance.exists",
   271                             that.tvars, that.qtype, to);
   272         }
   273         for (List<Type> l = undetvars; l.nonEmpty(); l = l.tail)
   274             maximizeInst((UndetVar) l.head, warn);
   275         // System.out.println(" = " + qtype1.map(getInstFun));//DEBUG
   277         // check bounds
   278         List<Type> targs = Type.map(undetvars, getInstFun);
   279         targs = types.subst(targs, that.tvars, targs);
   280         checkWithinBounds(that.tvars, targs, warn);
   282         return getInstFun.apply(qtype1);
   283     }
   285     /** Instantiate method type `mt' by finding instantiations of
   286      *  `tvars' so that method can be applied to `argtypes'.
   287      */
   288     public Type instantiateMethod(List<Type> tvars,
   289                                   MethodType mt,
   290                                   List<Type> argtypes,
   291                                   boolean allowBoxing,
   292                                   boolean useVarargs,
   293                                   Warner warn) throws NoInstanceException {
   294         //-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG
   295         List<Type> undetvars = Type.map(tvars, fromTypeVarFun);
   296         List<Type> formals = mt.argtypes;
   298         // instantiate all polymorphic argument types and
   299         // set up lower bounds constraints for undetvars
   300         Type varargsFormal = useVarargs ? formals.last() : null;
   301         while (argtypes.nonEmpty() && formals.head != varargsFormal) {
   302             Type ft = formals.head;
   303             Type at = argtypes.head.baseType();
   304             if (at.tag == FORALL)
   305                 at = instantiateArg((ForAll) at, ft, tvars, warn);
   306             Type sft = types.subst(ft, tvars, undetvars);
   307             boolean works = allowBoxing
   308                 ? types.isConvertible(at, sft, warn)
   309                 : types.isSubtypeUnchecked(at, sft, warn);
   310             if (!works) {
   311                 throw unambiguousNoInstanceException
   312                     .setMessage("no.conforming.assignment.exists",
   313                                 tvars, at, ft);
   314             }
   315             formals = formals.tail;
   316             argtypes = argtypes.tail;
   317         }
   318         if (formals.head != varargsFormal || // not enough args
   319             !useVarargs && argtypes.nonEmpty()) { // too many args
   320             // argument lists differ in length
   321             throw unambiguousNoInstanceException
   322                 .setMessage("arg.length.mismatch");
   323         }
   325         // for varargs arguments as well
   326         if (useVarargs) {
   327             Type elt = types.elemtype(varargsFormal);
   328             Type sft = types.subst(elt, tvars, undetvars);
   329             while (argtypes.nonEmpty()) {
   330                 Type ft = sft;
   331                 Type at = argtypes.head.baseType();
   332                 if (at.tag == FORALL)
   333                     at = instantiateArg((ForAll) at, ft, tvars, warn);
   334                 boolean works = types.isConvertible(at, sft, warn);
   335                 if (!works) {
   336                     throw unambiguousNoInstanceException
   337                         .setMessage("no.conforming.assignment.exists",
   338                                     tvars, at, ft);
   339                 }
   340                 argtypes = argtypes.tail;
   341             }
   342         }
   344         // minimize as yet undetermined type variables
   345         for (Type t : undetvars)
   346             minimizeInst((UndetVar) t, warn);
   348         /** Type variables instantiated to bottom */
   349         ListBuffer<Type> restvars = new ListBuffer<Type>();
   351         /** Instantiated types or TypeVars if under-constrained */
   352         ListBuffer<Type> insttypes = new ListBuffer<Type>();
   354         /** Instantiated types or UndetVars if under-constrained */
   355         ListBuffer<Type> undettypes = new ListBuffer<Type>();
   357         for (Type t : undetvars) {
   358             UndetVar uv = (UndetVar)t;
   359             if (uv.inst.tag == BOT) {
   360                 restvars.append(uv.qtype);
   361                 insttypes.append(uv.qtype);
   362                 undettypes.append(uv);
   363                 uv.inst = null;
   364             } else {
   365                 insttypes.append(uv.inst);
   366                 undettypes.append(uv.inst);
   367             }
   368         }
   369         checkWithinBounds(tvars, undettypes.toList(), warn);
   371         if (!restvars.isEmpty()) {
   372             // if there are uninstantiated variables,
   373             // quantify result type with them
   374             mt = new MethodType(mt.argtypes,
   375                                 new ForAll(restvars.toList(), mt.restype),
   376                                 mt.thrown, syms.methodClass);
   377         }
   379         // return instantiated version of method type
   380         return types.subst(mt, tvars, insttypes.toList());
   381     }
   382     //where
   384         /** Try to instantiate argument type `that' to given type `to'.
   385          *  If this fails, try to insantiate `that' to `to' where
   386          *  every occurrence of a type variable in `tvars' is replaced
   387          *  by an unknown type.
   388          */
   389         private Type instantiateArg(ForAll that,
   390                                     Type to,
   391                                     List<Type> tvars,
   392                                     Warner warn) throws NoInstanceException {
   393             List<Type> targs;
   394             try {
   395                 return instantiateExpr(that, to, warn);
   396             } catch (NoInstanceException ex) {
   397                 Type to1 = to;
   398                 for (List<Type> l = tvars; l.nonEmpty(); l = l.tail)
   399                     to1 = types.subst(to1, List.of(l.head), List.of(syms.unknownType));
   400                 return instantiateExpr(that, to1, warn);
   401             }
   402         }
   404     /** check that type parameters are within their bounds.
   405      */
   406     private void checkWithinBounds(List<Type> tvars,
   407                                    List<Type> arguments,
   408                                    Warner warn)
   409         throws NoInstanceException {
   410         for (List<Type> tvs = tvars, args = arguments;
   411              tvs.nonEmpty();
   412              tvs = tvs.tail, args = args.tail) {
   413             if (args.head instanceof UndetVar) continue;
   414             List<Type> bounds = types.subst(types.getBounds((TypeVar)tvs.head), tvars, arguments);
   415             if (!types.isSubtypeUnchecked(args.head, bounds, warn))
   416                 throw unambiguousNoInstanceException
   417                     .setMessage("inferred.do.not.conform.to.bounds",
   418                                 arguments, tvars);
   419         }
   420     }
   421 }

mercurial