@@ -9,7 +9,7 @@ import TypeUtils._
99import Contexts ._
1010import Flags ._
1111import ast ._
12- import Decorators ._
12+ import Decorators .{ show => _ , * }
1313import Symbols ._
1414import StdNames ._
1515import NameOps ._
@@ -26,6 +26,8 @@ import util.{SrcPos, NoSourcePosition}
2626import scala .annotation .internal .sharable
2727import scala .collection .mutable
2828
29+ import SpaceEngine .*
30+
2931/* Space logic for checking exhaustivity and unreachability of pattern matching
3032 *
3133 * Space can be thought of as a set of possible values. A type or a pattern
@@ -57,7 +59,6 @@ import scala.collection.mutable
5759
5860/** space definition */
5961sealed trait Space :
60- import SpaceEngine .*
6162
6263 @ sharable private val isSubspaceCache = mutable.HashMap .empty[Space , Boolean ]
6364
@@ -95,14 +96,14 @@ case object Empty extends Space
9596case class Typ (tp : Type , decomposed : Boolean = true ) extends Space :
9697 private var myDecompose : List [Typ ] | Null = null
9798
98- def canDecompose (using Context ): Boolean = decompose != SpaceEngine . ListOfTypNoType
99+ def canDecompose (using Context ): Boolean = decompose != ListOfTypNoType
99100
100101 def decompose (using Context ): List [Typ ] =
101102 val decompose = myDecompose
102103 if decompose == null then
103104 val decompose = tp match
104- case SpaceEngine . Parts (parts) => parts.map(Typ (_, decomposed = true ))
105- case _ => SpaceEngine . ListOfTypNoType
105+ case Parts (parts) => parts.map(Typ (_, decomposed = true ))
106+ case _ => ListOfTypNoType
106107 myDecompose = decompose
107108 decompose
108109 else decompose
@@ -346,7 +347,7 @@ object SpaceEngine {
346347 }
347348
348349 /** Return the space that represents the pattern `pat` */
349- def project (pat : Tree )(using Context ): Space = pat match {
350+ def project (pat : Tree )(using Context ): Space = trace( i " project( $pat ${pat.className} ${pat.tpe} ) " , debug, show)( pat match {
350351 case Literal (c) =>
351352 if (c.value.isInstanceOf [Symbol ])
352353 Typ (c.value.asInstanceOf [Symbol ].termRef, decomposed = false )
@@ -407,7 +408,7 @@ object SpaceEngine {
407408 case _ =>
408409 // Pattern is an arbitrary expression; assume a skolem (i.e. an unknown value) of the pattern type
409410 Typ (pat.tpe.narrow, decomposed = false )
410- }
411+ })
411412
412413 private def project (tp : Type )(using Context ): Space = tp match {
413414 case OrType (tp1, tp2) => Or (project(tp1) :: project(tp2) :: Nil )
@@ -461,16 +462,23 @@ object SpaceEngine {
461462 * If `isValue` is true, then pattern-bound symbols are erased to its upper bound.
462463 * This is needed to avoid spurious unreachable warnings. See tests/patmat/i6197.scala.
463464 */
464- private def erase (tp : Type , inArray : Boolean = false , isValue : Boolean = false )(using Context ): Type = trace(i " $tp erased to " , debug) {
465-
466- tp match {
465+ private def erase (tp : Type , inArray : Boolean = false , isValue : Boolean = false )(using Context ): Type =
466+ trace(i " erase( $tp${if inArray then " inArray" else " " }${if isValue then " isValue" else " " }) " , debug)(tp match {
467467 case tp @ AppliedType (tycon, args) if tycon.typeSymbol.isPatternBound =>
468468 WildcardType
469469
470470 case tp @ AppliedType (tycon, args) =>
471471 val args2 =
472- if (tycon.isRef(defn.ArrayClass )) args.map(arg => erase(arg, inArray = true , isValue = false ))
473- else args.map(arg => erase(arg, inArray = false , isValue = false ))
472+ if tycon.isRef(defn.ArrayClass ) then
473+ args.map(arg => erase(arg, inArray = true , isValue = false ))
474+ else tycon.typeParams.lazyZip(args).map { (tparam, arg) =>
475+ if isValue && tparam.paramVarianceSign == 0 then
476+ // when matching against a value,
477+ // any type argument for an invariant type parameter will be unchecked,
478+ // meaning it won't fail to match against anything; thus the wildcard replacement
479+ WildcardType
480+ else erase(arg, inArray = false , isValue = false )
481+ }
474482 tp.derivedAppliedType(erase(tycon, inArray, isValue = false ), args2)
475483
476484 case tp @ OrType (tp1, tp2) =>
@@ -488,8 +496,7 @@ object SpaceEngine {
488496 else WildcardType
489497
490498 case _ => tp
491- }
492- }
499+ })
493500
494501 /** Space of the pattern: unapplySeq(a, b, c: _*)
495502 */
@@ -873,16 +880,11 @@ object SpaceEngine {
873880 case _ => tp
874881 })
875882
876- def checkExhaustivity (_match : Match )(using Context ): Unit = {
877- val Match (sel, cases) = _match
878- debug.println(i " checking exhaustivity of ${_match}" )
879-
880- if (! exhaustivityCheckable(sel)) return
881-
882- val selTyp = toUnderlying(sel.tpe).dealias
883+ def checkExhaustivity (m : Match )(using Context ): Unit = if exhaustivityCheckable(m.selector) then trace(i " checkExhaustivity( $m) " , debug) {
884+ val selTyp = toUnderlying(m.selector.tpe).dealias
883885 debug.println(i " selTyp = $selTyp" )
884886
885- val patternSpace = Or (cases.foldLeft(List .empty[Space ]) { (acc, x) =>
887+ val patternSpace = Or (m. cases.foldLeft(List .empty[Space ]) { (acc, x) =>
886888 val space = if (x.guard.isEmpty) project(x.pat) else Empty
887889 debug.println(s " ${x.pat.show} ====> ${show(space)}" )
888890 space :: acc
@@ -899,7 +901,7 @@ object SpaceEngine {
899901 if uncovered.nonEmpty then
900902 val hasMore = uncovered.lengthCompare(6 ) > 0
901903 val deduped = dedup(uncovered.take(6 ))
902- report.warning(PatternMatchExhaustivity (showSpaces(deduped), hasMore), sel.srcPos )
904+ report.warning(PatternMatchExhaustivity (showSpaces(deduped), hasMore), m.selector )
903905 }
904906
905907 private def redundancyCheckable (sel : Tree )(using Context ): Boolean =
@@ -912,14 +914,10 @@ object SpaceEngine {
912914 && ! sel.tpe.widen.isRef(defn.QuotedExprClass )
913915 && ! sel.tpe.widen.isRef(defn.QuotedTypeClass )
914916
915- def checkRedundancy (_match : Match )(using Context ): Unit = {
916- val Match (sel, _) = _match
917- val cases = _match.cases.toIndexedSeq
918- debug.println(i " checking redundancy in $_match" )
919-
920- if (! redundancyCheckable(sel)) return
917+ def checkRedundancy (m : Match )(using Context ): Unit = if redundancyCheckable(m.selector) then trace(i " checkRedundancy( $m) " , debug) {
918+ val cases = m.cases.toIndexedSeq
921919
922- val selTyp = toUnderlying(sel .tpe).dealias
920+ val selTyp = toUnderlying(m.selector .tpe).dealias
923921 debug.println(i " selTyp = $selTyp" )
924922
925923 val isNullable = selTyp.classSymbol.isNullableClass
@@ -953,6 +951,7 @@ object SpaceEngine {
953951 for (pat <- deferred.reverseIterator)
954952 report.warning(MatchCaseUnreachable (), pat.srcPos)
955953 if pat != EmptyTree // rethrow case of catch uses EmptyTree
954+ && ! pat.symbol.isAllOf(SyntheticCase , butNot= Method ) // ExpandSAMs default cases use SyntheticCase
956955 && isSubspace(covered, prev)
957956 then {
958957 val nullOnly = isNullable && i == len - 1 && isWildcardArg(pat)
0 commit comments