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

Is expected::error() ment to throw if it contains a value. #113

Open
ashley-b opened this issue Jul 1, 2022 · 3 comments
Open

Is expected::error() ment to throw if it contains a value. #113

ashley-b opened this issue Jul 1, 2022 · 3 comments

Comments

@ashley-b
Copy link

ashley-b commented Jul 1, 2022

Sorry if this has already been answered, but I found this odd behaviour.
When I do the following test

tl::expected< std::size_t, std::errc >; s(20);
fmt::print("s.value() {}\n", s.value());
fmt::print("s.error() {}\n", s.error());

I get

s.value() 20
s.error() 20

I would have expected it to throw a bad_expected_access equivalent, like accessing value() when is set to unexpected.

@ashley-b
Copy link
Author

ashley-b commented Jul 2, 2022

As a follow up, I found the original author implementation and there solution is to use assert's to safe guard this.
https://github.com/viboes/std-make/blob/b498cd1c9468445781afdd3a9eafad9cb2efb96c/include/experimental/fundamental/v3/expected2/expected.hpp#L393-L412

@daira
Copy link
Contributor

daira commented Sep 5, 2022

According to the documentation, it is undefined behaviour. Other instances of undefined behaviour are to use operator* or operator-> on an error value (unlike value() which throws).

I agree with you; the behaviour should retain memory safety. There is no performance benefit to leaving this as undefined behaviour, because in practice correct code will look something like this:

tl::expected e = ...;
if (!e.has_value()) { ... e.error() ... }

Suppose that assert(!has_value()) were added in tl::expected::error(). Since an optimizing compiler will inline error(), it can see that the assertion is redundant and will completely optimize it out. And so in the majority of cases, only incorrect code would incur the cost of the assertion. Of course it's possible to construct artificial examples in which that's not the case. But even if the compiler doesn't optimize out the assert, the boolean that records whether the expected is a value or an error will in practice be in L1 cache, so there will not be extra cache misses.

For our use of tl::expected in Zcash, I added a patch almost identical (but also covering operator* and operator->) to the one linked above. I intend to file a PR for that here.

Note that this change does not introduce an incompatibility with any version of the std::expected proposal, because an assertion error is a valid refinement of undefined behaviour. Nor would it in practice lead to reliance on the assertion, because people don't want their programs to terminate with assertion failures, and it's still perfectly clear that such a program is incorrect.

daira added a commit to daira/expected that referenced this issue Sep 5, 2022
This affects operator->, operator*, and error() on a tl::expected
instance. The performance impact is likely negligible for typical uses.
refs TartanLlama#113

Signed-off-by: Daira Hopwood <[email protected]>
daira added a commit to daira/expected that referenced this issue Sep 5, 2022
This affects operator->, operator*, and error() on a tl::expected
instance. The performance impact is likely negligible for typical uses.
refs TartanLlama#113

Signed-off-by: Daira Hopwood <[email protected]>
daira added a commit to daira/expected that referenced this issue Dec 8, 2022
This affects operator->, operator*, and error() on a tl::expected
instance. The performance impact is likely negligible for typical uses.
refs TartanLlama#113

Signed-off-by: Daira Hopwood <[email protected]>
TartanLlama pushed a commit that referenced this issue Feb 15, 2023
* Add assertions for cases where the API documents undefined behaviour.
This affects operator->, operator*, and error() on a tl::expected
instance. The performance impact is likely negligible for typical uses.
refs #113

Signed-off-by: Daira Hopwood <[email protected]>

* Add tests for assertions.

Signed-off-by: Daira Hopwood <[email protected]>

* Add documentation for assertion behaviour.

Signed-off-by: Daira Hopwood <[email protected]>

---------

Signed-off-by: Daira Hopwood <[email protected]>
@daira
Copy link
Contributor

daira commented Feb 16, 2023

@ashley-b is #117 sufficient to fix this from your point of view?

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

2 participants