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

Tue, 04 Mar 2008 15:45:20 +0000

author
mcimadamore
date
Tue, 04 Mar 2008 15:45:20 +0000
changeset 8
38bd6375f37d
parent 5
b45f8d4794b7
child 17
6e4cefcce80a
permissions
-rw-r--r--

6663588: Compiler goes into infinite loop for Cyclic Inheritance test case
Summary: interplay between cyclic inheritance and tvar bounds hangs javac
Reviewed-by: jjg

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

mercurial