Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make named tuples a standard feature #21680

Merged
merged 5 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions compiler/src/dotty/tools/dotc/config/Feature.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ 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 @@ -66,7 +65,6 @@ 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
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ object ScalaSettingsProperties:
ScalaRelease.values.toList.map(_.show)

def supportedSourceVersions: List[String] =
SourceVersion.values.toList.map(_.toString)
(SourceVersion.values.toList.diff(SourceVersion.illegalSourceVersionNames)).toList.map(_.toString)

def supportedLanguageFeatures: List[ChoiceWithHelp[String]] =
Feature.values.map((n, d) => ChoiceWithHelp(n.toString, d))
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 @@ -651,7 +651,7 @@ object Parsers {
else leading :: Nil

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

if namedOK && isIdent && in.lookahead.token == EQUALS then
commaSeparated(() => namedArgType())
else if tupleOK && isIdent && in.lookahead.isColon && in.featureEnabled(Feature.namedTuples) then
else if tupleOK && isIdent && in.lookahead.isColon && sourceVersion.isAtLeast(`3.6`) 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 @@ -789,7 +789,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 && Feature.enabled(Feature.namedTuples) then
if nameIdx >= 0 && sourceVersion.isAtLeast(`3.6`) then
typed(
untpd.Apply(
untpd.Select(untpd.TypedSplice(qual), nme.apply),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package scala
import scala.language.experimental.clauseInterleaving
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 @@ -133,7 +130,6 @@ 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: 1 addition & 0 deletions library/src/scala/runtime/stdLibPatches/language.scala
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ 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,8 +1988,7 @@ class CompletionSuite extends BaseCompletionSuite:

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

@Test def `Selectable with namedTuple Fields member` =
check(
"""|import scala.language.experimental.namedTuples
|import scala.NamedTuple.*
"""|import scala.NamedTuple.*
|
|class NamedTupleSelectable extends Selectable {
| type Fields <: AnyNamedTuple
Expand Down Expand Up @@ -2091,7 +2089,7 @@ class CompletionSuite extends BaseCompletionSuite:
|""".stripMargin
)

@Test def `conflict-3` =
@Test def `conflict-3` =
check(
"""|package a
|object A {
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: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`
-- [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`
1 change: 0 additions & 1 deletion tests/neg/i20517.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import scala.language.experimental.namedTuples
import NamedTuple.From

case class Foo[+T](elem: T)
Expand Down
2 changes: 0 additions & 2 deletions tests/neg/named-tuple-selectable.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
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:5:9 ---------------------------------------------------------------------------
5 | case (name, age) => () // error
-- Error: tests/neg/named-tuples-2.scala:4:9 ---------------------------------------------------------------------------
4 | 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:6:9 ---------------------------------------------------------------------------
6 | case (n, a, m, x) => () // error
-- Error: tests/neg/named-tuples-2.scala:5:9 ---------------------------------------------------------------------------
5 | case (n, a, m, x) => () // error
| ^
| this case is unreachable since type (String, Int, Boolean) is not a subclass of class Tuple4
1 change: 0 additions & 1 deletion tests/neg/named-tuples-2.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
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:7:16 -----------------------------------------------------
7 |val p: Person = f // error
-- [E007] Type Mismatch Error: tests/neg/named-tuples-3.scala:5:16 -----------------------------------------------------
5 |val p: Person = f // error
| ^
| Found: NamedTuple.NamedTuple[(Int, Any), (Int, String)]
| Required: Person
Expand Down
2 changes: 0 additions & 2 deletions tests/neg/named-tuples-3.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import language.experimental.namedTuples

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

type Person = (name: Int, age: String)
Expand Down
92 changes: 46 additions & 46 deletions tests/neg/named-tuples.check
Original file line number Diff line number Diff line change
@@ -1,101 +1,101 @@
-- Error: tests/neg/named-tuples.scala:9:19 ----------------------------------------------------------------------------
9 | val illformed = (_2 = 2) // error
-- Error: tests/neg/named-tuples.scala:8:19 ----------------------------------------------------------------------------
8 | val illformed = (_2 = 2) // error
| ^^^^^^
| _2 cannot be used as the name of a tuple element because it is a regular tuple selector
-- Error: tests/neg/named-tuples.scala:10:20 ---------------------------------------------------------------------------
10 | type Illformed = (_1: Int) // error
| ^^^^^^^
| _1 cannot be used as the name of a tuple element because it is a regular tuple selector
-- Error: tests/neg/named-tuples.scala:11:40 ---------------------------------------------------------------------------
11 | val illformed2 = (name = "", age = 0, name = true) // error
-- Error: tests/neg/named-tuples.scala:9:20 ----------------------------------------------------------------------------
9 | type Illformed = (_1: Int) // error
| ^^^^^^^
| _1 cannot be used as the name of a tuple element because it is a regular tuple selector
-- Error: tests/neg/named-tuples.scala:10:40 ---------------------------------------------------------------------------
10 | val illformed2 = (name = "", age = 0, name = true) // error
| ^^^^^^^^^^^
| Duplicate tuple element name
-- Error: tests/neg/named-tuples.scala:12:45 ---------------------------------------------------------------------------
12 | type Illformed2 = (name: String, age: Int, name: Boolean) // error
-- Error: tests/neg/named-tuples.scala:11:45 ---------------------------------------------------------------------------
11 | type Illformed2 = (name: String, age: Int, name: Boolean) // error
| ^^^^^^^^^^^^^
| Duplicate tuple element name
-- [E007] Type Mismatch Error: tests/neg/named-tuples.scala:20:20 ------------------------------------------------------
20 | val _: NameOnly = person // error
-- [E007] Type Mismatch Error: tests/neg/named-tuples.scala:19:20 ------------------------------------------------------
19 | val _: NameOnly = person // error
| ^^^^^^
| Found: (Test.person : (name : String, age : Int))
| Required: Test.NameOnly
|
| longer explanation available when compiling with `-explain`
-- [E007] Type Mismatch Error: tests/neg/named-tuples.scala:21:18 ------------------------------------------------------
21 | val _: Person = nameOnly // error
-- [E007] Type Mismatch Error: tests/neg/named-tuples.scala:20:18 ------------------------------------------------------
20 | val _: Person = nameOnly // error
| ^^^^^^^^
| Found: (Test.nameOnly : (name : String))
| Required: Test.Person
|
| longer explanation available when compiling with `-explain`
-- [E172] Type Error: tests/neg/named-tuples.scala:22:41 ---------------------------------------------------------------
22 | val _: Person = (name = "") ++ nameOnly // error
-- [E172] Type Error: tests/neg/named-tuples.scala:21:41 ---------------------------------------------------------------
21 | val _: Person = (name = "") ++ nameOnly // error
| ^
| Cannot prove that Tuple.Disjoint[Tuple1[("name" : String)], Tuple1[("name" : String)]] =:= (true : Boolean).
-- [E008] Not Found Error: tests/neg/named-tuples.scala:23:9 -----------------------------------------------------------
23 | person._1 // error
-- [E008] Not Found Error: tests/neg/named-tuples.scala:22:9 -----------------------------------------------------------
22 | person._1 // error
| ^^^^^^^^^
| value _1 is not a member of (name : String, age : Int)
-- [E007] Type Mismatch Error: tests/neg/named-tuples.scala:25:36 ------------------------------------------------------
25 | val _: (age: Int, name: String) = person // error
-- [E007] Type Mismatch Error: tests/neg/named-tuples.scala:24:36 ------------------------------------------------------
24 | val _: (age: Int, name: String) = person // error
| ^^^^^^
| Found: (Test.person : (name : String, age : Int))
| Required: (age : Int, name : String)
|
| longer explanation available when compiling with `-explain`
-- Error: tests/neg/named-tuples.scala:27:17 ---------------------------------------------------------------------------
27 | val (name = x, agee = y) = person // error
-- Error: tests/neg/named-tuples.scala:26:17 ---------------------------------------------------------------------------
26 | val (name = x, agee = y) = person // error
| ^^^^^^^^
| No element named `agee` is defined in selector type (name : String, age : Int)
-- Error: tests/neg/named-tuples.scala:30:10 ---------------------------------------------------------------------------
30 | case (name = n, age = a) => () // error // error
-- Error: tests/neg/named-tuples.scala:29:10 ---------------------------------------------------------------------------
29 | case (name = n, age = a) => () // error // error
| ^^^^^^^^
| No element named `name` is defined in selector type (String, Int)
-- Error: tests/neg/named-tuples.scala:30:20 ---------------------------------------------------------------------------
30 | case (name = n, age = a) => () // error // error
-- Error: tests/neg/named-tuples.scala:29:20 ---------------------------------------------------------------------------
29 | case (name = n, age = a) => () // error // error
| ^^^^^^^
| No element named `age` is defined in selector type (String, Int)
-- [E172] Type Error: tests/neg/named-tuples.scala:32:27 ---------------------------------------------------------------
32 | val pp = person ++ (1, 2) // error
-- [E172] Type Error: tests/neg/named-tuples.scala:31:27 ---------------------------------------------------------------
31 | val pp = person ++ (1, 2) // error
| ^
| Cannot prove that Tuple.Disjoint[(("name" : String), ("age" : String)), Tuple] =:= (true : Boolean).
-- [E172] Type Error: tests/neg/named-tuples.scala:35:18 ---------------------------------------------------------------
35 | person ++ (1, 2) match // error
-- [E172] Type Error: tests/neg/named-tuples.scala:34:18 ---------------------------------------------------------------
34 | person ++ (1, 2) match // error
| ^
| Cannot prove that Tuple.Disjoint[(("name" : String), ("age" : String)), Tuple] =:= (true : Boolean).
-- Error: tests/neg/named-tuples.scala:38:17 ---------------------------------------------------------------------------
38 | val bad = ("", age = 10) // error
-- Error: tests/neg/named-tuples.scala:37:17 ---------------------------------------------------------------------------
37 | val bad = ("", age = 10) // error
| ^^^^^^^^
| Illegal combination of named and unnamed tuple elements
-- Error: tests/neg/named-tuples.scala:41:20 ---------------------------------------------------------------------------
41 | case (name = n, age) => () // error
-- Error: tests/neg/named-tuples.scala:40:20 ---------------------------------------------------------------------------
40 | case (name = n, age) => () // error
| ^^^
| Illegal combination of named and unnamed tuple elements
-- Error: tests/neg/named-tuples.scala:42:16 ---------------------------------------------------------------------------
42 | case (name, age = a) => () // error
-- Error: tests/neg/named-tuples.scala:41:16 ---------------------------------------------------------------------------
41 | case (name, age = a) => () // error
| ^^^^^^^
| Illegal combination of named and unnamed tuple elements
-- Error: tests/neg/named-tuples.scala:45:10 ---------------------------------------------------------------------------
45 | case (age = x) => // error
-- Error: tests/neg/named-tuples.scala:44:10 ---------------------------------------------------------------------------
44 | case (age = x) => // error
| ^^^^^^^
| No element named `age` is defined in selector type Tuple
-- [E172] Type Error: tests/neg/named-tuples.scala:47:27 ---------------------------------------------------------------
47 | val p2 = person ++ person // error
-- [E172] Type Error: tests/neg/named-tuples.scala:46:27 ---------------------------------------------------------------
46 | val p2 = person ++ person // error
| ^
|Cannot prove that Tuple.Disjoint[(("name" : String), ("age" : String)), (("name" : String), ("age" : String))] =:= (true : Boolean).
-- [E172] Type Error: tests/neg/named-tuples.scala:48:43 ---------------------------------------------------------------
48 | val p3 = person ++ (first = 11, age = 33) // error
-- [E172] Type Error: tests/neg/named-tuples.scala:47:43 ---------------------------------------------------------------
47 | val p3 = person ++ (first = 11, age = 33) // error
| ^
|Cannot prove that Tuple.Disjoint[(("name" : String), ("age" : String)), (("first" : String), ("age" : String))] =:= (true : Boolean).
-- [E007] Type Mismatch Error: tests/neg/named-tuples.scala:50:22 ------------------------------------------------------
50 | val p5 = person.zip((first = 11, age = 33)) // error
-- [E007] Type Mismatch Error: tests/neg/named-tuples.scala:49:22 ------------------------------------------------------
49 | val p5 = person.zip((first = 11, age = 33)) // error
| ^^^^^^^^^^^^^^^^^^^^^^
| Found: (first : Int, age : Int)
| Required: NamedTuple.NamedTuple[(("name" : String), ("age" : String)), Tuple]
|
| longer explanation available when compiling with `-explain`
-- [E007] Type Mismatch Error: tests/neg/named-tuples.scala:61:32 ------------------------------------------------------
61 | val typo: (name: ?, age: ?) = (name = "he", ag = 1) // error
-- [E007] Type Mismatch Error: tests/neg/named-tuples.scala:60:32 ------------------------------------------------------
60 | val typo: (name: ?, age: ?) = (name = "he", ag = 1) // error
| ^^^^^^^^^^^^^^^^^^^^^
| Found: (name : String, ag : Int)
| Required: (name : ?, age : ?)
Expand Down
3 changes: 1 addition & 2 deletions tests/neg/named-tuples.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import annotation.experimental
import language.experimental.namedTuples

@experimental object Test:
object Test:

type Person = (name: String, age: Int)
val person = (name = "Bob", age = 33): (name: String, age: Int)
Expand Down
2 changes: 0 additions & 2 deletions tests/new/test.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import language.experimental.namedTuples

type Person = (name: String, age: Int)

trait A:
Expand Down
2 changes: 0 additions & 2 deletions tests/pos/fieldsOf.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import language.experimental.namedTuples

case class Person(name: String, age: Int)

type PF = NamedTuple.From[Person]
Expand Down
1 change: 0 additions & 1 deletion tests/pos/i20377.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import language.experimental.namedTuples
import NamedTuple.{NamedTuple, AnyNamedTuple}

// Repros for bugs or questions
Expand Down
6 changes: 2 additions & 4 deletions tests/pos/i21300.scala
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import scala.language.experimental.namedTuples

class Test[S <: String & Singleton](name: S):

type NT = NamedTuple.NamedTuple[(S, "foo"), (Int, Long)]
def nt: NT = ???

type Name = S

type NT2 = NamedTuple.NamedTuple[(Name, "foo"), (Int, Long)]
def nt2: NT2 = ???

def test =
val foo = new Test("bar")

foo.nt.bar
foo.nt2.bar
1 change: 0 additions & 1 deletion tests/pos/named-tuple-combinators.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import scala.language.experimental.namedTuples

object Test:
// original code from issue https://github.com/scala/scala3/issues/20427
Expand Down
1 change: 0 additions & 1 deletion tests/pos/named-tuple-selectable.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import scala.language.experimental.namedTuples

class FromFields extends Selectable:
type Fields = (xs: List[Int], poly: [T] => (x: List[T]) => Option[T])
Expand Down
1 change: 0 additions & 1 deletion tests/pos/named-tuple-selections.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import scala.language.experimental.namedTuples

object Test1:
// original code from issue https://github.com/scala/scala3/issues/20439
Expand Down
1 change: 0 additions & 1 deletion tests/pos/named-tuple-unstable.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import scala.language.experimental.namedTuples
import NamedTuple.{AnyNamedTuple, NamedTuple}

trait Foo extends Selectable:
Expand Down
Loading
Loading