based on the technical specification n3589[1], and set behind the
command line flag '-fdefer-ts'

deferred statements execute at the end of the scope they're added on,
similar to the cleanup attribute, and they have much of the same
constraints as statement expressions, with added limitation that no
local jump shall jump over a defer statement, nor shall return, break,
and continue, jump out of one

1: https://open-std.org/JTC1/SC22/WG14/www/docs/n3589.pdf

gcc/ChangeLog:

        * doc/invoke.texi: Add -fdefer-ts documentation.
        * doc/standards.texi: Likewise.

gcc/c-family/ChangeLog:

        * c-common.cc: Add defer keyword.
        * c-common.h (enum rid): Add RID_DEFER.
        (D_CXX11): Adjust.
        (D_EXT): Likewise.
        (D_EXT89): Likewise.
        (D_EXT11): Likewise.
        (D_ASM): Likewise.
        (D_OBJC): Likewise.
        (D_CXX_OBJC): Likewise.
        (D_CXXWARN): Likewise.
        (D_CXX_CONCEPTS): Likewise.
        (D_TRANSMEM): Likewise.
        (D_CXX_CHAR8_T): Likewise.
        (D_CXX20): Likewise.
        (D_CXX_COROUTINES): Likewise.
        (D_CXX_MODULES): Likewise.
        (D_DEFER): Add defer keyword mask.
        * c-cppbuiltin.cc (c_cpp_builtins): Set __STDC_DEFER_TS25755__.
        * c.opt: Add -fdefer-ts flag.

gcc/c/ChangeLog:

        * c-decl.cc (struct c_spot_bindings): Add defer_blocks.
        (struct c_scope): Add has_defer_block flag.
        (set_spot_bindings): Clear defer_blocks.
        (c_binding_adjust_jump_barrier): Adjust for defer.
        (c_bindings_start_stmt_expr): Rename to c_bindings_start_jump_barrier.
        (c_bindings_start_jump_barrier): Adjust for defer.
        (c_bindings_end_stmt_expr): Rename to c_bindings_end_jump_barrier.
        (c_bindings_end_jump_barrier): Adjust for defer.
        (lookup_label_for_goto): Check defer contraints.
        (check_earlier_gotos): Likewise.
        (c_release_switch_bindings): Likewise.
        (c_check_switch_jump_warnings): Likewise.
        * c-parser.cc (c_parser_defer_statement): New function.
        (c_parser_statement_after_labels): Check defer contraints and parse 
defer.
        * c-tree.h (IN_DEFER_STMT): Add variant.
        (c_bindings_start_stmt_expr): Rename to c_bindings_start_jump_barrier.
        (c_bindings_end_stmt_expr): Rename to c_bindings_end_jump_barrier.
        (c_bindings_start_jump_barrier): New function.
        (c_bindings_end_jump_barrier): New function.
        (c_begin_defer_block): New function.
        (c_end_defer_block): New function.
        * c-typeck.cc (c_finish_return): Adjust.
        (c_finish_bc_stmt): Adjust.
        (c_begin_stmt_expr): Likewise.
        (c_finish_stmt_expr): Likewise.
        (c_begin_defer_block): New function.
        (c_end_defer_block): New function.

gcc/testsuite/ChangeLog:

        * gcc.dg/defer-1.c: New test.
        * gcc.dg/defer-2.c: New test.
        * gcc.dg/defer-3.c: New test.
        * gcc.dg/defer-4.c: New test.

Signed-off-by: Anna (navi) Figueiredo Gomes <n...@vlhl.dev>
---
v2:
 * added a test jumping into a defer block from above.
 * reworded the lookup_label_for_goto error message to mention the error
is a jump into the defer block, instead of out.
 * moved the 'defer' keyword behind a -fdefer-ts flag, and added texinfo
docs plus related failure test.

 gcc/c-family/c-common.cc       |   1 +
 gcc/c-family/c-common.h        |  39 +++---
 gcc/c-family/c-cppbuiltin.cc   |   3 +
 gcc/c-family/c.opt             |   4 +
 gcc/c/c-decl.cc                |  85 +++++++++++--
 gcc/c/c-parser.cc              |  41 ++++++
 gcc/c/c-tree.h                 |   8 +-
 gcc/c/c-typeck.cc              |  46 +++++--
 gcc/doc/invoke.texi            |   7 +-
 gcc/doc/standards.texi         |   3 +-
 gcc/testsuite/gcc.dg/defer-1.c | 224 +++++++++++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/defer-2.c |  90 +++++++++++++
 gcc/testsuite/gcc.dg/defer-3.c |  15 +++
 gcc/testsuite/gcc.dg/defer-4.c |   7 ++
 14 files changed, 533 insertions(+), 40 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/defer-1.c
 create mode 100644 gcc/testsuite/gcc.dg/defer-2.c
 create mode 100644 gcc/testsuite/gcc.dg/defer-3.c
 create mode 100644 gcc/testsuite/gcc.dg/defer-4.c

diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index e7dd4602ac1..a82135ecac0 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -518,6 +518,7 @@ const struct c_common_resword c_common_reswords[] =
   { "continue",                RID_CONTINUE,   0 },
   { "decltype",         RID_DECLTYPE,   D_CXXONLY | D_CXX11 | D_CXXWARN },
   { "default",         RID_DEFAULT,    0 },
+  { "defer",           RID_DEFER,      D_DEFER | D_C2Y | D_CONLY },
   { "delete",          RID_DELETE,     D_CXXONLY | D_CXXWARN },
   { "do",              RID_DO,         0 },
   { "double",          RID_DOUBLE,     0 },
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 5c0fddd5221..90c0565b72c 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -97,11 +97,11 @@ enum rid
   RID_IMAGINARY,
 
   /* C */
-  RID_INT,     RID_CHAR,   RID_FLOAT,    RID_DOUBLE, RID_VOID,
-  RID_ENUM,    RID_STRUCT, RID_UNION,    RID_IF,     RID_ELSE,
-  RID_WHILE,   RID_DO,     RID_FOR,      RID_SWITCH, RID_CASE,
-  RID_DEFAULT, RID_BREAK,  RID_CONTINUE, RID_RETURN, RID_GOTO,
-  RID_SIZEOF,  RID_BITINT,
+  RID_INT,     RID_CHAR,   RID_FLOAT, RID_DOUBLE, RID_VOID,
+  RID_ENUM,    RID_STRUCT, RID_UNION, RID_IF,     RID_ELSE,
+  RID_WHILE,   RID_DO,     RID_FOR,   RID_SWITCH, RID_CASE,
+  RID_DEFAULT, RID_DEFER,  RID_BREAK, RID_CONTINUE, RID_RETURN,
+  RID_GOTO,    RID_SIZEOF, RID_BITINT,
 
   /* C extensions */
   RID_ASM,       RID_TYPEOF,   RID_TYPEOF_UNQUAL, RID_ALIGNOF,  RID_ATTRIBUTE,
@@ -435,20 +435,21 @@ extern machine_mode c_default_pointer_mode;
 #define D_C99          0x0004  /* In C, C99 only.  */
 #define D_C23          0x0008  /* In C, C23 only.  */
 #define D_C2Y          0x0010  /* In C, C2y only.  */
-#define D_CXX11         0x0020 /* In C++, C++11 only.  */
-#define D_EXT          0x0040  /* GCC extension.  */
-#define D_EXT89                0x0080  /* GCC extension incorporated in C99.  
*/
-#define D_EXT11                0x0100  /* GCC extension incorporated in C23.  
*/
-#define D_ASM          0x0200  /* Disabled by -fno-asm.  */
-#define D_OBJC         0x0400  /* In Objective C and neither C nor C++.  */
-#define D_CXX_OBJC     0x0800  /* In Objective C, and C++, but not C.  */
-#define D_CXXWARN      0x1000  /* In C warn with -Wcxx-compat.  */
-#define D_CXX_CONCEPTS  0x2000 /* In C++, only with concepts.  */
-#define D_TRANSMEM     0x4000  /* C++ transactional memory TS.  */
-#define D_CXX_CHAR8_T  0x8000  /* In C++, only with -fchar8_t.  */
-#define D_CXX20                0x10000  /* In C++, C++20 only.  */
-#define D_CXX_COROUTINES 0x20000  /* In C++, only with coroutines.  */
-#define D_CXX_MODULES  0x40000  /* In C++, only with modules.  */
+#define D_DEFER                0x0020  /* C defer statements TS.  */
+#define D_CXX11                0x0040  /* In C++, C++11 only.  */
+#define D_EXT          0x0080  /* GCC extension.  */
+#define D_EXT89                0x0100  /* GCC extension incorporated in C99.  
*/
+#define D_EXT11                0x0200  /* GCC extension incorporated in C23.  
*/
+#define D_ASM          0x0400  /* Disabled by -fno-asm.  */
+#define D_OBJC         0x0800  /* In Objective C and neither C nor C++.  */
+#define D_CXX_OBJC     0x1000  /* In Objective C, and C++, but not C.  */
+#define D_CXXWARN      0x2000  /* In C warn with -Wcxx-compat.  */
+#define D_CXX_CONCEPTS  0x4000 /* In C++, only with concepts.  */
+#define D_TRANSMEM             0x8000  /* C++ transactional memory TS.  */
+#define D_CXX_CHAR8_T  0x10000 /* In C++, only with -fchar8_t.  */
+#define D_CXX20                0x20000  /* In C++, C++20 only.  */
+#define D_CXX_COROUTINES 0x40000  /* In C++, only with coroutines.  */
+#define D_CXX_MODULES  0x80000  /* In C++, only with modules.  */
 
 #define D_CXX_CONCEPTS_FLAGS D_CXXONLY | D_CXX_CONCEPTS
 #define D_CXX_CHAR8_T_FLAGS D_CXXONLY | D_CXX_CHAR8_T
diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc
index 4aea9028863..3d2013e5eaa 100644
--- a/gcc/c-family/c-cppbuiltin.cc
+++ b/gcc/c-family/c-cppbuiltin.cc
@@ -1596,6 +1596,9 @@ c_cpp_builtins (cpp_reader *pfile)
   if (flag_iso)
     cpp_define (pfile, "__STRICT_ANSI__");
 
+  if (flag_isoc2y && flag_defer_ts)
+    builtin_define_with_int_value ("__STDC_DEFER_TS25755__", 1);
+
   if (!flag_signed_char)
     cpp_define (pfile, "__CHAR_UNSIGNED__");
 
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 12877eb0e17..26db46c75f2 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1931,6 +1931,10 @@ fdefault-inline
 C++ ObjC++ Ignore
 Does nothing.  Preserved for backward compatibility.
 
+fdefer-ts
+C Var(flag_defer_ts)
+Enables support for defer statements from ISO/DIS TS 25755 for ISO C2Y
+
 fdiagnostics-show-template-tree
 C++ ObjC++ Var(flag_diagnostics_show_template_tree) Init(0)
 Print hierarchical comparisons when template types are mismatched.
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 56ca3078226..176e084e9ed 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -397,7 +397,7 @@ struct GTY(()) c_spot_bindings {
      of the label or goto.  This lets us look at older or newer
      bindings in the scope, as appropriate.  */
   struct c_binding *bindings_in_scope;
-  struct c_jump_barrier stmt_exprs;
+  struct c_jump_barrier stmt_exprs, defer_blocks;
 };
 
 /* This structure is used to keep track of bindings seen when a goto
@@ -533,6 +533,10 @@ struct GTY((chain_next ("%h.outer"))) c_scope {
      decl_jump_unsafe would return true for any of the bindings.  This
      is used to avoid looping over all the bindings unnecessarily.  */
   BOOL_BITFIELD has_jump_unsafe_decl : 1;
+
+  /* True if this scope has any deferred statement.  This is used to
+   * check invalid goto jumps. */
+  BOOL_BITFIELD has_defer_block : 1;
 };
 
 /* The scope currently in effect.  */
@@ -986,6 +990,8 @@ set_spot_bindings (struct c_spot_bindings *p, bool defining)
     }
   p->stmt_exprs.count = 0;
   p->stmt_exprs.left = false;
+  p->defer_blocks.count = 0;
+  p->defer_blocks.left = false;
 }
 
 /* Update spot bindings P as we pop out of SCOPE.  Return true if we
@@ -1605,9 +1611,12 @@ finish_underspecified_init (tree name, unsigned int 
prev_state)
 /* Adjust the bindings for the start of a statement expression.  */
 
 static void
-c_binding_adjust_jump_barrier (struct c_spot_bindings *binding, bool start)
+c_binding_adjust_jump_barrier (struct c_spot_bindings *binding,
+                              bool defer, bool start)
 {
-  struct c_jump_barrier *barrier = &binding->stmt_exprs;
+  struct c_jump_barrier *barrier = defer
+    ? &binding->defer_blocks
+    : &binding->stmt_exprs;
   barrier->count += start ? 1 : -1;
   if (barrier->count < 0)
     {
@@ -1617,10 +1626,13 @@ c_binding_adjust_jump_barrier (struct c_spot_bindings 
*binding, bool start)
 }
 
 void
-c_bindings_start_stmt_expr (struct c_spot_bindings* switch_bindings)
+c_bindings_start_jump_barrier (bool defer, struct c_spot_bindings* 
switch_bindings)
 {
   struct c_scope *scope;
 
+  if (defer)
+    current_scope->has_defer_block = true;
+
   for (scope = current_scope; scope != NULL; scope = scope->outer)
     {
       struct c_binding *b;
@@ -1638,20 +1650,24 @@ c_bindings_start_stmt_expr (struct c_spot_bindings* 
switch_bindings)
            continue;
          label_vars = b->u.label;
 
-         c_binding_adjust_jump_barrier (&label_vars->label_bindings, true);
+         c_binding_adjust_jump_barrier (&label_vars->label_bindings, defer, 
true);
          FOR_EACH_VEC_SAFE_ELT (label_vars->gotos, ix, g)
-             c_binding_adjust_jump_barrier (&g->goto_bindings, true);
+             c_binding_adjust_jump_barrier (&g->goto_bindings, defer, true);
        }
     }
 
   if (switch_bindings != NULL)
-      c_binding_adjust_jump_barrier (switch_bindings, true);
+    {
+      if (defer)
+       switch_bindings->scope->has_defer_block = true;
+      c_binding_adjust_jump_barrier (switch_bindings, defer, true);
+    }
 }
 
 /* Adjust the bindings for the end of a statement expression.  */
 
 void
-c_bindings_end_stmt_expr (struct c_spot_bindings *switch_bindings)
+c_bindings_end_jump_barrier (bool defer, struct c_spot_bindings 
*switch_bindings)
 {
   struct c_scope *scope;
 
@@ -1671,9 +1687,9 @@ c_bindings_end_stmt_expr (struct c_spot_bindings 
*switch_bindings)
          if (TREE_CODE (b->decl) != LABEL_DECL)
            continue;
          label_vars = b->u.label;
-         c_binding_adjust_jump_barrier (&label_vars->label_bindings, false);
+         c_binding_adjust_jump_barrier (&label_vars->label_bindings, defer, 
false);
          FOR_EACH_VEC_SAFE_ELT (label_vars->gotos, ix, g)
-           c_binding_adjust_jump_barrier (&g->goto_bindings, false);
+           c_binding_adjust_jump_barrier (&g->goto_bindings, defer, false);
        }
     }
 
@@ -1681,6 +1697,7 @@ c_bindings_end_stmt_expr (struct c_spot_bindings 
*switch_bindings)
     {
       c_binding_adjust_jump_barrier (switch_bindings, defer, false);
       gcc_assert (switch_bindings->stmt_exprs.count >= 0);
+      gcc_assert (switch_bindings->defer_blocks.count >= 0);
     }
 }
 
@@ -4158,6 +4175,17 @@ lookup_label_for_goto (location_t loc, tree name)
       inform (DECL_SOURCE_LOCATION (label), "label %qD defined here", label);
     }
 
+  if (label_vars->label_bindings.defer_blocks.left)
+    {
+      error_at (loc, "jump into defer block");
+      inform (DECL_SOURCE_LOCATION (label), "label %qD defined here", label);
+    }
+  else if (label_vars->label_bindings.scope->has_defer_block)
+    {
+      error_at (loc, "jump over defer block");
+      inform (DECL_SOURCE_LOCATION (label), "label %qD defined here", label);
+    }
+
   return label;
 }
 
@@ -4249,6 +4277,23 @@ check_earlier_gotos (tree label, struct c_label_vars* 
label_vars)
          inform (DECL_SOURCE_LOCATION (label), "label %qD defined here",
                  label);
        }
+
+      if (g->goto_bindings.defer_blocks.count > 0)
+       {
+         error_at (g->loc, "jump into defer block");
+         inform (DECL_SOURCE_LOCATION (label), "label %qD defined here", 
label);
+       }
+      else if (g->goto_bindings.defer_blocks.left)
+       {
+         error_at (g->loc, "jump out of defer block");
+         inform (DECL_SOURCE_LOCATION (label), "label %qD defined here", 
label);
+       }
+      else if (label_vars->label_bindings.scope->has_defer_block
+              || g->goto_bindings.scope->has_defer_block)
+       {
+         error_at (g->loc, "jump over defer block");
+         inform (DECL_SOURCE_LOCATION (label), "label %qD defined here", 
label);
+       }
     }
 
   /* Now that the label is defined, we will issue warnings about
@@ -4334,6 +4379,7 @@ void
 c_release_switch_bindings (struct c_spot_bindings *bindings)
 {
   gcc_assert (bindings->stmt_exprs.count == 0 && !bindings->stmt_exprs.left);
+  gcc_assert (bindings->defer_blocks.count == 0 && 
!bindings->defer_blocks.left);
   XDELETE (bindings);
 }
 
@@ -4405,6 +4451,25 @@ c_check_switch_jump_warnings (struct c_spot_bindings 
*switch_bindings,
       inform (switch_loc, "switch starts here");
     }
 
+  if (switch_bindings->defer_blocks.count > 0)
+    {
+      saw_error = true;
+      error_at (case_loc, "switch jumps into defer block");
+      inform (switch_loc, "switch starts here");
+    }
+  else if (switch_bindings->defer_blocks.left)
+    {
+      saw_error = true;
+      error_at (case_loc, "switch jumps out of defer block");
+      inform (switch_loc, "switch starts here");
+    }
+  else if (switch_bindings->scope->has_defer_block)
+    {
+      saw_error = true;
+      error_at (case_loc, "switch jumps over defer block");
+      inform (switch_loc, "switch starts here");
+    }
+
   return saw_error;
 }
 
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 1907293bc68..34c615f80b5 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -129,6 +129,8 @@ c_parse_init (void)
     mask |= D_C23;
   if (!flag_isoc2y)
     mask |= D_C2Y;
+  if (!flag_defer_ts)
+    mask |= D_DEFER;
   if (flag_no_asm)
     {
       mask |= D_ASM | D_EXT;
@@ -1765,6 +1767,7 @@ static void c_parser_statement_after_labels (c_parser *, 
bool *, tree,
 static tree c_parser_c99_block_statement (c_parser *, bool *,
                                          location_t * = NULL);
 static void c_parser_if_statement (c_parser *, bool *, vec<tree> *);
+static void c_parser_defer_statement (c_parser *parser, bool *if_p, tree 
before_labels);
 static void c_parser_switch_statement (c_parser *, bool *, tree);
 static void c_parser_while_statement (c_parser *, bool, unsigned short, bool,
                                      bool *, tree);
@@ -8230,6 +8233,9 @@ c_parser_statement_after_labels (c_parser *parser, bool 
*if_p,
        case RID_FOR:
          c_parser_for_statement (parser, false, 0, false, if_p, before_labels);
          break;
+       case RID_DEFER:
+         c_parser_defer_statement (parser, if_p, before_labels);
+         break;
        case RID_GOTO:
          c_parser_consume_token (parser);
          if (c_parser_next_token_is (parser, CPP_NAME))
@@ -8756,6 +8762,41 @@ c_parser_if_statement (c_parser *parser, bool *if_p, 
vec<tree> *chain)
   c_parser_maybe_reclassify_token (parser);
 }
 
+/* Parse a defer statement (ISO/DIS TS 25755 6.4).
+
+   defer-statement:
+     defer deferred-block */
+
+static void
+c_parser_defer_statement (c_parser *parser, bool *if_p, tree before_labels)
+{
+  location_t loc = c_parser_peek_token (parser)->location;
+  unsigned char save_in_statement = in_statement;
+  tree deferred;
+
+  gcc_assert (c_parser_next_token_is_keyword (parser, RID_DEFER));
+  c_parser_consume_token (parser);
+
+  if (c_parser_next_token_is (parser, CPP_SEMICOLON))
+    {
+      c_end_defer_block (loc, c_begin_defer_block ());
+      add_stmt (build_empty_stmt (loc));
+      c_parser_consume_token (parser);
+      warning_at (loc, OPT_Wempty_body,
+       "suggest braces around empty body in %<defer%> statement");
+      return;
+    }
+
+  deferred = c_begin_defer_block ();
+
+  in_statement = IN_DEFER_STMT;
+  c_parser_statement_after_labels (parser, if_p, before_labels);
+  in_statement = save_in_statement;
+
+  deferred = c_end_defer_block (loc, deferred);
+  push_cleanup(deferred, deferred, false);
+}
+
 /* Parse a switch statement (C90 6.6.4, C99 6.8.4, C11 6.8.4).
 
    switch-statement:
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index bb0b113754e..7ab9d009e76 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -643,6 +643,7 @@ extern struct obstack parser_obstack;
 #define IN_OMP_FOR             8
 #define IN_OBJC_FOREACH                16
 #define IN_NAMED_STMT          32
+#define IN_DEFER_STMT          64
 extern unsigned char in_statement;
 
 extern bool switch_statement_break_seen_p;
@@ -654,8 +655,8 @@ extern void finish_underspecified_init (tree, unsigned int);
 extern void push_scope (void);
 extern tree pop_scope (void);
 extern void c_mark_decl_jump_unsafe_in_current_scope ();
-extern void c_bindings_start_stmt_expr (struct c_spot_bindings *);
-extern void c_bindings_end_stmt_expr (struct c_spot_bindings *);
+extern void c_bindings_start_jump_barrier (bool, struct c_spot_bindings *);
+extern void c_bindings_end_jump_barrier (bool, struct c_spot_bindings *);
 
 extern void record_inline_static (location_t, tree, tree,
                                  enum c_inline_static_type);
@@ -869,6 +870,9 @@ extern tree c_end_compound_stmt (location_t, tree, bool);
 extern void c_finish_if_stmt (location_t, tree, tree, tree);
 extern void c_finish_loop (location_t, location_t, tree, location_t, tree,
                           tree, tree, tree, bool);
+
+extern tree c_begin_defer_block (void);
+extern tree c_end_defer_block (location_t loc, tree body);
 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);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index f26184e5603..7a939a823e1 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -12871,6 +12871,12 @@ c_finish_return (location_t loc, tree retval, tree 
origtype, bool musttail_p)
     warning_at (xloc, 0,
                "function declared %<noreturn%> has a %<return%> statement");
 
+  if (in_statement & IN_DEFER_STMT)
+    {
+      error_at (loc, "return from defer block");
+      return NULL_TREE;
+    }
+
   set_musttail_on_return (retval, xloc, musttail_p);
 
   if (retval)
@@ -13252,6 +13258,9 @@ c_finish_bc_stmt (location_t loc, tree label, bool 
is_break, tree name)
       case IN_OMP_FOR:
        error_at (loc, "break statement used with OpenMP for loop");
        return NULL_TREE;
+      case IN_DEFER_STMT:
+       error_at (loc, "break statement jumps out of defer block");
+       return NULL_TREE;
       case IN_ITERATION_STMT:
       case IN_OBJC_FOREACH:
        break;
@@ -13269,6 +13278,9 @@ c_finish_bc_stmt (location_t loc, tree label, bool 
is_break, tree name)
       case IN_OMP_BLOCK:
        error_at (loc, "invalid exit from OpenMP structured block");
        return NULL_TREE;
+      case IN_DEFER_STMT:
+       error_at (loc, "continue statement jumps out of defer block");
+       return NULL_TREE;
       case IN_ITERATION_STMT:
       case IN_OMP_FOR:
       case IN_OBJC_FOREACH:
@@ -13414,9 +13426,9 @@ c_begin_stmt_expr (void)
   keep_next_level ();
   ret = c_begin_compound_stmt (true);
 
-  c_bindings_start_stmt_expr (c_switch_stack == NULL
-                             ? NULL
-                             : c_switch_stack->bindings);
+  c_bindings_start_jump_barrier (false, c_switch_stack == NULL
+                            ? NULL
+                            : c_switch_stack->bindings);
 
   /* Mark the current statement list as belonging to a statement list.  */
   STATEMENT_LIST_STMT_EXPR (ret) = 1;
@@ -13435,9 +13447,9 @@ c_finish_stmt_expr (location_t loc, tree body)
 
   body = c_end_compound_stmt (loc, body, true);
 
-  c_bindings_end_stmt_expr (c_switch_stack == NULL
-                           ? NULL
-                           : c_switch_stack->bindings);
+  c_bindings_end_jump_barrier (false, c_switch_stack == NULL
+                         ? NULL
+                         : c_switch_stack->bindings);
 
   /* Locate the last statement in BODY.  See c_end_compound_stmt
      about always returning a BIND_EXPR.  */
@@ -13532,7 +13544,27 @@ c_finish_stmt_expr (location_t loc, tree body)
     return t;
   }
 }
-
+
+tree
+c_begin_defer_block (void)
+{
+  c_bindings_start_jump_barrier (true, c_switch_stack == NULL
+                            ? NULL
+                            : c_switch_stack->bindings);
+
+  return c_begin_compound_stmt (true);
+}
+
+tree
+c_end_defer_block (location_t loc, tree body)
+{
+  c_bindings_end_jump_barrier (true, c_switch_stack == NULL
+                         ? NULL
+                         : c_switch_stack->bindings);
+
+  return c_end_compound_stmt (loc, body, true);
+}
+
 /* Begin and end compound statements.  This is as simple as pushing
    and popping new statement lists from the tree.  */
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index c1e708beacf..b42a7a28b7f 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -206,7 +206,7 @@ in the following sections.
 -fpermitted-flt-eval-methods=@var{standard}
 -fplan9-extensions  -fsigned-bitfields  -funsigned-bitfields
 -fsigned-char  -funsigned-char  -fstrict-flex-arrays[=@var{n}]
--fsso-struct=@var{endianness}}
+-fsso-struct=@var{endianness} -fdefer-ts}
 
 @item C++ Language Options
 @xref{C++ Dialect Options,,Options Controlling C++ Dialect}.
@@ -2902,6 +2902,11 @@ the target (the default).  This option is not supported 
for C++.
 @strong{Warning:} the @option{-fsso-struct} switch causes GCC to generate
 code that is not binary compatible with code generated without it if the
 specified endianness is not the native endianness of the target.
+
+@opindex fdefer-ts
+@item -fdefer-ts
+Enables support for the defer keyword, as specified by ISO/DIS TS 25755,
+for @dfn{C2Y}.
 @end table
 
 @node C++ Dialect Options
diff --git a/gcc/doc/standards.texi b/gcc/doc/standards.texi
index 0d765b17aa2..4de2ca568c5 100644
--- a/gcc/doc/standards.texi
+++ b/gcc/doc/standards.texi
@@ -123,7 +123,8 @@ enabled with @option{-std=c23} or 
@option{-std=iso9899:2024}.
 
 A further version of the C standard, known as @dfn{C2Y}, is under
 development; experimental and incomplete support for this is enabled
-with @option{-std=c2y}.
+with @option{-std=c2y}. Under @dfn{C2Y}, GCC implements the defer
+technical specification, ISO/DIS TS 25755, enabled with @option{-fdefer-ts}.
 
 By default, GCC provides some extensions to the C language that, on
 rare occasions conflict with the C standard.  @xref{C
diff --git a/gcc/testsuite/gcc.dg/defer-1.c b/gcc/testsuite/gcc.dg/defer-1.c
new file mode 100644
index 00000000000..15323ddc2f3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/defer-1.c
@@ -0,0 +1,224 @@
+/* { dg-do run } */
+/* { dg-options "-std=c2y -fdefer-ts" } */
+
+#include <setjmp.h>
+
+#define assert(x) if (!(x)) __builtin_abort ()
+
+#if __STDC_DEFER_TS25755__ != 1
+# error "missing defer flag"
+#endif
+
+int a ()
+{
+  int r = 5;
+  return r;
+
+  defer r *= 2;
+}
+
+int b ()
+{
+  int r = 0;
+  goto b;
+  {
+    defer r += 2;
+  }
+b:
+  r += 5;
+  return r;
+}
+
+int c ()
+{
+  int r = 0;
+  {
+    defer r += 2;
+    goto b;
+  }
+b:
+  r += 5;
+  return r;
+}
+
+int d ()
+{
+  int r = 0;
+  goto b;
+  {
+b:
+    defer r += 2;
+  }
+  r += 5;
+  return r;
+}
+
+int e ()
+{
+  int r = 0;
+  {
+    goto b;
+    defer r += 2;
+  }
+b:;
+  r += 5;
+  return r;
+}
+
+int f ()
+{
+  int r = 4;
+  int *p = &r;
+  defer *p = 5;
+  return *p;
+}
+
+int g ()
+{
+  int r = 1;
+  {
+    defer r += 2;
+    if (true)
+      defer r *= 2;
+    r += 10;
+  }
+  return r;
+}
+
+int h ()
+{
+  int r = 1;
+  {
+    defer r += 2;
+    for (int i = 0; i < 5; i++)
+      defer r *= 2;
+  }
+  return r;
+}
+
+int i ()
+{
+  int r = 0;
+  {
+    defer {
+      defer r *= 4;
+      r *= 2;
+      defer {
+       r += 3;
+      }
+    }
+    defer r += 1;
+  }
+
+  return r;
+}
+
+int j ()
+{
+  int r = 0;
+  {
+    defer if (0) r = 5;
+  }
+  return r;
+}
+
+int global = 0;
+
+int k ()
+{
+  global = 5;
+  defer global = 10;
+  return global;
+}
+
+int l ()
+{
+  int r = 5;
+  int j = 2;
+
+  {
+    defer {
+      int j = 10;
+      r *= j;
+    }
+  }
+
+  return r;
+}
+
+int m ()
+{
+  int r = 0;
+
+  void clear(int *value) {
+    *value = 10;
+  }
+
+  {
+    [[gnu::cleanup(clear)]] int j = 1;
+    defer r = j * 2;
+  }
+  return r;
+}
+
+int n ()
+{
+  jmp_buf env;
+  int r = 0;
+
+  if (setjmp(env) != 0)
+    return r;
+
+  defer r = 1;
+
+  {
+    defer r = 2;
+    r++;
+    longjmp(env, 1);
+  }
+}
+
+int o ()
+{
+  int r = 0;
+
+  int foo() {
+    r = 2;
+    defer r *= 2;
+    return r;
+  }
+
+  return foo() + r;
+}
+
+int p (int *arg)
+{
+  *arg = 2;
+  defer *arg *= 2;
+  return *arg;
+}
+
+  int
+main ()
+{
+  int arg;
+
+  assert (a () == 5);
+  assert (b () == 5);
+  assert (c () == 7);
+  assert (d () == 7);
+  assert (e () == 5);
+  assert (f () == 4);
+  assert (g () == 14);
+  assert (h () == 34);
+  assert (i () == 20);
+  assert (j () == 0);
+  assert (k () == 5 && global == 10);
+  assert (l () == 50);
+  assert (m () == 2);
+  assert (n () == 1);
+  assert (o () == 6);
+  assert (p (&arg) == 2 && arg == 4);
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/defer-2.c b/gcc/testsuite/gcc.dg/defer-2.c
new file mode 100644
index 00000000000..7d9177db0f5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/defer-2.c
@@ -0,0 +1,90 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c2y -fdefer-ts" } */
+
+void a ()
+{
+  goto b; /* { dg-error "jump over defer block" } */
+  defer;
+b:
+}
+
+void b () {
+  defer {
+    goto b; /* { dg-error "jump out of defer block" } */
+  }
+b:
+}
+
+int c () {
+  defer {
+    return 5; /* { dg-error "return from defer block" } */
+  }
+  return 1;
+}
+
+void d () {
+  defer {
+b:
+  }
+  goto b; /* { dg-error "jump into defer block" } */
+}
+
+void e () {
+  goto b; /* { dg-error "jump over defer block" } */
+  {
+    defer;
+b:
+  }
+}
+
+void f ()
+{
+  goto b; /* { dg-error "jump into defer block" } */
+  defer {
+b:
+  };
+}
+
+void g () {
+  switch (1) {
+    defer;
+    default: /* { dg-error "switch jumps over defer block" } */
+    defer;
+    break;
+  }
+}
+
+void h () {
+  switch (1) {
+    default:
+      defer {
+       break; /* { dg-error "break statement jumps out of defer block" } */
+      }
+  }
+
+  for (;;) {
+    defer {
+      break; /* { dg-error "break statement jumps out of defer block" } */
+    }
+  }
+
+  for (;;) {
+    defer {
+      continue; /* { dg-error "continue statement jumps out of defer block" } 
*/
+    }
+  }
+}
+
+int i ()
+{
+  int r = 1;
+  {
+b:
+    r++;
+    defer r *= 2;
+    if (r < 10)
+      goto b; /* { dg-error "jump over defer block" } */
+  }
+
+  return r;
+}
diff --git a/gcc/testsuite/gcc.dg/defer-3.c b/gcc/testsuite/gcc.dg/defer-3.c
new file mode 100644
index 00000000000..c31e0b89224
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/defer-3.c
@@ -0,0 +1,15 @@
+/* { dg-do run } */
+/* { dg-options "-std=c2y -fdefer-ts" } */
+
+extern void exit(int);
+extern void abort(void);
+
+int main(void) {
+  {
+    defer {
+      exit(0);
+    }
+  }
+  abort();
+  return 1;
+}
diff --git a/gcc/testsuite/gcc.dg/defer-4.c b/gcc/testsuite/gcc.dg/defer-4.c
new file mode 100644
index 00000000000..eb351707112
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/defer-4.c
@@ -0,0 +1,7 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c2y" } */
+
+int main(void)
+{
+  defer; /* { dg-error "'defer' undeclared" "undeclared identifier" } */
+}
-- 
2.49.1

Reply via email to