Skip to content

Commit 9c19ada

Browse files
committed
Make sure in CI that we do not unexpectedly fall back on legacy match type reduction.
We introduce a new flag `-Yno-legacy-match-types`, which forbids the reduction of "legacy" match types. Like `-Yno-deep-subtypes`, it is meant to be used in our CI. With it, we check that we do not unexpectedly fall back on legacy match types in tests for which the specced match types are enough. Later, we should consider integrating that behavior with the source level so that it reaches users.
1 parent 628e50f commit 9c19ada

21 files changed

+119
-4
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ private sealed trait YSettings:
347347
val YprintPos: Setting[Boolean] = BooleanSetting("-Yprint-pos", "Show tree positions.")
348348
val YprintPosSyms: Setting[Boolean] = BooleanSetting("-Yprint-pos-syms", "Show symbol definitions positions.")
349349
val YnoDeepSubtypes: Setting[Boolean] = BooleanSetting("-Yno-deep-subtypes", "Throw an exception on deep subtyping call stacks.")
350+
val YnoLegacyMatchTypes: Setting[Boolean] = BooleanSetting("-Yno-legacy-match-types", "Refuse to reduce match types with legacy/unspecified patterns")
350351
val YnoPatmatOpt: Setting[Boolean] = BooleanSetting("-Yno-patmat-opt", "Disable all pattern matching optimizations.")
351352
val YplainPrinter: Setting[Boolean] = BooleanSetting("-Yplain-printer", "Pretty-print using a plain printer.")
352353
val YprintSyms: Setting[Boolean] = BooleanSetting("-Yprint-syms", "When printing trees print info in symbols instead of corresponding info in trees.")

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,8 @@ object MatchTypeTrace:
125125
|
126126
| ${casesText(cases)}"""
127127

128+
def legacyPatternText(scrut: Type, cas: MatchTypeCaseSpec.LegacyPatMat)(using Context): String =
129+
i"""Illegal match type because it contains the legacy, unspecifed case
130+
| ${caseText(cas)}"""
131+
128132
end MatchTypeTrace

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3428,6 +3428,9 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
34283428
MatchResult.Stuck
34293429

34303430
def recur(remaining: List[MatchTypeCaseSpec]): Type = remaining match
3431+
case (cas: MatchTypeCaseSpec.LegacyPatMat) :: _ if ctx.settings.YnoLegacyMatchTypes.value =>
3432+
val errorText = MatchTypeTrace.legacyPatternText(scrut, cas)
3433+
ErrorType(reporting.MatchTypeLegacyPattern(errorText))
34313434
case cas :: remaining1 =>
34323435
matchCase(cas) match
34333436
case MatchResult.Disjoint =>

compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
202202
case ImplausiblePatternWarningID // erorNumber: 186
203203
case SynchronizedCallOnBoxedClassID // errorNumber: 187
204204
case VarArgsParamCannotBeGivenID // erorNumber: 188
205+
case MatchTypeLegacyPatternID // errorNumber: 189
205206

206207
def errorNumber = ordinal - 1
207208

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3024,6 +3024,10 @@ class MatchTypeScrutineeCannotBeHigherKinded(tp: Type)(using Context)
30243024
def msg(using Context) = i"the scrutinee of a match type cannot be higher-kinded"
30253025
def explain(using Context) = ""
30263026

3027+
class MatchTypeLegacyPattern(errorText: String)(using Context) extends TypeMsg(MatchTypeLegacyPatternID):
3028+
def msg(using Context) = errorText
3029+
def explain(using Context) = ""
3030+
30273031
class ClosureCannotHaveInternalParameterDependencies(mt: Type)(using Context)
30283032
extends TypeMsg(ClosureCannotHaveInternalParameterDependenciesID):
30293033
def msg(using Context) =

compiler/test/dotty/tools/vulpix/TestConfiguration.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ object TestConfiguration {
1919
val checkOptions = Array(
2020
// "-Yscala2-unpickler", s"${Properties.scalaLibrary}",
2121
"-Yno-deep-subtypes",
22+
"-Yno-legacy-match-types",
2223
"-Yno-double-bindings",
2324
"-Yforce-sbt-phases",
2425
"-Xsemanticdb",

tests/neg/6570.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// scalac: -Yno-legacy-match-types:false
2+
13
object Base {
24
trait Trait1
35
trait Trait2

tests/neg/legacy-match-types.check

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
-- [E189] Type Error: tests/neg/legacy-match-types.scala:7:23 ----------------------------------------------------------
2+
7 |type InvNesting[X] = X match // error
3+
| ^
4+
| Illegal match type because it contains the legacy, unspecifed case
5+
| case Inv[Cov[t]] => t
6+
8 | case Inv[Cov[t]] => t
7+
-- [E189] Type Error: tests/neg/legacy-match-types.scala:10:26 ---------------------------------------------------------
8+
10 |type ContraNesting[X] = X match // error
9+
| ^
10+
| Illegal match type because it contains the legacy, unspecifed case
11+
| case Contra[Cov[t]] => t
12+
11 | case Contra[Cov[t]] => t
13+
-- [E189] Type Error: tests/neg/legacy-match-types.scala:15:22 ---------------------------------------------------------
14+
15 |type AndTypeMT[X] = X match // error
15+
| ^
16+
| Illegal match type because it contains the legacy, unspecifed case
17+
| case t & Seq[Any] => t
18+
16 | case t & Seq[Any] => t
19+
-- [E189] Type Error: tests/neg/legacy-match-types.scala:22:33 ---------------------------------------------------------
20+
22 |type TypeAliasWithBoundMT[X] = X match // error
21+
| ^
22+
| Illegal match type because it contains the legacy, unspecifed case
23+
| case IsSeq[t] => t
24+
23 | case IsSeq[t] => t
25+
-- [E189] Type Error: tests/neg/legacy-match-types.scala:33:34 ---------------------------------------------------------
26+
33 |type TypeMemberExtractorMT[X] = X match // error
27+
| ^
28+
| Illegal match type because it contains the legacy, unspecifed case
29+
| case TypeMemberAux[t] => t
30+
34 | case TypeMemberAux[t] => t

tests/neg/legacy-match-types.scala

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
class Inv[T]
2+
class Cov[+T]
3+
class Contra[-T]
4+
5+
// Nesting captures in non-covariant position
6+
7+
type InvNesting[X] = X match // error
8+
case Inv[Cov[t]] => t
9+
10+
type ContraNesting[X] = X match // error
11+
case Contra[Cov[t]] => t
12+
13+
// Intersection type to type-test and capture at the same time
14+
15+
type AndTypeMT[X] = X match // error
16+
case t & Seq[Any] => t
17+
18+
// Poly type alias with a bound to type-test and capture at the same time
19+
20+
type IsSeq[X <: Seq[Any]] = X
21+
22+
type TypeAliasWithBoundMT[X] = X match // error
23+
case IsSeq[t] => t
24+
25+
// Poly type alias with a type member refinement to extract the type member
26+
27+
class Base {
28+
type TypeMember
29+
}
30+
31+
type TypeMemberAux[X] = Base { type TypeMember = X }
32+
33+
type TypeMemberExtractorMT[X] = X match // error
34+
case TypeMemberAux[t] => t
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
trait Monoidal {
2+
type to[_] <: Tuple
3+
}
4+
5+
object eithers extends Monoidal {
6+
class Wrap[T]
7+
8+
type to[t] <: Tuple = Wrap[t] match {
9+
case Wrap[Nothing] => EmptyTuple
10+
case Wrap[other] => other match
11+
case Either[hd, tl] => hd *: to[tl]
12+
}
13+
}

tests/pos/10747-shapeless-min.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// scalac: -Yno-legacy-match-types:false
2+
13
trait Monoidal {
24
type to[_] <: Tuple
35
}

tests/pos/8647.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// scalac: -Yno-legacy-match-types:false
2+
13
final class Two[A, B]()
24

35
final class Blaaa

tests/pos/9757.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// scalac: -Yno-legacy-match-types:false
2+
13
type RemoveFrom[R, A] = R match {
24
case A & newType => newType
35
}

tests/pos/i10242.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
// https://github.com/lampepfl/dotty/issues/10242
1+
// scalac: -Yno-legacy-match-types:false
2+
3+
// https://github.com/lampepfl/dotty/issues/10242
24
type Foo[A, B <: A] = A
35

46
type Bar[A] = A match {

tests/pos/i15155.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// scalac: -Yno-legacy-match-types:false
2+
13
import scala.reflect.ClassTag
24
// https://github.com/json4s/json4s/blob/355d8751391773e0d79d04402a4f9fb7bfc684ec/ext/src/main/scala-3/org/json4s/ext/package.scala#L4-L8
35
type Aux[A] = { type Value = A }
@@ -8,4 +10,4 @@ type EnumValue[A <: Enumeration] = A match {
810
// https://github.com/json4s/json4s/blob/355d8751391773e0d79d04402a4f9fb7bfc684ec/ext/src/main/scala/org/json4s/ext/EnumSerializer.scala#L25-L26
911
class EnumSerializer[E <: Enumeration: ClassTag](enumeration: E) {
1012
val EnumerationClass = classOf[EnumValue[E]]
11-
}
13+
}

tests/pos/i16408.min1.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// scalac: -Yno-legacy-match-types:false
2+
13
object Helpers:
24
type NodeFun[R] = Matchable // compiles without [R] parameter
35

tests/pos/i16408.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// scalac: -Yno-legacy-match-types:false
2+
13
import scala.util.Try
24

35
trait RDF:

tests/pos/i16706.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// scalac: -Yno-legacy-match-types:false
2+
13
import scala.deriving.Mirror
24
import scala.reflect.ClassTag
35

@@ -14,4 +16,4 @@ transparent inline given derived[A](
1416
sealed trait Foo
1517
case class FooA(a: Int) extends Foo
1618

17-
val instance = derived[Foo] // error
19+
val instance = derived[Foo] // error

tests/pos/i17395.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// scalac: -Yno-legacy-match-types:false
2+
13
trait TC[T]
24

35
object TC {

tests/pos/i5625b.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// scalac: -Yno-legacy-match-types:false
2+
13
object Test {
24

35
type AV[t <: AnyVal] = t
@@ -13,4 +15,4 @@ object Test {
1315
summon[LeafElem[Array[Int]] =:= Int]
1416
summon[LeafElem[Iterable[Int]] =:= Int]
1517
summon[LeafElem[Int] =:= Int]
16-
}
18+
}

tests/run-macros/tasty-simplified/quoted_2.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// scalac: -Yno-legacy-match-types:false
2+
13
import Macros.simplified
24

35
object Test {

0 commit comments

Comments
 (0)