llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
29.39k stars 12.15k forks source link

clang 19 raises operator<=>() return type to std::strong_ordering for double #115182

Open pkl97 opened 3 weeks ago

pkl97 commented 3 weeks ago

This program compiles with clang 19.1.2 but fails to compile with MSVC 19.41:

#include <compare>

class C
{
    double d1;
    double d2;
    std::strong_ordering operator<=>(const C&) const = default;
};

int main()
{
    return 0;
}

The MSVC error message:

main.cpp(7): error C7549: 'C::operator <=>': defaulted comparison function cannot be declared to return a comparison category stronger than the common category among bases and members which was deduced to be 'std::partial_ordering'

In my opinion MSVC is correct because double by default only supports std::partial_ordering because of NaN values.

See also here: https://en.cppreference.com/w/cpp/utility/compare/partial_ordering

The built-in operator<=>between floating-point values uses this ordering: the positive zero and the negative zero compare equivalent, but can be distinguished, and NaN values compare unordered with any other value.

Why does clang compile the code?

llvmbot commented 3 weeks ago

@llvm/issue-subscribers-clang-frontend

Author: None (pkl97)

This program compiles with clang 19.1.2 but fails to compile with MSVC 19.41: ```c++ #include <compare> class C { double d1; double d2; std::strong_ordering operator<=>(const C&) const = default; }; int main() { return 0; } ``` The MSVC error message: > main.cpp(7): error C7549: 'C::operator <=>': > defaulted comparison function cannot be declared to return a comparison > category stronger than the common category among bases and members which was deduced to be 'std::partial_ordering' In my opinion MSVC is correct because double by default only supports std::partial_ordering because of NaN values. See also here: https://en.cppreference.com/w/cpp/utility/compare/partial_ordering > The built-in operator<=>between floating-point values uses this ordering: > the positive zero and the negative zero compare equivalent, but can be > distinguished, and NaN values compare unordered with any other value. Why does clang compile the code?
zygoloid commented 3 weeks ago

Per the standard, the code is valid and the operator<=> function is defined as deleted.

pkl97 commented 3 weeks ago

If the program is slightly modified to actually use the operator then an error is triggered.

#include <compare>
#include <iostream>

struct C
{
    double d1;
    double d2;
    std::strong_ordering operator<=>(const C&) const = default;
};

int main()
{
    const C c1{ .d1=1.0, .d2=2.0 }, c2{ .d1=2.0, .d2=1.0 };
    if (c1<c2)
        std::cout << "smaller" << std::endl;

    return 0;
}

[>>]$ clang++ -std=c++20 -Wall -Wextra main.cpp main.cpp:8:62: error: no matching conversion for static_cast from 'std::partial_ordering' to 'std::strong_ordering' 8 | std::strong_ordering operator<=>(const C&) const = default; | ^ main.cpp:14:11: note: in defaulted three-way comparison operator for 'C' first required here 14 | if (c1<c2) | ^ include/c++/v1/compare/ordering.h:189:7: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'std::partial_ordering' to 'const strong_ordering' for 1st argument 189 | class strong_ordering { | ^~~~~~~ include/c++/v1/__compare/ordering.h:189:7: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'std::partial_ordering' to 'strong_ordering' for 1st argument 189 | class strong_ordering { | ^~~~~~~ include/c++/v1/compare/ordering.h:192:44: note: candidate constructor not viable: no known conversion from 'std::partial_ordering' to '_OrdResult' for 1st argument 192 | _LIBCPP_HIDE_FROM_ABI explicit constexpr strong_ordering(OrdResult v) noexcept : value(_ValueT(__v)) {} | ^ ~~~~~~ 1 error generated.

Now the compilation fails with a hint that std::partial_ordering cannot be converted to std::strong_ordering. But there is no explicit error message about operator<=>() being deleted.

zygoloid commented 3 weeks ago

Yes, this definitely looks like a bug. Interesting that MSVC seems to have a different bug for the same situation.