https://gcc.gnu.org/g:2f15787f2e1a3afe2c2ad93d4eb0d3c1f73c8fbd

commit r15-105-g2f15787f2e1a3afe2c2ad93d4eb0d3c1f73c8fbd
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Thu May 2 09:34:31 2024 +0200

    c++: Implement C++26 P2573R2 - = delete("should have a reason"); [PR114458]
    
    The following patch implements the C++26 P2573R2
    = delete("should have a reason"); paper.
    I've tried to avoid increasing compile time memory for it when it isn't
    used (e.g. by adding a new lang_decl tree member), so the reason is
    represented as STRING_CST in DECL_INITIAL (which normally is for
    DECL_DELETED_FN error_mark_node) and to differentiate this delete("reason")
    initializer from some bogus attempt to initialize a function with "reason"
    using the RID_DELETE identifier as TREE_TYPE of the STRING_CST, as nothing
    needs to care about the type of the reason.  If preferred it could
    be say TREE_LIST with the reason STRING_CST and RID_DELETE identifier or
    something similar instead, but that would need more compile time memory when
    it is used.
    
    2024-05-02  Jakub Jelinek  <ja...@redhat.com>
    
            PR c++/114458
    gcc/c-family/
            * c-cppbuiltin.cc (c_cpp_builtins): Predefine
            __cpp_deleted_function=202403L for C++26.
    gcc/cp/ChangeLog
            * parser.cc (cp_parser_pure_specifier): Implement C++26 P2573R2
            - = delete("should have a reason");.  Parse deleted-function-body.
            * decl.cc (duplicate_decls): Copy DECL_INITIAL from DECL_DELETED_FN
            olddecl to newdecl if it is a STRING_CST.
            (cp_finish_decl): Handle deleted init with a reason.
            * decl2.cc: Include "escaped_string.h".
            (grokfield): Handle deleted init with a reason.
            (mark_used): Emit DECL_DELETED_FN reason in the message if any.
            * cp-tree.h (DECL_DELETED_FN): Document representation of
            = delete("reason") on a DECL.
    gcc/testsuite/
            * g++.dg/cpp26/feat-cxx26.C (__cpp_deleted_function): Add test.
            * g++.dg/cpp26/delete-reason1.C: New test.
            * g++.dg/cpp26/delete-reason2.C: New test.
            * g++.dg/parse/error65.C (f1): Adjust expected diagnostics.

Diff:
---
 gcc/c-family/c-cppbuiltin.cc                |  1 +
 gcc/cp/cp-tree.h                            |  5 +++-
 gcc/cp/decl.cc                              | 13 ++++++---
 gcc/cp/decl2.cc                             | 23 +++++++++++++---
 gcc/cp/parser.cc                            | 21 +++++++++++++++
 gcc/testsuite/g++.dg/cpp26/delete-reason1.C | 41 +++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp26/delete-reason2.C | 20 ++++++++++++++
 gcc/testsuite/g++.dg/cpp26/feat-cxx26.C     |  6 +++++
 gcc/testsuite/g++.dg/parse/error65.C        |  3 +--
 9 files changed, 124 insertions(+), 9 deletions(-)

diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc
index 0a927b28836..b6f25e4db3c 100644
--- a/gcc/c-family/c-cppbuiltin.cc
+++ b/gcc/c-family/c-cppbuiltin.cc
@@ -1092,6 +1092,7 @@ c_cpp_builtins (cpp_reader *pfile)
          cpp_define (pfile, "__cpp_static_assert=202306L");
          cpp_define (pfile, "__cpp_placeholder_variables=202306L");
          cpp_define (pfile, "__cpp_structured_bindings=202403L");
+         cpp_define (pfile, "__cpp_deleted_function=202403L");
        }
       if (flag_concepts)
         {
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 5d1bd6ba493..933504b4821 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4477,7 +4477,10 @@ get_vec_init_expr (tree t)
        && DECL_DECLARED_CONSTEXPR_P (NODE)                     \
        && DECL_CLASS_SCOPE_P (NODE)))
 
-/* Nonzero if DECL was declared with '= delete'.  */
+/* Nonzero if DECL was declared with '= delete'.
+   = delete("reason") is represented in addition to this flag by DECL_INITIAL
+   being STRING_CST with the reason and TREE_TYPE of the STRING_CST the
+   RID_DELETE IDENTIFIER_NODE.  */
 #define DECL_DELETED_FN(DECL) \
   (LANG_DECL_FN_CHECK (DECL)->min.base.threadprivate_or_deleted_p)
 
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index de0c02a39ee..378311c0f04 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -2420,6 +2420,10 @@ duplicate_decls (tree newdecl, tree olddecl, bool 
hiding, bool was_hidden)
                        "previous declaration of %qD", olddecl);
            }
          DECL_DELETED_FN (newdecl) |= DECL_DELETED_FN (olddecl);
+         if (DECL_DELETED_FN (olddecl)
+             && DECL_INITIAL (olddecl)
+             && TREE_CODE (DECL_INITIAL (olddecl)) == STRING_CST)
+           DECL_INITIAL (newdecl) = DECL_INITIAL (olddecl);
        }
     }
 
@@ -8597,17 +8601,20 @@ cp_finish_decl (tree decl, tree init, bool 
init_const_expr_p,
   if (init && TREE_CODE (decl) == FUNCTION_DECL)
     {
       tree clone;
-      if (init == ridpointers[(int)RID_DELETE])
+      if (init == ridpointers[(int)RID_DELETE]
+         || (TREE_CODE (init) == STRING_CST
+             && TREE_TYPE (init) == ridpointers[(int)RID_DELETE]))
        {
          /* FIXME check this is 1st decl.  */
          DECL_DELETED_FN (decl) = 1;
          DECL_DECLARED_INLINE_P (decl) = 1;
-         DECL_INITIAL (decl) = error_mark_node;
+         DECL_INITIAL (decl)
+           = TREE_CODE (init) == STRING_CST ? init : error_mark_node;
          FOR_EACH_CLONE (clone, decl)
            {
              DECL_DELETED_FN (clone) = 1;
              DECL_DECLARED_INLINE_P (clone) = 1;
-             DECL_INITIAL (clone) = error_mark_node;
+             DECL_INITIAL (clone) = DECL_INITIAL (decl);
            }
          init = NULL_TREE;
        }
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 1f84878b2b9..6913efa5355 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -50,6 +50,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "asan.h"
 #include "optabs-query.h"
 #include "omp-general.h"
+#include "escaped_string.h"
 
 /* Id for dumping the raw trees.  */
 int raw_dump_id;
@@ -1038,7 +1039,10 @@ grokfield (const cp_declarator *declarator,
     init = NULL_TREE;
 
   int initialized;
-  if (init == ridpointers[(int)RID_DELETE])
+  if (init == ridpointers[(int)RID_DELETE]
+      || (init
+         && TREE_CODE (init) == STRING_CST
+         && TREE_TYPE (init) == ridpointers[(int)RID_DELETE]))
     initialized = SD_DELETED;
   else if (init == ridpointers[(int)RID_DEFAULT])
     initialized = SD_DEFAULTED;
@@ -1123,10 +1127,14 @@ grokfield (const cp_declarator *declarator,
     {
       if (TREE_CODE (value) == FUNCTION_DECL)
        {
-         if (init == ridpointers[(int)RID_DELETE])
+         if (init == ridpointers[(int)RID_DELETE]
+             || (TREE_CODE (init) == STRING_CST
+                 && TREE_TYPE (init) == ridpointers[(int)RID_DELETE]))
            {
              DECL_DELETED_FN (value) = 1;
              DECL_DECLARED_INLINE_P (value) = 1;
+             if (TREE_CODE (init) == STRING_CST)
+               DECL_INITIAL (value) = init;
            }
          else if (init == ridpointers[(int)RID_DEFAULT])
            {
@@ -5912,7 +5920,16 @@ mark_used (tree decl, tsubst_flags_t complain /* = 
tf_warning_or_error */)
            sorry ("converting lambda that uses %<...%> to function pointer");
          else if (complain & tf_error)
            {
-             error ("use of deleted function %qD", decl);
+             if (DECL_INITIAL (decl)
+                 && TREE_CODE (DECL_INITIAL (decl)) == STRING_CST)
+               {
+                 escaped_string msg;
+                 msg.escape (TREE_STRING_POINTER (DECL_INITIAL (decl)));
+                 error ("use of deleted function %qD: %s",
+                        decl, (const char *) msg);
+               }
+             else
+               error ("use of deleted function %qD", decl);
              if (!maybe_explain_implicit_delete (decl))
                inform (DECL_SOURCE_LOCATION (decl), "declared here");
            }
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index a2bc6f69000..7c3cfcfcf4b 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -28634,6 +28634,27 @@ cp_parser_pure_specifier (cp_parser* parser)
       || token->keyword == RID_DELETE)
     {
       maybe_warn_cpp0x (CPP0X_DEFAULTED_DELETED);
+      if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
+       {
+         if (cxx_dialect >= cxx11 && cxx_dialect < cxx26)
+           pedwarn (cp_lexer_peek_token (parser->lexer)->location,
+                    OPT_Wc__26_extensions,
+                    "%<delete%> reason only available with "
+                    "%<-std=c++2c%> or %<-std=gnu++2c%>");
+
+         /* Consume the `('.  */
+         matching_parens parens;
+         parens.consume_open (parser);
+         tree reason = cp_parser_unevaluated_string_literal (parser);
+         /* Consume the `)'.  */
+         parens.require_close (parser);
+         if (TREE_CODE (reason) == STRING_CST)
+           {
+             TREE_TYPE (reason) = token->u.value;
+             return reason;
+           }
+       }
+
       return token->u.value;
     }
 
diff --git a/gcc/testsuite/g++.dg/cpp26/delete-reason1.C 
b/gcc/testsuite/g++.dg/cpp26/delete-reason1.C
new file mode 100644
index 00000000000..3a097f57d07
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/delete-reason1.C
@@ -0,0 +1,41 @@
+// P2573R2 = delete("should have a reason");
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+void foo () = delete ("reason");               // { dg-warning "'delete' 
reason only available with" "" { target c++23_down } }
+                                               // { dg-message "declared here" 
"" { target *-*-* } .-1 }
+struct S {
+  void bar () = delete ("another reason");     // { dg-warning "'delete' 
reason only available with" "" { target c++23_down } }
+};                                             // { dg-message "declared here" 
"" { target *-*-* } .-1 }
+int baz (int) = delete ("yet another reason"); // { dg-warning "'delete' 
reason only available with" "" { target c++23_down } }
+int baz (int);                                 // { dg-message "declared here" 
}
+template <typename T>
+void qux (T) = delete ("some other reason");   // { dg-warning "'delete' 
reason only available with" "" { target c++23_down } }
+                                               // { dg-message "declared here" 
"" { target *-*-* } .-1 }
+template <typename T>
+struct U {
+  U () = delete ("my reasons");                        // { dg-warning 
"'delete' reason only available with" "" { target c++23_down } }
+  U (int);                                     // { dg-message "declared here" 
"" { target *-*-* } .-1 }
+  ~U () = delete ("your reasons");             // { dg-warning "'delete' 
reason only available with" "" { target c++23_down } }
+};                                             // { dg-message "declared here" 
"" { target *-*-* } .-1 }
+template <>
+void qux (long long) = delete;                 // { dg-message "declared here" 
}
+template <typename T>
+void corge (T) = delete;                       // { dg-message "declared here" 
}
+template <>
+void corge (double) = delete ("their reasons");        // { dg-warning 
"'delete' reason only available with" "" { target c++23_down } }
+                                               // { dg-message "declared here" 
"" { target *-*-* } .-1 }
+
+void
+test (U<int> &x)
+{
+  foo ();                                      // { dg-error "use of deleted 
function 'void foo\\\(\\\)': reason" }
+  S{}.bar ();                                  // { dg-error "use of deleted 
function 'void S::bar\\\(\\\)': another reason" }
+  baz (0);                                     // { dg-error "use of deleted 
function 'int baz\\\(int\\\)': yet another reason" }
+  qux (0L);                                    // { dg-error "use of deleted 
function 'void qux\\\(T\\\) \\\[with T = long int\\\]': some other reason" }
+  qux (0LL);                                   // { dg-error "use of deleted 
function 'void qux\\\(T\\\) \\\[with T = long long int\\\]'" }
+  U<long> u;                                   // { dg-error "use of deleted 
function 'U<T>::U\\\(\\\) \\\[with T = long int\\\]': my reasons" }
+                                               // { dg-error "use of deleted 
function 'U<T>::~U\\\(\\\) \\\[with T = long int\\\]': your reasons" "" { 
target *-*-* } .-1 }
+  corge (0);                                   // { dg-error "use of deleted 
function 'void corge\\\(T\\\) \\\[with T = int\\\]'" }
+  corge (0.0);                                 // { dg-error "use of deleted 
function 'void corge\\\(T\\\) \\\[with T = double\\\]': their reasons" }
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/delete-reason2.C 
b/gcc/testsuite/g++.dg/cpp26/delete-reason2.C
new file mode 100644
index 00000000000..3bd6e158307
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/delete-reason2.C
@@ -0,0 +1,20 @@
+// P2573R2 = delete("should have a reason");
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+void foo () = delete (;                                // { dg-warning 
"'delete' reason only available with" "" { target c++23_down } }
+                                               // { dg-error "expected 
string-literal before ';' token" "" { target *-*-* } .-1 }
+                                               // { dg-error "expected '\\\)' 
before ';' token" "" { target *-*-* } .-2 }
+void bar () = delete ();                       // { dg-warning "'delete' 
reason only available with" "" { target c++23_down } }
+                                               // { dg-error "expected 
string-literal before '\\\)' token" "" { target *-*-* } .-1 }
+void baz () = delete (0);                      // { dg-warning "'delete' 
reason only available with" "" { target c++23_down } }
+                                               // { dg-error "expected 
string-literal before numeric constant" "" { target *-*-* } .-1 }
+                                               // { dg-error "expected '\\\)' 
before numeric constant" "" { target *-*-* } .-2 }
+                                               // { dg-error "expected ',' or 
';' before numeric constant" "" { target *-*-* } .-3 }
+void qux () = delete (L"");                    // { dg-warning "'delete' 
reason only available with" "" { target c++23_down } }
+                                               // { dg-error "a wide string is 
invalid in this context before '\\\)' token" "" { target *-*-* } .-1 }
+void corge () = delete (u8"");                 // { dg-warning "'delete' 
reason only available with" "" { target c++23_down } }
+                                               // { dg-error "a wide string is 
invalid in this context before '\\\)' token" "" { target *-*-* } .-1 }
+void garply () = delete ("something" + 0);     // { dg-warning "'delete' 
reason only available with" "" { target c++23_down } }
+                                               // { dg-error "expected '\\\)' 
before '\\\+' token" "" { target *-*-* } .-1 }
+                                               // { dg-error "expected ',' or 
';' before '\\\+' token" "" { target *-*-* } .-2 }
diff --git a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C 
b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
index 204f8bac47d..de66dcccd02 100644
--- a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
+++ b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
@@ -609,3 +609,9 @@
 #elif __cpp_placeholder_variables != 202306
 #  error "__cpp_placeholder_variables != 202306"
 #endif
+
+#ifndef __cpp_deleted_function
+#  error "__cpp_deleted_function"
+#elif __cpp_deleted_function != 202403
+#  error "__cpp_deleted_function != 202403"
+#endif
diff --git a/gcc/testsuite/g++.dg/parse/error65.C 
b/gcc/testsuite/g++.dg/parse/error65.C
index d9e0a4bfbcb..9031c9d591f 100644
--- a/gcc/testsuite/g++.dg/parse/error65.C
+++ b/gcc/testsuite/g++.dg/parse/error65.C
@@ -1,8 +1,7 @@
 // PR c++/111840
 // { dg-do compile { target c++11 } }
 
-// NB: =delete("reason") may be allowed via P2573.
-int f1() = delete("should have a reason"); // { dg-error "expected" }
+int f1() = delete("should have a reason"); // { dg-error "'delete' reason only 
available with" "" { target c++23_down } }
 int f2() = delete[""]; // { dg-error "expected" }
 int f3() = delete{""}; // { dg-error "expected" }
 int f4() = delete""; // { dg-error "expected" }

Reply via email to