Skip to content

Commit 41c3dca

Browse files
committed
fix #3144: emit warnings for unchecked type patterns
1 parent 2a72ffb commit 41c3dca

File tree

11 files changed

+56
-4
lines changed

11 files changed

+56
-4
lines changed

compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ public enum ErrorMessageID {
9797
DuplicatePrivateProtectedQualifierID,
9898
ExpectedStartOfTopLevelDefinitionID,
9999
MissingReturnTypeWithReturnStatementID,
100+
UncheckedTypePatternID,
100101
;
101102

102103
public int errorNumber() {

compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,16 @@ object messages {
880880
|"""
881881
}
882882

883+
case class UncheckedTypePattern(msg: String)(implicit ctx: Context)
884+
extends Message(UncheckedTypePatternID) {
885+
val kind = "Unchecked Type Pattern"
886+
887+
val explanation =
888+
hl"""|Type arguments and type refinements are erased during compile time, thus it's
889+
|impossible to check them at run-time.
890+
|"""
891+
}
892+
883893
case class MatchCaseUnreachable()(implicit ctx: Context)
884894
extends Message(MatchCaseUnreachableID) {
885895
val kind = s"""Match ${hl"case"} Unreachable"""

compiler/src/dotty/tools/dotc/transform/patmat/Space.scala

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import Types._
77
import Contexts._
88
import Flags._
99
import ast.Trees._
10-
import ast.{tpd, untpd}
10+
import ast.tpd
1111
import Decorators._
1212
import Symbols._
1313
import StdNames._
@@ -20,6 +20,7 @@ import ProtoTypes._
2020
import transform.SymUtils._
2121
import reporting.diagnostic.messages._
2222
import config.Printers.{exhaustivity => debug}
23+
import util.Positions.Position
2324

2425
/** Space logic for checking exhaustivity and unreachability of pattern matching
2526
*
@@ -403,21 +404,30 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
403404
else
404405
Prod(pat.tpe.stripAnnots, fun.tpe.widen, fun.symbol, pats.map(project), irrefutable(fun))
405406
case Typed(pat @ UnApply(_, _, _), _) => project(pat)
406-
case Typed(expr, _) => Typ(erase(expr.tpe.stripAnnots), true)
407+
case Typed(expr, tp) => Typ(erase(expr.tpe.stripAnnots)(tp.pos), true)
407408
case _ =>
408409
debug.println(s"unknown pattern: $pat")
409410
Empty
410411
}
411412

412413
/* Erase a type binding according to erasure semantics in pattern matching */
413-
def erase(tp: Type): Type = tp match {
414+
def erase(tp: Type)(implicit pos: Position): Type = tp match {
414415
case tp @ AppliedType(tycon, args) =>
415416
if (tycon.isRef(defn.ArrayClass)) tp.derivedAppliedType(tycon, args.map(erase))
416-
else tp.derivedAppliedType(tycon, args.map(t => WildcardType))
417+
else {
418+
val ignoreWarning = args.forall(p => p.typeSymbol.is(BindDefinedType) || p.isInstanceOf[TypeBounds])
419+
if (!ignoreWarning)
420+
ctx.warning(UncheckedTypePattern("type arguments are not unchecked since they are eliminated by erasure"), pos)
421+
422+
tp.derivedAppliedType(tycon, args.map(t => WildcardType))
423+
}
417424
case OrType(tp1, tp2) =>
418425
OrType(erase(tp1), erase(tp2))
419426
case AndType(tp1, tp2) =>
420427
AndType(erase(tp1), erase(tp2))
428+
case tp: RefinedType =>
429+
ctx.warning(UncheckedTypePattern("type refinement is not unchecked since it is eliminated by erasure"), pos)
430+
tp.derivedRefinedType(erase(tp.parent), tp.refinedName, WildcardType)
421431
case _ => tp
422432
}
423433

tests/patmat/3144.check

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2: Unchecked Type Pattern
2+
7: Unchecked Type Pattern

tests/patmat/3144.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
sealed trait Foo[T]
2+
case class Bar[T](s: String)
3+
4+
object Test {
5+
def shouldError[T](foo: Foo[T]): String =
6+
foo match {
7+
case bar: Bar[T] => bar.s
8+
}
9+
}

tests/patmat/enum-HList.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2: Unchecked Type Pattern

tests/patmat/enum-Tree.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
8: Unchecked Type Pattern

tests/patmat/t10019.check

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2: Pattern Match Exhaustivity: (List(_, _: _*), Nil), (List(_, _: _*), List(_, _, _: _*)), (Nil, List(_, _: _*)), (List(_, _, _: _*), List(_, _: _*))
2+
11: Pattern Match Exhaustivity: (Foo(None), Foo(_))

tests/patmat/t10019.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
object Bug {
2+
def foo[T](t1: List[T], t2: List[T]) = (t1, t2) match {
3+
case (Nil, Nil) => ()
4+
case (List(_), List(_)) => ()
5+
}
6+
}
7+
8+
object Bug2 {
9+
sealed case class Foo(e: Option[Int])
10+
11+
def loop(s: Foo, t: Foo): Nothing = (s,t) match {
12+
case (Foo(Some(_)), _) => ???
13+
}
14+
}

tests/patmat/t3683.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
7: Unchecked Type Pattern

tests/patmat/t3683a.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
8: Unchecked Type Pattern
12
14: Pattern Match Exhaustivity: XX()

0 commit comments

Comments
 (0)