Skip to content

Commit

Permalink
Make NamedTupls an experimental feature again
Browse files Browse the repository at this point in the history
  • Loading branch information
WojciechMazur committed Nov 27, 2024
1 parent c3d6c48 commit a4ea8bf
Show file tree
Hide file tree
Showing 51 changed files with 150 additions and 91 deletions.
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/config/Feature.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ object Feature:
val pureFunctions = experimental("pureFunctions")
val captureChecking = experimental("captureChecking")
val into = experimental("into")
val namedTuples = experimental("namedTuples")
val modularity = experimental("modularity")
val betterMatchTypeExtractors = experimental("betterMatchTypeExtractors")
val quotedPatternsWithPolymorphicFunctions = experimental("quotedPatternsWithPolymorphicFunctions")
Expand Down Expand Up @@ -65,6 +66,7 @@ object Feature:
(pureFunctions, "Enable pure functions for capture checking"),
(captureChecking, "Enable experimental capture checking"),
(into, "Allow into modifier on parameter types"),
(namedTuples, "Allow named tuples"),
(modularity, "Enable experimental modularity features"),
(betterMatchTypeExtractors, "Enable better match type extractors"),
(betterFors, "Enable improvements in `for` comprehensions")
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ object Parsers {
else leading :: Nil

def maybeNamed(op: () => Tree): () => Tree = () =>
if isIdent && in.lookahead.token == EQUALS && sourceVersion.isAtLeast(`3.6`) then
if isIdent && in.lookahead.token == EQUALS && in.featureEnabled(Feature.namedTuples) then
atSpan(in.offset):
val name = ident()
in.nextToken()
Expand Down Expand Up @@ -2172,7 +2172,7 @@ object Parsers {

if namedOK && isIdent && in.lookahead.token == EQUALS then
commaSeparated(() => namedArgType())
else if tupleOK && isIdent && in.lookahead.isColon && sourceVersion.isAtLeast(`3.6`) then
else if tupleOK && isIdent && in.lookahead.isColon && in.featureEnabled(Feature.namedTuples) then
commaSeparated(() => namedElem())
else
commaSeparated(() => argType())
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
def tryNamedTupleSelection() =
val namedTupleElems = qual.tpe.widenDealias.namedTupleElementTypes
val nameIdx = namedTupleElems.indexWhere(_._1 == selName)
if nameIdx >= 0 && sourceVersion.isAtLeast(`3.6`) then
if nameIdx >= 0 && Feature.enabled(Feature.namedTuples) then
typed(
untpd.Apply(
untpd.Select(untpd.TypedSplice(qual), nme.apply),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
layout: doc-page
title: "Named Tuples"
nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/named-tuples.html
nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/named-tuples.html
---

Starting in Scala 3.6, the elements of a tuple can be named. Example:
The elements of a tuple can now be named. Example:
```scala
type Person = (name: String, age: Int)
val Bob: Person = (name = "Bob", age = 33)
Expand Down
2 changes: 1 addition & 1 deletion docs/sidebar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ subsection:
- page: reference/other-new-features/export.md
- page: reference/other-new-features/opaques.md
- page: reference/other-new-features/opaques-details.md
- page: reference/other-new-features/named-tuples.md
- page: reference/other-new-features/open-classes.md
- page: reference/other-new-features/parameter-untupling.md
- page: reference/other-new-features/parameter-untupling-spec.md
Expand Down Expand Up @@ -159,6 +158,7 @@ subsection:
- page: reference/experimental/cc.md
- page: reference/experimental/purefuns.md
- page: reference/experimental/tupled-function.md
- page: reference/experimental/named-tuples.md
- page: reference/experimental/modularity.md
- page: reference/experimental/typeclasses.md
- page: reference/experimental/runtimeChecked.md
Expand Down
3 changes: 3 additions & 0 deletions library/src/scala/NamedTuple.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package scala
import annotation.experimental
import compiletime.ops.boolean.*

@experimental
object NamedTuple:

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

/** Separate from NamedTuple object so that we can match on the opaque type NamedTuple. */
@experimental
object NamedTupleDecomposition:
import NamedTuple.*
extension [N <: Tuple, V <: Tuple](x: NamedTuple[N, V])
Expand Down
1 change: 0 additions & 1 deletion library/src/scala/runtime/stdLibPatches/language.scala
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ object language:
* @see [[https://dotty.epfl.ch/docs/reference/experimental/named-tuples]]
*/
@compileTimeOnly("`namedTuples` can only be used at compile time in import statements")
@deprecated("The experimental.namedTuples language import is no longer needed since the feature is now standard", since = "3.6")
object namedTuples

/** Experimental support for new features for better modularity, including
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1988,7 +1988,8 @@ class CompletionSuite extends BaseCompletionSuite:

@Test def `namedTuple completions` =
check(
"""|import scala.NamedTuple.*
"""|import scala.language.experimental.namedTuples
|import scala.NamedTuple.*
|
|val person = (name = "Jamie", city = "Lausanne")
|
Expand All @@ -1999,7 +2000,8 @@ class CompletionSuite extends BaseCompletionSuite:

@Test def `Selectable with namedTuple Fields member` =
check(
"""|import scala.NamedTuple.*
"""|import scala.language.experimental.namedTuples
|import scala.NamedTuple.*
|
|class NamedTupleSelectable extends Selectable {
| type Fields <: AnyNamedTuple
Expand Down
14 changes: 7 additions & 7 deletions tests/neg/i20517.check
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
-- [E007] Type Mismatch Error: tests/neg/i20517.scala:9:43 -------------------------------------------------------------
9 | def dep(foo: Foo[Any]): From[foo.type] = (elem = "") // error
| ^^^^^^^^^^^
| Found: (elem : String)
| Required: NamedTuple.From[(foo : Foo[Any])]
|
| longer explanation available when compiling with `-explain`
-- [E007] Type Mismatch Error: tests/neg/i20517.scala:10:43 ------------------------------------------------------------
10 | def dep(foo: Foo[Any]): From[foo.type] = (elem = "") // error
| ^^^^^^^^^^^
| Found: (elem : String)
| Required: NamedTuple.From[(foo : Foo[Any])]
|
| longer explanation available when compiling with `-explain`
1 change: 1 addition & 0 deletions tests/neg/i20517.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import scala.language.experimental.namedTuples
import NamedTuple.From

case class Foo[+T](elem: T)
Expand Down
20 changes: 10 additions & 10 deletions tests/neg/infix-named-args.check
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
-- [E134] Type Error: tests/neg/infix-named-args.scala:2:13 ------------------------------------------------------------
2 | def f = 42 + (x = 1) // error // werror
-- [E134] Type Error: tests/neg/infix-named-args.scala:4:13 ------------------------------------------------------------
4 | def f = 42 + (x = 1) // error // werror
| ^^^^
| None of the overloaded alternatives of method + in class Int with types
| (x: Double): Double
Expand All @@ -11,29 +11,29 @@
| (x: Byte): Int
| (x: String): String
| match arguments ((x : Int)) (a named tuple)
-- [E204] Syntax Warning: tests/neg/infix-named-args.scala:2:15 --------------------------------------------------------
2 | def f = 42 + (x = 1) // error // werror
-- [E204] Syntax Warning: tests/neg/infix-named-args.scala:4:15 --------------------------------------------------------
4 | def f = 42 + (x = 1) // error // werror
| ^^^^^^^
|Ambigious syntax: this infix call argument list is interpreted as single named tuple argument, not as an named arguments list.
|This can be rewritten automatically under -rewrite -source 3.6-migration.
|
| longer explanation available when compiling with `-explain`
-- [E204] Syntax Warning: tests/neg/infix-named-args.scala:5:26 --------------------------------------------------------
5 | def g = new C() `multi` (x = 42, y = 27) // werror
-- [E204] Syntax Warning: tests/neg/infix-named-args.scala:7:26 --------------------------------------------------------
7 | def g = new C() `multi` (x = 42, y = 27) // werror
| ^^^^^^^^^^^^^^^^
|Ambigious syntax: this infix call argument list is interpreted as single named tuple argument, not as an named arguments list.
|This can be rewritten automatically under -rewrite -source 3.6-migration.
|
| longer explanation available when compiling with `-explain`
-- [E204] Syntax Warning: tests/neg/infix-named-args.scala:6:21 --------------------------------------------------------
6 | def h = new C() ** (x = 42, y = 27) // werror
-- [E204] Syntax Warning: tests/neg/infix-named-args.scala:8:21 --------------------------------------------------------
8 | def h = new C() ** (x = 42, y = 27) // werror
| ^^^^^^^^^^^^^^^^
|Ambigious syntax: this infix call argument list is interpreted as single named tuple argument, not as an named arguments list.
|This can be rewritten automatically under -rewrite -source 3.6-migration.
|
| longer explanation available when compiling with `-explain`
-- [E204] Syntax Warning: tests/neg/infix-named-args.scala:13:18 -------------------------------------------------------
13 | def f = this ** (x = 2) // werror
-- [E204] Syntax Warning: tests/neg/infix-named-args.scala:15:18 -------------------------------------------------------
15 | def f = this ** (x = 2) // werror
| ^^^^^^^
|Ambigious syntax: this infix call argument list is interpreted as single named tuple argument, not as an named arguments list.
|This can be rewritten automatically under -rewrite -source 3.6-migration.
Expand Down
2 changes: 2 additions & 0 deletions tests/neg/infix-named-args.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import scala.language.experimental.namedTuples

class C:
def f = 42 + (x = 1) // error // werror
def multi(x: Int, y: Int): Int = x + y
Expand Down
2 changes: 2 additions & 0 deletions tests/neg/named-tuple-selectable.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import scala.language.experimental.namedTuples

class FromFields extends Selectable:
type Fields = (i: Int)
def selectDynamic(key: String) =
Expand Down
8 changes: 4 additions & 4 deletions tests/neg/named-tuples-2.check
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
-- Error: tests/neg/named-tuples-2.scala:4:9 ---------------------------------------------------------------------------
4 | case (name, age) => () // error
-- Error: tests/neg/named-tuples-2.scala:5:9 ---------------------------------------------------------------------------
5 | case (name, age) => () // error
| ^
| this case is unreachable since type (String, Int, Boolean) is not a subclass of class Tuple2
-- Error: tests/neg/named-tuples-2.scala:5:9 ---------------------------------------------------------------------------
5 | case (n, a, m, x) => () // error
-- Error: tests/neg/named-tuples-2.scala:6:9 ---------------------------------------------------------------------------
6 | case (n, a, m, x) => () // error
| ^
| this case is unreachable since type (String, Int, Boolean) is not a subclass of class Tuple4
1 change: 1 addition & 0 deletions tests/neg/named-tuples-2.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import language.experimental.namedTuples
def Test =
val person = (name = "Bob", age = 33, married = true)
person match
Expand Down
4 changes: 2 additions & 2 deletions tests/neg/named-tuples-3.check
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
-- [E007] Type Mismatch Error: tests/neg/named-tuples-3.scala:5:16 -----------------------------------------------------
5 |val p: Person = f // error
-- [E007] Type Mismatch Error: tests/neg/named-tuples-3.scala:7:16 -----------------------------------------------------
7 |val p: Person = f // error
| ^
| Found: NamedTuple.NamedTuple[(Int, Any), (Int, String)]
| Required: Person
Expand Down
2 changes: 2 additions & 0 deletions tests/neg/named-tuples-3.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import language.experimental.namedTuples

def f: NamedTuple.NamedTuple[(Int, Any), (Int, String)] = ???

type Person = (name: Int, age: String)
Expand Down
Loading

0 comments on commit a4ea8bf

Please sign in to comment.