diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 302ad7987889..cb58ebf8c025 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -3360,37 +3360,44 @@ class MatchReducer(initctx: Context) extends TypeComparer(initctx) { * * See notably neg/wildcard-match.scala for examples of this. * - * See neg/i13780.scala and neg/i13780-1.scala for ClassCastException - * reproducers if we disable this check. + * See neg/i13780.scala, neg/i13780-1.scala and neg/i19746.scala for + * ClassCastException reproducers if we disable this check. */ - def followEverythingConcrete(tp: Type): Type = - val widenedTp = tp.widenDealias - val tp1 = widenedTp.normalized - - def followTp1: Type = - // If both widenDealias and normalized did something, start again - if (tp1 ne widenedTp) && (widenedTp ne tp) then followEverythingConcrete(tp1) - else tp1 + def isConcrete(tp: Type): Boolean = + val tp1 = tp.normalized tp1 match case tp1: TypeRef => - tp1.info match - case TypeAlias(tl: HKTypeLambda) => tl - case MatchAlias(tl: HKTypeLambda) => tl - case _ => followTp1 - case tp1 @ AppliedType(tycon, args) => - val concreteTycon = followEverythingConcrete(tycon) - if concreteTycon eq tycon then followTp1 - else followEverythingConcrete(concreteTycon.applyIfParameterized(args)) + if tp1.symbol.isClass then true + else + tp1.info match + case info: AliasingBounds => isConcrete(info.alias) + case _ => false + case tp1: AppliedType => + isConcrete(tp1.tycon) && isConcrete(tp1.superType) + case tp1: HKTypeLambda => + true + case tp1: TermRef => + !tp1.symbol.is(Param) && isConcrete(tp1.underlying) + case tp1: TermParamRef => + false + case tp1: SingletonType => + isConcrete(tp1.underlying) + case tp1: ExprType => + isConcrete(tp1.underlying) + case tp1: AnnotatedType => + isConcrete(tp1.parent) + case tp1: RefinedType => + isConcrete(tp1.underlying) + case tp1: RecType => + isConcrete(tp1.underlying) + case tp1: AndOrType => + isConcrete(tp1.tp1) && isConcrete(tp1.tp2) case _ => - followTp1 - end followEverythingConcrete - - def isConcrete(tp: Type): Boolean = - followEverythingConcrete(tp) match - case tp1: AndOrType => isConcrete(tp1.tp1) && isConcrete(tp1.tp2) - case tp1 => tp1.underlyingClassRef(refinementOK = true).exists + val tp2 = tp1.stripped.stripLazyRef + (tp2 ne tp) && isConcrete(tp2) + end isConcrete // Actual matching logic diff --git a/tests/neg/i19746.check b/tests/neg/i19746.check new file mode 100644 index 000000000000..6be8700bb550 --- /dev/null +++ b/tests/neg/i19746.check @@ -0,0 +1,7 @@ +-- [E007] Type Mismatch Error: tests/neg/i19746.scala:9:30 ------------------------------------------------------------- +9 | def asX(w: W[Any]): w.X = self // error: Type Mismatch + | ^^^^ + | Found: (self : Any) + | Required: w.X + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i19746.scala b/tests/neg/i19746.scala new file mode 100644 index 000000000000..b2969d4d3fef --- /dev/null +++ b/tests/neg/i19746.scala @@ -0,0 +1,15 @@ +trait V: + type X = this.type match + case W[x] => x + +trait W[+Y] extends V + +object Test: + extension (self: Any) def as[T]: T = + def asX(w: W[Any]): w.X = self // error: Type Mismatch + asX(new W[T] {}) + + def main(args: Array[String]): Unit = + val b = 0.as[Boolean] // java.lang.ClassCastException if the code is allowed to compile + println(b) +end Test diff --git a/tests/pos/TupleReverse.scala b/tests/pos/TupleReverse.scala index 9b83280afcf1..a5ef2ed69f0c 100644 --- a/tests/pos/TupleReverse.scala +++ b/tests/pos/TupleReverse.scala @@ -12,5 +12,6 @@ def test[T1, T2, T3, T4] = def test2[Tup <: Tuple] = summon[Reverse[Tup] =:= Reverse[Tup]] -def test3[T1, T2, T3, T4](tup1: (T1, T2, T3, T4)) = - summon[Reverse[tup1.type] =:= (T4, T3, T2, T1)] +def test3[T1, T2, T3, T4](tup1: (T1, T2, T3, T4)): Unit = + val tup11: (T1, T2, T3, T4) = tup1 + summon[Reverse[tup11.type] =:= (T4, T3, T2, T1)] diff --git a/tests/pos/TupleReverseOnto.scala b/tests/pos/TupleReverseOnto.scala index 09d5a323cb29..eca8a3e3033c 100644 --- a/tests/pos/TupleReverseOnto.scala +++ b/tests/pos/TupleReverseOnto.scala @@ -13,6 +13,7 @@ def test2[Tup1 <: Tuple, Tup2 <: Tuple] = summon[ReverseOnto[EmptyTuple, Tup1] =:= Tup1] summon[ReverseOnto[Tup1, EmptyTuple] =:= Reverse[Tup1]] -def test3[T1, T2, T3, T4](tup1: (T1, T2), tup2: (T3, T4)) = - summon[ReverseOnto[tup1.type, tup2.type] <:< (T2, T1, T3, T4)] - summon[ReverseOnto[tup1.type, tup2.type] =:= T2 *: T1 *: tup2.type] +def test3[T1, T2, T3, T4](tup1: (T1, T2), tup2: (T3, T4)): Unit = + val tup11: (T1, T2) = tup1 + summon[ReverseOnto[tup11.type, tup2.type] <:< (T2, T1, T3, T4)] + summon[ReverseOnto[tup11.type, tup2.type] =:= T2 *: T1 *: tup2.type]