@@ -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,58 +912,40 @@ 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)
931922
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
965949
966950 def checkMatch (m : Match )(using Context ): Unit =
967951 if exhaustivityCheckable(m.selector) then checkExhaustivity(m)
0 commit comments