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

[SR-9507] Using trailing closure syntax on func with default parameters does not throw ambiguity error when it should #51960

Closed
swift-ci opened this issue Dec 13, 2018 · 11 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself

Comments

@swift-ci
Copy link
Collaborator

Previous ID SR-9507
Radar None
Original Reporter bdorfman (JIRA User)
Type Bug
Status Resolved
Resolution Invalid

Attachment: Download

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug
Assignee None
Priority Medium

md5: 2b22884bba0432522dccb6768f3486fd

Issue Description:

Encountered on Swift 4.2. Playground with examples is attached.

If you have two functions that are differentiated based on the parameter name of a block and you convert to trailing closure syntax, you will get an ambiguity error that it cannot determine which of the two functions to resolve to.

However if one of those functions has optional parameters after the "trailing" closure, you will not get an error or warning and the compiler will just decide which of the functions you meant (currently it seems to always choose the one without optional parameters). This can cause the behavior that converting to trailing closure syntax actually changes the behavior of your app even though it appears to just be syntactic sugar differences.

@swift-ci
Copy link
Collaborator Author

Comment by Pierre van Houtryve (JIRA)

Hello! I took your code and put it on compiler explorer. I think it'll be easier for others to check the code that way. Here's the link https://godbolt.org/z/mC1khh

EDIT:

I think the problematic diagnostic is emitted in ConstraintSystem.cpp, line 2399.

name.isOperator() ? diag::ambiguous_operator_ref

There's the instantiation of TrailingClosureAmbiguityFailure right after it, which emits the 2 notes we're seeing

Here's TrailingClosureAmbiguityFailure::diagnoseAsNote

bool TrailingClosureAmbiguityFailure::diagnoseAsNote() {

Hope that helps!

@belkadan
Copy link
Contributor

This is correct behavior: a function that can be called without default arguments is always preferred over a function that can only be called with default arguments. Trailing closures don't change that behavior.

@swift-ci
Copy link
Collaborator Author

Comment by Brian Dorfman (JIRA)

I think you may be misunderstanding the issue, I don't believe it is invalid. It is not just about default values, it is about ambiguity caused by omitting parameter names when using trailing closures. If you look at the example code it seems pretty clear.

I really don't think this is correct behavior, but if it is then it really shouldn't be.

EDIT: To be entirely clear in case the example playground was ambiguous in any way, the issue is that the first example does not throw an error when it should. The second example shows the ambiguous function error correctly being thrown by the compiler. Both cases are: two different functions have different named final parameters and when you using trailing closures it is ambiguous which you mean because you omit that name. It is absolutely a behavior that only occurs because of trailing closures, which is why I believe you are misunderstanding the problem and have closed this incorrectly.

@belkadan
Copy link
Contributor

Those are just the rules. Trailing closures try to match a closure parameter without checking the label, and overloads without default arguments are preferred over overloads with default arguments. I suppose we could say that the latter rule doesn't apply when matching trailing closures (because the labels are ignored), but it's not clearly "better".

@belkadan
Copy link
Contributor

Sorry, "those are just the rules" is meant to say "this is behaving as designed", but not "we can't change the design".

@swift-ci
Copy link
Collaborator Author

Comment by Brian Dorfman (JIRA)

Maybe this is a conflict of the current "rules" for trailing closures and default parameters? The rules for trailing closures seem to be: If using a trailing closure would cause a ambiguous reference because you omitted the name of the closure, an error is thrown. This can be seen in the second example in the attached playground. However the first example has the exact same problem and does not throw an error. Is it actually intentionally part of the rules of the language that the "overloads without default arguments are preferred" override this "trailing closure ambiguity is an error" rule? Or is it just undefined behavior at the collision of these features that happens to manifest in this confusing way?

This has caused actual problems in our code, and so if this is actually intentionally designed that ambiguous references when using trailing closures that have default parameters is not an error, I would like to request this be revisited.

@belkadan
Copy link
Contributor

There's no rule that says "this must be diagnosed". The rules say "you look for the best overload by matching types and labels for the arguments that are there, including checking for default arguments; if there's no single best overload, then diagnose".

I really don't mean to give you a runaround on this, but this is a request to change the language, and in a way that could cause existing programs to stop building. That kind of thing has to be brought up on the forums following the Swift Evolution Process.

@swift-ci
Copy link
Collaborator Author

Comment by Brian Dorfman (JIRA)

Specifically I want to really emphasize the fact that the functions that are colliding in my example are actually named differently, and one is not overloading the other as far as my understanding of the Swift language works (parameter names are part of function names / key paths, functions with differently named parameters are different and not overloading each other). This is the part of this issue that only manifests with trailing closures and not otherwise with just optional parameters which is why I strongly feel this is a bug and not an intentional design choice.

@belkadan
Copy link
Contributor

The existence of default arguments means that overloads have to be considered based on the base name, even if you can usually use labels to distinguish parameters before having to rely on argument types. Trailing closures ignore argument labels for that particular parameter; that's what they do. You can argue about whether or not this is a good design choice, but the compiler is behaving as designed.

@belkadan
Copy link
Contributor

And sadly, even if it were considered a bug, we'd still have to be careful about changing it, because there are probably programs that depend on it.

@swift-ci
Copy link
Collaborator Author

Comment by Brian Dorfman (JIRA)

> Trailing closures ignore argument labels for that particular parameter

They don't let you do this in other cases where it is ambiguous, which is the basis of my considering this a bug. But ok, I will see about writing a Swift Evolution proposal instead. Thank you for the quick response and taking the time to talk through this with me.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself
Projects
None yet
Development

No branches or pull requests

2 participants