Following up on my December 7 email, it seems to me that just as GNU C
__attribute__((const)) does not imply C23 [[unsequenced]], GNU C
__attribute__((pure)) does not imply C23 [[reproducible]]. Also, Gnulib
should be fixed to not assume either implication. I installed the
attached Gnulib patch to do that, and to add commentary that I think
reflects both C23 and GNU C. This area continues to be obscure, though,
and if I made mistakes in commentary or code please let me know.
I think that luckily the fix makes little difference in practice today,
as every current compiler that supports C23 [[unsequenced]] and
[[reproducible]] also supports GNU C __attribute__((const)) and
__attribute__((pure)). However, the fix might fix bugs in applications
compiled by future C compilers that support the C23 but not the GNU C
attributes. Although the fix will also surely slow down those
applications, that's better than possibly having the bugs.
I ran across this issue when looking into some non-GNU code (the Time
Zone Database), which has a private header that incorrectly assumes that
__attribute__((const)) is stricter than [[unsequenced]] and that
__attribute__((pure)) is stricter than [[reproducible]]. I plan to fix
that too.From 8864e1c0f994dc80eac8a9b1a364e20cad93d67e Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email protected]>
Date: Sat, 20 Dec 2025 10:37:30 -0800
Subject: [PATCH] Fix [[unsequenced]] and [[reproducible]] confusion
* m4/gnulib-common.m4 (_GL_ATTRIBUTE_CONST, _GL_ATTRIBUTE_PURE):
Default to empty, not to _GL_ATTRIBUTE_UNSEQUENCED and
_GL_ATTRIBUTE_REPRODUCIBLE respectively, because unfortunately
__attribute__ ((const)) does not imply [[unsequenced]] and
__attribute__ ((pure)) does not imply [[reproducible]].
---
ChangeLog | 7 +++++++
m4/gnulib-common.m4 | 48 +++++++++++++++++++++++++--------------------
2 files changed, 34 insertions(+), 21 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 891c2edb67..161f0ce585 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
2025-12-20 Paul Eggert <[email protected]>
+ Fix [[unsequenced]] and [[reproducible]] confusion
+ * m4/gnulib-common.m4 (_GL_ATTRIBUTE_CONST, _GL_ATTRIBUTE_PURE):
+ Default to empty, not to _GL_ATTRIBUTE_UNSEQUENCED and
+ _GL_ATTRIBUTE_REPRODUCIBLE respectively, because unfortunately
+ __attribute__ ((const)) does not imply [[unsequenced]] and
+ __attribute__ ((pure)) does not imply [[reproducible]].
+
timespec: tweak timespec_sign
* lib/timespec.h (timespec_cmp, timespec_sign):
These functions are const, not merely pure.
diff --git a/m4/gnulib-common.m4 b/m4/gnulib-common.m4
index 7ec965c0e4..9cdb21ac26 100644
--- a/m4/gnulib-common.m4
+++ b/m4/gnulib-common.m4
@@ -422,15 +422,17 @@ AC_DEFUN([gl_COMMON_BODY], [
without examining state, and always returns exactly once -
e.g., does not raise an exception, call longjmp, or loop forever.
(This attribute is stricter than _GL_ATTRIBUTE_PURE because the
- function cannot observe state. It is stricter than
- _GL_ATTRIBUTE_UNSEQUENCED because the function must return exactly
- once and cannot access state addressed by its arguments.) */
+ function cannot observe state. Unlike _GL_ATTRIBUTE_UNSEQUENCED
+ the function must return exactly once and cannot access state
+ addressed by its pointer arguments or that happens to have the same
+ value for all calls to the function, but the function is allowed to
+ return a pointer to storage that can be modified later. */
/* Applies to: functions. */
#ifndef _GL_ATTRIBUTE_CONST
# if _GL_HAS_ATTRIBUTE (const)
# define _GL_ATTRIBUTE_CONST __attribute__ ((__const__))
# else
-# define _GL_ATTRIBUTE_CONST _GL_ATTRIBUTE_UNSEQUENCED
+# define _GL_ATTRIBUTE_CONST
# endif
#endif
@@ -752,30 +754,31 @@ AC_DEFUN([gl_COMMON_BODY], [
returns a value determined solely by its arguments's values
together with observable state, and always returns exactly once.
(This attribute is looser than _GL_ATTRIBUTE_CONST because the function
- can depend on observable state. It is stricter than
- _GL_ATTRIBUTE_REPRODUCIBLE because the function must return exactly
- once and cannot change state addressed by its arguments.) */
+ can depend on observable state.
+ Unlike _GL_ATTRIBUTE_REPRODUCIBLE the function must return exactly
+ once and cannot change state addressed by its arguments, but the
+ function can return a pointer to storage whose contents change later.) */
/* Applies to: functions. */
#ifndef _GL_ATTRIBUTE_PURE
# if _GL_HAS_ATTRIBUTE (pure)
# define _GL_ATTRIBUTE_PURE __attribute__ ((__pure__))
# else
-# define _GL_ATTRIBUTE_PURE _GL_ATTRIBUTE_REPRODUCIBLE
+# define _GL_ATTRIBUTE_PURE
# endif
#endif
/* _GL_ATTRIBUTE_REPRODUCIBLE declares:
It is OK for a compiler to move a call, or omit a duplicate call
- and reuse a cached value returned either directly or indirectly
- via a pointer argument, if other observable state is the same;
- however, these pointer arguments cannot alias.
+ and reuse a cached value returned either directly or indirectly via
+ a pointer, if other observable state is the same;
+ however, pointer arguments cannot alias.
This attribute is safe for a function that is effectless and idempotent;
see ISO C 23 § 6.7.13.8 for a definition of these terms.
(This attribute is looser than _GL_ATTRIBUTE_UNSEQUENCED because
- the function need not be stateless or independent. It is looser
- from _GL_ATTRIBUTE_PURE because the function need not return
- exactly once, and it can change state addressed by its pointer arguments
- that do not alias.)
+ the function need not be stateless or independent.
+ Unlike _GL_ATTRIBUTE_PURE the function need not return exactly once
+ and can change state addressed by its pointer arguments, but the
+ function cannot return a pointer to storage whose contents change later.)
See also <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2956.htm> and
<https://stackoverflow.com/questions/76847905/>.
ATTENTION! Efforts are underway to change the meaning of this attribute.
@@ -820,16 +823,19 @@ AC_DEFUN([gl_COMMON_BODY], [
/* _GL_ATTRIBUTE_UNSEQUENCED declares:
It is OK for a compiler to move a call, or omit a duplicate call
- and reuse a cached return value, addressed by its arguments is the same.
+ and reuse a cached value returned either directly or indirectly via
+ a pointer, if the state addressed by its pointer arguments is the same;
+ however, pointer arguments cannot alias.
This attribute is safe for a function that is effectless, idempotent,
stateless, and independent; see ISO C 23 § 6.7.13.8 for a definition of
these terms.
(This attribute is stricter than _GL_ATTRIBUTE_REPRODUCIBLE because
- the function must be stateless and independent. It differs from
- _GL_ATTRIBUTE_CONST because the function need not return exactly
- once and can depend on state accessed via its pointer arguments
- that do not alias, or on other state that happens to have the
- same value for all calls to the function.)
+ the function must be stateless and independent. Unlike
+ _GL_ATTRIBUTE_CONST the function need not return exactly once, and
+ can depend on state accessed via its pointer arguments or that
+ happens to have the same value for all calls to the function, but
+ the function cannot return a pointer to storage whose contents
+ change later.)
See also <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2956.htm> and
<https://stackoverflow.com/questions/76847905/>.
ATTENTION! Efforts are underway to change the meaning of this attribute.
--
2.51.0