@@ -169,6 +169,11 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
169
169
private inline def inFrozenGadtAndConstraint [T ](inline op : T ): T =
170
170
inFrozenGadtIf(true )(inFrozenConstraint(op))
171
171
172
+ extension (sym : Symbol )
173
+ private inline def onGadtBounds (inline op : TypeBounds => Boolean ): Boolean =
174
+ val bounds = gadtBounds(sym)
175
+ bounds != null && op(bounds)
176
+
172
177
protected def isSubType (tp1 : Type , tp2 : Type , a : ApproxState ): Boolean = {
173
178
val savedApprox = approx
174
179
val savedLeftRoot = leftRoot
@@ -465,19 +470,15 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
465
470
case AndType (tp21, tp22) => constrainRHSVars(tp21) && constrainRHSVars(tp22)
466
471
case _ => true
467
472
468
- // An & on the left side loses information. We compensate by also trying the join.
469
- // This is less ad-hoc than it looks since we produce joins in type inference,
470
- // and then need to check that they are indeed supertypes of the original types
471
- // under -Ycheck. Test case is i7965.scala.
472
- def containsAnd (tp : Type ): Boolean = tp.dealiasKeepRefiningAnnots match
473
- case tp : AndType => true
474
- case OrType (tp1, tp2) => containsAnd(tp1) || containsAnd(tp2)
475
- case _ => false
476
-
477
473
widenOK
478
474
|| joinOK
479
475
|| (tp1.isSoft || constrainRHSVars(tp2)) && recur(tp11, tp2) && recur(tp12, tp2)
480
476
|| containsAnd(tp1) && inFrozenGadt(recur(tp1.join, tp2))
477
+ // An & on the left side loses information. We compensate by also trying the join.
478
+ // This is less ad-hoc than it looks since we produce joins in type inference,
479
+ // and then need to check that they are indeed supertypes of the original types
480
+ // under -Ycheck. Test case is i7965.scala.
481
+
481
482
case tp1 : MatchType =>
482
483
val reduced = tp1.reduced
483
484
if (reduced.exists) recur(reduced, tp2) else thirdTry
@@ -489,11 +490,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
489
490
490
491
def thirdTryNamed (tp2 : NamedType ): Boolean = tp2.info match {
491
492
case info2 : TypeBounds =>
492
- def compareGADT : Boolean = {
493
- val gbounds2 = gadtBounds(tp2.symbol)
494
- (gbounds2 != null ) &&
495
- (isSubTypeWhenFrozen(tp1, gbounds2.lo) ||
496
- (tp1 match {
493
+ def compareGADT : Boolean =
494
+ tp2.symbol.onGadtBounds(gbounds2 =>
495
+ isSubTypeWhenFrozen(tp1, gbounds2.lo)
496
+ || tp1.match
497
497
case tp1 : NamedType if ctx.gadt.contains(tp1.symbol) =>
498
498
// Note: since we approximate constrained types only with their non-param bounds,
499
499
// we need to manually handle the case when we're comparing two constrained types,
@@ -502,10 +502,9 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
502
502
// comparing two constrained types, and that case will be handled here first.
503
503
ctx.gadt.isLess(tp1.symbol, tp2.symbol) && GADTusage (tp1.symbol) && GADTusage (tp2.symbol)
504
504
case _ => false
505
- }) ||
506
- narrowGADTBounds(tp2, tp1, approx, isUpper = false )) &&
507
- { isBottom(tp1) || GADTusage (tp2.symbol) }
508
- }
505
+ || narrowGADTBounds(tp2, tp1, approx, isUpper = false ))
506
+ && (isBottom(tp1) || GADTusage (tp2.symbol))
507
+
509
508
isSubApproxHi(tp1, info2.lo) || compareGADT || tryLiftedToThis2 || fourthTry
510
509
511
510
case _ =>
@@ -559,31 +558,35 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
559
558
case tp2 : TypeParamRef =>
560
559
compareTypeParamRef(tp2)
561
560
case tp2 : RefinedType =>
562
- def compareRefinedSlow : Boolean = {
561
+ def compareRefinedSlow : Boolean =
563
562
val name2 = tp2.refinedName
564
- recur(tp1, tp2.parent) &&
565
- (name2 == nme.WILDCARD || hasMatchingMember(name2, tp1, tp2))
566
- }
567
- def compareRefined : Boolean = {
563
+ recur(tp1, tp2.parent)
564
+ && (name2 == nme.WILDCARD || hasMatchingMember(name2, tp1, tp2))
565
+
566
+ def compareRefined : Boolean =
568
567
val tp1w = tp1.widen
569
568
val skipped2 = skipMatching(tp1w, tp2)
570
- if ((skipped2 eq tp2) || ! Config .fastPathForRefinedSubtype)
571
- tp1 match {
572
- case tp1 : AndType =>
573
- // Delay calling `compareRefinedSlow` because looking up a member
574
- // of an `AndType` can lead to a cascade of subtyping checks
575
- // This twist is needed to make collection/generic/ParFactory.scala compile
576
- fourthTry || compareRefinedSlow
577
- case tp1 : HKTypeLambda =>
578
- // HKTypeLambdas do not have members.
579
- fourthTry
580
- case _ =>
581
- compareRefinedSlow || fourthTry
582
- }
569
+ if (skipped2 eq tp2) || ! Config .fastPathForRefinedSubtype then
570
+ if containsAnd(tp1) then
571
+ tp2.parent match
572
+ case _ : RefinedType | _ : AndType =>
573
+ // maximally decompose RHS to limit the bad effects of the `either` that is necessary
574
+ // since LHS contains an AndType
575
+ recur(tp1, decomposeRefinements(tp2, Nil ))
576
+ case _ =>
577
+ // Delay calling `compareRefinedSlow` because looking up a member
578
+ // of an `AndType` can lead to a cascade of subtyping checks
579
+ // This twist is needed to make collection/generic/ParFactory.scala compile
580
+ fourthTry || compareRefinedSlow
581
+ else if tp1.isInstanceOf [HKTypeLambda ] then
582
+ // HKTypeLambdas do not have members.
583
+ fourthTry
584
+ else
585
+ compareRefinedSlow || fourthTry
583
586
else // fast path, in particular for refinements resulting from parameterization.
584
587
isSubRefinements(tp1w.asInstanceOf [RefinedType ], tp2, skipped2) &&
585
588
recur(tp1, skipped2)
586
- }
589
+
587
590
compareRefined
588
591
case tp2 : RecType =>
589
592
def compareRec = tp1.safeDealias match {
@@ -751,13 +754,12 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
751
754
case tp1 : TypeRef =>
752
755
tp1.info match {
753
756
case TypeBounds (_, hi1) =>
754
- def compareGADT = {
755
- val gbounds1 = gadtBounds(tp1.symbol)
756
- (gbounds1 != null ) &&
757
- (isSubTypeWhenFrozen(gbounds1.hi, tp2) ||
758
- narrowGADTBounds(tp1, tp2, approx, isUpper = true )) &&
759
- { tp2.isAny || GADTusage (tp1.symbol) }
760
- }
757
+ def compareGADT =
758
+ tp1.symbol.onGadtBounds(gbounds1 =>
759
+ isSubTypeWhenFrozen(gbounds1.hi, tp2)
760
+ || narrowGADTBounds(tp1, tp2, approx, isUpper = true ))
761
+ && (tp2.isAny || GADTusage (tp1.symbol))
762
+
761
763
isSubType(hi1, tp2, approx.addLow) || compareGADT || tryLiftedToThis1
762
764
case _ =>
763
765
def isNullable (tp : Type ): Boolean = tp.widenDealias match {
@@ -1033,17 +1035,12 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
1033
1035
1034
1036
var touchedGADTs = false
1035
1037
var gadtIsInstantiated = false
1036
- def byGadtBounds (sym : Symbol , tp : Type , fromAbove : Boolean ): Boolean = {
1037
- touchedGADTs = true
1038
- val b = gadtBounds(sym)
1039
- def boundsDescr = if b == null then " null" else b.show
1040
- b != null && inFrozenGadt {
1041
- if fromAbove then isSubType(b.hi, tp) else isSubType(tp, b.lo)
1042
- } && {
1043
- gadtIsInstantiated = b.isInstanceOf [TypeAlias ]
1044
- true
1045
- }
1046
- }
1038
+
1039
+ extension (sym : Symbol )
1040
+ inline def byGadtBounds (inline op : TypeBounds => Boolean ): Boolean =
1041
+ touchedGADTs = true
1042
+ sym.onGadtBounds(
1043
+ b => op(b) && { gadtIsInstantiated = b.isInstanceOf [TypeAlias ]; true })
1047
1044
1048
1045
def byGadtOrdering : Boolean =
1049
1046
ctx.gadt.contains(tycon1sym)
@@ -1052,8 +1049,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
1052
1049
1053
1050
val res = (
1054
1051
tycon1sym == tycon2sym && isSubPrefix(tycon1.prefix, tycon2.prefix)
1055
- || byGadtBounds(tycon1sym, tycon2, fromAbove = true )
1056
- || byGadtBounds(tycon2sym, tycon1, fromAbove = false )
1052
+ || tycon1sym. byGadtBounds(b => isSubTypeWhenFrozen(b.hi, tycon2) )
1053
+ || tycon2sym. byGadtBounds(b => isSubTypeWhenFrozen( tycon1, b.lo) )
1057
1054
|| byGadtOrdering
1058
1055
) && {
1059
1056
// There are two cases in which we can assume injectivity.
@@ -1691,6 +1688,26 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
1691
1688
else op2
1692
1689
end necessaryEither
1693
1690
1691
+ /** Decompose into conjunction of types each of which has only a single refinement */
1692
+ def decomposeRefinements (tp : Type , refines : List [(Name , Type )]): Type = tp match
1693
+ case RefinedType (parent, rname, rinfo) =>
1694
+ decomposeRefinements(parent, (rname, rinfo) :: refines)
1695
+ case AndType (tp1, tp2) =>
1696
+ AndType (decomposeRefinements(tp1, refines), decomposeRefinements(tp2, refines))
1697
+ case _ =>
1698
+ refines.map(RefinedType (tp, _, _): Type ).reduce(AndType (_, _))
1699
+
1700
+ /** Can comparing this type on the left lead to an either? This is the case if
1701
+ * the type is and AndType or contains embedded occurrences of AndTypes
1702
+ */
1703
+ def containsAnd (tp : Type ): Boolean = tp match
1704
+ case tp : AndType => true
1705
+ case OrType (tp1, tp2) => containsAnd(tp1) || containsAnd(tp2)
1706
+ case tp : TypeParamRef => containsAnd(bounds(tp).hi)
1707
+ case tp : TypeRef => containsAnd(tp.info.hiBound) || tp.symbol.onGadtBounds(gbounds => containsAnd(gbounds.hi))
1708
+ case tp : TypeProxy => containsAnd(tp.superType)
1709
+ case _ => false
1710
+
1694
1711
/** Does type `tp1` have a member with name `name` whose normalized type is a subtype of
1695
1712
* the normalized type of the refinement `tp2`?
1696
1713
* Normalization is as follows: If `tp2` contains a skolem to its refinement type,
0 commit comments