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" } */
> +}

Reply via email to