diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 07a75f23f49b..5aaf6b7c932d 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1502,8 +1502,11 @@ object desugar { tree1.withSpan(tree.span) else cpy.Tuple(tree)(elemValues) - val names = elems.collect: + var names = elems.collect: case NamedArg(name, arg) => name + if names.isEmpty then + typer.Inferencing.isFullyDefined(pt, typer.ForceDegree.failBottom) + names = pt.namedTupleNames if names.isEmpty || ctx.mode.is(Mode.Pattern) then tup else diff --git a/compiler/src/dotty/tools/dotc/core/TypeUtils.scala b/compiler/src/dotty/tools/dotc/core/TypeUtils.scala index fa42a9dea042..9c1ea0b4b214 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeUtils.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeUtils.scala @@ -134,6 +134,13 @@ class TypeUtils { def namedTupleElementTypes(using Context): List[(TermName, Type)] = namedTupleElementTypesUpTo(Int.MaxValue) + def namedTupleNames(using Context): List[Name] = + self.normalized.dealias match + case defn.NamedTuple(nmes, _) => + nmes.tupleElementTypes.getOrElse(Nil).map: + case ConstantType(Constants.Constant(str: String)) => str.toTermName + case _ => Nil + def isNamedTupleType(using Context): Boolean = self match case defn.NamedTuple(_, _) => true case _ => false diff --git a/library/src/scala/NamedTuple.scala b/library/src/scala/NamedTuple.scala index b06bc599f9fd..a19112c03f6d 100644 --- a/library/src/scala/NamedTuple.scala +++ b/library/src/scala/NamedTuple.scala @@ -6,7 +6,7 @@ import compiletime.ops.boolean.* object NamedTuple: opaque type AnyNamedTuple = Any - opaque type NamedTuple[N <: Tuple, +V <: Tuple] >: V <: AnyNamedTuple = V + opaque type NamedTuple[N <: Tuple, +V <: Tuple] <: AnyNamedTuple = V def apply[N <: Tuple, V <: Tuple](x: V): NamedTuple[N, V] = x diff --git a/tests/neg/named-tuples.check b/tests/neg/named-tuples.check index d9b6d686a587..a81041bf1b66 100644 --- a/tests/neg/named-tuples.check +++ b/tests/neg/named-tuples.check @@ -61,14 +61,24 @@ 28 | 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:30:27 --------------------------------------------------------------- +-- [E007] Type Mismatch Error: tests/neg/named-tuples.scala:30:21 ------------------------------------------------------ 30 | 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:33:18 --------------------------------------------------------------- + | ^^^^^^ + | Found: (Int, Int) + | Required: NamedTuple.NamedTuple[N2, Tuple] + | + | where: N2 is a type variable with constraint <: Tuple + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/named-tuples.scala:33:12 ------------------------------------------------------ 33 | person ++ (1, 2) match // error - | ^ - | Cannot prove that Tuple.Disjoint[(("name" : String), ("age" : String)), Tuple] =:= (true : Boolean). + | ^^^^^^ + | Found: (Int, Int) + | Required: NamedTuple.NamedTuple[N2, Tuple] + | + | where: N2 is a type variable with constraint <: Tuple + | + | longer explanation available when compiling with `-explain` -- Error: tests/neg/named-tuples.scala:36:17 --------------------------------------------------------------------------- 36 | val bad = ("", age = 10) // error | ^^^^^^^^ @@ -103,8 +113,8 @@ -- Warning: tests/neg/named-tuples.scala:25:29 ------------------------------------------------------------------------- 25 | val (name = x, agee = y) = person // error | ^^^^^^ - |pattern's type (String, Int) is more specialized than the right hand side expression's type (name : String, age : Int) + | pattern's type (String, Int) does not match the right hand side expression's type (name : String, age : Int) | - |If the narrowing is intentional, this can be communicated by adding `: @unchecked` after the expression, - |which may result in a MatchError at runtime. - |This patch can be rewritten automatically under -rewrite -source 3.2-migration. + | If the narrowing is intentional, this can be communicated by adding `: @unchecked` after the expression, + | which may result in a MatchError at runtime. + | This patch can be rewritten automatically under -rewrite -source 3.2-migration.