Skip to content

Introduce then statements #2000

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

Merged
merged 3 commits into from
Sep 1, 2023
Merged

Introduce then statements #2000

merged 3 commits into from
Sep 1, 2023

Conversation

hamishknight
Copy link
Contributor

These allow multi-statement if/switch expression branches that can produce a value at the end by saying then <expr>. This is gated behind an experimental feature option pending evolution discussion.

@hamishknight hamishknight requested a review from ahoppen as a code owner August 4, 2023 20:49
Copy link
Member

@ahoppen ahoppen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementation looks pretty good to me. I mostly have concerns with source breakage. I left a bunch of comments with whatever came to my mind.

func testThenStmt5() {
assertParse(
"""
then (1, 2)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m sure it’s been discussed somewhere but I missed it: What are the disambiguation rules here. This could be a function call as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The space right now IIUC. Ie. then(1, 2) -> function call, then (1, 2) -> then statement.

func testThenStmt7() {
assertParse(
"""
then []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly here, before the feature, this would be a subscript call to then.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, I don't expect writing functions and subscripts with an intermediate space is a common case tho.

func testThenStmt13() {
assertParse(
"""
then .foo
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here. Why isn’t this a member access on then?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the answer to basically all of these is "the whitespace". There's also:

then
  .foo

and I'm not sure if that one is a statement or member access with the rules as is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently that's a statement, I'm hoping that then is a fairly uncommon name for a variable, but if not we'll probably need an extra rule that says the following expression must be on the same line for it to be treated as a statement

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also disambiguate with (then), but that's pretty awful

// This is a function call.
assertParse(
"""
then {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you have then followed by a closure?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then ({}) was the answer there I believe

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, producing a closure from a then should a fairly uncommon thing to do anyways

Comment on lines +970 to +968
case .leftParen, .leftSquare, .period:
// These are handled based on whether there is trivia between the 'then'
// and the token. If so, it's a 'then' statement. Otherwise it should
// be treated as an expression, e.g `then(...)`, `then[...]`, `then.foo`.
return !self.currentToken.trailingTriviaText.isEmpty || !next.leadingTriviaText.isEmpty
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT, the following would be interpreted as a ThenStmt but I feel like it would be fairly common to write

then
  .filter { ... }
  .map { ... }

@hamishknight
Copy link
Contributor Author

Thanks for the review! I'll respond Monday since it's getting late here :)

@hamishknight hamishknight force-pushed the then branch 2 times, most recently from 2e588a3 to bff1bb2 Compare August 9, 2023 12:12
@hamishknight hamishknight requested a review from ahoppen August 9, 2023 12:13
@hamishknight
Copy link
Contributor Author

For now I've left the heuristic without the newline rule, I want to do some source compatibility testing to see how often that actually happens in code

@hamishknight
Copy link
Contributor Author

@ahoppen FYI the above force push diff is the correct diff of changes, I can separate out into a separate commit if you prefer tho

@hamishknight
Copy link
Contributor Author

hamishknight commented Aug 9, 2023

error: no type named 'ThenStmtSyntax' in module 'SwiftSyntax'
  case thenStmt(SwiftSyntax.ThenStmtSyntax)
                            ^

Gahhhh Swift 5.7 doesn't support SPI enum cases :( It was fixed in Swift 5.8

Copy link
Member

@ahoppen ahoppen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed offline, I think it would be good to split this PR into a preliminary PR that introduces the experimental language feature infrastructure and then the actual PR that introduces the then statements.

@hamishknight hamishknight force-pushed the then branch 2 times, most recently from 3570163 to 9faa43d Compare August 10, 2023 11:34
@hamishknight
Copy link
Contributor Author

Updated for feedback, and dropped the SPI from the syntax node for the time being as it's not supported.

These allow multi-statement `if`/`switch` expression
branches that can produce a value at the end by
saying `then <expr>`. This is gated behind an
experimental feature option pending evolution
discussion.
@hamishknight
Copy link
Contributor Author

swiftlang/swift#67454

@swift-ci please test

@hamishknight hamishknight merged commit 2504b3f into swiftlang:main Sep 1, 2023
@hamishknight hamishknight deleted the then branch September 1, 2023 21:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants