Skip to content

Commit 5f115fc

Browse files
noti0na1tgodzik
authored andcommitted
Only trust the type application part for case class unapplies
[Cherry-picked 67024a9]
1 parent b309461 commit 5f115fc

File tree

2 files changed

+24
-6
lines changed

2 files changed

+24
-6
lines changed

compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ object TypeTestsCasts {
5656
* 9. if `X` is `T1 | T2`, checkable(T1, P) && checkable(T2, P).
5757
* 10. otherwise, ""
5858
*/
59-
def whyUncheckable(X: Type, P: Type, span: Span)(using Context): String = atPhase(Phases.refchecksPhase.next) {
59+
def whyUncheckable(X: Type, P: Type, span: Span, trustTypeApplication: Boolean)(using Context): String = atPhase(Phases.refchecksPhase.next) {
6060
extension (inline s1: String) inline def &&(inline s2: String): String = if s1 == "" then s2 else s1
6161
extension (inline b: Boolean) inline def |||(inline s: String): String = if b then "" else s
6262

@@ -142,7 +142,7 @@ object TypeTestsCasts {
142142
case defn.ArrayOf(tpE) => recur(tpE, tpT)
143143
case _ => recur(defn.AnyType, tpT)
144144
}
145-
case tpe @ AppliedType(tycon, targs) =>
145+
case tpe @ AppliedType(tycon, targs) if !trustTypeApplication =>
146146
X.widenDealias match {
147147
case OrType(tp1, tp2) =>
148148
// This case is required to retrofit type inference,
@@ -360,8 +360,7 @@ object TypeTestsCasts {
360360
if (sym.isTypeTest) {
361361
val argType = tree.args.head.tpe
362362
val isTrusted = tree.hasAttachment(PatternMatcher.TrustedTypeTestKey)
363-
if !isTrusted then
364-
checkTypePattern(expr.tpe, argType, expr.srcPos)
363+
checkTypePattern(expr.tpe, argType, expr.srcPos, isTrusted)
365364
transformTypeTest(expr, argType,
366365
flagUnrelated = enclosingInlineds.isEmpty) // if test comes from inlined code, dont't flag it even if it always false
367366
}
@@ -386,10 +385,10 @@ object TypeTestsCasts {
386385
def checkBind(tree: Bind)(using Context) =
387386
checkTypePattern(defn.ThrowableType, tree.body.tpe, tree.srcPos)
388387

389-
private def checkTypePattern(exprTpe: Type, castTpe: Type, pos: SrcPos)(using Context) =
388+
private def checkTypePattern(exprTpe: Type, castTpe: Type, pos: SrcPos, trustTypeApplication: Boolean = false)(using Context) =
390389
val isUnchecked = exprTpe.widenTermRefExpr.hasAnnotation(defn.UncheckedAnnot)
391390
if !isUnchecked then
392-
val whyNot = whyUncheckable(exprTpe, castTpe, pos.span)
391+
val whyNot = whyUncheckable(exprTpe, castTpe, pos.span, trustTypeApplication)
393392
if whyNot.nonEmpty then
394393
report.uncheckedWarning(UncheckedTypePattern(castTpe, whyNot), pos)
395394

tests/neg/i22051.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//> using options -Werror
2+
3+
def boundary[T](body: (T => RuntimeException) => T): T =
4+
case class Break(value: T) extends RuntimeException
5+
try body(Break.apply)
6+
catch case Break(t) => t // error: pattern matching on local classes is unsound currently
7+
8+
def test =
9+
boundary[Int]: EInt =>
10+
val v: String = boundary[String]: EString =>
11+
throw EInt(3)
12+
v.length // a runtime error: java.lang.ClassCastException
13+
14+
def boundaryCorrectBehaviour[T](body: (T => RuntimeException) => T): T =
15+
object local:
16+
// A correct implementation, but is still treated as a local class right now
17+
case class Break(value: T) extends RuntimeException
18+
try body(local.Break.apply)
19+
catch case local.Break(t) => t // error

0 commit comments

Comments
 (0)