Skip to content

Commit 1c6e998

Browse files
committed
widenUnion now also widens Singletons
This relieves some of the load of `join` and offers more opportunities to improve union types by constraining further. Without this step we might lose some improvements compared to the situation before where we always widened singletons in OrTypes. I don't have a test case that demonstrates the difference, though.
1 parent c1db1b0 commit 1c6e998

File tree

3 files changed

+14
-11
lines changed

3 files changed

+14
-11
lines changed

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

+10-8
Original file line numberDiff line numberDiff line change
@@ -276,10 +276,11 @@ trait ConstraintHandling[AbstractContext] {
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`
278278
* (i.e. `inst.widenSingletons <:< bound` succeeds with satisfiable constraint)
279-
* and `bound` is not a subtype of `scala.Singleton`.
280279
* 2. If `inst` is a union type, approximate the union type from above by an intersection
281280
* of all common base types, provied the result is a subtype of `bound`.
282281
*
282+
* Don't do these widenings if `bound` is a subtype of `scala.Singleton`.
283+
*
283284
* At this point we also drop the @Repeated annotation to avoid inferring type arguments with it,
284285
* as those could leak the annotation to users (see run/inferred-repeated-result).
285286
*/
@@ -288,13 +289,14 @@ trait ConstraintHandling[AbstractContext] {
288289
val tpw = tp.widenUnion
289290
if ((tpw ne tp) && tpw <:< bound) tpw else tp
290291
}
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
292+
def widenSingle(tp: Type) = {
293+
val tpw = tp.widenSingletons
294+
if ((tpw ne tp) && tpw <:< bound) tpw else tp
295+
}
296+
val wideInst =
297+
if (isSubTypeWhenFrozen(bound, defn.SingletonType)) inst
298+
else widenOr(widenSingle(inst))
299+
wideInst.dropRepeatedAnnot
298300
}
299301

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

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -1033,7 +1033,8 @@ object Types {
10331033
case _ => this
10341034
}
10351035

1036-
/** If this type contains embedded union types, replace them by their joins.
1036+
/** Widen this type and if the result contains embedded union types, replace
1037+
* them by their joins.
10371038
* "Embedded" means: inside intersectons or recursive types, or in prefixes of refined types.
10381039
* If an embedded union is found, we first try to simplify or eliminate it by
10391040
* re-lubbing it while allowing type parameters to be constrained further.
@@ -1046,7 +1047,7 @@ object Types {
10461047
* is approximated by constraining `A` to be =:= to `Int` and returning `ArrayBuffer[Int]`
10471048
* instead of `ArrayBuffer[_ >: Int | A <: Int & A]`
10481049
*/
1049-
def widenUnion(implicit ctx: Context): Type = this match {
1050+
def widenUnion(implicit ctx: Context): Type = widen match {
10501051
case OrType(tp1, tp2) =>
10511052
ctx.typeComparer.lub(tp1.widenUnion, tp2.widenUnion, canConstrain = true) match {
10521053
case union: OrType => union.join

compiler/src/dotty/tools/dotc/typer/Namer.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1326,7 +1326,7 @@ class Namer { typer: Typer =>
13261326
val tp1 = tp.widenTermRefExpr match {
13271327
case ctp: ConstantType if isInlineVal => ctp
13281328
case ref: TypeRef if ref.symbol.is(ModuleClass) => tp
1329-
case _ => tp.widen.widenUnion
1329+
case _ => tp.widenUnion
13301330
}
13311331
tp1.dropRepeatedAnnot
13321332
}

0 commit comments

Comments
 (0)