-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[libc++] Avoid using **this in error messages for expected monadic operations #84840
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
Conversation
@llvm/pr-subscribers-libcxx Author: None (ZERO-N) ChangesFull diff: https://github.com/llvm/llvm-project/pull/84840.diff 3 Files Affected:
diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index 58e995809777c1..77360242781d87 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -34,7 +34,7 @@
"`3957 <https://wg21.link/LWG3957>`__","[container.alloc.reqmts] The value category of v should be claimed","Kona November 2023","","",""
"`3965 <https://wg21.link/LWG3965>`__","Incorrect example in [format.string.escaped] p3 for formatting of combining characters","Kona November 2023","","","|format|"
"`3970 <https://wg21.link/LWG3970>`__","[mdspan.syn] Missing definition of ``full_extent_t`` and ``full_extent``","Kona November 2023","","",""
-"`3973 <https://wg21.link/LWG3973>`__","Monadic operations should be ADL-proof","Kona November 2023","","",""
+"`3973 <https://wg21.link/LWG3973>`__","Monadic operations should be ADL-proof","Kona November 2023","|Complete|","19.0",""
"`3974 <https://wg21.link/LWG3974>`__","``mdspan::operator[]`` should not copy ``OtherIndexTypes``","Kona November 2023","","",""
"`3987 <https://wg21.link/LWG3987>`__","Including ``<flat_foo>`` doesn't provide ``std::begin``/``end``","Kona November 2023","","","|flat_containers|"
"`3990 <https://wg21.link/LWG3990>`__","Program-defined specializations of ``std::tuple`` and ``std::variant`` can't be properly supported","Kona November 2023","","",""
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 443d9257dc598d..fb52aff7b6a578 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -920,9 +920,9 @@ class expected : private __expected_base<_Tp, _Err> {
requires is_constructible_v<_Err, _Err&>
_LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & {
using _Up = remove_cvref_t<invoke_result_t<_Func, _Tp&>>;
- static_assert(__is_std_expected<_Up>::value, "The result of f(**this) must be a specialization of std::expected");
+ static_assert(__is_std_expected<_Up>::value, "The result of f(value()) must be a specialization of std::expected");
static_assert(is_same_v<typename _Up::error_type, _Err>,
- "The result of f(**this) must have the same error_type as this expected");
+ "The result of f(value()) must have the same error_type as this expected");
if (has_value()) {
return std::invoke(std::forward<_Func>(__f), this->__val());
}
@@ -933,9 +933,9 @@ class expected : private __expected_base<_Tp, _Err> {
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, const _Tp&>>;
- static_assert(__is_std_expected<_Up>::value, "The result of f(**this) must be a specialization of std::expected");
+ static_assert(__is_std_expected<_Up>::value, "The result of f(value()) must be a specialization of std::expected");
static_assert(is_same_v<typename _Up::error_type, _Err>,
- "The result of f(**this) must have the same error_type as this expected");
+ "The result of f(value()) must have the same error_type as this expected");
if (has_value()) {
return std::invoke(std::forward<_Func>(__f), this->__val());
}
@@ -947,9 +947,9 @@ class expected : private __expected_base<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && {
using _Up = remove_cvref_t<invoke_result_t<_Func, _Tp&&>>;
static_assert(
- __is_std_expected<_Up>::value, "The result of f(std::move(**this)) must be a specialization of std::expected");
+ __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(**this)) must have the same error_type as this expected");
+ "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->__val()));
}
@@ -961,9 +961,9 @@ class expected : private __expected_base<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& {
using _Up = remove_cvref_t<invoke_result_t<_Func, const _Tp&&>>;
static_assert(
- __is_std_expected<_Up>::value, "The result of f(std::move(**this)) must be a specialization of std::expected");
+ __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(**this)) must have the same error_type as this expected");
+ "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->__val()));
}
diff --git a/libcxx/test/libcxx/utilities/expected/expected.expected/and_then.mandates.verify.cpp b/libcxx/test/libcxx/utilities/expected/expected.expected/and_then.mandates.verify.cpp
index 8b3138fd73db31..c46ab633295c1a 100644
--- a/libcxx/test/libcxx/utilities/expected/expected.expected/and_then.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/expected/expected.expected/and_then.mandates.verify.cpp
@@ -11,22 +11,22 @@
// Test the mandates
// template<class F> constexpr auto and_then(F&& f) &;
// Mandates:
-// Let U be std::remove_cvref_t<std::invoke_result<F, decltype(**this)>>
+// Let U be std::remove_cvref_t<std::invoke_result<F, decltype(value())>>
// U is a specialization of std::expected and std::is_same_v<U:error_type, E> is true
// template<class F> constexpr auto and_then(F&& f) const &;
// Mandates:
-// Let U be std::remove_cvref_t<std::invoke_result<F, decltype(**this)>>
+// Let U be std::remove_cvref_t<std::invoke_result<F, decltype(value())>>
// U is a specialization of std::expected and std::is_same_v<U:error_type, E> is true
// template<class F> constexpr auto and_then(F&& f) &&;
// Mandates:
-// Let U be std::remove_cvref_t<std::invoke_result<F, decltype(**this)>>
+// Let U be std::remove_cvref_t<std::invoke_result<F, decltype(value())>>
// U is a specialization of std::expected and std::is_same_v<U:error_type, E> is true
// template<class F> constexpr auto and_then(F&& f) const &&;
// Mandates:
-// Let U be std::remove_cvref_t<std::invoke_result<F, decltype(**this)>>
+// Let U be std::remove_cvref_t<std::invoke_result<F, decltype(value())>>
// U is a specialization of std::expected and std::is_same_v<U:error_type, E> is true
#include <expected>
@@ -52,7 +52,7 @@ void test() {
{
std::expected<int, int> f1(1);
f1.and_then(lval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected<int, int>::and_then<int (&)(int &)>' requested here}}
- // expected-error-re@*:* {{static assertion failed {{.*}}The result of f(**this) must be a specialization of std::expected}}
+ // expected-error-re@*:* {{static assertion failed {{.*}}The result of f(value()) must be a specialization of std::expected}}
// expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}}
// expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}}
}
@@ -61,7 +61,7 @@ void test() {
{
std::expected<int, int> f1(1);
f1.and_then(lval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected<int, int>::and_then<std::expected<int, NotSameAsInt> (&)(int &)>' requested here}}
- // expected-error-re@*:* {{static assertion failed {{.*}}The result of f(**this) must have the same error_type as this expected}}
+ // expected-error-re@*:* {{static assertion failed {{.*}}The result of f(value()) must have the same error_type as this expected}}
}
}
@@ -71,7 +71,7 @@ void test() {
{
const std::expected<int, int> f1(1);
f1.and_then(clval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected<int, int>::and_then<int (&)(const int &)>' requested here}}
- // expected-error-re@*:* {{static assertion failed {{.*}}The result of f(**this) must be a specialization of std::expected}}
+ // expected-error-re@*:* {{static assertion failed {{.*}}The result of f(value()) must be a specialization of std::expected}}
// expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}}
// expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}}
}
@@ -80,7 +80,7 @@ void test() {
{
const std::expected<int, int> f1(1);
f1.and_then(clval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected<int, int>::and_then<std::expected<int, NotSameAsInt> (&)(const int &)>' requested here}}
- // expected-error-re@*:* {{static assertion failed {{.*}}The result of f(**this) must have the same error_type as this expected}}
+ // expected-error-re@*:* {{static assertion failed {{.*}}The result of f(value()) must have the same error_type as this expected}}
}
}
@@ -91,7 +91,7 @@ void test() {
{
std::expected<int, int> f1(1);
std::move(f1).and_then(rval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected<int, int>::and_then<int (&)(int &&)>' requested here}}
- // expected-error-re@*:* {{static assertion failed {{.*}}The result of f(std::move(**this)) must be a specialization of std::expected}}
+ // expected-error-re@*:* {{static assertion failed {{.*}}The result of f(std::move(value())) must be a specialization of std::expected}}
// expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}}
// expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}}
}
@@ -100,7 +100,7 @@ void test() {
{
std::expected<int, int> f1(1);
std::move(f1).and_then(rval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected<int, int>::and_then<std::expected<int, NotSameAsInt> (&)(int &&)>' requested here}}
- // expected-error-re@*:* {{static assertion failed {{.*}}The result of f(std::move(**this)) must have the same error_type as this expected}}
+ // expected-error-re@*:* {{static assertion failed {{.*}}The result of f(std::move(value())) must have the same error_type as this expected}}
}
}
@@ -110,7 +110,7 @@ void test() {
{
const std::expected<int, int> f1(1);
std::move(f1).and_then(crval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected<int, int>::and_then<int (&)(const int &&)>' requested here}}
- // expected-error-re@*:* {{static assertion failed {{.*}}The result of f(std::move(**this)) must be a specialization of std::expected}}
+ // expected-error-re@*:* {{static assertion failed {{.*}}The result of f(std::move(value())) must be a specialization of std::expected}}
// expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}}
// expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}}
}
@@ -119,7 +119,7 @@ void test() {
{
const std::expected<int, int> f1(1);
std::move(f1).and_then(crval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected<int, int>::and_then<std::expected<int, NotSameAsInt> (&)(const int &&)>' requested here}}
- // expected-error-re@*:* {{static assertion failed {{.*}}The result of f(std::move(**this)) must have the same error_type as this expected}}
+ // expected-error-re@*:* {{static assertion failed {{.*}}The result of f(std::move(value())) must have the same error_type as this expected}}
}
}
}
|
The title seems very misleading here. Also, while I think that the changes to the messages are an improvement, they aren't in any way related to implementing some feature. Please separate these changes. |
@frederick-vs-ja @philnik777 sorry for the misleading commit msg. |
No, it isn't, and has never been so in libc++. It should be clarified that only error messages are improved. |
@@ -920,9 +920,9 @@ class expected : private __expected_base<_Tp, _Err> { | |||
requires is_constructible_v<_Err, _Err&> | |||
_LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & { | |||
using _Up = remove_cvref_t<invoke_result_t<_Func, _Tp&>>; | |||
static_assert(__is_std_expected<_Up>::value, "The result of f(**this) must be a specialization of std::expected"); | |||
static_assert(__is_std_expected<_Up>::value, "The result of f(value()) must be a specialization of std::expected"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may be a bit misleading as value()
can be ill-formed (because the error type is not copyable) while monadic operations being well-formed. See also LWG3938 and LWG3940.
The resolution of LWG3973 uses val
(exposition-only member name). Perhaps we can use val
or __val()
(libc++-specific function name) in the error messages instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think so. Since https://reviews.llvm.org/D154116 we have addressed LWG3973's std::expected part(It seems like we never use **this
). This PR improves static_assert message. __val
seems more closely to the LWG wording. Using __val()
may requires the user to understand implementation details(e.g. __expected_base). @philnik777 Do you have any suggestions for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could just say something like "The result type of the functor must be a specialization of std::expected"? Most people will probably just have a lambda for it anyways. @cjdb might also have thoughts on how to word this.
Rebased and used |
Instead of using **this in error messages for std::expected monadic operations, use
value(). As shown in LWG3969, **this can trigger unintended ADL and while it's only
an error message, we might as well be ADL-correct there too.