@@ -9,7 +9,7 @@ import TypeUtils._
9
9
import Contexts ._
10
10
import Flags ._
11
11
import ast ._
12
- import Decorators ._
12
+ import Decorators .{ show => _ , * }
13
13
import Symbols ._
14
14
import StdNames ._
15
15
import NameOps ._
@@ -26,6 +26,8 @@ import util.{SrcPos, NoSourcePosition}
26
26
import scala .annotation .internal .sharable
27
27
import scala .collection .mutable
28
28
29
+ import SpaceEngine .*
30
+
29
31
/* Space logic for checking exhaustivity and unreachability of pattern matching
30
32
*
31
33
* Space can be thought of as a set of possible values. A type or a pattern
@@ -57,7 +59,6 @@ import scala.collection.mutable
57
59
58
60
/** space definition */
59
61
sealed trait Space :
60
- import SpaceEngine .*
61
62
62
63
@ sharable private val isSubspaceCache = mutable.HashMap .empty[Space , Boolean ]
63
64
@@ -95,14 +96,14 @@ case object Empty extends Space
95
96
case class Typ (tp : Type , decomposed : Boolean = true ) extends Space :
96
97
private var myDecompose : List [Typ ] | Null = null
97
98
98
- def canDecompose (using Context ): Boolean = decompose != SpaceEngine . ListOfTypNoType
99
+ def canDecompose (using Context ): Boolean = decompose != ListOfTypNoType
99
100
100
101
def decompose (using Context ): List [Typ ] =
101
102
val decompose = myDecompose
102
103
if decompose == null then
103
104
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
106
107
myDecompose = decompose
107
108
decompose
108
109
else decompose
@@ -346,7 +347,7 @@ object SpaceEngine {
346
347
}
347
348
348
349
/** 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 {
350
351
case Literal (c) =>
351
352
if (c.value.isInstanceOf [Symbol ])
352
353
Typ (c.value.asInstanceOf [Symbol ].termRef, decomposed = false )
@@ -407,7 +408,7 @@ object SpaceEngine {
407
408
case _ =>
408
409
// Pattern is an arbitrary expression; assume a skolem (i.e. an unknown value) of the pattern type
409
410
Typ (pat.tpe.narrow, decomposed = false )
410
- }
411
+ })
411
412
412
413
private def project (tp : Type )(using Context ): Space = tp match {
413
414
case OrType (tp1, tp2) => Or (project(tp1) :: project(tp2) :: Nil )
@@ -461,16 +462,23 @@ object SpaceEngine {
461
462
* If `isValue` is true, then pattern-bound symbols are erased to its upper bound.
462
463
* This is needed to avoid spurious unreachable warnings. See tests/patmat/i6197.scala.
463
464
*/
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 {
467
467
case tp @ AppliedType (tycon, args) if tycon.typeSymbol.isPatternBound =>
468
468
WildcardType
469
469
470
470
case tp @ AppliedType (tycon, args) =>
471
471
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
+ }
474
482
tp.derivedAppliedType(erase(tycon, inArray, isValue = false ), args2)
475
483
476
484
case tp @ OrType (tp1, tp2) =>
@@ -488,8 +496,7 @@ object SpaceEngine {
488
496
else WildcardType
489
497
490
498
case _ => tp
491
- }
492
- }
499
+ })
493
500
494
501
/** Space of the pattern: unapplySeq(a, b, c: _*)
495
502
*/
@@ -873,16 +880,11 @@ object SpaceEngine {
873
880
case _ => tp
874
881
})
875
882
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
883
885
debug.println(i " selTyp = $selTyp" )
884
886
885
- val patternSpace = Or (cases.foldLeft(List .empty[Space ]) { (acc, x) =>
887
+ val patternSpace = Or (m. cases.foldLeft(List .empty[Space ]) { (acc, x) =>
886
888
val space = if (x.guard.isEmpty) project(x.pat) else Empty
887
889
debug.println(s " ${x.pat.show} ====> ${show(space)}" )
888
890
space :: acc
@@ -899,7 +901,7 @@ object SpaceEngine {
899
901
if uncovered.nonEmpty then
900
902
val hasMore = uncovered.lengthCompare(6 ) > 0
901
903
val deduped = dedup(uncovered.take(6 ))
902
- report.warning(PatternMatchExhaustivity (showSpaces(deduped), hasMore), sel.srcPos )
904
+ report.warning(PatternMatchExhaustivity (showSpaces(deduped), hasMore), m.selector )
903
905
}
904
906
905
907
private def redundancyCheckable (sel : Tree )(using Context ): Boolean =
@@ -912,14 +914,10 @@ object SpaceEngine {
912
914
&& ! sel.tpe.widen.isRef(defn.QuotedExprClass )
913
915
&& ! sel.tpe.widen.isRef(defn.QuotedTypeClass )
914
916
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
921
919
922
- val selTyp = toUnderlying(sel .tpe).dealias
920
+ val selTyp = toUnderlying(m.selector .tpe).dealias
923
921
debug.println(i " selTyp = $selTyp" )
924
922
925
923
val isNullable = selTyp.classSymbol.isNullableClass
@@ -953,6 +951,7 @@ object SpaceEngine {
953
951
for (pat <- deferred.reverseIterator)
954
952
report.warning(MatchCaseUnreachable (), pat.srcPos)
955
953
if pat != EmptyTree // rethrow case of catch uses EmptyTree
954
+ && ! pat.symbol.isAllOf(SyntheticCase , butNot= Method ) // ExpandSAMs default cases use SyntheticCase
956
955
&& isSubspace(covered, prev)
957
956
then {
958
957
val nullOnly = isNullable && i == len - 1 && isWildcardArg(pat)
0 commit comments