Same-named argument and tuple labels?

This program compiles successfully (with default toolchains of Xcode 9.4 and Xcode 10 beta, and recent snapshots):

func f(x a: Int, x b: Int) -> (y: Int, y: Int) {
    return (a, b)
}
print(f(x: 1, x: 2)) // prints (y: 1, y: 2)

Are same-named argument and tuple labels supported by design, and if so, what is the rationale?

(I tried but could not find any information on this.)

1 Like

Good catch, would you mind file a bug report? This looks like a bug to me.

This might be a case of this not being explicitly disallowed (yet)?

As a user, I find this very confusing to say the least.

In the case of functions, the parameters can be differentiated based on their position, but when default values come into play, the behavior is no longer obvious:

func f(x a: Int = 0, x b: Int = 0) -> (y: Int, y: Int) {
    return (a, b)
}
print(f(x: 1)) // prints (y: 1, y: 0)

That code compiles but its behavior is not obvious.

The same is true for the following:

func f(x a: Int = 0, x b: Int = 0) -> (y: Int, y: Int) {
    return (a, b)
}
f(x: 1, x: 2).y // returns 1

So +1 from me on considering this a bug.

Imho that shouldn't be disallowed - but f(x: 1, x: 2).y should be an error in this case:
The components of (Int, Int) are both labeled in the same way (no label at all), and you can always access the contents by index.

1 Like

I must admit that I didn't know that and had to try it out.

If that's an error then what practical reasons would there be for naming two tuple elements the same?

I don't see a reason, as in the example with equal argument names that is not an ambiguity in in most cases and thus not an error. But here, unlike in a regular redeclaration, we have a possibility to disambiguate, which is why I agree with @tino in delaying the error until one actually ambiguously refers to the element by label.

1 Like

I'm hesitant to file tuple-related bugs since I have no idea what the design goals for tuples are, and I'm a bit worried that compiler developers might be working without a common goal in this area, in which case bug(?) reports and fixes(?) might just add more cruft and confusion. Here's a simple example to show what I mean by this:

// This compiles successfully in Xcode 9.4, 10 beta and recent snapshots:
let c = (singleElementTupleLabel: 123).singleElementTupleLabel

But this can be either (1) a bug or (2) expected behavior, depending on whether single element labeled tuple types should (1) continue to be banned or (2) be re-enabled.

I'd prefer (2) since it would remove a weird special case, and there are signs that things are moving in that direction as of Xcode 10 beta. I know you saw my other thread about this specific case but I'm linking to it here for reference.

4 Likes

Maybe @Joe_Groff can help.

I assume, but I'm not sure, that the following is also an example of ambiguously referring to the element by label (from within a pattern):

let t = (x: "first", x: "second")
let (a, x: b) = t print(a, b) // prints: second first

?

It might be a bit surprising that the x: in let (a, x: b) matches the first x: rather than the second, which results in the elements being swapped.

Please do file bugs. Now that the contours of the language are better defined we most definitely need to start thinking about filling in the gaps and cleaning up corner cases. As for cruft and confusion there have been disagreements and misunderstandings among compiler developers about what the correct behavior should be in certain cases, but we do try to review changes and make sure they’re consistent.

Your particular example of a single element tuple expression is a bug. It’s something I’ve wanted to finish off for a while but haven’t had the chance to yet. Single element tuples are banned in type position, and they should be banned in expressions too, it’s just not implemented properly yet. The behavior change between Swift 4 and 4.1 was not intentional here.

The reason single element tuples are problematic is that we have implicit conversions between tuple types with and without labels. But there’s no such thing as a tuple type with one element and no label, so instead you would need to define conversions between any arbitrary type T and a tuple type (foo: T). This is kind of a weird rule to have, and in older Swift releases this was implemented and it caused problems. I’ve been ripping out vestigial remnants of this conversion over the years and it simplified the type checker in several places.

3 Likes

@Slava_Pestov, thanks for the clarification. Can you also say whether the same-named argument and tuple labels as exemplified in the OP is a bug or not?

That is, should the following program compile (as it currently does):

func f(x a: Int, x b: Int) -> (y: Int, y: Int) {
    return (a, b)
}
print(f(x: 1, x: 2)) // prints (y: 1, y: 2)

?
(Note that x and y are used twice, producing same named argument and tuple labels.)


(I did report the single element tuple expression bug (not related to this thread) as SR-8109.)

Yeah, I think same-named tuple labels should probably be prohibited.

Thanks, filed SR-8974.