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);




Reply via email to