On 2026-07-29 Bruno Haible wrote:
> > I have understood that glibc headers will be fixed. Old glibc
> > versions might remain in use for years though
>
> Right. We attempt to have no test failures in the Gnulib test suite on
> various platforms. This typically means to #ifdef out some tests on
> some platforms. As you found out, 15 out of 21 tests would need some
> corrections. The consequences for the user would be that they should
> better NOT pass null pointers to these functions. In summary, quite
> some effort for quite little gain.
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.
> > glibc >= 2.34 && (GCC >= 15 || Clang):
> >
> > One could add
> >
> > #define __nonnull(param)
>
> Please not. This would turn off the null-pointer checking for many
> other functions.
I agree, it's not acceptable as it silences compiler warnings and
sanitizers. I got carried away with the workaround ideas.
> > With Clang 22.1.8, one test fails:
> >
> > test-bsearch
>
> Oh, interesting: clang appears to have its own database of function
> declarations, instead of relying on the header files.
The explanation is simpler. In n3322.pdf[1] that is linked from the
test-*.c files, Clang's behavior is described on page 8:
Clang only respects __attribute__((nonnull)) on function
definitions, but does not respect it on declarations. This is done
specifically so that nonnull annotations in libc headers are
ignored. This does not affect sanitizer behavior or compiler
warnings, which do respect the nonnull attributes.
glibc's <stdlib.h> includes <bits/stdlib-bsearch.h> which contains an
inline implementation of bsearch. When Clang sees a function
definition, the nonnull attribute affects optimizations and the null
pointer check is removed.
> So, as I see it, only these parts of your patch would be useful:
>
> + * 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.
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.).
> But for this part alone, we would also need copyright assignment
> papers from you with the FSF [1][2].
I did the test-memcpy.c change first. It's likely too short to be
copyrightable. The rest was repetitive copy-pasting and replacing the
function name. So I think this patch shouldn't cross the threshold for
copyright assignment. I don't have anything against it in principle, I
just don't want to think about it right now.
[1] https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3322.pdf
--
Lasse Collin
>From 49ce235c33f11ab08555ce2d338846989c9492f0 Mon Sep 17 00:00:00 2001
From: Lasse Collin <[email protected]>
Date: Wed, 29 Jul 2026 23:02:07 +0300
Subject: [PATCH] tests: Verify N3322 compatibility of five wmem* function
prototypes
Test that the compiler doesn't optimize a NULL check away when it sees
the prototypes in <wchar.h>. In glibc, these functions don't have the
nonnull attribute, so the tests pass.
* 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.
---
ChangeLog | 12 ++++++++++++
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 ++++++++++++
6 files changed, 72 insertions(+)
diff --git a/ChangeLog b/ChangeLog
index 07587269d4..15722aed70 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2026-07-29 Lasse Collin <[email protected]>
+
+ tests: Verify N3322 compatibility of five wmem* function prototypes.
+ Test that the compiler doesn't optimize a NULL check away when it sees
+ the prototypes in <wchar.h>. In glibc, these functions don't have the
+ nonnull attribute, so the tests pass.
+ * 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.
+
2026-07-28 Bruno Haible <[email protected]>
tests: Avoid some more runtime errors with Fil-C.
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