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

P0627R6: Function to mark unreachable code #2526

Merged
merged 13 commits into from
Feb 12, 2022

Conversation

AlexGuteniev
Copy link
Contributor

@AlexGuteniev AlexGuteniev commented Feb 7, 2022

Resolves #2531

@AlexGuteniev AlexGuteniev requested a review from a team as a code owner February 7, 2022 18:26
stl/inc/utility Outdated Show resolved Hide resolved
@CaseyCarter CaseyCarter changed the title unreachable code P0672R6: Function to mark unreachable code Feb 7, 2022
@CaseyCarter CaseyCarter added the cxx23 C++23 feature label Feb 7, 2022
stl/inc/utility Outdated Show resolved Hide resolved
stl/inc/yvals_core.h Show resolved Hide resolved
stl/inc/utility Outdated Show resolved Hide resolved
tests/std/tests/P0627R6_unreachable/test.cpp Outdated Show resolved Hide resolved
@fsb4000

This comment was marked as outdated.

@CaseyCarter CaseyCarter linked an issue Feb 8, 2022 that may be closed by this pull request
@StephanTLavavej StephanTLavavej changed the title P0672R6: Function to mark unreachable code P0627R6: Function to mark unreachable code Feb 8, 2022
stl/inc/utility Outdated

[[noreturn]] inline void unreachable() noexcept /* strengthened */ {
#ifdef _DEBUG
_CSTD abort();
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure this is a great idea. abort is well-defined, and therefore doesn't poison the code path leading to it with undefined behavior. That seems like a very subtle difference that folks may not be expecting when they flip the debug/release switch. (I'm going to call this "Comment" rather than "Request change" to get more reviewers' opinions.)

Copy link
Contributor Author

@AlexGuteniev AlexGuteniev Feb 8, 2022

Choose a reason for hiding this comment

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

I observed that __assume(false) does provide UB in debug. Up to this

int main()
{
   __assume(false);
   // does not necessarily return zero, even in /Od
}

Normally I'd except debug mode to catch any UBs that can be caught.

Also look into ATLASSUME for a precedent.

Copy link
Member

Choose a reason for hiding this comment

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

@AlexGuteniev I really like your idea of using _STL_UNREACHABLE here as we're already using it in product code for this exact purpose (in <format> and <variant>).

@CaseyCarter I share your concern about well-defined behavior. How would you feel about:

_STL_UNREACHABLE;
#ifdef _DEBUG
_CSTD abort();
#endif // _DEBUG

Copy link
Member

Choose a reason for hiding this comment

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

I think this is the best of both worlds. +1 from me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

_STL_UNREACHABLE;
#ifdef _DEBUG
_CSTD abort();
#endif // _DEBUG

Is not really a good option.
By having abort(); after _STL_UNREACHABLE; you ask for UB, and compiler may implement the UB by not actually calling abort, and in /O2 it really does! _DEBUG may be used with /O2, and I don't think #pragma optimize is good here.

If you want to enjoy UB even in _DEBUG here, I'd rather leave just __assume(false)/__builtin_unreachable(), wrapped in _STL_UNREACHABLE;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A possible way to be able to debug the unreachable is to guard the original _STL_UNREACHABLE in #ifndef _STL_UNREACHABLE. This way a user may do #define _STL_UNREACHABLE abort(), #define _STL_UNREACHABLE __debugbreak() or #define _STL_UNREACHABLE __ud2(). Still it is arguable place for a customization; if we decide to embrace the UB, let's have it.

Copy link
Member

Choose a reason for hiding this comment

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

By having abort(); after _STL_UNREACHABLE; you ask for UB, and compiler may implement the UB by not actually calling abort, and in /O2 it really does!

I'm fine with this, it is working as intended. The goal of putting abort here is not to define the behavior, it's to increase the likelihood that the outcome of the UB is to crash near to the occurrence of unreachable in the user's code. That outcome isn't deterministic and can't be made deterministic without breaking the intended functionality. I don't agree that the lack of determinism makes the abort call useless.

A possible way to be able to debug the unreachable is to guard the original _STL_UNREACHABLE in #ifndef _STL_UNREACHABLE.

If people sometimes want to not use unreachable they can define their own macros.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please review the comment I added next to abort().
(I don't think we can leave the code with deliberate UB without a comment).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also what exactly the semantic of _STL_UNREACHABLE?
I mean if this is precondition and not internal invariant, maybe the abort() should rather be embedded there rather than here?

@AlexGuteniev
Copy link
Contributor Author

Maybe should reuse:

STL/stl/inc/yvals_core.h

Lines 1500 to 1504 in ee6a79e

#ifdef __clang__
#define _STL_UNREACHABLE __builtin_unreachable()
#else // ^^^ clang ^^^ / vvv other vvv
#define _STL_UNREACHABLE __assume(false)
#endif // __clang__

tests/std/tests/P0627R6_unreachable/test.cpp Outdated Show resolved Hide resolved
stl/inc/yvals_core.h Outdated Show resolved Hide resolved
stl/inc/utility Outdated

[[noreturn]] inline void unreachable() noexcept /* strengthened */ {
#ifdef _DEBUG
_CSTD abort();
Copy link
Member

Choose a reason for hiding this comment

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

@AlexGuteniev I really like your idea of using _STL_UNREACHABLE here as we're already using it in product code for this exact purpose (in <format> and <variant>).

@CaseyCarter I share your concern about well-defined behavior. How would you feel about:

_STL_UNREACHABLE;
#ifdef _DEBUG
_CSTD abort();
#endif // _DEBUG

@CaseyCarter CaseyCarter self-requested a review February 9, 2022 00:12
@StephanTLavavej StephanTLavavej self-assigned this Feb 11, 2022
@StephanTLavavej
Copy link
Member

I'm mirroring this to the MSVC-internal repo - please notify me if any further changes are pushed.

@StephanTLavavej StephanTLavavej merged commit b6b9778 into microsoft:main Feb 12, 2022
@StephanTLavavej
Copy link
Member

Thanks for reaching this unreachable feature so quickly! 😹 🚀 🎉

@AlexGuteniev AlexGuteniev deleted the cantreach branch February 12, 2022 05:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cxx23 C++23 feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

P0627R6 unreachable()
5 participants