From bf4f126aa423af799f8f26856493e8828d4d4ff0 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 3 May 2022 18:07:26 +0100 Subject: [PATCH 1/2] Heal pattern-bound type by gathering constraints --- .../dotty/tools/dotc/core/PatternTypeConstrainer.scala | 8 +++++--- tests/pos/i14739.scala | 4 ++++ tests/pos/i14739.works.scala | 4 ++++ 3 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 tests/pos/i14739.scala create mode 100644 tests/pos/i14739.works.scala diff --git a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala index ac7cbf6c36a6..38ee0540b231 100644 --- a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala +++ b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala @@ -254,14 +254,16 @@ 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 || + // heal the type if it's a (pattern-bound) type variable, provided we didn't upcast the pattern type: + 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 From 414daeb8e6354d18ff4c25e373cd2e2305408437 Mon Sep 17 00:00:00 2001 From: Aleksander Boruch-Gruszecki Date: Tue, 17 May 2022 10:19:50 +0200 Subject: [PATCH 2/2] Update a comment in PatternTypeConstrainer --- .../src/dotty/tools/dotc/core/PatternTypeConstrainer.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala index 38ee0540b231..71e8e2015cd2 100644 --- a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala +++ b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala @@ -255,7 +255,8 @@ trait PatternTypeConstrainer { self: TypeComparer => tyconS.typeParams.lazyZip(argsS).lazyZip(argsP).forall { (param, argS, argP) => val variance = param.paramVarianceSign if variance == 0 || assumeInvariantRefinement || - // heal the type if it's a (pattern-bound) type variable, provided we didn't upcast the pattern type: + // 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