Skip to content

Commit

Permalink
Make sure in CI that we do not unexpectedly fall back on legacy match…
Browse files Browse the repository at this point in the history
… 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.
  • Loading branch information
sjrd committed Aug 10, 2023
1 parent b72458d commit 722d4cb
Show file tree
Hide file tree
Showing 21 changed files with 119 additions and 4 deletions.
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ private sealed trait YSettings:
val YprintPos: Setting[Boolean] = BooleanSetting("-Yprint-pos", "Show tree positions.")
val YprintPosSyms: Setting[Boolean] = BooleanSetting("-Yprint-pos-syms", "Show symbol definitions positions.")
val YnoDeepSubtypes: Setting[Boolean] = BooleanSetting("-Yno-deep-subtypes", "Throw an exception on deep subtyping call stacks.")
val YnoLegacyMatchTypes: Setting[Boolean] = BooleanSetting("-Yno-legacy-match-types", "Refuse to reduce match types with legacy/unspecified patterns")
val YnoPatmatOpt: Setting[Boolean] = BooleanSetting("-Yno-patmat-opt", "Disable all pattern matching optimizations.")
val YplainPrinter: Setting[Boolean] = BooleanSetting("-Yplain-printer", "Pretty-print using a plain printer.")
val YprintSyms: Setting[Boolean] = BooleanSetting("-Yprint-syms", "When printing trees print info in symbols instead of corresponding info in trees.")
Expand Down
4 changes: 4 additions & 0 deletions compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,8 @@ object MatchTypeTrace:
|
| ${casesText(cases)}"""

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

end MatchTypeTrace
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3425,6 +3425,9 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
MatchResult.Stuck

def recur(remaining: List[MatchTypeCaseSpec]): Type = remaining match
case (cas: MatchTypeCaseSpec.LegacyPatMat) :: _ if ctx.settings.YnoLegacyMatchTypes.value =>
val errorText = MatchTypeTrace.legacyPatternText(scrut, cas)
ErrorType(reporting.MatchTypeLegacyPattern(errorText))
case cas :: remaining1 =>
matchCase(cas) match
case MatchResult.Disjoint =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
case ImplausiblePatternWarningID // erorNumber: 186
case SynchronizedCallOnBoxedClassID // errorNumber: 187
case VarArgsParamCannotBeGivenID // erorNumber: 188
case MatchTypeLegacyPatternID // errorNumber: 189

def errorNumber = ordinal - 1

Expand Down
4 changes: 4 additions & 0 deletions compiler/src/dotty/tools/dotc/reporting/messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3024,6 +3024,10 @@ class MatchTypeScrutineeCannotBeHigherKinded(tp: Type)(using Context)
def msg(using Context) = i"the scrutinee of a match type cannot be higher-kinded"
def explain(using Context) = ""

class MatchTypeLegacyPattern(errorText: String)(using Context) extends TypeMsg(MatchTypeLegacyPatternID):
def msg(using Context) = errorText
def explain(using Context) = ""

class ClosureCannotHaveInternalParameterDependencies(mt: Type)(using Context)
extends TypeMsg(ClosureCannotHaveInternalParameterDependenciesID):
def msg(using Context) =
Expand Down
1 change: 1 addition & 0 deletions compiler/test/dotty/tools/vulpix/TestConfiguration.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ object TestConfiguration {
val checkOptions = Array(
// "-Yscala2-unpickler", s"${Properties.scalaLibrary}",
"-Yno-deep-subtypes",
"-Yno-legacy-match-types",
"-Yno-double-bindings",
"-Yforce-sbt-phases",
"-Xsemanticdb",
Expand Down
2 changes: 2 additions & 0 deletions tests/neg/6570.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// scalac: -Yno-legacy-match-types:false

object Base {
trait Trait1
trait Trait2
Expand Down
30 changes: 30 additions & 0 deletions tests/neg/legacy-match-types.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-- [E189] Type Error: tests/neg/legacy-match-types.scala:7:23 ----------------------------------------------------------
7 |type InvNesting[X] = X match // error
| ^
| Illegal match type because it contains the legacy, unspecifed case
| case Inv[Cov[t]] => t
8 | case Inv[Cov[t]] => t
-- [E189] Type Error: tests/neg/legacy-match-types.scala:10:26 ---------------------------------------------------------
10 |type ContraNesting[X] = X match // error
| ^
| Illegal match type because it contains the legacy, unspecifed case
| case Contra[Cov[t]] => t
11 | case Contra[Cov[t]] => t
-- [E189] Type Error: tests/neg/legacy-match-types.scala:15:22 ---------------------------------------------------------
15 |type AndTypeMT[X] = X match // error
| ^
| Illegal match type because it contains the legacy, unspecifed case
| case t & Seq[Any] => t
16 | case t & Seq[Any] => t
-- [E189] Type Error: tests/neg/legacy-match-types.scala:22:33 ---------------------------------------------------------
22 |type TypeAliasWithBoundMT[X] = X match // error
| ^
| Illegal match type because it contains the legacy, unspecifed case
| case IsSeq[t] => t
23 | case IsSeq[t] => t
-- [E189] Type Error: tests/neg/legacy-match-types.scala:33:34 ---------------------------------------------------------
33 |type TypeMemberExtractorMT[X] = X match // error
| ^
| Illegal match type because it contains the legacy, unspecifed case
| case TypeMemberAux[t] => t
34 | case TypeMemberAux[t] => t
34 changes: 34 additions & 0 deletions tests/neg/legacy-match-types.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
class Inv[T]
class Cov[+T]
class Contra[-T]

// Nesting captures in non-covariant position

type InvNesting[X] = X match // error
case Inv[Cov[t]] => t

type ContraNesting[X] = X match // error
case Contra[Cov[t]] => t

// Intersection type to type-test and capture at the same time

type AndTypeMT[X] = X match // error
case t & Seq[Any] => t

// Poly type alias with a bound to type-test and capture at the same time

type IsSeq[X <: Seq[Any]] = X

type TypeAliasWithBoundMT[X] = X match // error
case IsSeq[t] => t

// Poly type alias with a type member refinement to extract the type member

class Base {
type TypeMember
}

type TypeMemberAux[X] = Base { type TypeMember = X }

type TypeMemberExtractorMT[X] = X match // error
case TypeMemberAux[t] => t
13 changes: 13 additions & 0 deletions tests/pos/10747-shapeless-min-spec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
trait Monoidal {
type to[_] <: Tuple
}

object eithers extends Monoidal {
class Wrap[T]

type to[t] <: Tuple = Wrap[t] match {
case Wrap[Nothing] => EmptyTuple
case Wrap[other] => other match
case Either[hd, tl] => hd *: to[tl]
}
}
2 changes: 2 additions & 0 deletions tests/pos/10747-shapeless-min.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// scalac: -Yno-legacy-match-types:false

trait Monoidal {
type to[_] <: Tuple
}
Expand Down
2 changes: 2 additions & 0 deletions tests/pos/8647.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// scalac: -Yno-legacy-match-types:false

final class Two[A, B]()

final class Blaaa
Expand Down
2 changes: 2 additions & 0 deletions tests/pos/9757.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// scalac: -Yno-legacy-match-types:false

type RemoveFrom[R, A] = R match {
case A & newType => newType
}
Expand Down
4 changes: 3 additions & 1 deletion tests/pos/i10242.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// https://github.com/lampepfl/dotty/issues/10242
// scalac: -Yno-legacy-match-types:false

// https://github.com/lampepfl/dotty/issues/10242
type Foo[A, B <: A] = A

type Bar[A] = A match {
Expand Down
4 changes: 3 additions & 1 deletion tests/pos/i15155.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// scalac: -Yno-legacy-match-types:false

import scala.reflect.ClassTag
// https://github.com/json4s/json4s/blob/355d8751391773e0d79d04402a4f9fb7bfc684ec/ext/src/main/scala-3/org/json4s/ext/package.scala#L4-L8
type Aux[A] = { type Value = A }
Expand All @@ -8,4 +10,4 @@ type EnumValue[A <: Enumeration] = A match {
// https://github.com/json4s/json4s/blob/355d8751391773e0d79d04402a4f9fb7bfc684ec/ext/src/main/scala/org/json4s/ext/EnumSerializer.scala#L25-L26
class EnumSerializer[E <: Enumeration: ClassTag](enumeration: E) {
val EnumerationClass = classOf[EnumValue[E]]
}
}
2 changes: 2 additions & 0 deletions tests/pos/i16408.min1.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// scalac: -Yno-legacy-match-types:false

object Helpers:
type NodeFun[R] = Matchable // compiles without [R] parameter

Expand Down
2 changes: 2 additions & 0 deletions tests/pos/i16408.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// scalac: -Yno-legacy-match-types:false

import scala.util.Try

trait RDF:
Expand Down
4 changes: 3 additions & 1 deletion tests/pos/i16706.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// scalac: -Yno-legacy-match-types:false

import scala.deriving.Mirror
import scala.reflect.ClassTag

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

val instance = derived[Foo] // error
val instance = derived[Foo] // error
2 changes: 2 additions & 0 deletions tests/pos/i17395.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// scalac: -Yno-legacy-match-types:false

trait TC[T]

object TC {
Expand Down
4 changes: 3 additions & 1 deletion tests/pos/i5625b.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// scalac: -Yno-legacy-match-types:false

object Test {

type AV[t <: AnyVal] = t
Expand All @@ -13,4 +15,4 @@ object Test {
summon[LeafElem[Array[Int]] =:= Int]
summon[LeafElem[Iterable[Int]] =:= Int]
summon[LeafElem[Int] =:= Int]
}
}
2 changes: 2 additions & 0 deletions tests/run-macros/tasty-simplified/quoted_2.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// scalac: -Yno-legacy-match-types:false

import Macros.simplified

object Test {
Expand Down

0 comments on commit 722d4cb

Please sign in to comment.