Implement a C23 clang compatible musttail attribute similar to the earlier
C++ implementation in the C parser.
---
 gcc/c/c-parser.cc | 61 +++++++++++++++++++++++++++++++++++++----------
 gcc/c/c-tree.h    |  2 +-
 gcc/c/c-typeck.cc | 15 ++++++++++--
 3 files changed, 63 insertions(+), 15 deletions(-)

diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index c31349dae2ff..76931931e270 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -1616,6 +1616,11 @@ struct omp_for_parse_data {
   bool fail : 1;
 };
 
+struct attr_state
+{
+  bool musttail_p; // parsed a musttail for return
+};
+
 static bool c_parser_nth_token_starts_std_attributes (c_parser *,
                                                      unsigned int);
 static tree c_parser_std_attribute_specifier_sequence (c_parser *);
@@ -1660,7 +1665,7 @@ static location_t c_parser_compound_statement_nostart 
(c_parser *);
 static void c_parser_label (c_parser *, tree);
 static void c_parser_statement (c_parser *, bool *, location_t * = NULL);
 static void c_parser_statement_after_labels (c_parser *, bool *,
-                                            vec<tree> * = NULL);
+                                            vec<tree> * = NULL, attr_state = 
{});
 static tree c_parser_c99_block_statement (c_parser *, bool *,
                                          location_t * = NULL);
 static void c_parser_if_statement (c_parser *, bool *, vec<tree> *);
@@ -5757,6 +5762,8 @@ c_parser_std_attribute (c_parser *parser, bool for_tm,
        }
       goto out;
     }
+  else if (is_attribute_p ("musttail", name))
+    error ("%<musttail%> attribute has arguments");
   {
     location_t open_loc = c_parser_peek_token (parser)->location;
     matching_parens parens;
@@ -6943,6 +6950,28 @@ c_parser_handle_directive_omp_attributes (tree &attrs,
     }
 }
 
+/* Check if STD_ATTR contains a musttail attribute and handle it
+   PARSER is the parser and A is the output attr_state.  */
+
+static tree
+c_parser_handle_musttail (c_parser *parser, tree std_attrs, attr_state &a)
+{
+  if (c_parser_next_token_is_keyword (parser, RID_RETURN))
+    {
+      if (lookup_attribute ("gnu", "musttail", std_attrs))
+       {
+         std_attrs = remove_attribute ("gnu", "musttail", std_attrs);
+         a.musttail_p = true;
+       }
+      if (lookup_attribute ("clang", "musttail", std_attrs))
+       {
+         std_attrs = remove_attribute ("clang", "musttail", std_attrs);
+         a.musttail_p = true;
+       }
+    }
+  return std_attrs;
+}
+
 /* Parse a compound statement except for the opening brace.  This is
    used for parsing both compound statements and statement expressions
    (which follow different paths to handling the opening).  */
@@ -6959,6 +6988,7 @@ c_parser_compound_statement_nostart (c_parser *parser)
   bool in_omp_loop_block
     = omp_for_parse_state ? omp_for_parse_state->want_nested_loop : false;
   tree sl = NULL_TREE;
+  attr_state a = {};
 
   if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE))
     {
@@ -7097,7 +7127,10 @@ c_parser_compound_statement_nostart (c_parser *parser)
        = c_parser_nth_token_starts_std_attributes (parser, 1);
       tree std_attrs = NULL_TREE;
       if (have_std_attrs)
-       std_attrs = c_parser_std_attribute_specifier_sequence (parser);
+       {
+         std_attrs = c_parser_std_attribute_specifier_sequence (parser);
+         std_attrs = c_parser_handle_musttail (parser, std_attrs, a);
+       }
       if (c_parser_next_token_is_keyword (parser, RID_CASE)
          || c_parser_next_token_is_keyword (parser, RID_DEFAULT)
          || (c_parser_next_token_is (parser, CPP_NAME)
@@ -7245,7 +7278,7 @@ c_parser_compound_statement_nostart (c_parser *parser)
          last_stmt = true;
          mark_valid_location_for_stdc_pragma (false);
          if (!omp_for_parse_state)
-           c_parser_statement_after_labels (parser, NULL);
+           c_parser_statement_after_labels (parser, NULL, NULL, a);
          else
            {
              /* In canonical loop nest form, nested loops can only appear
@@ -7287,15 +7320,18 @@ c_parser_compound_statement_nostart (c_parser *parser)
 /* Parse all consecutive labels, possibly preceded by standard
    attributes.  In this context, a statement is required, not a
    declaration, so attributes must be followed by a statement that is
-   not just a semicolon.  */
+   not just a semicolon.  Returns an attr_state.  */
 
-static void
+static attr_state
 c_parser_all_labels (c_parser *parser)
 {
+  attr_state a = {};
   bool have_std_attrs;
   tree std_attrs = NULL;
   if ((have_std_attrs = c_parser_nth_token_starts_std_attributes (parser, 1)))
-    std_attrs = c_parser_std_attribute_specifier_sequence (parser);
+    std_attrs = c_parser_handle_musttail (parser,
+                   c_parser_std_attribute_specifier_sequence (parser), a);
+
   while (c_parser_next_token_is_keyword (parser, RID_CASE)
         || c_parser_next_token_is_keyword (parser, RID_DEFAULT)
         || (c_parser_next_token_is (parser, CPP_NAME)
@@ -7317,6 +7353,7 @@ c_parser_all_labels (c_parser *parser)
     }
   else if (have_std_attrs && c_parser_next_token_is (parser, CPP_SEMICOLON))
     c_parser_error (parser, "expected statement");
+  return a;
 }
 
 /* Parse a label (C90 6.6.1, C99 6.8.1, C11 6.8.1).
@@ -7560,11 +7597,11 @@ c_parser_label (c_parser *parser, tree std_attrs)
 static void
 c_parser_statement (c_parser *parser, bool *if_p, location_t *loc_after_labels)
 {
-  c_parser_all_labels (parser);
+  attr_state a = c_parser_all_labels (parser);
   if (loc_after_labels)
     *loc_after_labels = c_parser_peek_token (parser)->location;
   parser->omp_attrs_forbidden_p = false;
-  c_parser_statement_after_labels (parser, if_p, NULL);
+  c_parser_statement_after_labels (parser, if_p, NULL, a);
 }
 
 /* Parse a statement, other than a labeled statement.  CHAIN is a vector
@@ -7573,11 +7610,11 @@ c_parser_statement (c_parser *parser, bool *if_p, 
location_t *loc_after_labels)
 
    IF_P is used to track whether there's a (possibly labeled) if statement
    which is not enclosed in braces and has an else clause.  This is used to
-   implement -Wparentheses.  */
+   implement -Wparentheses. A has an earlier parsed attribute state.  */
 
 static void
 c_parser_statement_after_labels (c_parser *parser, bool *if_p,
-                                vec<tree> *chain)
+                                vec<tree> *chain, attr_state a)
 {
   location_t loc = c_parser_peek_token (parser)->location;
   tree stmt = NULL_TREE;
@@ -7645,7 +7682,7 @@ c_parser_statement_after_labels (c_parser *parser, bool 
*if_p,
          c_parser_consume_token (parser);
          if (c_parser_next_token_is (parser, CPP_SEMICOLON))
            {
-             stmt = c_finish_return (loc, NULL_TREE, NULL_TREE);
+             stmt = c_finish_return (loc, NULL_TREE, NULL_TREE, a.musttail_p);
              c_parser_consume_token (parser);
            }
          else
@@ -7654,7 +7691,7 @@ c_parser_statement_after_labels (c_parser *parser, bool 
*if_p,
              struct c_expr expr = c_parser_expression_conv (parser);
              mark_exp_read (expr.value);
              stmt = c_finish_return (EXPR_LOC_OR_LOC (expr.value, xloc),
-                                     expr.value, expr.original_type);
+                                     expr.value, expr.original_type, 
a.musttail_p);
              goto expect_semicolon;
            }
          break;
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index cf29534c0915..902cc8f6aa49 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -824,7 +824,7 @@ extern tree c_begin_stmt_expr (void);
 extern tree c_finish_stmt_expr (location_t, tree);
 extern tree c_process_expr_stmt (location_t, tree);
 extern tree c_finish_expr_stmt (location_t, tree);
-extern tree c_finish_return (location_t, tree, tree);
+extern tree c_finish_return (location_t, tree, tree, bool = false);
 extern tree c_finish_bc_stmt (location_t, tree, bool);
 extern tree c_finish_goto_label (location_t, tree);
 extern tree c_finish_goto_ptr (location_t, c_expr val);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 66c6abc9f076..144b001e3a6f 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -11422,10 +11422,10 @@ c_finish_goto_ptr (location_t loc, c_expr val)
    to return, or a null pointer for `return;' with no value.  LOC is
    the location of the return statement, or the location of the expression,
    if the statement has any.  If ORIGTYPE is not NULL_TREE, it
-   is the original type of RETVAL.  */
+   is the original type of RETVAL.  MUSTTAIL_P indicates a musttail attribute. 
 */
 
 tree
-c_finish_return (location_t loc, tree retval, tree origtype)
+c_finish_return (location_t loc, tree retval, tree origtype, bool musttail_p)
 {
   tree valtype = TREE_TYPE (TREE_TYPE (current_function_decl)), ret_stmt;
   bool no_warning = false;
@@ -11439,6 +11439,17 @@ c_finish_return (location_t loc, tree retval, tree 
origtype)
     warning_at (xloc, 0,
                "function declared %<noreturn%> has a %<return%> statement");
 
+  if (retval && musttail_p)
+    {
+      if (TREE_CODE (retval) == CALL_EXPR)
+       CALL_EXPR_MUST_TAIL_CALL (retval) = 1;
+      else if (TREE_CODE (retval) == TARGET_EXPR
+              && TREE_CODE (TARGET_EXPR_INITIAL (retval)) == CALL_EXPR)
+       CALL_EXPR_MUST_TAIL_CALL (TARGET_EXPR_INITIAL (retval)) = 1;
+      else
+       error_at (xloc, "cannot tail-call: return value must be call");
+    }
+
   if (retval)
     {
       tree semantic_type = NULL_TREE;
-- 
2.43.0

Reply via email to