@@ -2352,8 +2352,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
2352
2352
}
2353
2353
2354
2354
/** The greatest lower bound of two types */
2355
- def glb (tp1 : Type , tp2 : Type ): Type = /* >|> * / trace(s " glb( ${tp1.show}, ${tp2.show}) " , subtyping, show = true ) /* <|< */ {
2356
- if ( tp1 eq tp2) tp1
2355
+ def glb (tp1 : Type , tp2 : Type ): Type = // trace(s"glb(${tp1.show}, ${tp2.show})", subtyping, show = true):
2356
+ if tp1 eq tp2 then tp1
2357
2357
else if ! tp1.exists || (tp1 eq WildcardType ) then tp2
2358
2358
else if ! tp2.exists || (tp2 eq WildcardType ) then tp1
2359
2359
else if tp1.isAny && ! tp2.isLambdaSub || tp1.isAnyKind || isBottom(tp2) then tp2
@@ -2366,12 +2366,12 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
2366
2366
val tp2a = dropIfSuper(tp2, tp1)
2367
2367
if tp2a ne tp2 then glb(tp1, tp2a)
2368
2368
else tp2 match // normalize to disjunctive normal form if possible.
2369
- case tp2 @ OrType (tp21, tp22 ) =>
2370
- lub(tp1 & tp21 , tp1 & tp22 , isSoft = tp2.isSoft)
2369
+ case tp2 @ OrType (tp2L, tp2R ) =>
2370
+ lub(tp1 & tp2L , tp1 & tp2R , isSoft = tp2.isSoft)
2371
2371
case _ =>
2372
2372
tp1 match
2373
- case tp1 @ OrType (tp11, tp12 ) =>
2374
- lub(tp11 & tp2, tp12 & tp2, isSoft = tp1.isSoft)
2373
+ case tp1 @ OrType (tp1L, tp1R ) =>
2374
+ lub(tp1L & tp2, tp1R & tp2, isSoft = tp1.isSoft)
2375
2375
case tp1 : ConstantType =>
2376
2376
tp2 match
2377
2377
case tp2 : ConstantType =>
@@ -2386,8 +2386,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
2386
2386
NothingType
2387
2387
case _ => andType(tp1, tp2)
2388
2388
case _ => andType(tp1, tp2)
2389
+ end mergedGlb
2390
+
2389
2391
mergedGlb(dropExpr(tp1.stripLazyRef), dropExpr(tp2.stripLazyRef))
2390
- }
2392
+ end glb
2391
2393
2392
2394
def widenInUnions (using Context ): Boolean =
2393
2395
migrateTo3 || ctx.erasedTypes
@@ -2396,14 +2398,23 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
2396
2398
* @param canConstrain If true, new constraints might be added to simplify the lub.
2397
2399
* @param isSoft If the lub is a union, this determines whether it's a soft union.
2398
2400
*/
2399
- def lub (tp1 : Type , tp2 : Type , canConstrain : Boolean = false , isSoft : Boolean = true ): Type = /* >|> * / trace(s " lub( ${tp1.show}, ${tp2.show}, canConstrain= $canConstrain, isSoft= $isSoft) " , subtyping, show = true ) /* <|< */ {
2400
- if ( tp1 eq tp2) tp1
2401
+ def lub (tp1 : Type , tp2 : Type , canConstrain : Boolean = false , isSoft : Boolean = true ): Type = // trace(s"lub(${tp1.show}, ${tp2.show}, canConstrain=$canConstrain, isSoft=$isSoft)", subtyping, show = true):
2402
+ if tp1 eq tp2 then tp1
2401
2403
else if ! tp1.exists || (tp2 eq WildcardType ) then tp1
2402
2404
else if ! tp2.exists || (tp1 eq WildcardType ) then tp2
2403
2405
else if tp1.isAny && ! tp2.isLambdaSub || tp1.isAnyKind || isBottom(tp2) then tp1
2404
2406
else if tp2.isAny && ! tp1.isLambdaSub || tp2.isAnyKind || isBottom(tp1) then tp2
2405
2407
else
2406
- def mergedLub (tp1 : Type , tp2 : Type ): Type = {
2408
+ def mergedLub (tp1 : Type , tp2 : Type ): Type =
2409
+ // First, if tp1 and tp2 are the same singleton type, return one of them.
2410
+ if tp1.isSingleton && isSubType(tp1, tp2, whenFrozen = ! canConstrain) then
2411
+ return tp2
2412
+ if tp2.isSingleton && isSubType(tp2, tp1, whenFrozen = ! canConstrain) then
2413
+ return tp1
2414
+
2415
+ // Second, handle special cases when tp1 and tp2 are disjunctions of
2416
+ // singleton types. This saves time otherwise spent in
2417
+ // costly subtype comparisons performed in dropIfSub below.
2407
2418
tp1.atoms match
2408
2419
case Atoms .Range (lo1, hi1) if ! widenInUnions =>
2409
2420
tp2.atoms match
@@ -2413,20 +2424,24 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
2413
2424
if (hi1 & hi2).isEmpty then return orType(tp1, tp2, isSoft = isSoft)
2414
2425
case none =>
2415
2426
case none =>
2416
- val t1 = mergeIfSuper(tp1, tp2, canConstrain)
2417
- if (t1.exists) return t1
2418
2427
2419
- val t2 = mergeIfSuper(tp2, tp1, canConstrain)
2420
- if (t2.exists) return t2
2421
-
2422
- def widen (tp : Type ) = if (widenInUnions) tp.widen else tp.widenIfUnstable
2428
+ // Third, try to simplify after widening as follows:
2429
+ // 1. Drop all or-factors in tp2 that are subtypes of an or-factor
2430
+ // in tp1, yielding tp2Final.
2431
+ // 2. Drop all or-factors in tp1 that are subtypes of an or-factor
2432
+ // in tp2Final, yielding tp1Final.
2433
+ // 3. Combine the two final types in an OrType
2434
+ def widen (tp : Type ) =
2435
+ if widenInUnions then tp.widen else tp.widenIfUnstable
2423
2436
val tp1w = widen(tp1)
2424
2437
val tp2w = widen(tp2)
2425
- if ((tp1 ne tp1w) || (tp2 ne tp2w)) lub(tp1w, tp2w, canConstrain = canConstrain, isSoft = isSoft)
2426
- else orType(tp1w, tp2w, isSoft = isSoft) // no need to check subtypes again
2427
- }
2438
+ val tp2Final = dropIfSub(tp2w, tp1w, canConstrain)
2439
+ val tp1Final = dropIfSub(tp1w, tp2Final, canConstrain)
2440
+ recombine(tp1Final, tp2Final, orType(_, _, isSoft = isSoft))
2441
+ end mergedLub
2442
+
2428
2443
mergedLub(dropExpr(tp1.stripLazyRef), dropExpr(tp2.stripLazyRef))
2429
- }
2444
+ end lub
2430
2445
2431
2446
/** Try to produce joint arguments for a lub `A[T_1, ..., T_n] | A[T_1', ..., T_n']` using
2432
2447
* the following strategies:
@@ -2488,60 +2503,48 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
2488
2503
Nil
2489
2504
}
2490
2505
2491
- private def recombineAnd (tp : AndType , tp1 : Type , tp2 : Type ) =
2492
- if (! tp1.exists) tp2
2493
- else if (! tp2.exists) tp1
2494
- else tp.derivedAndType(tp1, tp2)
2506
+ private def recombine (tp1 : Type , tp2 : Type , rebuild : (Type , Type ) => Type ): Type =
2507
+ if ! tp1.exists then tp2
2508
+ else if ! tp2.exists then tp1
2509
+ else rebuild(tp1, tp2)
2510
+
2511
+ private def recombine (tp1 : Type , tp2 : Type , tp : AndOrType ): Type =
2512
+ recombine(tp1, tp2, tp.derivedAndOrType)
2495
2513
2496
2514
/** If some (&-operand of) `tp` is a supertype of `sub` replace it with `NoType`.
2497
2515
*/
2498
2516
private def dropIfSuper (tp : Type , sub : Type ): Type =
2499
- if (isSubTypeWhenFrozen(sub, tp)) NoType
2500
- else tp match {
2517
+
2518
+ def isSuperOf (sub : Type ): Boolean = sub match
2519
+ case AndType (sub1, sub2) => isSuperOf(sub1) || isSuperOf(sub2)
2520
+ case sub : TypeVar if sub.isInstantiated => isSuperOf(sub.inst)
2521
+ case _ => isSubTypeWhenFrozen(sub, tp)
2522
+
2523
+ tp match
2501
2524
case tp @ AndType (tp1, tp2) =>
2502
- recombineAnd(tp, dropIfSuper(tp1, sub), dropIfSuper(tp2, sub))
2525
+ recombine(dropIfSuper(tp1, sub), dropIfSuper(tp2, sub), tp)
2526
+ case tp : TypeVar if tp.isInstantiated =>
2527
+ dropIfSuper(tp.inst, sub)
2503
2528
case _ =>
2504
- tp
2505
- }
2529
+ if isSuperOf(sub) then NoType else tp
2530
+ end dropIfSuper
2506
2531
2507
- /** Merge `t1` into `tp2` if t1 is a subtype of some &-summand of tp2.
2508
- */
2509
- private def mergeIfSub (tp1 : Type , tp2 : Type ): Type =
2510
- if (isSubTypeWhenFrozen(tp1, tp2)) tp1
2511
- else tp2 match {
2512
- case tp2 @ AndType (tp21, tp22) =>
2513
- val lower1 = mergeIfSub(tp1, tp21)
2514
- if (lower1 eq tp21) tp2
2515
- else if (lower1.exists) lower1 & tp22
2516
- else {
2517
- val lower2 = mergeIfSub(tp1, tp22)
2518
- if (lower2 eq tp22) tp2
2519
- else if (lower2.exists) tp21 & lower2
2520
- else NoType
2521
- }
2522
- case _ =>
2523
- NoType
2524
- }
2532
+ /** If some (|-operand of) `tp` is a subtype of `sup` replace it with `NoType`. */
2533
+ private def dropIfSub (tp : Type , sup : Type , canConstrain : Boolean ): Type =
2525
2534
2526
- /** Merge `tp1` into `tp2` if tp1 is a supertype of some |-summand of tp2.
2527
- * @param canConstrain If true, new constraints might be added to make the merge possible.
2528
- */
2529
- private def mergeIfSuper (tp1 : Type , tp2 : Type , canConstrain : Boolean ): Type =
2530
- if (isSubType(tp2, tp1, whenFrozen = ! canConstrain)) tp1
2531
- else tp2 match {
2532
- case tp2 @ OrType (tp21, tp22) =>
2533
- val higher1 = mergeIfSuper(tp1, tp21, canConstrain)
2534
- if (higher1 eq tp21) tp2
2535
- else if (higher1.exists) lub(higher1, tp22, isSoft = tp2.isSoft)
2536
- else {
2537
- val higher2 = mergeIfSuper(tp1, tp22, canConstrain)
2538
- if (higher2 eq tp22) tp2
2539
- else if (higher2.exists) lub(tp21, higher2, isSoft = tp2.isSoft)
2540
- else NoType
2541
- }
2535
+ def isSubOf (sup : Type ): Boolean = sup match
2536
+ case OrType (sup1, sup2) => isSubOf(sup1) || isSubOf(sup2)
2537
+ case sup : TypeVar if sup.isInstantiated => isSubOf(sup.inst)
2538
+ case _ => isSubType(tp, sup, whenFrozen = ! canConstrain)
2539
+
2540
+ tp match
2541
+ case tp @ OrType (tp1, tp2) =>
2542
+ recombine(dropIfSub(tp1, sup, canConstrain), dropIfSub(tp2, sup, canConstrain), tp)
2543
+ case tp : TypeVar if tp.isInstantiated =>
2544
+ dropIfSub(tp.inst, sup, canConstrain)
2542
2545
case _ =>
2543
- NoType
2544
- }
2546
+ if isSubOf(sup) then NoType else tp
2547
+ end dropIfSub
2545
2548
2546
2549
/** There's a window of vulnerability between ElimByName and Erasure where some
2547
2550
* ExprTypes `=> T` that appear as parameters of function types are not yet converted
0 commit comments