@@ -2458,6 +2458,29 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
24582458 isSubRef(tp1, tp2) && isSubRef(tp2, tp1)
24592459 }
24602460
2461+ /** If the range `tp1..tp2` consist of a single type, that type, otherwise NoType`.
2462+ * This is the case if `tp1 =:= tp2`, but also if `tp1 <:< tp2`, `tp1` is a singleton type,
2463+ * and `tp2` derives from `scala.Singleton` and `sourceVersion.enablesDistributeAnd` (or vice-versa).
2464+ * Examples of the latter case:
2465+ *
2466+ * "name".type .. Singleton
2467+ * "name".type .. String & Singleton
2468+ * Singleton .. "name".type
2469+ * String & Singleton .. "name".type
2470+ *
2471+ * All consist of the single type `"name".type`.
2472+ */
2473+ def singletonInterval (tp1 : Type , tp2 : Type ): Type = {
2474+ def isSingletonBounds (lo : Type , hi : Type ) =
2475+ lo.isSingleton && hi.derivesFrom(defn.SingletonClass ) && isSubTypeWhenFrozen(lo, hi)
2476+ if (isSameTypeWhenFrozen(tp1, tp2)) tp1
2477+ else if sourceVersion.enablesDistributeAnd then
2478+ if (isSingletonBounds(tp1, tp2)) tp1
2479+ else if (isSingletonBounds(tp2, tp1)) tp2
2480+ else NoType
2481+ else NoType
2482+ }
2483+
24612484 /** The greatest lower bound of two types */
24622485 def glb (tp1 : Type , tp2 : Type ): Type = // trace(s"glb(${tp1.show}, ${tp2.show})", subtyping, show = true):
24632486 if tp1 eq tp2 then tp1
@@ -2563,7 +2586,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
25632586 case tparam :: tparamsRest =>
25642587 val arg1 :: args1Rest = args1 : @ unchecked
25652588 val arg2 :: args2Rest = args2 : @ unchecked
2566- val common = if isSameTypeWhenFrozen (arg1, arg2) then arg1 else NoType
2589+ val common = singletonInterval (arg1, arg2)
25672590 val v = tparam.paramVarianceSign
25682591 val lubArg =
25692592 if (common.exists) common
@@ -2595,7 +2618,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
25952618 case tparam :: tparamsRest =>
25962619 val arg1 :: args1Rest = args1 : @ unchecked
25972620 val arg2 :: args2Rest = args2 : @ unchecked
2598- val common = if isSameTypeWhenFrozen (arg1, arg2) then arg1 else NoType
2621+ val common = singletonInterval (arg1, arg2)
25992622 val v = tparam.paramVarianceSign
26002623 val glbArg =
26012624 if (common.exists) common
@@ -2748,10 +2771,19 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
27482771 }
27492772
27502773 /** Try to distribute `&` inside type, detect and handle conflicts
2751- * Note that an intersection cannot be pushed into an applied type, see tests/neg/i23435-min.
27522774 * @pre !(tp1 <: tp2) && !(tp2 <:< tp1) -- these cases were handled before
27532775 */
27542776 private def distributeAnd (tp1 : Type , tp2 : Type ): Type = tp1 match {
2777+ case tp1 @ AppliedType (tycon1, args1) if sourceVersion.enablesDistributeAnd =>
2778+ tp2 match {
2779+ case AppliedType (tycon2, args2)
2780+ if tycon1.typeSymbol == tycon2.typeSymbol && tycon1 =:= tycon2 =>
2781+ val jointArgs = glbArgs(args1, args2, tycon1.typeParams)
2782+ if (jointArgs.forall(_.exists)) (tycon1 & tycon2).appliedTo(jointArgs)
2783+ else NoType
2784+ case _ =>
2785+ NoType
2786+ }
27552787 case tp1 : RefinedType =>
27562788 // opportunistically merge same-named refinements
27572789 // this does not change anything semantically (i.e. merging or not merging
0 commit comments