This implements __builtin_assert_no_side_effects, aka PR c/57612. The idea is to have an easy way to assert that an expression is side-effect-free. This can be used in macros that expand their arguments more than once, to ensure that the macro's user doesn't pass in a side-effecting expression:
#define multi_expand(x) (__builtin_assert_no_side_effects(x) + (x)) I considered whether the new error should be suppressed inside typeof, alignof, and sizeof, but in the end decided that erroring is probably ok; as I couldn't think of a scenario where I'd reasonably expect the non-erroring behavior. Built and regtested on x86-64 Fedora 20. Ok for trunk? Tom b/gcc/ChangeLog: 2014-04-28 Tom Tromey <tro...@redhat.com> * doc/extend.texi (Other Builtins): Document __builtin_assert_no_side_effects. b/gcc/c-family/ChangeLog: 2014-04-28 Tom Tromey <tro...@redhat.com> * c-common.c (c_common_reswords): Add __builtin_assert_no_side_effects. * c-common.h (RID_BUILTIN_ASSERT_NO_SIDE_EFFECTS): New constant. b/gcc/c/ChangeLog: 2014-04-28 Tom Tromey <tro...@redhat.com> PR c/57612 * c-parser.c (error_on_side_effects): New function. (c_parser_postfix_expression): Update comment. Handle RID_BUILTIN_ASSERT_NO_SIDE_EFFECTS. b/gcc/testsuite/ChangeLog: 2014-04-28 Tom Tromey <tro...@redhat.com> * gcc.dg/builtin-assert-no-side-effects.c: New file. diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 0ad955d..f334fc4 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -437,6 +437,8 @@ const struct c_common_resword c_common_reswords[] = { "__attribute__", RID_ATTRIBUTE, 0 }, { "__auto_type", RID_AUTO_TYPE, D_CONLY }, { "__bases", RID_BASES, D_CXXONLY }, + { "__builtin_assert_no_side_effects", + RID_BUILTIN_ASSERT_NO_SIDE_EFFECTS, D_CONLY }, { "__builtin_choose_expr", RID_CHOOSE_EXPR, D_CONLY }, { "__builtin_complex", RID_BUILTIN_COMPLEX, D_CONLY }, { "__builtin_shuffle", RID_BUILTIN_SHUFFLE, 0 }, diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 57b7dce..3724ccb 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -101,6 +101,7 @@ enum rid RID_ASM, RID_TYPEOF, RID_ALIGNOF, RID_ATTRIBUTE, RID_VA_ARG, RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL, RID_CHOOSE_EXPR, RID_TYPES_COMPATIBLE_P, RID_BUILTIN_COMPLEX, RID_BUILTIN_SHUFFLE, + RID_BUILTIN_ASSERT_NO_SIDE_EFFECTS, RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128, RID_FRACT, RID_ACCUM, RID_AUTO_TYPE, diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index 56f79f6..192cf0e 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -6896,6 +6896,57 @@ c_parser_generic_selection (c_parser *parser) return error_expr; } +/* Issue an error if *TREE_PTR has a side effect. LOC actually points + to a location_t which is a reasonable error location if *TREE_PTR + does not have one. This function is used to implement + __builtin_assert_no_side_effects. */ + +static tree +error_on_side_effects (tree *tree_ptr, int *, void *data) +{ + location_t loc = * (location_t *) data; + + if (STATEMENT_CLASS_P (*tree_ptr)) + error_at (EXPR_LOC_OR_LOC (*tree_ptr, loc), + "statement inside %<__builtin_assert_no_side_effects%>"); + else if (EXPR_P (*tree_ptr)) + { + switch (TREE_CODE (*tree_ptr)) + { + case CALL_EXPR: + { + tree fn = CALL_EXPR_FN (*tree_ptr); + if (TREE_CODE (fn) == ADDR_EXPR) + { + fn = TREE_OPERAND (fn, 0); + if (TREE_CODE (fn) == FUNCTION_DECL + && (DECL_PURE_P (fn) || TREE_READONLY (fn))) + break; + } + } + /* Fall through. */ + + case TARGET_EXPR: + case MODIFY_EXPR: + case INIT_EXPR: + case PREDECREMENT_EXPR: + case PREINCREMENT_EXPR: + case POSTDECREMENT_EXPR: + case POSTINCREMENT_EXPR: + case VA_ARG_EXPR: + error_at (EXPR_LOC_OR_LOC (*tree_ptr, loc), + "side-effect inside %<__builtin_assert_no_side_effects%>"); + break; + + default: + break; + } + } + + /* Keep going so we report all the errors. */ + return NULL_TREE; +} + /* Parse a postfix expression (C90 6.3.1-6.3.2, C99 6.5.1-6.5.2). postfix-expression: @@ -6939,6 +6990,7 @@ c_parser_generic_selection (c_parser *parser) __builtin_shuffle ( assignment-expression , assignment-expression , assignment-expression, ) + __builtin_assert_no_side_effects ( assignment-expression ) offsetof-member-designator: identifier @@ -7286,6 +7338,22 @@ c_parser_postfix_expression (c_parser *parser) expr = integer_zerop (c) ? *e3_p : *e2_p; break; } + case RID_BUILTIN_ASSERT_NO_SIDE_EFFECTS: + { + c_parser_consume_token (parser); + if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) + { + expr.value = error_mark_node; + break; + } + + expr = c_parser_expr_no_commas (parser, NULL); + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, + "expected %<)%>"); + walk_tree_without_duplicates (&expr.value, error_on_side_effects, + (void *) &loc); + break; + } case RID_TYPES_COMPATIBLE_P: c_parser_consume_token (parser); if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 9780d92..c97ca14 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -9099,6 +9099,21 @@ Similar to @code{__builtin_bswap32}, except the argument and return types are 64 bit. @end deftypefn +@deftypefn {Built-in Function} @var{type} __builtin_assert_no_side_effects (@var{exp}) +This can be used to ensure that an expression does not have any side +effects. This is convenient for use in a macro that must expand an +argument multiple times. + +@var{exp} is a C expression. @code{__builtin_assert_no_side_effects} +has the same type and value as @var{exp}. However, if @var{exp} has a +side effect, GCC will issue an error. + +For the purposes of this builtin, a side effect is any assignment, +increment, or decrement; any statement expression, regardless of its +contents; or a call to a function that is neither @code{pure} nor +@code{const}. +@end deftypefn + @node Target Builtins @section Built-in Functions Specific to Particular Target Machines diff --git a/gcc/testsuite/gcc.dg/builtin-assert-no-side-effects.c b/gcc/testsuite/gcc.dg/builtin-assert-no-side-effects.c new file mode 100644 index 0000000..fd2fec1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-assert-no-side-effects.c @@ -0,0 +1,33 @@ +/* { dg-do compile } */ + +__attribute__ ((pure)) +int p (void) { return 23; } + +__attribute__ ((const)) +int c (void) { return 23; } + +int f (void) { return 23; } + +int +main (void) +{ + int x; + + __builtin_assert_no_side_effects (x); + __builtin_assert_no_side_effects (x >> 1); + __builtin_assert_no_side_effects (x << 2); + + __builtin_assert_no_side_effects (++x); /* { dg-error "side-effect" } */ + __builtin_assert_no_side_effects (x++); /* { dg-error "side-effect" } */ + __builtin_assert_no_side_effects (--x); /* { dg-error "side-effect" } */ + __builtin_assert_no_side_effects (x--); /* { dg-error "side-effect" } */ + + __builtin_assert_no_side_effects (x = 0); /* { dg-error "side-effect" } */ + __builtin_assert_no_side_effects (x = 1); /* { dg-error "side-effect" } */ + + __builtin_assert_no_side_effects (p ()); + __builtin_assert_no_side_effects (c ()); + __builtin_assert_no_side_effects (f ()); /* { dg-error "side-effect" } */ + + __builtin_assert_no_side_effects (__extension__ ({ int y = 75; })); /* { dg-error "statement inside" } */ +}