Skip to content

Commit 5069c08

Browse files
committed
Don't widen instance if bound is subtype of Singleton
This was the situation before the PR. I thought that simply checking whether the instance is a subtype of the bound is equivalent, but compare-singletons.scala shows that this is not the case. Here, we have a type variable X such that `r.type <:< X <:< Singleton` where `r: R` and `R <: Singleton`. With the previous changes in this PR, we instantiate `X` to `R`. With the revert in this commit we instantiate it to `r.type` instead.
1 parent 8e51c97 commit 5069c08

File tree

3 files changed

+24
-5
lines changed

3 files changed

+24
-5
lines changed

compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -275,19 +275,26 @@ trait ConstraintHandling[AbstractContext] {
275275
/** Widen inferred type `inst` with upper `bound`, according to the following rules:
276276
* 1. If `inst` is a singleton type, or a union containing some singleton types,
277277
* widen (all) the singleton type(s), provied the result is a subtype of `bound`
278-
* (i.e. `inst.widenSingletons <:< bound` succeeds with satisfiable constraint).
278+
* (i.e. `inst.widenSingletons <:< bound` succeeds with satisfiable constraint)
279+
* and `bound` is not a subtype of `scala.Singleton`.
279280
* 2. If `inst` is a union type, approximate the union type from above by an intersection
280281
* of all common base types, provied the result is a subtype of `bound`.
281282
*
282283
* At this point we also drop the @Repeated annotation to avoid inferring type arguments with it,
283284
* as those could leak the annotation to users (see run/inferred-repeated-result).
284285
*/
285286
def widenInferred(inst: Type, bound: Type)(implicit actx: AbstractContext): Type = {
286-
def tryWiden(tp: Type, widen: Type => Type) = {
287-
val tpw = widen(tp)
287+
def widenOr(tp: Type) = {
288+
val tpw = tp.widenUnion
288289
if ((tpw ne tp) && tpw <:< bound) tpw else tp
289290
}
290-
tryWiden(tryWiden(inst, _.widenSingletons), _.widenUnion).dropRepeatedAnnot
291+
def widenSingle(tp: Type) =
292+
if (isSubTypeWhenFrozen(bound, defn.SingletonType)) tp
293+
else {
294+
val tpw = tp.widenSingletons
295+
if ((tpw ne tp) && tpw <:< bound) tpw else tp
296+
}
297+
widenOr(widenSingle(inst)).dropRepeatedAnnot
291298
}
292299

293300
/** The instance type of `param` in the current constraint (which contains `param`).

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1046,7 +1046,7 @@ object Types {
10461046
* is approximated by constraining `A` to be =:= to `Int` and returning `ArrayBuffer[Int]`
10471047
* instead of `ArrayBuffer[_ >: Int | A <: Int & A]`
10481048
*/
1049-
def widenUnion(implicit ctx: Context): Type = widen match {
1049+
def widenUnion(implicit ctx: Context): Type = this match {
10501050
case OrType(tp1, tp2) =>
10511051
ctx.typeComparer.lub(tp1.widenUnion, tp2.widenUnion, canConstrain = true) match {
10521052
case union: OrType => union.join

tests/pos/compare-singletons.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class Refl {
2+
type S
3+
}
4+
5+
class A[R <: Refl & Singleton](val r: R) {
6+
def s: r.S = ???
7+
}
8+
9+
class B[R <: Refl & Singleton](val r: R) {
10+
val a = new A(r)
11+
val s: r.S = a.s
12+
}

0 commit comments

Comments
 (0)