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

via cfe-commits cfe-commits at lists.llvm.org
Mon Feb 3 09:40:47 PST 2025


higher-performance wrote:

I think we should heavily consider the wider solution space before going with this proposal. The problem we will quickly hit here is that we need to be sure that whatever we want can actually be expressed as a condition in the cases we want. That's not currently the case, which is going to pose problems.

For example, we'll want to be able to handle this case (let's call this the type-erased case):

```
std::map<std::string_view, std::string_view> m;
auto& value = m[std::string()];  // Gets caught
auto&& view = std::string_view(std::string());  // Gets caught
auto& value = m[std::string_view(std::string())];  // Doesn't get caught
```

As well as this case (call this the variadic case):

```
template<class... Args>
reference emplace_back(Args&&... args);
```

(I feel there are more interesting cases here -- these are just the two off the top of my head.)

To handle such cases we'd need to provide a way to **get** lifetimes as well, not merely **set** the attributes. Which means for `lifetimebound` we'll need something like `[[lifetimebound_if(__builtin_is_lifetimebound(arg))]]` as well. For `lifetime_capture_by`, I'm not clear right now if we'd ever need to detect it, but if we do then that would be a case we'd need to handle as well.

Given the above, the solution I would suggest considering here is the following:

What we ultimately _want_ is to be able to specify things in terms of **expressions**, I think.

That suggests we want should consider syntax like this:

```
template<class... Args>
reference emplace_back(Args&&... args [[clang::lifetimebound(__builtin_is_lifetimebound(*new T(std::forward<Args>(args)...)))]]);
```

I think we should consider something along the lines of the above.

An interesting observation here, though, is that we can avoid code duplication even more if we just make the inference based on existing `noexcept` conditions:

```
template<class... Args>
reference emplace_back(Args&&... args) noexcept(noexcept(*new T(std::forward<Args>(args)...)));
```

The main problem I see here is that the `noexcept` condition might not actually match the lifetime condition, because, for example, the specification might have failed to specify it that way.

There's a hack we can use to handle such cases:

```
template<class... Args>
reference emplace_back(Args&&... args) noexcept(false && noexcept(*new T(std::forward<Args>(args)...)));
```

The compiler would then detect the ANDing with `false`, and view it as a signal that the other `noexcept` expressions should be utilized as an "alternate body" for the function for code diagnostic/analysis purposes. A secondary benefit of this is that we also avoid code duplication with functions that have `noexcept`.

The proposal here would need some baking (e.g., perhaps it should be `noexcept(false && [[clang::lifetimebound]] noexcept(...))`? I don't know), but I think we should consider alternate directions like this, because they're the only ways I see to (a) handle the other cases we care about, while (b) avoiding a ton of duplicated code.

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


More information about the cfe-commits mailing list