@@ -320,21 +320,13 @@ class SpaceEngine(using Context) extends SpaceLogic {
320
320
private val scalaConsType = defn.ConsClass .typeRef
321
321
322
322
private val constantNullType = ConstantType (Constant (null ))
323
- private val constantNullSpace = Typ (constantNullType)
324
323
325
324
/** Does the given tree stand for the literal `null`? */
326
325
def isNullLit (tree : Tree ): Boolean = tree match {
327
326
case Literal (Constant (null )) => true
328
327
case _ => false
329
328
}
330
329
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
-
338
330
override def intersectUnrelatedAtomicTypes (tp1 : Type , tp2 : Type ): Space = trace(s " atomic intersection: ${AndType (tp1, tp2).show}" , debug) {
339
331
// Precondition: !isSubType(tp1, tp2) && !isSubType(tp2, tp1).
340
332
if (! ctx.explicitNulls && (tp1.isNullType || tp2.isNullType)) {
@@ -363,13 +355,10 @@ class SpaceEngine(using Context) extends SpaceLogic {
363
355
Typ (pat.tpe, decomposed = false )
364
356
365
357
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 )
370
359
371
360
case Ident (_) | Select (_, _) =>
372
- Typ (erase(pat.tpe.stripAnnots), decomposed = false )
361
+ Typ (erase(pat.tpe.stripAnnots, isValue = true ), decomposed = false )
373
362
374
363
case Alternative (trees) =>
375
364
Or (trees.map(project(_)))
@@ -389,18 +378,18 @@ class SpaceEngine(using Context) extends SpaceLogic {
389
378
else {
390
379
val (arity, elemTp, resultTp) = unapplySeqInfo(fun.tpe.widen.finalResultType, fun.srcPos)
391
380
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 )
393
382
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 )))
395
384
}
396
385
else
397
- Prod (erase(pat.tpe.stripAnnots), funRef, pats.map(project))
386
+ Prod (erase(pat.tpe.stripAnnots, isValue = false ), funRef, pats.map(project))
398
387
399
388
case Typed (pat @ UnApply (_, _, _), _) =>
400
389
project(pat)
401
390
402
391
case Typed (_, tpt) =>
403
- Typ (erase(tpt.tpe.stripAnnots), decomposed = false )
392
+ Typ (erase(tpt.tpe.stripAnnots, isValue = true ), decomposed = false )
404
393
405
394
case This (_) =>
406
395
Typ (pat.tpe.stripAnnots, decomposed = false )
@@ -461,29 +450,37 @@ class SpaceEngine(using Context) extends SpaceLogic {
461
450
* case (IntExpr(_), IntExpr(_)) =>
462
451
* case (BooleanExpr(_), BooleanExpr(_)) =>
463
452
* }
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.
464
459
*/
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) {
466
461
467
462
tp match {
468
463
case tp @ AppliedType (tycon, args) =>
469
464
if tycon.typeSymbol.isPatternBound then return WildcardType
470
465
471
466
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)
475
470
476
471
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)
478
473
479
474
case AndType (tp1, tp2) =>
480
- AndType (erase(tp1, inArray), erase(tp2, inArray))
475
+ AndType (erase(tp1, inArray, isValue ), erase(tp2, inArray, isValue ))
481
476
482
477
case tp @ RefinedType (parent, _, _) =>
483
- erase(parent)
478
+ erase(parent, inArray, isValue )
484
479
485
480
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
487
484
488
485
case _ => tp
489
486
}
@@ -867,25 +864,28 @@ class SpaceEngine(using Context) extends SpaceLogic {
867
864
&& ! sel.tpe.widen.isRef(defn.QuotedTypeClass )
868
865
869
866
def checkRedundancy (_match : Match ): Unit = {
867
+ debug.println(s " ---------------checking redundant patterns ${_match.show}" )
868
+
870
869
val Match (sel, cases) = _match
871
870
val selTyp = sel.tpe.widen.dealias
872
871
873
872
if (! redundancyCheckable(sel)) return
874
873
875
874
val targetSpace =
876
- if (ctx.explicitNulls || selTyp.classSymbol.isPrimitiveValueClass)
875
+ if ! selTyp.classSymbol.isNullableClass then
877
876
project(selTyp)
878
877
else
879
878
project(OrType (selTyp, constantNullType, soft = false ))
880
879
881
880
// 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 =
884
883
if (x.guard.isEmpty) project(x.pat)
885
884
else Empty
886
- }
887
885
888
- val spaces = projectPrevCases(cases)
886
+ debug.println(s " ${x.pat.show} ====> ${res}" )
887
+ res
888
+ }
889
889
890
890
(1 until cases.length).foreach { i =>
891
891
val pat = cases(i).pat
@@ -905,20 +905,13 @@ class SpaceEngine(using Context) extends SpaceLogic {
905
905
if (covered == Empty && ! isNullLit(pat)) covered = curr
906
906
907
907
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)
922
915
}
923
916
}
924
917
}
0 commit comments