Skip to content

[libc++] mark std::expected as nodiscard #130820

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

Open
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

hulxv
Copy link
Member

@hulxv hulxv commented Mar 11, 2025

Fix #130656

@hulxv hulxv requested a review from a team as a code owner March 11, 2025 19:31
Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Mar 11, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 11, 2025

@llvm/pr-subscribers-libcxx

Author: Mohamed Emad (hulxv)

Changes

Fix #130656


Full diff: https://github.com/llvm/llvm-project/pull/130820.diff

2 Files Affected:

  • (modified) libcxx/include/__cxx03/__expected/expected.h (+1-1)
  • (modified) libcxx/include/__expected/expected.h (+1-1)
diff --git a/libcxx/include/__cxx03/__expected/expected.h b/libcxx/include/__cxx03/__expected/expected.h
index 1d54bb9f6edeb..ce9676c68c2dc 100644
--- a/libcxx/include/__cxx03/__expected/expected.h
+++ b/libcxx/include/__cxx03/__expected/expected.h
@@ -58,7 +58,7 @@ _LIBCPP_PUSH_MACROS
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Tp, class _Err>
-class expected;
+class [[nodiscrad]] expected;
 
 template <class _Tp>
 struct __is_std_expected : false_type {};
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 03bbd1623ed5c..8dd32668bcb33 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -60,7 +60,7 @@ _LIBCPP_PUSH_MACROS
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Tp, class _Err>
-class expected;
+class [[nodiscard]] expected;
 
 template <class _Tp>
 struct __is_std_expected : false_type {};

@mordante mordante self-assigned this Mar 11, 2025
Copy link
Member

@mordante mordante left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this!

This needs tests to verify that the nodiscard works as expected.
libcxx/test/std/containers/associative/multimap/empty.verify.cpp
contains an example test.

@hulxv
Copy link
Member Author

hulxv commented Mar 11, 2025

Thanks for working on this!

This needs tests to verify that the nodiscard works as expected. libcxx/test/std/containers/associative/multimap/empty.verify.cpp contains an example test.

Thanks for guidance! I will add it now

Copy link
Contributor

@frederick-vs-ja frederick-vs-ja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also add [[nodiscard]] to this partial specialization:

template <class _Tp, class _Err>
requires is_void_v<_Tp>
class expected<_Tp, _Err> : private __expected_void_base<_Err> {
static_assert(__valid_std_unexpected<_Err>::value,

Superseded

Per CI failures, we should add void cast to function calls in these files to suppress warnings:

  • libcxx/utilities/expected/expected.expected/and_then.mandates.verify.cpp
  • libcxx/utilities/expected/expected.expected/or_else.mandates.verify.cpp
  • libcxx/utilities/expected/expected.void/and_then.mandates.verify.cpp
  • libcxx/utilities/expected/expected.void/or_else.mandates.verify.cpp

@hulxv hulxv requested a review from frederick-vs-ja March 13, 2025 16:50
Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm conflicted about making this change -- I suspect this could end up being really verbose. Before we actually land this, can you please ping @llvm/libcxx-vendors to give folks a chance to run this change internally first? I'd like to try that out to get some signal.

@zmodem
Copy link
Collaborator

zmodem commented Mar 20, 2025

Chromium isn't on C++23 yet, so we're not using std::expected and I'm not really familiar with the class, but I did notice that our own "backport" is marked [[nodiscard]]: https://source.chromium.org/chromium/chromium/src/+/main:base/types/expected.h;l=273;drc=9596f8af68e7ad21da9bf94249e8a314edf09329

@hulxv hulxv requested a review from mordante March 20, 2025 14:52
@hulxv hulxv force-pushed the fix/expected-nodiscard branch from 57dc193 to a5f9e46 Compare March 20, 2025 17:23
@Mick235711
Copy link
Contributor

If we are doing this, should we add a reason for why std::expected is marked as [[nodiscard]]? I believe it would be more helpful if a reason is supplied in the warning message for users who encountered a warning.
Although libc++ doesn't seem to use [[nodiscard("reason")]] at all currently. Is there a policy for that internally?

@mordante
Copy link
Member

If we are doing this, should we add a reason for why std::expected is marked as [[nodiscard]]? I believe it would be more helpful if a reason is supplied in the warning message for users who encountered a warning. Although libc++ doesn't seem to use [[nodiscard("reason")]] at all currently. Is there a policy for that internally?

AFAIK we never considered to add a reason. That feature was added in C++20, while nodiscard itself if C++17.

I'm not against adding a reason. FYI @philnik777 proposed to add reasons to deprecated.

@hulxv
Copy link
Member Author

hulxv commented Mar 24, 2025

I'm conflicted about making this change -- I suspect this could end up being really verbose. Before we actually land this, can you please ping @llvm/libcxx-vendors to give folks a chance to run this change internally first? I'd like to try that out to get some signal.

I came from a Rust background and we have Result that does the same behaviour as std::expected, the compiler warning us directly if this result isn't handled or assigned to a variable

let res: Result<T,E> = result_return_result(); // no warning
result_return_result(); // WARNING!!!
result_return_result().unwrap(); // no warning. ".unwrap()" works as a handler and returns the expected value

So, I don't find it a verbose thing and it's normal from my point of view

@strega-nil-re
Copy link

It seems like it's pretty much always a bug to not check an expected. I certainly have my opinions from MS-STL ([[nodiscard]] all the things!) but I don't see why anyone would ever want to implicitly ignore an expected.

@R-Goc
Copy link
Contributor

R-Goc commented Mar 24, 2025

I also feel like implicitly ignoring an expected is always a bug. It would be nice to see a reason added to it.

@StephanTLavavej
Copy link
Member

A data point that may be of interest - MSVC's STL marked std::expected (and std::unexpected, and all exception types) as [[nodiscard]] with microsoft/STL#5174. There's some rationale discussion in the PR. This recently shipped in VS 2022 17.14 Preview 1 and so far we haven't gotten any negative feedback about a blizzard of warnings.

@philnik777
Copy link
Contributor

While I like the idea in general, I really dislike that there is no way for the user to say "it's fine to discard the expected from this function", so we force the user to either not use expected when it may be OK to discard, or add casts to void on every single call site. Neither option seems great.

@Mick235711
Copy link
Contributor

While I like the idea in general, I really dislike that there is no way for the user to say "it's fine to discard the expected from this function", so we force the user to either not use expected when it may be OK to discard, or add casts to void on every single call site. Neither option seems great.

Well, in C++26, users will be able to do

std::ignore = some_func_returning_expected();

to signify that it is OK to discard an std::expected return, which seems much more readable than casting to void. (libc++ had already implemented this change to std::ignore)

Whether this is an acceptable solution to force on users is up for debate, though.

@hulxv hulxv requested a review from ldionne March 26, 2025 16:56
Copy link

github-actions bot commented Mar 26, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@mordante
Copy link
Member

@hulxv can you please fix the CI before requesting reviews again? If you need help with the CI issues feel free to reach out on Discord. I tend not to look at patches when there are a lot of CI issues.

@huixie90
Copy link
Member

Is there any paper adding ‘[[candiscard]]’ floating around ? I’d much prefer to making it possible for the function owner to make the function possible to discard the returned value, before we adding the blanket nodiscard for everyone. Imagine if we make printf nodiscard for the reason of not checking the error code? It will simply end up with lots of #pragma gcc diagnostic

@Mick235711
Copy link
Contributor

Is there any paper adding ‘[[candiscard]]’ floating around ? I’d much prefer to making it possible for the function owner to make the function possible to discard the returned value, before we adding the blanket nodiscard for everyone. Imagine if we make printf nodiscard for the reason of not checking the error code? It will simply end up with lots of #pragma gcc diagnostic

Yes, there is P2992R1 proposing the [[discard]] attribute. However, that depends on the ability to put attributes on expressions, which is a rather large and controversial step to take. Because of this, in St. Louis (2024-06) P2992R1 was rejected by EWG with a rather split vote.

@huixie90
Copy link
Member

Is there any paper adding ‘[[candiscard]]’ floating around ? I’d much prefer to making it possible for the function owner to make the function possible to discard the returned value, before we adding the blanket nodiscard for everyone. Imagine if we make printf nodiscard for the reason of not checking the error code? It will simply end up with lots of #pragma gcc diagnostic

Yes, there is P2992R1 proposing the [[discard]] attribute. However, that depends on the ability to put attributes on expressions, which is a rather large and controversial step to take. Because of this, in St. Louis (2024-06) P2992R1 was rejected by EWG with a rather split vote.

Thank is not the same though. that [[discard]] is somewhat similar to [[maybe_unused]] and it is intended to be used by the function caller. What I really like is for authors of the functions that return expected to override the class level expected's [[nodiscard]], and to say that my function's expected can be discarded

@hulxv hulxv force-pushed the fix/expected-nodiscard branch from 3c13814 to 23a7f6f Compare June 1, 2025 10:53
@hulxv hulxv force-pushed the fix/expected-nodiscard branch from 23a7f6f to 55da0b3 Compare June 1, 2025 11:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Mark std::expected as [[nodiscard]]