[clang] [WIP][clang]: Implement a conditional lifetimebound_if builtin. (PR #125520)

Haojian Wu via cfe-commits cfe-commits at lists.llvm.org
Fri Feb 21 07:28:02 PST 2025


hokein wrote:

Another thought for supporting the `emplace_back(Args...)` case (for STL only). 

The underlying implementation of the `emplace_back` relies on `std::allocator_traits::construct(Alloc& a, T* p, Args&&... args)`, so we could use the `lifetime_capture_by` annotation in the instantiated function (this annotation can be relatively easy to infer in clang).  

For example, consider an instantiated template (note that the function parameter should be an non-const rvalue reference):  

```cpp
construct(std::string_view* p, std::string&& arg [[clang::lifetime_capture_by(p)]]);
```  

Here, we annotate `arg` with `lifetime_capture_by(p)`, which should allow us to detect cases like:  

```cpp
construct(&view, std::string()); // Detects dangling pointer
```  

However, this approach doesn’t work for `emplace_back`, because in that case, we only see perfect forwarding in the function arguments (`construct(std::__to_address(__tx.__pos_), std::forward<_Args>(__args)...);`)

### Potential Extension:  

We could consider extending the analysis scope for non-const rvalue references. Specifically if a non-const rvalue reference parameter is annotated, we could always emit a warning when this function is being called. 

Example:  

```cpp
void add(std::vector<std::string_view>& container, std::string&& s [[clang::lifetime_capture_by]]);

void test() {  
    std::vector<std::string_view> abc;  
    add(abc, std::string());  // Case 1: Warning, point to a dead object  

    std::string b;  
    add(abc, std::move(b));   // Case 2: point to a moved-from object  

    add(abc, b); // invalid c++ code, cannot bind a lvalue to rvalue reference
}
```  

For non-const rvalue reference function parameters, there are only two legal options to pass the argument:  
1. A temporary object.  
2. A non-const object explicitly marked with `std::move()`.  

- **Case 1)** is a clear use-after-free issue, which is already detected by the current implementation.  
- **Case 2)** is a bit subtle. The moved-from object is still alive but in a valid-but-unspecified state. While it’s technically possible to use the object after a `std::move()`, the general programming guideline is to avoid doing so, as it could lead to potential use-after-move issues (we have a use-after-move clang-tidy check).

If we extend the analysis to warn on Case (2), we should be able to detect the `emplace_back` case. However, I’m not sure whether making the compiler stricter on this is a feasible idea.


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


More information about the cfe-commits mailing list