diff options
author | Martin Odersky <odersky@gmail.com> | 2015-11-11 18:19:28 +0100 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2015-11-17 13:49:53 +0100 |
commit | d53da9502fe0cb98ed5f8dfe64fbc6e62c0e3368 (patch) | |
tree | 0ee14895c030b210be039e27649b790c674e660a /src/dotty/tools | |
parent | 6b061b5b94fb4e13a97274b28e0ab9ac78b69f27 (diff) | |
download | dotty-d53da9502fe0cb98ed5f8dfe64fbc6e62c0e3368.tar.gz dotty-d53da9502fe0cb98ed5f8dfe64fbc6e62c0e3368.tar.bz2 dotty-d53da9502fe0cb98ed5f8dfe64fbc6e62c0e3368.zip |
Eta expand according to expected type parameter variance
When eta expanding a type `C` to `[vX] => C[X]` the variance `v`
is now the variance of the expected type, not the variance of the
type constructor `C`, as long as the variance of the expected type
is compatible with the variance of `C`.
Diffstat (limited to 'src/dotty/tools')
-rw-r--r-- | src/dotty/tools/dotc/core/TypeApplications.scala | 25 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala | 49 |
2 files changed, 63 insertions, 11 deletions
diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 2631e9964..be070dace 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -524,21 +524,28 @@ class TypeApplications(val self: Type) extends AnyVal { } } - /** Convert a type constructor `TC` with type parameters `T1, ..., Tn` to + /** 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] } * - * where XYZ is a corresponds to the variances of the type parameters. + * 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(implicit ctx: Context): Type = { - val tparams = typeParams - self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparams) + def EtaExpand(tparams: List[Symbol])(implicit ctx: Context): Type = { + def varianceCompatible(actual: Symbol, formal: Symbol) = + formal.variance == 0 || actual.variance == formal.variance + val tparamsToUse = + if (typeParams.corresponds(tparams)(varianceCompatible)) 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 + if (bound.isHK && !isHK && self.typeSymbol.isClass && typeParams.nonEmpty) EtaExpand(bound.typeParams) else self /** Eta expand the prefix in front of any refinements. */ @@ -546,7 +553,7 @@ class TypeApplications(val self: Type) extends AnyVal { case self: RefinedType => self.derivedRefinedType(self.parent.EtaExpandCore, self.refinedName, self.refinedInfo) case _ => - self.EtaExpand + self.EtaExpand(self.typeParams) } /** If `self` is a (potentially partially instantiated) eta expansion of type T, return T, @@ -645,7 +652,7 @@ class TypeApplications(val self: Type) extends AnyVal { param2.variance == param2.variance || param2.variance == 0 if (classBounds.exists(tycon.derivesFrom(_)) && tycon.typeParams.corresponds(tparams)(variancesMatch)) { - val expanded = tycon.EtaExpand + val expanded = tycon.EtaExpand(tparams) val lifted = (expanded /: targs) { (partialInst, targ) => val tparam = partialInst.typeParams.head RefinedType(partialInst, tparam.name, targ.bounds.withVariance(tparam.variance)) @@ -659,7 +666,7 @@ class TypeApplications(val self: Type) extends AnyVal { false } tparams.nonEmpty && - (typeParams.nonEmpty && p(EtaExpand) || + (typeParams.hasSameLengthAs(tparams) && p(EtaExpand(tparams)) || classBounds.nonEmpty && tryLift(self.baseClasses)) } } diff --git a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 321846de6..8f4b47058 100644 --- a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -151,6 +151,51 @@ object Scala2Unpickler { denot.info = ClassInfo( // final info denot.owner.thisType, denot.classSymbol, parentRefs, declsInRightOrder, ost) } + + /** Adapt arguments to type parameters so that variance of type lambda arguments + * agrees with variance 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 adaptArgs(tparams: List[Symbol], args: List[Type])(implicit ctx: Context): List[Type] = tparams match { + case tparam :: tparams1 => + val boundLambda = tparam.infoOrCompleter match { + case TypeBounds(_, hi) => hi.LambdaClass(forcing = false) + case _ => NoSymbol + } + def adaptArg(arg: Type): Type = arg match { + case arg: TypeRef if arg.symbol.isLambdaTrait => + assert(arg.symbol.typeParams.length == boundLambda.typeParams.length) + arg.prefix.select(boundLambda) + case arg: RefinedType => + arg.derivedRefinedType(adaptArg(arg.parent), arg.refinedName, arg.refinedInfo) + case _ => + arg + } + val arg = args.head + val adapted = if (boundLambda.exists) adaptArg(arg) else arg + adapted :: adaptArgs(tparams1, args.tail) + case nil => args + } } /** Unpickle symbol table information descending from a class and/or module root @@ -723,8 +768,8 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas else TypeRef(pre, sym.name.asTypeName) val args = until(end, readTypeRef) if (sym == defn.ByNameParamClass2x) ExprType(args.head) - else if (args.isEmpty && sym.typeParams.nonEmpty) tycon.EtaExpand - else tycon.appliedTo(args) + else if (args.isEmpty && sym.typeParams.nonEmpty) tycon.EtaExpand(sym.typeParams) + else tycon.appliedTo(adaptArgs(sym.typeParams, args)) case TYPEBOUNDStpe => TypeBounds(readTypeRef(), readTypeRef()) case REFINEDtpe => |