On 2026-07-29 Bruno Haible wrote:
> Lasse Collin wrote:
> > The tests should pass once C2y is finished and supported by
> > toolchains, but I suppose it's not useful to add them behind "#if
> > 0" now.
>
> Yes. *Now* is too early. In 10-15 years maybe (because we need to
> consider also the toolchains of then "old" platforms).

I have a hunch that only GCC < 15 and glibc's headers are problematic,
so excluding those might keep "make check" happy everywhere. If it
doesn't, then it reveals what other platforms have problems with N3322.

I attached a draft patch to demonstrate the idea. It is optimistic and
assumes that glibc 2.45 will have updated headers. The tests pass on
GNU/Linux (glibc 2.24, 2.41, and 2.43, and musl 1.2.6; a few GCC
versions from 4.9 to 16.1, and Clang 22) and on FreeBSD (Clang),
OpenBSD (Clang), NetBSD (Clang, GCC), AIX (ibm-clang, GCC), and Solaris
(Developer Studio).

If this isn't a good idea, then let's go with the shorter patch from
the earlier email.

> > For some reason glibc's <wchar.h> doesn't have the nonnull
> > attribute in the prototypes of these functions. Anyway, I attached
> > an updated patch in case it is useful (feel free to edit etc.).
>
> Thanks! This patch could go in.
[...]
> So, we can apply this (good) patch only once the paperwork with the
> FSF is complete.

OK. The instruction say "print, sign, and then email (or fax) a scanned
copy", which sounds easy enough. I assume I should use
doc/Copyright/request-assign.future. Let's continue this off-list.

-- 
Lasse Collin
>From 4adf8040229a55c54e29de2d5e7350776a039738 Mon Sep 17 00:00:00 2001
From: Lasse Collin <[email protected]>
Date: Thu, 30 Jul 2026 21:01:52 +0300
Subject: [PATCH] tests: Verify N3322 compatibility of the function prototypes

Test that the compiler doesn't optimize a NULL check away when it sees
the prototypes in the headers <string.h>, <wchar.h>, and <stdlib.h>.
Exclude GCC and glibc versions that are known to be problematic.
(The existing tests explicitly avoided the effects of the attributes
in the function prototypes.)
* tests/test-bsearch.c: Add the test null_bsearch.
* tests/test-memccpy.c: Add the test null_memccpy.
* tests/test-memchr.c: Add the test null_memchr.
* tests/test-memcmp.c: Add the test null_memcmp.
* tests/test-memcpy.c: Add the test null_memcpy.
* tests/test-memmove.c: Add the test null_memmove.
* tests/test-memset.c: Add the test null_memset.
* tests/test-memset_explicit.c: Add the test null_memset_explicit.
* tests/test-qsort.c: Add the test null_qsort.
* tests/test-strncat.c: Add the test null_strncat.
* tests/test-strncmp.c: Add the test null_strncmp.
* tests/test-strncpy.c: Add the test null_strncpy.
* tests/test-strndup.c: Add the test null_strndup.
* tests/test-wcsncat.c: Add the test null_wcsncat.
* tests/test-wcsncmp.c: Add the test null_wcsncmp.
* tests/test-wcsncpy.c: Add the test null_wcsncpy.
* tests/test-wmemchr.c: Add the test null_wmemchr.
* tests/test-wmemcmp.c: Add the test null_wmemcmp.
* tests/test-wmemcpy.c: Add the test null_wmemcpy.
* tests/test-wmemmove.c: Add the test null_wmemmove.
* tests/test-wmemset.c: Add the test null_wmemset.
---
 tests/test-bsearch.c         | 17 +++++++++++++++++
 tests/test-memccpy.c         | 15 +++++++++++++++
 tests/test-memchr.c          | 17 +++++++++++++++++
 tests/test-memcmp.c          | 17 +++++++++++++++++
 tests/test-memcpy.c          | 19 +++++++++++++++++++
 tests/test-memmove.c         | 17 +++++++++++++++++
 tests/test-memset.c          | 17 +++++++++++++++++
 tests/test-memset_explicit.c | 15 +++++++++++++++
 tests/test-qsort.c           | 17 +++++++++++++++++
 tests/test-strncat.c         | 17 +++++++++++++++++
 tests/test-strncmp.c         | 17 +++++++++++++++++
 tests/test-strncpy.c         | 17 +++++++++++++++++
 tests/test-strndup.c         | 17 +++++++++++++++++
 tests/test-wcsncat.c         | 15 +++++++++++++++
 tests/test-wcsncmp.c         | 15 +++++++++++++++
 tests/test-wcsncpy.c         | 15 +++++++++++++++
 tests/test-wmemchr.c         | 12 ++++++++++++
 tests/test-wmemcmp.c         | 12 ++++++++++++
 tests/test-wmemcpy.c         | 12 ++++++++++++
 tests/test-wmemmove.c        | 12 ++++++++++++
 tests/test-wmemset.c         | 12 ++++++++++++
 21 files changed, 324 insertions(+)

diff --git a/tests/test-bsearch.c b/tests/test-bsearch.c
index 6f318256cb..5c773d1410 100644
--- a/tests/test-bsearch.c
+++ b/tests/test-bsearch.c
@@ -21,6 +21,20 @@
 
 #include "macros.h"
 
+/* Test the prototype in <stdlib.h> + compiler.
+   In glibc >= 2.18, <bits/stdlib-bsearch.h> has an inline version.
+   Some glibc versions use the nonnull attribute, which breaks this test.  */
+static void *
+null_bsearch (void const *key, void const *base, size_t nel, size_t width,
+             int (*compar) (void const *, void const *))
+{
+  void const *p = bsearch (key, base, nel, width, compar);
+#if ! defined __GLIBC__ || 2 < __GLIBC__ + (45 <= __GLIBC_MINOR__)
+  ASSERT (base == NULL);
+#endif
+  return (void *) p;
+}
+
 /* Test the library, not the compiler+library.  */
 static void *
 lib_bsearch (void const *key, void const *base, size_t nel, size_t width,
@@ -48,5 +62,8 @@ main (void)
      <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3322.pdf>.  */
   ASSERT (bsearch ("x", NULL, 0, 1, cmp) == NULL);
 
+  volatile_bsearch = null_bsearch;
+  ASSERT (bsearch ("x", NULL, 0, 1, cmp) == NULL);
+
   return test_exit_status;
 }
diff --git a/tests/test-memccpy.c b/tests/test-memccpy.c
index 4232e36fb1..966d224708 100644
--- a/tests/test-memccpy.c
+++ b/tests/test-memccpy.c
@@ -23,6 +23,18 @@
 
 #include "macros.h"
 
+/* Test the prototype in <string.h> + compiler.
+   Some glibc versions use the nonnull attribute, which breaks this test.  */
+static void *
+null_memccpy (void *dest, const void *src, int c, size_t n)
+{
+  void *p = memccpy (dest, src, c, n);
+#if ! defined __GLIBC__ || 2 < __GLIBC__ + (45 <= __GLIBC_MINOR__)
+  ASSERT (dest == NULL);
+#endif
+  return p;
+}
+
 /* Test the library, not the compiler+library.  */
 static void *
 lib_memccpy (void *dest, void const *src, int c, size_t n)
@@ -47,5 +59,8 @@ main (void)
     ASSERT (memccpy (y, NULL, '?', 0) == NULL);
   }
 
+  volatile_memccpy = null_memccpy;
+  ASSERT (memccpy (NULL, "x", '?', 0) == NULL);
+
   return test_exit_status;
 }
diff --git a/tests/test-memchr.c b/tests/test-memchr.c
index 7d72dbac5b..cd79f67d56 100644
--- a/tests/test-memchr.c
+++ b/tests/test-memchr.c
@@ -27,6 +27,20 @@ SIGNATURE_CHECK (memchr, void *, (void const *, int, size_t));
 #include "zerosize-ptr.h"
 #include "macros.h"
 
+/* Test the prototype in <string.h> + compiler.
+   In GCC < 15 this is a builtin that has the nonnull attribute.
+   Some glibc versions use the nonnull attribute, which breaks this test.  */
+static void *
+null_memchr (void const *s, int c, size_t n)
+{
+  const void *p = memchr (s, c, n);
+#if (! defined __GNUC__ || __GNUC__ >= 15 || defined __clang__) \
+    && (! defined __GLIBC__ || 2 < __GLIBC__ + (45 <= __GLIBC_MINOR__))
+  ASSERT (s == NULL);
+#endif
+  return (void *) p;
+}
+
 /* Test the library, not the compiler+library.  */
 static void *
 lib_memchr (void const *s, int c, size_t n)
@@ -138,5 +152,8 @@ main (void)
      <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3322.pdf>.  */
   ASSERT (memchr (NULL, '?', 0) == NULL);
 
+  volatile_memchr = null_memchr;
+  ASSERT (memchr (NULL, '?', 0) == NULL);
+
   return test_exit_status;
 }
diff --git a/tests/test-memcmp.c b/tests/test-memcmp.c
index d773652214..8f6fc927af 100644
--- a/tests/test-memcmp.c
+++ b/tests/test-memcmp.c
@@ -25,6 +25,20 @@ SIGNATURE_CHECK (memcmp, int, (void const *, void const *, size_t));
 #include "zerosize-ptr.h"
 #include "macros.h"
 
+/* Test the prototype in <string.h> + compiler.
+   In GCC < 15 this is a builtin that has the nonnull attribute.
+   Some glibc versions use the nonnull attribute, which breaks this test.  */
+static int
+null_memcmp (void const *s1, void const *s2, size_t n)
+{
+  int r = memcmp (s1, s2, n);
+#if (! defined __GNUC__ || __GNUC__ >= 15 || defined __clang__) \
+    && (! defined __GLIBC__ || 2 < __GLIBC__ + (45 <= __GLIBC_MINOR__))
+  ASSERT (s1 == NULL);
+#endif
+  return r;
+}
+
 /* Test the library, not the compiler+library.  */
 static int
 lib_memcmp (void const *s1, void const *s2, size_t n)
@@ -86,5 +100,8 @@ main (void)
   ASSERT (memcmp ("x", NULL, 0) == 0);
   ASSERT (memcmp (NULL, NULL, 0) == 0);
 
+  volatile_memcmp = null_memcmp;
+  ASSERT (memcmp (NULL, "x", 0) == 0);
+
   return test_exit_status;
 }
diff --git a/tests/test-memcpy.c b/tests/test-memcpy.c
index 180525fca6..28165a7042 100644
--- a/tests/test-memcpy.c
+++ b/tests/test-memcpy.c
@@ -23,6 +23,20 @@
 
 #include "macros.h"
 
+/* Test the prototype in <string.h> + compiler.
+   In GCC < 15 this is a builtin that has the nonnull attribute.
+   Some glibc versions use the nonnull attribute, which breaks this test.  */
+static void *
+null_memcpy (void *dest, const void *src, size_t n)
+{
+  void *p = memcpy (dest, src, n);
+#if (! defined __GNUC__ || __GNUC__ >= 15 || defined __clang__) \
+    && (! defined __GLIBC__ || 2 < __GLIBC__ + (45 <= __GLIBC_MINOR__))
+  ASSERT (dest == NULL);
+#endif
+  return p;
+}
+
 /* Test the library, not the compiler+library.  */
 static void *
 lib_memcpy (void *s1, void const *s2, size_t n)
@@ -47,5 +61,10 @@ main (void)
     ASSERT (memcpy (y, NULL, 0) == y);
   }
 
+  /* Redirect via the volatile function pointer to ensure that that the ASSERT
+     in null_memcpy won't be optimized away by GCC 6.3.0.  */
+  volatile_memcpy = null_memcpy;
+  ASSERT (memcpy (NULL, "x", 0) == NULL);
+
   return test_exit_status;
 }
diff --git a/tests/test-memmove.c b/tests/test-memmove.c
index 438d5aaa83..71c428805e 100644
--- a/tests/test-memmove.c
+++ b/tests/test-memmove.c
@@ -23,6 +23,20 @@
 
 #include "macros.h"
 
+/* Test the prototype in <string.h> + compiler.
+   In GCC < 15 this is a builtin that has the nonnull attribute.
+   Some glibc versions use the nonnull attribute, which breaks this test.  */
+static void *
+null_memmove (void *dest, const void *src, size_t n)
+{
+  void * p = memmove (dest, src, n);
+#if (! defined __GNUC__ || __GNUC__ >= 15 || defined __clang__) \
+    && (! defined __GLIBC__ || 2 < __GLIBC__ + (45 <= __GLIBC_MINOR__))
+  ASSERT (dest == NULL);
+#endif
+  return p;
+}
+
 /* Test the library, not the compiler+library.  */
 static void *
 lib_memmove (void *s1, void const *s2, size_t n)
@@ -47,5 +61,8 @@ main (void)
     ASSERT (memmove (y, NULL, 0) == y);
   }
 
+  volatile_memmove = null_memmove;
+  ASSERT (memmove (NULL, "x", 0) == NULL);
+
   return test_exit_status;
 }
diff --git a/tests/test-memset.c b/tests/test-memset.c
index be744d0dfd..4781aed459 100644
--- a/tests/test-memset.c
+++ b/tests/test-memset.c
@@ -23,6 +23,20 @@
 
 #include "macros.h"
 
+/* Test the prototype in <string.h> + compiler.
+   In GCC < 15 this is a builtin that has the nonnull attribute.
+   Some glibc versions use the nonnull attribute, which breaks this test.  */
+static void *
+null_memset (void *s, int c, size_t n)
+{
+  void *p = memset (s, c, n);
+#if (! defined __GNUC__ || __GNUC__ >= 15 || defined __clang__) \
+    && (! defined __GLIBC__ || 2 < __GLIBC__ + (45 <= __GLIBC_MINOR__))
+  ASSERT (s == NULL);
+#endif
+  return p;
+}
+
 /* Test the library, not the compiler+library.  */
 static void *
 lib_memset (void *s, int c, size_t n)
@@ -41,5 +55,8 @@ main (void)
      <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3322.pdf>.  */
   ASSERT (memset (NULL, '?', 0) == NULL);
 
+  volatile_memset = null_memset;
+  ASSERT (memset (NULL, '?', 0) == NULL);
+
   return test_exit_status;
 }
diff --git a/tests/test-memset_explicit.c b/tests/test-memset_explicit.c
index d02431b4bb..0ecf2a5b3e 100644
--- a/tests/test-memset_explicit.c
+++ b/tests/test-memset_explicit.c
@@ -48,6 +48,18 @@ static char zero[SECRET_SIZE] = { 0 };
 # define memset_explicit memset
 #endif
 
+/* Test the prototype in <string.h> + compiler.
+   Some glibc versions use the nonnull attribute, which breaks this test.  */
+static void *
+null_memset_explicit (void *s, int c, size_t n)
+{
+  void *p = memset_explicit (s, c, n);
+#if ! defined __GLIBC__ || 2 < __GLIBC__ + (45 <= __GLIBC_MINOR__)
+  ASSERT (s == NULL);
+#endif
+  return p;
+}
+
 /* Test the library, not the compiler+library.  */
 static void *
 lib_memset_explicit (void *s, int c, size_t n)
@@ -268,5 +280,8 @@ main ()
      <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3322.pdf>.  */
   ASSERT (memset_explicit (NULL, '?', 0) == NULL);
 
+  volatile_memset_explicit = null_memset_explicit;
+  ASSERT (memset_explicit (NULL, '?', 0) == NULL);
+
   return test_exit_status;
 }
diff --git a/tests/test-qsort.c b/tests/test-qsort.c
index 43c4909f03..055911552f 100644
--- a/tests/test-qsort.c
+++ b/tests/test-qsort.c
@@ -19,6 +19,20 @@
 /* Specification.  */
 #include <stdlib.h>
 
+#include "macros.h"
+
+/* Test the prototype in <stdlib.h> + compiler.
+   Some glibc versions use the nonnull attribute, which breaks this test.  */
+static void
+null_qsort (void *base, size_t nel, size_t width,
+            int (*compar) (void const *, void const *))
+{
+  qsort (base, nel, width, compar);
+#if ! defined __GLIBC__ || 2 < __GLIBC__ + (45 <= __GLIBC_MINOR__)
+  ASSERT (base == NULL);
+#endif
+}
+
 /* Test the library, not the compiler+library.  */
 static void
 lib_qsort (void *base, size_t nel, size_t width,
@@ -46,5 +60,8 @@ main (void)
 
   qsort (NULL, 0, 1, cmp);
 
+  volatile_qsort = null_qsort;
+  qsort (NULL, 0, 1, cmp);
+
   return 0;
 }
diff --git a/tests/test-strncat.c b/tests/test-strncat.c
index 1817bb9d10..70567edeec 100644
--- a/tests/test-strncat.c
+++ b/tests/test-strncat.c
@@ -29,6 +29,20 @@ SIGNATURE_CHECK (strncat, char *, (char *, const char *, size_t));
 #include "zerosize-ptr.h"
 #include "macros.h"
 
+/* Test the prototype in <string.h> + compiler.
+   In GCC < 15 this is a builtin that has the nonnull attribute.
+   Some glibc versions use the nonnull attribute, which breaks this test.  */
+static char *
+null_strncat (char *s1, char const *s2, size_t n)
+{
+  char *p = strncat (s1, s2, n);
+#if (! defined __GNUC__ || __GNUC__ >= 15 || defined __clang__) \
+    && (! defined __GLIBC__ || 2 < __GLIBC__ + (45 <= __GLIBC_MINOR__))
+  ASSERT (s2 == NULL);
+#endif
+  return p;
+}
+
 /* Test the library, not the compiler+library.  */
 static char *
 lib_strncat (char *s1, char const *s2, size_t n)
@@ -80,6 +94,9 @@ main ()
   {
     char y[2] = { 'x', '\0' };
     ASSERT (strncat (y, NULL, 0) == y);
+
+    volatile_strncat = null_strncat;
+    ASSERT (strncat (y, NULL, 0) == y);
   }
 
   return test_exit_status;
diff --git a/tests/test-strncmp.c b/tests/test-strncmp.c
index 6d49af9056..0623a346d8 100644
--- a/tests/test-strncmp.c
+++ b/tests/test-strncmp.c
@@ -23,6 +23,20 @@
 
 #include "macros.h"
 
+/* Test the prototype in <string.h> + compiler.
+   In GCC < 15 this is a builtin that has the nonnull attribute.
+   Some glibc versions use the nonnull attribute, which breaks this test.  */
+static int
+null_strncmp (char const *s1, char const *s2, size_t n)
+{
+  int r = strncmp (s1, s2, n);
+#if (! defined __GNUC__ || __GNUC__ >= 15 || defined __clang__) \
+    && (! defined __GLIBC__ || 2 < __GLIBC__ + (45 <= __GLIBC_MINOR__))
+  ASSERT (s1 == NULL);
+#endif
+  return r;
+}
+
 /* Test the library, not the compiler+library.  */
 static int
 lib_strncmp (char const *s1, char const *s2, size_t n)
@@ -43,5 +57,8 @@ main (void)
   ASSERT (strncmp ("x", NULL, 0) == 0);
   ASSERT (strncmp (NULL, NULL, 0) == 0);
 
+  volatile_strncmp = null_strncmp;
+  ASSERT (strncmp (NULL, "x", 0) == 0);
+
   return test_exit_status;
 }
diff --git a/tests/test-strncpy.c b/tests/test-strncpy.c
index 83535a4bee..1d896f6561 100644
--- a/tests/test-strncpy.c
+++ b/tests/test-strncpy.c
@@ -25,6 +25,20 @@
 #include "zerosize-ptr.h"
 #include "macros.h"
 
+/* Test the prototype in <string.h> + compiler.
+   In GCC < 15 this is a builtin that has the nonnull attribute.
+   Some glibc versions use the nonnull attribute, which breaks this test.  */
+static char *
+null_strncpy (char *s1, char const *s2, size_t n)
+{
+  char *p = strncpy (s1, s2, n);
+#if (! defined __GNUC__ || __GNUC__ >= 15 || defined __clang__) \
+    && (! defined __GLIBC__ || 2 < __GLIBC__ + (45 <= __GLIBC_MINOR__))
+  ASSERT (s2 == NULL);
+#endif
+  return p;
+}
+
 /* Test the library, not the compiler+library.  */
 static char *
 lib_strncpy (char *s1, char const *s2, size_t n)
@@ -116,6 +130,9 @@ main (void)
   {
     char y[1];
     ASSERT (strncpy (y, NULL, 0) == y);
+
+    volatile_strncpy = null_strncpy;
+    ASSERT (strncpy (y, NULL, 0) == y);
   }
 
   return test_exit_status;
diff --git a/tests/test-strndup.c b/tests/test-strndup.c
index 10ad604f24..6e0002c690 100644
--- a/tests/test-strndup.c
+++ b/tests/test-strndup.c
@@ -23,6 +23,20 @@
 
 #include "macros.h"
 
+/* Test the prototype in <string.h> + compiler.
+   In GCC < 15 this is a builtin that has the nonnull attribute.
+   Some glibc versions use the nonnull attribute, which breaks this test.  */
+static char *
+null_strndup (char const *s, size_t size)
+{
+  char *p = strndup (s, size);
+#if (! defined __GNUC__ || __GNUC__ >= 15 || defined __clang__) \
+    && (! defined __GLIBC__ || 2 < __GLIBC__ + (45 <= __GLIBC_MINOR__))
+  ASSERT (s == NULL);
+#endif
+  return p;
+}
+
 /* Test the library, not the compiler+library.  */
 static char *
 lib_strndup (char const *s, size_t size)
@@ -41,5 +55,8 @@ main (void)
      <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3322.pdf>.  */
   ASSERT (strndup (NULL, 0) != NULL);
 
+  volatile_strndup = null_strndup;
+  ASSERT (strndup (NULL, 0) != NULL);
+
   return test_exit_status;
 }
diff --git a/tests/test-wcsncat.c b/tests/test-wcsncat.c
index 3aaa68a24f..ec34c17e88 100644
--- a/tests/test-wcsncat.c
+++ b/tests/test-wcsncat.c
@@ -25,6 +25,18 @@
 
 #include "macros.h"
 
+/* Test the prototype in <wchar.h> + compiler.
+   Some glibc versions use the nonnull attribute, which breaks this test.  */
+static wchar_t *
+null_wcsncat (wchar_t *ws1, wchar_t const *ws2, size_t n)
+{
+  wchar_t *p = wcsncat (ws1, ws2, n);
+#if ! defined __GLIBC__ || 2 < __GLIBC__ + (45 <= __GLIBC_MINOR__)
+  ASSERT (ws2 == NULL);
+#endif
+  return p;
+}
+
 /* Test the library, not the compiler+library.  */
 static wchar_t *
 lib_wcsncat (wchar_t *ws1, wchar_t const *ws2, size_t n)
@@ -50,6 +62,9 @@ main ()
   {
     wchar_t y[2] = { L'x', 0 };
     ASSERT (wcsncat (y, NULL, 0) == y);
+
+    volatile_wcsncat = null_wcsncat;
+    ASSERT (wcsncat (y, NULL, 0) == y);
   }
 
   return test_exit_status;
diff --git a/tests/test-wcsncmp.c b/tests/test-wcsncmp.c
index c43ce8273c..a5a099f9bc 100644
--- a/tests/test-wcsncmp.c
+++ b/tests/test-wcsncmp.c
@@ -25,6 +25,18 @@ SIGNATURE_CHECK (wcsncmp, int, (const wchar_t *, const wchar_t *, size_t));
 
 #include "macros.h"
 
+/* Test the prototype in <wchar.h> + compiler.
+   Some glibc versions use the nonnull attribute, which breaks this test.  */
+static int
+null_wcsncmp (wchar_t const *ws1, wchar_t const *ws2, size_t n)
+{
+  int r = wcsncmp (ws1, ws2, n);
+#if ! defined __GLIBC__ || 2 < __GLIBC__ + (45 <= __GLIBC_MINOR__)
+  ASSERT (ws1 == NULL);
+#endif
+  return r;
+}
+
 /* Test the library, not the compiler+library.  */
 static int
 lib_wcsncmp (wchar_t const *ws1, wchar_t const *ws2, size_t n)
@@ -196,5 +208,8 @@ main (int argc, char *argv[])
   ASSERT (wcsncmp (L"x", NULL, 0) == 0);
   ASSERT (wcsncmp (NULL, NULL, 0) == 0);
 
+  volatile_wcsncmp = null_wcsncmp;
+  ASSERT (wcsncmp (NULL, L"x", 0) == 0);
+
   return test_exit_status;
 }
diff --git a/tests/test-wcsncpy.c b/tests/test-wcsncpy.c
index 321fabd6ef..02bb08b2da 100644
--- a/tests/test-wcsncpy.c
+++ b/tests/test-wcsncpy.c
@@ -23,6 +23,18 @@
 
 #include "macros.h"
 
+/* Test the prototype in <wchar.h> + compiler.
+   Some glibc versions use the nonnull attribute, which breaks this test.  */
+static wchar_t *
+null_wcsncpy (wchar_t *ws1, wchar_t const *ws2, size_t n)
+{
+  wchar_t *p = wcsncpy (ws1, ws2, n);
+#if ! defined __GLIBC__ || 2 < __GLIBC__ + (45 <= __GLIBC_MINOR__)
+  ASSERT (ws2 == NULL);
+#endif
+  return p;
+}
+
 /* Test the library, not the compiler+library.  */
 static wchar_t *
 lib_wcsncpy (wchar_t *ws1, wchar_t const *ws2, size_t n)
@@ -46,6 +58,9 @@ main (void)
   {
     wchar_t y[1];
     ASSERT (wcsncpy (y, NULL, 0) == y);
+
+    volatile_wcsncpy = null_wcsncpy;
+    ASSERT (wcsncpy (y, NULL, 0) == y);
   }
 
   return test_exit_status;
diff --git a/tests/test-wmemchr.c b/tests/test-wmemchr.c
index 877f519aa8..04858ba93e 100644
--- a/tests/test-wmemchr.c
+++ b/tests/test-wmemchr.c
@@ -23,6 +23,15 @@
 
 #include "macros.h"
 
+/* Test the prototype in <wchar.h> + compiler.  */
+static wchar_t *
+null_wmemchr (wchar_t const *s, wchar_t wc, size_t n)
+{
+  wchar_t const *p = wmemchr (s, wc, n);
+  ASSERT (s == NULL);
+  return (wchar_t *) p;
+}
+
 /* Test the library, not the compiler+library.  */
 static wchar_t *
 lib_wmemchr (wchar_t const *s, wchar_t wc, size_t n)
@@ -41,5 +50,8 @@ main (void)
      <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3322.pdf>.  */
   ASSERT (wmemchr (NULL, L'?', 0) == NULL);
 
+  volatile_wmemchr = null_wmemchr;
+  ASSERT (wmemchr (NULL, L'?', 0) == NULL);
+
   return test_exit_status;
 }
diff --git a/tests/test-wmemcmp.c b/tests/test-wmemcmp.c
index 6b7564659d..8d68196074 100644
--- a/tests/test-wmemcmp.c
+++ b/tests/test-wmemcmp.c
@@ -25,6 +25,15 @@ SIGNATURE_CHECK (wmemcmp, int, (const wchar_t *, const wchar_t *, size_t));
 
 #include "macros.h"
 
+/* Test the prototype in <wchar.h> + compiler.  */
+static int
+null_wmemcmp (wchar_t const *ws1, wchar_t const *ws2, size_t n)
+{
+  int r = wmemcmp (ws1, ws2, n);
+  ASSERT (ws1 == NULL);
+  return r;
+}
+
 /* Test the library, not the compiler+library.  */
 static int
 lib_wmemcmp (wchar_t const *ws1, wchar_t const *ws2, size_t n)
@@ -108,5 +117,8 @@ main (int argc, char *argv[])
   ASSERT (wmemcmp (L"x", NULL, 0) == 0);
   ASSERT (wmemcmp (NULL, NULL, 0) == 0);
 
+  volatile_wmemcmp = null_wmemcmp;
+  ASSERT (wmemcmp (NULL, L"x", 0) == 0);
+
   return test_exit_status;
 }
diff --git a/tests/test-wmemcpy.c b/tests/test-wmemcpy.c
index f5bb779ce8..296a0cae1e 100644
--- a/tests/test-wmemcpy.c
+++ b/tests/test-wmemcpy.c
@@ -23,6 +23,15 @@
 
 #include "macros.h"
 
+/* Test the prototype in <wchar.h> + compiler.  */
+static wchar_t *
+null_wmemcpy (wchar_t *s1, wchar_t const *s2, size_t n)
+{
+  wchar_t *p = wmemcpy (s1, s2, n);
+  ASSERT (s1 == NULL);
+  return p;
+}
+
 /* Test the library, not the compiler+library.  */
 static wchar_t *
 lib_wmemcpy (wchar_t *s1, wchar_t const *s2, size_t n)
@@ -48,5 +57,8 @@ main (void)
     ASSERT (wmemcpy (y, NULL, 0) == y);
   }
 
+  volatile_wmemcpy = null_wmemcpy;
+  ASSERT (wmemcpy (NULL, L"x", 0) == NULL);
+
   return test_exit_status;
 }
diff --git a/tests/test-wmemmove.c b/tests/test-wmemmove.c
index 4e0401c37f..3737d62d98 100644
--- a/tests/test-wmemmove.c
+++ b/tests/test-wmemmove.c
@@ -23,6 +23,15 @@
 
 #include "macros.h"
 
+/* Test the prototype in <wchar.h> + compiler.  */
+static wchar_t *
+null_wmemmove (wchar_t *s1, wchar_t const *s2, size_t n)
+{
+  wchar_t *p = wmemmove (s1, s2, n);
+  ASSERT (s1 == NULL);
+  return p;
+}
+
 /* Test the library, not the compiler+library.  */
 static wchar_t *
 lib_wmemmove (wchar_t *s1, wchar_t const *s2, size_t n)
@@ -48,5 +57,8 @@ main (void)
     ASSERT (wmemmove (y, NULL, 0) == y);
   }
 
+  volatile_wmemmove = null_wmemmove;
+  ASSERT (wmemmove (NULL, L"x", 0) == NULL);
+
   return test_exit_status;
 }
diff --git a/tests/test-wmemset.c b/tests/test-wmemset.c
index a7fb360932..9d45f4c5a6 100644
--- a/tests/test-wmemset.c
+++ b/tests/test-wmemset.c
@@ -23,6 +23,15 @@
 
 #include "macros.h"
 
+/* Test the prototype in <wchar.h> + compiler.  */
+static wchar_t *
+null_wmemset (wchar_t *ws, wchar_t wc, size_t n)
+{
+  wchar_t *p = wmemset (ws, wc, n);
+  ASSERT (ws == NULL);
+  return p;
+}
+
 /* Test the library, not the compiler+library.  */
 static wchar_t *
 lib_wmemset (wchar_t *ws, wchar_t wc, size_t n)
@@ -41,5 +50,8 @@ main (void)
      <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3322.pdf>.  */
   ASSERT (wmemset (NULL, L'?', 0) == NULL);
 
+  volatile_wmemset = null_wmemset;
+  ASSERT (wmemset (NULL, L'?', 0) == NULL);
+
   return test_exit_status;
 }
-- 
2.55.0

Reply via email to