Skip to content

Commit 854d7ce

Browse files
committed
Refine bind tuple pattern typing for named tuples
1 parent 83e9c14 commit 854d7ce

File tree

2 files changed

+34
-2
lines changed

2 files changed

+34
-2
lines changed

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2774,6 +2774,20 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
27742774
if !isFullyDefined(pt, ForceDegree.all) then
27752775
return errorTree(tree, em"expected type of $tree is not fully defined")
27762776
val body1 = typed(tree.body, pt)
2777+
2778+
// When we pattern match a named tuple, both the named tuple pattern and the
2779+
// regular tuple pattern are desugared to a regular tuple unapply.
2780+
// If the pattern (body) is a named tuple pattern, we give the binding
2781+
// a named tuple type using pt; otherwise we give it the regular tuple type.
2782+
// For example, in `case x @ (a = 1, b = 2)`, the type of `x` will be `(a: Int, b: Int)`;
2783+
// in `case x @ (a, b)`, the type of `x` will be `(Int, Int)`.
2784+
def isNamedTuplePattern =
2785+
ctx.mode.is(Mode.Pattern)
2786+
&& pt.dealias.isNamedTupleType
2787+
&& tree.body.match
2788+
case untpd.Tuple((_: NamedArg) :: _) => true
2789+
case _ => false
2790+
27772791
body1 match {
27782792
case UnApply(fn, Nil, arg :: Nil)
27792793
if fn.symbol.exists && (fn.symbol.owner.derivesFrom(defn.TypeTestClass) || fn.symbol.owner == defn.ClassTagClass) && !body1.tpe.isError =>
@@ -2799,8 +2813,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
27992813
body1.isInstanceOf[RefTree] && !isWildcardArg(body1)
28002814
|| body1.isInstanceOf[Literal]
28012815
val symTp =
2802-
if isStableIdentifierOrLiteral || pt.dealias.isNamedTupleType then pt
2803-
// need to combine tuple element types with expected named type
2816+
if isStableIdentifierOrLiteral || isNamedTuplePattern then pt
28042817
else if isWildcardStarArg(body1)
28052818
|| pt == defn.ImplicitScrutineeTypeRef
28062819
|| body1.tpe <:< pt // There is some strange interaction with gadt matching.

tests/run/bind-tuple-pattern.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import annotation.experimental
2+
3+
def getNamedTuple: (x: Int, y: String) = (x = 42, y = "Hello")
4+
5+
@main def Test =
6+
getNamedTuple match
7+
case (x, y) => assert(x == 42 && y == "Hello")
8+
9+
getNamedTuple match
10+
case t @ (x = a, y = b) =>
11+
// t binds to a named tuple pattern
12+
// t: (x: Int, y: String)
13+
assert(a == t.x && b == t.y)
14+
15+
getNamedTuple match
16+
case t @ (a, b) =>
17+
// t binds to a regular tuple pattern
18+
// t: (Int, String)
19+
assert(t._1 == a && t._2 == b)

0 commit comments

Comments
 (0)