@@ -14,6 +14,7 @@ import typer.*, Applications.*, Inferencing.*, ProtoTypes.*
14
14
import util .*
15
15
16
16
import scala .annotation .internal .sharable
17
+ import scala .annotation .tailrec
17
18
import scala .collection .mutable
18
19
19
20
import SpaceEngine .*
@@ -696,7 +697,7 @@ object SpaceEngine {
696
697
else NoType
697
698
}.filter(_.exists)
698
699
parts
699
-
700
+ case tp : FlexibleType => List (tp.underlying, ConstantType ( Constant ( null )))
700
701
case _ => ListOfNoType
701
702
end rec
702
703
@@ -876,6 +877,7 @@ object SpaceEngine {
876
877
case tp : SingletonType => toUnderlying(tp.underlying)
877
878
case tp : ExprType => toUnderlying(tp.resultType)
878
879
case AnnotatedType (tp, annot) => AnnotatedType (toUnderlying(tp), annot)
880
+ case tp : FlexibleType => tp.derivedFlexibleType(toUnderlying(tp.underlying))
879
881
case _ => tp
880
882
})
881
883
@@ -910,58 +912,40 @@ object SpaceEngine {
910
912
&& ! sel.tpe.widen.isRef(defn.QuotedExprClass )
911
913
&& ! sel.tpe.widen.isRef(defn.QuotedTypeClass )
912
914
913
- def checkReachability (m : Match )(using Context ): Unit = trace(i " checkReachability( $m) " ) {
914
- val cases = m.cases.toIndexedSeq
915
-
915
+ def checkReachability (m : Match )(using Context ): Unit = trace(i " checkReachability( $m) " ):
916
916
val selTyp = toUnderlying(m.selector.tpe).dealias
917
-
918
- val isNullable = selTyp.classSymbol.isNullableClass
919
- val targetSpace = trace( i " targetSpace( $selTyp ) " )( if isNullable
917
+ val isNullable = selTyp. isInstanceOf [ FlexibleType ] || selTyp.classSymbol.isNullableClass
918
+ val targetSpace = trace( i " targetSpace( $ selTyp) " ) :
919
+ if isNullable && ! ctx.mode.is( Mode . SafeNulls )
920
920
then project(OrType (selTyp, ConstantType (Constant (null )), soft = false ))
921
921
else project(selTyp)
922
- )
923
-
924
- var i = 0
925
- val len = cases.length
926
- var prevs = List .empty[Space ]
927
- var deferred = List .empty[Tree ]
928
-
929
- while (i < len) {
930
- val CaseDef (pat, guard, _) = cases(i)
931
922
932
- val curr = trace(i " project( $pat) " )(project(pat))
933
-
934
- val covered = trace(" covered" )(simplify(intersect(curr, targetSpace)))
935
-
936
- val prev = trace(" prev" )(simplify(Or (prevs)))
937
-
938
- if prev == Empty && covered == Empty then // defer until a case is reachable
939
- deferred ::= pat
940
- else {
941
- for (pat <- deferred.reverseIterator)
942
- report.warning(MatchCaseUnreachable (), pat.srcPos)
943
- if pat != EmptyTree // rethrow case of catch uses EmptyTree
944
- && ! pat.symbol.isAllOf(SyntheticCase , butNot= Method ) // ExpandSAMs default cases use SyntheticCase
945
- && isSubspace(covered, Or (List (prev, Typ (defn.NullType )))) // for when Null is not subtype of AnyRef under explicit nulls
946
- then {
947
- val nullOnly =
948
- (isNullable || (defn.NullType <:< selTyp))
949
- && i == len - 1
950
- && isWildcardArg(pat)
951
- if nullOnly then {
952
- report.warning(MatchCaseOnlyNullWarning (), pat.srcPos)
953
- } else if isSubspace(covered, prev) then {
954
- report.warning(MatchCaseUnreachable (), pat.srcPos)
955
- }
956
- }
957
- deferred = Nil
958
- }
959
-
960
- // in redundancy check, take guard as false in order to soundly approximate
961
- prevs ::= (if guard.isEmpty then covered else Empty )
962
- i += 1
963
- }
964
- }
923
+ @ tailrec def recur (cases : List [CaseDef ], prevs : List [Space ], deferred : List [Tree ]): Unit =
924
+ cases match
925
+ case Nil =>
926
+ case CaseDef (pat, guard, _) :: rest =>
927
+ val curr = trace(i " project( $pat) " )(project(pat))
928
+ val covered = trace(" covered" )(simplify(intersect(curr, targetSpace)))
929
+ val prev = trace(" prev" )(simplify(Or (prevs)))
930
+ if prev == Empty && covered == Empty then // defer until a case is reachable
931
+ recur(rest, prevs, pat :: deferred)
932
+ else
933
+ for pat <- deferred.reverseIterator
934
+ do report.warning(MatchCaseUnreachable (), pat.srcPos)
935
+
936
+ if pat != EmptyTree // rethrow case of catch uses EmptyTree
937
+ && ! pat.symbol.isAllOf(SyntheticCase , butNot= Method ) // ExpandSAMs default cases use SyntheticCase
938
+ && isSubspace(covered, prev)
939
+ then
940
+ val nullOnly = isNullable && rest.isEmpty && isWildcardArg(pat)
941
+ val msg = if nullOnly then MatchCaseOnlyNullWarning () else MatchCaseUnreachable ()
942
+ report.warning(msg, pat.srcPos)
943
+
944
+ val newPrev = if guard.isEmpty then covered :: prevs else prevs
945
+ recur(rest, newPrev, Nil )
946
+
947
+ recur(m.cases, Nil , Nil )
948
+ end checkReachability
965
949
966
950
def checkMatch (m : Match )(using Context ): Unit =
967
951
if exhaustivityCheckable(m.selector) then checkExhaustivity(m)
0 commit comments