package dotty.tools
package dotc
package typer
import core._
import ast._
import Contexts._, Types._, Flags._, Denotations._, Names._, StdNames._, NameOps._, Symbols._
import Trees._
import Constants._
import Scopes._
import annotation.unchecked
import util.Positions._
import util.{Stats, SimpleMap}
import util.common._
import Decorators._
import Uniques._
import ErrorReporting.{errorType, InfoString}
import config.Printers._
import collection.mutable
object Inferencing {
import tpd._
/** A trait defining an `isCompatible` method. */
trait Compatibility {
/** Is there an implicit conversion from `tp` to `pt`? */
def viewExists(tp: Type, pt: Type)(implicit ctx: Context): Boolean
/** A type `tp` is compatible with a type `pt` if one of the following holds:
* 1. `tp` is a subtype of `pt`
* 2. `pt` is by name parameter type, and `tp` is compatible with its underlying type
* 3. there is an implicit conversion from `tp` to `pt`.
*/
def isCompatible(tp: Type, pt: Type)(implicit ctx: Context): Boolean =
tp.widenExpr <:< pt.widenExpr || viewExists(tp, pt)
/** Test compatibility after normalization in a fresh typerstate. */
def normalizedCompatible(tp: Type, pt: Type)(implicit ctx: Context) = {
val nestedCtx = ctx.fresh.withExploreTyperState
isCompatible(normalize(tp, pt)(nestedCtx), pt)(nestedCtx)
}
/** Check that the result type of the current method
* fits the given expected result type.
*/
def constrainResult(mt: Type, pt: Type)(implicit ctx: Context): Boolean = pt match {
case FunProto(_, result, _) =>
mt match {
case mt: MethodType =>
mt.isDependent || constrainResult(mt.resultType, pt.resultType)
case _ =>
true
}
case _: ValueTypeOrProto if !(pt isRef defn.UnitClass) =>
mt match {
case mt: MethodType =>
mt.isDependent || isCompatible(normalize(mt, pt), pt)
case _ =>
isCompatible(mt, pt)
}
case _ =>
true
}
}
object NoViewsAllowed extends Compatibility {
override def viewExists(tp: Type, pt: Type)(implicit ctx: Context): Boolean = false
}
/** A prototype for expressions [] that are part of a selection operation:
*
* [ ].name: proto
*/
abstract case class SelectionProto(val name: Name, val memberProto: Type, val compat: Compatibility)
extends CachedProxyType with ProtoType with ValueTypeOrProto {
override def isMatchedBy(tp1: Type)(implicit ctx: Context) = {
name == nme.WILDCARD || {
val mbr = tp1.member(name)
def qualifies(m: SingleDenotation) = compat.normalizedCompatible(m.info, memberProto)
mbr match { // hasAltWith inlined for performance
case mbr: SingleDenotation => mbr.exists && qualifies(mbr)
case _ => mbr hasAltWith qualifies
}
}
}
def underlying(implicit ctx: Context) = WildcardType
def derivedSelectionProto(name: Name, memberProto: Type, compat: Compatibility)(implicit ctx: Context) =
if ((name eq this.name) && (memberProto eq this.memberProto) && (compat eq this.compat)) this
else SelectionProto(name, memberProto, compat)
override def equals(that: Any): Boolean = that match {
case that: SelectionProto =>
(name eq that.name) && (memberProto == that.memberProto) && (compat eq that.compat)
case _ =>
false
}
def map(tm: TypeMap)(implicit ctx: Context) = derivedSelectionProto(name, tm(memberProto), compat)
def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context) = ta(x, memberProto)
override def computeHash = addDelta(doHash(name, memberProto), if (compat == NoViewsAllowed) 1 else 0)
}
class CachedSelectionProto(name: Name, memberProto: Type, compat: Compatibility) extends SelectionProto(name, memberProto, compat)
object SelectionProto {
def apply(name: Name, memberProto: Type, compat: Compatibility)(implicit ctx: Context): SelectionProto = {
val selproto = new CachedSelectionProto(name, memberProto, compat)
if (compat eq NoViewsAllowed) unique(selproto) else selproto
}
}
/** Create a selection proto-type, but only one level deep;
* treat constructors specially
*/
def selectionProto(name: Name, tp: Type, typer: Typer)(implicit ctx: Context) =
if (name.isConstructorName) WildcardType
else tp match {
case tp: UnapplyFunProto => new UnapplySelectionProto(name)
case tp: ProtoType => SelectionProto(name, WildcardType, typer)
case _ => SelectionProto(name, tp, typer)
}
/** A prototype for expressions [] that are in some unspecified selection operation
*
* [].?: ?
*
* Used to indicate that expression is in a context where the only valid
* operation is further selection. In this case, the expression need not be a value.
* @see checkValue
*/
object AnySelectionProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed)
/** A prototype for selections in pattern constructors */
class UnapplySelectionProto(name: Name) extends SelectionProto(name, WildcardType, NoViewsAllowed)
trait ApplyingProto extends ProtoType
/** A prototype for expressions that appear in function position
*
* [](args): resultType
*/
case class FunProto(args: List[untpd.Tree], override val resultType: Type, typer: Typer)(implicit ctx: Context)
extends UncachedGroundType with ApplyingProto {
private var myTypedArgs: List[Tree] = Nil
/** A map in which typed arguments can be stored to be later integrated in `typedArgs`. */
private var myTypedArg: SimpleMap[untpd.Tree, Tree] = SimpleMap.Empty
def isMatchedBy(tp: Type)(implicit ctx: Context) =
typer.isApplicable(tp, Nil, typedArgs, resultType)
def derivedFunProto(args: List[untpd.Tree], resultType: Type, typer: Typer) =
if ((args eq this.args) && (resultType eq this.resultType) && (typer eq this.typer)) this
else new FunProto(args, resultType, typer)
def argsAreTyped: Boolean = myTypedArgs.nonEmpty || args.isEmpty
/** The typed arguments. This takes any arguments already typed using
* `typedArg` into account.
*/
def typedArgs: List[Tree] = {
if (!argsAreTyped)
myTypedArgs = args mapconserve { arg =>
val targ = myTypedArg(arg)
if (targ != null) targ else typer.typed(arg)
}
myTypedArgs
}
/** Type single argument and remember the unadapted result in `myTypedArg`.
* used to avoid repeated typings of trees when backtracking.
*/
def typedArg(arg: untpd.Tree, formal: Type)(implicit ctx: Context): Tree = {
var targ = myTypedArg(arg)
if (targ == null) {
val counts = ctx.reporter.errorCounts
targ = typer.typedUnadapted(arg, formal)
if (ctx.reporter.wasSilent(counts))
myTypedArg = myTypedArg.updated(arg, targ)
}
typer.adapt(targ, formal)
}
override def toString = s"FunProto(${args mkString ","} => $resultType)"
def map(tm: TypeMap)(implicit ctx: Context): FunProto =
derivedFunProto(args, tm(resultType), typer)
def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = ta(x, resultType)
}
/** A prototype for implicitly inferred views:
*
* []: argType => resultType
*/
abstract case class ViewProto(argType: Type, override val resultType: Type)(implicit ctx: Context)
extends CachedGroundType with ApplyingProto {
def isMatchedBy(tp: Type)(implicit ctx: Context): Boolean = /*ctx.conditionalTraceIndented(lookingForInfo, i"?.info isMatchedBy $tp ${tp.getClass}")*/ {
ctx.typer.isApplicable(tp, argType :: Nil, resultType)
}
def derivedViewProto(argType: Type, resultType: Type)(implicit ctx: Context) =
if ((argType eq this.argType) && (resultType eq this.resultType)) this
else ViewProto(argType, resultType)
def map(tm: TypeMap)(implicit ctx: Context): ViewProto = derivedViewProto(tm(argType), tm(resultType))
def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = ta(ta(x, argType), resultType)
override def namedPartsWith(p: NamedType => Boolean)(implicit ctx: Context): collection.Set[NamedType] =
AndType.unchecked(argType, resultType).namedPartsWith(p) // this is more efficient than oring two namedParts sets
}
class CachedViewProto(argType: Type, resultType: Type)(implicit ctx: Context) extends ViewProto(argType, resultType) {
override def computeHash = doHash(argType, resultType)
}
object ViewProto {
def apply(argType: Type, resultType: Type)(implicit ctx: Context) =
unique(new CachedViewProto(argType, resultType))
}
class UnapplyFunProto(typer: Typer)(implicit ctx: Context) extends FunProto(
untpd.TypedSplice(dummyTreeOfType(WildcardType)) :: Nil, WildcardType, typer)
/** A prototype for expressions [] that are type-parameterized:
*
* [] [targs] resultType
*/
case class PolyProto(targs: List[Type], override val resultType: Type) extends UncachedGroundType with ProtoType {
override def isMatchedBy(tp: Type)(implicit ctx: Context) = {
def isInstantiatable(tp: Type) = tp.widen match {
case PolyType(paramNames) => paramNames.length == targs.length
case _ => false
}
isInstantiatable(tp) || tp.member(nme.apply).hasAltWith(d => isInstantiatable(d.info))
}
def derivedPolyProto(targs: List[Type], resultType: Type) =
if ((targs eq this.targs) && (resultType eq this.resultType)) this
else PolyProto(targs, resultType)
def map(tm: TypeMap)(implicit ctx: Context): PolyProto =
derivedPolyProto(targs mapConserve tm, tm(resultType))
def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = ta((x /: targs)(ta), resultType)
}
/** A prototype for expressions [] that are known to be functions:
*
* [] _
*/
object AnyFunctionProto extends UncachedGroundType with ProtoType {
def isMatchedBy(tp: Type)(implicit ctx: Context) = true
def map(tm: TypeMap)(implicit ctx: Context) = this
def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context) = x
}
/** The normalized form of a type
* - unwraps polymorphic types, tracking their parameters in the current constraint
* - skips implicit parameters
* - converts non-dependent method types to the corresponding function types
* - dereferences parameterless method types
* - dereferences nullary method types provided the corresponding function type
* is not a subtype of the expected type.
* Note: We need to take account of the possibility of inserting a () argument list in normalization. Otherwise, a type with a
* def toString(): String
* member would not count as a valid solution for ?{toString: String}. This would then lead to an implicit
* insertion, with a nice explosion of inference search because of course every implicit result has some sort
* of toString method. The problem is solved by dereferencing nullary method types if the corresponding
* function type is not compatible with the prototype.
*/
def normalize(tp: Type, pt: Type)(implicit ctx: Context): Type = Stats.track("normalize") {
tp.widenSingleton match {
case poly: PolyType => normalize(constrained(poly).resultType, pt)
case mt: MethodType if !mt.isDependent /*&& !pt.isInstanceOf[ApplyingProto]*/ =>
if (mt.isImplicit) mt.resultType
else {
val rt = normalize(mt.resultType, pt)
if (pt.isInstanceOf[ApplyingProto])
mt.derivedMethodType(mt.paramNames, mt.paramTypes, rt)
else {
val ft = defn.FunctionType(mt.paramTypes, rt)
if (mt.paramTypes.nonEmpty || ft <:< pt) ft else rt
}
}
case et: ExprType => et.resultType
case _ => tp
}
}
/** An enumeration controlling the degree of forcing in "is-dully-defined" checks. */
object ForceDegree extends Enumeration {
val none, // don't force type variables
noBottom, // force type variables, fail if forced to Nothing or Null
all = Value // force type variables, don't fail
}
/** Is type fully defined, meaning the type does not contain wildcard types
* or uninstantiated type variables. As a side effect, this will minimize
* any uninstantiated type variables, according to the given force degree,
* but only if the overall result of `isFullyDefined` is `true`.
* Variables that are successfully minimized do not count as uninstantiated.
*/
def isFullyDefined(tp: Type, force: ForceDegree.Value)(implicit ctx: Context): Boolean = {
val nestedCtx = ctx.fresh.withNewTyperState
val result = new IsFullyDefinedAccumulator(force)(nestedCtx).process(tp)
if (result) nestedCtx.typerState.commit()
result
}
/** The fully defined type, where all type variables are forced.
* Throws an error if type contains wildcards.
*/
def fullyDefinedType(tp: Type, what: String, pos: Position)(implicit ctx: Context) =
if (isFullyDefined(tp, ForceDegree.all)) tp
else throw new Error(i"internal error: type of $what $tp is not fully defined, pos = $pos") // !!! DEBUG
/** The accumulator which forces type variables using the policy encoded in `force`
* and returns whether the type is fully defined. Two phases:
* 1st Phase: Try to stantiate covariant and non-variant type variables to
* their lower bound. Record whether succesful.
* 2nd Phase: If first phase was succesful, instantiate all remaining type variables
* to their upper bound.
*/
private class IsFullyDefinedAccumulator(force: ForceDegree.Value)(implicit ctx: Context) extends TypeAccumulator[Boolean] {
private def instantiate(tvar: TypeVar, fromBelow: Boolean): Type = {
val inst = tvar.instantiate(fromBelow)
typr.println(i"forced instantiation of ${tvar.origin} = $inst")
inst
}
private var toMaximize: Boolean = false
def apply(x: Boolean, tp: Type): Boolean = tp.dealias match {
case _: WildcardType =>
false
case tvar: TypeVar if !tvar.isInstantiated =>
if (force == ForceDegree.none) false
else {
val minimize =
variance >= 0 && !(
force == ForceDegree.noBottom &&
isBottomType(ctx.typeComparer.approximation(tvar.origin, fromBelow = true)))
if (minimize) instantiate(tvar, fromBelow = true)
else toMaximize = true
foldOver(x, tvar)
}
case tp =>
foldOver(x, tp)
}
private class UpperInstantiator(implicit ctx: Context) extends TypeAccumulator[Unit] {
def apply(x: Unit, tp: Type): Unit = {
tp match {
case tvar: TypeVar if !tvar.isInstantiated =>
instantiate(tvar, fromBelow = false)
case _ =>
}
foldOver(x, tp)
}
}
def process(tp: Type): Boolean = {
val res = apply(true, tp)
if (res && toMaximize) new UpperInstantiator().apply((), tp)
res
}
}
def isBottomType(tp: Type)(implicit ctx: Context) =
tp == defn.NothingType || tp == defn.NullType
/** Recursively widen and also follow type declarations and type aliases. */
def widenForMatchSelector(tp: Type)(implicit ctx: Context): Type = tp.widen match {
case tp: TypeRef if !tp.symbol.isClass => widenForMatchSelector(tp.info.bounds.hi)
case tp => tp
}
/** Following type aliases and stripping refinements and annotations, if one arrives at a
* class type reference where the class has a companion module, a reference to
* that companion module. Otherwise NoType
*/
def companionRef(tp: Type)(implicit ctx: Context): Type = tp.underlyingClassRef match {
case tp: TypeRef =>
val companion = tp.classSymbol.companionModule
if (companion.exists)
companion.valRef.asSeenFrom(tp.prefix, companion.symbol.owner)
else NoType
case _ => NoType
}
/** Check that type arguments `args` conform to corresponding bounds in `poly` */
def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit =
for ((arg, bounds) <- args zip poly.paramBounds) {
def notConforms(which: String, bound: Type) =
ctx.error(i"Type argument ${arg.tpe} does not conform to $which bound $bound", arg.pos)
if (!(arg.tpe <:< bounds.hi)) notConforms("upper", bounds.hi)
if (!(bounds.lo <:< arg.tpe)) notConforms("lower", bounds.lo)
}
/** Check that type `tp` is stable.
* @return The type itself
*/
def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit =
if (!tp.isStable) ctx.error(i"Prefix of type ${tp.widenIfUnstable} is not stable", pos)
/** Check that `tp` is a class type with a stable prefix.
* @return Underlying class type if type checks out OK, ObjectClass.typeRef if not.
*/
def checkClassTypeWithStablePrefix(tp: Type, pos: Position)(implicit ctx: Context): TypeRef =
tp.underlyingClassRef match {
case tp: TypeRef =>
checkStable(tp.prefix, pos)
tp
case _ =>
ctx.error(i"$tp is not a class type", pos)
defn.ObjectClass.typeRef
}
/** Check that (return) type of implicit definition is not empty */
def checkImplicitTptNonEmpty(defTree: untpd.ValOrDefDef)(implicit ctx: Context): Unit = defTree.tpt match {
case TypeTree(original) if original.isEmpty =>
val resStr = if (defTree.isInstanceOf[untpd.DefDef]) "result " else ""
ctx.error(i"${resStr}type of implicit definition needs to be given explicitly", defTree.pos)
case _ =>
}
/** Check that a non-implicit parameter making up the first parameter section of an
* implicit conversion is not a singleton type.
*/
def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = vparamss match {
case (vparam :: Nil) :: _ if !(vparam.symbol is Implicit) =>
if (vparam.tpt.tpe.isInstanceOf[SingletonType])
ctx.error(s"implicit conversion may not have a parameter of singleton type", vparam.tpt.pos)
case _ =>
}
/** Ensure that first typeref in a list of parents points to a non-trait class.
* If that's not already the case, add one.
*/
def ensureFirstIsClass(prefs: List[TypeRef])(implicit ctx: Context): List[TypeRef] = {
def isRealClass(sym: Symbol) = sym.isClass && !(sym is Trait)
def realClassParent(tref: TypeRef): TypeRef =
if (isRealClass(tref.symbol)) tref
else tref.info.parents match {
case pref :: _ => if (isRealClass(pref.symbol)) pref else realClassParent(pref)
case nil => defn.ObjectClass.typeRef
}
def improve(clsRef: TypeRef, parent: TypeRef): TypeRef = {
val pclsRef = realClassParent(parent)
if (pclsRef.symbol derivesFrom clsRef.symbol) pclsRef else clsRef
}
prefs match {
case pref :: _ if isRealClass(pref.symbol) => prefs
case _ => (defn.ObjectClass.typeRef /: prefs)(improve) :: prefs
}
}
/** Forward bindings of all type parameters of `pcls`. That is, if the type parameter
* if instantiated in a parent class, include its type binding in the current class.
*/
def forwardTypeParams(pcls: ClassSymbol, cls: ClassSymbol, decls: Scope)(implicit ctx: Context): Unit = {
for (tparam <- pcls.typeParams) {
val argSym: Symbol = cls.thisType.member(tparam.name).symbol
argSym.info match {
case TypeAlias(TypeRef(ThisType(_), name)) =>
val from = cls.thisType.member(name).symbol
from.info match {
case bounds: TypeBounds =>
typr.println(s"forward ref $argSym $from $bounds")
ctx.forwardRef(argSym, from, bounds, cls, decls)
case _ =>
}
case _ =>
}
}
}
/** Check that class does not define */
def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = {
val seen = new mutable.HashMap[Name, List[Symbol]] {
override def default(key: Name) = Nil
}
typr.println(i"check no double defs $cls")
for (decl <- cls.info.decls) {
for (other <- seen(decl.name)) {
typr.println(i"conflict? $decl $other")
if (decl.signature matches other.signature) {
def doubleDefError(decl: Symbol, other: Symbol): Unit = {
def ofType = if (decl.isType) "" else i": ${other.info}"
def explanation =
if (!decl.isSourceMethod) ""
else "\n (both definitions have the same erased type signature)"
ctx.error(i"$decl is already defined as $other$ofType$explanation", decl.pos)
}
if (decl is Synthetic) doubleDefError(other, decl)
else doubleDefError(decl, other)
}
if ((decl is HasDefaultParams) && (other is HasDefaultParams)) {
ctx.error(i"two or more overloaded variants of $decl have default arguments")
decl resetFlag HasDefaultParams
}
}
seen(decl.name) = decl :: seen(decl.name)
}
}
def checkInstantiatable(cls: ClassSymbol, pos: Position): Unit = {
??? // to be done in later phase: check that class `cls` is legal in a new.
}
/** Approximate occurrences of parameter types and uninstantiated typevars
* by wildcard types.
*/
final def wildApprox(tp: Type, theMap: WildApproxMap = null)(implicit ctx: Context): Type = tp match {
case tp: NamedType => // default case, inlined for speed
if (tp.symbol.isStatic) tp
else tp.derivedSelect(wildApprox(tp.prefix, theMap))
case PolyParam(pt, pnum) =>
WildcardType(wildApprox(pt.paramBounds(pnum)).bounds)
case MethodParam(mt, pnum) =>
WildcardType(TypeBounds.upper(wildApprox(mt.paramTypes(pnum))))
case tp: TypeVar =>
val inst = tp.instanceOpt
if (inst.exists) wildApprox(inst)
else ctx.typerState.constraint.at(tp.origin) match {
case bounds: TypeBounds => wildApprox(WildcardType(bounds))
case NoType => WildcardType
}
case tp: AndType =>
val tp1a = wildApprox(tp.tp1)
val tp2a = wildApprox(tp.tp2)
def wildBounds(tp: Type) =
if (tp.isInstanceOf[WildcardType]) tp.bounds else TypeBounds.upper(tp)
if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType])
WildcardType(wildBounds(tp1a) & wildBounds(tp2a))
else
tp.derivedAndType(tp1a, tp2a)
case tp: OrType =>
val tp1a = wildApprox(tp.tp1)
val tp2a = wildApprox(tp.tp2)
if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType])
WildcardType(tp1a.bounds | tp2a.bounds)
else
tp.derivedOrType(tp1a, tp2a)
case tp: SelectionProto =>
tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto), NoViewsAllowed)
case tp: ViewProto =>
tp.derivedViewProto(wildApprox(tp.argType), wildApprox(tp.resultType))
case _: ThisType | _: BoundType | NoPrefix => // default case, inlined for speed
tp
case tp: RefinedType => // default case, inlined for speed
tp.derivedRefinedType(wildApprox(tp.parent, theMap), tp.refinedName, wildApprox(tp.refinedInfo, theMap))
case _ =>
(if (theMap != null) theMap else new WildApproxMap).mapOver(tp)
}
private[Inferencing] class WildApproxMap(implicit ctx: Context) extends TypeMap {
def apply(tp: Type) = wildApprox(tp, this)
}
/** Add all parameters in given polytype `pt` to the constraint's domain.
* If the constraint contains already some of these parameters in its domain,
* make a copy of the polytype and add the copy's type parameters instead.
* Return either the original polytype, or the copy, if one was made.
* Also, if `owningTree` is non-empty, add a type variable for each parameter.
* @return The added polytype, and the list of created type variables.
*/
def constrained(pt: PolyType, owningTree: untpd.Tree)(implicit ctx: Context): (PolyType, List[TypeVar]) = {
val state = ctx.typerState
def howmany = if (owningTree.isEmpty) "no" else "some"
def committable = if (ctx.typerState.isCommittable) "committable" else "uncommittable"
assert(owningTree.isEmpty != ctx.typerState.isCommittable,
s"inconsistent: $howmany typevars were added to $committable constraint ${state.constraint}")
def newTypeVars(pt: PolyType): List[TypeVar] =
for (n <- (0 until pt.paramNames.length).toList)
yield new TypeVar(PolyParam(pt, n), state, owningTree)
val added =
if (state.constraint contains pt) pt.copy(pt.paramNames, pt.paramBounds, pt.resultType)
else pt
val tvars = if (owningTree.isEmpty) Nil else newTypeVars(added)
state.constraint = state.constraint.add(added, tvars)
(added, tvars)
}
/** Same as `constrained(pt, EmptyTree)`, but returns just the created polytype */
def constrained(pt: PolyType)(implicit ctx: Context): PolyType = constrained(pt, EmptyTree)._1
/** Interpolate those undetermined type variables in the widened type of this tree
* which are introduced by type application contained in the tree.
* If such a variable appears covariantly in type `tp` or does not appear at all,
* approximate it by its lower bound. Otherwise, if it appears contravariantly
* in type `tp` approximate it by its upper bound.
*/
def interpolateUndetVars(tree: Tree)(implicit ctx: Context): Unit = Stats.track("interpolateUndetVars") {
val tp = tree.tpe.widen
val constraint = ctx.typerState.constraint
constr.println(s"interpolate undet vars in ${tp.show}, pos = ${tree.pos}, mode = ${ctx.mode}, undets = ${constraint.uninstVars map (tvar => s"${tvar.show}@${tvar.owningTree.pos}")}")
constr.println(s"qualifying undet vars: ${constraint.uninstVars filter qualifies map (tvar => s"$tvar / ${tvar.show}")}")
constr.println(s"fulltype: $tp") // !!! DEBUG
constr.println(s"constraint: ${constraint.show}")
def qualifies(tvar: TypeVar) = tree contains tvar.owningTree
val vs = tp.variances(tvar => (constraint contains tvar) && qualifies(tvar))
var changed = false
vs foreachBinding { (tvar, v) =>
if (v != 0) {
typr.println(s"interpolate ${if (v == 1) "co" else "contra"}variant ${tvar.show} in ${tp.show}")
tvar.instantiate(fromBelow = v == 1)
changed = true
}
}
if (changed) // instantiations might have uncovered new typevars to interpolate
interpolateUndetVars(tree)
else
constraint.foreachUninstVar { tvar =>
if (!(vs contains tvar) && qualifies(tvar)) {
typr.println(s"instantiating non-occurring ${tvar.show} in ${tp.show}")
tvar.instantiate(fromBelow = true)
}
}
}
/** Instantiate undetermined type variables to that type `tp` is
* maximized and return None. If this is not possible, because a non-variant
* typevar is not uniquely determined, return that typevar in a Some.
*/
def maximizeType(tp: Type)(implicit ctx: Context): Option[TypeVar] = Stats.track("maximizeType") {
val constraint = ctx.typerState.constraint
val vs = tp.variances(constraint contains _)
var result: Option[TypeVar] = None
vs foreachBinding { (tvar, v) =>
if (v == 1) tvar.instantiate(fromBelow = false)
else if (v == -1) tvar.instantiate(fromBelow = true)
else {
val bounds = ctx.typerState.constraint.bounds(tvar.origin)
if (!(bounds.hi <:< bounds.lo)) result = Some(tvar)
tvar.instantiate(fromBelow = false)
}
}
result
}
private lazy val dummyTree = untpd.Literal(Constant(null))
/** Dummy tree to be used as an argument of a FunProto or ViewProto type */
def dummyTreeOfType(tp: Type): Tree = dummyTree withTypeUnchecked tp
}
/* not needed right now
def isSubTypes(actuals: List[Type], formals: List[Type])(implicit ctx: Context): Boolean = formals match {
case formal :: formals1 =>
actuals match {
case actual :: actuals1 => actual <:< formal && isSubTypes(actuals1, formals1)
case _ => false
}
case nil =>
actuals.isEmpty
}
def formalParameters[T](mtp: MethodType, actuals: List[T])(isRepeated: T => Boolean)(implicit ctx: Context) =
if (mtp.isVarArgs && !(actuals.nonEmpty && isRepeated(actuals.last))) {
val leading = mtp.paramTypes.init
val repeated = mtp.paramTypes.last.typeArgs.head
val trailing = List.fill(actuals.length - leading.length)(repeated)
leading ++ trailing
}
else mtp.paramTypes
*/