aboutsummaryrefslogtreecommitdiff
path: root/src/dotty
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2015-12-13 13:21:54 +0100
committerMartin Odersky <odersky@gmail.com>2015-12-13 15:14:23 +0100
commitb350d209a20ebdf583d1dd2d3fdcd3be5073d2ef (patch)
tree6a607e7eb4a375d3b01b2700e267848a6cef13bf /src/dotty
parent88f24efb2858f46c146214bc7e51f5de17c31bc0 (diff)
downloaddotty-b350d209a20ebdf583d1dd2d3fdcd3be5073d2ef.tar.gz
dotty-b350d209a20ebdf583d1dd2d3fdcd3be5073d2ef.tar.bz2
dotty-b350d209a20ebdf583d1dd2d3fdcd3be5073d2ef.zip
Perform variance adaptation only when needed in isSubType
Previously adaptIfHK was performed on every type application. This made t3152 fail. We now do this only on demand, in isSubType. t3152 now passes again. But the change unmasked another error, which makes Iter2 fail to compile.
Diffstat (limited to 'src/dotty')
-rw-r--r--src/dotty/tools/dotc/core/TypeApplications.scala59
-rw-r--r--src/dotty/tools/dotc/core/TypeComparer.scala12
-rw-r--r--src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala2
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala2
-rw-r--r--src/dotty/tools/dotc/typer/Namer.scala2
5 files changed, 48 insertions, 29 deletions
diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala
index b2f22da20..f7727779d 100644
--- a/src/dotty/tools/dotc/core/TypeApplications.scala
+++ b/src/dotty/tools/dotc/core/TypeApplications.scala
@@ -167,11 +167,11 @@ object TypeApplications {
}
}
- /** Adapt all arguments to possible higher-kinded type parameters using adaptIfHK
+ /** Adapt all arguments to possible higher-kinded type parameters using etaExpandIfHK
*/
- def adaptArgs(tparams: List[Symbol], args: List[Type])(implicit ctx: Context): List[Type] =
+ def etaExpandIfHK(tparams: List[Symbol], args: List[Type])(implicit ctx: Context): List[Type] =
if (tparams.isEmpty) args
- else args.zipWithConserve(tparams)((arg, tparam) => arg.adaptIfHK(tparam.infoOrCompleter))
+ else args.zipWithConserve(tparams)((arg, tparam) => arg.etaExpandIfHK(tparam.infoOrCompleter))
def argRefs(rt: RefinedType, n: Int)(implicit ctx: Context) =
List.range(0, n).map(i => RefinedThis(rt).select(tpnme.hkArg(i)))
@@ -340,11 +340,21 @@ class TypeApplications(val self: Type) extends AnyVal {
self.EtaExpand(self.typeParams)
}
- /** 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:
+ /** 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
+ if (hkParams.isEmpty) self
+ else self match {
+ case self: TypeRef if self.symbol.isClass && self.typeParams.length == hkParams.length =>
+ EtaExpansion(self)
+ case _ => self
+ }
+ }
+
+ /** If argument A and type parameter P are higher-kinded, adapt the variances
+ * of A to those of P, ensuring that the variances of the type lambda A
+ * agree with the variances of corresponding higherkinded type parameters of P. Example:
*
* class Companion[+CC[X]]
* Companion[List]
@@ -367,25 +377,26 @@ class TypeApplications(val self: Type) extends AnyVal {
* 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 = {
+ def adaptHkVariances(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 _ =>
- def adaptArg(arg: Type): Type = arg match {
- case arg: TypeRef
- if arg.symbol.isLambdaTrait &&
- !arg.symbol.typeParams.corresponds(boundLambda.typeParams)(_.variance == _.variance) =>
- arg.prefix.select(boundLambda)
- case arg: RefinedType =>
- arg.derivedRefinedType(adaptArg(arg.parent), arg.refinedName, arg.refinedInfo)
- case _ =>
- arg
- }
- adaptArg(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 @ TypeAlias(alias) =>
+ arg.derivedTypeAlias(adaptArg(alias))
+ case arg @ TypeBounds(lo, hi) =>
+ arg.derivedTypeBounds(lo, adaptArg(hi))
+ case _ =>
+ arg
+ }
+ adaptArg(self)
}
}
diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala
index bf82bfe09..14b5a403c 100644
--- a/src/dotty/tools/dotc/core/TypeComparer.scala
+++ b/src/dotty/tools/dotc/core/TypeComparer.scala
@@ -703,11 +703,19 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
* Further, no refinement refers back to the refined type via a refined this.
* The precondition is established by `skipMatching`.
*/
- private def isSubRefinements(tp1: RefinedType, tp2: RefinedType, limit: Type): Boolean =
- isSubType(tp1.refinedInfo, tp2.refinedInfo) && (
+ private def isSubRefinements(tp1: RefinedType, tp2: RefinedType, limit: Type): Boolean = {
+ def hasSubRefinement(tp1: RefinedType, refine2: Type): Boolean = {
+ isSubType(tp1.refinedInfo, refine2) || {
+ // last effort: try to adapt variances of higher-kinded types if this is sound.
+ val adapted2 = refine2.adaptHkVariances(tp1.parent.member(tp1.refinedName).symbol.info)
+ adapted2.ne(refine2) && hasSubRefinement(tp1, adapted2)
+ }
+ }
+ hasSubRefinement(tp1, tp2.refinedInfo) && (
(tp2.parent eq limit) ||
isSubRefinements(
tp1.parent.asInstanceOf[RefinedType], tp2.parent.asInstanceOf[RefinedType], limit))
+ }
/** A type has been covered previously in subtype checking if it
* is some combination of TypeRefs that point to classes, where the
diff --git a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala
index b0e31202f..618e3ceea 100644
--- a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala
+++ b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala
@@ -715,7 +715,7 @@ 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.nonEmpty) tycon.safeAppliedTo(adaptArgs(sym.typeParams, args))
+ else if (args.nonEmpty) tycon.safeAppliedTo(etaExpandIfHK(sym.typeParams, args))
else if (sym.typeParams.nonEmpty) tycon.EtaExpand(sym.typeParams)
else tycon
case TYPEBOUNDStpe =>
diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala
index c6053d0fd..941b35d71 100644
--- a/src/dotty/tools/dotc/typer/Applications.scala
+++ b/src/dotty/tools/dotc/typer/Applications.scala
@@ -614,7 +614,7 @@ trait Applications extends Compatibility { self: Typer =>
}
def adaptTypeArg(tree: tpd.Tree, bound: Type)(implicit ctx: Context): tpd.Tree =
- tree.withType(tree.tpe.adaptIfHK(bound))
+ tree.withType(tree.tpe.etaExpandIfHK(bound))
/** Rewrite `new Array[T](....)` trees to calls of newXYZArray methods. */
def convertNewArray(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala
index fedbc98b8..ca37614bf 100644
--- a/src/dotty/tools/dotc/typer/Namer.scala
+++ b/src/dotty/tools/dotc/typer/Namer.scala
@@ -853,7 +853,7 @@ class Namer { typer: Typer =>
val tycon = tp.withoutArgs(args)
val tycon1 = this(tycon)
val tparams = tycon.typeParams
- val args1 = if (args.length == tparams.length) adaptArgs(tparams, args) else args
+ val args1 = if (args.length == tparams.length) etaExpandIfHK(tparams, args) else args
if ((tycon1 eq tycon) && (args1 eq args)) tp else tycon1.appliedTo(args1)
} else mapOver(tp)
case _ => mapOver(tp)