This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
implement __builtin_assert_no_side_effects
- From: Tom Tromey <tromey at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Tue, 29 Apr 2014 08:37:04 -0600
- Subject: implement __builtin_assert_no_side_effects
- Authentication-results: sourceware.org; auth=none
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 <tromey@redhat.com>
* doc/extend.texi (Other Builtins): Document
__builtin_assert_no_side_effects.
b/gcc/c-family/ChangeLog:
2014-04-28 Tom Tromey <tromey@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 <tromey@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 <tromey@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" } */
+}