-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[LifetimeSafety] Add support for conditional operators #167240
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -440,6 +440,7 @@ void no_error_loan_from_current_iteration(bool cond) { | |
| //===----------------------------------------------------------------------===// | ||
|
|
||
| View Identity(View v [[clang::lifetimebound]]); | ||
| MyObj* Identity(MyObj* v [[clang::lifetimebound]]); | ||
| View Choose(bool cond, View a [[clang::lifetimebound]], View b [[clang::lifetimebound]]); | ||
| MyObj* GetPointer(const MyObj& obj [[clang::lifetimebound]]); | ||
|
|
||
|
|
@@ -582,3 +583,75 @@ void lifetimebound_ctor() { | |
| } | ||
| (void)v; | ||
| } | ||
|
|
||
| // Conditional operator. | ||
| void conditional_operator_one_unsafe_branch(bool cond) { | ||
| MyObj safe; | ||
| MyObj* p = &safe; | ||
| { | ||
| MyObj temp; | ||
| p = cond ? &temp // expected-warning {{object whose reference is captured may not live long enough}} | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this trigger in all modes or only pessimitic/conservative? I vaguely remember discussing this but can't remember where we landed.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This triggers in only strict mode. Confidence is driven by liveness now and |
||
| : &safe; | ||
| } // expected-note {{destroyed here}} | ||
|
|
||
| // This is not a use-after-free for any value of `cond` but the analysis | ||
| // cannot reason this and marks the above as a false positive. This | ||
| // ensures safety regardless of cond's value. | ||
| if (cond) | ||
| p = &safe; | ||
| (void)*p; // expected-note {{later used here}} | ||
| } | ||
|
|
||
| void conditional_operator_two_unsafe_branches(bool cond) { | ||
| MyObj* p; | ||
| { | ||
| MyObj a, b; | ||
| p = cond ? &a // expected-warning {{object whose reference is captured does not live long enough}} | ||
| : &b; // expected-warning {{object whose reference is captured does not live long enough}} | ||
| } // expected-note 2 {{destroyed here}} | ||
| (void)*p; // expected-note 2 {{later used here}} | ||
| } | ||
|
|
||
| void conditional_operator_nested(bool cond) { | ||
| MyObj* p; | ||
| { | ||
| MyObj a, b, c, d; | ||
| p = cond ? cond ? &a // expected-warning {{object whose reference is captured does not live long enough}}. | ||
| : &b // expected-warning {{object whose reference is captured does not live long enough}}. | ||
| : cond ? &c // expected-warning {{object whose reference is captured does not live long enough}}. | ||
| : &d; // expected-warning {{object whose reference is captured does not live long enough}}. | ||
| } // expected-note 4 {{destroyed here}} | ||
| (void)*p; // expected-note 4 {{later used here}} | ||
| } | ||
|
|
||
| void conditional_operator_lifetimebound(bool cond) { | ||
| MyObj* p; | ||
| { | ||
| MyObj a, b; | ||
| p = Identity(cond ? &a // expected-warning {{object whose reference is captured does not live long enough}} | ||
| : &b); // expected-warning {{object whose reference is captured does not live long enough}} | ||
| } // expected-note 2 {{destroyed here}} | ||
| (void)*p; // expected-note 2 {{later used here}} | ||
| } | ||
|
|
||
| void conditional_operator_lifetimebound_nested(bool cond) { | ||
| MyObj* p; | ||
| { | ||
| MyObj a, b; | ||
| p = Identity(cond ? Identity(&a) // expected-warning {{object whose reference is captured does not live long enough}} | ||
| : Identity(&b)); // expected-warning {{object whose reference is captured does not live long enough}} | ||
| } // expected-note 2 {{destroyed here}} | ||
| (void)*p; // expected-note 2 {{later used here}} | ||
| } | ||
|
|
||
| void conditional_operator_lifetimebound_nested_deep(bool cond) { | ||
| MyObj* p; | ||
| { | ||
| MyObj a, b, c, d; | ||
| p = Identity(cond ? Identity(cond ? &a // expected-warning {{object whose reference is captured does not live long enough}} | ||
| : &b) // expected-warning {{object whose reference is captured does not live long enough}} | ||
| : Identity(cond ? &c // expected-warning {{object whose reference is captured does not live long enough}} | ||
| : &d)); // expected-warning {{object whose reference is captured does not live long enough}} | ||
| } // expected-note 4 {{destroyed here}} | ||
| (void)*p; // expected-note 4 {{later used here}} | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe comment why we only kill for the true branch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we kill to clear the initial state and merge (flow) both origins into it.