based on the technical specification 25755[1] as an extension

_Defer is provided as a keyword if gnu extensions are enabled, and
stddefer.h provided a macro for the 'defer' spelling

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/n3734.pdf

gcc/ChangeLog:

        * Makefile.in:
        * doc/invoke.texi:
        * ginclude/stddefer.h: New file.

gcc/c-family/ChangeLog:

        * c-common.cc: Add _Defer keywords.
        * c-common.h (enum rid): Add RID_DEFER.
        * c-cppbuiltin.cc (c_cpp_builtins): Set __STDC_DEFER_TS25755__.

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): Handle deferred blocks.
        (c_bindings_start_stmt_expr): Rename to c_bindings_start_jump_barrier.
        (c_bindings_start_jump_barrier): Handle deferred statements.
        (c_bindings_end_stmt_expr): Rename to c_bindings_end_jump_barrier.
        (c_bindings_end_jump_barrier): Handle deferred statements.
        (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):
        (c_finish_bc_stmt): Check for IN_DEFER_STMT.
        (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 <[email protected]>
---

Notes:
    v6:
     * remove -fdefer-ts flag and 'defer' as a keyword
     * add stddefer.h containing a defer -> _Defer macro
    v5:
     * enable '_Defer' keyword when extensions are enabled
    v4:
     * remove constraint on local backwards jump, making behaviour the same
    as the cleanup attribute.
    v3:
     * remove the need of -std=c2y for -fdefer-ts to take effect
    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/Makefile.in                |   1 +
 gcc/c-family/c-common.cc       |   1 +
 gcc/c-family/c-common.h        |   1 +
 gcc/c-family/c-cppbuiltin.cc   |   3 +
 gcc/c/c-decl.cc                |  80 +++++++++--
 gcc/c/c-parser.cc              |  39 +++++
 gcc/c/c-tree.h                 |   8 +-
 gcc/c/c-typeck.cc              |  46 +++++-
 gcc/ginclude/stddefer.h        |  38 +++++
 gcc/testsuite/gcc.dg/defer-1.c | 256 +++++++++++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/defer-2.c |  78 ++++++++++
 gcc/testsuite/gcc.dg/defer-3.c |  17 +++
 gcc/testsuite/gcc.dg/defer-4.c |   8 ++
 13 files changed, 557 insertions(+), 19 deletions(-)
 create mode 100644 gcc/ginclude/stddefer.h
 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/Makefile.in b/gcc/Makefile.in
index d7d5cbe7277..e9ac6a437c5 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -481,6 +481,7 @@ USER_H = $(srcdir)/ginclude/float.h \
         $(srcdir)/ginclude/stdatomic.h \
         $(srcdir)/ginclude/stdckdint.h \
         $(srcdir)/ginclude/stdcountof.h \
+        $(srcdir)/ginclude/stddefer.h \
         $(EXTRA_HEADERS)
 
 USER_H_INC_NEXT_PRE = @user_headers_inc_next_pre@
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index e7dd4602ac1..af019bd37bf 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -412,6 +412,7 @@ const struct c_common_resword c_common_reswords[] =
   { "_Decimal64",       RID_DFLOAT64,  D_CONLY },
   { "_Decimal128",      RID_DFLOAT128, D_CONLY },
   { "_Decimal64x",      RID_DFLOAT64X, D_CONLY },
+  { "_Defer",           RID_DEFER,     D_CONLY | D_EXT },
   { "_Fract",           RID_FRACT,     D_CONLY | D_EXT },
   { "_Accum",           RID_ACCUM,     D_CONLY | D_EXT },
   { "_Sat",             RID_SAT,       D_CONLY | D_EXT },
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 7c7e21d2d0e..be7d51193c6 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -113,6 +113,7 @@ enum rid
   RID_BUILTIN_HAS_ATTRIBUTE,   RID_BUILTIN_ASSOC_BARRIER,  RID_BUILTIN_STDC,
   RID_BUILTIN_COUNTED_BY_REF,
   RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128, RID_DFLOAT64X,
+  RID_DEFER,
 
   /* TS 18661-3 keywords, in the same sequence as the TI_* values.  */
   RID_FLOAT16,
diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc
index 4aea9028863..d9acaa1d72c 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_no_gnu_keywords)
+    builtin_define_with_int_value ("__STDC_DEFER_TS25755__", 1);
+
   if (!flag_signed_char)
     cpp_define (pfile, "__CHAR_UNSIGNED__");
 
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 56ca3078226..0bfb964de26 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,12 @@ 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);
+    }
+
   return label;
 }
 
@@ -4249,6 +4272,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 +4374,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 +4446,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 4a13fc0d384..a86b41b2356 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -1763,6 +1763,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);
@@ -8228,6 +8229,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))
@@ -8754,6 +8758,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/ginclude/stddefer.h b/gcc/ginclude/stddefer.h
new file mode 100644
index 00000000000..b142eebe490
--- /dev/null
+++ b/gcc/ginclude/stddefer.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 1998-2025 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+/*
+ * ISO C Standard:  7.16  Boolean type and values  <stddefer.h>
+ */
+
+#ifndef _STDDEFER_H_
+#define _STDDEFER_H_
+
+#ifndef __cplusplus
+
+#define __STDC_VERSION_STDDEFER_H__ 202602L
+#define defer  _Defer
+
+#endif /* __cplusplus */
+
+#endif /* stddefer.h */
diff --git a/gcc/testsuite/gcc.dg/defer-1.c b/gcc/testsuite/gcc.dg/defer-1.c
new file mode 100644
index 00000000000..8432a04a2e8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/defer-1.c
@@ -0,0 +1,256 @@
+/* { dg-do run } */
+/* { dg-options "" } */
+
+#include <stddefer.h>
+#include <setjmp.h>
+
+#define assert(x) if (!(x)) __builtin_abort ()
+
+#if __STDC_DEFER_TS25755__ != 2
+# 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 q ()
+{
+  int r = 1;
+  {
+b:
+    defer r *= 2;
+    if (r < 10)
+      goto b;
+  }
+
+  return r;
+}
+
+int r ()
+{
+  int r = 1;
+  {
+    defer r *= 2;
+b:
+    if (r < 10)
+      {
+       r++;
+       goto b;
+      }
+  }
+
+  return r;
+}
+
+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);
+  assert (q () == 32);
+  assert (r () == 20);
+
+  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..3e07a1b7840
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/defer-2.c
@@ -0,0 +1,78 @@
+/* { dg-do compile } */
+/* { dg-options "-fdefer-kw" } */
+
+#include <stddefer.h>
+
+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" } 
*/
+    }
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/defer-3.c b/gcc/testsuite/gcc.dg/defer-3.c
new file mode 100644
index 00000000000..ea2fff6b761
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/defer-3.c
@@ -0,0 +1,17 @@
+/* { dg-do run } */
+/* { dg-options "" } */
+
+#include <stddefer.h>
+
+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..67dd5d29365
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/defer-4.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+int main(void)
+{
+  _Defer;
+  defer; /* { dg-error "'defer' undeclared" "undeclared identifier" } */
+}
-- 
2.51.0

Reply via email to