diff options
author | Martin Odersky <odersky@gmail.com> | 2015-12-02 13:13:19 +0100 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2015-12-06 16:09:20 +0100 |
commit | 7eeb5657497536ebafd7bbf80ea74fbc805a3b77 (patch) | |
tree | ae0640458790440e484a436af7d7d6e1deb5821e /src/dotty/tools/dotc/core/TypeApplications.scala | |
parent | f89b5628a66442edcc8c31193a559c6b5c32e837 (diff) | |
download | dotty-7eeb5657497536ebafd7bbf80ea74fbc805a3b77.tar.gz dotty-7eeb5657497536ebafd7bbf80ea74fbc805a3b77.tar.bz2 dotty-7eeb5657497536ebafd7bbf80ea74fbc805a3b77.zip |
Extractors and other new functionality for type applications
Diffstat (limited to 'src/dotty/tools/dotc/core/TypeApplications.scala')
-rw-r--r-- | src/dotty/tools/dotc/core/TypeApplications.scala | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 8417620f1..3ce1c1cc4 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -36,6 +36,140 @@ object TypeApplications { case tp: TypeBounds => tp.hi case _ => tp } + + /** 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 } + */ + object TypeLambda { + def apply(variances: List[Int], + argBoundss: List[TypeBounds], + bodyFn: RefinedType => Type)(implicit ctx: Context): Type = { + def argRefinements(parent: Type, i: Int, bs: List[TypeBounds]): Type = bs match { + case b :: bs1 => + argRefinements(RefinedType(parent, tpnme.hkArg(i), b), i + 1, bs1) + case nil => + parent + } + RefinedType( + argRefinements(defn.LambdaTrait(variances).typeRef, 0, argBoundss), + tpnme.hkApply, rt => bodyFn(rt).bounds) + } + + def unapply(tp: Type)(implicit ctx: Context): Option[(List[Int], List[TypeBounds], Type)] = tp match { + case app @ RefinedType(prefix, tpnme.hkApply) => + val cls = prefix.classSymbol + val variances = cls.typeParams.map(_.variance) + val argBounds = prefix.argInfos.map(_.bounds) + Some((variances, argBounds, app.refinedInfo)) + case TypeBounds(lo, hi) => + unapply(hi) + case _ => + None + } + } + + /** Extractor for + * + * [v1 X1: B1, ..., vn Xn: Bn] -> C[X1, ..., Xn] + * + * where v1, ..., vn and B1, ..., Bn are the variances and bounds of the type parameters + * of the class C. + * + * @param tycon C + */ + object EtaExpansion { + def apply(tycon: TypeRef)(implicit ctx: Context) = { + assert(tycon.isEtaExpandable) + val tparams = tycon.typeParams + val variances = tycon.typeParams.map(_.variance) + TypeLambda(tparams.map(_.variance), tycon.paramBounds, + rt => tycon.appliedTo(tparams.map(RefinedThis(rt).select(_)))) + } + + def unapply(tp: Type)(implicit ctx: Context): Option[TypeRef] = { + def argsAreForwarders(args: List[Type], n: Int): Boolean = args match { + case TypeRef(RefinedThis(rt), sel) :: args1 => + rt.eq(tp) && sel == tpnme.hkArg(n) && argsAreForwarders(args1, n - 1) + case nil => + n == 0 + } + tp match { + case TypeLambda(_, argBounds, AppliedType(fn: TypeRef, args)) + if argsAreForwarders(args, tp.typeParams.length - 1) => Some(fn) + case _ => None + } + } + } + + /** Extractor for type application T[U_1, ..., U_n]. This is the refined type + * + * 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 + */ + object AppliedType { + def apply(tp: Type, args: List[Type])(implicit ctx: Context): Type = { + def matchParams(tp: Type, tparams: List[TypeSymbol], args: List[Type]): Type = args match { + case arg :: args1 => + try { + val tparam :: tparams1 = tparams + matchParams(RefinedType(tp, tparam.name, arg.toBounds(tparam)), tparams1, args1) + } catch { + case ex: MatchError => + println(s"applied type mismatch: $tp $args, typeParams = ${tp.typeSymbol.typeParams}") // !!! DEBUG + println(s"precomplete decls = ${tp.typeSymbol.unforcedDecls.toList.map(_.denot).mkString("\n ")}") + throw ex + } + case nil => tp + } + assert(args.nonEmpty) + val cls = tp.classSymbol + val refined = matchParams(tp, cls.typeParams, args) + if (cls.isLambdaTrait) TypeRef(refined, tpnme.hkApply) else refined + } + + def unapply(tp: Type)(implicit ctx: Context): Option[(Type, List[Type])] = tp match { + case TypeRef(prefix, tpnme.hkApply) => unapp(prefix) + case _ => unapp(tp) + } + + private def unapp(tp: Type)(implicit ctx: Context): Option[(Type, List[Type])] = tp match { + case _: RefinedType => + val tparams = tp.typeSymbol.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 + } + case _ => None + } + } + + /** Adapt all arguments to possible higher-kinded type parameters using adaptIfHK + */ + def adaptArgs(tparams: List[Symbol], args: List[Type])(implicit ctx: Context): List[Type] = + args.zipWithConserve(tparams)((arg, tparam) => arg.adaptIfHK(tparam.infoOrCompleter)) + + def argRefs(rt: RefinedType, n: Int)(implicit ctx: Context) = + List.range(0, n).map(i => RefinedThis(rt).select(tpnme.hkArg(i))) } import TypeApplications._ @@ -88,6 +222,77 @@ class TypeApplications(val self: Type) extends AnyVal { * Third, it won't return abstract higher-kinded type parameters, i.e. the type parameters of * an abstract type are always empty. */ + final def hkTypeParams(implicit ctx: Context): List[TypeSymbol] = self match { + case TypeLambda(_, _, body) => self.typeSymbol.typeParams.head :: body.hkTypeParams + case TypeBounds(lo, hi) => hi.hkTypeParams + case _ => Nil + } + + final def paramBounds(implicit ctx: Context): List[TypeBounds] = + typeParams.map(self.memberInfo(_).bounds) + + def LambdaTrait(implicit ctx: Context) = { + def skipArgs(tp: Type): Type = tp match { + case RefinedType(parent, pname) if pname.isHkArgName => skipArgs(parent) + case _ => tp + } + self.stripTypeVar match { + case RefinedType(parent, tpnme.hkApply) => skipArgs(parent).stripTypeVar match { + case ref @ TypeRef(_, lam) if lam.isLambdaTraitName => ref.symbol + case _ => NoSymbol + } + case _ => NoSymbol + } + } + + def isEtaExpandable(implicit ctx: Context) = self match { + case self: TypeRef => self.symbol.isClass && !self.name.isLambdaTraitName + case _ => false + } + + /** 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 + * agrees with variances of corresponding higherkinded type parameters. Example: + * + * class Companion[+CC[X]] + * Companion[List] + * + * with adaptArgs, this will expand to + * + * Companion[[X] => List[X]] + * + * instead of + * + * Companion[[+X] => List[X]] + * + * even though `List` is covariant. This adaptation is necessary to ignore conflicting + * variances in overriding members that have types of hk-type parameters such as `Companion[GenTraversable]` + * or `Companion[ListBuffer]`. Without the adaptation we would end up with + * + * Companion[[+X] => GenTraversable[X]] + * Companion[[X] => List[X]] + * + * and the second is not a subtype of the first. So if we have overridding memebrs of the two + * types we get an error. + */ + def adaptIfHK(bound: Type)(implicit ctx: Context): Type = { + val boundLambda = bound.LambdaTrait + val hkParams = boundLambda.typeParams + if (hkParams.isEmpty) self + else self match { + case self: TypeRef if self.symbol.isClass && self.typeParams.length == hkParams.length => + EtaExpansion(self).adaptIfHK(bound) + case arg: TypeRef + if arg.symbol.isLambdaTrait && + !arg.symbol.typeParams.corresponds(boundLambda.typeParams)(_.variance == _.variance) => + arg.prefix.select(boundLambda) + case _ => + self + } + } + final def rawTypeParams(implicit ctx: Context): List[TypeSymbol] = { self match { case self: ClassInfo => |