Skip to content

Commit cce70da

Browse files
authored
Make Named Tuples a stable feature in 3.7 (#22753)
We make the Named Tuples again a standard feature, after they were delayed before 3.6 release #22045 Includes fixes to make deprecated syntax warnings and its migration still work - in many cases `untpd.Tuple` has become `untpd.Parens`
2 parents 120f305 + d029170 commit cce70da

File tree

71 files changed

+174
-247
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+174
-247
lines changed

compiler/src/dotty/tools/dotc/config/Feature.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ object Feature:
3333
val pureFunctions = experimental("pureFunctions")
3434
val captureChecking = experimental("captureChecking")
3535
val into = experimental("into")
36-
val namedTuples = experimental("namedTuples")
3736
val modularity = experimental("modularity")
3837
val quotedPatternsWithPolymorphicFunctions = experimental("quotedPatternsWithPolymorphicFunctions")
3938
val betterFors = experimental("betterFors")
@@ -63,7 +62,6 @@ object Feature:
6362
(pureFunctions, "Enable pure functions for capture checking"),
6463
(captureChecking, "Enable experimental capture checking"),
6564
(into, "Allow into modifier on parameter types"),
66-
(namedTuples, "Allow named tuples"),
6765
(modularity, "Enable experimental modularity features"),
6866
(betterFors, "Enable improvements in `for` comprehensions")
6967
)

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,7 @@ object Parsers {
671671
else leading :: Nil
672672

673673
def maybeNamed(op: () => Tree): () => Tree = () =>
674-
if isIdent && in.lookahead.token == EQUALS && in.featureEnabled(Feature.namedTuples) then
674+
if isIdent && in.lookahead.token == EQUALS && sourceVersion.enablesNamedTuples then
675675
atSpan(in.offset):
676676
val name = ident()
677677
in.nextToken()
@@ -1160,6 +1160,13 @@ object Parsers {
11601160
patch(source, infixOp.span, asApply.show(using ctx.withoutColors))
11611161
asApply // allow to use pre-3.6 syntax in migration mode
11621162
else infixOp
1163+
case Parens(assign @ Assign(ident, value)) if !isNamedTupleOperator =>
1164+
report.errorOrMigrationWarning(DeprecatedInfixNamedArgumentSyntax(), infixOp.right.srcPos, MigrationVersion.AmbiguousNamedTupleSyntax)
1165+
if MigrationVersion.AmbiguousNamedTupleSyntax.needsPatch then
1166+
val asApply = cpy.Apply(infixOp)(Select(opInfo.operand, opInfo.operator.name), assign :: Nil)
1167+
patch(source, infixOp.span, asApply.show(using ctx.withoutColors))
1168+
asApply // allow to use pre-3.6 syntax in migration mode
1169+
else infixOp
11631170
case _ => infixOp
11641171
}
11651172

@@ -2177,7 +2184,7 @@ object Parsers {
21772184

21782185
if namedOK && isIdent && in.lookahead.token == EQUALS then
21792186
commaSeparated(() => namedArgType())
2180-
else if tupleOK && isIdent && in.lookahead.isColon && in.featureEnabled(Feature.namedTuples) then
2187+
else if tupleOK && isIdent && in.lookahead.isColon && sourceVersion.enablesNamedTuples then
21812188
commaSeparated(() => namedElem())
21822189
else
21832190
commaSeparated(() => argType())

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3362,7 +3362,7 @@ end QuotedTypeMissing
33623362

33633363
final class DeprecatedAssignmentSyntax(key: Name, value: untpd.Tree)(using Context) extends SyntaxMsg(DeprecatedAssignmentSyntaxID):
33643364
override protected def msg(using Context): String =
3365-
i"""Deprecated syntax: in the future it would be interpreted as a named tuple with one element,
3365+
i"""Deprecated syntax: since 3.7 this is interpreted as a named tuple with one element,
33663366
|not as an assignment.
33673367
|
33683368
|To assign a value, use curly braces: `{${key} = ${value}}`."""
@@ -3372,9 +3372,9 @@ final class DeprecatedAssignmentSyntax(key: Name, value: untpd.Tree)(using Conte
33723372

33733373
class DeprecatedInfixNamedArgumentSyntax()(using Context) extends SyntaxMsg(DeprecatedInfixNamedArgumentSyntaxID):
33743374
def msg(using Context) =
3375-
i"""Deprecated syntax: infix named arguments lists are deprecated; in the future it would be interpreted as a single name tuple argument.
3375+
i"""Deprecated syntax: infix named arguments lists are deprecated; since 3.7 it is interpreted as a single name tuple argument.
33763376
|To avoid this warning, either remove the argument names or use dotted selection."""
3377-
+ Message.rewriteNotice("This", version = SourceVersion.`3.6-migration`)
3377+
+ Message.rewriteNotice("This", version = SourceVersion.`3.7-migration`)
33783378

33793379
def explain(using Context) = ""
33803380

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

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -825,7 +825,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
825825
def tryNamedTupleSelection() =
826826
val namedTupleElems = qual.tpe.widenDealias.namedTupleElementTypes(true)
827827
val nameIdx = namedTupleElems.indexWhere(_._1 == selName)
828-
if nameIdx >= 0 && Feature.enabled(Feature.namedTuples) then
828+
if nameIdx >= 0 && sourceVersion.enablesNamedTuples then
829829
typed(
830830
untpd.Apply(
831831
untpd.Select(untpd.TypedSplice(qual), nme.apply),
@@ -3500,19 +3500,22 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
35003500
/** Checks if `tree` is a named tuple with one element that could be
35013501
* interpreted as an assignment, such as `(x = 1)`. If so, issues a warning.
35023502
*/
3503-
def checkDeprecatedAssignmentSyntax(tree: untpd.Tuple)(using Context): Unit =
3504-
tree.trees match
3505-
case List(NamedArg(name, value)) =>
3503+
def checkDeprecatedAssignmentSyntax(tree: untpd.Tuple | untpd.Parens)(using Context): Unit =
3504+
val assignmentArgs = tree match {
3505+
case untpd.Tuple(List(NamedArg(name, value))) =>
35063506
val tmpCtx = ctx.fresh.setNewTyperState()
35073507
typedAssign(untpd.Assign(untpd.Ident(name), value), WildcardType)(using tmpCtx)
3508-
if !tmpCtx.reporter.hasErrors then
3509-
// If there are no errors typing the above, then the named tuple is
3510-
// ambiguous and we issue a warning.
3511-
report.migrationWarning(DeprecatedAssignmentSyntax(name, value), tree.srcPos)
3512-
if MigrationVersion.AmbiguousNamedTupleSyntax.needsPatch then
3513-
patch(tree.source, Span(tree.span.start, tree.span.start + 1), "{")
3514-
patch(tree.source, Span(tree.span.end - 1, tree.span.end), "}")
3515-
case _ => ()
3508+
Option.unless(tmpCtx.reporter.hasErrors)(name -> value)
3509+
case untpd.Parens(Assign(ident: untpd.Ident, value)) => Some(ident.name -> value)
3510+
case _ => None
3511+
}
3512+
assignmentArgs.foreach: (name, value) =>
3513+
// If there are no errors typing the above, then the named tuple is
3514+
// ambiguous and we issue a warning.
3515+
report.migrationWarning(DeprecatedAssignmentSyntax(name, value), tree.srcPos)
3516+
if MigrationVersion.AmbiguousNamedTupleSyntax.needsPatch then
3517+
patch(tree.source, Span(tree.span.start, tree.span.start + 1), "{")
3518+
patch(tree.source, Span(tree.span.end - 1, tree.span.end), "}")
35163519

35173520
/** Retrieve symbol attached to given tree */
35183521
protected def retrieveSym(tree: untpd.Tree)(using Context): Symbol = tree.removeAttachment(SymOfTree) match {
@@ -3621,6 +3624,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
36213624
case tree: untpd.SplicePattern => typedSplicePattern(tree, pt)
36223625
case tree: untpd.MacroTree => report.error("Unexpected macro", tree.srcPos); tpd.nullLiteral // ill-formed code may reach here
36233626
case tree: untpd.Hole => typedHole(tree, pt)
3627+
case tree: untpd.Parens =>
3628+
checkDeprecatedAssignmentSyntax(tree)
3629+
typedUnadapted(desugar(tree, pt), pt, locked)
36243630
case _ => typedUnadapted(desugar(tree, pt), pt, locked)
36253631
}
36263632

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ class CompilationTests {
7979
compileFile("tests/rewrites/i20002.scala", defaultOptions.and("-indent", "-rewrite")),
8080
compileDir("tests/rewrites/annotation-named-pararamters", defaultOptions.and("-rewrite", "-source:3.6-migration")),
8181
compileFile("tests/rewrites/i21418.scala", unindentOptions.and("-rewrite", "-source:3.5-migration")),
82-
compileFile("tests/rewrites/infix-named-args.scala", defaultOptions.and("-rewrite", "-source:3.6-migration")),
82+
compileFile("tests/rewrites/infix-named-args.scala", defaultOptions.and("-rewrite", "-source:3.7-migration")),
8383
compileFile("tests/rewrites/ambiguous-named-tuple-assignment.scala", defaultOptions.and("-rewrite", "-source:3.6-migration")),
8484
compileFile("tests/rewrites/i21382.scala", defaultOptions.and("-indent", "-rewrite")),
8585
compileFile("tests/rewrites/unused.scala", defaultOptions.and("-rewrite", "-Wunused:all")),

docs/_docs/reference/experimental/named-tuples.md renamed to docs/_docs/reference/other-new-features/named-tuples.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
---
22
layout: doc-page
33
title: "Named Tuples"
4-
nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/named-tuples.html
4+
nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/named-tuples.html
55
---
66

7-
The elements of a tuple can now be named. Example:
7+
Starting in Scala 3.7, the elements of a tuple can be named.
8+
Example:
89
```scala
910
type Person = (name: String, age: Int)
1011
val Bob: Person = (name = "Bob", age = 33)

docs/sidebar.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ subsection:
7272
- page: reference/other-new-features/export.md
7373
- page: reference/other-new-features/opaques.md
7474
- page: reference/other-new-features/opaques-details.md
75+
- page: reference/other-new-features/named-tuples.md
7576
- page: reference/other-new-features/open-classes.md
7677
- page: reference/other-new-features/parameter-untupling.md
7778
- page: reference/other-new-features/parameter-untupling-spec.md
@@ -158,7 +159,6 @@ subsection:
158159
- page: reference/experimental/cc.md
159160
- page: reference/experimental/purefuns.md
160161
- page: reference/experimental/tupled-function.md
161-
- page: reference/experimental/named-tuples.md
162162
- page: reference/experimental/modularity.md
163163
- page: reference/experimental/typeclasses.md
164164
- page: reference/experimental/runtimeChecked.md

language-server/test/dotty/tools/languageserver/CompletionTest.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,8 +1725,7 @@ class CompletionTest {
17251725
.completion(m6, Set())
17261726

17271727
@Test def namedTupleCompletion: Unit =
1728-
code"""|import scala.language.experimental.namedTuples
1729-
|
1728+
code"""|
17301729
|val person: (name: String, city: String) =
17311730
| (name = "Jamie", city = "Lausanne")
17321731
|

library/src/scala/NamedTuple.scala

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package scala
2-
import annotation.experimental
32
import compiletime.ops.boolean.*
43

5-
@experimental
64
object NamedTuple:
75

86
/** The type to which named tuples get mapped to. For instance,
@@ -133,7 +131,6 @@ object NamedTuple:
133131
end NamedTuple
134132

135133
/** Separate from NamedTuple object so that we can match on the opaque type NamedTuple. */
136-
@experimental
137134
object NamedTupleDecomposition:
138135
import NamedTuple.*
139136
extension [N <: Tuple, V <: Tuple](x: NamedTuple[N, V])

library/src/scala/runtime/stdLibPatches/language.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ object language:
9797
* @see [[https://dotty.epfl.ch/docs/reference/experimental/named-tuples]]
9898
*/
9999
@compileTimeOnly("`namedTuples` can only be used at compile time in import statements")
100+
@deprecated("The experimental.namedTuples language import is no longer needed since the feature is now standard", since = "3.7")
100101
object namedTuples
101102

102103
/** Experimental support for new features for better modularity, including

0 commit comments

Comments
 (0)