* 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