On Tue, Apr 29, 2014 at 4:37 PM, Tom Tromey <tro...@redhat.com> wrote: > 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?
C++ support? Otherwise the documentation should clarify that this is only available for C. Thanks, Richard. > 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" } */ > +}