[libcxx-commits] [libcxx] [libc++] Ensure that `std::expected` has no tail padding (PR #69673)
Jan Kokemüller via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Oct 23 22:44:39 PDT 2023
jiixyj wrote:
> Perhaps we should use a mixed strategy:
>
> * when the `bool` flag lives in the tail padding of the union, the repr strategy should be used, and `expected` shouldn't have reusable tail padding,
>
> * otherwise, however, we should make the tail padding of `expected` (if any) reusable.
>
>
> It's painful that we have no `[[no_unique_address(condition)]]` yet.
What do you think about this:
```c++
#include <expected>
#include <iostream>
#include <memory>
#include <optional>
template <typename Val, typename Err>
union expected_union {
[[no_unique_address]] Val val;
[[no_unique_address]] Err unex;
};
template <typename Val, typename Err, bool StuffTail = false>
struct expected_repr {
private:
expected_union<Val, Err> union_;
[[no_unique_address]] bool has_val_;
};
template <typename Val, typename Err>
struct expected_repr<Val, Err, true> {
private:
[[no_unique_address]] expected_union<Val, Err> union_;
[[no_unique_address]] bool has_val_;
};
template <typename Val, typename Err, //
typename Repr = expected_repr<Val, Err, true>,
typename Union = expected_union<Val, Err>>
concept tail_stuffable = sizeof(Repr) == sizeof(Union);
template <typename Val, typename Err>
struct expected_base {
protected:
[[no_unique_address]] expected_repr<Val, Err, false> repr_;
};
template <typename Val, typename Err>
requires tail_stuffable<Val, Err>
struct expected_base<Val, Err> {
protected:
expected_repr<Val, Err, true> repr_;
};
template <typename Val, typename Err>
struct expected : private expected_base<Val, Err> {};
```
https://godbolt.org/z/5oj6TjTGd
You would have two `repr` types, depending on whether the bool flag can live in the union's tail padding or not, and switch between them with inheritance (sort of emulating conditional `[[no_unique_address]]`).
What I like about this is that you don't even need something like `std::__libcpp_datasize`. You can just ask the first `repr` type: "Can the `bool` be stuffed in your tail?"
In the "tail padding" case you would `std::destruct_at`/`std::construct_at` the whole `repr`, including the "has value" flag. In the "normal" case you would only `std::destruct_at`/`std::construct_at` the union and set the "has value" flag manually.
I'll try to implement this to see how bad it is regarding code complexity. But I'm optimistic!
https://github.com/llvm/llvm-project/pull/69673
More information about the libcxx-commits
mailing list