duke@1: /* mcimadamore@1178: * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@1: * duke@1: * This code is free software; you can redistribute it and/or modify it duke@1: * under the terms of the GNU General Public License version 2 only, as ohair@554: * published by the Free Software Foundation. Oracle designates this duke@1: * particular file as subject to the "Classpath" exception as provided ohair@554: * by Oracle in the LICENSE file that accompanied this code. duke@1: * duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@1: * version 2 for more details (a copy is included in the LICENSE file that duke@1: * accompanied this code). duke@1: * duke@1: * You should have received a copy of the GNU General Public License version duke@1: * 2 along with this work; if not, write to the Free Software Foundation, duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@1: * ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@554: * or visit www.oracle.com if you need additional information or have any ohair@554: * questions. duke@1: */ duke@1: duke@1: package com.sun.tools.javac.comp; duke@1: mcimadamore@1337: import com.sun.tools.javac.code.*; mcimadamore@1337: import com.sun.tools.javac.code.Symbol.*; mcimadamore@1337: import com.sun.tools.javac.code.Type.*; mcimadamore@1337: import com.sun.tools.javac.comp.Resolve.InapplicableMethodException; mcimadamore@1337: import com.sun.tools.javac.comp.Resolve.VerboseResolutionMode; mcimadamore@674: import com.sun.tools.javac.tree.JCTree; mcimadamore@674: import com.sun.tools.javac.tree.JCTree.JCTypeCast; mcimadamore@820: import com.sun.tools.javac.tree.TreeInfo; duke@1: import com.sun.tools.javac.util.*; duke@1: import com.sun.tools.javac.util.List; mcimadamore@1114: import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; duke@1: mcimadamore@1337: import java.util.HashMap; mcimadamore@1337: import java.util.Map; mcimadamore@1337: duke@1: import static com.sun.tools.javac.code.TypeTags.*; duke@1: duke@1: /** Helper class for type parameter inference, used by the attribution phase. duke@1: * jjg@581: *

This is NOT part of any supported API. jjg@581: * If you write code that depends on this, you do so at your own risk. duke@1: * This code and its internal interfaces are subject to change or duke@1: * deletion without notice. duke@1: */ duke@1: public class Infer { duke@1: protected static final Context.Key inferKey = duke@1: new Context.Key(); duke@1: duke@1: /** A value for prototypes that admit any type, including polymorphic ones. */ duke@1: public static final Type anyPoly = new Type(NONE, null); duke@1: duke@1: Symtab syms; duke@1: Types types; mcimadamore@396: Check chk; mcimadamore@299: Resolve rs; mcimadamore@1114: Log log; mcimadamore@89: JCDiagnostic.Factory diags; duke@1: duke@1: public static Infer instance(Context context) { duke@1: Infer instance = context.get(inferKey); duke@1: if (instance == null) duke@1: instance = new Infer(context); duke@1: return instance; duke@1: } duke@1: duke@1: protected Infer(Context context) { duke@1: context.put(inferKey, this); duke@1: syms = Symtab.instance(context); duke@1: types = Types.instance(context); mcimadamore@299: rs = Resolve.instance(context); mcimadamore@1114: log = Log.instance(context); mcimadamore@396: chk = Check.instance(context); mcimadamore@89: diags = JCDiagnostic.Factory.instance(context); mcimadamore@1298: inferenceException = new InferenceException(diags); duke@1: } duke@1: mcimadamore@1337: /** mcimadamore@1337: * This exception class is design to store a list of diagnostics corresponding mcimadamore@1337: * to inference errors that can arise during a method applicability check. mcimadamore@1337: */ mcimadamore@1186: public static class InferenceException extends InapplicableMethodException { duke@1: private static final long serialVersionUID = 0; duke@1: mcimadamore@1337: List messages = List.nil(); mcimadamore@1337: mcimadamore@299: InferenceException(JCDiagnostic.Factory diags) { mcimadamore@689: super(diags); duke@1: } mcimadamore@1337: mcimadamore@1337: @Override mcimadamore@1337: InapplicableMethodException setMessage(JCDiagnostic diag) { mcimadamore@1337: messages = messages.append(diag); mcimadamore@1337: return this; mcimadamore@1337: } mcimadamore@1337: mcimadamore@1337: @Override mcimadamore@1337: public JCDiagnostic getDiagnostic() { mcimadamore@1337: return messages.head; mcimadamore@1337: } mcimadamore@1337: mcimadamore@1337: void clear() { mcimadamore@1337: messages = List.nil(); mcimadamore@1337: } mcimadamore@299: } mcimadamore@299: mcimadamore@1298: private final InferenceException inferenceException; duke@1: duke@1: /*************************************************************************** duke@1: * Mini/Maximization of UndetVars duke@1: ***************************************************************************/ duke@1: duke@1: /** Instantiate undetermined type variable to its minimal upper bound. duke@1: * Throw a NoInstanceException if this not possible. duke@1: */ mcimadamore@1337: void maximizeInst(UndetVar that, Warner warn) throws InferenceException { mcimadamore@1337: List hibounds = Type.filter(that.hibounds, boundFilter); mcimadamore@1251: if (that.eq.isEmpty()) { mcimadamore@828: if (hibounds.isEmpty()) duke@1: that.inst = syms.objectType; mcimadamore@828: else if (hibounds.tail.isEmpty()) mcimadamore@828: that.inst = hibounds.head; mcimadamore@210: else mcimadamore@828: that.inst = types.glb(hibounds); mcimadamore@1251: } else { mcimadamore@1251: that.inst = that.eq.head; duke@1: } mcimadamore@210: if (that.inst == null || mcimadamore@298: that.inst.isErroneous()) mcimadamore@1298: throw inferenceException mcimadamore@210: .setMessage("no.unique.maximal.instance.exists", mcimadamore@828: that.qtype, hibounds); duke@1: } duke@1: mcimadamore@1337: private Filter boundFilter = new Filter() { mcimadamore@828: @Override mcimadamore@828: public boolean accepts(Type t) { mcimadamore@1337: return !t.isErroneous() && t.tag != BOT; mcimadamore@828: } mcimadamore@828: }; mcimadamore@828: jjg@110: /** Instantiate undetermined type variable to the lub of all its lower bounds. duke@1: * Throw a NoInstanceException if this not possible. duke@1: */ mcimadamore@1298: void minimizeInst(UndetVar that, Warner warn) throws InferenceException { mcimadamore@1337: List lobounds = Type.filter(that.lobounds, boundFilter); mcimadamore@1251: if (that.eq.isEmpty()) { mcimadamore@1337: if (lobounds.isEmpty()) { mcimadamore@1337: //do nothing - the inference variable is under-constrained mcimadamore@1337: return; mcimadamore@1337: } else if (lobounds.tail.isEmpty()) mcimadamore@828: that.inst = lobounds.head.isPrimitive() ? syms.errType : lobounds.head; duke@1: else { mcimadamore@828: that.inst = types.lub(lobounds); mcimadamore@5: } jjg@110: if (that.inst == null || that.inst.tag == ERROR) mcimadamore@1298: throw inferenceException duke@1: .setMessage("no.unique.minimal.instance.exists", mcimadamore@828: that.qtype, lobounds); mcimadamore@1251: } else { mcimadamore@1251: that.inst = that.eq.head; duke@1: } duke@1: } duke@1: duke@1: /*************************************************************************** duke@1: * Exported Methods duke@1: ***************************************************************************/ duke@1: mcimadamore@1337: /** mcimadamore@1337: * Instantiate uninferred inference variables (JLS 15.12.2.8). First mcimadamore@1337: * if the method return type is non-void, we derive constraints from the mcimadamore@1337: * expected type - then we use declared bound well-formedness to derive additional mcimadamore@1337: * constraints. If no instantiation exists, or if several incomparable mcimadamore@1337: * best instantiations exist throw a NoInstanceException. duke@1: */ mcimadamore@1337: public void instantiateUninferred(DiagnosticPosition pos, mcimadamore@1337: InferenceContext inferenceContext, mcimadamore@1337: MethodType mtype, mcimadamore@1337: Attr.ResultInfo resultInfo, mcimadamore@1337: Warner warn) throws InferenceException { mcimadamore@1268: Type to = resultInfo.pt; mcimadamore@1268: if (to.tag == NONE) { mcimadamore@1268: to = mtype.getReturnType().tag <= VOID ? mcimadamore@1268: mtype.getReturnType() : syms.objectType; mcimadamore@1268: } mcimadamore@1337: Type qtype1 = inferenceContext.asFree(mtype.getReturnType(), types); mcimadamore@753: if (!types.isSubtype(qtype1, mcimadamore@753: qtype1.tag == UNDETVAR ? types.boxedTypeOrType(to) : to)) { mcimadamore@1298: throw inferenceException mcimadamore@1337: .setMessage("infer.no.conforming.instance.exists", mcimadamore@1337: inferenceContext.restvars(), mtype.getReturnType(), to); duke@1: } duke@1: mcimadamore@1251: while (true) { mcimadamore@1251: boolean stuck = true; mcimadamore@1337: for (Type t : inferenceContext.undetvars) { mcimadamore@1251: UndetVar uv = (UndetVar)t; mcimadamore@1337: if (uv.inst == null && (uv.eq.nonEmpty() || !inferenceContext.free(uv.hibounds))) { mcimadamore@1251: maximizeInst((UndetVar)t, warn); mcimadamore@1251: stuck = false; mcimadamore@1251: } mcimadamore@1251: } mcimadamore@1337: if (inferenceContext.restvars().isEmpty()) { mcimadamore@1251: //all variables have been instantiated - exit mcimadamore@1251: break; mcimadamore@1251: } else if (stuck) { mcimadamore@1251: //some variables could not be instantiated because of cycles in mcimadamore@1251: //upper bounds - provide a (possibly recursive) default instantiation mcimadamore@1337: instantiateAsUninferredVars(inferenceContext); mcimadamore@1251: break; mcimadamore@1251: } else { mcimadamore@1251: //some variables have been instantiated - replace newly instantiated mcimadamore@1251: //variables in remaining upper bounds and continue mcimadamore@1337: for (Type t : inferenceContext.undetvars) { mcimadamore@1251: UndetVar uv = (UndetVar)t; mcimadamore@1337: uv.hibounds = inferenceContext.asInstTypes(uv.hibounds, types); mcimadamore@1251: } mcimadamore@1251: } mcimadamore@635: } duke@1: } mcimadamore@1251: mcimadamore@1251: /** mcimadamore@1251: * Infer cyclic inference variables as described in 15.12.2.8. mcimadamore@1251: */ mcimadamore@1337: private void instantiateAsUninferredVars(InferenceContext inferenceContext) { mcimadamore@1251: ListBuffer todo = ListBuffer.lb(); mcimadamore@1251: //step 1 - create fresh tvars mcimadamore@1337: for (Type t : inferenceContext.undetvars) { mcimadamore@635: UndetVar uv = (UndetVar)t; mcimadamore@1251: if (uv.inst == null) { mcimadamore@1251: TypeSymbol fresh_tvar = new TypeSymbol(Flags.SYNTHETIC, uv.qtype.tsym.name, null, uv.qtype.tsym.owner); mcimadamore@1251: fresh_tvar.type = new TypeVar(fresh_tvar, types.makeCompoundType(uv.hibounds), null); mcimadamore@1251: todo.append(uv); mcimadamore@1251: uv.inst = fresh_tvar.type; mcimadamore@1251: } mcimadamore@635: } mcimadamore@1251: //step 2 - replace fresh tvars in their bounds mcimadamore@1337: List formals = inferenceContext.inferenceVars(); mcimadamore@1251: for (Type t : todo) { mcimadamore@1251: UndetVar uv = (UndetVar)t; mcimadamore@1251: TypeVar ct = (TypeVar)uv.inst; mcimadamore@1337: ct.bound = types.glb(inferenceContext.asInstTypes(types.getBounds(ct), types)); mcimadamore@1251: if (ct.bound.isErroneous()) { mcimadamore@1251: //report inference error if glb fails mcimadamore@1251: reportBoundError(uv, BoundErrorKind.BAD_UPPER); mcimadamore@1251: } mcimadamore@1178: formals = formals.tail; mcimadamore@635: } mcimadamore@635: } duke@1: mcimadamore@1337: /** Instantiate a generic method type by finding instantiations for all its mcimadamore@1337: * inference variables so that it can be applied to a given argument type list. duke@1: */ mcimadamore@1268: public Type instantiateMethod(Env env, mcimadamore@547: List tvars, duke@1: MethodType mt, mcimadamore@1268: Attr.ResultInfo resultInfo, mcimadamore@1268: Symbol msym, mcimadamore@1268: List argtypes, mcimadamore@1268: boolean allowBoxing, mcimadamore@1268: boolean useVarargs, mcimadamore@1268: Warner warn) throws InferenceException { duke@1: //-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG mcimadamore@1337: final InferenceContext inferenceContext = new InferenceContext(tvars, types); mcimadamore@1337: inferenceException.clear(); mcimadamore@689: mcimadamore@1337: try { mcimadamore@1337: rs.checkRawArgumentsAcceptable(env, inferenceContext, argtypes, mt.getParameterTypes(), mcimadamore@1337: allowBoxing, useVarargs, warn, new InferenceCheckHandler(inferenceContext)); duke@1: mcimadamore@1337: // minimize as yet undetermined type variables mcimadamore@1337: for (Type t : inferenceContext.undetvars) { mcimadamore@1337: minimizeInst((UndetVar)t, warn); mcimadamore@1337: } duke@1: mcimadamore@1337: checkWithinBounds(inferenceContext, warn); duke@1: mcimadamore@1337: mt = (MethodType)inferenceContext.asInstType(mt, types); mcimadamore@396: mcimadamore@1337: List restvars = inferenceContext.restvars(); duke@1: mcimadamore@1337: if (!restvars.isEmpty()) { mcimadamore@1337: if (resultInfo != null) { mcimadamore@1337: instantiateUninferred(env.tree.pos(), inferenceContext, mt, resultInfo, warn); mcimadamore@1337: checkWithinBounds(inferenceContext, warn); mcimadamore@1337: mt = (MethodType)inferenceContext.asInstType(mt, types); mcimadamore@1337: if (rs.verboseResolutionMode.contains(VerboseResolutionMode.DEFERRED_INST)) { mcimadamore@1337: log.note(env.tree.pos, "deferred.method.inst", msym, mt, resultInfo.pt); mcimadamore@1337: } mcimadamore@1337: } mcimadamore@1337: } duke@1: mcimadamore@1337: // return instantiated version of method type mcimadamore@1337: return mt; mcimadamore@1337: } finally { mcimadamore@1337: inferenceContext.notifyChange(types); duke@1: } duke@1: } duke@1: //where duke@1: mcimadamore@1186: /** inference check handler **/ mcimadamore@1186: class InferenceCheckHandler implements Resolve.MethodCheckHandler { mcimadamore@1186: mcimadamore@1337: InferenceContext inferenceContext; mcimadamore@1186: mcimadamore@1337: public InferenceCheckHandler(InferenceContext inferenceContext) { mcimadamore@1337: this.inferenceContext = inferenceContext; mcimadamore@1186: } mcimadamore@1186: mcimadamore@1186: public InapplicableMethodException arityMismatch() { mcimadamore@1337: return inferenceException.setMessage("infer.arg.length.mismatch", inferenceContext.inferenceVars()); mcimadamore@1186: } mcimadamore@1296: public InapplicableMethodException argumentMismatch(boolean varargs, JCDiagnostic details) { mcimadamore@1186: String key = varargs ? mcimadamore@1296: "infer.varargs.argument.mismatch" : mcimadamore@1296: "infer.no.conforming.assignment.exists"; mcimadamore@1298: return inferenceException.setMessage(key, mcimadamore@1337: inferenceContext.inferenceVars(), details); mcimadamore@1186: } mcimadamore@1186: public InapplicableMethodException inaccessibleVarargs(Symbol location, Type expected) { mcimadamore@1298: return inferenceException.setMessage("inaccessible.varargs.type", mcimadamore@1186: expected, Kinds.kindName(location), location); mcimadamore@1186: } mcimadamore@1186: } mcimadamore@1186: mcimadamore@1251: /** check that type parameters are within their bounds. mcimadamore@895: */ mcimadamore@1337: void checkWithinBounds(InferenceContext inferenceContext, mcimadamore@1251: Warner warn) mcimadamore@1298: throws InferenceException { mcimadamore@1337: List tvars = inferenceContext.inferenceVars(); mcimadamore@1337: for (Type t : inferenceContext.undetvars) { mcimadamore@1251: UndetVar uv = (UndetVar)t; mcimadamore@1337: uv.hibounds = inferenceContext.asInstTypes(uv.hibounds, types); mcimadamore@1337: uv.lobounds = inferenceContext.asInstTypes(uv.lobounds, types); mcimadamore@1337: uv.eq = inferenceContext.asInstTypes(uv.eq, types); mcimadamore@1337: checkCompatibleUpperBounds(uv, inferenceContext.inferenceVars()); mcimadamore@1337: if (!inferenceContext.restvars().contains(tvars.head)) { mcimadamore@1337: Type inst = inferenceContext.asInstType(t, types); mcimadamore@1251: for (Type u : uv.hibounds) { mcimadamore@1337: if (!types.isSubtypeUnchecked(inst, inferenceContext.asFree(u, types), warn)) { mcimadamore@1251: reportBoundError(uv, BoundErrorKind.UPPER); mcimadamore@1251: } mcimadamore@1251: } mcimadamore@1251: for (Type l : uv.lobounds) { mcimadamore@1337: if (!types.isSubtypeUnchecked(inferenceContext.asFree(l, types), inst, warn)) { mcimadamore@1251: reportBoundError(uv, BoundErrorKind.LOWER); mcimadamore@1251: } mcimadamore@1251: } mcimadamore@1251: for (Type e : uv.eq) { mcimadamore@1337: if (!types.isSameType(inst, inferenceContext.asFree(e, types))) { mcimadamore@1251: reportBoundError(uv, BoundErrorKind.EQ); mcimadamore@1251: } mcimadamore@1251: } mcimadamore@1251: } mcimadamore@1337: tvars = tvars.tail; duke@1: } mcimadamore@895: } duke@1: mcimadamore@1251: void checkCompatibleUpperBounds(UndetVar uv, List tvars) { mcimadamore@1251: // VGJ: sort of inlined maximizeInst() below. Adding mcimadamore@1251: // bounds can cause lobounds that are above hibounds. mcimadamore@1251: ListBuffer hiboundsNoVars = ListBuffer.lb(); mcimadamore@1337: for (Type t : Type.filter(uv.hibounds, boundFilter)) { mcimadamore@1251: if (!t.containsAny(tvars)) { mcimadamore@1251: hiboundsNoVars.append(t); mcimadamore@1251: } duke@1: } mcimadamore@1251: List hibounds = hiboundsNoVars.toList(); mcimadamore@1251: Type hb = null; mcimadamore@1251: if (hibounds.isEmpty()) mcimadamore@1251: hb = syms.objectType; mcimadamore@1251: else if (hibounds.tail.isEmpty()) mcimadamore@1251: hb = hibounds.head; mcimadamore@1251: else mcimadamore@1251: hb = types.glb(hibounds); mcimadamore@1251: if (hb == null || hb.isErroneous()) mcimadamore@1251: reportBoundError(uv, BoundErrorKind.BAD_UPPER); mcimadamore@1251: } mcimadamore@1251: mcimadamore@1251: enum BoundErrorKind { mcimadamore@1251: BAD_UPPER() { mcimadamore@1251: @Override mcimadamore@1251: InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) { mcimadamore@1251: return ex.setMessage("incompatible.upper.bounds", uv.qtype, uv.hibounds); mcimadamore@1251: } mcimadamore@1251: }, mcimadamore@1251: UPPER() { mcimadamore@1251: @Override mcimadamore@1251: InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) { mcimadamore@1251: return ex.setMessage("inferred.do.not.conform.to.upper.bounds", uv.inst, uv.hibounds); mcimadamore@1251: } mcimadamore@1251: }, mcimadamore@1251: LOWER() { mcimadamore@1251: @Override mcimadamore@1251: InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) { mcimadamore@1251: return ex.setMessage("inferred.do.not.conform.to.lower.bounds", uv.inst, uv.lobounds); mcimadamore@1251: } mcimadamore@1251: }, mcimadamore@1251: EQ() { mcimadamore@1251: @Override mcimadamore@1251: InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) { mcimadamore@1251: return ex.setMessage("inferred.do.not.conform.to.eq.bounds", uv.inst, uv.eq); mcimadamore@1251: } mcimadamore@1251: }; mcimadamore@1251: mcimadamore@1251: abstract InapplicableMethodException setMessage(InferenceException ex, UndetVar uv); mcimadamore@1251: } mcimadamore@1251: //where mcimadamore@1251: void reportBoundError(UndetVar uv, BoundErrorKind bk) { mcimadamore@1298: throw bk.setMessage(inferenceException, uv); duke@1: } mcimadamore@674: mcimadamore@674: /** mcimadamore@674: * Compute a synthetic method type corresponding to the requested polymorphic mcimadamore@820: * method signature. The target return type is computed from the immediately mcimadamore@820: * enclosing scope surrounding the polymorphic-signature call. mcimadamore@674: */ mcimadamore@1239: Type instantiatePolymorphicSignatureInstance(Env env, mcimadamore@674: MethodSymbol spMethod, // sig. poly. method or null if none mcimadamore@820: List argtypes) { mcimadamore@674: final Type restype; mcimadamore@716: mcimadamore@820: //The return type for a polymorphic signature call is computed from mcimadamore@820: //the enclosing tree E, as follows: if E is a cast, then use the mcimadamore@820: //target type of the cast expression as a return type; if E is an mcimadamore@820: //expression statement, the return type is 'void' - otherwise the mcimadamore@820: //return type is simply 'Object'. A correctness check ensures that mcimadamore@820: //env.next refers to the lexically enclosing environment in which mcimadamore@820: //the polymorphic signature call environment is nested. mcimadamore@820: mcimadamore@820: switch (env.next.tree.getTag()) { jjg@1127: case TYPECAST: mcimadamore@820: JCTypeCast castTree = (JCTypeCast)env.next.tree; mcimadamore@820: restype = (TreeInfo.skipParens(castTree.expr) == env.tree) ? mcimadamore@820: castTree.clazz.type : mcimadamore@820: syms.objectType; mcimadamore@820: break; jjg@1127: case EXEC: mcimadamore@820: JCTree.JCExpressionStatement execTree = mcimadamore@820: (JCTree.JCExpressionStatement)env.next.tree; mcimadamore@820: restype = (TreeInfo.skipParens(execTree.expr) == env.tree) ? mcimadamore@820: syms.voidType : mcimadamore@820: syms.objectType; mcimadamore@820: break; mcimadamore@820: default: mcimadamore@820: restype = syms.objectType; mcimadamore@674: } mcimadamore@674: mcimadamore@674: List paramtypes = Type.map(argtypes, implicitArgType); mcimadamore@674: List exType = spMethod != null ? mcimadamore@674: spMethod.getThrownTypes() : mcimadamore@674: List.of(syms.throwableType); // make it throw all exceptions mcimadamore@674: mcimadamore@674: MethodType mtype = new MethodType(paramtypes, mcimadamore@674: restype, mcimadamore@674: exType, mcimadamore@674: syms.methodClass); mcimadamore@674: return mtype; mcimadamore@674: } mcimadamore@674: //where mcimadamore@674: Mapping implicitArgType = new Mapping ("implicitArgType") { mcimadamore@674: public Type apply(Type t) { mcimadamore@674: t = types.erasure(t); mcimadamore@674: if (t.tag == BOT) mcimadamore@674: // nulls type as the marker type Null (which has no instances) mcimadamore@674: // infer as java.lang.Void for now mcimadamore@674: t = types.boxedClass(syms.voidType).type; mcimadamore@674: return t; mcimadamore@674: } mcimadamore@674: }; mcimadamore@1337: mcimadamore@1337: /** mcimadamore@1337: * Mapping that turns inference variables into undet vars mcimadamore@1337: * (used by inference context) mcimadamore@1337: */ mcimadamore@1337: static Mapping fromTypeVarFun = new Mapping("fromTypeVarFun") { mcimadamore@1337: public Type apply(Type t) { mcimadamore@1337: if (t.tag == TYPEVAR) return new UndetVar(t); mcimadamore@1337: else return t.map(this); mcimadamore@1337: } mcimadamore@1337: }; mcimadamore@1337: mcimadamore@1337: /** mcimadamore@1337: * An inference context keeps track of the set of variables that are free mcimadamore@1337: * in the current context. It provides utility methods for opening/closing mcimadamore@1337: * types to their corresponding free/closed forms. It also provide hooks for mcimadamore@1337: * attaching deferred post-inference action (see PendingCheck). Finally, mcimadamore@1337: * it can be used as an entry point for performing upper/lower bound inference mcimadamore@1337: * (see InferenceKind). mcimadamore@1337: */ mcimadamore@1337: static class InferenceContext { mcimadamore@1337: mcimadamore@1337: /** mcimadamore@1337: * Single-method-interface for defining inference callbacks. Certain actions mcimadamore@1337: * (i.e. subtyping checks) might need to be redone after all inference variables mcimadamore@1337: * have been fixed. mcimadamore@1337: */ mcimadamore@1337: interface FreeTypeListener { mcimadamore@1337: void typesInferred(InferenceContext inferenceContext); mcimadamore@1337: } mcimadamore@1337: mcimadamore@1337: /** list of inference vars as undet vars */ mcimadamore@1337: List undetvars; mcimadamore@1337: mcimadamore@1337: /** list of inference vars in this context */ mcimadamore@1337: List inferencevars; mcimadamore@1337: mcimadamore@1337: java.util.Map> freeTypeListeners = mcimadamore@1337: new java.util.HashMap>(); mcimadamore@1337: mcimadamore@1337: List freetypeListeners = List.nil(); mcimadamore@1337: mcimadamore@1337: public InferenceContext(List inferencevars, Types types) { mcimadamore@1337: this.undetvars = Type.map(inferencevars, fromTypeVarFun); mcimadamore@1337: this.inferencevars = inferencevars; mcimadamore@1337: for (Type t : this.undetvars) { mcimadamore@1337: UndetVar uv = (UndetVar)t; mcimadamore@1337: uv.hibounds = types.getBounds((TypeVar)uv.qtype); mcimadamore@1337: } mcimadamore@1337: } mcimadamore@1337: mcimadamore@1337: /** mcimadamore@1337: * returns the list of free variables (as type-variables) in this mcimadamore@1337: * inference context mcimadamore@1337: */ mcimadamore@1337: List inferenceVars() { mcimadamore@1337: return inferencevars; mcimadamore@1337: } mcimadamore@1337: mcimadamore@1337: /** mcimadamore@1337: * returns the list of uninstantiated variables (as type-variables) in this mcimadamore@1337: * inference context (usually called after instantiate()) mcimadamore@1337: */ mcimadamore@1337: List restvars() { mcimadamore@1337: List undetvars = this.undetvars; mcimadamore@1337: ListBuffer restvars = ListBuffer.lb(); mcimadamore@1337: for (Type t : instTypes()) { mcimadamore@1337: UndetVar uv = (UndetVar)undetvars.head; mcimadamore@1337: if (uv.qtype == t) { mcimadamore@1337: restvars.append(t); mcimadamore@1337: } mcimadamore@1337: undetvars = undetvars.tail; mcimadamore@1337: } mcimadamore@1337: return restvars.toList(); mcimadamore@1337: } mcimadamore@1337: mcimadamore@1337: /** mcimadamore@1337: * is this type free? mcimadamore@1337: */ mcimadamore@1337: final boolean free(Type t) { mcimadamore@1337: return t.containsAny(inferencevars); mcimadamore@1337: } mcimadamore@1337: mcimadamore@1337: final boolean free(List ts) { mcimadamore@1337: for (Type t : ts) { mcimadamore@1337: if (free(t)) return true; mcimadamore@1337: } mcimadamore@1337: return false; mcimadamore@1337: } mcimadamore@1337: mcimadamore@1337: /** mcimadamore@1337: * Returns a list of free variables in a given type mcimadamore@1337: */ mcimadamore@1337: final List freeVarsIn(Type t) { mcimadamore@1337: ListBuffer buf = ListBuffer.lb(); mcimadamore@1337: for (Type iv : inferenceVars()) { mcimadamore@1337: if (t.contains(iv)) { mcimadamore@1337: buf.add(iv); mcimadamore@1337: } mcimadamore@1337: } mcimadamore@1337: return buf.toList(); mcimadamore@1337: } mcimadamore@1337: mcimadamore@1337: final List freeVarsIn(List ts) { mcimadamore@1337: ListBuffer buf = ListBuffer.lb(); mcimadamore@1337: for (Type t : ts) { mcimadamore@1337: buf.appendList(freeVarsIn(t)); mcimadamore@1337: } mcimadamore@1337: ListBuffer buf2 = ListBuffer.lb(); mcimadamore@1337: for (Type t : buf) { mcimadamore@1337: if (!buf2.contains(t)) { mcimadamore@1337: buf2.add(t); mcimadamore@1337: } mcimadamore@1337: } mcimadamore@1337: return buf2.toList(); mcimadamore@1337: } mcimadamore@1337: mcimadamore@1337: /** mcimadamore@1337: * Replace all free variables in a given type with corresponding mcimadamore@1337: * undet vars (used ahead of subtyping/compatibility checks to allow propagation mcimadamore@1337: * of inference constraints). mcimadamore@1337: */ mcimadamore@1337: final Type asFree(Type t, Types types) { mcimadamore@1337: return types.subst(t, inferencevars, undetvars); mcimadamore@1337: } mcimadamore@1337: mcimadamore@1337: final List asFree(List ts, Types types) { mcimadamore@1337: ListBuffer buf = ListBuffer.lb(); mcimadamore@1337: for (Type t : ts) { mcimadamore@1337: buf.append(asFree(t, types)); mcimadamore@1337: } mcimadamore@1337: return buf.toList(); mcimadamore@1337: } mcimadamore@1337: mcimadamore@1337: List instTypes() { mcimadamore@1337: ListBuffer buf = ListBuffer.lb(); mcimadamore@1337: for (Type t : undetvars) { mcimadamore@1337: UndetVar uv = (UndetVar)t; mcimadamore@1337: buf.append(uv.inst != null ? uv.inst : uv.qtype); mcimadamore@1337: } mcimadamore@1337: return buf.toList(); mcimadamore@1337: } mcimadamore@1337: mcimadamore@1337: /** mcimadamore@1337: * Replace all free variables in a given type with corresponding mcimadamore@1337: * instantiated types - if one or more free variable has not been mcimadamore@1337: * fully instantiated, it will still be available in the resulting type. mcimadamore@1337: */ mcimadamore@1337: Type asInstType(Type t, Types types) { mcimadamore@1337: return types.subst(t, inferencevars, instTypes()); mcimadamore@1337: } mcimadamore@1337: mcimadamore@1337: List asInstTypes(List ts, Types types) { mcimadamore@1337: ListBuffer buf = ListBuffer.lb(); mcimadamore@1337: for (Type t : ts) { mcimadamore@1337: buf.append(asInstType(t, types)); mcimadamore@1337: } mcimadamore@1337: return buf.toList(); mcimadamore@1337: } mcimadamore@1337: mcimadamore@1337: /** mcimadamore@1337: * Add custom hook for performing post-inference action mcimadamore@1337: */ mcimadamore@1337: void addFreeTypeListener(List types, FreeTypeListener ftl) { mcimadamore@1337: freeTypeListeners.put(ftl, freeVarsIn(types)); mcimadamore@1337: } mcimadamore@1337: mcimadamore@1337: /** mcimadamore@1337: * Mark the inference context as complete and trigger evaluation mcimadamore@1337: * of all deferred checks. mcimadamore@1337: */ mcimadamore@1337: void notifyChange(Types types) { mcimadamore@1337: InferenceException thrownEx = null; mcimadamore@1337: for (Map.Entry> entry : mcimadamore@1337: new HashMap>(freeTypeListeners).entrySet()) { mcimadamore@1337: if (!Type.containsAny(entry.getValue(), restvars())) { mcimadamore@1337: try { mcimadamore@1337: entry.getKey().typesInferred(this); mcimadamore@1337: freeTypeListeners.remove(entry.getKey()); mcimadamore@1337: } catch (InferenceException ex) { mcimadamore@1337: if (thrownEx == null) { mcimadamore@1337: thrownEx = ex; mcimadamore@1337: } mcimadamore@1337: } mcimadamore@1337: } mcimadamore@1337: } mcimadamore@1337: //inference exception multiplexing - present any inference exception mcimadamore@1337: //thrown when processing listeners as a single one mcimadamore@1337: if (thrownEx != null) { mcimadamore@1337: throw thrownEx; mcimadamore@1337: } mcimadamore@1337: } mcimadamore@895: } mcimadamore@1337: mcimadamore@1337: final InferenceContext emptyContext = new InferenceContext(List.nil(), types); mcimadamore@1337: }