Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@horenmar
Copy link
Contributor

This means that std::is_constructible_v<_Literal_zero, int> is now false, and thus libraries like Catch2 can detect the difference between a type comparable with int, and type comparable only with literal 0. This then lets it handle expressions like (a <=> b) == 0 inside REQUIRE.

This means that `std::is_constructible_v<_Literal_zero, int>` is
now false, and thus libraries like Catch2 can detect the difference
between a type comparable with `int`, and type comparable only
with literal `0`. This then lets it handle expressions like
`(a <=> b) == 0` inside `REQUIRE`.
@horenmar horenmar requested a review from a team as a code owner January 20, 2024 10:19
@horenmar
Copy link
Contributor Author

I would've liked to add some static asserts as tests, but I didn't see a place to add them to in the tests. I think it is fine to leave it as is, or, if you give me a pointer to where the tests should go, I will add them.

@horenmar horenmar marked this pull request as draft January 20, 2024 13:48
@fsb4000
Copy link
Contributor

fsb4000 commented Jan 20, 2024

48 tests failed. (https://dev.azure.com/vclibs/STL/_build/results?buildId=15956&view=ms.vss-test-web.build-test-results-tab)
I copied 3 outputs, they look similar.

D:\a\_work\1\s\tests\std\tests\P2278R4_basic_const_iterator\test.cpp(151): error C2666: 'std::operator ==': overloaded functions have similar conversions
D:\build\out\inc\compare(54): note: could be 'bool std::operator ==(const std::partial_ordering,std::_Literal_zero) noexcept' [found using argument-dependent lookup]
D:\build\out\inc\compare(58): note: or       'bool std::operator ==(std::partial_ordering,std::partial_ordering) noexcept' [found using argument-dependent lookup]
D:\build\out\inc\compare(190): note: or       'bool std::operator ==(const std::strong_ordering,std::_Literal_zero) noexcept' [found using argument-dependent lookup]
D:\build\out\inc\compare(54): note: or 'bool std::operator ==(const std::partial_ordering,std::_Literal_zero) noexcept' [synthesized expression 'y == x']
D:\build\out\inc\compare(58): note: or 'bool std::operator ==(std::partial_ordering,std::partial_ordering) noexcept' [synthesized expression 'y == x']
D:\a\_work\1\s\tests\std\tests\P2278R4_basic_const_iterator\test.cpp(151): note: while trying to match the argument list '(std::strong_ordering, const std::partial_ordering)'
D:\a\_work\1\s\tests\std\tests\P2278R4_basic_const_iterator\test.cpp(291): note: while evaluating constexpr function 'instantiator::call'
D:\a\_work\1\s\tests\std\tests\P2278R4_basic_const_iterator\test.cpp(378): note: while evaluating constexpr function 'instantiation_test'
D:\a\_work\1\s\tests\std\tests\P2278R4_basic_const_iterator\test.cpp(384): note: while evaluating constexpr function 'all_tests'
D:\a\_work\1\s\tests\std\tests\P2278R4_basic_const_iterator\test.cpp(151): note: the template instantiation context (the oldest one first) is
D:\a\_work\1\s\tests\std\tests\P2278R4_basic_const_iterator\test.cpp(291): note: see reference to function template instantiation 'void instantiator::call<test::iterator<std::random_access_iterator_tag,const int,test::CanDifference::no,test::CanCompare::yes,test::ProxyRef::yes,test::WrappedState::wrapped>>(void)' being compiled
D:\a\_work\1\s\tests\std\tests\P2278R4_basic_const_iterator\test.cpp(276): note: see reference to function template instantiation 'void test_one<test::iterator<std::random_access_iterator_tag,const int,test::CanDifference::no,test::CanCompare::yes,test::ProxyRef::yes,test::WrappedState::wrapped>>(It)' being compiled
        with
        [
            It=test::iterator<std::random_access_iterator_tag,const int,test::CanDifference::no,test::CanCompare::yes,test::ProxyRef::yes,test::WrappedState::wrapped>
        ]
D:\a\_work\1\s\tests\std\tests\P2278R4_basic_const_iterator\test.cpp(168): error C2666: 'std::operator ==': overloaded functions have similar conversions
D:\build\out\inc\compare(54): note: could be 'bool std::operator ==(const std::partial_ordering,std::_Literal_zero) noexcept' [found using argument-dependent lookup]
D:\a\_work\1\s\tests\std\tests\P0768R1_spaceship_cpos\test.cpp(494): error C2666: 'std::operator ==': overloaded functions have similar conversions
D:\build\out\inc\compare(123): note: could be 'bool std::operator ==(const std::weak_ordering,std::_Literal_zero) noexcept' [found using argument-dependent lookup]
D:\build\out\inc\compare(127): note: or       'bool std::operator ==(std::weak_ordering,std::weak_ordering) noexcept' [found using argument-dependent lookup]
D:\build\out\inc\compare(123): note: or 'bool std::operator ==(const std::weak_ordering,std::_Literal_zero) noexcept' [synthesized expression 'y == x']
D:\build\out\inc\compare(127): note: or 'bool std::operator ==(std::weak_ordering,std::weak_ordering) noexcept' [synthesized expression 'y == x']
D:\build\out\inc\compare(190): note: or 'bool std::operator ==(const std::strong_ordering,std::_Literal_zero) noexcept' [synthesized expression 'y == x']
D:\a\_work\1\s\tests\std\tests\P0768R1_spaceship_cpos\test.cpp(494): note: while trying to match the argument list '(std::weak_ordering, std::strong_ordering)'
D:\a\_work\1\s\tests\std\tests\P0768R1_spaceship_cpos\test.cpp(594): note: while evaluating constexpr function 'test_floating'
D:\a\_work\1\s\tests\std\tests\P0768R1_spaceship_cpos\test.cpp(638): note: while evaluating constexpr function 'test'
D:\a\_work\1\s\tests\std\tests\P0768R1_spaceship_cpos\test.cpp(494): note: the template instantiation context (the oldest one first) is
D:\a\_work\1\s\tests\std\tests\P0768R1_spaceship_cpos\test.cpp(538): note: see reference to function template instantiation 'void test_ranked_values<std::pair<int,float>[18]>(const PairArray (&))' being compiled
        with
        [
            PairArray=std::pair<int,float> [18]
        ]
D:\a\_work\1\s\llvm-project\libcxx\test\support\test_comparisons.h(167): error C2666: 'std::operator ==': overloaded functions have similar conversions
D:\build\out\inc\compare(123): note: could be 'bool std::operator ==(const std::weak_ordering,std::_Literal_zero) noexcept' [found using argument-dependent lookup]
D:\build\out\inc\compare(127): note: or       'bool std::operator ==(std::weak_ordering,std::weak_ordering) noexcept' [found using argument-dependent lookup]
D:\build\out\inc\compare(190): note: or       'bool std::operator ==(const std::strong_ordering,std::_Literal_zero) noexcept' [found using argument-dependent lookup]
D:\build\out\inc\compare(123): note: or 'bool std::operator ==(const std::weak_ordering,std::_Literal_zero) noexcept' [synthesized expression 'y == x']
D:\build\out\inc\compare(127): note: or 'bool std::operator ==(std::weak_ordering,std::weak_ordering) noexcept' [synthesized expression 'y == x']
D:\a\_work\1\s\llvm-project\libcxx\test\support\test_comparisons.h(167): note: while trying to match the argument list '(std::strong_ordering, Order)'
        with
        [
            Order=std::weak_ordering
        ]
D:\a\_work\1\s\llvm-project\libcxx\test\support\test_comparisons.h(167): note: the template instantiation context (the oldest one first) is
D:\a\_work\1\s\llvm-project\libcxx\test\std\re\re.submatch\re.submatch.op\compare.pass.cpp(348): note: see reference to function template instantiation 'void test_all_orderings<char>(void)' being compiled
D:\a\_work\1\s\llvm-project\libcxx\test\std\re\re.submatch\re.submatch.op\compare.pass.cpp(335): note: see reference to function template instantiation 'void test<std::basic_string<char,constexpr_char_traits<char>,std::allocator<char>>,std::weak_ordering>(void)' being compiled
D:\a\_work\1\s\llvm-project\libcxx\test\std\re\re.submatch\re.submatch.op\compare.pass.cpp(327): note: see reference to function template instantiation 'bool testOrder<std::weak_ordering,_Ty,std::sub_match<std::_String_const_iterator<std::_String_val<std::_Simple_types<_Elem>>>>>(const T &,const U &,Order)' being compiled
        with
        [
            _Ty=std::basic_string<char,constexpr_char_traits<char>,std::allocator<char>>,
            _Elem=char,
            T=std::basic_string<char,constexpr_char_traits<char>,std::allocator<char>>,
            U=std::sub_match<std::_String_const_iterator<std::_String_val<std::_Simple_types<char>>>>,
            Order=std::weak_ordering
        ]
--

@StephanTLavavej StephanTLavavej added the enhancement Something can be improved label Jan 21, 2024
@frederick-vs-ja
Copy link
Contributor

frederick-vs-ja commented Jan 21, 2024

The change can be regressive because it expects treating 0 as a null pointer constant, see #3581.

On the other hand, this PR, with ambiguity resolved and -Wzero-as-null-pointer-constant disabled, might be doing good things as both x <=> x == 1 - 1 and x <=> x == nullptr should be ideally rejected.

Perhaps we should ask MSVC/Clang frontend teams to add an internal type which can only be initialized with literal 0.

We can furtherly constrain the deleted overload to resolve ambiguity.

diff --git a/stl/inc/compare b/stl/inc/compare
index e35d1a3a..aafe073f 100644
--- a/stl/inc/compare
+++ b/stl/inc/compare
@@ -25,16 +25,22 @@ _STL_DISABLE_CLANG_WARNINGS
 #undef new
 
 _STD_BEGIN
+_EXPORT_STD struct strong_ordering;
+_EXPORT_STD struct weak_ordering;
+_EXPORT_STD struct partial_ordering;
+
 void _Literal_zero_is_expected();
 
 struct _Literal_zero {
-    template <class _Ty, enable_if_t<is_same_v<_Ty, int>, int> = 0>
-    consteval _Literal_zero(_Ty _Zero) noexcept {
+    consteval _Literal_zero(_Literal_zero* _Zero) noexcept {
         // Can't use _STL_VERIFY because this is a core header
-        if (_Zero != 0) {
+        if (_Zero != nullptr) {
             _Literal_zero_is_expected();
         }
     }
+    template <class _Ty,
+        enable_if_t<!_Is_any_of_v<_Ty, int, strong_ordering, weak_ordering, partial_ordering>, int> = 0>
+    _Literal_zero(_Ty) = delete;
 };
 
 using _Compare_t = signed char;

If we change _Literal_zero* to decltype([]{})*, then users can never pass anything other 0 after this PR (with corrections) even if the internal name _Literal_zero is considered usable. This is not sufficient, but I think we can still add other stuffs to make only 0 accepted.

@StephanTLavavej StephanTLavavej added the decision needed We need to choose something before working on this label Jan 22, 2024
@StephanTLavavej
Copy link
Member

I'm not convinced that the ambiguity can be properly resolved here (what if someone derives from a Standard comparison category?).

I also believe that our current implementation is conforming, and that Catch2 is relying on a non-Standard assumption here. Marking this as "decision needed"; I'll bring this up at the next weekly maintainer meeting on Wednesday.

@horenmar
Copy link
Contributor Author

I also believe that our current implementation is conforming, and that Catch2 is relying on a non-Standard assumption here.

This is correct, however the assumption, or rather the effect of it, is a useful one, and works for libc++ and libstdc++.

@cor3ntin
Copy link

I'm not convinced that the ambiguity can be properly resolved here (what if someone derives from a Standard comparison category?).

I also believe that our current implementation is conforming, and that Catch2 is relying on a non-Standard assumption here. Marking this as "decision needed"; I'll bring this up at the next weekly maintainer meeting on Wednesday.

The idea of a magic compiler type (or type attribute) seems to be a promising solution.
It's something I would be willing to explore in clang in the next few months if there is some appetite for it.
WDYT?

@StephanTLavavej
Copy link
Member

I don't really foresee the MSVC compiler frontend team having capacity to implement such magic any time soon.

@StephanTLavavej
Copy link
Member

We talked about this at our weekly maintainer meeting and agree that this explicitly falls under our Non-Goals as a non-Standard extension. Thanks for looking into this.

@horenmar horenmar deleted the devel-replace-literal-0-init branch January 24, 2024 23:15
@horenmar
Copy link
Contributor Author

Very well.

I do want to point out that this change is not a non-standard extension. The PR would change the implementation of an unspecified type to align it with the implementations in libstdc++ and libc++ and with the implementation in Microsoft/STL from ~10 months ago.

@horenmar
Copy link
Contributor Author

Also since I am having a related discussion in parallel somewhere else, I found that the change made in #3581 broke other people back in November and there is a devcom ticket about it.

It boils down to requires clause kinda lying: https://godbolt.org/z/5jbhKEEY9

@steve-downey
Copy link

#include <compare>

template<typename T, typename U>
constexpr bool not_equal(T lhs, U rhs) 
requires(requires(T lhs, U rhs) { lhs != rhs; })
{
        return lhs != rhs;
}

template<typename T, typename U>
constexpr bool not_equal(T lhs, U rhs) 
requires(!requires(T lhs, U rhs) { lhs != rhs; })
{
        return false;
}

int main() {
    constexpr int int_zero = 0;
    constexpr unsigned int unsigned_zero = 0;
    constexpr auto ordering = 1 <=> 1;
    constexpr bool b = not_equal(ordering, unsigned_zero);
    constexpr bool b = not_equal(ordering, int_zero); //FAILS ON MSVC Stdlib
    return b == false;
}

https://godbolt.org/z/s7vbh1PPf

Asking if an ordering is comparable with an int ought to be an answerable question.

@fsb4000
Copy link
Contributor

fsb4000 commented Jan 31, 2024

I created an issue about it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

decision needed We need to choose something before working on this enhancement Something can be improved

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants