@@ -22,9 +22,10 @@ import transform.SymUtils._
22
22
import reporting ._
23
23
import config .Printers .{exhaustivity => debug }
24
24
import util .{SrcPos , NoSourcePosition }
25
- import collection .mutable
26
25
27
- /** Space logic for checking exhaustivity and unreachability of pattern matching
26
+ import scala .collection .mutable
27
+
28
+ /* Space logic for checking exhaustivity and unreachability of pattern matching
28
29
*
29
30
* Space can be thought of as a set of possible values. A type or a pattern
30
31
* both refer to spaces. The space of a type is the values that inhabit the
@@ -53,16 +54,17 @@ import collection.mutable
53
54
*
54
55
*/
55
56
56
-
57
57
/** space definition */
58
58
sealed trait Space :
59
+ import SpaceEngine .*
60
+
59
61
private val isSubspaceCache = mutable.HashMap .empty[Space , Boolean ]
60
62
61
- def isSubspace (b : Space )(engine : SpaceEngine )( using Context ): Boolean =
63
+ def isSubspace (b : Space )(using Context ): Boolean =
62
64
if this == Empty then true
63
65
else if b == Empty then false
64
- else trace(s " isSubspace( ${engine. show(this )}, ${engine. show(b)}) " , debug) {
65
- isSubspaceCache.getOrElseUpdate(b, engine. computeIsSubspace(this , b))
66
+ else trace(s " isSubspace( ${show(this )}, ${show(b)}) " , debug) {
67
+ isSubspaceCache.getOrElseUpdate(b, computeIsSubspace(this , b))
66
68
}
67
69
end Space
68
70
@@ -83,44 +85,8 @@ case class Prod(tp: Type, unappTp: TermRef, params: List[Space]) extends Space
83
85
/** Union of spaces */
84
86
case class Or (spaces : Seq [Space ]) extends Space
85
87
86
- /** abstract space logic */
87
- trait SpaceLogic {
88
- /** Is `tp1` a subtype of `tp2`? */
89
- def isSubType (tp1 : Type , tp2 : Type ): Boolean
90
-
91
- /** True if we can assume that the two unapply methods are the same.
92
- * That is, given the same parameter, they return the same result.
93
- *
94
- * We assume that unapply methods are pure, but the same method may
95
- * be called with different prefixes, thus behaving differently.
96
- */
97
- def isSameUnapply (tp1 : TermRef , tp2 : TermRef ): Boolean
98
-
99
- /** Return a space containing the values of both types.
100
- *
101
- * The types should be atomic (non-decomposable) and unrelated (neither
102
- * should be a subtype of the other).
103
- */
104
- def intersectUnrelatedAtomicTypes (tp1 : Type , tp2 : Type ): Space
105
-
106
- /** Is the type `tp` decomposable? i.e. all values of the type can be covered
107
- * by its decomposed types.
108
- *
109
- * Abstract sealed class, OrType, Boolean and Java enums can be decomposed.
110
- */
111
- def canDecompose (tp : Type ): Boolean
112
-
113
- /** Return term parameter types of the extractor `unapp` */
114
- def signature (unapp : TermRef , scrutineeTp : Type , argLen : Int ): List [Type ]
115
-
116
- /** Get components of decomposable types */
117
- def decompose (tp : Type ): List [Typ ]
118
-
119
- /** Whether the extractor covers the given type */
120
- def covers (unapp : TermRef , scrutineeTp : Type , argLen : Int ): Boolean
121
-
122
- /** Display space in string format */
123
- def show (sp : Space ): String
88
+ object SpaceEngine {
89
+ import tpd ._
124
90
125
91
/** Simplify space such that a space equal to `Empty` becomes `Empty` */
126
92
def simplify (space : Space )(using Context ): Space = trace(s " simplify ${show(space)} --> " , debug, show)(space match {
@@ -174,7 +140,9 @@ trait SpaceLogic {
174
140
}
175
141
176
142
/** Is `a` a subspace of `b`? Equivalent to `simplify(simplify(a) - simplify(b)) == Empty`, but faster */
177
- def isSubspace (a : Space , b : Space )(using Context ): Boolean = trace(s " isSubspace( ${show(a)}, ${show(b)}) " , debug) {
143
+ def isSubspace (a : Space , b : Space )(using Context ): Boolean = a.isSubspace(b)
144
+
145
+ def computeIsSubspace (a : Space , b : Space )(using Context ): Boolean = {
178
146
def tryDecompose1 (tp : Type ) = canDecompose(tp) && isSubspace(Or (decompose(tp)), b)
179
147
def tryDecompose2 (tp : Type ) = canDecompose(tp) && isSubspace(a, Or (decompose(tp)))
180
148
@@ -302,9 +270,6 @@ trait SpaceLogic {
302
270
Or (spaces)
303
271
}
304
272
}
305
- }
306
-
307
- object SpaceEngine {
308
273
309
274
/** Is the unapply or unapplySeq irrefutable?
310
275
* @param unapp The unapply function reference
@@ -352,13 +317,13 @@ object SpaceEngine {
352
317
353
318
case _ => false
354
319
}
355
- }
356
-
357
- /** Scala implementation of space logic */
358
- class SpaceEngine (using Context ) extends SpaceLogic {
359
- import tpd ._
360
320
361
- override def intersectUnrelatedAtomicTypes (tp1 : Type , tp2 : Type ): Space = trace(s " atomic intersection: ${AndType (tp1, tp2).show}" , debug) {
321
+ /** Return a space containing the values of both types.
322
+ *
323
+ * The types should be atomic (non-decomposable) and unrelated (neither
324
+ * should be a subtype of the other).
325
+ */
326
+ def intersectUnrelatedAtomicTypes (tp1 : Type , tp2 : Type )(using Context ): Space = trace(i " atomic intersection: ${AndType (tp1, tp2)}" , debug) {
362
327
// Precondition: !isSubType(tp1, tp2) && !isSubType(tp2, tp1).
363
328
if ! ctx.mode.is(Mode .SafeNulls ) && (tp1.isNullType || tp2.isNullType) then
364
329
// Since projections of types don't include null, intersection with null is empty.
@@ -373,7 +338,7 @@ class SpaceEngine(using Context) extends SpaceLogic {
373
338
}
374
339
375
340
/** Return the space that represents the pattern `pat` */
376
- def project (pat : Tree ): Space = pat match {
341
+ def project (pat : Tree )( using Context ) : Space = pat match {
377
342
case Literal (c) =>
378
343
if (c.value.isInstanceOf [Symbol ])
379
344
Typ (c.value.asInstanceOf [Symbol ].termRef, decomposed = false )
@@ -436,12 +401,12 @@ class SpaceEngine(using Context) extends SpaceLogic {
436
401
Typ (pat.tpe.narrow, decomposed = false )
437
402
}
438
403
439
- private def project (tp : Type ): Space = tp match {
404
+ private def project (tp : Type )( using Context ) : Space = tp match {
440
405
case OrType (tp1, tp2) => Or (project(tp1) :: project(tp2) :: Nil )
441
406
case tp => Typ (tp, decomposed = true )
442
407
}
443
408
444
- private def unapplySeqInfo (resTp : Type , pos : SrcPos ): (Int , Type , Type ) = {
409
+ private def unapplySeqInfo (resTp : Type , pos : SrcPos )( using Context ) : (Int , Type , Type ) = {
445
410
var resultTp = resTp
446
411
var elemTp = unapplySeqTypeElemTp(resultTp)
447
412
var arity = productArity(resultTp, pos)
@@ -488,7 +453,7 @@ class SpaceEngine(using Context) extends SpaceLogic {
488
453
* If `isValue` is true, then pattern-bound symbols are erased to its upper bound.
489
454
* This is needed to avoid spurious unreachable warnings. See tests/patmat/i6197.scala.
490
455
*/
491
- private def erase (tp : Type , inArray : Boolean = false , isValue : Boolean = false ): Type = trace(i " $tp erased to " , debug) {
456
+ private def erase (tp : Type , inArray : Boolean = false , isValue : Boolean = false )( using Context ) : Type = trace(i " $tp erased to " , debug) {
492
457
493
458
tp match {
494
459
case tp @ AppliedType (tycon, args) if tycon.typeSymbol.isPatternBound =>
@@ -520,7 +485,7 @@ class SpaceEngine(using Context) extends SpaceLogic {
520
485
521
486
/** Space of the pattern: unapplySeq(a, b, c: _*)
522
487
*/
523
- def projectSeq (pats : List [Tree ]): Space = {
488
+ def projectSeq (pats : List [Tree ])( using Context ) : Space = {
524
489
if (pats.isEmpty) return Typ (defn.NilType , false )
525
490
526
491
val (items, zero) = if (isWildcardStarArg(pats.last))
@@ -535,29 +500,30 @@ class SpaceEngine(using Context) extends SpaceLogic {
535
500
}
536
501
}
537
502
538
- def isPrimToBox (tp : Type , pt : Type ): Boolean =
503
+ def isPrimToBox (tp : Type , pt : Type )( using Context ) : Boolean =
539
504
tp.isPrimitiveValueType && (defn.boxedType(tp).classSymbol eq pt.classSymbol)
540
505
541
- private val isSubspaceCache = mutable.HashMap .empty[(Space , Space , Context ), Boolean ]
542
-
543
- override def isSubspace (a : Space , b : Space )(using Context ): Boolean = a.isSubspace(b)(this )
544
-
545
- def computeIsSubspace (a : Space , b : Space )(using Context ): Boolean = super .isSubspace(a, b)
546
-
547
506
/** Is `tp1` a subtype of `tp2`? */
548
- def isSubType (tp1 : Type , tp2 : Type ): Boolean = trace(i " $tp1 <:< $tp2" , debug, show = true ) {
507
+ def isSubType (tp1 : Type , tp2 : Type )( using Context ) : Boolean = trace(i " $tp1 <:< $tp2" , debug, show = true ) {
549
508
if tp1 == ConstantType (Constant (null )) && ! ctx.mode.is(Mode .SafeNulls )
550
509
then tp2 == ConstantType (Constant (null ))
551
510
else tp1 <:< tp2
552
511
}
553
512
554
- def isSameUnapply (tp1 : TermRef , tp2 : TermRef ): Boolean =
513
+ /** True if we can assume that the two unapply methods are the same.
514
+ * That is, given the same parameter, they return the same result.
515
+ *
516
+ * We assume that unapply methods are pure, but the same method may
517
+ * be called with different prefixes, thus behaving differently.
518
+ */
519
+ def isSameUnapply (tp1 : TermRef , tp2 : TermRef )(using Context ): Boolean =
555
520
// always assume two TypeTest[S, T].unapply are the same if they are equal in types
556
521
(tp1.prefix.isStable && tp2.prefix.isStable || tp1.symbol == defn.TypeTest_unapply )
557
522
&& tp1 =:= tp2
558
523
559
- /** Parameter types of the case class type `tp`. Adapted from `unapplyPlan` in patternMatcher */
560
- def signature (unapp : TermRef , scrutineeTp : Type , argLen : Int ): List [Type ] = {
524
+ /** Return term parameter types of the extractor `unapp`.
525
+ * Parameter types of the case class type `tp`. Adapted from `unapplyPlan` in patternMatcher */
526
+ def signature (unapp : TermRef , scrutineeTp : Type , argLen : Int )(using Context ): List [Type ] = {
561
527
val unappSym = unapp.symbol
562
528
563
529
// println("scrutineeTp = " + scrutineeTp.show)
@@ -620,14 +586,14 @@ class SpaceEngine(using Context) extends SpaceLogic {
620
586
}
621
587
622
588
/** Whether the extractor covers the given type */
623
- def covers (unapp : TermRef , scrutineeTp : Type , argLen : Int ): Boolean =
589
+ def covers (unapp : TermRef , scrutineeTp : Type , argLen : Int )( using Context ) : Boolean =
624
590
SpaceEngine .isIrrefutable(unapp, argLen) || unapp.symbol == defn.TypeTest_unapply && {
625
591
val AppliedType (_, _ :: tp :: Nil ) = unapp.prefix.widen.dealias: @ unchecked
626
592
scrutineeTp <:< tp
627
593
}
628
594
629
595
/** Decompose a type into subspaces -- assume the type can be decomposed */
630
- def decompose (tp : Type ): List [Typ ] = trace(i " decompose( $tp) " , debug, show( _ : Seq [ Space ]) ) {
596
+ def decompose (tp : Type )( using Context ) : List [Typ ] = trace(i " decompose( $tp) " , debug, showSpaces ) {
631
597
def rec (tp : Type , mixins : List [Type ]): List [Typ ] = tp.dealias match {
632
598
case AndType (tp1, tp2) =>
633
599
def decomposeComponent (tpA : Type , tpB : Type ): List [Typ ] =
@@ -708,7 +674,7 @@ class SpaceEngine(using Context) extends SpaceLogic {
708
674
}
709
675
710
676
/** Abstract sealed types, or-types, Boolean and Java enums can be decomposed */
711
- def canDecompose (tp : Type ): Boolean =
677
+ def canDecompose (tp : Type )( using Context ) : Boolean =
712
678
val res = tp.dealias match
713
679
case AppliedType (tycon, _) if canDecompose(tycon) => true
714
680
case tp : NamedType if canDecompose(tp.prefix) => true
@@ -735,7 +701,7 @@ class SpaceEngine(using Context) extends SpaceLogic {
735
701
* C --> C if current owner is C !!!
736
702
*
737
703
*/
738
- def showType (tp : Type , showTypeArgs : Boolean = false ): String = {
704
+ def showType (tp : Type , showTypeArgs : Boolean = false )( using Context ) : String = {
739
705
val enclosingCls = ctx.owner.enclosingClass
740
706
741
707
def isOmittable (sym : Symbol ) =
@@ -776,7 +742,7 @@ class SpaceEngine(using Context) extends SpaceLogic {
776
742
}
777
743
778
744
/** Whether the counterexample is satisfiable. The space is flattened and non-empty. */
779
- def satisfiable (sp : Space ): Boolean = {
745
+ def satisfiable (sp : Space )( using Context ) : Boolean = {
780
746
def impossible : Nothing = throw new AssertionError (" `satisfiable` only accepts flattened space." )
781
747
782
748
def genConstraint (space : Space ): List [(Type , Type )] = space match {
@@ -807,10 +773,10 @@ class SpaceEngine(using Context) extends SpaceLogic {
807
773
checkConstraint(genConstraint(sp))(using ctx.fresh.setNewTyperState())
808
774
}
809
775
810
- def show (ss : Seq [Space ]): String = ss.map(show).mkString(" , " )
776
+ def showSpaces (ss : Seq [Space ])( using Context ): String = ss.map(show).mkString(" , " )
811
777
812
778
/** Display spaces */
813
- def show (s : Space ): String = {
779
+ def show (s : Space )( using Context ) : String = {
814
780
def params (tp : Type ): List [Type ] = tp.classSymbol.primaryConstructor.info.firstParamTypes
815
781
816
782
/** does the companion object of the given symbol have custom unapply */
@@ -862,7 +828,7 @@ class SpaceEngine(using Context) extends SpaceLogic {
862
828
doShow(s, flattenList = false )
863
829
}
864
830
865
- private def exhaustivityCheckable (sel : Tree ): Boolean = {
831
+ private def exhaustivityCheckable (sel : Tree )( using Context ) : Boolean = {
866
832
val seen = collection.mutable.Set .empty[Type ]
867
833
868
834
// Possible to check everything, but be compatible with scalac by default
@@ -891,8 +857,8 @@ class SpaceEngine(using Context) extends SpaceLogic {
891
857
res
892
858
}
893
859
894
- /** Whehter counter-examples should be further checked? True for GADTs. */
895
- private def shouldCheckExamples (tp : Type ): Boolean =
860
+ /** Whether counter-examples should be further checked? True for GADTs. */
861
+ private def shouldCheckExamples (tp : Type )( using Context ) : Boolean =
896
862
new TypeAccumulator [Boolean ] {
897
863
override def apply (b : Boolean , tp : Type ): Boolean = tp match {
898
864
case tref : TypeRef if tref.symbol.is(TypeParam ) && variance != 1 => true
@@ -903,7 +869,7 @@ class SpaceEngine(using Context) extends SpaceLogic {
903
869
/** Return the underlying type of non-module, non-constant, non-enum case singleton types.
904
870
* Also widen ExprType to its result type, and rewrap any annotation wrappers.
905
871
* For example, with `val opt = None`, widen `opt.type` to `None.type`. */
906
- def toUnderlying (tp : Type ): Type = trace(i " toUnderlying( $tp) " , show = true )(tp match {
872
+ def toUnderlying (tp : Type )( using Context ) : Type = trace(i " toUnderlying( $tp) " , show = true )(tp match {
907
873
case _ : ConstantType => tp
908
874
case tp : TermRef if tp.symbol.is(Module ) => tp
909
875
case tp : TermRef if tp.symbol.isAllOf(EnumCase ) => tp
@@ -913,7 +879,7 @@ class SpaceEngine(using Context) extends SpaceLogic {
913
879
case _ => tp
914
880
})
915
881
916
- def checkExhaustivity (_match : Match ): Unit = {
882
+ def checkExhaustivity (_match : Match )( using Context ) : Unit = {
917
883
val Match (sel, cases) = _match
918
884
debug.println(i " checking exhaustivity of ${_match}" )
919
885
@@ -939,10 +905,10 @@ class SpaceEngine(using Context) extends SpaceLogic {
939
905
if uncovered.nonEmpty then
940
906
val hasMore = uncovered.lengthCompare(6 ) > 0
941
907
val deduped = dedup(uncovered.take(6 ))
942
- report.warning(PatternMatchExhaustivity (show (deduped), hasMore), sel.srcPos)
908
+ report.warning(PatternMatchExhaustivity (showSpaces (deduped), hasMore), sel.srcPos)
943
909
}
944
910
945
- private def redundancyCheckable (sel : Tree ): Boolean =
911
+ private def redundancyCheckable (sel : Tree )( using Context ) : Boolean =
946
912
// Ignore Expr[T] and Type[T] for unreachability as a special case.
947
913
// Quote patterns produce repeated calls to the same unapply method, but with different implicit parameters.
948
914
// Since we assume that repeated calls to the same unapply method overlap
@@ -952,7 +918,7 @@ class SpaceEngine(using Context) extends SpaceLogic {
952
918
&& ! sel.tpe.widen.isRef(defn.QuotedExprClass )
953
919
&& ! sel.tpe.widen.isRef(defn.QuotedTypeClass )
954
920
955
- def checkRedundancy (_match : Match ): Unit = {
921
+ def checkRedundancy (_match : Match )( using Context ) : Unit = {
956
922
val Match (sel, _) = _match
957
923
val cases = _match.cases.toIndexedSeq
958
924
debug.println(i " checking redundancy in $_match" )
0 commit comments