Skip to content

Commit 6a252c0

Browse files
committed
Do not widen nullable unions
We need to be able to infer nullable unions: val s = someJavaMethod // need s: String|JavaNull inferred Normally, unions are widened when inferred, so we need to special case nullable unions.
1 parent fd76a61 commit 6a252c0

File tree

4 files changed

+30
-3
lines changed

4 files changed

+30
-3
lines changed

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,12 +1179,21 @@ object Types {
11791179
*
11801180
* is approximated by constraining `A` to be =:= to `Int` and returning `ArrayBuffer[Int]`
11811181
* instead of `ArrayBuffer[_ >: Int | A <: Int & A]`
1182+
*
1183+
* Exception (if `-YexplicitNulls` is set): if this type is a nullable union (i.e. of the form `T | Null`),
1184+
* then the top-level union isn't widened. This is needed so that type inference can infer nullable types.
11821185
*/
11831186
def widenUnion(implicit ctx: Context): Type = this match {
11841187
case OrType(tp1, tp2) =>
1185-
ctx.typeComparer.lub(tp1.widenUnion, tp2.widenUnion, canConstrain = true) match {
1186-
case union: OrType => union.join
1187-
case res => res
1188+
if (ctx.settings.YexplicitNulls.value && isNullableUnion) {
1189+
// Don't widen `T|Null`, since otherwise we wouldn't be able to infer nullable unions.
1190+
val OrType(leftTpe, nullTpe) = normNullableUnion
1191+
OrType(leftTpe.widenUnion, nullTpe)
1192+
} else {
1193+
ctx.typeComparer.lub(tp1.widenUnion, tp2.widenUnion, canConstrain = true) match {
1194+
case union: OrType => union.join
1195+
case res => res
1196+
}
11881197
}
11891198
case tp @ AndType(tp1, tp2) =>
11901199
tp derived_& (tp1.widenUnion, tp2.widenUnion)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class J {
2+
String foo() { return "hello"; }
3+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class S {
2+
val j = new J()
3+
val x = j.foo()
4+
// Check that the type of `x` is inferred to be `String|Null`.
5+
// i.e. the union isn't collapsed.
6+
val y: String|Null = x
7+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
class S {
3+
def foo[T](x: T): T = x
4+
// Check that the type argument to `foo` is inferred to be
5+
// `String|Null`: i.e. it isn't collapsed.
6+
val x = foo(if (1 == 2) "hello" else null)
7+
val y: String|Null = x
8+
}

0 commit comments

Comments
 (0)