Skip to content

Commit fb86c05

Browse files
authored
Merge pull request #12549 from dotty-staging/fix-12475
2 parents 41d262b + d0097f7 commit fb86c05

File tree

13 files changed

+173
-26
lines changed

13 files changed

+173
-26
lines changed

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

+5
Original file line numberDiff line numberDiff line change
@@ -2588,6 +2588,11 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
25882588
provablyDisjoint(tp1, gadtBounds(tp2.symbol).hi) || provablyDisjoint(tp1, tp2.superType)
25892589
case (tp1: TermRef, tp2: TermRef) if isEnumValueOrModule(tp1) && isEnumValueOrModule(tp2) =>
25902590
tp1.termSymbol != tp2.termSymbol
2591+
case (tp1: TermRef, tp2: TypeRef) if isEnumValueOrModule(tp1) && !tp1.classSymbols.exists(_.derivesFrom(tp2.classSymbol)) =>
2592+
// Note: enum values may have multiple parents
2593+
true
2594+
case (tp1: TypeRef, tp2: TermRef) if isEnumValueOrModule(tp2) && !tp2.classSymbols.exists(_.derivesFrom(tp1.classSymbol)) =>
2595+
true
25912596
case (tp1: Type, tp2: Type) if defn.isTupleType(tp1) =>
25922597
provablyDisjoint(tp1.toNestedPairs, tp2)
25932598
case (tp1: Type, tp2: Type) if defn.isTupleType(tp2) =>

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

+15-6
Original file line numberDiff line numberDiff line change
@@ -691,15 +691,24 @@ object TypeOps:
691691
*/
692692
private def instantiateToSubType(tp1: NamedType, tp2: Type)(using Context): Type = {
693693
// In order for a child type S to qualify as a valid subtype of the parent
694-
// T, we need to test whether it is possible S <: T. Therefore, we replace
695-
// type parameters in T with tvars, and see if the subtyping is true.
696-
val approximateTypeParams = new TypeMap {
694+
// T, we need to test whether it is possible S <: T.
695+
//
696+
// The check is different from subtype checking due to type parameters and
697+
// `this`. We perform the following operations to approximate the parameters:
698+
//
699+
// 1. Replace type parameters in T with tvars
700+
// 2. Replace `A.this.C` with `A#C` (see tests/patmat/i12681.scala)
701+
//
702+
val approximateParent = new TypeMap {
697703
val boundTypeParams = util.HashMap[TypeRef, TypeVar]()
698704

699705
def apply(tp: Type): Type = tp.dealias match {
700706
case _: MatchType =>
701707
tp // break cycles
702708

709+
case ThisType(tref: TypeRef) if !tref.symbol.isStaticOwner =>
710+
tref
711+
703712
case tp: TypeRef if !tp.symbol.isClass =>
704713
def lo = LazyRef.of(apply(tp.underlying.loBound))
705714
def hi = LazyRef.of(apply(tp.underlying.hiBound))
@@ -786,7 +795,7 @@ object TypeOps:
786795
// we manually patch subtyping check instead of changing TypeComparer.
787796
// See tests/patmat/i3645b.scala
788797
def parentQualify(tp1: Type, tp2: Type) = tp1.classSymbol.info.parents.exists { parent =>
789-
parent.argInfos.nonEmpty && approximateTypeParams(parent) <:< tp2
798+
parent.argInfos.nonEmpty && approximateParent(parent) <:< tp2
790799
}
791800

792801
def instantiate(): Type = {
@@ -796,8 +805,8 @@ object TypeOps:
796805

797806
if (protoTp1 <:< tp2) instantiate()
798807
else {
799-
val protoTp2 = approximateTypeParams(tp2)
800-
if (protoTp1 <:< protoTp2 || parentQualify(protoTp1, protoTp2)) instantiate()
808+
val approxTp2 = approximateParent(tp2)
809+
if (protoTp1 <:< approxTp2 || parentQualify(protoTp1, approxTp2)) instantiate()
801810
else NoType
802811
}
803812
}

compiler/src/dotty/tools/dotc/transform/SymUtils.scala

+2
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ object SymUtils:
100100
def whyNotGenericSum(declScope: Symbol)(using Context): String =
101101
if (!self.is(Sealed))
102102
s"it is not a sealed ${self.kindString}"
103+
else if (!self.isOneOf(AbstractOrTrait))
104+
s"it is not an abstract class"
103105
else {
104106
val children = self.children
105107
val companionMirror = self.useCompanionAsMirror

compiler/src/dotty/tools/dotc/transform/patmat/Space.scala

+26-10
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ trait SpaceLogic {
105105
def signature(unapp: TermRef, scrutineeTp: Type, argLen: Int): List[Type]
106106

107107
/** Get components of decomposable types */
108-
def decompose(tp: Type): List[Space]
108+
def decompose(tp: Type): List[Typ]
109109

110110
/** Whether the extractor covers the given type */
111111
def covers(unapp: TermRef, scrutineeTp: Type): Boolean
@@ -176,6 +176,8 @@ trait SpaceLogic {
176176
ss.forall(isSubspace(_, b))
177177
case (Typ(tp1, _), Typ(tp2, _)) =>
178178
isSubType(tp1, tp2)
179+
|| canDecompose(tp1) && tryDecompose1(tp1)
180+
|| canDecompose(tp2) && tryDecompose2(tp2)
179181
case (Typ(tp1, _), Or(ss)) => // optimization: don't go to subtraction too early
180182
ss.exists(isSubspace(a, _)) || tryDecompose1(tp1)
181183
case (_, Or(_)) =>
@@ -337,9 +339,7 @@ class SpaceEngine(using Context) extends SpaceLogic {
337339
val res = TypeComparer.provablyDisjoint(tp1, tp2)
338340

339341
if (res) Empty
340-
else if (tp1.isSingleton) Typ(tp1, true)
341-
else if (tp2.isSingleton) Typ(tp2, true)
342-
else Typ(AndType(tp1, tp2), true)
342+
else Typ(AndType(tp1, tp2), decomposed = true)
343343
}
344344
}
345345

@@ -591,14 +591,27 @@ class SpaceEngine(using Context) extends SpaceLogic {
591591
}
592592

593593
/** Decompose a type into subspaces -- assume the type can be decomposed */
594-
def decompose(tp: Type): List[Space] =
594+
def decompose(tp: Type): List[Typ] =
595595
tp.dealias match {
596596
case AndType(tp1, tp2) =>
597-
intersect(Typ(tp1, false), Typ(tp2, false)) match {
598-
case Or(spaces) => spaces.toList
599-
case Empty => Nil
600-
case space => List(space)
601-
}
597+
def decomposeComponent(tpA: Type, tpB: Type): List[Typ] =
598+
decompose(tpA).flatMap {
599+
case Typ(tp, _) =>
600+
if tp <:< tpB then
601+
Typ(tp, decomposed = true) :: Nil
602+
else if tpB <:< tp then
603+
Typ(tpB, decomposed = true) :: Nil
604+
else if TypeComparer.provablyDisjoint(tp, tpB) then
605+
Nil
606+
else
607+
Typ(AndType(tp, tpB), decomposed = true) :: Nil
608+
}
609+
610+
if canDecompose(tp1) then
611+
decomposeComponent(tp1, tp2)
612+
else
613+
decomposeComponent(tp2, tp1)
614+
602615
case OrType(tp1, tp2) => List(Typ(tp1, true), Typ(tp2, true))
603616
case tp if tp.isRef(defn.BooleanClass) =>
604617
List(
@@ -833,6 +846,9 @@ class SpaceEngine(using Context) extends SpaceLogic {
833846

834847
if (!exhaustivityCheckable(sel)) return
835848

849+
debug.println("checking " + _match.show)
850+
debug.println("selTyp = " + selTyp.show)
851+
836852
val patternSpace = Or(cases.foldLeft(List.empty[Space]) { (acc, x) =>
837853
val space = if (x.guard.isEmpty) project(x.pat) else Empty
838854
debug.println(s"${x.pat.show} ====> ${show(space)}")

scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala

+3-10
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,8 @@ trait TypesSupport:
2727
case ThisType(tpe) => inner(tpe)
2828
case AnnotatedType(tpe, _) => inner(tpe)
2929
case AppliedType(tpe, _) => inner(tpe)
30-
case tp @ TermRef(qual, typeName) =>
31-
qual match
32-
case _: TypeRepr | _: NoPrefix => Some(tp.termSymbol)
33-
case other => None
34-
case tp @ TypeRef(qual, typeName) =>
35-
qual match
36-
case _: TypeRepr | _: NoPrefix => Some(tp.typeSymbol)
37-
case other => None
30+
case tp @ TermRef(qual, typeName) => Some(tp.termSymbol)
31+
case tp @ TypeRef(qual, typeName) => Some(tp.typeSymbol)
3832

3933
val typeSymbol = extractTypeSymbol(method.returnTpt)
4034

@@ -204,8 +198,7 @@ trait TypesSupport:
204198
case tp @ TypeRef(qual, typeName) =>
205199
qual match {
206200
case r: RecursiveThis => texts(s"this.$typeName")
207-
case _: TypeRepr | _: NoPrefix => link(tp.typeSymbol)
208-
case other => noSupported(s"TypeRepr: $tp")
201+
case _: TypeRepr => link(tp.typeSymbol)
209202
}
210203
// convertTypeOrBoundsToReference(reflect)(qual) match {
211204
// case TypeReference(label, link, xs, _) => TypeReference(typeName, link + "/" + label, xs, true)

tests/patmat/i10667.scala

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
sealed trait A
2+
3+
enum Nums {
4+
case One
5+
case Two extends Nums with A
6+
case Three
7+
}
8+
9+
object Test {
10+
val list = List[Nums & A](Nums.Two)
11+
12+
list.map {
13+
case Nums.Two => ()
14+
}
15+
}

tests/patmat/i12475.scala

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
sealed trait Ty {
2+
type T
3+
}
4+
5+
class TUnit() extends Ty {
6+
type T = Unit
7+
}
8+
9+
case object TUnit extends TUnit()
10+
11+
final case class TFun(dom: Ty, cod: Ty) extends Ty {
12+
type T = dom.T => cod.T
13+
}
14+
15+
def default(ty: Ty): ty.T = (ty: ty.type & Ty) match {
16+
case a: (ty.type & TUnit) => (): a.T
17+
case a: (ty.type & TFun) =>
18+
val f = { (x: a.dom.T) => default(a.cod) }
19+
f: a.T
20+
}

tests/patmat/i12475b.scala

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
trait SomeRestriction
2+
3+
enum ADT {
4+
case A
5+
case B extends ADT with SomeRestriction
6+
}
7+
8+
object MinimalExample {
9+
val b: ADT & SomeRestriction = ADT.B
10+
11+
b match {
12+
case ADT.B => ???
13+
}
14+
}

tests/patmat/i12546.scala

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
trait SomeRestriction
2+
3+
enum ADT {
4+
case A extends ADT
5+
case B extends ADT with SomeRestriction
6+
}
7+
8+
object MinimalExample {
9+
val b: ADT & SomeRestriction = ADT.B
10+
11+
b match {
12+
case ADT.B => ???
13+
}
14+
}

tests/patmat/i12559.check

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
10: Match case Unreachable
2+
27: Match case Unreachable

tests/patmat/i12559.scala

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package akka.event
2+
3+
object TestA:
4+
sealed trait LogEvent
5+
6+
object LogEvent:
7+
def myOrdinal(e: LogEvent): Int = e match
8+
case e: Error => 0
9+
// case e: Warning => 1
10+
case e: LogEventWithMarker => 2
11+
12+
13+
class Error() extends LogEvent
14+
class Error2() extends Error() with LogEventWithMarker
15+
16+
// case class Warning() extends LogEvent
17+
18+
sealed trait LogEventWithMarker extends LogEvent
19+
20+
object TestB:
21+
sealed trait LogEvent
22+
23+
object LogEvent:
24+
def myOrdinal(e: LogEvent): Int = e match
25+
case e: Error => 0
26+
case e: Warning => 1
27+
case e: LogEventWithMarker => 2
28+
29+
30+
case class Error() extends LogEvent
31+
class Error2() extends Error() with LogEventWithMarker
32+
33+
case class Warning() extends LogEvent
34+
35+
sealed trait LogEventWithMarker extends LogEvent

tests/patmat/i12602.scala

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
sealed class Foo[T]
2+
object Foo extends Foo[Nothing]

tests/patmat/i12681.scala

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
object Examples {
2+
3+
case class Leaf1() extends i.Root
4+
case class Leaf2() extends i.Branch
5+
6+
val i = new Inner()
7+
8+
class Inner {
9+
10+
sealed trait Root
11+
sealed trait Branch extends Root
12+
13+
// simulate ordinal method of a Mirror.SumOf generated at this call site
14+
def myOrdinal(r: Root): Int = r match {
15+
case _: Examples.Leaf1 => 0
16+
case _: Inner.this.Branch => 1
17+
}
18+
}
19+
20+
}

0 commit comments

Comments
 (0)