On Mon, Oct 27, 2025 at 4:20 PM Anna (navi) Figueiredo Gomes
<[email protected]> wrote:
>
> based on the technical specification 25755[1][2]
>
> the keyword '_Defer' is enabled by default as an extension, while
> 'defer' is gated by '-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
> 2: 
> https://thephd.dev/_vendor/future_cxx/technical%20specification/C%20-%20defer/C%20-%20defer%20Technical%20Specification.pdf
>
> gcc/ChangeLog:
>
>         * doc/invoke.texi: Add -fdefer-ts documentation.
>         * doc/standards.texi: Likewise.
>
> gcc/c-family/ChangeLog:
>
>         * c-common.cc: Add defer and _Defer keywords.
>         * 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 <[email protected]>
> ---
> Notes:
>         v5:
>          * Add '_Defer' to all c versions as an extension, while keeping
>         'defer' under '-fdefer-ts'.
>         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/c-family/c-common.cc       |   2 +
>  gcc/c-family/c-common.h        |   2 +
>  gcc/c-family/c-cppbuiltin.cc   |   3 +
>  gcc/c-family/c.opt             |   4 +
>  gcc/c/c-decl.cc                |  80 +++++++++--
>  gcc/c/c-parser.cc              |  41 ++++++
>  gcc/c/c-tree.h                 |   8 +-
>  gcc/c/c-typeck.cc              |  46 +++++-
>  gcc/doc/invoke.texi            |   6 +-
>  gcc/doc/standards.texi         |   3 +-
>  gcc/testsuite/gcc.dg/defer-1.c | 255 +++++++++++++++++++++++++++++++++
>  gcc/testsuite/gcc.dg/defer-2.c |  76 ++++++++++
>  gcc/testsuite/gcc.dg/defer-3.c |  15 ++
>  gcc/testsuite/gcc.dg/defer-4.c |   8 ++
>  14 files changed, 528 insertions(+), 21 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..4fc31b0c924 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 },
> @@ -518,6 +519,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_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 7c7e21d2d0e..92b96c6e561 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,
> @@ -448,6 +449,7 @@ extern machine_mode c_default_pointer_mode;
>  #define D_CXX20                0x8000  /* In C++, C++20 only.  */
>  #define D_CXX_COROUTINES 0x10000  /* In C++, only with coroutines.  */
>  #define D_CXX_MODULES  0x20000  /* In C++, only with modules.  */
> +#define D_DEFER                0x40000 /* In C, only with -fdefer-ts */
>
>  #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..638bf810f07 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 || flag_defer_ts)
> +    builtin_define_with_int_value ("__STDC_DEFER_TS25755__", 1);

So the question becomes should __STDC_DEFER_TS25755__ be defined if
_Defer is only supported or should there be another define for that?

> +
>    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

Maybe:
Enables support for defer keyword and defer statements from ISO/DIS TS
25755 for ISO C2Y
Also I don't see why this can't be enabled for ObjC either.


> +
>  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..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)
>  {
As mentioned in the other patch, the comment before the function
should describe the (new) arguments.

> -  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..34b9e334436 100644
> --- a/gcc/c/c-parser.cc
> +++ b/gcc/c/c-parser.cc
> @@ -127,6 +127,8 @@ c_parse_init (void)
>      mask |= D_C99;
>    if (!flag_isoc23)
>      mask |= D_C23;
> +  if (!flag_defer_ts)
> +    mask |= D_DEFER;
>    if (flag_no_asm)
>      {
>        mask |= D_ASM | D_EXT;
> @@ -1763,6 +1765,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 +8231,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 +8760,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)
> +{

As mentioned, all functions need a comment before saying what it does
including what the arguments mean. (yes not all GCC follows this, I
have been trying to fix those up).

> +  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..368aa6ab227 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,10 @@ 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.

I would add "This option is not supported for C++." like other options
documentation.

Otherwise the patch looks good to me.

Thanks,
Andrew

>  @end table
>
>  @node C++ Dialect Options
> diff --git a/gcc/doc/standards.texi b/gcc/doc/standards.texi
> index 0d765b17aa2..8d82c92da0e 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}. 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..4b7cbe67921
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/defer-1.c
> @@ -0,0 +1,255 @@
> +/* { dg-do run } */
> +/* { dg-options "-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 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..5123024f11d
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/defer-2.c
> @@ -0,0 +1,76 @@
> +/* { dg-do compile } */
> +/* { dg-options "-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" 
> } */
> +    }
> +  }
> +}
> diff --git a/gcc/testsuite/gcc.dg/defer-3.c b/gcc/testsuite/gcc.dg/defer-3.c
> new file mode 100644
> index 00000000000..55217c15557
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/defer-3.c
> @@ -0,0 +1,15 @@
> +/* { dg-do run } */
> +/* { dg-options "-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..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