Skip to content

Commit 49a35e3

Browse files
committed
rewrite widenUnion
1 parent 00d6607 commit 49a35e3

File tree

3 files changed

+51
-47
lines changed

3 files changed

+51
-47
lines changed

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

Lines changed: 15 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,44 +1105,18 @@ object Types {
11051105
def widenUnion(implicit ctx: Context): Type = {
11061106
widen match {
11071107
case tp @ OrType(lhs, rhs) =>
1108-
def defaultJoin(tp1: Type, tp2: Type) =
1109-
ctx.typeComparer.lub(tp1, tp2, canConstrain = true) match {
1110-
case union: OrType => union.join
1111-
case res => res
1112-
}
1113-
1114-
// Given a type `tpe`, if it is already a nullable union, return it unchanged.
1115-
// Otherwise, construct a nullable union where `tpe` is the lhs (use `orig` to
1116-
// potentially avoid creating a new object for the union).
1117-
def ensureNullableUnion(tpe: Type, orig: OrType): Type = tpe match {
1118-
case orTpe: OrType if orTpe.tp2.isNullType => tpe
1119-
case _ => orig.derivedOrType(tpe, defn.NullType)
1120-
}
1121-
1122-
// Test for nullable union that assumes the type has already been normalized.
1123-
def isNullableUnionFast(tp: Type): Boolean = tp match {
1124-
case orTpe: OrType if orTpe.tp2.isNullType => true
1125-
case _ => false
1126-
}
1127-
1128-
if (ctx.explicitNulls) {
1129-
// Don't widen `T|Null`, since otherwise we wouldn't be able to infer nullable unions.
1130-
// This part relies on the postcondition of widenUnion: the result is either a
1131-
// non-union type, or a nullable union type where the rhs is `Null` type.
1132-
if (rhs.isNullType) ensureNullableUnion(lhs.widenUnion, tp)
1133-
else if (lhs.isNullType) ensureNullableUnion(rhs.widenUnion, tp)
1134-
else {
1135-
val lhsWiden = lhs.widenUnion
1136-
val rhsWiden = rhs.widenUnion
1137-
val tmpRes = defaultJoin(lhs.widenUnion, rhs.widenUnion)
1138-
if (isNullableUnionFast(lhsWiden) || isNullableUnionFast(rhsWiden))
1139-
// If either lhs or rhs is a nullable union,
1140-
// we need to ensure the result is also a nullable union.
1141-
ensureNullableUnion(tmpRes, tp)
1142-
else tmpRes
1143-
}
1108+
tp match {
1109+
case OrNull(tp1) =>
1110+
// Don't widen `T|Null`, since otherwise we wouldn't be able to infer nullable unions.
1111+
val tp1Widen = tp1.widenUnion
1112+
if (tp1Widen.isRef(defn.AnyClass)) tp1Widen
1113+
else tp.derivedOrType(tp1Widen, defn.NullType)
1114+
case _ =>
1115+
ctx.typeComparer.lub(lhs.widenUnion, rhs.widenUnion, canConstrain = true) match {
1116+
case union: OrType => union.join
1117+
case res => res
1118+
}
11441119
}
1145-
else defaultJoin(lhs.widenUnion, rhs.widenUnion)
11461120
case tp @ AndType(tp1, tp2) =>
11471121
tp derived_& (tp1.widenUnion, tp2.widenUnion)
11481122
case tp: RefinedType =>
@@ -2965,9 +2939,12 @@ object Types {
29652939
object OrNull {
29662940
def apply(tp: Type)(given Context) =
29672941
OrType(tp, defn.NullType)
2968-
def unapply(tp: Type)(given Context): Option[Type] =
2942+
def unapply(tp: Type)(given ctx: Context): Option[Type] =
2943+
if (ctx.explicitNulls) {
29692944
val tp1 = tp.stripNull
29702945
if tp1 ne tp then Some(tp1) else None
2946+
}
2947+
else None
29712948
}
29722949

29732950
object OrJavaNull {

tests/explicit-nulls/pos/dont-widen-singleton.scala

Lines changed: 0 additions & 9 deletions
This file was deleted.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Test that we correctly handle nullable unions when widening.
2+
// We keep nullable after widening.
3+
class Test {
4+
class A
5+
class B
6+
class C extends A
7+
8+
locally {
9+
val x: String|Null = ???
10+
val y = x // String|Null is inferred, this used to crash the compiler
11+
}
12+
13+
locally {
14+
val x: (Int | Null) | String = ???
15+
val y = x
16+
val _: Any = y
17+
}
18+
19+
locally {
20+
val x: (A | Null) | B = ???
21+
val y = x
22+
val _: AnyRef | Null = y
23+
}
24+
25+
locally {
26+
val x: A | (Null | C) = ???
27+
val y = x // after simplification before widenUnion, the type of x would become A | Null
28+
val _: A | Null = y
29+
}
30+
31+
locally {
32+
val x: (A | Null) | (Null | B) = ???
33+
val y = x
34+
val _: AnyRef | Null = y
35+
}
36+
}

0 commit comments

Comments
 (0)