@@ -320,21 +320,13 @@ class SpaceEngine(using Context) extends SpaceLogic {
320320 private val scalaConsType = defn.ConsClass .typeRef
321321
322322 private val constantNullType = ConstantType (Constant (null ))
323- private val constantNullSpace = Typ (constantNullType)
324323
325324 /** Does the given tree stand for the literal `null`? */
326325 def isNullLit (tree : Tree ): Boolean = tree match {
327326 case Literal (Constant (null )) => true
328327 case _ => false
329328 }
330329
331- /** Does the given space contain just the value `null`? */
332- def isNullSpace (space : Space ): Boolean = space match {
333- case Typ (tpe, _) => tpe.dealias == constantNullType || tpe.isNullType
334- case Or (spaces) => spaces.forall(isNullSpace)
335- case _ => false
336- }
337-
338330 override def intersectUnrelatedAtomicTypes (tp1 : Type , tp2 : Type ): Space = trace(s " atomic intersection: ${AndType (tp1, tp2).show}" , debug) {
339331 // Precondition: !isSubType(tp1, tp2) && !isSubType(tp2, tp1).
340332 if (! ctx.explicitNulls && (tp1.isNullType || tp2.isNullType)) {
@@ -363,13 +355,10 @@ class SpaceEngine(using Context) extends SpaceLogic {
363355 Typ (pat.tpe, decomposed = false )
364356
365357 case Ident (nme.WILDCARD ) =>
366- if pat.tpe.classSymbol.isNullableClass then
367- Or (Typ (erase(pat.tpe.stripAnnots), decomposed = false ) :: constantNullSpace :: Nil )
368- else
369- Typ (pat.tpe, decomposed = false )
358+ Typ (erase(pat.tpe.stripAnnots, isValue = true ), decomposed = false )
370359
371360 case Ident (_) | Select (_, _) =>
372- Typ (erase(pat.tpe.stripAnnots), decomposed = false )
361+ Typ (erase(pat.tpe.stripAnnots, isValue = true ), decomposed = false )
373362
374363 case Alternative (trees) =>
375364 Or (trees.map(project(_)))
@@ -389,18 +378,18 @@ class SpaceEngine(using Context) extends SpaceLogic {
389378 else {
390379 val (arity, elemTp, resultTp) = unapplySeqInfo(fun.tpe.widen.finalResultType, fun.srcPos)
391380 if (elemTp.exists)
392- Prod (erase(pat.tpe.stripAnnots), funRef, projectSeq(pats) :: Nil )
381+ Prod (erase(pat.tpe.stripAnnots, isValue = false ), funRef, projectSeq(pats) :: Nil )
393382 else
394- Prod (erase(pat.tpe.stripAnnots), funRef, pats.take(arity - 1 ).map(project) :+ projectSeq(pats.drop(arity - 1 )))
383+ Prod (erase(pat.tpe.stripAnnots, isValue = false ), funRef, pats.take(arity - 1 ).map(project) :+ projectSeq(pats.drop(arity - 1 )))
395384 }
396385 else
397- Prod (erase(pat.tpe.stripAnnots), funRef, pats.map(project))
386+ Prod (erase(pat.tpe.stripAnnots, isValue = false ), funRef, pats.map(project))
398387
399388 case Typed (pat @ UnApply (_, _, _), _) =>
400389 project(pat)
401390
402391 case Typed (_, tpt) =>
403- Typ (erase(tpt.tpe.stripAnnots), decomposed = false )
392+ Typ (erase(tpt.tpe.stripAnnots, isValue = true ), decomposed = false )
404393
405394 case This (_) =>
406395 Typ (pat.tpe.stripAnnots, decomposed = false )
@@ -461,29 +450,37 @@ class SpaceEngine(using Context) extends SpaceLogic {
461450 * case (IntExpr(_), IntExpr(_)) =>
462451 * case (BooleanExpr(_), BooleanExpr(_)) =>
463452 * }
453+ *
454+ * @param inArray whether `tp` is a type argument to `Array`
455+ * @param isValue whether `tp` is the type which match against values
456+ *
457+ * If `isValue` is true, then pattern-bound symbols are erased to its upper bound.
458+ * This is needed to avoid spurious unreachable warnings. See tests/patmat/i6197.scala.
464459 */
465- private def erase (tp : Type , inArray : Boolean = false ): Type = trace(i " $tp erased to " , debug) {
460+ private def erase (tp : Type , inArray : Boolean = false , isValue : Boolean = false ): Type = trace(i " $tp erased to " , debug) {
466461
467462 tp match {
468463 case tp @ AppliedType (tycon, args) =>
469464 if tycon.typeSymbol.isPatternBound then return WildcardType
470465
471466 val args2 =
472- if (tycon.isRef(defn.ArrayClass )) args.map(arg => erase(arg, inArray = true ))
473- else args.map(arg => erase(arg, inArray = false ))
474- tp.derivedAppliedType(erase(tycon, inArray), args2)
467+ if (tycon.isRef(defn.ArrayClass )) args.map(arg => erase(arg, inArray = true , isValue = false ))
468+ else args.map(arg => erase(arg, inArray = false , isValue = false ))
469+ tp.derivedAppliedType(erase(tycon, inArray, isValue = false ), args2)
475470
476471 case tp @ OrType (tp1, tp2) =>
477- OrType (erase(tp1, inArray), erase(tp2, inArray), tp.isSoft)
472+ OrType (erase(tp1, inArray, isValue ), erase(tp2, inArray, isValue ), tp.isSoft)
478473
479474 case AndType (tp1, tp2) =>
480- AndType (erase(tp1, inArray), erase(tp2, inArray))
475+ AndType (erase(tp1, inArray, isValue ), erase(tp2, inArray, isValue ))
481476
482477 case tp @ RefinedType (parent, _, _) =>
483- erase(parent)
478+ erase(parent, inArray, isValue )
484479
485480 case tref : TypeRef if tref.symbol.isPatternBound =>
486- if (inArray) tref.underlying else WildcardType
481+ if inArray then tref.underlying
482+ else if isValue then tref.superType
483+ else WildcardType
487484
488485 case _ => tp
489486 }
@@ -867,25 +864,28 @@ class SpaceEngine(using Context) extends SpaceLogic {
867864 && ! sel.tpe.widen.isRef(defn.QuotedTypeClass )
868865
869866 def checkRedundancy (_match : Match ): Unit = {
867+ debug.println(s " ---------------checking redundant patterns ${_match.show}" )
868+
870869 val Match (sel, cases) = _match
871870 val selTyp = sel.tpe.widen.dealias
872871
873872 if (! redundancyCheckable(sel)) return
874873
875874 val targetSpace =
876- if (ctx.explicitNulls || selTyp.classSymbol.isPrimitiveValueClass)
875+ if ! selTyp.classSymbol.isNullableClass then
877876 project(selTyp)
878877 else
879878 project(OrType (selTyp, constantNullType, soft = false ))
880879
881880 // in redundancy check, take guard as false in order to soundly approximate
882- def projectPrevCases ( cases : List [ CaseDef ]) : List [ Space ] =
883- cases.map { x =>
881+ val spaces = cases.map { x =>
882+ val res =
884883 if (x.guard.isEmpty) project(x.pat)
885884 else Empty
886- }
887885
888- val spaces = projectPrevCases(cases)
886+ debug.println(s " ${x.pat.show} ====> ${res}" )
887+ res
888+ }
889889
890890 (1 until cases.length).foreach { i =>
891891 val pat = cases(i).pat
@@ -905,20 +905,13 @@ class SpaceEngine(using Context) extends SpaceLogic {
905905 if (covered == Empty && ! isNullLit(pat)) covered = curr
906906
907907 if (isSubspace(covered, prevs)) {
908- report.warning(MatchCaseUnreachable (), pat.srcPos)
909- }
910-
911- // if last case is `_` and only matches `null`, produce a warning
912- // If explicit nulls are enabled, this check isn't needed because most of the cases
913- // that would trigger it would also trigger unreachability warnings.
914- if (! ctx.explicitNulls && i == cases.length - 1 && ! isNullLit(pat) ) {
915- val spaces = flatten(simplify(minus(covered, prevs)))
916- if spaces.lengthCompare(10 ) < 0 then
917- dedup(spaces).toList match
918- case Typ (`constantNullType`, _) :: Nil =>
919- report.warning(MatchCaseOnlyNullWarning (), pat.srcPos)
920- case s =>
921- debug.println(" `_` matches = " + s)
908+ if i == cases.length - 1
909+ && isWildcardArg(pat)
910+ && pat.tpe.classSymbol.isNullableClass
911+ then
912+ report.warning(MatchCaseOnlyNullWarning (), pat.srcPos)
913+ else
914+ report.warning(MatchCaseUnreachable (), pat.srcPos)
922915 }
923916 }
924917 }
0 commit comments