@@ -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)) {
@@ -355,18 +347,18 @@ class SpaceEngine(using Context) extends SpaceLogic {
355
347
def project (pat : Tree ): Space = pat match {
356
348
case Literal (c) =>
357
349
if (c.value.isInstanceOf [Symbol ])
358
- Typ (c.value.asInstanceOf [Symbol ].termRef, false )
350
+ Typ (c.value.asInstanceOf [Symbol ].termRef, decomposed = false )
359
351
else
360
- Typ (ConstantType (c), false )
352
+ Typ (ConstantType (c), decomposed = false )
361
353
362
354
case pat : Ident if isBackquoted(pat) =>
363
- Typ (pat.tpe, false )
355
+ Typ (pat.tpe, decomposed = false )
364
356
365
357
case Ident (nme.WILDCARD ) =>
366
- Or ( Typ (erase(pat.tpe.stripAnnots), false ) :: constantNullSpace :: Nil )
358
+ Typ (erase(pat.tpe.stripAnnots, isValue = true ), decomposed = false )
367
359
368
360
case Ident (_) | Select (_, _) =>
369
- Typ (erase(pat.tpe.stripAnnots), false )
361
+ Typ (erase(pat.tpe.stripAnnots, isValue = true ), decomposed = false )
370
362
371
363
case Alternative (trees) =>
372
364
Or (trees.map(project(_)))
@@ -386,31 +378,31 @@ class SpaceEngine(using Context) extends SpaceLogic {
386
378
else {
387
379
val (arity, elemTp, resultTp) = unapplySeqInfo(fun.tpe.widen.finalResultType, fun.srcPos)
388
380
if (elemTp.exists)
389
- Prod (erase(pat.tpe.stripAnnots), funRef, projectSeq(pats) :: Nil )
381
+ Prod (erase(pat.tpe.stripAnnots, isValue = false ), funRef, projectSeq(pats) :: Nil )
390
382
else
391
- 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 )))
392
384
}
393
385
else
394
- Prod (erase(pat.tpe.stripAnnots), funRef, pats.map(project))
386
+ Prod (erase(pat.tpe.stripAnnots, isValue = false ), funRef, pats.map(project))
395
387
396
388
case Typed (pat @ UnApply (_, _, _), _) =>
397
389
project(pat)
398
390
399
391
case Typed (_, tpt) =>
400
- Typ (erase(tpt.tpe.stripAnnots), true )
392
+ Typ (erase(tpt.tpe.stripAnnots, isValue = true ), decomposed = false )
401
393
402
394
case This (_) =>
403
- Typ (pat.tpe.stripAnnots, false )
395
+ Typ (pat.tpe.stripAnnots, decomposed = false )
404
396
405
397
case EmptyTree => // default rethrow clause of try/catch, check tests/patmat/try2.scala
406
- Typ (WildcardType , false )
398
+ Typ (WildcardType , decomposed = false )
407
399
408
400
case Block (Nil , expr) =>
409
401
project(expr)
410
402
411
403
case _ =>
412
404
// Pattern is an arbitrary expression; assume a skolem (i.e. an unknown value) of the pattern type
413
- Typ (pat.tpe.narrow, false )
405
+ Typ (pat.tpe.narrow, decomposed = false )
414
406
}
415
407
416
408
private def project (tp : Type ): Space = tp match {
@@ -458,29 +450,37 @@ class SpaceEngine(using Context) extends SpaceLogic {
458
450
* case (IntExpr(_), IntExpr(_)) =>
459
451
* case (BooleanExpr(_), BooleanExpr(_)) =>
460
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.
461
459
*/
462
- 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) {
463
461
464
462
tp match {
465
463
case tp @ AppliedType (tycon, args) =>
466
464
if tycon.typeSymbol.isPatternBound then return WildcardType
467
465
468
466
val args2 =
469
- if (tycon.isRef(defn.ArrayClass )) args.map(arg => erase(arg, inArray = true ))
470
- else args.map(arg => erase(arg, inArray = false ))
471
- 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)
472
470
473
471
case tp @ OrType (tp1, tp2) =>
474
- OrType (erase(tp1, inArray), erase(tp2, inArray), tp.isSoft)
472
+ OrType (erase(tp1, inArray, isValue ), erase(tp2, inArray, isValue ), tp.isSoft)
475
473
476
474
case AndType (tp1, tp2) =>
477
- AndType (erase(tp1, inArray), erase(tp2, inArray))
475
+ AndType (erase(tp1, inArray, isValue ), erase(tp2, inArray, isValue ))
478
476
479
477
case tp @ RefinedType (parent, _, _) =>
480
- erase(parent)
478
+ erase(parent, inArray, isValue )
481
479
482
480
case tref : TypeRef if tref.symbol.isPatternBound =>
483
- if (inArray) tref.underlying else WildcardType
481
+ if inArray then tref.underlying
482
+ else if isValue then tref.superType
483
+ else WildcardType
484
484
485
485
case _ => tp
486
486
}
@@ -864,25 +864,28 @@ class SpaceEngine(using Context) extends SpaceLogic {
864
864
&& ! sel.tpe.widen.isRef(defn.QuotedTypeClass )
865
865
866
866
def checkRedundancy (_match : Match ): Unit = {
867
+ debug.println(s " ---------------checking redundant patterns ${_match.show}" )
868
+
867
869
val Match (sel, cases) = _match
868
870
val selTyp = sel.tpe.widen.dealias
869
871
870
872
if (! redundancyCheckable(sel)) return
871
873
872
874
val targetSpace =
873
- if (ctx.explicitNulls || selTyp.classSymbol.isPrimitiveValueClass)
875
+ if ! selTyp.classSymbol.isNullableClass then
874
876
project(selTyp)
875
877
else
876
878
project(OrType (selTyp, constantNullType, soft = false ))
877
879
878
880
// in redundancy check, take guard as false in order to soundly approximate
879
- def projectPrevCases ( cases : List [ CaseDef ]) : List [ Space ] =
880
- cases.map { x =>
881
+ val spaces = cases.map { x =>
882
+ val res =
881
883
if (x.guard.isEmpty) project(x.pat)
882
884
else Empty
883
- }
884
885
885
- val spaces = projectPrevCases(cases)
886
+ debug.println(s " ${x.pat.show} ====> ${res}" )
887
+ res
888
+ }
886
889
887
890
(1 until cases.length).foreach { i =>
888
891
val pat = cases(i).pat
@@ -902,20 +905,13 @@ class SpaceEngine(using Context) extends SpaceLogic {
902
905
if (covered == Empty && ! isNullLit(pat)) covered = curr
903
906
904
907
if (isSubspace(covered, prevs)) {
905
- report.warning(MatchCaseUnreachable (), pat.srcPos)
906
- }
907
-
908
- // if last case is `_` and only matches `null`, produce a warning
909
- // If explicit nulls are enabled, this check isn't needed because most of the cases
910
- // that would trigger it would also trigger unreachability warnings.
911
- if (! ctx.explicitNulls && i == cases.length - 1 && ! isNullLit(pat) ) {
912
- val spaces = flatten(simplify(minus(covered, prevs)))
913
- if spaces.lengthCompare(10 ) < 0 then
914
- dedup(spaces).toList match
915
- case Typ (`constantNullType`, _) :: Nil =>
916
- report.warning(MatchCaseOnlyNullWarning (), pat.srcPos)
917
- case s =>
918
- 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)
919
915
}
920
916
}
921
917
}
0 commit comments