* 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.

---

based on n3589[1], this feature mostly makes use of already-established
logic, namely push_cleanup used by the cleanup attribute, and the
constraints put in place for checking local jumps against statement
expressions
1: https://open-std.org/JTC1/SC22/WG14/www/docs/n3589.pdf

Anna (navi) Figueiredo Gomes (4):
  c: add D_C2Y disable mask
  c: handle expression nodes in push_cleanup
  c: introduce jump barriers for statement expressions
  c: implement the defer keyword

 gcc/c-family/c-common.cc       |   1 +
 gcc/c-family/c-common.h        |  40 +++---
 gcc/c-family/c-cppbuiltin.cc   |   3 +
 gcc/c-family/c.opt             |   4 +
 gcc/c/c-decl.cc                | 141 +++++++++++++++------
 gcc/c/c-parser.cc              |  43 +++++++
 gcc/c/c-tree.h                 |   8 +-
 gcc/c/c-typeck.cc              |  56 +++++++--
 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, 573 insertions(+), 69 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

Range-diff against v1:
1:  51b7a0fc41f = 1:  51b7a0fc41f c: add D_C2Y disable mask
2:  1f386002702 = 2:  1f386002702 c: handle expression nodes in push_cleanup
3:  4d720d3b0a1 = 3:  4d720d3b0a1 c: introduce jump barriers for statement 
expressions
4:  c48bef9ee42 ! 4:  ef8d6f226b9 c: implement the defer keyword
    @@ Metadata
      ## Commit message ##
         c: implement the defer keyword
     
    -    based on the technical specification n3589[1]
    +    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
    @@ Commit message
     
         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:
     
    @@ Commit message
                 * 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>
     
    @@ gcc/c-family/c-common.cc: 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_C2Y | D_CONLY },
    ++  { "defer",              RID_DEFER,      D_DEFER | D_C2Y | D_CONLY },
        { "delete",             RID_DELETE,     D_CXXONLY | D_CXXWARN },
        { "do",         RID_DO,         0 },
        { "double",             RID_DOUBLE,     0 },
    @@ gcc/c-family/c-common.h: enum rid
      
        /* C extensions */
        RID_ASM,       RID_TYPEOF,   RID_TYPEOF_UNQUAL, RID_ALIGNOF,  
RID_ATTRIBUTE,
    +@@ gcc/c-family/c-common.h: 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
     
      ## gcc/c-family/c-cppbuiltin.cc ##
     @@ gcc/c-family/c-cppbuiltin.cc: c_cpp_builtins (cpp_reader *pfile)
        if (flag_iso)
          cpp_define (pfile, "__STRICT_ANSI__");
      
    -+  if (flag_isoc2y)
    ++  if (flag_isoc2y && flag_defer_ts)
     +    builtin_define_with_int_value ("__STDC_DEFER_TS25755__", 1);
     +
        if (!flag_signed_char)
          cpp_define (pfile, "__CHAR_UNSIGNED__");
      
     
    + ## gcc/c-family/c.opt ##
    +@@ gcc/c-family/c.opt: 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.
    +
      ## gcc/c/c-decl.cc ##
     @@ gcc/c/c-decl.cc: struct GTY(()) c_spot_bindings {
           of the label or goto.  This lets us look at older or newer
    @@ gcc/c/c-decl.cc: lookup_label_for_goto (location_t loc, tree name)
      
     +  if (label_vars->label_bindings.defer_blocks.left)
     +    {
    -+      auto_diagnostic_group d;
    -+      error_at (loc, "jump out of defer block");
    ++      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)
     +    {
    -+      auto_diagnostic_group d;
     +      error_at (loc, "jump over defer block");
     +      inform (DECL_SOURCE_LOCATION (label), "label %qD defined here", 
label);
     +    }
    @@ gcc/c/c-decl.cc: lookup_label_for_goto (location_t loc, tree name)
      }
      
     @@ gcc/c/c-decl.cc: check_earlier_gotos (tree label, struct c_label_vars* 
label_vars)
    - 
    -       if (g->goto_bindings.stmt_exprs.count > 0)
    -   {
    --    auto_diagnostic_group d;
    -     error_at (g->loc, "jump into statement expression");
          inform (DECL_SOURCE_LOCATION (label), "label %qD defined here",
    --            label);
    -+        label);
    -+  }
    +             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);
    ++    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);
    ++    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);
    -   }
    ++    inform (DECL_SOURCE_LOCATION (label), "label %qD defined here", 
label);
    ++  }
          }
      
    +   /* Now that the label is defined, we will issue warnings about
     @@ gcc/c/c-decl.cc: void
      c_release_switch_bindings (struct c_spot_bindings *bindings)
      {
    @@ gcc/c/c-decl.cc: c_check_switch_jump_warnings (struct c_spot_bindings 
*switch_bi
     +      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;
    @@ gcc/c/c-decl.cc: c_check_switch_jump_warnings (struct c_spot_bindings 
*switch_bi
      
     
      ## gcc/c/c-parser.cc ##
    +@@ gcc/c/c-parser.cc: 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;
     @@ gcc/c/c-parser.cc: static void c_parser_statement_after_labels 
(c_parser *, bool *, tree,
      static tree c_parser_c99_block_statement (c_parser *, bool *,
                                          location_t * = NULL);
    @@ gcc/c/c-typeck.cc: c_finish_stmt_expr (location_t loc, tree body)
         and popping new statement lists from the tree.  */
      
     
    + ## gcc/doc/invoke.texi ##
    +@@ gcc/doc/invoke.texi: 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}.
    +@@ gcc/doc/invoke.texi: 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
    +
    + ## gcc/doc/standards.texi ##
    +@@ gcc/doc/standards.texi: 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
    +
      ## gcc/testsuite/gcc.dg/defer-1.c (new) ##
     @@
     +/* { dg-do run } */
    -+/* { dg-options "-std=c2y" } */
    ++/* { dg-options "-std=c2y -fdefer-ts" } */
     +
     +#include <setjmp.h>
     +
    @@ gcc/testsuite/gcc.dg/defer-1.c (new)
      ## gcc/testsuite/gcc.dg/defer-2.c (new) ##
     @@
     +/* { dg-do compile } */
    -+/* { dg-options "-std=c2y" } */
    ++/* { dg-options "-std=c2y -fdefer-ts" } */
     +
     +void a ()
     +{
    @@ gcc/testsuite/gcc.dg/defer-2.c (new)
     +  defer {
     +b:
     +  }
    -+  goto b; /* { dg-error "jump out of defer block" } */
    ++  goto b; /* { dg-error "jump into defer block" } */
     +}
     +
     +void e () {
    @@ gcc/testsuite/gcc.dg/defer-2.c (new)
     +  }
     +}
     +
    -+void f () {
    ++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" } */
    @@ gcc/testsuite/gcc.dg/defer-2.c (new)
     +  }
     +}
     +
    -+void g () {
    ++void h () {
     +  switch (1) {
     +    default:
     +      defer {
    @@ gcc/testsuite/gcc.dg/defer-2.c (new)
     +  }
     +}
     +
    -+int h ()
    ++int i ()
     +{
     +  int r = 1;
     +  {
    @@ gcc/testsuite/gcc.dg/defer-2.c (new)
     +
     +  return r;
     +}
    -+
     
      ## gcc/testsuite/gcc.dg/defer-3.c (new) ##
     @@
     +/* { dg-do run } */
    -+/* { dg-options "-std=c2y" } */
    ++/* { dg-options "-std=c2y -fdefer-ts" } */
     +
     +extern void exit(int);
     +extern void abort(void);
    @@ gcc/testsuite/gcc.dg/defer-3.c (new)
     +  abort();
     +  return 1;
     +}
    +
    + ## gcc/testsuite/gcc.dg/defer-4.c (new) ##
    +@@
    ++/* { dg-do compile } */
    ++/* { dg-options "-std=c2y" } */
    ++
    ++int main(void)
    ++{
    ++  defer; /* { dg-error "'defer' undeclared" "undeclared identifier" } */
    ++}
-- 
2.49.1

Reply via email to