From 8efdbdc9843f66fbb8fc39aa2716da0fe7187fd9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 4 Feb 2016 14:37:44 +0100 Subject: Try to make refinements match in approximateUnions See comment in Typer#approximateUnion for an explanation. Fixes #1045. --- src/dotty/tools/dotc/core/TypeOps.scala | 24 ++++++++++++++++++++++-- src/dotty/tools/dotc/core/Types.scala | 19 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala index 7121d94df..28730fec9 100644 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ b/src/dotty/tools/dotc/core/TypeOps.scala @@ -276,6 +276,26 @@ trait TypeOps { this: Context => // TODO: Make standalone object. case TypeBounds(_, hi) => hi case nx => nx } + /** If `tp1` and `tp2` are typebounds, try to make one fit into the other + * or to make them equal, by instantiating uninstantiated type variables. + */ + def homogenizedUnion(tp1: Type, tp2: Type): Type = { + def fitInto(tp1: Type, tp2: Type): Unit = tp1 match { + case tp1: TypeBounds => + tp2 match { + case tp2: TypeBounds => + val nestedCtx = ctx.fresh.setNewTyperState + if (tp2.boundsInterval.contains(tp1.boundsInterval)(nestedCtx)) + nestedCtx.typerState.commit() + case _ => + } + case _ => + } + fitInto(tp1, tp2) + fitInto(tp2, tp1) + tp1 | tp2 + } + tp1 match { case tp1: RefinedType => tp2 match { @@ -283,8 +303,8 @@ trait TypeOps { this: Context => // TODO: Make standalone object. return tp1.derivedRefinedType( approximateUnion(OrType(tp1.parent, tp2.parent)), tp1.refinedName, - (tp1.refinedInfo | tp2.refinedInfo).substRefinedThis(tp2, RefinedThis(tp1))) - .ensuring { x => println(i"approx or $tp1 | $tp2 = $x"); true } // DEBUG + homogenizedUnion(tp1.refinedInfo, tp2.refinedInfo).substRefinedThis(tp2, RefinedThis(tp1))) + //.ensuring { x => println(i"approx or $tp1 | $tp2 = $x\n constr = ${ctx.typerState.constraint}"); true } // DEBUG case _ => } case _ => diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index e266bab6f..812edf2fe 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1199,6 +1199,15 @@ object Types { * class B extends C[B] with D with E * * we approximate `A | B` by `C[A | B] with D` + * + * As a second measure we also homogenize refinements containing + * type variables. For instance, if `A` is an instantiatable type variable, + * then + * + * ArrayBuffer[Int] | ArrayBuffer[A] + * + * is approximated by instantiating `A` to `Int` and returning `ArrayBuffer[Int]` + * instead of `ArrayBuffer[_ >: Int | A <: Int & A]` */ def approximateUnion(implicit ctx: Context) = ctx.approximateUnion(this) @@ -2847,6 +2856,11 @@ object Types { case _ => super.| (that) } + /** The implied bounds, where aliases are mapped to intervals from + * Nothing/Any + */ + def boundsInterval(implicit ctx: Context): TypeBounds = this + /** If this type and that type have the same variance, this variance, otherwise 0 */ final def commonVariance(that: TypeBounds): Int = (this.variance + that.variance) / 2 @@ -2884,6 +2898,11 @@ object Types { else if (v < 0) derivedTypeAlias(this.lo & that.lo, v) else super.| (that) } + + override def boundsInterval(implicit ctx: Context): TypeBounds = + if (variance == 0) this + else if (variance < 0) TypeBounds.lower(alias) + else TypeBounds.upper(alias) } class CachedTypeAlias(alias: Type, variance: Int, hc: Int) extends TypeAlias(alias, variance) { -- cgit v1.2.3