Skip to content

Require SwiftASTContextReader to be valid at all times #1440

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 1 commit into from
Jul 10, 2020

Conversation

vedantk
Copy link

@vedantk vedantk commented Jul 9, 2020

Prior to this patch, it was possible to construct an invalid
SwiftASTContextReader (e.g. via default-init). This led to subtle bugs:
in rdar://65276251 for example, an invalid SwiftASTContextReader was
assigned into an llvm::Optional, and subsequent code crashed because it
assumed that the loaded llvm::Optional contained a valid instance.

Make it so that SwiftASTContextReader is valid at all times.

rdar://65276251

Prior to this patch, it was possible to construct an invalid
SwiftASTContextReader (e.g. via default-init). This led to subtle bugs:
in rdar://65276251 for example, an invalid SwiftASTContextReader was
assigned into an llvm::Optional, and subsequent code crashed because it
assumed that the loaded llvm::Optional contained a valid instance.

Make it so that SwiftASTContextReader is valid at all times.

rdar://65276251
@vedantk vedantk requested review from adrian-prantl and dcci and removed request for adrian-prantl July 9, 2020 21:59
@vedantk
Copy link
Author

vedantk commented Jul 9, 2020

@swift-ci test

lldb::TargetSP target_sp(GetTargetSP());
if (!target_sp)
return {};
return llvm::None;

Choose a reason for hiding this comment

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

I guess we could leave this as is, too.

@adrian-prantl
Copy link

It's not quite clear to me how this change improves the code, but I'm probably missing something. Previously SwiftASTContextReader had an operator bool() that returned false if the context was invalid, now we vend an Optional, with effectively the same semantics?

What's an example of a bad use the this prevents?

@adrian-prantl
Copy link

Looking at the radar, could we have fixed this by changing

   llvm::Optional<SwiftASTContextReader> scratch_ctx;
   if (instance) {
    scratch_ctx = instance->GetScratchSwiftASTContext();
    if (!scratch_ctx)
       return llvm::None;
   }

to

   SwiftASTContextReader scratch_ctx;
   if (instance) {
    scratch_ctx = instance->GetScratchSwiftASTContext();
    if (!scratch_ctx)
       return llvm::None;
   }

?

@adrian-prantl
Copy link

My point is, I don't think we can prevent that kind of stupidity at the call site ;-)
(speaking as the likely author here)

@adrian-prantl
Copy link

Hmm, you are right, this patch does protect against this misuse, because assigning an Optional to an Optional won't auto-wrap the Optional in another Optional!

@vedantk
Copy link
Author

vedantk commented Jul 10, 2020

Yep, we certainly could have fixed the bug that way.

In general I think Optional<T> x = get(); if (!x) { ... } is much better than T x = get(); if (!x) { ... }. For one, it forces us to think about validity (you can't simply forget and type "x.stuff()" without unwrapping the Optional -- the compiler will yell at you). It also separates the concerns of checking-for-validity and doing-things-with-the-instance.

In this case, it looks like we'd make_unique a copy of a SwiftASTContextReader, even if the original was invalid. We no longer do that. For a different kind of example, note how tons of functions have to repeatedly validity-check CompilerType in lldb. The same code smell doesn't exist in llvm, where there is simply no notion of an invalid Type.

@vedantk
Copy link
Author

vedantk commented Jul 10, 2020

@swift-ci test Linux platform

@adrian-prantl
Copy link

I like the argument that everything following an "optional" pattern should literally be an llvm::Optional, that's a good point!
With the Reader being always valid, why does SwiftASTContextForExpressions *get() still return a pointer? Could we hide this indirection by defining an operator SwiftASTContextForExpression &() or some other trick? It's a little weird having to dereference the contents of the Optional even though it is guaranteed to be non-null.

@vedantk
Copy link
Author

vedantk commented Jul 10, 2020

I agree it's a bit weird. I left 'get' as-is for convenience, since a large number of APIs expect SwiftASTContext *. I'm open to changing that, although maybe we should look at changing those APIs first?

@adrian-prantl
Copy link

What do you think about a tiny patch for the swift-5.3 branch that just fixes the programming error, and the idealized refactoring for master/master-next?

@adrian-prantl
Copy link

Since all APIs taking a SwiftASTContext * probably return an error if it is null, we might even be able to delete some code by converting it to a reference.

@vedantk
Copy link
Author

vedantk commented Jul 10, 2020

I'll spin off a more targeted bugfix for 5.3. Thanks!

@vedantk vedantk merged commit 4510748 into swiftlang:swift/master Jul 10, 2020
@adrian-prantl
Copy link

If you haven't done so already, can you please also cherry-pick this to swift/master-next?
We don't have an automerger in that direction.

@vedantk
Copy link
Author

vedantk commented Jul 10, 2020

Just finished the merge.

vedantk added a commit to vedantk/apple-llvm-project that referenced this pull request Jul 10, 2020
This is a narrower version of
swiftlang#1440 spun off for 5.3.

I've kept 'scratch_ctx' wrapped in an Optional to minimize the necessary
source changes. Simply removing the Optional triggers a subsequent
assert because the context lock is not taken (even though the context
reader is invalid).

rdar://65276251
@vedantk vedantk deleted the eng/PR-65276251 branch July 11, 2020 22:51
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.

3 participants