Skip to content

P0627R6: Function to mark unreachable code - #2526

Merged
Stephan T. Lavavej (StephanTLavavej) merged 13 commits into
microsoft:mainfrom
AlexGuteniev:cantreach
Feb 12, 2022
Merged

Stephan T. Lavavej (StephanTLavavej) merged 13 commits into
microsoft:mainfrom
AlexGuteniev:cantreach

Conversation

@AlexGuteniev

@AlexGuteniev Alex Guteniev (AlexGuteniev) commented Feb 7, 2022

Copy link
Copy Markdown
Contributor

Resolves #2531

@AlexGuteniev
Alex Guteniev (AlexGuteniev) requested a review from a team as a code owner February 7, 2022 18:26
Comment thread stl/inc/utility Outdated
@CaseyCarter Casey Carter (CaseyCarter) changed the title unreachable code P0672R6: Function to mark unreachable code Feb 7, 2022
@CaseyCarter Casey Carter (CaseyCarter) added the cxx23 C++23 feature label Feb 7, 2022
Comment thread stl/inc/utility Outdated
Comment thread stl/inc/yvals_core.h
Comment thread stl/inc/utility Outdated
Comment thread tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp Outdated
Comment thread tests/std/tests/P0627R6_unreachable/test.cpp Outdated
@fsb4000

This comment was marked as outdated.

@CaseyCarter Casey Carter (CaseyCarter) linked an issue Feb 8, 2022 that may be closed by this pull request
@StephanTLavavej Stephan T. Lavavej (StephanTLavavej) changed the title P0672R6: Function to mark unreachable code P0627R6: Function to mark unreachable code Feb 8, 2022
Comment thread tests/std/tests/P0627R6_unreachable/test.cpp
Comment thread stl/inc/utility Outdated

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

Copy link
Copy Markdown
Contributor

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.)

@AlexGuteniev Alex Guteniev (AlexGuteniev) Feb 8, 2022

Copy link
Copy Markdown
Contributor Author

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
Copy Markdown
Member

Choose a reason for hiding this comment

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

Alex Guteniev (@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>).

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

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

Copy link
Copy Markdown
Contributor

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Contributor

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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__

Comment thread tests/std/tests/P0627R6_unreachable/test.cpp Outdated
Comment thread stl/inc/yvals_core.h Outdated
Comment thread stl/inc/utility Outdated

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Alex Guteniev (@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>).

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

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

@StephanTLavavej

Copy link
Copy Markdown
Member

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

@StephanTLavavej
Stephan T. Lavavej (StephanTLavavej) merged commit b6b9778 into microsoft:main Feb 12, 2022
@StephanTLavavej

Copy link
Copy Markdown
Member

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

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