Here is a patch that that changes Write-strings into a proper
warning and adding another flag to toggle the const qualifier
as discussed in the bugzilla.  The test for string literals
can probably be simplified, or perhaps there is a better way.


Bootstrapped and regression tested on x86_64.



    c: split -Wwrite-strings into a proper warning and a flag [PR61579]
    
    This adds a new flag -fconst-string-literals that toggles whether
    string literals are const-qualified or not.  The warning -Wwrite-strings
    is reimplemented as warning that warns whenever a string literal is
    implicitely converted to a pointer to a non-const-qualified character
    type independently on whether the string literal is actually const
    qualified or not.  For C++ and with -fconst-string-literals also C,
    the warning is activated by default.
    
            PR c/61579
    
    gcc/c-family/ChangeLog:
            * c-common.cc (fix_string_type): Toggle const literals
            based on new flag.
            * c.opt (-fconst-string-literals): New flag.
            * c-opts.cc (c_common_init_options_struct,
            c_common_post_options): Adapt.
    
    gcc/c/ChangeLog:
            * c-typeck.cc (string_literal_p): New function.
            (convert_for_assignment): Implement new warning.
    
    gcc/doc/ChangeLog:
            * invoke.texi (-Wwrite-strings): Adapt.
            (-fconst-string-literals): Document.
    
    gcc/testsuite/ChangeLog:
            * gcc.dg/Wwrite-strings-1.c: Adapt.
            * gcc.dg/Wwrite-strings-2.c: New test.
            * gcc.dg/Wwrite-strings-3.c: New test.
            * gcc.dg/Wwrite-strings-4.c: New test.
            * gcc.dg/cpp/line3.c: Adapt.

diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index 6380bec4515..843f38aa651 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -885,10 +885,6 @@ fix_string_type (tree value)
      standard says that a string literal has type `const char[N]' or
      `const wchar_t[N]'.  We use the same logic when invoked as a C
      front-end with -Wwrite-strings.
-     ??? We should change the type of an expression depending on the
-     state of a warning flag.  We should just be warning -- see how
-     this is handled in the C++ front-end for the deprecated implicit
-     conversion from string literals to `char*' or `wchar_t*'.
 
      The C++ front end relies on TYPE_MAIN_VARIANT of a cv-qualified
      array type being the unqualified version of that type.
@@ -898,7 +894,7 @@ fix_string_type (tree value)
      unconditionally.  */
   i_type = build_index_type (size_int (nchars - 1));
   a_type = build_array_type (e_type, i_type);
-  if (c_dialect_cxx() || warn_write_strings)
+  if (c_dialect_cxx() || flag_const_string_literals)
     a_type = c_build_qualified_type (a_type, TYPE_QUAL_CONST);
 
   TREE_TYPE (value) = a_type;
diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc
index 0e2d8bdcfdd..b05ea3d6900 100644
--- a/gcc/c-family/c-opts.cc
+++ b/gcc/c-family/c-opts.cc
@@ -226,7 +226,6 @@ c_common_init_options_struct (struct gcc_options *opts)
 {
   opts->x_flag_exceptions = c_dialect_cxx ();
   opts->x_warn_pointer_arith = c_dialect_cxx ();
-  opts->x_warn_write_strings = c_dialect_cxx ();
   opts->x_flag_warn_unused_result = true;
 
   /* By default, C99-like requirements for complex multiply and divide.  */
@@ -1051,6 +1050,9 @@ c_common_post_options (const char **pfilename)
                                 && (cxx_dialect >= cxx11 || flag_isoc99)
                                 && cxx_dialect < cxx20);
 
+  SET_OPTION_IF_UNSET (&global_options, &global_options_set, 
warn_write_strings,
+                      c_dialect_cxx () || flag_const_string_literals > 0);
+
   /* -Wregister is enabled by default in C++17.  */
   SET_OPTION_IF_UNSET (&global_options, &global_options_set, warn_register,
                       cxx_dialect >= cxx17);
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 0260b9dbf42..4f32d726ee0 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1884,6 +1884,10 @@ fconserve-space
 C++ ObjC++ Ignore
 Does nothing.  Preserved for backward compatibility.
 
+fconst-string-literals
+C ObjC Var(flag_const_string_literals)
+String literals are const qualified.
+
 fconstant-string-class=
 ObjC ObjC++ Joined RejectNegative MissingArgError(no class name specified with 
%qs)
 -fconst-string-class=<name>    Use class <name> for constant strings.
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index ed234f075ca..3e312cd50d1 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -8370,6 +8370,41 @@ maybe_inform_typedef_location (tree type, const char 
*highlight_color)
   inform (&richloc, "%e declared here", &e_typedef_decl);
 }
 
+
+/* Test whether EXPR is a string literal.  */
+
+static bool
+string_literal_p (tree expr)
+{
+  if (expr == error_mark_node)
+    return false;
+  if (TREE_CODE (expr) == NOP_EXPR)
+    expr = TREE_OPERAND(expr, 0);
+  tree type = TREE_TYPE (expr);
+  if (TREE_CODE (type) != POINTER_TYPE)
+    return false;
+  type = TREE_TYPE (type);
+  if (TREE_CODE (type) != ARRAY_TYPE)
+    return false;
+  tree tchar = TYPE_MAIN_VARIANT (TREE_TYPE (type));
+  if (!char_type_p (tchar))
+    return false;
+  type = TREE_TYPE (expr);
+  if (TREE_CODE (expr) != ADDR_EXPR)
+    return false;
+  type = TREE_TYPE (type);
+  if (TREE_CODE (type) != ARRAY_TYPE
+      || tchar != TYPE_MAIN_VARIANT (TREE_TYPE (type)))
+    return false;
+  expr = TREE_OPERAND (expr, 0);
+  type = TREE_TYPE (expr);
+  if (TREE_CODE (expr) != STRING_CST
+      || TREE_CODE (type) != ARRAY_TYPE)
+    return false;
+  return true; 
+}
+
+
 /* Convert value RHS to type TYPE as preparation for an assignment to
    an lvalue of type TYPE.  If ORIGTYPE is not NULL_TREE, it is the
    original type of RHS; this differs from TREE_TYPE (RHS) for enum
@@ -8602,6 +8637,22 @@ convert_for_assignment (location_t location, location_t 
expr_loc, tree type,
        }
     }
 
+  if (codel == POINTER_TYPE && string_literal_p (rhs))
+    {
+      tree ttl = TREE_TYPE (type);
+      tree ttr = TREE_TYPE (rhstype);
+      if (!(TYPE_QUALS (ttl) & TYPE_QUAL_CONST)
+         && comptypes (TYPE_MAIN_VARIANT (ttl), TYPE_MAIN_VARIANT (ttr)))
+       {
+         warning_at (location, OPT_Wwrite_strings,
+                     "implicit conversion of string literal to "
+                     "unqualified pointer");
+        if (type != rhstype)
+           rhs = convert (type, rhs);
+         return rhs;
+       }
+    }
+
   if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (rhstype))
     {
       warn_for_address_of_packed_member (type, orig_rhs);
@@ -8841,6 +8892,7 @@ convert_for_assignment (location_t location, location_t 
expr_loc, tree type,
       /* Opaque pointers are treated like void pointers.  */
       is_opaque_pointer = vector_targets_convertible_p (ttl, ttr);
 
+
       /* The Plan 9 compiler permits a pointer to a struct to be
         automatically converted into a pointer to an anonymous field
         within the struct.  */
@@ -9038,7 +9090,7 @@ convert_for_assignment (location_t location, location_t 
expr_loc, tree type,
         and vice versa; otherwise, targets must be the same.
         Meanwhile, the lhs target must have all the qualifiers of the rhs.  */
       if ((VOID_TYPE_P (ttl) && !TYPE_ATOMIC (ttl))
-         || (VOID_TYPE_P (ttr) && !TYPE_ATOMIC (ttr))
+         || (VOID_TYPE_P (ttr) && !TYPE_ATOMIC (ttr)) // FIXME
          || (target_cmp = comp_target_types (location, type, rhstype))
          || is_opaque_pointer
          || ((c_common_unsigned_type (mvl)
@@ -9071,10 +9123,9 @@ convert_for_assignment (location_t location, location_t 
expr_loc, tree type,
             }
           else if (pedantic
              && ((VOID_TYPE_P (ttl) && TREE_CODE (ttr) == FUNCTION_TYPE)
-                 ||
-                 (VOID_TYPE_P (ttr)
-                  && !null_pointer_constant
-                  && TREE_CODE (ttl) == FUNCTION_TYPE)))
+                 || (VOID_TYPE_P (ttr)
+                     && !null_pointer_constant
+                     && TREE_CODE (ttl) == FUNCTION_TYPE)))
            PEDWARN_FOR_ASSIGNMENT (location, expr_loc, OPT_Wpedantic,
                                    G_("ISO C forbids passing argument %d of "
                                       "%qE between function pointer "
@@ -9090,11 +9141,11 @@ convert_for_assignment (location_t location, location_t 
expr_loc, tree type,
          else if (TREE_CODE (ttr) != FUNCTION_TYPE
                   && TREE_CODE (ttl) != FUNCTION_TYPE)
            {
-              /* Assignments between atomic and non-atomic objects are OK.  */
-              bool warn_quals_ped = TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttr)
-                                    & ~TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC 
(ttl);
-              bool warn_quals = TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttr)
-                                & ~TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC 
(strip_array_types (ttl));
+             /* Assignments between atomic and non-atomic objects are OK.  */
+             bool warn_quals_ped = TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttr)
+                                   & ~TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttl);
+             bool warn_quals = TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttr)
+                               & ~TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC 
(strip_array_types (ttl));
 
              /* Don't warn about loss of qualifier for conversions from
                 qualified void* to pointers to arrays with corresponding
@@ -9117,8 +9168,7 @@ convert_for_assignment (location_t location, location_t 
expr_loc, tree type,
 
              /* If this is not a case of ignoring a mismatch in signedness,
                 no warning.  */
-             else if (VOID_TYPE_P (ttl) || VOID_TYPE_P (ttr)
-                      || target_cmp)
+             else if (VOID_TYPE_P (ttl) || VOID_TYPE_P (ttr) || target_cmp)
                ;
              /* If there is a mismatch, do warn.  */
              else if (warn_pointer_sign)
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index f2bcf410bf0..449ae683eba 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -3287,6 +3287,10 @@ to C++20 concepts.
 Specify maximum error replay depth during recursive diagnosis of a constraint
 satisfaction failure.  The default is 1.
 
+@opindex fconst-string-literals
+@item -fconst-string-literals
+When compiling C, give string literals a @code{const}-qualified type.
+
 @opindex fconstexpr-depth
 @item -fconstexpr-depth=@var{n}
 Set the maximum nested evaluation depth for C++11 constexpr functions
@@ -10256,14 +10260,11 @@ This warning is enabled by default.
 @opindex Wwrite-strings
 @opindex Wno-write-strings
 @item -Wwrite-strings
-When compiling C, give string constants the type @code{const
-char[@var{length}]} so that copying the address of one into a
-non-@code{const} @code{char *} pointer produces a warning.  These
-warnings help you find at compile time code that can try to write
+When compiling C, warn when implicitly converting from string literals
+to the corresponding non-@code{const} pointer type.
+These warnings help you find at compile time code that can try to write
 into a string constant, but only if you have been very careful about
-using @code{const} in declarations and prototypes.  Otherwise, it is
-just a nuisance. This is why we did not make @option{-Wall} request
-these warnings.
+using @code{const} in declarations and prototypes.
 
 When compiling C++, warn about the deprecated conversion from string
 literals to @code{char *}.  This warning is enabled by default for C++
diff --git a/gcc/testsuite/gcc.dg/Wwrite-strings-1.c 
b/gcc/testsuite/gcc.dg/Wwrite-strings-1.c
index 24547d02bd8..ea2f28ecd6c 100644
--- a/gcc/testsuite/gcc.dg/Wwrite-strings-1.c
+++ b/gcc/testsuite/gcc.dg/Wwrite-strings-1.c
@@ -5,4 +5,4 @@
 /* { dg-do compile } */
 /* { dg-options "-Wwrite-strings" } */
 typedef char T[1];
-T *p = &""; /* { dg-warning "initialization discards 'const' qualifier from 
pointer target type" } */
+T *p = &""; /* { dg-warning "string literal" } */
diff --git a/gcc/testsuite/gcc.dg/Wwrite-strings-2.c 
b/gcc/testsuite/gcc.dg/Wwrite-strings-2.c
new file mode 100644
index 00000000000..f5562d17426
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wwrite-strings-2.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -Wwrite-strings" } */
+
+int foo(int)
+{
+       _Generic(typeof(""), char[]: 1);
+       char *p = "";                           /* { dg-warning "string 
literal" } */
+       char (*q)[] = &"";                      /* { dg-warning "string 
literal" } */
+       unsigned char *r = u8"";                /* { dg-warning "string 
literal" } */
+}
+
diff --git a/gcc/testsuite/gcc.dg/Wwrite-strings-3.c 
b/gcc/testsuite/gcc.dg/Wwrite-strings-3.c
new file mode 100644
index 00000000000..6853d2989c0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wwrite-strings-3.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -fconst-string-literals" } */
+
+int foo(int)
+{
+       _Generic(typeof(""), const char[]: 1);
+       char *p = "";                           /* { dg-warning "string 
literal" } */
+       char (*q)[] = &"";                      /* { dg-warning "string 
literal" } */
+       unsigned char *r = u8"";                /* { dg-warning "string 
literal" } */
+}
+
diff --git a/gcc/testsuite/gcc.dg/Wwrite-strings-4.c 
b/gcc/testsuite/gcc.dg/Wwrite-strings-4.c
new file mode 100644
index 00000000000..7b15b4788c8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wwrite-strings-4.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -fconst-string-literals -Wno-write-strings" } */
+
+int foo(int)
+{
+       _Generic(typeof(""), const char[]: 1);
+       char *p = "";
+       char (*q)[] = &"";
+       unsigned char *r = u8"";
+}
+
diff --git a/gcc/testsuite/gcc.dg/cpp/line3.c b/gcc/testsuite/gcc.dg/cpp/line3.c
index 4b40e4a6a95..e1ae5bf7584 100644
--- a/gcc/testsuite/gcc.dg/cpp/line3.c
+++ b/gcc/testsuite/gcc.dg/cpp/line3.c
@@ -13,14 +13,14 @@ main(void)
 {
   char *A;
 
-  A = "text";          /* { dg-warning "discards 'const' qualifier" "case 
zero" } */
-  A = one("text"       /* { dg-warning "discards 'const' qualifier" "case one" 
} */
+  A = "text";          /* { dg-warning "string literal" "case zero" } */
+  A = one("text"       /* { dg-warning "string literal" "case one" } */
          "text")
        ;
-  A = two("text"       /* { dg-warning "discards 'const' qualifier" "case two" 
} */
+  A = two("text"       /* { dg-warning "string literal" "case two" } */
          "text")
        ;
-  A = four("text"      /* { dg-warning "discards 'const' qualifier" "case 
four" } */
+  A = four("text"      /* { dg-warning "string literal" "case four" } */
           "text")
        ;
 

Reply via email to