From: "navi" <[email protected]> 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: * 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: navi <[email protected]> --- 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 | 78 ++++++++-- gcc/c/c-parser.cc | 39 +++++ gcc/c/c-tree.h | 8 +- gcc/c/c-typeck.cc | 50 ++++++- 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, 559 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 06e711094ef..43a1fc83cf7 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -477,6 +477,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 a16288f4441..3c427c054c9 100644 --- a/gcc/c-family/c-common.cc +++ b/gcc/c-family/c-common.cc @@ -415,6 +415,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 5711e174049..4e4e9996e2f 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -114,6 +114,7 @@ enum rid RID_BUILTIN_COUNTED_BY_REF, RID_BUILTIN_BSWAPG, RID_BUILTIN_BITREVERSEG, 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 a9cbd6b6f24..9eef5bbc8a6 100644 --- a/gcc/c-family/c-cppbuiltin.cc +++ b/gcc/c-family/c-cppbuiltin.cc @@ -1655,6 +1655,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 62ed56745ff..a98b1e72c7a 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -401,7 +401,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 @@ -535,6 +535,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 has_jump_unsafe_decl : 1; + + /* True if this scope has any deferred statement. This is used to + * check invalid goto jumps. */ + bool has_defer_block : 1; }; /* The scope currently in effect. */ @@ -992,6 +996,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 @@ -1618,10 +1624,11 @@ finish_underspecified_init (tree name, unsigned int prev_state) } -/* Push or pop the jump barrier count of a statement expression. */ +/* Push or pop the jump barrier count of a statement expression or defer block. */ 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 = defer ? &binding->defer_blocks @@ -1642,10 +1649,13 @@ c_binding_adjust_jump_barrier (struct c_spot_bindings *binding, bool start) /* Adjust the bindings for the start of a statement expression or defer block. */ 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; @@ -1663,20 +1673,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 or defer block. */ 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; @@ -1696,9 +1710,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); } } @@ -1706,6 +1720,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); } } @@ -4297,6 +4312,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; } @@ -4388,6 +4409,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 @@ -4473,6 +4511,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); } @@ -4544,6 +4583,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 4eba2999727..7a9e9069253 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -1849,6 +1849,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); @@ -8382,6 +8383,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)) @@ -8908,6 +8912,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 511b155d46d..046c3e318ba 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -667,6 +667,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; @@ -678,8 +679,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); @@ -924,6 +925,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 641d4dc57ea..c8164f7d76b 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -13381,6 +13381,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) @@ -13768,6 +13774,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; @@ -13785,6 +13794,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: @@ -13930,9 +13942,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; @@ -13951,9 +13963,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. */ @@ -14048,7 +14060,31 @@ c_finish_stmt_expr (location_t loc, tree body) return t; } } - + +/* Starts a new deferred block to be executed on scope exit. */ + +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); +} + +/* Ends a deferred block. */ + +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.54.0
