Skip to content

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
Member

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
@github-actions
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 robincaloudis force-pushed the rc-isfinite-overload branch from 276d412 to c858bb6 Compare July 16, 2024 05:56
@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?

@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!

@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 :-)

@robincaloudis robincaloudis force-pushed the rc-isfinite-overload branch from 2e6a04d to 8d56623 Compare July 16, 2024 18:55
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
@AngryLoki
Copy link
Contributor

That was quick! Thanks everyone!

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