aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/core/TypeApplications.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/dotty/tools/dotc/core/TypeApplications.scala')
-rw-r--r--src/dotty/tools/dotc/core/TypeApplications.scala616
1 files changed, 259 insertions, 357 deletions
diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala
index 3ed1798ed..314233709 100644
--- a/src/dotty/tools/dotc/core/TypeApplications.scala
+++ b/src/dotty/tools/dotc/core/TypeApplications.scala
@@ -4,7 +4,7 @@ package core
import Types._
import Contexts._
import Symbols._
-import SymDenotations.TypeParamsCompleter
+import SymDenotations.{LazyType, TypeParamsCompleter}
import Decorators._
import util.Stats._
import util.common._
@@ -15,6 +15,7 @@ import StdNames.tpnme
import util.Positions.Position
import config.Printers._
import collection.mutable
+import dotty.tools.dotc.config.Config
import java.util.NoSuchElementException
object TypeApplications {
@@ -37,56 +38,23 @@ object TypeApplications {
case _ => tp
}
- /** Does the variance of `sym1` conform to the variance of `sym2`?
+ /** Does variance `v1` conform to variance `v2`?
* This is the case if the variances are the same or `sym` is nonvariant.
*/
- def varianceConforms(sym1: TypeSymbol, sym2: TypeSymbol)(implicit ctx: Context) =
- sym1.variance == sym2.variance || sym2.variance == 0
+ def varianceConforms(v1: Int, v2: Int): Boolean =
+ v1 == v2 || v2 == 0
- def variancesConform(syms1: List[TypeSymbol], syms2: List[TypeSymbol])(implicit ctx: Context) =
- syms1.corresponds(syms2)(varianceConforms)
-
- /** Extractor for
- *
- * [v1 X1: B1, ..., vn Xn: Bn] -> T
- * ==>
- * Lambda$_v1...vn { type $hk_i: B_i, type $Apply = [X_i := this.$Arg_i] T }
+ /** Does the variance of type parameter `tparam1` conform to the variance of type parameter `tparam2`?
*/
- object TypeLambda {
- def apply(variances: List[Int],
- argBoundsFns: List[RefinedType => TypeBounds],
- bodyFn: RefinedType => Type)(implicit ctx: Context): Type = {
- def argRefinements(parent: Type, i: Int, bs: List[RefinedType => TypeBounds]): Type = bs match {
- case b :: bs1 =>
- argRefinements(RefinedType(parent, tpnme.hkArg(i), b), i + 1, bs1)
- case nil =>
- parent
- }
- assert(variances.nonEmpty)
- assert(argBoundsFns.length == variances.length)
- RefinedType(
- argRefinements(defn.LambdaTrait(variances).typeRef, 0, argBoundsFns),
- tpnme.hkApply, bodyFn(_).bounds.withVariance(1))
- }
+ def varianceConforms(tparam1: TypeParamInfo, tparam2: TypeParamInfo)(implicit ctx: Context): Boolean =
+ varianceConforms(tparam1.paramVariance, tparam2.paramVariance)
- def unapply(tp: Type)(implicit ctx: Context): Option[(List[Int], List[TypeBounds], Type)] = tp match {
- case app @ RefinedType(parent, tpnme.hkApply) =>
- val cls = parent.typeSymbol
- val variances = cls.typeParams.map(_.variance)
- def collectBounds(t: Type, acc: List[TypeBounds]): List[TypeBounds] = t match {
- case t @ RefinedType(p, rname) =>
- assert(rname.isHkArgName)
- collectBounds(p, t.refinedInfo.bounds :: acc)
- case TypeRef(_, lname) =>
- assert(lname.isLambdaTraitName)
- acc
- }
- val argBounds = collectBounds(parent, Nil)
- Some((variances, argBounds, app.refinedInfo.argInfo))
- case _ =>
- None
- }
- }
+ /** Do the variances of type parameters `tparams1` conform to the variances
+ * of corresponding type parameters `tparams2`?
+ * This is only the case of `tparams1` and `tparams2` have the same length.
+ */
+ def variancesConform(tparams1: List[TypeParamInfo], tparams2: List[TypeParamInfo])(implicit ctx: Context): Boolean =
+ tparams1.corresponds(tparams2)(varianceConforms)
/** Extractor for
*
@@ -98,25 +66,14 @@ object TypeApplications {
* @param tycon C
*/
object EtaExpansion {
- def apply(tycon: TypeRef)(implicit ctx: Context) = {
- assert(tycon.isEtaExpandable)
- tycon.EtaExpand(tycon.typeParams)
+ def apply(tycon: Type)(implicit ctx: Context) = {
+ assert(tycon.typeParams.nonEmpty, tycon)
+ tycon.EtaExpand(tycon.typeParamSymbols)
}
- def unapply(tp: Type)(implicit ctx: Context): Option[TypeRef] = {
- def argsAreForwarders(args: List[Type], n: Int): Boolean = args match {
- case Nil =>
- n == 0
- case TypeRef(RefinedThis(rt), sel) :: args1 =>
- rt.eq(tp) && sel == tpnme.hkArg(n - 1) && argsAreForwarders(args1, n - 1)
- case _ =>
- false
- }
- tp match {
- case TypeLambda(_, argBounds, AppliedType(fn: TypeRef, args))
- if argsAreForwarders(args, tp.typeParams.length) => Some(fn)
- case _ => None
- }
+ def unapply(tp: Type)(implicit ctx: Context): Option[TypeRef] = tp match {
+ case tp @ TypeLambda(tparams, AppliedType(fn: TypeRef, args)) if (args == tparams.map(_.toArg)) => Some(fn)
+ case _ => None
}
}
@@ -124,94 +81,112 @@ object TypeApplications {
*
* T { type p_1 v_1= U_1; ...; type p_n v_n= U_n }
*
- * where v_i, p_i are the variances and names of the type parameters of T,
- * If `T`'s class symbol is a lambda trait, follow the refined type with a
- * projection
- *
- * T { ... } # $Apply
+ * where v_i, p_i are the variances and names of the type parameters of T.
*/
object AppliedType {
def apply(tp: Type, args: List[Type])(implicit ctx: Context): Type = tp.appliedTo(args)
def unapply(tp: Type)(implicit ctx: Context): Option[(Type, List[Type])] = tp match {
- case TypeRef(prefix, tpnme.hkApply) => unapp(prefix)
- case _ =>
- unapp(tp) match {
- case Some((tycon: TypeRef, _)) if tycon.symbol.isLambdaTrait =>
- // We are seeing part of a lambda abstraction, not an applied type
- None
- case x => x
+ case tp: RefinedType =>
+ var refinements: List[RefinedType] = Nil
+ var tycon = tp.stripTypeVar
+ while (tycon.isInstanceOf[RefinedType]) {
+ val rt = tycon.asInstanceOf[RefinedType]
+ refinements = rt :: refinements
+ tycon = rt.parent.stripTypeVar
}
- }
-
- private def unapp(tp: Type)(implicit ctx: Context): Option[(Type, List[Type])] = tp match {
- case _: RefinedType =>
- val tparams = tp.classSymbol.typeParams
- if (tparams.isEmpty) None
- else {
- val argBuf = new mutable.ListBuffer[Type]
- def stripArgs(tp: Type, n: Int): Type =
- if (n == 0) tp
- else tp match {
- case tp @ RefinedType(parent, pname) if pname == tparams(n - 1).name =>
- val res = stripArgs(parent, n - 1)
- if (res.exists) argBuf += tp.refinedInfo.argInfo
- res
- case _ =>
- NoType
- }
- val res = stripArgs(tp, tparams.length)
- if (res.exists) Some((res, argBuf.toList)) else None
+ def collectArgs(tparams: List[TypeParamInfo],
+ refinements: List[RefinedType],
+ argBuf: mutable.ListBuffer[Type]): Option[(Type, List[Type])] = refinements match {
+ case Nil if tparams.isEmpty && argBuf.nonEmpty =>
+ Some((tycon, argBuf.toList))
+ case RefinedType(_, rname, rinfo) :: refinements1
+ if tparams.nonEmpty && rname == tparams.head.paramName =>
+ collectArgs(tparams.tail, refinements1, argBuf += rinfo.argInfo)
+ case _ =>
+ None
}
- case _ => None
+ collectArgs(tycon.typeParams, refinements, new mutable.ListBuffer[Type])
+ case HKApply(tycon, args) =>
+ Some((tycon, args))
+ case _ =>
+ None
}
}
/** Adapt all arguments to possible higher-kinded type parameters using etaExpandIfHK
*/
- def etaExpandIfHK(tparams: List[Symbol], args: List[Type])(implicit ctx: Context): List[Type] =
+ def EtaExpandIfHK(tparams: List[TypeParamInfo], args: List[Type])(implicit ctx: Context): List[Type] =
if (tparams.isEmpty) args
- else args.zipWithConserve(tparams)((arg, tparam) => arg.etaExpandIfHK(tparam.infoOrCompleter))
+ else args.zipWithConserve(tparams)((arg, tparam) => arg.EtaExpandIfHK(tparam.paramBoundsOrCompleter))
- /** The references `<rt>.this.$hk0, ..., <rt>.this.$hk<n-1>`. */
- def argRefs(rt: RefinedType, n: Int)(implicit ctx: Context) =
- List.range(0, n).map(i => RefinedThis(rt).select(tpnme.hkArg(i)))
-
- /** Merge `tp1` and `tp2` under a common lambda, combining them with `op`.
- * @param tparams1 The type parameters of `tp1`
- * @param tparams2 The type parameters of `tp2`
- * @pre tparams1.length == tparams2.length
- * Produces the type lambda
+ /** A type map that tries to reduce (part of) the result type of the type lambda `tycon`
+ * with the given `args`(some of which are wildcard arguments represented by type bounds).
+ * Non-wildcard arguments are substituted everywhere as usual. A wildcard argument
+ * `>: L <: H` is substituted for a type lambda parameter `X` only under certain conditions.
*
- * [v1 X1 B1, ..., vn Xn Bn] -> op(tp1[X1, ..., Xn], tp2[X1, ..., Xn])
+ * 1. If Mode.AllowLambdaWildcardApply is set:
+ * The wildcard argument is substituted only if `X` appears in a toplevel refinement of the form
*
- * where
+ * { type A = X }
+ *
+ * and there are no other occurrences of `X` in the reduced type. In that case
+ * the refinement above is replaced by
+ *
+ * { type A >: L <: U }
+ *
+ * The `allReplaced` field indicates whether all occurrences of type lambda parameters
+ * in the reduced type have been replaced with arguments.
*
- * - variances `vi` are the variances of corresponding type parameters for `tp1`
- * or `tp2`, or are 0 of the latter disagree.
- * - bounds `Bi` are the intersection of the corresponding type parameter bounds
- * of `tp1` and `tp2`.
+ * 2. If Mode.AllowLambdaWildcardApply is not set:
+ * All refinements of the form
+ *
+ * { type A = X }
+ *
+ * are replaced by:
+ *
+ * { type A >: L <: U }
+ *
+ * Any other occurrence of `X` in `tycon` is replaced by `U`, if the
+ * occurrence of `X` in `tycon` is covariant, or nonvariant, or by `L`,
+ * if the occurrence is contravariant.
+ *
+ * The idea is that the `AllowLambdaWildcardApply` mode is used to check whether
+ * a type can be soundly reduced, and to give an error or warning if that
+ * is not the case. By contrast, the default mode, with `AllowLambdaWildcardApply`
+ * not set, reduces all applications even if this yields a different type, so
+ * its postcondition is that no type parameters of `tycon` appear in the
+ * result type. Using this mode, we can guarantee that `appliedTo` will never
+ * produce a higher-kinded application with a type lambda as type constructor.
*/
- def hkCombine(tp1: Type, tp2: Type,
- tparams1: List[TypeSymbol], tparams2: List[TypeSymbol], op: (Type, Type) => Type)
- (implicit ctx: Context): Type = {
- val variances = (tparams1, tparams2).zipped.map { (tparam1, tparam2) =>
- val v1 = tparam1.variance
- val v2 = tparam2.variance
- if (v1 == v2) v1 else 0
+ class Reducer(tycon: TypeLambda, args: List[Type])(implicit ctx: Context) extends TypeMap {
+ private var available = (0 until args.length).toSet
+ var allReplaced = true
+ def hasWildcardArg(p: PolyParam) =
+ p.binder == tycon && args(p.paramNum).isInstanceOf[TypeBounds]
+ def canReduceWildcard(p: PolyParam) =
+ !ctx.mode.is(Mode.AllowLambdaWildcardApply) || available.contains(p.paramNum)
+ def apply(t: Type) = t match {
+ case t @ TypeAlias(p: PolyParam) if hasWildcardArg(p) && canReduceWildcard(p) =>
+ available -= p.paramNum
+ args(p.paramNum)
+ case p: PolyParam if p.binder == tycon =>
+ args(p.paramNum) match {
+ case TypeBounds(lo, hi) =>
+ if (ctx.mode.is(Mode.AllowLambdaWildcardApply)) { allReplaced = false; p }
+ else if (variance < 0) lo
+ else hi
+ case arg =>
+ arg
+ }
+ case _: TypeBounds | _: HKApply =>
+ val saved = available
+ available = Set()
+ try mapOver(t)
+ finally available = saved
+ case _ =>
+ mapOver(t)
}
- val bounds: List[RefinedType => TypeBounds] =
- (tparams1, tparams2).zipped.map { (tparam1, tparam2) =>
- val b1: RefinedType => TypeBounds =
- tp1.memberInfo(tparam1).bounds.internalizeFrom(tparams1)
- val b2: RefinedType => TypeBounds =
- tp2.memberInfo(tparam2).bounds.internalizeFrom(tparams2)
- (rt: RefinedType) => b1(rt) & b2(rt)
- }
- val app1: RefinedType => Type = rt => tp1.appliedTo(argRefs(rt, tparams1.length))
- val app2: RefinedType => Type = rt => tp2.appliedTo(argRefs(rt, tparams2.length))
- val body: RefinedType => Type = rt => op(app1(rt), app2(rt))
- TypeLambda(variances, bounds, body)
}
}
@@ -233,38 +208,25 @@ class TypeApplications(val self: Type) extends AnyVal {
* with the bounds on its hk args. See `LambdaAbstract`, where these
* types get introduced, and see `isBoundedLambda` below for the test.
*/
- final def typeParams(implicit ctx: Context): List[TypeSymbol] = /*>|>*/ track("typeParams") /*<|<*/ {
+ final def typeParams(implicit ctx: Context): List[TypeParamInfo] = /*>|>*/ track("typeParams") /*<|<*/ {
self match {
case self: ClassInfo =>
self.cls.typeParams
+ case self: TypeLambda =>
+ self.typeParams
case self: TypeRef =>
val tsym = self.symbol
if (tsym.isClass) tsym.typeParams
- else tsym.infoOrCompleter match {
- case completer: TypeParamsCompleter =>
- val tparams = completer.completerTypeParams(tsym)
- defn.LambdaTrait(tparams.map(_.variance)).typeParams
- case _ =>
- if (!tsym.isCompleting || tsym.isAliasType) tsym.info.typeParams
- else
- // We are facing a problem when computing the type parameters of an uncompleted
- // abstract type. We can't access the bounds of the symbol yet because that
- // would cause a cause a cyclic reference. So we return `Nil` instead
- // and try to make up for it later. The acrobatics in Scala2Unpicker#readType
- // for reading a TypeRef show what's needed.
- Nil
- }
+ else if (!tsym.isCompleting) tsym.info.typeParams
+ else Nil
case self: RefinedType =>
- // inlined and optimized version of
- // val sym = self.LambdaTrait
- // if (sym.exists) return sym.typeParams
- if (self.refinedName == tpnme.hkApply) {
- val sym = self.parent.classSymbol
- if (sym.isLambdaTrait) return sym.typeParams
- }
- self.parent.typeParams.filterNot(_.name == self.refinedName)
- case self: SingletonType =>
+ self.parent.typeParams.filterNot(_.paramName == self.refinedName)
+ case self: RecType =>
+ self.parent.typeParams
+ case _: HKApply | _: SingletonType =>
Nil
+ case self: WildcardType =>
+ self.optBounds.typeParams
case self: TypeProxy =>
self.underlying.typeParams
case _ =>
@@ -272,6 +234,18 @@ class TypeApplications(val self: Type) extends AnyVal {
}
}
+ /** If `self` is a higher-kinded type, its type parameters, otherwise Nil */
+ final def hkTypeParams(implicit ctx: Context): List[TypeParamInfo] =
+ if (isHK) typeParams else Nil
+
+ /** If `self` is a generic class, its type parameter symbols, otherwise Nil */
+ final def typeParamSymbols(implicit ctx: Context): List[TypeSymbol] = typeParams match {
+ case (_: Symbol) :: _ =>
+ assert(typeParams.forall(_.isInstanceOf[Symbol]))
+ typeParams.asInstanceOf[List[TypeSymbol]]
+ case _ => Nil
+ }
+
/** The named type parameters declared or inherited by this type.
* These are all uninstantiated named type parameters of this type or one
* of its base types.
@@ -326,112 +300,61 @@ class TypeApplications(val self: Type) extends AnyVal {
if (params.exists(_.name == self.refinedName)) parent1
else self.derivedRefinedType(parent1, self.refinedName, self.refinedInfo)
case self: TypeProxy =>
- self.underlying.widenToNamedTypeParams(params)
+ self.superType.widenToNamedTypeParams(params)
case self: AndOrType =>
self.derivedAndOrType(
self.tp1.widenToNamedTypeParams(params), self.tp2.widenToNamedTypeParams(params))
}
}
- /** The Lambda trait underlying a type lambda */
- def LambdaTrait(implicit ctx: Context): Symbol = self.stripTypeVar match {
- case RefinedType(parent, tpnme.hkApply) =>
- val sym = self.classSymbol
- if (sym.isLambdaTrait) sym else NoSymbol
- case TypeBounds(lo, hi) => hi.LambdaTrait
- case _ => NoSymbol
- }
-
- /** Is receiver type higher-kinded (i.e. of kind != "*")? */
+ /** Is self type higher-kinded (i.e. of kind != "*")? */
def isHK(implicit ctx: Context): Boolean = self.dealias match {
case self: TypeRef => self.info.isHK
- case RefinedType(_, name) => name == tpnme.hkApply
- case TypeBounds(_, hi) => hi.isHK
- case _ => false
- }
-
- /** is receiver of the form T#$Apply? */
- def isHKApply: Boolean = self match {
- case TypeRef(_, name) => name == tpnme.hkApply
+ case self: RefinedType => false
+ case self: TypeLambda => true
+ case self: HKApply => false
+ case self: SingletonType => false
+ case self: TypeVar =>
+ // Using `origin` instead of `underlying`, as is done for typeParams,
+ // avoids having to set ephemeral in some cases.
+ self.origin.isHK
+ case self: WildcardType => self.optBounds.isHK
+ case self: TypeProxy => self.underlying.isHK
case _ => false
}
- /** True if it can be determined without forcing that the class symbol
- * of this application exists and is not a lambda trait.
- * Equivalent to
- *
- * self.classSymbol.exists && !self.classSymbol.isLambdaTrait
- *
- * but without forcing anything.
- */
- def classNotLambda(implicit ctx: Context): Boolean = self.stripTypeVar match {
- case self: RefinedType =>
- self.parent.classNotLambda
- case self: TypeRef =>
- self.denot.exists && {
- val sym = self.symbol
- if (sym.isClass) !sym.isLambdaTrait
- else sym.isCompleted && self.info.isAlias && self.info.bounds.hi.classNotLambda
- }
- case _ =>
- false
- }
-
- /** Dealias type if it can be done without forcing anything */
+ /** Dealias type if it can be done without forcing the TypeRef's info */
def safeDealias(implicit ctx: Context): Type = self match {
case self: TypeRef if self.denot.exists && self.symbol.isAliasType =>
- self.info.bounds.hi.stripTypeVar.safeDealias
+ self.superType.stripTypeVar.safeDealias
case _ =>
self
}
- /** Replace references to type parameters with references to hk arguments `this.$hk_i`
- * Care is needed not to cause cyclic reference errors, hence `SafeSubstMap`.
- */
- private[TypeApplications] def internalizeFrom[T <: Type](tparams: List[Symbol])(implicit ctx: Context): RefinedType => T =
- (rt: RefinedType) =>
- new ctx.SafeSubstMap(tparams , argRefs(rt, tparams.length))
- .apply(self).asInstanceOf[T]
-
/** Lambda abstract `self` with given type parameters. Examples:
*
* type T[X] = U becomes type T = [X] -> U
- * type T[X] >: L <: U becomes type T >: L <: ([X] -> _ <: U)
+ * type T[X] >: L <: U becomes type T >: L <: ([X] -> U)
+ *
+ * TODO: Handle parameterized lower bounds
*/
- def LambdaAbstract(tparams: List[Symbol])(implicit ctx: Context): Type = {
-
- /** Replace references to type parameters with references to hk arguments `this.$hk_i`
- * Care is needed not to cause cycles, hence `SafeSubstMap`.
- */
- def internalize[T <: Type](tp: T) =
- (rt: RefinedType) =>
- new ctx.SafeSubstMap(tparams, argRefs(rt, tparams.length))
- .apply(tp).asInstanceOf[T]
-
- def expand(tp: Type) = {
+ def LambdaAbstract(tparams: List[TypeParamInfo])(implicit ctx: Context): Type = {
+ def expand(tp: Type) =
TypeLambda(
- tparams.map(_.variance),
- tparams.map(tparam => internalize(self.memberInfo(tparam).bounds)),
- internalize(tp))
- }
+ tpnme.syntheticLambdaParamNames(tparams.length), tparams.map(_.paramVariance))(
+ tl => tparams.map(tparam => tl.lifted(tparams, tparam.paramBounds).bounds),
+ tl => tl.lifted(tparams, tp))
self match {
case self: TypeAlias =>
self.derivedTypeAlias(expand(self.alias))
case self @ TypeBounds(lo, hi) =>
- self.derivedTypeBounds(lo, expand(TypeBounds.upper(hi)))
+ self.derivedTypeBounds(
+ if (lo.isRef(defn.NothingClass)) lo else expand(lo),
+ expand(hi))
case _ => expand(self)
}
}
- /** A type ref is eta expandable if it refers to a non-lambda class.
- * In that case we can look for parameterized base types of the type
- * to eta expand them.
- */
- def isEtaExpandable(implicit ctx: Context) = self match {
- case self: TypeRef => self.symbol.isClass && !self.name.isLambdaTraitName
- case _ => false
- }
-
/** Convert a type constructor `TC` which has type parameters `T1, ..., Tn`
* in a context where type parameters `U1,...,Un` are expected to
*
@@ -443,23 +366,18 @@ class TypeApplications(val self: Type) extends AnyVal {
* v1 is compatible with v2, if v1 = v2 or v2 is non-variant.
*/
def EtaExpand(tparams: List[TypeSymbol])(implicit ctx: Context): Type = {
- val tparamsToUse = if (variancesConform(typeParams, tparams)) tparams else typeParams
+ val tparamsToUse = if (variancesConform(typeParams, tparams)) tparams else typeParamSymbols
self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparamsToUse)
//.ensuring(res => res.EtaReduce =:= self, s"res = $res, core = ${res.EtaReduce}, self = $self, hc = ${res.hashCode}")
}
- /** Eta expand the prefix in front of any refinements. */
- def EtaExpandCore(implicit ctx: Context): Type = self.stripTypeVar match {
- case self: RefinedType =>
- self.derivedRefinedType(self.parent.EtaExpandCore, self.refinedName, self.refinedInfo)
- case _ =>
- self.EtaExpand(self.typeParams)
- }
+ /** If self is not higher-kinded, eta expand it. */
+ def ensureHK(implicit ctx: Context): Type =
+ if (isHK) self else EtaExpansion(self)
/** Eta expand if `self` is a (non-lambda) class reference and `bound` is a higher-kinded type */
- def etaExpandIfHK(bound: Type)(implicit ctx: Context): Type = {
- val boundLambda = bound.LambdaTrait
- val hkParams = boundLambda.typeParams
+ def EtaExpandIfHK(bound: Type)(implicit ctx: Context): Type = {
+ val hkParams = bound.hkTypeParams
if (hkParams.isEmpty) self
else self match {
case self: TypeRef if self.symbol.isClass && self.typeParams.length == hkParams.length =>
@@ -500,21 +418,21 @@ class TypeApplications(val self: Type) extends AnyVal {
* is covariant is irrelevant, so can be ignored.
*/
def adaptHkVariances(bound: Type)(implicit ctx: Context): Type = {
- val boundLambda = bound.LambdaTrait
- val hkParams = boundLambda.typeParams
+ val hkParams = bound.hkTypeParams
if (hkParams.isEmpty) self
else {
def adaptArg(arg: Type): Type = arg match {
- case arg: TypeRef if arg.symbol.isLambdaTrait &&
- !arg.symbol.typeParams.corresponds(hkParams)(_.variance == _.variance) &&
- arg.symbol.typeParams.corresponds(hkParams)(varianceConforms) =>
- arg.prefix.select(boundLambda)
- case arg: RefinedType =>
- arg.derivedRefinedType(adaptArg(arg.parent), arg.refinedName, arg.refinedInfo)
+ case arg @ TypeLambda(tparams, body) if
+ !tparams.corresponds(hkParams)(_.paramVariance == _.paramVariance) &&
+ tparams.corresponds(hkParams)(varianceConforms) =>
+ TypeLambda(tparams.map(_.paramName), hkParams.map(_.paramVariance))(
+ tl => arg.paramBounds.map(_.subst(arg, tl).bounds),
+ tl => arg.resultType.subst(arg, tl)
+ )
case arg @ TypeAlias(alias) =>
arg.derivedTypeAlias(adaptArg(alias))
case arg @ TypeBounds(lo, hi) =>
- arg.derivedTypeBounds(lo, adaptArg(hi))
+ arg.derivedTypeBounds(adaptArg(lo), adaptArg(hi))
case _ =>
arg
}
@@ -522,99 +440,113 @@ class TypeApplications(val self: Type) extends AnyVal {
}
}
- /** Encode
+ /** The type representing
*
* T[U1, ..., Un]
*
* where
* @param self = `T`
* @param args = `U1,...,Un`
- * performing the following simplifications
- *
- * 1. If `T` is an eta expansion `[X1,..,Xn] -> C[X1,...,Xn]` of class `C` compute
- * `C[U1, ..., Un]` instead.
- * 2. If `T` is some other type lambda `[X1,...,Xn] -> S` none of the arguments
- * `U1,...,Un` is a wildcard, compute `[X1:=U1, ..., Xn:=Un]S` instead.
- * 3. If `T` is a polytype, instantiate it to `U1,...,Un`.
*/
final def appliedTo(args: List[Type])(implicit ctx: Context): Type = /*>|>*/ track("appliedTo") /*<|<*/ {
- def substHkArgs = new TypeMap {
- def apply(tp: Type): Type = tp match {
- case TypeRef(RefinedThis(rt), name) if rt.eq(self) && name.isHkArgName =>
- args(name.hkArgIndex)
- case _ =>
- mapOver(tp)
- }
- }
- if (args.isEmpty || ctx.erasedTypes) self
- else self.stripTypeVar match {
- case EtaExpansion(self1) =>
- self1.appliedTo(args)
- case TypeLambda(_, _, body) if !args.exists(_.isInstanceOf[TypeBounds]) =>
- substHkArgs(body)
- case self: PolyType =>
- self.instantiate(args)
- case self1 =>
- self1.safeDealias.appliedTo(args, typeParams)
- }
- }
-
- /** Encode application `T[U1, ..., Un]` without simplifications, where
- * @param self = `T`
- * @param args = `U1, ..., Un`
- * @param tparams are assumed to be the type parameters of `T`.
- */
- final def appliedTo(args: List[Type], typParams: List[TypeSymbol])(implicit ctx: Context): Type = {
- def matchParams(t: Type, tparams: List[TypeSymbol], args: List[Type])(implicit ctx: Context): Type = args match {
+ val typParams = self.typeParams
+ def matchParams(t: Type, tparams: List[TypeParamInfo], args: List[Type])(implicit ctx: Context): Type = args match {
case arg :: args1 =>
try {
val tparam :: tparams1 = tparams
- matchParams(RefinedType(t, tparam.name, arg.toBounds(tparam)), tparams1, args1)
+ matchParams(RefinedType(t, tparam.paramName, arg.toBounds(tparam)), tparams1, args1)
} catch {
case ex: MatchError =>
- println(s"applied type mismatch: $self $args, typeParams = $typParams") // !!! DEBUG
+ println(s"applied type mismatch: $self with underlying ${self.underlyingIfProxy}, args = $args, typeParams = $typParams") // !!! DEBUG
//println(s"precomplete decls = ${self.typeSymbol.unforcedDecls.toList.map(_.denot).mkString("\n ")}")
throw ex
}
case nil => t
}
- assert(args.nonEmpty)
- matchParams(self, typParams, args) match {
- case refined @ RefinedType(_, pname) if pname.isHkArgName =>
- TypeRef(refined, tpnme.hkApply)
- case refined =>
- refined
+ val stripped = self.stripTypeVar
+ val dealiased = stripped.safeDealias
+ if (args.isEmpty || ctx.erasedTypes) self
+ else dealiased match {
+ case dealiased: TypeLambda =>
+ def tryReduce =
+ if (!args.exists(_.isInstanceOf[TypeBounds])) {
+ val followAlias = stripped match {
+ case stripped: TypeRef =>
+ stripped.symbol.is(BaseTypeArg)
+ case _ =>
+ Config.simplifyApplications && {
+ dealiased.resType match {
+ case AppliedType(tyconBody, _) =>
+ variancesConform(typParams, tyconBody.typeParams)
+ // Reducing is safe for type inference, as kind of type constructor does not change
+ case _ => false
+ }
+ }
+ }
+ if ((dealiased eq stripped) || followAlias) dealiased.instantiate(args)
+ else HKApply(self, args)
+ }
+ else dealiased.resType match {
+ case AppliedType(tycon, args1) if tycon.safeDealias ne tycon =>
+ // In this case we should always dealias since we cannot handle
+ // higher-kinded applications to wildcard arguments.
+ dealiased
+ .derivedTypeLambda(resType = tycon.safeDealias.appliedTo(args1))
+ .appliedTo(args)
+ case _ =>
+ val reducer = new Reducer(dealiased, args)
+ val reduced = reducer(dealiased.resType)
+ if (reducer.allReplaced) reduced
+ else HKApply(dealiased, args)
+ }
+ tryReduce
+ case dealiased: PolyType =>
+ dealiased.instantiate(args)
+ case dealiased: AndOrType =>
+ dealiased.derivedAndOrType(dealiased.tp1.appliedTo(args), dealiased.tp2.appliedTo(args))
+ case dealiased: TypeAlias =>
+ dealiased.derivedTypeAlias(dealiased.alias.appliedTo(args))
+ case dealiased: TypeBounds =>
+ dealiased.derivedTypeBounds(dealiased.lo.appliedTo(args), dealiased.hi.appliedTo(args))
+ case dealiased: LazyRef =>
+ LazyRef(() => dealiased.ref.appliedTo(args))
+ case dealiased: WildcardType =>
+ dealiased
+ case dealiased: TypeRef if dealiased.symbol == defn.NothingClass =>
+ dealiased
+ case _ if typParams.isEmpty || typParams.head.isInstanceOf[LambdaParam] =>
+ HKApply(self, args)
+ case dealiased =>
+ matchParams(dealiased, typParams, args)
}
}
final def appliedTo(arg: Type)(implicit ctx: Context): Type = appliedTo(arg :: Nil)
final def appliedTo(arg1: Type, arg2: Type)(implicit ctx: Context): Type = appliedTo(arg1 :: arg2 :: Nil)
+ final def applyIfParameterized(args: List[Type])(implicit ctx: Context): Type =
+ if (typeParams.nonEmpty) appliedTo(args) else self
+
/** A cycle-safe version of `appliedTo` where computing type parameters do not force
* the typeconstructor. Instead, if the type constructor is completing, we make
* up hk type parameters matching the arguments. This is needed when unpickling
* Scala2 files such as `scala.collection.generic.Mapfactory`.
*/
- final def safeAppliedTo(args: List[Type])(implicit ctx: Context) = {
- val safeTypeParams = self match {
- case self: TypeRef if !self.symbol.isClass && self.symbol.isCompleting =>
- // This happens when unpickling e.g. scala$collection$generic$GenMapFactory$$CC
- ctx.warning(i"encountered F-bounded higher-kinded type parameters for ${self.symbol}; assuming they are invariant")
- defn.LambdaTrait(args map alwaysZero).typeParams
- case _ =>
- typeParams
- }
- appliedTo(args, safeTypeParams)
+ final def safeAppliedTo(args: List[Type])(implicit ctx: Context) = self match {
+ case self: TypeRef if !self.symbol.isClass && self.symbol.isCompleting =>
+ HKApply(self, args)
+ case _ =>
+ appliedTo(args)
}
/** Turn this type, which is used as an argument for
* type parameter `tparam`, into a TypeBounds RHS
*/
- final def toBounds(tparam: Symbol)(implicit ctx: Context): TypeBounds = self match {
+ final def toBounds(tparam: TypeParamInfo)(implicit ctx: Context): TypeBounds = self match {
case self: TypeBounds => // this can happen for wildcard args
self
case _ =>
- val v = tparam.variance
+ val v = tparam.paramVariance
/* Not neeeded.
if (v > 0 && !(tparam is Local) && !(tparam is ExpandedTypeParam)) TypeBounds.upper(self)
else if (v < 0 && !(tparam is Local) && !(tparam is ExpandedTypeParam)) TypeBounds.lower(self)
@@ -628,7 +560,11 @@ class TypeApplications(val self: Type) extends AnyVal {
*/
final def baseArgInfos(base: Symbol)(implicit ctx: Context): List[Type] =
if (self derivesFrom base)
- base.typeParams map (param => self.member(param.name).info.argInfo)
+ self.dealias match {
+ case self: TypeRef if !self.symbol.isClass => self.superType.baseArgInfos(base)
+ case self: HKApply => self.superType.baseArgInfos(base)
+ case _ => base.typeParams.map(param => self.member(param.name).info.argInfo)
+ }
else
Nil
@@ -650,14 +586,6 @@ class TypeApplications(val self: Type) extends AnyVal {
final def baseArgTypesHi(base: Symbol)(implicit ctx: Context): List[Type] =
baseArgInfos(base) mapConserve boundsToHi
- /** The first type argument of the base type instance wrt `base` of this type */
- final def firstBaseArgInfo(base: Symbol)(implicit ctx: Context): Type = base.typeParams match {
- case param :: _ if self derivesFrom base =>
- self.member(param.name).info.argInfo
- case _ =>
- NoType
- }
-
/** The base type including all type arguments and applicable refinements
* of this type. Refinements are applicable if they refine a member of
* the parent type which furthermore is not a name-mangled type parameter.
@@ -671,10 +599,12 @@ class TypeApplications(val self: Type) extends AnyVal {
case TypeBounds(_, hi) => hi.baseTypeWithArgs(base)
case _ => default
}
- case tp @ RefinedType(parent, name) if !tp.member(name).symbol.is(ExpandedTypeParam) =>
+ case tp @ RefinedType(parent, name, _) if !tp.member(name).symbol.is(ExpandedTypeParam) =>
tp.wrapIfMember(parent.baseTypeWithArgs(base))
case tp: TermRef =>
tp.underlying.baseTypeWithArgs(base)
+ case tp: HKApply =>
+ tp.superType.baseTypeWithArgs(base)
case AndType(tp1, tp2) =>
tp1.baseTypeWithArgs(base) & tp2.baseTypeWithArgs(base)
case OrType(tp1, tp2) =>
@@ -729,17 +659,16 @@ class TypeApplications(val self: Type) extends AnyVal {
/** The core type without any type arguments.
* @param `typeArgs` must be the type arguments of this type.
*/
- final def withoutArgs(typeArgs: List[Type]): Type = typeArgs match {
- case _ :: typeArgs1 =>
- val RefinedType(tycon, _) = self
- tycon.withoutArgs(typeArgs1)
- case nil =>
- self
- }
-
- final def typeConstructor(implicit ctx: Context): Type = self.stripTypeVar match {
- case AppliedType(tycon, _) => tycon
- case self => self
+ final def withoutArgs(typeArgs: List[Type]): Type = self match {
+ case HKApply(tycon, args) => tycon
+ case _ =>
+ typeArgs match {
+ case _ :: typeArgs1 =>
+ val RefinedType(tycon, _, _) = self
+ tycon.withoutArgs(typeArgs1)
+ case nil =>
+ self
+ }
}
/** If this is the image of a type argument; recover the type argument,
@@ -761,33 +690,6 @@ class TypeApplications(val self: Type) extends AnyVal {
def elemType(implicit ctx: Context): Type = self match {
case defn.ArrayOf(elemtp) => elemtp
case JavaArrayType(elemtp) => elemtp
- case _ => firstBaseArgInfo(defn.SeqClass)
- }
-
- /** Does this type contain RefinedThis type with `target` as its underling
- * refinement type?
- */
- def containsRefinedThis(target: Type)(implicit ctx: Context): Boolean = {
- def recur(tp: Type): Boolean = tp.stripTypeVar match {
- case RefinedThis(tp) =>
- tp eq target
- case tp: NamedType =>
- if (tp.symbol.isClass) !tp.symbol.isStatic && recur(tp.prefix)
- else tp.info match {
- case TypeAlias(alias) => recur(alias)
- case _ => recur(tp.prefix)
- }
- case tp: RefinedType =>
- recur(tp.refinedInfo) || recur(tp.parent)
- case tp: TypeBounds =>
- recur(tp.lo) || recur(tp.hi)
- case tp: AnnotatedType =>
- recur(tp.underlying)
- case tp: AndOrType =>
- recur(tp.tp1) || recur(tp.tp2)
- case _ =>
- false
- }
- recur(self)
+ case _ => baseArgInfos(defn.SeqClass).headOption.getOrElse(NoType)
}
}