Skip to content

Commit c91ee59

Browse files
committed
Emit unchecked warning for abstract type args in non-final types
1 parent 212d87e commit c91ee59

File tree

3 files changed

+47
-20
lines changed

3 files changed

+47
-20
lines changed

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

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -144,26 +144,30 @@ object TypeTestsCasts {
144144
case _ => recur(defn.AnyType, tpT)
145145
}
146146
case tpe @ AppliedType(tycon, targs) if !trustTypeApplication =>
147-
X.widenDealias match {
148-
case OrType(tp1, tp2) =>
149-
// This case is required to retrofit type inference,
150-
// which cut constraints in the following two cases:
151-
// - T1 <:< T2 | T3
152-
// - T1 & T2 <:< T3
153-
// See TypeComparer#either
154-
recur(tp1, P) && recur(tp2, P)
155-
case tpX: FlexibleType =>
156-
recur(tpX.underlying, P)
157-
case x =>
158-
// always false test warnings are emitted elsewhere
159-
// provablyDisjoint wants fully applied types as input; because we're in the middle of erasure, we sometimes get raw types here
160-
val xApplied =
161-
val tparams = x.typeParams
162-
if tparams.isEmpty then x else x.appliedTo(tparams.map(_ => WildcardType))
163-
TypeComparer.provablyDisjoint(xApplied, tpe.derivedAppliedType(tycon, targs.map(_ => WildcardType)))
164-
|| typeArgsDeterminable(X, tpe)
165-
||| i"its type arguments can't be determined from $X"
166-
}
147+
val abstractArgs = targs.filter(isAbstract)
148+
if abstractArgs.nonEmpty && !tycon.classSymbol.is(Final) then
149+
i"type arguments $abstractArgs refer to abstract types,"
150+
else
151+
X.widenDealias match {
152+
case OrType(tp1, tp2) =>
153+
// This case is required to retrofit type inference,
154+
// which cut constraints in the following two cases:
155+
// - T1 <:< T2 | T3
156+
// - T1 & T2 <:< T3
157+
// See TypeComparer#either
158+
recur(tp1, P) && recur(tp2, P)
159+
case tpX: FlexibleType =>
160+
recur(tpX.underlying, P)
161+
case x =>
162+
// always false test warnings are emitted elsewhere
163+
// provablyDisjoint wants fully applied types as input; because we're in the middle of erasure, we sometimes get raw types here
164+
val xApplied =
165+
val tparams = x.typeParams
166+
if tparams.isEmpty then x else x.appliedTo(tparams.map(_ => WildcardType))
167+
TypeComparer.provablyDisjoint(xApplied, tpe.derivedAppliedType(tycon, targs.map(_ => WildcardType)))
168+
|| typeArgsDeterminable(X, tpe)
169+
||| i"its type arguments can't be determined from $X"
170+
}
167171
case AndType(tp1, tp2) => recur(X, tp1) && recur(X, tp2)
168172
case OrType(tp1, tp2) => recur(X, tp1) && recur(X, tp2)
169173
case AnnotatedType(t, _) => recur(X, t)

tests/neg/i24322.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
trait A[+T]:
2+
def x: T
3+
trait B[+T] extends A[T]:
4+
def y: T
5+
6+
object Troll extends A[Int] with B[Any]:
7+
def x: Int = 0
8+
def y: Any = ""
9+
10+
def f[T](a: A[T]): T = a match {
11+
case b: B[T] => b.y // error
12+
case _ => a.x
13+
}

tests/pos/i24322.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
trait A[+T]:
2+
def x: T
3+
final class B[+T] extends A[T]:
4+
def x: T = ???
5+
def y: T = ???
6+
7+
def f[T](a: A[T]): T = a match {
8+
case b: B[T] => b.y // compiles without warning
9+
case _ => a.x
10+
}

0 commit comments

Comments
 (0)