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

Reply via email to