@@ -1135,6 +1135,17 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
1135
1135
case elsep : untpd.If => isIncomplete(elsep)
1136
1136
case _ => false
1137
1137
1138
+ // Insert a GADT cast if the type of the branch does not conform
1139
+ // to the type assigned to the whole if tree.
1140
+ // This happens when the computation of the type of the if tree
1141
+ // uses GADT constraints. See #15646.
1142
+ def gadtAdaptBranch (tree : Tree , branchPt : Type ): Tree =
1143
+ TypeComparer .testSubType(tree.tpe.widenExpr, branchPt) match {
1144
+ case CompareResult .OKwithGADTUsed =>
1145
+ insertGadtCast(tree, tree.tpe.widen, branchPt)
1146
+ case _ => tree
1147
+ }
1148
+
1138
1149
val branchPt = if isIncomplete(tree) then defn.UnitType else pt.dropIfProto
1139
1150
1140
1151
val result =
@@ -1148,7 +1159,16 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
1148
1159
val elsep0 = typed(tree.elsep, branchPt)(using cond1.nullableContextIf(false ))
1149
1160
thenp0 :: elsep0 :: Nil
1150
1161
}: @ unchecked
1151
- assignType(cpy.If (tree)(cond1, thenp1, elsep1), thenp1, elsep1)
1162
+
1163
+ val resType = thenp1.tpe | elsep1.tpe
1164
+ val thenp2 :: elsep2 :: Nil =
1165
+ (thenp1 :: elsep1 :: Nil ) map { t =>
1166
+ // Adapt each branch to ensure that their types conforms to the
1167
+ // type assigned to the if tree by inserting GADT casts.
1168
+ gadtAdaptBranch(t, resType)
1169
+ }: @ unchecked
1170
+
1171
+ cpy.If (tree)(cond1, thenp2, elsep2).withType(resType)
1152
1172
1153
1173
def thenPathInfo = cond1.notNullInfoIf(true ).seq(result.thenp.notNullInfo)
1154
1174
def elsePathInfo = cond1.notNullInfoIf(false ).seq(result.elsep.notNullInfo)
@@ -3763,20 +3783,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
3763
3783
gadts.println(i " unnecessary GADTused for $tree: ${tree.tpe.widenExpr} vs $pt in ${ctx.source}" )
3764
3784
res
3765
3785
} =>
3766
- // Insert an explicit cast, so that -Ycheck in later phases succeeds.
3767
- // The check "safeToInstantiate" in `maximizeType` works to prevent unsound GADT casts.
3768
- val target =
3769
- if tree.tpe.isSingleton then
3770
- val conj = AndType (tree.tpe, pt)
3771
- if tree.tpe.isStable && ! conj.isStable then
3772
- // this is needed for -Ycheck. Without the annotation Ycheck will
3773
- // skolemize the result type which will lead to different types before
3774
- // and after checking. See i11955.scala.
3775
- AnnotatedType (conj, Annotation (defn.UncheckedStableAnnot ))
3776
- else conj
3777
- else pt
3778
- gadts.println(i " insert GADT cast from $tree to $target" )
3779
- tree.cast(target)
3786
+ insertGadtCast(tree, wtp, pt)
3780
3787
case _ =>
3781
3788
// typr.println(i"OK ${tree.tpe}\n${TypeComparer.explained(_.isSubType(tree.tpe, pt))}") // uncomment for unexpected successes
3782
3789
tree
@@ -4207,4 +4214,36 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
4207
4214
EmptyTree
4208
4215
else typedExpr(call, defn.AnyType )
4209
4216
4217
+ /** Insert GADT cast to target type `pt` on the `tree`
4218
+ * so that -Ycheck in later phases succeeds.
4219
+ * The check "safeToInstantiate" in `maximizeType` works to prevent unsound GADT casts.
4220
+ */
4221
+ private def insertGadtCast (tree : Tree , wtp : Type , pt : Type )(using Context ): Tree =
4222
+ val target =
4223
+ if tree.tpe.isSingleton then
4224
+ // In the target type, when the singleton type is intersected, we also intersect
4225
+ // the GADT-approximated type of the singleton to avoid the loss of
4226
+ // information. See #15646.
4227
+ val gadtApprox = Inferencing .approximateGADT(wtp)
4228
+ gadts.println(i " gadt approx $wtp ~~~ $gadtApprox" )
4229
+ val conj =
4230
+ TypeComparer .testSubType(gadtApprox, pt) match {
4231
+ case CompareResult .OK =>
4232
+ // GADT approximation of the tree type is a subtype of expected type under empty GADT
4233
+ // constraints, so it is enough to only have the GADT approximation.
4234
+ AndType (tree.tpe, gadtApprox)
4235
+ case _ =>
4236
+ // In other cases, we intersect both the approximated type and the expected type.
4237
+ AndType (AndType (tree.tpe, gadtApprox), pt)
4238
+ }
4239
+ if tree.tpe.isStable && ! conj.isStable then
4240
+ // this is needed for -Ycheck. Without the annotation Ycheck will
4241
+ // skolemize the result type which will lead to different types before
4242
+ // and after checking. See i11955.scala.
4243
+ AnnotatedType (conj, Annotation (defn.UncheckedStableAnnot ))
4244
+ else conj
4245
+ else pt
4246
+ gadts.println(i " insert GADT cast from $tree to $target" )
4247
+ tree.cast(target)
4248
+ end insertGadtCast
4210
4249
}
0 commit comments