Skip to content

Commit 2238324

Browse files
Re-enable distributeAnd until source version 3.8
1 parent 6386817 commit 2238324

File tree

7 files changed

+52
-6
lines changed

7 files changed

+52
-6
lines changed

compiler/src/dotty/tools/dotc/config/SourceVersion.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ enum SourceVersion:
4343
def enablesNewGivens = isAtLeast(`3.6`)
4444
def enablesNamedTuples = isAtLeast(`3.7`)
4545
def enablesBetterFors(using Context) = isAtLeast(`3.7`) && isPreviewEnabled
46+
/** See PR #23441 and tests/neg/i23435-min */
47+
def enablesDistributeAnd = !isAtLeast(`3.8`)
4648

4749
def requiresNewSyntax = isAtLeast(future)
4850

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Symbols.*
88
import SymDenotations.LazyType
99
import Decorators.*
1010
import util.Stats.*
11+
import config.Feature.sourceVersion
1112
import Names.*
1213
import StdNames.nme
1314
import Flags.{Module, Provisional}
@@ -482,9 +483,10 @@ class TypeApplications(val self: Type) extends AnyVal {
482483
if tp.tp1.isBottomType then elemType(tp.tp2)
483484
else if tp.tp2.isBottomType then elemType(tp.tp1)
484485
else tp.derivedOrType(elemType(tp.tp1), elemType(tp.tp2))
485-
case AndType(tp1, tp2) =>
486-
// see #23435 for why this is not `tp.derivedAndType(elemType(tp1), ...)`
487-
OrType(elemType(tp1), elemType(tp2), soft = false)
486+
case tp @ AndType(tp1, tp2) =>
487+
if sourceVersion.enablesDistributeAnd
488+
then tp.derivedAndType(elemType(tp1), elemType(tp2))
489+
else OrType(elemType(tp1), elemType(tp2), soft = false)
488490
case _ =>
489491
tp.baseType(from).argInfos.headOption.getOrElse(defn.NothingType)
490492
end elemType

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

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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

tests/neg/i23435-min.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//> using options -source:3.8
12

23
type Or[+A, +B] = A | B
34

tests/neg/i23435.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//> using options -source:3.8
12

23
trait L[+A]{val a:A}
34
trait R[+B]{val b: B}

tests/neg/singletonInterval.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//> using options -source:3.8
12

23
/** Why the singletonInterval logic cannot be applied for lubArgs and glbArgs in TypeComparer. */
34

tests/pos/i23435-min-3.7.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//> using options -source:3.7
2+
3+
type Or[+A, +B] = A | B
4+
5+
val x: Or[Int, String] & Or[String, Int] = 3
6+
val y: Or[Int & String, String & Int] = x // ok in 3.7, error in 3.8
7+
val z: String = y

0 commit comments

Comments
 (0)