Glibc defines obsolete isinf(double) and isnan(double) functions from Unix98. For C99 these are hidden by the type-generic isinf and isnan macros, but in C++11 <cmath> #undefs those macros, exposing the Unix98 functions. They then conflict with our own std::isinf(double) and std::isnan(double) if users add them to the global namespace:
#include <cmath> using std::isinf; d.cc:3:12: error: ‘constexpr bool std::isinf(double)’ conflicts with a previous declaration using std::isinf; ^ This has been reported three times now, and with the fix I'm planning for PR14608 this becomes essential to fix, because I'm adding a <math.h> wrapper containing exactly the code above. At https://sourceware.org/bugzilla/show_bug.cgi?id=19439 I've proposed a glibc patch to suppress those functions for C++11 and later, but we still need to make libstdc++ work with existing glibc releases. This patch adds a configure check for non-macro isinf and isnan declarations, and pulls them into namespace std when they're found. This means we define the functions with the wrong return type (int not bool) but that's better than having conflicting declarations that don't compile at all! I'm only checking for those functions for *-*-*gnu* targets, as I don't know of any other targets where it's an issue. Solaris and the BSDs don't define those functions. If it affects other targets we can extend the check to cover them too. With a fixed glibc (or other C libraries that don't declare those functions) we continue to define extern "C++" bool std::isinf(double), which is the correct signature and mangles differently to the glibc functions. I've tested this on x86_64-linux with and without the glibc fix, and on powerpc64-linux and x86_64-freebsd10.2 and although the interaction between libc and libstdc++ is always tricky, I'm confident it's safe and certainly more correct than what we have now. We could potentially even backport it to the branches. Does anyone see any problem with this patch, or the proposed glibc change?
commit e5a8514077918b7885266ab3e136ae7b8ad65f99 Author: Jonathan Wakely <jwak...@redhat.com> Date: Thu Jan 7 16:01:09 2016 +0000 Use ::isinf and ::isnan if libc defines them PR libstdc++/48891 * acinclude.m4 (GLIBCXX_CHECK_MATH11_PROTO): Check for obsolete isinf and isnan functions. * config.h.in: Regenerate. * configure: Regenerate. * include/c_global/cmath (isinf(double), isnan(double)) [_GLIBCXX_HAVE_OBSOLETE_ISINF_ISNAN]: Import via using-directive. * testsuite/26_numerics/headers/cmath/48891.cc: New. diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4 index 75e4667..b76e8d5 100644 --- a/libstdc++-v3/acinclude.m4 +++ b/libstdc++-v3/acinclude.m4 @@ -2186,6 +2186,40 @@ AC_DEFUN([GLIBCXX_CHECK_MATH11_PROTO], [ fi AC_MSG_RESULT([$glibcxx_cv_math11_overload]) ;; + *-*-*gnu*) + # If <math.h> defines the obsolete isinf(double) and isnan(double) + # functions (instead of or as well as the C99 generic macros) then we + # can't define std::isinf(double) and std::isnan(double) in <cmath> + # and must use the ones from <math.h> instead. + AC_MSG_CHECKING([for obsolete isinf and isnan functions in <math.h>]) + AC_CACHE_VAL(glibcxx_cv_obsolete_isinf_isnan, [ + AC_COMPILE_IFELSE([AC_LANG_SOURCE( + [#include <math.h> + #undef isinf + #undef isnan + namespace std { + using ::isinf; + bool isinf(float); + bool isinf(long double); + using ::isnan; + bool isnan(float); + bool isnan(long double); + } + using std::isinf; + using std::isnan; + bool b = isinf(0.0) || isnan(0.0); + ])], + [glibcxx_cv_obsolete_isinf_isnan=yes], + [glibcxx_cv_obsolete_isinf_isnan=no] + )]) + + + if test $glibcxx_cv_obsolete_isinf_isnan = yes; then + AC_DEFINE(HAVE_OBSOLETE_ISINF_ISNAN, 1, + [Define if <math.h> defines obsolete isinf and isnan functions.]) + fi + AC_MSG_RESULT([$glibcxx_cv_obsolete_isinf_isnan]) + ;; esac CXXFLAGS="$ac_save_CXXFLAGS" diff --git a/libstdc++-v3/include/c_global/cmath b/libstdc++-v3/include/c_global/cmath index da7ec28..6269e32 100644 --- a/libstdc++-v3/include/c_global/cmath +++ b/libstdc++-v3/include/c_global/cmath @@ -606,9 +606,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION isinf(float __x) { return __builtin_isinf(__x); } +#ifdef _GLIBCXX_HAVE_OBSOLETE_ISINF_ISNAN + using ::isinf; +#else constexpr bool isinf(double __x) { return __builtin_isinf(__x); } +#endif constexpr bool isinf(long double __x) @@ -626,9 +630,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION isnan(float __x) { return __builtin_isnan(__x); } +#ifdef _GLIBCXX_HAVE_OBSOLETE_ISINF_ISNAN + using ::isnan; +#else constexpr bool isnan(double __x) { return __builtin_isnan(__x); } +#endif constexpr bool isnan(long double __x) diff --git a/libstdc++-v3/testsuite/26_numerics/headers/cmath/48891.cc b/libstdc++-v3/testsuite/26_numerics/headers/cmath/48891.cc new file mode 100644 index 0000000..ef94fe1 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/headers/cmath/48891.cc @@ -0,0 +1,30 @@ +// Copyright (C) 2016 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options "-std=gnu++11" } +// { dg-do compile } + +// PR libstdc++/48891 + +#include <math.h> +#include <cmath> + +using std::isinf; +using std::isnan; + +bool d1 = isinf(1.0); +bool d2 = isnan(1.0);