Skip to content

Commit 8efdbdc

Browse files
committed
Try to make refinements match in approximateUnions
See comment in Typer#approximateUnion for an explanation. Fixes #1045.
1 parent 7d1d93e commit 8efdbdc

File tree

3 files changed

+48
-2
lines changed

3 files changed

+48
-2
lines changed

src/dotty/tools/dotc/core/TypeOps.scala

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,15 +276,35 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
276276
case TypeBounds(_, hi) => hi
277277
case nx => nx
278278
}
279+
/** If `tp1` and `tp2` are typebounds, try to make one fit into the other
280+
* or to make them equal, by instantiating uninstantiated type variables.
281+
*/
282+
def homogenizedUnion(tp1: Type, tp2: Type): Type = {
283+
def fitInto(tp1: Type, tp2: Type): Unit = tp1 match {
284+
case tp1: TypeBounds =>
285+
tp2 match {
286+
case tp2: TypeBounds =>
287+
val nestedCtx = ctx.fresh.setNewTyperState
288+
if (tp2.boundsInterval.contains(tp1.boundsInterval)(nestedCtx))
289+
nestedCtx.typerState.commit()
290+
case _ =>
291+
}
292+
case _ =>
293+
}
294+
fitInto(tp1, tp2)
295+
fitInto(tp2, tp1)
296+
tp1 | tp2
297+
}
298+
279299
tp1 match {
280300
case tp1: RefinedType =>
281301
tp2 match {
282302
case tp2: RefinedType if tp1.refinedName == tp2.refinedName =>
283303
return tp1.derivedRefinedType(
284304
approximateUnion(OrType(tp1.parent, tp2.parent)),
285305
tp1.refinedName,
286-
(tp1.refinedInfo | tp2.refinedInfo).substRefinedThis(tp2, RefinedThis(tp1)))
287-
.ensuring { x => println(i"approx or $tp1 | $tp2 = $x"); true } // DEBUG
306+
homogenizedUnion(tp1.refinedInfo, tp2.refinedInfo).substRefinedThis(tp2, RefinedThis(tp1)))
307+
//.ensuring { x => println(i"approx or $tp1 | $tp2 = $x\n constr = ${ctx.typerState.constraint}"); true } // DEBUG
288308
case _ =>
289309
}
290310
case _ =>

src/dotty/tools/dotc/core/Types.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,15 @@ object Types {
11991199
* class B extends C[B] with D with E
12001200
*
12011201
* we approximate `A | B` by `C[A | B] with D`
1202+
*
1203+
* As a second measure we also homogenize refinements containing
1204+
* type variables. For instance, if `A` is an instantiatable type variable,
1205+
* then
1206+
*
1207+
* ArrayBuffer[Int] | ArrayBuffer[A]
1208+
*
1209+
* is approximated by instantiating `A` to `Int` and returning `ArrayBuffer[Int]`
1210+
* instead of `ArrayBuffer[_ >: Int | A <: Int & A]`
12021211
*/
12031212
def approximateUnion(implicit ctx: Context) = ctx.approximateUnion(this)
12041213

@@ -2847,6 +2856,11 @@ object Types {
28472856
case _ => super.| (that)
28482857
}
28492858

2859+
/** The implied bounds, where aliases are mapped to intervals from
2860+
* Nothing/Any
2861+
*/
2862+
def boundsInterval(implicit ctx: Context): TypeBounds = this
2863+
28502864
/** If this type and that type have the same variance, this variance, otherwise 0 */
28512865
final def commonVariance(that: TypeBounds): Int = (this.variance + that.variance) / 2
28522866

@@ -2884,6 +2898,11 @@ object Types {
28842898
else if (v < 0) derivedTypeAlias(this.lo & that.lo, v)
28852899
else super.| (that)
28862900
}
2901+
2902+
override def boundsInterval(implicit ctx: Context): TypeBounds =
2903+
if (variance == 0) this
2904+
else if (variance < 0) TypeBounds.lower(alias)
2905+
else TypeBounds.upper(alias)
28872906
}
28882907

28892908
class CachedTypeAlias(alias: Type, variance: Int, hc: Int) extends TypeAlias(alias, variance) {

tests/pos/i1045.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import scala.collection._
2+
object T {
3+
val newSymbolMap: mutable.HashMap[String, mutable.HashMap[Int, Double]] = mutable.HashMap.empty
4+
val map = newSymbolMap.getOrElse("a", mutable.HashMap.empty)
5+
map.put(1, 0.0)
6+
newSymbolMap.put("a", map)
7+
}

0 commit comments

Comments
 (0)