diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala index 70e8302d93dc..34febf3be689 100644 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ b/src/dotty/tools/dotc/core/TypeOps.scala @@ -266,31 +266,72 @@ trait TypeOps { this: Context => // TODO: Make standalone object. val accu1 = if (accu exists (_ derivesFrom c)) accu else c :: accu if (cs == c.baseClasses) accu1 else dominators(rest, accu1) } - if (ctx.featureEnabled(defn.LanguageModuleClass, nme.keepUnions)) tp - else tp match { - case tp: OrType => - def isClassRef(tp: Type): Boolean = tp match { - case tp: TypeRef => tp.symbol.isClass - case tp: RefinedType => isClassRef(tp.parent) - case _ => false - } - def next(tp: TypeProxy) = tp.underlying match { - case TypeBounds(_, hi) => hi - case nx => nx - } - tp.tp1 match { - case tp1: TypeProxy if !isClassRef(tp1) => - approximateUnion(next(tp1) | tp.tp2) - case _ => - tp.tp2 match { - case tp2: TypeProxy if !isClassRef(tp2) => - approximateUnion(tp.tp1 | next(tp2)) + def approximateOr(tp1: Type, tp2: Type): Type = { + def isClassRef(tp: Type): Boolean = tp match { + case tp: TypeRef => tp.symbol.isClass + case tp: RefinedType => isClassRef(tp.parent) + case _ => false + } + def next(tp: TypeProxy) = tp.underlying match { + 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 = { + tp1 match { + case tp1: TypeBounds => + tp2 match { + case tp2: TypeBounds => + def fitInto(tp1: TypeBounds, tp2: TypeBounds): Unit = { + val nestedCtx = ctx.fresh.setNewTyperState + if (tp2.boundsInterval.contains(tp1.boundsInterval)(nestedCtx)) + nestedCtx.typerState.commit() + } + fitInto(tp1, tp2) + fitInto(tp2, tp1) case _ => - val commonBaseClasses = tp.mapReduceOr(_.baseClasses)(intersect) - val doms = dominators(commonBaseClasses, Nil) - doms.map(tp.baseTypeWithArgs).reduceLeft(AndType.apply) } + case _ => } + tp1 | tp2 + } + + tp1 match { + case tp1: RefinedType => + tp2 match { + case tp2: RefinedType if tp1.refinedName == tp2.refinedName => + return tp1.derivedRefinedType( + approximateUnion(OrType(tp1.parent, tp2.parent)), + tp1.refinedName, + 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 _ => + } + tp1 match { + case tp1: TypeProxy if !isClassRef(tp1) => + approximateUnion(next(tp1) | tp2) + case _ => + tp2 match { + case tp2: TypeProxy if !isClassRef(tp2) => + approximateUnion(tp1 | next(tp2)) + case _ => + val commonBaseClasses = tp.mapReduceOr(_.baseClasses)(intersect) + val doms = dominators(commonBaseClasses, Nil) + def baseTp(cls: ClassSymbol): Type = + if (tp1.typeParams.nonEmpty) tp.baseTypeRef(cls) + else tp.baseTypeWithArgs(cls) + doms.map(baseTp).reduceLeft(AndType.apply) + } + } + } + if (ctx.featureEnabled(defn.LanguageModuleClass, nme.keepUnions)) tp + else tp match { + case tp: OrType => + approximateOr(tp.tp1, tp.tp2) case tp @ AndType(tp1, tp2) => tp derived_& (approximateUnion(tp1), approximateUnion(tp2)) case tp: RefinedType => diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 12c05b578915..47a4f088f754 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) @@ -2848,6 +2857,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 @@ -2885,6 +2899,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/run/t5293-map.scala b/tests/disabled/run/t5293-map.scala similarity index 100% rename from tests/run/t5293-map.scala rename to tests/disabled/run/t5293-map.scala diff --git a/tests/pos/i1045.scala b/tests/pos/i1045.scala new file mode 100644 index 000000000000..f5985af9235a --- /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) +} diff --git a/tests/pos/intersection.scala b/tests/pos/intersection.scala index 2b9f6c0b72ba..a4c19b5af406 100644 --- a/tests/pos/intersection.scala +++ b/tests/pos/intersection.scala @@ -1,3 +1,4 @@ +import dotty.language.keepUnions object intersection { class A @@ -11,9 +12,6 @@ object intersection { val a: A & B => Unit = z val b: (A => Unit) | (B => Unit) = z - - - type needsA = A => Nothing type needsB = B => Nothing } diff --git a/tests/pending/run/vector1.check b/tests/run/vector1.check similarity index 100% rename from tests/pending/run/vector1.check rename to tests/run/vector1.check diff --git a/tests/pending/run/vector1.scala b/tests/run/vector1.scala similarity index 100% rename from tests/pending/run/vector1.scala rename to tests/run/vector1.scala