-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[libcxx] Undefine all supported C math functions #94533
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
base: main
Are you sure you want to change the base?
Conversation
The C standard allows these to be defined as macros which is problem when providing the C++ versions and so we need to undefine the C macros first. Rather than undefining these selectively, we should undefine all of them since we have any guarantees which are going to be defined as macros and it depeneds on the target platform. We don't need to guard these with #ifdef since undefining a symbol that's not defined is not an error. This is related to issue llvm#84879.
@llvm/pr-subscribers-libcxx Author: Petr Hosek (petrhosek) ChangesThe C standard allows these to be defined as macros which is problem when providing the C++ versions and so we need to undefine the C macros first. Rather than undefining these selectively, we should undefine all of them since we have any guarantees which are going to be defined as macros and it depeneds on the target platform. We don't need to guard these with #ifdef since undefining a symbol that's not defined is not an error. This is related to issue #84879. Full diff: https://github.com/llvm/llvm-project/pull/94533.diff 2 Files Affected:
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 4e6304a753984..893dca8dc1c17 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -307,53 +307,74 @@ long double truncl(long double x);
// back to C++ linkage before including these C++ headers.
extern "C++" {
-# ifdef fpclassify
-# undef fpclassify
-# endif
-
-# ifdef signbit
-# undef signbit
-# endif
-
-# ifdef isfinite
-# undef isfinite
-# endif
-
-# ifdef isinf
-# undef isinf
-# endif
-
-# ifdef isnan
-# undef isnan
-# endif
-
-# ifdef isnormal
-# undef isnormal
-# endif
-
-# ifdef isgreater
-# undef isgreater
-# endif
-
-# ifdef isgreaterequal
-# undef isgreaterequal
-# endif
-
-# ifdef isless
-# undef isless
-# endif
-
-# ifdef islessequal
-# undef islessequal
-# endif
-
-# ifdef islessgreater
-# undef islessgreater
-# endif
-
-# ifdef isunordered
-# undef isunordered
-# endif
+# undef acos
+# undef acosh
+# undef asin
+# undef asinh
+# undef atan
+# undef atan2
+# undef atanh
+# undef cbrt
+# undef ceil
+# undef copysign
+# undef cos
+# undef cosh
+# undef erf
+# undef erfc
+# undef exp
+# undef exp2
+# undef expm1
+# undef fabs
+# undef fdim
+# undef floor
+# undef fma
+# undef fmax
+# undef fmin
+# undef fmod
+# undef fpclassify
+# undef frexp
+# undef hypot
+# undef ilogb
+# undef isfinite
+# undef isgreater
+# undef isgreaterequal
+# undef isinf
+# undef isless
+# undef islessequal
+# undef islessgreater
+# undef isnan
+# undef isnormal
+# undef isunordered
+# undef ldexp
+# undef lgamma
+# undef llrint
+# undef llround
+# undef log
+# undef log10
+# undef log1p
+# undef log2
+# undef logb
+# undef lrint
+# undef lround
+# undef modf
+# undef nearbyint
+# undef nextafter
+# undef nexttoward
+# undef pow
+# undef remainder
+# undef remquo
+# undef rint
+# undef round
+# undef scalbln
+# undef scalbn
+# undef signbit
+# undef sin
+# undef sinh
+# undef sqrt
+# undef tan
+# undef tanh
+# undef tgamma
+# undef trunc
# include <__math/abs.h>
# include <__math/copysign.h>
diff --git a/libcxx/include/stdlib.h b/libcxx/include/stdlib.h
index a74344d49150c..b1151b58a356f 100644
--- a/libcxx/include/stdlib.h
+++ b/libcxx/include/stdlib.h
@@ -98,15 +98,9 @@ void *aligned_alloc(size_t alignment, size_t size); // C11
extern "C++" {
// abs
-# ifdef abs
-# undef abs
-# endif
-# ifdef labs
-# undef labs
-# endif
-# ifdef llabs
-# undef llabs
-# endif
+# undef abs
+# undef labs
+# undef llabs
// MSVCRT already has the correct prototype in <stdlib.h> if __cplusplus is defined
# if !defined(_LIBCPP_MSVCRT)
@@ -128,15 +122,9 @@ _LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI long double abs(long double __lcp
// div
-# ifdef div
-# undef div
-# endif
-# ifdef ldiv
-# undef ldiv
-# endif
-# ifdef lldiv
-# undef lldiv
-# endif
+# undef div
+# undef ldiv
+# undef lldiv
// MSVCRT already has the correct prototype in <stdlib.h> if __cplusplus is defined
# if !defined(_LIBCPP_MSVCRT)
|
Looking at isnan as an example, it looks like
Locally, mine is defined as
So the macros are still being used it seems. |
So the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This breaks if one of the __math/
headers is included before math.h
is included. I also don't understand what the failure mode is here. These macros should always be #undef
ed right after they are defined.
We trying to use libc++ with FreeRTOS which defines some of the |
Are you sure that the C standard allows this? The ones we |
Yes, this is explicitly mentioned in the section 7.1.4 Use of library functions of the C standard:
|
Interesting. But wouldn't that mean that we'd technically also have to |
Just as philnik777 points out, it looks like that's what's happening here. For isnan, it's first
then we include
|
I have reverted to the earlier implementation which shouldn't have this issue and include a comment with explanation for why this is needed.
I believe we only need to do this for functions where we provide our own definition in libc++. |
In C++ users are free to write namespace my_ns {
void memcpy() {}
} though, which would break if |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I agree with @philnik777 's comments about this being needed for every C library function.
Basically, we have to choose whether libc++ works on top of a conforming C Standard Library per the C Standard, or whether we require C++ friendliness in a C Standard Library for it to be usable with libc++.
If we support an arbitrary conforming C Standard Library, then per 7.1.4 it does seem like they are allowed to define functions as macros, which is extremely unfriendly to C++ due to namespaces amongst other things. I've come across this issue (with memcpy
for example) several times when porting libc++ to new platforms.
So far, what I've always done was to ask the underlying C library to stop using macros because that is C++-unfriendly. However, it might be reasonable for libc++ to support that.
Doing this correctly in the general case is going to be non-trivial, though, since it basically means that we can't blindly reuse names from the C Standard Library anymore. For example, strxfrm
isn't defined anywhere by libc++, we just reuse it from the C stdlib:
// <cstring>
namespace std {
using ::strxfrm;
}
If we wanted to work in spite of §7.1.4, we'd basically have to #undef strxfrm
and then define it ourselves as a function. I believe the logical conclusion here is that we basically can't reuse anything from the C Standard Library, we'd have to implement everything ourselves.
This makes me wonder whether that is a viable approach. Thoughts?
# ifdef isunordered | ||
# undef isunordered | ||
# endif | ||
// According to section 7.1.4 Use of library functions of the C standard, any |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO it would make more sense to undefine each macro where the corresponding libc++ function is implemented. For example in
#undef cosh
inline _LIBCPP_HIDE_FROM_ABI float cosh(float __x) _NOEXCEPT { return __builtin_coshf(__x); }
// etc...
in libcxx/include/__math/hyperbolic_functions.h
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried that in 745745c but that lead to build errors, see #94533 (comment). I noticed that the particular <cmath>
include that triggered that build error was removed by @philnik777 in 0dd8c0d so it might worth trying it again and see if there are any remaining issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I read that comment, but I still don't understand what's happening that would prevent the #undef
from being located right before the definition of the libc++ function with the same name. It seems like we need exactly one canonical place where we #undef foo
and then define namespace std { int foo(); }
, and I don't see how this can lead to problems?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ldionne Consider
#include <__math/traits.h>
#include <math.h>
The __math/traits.h
would try to #undef
a macro that will only be defined when including math.h
. Does that make it clearer?
@petrhosek Do you plan to pursue this? |
You're right that you need So it's entirely sufficient and entirely conforms with the C standard to write |
Ah ah! Thanks a lot for providing this insight, that was missing from my understanding. A lot of things make more sense now. In that case, I believe the right path forward would be to |
We'd also have to |
@petrhosek Do you plan to pursue this again? |
The C standard allows these to be defined as macros which is problem when providing the C++ versions and so we need to undefine the C macros first. Rather than undefining these selectively, we should undefine all of them since we have any guarantees which are going to be defined as macros and it depeneds on the target platform.
We don't need to guard these with #ifdef since undefining a symbol that's not defined is not an error.
This is related to issue #84879.