Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[expected.object.monadic][LWG 3938] Use **this may cause compile fail #6500

Closed
yronglin opened this issue Aug 22, 2023 · 8 comments
Closed

Comments

@yronglin
Copy link

yronglin commented Aug 22, 2023

I'm working on address LWG3938(https://wg21.link/LWG3938) in libc++, there maybe 2 issues in the wording:

  1. *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 &&
template <class _Func>
  requires is_constructible_v<_Err, const _Err&&>
_LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& {
  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()));
}

When and_then called in:

#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;
}

This may compile error, because and_then calls std::invoke(std::forward<_Func>(__f), std::move(**this));and std::invoke(std::forward<_Func>(__f), std::move(**this)); will call UnexpectedCRVal::operator()(const int&) , which is a deleted function.

  1. invalid use of 'this' outside of a non-static member function

The standard wording:

template<class F> constexpr auto or_else(F&& f) &;
template<class F> constexpr auto or_else(F&& f) const &;
...
Constraints: is_constructible_v<T, decltype(**this)> is true.
template<class F> constexpr auto transform_error(F&& f) &;
template<class F> constexpr auto transform_error(F&& f) const &;
...
Constraints: is_constructible_v<T, decltype(**this)> is true.
@jensmaurer
Copy link
Member

You are referring to CWG3938. This core issue does not exist (yet), we're at least 1000 core issues away from that. Maybe you meant to refer to LWG3938.

In any case, your concerns appear to be non-editorial in nature: If a type computation prescribed by the standard yields an incorrect type, that's not for the editors to fix. Please file an LWG issue with your concerns.

@yronglin
Copy link
Author

yronglin commented Aug 22, 2023

You are referring to CWG3938. This core issue does not exist (yet), we're at least 1000 core issues away from that. Maybe you meant to refer to LWG3938.

In any case, your concerns appear to be non-editorial in nature: If a type computation prescribed by the standard yields an incorrect type, that's not for the editors to fix. Please file an LWG issue with your concerns.

I'm so sorry, that is a typo, I'm referring to LWG3938, and thanks for your tips.

@yronglin yronglin changed the title [expected.object.monadic][CWG 3938] Use **this may cause compile fail [expected.object.monadic][LWG 3938] Use **this may cause compile fail Aug 22, 2023
@frederick-vs-ja
Copy link
Contributor

template <class _Func>
  requires is_constructible_v<_Err, const _Err&&>
_LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& {
  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()));
}

It seems to me that you detected a wrong type. _Up should be remove_cvref_t<invoke_result_t<_Func, decltype(std::move(**this))>> in this overload.
(Anyway, I think the status quo of type detection of these overloads in libc++ ((1), (2), (3), (4)) is fine. We should just change the message in static_assert.)

2. invalid use of 'this' outside of a non-static member function

The standard wording:

template<class F> constexpr auto or_else(F&& f) &;
template<class F> constexpr auto or_else(F&& f) const &;
...
Constraints: is_constructible_v<T, decltype(**this)> is true.
template<class F> constexpr auto transform_error(F&& f) &;
template<class F> constexpr auto transform_error(F&& f) const &;
...
Constraints: is_constructible_v<T, decltype(**this)> is true.

The wording is specifying non-static member functions, isn't it?

@JohelEGP
Copy link
Contributor

JohelEGP commented Aug 23, 2023

Yes.
But maybe they're trying to use a requires clause to implement that constraint.
It seems that you can use this in requires clauses that are trailing only: https://compiler-explorer.com/z/b6GPf41d4.

@frederick-vs-ja
Copy link
Contributor

But maybe they're trying to use a requires clause to implement that constraint.

I guess it's unnecessary to make Constraints conform to requires-clauses. In the standard wording, IMO it's better to specify conditions in Mandates and Constraints in the same contexts.

@jensmaurer
Copy link
Member

invalid use of 'this' outside of a non-static member function

This is not-a-defect. We're describing a non-static member function here, and this certainly has a this pointer. Maybe you can't implement those "constraints" the most obvious way, but that's your problem as an implementer, not a specification problem.

@jwakely
Copy link
Member

jwakely commented Aug 23, 2023

I'm working on address LWG3938(https://wg21.link/LWG3938) in libc++, there maybe 2 issues in the wording:

1. `*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 &&`

The wording clearly says

Let U be remove_cvref_t<invoke_result_t<F, decltype(std::move(**this))>>.

But you forgot the move when declaring _Up:

template <class _Func>
  requires is_constructible_v<_Err, const _Err&&>
_LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& {
  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()));
}

This is just an implementation bug.

2. invalid use of 'this' outside of a non-static member function

The standard wording:

template<class F> constexpr auto or_else(F&& f) &;
template<class F> constexpr auto or_else(F&& f) const &;
...
Constraints: is_constructible_v<T, decltype(**this)> is true.
template<class F> constexpr auto transform_error(F&& f) &;
template<class F> constexpr auto transform_error(F&& f) const &;
...
Constraints: is_constructible_v<T, decltype(**this)> is true.

This wording is fine too. If you use this outside the member function definition that's just another implementation bug.

Feel free to replace decltype(**this) with T& for the first overload and const T& for the second overload, you don't have to spell it the same way as the standard does.

@jwakely jwakely closed this as not planned Won't fix, can't repro, duplicate, stale Aug 23, 2023
@yronglin
Copy link
Author

Thanks for the clarification and advice, the first question was my mistake.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants