Pierre Ossman <oss...@cendio.se> reported in <https://savannah.gnu.org/bugs/?67152>:
In file included from /BUILD/gettext-0.25/gettext-tools/src/msgmerge.c:36, from /BUILD/gettext-0.25/gettext-tools/woe32dll/c++msgmerge.cc:1: /BUILD/gettext-0.25/gettext-tools/src/msgmerge.c: In function 'int main(int, char**)': ../gnulib-lib/error.h:422:22: error: 'unreachable' was not declared in this scope 422 | (status) != 0 ? unreachable () : (void) 0) | ^~~~~~~~~~~ ../gnulib-lib/error.h:432:8: note: in expansion of macro '__gl_error_call1' 432 | ? __gl_error_call1 (function, status, __VA_ARGS__) \ | ^~~~~~~~~~~~~~~~ ../gnulib-lib/error.h:505:7: note: in expansion of macro '__gl_error_call' 505 | __gl_error_call (error, status, __VA_ARGS__) | ^~~~~~~~~~~~~~~ /BUILD/gettext-0.25/gettext-tools/src/msgmerge.c:390:7: note: in expansion of macro 'error' 390 | error (EXIT_SUCCESS, 0, _("no input files given")); | ^~~~~ We want <error.h> to be usable both in C and in C++ mode, without changes. Therefore it is best to make 'unreachable' available from <stddef.h> also in C++ mode. But without creating a conflict with 'unreachable' defined in C++ <utility> (cf. <https://en.cppreference.com/w/cpp/utility/unreachable>). This patch does it. 2025-05-27 Bruno Haible <br...@clisp.org> stddef-h: Make 'unreachable' usable in C++ mode. Reported by Pierre Ossman <oss...@cendio.se> at <https://savannah.gnu.org/bugs/?67152>. * m4/stddef_h.m4 (gl_STDDEF_H): Also test whether unreachable is defined by <stddef.h> in C++ mode. * lib/stddef.in.h: In C++ mode, include <utility> and either import 'unreachable' from the std namespace or define it as an inline function. * tests/test-stddef-h.c: Disable some tests in C++ mode. * tests/test-stddef-h-c++.cc: Perform nearly the same tests in C++ mode as in C mode. * tests/test-stddef-h-c++2.cc: Rename some variables. Disable the NULL test with clang on Windows. * tests/test-stddef-h-c++3.cc: New file. * modules/stddef-h-c++-tests (Files): Include it. (Makefile.am): Link test-stddef-h-c++ with test-stddef-h-c++3.o. diff --git a/lib/stddef.in.h b/lib/stddef.in.h index dc689b8df8..2754e881e7 100644 --- a/lib/stddef.in.h +++ b/lib/stddef.in.h @@ -188,38 +188,66 @@ typedef union #endif /* ISO C 23 ยง 7.21.1 The unreachable macro */ -#ifndef unreachable /* Code borrowed from verify.h. */ -# ifndef _GL_HAS_BUILTIN_UNREACHABLE -# if defined __clang_major__ && __clang_major__ < 5 -# define _GL_HAS_BUILTIN_UNREACHABLE 0 -# elif 4 < __GNUC__ + (5 <= __GNUC_MINOR__) && !defined __clang__ -# define _GL_HAS_BUILTIN_UNREACHABLE 1 -# elif defined __has_builtin -# define _GL_HAS_BUILTIN_UNREACHABLE __has_builtin (__builtin_unreachable) -# else -# define _GL_HAS_BUILTIN_UNREACHABLE 0 -# endif +#ifndef _GL_HAS_BUILTIN_UNREACHABLE +# if defined __clang_major__ && __clang_major__ < 5 +# define _GL_HAS_BUILTIN_UNREACHABLE 0 +# elif 4 < __GNUC__ + (5 <= __GNUC_MINOR__) && !defined __clang__ +# define _GL_HAS_BUILTIN_UNREACHABLE 1 +# elif defined __has_builtin +# define _GL_HAS_BUILTIN_UNREACHABLE __has_builtin (__builtin_unreachable) +# else +# define _GL_HAS_BUILTIN_UNREACHABLE 0 # endif +#endif -# if _GL_HAS_BUILTIN_UNREACHABLE -# define unreachable() __builtin_unreachable () -# elif 1200 <= _MSC_VER -# define unreachable() __assume (0) -# else +#if _GL_HAS_BUILTIN_UNREACHABLE +# define _gl_unreachable() __builtin_unreachable () +#elif 1200 <= _MSC_VER +# define _gl_unreachable() __assume (0) +#else /* Declare abort(), without including <stdlib.h>. */ extern -# if defined __cplusplus +# if defined __cplusplus "C" -# endif +# endif _Noreturn void abort (void) -# if defined __cplusplus && (__GLIBC__ >= 2) +# if defined __cplusplus && (__GLIBC__ >= 2) _GL_ATTRIBUTE_NOTHROW -# endif +# endif ; -# define unreachable() abort () +# define _gl_unreachable() abort () +#endif + +#ifndef __cplusplus +/* In C, define unreachable as a macro. */ + +# ifndef unreachable +# define unreachable() _gl_unreachable () +# endif + +#else +/* In C++, define unreachable as an inline function. */ + +/* With some versions of MSVC, the inclusion of <utility> here causes errors + when <cstddef> gets included: + type_traits(1164): error C2065: 'max_align_t': undeclared identifier */ +# if !defined _MSC_VER +extern "C++" { /* needed for Cygwin */ +# include <utility> +} +# endif + +# if defined __cpp_lib_unreachable /* C++23 or newer */ + +using std::unreachable; + +# else + +inline void unreachable () { _gl_unreachable (); } + # endif #endif diff --git a/m4/stddef_h.m4 b/m4/stddef_h.m4 index 3bc8cd85fe..bdcb428017 100644 --- a/m4/stddef_h.m4 +++ b/m4/stddef_h.m4 @@ -1,5 +1,5 @@ # stddef_h.m4 -# serial 21 +# serial 22 dnl Copyright (C) 2009-2025 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -64,20 +64,45 @@ AC_DEFUN_ONCE([gl_STDDEF_H] GL_GENERATE_STDDEF_H=true fi - AC_CACHE_CHECK([for unreachable], - [gl_cv_func_unreachable], + AC_CACHE_CHECK([for unreachable in C], + [gl_cv_c_func_unreachable], [AC_LINK_IFELSE( [AC_LANG_PROGRAM( [[#include <stddef.h> ]], [[unreachable (); ]])], - [gl_cv_func_unreachable=yes], - [gl_cv_func_unreachable=no]) + [gl_cv_c_func_unreachable=yes], + [gl_cv_c_func_unreachable=no]) ]) - if test $gl_cv_func_unreachable = no; then + if test $gl_cv_c_func_unreachable = no; then GL_GENERATE_STDDEF_H=true fi + if test "$CXX" != no; then + dnl C++ <utility> has std::unreachable, + dnl see <https://en.cppreference.com/w/cpp/utility/unreachable>, + dnl but we want an unreachable() that is available from <stddef.h>, + dnl like in ISO C 23. + AC_CACHE_CHECK([for unreachable in <stddef.h> in C++], + [gl_cv_cxx_func_unreachable], + [dnl We can't use AC_LANG_PUSH([C++]) and AC_LANG_POP([C++]) here, due to + dnl an autoconf bug <https://savannah.gnu.org/support/?110294>. + cat > conftest.cpp <<\EOF +#include <stddef.h> +int main (void) { unreachable (); } +EOF + gl_command="$CXX $CXXFLAGS $CPPFLAGS -c conftest.cpp" + if AC_TRY_EVAL([gl_command]); then + gl_cv_cxx_func_unreachable=yes + else + gl_cv_cxx_func_unreachable=no + fi + rm -fr conftest* + ]) + if test $gl_cv_cxx_func_unreachable = no; then + GL_GENERATE_STDDEF_H=true + fi + fi dnl https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114869 AC_CACHE_CHECK([whether nullptr_t needs <stddef.h>], diff --git a/modules/stddef-h-c++-tests b/modules/stddef-h-c++-tests index b46418837a..4067a13cfb 100644 --- a/modules/stddef-h-c++-tests +++ b/modules/stddef-h-c++-tests @@ -1,6 +1,7 @@ Files: tests/test-stddef-h-c++.cc tests/test-stddef-h-c++2.cc +tests/test-stddef-h-c++3.cc Status: c++-test @@ -14,5 +15,5 @@ Makefile.am: if ANSICXX TESTS += test-stddef-h-c++ check_PROGRAMS += test-stddef-h-c++ -test_stddef_h_c___SOURCES = test-stddef-h-c++.cc test-stddef-h-c++2.cc +test_stddef_h_c___SOURCES = test-stddef-h-c++.cc test-stddef-h-c++2.cc test-stddef-h-c++3.cc endif diff --git a/tests/test-stddef-h-c++.cc b/tests/test-stddef-h-c++.cc index 8d5b4a9b50..e7142f6105 100644 --- a/tests/test-stddef-h-c++.cc +++ b/tests/test-stddef-h-c++.cc @@ -17,12 +17,4 @@ /* Written by Bruno Haible <br...@clisp.org>, 2019. */ #define GNULIB_NAMESPACE gnulib -#include <config.h> - -#include <stddef.h> - - -int -main () -{ -} +#include "test-stddef-h.c" diff --git a/tests/test-stddef-h-c++2.cc b/tests/test-stddef-h-c++2.cc index e207976d13..a9d25c2e49 100644 --- a/tests/test-stddef-h-c++2.cc +++ b/tests/test-stddef-h-c++2.cc @@ -23,10 +23,10 @@ <https://en.cppreference.com/w/cpp/header/cstddef>. */ /* Check that appropriate types are defined. */ -ptrdiff_t b = 1; -size_t c = 2; +ptrdiff_t b2 = 1; +size_t c2 = 2; -#if !defined __cplusplus || defined __GNUC__ || defined __clang__ +#if !(defined __cplusplus && defined _MSC_VER) /* Check that NULL can be passed through varargs as a pointer type, per POSIX 2008. */ static_assert (sizeof NULL == sizeof (void *)); diff --git a/tests/test-stddef-h-c++3.cc b/tests/test-stddef-h-c++3.cc new file mode 100644 index 0000000000..77242183c8 --- /dev/null +++ b/tests/test-stddef-h-c++3.cc @@ -0,0 +1,44 @@ +/* Test of <stddef.h> substitute in C++ mode. + Copyright (C) 2019-2025 Free Software Foundation, Inc. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 program. If not, see <https://www.gnu.org/licenses/>. */ + +#include <config.h> + +/* Define unreachable. */ +#include <stddef.h> +/* Define std::unreachable (in C++23 or newer). */ +#include <utility> + +void +test_cxx_unreachable_1 () +{ + if (2 < 1) + unreachable (); +#if defined __cpp_lib_unreachable + if (3 < 1) + std::unreachable (); +#endif +} + +#if defined __cpp_lib_unreachable +using std::unreachable; + +void +test_cxx_unreachable_2 () +{ + if (3 < 1) + unreachable (); +} +#endif diff --git a/tests/test-stddef-h.c b/tests/test-stddef-h.c index 1e577d0787..f201b411ab 100644 --- a/tests/test-stddef-h.c +++ b/tests/test-stddef-h.c @@ -24,11 +24,15 @@ wchar_t a = 'c'; ptrdiff_t b = 1; size_t c = 2; +#if !defined __cplusplus || __cplusplus >= 201103 max_align_t mat; +#endif +#if !(defined __cplusplus && defined _MSC_VER) /* Check that NULL can be passed through varargs as a pointer type, per POSIX 2008. */ static_assert (sizeof NULL == sizeof (void *)); +#endif /* Check that offsetof produces integer constants with correct type. */ struct d @@ -43,6 +47,7 @@ struct d static_assert (sizeof (offsetof (struct d, e)) == sizeof (size_t)); static_assert (offsetof (struct d, f) == 1); +#if !defined __cplusplus || __cplusplus >= 201103 /* Check max_align_t's alignment. */ static_assert (alignof (double) <= alignof (max_align_t)); static_assert (alignof (int) <= alignof (max_align_t)); @@ -52,7 +57,7 @@ static_assert (alignof (ptrdiff_t) <= alignof (max_align_t)); static_assert (alignof (size_t) <= alignof (max_align_t)); static_assert (alignof (wchar_t) <= alignof (max_align_t)); static_assert (alignof (struct d) <= alignof (max_align_t)); -#if defined __GNUC__ || defined __clang__ || defined __IBM__ALIGNOF__ +# if defined __GNUC__ || defined __clang__ || defined __IBM__ALIGNOF__ static_assert (__alignof__ (double) <= __alignof__ (max_align_t)); static_assert (__alignof__ (int) <= __alignof__ (max_align_t)); static_assert (__alignof__ (long double) <= __alignof__ (max_align_t)); @@ -61,6 +66,7 @@ static_assert (__alignof__ (ptrdiff_t) <= __alignof__ (max_align_t)); static_assert (__alignof__ (size_t) <= __alignof__ (max_align_t)); static_assert (__alignof__ (wchar_t) <= __alignof__ (max_align_t)); static_assert (__alignof__ (struct d) <= __alignof__ (max_align_t)); +# endif #endif int test_unreachable_optimization (int x);