From ebbcf3b0fcadc10c3b3e8ad755aa96266c0bbfb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Michal?= Date: Wed, 26 Nov 2025 02:35:50 +0100 Subject: [PATCH 1/8] Add tests. --- .../tests/P0768R1_spaceship_operator/test.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/std/tests/P0768R1_spaceship_operator/test.cpp b/tests/std/tests/P0768R1_spaceship_operator/test.cpp index 5db13bb77d4..b4780ae47ac 100644 --- a/tests/std/tests/P0768R1_spaceship_operator/test.cpp +++ b/tests/std/tests/P0768R1_spaceship_operator/test.cpp @@ -217,6 +217,19 @@ void test_algorithm() { assert((test_algorithm2())); } +// Test added to handle GH-5689 +template +void test_not_constructible() { + static_assert(!std::is_default_constructible_v); + static_assert(!std::is_constructible_v); + + // How to test this does not compile? + //{ + // auto [n] = 0 <=> 0; + // (void) n; + //} +} + int main() { static_assert(test_orderings()); test_orderings(); @@ -230,4 +243,8 @@ int main() { test_algorithm(); test_algorithm(); test_algorithm(); + + test_not_constructible(); + test_not_constructible(); + test_not_constructible(); } From b03a19d7eb17556be306627b7fd0403a6ebd0536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Michal?= Date: Wed, 26 Nov 2025 02:39:10 +0100 Subject: [PATCH 2/8] Make {strong, partial, weak}_ordering not default constructible and constructible from int. Prevent structured bindings. --- stl/inc/compare | 48 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/stl/inc/compare b/stl/inc/compare index 09554ea1511..ebc070731e0 100644 --- a/stl/inc/compare +++ b/stl/inc/compare @@ -12,6 +12,7 @@ _EMIT_STL_WARNING(STL4038, "The contents of are available only with C++20 or later."); #else // ^^^ !_HAS_CXX20 / _HAS_CXX20 vvv #include +#include #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) @@ -102,9 +103,16 @@ _EXPORT_STD struct partial_ordering { // The stored value is either less (0xff), equivalent (0x00), greater (0x01), or unordered (0x80). // Subtracting from 0 produces either 0x01, 0x00, 0xff, or 0x80. Note that the effect is to // exchange less for greater (and vice versa), while leaving equivalent and unordered unchanged. - return {static_cast<_Compare_t>(0 - static_cast(_Val._Value))}; + return partial_ordering{static_cast<_Compare_t>(0 - static_cast(_Val._Value))}; } +private: + friend struct weak_ordering; + friend struct strong_ordering; + + explicit constexpr partial_ordering(_Compare_t _Value) : _Value{_Value} {} + +public: _Compare_t _Value; }; @@ -119,7 +127,7 @@ _EXPORT_STD struct weak_ordering { static const weak_ordering greater; constexpr operator partial_ordering() const noexcept { - return {static_cast<_Compare_t>(_Value)}; + return partial_ordering{static_cast<_Compare_t>(_Value)}; } _NODISCARD friend constexpr bool operator==(const weak_ordering _Val, _Literal_zero) noexcept { @@ -165,9 +173,15 @@ _EXPORT_STD struct weak_ordering { } _NODISCARD friend constexpr weak_ordering operator<=>(_Literal_zero, const weak_ordering _Val) noexcept { - return {static_cast<_Compare_t>(-_Val._Value)}; + return weak_ordering{static_cast<_Compare_t>(-_Val._Value)}; } +private: + friend struct strong_ordering; + + explicit constexpr weak_ordering(_Compare_t _Value) : _Value{_Value} {} + +public: _Compare_t _Value; }; @@ -182,11 +196,11 @@ _EXPORT_STD struct strong_ordering { static const strong_ordering greater; constexpr operator partial_ordering() const noexcept { - return {static_cast<_Compare_t>(_Value)}; + return partial_ordering{static_cast<_Compare_t>(_Value)}; } constexpr operator weak_ordering() const noexcept { - return {static_cast<_Compare_t>(_Value)}; + return weak_ordering{static_cast<_Compare_t>(_Value)}; } _NODISCARD friend constexpr bool operator==(const strong_ordering _Val, _Literal_zero) noexcept { @@ -232,9 +246,13 @@ _EXPORT_STD struct strong_ordering { } _NODISCARD friend constexpr strong_ordering operator<=>(_Literal_zero, const strong_ordering _Val) noexcept { - return {static_cast<_Compare_t>(-_Val._Value)}; + return strong_ordering{static_cast<_Compare_t>(-_Val._Value)}; } +private: + explicit constexpr strong_ordering(_Compare_t _Value) : _Value{_Value} {} + +public: _Compare_t _Value; }; @@ -243,6 +261,24 @@ inline constexpr strong_ordering strong_ordering::equal{static_cast<_Compare_t>( inline constexpr strong_ordering strong_ordering::equivalent{static_cast<_Compare_t>(_Compare_eq::equivalent)}; inline constexpr strong_ordering strong_ordering::greater{static_cast<_Compare_t>(_Compare_ord::greater)}; +template +struct tuple_size; + +template<> +struct tuple_size { + void value(); +}; + +template <> +struct tuple_size { + void value(); +}; + +template <> +struct tuple_size { + void value(); +}; + _EXPORT_STD _NODISCARD constexpr bool is_eq(const partial_ordering _Comp) noexcept { return _Comp == 0; } From 3eafafb89b26a0e1e9535c68f32a24c46f518326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Michal?= Date: Wed, 26 Nov 2025 02:40:44 +0100 Subject: [PATCH 3/8] Say constexpr explicit rather than the other way around. --- stl/inc/compare | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/compare b/stl/inc/compare index ebc070731e0..d8debabcde6 100644 --- a/stl/inc/compare +++ b/stl/inc/compare @@ -110,7 +110,7 @@ private: friend struct weak_ordering; friend struct strong_ordering; - explicit constexpr partial_ordering(_Compare_t _Value) : _Value{_Value} {} + constexpr explicit partial_ordering(_Compare_t _Value) : _Value{_Value} {} public: _Compare_t _Value; @@ -179,7 +179,7 @@ _EXPORT_STD struct weak_ordering { private: friend struct strong_ordering; - explicit constexpr weak_ordering(_Compare_t _Value) : _Value{_Value} {} + constexpr explicit weak_ordering(_Compare_t _Value) : _Value{_Value} {} public: _Compare_t _Value; @@ -250,7 +250,7 @@ _EXPORT_STD struct strong_ordering { } private: - explicit constexpr strong_ordering(_Compare_t _Value) : _Value{_Value} {} + constexpr explicit strong_ordering(_Compare_t _Value) : _Value{_Value} {} public: _Compare_t _Value; From 83d2f42318b7b883b411ae0e82f1197150c72424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Michal?= Date: Wed, 26 Nov 2025 02:45:58 +0100 Subject: [PATCH 4/8] Mention the github issue in code. --- stl/inc/compare | 1 + 1 file changed, 1 insertion(+) diff --git a/stl/inc/compare b/stl/inc/compare index d8debabcde6..54843b52b26 100644 --- a/stl/inc/compare +++ b/stl/inc/compare @@ -264,6 +264,7 @@ inline constexpr strong_ordering strong_ordering::greater{static_cast<_Compare_t template struct tuple_size; +// Prevent structured binding to objects of xxx_ordering, see GH-5689 template<> struct tuple_size { void value(); From 86ffa8d36c1fc90c7e86b395cb501f00d1321c3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Michal?= Date: Wed, 26 Nov 2025 02:53:55 +0100 Subject: [PATCH 5/8] Fix incorrect formatting. --- stl/inc/compare | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/compare b/stl/inc/compare index 54843b52b26..ec40996fc80 100644 --- a/stl/inc/compare +++ b/stl/inc/compare @@ -261,11 +261,11 @@ inline constexpr strong_ordering strong_ordering::equal{static_cast<_Compare_t>( inline constexpr strong_ordering strong_ordering::equivalent{static_cast<_Compare_t>(_Compare_eq::equivalent)}; inline constexpr strong_ordering strong_ordering::greater{static_cast<_Compare_t>(_Compare_ord::greater)}; -template +template struct tuple_size; // Prevent structured binding to objects of xxx_ordering, see GH-5689 -template<> +template <> struct tuple_size { void value(); }; From 2ebf75a0501ff6601301b569d06effdbaedf308b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Michal?= Date: Wed, 26 Nov 2025 03:12:58 +0100 Subject: [PATCH 6/8] Remove heder include circular dependency. --- stl/inc/compare | 1 - 1 file changed, 1 deletion(-) diff --git a/stl/inc/compare b/stl/inc/compare index ec40996fc80..686f2f72e28 100644 --- a/stl/inc/compare +++ b/stl/inc/compare @@ -12,7 +12,6 @@ _EMIT_STL_WARNING(STL4038, "The contents of are available only with C++20 or later."); #else // ^^^ !_HAS_CXX20 / _HAS_CXX20 vvv #include -#include #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) From 9dcdd2b93f9df7819972c8de00ed9807838b5161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Michal?= Date: Wed, 26 Nov 2025 11:57:03 +0100 Subject: [PATCH 7/8] Restore xxx_ordering eligibility to passing in registers under MSVC calling convention. --- stl/inc/compare | 61 ++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/stl/inc/compare b/stl/inc/compare index 686f2f72e28..0c78f914117 100644 --- a/stl/inc/compare +++ b/stl/inc/compare @@ -47,7 +47,11 @@ enum class _Compare_eq : _Compare_t { equal = 0, equivalent = equal }; enum class _Compare_ord : _Compare_t { less = -1, greater = 1 }; enum class _Compare_ncmp : _Compare_t { unordered = -128 }; +struct _Secret_ordering_construction_tag {}; + _EXPORT_STD struct partial_ordering { + constexpr partial_ordering(same_as<_Secret_ordering_construction_tag> auto) = delete; + static const partial_ordering less; static const partial_ordering equivalent; static const partial_ordering greater; @@ -102,31 +106,28 @@ _EXPORT_STD struct partial_ordering { // The stored value is either less (0xff), equivalent (0x00), greater (0x01), or unordered (0x80). // Subtracting from 0 produces either 0x01, 0x00, 0xff, or 0x80. Note that the effect is to // exchange less for greater (and vice versa), while leaving equivalent and unordered unchanged. - return partial_ordering{static_cast<_Compare_t>(0 - static_cast(_Val._Value))}; + return _STD _Bit_cast(static_cast<_Compare_t>(0 - static_cast(_Val._Value))); } -private: - friend struct weak_ordering; - friend struct strong_ordering; - - constexpr explicit partial_ordering(_Compare_t _Value) : _Value{_Value} {} - -public: _Compare_t _Value; }; -inline constexpr partial_ordering partial_ordering::less{static_cast<_Compare_t>(_Compare_ord::less)}; -inline constexpr partial_ordering partial_ordering::equivalent{static_cast<_Compare_t>(_Compare_eq::equivalent)}; -inline constexpr partial_ordering partial_ordering::greater{static_cast<_Compare_t>(_Compare_ord::greater)}; -inline constexpr partial_ordering partial_ordering::unordered{static_cast<_Compare_t>(_Compare_ncmp::unordered)}; +inline constexpr partial_ordering partial_ordering::less{_STD _Bit_cast(_Compare_ord::less)}; +inline constexpr partial_ordering partial_ordering::equivalent{ + _STD _Bit_cast(_Compare_eq::equivalent)}; +inline constexpr partial_ordering partial_ordering::greater{_STD _Bit_cast(_Compare_ord::greater)}; +inline constexpr partial_ordering partial_ordering::unordered{ + _STD _Bit_cast(_Compare_ncmp::unordered)}; _EXPORT_STD struct weak_ordering { + constexpr weak_ordering(same_as<_Secret_ordering_construction_tag> auto) = delete; + static const weak_ordering less; static const weak_ordering equivalent; static const weak_ordering greater; constexpr operator partial_ordering() const noexcept { - return partial_ordering{static_cast<_Compare_t>(_Value)}; + return _STD _Bit_cast(_Value); } _NODISCARD friend constexpr bool operator==(const weak_ordering _Val, _Literal_zero) noexcept { @@ -172,34 +173,30 @@ _EXPORT_STD struct weak_ordering { } _NODISCARD friend constexpr weak_ordering operator<=>(_Literal_zero, const weak_ordering _Val) noexcept { - return weak_ordering{static_cast<_Compare_t>(-_Val._Value)}; + return _STD _Bit_cast(static_cast<_Compare_t>(-_Val._Value)); } -private: - friend struct strong_ordering; - - constexpr explicit weak_ordering(_Compare_t _Value) : _Value{_Value} {} - -public: _Compare_t _Value; }; -inline constexpr weak_ordering weak_ordering::less{static_cast<_Compare_t>(_Compare_ord::less)}; -inline constexpr weak_ordering weak_ordering::equivalent{static_cast<_Compare_t>(_Compare_eq::equivalent)}; -inline constexpr weak_ordering weak_ordering::greater{static_cast<_Compare_t>(_Compare_ord::greater)}; +inline constexpr weak_ordering weak_ordering::less{_STD _Bit_cast(_Compare_ord::less)}; +inline constexpr weak_ordering weak_ordering::equivalent{_STD _Bit_cast(_Compare_eq::equivalent)}; +inline constexpr weak_ordering weak_ordering::greater{_STD _Bit_cast(_Compare_ord::greater)}; _EXPORT_STD struct strong_ordering { + constexpr strong_ordering(same_as<_Secret_ordering_construction_tag> auto) = delete; + static const strong_ordering less; static const strong_ordering equal; static const strong_ordering equivalent; static const strong_ordering greater; constexpr operator partial_ordering() const noexcept { - return partial_ordering{static_cast<_Compare_t>(_Value)}; + return _STD _Bit_cast(_Value); } constexpr operator weak_ordering() const noexcept { - return weak_ordering{static_cast<_Compare_t>(_Value)}; + return _STD _Bit_cast(_Value); } _NODISCARD friend constexpr bool operator==(const strong_ordering _Val, _Literal_zero) noexcept { @@ -245,20 +242,16 @@ _EXPORT_STD struct strong_ordering { } _NODISCARD friend constexpr strong_ordering operator<=>(_Literal_zero, const strong_ordering _Val) noexcept { - return strong_ordering{static_cast<_Compare_t>(-_Val._Value)}; + return _STD _Bit_cast(static_cast<_Compare_t>(-_Val._Value)); } -private: - constexpr explicit strong_ordering(_Compare_t _Value) : _Value{_Value} {} - -public: _Compare_t _Value; }; -inline constexpr strong_ordering strong_ordering::less{static_cast<_Compare_t>(_Compare_ord::less)}; -inline constexpr strong_ordering strong_ordering::equal{static_cast<_Compare_t>(_Compare_eq::equal)}; -inline constexpr strong_ordering strong_ordering::equivalent{static_cast<_Compare_t>(_Compare_eq::equivalent)}; -inline constexpr strong_ordering strong_ordering::greater{static_cast<_Compare_t>(_Compare_ord::greater)}; +inline constexpr strong_ordering strong_ordering::less{_STD _Bit_cast(_Compare_ord::less)}; +inline constexpr strong_ordering strong_ordering::equal{_STD _Bit_cast(_Compare_eq::equal)}; +inline constexpr strong_ordering strong_ordering::equivalent{_STD _Bit_cast(_Compare_eq::equivalent)}; +inline constexpr strong_ordering strong_ordering::greater{_STD _Bit_cast(_Compare_ord::greater)}; template struct tuple_size; From bb8654b4ce0c99613b29484522e74922a0bcb502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Michal?= Date: Wed, 26 Nov 2025 12:03:59 +0100 Subject: [PATCH 8/8] Add TRANSITION, ABI comment. --- stl/inc/compare | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stl/inc/compare b/stl/inc/compare index 0c78f914117..b95562018f4 100644 --- a/stl/inc/compare +++ b/stl/inc/compare @@ -47,6 +47,9 @@ enum class _Compare_eq : _Compare_t { equal = 0, equivalent = equal }; enum class _Compare_ord : _Compare_t { less = -1, greater = 1 }; enum class _Compare_ncmp : _Compare_t { unordered = -128 }; +// TRANSITION, ABI, this is used for allowing comparison category types to be passing and returning in registers +// in MSVC ABI while making them non-default-constructible. +// It is intentional to use a specific tag type to avoid impact on overload resolution. struct _Secret_ordering_construction_tag {}; _EXPORT_STD struct partial_ordering {