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@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;
duke@1: import com.sun.tools.javac.code.*;
duke@1: import com.sun.tools.javac.code.Type.*;
mcimadamore@396: import com.sun.tools.javac.code.Type.ForAll.ConstraintKind;
mcimadamore@299: import com.sun.tools.javac.code.Symbol.*;
mcimadamore@1186: import com.sun.tools.javac.comp.Resolve.InapplicableMethodException;
mcimadamore@1114: import com.sun.tools.javac.comp.Resolve.VerboseResolutionMode;
mcimadamore@1114: import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
duke@1:
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@89: ambiguousNoInstanceException =
mcimadamore@89: new NoInstanceException(true, diags);
mcimadamore@89: unambiguousNoInstanceException =
mcimadamore@89: new NoInstanceException(false, diags);
mcimadamore@299: invalidInstanceException =
mcimadamore@299: new InvalidInstanceException(diags);
mcimadamore@299:
duke@1: }
duke@1:
mcimadamore@1186: public static class InferenceException extends InapplicableMethodException {
duke@1: private static final long serialVersionUID = 0;
duke@1:
mcimadamore@299: InferenceException(JCDiagnostic.Factory diags) {
mcimadamore@689: super(diags);
duke@1: }
mcimadamore@299: }
mcimadamore@299:
mcimadamore@299: public static class NoInstanceException extends InferenceException {
mcimadamore@299: private static final long serialVersionUID = 1;
mcimadamore@299:
mcimadamore@299: boolean isAmbiguous; // exist several incomparable best instances?
mcimadamore@299:
mcimadamore@299: NoInstanceException(boolean isAmbiguous, JCDiagnostic.Factory diags) {
mcimadamore@299: super(diags);
mcimadamore@299: this.isAmbiguous = isAmbiguous;
duke@1: }
duke@1: }
mcimadamore@299:
mcimadamore@299: public static class InvalidInstanceException extends InferenceException {
mcimadamore@299: private static final long serialVersionUID = 2;
mcimadamore@299:
mcimadamore@299: InvalidInstanceException(JCDiagnostic.Factory diags) {
mcimadamore@299: super(diags);
mcimadamore@299: }
mcimadamore@299: }
mcimadamore@299:
mcimadamore@89: private final NoInstanceException ambiguousNoInstanceException;
mcimadamore@89: private final NoInstanceException unambiguousNoInstanceException;
mcimadamore@299: private final InvalidInstanceException invalidInstanceException;
duke@1:
duke@1: /***************************************************************************
duke@1: * Auxiliary type values and classes
duke@1: ***************************************************************************/
duke@1:
duke@1: /** A mapping that turns type variables into undetermined type variables.
duke@1: */
duke@1: Mapping fromTypeVarFun = new Mapping("fromTypeVarFun") {
duke@1: public Type apply(Type t) {
duke@1: if (t.tag == TYPEVAR) return new UndetVar(t);
duke@1: else return t.map(this);
duke@1: }
duke@1: };
duke@1:
duke@1: /** A mapping that returns its type argument with every UndetVar replaced
duke@1: * by its `inst' field. Throws a NoInstanceException
duke@1: * if this not possible because an `inst' field is null.
mcimadamore@635: * Note: mutually referring undertvars will be left uninstantiated
mcimadamore@635: * (that is, they will be replaced by the underlying type-variable).
duke@1: */
mcimadamore@635:
duke@1: Mapping getInstFun = new Mapping("getInstFun") {
duke@1: public Type apply(Type t) {
duke@1: switch (t.tag) {
mcimadamore@635: case UNKNOWN:
duke@1: throw ambiguousNoInstanceException
mcimadamore@635: .setMessage("undetermined.type");
mcimadamore@635: case UNDETVAR:
mcimadamore@635: UndetVar that = (UndetVar) t;
mcimadamore@635: if (that.inst == null)
mcimadamore@635: throw ambiguousNoInstanceException
mcimadamore@635: .setMessage("type.variable.has.undetermined.type",
mcimadamore@635: that.qtype);
mcimadamore@635: return isConstraintCyclic(that) ?
mcimadamore@635: that.qtype :
mcimadamore@635: apply(that.inst);
mcimadamore@635: default:
mcimadamore@635: return t.map(this);
duke@1: }
duke@1: }
mcimadamore@635:
mcimadamore@635: private boolean isConstraintCyclic(UndetVar uv) {
mcimadamore@635: Types.UnaryVisitor constraintScanner =
mcimadamore@635: new Types.UnaryVisitor() {
mcimadamore@635:
mcimadamore@635: List seen = List.nil();
mcimadamore@635:
mcimadamore@635: Boolean visit(List ts) {
mcimadamore@635: for (Type t : ts) {
mcimadamore@635: if (visit(t)) return true;
mcimadamore@635: }
mcimadamore@635: return false;
mcimadamore@635: }
mcimadamore@635:
mcimadamore@635: public Boolean visitType(Type t, Void ignored) {
mcimadamore@635: return false;
mcimadamore@635: }
mcimadamore@635:
mcimadamore@635: @Override
mcimadamore@635: public Boolean visitClassType(ClassType t, Void ignored) {
mcimadamore@635: if (t.isCompound()) {
mcimadamore@635: return visit(types.supertype(t)) ||
mcimadamore@635: visit(types.interfaces(t));
mcimadamore@635: } else {
mcimadamore@635: return visit(t.getTypeArguments());
mcimadamore@635: }
mcimadamore@635: }
mcimadamore@635: @Override
mcimadamore@635: public Boolean visitWildcardType(WildcardType t, Void ignored) {
mcimadamore@635: return visit(t.type);
mcimadamore@635: }
mcimadamore@635:
mcimadamore@635: @Override
mcimadamore@635: public Boolean visitUndetVar(UndetVar t, Void ignored) {
mcimadamore@635: if (seen.contains(t)) {
mcimadamore@635: return true;
mcimadamore@635: } else {
mcimadamore@635: seen = seen.prepend(t);
mcimadamore@635: return visit(t.inst);
mcimadamore@635: }
mcimadamore@635: }
mcimadamore@635: };
mcimadamore@635: return constraintScanner.visit(uv);
mcimadamore@635: }
duke@1: };
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: */
duke@1: void maximizeInst(UndetVar that, Warner warn) throws NoInstanceException {
mcimadamore@828: List hibounds = Type.filter(that.hibounds, errorFilter);
duke@1: if (that.inst == null) {
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);
duke@1: }
mcimadamore@210: if (that.inst == null ||
mcimadamore@298: that.inst.isErroneous())
mcimadamore@210: throw ambiguousNoInstanceException
mcimadamore@210: .setMessage("no.unique.maximal.instance.exists",
mcimadamore@828: that.qtype, hibounds);
duke@1: }
duke@1: //where
duke@1: private boolean isSubClass(Type t, final List ts) {
duke@1: t = t.baseType();
duke@1: if (t.tag == TYPEVAR) {
duke@1: List bounds = types.getBounds((TypeVar)t);
duke@1: for (Type s : ts) {
duke@1: if (!types.isSameType(t, s.baseType())) {
duke@1: for (Type bound : bounds) {
duke@1: if (!isSubClass(bound, List.of(s.baseType())))
duke@1: return false;
duke@1: }
duke@1: }
duke@1: }
duke@1: } else {
duke@1: for (Type s : ts) {
duke@1: if (!t.tsym.isSubClass(s.baseType().tsym, types))
duke@1: return false;
duke@1: }
duke@1: }
duke@1: return true;
duke@1: }
duke@1:
mcimadamore@828: private Filter errorFilter = new Filter() {
mcimadamore@828: @Override
mcimadamore@828: public boolean accepts(Type t) {
mcimadamore@828: return !t.isErroneous();
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: */
duke@1: void minimizeInst(UndetVar that, Warner warn) throws NoInstanceException {
mcimadamore@828: List lobounds = Type.filter(that.lobounds, errorFilter);
duke@1: if (that.inst == null) {
mcimadamore@828: if (lobounds.isEmpty())
duke@1: that.inst = syms.botType;
mcimadamore@828: 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)
duke@1: throw ambiguousNoInstanceException
duke@1: .setMessage("no.unique.minimal.instance.exists",
mcimadamore@828: that.qtype, lobounds);
duke@1: // VGJ: sort of inlined maximizeInst() below. Adding
duke@1: // bounds can cause lobounds that are above hibounds.
mcimadamore@828: List hibounds = Type.filter(that.hibounds, errorFilter);
mcimadamore@1087: Type hb = null;
mcimadamore@828: if (hibounds.isEmpty())
mcimadamore@1087: hb = syms.objectType;
mcimadamore@1087: else if (hibounds.tail.isEmpty())
mcimadamore@828: hb = hibounds.head;
mcimadamore@1087: else
mcimadamore@1087: hb = types.glb(hibounds);
duke@1: if (hb == null ||
mcimadamore@1087: hb.isErroneous())
mcimadamore@1087: throw ambiguousNoInstanceException
mcimadamore@1087: .setMessage("incompatible.upper.bounds",
mcimadamore@1087: that.qtype, hibounds);
duke@1: }
duke@1: }
duke@1:
mcimadamore@1186: Type asUndetType(Type t, List undetvars) {
mcimadamore@1186: return types.subst(t, inferenceVars(undetvars), undetvars);
mcimadamore@1186: }
mcimadamore@1186:
mcimadamore@1186: List inferenceVars(List undetvars) {
mcimadamore@1186: ListBuffer tvars = ListBuffer.lb();
mcimadamore@1186: for (Type uv : undetvars) {
mcimadamore@1186: tvars.append(((UndetVar)uv).qtype);
mcimadamore@1186: }
mcimadamore@1186: return tvars.toList();
mcimadamore@1186: }
mcimadamore@1186:
duke@1: /***************************************************************************
duke@1: * Exported Methods
duke@1: ***************************************************************************/
duke@1:
duke@1: /** Try to instantiate expression type `that' to given type `to'.
duke@1: * If a maximal instantiation exists which makes this type
duke@1: * a subtype of type `to', return the instantiated type.
duke@1: * If no instantiation exists, or if several incomparable
duke@1: * best instantiations exist throw a NoInstanceException.
duke@1: */
duke@1: public Type instantiateExpr(ForAll that,
duke@1: Type to,
mcimadamore@299: Warner warn) throws InferenceException {
duke@1: List undetvars = Type.map(that.tvars, fromTypeVarFun);
duke@1: for (List l = undetvars; l.nonEmpty(); l = l.tail) {
mcimadamore@396: UndetVar uv = (UndetVar) l.head;
mcimadamore@396: TypeVar tv = (TypeVar)uv.qtype;
duke@1: ListBuffer hibounds = new ListBuffer();
mcimadamore@615: for (Type t : that.getConstraints(tv, ConstraintKind.EXTENDS)) {
mcimadamore@635: hibounds.append(types.subst(t, that.tvars, undetvars));
duke@1: }
mcimadamore@635:
mcimadamore@396: List inst = that.getConstraints(tv, ConstraintKind.EQUAL);
mcimadamore@396: if (inst.nonEmpty() && inst.head.tag != BOT) {
mcimadamore@396: uv.inst = inst.head;
mcimadamore@396: }
mcimadamore@396: uv.hibounds = hibounds.toList();
duke@1: }
duke@1: Type qtype1 = types.subst(that.qtype, that.tvars, undetvars);
mcimadamore@753: if (!types.isSubtype(qtype1,
mcimadamore@753: qtype1.tag == UNDETVAR ? types.boxedTypeOrType(to) : to)) {
duke@1: throw unambiguousNoInstanceException
mcimadamore@689: .setMessage("infer.no.conforming.instance.exists",
duke@1: that.tvars, that.qtype, to);
duke@1: }
duke@1: for (List l = undetvars; l.nonEmpty(); l = l.tail)
duke@1: maximizeInst((UndetVar) l.head, warn);
duke@1: // System.out.println(" = " + qtype1.map(getInstFun));//DEBUG
duke@1:
duke@1: // check bounds
duke@1: List targs = Type.map(undetvars, getInstFun);
mcimadamore@635: if (Type.containsAny(targs, that.tvars)) {
mcimadamore@635: //replace uninferred type-vars
mcimadamore@635: targs = types.subst(targs,
mcimadamore@635: that.tvars,
mcimadamore@1178: instantiateAsUninferredVars(undetvars, that.tvars));
mcimadamore@635: }
mcimadamore@396: return chk.checkType(warn.pos(), that.inst(targs, types), to);
duke@1: }
mcimadamore@635: //where
mcimadamore@1178: private List instantiateAsUninferredVars(List undetvars, List tvars) {
mcimadamore@1178: Assert.check(undetvars.length() == tvars.length());
mcimadamore@635: ListBuffer new_targs = ListBuffer.lb();
mcimadamore@1178: //step 1 - create synthetic captured vars
mcimadamore@635: for (Type t : undetvars) {
mcimadamore@635: UndetVar uv = (UndetVar)t;
mcimadamore@635: Type newArg = new CapturedType(t.tsym.name, t.tsym, uv.inst, syms.botType, null);
mcimadamore@635: new_targs = new_targs.append(newArg);
mcimadamore@635: }
mcimadamore@635: //step 2 - replace synthetic vars in their bounds
mcimadamore@1178: List formals = tvars;
mcimadamore@635: for (Type t : new_targs.toList()) {
mcimadamore@635: CapturedType ct = (CapturedType)t;
mcimadamore@635: ct.bound = types.subst(ct.bound, tvars, new_targs.toList());
mcimadamore@1178: WildcardType wt = new WildcardType(syms.objectType, BoundKind.UNBOUND, syms.boundClass);
mcimadamore@1178: wt.bound = (TypeVar)formals.head;
mcimadamore@635: ct.wildcard = wt;
mcimadamore@1178: formals = formals.tail;
mcimadamore@635: }
mcimadamore@635: return new_targs.toList();
mcimadamore@635: }
duke@1:
duke@1: /** Instantiate method type `mt' by finding instantiations of
duke@1: * `tvars' so that method can be applied to `argtypes'.
duke@1: */
mcimadamore@547: public Type instantiateMethod(final Env env,
mcimadamore@547: List tvars,
duke@1: MethodType mt,
mcimadamore@580: final Symbol msym,
mcimadamore@299: final List argtypes,
mcimadamore@299: final boolean allowBoxing,
mcimadamore@299: final boolean useVarargs,
mcimadamore@299: final Warner warn) throws InferenceException {
duke@1: //-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG
duke@1: List undetvars = Type.map(tvars, fromTypeVarFun);
mcimadamore@1186: //final List capturedArgs = types.capture(argtypes);
mcimadamore@689:
mcimadamore@1186: final List capturedArgs =
mcimadamore@1186: rs.checkRawArgumentsAcceptable(env, undetvars, argtypes, mt.getParameterTypes(),
mcimadamore@1186: allowBoxing, useVarargs, warn, new InferenceCheckHandler(undetvars));
duke@1:
duke@1: // minimize as yet undetermined type variables
duke@1: for (Type t : undetvars)
duke@1: minimizeInst((UndetVar) t, warn);
duke@1:
duke@1: /** Type variables instantiated to bottom */
duke@1: ListBuffer restvars = new ListBuffer();
duke@1:
mcimadamore@396: /** Undet vars instantiated to bottom */
mcimadamore@396: final ListBuffer restundet = new ListBuffer();
mcimadamore@396:
duke@1: /** Instantiated types or TypeVars if under-constrained */
duke@1: ListBuffer insttypes = new ListBuffer();
duke@1:
duke@1: /** Instantiated types or UndetVars if under-constrained */
duke@1: ListBuffer undettypes = new ListBuffer();
duke@1:
duke@1: for (Type t : undetvars) {
duke@1: UndetVar uv = (UndetVar)t;
duke@1: if (uv.inst.tag == BOT) {
duke@1: restvars.append(uv.qtype);
mcimadamore@396: restundet.append(uv);
duke@1: insttypes.append(uv.qtype);
duke@1: undettypes.append(uv);
duke@1: uv.inst = null;
duke@1: } else {
duke@1: insttypes.append(uv.inst);
duke@1: undettypes.append(uv.inst);
duke@1: }
duke@1: }
duke@1: checkWithinBounds(tvars, undettypes.toList(), warn);
duke@1:
mcimadamore@299: mt = (MethodType)types.subst(mt, tvars, insttypes.toList());
mcimadamore@299:
duke@1: if (!restvars.isEmpty()) {
duke@1: // if there are uninstantiated variables,
duke@1: // quantify result type with them
mcimadamore@299: final List inferredTypes = insttypes.toList();
mcimadamore@299: final List all_tvars = tvars; //this is the wrong tvars
mcimadamore@1114: return new UninferredMethodType(env.tree.pos(), msym, mt, restvars.toList()) {
mcimadamore@299: @Override
mcimadamore@895: List getConstraints(TypeVar tv, ConstraintKind ck) {
mcimadamore@396: for (Type t : restundet.toList()) {
mcimadamore@396: UndetVar uv = (UndetVar)t;
mcimadamore@396: if (uv.qtype == tv) {
mcimadamore@396: switch (ck) {
mcimadamore@615: case EXTENDS: return uv.hibounds.appendList(types.subst(types.getBounds(tv), all_tvars, inferredTypes));
mcimadamore@396: case SUPER: return uv.lobounds;
mcimadamore@396: case EQUAL: return uv.inst != null ? List.of(uv.inst) : List.nil();
mcimadamore@396: }
mcimadamore@396: }
mcimadamore@396: }
mcimadamore@396: return List.nil();
mcimadamore@396: }
mcimadamore@396: @Override
mcimadamore@895: void check(List inferred, Types types) throws NoInstanceException {
mcimadamore@845: // check that actuals conform to inferred formals
mcimadamore@895: checkArgumentsAcceptable(env, capturedArgs, getParameterTypes(), allowBoxing, useVarargs, warn);
mcimadamore@396: // check that inferred bounds conform to their bounds
mcimadamore@396: checkWithinBounds(all_tvars,
mcimadamore@299: types.subst(inferredTypes, tvars, inferred), warn);
mcimadamore@547: if (useVarargs) {
mcimadamore@895: chk.checkVararg(env.tree.pos(), getParameterTypes(), msym);
mcimadamore@547: }
mcimadamore@299: }};
duke@1: }
mcimadamore@299: else {
mcimadamore@845: // check that actuals conform to inferred formals
mcimadamore@845: checkArgumentsAcceptable(env, capturedArgs, mt.getParameterTypes(), allowBoxing, useVarargs, warn);
mcimadamore@299: // return instantiated version of method type
mcimadamore@299: return mt;
mcimadamore@299: }
duke@1: }
duke@1: //where
duke@1:
mcimadamore@1186: /** inference check handler **/
mcimadamore@1186: class InferenceCheckHandler implements Resolve.MethodCheckHandler {
mcimadamore@1186:
mcimadamore@1186: List undetvars;
mcimadamore@1186:
mcimadamore@1186: public InferenceCheckHandler(List undetvars) {
mcimadamore@1186: this.undetvars = undetvars;
mcimadamore@1186: }
mcimadamore@1186:
mcimadamore@1186: public InapplicableMethodException arityMismatch() {
mcimadamore@1186: return unambiguousNoInstanceException.setMessage("infer.arg.length.mismatch");
mcimadamore@1186: }
mcimadamore@1186: public InapplicableMethodException argumentMismatch(boolean varargs, Type found, Type expected) {
mcimadamore@1186: String key = varargs ?
mcimadamore@1186: "infer.varargs.argument.mismatch" :
mcimadamore@1186: "infer.no.conforming.assignment.exists";
mcimadamore@1186: return unambiguousNoInstanceException.setMessage(key,
mcimadamore@1186: inferenceVars(undetvars), found, expected);
mcimadamore@1186: }
mcimadamore@1186: public InapplicableMethodException inaccessibleVarargs(Symbol location, Type expected) {
mcimadamore@1186: return unambiguousNoInstanceException.setMessage("inaccessible.varargs.type",
mcimadamore@1186: expected, Kinds.kindName(location), location);
mcimadamore@1186: }
mcimadamore@1186: }
mcimadamore@1186:
mcimadamore@895: /**
mcimadamore@895: * A delegated type representing a partially uninferred method type.
mcimadamore@895: * The return type of a partially uninferred method type is a ForAll
mcimadamore@895: * type - when the return type is instantiated (see Infer.instantiateExpr)
mcimadamore@895: * the underlying method type is also updated.
mcimadamore@895: */
mcimadamore@1114: abstract class UninferredMethodType extends DelegatedType {
mcimadamore@895:
mcimadamore@895: final List tvars;
mcimadamore@1114: final Symbol msym;
mcimadamore@1114: final DiagnosticPosition pos;
mcimadamore@895:
mcimadamore@1114: public UninferredMethodType(DiagnosticPosition pos, Symbol msym, MethodType mtype, List tvars) {
mcimadamore@895: super(METHOD, new MethodType(mtype.argtypes, null, mtype.thrown, mtype.tsym));
mcimadamore@895: this.tvars = tvars;
mcimadamore@1114: this.msym = msym;
mcimadamore@1114: this.pos = pos;
mcimadamore@895: asMethodType().restype = new UninferredReturnType(tvars, mtype.restype);
mcimadamore@895: }
mcimadamore@895:
mcimadamore@895: @Override
mcimadamore@895: public MethodType asMethodType() {
mcimadamore@895: return qtype.asMethodType();
mcimadamore@895: }
mcimadamore@895:
mcimadamore@895: @Override
mcimadamore@895: public Type map(Mapping f) {
mcimadamore@895: return qtype.map(f);
mcimadamore@895: }
mcimadamore@895:
mcimadamore@895: void instantiateReturnType(Type restype, List inferred, Types types) throws NoInstanceException {
mcimadamore@895: //update method type with newly inferred type-arguments
mcimadamore@895: qtype = new MethodType(types.subst(getParameterTypes(), tvars, inferred),
mcimadamore@895: restype,
mcimadamore@895: types.subst(UninferredMethodType.this.getThrownTypes(), tvars, inferred),
mcimadamore@895: UninferredMethodType.this.qtype.tsym);
mcimadamore@895: check(inferred, types);
mcimadamore@895: }
mcimadamore@895:
mcimadamore@895: abstract void check(List inferred, Types types) throws NoInstanceException;
mcimadamore@895:
mcimadamore@895: abstract List getConstraints(TypeVar tv, ConstraintKind ck);
mcimadamore@895:
mcimadamore@895: class UninferredReturnType extends ForAll {
mcimadamore@895: public UninferredReturnType(List tvars, Type restype) {
mcimadamore@895: super(tvars, restype);
mcimadamore@895: }
mcimadamore@895: @Override
mcimadamore@895: public Type inst(List actuals, Types types) {
mcimadamore@895: Type newRestype = super.inst(actuals, types);
mcimadamore@895: instantiateReturnType(newRestype, actuals, types);
mcimadamore@1114: if (rs.verboseResolutionMode.contains(VerboseResolutionMode.DEFERRED_INST)) {
mcimadamore@1114: log.note(pos, "deferred.method.inst", msym, UninferredMethodType.this.qtype, newRestype);
mcimadamore@1114: }
mcimadamore@895: return newRestype;
mcimadamore@895: }
mcimadamore@895: @Override
mcimadamore@895: public List getConstraints(TypeVar tv, ConstraintKind ck) {
mcimadamore@895: return UninferredMethodType.this.getConstraints(tv, ck);
mcimadamore@895: }
mcimadamore@895: }
mcimadamore@895: }
mcimadamore@895:
mcimadamore@845: private void checkArgumentsAcceptable(Env env, List actuals, List formals,
mcimadamore@845: boolean allowBoxing, boolean useVarargs, Warner warn) {
mcimadamore@845: try {
mcimadamore@845: rs.checkRawArgumentsAcceptable(env, actuals, formals,
mcimadamore@845: allowBoxing, useVarargs, warn);
mcimadamore@845: }
mcimadamore@1186: catch (InapplicableMethodException ex) {
mcimadamore@845: // inferred method is not applicable
mcimadamore@845: throw invalidInstanceException.setMessage(ex.getDiagnostic());
mcimadamore@845: }
mcimadamore@845: }
mcimadamore@845:
mcimadamore@895: /** Try to instantiate argument type `that' to given type `to'.
mcimadamore@895: * If this fails, try to insantiate `that' to `to' where
mcimadamore@895: * every occurrence of a type variable in `tvars' is replaced
mcimadamore@895: * by an unknown type.
mcimadamore@895: */
mcimadamore@895: private Type instantiateArg(ForAll that,
mcimadamore@895: Type to,
mcimadamore@895: List tvars,
mcimadamore@895: Warner warn) throws InferenceException {
mcimadamore@895: List targs;
mcimadamore@895: try {
mcimadamore@895: return instantiateExpr(that, to, warn);
mcimadamore@895: } catch (NoInstanceException ex) {
mcimadamore@895: Type to1 = to;
mcimadamore@895: for (List l = tvars; l.nonEmpty(); l = l.tail)
mcimadamore@895: to1 = types.subst(to1, List.of(l.head), List.of(syms.unknownType));
mcimadamore@895: return instantiateExpr(that, to1, warn);
duke@1: }
mcimadamore@895: }
duke@1:
duke@1: /** check that type parameters are within their bounds.
duke@1: */
mcimadamore@615: void checkWithinBounds(List tvars,
duke@1: List arguments,
duke@1: Warner warn)
mcimadamore@299: throws InvalidInstanceException {
duke@1: for (List tvs = tvars, args = arguments;
duke@1: tvs.nonEmpty();
duke@1: tvs = tvs.tail, args = args.tail) {
mcimadamore@828: if (args.head instanceof UndetVar ||
mcimadamore@828: tvars.head.getUpperBound().isErroneous()) continue;
duke@1: List bounds = types.subst(types.getBounds((TypeVar)tvs.head), tvars, arguments);
duke@1: if (!types.isSubtypeUnchecked(args.head, bounds, warn))
mcimadamore@299: throw invalidInstanceException
duke@1: .setMessage("inferred.do.not.conform.to.bounds",
mcimadamore@299: args.head, bounds);
duke@1: }
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@674: Type instantiatePolymorphicSignatureInstance(Env env, Type site,
mcimadamore@674: Name name,
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@895: }