Skip to content

Commit 02caa36

Browse files
committed
More systematic checking for AndTypes on the left
1 parent f10edbf commit 02caa36

File tree

1 file changed

+44
-37
lines changed

1 file changed

+44
-37
lines changed

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

Lines changed: 44 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,11 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
169169
private inline def inFrozenGadtAndConstraint[T](inline op: T): T =
170170
inFrozenGadtIf(true)(inFrozenConstraint(op))
171171

172+
extension (tp: TypeRef)
173+
private inline def onGadtBounds(inline op: TypeBounds => Boolean): Boolean =
174+
val bounds = gadtBounds(tp.symbol)
175+
bounds != null && op(bounds)
176+
172177
protected def isSubType(tp1: Type, tp2: Type, a: ApproxState): Boolean = {
173178
val savedApprox = approx
174179
val savedLeftRoot = leftRoot
@@ -465,19 +470,15 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
465470
case AndType(tp21, tp22) => constrainRHSVars(tp21) && constrainRHSVars(tp22)
466471
case _ => true
467472

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-
477473
widenOK
478474
|| joinOK
479475
|| (tp1.isSoft || constrainRHSVars(tp2)) && recur(tp11, tp2) && recur(tp12, tp2)
480476
|| 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+
481482
case tp1: MatchType =>
482483
val reduced = tp1.reduced
483484
if (reduced.exists) recur(reduced, tp2) else thirdTry
@@ -559,40 +560,35 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
559560
case tp2: TypeParamRef =>
560561
compareTypeParamRef(tp2)
561562
case tp2: RefinedType =>
562-
def compareRefinedSlow: Boolean = {
563+
def compareRefinedSlow: Boolean =
563564
val name2 = tp2.refinedName
564-
recur(tp1, tp2.parent) &&
565-
(name2 == nme.WILDCARD || hasMatchingMember(name2, tp1, tp2))
566-
}
567-
def compareRefined: Boolean = {
565+
recur(tp1, tp2.parent)
566+
&& (name2 == nme.WILDCARD || hasMatchingMember(name2, tp1, tp2))
567+
568+
def compareRefined: Boolean =
568569
val tp1w = tp1.widen
569570
val skipped2 = skipMatching(tp1w, tp2)
570-
if ((skipped2 eq tp2) || !Config.fastPathForRefinedSubtype)
571-
tp1 match {
572-
case tp1: AndType =>
573-
// TODO: this should really be an in depth analysis whether LHS contains
574-
// an AndType, or has an AndType as bound. What matters is to predict
575-
// whether we will be forced into an either later on.
576-
tp2.parent match
577-
case _: RefinedType | _: AndType =>
578-
// maximally decompose RHS to limit the bad effects of the `either` that is necessary
579-
// since LHS is an AndType
580-
recur(tp1, decomposeRefinements(tp2, Nil))
581-
case _ =>
582-
// Delay calling `compareRefinedSlow` because looking up a member
583-
// of an `AndType` can lead to a cascade of subtyping checks
584-
// This twist is needed to make collection/generic/ParFactory.scala compile
585-
fourthTry || compareRefinedSlow
586-
case tp1: HKTypeLambda =>
587-
// HKTypeLambdas do not have members.
588-
fourthTry
589-
case _ =>
590-
compareRefinedSlow || fourthTry
591-
}
571+
if (skipped2 eq tp2) || !Config.fastPathForRefinedSubtype then
572+
if containsAnd(tp1) then
573+
tp2.parent match
574+
case _: RefinedType | _: AndType =>
575+
// maximally decompose RHS to limit the bad effects of the `either` that is necessary
576+
// since LHS contains an AndType
577+
recur(tp1, decomposeRefinements(tp2, Nil))
578+
case _ =>
579+
// Delay calling `compareRefinedSlow` because looking up a member
580+
// of an `AndType` can lead to a cascade of subtyping checks
581+
// This twist is needed to make collection/generic/ParFactory.scala compile
582+
fourthTry || compareRefinedSlow
583+
else if tp1.isInstanceOf[HKTypeLambda] then
584+
// HKTypeLambdas do not have members.
585+
fourthTry
586+
else
587+
compareRefinedSlow || fourthTry
592588
else // fast path, in particular for refinements resulting from parameterization.
593589
isSubRefinements(tp1w.asInstanceOf[RefinedType], tp2, skipped2) &&
594590
recur(tp1, skipped2)
595-
}
591+
596592
compareRefined
597593
case tp2: RecType =>
598594
def compareRec = tp1.safeDealias match {
@@ -1709,6 +1705,17 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
17091705
case _ =>
17101706
refines.map(RefinedType(tp, _, _): Type).reduce(AndType(_, _))
17111707

1708+
/** Can comparing this type on the left lead to an either? This is the case if
1709+
* the type is and AndType or contains embedded occurrences of AndTypes
1710+
*/
1711+
def containsAnd(tp: Type): Boolean = tp match
1712+
case tp: AndType => true
1713+
case OrType(tp1, tp2) => containsAnd(tp1) || containsAnd(tp2)
1714+
case tp: TypeParamRef => containsAnd(bounds(tp).hi)
1715+
case tp: TypeRef => containsAnd(tp.info.hiBound) || tp.onGadtBounds(gbounds => containsAnd(gbounds.hi))
1716+
case tp: TypeProxy => containsAnd(tp.superType)
1717+
case _ => false
1718+
17121719
/** Does type `tp1` have a member with name `name` whose normalized type is a subtype of
17131720
* the normalized type of the refinement `tp2`?
17141721
* Normalization is as follows: If `tp2` contains a skolem to its refinement type,

0 commit comments

Comments
 (0)