scala / scala3

The Scala 3 compiler, also known as Dotty.
https://dotty.epfl.ch
Apache License 2.0
5.79k stars 1.04k forks source link

Named Tuple element selectable even when multiple elements have same name #21301

Open kavedaa opened 1 month ago

kavedaa commented 1 month ago

Compiler version

3.5.0-RC5 and others.

Minimized code

Welcome to Scala 3.6.0-RC1-bin-20240729-4429d73-NIGHTLY-git-4429d73 (21, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.

scala> import scala.language.experimental.namedTuples

scala> val x = (1, 2)
val x: (Int, Int) = (1,2)

scala> val y: NamedTuple.NamedTuple[("a", "a"), (Int, Int)] = x
val y: (a : Int, a : Int) = (1,2)

scala> y.a
val res0: Int = 1

Expectation

Compilation error on y.a.

soronpo commented 1 month ago

The error should be in preventing such named tuple as defined by y. I'm not sure if it's possible with current named tuple implementation.

bishabosha commented 1 month ago

The problem identified is manually constructing the type - it's on you to enforce the uniqueness - if you use the named tuple syntax, or any of the named tuple methods then there is a check

It's a bit like asInstanceOf

kavedaa commented 1 month ago

@bishabosha I think the questions should rather be:

  1. Is it possible for the compiler to detect multiple elements with same name?
  2. If so, would that be useful?

I think the answer to 2) is clearly "yes". I don't have sufficiently technical knowledge to answer 1), but my assumption was that at y.a the compiler has to at least look up which element corresponds to a - if any at all - and that at that point it would be possible to detect that there was not only one but multiple elements having that name. I could be wrong of course.

bishabosha commented 1 month ago

There could probably be put in an ambiguity check on selection, here is the code taken from Typer:

def tryNamedTupleSelection() =
  val namedTupleElems = qual.tpe.widenDealias.namedTupleElementTypes
  val nameIdx = namedTupleElems.indexWhere(_._1 == selName) 
  if nameIdx >= 0 && Feature.enabled(Feature.namedTuples) then // ? include extra `count == 1` check?
    typed(
      untpd.Apply(
        untpd.Select(untpd.TypedSplice(qual), nme.apply),
        untpd.Literal(Constant(nameIdx))),
      pt)
  else EmptyTree

otherwise the cost of peephole checking all types in case they might be some malformed named tuple might be too much