Pierre Ossman <[email protected]> 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 <[email protected]>
stddef-h: Make 'unreachable' usable in C++ mode.
Reported by Pierre Ossman <[email protected]>
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 <[email protected]>, 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);