@@ -9,7 +9,7 @@ import TypeUtils._
99import Contexts ._
1010import Flags ._
1111import ast ._
12- import Decorators ._
12+ import Decorators .{ show as * , * }
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,19 @@ 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 WildcardType
476+ else erase(arg, inArray = false , isValue = false )
477+ }
474478 tp.derivedAppliedType(erase(tycon, inArray, isValue = false ), args2)
475479
476480 case tp @ OrType (tp1, tp2) =>
@@ -488,8 +492,7 @@ object SpaceEngine {
488492 else WildcardType
489493
490494 case _ => tp
491- }
492- }
495+ })
493496
494497 /** Space of the pattern: unapplySeq(a, b, c: _*)
495498 */
@@ -873,16 +876,11 @@ object SpaceEngine {
873876 case _ => tp
874877 })
875878
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
879+ def checkExhaustivity (m : Match )(using Context ): Unit = if exhaustivityCheckable(m.selector) then trace(i " checkExhaustivity( $m) " , debug) {
880+ val selTyp = toUnderlying(m.selector.tpe).dealias
883881 debug.println(i " selTyp = $selTyp" )
884882
885- val patternSpace = Or (cases.foldLeft(List .empty[Space ]) { (acc, x) =>
883+ val patternSpace = Or (m. cases.foldLeft(List .empty[Space ]) { (acc, x) =>
886884 val space = if (x.guard.isEmpty) project(x.pat) else Empty
887885 debug.println(s " ${x.pat.show} ====> ${show(space)}" )
888886 space :: acc
@@ -899,7 +897,7 @@ object SpaceEngine {
899897 if uncovered.nonEmpty then
900898 val hasMore = uncovered.lengthCompare(6 ) > 0
901899 val deduped = dedup(uncovered.take(6 ))
902- report.warning(PatternMatchExhaustivity (showSpaces(deduped), hasMore), sel.srcPos )
900+ report.warning(PatternMatchExhaustivity (showSpaces(deduped), hasMore), m )
903901 }
904902
905903 private def redundancyCheckable (sel : Tree )(using Context ): Boolean =
@@ -912,14 +910,10 @@ object SpaceEngine {
912910 && ! sel.tpe.widen.isRef(defn.QuotedExprClass )
913911 && ! sel.tpe.widen.isRef(defn.QuotedTypeClass )
914912
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
913+ def checkRedundancy (m : Match )(using Context ): Unit = if redundancyCheckable(m.selector) then trace(i " checkRedundancy( $m) " , debug) {
914+ val cases = m.cases.toIndexedSeq
921915
922- val selTyp = toUnderlying(sel .tpe).dealias
916+ val selTyp = toUnderlying(m.selector .tpe).dealias
923917 debug.println(i " selTyp = $selTyp" )
924918
925919 val isNullable = selTyp.classSymbol.isNullableClass
@@ -953,6 +947,7 @@ object SpaceEngine {
953947 for (pat <- deferred.reverseIterator)
954948 report.warning(MatchCaseUnreachable (), pat.srcPos)
955949 if pat != EmptyTree // rethrow case of catch uses EmptyTree
950+ && ! pat.symbol.isAllOf(SyntheticCase , butNot= Method ) // ExpandSAMs default cases use SyntheticCase
956951 && isSubspace(covered, prev)
957952 then {
958953 val nullOnly = isNullable && i == len - 1 && isWildcardArg(pat)
0 commit comments