aboutsummaryrefslogtreecommitdiff
path: root/src/dotty
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2015-11-11 18:19:28 +0100
committerMartin Odersky <odersky@gmail.com>2015-11-17 13:49:53 +0100
commitd53da9502fe0cb98ed5f8dfe64fbc6e62c0e3368 (patch)
tree0ee14895c030b210be039e27649b790c674e660a /src/dotty
parent6b061b5b94fb4e13a97274b28e0ab9ac78b69f27 (diff)
downloaddotty-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')
-rw-r--r--src/dotty/tools/dotc/core/TypeApplications.scala25
-rw-r--r--src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala49
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 =>