Skip to content

[Async Refactoring] Better handle non-refutable patterns #37720

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 11 commits into from
Jun 9, 2021

Conversation

hamishknight
Copy link
Contributor

@hamishknight hamishknight commented Jun 1, 2021

Keep track of patterns that bind multiple vars and print them out when converting an async call. If the parameter being bound isn't referenced elsewhere, we'll print the pattern inline as e.g:

let ((x, y), z) = await foo()

Otherwise, if the parameter is referenced elsewhere in the block we'll print the pattern out of line, such as:

let (res, z) = await foo()
let (x, y) = res

In addition, make sure to print var bindings out of line if there's also a let binding, e.g:

let x = await foo()
var y = x

This ensures any mutations to y doesn't affect x. If there's only a single var binding, we'll print it inline.

Finally this PR ensures that refutable patterns and unclassified switch cases are left unhandled rather than being silently added to the success block.

rdar://77802560

@hamishknight hamishknight requested a review from bnbarham June 1, 2021 17:38
@hamishknight hamishknight changed the title [Async Refactoring] Better handle non-refutable patterns in async transform [Async Refactoring] Better handle non-refutable patterns Jun 1, 2021
@swiftlang swiftlang deleted a comment from swift-ci Jun 1, 2021
@hamishknight hamishknight force-pushed the all-the-patter branch 2 times, most recently from d994943 to 8a21fbb Compare June 4, 2021 15:41
@hamishknight
Copy link
Contributor Author

Updated to unify the code paths for single-var patterns and multi-var patterns a little (though a lot of logic is only really interested in single-var patterns), and also changed the behavior to make sure we always print var bindings out of line if there are also other let bindings present, as the var bindings may need separate storage for any mutations made.

I've left the handling of mixed let and var bindings in a single pattern for a future PR though.

Copy link
Contributor

@bnbarham bnbarham left a comment

Choose a reason for hiding this comment

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

Might be worth running these changes through the stress tester first given they're fairly... extensive. But otherwise, LGTM - very cool!

// OUT-OF-LINE-CASE-NEXT: print(str1, str2, strs)

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=FALLBACK %s
stringTupleParam { strs, err in
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems weird to fallback here since the guard makes it pretty clear the rest is a success... Is it just because we see the err == nil in an if with another unhandled condition?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah that's exactly why, and I definitely agree we could do better here. I'll see if I can handle it along with cases like rdar://78564388. For now, I'll add a FIXME and add another fallback test case that's a little more reasonable :)

// MIXED-TUPLE-RESULT-NEXT: }

// RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-TUPLE-RESULT2 %s
mixedTupleResult { res in
Copy link
Contributor

Choose a reason for hiding this comment

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

There's probably a few tests in here that are a little superfluous given others but... eh.

At some point I'd like to update the %refactor* tools to be similar to the swift-ide-test batch completion ones given the number we're adding 😆.

Copy link
Contributor Author

@hamishknight hamishknight Jun 5, 2021

Choose a reason for hiding this comment

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

Ah, I think what happened here is I ended up adding the MIXED-TUPLE-RESULT test case after this one and then didn't notice the overlap. I'll tweak this one so it's at least testing something slightly different.

At some point I'd like to update the %refactor* tools to be similar to the swift-ide-test batch completion ones given the number we're adding 😆.

That'd be awesome!

@hamishknight
Copy link
Contributor Author

TIL apparently you can do

@swift-ci please SourceKit stress test

@hamishknight
Copy link
Contributor Author

Hah, that uncovered another ASTWalker bug

@swiftlang swiftlang deleted a comment from swift-ci Jun 6, 2021
@bnbarham
Copy link
Contributor

bnbarham commented Jun 8, 2021

Hah, that uncovered another ASTWalker bug

Woo! (sort of :P)

I don't believe this case currently can come up,
but leave it explicitly unhandled for now so we
don't perform an erroneous transform if it ever
comes up in the future.
These aren't currently supported.
Previously we would silently add these to the
success block. For now, let's leave them
unhandled.
Add the necessary walking hooks, and fix
ReferenceCollector to use it.
Use a DenseMap to track the number of references to
a given decl in a scope.
Allow it to iterate over an arbitrary container
type.
Keep track of patterns that bind multiple vars and
print them out when converting an async call. If
the parameter being bound isn't referenced elsewhere,
we'll print the pattern inline as e.g:

```
let ((x, y), z) = await foo()
```

Otherwise, if the parameter is referenced elsewhere
in the block we'll print the pattern out of line,
such as:

```
let (res, z) = await foo()
let (x, y) = res
```

In addition, make sure to print var bindings out
of line if there's also a let binding, e.g:

```
let x = await foo()
var y = x
```

This ensures any mutations to y doesn't affect x.
If there's only a single var binding, we'll print
it inline.

rdar://77802560
If we're in a case stmt body, any DeclRefExprs
to vars bound by a pattern will actually be to an
implicit var decl that's declared for the body. We
therefore need to walk to its "parent" to get to
the var referenced by the pattern, which will have
the correct entries in the naming and placeholder
maps.
@hamishknight
Copy link
Contributor Author

@swift-ci please test

@hamishknight
Copy link
Contributor Author

@swift-ci please SourceKit stress test

@hamishknight
Copy link
Contributor Author

Stress tester failure unrelated

@hamishknight hamishknight merged commit 107780c into swiftlang:main Jun 9, 2021
@hamishknight hamishknight deleted the all-the-patter branch June 9, 2021 15:19
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.

2 participants