[clang] [clang] Implement lifetime analysis for lifetime_capture_by(X) (PR #115921)
Boaz Brickner via cfe-commits
cfe-commits at lists.llvm.org
Fri Nov 15 06:09:52 PST 2024
================
@@ -0,0 +1,220 @@
+// RUN: %clang_cc1 --std=c++20 -fsyntax-only -Wdangling -Wdangling-field -Wreturn-stack-address -verify %s
+
+#include "Inputs/lifetime-analysis.h"
+
+struct X {
+ const int *x;
+ void captureInt(const int& x [[clang::lifetime_capture_by(this)]]) { this->x = &x; }
+ void captureSV(std::string_view sv [[clang::lifetime_capture_by(this)]]);
+};
+///////////////////////////
+// Detect dangling cases.
+///////////////////////////
+void captureInt(const int &i [[clang::lifetime_capture_by(x)]], X &x);
+void captureRValInt(int &&i [[clang::lifetime_capture_by(x)]], X &x);
+void noCaptureInt(int i [[clang::lifetime_capture_by(x)]], X &x);
+
+std::string_view substr(const std::string& s [[clang::lifetimebound]]);
+std::string_view strcopy(const std::string& s);
+
+void captureSV(std::string_view s [[clang::lifetime_capture_by(x)]], X &x);
+void captureRValSV(std::string_view &&sv [[clang::lifetime_capture_by(x)]], X &x);
+void noCaptureSV(std::string_view sv, X &x);
+void captureS(const std::string &s [[clang::lifetime_capture_by(x)]], X &x);
+void captureRValS(std::string &&s [[clang::lifetime_capture_by(x)]], X &x);
+
+const std::string& getLB(const std::string &s [[clang::lifetimebound]]);
+const std::string& getLB(std::string_view sv [[clang::lifetimebound]]);
+const std::string* getPointerLB(const std::string &s [[clang::lifetimebound]]);
+const std::string* getPointerNoLB(const std::string &s);
+
+void capturePointer(const std::string* sp [[clang::lifetime_capture_by(x)]], X &x);
+
+struct ThisIsCaptured {
+ void capture(X &x) [[clang::lifetime_capture_by(x)]];
+};
+
+void captureByGlobal(std::string_view s [[clang::lifetime_capture_by(global)]]);
+void captureByUnknown(std::string_view s [[clang::lifetime_capture_by(unknown)]]);
+
+void use() {
+ std::string_view local_sv;
+ std::string local_s;
+ X x;
+ // Capture an 'int'.
+ int local;
+ captureInt(1, // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
+ x);
+ captureRValInt(1, x); // expected-warning {{object whose reference is captured by 'x'}}
+ captureInt(local, x);
+ noCaptureInt(1, x);
+ noCaptureInt(local, x);
+
+ // Capture using std::string_view.
+ captureSV(local_sv, x);
+ captureSV(std::string(), // expected-warning {{object whose reference is captured by 'x'}}
+ x);
+ captureSV(substr(
+ std::string() // expected-warning {{object whose reference is captured by 'x'}}
+ ), x);
+ captureSV(substr(local_s), x);
+ captureSV(strcopy(std::string()), x);
+ captureRValSV(std::move(local_sv), x);
+ captureRValSV(std::string(), x); // expected-warning {{object whose reference is captured by 'x'}}
+ captureRValSV(std::string_view{"abcd"}, x);
+ captureRValSV(substr(local_s), x);
+ captureRValSV(substr(std::string()), x); // expected-warning {{object whose reference is captured by 'x'}}
+ captureRValSV(strcopy(std::string()), x);
+ noCaptureSV(local_sv, x);
+ noCaptureSV(std::string(), x);
+ noCaptureSV(substr(std::string()), x);
+
+ // Capture using std::string.
+ captureS(std::string(), x); // expected-warning {{object whose reference is captured by 'x'}}
+ captureS(local_s, x);
+ captureRValS(std::move(local_s), x);
+ captureRValS(std::string(), x); // expected-warning {{object whose reference is captured by 'x'}}
+
+ // Capture with lifetimebound.
+ captureSV(getLB(std::string()), x); // expected-warning {{object whose reference is captured by 'x'}}
+ captureSV(getLB(substr(std::string())), x); // expected-warning {{object whose reference is captured by 'x'}}
+ captureSV(getLB(getLB(
+ std::string() // expected-warning {{object whose reference is captured by 'x'}}
+ )), x);
+ capturePointer(getPointerLB(std::string()), x); // expected-warning {{object whose reference is captured by 'x'}}
+ capturePointer(getPointerLB(*getPointerLB(
+ std::string() // expected-warning {{object whose reference is captured by 'x'}}
+ )), x);
+ capturePointer(getPointerNoLB(std::string()), x);
+
+ // Member functions.
+ x.captureInt(1); // expected-warning {{object whose reference is captured by 'x'}}
+ x.captureSV(std::string()); // expected-warning {{object whose reference is captured by 'x'}}
+ x.captureSV(substr(std::string())); // expected-warning {{object whose reference is captured by 'x'}}
+ x.captureSV(strcopy(std::string()));
+
+ // 'this' is captured.
+ ThisIsCaptured{}.capture(x); // expected-warning {{object whose reference is captured by 'x'}}
+ ThisIsCaptured TIS;
+ TIS.capture(x);
+
+ // capture by global.
+ captureByGlobal(std::string()); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}}
+ captureByGlobal(substr(std::string())); // expected-warning {{captured}}
+ captureByGlobal(local_s);
+ captureByGlobal(local_sv);
+
+ // // capture by unknown.
+ captureByGlobal(std::string()); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}}
+ captureByGlobal(substr(std::string())); // expected-warning {{captured}}
+ captureByGlobal(local_s);
+ captureByGlobal(local_sv);
+}
+
+template<typename T> struct IsPointerLikeTypeImpl : std::false_type {};
+template<> struct IsPointerLikeTypeImpl<std::string_view> : std::true_type {};
+template<typename T> concept IsPointerLikeType = std::is_pointer<T>::value || IsPointerLikeTypeImpl<T>::value;
+
+// Templated containers having no distinction between pointer-like and other element type.
+template<class T>
+struct MySet {
+ void insert(T&& t [[clang::lifetime_capture_by(this)]]);
+ void insert(const T& t [[clang::lifetime_capture_by(this)]]);
+};
+void user_defined_containers() {
+ MySet<int> set_of_int;
+ set_of_int.insert(1); // expected-warning {{object whose reference is captured by 'set_of_int' will be destroyed}}
+ MySet<std::string_view> set_of_sv;
+ set_of_sv.insert(std::string()); // expected-warning {{object whose reference is captured by 'set_of_sv' will be destroyed}}
+}
+
+// Templated containers having **which distinguishes** between pointer-like and other element type.
+template<class T>
+struct MyVector {
+ void push_back(T&& t [[clang::lifetime_capture_by(this)]]) requires IsPointerLikeType<T>;
+ void push_back(const T& t [[clang::lifetime_capture_by(this)]]) requires IsPointerLikeType<T>;
+
+ void push_back(T&& t) requires (!IsPointerLikeType<T>);
+ void push_back(const T& t) requires (!IsPointerLikeType<T>);
+};
+
+// Container of pointers.
+struct [[gsl::Pointer()]] MyStringView : public std::string_view {
+ MyStringView();
+ MyStringView(std::string_view&&);
+ MyStringView(const MyStringView&);
+ MyStringView(const std::string&);
+};
+template<> struct IsPointerLikeTypeImpl<MyStringView> : std::true_type {};
+
+std::optional<std::string_view> getOptionalSV();
+std::optional<std::string> getOptionalS();
+std::optional<MyStringView> getOptionalMySV();
+MyStringView getMySV();
+
+class MyStringViewNotPointer : public std::string_view {};
+std::optional<MyStringViewNotPointer> getOptionalMySVNotP();
+MyStringViewNotPointer getMySVNotP();
+
+void container_of_pointers() {
+ std::string local;
+ MyVector<std::string> vs;
----------------
bricknerb wrote:
Can you rename these variables with easier to follow ones?
Avoid abbreviations when it's not trivial.
https://github.com/llvm/llvm-project/pull/115921
More information about the cfe-commits
mailing list