@@ -248,54 +248,62 @@ object TypeErasure {
248248 * The reason to pick last is that we prefer classes over traits that way,
249249 * which leads to more predictable bytecode and (?) faster dynamic dispatch.
250250 */
251- def erasedLub (tp1 : Type , tp2 : Type )(implicit ctx : Context ): Type = tp1 match {
252- case JavaArrayType (elem1) =>
253- import dotty .tools .dotc .transform .TypeUtils ._
254- tp2 match {
255- case JavaArrayType (elem2) =>
256- if (elem1.isPrimitiveValueType || elem2.isPrimitiveValueType) {
257- if (elem1.classSymbol eq elem2.classSymbol) // same primitive
258- JavaArrayType (elem1)
259- else defn.ObjectType
260- } else JavaArrayType (erasedLub(elem1, elem2))
261- case _ => defn.ObjectType
262- }
263- case _ =>
264- tp2 match {
265- case JavaArrayType (_) => defn.ObjectType
266- case _ =>
267- val cls2 = tp2.classSymbol
268-
269- /** takeWhile+1 */
270- def takeUntil [T ](l : List [T ])(f : T => Boolean ): List [T ] = {
271- @ tailrec def loop (tail : List [T ], acc : List [T ]): List [T ] =
272- tail match {
273- case h :: t => loop(if (f(h)) t else Nil , h :: acc)
274- case Nil => acc.reverse
275- }
276- loop(l, Nil )
277- }
278-
279- // We are not interested in anything that is not a supertype of tp2
280- val tp2superclasses = tp1.baseClasses.filter(cls2.derivesFrom)
281-
282- // From the spec, "Linearization also satisfies the property that a
283- // linearization of a class always contains the linearization of its
284- // direct superclass as a suffix"; it's enough to consider every
285- // candidate up to the first class.
286- val candidates = takeUntil(tp2superclasses)(! _.is(Trait ))
287-
288- // Candidates st "no other common superclass or trait derives from S"
289- val minimums = candidates.filter { cand =>
290- candidates.forall(x => ! x.derivesFrom(cand) || x.eq(cand))
291- }
292-
293- // Pick the last minimum to prioritise classes over traits
294- minimums.lastOption match {
295- case Some (lub) => valueErasure(lub.typeRef)
296- case _ => defn.ObjectType
297- }
298- }
251+ def erasedLub (tp1 : Type , tp2 : Type )(implicit ctx : Context ): Type = {
252+ // After erasure, C | {Null, Nothing} is just C, if C is a reference type.
253+ // We need to short-circuit this case here because the regular lub logic below
254+ // relies on the class hierarchy, which doesn't properly capture `Null`s subtyping
255+ // behaviour.
256+ if (defn.isBottomType(tp1) && tp2.derivesFrom(defn.ObjectClass )) return tp2
257+ if (defn.isBottomType(tp2) && tp1.derivesFrom(defn.ObjectClass )) return tp1
258+ tp1 match {
259+ case JavaArrayType (elem1) =>
260+ import dotty .tools .dotc .transform .TypeUtils ._
261+ tp2 match {
262+ case JavaArrayType (elem2) =>
263+ if (elem1.isPrimitiveValueType || elem2.isPrimitiveValueType) {
264+ if (elem1.classSymbol eq elem2.classSymbol) // same primitive
265+ JavaArrayType (elem1)
266+ else defn.ObjectType
267+ } else JavaArrayType (erasedLub(elem1, elem2))
268+ case _ => defn.ObjectType
269+ }
270+ case _ =>
271+ tp2 match {
272+ case JavaArrayType (_) => defn.ObjectType
273+ case _ =>
274+ val cls2 = tp2.classSymbol
275+
276+ /** takeWhile+1 */
277+ def takeUntil [T ](l : List [T ])(f : T => Boolean ): List [T ] = {
278+ @ tailrec def loop (tail : List [T ], acc : List [T ]): List [T ] =
279+ tail match {
280+ case h :: t => loop(if (f(h)) t else Nil , h :: acc)
281+ case Nil => acc.reverse
282+ }
283+ loop(l, Nil )
284+ }
285+
286+ // We are not interested in anything that is not a supertype of tp2
287+ val tp2superclasses = tp1.baseClasses.filter(cls2.derivesFrom)
288+
289+ // From the spec, "Linearization also satisfies the property that a
290+ // linearization of a class always contains the linearization of its
291+ // direct superclass as a suffix"; it's enough to consider every
292+ // candidate up to the first class.
293+ val candidates = takeUntil(tp2superclasses)(! _.is(Trait ))
294+
295+ // Candidates st "no other common superclass or trait derives from S"
296+ val minimums = candidates.filter { cand =>
297+ candidates.forall(x => ! x.derivesFrom(cand) || x.eq(cand))
298+ }
299+
300+ // Pick the last minimum to prioritise classes over traits
301+ minimums.lastOption match {
302+ case Some (lub) => valueErasure(lub.typeRef)
303+ case _ => defn.ObjectType
304+ }
305+ }
306+ }
299307 }
300308
301309 /** The erased greatest lower bound of two erased type picks one of the two argument types.
0 commit comments