Skip to content

Commit c367a50

Browse files
committed
Drop unnamed tuple <: named tuple, but allow literal tuple to conform
We can drop the unnamed tuple <: named tuple relationship, but still allow tuple syntax to conform to a named tuple prototype, with a small change in Desugar#tuple.
1 parent 800aa0e commit c367a50

File tree

4 files changed

+32
-12
lines changed

4 files changed

+32
-12
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

+4-1
Original file line numberDiff line numberDiff line change
@@ -1502,8 +1502,11 @@ object desugar {
15021502
tree1.withSpan(tree.span)
15031503
else
15041504
cpy.Tuple(tree)(elemValues)
1505-
val names = elems.collect:
1505+
var names = elems.collect:
15061506
case NamedArg(name, arg) => name
1507+
if names.isEmpty then
1508+
typer.Inferencing.isFullyDefined(pt, typer.ForceDegree.failBottom)
1509+
names = pt.namedTupleNames
15071510
if names.isEmpty || ctx.mode.is(Mode.Pattern) then
15081511
tup
15091512
else

compiler/src/dotty/tools/dotc/core/TypeUtils.scala

+7
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,13 @@ class TypeUtils {
134134
def namedTupleElementTypes(using Context): List[(TermName, Type)] =
135135
namedTupleElementTypesUpTo(Int.MaxValue)
136136

137+
def namedTupleNames(using Context): List[Name] =
138+
self.normalized.dealias match
139+
case defn.NamedTuple(nmes, _) =>
140+
nmes.tupleElementTypes.getOrElse(Nil).map:
141+
case ConstantType(Constants.Constant(str: String)) => str.toTermName
142+
case _ => Nil
143+
137144
def isNamedTupleType(using Context): Boolean = self match
138145
case defn.NamedTuple(_, _) => true
139146
case _ => false

library/src/scala/NamedTuple.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import compiletime.ops.boolean.*
66
object NamedTuple:
77

88
opaque type AnyNamedTuple = Any
9-
opaque type NamedTuple[N <: Tuple, +V <: Tuple] >: V <: AnyNamedTuple = V
9+
opaque type NamedTuple[N <: Tuple, +V <: Tuple] <: AnyNamedTuple = V
1010

1111
def apply[N <: Tuple, V <: Tuple](x: V): NamedTuple[N, V] = x
1212

tests/neg/named-tuples.check

+20-10
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,24 @@
6161
28 | case (name = n, age = a) => () // error // error
6262
| ^^^^^^^
6363
| No element named `age` is defined in selector type (String, Int)
64-
-- [E172] Type Error: tests/neg/named-tuples.scala:30:27 ---------------------------------------------------------------
64+
-- [E007] Type Mismatch Error: tests/neg/named-tuples.scala:30:21 ------------------------------------------------------
6565
30 | val pp = person ++ (1, 2) // error
66-
| ^
67-
| Cannot prove that Tuple.Disjoint[(("name" : String), ("age" : String)), Tuple] =:= (true : Boolean).
68-
-- [E172] Type Error: tests/neg/named-tuples.scala:33:18 ---------------------------------------------------------------
66+
| ^^^^^^
67+
| Found: (Int, Int)
68+
| Required: NamedTuple.NamedTuple[N2, Tuple]
69+
|
70+
| where: N2 is a type variable with constraint <: Tuple
71+
|
72+
| longer explanation available when compiling with `-explain`
73+
-- [E007] Type Mismatch Error: tests/neg/named-tuples.scala:33:12 ------------------------------------------------------
6974
33 | person ++ (1, 2) match // error
70-
| ^
71-
| Cannot prove that Tuple.Disjoint[(("name" : String), ("age" : String)), Tuple] =:= (true : Boolean).
75+
| ^^^^^^
76+
| Found: (Int, Int)
77+
| Required: NamedTuple.NamedTuple[N2, Tuple]
78+
|
79+
| where: N2 is a type variable with constraint <: Tuple
80+
|
81+
| longer explanation available when compiling with `-explain`
7282
-- Error: tests/neg/named-tuples.scala:36:17 ---------------------------------------------------------------------------
7383
36 | val bad = ("", age = 10) // error
7484
| ^^^^^^^^
@@ -103,8 +113,8 @@
103113
-- Warning: tests/neg/named-tuples.scala:25:29 -------------------------------------------------------------------------
104114
25 | val (name = x, agee = y) = person // error
105115
| ^^^^^^
106-
|pattern's type (String, Int) is more specialized than the right hand side expression's type (name : String, age : Int)
116+
| pattern's type (String, Int) does not match the right hand side expression's type (name : String, age : Int)
107117
|
108-
|If the narrowing is intentional, this can be communicated by adding `: @unchecked` after the expression,
109-
|which may result in a MatchError at runtime.
110-
|This patch can be rewritten automatically under -rewrite -source 3.2-migration.
118+
| If the narrowing is intentional, this can be communicated by adding `: @unchecked` after the expression,
119+
| which may result in a MatchError at runtime.
120+
| This patch can be rewritten automatically under -rewrite -source 3.2-migration.

0 commit comments

Comments
 (0)