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  <br...@clisp.org>

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




Reply via email to