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-11724] Provide a diagnostic when a closure parameter is declared with type sugar #54133

Closed
CodaFi opened this issue Nov 6, 2019 · 10 comments · Fixed by #28315
Closed

[SR-11724] Provide a diagnostic when a closure parameter is declared with type sugar #54133

CodaFi opened this issue Nov 6, 2019 · 10 comments · Fixed by #28315
Assignees
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. closures Feature: closures compiler The Swift compiler in itself diagnostics QoI Bug: Diagnostics Quality of Implementation expressions Feature: expressions good first issue Good for newcomers parser Area → compiler: The legacy C++ parser

Comments

@CodaFi
Copy link
Member

CodaFi commented Nov 6, 2019

Previous ID SR-11724
Radar rdar://problem/56950572
Original Reporter @CodaFi
Type Bug
Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, DiagnosticsQoI, StarterBug
Assignee None
Priority Medium

md5: 48f0d1bec40f4cb6b0df056014613eb7

Issue Description:

Closure parameters are already allowed to shadow types. What they are not allowed to do is shadow sugared types, or attempt to destructure them:

struct Bar {}

struct Foo {
  func registerCallback(_ callback: @escaping ([Bar]) -> Void) {}
  func registerCallback(_ callback: @escaping ([String: Bar]) -> Void) {}
  func registerCallback(_ callback: @escaping (Bar?) -> Void) {}
}

Foo().registerCallback { ([Bar]) in }
Foo().registerCallback { ([String: Bar]) in }
Foo().registerCallback { (Bar?) in }

This emits a pile of diagnostics, and none of them are good. We should be able to catch this during Parse and emit a much better diagnostic. Ideally one that inserts what the user probably meant

Foo().registerCallback { (_: [Bar]) in }
Foo().registerCallback { (_: [String: Bar]) in }
Foo().registerCallback { (_: Bar?) in }
@CodaFi
Copy link
Member Author

CodaFi commented Nov 6, 2019

@swift-ci create

@CodaFi
Copy link
Member Author

CodaFi commented Nov 6, 2019

Tagging this as a starter bug, but it's kind of an open-ended intermediate bug. The parsing logic for ParamDecls is a mess. But we should at least be emitting some kind of diagnostic consistently when we come across these kinds of parameters. Today, it's up to overload resolution to make sure this is rejected, which is way too late.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@michaelleejl
Copy link

Hey @CodaFi - I'd like to start contributing to Swift.

I think I'll need some initial orientation. Where is the parsing logic for ParamDecls? I've had a brief look at ParsePattern.cpp, which looks like a good place to start digging.

Full disclosure: I'm in university right now, so progress might be quite bursty.

If this is ok, I'm wondering if I could be assigned this issue? Thanks!

@AnthonyLatsis AnthonyLatsis added parser Area → compiler: The legacy C++ parser closures Feature: closures expressions Feature: expressions labels Nov 4, 2023
@AnthonyLatsis
Copy link
Collaborator

@michaelleejl Would you be interested in addressing this in the new parser instead, which is written in Swift? Diagnostic improvements to the old parser are somewhat undesirable at this point because we would still have to pick them over to the new parser before shipping it.


Interestingly, the old parser accepts the following case with a warning, and rejects the Int? case:

func test(_: ([Int]) -> Void) {}
test { ([Int]) in } // warning: unnamed parameters must be written with the empty name '_' [parameter_unnamed_warn]

The new parser rejects them all with e.g. error: unexpected code '[Int]' in parameter clause.

@ahoppen Is this expected? If it is, we should modify the warning message to say that it will become an error in, I suppose, Swift 6.

@michaelleejl
Copy link

@AnthonyLatsis Yeah sure that makes sense! Happy to work on the new parser. Where do I find that?

@AnthonyLatsis
Copy link
Collaborator

The new parser lives in the swift-syntax repository. Alex can transfer the issue once they get to it.

@michaelleejl
Copy link

(Very low priority) Out of curiosity, I see that the parser is a recursive descent parser. Is there a reason this is done, instead of say an LR parser? I assume there are no left-recursive productions in the grammar?

@ahoppen
Copy link
Contributor

ahoppen commented Nov 6, 2023

Is this expected? If it is, we should modify the warning message to say that it will become an error in, I suppose, Swift 6.

That’s a bug. I filed apple/swift-syntax#2331.

(Very low priority) Out of curiosity, I see that the parser is a recursive descent parser. Is there a reason this is done, instead of say an LR parser? I assume there are no left-recursive productions in the grammar?

I have never implemented an LR parser but my impression has always been that recursive decent parsers are a lot easier to maintain and it’s a lot easier to recover from parsing errors than with LR parsers. @CodaFi Do you have anything to add? Did we ever consider an LR parser for Swift?

@CodaFi
Copy link
Member Author

CodaFi commented Nov 6, 2023

I don't think we really considered some underlying formal model for the parser when we brought up either the C++ or the Swift parsers. I think a lot of the C-family compiler folk have settled naturally on recursive descent for its ease of maintainability and the ability to more easily interleave diagnostics with the natural flow of control while parsing. (Now, for the Swift parser where we take pains to separate the diagnostics pass from the parsing pass, this "interleaving problem" is a moot point). It's also worth noting we're not actually sure what a purely properly formal model of Swift's grammar is! The grammar outlined in the Swift book, for example, is descriptive, not prescriptive. If you translated it line for line, you would certainly parse a subset of Swift. But as authors of parsers for programming languages of any notable size inevitably come to discover: the parser accepts a larger grammar than its authors intend.

And this is not always a bad thing! I consider it table stakes for a parser for a modern programming language to accept a larger "semantic shadow grammar" than the formal language spec to enable users in the throes of software development to have decent errors in their IDEs of choice. For a recursive descent parser, it's far easier to carve out those extra niches than in my experience with LR or other kinds of (not always, but frequently) table/tool-driven approaches.

@AnthonyLatsis
Copy link
Collaborator

This happens to be fixed in the old parser (see linked PR). For example:

warning: unnamed parameters must be written with the empty name '_' [parameter_unnamed_warn]
test { ([X]) in }
        ^
        _: 

Let us close this issue and use apple/swift-syntax#2340 to track the analogous problem in the new parser.

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. closures Feature: closures compiler The Swift compiler in itself diagnostics QoI Bug: Diagnostics Quality of Implementation expressions Feature: expressions good first issue Good for newcomers parser Area → compiler: The legacy C++ parser
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants