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@1338: import com.sun.tools.javac.code.Type.UndetVar.InferenceBound;
mcimadamore@1347: import com.sun.tools.javac.comp.DeferredAttr.AttrMode;
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:
jjg@1374: import static com.sun.tools.javac.code.TypeTag.*;
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@1347: DeferredAttr deferredAttr;
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@1347: deferredAttr = DeferredAttr.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@1338: List hibounds = Type.filter(that.getBounds(InferenceBound.UPPER), boundFilter);
mcimadamore@1338: if (that.getBounds(InferenceBound.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@1338: that.inst = that.getBounds(InferenceBound.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) {
jjg@1374: return !t.isErroneous() && !t.hasTag(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@1338: List lobounds = Type.filter(that.getBounds(InferenceBound.LOWER), boundFilter);
mcimadamore@1338: if (that.getBounds(InferenceBound.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@1374: if (that.inst == null || that.inst.hasTag(ERROR))
mcimadamore@1298: throw inferenceException
duke@1: .setMessage("no.unique.minimal.instance.exists",
mcimadamore@828: that.qtype, lobounds);
mcimadamore@1251: } else {
mcimadamore@1338: that.inst = that.getBounds(InferenceBound.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;
jjg@1374: if (to.hasTag(NONE) || resultInfo.checkContext.inferenceContext().free(resultInfo.pt)) {
jjg@1374: to = mtype.getReturnType().isPrimitiveOrVoid() ?
mcimadamore@1268: mtype.getReturnType() : syms.objectType;
mcimadamore@1268: }
mcimadamore@1337: Type qtype1 = inferenceContext.asFree(mtype.getReturnType(), types);
mcimadamore@753: if (!types.isSubtype(qtype1,
jjg@1374: qtype1.hasTag(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@1338: if (uv.inst == null && (uv.getBounds(InferenceBound.EQ).nonEmpty() ||
mcimadamore@1338: !inferenceContext.free(uv.getBounds(InferenceBound.UPPER)))) {
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@1338: uv.substBounds(inferenceContext.inferenceVars(), inferenceContext.instTypes(), 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@1338: fresh_tvar.type = new TypeVar(fresh_tvar, types.makeCompoundType(uv.getBounds(InferenceBound.UPPER)), 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@1347: Resolve.MethodResolutionContext resolveContext,
mcimadamore@1268: Warner warn) throws InferenceException {
duke@1: //-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG
mcimadamore@1348: final InferenceContext inferenceContext = new InferenceContext(tvars, this, true);
mcimadamore@1337: inferenceException.clear();
mcimadamore@689:
mcimadamore@1337: try {
mcimadamore@1347: rs.checkRawArgumentsAcceptable(env, msym, resolveContext.attrMode(), inferenceContext,
mcimadamore@1347: argtypes, mt.getParameterTypes(), allowBoxing, useVarargs, warn,
mcimadamore@1347: 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@1338: if (resultInfo != null && !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) {
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@1338: Warner warn) throws InferenceException {
mcimadamore@1338: //step 1 - check compatibility of instantiated type w.r.t. initial bounds
mcimadamore@1337: for (Type t : inferenceContext.undetvars) {
mcimadamore@1251: UndetVar uv = (UndetVar)t;
mcimadamore@1338: uv.substBounds(inferenceContext.inferenceVars(), inferenceContext.instTypes(), types);
mcimadamore@1337: checkCompatibleUpperBounds(uv, inferenceContext.inferenceVars());
mcimadamore@1338: if (!inferenceContext.restvars().contains(uv.qtype)) {
mcimadamore@1337: Type inst = inferenceContext.asInstType(t, types);
mcimadamore@1338: for (Type u : uv.getBounds(InferenceBound.UPPER)) {
mcimadamore@1337: if (!types.isSubtypeUnchecked(inst, inferenceContext.asFree(u, types), warn)) {
mcimadamore@1251: reportBoundError(uv, BoundErrorKind.UPPER);
mcimadamore@1251: }
mcimadamore@1251: }
mcimadamore@1338: for (Type l : uv.getBounds(InferenceBound.LOWER)) {
mcimadamore@1338: Assert.check(!inferenceContext.free(l));
mcimadamore@1338: if (!types.isSubtypeUnchecked(l, inst, warn)) {
mcimadamore@1251: reportBoundError(uv, BoundErrorKind.LOWER);
mcimadamore@1251: }
mcimadamore@1251: }
mcimadamore@1338: for (Type e : uv.getBounds(InferenceBound.EQ)) {
mcimadamore@1338: Assert.check(!inferenceContext.free(e));
mcimadamore@1338: if (!types.isSameType(inst, e)) {
mcimadamore@1251: reportBoundError(uv, BoundErrorKind.EQ);
mcimadamore@1251: }
mcimadamore@1251: }
mcimadamore@1251: }
mcimadamore@1338: }
mcimadamore@1338:
mcimadamore@1338: //step 2 - check that eq bounds are consistent w.r.t. eq/lower bounds
mcimadamore@1338: for (Type t : inferenceContext.undetvars) {
mcimadamore@1338: UndetVar uv = (UndetVar)t;
mcimadamore@1338: //check eq bounds consistency
mcimadamore@1338: Type eq = null;
mcimadamore@1338: for (Type e : uv.getBounds(InferenceBound.EQ)) {
mcimadamore@1338: Assert.check(!inferenceContext.free(e));
mcimadamore@1338: if (eq != null && !types.isSameType(e, eq)) {
mcimadamore@1338: reportBoundError(uv, BoundErrorKind.EQ);
mcimadamore@1338: }
mcimadamore@1338: eq = e;
mcimadamore@1338: for (Type l : uv.getBounds(InferenceBound.LOWER)) {
mcimadamore@1338: Assert.check(!inferenceContext.free(l));
mcimadamore@1338: if (!types.isSubtypeUnchecked(l, e, warn)) {
mcimadamore@1338: reportBoundError(uv, BoundErrorKind.BAD_EQ_LOWER);
mcimadamore@1338: }
mcimadamore@1338: }
mcimadamore@1338: for (Type u : uv.getBounds(InferenceBound.UPPER)) {
mcimadamore@1338: if (inferenceContext.free(u)) continue;
mcimadamore@1338: if (!types.isSubtypeUnchecked(e, u, warn)) {
mcimadamore@1338: reportBoundError(uv, BoundErrorKind.BAD_EQ_UPPER);
mcimadamore@1338: }
mcimadamore@1338: }
mcimadamore@1338: }
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@1338: for (Type t : Type.filter(uv.getBounds(InferenceBound.UPPER), 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@1338: return ex.setMessage("incompatible.upper.bounds", uv.qtype,
mcimadamore@1338: uv.getBounds(InferenceBound.UPPER));
mcimadamore@1338: }
mcimadamore@1338: },
mcimadamore@1338: BAD_EQ_UPPER() {
mcimadamore@1338: @Override
mcimadamore@1338: InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
mcimadamore@1338: return ex.setMessage("incompatible.eq.upper.bounds", uv.qtype,
mcimadamore@1338: uv.getBounds(InferenceBound.EQ), uv.getBounds(InferenceBound.UPPER));
mcimadamore@1338: }
mcimadamore@1338: },
mcimadamore@1338: BAD_EQ_LOWER() {
mcimadamore@1338: @Override
mcimadamore@1338: InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
mcimadamore@1338: return ex.setMessage("incompatible.eq.lower.bounds", uv.qtype,
mcimadamore@1338: uv.getBounds(InferenceBound.EQ), uv.getBounds(InferenceBound.LOWER));
mcimadamore@1251: }
mcimadamore@1251: },
mcimadamore@1251: UPPER() {
mcimadamore@1251: @Override
mcimadamore@1251: InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
mcimadamore@1338: return ex.setMessage("inferred.do.not.conform.to.upper.bounds", uv.inst,
mcimadamore@1338: uv.getBounds(InferenceBound.UPPER));
mcimadamore@1251: }
mcimadamore@1251: },
mcimadamore@1251: LOWER() {
mcimadamore@1251: @Override
mcimadamore@1251: InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
mcimadamore@1338: return ex.setMessage("inferred.do.not.conform.to.lower.bounds", uv.inst,
mcimadamore@1338: uv.getBounds(InferenceBound.LOWER));
mcimadamore@1251: }
mcimadamore@1251: },
mcimadamore@1251: EQ() {
mcimadamore@1251: @Override
mcimadamore@1251: InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
mcimadamore@1338: return ex.setMessage("inferred.do.not.conform.to.eq.bounds", uv.inst,
mcimadamore@1338: uv.getBounds(InferenceBound.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@1348: //
mcimadamore@1348: /**
mcimadamore@1348: * This method is used to infer a suitable target functional interface in case
mcimadamore@1348: * the original parameterized interface contains wildcards. An inference process
mcimadamore@1348: * is applied so that wildcard bounds, as well as explicit lambda/method ref parameters
mcimadamore@1348: * (where applicable) are used to constraint the solution.
mcimadamore@1348: */
mcimadamore@1348: public Type instantiateFunctionalInterface(DiagnosticPosition pos, Type funcInterface,
mcimadamore@1348: List paramTypes, Check.CheckContext checkContext) {
mcimadamore@1348: if (types.capture(funcInterface) == funcInterface) {
mcimadamore@1348: //if capture doesn't change the type then return the target unchanged
mcimadamore@1348: //(this means the target contains no wildcards!)
mcimadamore@1348: return funcInterface;
mcimadamore@1348: } else {
mcimadamore@1348: Type formalInterface = funcInterface.tsym.type;
mcimadamore@1348: InferenceContext funcInterfaceContext =
mcimadamore@1348: new InferenceContext(funcInterface.tsym.type.getTypeArguments(), this, false);
mcimadamore@1348: if (paramTypes != null) {
mcimadamore@1348: //get constraints from explicit params (this is done by
mcimadamore@1348: //checking that explicit param types are equal to the ones
mcimadamore@1348: //in the functional interface descriptors)
mcimadamore@1348: List descParameterTypes = types.findDescriptorType(formalInterface).getParameterTypes();
mcimadamore@1348: if (descParameterTypes.size() != paramTypes.size()) {
mcimadamore@1348: checkContext.report(pos, diags.fragment("incompatible.arg.types.in.lambda"));
mcimadamore@1348: return types.createErrorType(funcInterface);
mcimadamore@1348: }
mcimadamore@1348: for (Type p : descParameterTypes) {
mcimadamore@1348: if (!types.isSameType(funcInterfaceContext.asFree(p, types), paramTypes.head)) {
mcimadamore@1348: checkContext.report(pos, diags.fragment("no.suitable.functional.intf.inst", funcInterface));
mcimadamore@1348: return types.createErrorType(funcInterface);
mcimadamore@1348: }
mcimadamore@1348: paramTypes = paramTypes.tail;
mcimadamore@1348: }
mcimadamore@1348: for (Type t : funcInterfaceContext.undetvars) {
mcimadamore@1348: UndetVar uv = (UndetVar)t;
mcimadamore@1415: minimizeInst(uv, types.noWarnings);
mcimadamore@1348: if (uv.inst == null &&
mcimadamore@1348: Type.filter(uv.getBounds(InferenceBound.UPPER), boundFilter).nonEmpty()) {
mcimadamore@1415: maximizeInst(uv, types.noWarnings);
mcimadamore@1348: }
mcimadamore@1348: }
mcimadamore@1348:
mcimadamore@1348: formalInterface = funcInterfaceContext.asInstType(formalInterface, types);
mcimadamore@1348: }
mcimadamore@1348: ListBuffer typeargs = ListBuffer.lb();
mcimadamore@1348: List actualTypeargs = funcInterface.getTypeArguments();
mcimadamore@1348: //for remaining uninferred type-vars in the functional interface type,
mcimadamore@1348: //simply replace the wildcards with its bound
mcimadamore@1348: for (Type t : formalInterface.getTypeArguments()) {
jjg@1374: if (actualTypeargs.head.hasTag(WILDCARD)) {
mcimadamore@1348: WildcardType wt = (WildcardType)actualTypeargs.head;
mcimadamore@1348: typeargs.append(wt.type);
mcimadamore@1348: } else {
mcimadamore@1348: typeargs.append(actualTypeargs.head);
mcimadamore@1348: }
mcimadamore@1348: actualTypeargs = actualTypeargs.tail;
mcimadamore@1348: }
mcimadamore@1348: Type owntype = types.subst(formalInterface, funcInterfaceContext.inferenceVars(), typeargs.toList());
mcimadamore@1348: if (!chk.checkValidGenericType(owntype)) {
mcimadamore@1348: //if the inferred functional interface type is not well-formed,
mcimadamore@1348: //or if it's not a subtype of the original target, issue an error
mcimadamore@1348: checkContext.report(pos, diags.fragment("no.suitable.functional.intf.inst", funcInterface));
mcimadamore@1348: return types.createErrorType(funcInterface);
mcimadamore@1348: }
mcimadamore@1348: return owntype;
mcimadamore@1348: }
mcimadamore@1348: }
mcimadamore@1348: //
mcimadamore@1348:
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@1347: Resolve.MethodResolutionContext resolveContext,
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@1347: List paramtypes = Type.map(argtypes, new ImplicitArgType(spMethod, resolveContext.step));
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@1347: class ImplicitArgType extends DeferredAttr.DeferredTypeMap {
mcimadamore@1347:
mcimadamore@1347: public ImplicitArgType(Symbol msym, Resolve.MethodResolutionPhase phase) {
mcimadamore@1347: deferredAttr.super(AttrMode.SPECULATIVE, msym, phase);
mcimadamore@1347: }
mcimadamore@1347:
mcimadamore@1347: public Type apply(Type t) {
mcimadamore@1347: t = types.erasure(super.apply(t));
jjg@1374: if (t.hasTag(BOT))
mcimadamore@1347: // nulls type as the marker type Null (which has no instances)
mcimadamore@1347: // infer as java.lang.Void for now
mcimadamore@1347: t = types.boxedClass(syms.voidType).type;
mcimadamore@1347: return t;
mcimadamore@1347: }
mcimadamore@1347: }
mcimadamore@1337:
mcimadamore@1337: /**
mcimadamore@1337: * Mapping that turns inference variables into undet vars
mcimadamore@1337: * (used by inference context)
mcimadamore@1337: */
mcimadamore@1348: class FromTypeVarFun extends Mapping {
mcimadamore@1348:
mcimadamore@1348: boolean includeBounds;
mcimadamore@1348:
mcimadamore@1348: FromTypeVarFun(boolean includeBounds) {
mcimadamore@1348: super("fromTypeVarFunWithBounds");
mcimadamore@1348: this.includeBounds = includeBounds;
mcimadamore@1348: }
mcimadamore@1348:
mcimadamore@1337: public Type apply(Type t) {
jjg@1374: if (t.hasTag(TYPEVAR)) return new UndetVar((TypeVar)t, types, includeBounds);
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@1348: public InferenceContext(List inferencevars, Infer infer, boolean includeBounds) {
mcimadamore@1348: this.undetvars = Type.map(inferencevars, infer.new FromTypeVarFun(includeBounds));
mcimadamore@1337: this.inferencevars = inferencevars;
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@1347:
mcimadamore@1347: void solveAny(List varsToSolve, Types types, Infer infer) {
mcimadamore@1347: boolean progress = false;
mcimadamore@1347: for (Type t : varsToSolve) {
mcimadamore@1347: UndetVar uv = (UndetVar)asFree(t, types);
mcimadamore@1347: if (uv.inst == null) {
mcimadamore@1415: infer.minimizeInst(uv, types.noWarnings);
mcimadamore@1347: if (uv.inst != null) {
mcimadamore@1347: progress = true;
mcimadamore@1347: }
mcimadamore@1347: }
mcimadamore@1347: }
mcimadamore@1347: if (!progress) {
mcimadamore@1347: throw infer.inferenceException.setMessage("cyclic.inference", varsToSolve);
mcimadamore@1347: }
mcimadamore@1347: }
mcimadamore@895: }
mcimadamore@1337:
mcimadamore@1348: final InferenceContext emptyContext = new InferenceContext(List.nil(), this, false);
mcimadamore@1337: }