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

[libc++] Fix acceptance of convertible-to-{float,double,long double} in std::isfinite() #98841

Merged
merged 9 commits into from
Jul 18, 2024

Conversation

robincaloudis
Copy link
Contributor

Closes #98816.

The following error is raised if
convertibles are used as arguments
to isfinite(): "error: "no matching
function for call to 'isfinite'".
This time, we test for the overload
long double.
@robincaloudis robincaloudis requested a review from a team as a code owner July 14, 2024 22:13
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Jul 14, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Jul 14, 2024

@llvm/pr-subscribers-libcxx

Author: Robin Caloudis (robincaloudis)

Changes

Closes #98816.


Full diff: https://github.com/llvm/llvm-project/pull/98841.diff

2 Files Affected:

  • (modified) libcxx/include/__math/traits.h (+14)
  • (modified) libcxx/test/std/numerics/c.math/isfinite.pass.cpp (+33)
diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index a448266797557..07e5a74ac2362 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -55,6 +55,20 @@ _LIBCPP_NODISCARD _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isfin
   return true;
 }
 
+#ifdef _LIBCPP_PREFERRED_OVERLOAD
+_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isfinite(float __x) _NOEXCEPT {
+  return __builtin_isfinite(__x);
+}
+
+_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD bool
+isfinite(double __x) _NOEXCEPT {
+  return __builtin_isfinite(__x);
+}
+
+_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isfinite(long double __x) _NOEXCEPT {
+  return __builtin_isfinite(__x);
+}
+#endif
 // isinf
 
 template <class _A1, __enable_if_t<is_arithmetic<_A1>::value && numeric_limits<_A1>::has_infinity, int> = 0>
diff --git a/libcxx/test/std/numerics/c.math/isfinite.pass.cpp b/libcxx/test/std/numerics/c.math/isfinite.pass.cpp
index 6bbc3aaac6d13..56d9550348a9b 100644
--- a/libcxx/test/std/numerics/c.math/isfinite.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isfinite.pass.cpp
@@ -62,9 +62,42 @@ struct TestInt {
   }
 };
 
+struct ConvertibleFloat {
+    int value;
+
+    ConvertibleFloat(int v) : value(v) {}
+
+    operator float() const {
+        return static_cast<float>(value);
+    }
+};
+
+struct ConvertibleDouble {
+    int value;
+
+    ConvertibleDouble(int v) : value(v) {}
+
+    operator double() const {
+        return static_cast<double>(value);
+    }
+};
+
+struct ConvertibleLongDouble {
+    int value;
+
+    ConvertibleLongDouble(int v) : value(v) {}
+
+    operator long double() const {
+        return static_cast<long double>(value);
+    }
+};
+
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
+  assert(std::isfinite(ConvertibleFloat(0)));
+  assert(std::isfinite(ConvertibleDouble(0)));
+  assert(std::isfinite(ConvertibleLongDouble(0)));
 
   return 0;
 }

@robincaloudis robincaloudis marked this pull request as draft July 14, 2024 22:15
Copy link

github-actions bot commented Jul 14, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@robincaloudis robincaloudis marked this pull request as ready for review July 14, 2024 22:37
@AngryLoki
Copy link
Contributor

AngryLoki commented Jul 15, 2024

Fails in gcc test... Looks like std::isnan _LIBCPP_PREFERRED_OVERLOAD also never worked in gcc too and it was not covered with tests. I guess it is possible to implement it in gcc (example: https://godbolt.org/z/vcrGWzcvj). But I don't know if this level of complication is needed. libstdc++ has no such thing as "preferred overload" and just fails with call to 'isfinite' is ambiguous when there are multiple user-defined conversion functions.

@ldionne
Copy link
Member

ldionne commented Jul 15, 2024

Thanks for the patch! Is there a reason why we must apply _LIBCPP_PREFERRED_OVERLOAD at all? This might be a naive question, but GCC implements the same thing without any trickery so I wonder why we need _LIBCPP_PREFERRED_OVERLOAD in the first place.

@robincaloudis robincaloudis force-pushed the rc-isfinite-overload branch 5 times, most recently from 8d9bd88 to 276d412 Compare July 15, 2024 22:07
@robincaloudis
Copy link
Contributor Author

robincaloudis commented Jul 16, 2024

Thanks for the hint! You two are completely right. _LIBCPP_PREFERRED_OVERLOAD is not needed. So far, all tests look good.

Once this is merged, I will also properly test the convertible types for std::{isnan, isinf}in #98952 as they seem to be broken due to the usage of _LIBCPP_PREFERRED_OVERLOAD.

Copy link
Contributor

@philnik777 philnik777 left a comment

Choose a reason for hiding this comment

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

Could you also check the other traits to make sure they're correctly implemented?

libcxx/test/std/numerics/c.math/isfinite.pass.cpp Outdated Show resolved Hide resolved
@robincaloudis
Copy link
Contributor Author

Could you also check the other traits to make sure they're correctly implemented?

Sure, I'll check them.

Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

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

LGTM once CI is done running. MacOS is a bit backed up right now due to an outage but it should get there eventually. Thanks!

libcxx/test/std/numerics/c.math/isfinite.pass.cpp Outdated Show resolved Hide resolved
@ldionne
Copy link
Member

ldionne commented Jul 16, 2024

Sorry, @philnik777 and I got in a race condition. Please make sure to satisfy Nikolas' requests as well. Thanks :-)

Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

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

Can be merged once CI is done running.

@robincaloudis
Copy link
Contributor Author

CI is done running. Could you merge this PR? I lack write access. Thanks!

@ldionne ldionne merged commit 7f2bd53 into llvm:main Jul 18, 2024
55 checks passed
@AngryLoki
Copy link
Contributor

That was quick! Thanks everyone!

Harini0924 pushed a commit to Harini0924/llvm-project that referenced this pull request Jul 22, 2024
sgundapa pushed a commit to sgundapa/upstream_effort that referenced this pull request Jul 23, 2024
yuxuanchen1997 pushed a commit that referenced this pull request Jul 25, 2024
…in std::isfinite() (#98841)

Summary: Closes #98816.

Test Plan: 

Reviewers: 

Subscribers: 

Tasks: 

Tags: 


Differential Revision: https://phabricator.intern.facebook.com/D60250840
ldionne pushed a commit that referenced this pull request Aug 16, 2024
…and `std::isinf()` (#98952)

Following up on #98841.

Changes:
- Properly test convertible types for `std::isnan()` and `std::inf()`
- Tighten conditional in `cmath.pass.cpp` (Find insights on `_LIBCPP_PREFERRED_OVERLOAD` below)
- Tighten preprocessor guard in `traits.h`

Insights into why `_LIBCPP_PREFERRED_OVERLOAD` is needed:

(i) When libc++ is layered on top of glibc on Linux, glibc's `math.h` is
    included. When compiling with `-std=c++03`, this header brings the
    function declaration of `isinf(double)` [1] and `isnan(double)` [2]
    into scope. This differs from the C99 Standard as only the macros
    `#define isnan(arg)` and `#define isinf(arg)` are expected.

    Therefore, libc++ needs to respect the presense of the `double` overload
    and cannot redefine it as it will conflict with the declaration already
    in scope. For `-std=c++11` and beyond this issue is fixed, as glibc
    guards both the `isinf` and `isnan` by preprocessor macros.

(ii) When libc++ is layered on top of Bionic's libc, `math.h` exposes a
     function prototype for `isinf(double)` with return type `int`. This
     function prototype in Bionic's libc is not guarded by any preprocessor
     macros [3].

`_LIBCPP_PREFERRED_OVERLOAD` specifies that a given overload is a better match
than an otherwise equally good function declaration. This is implemented in
modern versions of Clang via `__attribute__((__enable_if__))`, and not elsewhere.
See [4] for details. We use `_LIBCPP_PREFERRED_OVERLOAD` to define overloads in
the global namespace that displace the overloads provided by the C
libraries mentioned above.

[1]: https://github.com/bminor/glibc/blob/fe9408087583fd7a6f61bb0dbcf2fd4e83186afa/math/bits/mathcalls.h#L185-L194
[2]: https://github.com/bminor/glibc/blob/fe9408087583fd7a6f61bb0dbcf2fd4e83186afa/math/bits/mathcalls.h#L222-L231
[3]: https://cs.android.com/android/platform/superproject/+/master:bionic/libc/include/math.h;l=322-323;drc=master?hl=fr-BE%22https:%2F%2Fsupport.google.com%2Fmerchants%2Fanswer%2F188494%5C%22%22https:%2F%2Fsupport.google.com%2Fmerchants%2Fanswer%2F188494%5C%22
[4]: 5fd17ab
philnik777 pushed a commit that referenced this pull request Aug 28, 2024
…sfinite}` (#106224)

## Why
Since #98841 and
#98952, the constrained
overloads are unused and not needed anymore as we added explicit
overloads for all floating point types. I forgot to remove them in the
mentioned PRs.

## What
Remove them.
5c4lar pushed a commit to 5c4lar/llvm-project that referenced this pull request Aug 29, 2024
…sfinite}` (llvm#106224)

## Why
Since llvm#98841 and
llvm#98952, the constrained
overloads are unused and not needed anymore as we added explicit
overloads for all floating point types. I forgot to remove them in the
mentioned PRs.

## What
Remove them.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[libc++] std::isfinite() does not accept convertible-to-float
5 participants