Skip to content

Commit a462528

Browse files
Backport "Heal occurrences of => T between ElimByName and Erasure" to LTS (#20871)
Backports #19558 to the LTS branch. PR submitted by the release tooling. [skip ci]
2 parents c1dbf72 + 0b44bbe commit a462528

File tree

2 files changed

+44
-34
lines changed

2 files changed

+44
-34
lines changed

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

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2230,40 +2230,35 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
22302230
else if !tp2.exists || (tp2 eq WildcardType) then tp1
22312231
else if tp1.isAny && !tp2.isLambdaSub || tp1.isAnyKind || isBottom(tp2) then tp2
22322232
else if tp2.isAny && !tp1.isLambdaSub || tp2.isAnyKind || isBottom(tp1) then tp1
2233-
else tp2 match
2234-
case tp2: LazyRef =>
2235-
glb(tp1, tp2.ref)
2236-
case _ =>
2237-
tp1 match
2238-
case tp1: LazyRef =>
2239-
glb(tp1.ref, tp2)
2240-
case _ =>
2241-
val tp1a = dropIfSuper(tp1, tp2)
2242-
if tp1a ne tp1 then glb(tp1a, tp2)
2243-
else
2244-
val tp2a = dropIfSuper(tp2, tp1)
2245-
if tp2a ne tp2 then glb(tp1, tp2a)
2246-
else tp2 match // normalize to disjunctive normal form if possible.
2247-
case tp2 @ OrType(tp21, tp22) =>
2248-
lub(tp1 & tp21, tp1 & tp22, isSoft = tp2.isSoft)
2249-
case _ =>
2250-
tp1 match
2251-
case tp1 @ OrType(tp11, tp12) =>
2252-
lub(tp11 & tp2, tp12 & tp2, isSoft = tp1.isSoft)
2253-
case tp1: ConstantType =>
2254-
tp2 match
2255-
case tp2: ConstantType =>
2256-
// Make use of the fact that the intersection of two constant types
2257-
// types which are not subtypes of each other is known to be empty.
2258-
// Note: The same does not apply to singleton types in general.
2259-
// E.g. we could have a pattern match against `x.type & y.type`
2260-
// which might succeed if `x` and `y` happen to be the same ref
2261-
// at run time. It would not work to replace that with `Nothing`.
2262-
// However, maybe we can still apply the replacement to
2263-
// types which are not explicitly written.
2264-
NothingType
2265-
case _ => andType(tp1, tp2)
2233+
else
2234+
def mergedGlb(tp1: Type, tp2: Type): Type =
2235+
val tp1a = dropIfSuper(tp1, tp2)
2236+
if tp1a ne tp1 then glb(tp1a, tp2)
2237+
else
2238+
val tp2a = dropIfSuper(tp2, tp1)
2239+
if tp2a ne tp2 then glb(tp1, tp2a)
2240+
else tp2 match // normalize to disjunctive normal form if possible.
2241+
case tp2 @ OrType(tp21, tp22) =>
2242+
lub(tp1 & tp21, tp1 & tp22, isSoft = tp2.isSoft)
2243+
case _ =>
2244+
tp1 match
2245+
case tp1 @ OrType(tp11, tp12) =>
2246+
lub(tp11 & tp2, tp12 & tp2, isSoft = tp1.isSoft)
2247+
case tp1: ConstantType =>
2248+
tp2 match
2249+
case tp2: ConstantType =>
2250+
// Make use of the fact that the intersection of two constant types
2251+
// types which are not subtypes of each other is known to be empty.
2252+
// Note: The same does not apply to singleton types in general.
2253+
// E.g. we could have a pattern match against `x.type & y.type`
2254+
// which might succeed if `x` and `y` happen to be the same ref
2255+
// at run time. It would not work to replace that with `Nothing`.
2256+
// However, maybe we can still apply the replacement to
2257+
// types which are not explicitly written.
2258+
NothingType
22662259
case _ => andType(tp1, tp2)
2260+
case _ => andType(tp1, tp2)
2261+
mergedGlb(dropExpr(tp1.stripLazyRef), dropExpr(tp2.stripLazyRef))
22672262
}
22682263

22692264
def widenInUnions(using Context): Boolean =
@@ -2303,7 +2298,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
23032298
if ((tp1 ne tp1w) || (tp2 ne tp2w)) lub(tp1w, tp2w, canConstrain = canConstrain, isSoft = isSoft)
23042299
else orType(tp1w, tp2w, isSoft = isSoft) // no need to check subtypes again
23052300
}
2306-
mergedLub(tp1.stripLazyRef, tp2.stripLazyRef)
2301+
mergedLub(dropExpr(tp1.stripLazyRef), dropExpr(tp2.stripLazyRef))
23072302
}
23082303

23092304
/** Try to produce joint arguments for a lub `A[T_1, ..., T_n] | A[T_1', ..., T_n']` using
@@ -2421,6 +2416,19 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
24212416
NoType
24222417
}
24232418

2419+
/** There's a window of vulnerability between ElimByName and Erasure where some
2420+
* ExprTypes `=> T` that appear as parameters of function types are not yet converted
2421+
* to by-name functions `() ?=> T`. These would cause an assertion violation when
2422+
* used as operands of glb or lub. We fix this on the fly here. As explained in
2423+
* ElimByName, we can't fix it beforehand by mapping all occurrences of `=> T` to
2424+
* `() ?=> T` since that could lead to cycles.
2425+
*/
2426+
private def dropExpr(tp: Type): Type = tp match
2427+
case ExprType(rt) if (Phases.elimByNamePhase <= ctx.phase) && !ctx.erasedTypes =>
2428+
defn.ByNameFunction(rt)
2429+
case _ =>
2430+
tp
2431+
24242432
private def andTypeGen(tp1: Type, tp2: Type, op: (Type, Type) => Type,
24252433
original: (Type, Type) => Type = _ & _, isErased: Boolean = ctx.erasedTypes): Type = trace(s"andTypeGen(${tp1.show}, ${tp2.show})", subtyping, show = true) {
24262434
val t1 = distributeAnd(tp1, tp2)

tests/pos/i19548.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def byName[T](p: => T): T = p
2+
val test = (if ??? then byName else (??? : ((=> Int) => Int))) (42)

0 commit comments

Comments
 (0)