diff options
Diffstat (limited to 'src/dotty/tools/dotc/core/TypeApplications.scala')
-rw-r--r-- | src/dotty/tools/dotc/core/TypeApplications.scala | 616 |
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) } } |