From a4ea8bfb92142b86cd6a06071464ea8c66f7f2a7 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Wed, 27 Nov 2024 18:49:45 +0100 Subject: [PATCH 1/3] Make NamedTupls an experimental feature again --- .../src/dotty/tools/dotc/config/Feature.scala | 2 + .../dotty/tools/dotc/parsing/Parsers.scala | 4 +- .../src/dotty/tools/dotc/typer/Typer.scala | 2 +- .../named-tuples.md | 4 +- docs/sidebar.yml | 2 +- library/src/scala/NamedTuple.scala | 3 + .../runtime/stdLibPatches/language.scala | 1 - .../pc/tests/completion/CompletionSuite.scala | 6 +- tests/neg/i20517.check | 14 +-- tests/neg/i20517.scala | 1 + tests/neg/infix-named-args.check | 20 ++-- tests/neg/infix-named-args.scala | 2 + tests/neg/named-tuple-selectable.scala | 2 + tests/neg/named-tuples-2.check | 8 +- tests/neg/named-tuples-2.scala | 1 + tests/neg/named-tuples-3.check | 4 +- tests/neg/named-tuples-3.scala | 2 + tests/neg/named-tuples.check | 92 +++++++++---------- tests/neg/named-tuples.scala | 3 +- tests/new/test.scala | 2 + tests/pos/fieldsOf.scala | 2 + tests/pos/i20377.scala | 1 + tests/pos/i21300.scala | 6 +- tests/pos/i21413.scala | 2 + tests/pos/named-tuple-combinators.scala | 1 + tests/pos/named-tuple-selectable.scala | 1 + tests/pos/named-tuple-selections.scala | 1 + tests/pos/named-tuple-unstable.scala | 1 + tests/pos/named-tuple-widen.scala | 1 + tests/pos/named-tuples-ops-mirror.scala | 1 + tests/pos/named-tuples1.scala | 1 + tests/pos/namedtuple-src-incompat.scala | 1 + tests/pos/tuple-ops.scala | 1 + tests/rewrites/infix-named-args.check | 4 +- tests/rewrites/infix-named-args.scala | 2 + .../stdlibExperimentalDefinitions.scala | 6 ++ tests/run/named-patmatch.scala | 1 + tests/run/named-patterns.scala | 1 + tests/run/named-tuple-ops.scala | 1 + tests/run/named-tuples-xxl.scala | 1 + tests/run/named-tuples.scala | 1 + tests/run/tyql.scala | 1 + tests/warn/21681.check | 4 +- tests/warn/21681.scala | 2 + tests/warn/21681b.check | 4 +- tests/warn/21681b.scala | 2 + tests/warn/21681c.check | 4 +- tests/warn/21681c.scala | 2 + tests/warn/21770.check | 4 +- tests/warn/21770.scala | 4 +- tests/warn/infix-named-args-migration.scala | 2 + 51 files changed, 150 insertions(+), 91 deletions(-) rename docs/_docs/reference/{other-new-features => experimental}/named-tuples.md (98%) diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index ad20bab46c1e..8b9a64924ace 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -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") @@ -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") diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index ca8ebaf79b09..3ea4131d5d78 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -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() @@ -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()) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 13f7b4eb1726..57a027653241 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -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), diff --git a/docs/_docs/reference/other-new-features/named-tuples.md b/docs/_docs/reference/experimental/named-tuples.md similarity index 98% rename from docs/_docs/reference/other-new-features/named-tuples.md rename to docs/_docs/reference/experimental/named-tuples.md index 5483c5cc255b..27d74259725d 100644 --- a/docs/_docs/reference/other-new-features/named-tuples.md +++ b/docs/_docs/reference/experimental/named-tuples.md @@ -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) diff --git a/docs/sidebar.yml b/docs/sidebar.yml index 74aee3dfc668..a306d8bdf274 100644 --- a/docs/sidebar.yml +++ b/docs/sidebar.yml @@ -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 @@ -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 diff --git a/library/src/scala/NamedTuple.scala b/library/src/scala/NamedTuple.scala index d105cf042f37..6da7f940dc47 100644 --- a/library/src/scala/NamedTuple.scala +++ b/library/src/scala/NamedTuple.scala @@ -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, @@ -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]) diff --git a/library/src/scala/runtime/stdLibPatches/language.scala b/library/src/scala/runtime/stdLibPatches/language.scala index b8d990cf56f5..547710d55293 100644 --- a/library/src/scala/runtime/stdLibPatches/language.scala +++ b/library/src/scala/runtime/stdLibPatches/language.scala @@ -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 diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala index 7be2ea6181ef..ab28baea994b 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala @@ -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") | @@ -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 diff --git a/tests/neg/i20517.check b/tests/neg/i20517.check index 119c34025ee0..55aeff46572b 100644 --- a/tests/neg/i20517.check +++ b/tests/neg/i20517.check @@ -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` diff --git a/tests/neg/i20517.scala b/tests/neg/i20517.scala index 342a7d86ca7e..11c4432434dd 100644 --- a/tests/neg/i20517.scala +++ b/tests/neg/i20517.scala @@ -1,3 +1,4 @@ +import scala.language.experimental.namedTuples import NamedTuple.From case class Foo[+T](elem: T) diff --git a/tests/neg/infix-named-args.check b/tests/neg/infix-named-args.check index 0cfbbaef73a3..291c7616a57c 100644 --- a/tests/neg/infix-named-args.check +++ b/tests/neg/infix-named-args.check @@ -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 @@ -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. diff --git a/tests/neg/infix-named-args.scala b/tests/neg/infix-named-args.scala index d8616899540c..b0ef555cf965 100644 --- a/tests/neg/infix-named-args.scala +++ b/tests/neg/infix-named-args.scala @@ -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 diff --git a/tests/neg/named-tuple-selectable.scala b/tests/neg/named-tuple-selectable.scala index c81eba1237ff..5cf7e68654ef 100644 --- a/tests/neg/named-tuple-selectable.scala +++ b/tests/neg/named-tuple-selectable.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.namedTuples + class FromFields extends Selectable: type Fields = (i: Int) def selectDynamic(key: String) = diff --git a/tests/neg/named-tuples-2.check b/tests/neg/named-tuples-2.check index daa1c0d69069..0a52d5f3989b 100644 --- a/tests/neg/named-tuples-2.check +++ b/tests/neg/named-tuples-2.check @@ -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 diff --git a/tests/neg/named-tuples-2.scala b/tests/neg/named-tuples-2.scala index b3917d9ad57c..0507891e0549 100644 --- a/tests/neg/named-tuples-2.scala +++ b/tests/neg/named-tuples-2.scala @@ -1,3 +1,4 @@ +import language.experimental.namedTuples def Test = val person = (name = "Bob", age = 33, married = true) person match diff --git a/tests/neg/named-tuples-3.check b/tests/neg/named-tuples-3.check index 2809836b4803..2091c36191c0 100644 --- a/tests/neg/named-tuples-3.check +++ b/tests/neg/named-tuples-3.check @@ -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 diff --git a/tests/neg/named-tuples-3.scala b/tests/neg/named-tuples-3.scala index 21e6ed9b3741..0f1215338b0a 100644 --- a/tests/neg/named-tuples-3.scala +++ b/tests/neg/named-tuples-3.scala @@ -1,3 +1,5 @@ +import language.experimental.namedTuples + def f: NamedTuple.NamedTuple[(Int, Any), (Int, String)] = ??? type Person = (name: Int, age: String) diff --git a/tests/neg/named-tuples.check b/tests/neg/named-tuples.check index 8ec958b6a75d..db3cc703722f 100644 --- a/tests/neg/named-tuples.check +++ b/tests/neg/named-tuples.check @@ -1,101 +1,101 @@ --- Error: tests/neg/named-tuples.scala:8:19 ---------------------------------------------------------------------------- -8 | val illformed = (_2 = 2) // error +-- Error: tests/neg/named-tuples.scala:9:19 ---------------------------------------------------------------------------- +9 | 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: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 +-- 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 | ^^^^^^^^^^^ | Duplicate tuple element name --- Error: tests/neg/named-tuples.scala:11:45 --------------------------------------------------------------------------- -11 | type Illformed2 = (name: String, age: Int, name: Boolean) // error +-- Error: tests/neg/named-tuples.scala:12:45 --------------------------------------------------------------------------- +12 | type Illformed2 = (name: String, age: Int, name: Boolean) // error | ^^^^^^^^^^^^^ | Duplicate tuple element name --- [E007] Type Mismatch Error: tests/neg/named-tuples.scala:19:20 ------------------------------------------------------ -19 | val _: NameOnly = person // error +-- [E007] Type Mismatch Error: tests/neg/named-tuples.scala:20:20 ------------------------------------------------------ +20 | 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:20:18 ------------------------------------------------------ -20 | val _: Person = nameOnly // error +-- [E007] Type Mismatch Error: tests/neg/named-tuples.scala:21:18 ------------------------------------------------------ +21 | 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:21:41 --------------------------------------------------------------- -21 | val _: Person = (name = "") ++ nameOnly // error +-- [E172] Type Error: tests/neg/named-tuples.scala:22:41 --------------------------------------------------------------- +22 | 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:22:9 ----------------------------------------------------------- -22 | person._1 // error +-- [E008] Not Found Error: tests/neg/named-tuples.scala:23:9 ----------------------------------------------------------- +23 | person._1 // error | ^^^^^^^^^ | value _1 is not a member of (name : String, age : Int) --- [E007] Type Mismatch Error: tests/neg/named-tuples.scala:24:36 ------------------------------------------------------ -24 | val _: (age: Int, name: String) = person // error +-- [E007] Type Mismatch Error: tests/neg/named-tuples.scala:25:36 ------------------------------------------------------ +25 | 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:26:17 --------------------------------------------------------------------------- -26 | val (name = x, agee = y) = person // error +-- Error: tests/neg/named-tuples.scala:27:17 --------------------------------------------------------------------------- +27 | 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:29:10 --------------------------------------------------------------------------- -29 | case (name = n, age = a) => () // error // error +-- Error: tests/neg/named-tuples.scala:30:10 --------------------------------------------------------------------------- +30 | case (name = n, age = a) => () // error // error | ^^^^^^^^ | No element named `name` is defined in selector type (String, Int) --- Error: tests/neg/named-tuples.scala:29:20 --------------------------------------------------------------------------- -29 | case (name = n, age = a) => () // error // error +-- Error: tests/neg/named-tuples.scala:30:20 --------------------------------------------------------------------------- +30 | 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:31:27 --------------------------------------------------------------- -31 | val pp = person ++ (1, 2) // error +-- [E172] Type Error: tests/neg/named-tuples.scala:32:27 --------------------------------------------------------------- +32 | 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:34:18 --------------------------------------------------------------- -34 | person ++ (1, 2) match // error +-- [E172] Type Error: tests/neg/named-tuples.scala:35:18 --------------------------------------------------------------- +35 | person ++ (1, 2) match // error | ^ | Cannot prove that Tuple.Disjoint[(("name" : String), ("age" : String)), Tuple] =:= (true : Boolean). --- Error: tests/neg/named-tuples.scala:37:17 --------------------------------------------------------------------------- -37 | val bad = ("", age = 10) // error +-- Error: tests/neg/named-tuples.scala:38:17 --------------------------------------------------------------------------- +38 | val bad = ("", age = 10) // error | ^^^^^^^^ | Illegal combination of named and unnamed tuple elements --- Error: tests/neg/named-tuples.scala:40:20 --------------------------------------------------------------------------- -40 | case (name = n, age) => () // error +-- Error: tests/neg/named-tuples.scala:41:20 --------------------------------------------------------------------------- +41 | case (name = n, age) => () // error | ^^^ | Illegal combination of named and unnamed tuple elements --- Error: tests/neg/named-tuples.scala:41:16 --------------------------------------------------------------------------- -41 | case (name, age = a) => () // error +-- Error: tests/neg/named-tuples.scala:42:16 --------------------------------------------------------------------------- +42 | case (name, age = a) => () // error | ^^^^^^^ | Illegal combination of named and unnamed tuple elements --- Error: tests/neg/named-tuples.scala:44:10 --------------------------------------------------------------------------- -44 | case (age = x) => // error +-- Error: tests/neg/named-tuples.scala:45:10 --------------------------------------------------------------------------- +45 | case (age = x) => // error | ^^^^^^^ | No element named `age` is defined in selector type Tuple --- [E172] Type Error: tests/neg/named-tuples.scala:46:27 --------------------------------------------------------------- -46 | val p2 = person ++ person // error +-- [E172] Type Error: tests/neg/named-tuples.scala:47:27 --------------------------------------------------------------- +47 | 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:47:43 --------------------------------------------------------------- -47 | val p3 = person ++ (first = 11, age = 33) // error +-- [E172] Type Error: tests/neg/named-tuples.scala:48:43 --------------------------------------------------------------- +48 | 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:49:22 ------------------------------------------------------ -49 | val p5 = person.zip((first = 11, age = 33)) // error +-- [E007] Type Mismatch Error: tests/neg/named-tuples.scala:50:22 ------------------------------------------------------ +50 | 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:60:32 ------------------------------------------------------ -60 | val typo: (name: ?, age: ?) = (name = "he", ag = 1) // error +-- [E007] Type Mismatch Error: tests/neg/named-tuples.scala:61:32 ------------------------------------------------------ +61 | val typo: (name: ?, age: ?) = (name = "he", ag = 1) // error | ^^^^^^^^^^^^^^^^^^^^^ | Found: (name : String, ag : Int) | Required: (name : ?, age : ?) diff --git a/tests/neg/named-tuples.scala b/tests/neg/named-tuples.scala index daae6e26bac2..8f78f7915206 100644 --- a/tests/neg/named-tuples.scala +++ b/tests/neg/named-tuples.scala @@ -1,6 +1,7 @@ import annotation.experimental +import language.experimental.namedTuples -object Test: +@experimental object Test: type Person = (name: String, age: Int) val person = (name = "Bob", age = 33): (name: String, age: Int) diff --git a/tests/new/test.scala b/tests/new/test.scala index dc1891f3525c..18644422ab06 100644 --- a/tests/new/test.scala +++ b/tests/new/test.scala @@ -1,3 +1,5 @@ +import language.experimental.namedTuples + type Person = (name: String, age: Int) trait A: diff --git a/tests/pos/fieldsOf.scala b/tests/pos/fieldsOf.scala index 08f20a1f7e8e..2594dae2cbf7 100644 --- a/tests/pos/fieldsOf.scala +++ b/tests/pos/fieldsOf.scala @@ -1,3 +1,5 @@ +import language.experimental.namedTuples + case class Person(name: String, age: Int) type PF = NamedTuple.From[Person] diff --git a/tests/pos/i20377.scala b/tests/pos/i20377.scala index a555e01867ab..661fa7adfca9 100644 --- a/tests/pos/i20377.scala +++ b/tests/pos/i20377.scala @@ -1,3 +1,4 @@ +import language.experimental.namedTuples import NamedTuple.{NamedTuple, AnyNamedTuple} // Repros for bugs or questions diff --git a/tests/pos/i21300.scala b/tests/pos/i21300.scala index e7c7965b0e9a..22859482ef98 100644 --- a/tests/pos/i21300.scala +++ b/tests/pos/i21300.scala @@ -1,15 +1,17 @@ +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 diff --git a/tests/pos/i21413.scala b/tests/pos/i21413.scala index d2dc52e34630..72b5c6d59d8d 100644 --- a/tests/pos/i21413.scala +++ b/tests/pos/i21413.scala @@ -1,2 +1,4 @@ +import scala.language.experimental.namedTuples + val x = (aaa = 1).aaa //val y = x.aaa \ No newline at end of file diff --git a/tests/pos/named-tuple-combinators.scala b/tests/pos/named-tuple-combinators.scala index c027ba688d02..a5134b2e7d26 100644 --- a/tests/pos/named-tuple-combinators.scala +++ b/tests/pos/named-tuple-combinators.scala @@ -1,3 +1,4 @@ +import scala.language.experimental.namedTuples object Test: // original code from issue https://github.com/scala/scala3/issues/20427 diff --git a/tests/pos/named-tuple-selectable.scala b/tests/pos/named-tuple-selectable.scala index 0e1324f70ae6..be5f0400e58c 100644 --- a/tests/pos/named-tuple-selectable.scala +++ b/tests/pos/named-tuple-selectable.scala @@ -1,3 +1,4 @@ +import scala.language.experimental.namedTuples class FromFields extends Selectable: type Fields = (xs: List[Int], poly: [T] => (x: List[T]) => Option[T]) diff --git a/tests/pos/named-tuple-selections.scala b/tests/pos/named-tuple-selections.scala index 7b73daad2e72..c3569f21b323 100644 --- a/tests/pos/named-tuple-selections.scala +++ b/tests/pos/named-tuple-selections.scala @@ -1,3 +1,4 @@ +import scala.language.experimental.namedTuples object Test1: // original code from issue https://github.com/scala/scala3/issues/20439 diff --git a/tests/pos/named-tuple-unstable.scala b/tests/pos/named-tuple-unstable.scala index d15bdc578a3a..6a6a36732a14 100644 --- a/tests/pos/named-tuple-unstable.scala +++ b/tests/pos/named-tuple-unstable.scala @@ -1,3 +1,4 @@ +import scala.language.experimental.namedTuples import NamedTuple.{AnyNamedTuple, NamedTuple} trait Foo extends Selectable: diff --git a/tests/pos/named-tuple-widen.scala b/tests/pos/named-tuple-widen.scala index cc12a5f09b16..410832e04c17 100644 --- a/tests/pos/named-tuple-widen.scala +++ b/tests/pos/named-tuple-widen.scala @@ -1,3 +1,4 @@ +import language.experimental.namedTuples class A class B diff --git a/tests/pos/named-tuples-ops-mirror.scala b/tests/pos/named-tuples-ops-mirror.scala index b8745cf785d5..f66eb89534fb 100644 --- a/tests/pos/named-tuples-ops-mirror.scala +++ b/tests/pos/named-tuples-ops-mirror.scala @@ -1,3 +1,4 @@ +import language.experimental.namedTuples import NamedTuple.* @FailsWith[HttpError] diff --git a/tests/pos/named-tuples1.scala b/tests/pos/named-tuples1.scala index 532f1df7efd4..58e3fc065e61 100644 --- a/tests/pos/named-tuples1.scala +++ b/tests/pos/named-tuples1.scala @@ -1,4 +1,5 @@ import annotation.experimental +import language.experimental.namedTuples @main def Test = val bob = (name = "Bob", age = 33): (name: String, age: Int) diff --git a/tests/pos/namedtuple-src-incompat.scala b/tests/pos/namedtuple-src-incompat.scala index 76eb5e4aa850..57451a4321b7 100644 --- a/tests/pos/namedtuple-src-incompat.scala +++ b/tests/pos/namedtuple-src-incompat.scala @@ -1,3 +1,4 @@ +import language.experimental.namedTuples var age = 22 val x = (age = 1) val _: (age: Int) = x diff --git a/tests/pos/tuple-ops.scala b/tests/pos/tuple-ops.scala index e89c0e8e51aa..739b1ebeeb02 100644 --- a/tests/pos/tuple-ops.scala +++ b/tests/pos/tuple-ops.scala @@ -1,3 +1,4 @@ +import language.experimental.namedTuples import Tuple.* def test = diff --git a/tests/rewrites/infix-named-args.check b/tests/rewrites/infix-named-args.check index a50593ef18a8..5f59cf272ba1 100644 --- a/tests/rewrites/infix-named-args.check +++ b/tests/rewrites/infix-named-args.check @@ -1,3 +1,5 @@ +import scala.language.experimental.namedTuples + class C: def multi(x: Int, y: Int): Int = x + y def **(x: Int, y: Int): Int = x + y @@ -12,4 +14,4 @@ class D(d: Int): def f = this.**(x = 2) def g = this ** 2 def h = this ** ((x = 2)) - def i = this.**(x = (1 + 1)) \ No newline at end of file + def i = this.**(x = (1 + 1)) diff --git a/tests/rewrites/infix-named-args.scala b/tests/rewrites/infix-named-args.scala index bcdf4a21a9d2..a954776a9104 100644 --- a/tests/rewrites/infix-named-args.scala +++ b/tests/rewrites/infix-named-args.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.namedTuples + class C: def multi(x: Int, y: Int): Int = x + y def **(x: Int, y: Int): Int = x + y diff --git a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala index 7a8dcb9bd2df..65e3a730ee7e 100644 --- a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala @@ -77,6 +77,12 @@ val experimentalDefinitionInLibrary = Set( // New feature: fromNullable for explicit nulls "scala.Predef$.fromNullable", + // New feature: named tuples + "scala.NamedTuple", + "scala.NamedTuple$", + "scala.NamedTupleDecomposition", + "scala.NamedTupleDecomposition$", + // New feature: modularity "scala.Precise", "scala.annotation.internal.WitnessNames", diff --git a/tests/run/named-patmatch.scala b/tests/run/named-patmatch.scala index 6fe1934f008e..e62497e4aa8f 100644 --- a/tests/run/named-patmatch.scala +++ b/tests/run/named-patmatch.scala @@ -1,4 +1,5 @@ import annotation.experimental +import language.experimental.namedTuples @main def Test = locally: diff --git a/tests/run/named-patterns.scala b/tests/run/named-patterns.scala index e92bbf751c22..7c24dc8d683a 100644 --- a/tests/run/named-patterns.scala +++ b/tests/run/named-patterns.scala @@ -1,3 +1,4 @@ +import language.experimental.namedTuples object Test1: class Person(val name: String, val age: Int) diff --git a/tests/run/named-tuple-ops.scala b/tests/run/named-tuple-ops.scala index 8c6db6f2fa1c..076ab5028c6c 100644 --- a/tests/run/named-tuple-ops.scala +++ b/tests/run/named-tuple-ops.scala @@ -1,4 +1,5 @@ //> using options -source future +import language.experimental.namedTuples import scala.compiletime.asMatchable type City = (name: String, zip: Int, pop: Int) diff --git a/tests/run/named-tuples-xxl.scala b/tests/run/named-tuples-xxl.scala index 8c831fb1d223..3a0a1e5e1294 100644 --- a/tests/run/named-tuples-xxl.scala +++ b/tests/run/named-tuples-xxl.scala @@ -1,3 +1,4 @@ +import language.experimental.namedTuples import NamedTuple.toTuple type Person = ( diff --git a/tests/run/named-tuples.scala b/tests/run/named-tuples.scala index c99393a403b3..406c6195cf0f 100644 --- a/tests/run/named-tuples.scala +++ b/tests/run/named-tuples.scala @@ -1,3 +1,4 @@ +import language.experimental.namedTuples import NamedTuple.* type Person = (name: String, age: Int) diff --git a/tests/run/tyql.scala b/tests/run/tyql.scala index ee3fd1138265..8fe253b559ac 100644 --- a/tests/run/tyql.scala +++ b/tests/run/tyql.scala @@ -1,3 +1,4 @@ +import language.experimental.namedTuples import NamedTuple.{NamedTuple, AnyNamedTuple} /* This is a demonstrator that shows how to map regular for expressions to diff --git a/tests/warn/21681.check b/tests/warn/21681.check index e86ce4e36134..6ca2c3816b01 100644 --- a/tests/warn/21681.check +++ b/tests/warn/21681.check @@ -1,5 +1,5 @@ --- [E203] Syntax Migration Warning: tests/warn/21681.scala:3:2 --------------------------------------------------------- -3 | (age = 29) // warn +-- [E203] Syntax Migration Warning: tests/warn/21681.scala:5:2 --------------------------------------------------------- +5 | (age = 29) // warn | ^^^^^^^^^^ | Ambiguous syntax: this is interpreted as a named tuple with one element, | not as an assignment. diff --git a/tests/warn/21681.scala b/tests/warn/21681.scala index 76a19c96e1cb..67f45571ecf6 100644 --- a/tests/warn/21681.scala +++ b/tests/warn/21681.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.namedTuples + def main() = var age: Int = 28 (age = 29) // warn diff --git a/tests/warn/21681b.check b/tests/warn/21681b.check index 32760e00ebb6..c77c1438d9a6 100644 --- a/tests/warn/21681b.check +++ b/tests/warn/21681b.check @@ -1,5 +1,5 @@ --- [E203] Syntax Migration Warning: tests/warn/21681b.scala:3:2 -------------------------------------------------------- -3 | (age = 29) // warn +-- [E203] Syntax Migration Warning: tests/warn/21681b.scala:5:2 -------------------------------------------------------- +5 | (age = 29) // warn | ^^^^^^^^^^ | Ambiguous syntax: this is interpreted as a named tuple with one element, | not as an assignment. diff --git a/tests/warn/21681b.scala b/tests/warn/21681b.scala index 710d69b0dd23..44d04fc98aad 100644 --- a/tests/warn/21681b.scala +++ b/tests/warn/21681b.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.namedTuples + object Test: var age: Int = 28 (age = 29) // warn diff --git a/tests/warn/21681c.check b/tests/warn/21681c.check index 11c427f87cfe..00c74ddba5bc 100644 --- a/tests/warn/21681c.check +++ b/tests/warn/21681c.check @@ -1,5 +1,5 @@ --- [E203] Syntax Migration Warning: tests/warn/21681c.scala:5:2 -------------------------------------------------------- -5 | (age = 29) // warn +-- [E203] Syntax Migration Warning: tests/warn/21681c.scala:7:2 -------------------------------------------------------- +7 | (age = 29) // warn | ^^^^^^^^^^ | Ambiguous syntax: this is interpreted as a named tuple with one element, | not as an assignment. diff --git a/tests/warn/21681c.scala b/tests/warn/21681c.scala index 5e2eae11708c..a0c361382a54 100644 --- a/tests/warn/21681c.scala +++ b/tests/warn/21681c.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.namedTuples + object Test: def age: Int = ??? def age_=(x: Int): Unit = () diff --git a/tests/warn/21770.check b/tests/warn/21770.check index 0899f11d6ca5..10a1c287599c 100644 --- a/tests/warn/21770.check +++ b/tests/warn/21770.check @@ -1,5 +1,5 @@ --- [E203] Syntax Migration Warning: tests/warn/21770.scala:5:9 --------------------------------------------------------- -5 | f(i => (cache = Some(i))) // warn +-- [E203] Syntax Migration Warning: tests/warn/21770.scala:7:9 --------------------------------------------------------- +7 | f(i => (cache = Some(i))) // warn | ^^^^^^^^^^^^^^^^^ | Ambiguous syntax: this is interpreted as a named tuple with one element, | not as an assignment. diff --git a/tests/warn/21770.scala b/tests/warn/21770.scala index 9696a31d6ba8..8ee5b52e7b3f 100644 --- a/tests/warn/21770.scala +++ b/tests/warn/21770.scala @@ -1,5 +1,7 @@ +import scala.language.experimental.namedTuples + def f(g: Int => Unit) = g(0) -def test = +def test = var cache: Option[Int] = None f(i => (cache = Some(i))) // warn diff --git a/tests/warn/infix-named-args-migration.scala b/tests/warn/infix-named-args-migration.scala index df4bfb50271c..361004f08f13 100644 --- a/tests/warn/infix-named-args-migration.scala +++ b/tests/warn/infix-named-args-migration.scala @@ -1,4 +1,6 @@ //> using options -source:3.6-migration +import scala.language.experimental.namedTuples + class C: def f = 42 + (x = 1) // warn // interpreted as 42.+(x = 1) under migration, x is a valid synthetic parameter name def multi(x: Int, y: Int): Int = x + y From 861beeef093501717bf322a5b442199188664daa Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Wed, 27 Nov 2024 22:36:20 +0100 Subject: [PATCH 2/3] Add migration rewrite for deprecated assignment syntax --- .../tools/dotc/config/MigrationVersion.scala | 2 +- .../dotty/tools/dotc/parsing/Parsers.scala | 4 ++-- .../tools/dotc/reporting/ErrorMessageID.scala | 4 ++-- .../dotty/tools/dotc/reporting/messages.scala | 18 ++++++++--------- .../src/dotty/tools/dotc/typer/Typer.scala | 6 +++--- tests/neg/infix-named-args.check | 20 ++++++++----------- tests/warn/21681.check | 2 +- tests/warn/21681b.check | 2 +- tests/warn/21681c.check | 2 +- tests/warn/21770.check | 2 +- 10 files changed, 28 insertions(+), 34 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/MigrationVersion.scala b/compiler/src/dotty/tools/dotc/config/MigrationVersion.scala index 247e3f62a98d..1d99caa789d3 100644 --- a/compiler/src/dotty/tools/dotc/config/MigrationVersion.scala +++ b/compiler/src/dotty/tools/dotc/config/MigrationVersion.scala @@ -26,7 +26,7 @@ enum MigrationVersion(val warnFrom: SourceVersion, val errorFrom: SourceVersion) case WithOperator extends MigrationVersion(`3.4`, future) case FunctionUnderscore extends MigrationVersion(`3.4`, future) case NonNamedArgumentInJavaAnnotation extends MigrationVersion(`3.6`, `3.6`) - case AmbiguousNamedTupleInfixApply extends MigrationVersion(`3.6`, never) + case AmbiguousNamedTupleSyntax extends MigrationVersion(`3.6`, future) case ImportWildcard extends MigrationVersion(future, future) case ImportRename extends MigrationVersion(future, future) case ParameterEnclosedByParenthesis extends MigrationVersion(future, future) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 3ea4131d5d78..220053e277a5 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1149,8 +1149,8 @@ object Parsers { if isType then infixOp else infixOp.right match case Tuple(args) if args.exists(_.isInstanceOf[NamedArg]) && !isNamedTupleOperator => - report.errorOrMigrationWarning(AmbiguousNamedTupleInfixApply(), infixOp.right.srcPos, MigrationVersion.AmbiguousNamedTupleInfixApply) - if MigrationVersion.AmbiguousNamedTupleInfixApply.needsPatch then + report.errorOrMigrationWarning(DeprecatedInfixNamedArgumentSyntax(), infixOp.right.srcPos, MigrationVersion.AmbiguousNamedTupleSyntax) + if MigrationVersion.AmbiguousNamedTupleSyntax.needsPatch then val asApply = cpy.Apply(infixOp)(Select(opInfo.operand, opInfo.operator.name), args) patch(source, infixOp.span, asApply.show(using ctx.withoutColors)) asApply // allow to use pre-3.6 syntax in migration mode diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index 35c170858bbf..2c3774b59a9a 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -216,8 +216,8 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case FinalLocalDefID // errorNumber: 200 case NonNamedArgumentInJavaAnnotationID // errorNumber: 201 case QuotedTypeMissingID // errorNumber: 202 - case AmbiguousNamedTupleAssignmentID // errorNumber: 203 - case AmbiguousNamedTupleInfixApplyID // errorNumber: 204 + case DeprecatedAssignmentSyntaxID // errorNumber: 203 + case DeprecatedInfixNamedArgumentSyntaxID // errorNumber: 204 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index f94a4b58d6fb..2e74c5816c5e 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -3344,21 +3344,19 @@ final class QuotedTypeMissing(tpe: Type)(using Context) extends StagingMessage(Q end QuotedTypeMissing -final class AmbiguousNamedTupleAssignment(key: Name, value: untpd.Tree)(using Context) extends SyntaxMsg(AmbiguousNamedTupleAssignmentID): +final class DeprecatedAssignmentSyntax(key: Name, value: untpd.Tree)(using Context) extends SyntaxMsg(DeprecatedAssignmentSyntaxID): override protected def msg(using Context): String = - i"""Ambiguous syntax: this is interpreted as a named tuple with one element, + i"""Deprecated syntax: in the future it would be interpreted as a named tuple with one element, |not as an assignment. | |To assign a value, use curly braces: `{${key} = ${value}}`.""" - + override protected def explain(using Context): String = "" -class AmbiguousNamedTupleInfixApply()(using Context) extends SyntaxMsg(AmbiguousNamedTupleInfixApplyID): +class DeprecatedInfixNamedArgumentSyntax()(using Context) extends SyntaxMsg(DeprecatedInfixNamedArgumentSyntaxID): def msg(using Context) = - "Ambigious syntax: this infix call argument list is interpreted as single named tuple argument, not as an named arguments list." - + Message.rewriteNotice("This", version = SourceVersion.`3.6-migration`) + i"""Deprecated syntax: infix named arguments lists are deprecated; in the future it would be interpreted as a single name tuple argument. + |To avoid this warning, either remove the argument names or use dotted selection.""" + + Message.rewriteNotice("This", version = SourceVersion.`3.6-migration`) - def explain(using Context) = - i"""Starting with Scala 3.6 infix named arguments are interpretted as Named Tuple. - | - |To avoid this warning, either remove the argument names or use dotted selection.""" + def explain(using Context) = "" diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 57a027653241..d74a69f2a114 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3404,7 +3404,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer /** Translate tuples of all arities */ def typedTuple(tree: untpd.Tuple, pt: Type)(using Context): Tree = val tree1 = desugar.tuple(tree, pt) - checkAmbiguousNamedTupleAssignment(tree) + checkDeprecatedAssignmentSyntax(tree) if tree1 ne tree then typed(tree1, pt) else val arity = tree.trees.length @@ -3433,7 +3433,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer /** Checks if `tree` is a named tuple with one element that could be * interpreted as an assignment, such as `(x = 1)`. If so, issues a warning. */ - def checkAmbiguousNamedTupleAssignment(tree: untpd.Tuple)(using Context): Unit = + def checkDeprecatedAssignmentSyntax(tree: untpd.Tuple)(using Context): Unit = tree.trees match case List(NamedArg(name, value)) => val tmpCtx = ctx.fresh.setNewTyperState() @@ -3441,7 +3441,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if !tmpCtx.reporter.hasErrors then // If there are no errors typing the above, then the named tuple is // ambiguous and we issue a warning. - report.migrationWarning(AmbiguousNamedTupleAssignment(name, value), tree.srcPos) + report.migrationWarning(DeprecatedAssignmentSyntax(name, value), tree.srcPos) case _ => () /** Retrieve symbol attached to given tree */ diff --git a/tests/neg/infix-named-args.check b/tests/neg/infix-named-args.check index 291c7616a57c..d960892a9624 100644 --- a/tests/neg/infix-named-args.check +++ b/tests/neg/infix-named-args.check @@ -14,28 +14,24 @@ -- [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. + |Deprecated syntax: infix named arguments lists are deprecated; in the future it would be interpreted as a single name tuple argument. + |To avoid this warning, either remove the argument names or use dotted selection. |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: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. + |Deprecated syntax: infix named arguments lists are deprecated; in the future it would be interpreted as a single name tuple argument. + |To avoid this warning, either remove the argument names or use dotted selection. |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: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. + |Deprecated syntax: infix named arguments lists are deprecated; in the future it would be interpreted as a single name tuple argument. + |To avoid this warning, either remove the argument names or use dotted selection. |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: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. + |Deprecated syntax: infix named arguments lists are deprecated; in the future it would be interpreted as a single name tuple argument. + |To avoid this warning, either remove the argument names or use dotted selection. |This can be rewritten automatically under -rewrite -source 3.6-migration. - | - | longer explanation available when compiling with `-explain` diff --git a/tests/warn/21681.check b/tests/warn/21681.check index 6ca2c3816b01..5156a600d609 100644 --- a/tests/warn/21681.check +++ b/tests/warn/21681.check @@ -1,7 +1,7 @@ -- [E203] Syntax Migration Warning: tests/warn/21681.scala:5:2 --------------------------------------------------------- 5 | (age = 29) // warn | ^^^^^^^^^^ - | Ambiguous syntax: this is interpreted as a named tuple with one element, + | Deprecated syntax: in the future it would be interpreted as a named tuple with one element, | not as an assignment. | | To assign a value, use curly braces: `{age = 29}`. diff --git a/tests/warn/21681b.check b/tests/warn/21681b.check index c77c1438d9a6..dd28df3168ed 100644 --- a/tests/warn/21681b.check +++ b/tests/warn/21681b.check @@ -1,7 +1,7 @@ -- [E203] Syntax Migration Warning: tests/warn/21681b.scala:5:2 -------------------------------------------------------- 5 | (age = 29) // warn | ^^^^^^^^^^ - | Ambiguous syntax: this is interpreted as a named tuple with one element, + | Deprecated syntax: in the future it would be interpreted as a named tuple with one element, | not as an assignment. | | To assign a value, use curly braces: `{age = 29}`. diff --git a/tests/warn/21681c.check b/tests/warn/21681c.check index 00c74ddba5bc..bfb62618cbb9 100644 --- a/tests/warn/21681c.check +++ b/tests/warn/21681c.check @@ -1,7 +1,7 @@ -- [E203] Syntax Migration Warning: tests/warn/21681c.scala:7:2 -------------------------------------------------------- 7 | (age = 29) // warn | ^^^^^^^^^^ - | Ambiguous syntax: this is interpreted as a named tuple with one element, + | Deprecated syntax: in the future it would be interpreted as a named tuple with one element, | not as an assignment. | | To assign a value, use curly braces: `{age = 29}`. diff --git a/tests/warn/21770.check b/tests/warn/21770.check index 10a1c287599c..6c978a6078a2 100644 --- a/tests/warn/21770.check +++ b/tests/warn/21770.check @@ -1,7 +1,7 @@ -- [E203] Syntax Migration Warning: tests/warn/21770.scala:7:9 --------------------------------------------------------- 7 | f(i => (cache = Some(i))) // warn | ^^^^^^^^^^^^^^^^^ - | Ambiguous syntax: this is interpreted as a named tuple with one element, + | Deprecated syntax: in the future it would be interpreted as a named tuple with one element, | not as an assignment. | | To assign a value, use curly braces: `{cache = Some(i)}`. From 24c3e7f601c6ba74c541f1603251253bfde45ab3 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Wed, 27 Nov 2024 23:54:11 +0100 Subject: [PATCH 3/3] Add migration rewrite deprecated assignment syntax --- .../dotty/tools/dotc/reporting/messages.scala | 1 + .../src/dotty/tools/dotc/typer/Typer.scala | 3 +++ .../dotty/tools/dotc/CompilationTests.scala | 1 + .../ambigious-named-tuple-assignment.check | 19 +++++++++++++++++++ .../ambigious-named-tuple-assignment.scala | 19 +++++++++++++++++++ tests/warn/21681.check | 1 + tests/warn/21681b.check | 1 + tests/warn/21681c.check | 1 + tests/warn/21770.check | 1 + 9 files changed, 47 insertions(+) create mode 100644 tests/rewrites/ambigious-named-tuple-assignment.check create mode 100644 tests/rewrites/ambigious-named-tuple-assignment.scala diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 2e74c5816c5e..b396aa62f599 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -3350,6 +3350,7 @@ final class DeprecatedAssignmentSyntax(key: Name, value: untpd.Tree)(using Conte |not as an assignment. | |To assign a value, use curly braces: `{${key} = ${value}}`.""" + + Message.rewriteNotice("This", version = SourceVersion.`3.6-migration`) override protected def explain(using Context): String = "" diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index d74a69f2a114..f5318759bac2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3442,6 +3442,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // If there are no errors typing the above, then the named tuple is // ambiguous and we issue a warning. report.migrationWarning(DeprecatedAssignmentSyntax(name, value), tree.srcPos) + if MigrationVersion.AmbiguousNamedTupleSyntax.needsPatch then + patch(tree.source, Span(tree.span.start, tree.span.start + 1), "{") + patch(tree.source, Span(tree.span.end - 1, tree.span.end), "}") case _ => () /** Retrieve symbol attached to given tree */ diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 3bd3b5138fad..9f72db6fc390 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -80,6 +80,7 @@ class CompilationTests { compileDir("tests/rewrites/annotation-named-pararamters", defaultOptions.and("-rewrite", "-source:3.6-migration")), compileFile("tests/rewrites/i21418.scala", unindentOptions.and("-rewrite", "-source:3.5-migration")), compileFile("tests/rewrites/infix-named-args.scala", defaultOptions.and("-rewrite", "-source:3.6-migration")), + compileFile("tests/rewrites/ambigious-named-tuple-assignment.scala", defaultOptions.and("-rewrite", "-source:3.6-migration")), ).checkRewrites() } diff --git a/tests/rewrites/ambigious-named-tuple-assignment.check b/tests/rewrites/ambigious-named-tuple-assignment.check new file mode 100644 index 000000000000..00e6cc4112f1 --- /dev/null +++ b/tests/rewrites/ambigious-named-tuple-assignment.check @@ -0,0 +1,19 @@ +import scala.language.experimental.namedTuples + +object i21770: + def f(g: Int => Unit) = g(0) + var cache: Option[Int] = None + f(i => {cache = Some(i)}) + +object i21861: + var age: Int = 28 + { + age = 29 + } + + +object i21861c: + def age: Int = ??? + def age_=(x: Int): Unit = () + age = 29 + { age = 29 } diff --git a/tests/rewrites/ambigious-named-tuple-assignment.scala b/tests/rewrites/ambigious-named-tuple-assignment.scala new file mode 100644 index 000000000000..e9685b7b58cf --- /dev/null +++ b/tests/rewrites/ambigious-named-tuple-assignment.scala @@ -0,0 +1,19 @@ +import scala.language.experimental.namedTuples + +object i21770: + def f(g: Int => Unit) = g(0) + var cache: Option[Int] = None + f(i => (cache = Some(i))) + +object i21861: + var age: Int = 28 + ( + age = 29 + ) + + +object i21861c: + def age: Int = ??? + def age_=(x: Int): Unit = () + age = 29 + ( age = 29 ) diff --git a/tests/warn/21681.check b/tests/warn/21681.check index 5156a600d609..adf3586e6e0b 100644 --- a/tests/warn/21681.check +++ b/tests/warn/21681.check @@ -5,3 +5,4 @@ | not as an assignment. | | To assign a value, use curly braces: `{age = 29}`. + | This can be rewritten automatically under -rewrite -source 3.6-migration. diff --git a/tests/warn/21681b.check b/tests/warn/21681b.check index dd28df3168ed..09c007f351b4 100644 --- a/tests/warn/21681b.check +++ b/tests/warn/21681b.check @@ -5,3 +5,4 @@ | not as an assignment. | | To assign a value, use curly braces: `{age = 29}`. + | This can be rewritten automatically under -rewrite -source 3.6-migration. diff --git a/tests/warn/21681c.check b/tests/warn/21681c.check index bfb62618cbb9..20273f723384 100644 --- a/tests/warn/21681c.check +++ b/tests/warn/21681c.check @@ -5,3 +5,4 @@ | not as an assignment. | | To assign a value, use curly braces: `{age = 29}`. + | This can be rewritten automatically under -rewrite -source 3.6-migration. diff --git a/tests/warn/21770.check b/tests/warn/21770.check index 6c978a6078a2..7853d77a423c 100644 --- a/tests/warn/21770.check +++ b/tests/warn/21770.check @@ -5,3 +5,4 @@ | not as an assignment. | | To assign a value, use curly braces: `{cache = Some(i)}`. + | This can be rewritten automatically under -rewrite -source 3.6-migration.