[clang] [llvm] [clang] Implement lifetime analysis for lifetime_capture_by(X) (PR #115921)

Haojian Wu via cfe-commits cfe-commits at lists.llvm.org
Mon Nov 18 05:31:58 PST 2024


================
@@ -3922,16 +3922,41 @@ def LifetimeCaptureByDocs : Documentation {
   let Category = DocCatFunction;
   let Content = [{
     Similar to `lifetimebound`_, the ``lifetime_capture_by(X)`` attribute on a function
-parameter or implicit object parameter indicates that that objects that are referred to
-by that parameter may also be referred to by the capturing entity ``X``.
+parameter or implicit object parameter indicates that objects that are referred to by
+that parameter may also be referred to by a capturing entity ``X``.
+
+Below is a list of types of the parameters and what they're considered to refer to:
+
+- A reference param is considered to refer to its referenced object.
+- A pointer param is considered to refer to its pointee.
+- A ``std::initializer_list<T>`` is considered to refer to its underlying array.
+- Aggregates (arrays and simple ``struct``\s) are considered to refer to all
----------------
hokein wrote:

> Added a captureVector case. Let me know if you wanted something else.

Thanks. Sorry for not being clear earilier -- I was actually thinking of examples involving raw arrays and initializer lists.

For raw arrays, an example using `lifetimebound` might look like this:

```cpp
int* k(int array [[clang::lifetimebound]] [2]);

int* test() {
   int array[2]; 
   return k(array); // Dangling: returns a pointer to a stack address
}
```

However, it doesn’t seem feasible to use `lifetimebound_capture_by` for raw arrays because there isn't a way to generate a temporary array within a function call statement.

For initializer lists, the idea is something like:

```cpp
void k(std::initializer_list<int> abc [[clang::lifetime_capture_by(x)]], Set& x);

void test() {
   k({1, 2}, x); // Warning: the initializer list might outlive the captured storage
}
```

> I’m planning to add an error check in a follow-up to ensure that the parameter must be a `gsl::Pointer` or a reference type. What are your thoughts on this?

For simple structs, I think the current implementation still works like:

```cpp
struct Foo {
   const int& b;
};

int* test(Foo param [[clang::lifetimebound]]);

void capture(Foo param [[clang::lifetime_capture_by(x)]], std::set<Foo>& x);

void example() {
   int* b = test(Foo{1}); // Dangling: reference to a temporary constant object
   capture(Foo{1}, x);    // Warning
}
```



https://github.com/llvm/llvm-project/pull/115921


More information about the cfe-commits mailing list