aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2016-02-04 14:37:44 +0100
committerMartin Odersky <odersky@gmail.com>2016-02-04 14:37:56 +0100
commit8efdbdc9843f66fbb8fc39aa2716da0fe7187fd9 (patch)
tree243f8f8ece4db7840291c6ce80414caed284762e
parent7d1d93e95113802bee77b9d2b89475a56be46bf7 (diff)
downloaddotty-8efdbdc9843f66fbb8fc39aa2716da0fe7187fd9.tar.gz
dotty-8efdbdc9843f66fbb8fc39aa2716da0fe7187fd9.tar.bz2
dotty-8efdbdc9843f66fbb8fc39aa2716da0fe7187fd9.zip
Try to make refinements match in approximateUnions
See comment in Typer#approximateUnion for an explanation. Fixes #1045.
-rw-r--r--src/dotty/tools/dotc/core/TypeOps.scala24
-rw-r--r--src/dotty/tools/dotc/core/Types.scala19
-rw-r--r--tests/pos/i1045.scala7
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)
+}