diff options
-rw-r--r-- | src/dotty/tools/dotc/core/TypeOps.scala | 24 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Types.scala | 19 | ||||
-rw-r--r-- | tests/pos/i1045.scala | 7 |
3 files changed, 48 insertions, 2 deletions
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) { diff --git a/tests/pos/i1045.scala b/tests/pos/i1045.scala new file mode 100644 index 000000000..f5985af92 --- /dev/null +++ b/tests/pos/i1045.scala @@ -0,0 +1,7 @@ +import scala.collection._ +object T { + val newSymbolMap: mutable.HashMap[String, mutable.HashMap[Int, Double]] = mutable.HashMap.empty + val map = newSymbolMap.getOrElse("a", mutable.HashMap.empty) + map.put(1, 0.0) + newSymbolMap.put("a", map) +} |