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

Ilya Biryukov via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 4 08:52:06 PST 2025


ilya-biryukov wrote:

> w.r.t. ABI, I think the end state here would conceptually be most likely be similar to __attribute__((diagnose_if(...))). Does that affect ABI?

I believe it does not as it only produces warnings. However, having an way to query the presence of attributes in the code would allow for things like overloading or template specializations based on lifetimes. Passing information to compiler from code seems fine, it's passing the information from compiler back to the code that gives me a pause:
```cpp
template <bool IsLifetimebound>
struct Foo {
  static void call() {
    if (IsLifetimebound)
      std::cerr << "I am being called from a function that passes temporaries\n";
  } 

  using type = std::conditional_t<IsLifetimebound, int, double>;
};

template <class T>
struct SomeClass {

  void some_method(const T& a [[clang::lifetimebound]]) {
    // Results of the program below in Clang and GCC are different.
    // Not sure if C++ standard is okay with this.
    using MyFoo = Foo<__is_lifetimebound(a)>;
    MyFoo::call();
    std::vector<MyFoo::type> vec;
    // ...
  } 
};
```

There are other ways to achieve that and there are attributes that may change behavior of the program and its meaning, so maybe some folks feel it is acceptable. I feel this is probably a bad idea and we should stick to the principle that attributes like lifetimebound can be safety removed without changing runtime semantics of the program.


> w.r.t. False positives due to annotating the entire pack -- that's a great point. I think it's avoided by the noexcept approach. Any thoughts on that?

Noexcept and lifetimebound are quite orthogonal, so I am a little unsure about the particular proposal as is. However, I am also for avoiding duplication if possible. And given that the template function bodies have to be available anyway, we could maybe even utilize them (for function that don't have `noexcept`). I am thinking about some combination of two attributes (this gets complex really quickly, as you can see, looking for better ideas):

```cpp

// Can be taken from signature or body, e.g. from noexcept.
template<class ...Args>
void emplace_back(Args &&...args) [[clang::infered_fwd_lifetimebound_from_call]] noexcept(noexcept([[clang::infer_lifetimebound_from_here]] new T(std::forward(args)...))) {}

// Or from the body itself when there isn't noexcept
template<class ...Args>
void emplace_back(Args &&...args) [[clang::infered_lifetimebound_from_call]] {
  [[clang::infer_lifetimebound_from_here]] my_vector.push_back(T(std::forward(args)...)));
}

// Presumably we could also have some default inferences rules, e.g.
// - we infer lifetimebound from a single call either in noexcept or inside the body mentioning that argument.
// - if there's no such call or more than one mention of an argument, one has to mark the call with a special attribute.
// - std::forward/static_cast<T&&> is ignored somehow.
```


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


More information about the cfe-commits mailing list