From 805fdd4a451df6686c05d1356009a2cea97b5362 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 29 Jul 2017 11:49:11 +0200 Subject: [PATCH 1/3] Refine variance for NamedTypes in TypeMaps --- compiler/src/dotty/tools/dotc/core/Types.scala | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 40fbc80f85f0..52a374e63afb 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3713,8 +3713,19 @@ object Types { tp match { case tp: NamedType => if (stopAtStatic && tp.symbol.isStatic) tp - else derivedSelect(tp, this(tp.prefix)) - + else { + val saved = variance + variance = variance max 0 + // A prefix is never contravariant. Even if say `p.A` is used in a contravariant + // context, we cannot assume contravariance for `p` because `p`'s lower + // bound might not have a binding for `A` (e.g. the lower bound could be `Nothing`). + // By contrast, covariance does translate to the prefix, since we have that + // if `p <: q` then `p.A <: q.A`, and well-formedness requires that `A` is a member + // of `p`'s upper bound. + val prefix1 = this(tp.prefix) + variance = saved + derivedSelect(tp, prefix1) + } case _: ThisType | _: BoundType | NoPrefix => tp From 60c7cacde34ad77d05226b6352dbb96c2be2af8b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 29 Jul 2017 12:38:00 +0200 Subject: [PATCH 2/3] Treat variances in type accumulators and type maps the same Bonus point: No need to override applyPrefix in variances accumulator. This means we have reduced three different ways to compute the current variance to one. --- compiler/src/dotty/tools/dotc/core/Types.scala | 4 ++-- compiler/src/dotty/tools/dotc/typer/Inferencing.scala | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 52a374e63afb..15d1f675205b 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3924,9 +3924,9 @@ object Types { protected var variance = 1 - protected def applyToPrefix(x: T, tp: NamedType) = { + protected final def applyToPrefix(x: T, tp: NamedType) = { val saved = variance - variance = 0 + variance = variance max 0 // see remark on NamedType case in TypeMap val result = this(x, tp.prefix) variance = saved result diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index b2c8fd0595a0..99a6e5373fa9 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -319,8 +319,6 @@ object Inferencing { case _ => foldOver(vmap, t) } - override def applyToPrefix(vmap: VarianceMap, t: NamedType) = - apply(vmap, t.prefix) } /** Include in `vmap` type variables occurring in the constraints of type variables From 8683f665c16e8af2f8381c7494b5d2e9fa499ad1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 29 Jul 2017 19:10:52 +0200 Subject: [PATCH 3/3] Refine determination of prefix in typedIdent --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 76bc9360f6aa..a389cf77e98c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -268,7 +268,20 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (qualifies(defDenot)) { val found = if (isSelfDenot(defDenot)) curOwner.enclosingClass.thisType - else curOwner.thisType.select(name, defDenot) + else { + val effectiveOwner = + if (curOwner.isTerm && defDenot.symbol.isType) + // Don't mix NoPrefix and thisType prefixes, since type comparer + // would not detect types to be compatible. Note: If we replace the + // 2nd condition by `defDenot.symbol.maybeOwner.isType` we get lots + // of failures in the `tastyBootstrap` test. Trying to compile these + // files in isolation works though. + // TODO: Investigate why that happens. + defDenot.symbol.owner + else + curOwner + effectiveOwner.thisType.select(name, defDenot) + } if (!(curOwner is Package) || isDefinedInCurrentUnit(defDenot)) result = checkNewOrShadowed(found, definition) // no need to go further out, we found highest prec entry else {