-
Notifications
You must be signed in to change notification settings - Fork 14.1k
[clang] Fix the post-filtering heuristic for GSLPointer. #114044
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
15b7806
52d3387
8cb609c
1666c4f
352d342
80a50d1
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 |
---|---|---|
|
@@ -604,8 +604,9 @@ struct [[gsl::Pointer]] Span { | |
|
||
// Pointer from Owner<Pointer> | ||
std::string_view test5() { | ||
std::string_view a = StatusOr<std::string_view>().valueLB(); // expected-warning {{object backing the pointer will be dest}} | ||
return StatusOr<std::string_view>().valueLB(); // expected-warning {{returning address of local temporary}} | ||
// The Owner<Pointer> doesn't own the object which its inner pointer points to. | ||
std::string_view a = StatusOr<std::string_view>().valueLB(); // OK | ||
return StatusOr<std::string_view>().valueLB(); // OK | ||
|
||
// No dangling diagnostics on non-lifetimebound methods. | ||
std::string_view b = StatusOr<std::string_view>().valueNoLB(); | ||
|
@@ -652,7 +653,7 @@ Span<std::string> test10(StatusOr<std::vector<std::string>> aa) { | |
|
||
// Pointer<Owner>> from Owner<Pointer<Owner>> | ||
Span<std::string> test11(StatusOr<Span<std::string>> aa) { | ||
return aa.valueLB(); // expected-warning {{address of stack memory}} | ||
return aa.valueLB(); // OK | ||
return aa.valueNoLB(); // OK. | ||
} | ||
|
||
|
@@ -693,3 +694,86 @@ void test() { | |
auto y = std::set<int>{}.begin(); // expected-warning {{object backing the pointer}} | ||
} | ||
} // namespace GH118064 | ||
|
||
namespace LifetimeboundInterleave { | ||
|
||
const std::string& Ref(const std::string& abc [[clang::lifetimebound]]); | ||
hokein marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
std::string_view TakeSv(std::string_view abc [[clang::lifetimebound]]); | ||
std::string_view TakeStrRef(const std::string& abc [[clang::lifetimebound]]); | ||
std::string_view TakeStr(std::string abc [[clang::lifetimebound]]); | ||
|
||
std::string_view test1() { | ||
std::string_view t1 = Ref(std::string()); // expected-warning {{object backing}} | ||
t1 = Ref(std::string()); // expected-warning {{object backing}} | ||
return Ref(std::string()); // expected-warning {{returning address}} | ||
|
||
std::string_view t2 = TakeSv(std::string()); // expected-warning {{object backing}} | ||
t2 = TakeSv(std::string()); // expected-warning {{object backing}} | ||
return TakeSv(std::string()); // expected-warning {{returning address}} | ||
|
||
std::string_view t3 = TakeStrRef(std::string()); // expected-warning {{temporary}} | ||
t3 = TakeStrRef(std::string()); // expected-warning {{object backing}} | ||
return TakeStrRef(std::string()); // expected-warning {{returning address}} | ||
|
||
|
||
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. nit: delete extra new line |
||
std::string_view t4 = TakeStr(std::string()); | ||
t4 = TakeStr(std::string()); | ||
return TakeStr(std::string()); | ||
} | ||
|
||
template <typename T> | ||
struct Foo { | ||
const T& get() const [[clang::lifetimebound]]; | ||
const T& getNoLB() const; | ||
}; | ||
std::string_view test2(Foo<std::string> r1, Foo<std::string_view> r2) { | ||
std::string_view t1 = Foo<std::string>().get(); // expected-warning {{object backing}} | ||
t1 = Foo<std::string>().get(); // expected-warning {{object backing}} | ||
return r1.get(); // expected-warning {{address of stack}} | ||
|
||
std::string_view t2 = Foo<std::string_view>().get(); | ||
t2 = Foo<std::string_view>().get(); | ||
return r2.get(); | ||
|
||
// no warning on no-LB-annotated method. | ||
std::string_view t3 = Foo<std::string>().getNoLB(); | ||
t3 = Foo<std::string>().getNoLB(); | ||
return r1.getNoLB(); | ||
} | ||
|
||
struct Bar {}; | ||
struct [[gsl::Pointer]] Pointer { | ||
Pointer(const Bar & bar [[clang::lifetimebound]]); | ||
}; | ||
Pointer test3(Bar bar) { | ||
Pointer p = Pointer(Bar()); // expected-warning {{temporary}} | ||
p = Pointer(Bar()); // expected-warning {{object backing}} | ||
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 was a false negative before, this patch also fixes it. |
||
return bar; // expected-warning {{address of stack}} | ||
} | ||
|
||
template<typename T> | ||
struct MySpan { | ||
MySpan(const std::vector<T>& v); | ||
using iterator = std::iterator<T>; | ||
iterator begin() const [[clang::lifetimebound]]; | ||
}; | ||
template <typename T> | ||
typename MySpan<T>::iterator ReturnFirstIt(const MySpan<T>& v [[clang::lifetimebound]]); | ||
|
||
void test4() { | ||
std::vector<int> v{1}; | ||
// MySpan<T> doesn't own any underlying T objects, the pointee object of | ||
// the MySpan iterator is still alive when the whole span is destroyed, thus | ||
// no diagnostic. | ||
const int& t1 = *MySpan<int>(v).begin(); | ||
Comment on lines
+755
to
+769
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. I have mixed feelings about this. For example, this silences the warning on: iterator getBeginOff(const MySpan<int>& ms [[clang::lifetimebound]]);
const int& t1 = *getBeginOf(MySpan<int>(v)); (Can you also add this test case?) 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. We have this test case already, see the 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. Thanks. |
||
const int& t2 = *ReturnFirstIt(MySpan<int>(v)); | ||
// Ideally, we would diagnose the following case, but due to implementation | ||
// constraints, we do not. | ||
const int& t4 = *MySpan<int>(std::vector<int>{}).begin(); | ||
|
||
auto it1 = MySpan<int>(v).begin(); // expected-warning {{temporary whose address is use}} | ||
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. Do you think this is correct behaviour ? 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. Yes, this behavior is expected per the
In this case, whether 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. Hmm. Well if both iterator and MySpan are pointers then we could make it not warn. But I agree that this is expanding the scope of lifetimebound definition to places where it is not possible. Lifetimes of internal subobjects probably has a better solution with rust-like lifetimes/type system. |
||
auto it2 = ReturnFirstIt(MySpan<int>(v)); // expected-warning {{temporary whose address is used}} | ||
} | ||
|
||
} // namespace LifetimeboundInterleave |
Uh oh!
There was an error while loading. Please reload this page.