This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

implement __builtin_assert_no_side_effects


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]