From 0fc19e0e1ef7b56f9ca0649fde35765892cf1a53 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 9 Oct 2013 12:36:43 +0200 Subject: Cleanup of new scheme for handling local type parameters and type members. If a type parameter or local type member is co/contravariant, its instantiation is a special alias type that remembers the variance. These alias types can be refined with subtypes in subclasses and intersection and union translate to their bounds. --- src/dotty/tools/dotc/core/Flags.scala | 4 +- src/dotty/tools/dotc/core/SymDenotations.scala | 12 +++++- src/dotty/tools/dotc/core/TypeComparer.scala | 29 ++++++++----- src/dotty/tools/dotc/core/Types.scala | 56 +++++++++++++++++--------- src/dotty/tools/dotc/typer/Namer.scala | 2 +- 5 files changed, 69 insertions(+), 34 deletions(-) diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index 17ff18b73..593fb2bbb 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -458,10 +458,10 @@ object Flags { final val ParamOrAccessor = Param | Accessor /** A covariant type parameter instance */ - final val CovariantExpanded = allOf(ExpandedName, Covariant) + final val LocalCovariant = allOf(Local, Covariant) /** A contravariant type parameter instance */ - final val ContravariantExpanded = allOf(ExpandedName, Contravariant) + final val LocalContravariant = allOf(Local, Contravariant) /** A covariant type parameter instance */ final val TypeParamOrInstance = TypeParam | TypeParamInstance diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 8a4cdf1e5..9142e3185 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -117,7 +117,7 @@ object SymDenotations { } protected[dotc] final def info_=(tp: Type) = { - def illegal: String = s"illegal type for module $this: $tp" + def illegal: String = s"illegal type for $this: $tp" /* if (this is Module) // make sure module invariants that allow moduleClass and sourceModule to work are kept. tp match { @@ -646,7 +646,7 @@ object SymDenotations { def symRef(implicit ctx: Context): NamedType = NamedType.withSym(owner.thisType, symbol) - /** The variance of this type parameter as an Int, with + /** The variance of this type parameter or type member as an Int, with * +1 = Covariant, -1 = Contravariant, 0 = Nonvariant, or not a type parameter */ final def variance: Int = @@ -654,6 +654,14 @@ object SymDenotations { else if (this is Contravariant) -1 else 0 + /** If this is a privatye[this] or protected[this] type parameter or type member, + * its variance, otherwise 0. + */ + final def localVariance: Int = + if (this is LocalCovariant) 1 + else if (this is LocalContravariant) -1 + else 0 + override def toString = { val kindString = if (this is ModuleClass) "module class" diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 14b0413ec..c54312e01 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -254,11 +254,12 @@ class TypeComparer(initctx: Context) extends DotClass { case _ => isSubType(tp1, restpe2) } - case TypeBounds(lo2, hi2) => + case tp2 @ TypeBounds(lo2, hi2) => tp1 match { - case TypeBounds(lo1, hi1) => - ((lo2 isRef NothingClass) || isSubType(lo2, lo1)) && - ((hi2 isRef AnyClass) || isSubType(hi1, hi2)) + case tp1 @ TypeBounds(lo1, hi1) => + val v = tp1.variance + tp2.variance + ((v > 0) || (lo2 isRef NothingClass) || isSubType(lo2, lo1)) && + ((v < 0) || (hi2 isRef AnyClass) || isSubType(hi1, hi2)) case tp1: ClassInfo => val tt = tp1.typeConstructor // was typeTemplate isSubType(lo2, tt) && isSubType(tt, hi2) @@ -434,7 +435,7 @@ class TypeComparer(initctx: Context) extends DotClass { } } } - + /** The greatest lower bound of a list types */ final def glb(tps: List[Type]): Type = (defn.AnyType /: tps)(glb) @@ -552,9 +553,14 @@ class TypeComparer(initctx: Context) extends DotClass { /** Try to distribute `&` inside type, detect and handle conflicts */ private def distributeAnd(tp1: Type, tp2: Type): Type = tp1 match { - case TypeBounds(lo1, hi1) => + case tp1 @ TypeBounds(lo1, hi1) => tp2 match { - case TypeBounds(lo2, hi2) => + case tp2 @ TypeBounds(lo2, hi2) => + if ((lo1 eq hi1) && (lo2 eq hi2)) { + val v = (tp1.variance + tp2.variance) / 2 + if (v > 0) return TypeAlias(hi1 & hi2, v) + if (v < 0) return TypeAlias(lo1 | lo2, v) + } TypeBounds(lo1 | lo2, hi1 & hi2) case _ => andConflict(tp1, tp2) @@ -615,9 +621,14 @@ class TypeComparer(initctx: Context) extends DotClass { /** Try to distribute `|` inside type, detect and handle conflicts */ private def distributeOr(tp1: Type, tp2: Type): Type = tp1 match { - case TypeBounds(lo1, hi1) => + case tp1 @ TypeBounds(lo1, hi1) => tp2 match { - case TypeBounds(lo2, hi2) => + case tp2 @ TypeBounds(lo2, hi2) => + if ((lo1 eq hi1) && (lo2 eq hi2)) { + val v = (tp1.variance + tp2.variance) / 2 + if (v > 0) return TypeAlias(hi1 | hi2, v) + if (v < 0) return TypeAlias(lo1 & lo2, v) + } TypeBounds(lo1 & lo2, hi1 | hi2) case _ => orConflict(tp1, tp2) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 978c39cc9..b5cd8c3ee 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -843,9 +843,9 @@ object Types { */ final def toBounds(tparam: Symbol)(implicit ctx: Context): TypeBounds = { val v = tparam.variance - if (v > 0) TypeBounds.upper(this) - else if (v < 0) TypeBounds.lower(this) - else TypeAlias(this) + if (v > 0 && !(tparam is Local)) TypeBounds.upper(this) + else if (v < 0 && !(tparam is Local)) TypeBounds.lower(this) + else TypeAlias(this, v) } /** The type arguments of the base type instance wrt `base` of this type */ @@ -893,11 +893,13 @@ object Types { */ final def argType(tparam: Symbol)(implicit ctx: Context): Type = this match { case TypeBounds(lo, hi) => - val v = tparam.variance - if (v > 0 && (lo isRef defn.NothingClass)) hi - else if (v < 0 && (hi isRef defn.AnyClass)) lo - else if (v == 0 && (lo eq hi)) lo - else NoType + if (lo eq hi) hi + else { + val v = tparam.variance + if (v > 0 && (lo isRef defn.NothingClass)) hi + else if (v < 0 && (hi isRef defn.AnyClass)) lo + else NoType + } case _ => NoType } @@ -1978,31 +1980,34 @@ object Types { assert(lo.isInstanceOf[TermType], lo+" "+lo.getClass) assert(hi.isInstanceOf[TermType], hi+" "+hi.getClass) + def variance: Int = 0 + override def underlying(implicit ctx: Context): Type = hi - def derivedTypeBounds(lo: Type, hi: Type)(implicit ctx: Context) = - if ((lo eq this.lo) && (hi eq this.hi)) this - else TypeBounds(lo, hi) + def derivedTypeBounds(lo: Type, hi: Type, variance: Int = this.variance)(implicit ctx: Context) = + if ((lo eq this.lo) && (hi eq this.hi) && (variance == this.variance)) this + else TypeBounds(lo, hi, variance) def contains(tp: Type)(implicit ctx: Context) = lo <:< tp && tp <:< hi def &(that: TypeBounds)(implicit ctx: Context): TypeBounds = - derivedTypeBounds(this.lo | that.lo, this.hi & that.hi) + derivedTypeBounds(this.lo | that.lo, this.hi & that.hi, (this.variance + that.variance) / 2) def | (that: TypeBounds)(implicit ctx: Context): TypeBounds = - derivedTypeBounds(this.lo & that.lo, this.hi | that.hi) + derivedTypeBounds(this.lo & that.lo, this.hi | that.hi, (this.variance + that.variance) / 2) override def & (that: Type)(implicit ctx: Context) = that match { case that: TypeBounds => this & that - case that: ClassInfo => this & that.bounds + case _ => super.& (that) } override def | (that: Type)(implicit ctx: Context) = that match { case that: TypeBounds => this | that + case _ => super.| (that) } def map(f: Type => Type)(implicit ctx: Context): TypeBounds = - TypeBounds(f(lo), f(hi)) + derivedTypeBounds(f(lo), f(hi)) /** Given a higher-kinded abstract type * @@ -2051,17 +2056,28 @@ object Types { } final class CachedTypeBounds(lo: Type, hi: Type) extends TypeBounds(lo, hi) + final class CoTypeBounds(lo: Type, hi: Type) extends TypeBounds(lo, hi) { + override def variance = 1 + } + final class ContraTypeBounds(lo: Type, hi: Type) extends TypeBounds(lo, hi) { + override def variance = -1 + } object TypeBounds { def empty(implicit ctx: Context) = apply(defn.NothingType, defn.AnyType) - def upper(hi: Type)(implicit ctx: Context) = apply(defn.NothingType, hi) - def lower(lo: Type)(implicit ctx: Context) = apply(lo, defn.AnyType) - def apply(lo: Type, hi: Type)(implicit ctx: Context) = - unique(new CachedTypeBounds(lo, hi)) + def upper(hi: Type, variance: Int = 0)(implicit ctx: Context) = apply(defn.NothingType, hi, variance) + def lower(lo: Type, variance: Int = 0)(implicit ctx: Context) = apply(lo, defn.AnyType, variance) + //def apply(lo: Type, hi: Type)(implicit ctx: Context): TypeBounds = apply(lo, hi, 0) + def apply(lo: Type, hi: Type, variance: Int = 0)(implicit ctx: Context): TypeBounds = + unique( + if (variance == 0) new CachedTypeBounds(lo, hi) + else if (variance == 1) new CoTypeBounds(lo, hi) + else new ContraTypeBounds(lo, hi) + ) } object TypeAlias { - def apply(tp: Type)(implicit ctx: Context) = TypeBounds(tp, tp) + def apply(tp: Type, variance: Int = 0)(implicit ctx: Context) = TypeBounds(tp, tp, variance) def unapply(tp: Type): Option[Type] = tp match { case TypeBounds(lo, hi) if lo eq hi => Some(lo) case _ => None diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index e9eb7df0b..72ed20454 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -431,7 +431,7 @@ class Namer { typer: Typer => else rhsType case _ => if (tparamSyms.nonEmpty) rhsType.LambdaAbstract(tparamSyms)(ctx.error(_, _)) - else TypeBounds(rhsType, rhsType) + else TypeAlias(rhsType, sym.localVariance) } } } \ No newline at end of file -- cgit v1.2.3