Somehow I missed that this patch
* produces compilation errors in the gnulib tests:
../../gltests/test-string-buffer-reversed.c:113:32: error: passing
'rw_string_desc_t' (aka 'struct rw_string_desc_t') to parameter of incompatible
type 'string_desc_t' (aka 'struct string_desc_t')
113 | sbr_prepend_desc (&buffer, sd_new_addr (5, "de\0fg"));
| ^~~~~~~~~~~~~~~~~~~~~~~~~
* does not work with GCC versions < 4.9, as these versions don't support
_Generic. (See <https://gcc.gnu.org/gcc-4.9/changes.html>.)
This patch fixes both problems.
2025-05-10 Bruno Haible <[email protected]>
string-desc: Fixes for string literals and older GCC versions.
* lib/string-desc.h (HAVE__GENERIC, HAVE_BUILTIN_CHOOSE_EXPR,
HAVE_BUILTIN_CONSTANT_P, HAVE_RW_STRING_DESC): New macros.
Use HAVE_RW_STRING_DESC instead of HAVE_STATEMENT_EXPRESSIONS
everywhere.
(sd_new_addr): Use __builtin_choose_expr to deal with string literals.
* lib/xstring-desc.h: Use HAVE_RW_STRING_DESC instead of
HAVE_STATEMENT_EXPRESSIONS.
* lib/string-buffer.c (sb_dupfree): Construct an empty writable string
without a cast.
* lib/xstring-buffer.c (sb_xdupfree): Likewise.
* lib/string-buffer-reversed.c (sbr_dupfree): Likewise.
* lib/xstring-buffer-reversed.c (sbr_xdupfree): Likewise.
diff --git a/lib/string-buffer-reversed.c b/lib/string-buffer-reversed.c
index 8b55650bdf..6d9a246cc2 100644
--- a/lib/string-buffer-reversed.c
+++ b/lib/string-buffer-reversed.c
@@ -185,7 +185,7 @@ sbr_dupfree (struct string_buffer_reversed *buffer)
fail:
sbr_free (buffer);
- return sd_new_addr (0, (char *) NULL);
+ return sd_readwrite (sd_new_empty ());
}
char *
diff --git a/lib/string-buffer.c b/lib/string-buffer.c
index 50a326dd23..7b5bd24c08 100644
--- a/lib/string-buffer.c
+++ b/lib/string-buffer.c
@@ -180,7 +180,7 @@ sb_dupfree (struct string_buffer *buffer)
fail:
sb_free (buffer);
- return sd_new_addr (0, (char *) NULL);
+ return sd_readwrite (sd_new_empty ());
}
char *
diff --git a/lib/string-desc.h b/lib/string-desc.h
index db6925d0d8..adbb1e6a2e 100644
--- a/lib/string-desc.h
+++ b/lib/string-desc.h
@@ -38,12 +38,56 @@
/* Get idx_t. */
#include "idx.h"
-/* Whether the compiler supports statement-expressions. */
+/* Whether the compiler supports statement-expressions.
+ Test program:
+ int f (int x) { return ({ x; x; }); }
+ */
#if defined __GNUC__ || defined __clang__ \
|| (defined __SUNPRO_C && __SUNPRO_C >= 0x5150)
# define HAVE_STATEMENT_EXPRESSIONS 1
#endif
+/* Whether the compiler supports _Generic.
+ Test program:
+ int f (int x) { return _Generic (x, char *: 2, int: 3); }
+ */
+#if (defined __GNUC__ && __GNUC__ + (__GNUC_MINOR__ >= 9) > 4) \
+ || (defined __clang__ && __clang_major__ >= 3) \
+ || (defined __SUNPRO_C && __SUNPRO_C >= 0x5150)
+# define HAVE__GENERIC 1
+#endif
+
+/* Whether the compiler supports __builtin_choose_expr.
+ _Generic and __builtin_choose_expr are like conditional expressions,
+ except that the return types of the branches need not match: They avoid an
+ "error: type mismatch in conditional expression".
+ Test program:
+ int f (int x) { return __builtin_choose_expr (sizeof (x) == 4, 2, 3); }
+ */
+#if (defined __GNUC__ && __GNUC__ + (__GNUC_MINOR__ >= 1) > 3) \
+ || (defined __clang__ && __clang_major__ >= 3)
+# define HAVE_BUILTIN_CHOOSE_EXPR 1
+#endif
+
+/* Whether the compiler supports __builtin_constant_p and whether that built-in
+ returns true for string literals.
+ _Generic has an antiquated treatment of string literals: It treats string
+ literals like 'char *', not 'const char *'. To compensate for this, we need
+ __builtin_constant_p.
+ Test program:
+ int i, v[__builtin_constant_p ("x") > __builtin_constant_p (i) ? 1 : -1];
+ */
+#if (defined __GNUC__ && __GNUC__ + (__GNUC_MINOR__ >= 1) > 3) \
+ || (defined __clang__ && __clang_major__ >= 4)
+# define HAVE_BUILTIN_CONSTANT_P 1
+#endif
+
+/* Whether we support rw_string_desc_t as distinct from string_desc_t. */
+#if HAVE_STATEMENT_EXPRESSIONS && HAVE__GENERIC && HAVE_BUILTIN_CHOOSE_EXPR \
+ && HAVE_BUILTIN_CONSTANT_P
+# define HAVE_RW_STRING_DESC 1
+#endif
+
_GL_INLINE_HEADER_BEGIN
#ifndef GL_STRING_DESC_INLINE
# define GL_STRING_DESC_INLINE _GL_INLINE
@@ -69,7 +113,7 @@ struct rw_string_desc_t
idx_t _nbytes;
char *_data;
};
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
typedef struct string_desc_t string_desc_t;
struct string_desc_t
{
@@ -273,7 +317,7 @@ extern void sd_free (rw_string_desc_t s);
/* ==== Inline function definitions ==== */
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
GL_STRING_DESC_INLINE string_desc_t
sd_readonly (rw_string_desc_t s)
{
@@ -286,7 +330,7 @@ sd_readonly (rw_string_desc_t s)
# define sd_readonly(s) (s)
#endif
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
GL_STRING_DESC_INLINE rw_string_desc_t
sd_readwrite (string_desc_t s)
{
@@ -299,7 +343,7 @@ sd_readwrite (string_desc_t s)
# define sd_readwrite(s) (s)
#endif
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
# define sd_length(s) \
_Generic (s, \
string_desc_t : (s)._nbytes, \
@@ -312,7 +356,7 @@ sd_length (string_desc_t s)
}
#endif
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
# define sd_char_at(s, i) \
({typeof (s) _s_ = (s); \
_sd_char_at (_s_._nbytes, _s_._data, i); \
@@ -339,7 +383,7 @@ sd_char_at (string_desc_t s, idx_t i)
}
#endif
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
# define sd_data(s) \
_Generic (s, \
string_desc_t : (s)._data, \
@@ -352,7 +396,7 @@ sd_data (string_desc_t s)
}
#endif
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
# define sd_is_empty(s) \
_Generic (s, \
string_desc_t : (s)._nbytes == 0, \
@@ -369,7 +413,7 @@ extern bool _sd_equals (idx_t a_nbytes, const char *a_data,
idx_t b_nbytes, const char *b_data)
_GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1)
_GL_ATTRIBUTE_NONNULL_IF_NONZERO (4, 3);
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
# define sd_equals(a, b) \
({typeof (a) _a_ = (a); \
typeof (b) _b_ = (b); \
@@ -387,7 +431,7 @@ extern bool _sd_startswith (idx_t s_nbytes, const char
*s_data,
idx_t prefix_nbytes, const char *prefix_data)
_GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1)
_GL_ATTRIBUTE_NONNULL_IF_NONZERO (4, 3);
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
# define sd_startswith(s, prefix) \
({typeof (s) _s_ = (s); \
typeof (prefix) _prefix_ = (prefix); \
@@ -405,7 +449,7 @@ extern bool _sd_endswith (idx_t s_nbytes, const char
*s_data,
idx_t suffix_nbytes, const char *suffix_data)
_GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1)
_GL_ATTRIBUTE_NONNULL_IF_NONZERO (4, 3);
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
# define sd_endswith(s, suffix) \
({typeof (s) _s_ = (s); \
typeof (suffix) _suffix_ = (suffix); \
@@ -423,7 +467,7 @@ extern int _sd_cmp (idx_t a_nbytes, const char *a_data,
idx_t b_nbytes, const char *b_data)
_GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1)
_GL_ATTRIBUTE_NONNULL_IF_NONZERO (4, 3);
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
# define sd_cmp(a, b) \
({typeof (a) _a_ = (a); \
typeof (b) _b_ = (b); \
@@ -441,7 +485,7 @@ extern int _sd_c_casecmp (idx_t a_nbytes, const char
*a_data,
idx_t b_nbytes, const char *b_data)
_GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1)
_GL_ATTRIBUTE_NONNULL_IF_NONZERO (4, 3);
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
# define sd_c_casecmp(a, b) \
({typeof (a) _a_ = (a); \
typeof (b) _b_ = (b); \
@@ -457,7 +501,7 @@ sd_c_casecmp (string_desc_t a, string_desc_t b)
extern ptrdiff_t _sd_index (idx_t s_nbytes, const char *s_data, char c)
_GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1);
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
# define sd_index(s, c) \
({typeof (s) _s_ = (s); \
_sd_index (_s_._nbytes, _s_._data, c); \
@@ -472,7 +516,7 @@ sd_index (string_desc_t s, char c)
extern ptrdiff_t _sd_last_index (idx_t s_nbytes, const char *s_data, char c)
_GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1);
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
# define sd_last_index(s, c) \
({typeof (s) _s_ = (s); \
_sd_last_index (_s_._nbytes, _s_._data, c); \
@@ -489,7 +533,7 @@ extern ptrdiff_t _sd_contains (idx_t haystack_nbytes, const
char *haystack_data,
idx_t needle_nbytes, const char *needle_data)
_GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1)
_GL_ATTRIBUTE_NONNULL_IF_NONZERO (4, 3);
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
# define sd_contains(haystack, needle) \
({typeof (haystack) _haystack_ = (haystack); \
typeof (needle) _needle_ = (needle); \
@@ -509,17 +553,20 @@ extern string_desc_t _sd_new_addr (idx_t n, const char
*addr)
_GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1);
extern rw_string_desc_t _rwsd_new_addr (idx_t n, /*!*/const/*!*/ char *addr)
_GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1);
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
# define sd_new_addr(n, addr) \
_Generic (addr, \
const char * : _sd_new_addr (n, addr), \
- char * : _rwsd_new_addr (n, addr))
+ char * : /* Treat string literals like 'const char *'. */ \
+ __builtin_choose_expr (__builtin_constant_p (addr), \
+ _sd_new_addr (n, addr), \
+ _rwsd_new_addr (n, addr)))
#else
# define sd_new_addr(n, addr) \
_rwsd_new_addr (n, addr)
#endif
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
# define sd_substring(s, start, end) \
({typeof (s) _s_ = (s); \
idx_t _start_ = (start); \
@@ -548,7 +595,7 @@ sd_substring (string_desc_t s, idx_t start, idx_t end)
extern int _sd_write (int fd, idx_t s_nbytes, const char *s_data)
_GL_ATTRIBUTE_NONNULL_IF_NONZERO (3, 2);
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
# define sd_write(fd, s) \
({int _fd_ = (fd); \
typeof (s) _s_ = (s); \
@@ -564,7 +611,7 @@ sd_write (int fd, string_desc_t s)
extern int _sd_fwrite (FILE *fp, idx_t s_nbytes, const char *s_data)
_GL_ATTRIBUTE_NONNULL_IF_NONZERO (3, 2);
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
# define sd_fwrite(fp, s) \
({FILE *_fp_ = (fp); \
typeof (s) _s_ = (s); \
@@ -581,7 +628,7 @@ sd_fwrite (FILE *fp, string_desc_t s)
extern int _sd_copy (rw_string_desc_t *resultp,
idx_t s_nbytes, const char *s_data)
_GL_ATTRIBUTE_NONNULL_IF_NONZERO (3, 2);
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
# define sd_copy(resultp, s) \
({rw_string_desc_t *_resultp_ = (resultp); \
typeof (s) _s_ = (s); \
@@ -597,7 +644,7 @@ sd_copy (rw_string_desc_t *resultp, string_desc_t s)
extern char *_sd_c (idx_t s_nbytes, const char *s_data)
_GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1);
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
# define sd_c(s) \
({typeof (s) _s_ = (s); \
_sd_c (_s_._nbytes, _s_._data); \
@@ -613,7 +660,7 @@ sd_c (string_desc_t s)
extern void _sd_overwrite (rw_string_desc_t s, idx_t start,
idx_t t_nbytes, const char *t_data)
_GL_ATTRIBUTE_NONNULL_IF_NONZERO (4, 3);
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
# define sd_overwrite(s, start, t) \
({rw_string_desc_t _s_ = (s); \
idx_t _start_ = (start); \
diff --git a/lib/xstring-buffer-reversed.c b/lib/xstring-buffer-reversed.c
index 51d7ffc4f7..e666e679ac 100644
--- a/lib/xstring-buffer-reversed.c
+++ b/lib/xstring-buffer-reversed.c
@@ -50,7 +50,7 @@ sbr_xdupfree (struct string_buffer_reversed *buffer)
if (buffer->error)
{
sbr_free (buffer);
- return sd_new_addr (0, (char *) NULL);
+ return sd_readwrite (sd_new_empty ());
}
rw_string_desc_t contents = sbr_dupfree (buffer);
if (sd_data (contents) == NULL)
diff --git a/lib/xstring-buffer.c b/lib/xstring-buffer.c
index 3b2691d12e..4d4beb657e 100644
--- a/lib/xstring-buffer.c
+++ b/lib/xstring-buffer.c
@@ -59,7 +59,7 @@ sb_xdupfree (struct string_buffer *buffer)
if (buffer->error)
{
sb_free (buffer);
- return sd_new_addr (0, (char *) NULL);
+ return sd_readwrite (sd_new_empty ());
}
rw_string_desc_t contents = sb_dupfree (buffer);
if (sd_data (contents) == NULL)
diff --git a/lib/xstring-desc.h b/lib/xstring-desc.h
index 17ea35e357..78205e4994 100644
--- a/lib/xstring-desc.h
+++ b/lib/xstring-desc.h
@@ -111,7 +111,7 @@ _xsd_c (idx_t s_nbytes, const char *s_data)
xalloc_die ();
return result;
}
-#if HAVE_STATEMENT_EXPRESSIONS
+#if HAVE_RW_STRING_DESC
# define xsd_c(s) \
({typeof (s) _xs_ = (s); \
_xsd_c (_xs_._nbytes, _xs_._data); \