Skip to content

Fix for issue #65913 - Compiler suggests a wrong insertion point for 'await' in if let ... #71716

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

Closed
wants to merge 0 commits into from

Conversation

jameesbrown
Copy link
Contributor

Resolves #65913
I Added check in typeCheckEffects.cpp in the function diagnoseUncoveredAsyncSite(:) to see if we are looking at a shorthand version of optional binding. If so, we change the fix-it to be syntactically correct. Note that this still gives the correct fix-it for the non-shorthand version.

image

image

@jameesbrown
Copy link
Contributor Author

@AnthonyLatsis I think this should resolve #65913, I added a test because It didn’t look like there was one for this diagnostic yet. I also reran all of the tests in Sema and smoke tested (manually) around optional binding/async compiler diagnostics. This is my first time contributing so please let me know if there’s something I didn’t do properly.

@YOCKOW
Copy link
Member

YOCKOW commented Feb 21, 2024

@swift-ci Please smoke test

@jameesbrown
Copy link
Contributor Author

Looks like the test failed because I did not add // REQUIRES: concurrency, I added this and also -disable-availability-checking though I am not sure if that is necessary.

Copy link
Collaborator

@AnthonyLatsis AnthonyLatsis left a comment

Choose a reason for hiding this comment

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

Thank you! This is a really good start.

Comment on lines 3579 to 3591
// Check if we are looking at a shorthand optional binding.
// In that case, we'll want to supply a different fixIt.
const DeclRefExpr *declRefExpr;
if (const LoadExpr *loadExpr = dyn_cast<LoadExpr>(anchor)) {
declRefExpr = dyn_cast<DeclRefExpr>(loadExpr->getSubExpr());
} else {
declRefExpr = dyn_cast<DeclRefExpr>(anchor);
}
if (declRefExpr && declRefExpr->isImplicit()) {
if (const VarDecl *varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl())) {
fixIt = (varDecl->getNameStr() + " = await ").str();
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

LoadExpr is not the only thing that can get in the way:

class A {}
class B: A {}

func test() async {
  async let result: B? = nil
  if let result: A {}
}

Implicitness is a necessary condition for shorthand optional binding here, but I am not quite as certain it is sufficient too. You may have noticed by now that inserting result = await before a legal typed pattern as above will not work. The general solution is to insert after the pattern, so it seems we need to propagate the pattern one way or another for both a definitive check and the correct insertion location.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes you're right, a more general solution that is aware of the actual pattern would be better (rather than inferring its existence like we are now). I had not thought of the case of inserting before a legal typed pattern, I may have been too focused on a specific set of cases. I will work on this some more and see what I can come up with. Thanks for taking a look at it and pointing me in the right direction!

Copy link
Contributor Author

@jameesbrown jameesbrown Feb 26, 2024

Choose a reason for hiding this comment

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

@AnthonyLatsis I was able to find a better solution, you'd given me a good hint. Even still, it took me a while to realize we probably have other fixItInsert functions. The new solution is to use fixItInsertAfter with the specialized fixIt given the conditions:

  1. The expression type is optional.
  2. Has a sub expression that is a DeclRefExpr and Implicit.

My reasoning is that I saw in ParseStmt.cpp, where we parse a shorthand optional binding we have:

else if (!ThePattern.getPtrOrNull()->getBoundName().empty()) {
    auto bindingName = DeclNameRef(ThePattern.getPtrOrNull()->getBoundName());
    auto loc = DeclNameLoc(ThePattern.getPtrOrNull()->getEndLoc());
    auto declRefExpr = new (Context) UnresolvedDeclRefExpr(bindingName,
                                                           DeclRefKind::Ordinary,
                                                           loc);
    
     declRefExpr->setImplicit();
     Init = makeParserResult(declRefExpr);

And I am thinking that implies 1 and 2, though I'm still unsure if it holds in the other direction.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi @AnthonyLatsis, could you take another look at this for me when you get the chance?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Hello, James. I’m sorry it took me so long to get back to you. What happened to the changes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi, sorry I was updating my checkout last night and switched the branch that I have this PR on without realizing it would close this. I reopened it with the changes on #72726.

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.

Compiler suggests a wrong insertion point for 'await' in if let ....
3 participants