@@ -14,6 +14,7 @@ import typer.*, Applications.*, Inferencing.*, ProtoTypes.*
1414import util .*
1515
1616import scala .annotation .internal .sharable
17+ import scala .annotation .tailrec
1718import scala .collection .mutable
1819
1920import SpaceEngine .*
@@ -696,7 +697,7 @@ object SpaceEngine {
696697 else NoType
697698 }.filter(_.exists)
698699 parts
699-
700+ case tp : FlexibleType => List (tp.underlying, ConstantType ( Constant ( null )))
700701 case _ => ListOfNoType
701702 end rec
702703
@@ -876,6 +877,7 @@ object SpaceEngine {
876877 case tp : SingletonType => toUnderlying(tp.underlying)
877878 case tp : ExprType => toUnderlying(tp.resultType)
878879 case AnnotatedType (tp, annot) => AnnotatedType (toUnderlying(tp), annot)
880+ case tp : FlexibleType => tp.derivedFlexibleType(toUnderlying(tp.underlying))
879881 case _ => tp
880882 })
881883
@@ -910,52 +912,41 @@ object SpaceEngine {
910912 && ! sel.tpe.widen.isRef(defn.QuotedExprClass )
911913 && ! sel.tpe.widen.isRef(defn.QuotedTypeClass )
912914
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) " ):
916916 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 )
920920 then project(OrType (selTyp, ConstantType (Constant (null )), soft = false ))
921921 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-
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)))
937922
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, prev)
946- then {
947- val nullOnly = isNullable && i == len - 1 && isWildcardArg(pat)
948- val msg = if nullOnly then MatchCaseOnlyNullWarning () else MatchCaseUnreachable ()
949- report.warning(msg, pat.srcPos)
950- }
951- deferred = Nil
952- }
953-
954- // in redundancy check, take guard as false in order to soundly approximate
955- prevs ::= (if guard.isEmpty then covered else Empty )
956- i += 1
957- }
958- }
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+ // in redundancy check, take guard as false in order to soundly approximate
945+ val newPrev = if guard.isEmpty then covered :: prevs else prevs
946+ recur(rest, newPrev, Nil )
947+
948+ recur(m.cases, Nil , Nil )
949+ end checkReachability
959950
960951 def checkMatch (m : Match )(using Context ): Unit =
961952 if exhaustivityCheckable(m.selector) then checkExhaustivity(m)
0 commit comments