Skip to content

Commit 86d1a8c

Browse files
committed
Avoid expensive computation for null warnings
1 parent b873087 commit 86d1a8c

File tree

4 files changed

+40
-47
lines changed

4 files changed

+40
-47
lines changed

Diff for: compiler/src/dotty/tools/dotc/reporting/messages.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,7 @@ import transform.SymUtils._
838838

839839
class MatchCaseOnlyNullWarning()(using Context)
840840
extends PatternMatchMsg(MatchCaseOnlyNullWarningID) {
841-
def msg = em"""Only ${hl("null")} is matched. Consider using ${hl("case null =>")} instead."""
841+
def msg = em"""Unreachable case. If ${hl("null")} is matched, consider using ${hl("case null =>")}."""
842842
def explain = ""
843843
}
844844

Diff for: compiler/src/dotty/tools/dotc/transform/patmat/Space.scala

+37-44
Original file line numberDiff line numberDiff line change
@@ -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
}

Diff for: tests/patmat/i12530.check

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
6: Match case Unreachable
2+
14: Match case Unreachable

Diff for: tests/patmat/null.check

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
6: Match case Unreachable
1+
6: Pattern Match
22
13: Pattern Match
3-
18: Match case Unreachable
43
20: Pattern Match

0 commit comments

Comments
 (0)