[libcxx-commits] [PATCH] D154116: [libc++] Implement LWG3938 (Cannot use std::expected monadic ops with move-only error_type)

Yurong via Phabricator via libcxx-commits libcxx-commits at lists.llvm.org
Wed Jul 5 10:06:27 PDT 2023


yronglin marked 2 inline comments as done.
yronglin added a comment.

Thanks for your review! @Mordante



================
Comment at: libcxx/docs/ReleaseNotes.rst:50
 - P2572R1 - ``std::format`` fill character allowances
+- LWG3938 - Cannot use ``std::expected`` monadic ops with move-only error_type
 
----------------
Mordante wrote:
> We typically don't mention LWG issues in our release notes, unless they have a huge impact.
Thanks for your tips, I'll remove that.


================
Comment at: libcxx/include/__expected/expected.h:694
   template <class _Func>
     requires is_constructible_v<_Tp, _Tp&>
   _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) & {
----------------
Mordante wrote:
> Can you update this to the Standard wording too? the same for the other places.
I tried to keep the same with the std wording, but it compile failed:
```
error: invalid use of 'this' outside of a non-static member function
  694 |     requires is_constructible_v<_Tp, decltype(**this)>
      |                                                 ^
```


================
Comment at: libcxx/include/__expected/expected.h:694
   template <class _Func>
     requires is_constructible_v<_Tp, _Tp&>
   _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) & {
----------------
yronglin wrote:
> Mordante wrote:
> > Can you update this to the Standard wording too? the same for the other places.
> I tried to keep the same with the std wording, but it compile failed:
> ```
> error: invalid use of 'this' outside of a non-static member function
>   694 |     requires is_constructible_v<_Tp, decltype(**this)>
>       |                                                 ^
> ```
As we talked in discord:

1. `*this` always produces an LValue

```
  template <class _Func>
    requires is_constructible_v<_Err, const _Err&&>
  _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& {
    static_assert(std::is_same_v<decltype(*this), const std::expected<_Tp, _Err>&&>);
    static_assert(std::is_same_v<decltype(**this), const _Tp&&>);
    using _Up = remove_cvref_t<invoke_result_t<_Func, decltype(**this)>>;
    static_assert(
        __is_std_expected<_Up>::value, "The result of f(std::move(value())) must be a specialization of std::expected");
    static_assert(is_same_v<typename _Up::error_type, _Err>,
                  "The result of f(std::move(value())) must have the same error_type as this expected");
    if (has_value()) {
      return std::invoke(std::forward<_Func>(__f), std::move(**this));
    }
    return _Up(unexpect, std::move(error()));
  }
```
the two `static_assert` fails:
```
 static_assert(std::is_same_v<decltype(*this), const std::expected<_Tp, _Err>&&>);
 static_assert(std::is_same_v<decltype(**this), const _Tp&&>);
```
when `and_then` called by:
```
#include <expected>
#include <cassert>

struct UnexpectedCRVal {
  std::expected<int, int> operator()(int&)       = delete;
  std::expected<int, int> operator()(const int&) = delete;
  std::expected<int, int> operator()(int&&)      = delete;
  constexpr std::expected<int, int> operator()(const int&&) { return std::expected<int, int>(std::unexpected<int>(5)); }
};

int main() {
    const std::expected<int, int> e{0};
    assert(std::move(e).and_then(UnexpectedCRVal{}).error() == 5);
    return 0;
}
```

The reason is that: `*this` always produces an Lvalue http://eel.is/c++draft/expr.unary.op#1.sentence-3 , so the type of `decltype(**this)` is `const _Tp &` but not `const _Tp &&` 

2. we can use no-static member function in constraints

std wording: `Constraints: is_constructible_v<E, decltype(std​::​move(error()))> is true.`
we can't use `decltype(std​::​move(error()))` outside a non-static member function, and we use `const _Err&&` as a workaround before.
Also `is_constructible_v<decltype(**this)>` in constraints has the same issue:
```
error: invalid use of 'this' outside of a non-static member function
  694 |     requires is_constructible_v<_Tp, decltype(**this)>
          |      
```


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D154116/new/

https://reviews.llvm.org/D154116



More information about the libcxx-commits mailing list