diff --git a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala index ac7cbf6c36a6..71e8e2015cd2 100644 --- a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala +++ b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala @@ -254,14 +254,17 @@ trait PatternTypeConstrainer { self: TypeComparer => val result = tyconS.typeParams.lazyZip(argsS).lazyZip(argsP).forall { (param, argS, argP) => val variance = param.paramVarianceSign - if variance != 0 && !assumeInvariantRefinement then true - else { + if variance == 0 || assumeInvariantRefinement || + // As a special case, when pattern and scrutinee types have the same type constructor, + // we infer better bounds for pattern-bound abstract types. + argP.typeSymbol.isPatternBound && patternTp.classSymbol == scrutineeTp.classSymbol + then val TypeBounds(loS, hiS) = argS.bounds var res = true if variance < 1 then res &&= isSubType(loS, argP) if variance > -1 then res &&= isSubType(argP, hiS) res - } + else true } if !result then constraint = saved diff --git a/tests/pos/i14739.scala b/tests/pos/i14739.scala new file mode 100644 index 000000000000..42c396d59388 --- /dev/null +++ b/tests/pos/i14739.scala @@ -0,0 +1,4 @@ +abstract class Foo[+A]: + def head: A + def foo[T](xs: Foo[T]): T = xs match + case xs: Foo[u] => xs.head: u diff --git a/tests/pos/i14739.works.scala b/tests/pos/i14739.works.scala new file mode 100644 index 000000000000..1956f23d2821 --- /dev/null +++ b/tests/pos/i14739.works.scala @@ -0,0 +1,4 @@ +abstract class Foo[T]: + def head: T + def foo(xs: Foo[T]): T = xs match + case xs: Foo[u] => xs.head: u