diff options
Diffstat (limited to 'src/dotty/tools/dotc/core')
-rw-r--r-- | src/dotty/tools/dotc/core/TypeApplications.scala | 407 |
1 files changed, 107 insertions, 300 deletions
diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index ab15b3e1a..684e83633 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -107,12 +107,6 @@ object TypeApplications { tp match { case TypeLambda(_, argBounds, AppliedType(fn: TypeRef, args)) if argsAreForwarders(args, tp.typeParams.length) => Some(fn) - //case TypeLambda(_, argBounds, AppliedType(fn: TypeRef, args)) => - // println(i"eta expansion failed because args $args are not forwarders for ${tp.toString}") - // None - //case TypeLambda(_, argBounds, _) => - // println(i"eta expansion failed because body is not applied type") - // None case _ => None } } @@ -210,9 +204,14 @@ class TypeApplications(val self: Type) extends AnyVal { Nil else tsym.info.typeParams case self: RefinedType => - val hkParams = self.hkTypeParams - if (hkParams.nonEmpty) hkParams - else self.parent.typeParams.filterNot(_.name == self.refinedName) + // 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 => Nil case self: TypeProxy => @@ -222,16 +221,6 @@ class TypeApplications(val self: Type) extends AnyVal { } } - /** The higherkinded type parameters in case this is a type lambda - * - * [X1, ..., Xn] -> T - * - * These are the parameters of the underlying lambda class. - * Returns `Nil` for all other types. - */ - final def hkTypeParams(implicit ctx: Context): List[TypeSymbol] = - self.LambdaTrait.typeParams - /** The Lambda trait underlying a type lambda */ def LambdaTrait(implicit ctx: Context): Symbol = self.stripTypeVar match { case RefinedType(parent, tpnme.hkApply) => @@ -241,25 +230,56 @@ class TypeApplications(val self: Type) extends AnyVal { case _ => NoSymbol } - /** A type ref is eta expandable if it refers to a non-lambda class. - * In that case we can look for parameterized base types fo the type - * to eta expand them. - */ - def isEtaExpandable(implicit ctx: Context) = self match { - case self: TypeRef => self.symbol.isClass && !self.name.isLambdaTraitName + /** Is receiver 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 _ => 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 + } + /** 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) */ 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) = { TypeLambda( tparams.map(_.variance), @@ -274,15 +294,41 @@ class TypeApplications(val self: Type) extends AnyVal { case _ => expand(self) } } -/* - def betaReduce(implicit ctx: Context): Type = self.stripTypeVar match { - case TypeRef(prefix, tpnme.hkApply) => - prefix.betaReduce - case self @ RefinedType(parent, tpnme.hkArg) if parent.isTypeLambda => - HKApplication(parent, self.refinedInfo.dropAlias) + + /** 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 + * + * LambdaXYZ { Apply = TC[hk$0, ..., hk$n] } + * + * Here, XYZ corresponds to the variances of + * - `U1,...,Un` if the variances of `T1,...,Tn` are pairwise compatible with `U1,...,Un`, + * - `T1,...,Tn` otherwise. + * 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 + 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) } -*/ - /** Adapt argument A to type parameter P in the case P is higher-kinded. + + /** Adapt argument A to type parameter P in the case P is higher-kinded. * This means: * (1) Make sure that A is a type lambda, if necessary by eta-expanding it. * (2) Make sure the variances of the type lambda @@ -331,77 +377,21 @@ class TypeApplications(val self: Type) extends AnyVal { } } - /* - /** If type `self` is equal, aliased-to, or upperbounded-by a type of the form - * `LambdaXYZ { ... }`, the class symbol of that type, otherwise NoSymbol. - * symbol of that type, otherwise NoSymbol. - * @param forcing if set, might force completion. If not, never forces - * but returns NoSymbol when it would have to otherwise. - */ - def LambdaClass(forcing: Boolean)(implicit ctx: Context): Symbol = track("LambdaClass") { self.stripTypeVar match { - case self: TypeRef => - val sym = self.symbol - if (sym.isLambdaTrait) sym - else if (sym.isClass || sym.isCompleting && !forcing) NoSymbol - else self.info.LambdaClass(forcing) - case self: TypeProxy => - self.underlying.LambdaClass(forcing) - case _ => - NoSymbol - }} - - /** Is type `self` equal, aliased-to, or upperbounded-by a type of the form - * `LambdaXYZ { ... }`? - */ - def isLambda(implicit ctx: Context): Boolean = - LambdaClass(forcing = true).exists - - /** Same is `isLambda`, except that symbol denotations are not forced - * Symbols in completion count as not lambdas. - */ - def isSafeLambda(implicit ctx: Context): Boolean = - LambdaClass(forcing = false).exists - - /** Is type `self` a Lambda with all hk$i fields fully instantiated? */ - def isInstantiatedLambda(implicit ctx: Context): Boolean = - isSafeLambda && typeParams.isEmpty -*/ - /** Is receiver 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 _ => 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 + /** Encode * - * self.classSymbol.exists && !self.classSymbol.isLambdaTrait + * T[U1, ..., Un] * - * but without forcing anything. + * 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`. */ - 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 - } - - /** Encode the type resulting from applying this type to given arguments */ final def appliedTo(args: List[Type])(implicit ctx: Context): Type = /*>|>*/ track("appliedTo") /*<|<*/ { def substHkArgs = new TypeMap { def apply(tp: Type): Type = tp match { @@ -424,7 +414,12 @@ class TypeApplications(val self: Type) extends AnyVal { } } - def appliedTo(args: List[Type], typParams: List[TypeSymbol])(implicit ctx: Context): Type = { + /** 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 { case arg :: args1 => try { @@ -447,10 +442,14 @@ class TypeApplications(val self: Type) extends AnyVal { } } - 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) + /** 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 => @@ -462,18 +461,6 @@ class TypeApplications(val self: Type) extends AnyVal { } appliedTo(args, safeTypeParams) } -/* - /** Simplify a fully instantiated type of the form `LambdaX{... type Apply = T } # Apply` to `T`. - */ - def simplifyApply(implicit ctx: Context): Type = self match { - case self @ TypeRef(prefix, tpnme.hkApply) if prefix.isInstantiatedLambda => - prefix.member(tpnme.hkApply).info match { - case TypeAlias(alias) => alias - case _ => self - } - case _ => self - } -*/ /** Turn this type, which is used as an argument for * type parameter `tparam`, into a TypeBounds RHS @@ -488,7 +475,7 @@ class TypeApplications(val self: Type) extends AnyVal { else TypeAlias(self, v) } - /** The type arguments of this type's base type instance wrt.`base`. + /** The type arguments of this type's base type instance wrt. `base`. * Existential types in arguments are returned as TypeBounds instances. */ final def baseArgInfos(base: Symbol)(implicit ctx: Context): List[Type] = @@ -616,6 +603,7 @@ class TypeApplications(val self: Type) extends AnyVal { case _ => NoType } + /** If this is a type alias, its underlying type, otherwise the type itself */ def dropAlias(implicit ctx: Context): Type = self match { case TypeAlias(alias) => alias case _ => self @@ -628,6 +616,9 @@ class TypeApplications(val self: Type) extends AnyVal { 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) => @@ -651,188 +642,4 @@ class TypeApplications(val self: Type) extends AnyVal { } recur(self) } -/* - /** The typed lambda abstraction of this type `T` relative to `boundSyms`. - * This is: - * - * LambdaXYZ{ bounds }{ type Apply = toHK(T) } - * - * where - * - XYZ reflects the variances of the bound symbols, - * - `bounds` consists of type declarations `type hk$i >: toHK(L) <: toHK(U), - * one for each type parameter in `T` with non-trivial bounds L,U. - * - `toHK` is a substitution that replaces every bound symbol sym_i by - * `this.hk$i`. - * - * TypeBounds are lambda abstracting by lambda abstracting their upper bound. - * - * @param cycleParanoid If `true` don't force denotation of a TypeRef unless - * its name matches one of `boundSyms`. Needed to avoid cycles - * involving F-boundes hk-types when reading Scala2 collection classes - * with new hk-scheme. - */ - def LambdaAbstract(boundSyms: List[Symbol], cycleParanoid: Boolean = false)(implicit ctx: Context): Type = { - def expand(tp: Type): Type = { - val lambda = defn.LambdaTrait(boundSyms.map(_.variance)) - def toHK(tp: Type) = (rt: RefinedType) => { - val argRefs = boundSyms.indices.toList.map(i => - RefinedThis(rt).select(tpnme.hkArg(i))) - val substituted = - if (cycleParanoid) new ctx.SafeSubstMap(boundSyms, argRefs).apply(tp) - else tp.subst(boundSyms, argRefs) - substituted.bounds.withVariance(1) - } - val boundNames = new mutable.ListBuffer[Name] - val boundss = new mutable.ListBuffer[TypeBounds] - for (sym <- boundSyms) { - val bounds = sym.info.bounds - if (!(TypeBounds.empty frozen_<:< bounds)) { - boundNames += sym.name - boundss += bounds - } - } - val lambdaWithBounds = - RefinedType.make(lambda.typeRef, boundNames.toList, boundss.toList.map(toHK)) - RefinedType(lambdaWithBounds, tpnme.hkApply, toHK(tp)) - } - self match { - case self @ TypeBounds(lo, hi) => - self.derivedTypeBounds(lo, expand(TypeBounds.upper(hi))) - case _ => - expand(self) - } - } -*/ - /** Convert a type constructor `TC` which has type parameters `T1, ..., Tn` - * in a context where type parameters `U1,...,Un` are expected to - * - * LambdaXYZ { Apply = TC[hk$0, ..., hk$n] } - * - * Here, XYZ corresponds to the variances of - * - `U1,...,Un` if the variances of `T1,...,Tn` are pairwise compatible with `U1,...,Un`, - * - `T1,...,Tn` otherwise. - * 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 - 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 if `bound` is a higher-kinded type */ - def EtaExpandIfHK(bound: Type)(implicit ctx: Context): Type = - if (bound.isHK && !isHK && self.typeSymbol.isClass && typeParams.nonEmpty) EtaExpand(bound.typeParams) - else self -*/ - /** 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 a (potentially partially instantiated) eta expansion of type T, return T, - * otherwise NoType. More precisely if `self` is of the form - * - * T { type $apply = U[T1, ..., Tn] } - * - * where - * - * - hk$0, ..., hk${m-1} are the type parameters of T - * - a sublist of the arguments Ti_k (k = 0,...,m_1) are of the form T{...}.this.hk$i_k - * - * rewrite `self` to - * - * U[T'1,...T'j] - * - * where - * - * T'j = _ >: Lj <: Uj if j is in the i_k list defined above - * where Lj and Uj are the bounds of hk$j mapped using `fromHK`. - * = fromHK(Tj) otherwise. - * - * `fromHK` is the function that replaces every occurrence of `<self>.this.hk$i` by the - * corresponding parameter reference in `U[T'1,...T'j]` - */ - def EtaReduce(implicit ctx: Context): Type = { - def etaCore(tp: Type, tparams: List[Symbol]): Type = tparams match { - case Nil => tp - case tparam :: otherParams => - tp match { - case tp: RefinedType => - tp.refinedInfo match { - case TypeAlias(TypeRef(RefinedThis(rt), rname)) - if (rname == tparam.name) && (rt eq self) => - // we have a binding T = Lambda$XYZ{...}.this.hk$i where hk$i names the current `tparam`. - val pcore = etaCore(tp.parent, otherParams) - val hkBounds = self.member(rname).info.bounds - if (TypeBounds.empty frozen_<:< hkBounds) pcore - else tp.derivedRefinedType(pcore, tp.refinedName, hkBounds) - case _ => - val pcore = etaCore(tp.parent, tparams) - if (pcore.exists) tp.derivedRefinedType(pcore, tp.refinedName, tp.refinedInfo) - else NoType - } - case _ => - NoType - } - } - // Map references `Lambda$XYZ{...}.this.hk$i to corresponding parameter references of the reduced core. - def fromHK(reduced: Type) = reduced match { - case reduced: RefinedType => - new TypeMap { - def apply(tp: Type): Type = tp match { - case TypeRef(RefinedThis(binder), name) if binder eq self => - assert(name.isHkArgName) - RefinedThis(reduced).select(reduced.typeParams.apply(name.hkArgIndex)) - case _ => - mapOver(tp) - } - }.apply(reduced) - case _ => - reduced - } - - self match { - case self @ RefinedType(parent, tpnme.hkApply) => - val lc = parent.LambdaClass(forcing = false) - self.refinedInfo match { - case TypeAlias(alias) if lc.exists => - fromHK(etaCore(alias, lc.typeParams.reverse)) - case _ => NoType - } - case _ => NoType - } - } - - /** Test whether this type has a base type of the form `B[T1, ..., Tn]` where - * the type parameters of `B` match one-by-one the variances of `tparams`, - * and where the lambda abstracted type - * - * LambdaXYZ { type Apply = B[hk$0, ..., hk${n-1}] } - * { type hk$0 = T1; ...; type hk${n-1} = Tn } - * - * satisfies predicate `p`. Try base types in the order of their occurrence in `baseClasses`. - * A type parameter matches a variance V if it has V as its variance or if V == 0. - * @param classBounds A hint to bound the search. Only types that derive from one of the - * classes in classBounds are considered. - */ - def testLifted(tparams: List[Symbol], p: Type => Boolean, classBounds: List[ClassSymbol] = Nil)(implicit ctx: Context): Boolean = { - def recur(bcs: List[ClassSymbol]): Boolean = bcs match { - case bc :: bcs1 => - val baseRef = self.baseTypeRef(bc) - def variancesMatch(param1: Symbol, param2: Symbol) = - param2.variance == param2.variance || param2.variance == 0 - (classBounds.exists(bc.derivesFrom) && - baseRef.typeParams.corresponds(tparams)(variancesMatch) && - p(baseRef.appliedTo(self.baseArgInfos(bc))) - || - recur(bcs1)) - case nil => - false - } - classBounds.nonEmpty && recur(self.baseClasses) - } - */ } |