From 104ee6267161661fc3990e95839baeac669598a4 Mon Sep 17 00:00:00 2001 From: Eugene Flesselle Date: Tue, 26 Mar 2024 18:15:11 +0100 Subject: [PATCH] Always use baseType when constraining patternTp with scrutineeTp In the following example: ``` type Cond[B <: Boolean] <: Tuple2[String, String] = B match ... type Decoded[B <: Boolean] = Cond[B] match case (h1, _) => Int ``` When constraining the `(h1, _)` pattern with `Cond[B]`, we incorrectly assumed we could constrain h1 with B, because `Cond[B]` is an applied type of which the baseType is Tuple2. The issue can be fixed in constrainSimplePatternType by obtaining the baseType for both the patternTp and scrutineeTp, with the most general base of the two. So in the above example, we wound constrain `B` with String by obtaining `(String, String)` from `Cond[B]`. --- .../dotc/core/PatternTypeConstrainer.scala | 12 ++++---- tests/pos/i19706.scala | 29 +++++++++++++++++++ 2 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 tests/pos/i19706.scala diff --git a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala index 38f8e19e2737..7942bbaa3d45 100644 --- a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala +++ b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala @@ -200,8 +200,8 @@ trait PatternTypeConstrainer { self: TypeComparer => * * This function expects to receive two types (scrutinee and pattern), both * of which have class symbols, one of which is derived from another. If the - * type "being derived from" is an applied type, it will 1) "upcast" the - * deriving type to an applied type with the same constructor and 2) infer + * type "being derived from" is an applied type, it will 1) "upcast" both + * types to an applied type with the same constructor and 2) infer * constraints for the applied types' arguments that follow from both * types being inhabited by one value (the scrutinee). * @@ -252,11 +252,9 @@ trait PatternTypeConstrainer { self: TypeComparer => val scrutineeCls = scrutineeTp.classSymbol // NOTE: we already know that there is a derives-from relationship in either direction - val upcastPattern = - patternCls.derivesFrom(scrutineeCls) - - val pt = if upcastPattern then patternTp.baseType(scrutineeCls) else patternTp - val tp = if !upcastPattern then scrutineeTp.baseType(patternCls) else scrutineeTp + val base = if patternCls.derivesFrom(scrutineeCls) then scrutineeCls else patternCls + val pt = patternTp.baseType(base) + val tp = scrutineeTp.baseType(base) val assumeInvariantRefinement = migrateTo3 || forceInvariantRefinement || refinementIsInvariant(patternTp) diff --git a/tests/pos/i19706.scala b/tests/pos/i19706.scala new file mode 100644 index 000000000000..ba66b3baf5c4 --- /dev/null +++ b/tests/pos/i19706.scala @@ -0,0 +1,29 @@ + +import scala.compiletime.ops.string.{Length, Matches, Substring} + +def emptyContext(): Unit = + summon[Decoded["Tuple(0, EmptyTuple)"] =:= 0 *: EmptyTuple] + +type Decoded[S <: String] = Matches[S, "Tuple(.+, .+)"] match + case true => Parsed[Substring[S, 6, 19], 0, ""] match + case (h, t) => Decoded["0"] *: EmptyTuple + case false => 0 + +type Parsed[S <: String, I <: Int, A <: String] <: (String, String) = Matches[S, "other"] match + case true => I match + case 1 => ("", "") + case _ => Parsed[Substring[S, 1, Length[S]], I, ""] + case false => ("0", "EmptyTuple") + + +object Minimization: + + type Cond[B <: Boolean] <: Tuple2[String, String] = B match + case true => ("", "") + case false => ("a", "b") + + type Decoded[B <: Boolean] = Cond[B] match + case (h1, _) => Int + + val _: Decoded[false] = 1 +