On Thu, Mar 13, 2025 at 03:52:25PM -0400, Jason Merrill wrote:
> > > How about changing cp_parser_std_attribute_spec_seq in cp_parser_statement
> > > to cp_parser_attributes_opt?
> > 
> > I'd be afraid that would be quite significant change of behavior everywhere,
> > something that C doesn't allow (like mixing std and GNU attributes in any
> > orders or [[]] __attribute__(()) [[]][[]] __attribute__(())
> > expression-statement).  Or it would allow __attribute__(()) on while, do,
> > for, if, switch, ..., again something that wasn't accepted before.
> 
> Do you think those changes are undesirable?  We've previously had to fix
> cases where we were failing to support mixing of std and GNU attributes.

I know, but we've never allowed GNU attributes on most of those, neither
does clang, we don't allow it in C and with the exception of
fallthrough/assume on empty statement and musttail on return we currently
even don't have any uses for it.  If we start accepting it, we'd then
need to support it forever.

> Yes, it seems desirable to move all the attribute handling out of
> cp_parser_expression_statement; in the grammar, the attributes aren't part
> of the expression-statement, and it's odd to handle fallthrough in both
> places.

Here is adjusted patch which just moves the __attribute__((musttail)) return
handling to cp_parser_statement.  So far tested on dg.exp=musttail*

Moving the GNU attribute assume/fallthrough handling on empty statement handling
from cp_parser_expression_statement would regress
int bar (int x) { return x; }

void
foo (void)
{
  if (__attribute__(()); true)
    ;
  if (__attribute__((assume (bar (1)))); true)
    ;
//  if (__attribute__((fallthrough)); true)
//    ;
}
accepted by g++ 13 and 14 (fallthrough commented out, that is diagnosed
with error).

2025-03-13  Jakub Jelinek  <ja...@redhat.com>

        PR c/116545
gcc/
        * doc/extend.texi (musttail statement attribute): Document
        that musttail GNU attribute can be used as well.
gcc/c-family/
        * c-attribs.cc (c_common_clang_attributes): Add musttail.
gcc/c/
        * c-parser.cc (c_parser_declaration_or_fndef): Parse
        __attribute__((musttail)) return.
        (c_parser_handle_musttail): Diagnose attribute arguments.
        (c_parser_statement_after_labels): Parse
        __attribute__((musttail)) return.
gcc/cp/
        * parser.cc (cp_parser_statement): Parse __attribute__((musttail))
        return.
gcc/testsuite/
        * c-c++-common/musttail15.c: New test.
        * c-c++-common/musttail16.c: New test.
        * c-c++-common/musttail17.c: New test.
        * c-c++-common/musttail18.c: New test.
        * c-c++-common/musttail19.c: New test.
        * c-c++-common/musttail20.c: New test.
        * c-c++-common/musttail21.c: New test.
        * c-c++-common/musttail22.c: New test.
        * c-c++-common/musttail23.c: New test.
        * c-c++-common/musttail24.c: New test.
        * g++.dg/musttail7.C: New test.
        * g++.dg/musttail8.C: New test.
        * g++.dg/musttail12.C: New test.
        * g++.dg/musttail13.C: New test.

--- gcc/doc/extend.texi.jj      2025-03-13 14:04:54.160230977 +0100
+++ gcc/doc/extend.texi 2025-03-13 20:36:28.719373035 +0100
@@ -10241,18 +10241,22 @@ have to optimize it to just @code{return
 @cindex @code{musttail} statement attribute
 @item musttail
 
-The @code{gnu::musttail} or @code{clang::musttail} attribute
-can be applied to a @code{return} statement with a return-value expression
-that is a function call.  It asserts that the call must be a tail call that
-does not allocate extra stack space, so it is safe to use tail recursion
-to implement long running loops.
+The @code{gnu::musttail} or @code{clang::musttail} standard attribute
+or @code{musttail} GNU attribute can be applied to a @code{return} statement
+with a return-value expression that is a function call.  It asserts that the
+call must be a tail call that does not allocate extra stack space, so it is
+safe to use tail recursion to implement long running loops.
 
 @smallexample
 [[gnu::musttail]] return foo();
 @end smallexample
 
+@smallexample
+__attribute__((musttail)) return bar();
+@end smallexample
+
 If the compiler cannot generate a @code{musttail} tail call it will report
-an error. On some targets tail calls may never be supported.
+an error.  On some targets tail calls may never be supported.
 Tail calls cannot reference locals in memory, which may affect
 builds without optimization when passing small structures, or passing
 or returning large structures.  Enabling @option{-O1} or @option{-O2} can
--- gcc/c-family/c-attribs.cc.jj        2025-03-13 14:04:54.041232614 +0100
+++ gcc/c-family/c-attribs.cc   2025-03-13 20:36:28.735372814 +0100
@@ -651,7 +651,9 @@ const struct scoped_attribute_specs c_co
 /* Attributes also recognized in the clang:: namespace.  */
 const struct attribute_spec c_common_clang_attributes[] = {
   { "flag_enum",             0, 0, false, true, false, false,
-                             handle_flag_enum_attribute, NULL }
+                             handle_flag_enum_attribute, NULL },
+  { "musttail",                      0, 0, false, false, false,
+                             false, handle_musttail_attribute, NULL }
 };
 
 const struct scoped_attribute_specs c_common_clang_attribute_table =
--- gcc/c/c-parser.cc.jj        2025-03-13 14:04:54.078232105 +0100
+++ gcc/c/c-parser.cc   2025-03-13 20:36:28.766372386 +0100
@@ -1820,6 +1820,7 @@ static void c_parser_objc_at_dynamic_dec
 static bool c_parser_objc_diagnose_bad_element_prefix
   (c_parser *, struct c_declspecs *);
 static location_t c_parser_parse_rtl_body (c_parser *, char *);
+static tree c_parser_handle_musttail (c_parser *, tree, attr_state &);
 
 #if ENABLE_ANALYZER
 
@@ -2519,6 +2520,26 @@ c_parser_declaration_or_fndef (c_parser
        c_finish_oacc_routine (oacc_routine_data, NULL_TREE, false);
       return result;
     }
+  else if (specs->typespec_kind == ctsk_none
+          && c_parser_next_token_is_keyword (parser, RID_RETURN))
+    {
+      attr_state astate = {};
+      specs->attrs = c_parser_handle_musttail (parser, specs->attrs, astate);
+      if (astate.musttail_p)
+       {
+         if (specs->attrs)
+           {
+             auto_urlify_attributes sentinel;
+             warning_at (c_parser_peek_token (parser)->location,
+                         OPT_Wattributes,
+                         "attribute %<musttail%> mixed with other attributes "
+                         "on %<return%> statement");
+           }
+         c_parser_statement_after_labels (parser, NULL, NULL_TREE, NULL,
+                                          astate);
+         return result;
+       }
+    }
 
   /* Provide better error recovery.  Note that a type name here is usually
      better diagnosed as a redeclaration.  */
@@ -7373,8 +7394,12 @@ c_parser_handle_musttail (c_parser *pars
 {
   if (c_parser_next_token_is_keyword (parser, RID_RETURN))
     {
-      if (lookup_attribute ("gnu", "musttail", std_attrs))
+      if (tree a = lookup_attribute ("gnu", "musttail", std_attrs))
        {
+         for (; a; a = lookup_attribute ("gnu", "musttail", TREE_CHAIN (a)))
+           if (TREE_VALUE (a))
+             error ("%qs attribute does not take any arguments",
+                    "musttail");
          std_attrs = remove_attribute ("gnu", "musttail", std_attrs);
          attr.musttail_p = true;
        }
@@ -8237,7 +8262,8 @@ c_parser_statement_after_labels (c_parse
        case RID_ATTRIBUTE:
          {
            /* Allow '__attribute__((fallthrough));' or
-              '__attribute__((assume(cond)));'.  */
+              '__attribute__((assume(cond)));' or
+              '__attribute__((musttail))) return'.  */
            tree attrs = c_parser_gnu_attributes (parser);
            bool has_assume = lookup_attribute ("assume", attrs);
            if (has_assume)
@@ -8252,6 +8278,20 @@ c_parser_statement_after_labels (c_parse
                    has_assume = false;
                  }
              }
+           gcc_assert (!astate.musttail_p);
+           attrs = c_parser_handle_musttail (parser, attrs, astate);
+           if (astate.musttail_p)
+             {
+               if (attrs)
+                 {
+                   auto_urlify_attributes sentinel;
+                   warning_at (c_parser_peek_token (parser)->location,
+                               OPT_Wattributes,
+                               "attribute %<musttail%> mixed with other "
+                               "attributes on %<return%> statement");
+                 }
+               goto restart;
+             }
            if (attribute_fallthrough_p (attrs))
              {
                if (c_parser_next_token_is (parser, CPP_SEMICOLON))
--- gcc/cp/parser.cc.jj 2025-03-13 14:04:54.153231073 +0100
+++ gcc/cp/parser.cc    2025-03-13 20:52:57.016712546 +0100
@@ -2888,6 +2888,8 @@ static size_t cp_parser_skip_std_attribu
   (cp_parser *, size_t);
 static size_t cp_parser_skip_attributes_opt
   (cp_parser *, size_t);
+static size_t cp_parser_skip_gnu_attributes_opt
+  (cp_parser *, size_t);
 static bool cp_parser_extension_opt
   (cp_parser *, int *);
 static void cp_parser_label_declaration
@@ -13155,6 +13157,27 @@ cp_parser_statement (cp_parser* parser,
          statement = cp_parser_transaction_cancel (parser);
          break;
 
+       case RID_ATTRIBUTE:
+         {
+           unsigned int n = cp_parser_skip_gnu_attributes_opt (parser, 1);
+           if (cp_lexer_nth_token_is_keyword (parser->lexer, n, RID_RETURN))
+             {
+               /* __attribute__((musttail)) return expr;  */
+               tree attr = cp_parser_gnu_attributes_opt (parser);
+               for (tree a = lookup_attribute ("musttail", attr);
+                    a; a = lookup_attribute ("musttail", TREE_CHAIN (a)))
+                 if (TREE_VALUE (a))
+                   error ("%qs attribute does not take any arguments",
+                          "musttail");
+               statement = cp_parser_jump_statement (parser, attr);
+               if (attr != NULL_TREE && any_nonignored_attribute_p (attr))
+                 warning_at (statement_location, OPT_Wattributes,
+                             "attributes at the beginning of statement are "
+                             "ignored");
+             }
+         }
+         break;
+
        default:
          /* It might be a keyword like `int' that can start a
             declaration-statement.  */
--- gcc/testsuite/c-c++-common/musttail15.c.jj  2025-03-13 20:36:28.775372262 
+0100
+++ gcc/testsuite/c-c++-common/musttail15.c     2025-03-13 20:36:28.775372262 
+0100
@@ -0,0 +1,14 @@
+/* { dg-do compile { target { musttail && { c || c++11 } } } } */
+/* { dg-additional-options "-fdelayed-branch" { target sparc*-*-* } } */
+
+int __attribute__((noinline,noclone,noipa))
+callee (int i)
+{
+  return i * i;
+}
+
+int __attribute__((noinline,noclone,noipa))
+caller (int i)
+{
+  __attribute__((musttail)) return callee (i + 1);
+}
--- gcc/testsuite/c-c++-common/musttail16.c.jj  2025-03-13 20:36:28.775372262 
+0100
+++ gcc/testsuite/c-c++-common/musttail16.c     2025-03-13 20:36:28.775372262 
+0100
@@ -0,0 +1,33 @@
+/* { dg-do compile { target { musttail && { c || c++11 } } } } */
+
+struct box { char field[256]; int i; };
+
+int __attribute__((noinline,noclone,noipa))
+test_2_callee (int i, struct box b)
+{
+  if (b.field[0])
+    return 5;
+  return i * i;
+}
+
+int __attribute__((noinline,noclone,noipa))
+test_2_caller (int i)
+{
+  struct box b;
+  __attribute__((musttail)) return test_2_callee (i + 1, b); /* { dg-error 
"cannot tail-call: " } */
+}
+
+extern void setjmp (void);
+void
+test_3 (void)
+{
+  __attribute__((musttail)) return setjmp (); /* { dg-error "cannot tail-call: 
" } */
+}
+
+extern float f7(void);
+
+int
+test_6 (void)
+{
+  __attribute__((musttail)) return f7(); /* { dg-error "cannot tail-call: " } 
*/
+}
--- gcc/testsuite/c-c++-common/musttail17.c.jj  2025-03-13 20:36:28.775372262 
+0100
+++ gcc/testsuite/c-c++-common/musttail17.c     2025-03-13 20:36:28.775372262 
+0100
@@ -0,0 +1,17 @@
+/* { dg-do compile { target { musttail && { c || c++11 } } } } */
+
+struct box { char field[64]; int i; };
+
+struct box __attribute__((noinline,noclone,noipa))
+returns_struct (int i)
+{
+  struct box b;
+  b.i = i * i;
+  return b;
+}
+
+int __attribute__((noinline,noclone))
+test_1 (int i)
+{
+  __attribute__((musttail)) return returns_struct (i * 5).i; /* { dg-error 
"cannot tail-call: " } */
+}
--- gcc/testsuite/c-c++-common/musttail18.c.jj  2025-03-13 20:36:28.775372262 
+0100
+++ gcc/testsuite/c-c++-common/musttail18.c     2025-03-13 20:36:28.775372262 
+0100
@@ -0,0 +1,14 @@
+/* { dg-do compile { target { musttail && { c || c++11 } } } } */
+/* { dg-additional-options "-fdelayed-branch" { target sparc*-*-* } } */
+
+void __attribute__((noipa)) f() {}
+
+void f2()
+{
+  __attribute__((__musttail__)) return f2();
+}
+
+void f3()
+{
+  __attribute__((__musttail__)) return f();
+}
--- gcc/testsuite/c-c++-common/musttail19.c.jj  2025-03-13 20:36:28.776372248 
+0100
+++ gcc/testsuite/c-c++-common/musttail19.c     2025-03-13 20:36:28.776372248 
+0100
@@ -0,0 +1,17 @@
+/* { dg-do compile { target { musttail && { c || c++11 } } } } */
+
+float f1(void);
+
+int f2(void)
+{
+  __attribute__((musttail)) return f1 (); /* { dg-error "changed after call" } 
*/
+}
+
+
+int f3(int *);
+
+int f4(void)
+{
+  int x;
+  __attribute__((musttail)) return f3(&x); /* { dg-error "\(refers to 
locals|other reasons\)" } */
+}
--- gcc/testsuite/c-c++-common/musttail20.c.jj  2025-03-13 20:36:28.776372248 
+0100
+++ gcc/testsuite/c-c++-common/musttail20.c     2025-03-13 20:36:28.776372248 
+0100
@@ -0,0 +1,15 @@
+/* { dg-do compile { target { struct_musttail && { c || c++11 } } } } */
+/* { dg-additional-options "-fdelayed-branch" { target sparc*-*-* } } */
+
+struct str
+{
+  int a, b;
+};
+struct str
+cstruct (int x)
+{
+  if (x < 10)
+    L:
+    __attribute__((musttail)) return cstruct (x + 1);  /* { dg-warning 
"'musttail' attribute ignored" "" { target c } } */
+  return ((struct str){ x, 0 });
+}
--- gcc/testsuite/c-c++-common/musttail21.c.jj  2025-03-13 20:36:28.776372248 
+0100
+++ gcc/testsuite/c-c++-common/musttail21.c     2025-03-13 20:36:28.776372248 
+0100
@@ -0,0 +1,5 @@
+/* { dg-do compile { target { c || c++11 } } } */
+void f(void)
+{
+  __attribute__((musttail)) return; /* { dg-error "cannot tail-call.*return 
value must be a call" } */
+}
--- gcc/testsuite/c-c++-common/musttail22.c.jj  2025-03-13 20:36:28.776372248 
+0100
+++ gcc/testsuite/c-c++-common/musttail22.c     2025-03-13 20:36:28.776372248 
+0100
@@ -0,0 +1,90 @@
+/* PR tree-optimization/118430 */
+/* { dg-do compile { target musttail } } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-final { scan-tree-dump-times "  \[^\n\r]* = bar \\\(\[^\n\r]\*\\\); 
\\\[tail call\\\] \\\[must tail call\\\]" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "  \[^\n\r]* = freddy \\\(\[^\n\r]\*\\\); 
\\\[tail call\\\] \\\[must tail call\\\]" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-not "  (?:bar|freddy) \\\(\[^\n\r]\*\\\); 
\\\[tail call\\\]" "optimized" } } */
+
+__attribute__ ((noipa)) void
+foo (int x)
+{
+  (void) x;
+}
+
+__attribute__ ((noinline)) int
+bar (int x)
+{
+  foo (x);
+  return 1;
+}
+
+__attribute__ ((noinline)) int
+baz (int *x)
+{
+  foo (*x);
+  return 2;
+}
+
+__attribute__((noipa)) int
+qux (int x)
+{
+  {
+    int v;
+    foo (x);
+    baz (&v);
+  }
+  __attribute__((musttail))
+  return bar (x);
+}
+
+__attribute__((noipa)) int
+corge (int x)
+{
+  {
+    int v;
+    foo (x);
+    baz (&v);
+  }
+  return bar (x) + 1;
+}
+
+__attribute__ ((noinline)) float
+freddy (int x)
+{
+  foo (x);
+  return 1.75f;
+}
+
+__attribute__((noipa)) float
+garply (int x)
+{
+  {
+    int v;
+    foo (x);
+    baz (&v);
+  }
+  __attribute__((musttail))
+  return freddy (x);
+}
+
+__attribute__((noipa)) float
+quux (int x)
+{
+  {
+    int v;
+    foo (x);
+    baz (&v);
+  }
+  return freddy (x) + 0.25f;
+}
+
+int v;
+
+int
+main ()
+{
+  qux (v);
+  corge (v);
+  garply (v);
+  quux (v);
+}
--- gcc/testsuite/c-c++-common/musttail23.c.jj  2025-03-13 20:36:28.776372248 
+0100
+++ gcc/testsuite/c-c++-common/musttail23.c     2025-03-13 20:36:28.776372248 
+0100
@@ -0,0 +1,45 @@
+/* { dg-do compile } */
+/* { dg-options "-W -Wall" } */
+
+void bar (void);
+
+void
+foo (int x)
+{
+  __attribute__((musttail));                           /* { dg-warning "empty 
declaration" "" { target c } } */
+                                                       /* { dg-warning 
"attributes at the beginning of statement are ignored" "" { target c++ } .-1 } 
*/
+  if (x == 1)
+    __attribute__((musttail (1))) return bar ();       /* { dg-error 
"'musttail' attribute does not take any arguments" } */
+  if (x == 2)
+    __attribute__((musttail (1, "", 3))) return bar ();        /* { dg-error 
"'musttail' attribute does not take any arguments" } */
+  if (x == 3)
+    [[gnu::musttail (1)]] return bar ();               /* { dg-error 
"'musttail' attribute does not take any arguments" } */
+                                                       /* { dg-error 
"expected" "" { target c } .-1 } */
+  if (x == 4)
+    [[gnu::musttail (1, "", 3)]] return bar ();                /* { dg-error 
"'musttail' attribute does not take any arguments" } */
+                                                       /* { dg-error 
"expected" "" { target c } .-1 } */
+  if (x == 3)
+    [[clang::musttail (1)]] return bar ();             /* { dg-error 
"'musttail' attribute does not take any arguments" } */
+                                                       /* { dg-error 
"expected" "" { target c } .-1 } */
+  if (x == 4)
+    [[clang::musttail (1, "", 3)]] return bar ();      /* { dg-error 
"'musttail' attribute does not take any arguments" } */
+                                                       /* { dg-error 
"expected" "" { target c } .-1 } */
+  if (x == 5)
+    __attribute__((fallthrough, musttail)) return bar (); /* { dg-warning 
"attribute 'musttail' mixed with other attributes on 'return' statement" "" { 
target c } } */
+                                                       /* { dg-warning 
"attributes at the beginning of statement are ignored" "" { target c++ } .-1 } 
*/
+
+  if (x == 6)
+    [[fallthrough]] [[gnu::musttail]] return bar ();   /* { dg-warning 
"'fallthrough' attribute ignored" "" { target c } } */
+                                                       /* { dg-warning 
"attributes at the beginning of statement are ignored" "" { target c++ } .-1 } 
*/
+  if (x == 7)
+    [[clang::musttail, fallthrough]] return bar ();    /* { dg-warning 
"'fallthrough' attribute ignored" "" { target c } } */
+                                                       /* { dg-warning 
"attributes at the beginning of statement are ignored" "" { target c++ } .-1 } 
*/
+  if (x == 8)
+    __attribute__((musttail, musttail)) return bar ();
+  if (x == 9)
+    [[gnu::musttail, gnu::musttail]] return bar ();
+  if (x == 10)
+    [[clang::musttail]] [[clang::musttail]] return bar ();
+  if (x == 11)
+    [[clang::musttail]] [[gnu::musttail]] return bar ();
+}
--- gcc/testsuite/c-c++-common/musttail24.c.jj  2025-03-13 20:36:28.776372248 
+0100
+++ gcc/testsuite/c-c++-common/musttail24.c     2025-03-13 20:36:28.776372248 
+0100
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+#if !__has_attribute (musttail)
+#error missing musttail attribute
+#endif
+#ifdef __cplusplus
+#if !__has_cpp_attribute (gnu::musttail)
+#error missing gnu::musttail attribute
+#endif
+#if !__has_cpp_attribute (clang::musttail)
+#error missing clang::musttail attribute
+#endif
+#else
+#if !__has_c_attribute (gnu::musttail)
+#error missing gnu::musttail attribute
+#endif
+#if !__has_c_attribute (clang::musttail)
+#error missing clang::musttail attribute
+#endif
+#endif
--- gcc/testsuite/g++.dg/musttail7.C.jj 2025-03-13 20:36:28.776372248 +0100
+++ gcc/testsuite/g++.dg/musttail7.C    2025-03-13 20:36:28.776372248 +0100
@@ -0,0 +1,60 @@
+/* { dg-do compile { target { struct_musttail } } } */
+/* { dg-require-effective-target external_musttail } */
+/* A lot of architectures will not build this due to PR115606 and PR115607 */
+/* { dg-options "-std=gnu++11" } */
+/* { dg-additional-options "-fdelayed-branch" { target sparc*-*-* } } */
+
+class Foo {
+public:
+  int a, b;
+  Foo(int a, int b) : a(a), b(b) {}
+};
+
+Foo __attribute__((noinline,noclone,noipa))
+callee (int i)
+{
+  return Foo(i, i+1);
+}
+
+Foo __attribute__((noinline,noclone,noipa))
+caller (int i)
+{
+  __attribute__((__musttail__)) return callee (i + 1);
+}
+
+template<typename T>
+T __attribute__((noinline,noclone,noipa)) foo (T i)
+{
+  return i + 1;
+}
+
+int
+caller2 (int k)
+{
+  __attribute__((__musttail__)) return foo<int>(1);
+}
+
+template<typename T>
+T caller3 (T v)
+{
+  __attribute__((__musttail__)) return foo<T>(v);
+}
+
+int call3(int i)
+{
+  __attribute__((__musttail__)) return caller3<int>(i + 1);
+}
+
+struct Bar {
+  int a;
+  Bar(int a) : a(a) {}
+  Bar operator+(Bar o) { return Bar(a + o.a); }
+};
+
+#if __OPTIMIZE__ >= 1
+Bar
+caller4 (Bar k)
+{
+  __attribute__((__musttail__)) return caller3<Bar>(Bar(99));
+}
+#endif
--- gcc/testsuite/g++.dg/musttail8.C.jj 2025-03-13 20:36:28.777372234 +0100
+++ gcc/testsuite/g++.dg/musttail8.C    2025-03-13 20:36:28.777372234 +0100
@@ -0,0 +1,10 @@
+/* { dg-do compile { target { musttail } } } */
+/* { dg-options "-std=gnu++11" } */
+/* { dg-additional-options "-fdelayed-branch" { target sparc*-*-* } } */
+
+extern void foo();
+
+void f() noexcept
+{
+  __attribute__((musttail)) return foo(); /* { dg-error "call may throw 
exception that does not propagate" } */
+}
--- gcc/testsuite/g++.dg/musttail12.C.jj        2025-03-13 20:36:28.777372234 
+0100
+++ gcc/testsuite/g++.dg/musttail12.C   2025-03-13 20:36:28.777372234 +0100
@@ -0,0 +1,40 @@
+/* { dg-do compile { target { musttail } } } */
+/* { dg-options "-std=gnu++11" } */
+/* { dg-additional-options "-fdelayed-branch" { target sparc*-*-* } } */
+
+template <class T> T f();
+
+double g() { __attribute__((musttail)) return f<int>(); } /* { dg-error 
"cannot tail-call" } */
+
+template <class T>
+__attribute__((noinline, noclone, noipa))
+T g1() { __attribute__((musttail)) return f<T>(); } /* { dg-error "target is 
not able" "" { target { ! external_musttail } } } */
+
+template <class T>
+__attribute__((noinline, noclone, noipa))
+T g2() { __attribute__((musttail)) return f<T>(); } /* { dg-error "target is 
not able" "" { target { ! external_musttail } } } */
+
+template <class T>
+__attribute__((noinline, noclone, noipa))
+/* Would work with -O1.  */
+T g3() { __attribute__((musttail)) return f<T>(); } /* { dg-error "cannot 
tail-call" } */
+
+template <class T>
+__attribute__((noinline, noclone, noipa))
+T g4() { __attribute__((musttail)) return f<double>(); } /* { dg-error "cannot 
tail-call" } */
+
+class C
+{
+  double x;
+public:
+  C(double x) : x(x) {}
+  ~C() { asm("":::"memory"); }
+};
+
+int main()
+{
+  g1<int>();
+  g2<double>();
+  g3<C>();
+  g4<int>();
+}
--- gcc/testsuite/g++.dg/musttail13.C.jj        2025-03-13 20:36:28.777372234 
+0100
+++ gcc/testsuite/g++.dg/musttail13.C   2025-03-13 20:36:28.777372234 +0100
@@ -0,0 +1,33 @@
+/* { dg-do compile { target { musttail } } } */
+/* { dg-options "-std=gnu++11" } */
+/* { dg-additional-options "-fdelayed-branch" { target sparc*-*-* } } */
+
+template <class T> T f();
+
+class C
+{
+  double x;
+public:
+  C(double x) : x(x) {}
+  ~C() { asm("":::"memory"); }
+  operator int() { return x; }
+};
+
+template <class T>
+__attribute__((noinline, noclone, noipa))
+T g5() { __attribute__((musttail)) return f<C> (); } /* { dg-error "cannot 
tail-call" } */
+
+C h();
+
+__attribute__((noinline, noclone, noipa))
+int g6() { __attribute__((__musttail__)) return h (); }  /* { dg-error "cannot 
tail-call" } */
+
+__attribute__((noinline, noclone, noipa))
+C g7() { __attribute__((musttail)) return h (); } /* { dg-error "cannot 
tail-call" } */
+
+int main()
+{
+  g5<int> ();
+  g6 ();
+  g7 ();
+}


        Jakub

Reply via email to