Skip to content

Commit

Permalink
Always use baseType when constraining patternTp with scrutineeTp
Browse files Browse the repository at this point in the history
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]`.
  • Loading branch information
EugeneFlesselle committed Apr 6, 2024
1 parent 960858d commit 521ce95
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 7 deletions.
12 changes: 5 additions & 7 deletions compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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).
*
Expand Down Expand Up @@ -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)
Expand Down
29 changes: 29 additions & 0 deletions tests/pos/i19706.scala
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 521ce95

Please sign in to comment.